@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.
Files changed (51) hide show
  1. package/README.md +106 -117
  2. package/ai/INDEX.txt +310 -0
  3. package/ai/advanced-patterns.txt +506 -0
  4. package/ai/basic-setup.txt +336 -0
  5. package/ai/context-menu.txt +349 -0
  6. package/ai/data-handling.txt +390 -0
  7. package/ai/drag-drop.txt +397 -0
  8. package/ai/events-callbacks.txt +382 -0
  9. package/ai/import-patterns.txt +271 -0
  10. package/ai/performance.txt +349 -0
  11. package/ai/search-features.txt +359 -0
  12. package/ai/styling-theming.txt +354 -0
  13. package/ai/tree-editing.txt +423 -0
  14. package/ai/typescript-types.txt +357 -0
  15. package/dist/components/Node.svelte +47 -40
  16. package/dist/components/Node.svelte.d.ts +1 -1
  17. package/dist/components/Tree.svelte +384 -1479
  18. package/dist/components/Tree.svelte.d.ts +30 -28
  19. package/dist/components/TreeProvider.svelte +28 -0
  20. package/dist/components/TreeProvider.svelte.d.ts +28 -0
  21. package/dist/constants.generated.d.ts +1 -1
  22. package/dist/constants.generated.js +1 -1
  23. package/dist/core/TreeController.svelte.d.ts +353 -0
  24. package/dist/core/TreeController.svelte.js +1503 -0
  25. package/dist/core/createTreeController.d.ts +9 -0
  26. package/dist/core/createTreeController.js +11 -0
  27. package/dist/global-api.d.ts +1 -1
  28. package/dist/global-api.js +5 -5
  29. package/dist/index.d.ts +10 -6
  30. package/dist/index.js +7 -3
  31. package/dist/logger.d.ts +7 -6
  32. package/dist/logger.js +0 -2
  33. package/dist/ltree/indexer.js +2 -4
  34. package/dist/ltree/ltree-node.svelte.d.ts +2 -1
  35. package/dist/ltree/ltree-node.svelte.js +1 -0
  36. package/dist/ltree/ltree.svelte.d.ts +1 -1
  37. package/dist/ltree/ltree.svelte.js +168 -175
  38. package/dist/ltree/types.d.ts +12 -8
  39. package/dist/perf-logger.d.ts +2 -1
  40. package/dist/perf-logger.js +0 -2
  41. package/dist/styles/main.scss +78 -78
  42. package/dist/styles.css +41 -41
  43. package/dist/styles.css.map +1 -1
  44. package/dist/vendor/loglevel/index.d.ts +55 -2
  45. package/dist/vendor/loglevel/prefix.d.ts +23 -2
  46. package/package.json +96 -95
  47. package/dist/ltree/ltree-demo.d.ts +0 -2
  48. package/dist/ltree/ltree-demo.js +0 -90
  49. package/dist/vendor/loglevel/loglevel-esm.d.ts +0 -2
  50. package/dist/vendor/loglevel/loglevel-plugin-prefix-esm.d.ts +0 -7
  51. package/dist/vendor/loglevel/loglevel-plugin-prefix.d.ts +0 -2
@@ -0,0 +1,423 @@
1
+ TREE EDITING
2
+ ============
3
+
4
+ CRITICAL: Built-in methods for programmatic tree editing
5
+ - addNode: Add new nodes
6
+ - moveNode: Move nodes with subtrees
7
+ - removeNode: Delete nodes
8
+ - Automatic path management
9
+
10
+ ADD NODE
11
+ --------
12
+ <script>
13
+ let treeRef;
14
+
15
+ function addChild(parentPath) {
16
+ const result = treeRef.addNode(
17
+ parentPath, // Parent path (empty string = root)
18
+ {
19
+ id: Date.now().toString(),
20
+ path: '', // Will be auto-calculated
21
+ name: 'New Item',
22
+ sortOrder: 100
23
+ }
24
+ );
25
+
26
+ if (result.success) {
27
+ console.log('Added node:', result.node);
28
+ console.log('New path:', result.node.path);
29
+ } else {
30
+ console.log('Failed:', result.error);
31
+ }
32
+ }
33
+ </script>
34
+
35
+ <Tree bind:this={treeRef} {data} ... />
36
+ <button onclick={() => addChild('1.2')}>Add to 1.2</button>
37
+ <button onclick={() => addChild('')}>Add to Root</button>
38
+
39
+ addNode signature:
40
+ addNode(parentPath: string, data: T, pathSegment?: string)
41
+
42
+ - parentPath: Where to add (empty = root)
43
+ - data: Your data object (path auto-calculated)
44
+ - pathSegment: Optional custom segment (otherwise auto-generated)
45
+
46
+ Returns: { success: boolean, node?: LTreeNode<T>, error?: string }
47
+
48
+ MOVE NODE
49
+ ---------
50
+ <script>
51
+ function moveNode(sourcePath, targetPath, position) {
52
+ const result = treeRef.moveNode(sourcePath, targetPath, position);
53
+
54
+ if (result.success) {
55
+ console.log('Moved to:', result.newPath);
56
+ }
57
+ }
58
+ </script>
59
+
60
+ <button onclick={() => moveNode('1.2', '2.1', 'before')}>
61
+ Move 1.2 before 2.1
62
+ </button>
63
+
64
+ <button onclick={() => moveNode('1.2', '2', 'child')}>
65
+ Move 1.2 as child of 2
66
+ </button>
67
+
68
+ moveNode signature:
69
+ moveNode(sourcePath: string, targetPath: string, position: 'before' | 'after' | 'child')
70
+
71
+ - sourcePath: Node to move (includes all descendants)
72
+ - targetPath: Target node
73
+ - position: Where relative to target
74
+
75
+ Positions:
76
+ - 'before': Insert as sibling before target
77
+ - 'after': Insert as sibling after target
78
+ - 'child': Insert as child of target
79
+
80
+ REMOVE NODE
81
+ -----------
82
+ <script>
83
+ function deleteNode(path) {
84
+ const result = treeRef.removeNode(path, true);
85
+
86
+ if (result.success) {
87
+ console.log('Removed:', result.removedCount, 'nodes');
88
+ }
89
+ }
90
+ </script>
91
+
92
+ <button onclick={() => deleteNode('1.2')}>Delete 1.2</button>
93
+
94
+ removeNode signature:
95
+ removeNode(path: string, includeDescendants?: boolean)
96
+
97
+ - path: Node to remove
98
+ - includeDescendants: Remove children too (default: true)
99
+
100
+ Returns: { success: boolean, removedCount: number, error?: string }
101
+
102
+ ORDER MEMBER
103
+ ------------
104
+ CRITICAL: For proper sibling ordering, set orderMember
105
+
106
+ <Tree orderMember="sortOrder" ... />
107
+
108
+ const data = [
109
+ { path: '1.1', name: 'First', sortOrder: 10 },
110
+ { path: '1.2', name: 'Second', sortOrder: 20 },
111
+ { path: '1.3', name: 'Third', sortOrder: 30 }
112
+ ];
113
+
114
+ When moving nodes with orderMember:
115
+ - Tree auto-calculates new order values
116
+ - Uses midpoint between adjacent values
117
+ - Example: Moving between 10 and 20 → sortOrder = 15
118
+
119
+ HELPER METHODS
120
+ --------------
121
+ Get node by path:
122
+ const node = treeRef.getNodeByPath('1.2.3');
123
+ console.log(node?.data.name);
124
+
125
+ Get children of a node:
126
+ const children = treeRef.getChildren('1.2');
127
+ console.log('Children count:', children.length);
128
+
129
+ Get siblings (including self):
130
+ const siblings = treeRef.getSiblings('1.2.3');
131
+ const index = siblings.findIndex(s => s.path === '1.2.3');
132
+
133
+ Refresh siblings (re-sort):
134
+ treeRef.refreshSiblings('1.2'); // Parent path
135
+
136
+ Refresh specific node:
137
+ treeRef.refreshNode('1.2.3');
138
+
139
+ MOVE UP/DOWN
140
+ ------------
141
+ <script>
142
+ function moveUp(nodePath) {
143
+ const siblings = treeRef.getSiblings(nodePath);
144
+ const index = siblings.findIndex(s => s.path === nodePath);
145
+
146
+ if (index > 0) {
147
+ treeRef.moveNode(nodePath, siblings[index - 1].path, 'before');
148
+ }
149
+ }
150
+
151
+ function moveDown(nodePath) {
152
+ const siblings = treeRef.getSiblings(nodePath);
153
+ const index = siblings.findIndex(s => s.path === nodePath);
154
+
155
+ if (index < siblings.length - 1) {
156
+ treeRef.moveNode(nodePath, siblings[index + 1].path, 'after');
157
+ }
158
+ }
159
+ </script>
160
+
161
+ UPDATE NODE
162
+ -----------
163
+ Update a node's data properties:
164
+
165
+ <script>
166
+ function updateNodeData(path) {
167
+ treeRef.updateNode(path, {
168
+ name: 'Updated Name',
169
+ status: 'modified'
170
+ });
171
+ }
172
+ </script>
173
+
174
+ updateNode signature:
175
+ updateNode(path: string, data: Partial<T>)
176
+
177
+ - path: Node to update
178
+ - data: Partial data to merge into existing data
179
+
180
+ GET/SET EXPANDED STATE
181
+ ----------------------
182
+ Save and restore expanded state:
183
+
184
+ <script>
185
+ // Save expanded state
186
+ const expandedPaths = treeRef.getExpandedPaths();
187
+ localStorage.setItem('expandedPaths', JSON.stringify(expandedPaths));
188
+
189
+ // Restore expanded state
190
+ const saved = JSON.parse(localStorage.getItem('expandedPaths') || '[]');
191
+ treeRef.setExpandedPaths(saved);
192
+ </script>
193
+
194
+ GET ALL DATA
195
+ ------------
196
+ Get all tree data as flat array (with any modifications):
197
+
198
+ const allItems = treeRef.getAllData();
199
+
200
+ APPLY CHANGES
201
+ -------------
202
+ Force-apply pending changes and refresh the tree:
203
+
204
+ treeRef.applyChanges();
205
+
206
+ COPY NODE
207
+ ---------
208
+ <script>
209
+ function copyNode(sourcePath, targetPath, position) {
210
+ const result = treeRef.copyNodeWithDescendants(
211
+ sourcePath,
212
+ targetPath,
213
+ position
214
+ );
215
+
216
+ if (result.success) {
217
+ console.log('Copied to:', result.newPath);
218
+ }
219
+ }
220
+ </script>
221
+
222
+ copyNodeWithDescendants signature:
223
+ copyNodeWithDescendants(
224
+ sourcePath: string,
225
+ targetParentPath?: string,
226
+ siblingPath?: string,
227
+ position?: 'before' | 'after'
228
+ )
229
+
230
+ NEW ITEM WORKFLOW
231
+ -----------------
232
+ Complete workflow for adding items:
233
+
234
+ <script>
235
+ let treeRef;
236
+ let selectedNode = $state(null);
237
+
238
+ function addSibling() {
239
+ if (!selectedNode) return;
240
+
241
+ const parentPath = selectedNode.parentPath || '';
242
+ const result = treeRef.addNode(parentPath, {
243
+ id: crypto.randomUUID(),
244
+ path: '',
245
+ name: 'New Sibling',
246
+ sortOrder: Date.now()
247
+ });
248
+
249
+ if (result.success) {
250
+ // Select the new node
251
+ selectedNode = result.node;
252
+ // Optionally scroll to it
253
+ treeRef.scrollToPath(result.node.path);
254
+ }
255
+ }
256
+
257
+ function addChild() {
258
+ if (!selectedNode) return;
259
+
260
+ const result = treeRef.addNode(selectedNode.path, {
261
+ id: crypto.randomUUID(),
262
+ path: '',
263
+ name: 'New Child',
264
+ sortOrder: Date.now()
265
+ });
266
+
267
+ if (result.success) {
268
+ // Expand parent to show new child
269
+ treeRef.expandNodes(selectedNode.path);
270
+ // Select the new node
271
+ selectedNode = result.node;
272
+ }
273
+ }
274
+
275
+ function deleteSelected() {
276
+ if (!selectedNode) return;
277
+
278
+ if (confirm(`Delete ${selectedNode.data.name}?`)) {
279
+ const parentPath = selectedNode.parentPath;
280
+ treeRef.removeNode(selectedNode.path);
281
+ selectedNode = null;
282
+ }
283
+ }
284
+ </script>
285
+
286
+ <Tree bind:this={treeRef} bind:selectedNode />
287
+
288
+ <button onclick={addSibling} disabled={!selectedNode}>Add Sibling</button>
289
+ <button onclick={addChild} disabled={!selectedNode}>Add Child</button>
290
+ <button onclick={deleteSelected} disabled={!selectedNode}>Delete</button>
291
+
292
+ INLINE EDITING
293
+ --------------
294
+ Edit node name inline:
295
+
296
+ <script>
297
+ let editingPath = $state(null);
298
+ let editValue = $state('');
299
+
300
+ function startEdit(node) {
301
+ editingPath = node.path;
302
+ editValue = node.data.name;
303
+ }
304
+
305
+ function saveEdit() {
306
+ if (editingPath) {
307
+ // Update your data
308
+ data = data.map(item =>
309
+ item.path === editingPath
310
+ ? { ...item, name: editValue }
311
+ : item
312
+ );
313
+ editingPath = null;
314
+ }
315
+ }
316
+ </script>
317
+
318
+ <Tree {data}>
319
+ {#snippet nodeTemplate(node)}
320
+ {#if editingPath === node.path}
321
+ <input
322
+ bind:value={editValue}
323
+ onblur={saveEdit}
324
+ onkeydown={(e) => e.key === 'Enter' && saveEdit()}
325
+ autofocus
326
+ />
327
+ {:else}
328
+ <span ondblclick={() => startEdit(node)}>
329
+ {node.data.name}
330
+ </span>
331
+ {/if}
332
+ {/snippet}
333
+ </Tree>
334
+
335
+ BATCH OPERATIONS
336
+ ----------------
337
+ Multiple operations at once:
338
+
339
+ <script>
340
+ function batchUpdate() {
341
+ // Disable reactivity during batch
342
+ const operations = [
343
+ () => treeRef.addNode('1', { name: 'Item A' }),
344
+ () => treeRef.addNode('1', { name: 'Item B' }),
345
+ () => treeRef.moveNode('2.1', '1', 'child')
346
+ ];
347
+
348
+ operations.forEach(op => op());
349
+
350
+ // Refresh after batch
351
+ treeRef.refreshSiblings('1');
352
+ }
353
+ </script>
354
+
355
+ UNDO/REDO SUPPORT
356
+ -----------------
357
+ Track operations for undo:
358
+
359
+ <script>
360
+ let history = $state([]);
361
+ let historyIndex = $state(-1);
362
+
363
+ function recordOperation(type, data, undo) {
364
+ // Clear redo history
365
+ history = history.slice(0, historyIndex + 1);
366
+ // Add new operation
367
+ history = [...history, { type, data, undo }];
368
+ historyIndex = history.length - 1;
369
+ }
370
+
371
+ function addNode(parentPath, nodeData) {
372
+ const result = treeRef.addNode(parentPath, nodeData);
373
+ if (result.success) {
374
+ recordOperation('add', result.node, () => {
375
+ treeRef.removeNode(result.node.path);
376
+ });
377
+ }
378
+ }
379
+
380
+ function undo() {
381
+ if (historyIndex >= 0) {
382
+ history[historyIndex].undo();
383
+ historyIndex--;
384
+ }
385
+ }
386
+ </script>
387
+
388
+ SYNC WITH SERVER
389
+ ----------------
390
+ <script>
391
+ async function addNodeWithSync(parentPath, nodeData) {
392
+ // Optimistic update
393
+ const result = treeRef.addNode(parentPath, nodeData);
394
+
395
+ if (result.success) {
396
+ try {
397
+ // Sync to server
398
+ await api.createNode({
399
+ ...nodeData,
400
+ path: result.node.path
401
+ });
402
+ } catch (error) {
403
+ // Rollback on failure
404
+ treeRef.removeNode(result.node.path);
405
+ showError('Failed to save');
406
+ }
407
+ }
408
+ }
409
+ </script>
410
+
411
+ BEST PRACTICES
412
+ --------------
413
+ ✅ Use orderMember for proper sibling ordering
414
+ ✅ Handle operation results (success/error)
415
+ ✅ Expand parent after adding child
416
+ ✅ Update selection after operations
417
+ ✅ Sync with server if needed
418
+ ✅ Consider undo/redo for complex apps
419
+
420
+ ❌ Don't forget to set orderMember
421
+ ❌ Don't assume operations always succeed
422
+ ❌ Don't mutate data directly (use methods)
423
+ ❌ Don't forget to refresh UI after batch operations