@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.
- package/README.md +106 -117
- package/ai/INDEX.txt +310 -0
- package/ai/advanced-patterns.txt +506 -0
- package/ai/basic-setup.txt +336 -0
- package/ai/context-menu.txt +349 -0
- package/ai/data-handling.txt +390 -0
- package/ai/drag-drop.txt +397 -0
- package/ai/events-callbacks.txt +382 -0
- package/ai/import-patterns.txt +271 -0
- package/ai/performance.txt +349 -0
- package/ai/search-features.txt +359 -0
- package/ai/styling-theming.txt +354 -0
- package/ai/tree-editing.txt +423 -0
- package/ai/typescript-types.txt +357 -0
- package/dist/components/Node.svelte +47 -40
- package/dist/components/Node.svelte.d.ts +1 -1
- package/dist/components/Tree.svelte +384 -1479
- package/dist/components/Tree.svelte.d.ts +30 -28
- 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 +353 -0
- package/dist/core/TreeController.svelte.js +1503 -0
- package/dist/core/createTreeController.d.ts +9 -0
- package/dist/core/createTreeController.js +11 -0
- package/dist/global-api.d.ts +1 -1
- package/dist/global-api.js +5 -5
- package/dist/index.d.ts +10 -6
- package/dist/index.js +7 -3
- package/dist/logger.d.ts +7 -6
- package/dist/logger.js +0 -2
- package/dist/ltree/indexer.js +2 -4
- package/dist/ltree/ltree-node.svelte.d.ts +2 -1
- package/dist/ltree/ltree-node.svelte.js +1 -0
- package/dist/ltree/ltree.svelte.d.ts +1 -1
- package/dist/ltree/ltree.svelte.js +168 -175
- package/dist/ltree/types.d.ts +12 -8
- package/dist/perf-logger.d.ts +2 -1
- package/dist/perf-logger.js +0 -2
- package/dist/styles/main.scss +78 -78
- package/dist/styles.css +41 -41
- package/dist/styles.css.map +1 -1
- package/dist/vendor/loglevel/index.d.ts +55 -2
- package/dist/vendor/loglevel/prefix.d.ts +23 -2
- package/package.json +96 -95
- package/dist/ltree/ltree-demo.d.ts +0 -2
- package/dist/ltree/ltree-demo.js +0 -90
- package/dist/vendor/loglevel/loglevel-esm.d.ts +0 -2
- package/dist/vendor/loglevel/loglevel-plugin-prefix-esm.d.ts +0 -7
- 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
|