@neovici/cosmoz-treenode-navigator 5.2.0 → 5.3.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/CHANGELOG.md CHANGED
@@ -1,3 +1,17 @@
1
+ # [5.3.0](https://github.com/neovici/cosmoz-treenode-navigator/compare/v5.2.0...v5.3.0) (2022-08-01)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * close on enter ([e7cd4d1](https://github.com/neovici/cosmoz-treenode-navigator/commit/e7cd4d191abdf1b9486a5d097154f530130d4a96))
7
+ * **cosmoz-treenode-button-view:** pass opened to treenode ([4f43ade](https://github.com/neovici/cosmoz-treenode-navigator/commit/4f43ade0844fe5168cf84f6f7833dc61a608e176))
8
+ * **renderLevel:** handle leaf nodes ([760f6bf](https://github.com/neovici/cosmoz-treenode-navigator/commit/760f6bf4888ccbec0184ca72ea21ab4781e66f46))
9
+
10
+
11
+ ### Features
12
+
13
+ * **treenode-navigator:** autoscroll to highlighed node ([5c55a38](https://github.com/neovici/cosmoz-treenode-navigator/commit/5c55a38ad406b3d711405b8173c388fb26d5eb6c))
14
+
1
15
  # [5.2.0](https://github.com/neovici/cosmoz-treenode-navigator/compare/v5.1.0...v5.2.0) (2022-08-01)
2
16
 
3
17
 
@@ -127,7 +127,8 @@ class CosmozTreenodeButtonView extends translatable(PolymerElement) {
127
127
  <cosmoz-dialog
128
128
  id="dialogTree"
129
129
  class="treeDialog"
130
- on-iron-overlay-opened="focusSearch"
130
+ on-iron-overlay-opened="onOpened"
131
+ on-iron-overlay-closed="onClosed"
131
132
  modal
132
133
  prerender
133
134
  >
@@ -147,6 +148,7 @@ class CosmozTreenodeButtonView extends translatable(PolymerElement) {
147
148
  nodes-on-node-path="{{ nodesOnNodePath }}"
148
149
  on-node-dblclicked="_selectNodeAndCloseDialog"
149
150
  on-select-node="selectNode"
151
+ opened="[[ opened ]]"
150
152
  >
151
153
  <slot></slot>
152
154
  </cosmoz-treenode-navigator>
@@ -174,13 +176,13 @@ class CosmozTreenodeButtonView extends translatable(PolymerElement) {
174
176
  return {
175
177
  multiSelection: {
176
178
  type: Boolean,
177
- value: false
179
+ value: false,
178
180
  },
179
181
  /*
180
182
  * The main node structure
181
183
  */
182
184
  tree: {
183
- type: Tree
185
+ type: Tree,
184
186
  },
185
187
  /*
186
188
  * Currently selected node object
@@ -190,7 +192,7 @@ class CosmozTreenodeButtonView extends translatable(PolymerElement) {
190
192
  value() {
191
193
  return {};
192
194
  },
193
- notify: true
195
+ notify: true,
194
196
  },
195
197
  /**
196
198
  * Selected nodes
@@ -198,32 +200,40 @@ class CosmozTreenodeButtonView extends translatable(PolymerElement) {
198
200
  selectedNodes: {
199
201
  type: Array,
200
202
  notify: true,
201
- value: () => []
203
+ value: () => [],
202
204
  },
203
205
  /**
204
206
  * If true, reset button gets hidden
205
207
  */
206
208
  noReset: {
207
209
  type: Boolean,
208
- value: false
210
+ value: false,
211
+ },
212
+ /**
213
+ * If true opened
214
+ */
215
+ opened: {
216
+ type: Boolean,
217
+ value: false,
209
218
  },
219
+
210
220
  /*
211
221
  * Placeholder for the search field
212
222
  */
213
223
  searchPlaceholder: {
214
- type: String
224
+ type: String,
215
225
  },
216
226
  /*
217
227
  * Placeholder for button text
218
228
  */
219
229
  buttonTextPlaceholder: {
220
230
  type: String,
221
- computed: 'getButtonTextPlaceholder(multiSelection)'
231
+ computed: 'getButtonTextPlaceholder(multiSelection)',
222
232
  },
223
233
 
224
234
  buttonText: {
225
235
  type: String,
226
- computed: '_getButtonLabel(nodesOnNodePath, buttonTextPlaceholder)'
236
+ computed: '_getButtonLabel(nodesOnNodePath, buttonTextPlaceholder)',
227
237
  },
228
238
 
229
239
  /*
@@ -231,13 +241,13 @@ class CosmozTreenodeButtonView extends translatable(PolymerElement) {
231
241
  */
232
242
  nodePath: {
233
243
  type: String,
234
- notify: true
244
+ notify: true,
235
245
  },
236
246
  /*
237
247
  * The nodes on the path of the selected node
238
248
  */
239
249
  nodesOnNodePath: {
240
- type: Array
250
+ type: Array,
241
251
  },
242
252
  /*
243
253
  * Text displayed when local search has finished
@@ -245,14 +255,14 @@ class CosmozTreenodeButtonView extends translatable(PolymerElement) {
245
255
  */
246
256
  searchGlobalPlaceholder: {
247
257
  type: String,
248
- value: 'Click to search again but globally.'
258
+ value: 'Click to search again but globally.',
249
259
  },
250
260
  /*
251
261
  * Settable text for dialog title.
252
262
  */
253
263
  dialogText: {
254
264
  type: String,
255
- value: 'Search or navigate to chosen destination'
265
+ value: 'Search or navigate to chosen destination',
256
266
  },
257
267
  /*
258
268
  * Minimum length before an search
@@ -260,14 +270,14 @@ class CosmozTreenodeButtonView extends translatable(PolymerElement) {
260
270
  */
261
271
  searchMinLength: {
262
272
  type: Number,
263
- value: 1
273
+ value: 1,
264
274
  },
265
275
  /*
266
276
  * Path string of highlighted (focused) node
267
277
  */
268
278
  highlightedNodePath: {
269
- type: String
270
- }
279
+ type: String,
280
+ },
271
281
  };
272
282
  }
273
283
  /**
@@ -324,8 +334,8 @@ class CosmozTreenodeButtonView extends translatable(PolymerElement) {
324
334
  return placeholder;
325
335
  }
326
336
  return pathParts
327
- .filter(n => n)
328
- .map(part => part[this.tree.searchProperty])
337
+ .filter((n) => n)
338
+ .map((part) => part[this.tree.searchProperty])
329
339
  .join(' / ');
330
340
  }
331
341
  /**
@@ -348,7 +358,10 @@ class CosmozTreenodeButtonView extends translatable(PolymerElement) {
348
358
  * @returns {void}
349
359
  */
350
360
  focusSearch() {
351
- this.$.dialogTree.paperDialog.querySelector('#treeNavigator').focus();
361
+ this.$.dialogTree.paperDialog
362
+ .querySelector('#treeNavigator')
363
+ .shadowRoot.querySelector('cosmoz-input')
364
+ .focus();
352
365
  }
353
366
  /**
354
367
  * Reset the component to make it ready for reuse
@@ -383,7 +396,7 @@ class CosmozTreenodeButtonView extends translatable(PolymerElement) {
383
396
  if (this.multiSelection) {
384
397
  if (
385
398
  !this.selectedNodes.some(
386
- node => node.pathLocator === this.highlightedNodePath
399
+ (node) => node.pathLocator === this.highlightedNodePath
387
400
  )
388
401
  ) {
389
402
  this.push('selectedNodes', this.selectedNode);
@@ -423,5 +436,13 @@ class CosmozTreenodeButtonView extends translatable(PolymerElement) {
423
436
  }
424
437
  );
425
438
  }
439
+
440
+ onOpened() {
441
+ this.opened = true;
442
+ this.focusSearch();
443
+ }
444
+ onClosed() {
445
+ this.opened = false;
446
+ }
426
447
  }
427
448
  customElements.define(CosmozTreenodeButtonView.is, CosmozTreenodeButtonView);
@@ -1,8 +1,17 @@
1
- import { component, useEffect, useState, useMemo, useCallback } from 'haunted';
1
+ import {
2
+ component,
3
+ useEffect,
4
+ useState,
5
+ useMemo,
6
+ useCallback,
7
+ useRef,
8
+ } from 'haunted';
2
9
  import { html } from 'lit-html';
3
10
  import { when } from 'lit-html/directives/when.js';
4
11
  import { guard } from 'lit-html/directives/guard.js';
12
+ import { ref } from 'lit-html/directives/ref.js';
5
13
  import { virtualize } from '@lit-labs/virtualizer/virtualize.js';
14
+
6
15
  import '@neovici/cosmoz-input';
7
16
  import { useMeta } from '@neovici/cosmoz-utils/hooks/use-meta';
8
17
  import { notifyProperty } from '@neovici/cosmoz-utils/hooks/use-notify-property';
@@ -35,6 +44,7 @@ const TreenodeNavigator = (host) => {
35
44
  */
36
45
  searchMinLength,
37
46
  nodePath,
47
+ opened,
38
48
  } = host,
39
49
  /**
40
50
  * The path of the opened node
@@ -72,7 +82,8 @@ const TreenodeNavigator = (host) => {
72
82
  setHighlightedNode();
73
83
  },
74
84
  [tree]
75
- );
85
+ ),
86
+ listRef = useRef();
76
87
 
77
88
  useEffect(() => {
78
89
  if (!openNodePath) {
@@ -98,57 +109,87 @@ const TreenodeNavigator = (host) => {
98
109
  const meta = useMeta({ dataPlane, highlightedNode, openNode });
99
110
 
100
111
  useEffect(() => {
101
- // eslint-disable-next-line max-lines-per-function
102
- const handler = (e) => {
103
- if ((e.ctrlKey && e.altKey) || e.defaultPrevented) {
104
- return;
105
- }
106
- const { dataPlane: items, highlightedNode: node, openNode: open } = meta,
107
- getIndex = () =>
108
- items.findIndex((i) => i.pathLocator === node?.pathLocator);
109
-
110
- switch (e.key) {
111
- case 'Up':
112
- case 'ArrowUp':
113
- {
114
- const idx = getIndex();
115
- e.preventDefault();
116
- setHighlightedNode(items[Math.max(idx - 1, 0)]);
117
- }
118
- break;
119
- case 'Down':
120
- case 'ArrowDown':
121
- {
122
- const idx = getIndex();
123
- if (idx < items.length - 1) {
112
+ if (!opened) {
113
+ return;
114
+ }
115
+
116
+ const getVlist = () => {
117
+ const list = listRef.current,
118
+ vlist = list[Object.getOwnPropertySymbols(list)[0]];
119
+ return vlist;
120
+ },
121
+ // eslint-disable-next-line max-lines-per-function, max-statements
122
+ handler = (e) => {
123
+ if ((e.ctrlKey && e.altKey) || e.defaultPrevented) {
124
+ return;
125
+ }
126
+ const {
127
+ dataPlane: items,
128
+ highlightedNode: node,
129
+ openNode: open,
130
+ } = meta,
131
+ vlist = getVlist(),
132
+ getIndex = () =>
133
+ items.findIndex((i) => i.pathLocator === node?.pathLocator);
134
+
135
+ switch (e.key) {
136
+ case 'Up':
137
+ case 'ArrowUp':
138
+ {
139
+ const idx = getIndex(),
140
+ nidx = Math.max(idx - 1, 0);
124
141
  e.preventDefault();
125
- setHighlightedNode(items[idx + 1]);
142
+ setHighlightedNode(items[nidx]);
143
+ if (nidx < vlist._firstVisible) {
144
+ vlist.scrollToIndex = { index: nidx, position: 'start' };
145
+ }
126
146
  }
127
- }
128
- break;
129
-
130
- case 'Enter':
131
- if (node) {
132
- e.preventDefault();
133
- host.dispatchEvent(new CustomEvent('select-node'));
134
- }
135
- break;
136
-
137
- case 'Right':
138
- case 'ArrowRight':
139
- if (node) {
140
- e.preventDefault();
141
- open(node);
142
- }
143
- break;
144
- default:
145
- break;
146
- }
147
- };
147
+ break;
148
+ case 'Down':
149
+ case 'ArrowDown':
150
+ {
151
+ const idx = getIndex();
152
+ if (idx < items.length - 1) {
153
+ const nidx = idx + 1;
154
+ e.preventDefault();
155
+ setHighlightedNode(items[nidx]);
156
+ if (nidx > vlist._lastVisible) {
157
+ vlist.scrollToIndex = { index: nidx, position: 'end' };
158
+ }
159
+ }
160
+ }
161
+ break;
162
+
163
+ case 'Enter':
164
+ if (node) {
165
+ e.preventDefault();
166
+ host.dispatchEvent(new CustomEvent('node-dblclicked'));
167
+ }
168
+ break;
169
+
170
+ case 'Right':
171
+ case 'ArrowRight':
172
+ if (node && tree.hasChildren(node)) {
173
+ e.preventDefault();
174
+ open(node);
175
+ }
176
+ break;
177
+ default:
178
+ break;
179
+ }
180
+ };
181
+
182
+ // autoscroll on open
183
+ (() => {
184
+ getVlist().scrollToIndex = {
185
+ index: meta.dataPlane?.indexOf(meta.highlightedNode),
186
+ position: 'center',
187
+ };
188
+ })();
148
189
 
149
190
  document.addEventListener('keydown', handler, true);
150
191
  return () => document.removeEventListener('keydown', handler, true);
151
- }, [meta]);
192
+ }, [opened, meta]);
152
193
 
153
194
  const renderItem = (node, index) => html` <div class="item">
154
195
  ${((parentPath) =>
@@ -227,9 +268,13 @@ const TreenodeNavigator = (host) => {
227
268
  </div>
228
269
  ${when(
229
270
  tree,
230
- () => html` <div class="items">
271
+ () => html` <div class="items" ${ref((el) => (listRef.current = el))}>
231
272
  <div virtualizer-sizer></div>
232
- ${virtualize({ items: dataPlane, renderItem, scroller: true })}
273
+ ${virtualize({
274
+ items: dataPlane,
275
+ renderItem,
276
+ scroller: true,
277
+ })}
233
278
  </div>`
234
279
  )}
235
280
  ${when(
package/helpers.js CHANGED
@@ -19,7 +19,6 @@ export const //
19
19
  }
20
20
  const node = tree.getNodeByPathLocator(pathLocator),
21
21
  children = tree.getChildren(node),
22
- level = children.length > 0 ? children : node,
23
22
  searchProperty = tree.searchProperty,
24
23
  sortFunc = (a, b) => {
25
24
  // First sort based on "folder" status (containing children)
@@ -42,7 +41,7 @@ export const //
42
41
  }
43
42
  return 0;
44
43
  };
45
- return level.sort(sortFunc);
44
+ return children.length > 0 ? children.sort(sortFunc) : node;
46
45
  },
47
46
  /**
48
47
  * Returns the found nodes based on a search string and a given tree to be searched
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@neovici/cosmoz-treenode-navigator",
3
- "version": "5.2.0",
3
+ "version": "5.3.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",