@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,382 @@
1
+ EVENTS AND CALLBACKS
2
+ ====================
3
+
4
+ CRITICAL: Event-driven architecture
5
+ - Callbacks for user interactions
6
+ - Callbacks for data transformation
7
+ - Callbacks for drag and drop
8
+ - All callbacks receive LTreeNode<T>
9
+
10
+ NODE CLICK
11
+ ----------
12
+ <Tree
13
+ onNodeClicked={(node) => {
14
+ console.log('Clicked:', node.data.name);
15
+ console.log('Path:', node.path);
16
+ console.log('Level:', node.level);
17
+ }}
18
+ />
19
+
20
+ Toggle on click (default: true):
21
+ <Tree shouldToggleOnNodeClick={false} />
22
+
23
+ When false, clicks only select, don't expand/collapse.
24
+
25
+ DRAG EVENTS
26
+ -----------
27
+ <Tree
28
+ onNodeDragStart={(node, event) => {
29
+ console.log('Started dragging:', node.data.name);
30
+ // Can modify event.dataTransfer here
31
+ }}
32
+
33
+ onNodeDragOver={(node, event) => {
34
+ console.log('Dragging over:', node.data.name);
35
+ }}
36
+
37
+ onNodeDrop={(dropNode, draggedNode, position, event, operation) => {
38
+ console.log('Dropped:', draggedNode.data.name);
39
+ console.log('Target:', dropNode?.data.name);
40
+ console.log('Position:', position); // 'before', 'after', 'child'
41
+ console.log('Operation:', operation); // 'move', 'copy'
42
+ }}
43
+ />
44
+
45
+ BEFORE DROP CALLBACK
46
+ --------------------
47
+ Validate or modify drops before they happen:
48
+
49
+ <Tree
50
+ beforeDropCallback={async (dropNode, draggedNode, position, event, operation) => {
51
+ // Cancel the drop
52
+ if (draggedNode.data.locked) {
53
+ return false;
54
+ }
55
+
56
+ // Show confirmation
57
+ if (!await confirm('Are you sure?')) {
58
+ return false;
59
+ }
60
+
61
+ // Modify position
62
+ if (position === 'child' && dropNode.data.type === 'file') {
63
+ return { position: 'after' };
64
+ }
65
+
66
+ // Modify operation
67
+ if (event.shiftKey) {
68
+ return { operation: 'copy' };
69
+ }
70
+
71
+ // Proceed normally
72
+ return true;
73
+ }}
74
+ />
75
+
76
+ Return values:
77
+ - false → Cancel drop
78
+ - true / undefined → Proceed normally
79
+ - { position: 'before'|'after'|'child' } → Override position
80
+ - { operation: 'move'|'copy' } → Override operation
81
+ - { position, operation } → Override both
82
+ - Promise<...> → Async validation
83
+
84
+ DISPLAY VALUE CALLBACK
85
+ ----------------------
86
+ Compute display text dynamically:
87
+
88
+ <Tree
89
+ getDisplayValueCallback={(node) => {
90
+ const item = node.data;
91
+ return `${item.firstName} ${item.lastName}`;
92
+ }}
93
+ />
94
+
95
+ Complex example:
96
+ <Tree
97
+ getDisplayValueCallback={(node) => {
98
+ const item = node.data;
99
+ const count = item.children?.length || 0;
100
+ const suffix = count > 0 ? ` (${count})` : '';
101
+ return `${item.name}${suffix}`;
102
+ }}
103
+ />
104
+
105
+ SEARCH VALUE CALLBACK
106
+ ---------------------
107
+ Control what text gets indexed for search:
108
+
109
+ <Tree
110
+ getSearchValueCallback={(node) => {
111
+ const item = node.data;
112
+ return `${item.name} ${item.email} ${item.tags.join(' ')}`;
113
+ }}
114
+ />
115
+
116
+ ALLOWED DROP POSITIONS CALLBACK
117
+ -------------------------------
118
+ Control which drop positions are allowed per node:
119
+
120
+ <Tree
121
+ getAllowedDropPositionsCallback={(node) => {
122
+ if (node.data.type === 'file') return ['before', 'after'];
123
+ if (node.data.type === 'trash') return ['child'];
124
+ return undefined; // all positions
125
+ }}
126
+ />
127
+
128
+ CONTEXT MENU CALLBACK
129
+ ---------------------
130
+ Generate context menu items dynamically:
131
+
132
+ <Tree
133
+ contextMenuCallback={(node, closeMenuCallback) => {
134
+ return [
135
+ {
136
+ icon: '📂',
137
+ title: 'Open',
138
+ callback: () => {
139
+ openItem(node.data);
140
+ closeMenuCallback();
141
+ }
142
+ },
143
+ { isDivider: true },
144
+ {
145
+ icon: '🗑️',
146
+ title: 'Delete',
147
+ isDisabled: node.data.isProtected,
148
+ callback: async () => {
149
+ if (await confirmDelete()) {
150
+ deleteItem(node.data);
151
+ closeMenuCallback();
152
+ }
153
+ }
154
+ }
155
+ ];
156
+ }}
157
+ />
158
+
159
+ SORT CALLBACK
160
+ -------------
161
+ Custom sorting for tree display:
162
+
163
+ <Tree
164
+ sortCallback={(nodes) => {
165
+ return nodes.sort((a, b) => {
166
+ // Sort by level first (required for proper tree construction)
167
+ const aLevel = a.path.split('.').length;
168
+ const bLevel = b.path.split('.').length;
169
+ if (aLevel !== bLevel) return aLevel - bLevel;
170
+
171
+ // Then by your criteria
172
+ return (a.data?.name ?? '').localeCompare(b.data?.name ?? '');
173
+ });
174
+ }}
175
+ />
176
+
177
+ INITIALIZE INDEX CALLBACK
178
+ -------------------------
179
+ Custom FlexSearch index configuration:
180
+
181
+ <Tree
182
+ initializeIndexCallback={() => {
183
+ return new Index({
184
+ tokenize: 'forward',
185
+ resolution: 9,
186
+ cache: true
187
+ });
188
+ }}
189
+ />
190
+
191
+ BINDING VS CALLBACK
192
+ -------------------
193
+ Bindable props (two-way):
194
+ - searchText
195
+ - selectedNode
196
+ - insertResult
197
+
198
+ Callback props (one-way notification):
199
+ - onNodeClicked
200
+ - onNodeDrop
201
+ - onNodeDragStart
202
+ - onNodeDragOver
203
+
204
+ Example combining both:
205
+
206
+ <script>
207
+ let selectedNode = $state(null);
208
+
209
+ function handleClick(node) {
210
+ // Called in addition to selectedNode update
211
+ console.log('Clicked:', node.path);
212
+ analytics.track('node_click', { path: node.path });
213
+ }
214
+ </script>
215
+
216
+ <Tree
217
+ bind:selectedNode
218
+ onNodeClicked={handleClick}
219
+ />
220
+
221
+ CALLBACK SIGNATURES
222
+ -------------------
223
+ onNodeClicked:
224
+ (node: LTreeNode<T>) => void
225
+
226
+ onNodeDragStart:
227
+ (node: LTreeNode<T>, event: DragEvent) => void
228
+
229
+ onNodeDragOver:
230
+ (node: LTreeNode<T>, event: DragEvent) => void
231
+
232
+ onNodeDrop:
233
+ (
234
+ dropNode: LTreeNode<T> | null,
235
+ draggedNode: LTreeNode<T>,
236
+ position: 'before' | 'after' | 'child',
237
+ event: DragEvent,
238
+ operation: 'move' | 'copy'
239
+ ) => void
240
+
241
+ beforeDropCallback:
242
+ (
243
+ dropNode: LTreeNode<T> | null,
244
+ draggedNode: LTreeNode<T>,
245
+ position: 'before' | 'after' | 'child',
246
+ event: DragEvent,
247
+ operation: 'move' | 'copy'
248
+ ) => boolean | { position?, operation? } | Promise<...>
249
+
250
+ getDisplayValueCallback:
251
+ (node: LTreeNode<T>) => string
252
+
253
+ getSearchValueCallback:
254
+ (node: LTreeNode<T>) => string
255
+
256
+ getAllowedDropPositionsCallback:
257
+ (node: LTreeNode<T>) => DropPosition[] | null | undefined
258
+
259
+ contextMenuCallback:
260
+ (node: LTreeNode<T>, closeMenu: () => void) => ContextMenuItem[]
261
+
262
+ sortCallback:
263
+ (nodes: LTreeNode<T>[]) => LTreeNode<T>[]
264
+
265
+ initializeIndexCallback:
266
+ () => Index
267
+
268
+ ASYNC CALLBACKS
269
+ ---------------
270
+ These callbacks support async/Promise:
271
+
272
+ - beforeDropCallback (awaited before drop)
273
+ - contextMenuCallback item callbacks (individual actions)
274
+
275
+ Example async beforeDropCallback:
276
+ beforeDropCallback={async (drop, drag, pos) => {
277
+ const allowed = await checkPermission(drag.data);
278
+ return allowed;
279
+ }}
280
+
281
+ Example async context menu:
282
+ {
283
+ title: 'Save',
284
+ callback: async () => {
285
+ await saveToServer();
286
+ closeMenu();
287
+ }
288
+ }
289
+
290
+ ERROR HANDLING
291
+ --------------
292
+ Handle errors in async callbacks:
293
+
294
+ beforeDropCallback={async (drop, drag, pos) => {
295
+ try {
296
+ await validateMove(drag, drop);
297
+ return true;
298
+ } catch (error) {
299
+ showError(error.message);
300
+ return false;
301
+ }
302
+ }}
303
+
304
+ Context menu with error handling:
305
+ {
306
+ title: 'Delete',
307
+ callback: async () => {
308
+ try {
309
+ await deleteItem(node.data);
310
+ closeMenu();
311
+ } catch (error) {
312
+ showError('Failed to delete');
313
+ // Menu stays open
314
+ }
315
+ }
316
+ }
317
+
318
+ CALLBACK PRIORITY
319
+ -----------------
320
+ When both member and callback are set, callback wins:
321
+
322
+ getDisplayValueCallback > displayValueMember
323
+
324
+ <Tree
325
+ displayValueMember="name" // Ignored
326
+ getDisplayValueCallback={(n) => n.data.title} // Used
327
+ />
328
+
329
+ COMMON PATTERNS
330
+ ---------------
331
+ Selection tracking:
332
+ <script>
333
+ let history = $state([]);
334
+ </script>
335
+
336
+ <Tree
337
+ onNodeClicked={(node) => {
338
+ history = [...history, node.path];
339
+ }}
340
+ />
341
+
342
+ Prevent certain drops:
343
+ <Tree
344
+ beforeDropCallback={(drop, drag) => {
345
+ // Prevent dropping folders into files
346
+ if (drag.data.type === 'folder' && drop?.data.type === 'file') {
347
+ showError('Cannot drop folder into file');
348
+ return false;
349
+ }
350
+ return true;
351
+ }}
352
+ />
353
+
354
+ Analytics integration:
355
+ <Tree
356
+ onNodeClicked={(node) => {
357
+ analytics.track('tree_click', {
358
+ path: node.path,
359
+ type: node.data.type
360
+ });
361
+ }}
362
+ onNodeDrop={(drop, drag, pos) => {
363
+ analytics.track('tree_drop', {
364
+ from: drag.path,
365
+ to: drop?.path,
366
+ position: pos
367
+ });
368
+ }}
369
+ />
370
+
371
+ BEST PRACTICES
372
+ --------------
373
+ ✅ Use callbacks for side effects (logging, analytics)
374
+ ✅ Use bindings for state synchronization
375
+ ✅ Handle errors in async callbacks
376
+ ✅ Return early from beforeDropCallback for validation
377
+ ✅ Close context menu after successful action
378
+
379
+ ❌ Don't modify node data directly in callbacks
380
+ ❌ Don't do heavy computation in callbacks
381
+ ❌ Don't forget error handling in async callbacks
382
+ ❌ Don't block UI with sync operations
@@ -0,0 +1,271 @@
1
+ IMPORT PATTERNS
2
+ ===============
3
+
4
+ CRITICAL: Always import styles along with the component
5
+ - Component has no embedded styles
6
+ - Styles must be imported separately
7
+ - Both CSS and SCSS versions available
8
+
9
+ BASIC IMPORT
10
+ ------------
11
+ // Component import
12
+ import { Tree } from '@keenmate/svelte-treeview';
13
+
14
+ // Style import (pick one)
15
+ import '@keenmate/svelte-treeview/styles.css'; // Compiled CSS
16
+ import '@keenmate/svelte-treeview/styles.scss'; // SCSS source
17
+
18
+ COMPONENT EXPORTS
19
+ -----------------
20
+ Named exports from the package:
21
+
22
+ // Main component
23
+ import { Tree } from '@keenmate/svelte-treeview';
24
+
25
+ // Types
26
+ import type {
27
+ LTreeNode,
28
+ Ltree,
29
+ ContextMenuItem,
30
+ DropPosition,
31
+ InsertArrayResult,
32
+ NodeCallbacks,
33
+ NodeConfig
34
+ } from '@keenmate/svelte-treeview';
35
+
36
+ // Logging utilities
37
+ import {
38
+ enableLogging,
39
+ disableLogging,
40
+ setLogLevel,
41
+ setCategoryLevel,
42
+ enablePerfLogging,
43
+ disablePerfLogging,
44
+ setPerfThreshold
45
+ } from '@keenmate/svelte-treeview';
46
+
47
+ STYLE IMPORTS
48
+ -------------
49
+ Option 1: In JavaScript entry point
50
+ // main.ts or main.js
51
+ import '@keenmate/svelte-treeview/styles.css';
52
+
53
+ Option 2: In root layout (SvelteKit)
54
+ // +layout.svelte
55
+ <script>
56
+ import '@keenmate/svelte-treeview/styles.css';
57
+ </script>
58
+
59
+ Option 3: In component file
60
+ // MyComponent.svelte
61
+ <script>
62
+ import { Tree } from '@keenmate/svelte-treeview';
63
+ import '@keenmate/svelte-treeview/styles.css';
64
+ </script>
65
+
66
+ Option 4: In Svelte style block (SCSS only)
67
+ <style lang="scss">
68
+ @import '@keenmate/svelte-treeview/styles.scss';
69
+ </style>
70
+
71
+ SCSS CUSTOMIZATION
72
+ ------------------
73
+ Override variables before importing:
74
+
75
+ <style lang="scss">
76
+ // Override variables
77
+ $tree-node-indent-per-level: 1.5rem;
78
+ $ltree-primary: #6366f1;
79
+
80
+ // Then import
81
+ @import '@keenmate/svelte-treeview/styles.scss';
82
+ </style>
83
+
84
+ TYPE IMPORTS
85
+ ------------
86
+ Import types for TypeScript:
87
+
88
+ import type { LTreeNode } from '@keenmate/svelte-treeview';
89
+
90
+ interface MyItem {
91
+ id: string;
92
+ path: string;
93
+ name: string;
94
+ }
95
+
96
+ let selectedNode: LTreeNode<MyItem> | null = $state(null);
97
+
98
+ Full type imports:
99
+
100
+ import type {
101
+ LTreeNode, // Tree node interface
102
+ Ltree, // Tree instance interface
103
+ ContextMenuItem, // Context menu item
104
+ DropPosition, // 'before' | 'after' | 'child'
105
+ InsertArrayResult, // Insert operation result
106
+ NodeCallbacks, // Event callback types
107
+ NodeConfig // Node configuration types
108
+ } from '@keenmate/svelte-treeview';
109
+
110
+ LOGGING IMPORTS
111
+ ---------------
112
+ Debug and performance logging:
113
+
114
+ // Enable all logging
115
+ import { enableLogging } from '@keenmate/svelte-treeview';
116
+ enableLogging();
117
+
118
+ // Category-specific logging
119
+ import { setCategoryLevel } from '@keenmate/svelte-treeview';
120
+ setCategoryLevel('LTREE:DRAG', 'debug');
121
+ setCategoryLevel('LTREE:RENDER', 'debug');
122
+
123
+ // Performance logging
124
+ import { enablePerfLogging, setPerfThreshold } from '@keenmate/svelte-treeview';
125
+ enablePerfLogging();
126
+ setPerfThreshold(100); // Only log operations > 100ms
127
+
128
+ Log categories:
129
+ - LTREE:INIT - Initialization
130
+ - LTREE:DATA - Data operations
131
+ - LTREE:RENDER - Rendering
132
+ - LTREE:INDEX - Search indexing
133
+ - LTREE:DRAG - Drag and drop
134
+ - LTREE:UI - User interactions
135
+
136
+ GLOBAL API
137
+ ----------
138
+ Access from browser console:
139
+
140
+ // Check version
141
+ window.components['svelte-treeview'].version()
142
+
143
+ // Enable logging from console
144
+ window.components['svelte-treeview'].logging.enableLogging()
145
+
146
+ // Performance logging
147
+ window.components['svelte-treeview'].perf.enable()
148
+
149
+ // Get package info
150
+ window.components['svelte-treeview'].config
151
+
152
+ SVELTEKIT IMPORT
153
+ ----------------
154
+ // +page.svelte
155
+ <script lang="ts">
156
+ import { Tree } from '@keenmate/svelte-treeview';
157
+ import type { LTreeNode } from '@keenmate/svelte-treeview';
158
+
159
+ interface Item {
160
+ id: string;
161
+ path: string;
162
+ name: string;
163
+ }
164
+
165
+ let data: Item[] = $state([]);
166
+ let selectedNode: LTreeNode<Item> | null = $state(null);
167
+ </script>
168
+
169
+ <Tree
170
+ {data}
171
+ idMember="id"
172
+ pathMember="path"
173
+ bind:selectedNode
174
+ />
175
+
176
+ // +layout.svelte (for global styles)
177
+ <script>
178
+ import '@keenmate/svelte-treeview/styles.css';
179
+ </script>
180
+
181
+ <slot />
182
+
183
+ VITE + SVELTE IMPORT
184
+ --------------------
185
+ // main.ts
186
+ import './app.css'
187
+ import '@keenmate/svelte-treeview/styles.css';
188
+ import App from './App.svelte'
189
+
190
+ const app = new App({
191
+ target: document.getElementById('app')!,
192
+ })
193
+
194
+ export default app
195
+
196
+ // App.svelte
197
+ <script lang="ts">
198
+ import { Tree } from '@keenmate/svelte-treeview';
199
+ </script>
200
+
201
+ DYNAMIC IMPORT
202
+ --------------
203
+ For code splitting:
204
+
205
+ <script>
206
+ import { onMount } from 'svelte';
207
+
208
+ let Tree;
209
+
210
+ onMount(async () => {
211
+ const module = await import('@keenmate/svelte-treeview');
212
+ Tree = module.Tree;
213
+
214
+ // Import styles dynamically
215
+ await import('@keenmate/svelte-treeview/styles.css');
216
+ });
217
+ </script>
218
+
219
+ {#if Tree}
220
+ <svelte:component this={Tree} {data} ... />
221
+ {/if}
222
+
223
+ PACKAGE STRUCTURE
224
+ -----------------
225
+ @keenmate/svelte-treeview/
226
+ ├── dist/
227
+ │ ├── index.js # Main entry (ESM)
228
+ │ ├── index.d.ts # TypeScript declarations
229
+ │ ├── styles.css # Compiled CSS
230
+ │ └── styles.scss # SCSS source
231
+ └── package.json
232
+
233
+ PEER DEPENDENCIES
234
+ -----------------
235
+ Required:
236
+ - svelte: ^5.0.0
237
+
238
+ Optional:
239
+ - flexsearch: ^0.8.205 (for search functionality)
240
+
241
+ If using search features:
242
+ npm install flexsearch
243
+
244
+ TROUBLESHOOTING
245
+ ---------------
246
+ "Module not found" error:
247
+ ✅ npm install @keenmate/svelte-treeview
248
+ ✅ Check node_modules exists
249
+
250
+ "Tree is not defined":
251
+ ✅ import { Tree } from '@keenmate/svelte-treeview';
252
+ ❌ import Tree from '@keenmate/svelte-treeview';
253
+
254
+ No styles showing:
255
+ ✅ import '@keenmate/svelte-treeview/styles.css';
256
+ ✅ Check import is actually executed
257
+
258
+ TypeScript errors:
259
+ ✅ import type { LTreeNode } from '@keenmate/svelte-treeview';
260
+ ✅ Check tsconfig.json has proper module resolution
261
+
262
+ BEST PRACTICES
263
+ --------------
264
+ ✅ Import styles once at app root level
265
+ ✅ Use type imports for TypeScript
266
+ ✅ Use named exports (not default)
267
+ ✅ Import logging utilities only in development
268
+
269
+ ❌ Don't import styles multiple times
270
+ ❌ Don't use default imports
271
+ ❌ Don't forget peer dependencies