neo.mjs 6.10.14 → 6.10.16

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.
@@ -20,9 +20,9 @@ class ServiceWorker extends ServiceBase {
20
20
  */
21
21
  singleton: true,
22
22
  /**
23
- * @member {String} version='6.10.14'
23
+ * @member {String} version='6.10.16'
24
24
  */
25
- version: '6.10.14'
25
+ version: '6.10.16'
26
26
  }
27
27
 
28
28
  /**
@@ -3,6 +3,6 @@
3
3
  "basePath" : "../../",
4
4
  "environment" : "development",
5
5
  "mainPath" : "./Main.mjs",
6
- "mainThreadAddons": ["DragDrop", "HighlightJS", "Stylesheet", "Markdown"],
6
+ "mainThreadAddons": ["DragDrop", "HighlightJS", "Navigator", "Stylesheet", "Markdown"],
7
7
  "themes" : ["neo-theme-neo-light"]
8
8
  }
@@ -20,9 +20,9 @@ class ServiceWorker extends ServiceBase {
20
20
  */
21
21
  singleton: true,
22
22
  /**
23
- * @member {String} version='6.10.14'
23
+ * @member {String} version='6.10.16'
24
24
  */
25
- version: '6.10.14'
25
+ version: '6.10.16'
26
26
  }
27
27
 
28
28
  /**
package/package.json CHANGED
@@ -1,73 +1,73 @@
1
1
  {
2
- "name" : "neo.mjs",
3
- "version" : "6.10.14",
4
- "description" : "The webworkers driven UI framework",
5
- "type" : "module",
6
- "repository" : {
7
- "type" : "git",
8
- "url" : "https://github.com/neomjs/neo.git"
9
- },
10
- "bin" : {
11
- "neo-cc" : "./buildScripts/createClass.mjs"
12
- },
13
- "scripts" : {
14
- "add-config" : "node ./buildScripts/addConfig.mjs",
15
- "build-all" : "node ./buildScripts/buildAll.mjs -f -n",
16
- "build-all-questions" : "node ./buildScripts/buildAll.mjs -f",
17
- "build-themes" : "node ./buildScripts/buildThemes.mjs -f",
18
- "build-threads" : "node ./buildScripts/webpack/buildThreads.mjs -f",
19
- "convert-design-tokens" : "node ./buildScripts/convertDesignTokens.mjs",
20
- "create-app" : "node ./buildScripts/createApp.mjs",
21
- "create-app-minimal" : "node ./buildScripts/createAppMinimal.mjs",
22
- "create-class" : "node ./buildScripts/createClass.mjs",
23
- "create-component" : "node ./buildScripts/createComponent.mjs",
24
- "generate-docs-json" : "node ./buildScripts/docs/jsdocx.mjs",
25
- "inject-package-version" : "node ./buildScripts/injectPackageVersion.mjs",
26
- "server-start" : "webpack serve -c ./buildScripts/webpack/webpack.server.config.mjs --open",
27
- "test" : "echo \"Error: no test specified\" && exit 1",
28
- "watch-themes" : "node ./buildScripts/watchThemes.mjs"
29
- },
30
- "keywords" : [
31
- "javascript",
32
- "frontend",
33
- "framework",
34
- "webworker",
35
- "ecmascript",
36
- "css",
37
- "json"
38
- ],
39
- "author" : "Tobias Uhlig, Rich Waters",
40
- "license" : "MIT",
41
- "bugs" : {
42
- "url" : "https://github.com/neomjs/neo/issues"
43
- },
44
- "homepage" : "https://neomjs.github.io/pages/",
45
- "devDependencies" : {
46
- "@fortawesome/fontawesome-free" : "^6.5.1",
47
- "autoprefixer" : "^10.4.17",
48
- "chalk" : "^5.3.0",
49
- "clean-webpack-plugin" : "^4.0.0",
50
- "commander" : "^11.1.0",
51
- "cssnano" : "^6.0.3",
52
- "envinfo" : "^7.11.0",
53
- "fs-extra" : "^11.2.0",
54
- "highlightjs-line-numbers.js" : "^2.8.0",
55
- "inquirer" : "^9.2.12",
56
- "neo-jsdoc" : "1.0.1",
57
- "neo-jsdoc-x" : "1.0.5",
58
- "postcss" : "^8.4.33",
59
- "sass" : "^1.70.0",
60
- "siesta-lite" : "5.5.2",
61
- "showdown" : "^2.1.0",
62
- "url" : "^0.11.3",
63
- "webpack" : "^5.90.0",
64
- "webpack-cli" : "^5.1.4",
65
- "webpack-dev-server" : "4.15.1",
66
- "webpack-hook-plugin" : "^1.0.7",
67
- "webpack-node-externals" : "^3.0.0"
68
- },
69
- "funding" : {
70
- "type" : "GitHub Sponsors",
71
- "url" : "https://github.com/sponsors/tobiu"
72
- }
2
+ "name": "neo.mjs",
3
+ "version": "6.10.16",
4
+ "description": "The webworkers driven UI framework",
5
+ "type": "module",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "https://github.com/neomjs/neo.git"
9
+ },
10
+ "bin": {
11
+ "neo-cc": "./buildScripts/createClass.mjs"
12
+ },
13
+ "scripts": {
14
+ "add-config": "node ./buildScripts/addConfig.mjs",
15
+ "build-all": "node ./buildScripts/buildAll.mjs -f -n",
16
+ "build-all-questions": "node ./buildScripts/buildAll.mjs -f",
17
+ "build-themes": "node ./buildScripts/buildThemes.mjs -f",
18
+ "build-threads": "node ./buildScripts/webpack/buildThreads.mjs -f",
19
+ "convert-design-tokens": "node ./buildScripts/convertDesignTokens.mjs",
20
+ "create-app": "node ./buildScripts/createApp.mjs",
21
+ "create-app-minimal": "node ./buildScripts/createAppMinimal.mjs",
22
+ "create-class": "node ./buildScripts/createClass.mjs",
23
+ "create-component": "node ./buildScripts/createComponent.mjs",
24
+ "generate-docs-json": "node ./buildScripts/docs/jsdocx.mjs",
25
+ "inject-package-version": "node ./buildScripts/injectPackageVersion.mjs",
26
+ "server-start": "webpack serve -c ./buildScripts/webpack/webpack.server.config.mjs --open",
27
+ "test": "echo \"Error: no test specified\" && exit 1",
28
+ "watch-themes": "node ./buildScripts/watchThemes.mjs"
29
+ },
30
+ "keywords": [
31
+ "javascript",
32
+ "frontend",
33
+ "framework",
34
+ "webworker",
35
+ "ecmascript",
36
+ "css",
37
+ "json"
38
+ ],
39
+ "author": "Tobias Uhlig, Rich Waters",
40
+ "license": "MIT",
41
+ "bugs": {
42
+ "url": "https://github.com/neomjs/neo/issues"
43
+ },
44
+ "homepage": "https://neomjs.github.io/pages/",
45
+ "devDependencies": {
46
+ "@fortawesome/fontawesome-free": "^6.5.1",
47
+ "autoprefixer": "^10.4.17",
48
+ "chalk": "^5.3.0",
49
+ "clean-webpack-plugin": "^4.0.0",
50
+ "commander": "^12.0.0",
51
+ "cssnano": "^6.0.3",
52
+ "envinfo": "^7.11.1",
53
+ "fs-extra": "^11.2.0",
54
+ "highlightjs-line-numbers.js": "^2.8.0",
55
+ "inquirer": "^9.2.14",
56
+ "neo-jsdoc": "1.0.1",
57
+ "neo-jsdoc-x": "1.0.5",
58
+ "postcss": "^8.4.33",
59
+ "sass": "^1.70.0",
60
+ "siesta-lite": "5.5.2",
61
+ "showdown": "^2.1.0",
62
+ "url": "^0.11.3",
63
+ "webpack": "^5.90.1",
64
+ "webpack-cli": "^5.1.4",
65
+ "webpack-dev-server": "4.15.1",
66
+ "webpack-hook-plugin": "^1.0.7",
67
+ "webpack-node-externals": "^3.0.0"
68
+ },
69
+ "funding": {
70
+ "type": "GitHub Sponsors",
71
+ "url": "https://github.com/sponsors/tobiu"
72
+ }
73
73
  }
@@ -236,12 +236,12 @@ const DefaultConfig = {
236
236
  useVdomWorker: true,
237
237
  /**
238
238
  * buildScripts/injectPackageVersion.mjs will update this value
239
- * @default '6.10.14'
239
+ * @default '6.10.16'
240
240
  * @memberOf! module:Neo
241
241
  * @name config.version
242
242
  * @type String
243
243
  */
244
- version: '6.10.14'
244
+ version: '6.10.16'
245
245
  };
246
246
 
247
247
  Object.assign(DefaultConfig, {
@@ -258,7 +258,7 @@ class Base extends Component {
258
258
  menuConfig = isArray ? {} : value,
259
259
  model = me.getModel(),
260
260
 
261
- config = {
261
+ config = Neo.merge({
262
262
  module : module.default,
263
263
  align : {edgeAlign : 't0-b0', target: me.id},
264
264
  appName : me.appName,
@@ -266,9 +266,8 @@ class Base extends Component {
266
266
  floating : true,
267
267
  hidden : true,
268
268
  parentComponent: me,
269
- theme : me.theme,
270
- ...menuConfig
271
- };
269
+ theme : me.theme
270
+ }, menuConfig);
272
271
 
273
272
  if (items) {
274
273
  config.items = items
@@ -544,8 +543,7 @@ class Base extends Component {
544
543
  menuList.hidden = hidden;
545
544
 
546
545
  if (!hidden) {
547
- await this.timeout(50);
548
- menuList.focus()
546
+ await this.timeout(50)
549
547
  }
550
548
  }
551
549
  }
@@ -76,7 +76,7 @@ class RecordFactory extends Base {
76
76
 
77
77
  if (Array.isArray(model.fields)) {
78
78
  model.fields.forEach(field => {
79
- let value = config[field.name],
79
+ let value = config[field.mapping || field.name],
80
80
  symbol = Symbol.for(field.name),
81
81
  parsedValue;
82
82
 
@@ -126,8 +126,8 @@ class RecordFactory extends Base {
126
126
  }
127
127
  }
128
128
 
129
- Object.defineProperties(me, properties);
130
- });
129
+ Object.defineProperties(me, properties)
130
+ })
131
131
  }
132
132
  }
133
133
 
@@ -136,7 +136,7 @@ class RecordFactory extends Base {
136
136
  * @param {Object} fields
137
137
  */
138
138
  set(fields) {
139
- instance.setRecordFields(model, this, fields);
139
+ instance.setRecordFields(model, this, fields)
140
140
  }
141
141
 
142
142
  /**
@@ -144,17 +144,17 @@ class RecordFactory extends Base {
144
144
  * @param {Object} fields
145
145
  */
146
146
  setSilent(fields) {
147
- instance.setRecordFields(model, this, fields, true);
147
+ instance.setRecordFields(model, this, fields, true)
148
148
  }
149
149
  };
150
150
 
151
151
  Object.defineProperty(cls.prototype, 'isRecord', { value : true });
152
152
  Object.defineProperty(cls, 'isClass', { vale : true });
153
153
 
154
- return ns[key];
154
+ return ns[key]
155
155
  }
156
156
 
157
- return ns;
157
+ return ns
158
158
  }
159
159
  }
160
160
 
@@ -209,7 +209,7 @@ class RecordFactory extends Base {
209
209
  * @returns {Boolean}
210
210
  */
211
211
  isRecord(record) {
212
- return record?.[Symbol.for('isRecord')] || false;
212
+ return record?.isRecord;
213
213
  }
214
214
 
215
215
  /**
package/src/list/Base.mjs CHANGED
@@ -250,30 +250,32 @@ class Base extends Component {
250
250
  * @protected
251
251
  */
252
252
  afterSetHeaderlessActiveIndex(value, oldValue) {
253
- let me = this,
254
- activeIndex;
253
+ let me = this;
255
254
 
256
255
  if (Neo.isNumber(value)) {
257
- activeIndex = me.getActiveIndex(value);
258
-
259
- me.activeIndex = activeIndex
256
+ me.activeIndex = me.store.getCount() ? value: null
260
257
  } else if (Neo.isNumber(oldValue)) {
261
258
  me.activeIndex = null
262
259
  }
263
260
  }
264
261
 
265
- afterSetMounted(value) {
262
+ /**
263
+ * Triggered after the mounted config got changed
264
+ * @param {Boolean} value
265
+ * @param {Boolean} oldValue
266
+ * @protected
267
+ */
268
+ afterSetMounted(value, oldValue) {
266
269
  const me = this;
267
270
 
268
271
  // Tear down navigation before we lose the element
269
272
  if (!value && me.hasNavigator) {
270
273
  Neo.main.addon.Navigator.unsubscribe(me.navigator);
274
+
271
275
  me.hasNavigator = false;
272
- me.activeIndex = null
276
+ me.activeIndex = null
273
277
  }
274
278
 
275
- super.afterSetMounted(...arguments);
276
-
277
279
  if (value) {
278
280
  // Set up item navigation in the list
279
281
  if (!me.hasNavigator) {
@@ -282,11 +284,15 @@ class Base extends Component {
282
284
  id : me.id,
283
285
  selector : `.${me.itemCls}:not(.neo-disabled,.neo-list-header)`,
284
286
  ...me.navigator
285
- }
286
- me.hasNavigator = true;
287
+ };
288
+
289
+ me.hasNavigator = true
287
290
  }
291
+
288
292
  Neo.main.addon.Navigator.subscribe(me.navigator)
289
293
  }
294
+
295
+ super.afterSetMounted(value, oldValue)
290
296
  }
291
297
 
292
298
  /**
@@ -546,10 +552,10 @@ class Base extends Component {
546
552
 
547
553
  /**
548
554
  * Calls focus() on the top level DOM node of this component or on a given node via id
549
- * @param {String} [id]
555
+ * @param {String} id
550
556
  */
551
557
  focus(id) {
552
- Neo.main.addon.Navigator.navigateTo(id, this.navigator)
558
+ Neo.main.addon.Navigator.navigateTo([id, this.navigator])
553
559
  }
554
560
 
555
561
  /**
@@ -362,9 +362,8 @@ class DomAccess extends Base {
362
362
  if (node) {
363
363
  // The children property means focus inner elements if possible.
364
364
  if (!DomUtils.isFocusable(node) && data.children) {
365
- // Prefer to focus input fields over buttons.
366
- // querySelector('input,textarea,button') returns buttons first, so use multiple calls.
367
- node = node.querySelector('input:not(:disabled)') || node.querySelector('textarea:not(:disabled)') || node.querySelector('button:not(:disabled)') || [...node.querySelectorAll('*')].find(DomUtils.isFocusable);
365
+ // query for the first focusable decendent
366
+ node = DomUtils.query(node, DomUtils.isFocusable);
368
367
  }
369
368
  if (node) {
370
369
  node.focus();
@@ -30,9 +30,9 @@ class Navigator extends Base {
30
30
  */
31
31
  remote: {
32
32
  app: [
33
+ 'navigateTo',
33
34
  'subscribe',
34
- 'unsubscribe',
35
- 'navigateTo'
35
+ 'unsubscribe'
36
36
  ]
37
37
  },
38
38
  /**
@@ -114,7 +114,7 @@ class Navigator extends Base {
114
114
  // The navigables we are dealing with, if they are focusable must *not* be tabbable.
115
115
  // Only *one* must be tabbable, so that tabbing into the subject element goes to the
116
116
  // one active element.
117
- //
117
+ //
118
118
  // Tabbing *from* that must exit the subject element.
119
119
  //
120
120
  // So we must ensure that all the focusable elements except the first are not tabbable.
@@ -127,7 +127,14 @@ class Navigator extends Base {
127
127
  }
128
128
 
129
129
  const
130
- focusables = DomUtils.queryAll(data.subject, DomUtils.isFocusable),
130
+ // Extract all our navigable items, and find the focusable within
131
+ focusables = Array.from(data.subject.querySelectorAll(data.selector)).reduce((value,item ) => {
132
+ const f = DomUtils.query(item, DomUtils.isFocusable);
133
+ if (f){
134
+ value.push(f);
135
+ }
136
+ return value;
137
+ }, []),
131
138
  defaultActiveItem = focusables[0] || data.subject.querySelector(data.selector);
132
139
 
133
140
  // Ensure the items are not tabbable.
@@ -192,7 +199,7 @@ class Navigator extends Base {
192
199
  const { target } = e;
193
200
 
194
201
  // Clear active class from the item we are leaving from.
195
- target.closest(data.selector).classList.remove(data.activeCls);
202
+ target.closest(data.selector)?.classList.remove(data.activeCls);
196
203
 
197
204
  // On focusout, leave the last active item as tabbable so user can TAB back in here
198
205
  if (!DomUtils.isTabbable(target)) {
@@ -205,7 +212,7 @@ class Navigator extends Base {
205
212
 
206
213
  // If there was a focusable under the mouse, mousedown will have focused it and and we
207
214
  // will have respond to that in navigateFocusInHandler.
208
- // If not, we navigate programatically.
215
+ // If not, we navigate programmatically.
209
216
  if (target && !data.findFocusable(target)) {
210
217
  this.navigateTo(target, data);
211
218
  }
@@ -214,8 +221,8 @@ class Navigator extends Base {
214
221
  navigateMouseDownHandler(e, data) {
215
222
  const target = e.target.closest(data.selector);
216
223
 
217
- // If there is a focusable undet the mouse, it will take focus, and we respond to that in navigateFocusInHandler.
218
- // If not, we have to programatically activate on click, but we must not draw focus away from
224
+ // If there is a focusable under the mouse, it will take focus, and we respond to that in navigateFocusInHandler.
225
+ // If not, we have to programmatically activate on click, but we must not draw focus away from
219
226
  // where it is, so preventDefault
220
227
  if (target && !data.findFocusable(target)) {
221
228
  e.preventDefault();
@@ -241,12 +248,12 @@ class Navigator extends Base {
241
248
  if (containerStyle.display === 'flex' && containerStyle.flexDirection === 'row'
242
249
  || itemStyle.display === 'inline' || itemStyle.display === 'inline-block') {
243
250
  data.previousKey = 'ArrowLeft';
244
- data.nextKey = 'ArrowRight';
251
+ data.nextKey = 'ArrowRight';
245
252
  }
246
253
  // Child elements layed out vertically.
247
254
  else {
248
255
  data.previousKey = 'ArrowUp';
249
- data.nextKey = 'ArrowDown';
256
+ data.nextKey = 'ArrowDown';
250
257
  }
251
258
  }
252
259
 
@@ -314,7 +321,7 @@ class Navigator extends Base {
314
321
  }
315
322
 
316
323
  /**
317
- * Navigates to the passed
324
+ * Navigates to the passed
318
325
  * @param {String|Number} newActiveElement The id of the new active element in the subject
319
326
  * element, or the index of the item.
320
327
  * @param {Object} data The data block as passed to {@link #subscribe}
package/src/menu/List.mjs CHANGED
@@ -38,6 +38,10 @@ class List extends BaseList {
38
38
  * @member {Boolean} hideOnLeafItemClick=true
39
39
  */
40
40
  hideOnLeafItemClick: true,
41
+ /**
42
+ * @member {Boolean} itemsFocusable=true
43
+ */
44
+ itemsFocusable: true,
41
45
  /**
42
46
  * Optionally pass menu.Store data directly
43
47
  * @member {Object[]|null} items_=null
@@ -100,7 +104,7 @@ class List extends BaseList {
100
104
  * @member {Object} _vdom
101
105
  */
102
106
  _vdom:
103
- {tag: 'ul', tabIndex: -1, cn: []}
107
+ {tag: 'ul', cn: []}
104
108
  }
105
109
 
106
110
  /**
@@ -494,7 +494,7 @@ class Component extends Base {
494
494
  let me = this,
495
495
  data, keyLeaf, parentModel, scope;
496
496
 
497
- if (Neo.isObject(value) && !value[Symbol.for('isRecord')]) {
497
+ if (Neo.isObject(value) && !value.isRecord) {
498
498
  Object.entries(value).forEach(([dataKey, dataValue]) => {
499
499
  me.internalSetData(`${key}.${dataKey}`, dataValue, originModel)
500
500
  })
@@ -1,5 +1,6 @@
1
1
  import Base from '../core/Base.mjs';
2
2
  import NeoArray from '../util/Array.mjs';
3
+ import NeoString from '../util/String.mjs';
3
4
  import Style from '../util/Style.mjs';
4
5
  import VNode from './VNode.mjs';
5
6
  import VNodeUtil from '../util/VNode.mjs';
@@ -577,14 +578,18 @@ class Helper extends Base {
577
578
  Object.entries(attributes).forEach(([key, value]) => {
578
579
  if (this.voidAttributes.includes(key)) {
579
580
  if (value === 'true') { // vnode attribute values get converted into strings
580
- string += ` ${key}`;
581
+ string += ` ${key}`
581
582
  }
582
583
  } else if (key !== 'removeDom') {
583
- string += ` ${key}="${value?.replaceAll?.('"', '"') ?? value}"`;
584
+ if (key === 'value') {
585
+ value = NeoString.escapeHtml(value)
586
+ }
587
+
588
+ string += ` ${key}="${value?.replaceAll?.('"', '"') ?? value}"`
584
589
  }
585
590
  });
586
591
 
587
- return string + '>';
592
+ return string + '>'
588
593
  }
589
594
 
590
595
  /**