@keenmate/svelte-treeview 1.2.12 → 2.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 +98 -98
- package/dist/Branch.svelte +152 -151
- package/dist/Branch.svelte.d.ts +2 -2
- package/dist/Checkbox.svelte +42 -42
- package/dist/Checkbox.svelte.d.ts +1 -1
- package/dist/TreeView.svelte +87 -84
- package/dist/TreeView.svelte.d.ts +2 -1
- package/dist/constants.d.ts +1 -1
- package/dist/constants.js +25 -25
- package/dist/helpers/tree-helper.d.ts +5 -7
- package/dist/helpers/tree-helper.js +44 -40
- package/dist/index.d.ts +6 -6
- package/dist/index.js +6 -6
- package/dist/menu/ContextMenu.svelte +11 -11
- package/dist/menu/ContextMenu.svelte.d.ts +1 -1
- package/dist/menu/Menu.svelte +57 -50
- package/dist/menu/MenuDivider.svelte +10 -10
- package/dist/menu/MenuOption.svelte +53 -49
- package/dist/menu/menu.js +3 -3
- package/dist/providers/drag-drop-provider.d.ts +2 -2
- package/dist/providers/drag-drop-provider.js +1 -1
- package/dist/providers/movement-provider.d.ts +2 -2
- package/dist/providers/movement-provider.js +8 -7
- package/dist/providers/selection-provider.d.ts +2 -2
- package/dist/providers/selection-provider.js +15 -13
- package/dist/tree-styles.sass +121 -110
- package/dist/types.d.ts +2 -0
- package/package.json +76 -76
package/README.md
CHANGED
|
@@ -1,98 +1,98 @@
|
|
|
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 when expanding
|
|
8
|
-
- choose what object properties to use to get necessary information (id, path, ...)
|
|
9
|
-
- enable checkboxes on whole tree or just per node
|
|
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
|
-
- keyboard navigation
|
|
15
|
-
|
|
16
|
-
## Instalation
|
|
17
|
-
|
|
18
|
-
install the package `@keenmate/svelte-treeview` using your favourite package manager.
|
|
19
|
-
|
|
20
|
-
> [!warning]
|
|
21
|
-
> **Font awesome is required for expand/collapse icons.**
|
|
22
|
-
> If you wish to not use FA, you need to change all icons in classes properties
|
|
23
|
-
|
|
24
|
-
## Minimal usage
|
|
25
|
-
|
|
26
|
-
Tree and treeId are only mandatory attributes.
|
|
27
|
-
Tree has to be list of nodes. Only mandatory property of node is nodePath.
|
|
28
|
-
You can specify which keys to use for what properties by setting **props**.
|
|
29
|
-
|
|
30
|
-
```svelte
|
|
31
|
-
<script lang="ts">
|
|
32
|
-
import { TreeView } from '$lib/index.js';
|
|
33
|
-
|
|
34
|
-
let tree = [
|
|
35
|
-
{ nodePath: 'animals', title: 'Animals', hasChildren: true },
|
|
36
|
-
//...
|
|
37
|
-
{ nodePath: 'animals.insects.butterflies', title: 'Butterflies' }
|
|
38
|
-
];
|
|
39
|
-
</script>
|
|
40
|
-
|
|
41
|
-
<TreeView {tree} treeId="my-tree" let:node>
|
|
42
|
-
{node.title}
|
|
43
|
-
</TreeView>
|
|
44
|
-
|
|
45
|
-
```
|
|
46
|
-
|
|
47
|
-
For more examples see `src/routes/`
|
|
48
|
-
|
|
49
|
-
> [!note]
|
|
50
|
-
> Both **id** and **path** is required for tree to work.
|
|
51
|
-
> By default tree uses nodePath property for both.
|
|
52
|
-
> So if you change propery fro path, you need to also change id property.
|
|
53
|
-
> You can change both using props attribute.
|
|
54
|
-
|
|
55
|
-
## Properties
|
|
56
|
-
|
|
57
|
-
| Name | Type | Default | Description |
|
|
58
|
-
| ---------------------- | ------------------------------------------------------------------ | ------- | ----------------------------------------------------------------------------------------------------- |
|
|
59
|
-
| treeId | string | | value used to generate ids of nodes |
|
|
60
|
-
| tree | array of nodes | | represents tree strucuture |
|
|
61
|
-
| value | array of selected nodeIds | [] | |
|
|
62
|
-
| verticalLines | bool | false | show vertical guide lines |
|
|
63
|
-
| readonly | bool | false | dont allow selection and drag and drop |
|
|
64
|
-
| separator | string | "." | |
|
|
65
|
-
| recursiveSelection | bool | false | changes behavior of selection, see [Selection](#selection) |
|
|
66
|
-
| selectionMode | SelectionModes | none | changes selection mode, see [Selection](#selection) |
|
|
67
|
-
| onlyLeafCheckboxes | bool | false | hides non leaf checkboxed, see [Selection](#selection) |
|
|
68
|
-
| hideDisabledCheckboxes | bool | false | hides checkboxes instead of disabling, see [Selection](#selection) |
|
|
69
|
-
| loadChildrenAsync | ExpandedCallback | null | function that is called when node is expanded, see [Async loading](#async-loading) |
|
|
70
|
-
| showContextMenu | bool | false | On right click dispaly context menu defined in `context-menu` slot, see [Context menu](#context-menu) |
|
|
71
|
-
| expansionThreshold | number | 0 | Expand all nodes when there is less than number provided |
|
|
72
|
-
| customClasses | Partial<CustomizableClasses> | {} | changes classes used on same elements, see [Custom classes](#custom-classes) |
|
|
73
|
-
| filter | (node: Node) => boolean or null | null | function that is used for fitlering. It is called on every node |
|
|
74
|
-
| dragAndDrop | bool | false | enables drag and drop, see [Drag and drop](#drag-and-drop) |
|
|
75
|
-
| dropDisabledCallback | (draggendNode: Node, targetNode: Node) => Promise<boolean> or null | null | function called when draging over new node, see [Drag and drop](#drag-and-drop) |
|
|
76
|
-
| useKeyboardNavigation | bool | false | enables keyboard navigation , see [Keyboard navigation](#keyboard-navigation) |
|
|
77
|
-
| logger | ((...data: any[]) => void) or null | null | function that acts as logger for tree, mostly used for debugging |
|
|
78
|
-
|
|
79
|
-
## Selection
|
|
80
|
-
|
|
81
|
-
## Async loading
|
|
82
|
-
|
|
83
|
-
## Context menu
|
|
84
|
-
|
|
85
|
-
## Custom classes
|
|
86
|
-
|
|
87
|
-
## Drag and drop
|
|
88
|
-
> [!NOTE]
|
|
89
|
-
> In memory drag and drop is not yet supported. Tree just dispatches `moved` event with dragged node(`node`), target node (`target`) and insertion type (`insertType`).
|
|
90
|
-
> In future, this package will export function, that will allow you to easily compute new tree on frontend.
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
## Keyboard navigation
|
|
94
|
-
|
|
95
|
-
Enable keyboard navigation by setting `useKeyboardNavigation` to true.
|
|
96
|
-
|
|
97
|
-
Use arrows to navigata tree. First you need to focus some node,
|
|
98
|
-
you can use `focusNode` to do that. Use Enter or Space to select checkbox.
|
|
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 when expanding
|
|
8
|
+
- choose what object properties to use to get necessary information (id, path, ...)
|
|
9
|
+
- enable checkboxes on whole tree or just per node
|
|
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
|
+
- keyboard navigation
|
|
15
|
+
|
|
16
|
+
## Instalation
|
|
17
|
+
|
|
18
|
+
install the package `@keenmate/svelte-treeview` using your favourite package manager.
|
|
19
|
+
|
|
20
|
+
> [!warning]
|
|
21
|
+
> **Font awesome is required for expand/collapse icons.**
|
|
22
|
+
> If you wish to not use FA, you need to change all icons in classes properties
|
|
23
|
+
|
|
24
|
+
## Minimal usage
|
|
25
|
+
|
|
26
|
+
Tree and treeId are only mandatory attributes.
|
|
27
|
+
Tree has to be list of nodes. Only mandatory property of node is nodePath.
|
|
28
|
+
You can specify which keys to use for what properties by setting **props**.
|
|
29
|
+
|
|
30
|
+
```svelte
|
|
31
|
+
<script lang="ts">
|
|
32
|
+
import { TreeView } from '$lib/index.js';
|
|
33
|
+
|
|
34
|
+
let tree = [
|
|
35
|
+
{ nodePath: 'animals', title: 'Animals', hasChildren: true },
|
|
36
|
+
//...
|
|
37
|
+
{ nodePath: 'animals.insects.butterflies', title: 'Butterflies' }
|
|
38
|
+
];
|
|
39
|
+
</script>
|
|
40
|
+
|
|
41
|
+
<TreeView {tree} treeId="my-tree" let:node>
|
|
42
|
+
{node.title}
|
|
43
|
+
</TreeView>
|
|
44
|
+
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
For more examples see `src/routes/`
|
|
48
|
+
|
|
49
|
+
> [!note]
|
|
50
|
+
> Both **id** and **path** is required for tree to work.
|
|
51
|
+
> By default tree uses nodePath property for both.
|
|
52
|
+
> So if you change propery fro path, you need to also change id property.
|
|
53
|
+
> You can change both using props attribute.
|
|
54
|
+
|
|
55
|
+
## Properties
|
|
56
|
+
|
|
57
|
+
| Name | Type | Default | Description |
|
|
58
|
+
| ---------------------- | ------------------------------------------------------------------ | ------- | ----------------------------------------------------------------------------------------------------- |
|
|
59
|
+
| treeId | string | | value used to generate ids of nodes |
|
|
60
|
+
| tree | array of nodes | | represents tree strucuture |
|
|
61
|
+
| value | array of selected nodeIds | [] | |
|
|
62
|
+
| verticalLines | bool | false | show vertical guide lines |
|
|
63
|
+
| readonly | bool | false | dont allow selection and drag and drop |
|
|
64
|
+
| separator | string | "." | |
|
|
65
|
+
| recursiveSelection | bool | false | changes behavior of selection, see [Selection](#selection) |
|
|
66
|
+
| selectionMode | SelectionModes | none | changes selection mode, see [Selection](#selection) |
|
|
67
|
+
| onlyLeafCheckboxes | bool | false | hides non leaf checkboxed, see [Selection](#selection) |
|
|
68
|
+
| hideDisabledCheckboxes | bool | false | hides checkboxes instead of disabling, see [Selection](#selection) |
|
|
69
|
+
| loadChildrenAsync | ExpandedCallback | null | function that is called when node is expanded, see [Async loading](#async-loading) |
|
|
70
|
+
| showContextMenu | bool | false | On right click dispaly context menu defined in `context-menu` slot, see [Context menu](#context-menu) |
|
|
71
|
+
| expansionThreshold | number | 0 | Expand all nodes when there is less than number provided |
|
|
72
|
+
| customClasses | Partial<CustomizableClasses> | {} | changes classes used on same elements, see [Custom classes](#custom-classes) |
|
|
73
|
+
| filter | (node: Node) => boolean or null | null | function that is used for fitlering. It is called on every node |
|
|
74
|
+
| dragAndDrop | bool | false | enables drag and drop, see [Drag and drop](#drag-and-drop) |
|
|
75
|
+
| dropDisabledCallback | (draggendNode: Node, targetNode: Node) => Promise<boolean> or null | null | function called when draging over new node, see [Drag and drop](#drag-and-drop) |
|
|
76
|
+
| useKeyboardNavigation | bool | false | enables keyboard navigation , see [Keyboard navigation](#keyboard-navigation) |
|
|
77
|
+
| logger | ((...data: any[]) => void) or null | null | function that acts as logger for tree, mostly used for debugging |
|
|
78
|
+
|
|
79
|
+
## Selection
|
|
80
|
+
|
|
81
|
+
## Async loading
|
|
82
|
+
|
|
83
|
+
## Context menu
|
|
84
|
+
|
|
85
|
+
## Custom classes
|
|
86
|
+
|
|
87
|
+
## Drag and drop
|
|
88
|
+
> [!NOTE]
|
|
89
|
+
> In memory drag and drop is not yet supported. Tree just dispatches `moved` event with dragged node(`node`), target node (`target`) and insertion type (`insertType`).
|
|
90
|
+
> In future, this package will export function, that will allow you to easily compute new tree on frontend.
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
## Keyboard navigation
|
|
94
|
+
|
|
95
|
+
Enable keyboard navigation by setting `useKeyboardNavigation` to true.
|
|
96
|
+
|
|
97
|
+
Use arrows to navigata tree. First you need to focus some node,
|
|
98
|
+
you can use `focusNode` to do that. Use Enter or Space to select checkbox.
|
package/dist/Branch.svelte
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
<script>import { createEventDispatcher } from
|
|
2
|
-
import Checkbox from
|
|
3
|
-
import {
|
|
4
|
-
import { capturedKeys } from
|
|
1
|
+
<script>import { createEventDispatcher } from "svelte";
|
|
2
|
+
import Checkbox from "./Checkbox.svelte";
|
|
3
|
+
import { InsertionType, SelectionModes } from "./types.js";
|
|
4
|
+
import { capturedKeys } from "./constants.js";
|
|
5
5
|
const dispatch = createEventDispatcher();
|
|
6
6
|
export let tree;
|
|
7
7
|
export let treeId;
|
|
@@ -29,7 +29,7 @@ $: if (focusedNode && liElements[getNodeId(focusedNode)]) {
|
|
|
29
29
|
liElements[getNodeId(focusedNode)].focus();
|
|
30
30
|
}
|
|
31
31
|
function setExpansion(node, changeTo) {
|
|
32
|
-
dispatch(
|
|
32
|
+
dispatch("internal-expand", { node: node, changeTo });
|
|
33
33
|
}
|
|
34
34
|
function isExpanded(node, depth, expandToDepth) {
|
|
35
35
|
const nodeExpanded = node.expanded;
|
|
@@ -41,26 +41,26 @@ function isExpanded(node, depth, expandToDepth) {
|
|
|
41
41
|
}
|
|
42
42
|
//checkboxes
|
|
43
43
|
function selectionChanged(node) {
|
|
44
|
-
dispatch(
|
|
44
|
+
dispatch("internal-selectionChanged", { node: node });
|
|
45
45
|
}
|
|
46
46
|
// drag and drop
|
|
47
47
|
function handleDragStart(e, node) {
|
|
48
|
-
dispatch(
|
|
48
|
+
dispatch("internal-handleDragStart", { node: node, e: e });
|
|
49
49
|
}
|
|
50
50
|
function handleDragDrop(e, node, el) {
|
|
51
|
-
dispatch(
|
|
51
|
+
dispatch("internal-handleDragDrop", { node: node, event: e, element: el });
|
|
52
52
|
}
|
|
53
53
|
function handleDragOver(e, node, el, nest) {
|
|
54
|
-
dispatch(
|
|
54
|
+
dispatch("internal-handleDragOver", { node: node, event: e, element: el, nest });
|
|
55
55
|
}
|
|
56
56
|
function handleDragEnter(e, node, el) {
|
|
57
|
-
dispatch(
|
|
57
|
+
dispatch("internal-handleDragEnter", { node: node, event: e, element: el });
|
|
58
58
|
}
|
|
59
59
|
function handleDragEnd(e, node) {
|
|
60
|
-
dispatch(
|
|
60
|
+
dispatch("internal-handleDragEnd", { node: node, event: e });
|
|
61
61
|
}
|
|
62
62
|
function handleDragLeave(e, node, el) {
|
|
63
|
-
dispatch(
|
|
63
|
+
dispatch("internal-handleDragLeave", { node: node, event: e, element: el });
|
|
64
64
|
}
|
|
65
65
|
function handleKeyPress(e, node) {
|
|
66
66
|
if (!capturedKeys.includes(e.key)) {
|
|
@@ -68,148 +68,149 @@ function handleKeyPress(e, node) {
|
|
|
68
68
|
}
|
|
69
69
|
e.preventDefault();
|
|
70
70
|
e.stopPropagation();
|
|
71
|
-
dispatch(
|
|
71
|
+
dispatch("internal-keypress", { event: e, node });
|
|
72
72
|
}
|
|
73
73
|
function getHighlighMode(node, highlightedNode, insertionType) {
|
|
74
74
|
// return InsertionType.insertAbove;
|
|
75
|
-
if (highlightedNode?.path !== node.path)
|
|
75
|
+
if (highlightedNode?.path !== node.path) {
|
|
76
76
|
return InsertionType.none;
|
|
77
|
+
}
|
|
77
78
|
return insertionType;
|
|
78
79
|
}
|
|
79
|
-
</script>
|
|
80
|
-
|
|
81
|
-
<ul
|
|
82
|
-
class:show-lines={childDepth === 0 && verticalLines}
|
|
83
|
-
class:child-menu={childDepth > 0}
|
|
84
|
-
class={childDepth === 0 ? classes.treeClass : ''}
|
|
85
|
-
>
|
|
86
|
-
<!-- TODO fix accessibility -->
|
|
87
|
-
<!-- svelte-ignore a11y-no-noninteractive-element-interactions -->
|
|
88
|
-
{#each directChildren as node (getNodeId(node))}
|
|
89
|
-
{@const expanded = isExpanded(node, childDepth, expandTo)}
|
|
90
|
-
{@const draggable = !readonly && dragAndDrop && !node.dragDisabled}
|
|
91
|
-
{@const isCurrentlyDragged = draggedNode && node.path.startsWith(draggedNode?.path)}
|
|
92
|
-
{@const effectiveHighlight = getHighlighMode(node, highlightedNode, insertionType)}
|
|
93
|
-
<!-- svelte-ignore a11y-no-noninteractive-tabindex -->
|
|
94
|
-
<li
|
|
95
|
-
class:is-child={helper.nodePathIsChild(node.path)}
|
|
96
|
-
class:has-children={node.hasChildren}
|
|
97
|
-
on:contextmenu|stopPropagation={(e) => {
|
|
98
|
-
dispatch('open-ctxmenu', { e, node });
|
|
99
|
-
}}
|
|
100
|
-
on:drop|stopPropagation={(e) => handleDragDrop(e, node, liElements[getNodeId(node)])}
|
|
101
|
-
on:dragover|stopPropagation={(e) =>
|
|
102
|
-
handleDragOver(e, node, liElements[getNodeId(node)], false)}
|
|
103
|
-
on:dragenter|stopPropagation={(e) => handleDragEnter(e, node, liElements[getNodeId(node)])}
|
|
104
|
-
on:dragleave|stopPropagation={(e) => handleDragLeave(e, node, liElements[getNodeId(node)])}
|
|
105
|
-
bind:this={liElements[getNodeId(node)]}
|
|
106
|
-
on:keydown={(e) => handleKeyPress(e, node)}
|
|
107
|
-
tabindex={allowKeyboardNavigation ? 1 : -1}
|
|
108
|
-
>
|
|
109
|
-
{#if effectiveHighlight == InsertionType.insertAbove}
|
|
110
|
-
<div class="insert-line-wrapper">
|
|
111
|
-
<div class="insert-line {classes.insertLineClass}" />
|
|
112
|
-
</div>
|
|
113
|
-
{/if}
|
|
114
|
-
|
|
115
|
-
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
|
116
|
-
<div
|
|
117
|
-
class="tree-item
|
|
118
|
-
{effectiveHighlight === InsertionType.nest ? classes.expandClass : ''}
|
|
119
|
-
{classes.nodeClass} {isCurrentlyDragged ? classes.currentlyDraggedClass : ''}"
|
|
120
|
-
class:div-has-children={node.hasChildren}
|
|
121
|
-
class:hover={effectiveHighlight !== InsertionType.none}
|
|
122
|
-
{draggable}
|
|
123
|
-
on:dragstart={(e) => handleDragStart(e, node)}
|
|
124
|
-
on:dragend={(e) => handleDragEnd(e, node)}
|
|
125
|
-
>
|
|
126
|
-
{#if node.hasChildren}
|
|
127
|
-
<button
|
|
128
|
-
class="expansion-button arrow"
|
|
129
|
-
on:click={() => setExpansion(node, !expanded)}
|
|
130
|
-
type="button"
|
|
131
|
-
tabindex="-1"
|
|
132
|
-
>
|
|
133
|
-
<i class="fixed-icon arrow {expanded ? classes.collapseIcon : classes.expandIcon}" />
|
|
134
|
-
</button>
|
|
135
|
-
{:else}
|
|
136
|
-
<span class="fixed-icon" />
|
|
137
|
-
{/if}
|
|
138
|
-
|
|
139
|
-
<Checkbox
|
|
140
|
-
{checkboxes}
|
|
141
|
-
{recursive}
|
|
142
|
-
{node}
|
|
143
|
-
{onlyLeafCheckboxes}
|
|
144
|
-
{hideDisabledCheckboxes}
|
|
145
|
-
{readonly}
|
|
146
|
-
on:select={({ detail: { node } }) => selectionChanged(node)}
|
|
147
|
-
/>
|
|
148
|
-
<span class:pointer-cursor={draggable}>
|
|
149
|
-
<slot node={node.originalNode} />
|
|
150
|
-
</span>
|
|
151
|
-
|
|
152
|
-
{#if dragAndDrop && node.nestAllowed}
|
|
153
|
-
<span
|
|
154
|
-
on:dragover|stopPropagation={(e) =>
|
|
155
|
-
handleDragOver(e, node, liElements[getNodeId(node)], true)}
|
|
156
|
-
>
|
|
157
|
-
<i class="fixed-icon {classes.nestIcon}" />
|
|
158
|
-
|
|
159
|
-
{#if effectiveHighlight === InsertionType.nest}
|
|
160
|
-
<slot name="nest-highlight" />
|
|
161
|
-
{/if}
|
|
162
|
-
</span>
|
|
163
|
-
{/if}
|
|
164
|
-
</div>
|
|
165
|
-
{#if expanded && node.hasChildren}
|
|
166
|
-
<svelte:self
|
|
167
|
-
branchRootNode={node}
|
|
168
|
-
childDepth={childDepth + 1}
|
|
169
|
-
{treeId}
|
|
170
|
-
{checkboxes}
|
|
171
|
-
{tree}
|
|
172
|
-
{recursive}
|
|
173
|
-
{helper}
|
|
174
|
-
{classes}
|
|
175
|
-
{readonly}
|
|
176
|
-
{onlyLeafCheckboxes}
|
|
177
|
-
{hideDisabledCheckboxes}
|
|
178
|
-
{expandTo}
|
|
179
|
-
{dragAndDrop}
|
|
180
|
-
{verticalLines}
|
|
181
|
-
{draggedNode}
|
|
182
|
-
{highlightedNode}
|
|
183
|
-
{insertionType}
|
|
184
|
-
{focusedNode}
|
|
185
|
-
{allowKeyboardNavigation}
|
|
186
|
-
on:open-ctxmenu
|
|
187
|
-
on:internal-expand
|
|
188
|
-
on:internal-selectionChanged
|
|
189
|
-
on:internal-handleDragStart
|
|
190
|
-
on:internal-handleDragDrop
|
|
191
|
-
on:internal-handleDragOver
|
|
192
|
-
on:internal-handleDragEnter
|
|
193
|
-
on:internal-handleDragEnd
|
|
194
|
-
on:internal-handleDragLeave
|
|
195
|
-
on:internal-keypress
|
|
196
|
-
let:node={nodeNested}
|
|
197
|
-
>
|
|
198
|
-
<slot node={nodeNested} />
|
|
199
|
-
<svelte:fragment slot="nest-highlight">
|
|
200
|
-
<slot name="nest-highlight" />
|
|
201
|
-
</svelte:fragment>
|
|
202
|
-
</svelte:self>
|
|
203
|
-
{/if}
|
|
204
|
-
{#if !expanded && node.hasChildren}
|
|
205
|
-
<ul class:child-menu={childDepth > 0} />
|
|
206
|
-
{/if}
|
|
207
|
-
<!-- Show line if insering -->
|
|
208
|
-
{#if effectiveHighlight === InsertionType.insertBelow}
|
|
209
|
-
<div class="insert-line-wrapper">
|
|
210
|
-
<div class="insert-line {classes.insertLineClass}" />
|
|
211
|
-
</div>
|
|
212
|
-
{/if}
|
|
213
|
-
</li>
|
|
214
|
-
{/each}
|
|
215
|
-
</ul>
|
|
80
|
+
</script>
|
|
81
|
+
|
|
82
|
+
<ul
|
|
83
|
+
class:show-lines={childDepth === 0 && verticalLines}
|
|
84
|
+
class:child-menu={childDepth > 0}
|
|
85
|
+
class={childDepth === 0 ? classes.treeClass : ''}
|
|
86
|
+
>
|
|
87
|
+
<!-- TODO fix accessibility -->
|
|
88
|
+
<!-- svelte-ignore a11y-no-noninteractive-element-interactions -->
|
|
89
|
+
{#each directChildren as node (getNodeId(node))}
|
|
90
|
+
{@const expanded = isExpanded(node, childDepth, expandTo)}
|
|
91
|
+
{@const draggable = !readonly && dragAndDrop && !node.dragDisabled}
|
|
92
|
+
{@const isCurrentlyDragged = draggedNode && node.path.startsWith(draggedNode?.path)}
|
|
93
|
+
{@const effectiveHighlight = getHighlighMode(node, highlightedNode, insertionType)}
|
|
94
|
+
<!-- svelte-ignore a11y-no-noninteractive-tabindex -->
|
|
95
|
+
<li
|
|
96
|
+
class:is-child={helper.nodePathIsChild(node.path)}
|
|
97
|
+
class:has-children={node.hasChildren}
|
|
98
|
+
on:contextmenu|stopPropagation={(e) => {
|
|
99
|
+
dispatch('open-ctxmenu', { e, node });
|
|
100
|
+
}}
|
|
101
|
+
on:drop|stopPropagation={(e) => handleDragDrop(e, node, liElements[getNodeId(node)])}
|
|
102
|
+
on:dragover|stopPropagation={(e) =>
|
|
103
|
+
handleDragOver(e, node, liElements[getNodeId(node)], false)}
|
|
104
|
+
on:dragenter|stopPropagation={(e) => handleDragEnter(e, node, liElements[getNodeId(node)])}
|
|
105
|
+
on:dragleave|stopPropagation={(e) => handleDragLeave(e, node, liElements[getNodeId(node)])}
|
|
106
|
+
bind:this={liElements[getNodeId(node)]}
|
|
107
|
+
on:keydown={(e) => handleKeyPress(e, node)}
|
|
108
|
+
tabindex={allowKeyboardNavigation ? 1 : -1}
|
|
109
|
+
>
|
|
110
|
+
{#if effectiveHighlight == InsertionType.insertAbove}
|
|
111
|
+
<div class="insert-line-wrapper">
|
|
112
|
+
<div class="insert-line {classes.insertLineClass}" />
|
|
113
|
+
</div>
|
|
114
|
+
{/if}
|
|
115
|
+
|
|
116
|
+
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
|
117
|
+
<div
|
|
118
|
+
class="tree-item
|
|
119
|
+
{effectiveHighlight === InsertionType.nest ? classes.expandClass : ''}
|
|
120
|
+
{classes.nodeClass} {isCurrentlyDragged ? classes.currentlyDraggedClass : ''}"
|
|
121
|
+
class:div-has-children={node.hasChildren}
|
|
122
|
+
class:hover={effectiveHighlight !== InsertionType.none}
|
|
123
|
+
{draggable}
|
|
124
|
+
on:dragstart={(e) => handleDragStart(e, node)}
|
|
125
|
+
on:dragend={(e) => handleDragEnd(e, node)}
|
|
126
|
+
>
|
|
127
|
+
{#if node.hasChildren}
|
|
128
|
+
<button
|
|
129
|
+
class="expansion-button arrow"
|
|
130
|
+
on:click={() => setExpansion(node, !expanded)}
|
|
131
|
+
type="button"
|
|
132
|
+
tabindex="-1"
|
|
133
|
+
>
|
|
134
|
+
<i class="fixed-icon arrow {expanded ? classes.collapseIcon : classes.expandIcon}" />
|
|
135
|
+
</button>
|
|
136
|
+
{:else}
|
|
137
|
+
<span class="fixed-icon" />
|
|
138
|
+
{/if}
|
|
139
|
+
|
|
140
|
+
<Checkbox
|
|
141
|
+
{checkboxes}
|
|
142
|
+
{recursive}
|
|
143
|
+
{node}
|
|
144
|
+
{onlyLeafCheckboxes}
|
|
145
|
+
{hideDisabledCheckboxes}
|
|
146
|
+
{readonly}
|
|
147
|
+
on:select={({ detail: { node } }) => selectionChanged(node)}
|
|
148
|
+
/>
|
|
149
|
+
<span class:pointer-cursor={draggable}>
|
|
150
|
+
<slot node={node.originalNode} />
|
|
151
|
+
</span>
|
|
152
|
+
|
|
153
|
+
{#if dragAndDrop && node.nestAllowed}
|
|
154
|
+
<span
|
|
155
|
+
on:dragover|stopPropagation={(e) =>
|
|
156
|
+
handleDragOver(e, node, liElements[getNodeId(node)], true)}
|
|
157
|
+
>
|
|
158
|
+
<i class="fixed-icon {classes.nestIcon}" />
|
|
159
|
+
|
|
160
|
+
{#if effectiveHighlight === InsertionType.nest}
|
|
161
|
+
<slot name="nest-highlight" />
|
|
162
|
+
{/if}
|
|
163
|
+
</span>
|
|
164
|
+
{/if}
|
|
165
|
+
</div>
|
|
166
|
+
{#if expanded && node.hasChildren}
|
|
167
|
+
<svelte:self
|
|
168
|
+
branchRootNode={node}
|
|
169
|
+
childDepth={childDepth + 1}
|
|
170
|
+
{treeId}
|
|
171
|
+
{checkboxes}
|
|
172
|
+
{tree}
|
|
173
|
+
{recursive}
|
|
174
|
+
{helper}
|
|
175
|
+
{classes}
|
|
176
|
+
{readonly}
|
|
177
|
+
{onlyLeafCheckboxes}
|
|
178
|
+
{hideDisabledCheckboxes}
|
|
179
|
+
{expandTo}
|
|
180
|
+
{dragAndDrop}
|
|
181
|
+
{verticalLines}
|
|
182
|
+
{draggedNode}
|
|
183
|
+
{highlightedNode}
|
|
184
|
+
{insertionType}
|
|
185
|
+
{focusedNode}
|
|
186
|
+
{allowKeyboardNavigation}
|
|
187
|
+
on:open-ctxmenu
|
|
188
|
+
on:internal-expand
|
|
189
|
+
on:internal-selectionChanged
|
|
190
|
+
on:internal-handleDragStart
|
|
191
|
+
on:internal-handleDragDrop
|
|
192
|
+
on:internal-handleDragOver
|
|
193
|
+
on:internal-handleDragEnter
|
|
194
|
+
on:internal-handleDragEnd
|
|
195
|
+
on:internal-handleDragLeave
|
|
196
|
+
on:internal-keypress
|
|
197
|
+
let:node={nodeNested}
|
|
198
|
+
>
|
|
199
|
+
<slot node={nodeNested} />
|
|
200
|
+
<svelte:fragment slot="nest-highlight">
|
|
201
|
+
<slot name="nest-highlight" />
|
|
202
|
+
</svelte:fragment>
|
|
203
|
+
</svelte:self>
|
|
204
|
+
{/if}
|
|
205
|
+
{#if !expanded && node.hasChildren}
|
|
206
|
+
<ul class:child-menu={childDepth > 0} />
|
|
207
|
+
{/if}
|
|
208
|
+
<!-- Show line if insering -->
|
|
209
|
+
{#if effectiveHighlight === InsertionType.insertBelow}
|
|
210
|
+
<div class="insert-line-wrapper">
|
|
211
|
+
<div class="insert-line {classes.insertLineClass}" />
|
|
212
|
+
</div>
|
|
213
|
+
{/if}
|
|
214
|
+
</li>
|
|
215
|
+
{/each}
|
|
216
|
+
</ul>
|
package/dist/Branch.svelte.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { SvelteComponent } from "svelte";
|
|
2
|
-
import {
|
|
3
|
-
import type { TreeHelper } from
|
|
2
|
+
import { type CustomizableClasses, InsertionType, type Node, SelectionModes } from "./types.js";
|
|
3
|
+
import type { TreeHelper } from "./helpers/tree-helper.js";
|
|
4
4
|
declare const __propDef: {
|
|
5
5
|
props: {
|
|
6
6
|
tree: Node[];
|
package/dist/Checkbox.svelte
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
<script>import { createEventDispatcher } from
|
|
2
|
-
import { SelectionModes, VisualState } from
|
|
3
|
-
import { isSelectable } from
|
|
1
|
+
<script>import { createEventDispatcher } from "svelte";
|
|
2
|
+
import { SelectionModes, VisualState } from "./types.js";
|
|
3
|
+
import { isSelectable } from "./providers/selection-provider.js";
|
|
4
4
|
export let checkboxes;
|
|
5
5
|
export let recursive;
|
|
6
6
|
export let node;
|
|
@@ -10,44 +10,44 @@ export let readonly = false;
|
|
|
10
10
|
const dispatch = createEventDispatcher();
|
|
11
11
|
function onSelect(e, node) {
|
|
12
12
|
// e.preventDefault();
|
|
13
|
-
dispatch(
|
|
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
|
-
tabindex="-1"
|
|
23
|
-
type="checkbox"
|
|
24
|
-
class="arrow"
|
|
25
|
-
on:click={(e) => onSelect(e, node)}
|
|
26
|
-
on:keypress={(e) => e.key === 'Enter' && onSelect(e, node)}
|
|
27
|
-
checked={node.visualState === VisualState.selected}
|
|
28
|
-
indeterminate={node.visualState === VisualState.indeterminate}
|
|
29
|
-
disabled={readonly}
|
|
30
|
-
/>
|
|
31
|
-
{:else}
|
|
32
|
-
<input
|
|
33
|
-
tabindex="-1"
|
|
34
|
-
class="arrow"
|
|
35
|
-
type="checkbox"
|
|
36
|
-
on:click={null}
|
|
37
|
-
disabled={true}
|
|
38
|
-
checked={node.visualState === VisualState.selected}
|
|
39
|
-
indeterminate={node.visualState === VisualState.indeterminate}
|
|
40
|
-
class:invisible={hideDisabledCheckboxes}
|
|
41
|
-
/>
|
|
42
|
-
{/if}
|
|
43
|
-
{:else}
|
|
44
|
-
<input
|
|
45
|
-
tabindex="-1"
|
|
46
|
-
class="arrow"
|
|
47
|
-
type="checkbox"
|
|
48
|
-
on:click|preventDefault|stopPropagation
|
|
49
|
-
disabled={true}
|
|
50
|
-
class:invisible={hideDisabledCheckboxes}
|
|
51
|
-
/>
|
|
52
|
-
{/if}
|
|
53
|
-
{/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
|
+
tabindex="-1"
|
|
23
|
+
type="checkbox"
|
|
24
|
+
class="arrow"
|
|
25
|
+
on:click={(e) => onSelect(e, node)}
|
|
26
|
+
on:keypress={(e) => e.key === 'Enter' && onSelect(e, node)}
|
|
27
|
+
checked={node.visualState === VisualState.selected}
|
|
28
|
+
indeterminate={node.visualState === VisualState.indeterminate}
|
|
29
|
+
disabled={readonly}
|
|
30
|
+
/>
|
|
31
|
+
{:else}
|
|
32
|
+
<input
|
|
33
|
+
tabindex="-1"
|
|
34
|
+
class="arrow"
|
|
35
|
+
type="checkbox"
|
|
36
|
+
on:click={null}
|
|
37
|
+
disabled={true}
|
|
38
|
+
checked={node.visualState === VisualState.selected}
|
|
39
|
+
indeterminate={node.visualState === VisualState.indeterminate}
|
|
40
|
+
class:invisible={hideDisabledCheckboxes}
|
|
41
|
+
/>
|
|
42
|
+
{/if}
|
|
43
|
+
{:else}
|
|
44
|
+
<input
|
|
45
|
+
tabindex="-1"
|
|
46
|
+
class="arrow"
|
|
47
|
+
type="checkbox"
|
|
48
|
+
on:click|preventDefault|stopPropagation
|
|
49
|
+
disabled={true}
|
|
50
|
+
class:invisible={hideDisabledCheckboxes}
|
|
51
|
+
/>
|
|
52
|
+
{/if}
|
|
53
|
+
{/if}
|