neo.mjs 6.10.17 → 6.11.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/apps/ServiceWorker.mjs +2 -2
- package/examples/ServiceWorker.mjs +2 -2
- package/package.json +2 -2
- package/resources/scss/src/tree/List.scss +10 -5
- package/src/DefaultConfig.mjs +2 -2
- package/src/Neo.mjs +41 -10
- package/src/core/Base.mjs +9 -0
- package/src/core/Util.mjs +0 -10
- package/src/list/Base.mjs +4 -5
- package/src/main/addon/Navigator.mjs +7 -1
- package/src/selection/Model.mjs +7 -5
- package/src/tree/Accordion.mjs +3 -2
- package/src/tree/List.mjs +5 -4
package/apps/ServiceWorker.mjs
CHANGED
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "neo.mjs",
|
3
|
-
"version": "6.
|
3
|
+
"version": "6.11.0",
|
4
4
|
"description": "The webworkers driven UI framework",
|
5
5
|
"type": "module",
|
6
6
|
"repository": {
|
@@ -55,7 +55,7 @@
|
|
55
55
|
"inquirer": "^9.2.14",
|
56
56
|
"neo-jsdoc": "1.0.1",
|
57
57
|
"neo-jsdoc-x": "1.0.5",
|
58
|
-
"postcss": "^8.4.
|
58
|
+
"postcss": "^8.4.35",
|
59
59
|
"sass": "^1.70.0",
|
60
60
|
"siesta-lite": "5.5.2",
|
61
61
|
"showdown": "^2.1.0",
|
@@ -1,9 +1,14 @@
|
|
1
1
|
.neo-tree-list {
|
2
|
-
border
|
3
|
-
color
|
4
|
-
display
|
5
|
-
|
6
|
-
|
2
|
+
border : none;
|
3
|
+
color : var(--tree-list-color);
|
4
|
+
display : flex;
|
5
|
+
flex-direction : column;
|
6
|
+
overflow : hidden;
|
7
|
+
position : relative;
|
8
|
+
|
9
|
+
// Allow item's scrollIntoView upwards to avoid
|
10
|
+
// being hidden below a sticky item stuck at the top.
|
11
|
+
scroll-padding-block-start : 3em;
|
7
12
|
|
8
13
|
.neo-list {
|
9
14
|
overflow : visible;
|
package/src/DefaultConfig.mjs
CHANGED
@@ -236,12 +236,12 @@ const DefaultConfig = {
|
|
236
236
|
useVdomWorker: true,
|
237
237
|
/**
|
238
238
|
* buildScripts/injectPackageVersion.mjs will update this value
|
239
|
-
* @default '6.
|
239
|
+
* @default '6.11.0'
|
240
240
|
* @memberOf! module:Neo
|
241
241
|
* @name config.version
|
242
242
|
* @type String
|
243
243
|
*/
|
244
|
-
version: '6.
|
244
|
+
version: '6.11.0'
|
245
245
|
};
|
246
246
|
|
247
247
|
Object.assign(DefaultConfig, {
|
package/src/Neo.mjs
CHANGED
@@ -1,8 +1,10 @@
|
|
1
1
|
import DefaultConfig from './DefaultConfig.mjs';
|
2
2
|
|
3
|
-
const
|
4
|
-
|
5
|
-
|
3
|
+
const
|
4
|
+
camelRegex = /-./g,
|
5
|
+
configSymbol = Symbol.for('configSymbol'),
|
6
|
+
getSetCache = Symbol('getSetCache'),
|
7
|
+
typeDetector = {
|
6
8
|
function: (item) => {
|
7
9
|
if (item.prototype?.constructor.isClass) {
|
8
10
|
return 'NeoClass'
|
@@ -60,17 +62,19 @@ Neo = globalThis.Neo = Object.assign({
|
|
60
62
|
* @tutorial 02_ClassSystem
|
61
63
|
*/
|
62
64
|
applyClassConfig(cls) {
|
63
|
-
let baseCfg
|
64
|
-
|
65
|
-
|
66
|
-
|
65
|
+
let baseCfg = null,
|
66
|
+
ntypeChain = [],
|
67
|
+
ntypeMap = Neo.ntypeMap,
|
68
|
+
proto = cls.prototype || cls,
|
69
|
+
protos = [],
|
67
70
|
cfg, config, ctor, ntype;
|
68
71
|
|
69
72
|
while (proto.__proto__) {
|
70
73
|
ctor = proto.constructor;
|
71
74
|
|
72
75
|
if (Object.hasOwn(ctor, 'classConfigApplied')) {
|
73
|
-
baseCfg
|
76
|
+
baseCfg = Neo.clone(ctor.config, true);
|
77
|
+
ntypeChain = [...ctor.ntypeChain];
|
74
78
|
break
|
75
79
|
}
|
76
80
|
|
@@ -113,6 +117,8 @@ Neo = globalThis.Neo = Object.assign({
|
|
113
117
|
if (Object.hasOwn(cfg, 'ntype')) {
|
114
118
|
ntype = cfg.ntype;
|
115
119
|
|
120
|
+
ntypeChain.unshift(ntype);
|
121
|
+
|
116
122
|
// Running the docs app inside a workspace can pull in the same classes from different roots,
|
117
123
|
// so we want to check for different class names as well
|
118
124
|
if (Object.hasOwn(ntypeMap, ntype) && cfg.className !== ntypeMap[ntype]) {
|
@@ -136,7 +142,7 @@ Neo = globalThis.Neo = Object.assign({
|
|
136
142
|
applyMixins(ctor, mixins);
|
137
143
|
|
138
144
|
if (Neo.ns('Neo.core.Observable', false, ctor.prototype.mixins)) {
|
139
|
-
ctor.observable = true
|
145
|
+
ctor.observable = true
|
140
146
|
}
|
141
147
|
}
|
142
148
|
|
@@ -148,7 +154,8 @@ Neo = globalThis.Neo = Object.assign({
|
|
148
154
|
Object.assign(ctor, {
|
149
155
|
classConfigApplied: true,
|
150
156
|
config : Neo.clone(config, true),
|
151
|
-
isClass : true
|
157
|
+
isClass : true,
|
158
|
+
ntypeChain
|
152
159
|
});
|
153
160
|
|
154
161
|
!config.singleton && this.applyToGlobalNs(cls)
|
@@ -156,6 +163,10 @@ Neo = globalThis.Neo = Object.assign({
|
|
156
163
|
|
157
164
|
proto = cls.prototype || cls;
|
158
165
|
|
166
|
+
ntypeChain.forEach(ntype => {
|
167
|
+
proto[`is${Neo.capitalize(Neo.camel(ntype))}`] = true
|
168
|
+
});
|
169
|
+
|
159
170
|
if (proto.singleton) {
|
160
171
|
cls = Neo.create(cls);
|
161
172
|
Neo.applyToGlobalNs(cls)
|
@@ -234,6 +245,26 @@ Neo = globalThis.Neo = Object.assign({
|
|
234
245
|
return target
|
235
246
|
},
|
236
247
|
|
248
|
+
/**
|
249
|
+
* Converts kebab-case strings into camel-case
|
250
|
+
* @memberOf module:Neo
|
251
|
+
* @param {String} value The target object
|
252
|
+
* @returns {String}
|
253
|
+
*/
|
254
|
+
camel(value) {
|
255
|
+
return value.replace(camelRegex, match => match[1].toUpperCase())
|
256
|
+
},
|
257
|
+
|
258
|
+
/**
|
259
|
+
* Makes the first character of a string uppercase
|
260
|
+
* @memberOf module:Neo
|
261
|
+
* @param {String} value
|
262
|
+
* @returns {Boolean|String} Returns false for non string inputs
|
263
|
+
*/
|
264
|
+
capitalize(value) {
|
265
|
+
return value[0].toUpperCase() + value.slice(1)
|
266
|
+
},
|
267
|
+
|
237
268
|
/**
|
238
269
|
* @memberOf module:Neo
|
239
270
|
* @param {Object|Array|*} obj
|
package/src/core/Base.mjs
CHANGED
@@ -327,6 +327,15 @@ class Base {
|
|
327
327
|
return this.constructor[key]
|
328
328
|
}
|
329
329
|
|
330
|
+
/**
|
331
|
+
* Check if a given ntype exists inside the proto chain, including the top level class
|
332
|
+
* @param {String} ntype
|
333
|
+
* @returns {Boolean}
|
334
|
+
*/
|
335
|
+
hasNtype(ntype) {
|
336
|
+
return this.constructor.ntypeChain.includes(ntype)
|
337
|
+
}
|
338
|
+
|
330
339
|
/**
|
331
340
|
* Gets triggered after onConstructed() is done
|
332
341
|
* @see {@link Neo.core.Base#onConstructed onConstructed}
|
package/src/core/Util.mjs
CHANGED
@@ -36,15 +36,6 @@ class Util extends Base {
|
|
36
36
|
});
|
37
37
|
}
|
38
38
|
|
39
|
-
/**
|
40
|
-
* Makes the first character of a string uppercase
|
41
|
-
* @param {String} value
|
42
|
-
* @returns {Boolean|String} Returns false for non string inputs
|
43
|
-
*/
|
44
|
-
static capitalize(value) {
|
45
|
-
return value[0].toUpperCase() + value.slice(1)
|
46
|
-
}
|
47
|
-
|
48
39
|
/**
|
49
40
|
* Transforms a styles string into a styles object using camelcase syntax
|
50
41
|
* @param {String} string The styles string to parse
|
@@ -225,7 +216,6 @@ Neo.applyFromNs(Neo, Util, {
|
|
225
216
|
bindMethods : 'bindMethods',
|
226
217
|
createStyleObject: 'createStyleObject',
|
227
218
|
createStyles : 'createStyles',
|
228
|
-
capitalize : 'capitalize',
|
229
219
|
decamel : 'decamel',
|
230
220
|
isArray : 'isArray',
|
231
221
|
isBoolean : 'isBoolean',
|
package/src/list/Base.mjs
CHANGED
@@ -411,14 +411,13 @@ class Base extends Component {
|
|
411
411
|
itemContent = me.createItemContent(record, index),
|
412
412
|
itemId = me.getItemId(record[me.getKeyProperty()]),
|
413
413
|
selectionModel = me.selectionModel,
|
414
|
+
isSelected = !me.disableSelection && selectionModel?.isSelected(itemId),
|
414
415
|
item;
|
415
416
|
|
416
417
|
isHeader && cls.push('neo-list-header');
|
417
418
|
|
418
|
-
if (
|
419
|
-
|
420
|
-
cls.push(selectionModel.selectedCls)
|
421
|
-
}
|
419
|
+
if (isSelected){
|
420
|
+
cls.push(selectionModel.selectedCls)
|
422
421
|
}
|
423
422
|
|
424
423
|
if (record.cls) {
|
@@ -432,7 +431,7 @@ class Base extends Component {
|
|
432
431
|
item = {
|
433
432
|
id : itemId,
|
434
433
|
tag : isHeader ? 'dt' : me.itemTagName,
|
435
|
-
'aria-selected' :
|
434
|
+
'aria-selected' : isSelected,
|
436
435
|
cls
|
437
436
|
};
|
438
437
|
|
@@ -348,13 +348,19 @@ class Navigator extends Base {
|
|
348
348
|
return;
|
349
349
|
}
|
350
350
|
|
351
|
+
// Scroll the target into view smoothly before we focus it without triggering a scroll
|
352
|
+
newActiveElement.scrollIntoView({
|
353
|
+
block : 'nearest',
|
354
|
+
behavior : 'smooth'
|
355
|
+
});
|
356
|
+
|
351
357
|
// Find a focusable element which may be the item, or inside the item to draw focus to.
|
352
358
|
// For example a Chip list in which .neo-list-items contain focusable Chips.
|
353
359
|
const focusTarget = DomUtils.query(newActiveElement, DomUtils.isFocusable);
|
354
360
|
|
355
361
|
// If the item contains a focusable, we focus it and then react in navigateFocusInHandler
|
356
362
|
if (focusTarget) {
|
357
|
-
focusTarget.focus();
|
363
|
+
focusTarget.focus({ preventScroll : true });
|
358
364
|
}
|
359
365
|
// If not, we programatically navigate there
|
360
366
|
else {
|
package/src/selection/Model.mjs
CHANGED
@@ -96,7 +96,7 @@ class Model extends Base {
|
|
96
96
|
deselect(item, silent, itemCollection=this.items, selectedCls) {
|
97
97
|
// We hold vdom ids for now, so all incoming selections must be converted.
|
98
98
|
item = item.isRecord ? view.getItemId(item) : Neo.isObject(item) ? item.id : item;
|
99
|
-
|
99
|
+
|
100
100
|
if (itemCollection.includes(item)) {
|
101
101
|
let me = this,
|
102
102
|
view = me.view,
|
@@ -231,7 +231,7 @@ class Model extends Base {
|
|
231
231
|
|
232
232
|
items.forEach((node, i) => {
|
233
233
|
node = view.getVdomChild(node);
|
234
|
-
|
234
|
+
|
235
235
|
if (node) {
|
236
236
|
node.cls = NeoArray.add(node.cls || [], selectedCls || me.selectedCls);
|
237
237
|
node['aria-selected'] = true;
|
@@ -257,10 +257,12 @@ class Model extends Base {
|
|
257
257
|
* @param {Object} item
|
258
258
|
*/
|
259
259
|
toggleSelection(item) {
|
260
|
-
|
261
|
-
|
260
|
+
let me = this;
|
261
|
+
|
262
|
+
if (me.isSelected(item)) {
|
263
|
+
me.deselect(item)
|
262
264
|
} else {
|
263
|
-
|
265
|
+
me.select(item)
|
264
266
|
}
|
265
267
|
}
|
266
268
|
|
package/src/tree/Accordion.mjs
CHANGED
@@ -83,7 +83,7 @@ class AccordionTree extends TreeList {
|
|
83
83
|
*/
|
84
84
|
_vdom:
|
85
85
|
{cn: [
|
86
|
-
{tag: 'ul', cls: ['neo-list-container', 'neo-list', 'neo-accordion-style'],
|
86
|
+
{tag: 'ul', cls: ['neo-list-container', 'neo-list', 'neo-accordion-style'], cn: []}
|
87
87
|
]}
|
88
88
|
}
|
89
89
|
|
@@ -260,7 +260,8 @@ class AccordionTree extends TreeList {
|
|
260
260
|
id = me.getItemId(item.id);
|
261
261
|
|
262
262
|
tmpRoot.cn.push({
|
263
|
-
|
263
|
+
tabIndex : -1,
|
264
|
+
tag : 'li',
|
264
265
|
cls,
|
265
266
|
id,
|
266
267
|
cn : [{
|
package/src/tree/List.mjs
CHANGED
@@ -221,10 +221,11 @@ class Tree extends Base {
|
|
221
221
|
}
|
222
222
|
|
223
223
|
tmpRoot.cn.push({
|
224
|
-
tag
|
224
|
+
tag : 'li',
|
225
|
+
tabIndex : -1,
|
225
226
|
cls,
|
226
|
-
id
|
227
|
-
cn
|
227
|
+
id : me.getItemId(item.id),
|
228
|
+
cn : [{
|
228
229
|
tag : 'span',
|
229
230
|
cls : [itemCls + '-content', item.iconCls],
|
230
231
|
innerHTML: item.name,
|
@@ -232,7 +233,7 @@ class Tree extends Base {
|
|
232
233
|
pointerEvents: 'none'
|
233
234
|
}
|
234
235
|
}],
|
235
|
-
style: {
|
236
|
+
style : {
|
236
237
|
padding : '10px',
|
237
238
|
position: item.isLeaf ? null : 'sticky',
|
238
239
|
top : item.isLeaf ? null : (level * 38) + 'px',
|