neo.mjs 5.0.1 → 5.0.3

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.
@@ -0,0 +1,154 @@
1
+ # neo.mjs Coding Guidelines
2
+
3
+ Inside the neo repo the following coding guidelines are mandatory.
4
+ They ensure a high code quality and consistency.
5
+ We strongly recommend to also stick to them when creating your own workspaces and apps via `npx neo-app`.
6
+
7
+ ## Content
8
+ 1. Import statements
9
+ 2. Anatomy of a neo class / JS module
10
+
11
+
12
+ ## 1. import statements
13
+ ```javascript
14
+ import Container from '../../../node_modules/neo.mjs/src/container/Base.mjs';
15
+ import EarthquakesTable from './earthquakes/Table.mjs';
16
+ import GoogleMapsComponent from '../../../node_modules/neo.mjs/src/component/wrapper/GoogleMaps.mjs';
17
+ import Toast from '../../../node_modules/neo.mjs/src/component/Toast.mjs';
18
+ import ViewController from './MainViewController.mjs';
19
+ import ViewModel from './MainViewModel.mjs';
20
+ ```
21
+ * Use block formatting. This makes it easy to spot invalid paths.
22
+ * Use single quotes.
23
+ * Imports get sorted by module name. There are ongoing discussions if we should switch to a path based sorting instead.
24
+ * Feel free to rename module import names as needed. Neo classes are using the default export.
25
+
26
+ ## 2. Anatomy of a neo class / JS module
27
+ ```javascript
28
+ import Component from '../component/Base.mjs';
29
+ import NeoArray from '../util/Array.mjs';
30
+
31
+ /**
32
+ * @class Neo.button.Base
33
+ * @extends Neo.component.Base
34
+ */
35
+ class Base extends Component {
36
+ /**
37
+ * Valid values for badgePosition
38
+ * @member {String[]} badgePositions=['bottom-left','bottom-right','top-left','top-right']
39
+ * @protected
40
+ * @static
41
+ */
42
+ static badgePositions = ['bottom-left', 'bottom-right', 'top-left', 'top-right']
43
+ /**
44
+ * Valid values for iconPosition
45
+ * @member {String[]} iconPositions=['top','right','bottom','left']
46
+ * @protected
47
+ * @static
48
+ */
49
+ static iconPositions = ['top', 'right', 'bottom', 'left']
50
+
51
+ static config = {
52
+ /**
53
+ * @member {String} className='Neo.button.Base'
54
+ * @protected
55
+ */
56
+ className: 'Neo.button.Base',
57
+ /**
58
+ * @member {String} ntype='button'
59
+ * @protected
60
+ */
61
+ ntype: 'button',
62
+ /**
63
+ * Change the browser hash value on click
64
+ * @member {String|null} route_=null
65
+ */
66
+ route_: null,
67
+ /**
68
+ * True adds an expanding circle on click
69
+ * @member {Boolean} useRippleEffect_=true
70
+ */
71
+ useRippleEffect_: true,
72
+ /**
73
+ * @member {Object} _vdom
74
+ */
75
+ _vdom:
76
+ {tag: 'button', type: 'button', cn: [
77
+ {tag: 'span', cls: ['neo-button-glyph']},
78
+ {tag: 'span', cls: ['neo-button-text']},
79
+ {cls: ['neo-button-badge']},
80
+ {cls: ['neo-button-ripple-wrapper'], cn: [
81
+ {cls: ['neo-button-ripple']}
82
+ ]}
83
+ ]}
84
+ }
85
+
86
+ /**
87
+ * Time in ms for the ripple effect when clicking on the button.
88
+ * Only active if useRippleEffect is set to true.
89
+ * @member {Number} rippleEffectDuration=400
90
+ */
91
+ rippleEffectDuration = 400
92
+ /**
93
+ * Internal flag to store the last setTimeout() id for ripple effect remove node callbacks
94
+ * @member {Number} #rippleTimeoutId=null
95
+ * @private
96
+ */
97
+ #rippleTimeoutId = null
98
+
99
+ /**
100
+ * Triggered after the route config got changed
101
+ * @param {String} value
102
+ * @param {String} oldValue
103
+ * @protected
104
+ */
105
+ afterSetRoute(value, oldValue) {
106
+ let me = this;
107
+
108
+ value && me.addDomListeners({
109
+ click: me.changeRoute,
110
+ scope: me
111
+ });
112
+ }
113
+
114
+ /**
115
+ * Triggered before the iconPosition config gets changed
116
+ * @param {String} value
117
+ * @param {String} oldValue
118
+ * @protected
119
+ */
120
+ beforeSetIconPosition(value, oldValue) {
121
+ return this.beforeSetEnumValue(value, oldValue, 'iconPosition');
122
+ }
123
+
124
+ /**
125
+ * Convenience shortcut
126
+ * @returns {Object}
127
+ */
128
+ getIconNode() {
129
+ return this.getVdomRoot().cn[0];
130
+ }
131
+ }
132
+
133
+ Neo.applyClassConfig(Base);
134
+
135
+ export default Base;
136
+
137
+ ```
138
+ * Use JSDoc based comments for all top level items as well as top level configs
139
+ * Class content order:
140
+ - static configs (ordered chronologically)
141
+ - static config as the last item. This one does not need a comment, but is prefixed with an empty line.
142
+ - non-static class fields (ordered chronologically)
143
+ - construct() in case you are using it
144
+ - all other class methods are ordered chronologically
145
+ * Module order:
146
+ - Import statements formatted according to 1.
147
+ - empty line
148
+ - class definition
149
+ - empty line
150
+ - Neo.applyClassConfig(<ClassName>)
151
+ - empty line
152
+ - export statement
153
+ - empty line
154
+
@@ -8,12 +8,6 @@ import ServiceBase from '../src/worker/ServiceBase.mjs';
8
8
  * @singleton
9
9
  */
10
10
  class ServiceWorker extends ServiceBase {
11
- /**
12
- * @member {String} workerId='service'
13
- * @protected
14
- */
15
- workerId = 'service'
16
-
17
11
  static config = {
18
12
  /**
19
13
  * @member {String} className='Neo.ServiceWorker'
@@ -26,16 +20,18 @@ class ServiceWorker extends ServiceBase {
26
20
  */
27
21
  singleton: true,
28
22
  /**
29
- * @member {String} version='5.0.0'
23
+ * @member {String} version='5.0.3'
30
24
  */
31
- version: '5.0.0'
25
+ version: '5.0.3'
32
26
  }
33
- }
34
27
 
35
- Neo.applyClassConfig(ServiceWorker);
36
-
37
- let instance = Neo.create(ServiceWorker);
28
+ /**
29
+ * @member {String} workerId='service'
30
+ * @protected
31
+ */
32
+ workerId = 'service'
33
+ }
38
34
 
39
- Neo.applyToGlobalNs(instance);
35
+ let instance = Neo.applyClassConfig(ServiceWorker);
40
36
 
41
37
  export default instance;
@@ -20,7 +20,7 @@ class AttributionComponent extends Component {
20
20
  * @member {Object} vdom
21
21
  */
22
22
  vdom:
23
- {tag: 'div', style: {margin: '20px'}, cn: [
23
+ {style: {margin: '20px'}, cn: [
24
24
  {tag: 'h2', html: 'Attribution'},
25
25
  {tag: 'ul', cn: [
26
26
  {tag: 'li', html: 'The logos were created by <a target="_blank" href="https://www.driefmeier.com/">Sebastian Driefmeier</a>. Thank you!'},
@@ -309,17 +309,15 @@ class MainContainerController extends ComponentController {
309
309
  let table = this.getReference('table');
310
310
 
311
311
  table.vdom.cn[0].cn[1].cn.push({
312
- tag : 'div',
313
- cls : ['neo-box-label', 'neo-label'],
312
+ cls : ['neo-box-label', 'neo-label'],
313
+ style: {margin: '20px'},
314
+
314
315
  html: [
315
316
  'Summary data did not arrive after 2s.</br>',
316
317
  'Please double-check if the API is offline:</br></br>',
317
318
  '<a target="_blank" href="https://disease.sh/all">NovelCOVID/API all endpoint</a></br></br>',
318
319
  'and if so please try again later.'
319
- ].join(''),
320
- style: {
321
- margin: '20px'
322
- }
320
+ ].join('')
323
321
  });
324
322
 
325
323
  table.update();
@@ -38,7 +38,7 @@ class CountryGallery extends Gallery {
38
38
  itemTpl:
39
39
  {cls: ['neo-gallery-item', 'image-wrap', 'view', 'neo-transition-1000'], tabIndex: '-1', cn: [
40
40
  {cls: ['neo-item-wrapper'], style: {}, cn: [
41
- {tag: 'div', cls: ['neo-country-gallery-item'], style: {}, cn: [
41
+ {cls: ['neo-country-gallery-item'], style: {}, cn: [
42
42
  {cls: ['neo-item-header'], cn: [
43
43
  {tag: 'img', cls: ['neo-flag']},
44
44
  {}
@@ -38,7 +38,7 @@ class CountryHelix extends Helix {
38
38
  itemTpl:
39
39
  {cls: ['surface', 'neo-helix-item'], style: {}, tabIndex: '-1', cn: [
40
40
  {cls: ['neo-item-wrapper'], style: {}, cn: [
41
- {tag: 'div', cls: ['neo-country-helix-item'], style: {}, cn: [
41
+ {cls: ['neo-country-helix-item'], style: {}, cn: [
42
42
  {cls: ['neo-item-header'], cn: [
43
43
  {tag: 'img', cls: ['neo-flag']},
44
44
  {}
@@ -20,7 +20,7 @@ class AttributionComponent extends Component {
20
20
  * @member {Object} vdom
21
21
  */
22
22
  vdom:
23
- {tag: 'div', style: {margin: '20px'}, cn: [
23
+ {style: {margin: '20px'}, cn: [
24
24
  {tag: 'h2', html: 'Attribution'},
25
25
  {tag: 'ul', cn: [
26
26
  {tag: 'li', html: 'The logos were created by <a target="_blank" href="https://www.driefmeier.com/">Sebastian Driefmeier</a>. Thank you!'},
@@ -494,17 +494,15 @@ class MainContainerController extends ComponentController {
494
494
  const table = this.getReference('table');
495
495
 
496
496
  table.vdom.cn[0].cn[1].cn.push({
497
- tag : 'div',
498
497
  cls : ['neo-box-label', 'neo-label'],
499
- html : [
498
+ style: {margin: '20px'},
499
+
500
+ html: [
500
501
  'Summary data did not arrive after 2s.</br>',
501
502
  'Please double-check if the API is offline:</br></br>',
502
503
  '<a target="_blank" href="https://disease.sh/all">NovelCOVID/API all endpoint</a></br></br>',
503
504
  'and if so please try again later.'
504
- ].join(''),
505
- style: {
506
- margin: '20px'
507
- }
505
+ ].join('')
508
506
  });
509
507
 
510
508
  table.update();
@@ -35,60 +35,40 @@ class CountryGallery extends Gallery {
35
35
  /**
36
36
  * @member {Object} itemTpl_
37
37
  */
38
- itemTpl: {
39
- cls : ['neo-gallery-item', 'image-wrap', 'view', 'neo-transition-1000'],
40
- tabIndex: '-1',
41
- cn: [{
42
- cls : ['neo-item-wrapper'],
43
- style: {},
44
- cn: [{
45
- tag : 'div',
46
- cls : ['neo-country-gallery-item'],
47
- style: {},
48
-
49
- cn: [{
50
- cls: ['neo-item-header'],
51
- cn: [{
52
- tag: 'img',
53
- cls: ['neo-flag']
54
- }, {
55
-
56
- }]
57
- }, {
58
- tag: 'table',
59
- cls: ['neo-content-table'],
60
- cn : [{
61
- tag: 'tr',
62
- cn : [
63
- {tag: 'td', html: 'Cases'},
64
- {tag: 'td', cls: ['neo-align-right']},
65
- {tag: 'td', style: {width: '100%'}},
66
- {tag: 'td', html: 'Cases today'},
67
- {tag: 'td', cls: ['neo-align-right']}
68
- ]
69
- }, {
70
- tag: 'tr',
71
- cn : [
72
- {tag: 'td', html: 'Deaths'},
73
- {tag: 'td', cls: ['neo-align-right', 'neo-content-deaths']},
74
- {tag: 'td', style: {width: '100%'}},
75
- {tag: 'td', html: 'Deaths today'},
76
- {tag: 'td', cls: ['neo-align-right', 'neo-content-deaths']}
77
- ]
78
- }, {
79
- tag: 'tr',
80
- cn : [
81
- {tag: 'td', html: 'Recovered'},
82
- {tag: 'td', cls: ['neo-align-right', 'neo-content-recovered']},
83
- {tag: 'td', style: {width: '100%'}},
84
- {tag: 'td', html: 'Critical'},
85
- {tag: 'td', cls: ['neo-align-right', 'neo-content-critical']}
86
- ]
87
- }]
88
- }]
89
- }]
90
- }]
91
- },
38
+ itemTpl:
39
+ {cls: ['neo-gallery-item', 'image-wrap', 'view', 'neo-transition-1000'], tabIndex: '-1', cn: [
40
+ {cls: ['neo-item-wrapper'], style: {}, cn: [
41
+ {cls: ['neo-country-gallery-item'], style: {}, cn: [
42
+ {cls: ['neo-item-header'], cn: [
43
+ {tag: 'img', cls: ['neo-flag']},
44
+ {}
45
+ ]},
46
+ {tag: 'table', cls: ['neo-content-table'], cn: [
47
+ {tag: 'tr', cn: [
48
+ {tag: 'td', html: 'Cases'},
49
+ {tag: 'td', cls: ['neo-align-right']},
50
+ {tag: 'td', style: {width: '100%'}},
51
+ {tag: 'td', html: 'Cases today'},
52
+ {tag: 'td', cls: ['neo-align-right']}
53
+ ]},
54
+ {tag: 'tr', cn: [
55
+ {tag: 'td', html: 'Deaths'},
56
+ {tag: 'td', cls: ['neo-align-right', 'neo-content-deaths']},
57
+ {tag: 'td', style: {width: '100%'}},
58
+ {tag: 'td', html: 'Deaths today'},
59
+ {tag: 'td', cls: ['neo-align-right', 'neo-content-deaths']}
60
+ ]},
61
+ {tag: 'tr', cn: [
62
+ {tag: 'td', html: 'Recovered'},
63
+ {tag: 'td', cls: ['neo-align-right', 'neo-content-recovered']},
64
+ {tag: 'td', style: {width: '100%'}},
65
+ {tag: 'td', html: 'Critical'},
66
+ {tag: 'td', cls: ['neo-align-right', 'neo-content-critical']}
67
+ ]}
68
+ ]}
69
+ ]}
70
+ ]}
71
+ ]},
92
72
  /**
93
73
  * The item width of the gallery
94
74
  * @member {Number} itemWidth=320
@@ -38,7 +38,7 @@ class CountryHelix extends Helix {
38
38
  itemTpl:
39
39
  {cls: ['surface', 'neo-helix-item'], style: {}, tabIndex: '-1', cn: [
40
40
  {cls: ['neo-item-wrapper'], style: {}, cn: [
41
- {tag: 'div', cls: ['neo-country-helix-item'], style: {}, cn: [
41
+ {cls: ['neo-country-helix-item'], style: {}, cn: [
42
42
  {cls: ['neo-item-header'], cn: [
43
43
  {tag: 'img', cls: ['neo-flag']},
44
44
  {}
@@ -1,4 +1,17 @@
1
1
  [
2
+ {
3
+ "author" : "Torsten Dinkheller",
4
+ "authorImage" : "author_TorstenDinkheller.jpg",
5
+ "date" : "Jan 28, 2023",
6
+ "id" : 56,
7
+ "image" : "toast-tutorial-medium.jpg",
8
+ "name" : "Reusable Next Gen Toast — to Industry Standard",
9
+ "provider" : "Medium",
10
+ "publisher" : "ITNEXT",
11
+ "selectedInto": [],
12
+ "type" : "Blog Post",
13
+ "url" : "https://itnext.io/reusable-next-gen-toast-to-industry-standard-502d2950701f?source=friends_link&sk=f74ba5b5161986d9d8dec6e91ba11a5b"
14
+ },
2
15
  {
3
16
  "author" : "Tobias Uhlig",
4
17
  "authorImage" : "author_TobiasUhlig.jpeg",
@@ -7,10 +20,10 @@
7
20
  "image" : "the-next-generation-of-front-end-development.png",
8
21
  "name" : "The Next Generation of Front-end Development",
9
22
  "provider" : "Medium",
10
- "publisher" : "",
23
+ "publisher" : "ITNEXT",
11
24
  "selectedInto": [],
12
25
  "type" : "Blog Post",
13
- "url" : "https://tobiasuhlig.medium.com/the-next-generation-of-front-end-development-65887c59b173?source=friends_link&sk=51654f60033120e84454791015d8d205"
26
+ "url" : "https://itnext.io/the-next-generation-of-front-end-development-65887c59b173?source=friends_link&sk=51654f60033120e84454791015d8d205"
14
27
  },
15
28
  {
16
29
  "author" : "Tobias Uhlig",
@@ -8,12 +8,6 @@ import ServiceBase from '../src/worker/ServiceBase.mjs';
8
8
  * @singleton
9
9
  */
10
10
  class ServiceWorker extends ServiceBase {
11
- /**
12
- * @member {String} workerId='service'
13
- * @protected
14
- */
15
- workerId = 'service'
16
-
17
11
  static config = {
18
12
  /**
19
13
  * @member {String} className='Neo.ServiceWorker'
@@ -26,16 +20,18 @@ class ServiceWorker extends ServiceBase {
26
20
  */
27
21
  singleton: true,
28
22
  /**
29
- * @member {String} version='5.0.0'
23
+ * @member {String} version='5.0.3'
30
24
  */
31
- version: '5.0.0'
25
+ version: '5.0.3'
32
26
  }
33
- }
34
27
 
35
- Neo.applyClassConfig(ServiceWorker);
36
-
37
- let instance = Neo.create(ServiceWorker);
28
+ /**
29
+ * @member {String} workerId='service'
30
+ * @protected
31
+ */
32
+ workerId = 'service'
33
+ }
38
34
 
39
- Neo.applyToGlobalNs(instance);
35
+ let instance = Neo.applyClassConfig(ServiceWorker);
40
36
 
41
37
  export default instance;
@@ -64,7 +64,7 @@ class CountryGallery extends Gallery {
64
64
 
65
65
  return {cls, id: me.getItemVnodeId(record[me.keyProperty]), tabIndex: '-1', cn: [
66
66
  {cls: ['neo-item-wrapper'], style: {height: me.itemHeight + 'px'}, cn: [
67
- {tag: 'div', cls: ['neo-country-gallery-item'], style, cn: [
67
+ {cls: ['neo-country-gallery-item'], style, cn: [
68
68
  {cls: ['neo-item-header'], cn: [
69
69
  {tag: 'img', cls: ['neo-flag'], src: me.getCountryFlagUrl(record.country)},
70
70
  {html: record.country}
@@ -35,7 +35,7 @@ class CountryHelix extends Helix {
35
35
  itemTpl:
36
36
  {cls: ['surface', 'neo-helix-item'], style: {}, tabIndex: '-1', cn: [
37
37
  {cls: ['neo-item-wrapper'], style: {}, cn: [
38
- {tag: 'div', cls : ['neo-country-helix-item'], style: {}, cn: [
38
+ {cls: ['neo-country-helix-item'], style: {}, cn: [
39
39
  {cls: ['neo-item-header'], cn: [
40
40
  {tag: 'img', cls: ['neo-flag']},
41
41
  {}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "neo.mjs",
3
- "version": "5.0.1",
3
+ "version": "5.0.3",
4
4
  "description": "The webworkers driven UI framework",
5
5
  "type": "module",
6
6
  "repository": {
@@ -65,9 +65,10 @@
65
65
  }
66
66
 
67
67
  .website-header-buttons {
68
- position: absolute;
69
- right : 0;
70
- top : 0;
68
+ background-color: transparent;
69
+ position : absolute;
70
+ right : 0;
71
+ top : 0;
71
72
  }
72
73
 
73
74
  @media (max-height: 400px), (max-width: 600px) {
@@ -132,4 +133,4 @@
132
133
  margin-right: 5px;
133
134
  }
134
135
  }
135
- }
136
+ }
@@ -237,12 +237,12 @@ const DefaultConfig = {
237
237
  useVdomWorker: true,
238
238
  /**
239
239
  * buildScripts/injectPackageVersion.mjs will update this value
240
- * @default '5.0.0'
240
+ * @default '5.0.3'
241
241
  * @memberOf! module:Neo
242
242
  * @name config.version
243
243
  * @type String
244
244
  */
245
- version: '5.0.0'
245
+ version: '5.0.3'
246
246
  };
247
247
 
248
248
  Object.assign(DefaultConfig, {
@@ -1,4 +1,4 @@
1
1
  fetch('./neo-config.json').then(r => r.json()).then(d => {
2
- self.Neo = {config: {...d}};
2
+ globalThis.Neo = {config: {...d}};
3
3
  import(d.mainPath);
4
4
  });
package/src/Neo.mjs CHANGED
@@ -48,15 +48,16 @@ Neo = globalThis.Neo = Object.assign({
48
48
  * @tutorial 02_ClassSystem
49
49
  */
50
50
  applyClassConfig(cls) {
51
- let baseCfg = null,
52
- proto = cls.prototype || cls,
53
- protos = [],
51
+ let baseCfg = null,
52
+ ntypeMap = Neo.ntypeMap,
53
+ proto = cls.prototype || cls,
54
+ protos = [],
54
55
  config, ctor, overrides;
55
56
 
56
57
  while (proto.__proto__) {
57
58
  ctor = proto.constructor;
58
59
 
59
- if (ctor.hasOwnProperty('classConfigApplied')) {
60
+ if (Object.hasOwn(ctor, 'classConfigApplied')) {
60
61
  baseCfg = Neo.clone(ctor.config, true);
61
62
  break;
62
63
  }
@@ -73,40 +74,40 @@ Neo = globalThis.Neo = Object.assign({
73
74
  let cfg = ctor.config || {},
74
75
  mixins;
75
76
 
76
- if (cfg) {
77
- Object.entries(cfg).forEach(([key, value]) => {
78
- if (key.slice(-1) === '_') {
79
- delete cfg[key];
80
- key = key.slice(0, -1);
81
- cfg[key] = value;
82
- autoGenerateGetSet(element, key);
83
- }
77
+ Object.entries(cfg).forEach(([key, value]) => {
78
+ if (key.slice(-1) === '_') {
79
+ delete cfg[key];
80
+ key = key.slice(0, -1);
81
+ cfg[key] = value;
82
+ autoGenerateGetSet(element, key);
83
+ }
84
84
 
85
- // only apply properties which have no setters inside the prototype chain
86
- // those will get applied on create (Neo.core.Base -> initConfig)
87
- else if (!Neo.hasPropertySetter(element, key)) {
88
- Object.defineProperty(element, key, {
89
- enumerable: true,
90
- value,
91
- writable : true
92
- });
93
- }
94
- });
95
- }
85
+ // only apply properties which have no setters inside the prototype chain
86
+ // those will get applied on create (Neo.core.Base -> initConfig)
87
+ else if (!Neo.hasPropertySetter(element, key)) {
88
+ Object.defineProperty(element, key, {
89
+ enumerable: true,
90
+ value,
91
+ writable : true
92
+ });
93
+ }
94
+ });
96
95
 
97
- if (cfg.hasOwnProperty('ntype')) {
98
- Neo.ntypeMap[cfg.ntype] = cfg.className;
99
- }
96
+ if (Object.hasOwn(cfg, 'ntype')) {
97
+ if (Object.hasOwn(ntypeMap, cfg.ntype)) {
98
+ throw new Error(`ntype conflict for '${cfg.ntype}' inside the classes:\n${ntypeMap[cfg.ntype]}\n${cfg.className}`);
99
+ }
100
100
 
101
- mixins = config.hasOwnProperty('mixins') && config.mixins || [];
101
+ ntypeMap[cfg.ntype] = cfg.className;
102
+ }
102
103
 
103
- let foo = false;
104
+ mixins = Object.hasOwn(config, 'mixins') && config.mixins || [];
104
105
 
105
106
  if (ctor.observable) {
106
107
  mixins.push('Neo.core.Observable');
107
108
  }
108
109
 
109
- if (cfg.hasOwnProperty('mixins') && Array.isArray(cfg.mixins) && cfg.mixins.length > 0) {
110
+ if (Object.hasOwn(cfg, 'mixins') && Array.isArray(cfg.mixins) && cfg.mixins.length > 0) {
110
111
  mixins.push(...cfg.mixins);
111
112
  }
112
113
 
@@ -208,7 +209,7 @@ Neo = globalThis.Neo = Object.assign({
208
209
  assignDefaults(target, defaults) {
209
210
  if (target && Neo.typeOf(defaults) === 'Object') {
210
211
  Object.entries(defaults).forEach(([key, value]) => {
211
- if (!target.hasOwnProperty(key)) {
212
+ if (!Object.hasOwn(target, key)) {
212
213
  target[key] = value;
213
214
  }
214
215
  });
@@ -547,7 +548,7 @@ function autoGenerateGetSet(proto, key) {
547
548
  get() {
548
549
  let me = this,
549
550
  beforeGet = `beforeGet${key[0].toUpperCase() + key.slice(1)}`,
550
- hasNewKey = me[configSymbol].hasOwnProperty(key),
551
+ hasNewKey = Object.hasOwn(me[configSymbol], key),
551
552
  newKey = me[configSymbol][key],
552
553
  value = hasNewKey ? newKey : me['_' + key];
553
554
 
package/src/core/Base.mjs CHANGED
@@ -68,7 +68,6 @@ class Base {
68
68
  }
69
69
 
70
70
  /**
71
- * Consumes the static getConfig() return object.
72
71
  * Applies the observable mixin if needed, grants remote access if needed.
73
72
  * @param {Object} config={}
74
73
  */
package/src/grid/View.mjs CHANGED
@@ -1,4 +1,5 @@
1
1
  import Component from '../component/Base.mjs';
2
+ import VDomUtil from '../util/VDom.mjs';
2
3
 
3
4
  /**
4
5
  * @class Neo.grid.View
@@ -175,6 +176,33 @@ class View extends Component {
175
176
  return this.id + '__' + record[this.store.keyProperty] + '__' + field;
176
177
  }
177
178
 
179
+ /**
180
+ * Get the matching record by passing a row id, a cell id or an id inside a table cell.
181
+ * @param {String} nodeId
182
+ * @returns {Object|null}
183
+ */
184
+ getRecord(nodeId) {
185
+ let me = this,
186
+ record = me.getRecordByRowId(nodeId),
187
+ node, parentNodes;
188
+
189
+ if (record) {
190
+ return record;
191
+ }
192
+
193
+ parentNodes = VDomUtil.getParentNodes(me.vdom, nodeId);
194
+
195
+ for (node of parentNodes) {
196
+ record = me.getRecordByRowId(node.id);
197
+
198
+ if (record) {
199
+ return record;
200
+ }
201
+ }
202
+
203
+ return null;
204
+ }
205
+
178
206
  /**
179
207
  * @param {String} rowId
180
208
  * @returns {Object}
@@ -1,4 +1,5 @@
1
1
  import Component from '../component/Base.mjs';
2
+ import VDomUtil from '../util/VDom.mjs';
2
3
 
3
4
  /**
4
5
  * @class Neo.table.View
@@ -201,6 +202,33 @@ class View extends Component {
201
202
  return this.id + '__' + record[this.store.keyProperty] + '__' + dataField;
202
203
  }
203
204
 
205
+ /**
206
+ * Get the matching record by passing a row id, a cell id or an id inside a table cell.
207
+ * @param {String} nodeId
208
+ * @returns {Object|null}
209
+ */
210
+ getRecord(nodeId) {
211
+ let me = this,
212
+ record = me.getRecordByRowId(nodeId),
213
+ node, parentNodes;
214
+
215
+ if (record) {
216
+ return record;
217
+ }
218
+
219
+ parentNodes = VDomUtil.getParentNodes(me.vdom, nodeId);
220
+
221
+ for (node of parentNodes) {
222
+ record = me.getRecordByRowId(node.id);
223
+
224
+ if (record) {
225
+ return record;
226
+ }
227
+ }
228
+
229
+ return null;
230
+ }
231
+
204
232
  /**
205
233
  * @param {String} rowId
206
234
  * @returns {Object}
@@ -1,3 +0,0 @@
1
- .neo-radiofield {
2
-
3
- }