@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.
- package/README.md +234 -227
- package/dist/canvas/CanvasTree.svelte +1657 -0
- package/dist/canvas/CanvasTree.svelte.d.ts +148 -0
- package/dist/canvas/canvas-interaction.d.ts +67 -0
- package/dist/canvas/canvas-interaction.js +590 -0
- package/dist/canvas/canvas-layout-balanced.d.ts +18 -0
- package/dist/canvas/canvas-layout-balanced.js +212 -0
- package/dist/canvas/canvas-layout-box.d.ts +10 -0
- package/dist/canvas/canvas-layout-box.js +194 -0
- package/dist/canvas/canvas-layout-fishbone.d.ts +11 -0
- package/dist/canvas/canvas-layout-fishbone.js +349 -0
- package/dist/canvas/canvas-layout-radial.d.ts +21 -0
- package/dist/canvas/canvas-layout-radial.js +233 -0
- package/dist/canvas/canvas-layout-sunburst.d.ts +27 -0
- package/dist/canvas/canvas-layout-sunburst.js +457 -0
- package/dist/canvas/canvas-layout.d.ts +11 -0
- package/dist/canvas/canvas-layout.js +509 -0
- package/dist/canvas/canvas-renderer.d.ts +46 -0
- package/dist/canvas/canvas-renderer.js +828 -0
- package/dist/canvas/canvas-text.d.ts +11 -0
- package/dist/canvas/canvas-text.js +64 -0
- package/dist/canvas/canvas-theme.d.ts +58 -0
- package/dist/canvas/canvas-theme.js +128 -0
- package/dist/canvas/types.d.ts +187 -0
- package/dist/canvas/types.js +1 -0
- package/dist/components/Node.svelte +86 -38
- package/dist/components/Tree.svelte +265 -1234
- package/dist/components/Tree.svelte.d.ts +31 -23
- 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 +309 -0
- package/dist/core/TreeController.svelte.js +1324 -0
- package/dist/core/createTreeController.d.ts +9 -0
- package/dist/core/createTreeController.js +11 -0
- package/dist/index.d.ts +10 -1
- package/dist/index.js +9 -0
- package/dist/ltree/indexer.js +2 -1
- package/dist/ltree/ltree-node.svelte.d.ts +3 -0
- package/dist/ltree/ltree-node.svelte.js +2 -0
- package/dist/ltree/ltree.svelte.d.ts +1 -1
- package/dist/ltree/ltree.svelte.js +96 -91
- package/dist/ltree/types.d.ts +12 -5
- package/dist/styles/main.scss +58 -58
- package/dist/styles.css +26 -26
- package/dist/styles.css.map +1 -1
- 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
|
-
|
|
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
|
-
|
|
7
|
+
Browse interactive code examples and the full API reference at **[svelte-treeview.keenmate.dev](https://svelte-treeview.keenmate.dev)**
|
|
9
8
|
|
|
10
|
-
|
|
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
|
-
|
|
24
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
//
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
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
|
-
|
|
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 (~
|
|
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
|
-
##
|
|
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
|
-
|
|
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'
|
|
280
|
-
{ path: '1.1', name: 'Item 1'
|
|
281
|
-
{ path: '2', name: 'Folder 2'
|
|
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
|
-
##
|
|
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 |
|
|
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
|
-
##
|
|
618
|
+
## API Reference
|
|
584
619
|
|
|
585
620
|
### Tree Component Props
|
|
586
621
|
|
|
587
|
-
#### Core
|
|
588
|
-
| Prop | Type |
|
|
589
|
-
|
|
590
|
-
| `data` | `T[]` |
|
|
591
|
-
| `idMember` | `string` |
|
|
592
|
-
| `pathMember` | `string` |
|
|
593
|
-
| `sortCallback` | `(items: T[]) => T[]` |
|
|
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
|
-
|
|
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
|
|
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
|
-
| `
|
|
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
|
-
####
|
|
675
|
-
|
|
|
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
|
-
|
|
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(
|
|
1020
|
-
console.log(
|
|
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
|
-
##
|
|
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()
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
##
|
|
1105
|
+
## Support
|
|
1099
1106
|
|
|
1100
1107
|
- **GitHub Issues**: [Report bugs or request features](https://github.com/keenmate/svelte-treeview/issues)
|
|
1101
|
-
- **
|
|
1108
|
+
- **Live demo & docs**: [svelte-treeview.keenmate.dev](https://svelte-treeview.keenmate.dev)
|
|
1102
1109
|
|
|
1103
1110
|
---
|
|
1104
1111
|
|