@keenmate/svelte-treeview 1.0.0-beta.2 → 1.0.0-beta.3

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,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
- - 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
+ # 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
+ ```
@@ -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 canNest;
22
- export let validTarget;
23
- export let insPos;
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-handleDragStart', { node: node, event: e, element: el });
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
- *check if this node is one being hovered over (highlited) and is valid target
61
- */
62
- function highlighThisNode(node, highlitedNode, validTarget) {
63
- return validTarget && highlitedNode.path == node.path;
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 nesthighlighed = highlightNesting(node, highlightedNode, validTarget, canNest)}
100
- {@const insertHighlighted = highlightInsert(node, highlightedNode, validTarget, canNest)}
101
- {@const expanded = isExpanded(node, childDepth, expandTo)}
102
- {@const hasChildren = node.hasChildren}
103
- {@const draggable = !readonly && dragAndDrop && node.isDraggable}
104
- {@const isCurrentlyDragged =
105
- draggedPath == node.path || (draggedPath && node.path?.startsWith(draggedPath))}
106
-
107
- <li
108
- class:is-child={helper.nodePathIsChild(node.path)}
109
- class:has-children={hasChildren}
110
- on:contextmenu|stopPropagation={(e) => {
111
- dispatch('open-ctxmenu', { e: e, node: Node });
112
- }}
113
- on:drop|stopPropagation={(e) => handleDragDrop(e, node, liElements[getNodeId(node)])}
114
- on:dragover|stopPropagation={(e) => handleDragOver(e, node, liElements[getNodeId(node)])}
115
- on:dragenter|stopPropagation={(e) => handleDragEnter(e, node, liElements[getNodeId(node)])}
116
- on:dragleave|stopPropagation={(e) => handleDragLeave(e, node, liElements[getNodeId(node)])}
117
- bind:this={liElements[getNodeId(node)]}
118
- >
119
- {#if insPos == InsertionType.above && insertHighlighted}
120
- <div class="insert-line-wrapper">
121
- <div class="insert-line {classes.inserLineClass}" />
122
- </div>
123
- {/if}
124
-
125
- <!-- svelte-ignore a11y-no-static-element-interactions -->
126
- <div
127
- class="tree-item
128
- {nesthighlighed ? classes.expandClass : ''}
129
- {classes.nodeClass} {isCurrentlyDragged ? classes.currentlyDraggedClass : ''}"
130
- class:div-has-children={hasChildren}
131
- class:hover={insertHighlighted || nesthighlighed}
132
- {draggable}
133
- on:dragstart={(e) => handleDragStart(e, node)}
134
- on:dragend={(e) => handleDragEnd(e, node)}
135
- >
136
- {#if hasChildren}
137
- <button
138
- class="expansion-button"
139
- on:click={() => setExpansion(node, !expanded)}
140
- type="button"
141
- >
142
- <i class="fa-fw {expanded ? classes.expandIcon : classes.collapseIcon}" />
143
- </button>
144
- {:else}
145
- <span class="fa-fw" />
146
- {/if}
147
-
148
- <Checkbox
149
- {checkboxes}
150
- {recursive}
151
- {node}
152
- {onlyLeafCheckboxes}
153
- {hideDisabledCheckboxes}
154
- {readonly}
155
- on:select={({ detail: { node } }) => selectionChanged(node)}
156
- />
157
- <span class:pointer-cursor={draggable}>
158
- <slot node={node.originalNode} />
159
- </span>
160
- </div>
161
-
162
- {#if nesthighlighed}
163
- <div class="insert-line-wrapper">
164
- <div
165
- class="insert-line insert-line-child {classes.inserLineClass} {classes.inserLineNestClass}"
166
- />
167
- </div>
168
- {/if}
169
- {#if expanded && hasChildren}
170
- <svelte:self
171
- branchRootNode={node}
172
- childDepth={childDepth + 1}
173
- {treeId}
174
- {checkboxes}
175
- {tree}
176
- {recursive}
177
- {helper}
178
- {classes}
179
- {readonly}
180
- {onlyLeafCheckboxes}
181
- {hideDisabledCheckboxes}
182
- {expandTo}
183
- {draggedPath}
184
- {dragAndDrop}
185
- {verticalLines}
186
- {canNest}
187
- {insPos}
188
- {validTarget}
189
- {highlightedNode}
190
- on:open-ctxmenu
191
- on:internal-expand
192
- on:internal-selectionChanged
193
- let:node={nodeNested}
194
- >
195
- <slot node={nodeNested} />
196
- </svelte:self>
197
- {/if}
198
- {#if !expanded && hasChildren}
199
- <ul class:child-menu={childDepth > 0} />
200
- {/if}
201
- <!-- Show line if insering -->
202
- {#if insPos === InsertionType.below && insertHighlighted}
203
- <div class="insert-line-wrapper">
204
- <div class="insert-line {classes.inserLineClass}" />
205
- </div>
206
- {/if}
207
- </li>
208
- {/each}
209
- </ul>
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: e, node: 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>
@@ -1,6 +1,6 @@
1
1
  import { SvelteComponent } from "svelte";
2
- import { SelectionModes, InsertionType, type Node } from './types.js';
3
- import type { CustomizableClasses, TreeHelper } from './index.js';
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
- canNest: boolean;
23
- validTarget: boolean;
24
- insPos: InsertionType;
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;
@@ -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}