@keenmate/svelte-treeview 4.8.0 → 5.0.0-rc02
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/README.md +106 -117
- package/ai/INDEX.txt +310 -0
- package/ai/advanced-patterns.txt +506 -0
- package/ai/basic-setup.txt +336 -0
- package/ai/context-menu.txt +349 -0
- package/ai/data-handling.txt +390 -0
- package/ai/drag-drop.txt +397 -0
- package/ai/events-callbacks.txt +382 -0
- package/ai/import-patterns.txt +271 -0
- package/ai/performance.txt +349 -0
- package/ai/search-features.txt +359 -0
- package/ai/styling-theming.txt +354 -0
- package/ai/tree-editing.txt +423 -0
- package/ai/typescript-types.txt +357 -0
- package/dist/components/Node.svelte +47 -40
- package/dist/components/Node.svelte.d.ts +1 -1
- package/dist/components/Tree.svelte +384 -1479
- package/dist/components/Tree.svelte.d.ts +30 -28
- package/dist/components/TreeProvider.svelte +28 -0
- package/dist/components/TreeProvider.svelte.d.ts +28 -0
- package/dist/constants.generated.d.ts +1 -1
- package/dist/constants.generated.js +1 -1
- package/dist/core/TreeController.svelte.d.ts +353 -0
- package/dist/core/TreeController.svelte.js +1503 -0
- package/dist/core/createTreeController.d.ts +9 -0
- package/dist/core/createTreeController.js +11 -0
- package/dist/global-api.d.ts +1 -1
- package/dist/global-api.js +5 -5
- package/dist/index.d.ts +10 -6
- package/dist/index.js +7 -3
- package/dist/logger.d.ts +7 -6
- package/dist/logger.js +0 -2
- package/dist/ltree/indexer.js +2 -4
- package/dist/ltree/ltree-node.svelte.d.ts +2 -1
- package/dist/ltree/ltree-node.svelte.js +1 -0
- package/dist/ltree/ltree.svelte.d.ts +1 -1
- package/dist/ltree/ltree.svelte.js +168 -175
- package/dist/ltree/types.d.ts +12 -8
- package/dist/perf-logger.d.ts +2 -1
- package/dist/perf-logger.js +0 -2
- package/dist/styles/main.scss +78 -78
- package/dist/styles.css +41 -41
- package/dist/styles.css.map +1 -1
- package/dist/vendor/loglevel/index.d.ts +55 -2
- package/dist/vendor/loglevel/prefix.d.ts +23 -2
- package/package.json +96 -95
- package/dist/ltree/ltree-demo.d.ts +0 -2
- package/dist/ltree/ltree-demo.js +0 -90
- package/dist/vendor/loglevel/loglevel-esm.d.ts +0 -2
- package/dist/vendor/loglevel/loglevel-plugin-prefix-esm.d.ts +0 -7
- package/dist/vendor/loglevel/loglevel-plugin-prefix.d.ts +0 -2
package/README.md
CHANGED
|
@@ -6,90 +6,38 @@ A high-performance, feature-rich hierarchical tree view component for Svelte 5 w
|
|
|
6
6
|
|
|
7
7
|
Browse interactive code examples and the full API reference at **[svelte-treeview.keenmate.dev](https://svelte-treeview.keenmate.dev)**
|
|
8
8
|
|
|
9
|
-
##
|
|
9
|
+
## v5.0: Core/Renderer Split + Virtual Scroll
|
|
10
10
|
|
|
11
|
-
> [!
|
|
12
|
-
> **
|
|
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 architecture is open for you to build your own custom renderers on top of the same core via `TreeProvider` and `TreeController`.
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
**Key changes in v5:**
|
|
15
|
+
- **Core/Renderer split**: Use the built-in HTML `Tree` renderer, or create custom visualizations (Canvas, WebGL, SVG) via `TreeProvider` + `TreeController`
|
|
16
|
+
- **Virtual scroll**: Render 50,000+ node trees smoothly with `virtualScroll={true}` — only ~50 DOM nodes at any time
|
|
17
|
+
- **Canvas companion**: For canvas rendering, install [`@keenmate/svelte-treeview-canvas`](https://github.com/keenmate/svelte-treeview-canvas)
|
|
18
|
+
- **Drop position naming**: `'above'`/`'below'` renamed to `'before'`/`'after'` (CSS classes and events updated accordingly)
|
|
15
19
|
|
|
16
|
-
|
|
17
|
-
|------|----------|-------------|
|
|
18
|
-
| **Recursive** | Small trees (<500 nodes) | Traditional nested Svelte components |
|
|
19
|
-
| **Progressive** (default) | Medium trees (500–10,000) | Flat rendering with exponential batching |
|
|
20
|
-
| **Virtual Scroll** | Large trees (10,000+) | Only visible rows + overscan are in the DOM |
|
|
20
|
+
### Rendering Modes
|
|
21
21
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
virtualOverscan={5}
|
|
28
|
-
virtualContainerHeight="600px"
|
|
29
|
-
/>
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
**Also in this release:**
|
|
33
|
-
- **Search result navigation** — dual-mode filter/search with result counter, prev/next (Enter/Shift+Enter), round-robin cycling
|
|
34
|
-
- **Floating drop zones auto-expand** when positions are restricted — pure CSS `:not(:has())`, zero JS overhead
|
|
35
|
-
- **Cross-tree drop positioning fixed** — above/below placement now works correctly between trees
|
|
36
|
-
- **Unified indentation & gaps** across all three rendering modes
|
|
37
|
-
- **Drag & drop disabled by default** — set `dragDropMode="both"` to enable
|
|
38
|
-
|
|
39
|
-
## v4.7: Per-Node Drop Position Restrictions
|
|
40
|
-
|
|
41
|
-
> [!NOTE]
|
|
42
|
-
> **You can now restrict which drop positions (above/below/child) are allowed per node.**
|
|
22
|
+
| Mode | Props | DOM Nodes | Best For |
|
|
23
|
+
|------|-------|-----------|----------|
|
|
24
|
+
| Recursive | `useFlatRendering={false}` | All | Small trees (<100 nodes) |
|
|
25
|
+
| Flat (default) | `useFlatRendering={true}` | All | Medium trees (100–10K) |
|
|
26
|
+
| Virtual | `virtualScroll={true}` | ~50 | Large trees (10K+) |
|
|
43
27
|
|
|
44
|
-
Use `getAllowedDropPositionsCallback` for dynamic logic or `allowedDropPositionsMember` for server data:
|
|
45
|
-
```typescript
|
|
46
|
-
// Files can only have siblings, trash only accepts children
|
|
47
|
-
function getAllowedDropPositions(node) {
|
|
48
|
-
if (node.data?.type === 'file') return ['above', 'below'];
|
|
49
|
-
if (node.data?.type === 'trash') return ['child'];
|
|
50
|
-
return undefined; // all positions allowed (default)
|
|
51
|
-
}
|
|
52
|
-
```
|
|
53
|
-
|
|
54
|
-
## v4.6: Progressive Flat Rendering
|
|
55
|
-
|
|
56
|
-
> [!NOTE]
|
|
57
|
-
> **The tree now uses progressive flat rendering by default for significantly improved performance.**
|
|
58
|
-
|
|
59
|
-
**What this means:**
|
|
60
|
-
- The tree renders immediately with the first batch of nodes (~20 by default)
|
|
61
|
-
- Remaining nodes are rendered progressively in subsequent frames
|
|
62
|
-
- For large trees (5000+ nodes), you'll see nodes appear over ~100-500ms instead of a single long freeze
|
|
63
|
-
- The UI remains responsive during rendering
|
|
64
|
-
|
|
65
|
-
**Configuration options:**
|
|
66
28
|
```svelte
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
useFlatRendering={true} <!-- Default: true (flat mode) -->
|
|
70
|
-
progressiveRender={true} <!-- Default: true (batched rendering) -->
|
|
71
|
-
initialBatchSize={20} <!-- First batch size (default: 20) -->
|
|
72
|
-
maxBatchSize={500} <!-- Maximum batch size cap (default: 500) -->
|
|
73
|
-
/>
|
|
74
|
-
```
|
|
75
|
-
|
|
76
|
-
**Exponential batching:** The first batch renders 20 nodes instantly, then doubles each frame (20 → 40 → 80 → 160 → 320 → 500...) for optimal perceived performance.
|
|
29
|
+
<!-- Virtual scroll for large trees -->
|
|
30
|
+
<Tree {data} virtualScroll={true} virtualContainerHeight="500px" />
|
|
77
31
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
<Tree
|
|
81
|
-
{data}
|
|
82
|
-
useFlatRendering={false} <!-- Uses recursive Node components -->
|
|
83
|
-
/>
|
|
32
|
+
<!-- Flat mode (default) with progressive batching -->
|
|
33
|
+
<Tree {data} progressiveRender={true} initialBatchSize={20} maxBatchSize={500} />
|
|
84
34
|
```
|
|
85
35
|
|
|
86
|
-
Recursive mode may be preferred for very small trees or when you need the `{#key changeTracker}` behavior that recreates all nodes on any change.
|
|
87
|
-
|
|
88
36
|
## Features
|
|
89
37
|
|
|
90
38
|
- **Svelte 5 Native**: Built specifically for Svelte 5 with full support for runes and modern Svelte patterns
|
|
91
|
-
- **High Performance**: Flat rendering
|
|
92
|
-
- **Drag & Drop**: Built-in drag and drop with position control (
|
|
39
|
+
- **High Performance**: Flat rendering with progressive loading, virtual scroll for 50,000+ nodes
|
|
40
|
+
- **Drag & Drop**: Built-in drag and drop with position control (before/after/child), touch support, and async validation
|
|
93
41
|
- **Tree Editing**: Built-in methods for add, move, remove operations with automatic path management
|
|
94
42
|
- **Search & Filter**: Integrated FlexSearch for fast, full-text search capabilities
|
|
95
43
|
- **Flexible Data Sources**: Works with any hierarchical data structure
|
|
@@ -175,14 +123,14 @@ import '@keenmate/svelte-treeview/styles.scss';
|
|
|
175
123
|
idMember="path"
|
|
176
124
|
pathMember="path"
|
|
177
125
|
selectedNodeClass="ltree-selected-bold"
|
|
178
|
-
onNodeClicked={(node) => console.log('Clicked:', node.data
|
|
126
|
+
onNodeClicked={(node) => console.log('Clicked:', node.data?.name)}
|
|
179
127
|
>
|
|
180
128
|
{#snippet nodeTemplate(node)}
|
|
181
129
|
<div class="d-flex align-items-center">
|
|
182
|
-
<span class="me-2">{node.data
|
|
183
|
-
<strong>{node.data
|
|
184
|
-
{#if node.data
|
|
185
|
-
<small class="text-muted ms-2">({node.data
|
|
130
|
+
<span class="me-2">{node.data?.icon}</span>
|
|
131
|
+
<strong>{node.data?.name}</strong>
|
|
132
|
+
{#if node.data?.size}
|
|
133
|
+
<small class="text-muted ms-2">({node.data?.size})</small>
|
|
186
134
|
{/if}
|
|
187
135
|
</div>
|
|
188
136
|
{/snippet}
|
|
@@ -297,13 +245,13 @@ For complete FlexSearch documentation, visit: [FlexSearch Options](https://githu
|
|
|
297
245
|
];
|
|
298
246
|
|
|
299
247
|
function onDragStart(node, event) {
|
|
300
|
-
console.log('Dragging:', node.data
|
|
248
|
+
console.log('Dragging:', node.data?.name);
|
|
301
249
|
}
|
|
302
250
|
|
|
303
251
|
// Same-tree moves are auto-handled - this callback is for notification/custom logic
|
|
304
252
|
function onDrop(dropNode, draggedNode, position, event, operation) {
|
|
305
|
-
console.log(`Dropped ${draggedNode.data
|
|
306
|
-
// position is '
|
|
253
|
+
console.log(`Dropped ${draggedNode.data?.name} ${position} ${dropNode?.data?.name}`);
|
|
254
|
+
// position is 'before', 'after', or 'child'
|
|
307
255
|
// operation is 'move' or 'copy' (Ctrl+drag)
|
|
308
256
|
}
|
|
309
257
|
</script>
|
|
@@ -323,16 +271,16 @@ For complete FlexSearch documentation, visit: [FlexSearch Options](https://githu
|
|
|
323
271
|
|
|
324
272
|
#### Drop Position Control
|
|
325
273
|
|
|
326
|
-
When using `dropZoneMode="floating"
|
|
327
|
-
- **
|
|
328
|
-
- **
|
|
274
|
+
When using `dropZoneMode="floating"`, users can choose where to drop:
|
|
275
|
+
- **Before**: Insert as sibling before the target node
|
|
276
|
+
- **After**: Insert as sibling after the target node
|
|
329
277
|
- **Child**: Insert as child of the target node
|
|
330
278
|
|
|
331
279
|
#### Per-Node Drop Position Restrictions
|
|
332
280
|
|
|
333
281
|
You can restrict which drop positions are allowed per node. This is useful for:
|
|
334
|
-
- **Trash/Recycle Bin**: Only allow dropping INTO (child), not
|
|
335
|
-
- **Files**: Only allow
|
|
282
|
+
- **Trash/Recycle Bin**: Only allow dropping INTO (child), not before/after
|
|
283
|
+
- **Files**: Only allow before/after (can't drop INTO a file)
|
|
336
284
|
- **Folders**: Allow all positions (default)
|
|
337
285
|
|
|
338
286
|
```svelte
|
|
@@ -341,7 +289,7 @@ You can restrict which drop positions are allowed per node. This is useful for:
|
|
|
341
289
|
|
|
342
290
|
// Dynamic callback approach
|
|
343
291
|
function getAllowedDropPositions(node: LTreeNode<MyItem>): DropPosition[] | null {
|
|
344
|
-
if (node.data?.type === 'file') return ['
|
|
292
|
+
if (node.data?.type === 'file') return ['before', 'after'];
|
|
345
293
|
if (node.data?.type === 'trash') return ['child'];
|
|
346
294
|
return undefined; // all positions allowed
|
|
347
295
|
}
|
|
@@ -383,7 +331,7 @@ Use `beforeDropCallback` to validate or modify drops, including async operations
|
|
|
383
331
|
if (position === 'child' && !dropNode.data.isFolder) {
|
|
384
332
|
const confirmed = await showConfirmDialog('Drop as sibling instead?');
|
|
385
333
|
if (!confirmed) return false;
|
|
386
|
-
return { position: '
|
|
334
|
+
return { position: 'after' }; // Override position
|
|
387
335
|
}
|
|
388
336
|
|
|
389
337
|
// Proceed normally
|
|
@@ -424,7 +372,7 @@ The tree provides built-in methods for programmatic editing:
|
|
|
424
372
|
const siblings = treeRef.getSiblings(selectedNode.path);
|
|
425
373
|
const index = siblings.findIndex(s => s.path === selectedNode.path);
|
|
426
374
|
if (index > 0) {
|
|
427
|
-
treeRef.moveNode(selectedNode.path, siblings[index - 1].path, '
|
|
375
|
+
treeRef.moveNode(selectedNode.path, siblings[index - 1].path, 'before');
|
|
428
376
|
}
|
|
429
377
|
}
|
|
430
378
|
|
|
@@ -443,7 +391,7 @@ The tree provides built-in methods for programmatic editing:
|
|
|
443
391
|
/>
|
|
444
392
|
```
|
|
445
393
|
|
|
446
|
-
**Note**: When using `orderMember`, the tree automatically calculates sort order values when moving nodes with '
|
|
394
|
+
**Note**: When using `orderMember`, the tree automatically calculates sort order values when moving nodes with 'before' or 'after' positions.
|
|
447
395
|
|
|
448
396
|
### With Context Menus
|
|
449
397
|
|
|
@@ -462,30 +410,30 @@ The tree supports context menus with two approaches: callback-based (recommended
|
|
|
462
410
|
{ path: '2', name: 'Images', type: 'folder', canEdit: false, canDelete: true }
|
|
463
411
|
];
|
|
464
412
|
|
|
465
|
-
function createContextMenu(node): ContextMenuItem[] {
|
|
413
|
+
function createContextMenu(node, closeMenu: () => void): ContextMenuItem[] {
|
|
466
414
|
const items: ContextMenuItem[] = [];
|
|
467
415
|
|
|
468
416
|
// Always available
|
|
469
417
|
items.push({
|
|
470
418
|
icon: '📂',
|
|
471
419
|
title: 'Open',
|
|
472
|
-
callback: () => alert(`Opening ${node.data
|
|
420
|
+
callback: () => alert(`Opening ${node.data?.name}`)
|
|
473
421
|
});
|
|
474
422
|
|
|
475
423
|
// Conditional actions based on node data
|
|
476
|
-
if (node.data
|
|
424
|
+
if (node.data?.canEdit) {
|
|
477
425
|
items.push({
|
|
478
426
|
icon: '✏️',
|
|
479
427
|
title: 'Edit',
|
|
480
|
-
callback: () => alert(`Editing ${node.data
|
|
428
|
+
callback: () => alert(`Editing ${node.data?.name}`)
|
|
481
429
|
});
|
|
482
430
|
}
|
|
483
431
|
|
|
484
|
-
if (node.data
|
|
432
|
+
if (node.data?.canDelete) {
|
|
485
433
|
items.push({
|
|
486
434
|
icon: '🗑️',
|
|
487
435
|
title: 'Delete',
|
|
488
|
-
callback: () => confirm(`Delete ${node.data
|
|
436
|
+
callback: () => confirm(`Delete ${node.data?.name}?`) && alert('Deleted!')
|
|
489
437
|
});
|
|
490
438
|
}
|
|
491
439
|
|
|
@@ -523,11 +471,11 @@ The tree supports context menus with two approaches: callback-based (recommended
|
|
|
523
471
|
pathMember="path"
|
|
524
472
|
>
|
|
525
473
|
{#snippet contextMenu(node, closeMenu)}
|
|
526
|
-
<div class="context-menu-item" onclick={() => { alert(`Open ${node.data
|
|
474
|
+
<div class="context-menu-item" onclick={() => { alert(`Open ${node.data?.name}`); closeMenu(); }}>
|
|
527
475
|
📂 Open
|
|
528
476
|
</div>
|
|
529
477
|
<div class="context-menu-divider"></div>
|
|
530
|
-
<div class="context-menu-item" onclick={() => { alert(`Delete ${node.data
|
|
478
|
+
<div class="context-menu-item" onclick={() => { alert(`Delete ${node.data?.name}`); closeMenu(); }}>
|
|
531
479
|
🗑️ Delete
|
|
532
480
|
</div>
|
|
533
481
|
{/snippet}
|
|
@@ -645,12 +593,11 @@ The component includes several pre-built classes for styling selected nodes:
|
|
|
645
593
|
| `data` | `T[]` | **required** | Array of data objects |
|
|
646
594
|
| `idMember` | `string` | **required** | Property name for unique identifiers |
|
|
647
595
|
| `pathMember` | `string` | **required** | Property name for hierarchical paths |
|
|
648
|
-
| `sortCallback` | `(items: T[]) => T[]` |
|
|
596
|
+
| `sortCallback` | `(items: LTreeNode<T>[]) => LTreeNode<T>[]` | `undefined` | Function to sort tree nodes |
|
|
649
597
|
|
|
650
598
|
#### Data Mapping Properties
|
|
651
599
|
| Prop | Type | Default | Description |
|
|
652
600
|
|------|------|---------|-------------|
|
|
653
|
-
| `treeId` | `string \| null` | `null` | Unique identifier for the tree |
|
|
654
601
|
| `parentPathMember` | `string \| null` | `null` | Property name for parent path references |
|
|
655
602
|
| `levelMember` | `string \| null` | `null` | Property name for node level |
|
|
656
603
|
| `isExpandedMember` | `string \| null` | `null` | Property name for expanded state |
|
|
@@ -658,6 +605,9 @@ The component includes several pre-built classes for styling selected nodes:
|
|
|
658
605
|
| `isDraggableMember` | `string \| null` | `null` | Property name for draggable state |
|
|
659
606
|
| `isDropAllowedMember` | `string \| null` | `null` | Property name for drop allowed state |
|
|
660
607
|
| `allowedDropPositionsMember` | `string \| null` | `null` | Property name for allowed drop positions array |
|
|
608
|
+
| `isCollapsibleMember` | `string \| null` | `null` | Property name for collapsible state |
|
|
609
|
+
| `getIsCollapsibleCallback` | `(node) => boolean` | `undefined` | Callback to determine if a node is collapsible |
|
|
610
|
+
| `getIsDraggableCallback` | `(node) => boolean` | `undefined` | Callback to determine if a node is draggable |
|
|
661
611
|
| `hasChildrenMember` | `string \| null` | `null` | Property name for children existence |
|
|
662
612
|
| `isSorted` | `boolean \| null` | `null` | Whether items should be sorted |
|
|
663
613
|
|
|
@@ -668,7 +618,7 @@ The component includes several pre-built classes for styling selected nodes:
|
|
|
668
618
|
| `getDisplayValueCallback` | `(node) => string` | `undefined` | Function to get display value |
|
|
669
619
|
| `searchValueMember` | `string \| null` | `null` | Property name for search indexing |
|
|
670
620
|
| `getSearchValueCallback` | `(node) => string` | `undefined` | Function to get search value |
|
|
671
|
-
| `shouldUseInternalSearchIndex` | `boolean` | `
|
|
621
|
+
| `shouldUseInternalSearchIndex` | `boolean` | `true` | Enable built-in search functionality |
|
|
672
622
|
| `initializeIndexCallback` | `() => Index` | `undefined` | Function to initialize search index |
|
|
673
623
|
| `searchText` | `string` (bindable) | `undefined` | Current search text |
|
|
674
624
|
|
|
@@ -697,9 +647,10 @@ Without both requirements, no search indexing will occur.
|
|
|
697
647
|
|------|------|---------|-------------|
|
|
698
648
|
| `expandLevel` | `number \| null` | `2` | Automatically expand nodes up to this level |
|
|
699
649
|
| `shouldToggleOnNodeClick` | `boolean` | `true` | Toggle expansion on node click |
|
|
700
|
-
| `orderMember` | `string \| null` | `null` | Property name for sort order (enables
|
|
650
|
+
| `orderMember` | `string \| null` | `null` | Property name for sort order (enables before/after positioning in drag-drop) |
|
|
701
651
|
| `indexerBatchSize` | `number \| null` | `25` | Number of nodes to process per batch during search indexing |
|
|
702
652
|
| `indexerTimeout` | `number \| null` | `50` | Maximum time (ms) to wait for idle callback before forcing indexing |
|
|
653
|
+
| `isLoading` | `boolean` | `false` | Show loading placeholder instead of tree content |
|
|
703
654
|
| `shouldDisplayDebugInformation` | `boolean` | `false` | Show debug information panel with tree statistics and enable console debug logging |
|
|
704
655
|
| `shouldDisplayContextMenuInDebugMode` | `boolean` | `false` | Display persistent context menu at fixed position for styling development |
|
|
705
656
|
|
|
@@ -710,6 +661,14 @@ Without both requirements, no search indexing will occur.
|
|
|
710
661
|
| `progressiveRender` | `boolean` | `true` | Progressively render nodes in batches |
|
|
711
662
|
| `initialBatchSize` | `number` | `20` | First batch size for progressive rendering |
|
|
712
663
|
| `maxBatchSize` | `number` | `500` | Maximum batch size cap |
|
|
664
|
+
| `virtualScroll` | `boolean` | `false` | Enable virtual scrolling (flat mode only, renders visible + overscan rows) |
|
|
665
|
+
| `virtualRowHeight` | `number` | auto | Explicit row height in px (auto-measured from first row if not set) |
|
|
666
|
+
| `virtualOverscan` | `number` | `5` | Extra rows rendered above/below viewport |
|
|
667
|
+
| `virtualContainerHeight` | `string` | auto/`'400px'` | CSS height for scroll container (auto-detected from parent if not set) |
|
|
668
|
+
| `isRendering` | `boolean` (bindable) | `false` | Whether the tree is currently rendering (useful for progress indicators) |
|
|
669
|
+
| `onRenderStart` | `() => void` | `undefined` | Called when progressive rendering begins |
|
|
670
|
+
| `onRenderProgress` | `(rendered: number, total: number) => void` | `undefined` | Called after each batch with progress info |
|
|
671
|
+
| `onRenderComplete` | `() => void` | `undefined` | Called when progressive rendering finishes |
|
|
713
672
|
|
|
714
673
|
#### Drag & Drop Properties
|
|
715
674
|
| Prop | Type | Default | Description |
|
|
@@ -731,7 +690,7 @@ Without both requirements, no search indexing will occur.
|
|
|
731
690
|
| `onNodeClicked` | `(node) => void` | `undefined` | Node click event handler |
|
|
732
691
|
| `onNodeDragStart` | `(node, event) => void` | `undefined` | Drag start event handler |
|
|
733
692
|
| `onNodeDragOver` | `(node, event) => void` | `undefined` | Drag over event handler |
|
|
734
|
-
| `onNodeDrop` | `(dropNode, draggedNode, position, event, operation) => void` | `undefined` | Drop event handler. Position is `'
|
|
693
|
+
| `onNodeDrop` | `(dropNode, draggedNode, position, event, operation) => void` | `undefined` | Drop event handler. `dropNode` can be `null` (e.g., drop on empty tree). Position is `'before'`, `'after'`, or `'child'`. Operation is `'move'` or `'copy'` |
|
|
735
694
|
|
|
736
695
|
#### Visual Styling Properties
|
|
737
696
|
| Prop | Type | Default | Description |
|
|
@@ -750,9 +709,10 @@ Without both requirements, no search indexing will occur.
|
|
|
750
709
|
|---------|------------|-------------|
|
|
751
710
|
| `nodeTemplate` | `(node)` | Custom node template |
|
|
752
711
|
| `treeHeader` | | Tree header content |
|
|
753
|
-
| `treeBody` | | Tree body content |
|
|
754
712
|
| `treeFooter` | | Tree footer content |
|
|
755
|
-
| `noDataFound` | |
|
|
713
|
+
| `noDataFound` | | Content shown when tree has no data |
|
|
714
|
+
| `dropPlaceholder` | | Content shown in empty drop target tree |
|
|
715
|
+
| `loadingPlaceholder` | | Content shown while `isLoading` is true |
|
|
756
716
|
| `contextMenu` | `(node, closeMenu)` | Context menu template |
|
|
757
717
|
|
|
758
718
|
#### Public Methods
|
|
@@ -767,19 +727,31 @@ Without both requirements, no search indexing will occur.
|
|
|
767
727
|
| `scrollToPath` | `path: string, options?: ScrollToPathOptions` | Scroll to and highlight a specific node |
|
|
768
728
|
| `update` | `updates: Partial<Props>` | Programmatically update component props from external JavaScript |
|
|
769
729
|
| `addNode` | `parentPath: string, data: T, pathSegment?: string` | Add a new node under the specified parent |
|
|
770
|
-
| `moveNode` | `sourcePath: string, targetPath: string, position: '
|
|
730
|
+
| `moveNode` | `sourcePath: string, targetPath: string, position: 'before' \| 'after' \| 'child'` | Move a node to a new location |
|
|
771
731
|
| `removeNode` | `path: string, includeDescendants?: boolean` | Remove a node (and optionally its descendants) |
|
|
772
732
|
| `getNodeByPath` | `path: string` | Get a node by its path |
|
|
773
733
|
| `getChildren` | `parentPath: string` | Get direct children of a node |
|
|
774
734
|
| `getSiblings` | `path: string` | Get siblings of a node (including itself) |
|
|
735
|
+
| `updateNode` | `path: string, data: Partial<T>` | Update a node's data properties |
|
|
736
|
+
| `copyNodeWithDescendants` | `sourcePath: string, targetPath: string, position: DropPosition` | Copy a node and its subtree to a new location |
|
|
737
|
+
| `refreshNode` | `path: string` | Force re-render of a specific node |
|
|
738
|
+
| `refreshSiblings` | `path: string` | Force re-render of a node's siblings |
|
|
739
|
+
| `getExpandedPaths` | | Get array of all currently expanded node paths |
|
|
740
|
+
| `setExpandedPaths` | `paths: string[]` | Restore expanded state from saved paths |
|
|
741
|
+
| `getAllData` | | Get all tree data as a flat array |
|
|
742
|
+
| `applyChanges` | | Apply pending changes and refresh the tree |
|
|
743
|
+
| `closeContextMenu` | | Programmatically close the context menu |
|
|
775
744
|
|
|
776
745
|
#### ScrollToPath Options
|
|
777
746
|
|
|
778
747
|
| Option | Type | Default | Description |
|
|
779
748
|
|--------|------|---------|-------------|
|
|
780
749
|
| `expand` | `boolean` | `true` | Automatically expand parent nodes to make target visible |
|
|
750
|
+
| `expandTarget` | `boolean` | `false` | Also expand the target node itself (not just its ancestors) |
|
|
781
751
|
| `highlight` | `boolean` | `true` | Apply temporary highlight animation to the target node |
|
|
782
752
|
| `scrollOptions` | `ScrollIntoViewOptions` | `{ behavior: 'smooth', block: 'center' }` | Native browser scroll options |
|
|
753
|
+
| `containerScroll` | `boolean` | `false` | Scroll only within nearest scrollable ancestor (prevents page scroll) |
|
|
754
|
+
| `containerElement` | `HTMLElement` | `undefined` | Explicit scrollable container element to use for scrolling |
|
|
783
755
|
|
|
784
756
|
**Usage Example:**
|
|
785
757
|
```typescript
|
|
@@ -795,6 +767,9 @@ await tree.scrollToPath('1.2.3', {
|
|
|
795
767
|
block: 'start'
|
|
796
768
|
}
|
|
797
769
|
});
|
|
770
|
+
|
|
771
|
+
// Scroll within a scrollable container (prevents page scroll)
|
|
772
|
+
await tree.scrollToPath('1.2.3', { containerScroll: true });
|
|
798
773
|
```
|
|
799
774
|
|
|
800
775
|
**Highlight Classes Example:**
|
|
@@ -958,7 +933,7 @@ const sortCallback = (items: LTreeNode<T>[]) => {
|
|
|
958
933
|
}
|
|
959
934
|
|
|
960
935
|
// Then sort by your custom criteria
|
|
961
|
-
return a.data
|
|
936
|
+
return (a.data?.name ?? '').localeCompare(b.data?.name ?? '');
|
|
962
937
|
});
|
|
963
938
|
};
|
|
964
939
|
```
|
|
@@ -1029,36 +1004,40 @@ interface InsertArrayResult<T> {
|
|
|
1029
1004
|
|
|
1030
1005
|
The component is optimized for large datasets:
|
|
1031
1006
|
|
|
1007
|
+
- **Virtual Scroll**: Renders only visible rows (~50 DOM nodes) for trees with 50,000+ nodes
|
|
1032
1008
|
- **Flat Rendering Mode**: Single `{#each}` loop instead of recursive components (default, ~12x faster initial render)
|
|
1033
1009
|
- **Progressive Rendering**: Batched rendering prevents UI freeze during initial load
|
|
1034
|
-
- **Context-Based Callbacks**: Stable function references eliminate unnecessary re-renders
|
|
1035
|
-
- **LTree**: Efficient hierarchical data structure
|
|
1036
1010
|
- **Async Search Indexing**: Uses `requestIdleCallback` for non-blocking search index building
|
|
1037
|
-
- **
|
|
1038
|
-
- **Search Indexing**: Uses FlexSearch for fast search operations
|
|
1011
|
+
- **LTree**: Efficient hierarchical data structure with FlexSearch integration
|
|
1039
1012
|
|
|
1040
1013
|
### Performance Benchmarks (5500 nodes)
|
|
1041
1014
|
|
|
1042
1015
|
| Operation | Time |
|
|
1043
1016
|
|-----------|------|
|
|
1044
|
-
| Initial render | ~25ms |
|
|
1017
|
+
| Initial render (flat) | ~25ms |
|
|
1018
|
+
| Initial render (virtual) | ~5ms |
|
|
1045
1019
|
| Expand/collapse | ~100-150ms |
|
|
1046
1020
|
| Search filtering | <50ms |
|
|
1021
|
+
| insertArray | <100ms |
|
|
1022
|
+
|
|
1023
|
+
### Virtual Scroll
|
|
1047
1024
|
|
|
1048
|
-
|
|
1025
|
+
For trees with 10,000+ nodes, enable virtual scroll to keep DOM size constant:
|
|
1049
1026
|
|
|
1050
|
-
**Flat Rendering Mode** (default) - Renders all visible nodes in a single loop:
|
|
1051
1027
|
```svelte
|
|
1052
1028
|
<Tree
|
|
1053
1029
|
{data}
|
|
1054
|
-
|
|
1055
|
-
|
|
1030
|
+
virtualScroll={true}
|
|
1031
|
+
virtualContainerHeight="500px"
|
|
1032
|
+
virtualOverscan={5}
|
|
1056
1033
|
/>
|
|
1057
1034
|
```
|
|
1058
1035
|
|
|
1059
|
-
|
|
1036
|
+
Virtual scroll auto-measures row height from the first rendered node. Override with `virtualRowHeight={32}` if needed. Requires flat rendering mode (the default).
|
|
1037
|
+
|
|
1038
|
+
### Performance Logging
|
|
1060
1039
|
|
|
1061
|
-
|
|
1040
|
+
Built-in performance measurement for debugging:
|
|
1062
1041
|
```typescript
|
|
1063
1042
|
import { enablePerfLogging } from '@keenmate/svelte-treeview';
|
|
1064
1043
|
enablePerfLogging();
|
|
@@ -1069,6 +1048,16 @@ window.components['svelte-treeview'].perf.enable()
|
|
|
1069
1048
|
|
|
1070
1049
|
**Important**: See the [$state.raw() tip](#quick-start) above - using `$state()` instead of `$state.raw()` for tree data can cause 5,000x slowdown!
|
|
1071
1050
|
|
|
1051
|
+
## CanvasTree (Canvas-Based Rendering)
|
|
1052
|
+
|
|
1053
|
+
Canvas rendering is available as a separate companion package: [`@keenmate/svelte-treeview-canvas`](https://github.com/keenmate/svelte-treeview-canvas)
|
|
1054
|
+
|
|
1055
|
+
It renders trees on HTML5 Canvas for high-performance visualization with multiple layout modes (tree, balanced, fishbone, radial, box), keyboard navigation, drag & drop, and custom node rendering. Install it separately:
|
|
1056
|
+
|
|
1057
|
+
```bash
|
|
1058
|
+
npm install @keenmate/svelte-treeview-canvas
|
|
1059
|
+
```
|
|
1060
|
+
|
|
1072
1061
|
## Development Setup & Contributing
|
|
1073
1062
|
|
|
1074
1063
|
For developers working on the project, you can use either standard npm commands or the provided Makefile:
|