Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions templates/design/app/components/design/LayersPanel.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { describe, expect, test } from "vitest";

import {
canUseActiveDragStateForDrop,
Comment thread
builder-io-integration[bot] marked this conversation as resolved.
type LayersPanelMoveIntent,
} from "./LayersPanel";

const dragState = { sourceId: "source", draggedIds: ["source"] };
const intent: LayersPanelMoveIntent = {
draggedIds: ["source"],
targetId: "target",
placement: "inside",
};

describe("canUseActiveDragStateForDrop", () => {
test("requires a matching active drop intent for empty payload fallback", () => {
expect(canUseActiveDragStateForDrop(dragState, null, "target")).toBe(false);
expect(
canUseActiveDragStateForDrop(
dragState,
{ ...intent, targetId: "other" },
"target",
),
).toBe(false);
expect(canUseActiveDragStateForDrop(dragState, intent, "target")).toBe(
true,
);
});
});
24 changes: 23 additions & 1 deletion templates/design/app/components/design/LayersPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@
source: "keyboard" | "pointer";
}

export interface LayersPanelMoveIntent {
interface LayersPanelMoveIntent {
draggedIds: string[];
targetId: string;
placement: "before" | "after" | "inside";
Expand Down Expand Up @@ -319,6 +319,22 @@
let activeDragState: { sourceId: string; draggedIds: string[] } | null = null;
let activeDropIntent: LayersPanelMoveIntent | null = null;

function canUseActiveDragStateForDrop(
dragState: { sourceId: string; draggedIds: string[] } | null,
dropIntent: LayersPanelMoveIntent | null,
targetId: string,
): boolean {
return Boolean(
dragState &&
dragState.sourceId !== targetId &&
dragState.draggedIds.includes(dragState.sourceId) &&
dropIntent?.targetId === targetId,
);
}

export { canUseActiveDragStateForDrop };
export type { LayersPanelMoveIntent };

// Module-level continuous-toggle-drag state for the eye/lock icon
// "click-drag across a run of rows" gesture (Figma parity, unique-paths.md
// #13): a plain mousedown/up, not HTML5 DnD, so per-row React state can't
Expand Down Expand Up @@ -1411,7 +1427,7 @@
});
});
return () => window.cancelAnimationFrame(frame);
}, [activeScreenId, screenOverviewActive, screenRows]);

Check warning on line 1430 in templates/design/app/components/design/LayersPanel.tsx

View workflow job for this annotation

GitHub Actions / Lint & format

react-hooks(exhaustive-deps)

React hook useEffect depends on `screenRows`, which changes every render

const collapseLayers = useCallback(() => {
onExpandedIdsChange(collapsedIds);
Expand Down Expand Up @@ -1555,8 +1571,8 @@
ref={layersPanelRef}
data-layers-panel
className={cn(
"[--design-baseline-unit:4px] [--design-control-height:20px] [--design-icon-size:12px] [--design-row-height:24px] [--design-section-height:28px]",

Check warning on line 1574 in templates/design/app/components/design/LayersPanel.tsx

View workflow job for this annotation

GitHub Actions / Lint & format

shadcn(no-arbitrary-values)

"[--design-section-height:28px]" hardcodes an off-token value. Use a theme token or scale value instead. Use the component's props, variants, and theme tokens. Read .agents/skills/shadcn-ui/SKILL.md before changing UI.

Check warning on line 1574 in templates/design/app/components/design/LayersPanel.tsx

View workflow job for this annotation

GitHub Actions / Lint & format

shadcn(no-arbitrary-values)

"[--design-row-height:24px]" hardcodes an off-token value. Use a theme token or scale value instead. Use the component's props, variants, and theme tokens. Read .agents/skills/shadcn-ui/SKILL.md before changing UI.

Check warning on line 1574 in templates/design/app/components/design/LayersPanel.tsx

View workflow job for this annotation

GitHub Actions / Lint & format

shadcn(no-arbitrary-values)

"[--design-icon-size:12px]" hardcodes an off-token value. Use a theme token or scale value instead. Use the component's props, variants, and theme tokens. Read .agents/skills/shadcn-ui/SKILL.md before changing UI.

Check warning on line 1574 in templates/design/app/components/design/LayersPanel.tsx

View workflow job for this annotation

GitHub Actions / Lint & format

shadcn(no-arbitrary-values)

"[--design-control-height:20px]" hardcodes an off-token value. Use a theme token or scale value instead. Use the component's props, variants, and theme tokens. Read .agents/skills/shadcn-ui/SKILL.md before changing UI.

Check warning on line 1574 in templates/design/app/components/design/LayersPanel.tsx

View workflow job for this annotation

GitHub Actions / Lint & format

shadcn(no-arbitrary-values)

"[--design-baseline-unit:4px]" hardcodes an off-token value. Use a theme token or scale value instead. Use the component's props, variants, and theme tokens. Read .agents/skills/shadcn-ui/SKILL.md before changing UI.
"flex h-full min-h-0 w-full flex-col overflow-hidden bg-[var(--design-editor-panel-bg)] text-[11px] font-normal text-foreground",

Check warning on line 1575 in templates/design/app/components/design/LayersPanel.tsx

View workflow job for this annotation

GitHub Actions / Lint & format

shadcn(no-arbitrary-values)

"text-[11px]" hardcodes an off-token value. Nearest on the scale: text-xs (12px), text-sm (14px). Use the component's props, variants, and theme tokens. Read .agents/skills/shadcn-ui/SKILL.md before changing UI.

Check warning on line 1575 in templates/design/app/components/design/LayersPanel.tsx

View workflow job for this annotation

GitHub Actions / Lint & format

shadcn(no-arbitrary-values)

"bg-[var(--design-editor-panel-bg)]" hardcodes an off-token value. Use a theme token or scale value instead. Use the component's props, variants, and theme tokens. Read .agents/skills/shadcn-ui/SKILL.md before changing UI.
className,
)}
aria-label={labels.title}
Expand All @@ -1565,12 +1581,12 @@
<div
ref={screenSectionRef}
data-screen-section
className="flex min-h-0 shrink-0 flex-col overflow-hidden border-b border-[var(--design-editor-panel-divider-color)] pb-1"

Check warning on line 1584 in templates/design/app/components/design/LayersPanel.tsx

View workflow job for this annotation

GitHub Actions / Lint & format

shadcn(no-arbitrary-values)

"border-[var(--design-editor-panel-divider-color)]" hardcodes an off-token value. Use a theme token or scale value instead. Use the component's props, variants, and theme tokens. Read .agents/skills/shadcn-ui/SKILL.md before changing UI.
style={{
maxHeight: "30%",
...(screenSectionHeight === null
? {}
: { height: `${screenSectionHeight}px` }),

Check warning on line 1589 in templates/design/app/components/design/LayersPanel.tsx

View workflow job for this annotation

GitHub Actions / Lint & format

shadcn(no-inline-styles)

Dynamic style object cannot be checked. Build it from CSS custom properties only. Use the component's props, variants, and theme tokens. Read .agents/skills/shadcn-ui/SKILL.md before changing UI.
}}
>
<div
Expand Down Expand Up @@ -2297,6 +2313,12 @@
} catch {
// Ignore malformed drag payloads and fall back to the primary id.
}
if (
!draggedIds.some(Boolean) &&
canUseActiveDragStateForDrop(activeDragState, activeDropIntent, node.id)
) {
draggedIds = activeDragState!.draggedIds;
}
const cleanedIds = draggedIds.filter(
(id) => id && id !== node.id && !id.startsWith("__"),
);
Expand Down
Loading