@keenmate/svelte-treeview 5.0.0-rc10 → 5.0.0-rc11
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/CHANGELOG.md +931 -889
- package/README.md +362 -1103
- package/ai/INDEX.txt +349 -349
- package/ai/advanced-patterns.txt +1 -1
- package/ai/drag-drop.txt +397 -397
- package/ai/performance.txt +349 -349
- package/ai/tree-editing.txt +1 -1
- package/component-variables.manifest.json +143 -145
- package/dist/components/ContextMenuDivider.svelte +2 -2
- package/dist/components/ContextMenuItem.svelte +58 -10
- package/dist/components/ContextMenuLevel.svelte +131 -0
- package/dist/components/ContextMenuLevel.svelte.d.ts +32 -0
- package/dist/components/Node.svelte +491 -482
- package/dist/components/Node.svelte.d.ts +1 -1
- package/dist/components/Tree.svelte +1323 -1300
- package/dist/components/Tree.svelte.d.ts +62 -72
- package/dist/constants.generated.d.ts +1 -1
- package/dist/constants.generated.js +1 -1
- package/dist/core/TreeController.svelte.d.ts +73 -53
- package/dist/core/TreeController.svelte.js +479 -124
- package/dist/index.d.ts +1 -1
- package/dist/ltree/ltree.svelte.js +73 -4
- package/dist/ltree/types.d.ts +8 -0
- package/dist/styles/animations.css +17 -17
- package/dist/styles/base.css +14 -13
- package/dist/styles/checkbox.css +49 -36
- package/dist/styles/context-menu.css +40 -57
- package/dist/styles/dark-mode.css +29 -124
- package/dist/styles/debug.css +15 -15
- package/dist/styles/drag-drop.css +170 -170
- package/dist/styles/drop-zones.css +85 -85
- package/dist/styles/loading.css +12 -12
- package/dist/styles/main.css +44 -44
- package/dist/styles/node.css +24 -21
- package/dist/styles/states.css +59 -28
- package/dist/styles/toggle-icons.css +29 -29
- package/dist/styles/variables.css +121 -119
- package/dist/styles.css +423 -475
- package/package.json +4 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,889 +1,931 @@
|
|
|
1
|
-
# Changelog
|
|
2
|
-
|
|
3
|
-
All notable changes to this project will be documented in this file.
|
|
4
|
-
|
|
5
|
-
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
|
-
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
-
|
|
8
|
-
## [
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
### Changed
|
|
13
|
-
- **
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
-
|
|
19
|
-
- **
|
|
20
|
-
-
|
|
21
|
-
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
-
|
|
26
|
-
-
|
|
27
|
-
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
- **`
|
|
37
|
-
- **
|
|
38
|
-
-
|
|
39
|
-
-
|
|
40
|
-
- **
|
|
41
|
-
-
|
|
42
|
-
-
|
|
43
|
-
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
- **
|
|
48
|
-
|
|
49
|
-
-
|
|
50
|
-
-
|
|
51
|
-
|
|
52
|
-
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
-
|
|
56
|
-
|
|
57
|
-
-
|
|
58
|
-
|
|
59
|
-
###
|
|
60
|
-
-
|
|
61
|
-
-
|
|
62
|
-
- **`
|
|
63
|
-
-
|
|
64
|
-
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
-
|
|
79
|
-
-
|
|
80
|
-
- **`
|
|
81
|
-
-
|
|
82
|
-
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
- **
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
-
|
|
97
|
-
- **`
|
|
98
|
-
-
|
|
99
|
-
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
- **
|
|
103
|
-
- **
|
|
104
|
-
- **`
|
|
105
|
-
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
- **
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
- **`
|
|
113
|
-
-
|
|
114
|
-
- **`
|
|
115
|
-
|
|
116
|
-
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
- **
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
-
|
|
143
|
-
-
|
|
144
|
-
-
|
|
145
|
-
-
|
|
146
|
-
- **`
|
|
147
|
-
- **
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
- **
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
- **`
|
|
158
|
-
-
|
|
159
|
-
- **`
|
|
160
|
-
- **`
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
- **
|
|
175
|
-
- **
|
|
176
|
-
- **
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
-
|
|
180
|
-
|
|
181
|
-
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
-
|
|
188
|
-
-
|
|
189
|
-
- **
|
|
190
|
-
-
|
|
191
|
-
-
|
|
192
|
-
-
|
|
193
|
-
- **
|
|
194
|
-
- **
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
- **
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
- **`
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
- **
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
###
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
-
|
|
251
|
-
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
-
|
|
261
|
-
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
-
|
|
268
|
-
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
## [4.
|
|
288
|
-
|
|
289
|
-
### Added
|
|
290
|
-
- **
|
|
291
|
-
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
- `
|
|
298
|
-
-
|
|
299
|
-
-
|
|
300
|
-
- **
|
|
301
|
-
-
|
|
302
|
-
-
|
|
303
|
-
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
-
|
|
308
|
-
-
|
|
309
|
-
-
|
|
310
|
-
-
|
|
311
|
-
-
|
|
312
|
-
- **
|
|
313
|
-
-
|
|
314
|
-
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
- **
|
|
318
|
-
-
|
|
319
|
-
-
|
|
320
|
-
- **
|
|
321
|
-
- `
|
|
322
|
-
-
|
|
323
|
-
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
- `
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
-
|
|
344
|
-
-
|
|
345
|
-
-
|
|
346
|
-
-
|
|
347
|
-
-
|
|
348
|
-
-
|
|
349
|
-
|
|
350
|
-
-
|
|
351
|
-
-
|
|
352
|
-
-
|
|
353
|
-
-
|
|
354
|
-
|
|
355
|
-
-
|
|
356
|
-
-
|
|
357
|
-
-
|
|
358
|
-
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
-
|
|
364
|
-
-
|
|
365
|
-
-
|
|
366
|
-
-
|
|
367
|
-
|
|
368
|
-
-
|
|
369
|
-
-
|
|
370
|
-
-
|
|
371
|
-
-
|
|
372
|
-
|
|
373
|
-
-
|
|
374
|
-
-
|
|
375
|
-
-
|
|
376
|
-
-
|
|
377
|
-
-
|
|
378
|
-
- **
|
|
379
|
-
-
|
|
380
|
-
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
-
|
|
385
|
-
|
|
386
|
-
-
|
|
387
|
-
-
|
|
388
|
-
-
|
|
389
|
-
-
|
|
390
|
-
- `
|
|
391
|
-
-
|
|
392
|
-
-
|
|
393
|
-
-
|
|
394
|
-
|
|
395
|
-
-
|
|
396
|
-
-
|
|
397
|
-
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
-
|
|
401
|
-
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
-
|
|
405
|
-
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
- **
|
|
411
|
-
-
|
|
412
|
-
-
|
|
413
|
-
-
|
|
414
|
-
-
|
|
415
|
-
-
|
|
416
|
-
-
|
|
417
|
-
-
|
|
418
|
-
-
|
|
419
|
-
-
|
|
420
|
-
|
|
421
|
-
-
|
|
422
|
-
-
|
|
423
|
-
-
|
|
424
|
-
-
|
|
425
|
-
-
|
|
426
|
-
-
|
|
427
|
-
-
|
|
428
|
-
|
|
429
|
-
-
|
|
430
|
-
-
|
|
431
|
-
-
|
|
432
|
-
-
|
|
433
|
-
-
|
|
434
|
-
-
|
|
435
|
-
|
|
436
|
-
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
- **
|
|
443
|
-
-
|
|
444
|
-
-
|
|
445
|
-
-
|
|
446
|
-
|
|
447
|
-
-
|
|
448
|
-
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
- **
|
|
453
|
-
-
|
|
454
|
-
-
|
|
455
|
-
- **
|
|
456
|
-
-
|
|
457
|
-
-
|
|
458
|
-
|
|
459
|
-
-
|
|
460
|
-
-
|
|
461
|
-
|
|
462
|
-
-
|
|
463
|
-
|
|
464
|
-
-
|
|
465
|
-
-
|
|
466
|
-
|
|
467
|
-
- **
|
|
468
|
-
-
|
|
469
|
-
-
|
|
470
|
-
-
|
|
471
|
-
-
|
|
472
|
-
|
|
473
|
-
-
|
|
474
|
-
-
|
|
475
|
-
-
|
|
476
|
-
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
**
|
|
495
|
-
-
|
|
496
|
-
-
|
|
497
|
-
-
|
|
498
|
-
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
-
|
|
502
|
-
-
|
|
503
|
-
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
-
|
|
508
|
-
-
|
|
509
|
-
|
|
510
|
-
-
|
|
511
|
-
|
|
512
|
-
-
|
|
513
|
-
-
|
|
514
|
-
-
|
|
515
|
-
-
|
|
516
|
-
|
|
517
|
-
-
|
|
518
|
-
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
-
|
|
549
|
-
|
|
550
|
-
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
-
|
|
556
|
-
|
|
557
|
-
- Added
|
|
558
|
-
-
|
|
559
|
-
|
|
560
|
-
-
|
|
561
|
-
-
|
|
562
|
-
-
|
|
563
|
-
|
|
564
|
-
-
|
|
565
|
-
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
-
|
|
572
|
-
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
-
|
|
579
|
-
-
|
|
580
|
-
-
|
|
581
|
-
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
-
|
|
586
|
-
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
- **
|
|
603
|
-
-
|
|
604
|
-
-
|
|
605
|
-
-
|
|
606
|
-
|
|
607
|
-
-
|
|
608
|
-
-
|
|
609
|
-
-
|
|
610
|
-
-
|
|
611
|
-
|
|
612
|
-
###
|
|
613
|
-
- **
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
-
|
|
625
|
-
|
|
626
|
-
-
|
|
627
|
-
-
|
|
628
|
-
-
|
|
629
|
-
|
|
630
|
-
-
|
|
631
|
-
-
|
|
632
|
-
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
-
|
|
637
|
-
-
|
|
638
|
-
-
|
|
639
|
-
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
-
|
|
646
|
-
-
|
|
647
|
-
-
|
|
648
|
-
-
|
|
649
|
-
- **
|
|
650
|
-
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
- **
|
|
669
|
-
-
|
|
670
|
-
-
|
|
671
|
-
-
|
|
672
|
-
- **
|
|
673
|
-
-
|
|
674
|
-
-
|
|
675
|
-
-
|
|
676
|
-
-
|
|
677
|
-
-
|
|
678
|
-
|
|
679
|
-
-
|
|
680
|
-
-
|
|
681
|
-
-
|
|
682
|
-
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
- **
|
|
692
|
-
- **
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
-
|
|
698
|
-
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
-
|
|
705
|
-
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
-
|
|
713
|
-
-
|
|
714
|
-
|
|
715
|
-
-
|
|
716
|
-
-
|
|
717
|
-
|
|
718
|
-
-
|
|
719
|
-
-
|
|
720
|
-
-
|
|
721
|
-
|
|
722
|
-
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
-
|
|
729
|
-
-
|
|
730
|
-
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
- **
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
-
|
|
741
|
-
-
|
|
742
|
-
-
|
|
743
|
-
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
- **
|
|
757
|
-
- **
|
|
758
|
-
- **
|
|
759
|
-
- **
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
- **
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
- **
|
|
769
|
-
-
|
|
770
|
-
-
|
|
771
|
-
|
|
772
|
-
- **
|
|
773
|
-
- **
|
|
774
|
-
- **Optimized
|
|
775
|
-
|
|
776
|
-
- **
|
|
777
|
-
- **
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
- **
|
|
782
|
-
-
|
|
783
|
-
-
|
|
784
|
-
-
|
|
785
|
-
- **
|
|
786
|
-
-
|
|
787
|
-
- Automatic
|
|
788
|
-
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
- **
|
|
800
|
-
- **
|
|
801
|
-
- **
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
-
|
|
808
|
-
-
|
|
809
|
-
-
|
|
810
|
-
- **
|
|
811
|
-
-
|
|
812
|
-
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
- **
|
|
816
|
-
- **
|
|
817
|
-
- **
|
|
818
|
-
- **
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
- **
|
|
824
|
-
-
|
|
825
|
-
-
|
|
826
|
-
-
|
|
827
|
-
- **
|
|
828
|
-
-
|
|
829
|
-
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
- **
|
|
834
|
-
-
|
|
835
|
-
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
- **
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
- **
|
|
847
|
-
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
-
|
|
851
|
-
-
|
|
852
|
-
- **
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
- **
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
- `
|
|
868
|
-
- `
|
|
869
|
-
- **
|
|
870
|
-
- **
|
|
871
|
-
- **Debug
|
|
872
|
-
|
|
873
|
-
### Changed
|
|
874
|
-
- **
|
|
875
|
-
- **
|
|
876
|
-
- **
|
|
877
|
-
- **Debug
|
|
878
|
-
|
|
879
|
-
###
|
|
880
|
-
- **
|
|
881
|
-
- **
|
|
882
|
-
- **
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
-
|
|
888
|
-
- Enhanced
|
|
889
|
-
-
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [5.0.0-rc11] - 2026-06-25 [PUBLISHED]
|
|
9
|
+
|
|
10
|
+
Consolidated release folding the work previously staged under the unpublished rc12 and rc13 headings (npm's last published `rc` was rc10; the rc12/rc13 labels were never shipped, so this lands as rc11). Three themes: a `/validate-web-component` alignment sweep against the BlissFramework `web-components` guidelines (BEM/prefix `ltree`→`stv` rename, `is*/should*` boolean naming, dark-mode Strategy-B rewrite, README split), the three-level selection / highlight / focus API normalization, and a batch of drag-drop and highlight-marker correctness fixes. Within-RC references to "rc12" below denote earlier iterations of this same unpublished cycle.
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
- **Selection / highlight / focus API normalized into three symmetric verb-families**: the imperative methods had drifted into "pure chaos" — `selectNode`/`selectNodes` were `@deprecated` aliases that secretly drove the *highlight* set (not checkboxes), the highlight set cleared with `clearHighlight()` while the checkbox set cleared with the differently-named `deselectAll()`, `highlightNodes()` silently *replaced* the set while reading like "add these", there was no imperative way to set the checkbox set by path, and no imperative focus method at all. The surface is now three concerns × the same shape. **Highlight (UI multi-select — `highlightedPaths`):** `highlightNode(path, mode?, opts?)`, `highlightNodes(paths, opts?)` (now **additive**), `setHighlightedPaths(paths, opts?)` (replace), `highlightAll(opts?)`, `clearHighlight(paths?, opts?)` (path-optional). **Selection (checkbox / data state — `selectedPaths`):** `selectNode(path, opts?)` (now actually checks the box, cascades in cascade mode), `selectNodes(paths, opts?)` (additive), `setSelectedPaths(paths, opts?)` (replace), `selectAll(opts?)`, `deselectNode(path, opts?)`, `clearSelection(paths?, opts?)` (renames `deselectAll`). **Focus (single cursor — `focusedNode`):** new `focusNode(path, opts?)` / `clearFocus(opts?)`. Two shared public types replace the inline literals everywhere: `HighlightMode = 'replace' | 'toggle' | 'range'` (the existing `SelectionMode` name was already taken for `'single' | 'multi'`) and `TreeMutationOptions = { silent?: boolean }`. Breaking within the RC: `deselectAll` → `clearSelection`; `clearHighlight({silent})` → `clearHighlight(undefined, {silent})` (options moved to the 2nd arg); `selectNode`/`selectNodes` flip from highlight-aliases to real checkbox setters; `highlightNodes` flips from replace to additive (use `setHighlightedPaths` for the old behavior). Internal `navTo` (keyboard nav) repointed from the old `selectNode`-alias to `highlightNode`. Mirror change applied to `@keenmate/web-treeview` (where the same normalization also renamed its highlight-`selectAll` → `highlightAll`, repointed Ctrl+A to `highlightAll`, and gave it real checkbox `selectNode`/`selectNodes`). `svelte-check` 0 errors; full e2e green.
|
|
14
|
+
- **Boolean Props renamed to follow the `is*/should*/has*/can*` rule** (C-NC-3): ten flags that were ambiguous between verb and noun now read unambiguously as predicates. `showCheckboxes` → `shouldShowCheckboxes`, `clickTogglesCheckbox` → `shouldClickToggleCheckbox`, `accordionExpand` → `isAccordionExpand`, `progressiveRender` → `isProgressiveRender`, `useFlatRendering` → `isFlatRenderingEnabled`, `virtualScroll` → `isVirtualScrollEnabled`, `allowCopy` → `isCopyAllowed`, `autoHandleCopy` → `shouldAutoHandleCopy`, `autoHandleMove` → `shouldAutoHandleMove`, `autoHandlePaste` → `shouldAutoHandlePaste`. Six existing flags already followed the rule (`isSorted`, `isLoading`, `isRendering`, `shouldUseInternalSearchIndex`, `shouldDisplayDebugInformation`, `shouldDisplayContextMenuInDebugMode`) and are unchanged. Old names are not aliased — within an RC cycle the API is unstable by definition; consumers on rc12 search-and-replace each prop site. Applied across `src/lib/`, `src/routes/`, `e2e/`, `docs/`, `ai/`, `README.md`, `CHANGELOG.md`, `CLAUDE.md` (~314 occurrences across 36 files). `svelte-check` reports 0 errors after the sweep.
|
|
15
|
+
- **`dark-mode.css` migrated to Strategy B (color-scheme flipping)**: the previous file (~140 lines) re-declared every `--stv-*` token inside each conditional signal block (Strategy A). The new file (~45 lines) flips `color-scheme: dark` (or `light`) on the framework-theme + per-instance selectors and lets the `light-dark(<light>, <dark>)` fallbacks already present in `variables.css` resolve the dark branch. Three wins: (a) ~5× shorter file — adding a new themeable variable no longer requires touching every signal block; (b) consumer `--base-*` overrides survive — Strategy A's hardcoded dark literals (e.g. `#1a1a1a`) silently replaced themed values, Strategy B leaves the consumer chain untouched; (c) aligns with what web-treeview is migrating to. Signal precedence is unchanged: per-instance `.stv__container[data-theme]` → framework ancestor class → page `color-scheme` via `light-dark()` → OS preference via `@media`.
|
|
16
|
+
- **`component-variables.manifest.json` prefix + variable names refreshed**: top-level `"prefix": "ltree"` → `"prefix": "stv"`, and every `componentVariables[].name` renamed `ltree-X` → `stv-X` (the rc12 BEM rename touched the code but left the manifest out of sync — theme-designer and any consumer reading the manifest saw names that no longer existed in the CSS). All five auto-script "name declared but not defined" warnings (`ltree-rem`, `ltree-primary`, `ltree-success`, `ltree-danger`, `ltree-light`) resolve cleanly after the rename. `--base-*` usage strings updated to reference `--stv-*` where they cross-link.
|
|
17
|
+
- **`--stv-light` dark fallback tuned from `#1a1a1a` to `#2b2b2b`**: `--stv-light` doubles as the context-menu hover surface and the elevated chip background, so a dark value identical to `--stv-bg` (`#1a1a1a`) flattened the elevated surface against the main bg in dark mode. The new value matches `--stv-elevated-bg` and keeps the surface distinguishable. Affects `variables.css:51` only.
|
|
18
|
+
- **`.stv__container` declares `display: block` explicitly** (C-TC-7): the host `<div>` defaults to block already, but the rule requires the declaration to be explicit so the cascade is reasoned-about, and matches web-treeview's container. Single line in `base.css`.
|
|
19
|
+
- **README cut from 1103 → 347 lines** (C-RS-2): three long sections — `## Advanced Usage`, `## Styling and Customization`, `## API Reference` — extracted to topical docs under `docs/`. The README now opens with a new `## What is it` intro (value proposition + audience) and routes readers to deeper material via a `## Demos & docs` link block. What's-New bullets reformatted to the canonical `**lead phrase — short headline** — prose body` form with a real em-dash (U+2014) between the bold lead and the prose (C-RS-16). Historical `.ltree-container` / `--ltree-*` references in the rc10 and rc09 bullets renamed to `.stv__container` / `--stv-*` with a parenthetical note that the BEM rename shipped earlier in this cycle.
|
|
20
|
+
- **CSS prefix rename, BEM class shape**: every CSS class and variable now follows the BlissFramework `naming-conventions.md` BEM rule (`<prefix>__element--modifier`, two underscore levels max), with the registered prefix changing from `ltree` to `stv`. Mechanical part: `--ltree-*` → `--stv-*` (every variable; consumer overrides need to swap the prefix). BEM part: every `.ltree-X` class is rewritten — elements as `.stv__X` (e.g. `.ltree-node` → `.stv__node`, `.ltree-node-content` → `.stv__node-content`, `.ltree-context-menu-item` → `.stv__context-menu-item`), state classes as modifiers (e.g. `.ltree-drag-over` → `.stv__node-content--drag-over`, `.ltree-context-menu-item-disabled` → `.stv__context-menu-item--disabled`, `.ltree-icon-expand` → `.stv__toggle-icon--expand`, `.ltree-drop-zones-around` → `.stv__drop-zones--around`). Semantic rename folded in: `.ltree-selected-{bold,border,brackets}` → `.stv__node-content--highlight-{bold,border,brackets}` and `.ltree-selected-highlight` → `.stv__node-content--highlight-fill`. One non-BEM utility kept: `.stv__clickable` (was `.ltree-clickable`) — applied to both the toggle-icon span and the node-content div as a plain `cursor: pointer` marker; promoting it to a modifier of one or the other would force a duplicate class. `BlissFramework/guidelines` reservation table now lists `stv` (svelte-treeview) and `wtv` (web-treeview) instead of the legacy `ltree` exception. Internal directory names (`src/lib/ltree/`, `ltree-node.svelte.ts`, `ltree-helpers.ts`) and their imports are untouched — those refer to the LTree data structure, not the CSS prefix. The full rename was applied by `scripts/rename-css-prefix.mjs`, kept in-tree.
|
|
21
|
+
- **Context menu positioning ported to `@floating-ui/dom`**: The root menu and every submenu are now placed by `computePosition` + `autoUpdate` instead of raw `left/top` inline styles and CSS `:hover` show/hide. Root menu uses a virtual reference at `(contextMenuX + contextMenuXOffset, contextMenuY + contextMenuYOffset)` with `bottom-start` placement + `flip()` + `shift({padding:8})` so the menu now flips above the cursor near the viewport bottom and slides sideways instead of clipping. Submenus use `right-start` with a `left-start` fallback. `contextMenuXOffset` / `contextMenuYOffset` props keep their existing semantics — the menu's top-left still lands exactly at `(cursor + offsets)`; the `offset` middleware is set to `0` so no extra gap is introduced. Internally: the recursive context-menu snippet in `Tree.svelte` was extracted into a new `ContextMenuLevel.svelte` component, and `ContextMenuItem.svelte` (the public snippet-based component) was updated to mount its submenu on hover (150ms hide grace) instead of relying on CSS `:hover`. CSS `:hover > .ltree-context-submenu` rules and `position: absolute; left: 100%` removed from `context-menu.css`.
|
|
22
|
+
|
|
23
|
+
### Added
|
|
24
|
+
- **`docs/usage.md`, `docs/theming.md`, `docs/examples.md`, `docs/accessibility.md`** at the package root: usage carries the full Props / Methods / Events / Snippets reference; theming carries the `--base-*` / `--stv-*` contract, the cascade-layer footgun warning, dark-mode signal precedence, and the BEM class reference; examples carries the drag-drop / multi-select / context-menu / search / tree-edit cookbook; accessibility carries the keyboard navigation table (derived from `Tree.svelte` `handleTreeKeydown`) and the three-level selection model. All four are linked from the README's `## Demos & docs` section.
|
|
25
|
+
- **`## About` and `## Built with BlissFramework` sections in the README** (C-RS-14 / C-RS-15): About credits KeenMate, frames the Pure Admin / `@keenmate/theme-designer` integration as opt-in via the `--base-*` taxonomy, and clarifies that there's no runtime dependency on Pure Admin. BlissFramework links to the live guidelines.
|
|
26
|
+
- **`VALIDATION-NOTES.md` at the package root**: register for accepted deviations from the BlissFramework component guidelines that the team has decided are correct outcomes for this component. Initial entries cover C-CST-4 (namespace-style Logic class split across `src/lib/core/` + `src/lib/ltree/`), C-NC-6 (six structural `*Member` props without paired `getXCallback` — `id`, `path`, `parentPath`, `level`, `hasChildren`, `order` — because no consumer would want a per-node computed override for "what is this node's id"), C-NC-8 / C-CSS-7 (framework-theme `.dark` / `.light` ancestor qualifiers in `dark-mode.css` are not classes the component emits — they're conventions the component honors), and C-CS-5 (dark-mode fixture lives at SvelteKit routes/, not `docs/test/`, because this is a SvelteKit app). Future `/validate-web-component` runs read this file and downgrade matching flags from ❌ Fail to ⚠️ Exception.
|
|
27
|
+
- **`.stv__node-content--highlight-glow` built-in highlight flavor + a "Glow (soft ring)" option in `/examples/interaction`**: a fifth shipped highlight class (joining bold / border / brackets / fill) — a primary-tinted background plus a soft `box-shadow` glow ring, distinct from the hard-edged `--highlight-border` and the flat `--highlight-fill`. The interaction demo's "Highlighted Style" dropdown gains the option, and a new "Adding your own highlight & focus styles" note + code block walks through both referencing a built-in highlight class and defining a custom `:global()` focus class. The theming reference table now lists `--highlight-fill` and `--highlight-glow` (previously only bold / border / brackets were documented). A fourth "Focused Style" demo option — "Ring (full outline)" (`demo-focused-ring`, page-scoped) — was added alongside the existing outline / underline / bg-tint recipes.
|
|
28
|
+
- **Dynamic Theme Switching demo at the bottom of `/examples/theming`**: ported from `@keenmate/web-daterangepicker`'s `examples-theming.html` "Dynamic Theme Switching" section. A single demo tree with two button rows underneath — seven brand buttons (Default / Material / Neon / Sharp / Soft / Forest / Glass, each rendered in the accent color of the theme it switches to so the row reads as a swatch picker) that hot-swap a class on the wrapper, and three color-scheme buttons (Inherit / Light / Dark) that drive the per-instance `theme` prop on `<Tree>`. Reuses the existing `.playground-wrapper.brand-*` CSS so all seven brand themes work with zero new theme CSS. A live code block under the buttons shows the JS equivalent for both swaps.
|
|
29
|
+
- **`@floating-ui/dom` runtime dependency** (`^1.7.6`): Required by the context menu positioning above. Matches the version pinned in `@keenmate/web-treeview`.
|
|
30
|
+
|
|
31
|
+
### Fixed
|
|
32
|
+
- **`collapseNodes`, `collapseAll`, and `expandAll` now refresh the toggle UI of every affected node**: same class of bug as the rc12 `expandNodes({exclusive:true})` fix, but in three sibling code paths that were missed. `collapseNodes` (the path that handles dblclick-to-collapse in `clickBehavior='select'`), `collapseAll`'s `collapseRecursive`, and `expandAll`'s `setExpandedRecursive` + exclusive-mode `trim` helper all flipped `node.isExpanded` without bumping `node._rev`. The flat-mode keyed `{#each}` then reused the existing Node component, leaving `class:expanded={node.isExpanded}` on the toggle icon stale — visually the chevron stayed rotated-down on collapsed parents (and rotated-right on just-expanded ones) until something else forced a re-render. Children-visibility worked correctly because `visibleFlatNodes` re-derives from `changeTracker`. Each site now bumps `_rev` after flipping `isExpanded`, guarded by an "actually changed" check so already-correct rows don't churn. The same gap existed in web-treeview's ltree — mirrored there for parity, though its DOM-diff renderer reads `data-expanded` directly and masked the user-visible symptom.
|
|
33
|
+
- **`get*Callback` seed-time props now see `node.data` populated**: `insertArray` and `insertBranch` evaluated `getIsExpandedCallback`, `getIsSelectableCallback`, `getIsSelectedCallback`, `getIsDraggableCallback`, and `getIsDropAllowedCallback` *before* assigning `node.data = row`. Consumer callbacks of the shape `(node) => node.data?.X !== false` therefore saw `undefined` and returned their default branch — typically `true` — so every per-node opt-out silently failed. Manifested in `/examples/drag-drop`: File C carrying `isDraggable: false` in its data was still draggable. Fix moves `node.data = row` above the callback block at both sites. Regression coverage: new `src/lib/ltree/seed-callbacks.test.ts` (vitest, 7 cases, runs in 4ms) exercises all five callbacks reading from `node.data?.X` in both `insertArray` and `insertBranch` — 4 of 7 tests fail against the pre-fix code.
|
|
34
|
+
- **`.stv__checkbox` defended against unlayered consumer resets (Bootstrap reboot)**: Bootstrap's `reboot.css` ships an unlayered `label { display: inline-block }` which, per the cascade-layers spec, beats *every* layered rule regardless of selector specificity. Result: in a Bootstrap host page the checkbox label fell back to `inline-block` and the visually-hidden absolute-positioned `<input>` shoved the `.stv__checkbox-box` out of line vertically. Fix: `.stv__checkbox { display: inline-flex !important; margin: 0 4px 0 0 !important; }` in the component layer. The `!important` here is not a hack — it's the documented escape hatch for component-layer rules colliding with unlayered consumer resets (the rc10 changelog already warns about the unlayered-reset footgun; this is the first place we hit it in practice). Audit of the rest of the library found no other reboot-collision-prone elements emitted — every other tag is a `<div>` or `<span>`. Inline comment in `checkbox.css` documents the policy so future `<label>` / `<button>` / `<input>` rules know to mirror it.
|
|
35
|
+
- **Escape now clears the highlight set (multi-select) and cancels a pending cut**: previously Escape did nothing when nodes were Ctrl/Shift-click highlighted — surprising because every analogous tree (Finder, Explorer, VS Code's explorer) treats Escape as "clear what I just picked". The new handler in `Tree.svelte handleTreeKeydown` runs in priority order: (1) if `getClipboardOperation() === 'cut'` and clipboard has content → `cancelCut()` (the cut-dimmed rows un-dim and the operation is aborted, matching the existing Ctrl+C/X/V flow), (2) else if `highlightedPaths.size > 0` → `clearHighlight()`, (3) else `handled = false` so the event falls through to whatever surrounding UI wants it. Checkboxes (`selectedPaths`) are *not* cleared by Escape — they're deliberate state ("yes I want this row") and shouldn't be wiped by a stray keypress; consumers wanting that behavior call `deselectAll()` themselves. Context-menu Escape stays on its own window-level listener and never reaches this path. Mirror change applied to `@keenmate/web-treeview` (its `DomRenderer` previously routed Escape to `deselectAll()`, which only touched the checkbox set — the rc06 three-level split made that wrong; the controller's `clearHighlight()` is the right call).
|
|
36
|
+
- **`addNode` (and therefore `copyNodeWithDescendants` / `applyChanges` `'create'`) now seeds per-node flags from the `getIs*Callback` / `*Member` props — dropped/added nodes are no longer stuck non-draggable and drop-rejecting**: `addNode` built its node with `createLTreeNode()` (defaults `isDraggable: false`, `isDropAllowed: false`) and never ran the seed resolution that `insertArray`/`insertBranch` do, so a programmatically-added node kept those defaults. The DOM `draggable` attribute (`Node.svelte`) and the drag-over / drop gates read `node.isDraggable` / `node.isDropAllowed` *directly* (not via the dynamic `getNodeIs*` resolvers), so the unseeded node rendered non-draggable and rejected drops even when the tree had a `getIsDraggableCallback` / `getIsDropAllowedCallback`. Surfaced in `/examples/drag-drop`: dropping a subtree into the (initially empty) Target tree worked once — the first drop lands on the empty root, which has no node to gate it — but every node copied in via `copyNodeWithDescendants` was then frozen (couldn't be dragged out, and drops on/around it were refused). Fix mirrors `insertArray`'s seed block in `addNode` (callback > member > default for `isExpanded` / `isSelectable` / `isSelected` / `isDraggable` / `isDropAllowed` / `isCollapsible` / `allowedDropPositions`), evaluated after `node.data` is assigned so the callbacks see populated data. Regression coverage: two new `describe` blocks in `src/lib/ltree/seed-callbacks.test.ts` (vitest) exercising `addNode` and `copyNodeWithDescendants` — both flags resolve from the callback on the new/copied nodes (fail against the pre-fix defaults). web-treeview's `addNode` already seeded the core flags (no user-visible bug there); it gained the two remaining lines (`isCollapsible` / `allowedDropPositions`) for full parity.
|
|
37
|
+
- **Multi-drag now respects per-node draggability — a locked node (`isDraggable=false`) in the highlight set no longer rides along**: dragging a multi-highlight set moves every top-level highlighted subtree, but the `isMultiDrag` branch in `_onNodeDrop` pulled those paths straight from `highlightedPaths` via `_getTopLevelHighlightedPaths()` without ever re-checking draggability. The single-drag path is gated at drag *start* (`startDrag` bails when `!getNodeIsDraggable(node)`), but multi-drag bypassed that gate entirely — so a pinned node that merely happened to be Ctrl/Shift-highlighted alongside draggable siblings got moved anyway. Reproduced in `/examples/drag-drop`: highlighting File A + File B + the locked "File C (pinned)" and dragging the block to the other tree carried File C along. Fix adds a `.filter()` on the top-level paths that drops any node whose `getNodeIsDraggable()` is false (locked descendants that ride *inside* a draggable ancestor's subtree are unaffected — only top-level members of the moved set are gated). Regression coverage: new "Multi-Drag with a locked node" section in `/test/drag-drop` + a case in `e2e/drag-drop.spec.ts` (highlight A/B/locked-C, drag onto D → only A/B nest, C stays at root; fails against the pre-fix code which left just `['Lock-D']` at root). Mirror change applied to `@keenmate/web-treeview` (identical `isMultiDrag` path in its `TreeController`). Note this only covers *same-tree* multi-drag, which the library auto-handles; *cross-tree* multi-drag passes only the lead node ref to `onNodeDrop`, so the consumer reconstructs the set from `highlightedPaths` and is responsible for its own draggability filter — `/examples/drag-drop`'s `handleTargetDrop` was updated to do exactly that (it was the path that still carried "File C (pinned)" into the target tree).
|
|
38
|
+
- **`.stv__node-content--highlighted` marker is now a FALLBACK — it no longer fights a configured `highlightedNodeClass`**: the rc12 marker was applied unconditionally (`class:stv__node-content--highlighted={node.isHighlighted}`), so its subtle default background/outline was painted *underneath* whatever `highlightedNodeClass` the consumer set. Picking "Bold" in `/examples/interaction` therefore showed bold text **on top of** the marker's tint — the marker fighting the chosen style. The fix gates the marker on the class being unset: `class:stv__node-content--highlighted={node.isHighlighted && !highlightedNodeClass}`. Now the marker only provides its default look when nothing is configured (so `selectionMode='multi'` still has visible feedback out of the box), and steps aside entirely the moment a highlight class is set — the configured class becomes the sole source of highlight styling, no `!important` overrides needed. While tracing this: `.stv__node-content--multi-selected` was found to be dead CSS (defined in `states.css`, never applied by any component) — left in place for now, flagged for a separate cleanup pass. Mirror change applied to `@keenmate/web-treeview` (its `DomRenderer` had the same unconditional add in both the create and diff-update paths).
|
|
39
|
+
- **`.stv__node-content--focused` is now a pure CSS hook with no default styles**: the rc12 always-on marker shipped a subtle outline (`outline: 2px solid var(--stv-focused-outline, accent 60%)`), reasoning that keyboard nav should be visible without configuration. But the demo's `/examples/interaction` page exposes a "Focused Style: None (invisible focus)" dropdown option that promised exactly that — *no* focus visual — and rc12's default contradicted the dropdown: picking "None" still painted an outline on the focused row (most visible after Shift+click range, where the last-clicked row is the only focused one). The fix honors the dropdown's promise: `.stv__node-content--focused` is now an empty rule (a pure CSS hook, same philosophy as `.stv__node-label`). Apps that want the rc12 default outline write their own rule and pass the class via `focusedNodeClass` — see the `demo-focused-outline` recipe in `/examples/interaction` source. `--stv-focused-outline` is no longer referenced and is dropped as a variable. `.stv__node-content--highlighted` keeps its default subtle look (selectionMode='multi' still produces visible feedback without configuration) — the rc12 promise there is sound; only the focused default went too far. Mirror change applied to `@keenmate/web-treeview` for parity.
|
|
40
|
+
- **Checkbox checkmark + indeterminate dash now scale with `--stv-checkbox-size`**: the previous rules hardcoded pixel positions for the checkmark (`left: 4px; top: 1px; width: 4px; height: 8px`) and the indeterminate dash (`left: 3px; width: calc(--stv-checkbox-size - 8px)` — asymmetric gap), so bumping `--stv-checkbox-size` from the default `1.5rem` (15px) to e.g. `2.4rem` (24px) left the checkmark anchored to the upper-left corner and the dash sitting off-center. New rules use percentages for both indicators and `translate(-50%, -58%) rotate(45deg)` to center the rotated rectangle's optical center (the −58% instead of −50% compensates for the post-rotation visual mass shifting slightly downward). At the default 15px size the rendered checkmark / dash pixels are within ±1px of the pre-fix output, so no visual shift unless the box is resized. New `--stv-checkbox-checkmark-width` variable (default `2px`, intentionally non-scaling — same "hairline" policy as `--stv-checkbox-border-width`).
|
|
41
|
+
- **Desktop drag-drop now honors `node.isDropAllowed`**: the `isDropAllowedMember` / `getIsDropAllowedCallback` props seed `node.isDropAllowed`, but only the touch path consulted it (`TreeController.svelte.ts` lines 3110 + 3199); the desktop `_onNodeDragOver`, `_onNodeDrop`, public `dragOver` / `drop`, and the glow-mode `_onZoneDrop` all skipped it. Net result: setting `isDropAllowed: false` muted touch drops but desktop drag-and-drop went through anyway — surprising given the prop's name. All five sites now gate on `node.isDropAllowed`. The dragover gate suppresses the hover highlight (visual feedback that the target won't accept), and the drop-side gate is the real enforcement (Node.svelte's ondragover calls `event.preventDefault()` itself before forwarding to the controller, so the drop event fires regardless of the dragover gate — the drop handlers must filter). Backwards-compat: every existing fixture and example already opts in via `getIsDropAllowedCallback={() => true}`, so no consumer of the in-tree code is affected.
|
|
42
|
+
- **`expandNodes(path, { exclusive: true })` now updates the toggle UI of collapsed siblings**: The `exclusive` option trims off-spine branches by setting `child.isExpanded = false`, and the expand walk sets `node.isExpanded = true`. Both mutations were missing a `_rev` bump, so the flat-mode keyed `{#each}` reused the existing Node component for the affected rows. The reused component left the toggle icon's `class:expanded` modifier stale — visually the branch looked still-expanded (chevron rotated, even though children were gone) and the just-expanded target's toggle still looked collapsed (even though its children were now visible). The children-visibility side reacted correctly because `visibleFlatNodes` re-derives from `changeTracker`. The fix bumps `_rev` on every mutation in both phases (matching the pattern `Node.svelte`'s accordion-collapse already used). Covered by `e2e/exclusive-expand.spec.ts` against `/test/exclusive-expand`. (web-treeview is unaffected: its `expandNodes` doesn't support `exclusive`, so there's no off-spine trim path to fix.)
|
|
43
|
+
- **`scrollToPath` reliably scrolls to targets in just-expanded deep branches**: With `isFlatRenderingEnabled` + `isProgressiveRender` (the default), `expandNodes` reveals new rows in rAF-deferred batches sized `initialBatchSize` (default 20) and doubling. Previously, `scrollToPath` queried the DOM after one `tick()` — only the immediate batch was rendered, so any target row past that batch produced a `console.warn("DOM element not found")` and the function returned `false` without scrolling or highlighting. Symptom: in long deep trees, search-result navigation appeared to do nothing for hits in collapsed branches with many siblings. Fix: the non-virtual branch of `scrollToPath` now retries the DOM lookup for up to ~6 additional frames (each `await rAF + tick`) before giving up, matching the retry pattern already used by the virtual-scroll branch's highlight step. Covered by `e2e/search-deep.spec.ts` against `/test/search-deep` (1000 leaves over 3 levels, `initialBatchSize=5`, targets at the 10th sibling position so they fall in the deferred batch). Same fix applied to `@keenmate/web-treeview` (identical progressive-render logic, identical bug).
|
|
44
|
+
- **Esc-cancelling a drag no longer leaves the source node "stuck" selected**: `_onNodeDragStart` schedules a rAF that replaces `highlightedPaths` with the dragged node (OS-convention selection sync so multi-drag visually shows what's moving). Previously, if the user then pressed Esc, the browser fired `dragend` with `dropEffect = 'none'` and the controller still left the dragged node highlighted, applying `.stv__node-content--highlight-bold` (bold + primary color) to the source row even though the user had cancelled. Two-part fix: (1) the rAF now snapshots the prior `highlightedPaths` before mutating, and `_onNodeDragEnd` rolls the highlight back when `dropEffect === 'none'`; (2) `_onNodeDragStart` now also attaches the `dragend` listener directly to the source element instead of relying solely on the `.stv__container` listener — the rAF's `tree.refresh()` detaches the source row from the document, so the subsequent `dragend` couldn't bubble to the container and the handler never ran. Direct-element listeners still fire after detachment. Successful drops behave as before — the new highlight stays. Covered by `e2e/drag-esc-cancel.spec.ts`. (web-treeview is unaffected: it has no OS-convention highlight sync on drag start, and already attaches `dragend` at both `bodyEl` and `document` levels.)
|
|
45
|
+
|
|
46
|
+
### Added
|
|
47
|
+
- **Default label now wraps in `<span class="stv__node-label">` — DOM parity with `@keenmate/web-treeview`**: when no `nodeTemplate` snippet is supplied, the display value is emitted inside a `.stv__node-label` span instead of as bare text inside `.stv__node-content`. Matches web-treeview's default render path so the two packages produce structurally identical rows. The wrapper is a CSS hook only — no default styles (intentionally empty rule body). The orphaned `--stv-node-label-font-weight` and `--stv-node-label-margin-right` variables in `variables.css` are removed; both were unreferenced (the class never existed in markup before this release) so no consumer chain breaks. If you were targeting the bare text inside `.stv__node-content` with a CSS rule, switch the selector to `.stv__node-label`.
|
|
48
|
+
|
|
49
|
+
- **`/test/highlight-focus` fixture + `e2e/highlight-focus.spec.ts` (8 tests)**: closes the e2e gap that let the highlight-marker fight ship — there was no test asserting on the `--highlighted` / `--focused` marker classes at all. The fixture exposes Highlight-Class and Focus-Class dropdowns over a `selectionMode='multi'`, `clickBehavior='select'` tree. Spec asserts: no `highlightedNodeClass` → highlighted row gets the fallback `--highlighted` marker; setting Bold/Glow → the custom class is applied and the marker is **absent** (the anti-fight contract — fails against the pre-fix unconditional marker); switching back to none restores the marker and drops the custom class; the `--focused` hook is present on the focused row even with no `focusedNodeClass`; `focusedNodeClass` lands on exactly the focused row; focus is single (moving focus moves both the hook and the custom class, exactly one `--focused` on the page); and highlight + focus classes stack independently on the same row while the marker stays away. Mirrored in `@keenmate/web-treeview` (`test/highlight-focus.html` + `e2e/highlight-focus.spec.ts`, 7 tests — no in-place class-switch test because its diff renderer only re-renders a row on `_rev` bump).
|
|
50
|
+
- **`/test/callbacks` fixture + `e2e/callbacks.spec.ts` (9 tests)**: closes the e2e gap that let the seed-callback bug ship. The existing `/test/drag-drop` fixture covers `allowedDropPositionsMember` (data-field form) but never used `getIsDraggableCallback`, `getAllowedDropPositionsCallback`, or `getIsDropAllowedCallback`, so none of the per-node callback opt-outs had a failing test. The new fixture wires all three callbacks reading from `node.data` and exposes drop state via `data-testid` spans. Spec asserts: `draggable="false"` on a pinned row + no `onNodeDrop` fires when dragging it; drops on a `['child']`-only target snap to child even when aimed at "before"; drops on a `['before','after']` target snap to before/after when aimed at "child"; and drops onto a row with `isDropAllowed: false` don't fire `onNodeDrop` at all. Two `isDraggable` tests and the `isDropAllowed` test all fail against the pre-fix code.
|
|
51
|
+
|
|
52
|
+
## [5.0.0-rc10] - 2026-06-10
|
|
53
|
+
|
|
54
|
+
### Changed
|
|
55
|
+
- **CSS custom properties rescoped from `:root` to `.ltree-container`**: All `--ltree-*` declarations now live on the tree's root element instead of the document root. This mirrors the `:host`-scoped pattern from `@keenmate/web-multiselect` / `web-daterangepicker` and is the only way `--base-*` theming actually works at subtree scope. Previously, a wrapper around the tree that set `--base-accent-color: red` had no effect because `--ltree-primary`'s `var(--base-accent-color, ...)` substitution was frozen at `:root`. With the new scope, the substitution recomputes on `.ltree-container` (a descendant of the wrapper), and `--base-*` set on any ancestor flows through correctly. Multiple trees on the same page can now be themed differently via subtree wrappers.
|
|
56
|
+
|
|
57
|
+
**Consumer migration**: setting `--ltree-*` on a wrapper element (e.g., `.my-theme { --ltree-primary: red }`) no longer cascades into the tree, because `.ltree-container` has its own direct rule for every `--ltree-*` token. To theme a subtree, prefer `--base-accent-color` (and the rest of the `--base-*` taxonomy) on the wrapper — every primary-derived tint follows via the existing `color-mix()` chains. To override an `--ltree-*` directly, target `.ltree-container`: `.my-wrap .ltree-container { --ltree-primary: red }`.
|
|
58
|
+
|
|
59
|
+
### Added
|
|
60
|
+
- **`--ltree-bg` CSS variable**: The tree's `.ltree-container` now paints its own default background (`var(--base-main-bg, light-dark(#ffffff, #1a1a1a))`) so consumers don't need to wrap the tree in a colored container for a visible surface. The dark-mode CSS flips `--ltree-bg` alongside the other surface tokens. Set `--ltree-bg: transparent` to restore the pre-rc10 layered behavior.
|
|
61
|
+
- **Built-in dark mode with belt-and-suspenders coverage**: New `dark-mode.css` partial in an `@layer overrides;` cascade catches all four signals — OS preference via `@media (prefers-color-scheme: dark)`, framework theme classes (`[data-theme="dark"]`, `[data-bs-theme="dark"]`, `.dark`), per-instance opt-in via the new `theme` prop on `<Tree>`, and symmetric `light` selectors so a single tree can be forced back to light on a dark page. Every `--ltree-*` color variable's fallback now uses `light-dark(<light>, <dark>)` so consumers who declare `html { color-scheme: light dark }` get OS-aware behavior automatically with zero JavaScript.
|
|
62
|
+
- **`theme` prop on `<Tree>`**: `'dark' | 'light' | null | undefined`. Forwards to the root `.ltree-container` as `data-theme`, where the stylesheet's per-instance selectors (`.ltree-container[data-theme="dark"]`) take over. Leave `undefined` to inherit from the page (OS, framework class, etc.).
|
|
63
|
+
- **`--ltree-elevated-bg`, `--tree-ghost-shadow` CSS variables**: `elevated-bg` reads through the canonical `--base-elevated-bg` chain for surfaces above the main background (currently feeds the context-menu chain); `tree-ghost-shadow` replaces a hardcoded `rgba(0,0,0,0.3)` literal in the touch drag ghost so consumers can recolor or remove the shadow.
|
|
64
|
+
- **`getIsDropAllowedCallback` prop + `getNodeIsDropAllowed(node)` method**: Callback variant for the existing `isDropAllowedMember` data-field prop, matching the pattern rc09 introduced for `getIsExpandedCallback` / `getIsSelectableCallback` / `getIsSelectedCallback`. Seeded at `insertArray` time. Precedence: callback > member > default. Additionally, `getIsDraggableCallback` is now actually applied during the seed walk (the prop existed in rc09 but inert at insert-time — only consumed lazily in some paths). Precedence: callback > member > default for both.
|
|
65
|
+
|
|
66
|
+
### Changed
|
|
67
|
+
- **CSS file layout aligned with the Bliss web-component guidelines**: All `_<partial>.css` renamed to `<partial>.css` (the underscore was a legacy SASS convention and these are plain CSS modules). `main.css` now declares `@layer variables, component, overrides;` and wraps every `@import` in `layer(...)` — consumers' unlayered overrides automatically beat every rule in the library, no `!important` needed. New canonical Tier-2 stubs (`controls.css`, `floating.css`, `animations.css`) added even where empty, so the file set is identical across the KeenMate component suite. `@keyframes bounce` and `@keyframes ltree-spin` moved into `animations.css`.
|
|
68
|
+
- **Loading overlay background adapts to theme**: `--ltree-loading-bg` no longer hardcodes `rgba(255, 255, 255, 0.8)`; it now composes 80% of `--base-main-bg` against the page so the semi-transparent overlay reads correctly on both light and dark surfaces.
|
|
69
|
+
- **`component-variables.manifest.json` extended**: `base-elevated-bg` added to the `baseVariables` list; `ltree-elevated-bg` and `tree-ghost-shadow` added to `componentVariables`. The manifest now stays in sync with the new variable surface.
|
|
70
|
+
|
|
71
|
+
### Fixed
|
|
72
|
+
- **Virtual scroll stuck at bottom after filter shrinks the tree**: When the user scrolled down and then typed a search filter, `visibleFlatNodes` collapsed and `vsTotalHeight` shrank below the previous scroll position. The browser silently clamped the container's actual `scrollTop`, but the controller's `vsScrollTop` state kept its stale large value — so `vsStartIndex` fell out of range, `flatNodesToRender` was empty, and the scrollbar appeared frozen near the bottom with no content rendered. A new `$effect` now reacts to `vsTotalHeight` and clamps both `vsScrollTop` and the container's `scrollTop` to the new max whenever content shrinks below the current scroll position.
|
|
73
|
+
- **Double-click expand silently broken in `clickBehavior='select'` mode**: The browser's native `dblclick` event never fired because the first click triggered focus → `_setFocusedNode` bumped `node._rev` → the flat-mode `{#each}` destroyed and recreated the row, so the second click landed on a different DOM element and the browser refused to synthesize a `dblclick`. The controller now detects double-clicks manually (`_lastSelectClickPath` / `_lastSelectClickTime`, 400 ms threshold) so a rapid second click on the same node toggles its expand state regardless of re-render. Node-level `_onNodeDblClicked` removed (it could double-toggle if the browser ever did fire `dblclick`).
|
|
74
|
+
|
|
75
|
+
## [5.0.0-rc09] - 2026-06-07
|
|
76
|
+
|
|
77
|
+
### Added
|
|
78
|
+
- **`selectionMode: 'single' | 'multi'` prop** (default `'single'`): Decides how highlight cardinality and modifier keys behave. In `'single'`, plain click sets highlight to one node and Ctrl/Shift+click degrade to plain click; Shift+Arrow and Enter are no-ops. In `'multi'`, Ctrl+click toggles, Shift+click extends a range from the focused node, Shift+Arrow extends one step, and Enter toggles highlight on the focused node. Matches the [selection-highlight-model.md](selection-highlight-model.md) decision record.
|
|
79
|
+
- **Implicit highlight → selection mirroring when `shouldShowCheckboxes` is false**: Every change to `highlightedPaths` is mirrored into `selectedPaths`, and `onSelectionChange` fires alongside `onHighlightChange`. When checkboxes are visible the two sets stay decoupled — highlight is the cursor / range cursor, checkbox state is the form selection. No new toggle prop; the rule follows `shouldShowCheckboxes` directly. Programmatic API (`highlightNode`, `highlightNodes`, `clearHighlight`) mirrors too and respects `{ silent: true }`.
|
|
80
|
+
- **`shouldClickToggleCheckbox` prop**: Boolean (default `false`). When `true` AND `shouldShowCheckboxes` is on AND the node is selectable, a plain click on the node label runs the checkbox-toggle path instead of focusing/highlighting — `focusedNode` and `highlightedPaths` stay untouched. Expand-on-click still fires when `clickBehavior` is `'expand'` or `'expand-and-focus'`. Modified clicks (Ctrl/Shift) fall through to the normal multi-highlight path so range/toggle selection still works.
|
|
81
|
+
- **`getIsExpandedCallback`, `getIsSelectableCallback`, `getIsSelectedCallback` props**: Callback variants for the existing `isExpandedMember` / `isSelectableMember` / `isSelectedMember` data-field props, matching the same pattern as `getIsDraggableCallback` and `getIsCollapsibleCallback`. All three are seed-only — invoked once per node during `insertArray` — so subsequent user mutations (checkbox clicks, expand button) aren't overridden by the callback. Precedence: callback > member > default. The post-insert `selectedPaths` seeding walk now also triggers when `getIsSelectedCallback` is set, not just when `isSelectedMember` is set.
|
|
82
|
+
- **Lucide SVG toggle icons**: All built-in `ltree-icon-*` classes (default chevron, alt filled-triangle, plus/minus, arrow, leaf) now render Lucide SVGs via `mask-image` + `background-color: currentColor` instead of UTF-8 glyph characters (`▶ ▼ ⯈ ⯆ + − → ↓ •`). Icons inherit text color automatically, render cleanly at any size, and don't depend on font fallback. Zero added DOM nodes — still a single `::before` pseudo-element per toggle.
|
|
83
|
+
- **`--ltree-toggle-icon-size` / `--ltree-toggle-icon-width` / `--ltree-toggle-icon-color` CSS variables**: Runtime overrides for the toggle column's icon size (default `16px`, was `12px`), column width (`20px`), and color (`#6c757d`). The SCSS variables (`$tree-toggle-icon-*`) still drive the build-time defaults; the CSS vars are bridged via `var(--…, #{$…})` so users can scale icons live without recompiling SCSS.
|
|
84
|
+
- **Toggle icon live demo on `/examples/theming`**: A new card with radio controls for the four built-in icon sets and the `toggleIconMode` (`rotate` / `swap`) prop, wired to a live `<Tree>`. A code snippet under the controls reflects the current selection so users can copy-paste the props they see.
|
|
85
|
+
- **Full CSS-variable theming surface (`--ltree-*` with `--base-*` chain)**: Every styling value is exposed as a CSS custom property at `:root`. The resolution chain is end-user override → `--base-*` token (shared with other `@keenmate/*` web components) → hardcoded default. Roughly fifty new variables covering typography, node layout, toggle icon, checkbox, selection / highlight / drag states, drop zones (pastel + glow modes), context menu, debug info, scroll highlight, and loading overlay. Pattern matches `@keenmate/web-multiselect` and `@keenmate/web-daterangepicker`.
|
|
86
|
+
|
|
87
|
+
### Changed
|
|
88
|
+
- **`!isSelectable` now also gates highlight (and therefore the mirrored selection in no-checkbox mode), not just the checkbox render**: Click handlers and Shift+arrow range expansion skip non-selectable nodes when building the highlight set. Focus and arrow navigation are NOT gated — the focused row can still land on `!isSelectable` nodes so consumers can show external detail panels (sidebars, breadcrumbs, etc.).
|
|
89
|
+
- **Right-click no longer moves focus or highlight**: `_onNodeRightClicked` only opens the context menu at the right-clicked node. Consumers reading the focused/highlighted set inside `getContextMenuItemsCallback` should now use the `node` argument that the callback receives, since right-click no longer rewrites `focusedNode` / `highlightedPaths`. The selected-nodes argument continues to reflect whatever multi-highlight the user had before opening the menu.
|
|
90
|
+
- **Drag and drop now moves the whole highlight set when the dragged node is in `highlightedPaths`** (same-tree, `operation: 'move'`, `shouldAutoHandleMove: true`): the algorithm picks the **top-level selected** subset — highlighted paths whose nearest highlighted ancestor is NOT in the set — and moves each one's full subtree. Selected descendants of a top-level node are absorbed (they ride along inside their ancestor's subtree, not separately extracted). Cross-tree multi-drag still moves the single dragged node (the cross-tree payload only carries one node).
|
|
91
|
+
- **Multi-drag now chains subsequent moves 'after' the previously moved subtree** (was: subsequent moves landed as children of `dropNode` regardless of the requested position). Dropping selection {A, B, C} 'after D' now produces `[D, A, B, C]` as siblings (previously: `[D]` at root with A/B/C as children of D). 'before D' produces `[A, B, C, D]`. 'child of D' produces D with children `[A, B, C]`. Source order is preserved within the moved set.
|
|
92
|
+
- **Dragging a non-highlighted node now replaces the highlight with that single node before the drag begins**: Matches the Windows Explorer / macOS Finder convention where mousedown on an unselected item selects it. Previously, the prior highlight stayed visible (the `click` event never fired because the user dragged instead of releasing) while the drag silently carried only the single grabbed node — making it impossible to tell what would actually move. The replacement runs in `_onNodeDragStart` and fires `onHighlightChange` / mirrors to `selectedPaths` like a plain click. Skipped if the dragged node is already in the highlight set (multi-drag) or is not selectable.
|
|
93
|
+
- **Keyboard: Enter toggles focused highlight in multi mode (was: always expand/collapse)**. In single mode Enter is now a no-op (falls through to the host). Space falls back to expand/collapse when no checkboxes are shown, but toggles the focused node's checkbox when `shouldShowCheckboxes` is on.
|
|
94
|
+
- **Ctrl/Shift+click on a node no longer toggles expand/collapse**: The modifier-click gesture is reserved for highlight management (toggle / range). Previously, in the default `clickBehavior='expand-and-focus'` mode, modified clicks both updated the highlight set AND expanded/collapsed the row, which made it impossible to multi-select folders without their contents flickering open. Plain click still expands as before. Matches OS file explorer conventions.
|
|
95
|
+
- **Stylesheet ported from SCSS to pure CSS**: `src/lib/styles/main.scss` (1095 lines, one file) split into eleven partials under `src/lib/styles/` (`_variables.css`, `_base.css`, `_node.css`, `_toggle-icons.css`, `_checkbox.css`, `_states.css`, `_drag-drop.css`, `_drop-zones.css`, `_context-menu.css`, `_debug.css`, `_loading.css`) plus a `main.css` entry point that chains them via CSS `@import`. SCSS `@mixin` constructs replaced with shared base-class boilerplate. Build now uses `lightningcss-cli` (one devDep) to bundle the imports into `dist/styles.css`; `sass` removed from devDependencies. Pattern matches the file layout in `@keenmate/web-multiselect` and `@keenmate/web-daterangepicker`.
|
|
96
|
+
- **`--ltree-rem` base sizing unit**: New CSS variable (default `10px`) drives every dimension via `calc(N * var(--ltree-rem))` — font sizes, paddings, margins, widths, radii, spinner size, etc. Set `--ltree-rem` once to scale the whole component proportionally (e.g., `--ltree-rem: 12px` for 20% larger). Set it to `1rem` to scale with document font-size (Pure Admin pattern). Default rendering is visually identical to the previous absolute-px output. Hairline borders (`1.5px` checkbox, `3px` glow) and animation timings stay absolute. Matches the `--ms-rem` / `--drp-rem` pattern in the sibling packages.
|
|
97
|
+
- **`component-variables.manifest.json` published at the package root**: Mirrors the schema used by `@keenmate/web-multiselect` and `@keenmate/web-daterangepicker`. Lists every `--base-*` token the component reads (with `required: true/false` flags) and every `--ltree-*` variable the component publishes (categorized: `color`, `sizing`, `spacing`, `border`, `surface`, `typography`, `state`, `drop-zone`, `context-menu`, `icon`, `animation`). Intended for IDE auto-completion, Storybook integration, and theme-designer tooling. Exported via the `./component-variables.manifest.json` package entry.
|
|
98
|
+
- **Hover background follows `--ltree-primary` by default**: `--ltree-node-hover-bg` default changed from solid `#f8f9fa` to `color-mix(in srgb, var(--ltree-primary) 8%, transparent)`. Themes that set only `--ltree-primary` now get a matching hover tint automatically; the explicit `--base-hover-bg` token override still wins over the derived default. Visually similar to the previous gray at default primary `#0d6efd`.
|
|
99
|
+
- **`--ltree-checkbox-focus-ring` split into tunable parts**: `--ltree-checkbox-focus-ring-width` (default `2px`) and `--ltree-checkbox-focus-ring-color` (default `color-mix(--ltree-primary 25%)`) — the composed `--ltree-checkbox-focus-ring` shorthand still works and now references both. Set just one part to widen the ring or recolor it without redeclaring the full box-shadow.
|
|
100
|
+
|
|
101
|
+
### Breaking
|
|
102
|
+
- **Default `selectionMode = 'single'` changes click semantics for existing multi-select users**: Previously Ctrl/Shift+click always built a multi-highlight regardless of any prop. Now the default is single-select — to keep the old behaviour, opt in with `selectionMode='multi'`. Plain click + Arrow nav are unchanged.
|
|
103
|
+
- **`selectedPaths` becomes populated in no-checkbox trees** because highlight is mirrored into selection. Consumers reading `selectedPaths` from no-checkbox `<Tree>` instances will now see values that previously stayed empty. To keep the old empty-`selectedPaths` behaviour, render checkboxes (and ignore them with CSS) or keep your own derived "real selection" set.
|
|
104
|
+
- **`lastHighlightedPath` (private), `isHighlightAnchor` (public on `LTreeNode`), `.ltree-highlight-anchor` (CSS class), `--ltree-highlight-anchor-width` / `--ltree-highlight-anchor-color` (CSS vars), and the matching `component-variables.manifest.json` entries removed**: The focused node now serves as the anchor for Shift+range operations. Internal Shift+Arrow uses a hidden `_shiftCursor` field — consumers never see it.
|
|
105
|
+
- **`./styles.scss` package export removed.** Consumers using `import '@keenmate/svelte-treeview/styles.scss'` must switch to `import '@keenmate/svelte-treeview/styles.css'` (the canonical export, unchanged). For per-section theming, individual partials are now available under `./styles/_<section>.css`.
|
|
106
|
+
- **`--ltree-primary-rgb` / `--ltree-success-rgb` / `--ltree-danger-rgb` removed** (along with `--base-accent-color-rgb` / `--base-success-color-rgb` / `--base-danger-color-rgb` from the `--base-*` chain). Tinted variants are now derived from the parent color via `color-mix(in srgb, var(--ltree-x) N%, transparent)` — so setting `--ltree-primary: red` automatically tints the multi-select background, dragover background, drop placeholder, focus ring, scroll highlight, debug panel, and danger context-menu hover without needing a companion `-rgb` variable. Matches the `@keenmate/web-multiselect` pattern. Minimum browser baseline: Chrome 111 / Safari 16.4 / Firefox 113 (all from March-May 2023).
|
|
107
|
+
- **Indent scaling semantics**: `--ltree-node-indent-per-level` was previously `0.5rem` (document-relative — followed `html { font-size }`). It is now `calc(0.8 * var(--ltree-rem))` — same visual default (8 px), but scales with `--ltree-rem` instead of document `rem`. To restore the old document-relative behavior, set `--ltree-rem: 1rem`.
|
|
108
|
+
- **SCSS variable overrides (`$tree-*`, `$drop-*`, `$primary-*`) no longer supported.** Migrate to CSS custom properties at runtime: `--ltree-node-font-size: 16px` instead of `@use '@keenmate/svelte-treeview/styles.scss' with ($tree-node-font-size: 16px)`. The full variable surface is documented at `/examples/theming`.
|
|
109
|
+
|
|
110
|
+
### Fixed
|
|
111
|
+
- **Stale `dragOverNodeClass` highlights piling up across rows during drag**: Each `Node` tracked its own local `isDraggedOver` flag, toggled by the row's own `ondragover` / `ondragleave`. HTML5 `dragleave` is unreliable when crossing between sibling rows fast (it can fire with cursor coordinates still inside the leaving row's rect, or skip altogether when `dragenter` on the next row beats it) — so the dashed-green dragover highlight got stuck on rows the cursor had already left, leaking one stale highlight per missed leave. The class is now applied via direct `classList.add/remove` on the previous-and-new DOM nodes from a controller `$effect` that watches the centralized `hoveredNodeForDrop`, matching the existing touch-drag pattern. This guarantees exactly one highlighted row at a time and the cost stays O(1) per node-crossing regardless of tree size — no per-Node prop propagation. The dead per-node `isDraggedOver` state was removed.
|
|
112
|
+
- **`ltree-selected-brackets` invisible when checkboxes are enabled**: The adjacent-sibling rule that hides the `❯ ❮` pseudo-elements (because the checkbox already signals selection) left the class with zero visual effect — highlighted nodes looked identical to unhighlighted ones. The suppressed selector now also applies `font-weight: bold` and `color: var(--ltree-primary)` as a fallback so the highlight is always visible.
|
|
113
|
+
- **Independent checkbox mode auto-checking parents**: In `checkboxMode="independent"`, the post-toggle ancestor walk still ran and propagated `isSelected = true` up to parents when all descendants happened to be selected — making parent checkboxes auto-check themselves despite the "independent" promise. The walk is now gated to cascade mode only; independent mode leaves parents fully alone.
|
|
114
|
+
- **`Node.svelte` stale icon/highlight classes after `tree.update()`**: `expandIconClass`, `collapseIconClass`, `leafIconClass`, `highlightedNodeClass`, `focusedNodeClass`, `dragOverNodeClass`, and `isCopyAllowed` were destructured from the shared `NodeConfig` `$state` proxy as primitive snapshots, so once Node mounted they never updated. Replaced with `$derived(config.x)` so runtime updates (including the new icon-set radios in the theming demo) actually propagate.
|
|
115
|
+
|
|
116
|
+
## [5.0.0-rc08] - 2026-05-27
|
|
117
|
+
|
|
118
|
+
### Added
|
|
119
|
+
- **`{ silent: true }` option on highlight/selection methods**: `highlightNode`, `highlightNodes`, `clearHighlight`, `deselectAll` (and deprecated `selectNode`/`selectNodes`) now accept `{ silent: true }` to update state without firing `onNodeClick` / `onHighlightChange` / `onSelectionChange`. Intended for URL-restore flows (deep links loading form data from query params) where firing the change callback would re-trigger form loaders and clobber the data the URL just supplied. Silent mode also skips focusing the tree container so it doesn't steal focus from whatever the user is interacting with.
|
|
120
|
+
- **Array variants on expand/collapse methods**: `expandNodes`, `collapseNodes`, `expandAll`, and `collapseAll` now accept `string | string[]`. Single emit per call regardless of array length.
|
|
121
|
+
- **`{ exclusive: true }` option on `expandNodes` and `expandAll`**: Opens the target path(s) and collapses anything currently expanded that isn't on the union-of-spines (and, for `expandAll`, not under a target subtree). Equivalent to `collapseAll() + expandNodes(path)` but in a single pass with one emit — downstream listeners (transition animations, URL sync, virtualized renderers) don't see the intermediate fully-collapsed state. Non-collapsible nodes (`isCollapsible === false`) are never touched.
|
|
122
|
+
- **`{ noEmit: true }` option on all four expand/collapse methods**: Skips the change emit, enabling batching multiple operations and emitting once at the end via `tree.refresh()`.
|
|
123
|
+
- **`/examples/silent-highlight` demo page**: URL-restore scenario with loud vs. silent toggle showing how form data is preserved in silent mode.
|
|
124
|
+
- **`/examples/expand-collapse` demo page**: Demonstrates array variants and exclusive focus mode.
|
|
125
|
+
|
|
126
|
+
## [5.0.0-rc07] - 2026-05-23
|
|
127
|
+
|
|
128
|
+
### Added
|
|
129
|
+
- **`isSelectedMember` prop**: Data field name that seeds `node.isSelected` at `insertArray` time. The controller walks the tree after insert and pre-populates the bindable `selectedPaths` Set with every path where the field is truthy — independent of `isSelectableMember`, so non-selectable nodes can still ship checked.
|
|
130
|
+
- **`isSelectableMember` prop now publicly wired**: Previously declared on the core types but not exposed through `Tree.svelte`'s prop/update interface. Now end-to-end usable. Controls whether a node renders a checkbox and carries the `ltree-clickable` class.
|
|
131
|
+
|
|
132
|
+
### Fixed
|
|
133
|
+
- **Filter race with async indexing**: A `bind:searchText` change that landed while the FlexSearch index was still being built (via `requestIdleCallback` batches) saw an empty index, hid the entire tree, and never recovered — no mechanism re-applied the filter once indexing completed. `filterNodes` now remembers the active query and the indexer's `onComplete` callback re-runs it, so the visible filter catches up automatically regardless of `indexerBatchSize`, dataset size, or how fast the user types.
|
|
134
|
+
|
|
135
|
+
## [5.0.0-rc06] - 2026-03-31
|
|
136
|
+
|
|
137
|
+
### Added
|
|
138
|
+
- **`shouldShowCheckboxes` prop**: Renders a checkbox before each selectable node with custom styling and indeterminate support.
|
|
139
|
+
- **`checkboxMode` prop** (`'independent'` | `'cascade'`): Controls whether checking a parent cascades to all descendants. Indeterminate state shown when partial.
|
|
140
|
+
- **`beforeCheckboxToggleCallback` interceptor**: Cancel or override checkbox toggles.
|
|
141
|
+
- **Three-level selection model**: Separated into `focusedNode` (single node, click/arrows), `highlightedPaths` (multi-select, Ctrl/Shift+click), and `selectedPaths` (checkbox data state). Highlight and checkbox state are independent — build a highlight selection, then check/uncheck all highlighted nodes with one checkbox click.
|
|
142
|
+
- **`onHighlightChange` event**: Fires when highlighted paths change (Ctrl+click, Shift+click, etc.).
|
|
143
|
+
- **Bulk checkbox via highlight**: When multiple nodes are highlighted and a checkbox in the highlight is clicked, all highlighted nodes toggle together.
|
|
144
|
+
- **Shift+Arrow/Home/End keyboard highlight**: Shift+ArrowDown/Up extends highlight range by one sibling, Shift+Home/End extends to first/last visible node.
|
|
145
|
+
- **PageUp/PageDown navigation**: Jumps 10 visible nodes forward/back. Shift+PageUp/PageDown extends highlight by 10 nodes.
|
|
146
|
+
- **`ltree-selected-highlight` CSS class**: Explorer-style blue background highlight. Customizable via `--ltree-highlight-bg` and `--ltree-highlight-color`.
|
|
147
|
+
- **Interaction example page** (`/examples/interaction`): Interactive demos for click behavior, checkboxes, multi-select, and keyboard navigation. All settings persisted to localStorage.
|
|
148
|
+
|
|
149
|
+
### Fixed
|
|
150
|
+
- **Keyboard navigation not working after node click**: Clicking a node now auto-focuses the tree container, so arrow keys work immediately without having to click the container separately.
|
|
151
|
+
- **Svelte proxy equality warning on checkbox toggle**: `_setFocusedNode` now compares by path instead of object identity to avoid `state_proxy_equality_mismatch`.
|
|
152
|
+
|
|
153
|
+
### Breaking
|
|
154
|
+
- **`selectedNode` → `focusedNode`**: Renamed prop and bindable. The single focused node (last clicked / arrow-keyed to).
|
|
155
|
+
- **`selectedPaths` repurposed**: Now represents checkbox-only data state. For click/highlight multi-select, use `highlightedPaths`.
|
|
156
|
+
- **`highlightedPaths` (new)**: Replaces old `selectedPaths` for Ctrl+click / Shift+click UI highlight.
|
|
157
|
+
- **`selectedNodeClass` → `highlightedNodeClass`**: CSS class applied to highlighted nodes.
|
|
158
|
+
- **`focusedNodeClass` (new)**: CSS class applied to the single focused node.
|
|
159
|
+
- **`onSelectionChange` repurposed**: Now fires on checkbox selection changes only. Use `onHighlightChange` for highlight changes.
|
|
160
|
+
- **`selectNode()` / `selectNodes()` deprecated**: Use `highlightNode()` / `highlightNodes()` instead.
|
|
161
|
+
|
|
162
|
+
## [5.0.0-rc05] - 2026-03-26
|
|
163
|
+
|
|
164
|
+
### Added
|
|
165
|
+
- **`clickBehavior` prop** (`'select'` | `'expand'` | `'expand-and-focus'`, default `'expand-and-focus'`): Controls what happens on node click. Replaces the boolean `shouldToggleOnNodeClick` prop. Matches canvas package's `ClickBehavior` type.
|
|
166
|
+
|
|
167
|
+
### Breaking
|
|
168
|
+
- **`shouldToggleOnNodeClick` removed**: Replace `shouldToggleOnNodeClick={true}` with `clickBehavior="expand-and-focus"` (default) and `shouldToggleOnNodeClick={false}` with `clickBehavior="select"`.
|
|
169
|
+
|
|
170
|
+
## [5.0.0-rc04] - 2026-03-12
|
|
171
|
+
|
|
172
|
+
### Added
|
|
173
|
+
- **`TreeNavigation<T>` interface**: Pluggable keyboard navigation per renderer. Each renderer (HTML, Canvas) provides its own spatial implementation. Users can override individual methods via `TreeNavigationOverrides<T>`.
|
|
174
|
+
- **Bulk subtree operations**: `insertBranch(parentPath, nodes)`, `replaceBranch(path, nodes)`, `deleteBranch(path)` on TreeController — perform subtree-level add/replace/remove with a single tree emission.
|
|
175
|
+
- **Clipboard API on TreeController**: `copyNodes(paths?)`, `cutNodes(paths?)`, `pasteNodes(targetPath, position, options?)`, `cancelCut()`, `hasClipboard()`, `getClipboardOperation()`. Cut nodes are dimmed via `cutPaths` set. Supports `shouldAutoHandlePaste` (default) and manual mode for server-driven workflows.
|
|
176
|
+
- **Branch-operations example** (`/examples/branch-operations`): Server-simulated cut/paste workflow demonstrating `deleteBranch` + `insertBranch` with clipboard integration.
|
|
177
|
+
|
|
178
|
+
### Fixed
|
|
179
|
+
- **`.ltree-container` missing `outline: none`**: Container div now suppresses browser focus outline.
|
|
180
|
+
|
|
181
|
+
## [5.0.0-rc03] - 2026-03-08
|
|
182
|
+
|
|
183
|
+
### Added
|
|
184
|
+
- **Multi-select**: Ctrl+click (Cmd on Mac) toggles individual nodes in/out of selection. Shift+click selects a range from the last-clicked anchor to the current node. Plain click clears selection and selects one node.
|
|
185
|
+
- **`selectedPaths` bindable prop**: `Set<string>` of all selected node paths. Two-way binding for external control.
|
|
186
|
+
- **`rangeSelectionMode` prop**: `'visual'` (default) selects only visible/expanded nodes between anchor and target; `'logical'` selects all nodes in depth-first tree order including collapsed children.
|
|
187
|
+
- **`onSelectionChanged` event**: `(paths: Set<string>, nodes: LTreeNode<T>[]) => void` fires when selection changes.
|
|
188
|
+
- **`contextMenuCallback` 3rd parameter**: Now receives `selectedNodes?: LTreeNode<T>[]` — enables selection-aware context menus.
|
|
189
|
+
- **Public multi-select API on TreeController**: `selectNode(path, mode)`, `selectNodes(paths)`, `deselectAll()`, `getSelectedNodes()`, `isNodeSelected(path)`.
|
|
190
|
+
- **`SelectionModifiers` type export**: `{ ctrl: boolean; shift: boolean }` for modifier-aware click handling.
|
|
191
|
+
- **`.ltree-multi-selected` CSS class**: Styling for nodes in a multi-selection.
|
|
192
|
+
- **Unified Context Menu types** (`ContextMenuItem`, `ContextMenuDivider`, `ContextMenuEntry`): Shared type system across svelte-treeview and canvas-tree. Breaking change from old API: `title` → `label`, `callback` → `onclick`, `isDivider` flag replaced by separate `ContextMenuDivider` type with `divider: true` discriminator.
|
|
193
|
+
- **Named dividers**: `{ divider: true, label: 'Section' }` renders as `──── Section ────`
|
|
194
|
+
- **Keyboard shortcuts**: `shortcut` field renders right-aligned hint and activates on keypress when menu is open
|
|
195
|
+
- **Submenus**: `children: ContextMenuEntry[]` opens nested menu on hover
|
|
196
|
+
- **Visibility control**: `isVisible: false` hides items in callback approach (snippet approach uses `{#if}`)
|
|
197
|
+
- **Flexible styling**: `className` replaces dedicated `danger` boolean — use `className="danger"` or any custom class
|
|
198
|
+
- **Async onclick**: `onclick` supports `Promise<void>` return with try/catch error handling
|
|
199
|
+
- **`ContextMenuItemC` / `ContextMenuDividerC` Svelte components**: Declarative context menu building inside the `contextMenu` snippet. Supports nested children slot for submenus.
|
|
200
|
+
- **Context menu keyboard shortcuts**: When context menu is open, pressing a shortcut key (e.g. `C`, `Shift+N`, `F2`) triggers the matching item's `onclick`. Supports modifier keys (Ctrl, Shift, Alt). Escape closes the menu.
|
|
201
|
+
- **`isAccordionExpand` prop**: Per-parent accordion behavior — expanding a node automatically collapses its siblings (children of the same parent). Opt-in via `isAccordionExpand={true}`. Respects `isCollapsibleMember`/`getIsCollapsibleCallback` — non-collapsible siblings are not force-collapsed. Programmatic methods (`expandAll`, `expandNodes`) remain unconstrained.
|
|
202
|
+
- **`toggleIconMode` prop** (`'rotate'` | `'swap'`, default `'rotate'`): Controls how expand/collapse icons behave. `'rotate'` mode (new default) always uses `expandIconClass` (▶) and rotates it 90° via CSS when expanded — smooth animated transition. `'swap'` mode switches between `expandIconClass` (▶) and `collapseIconClass` (▼) classes (previous behavior). Ported from `@keenmate/web-treeview`.
|
|
203
|
+
|
|
204
|
+
### Fixed
|
|
205
|
+
- **Expand/collapse icon not updating in flat rendering mode**: `toggleExpanded()` now bumps `node._rev` so the flat-mode `{#each}` key changes and Svelte re-renders the node with the correct icon state.
|
|
206
|
+
- **`.expanded` CSS transform was a no-op**: `.ltree-toggle-icon.expanded` had `rotate(0deg)` (no effect). Fixed to `rotate(90deg)` for the new `rotate` toggle icon mode.
|
|
207
|
+
|
|
208
|
+
## [5.0.0-rc02] - 2026-03-05
|
|
209
|
+
|
|
210
|
+
### Architecture (Breaking)
|
|
211
|
+
- **Core/Renderer split**: Tree logic (`TreeController`) fully separated from rendering. `Tree.svelte` is now a thin wrapper delegating to `TreeController`. New `TreeProvider` component enables custom renderers (Canvas, WebGL, SVG) on the same core.
|
|
212
|
+
- **Drop position naming**: `'above'`/`'below'` renamed to `'before'`/`'after'` throughout (`DropPosition` type, CSS classes, events). `'child'` unchanged.
|
|
213
|
+
- **Canvas rendering extracted**: Canvas-based rendering (`CanvasTree`, layouts, themes) moved to separate package `@keenmate/svelte-treeview-canvas`.
|
|
214
|
+
|
|
215
|
+
### Added
|
|
216
|
+
- **Render mode switch for examples**: Shared `RenderModeSwitch` component across all example pages. Three-button segmented control (Recursive / Progressive / Virtual) with localStorage persistence. All example `<Tree>` instances receive mode props via `{...getTreeProps()}`.
|
|
217
|
+
- **Virtual scroll mode** (`isVirtualScrollEnabled`, `virtualRowHeight`, `virtualOverscan`, `virtualContainerHeight`): Only renders visible nodes + overscan rows in a fixed-height scrollable container. Enables smooth scrolling through trees with 50,000+ nodes while maintaining ~50 DOM nodes. rAF-throttled scroll handler, auto-measures row height if not explicitly set.
|
|
218
|
+
- **Virtual scroll `scrollToPath`**: Index-based scrolling that centers the target node in the viewport, waits for rAF-throttled scroll + re-render, applies highlight with retry logic.
|
|
219
|
+
- **Search navigation UX**: Filter/search mode toggle on the search example page. Filter mode hides non-matching nodes, search mode keeps tree visible and navigates to highlighted results. Includes result counter, prev/next chevron buttons, Enter/Shift+Enter keyboard navigation (round-robin), Escape to clear.
|
|
220
|
+
- **CSS zone auto-expand**: Floating drop zones auto-expand when positions are hidden (via `allowedDropPositions`). Uses `:not(:has())` CSS rules for "around", "above", "below" layouts.
|
|
221
|
+
- **Unified flat rendering indentation**: Both flat and recursive modes use `--tree-node-indent-per-level` CSS variable. Added `flatGap` logic for parent-to-first-child gap matching.
|
|
222
|
+
- **`overscroll-behavior: contain`** on `.ltree-virtual-scroll` container.
|
|
223
|
+
- **`isCollapsibleMember` / `getIsCollapsibleCallback`**: Per-node collapsibility control.
|
|
224
|
+
- **`getIsDraggableCallback`**: Dynamic per-node draggability.
|
|
225
|
+
- **`applyChanges()` batch method**: Apply multiple tree edits in a single operation.
|
|
226
|
+
- **`_rev` change tracking** on nodes for efficient keyed rendering.
|
|
227
|
+
|
|
228
|
+
### Fixed
|
|
229
|
+
- **Floating drop zones clipped by scrollable containers**: Moved floating drop zone rendering from `Node.svelte` (absolute positioning inside each node) to `Tree.svelte` (fixed positioning at tree level with `z-index: 10000`). Zones now escape `overflow: hidden` containers. Handler methods (`isFloatingPositionAllowed`, `handleFloatingZoneDragOver`, `handleFloatingZoneDragLeave`, `handleFloatingZoneDrop`) added to `TreeController`.
|
|
230
|
+
- **Glow and floating drop zones showing simultaneously**: In flat rendering mode, `dropZoneMode` destructured from `NodeConfig` context was a stale snapshot (nodes aren't recreated). Changed to `$derived(config.dropZoneMode)` so it reads through the reactive proxy on each evaluation.
|
|
231
|
+
- **"After" drop position placing node at end instead of between siblings**: `moveNode` and `addNode` converted root nodes' `parentPath` from `''` to `null` via `|| null`, while `insertArray` used `getParentPath()` which returns `''`. This mismatch caused `sortCallback` to skip the `sortOrder` comparison (`'' !== null` → entered parentPath branch → returned 0), preserving insertion order instead of respecting sort order.
|
|
232
|
+
- **Empty tree drop placeholder ignoring `dragDropMode`**: `handleEmptyTreeDragOver/Drop/TouchEnd` now check `dragDropMode !== 'none'` before activating the drop placeholder.
|
|
233
|
+
- **`treeId` not synced on prop changes**: Added `$effect` in `Tree.svelte` to sync `treeId` prop to controller after mount.
|
|
234
|
+
- **`dropZoneStart` not used in glow mode**: `calculateGlowPosition` now uses `dropZoneStart` to compute the child zone threshold instead of hardcoded `width/2`.
|
|
235
|
+
- **Sort order mismatch between recursive and flat modes**: `visibleFlatNodes` applied `sortCallback` during traversal while recursive mode relied on insertion-order `Object.values()`. Removed runtime sort from flat traversal to match recursive mode — both now use insertion-time sorting from `insertArray()`.
|
|
236
|
+
- **Critical `$state()` vs `$state.raw()` performance regression**: TreeController's `data` property deep-proxied user data, causing **5,500x slowdown** with 8,000+ nodes. Changed `data`, `selectedNode`, `insertResult`, `contextMenuNode`, `hoveredNodeForDrop`, `touchDragState`, `flatRenderedIds`, `flatRenderQueue` to `$state.raw()`.
|
|
237
|
+
|
|
238
|
+
### Changed
|
|
239
|
+
- **Custom Layout Example — Explicit Drop Zones**: Replaced invisible spatial detection with visible drop zone pills (Before / After / Child) on dendrograms.
|
|
240
|
+
|
|
241
|
+
## [4.7.2] - 2026-02-17
|
|
242
|
+
|
|
243
|
+
### Fixed
|
|
244
|
+
- **`bodyClass` prop not working** ([#24](https://github.com/keenmate/svelte-treeview/issues/24)): `class:bodyClass` was toggling a literal CSS class named `"bodyClass"` instead of applying the user's custom class value. Changed to `class={bodyClass}`.
|
|
245
|
+
|
|
246
|
+
## [4.7.1] - 2026-02-17
|
|
247
|
+
|
|
248
|
+
### Changed
|
|
249
|
+
- **Drag and Drop Disabled by Default**: `dragDropMode` now defaults to `'none'` instead of `'both'`
|
|
250
|
+
- Most trees are read-only, so this is a safer default
|
|
251
|
+
- To enable drag and drop, explicitly set `dragDropMode="both"` (or `"self"` / `"cross"`)
|
|
252
|
+
|
|
253
|
+
### Fixed
|
|
254
|
+
- Removed development `console.log` statements from `Tree.svelte` and `ltree.svelte.ts`
|
|
255
|
+
|
|
256
|
+
## [4.7.0] - 2026-02-11
|
|
257
|
+
|
|
258
|
+
### Added
|
|
259
|
+
- **Per-Node Drop Position Restrictions**: New feature to control which drop positions are allowed per node
|
|
260
|
+
- `allowedDropPositionsMember` - Property name mapping for static data (e.g., from server)
|
|
261
|
+
- `getAllowedDropPositionsCallback` - Callback for dynamic position logic based on node type
|
|
262
|
+
- `DropPosition` type exported: `'above' | 'below' | 'child'`
|
|
263
|
+
- Example use cases:
|
|
264
|
+
- Trash folder: `['child']` only (can drop INTO, not above/below)
|
|
265
|
+
- Files: `['above', 'below']` only (can't drop INTO a file)
|
|
266
|
+
- Folders: `undefined` or `[]` (all positions allowed - default)
|
|
267
|
+
- Works with both glow mode (snaps to nearest allowed position) and floating mode (only shows allowed zones)
|
|
268
|
+
- Backwards compatible: `undefined`/empty array = all positions allowed
|
|
269
|
+
|
|
270
|
+
### Example Usage
|
|
271
|
+
```typescript
|
|
272
|
+
// Callback approach (dynamic logic)
|
|
273
|
+
function getAllowedDropPositionsCallback(node: LTreeNode<T>): DropPosition[] | null {
|
|
274
|
+
if (node.data?.type === 'file') return ['above', 'below'];
|
|
275
|
+
if (node.data?.type === 'trash') return ['child'];
|
|
276
|
+
return undefined; // all positions allowed
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// Member approach (server data)
|
|
280
|
+
const data = [
|
|
281
|
+
{ path: '1', name: 'Trash', allowedDropPositions: ['child'] },
|
|
282
|
+
{ path: '2', name: 'Document.pdf', allowedDropPositions: ['above', 'below'] },
|
|
283
|
+
{ path: '3', name: 'Projects' }, // all positions (default)
|
|
284
|
+
];
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
## [4.6.0] - 2026-02-11
|
|
288
|
+
|
|
289
|
+
### Added
|
|
290
|
+
- **Flat Rendering Mode**: New `isFlatRenderingEnabled` prop (default: `true`) for significantly faster rendering
|
|
291
|
+
- Renders all visible nodes in a single `{#each}` loop instead of recursive components
|
|
292
|
+
- Initial render ~12x faster (300ms → 25ms for 5500 nodes)
|
|
293
|
+
- Progressive rendering batches initial load to prevent UI freeze
|
|
294
|
+
- Documentation: `docs/FLAT_MODE_PERFORMANCE.md`
|
|
295
|
+
- **Context-Based Node Configuration**: Moved stable callbacks and config to Svelte context
|
|
296
|
+
- `NodeCallbacks<T>` interface for all event handlers (click, drag, drop, touch)
|
|
297
|
+
- `NodeConfig` interface for stable configuration (icons, classes, drop zone settings)
|
|
298
|
+
- Eliminates inline arrow function re-renders (5500 nodes no longer re-evaluate on array changes)
|
|
299
|
+
- Exported types: `NodeCallbacks`, `NodeConfig` from package index
|
|
300
|
+
- **Async beforeDropCallback**: `beforeDropCallback` now supports async/Promise return values
|
|
301
|
+
- Enables showing confirmation dialogs before completing a drop
|
|
302
|
+
- Can await user input to decide whether to cancel, proceed, or modify the drop
|
|
303
|
+
- Example: `async (dropNode, draggedNode, position) => { return await showDialog(); }`
|
|
304
|
+
|
|
305
|
+
### Enhanced
|
|
306
|
+
- **Exponential Batch Sizing**: Progressive rendering now uses exponential batching (20 → 40 → 80 → 160...)
|
|
307
|
+
- First batch (20 nodes) renders instantly for immediate visual feedback
|
|
308
|
+
- Batch size doubles each frame up to `maxBatchSize` (default 500)
|
|
309
|
+
- New props: `initialBatchSize` (default 20), `maxBatchSize` (default 500)
|
|
310
|
+
- Replaces fixed `renderBatchSize` prop
|
|
311
|
+
- Expand/collapse on large trees (>1000 nodes): Immediate add to minimize diffs
|
|
312
|
+
- **Tree Editor Example**: Enhanced with async drop validation dialog
|
|
313
|
+
- Demonstrates async `beforeDropCallback` with native confirm dialog
|
|
314
|
+
- Shows warning when drop position is modified
|
|
315
|
+
|
|
316
|
+
### Fixed
|
|
317
|
+
- **Scroll Highlight Stacking**: Fixed multiple nodes staying highlighted when rapidly clicking prev/next
|
|
318
|
+
- Previous highlight now immediately cleared when navigating to new node
|
|
319
|
+
- Timeout properly cancelled to prevent stale highlight removal
|
|
320
|
+
- **Flat Mode Node Updates After Move**: Fixed nodes not re-rendering after `moveNode()` in flat mode
|
|
321
|
+
- Keyed each now uses `node.id + path + hasChildren` to detect moved nodes
|
|
322
|
+
- Moved nodes now correctly update their indentation level
|
|
323
|
+
- Parent nodes correctly update toggle icon when children are moved away
|
|
324
|
+
|
|
325
|
+
### Changed
|
|
326
|
+
- **Default Rendering Mode**: `isFlatRenderingEnabled` now defaults to `true` (was `false`)
|
|
327
|
+
- **Default Progressive Render**: `isProgressiveRender` now defaults to `true` (was `false`)
|
|
328
|
+
|
|
329
|
+
## [4.5.0] - 2026-02-09
|
|
330
|
+
|
|
331
|
+
### Added
|
|
332
|
+
- **Drop Zone Layout Configuration**: New props to customize drop zone appearance and positioning
|
|
333
|
+
- `dropZoneLayout` - Controls zone arrangement with 5 layout options:
|
|
334
|
+
- `'around'` (default) - Above zone on top, Below/Child zones on bottom
|
|
335
|
+
- `'above'` - All 3 zones in a horizontal row above the node
|
|
336
|
+
- `'below'` - All 3 zones in a horizontal row below the node
|
|
337
|
+
- `'wave'` - Zones stacked vertically (above/child/below) with fixed width
|
|
338
|
+
- `'wave2'` - Diagonal wave pattern with Above/Below offset 7% to the left
|
|
339
|
+
- `dropZoneStart` - Number (0-100) controlling where zones start horizontally (default: 33%)
|
|
340
|
+
- `dropZoneMaxWidth` - Max width in pixels for wave layouts (default: 120px)
|
|
341
|
+
- **New TypeScript Type**: `DropZoneLayout` type exported from `types.ts`
|
|
342
|
+
- **Mobile Touch Drag and Drop**: Full touch support for drag and drop on mobile devices
|
|
343
|
+
- Long-press (300ms) to initiate drag - distinguishes from tap and scroll
|
|
344
|
+
- Visual ghost element follows finger during drag showing the dragged node
|
|
345
|
+
- Drop target highlighting using existing `dragOverNodeClass` prop
|
|
346
|
+
- Haptic feedback via `navigator.vibrate()` when drag starts (on supported devices)
|
|
347
|
+
- Automatic cancellation if finger moves >10px before long-press completes (allows normal scrolling)
|
|
348
|
+
- Works alongside existing desktop HTML5 drag and drop - same `onNodeDrop` callback for both
|
|
349
|
+
- **Drop Placeholder for Empty Trees**: When dragging nodes to an empty tree, a drop zone placeholder appears
|
|
350
|
+
- Shows visual drop target in empty trees during drag operations
|
|
351
|
+
- Works with both desktop (HTML5 DnD) and touch drag
|
|
352
|
+
- Customizable via `dropPlaceholder` snippet prop for custom content
|
|
353
|
+
- `onNodeDrop` callback receives `null` as `dropNode` for root-level drops into empty trees
|
|
354
|
+
- **Drop Position Indicators**: Visual indicators showing exactly where dropped items will be placed
|
|
355
|
+
- Three drop positions per node: `'above'` (sibling before), `'child'` (as child), `'below'` (sibling after)
|
|
356
|
+
- Absolutely positioned indicators on right half of node to prevent layout shifts
|
|
357
|
+
- Position calculated from mouse Y: top 25% = above, middle 50% = child, bottom 25% = below
|
|
358
|
+
- New CSS classes: `.ltree-drop-indicators`, `.ltree-drop-above`, `.ltree-drop-child`, `.ltree-drop-below`
|
|
359
|
+
- **Root Drop Zone**: Drop zone that appears at bottom of non-empty trees during drag
|
|
360
|
+
- Allows dropping items as root-level nodes in trees that already have content
|
|
361
|
+
- New CSS class: `.ltree-root-drop-zone`
|
|
362
|
+
- **Drag Drop Mode Control**: New `dragDropMode` prop to control allowed drag operations
|
|
363
|
+
- `'none'` - Drag and drop disabled
|
|
364
|
+
- `'self'` - Only within same tree
|
|
365
|
+
- `'cross'` - Only between different trees
|
|
366
|
+
- `'both'` - Both self and cross-tree (default)
|
|
367
|
+
- **Sibling Order Support**: New `orderMember` prop for explicit sibling ordering
|
|
368
|
+
- Specifies which field in user data contains the sort order value
|
|
369
|
+
- Used by default sort to order siblings within the same parent
|
|
370
|
+
- Required for proper above/below positioning in drag-drop tree editors
|
|
371
|
+
- Example: `orderMember="sortOrder"` with data like `{ path: '1.1', name: 'A', sortOrder: 10 }`
|
|
372
|
+
- **Tree Editor Helper Methods**: New methods for building tree editors
|
|
373
|
+
- `getChildren(parentPath)` - Get direct children of a node
|
|
374
|
+
- `getSiblings(path)` - Get all siblings of a node (including itself)
|
|
375
|
+
- `getNodeByPath(path)` - Get a node by its path
|
|
376
|
+
- `refreshSiblings(parentPath)` - Re-sort children of a parent using orderMember
|
|
377
|
+
- `refreshNode(path)` - Trigger re-render for a specific node
|
|
378
|
+
- **Tree Editor Mutation Methods**: New methods for modifying tree structure
|
|
379
|
+
- `addNode(parentPath, data, pathSegment?)` - Add a new node to the tree
|
|
380
|
+
- `moveNode(sourcePath, targetPath, position)` - Move a node with full subtree to a new location
|
|
381
|
+
- Supports 'above', 'below', and 'child' positions
|
|
382
|
+
- Automatically updates paths of all descendants
|
|
383
|
+
- Calculates order values when orderMember is set
|
|
384
|
+
- `removeNode(path, includeDescendants?)` - Remove a node from the tree
|
|
385
|
+
- **Example Pages**: New `/examples` route with interactive demos (same look-and-feel as web-multiselect)
|
|
386
|
+
- Landing page with feature cards linking to 7 example sections
|
|
387
|
+
- Basic Examples: tree rendering, expand level control, scroll to path, programmatic expand/collapse
|
|
388
|
+
- Drag & Drop: two-tree drag demo, touch drag instructions, drop placeholder customization
|
|
389
|
+
- Context Menu: callback-based menus, dynamic items, icons, disabled states, dividers
|
|
390
|
+
- Search & Filter: live filtering with `searchText`, `searchNodes()` query method
|
|
391
|
+
- Theming: CSS variable reference, theme examples (default, purple, dark, green)
|
|
392
|
+
- Data Structures: path-based hierarchy, custom separators, insert result validation
|
|
393
|
+
- Tree Editor: add/move/remove nodes with drag-drop and orderMember support
|
|
394
|
+
- **Drop Indicator Arrows**: Visual arrow indicators for glow-mode drop zones
|
|
395
|
+
- Arrows positioned at 66% of row width, centered vertically
|
|
396
|
+
- Uses Lucide SVG icons as data URIs: `arrow-big-up`, `arrow-big-down`, `arrow-big-right-dash`
|
|
397
|
+
- Child arrow rotated 45° for diagonal pointing effect
|
|
398
|
+
- Fully customizable via SCSS variables
|
|
399
|
+
- **Ctrl+Drag Copy Operation**: Full support for copying nodes via Ctrl+drag
|
|
400
|
+
- `isCopyAllowed` prop enables Ctrl+drag to copy instead of move
|
|
401
|
+
- `shouldAutoHandleCopy` prop (default: true) controls whether Tree auto-handles same-tree copies
|
|
402
|
+
- `true`: Tree creates copy with generated ID (`{id}_copy_{timestamp}`) - good for batch/offline mode
|
|
403
|
+
- `false`: User callback handles copy (for DB/API integration) - good for online/live mode
|
|
404
|
+
- Copy operations respect drop position (above/below/child) just like moves
|
|
405
|
+
- Visual feedback: `.ltree-drop-copy` class applied during copy operations
|
|
406
|
+
- **Position Support for copyNodeWithDescendants**: Enhanced copy method now supports sibling positioning
|
|
407
|
+
- New optional parameters: `siblingPath` and `position` ('above' | 'below')
|
|
408
|
+
- Copies can be placed at specific positions relative to siblings
|
|
409
|
+
- Uses same `orderMember` logic as `moveNode` for consistent ordering
|
|
410
|
+
- **Logging Infrastructure**: Categorized logging using vendored loglevel library
|
|
411
|
+
- Six log categories: `LTREE:INIT`, `LTREE:DATA`, `LTREE:RENDER`, `LTREE:INDEX`, `LTREE:DRAG`, `LTREE:UI`
|
|
412
|
+
- Color-coded console output with timestamps for easy debugging
|
|
413
|
+
- Disabled by default (silent mode) for production
|
|
414
|
+
- Exported utilities: `enableLogging()`, `disableLogging()`, `setLogLevel()`, `setCategoryLevel()`
|
|
415
|
+
- UI logging: node clicks, expand/collapse, selection changes, context menu
|
|
416
|
+
- Drag logging: drag start/end, drop operations, touch drag events
|
|
417
|
+
- Render logging: progressive rendering frame stats
|
|
418
|
+
- Index logging: async search indexing progress
|
|
419
|
+
- New `/dev/logging` demo page for testing log levels and categories
|
|
420
|
+
- **Performance Logging**: Dedicated performance measurement utilities
|
|
421
|
+
- Measures key operations: `insertArray` (conversion/sort/insert phases), `filterNodes`, `expandAll`, `collapseAll`
|
|
422
|
+
- Output includes duration, item count, per-item time, and items/sec throughput
|
|
423
|
+
- Summary view showing breakdown by phase with percentages
|
|
424
|
+
- Configurable threshold to only log operations slower than X ms
|
|
425
|
+
- Exported utilities: `enablePerfLogging()`, `disablePerfLogging()`, `setPerfThreshold(ms)`
|
|
426
|
+
- Browser console access: `window.components['svelte-treeview'].perf.enable()`
|
|
427
|
+
- Purple color-coded output for easy identification
|
|
428
|
+
- **Global Runtime API**: `window.components['svelte-treeview']` for browser console access
|
|
429
|
+
- `config` - Package info (name, version, author, license, repository, homepage) read from package.json at build time
|
|
430
|
+
- `version()` - Returns current version string
|
|
431
|
+
- `logging.enableLogging()` / `logging.disableLogging()` - Toggle all logging
|
|
432
|
+
- `logging.setLogLevel(level)` - Set level for all categories
|
|
433
|
+
- `logging.setCategoryLevel(category, level)` - Set level for specific category
|
|
434
|
+
- `logging.getCategories()` - List available log categories
|
|
435
|
+
- **Container-Scoped Scrolling**: New `containerScroll` option for `scrollToPath()`
|
|
436
|
+
- `scrollToPath(path, { containerScroll: true })` scrolls only within the nearest scrollable ancestor
|
|
437
|
+
- Prevents page-level scrolling when tree is inside a scrollable container
|
|
438
|
+
- Automatically finds the scrollable parent element (overflow: auto/scroll)
|
|
439
|
+
- Useful for search result navigation without disrupting page position
|
|
440
|
+
|
|
441
|
+
### Enhanced
|
|
442
|
+
- **Drop Zone Styling**: Improved visual feedback during drag operations
|
|
443
|
+
- Semi-transparent zones (0.25 opacity) that become solid (0.85) when hovered
|
|
444
|
+
- Modern pastel color palette: sage green for Above, peach/coral for Below, lavender for Child
|
|
445
|
+
- Interactive controls in `/examples/drag-drop` to test all layout configurations
|
|
446
|
+
- **Drop Zone SCSS Variables**: Full customization of drop zone appearance via SCSS variables
|
|
447
|
+
- `$drop-zone-border-radius` - Border radius for all zones (default: 0)
|
|
448
|
+
- Per-zone variables for backgrounds, colors, and shadows in both inactive and active states:
|
|
449
|
+
- Above: `$drop-zone-above-bg`, `$drop-zone-above-color`, `$drop-zone-above-active-bg`, `$drop-zone-above-active-color`, `$drop-zone-above-active-shadow`
|
|
450
|
+
- Below: `$drop-zone-below-bg`, `$drop-zone-below-color`, `$drop-zone-below-active-bg`, `$drop-zone-below-active-color`, `$drop-zone-below-active-shadow`
|
|
451
|
+
- Child: `$drop-zone-child-bg`, `$drop-zone-child-color`, `$drop-zone-child-active-bg`, `$drop-zone-child-active-color`, `$drop-zone-child-active-shadow`
|
|
452
|
+
- **Drop Zone Positioning**: Moved drop zones from inside `.ltree-node-content` to `.ltree-node-row` level
|
|
453
|
+
- Eliminates padding-related gaps that made zones hard to reach
|
|
454
|
+
- More predictable positioning relative to the full row width
|
|
455
|
+
- **Wave2 Layout Overlap**: Added 10% overlap for Above/Below zones in wave2 layout
|
|
456
|
+
- Ensures first node's Above zone and last node's Below zone are always reachable
|
|
457
|
+
- Child zone shrunk to 80% height to accommodate overlap without zone collision
|
|
458
|
+
- **dropZoneStart Flexibility**: Now accepts both number (percentage) and string (any CSS value)
|
|
459
|
+
- Number: treated as percentage (e.g., `33` → `33%`)
|
|
460
|
+
- String: used as-is (e.g., `"33%"`, `"50px"`, `"3rem"`)
|
|
461
|
+
- **Touch UX**: Added CSS properties to prevent text selection during touch drag
|
|
462
|
+
- `-webkit-user-select: none` and `-webkit-touch-callout: none` on node content
|
|
463
|
+
- **Ghost Element Styling**: New `.ltree-touch-ghost` CSS class with customizable CSS variables
|
|
464
|
+
- `--tree-ghost-bg`: Background color (default: rgba(59, 130, 246, 0.9))
|
|
465
|
+
- `--tree-ghost-color`: Text color (default: white)
|
|
466
|
+
- **Drop Placeholder Styling**: New `.ltree-drop-placeholder` and `.ltree-drop-placeholder-content` CSS classes
|
|
467
|
+
- **Drop Indicator Arrow SCSS Variables**: Full customization of arrow indicators via SCSS variables
|
|
468
|
+
- `$drop-arrow-above`, `$drop-arrow-below`, `$drop-arrow-child` - SVG data URIs for each direction
|
|
469
|
+
- `$drop-arrow-size` - Arrow size (default: 24px)
|
|
470
|
+
- `$drop-arrow-position` - Horizontal position within row (default: 66%)
|
|
471
|
+
- `$drop-arrow-above-rotation`, `$drop-arrow-below-rotation`, `$drop-arrow-child-rotation` - Rotation angles
|
|
472
|
+
- **Search Example Page Redesign**: Merged filter and search cards into unified search experience
|
|
473
|
+
- Single search input with live filtering and result navigation
|
|
474
|
+
- Prev/Next buttons to traverse search results with wrap-around
|
|
475
|
+
- Result counter showing "X of Y" position indicator
|
|
476
|
+
- Keyboard navigation: Enter = next result, Shift+Enter = previous
|
|
477
|
+
- Clickable result list with active item highlighting
|
|
478
|
+
- Auto-scroll to first result when searching
|
|
479
|
+
|
|
480
|
+
### Fixed
|
|
481
|
+
- **Empty Tree Drop Placeholder**: Fixed drop placeholder not appearing when dragging to empty trees
|
|
482
|
+
- Added missing `ondragenter` handler to empty state divs
|
|
483
|
+
- Added `min-height: 60px` to `.ltree-empty-state` to ensure drop target is always reachable
|
|
484
|
+
- **Drag-Drop Demo ID/Path Mismatch**: Fixed bug where second node drop to first node didn't work on first try
|
|
485
|
+
- Root cause: `nextId++` post-increment caused id and path to use different values
|
|
486
|
+
- Fixed by extracting `const itemId = nextId++` before using in object properties
|
|
487
|
+
- **Tree Data Reset**: Fixed `insertArray` not clearing existing tree data when called with new/empty data
|
|
488
|
+
- Previously, setting `data = []` would not clear the tree - existing nodes remained visible
|
|
489
|
+
- Now `insertArray` properly resets root children, nodeCount, and maxLevel before inserting
|
|
490
|
+
- **Example Pages Data Insertion**: Added `isSorted={true}` to all example page Tree components
|
|
491
|
+
- Prevents "Could not find parent node" errors caused by `sortCallback` sorting data before insertion
|
|
492
|
+
- When `sortCallback` alphabetizes data, children could be inserted before parents (e.g., "AuthService" before "Services")
|
|
493
|
+
- `isSorted={true}` tells the tree to skip pre-sorting and only use `sortCallback` for display ordering
|
|
494
|
+
- **Search Example Async Index**: Added note explaining that search index is built asynchronously
|
|
495
|
+
- Added Enter key support for better UX when retrying searches
|
|
496
|
+
- Users are now informed to wait a moment if no results appear immediately after page load
|
|
497
|
+
- **Search Example Reactivity**: Made "Search Nodes (Query)" input reactive
|
|
498
|
+
- Added `$effect` to automatically trigger search when input changes
|
|
499
|
+
- **Glow Mode Border Radius**: Fixed border-radius appearing on glow drop indicators during drag
|
|
500
|
+
- Changed `$tree-node-content-border-radius` default from `4px` to `0`
|
|
501
|
+
- **Search Example Documentation**: Fixed incorrect prop name in search configuration table
|
|
502
|
+
- Changed `searchValueCallback` → `getSearchValueCallback` (correct prop name)
|
|
503
|
+
- Fixed callback signature from `(item: T) => string` to `(node: LTreeNode<T>) => string`
|
|
504
|
+
- **LTreeNode Type Export**: Added `LTreeNode` re-export from `types.ts`
|
|
505
|
+
- Fixes import errors when using `import type { LTreeNode } from '$lib/ltree/types'`
|
|
506
|
+
- **Search Example Endless Loop**: Fixed `$effect` causing infinite loop on search
|
|
507
|
+
- Used Svelte's `untrack()` to prevent reactive state updates from re-triggering the effect
|
|
508
|
+
- Effect now only reacts to `searchText` changes, not internal state mutations
|
|
509
|
+
- **Search Example Container Scroll**: Fixed prev/next navigation scrolling the entire page
|
|
510
|
+
- Now uses `scrollToPath(path, { containerScroll: true })` for container-scoped scrolling
|
|
511
|
+
- **Critical Performance Bug in insertArray**: Fixed O(n²) algorithm causing 85+ second load times
|
|
512
|
+
- Progressive render check was iterating all remaining nodes for every node at expandLevel
|
|
513
|
+
- With 17,000 nodes this caused ~145 million iterations instead of ~34,000
|
|
514
|
+
- Fix: Pre-compute last expandLevel index once, then use simple index comparison
|
|
515
|
+
- Result: Load time reduced from 85+ seconds to under 1 second
|
|
516
|
+
- **Global API Constants**: Fixed `__PACKAGE_NAME__ is not defined` error when using library in other projects
|
|
517
|
+
- Vite `define` constants only work during dev, not when library is built with `svelte-package`
|
|
518
|
+
- Added `scripts/generate-constants.js` to bake package.json values into `constants.generated.ts`
|
|
519
|
+
|
|
520
|
+
### Important - Svelte 5 Performance
|
|
521
|
+
|
|
522
|
+
**Use `$state.raw()` for large datasets passed to Tree component**
|
|
523
|
+
|
|
524
|
+
When passing large arrays (1000+ items) to the Tree component, use `$state.raw()` instead of `$state()` to avoid severe performance degradation:
|
|
525
|
+
|
|
526
|
+
```typescript
|
|
527
|
+
// SLOW - Svelte deeply proxies all 8000+ objects, causing 5000x slowdown
|
|
528
|
+
let treeNodes = $state<TreeNode[]>([])
|
|
529
|
+
treeNodes = response.data // Each item becomes a Proxy
|
|
530
|
+
|
|
531
|
+
// FAST - Array is reactive but items remain plain objects
|
|
532
|
+
let treeNodes = $state.raw<TreeNode[]>([])
|
|
533
|
+
treeNodes = response.data // Items stay as plain objects
|
|
534
|
+
```
|
|
535
|
+
|
|
536
|
+
**Why this matters:**
|
|
537
|
+
- `$state()` creates deep proxies - every nested object becomes a Proxy
|
|
538
|
+
- Tree's `insertArray()` accesses multiple properties on each data item
|
|
539
|
+
- With 8000 items × ~10 property accesses = 80,000 proxy operations
|
|
540
|
+
- Proxy overhead: ~2.2ms per item vs ~0.0004ms for plain objects (5,500x slower)
|
|
541
|
+
|
|
542
|
+
**Symptoms of this issue:**
|
|
543
|
+
- Tree takes 15-90+ seconds to render with thousands of items
|
|
544
|
+
- Console shows `[Violation] 'message' handler took XXXXms`
|
|
545
|
+
- Same data loads instantly in isolated test environment
|
|
546
|
+
|
|
547
|
+
**The fix does NOT affect reactivity** - changes to `treeNodes` array itself still trigger updates. Only the individual items inside lose deep reactivity, which Tree doesn't need.
|
|
548
|
+
- Build now runs `npm run generate-constants` before `svelte-package`
|
|
549
|
+
- **Glow Mode Border Radius**: Added `!important` to `border-radius: 0` on glow classes to ensure override
|
|
550
|
+
- Removed `border-radius: 4px !important` from `.ltree-dragover-glow` class
|
|
551
|
+
- Removed `border-radius` from `.ltree-node-content` transition to prevent animation glitch
|
|
552
|
+
- **Glow Mode Drop Validation**: Fixed glow showing on invalid drop targets
|
|
553
|
+
- When `dragDropMode='cross'` prevents drops, glow no longer appears on hover
|
|
554
|
+
- Cleared `hoveredNodeForDrop` state when drop validation fails
|
|
555
|
+
- Debug logging for drop rejection now gated behind `shouldDisplayDebugInformation`
|
|
556
|
+
- **Glow Mode Border Radius Transition**: Fixed double border artifact on node hover during drag
|
|
557
|
+
- Added `border-radius` to transition property for smooth animation
|
|
558
|
+
- Eliminates visual conflict between base rounded corners and glow's straight corners
|
|
559
|
+
- **Ctrl+Drag dropEffect Timing**: Fixed copy operation failing with `dropEffect: none`
|
|
560
|
+
- Root cause: Node's `ondragover` read stale `dropOperation` prop before Tree updated it
|
|
561
|
+
- Fix: Node now reads `event.ctrlKey` directly to calculate dropEffect immediately
|
|
562
|
+
- Also added dropEffect confirmation in `ondrop` handlers for spec compliance
|
|
563
|
+
- **Missing isCopyAllowed Prop in Recursive Node**: Fixed `isCopyAllowed` not being passed to child nodes
|
|
564
|
+
- Caused Ctrl+drag to fail on non-root nodes since they defaulted to `isCopyAllowed=false`
|
|
565
|
+
- Added `{isCopyAllowed}` to recursive Node component call
|
|
566
|
+
|
|
567
|
+
### Changed
|
|
568
|
+
- **BREAKING: onNodeDrop Signature**: Callback signature updated to include drop position
|
|
569
|
+
- Before: `onNodeDrop?: (dropNode, draggedNode, event) => void`
|
|
570
|
+
- After: `onNodeDrop?: (dropNode, draggedNode, position, event) => void`
|
|
571
|
+
- `position` is `'above'`, `'below'`, or `'child'` indicating where item should be placed
|
|
572
|
+
- `dropNode` can be `null` when dropping into empty tree or root drop zone
|
|
573
|
+
|
|
574
|
+
## [4.4.0] - 2025-10-02
|
|
575
|
+
|
|
576
|
+
### Added
|
|
577
|
+
- **External Update Method**: New `update()` method for programmatic prop updates from vanilla JavaScript
|
|
578
|
+
- Allows external code to update component props without Svelte reactivity
|
|
579
|
+
- Accepts partial object with any Tree props (excluding snippets/templates)
|
|
580
|
+
- Useful for HTML/JavaScript integration and dynamic configuration
|
|
581
|
+
- Example: `tree.update({ searchText: 'query', expandLevel: 3, data: newData })`
|
|
582
|
+
|
|
583
|
+
### Fixed
|
|
584
|
+
- **Search Functionality**: Fixed search filtering in context-menu dev page
|
|
585
|
+
- Added missing `searchValueMember="name"` prop to enable proper search indexing
|
|
586
|
+
- Search now correctly filters nodes by name instead of filtering everything out
|
|
587
|
+
|
|
588
|
+
### Changed
|
|
589
|
+
- **Code Cleanup**: Renamed internal "trie" references to "tree" for consistency
|
|
590
|
+
- Updated variable names in Tree.svelte, Node.svelte, and ltree-demo.ts
|
|
591
|
+
- Removed "trie" from package.json keywords
|
|
592
|
+
- Improved code readability and naming consistency throughout codebase
|
|
593
|
+
|
|
594
|
+
## [4.3.1] - 2025-09-25
|
|
595
|
+
|
|
596
|
+
### Enhanced
|
|
597
|
+
- **Async Callback Support**: Context menu callbacks now fully support async operations
|
|
598
|
+
- Updated `callback: () => void | Promise<void>` signature in `ContextMenuItem` interface
|
|
599
|
+
- Added automatic error handling for async callbacks with try/catch wrapper
|
|
600
|
+
- Menu item clicks properly await async operations before completing
|
|
601
|
+
- Errors in async callbacks are logged to console for debugging
|
|
602
|
+
- **Robust Error Handling**: Async callback failures don't break menu functionality
|
|
603
|
+
- Failed async operations are caught and logged automatically
|
|
604
|
+
- Developers can implement custom error handling within their callbacks
|
|
605
|
+
- Menu stays open on errors, allowing users to retry actions
|
|
606
|
+
- **Enhanced Dev Examples**: Added comprehensive async callback demonstrations
|
|
607
|
+
- Copy action with simulated network delay
|
|
608
|
+
- New folder creation with error simulation (20% failure rate)
|
|
609
|
+
- Database backup with long-running operation simulation
|
|
610
|
+
- Shows patterns for success/failure handling and conditional menu closing
|
|
611
|
+
|
|
612
|
+
### Documentation
|
|
613
|
+
- **Async Patterns**: Examples showing proper async callback implementation
|
|
614
|
+
- **Error Handling**: Best practices for managing async operation failures
|
|
615
|
+
- **Menu Control**: Demonstrated conditional closing based on operation success/failure
|
|
616
|
+
|
|
617
|
+
## [4.3.0] - 2025-09-25
|
|
618
|
+
|
|
619
|
+
### Added
|
|
620
|
+
- **Enhanced Context Menu Control**: Context menu callback now receives `closeMenuCallback` parameter for programmatic menu control
|
|
621
|
+
- `contextMenuCallback?: (node: LTreeNode<T>, closeMenuCallback: () => void) => ContextMenuItem[]`
|
|
622
|
+
- Developers can now control when/if context menu closes after menu item actions
|
|
623
|
+
- Enables conditional closing patterns (e.g., don't close on cancel, only on success)
|
|
624
|
+
- Public `closeContextMenu()` method exported for external control
|
|
625
|
+
- **Context Menu Item Styling**: New `className?: string` property in `ContextMenuItem` interface
|
|
626
|
+
- Apply custom CSS classes to individual menu items for styling
|
|
627
|
+
- Supports multiple classes (space-separated strings)
|
|
628
|
+
- Example: `className: 'text-danger fw-bold'` for destructive actions
|
|
629
|
+
- **Enhanced Dev Examples**: Updated context menu examples to demonstrate new features
|
|
630
|
+
- Conditional menu closing patterns for different action types
|
|
631
|
+
- CSS class styling demonstrations with Bootstrap classes
|
|
632
|
+
- Improved UX patterns showing when to close vs keep menu open
|
|
633
|
+
|
|
634
|
+
### Enhanced
|
|
635
|
+
- **Flexible Menu Behavior**: Context menu now supports various interaction patterns
|
|
636
|
+
- Immediate close after action completion
|
|
637
|
+
- Conditional close based on user confirmation
|
|
638
|
+
- Persistent menu for multi-step operations
|
|
639
|
+
- Custom styling per menu item type
|
|
640
|
+
|
|
641
|
+
## [4.2.1] - 2025-09-24
|
|
642
|
+
|
|
643
|
+
### Enhanced
|
|
644
|
+
- **Debug Context Menu Positioning**: Improved debug context menu to position relative to tree element instead of viewport
|
|
645
|
+
- Debug menu now appears 200px right and 100px down from each tree's top-left corner
|
|
646
|
+
- Supports multiple trees on same page with individual positioning
|
|
647
|
+
- Enhanced debug logging to include tree ID and calculated position coordinates
|
|
648
|
+
- Better for CSS development when tree is not at top-left of viewport
|
|
649
|
+
- **Debug Context Menu Robustness**: Enhanced debug mode to work with single-node trees
|
|
650
|
+
- Uses second node when available, falls back to first node for single-node trees
|
|
651
|
+
- More flexible node selection for debug menu display
|
|
652
|
+
- Improved reliability for development scenarios
|
|
653
|
+
|
|
654
|
+
### Fixed
|
|
655
|
+
- **Debug Mode State Management**: Fixed context menu interference between debug mode and normal right-click menus
|
|
656
|
+
- Added `isDebugMenuActive` state tracking to prevent debug logic from hiding user-triggered menus
|
|
657
|
+
- Normal right-click context menus now work properly when debug mode is disabled
|
|
658
|
+
- Proper cleanup of debug state when switching between modes
|
|
659
|
+
- **Debug Mode Requirements**: Relaxed debug context menu requirements to support edge cases
|
|
660
|
+
- Changed minimum tree length requirement from `> 1` to `> 0` for better compatibility
|
|
661
|
+
- Debug mode now works with any non-empty tree structure
|
|
662
|
+
|
|
663
|
+
## [4.2.0] - 2025-09-24
|
|
664
|
+
|
|
665
|
+
### Added
|
|
666
|
+
- **Context Menu System**: Comprehensive context menu functionality with two implementation approaches
|
|
667
|
+
- **Callback-based Context Menus**: New `contextMenuCallback` prop that accepts a function `(node: LTreeNode<T>) => ContextMenuItem[]`
|
|
668
|
+
- **ContextMenuItem Interface**: New interface with `icon`, `title`, `isDisabled`, `callback`, and `isDivider` properties
|
|
669
|
+
- **Position Offset Configuration**: New `contextMenuXOffset` (default: 8px) and `contextMenuYOffset` (default: 0px) props for cursor clearance
|
|
670
|
+
- **Debug Mode**: New `shouldDisplayContextMenuInDebugMode` prop for persistent context menu display at fixed position (200px, 100px)
|
|
671
|
+
- **Snippet-based Support**: Maintains backward compatibility with existing `{#snippet contextMenu(node, closeMenu)}` approach
|
|
672
|
+
- **Enhanced Context Menu UX**:
|
|
673
|
+
- Auto-close on scroll events (mouse wheel, scrollbar, touch, programmatic)
|
|
674
|
+
- Auto-close on outside clicks
|
|
675
|
+
- Support for disabled menu items with visual feedback
|
|
676
|
+
- Support for menu dividers for visual organization
|
|
677
|
+
- Rich icon support for menu items
|
|
678
|
+
- **Development Tools**: New `/dev/context-menu` page with comprehensive examples
|
|
679
|
+
- Basic file system context menu example with conditional actions
|
|
680
|
+
- Advanced server management example with status-based and type-specific menus
|
|
681
|
+
- Real-time offset configuration testing
|
|
682
|
+
- Interactive demonstration of all context menu features
|
|
683
|
+
- Debug context menu mode with `shouldDisplayContextMenuInDebugMode` for easy styling development
|
|
684
|
+
- Navigation link added to main layout for easy access
|
|
685
|
+
|
|
686
|
+
### Enhanced
|
|
687
|
+
- **CSS Styling**: Added comprehensive context menu styles in `main.scss`
|
|
688
|
+
- `.ltree-context-menu`, `.ltree-context-menu-item`, `.ltree-context-menu-icon`, `.ltree-context-menu-divider` classes
|
|
689
|
+
- Support for disabled states with `.ltree-context-menu-item-disabled`
|
|
690
|
+
- Flexible layout with proper hover effects and visual hierarchy
|
|
691
|
+
- **Type Safety**: Full TypeScript support for all context menu features
|
|
692
|
+
- **Documentation**: Comprehensive README and CLAUDE.md updates covering both implementation approaches
|
|
693
|
+
|
|
694
|
+
### Fixed
|
|
695
|
+
- **Context Menu Scroll Behavior**: Fixed issue where context menu remained visible when scrolling
|
|
696
|
+
- Added scroll event listeners with capture phase to catch all scroll events
|
|
697
|
+
- Added wheel event listeners for mouse wheel scrolling
|
|
698
|
+
- Context menu now properly closes on any scroll interaction
|
|
699
|
+
|
|
700
|
+
## [4.1.1] - 2025-09-23
|
|
701
|
+
|
|
702
|
+
### Fixed
|
|
703
|
+
- **TreePathSeparator Default Value**: Fixed `treePathSeparator` parameter to properly default to '.' when not provided to Tree.svelte
|
|
704
|
+
- Previously, when `treePathSeparator` was undefined, the reactive effect would override the ltree's internal default
|
|
705
|
+
- Now defaults to '.' in the parameter destructuring, ensuring consistent behavior
|
|
706
|
+
|
|
707
|
+
## [4.1.0] - 2025-09-23
|
|
708
|
+
|
|
709
|
+
### Fixed
|
|
710
|
+
- **Critical Sorting Bug**: Fixed default sort method to sort by level first, ensuring proper hierarchical tree construction
|
|
711
|
+
- Previously sorted by parent path first, causing level 3 nodes to be inserted before level 2 nodes
|
|
712
|
+
- Now sorts by level (depth) first, then parent path, then display value
|
|
713
|
+
- Eliminates "Could not find parent node" errors when nodes are inserted out of level order
|
|
714
|
+
- **Progressive Rendering**: Fixed progressive rendering feature to work correctly with proper level-based sorting
|
|
715
|
+
- Progressive rendering now displays levels 1-2 immediately while deeper levels continue processing
|
|
716
|
+
- Improves perceived performance for large datasets by showing initial tree structure quickly
|
|
717
|
+
- **TreePathSeparator Reactivity**: Fixed Tree component to properly update internal separator when `treePathSeparator` prop changes
|
|
718
|
+
- Added reactive effect to update ltree's separator property when prop changes
|
|
719
|
+
- Prevents race conditions where data is processed with wrong separator
|
|
720
|
+
- Fixes filesystem demo and other custom separator use cases
|
|
721
|
+
- **Sort Functions in Examples**: Updated all demo sort functions to calculate level from path depth during sorting
|
|
722
|
+
- Home page, dev page, and filesystem examples now use path-based level calculation
|
|
723
|
+
- Ensures consistent level-first sorting across all demos and examples
|
|
724
|
+
- Prevents insertion failures in example applications
|
|
725
|
+
|
|
726
|
+
### Enhanced
|
|
727
|
+
- **Test Coverage**: Added comprehensive test suite for sorting functionality
|
|
728
|
+
- Tests verify level-first sorting behavior with various hierarchical data structures
|
|
729
|
+
- Validates progressive rendering scenarios and sort correctness
|
|
730
|
+
- Uses Vitest framework for fast, reliable testing
|
|
731
|
+
|
|
732
|
+
### Changed
|
|
733
|
+
- **Default Sort Algorithm**: Updated `_defaultSort` method to prioritize level over parent path for hierarchical correctness
|
|
734
|
+
- **Example Sort Functions**: All demo applications now use level-first sorting for consistent behavior
|
|
735
|
+
|
|
736
|
+
## [4.0.1] - 2025-01-23
|
|
737
|
+
|
|
738
|
+
### Fixed
|
|
739
|
+
- **treePathSeparator Propagation**: Fixed helper functions (`getParentPath`, `getRelativePath`, `getPathSegments`) to properly use the configured `treePathSeparator` instead of hardcoded "." separator
|
|
740
|
+
- All path manipulation functions now respect the custom separator setting
|
|
741
|
+
- Ensures consistent path handling throughout the tree operations when using custom separators like "/"
|
|
742
|
+
- Fixed `getRelativePath` to use `pathSeparator.length` instead of assuming single character
|
|
743
|
+
- Fixed `getLevel` to properly count segments with multi-character separators
|
|
744
|
+
|
|
745
|
+
### Added
|
|
746
|
+
- **Test Suite**: Added comprehensive test coverage for ltree helper functions
|
|
747
|
+
- 24 test cases covering single-character, multi-character, and edge case separators
|
|
748
|
+
- Vitest testing framework integration with `npm run test` and `make test` commands
|
|
749
|
+
- Tests validate proper handling of separators like `"::"`, `"->>"`, `"<|>"` and complex edge cases
|
|
750
|
+
|
|
751
|
+
## [4.0.0] - 2025-01-09
|
|
752
|
+
|
|
753
|
+
### Added
|
|
754
|
+
- **Complete Showcase Site Redesign**: Comprehensive overhaul of the documentation and demo site
|
|
755
|
+
- **API Reference Page**: Complete tabbed reference with properties, methods, events, and templates tables
|
|
756
|
+
- **Professional Navigation**: Fixed-top navbar with burger menu, GitHub link, and responsive sidebar
|
|
757
|
+
- **Ocean Color Scheme**: Beautiful blue-themed design using Coolors.co palette (#00171F, #003459, #007EA7, #00A7E1, #FFFFFF)
|
|
758
|
+
- **Responsive Layout**: Mobile-first design with collapsible sidebar and backdrop overlay
|
|
759
|
+
- **Enhanced Examples**: Four comprehensive code examples with descriptions in tabbed interface
|
|
760
|
+
- **Docker Production Setup**: Complete containerization for static site deployment
|
|
761
|
+
- **Multi-stage Dockerfile**: Optimized build with Node.js builder and nginx production stage
|
|
762
|
+
- **Static Site Generation**: SvelteKit configuration for pre-rendered HTML pages
|
|
763
|
+
- **Make Commands**: Docker build, run, and management commands with custom registry support
|
|
764
|
+
- **Nginx Configuration**: Optimized serving with gzip, caching, and SPA routing support
|
|
765
|
+
|
|
766
|
+
### Changed
|
|
767
|
+
- **Layout Architecture**: Moved from nested Bootstrap containers to clean, consistent structure
|
|
768
|
+
- **Fixed Navigation**: Top navbar with brand, burger menu, and GitHub link
|
|
769
|
+
- **Sidebar Design**: Fixed-width (280px) sidebar with consistent icon spacing
|
|
770
|
+
- **Footer Integration**: Professional footer with KeenMate branding
|
|
771
|
+
- **SvelteKit Configuration**: Updated for optimal static generation
|
|
772
|
+
- **Static Adapter**: Switched from adapter-auto to adapter-static for reliable builds
|
|
773
|
+
- **Prerendering**: Enabled SSR and prerender for all showcase pages
|
|
774
|
+
- **Build Output**: Optimized for nginx serving with proper fallback handling
|
|
775
|
+
- **Page Structure Consistency**: Standardized header structure across all showcase pages
|
|
776
|
+
- **Removed Redundant Containers**: Eliminated nested container-fluid wrappers
|
|
777
|
+
- **Clean Headers**: Direct h1 and description elements without Bootstrap grid overhead
|
|
778
|
+
|
|
779
|
+
### Enhanced
|
|
780
|
+
- **Visual Design**: Professional styling throughout the showcase site
|
|
781
|
+
- **Fixed Icon Alignment**: Consistent 1.5rem width for sidebar navigation icons
|
|
782
|
+
- **Gradient Backgrounds**: Sophisticated color gradients across navbar, sidebar, and footer
|
|
783
|
+
- **Interactive Elements**: Hover effects, focus states, and smooth transitions
|
|
784
|
+
- **Typography**: Clear hierarchy with proper contrast and accessibility
|
|
785
|
+
- **User Experience**: Improved navigation and usability
|
|
786
|
+
- **Always-Visible Burger Menu**: Toggle sidebar on any screen size for flexible layout
|
|
787
|
+
- **Responsive Behavior**: Automatic sidebar hiding on mobile with backdrop close
|
|
788
|
+
- **Tab Navigation**: Full-width code examples with clean tab interface
|
|
789
|
+
- **Mobile Optimization**: Touch-friendly interactions and responsive text sizing
|
|
790
|
+
|
|
791
|
+
### Fixed
|
|
792
|
+
- **Container Structure**: Resolved double-container issues causing layout inconsistencies
|
|
793
|
+
- **Sidebar Toggle**: Fixed burger menu functionality to work across all screen sizes
|
|
794
|
+
- **Static Generation**: Proper SvelteKit configuration for nginx-compatible static builds
|
|
795
|
+
- **Icon Spacing**: Consistent navigation icon width preventing text misalignment
|
|
796
|
+
|
|
797
|
+
### Documentation
|
|
798
|
+
- **API Reference**: Complete tables for all component properties, methods, events, and templates
|
|
799
|
+
- **Usage Examples**: Real-world code examples including organization tree configuration
|
|
800
|
+
- **Docker Documentation**: Make commands and containerization setup
|
|
801
|
+
- **Responsive Design**: Mobile-first approach with professional styling
|
|
802
|
+
|
|
803
|
+
## [4.0.0-rc.08] - 2025-01-08
|
|
804
|
+
|
|
805
|
+
### Added
|
|
806
|
+
- **searchNodes() Method**: New public method `searchNodes(searchText)` that returns an array of matching nodes without filtering the tree display
|
|
807
|
+
- Programmatically search nodes using the internal search index
|
|
808
|
+
- Returns `LTreeNode<T>[]` array of matching nodes
|
|
809
|
+
- Useful for building custom search interfaces, suggestions, and result summaries
|
|
810
|
+
- **Configurable Path Separators**: New `treePathSeparator` property allows custom hierarchical path separators
|
|
811
|
+
- Default remains `"."` for backward compatibility (e.g., "1.2.3")
|
|
812
|
+
- Support for custom separators like `"/"` for file system style paths (e.g., "1/src/components")
|
|
813
|
+
- All path operations throughout the component respect the custom separator
|
|
814
|
+
- **Data Structure Showcase Page**: New comprehensive `/data-structure` showcase page with four detailed sections:
|
|
815
|
+
- **LTree Path Structure**: Understanding path-based hierarchical data model
|
|
816
|
+
- **Optimized Data Structure**: Precomputed values for better performance
|
|
817
|
+
- **Custom Path Separators**: Live demo with file system style paths using "/" separator
|
|
818
|
+
- **External Search & Data Management**: Managing search outside the tree component
|
|
819
|
+
- **Invalid Data Structures**: Common mistakes and unsupported patterns
|
|
820
|
+
- **Enhanced Search Showcase**: Added new `searchNodes()` method demonstration section to `/search` page
|
|
821
|
+
- Interactive search interface showing difference between `searchNodes()` and `filterNodes()`
|
|
822
|
+
- Live examples with result display and usage patterns
|
|
823
|
+
- **Insert Result Information**: New `insertResult` bindable property provides detailed information about data insertion
|
|
824
|
+
- `InsertArrayResult<T>` interface with successful count and failed nodes array
|
|
825
|
+
- Each failed node includes original data, processed node, and error message
|
|
826
|
+
- Useful for data validation, debugging, and handling incomplete datasets
|
|
827
|
+
- **Drag-over Visual Feedback**: New `dragOverNodeClass` property for highlighting nodes during drag operations
|
|
828
|
+
- Two built-in classes: `ltree-dragover-highlight` (dashed border) and `ltree-dragover-glow` (shadow effect)
|
|
829
|
+
- Automatic state management with proper drag event handling
|
|
830
|
+
- Provides clear visual feedback for drop targets during drag-and-drop operations
|
|
831
|
+
|
|
832
|
+
### Changed
|
|
833
|
+
- **Documentation Updates**: Updated README.md, CLAUDE.md, and showcase pages with new features
|
|
834
|
+
- Added `searchNodes` to public methods documentation
|
|
835
|
+
- Added `treePathSeparator` to Tree Configuration properties table
|
|
836
|
+
- Updated architecture description to reflect configurable separators
|
|
837
|
+
- Fixed path requirements documentation to clarify separator flexibility
|
|
838
|
+
- **Navigation Enhancement**: Added "Data Structure" page to sidebar navigation with 🗂️ icon
|
|
839
|
+
|
|
840
|
+
### Enhanced
|
|
841
|
+
- **Type System**: Updated `Ltree<T>` interface to include `searchNodes` method signature
|
|
842
|
+
- **Internal Architecture**: Enhanced `createLTree` function to accept configurable `treePathSeparator` parameter
|
|
843
|
+
- **Component Integration**: Updated `Tree.svelte` component to pass through `treePathSeparator` property
|
|
844
|
+
|
|
845
|
+
### Fixed
|
|
846
|
+
- **Node Indentation**: Fixed `Node.svelte` indent style to use consistent per-level indentation instead of cumulative indentation
|
|
847
|
+
- Previously: Each level had exponentially increasing indent (level * indent-per-level)
|
|
848
|
+
- Now: Each level uses fixed CSS variable `--tree-node-indent-per-level` allowing proper CSS-based indentation control
|
|
849
|
+
- **Search Index Accuracy**: Fixed `insertArray` to only add successfully inserted nodes to `flatTreeNodes` array
|
|
850
|
+
- Prevents search index from returning incorrect node indices for nodes that failed to insert
|
|
851
|
+
- Failed nodes are no longer included in search operations, ensuring search results match visible tree structure
|
|
852
|
+
- **Error Message Clarity**: Improved `insertTreeNode` error messages to include the failing node's path
|
|
853
|
+
- Error format: `"Node: {path} - Could not find parent node: {parentPath}"`
|
|
854
|
+
- Makes debugging hierarchical data issues much clearer
|
|
855
|
+
|
|
856
|
+
### Documentation
|
|
857
|
+
- **Comprehensive Examples**: Added working code examples for both basic and advanced use cases
|
|
858
|
+
- **Path Separator Flexibility**: Clarified that paths don't need to be dot-separated, can use any consistent separator
|
|
859
|
+
- **External Data Management**: Detailed examples of filtering data outside the tree component
|
|
860
|
+
- **Performance Optimization**: Guidelines for when to use precomputed values vs automatic calculations
|
|
861
|
+
|
|
862
|
+
## [4.0.0-rc.07] - 2025-01-06
|
|
863
|
+
|
|
864
|
+
### Added
|
|
865
|
+
- **Customizable Scroll Highlight**: New `scrollHighlightClass` property allows users to define custom CSS classes for scroll highlight effects
|
|
866
|
+
- **Built-in Highlight Options**: Added pre-built highlight classes:
|
|
867
|
+
- `ltree-scroll-highlight` - Background glow with blue color (default)
|
|
868
|
+
- `ltree-scroll-highlight-arrow` - Red arrow indicator positioned to the right of the node
|
|
869
|
+
- **Scroll Highlight Timeout Control**: New `scrollHighlightTimeout` property (default: 4000ms) controls duration of highlight effect
|
|
870
|
+
- **Enhanced scrollToPath Method**: Improved scroll highlighting with proper element targeting and CSS class management
|
|
871
|
+
- **Debug Logging Control for Indexer**: Added `shouldDisplayDebugInformation` property to Indexer class for consistent debug logging control
|
|
872
|
+
|
|
873
|
+
### Changed
|
|
874
|
+
- **Removed CSS Animation Dependencies**: Scroll highlighting now uses pure CSS classes instead of CSS animations for better timeout control
|
|
875
|
+
- **Improved Element Targeting**: `scrollToPath` now targets `.ltree-node-content` specifically for more precise highlighting
|
|
876
|
+
- **Enhanced Documentation**: Updated README with comprehensive examples for highlight customization
|
|
877
|
+
- **Consistent Debug Logging**: All indexer console.log messages now respect the `shouldDisplayDebugInformation` flag for unified logging control
|
|
878
|
+
|
|
879
|
+
### Fixed
|
|
880
|
+
- **Scroll Highlight Duration**: Fixed issue where CSS animations overrode JavaScript timeout values
|
|
881
|
+
- **Element Selection**: Improved DOM element selection for scroll highlighting functionality
|
|
882
|
+
- **LTree Path Traversal**: Fixed `expandNodes` and `collapseNodes` methods by correctly prefixing path segments with 'x' prefix to match internal tree structure storage
|
|
883
|
+
|
|
884
|
+
## [4.0.0-rc.05] - 2025-09-05
|
|
885
|
+
|
|
886
|
+
### Added
|
|
887
|
+
- **Optimized Async Search Indexing**: Improved indexing implementation that processes entire queue at once during idle time instead of small batches
|
|
888
|
+
- **Enhanced Indexing Performance**: Increased batch size from 100 to 1000 nodes and streamlined queue processing
|
|
889
|
+
- **Better Debug Logging**: Added conditional debug logging for indexing operations when `shouldDisplayDebugInformation` is enabled
|
|
890
|
+
|
|
891
|
+
### Changed
|
|
892
|
+
- **Indexing Architecture**: Refactored async indexing to process all queued nodes in a single idle callback rather than batched processing
|
|
893
|
+
- **Queue Management**: Simplified indexing queue processing with more efficient completion handling
|
|
894
|
+
- **TypeScript Support**: Added `Tuple<T, U>` type import for enhanced type safety
|
|
895
|
+
|
|
896
|
+
### Performance
|
|
897
|
+
- **Faster Indexing**: Single-pass indexing of entire queue reduces overhead and callback scheduling
|
|
898
|
+
- **Reduced Idle Callbacks**: Less frequent but more efficient use of `requestIdleCallback`
|
|
899
|
+
- **Improved Memory Usage**: More efficient queue management with immediate processing
|
|
900
|
+
|
|
901
|
+
## [4.0.0] - 2025-09-01
|
|
902
|
+
|
|
903
|
+
### Added
|
|
904
|
+
- **Asynchronous Search Indexing**: Search indexing now uses `requestIdleCallback` for non-blocking performance
|
|
905
|
+
- **Statistics Tracking**: New `statistics` getter provides real-time data:
|
|
906
|
+
- `nodeCount`: Total number of nodes in the tree
|
|
907
|
+
- `maxLevel`: Maximum depth level of the tree
|
|
908
|
+
- `filteredNodeCount`: Number of nodes currently visible when filtering
|
|
909
|
+
- `isIndexing`: Boolean indicating if search indexing is in progress
|
|
910
|
+
- `pendingIndexCount`: Number of nodes pending indexing
|
|
911
|
+
- **Expand Level Control**: New `expandLevel` property (default: 2) automatically expands nodes up to specified depth
|
|
912
|
+
- **Drag & Drop Properties**: Added `isDraggableMember` and `isDropAllowedMember` for fine-grained drag & drop control
|
|
913
|
+
- **Debug Information Panel**: Enhanced debug display with collapsible interface showing tree statistics and indexing progress
|
|
914
|
+
|
|
915
|
+
### Changed
|
|
916
|
+
- **Breaking**: Renamed internal references from "Trie" to "LTree" for consistency
|
|
917
|
+
- **Breaking**: `trieId` property renamed to `treeId`
|
|
918
|
+
- **Search Performance**: Tree now renders immediately while search indexing happens asynchronously
|
|
919
|
+
- **Debug Styling**: Updated debug panel styling to use `em` units with reduced padding
|
|
920
|
+
|
|
921
|
+
### Performance
|
|
922
|
+
- **Non-blocking UI**: Tree renders immediately while search indexing occurs during browser idle time
|
|
923
|
+
- **Improved Large Dataset Handling**: Async indexing prevents UI freezing with large data sets
|
|
924
|
+
- **Batch Processing**: Search indexing processes nodes in batches during idle periods
|
|
925
|
+
- **Graceful Degradation**: Falls back to `setTimeout` on browsers without `requestIdleCallback` support
|
|
926
|
+
|
|
927
|
+
### Documentation
|
|
928
|
+
- Added comprehensive documentation for async search indexing
|
|
929
|
+
- Added warning about search indexing requirements
|
|
930
|
+
- Enhanced API documentation with new properties and statistics
|
|
931
|
+
- Updated performance section highlighting async capabilities
|