@keenmate/svelte-treeview 4.6.0 → 5.0.0-rc01

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 (48) hide show
  1. package/README.md +234 -227
  2. package/dist/canvas/CanvasTree.svelte +1657 -0
  3. package/dist/canvas/CanvasTree.svelte.d.ts +148 -0
  4. package/dist/canvas/canvas-interaction.d.ts +67 -0
  5. package/dist/canvas/canvas-interaction.js +590 -0
  6. package/dist/canvas/canvas-layout-balanced.d.ts +18 -0
  7. package/dist/canvas/canvas-layout-balanced.js +212 -0
  8. package/dist/canvas/canvas-layout-box.d.ts +10 -0
  9. package/dist/canvas/canvas-layout-box.js +194 -0
  10. package/dist/canvas/canvas-layout-fishbone.d.ts +11 -0
  11. package/dist/canvas/canvas-layout-fishbone.js +349 -0
  12. package/dist/canvas/canvas-layout-radial.d.ts +21 -0
  13. package/dist/canvas/canvas-layout-radial.js +233 -0
  14. package/dist/canvas/canvas-layout-sunburst.d.ts +27 -0
  15. package/dist/canvas/canvas-layout-sunburst.js +457 -0
  16. package/dist/canvas/canvas-layout.d.ts +11 -0
  17. package/dist/canvas/canvas-layout.js +509 -0
  18. package/dist/canvas/canvas-renderer.d.ts +46 -0
  19. package/dist/canvas/canvas-renderer.js +828 -0
  20. package/dist/canvas/canvas-text.d.ts +11 -0
  21. package/dist/canvas/canvas-text.js +64 -0
  22. package/dist/canvas/canvas-theme.d.ts +58 -0
  23. package/dist/canvas/canvas-theme.js +128 -0
  24. package/dist/canvas/types.d.ts +187 -0
  25. package/dist/canvas/types.js +1 -0
  26. package/dist/components/Node.svelte +86 -38
  27. package/dist/components/Tree.svelte +265 -1234
  28. package/dist/components/Tree.svelte.d.ts +31 -23
  29. package/dist/components/TreeProvider.svelte +28 -0
  30. package/dist/components/TreeProvider.svelte.d.ts +28 -0
  31. package/dist/constants.generated.d.ts +1 -1
  32. package/dist/constants.generated.js +1 -1
  33. package/dist/core/TreeController.svelte.d.ts +309 -0
  34. package/dist/core/TreeController.svelte.js +1324 -0
  35. package/dist/core/createTreeController.d.ts +9 -0
  36. package/dist/core/createTreeController.js +11 -0
  37. package/dist/index.d.ts +10 -1
  38. package/dist/index.js +9 -0
  39. package/dist/ltree/indexer.js +2 -1
  40. package/dist/ltree/ltree-node.svelte.d.ts +3 -0
  41. package/dist/ltree/ltree-node.svelte.js +2 -0
  42. package/dist/ltree/ltree.svelte.d.ts +1 -1
  43. package/dist/ltree/ltree.svelte.js +96 -91
  44. package/dist/ltree/types.d.ts +12 -5
  45. package/dist/styles/main.scss +58 -58
  46. package/dist/styles.css +26 -26
  47. package/dist/styles.css.map +1 -1
  48. package/package.json +1 -1
package/README.md CHANGED
@@ -2,94 +2,42 @@
2
2
 
3
3
  A high-performance, feature-rich hierarchical tree view component for Svelte 5 with drag & drop support, search functionality, and flexible data structures using LTree.
4
4
 
5
- > [!IMPORTANT]
6
- > **Looking for a framework-agnostic solution?** There's also a web component version that can be used standalone or in other frameworks at https://github.com/KeenMate/web-treeview/
5
+ ## Live Demo
7
6
 
8
- ## 🚀 Features
7
+ Browse interactive code examples and the full API reference at **[svelte-treeview.keenmate.dev](https://svelte-treeview.keenmate.dev)**
9
8
 
10
- - **Svelte 5 Native**: Built specifically for Svelte 5 with full support for runes and modern Svelte patterns
11
- - **High Performance**: Flat rendering mode with progressive loading for 5000+ nodes
12
- - **Drag & Drop**: Built-in drag and drop with position control (above/below/child), touch support, and async validation
13
- - **Tree Editing**: Built-in methods for add, move, remove operations with automatic path management
14
- - **Search & Filter**: Integrated FlexSearch for fast, full-text search capabilities
15
- - **Flexible Data Sources**: Works with any hierarchical data structure
16
- - **Context Menus**: Dynamic right-click menus with callback-based generation, icons, disabled states
17
- - **Visual Customization**: Extensive styling options and icon customization
18
- - **TypeScript Support**: Full TypeScript support with comprehensive type definitions
19
- - **Accessibility**: Built with accessibility in mind
20
-
21
- ## 📦 Installation
9
+ ## v5.0: Core/Renderer Split
22
10
 
23
- ```bash
24
- npm install @keenmate/svelte-treeview
25
- ```
26
-
27
- ## 🔨 Development Setup
28
-
29
- For developers working on the project, you can use either standard npm commands or the provided Makefile (which provides a unified interface for all contributors):
30
-
31
- ```bash
32
- # Using Makefile (recommended for consistency)
33
- make setup # or make install
34
- make dev
35
-
36
- # Or using standard npm commands
37
- npm install
38
- npm run dev
39
- ```
40
-
41
- ## 🎨 Importing Styles
42
-
43
- The component requires CSS to display correctly. Import the styles in your app:
44
-
45
- ### Option 1: Import SCSS in your main app file
46
- ```javascript
47
- // In your main.js or main.ts
48
- import '@keenmate/svelte-treeview/styles.scss';
49
- ```
50
-
51
- ### Option 2: Import in your Svelte component
52
- ```svelte
53
- <style>
54
- @import '@keenmate/svelte-treeview/styles.scss';
55
- </style>
56
- ```
57
-
58
- ### Option 3: Use with your build system
59
- If using Vite, Webpack, or similar, you can import the SCSS:
60
- ```javascript
61
- import '@keenmate/svelte-treeview/styles.scss';
62
- ```
11
+ > [!IMPORTANT]
12
+ > **In version 5, the tree core (data structure, expand/collapse, search, drag & drop logic) has been completely separated from the renderer.** The library now ships with multiple built-in renderers — the classic HTML `Tree`, a high-performance `CanvasTree` (with tree, balanced, fishbone, radial, box, and sunburst layouts) — and the architecture is open for you to build your own custom renderers on top of the same core.
63
13
 
64
- ## ⚠️ Performance Warning: Use `$state.raw()` for Large Datasets
14
+ This means you can:
15
+ - Use the built-in renderers as-is for common use cases
16
+ - Create entirely custom visualizations (WebGL, SVG, etc.) powered by the same tree core
17
+ - Mix and match — use the HTML tree for editing and a canvas renderer for overview
65
18
 
66
- > [!WARNING]
67
- > **When passing large arrays (1000+ items) to the Tree component, use `$state.raw()` instead of `$state()` to avoid severe performance issues.**
19
+ ## New in v4.7: Per-Node Drop Position Restrictions
68
20
 
69
- Svelte 5's `$state()` creates deep proxies for all nested objects. With thousands of items, this causes massive overhead during tree operations.
21
+ > [!NOTE]
22
+ > **You can now restrict which drop positions (above/below/child) are allowed per node.**
70
23
 
24
+ Use `getAllowedDropPositionsCallback` for dynamic logic or `allowedDropPositionsMember` for server data:
71
25
  ```typescript
72
- // SLOW - Each item becomes a Proxy (5000x slower with large datasets)
73
- let treeData = $state<TreeNode[]>([])
74
-
75
- // FAST - Items remain plain objects
76
- let treeData = $state.raw<TreeNode[]>([])
26
+ // Files can only have siblings, trash only accepts children
27
+ function getAllowedDropPositions(node) {
28
+ if (node.data?.type === 'file') return ['above', 'below'];
29
+ if (node.data?.type === 'trash') return ['child'];
30
+ return undefined; // all positions allowed (default)
31
+ }
77
32
  ```
78
33
 
79
- **Symptoms of this issue:**
80
- - Tree takes 15-90+ seconds to render with thousands of items
81
- - Console shows `[Violation] 'message' handler took XXXXms`
82
- - Same data loads instantly in isolated test
83
-
84
- The array itself remains reactive - only individual items lose deep reactivity (which Tree doesn't need).
85
-
86
- ## 📢 New in v4.6: Progressive Flat Rendering
34
+ ## v4.6: Progressive Flat Rendering
87
35
 
88
36
  > [!NOTE]
89
37
  > **The tree now uses progressive flat rendering by default for significantly improved performance.**
90
38
 
91
39
  **What this means:**
92
- - The tree renders immediately with the first batch of nodes (~200 by default)
40
+ - The tree renders immediately with the first batch of nodes (~20 by default)
93
41
  - Remaining nodes are rendered progressively in subsequent frames
94
42
  - For large trees (5000+ nodes), you'll see nodes appear over ~100-500ms instead of a single long freeze
95
43
  - The UI remains responsive during rendering
@@ -117,12 +65,47 @@ The array itself remains reactive - only individual items lose deep reactivity (
117
65
 
118
66
  Recursive mode may be preferred for very small trees or when you need the `{#key changeTracker}` behavior that recreates all nodes on any change.
119
67
 
120
- ## 🎯 Quick Start
68
+ ## Features
69
+
70
+ - **Svelte 5 Native**: Built specifically for Svelte 5 with full support for runes and modern Svelte patterns
71
+ - **High Performance**: Flat rendering mode with progressive loading for 5000+ nodes
72
+ - **Drag & Drop**: Built-in drag and drop with position control (above/below/child), touch support, and async validation
73
+ - **Tree Editing**: Built-in methods for add, move, remove operations with automatic path management
74
+ - **Search & Filter**: Integrated FlexSearch for fast, full-text search capabilities
75
+ - **Flexible Data Sources**: Works with any hierarchical data structure
76
+ - **Context Menus**: Dynamic right-click menus with callback-based generation, icons, disabled states
77
+ - **Visual Customization**: Extensive styling options and icon customization
78
+ - **TypeScript Support**: Full TypeScript support with comprehensive type definitions
79
+ - **Accessibility**: Built with accessibility in mind
80
+
81
+ ## Installation
82
+
83
+ ```bash
84
+ npm install @keenmate/svelte-treeview
85
+ ```
86
+
87
+ ### Importing Styles
88
+
89
+ The component requires CSS to display correctly. Import the styles in your app:
90
+
91
+ **JavaScript import** (in your main.js/main.ts or Vite/Webpack entry):
92
+ ```javascript
93
+ import '@keenmate/svelte-treeview/styles.scss';
94
+ ```
95
+
96
+ **Svelte component import:**
97
+ ```svelte
98
+ <style>
99
+ @import '@keenmate/svelte-treeview/styles.scss';
100
+ </style>
101
+ ```
102
+
103
+ ## Quick Start
121
104
 
122
105
  ```svelte
123
106
  <script lang="ts">
124
107
  import { Tree } from '@keenmate/svelte-treeview';
125
-
108
+
126
109
  const data = [
127
110
  { path: '1', name: 'Documents', type: 'folder' },
128
111
  { path: '1.1', name: 'Projects', type: 'folder' },
@@ -141,14 +124,24 @@ Recursive mode may be preferred for very small trees or when you need the `{#key
141
124
  />
142
125
  ```
143
126
 
144
- ## 🔧 Advanced Usage
127
+ > [!TIP]
128
+ > **Performance tip:** When passing large arrays (1000+ items) to the Tree component, use `$state.raw()` instead of `$state()` to avoid severe performance issues. Svelte 5's `$state()` creates deep proxies — with thousands of items this causes up to 5,000x slowdown. The array itself remains reactive; only individual items lose deep reactivity (which Tree doesn't need).
129
+ > ```typescript
130
+ > // BAD - Each item becomes a Proxy
131
+ > let treeData = $state<TreeNode[]>([])
132
+ >
133
+ > // GOOD - Items remain plain objects
134
+ > let treeData = $state.raw<TreeNode[]>([])
135
+ > ```
136
+
137
+ ## Advanced Usage
145
138
 
146
139
  ### With Custom Node Templates
147
140
 
148
141
  ```svelte
149
142
  <script lang="ts">
150
143
  import { Tree } from '@keenmate/svelte-treeview';
151
-
144
+
152
145
  const fileData = [
153
146
  { path: '1', name: 'Documents', type: 'folder', icon: '📁' },
154
147
  { path: '1.1', name: 'report.pdf', type: 'file', icon: '📄', size: '2.3 MB' },
@@ -181,15 +174,15 @@ Recursive mode may be preferred for very small trees or when you need the `{#key
181
174
  ```svelte
182
175
  <script lang="ts">
183
176
  import { Tree } from '@keenmate/svelte-treeview';
184
-
177
+
185
178
  let searchText = $state('');
186
179
  const data = [/* your data */];
187
180
  </script>
188
181
 
189
- <input
190
- type="text"
191
- placeholder="Search..."
192
- bind:value={searchText}
182
+ <input
183
+ type="text"
184
+ placeholder="Search..."
185
+ bind:value={searchText}
193
186
  />
194
187
 
195
188
  <Tree
@@ -208,29 +201,29 @@ Recursive mode may be preferred for very small trees or when you need the `{#key
208
201
  <script lang="ts">
209
202
  import { Tree } from '@keenmate/svelte-treeview';
210
203
  import type { SearchOptions } from 'flexsearch';
211
-
204
+
212
205
  let treeRef;
213
206
  const data = [/* your data */];
214
-
207
+
215
208
  // Programmatic search with FlexSearch options
216
209
  function performAdvancedSearch(searchTerm: string) {
217
210
  const searchOptions: SearchOptions = {
218
211
  suggest: true, // Enable suggestions for typos
219
- limit: 10, // Limit results to 10 items
212
+ limit: 10, // Limit results to 10 items
220
213
  bool: "and" // Use AND logic for multiple terms
221
214
  };
222
-
215
+
223
216
  const results = treeRef.searchNodes(searchTerm, searchOptions);
224
217
  console.log('Advanced search results:', results);
225
218
  }
226
-
219
+
227
220
  // Programmatic filtering with options
228
221
  function filterWithOptions(searchTerm: string) {
229
222
  const searchOptions: SearchOptions = {
230
223
  threshold: 0.8, // Similarity threshold
231
224
  depth: 2 // Search depth
232
225
  };
233
-
226
+
234
227
  treeRef.filterNodes(searchTerm, searchOptions);
235
228
  }
236
229
  </script>
@@ -269,6 +262,8 @@ For complete FlexSearch documentation, visit: [FlexSearch Options](https://githu
269
262
 
270
263
  ### With Drag & Drop
271
264
 
265
+ **Note:** Drag and drop is disabled by default. Set `dragDropMode` to enable it.
266
+
272
267
  ```svelte
273
268
  <script lang="ts">
274
269
  import { Tree } from '@keenmate/svelte-treeview';
@@ -276,9 +271,9 @@ For complete FlexSearch documentation, visit: [FlexSearch Options](https://githu
276
271
  let treeRef: Tree<MyNode>;
277
272
 
278
273
  const data = [
279
- { path: '1', name: 'Folder 1', isDraggable: true },
280
- { path: '1.1', name: 'Item 1', isDraggable: true },
281
- { path: '2', name: 'Folder 2', isDraggable: true }
274
+ { path: '1', name: 'Folder 1' },
275
+ { path: '1.1', name: 'Item 1' },
276
+ { path: '2', name: 'Folder 2' }
282
277
  ];
283
278
 
284
279
  function onDragStart(node, event) {
@@ -298,6 +293,7 @@ For complete FlexSearch documentation, visit: [FlexSearch Options](https://githu
298
293
  {data}
299
294
  idMember="path"
300
295
  pathMember="path"
296
+ dragDropMode="both"
301
297
  orderMember="sortOrder"
302
298
  dragOverNodeClass="ltree-dragover-highlight"
303
299
  onNodeDragStart={onDragStart}
@@ -312,6 +308,45 @@ When using `dropZoneMode="floating"` (default), users can choose where to drop:
312
308
  - **Below**: Insert as sibling after the target node
313
309
  - **Child**: Insert as child of the target node
314
310
 
311
+ #### Per-Node Drop Position Restrictions
312
+
313
+ You can restrict which drop positions are allowed per node. This is useful for:
314
+ - **Trash/Recycle Bin**: Only allow dropping INTO (child), not above/below
315
+ - **Files**: Only allow above/below (can't drop INTO a file)
316
+ - **Folders**: Allow all positions (default)
317
+
318
+ ```svelte
319
+ <script lang="ts">
320
+ import { Tree, type DropPosition, type LTreeNode } from '@keenmate/svelte-treeview';
321
+
322
+ // Dynamic callback approach
323
+ function getAllowedDropPositions(node: LTreeNode<MyItem>): DropPosition[] | null {
324
+ if (node.data?.type === 'file') return ['above', 'below'];
325
+ if (node.data?.type === 'trash') return ['child'];
326
+ return undefined; // all positions allowed
327
+ }
328
+ </script>
329
+
330
+ <Tree
331
+ {data}
332
+ getAllowedDropPositionsCallback={getAllowedDropPositions}
333
+ />
334
+ ```
335
+
336
+ Or use the member approach for server-side data:
337
+ ```svelte
338
+ <Tree
339
+ {data}
340
+ allowedDropPositionsMember="allowedDropPositions"
341
+ />
342
+
343
+ <!-- Where data items have: { allowedDropPositions: ['child'] } -->
344
+ ```
345
+
346
+ When restrictions are applied:
347
+ - **Glow mode**: Snaps to the nearest allowed position
348
+ - **Floating mode**: Only renders buttons for allowed positions
349
+
315
350
  #### Async Drop Validation
316
351
 
317
352
  Use `beforeDropCallback` to validate or modify drops, including async operations like confirmation dialogs:
@@ -488,7 +523,7 @@ The tree supports context menus with two approaches: callback-based (recommended
488
523
  - **Auto-close**: Closes on scroll, click outside, or programmatically
489
524
  - **Type safety**: Full TypeScript support with `ContextMenuItem` interface
490
525
 
491
- ## 🎨 Styling and Customization
526
+ ## Styling and Customization
492
527
 
493
528
  The component comes with default styles that provide a clean, modern look. You can customize it extensively:
494
529
 
@@ -558,7 +593,7 @@ The component includes several pre-built classes for styling selected nodes:
558
593
  |-------|-------------|---------------|
559
594
  | `ltree-selected-bold` | Bold text with primary color | **Bold text** in theme primary color |
560
595
  | `ltree-selected-border` | Border and background highlight | Solid border with light background |
561
- | `ltree-selected-brackets` | Decorative brackets around text | **Node Text** |
596
+ | `ltree-selected-brackets` | Decorative brackets around text | > **Node Text** < |
562
597
 
563
598
  **Available Drag-over Node Classes:**
564
599
 
@@ -580,17 +615,17 @@ The component includes several pre-built classes for styling selected nodes:
580
615
  />
581
616
  ```
582
617
 
583
- ## 📚 API Reference
618
+ ## API Reference
584
619
 
585
620
  ### Tree Component Props
586
621
 
587
- #### Core Required Properties
588
- | Prop | Type | Required | Description |
589
- |------|------|----------|-------------|
590
- | `data` | `T[]` | | Array of data objects |
591
- | `idMember` | `string` | | Property name for unique identifiers |
592
- | `pathMember` | `string` | | Property name for hierarchical paths |
593
- | `sortCallback` | `(items: T[]) => T[]` | | Function to sort items |
622
+ #### Core Properties
623
+ | Prop | Type | Default | Description |
624
+ |------|------|---------|-------------|
625
+ | `data` | `T[]` | **required** | Array of data objects |
626
+ | `idMember` | `string` | **required** | Property name for unique identifiers |
627
+ | `pathMember` | `string` | **required** | Property name for hierarchical paths |
628
+ | `sortCallback` | `(items: T[]) => T[]` | default sort | Function to sort items (optional) |
594
629
 
595
630
  #### Data Mapping Properties
596
631
  | Prop | Type | Default | Description |
@@ -602,6 +637,7 @@ The component includes several pre-built classes for styling selected nodes:
602
637
  | `isSelectedMember` | `string \| null` | `null` | Property name for selected state |
603
638
  | `isDraggableMember` | `string \| null` | `null` | Property name for draggable state |
604
639
  | `isDropAllowedMember` | `string \| null` | `null` | Property name for drop allowed state |
640
+ | `allowedDropPositionsMember` | `string \| null` | `null` | Property name for allowed drop positions array |
605
641
  | `hasChildrenMember` | `string \| null` | `null` | Property name for children existence |
606
642
  | `isSorted` | `boolean \| null` | `null` | Whether items should be sorted |
607
643
 
@@ -618,13 +654,13 @@ The component includes several pre-built classes for styling selected nodes:
618
654
 
619
655
  **Note**: When `shouldUseInternalSearchIndex` is enabled, node indexing is performed asynchronously using `requestIdleCallback` (with fallback to `setTimeout`). This ensures the tree renders immediately while search indexing happens during browser idle time, providing better performance for large datasets.
620
656
 
621
- **⚠️ Important**: For internal search indexing to work, you must:
657
+ **Important**: For internal search indexing to work, you must:
622
658
  1. Set `shouldUseInternalSearchIndex={true}`
623
659
  2. Provide either `searchValueMember` (property name) or `getSearchValueCallback` (function)
624
660
 
625
661
  Without both requirements, no search indexing will occur.
626
662
 
627
- **Performance Tuning**:
663
+ **Performance Tuning**:
628
664
  - `indexerBatchSize` controls how many nodes are processed per idle callback. Lower values (10-25) provide smoother UI performance but slower indexing, while higher values (50-100) index faster but may cause brief UI pauses. Default: 25.
629
665
  - `indexerTimeout` sets the maximum wait time before forcing indexing when the browser is busy. Lower values (25-50ms) ensure more responsive indexing, while higher values (100-200ms) give more time for genuine idle periods. Default: 50ms.
630
666
 
@@ -641,23 +677,42 @@ Without both requirements, no search indexing will occur.
641
677
  |------|------|---------|-------------|
642
678
  | `expandLevel` | `number \| null` | `2` | Automatically expand nodes up to this level |
643
679
  | `shouldToggleOnNodeClick` | `boolean` | `true` | Toggle expansion on node click |
644
- | `useFlatRendering` | `boolean` | `true` | Use flat rendering mode (faster for large trees) |
645
- | `progressiveRender` | `boolean` | `true` | Progressively render nodes in batches |
646
- | `renderBatchSize` | `number` | `50` | Number of nodes to render per batch |
647
680
  | `orderMember` | `string \| null` | `null` | Property name for sort order (enables above/below positioning in drag-drop) |
648
681
  | `indexerBatchSize` | `number \| null` | `25` | Number of nodes to process per batch during search indexing |
649
682
  | `indexerTimeout` | `number \| null` | `50` | Maximum time (ms) to wait for idle callback before forcing indexing |
650
- | `shouldDisplayDebugInformation` | `boolean` | `false` | Show debug information panel with tree statistics and enable console debug logging for tree operations and async search indexing |
683
+ | `shouldDisplayDebugInformation` | `boolean` | `false` | Show debug information panel with tree statistics and enable console debug logging |
651
684
  | `shouldDisplayContextMenuInDebugMode` | `boolean` | `false` | Display persistent context menu at fixed position for styling development |
652
685
 
686
+ #### Rendering Properties
687
+ | Prop | Type | Default | Description |
688
+ |------|------|---------|-------------|
689
+ | `useFlatRendering` | `boolean` | `true` | Use flat rendering mode (faster for large trees) |
690
+ | `progressiveRender` | `boolean` | `true` | Progressively render nodes in batches |
691
+ | `initialBatchSize` | `number` | `20` | First batch size for progressive rendering |
692
+ | `maxBatchSize` | `number` | `500` | Maximum batch size cap |
693
+ | `flatIndentSize` | `string` | `'1.5rem'` | Indentation size per level in flat mode |
694
+
695
+ #### Drag & Drop Properties
696
+ | Prop | Type | Default | Description |
697
+ |------|------|---------|-------------|
698
+ | `dragDropMode` | `DragDropMode` | `'none'` | Controls allowed drag operations: `'none'`, `'self'`, `'cross'`, `'both'` |
699
+ | `dropZoneMode` | `string` | `'glow'` | Drop indicator style: `'floating'` or `'glow'` |
700
+ | `dropZoneLayout` | `string` | `'around'` | Zone arrangement: `'around'`, `'above'`, `'below'`, `'wave'`, `'wave2'` |
701
+ | `dropZoneStart` | `number \| string` | `33` | Where zones start horizontally (number=%, string=CSS value) |
702
+ | `dropZoneMaxWidth` | `number` | `120` | Max width in pixels for wave layouts |
703
+ | `allowCopy` | `boolean` | `false` | Enable Ctrl+drag to copy instead of move |
704
+ | `autoHandleCopy` | `boolean` | `true` | Auto-handle same-tree copies (false for external DB/API) |
705
+ | `allowedDropPositionsMember` | `string \| null` | `null` | Property name for allowed drop positions array |
706
+ | `getAllowedDropPositionsCallback` | `(node) => DropPosition[] \| null` | `undefined` | Callback returning allowed drop positions per node |
707
+ | `beforeDropCallback` | `(dropNode, draggedNode, position, event, operation) => ...` | `undefined` | Async-capable callback to validate/modify drops |
708
+
653
709
  #### Event Handler Properties
654
710
  | Prop | Type | Default | Description |
655
711
  |------|------|---------|-------------|
656
712
  | `onNodeClicked` | `(node) => void` | `undefined` | Node click event handler |
657
713
  | `onNodeDragStart` | `(node, event) => void` | `undefined` | Drag start event handler |
658
714
  | `onNodeDragOver` | `(node, event) => void` | `undefined` | Drag over event handler |
659
- | `beforeDropCallback` | `(dropNode, draggedNode, position, event, operation) => boolean \| { position?, operation? } \| Promise<...>` | `undefined` | Async-capable callback to validate/modify drops before they happen |
660
- | `onNodeDrop` | `(dropNode, draggedNode, position, event, operation) => void` | `undefined` | Drop event handler. Position is 'above', 'below', or 'child'. Operation is 'move' or 'copy' |
715
+ | `onNodeDrop` | `(dropNode, draggedNode, position, event, operation) => void` | `undefined` | Drop event handler. Position is `'above'`, `'below'`, or `'child'`. Operation is `'move'` or `'copy'` |
661
716
 
662
717
  #### Visual Styling Properties
663
718
  | Prop | Type | Default | Description |
@@ -671,15 +726,15 @@ Without both requirements, no search indexing will occur.
671
726
  | `scrollHighlightTimeout` | `number \| null` | `4000` | Duration (ms) for scroll highlight animation |
672
727
  | `scrollHighlightClass` | `string \| null` | `'ltree-scroll-highlight'` | CSS class to apply for scroll highlight effect |
673
728
 
674
- #### Available Slots
675
- | Slot | Description |
676
- |------|-------------|
677
- | `nodeTemplate` | Custom node template |
678
- | `treeHeader` | Tree header content |
679
- | `treeBody` | Tree body content |
680
- | `treeFooter` | Tree footer content |
681
- | `noDataFound` | No data template |
682
- | `contextMenu` | Context menu template |
729
+ #### Snippets
730
+ | Snippet | Parameters | Description |
731
+ |---------|------------|-------------|
732
+ | `nodeTemplate` | `(node)` | Custom node template |
733
+ | `treeHeader` | | Tree header content |
734
+ | `treeBody` | | Tree body content |
735
+ | `treeFooter` | | Tree footer content |
736
+ | `noDataFound` | | No data template |
737
+ | `contextMenu` | `(node, closeMenu)` | Context menu template |
683
738
 
684
739
  #### Public Methods
685
740
  | Method | Parameters | Description |
@@ -852,99 +907,7 @@ When enabled, the component will log detailed information to the browser console
852
907
 
853
908
  This provides valuable insights for performance optimization and troubleshooting, especially when working with large datasets or complex search operations.
854
909
 
855
- ### Events
856
-
857
- #### onNodeClicked(node)
858
- Triggered when a node is clicked.
859
-
860
- #### onNodeDragStart(node, event)
861
- Triggered when drag operation starts.
862
-
863
- #### onNodeDragOver(node, event)
864
- Triggered when dragging over a potential drop target.
865
-
866
- #### beforeDropCallback(dropNode, draggedNode, position, event, operation)
867
- Called before a drop is processed. Can be async for showing dialogs.
868
- - Return `false` to cancel the drop
869
- - Return `{ position: 'above'|'below'|'child' }` to override position
870
- - Return `{ operation: 'move'|'copy' }` to override operation
871
- - Return `true` or `undefined` to proceed normally
872
-
873
- #### onNodeDrop(dropNode, draggedNode, position, event, operation)
874
- Triggered when a node is dropped. For same-tree moves, the tree auto-handles the move and this callback is for notification.
875
- - `position`: 'above', 'below', or 'child'
876
- - `operation`: 'move' or 'copy' (Ctrl+drag)
877
-
878
- ### Slots
879
-
880
- #### nodeTemplate
881
- Custom template for rendering node content.
882
-
883
- ```svelte
884
- {#snippet nodeTemplate(node)}
885
- <!-- Your custom node content -->
886
- {/snippet}
887
- ```
888
-
889
- #### contextMenu
890
- Custom context menu template (snippet-based approach).
891
-
892
- ```svelte
893
- {#snippet contextMenu(node, closeMenu)}
894
- <button onclick={() => { /* action */ closeMenu(); }}>
895
- Action
896
- </button>
897
- {/snippet}
898
- ```
899
-
900
- ### Context Menu Properties
901
-
902
- #### contextMenuCallback
903
- Function that generates context menu items dynamically.
904
-
905
- ```typescript
906
- contextMenuCallback: (node: LTreeNode<T>) => ContextMenuItem[]
907
- ```
908
-
909
- Where `ContextMenuItem` is:
910
- ```typescript
911
- interface ContextMenuItem {
912
- icon?: string; // Optional icon (emoji or text)
913
- title: string; // Menu item text
914
- isDisabled?: boolean; // Whether item is disabled
915
- callback: () => void; // Action to perform
916
- isDivider?: boolean; // Render as divider instead of item
917
- }
918
- ```
919
-
920
- #### contextMenuXOffset
921
- Horizontal offset from cursor position (default: 8px).
922
-
923
- #### contextMenuYOffset
924
- Vertical offset from cursor position (default: 0px).
925
-
926
- #### shouldDisplayContextMenuInDebugMode
927
- When enabled, displays a persistent context menu at a fixed position for styling development (default: false).
928
-
929
- ```svelte
930
- <Tree
931
- {data}
932
- contextMenuCallback={createContextMenu}
933
- shouldDisplayContextMenuInDebugMode={true}
934
- shouldDisplayDebugInformation={true}
935
- contextMenuXOffset={10}
936
- contextMenuYOffset={5}
937
- />
938
- ```
939
-
940
- **Debug Mode Features:**
941
- - Shows context menu for the second node (or first if only one exists)
942
- - Positions menu 200px right and 100px down from tree's top-left corner
943
- - Persistent display - no need to right-click repeatedly
944
- - Perfect for CSS styling and position testing
945
- - Works with both callback-based and snippet-based context menus
946
-
947
- ## 🏗️ Data Structure
910
+ ## Data Structure
948
911
 
949
912
  The component expects hierarchical data with path-based organization:
950
913
 
@@ -1004,21 +967,21 @@ interface InsertArrayResult<T> {
1004
967
  ```svelte
1005
968
  <script lang="ts">
1006
969
  import { Tree } from '@keenmate/svelte-treeview';
1007
-
970
+
1008
971
  let insertResult = $state();
1009
-
972
+
1010
973
  const data = [
1011
974
  { id: '1', path: '1', name: 'Root' },
1012
975
  { id: '1.2', path: '1.2', name: 'Child' }, // Missing parent "1.1"
1013
976
  { id: '1.1.1', path: '1.1.1', name: 'Deep' } // Missing parent "1.1"
1014
977
  ];
1015
-
978
+
1016
979
  // Check results after tree processes data
1017
980
  $effect(() => {
1018
981
  if (insertResult) {
1019
- console.log(`✅ ${insertResult.successful} nodes inserted successfully`);
1020
- console.log(`❌ ${insertResult.failed.length} nodes failed to insert`);
1021
-
982
+ console.log(`${insertResult.successful} nodes inserted successfully`);
983
+ console.log(`${insertResult.failed.length} nodes failed to insert`);
984
+
1022
985
  insertResult.failed.forEach(failure => {
1023
986
  console.log(`Failed: ${failure.originalData.name} - ${failure.error}`);
1024
987
  });
@@ -1026,10 +989,10 @@ interface InsertArrayResult<T> {
1026
989
  });
1027
990
  </script>
1028
991
 
1029
- <Tree
1030
- {data}
1031
- idMember="id"
1032
- pathMember="path"
992
+ <Tree
993
+ {data}
994
+ idMember="id"
995
+ pathMember="path"
1033
996
  displayValueMember="name"
1034
997
  bind:insertResult
1035
998
  />
@@ -1043,7 +1006,7 @@ interface InsertArrayResult<T> {
1043
1006
  - **Search Accuracy**: Failed nodes are excluded from search index, ensuring search results match visible tree
1044
1007
  - **User Feedback**: Inform users about data issues with detailed failure information
1045
1008
 
1046
- ## 🚀 Performance
1009
+ ## Performance
1047
1010
 
1048
1011
  The component is optimized for large datasets:
1049
1012
 
@@ -1085,20 +1048,64 @@ enablePerfLogging();
1085
1048
  window.components['svelte-treeview'].perf.enable()
1086
1049
  ```
1087
1050
 
1088
- **Important**: See the [$state.raw() warning](#%EF%B8%8F-performance-warning-use-stateraw-for-large-datasets) above - using `$state()` instead of `$state.raw()` for tree data can cause 5,000x slowdown!
1051
+ **Important**: See the [$state.raw() tip](#quick-start) above - using `$state()` instead of `$state.raw()` for tree data can cause 5,000x slowdown!
1052
+
1053
+ ## CanvasTree (Canvas-Based Rendering)
1054
+
1055
+ `CanvasTree` renders the tree on an HTML5 Canvas for high-performance visualization of large hierarchies. It supports multiple layout modes, keyboard navigation, drag & drop, and custom node rendering.
1056
+
1057
+ ### Layout Modes
1058
+
1059
+ | Mode | Description |
1060
+ |------|-------------|
1061
+ | `tree` | Standard hierarchical tree (default) |
1062
+ | `balanced` | Root centered with two symmetric arms |
1063
+ | `fishbone` | Spine with alternating branches above/below |
1064
+ | `radial` | Star / concentric rings from center |
1065
+ | `box` | Space-filling treemap |
1066
+
1067
+ ### Fishbone Navigation
1068
+
1069
+ In fishbone layout, keyboard navigation follows the fishbone structure:
1070
+
1071
+ - **Left/Right**: Navigate between same-side nodes along the spine axis. Spine nodes stay on their side; branch nodes traverse same-depth peers across all spine branches.
1072
+ - **Up/Down**: Navigate parent/child within a branch. From spine nodes, enters branch children on the pressed side.
1073
+ - **Cross-spine** (optional): When `fishboneCrossNav={true}`, Up/Down crosses to the opposite side of the spine when no more nodes exist in the current direction.
1074
+
1075
+ ```svelte
1076
+ <CanvasTree
1077
+ {data}
1078
+ layoutMode="fishbone"
1079
+ fishboneCrossNav={false} <!-- default: stops at spine boundary -->
1080
+ />
1081
+ ```
1082
+
1083
+ ## Development Setup & Contributing
1084
+
1085
+ For developers working on the project, you can use either standard npm commands or the provided Makefile:
1086
+
1087
+ ```bash
1088
+ # Using Makefile (recommended for consistency)
1089
+ make setup # or make install
1090
+ make dev
1089
1091
 
1090
- ## 🤝 Contributing
1092
+ # Or using standard npm commands
1093
+ npm install
1094
+ npm run dev
1095
+ ```
1091
1096
 
1092
1097
  We welcome contributions! Please see our contributing guidelines for details.
1093
1098
 
1094
- ## 📄 License
1099
+ > **For AI Agents / LLMs**: Comprehensive documentation is available in the `ai/` folder with topic-specific files (basic-setup.txt, drag-drop.txt, performance.txt, etc.). Start with `ai/INDEX.txt` for navigation.
1100
+
1101
+ ## License
1095
1102
 
1096
1103
  MIT License - see LICENSE file for details.
1097
1104
 
1098
- ## 🆘 Support
1105
+ ## Support
1099
1106
 
1100
1107
  - **GitHub Issues**: [Report bugs or request features](https://github.com/keenmate/svelte-treeview/issues)
1101
- - **Documentation**: [Full documentation](https://github.com/keenmate/svelte-treeview#readme)
1108
+ - **Live demo & docs**: [svelte-treeview.keenmate.dev](https://svelte-treeview.keenmate.dev)
1102
1109
 
1103
1110
  ---
1104
1111