diff --git a/web/package.json b/web/package.json index 9ee0784..3102221 100644 --- a/web/package.json +++ b/web/package.json @@ -52,6 +52,8 @@ "vitest": "^4.1.11" }, "dependencies": { + "@dnd-kit/dom": "^0.3.2", + "@dnd-kit/svelte": "^0.3.2", "@fontsource/ibm-plex-mono": "^5.3.0", "@fontsource/ibm-plex-sans": "^5.3.0", "@lucide/svelte": "^1.33.0", diff --git a/web/pnpm-lock.yaml b/web/pnpm-lock.yaml index 9ae02cb..29a2a9a 100644 --- a/web/pnpm-lock.yaml +++ b/web/pnpm-lock.yaml @@ -8,6 +8,12 @@ importers: .: dependencies: + '@dnd-kit/dom': + specifier: ^0.3.2 + version: 0.3.2 + '@dnd-kit/svelte': + specifier: ^0.3.2 + version: 0.3.2(svelte@5.56.9(@typescript-eslint/types@8.67.0)) '@fontsource/ibm-plex-mono': specifier: ^5.3.0 version: 5.3.0 @@ -196,6 +202,26 @@ packages: peerDependencies: postcss-selector-parser: ^7.1.1 + '@dnd-kit/abstract@0.3.2': + resolution: {integrity: sha512-uvPVK+SZYD6Viddn9M0K0JQdXknuVSxA/EbMlFRanve3P/XTc18oLa5zGftKSGjfQGmuzkZ34E26DSbly1zi3Q==} + + '@dnd-kit/collision@0.3.2': + resolution: {integrity: sha512-pNmNSLCI8S9fNQ7QJ3fBCDjiT0sqBhUFcKgmyYaGvGCAU+kq0AP8OWlh0JSisc9k5mFyxmRpmFQcnJpILz/RPA==} + + '@dnd-kit/dom@0.3.2': + resolution: {integrity: sha512-cIUAVgt2szQyz6JRy7I+0r+xeyOAGH21Y15hb5bIyHoDEaZBvIDH+OOlD9eoLjCbsxDLN9WloU2CBi3OE6LYDg==} + + '@dnd-kit/geometry@0.3.2': + resolution: {integrity: sha512-3UBPuIS7E3oGiHxOE8h810QA+0pnrnCtGxl4Os1z3yy5YkC/BEYGY+TxWPTQaY1/OMV7GCX7ZNMlama2QN3n3w==} + + '@dnd-kit/state@0.3.2': + resolution: {integrity: sha512-dLUIkoYrIJhGXfF2wGLTfb46vUokEsO/OoE21TSfmahYrx7ysTmnwbePsznFaHlwgZhQEh6AlLvthLCeY21b1A==} + + '@dnd-kit/svelte@0.3.2': + resolution: {integrity: sha512-HkTnx/3GnxfVNqHSojxhxp5r+KymL9Q7UI2gonXN2+bmdObw03FSQOjicOoRzlV2u9o5DtK1sU4YUQAfkLCGGQ==} + peerDependencies: + svelte: ^5.29.0 + '@eslint-community/eslint-utils@4.10.1': resolution: {integrity: sha512-cuadcxVFE8sDK6iWJbs8Sn0av2Nrh2QSGQhVlBW9AaAHqHwjWsZHT8LJ4hFGPh7ASBV2deFdM7H/DPjulmh8rg==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -313,6 +339,9 @@ packages: '@polka/url@1.0.0-next.29': resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==} + '@preact/signals-core@1.14.4': + resolution: {integrity: sha512-HNB6HYeYKhQbJ1aKl+YRjrS4+QWHLKX6qKoUsfS/m0vqzsVaEBiZiaKbG/e+NKk2ch5ALQr/ihWaMHxiCuuWHA==} + '@rolldown/binding-android-arm64@1.2.4': resolution: {integrity: sha512-jHC2cnyKz5xU2fhECtFl8OZ83cYNt13GZQD+0uMJ/X3o+ijmd56okHhTUwxVSHPx1IRVIJEZ1/1pPzeLCU6XKA==} engines: {node: ^20.19.0 || >=22.12.0} @@ -1518,6 +1547,9 @@ packages: peerDependencies: typescript: '>=4.8.4' + tslib@2.8.1: + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + type-check@0.4.0: resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} engines: {node: '>= 0.8.0'} @@ -1744,6 +1776,44 @@ snapshots: dependencies: postcss-selector-parser: 7.1.5 + '@dnd-kit/abstract@0.3.2': + dependencies: + '@dnd-kit/geometry': 0.3.2 + '@dnd-kit/state': 0.3.2 + tslib: 2.8.1 + + '@dnd-kit/collision@0.3.2': + dependencies: + '@dnd-kit/abstract': 0.3.2 + '@dnd-kit/geometry': 0.3.2 + tslib: 2.8.1 + + '@dnd-kit/dom@0.3.2': + dependencies: + '@dnd-kit/abstract': 0.3.2 + '@dnd-kit/collision': 0.3.2 + '@dnd-kit/geometry': 0.3.2 + '@dnd-kit/state': 0.3.2 + tslib: 2.8.1 + + '@dnd-kit/geometry@0.3.2': + dependencies: + '@dnd-kit/state': 0.3.2 + tslib: 2.8.1 + + '@dnd-kit/state@0.3.2': + dependencies: + '@preact/signals-core': 1.14.4 + tslib: 2.8.1 + + '@dnd-kit/svelte@0.3.2(svelte@5.56.9(@typescript-eslint/types@8.67.0))': + dependencies: + '@dnd-kit/abstract': 0.3.2 + '@dnd-kit/dom': 0.3.2 + '@dnd-kit/state': 0.3.2 + svelte: 5.56.9(@typescript-eslint/types@8.67.0) + tslib: 2.8.1 + '@eslint-community/eslint-utils@4.10.1(eslint@10.9.0(supports-color@10.2.2))': dependencies: eslint: 10.9.0(supports-color@10.2.2) @@ -1847,6 +1917,8 @@ snapshots: '@polka/url@1.0.0-next.29': {} + '@preact/signals-core@1.14.4': {} + '@rolldown/binding-android-arm64@1.2.4': optional: true @@ -2991,6 +3063,8 @@ snapshots: dependencies: typescript: 6.0.3 + tslib@2.8.1: {} + type-check@0.4.0: dependencies: prelude-ls: 1.2.1 diff --git a/web/src/app.css b/web/src/app.css index 4633ea0..d6d0e33 100644 --- a/web/src/app.css +++ b/web/src/app.css @@ -177,6 +177,7 @@ --duration-l: 0.8s; --z-dropdown: 10; + --z-drag: 20; } [data-theme="dark"] { diff --git a/web/src/lib/components/display/StepCard.svelte b/web/src/lib/components/display/StepCard.svelte index d449452..9ba95bd 100644 --- a/web/src/lib/components/display/StepCard.svelte +++ b/web/src/lib/components/display/StepCard.svelte @@ -3,55 +3,44 @@ import IconButton from "$lib/components/ui/IconButton.svelte"; import { t } from "$lib/i18n/t"; import type { Snippet } from "svelte"; + import type { Attachment } from "svelte/attachments"; interface Props { index: number; title?: string; type?: string; - draggable?: boolean; collapsed?: boolean; tools?: Snippet; children?: Snippet; onremove?: () => void; ontoggle?: () => void; - ondragstart?: (e: DragEvent) => void; - ondragover?: (e: DragEvent) => void; - ondrop?: (e: DragEvent) => void; - ondragend?: () => void; + // dnd-kit attachments wired by SortableStepCard; noop on static usage. + elementAttachment?: Attachment; + handleAttachment?: Attachment; } + const noopAttach: Attachment = () => {}; + let { index, title, type, - draggable = false, collapsed = false, tools, children, onremove, ontoggle, - ondragstart, - ondragover, - ondrop, - ondragend, + elementAttachment = noopAttach, + handleAttachment = noopAttach, }: Props = $props(); -
ondragover?.(e)} - ondrop={(e) => ondrop?.(e)} -> -
ondragstart?.(e)} - ondragend={() => ondragend?.()} - > +
+
{index.toString().padStart(2, "0")} -