@placeholderco/placeholder-ui 1.0.12 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -1
- package/README.md +134 -130
- package/dist/app.css +178 -0
- package/dist/bootstrap.min.css +3600 -0
- package/dist/cards/GitCard.svelte +115 -0
- package/dist/cards/GitCard.svelte.d.ts +22 -0
- package/dist/display/Avatar.svelte +29 -17
- package/dist/display/Avatar.svelte.d.ts +12 -2
- package/dist/display/LinkCollection.svelte +40 -18
- package/dist/display/LinkCollection.svelte.d.ts +9 -2
- package/dist/display/Paper.svelte +31 -19
- package/dist/display/Paper.svelte.d.ts +12 -3
- package/dist/display/alert/Alert.svelte +129 -0
- package/dist/display/alert/Alert.svelte.d.ts +15 -0
- package/dist/display/alert/AlertVariant.d.ts +1 -0
- package/dist/display/alert/AlertVariant.js +1 -0
- package/dist/display/filetree/FileTree.svelte +259 -0
- package/dist/display/filetree/FileTree.svelte.d.ts +76 -0
- package/dist/display/filetree/FileTreeItem.svelte +396 -0
- package/dist/display/filetree/FileTreeItem.svelte.d.ts +40 -0
- package/dist/display/filetree/FileTreeNode.d.ts +46 -0
- package/dist/display/filetree/FileTreeNode.js +115 -0
- package/dist/display/filetree/index.d.ts +5 -0
- package/dist/display/filetree/index.js +3 -0
- package/dist/editors/CodeMirror.svelte +306 -0
- package/dist/editors/CodeMirror.svelte.d.ts +31 -0
- package/dist/editors/tiptap/LinkActionMenu.svelte +307 -0
- package/dist/editors/tiptap/LinkActionMenu.svelte.d.ts +8 -0
- package/dist/editors/tiptap/TipTap.svelte +856 -0
- package/dist/editors/tiptap/TipTap.svelte.d.ts +54 -0
- package/dist/editors/tiptap/TipTapBubble.svelte +790 -0
- package/dist/editors/tiptap/TipTapBubble.svelte.d.ts +44 -0
- package/dist/editors/tiptap/collaboration.d.ts +55 -0
- package/dist/editors/tiptap/collaboration.js +1 -0
- package/dist/editors/tiptap/extensions/Alert.d.ts +18 -0
- package/dist/editors/tiptap/extensions/Alert.js +72 -0
- package/dist/editors/tiptap/extensions/CodeBlockEditor.d.ts +21 -0
- package/dist/editors/tiptap/extensions/CodeBlockEditor.js +326 -0
- package/dist/editors/tiptap/extensions/Div.d.ts +19 -0
- package/dist/editors/tiptap/extensions/Div.js +89 -0
- package/dist/editors/tiptap/extensions/Span.d.ts +5 -0
- package/dist/editors/tiptap/extensions/Span.js +56 -0
- package/dist/editors/tiptap/extensions/codeBlockEditor.css +236 -0
- package/dist/editors/tiptap/extensions/codeBlockLanguages.d.ts +17 -0
- package/dist/editors/tiptap/extensions/codeBlockLanguages.js +124 -0
- package/dist/editors/tiptap/extensions/index.d.ts +7 -0
- package/dist/editors/tiptap/extensions/index.js +5 -0
- package/dist/editors/tiptap/toolbar/AlertButton.svelte +261 -0
- package/dist/editors/tiptap/toolbar/AlertButton.svelte.d.ts +7 -0
- package/dist/editors/tiptap/toolbar/AlignCenterButton.svelte +18 -0
- package/dist/editors/tiptap/toolbar/AlignCenterButton.svelte.d.ts +7 -0
- package/dist/editors/tiptap/toolbar/AlignJustifyButton.svelte +18 -0
- package/dist/editors/tiptap/toolbar/AlignJustifyButton.svelte.d.ts +7 -0
- package/dist/editors/tiptap/toolbar/AlignLeftButton.svelte +18 -0
- package/dist/editors/tiptap/toolbar/AlignLeftButton.svelte.d.ts +7 -0
- package/dist/editors/tiptap/toolbar/AlignRightButton.svelte +18 -0
- package/dist/editors/tiptap/toolbar/AlignRightButton.svelte.d.ts +7 -0
- package/dist/editors/tiptap/toolbar/BoldButton.svelte +18 -0
- package/dist/editors/tiptap/toolbar/BoldButton.svelte.d.ts +7 -0
- package/dist/editors/tiptap/toolbar/BulletListButton.svelte +18 -0
- package/dist/editors/tiptap/toolbar/BulletListButton.svelte.d.ts +7 -0
- package/dist/editors/tiptap/toolbar/CodeBlockButton.svelte +18 -0
- package/dist/editors/tiptap/toolbar/CodeBlockButton.svelte.d.ts +7 -0
- package/dist/editors/tiptap/toolbar/CodeButton.svelte +18 -0
- package/dist/editors/tiptap/toolbar/CodeButton.svelte.d.ts +7 -0
- package/dist/editors/tiptap/toolbar/ColorButton.svelte +299 -0
- package/dist/editors/tiptap/toolbar/ColorButton.svelte.d.ts +20 -0
- package/dist/editors/tiptap/toolbar/DocumentButton.svelte +58 -0
- package/dist/editors/tiptap/toolbar/DocumentButton.svelte.d.ts +9 -0
- package/dist/editors/tiptap/toolbar/HeadingButton.svelte +30 -0
- package/dist/editors/tiptap/toolbar/HeadingButton.svelte.d.ts +8 -0
- package/dist/editors/tiptap/toolbar/HtmlButton.svelte +68 -0
- package/dist/editors/tiptap/toolbar/HtmlButton.svelte.d.ts +7 -0
- package/dist/editors/tiptap/toolbar/ImageButton.svelte +54 -0
- package/dist/editors/tiptap/toolbar/ImageButton.svelte.d.ts +8 -0
- package/dist/editors/tiptap/toolbar/ItalicButton.svelte +18 -0
- package/dist/editors/tiptap/toolbar/ItalicButton.svelte.d.ts +7 -0
- package/dist/editors/tiptap/toolbar/LinkButton.svelte +257 -0
- package/dist/editors/tiptap/toolbar/LinkButton.svelte.d.ts +7 -0
- package/dist/editors/tiptap/toolbar/OrderedListButton.svelte +18 -0
- package/dist/editors/tiptap/toolbar/OrderedListButton.svelte.d.ts +7 -0
- package/dist/editors/tiptap/toolbar/RedoButton.svelte +18 -0
- package/dist/editors/tiptap/toolbar/RedoButton.svelte.d.ts +7 -0
- package/dist/editors/tiptap/toolbar/StrikethroughButton.svelte +18 -0
- package/dist/editors/tiptap/toolbar/StrikethroughButton.svelte.d.ts +7 -0
- package/dist/editors/tiptap/toolbar/SubscriptButton.svelte +18 -0
- package/dist/editors/tiptap/toolbar/SubscriptButton.svelte.d.ts +7 -0
- package/dist/editors/tiptap/toolbar/SuperscriptButton.svelte +18 -0
- package/dist/editors/tiptap/toolbar/SuperscriptButton.svelte.d.ts +7 -0
- package/dist/editors/tiptap/toolbar/TableButton.svelte +258 -0
- package/dist/editors/tiptap/toolbar/TableButton.svelte.d.ts +7 -0
- package/dist/editors/tiptap/toolbar/ToolbarButton.svelte +84 -0
- package/dist/editors/tiptap/toolbar/ToolbarButton.svelte.d.ts +10 -0
- package/dist/editors/tiptap/toolbar/UnderlineButton.svelte +18 -0
- package/dist/editors/tiptap/toolbar/UnderlineButton.svelte.d.ts +7 -0
- package/dist/editors/tiptap/toolbar/UndoButton.svelte +18 -0
- package/dist/editors/tiptap/toolbar/UndoButton.svelte.d.ts +7 -0
- package/dist/form/Autocomplete.svelte +86 -135
- package/dist/form/Autocomplete.svelte.d.ts +6 -8
- package/dist/form/AutocompleteMulti.svelte +385 -227
- package/dist/form/AutocompleteMulti.svelte.d.ts +59 -10
- package/dist/form/Checkbox.svelte +59 -54
- package/dist/form/Checkbox.svelte.d.ts +14 -5
- package/dist/form/Chips.svelte +29 -13
- package/dist/form/Chips.svelte.d.ts +16 -2
- package/dist/form/ComboBox.svelte +30 -20
- package/dist/form/ComboBox.svelte.d.ts +1 -1
- package/dist/form/ComboBoxItemBuilder.svelte +161 -188
- package/dist/form/ComboBoxItemBuilder.svelte.d.ts +19 -2
- package/dist/form/ComboBoxMulti.svelte +38 -24
- package/dist/form/ComboBoxMulti.svelte.d.ts +1 -1
- package/dist/form/CronBuilder.svelte +84 -67
- package/dist/form/CronBuilder.svelte.d.ts +16 -3
- package/dist/form/DatePicker.svelte +148 -195
- package/dist/form/DateRangePicker.svelte +101 -98
- package/dist/form/DateTimePicker.svelte +167 -208
- package/dist/form/DateTimePicker.svelte.d.ts +10 -2
- package/dist/form/FormGroup.svelte +33 -10
- package/dist/form/FormGroup.svelte.d.ts +12 -2
- package/dist/form/Number.svelte +70 -47
- package/dist/form/Number.svelte.d.ts +36 -6
- package/dist/form/Radio.svelte +18 -18
- package/dist/form/RadioGroup.svelte +23 -12
- package/dist/form/RadioGroup.svelte.d.ts +13 -2
- package/dist/form/SegmentedControl.svelte +76 -56
- package/dist/form/SegmentedControl.svelte.d.ts +9 -4
- package/dist/form/Select.svelte +150 -233
- package/dist/form/Select.svelte.d.ts +29 -5
- package/dist/form/SelectMulti.svelte +163 -277
- package/dist/form/SelectMulti.svelte.d.ts +36 -7
- package/dist/form/Slider.svelte +183 -269
- package/dist/form/Slider.svelte.d.ts +32 -17
- package/dist/form/StringArrayBuilder.svelte +249 -0
- package/dist/form/StringArrayBuilder.svelte.d.ts +15 -0
- package/dist/form/Switch.svelte +164 -106
- package/dist/form/Switch.svelte.d.ts +22 -3
- package/dist/form/TextArea.svelte +49 -29
- package/dist/form/TextArea.svelte.d.ts +23 -3
- package/dist/form/Textbox.svelte +69 -68
- package/dist/form/Textbox.svelte.d.ts +32 -4
- package/dist/form/TimePicker.svelte +42 -47
- package/dist/form/TimePicker.svelte.d.ts +12 -2
- package/dist/form/comboBoxCore.svelte.d.ts +99 -0
- package/dist/form/comboBoxCore.svelte.js +243 -0
- package/dist/icon/Icon.svelte +8 -3
- package/dist/icon/Icon.svelte.d.ts +8 -3
- package/dist/icon/file-type-bmp.svg +1 -0
- package/dist/icon/file-type-csv.svg +1 -0
- package/dist/icon/file-type-doc.svg +1 -0
- package/dist/icon/file-type-docx.svg +1 -0
- package/dist/icon/file-type-jpg.svg +1 -0
- package/dist/icon/file-type-pdf.svg +1 -0
- package/dist/icon/file-type-png.svg +1 -0
- package/dist/icon/file-type-ppt.svg +1 -0
- package/dist/icon/file-type-svg.svg +1 -0
- package/dist/icon/file-type-txt.svg +1 -0
- package/dist/icon/file-type-xls.svg +1 -0
- package/dist/icon/file-type-zip.svg +1 -0
- package/dist/icon/index.d.ts +166 -151
- package/dist/icon/index.js +166 -152
- package/dist/icon/logo-placeholder.svg +6 -0
- package/dist/icon/source-code.svg +1 -0
- package/dist/icon/sun-moon.svg +1 -0
- package/dist/icon/table.svg +6 -0
- package/dist/index.d.ts +87 -26
- package/dist/index.js +25 -28
- package/dist/layout/AppShell.svelte +12 -162
- package/dist/layout/AppShell.svelte.d.ts +6 -27
- package/dist/layout/CustomNavbar.svelte +9 -7
- package/dist/layout/CustomNavbar.svelte.d.ts +4 -2
- package/dist/layout/Navbar.svelte +161 -129
- package/dist/layout/Navbar.svelte.d.ts +29 -15
- package/dist/layout/NavbarItemDisplay.svelte +2 -2
- package/dist/layout/NavbarItemView.svelte +182 -0
- package/dist/layout/NavbarItemView.svelte.d.ts +10 -0
- package/dist/layout/Sidenav.svelte +280 -341
- package/dist/layout/Sidenav.svelte.d.ts +36 -4
- package/dist/models/ComboBoxItem.d.ts +2 -0
- package/dist/models/Employee.d.ts +53 -0
- package/dist/models/Employee.js +23 -0
- package/dist/models/Hyperlink.d.ts +3 -1
- package/dist/models/LinkCollectionItem.d.ts +3 -1
- package/dist/models/NavbarItem.d.ts +6 -2
- package/dist/theme.svelte.d.ts +0 -1
- package/dist/theme.svelte.js +46 -58
- package/dist/ui/Accordion.svelte +81 -242
- package/dist/ui/Accordion.svelte.d.ts +37 -14
- package/dist/ui/AccordionItem.svelte +303 -0
- package/dist/ui/AccordionItem.svelte.d.ts +21 -0
- package/dist/ui/ActionIcon.svelte +25 -21
- package/dist/ui/ActionIcon.svelte.d.ts +15 -2
- package/dist/ui/Badge.svelte +163 -171
- package/dist/ui/Badge.svelte.d.ts +13 -5
- package/dist/ui/Button.svelte +178 -205
- package/dist/ui/Button.svelte.d.ts +19 -2
- package/dist/ui/ButtonVariant.d.ts +15 -1
- package/dist/ui/ContextMenu.svelte +146 -0
- package/dist/ui/ContextMenu.svelte.d.ts +37 -0
- package/dist/ui/ContextMenuList.svelte +188 -0
- package/dist/ui/ContextMenuList.svelte.d.ts +12 -0
- package/dist/ui/Dialog.svelte +183 -111
- package/dist/ui/Dialog.svelte.d.ts +21 -4
- package/dist/ui/Drawer.svelte +237 -184
- package/dist/ui/Drawer.svelte.d.ts +20 -2
- package/dist/ui/Dropdown.svelte +24 -14
- package/dist/ui/Dropdown.svelte.d.ts +11 -2
- package/dist/ui/Dropzone.svelte +159 -57
- package/dist/ui/Dropzone.svelte.d.ts +29 -2
- package/dist/ui/Link.svelte +12 -4
- package/dist/ui/Link.svelte.d.ts +8 -2
- package/dist/ui/Loader.svelte +23 -18
- package/dist/ui/Loader.svelte.d.ts +5 -2
- package/dist/ui/Logo.svelte +37 -0
- package/dist/ui/Logo.svelte.d.ts +16 -0
- package/dist/ui/MultiSortable.svelte +287 -0
- package/dist/ui/MultiSortable.svelte.d.ts +50 -0
- package/dist/ui/Pagination.svelte +15 -9
- package/dist/ui/Pagination.svelte.d.ts +6 -2
- package/dist/ui/Popover.svelte +43 -65
- package/dist/ui/Popover.svelte.d.ts +14 -5
- package/dist/ui/ProgressBar.svelte +28 -30
- package/dist/ui/ProgressBarVariant.d.ts +7 -1
- package/dist/ui/SegmentedControl.svelte +0 -0
- package/dist/ui/SegmentedControl.svelte.d.ts +26 -0
- package/dist/ui/Sortable.svelte +163 -0
- package/dist/ui/Sortable.svelte.d.ts +47 -0
- package/dist/ui/Table.svelte +383 -80
- package/dist/ui/Table.svelte.d.ts +43 -2
- package/dist/ui/Tabs.svelte +22 -18
- package/dist/ui/Tabs.svelte.d.ts +6 -2
- package/dist/ui/ThemeSwitcher.svelte +26 -7
- package/dist/ui/ThemeSwitcher.svelte.d.ts +4 -2
- package/dist/ui/Tooltip.svelte +312 -180
- package/dist/ui/Tooltip.svelte.d.ts +4 -3
- package/dist/uiTheme.svelte.d.ts +86 -0
- package/dist/uiTheme.svelte.js +214 -0
- package/dist/util/DateFunctions.js +1 -1
- package/dist/util/DragDrop.d.ts +50 -0
- package/dist/util/DragDrop.js +154 -0
- package/dist/util/NavigateTo.js +2 -2
- package/dist/util/dayjs.d.ts +4 -0
- package/dist/util/dayjs.js +15 -0
- package/package.json +66 -26
- package/dist/display/Alert.svelte +0 -179
- package/dist/display/Alert.svelte.d.ts +0 -13
- package/dist/form/FileInput.svelte +0 -235
- package/dist/form/FileInput.svelte.d.ts +0 -23
- package/dist/form/PasswordInput.svelte +0 -253
- package/dist/form/PasswordInput.svelte.d.ts +0 -34
- package/dist/form/Rating.svelte +0 -235
- package/dist/form/Rating.svelte.d.ts +0 -21
- package/dist/styles/components.css +0 -199
- package/dist/styles/dark.css +0 -146
- package/dist/styles/index.css +0 -116
- package/dist/styles/reset.css +0 -110
- package/dist/styles/semantic.css +0 -86
- package/dist/styles/tokens.css +0 -203
- package/dist/styles/utilities.css +0 -523
- package/dist/ui/Breadcrumbs.svelte +0 -131
- package/dist/ui/Breadcrumbs.svelte.d.ts +0 -16
- package/dist/ui/LoadingOverlay.svelte +0 -53
- package/dist/ui/LoadingOverlay.svelte.d.ts +0 -11
- package/dist/ui/Progress.svelte +0 -191
- package/dist/ui/Progress.svelte.d.ts +0 -15
- package/dist/ui/RingProgress.svelte +0 -141
- package/dist/ui/RingProgress.svelte.d.ts +0 -18
- package/dist/ui/Skeleton.svelte +0 -85
- package/dist/ui/Skeleton.svelte.d.ts +0 -12
- package/dist/ui/Stepper.svelte +0 -355
- package/dist/ui/Stepper.svelte.d.ts +0 -24
- package/dist/ui/Timeline.svelte +0 -225
- package/dist/ui/Timeline.svelte.d.ts +0 -23
- /package/dist/{actions → util}/ClickOutside.d.ts +0 -0
- /package/dist/{actions → util}/ClickOutside.js +0 -0
- /package/dist/{transitions → util}/Transitions.d.ts +0 -0
- /package/dist/{transitions → util}/Transitions.js +0 -0
|
@@ -0,0 +1,396 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import Icon from '../../icon/Icon.svelte';
|
|
3
|
+
import { iconChevronRight, iconFolder, iconFile } from '../../icon/index.js';
|
|
4
|
+
import { draggable, droppable, type DragContext } from '../../util/DragDrop.js';
|
|
5
|
+
import type { FileTreeNode } from './FileTreeNode.js';
|
|
6
|
+
import type { Snippet } from 'svelte';
|
|
7
|
+
import { fly } from 'svelte/transition';
|
|
8
|
+
import Self from './FileTreeItem.svelte';
|
|
9
|
+
|
|
10
|
+
export type DropZone = 'before' | 'inside' | 'after' | null;
|
|
11
|
+
|
|
12
|
+
interface Props {
|
|
13
|
+
node: FileTreeNode;
|
|
14
|
+
depth: number;
|
|
15
|
+
dragType: string;
|
|
16
|
+
selectedId?: string;
|
|
17
|
+
/** When false, the row cannot be dragged and won't accept drops */
|
|
18
|
+
isDraggable?: boolean;
|
|
19
|
+
/** Called when this row is clicked (selection) */
|
|
20
|
+
onSelect?: (node: FileTreeNode) => void;
|
|
21
|
+
/** Called when this row is double-clicked */
|
|
22
|
+
onDoubleClick?: (node: FileTreeNode) => void;
|
|
23
|
+
/** Called when the user wants to expand/collapse a folder */
|
|
24
|
+
onToggle?: (node: FileTreeNode) => void;
|
|
25
|
+
/** Called when a drag drops onto/around this row. Position 'inside' is only valid for folders. */
|
|
26
|
+
onMove?: (sourceId: string, targetId: string, position: 'before' | 'inside' | 'after') => void;
|
|
27
|
+
/** Called when this row is right-clicked */
|
|
28
|
+
onContextMenu?: (node: FileTreeNode, event: MouseEvent) => void;
|
|
29
|
+
/** Optional snippet to customize node label area */
|
|
30
|
+
nodeContent?: Snippet<[FileTreeNode]>;
|
|
31
|
+
/** Optional snippet rendering a trailing action bar for the row */
|
|
32
|
+
nodeActions?: Snippet<[FileTreeNode]>;
|
|
33
|
+
/** When the action bar appears */
|
|
34
|
+
nodeActionsTrigger?: 'selected' | 'hover' | 'always';
|
|
35
|
+
/** Where the action bar sits: pinned to the row end, or beside the label */
|
|
36
|
+
nodeActionsPlacement?: 'end' | 'beside';
|
|
37
|
+
/** When false, folders can't be collapsed and always render expanded */
|
|
38
|
+
collapsible?: boolean;
|
|
39
|
+
/** When true, the label gets a title attribute (native tooltip) with the node name */
|
|
40
|
+
showTitles?: boolean;
|
|
41
|
+
/** True while any row in the tree is being dragged */
|
|
42
|
+
dragging?: boolean;
|
|
43
|
+
/** Notifies the tree when this row starts/ends dragging */
|
|
44
|
+
onDragState?: (dragging: boolean) => void;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
let {
|
|
48
|
+
node,
|
|
49
|
+
depth,
|
|
50
|
+
dragType,
|
|
51
|
+
selectedId,
|
|
52
|
+
isDraggable = true,
|
|
53
|
+
onSelect,
|
|
54
|
+
onDoubleClick,
|
|
55
|
+
onToggle,
|
|
56
|
+
onMove,
|
|
57
|
+
onContextMenu,
|
|
58
|
+
nodeContent,
|
|
59
|
+
nodeActions,
|
|
60
|
+
nodeActionsTrigger = 'selected',
|
|
61
|
+
nodeActionsPlacement = 'end',
|
|
62
|
+
collapsible = true,
|
|
63
|
+
showTitles = false,
|
|
64
|
+
dragging = false,
|
|
65
|
+
onDragState
|
|
66
|
+
}: Props = $props();
|
|
67
|
+
|
|
68
|
+
let dropZone = $state<DropZone>(null);
|
|
69
|
+
let hovered = $state(false);
|
|
70
|
+
let hoverExpandTimer: ReturnType<typeof setTimeout> | null = null;
|
|
71
|
+
|
|
72
|
+
const showActions = $derived(
|
|
73
|
+
!!nodeActions &&
|
|
74
|
+
!dragging &&
|
|
75
|
+
(nodeActionsTrigger === 'always' ||
|
|
76
|
+
(nodeActionsTrigger === 'selected' && selectedId === node.id) ||
|
|
77
|
+
(nodeActionsTrigger === 'hover' && hovered))
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
const isFolder = $derived(node.type === 'folder');
|
|
81
|
+
const hasChildren = $derived(isFolder && (node.children?.length ?? 0) > 0);
|
|
82
|
+
const expanded = $derived(!collapsible || !!node.expanded);
|
|
83
|
+
|
|
84
|
+
interface TreePayload {
|
|
85
|
+
sourceId: string;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function computeZone(e: DragEvent, el: HTMLElement): DropZone {
|
|
89
|
+
const rect = el.getBoundingClientRect();
|
|
90
|
+
const y = e.clientY - rect.top;
|
|
91
|
+
const h = rect.height;
|
|
92
|
+
if (!isFolder) {
|
|
93
|
+
return y < h / 2 ? 'before' : 'after';
|
|
94
|
+
}
|
|
95
|
+
// Folders have three zones: top quarter, middle (into), bottom quarter
|
|
96
|
+
if (y < h * 0.25) return 'before';
|
|
97
|
+
if (y > h * 0.75) return 'after';
|
|
98
|
+
return 'inside';
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function clearHoverExpand() {
|
|
102
|
+
if (hoverExpandTimer) {
|
|
103
|
+
clearTimeout(hoverExpandTimer);
|
|
104
|
+
hoverExpandTimer = null;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function scheduleHoverExpand() {
|
|
109
|
+
if (!isFolder || expanded) return;
|
|
110
|
+
clearHoverExpand();
|
|
111
|
+
hoverExpandTimer = setTimeout(() => {
|
|
112
|
+
onToggle?.(node);
|
|
113
|
+
hoverExpandTimer = null;
|
|
114
|
+
}, 600);
|
|
115
|
+
}
|
|
116
|
+
</script>
|
|
117
|
+
|
|
118
|
+
<div class="ft-node-wrap" style="--depth: {depth}">
|
|
119
|
+
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
120
|
+
<div
|
|
121
|
+
class="ft-row-wrap"
|
|
122
|
+
class:selected={selectedId === node.id}
|
|
123
|
+
class:actions-beside={nodeActionsPlacement === 'beside'}
|
|
124
|
+
onmouseenter={() => (hovered = true)}
|
|
125
|
+
onmouseleave={() => (hovered = false)}
|
|
126
|
+
>
|
|
127
|
+
<button
|
|
128
|
+
type="button"
|
|
129
|
+
class="ft-row"
|
|
130
|
+
class:drop-before={dropZone === 'before'}
|
|
131
|
+
class:drop-after={dropZone === 'after'}
|
|
132
|
+
class:drop-inside={dropZone === 'inside'}
|
|
133
|
+
onclick={() => {
|
|
134
|
+
onSelect?.(node);
|
|
135
|
+
// Expand on row click, but never collapse — collapsing is chevron-only.
|
|
136
|
+
if (isFolder && collapsible && !expanded) onToggle?.(node);
|
|
137
|
+
}}
|
|
138
|
+
ondblclick={() => onDoubleClick?.(node)}
|
|
139
|
+
oncontextmenu={(e) => onContextMenu?.(node, e)}
|
|
140
|
+
use:draggable={{
|
|
141
|
+
payload: { sourceId: node.id } satisfies TreePayload,
|
|
142
|
+
type: dragType,
|
|
143
|
+
source: node.id,
|
|
144
|
+
disabled: !isDraggable,
|
|
145
|
+
onDragStart: () => onDragState?.(true),
|
|
146
|
+
onDragEnd: () => {
|
|
147
|
+
dropZone = null;
|
|
148
|
+
clearHoverExpand();
|
|
149
|
+
onDragState?.(false);
|
|
150
|
+
}
|
|
151
|
+
}}
|
|
152
|
+
use:droppable={{
|
|
153
|
+
accepts: dragType,
|
|
154
|
+
disabled: !isDraggable,
|
|
155
|
+
canDrop: (ctx: DragContext<TreePayload>) => ctx.payload.sourceId !== node.id,
|
|
156
|
+
onDragOver: (_, e) => {
|
|
157
|
+
const zone = computeZone(e, e.currentTarget as HTMLElement);
|
|
158
|
+
if (zone !== dropZone) {
|
|
159
|
+
dropZone = zone;
|
|
160
|
+
if (zone === 'inside') scheduleHoverExpand();
|
|
161
|
+
else clearHoverExpand();
|
|
162
|
+
}
|
|
163
|
+
},
|
|
164
|
+
onDragLeave: () => {
|
|
165
|
+
dropZone = null;
|
|
166
|
+
clearHoverExpand();
|
|
167
|
+
},
|
|
168
|
+
onDrop: (ctx: DragContext<TreePayload>) => {
|
|
169
|
+
const zone = dropZone;
|
|
170
|
+
dropZone = null;
|
|
171
|
+
clearHoverExpand();
|
|
172
|
+
if (!zone) return;
|
|
173
|
+
onMove?.(ctx.payload.sourceId, node.id, zone);
|
|
174
|
+
}
|
|
175
|
+
}}
|
|
176
|
+
>
|
|
177
|
+
<span class="ft-indent">
|
|
178
|
+
{#each Array(depth) as _, i (i)}
|
|
179
|
+
<span class="ft-indent-line"></span>
|
|
180
|
+
{/each}
|
|
181
|
+
</span>
|
|
182
|
+
<!-- svelte-ignore a11y_click_events_have_key_events -->
|
|
183
|
+
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
184
|
+
<span
|
|
185
|
+
class="ft-chevron"
|
|
186
|
+
class:invisible={!isFolder || !collapsible}
|
|
187
|
+
class:open={expanded}
|
|
188
|
+
onclick={(e) => {
|
|
189
|
+
if (!isFolder || !collapsible) return;
|
|
190
|
+
e.stopPropagation();
|
|
191
|
+
onToggle?.(node);
|
|
192
|
+
}}
|
|
193
|
+
ondblclick={(e) => {
|
|
194
|
+
if (isFolder && collapsible) e.stopPropagation();
|
|
195
|
+
}}
|
|
196
|
+
>
|
|
197
|
+
<Icon svg={iconChevronRight} size="14px" />
|
|
198
|
+
</span>
|
|
199
|
+
<span class="ft-icon">
|
|
200
|
+
<Icon svg={node.icon ?? (isFolder ? iconFolder : iconFile)} size="16px" />
|
|
201
|
+
</span>
|
|
202
|
+
{#if nodeContent}
|
|
203
|
+
{@render nodeContent(node)}
|
|
204
|
+
{:else}
|
|
205
|
+
<span class="ft-label" title={showTitles ? node.name : undefined}>{node.name}</span>
|
|
206
|
+
{/if}
|
|
207
|
+
</button>
|
|
208
|
+
|
|
209
|
+
{#if showActions}
|
|
210
|
+
<div class="ft-actions" transition:fly={{ x: 6, duration: 180 }}>
|
|
211
|
+
{@render nodeActions?.(node)}
|
|
212
|
+
</div>
|
|
213
|
+
{/if}
|
|
214
|
+
</div>
|
|
215
|
+
|
|
216
|
+
{#if isFolder && expanded && node.children}
|
|
217
|
+
<div class="ft-children">
|
|
218
|
+
{#each node.children as child (child.id)}
|
|
219
|
+
<Self
|
|
220
|
+
node={child}
|
|
221
|
+
depth={depth + 1}
|
|
222
|
+
{dragType}
|
|
223
|
+
{selectedId}
|
|
224
|
+
{isDraggable}
|
|
225
|
+
{onSelect}
|
|
226
|
+
{onDoubleClick}
|
|
227
|
+
{onToggle}
|
|
228
|
+
{onMove}
|
|
229
|
+
{onContextMenu}
|
|
230
|
+
{nodeContent}
|
|
231
|
+
{nodeActions}
|
|
232
|
+
{nodeActionsTrigger}
|
|
233
|
+
{nodeActionsPlacement}
|
|
234
|
+
{collapsible}
|
|
235
|
+
{showTitles}
|
|
236
|
+
{dragging}
|
|
237
|
+
{onDragState}
|
|
238
|
+
/>
|
|
239
|
+
{/each}
|
|
240
|
+
</div>
|
|
241
|
+
{/if}
|
|
242
|
+
</div>
|
|
243
|
+
|
|
244
|
+
<style>
|
|
245
|
+
.ft-node-wrap {
|
|
246
|
+
display: contents;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
.ft-row-wrap {
|
|
250
|
+
position: relative;
|
|
251
|
+
display: flex;
|
|
252
|
+
align-items: center;
|
|
253
|
+
border-left: 3px solid transparent;
|
|
254
|
+
border-radius: 0 3px 3px 0;
|
|
255
|
+
transition: background-color 0.15s ease;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
.ft-row-wrap:hover {
|
|
259
|
+
background-color: rgba(var(--ui-primary-rgbc), 0.08);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
:global(.dark) .ft-row-wrap:hover {
|
|
263
|
+
background-color: rgba(var(--ui-primary-rgbc), 0.2);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
.ft-row-wrap.selected {
|
|
267
|
+
background-color: rgba(var(--ui-accent-rgbc), 0.2);
|
|
268
|
+
border-left-color: var(--ui-accent);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
:global(.dark) .ft-row-wrap.selected {
|
|
272
|
+
background-color: rgba(var(--ui-primary-rgbc), 0.3);
|
|
273
|
+
border-left-color: var(--ui-primary);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
.ft-row {
|
|
277
|
+
position: relative;
|
|
278
|
+
display: flex;
|
|
279
|
+
align-items: center;
|
|
280
|
+
gap: 0.25rem;
|
|
281
|
+
flex: 1 1 auto;
|
|
282
|
+
min-width: 0;
|
|
283
|
+
padding: 0.375rem 0.5rem;
|
|
284
|
+
background: none;
|
|
285
|
+
border: 0;
|
|
286
|
+
color: var(--text-color);
|
|
287
|
+
font-size: 0.875rem;
|
|
288
|
+
font-weight: 500;
|
|
289
|
+
text-align: left;
|
|
290
|
+
cursor: pointer;
|
|
291
|
+
user-select: none;
|
|
292
|
+
transition: color 0.15s ease;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/* 'beside' placement: the row shrinks to its content so the actions sit
|
|
296
|
+
immediately after the label instead of at the far right of the row. */
|
|
297
|
+
.ft-row-wrap.actions-beside .ft-row {
|
|
298
|
+
flex: 0 1 auto;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
.ft-row-wrap.actions-beside .ft-label {
|
|
302
|
+
flex: 0 1 auto;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
.ft-actions {
|
|
306
|
+
display: flex;
|
|
307
|
+
align-items: center;
|
|
308
|
+
flex-shrink: 0;
|
|
309
|
+
gap: 0.125rem;
|
|
310
|
+
padding-right: 0.5rem;
|
|
311
|
+
padding-left: 0.25rem;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
.ft-indent {
|
|
315
|
+
display: inline-flex;
|
|
316
|
+
flex-shrink: 0;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
.ft-indent-line {
|
|
320
|
+
display: inline-block;
|
|
321
|
+
width: 14px;
|
|
322
|
+
height: 100%;
|
|
323
|
+
border-left: 1px solid var(--border-color);
|
|
324
|
+
margin-right: 2px;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
.ft-chevron {
|
|
328
|
+
display: inline-flex;
|
|
329
|
+
align-items: center;
|
|
330
|
+
justify-content: center;
|
|
331
|
+
width: 16px;
|
|
332
|
+
flex-shrink: 0;
|
|
333
|
+
cursor: pointer;
|
|
334
|
+
border-radius: 3px;
|
|
335
|
+
transition: transform 0.12s ease;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
.ft-chevron:not(.invisible):hover {
|
|
339
|
+
background-color: rgba(var(--ui-primary-rgbc), 0.15);
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
.ft-chevron.open {
|
|
343
|
+
transform: rotate(90deg);
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
.ft-chevron.invisible {
|
|
347
|
+
visibility: hidden;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
.ft-icon {
|
|
351
|
+
display: inline-flex;
|
|
352
|
+
flex-shrink: 0;
|
|
353
|
+
color: var(--ui-primary);
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
:global(.dark) .ft-icon {
|
|
357
|
+
color: var(--ui-accent);
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
.ft-label {
|
|
361
|
+
flex: 1;
|
|
362
|
+
min-width: 0;
|
|
363
|
+
overflow: hidden;
|
|
364
|
+
text-overflow: ellipsis;
|
|
365
|
+
white-space: nowrap;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
.ft-row.drop-before::before,
|
|
369
|
+
.ft-row.drop-after::after {
|
|
370
|
+
content: '';
|
|
371
|
+
position: absolute;
|
|
372
|
+
left: 0;
|
|
373
|
+
right: 0;
|
|
374
|
+
height: 2px;
|
|
375
|
+
background: var(--ui-tertiary);
|
|
376
|
+
pointer-events: none;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
.ft-row.drop-before::before {
|
|
380
|
+
top: -1px;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
.ft-row.drop-after::after {
|
|
384
|
+
bottom: -1px;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
.ft-row.drop-inside {
|
|
388
|
+
outline: 2px dashed var(--ui-tertiary);
|
|
389
|
+
outline-offset: -2px;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
.ft-children {
|
|
393
|
+
display: flex;
|
|
394
|
+
flex-direction: column;
|
|
395
|
+
}
|
|
396
|
+
</style>
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { FileTreeNode } from './FileTreeNode.js';
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
3
|
+
export type DropZone = 'before' | 'inside' | 'after' | null;
|
|
4
|
+
interface Props {
|
|
5
|
+
node: FileTreeNode;
|
|
6
|
+
depth: number;
|
|
7
|
+
dragType: string;
|
|
8
|
+
selectedId?: string;
|
|
9
|
+
/** When false, the row cannot be dragged and won't accept drops */
|
|
10
|
+
isDraggable?: boolean;
|
|
11
|
+
/** Called when this row is clicked (selection) */
|
|
12
|
+
onSelect?: (node: FileTreeNode) => void;
|
|
13
|
+
/** Called when this row is double-clicked */
|
|
14
|
+
onDoubleClick?: (node: FileTreeNode) => void;
|
|
15
|
+
/** Called when the user wants to expand/collapse a folder */
|
|
16
|
+
onToggle?: (node: FileTreeNode) => void;
|
|
17
|
+
/** Called when a drag drops onto/around this row. Position 'inside' is only valid for folders. */
|
|
18
|
+
onMove?: (sourceId: string, targetId: string, position: 'before' | 'inside' | 'after') => void;
|
|
19
|
+
/** Called when this row is right-clicked */
|
|
20
|
+
onContextMenu?: (node: FileTreeNode, event: MouseEvent) => void;
|
|
21
|
+
/** Optional snippet to customize node label area */
|
|
22
|
+
nodeContent?: Snippet<[FileTreeNode]>;
|
|
23
|
+
/** Optional snippet rendering a trailing action bar for the row */
|
|
24
|
+
nodeActions?: Snippet<[FileTreeNode]>;
|
|
25
|
+
/** When the action bar appears */
|
|
26
|
+
nodeActionsTrigger?: 'selected' | 'hover' | 'always';
|
|
27
|
+
/** Where the action bar sits: pinned to the row end, or beside the label */
|
|
28
|
+
nodeActionsPlacement?: 'end' | 'beside';
|
|
29
|
+
/** When false, folders can't be collapsed and always render expanded */
|
|
30
|
+
collapsible?: boolean;
|
|
31
|
+
/** When true, the label gets a title attribute (native tooltip) with the node name */
|
|
32
|
+
showTitles?: boolean;
|
|
33
|
+
/** True while any row in the tree is being dragged */
|
|
34
|
+
dragging?: boolean;
|
|
35
|
+
/** Notifies the tree when this row starts/ends dragging */
|
|
36
|
+
onDragState?: (dragging: boolean) => void;
|
|
37
|
+
}
|
|
38
|
+
declare const FileTreeItem: import("svelte").Component<Props, {}, "">;
|
|
39
|
+
type FileTreeItem = ReturnType<typeof FileTreeItem>;
|
|
40
|
+
export default FileTreeItem;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
export type FileTreeNodeType = 'file' | 'folder';
|
|
2
|
+
export interface FileTreeNode {
|
|
3
|
+
/** Unique identifier within the tree */
|
|
4
|
+
id: string;
|
|
5
|
+
/** Display label */
|
|
6
|
+
name: string;
|
|
7
|
+
type: FileTreeNodeType;
|
|
8
|
+
/** Present on folders. Absent (or empty) on files. */
|
|
9
|
+
children?: FileTreeNode[];
|
|
10
|
+
/** Persisted open/closed state for folders */
|
|
11
|
+
expanded?: boolean;
|
|
12
|
+
/** Optional custom icon (raw SVG string). Overrides the default file/folder icon. */
|
|
13
|
+
icon?: string;
|
|
14
|
+
/** Arbitrary application data ignored by the component */
|
|
15
|
+
data?: unknown;
|
|
16
|
+
}
|
|
17
|
+
export interface FileTreeDropTarget {
|
|
18
|
+
/** Parent id — null for tree root */
|
|
19
|
+
parentId: string | null;
|
|
20
|
+
/** Insertion index within parent.children. -1 means append at end (used when dropping INTO an empty folder). */
|
|
21
|
+
index: number;
|
|
22
|
+
}
|
|
23
|
+
interface Found {
|
|
24
|
+
node: FileTreeNode;
|
|
25
|
+
parent: FileTreeNode[];
|
|
26
|
+
index: number;
|
|
27
|
+
}
|
|
28
|
+
export declare function findNode(nodes: FileTreeNode[], id: string): Found | null;
|
|
29
|
+
export declare function findParent(nodes: FileTreeNode[], id: string): FileTreeNode | null;
|
|
30
|
+
/** Returns the chain of nodes from the root down to (and including) `id`, or null if not found. */
|
|
31
|
+
export declare function findPath(nodes: FileTreeNode[], id: string): FileTreeNode[] | null;
|
|
32
|
+
/**
|
|
33
|
+
* Expands every ancestor folder of `id` so the node is visible. Mutates the tree in place.
|
|
34
|
+
* Returns true if any folder's expanded state changed.
|
|
35
|
+
*/
|
|
36
|
+
export declare function expandTo(nodes: FileTreeNode[], id: string): boolean;
|
|
37
|
+
/** True if `ancestorId` is `id` or an ancestor of `id` in the tree. */
|
|
38
|
+
export declare function isAncestor(nodes: FileTreeNode[], ancestorId: string, id: string): boolean;
|
|
39
|
+
/**
|
|
40
|
+
* Moves the node identified by `sourceId` to `target`.
|
|
41
|
+
* Returns a new tree array; the input is not mutated.
|
|
42
|
+
* Returns the original tree unchanged if the move is illegal
|
|
43
|
+
* (source missing, target inside source's subtree, etc.).
|
|
44
|
+
*/
|
|
45
|
+
export declare function moveNode(tree: FileTreeNode[], sourceId: string, target: FileTreeDropTarget): FileTreeNode[];
|
|
46
|
+
export {};
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
export function findNode(nodes, id) {
|
|
2
|
+
for (let i = 0; i < nodes.length; i++) {
|
|
3
|
+
const n = nodes[i];
|
|
4
|
+
if (n.id === id)
|
|
5
|
+
return { node: n, parent: nodes, index: i };
|
|
6
|
+
if (n.children) {
|
|
7
|
+
const found = findNode(n.children, id);
|
|
8
|
+
if (found)
|
|
9
|
+
return found;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
return null;
|
|
13
|
+
}
|
|
14
|
+
export function findParent(nodes, id) {
|
|
15
|
+
for (const n of nodes) {
|
|
16
|
+
if (n.children?.some((c) => c.id === id))
|
|
17
|
+
return n;
|
|
18
|
+
if (n.children) {
|
|
19
|
+
const found = findParent(n.children, id);
|
|
20
|
+
if (found)
|
|
21
|
+
return found;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
/** Returns the chain of nodes from the root down to (and including) `id`, or null if not found. */
|
|
27
|
+
export function findPath(nodes, id) {
|
|
28
|
+
for (const n of nodes) {
|
|
29
|
+
if (n.id === id)
|
|
30
|
+
return [n];
|
|
31
|
+
if (n.children) {
|
|
32
|
+
const sub = findPath(n.children, id);
|
|
33
|
+
if (sub)
|
|
34
|
+
return [n, ...sub];
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Expands every ancestor folder of `id` so the node is visible. Mutates the tree in place.
|
|
41
|
+
* Returns true if any folder's expanded state changed.
|
|
42
|
+
*/
|
|
43
|
+
export function expandTo(nodes, id) {
|
|
44
|
+
const path = findPath(nodes, id);
|
|
45
|
+
if (!path)
|
|
46
|
+
return false;
|
|
47
|
+
let changed = false;
|
|
48
|
+
for (const n of path.slice(0, -1)) {
|
|
49
|
+
if (n.type === 'folder' && !n.expanded) {
|
|
50
|
+
n.expanded = true;
|
|
51
|
+
changed = true;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return changed;
|
|
55
|
+
}
|
|
56
|
+
/** True if `ancestorId` is `id` or an ancestor of `id` in the tree. */
|
|
57
|
+
export function isAncestor(nodes, ancestorId, id) {
|
|
58
|
+
const found = findNode(nodes, ancestorId);
|
|
59
|
+
if (!found)
|
|
60
|
+
return false;
|
|
61
|
+
const stack = [found.node];
|
|
62
|
+
while (stack.length) {
|
|
63
|
+
const n = stack.pop();
|
|
64
|
+
if (n.id === id)
|
|
65
|
+
return true;
|
|
66
|
+
if (n.children)
|
|
67
|
+
stack.push(...n.children);
|
|
68
|
+
}
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Moves the node identified by `sourceId` to `target`.
|
|
73
|
+
* Returns a new tree array; the input is not mutated.
|
|
74
|
+
* Returns the original tree unchanged if the move is illegal
|
|
75
|
+
* (source missing, target inside source's subtree, etc.).
|
|
76
|
+
*/
|
|
77
|
+
export function moveNode(tree, sourceId, target) {
|
|
78
|
+
if (target.parentId && isAncestor(tree, sourceId, target.parentId)) {
|
|
79
|
+
return tree;
|
|
80
|
+
}
|
|
81
|
+
const clone = cloneTree(tree);
|
|
82
|
+
const found = findNode(clone, sourceId);
|
|
83
|
+
if (!found)
|
|
84
|
+
return tree;
|
|
85
|
+
const [removed] = found.parent.splice(found.index, 1);
|
|
86
|
+
let destChildren;
|
|
87
|
+
if (target.parentId === null) {
|
|
88
|
+
destChildren = clone;
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
const destParent = findNode(clone, target.parentId);
|
|
92
|
+
if (!destParent || destParent.node.type !== 'folder') {
|
|
93
|
+
// roll back
|
|
94
|
+
found.parent.splice(found.index, 0, removed);
|
|
95
|
+
return tree;
|
|
96
|
+
}
|
|
97
|
+
if (!destParent.node.children)
|
|
98
|
+
destParent.node.children = [];
|
|
99
|
+
destChildren = destParent.node.children;
|
|
100
|
+
}
|
|
101
|
+
// Adjust index if removing from same parent shifted positions
|
|
102
|
+
let insertAt = target.index;
|
|
103
|
+
if (target.index === -1)
|
|
104
|
+
insertAt = destChildren.length;
|
|
105
|
+
else if (found.parent === destChildren && found.index < target.index)
|
|
106
|
+
insertAt--;
|
|
107
|
+
destChildren.splice(insertAt, 0, removed);
|
|
108
|
+
return clone;
|
|
109
|
+
}
|
|
110
|
+
function cloneTree(nodes) {
|
|
111
|
+
return nodes.map((n) => ({
|
|
112
|
+
...n,
|
|
113
|
+
children: n.children ? cloneTree(n.children) : n.children
|
|
114
|
+
}));
|
|
115
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { default as FileTree } from './FileTree.svelte';
|
|
2
|
+
export { default as FileTreeItem } from './FileTreeItem.svelte';
|
|
3
|
+
export type { FileTreeProps, FileTreeDropEvent } from './FileTree.svelte';
|
|
4
|
+
export type { FileTreeNode, FileTreeNodeType, FileTreeDropTarget } from './FileTreeNode.js';
|
|
5
|
+
export { findNode, findParent, findPath, expandTo, isAncestor, moveNode } from './FileTreeNode.js';
|