@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,397 @@
1
+ DRAG AND DROP
2
+ =============
3
+
4
+ CRITICAL: Drag and drop is DISABLED by default
5
+ - Set dragDropMode="both" to enable
6
+ - Three drop positions: before, after, child
7
+ - Same-tree and cross-tree moves
8
+ - Touch support for mobile
9
+ - Copy operations with Ctrl+drag
10
+ - Async validation with beforeDropCallback
11
+ - Per-node drop position restrictions
12
+
13
+ BASIC DRAG AND DROP
14
+ -------------------
15
+ <script>
16
+ function handleDrop(dropNode, draggedNode, position, event, operation) {
17
+ console.log(`Dropped ${draggedNode.data.name}`);
18
+ console.log(`Position: ${position}`); // 'before', 'after', 'child'
19
+ console.log(`Target: ${dropNode?.data.name}`);
20
+ console.log(`Operation: ${operation}`); // 'move' or 'copy'
21
+ }
22
+ </script>
23
+
24
+ <Tree
25
+ {data}
26
+ idMember="id"
27
+ pathMember="path"
28
+ dragDropMode="both"
29
+ dragOverNodeClass="ltree-dragover-glow"
30
+ onNodeDrop={handleDrop}
31
+ />
32
+
33
+ DROP POSITIONS
34
+ --------------
35
+ Three positions available when dropping:
36
+
37
+ 'before' - Insert as sibling BEFORE the target node
38
+ 'after' - Insert as sibling AFTER the target node
39
+ 'child' - Insert as CHILD of the target node
40
+
41
+ The position is calculated based on mouse Y position:
42
+ - Top 25% of node → 'before'
43
+ - Middle 50% of node → 'child'
44
+ - Bottom 25% of node → 'after'
45
+
46
+ DROP ZONE MODES
47
+ ---------------
48
+ Two visual modes for drop zones:
49
+
50
+ // Glow mode (default) - border/shadow indicators
51
+ <Tree dropZoneMode="glow" dragOverNodeClass="ltree-dragover-glow" />
52
+
53
+ // Floating mode - popup buttons
54
+ <Tree dropZoneMode="floating" />
55
+
56
+ Glow mode classes:
57
+ - ltree-glow-before - top border glow (before position)
58
+ - ltree-glow-after - bottom border glow (after position)
59
+ - ltree-glow-child - full border glow (child position)
60
+
61
+ DROP ZONE LAYOUTS (Floating Mode)
62
+ ---------------------------------
63
+ <Tree
64
+ dropZoneMode="floating"
65
+ dropZoneLayout="around" <!-- Default: before on top, after/child on bottom -->
66
+ dropZoneStart={33} <!-- Start position (percentage) -->
67
+ />
68
+
69
+ Layout options:
70
+ - 'around' - Before on top, After/Child on bottom
71
+ - 'above' - All 3 zones in row above node
72
+ - 'below' - All 3 zones in row below node
73
+ - 'wave' - Stacked vertically
74
+ - 'wave2' - Diagonal wave pattern
75
+
76
+ ZONE AUTO-EXPAND
77
+ When some drop positions are restricted (via allowedDropPositions),
78
+ hidden zones auto-expand using CSS :not(:has()) rules:
79
+ - 3 zones visible: normal thirds
80
+ - 2 zones visible: each gets half the space
81
+ - 1 zone visible: full width
82
+ Works in 'around', 'above', and 'below' layouts.
83
+
84
+ DROP POSITION RESTRICTIONS
85
+ --------------------------
86
+ CRITICAL: Restrict which positions are allowed per node
87
+
88
+ Use case examples:
89
+ - Trash folder: only 'child' (drop INTO, not around)
90
+ - Files: only 'before'/'after' (can't drop INTO a file)
91
+ - Folders: all positions (default)
92
+
93
+ Method 1: Callback (dynamic logic)
94
+
95
+ <Tree
96
+ getAllowedDropPositionsCallback={(node) => {
97
+ if (node.data.type === 'file') return ['before', 'after'];
98
+ if (node.data.type === 'trash') return ['child'];
99
+ return undefined; // all positions allowed
100
+ }}
101
+ />
102
+
103
+ Method 2: Member property (server data)
104
+
105
+ const data = [
106
+ { path: '1', name: 'Trash', allowedDropPositions: ['child'] },
107
+ { path: '2', name: 'File.txt', allowedDropPositions: ['before', 'after'] },
108
+ { path: '3', name: 'Folder' } // undefined = all positions
109
+ ];
110
+
111
+ <Tree allowedDropPositionsMember="allowedDropPositions" />
112
+
113
+ Behavior when restricted:
114
+ - Glow mode: snaps to nearest allowed position
115
+ - Floating mode: only shows buttons for allowed positions
116
+
117
+ DRAG DROP MODE
118
+ --------------
119
+ Control which drag operations are allowed:
120
+
121
+ <Tree dragDropMode="none" /> <!-- Default: disabled -->
122
+
123
+ Options:
124
+ - 'none' - Drag and drop disabled (DEFAULT)
125
+ - 'self' - Only within same tree
126
+ - 'cross' - Only between different trees
127
+ - 'both' - Both self and cross-tree
128
+
129
+ SAME-TREE MOVE (Auto-handled)
130
+ -----------------------------
131
+ When dropping within the same tree, the move is auto-handled:
132
+
133
+ <Tree
134
+ {data}
135
+ orderMember="sortOrder"
136
+ onNodeDrop={(dropNode, draggedNode, position, event, operation) => {
137
+ // Move already happened! This is for notification only.
138
+ console.log(`Moved ${draggedNode.data.name} ${position} ${dropNode.data.name}`);
139
+ }}
140
+ />
141
+
142
+ IMPORTANT: Set orderMember for proper sibling ordering
143
+
144
+ CROSS-TREE DRAG
145
+ ---------------
146
+ <script>
147
+ let leftData = $state.raw([...]);
148
+ let rightData = $state.raw([...]);
149
+
150
+ function handleLeftDrop(dropNode, draggedNode, position, event, operation) {
151
+ // Handle item coming from right tree
152
+ }
153
+
154
+ function handleRightDrop(dropNode, draggedNode, position, event, operation) {
155
+ // Handle item coming from left tree
156
+ }
157
+ </script>
158
+
159
+ <Tree data={leftData} treeId="left" onNodeDrop={handleLeftDrop} />
160
+ <Tree data={rightData} treeId="right" onNodeDrop={handleRightDrop} />
161
+
162
+ ASYNC DROP VALIDATION
163
+ ---------------------
164
+ Use beforeDropCallback for validation or modification:
165
+
166
+ <script>
167
+ async function beforeDrop(dropNode, draggedNode, position, event, operation) {
168
+ // Cancel the drop
169
+ if (draggedNode.data.locked) {
170
+ return false;
171
+ }
172
+
173
+ // Show confirmation dialog
174
+ if (position === 'child' && !dropNode.data.isFolder) {
175
+ const confirmed = await showConfirmDialog('Convert to folder?');
176
+ if (!confirmed) return false;
177
+ }
178
+
179
+ // Modify position
180
+ if (position === 'child' && dropNode.data.type === 'file') {
181
+ return { position: 'after' };
182
+ }
183
+
184
+ // Proceed normally
185
+ return true;
186
+ }
187
+ </script>
188
+
189
+ <Tree beforeDropCallback={beforeDrop} onNodeDrop={handleDrop} />
190
+
191
+ Return values:
192
+ - false → Cancel the drop
193
+ - true / undefined → Proceed normally
194
+ - { position: 'before' } → Override position
195
+ - { operation: 'copy' } → Override operation
196
+ - Promise<...> → Async validation
197
+
198
+ COPY OPERATION (Ctrl+Drag)
199
+ --------------------------
200
+ Enable copying with Ctrl+drag:
201
+
202
+ <Tree
203
+ allowCopy={true}
204
+ autoHandleCopy={true} <!-- Default: auto-creates copy -->
205
+ onNodeDrop={(dropNode, draggedNode, position, event, operation) => {
206
+ if (operation === 'copy') {
207
+ console.log('Copied node');
208
+ } else {
209
+ console.log('Moved node');
210
+ }
211
+ }}
212
+ />
213
+
214
+ autoHandleCopy options:
215
+ - true: Tree creates copy with generated ID (good for batch/offline)
216
+ - false: Your callback handles copy (good for DB/API integration)
217
+
218
+ DRAGGABLE CONFIGURATION
219
+ -----------------------
220
+ Control which nodes can be dragged:
221
+
222
+ // All nodes draggable (default)
223
+ <Tree />
224
+
225
+ // Using member property
226
+ const data = [
227
+ { path: '1', name: 'Draggable', isDraggable: true },
228
+ { path: '2', name: 'Fixed', isDraggable: false }
229
+ ];
230
+
231
+ <Tree isDraggableMember="isDraggable" />
232
+
233
+ DROP ALLOWED CONFIGURATION
234
+ --------------------------
235
+ Control which nodes accept drops:
236
+
237
+ // Using member property
238
+ const data = [
239
+ { path: '1', name: 'Can receive drops', isDropAllowed: true },
240
+ { path: '2', name: 'Cannot receive drops', isDropAllowed: false }
241
+ ];
242
+
243
+ <Tree isDropAllowedMember="isDropAllowed" />
244
+
245
+ ORDER MEMBER (SIBLING ORDERING)
246
+ -------------------------------
247
+ CRITICAL: For proper before/after positioning
248
+
249
+ const data = [
250
+ { path: '1.1', name: 'First', sortOrder: 10 },
251
+ { path: '1.2', name: 'Second', sortOrder: 20 },
252
+ { path: '1.3', name: 'Third', sortOrder: 30 }
253
+ ];
254
+
255
+ <Tree orderMember="sortOrder" />
256
+
257
+ When moving nodes:
258
+ - Tree auto-calculates new order values
259
+ - Inserts between adjacent values
260
+ - Uses midpoint: (10 + 20) / 2 = 15
261
+
262
+ ROOT DROP ZONE
263
+ --------------
264
+ Drop zone at bottom for adding root-level items:
265
+
266
+ When dragging, a root drop zone appears at tree bottom
267
+ - Dropping there adds item as root node
268
+ - dropNode is null in callback
269
+
270
+ <Tree
271
+ onNodeDrop={(dropNode, draggedNode, position, event) => {
272
+ if (dropNode === null) {
273
+ console.log('Dropped at root level');
274
+ }
275
+ }}
276
+ />
277
+
278
+ EMPTY TREE DROP
279
+ ---------------
280
+ Custom placeholder for empty trees:
281
+
282
+ <Tree {data}>
283
+ {#snippet dropPlaceholder()}
284
+ <div class="drop-here">
285
+ Drop items here to start
286
+ </div>
287
+ {/snippet}
288
+ </Tree>
289
+
290
+ When dropping to empty tree:
291
+ - dropNode is null
292
+ - position is 'child'
293
+
294
+ TOUCH DRAG SUPPORT
295
+ ------------------
296
+ Enabled by default on touch devices:
297
+
298
+ - Long-press (300ms) initiates drag
299
+ - Distinguishes from tap and scroll
300
+ - Ghost element follows finger
301
+ - Haptic feedback on supported devices
302
+ - Move >10px before long-press cancels (allows scrolling)
303
+
304
+ Same callback as desktop:
305
+ <Tree onNodeDrop={handleDrop} />
306
+
307
+ DRAG EVENTS
308
+ -----------
309
+ <Tree
310
+ onNodeDragStart={(node, event) => {
311
+ console.log('Started dragging:', node.data.name);
312
+ }}
313
+ onNodeDragOver={(node, event) => {
314
+ console.log('Dragging over:', node.data.name);
315
+ }}
316
+ onNodeDrop={(dropNode, draggedNode, position, event, operation) => {
317
+ console.log('Dropped');
318
+ }}
319
+ />
320
+
321
+ VISUAL FEEDBACK CLASSES
322
+ -----------------------
323
+ Drag-over styling:
324
+
325
+ <Tree dragOverNodeClass="ltree-dragover-glow" />
326
+
327
+ Built-in classes:
328
+ - ltree-dragover-highlight - Dashed border
329
+ - ltree-dragover-glow - Shadow glow effect
330
+
331
+ Copy operation class:
332
+ - ltree-drop-copy - Applied during Ctrl+drag
333
+
334
+ PROGRAMMATIC MOVE
335
+ -----------------
336
+ Move nodes programmatically:
337
+
338
+ <script>
339
+ let treeRef;
340
+
341
+ function moveUp(nodePath) {
342
+ const siblings = treeRef.getSiblings(nodePath);
343
+ const index = siblings.findIndex(s => s.path === nodePath);
344
+ if (index > 0) {
345
+ treeRef.moveNode(nodePath, siblings[index - 1].path, 'before');
346
+ }
347
+ }
348
+ </script>
349
+
350
+ <Tree bind:this={treeRef} />
351
+
352
+ COMMON PATTERNS
353
+ ---------------
354
+ File manager:
355
+ <Tree
356
+ dragDropMode="self"
357
+ orderMember="sortOrder"
358
+ getAllowedDropPositionsCallback={(node) =>
359
+ node.data.type === 'file' ? ['before', 'after'] : undefined
360
+ }
361
+ onNodeDrop={handleDrop}
362
+ />
363
+
364
+ Two-tree transfer:
365
+ <Tree data={available} treeId="available" dragDropMode="cross" />
366
+ <Tree data={selected} treeId="selected" dragDropMode="cross" />
367
+
368
+ With confirmation:
369
+ <Tree
370
+ beforeDropCallback={async (drop, drag, pos) => {
371
+ return await confirm(`Move ${drag.data.name}?`);
372
+ }}
373
+ />
374
+
375
+ DEBUGGING
376
+ ---------
377
+ Enable drag logging:
378
+
379
+ import { setCategoryLevel } from '@keenmate/svelte-treeview';
380
+ setCategoryLevel('LTREE:DRAG', 'debug');
381
+
382
+ Debug panel:
383
+ <Tree shouldDisplayDebugInformation={true} />
384
+ // Shows currently dragged node
385
+
386
+ BEST PRACTICES
387
+ --------------
388
+ ✅ Set orderMember for sibling ordering
389
+ ✅ Use beforeDropCallback for validation
390
+ ✅ Handle cross-tree drops explicitly
391
+ ✅ Use getAllowedDropPositionsCallback for restrictions
392
+ ✅ Provide visual feedback (dragOverNodeClass)
393
+
394
+ ❌ Don't forget orderMember (causes position issues)
395
+ ❌ Don't modify data in beforeDropCallback (use onNodeDrop)
396
+ ❌ Don't assume same-tree vs cross-tree without checking
397
+ ❌ Don't block UI in sync beforeDropCallback