@keenmate/svelte-treeview 0.3.5 → 0.3.6
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 +28 -3
- package/dist/Branch.svelte +4 -10
- package/dist/Branch.svelte.d.ts +1 -1
- package/dist/Checkbox.svelte +4 -1
- package/dist/TreeView.svelte +177 -153
- package/dist/TreeView.svelte.d.ts +1 -2
- package/dist/constants.d.ts +2 -1
- package/dist/constants.js +5 -0
- package/dist/helpers/tree-helper.d.ts +17 -17
- package/dist/helpers/tree-helper.js +18 -8
- package/dist/{helpers/drag-drop-helpers.d.ts → providers/drag-drop-provider.d.ts} +6 -10
- package/dist/{helpers/drag-drop-helpers.js → providers/drag-drop-provider.js} +14 -28
- package/dist/{helpers/selection-helpers.d.ts → providers/selection-provider.d.ts} +3 -3
- package/dist/{helpers/selection-helpers.js → providers/selection-provider.js} +1 -1
- package/dist/stores/drag-and-drop-store.d.ts +13 -0
- package/dist/stores/drag-and-drop-store.js +23 -0
- package/dist/tree-styles.sass +1 -0
- package/dist/types.d.ts +12 -2
- package/dist/types.js +13 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -4,10 +4,35 @@ The most elaborate treeview for svelte on earth (or even in our galaxy).
|
|
|
4
4
|
|
|
5
5
|
## Features
|
|
6
6
|
|
|
7
|
+
- load new nodes whne expanding
|
|
7
8
|
- automatically expanding to given depth
|
|
8
9
|
- customization of all object properties
|
|
9
|
-
-
|
|
10
|
+
- checkboxes enabled on whole tree or based on property
|
|
11
|
+
- recursive seletion mode, where leafes can be selected
|
|
12
|
+
- build-in support for search
|
|
10
13
|
|
|
11
|
-
##
|
|
14
|
+
## Instalation
|
|
12
15
|
|
|
13
|
-
svelte-treeview
|
|
16
|
+
install the package `@keenmate/svelte-treeview` using your favourite package manager.
|
|
17
|
+
|
|
18
|
+
Font awesome is required for expand/collapse icons.
|
|
19
|
+
|
|
20
|
+
## Minimal usage
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
```svelte
|
|
24
|
+
<script lang="ts">
|
|
25
|
+
import { TreeView } from '$lib/index.js';
|
|
26
|
+
|
|
27
|
+
let tree = [
|
|
28
|
+
{ nodePath: 'animals', title: 'Animals', hasChildren: true },
|
|
29
|
+
//...
|
|
30
|
+
{ nodePath: 'animals.insects.butterflies', title: 'Butterflies' }
|
|
31
|
+
];
|
|
32
|
+
</script>
|
|
33
|
+
|
|
34
|
+
<TreeView {tree} treeId="my-tree" let:node>
|
|
35
|
+
{node.title}
|
|
36
|
+
</TreeView>
|
|
37
|
+
|
|
38
|
+
```
|
package/dist/Branch.svelte
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<script>import { createEventDispatcher } from 'svelte';
|
|
2
2
|
import Checkbox from './Checkbox.svelte';
|
|
3
|
-
import { SelectionModes } from './types.js';
|
|
3
|
+
import { SelectionModes, InsertionType } from './types.js';
|
|
4
4
|
const dispatch = createEventDispatcher();
|
|
5
5
|
export let tree;
|
|
6
6
|
export let treeId;
|
|
@@ -22,12 +22,6 @@ export let canNest;
|
|
|
22
22
|
export let validTarget;
|
|
23
23
|
export let insPos;
|
|
24
24
|
const getNodeId = (node) => `${treeId}-${helper.path(node)}`;
|
|
25
|
-
// get children nodes
|
|
26
|
-
function getChildren(tree) {
|
|
27
|
-
const directChildren = helper.getDirectChildren(tree, helper.path(branchRootNode));
|
|
28
|
-
const orderedChildren = helper.dragDrop.OrderByPriority(directChildren);
|
|
29
|
-
return orderedChildren;
|
|
30
|
-
}
|
|
31
25
|
function setExpansion(node, changeTo) {
|
|
32
26
|
dispatch('internal-expand', { node: node, changeTo });
|
|
33
27
|
}
|
|
@@ -100,7 +94,7 @@ let liElements = {};
|
|
|
100
94
|
class:child-menu={childDepth > 0}
|
|
101
95
|
class={childDepth === 0 ? classes.treeClass : ''}
|
|
102
96
|
>
|
|
103
|
-
{#each
|
|
97
|
+
{#each helper.getDirectChildren(tree, helper.path(branchRootNode)) as node (getNodeId(node))}
|
|
104
98
|
{@const nesthighlighed = highlightNesting(node, highlightedNode, validTarget, canNest)}
|
|
105
99
|
{@const insertHighlighted = highlightInsert(node, highlightedNode, validTarget, canNest)}
|
|
106
100
|
{@const expanded = isExpanded(node, childDepth, expandTo)}
|
|
@@ -122,7 +116,7 @@ let liElements = {};
|
|
|
122
116
|
on:dragleave|stopPropagation={(e) => handleDragLeave(e, node, liElements[getNodeId(node)])}
|
|
123
117
|
bind:this={liElements[getNodeId(node)]}
|
|
124
118
|
>
|
|
125
|
-
{#if insPos ==
|
|
119
|
+
{#if insPos == InsertionType.above && insertHighlighted}
|
|
126
120
|
<div class="insert-line-wrapper">
|
|
127
121
|
<div class="insert-line {classes.inserLineClass}" />
|
|
128
122
|
</div>
|
|
@@ -208,7 +202,7 @@ let liElements = {};
|
|
|
208
202
|
<ul class:child-menu={childDepth > 0} />
|
|
209
203
|
{/if}
|
|
210
204
|
<!-- Show line if insering -->
|
|
211
|
-
{#if insPos
|
|
205
|
+
{#if insPos === InsertionType.below && insertHighlighted}
|
|
212
206
|
<div class="insert-line-wrapper">
|
|
213
207
|
<div class="insert-line {classes.inserLineClass}" />
|
|
214
208
|
</div>
|
package/dist/Branch.svelte.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { SvelteComponent } from "svelte";
|
|
2
|
-
import { SelectionModes,
|
|
2
|
+
import { SelectionModes, InsertionType, type Node } from './types.js';
|
|
3
3
|
import type { CustomizableClasses, TreeHelper } from './index.js';
|
|
4
4
|
declare const __propDef: {
|
|
5
5
|
props: {
|
package/dist/Checkbox.svelte
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
<script>import { createEventDispatcher } from 'svelte';
|
|
2
2
|
import { SelectionModes } from './types.js';
|
|
3
|
+
import { SelectionProvider } from './providers/selection-provider.js';
|
|
3
4
|
export let checkboxes;
|
|
4
5
|
export let helper;
|
|
5
6
|
export let recursive;
|
|
@@ -16,6 +17,8 @@ $: {
|
|
|
16
17
|
indeterminate = false;
|
|
17
18
|
}
|
|
18
19
|
}
|
|
20
|
+
// TODO pass from root
|
|
21
|
+
$: selectionProvider = new SelectionProvider(helper, recursive);
|
|
19
22
|
const dispatch = createEventDispatcher();
|
|
20
23
|
function onSelect(node) {
|
|
21
24
|
dispatch('select', { node });
|
|
@@ -23,7 +26,7 @@ function onSelect(node) {
|
|
|
23
26
|
</script>
|
|
24
27
|
|
|
25
28
|
{#if checkboxes == SelectionModes.perNode || checkboxes == SelectionModes.all}
|
|
26
|
-
{#if
|
|
29
|
+
{#if selectionProvider.isSelectable(node, checkboxes)}
|
|
27
30
|
<!-- select node -->
|
|
28
31
|
{#if !recursive || (recursive && !helper.props.hasChildren(node))}
|
|
29
32
|
<input
|
package/dist/TreeView.svelte
CHANGED
|
@@ -5,12 +5,13 @@ import { SelectionModes as SelectionModes } from './types.js';
|
|
|
5
5
|
import { TreeHelper } from './index.js';
|
|
6
6
|
import Branch from './Branch.svelte';
|
|
7
7
|
import { PropertyHelper } from './helpers/property-helper.js';
|
|
8
|
+
import { SelectionProvider } from './providers/selection-provider.js';
|
|
8
9
|
const dispatch = createEventDispatcher();
|
|
9
10
|
export let treeId;
|
|
10
11
|
/**
|
|
11
12
|
* Array of nodes that represent tree structure.
|
|
12
13
|
* Each node should have unique path
|
|
13
|
-
* All tree modifications are
|
|
14
|
+
* All tree modifications are made by modifying this array, so you need to bind it to parent component
|
|
14
15
|
*/
|
|
15
16
|
export let tree;
|
|
16
17
|
/**
|
|
@@ -118,6 +119,7 @@ $: helper = new TreeHelper(propHelper, {
|
|
|
118
119
|
checkboxes: selectionMode,
|
|
119
120
|
separator
|
|
120
121
|
});
|
|
122
|
+
$: selectionProvider = new SelectionProvider(helper, recursiveSelection);
|
|
121
123
|
$: filteredTree = helper.searchTree(tree, filter);
|
|
122
124
|
// compute vissual tree still caleed twice, because if we force update changes tree
|
|
123
125
|
// which fires this event again
|
|
@@ -191,13 +193,13 @@ function computeVisualTree(_tree) {
|
|
|
191
193
|
return;
|
|
192
194
|
}
|
|
193
195
|
debugLog('computing visual tree', { tree: _tree });
|
|
194
|
-
|
|
196
|
+
selectionProvider.recomputeAllVisualStates(_tree);
|
|
195
197
|
}
|
|
196
198
|
function onSelectionChanged(event) {
|
|
197
199
|
const { node } = event.detail;
|
|
198
200
|
const nodePath = helper.path(node);
|
|
199
|
-
const changeTo = !
|
|
200
|
-
|
|
201
|
+
const changeTo = !selectionProvider.isSelected(node);
|
|
202
|
+
selectionProvider.setSelection(tree, nodePath, changeTo);
|
|
201
203
|
debugLog("changing selection of node '", nodePath, "' to ", !propHelper.selected(node));
|
|
202
204
|
forceUpdate();
|
|
203
205
|
dispatch('selection', {
|
|
@@ -226,154 +228,173 @@ function debugLog(...data) {
|
|
|
226
228
|
function forceUpdate() {
|
|
227
229
|
tree = tree;
|
|
228
230
|
}
|
|
229
|
-
//#region drag and drop
|
|
230
|
-
function handleDragStart(e, node) {
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
}
|
|
243
|
-
function handleDragDrop(e, node, el) {
|
|
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
|
-
|
|
288
|
-
|
|
289
|
-
|
|
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
|
-
|
|
231
|
+
// //#region drag and drop
|
|
232
|
+
// function handleDragStart(e: DragEvent, node: Node) {
|
|
233
|
+
// // dont allos drag if is draggable is false
|
|
234
|
+
// if (propHelper.isDraggable(node) === false) {
|
|
235
|
+
// e.preventDefault();
|
|
236
|
+
// return;
|
|
237
|
+
// }
|
|
238
|
+
// console.log('dragstart from: ' + helper.path(node));
|
|
239
|
+
// //@ts-ignore
|
|
240
|
+
// e.dataTransfer.dropEffect = 'move';
|
|
241
|
+
// //@ts-ignore
|
|
242
|
+
// e.dataTransfer.setData('node_id', helper.path(node));
|
|
243
|
+
// draggedPath = helper.path(node);
|
|
244
|
+
// }
|
|
245
|
+
// function handleDragDrop(e: DragEvent, node: Node, el: HTMLElement) {
|
|
246
|
+
// //should be necesary but just in case
|
|
247
|
+
// highlightedNode = null;
|
|
248
|
+
// if (readonly || !dragAndDrop) return;
|
|
249
|
+
// //@ts-ignore
|
|
250
|
+
// draggedPath = e.dataTransfer.getData('node_id');
|
|
251
|
+
// console.log(draggedPath + ' dropped on: ' + helper.path(node));
|
|
252
|
+
// //important to check if timetonest is set, otherwise you could spend 30 minutes fixing this shit :)
|
|
253
|
+
// if (timeToNest) {
|
|
254
|
+
// const nowTimestamp = new Date();
|
|
255
|
+
// canNestTime =
|
|
256
|
+
// (dragenterTimestamp ? nowTimestamp.getTime() - dragenterTimestamp.getTime() : 1) >
|
|
257
|
+
// timeToNest;
|
|
258
|
+
// }
|
|
259
|
+
// let newNode = helper.findNode(tree, draggedPath);
|
|
260
|
+
// let oldNode = { ...(newNode as any) };
|
|
261
|
+
// let oldParent = helper.findNode(tree, helper.getParentNodePath(draggedPath));
|
|
262
|
+
// let insType = canNest ? 0 : helper.dragDrop.getInsertionPosition(e, el);
|
|
263
|
+
// //cancel move if its not valid
|
|
264
|
+
// if (insType == 0 && propHelper.nestDisabled(node) === true) return;
|
|
265
|
+
// else if ((insType == -1 || insType == 1) && propHelper.insertDisabled(node) === true) return;
|
|
266
|
+
// //callback can cancell move
|
|
267
|
+
// if (
|
|
268
|
+
// beforeMovedCallback &&
|
|
269
|
+
// beforeMovedCallback(oldNode, oldParent, node, helper.dragDrop.huminifyInsType(insType)) ===
|
|
270
|
+
// false
|
|
271
|
+
// )
|
|
272
|
+
// return;
|
|
273
|
+
// tree = helper.dragDrop.moveNode(
|
|
274
|
+
// tree,
|
|
275
|
+
// draggedPath,
|
|
276
|
+
// helper.path(node),
|
|
277
|
+
// insType,
|
|
278
|
+
// recalculateNodePath
|
|
279
|
+
// );
|
|
280
|
+
// let newParent = helper.findNode(tree, helper.getParentNodePath(helper.path(newNode))) ?? null;
|
|
281
|
+
// dispatch('moved', {
|
|
282
|
+
// oldParent: oldParent,
|
|
283
|
+
// newParent: newParent,
|
|
284
|
+
// oldNode: oldNode,
|
|
285
|
+
// newNode: newNode,
|
|
286
|
+
// targetNode: node,
|
|
287
|
+
// insType: helper.dragDrop.huminifyInsType(insType)
|
|
288
|
+
// });
|
|
289
|
+
// //reset props
|
|
290
|
+
// dragenterTimestamp = null;
|
|
291
|
+
// draggedPath = null;
|
|
292
|
+
// highlightedNode = null;
|
|
293
|
+
// }
|
|
294
|
+
// function handleDragOver(e: DragEvent, node: Node, el: HTMLElement) {
|
|
295
|
+
// insPos = helper.dragDrop.getInsertionPosition(e, el);
|
|
296
|
+
// //if you are further away from right then treshold allow nesting
|
|
297
|
+
// // @ts-ignore
|
|
298
|
+
// let diff = e.x - e.target?.getBoundingClientRect()?.x;
|
|
299
|
+
// if (pixelNestTreshold && diff > pixelNestTreshold) {
|
|
300
|
+
// canNestPos = true;
|
|
301
|
+
// } else {
|
|
302
|
+
// canNestPos = false;
|
|
303
|
+
// }
|
|
304
|
+
// //allow drop if valid target
|
|
305
|
+
// if (validTarget) e.preventDefault();
|
|
306
|
+
// }
|
|
307
|
+
// function handleDragEnter(e: DragEvent, node: Node, el: HTMLElement) {
|
|
308
|
+
// setTimeout(() => {
|
|
309
|
+
// insPos = helper.dragDrop.getInsertionPosition(e, el);
|
|
310
|
+
// validTarget = true;
|
|
311
|
+
// dragenterTimestamp = new Date();
|
|
312
|
+
// // will cause flashing when moving wrom node to node while be able to nest
|
|
313
|
+
// //* have to be here if you only use time
|
|
314
|
+
// highlightedNode = node;
|
|
315
|
+
// if (timeToNest) {
|
|
316
|
+
// canNestTime = false;
|
|
317
|
+
// //this is so that only one timeout is ticking at one time
|
|
318
|
+
// clearTimeout(dragTimeout);
|
|
319
|
+
// dragTimeout = setTimeout(() => {
|
|
320
|
+
// canNestTime = true;
|
|
321
|
+
// }, timeToNest);
|
|
322
|
+
// }
|
|
323
|
+
// //dont allow drop on child element and if both insertDisabled and nestDisabled to true
|
|
324
|
+
// if (
|
|
325
|
+
// helper.path(node)?.startsWith(draggedPath ?? '') ||
|
|
326
|
+
// (propHelper.insertDisabled(node) === true && propHelper.nestDisabled(node) === true)
|
|
327
|
+
// ) {
|
|
328
|
+
// validTarget = false;
|
|
329
|
+
// }
|
|
330
|
+
// //if defined calling callback
|
|
331
|
+
// if (dragEnterCallback) {
|
|
332
|
+
// //get node for event
|
|
333
|
+
// let draggedNode = helper.findNode(tree, draggedPath ?? '');
|
|
334
|
+
// let oldParent = helper.findNode(tree, helper.getParentNodePath(draggedPath ?? ''));
|
|
335
|
+
// //callback returning false means that it isnt valid target
|
|
336
|
+
// if (dragEnterCallback(draggedNode, oldParent, node) === false) {
|
|
337
|
+
// validTarget = false;
|
|
338
|
+
// }
|
|
339
|
+
// }
|
|
340
|
+
// }, 1);
|
|
341
|
+
// e.preventDefault();
|
|
342
|
+
// }
|
|
343
|
+
// function handleDragEnd(e: DragEvent, node: Node) {
|
|
344
|
+
// //reset prop on next tick
|
|
345
|
+
// setTimeout(() => {
|
|
346
|
+
// draggedPath = null;
|
|
347
|
+
// highlightedNode = null;
|
|
348
|
+
// }, 1);
|
|
349
|
+
// }
|
|
350
|
+
// function handleDragleave(e: DragEvent, node: Node, el: HTMLElement) {
|
|
351
|
+
// // highlightedNode = null;
|
|
352
|
+
// }
|
|
353
|
+
// /**
|
|
354
|
+
// *check if this node is one being hovered over (highlited) and is valid target
|
|
355
|
+
// */
|
|
356
|
+
// function highlighThisNode(node: Node, highlitedNode: Node, validTarget: boolean) {
|
|
357
|
+
// return validTarget && helper.path(highlitedNode) == helper.path(node);
|
|
358
|
+
// }
|
|
359
|
+
// /**
|
|
360
|
+
// * returns true, it should highlight nesting on this node
|
|
361
|
+
// * @param node node
|
|
362
|
+
// * @param highlitedNode highlited node
|
|
363
|
+
// * @param validTarget valid target
|
|
364
|
+
// * @param canNest can nest
|
|
365
|
+
// */
|
|
366
|
+
// function highlightNesting(
|
|
367
|
+
// node: Node,
|
|
368
|
+
// highlitedNode: Node,
|
|
369
|
+
// validTarget: boolean,
|
|
370
|
+
// canNest: boolean
|
|
371
|
+
// ) {
|
|
372
|
+
// return (
|
|
373
|
+
// canNest &&
|
|
374
|
+
// highlighThisNode(node, highlitedNode, validTarget) &&
|
|
375
|
+
// propHelper.nestDisabled(node) !== true
|
|
376
|
+
// );
|
|
377
|
+
// }
|
|
378
|
+
// /**
|
|
379
|
+
// * returns true, it should highlight nesting on this node
|
|
380
|
+
// * @param node node
|
|
381
|
+
// * @param highlitedNode highlited node
|
|
382
|
+
// * @param validTarget valid target
|
|
383
|
+
// * @param canNest can nest
|
|
384
|
+
// */
|
|
385
|
+
// function highlightInsert(
|
|
386
|
+
// node: Node,
|
|
387
|
+
// highlitedNode: Node,
|
|
388
|
+
// validTarget: boolean,
|
|
389
|
+
// canNest: boolean
|
|
390
|
+
// ) {
|
|
391
|
+
// return (
|
|
392
|
+
// !canNest &&
|
|
393
|
+
// highlighThisNode(node, highlitedNode, validTarget) &&
|
|
394
|
+
// propHelper.insertDisabled(node) !== true
|
|
395
|
+
// );
|
|
396
|
+
// }
|
|
397
|
+
// //#endregion
|
|
377
398
|
</script>
|
|
378
399
|
|
|
379
400
|
<Branch
|
|
@@ -410,7 +431,10 @@ function highlightInsert(node, highlitedNode, validTarget, canNest) {
|
|
|
410
431
|
</svelte:fragment>
|
|
411
432
|
</ContextMenu>
|
|
412
433
|
|
|
413
|
-
<style global>:global(.treeview
|
|
434
|
+
<style global>:global(.treeview) {
|
|
435
|
+
padding: 0;
|
|
436
|
+
}
|
|
437
|
+
:global(.treeview.show-lines) :global(ul:before) {
|
|
414
438
|
border-left: solid black 1px;
|
|
415
439
|
}
|
|
416
440
|
:global(.treeview.show-lines) :global(ul) :global(li:before) {
|
|
@@ -6,7 +6,7 @@ declare const __propDef: {
|
|
|
6
6
|
/**
|
|
7
7
|
* Array of nodes that represent tree structure.
|
|
8
8
|
* Each node should have unique path
|
|
9
|
-
* All tree modifications are
|
|
9
|
+
* All tree modifications are made by modifying this array, so you need to bind it to parent component
|
|
10
10
|
*/ tree: Node[];
|
|
11
11
|
/**
|
|
12
12
|
* Object properties where information about node is stored
|
|
@@ -82,7 +82,6 @@ declare const __propDef: {
|
|
|
82
82
|
selection: CustomEvent<any>;
|
|
83
83
|
selected: CustomEvent<any>;
|
|
84
84
|
unselected: CustomEvent<any>;
|
|
85
|
-
moved: CustomEvent<any>;
|
|
86
85
|
} & {
|
|
87
86
|
[evt: string]: CustomEvent<any>;
|
|
88
87
|
};
|
package/dist/constants.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type HelperConfig, type CustomizableClasses, type Props } from './types.js';
|
|
2
2
|
export declare const defaultPropNames: Props;
|
|
3
3
|
export declare const defaultPixelTreshold = 50;
|
|
4
4
|
export declare const defaultClasses: CustomizableClasses;
|
|
5
|
+
export declare const defaultConfig: HelperConfig;
|
package/dist/constants.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { SelectionModes } from './types.js';
|
|
1
2
|
export const defaultPropNames = {
|
|
2
3
|
nodePath: 'nodePath',
|
|
3
4
|
hasChildren: 'hasChildren',
|
|
@@ -22,3 +23,7 @@ export const defaultClasses = {
|
|
|
22
23
|
inserLineClass: '',
|
|
23
24
|
inserLineNestClass: ''
|
|
24
25
|
};
|
|
26
|
+
export const defaultConfig = {
|
|
27
|
+
separator: '.',
|
|
28
|
+
checkboxes: SelectionModes.none
|
|
29
|
+
};
|
|
@@ -1,34 +1,34 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { DragAndDropHelper } from './drag-drop-helpers.js';
|
|
3
|
-
import type { Node, NodePath, HelperConfig } from '../types.js';
|
|
1
|
+
import type { Node, NodePath, HelperConfig, Tree } from '../types.js';
|
|
4
2
|
import type { PropertyHelper } from './property-helper.js';
|
|
5
3
|
export declare class TreeHelper {
|
|
6
4
|
props: PropertyHelper;
|
|
7
5
|
config: HelperConfig;
|
|
8
|
-
selection: SelectionHelper;
|
|
9
|
-
dragDrop: DragAndDropHelper;
|
|
10
6
|
constructor(props: PropertyHelper, config?: HelperConfig);
|
|
11
7
|
path(node: Node | null): NodePath;
|
|
12
8
|
getParentNodePath(nodePath: NodePath): NodePath;
|
|
13
9
|
isChildrenOf(parentNodePath: NodePath, childrenNodePath: NodePath): boolean | undefined;
|
|
14
|
-
hasChildren(tree:
|
|
15
|
-
findNode(tree:
|
|
10
|
+
hasChildren(tree: Tree, nodePath: NodePath): unknown;
|
|
11
|
+
findNode(tree: Tree, nodePath: NodePath): Node;
|
|
16
12
|
nodePathIsChild(nodePath: NodePath): boolean | undefined;
|
|
17
|
-
getDirectChildren(tree:
|
|
18
|
-
allCHildren(tree:
|
|
19
|
-
getAllLeafNodes(tree:
|
|
20
|
-
joinTrees(filteredTree:
|
|
21
|
-
mergeTrees(oldTree:
|
|
13
|
+
getDirectChildren(tree: Tree, parentNodePath: NodePath): Tree;
|
|
14
|
+
allCHildren(tree: Tree, parentNodePath: NodePath): unknown[];
|
|
15
|
+
getAllLeafNodes(tree: Tree): unknown[];
|
|
16
|
+
joinTrees(filteredTree: Tree, tree: Tree): unknown[];
|
|
17
|
+
mergeTrees(oldTree: Tree, addedTree: Tree, nodePath?: string): unknown[];
|
|
22
18
|
/** toggles expansion on
|
|
23
19
|
*/
|
|
24
|
-
changeExpansion(tree:
|
|
20
|
+
changeExpansion(tree: Tree, node: Node, changeTo: boolean): void;
|
|
25
21
|
/** changes expansion of every node that has this.hasChildren set to true
|
|
26
22
|
*/
|
|
27
|
-
changeEveryExpansion(tree:
|
|
23
|
+
changeEveryExpansion(tree: Tree, changeTo: boolean): unknown[];
|
|
28
24
|
/** changes expansion of every node that has this.hasChildren set to true if they are abose set level and expansion property isnt set
|
|
29
25
|
*/
|
|
30
|
-
expandToLevel(tree:
|
|
26
|
+
expandToLevel(tree: Tree, level: number): unknown[];
|
|
31
27
|
getDepthLevel(nodePath: NodePath): number;
|
|
32
|
-
searchTree(tree:
|
|
33
|
-
getParents(tree:
|
|
28
|
+
searchTree(tree: Tree, filter: (node: unknown) => boolean): unknown[];
|
|
29
|
+
getParents(tree: Tree, node: Node): unknown[];
|
|
30
|
+
/** orders nodes by priorityProp
|
|
31
|
+
*/
|
|
32
|
+
orderByPriority(tree: Tree): Tree;
|
|
33
|
+
getNodeId(nodePath: NodePath): string | null;
|
|
34
34
|
}
|
|
@@ -1,17 +1,12 @@
|
|
|
1
1
|
import orderBy from 'lodash.unionby'; // used by tree merge
|
|
2
2
|
import uniqueBy from 'lodash.uniqby'; // used by tree merge
|
|
3
|
-
import {
|
|
4
|
-
import { DragAndDropHelper } from './drag-drop-helpers.js';
|
|
3
|
+
import { defaultConfig } from '../constants.js';
|
|
5
4
|
export class TreeHelper {
|
|
6
5
|
props;
|
|
7
6
|
config;
|
|
8
|
-
|
|
9
|
-
dragDrop;
|
|
10
|
-
constructor(props, config = {}) {
|
|
7
|
+
constructor(props, config = defaultConfig) {
|
|
11
8
|
this.props = props;
|
|
12
9
|
this.config = config;
|
|
13
|
-
this.selection = new SelectionHelper(this, config?.recursive ?? false);
|
|
14
|
-
this.dragDrop = new DragAndDropHelper(this);
|
|
15
10
|
}
|
|
16
11
|
// replace with this.props.path
|
|
17
12
|
path(node) {
|
|
@@ -47,7 +42,8 @@ export class TreeHelper {
|
|
|
47
42
|
const children = (tree || []).filter((x) => !parentNodePath
|
|
48
43
|
? !this.nodePathIsChild(this.path(x))
|
|
49
44
|
: this.getParentNodePath(this.path(x)) === parentNodePath);
|
|
50
|
-
|
|
45
|
+
const ordered = this.orderByPriority(children);
|
|
46
|
+
return ordered;
|
|
51
47
|
}
|
|
52
48
|
allCHildren(tree, parentNodePath) {
|
|
53
49
|
const children = tree.filter((x) => this.isChildrenOf(parentNodePath, this.path(x)));
|
|
@@ -126,4 +122,18 @@ export class TreeHelper {
|
|
|
126
122
|
const parentNodes = tree.filter((n) => parentsPaths.some((parentNodePath) => this.path(n) === parentNodePath));
|
|
127
123
|
return parentNodes;
|
|
128
124
|
}
|
|
125
|
+
/** orders nodes by priorityProp
|
|
126
|
+
*/
|
|
127
|
+
orderByPriority(tree) {
|
|
128
|
+
// TODO investigata that it really works
|
|
129
|
+
tree.sort((a, b) => this.props.priority(b) ? this.props.priority(a) - this.props.priority(b) : 1);
|
|
130
|
+
return tree;
|
|
131
|
+
}
|
|
132
|
+
getNodeId(nodePath) {
|
|
133
|
+
if (nodePath == null) {
|
|
134
|
+
console.warn('getting node id of null node path');
|
|
135
|
+
return null;
|
|
136
|
+
}
|
|
137
|
+
return nodePath?.split(this.config.separator).slice(-1)[0];
|
|
138
|
+
}
|
|
129
139
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import type { PropertyHelper } from '
|
|
2
|
-
import type { TreeHelper } from '
|
|
3
|
-
import
|
|
4
|
-
export declare class
|
|
1
|
+
import type { PropertyHelper } from '../helpers/property-helper.js';
|
|
2
|
+
import type { TreeHelper } from '../helpers/tree-helper.js';
|
|
3
|
+
import { InsertionType, type Node, type NodePath, type Tree } from '../types.js';
|
|
4
|
+
export declare class DragDropProvider {
|
|
5
5
|
helper: TreeHelper;
|
|
6
6
|
props: PropertyHelper;
|
|
7
7
|
separator: string;
|
|
@@ -15,7 +15,7 @@ export declare class DragAndDropHelper {
|
|
|
15
15
|
* @param {int} insType - if true, it will insert moved node as child of target node, if false, it will insert it bellow it in priority
|
|
16
16
|
* @param {boolean} recalculateNodePath - wont recalculare id of moved node, used when last part of nodePath is always unique
|
|
17
17
|
*/
|
|
18
|
-
moveNode(tree: Node[], movedNodePath: NodePath, targetNodePath: NodePath, insType:
|
|
18
|
+
moveNode(tree: Node[], movedNodePath: NodePath, targetNodePath: NodePath, insType: InsertionType, recalculateNodePath: boolean): Node[];
|
|
19
19
|
calculateNewNodePath(tree: Tree, parentNodePath: NodePath, movedNodePath: NodePath, recalculateNodePath: boolean): string;
|
|
20
20
|
updatePriority(tree: Tree, node: Node, parentNodePath: NodePath, newNodePath: NodePath, targetNode: Node, insType: InsertionType): void;
|
|
21
21
|
/** recomputes all priorities after inserted priority.F
|
|
@@ -23,10 +23,6 @@ export declare class DragAndDropHelper {
|
|
|
23
23
|
*/
|
|
24
24
|
recalculatesPriorities(tree: Tree, parentNode: NodePath, movedNodePath: NodePath, insertedPriority?: number): void;
|
|
25
25
|
/** return biggest value of nodepath number that children are using +1 */
|
|
26
|
-
getNextNodeId(tree: Tree, parentPath: NodePath):
|
|
26
|
+
getNextNodeId(tree: Tree, parentPath: NodePath): string;
|
|
27
27
|
getInsertionPosition(e: DragEvent, element: HTMLElement): InsertionType;
|
|
28
|
-
huminifyInsType(insType: InsertionType): string;
|
|
29
|
-
/** orders nodes by priorityProp
|
|
30
|
-
*/
|
|
31
|
-
OrderByPriority(tree: Tree): Tree;
|
|
32
28
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
|
|
1
|
+
import { InsertionType } from '../types.js';
|
|
2
|
+
export class DragDropProvider {
|
|
2
3
|
helper;
|
|
3
4
|
props;
|
|
4
5
|
separator;
|
|
@@ -22,7 +23,7 @@ export class DragAndDropHelper {
|
|
|
22
23
|
// cannot move root node
|
|
23
24
|
if (!movedNodePath)
|
|
24
25
|
return tree;
|
|
25
|
-
const isNesting = insType
|
|
26
|
+
const isNesting = insType === InsertionType.nest;
|
|
26
27
|
// if you are not isNestinging, you want to be on same level
|
|
27
28
|
//so you will have same parent as target node
|
|
28
29
|
const parentNodePath = isNesting
|
|
@@ -66,7 +67,7 @@ export class DragAndDropHelper {
|
|
|
66
67
|
const oldIndex = tree.findIndex((x) => this.path(x) == newNodePath);
|
|
67
68
|
tree.splice(oldIndex, 1);
|
|
68
69
|
const index = tree.findIndex((x) => this.path(x) == this.path(targetNode));
|
|
69
|
-
tree.splice(index + (insType ==
|
|
70
|
+
tree.splice(index + (insType == InsertionType.above ? 0 : 1), 0, movedNode);
|
|
70
71
|
//TODO maybe add option to setting this.hasChildren to false when moved last children
|
|
71
72
|
//hide plus icon if parent of moved node doesnt have any more children
|
|
72
73
|
const oldParent = this.helper.findNode(tree, this.helper.getParentNodePath(movedNodePath));
|
|
@@ -85,18 +86,20 @@ export class DragAndDropHelper {
|
|
|
85
86
|
}
|
|
86
87
|
else {
|
|
87
88
|
//get last segment of path
|
|
88
|
-
nodeId =
|
|
89
|
+
nodeId = this.helper.getNodeId(movedNodePath);
|
|
89
90
|
}
|
|
90
|
-
|
|
91
|
+
if (parentNodePath === null)
|
|
92
|
+
return nodeId;
|
|
93
|
+
return `${parentNodePath}${this.separator}${nodeId}`;
|
|
91
94
|
}
|
|
92
95
|
updatePriority(tree, node, parentNodePath, newNodePath, targetNode, insType) {
|
|
93
|
-
const isNesting = insType ==
|
|
96
|
+
const isNesting = insType == InsertionType.nest;
|
|
94
97
|
if (isNesting || this.props.priority(targetNode) != null) {
|
|
95
98
|
let newpriority = 0;
|
|
96
99
|
if (!isNesting) {
|
|
97
100
|
//calculate next
|
|
98
101
|
newpriority = this.props.priority(targetNode) ?? 0;
|
|
99
|
-
if (insType ==
|
|
102
|
+
if (insType == InsertionType.below) {
|
|
100
103
|
newpriority += 1;
|
|
101
104
|
}
|
|
102
105
|
else {
|
|
@@ -117,7 +120,7 @@ export class DragAndDropHelper {
|
|
|
117
120
|
//? maybe it will recalculate properly if dont set insertedPriority
|
|
118
121
|
recalculatesPriorities(tree, parentNode, movedNodePath, insertedPriority = -1) {
|
|
119
122
|
let nextPriority = insertedPriority + 1;
|
|
120
|
-
this.
|
|
123
|
+
this.helper.orderByPriority(this.helper.allCHildren(tree, parentNode)).forEach((node) => {
|
|
121
124
|
if (this.props.priority(node) >= insertedPriority && this.path(node) != movedNodePath) {
|
|
122
125
|
this.props.setPriority(node, nextPriority++);
|
|
123
126
|
}
|
|
@@ -134,31 +137,14 @@ export class DragAndDropHelper {
|
|
|
134
137
|
max = Math.max(max, num);
|
|
135
138
|
}
|
|
136
139
|
});
|
|
137
|
-
return max + 1;
|
|
140
|
+
return (max + 1).toString();
|
|
138
141
|
}
|
|
139
142
|
getInsertionPosition(e, element) {
|
|
140
143
|
const targetCords = element.getBoundingClientRect();
|
|
141
144
|
const half = targetCords.bottom - targetCords.height / 2;
|
|
142
145
|
if (e.y < half) {
|
|
143
|
-
return
|
|
146
|
+
return InsertionType.below;
|
|
144
147
|
}
|
|
145
|
-
return
|
|
146
|
-
}
|
|
147
|
-
huminifyInsType(insType) {
|
|
148
|
-
switch (insType) {
|
|
149
|
-
case 1:
|
|
150
|
-
return 'before';
|
|
151
|
-
case 0:
|
|
152
|
-
return 'inside';
|
|
153
|
-
case -1:
|
|
154
|
-
return 'after';
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
/** orders nodes by priorityProp
|
|
158
|
-
*/
|
|
159
|
-
OrderByPriority(tree) {
|
|
160
|
-
// TODO investigata that it really works
|
|
161
|
-
tree.sort((a, b) => this.props.priority(b) ? this.props.priority(a) - this.props.priority(b) : 1);
|
|
162
|
-
return tree;
|
|
148
|
+
return InsertionType.above;
|
|
163
149
|
}
|
|
164
150
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import type { PropertyHelper } from '
|
|
2
|
-
import type { TreeHelper } from '
|
|
1
|
+
import type { PropertyHelper } from '../helpers/property-helper.js';
|
|
2
|
+
import type { TreeHelper } from '../helpers/tree-helper.js';
|
|
3
3
|
import { SelectionModes, type Node, type NodePath, type Tree } from '../types.js';
|
|
4
|
-
export declare class
|
|
4
|
+
export declare class SelectionProvider {
|
|
5
5
|
helper: TreeHelper;
|
|
6
6
|
props: PropertyHelper;
|
|
7
7
|
recursiveMode: boolean;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/// <reference types="svelte" />
|
|
2
|
+
import type { TreeHelper } from '../helpers/tree-helper.js';
|
|
3
|
+
import { type BeforeMovedCallback, type DragEnterCallback, HighlighType } from '../types.js';
|
|
4
|
+
import { type Readable } from 'svelte/store';
|
|
5
|
+
type dragConfig = {
|
|
6
|
+
dragEnterCallback: DragEnterCallback | null;
|
|
7
|
+
beforeMovedCallback: BeforeMovedCallback | null;
|
|
8
|
+
};
|
|
9
|
+
export declare function startDrag(config: dragConfig): {
|
|
10
|
+
highligh(helper: TreeHelper, node: Node): Readable<HighlighType>;
|
|
11
|
+
isDragged(helper: TreeHelper, node: Node): Readable<boolean>;
|
|
12
|
+
};
|
|
13
|
+
export {};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { HighlighType } from '../types.js';
|
|
2
|
+
import { derived, writable } from 'svelte/store';
|
|
3
|
+
const storeDefaults = {
|
|
4
|
+
dragging: false,
|
|
5
|
+
x: 0,
|
|
6
|
+
y: 0
|
|
7
|
+
};
|
|
8
|
+
export function startDrag(config) {
|
|
9
|
+
const draggedNodeStore = writable(null);
|
|
10
|
+
return {
|
|
11
|
+
highligh(helper, node) {
|
|
12
|
+
// forces update when gragged nodes is changed
|
|
13
|
+
return derived([draggedNodeStore], ([store]) => {
|
|
14
|
+
return HighlighType.none;
|
|
15
|
+
});
|
|
16
|
+
},
|
|
17
|
+
isDragged(helper, node) {
|
|
18
|
+
return derived([draggedNodeStore], ([draggedNode]) => {
|
|
19
|
+
return helper.path(node) === helper.path(draggedNode);
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
}
|
package/dist/tree-styles.sass
CHANGED
package/dist/types.d.ts
CHANGED
|
@@ -23,7 +23,11 @@ export declare enum SelectionModes {
|
|
|
23
23
|
}
|
|
24
24
|
export type Node = unknown;
|
|
25
25
|
export type Tree = Node[];
|
|
26
|
-
export
|
|
26
|
+
export declare enum InsertionType {
|
|
27
|
+
above = "above",
|
|
28
|
+
below = "below",
|
|
29
|
+
nest = "nest"
|
|
30
|
+
}
|
|
27
31
|
export type NodePath = string | null;
|
|
28
32
|
export type CustomizableClasses = {
|
|
29
33
|
treeClass: string;
|
|
@@ -39,8 +43,14 @@ export type DragEnterCallback = (draggendNode: Node, oldParent: Node, newParent:
|
|
|
39
43
|
export type BeforeMovedCallback = (draggendNode: Node, oldParent: Node, newParent: Node, insertionType: string) => boolean;
|
|
40
44
|
export type ExpandedCallback = (node: Node) => Promise<Node[]>;
|
|
41
45
|
export type HelperConfig = {
|
|
42
|
-
separator
|
|
46
|
+
separator: string;
|
|
43
47
|
recursive?: boolean;
|
|
44
48
|
recalculateNodePath?: boolean;
|
|
45
49
|
checkboxes?: SelectionModes;
|
|
46
50
|
};
|
|
51
|
+
export declare enum HighlighType {
|
|
52
|
+
nest = "nest",
|
|
53
|
+
insertAbove = "insert-above",
|
|
54
|
+
insertBelow = "insert-below",
|
|
55
|
+
none = "none"
|
|
56
|
+
}
|
package/dist/types.js
CHANGED
|
@@ -10,3 +10,16 @@ export var SelectionModes;
|
|
|
10
10
|
SelectionModes["perNode"] = "perNode";
|
|
11
11
|
SelectionModes["none"] = "none";
|
|
12
12
|
})(SelectionModes || (SelectionModes = {}));
|
|
13
|
+
export var InsertionType;
|
|
14
|
+
(function (InsertionType) {
|
|
15
|
+
InsertionType["above"] = "above";
|
|
16
|
+
InsertionType["below"] = "below";
|
|
17
|
+
InsertionType["nest"] = "nest";
|
|
18
|
+
})(InsertionType || (InsertionType = {}));
|
|
19
|
+
export var HighlighType;
|
|
20
|
+
(function (HighlighType) {
|
|
21
|
+
HighlighType["nest"] = "nest";
|
|
22
|
+
HighlighType["insertAbove"] = "insert-above";
|
|
23
|
+
HighlighType["insertBelow"] = "insert-below";
|
|
24
|
+
HighlighType["none"] = "none";
|
|
25
|
+
})(HighlighType || (HighlighType = {}));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@keenmate/svelte-treeview",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.6",
|
|
4
4
|
"scripts": {
|
|
5
5
|
"dev": "vite dev",
|
|
6
6
|
"build": "vite build && npm run package",
|
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
"svelte-multiselect": "github:KeenMate/svelte-multiselect",
|
|
54
54
|
"svelte-preprocess": "^5.0.4",
|
|
55
55
|
"tslib": "^2.4.1",
|
|
56
|
-
"typescript": "^5.
|
|
56
|
+
"typescript": "^5.4.4",
|
|
57
57
|
"vite": "^4.3.6",
|
|
58
58
|
"vitest": "^1.4.0"
|
|
59
59
|
},
|