@keenmate/svelte-treeview 0.3.5 → 1.0.0-beta.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 CHANGED
@@ -1,13 +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
- - automatically expanding to given depth
8
- - customization of all object properties
9
- -
10
-
11
- ## Current state
12
-
13
- svelte-treeview is undergoing big upgrade righ now, so docs will be added later.
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
+ - automatically expanding to given depth
9
+ - customization of all object properties
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
13
+
14
+ ## Instalation
15
+
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
+ ```
@@ -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
  }
@@ -76,6 +70,8 @@ function highlighThisNode(node, highlitedNode, validTarget) {
76
70
  * @param canNest can nest
77
71
  */
78
72
  function highlightNesting(node, highlitedNode, validTarget, canNest) {
73
+ if (!highlitedNode)
74
+ return false;
79
75
  return (canNest &&
80
76
  highlighThisNode(node, highlitedNode, validTarget) &&
81
77
  helper.props.nestDisabled(node) !== true);
@@ -88,131 +84,133 @@ function highlightNesting(node, highlitedNode, validTarget, canNest) {
88
84
  * @param canNest can nest
89
85
  */
90
86
  function highlightInsert(node, highlitedNode, validTarget, canNest) {
87
+ if (!highlitedNode)
88
+ return false;
91
89
  return (!canNest &&
92
90
  highlighThisNode(node, highlitedNode, validTarget) &&
93
91
  helper.props.nestDisabled(node) !== true);
94
92
  }
95
93
  let liElements = {};
96
- </script>
97
-
98
- <ul
99
- class:show-lines={childDepth === 0 && verticalLines}
100
- class:child-menu={childDepth > 0}
101
- class={childDepth === 0 ? classes.treeClass : ''}
102
- >
103
- {#each getChildren(tree) as node (getNodeId(node))}
104
- {@const nesthighlighed = highlightNesting(node, highlightedNode, validTarget, canNest)}
105
- {@const insertHighlighted = highlightInsert(node, highlightedNode, validTarget, canNest)}
106
- {@const expanded = isExpanded(node, childDepth, expandTo)}
107
- {@const hasChildren = helper.props.hasChildren(node)}
108
- {@const draggable = !readonly && dragAndDrop && helper.props.isDraggable(node)}
109
- {@const isCurrentlyDragged =
110
- draggedPath == helper.path(node) ||
111
- (draggedPath && helper.path(node)?.startsWith(draggedPath))}
112
-
113
- <li
114
- class:is-child={helper.nodePathIsChild(helper.path(node))}
115
- class:has-children={hasChildren}
116
- on:contextmenu|stopPropagation={(e) => {
117
- dispatch('open-ctxmenu', { e: e, node: Node });
118
- }}
119
- on:drop|stopPropagation={(e) => handleDragDrop(e, node, liElements[getNodeId(node)])}
120
- on:dragover|stopPropagation={(e) => handleDragOver(e, node, liElements[getNodeId(node)])}
121
- on:dragenter|stopPropagation={(e) => handleDragEnter(e, node, liElements[getNodeId(node)])}
122
- on:dragleave|stopPropagation={(e) => handleDragLeave(e, node, liElements[getNodeId(node)])}
123
- bind:this={liElements[getNodeId(node)]}
124
- >
125
- {#if insPos == 1 && insertHighlighted}
126
- <div class="insert-line-wrapper">
127
- <div class="insert-line {classes.inserLineClass}" />
128
- </div>
129
- {/if}
130
-
131
- <!-- svelte-ignore a11y-no-static-element-interactions -->
132
- <div
133
- class="tree-item
134
- {nesthighlighed ? classes.expandClass : ''}
135
- {classes.nodeClass} {isCurrentlyDragged ? classes.currentlyDraggedClass : ''}"
136
- class:div-has-children={hasChildren}
137
- class:hover={insertHighlighted || nesthighlighed}
138
- {draggable}
139
- on:dragstart={(e) => handleDragStart(e, node)}
140
- on:dragend={(e) => handleDragEnd(e, node)}
141
- >
142
- {#if hasChildren}
143
- <!-- svelte-ignore a11y-click-events-have-key-events -->
144
- <span on:click={() => setExpansion(node, !expanded)}>
145
- <!-- use callback overrides expanded -->
146
- <i
147
- class="far {expanded ? classes.expandedToggleClass : classes.collapsedToggleClass}"
148
- class:fa-minus-square={expanded}
149
- class:fa-plus-square={!expanded || helper.props.useCallback(node)}
150
- />
151
- </span>
152
- {:else}
153
- <span />
154
- {/if}
155
-
156
- <Checkbox
157
- {checkboxes}
158
- {helper}
159
- {recursive}
160
- {node}
161
- {onlyLeafCheckboxes}
162
- {hideDisabledCheckboxes}
163
- {readonly}
164
- on:select={({ detail: { node } }) => selectionChanged(node)}
165
- />
166
- <span class:pointer-cursor={draggable}>
167
- <slot {node} />
168
- </span>
169
- </div>
170
-
171
- {#if nesthighlighed}
172
- <div class="insert-line-wrapper">
173
- <div
174
- class="insert-line insert-line-child {classes.inserLineClass} {classes.inserLineNestClass}"
175
- />
176
- </div>
177
- {/if}
178
- {#if expanded && hasChildren}
179
- <svelte:self
180
- branchRootNode={node}
181
- childDepth={childDepth + 1}
182
- {treeId}
183
- {checkboxes}
184
- {tree}
185
- {recursive}
186
- {helper}
187
- {classes}
188
- {readonly}
189
- {onlyLeafCheckboxes}
190
- {hideDisabledCheckboxes}
191
- {expandTo}
192
- {draggedPath}
193
- {dragAndDrop}
194
- {verticalLines}
195
- {canNest}
196
- {insPos}
197
- {validTarget}
198
- {highlightedNode}
199
- on:open-ctxmenu
200
- on:internal-expand
201
- on:internal-selectionChanged
202
- let:node={nodeNested}
203
- >
204
- <slot node={nodeNested} />
205
- </svelte:self>
206
- {/if}
207
- {#if !expanded && hasChildren}
208
- <ul class:child-menu={childDepth > 0} />
209
- {/if}
210
- <!-- Show line if insering -->
211
- {#if insPos == -1 && insertHighlighted}
212
- <div class="insert-line-wrapper">
213
- <div class="insert-line {classes.inserLineClass}" />
214
- </div>
215
- {/if}
216
- </li>
217
- {/each}
218
- </ul>
94
+ </script>
95
+
96
+ <ul
97
+ class:show-lines={childDepth === 0 && verticalLines}
98
+ class:child-menu={childDepth > 0}
99
+ class={childDepth === 0 ? classes.treeClass : ''}
100
+ >
101
+ {#each helper.getDirectChildren(tree, helper.path(branchRootNode)) as node (getNodeId(node))}
102
+ {@const nesthighlighed = highlightNesting(node, highlightedNode, validTarget, canNest)}
103
+ {@const insertHighlighted = highlightInsert(node, highlightedNode, validTarget, canNest)}
104
+ {@const expanded = isExpanded(node, childDepth, expandTo)}
105
+ {@const hasChildren = helper.props.hasChildren(node)}
106
+ {@const draggable = !readonly && dragAndDrop && helper.props.isDraggable(node)}
107
+ {@const isCurrentlyDragged =
108
+ draggedPath == helper.path(node) ||
109
+ (draggedPath && helper.path(node)?.startsWith(draggedPath))}
110
+
111
+ <li
112
+ class:is-child={helper.nodePathIsChild(helper.path(node))}
113
+ class:has-children={hasChildren}
114
+ on:contextmenu|stopPropagation={(e) => {
115
+ dispatch('open-ctxmenu', { e: e, node: Node });
116
+ }}
117
+ on:drop|stopPropagation={(e) => handleDragDrop(e, node, liElements[getNodeId(node)])}
118
+ on:dragover|stopPropagation={(e) => handleDragOver(e, node, liElements[getNodeId(node)])}
119
+ on:dragenter|stopPropagation={(e) => handleDragEnter(e, node, liElements[getNodeId(node)])}
120
+ on:dragleave|stopPropagation={(e) => handleDragLeave(e, node, liElements[getNodeId(node)])}
121
+ bind:this={liElements[getNodeId(node)]}
122
+ >
123
+ {#if insPos == InsertionType.above && insertHighlighted}
124
+ <div class="insert-line-wrapper">
125
+ <div class="insert-line {classes.inserLineClass}" />
126
+ </div>
127
+ {/if}
128
+
129
+ <!-- svelte-ignore a11y-no-static-element-interactions -->
130
+ <div
131
+ class="tree-item
132
+ {nesthighlighed ? classes.expandClass : ''}
133
+ {classes.nodeClass} {isCurrentlyDragged ? classes.currentlyDraggedClass : ''}"
134
+ class:div-has-children={hasChildren}
135
+ class:hover={insertHighlighted || nesthighlighed}
136
+ {draggable}
137
+ on:dragstart={(e) => handleDragStart(e, node)}
138
+ on:dragend={(e) => handleDragEnd(e, node)}
139
+ >
140
+ {#if hasChildren}
141
+ <!-- svelte-ignore a11y-click-events-have-key-events -->
142
+ <span on:click={() => setExpansion(node, !expanded)}>
143
+ <!-- use callback overrides expanded -->
144
+ <i
145
+ class="far {expanded ? classes.expandedToggleClass : classes.collapsedToggleClass}"
146
+ class:fa-minus-square={expanded}
147
+ class:fa-plus-square={!expanded || helper.props.useCallback(node)}
148
+ />
149
+ </span>
150
+ {:else}
151
+ <span />
152
+ {/if}
153
+
154
+ <Checkbox
155
+ {checkboxes}
156
+ {helper}
157
+ {recursive}
158
+ {node}
159
+ {onlyLeafCheckboxes}
160
+ {hideDisabledCheckboxes}
161
+ {readonly}
162
+ on:select={({ detail: { node } }) => selectionChanged(node)}
163
+ />
164
+ <span class:pointer-cursor={draggable}>
165
+ <slot {node} />
166
+ </span>
167
+ </div>
168
+
169
+ {#if nesthighlighed}
170
+ <div class="insert-line-wrapper">
171
+ <div
172
+ class="insert-line insert-line-child {classes.inserLineClass} {classes.inserLineNestClass}"
173
+ />
174
+ </div>
175
+ {/if}
176
+ {#if expanded && hasChildren}
177
+ <svelte:self
178
+ branchRootNode={node}
179
+ childDepth={childDepth + 1}
180
+ {treeId}
181
+ {checkboxes}
182
+ {tree}
183
+ {recursive}
184
+ {helper}
185
+ {classes}
186
+ {readonly}
187
+ {onlyLeafCheckboxes}
188
+ {hideDisabledCheckboxes}
189
+ {expandTo}
190
+ {draggedPath}
191
+ {dragAndDrop}
192
+ {verticalLines}
193
+ {canNest}
194
+ {insPos}
195
+ {validTarget}
196
+ {highlightedNode}
197
+ on:open-ctxmenu
198
+ on:internal-expand
199
+ on:internal-selectionChanged
200
+ let:node={nodeNested}
201
+ >
202
+ <slot node={nodeNested} />
203
+ </svelte:self>
204
+ {/if}
205
+ {#if !expanded && hasChildren}
206
+ <ul class:child-menu={childDepth > 0} />
207
+ {/if}
208
+ <!-- Show line if insering -->
209
+ {#if insPos === InsertionType.below && insertHighlighted}
210
+ <div class="insert-line-wrapper">
211
+ <div class="insert-line {classes.inserLineClass}" />
212
+ </div>
213
+ {/if}
214
+ </li>
215
+ {/each}
216
+ </ul>
@@ -1,5 +1,5 @@
1
1
  import { SvelteComponent } from "svelte";
2
- import { SelectionModes, type InsertionType, type Node } from './types.js';
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: {
@@ -16,7 +16,7 @@ declare const __propDef: {
16
16
  classes: CustomizableClasses;
17
17
  helper: TreeHelper;
18
18
  draggedPath: string | null;
19
- highlightedNode: Node;
19
+ highlightedNode: Node | null;
20
20
  childDepth: number;
21
21
  branchRootNode: Node | null;
22
22
  canNest: boolean;
@@ -1,5 +1,6 @@
1
1
  <script>import { createEventDispatcher } from 'svelte';
2
- import { SelectionModes } from './types.js';
2
+ import { SelectionModes, VisualState } 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,46 +17,48 @@ $: {
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 });
22
25
  }
23
- </script>
24
-
25
- {#if checkboxes == SelectionModes.perNode || checkboxes == SelectionModes.all}
26
- {#if helper.selection.isSelectable(node, checkboxes)}
27
- <!-- select node -->
28
- {#if !recursive || (recursive && !helper.props.hasChildren(node))}
29
- <input
30
- type="checkbox"
31
- on:change={() => onSelect(node)}
32
- checked={helper.props.selected(node)}
33
- disabled={readonly}
34
- />
35
- <!-- select children-->
36
- {:else if !onlyLeafCheckboxes}
37
- <!-- @ts-ingore -->
38
- <input
39
- type="checkbox"
40
- on:click={() => onSelect(node)}
41
- checked={helper.props.visualState(node) == 'true'}
42
- bind:indeterminate
43
- disabled={readonly}
44
- />
45
- {:else}
46
- <input
47
- type="checkbox"
48
- on:click|preventDefault={null}
49
- disabled={true}
50
- class:invisible={hideDisabledCheckboxes}
51
- />
52
- {/if}
53
- {:else}
54
- <input
55
- type="checkbox"
56
- on:click|preventDefault|stopPropagation
57
- disabled={true}
58
- class:invisible={hideDisabledCheckboxes}
59
- />
60
- {/if}
61
- {/if}
26
+ </script>
27
+
28
+ {#if checkboxes == SelectionModes.perNode || checkboxes == SelectionModes.all}
29
+ {#if selectionProvider.isSelectable(node, checkboxes)}
30
+ <!-- select node -->
31
+ {#if !recursive || (recursive && !helper.props.hasChildren(node))}
32
+ <input
33
+ type="checkbox"
34
+ on:change={() => onSelect(node)}
35
+ checked={helper.props.selected(node)}
36
+ disabled={readonly}
37
+ />
38
+ <!-- select children-->
39
+ {:else if !onlyLeafCheckboxes}
40
+ <!-- @ts-ingore -->
41
+ <input
42
+ type="checkbox"
43
+ on:click={() => onSelect(node)}
44
+ checked={helper.props.visualState(node) === VisualState.selected}
45
+ {indeterminate}
46
+ disabled={readonly}
47
+ />
48
+ {:else}
49
+ <input
50
+ type="checkbox"
51
+ on:click={null}
52
+ disabled={true}
53
+ class:invisible={hideDisabledCheckboxes}
54
+ />
55
+ {/if}
56
+ {:else}
57
+ <input
58
+ type="checkbox"
59
+ on:click|preventDefault|stopPropagation
60
+ disabled={true}
61
+ class:invisible={hideDisabledCheckboxes}
62
+ />
63
+ {/if}
64
+ {/if}