neo.mjs 6.10.16 → 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/apps/form/neo-config.json +1 -1
- package/apps/form/view/SideNavList.mjs +4 -0
- package/apps/form/view/pages/Page6.mjs +6 -4
- 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/form/field/TextArea.mjs +35 -11
- package/src/list/Base.mjs +4 -5
- package/src/main/DomAccess.mjs +9 -1
- 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 +8 -9
package/apps/ServiceWorker.mjs
CHANGED
@@ -20,24 +20,26 @@ class Page6 extends FormPageContainer {
|
|
20
20
|
* @member {Object} itemDefaults
|
21
21
|
*/
|
22
22
|
itemDefaults: {
|
23
|
-
module: TextArea
|
23
|
+
module : TextArea,
|
24
|
+
autoGrow: true
|
24
25
|
},
|
25
26
|
/**
|
26
27
|
* @member {Object[]} items
|
27
28
|
*/
|
28
29
|
items: [{
|
29
|
-
height : 200,
|
30
30
|
labelText: 'Page 6 Field 1',
|
31
|
+
minHeight: 150,
|
31
32
|
name : 'field1',
|
32
33
|
required : true,
|
33
34
|
value : 'Lorem ipsum'
|
34
35
|
}, {
|
35
|
-
height : 300,
|
36
36
|
labelText: 'Page 6 Field 2',
|
37
|
+
minHeight: 150,
|
37
38
|
name : 'field2'
|
38
39
|
}, {
|
39
40
|
labelText: 'Page 6 Field 3',
|
40
|
-
name : 'field3'
|
41
|
+
name : 'field3',
|
42
|
+
readOnly : true
|
41
43
|
}]
|
42
44
|
}
|
43
45
|
}
|
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',
|
@@ -79,12 +79,20 @@ class TextArea extends Text {
|
|
79
79
|
wrap_: null
|
80
80
|
}
|
81
81
|
|
82
|
-
|
83
|
-
|
82
|
+
/**
|
83
|
+
* Triggered after the autoGrow config got changed
|
84
|
+
* @param {Boolean} value
|
85
|
+
* @param {Boolean} oldValue
|
86
|
+
* @protected
|
87
|
+
*/
|
88
|
+
afterSetAutoGrow(value, oldValue) {
|
89
|
+
let me = this;
|
90
|
+
|
91
|
+
value && me.syncAutoGrowMonitor();
|
84
92
|
|
85
93
|
// Restore any configured height if autoGrow turned off
|
86
|
-
if (!
|
87
|
-
|
94
|
+
if (!value) {
|
95
|
+
me.afterSetHeight(me._height);
|
88
96
|
}
|
89
97
|
}
|
90
98
|
|
@@ -150,13 +158,24 @@ class TextArea extends Text {
|
|
150
158
|
* @protected
|
151
159
|
*/
|
152
160
|
afterSetValue(value, oldValue) {
|
153
|
-
let
|
161
|
+
let me = this,
|
162
|
+
inputEl = me.getInputEl();
|
154
163
|
|
155
164
|
if (inputEl) {
|
156
165
|
inputEl.html = StringUtil.escapeHtml(value);
|
157
166
|
}
|
158
167
|
|
159
168
|
super.afterSetValue(value, oldValue);
|
169
|
+
|
170
|
+
if (me.autoGrow && me.mounted && me.readOnly) {
|
171
|
+
setTimeout(() => {
|
172
|
+
Neo.main.DomAccess.monitorAutoGrowHandler({
|
173
|
+
appName : me.appName,
|
174
|
+
id : inputEl.id,
|
175
|
+
windowId: me.windowId
|
176
|
+
})
|
177
|
+
}, 50)
|
178
|
+
}
|
160
179
|
}
|
161
180
|
|
162
181
|
/**
|
@@ -180,14 +199,19 @@ class TextArea extends Text {
|
|
180
199
|
return this.beforeSetEnumValue(value, oldValue, 'wrap', 'wrapValues');
|
181
200
|
}
|
182
201
|
|
202
|
+
/**
|
203
|
+
*
|
204
|
+
*/
|
183
205
|
async syncAutoGrowMonitor() {
|
184
|
-
|
185
|
-
|
206
|
+
let me = this;
|
207
|
+
|
208
|
+
if (me.mounted && me.autoGrow) {
|
209
|
+
// Delegate monitoring of sizes to the main thread.
|
186
210
|
Neo.main.DomAccess.monitorAutoGrow({
|
187
|
-
appName :
|
188
|
-
id :
|
189
|
-
autoGrow :
|
190
|
-
})
|
211
|
+
appName : me.appName,
|
212
|
+
id : me.getInputElId(),
|
213
|
+
autoGrow : me.autoGrow
|
214
|
+
})
|
191
215
|
}
|
192
216
|
}
|
193
217
|
}
|
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
|
|
package/src/main/DomAccess.mjs
CHANGED
@@ -88,6 +88,7 @@ class DomAccess extends Base {
|
|
88
88
|
'getScrollingDimensions',
|
89
89
|
'measure',
|
90
90
|
'monitorAutoGrow',
|
91
|
+
'monitorAutoGrowHandler',
|
91
92
|
'navigate',
|
92
93
|
'navigateTo',
|
93
94
|
'scrollBy',
|
@@ -637,8 +638,15 @@ class DomAccess extends Base {
|
|
637
638
|
})
|
638
639
|
}
|
639
640
|
|
640
|
-
|
641
|
+
/**
|
642
|
+
*
|
643
|
+
* @param {Event|Object} data
|
644
|
+
* @param {String} [data.id]
|
645
|
+
* @param {HTMLElement} [data.target]
|
646
|
+
*/
|
647
|
+
monitorAutoGrowHandler(data) {
|
641
648
|
const
|
649
|
+
target = data.target || this.getElement(data.id),
|
642
650
|
{ style } = target,
|
643
651
|
{ style : inputStyle } = target.closest('.neo-textarea');
|
644
652
|
|
@@ -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
@@ -60,11 +60,9 @@ class Tree extends Base {
|
|
60
60
|
* @member {Object} _vdom
|
61
61
|
*/
|
62
62
|
_vdom:
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
]
|
67
|
-
}
|
63
|
+
{cn: [
|
64
|
+
{tag: 'ul', cls: ['neo-list-container', 'neo-list'], tabIndex: -1, cn: []}
|
65
|
+
]}
|
68
66
|
}
|
69
67
|
|
70
68
|
/**
|
@@ -223,10 +221,11 @@ class Tree extends Base {
|
|
223
221
|
}
|
224
222
|
|
225
223
|
tmpRoot.cn.push({
|
226
|
-
tag
|
224
|
+
tag : 'li',
|
225
|
+
tabIndex : -1,
|
227
226
|
cls,
|
228
|
-
id
|
229
|
-
cn
|
227
|
+
id : me.getItemId(item.id),
|
228
|
+
cn : [{
|
230
229
|
tag : 'span',
|
231
230
|
cls : [itemCls + '-content', item.iconCls],
|
232
231
|
innerHTML: item.name,
|
@@ -234,7 +233,7 @@ class Tree extends Base {
|
|
234
233
|
pointerEvents: 'none'
|
235
234
|
}
|
236
235
|
}],
|
237
|
-
style: {
|
236
|
+
style : {
|
238
237
|
padding : '10px',
|
239
238
|
position: item.isLeaf ? null : 'sticky',
|
240
239
|
top : item.isLeaf ? null : (level * 38) + 'px',
|