@keenmate/svelte-treeview 4.6.0 → 4.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +218 -222
- package/dist/components/Node.svelte +89 -72
- package/dist/components/Node.svelte.d.ts +1 -1
- package/dist/components/Tree.svelte +422 -171
- package/dist/components/Tree.svelte.d.ts +37 -13
- package/dist/constants.generated.d.ts +1 -1
- package/dist/constants.generated.js +1 -1
- package/dist/ltree/indexer.js +5 -2
- 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 +120 -73
- package/dist/ltree/types.d.ts +9 -3
- package/dist/styles/main.scss +53 -6
- package/dist/styles.css +43 -6
- package/dist/styles.css.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,94 +2,62 @@
|
|
|
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
|
+
## New in v4.8: Virtual Scroll & Unified Rendering
|
|
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
|
-
```
|
|
11
|
+
> [!NOTE]
|
|
12
|
+
> **New virtual scroll mode renders only visible nodes — handle 50,000+ items without DOM bloat.**
|
|
40
13
|
|
|
41
|
-
|
|
14
|
+
Three rendering modes, one consistent look:
|
|
42
15
|
|
|
43
|
-
|
|
16
|
+
| Mode | Best for | How it works |
|
|
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 |
|
|
44
21
|
|
|
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
22
|
```svelte
|
|
53
|
-
<
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
```javascript
|
|
61
|
-
import '@keenmate/svelte-treeview/styles.scss';
|
|
23
|
+
<Tree
|
|
24
|
+
{data}
|
|
25
|
+
virtualScroll={true}
|
|
26
|
+
virtualRowHeight={28}
|
|
27
|
+
virtualOverscan={5}
|
|
28
|
+
virtualContainerHeight="600px"
|
|
29
|
+
/>
|
|
62
30
|
```
|
|
63
31
|
|
|
64
|
-
|
|
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
|
|
65
38
|
|
|
66
|
-
|
|
67
|
-
> **When passing large arrays (1000+ items) to the Tree component, use `$state.raw()` instead of `$state()` to avoid severe performance issues.**
|
|
39
|
+
## v4.7: Per-Node Drop Position Restrictions
|
|
68
40
|
|
|
69
|
-
|
|
41
|
+
> [!NOTE]
|
|
42
|
+
> **You can now restrict which drop positions (above/below/child) are allowed per node.**
|
|
70
43
|
|
|
44
|
+
Use `getAllowedDropPositionsCallback` for dynamic logic or `allowedDropPositionsMember` for server data:
|
|
71
45
|
```typescript
|
|
72
|
-
//
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
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
|
+
}
|
|
77
52
|
```
|
|
78
53
|
|
|
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
|
|
54
|
+
## v4.6: Progressive Flat Rendering
|
|
87
55
|
|
|
88
56
|
> [!NOTE]
|
|
89
57
|
> **The tree now uses progressive flat rendering by default for significantly improved performance.**
|
|
90
58
|
|
|
91
59
|
**What this means:**
|
|
92
|
-
- The tree renders immediately with the first batch of nodes (~
|
|
60
|
+
- The tree renders immediately with the first batch of nodes (~20 by default)
|
|
93
61
|
- Remaining nodes are rendered progressively in subsequent frames
|
|
94
62
|
- For large trees (5000+ nodes), you'll see nodes appear over ~100-500ms instead of a single long freeze
|
|
95
63
|
- The UI remains responsive during rendering
|
|
@@ -117,12 +85,47 @@ The array itself remains reactive - only individual items lose deep reactivity (
|
|
|
117
85
|
|
|
118
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.
|
|
119
87
|
|
|
120
|
-
##
|
|
88
|
+
## Features
|
|
89
|
+
|
|
90
|
+
- **Svelte 5 Native**: Built specifically for Svelte 5 with full support for runes and modern Svelte patterns
|
|
91
|
+
- **High Performance**: Flat rendering mode with progressive loading for 5000+ nodes
|
|
92
|
+
- **Drag & Drop**: Built-in drag and drop with position control (above/below/child), touch support, and async validation
|
|
93
|
+
- **Tree Editing**: Built-in methods for add, move, remove operations with automatic path management
|
|
94
|
+
- **Search & Filter**: Integrated FlexSearch for fast, full-text search capabilities
|
|
95
|
+
- **Flexible Data Sources**: Works with any hierarchical data structure
|
|
96
|
+
- **Context Menus**: Dynamic right-click menus with callback-based generation, icons, disabled states
|
|
97
|
+
- **Visual Customization**: Extensive styling options and icon customization
|
|
98
|
+
- **TypeScript Support**: Full TypeScript support with comprehensive type definitions
|
|
99
|
+
- **Accessibility**: Built with accessibility in mind
|
|
100
|
+
|
|
101
|
+
## Installation
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
npm install @keenmate/svelte-treeview
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### Importing Styles
|
|
108
|
+
|
|
109
|
+
The component requires CSS to display correctly. Import the styles in your app:
|
|
110
|
+
|
|
111
|
+
**JavaScript import** (in your main.js/main.ts or Vite/Webpack entry):
|
|
112
|
+
```javascript
|
|
113
|
+
import '@keenmate/svelte-treeview/styles.scss';
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
**Svelte component import:**
|
|
117
|
+
```svelte
|
|
118
|
+
<style>
|
|
119
|
+
@import '@keenmate/svelte-treeview/styles.scss';
|
|
120
|
+
</style>
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
## Quick Start
|
|
121
124
|
|
|
122
125
|
```svelte
|
|
123
126
|
<script lang="ts">
|
|
124
127
|
import { Tree } from '@keenmate/svelte-treeview';
|
|
125
|
-
|
|
128
|
+
|
|
126
129
|
const data = [
|
|
127
130
|
{ path: '1', name: 'Documents', type: 'folder' },
|
|
128
131
|
{ path: '1.1', name: 'Projects', type: 'folder' },
|
|
@@ -141,14 +144,24 @@ Recursive mode may be preferred for very small trees or when you need the `{#key
|
|
|
141
144
|
/>
|
|
142
145
|
```
|
|
143
146
|
|
|
144
|
-
|
|
147
|
+
> [!TIP]
|
|
148
|
+
> **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).
|
|
149
|
+
> ```typescript
|
|
150
|
+
> // BAD - Each item becomes a Proxy
|
|
151
|
+
> let treeData = $state<TreeNode[]>([])
|
|
152
|
+
>
|
|
153
|
+
> // GOOD - Items remain plain objects
|
|
154
|
+
> let treeData = $state.raw<TreeNode[]>([])
|
|
155
|
+
> ```
|
|
156
|
+
|
|
157
|
+
## Advanced Usage
|
|
145
158
|
|
|
146
159
|
### With Custom Node Templates
|
|
147
160
|
|
|
148
161
|
```svelte
|
|
149
162
|
<script lang="ts">
|
|
150
163
|
import { Tree } from '@keenmate/svelte-treeview';
|
|
151
|
-
|
|
164
|
+
|
|
152
165
|
const fileData = [
|
|
153
166
|
{ path: '1', name: 'Documents', type: 'folder', icon: '📁' },
|
|
154
167
|
{ path: '1.1', name: 'report.pdf', type: 'file', icon: '📄', size: '2.3 MB' },
|
|
@@ -181,15 +194,15 @@ Recursive mode may be preferred for very small trees or when you need the `{#key
|
|
|
181
194
|
```svelte
|
|
182
195
|
<script lang="ts">
|
|
183
196
|
import { Tree } from '@keenmate/svelte-treeview';
|
|
184
|
-
|
|
197
|
+
|
|
185
198
|
let searchText = $state('');
|
|
186
199
|
const data = [/* your data */];
|
|
187
200
|
</script>
|
|
188
201
|
|
|
189
|
-
<input
|
|
190
|
-
type="text"
|
|
191
|
-
placeholder="Search..."
|
|
192
|
-
bind:value={searchText}
|
|
202
|
+
<input
|
|
203
|
+
type="text"
|
|
204
|
+
placeholder="Search..."
|
|
205
|
+
bind:value={searchText}
|
|
193
206
|
/>
|
|
194
207
|
|
|
195
208
|
<Tree
|
|
@@ -208,29 +221,29 @@ Recursive mode may be preferred for very small trees or when you need the `{#key
|
|
|
208
221
|
<script lang="ts">
|
|
209
222
|
import { Tree } from '@keenmate/svelte-treeview';
|
|
210
223
|
import type { SearchOptions } from 'flexsearch';
|
|
211
|
-
|
|
224
|
+
|
|
212
225
|
let treeRef;
|
|
213
226
|
const data = [/* your data */];
|
|
214
|
-
|
|
227
|
+
|
|
215
228
|
// Programmatic search with FlexSearch options
|
|
216
229
|
function performAdvancedSearch(searchTerm: string) {
|
|
217
230
|
const searchOptions: SearchOptions = {
|
|
218
231
|
suggest: true, // Enable suggestions for typos
|
|
219
|
-
limit: 10, // Limit results to 10 items
|
|
232
|
+
limit: 10, // Limit results to 10 items
|
|
220
233
|
bool: "and" // Use AND logic for multiple terms
|
|
221
234
|
};
|
|
222
|
-
|
|
235
|
+
|
|
223
236
|
const results = treeRef.searchNodes(searchTerm, searchOptions);
|
|
224
237
|
console.log('Advanced search results:', results);
|
|
225
238
|
}
|
|
226
|
-
|
|
239
|
+
|
|
227
240
|
// Programmatic filtering with options
|
|
228
241
|
function filterWithOptions(searchTerm: string) {
|
|
229
242
|
const searchOptions: SearchOptions = {
|
|
230
243
|
threshold: 0.8, // Similarity threshold
|
|
231
244
|
depth: 2 // Search depth
|
|
232
245
|
};
|
|
233
|
-
|
|
246
|
+
|
|
234
247
|
treeRef.filterNodes(searchTerm, searchOptions);
|
|
235
248
|
}
|
|
236
249
|
</script>
|
|
@@ -269,6 +282,8 @@ For complete FlexSearch documentation, visit: [FlexSearch Options](https://githu
|
|
|
269
282
|
|
|
270
283
|
### With Drag & Drop
|
|
271
284
|
|
|
285
|
+
**Note:** Drag and drop is disabled by default. Set `dragDropMode` to enable it.
|
|
286
|
+
|
|
272
287
|
```svelte
|
|
273
288
|
<script lang="ts">
|
|
274
289
|
import { Tree } from '@keenmate/svelte-treeview';
|
|
@@ -276,9 +291,9 @@ For complete FlexSearch documentation, visit: [FlexSearch Options](https://githu
|
|
|
276
291
|
let treeRef: Tree<MyNode>;
|
|
277
292
|
|
|
278
293
|
const data = [
|
|
279
|
-
{ path: '1', name: 'Folder 1'
|
|
280
|
-
{ path: '1.1', name: 'Item 1'
|
|
281
|
-
{ path: '2', name: 'Folder 2'
|
|
294
|
+
{ path: '1', name: 'Folder 1' },
|
|
295
|
+
{ path: '1.1', name: 'Item 1' },
|
|
296
|
+
{ path: '2', name: 'Folder 2' }
|
|
282
297
|
];
|
|
283
298
|
|
|
284
299
|
function onDragStart(node, event) {
|
|
@@ -298,6 +313,7 @@ For complete FlexSearch documentation, visit: [FlexSearch Options](https://githu
|
|
|
298
313
|
{data}
|
|
299
314
|
idMember="path"
|
|
300
315
|
pathMember="path"
|
|
316
|
+
dragDropMode="both"
|
|
301
317
|
orderMember="sortOrder"
|
|
302
318
|
dragOverNodeClass="ltree-dragover-highlight"
|
|
303
319
|
onNodeDragStart={onDragStart}
|
|
@@ -312,6 +328,45 @@ When using `dropZoneMode="floating"` (default), users can choose where to drop:
|
|
|
312
328
|
- **Below**: Insert as sibling after the target node
|
|
313
329
|
- **Child**: Insert as child of the target node
|
|
314
330
|
|
|
331
|
+
#### Per-Node Drop Position Restrictions
|
|
332
|
+
|
|
333
|
+
You can restrict which drop positions are allowed per node. This is useful for:
|
|
334
|
+
- **Trash/Recycle Bin**: Only allow dropping INTO (child), not above/below
|
|
335
|
+
- **Files**: Only allow above/below (can't drop INTO a file)
|
|
336
|
+
- **Folders**: Allow all positions (default)
|
|
337
|
+
|
|
338
|
+
```svelte
|
|
339
|
+
<script lang="ts">
|
|
340
|
+
import { Tree, type DropPosition, type LTreeNode } from '@keenmate/svelte-treeview';
|
|
341
|
+
|
|
342
|
+
// Dynamic callback approach
|
|
343
|
+
function getAllowedDropPositions(node: LTreeNode<MyItem>): DropPosition[] | null {
|
|
344
|
+
if (node.data?.type === 'file') return ['above', 'below'];
|
|
345
|
+
if (node.data?.type === 'trash') return ['child'];
|
|
346
|
+
return undefined; // all positions allowed
|
|
347
|
+
}
|
|
348
|
+
</script>
|
|
349
|
+
|
|
350
|
+
<Tree
|
|
351
|
+
{data}
|
|
352
|
+
getAllowedDropPositionsCallback={getAllowedDropPositions}
|
|
353
|
+
/>
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
Or use the member approach for server-side data:
|
|
357
|
+
```svelte
|
|
358
|
+
<Tree
|
|
359
|
+
{data}
|
|
360
|
+
allowedDropPositionsMember="allowedDropPositions"
|
|
361
|
+
/>
|
|
362
|
+
|
|
363
|
+
<!-- Where data items have: { allowedDropPositions: ['child'] } -->
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
When restrictions are applied:
|
|
367
|
+
- **Glow mode**: Snaps to the nearest allowed position
|
|
368
|
+
- **Floating mode**: Only renders buttons for allowed positions
|
|
369
|
+
|
|
315
370
|
#### Async Drop Validation
|
|
316
371
|
|
|
317
372
|
Use `beforeDropCallback` to validate or modify drops, including async operations like confirmation dialogs:
|
|
@@ -488,7 +543,7 @@ The tree supports context menus with two approaches: callback-based (recommended
|
|
|
488
543
|
- **Auto-close**: Closes on scroll, click outside, or programmatically
|
|
489
544
|
- **Type safety**: Full TypeScript support with `ContextMenuItem` interface
|
|
490
545
|
|
|
491
|
-
##
|
|
546
|
+
## Styling and Customization
|
|
492
547
|
|
|
493
548
|
The component comes with default styles that provide a clean, modern look. You can customize it extensively:
|
|
494
549
|
|
|
@@ -558,7 +613,7 @@ The component includes several pre-built classes for styling selected nodes:
|
|
|
558
613
|
|-------|-------------|---------------|
|
|
559
614
|
| `ltree-selected-bold` | Bold text with primary color | **Bold text** in theme primary color |
|
|
560
615
|
| `ltree-selected-border` | Border and background highlight | Solid border with light background |
|
|
561
|
-
| `ltree-selected-brackets` | Decorative brackets around text |
|
|
616
|
+
| `ltree-selected-brackets` | Decorative brackets around text | > **Node Text** < |
|
|
562
617
|
|
|
563
618
|
**Available Drag-over Node Classes:**
|
|
564
619
|
|
|
@@ -580,17 +635,17 @@ The component includes several pre-built classes for styling selected nodes:
|
|
|
580
635
|
/>
|
|
581
636
|
```
|
|
582
637
|
|
|
583
|
-
##
|
|
638
|
+
## API Reference
|
|
584
639
|
|
|
585
640
|
### Tree Component Props
|
|
586
641
|
|
|
587
|
-
#### Core
|
|
588
|
-
| Prop | Type |
|
|
589
|
-
|
|
590
|
-
| `data` | `T[]` |
|
|
591
|
-
| `idMember` | `string` |
|
|
592
|
-
| `pathMember` | `string` |
|
|
593
|
-
| `sortCallback` | `(items: T[]) => T[]` |
|
|
642
|
+
#### Core Properties
|
|
643
|
+
| Prop | Type | Default | Description |
|
|
644
|
+
|------|------|---------|-------------|
|
|
645
|
+
| `data` | `T[]` | **required** | Array of data objects |
|
|
646
|
+
| `idMember` | `string` | **required** | Property name for unique identifiers |
|
|
647
|
+
| `pathMember` | `string` | **required** | Property name for hierarchical paths |
|
|
648
|
+
| `sortCallback` | `(items: T[]) => T[]` | default sort | Function to sort items (optional) |
|
|
594
649
|
|
|
595
650
|
#### Data Mapping Properties
|
|
596
651
|
| Prop | Type | Default | Description |
|
|
@@ -602,6 +657,7 @@ The component includes several pre-built classes for styling selected nodes:
|
|
|
602
657
|
| `isSelectedMember` | `string \| null` | `null` | Property name for selected state |
|
|
603
658
|
| `isDraggableMember` | `string \| null` | `null` | Property name for draggable state |
|
|
604
659
|
| `isDropAllowedMember` | `string \| null` | `null` | Property name for drop allowed state |
|
|
660
|
+
| `allowedDropPositionsMember` | `string \| null` | `null` | Property name for allowed drop positions array |
|
|
605
661
|
| `hasChildrenMember` | `string \| null` | `null` | Property name for children existence |
|
|
606
662
|
| `isSorted` | `boolean \| null` | `null` | Whether items should be sorted |
|
|
607
663
|
|
|
@@ -618,13 +674,13 @@ The component includes several pre-built classes for styling selected nodes:
|
|
|
618
674
|
|
|
619
675
|
**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
676
|
|
|
621
|
-
|
|
677
|
+
**Important**: For internal search indexing to work, you must:
|
|
622
678
|
1. Set `shouldUseInternalSearchIndex={true}`
|
|
623
679
|
2. Provide either `searchValueMember` (property name) or `getSearchValueCallback` (function)
|
|
624
680
|
|
|
625
681
|
Without both requirements, no search indexing will occur.
|
|
626
682
|
|
|
627
|
-
**Performance Tuning**:
|
|
683
|
+
**Performance Tuning**:
|
|
628
684
|
- `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
685
|
- `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
686
|
|
|
@@ -641,23 +697,41 @@ Without both requirements, no search indexing will occur.
|
|
|
641
697
|
|------|------|---------|-------------|
|
|
642
698
|
| `expandLevel` | `number \| null` | `2` | Automatically expand nodes up to this level |
|
|
643
699
|
| `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
700
|
| `orderMember` | `string \| null` | `null` | Property name for sort order (enables above/below positioning in drag-drop) |
|
|
648
701
|
| `indexerBatchSize` | `number \| null` | `25` | Number of nodes to process per batch during search indexing |
|
|
649
702
|
| `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
|
|
703
|
+
| `shouldDisplayDebugInformation` | `boolean` | `false` | Show debug information panel with tree statistics and enable console debug logging |
|
|
651
704
|
| `shouldDisplayContextMenuInDebugMode` | `boolean` | `false` | Display persistent context menu at fixed position for styling development |
|
|
652
705
|
|
|
706
|
+
#### Rendering Properties
|
|
707
|
+
| Prop | Type | Default | Description |
|
|
708
|
+
|------|------|---------|-------------|
|
|
709
|
+
| `useFlatRendering` | `boolean` | `true` | Use flat rendering mode (faster for large trees) |
|
|
710
|
+
| `progressiveRender` | `boolean` | `true` | Progressively render nodes in batches |
|
|
711
|
+
| `initialBatchSize` | `number` | `20` | First batch size for progressive rendering |
|
|
712
|
+
| `maxBatchSize` | `number` | `500` | Maximum batch size cap |
|
|
713
|
+
|
|
714
|
+
#### Drag & Drop Properties
|
|
715
|
+
| Prop | Type | Default | Description |
|
|
716
|
+
|------|------|---------|-------------|
|
|
717
|
+
| `dragDropMode` | `DragDropMode` | `'none'` | Controls allowed drag operations: `'none'`, `'self'`, `'cross'`, `'both'` |
|
|
718
|
+
| `dropZoneMode` | `string` | `'glow'` | Drop indicator style: `'floating'` or `'glow'` |
|
|
719
|
+
| `dropZoneLayout` | `string` | `'around'` | Zone arrangement: `'around'`, `'above'`, `'below'`, `'wave'`, `'wave2'` |
|
|
720
|
+
| `dropZoneStart` | `number \| string` | `33` | Where zones start horizontally (number=%, string=CSS value) |
|
|
721
|
+
| `dropZoneMaxWidth` | `number` | `120` | Max width in pixels for wave layouts |
|
|
722
|
+
| `allowCopy` | `boolean` | `false` | Enable Ctrl+drag to copy instead of move |
|
|
723
|
+
| `autoHandleCopy` | `boolean` | `true` | Auto-handle same-tree copies (false for external DB/API) |
|
|
724
|
+
| `allowedDropPositionsMember` | `string \| null` | `null` | Property name for allowed drop positions array |
|
|
725
|
+
| `getAllowedDropPositionsCallback` | `(node) => DropPosition[] \| null` | `undefined` | Callback returning allowed drop positions per node |
|
|
726
|
+
| `beforeDropCallback` | `(dropNode, draggedNode, position, event, operation) => ...` | `undefined` | Async-capable callback to validate/modify drops |
|
|
727
|
+
|
|
653
728
|
#### Event Handler Properties
|
|
654
729
|
| Prop | Type | Default | Description |
|
|
655
730
|
|------|------|---------|-------------|
|
|
656
731
|
| `onNodeClicked` | `(node) => void` | `undefined` | Node click event handler |
|
|
657
732
|
| `onNodeDragStart` | `(node, event) => void` | `undefined` | Drag start event handler |
|
|
658
733
|
| `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' |
|
|
734
|
+
| `onNodeDrop` | `(dropNode, draggedNode, position, event, operation) => void` | `undefined` | Drop event handler. Position is `'above'`, `'below'`, or `'child'`. Operation is `'move'` or `'copy'` |
|
|
661
735
|
|
|
662
736
|
#### Visual Styling Properties
|
|
663
737
|
| Prop | Type | Default | Description |
|
|
@@ -671,15 +745,15 @@ Without both requirements, no search indexing will occur.
|
|
|
671
745
|
| `scrollHighlightTimeout` | `number \| null` | `4000` | Duration (ms) for scroll highlight animation |
|
|
672
746
|
| `scrollHighlightClass` | `string \| null` | `'ltree-scroll-highlight'` | CSS class to apply for scroll highlight effect |
|
|
673
747
|
|
|
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 |
|
|
748
|
+
#### Snippets
|
|
749
|
+
| Snippet | Parameters | Description |
|
|
750
|
+
|---------|------------|-------------|
|
|
751
|
+
| `nodeTemplate` | `(node)` | Custom node template |
|
|
752
|
+
| `treeHeader` | | Tree header content |
|
|
753
|
+
| `treeBody` | | Tree body content |
|
|
754
|
+
| `treeFooter` | | Tree footer content |
|
|
755
|
+
| `noDataFound` | | No data template |
|
|
756
|
+
| `contextMenu` | `(node, closeMenu)` | Context menu template |
|
|
683
757
|
|
|
684
758
|
#### Public Methods
|
|
685
759
|
| Method | Parameters | Description |
|
|
@@ -852,99 +926,7 @@ When enabled, the component will log detailed information to the browser console
|
|
|
852
926
|
|
|
853
927
|
This provides valuable insights for performance optimization and troubleshooting, especially when working with large datasets or complex search operations.
|
|
854
928
|
|
|
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
|
|
929
|
+
## Data Structure
|
|
948
930
|
|
|
949
931
|
The component expects hierarchical data with path-based organization:
|
|
950
932
|
|
|
@@ -1004,21 +986,21 @@ interface InsertArrayResult<T> {
|
|
|
1004
986
|
```svelte
|
|
1005
987
|
<script lang="ts">
|
|
1006
988
|
import { Tree } from '@keenmate/svelte-treeview';
|
|
1007
|
-
|
|
989
|
+
|
|
1008
990
|
let insertResult = $state();
|
|
1009
|
-
|
|
991
|
+
|
|
1010
992
|
const data = [
|
|
1011
993
|
{ id: '1', path: '1', name: 'Root' },
|
|
1012
994
|
{ id: '1.2', path: '1.2', name: 'Child' }, // Missing parent "1.1"
|
|
1013
995
|
{ id: '1.1.1', path: '1.1.1', name: 'Deep' } // Missing parent "1.1"
|
|
1014
996
|
];
|
|
1015
|
-
|
|
997
|
+
|
|
1016
998
|
// Check results after tree processes data
|
|
1017
999
|
$effect(() => {
|
|
1018
1000
|
if (insertResult) {
|
|
1019
|
-
console.log(
|
|
1020
|
-
console.log(
|
|
1021
|
-
|
|
1001
|
+
console.log(`${insertResult.successful} nodes inserted successfully`);
|
|
1002
|
+
console.log(`${insertResult.failed.length} nodes failed to insert`);
|
|
1003
|
+
|
|
1022
1004
|
insertResult.failed.forEach(failure => {
|
|
1023
1005
|
console.log(`Failed: ${failure.originalData.name} - ${failure.error}`);
|
|
1024
1006
|
});
|
|
@@ -1026,10 +1008,10 @@ interface InsertArrayResult<T> {
|
|
|
1026
1008
|
});
|
|
1027
1009
|
</script>
|
|
1028
1010
|
|
|
1029
|
-
<Tree
|
|
1030
|
-
{data}
|
|
1031
|
-
idMember="id"
|
|
1032
|
-
pathMember="path"
|
|
1011
|
+
<Tree
|
|
1012
|
+
{data}
|
|
1013
|
+
idMember="id"
|
|
1014
|
+
pathMember="path"
|
|
1033
1015
|
displayValueMember="name"
|
|
1034
1016
|
bind:insertResult
|
|
1035
1017
|
/>
|
|
@@ -1043,7 +1025,7 @@ interface InsertArrayResult<T> {
|
|
|
1043
1025
|
- **Search Accuracy**: Failed nodes are excluded from search index, ensuring search results match visible tree
|
|
1044
1026
|
- **User Feedback**: Inform users about data issues with detailed failure information
|
|
1045
1027
|
|
|
1046
|
-
##
|
|
1028
|
+
## Performance
|
|
1047
1029
|
|
|
1048
1030
|
The component is optimized for large datasets:
|
|
1049
1031
|
|
|
@@ -1085,20 +1067,34 @@ enablePerfLogging();
|
|
|
1085
1067
|
window.components['svelte-treeview'].perf.enable()
|
|
1086
1068
|
```
|
|
1087
1069
|
|
|
1088
|
-
**Important**: See the [$state.raw()
|
|
1070
|
+
**Important**: See the [$state.raw() tip](#quick-start) above - using `$state()` instead of `$state.raw()` for tree data can cause 5,000x slowdown!
|
|
1071
|
+
|
|
1072
|
+
## Development Setup & Contributing
|
|
1089
1073
|
|
|
1090
|
-
|
|
1074
|
+
For developers working on the project, you can use either standard npm commands or the provided Makefile:
|
|
1075
|
+
|
|
1076
|
+
```bash
|
|
1077
|
+
# Using Makefile (recommended for consistency)
|
|
1078
|
+
make setup # or make install
|
|
1079
|
+
make dev
|
|
1080
|
+
|
|
1081
|
+
# Or using standard npm commands
|
|
1082
|
+
npm install
|
|
1083
|
+
npm run dev
|
|
1084
|
+
```
|
|
1091
1085
|
|
|
1092
1086
|
We welcome contributions! Please see our contributing guidelines for details.
|
|
1093
1087
|
|
|
1094
|
-
|
|
1088
|
+
> **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.
|
|
1089
|
+
|
|
1090
|
+
## License
|
|
1095
1091
|
|
|
1096
1092
|
MIT License - see LICENSE file for details.
|
|
1097
1093
|
|
|
1098
|
-
##
|
|
1094
|
+
## Support
|
|
1099
1095
|
|
|
1100
1096
|
- **GitHub Issues**: [Report bugs or request features](https://github.com/keenmate/svelte-treeview/issues)
|
|
1101
|
-
- **
|
|
1097
|
+
- **Live demo & docs**: [svelte-treeview.keenmate.dev](https://svelte-treeview.keenmate.dev)
|
|
1102
1098
|
|
|
1103
1099
|
---
|
|
1104
1100
|
|