neo.mjs 5.1.16 → 5.2.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.
@@ -20,9 +20,9 @@ class ServiceWorker extends ServiceBase {
20
20
  */
21
21
  singleton: true,
22
22
  /**
23
- * @member {String} version='5.1.16'
23
+ * @member {String} version='5.2.0'
24
24
  */
25
- version: '5.1.16'
25
+ version: '5.2.0'
26
26
  }
27
27
 
28
28
  /**
@@ -3,7 +3,8 @@ Neo.overwrites = {
3
3
  form: {
4
4
  field: {
5
5
  Text: {
6
- labelPosition_: 'inline'
6
+ labelPosition_ : 'inline',
7
+ showOptionalText_: true
7
8
  }
8
9
  }
9
10
  }
@@ -38,8 +38,14 @@ class FormContainer extends BaseFormContainer {
38
38
  cls : ['form-header-label'],
39
39
  flex : 'none'
40
40
  }, '->', {
41
+ iconCls: ['fas', 'fa-file'],
41
42
  handler: 'onValidatePageButtonClick',
42
- text : 'Validate Page'
43
+ text : 'Validate page'
44
+ }, {
45
+ iconCls: ['fas', 'fa-layer-group'],
46
+ handler: 'onValidateAllPagesButtonClick',
47
+ style : {marginLeft: '1em'},
48
+ text : 'Validate all pages'
43
49
  }]
44
50
  }, {
45
51
  module : Container,
@@ -50,27 +56,18 @@ class FormContainer extends BaseFormContainer {
50
56
  bind : {activeIndex: data => data.activeIndex}
51
57
  },
52
58
 
53
- items: [{
54
- module: () => import('./pages/Page1.mjs')
55
- }, {
56
- module: () => import('./pages/Page2.mjs')
57
- }, {
58
- module: () => import('./pages/Page3.mjs')
59
- }, {
60
- module: () => import('./pages/Page4.mjs')
61
- }, {
62
- module: () => import('./pages/Page5.mjs')
63
- }, {
64
- module: () => import('./pages/Page6.mjs')
65
- }, {
66
- module: () => import('./pages/Page7.mjs')
67
- }, {
68
- module: () => import('./pages/Page8.mjs')
69
- }, {
70
- module: () => import('./pages/Page9.mjs')
71
- }, {
72
- module: () => import('./pages/Page10.mjs')
73
- }]
59
+ items: [
60
+ {module: () => import('./pages/Page1.mjs')},
61
+ {module: () => import('./pages/Page2.mjs')},
62
+ {module: () => import('./pages/Page3.mjs')},
63
+ {module: () => import('./pages/Page4.mjs')},
64
+ {module: () => import('./pages/Page5.mjs')},
65
+ {module: () => import('./pages/Page6.mjs')},
66
+ {module: () => import('./pages/Page7.mjs')},
67
+ {module: () => import('./pages/Page8.mjs')},
68
+ {module: () => import('./pages/Page9.mjs')},
69
+ {module: () => import('./pages/Page10.mjs')}
70
+ ]
74
71
  }, {
75
72
  module: Toolbar,
76
73
  cls : ['form-footer'],
@@ -40,8 +40,9 @@ class Viewport extends BaseViewport {
40
40
  }
41
41
  }]
42
42
  }, {
43
- module: FormContainer,
44
- style : {margin: '20px'}
43
+ module : FormContainer,
44
+ reference: 'main-form',
45
+ style : {margin: '20px'}
45
46
  }],
46
47
  /**
47
48
  * @member {Object} layout={ntype:'hbox',align:'stretch'}
@@ -16,19 +16,59 @@ class ViewportController extends Component {
16
16
  /**
17
17
  * @param {Object} data
18
18
  */
19
- onValidatePageButtonClick(data) {
19
+ async onValidateAllPagesButtonClick(data) {
20
+ let me = this,
21
+ form = me.getReference('main-form'),
22
+ isValid = await form.validate(),
23
+ formValues = await form.getValues();
24
+
25
+ console.log({isValid, formValues});
26
+
27
+ await me.updateRecordValidityState()
28
+ }
29
+
30
+ /**
31
+ * @param {Object} data
32
+ */
33
+ async onValidatePageButtonClick(data) {
34
+ let me = this,
35
+ activeIndex = me.getModel().data.activeIndex,
36
+ activeCard = me.getReference('pages-container').items[activeIndex],
37
+ formValues = await activeCard.getValues();
38
+
39
+ await activeCard.validate();
40
+ await me.updateRecordValidityState(activeIndex)
41
+
42
+ console.log(`Current page: ${activeIndex + 1}`, formValues);
43
+ }
44
+
45
+ /**
46
+ * Not passing a pageIndex validates all pages
47
+ * @param {Number|null} [pageIndex]
48
+ * @returns {Promise<void>}
49
+ */
50
+ async updateRecordValidityState(pageIndex=null) {
20
51
  let me = this,
21
52
  model = me.getModel(),
22
- activeIndex = model.data.activeIndex,
23
53
  pagesContainer = me.getReference('pages-container'),
54
+ sideNav = me.getReference('side-nav'),
24
55
  store = model.getStore('sideNav'),
25
- activeCard = pagesContainer.items[activeIndex],
26
- listIndex = me.getReference('side-nav').getActiveIndex(activeIndex),
27
- isValid = activeCard.validate();
56
+ i = 0,
57
+ len = pagesContainer.items.length,
58
+ isValid, listIndex, page;
59
+
60
+ if (Neo.isNumber(pageIndex)) {
61
+ i = pageIndex;
62
+ len = pageIndex + 1;
63
+ }
28
64
 
29
- console.log(`Current page: ${activeIndex + 1}`, activeCard.getValues());
65
+ for (; i < len; i++) {
66
+ page = pagesContainer.items[i];
67
+ listIndex = sideNav.getActiveIndex(i);
68
+ isValid = await page.isValid();
30
69
 
31
- store.getAt(listIndex).isValid = isValid;
70
+ store.getAt(listIndex).isValid = isValid;
71
+ }
32
72
  }
33
73
  }
34
74
 
@@ -20,9 +20,9 @@ class ServiceWorker extends ServiceBase {
20
20
  */
21
21
  singleton: true,
22
22
  /**
23
- * @member {String} version='5.1.16'
23
+ * @member {String} version='5.2.0'
24
24
  */
25
- version: '5.1.16'
25
+ version: '5.2.0'
26
26
  }
27
27
 
28
28
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "neo.mjs",
3
- "version": "5.1.16",
3
+ "version": "5.2.0",
4
4
  "description": "The webworkers driven UI framework",
5
5
  "type": "module",
6
6
  "repository": {
@@ -40,6 +40,7 @@
40
40
  .neo-label-wrapper {
41
41
  .neo-center-border, .neo-left-border, .neo-right-border {
42
42
  border-bottom-color: v(textfield-border-color-hovered);
43
+ border-top-color : v(textfield-border-color-hovered);
43
44
  }
44
45
 
45
46
  .neo-left-border, .neo-right-border {
@@ -237,12 +237,12 @@ const DefaultConfig = {
237
237
  useVdomWorker: true,
238
238
  /**
239
239
  * buildScripts/injectPackageVersion.mjs will update this value
240
- * @default '5.1.16'
240
+ * @default '5.2.0'
241
241
  * @memberOf! module:Neo
242
242
  * @name config.version
243
243
  * @type String
244
244
  */
245
- version: '5.1.16'
245
+ version: '5.2.0'
246
246
  };
247
247
 
248
248
  Object.assign(DefaultConfig, {
@@ -31,11 +31,30 @@ class Container extends BaseContainer {
31
31
  }
32
32
 
33
33
  /**
34
- * Either pass a field id or name
34
+ * @param {Neo.container.Base} parent
35
+ * @param {Object[]} modules
36
+ * @returns {Object[]}
37
+ */
38
+ findNotLoadedModules(parent=this, modules=[]) {
39
+ parent.items.forEach(item => {
40
+ if (Neo.typeOf(item.module) === 'Function' && !item.isLoading) {
41
+ modules.push({item, parent});
42
+ } else {
43
+ item.items && this.findNotLoadedModules(item, modules);
44
+ }
45
+ });
46
+
47
+ return modules;
48
+ }
49
+
50
+ /**
51
+ * Either pass a field name or id
35
52
  * @param {String} name
36
- * @returns {Neo.form.field.Base|null} fields
53
+ * @returns {Promise<Neo.form.field.Base|null>} fields
37
54
  */
38
- getField(name) {
55
+ async getField(name) {
56
+ await this.loadModules();
57
+
39
58
  let fields = ComponentManager.getChildComponents(this),
40
59
  field;
41
60
 
@@ -51,11 +70,13 @@ class Container extends BaseContainer {
51
70
  }
52
71
 
53
72
  /**
54
- * @returns {Neo.form.field.Base[]} fields
73
+ * @returns {Promise<Neo.form.field.Base[]>} fields
55
74
  */
56
- getFields() {
75
+ async getFields() {
57
76
  let fields = [];
58
77
 
78
+ await this.loadModules();
79
+
59
80
  ComponentManager.getChildComponents(this).forEach(item => {
60
81
  item instanceof BaseField && fields.push(item);
61
82
  });
@@ -64,12 +85,13 @@ class Container extends BaseContainer {
64
85
  }
65
86
 
66
87
  /**
67
- * @returns {Object}
88
+ * @returns {Promise<Object>}
68
89
  */
69
- getSubmitValues() {
70
- let values = {};
90
+ async getSubmitValues() {
91
+ let fields = await this.getFields(),
92
+ values = {};
71
93
 
72
- this.getFields().forEach(item => {
94
+ fields.forEach(item => {
73
95
  values[item.name || item.id] = item.getSubmitValue();
74
96
  });
75
97
 
@@ -77,12 +99,13 @@ class Container extends BaseContainer {
77
99
  }
78
100
 
79
101
  /**
80
- * @returns {Object}
102
+ * @returns {Promise<Object>}
81
103
  */
82
- getValues() {
83
- let values = {};
104
+ async getValues() {
105
+ let fields = await this.getFields(),
106
+ values = {};
84
107
 
85
- this.getFields().forEach(item => {
108
+ fields.forEach(item => {
86
109
  values[item.name || item.id] = item.value;
87
110
  });
88
111
 
@@ -91,10 +114,10 @@ class Container extends BaseContainer {
91
114
 
92
115
  /**
93
116
  * Returns true in case no form field isValid() call returns false
94
- * @returns {Boolean}
117
+ * @returns {Promise<Boolean>}
95
118
  */
96
- isValid() {
97
- let fields = this.getFields(),
119
+ async isValid() {
120
+ let fields = await this.getFields(),
98
121
  i = 0,
99
122
  len = fields.length;
100
123
 
@@ -107,16 +130,35 @@ class Container extends BaseContainer {
107
130
  return true;
108
131
  }
109
132
 
133
+ /**
134
+ * Loads all not loaded items inside card layouts
135
+ * @returns {Promise<Neo.component.Base[]>}
136
+ */
137
+ async loadModules() {
138
+ let me = this,
139
+ modules = me.findNotLoadedModules(),
140
+ promises = [];
141
+
142
+ modules.forEach(module => {
143
+ promises.push(module.parent.layout.loadModule(module.item));
144
+ });
145
+
146
+ modules = await Promise.all(promises);
147
+
148
+ return modules;
149
+ }
150
+
110
151
  /**
111
152
  * Resets field values by field name or field id.
112
153
  * Fields not included with a value will get reset to null.
113
154
  * @param {Object} [values]
114
155
  */
115
- reset(values={}) {
116
- let keys = values ? Object.keys(values) : [],
156
+ async reset(values={}) {
157
+ let keys = values ? Object.keys(values) : [],
158
+ fields = await this.getFields(),
117
159
  index;
118
160
 
119
- this.getFields().forEach(item => {
161
+ fields.forEach(item => {
120
162
  index = keys.indexOf(item.name);
121
163
 
122
164
  if (index < 0) {
@@ -124,18 +166,19 @@ class Container extends BaseContainer {
124
166
  }
125
167
 
126
168
  item.reset(index > -1 ? values[keys[index]] : null);
127
- });
169
+ })
128
170
  }
129
171
 
130
172
  /**
131
173
  * Set field values by field name or field id
132
174
  * @param {Object} values={}
133
175
  */
134
- setValues(values={}) {
135
- let keys = Object.keys(values),
176
+ async setValues(values={}) {
177
+ let keys = Object.keys(values),
178
+ fields = await this.getFields(),
136
179
  index;
137
180
 
138
- this.getFields().forEach(item => {
181
+ fields.forEach(item => {
139
182
  index = keys.indexOf(item.name);
140
183
 
141
184
  if (index < 0) {
@@ -145,20 +188,28 @@ class Container extends BaseContainer {
145
188
  if (index > -1) {
146
189
  item.value = values[keys[index]];
147
190
  }
148
- });
191
+ })
149
192
  }
150
193
 
151
194
  /**
152
- * Updates the invalid state for all fields, which have updateValidationIndicators() implemented.
153
- * This can be useful for create entity forms which show up "clean", when pressing a submit button.
154
- * @returns {Boolean}
195
+ * Updates the invalid state for all fields which have validate() implemented.
196
+ * This can be useful for create-entity forms which show up "clean" until pressing a submit button.
197
+ * @returns {Promise<Boolean>}
155
198
  */
156
- validate() {
157
- this.getFields().forEach(item => {
158
- item.validate?.(false);
199
+ async validate() {
200
+ let isValid = true,
201
+ fields = await this.getFields(),
202
+ validField;
203
+
204
+ fields.forEach(item => {
205
+ validField = item.validate?.(false);
206
+
207
+ if (!validField) {
208
+ isValid = false;
209
+ }
159
210
  });
160
211
 
161
- return this.isValid();
212
+ return isValid;
162
213
  }
163
214
  }
164
215
 
@@ -570,7 +570,7 @@ class Text extends Base {
570
570
 
571
571
  me.validate(); // silent
572
572
  me.changeInputElKey('required', value ? value : null, true); // silent update
573
- me.labelText = me.labelText;
573
+ me.labelText = me.labelText; // triggers a vdom update
574
574
  }
575
575
 
576
576
  /**
@@ -786,8 +786,10 @@ class Text extends Base {
786
786
  labelOptionalText = me.labelOptionalText,
787
787
  hasOptionalText = value.endsWith(labelOptionalText);
788
788
 
789
- if (me.showOptionalText && !me.required && !hasOptionalText) {
790
- value += labelOptionalText;
789
+ if (me.showOptionalText && !me.required) {
790
+ if (!hasOptionalText) {
791
+ value += labelOptionalText;
792
+ }
791
793
  } else if (value && hasOptionalText) {
792
794
  value = value.replace(labelOptionalText, '');
793
795
  }
@@ -1086,11 +1088,13 @@ class Text extends Base {
1086
1088
  onFocusLeave(data) {
1087
1089
  let me = this,
1088
1090
  centerBorderEl = me.getCenterBorderEl(), // labelPosition: 'inline'
1089
- cls = me.cls;
1091
+ cls;
1090
1092
 
1091
1093
  if (!me.readOnly) {
1092
1094
  me.validate(); // silent
1093
1095
 
1096
+ cls = me.cls; // has to get set after validate()
1097
+
1094
1098
  NeoArray.remove(cls, 'neo-focus');
1095
1099
  me.cls = cls;
1096
1100
 
@@ -65,11 +65,10 @@ class Card extends Base {
65
65
  sCfg = me.constructor,
66
66
  needsUpdate = false,
67
67
  removeInactiveCards = me.removeInactiveCards,
68
- i, isActiveIndex, item, items, len, module, proto, vdom, wrapperCls;
68
+ i, isActiveIndex, item, items, len, module, wrapperCls;
69
69
 
70
70
  if (Neo.isNumber(value) && container) {
71
71
  items = container.items;
72
- vdom = container.vdom;
73
72
  len = items.length;
74
73
 
75
74
  if (!items[value]) {
@@ -80,7 +79,7 @@ class Card extends Base {
80
79
  for (i=0; i < len; i++) {
81
80
  module = items[i].module;
82
81
 
83
- if (i === value && !module?.isClass && Neo.isFunction(module)) {
82
+ if (i === value && Neo.typeOf(module) === 'Function') {
84
83
  needsUpdate = true;
85
84
  break;
86
85
  }
@@ -91,23 +90,8 @@ class Card extends Base {
91
90
  item = items[i];
92
91
  module = item.module;
93
92
 
94
- if (isActiveIndex && !module?.isClass && Neo.isFunction(module)) {
95
- module = await module();
96
- module = module.default;
97
- proto = module.prototype;
98
- wrapperCls = item.wrapperCls || proto.constructor.config.wrapperCls || [];
99
-
100
- item.className = proto.className;
101
- item.wrapperCls = [...wrapperCls, sCfg.itemCls];
102
- item.module = module;
103
-
104
- delete item.vdom;
105
-
106
- items[i] = item = Neo.create(item);
107
-
108
- container.fire('cardLoaded', {item});
109
-
110
- vdom.cn[i] = item.vdom;
93
+ if (isActiveIndex && Neo.typeOf(module) === 'Function') {
94
+ item = await me.loadModule(item, i);
111
95
  }
112
96
 
113
97
  if (item instanceof Neo.core.Base) {
@@ -180,6 +164,53 @@ class Card extends Base {
180
164
  container.wrapperCls = wrapperCls;
181
165
  }
182
166
 
167
+ /**
168
+ * Loads a component.Base module which is defined via module: () => import('...')
169
+ * @param {Object} item
170
+ * @param {Number} [index]
171
+ * @returns {Neo.component.Base}
172
+ */
173
+ async loadModule(item, index) {
174
+ let me = this,
175
+ containerId = me.containerId,
176
+ container = Neo.getComponent(containerId) || Neo.get(containerId), // the instance might not be registered yet
177
+ items = container.items,
178
+ sCfg = me.constructor,
179
+ vdom = container.vdom,
180
+ module = item.module,
181
+ proto, wrapperCls;
182
+
183
+ if (!Neo.isNumber(index)) {
184
+ index = items.indexOf(item);
185
+ }
186
+
187
+ item.isLoading = true; // prevent the item from getting queued multiple times inside form.Container
188
+
189
+ module = await module();
190
+ module = module.default;
191
+ proto = module.prototype;
192
+ wrapperCls = item.wrapperCls || proto.constructor.config.wrapperCls || [];
193
+
194
+ item.className = proto.className;
195
+ item.wrapperCls = [...wrapperCls, sCfg.itemCls];
196
+ item.module = module;
197
+
198
+ delete item.isLoading;
199
+ delete item.vdom;
200
+
201
+ items[index] = item = Neo.create(item);
202
+
203
+ if (me.removeInactiveCards) {
204
+ item.vdom.removeDom = true;
205
+ }
206
+
207
+ container.fire('cardLoaded', {item});
208
+
209
+ vdom.cn[index] = item.vdom;
210
+
211
+ return item;
212
+ }
213
+
183
214
  /**
184
215
  * Removes all CSS rules from the container this layout is bound to.
185
216
  * Gets called when switching to a different layout.