@neovici/cosmoz-treenode-navigator 5.0.1 → 5.1.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.
@@ -12,22 +12,17 @@ jobs:
12
12
  runs-on: ubuntu-latest
13
13
  steps:
14
14
  - name: Checkout
15
- uses: actions/checkout@v1
16
-
17
- - name: Use Node.js 14.x
18
- uses: actions/setup-node@v1
15
+ uses: actions/checkout@v2
19
16
  with:
20
- node-version: 14.x
17
+ fetch-depth: 0
21
18
 
22
- - name: Restore npm cache
23
- id: node-cache
24
- uses: actions/cache@v2
19
+ - name: Use Node.js
20
+ uses: actions/setup-node@v2
25
21
  with:
26
- path: node_modules
27
- key: ${{ runner.os }}-node-0-${{ hashFiles('**/package-lock.json') }}
22
+ node-version: 16.x
23
+ cache: 'npm'
28
24
 
29
25
  - name: Install
30
- if: steps.node-cache.outputs.cache-hit != 'true'
31
26
  run: npm ci
32
27
 
33
28
  - name: Lint commit messages
package/CHANGELOG.md CHANGED
@@ -1,3 +1,17 @@
1
+ # [5.1.0](https://github.com/neovici/cosmoz-treenode-navigator/compare/v5.0.1...v5.1.0) (2022-07-28)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * **ci:** worflow typo ([17b92aa](https://github.com/neovici/cosmoz-treenode-navigator/commit/17b92aa332f0411b6548729002fd9cf62a99d223))
7
+
8
+
9
+ ### Features
10
+
11
+ * **cosmoz-treenode-navigator:** add keyboard navigation ([26b6d42](https://github.com/neovici/cosmoz-treenode-navigator/commit/26b6d42d99737d0b48a7fbc21a8677117b1d8a9e))
12
+ * refactor with lit-virtualizer instead of listbox ([7b69ab4](https://github.com/neovici/cosmoz-treenode-navigator/commit/7b69ab4e0e049c9c7f8e03c3b781f02fab83eb18))
13
+ * support `Right` key ([28a4df6](https://github.com/neovici/cosmoz-treenode-navigator/commit/28a4df6d35bc62ac49aba81135891ce8fcacfa05))
14
+
1
15
  ## [5.0.1](https://github.com/neovici/cosmoz-treenode-navigator/compare/v5.0.0...v5.0.1) (2022-07-27)
2
16
 
3
17
 
@@ -1,25 +1,23 @@
1
1
  import { component, useEffect, useState, useMemo, useCallback } from 'haunted';
2
2
  import { html } from 'lit-html';
3
3
  import { when } from 'lit-html/directives/when.js';
4
-
4
+ import { virtualize } from '@lit-labs/virtualizer/virtualize.js';
5
5
  import '@neovici/cosmoz-input';
6
- import '@neovici/cosmoz-autocomplete';
6
+ import { useMeta } from '@neovici/cosmoz-utils/hooks/use-meta';
7
7
  import { notifyProperty } from '@neovici/cosmoz-utils/hooks/use-notify-property';
8
8
 
9
9
  import {
10
10
  computeDataPlane,
11
11
  computeRowClass,
12
12
  computeSearching,
13
- getNodeName,
14
- hasChildren,
15
- nodeStyles,
16
13
  normalizeNodes,
17
14
  onNodeDblClicked,
18
15
  renderLevel,
19
- showGlobalSearchBtn
16
+ showGlobalSearchBtn,
20
17
  } from './helpers';
18
+ import styles from './cosmoz-treenode-navigator.styles';
21
19
 
22
- const TreenodeNavigator = host => {
20
+ const TreenodeNavigator = (host) => {
23
21
  const {
24
22
  /**
25
23
  * The main node structure
@@ -38,7 +36,7 @@ const TreenodeNavigator = host => {
38
36
  * Minimum length of searchValue to trigger a search
39
37
  */
40
38
  searchMinLength,
41
- nodePath
39
+ nodePath,
42
40
  } = host,
43
41
  /**
44
42
  * The path of the opened node
@@ -47,7 +45,16 @@ const TreenodeNavigator = host => {
47
45
  /**
48
46
  * The nodes on the path of the opened node
49
47
  */
50
- [nodesOnOpenNodePath, setNodesOnOpenNodePath] = useState([]),
48
+ nodesOnOpenNodePath = useMemo(
49
+ () =>
50
+ openNodePath
51
+ ? normalizeNodes(
52
+ tree,
53
+ tree.getPathNodes(openNodePath).filter((item) => item)
54
+ )
55
+ : [],
56
+ [tree, openNodePath]
57
+ ),
51
58
  /**
52
59
  * The highlighted (focused) node
53
60
  * This is the node which is currently selected in the list
@@ -57,168 +64,172 @@ const TreenodeNavigator = host => {
57
64
  * The search string
58
65
  */
59
66
  [searchValue, setSearchValue] = useState(''),
67
+ isSearching = useMemo(
68
+ () => computeSearching(searchValue, searchMinLength),
69
+ [searchValue, searchMinLength]
70
+ ),
71
+ renderedLevel = useMemo(
72
+ () => renderLevel(openNodePath, tree),
73
+ [openNodePath, tree]
74
+ ),
60
75
  /**
61
76
  * The currently displayed node list
62
77
  */
63
- dataPlane = useMemo(() => {
64
- return computeDataPlane(
65
- computeSearching(searchValue, searchMinLength),
66
- searchValue,
67
- renderLevel(openNodePath, tree),
68
- tree
69
- );
70
- }, [tree, openNodePath, highlightedNode, searchValue]),
78
+ dataPlane = useMemo(
79
+ () => computeDataPlane(isSearching, searchValue, renderedLevel, tree),
80
+ [isSearching, searchValue, renderedLevel, tree]
81
+ ),
71
82
  /**
72
83
  * Opens a node (renderLevel) based on a given path
73
84
  * @param {object} clickedNode - The clicked node
74
85
  * @return {undefined}
75
86
  */
76
- openNode = useCallback(clickedNode => {
77
- if (hasChildren(clickedNode)) {
78
- setOpenNodePath(clickedNode.path);
79
- setSearchValue('');
80
- }
81
- if (!clickedNode) {
82
- setOpenNodePath('');
83
- setSearchValue('');
84
- setHighlightedNode(null);
85
- }
86
- }, []),
87
+ openNode = useCallback(
88
+ (clickedNode) => {
89
+ if (tree.hasChildren(clickedNode)) {
90
+ setOpenNodePath(clickedNode.path);
91
+ setSearchValue('');
92
+ }
93
+ if (!clickedNode) {
94
+ setOpenNodePath('');
95
+ setSearchValue('');
96
+ setHighlightedNode(null);
97
+ }
98
+ },
99
+ [tree]
100
+ ),
87
101
  /**
88
102
  * Returns true, if the path of a node should be visible in the view
89
103
  * @param {Number} index - The node's current index in the list
90
104
  * @param {Object} parentSectionName - The nodes current parent section name
91
105
  * @return {Boolean} - If the path should be visible
92
106
  */
93
- renderSection = (index, parentSectionName) => {
94
- const _dataPlane = computeDataPlane(
95
- computeSearching(searchValue, searchMinLength),
96
- searchValue,
97
- renderLevel(openNodePath, tree),
98
- tree
99
- );
100
- if (
101
- !computeSearching(searchValue, searchMinLength) ||
102
- index == null ||
103
- _dataPlane == null ||
104
- index >= _dataPlane.length ||
105
- parentSectionName == null
106
- ) {
107
- return false;
108
- }
109
- if (index === 0) {
110
- return true;
111
- }
112
- const prevItem = _dataPlane[index - 1];
113
- return prevItem.parentSectionName !== parentSectionName;
114
- };
115
-
116
- useEffect(() => {
117
- if (!openNodePath) {
118
- notifyProperty(host, 'highlightedNodePath', '');
119
- notifyProperty(host, 'nodesOnNodePath', []);
120
- return;
121
- }
122
- setNodesOnOpenNodePath(
123
- normalizeNodes(tree.getPathNodes(openNodePath).filter(item => item))
107
+ renderSection = useCallback(
108
+ (index, parentSectionName) => {
109
+ if (
110
+ !isSearching ||
111
+ index == null ||
112
+ dataPlane == null ||
113
+ index >= dataPlane.length ||
114
+ parentSectionName == null
115
+ ) {
116
+ return false;
117
+ }
118
+ if (index === 0) {
119
+ return true;
120
+ }
121
+ const prevItem = dataPlane[index - 1];
122
+ return prevItem.parentSectionName !== parentSectionName;
123
+ },
124
+ [isSearching, dataPlane]
124
125
  );
125
- }, [openNodePath]);
126
126
 
127
127
  useEffect(() => {
128
128
  if (!openNodePath) {
129
129
  notifyProperty(host, 'highlightedNodePath', '');
130
- return;
130
+ notifyProperty(host, 'nodesOnNodePath', []);
131
131
  }
132
- setNodesOnOpenNodePath(
133
- normalizeNodes(tree.getPathNodes(openNodePath).filter(item => item))
134
- );
135
132
  }, [openNodePath]);
136
133
 
137
134
  useEffect(() => {
138
135
  if (nodePath !== undefined) {
139
136
  host.dispatchEvent(
140
137
  new CustomEvent('select-node', {
141
- detail: {}
138
+ detail: {},
142
139
  })
143
140
  );
144
141
  }
145
142
  }, [nodePath]);
146
143
 
147
144
  useEffect(() => {
148
- notifyProperty(
149
- host,
150
- 'highlightedNodePath',
151
- !highlightedNode ? '' : highlightedNode.path
152
- );
145
+ notifyProperty(host, 'highlightedNodePath', highlightedNode?.path || '');
153
146
  }, [highlightedNode]);
154
147
 
155
- return html`
156
- <style>
157
- :host {
158
- --cosmoz-treenode-navigator-select-node-icon-color: var(
159
- --primary-color,
160
- white
161
- );
162
- --cosmoz-treenode-navigator-list-item-focused-color: #f0f8ff;
163
- }
164
-
165
- #header {
166
- margin: 0 16px;
167
- }
168
-
169
- #header a {
170
- text-decoration: none;
171
- color: inherit;
172
- }
173
-
174
- .icon {
175
- display: inline-block;
176
- position: relative;
177
- padding: 8px;
178
- outline: none;
179
- user-select: none;
180
- cursor: pointer;
181
- z-index: 0;
182
- line-height: 1;
183
- width: 40px;
184
- height: 40px;
185
- box-sizing: border-box;
186
- }
148
+ const meta = useMeta({ dataPlane, highlightedNode, openNode });
187
149
 
188
- .path {
189
- display: flex;
190
- align-items: center;
191
- flex-wrap: wrap;
150
+ useEffect(() => {
151
+ const handler = (e) => {
152
+ if ((e.ctrlKey && e.altKey) || e.defaultPrevented) {
153
+ return;
192
154
  }
155
+ const { dataPlane: items, highlightedNode: node, openNode: open } = meta;
156
+ switch (e.key) {
157
+ case 'Up':
158
+ case 'ArrowUp':
159
+ {
160
+ const idx = items.findIndex((i) => i.path === node?.path);
161
+ e.preventDefault();
162
+ setHighlightedNode(items[Math.max(idx - 1, 0)]);
163
+ }
164
+ break;
165
+ case 'Down':
166
+ case 'ArrowDown':
167
+ {
168
+ const idx = items.findIndex((i) => i.path === node?.path);
169
+ if (idx < items.length - 1) {
170
+ e.preventDefault();
171
+ setHighlightedNode(items[idx + 1]);
172
+ }
173
+ }
174
+ break;
193
175
 
194
- .slash {
195
- margin: 0 2px;
176
+ case 'Right':
177
+ case 'ArrowRight':
178
+ case 'Enter':
179
+ if (node) {
180
+ e.preventDefault();
181
+ open(node);
182
+ }
183
+ break;
184
+ default:
185
+ break;
196
186
  }
187
+ };
197
188
 
198
- .pointer {
199
- cursor: pointer;
200
- }
189
+ document.addEventListener('keydown', handler, true);
190
+ return () => document.removeEventListener('keydown', handler, true);
191
+ }, [meta]);
201
192
 
202
- cosmoz-listbox {
203
- box-shadow: none;
204
- display: block;
205
- height: var(--cosmoz-treenode-navigator-list-height, 50vh);
206
- position: static;
207
- width: 100%;
208
- }
193
+ const renderItem = (node, index) => html` <div class="item">
194
+ ${when(
195
+ renderSection(index, node.parentSectionName),
196
+ () => html` <div class="section">${node.parentSectionName}</div> `
197
+ )}
198
+ <div
199
+ class=${computeRowClass('node', node, highlightedNode)}
200
+ @click=${() => setHighlightedNode(node)}
201
+ @dblclick=${(e) => onNodeDblClicked(e, host)}
202
+ >
203
+ <div class="name">${node.name}</div>
204
+ ${when(
205
+ tree.hasChildren(node),
206
+ () => html`
207
+ <span class="icon" @click=${() => openNode(node)}>
208
+ <svg
209
+ viewBox="0 0 24 24"
210
+ preserveAspectRatio="xMidYMid meet"
211
+ focusable="false"
212
+ style="pointer-events: none; display: block; width: 100%; height: 100%;"
213
+ >
214
+ <g>
215
+ <path
216
+ d="M12 4l-1.41 1.41L16.17 11H4v2h12.17l-5.58 5.59L12 20l8-8z"
217
+ ></path>
218
+ </g>
219
+ </svg>
220
+ </span>
221
+ `
222
+ )}
223
+ </div>
224
+ </div>`;
209
225
 
210
- .btn-ghost {
211
- background: transparent;
212
- border: none;
213
- cursor: pointer;
214
- margin: 0 0.29em;
215
- padding: 0.7em 0.57em;
216
- text-transform: uppercase;
217
- }
226
+ return html`
227
+ <style>
228
+ ${styles}
218
229
  </style>
219
- <div id="header">
230
+ <div class="header">
220
231
  <h3 class="path">
221
- <span class="icon" @click=${ () => openNode() }>
232
+ <span class="icon" @click=${() => openNode()}>
222
233
  <svg
223
234
  viewBox="0 0 24 24"
224
235
  preserveAspectRatio="xMidYMid meet"
@@ -228,83 +239,35 @@ const TreenodeNavigator = host => {
228
239
  <g><path d="M10 20v-6h4v6h5v-8h3L12 3 2 12h3v8z"></path></g>
229
240
  </svg>
230
241
  </span>
231
- ${ nodesOnOpenNodePath.map(node => {
232
- return html` <span class="slash">/</span>
233
- <span class="pointer" tabindex="0" @click="${ () => openNode(node) }"
234
- >${ getNodeName(node) }</span
242
+ ${nodesOnOpenNodePath.map((node) => {
243
+ return html` <span class="slash">/</span>
244
+ <span class="pointer" tabindex="0" @click=${() => openNode(node)}
245
+ >${node[tree.searchProperty]}</span
235
246
  >`;
236
- }) }
247
+ })}
237
248
  </h3>
238
249
  <cosmoz-input
239
250
  tabindex="0"
240
- .value=${ searchValue }
241
- .placeholder="${ searchPlaceholder }"
242
- @input="${ ({ target }) => setSearchValue(target.value) }"
251
+ .value=${searchValue}
252
+ .placeholder=${searchPlaceholder}
253
+ @input=${(e) => setSearchValue(e.target.value)}
243
254
  />
244
255
  </div>
245
- ${ when(
246
- tree,
247
- () => html`
248
- <cosmoz-listbox
249
- .query="${ searchValue }"
250
- .items="${ dataPlane }"
251
- .textual="${ item => item.name }"
252
- .itemRenderer=${ (node, index) => html`
253
- <div class="node-item-wrapper">
254
- <style>
255
- ${ nodeStyles }
256
- </style>
257
- ${ when(
258
- renderSection(index, node.parentSectionName),
259
- () => html`
260
- <div class="section">${ node.parentSectionName }</div>
261
- `
262
- ) }
263
- <div
264
- class="${ computeRowClass(
265
- 'node-item pointer',
266
- node,
267
- highlightedNode
268
- ) }"
269
- @click="${ () => setHighlightedNode(node) }"
270
- @dblclick="${ e => onNodeDblClicked(e, host) }"
271
- >
272
- <div style="flex: auto">${ node.name }</div>
273
- ${ when(
274
- hasChildren(node),
275
- () => html`
276
- <span class="icon" @click="${ () => openNode(node) }">
277
- <svg
278
- viewBox="0 0 24 24"
279
- preserveAspectRatio="xMidYMid meet"
280
- focusable="false"
281
- style="pointer-events: none; display: block; width: 100%; height: 100%;"
282
- >
283
- <g>
284
- <path
285
- d="M12 4l-1.41 1.41L16.17 11H4v2h12.17l-5.58 5.59L12 20l8-8z"
286
- ></path>
287
- </g>
288
- </svg>
289
- </span>
290
- `
291
- ) }
292
- </div>
293
- </div>
294
- ` }
295
- </cosmoz-listbox>`
296
- ) }
297
- ${ when(
298
- showGlobalSearchBtn(
299
- computeSearching(searchValue, searchMinLength),
300
- openNodePath
301
- ),
302
- () => html`
303
- <button class="btn-ghost" @click="${ () => setOpenNodePath('') }">
304
- ${ searchGlobalPlaceholder }
256
+ ${when(
257
+ tree,
258
+ () => html` <div class="items">
259
+ <div virtualizer-sizer></div>
260
+ ${virtualize({ items: dataPlane, renderItem, scroller: true })}
261
+ </div>`
262
+ )}
263
+ ${when(
264
+ showGlobalSearchBtn(isSearching, openNodePath),
265
+ () => html`
266
+ <button class="btn-ghost" @click=${() => setOpenNodePath('')}>
267
+ ${searchGlobalPlaceholder}
305
268
  </button>
306
269
  `
307
- ) }
270
+ )}
308
271
  `;
309
272
  };
310
273
 
@@ -0,0 +1,119 @@
1
+ import { tagged as css } from '@neovici/cosmoz-utils';
2
+
3
+ export default css`
4
+ :host {
5
+ --cosmoz-treenode-navigator-select-node-icon-color: var(
6
+ --primary-color,
7
+ white
8
+ );
9
+ --cosmoz-treenode-navigator-list-item-focused-color: #f0f8ff;
10
+ }
11
+
12
+ .header {
13
+ margin: 0 16px;
14
+ }
15
+
16
+ .header a {
17
+ text-decoration: none;
18
+ color: inherit;
19
+ }
20
+
21
+ .icon {
22
+ display: inline-block;
23
+ position: relative;
24
+ padding: 8px;
25
+ outline: none;
26
+ user-select: none;
27
+ cursor: pointer;
28
+ z-index: 0;
29
+ line-height: 1;
30
+ width: 40px;
31
+ height: 40px;
32
+ box-sizing: border-box;
33
+ }
34
+
35
+ .path {
36
+ display: flex;
37
+ align-items: center;
38
+ flex-wrap: wrap;
39
+ }
40
+
41
+ .slash {
42
+ margin: 0 2px;
43
+ }
44
+
45
+ .pointer {
46
+ }
47
+
48
+ .items {
49
+ height: var(--cosmoz-treenode-navigator-list-height, 50vh);
50
+ width: 100%;
51
+ }
52
+
53
+ .section {
54
+ background-color: #f5f5f5;
55
+ padding: 5px;
56
+ }
57
+
58
+ .pointer {
59
+ cursor: pointer;
60
+ }
61
+
62
+ .item {
63
+ width: 100%;
64
+ }
65
+
66
+ .node {
67
+ align-items: center;
68
+ display: flex;
69
+ font-family: 'Roboto', 'Noto', sans-serif;
70
+ font-size: 16px;
71
+ font-weight: 400;
72
+ height: 40px;
73
+ line-height: 24px;
74
+ padding: 6px 12px;
75
+ cursor: pointer;
76
+ }
77
+ .name {
78
+ flex: auto;
79
+ }
80
+ .icon {
81
+ box-sizing: border-box;
82
+ cursor: pointer;
83
+ display: inline-block;
84
+ height: 40px;
85
+ line-height: 1;
86
+ outline: none;
87
+ padding: 8px;
88
+ position: relative;
89
+ user-select: none;
90
+ width: 40px;
91
+ z-index: 0;
92
+ }
93
+
94
+ .node.selected {
95
+ background-color: var(
96
+ --cosmoz-listbox-active-color,
97
+ var(--cosmoz-selection-color, rgba(58, 145, 226, 0.1))
98
+ );
99
+ transition: background-color 0.2s ease-out;
100
+ }
101
+
102
+ .node.selected .icon svg {
103
+ fill: var(--cosmoz-treenode-navigator-select-node-icon-color);
104
+ transition: color 0.8s ease-out;
105
+ }
106
+
107
+ .btn-ghost {
108
+ background: transparent;
109
+ border: none;
110
+ cursor: pointer;
111
+ margin: 0 20px;
112
+ padding: 0.7em 0.57em;
113
+ width: calc(100% - 50px);
114
+ border: solid 1px #a4abae;
115
+ border-radius: 16px;
116
+ font-size: 14px;
117
+ font-family: inherit;
118
+ }
119
+ `;
package/helpers.js CHANGED
@@ -1,16 +1,4 @@
1
- import { DefaultTree } from '@neovici/cosmoz-tree/cosmoz-default-tree';
2
- import basicTree from './test/data/basicTree';
3
- import { tagged as css } from '@neovici/cosmoz-utils';
4
-
5
- const tree = new DefaultTree(basicTree),
6
- /**
7
- * Returns true if a given node has children
8
- * @param {Object} node - The node
9
- * @return {Boolean} - True if node has children
10
- */
11
- hasChildren = node => {
12
- return tree.hasChildren(node);
13
- },
1
+ export const //
14
2
  /**
15
3
  * Returns true, if a search string is eligable to trigger a search
16
4
  * @param {String} value - The search string
@@ -23,27 +11,30 @@ const tree = new DefaultTree(basicTree),
23
11
  /**
24
12
  * Normalizes and returns an Array of nodes
25
13
  * with the properties name, path, parentSectionName, children
14
+ * @param {Object} tree - The cosmoz tree
26
15
  * @param {Array} nodes - The input nodes
27
16
  * @return {Array} - The normalized nodes
28
17
  */
29
- normalizeNodes = nodes => {
18
+ normalizeNodes = (tree, nodes) => {
30
19
  if (!Array.isArray(nodes)) {
31
20
  return [];
32
21
  }
33
- return nodes.map(node => {
22
+ return nodes.map((node) => {
34
23
  if (!node) {
35
24
  return node;
36
25
  }
37
26
  const path = node.pathLocator || node.path,
38
27
  pathLocatorSeparator = tree.pathLocatorSeparator,
39
- parentPath = path.includes(pathLocatorSeparator) ? path.substring(0, path.lastIndexOf(pathLocatorSeparator)) : path;
28
+ parentPath = path.includes(pathLocatorSeparator)
29
+ ? path.substring(0, path.lastIndexOf(pathLocatorSeparator))
30
+ : path;
40
31
  return {
41
32
  name: node[tree.searchProperty],
42
33
  path,
43
34
  children: node[tree.childProperty],
44
35
  parentSectionName: tree.getPathString(parentPath, tree.searchProperty),
45
36
  id: node.id,
46
- pathLocator: node.pathLocator
37
+ pathLocator: node.pathLocator,
47
38
  };
48
39
  });
49
40
  },
@@ -58,7 +49,7 @@ const tree = new DefaultTree(basicTree),
58
49
  computeDataPlane = (searching, searchString, renderedLevel, tree) => {
59
50
  if (searching && tree) {
60
51
  const results = tree.searchNodes(searchString, renderedLevel, false);
61
- return normalizeNodes(results);
52
+ return normalizeNodes(tree, results);
62
53
  }
63
54
  return renderedLevel;
64
55
  },
@@ -78,11 +69,11 @@ const tree = new DefaultTree(basicTree),
78
69
  level = tree.hasChildren(node) ? children : node,
79
70
  sortFunc = (a, b) => {
80
71
  // First sort based on "folder" status (containing children)
81
- if (hasChildren(a)) {
82
- if (!hasChildren(b)) {
72
+ if (tree.hasChildren(a)) {
73
+ if (!tree.hasChildren(b)) {
83
74
  return -1;
84
75
  }
85
- } else if (hasChildren(b)) {
76
+ } else if (tree.hasChildren(b)) {
86
77
  return 1;
87
78
  }
88
79
  // Then sort on searchProperty
@@ -97,8 +88,7 @@ const tree = new DefaultTree(basicTree),
97
88
  }
98
89
  return 0;
99
90
  };
100
- return normalizeNodes(level)
101
- .sort(sortFunc);
91
+ return normalizeNodes(tree, level).sort(sortFunc);
102
92
  },
103
93
  /**
104
94
  * Returns the classes of a row based its selection state
@@ -110,9 +100,10 @@ const tree = new DefaultTree(basicTree),
110
100
  computeRowClass = (classes, node, highlightedNode) => {
111
101
  let selected = false;
112
102
  if (highlightedNode) {
113
- selected = node === highlightedNode
114
- || node.id && node.id === highlightedNode.id
115
- || node.pathLocator === highlightedNode.pathLocator;
103
+ selected =
104
+ node === highlightedNode ||
105
+ (node.id && node.id === highlightedNode.id) ||
106
+ node.pathLocator === highlightedNode.pathLocator;
116
107
  }
117
108
  return selected ? classes + ' selected' : classes;
118
109
  },
@@ -123,19 +114,13 @@ const tree = new DefaultTree(basicTree),
123
114
  * @return {undefined}
124
115
  */
125
116
  onNodeDblClicked = (event, host) => {
126
- host.dispatchEvent(new CustomEvent('node-dblclicked', {
127
- detail: {
128
- model: event.model
129
- }
130
- }));
131
- },
132
- /**
133
- * Returns the name of a given node
134
- * @param {Object} node - The node
135
- * @return {String} - The name
136
- */
137
- getNodeName = node => {
138
- return node[tree.searchProperty];
117
+ host.dispatchEvent(
118
+ new CustomEvent('node-dblclicked', {
119
+ detail: {
120
+ model: event.model,
121
+ },
122
+ })
123
+ );
139
124
  },
140
125
  /**
141
126
  * Returns true, if the button should be visible
@@ -156,7 +141,7 @@ const tree = new DefaultTree(basicTree),
156
141
  if (!tree || !pathLocator) {
157
142
  return [];
158
143
  }
159
- return normalizeNodes(tree.getPathNodes(pathLocator));
144
+ return normalizeNodes(tree, tree.getPathNodes(pathLocator));
160
145
  },
161
146
  /**
162
147
  * Returns a node based on a given path locator.
@@ -175,78 +160,7 @@ const tree = new DefaultTree(basicTree),
175
160
  let nodes;
176
161
 
177
162
  if (!node) {
178
- nodes = tree.getPathNodes(pathLocator).filter(n => n != null);
163
+ nodes = tree.getPathNodes(pathLocator).filter((n) => n != null);
179
164
  }
180
165
  return nodes && nodes.length > 0 ? nodes.pop() : node;
181
- },
182
- nodeStyles = css`
183
- .section {
184
- background-color: #f5f5f5;
185
- padding: 5px;
186
- }
187
-
188
- .pointer {
189
- cursor: pointer;
190
- }
191
-
192
- .item {
193
- padding: 0;
194
- }
195
-
196
- .item[data-index] {
197
- background: transparent;
198
- }
199
-
200
- .node-item {
201
- align-items: center;
202
- display: flex;
203
- font-family: 'Roboto', 'Noto', sans-serif;
204
- font-size: 16px;
205
- font-weight: 400;
206
- height: 40px;
207
- line-height: 24px;
208
- padding: 6px 12px;
209
- }
210
-
211
- .node-item-wrapper {
212
- width: 100%;
213
- }
214
-
215
- .icon {
216
- box-sizing: border-box;
217
- cursor: pointer;
218
- display: inline-block;
219
- height: 40px;
220
- line-height: 1;
221
- outline: none;
222
- padding: 8px;
223
- position: relative;
224
- user-select: none;
225
- width: 40px;
226
- z-index: 0;
227
- }
228
-
229
- .node-item.selected {
230
- background-color: var(--cosmoz-listbox-active-color, var(--cosmoz-selection-color, rgba(58, 145, 226, 0.1)));
231
- transition: background-color 0.2s ease-out;
232
- }
233
-
234
- .node-item.selected .icon svg {
235
- fill: var(--cosmoz-treenode-navigator-select-node-icon-color);
236
- transition: color 0.8s ease-out;
237
- }`;
238
-
239
- export {
240
- hasChildren,
241
- computeSearching,
242
- normalizeNodes,
243
- renderLevel,
244
- computeDataPlane,
245
- computeRowClass,
246
- onNodeDblClicked,
247
- getNodeName,
248
- showGlobalSearchBtn,
249
- getTreePathParts,
250
- getNode,
251
- nodeStyles
252
- };
166
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@neovici/cosmoz-treenode-navigator",
3
- "version": "5.0.1",
3
+ "version": "5.1.0",
4
4
  "description": "A Polymer component that lets you navigate and search through hierarchically structured data-nodes and select one of them.",
5
5
  "keywords": [
6
6
  "polymer",
@@ -54,7 +54,7 @@
54
54
  }
55
55
  },
56
56
  "dependencies": {
57
- "@neovici/cosmoz-autocomplete": "^5.0.0",
57
+ "@lit-labs/virtualizer": "^0.7.1",
58
58
  "@neovici/cosmoz-dialog": "^3.0.0",
59
59
  "@neovici/cosmoz-i18next": "^3.2.0",
60
60
  "@neovici/cosmoz-input": "^3.0.0",
@@ -80,9 +80,9 @@
80
80
  "@polymer/test-fixture": "^4.0.2",
81
81
  "@semantic-release/changelog": "^6.0.0",
82
82
  "@semantic-release/git": "^10.0.0",
83
- "@webcomponents/webcomponentsjs": "^2.0.0",
84
83
  "@web/dev-server": "^0.1.25",
85
84
  "@web/test-runner": "^0.13.19",
85
+ "@webcomponents/webcomponentsjs": "^2.0.0",
86
86
  "husky": "^8.0.0",
87
87
  "semantic-release": "^19.0.3"
88
88
  }
@@ -2,7 +2,7 @@ import { assert, fixture, html } from '@open-wc/testing';
2
2
  import { DefaultTree } from '@neovici/cosmoz-tree/cosmoz-default-tree';
3
3
  import basicTree from './data/basicTree.js';
4
4
  import '../cosmoz-treenode-navigator';
5
- import { computeDataPlane, computeSearching, hasChildren, renderLevel } from '../helpers';
5
+ import { computeDataPlane, computeSearching, renderLevel } from '../helpers';
6
6
 
7
7
  suite('cosmoz-treenode-navigator', () => {
8
8
  let navigator;
@@ -60,9 +60,9 @@ suite('cosmoz-treenode-navigator', () => {
60
60
  parentSectionName: 'D:',
61
61
  pathLocator: '1000'
62
62
  };
63
- assert.equal(hasChildren(node), true);
63
+ assert.equal(navigator.tree.hasChildren(node), true);
64
64
  const node2 = { name: 'Git', path: '1.5.7', parentSectionName: 'C:/Program Files', pathLocator: '1.5.7' };
65
- assert.equal(hasChildren(node2), false);
65
+ assert.equal(navigator.tree.hasChildren(node2), false);
66
66
  });
67
67
 
68
68
  test('match search string', () => {