neo.mjs 6.10.6 → 6.10.7

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.6'
23
+ * @member {String} version='6.10.7'
24
24
  */
25
- version: '6.10.6'
25
+ version: '6.10.7'
26
26
  }
27
27
 
28
28
  /**
@@ -2,9 +2,13 @@ Neo.overwrites = {
2
2
  Neo: {
3
3
  form: {
4
4
  field: {
5
+ CheckBox: {
6
+ useAlertState_: true
7
+ },
5
8
  Text: {
6
9
  labelPosition_ : 'inline',
7
- showOptionalText_: true
10
+ showOptionalText_: true,
11
+ useAlertState_ : true
8
12
  }
9
13
  }
10
14
  }
@@ -24,6 +24,7 @@ class LivePreview extends Base {
24
24
  items: [{
25
25
  module : TabContainer,
26
26
  reference: 'tab-container',
27
+ cls: 'live-preview-container',
27
28
 
28
29
  items: [{
29
30
  module: TextArea,
@@ -20,9 +20,9 @@ class ServiceWorker extends ServiceBase {
20
20
  */
21
21
  singleton: true,
22
22
  /**
23
- * @member {String} version='6.10.6'
23
+ * @member {String} version='6.10.7'
24
24
  */
25
- version: '6.10.6'
25
+ version: '6.10.7'
26
26
  }
27
27
 
28
28
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "neo.mjs",
3
- "version": "6.10.6",
3
+ "version": "6.10.7",
4
4
  "description": "The webworkers driven UI framework",
5
5
  "type": "module",
6
6
  "repository": {
@@ -5,3 +5,57 @@
5
5
  margin: 0 2px;
6
6
  }
7
7
  }
8
+
9
+
10
+
11
+
12
+ // Live preview Container
13
+ .live-preview-container {
14
+
15
+ .neo-tab-header-toolbar {
16
+ background-color: var(--sem-color-bg-neutral-default);
17
+ padding: var(--cmp-tab-strip-height);
18
+ border: 1px solid #F1F1F1;
19
+ border-top-left-radius: calc(var(--cmp-tab-strip-height) + var(--cmp-button-borderradius));
20
+ border-top-right-radius: calc(var(--cmp-tab-strip-height) + var(--cmp-button-borderradius));
21
+ }
22
+
23
+ .neo-tab-strip {
24
+ margin-top: calc(var(--cmp-tab-strip-height) * (-1) - 1px);
25
+ }
26
+
27
+ .neo-tab-content-container {
28
+ background-color: transparent;
29
+ border: 1px solid #F1F1F1;
30
+ border-top: 0;
31
+ border-bottom-left-radius: calc(var(--cmp-tab-strip-height) + var(--cmp-button-borderradius));
32
+ border-bottom-right-radius: calc(var(--cmp-tab-strip-height) + var(--cmp-button-borderradius));
33
+ }
34
+ }
35
+
36
+ // Scrollbar Behaviour
37
+ ::-webkit-scrollbar {
38
+ width: 6px;
39
+ }
40
+
41
+ ::-webkit-scrollbar-track {
42
+ background: transparent;
43
+ }
44
+
45
+ ::-webkit-scrollbar-thumb {
46
+ background: transparent;
47
+ }
48
+
49
+ .neo-tree-list:hover {
50
+ ::-webkit-scrollbar-thumb {
51
+ background: #b2b2b2;
52
+ border-radius: 100px;
53
+ }
54
+ }
55
+
56
+ .learn-content:hover {
57
+ ::-webkit-scrollbar-thumb {
58
+ background: #b2b2b2;
59
+ border-radius: 100px;
60
+ }
61
+ }
@@ -57,6 +57,12 @@
57
57
  .neo-checkbox-icon {
58
58
  color: var(--textfield-border-color-invalid);
59
59
  }
60
+
61
+ &.neo-use-alert-state {
62
+ .neo-checkbox-icon {
63
+ color: var(--textfield-border-color-alert);
64
+ }
65
+ }
60
66
  }
61
67
 
62
68
  &.neo-label-top {
@@ -78,4 +84,10 @@
78
84
  flex: 1 0 1px;
79
85
  }
80
86
  }
87
+
88
+ &.neo-use-alert-state {
89
+ .neo-error {
90
+ color: var(--textfield-border-color-alert);
91
+ }
92
+ }
81
93
  }
@@ -83,6 +83,50 @@
83
83
  }
84
84
  }
85
85
 
86
+ &.neo-empty-required {
87
+ .neo-input-wrapper {
88
+ border-color: var(--textfield-border-color-alert);
89
+ }
90
+
91
+ &.label-inline {
92
+ .neo-label-wrapper {
93
+ .neo-center-border, .neo-left-border, .neo-right-border {
94
+ border-bottom-color: var(--textfield-border-color-alert);
95
+ }
96
+
97
+ .neo-left-border, .neo-right-border {
98
+ border-top-color: var(--textfield-border-color-alert);
99
+ }
100
+
101
+ .neo-left-border {
102
+ border-left-color: var(--textfield-border-color-alert);
103
+ }
104
+
105
+ .neo-right-border {
106
+ border-right-color: var(--textfield-border-color-alert);
107
+ }
108
+ }
109
+ }
110
+
111
+ .neo-textfield-error {
112
+ color: var(--textfield-border-color-alert);
113
+ }
114
+
115
+ &:not(.label-inline, .neo-input-wrapper) {
116
+ .neo-textfield-input {
117
+ border-color: var(--textfield-border-color-alert);
118
+ }
119
+ }
120
+
121
+ &.neo-invalid:not(.neo-disabled, .neo-has-content) {
122
+ .neo-label-wrapper {
123
+ .neo-center-border {
124
+ border-top-color: var(--textfield-border-color-alert);
125
+ }
126
+ }
127
+ }
128
+ }
129
+
86
130
  &:not(.label-inline, .neo-input-wrapper) {
87
131
  .neo-textfield-input {
88
132
  border-color: var(--textfield-border-color-invalid);
@@ -1,6 +1,7 @@
1
1
  :root .neo-theme-dark { // .neo-textfield
2
2
  --textfield-border-color : #424242;
3
3
  --textfield-border-color-active : #5d83a7;
4
+ --textfield-border-color-alert : orange;
4
5
  --textfield-border-color-disabled : #424242;
5
6
  --textfield-border-color-hovered : #424242;
6
7
  --textfield-border-color-invalid : brown;
@@ -1,6 +1,7 @@
1
1
  :root .neo-theme-light { // .neo-textfield
2
2
  --textfield-border-color : #ddd;
3
3
  --textfield-border-color-active : #1c60a0;
4
+ --textfield-border-color-alert : darkorange;
4
5
  --textfield-border-color-disabled : #ddd;
5
6
  --textfield-border-color-hovered : #ddd;
6
7
  --textfield-border-color-invalid : brown;
@@ -62,7 +62,7 @@
62
62
 
63
63
  --cmp-tab-borderradius : var(--sem-borderradius-large);
64
64
  --cmp-tab-gap : var(--sem-spacing-xsmall);
65
- --cmp-tab-height : var(--sem-height-xxlarge);
65
+ --cmp-tab-height : var(--sem-height-xlarge);
66
66
  --cmp-tab-spacinghorizontal : var(--sem-spacing-medium);
67
67
  --cmp-tab-strip-height : 3px;
68
68
  --cmp-tab-strip-selected : var(--sem-color-fg-neutral-contrast);
@@ -1,6 +1,7 @@
1
1
  :root .neo-theme-neo-light { // .neo-textfield
2
2
  --textfield-border-color : #ddd;
3
3
  --textfield-border-color-active : #1c60a0;
4
+ --textfield-border-color-alert : darkorange;
4
5
  --textfield-border-color-disabled : #ddd;
5
6
  --textfield-border-color-hovered : #ddd;
6
7
  --textfield-border-color-invalid : brown;
@@ -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.6'
239
+ * @default '6.10.7'
240
240
  * @memberOf! module:Neo
241
241
  * @name config.version
242
242
  * @type String
243
243
  */
244
- version: '6.10.6'
244
+ version: '6.10.7'
245
245
  };
246
246
 
247
247
  Object.assign(DefaultConfig, {
@@ -173,7 +173,7 @@ class Base extends Component {
173
173
 
174
174
  badgeNode.cls = cls;
175
175
 
176
- me.update();
176
+ me.update()
177
177
  }
178
178
 
179
179
  /**
@@ -188,7 +188,7 @@ class Base extends Component {
188
188
  badgeNode.html = value;
189
189
  badgeNode.removeDom = !Boolean(value);
190
190
 
191
- this.update();
191
+ this.update()
192
192
  }
193
193
 
194
194
  /**
@@ -221,7 +221,7 @@ class Base extends Component {
221
221
  }
222
222
 
223
223
  if (value === '') {
224
- value = null;
224
+ value = null
225
225
  }
226
226
 
227
227
  iconNode.style.color = value;
@@ -240,7 +240,7 @@ class Base extends Component {
240
240
  NeoArray.remove(cls, 'icon-' + oldValue);
241
241
  NeoArray.add(cls, 'icon-' + value);
242
242
 
243
- this.cls = cls;
243
+ this.cls = cls
244
244
  }
245
245
 
246
246
  /**
@@ -293,7 +293,7 @@ class Base extends Component {
293
293
  let cls = this.cls;
294
294
 
295
295
  NeoArray.toggle(cls, 'pressed', value === true);
296
- this.cls = cls;
296
+ this.cls = cls
297
297
  }
298
298
 
299
299
  /**
@@ -344,10 +344,10 @@ class Base extends Component {
344
344
 
345
345
  if (value) {
346
346
  vdomRoot.href = value;
347
- vdomRoot.tag = 'a';
347
+ vdomRoot.tag = 'a'
348
348
  } else {
349
349
  delete vdomRoot.href;
350
- vdomRoot.tag = 'button';
350
+ vdomRoot.tag = 'button'
351
351
  }
352
352
 
353
353
  this.update()
@@ -376,9 +376,9 @@ class Base extends Component {
376
376
  vdomRoot = me.getVdomRoot();
377
377
 
378
378
  if (me.url) {
379
- vdomRoot.target = value;
379
+ vdomRoot.target = value
380
380
  } else {
381
- delete vdomRoot.target;
381
+ delete vdomRoot.target
382
382
  }
383
383
 
384
384
  me.update()
@@ -393,7 +393,7 @@ class Base extends Component {
393
393
  let iconCls = this._iconCls;
394
394
 
395
395
  if (Array.isArray(iconCls)) {
396
- return iconCls.join(' ');
396
+ return iconCls.join(' ')
397
397
  }
398
398
 
399
399
  return iconCls
@@ -419,7 +419,7 @@ class Base extends Component {
419
419
  */
420
420
  beforeSetIconCls(value, oldValue) {
421
421
  if (value && !Array.isArray(value)) {
422
- value = value.split(' ').filter(Boolean);
422
+ value = value.split(' ').filter(Boolean)
423
423
  }
424
424
 
425
425
  return value
@@ -454,7 +454,7 @@ class Base extends Component {
454
454
  */
455
455
  destroy(updateParentVdom=false, silent=false) {
456
456
  this.menuList && this.menuList.destroy(true, false);
457
- super.destroy(updateParentVdom, silent);
457
+ super.destroy(updateParentVdom, silent)
458
458
  }
459
459
 
460
460
  /**
@@ -149,6 +149,7 @@ class Container extends BaseContainer {
149
149
  let fields = await this.getFields(),
150
150
  i = 0,
151
151
  hasCleanFields = false,
152
+ hasAlertFields = false,
152
153
  hasInvalidFields = false,
153
154
  hasUncleanFields = false,
154
155
  hasValidFields = false,
@@ -161,11 +162,19 @@ class Container extends BaseContainer {
161
162
  isValid = field.isValid();
162
163
 
163
164
  if (!isClean && !isValid) {
164
- return 'invalid'
165
+ if (field.isEmptyAndRequired?.()) {
166
+ hasAlertFields = true
167
+ } else {
168
+ return 'invalid'
169
+ }
165
170
  } else if (isValid) {
166
171
  hasValidFields = true
167
172
  } else if (!isValid) {
168
- hasInvalidFields = true
173
+ if (field.isEmptyAndRequired?.()) {
174
+ hasAlertFields = true
175
+ } else {
176
+ hasInvalidFields = true
177
+ }
169
178
  }
170
179
 
171
180
  if (isClean) {
@@ -175,11 +184,11 @@ class Container extends BaseContainer {
175
184
  }
176
185
  }
177
186
 
178
- if (!hasInvalidFields) {
187
+ if (!hasAlertFields && !hasInvalidFields) {
179
188
  return 'valid'
180
189
  }
181
190
 
182
- if (hasCleanFields && !hasUncleanFields) {
191
+ if (!hasAlertFields && hasCleanFields && !hasUncleanFields) {
183
192
  return 'clean'
184
193
  }
185
194
 
@@ -124,6 +124,13 @@ class CheckBox extends Base {
124
124
  * @member {*} uncheckedValue=false
125
125
  */
126
126
  uncheckedValue: false,
127
+ /**
128
+ * Using the alert state will display an empty but required field in orange instead of red.
129
+ * Intended to get combined with form.Container: getFormState().
130
+ * See apps/form as an example.
131
+ * @member {Boolean} useAlertState_=false
132
+ */
133
+ useAlertState_: false,
127
134
  /**
128
135
  * @member {Boolean|Number|String|null} value=true
129
136
  */
@@ -357,6 +364,18 @@ class CheckBox extends Base {
357
364
  oldValue !== undefined && this.validate(false)
358
365
  }
359
366
 
367
+ /**
368
+ * Triggered after the useAlertState groupRequired got changed
369
+ * @param {Boolean} value
370
+ * @param {Boolean} oldValue
371
+ * @protected
372
+ */
373
+ afterSetUseAlertState(value, oldValue) {
374
+ let cls = this.cls;
375
+ NeoArray.toggle(cls, 'neo-use-alert-state', value);
376
+ this.cls = cls
377
+ }
378
+
360
379
  /**
361
380
  * Triggered after the value config got changed
362
381
  * @param {String} value
@@ -505,6 +524,14 @@ class CheckBox extends Base {
505
524
  return `${this.id}__value-label`
506
525
  }
507
526
 
527
+ /**
528
+ * @returns {Boolean}
529
+ */
530
+ isEmptyAndRequired() {
531
+ // Assuming that checkboxes & radios can only validate false in case they are empty & required
532
+ return !this.validate(false)
533
+ }
534
+
508
535
  /**
509
536
  * @returns {Boolean}
510
537
  */
@@ -230,6 +230,13 @@ class Text extends Base {
230
230
  * @member {Object|Object[]|null} triggers_=null
231
231
  */
232
232
  triggers_: null,
233
+ /**
234
+ * Using the alert state will display an empty but required field in orange instead of red.
235
+ * Intended to get combined with form.Container: getFormState().
236
+ * See apps/form as an example.
237
+ * @member {Boolean} useAlertState_=false
238
+ */
239
+ useAlertState_: false,
233
240
  /**
234
241
  * A string based value will get resolved into the closest controller which implements it
235
242
  * @member {Function|String|null} validator=null
@@ -848,6 +855,8 @@ class Text extends Base {
848
855
 
849
856
  cls = me.cls;
850
857
 
858
+ me.useAlertState && NeoArray.toggle(cls, 'neo-empty-required', me.isEmpty() && me.required);
859
+
851
860
  NeoArray[me.hasContent() ? 'add' : 'remove'](cls, 'neo-has-content');
852
861
  NeoArray[isDirty ? 'add' : 'remove'](cls, 'neo-is-dirty');
853
862
  me.cls = cls;
@@ -1219,6 +1228,13 @@ class Text extends Base {
1219
1228
  return !(this.value?.toString().length > 0)
1220
1229
  }
1221
1230
 
1231
+ /**
1232
+ * @returns {Boolean}
1233
+ */
1234
+ isEmptyAndRequired() {
1235
+ return this.isEmpty() && this.required
1236
+ }
1237
+
1222
1238
  /**
1223
1239
  * @returns {Boolean}
1224
1240
  */
@@ -118,9 +118,10 @@ class DomEvents extends Base {
118
118
 
119
119
  let me = this;
120
120
 
121
- document.addEventListener('DOMContentLoaded', me.onDomContentLoaded.bind(me));
122
- document.addEventListener('selectionchange', me.onSelectionChange .bind(me));
123
- window .addEventListener('hashchange', me.onHashChange .bind(me));
121
+ document.addEventListener('DOMContentLoaded', me.onDomContentLoaded .bind(me));
122
+ document.addEventListener('selectionchange', me.onSelectionChange .bind(me));
123
+ window .addEventListener('orientationchange', me.onOrientationChange.bind(me));
124
+ window .addEventListener('hashchange', me.onHashChange .bind(me));
124
125
 
125
126
  if (Neo.config.useSharedWorkers) {
126
127
  window.addEventListener('beforeunload', me.onBeforeUnload.bind(me))
@@ -270,10 +271,10 @@ class DomEvents extends Base {
270
271
  };
271
272
 
272
273
  if (event.relatedTarget) {
273
- result.relatedTarget = this.getTargetData(event.relatedTarget);
274
+ result.relatedTarget = this.getTargetData(event.relatedTarget)
274
275
  }
275
276
 
276
- return result;
277
+ return result
277
278
  }
278
279
 
279
280
  /**
@@ -350,7 +351,7 @@ class DomEvents extends Base {
350
351
 
351
352
  path.push(this.getTargetData(target));
352
353
 
353
- return path;
354
+ return path
354
355
  }
355
356
 
356
357
  /**
@@ -580,6 +581,23 @@ class DomEvents extends Base {
580
581
  this.sendMessageToApp(this.getMouseEventData(event))
581
582
  }
582
583
 
584
+ /**
585
+ * @param {Event} event
586
+ */
587
+ onOrientationChange(event) {
588
+ const
589
+ orientation = screen.orientation,
590
+ angle = orientation.angle,
591
+ layout = angle === 0 || angle === 180 ? 'portrait' : 'landscape',
592
+ type = orientation.type,
593
+ manager = Neo.worker.Manager;
594
+
595
+ manager.sendMessage('app', {
596
+ action: 'orientationChange',
597
+ data : {angle, layout, type}
598
+ })
599
+ }
600
+
583
601
  /**
584
602
  * @param {Event} event
585
603
  */
@@ -810,10 +828,7 @@ class DomEvents extends Base {
810
828
 
811
829
  for (j = 0; j < countTargets; j++) {
812
830
  if (node.classList?.contains(targetArray[j])) {
813
- return {
814
- cls: targetArray[j],
815
- node
816
- };
831
+ return {cls: targetArray[j], node}
817
832
  }
818
833
  }
819
834
  }
@@ -30,6 +30,7 @@ class App extends Base {
30
30
  'createNeoInstance',
31
31
  'destroyNeoInstance',
32
32
  'fireEvent',
33
+ 'getConfigs',
33
34
  'setConfigs'
34
35
  ]
35
36
  },
@@ -225,6 +226,36 @@ class App extends Base {
225
226
  })
226
227
  }
227
228
 
229
+ /**
230
+ * Get configs of any app realm based Neo instance from main
231
+ * @param {Object} data
232
+ * @param {String} data.id
233
+ * @param {String|String[]} data.keys
234
+ * Returns an array of configs if a keys array was passed.
235
+ * Returns the value of a given config directly, in case no array was passed
236
+ * Returns false, in case no instance got found.
237
+ * @returns {*}
238
+ */
239
+ getConfigs(data) {
240
+ let instance = Neo.get(data.id),
241
+ keys = data.keys,
242
+ returnArray = [];
243
+
244
+ if (instance) {
245
+ if (!Array.isArray(keys)) {
246
+ return instance[keys]
247
+ }
248
+
249
+ keys.forEach(key => {
250
+ returnArray.push(instance[key])
251
+ });
252
+
253
+ return returnArray
254
+ }
255
+
256
+ return false
257
+ }
258
+
228
259
  /**
229
260
  * @param {String} path
230
261
  * @returns {Promise}
@@ -342,6 +373,19 @@ class App extends Base {
342
373
  })
343
374
  }
344
375
 
376
+ /**
377
+ * Fire event on all apps
378
+ * @param {Object} data
379
+ * @param {Number} data.angle
380
+ * @param {String} data.layout landscape|portrait
381
+ * @param {Number} data.type landscape-primary|landscape-secondary|portrait-primary|portrait-secondary
382
+ */
383
+ onOrientationChange(data) {
384
+ Object.values(Neo.apps).forEach(app => {
385
+ app.fire('orientationchange', data.data)
386
+ })
387
+ }
388
+
345
389
  /**
346
390
  * @param {Object} msg
347
391
  */
@@ -47,7 +47,7 @@ class RemoteMethodAccess extends Base {
47
47
 
48
48
  me.isSharedWorker && me.assignPort(data, opts);
49
49
 
50
- return me.promiseMessage(origin, opts, buffer);
50
+ return me.promiseMessage(origin, opts, buffer)
51
51
  }
52
52
  }
53
53
 
@@ -63,13 +63,13 @@ class RemoteMethodAccess extends Base {
63
63
 
64
64
  methods.forEach(method => {
65
65
  if (remote.origin !== 'main' && pkg[method]) {
66
- throw new Error('Duplicate remote method definition ' + className + '.' + method);
66
+ throw new Error('Duplicate remote method definition ' + className + '.' + method)
67
67
  }
68
68
 
69
69
  if (!pkg[method] ) {
70
- pkg[method] = me.generateRemote(remote, method);
70
+ pkg[method] = me.generateRemote(remote, method)
71
71
  }
72
- });
72
+ })
73
73
  }
74
74
  }
75
75
 
@@ -82,30 +82,27 @@ class RemoteMethodAccess extends Base {
82
82
  out, method;
83
83
 
84
84
  if (!pkg) {
85
- throw new Error('Invalid remote namespace "' + msg.remoteClassName + '"');
85
+ throw new Error('Invalid remote namespace "' + msg.remoteClassName + '"')
86
86
  }
87
87
 
88
88
  method = pkg[msg.remoteMethod];
89
89
 
90
90
  if (!method) {
91
- throw new Error('Invalid remote method name "' + msg.remoteMethod + '"');
91
+ throw new Error('Invalid remote method name "' + msg.remoteMethod + '"')
92
92
  }
93
93
 
94
94
  if (Array.isArray(msg.data)) {
95
- out = method.call(pkg, ...msg.data);
95
+ out = method.call(pkg, ...msg.data)
96
96
  } else {
97
- out = method.call(pkg, msg.data);
97
+ out = method.call(pkg, msg.data)
98
98
  }
99
99
 
100
100
  if (out instanceof Promise) {
101
- out.then(data => {
102
- me.resolve(msg, data);
103
- })
104
- .catch(err => {
105
- me.reject(msg, err);
106
- });
101
+ out
102
+ .catch(err => {me.reject(msg, err)})
103
+ .then(data => {me.resolve(msg, data)})
107
104
  } else {
108
- me.resolve(msg, out);
105
+ me.resolve(msg, out)
109
106
  }
110
107
  }
111
108
 
@@ -125,7 +122,7 @@ class RemoteMethodAccess extends Base {
125
122
  };
126
123
 
127
124
  me.isSharedWorker && me.assignPort(msg, opts);
128
- me.sendMessage(msg.origin, opts);
125
+ me.sendMessage(msg.origin, opts)
129
126
  }
130
127
 
131
128
  /**
@@ -143,7 +140,7 @@ class RemoteMethodAccess extends Base {
143
140
  };
144
141
 
145
142
  me.isSharedWorker && me.assignPort(msg, opts);
146
- me.sendMessage(msg.origin, opts);
143
+ me.sendMessage(msg.origin, opts)
147
144
  }
148
145
  }
149
146