@warkypublic/svelix 0.1.46 → 0.2.1

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.
Files changed (129) hide show
  1. package/README.md +93 -33
  2. package/dist/components/BetterMenu/BetterMenu.svelte +21 -37
  3. package/dist/components/BetterMenu/BetterMenuAsyncButton.svelte +21 -31
  4. package/dist/components/BetterMenu/BetterMenuPreview.svelte +27 -29
  5. package/dist/components/BetterMenu/MenuRenderer.svelte +40 -51
  6. package/dist/components/Boxer/Boxer.svelte +492 -602
  7. package/dist/components/Boxer/BoxerTarget.svelte +6 -46
  8. package/dist/components/Boxer/BoxerTarget.svelte.d.ts +1 -0
  9. package/dist/components/Boxer/types.d.ts +0 -1
  10. package/dist/components/Button.svelte +19 -43
  11. package/dist/components/ButtonPreview.svelte +2 -11
  12. package/dist/components/CardGrid/CardGrid.svelte +489 -614
  13. package/dist/components/CardGrid/CardGrid.svelte.d.ts +4 -0
  14. package/dist/components/CardGrid/CardGridFilterPanel.svelte +111 -90
  15. package/dist/components/CardGrid/DefaultCard.svelte +86 -97
  16. package/dist/components/CardGrid/DefaultCard.svelte.d.ts +1 -0
  17. package/dist/components/CardGrid/ImageCardStory.svelte +87 -36
  18. package/dist/components/CardGrid/cardGrid.d.ts +24 -0
  19. package/dist/components/CardGrid/cardGrid.js +20 -0
  20. package/dist/components/ContentEditor/CollaboraInsertTextDemo.svelte +5 -10
  21. package/dist/components/ContentEditor/ContentEditor.svelte +24 -50
  22. package/dist/components/ContentEditor/subcomponents/AudioPlayer.svelte +17 -17
  23. package/dist/components/ContentEditor/subcomponents/CollaboraEditor.svelte +244 -309
  24. package/dist/components/ContentEditor/subcomponents/EmailViewer.svelte +2 -3
  25. package/dist/components/ContentEditor/subcomponents/ImageViewer.svelte +14 -15
  26. package/dist/components/ContentEditor/subcomponents/MarkdownViewer.svelte +74 -90
  27. package/dist/components/ContentEditor/subcomponents/MonacoEditor.svelte +75 -103
  28. package/dist/components/ContentEditor/subcomponents/Office365Editor.svelte +234 -297
  29. package/dist/components/ContentEditor/subcomponents/PdfViewer.svelte +14 -20
  30. package/dist/components/ContentEditor/subcomponents/TextEditor.svelte +122 -150
  31. package/dist/components/ContentEditor/subcomponents/UnknownFile.svelte +105 -126
  32. package/dist/components/ContentEditor/subcomponents/VideoPlayer.svelte +17 -17
  33. package/dist/components/ContentEditor/subcomponents/ZipViewer.svelte +2 -3
  34. package/dist/components/ErrorBoundary/ErrorBoundary.svelte +41 -58
  35. package/dist/components/ErrorBoundary/ErrorBoundaryPreview.svelte +8 -16
  36. package/dist/components/Former/Former.svelte +306 -424
  37. package/dist/components/Former/FormerButtonArea.svelte +17 -68
  38. package/dist/components/Former/FormerDrawer.svelte +16 -80
  39. package/dist/components/Former/FormerDrawerPreview.svelte +99 -89
  40. package/dist/components/Former/FormerModal.svelte +16 -74
  41. package/dist/components/Former/FormerModalPreview.svelte +99 -89
  42. package/dist/components/Former/FormerPreview.svelte +76 -78
  43. package/dist/components/Former/FormerRestApiPreview.svelte +55 -48
  44. package/dist/components/Former/formerOverlayHelpers.svelte.d.ts +11 -0
  45. package/dist/components/Former/formerOverlayHelpers.svelte.js +38 -0
  46. package/dist/components/Former/index.d.ts +1 -0
  47. package/dist/components/Former/index.js +1 -0
  48. package/dist/components/FormerControllers/ButtonCtrl.svelte +13 -49
  49. package/dist/components/FormerControllers/CtrlFieldShell.svelte +15 -0
  50. package/dist/components/FormerControllers/CtrlFieldShell.svelte.d.ts +12 -0
  51. package/dist/components/FormerControllers/DateTimeCtrl/DateTimeCtrl.svelte +285 -457
  52. package/dist/components/FormerControllers/DateTimeCtrl/DateTimeCtrlCalendar.svelte +95 -136
  53. package/dist/components/FormerControllers/DateTimeCtrl/DateTimeCtrlPickerPanel.svelte +3 -62
  54. package/dist/components/FormerControllers/DateTimeCtrl/DateTimeCtrlTimeFields.svelte +143 -217
  55. package/dist/components/FormerControllers/IconButtonCtrl.svelte +13 -47
  56. package/dist/components/FormerControllers/InlineWrapper.svelte +12 -41
  57. package/dist/components/FormerControllers/InlineWrapperPreview.svelte +4 -26
  58. package/dist/components/FormerControllers/NativeSelectCtrl.svelte +7 -39
  59. package/dist/components/FormerControllers/NumberInputCtrl.svelte +21 -43
  60. package/dist/components/FormerControllers/PasswordInputCtrl.svelte +8 -33
  61. package/dist/components/FormerControllers/SwitchCtrl.svelte +4 -20
  62. package/dist/components/FormerControllers/TextAreaCtrl.svelte +7 -34
  63. package/dist/components/FormerControllers/TextInputCtrl.svelte +7 -34
  64. package/dist/components/FormerControllers/ctrlLoading.svelte.d.ts +8 -0
  65. package/dist/components/FormerControllers/ctrlLoading.svelte.js +43 -0
  66. package/dist/components/FormerControllers/index.d.ts +2 -0
  67. package/dist/components/FormerControllers/index.js +2 -0
  68. package/dist/components/FormerControllers/utils.d.ts +1 -0
  69. package/dist/components/FormerControllers/utils.js +3 -0
  70. package/dist/components/GlobalStateStore/GlobalStateStoreProvider.svelte +73 -120
  71. package/dist/components/Gridler/components/Gridler.svelte +344 -442
  72. package/dist/components/Gridler/components/GridlerCanvas.svelte +720 -1115
  73. package/dist/components/Gridler/components/GridlerEditor.svelte +2 -15
  74. package/dist/components/Gridler/components/GridlerFull.svelte +521 -841
  75. package/dist/components/Gridler/components/GridlerFull.svelte.d.ts +1 -1
  76. package/dist/components/Gridler/components/GridlerFullWithFormerPreview.svelte +194 -147
  77. package/dist/components/Gridler/components/GridlerSearch.svelte +7 -16
  78. package/dist/components/Gridler/components/GridlerSearchToggle.svelte +7 -19
  79. package/dist/components/Gridler/types.d.ts +1 -1
  80. package/dist/components/Gridler/utils/serverPaging.d.ts +1 -0
  81. package/dist/components/Gridler/utils/serverPaging.js +7 -0
  82. package/dist/components/Icons/IconAdd.svelte +1 -3
  83. package/dist/components/Icons/IconAlertCircle.svelte +1 -3
  84. package/dist/components/Icons/IconAlertTriangle.svelte +1 -3
  85. package/dist/components/Icons/IconCamera.svelte +1 -3
  86. package/dist/components/Icons/IconClose.svelte +1 -3
  87. package/dist/components/Icons/IconEdit.svelte +1 -3
  88. package/dist/components/Icons/IconFilter.svelte +1 -3
  89. package/dist/components/Icons/IconSearch.svelte +1 -3
  90. package/dist/components/Icons/IconSort.svelte +1 -3
  91. package/dist/components/Icons/IconTrash.svelte +1 -3
  92. package/dist/components/Portal/Portal.svelte +14 -38
  93. package/dist/components/Screenshot/Screenshot.svelte +7 -13
  94. package/dist/components/Svark/Svark.svelte +680 -852
  95. package/dist/components/Svark/SvarkContextMenu.svelte +44 -63
  96. package/dist/components/Svark/SvarkInfiniteLoader.svelte +2 -6
  97. package/dist/components/Svark/SvarkPager.svelte +2 -28
  98. package/dist/components/Svark/SvarkSelectionDemo.svelte +4 -8
  99. package/dist/components/Svark/SvarkTopBar.svelte +9 -74
  100. package/dist/components/SvarkGrid/SvarkGrid.svelte +877 -1123
  101. package/dist/components/SvarkGrid/components/SvarkColumnFilterForm.svelte +45 -58
  102. package/dist/components/SvarkGrid/components/SvarkContextMenu.svelte +44 -63
  103. package/dist/components/SvarkGrid/components/SvarkHeaderFilterCell.svelte +41 -75
  104. package/dist/components/SvarkGrid/components/SvarkInfiniteLoader.svelte +2 -6
  105. package/dist/components/SvarkGrid/components/SvarkPager.svelte +2 -28
  106. package/dist/components/SvarkGrid/components/SvarkSearchPopover.svelte +8 -16
  107. package/dist/components/SvarkGrid/components/SvarkTopBar.svelte +6 -62
  108. package/dist/components/SvarkGrid/internal/SvarkGridView.svelte +345 -507
  109. package/dist/components/VTree/VTree.svelte +454 -431
  110. package/dist/components/VTree/VTree.utils.d.ts +23 -0
  111. package/dist/components/VTree/VTree.utils.js +73 -0
  112. package/dist/components/VTree/VTreeContextMenu.svelte +21 -33
  113. package/dist/components/VTree/VTreeControlledSelectionDemo.svelte +20 -0
  114. package/dist/components/VTree/VTreeControlledSelectionDemo.svelte.d.ts +18 -0
  115. package/dist/components/VTree/VTreeCustomizationDemo.svelte +23 -0
  116. package/dist/components/VTree/VTreeCustomizationDemo.svelte.d.ts +18 -0
  117. package/dist/components/VTree/VTreeEventsDemo.svelte +48 -58
  118. package/dist/components/VTree/VTreeInteractionDemo.svelte +29 -0
  119. package/dist/components/VTree/VTreeInteractionDemo.svelte.d.ts +18 -0
  120. package/dist/components/VTree/VTreeMultiSelectDemo.svelte +27 -0
  121. package/dist/components/VTree/VTreeMultiSelectDemo.svelte.d.ts +18 -0
  122. package/dist/components/VTree/VTreeRow.svelte +77 -115
  123. package/dist/components/VTree/VTreeRow.svelte.d.ts +34 -18
  124. package/dist/components/VTree/VTreeSearch.svelte +6 -13
  125. package/dist/components/VTree/VTreeVirtualViewport.svelte +54 -143
  126. package/dist/components/VTree/VTreeVirtualViewport.svelte.d.ts +35 -18
  127. package/dist/components/VTree/types.d.ts +39 -1
  128. package/llm/COMPONENT_GUIDE.md +25 -0
  129. package/package.json +51 -50
@@ -1,68 +1,50 @@
1
- <script lang="ts">
2
- import type { VTreeFlatRow } from './VTree.models.js';
3
- import type { VTreeRecord } from './types.js';
4
- import type { Snippet } from 'svelte';
5
-
6
- export interface Props<T extends VTreeRecord = VTreeRecord> {
7
- row: VTreeFlatRow<T>;
8
- index: number;
9
- rowHeight: number;
10
- indent: number;
11
- isFocused: boolean;
12
- isSelected: boolean;
13
- getNodeLabel: (node: T) => string;
14
- getNodeDescription: (node: T) => string;
15
- treeDataSnapshot: T[];
16
- leftSection?: Snippet<[{
17
- node: T;
18
- level: number;
19
- rowIndex: number;
20
- treeData: T[];
21
- }]>;
22
- rightSection?: Snippet<[{
23
- node: T;
24
- level: number;
25
- rowIndex: number;
26
- treeData: T[];
27
- }]>;
28
- onselectrow?: (index: number, event: MouseEvent) => void;
29
- onrowdoubleclick?: (index: number, event: MouseEvent) => void;
30
- onrowcontext?: (index: number, event: MouseEvent) => void;
31
- ontogglerow?: (node: T) => void;
32
- }
33
-
34
- const {
35
- row,
36
- index,
37
- rowHeight,
38
- indent,
39
- isFocused,
40
- isSelected,
41
- getNodeLabel,
42
- getNodeDescription,
43
- treeDataSnapshot,
44
- leftSection,
45
- rightSection,
46
- onselectrow,
47
- onrowdoubleclick,
48
- onrowcontext,
49
- ontogglerow
50
- }: Props = $props();
51
-
52
- const rowClasses = $derived(
53
- [
54
- 'grid grid-cols-[1fr_auto] items-center select-none border-b border-surface-200-700 hover:bg-surface-100-800',
55
- isFocused ? 'bg-primary-100 dark:bg-primary-900/35' : '',
56
- isSelected ? 'bg-primary-200 dark:bg-primary-900/60' : ''
57
- ].join(' ')
58
- );
59
-
60
- const sectionArgs = $derived({
61
- node: row.node,
62
- level: row.level,
63
- rowIndex: index,
64
- treeData: treeDataSnapshot
65
- });
1
+ <script lang="ts">import { isImageLikeValue, mergeClasses } from "./VTree.utils.js";
2
+ const { row, index, rowHeight, autoMeasure = false, indent, isFocused, isSelected, multiple = false, selectionMode = "modifier", isChecked = false, iconValue, icon, showRowSeparator = true, hoverEffect = true, classes, getNodeLabel, getNodeDescription, treeDataSnapshot, leftSection, rightSection, onselectrow, oncheckboxselect, onrowdoubleclick, onrowcontext, ontogglerow, onrowlongpress, onrowhover, onrowhoverend, longPressDelay = 500, longPressMoveTolerance = 10 } = $props();
3
+ const DEFAULT_CLASSES = {
4
+ row: "grid grid-cols-[1fr_auto] items-center select-none",
5
+ rowSeparator: "border-b border-surface-200-700",
6
+ rowFocused: "bg-primary-100 dark:bg-primary-900/35",
7
+ rowSelected: "bg-primary-200 dark:bg-primary-900/60",
8
+ rowHover: "hover:bg-surface-100-800",
9
+ expandIcon: "inline-flex h-5 w-5 shrink-0 items-center justify-center rounded text-surface-700-200-token hover:bg-surface-200-700 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary-500",
10
+ indentGuide: "min-w-0 flex items-center gap-1.5 pr-2 text-surface-900-100-token",
11
+ label: "truncate text-sm",
12
+ description: "truncate text-xs text-surface-500-400-token",
13
+ iconWrapper: "inline-flex h-5 w-5 shrink-0 items-center justify-center text-sm"
14
+ };
15
+ let longPressTimer;
16
+ let startX = 0;
17
+ let startY = 0;
18
+ let suppressNextClick = false;
19
+ const sectionArgs = $derived({
20
+ node: row.node,
21
+ level: row.level,
22
+ rowIndex: index,
23
+ treeData: treeDataSnapshot
24
+ });
25
+ const rowClasses = $derived([
26
+ mergeClasses(DEFAULT_CLASSES.row, classes?.row),
27
+ showRowSeparator ? mergeClasses(DEFAULT_CLASSES.rowSeparator, classes?.rowSeparator) : "",
28
+ hoverEffect ? mergeClasses(DEFAULT_CLASSES.rowHover, classes?.rowHover) : "",
29
+ isFocused ? mergeClasses(DEFAULT_CLASSES.rowFocused, classes?.rowFocused) : "",
30
+ isSelected ? mergeClasses(DEFAULT_CLASSES.rowSelected, classes?.rowSelected) : ""
31
+ ].filter(Boolean).join(" "));
32
+ function cancelLongPress() {
33
+ clearTimeout(longPressTimer);
34
+ longPressTimer = undefined;
35
+ }
36
+ function handlePointerDown(event) {
37
+ startX = event.clientX;
38
+ startY = event.clientY;
39
+ cancelLongPress();
40
+ longPressTimer = setTimeout(() => {
41
+ suppressNextClick = true;
42
+ onrowlongpress?.(index, event);
43
+ }, longPressDelay);
44
+ }
45
+ function handlePointerMove(event) {
46
+ if (Math.hypot(event.clientX - startX, event.clientY - startY) > longPressMoveTolerance) cancelLongPress();
47
+ }
66
48
  </script>
67
49
 
68
50
  <div
@@ -72,65 +54,45 @@
72
54
  aria-expanded={row.hasChildren ? row.expanded : undefined}
73
55
  aria-selected={isSelected}
74
56
  tabindex="-1"
75
- style={`height: ${rowHeight}px;`}
76
- onclick={(event) => onselectrow?.(index, event)}
77
- ondblclick={(event) => onrowdoubleclick?.(index, event)}
78
- oncontextmenu={(event) => {
79
- event.stopPropagation();
80
- onrowcontext?.(index, event);
81
- }}
82
- onkeydown={(event) => {
83
- if (event.key !== 'Enter' && event.key !== ' ') return;
84
- event.preventDefault();
85
- onselectrow?.(index, new MouseEvent('click'));
86
- if (row.hasChildren) {
87
- ontogglerow?.(row.node);
88
- }
57
+ style={autoMeasure ? `min-height: ${rowHeight}px;` : `height: ${rowHeight}px;`}
58
+ onclick={(event) => {
59
+ if (suppressNextClick) { suppressNextClick = false; return; }
60
+ onselectrow?.(index, event);
89
61
  }}
62
+ ondblclick={(event) => onrowdoubleclick?.(index, event)}
63
+ oncontextmenu={(event) => { event.stopPropagation(); onrowcontext?.(index, event); }}
64
+ onkeydown={() => undefined}
65
+ onpointerdown={handlePointerDown}
66
+ onpointermove={handlePointerMove}
67
+ onpointerup={cancelLongPress}
68
+ onpointercancel={cancelLongPress}
69
+ onpointerenter={(event) => onrowhover?.(index, event)}
70
+ onpointerleave={(event) => { cancelLongPress(); onrowhoverend?.(index, event); }}
90
71
  >
91
- <div
92
- class="min-w-0 flex items-center gap-1.5 pr-2 text-surface-900-100-token"
93
- style={`padding-left: ${(row.level - 1) * indent}px;`}
94
- >
72
+ <div class={mergeClasses(DEFAULT_CLASSES.indentGuide, classes?.indentGuide)} style={`padding-left: ${(row.level - 1) * indent}px;`}>
95
73
  {#if leftSection}
96
- <span class="inline-flex shrink-0 items-center">{@render leftSection(sectionArgs)}</span>
74
+ <span class="inline-flex shrink-0 items-center">
75
+ {@render leftSection(sectionArgs)}
76
+ </span>
97
77
  {/if}
98
-
99
78
  {#if row.hasChildren}
100
- <button
101
- class="inline-flex h-5 w-5 shrink-0 items-center justify-center rounded text-surface-700-200-token hover:bg-surface-200-700 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary-500"
102
- type="button"
103
- aria-label={row.expanded ? 'Collapse node' : 'Expand node'}
104
- onclick={(event) => {
105
- event.stopPropagation();
106
- ontogglerow?.(row.node);
107
- }}
108
- >
109
- {#if row.loading}
110
- <span
111
- class="h-3 w-3 animate-spin rounded-full border-2 border-surface-400/30 border-t-surface-700 dark:border-surface-300/30 dark:border-t-surface-100"
112
- aria-hidden="true"
113
- ></span>
114
- {:else}
115
- {row.expanded ? '▾' : '▸'}
116
- {/if}
79
+ <button class={mergeClasses(DEFAULT_CLASSES.expandIcon, classes?.expandIcon)} type="button" aria-label={row.expanded ? 'Collapse node' : 'Expand node'} onclick={(event) => { event.stopPropagation(); ontogglerow?.(row.node); }}>
80
+ {#if row.loading}<span class="h-3 w-3 animate-spin rounded-full border-2 border-surface-400/30 border-t-surface-700 dark:border-surface-300/30 dark:border-t-surface-100" aria-hidden="true"></span>{:else}{row.expanded ? '▾' : '▸'}{/if}
117
81
  </button>
118
- {:else}
119
- <span class="inline-flex h-5 w-5 shrink-0" aria-hidden="true"></span>
82
+ {:else}<span class="inline-flex h-5 w-5 shrink-0" aria-hidden="true"></span>{/if}
83
+ {#if multiple && selectionMode === 'checkbox'}
84
+ <input class="h-4 w-4 shrink-0" type="checkbox" checked={isChecked} aria-label={`Select ${getNodeLabel(row.node)}`} onclick={(event) => { event.stopPropagation(); oncheckboxselect?.(index, event); }} />
120
85
  {/if}
121
-
122
- <span class="truncate text-sm" class:font-semibold={row.matched}>{getNodeLabel(row.node)}</span>
123
- {#if getNodeDescription(row.node)}
124
- <span class="truncate text-xs text-surface-500-400-token">{getNodeDescription(row.node)}</span>
86
+ {#if icon}
87
+ <span class={mergeClasses(DEFAULT_CLASSES.iconWrapper, classes?.iconWrapper)}>{@render icon(sectionArgs)}</span>
88
+ {:else if iconValue !== undefined && iconValue !== null && iconValue !== ''}
89
+ <span class={mergeClasses(DEFAULT_CLASSES.iconWrapper, classes?.iconWrapper)}>{#if isImageLikeValue(iconValue)}<img class="h-4 w-4 object-contain" src={String(iconValue)} alt="" />{:else}{String(iconValue)}{/if}</span>
125
90
  {/if}
91
+ <span class={mergeClasses(DEFAULT_CLASSES.label, classes?.label)} class:font-semibold={row.matched}>{getNodeLabel(row.node)}</span>
92
+ {#if getNodeDescription(row.node)}<span class={mergeClasses(DEFAULT_CLASSES.description, classes?.description)}>{getNodeDescription(row.node)}</span>{/if}
126
93
  </div>
127
-
128
94
  <div class="flex items-center gap-2 pr-2">
129
- {#if row.error}
130
- <span class="text-xs text-error-500">{row.error}</span>
131
- {/if}
132
- {#if rightSection}
133
- <span class="inline-flex items-center">{@render rightSection(sectionArgs)}</span>
134
- {/if}
95
+ {#if row.error}<span class="text-xs text-error-500">{row.error}</span>{/if}
96
+ {#if rightSection}<span class="inline-flex items-center">{@render rightSection(sectionArgs)}</span>{/if}
135
97
  </div>
136
98
  </div>
@@ -1,36 +1,52 @@
1
- import type { VTreeFlatRow } from './VTree.models.js';
2
- import type { VTreeRecord } from './types.js';
3
1
  import type { Snippet } from 'svelte';
2
+ import type { VTreeFlatRow } from './VTree.models.js';
3
+ import type { VTreeClassOverrides, VTreeRecord } from './types.js';
4
4
  export interface Props<T extends VTreeRecord = VTreeRecord> {
5
5
  row: VTreeFlatRow<T>;
6
6
  index: number;
7
7
  rowHeight: number;
8
+ autoMeasure?: boolean;
8
9
  indent: number;
9
10
  isFocused: boolean;
10
11
  isSelected: boolean;
12
+ multiple?: boolean;
13
+ selectionMode?: 'modifier' | 'checkbox';
14
+ isChecked?: boolean;
15
+ iconValue?: unknown;
16
+ icon?: Snippet<[{
17
+ node: T;
18
+ level: number;
19
+ rowIndex: number;
20
+ treeData: T[];
21
+ }]>;
22
+ showRowSeparator?: boolean;
23
+ hoverEffect?: boolean;
24
+ classes?: VTreeClassOverrides;
11
25
  getNodeLabel: (node: T) => string;
12
26
  getNodeDescription: (node: T) => string;
13
27
  treeDataSnapshot: T[];
14
- leftSection?: Snippet<[
15
- {
16
- node: T;
17
- level: number;
18
- rowIndex: number;
19
- treeData: T[];
20
- }
21
- ]>;
22
- rightSection?: Snippet<[
23
- {
24
- node: T;
25
- level: number;
26
- rowIndex: number;
27
- treeData: T[];
28
- }
29
- ]>;
28
+ leftSection?: Snippet<[{
29
+ node: T;
30
+ level: number;
31
+ rowIndex: number;
32
+ treeData: T[];
33
+ }]>;
34
+ rightSection?: Snippet<[{
35
+ node: T;
36
+ level: number;
37
+ rowIndex: number;
38
+ treeData: T[];
39
+ }]>;
30
40
  onselectrow?: (index: number, event: MouseEvent) => void;
41
+ oncheckboxselect?: (index: number, event: MouseEvent) => void;
31
42
  onrowdoubleclick?: (index: number, event: MouseEvent) => void;
32
43
  onrowcontext?: (index: number, event: MouseEvent) => void;
33
44
  ontogglerow?: (node: T) => void;
45
+ onrowlongpress?: (index: number, event: PointerEvent) => void;
46
+ onrowhover?: (index: number, event: PointerEvent) => void;
47
+ onrowhoverend?: (index: number, event: PointerEvent) => void;
48
+ longPressDelay?: number;
49
+ longPressMoveTolerance?: number;
34
50
  }
35
51
  declare const VTreeRow: import("svelte").Component<Props<VTreeRecord>, {}, "">;
36
52
  type VTreeRow = ReturnType<typeof VTreeRow>;
@@ -1,16 +1,9 @@
1
- <script lang="ts">
2
- export interface Props {
3
- value: string;
4
- placeholder?: string;
5
- oninputvalue?: (value: string) => void;
6
- }
7
-
8
- const { value, placeholder = 'Search tree...', oninputvalue }: Props = $props();
9
-
10
- function handleInput(event: Event) {
11
- const target = event.currentTarget as HTMLInputElement;
12
- oninputvalue?.(target.value);
13
- }
1
+ <script lang="ts">const { value, placeholder = "Search tree...", oninputvalue } = $props();
2
+ function handleInput(event) {
3
+ const target = event.currentTarget;
4
+ oninputvalue?.(target.value);
5
+ }
6
+ export {};
14
7
  </script>
15
8
 
16
9
  <div class="flex">
@@ -1,154 +1,65 @@
1
- <script lang="ts">
2
- import { createVirtualizer, type SvelteVirtualizer } from '@tanstack/svelte-virtual';
3
- import type { Snippet } from 'svelte';
4
- import VTreeRow from './VTreeRow.svelte';
5
- import type { VTreeFlatRow } from './VTree.models.js';
6
- import type { VTreeRecord } from './types.js';
7
-
8
- export interface Props<T extends VTreeRecord = VTreeRecord> {
9
- rows: VTreeFlatRow<T>[];
10
- rowHeight: number;
11
- height: string | number;
12
- indent: number;
13
- overscan: number;
14
- focusedIndex: number;
15
- selectedId: string | null;
16
- loadingRoot: boolean;
17
- loadingMessage: string;
18
- emptyMessage: string;
19
- getNodeLabel: (node: T) => string;
20
- getNodeDescription: (node: T) => string;
21
- treeDataSnapshot: T[];
22
- leftSection?: Snippet<[{
23
- node: T;
24
- level: number;
25
- rowIndex: number;
26
- treeData: T[];
27
- }]>;
28
- rightSection?: Snippet<[{
29
- node: T;
30
- level: number;
31
- rowIndex: number;
32
- treeData: T[];
33
- }]>;
34
- onselectrow?: (index: number, event: MouseEvent) => void;
35
- onrowdoubleclick?: (index: number, event: MouseEvent) => void;
36
- onrowcontext?: (index: number, event: MouseEvent) => void;
37
- onviewportcontext?: (event: MouseEvent) => void;
38
- ontogglerow?: (node: T) => void;
39
- ontreekeydown?: (event: KeyboardEvent) => void;
40
- }
41
-
42
- const {
43
- rows,
44
- rowHeight,
45
- height,
46
- indent,
47
- overscan,
48
- focusedIndex,
49
- selectedId,
50
- loadingRoot,
51
- loadingMessage,
52
- emptyMessage,
53
- getNodeLabel,
54
- getNodeDescription,
55
- treeDataSnapshot,
56
- leftSection,
57
- rightSection,
58
- onselectrow,
59
- onrowdoubleclick,
60
- onrowcontext,
61
- onviewportcontext,
62
- ontogglerow,
63
- ontreekeydown
64
- }: Props = $props();
65
-
66
- let viewportRef = $state<HTMLDivElement | null>(null);
67
- let virtualizerInstance = $state<SvelteVirtualizer<HTMLDivElement, HTMLDivElement> | null>(null);
68
-
69
- function toCssSize(value: string | number): string {
70
- return typeof value === 'number' ? `${value}px` : value;
71
- }
72
-
73
- export function scrollToIndex(index: number) {
74
- virtualizerInstance?.scrollToIndex(index, { align: 'auto' });
75
- }
76
-
77
- $effect(() => {
78
- if (!viewportRef) {
79
- virtualizerInstance = null;
80
- return;
81
- }
82
-
83
- const virtualizer = createVirtualizer<HTMLDivElement, HTMLDivElement>({
84
- count: rows.length,
85
- estimateSize: () => rowHeight,
86
- overscan,
87
- getScrollElement: () => viewportRef
88
- });
89
-
90
- return virtualizer.subscribe((instance) => {
91
- virtualizerInstance = instance;
92
- });
93
- });
94
-
95
- $effect(() => {
96
- if (!virtualizerInstance || !viewportRef) return;
97
- virtualizerInstance.setOptions({
98
- count: rows.length,
99
- estimateSize: () => rowHeight,
100
- overscan,
101
- getScrollElement: () => viewportRef
102
- });
103
- });
104
-
105
- const virtualItems = $derived(virtualizerInstance?.getVirtualItems() ?? []);
106
- const totalSize = $derived(virtualizerInstance?.getTotalSize() ?? 0);
1
+ <script lang="ts">import { createVirtualizer } from "@tanstack/svelte-virtual";
2
+ import VTreeRow from "./VTreeRow.svelte";
3
+ import { resolveRowHeight } from "./VTree.utils.js";
4
+ const { rows, rowHeight, autoMeasure = false, height, indent, overscan, focusedIndex, selectedId, selectedNodeId, selectedItemIds = [], loadingRoot, loadingMessage, emptyMessage, multiple = false, selectionMode = "modifier", iconField, icon, classes, showRowSeparator = true, hoverEffect = true, getNodeLabel, getNodeDescription, treeDataSnapshot, leftSection, rightSection, onselectrow, oncheckboxselect, onrowdoubleclick, onrowcontext, onviewportcontext, ontogglerow, onrowlongpress, onrowhover, onrowhoverend, longPressDelay, longPressMoveTolerance, ontreekeydown } = $props();
5
+ let viewportRef = $state(null);
6
+ let virtualizerInstance = $state(null);
7
+ function toCssSize(value) {
8
+ return typeof value === "number" ? `${value}px` : value;
9
+ }
10
+ function estimate(index) {
11
+ const row = rows[index];
12
+ return resolveRowHeight(rowHeight, row?.node, row?.level ?? 1, index);
13
+ }
14
+ export function scrollToIndex(index) {
15
+ virtualizerInstance?.scrollToIndex(index, { align: "auto" });
16
+ }
17
+ function measure(node) {
18
+ if (autoMeasure) virtualizerInstance?.measureElement(node);
19
+ }
20
+ $effect(() => {
21
+ if (!viewportRef) {
22
+ virtualizerInstance = null;
23
+ return;
24
+ }
25
+ const virtualizer = createVirtualizer({
26
+ count: rows.length,
27
+ estimateSize: estimate,
28
+ overscan,
29
+ getScrollElement: () => viewportRef
30
+ });
31
+ return virtualizer.subscribe((instance) => {
32
+ virtualizerInstance = instance;
33
+ });
34
+ });
35
+ $effect(() => {
36
+ if (!virtualizerInstance || !viewportRef) return;
37
+ virtualizerInstance.setOptions({
38
+ count: rows.length,
39
+ estimateSize: estimate,
40
+ overscan,
41
+ getScrollElement: () => viewportRef
42
+ });
43
+ });
44
+ const virtualItems = $derived(virtualizerInstance?.getVirtualItems() ?? []);
45
+ const totalSize = $derived(virtualizerInstance?.getTotalSize() ?? 0);
107
46
  </script>
108
47
 
109
- <div
110
- class="card overflow-auto border border-surface-300-700 bg-surface-50-950 focus:outline-none focus-visible:ring-2 focus-visible:ring-primary-500"
111
- bind:this={viewportRef}
112
- style={`height: ${toCssSize(height)};`}
113
- onkeydown={(event) => ontreekeydown?.(event)}
114
- oncontextmenu={(event) => onviewportcontext?.(event)}
115
- tabindex="0"
116
- role="tree"
117
- aria-label="Virtual tree"
118
- >
48
+ <div class="card overflow-auto border border-surface-300-700 bg-surface-50-950 focus:outline-none focus-visible:ring-2 focus-visible:ring-primary-500" bind:this={viewportRef} style={`height: ${toCssSize(height)};`} onkeydown={(event) => ontreekeydown?.(event)} oncontextmenu={(event) => onviewportcontext?.(event)} tabindex="0" role="tree" aria-label="Virtual tree">
119
49
  {#if loadingRoot}
120
50
  <div class="grid min-h-24 place-items-center px-3 text-sm text-surface-500-400-token">{loadingMessage}</div>
121
51
  {:else if rows.length === 0}
122
52
  <div class="grid min-h-24 place-items-center px-3 text-sm text-surface-500-400-token">{emptyMessage}</div>
123
53
  {:else}
124
54
  <div class="relative w-full" style={`height: ${totalSize}px;`}>
125
- <div
126
- class="absolute left-0 top-0 w-full"
127
- style={`transform: translateY(${virtualItems[0]?.start ?? 0}px);`}
128
- >
129
- {#each virtualItems as item (item.key)}
130
- {@const row = rows[item.index]}
131
- {#if row}
132
- <VTreeRow
133
- row={row}
134
- index={item.index}
135
- {rowHeight}
136
- {indent}
137
- isFocused={item.index === focusedIndex}
138
- isSelected={row.id === selectedId}
139
- {getNodeLabel}
140
- {getNodeDescription}
141
- {treeDataSnapshot}
142
- {leftSection}
143
- {rightSection}
144
- {onselectrow}
145
- {onrowdoubleclick}
146
- {onrowcontext}
147
- {ontogglerow}
148
- />
149
- {/if}
150
- {/each}
151
- </div>
55
+ {#each virtualItems as item (item.key)}
56
+ {@const row = rows[item.index]}
57
+ {#if row}
58
+ <div class="absolute left-0 top-0 w-full" style={`transform: translateY(${item.start}px);`} {@attach measure}>
59
+ <VTreeRow row={row} index={item.index} rowHeight={estimate(item.index)} {autoMeasure} {indent} isFocused={item.index === focusedIndex} isSelected={row.id === (selectedNodeId || selectedId)} {multiple} {selectionMode} isChecked={selectedItemIds.includes(row.id)} iconValue={iconField ? row.node[iconField] : undefined} {icon} {classes} {showRowSeparator} {hoverEffect} {getNodeLabel} {getNodeDescription} {treeDataSnapshot} {leftSection} {rightSection} {onselectrow} {oncheckboxselect} {onrowdoubleclick} {onrowcontext} {ontogglerow} {onrowlongpress} {onrowhover} {onrowhoverend} {longPressDelay} {longPressMoveTolerance} />
60
+ </div>
61
+ {/if}
62
+ {/each}
152
63
  </div>
153
64
  {/if}
154
65
  </div>
@@ -1,41 +1,58 @@
1
1
  import type { Snippet } from 'svelte';
2
2
  import type { VTreeFlatRow } from './VTree.models.js';
3
- import type { VTreeRecord } from './types.js';
3
+ import type { VTreeClassOverrides, VTreeRecord } from './types.js';
4
4
  export interface Props<T extends VTreeRecord = VTreeRecord> {
5
5
  rows: VTreeFlatRow<T>[];
6
- rowHeight: number;
6
+ rowHeight: number | ((node: T, level: number, index: number) => number);
7
+ autoMeasure?: boolean;
7
8
  height: string | number;
8
9
  indent: number;
9
10
  overscan: number;
10
11
  focusedIndex: number;
11
12
  selectedId: string | null;
13
+ selectedNodeId?: string;
14
+ selectedItemIds?: string[];
12
15
  loadingRoot: boolean;
13
16
  loadingMessage: string;
14
17
  emptyMessage: string;
18
+ multiple?: boolean;
19
+ selectionMode?: 'modifier' | 'checkbox';
20
+ iconField?: keyof T & string;
21
+ icon?: Snippet<[{
22
+ node: T;
23
+ level: number;
24
+ rowIndex: number;
25
+ treeData: T[];
26
+ }]>;
27
+ classes?: VTreeClassOverrides;
28
+ showRowSeparator?: boolean;
29
+ hoverEffect?: boolean;
15
30
  getNodeLabel: (node: T) => string;
16
31
  getNodeDescription: (node: T) => string;
17
32
  treeDataSnapshot: T[];
18
- leftSection?: Snippet<[
19
- {
20
- node: T;
21
- level: number;
22
- rowIndex: number;
23
- treeData: T[];
24
- }
25
- ]>;
26
- rightSection?: Snippet<[
27
- {
28
- node: T;
29
- level: number;
30
- rowIndex: number;
31
- treeData: T[];
32
- }
33
- ]>;
33
+ leftSection?: Snippet<[{
34
+ node: T;
35
+ level: number;
36
+ rowIndex: number;
37
+ treeData: T[];
38
+ }]>;
39
+ rightSection?: Snippet<[{
40
+ node: T;
41
+ level: number;
42
+ rowIndex: number;
43
+ treeData: T[];
44
+ }]>;
34
45
  onselectrow?: (index: number, event: MouseEvent) => void;
46
+ oncheckboxselect?: (index: number, event: MouseEvent) => void;
35
47
  onrowdoubleclick?: (index: number, event: MouseEvent) => void;
36
48
  onrowcontext?: (index: number, event: MouseEvent) => void;
37
49
  onviewportcontext?: (event: MouseEvent) => void;
38
50
  ontogglerow?: (node: T) => void;
51
+ onrowlongpress?: (index: number, event: PointerEvent) => void;
52
+ onrowhover?: (index: number, event: PointerEvent) => void;
53
+ onrowhoverend?: (index: number, event: PointerEvent) => void;
54
+ longPressDelay?: number;
55
+ longPressMoveTolerance?: number;
39
56
  ontreekeydown?: (event: KeyboardEvent) => void;
40
57
  }
41
58
  declare const VTreeVirtualViewport: import("svelte").Component<Props<VTreeRecord>, {
@@ -45,7 +45,8 @@ export interface VTreeProps<T extends VTreeRecord = VTreeRecord> {
45
45
  childrenField?: keyof T & string;
46
46
  hasChildrenField?: keyof T & string;
47
47
  height?: number | string;
48
- rowHeight?: number;
48
+ rowHeight?: number | ((node: T, level: number, index: number) => number);
49
+ autoMeasure?: boolean;
49
50
  indent?: number;
50
51
  overscan?: number;
51
52
  searchable?: boolean;
@@ -80,4 +81,41 @@ export interface VTreeProps<T extends VTreeRecord = VTreeRecord> {
80
81
  ontoggle?: (node: T, expanded: boolean) => void;
81
82
  onsearch?: (query: string) => void;
82
83
  onloaderror?: (error: unknown, node?: T) => void;
84
+ iconField?: keyof T & string;
85
+ icon?: Snippet<[{
86
+ node: T;
87
+ level: number;
88
+ rowIndex: number;
89
+ treeData: T[];
90
+ }]>;
91
+ classes?: VTreeClassOverrides;
92
+ showRowSeparator?: boolean;
93
+ hoverEffect?: boolean;
94
+ /** Controlled single-selection. Undefined means uncontrolled; null means explicitly empty. */
95
+ selected?: T | null;
96
+ /** Enables selectedItems/onSelectedItemsChange multi-selection. */
97
+ multiple?: boolean;
98
+ selectionMode?: 'modifier' | 'checkbox';
99
+ onSelectedChange?: (node: T | null) => void;
100
+ selectedItems?: T[];
101
+ onSelectedItemsChange?: (items: T[]) => void;
102
+ onsingleclick?: (node: T, treeData: T[], event: MouseEvent) => void;
103
+ singleClickDelay?: number;
104
+ onlongpress?: (node: T, treeData: T[], event: PointerEvent) => void;
105
+ longPressDelay?: number;
106
+ longPressMoveTolerance?: number;
107
+ onrowhover?: (node: T, treeData: T[], rowIndex: number, event: PointerEvent) => void;
108
+ onrowhoverend?: (node: T, treeData: T[], rowIndex: number, event: PointerEvent) => void;
109
+ }
110
+ export interface VTreeClassOverrides {
111
+ row?: string;
112
+ rowSeparator?: string;
113
+ rowFocused?: string;
114
+ rowSelected?: string;
115
+ rowHover?: string;
116
+ expandIcon?: string;
117
+ indentGuide?: string;
118
+ label?: string;
119
+ description?: string;
120
+ iconWrapper?: string;
83
121
  }