@neovici/cosmoz-treenode-navigator 5.2.0 → 5.4.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,24 @@
1
+ # [5.4.0](https://github.com/neovici/cosmoz-treenode-navigator/compare/v5.3.0...v5.4.0) (2022-11-03)
2
+
3
+
4
+ ### Features
5
+
6
+ * **select:** close dialog manually ([3abb0d0](https://github.com/neovici/cosmoz-treenode-navigator/commit/3abb0d0d75c11ad3e77001ba401f4fe998ad66a7))
7
+
8
+ # [5.3.0](https://github.com/neovici/cosmoz-treenode-navigator/compare/v5.2.0...v5.3.0) (2022-08-01)
9
+
10
+
11
+ ### Bug Fixes
12
+
13
+ * close on enter ([e7cd4d1](https://github.com/neovici/cosmoz-treenode-navigator/commit/e7cd4d191abdf1b9486a5d097154f530130d4a96))
14
+ * **cosmoz-treenode-button-view:** pass opened to treenode ([4f43ade](https://github.com/neovici/cosmoz-treenode-navigator/commit/4f43ade0844fe5168cf84f6f7833dc61a608e176))
15
+ * **renderLevel:** handle leaf nodes ([760f6bf](https://github.com/neovici/cosmoz-treenode-navigator/commit/760f6bf4888ccbec0184ca72ea21ab4781e66f46))
16
+
17
+
18
+ ### Features
19
+
20
+ * **treenode-navigator:** autoscroll to highlighed node ([5c55a38](https://github.com/neovici/cosmoz-treenode-navigator/commit/5c55a38ad406b3d711405b8173c388fb26d5eb6c))
21
+
1
22
  # [5.2.0](https://github.com/neovici/cosmoz-treenode-navigator/compare/v5.1.0...v5.2.0) (2022-08-01)
2
23
 
3
24
 
package/cc-test-reporter CHANGED
Binary file
@@ -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,13 +148,13 @@ 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>
153
155
  <div class="buttons">
154
156
  <paper-button
155
157
  disabled="[[!highlightedNodePath]]"
156
- dialog-confirm
157
158
  autofocus
158
159
  on-click="selectNode"
159
160
  >[[ _('Select', t) ]]</paper-button
@@ -174,13 +175,13 @@ class CosmozTreenodeButtonView extends translatable(PolymerElement) {
174
175
  return {
175
176
  multiSelection: {
176
177
  type: Boolean,
177
- value: false
178
+ value: false,
178
179
  },
179
180
  /*
180
181
  * The main node structure
181
182
  */
182
183
  tree: {
183
- type: Tree
184
+ type: Tree,
184
185
  },
185
186
  /*
186
187
  * Currently selected node object
@@ -190,7 +191,7 @@ class CosmozTreenodeButtonView extends translatable(PolymerElement) {
190
191
  value() {
191
192
  return {};
192
193
  },
193
- notify: true
194
+ notify: true,
194
195
  },
195
196
  /**
196
197
  * Selected nodes
@@ -198,32 +199,40 @@ class CosmozTreenodeButtonView extends translatable(PolymerElement) {
198
199
  selectedNodes: {
199
200
  type: Array,
200
201
  notify: true,
201
- value: () => []
202
+ value: () => [],
202
203
  },
203
204
  /**
204
205
  * If true, reset button gets hidden
205
206
  */
206
207
  noReset: {
207
208
  type: Boolean,
208
- value: false
209
+ value: false,
210
+ },
211
+ /**
212
+ * If true opened
213
+ */
214
+ opened: {
215
+ type: Boolean,
216
+ value: false,
209
217
  },
218
+
210
219
  /*
211
220
  * Placeholder for the search field
212
221
  */
213
222
  searchPlaceholder: {
214
- type: String
223
+ type: String,
215
224
  },
216
225
  /*
217
226
  * Placeholder for button text
218
227
  */
219
228
  buttonTextPlaceholder: {
220
229
  type: String,
221
- computed: 'getButtonTextPlaceholder(multiSelection)'
230
+ computed: 'getButtonTextPlaceholder(multiSelection)',
222
231
  },
223
232
 
224
233
  buttonText: {
225
234
  type: String,
226
- computed: '_getButtonLabel(nodesOnNodePath, buttonTextPlaceholder)'
235
+ computed: '_getButtonLabel(nodesOnNodePath, buttonTextPlaceholder)',
227
236
  },
228
237
 
229
238
  /*
@@ -231,13 +240,13 @@ class CosmozTreenodeButtonView extends translatable(PolymerElement) {
231
240
  */
232
241
  nodePath: {
233
242
  type: String,
234
- notify: true
243
+ notify: true,
235
244
  },
236
245
  /*
237
246
  * The nodes on the path of the selected node
238
247
  */
239
248
  nodesOnNodePath: {
240
- type: Array
249
+ type: Array,
241
250
  },
242
251
  /*
243
252
  * Text displayed when local search has finished
@@ -245,14 +254,14 @@ class CosmozTreenodeButtonView extends translatable(PolymerElement) {
245
254
  */
246
255
  searchGlobalPlaceholder: {
247
256
  type: String,
248
- value: 'Click to search again but globally.'
257
+ value: 'Click to search again but globally.',
249
258
  },
250
259
  /*
251
260
  * Settable text for dialog title.
252
261
  */
253
262
  dialogText: {
254
263
  type: String,
255
- value: 'Search or navigate to chosen destination'
264
+ value: 'Search or navigate to chosen destination',
256
265
  },
257
266
  /*
258
267
  * Minimum length before an search
@@ -260,14 +269,14 @@ class CosmozTreenodeButtonView extends translatable(PolymerElement) {
260
269
  */
261
270
  searchMinLength: {
262
271
  type: Number,
263
- value: 1
272
+ value: 1,
264
273
  },
265
274
  /*
266
275
  * Path string of highlighted (focused) node
267
276
  */
268
277
  highlightedNodePath: {
269
- type: String
270
- }
278
+ type: String,
279
+ },
271
280
  };
272
281
  }
273
282
  /**
@@ -324,8 +333,8 @@ class CosmozTreenodeButtonView extends translatable(PolymerElement) {
324
333
  return placeholder;
325
334
  }
326
335
  return pathParts
327
- .filter(n => n)
328
- .map(part => part[this.tree.searchProperty])
336
+ .filter((n) => n)
337
+ .map((part) => part[this.tree.searchProperty])
329
338
  .join(' / ');
330
339
  }
331
340
  /**
@@ -348,7 +357,10 @@ class CosmozTreenodeButtonView extends translatable(PolymerElement) {
348
357
  * @returns {void}
349
358
  */
350
359
  focusSearch() {
351
- this.$.dialogTree.paperDialog.querySelector('#treeNavigator').focus();
360
+ this.$.dialogTree.paperDialog
361
+ .querySelector('#treeNavigator')
362
+ .shadowRoot.querySelector('cosmoz-input')
363
+ .focus();
352
364
  }
353
365
  /**
354
366
  * Reset the component to make it ready for reuse
@@ -383,7 +395,7 @@ class CosmozTreenodeButtonView extends translatable(PolymerElement) {
383
395
  if (this.multiSelection) {
384
396
  if (
385
397
  !this.selectedNodes.some(
386
- node => node.pathLocator === this.highlightedNodePath
398
+ (node) => node.pathLocator === this.highlightedNodePath
387
399
  )
388
400
  ) {
389
401
  this.push('selectedNodes', this.selectedNode);
@@ -391,6 +403,7 @@ class CosmozTreenodeButtonView extends translatable(PolymerElement) {
391
403
  this.nodePath = '';
392
404
  this.selectedNode = {};
393
405
  }
406
+ this.$.dialogTree.close();
394
407
  }
395
408
  /**
396
409
  * Selects node and closes the dialog
@@ -398,7 +411,6 @@ class CosmozTreenodeButtonView extends translatable(PolymerElement) {
398
411
  */
399
412
  _selectNodeAndCloseDialog() {
400
413
  this.selectNode();
401
- this.$.dialogTree.close();
402
414
  }
403
415
  /**
404
416
  * Determine if selected nodes container should be visible or not.
@@ -423,5 +435,13 @@ class CosmozTreenodeButtonView extends translatable(PolymerElement) {
423
435
  }
424
436
  );
425
437
  }
438
+
439
+ onOpened() {
440
+ this.opened = true;
441
+ this.focusSearch();
442
+ }
443
+ onClosed() {
444
+ this.opened = false;
445
+ }
426
446
  }
427
447
  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.4.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",