@keenmate/svelte-treeview 4.0.0-rc07 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +144 -1
- package/dist/components/Node.svelte +26 -7
- package/dist/components/Node.svelte.d.ts +1 -0
- package/dist/components/Tree.svelte +31 -32
- package/dist/components/Tree.svelte.d.ts +11 -3
- package/dist/ltree/ltree-demo.js +1 -1
- package/dist/ltree/ltree.svelte.d.ts +1 -1
- package/dist/ltree/ltree.svelte.js +47 -11
- package/dist/ltree/types.d.ts +13 -2
- package/dist/styles/main.scss +42 -1
- package/dist/styles.css +35 -1
- package/dist/styles.css.map +1 -1
- package/package.json +6 -4
package/README.md
CHANGED
|
@@ -142,6 +142,71 @@ import '@keenmate/svelte-treeview/styles.scss';
|
|
|
142
142
|
/>
|
|
143
143
|
```
|
|
144
144
|
|
|
145
|
+
### With Advanced Search Options
|
|
146
|
+
|
|
147
|
+
```svelte
|
|
148
|
+
<script lang="ts">
|
|
149
|
+
import { Tree } from '@keenmate/svelte-treeview';
|
|
150
|
+
import type { SearchOptions } from 'flexsearch';
|
|
151
|
+
|
|
152
|
+
let treeRef;
|
|
153
|
+
const data = [/* your data */];
|
|
154
|
+
|
|
155
|
+
// Programmatic search with FlexSearch options
|
|
156
|
+
function performAdvancedSearch(searchTerm: string) {
|
|
157
|
+
const searchOptions: SearchOptions = {
|
|
158
|
+
suggest: true, // Enable suggestions for typos
|
|
159
|
+
limit: 10, // Limit results to 10 items
|
|
160
|
+
bool: "and" // Use AND logic for multiple terms
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
const results = treeRef.searchNodes(searchTerm, searchOptions);
|
|
164
|
+
console.log('Advanced search results:', results);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// Programmatic filtering with options
|
|
168
|
+
function filterWithOptions(searchTerm: string) {
|
|
169
|
+
const searchOptions: SearchOptions = {
|
|
170
|
+
threshold: 0.8, // Similarity threshold
|
|
171
|
+
depth: 2 // Search depth
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
treeRef.filterNodes(searchTerm, searchOptions);
|
|
175
|
+
}
|
|
176
|
+
</script>
|
|
177
|
+
|
|
178
|
+
<Tree
|
|
179
|
+
bind:this={treeRef}
|
|
180
|
+
{data}
|
|
181
|
+
idMember="path"
|
|
182
|
+
pathMember="path"
|
|
183
|
+
shouldUseInternalSearchIndex={true}
|
|
184
|
+
searchValueMember="name"
|
|
185
|
+
/>
|
|
186
|
+
|
|
187
|
+
<button onclick={() => performAdvancedSearch('document')}>
|
|
188
|
+
Advanced Search
|
|
189
|
+
</button>
|
|
190
|
+
<button onclick={() => filterWithOptions('project')}>
|
|
191
|
+
Filter with Options
|
|
192
|
+
</button>
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
#### FlexSearch Options Reference
|
|
196
|
+
|
|
197
|
+
The `searchOptions` parameter accepts any options supported by FlexSearch. Common options include:
|
|
198
|
+
|
|
199
|
+
| Option | Type | Description | Example |
|
|
200
|
+
|--------|------|-------------|---------|
|
|
201
|
+
| `suggest` | `boolean` | Enable suggestions for typos | `{ suggest: true }` |
|
|
202
|
+
| `limit` | `number` | Maximum number of results | `{ limit: 10 }` |
|
|
203
|
+
| `threshold` | `number` | Similarity threshold (0-1) | `{ threshold: 0.8 }` |
|
|
204
|
+
| `depth` | `number` | Search depth for nested content | `{ depth: 2 }` |
|
|
205
|
+
| `bool` | `string` | Boolean logic: "and", "or" | `{ bool: "and" }` |
|
|
206
|
+
| `where` | `object` | Filter by field values | `{ where: { type: "folder" } }` |
|
|
207
|
+
|
|
208
|
+
For complete FlexSearch documentation, visit: [FlexSearch Options](https://github.com/nextapps-de/flexsearch#options)
|
|
209
|
+
|
|
145
210
|
### With Drag & Drop
|
|
146
211
|
|
|
147
212
|
```svelte
|
|
@@ -183,6 +248,7 @@ import '@keenmate/svelte-treeview/styles.scss';
|
|
|
183
248
|
data={targetData}
|
|
184
249
|
idMember="path"
|
|
185
250
|
pathMember="path"
|
|
251
|
+
dragOverNodeClass="ltree-dragover-highlight"
|
|
186
252
|
onNodeDrop={onDrop}
|
|
187
253
|
/>
|
|
188
254
|
</div>
|
|
@@ -199,7 +265,7 @@ The component uses CSS custom properties for easy theming:
|
|
|
199
265
|
|
|
200
266
|
```css
|
|
201
267
|
:root {
|
|
202
|
-
--tree-node-indent-per-level: 0.5rem;
|
|
268
|
+
--tree-node-indent-per-level: 0.5rem; /* Controls indentation for each hierarchy level */
|
|
203
269
|
--ltree-primary: #0d6efd;
|
|
204
270
|
--ltree-primary-rgb: 13, 110, 253;
|
|
205
271
|
--ltree-success: #198754;
|
|
@@ -212,6 +278,8 @@ The component uses CSS custom properties for easy theming:
|
|
|
212
278
|
}
|
|
213
279
|
```
|
|
214
280
|
|
|
281
|
+
**Note**: The `--tree-node-indent-per-level` variable controls the consistent indentation applied at each hierarchy level. Each nested level receives this fixed indent amount, creating proper visual hierarchy without exponential indentation growth.
|
|
282
|
+
|
|
215
283
|
### SCSS Variables (if using SCSS)
|
|
216
284
|
|
|
217
285
|
If you're building the styles from SCSS source, you can override these variables:
|
|
@@ -232,6 +300,7 @@ $primary-color: #custom-color;
|
|
|
232
300
|
- `.ltree-node-content` - Node content area
|
|
233
301
|
- `.ltree-toggle-icon` - Expand/collapse icons
|
|
234
302
|
- `.ltree-selected-*` - Selected node styles
|
|
303
|
+
- `.ltree-dragover-*` - Drag-over node styles
|
|
235
304
|
- `.ltree-draggable` - Draggable nodes
|
|
236
305
|
- `.ltree-context-menu` - Context menu styling
|
|
237
306
|
- `.ltree-drag-over` - Applied during drag operations
|
|
@@ -258,6 +327,13 @@ The component includes several pre-built classes for styling selected nodes:
|
|
|
258
327
|
| `ltree-selected-border` | Border and background highlight | Solid border with light background |
|
|
259
328
|
| `ltree-selected-brackets` | Decorative brackets around text | ❯ **Node Text** ❮ |
|
|
260
329
|
|
|
330
|
+
**Available Drag-over Node Classes:**
|
|
331
|
+
|
|
332
|
+
| Class | Description | Visual Effect |
|
|
333
|
+
|-------|-------------|---------------|
|
|
334
|
+
| `ltree-dragover-highlight` | Dashed border with success color background | Green dashed border with subtle background |
|
|
335
|
+
| `ltree-dragover-glow` | Blue glow effect | Glowing shadow effect with primary color theme |
|
|
336
|
+
|
|
261
337
|
### Custom Icon Classes
|
|
262
338
|
|
|
263
339
|
```svelte
|
|
@@ -323,7 +399,9 @@ Without both requirements, no search indexing will occur.
|
|
|
323
399
|
| Prop | Type | Default | Description |
|
|
324
400
|
|------|------|---------|-------------|
|
|
325
401
|
| `treeId` | `string \| null` | auto-generated | Unique identifier for the tree |
|
|
402
|
+
| `treePathSeparator` | `string \| null` | `"."` | Separator character for hierarchical paths (e.g., "." for "1.2.3" or "/" for "1/2/3") |
|
|
326
403
|
| `selectedNode` | `LTreeNode<T>` (bindable) | `undefined` | Currently selected node |
|
|
404
|
+
| `insertResult` | `InsertArrayResult<T>` (bindable) | `undefined` | Result of the last data insertion including failed nodes |
|
|
327
405
|
|
|
328
406
|
#### Behavior Properties
|
|
329
407
|
| Prop | Type | Default | Description |
|
|
@@ -347,6 +425,7 @@ Without both requirements, no search indexing will occur.
|
|
|
347
425
|
|------|------|---------|-------------|
|
|
348
426
|
| `bodyClass` | `string \| null` | `undefined` | CSS class for tree body |
|
|
349
427
|
| `selectedNodeClass` | `string \| null` | `undefined` | CSS class for selected nodes |
|
|
428
|
+
| `dragOverNodeClass` | `string \| null` | `undefined` | CSS class for nodes being dragged over |
|
|
350
429
|
| `expandIconClass` | `string \| null` | `"ltree-icon-expand"` | CSS class for expand icons |
|
|
351
430
|
| `collapseIconClass` | `string \| null` | `"ltree-icon-collapse"` | CSS class for collapse icons |
|
|
352
431
|
| `leafIconClass` | `string \| null` | `"ltree-icon-leaf"` | CSS class for leaf node icons |
|
|
@@ -370,6 +449,8 @@ Without both requirements, no search indexing will occur.
|
|
|
370
449
|
| `collapseNodes` | `nodePath: string` | Collapse nodes at specified path |
|
|
371
450
|
| `expandAll` | `nodePath?: string` | Expand all nodes or nodes under path |
|
|
372
451
|
| `collapseAll` | `nodePath?: string` | Collapse all nodes or nodes under path |
|
|
452
|
+
| `filterNodes` | `searchText: string, searchOptions?: SearchOptions` | Filter the tree display using internal search index with optional FlexSearch options |
|
|
453
|
+
| `searchNodes` | `searchText: string \| null \| undefined, searchOptions?: SearchOptions` | Search nodes using internal search index and return matching nodes with optional FlexSearch options |
|
|
373
454
|
| `scrollToPath` | `path: string, options?: ScrollToPathOptions` | Scroll to and highlight a specific node |
|
|
374
455
|
|
|
375
456
|
#### ScrollToPath Options
|
|
@@ -541,12 +622,74 @@ interface NodeData {
|
|
|
541
622
|
- Second level: `"1.1"`, `"1.2"`, `"2.1"`
|
|
542
623
|
- Third level: `"1.1.1"`, `"1.2.1"`, `"2.1.1"`
|
|
543
624
|
|
|
625
|
+
### Insert Result Information
|
|
626
|
+
|
|
627
|
+
The tree provides detailed information about data insertion through the `insertResult` bindable property:
|
|
628
|
+
|
|
629
|
+
```typescript
|
|
630
|
+
interface InsertArrayResult<T> {
|
|
631
|
+
successful: number; // Number of nodes successfully inserted
|
|
632
|
+
failed: Array<{ // Nodes that failed to insert
|
|
633
|
+
node: LTreeNode<T>; // The processed tree node
|
|
634
|
+
originalData: T; // The original data object
|
|
635
|
+
error: string; // Error message (usually "Could not find parent...")
|
|
636
|
+
}>;
|
|
637
|
+
total: number; // Total number of nodes processed
|
|
638
|
+
}
|
|
639
|
+
```
|
|
640
|
+
|
|
641
|
+
#### Usage Example
|
|
642
|
+
|
|
643
|
+
```svelte
|
|
644
|
+
<script lang="ts">
|
|
645
|
+
import { Tree } from '@keenmate/svelte-treeview';
|
|
646
|
+
|
|
647
|
+
let insertResult = $state();
|
|
648
|
+
|
|
649
|
+
const data = [
|
|
650
|
+
{ id: '1', path: '1', name: 'Root' },
|
|
651
|
+
{ id: '1.2', path: '1.2', name: 'Child' }, // Missing parent "1.1"
|
|
652
|
+
{ id: '1.1.1', path: '1.1.1', name: 'Deep' } // Missing parent "1.1"
|
|
653
|
+
];
|
|
654
|
+
|
|
655
|
+
// Check results after tree processes data
|
|
656
|
+
$effect(() => {
|
|
657
|
+
if (insertResult) {
|
|
658
|
+
console.log(`✅ ${insertResult.successful} nodes inserted successfully`);
|
|
659
|
+
console.log(`❌ ${insertResult.failed.length} nodes failed to insert`);
|
|
660
|
+
|
|
661
|
+
insertResult.failed.forEach(failure => {
|
|
662
|
+
console.log(`Failed: ${failure.originalData.name} - ${failure.error}`);
|
|
663
|
+
});
|
|
664
|
+
}
|
|
665
|
+
});
|
|
666
|
+
</script>
|
|
667
|
+
|
|
668
|
+
<Tree
|
|
669
|
+
{data}
|
|
670
|
+
idMember="id"
|
|
671
|
+
pathMember="path"
|
|
672
|
+
displayValueMember="name"
|
|
673
|
+
bind:insertResult
|
|
674
|
+
/>
|
|
675
|
+
```
|
|
676
|
+
|
|
677
|
+
#### Benefits
|
|
678
|
+
|
|
679
|
+
- **Data Validation**: Identify missing parent nodes in hierarchical data
|
|
680
|
+
- **Debugging**: Clear error messages with node paths like "Node: 1.1.1 - Could not find parent node: 1.1"
|
|
681
|
+
- **Data Integrity**: Handle incomplete datasets gracefully
|
|
682
|
+
- **Search Accuracy**: Failed nodes are excluded from search index, ensuring search results match visible tree
|
|
683
|
+
- **User Feedback**: Inform users about data issues with detailed failure information
|
|
684
|
+
|
|
544
685
|
## 🚀 Performance
|
|
545
686
|
|
|
546
687
|
The component is optimized for large datasets:
|
|
547
688
|
|
|
548
689
|
- **LTree**: Efficient hierarchical data structure
|
|
549
690
|
- **Async Search Indexing**: Uses `requestIdleCallback` for non-blocking search index building
|
|
691
|
+
- **Accurate Search Results**: Search index only includes successfully inserted nodes, ensuring results match visible tree structure
|
|
692
|
+
- **Consistent Visual Hierarchy**: Optimized CSS-based indentation prevents exponential spacing growth
|
|
550
693
|
- **Virtual Scrolling**: (Coming soon)
|
|
551
694
|
- **Lazy Loading**: (Coming soon)
|
|
552
695
|
- **Search Indexing**: Uses FlexSearch for fast search operations
|
|
@@ -22,13 +22,14 @@
|
|
|
22
22
|
collapseIconClass?: string | null | undefined;
|
|
23
23
|
leafIconClass?: string | null | undefined;
|
|
24
24
|
selectedNodeClass?: string | null | undefined;
|
|
25
|
+
dragOverNodeClass?: string | null | undefined;
|
|
25
26
|
isDraggedNode?: boolean | null | undefined;
|
|
26
27
|
}
|
|
27
28
|
|
|
28
29
|
// Destructure props using Svelte 5 syntax
|
|
29
30
|
let {
|
|
30
31
|
node,
|
|
31
|
-
children,
|
|
32
|
+
children = undefined,
|
|
32
33
|
onNodeClicked,
|
|
33
34
|
onNodeRightClicked,
|
|
34
35
|
onNodeDragStart,
|
|
@@ -43,16 +44,20 @@
|
|
|
43
44
|
collapseIconClass = "ltree-icon-collapse",
|
|
44
45
|
leafIconClass = "ltree-icon-leaf",
|
|
45
46
|
selectedNodeClass,
|
|
47
|
+
dragOverNodeClass,
|
|
46
48
|
isDraggedNode = false,
|
|
47
49
|
}: Props = $props()
|
|
48
50
|
|
|
49
51
|
const trie = getContext<Ltree<T>>("Ltree")
|
|
50
52
|
|
|
53
|
+
// Drag over state
|
|
54
|
+
let isDraggedOver = $state(false);
|
|
55
|
+
|
|
51
56
|
// Convert reactive statements to derived values
|
|
52
57
|
const childrenWithData = $derived(Object.values(node?.children || []))
|
|
53
58
|
const hasChildren = $derived(node?.hasChildren || false)
|
|
54
59
|
const indentStyle = $derived(
|
|
55
|
-
`margin-left:
|
|
60
|
+
`margin-left: var(--tree-node-indent-per-level, 0.5rem)`,
|
|
56
61
|
)
|
|
57
62
|
|
|
58
63
|
function toggleExpanded() {
|
|
@@ -71,9 +76,9 @@
|
|
|
71
76
|
</script>
|
|
72
77
|
|
|
73
78
|
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
74
|
-
<div
|
|
75
|
-
class="ltree-node"
|
|
76
|
-
id="{node.treeId}-{node.id}"
|
|
79
|
+
<div
|
|
80
|
+
class="ltree-node"
|
|
81
|
+
id="{node.treeId}-{node.id}"
|
|
77
82
|
data-tree-path="{node.path}"
|
|
78
83
|
style={indentStyle}
|
|
79
84
|
>
|
|
@@ -96,7 +101,7 @@
|
|
|
96
101
|
<!-- svelte-ignore a11y_click_events_have_key_events -->
|
|
97
102
|
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
98
103
|
<div
|
|
99
|
-
class="ltree-node-content {node.isSelected ? selectedNodeClass : ''}"
|
|
104
|
+
class="ltree-node-content {node.isSelected ? selectedNodeClass : ''} {isDraggedOver && dragOverNodeClass ? dragOverNodeClass : ''}"
|
|
100
105
|
class:ltree-clickable={node.isSelectable}
|
|
101
106
|
class:ltree-dragged={isDraggedNode}
|
|
102
107
|
class:ltree-draggable={node?.isDraggable}
|
|
@@ -125,12 +130,25 @@
|
|
|
125
130
|
}
|
|
126
131
|
}}
|
|
127
132
|
ondragover={(e) => {
|
|
128
|
-
if (e.dataTransfer?.types.includes("application/svelte-treeview"))
|
|
133
|
+
if (e.dataTransfer?.types.includes("application/svelte-treeview")) {
|
|
129
134
|
e.preventDefault();
|
|
135
|
+
isDraggedOver = true;
|
|
136
|
+
}
|
|
130
137
|
onNodeDragOver?.(node, e);
|
|
131
138
|
}}
|
|
139
|
+
ondragleave={(e) => {
|
|
140
|
+
// Only reset if we're actually leaving the node (not entering a child)
|
|
141
|
+
const rect = e.currentTarget.getBoundingClientRect();
|
|
142
|
+
const x = e.clientX;
|
|
143
|
+
const y = e.clientY;
|
|
144
|
+
|
|
145
|
+
if (x < rect.left || x >= rect.right || y < rect.top || y >= rect.bottom) {
|
|
146
|
+
isDraggedOver = false;
|
|
147
|
+
}
|
|
148
|
+
}}
|
|
132
149
|
ondrop={(e) => {
|
|
133
150
|
e.stopPropagation();
|
|
151
|
+
isDraggedOver = false;
|
|
134
152
|
onNodeDrop?.(node, e);
|
|
135
153
|
}}
|
|
136
154
|
>
|
|
@@ -158,6 +176,7 @@
|
|
|
158
176
|
{collapseIconClass}
|
|
159
177
|
{leafIconClass}
|
|
160
178
|
{selectedNodeClass}
|
|
179
|
+
{dragOverNodeClass}
|
|
161
180
|
{isDraggedNode}
|
|
162
181
|
/>
|
|
163
182
|
{/each}
|
|
@@ -15,6 +15,7 @@ declare function $$render<T>(): {
|
|
|
15
15
|
collapseIconClass?: string | null | undefined;
|
|
16
16
|
leafIconClass?: string | null | undefined;
|
|
17
17
|
selectedNodeClass?: string | null | undefined;
|
|
18
|
+
dragOverNodeClass?: string | null | undefined;
|
|
18
19
|
isDraggedNode?: boolean | null | undefined;
|
|
19
20
|
};
|
|
20
21
|
exports: {};
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
<script lang="ts" generics="T">
|
|
2
|
-
import type { Index } from 'flexsearch';
|
|
2
|
+
import type { Index, SearchOptions } from 'flexsearch';
|
|
3
3
|
import Node from './Node.svelte';
|
|
4
4
|
import { type LTreeNode } from '../ltree/ltree-node.svelte.js';
|
|
5
5
|
import { createLTree } from '../ltree/ltree.svelte.js';
|
|
6
|
-
import { type Ltree } from '../ltree/types.js';
|
|
6
|
+
import { type Ltree, type InsertArrayResult } from '../ltree/types.js';
|
|
7
7
|
import { setContext, tick } from 'svelte';
|
|
8
8
|
|
|
9
9
|
// Context menu state
|
|
@@ -37,11 +37,13 @@
|
|
|
37
37
|
getSearchValueCallback?: (node: LTreeNode<T>) => string;
|
|
38
38
|
|
|
39
39
|
treeId?: string | null | undefined;
|
|
40
|
+
treePathSeparator?: string | null | undefined;
|
|
40
41
|
sortCallback?: (items: LTreeNode<T>[]) => LTreeNode<T>[];
|
|
41
42
|
|
|
42
43
|
// DATA
|
|
43
44
|
data: T[];
|
|
44
45
|
selectedNode?: LTreeNode<T> | null | undefined;
|
|
46
|
+
insertResult?: InsertArrayResult<T> | null | undefined;
|
|
45
47
|
|
|
46
48
|
// SLOTS
|
|
47
49
|
nodeTemplate?: any;
|
|
@@ -70,6 +72,7 @@
|
|
|
70
72
|
// VISUALS
|
|
71
73
|
bodyClass?: string | null | undefined;
|
|
72
74
|
selectedNodeClass?: string | null | undefined;
|
|
75
|
+
dragOverNodeClass?: string | null | undefined;
|
|
73
76
|
expandIconClass?: string | null | undefined;
|
|
74
77
|
collapseIconClass?: string | null | undefined;
|
|
75
78
|
leafIconClass?: string | null | undefined;
|
|
@@ -79,6 +82,7 @@
|
|
|
79
82
|
|
|
80
83
|
let {
|
|
81
84
|
treeId,
|
|
85
|
+
treePathSeparator,
|
|
82
86
|
|
|
83
87
|
// MAPPINGS
|
|
84
88
|
idMember,
|
|
@@ -102,13 +106,14 @@
|
|
|
102
106
|
// DATA
|
|
103
107
|
data = $bindable(),
|
|
104
108
|
selectedNode = $bindable(),
|
|
109
|
+
insertResult = $bindable(),
|
|
105
110
|
|
|
106
111
|
// SLOTS
|
|
107
|
-
nodeTemplate,
|
|
108
|
-
treeHeader,
|
|
109
|
-
treeFooter,
|
|
110
|
-
noDataFound,
|
|
111
|
-
contextMenu,
|
|
112
|
+
nodeTemplate = undefined,
|
|
113
|
+
treeHeader = undefined,
|
|
114
|
+
treeFooter = undefined,
|
|
115
|
+
noDataFound = undefined,
|
|
116
|
+
contextMenu = undefined,
|
|
112
117
|
|
|
113
118
|
// BEHAVIOUR
|
|
114
119
|
expandLevel = 2,
|
|
@@ -133,21 +138,13 @@
|
|
|
133
138
|
collapseIconClass = 'ltree-icon-collapse',
|
|
134
139
|
leafIconClass = 'ltree-icon-leaf',
|
|
135
140
|
selectedNodeClass,
|
|
141
|
+
dragOverNodeClass,
|
|
136
142
|
scrollHighlightTimeout = 4000,
|
|
137
143
|
scrollHighlightClass = 'ltree-scroll-highlight'
|
|
138
144
|
}: Props = $props();
|
|
139
145
|
|
|
140
146
|
export async function expandNodes(nodePath: string) {
|
|
141
147
|
tree.expandNodes(nodePath);
|
|
142
|
-
|
|
143
|
-
// trie.dummyText = Date.now().toLocaleString();
|
|
144
|
-
// console.log(trie.dummyText);
|
|
145
|
-
// rootNodes.forEach((element) => {
|
|
146
|
-
// if (element.path === nodePath) {
|
|
147
|
-
// element.isExpanded = !element.isExpanded;
|
|
148
|
-
// console.log(element)
|
|
149
|
-
// }
|
|
150
|
-
// });
|
|
151
148
|
}
|
|
152
149
|
|
|
153
150
|
export async function collapseNodes(nodePath: string) {
|
|
@@ -162,6 +159,17 @@
|
|
|
162
159
|
tree?.collapseAll(nodePath);
|
|
163
160
|
}
|
|
164
161
|
|
|
162
|
+
export function filterNodes(searchText: string, searchOptions?: SearchOptions): void {
|
|
163
|
+
tree?.filterNodes(searchText, searchOptions);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
export function searchNodes(
|
|
167
|
+
searchText: string | null | undefined,
|
|
168
|
+
searchOptions?: SearchOptions
|
|
169
|
+
): LTreeNode<T>[] {
|
|
170
|
+
return tree?.searchNodes(searchText, searchOptions) || [];
|
|
171
|
+
}
|
|
172
|
+
|
|
165
173
|
export async function scrollToPath(
|
|
166
174
|
path: string,
|
|
167
175
|
options?: { expand?: boolean; highlight?: boolean; scrollOptions?: ScrollIntoViewOptions }
|
|
@@ -204,17 +212,7 @@
|
|
|
204
212
|
// Highlight the node temporarily if requested
|
|
205
213
|
if (highlight && scrollHighlightClass) {
|
|
206
214
|
contentDiv.classList.add(scrollHighlightClass);
|
|
207
|
-
console.log(
|
|
208
|
-
'🚀 elementId ~ scrollToPath ~ adding scrollHighlightClass:',
|
|
209
|
-
elementId,
|
|
210
|
-
scrollHighlightClass
|
|
211
|
-
);
|
|
212
215
|
setTimeout(() => {
|
|
213
|
-
console.log(
|
|
214
|
-
'🚀 elementId ~ scrollToPath ~ removing scrollHighlightClass:',
|
|
215
|
-
elementId,
|
|
216
|
-
scrollHighlightClass
|
|
217
|
-
);
|
|
218
216
|
contentDiv.classList.remove(scrollHighlightClass);
|
|
219
217
|
}, scrollHighlightTimeout);
|
|
220
218
|
}
|
|
@@ -244,6 +242,7 @@
|
|
|
244
242
|
searchValueMember,
|
|
245
243
|
getSearchValueCallback,
|
|
246
244
|
treeId,
|
|
245
|
+
treePathSeparator,
|
|
247
246
|
|
|
248
247
|
expandLevel,
|
|
249
248
|
|
|
@@ -265,7 +264,9 @@
|
|
|
265
264
|
});
|
|
266
265
|
|
|
267
266
|
$effect(() => {
|
|
268
|
-
tree
|
|
267
|
+
if (tree && data) {
|
|
268
|
+
insertResult = tree.insertArray(data);
|
|
269
|
+
}
|
|
269
270
|
});
|
|
270
271
|
|
|
271
272
|
// $inspect("trie change tracker", trie?.changeTracker?.toString());
|
|
@@ -428,9 +429,7 @@
|
|
|
428
429
|
</div>
|
|
429
430
|
{/if}
|
|
430
431
|
|
|
431
|
-
{
|
|
432
|
-
{@render treeHeader?.()}
|
|
433
|
-
{/if}
|
|
432
|
+
{@render treeHeader?.()}
|
|
434
433
|
<div class:bodyClass>
|
|
435
434
|
{#if tree?.root}
|
|
436
435
|
{#key tree.changeTracker}
|
|
@@ -449,6 +448,7 @@
|
|
|
449
448
|
{collapseIconClass}
|
|
450
449
|
{leafIconClass}
|
|
451
450
|
{selectedNodeClass}
|
|
451
|
+
{dragOverNodeClass}
|
|
452
452
|
isDraggedNode={draggedNode === node}
|
|
453
453
|
/>
|
|
454
454
|
{:else}
|
|
@@ -464,9 +464,8 @@
|
|
|
464
464
|
</div>
|
|
465
465
|
{/if}
|
|
466
466
|
</div>
|
|
467
|
-
|
|
467
|
+
|
|
468
468
|
{@render treeFooter?.()}
|
|
469
|
-
{/if}
|
|
470
469
|
|
|
471
470
|
<!-- Context Menu -->
|
|
472
471
|
{#if contextMenuVisible && contextMenu && contextMenuNode}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import type { Index } from 'flexsearch';
|
|
1
|
+
import type { Index, SearchOptions } from 'flexsearch';
|
|
2
2
|
import { type LTreeNode } from '../ltree/ltree-node.svelte.js';
|
|
3
|
+
import { type InsertArrayResult } from '../ltree/types.js';
|
|
3
4
|
declare function $$render<T>(): {
|
|
4
5
|
props: {
|
|
5
6
|
trieId?: string | null | undefined;
|
|
@@ -18,9 +19,11 @@ declare function $$render<T>(): {
|
|
|
18
19
|
searchValueMember?: string | null | undefined;
|
|
19
20
|
getSearchValueCallback?: (node: LTreeNode<T>) => string;
|
|
20
21
|
treeId?: string | null | undefined;
|
|
22
|
+
treePathSeparator?: string | null | undefined;
|
|
21
23
|
sortCallback?: (items: LTreeNode<T>[]) => LTreeNode<T>[];
|
|
22
24
|
data: T[];
|
|
23
25
|
selectedNode?: LTreeNode<T> | null | undefined;
|
|
26
|
+
insertResult?: InsertArrayResult<T> | null | undefined;
|
|
24
27
|
nodeTemplate?: any;
|
|
25
28
|
treeHeader?: any;
|
|
26
29
|
treeBody?: any;
|
|
@@ -41,6 +44,7 @@ declare function $$render<T>(): {
|
|
|
41
44
|
onNodeDrop?: (node: LTreeNode<T>, draggedNode: LTreeNode<T>, event: DragEvent) => void;
|
|
42
45
|
bodyClass?: string | null | undefined;
|
|
43
46
|
selectedNodeClass?: string | null | undefined;
|
|
47
|
+
dragOverNodeClass?: string | null | undefined;
|
|
44
48
|
expandIconClass?: string | null | undefined;
|
|
45
49
|
collapseIconClass?: string | null | undefined;
|
|
46
50
|
leafIconClass?: string | null | undefined;
|
|
@@ -52,13 +56,15 @@ declare function $$render<T>(): {
|
|
|
52
56
|
collapseNodes: (nodePath: string) => Promise<void>;
|
|
53
57
|
expandAll: (nodePath?: string | null | undefined) => void;
|
|
54
58
|
collapseAll: (nodePath?: string | null | undefined) => void;
|
|
59
|
+
filterNodes: (searchText: string, searchOptions?: SearchOptions) => void;
|
|
60
|
+
searchNodes: (searchText: string | null | undefined, searchOptions?: SearchOptions) => LTreeNode<T>[];
|
|
55
61
|
scrollToPath: (path: string, options?: {
|
|
56
62
|
expand?: boolean;
|
|
57
63
|
highlight?: boolean;
|
|
58
64
|
scrollOptions?: ScrollIntoViewOptions;
|
|
59
65
|
}) => Promise<boolean>;
|
|
60
66
|
};
|
|
61
|
-
bindings: "data" | "selectedNode" | "searchText";
|
|
67
|
+
bindings: "data" | "selectedNode" | "insertResult" | "searchText";
|
|
62
68
|
slots: {};
|
|
63
69
|
events: {};
|
|
64
70
|
};
|
|
@@ -66,12 +72,14 @@ declare class __sveltets_Render<T> {
|
|
|
66
72
|
props(): ReturnType<typeof $$render<T>>['props'];
|
|
67
73
|
events(): ReturnType<typeof $$render<T>>['events'];
|
|
68
74
|
slots(): ReturnType<typeof $$render<T>>['slots'];
|
|
69
|
-
bindings(): "data" | "selectedNode" | "searchText";
|
|
75
|
+
bindings(): "data" | "selectedNode" | "insertResult" | "searchText";
|
|
70
76
|
exports(): {
|
|
71
77
|
expandNodes: (nodePath: string) => Promise<void>;
|
|
72
78
|
collapseNodes: (nodePath: string) => Promise<void>;
|
|
73
79
|
expandAll: (nodePath?: string | null | undefined) => void;
|
|
74
80
|
collapseAll: (nodePath?: string | null | undefined) => void;
|
|
81
|
+
filterNodes: (searchText: string, searchOptions?: SearchOptions) => void;
|
|
82
|
+
searchNodes: (searchText: string | null | undefined, searchOptions?: SearchOptions) => LTreeNode<T>[];
|
|
75
83
|
scrollToPath: (path: string, options?: {
|
|
76
84
|
expand?: boolean;
|
|
77
85
|
highlight?: boolean;
|
package/dist/ltree/ltree-demo.js
CHANGED
|
@@ -78,7 +78,7 @@ export function performanceTest() {
|
|
|
78
78
|
console.time('Prefix search for "1."');
|
|
79
79
|
const results = trie.findByPrefix('1.');
|
|
80
80
|
console.timeEnd('Prefix search for "1."');
|
|
81
|
-
console.log(`Found ${results
|
|
81
|
+
console.log(`Found ${results?.length} paths with prefix "1."`);
|
|
82
82
|
// Memory usage
|
|
83
83
|
const stats = trie.getStats();
|
|
84
84
|
console.log('Final statistics:', stats);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { Index } from 'flexsearch';
|
|
2
2
|
import { type LTreeNode } from './ltree-node.svelte';
|
|
3
3
|
import type { Ltree } from './types.js';
|
|
4
|
-
export declare function createLTree<T>(_idMember: string, _pathMember: string, _parentPathMember?: string | null | undefined, _levelMember?: string | null | undefined, _hasChildrenMember?: string | null | undefined, _isExpandedMember?: string | null | undefined, _isSelectableMember?: string | null | undefined, _isDraggableMember?: string | null | undefined, _isDropAllowedMember?: string | null | undefined, _displayValueMember?: string | null | undefined, _getDisplayValueCallback?: (node: LTreeNode<T>) => string, _searchValueMember?: string | null | undefined, _getSearchValueCallback?: (node: LTreeNode<T>) => string, _treeId?: string, _expandLevel?: number | null | undefined, _shouldUseInternalSearchIndex?: boolean | null | undefined, _initializeIndexCallback?: () => Index, _indexerBatchSize?: number | null | undefined, _indexerTimeout?: number | null | undefined, opts?: Partial<Ltree<T>>): Ltree<T>;
|
|
4
|
+
export declare function createLTree<T>(_idMember: string, _pathMember: string, _parentPathMember?: string | null | undefined, _levelMember?: string | null | undefined, _hasChildrenMember?: string | null | undefined, _isExpandedMember?: string | null | undefined, _isSelectableMember?: string | null | undefined, _isDraggableMember?: string | null | undefined, _isDropAllowedMember?: string | null | undefined, _displayValueMember?: string | null | undefined, _getDisplayValueCallback?: (node: LTreeNode<T>) => string, _searchValueMember?: string | null | undefined, _getSearchValueCallback?: (node: LTreeNode<T>) => string, _treeId?: string, _treePathSeparator?: string | null | undefined, _expandLevel?: number | null | undefined, _shouldUseInternalSearchIndex?: boolean | null | undefined, _initializeIndexCallback?: () => Index, _indexerBatchSize?: number | null | undefined, _indexerTimeout?: number | null | undefined, opts?: Partial<Ltree<T>>): Ltree<T>;
|
|
@@ -4,7 +4,7 @@ import { isEmptyString } from '../helpers/string-helpers.js';
|
|
|
4
4
|
import { getLevel, getParentPath, getPathSegments, getRelativePath } from '../helpers/ltree-helpers.js';
|
|
5
5
|
import { createSearchIndex } from './flex.js';
|
|
6
6
|
import { Indexer } from './indexer.js';
|
|
7
|
-
export function createLTree(_idMember, _pathMember, _parentPathMember, _levelMember, _hasChildrenMember, _isExpandedMember, _isSelectableMember, _isDraggableMember, _isDropAllowedMember, _displayValueMember, _getDisplayValueCallback, _searchValueMember, _getSearchValueCallback, _treeId, _expandLevel, _shouldUseInternalSearchIndex, _initializeIndexCallback, _indexerBatchSize, _indexerTimeout, opts) {
|
|
7
|
+
export function createLTree(_idMember, _pathMember, _parentPathMember, _levelMember, _hasChildrenMember, _isExpandedMember, _isSelectableMember, _isDraggableMember, _isDropAllowedMember, _displayValueMember, _getDisplayValueCallback, _searchValueMember, _getSearchValueCallback, _treeId, _treePathSeparator, _expandLevel, _shouldUseInternalSearchIndex, _initializeIndexCallback, _indexerBatchSize, _indexerTimeout, opts) {
|
|
8
8
|
let shouldCalculateParentPath = isEmptyString(_parentPathMember);
|
|
9
9
|
let shouldCalculateLevel = isEmptyString(_levelMember);
|
|
10
10
|
let shouldCalculateHasChildren = isEmptyString(_hasChildrenMember);
|
|
@@ -40,7 +40,7 @@ export function createLTree(_idMember, _pathMember, _parentPathMember, _levelMem
|
|
|
40
40
|
}
|
|
41
41
|
return {
|
|
42
42
|
// Properties
|
|
43
|
-
treePathSeparator: '.',
|
|
43
|
+
treePathSeparator: _treePathSeparator || '.',
|
|
44
44
|
root,
|
|
45
45
|
get changeTracker() {
|
|
46
46
|
return changeTracker;
|
|
@@ -87,6 +87,7 @@ export function createLTree(_idMember, _pathMember, _parentPathMember, _levelMem
|
|
|
87
87
|
data = data || [];
|
|
88
88
|
// Clear any pending indexing from previous calls
|
|
89
89
|
indexer?.clearQueue();
|
|
90
|
+
flatTreeNodes = [];
|
|
90
91
|
performance.mark('conversion-start');
|
|
91
92
|
let mappedData = data.map((row, index) => {
|
|
92
93
|
const node = createLTreeNode();
|
|
@@ -132,22 +133,34 @@ export function createLTree(_idMember, _pathMember, _parentPathMember, _levelMem
|
|
|
132
133
|
console.log(`[Tree ${_treeId}] Mapped data after sort`, mappedData);
|
|
133
134
|
performance.mark('sort-end');
|
|
134
135
|
performance.mark('insert-start');
|
|
135
|
-
const
|
|
136
|
+
const failedNodes = [];
|
|
136
137
|
const itemsToIndex = [];
|
|
138
|
+
let realIndex = 0; // this is used to avoid scenario, when node cannot found a parent
|
|
139
|
+
let successfulCount = 0;
|
|
137
140
|
mappedData.forEach((node, index) => {
|
|
138
141
|
const result = this.insertTreeNode(node.parentPath, node, true);
|
|
139
142
|
if (result) {
|
|
140
|
-
|
|
143
|
+
failedNodes.push({
|
|
144
|
+
node: node,
|
|
145
|
+
originalData: data[index],
|
|
146
|
+
error: result
|
|
147
|
+
});
|
|
141
148
|
}
|
|
142
149
|
else {
|
|
150
|
+
successfulCount++;
|
|
143
151
|
// Collect items for batch indexing
|
|
144
152
|
if (_shouldUseInternalSearchIndex && indexer) {
|
|
145
|
-
|
|
153
|
+
flatTreeNodes.push(node);
|
|
154
|
+
itemsToIndex.push({ node, index: realIndex });
|
|
155
|
+
realIndex++;
|
|
146
156
|
}
|
|
147
157
|
}
|
|
148
158
|
});
|
|
149
|
-
|
|
150
|
-
|
|
159
|
+
// Log errors for backward compatibility and debugging
|
|
160
|
+
if (failedNodes.length > 0) {
|
|
161
|
+
const errorMessages = failedNodes.map((f) => f.error);
|
|
162
|
+
console.warn(`[Tree ${_treeId}] ${failedNodes.length} nodes failed to insert:`, errorMessages);
|
|
163
|
+
}
|
|
151
164
|
// Batch add items to indexer
|
|
152
165
|
if (itemsToIndex.length > 0 && indexer) {
|
|
153
166
|
indexer.setCallbacks(undefined, // no progress callback for now
|
|
@@ -172,11 +185,16 @@ export function createLTree(_idMember, _pathMember, _parentPathMember, _levelMem
|
|
|
172
185
|
console.log(`[Tree ${_treeId}] Conversion took: ${measure.duration}ms`);
|
|
173
186
|
measure = performance.getEntriesByName('insert-duration')[0];
|
|
174
187
|
console.log(`[Tree ${_treeId}] Insert took: ${measure.duration}ms`);
|
|
188
|
+
return {
|
|
189
|
+
successful: successfulCount,
|
|
190
|
+
failed: failedNodes,
|
|
191
|
+
total: data.length
|
|
192
|
+
};
|
|
175
193
|
},
|
|
176
194
|
insertTreeNode: function (parentPath, newNode, noEmitChanges) {
|
|
177
195
|
const parentNode = this.getNodeByPath(parentPath);
|
|
178
196
|
if (!parentNode) {
|
|
179
|
-
return `Could not find
|
|
197
|
+
return `Node: ${newNode.path} - Could not find parent node: ${parentPath}`;
|
|
180
198
|
}
|
|
181
199
|
if (shouldCalculateLevel) {
|
|
182
200
|
newNode.level = (parentNode.level || 0) + 1;
|
|
@@ -191,13 +209,12 @@ export function createLTree(_idMember, _pathMember, _parentPathMember, _levelMem
|
|
|
191
209
|
nodeCount++;
|
|
192
210
|
maxLevel = Math.max(maxLevel, newNode.level || 0);
|
|
193
211
|
}
|
|
194
|
-
flatTreeNodes.push(newNode);
|
|
195
212
|
if (!noEmitChanges) {
|
|
196
213
|
this._emitTreeChanged();
|
|
197
214
|
}
|
|
198
215
|
return null;
|
|
199
216
|
},
|
|
200
|
-
filterNodes(_searchText) {
|
|
217
|
+
filterNodes(_searchText, _searchOptions) {
|
|
201
218
|
if (this.shouldDisplayDebugInformation)
|
|
202
219
|
console.log(`[Tree ${_treeId}] Filtering nodes by:`, _searchText);
|
|
203
220
|
if (isEmptyString(_searchText)) {
|
|
@@ -214,10 +231,29 @@ export function createLTree(_idMember, _pathMember, _parentPathMember, _levelMem
|
|
|
214
231
|
console.warn(`[Tree ${_treeId}] Internal search index is disabled`);
|
|
215
232
|
return;
|
|
216
233
|
}
|
|
217
|
-
const resultIndices = searchIndex.search(_searchText);
|
|
234
|
+
const resultIndices = searchIndex.search(_searchText, _searchOptions);
|
|
218
235
|
const foundPaths = resultIndices.map((row) => flatTreeNodes[row].path);
|
|
236
|
+
if (this.shouldDisplayDebugInformation)
|
|
237
|
+
console.warn(`[Tree ${_treeId}] Found indices:`, resultIndices, foundPaths);
|
|
219
238
|
this.createFilteredTree(foundPaths);
|
|
220
239
|
},
|
|
240
|
+
searchNodes(_searchText, _searchOptions) {
|
|
241
|
+
if (this.shouldDisplayDebugInformation)
|
|
242
|
+
console.log(`[Tree ${_treeId}] Searching nodes by:`, _searchText);
|
|
243
|
+
if (isEmptyString(_searchText)) {
|
|
244
|
+
return [];
|
|
245
|
+
}
|
|
246
|
+
if (!_shouldUseInternalSearchIndex) {
|
|
247
|
+
if (this.shouldDisplayDebugInformation)
|
|
248
|
+
console.warn(`[Tree ${_treeId}] Internal search index is disabled`);
|
|
249
|
+
return [];
|
|
250
|
+
}
|
|
251
|
+
const resultIndices = searchIndex.search(_searchText, _searchOptions);
|
|
252
|
+
const foundNodes = resultIndices.map((row) => flatTreeNodes[row]);
|
|
253
|
+
if (this.shouldDisplayDebugInformation)
|
|
254
|
+
console.warn(`[Tree ${_treeId}] Found indices:`, resultIndices, foundNodes);
|
|
255
|
+
return foundNodes;
|
|
256
|
+
},
|
|
221
257
|
createFilteredTree(targetPaths) {
|
|
222
258
|
filteredRoot.children = {};
|
|
223
259
|
filteredTree = null;
|
package/dist/ltree/types.d.ts
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
|
+
import type { SearchOptions } from 'flexsearch';
|
|
1
2
|
import type { LTreeNode } from './ltree-node.svelte';
|
|
2
3
|
export type Tuple<T, U> = [T, U];
|
|
4
|
+
export interface InsertArrayResult<T> {
|
|
5
|
+
successful: number;
|
|
6
|
+
failed: Array<{
|
|
7
|
+
node: LTreeNode<T>;
|
|
8
|
+
originalData: T;
|
|
9
|
+
error: string;
|
|
10
|
+
}>;
|
|
11
|
+
total: number;
|
|
12
|
+
}
|
|
3
13
|
export interface Ltree<T> {
|
|
4
14
|
treePathSeparator: string;
|
|
5
15
|
root: LTreeNode<T>;
|
|
@@ -29,9 +39,10 @@ export interface Ltree<T> {
|
|
|
29
39
|
nodeCount: number;
|
|
30
40
|
maxLevel: number;
|
|
31
41
|
};
|
|
32
|
-
insertArray(data: T[]):
|
|
42
|
+
insertArray(data: T[]): InsertArrayResult<T>;
|
|
33
43
|
insertTreeNode(parentPath: string, newNode: LTreeNode<T>, noEmitChanges?: boolean): string | null;
|
|
34
|
-
filterNodes(_searchText: string): void;
|
|
44
|
+
filterNodes(_searchText: string, _searchOptions?: SearchOptions): void;
|
|
45
|
+
searchNodes(_searchText: string | null | undefined, _searchOptions?: SearchOptions): LTreeNode<T>[];
|
|
35
46
|
createFilteredTree(targetPaths: string[]): void;
|
|
36
47
|
clearFilter(): void;
|
|
37
48
|
expandAll(nodePath?: string | null | undefined): void;
|
package/dist/styles/main.scss
CHANGED
|
@@ -190,6 +190,35 @@ $body-color: #212529 !default;
|
|
|
190
190
|
}
|
|
191
191
|
}
|
|
192
192
|
|
|
193
|
+
// Drag-over Node Styles
|
|
194
|
+
.ltree-dragover-highlight {
|
|
195
|
+
background-color: rgba(var(--ltree-success-rgb), 0.15) !important;
|
|
196
|
+
border: 2px dashed var(--ltree-success) !important;
|
|
197
|
+
border-radius: 4px !important;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
.ltree-dragover-glow {
|
|
201
|
+
background-color: rgba(var(--ltree-primary-rgb), 0.1) !important;
|
|
202
|
+
box-shadow: 0 0 8px rgba(var(--ltree-primary-rgb), 0.4) !important;
|
|
203
|
+
border-radius: 4px !important;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
@keyframes bounce {
|
|
207
|
+
0%,
|
|
208
|
+
20%,
|
|
209
|
+
50%,
|
|
210
|
+
80%,
|
|
211
|
+
100% {
|
|
212
|
+
transform: translateY(-50%);
|
|
213
|
+
}
|
|
214
|
+
40% {
|
|
215
|
+
transform: translateY(-60%);
|
|
216
|
+
}
|
|
217
|
+
60% {
|
|
218
|
+
transform: translateY(-40%);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
193
222
|
// Drag and Drop Styles
|
|
194
223
|
.ltree-draggable {
|
|
195
224
|
cursor: grab;
|
|
@@ -339,12 +368,24 @@ $body-color: #212529 !default;
|
|
|
339
368
|
|
|
340
369
|
.ltree-scroll-highlight-arrow {
|
|
341
370
|
position: relative;
|
|
371
|
+
&::before {
|
|
372
|
+
content: '⇨'; /* arrow pointing right */
|
|
373
|
+
position: absolute;
|
|
374
|
+
left: -1.2em; /* place outside row */
|
|
375
|
+
top: 50%; /* vertical center */
|
|
376
|
+
transform: translateY(-57%);
|
|
377
|
+
font-size: 3em;
|
|
378
|
+
pointer-events: none; /* arrow doesn’t block hover */
|
|
379
|
+
color: var(--ltree-danger);
|
|
380
|
+
|
|
381
|
+
z-index: 1;
|
|
382
|
+
}
|
|
342
383
|
&::after {
|
|
343
384
|
content: '⇦'; /* arrow pointing right */
|
|
344
385
|
position: absolute;
|
|
345
386
|
right: 0em; /* place outside row */
|
|
346
387
|
top: 50%; /* vertical center */
|
|
347
|
-
transform: translateY(-
|
|
388
|
+
transform: translateY(-57%);
|
|
348
389
|
font-size: 3em;
|
|
349
390
|
pointer-events: none; /* arrow doesn’t block hover */
|
|
350
391
|
color: var(--ltree-danger);
|
package/dist/styles.css
CHANGED
|
@@ -138,6 +138,29 @@
|
|
|
138
138
|
font-weight: bold;
|
|
139
139
|
}
|
|
140
140
|
|
|
141
|
+
.ltree-dragover-highlight {
|
|
142
|
+
background-color: rgba(var(--ltree-success-rgb), 0.15) !important;
|
|
143
|
+
border: 2px dashed var(--ltree-success) !important;
|
|
144
|
+
border-radius: 4px !important;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
.ltree-dragover-glow {
|
|
148
|
+
background-color: rgba(var(--ltree-primary-rgb), 0.1) !important;
|
|
149
|
+
box-shadow: 0 0 8px rgba(var(--ltree-primary-rgb), 0.4) !important;
|
|
150
|
+
border-radius: 4px !important;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
@keyframes bounce {
|
|
154
|
+
0%, 20%, 50%, 80%, 100% {
|
|
155
|
+
transform: translateY(-50%);
|
|
156
|
+
}
|
|
157
|
+
40% {
|
|
158
|
+
transform: translateY(-60%);
|
|
159
|
+
}
|
|
160
|
+
60% {
|
|
161
|
+
transform: translateY(-40%);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
141
164
|
.ltree-draggable {
|
|
142
165
|
cursor: grab;
|
|
143
166
|
}
|
|
@@ -263,12 +286,23 @@
|
|
|
263
286
|
.ltree-scroll-highlight-arrow {
|
|
264
287
|
position: relative;
|
|
265
288
|
}
|
|
289
|
+
.ltree-scroll-highlight-arrow::before {
|
|
290
|
+
content: "⇨"; /* arrow pointing right */
|
|
291
|
+
position: absolute;
|
|
292
|
+
left: -1.2em; /* place outside row */
|
|
293
|
+
top: 50%; /* vertical center */
|
|
294
|
+
transform: translateY(-57%);
|
|
295
|
+
font-size: 3em;
|
|
296
|
+
pointer-events: none; /* arrow doesn’t block hover */
|
|
297
|
+
color: var(--ltree-danger);
|
|
298
|
+
z-index: 1;
|
|
299
|
+
}
|
|
266
300
|
.ltree-scroll-highlight-arrow::after {
|
|
267
301
|
content: "⇦"; /* arrow pointing right */
|
|
268
302
|
position: absolute;
|
|
269
303
|
right: 0em; /* place outside row */
|
|
270
304
|
top: 50%; /* vertical center */
|
|
271
|
-
transform: translateY(-
|
|
305
|
+
transform: translateY(-57%);
|
|
272
306
|
font-size: 3em;
|
|
273
307
|
pointer-events: none; /* arrow doesn’t block hover */
|
|
274
308
|
color: var(--ltree-danger);
|
package/dist/styles.css.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sourceRoot":"","sources":["../src/lib/styles/main.scss"],"names":[],"mappings":";AAmCA;EACC;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAID;EACC,aA5CA;;AA+CA;EACC;EACA,OAlCqB;EAmCrB;EACA;;;AAIF;EACC,aAxDA;EAyDA,WAvDqB;;AAyDrB;EACC;EACA;;AAGD;EACC;EACA;EACA,SAhE0B;EAiE1B,eAhEgC;EAiEhC;EACA;EACA;;AAEA;EACC,kBArEkB;;AAyEpB;EACC;;AAGD;EACC,OA7EuB;EA8EvB;EACA;EACA,cA7E8B;EA8E9B,WAhF2B;EAiF3B,OAhFuB;EAiFvB;EACA;;AAEA;EACC;;AAIF;EACC,cAxF4B;EAyF5B,WAxFyB;;AA2F1B;EACC,aA3F4B;EA4F5B,cA3F6B;;AA8F9B;EACC,WA9FyB;EA+FzB,OA9FqB;;AAiGtB;EACC,YAjGyB;;;AAsG3B;EACC;;;AAGD;EACC;;;AAGD;EACC;;;AAID;EACC;;;AAGD;EACC;;;AAGD;EACC;;;AAGD;EACC;;;AAGD;EACC;;;AAGD;EACC;;;AAID;EACC;EACA;;;AAGD;EACC;EACA;EACA;EACA;;;AAGD;EACC;;AAEA;EACC;EACA;EACA;EACA;;AAGD;EACC;EACA;EACA;EACA;;;AAKF;EACC;;AAEA;EACC;;;AAIF;EACC;EACA;EACA,YACC;;;AAKD;EACC;EACA;EACA;;AAEA;EACC;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAIF;EACC;EACA;;AAGD;EACC;EACA;;;AAKF;EACC;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACC;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACC;;AAGD;EACC;EACA;;AAGD;EACC;;AAEA;EACC;;AAKH;EACC;EACA;EACA;;;AAKF;EACC,
|
|
1
|
+
{"version":3,"sourceRoot":"","sources":["../src/lib/styles/main.scss"],"names":[],"mappings":";AAmCA;EACC;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAID;EACC,aA5CA;;AA+CA;EACC;EACA,OAlCqB;EAmCrB;EACA;;;AAIF;EACC,aAxDA;EAyDA,WAvDqB;;AAyDrB;EACC;EACA;;AAGD;EACC;EACA;EACA,SAhE0B;EAiE1B,eAhEgC;EAiEhC;EACA;EACA;;AAEA;EACC,kBArEkB;;AAyEpB;EACC;;AAGD;EACC,OA7EuB;EA8EvB;EACA;EACA,cA7E8B;EA8E9B,WAhF2B;EAiF3B,OAhFuB;EAiFvB;EACA;;AAEA;EACC;;AAIF;EACC,cAxF4B;EAyF5B,WAxFyB;;AA2F1B;EACC,aA3F4B;EA4F5B,cA3F6B;;AA8F9B;EACC,WA9FyB;EA+FzB,OA9FqB;;AAiGtB;EACC,YAjGyB;;;AAsG3B;EACC;;;AAGD;EACC;;;AAGD;EACC;;;AAID;EACC;;;AAGD;EACC;;;AAGD;EACC;;;AAGD;EACC;;;AAGD;EACC;;;AAGD;EACC;;;AAID;EACC;EACA;;;AAGD;EACC;EACA;EACA;EACA;;;AAGD;EACC;;AAEA;EACC;EACA;EACA;EACA;;AAGD;EACC;EACA;EACA;EACA;;;AAKF;EACC;EACA;EACA;;;AAGD;EACC;EACA;EACA;;;AAGD;EACC;IAKC;;EAED;IACC;;EAED;IACC;;;AAKF;EACC;;AAEA;EACC;;;AAIF;EACC;EACA;EACA,YACC;;;AAKD;EACC;EACA;EACA;;AAEA;EACC;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAIF;EACC;EACA;;AAGD;EACC;EACA;;;AAKF;EACC;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACC;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACC;;AAGD;EACC;EACA;;AAGD;EACC;;AAEA;EACC;;AAKH;EACC;EACA;EACA;;;AAKF;EACC,aAxTA;EAyTA;EACA;EACA;EACA;EACA;EACA;;AAGC;EACC;EACA;EACA;EACA;;AAEA;EACC;;AAIF;EACC;EACA;;AAIF;EACC;EACA;EACA;;AAEA;EACC;EACA;EACA;EACA;EACA;EACA;EACA;;;AAMH;EACC;EACA;EACA;;;AAGD;EACC;;AACA;EACC;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EAEA;;AAED;EACC;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EAEA;;;AAKF;EAEC;EACA;EACA;EAGA","file":"styles.css"}
|
package/package.json
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@keenmate/svelte-treeview",
|
|
3
|
-
"version": "4.0.0
|
|
3
|
+
"version": "4.0.0",
|
|
4
4
|
"scripts": {
|
|
5
|
-
"dev": "vite dev --port
|
|
5
|
+
"dev": "vite dev --port 17777",
|
|
6
6
|
"build": "vite build && npm run prepack",
|
|
7
|
+
"build:showcase": "vite build",
|
|
7
8
|
"preview": "vite preview",
|
|
8
9
|
"prepare": "svelte-kit sync || echo ''",
|
|
9
10
|
"prepack": "svelte-kit sync && svelte-package && sass src/lib/styles.scss dist/styles.css && publint",
|
|
@@ -40,10 +41,11 @@
|
|
|
40
41
|
"@eslint/compat": "^1.2.5",
|
|
41
42
|
"@eslint/js": "^9.18.0",
|
|
42
43
|
"@sveltejs/adapter-auto": "^6.0.0",
|
|
44
|
+
"@sveltejs/adapter-static": "^3.0.0",
|
|
43
45
|
"@sveltejs/kit": "^2.22.0",
|
|
44
46
|
"@sveltejs/package": "^2.0.0",
|
|
45
47
|
"@sveltejs/vite-plugin-svelte": "^6.0.0",
|
|
46
|
-
"@types/flexsearch": "^0.7.
|
|
48
|
+
"@types/flexsearch": "^0.7.42",
|
|
47
49
|
"eslint": "^9.18.0",
|
|
48
50
|
"eslint-config-prettier": "^10.0.1",
|
|
49
51
|
"eslint-plugin-svelte": "^3.0.0",
|
|
@@ -60,7 +62,7 @@
|
|
|
60
62
|
"vite": "^7.0.4"
|
|
61
63
|
},
|
|
62
64
|
"optionalDependencies": {
|
|
63
|
-
"flexsearch": "^0.8.
|
|
65
|
+
"flexsearch": "^0.8.212"
|
|
64
66
|
},
|
|
65
67
|
"keywords": [
|
|
66
68
|
"svelte",
|