@keenmate/svelte-treeview 1.0.0-beta.2 → 1.0.0-beta.4
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 +38 -38
- package/dist/Branch.svelte +139 -156
- package/dist/Branch.svelte.d.ts +7 -7
- package/dist/Checkbox.svelte +30 -30
- package/dist/TreeView.svelte +127 -54
- package/dist/TreeView.svelte.d.ts +7 -6
- package/dist/constants.d.ts +1 -2
- package/dist/constants.js +3 -5
- package/dist/helpers/tree-helper.d.ts +9 -9
- package/dist/helpers/tree-helper.js +15 -9
- package/dist/menu/ContextMenu.svelte +10 -10
- package/dist/menu/Menu.svelte +50 -50
- package/dist/menu/MenuDivider.svelte +10 -10
- package/dist/menu/MenuOption.svelte +49 -49
- package/dist/menu/menu.js +2 -2
- package/dist/providers/drag-drop-provider.d.ts +9 -0
- package/dist/providers/drag-drop-provider.js +33 -186
- package/dist/providers/selection-provider.d.ts +2 -2
- package/dist/providers/selection-provider.js +1 -1
- package/dist/tree-styles.sass +105 -104
- package/dist/types.d.ts +10 -15
- package/dist/types.js +3 -9
- package/package.json +72 -71
package/README.md
CHANGED
|
@@ -1,38 +1,38 @@
|
|
|
1
|
-
# Svelte Treeview
|
|
2
|
-
|
|
3
|
-
The most elaborate treeview for svelte on earth (or even in our galaxy).
|
|
4
|
-
|
|
5
|
-
## Features
|
|
6
|
-
|
|
7
|
-
- load new nodes whne expanding
|
|
8
|
-
-
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
-
|
|
12
|
-
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
```svelte
|
|
24
|
-
<script lang="ts">
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
//...
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
</script>
|
|
33
|
-
|
|
34
|
-
<TreeView {tree} treeId="my-tree" let:node>
|
|
35
|
-
|
|
36
|
-
</TreeView>
|
|
37
|
-
|
|
38
|
-
```
|
|
1
|
+
# Svelte Treeview
|
|
2
|
+
|
|
3
|
+
The most elaborate treeview for svelte on earth (or even in our galaxy).
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- load new nodes whne expanding
|
|
8
|
+
- customization of all object properties
|
|
9
|
+
- checkboxes enabled on whole tree or based on property
|
|
10
|
+
- recursive seletion mode, where leafes can be selected
|
|
11
|
+
- build-in support for search
|
|
12
|
+
- drag and drop functionality controlable per node
|
|
13
|
+
- context menu
|
|
14
|
+
|
|
15
|
+
## Instalation
|
|
16
|
+
|
|
17
|
+
install the package `@keenmate/svelte-treeview` using your favourite package manager.
|
|
18
|
+
|
|
19
|
+
**Font awesome is required for expand/collapse icons.**
|
|
20
|
+
|
|
21
|
+
## Minimal usage
|
|
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
|
@@ -14,13 +14,11 @@ export let readonly;
|
|
|
14
14
|
export let expandTo;
|
|
15
15
|
export let classes;
|
|
16
16
|
export let helper;
|
|
17
|
-
export let draggedPath;
|
|
18
|
-
export let highlightedNode;
|
|
19
17
|
export let childDepth;
|
|
20
18
|
export let branchRootNode;
|
|
21
|
-
export let
|
|
22
|
-
export let
|
|
23
|
-
export let
|
|
19
|
+
export let draggedNode;
|
|
20
|
+
export let highlightedNode;
|
|
21
|
+
export let insertionType;
|
|
24
22
|
const getNodeId = (node) => `${treeId}-${node.path}`;
|
|
25
23
|
function setExpansion(node, changeTo) {
|
|
26
24
|
dispatch('internal-expand', { node: node, changeTo });
|
|
@@ -42,10 +40,10 @@ function handleDragStart(e, node) {
|
|
|
42
40
|
dispatch('internal-handleDragStart', { node: node, e: e });
|
|
43
41
|
}
|
|
44
42
|
function handleDragDrop(e, node, el) {
|
|
45
|
-
dispatch('internal-
|
|
43
|
+
dispatch('internal-handleDragDrop', { node: node, event: e, element: el });
|
|
46
44
|
}
|
|
47
|
-
function handleDragOver(e, node, el) {
|
|
48
|
-
dispatch('internal-handleDragOver', { node: node, event: e, element: el });
|
|
45
|
+
function handleDragOver(e, node, el, nest) {
|
|
46
|
+
dispatch('internal-handleDragOver', { node: node, event: e, element: el, nest });
|
|
49
47
|
}
|
|
50
48
|
function handleDragEnter(e, node, el) {
|
|
51
49
|
dispatch('internal-handleDragEnter', { node: node, event: e, element: el });
|
|
@@ -56,154 +54,139 @@ function handleDragEnd(e, node) {
|
|
|
56
54
|
function handleDragLeave(e, node, el) {
|
|
57
55
|
dispatch('internal-handleDragLeave', { node: node, event: e, element: el });
|
|
58
56
|
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
return
|
|
64
|
-
}
|
|
65
|
-
/**
|
|
66
|
-
* returns true, it should highlight nesting on this node
|
|
67
|
-
* @param node node
|
|
68
|
-
* @param highlitedNode highlited node
|
|
69
|
-
* @param validTarget valid target
|
|
70
|
-
* @param canNest can nest
|
|
71
|
-
*/
|
|
72
|
-
function highlightNesting(node, highlitedNode, validTarget, canNest) {
|
|
73
|
-
if (!highlitedNode)
|
|
74
|
-
return false;
|
|
75
|
-
return (canNest && highlighThisNode(node, highlitedNode, validTarget) && node.nestDisabled !== true);
|
|
76
|
-
}
|
|
77
|
-
/**
|
|
78
|
-
* returns true, it should highlight nesting on this node
|
|
79
|
-
* @param node node
|
|
80
|
-
* @param highlitedNode highlited node
|
|
81
|
-
* @param validTarget valid target
|
|
82
|
-
* @param canNest can nest
|
|
83
|
-
*/
|
|
84
|
-
function highlightInsert(node, highlitedNode, validTarget, canNest) {
|
|
85
|
-
if (!highlitedNode)
|
|
86
|
-
return false;
|
|
87
|
-
return (!canNest && highlighThisNode(node, highlitedNode, validTarget) && node.nestDisabled !== true);
|
|
57
|
+
function getHighlighMode(node, highlightedNode, insertionType) {
|
|
58
|
+
// return InsertionType.insertAbove;
|
|
59
|
+
if (highlightedNode?.path !== node.path)
|
|
60
|
+
return InsertionType.none;
|
|
61
|
+
return insertionType;
|
|
88
62
|
}
|
|
89
63
|
// TODO maybe this can be removed?
|
|
90
64
|
let liElements = {};
|
|
91
|
-
</script>
|
|
92
|
-
|
|
93
|
-
<ul
|
|
94
|
-
class:show-lines={childDepth === 0 && verticalLines}
|
|
95
|
-
class:child-menu={childDepth > 0}
|
|
96
|
-
class={childDepth === 0 ? classes.treeClass : ''}
|
|
97
|
-
>
|
|
98
|
-
{#each helper.getDirectChildren(tree, branchRootNode?.path ?? null) as node (getNodeId(node))}
|
|
99
|
-
{@const
|
|
100
|
-
{@const
|
|
101
|
-
{@const
|
|
102
|
-
{@const
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
on:
|
|
111
|
-
|
|
112
|
-
}
|
|
113
|
-
on:
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
class=
|
|
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
|
-
on:open-ctxmenu
|
|
191
|
-
on:internal-expand
|
|
192
|
-
on:internal-selectionChanged
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
</
|
|
206
|
-
{/if}
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
65
|
+
</script>
|
|
66
|
+
|
|
67
|
+
<ul
|
|
68
|
+
class:show-lines={childDepth === 0 && verticalLines}
|
|
69
|
+
class:child-menu={childDepth > 0}
|
|
70
|
+
class={childDepth === 0 ? classes.treeClass : ''}
|
|
71
|
+
>
|
|
72
|
+
{#each helper.getDirectChildren(tree, branchRootNode?.path ?? null) as node (getNodeId(node))}
|
|
73
|
+
{@const expanded = isExpanded(node, childDepth, expandTo)}
|
|
74
|
+
{@const draggable = !readonly && dragAndDrop && !node.dragDisabled}
|
|
75
|
+
{@const isCurrentlyDragged = draggedNode && node.path.startsWith(draggedNode?.path)}
|
|
76
|
+
{@const effectiveHighlight = getHighlighMode(node, highlightedNode, insertionType)}
|
|
77
|
+
<li
|
|
78
|
+
class:is-child={helper.nodePathIsChild(node.path)}
|
|
79
|
+
class:has-children={node.hasChildren}
|
|
80
|
+
on:contextmenu|stopPropagation={(e) => {
|
|
81
|
+
dispatch('open-ctxmenu', { e, node });
|
|
82
|
+
}}
|
|
83
|
+
on:drop|stopPropagation={(e) => handleDragDrop(e, node, liElements[getNodeId(node)])}
|
|
84
|
+
on:dragover|stopPropagation={(e) =>
|
|
85
|
+
handleDragOver(e, node, liElements[getNodeId(node)], false)}
|
|
86
|
+
on:dragenter|stopPropagation={(e) => handleDragEnter(e, node, liElements[getNodeId(node)])}
|
|
87
|
+
on:dragleave|stopPropagation={(e) => handleDragLeave(e, node, liElements[getNodeId(node)])}
|
|
88
|
+
bind:this={liElements[getNodeId(node)]}
|
|
89
|
+
>
|
|
90
|
+
{#if effectiveHighlight == InsertionType.insertAbove}
|
|
91
|
+
<div class="insert-line-wrapper">
|
|
92
|
+
<div class="insert-line {classes.inserLineClass}" />
|
|
93
|
+
</div>
|
|
94
|
+
{/if}
|
|
95
|
+
|
|
96
|
+
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
|
97
|
+
<div
|
|
98
|
+
class="tree-item
|
|
99
|
+
{effectiveHighlight === InsertionType.nest ? classes.expandClass : ''}
|
|
100
|
+
{classes.nodeClass} {isCurrentlyDragged ? classes.currentlyDraggedClass : ''}"
|
|
101
|
+
class:div-has-children={node.hasChildren}
|
|
102
|
+
class:hover={effectiveHighlight !== InsertionType.none}
|
|
103
|
+
{draggable}
|
|
104
|
+
on:dragstart={(e) => handleDragStart(e, node)}
|
|
105
|
+
on:dragend={(e) => handleDragEnd(e, node)}
|
|
106
|
+
>
|
|
107
|
+
{#if node.hasChildren}
|
|
108
|
+
<button
|
|
109
|
+
class="expansion-button"
|
|
110
|
+
on:click={() => setExpansion(node, !expanded)}
|
|
111
|
+
type="button"
|
|
112
|
+
>
|
|
113
|
+
<i class="fa-fw {expanded ? classes.collapseIcon : classes.expandIcon}" />
|
|
114
|
+
</button>
|
|
115
|
+
{:else}
|
|
116
|
+
<span class="fa-fw" />
|
|
117
|
+
{/if}
|
|
118
|
+
|
|
119
|
+
<Checkbox
|
|
120
|
+
{checkboxes}
|
|
121
|
+
{recursive}
|
|
122
|
+
{node}
|
|
123
|
+
{onlyLeafCheckboxes}
|
|
124
|
+
{hideDisabledCheckboxes}
|
|
125
|
+
{readonly}
|
|
126
|
+
on:select={({ detail: { node } }) => selectionChanged(node)}
|
|
127
|
+
/>
|
|
128
|
+
<span class:pointer-cursor={draggable}>
|
|
129
|
+
<slot node={node.originalNode} />
|
|
130
|
+
</span>
|
|
131
|
+
|
|
132
|
+
{#if dragAndDrop && node.nestAllowed}
|
|
133
|
+
<span
|
|
134
|
+
on:dragover|stopPropagation={(e) =>
|
|
135
|
+
handleDragOver(e, node, liElements[getNodeId(node)], true)}
|
|
136
|
+
>
|
|
137
|
+
<i class="fa-fw {classes.nestIcon}" />
|
|
138
|
+
|
|
139
|
+
{#if effectiveHighlight === InsertionType.nest}
|
|
140
|
+
<slot name="nest-highlight" />
|
|
141
|
+
{/if}
|
|
142
|
+
</span>
|
|
143
|
+
{/if}
|
|
144
|
+
</div>
|
|
145
|
+
{#if expanded && node.hasChildren}
|
|
146
|
+
<svelte:self
|
|
147
|
+
branchRootNode={node}
|
|
148
|
+
childDepth={childDepth + 1}
|
|
149
|
+
{treeId}
|
|
150
|
+
{checkboxes}
|
|
151
|
+
{tree}
|
|
152
|
+
{recursive}
|
|
153
|
+
{helper}
|
|
154
|
+
{classes}
|
|
155
|
+
{readonly}
|
|
156
|
+
{onlyLeafCheckboxes}
|
|
157
|
+
{hideDisabledCheckboxes}
|
|
158
|
+
{expandTo}
|
|
159
|
+
{dragAndDrop}
|
|
160
|
+
{verticalLines}
|
|
161
|
+
{draggedNode}
|
|
162
|
+
{highlightedNode}
|
|
163
|
+
{insertionType}
|
|
164
|
+
on:open-ctxmenu
|
|
165
|
+
on:internal-expand
|
|
166
|
+
on:internal-selectionChanged
|
|
167
|
+
on:internal-handleDragStart
|
|
168
|
+
on:internal-handleDragDrop
|
|
169
|
+
on:internal-handleDragOver
|
|
170
|
+
on:internal-handleDragEnter
|
|
171
|
+
on:internal-handleDragEnd
|
|
172
|
+
on:internal-handleDragLeave
|
|
173
|
+
let:node={nodeNested}
|
|
174
|
+
>
|
|
175
|
+
<slot node={nodeNested} />
|
|
176
|
+
<svelte:fragment slot="nest-highlight">
|
|
177
|
+
<slot name="nest-highlight" />
|
|
178
|
+
</svelte:fragment>
|
|
179
|
+
</svelte:self>
|
|
180
|
+
{/if}
|
|
181
|
+
{#if !expanded && node.hasChildren}
|
|
182
|
+
<ul class:child-menu={childDepth > 0} />
|
|
183
|
+
{/if}
|
|
184
|
+
<!-- Show line if insering -->
|
|
185
|
+
{#if effectiveHighlight === InsertionType.insertBelow}
|
|
186
|
+
<div class="insert-line-wrapper">
|
|
187
|
+
<div class="insert-line {classes.inserLineClass}" />
|
|
188
|
+
</div>
|
|
189
|
+
{/if}
|
|
190
|
+
</li>
|
|
191
|
+
{/each}
|
|
192
|
+
</ul>
|
package/dist/Branch.svelte.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { SvelteComponent } from "svelte";
|
|
2
|
-
import { SelectionModes, InsertionType, type Node } from './types.js';
|
|
3
|
-
import type {
|
|
2
|
+
import { SelectionModes, InsertionType, type Node, type CustomizableClasses } from './types.js';
|
|
3
|
+
import type { TreeHelper } from './helpers/tree-helper.js';
|
|
4
4
|
declare const __propDef: {
|
|
5
5
|
props: {
|
|
6
6
|
tree: Node[];
|
|
@@ -15,19 +15,18 @@ declare const __propDef: {
|
|
|
15
15
|
expandTo: number;
|
|
16
16
|
classes: CustomizableClasses;
|
|
17
17
|
helper: TreeHelper;
|
|
18
|
-
draggedPath: string | null;
|
|
19
|
-
highlightedNode: Node | null;
|
|
20
18
|
childDepth: number;
|
|
21
19
|
branchRootNode: Node | null;
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
20
|
+
draggedNode: Node | null;
|
|
21
|
+
highlightedNode: Node | null;
|
|
22
|
+
insertionType: InsertionType;
|
|
25
23
|
};
|
|
26
24
|
events: {
|
|
27
25
|
'open-ctxmenu': CustomEvent<any>;
|
|
28
26
|
'internal-expand': CustomEvent<any>;
|
|
29
27
|
'internal-selectionChanged': CustomEvent<any>;
|
|
30
28
|
'internal-handleDragStart': CustomEvent<any>;
|
|
29
|
+
'internal-handleDragDrop': CustomEvent<any>;
|
|
31
30
|
'internal-handleDragOver': CustomEvent<any>;
|
|
32
31
|
'internal-handleDragEnter': CustomEvent<any>;
|
|
33
32
|
'internal-handleDragEnd': CustomEvent<any>;
|
|
@@ -39,6 +38,7 @@ declare const __propDef: {
|
|
|
39
38
|
default: {
|
|
40
39
|
node: any;
|
|
41
40
|
};
|
|
41
|
+
'nest-highlight': {};
|
|
42
42
|
};
|
|
43
43
|
};
|
|
44
44
|
export type BranchProps = typeof __propDef.props;
|
package/dist/Checkbox.svelte
CHANGED
|
@@ -13,33 +13,33 @@ function onSelect(e, node) {
|
|
|
13
13
|
dispatch('select', { node });
|
|
14
14
|
return false;
|
|
15
15
|
}
|
|
16
|
-
</script>
|
|
17
|
-
|
|
18
|
-
{#if checkboxes == SelectionModes.perNode || checkboxes == SelectionModes.all}
|
|
19
|
-
{#if isSelectable(node, checkboxes)}
|
|
20
|
-
{#if !recursive || (recursive && !node.hasChildren) || !onlyLeafCheckboxes}
|
|
21
|
-
<input
|
|
22
|
-
type="checkbox"
|
|
23
|
-
on:click={(e) => onSelect(e, node)}
|
|
24
|
-
on:keypress={(e) => e.key === 'Enter' && onSelect(e, node)}
|
|
25
|
-
checked={node.visualState === VisualState.selected}
|
|
26
|
-
indeterminate={node.visualState === VisualState.indeterminate}
|
|
27
|
-
disabled={readonly}
|
|
28
|
-
/>
|
|
29
|
-
{:else}
|
|
30
|
-
<input
|
|
31
|
-
type="checkbox"
|
|
32
|
-
on:click={null}
|
|
33
|
-
disabled={true}
|
|
34
|
-
class:invisible={hideDisabledCheckboxes}
|
|
35
|
-
/>
|
|
36
|
-
{/if}
|
|
37
|
-
{:else}
|
|
38
|
-
<input
|
|
39
|
-
type="checkbox"
|
|
40
|
-
on:click|preventDefault|stopPropagation
|
|
41
|
-
disabled={true}
|
|
42
|
-
class:invisible={hideDisabledCheckboxes}
|
|
43
|
-
/>
|
|
44
|
-
{/if}
|
|
45
|
-
{/if}
|
|
16
|
+
</script>
|
|
17
|
+
|
|
18
|
+
{#if checkboxes == SelectionModes.perNode || checkboxes == SelectionModes.all}
|
|
19
|
+
{#if isSelectable(node, checkboxes)}
|
|
20
|
+
{#if !recursive || (recursive && !node.hasChildren) || !onlyLeafCheckboxes}
|
|
21
|
+
<input
|
|
22
|
+
type="checkbox"
|
|
23
|
+
on:click={(e) => onSelect(e, node)}
|
|
24
|
+
on:keypress={(e) => e.key === 'Enter' && onSelect(e, node)}
|
|
25
|
+
checked={node.visualState === VisualState.selected}
|
|
26
|
+
indeterminate={node.visualState === VisualState.indeterminate}
|
|
27
|
+
disabled={readonly}
|
|
28
|
+
/>
|
|
29
|
+
{:else}
|
|
30
|
+
<input
|
|
31
|
+
type="checkbox"
|
|
32
|
+
on:click={null}
|
|
33
|
+
disabled={true}
|
|
34
|
+
class:invisible={hideDisabledCheckboxes}
|
|
35
|
+
/>
|
|
36
|
+
{/if}
|
|
37
|
+
{:else}
|
|
38
|
+
<input
|
|
39
|
+
type="checkbox"
|
|
40
|
+
on:click|preventDefault|stopPropagation
|
|
41
|
+
disabled={true}
|
|
42
|
+
class:invisible={hideDisabledCheckboxes}
|
|
43
|
+
/>
|
|
44
|
+
{/if}
|
|
45
|
+
{/if}
|