@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 +14 -0
- package/cosmoz-treenode-button-view.js +41 -20
- package/cosmoz-treenode-navigator.js +95 -50
- package/helpers.js +1 -2
- package/package.json +1 -1
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="
|
|
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
|
|
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 {
|
|
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