@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 +21 -0
- package/cc-test-reporter +0 -0
- package/cosmoz-treenode-button-view.js +42 -22
- package/cosmoz-treenode-navigator.js +95 -50
- package/helpers.js +1 -2
- package/package.json +1 -1
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="
|
|
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
|
|
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 {
|
|
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
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
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[
|
|
142
|
+
setHighlightedNode(items[nidx]);
|
|
143
|
+
if (nidx < vlist._firstVisible) {
|
|
144
|
+
vlist.scrollToIndex = { index: nidx, position: 'start' };
|
|
145
|
+
}
|
|
126
146
|
}
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
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({
|
|
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
|
|
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