neo.mjs 6.3.0 → 6.3.2
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/examples/ServiceWorker.mjs +2 -2
- package/package.json +1 -1
- package/src/DefaultConfig.mjs +2 -2
- package/src/form/Container.mjs +46 -37
- package/src/main/addon/PrefixField.mjs +149 -0
- package/src/plugin/Popover.mjs +194 -0
package/apps/ServiceWorker.mjs
CHANGED
package/package.json
CHANGED
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.3.
|
239
|
+
* @default '6.3.2'
|
240
240
|
* @memberOf! module:Neo
|
241
241
|
* @name config.version
|
242
242
|
* @type String
|
243
243
|
*/
|
244
|
-
version: '6.3.
|
244
|
+
version: '6.3.2'
|
245
245
|
};
|
246
246
|
|
247
247
|
Object.assign(DefaultConfig, {
|
package/src/form/Container.mjs
CHANGED
@@ -123,8 +123,8 @@ class Container extends BaseContainer {
|
|
123
123
|
|
124
124
|
await this.loadModules();
|
125
125
|
|
126
|
-
ComponentManager.getChildComponents(this).forEach(
|
127
|
-
|
126
|
+
ComponentManager.getChildComponents(this).forEach(field => {
|
127
|
+
field instanceof BaseField && fields.push(field)
|
128
128
|
});
|
129
129
|
|
130
130
|
return fields
|
@@ -137,31 +137,31 @@ class Container extends BaseContainer {
|
|
137
137
|
let fields = await this.getFields(),
|
138
138
|
Radio = Neo.form.field.Radio,
|
139
139
|
values = {},
|
140
|
-
|
140
|
+
fieldName, key, ns, nsArray, value;
|
141
141
|
|
142
|
-
fields.forEach(
|
143
|
-
value =
|
142
|
+
fields.forEach(field => {
|
143
|
+
value = field.getValue();
|
144
144
|
|
145
|
-
if (
|
146
|
-
|
145
|
+
if (field.name) {
|
146
|
+
fieldName = field.name;
|
147
147
|
|
148
|
-
if (
|
149
|
-
|
148
|
+
if (field.formGroup) {
|
149
|
+
fieldName = field.formGroup + '.' + fieldName;
|
150
150
|
}
|
151
151
|
|
152
|
-
nsArray =
|
152
|
+
nsArray = fieldName.split('.');
|
153
153
|
key = nsArray.pop();
|
154
154
|
ns = Neo.nsWithArrays(nsArray, true, values);
|
155
155
|
} else {
|
156
|
-
key =
|
156
|
+
key = field.id;
|
157
157
|
ns = values
|
158
158
|
}
|
159
159
|
|
160
160
|
// Ensuring that Radios will not return arrays
|
161
|
-
if (Radio &&
|
161
|
+
if (Radio && field instanceof Radio) {
|
162
162
|
// Only overwrite an existing value with a checked value
|
163
163
|
if (Object.hasOwn(ns, key)) {
|
164
|
-
if (value !==
|
164
|
+
if (value !== field.uncheckedValue) {
|
165
165
|
ns[key] = value
|
166
166
|
}
|
167
167
|
} else {
|
@@ -174,13 +174,13 @@ class Container extends BaseContainer {
|
|
174
174
|
* (multiple fields using the same name)
|
175
175
|
*/
|
176
176
|
else if (Object.hasOwn(ns, key) && value !== undefined) {
|
177
|
-
if (ns[key] ===
|
177
|
+
if (ns[key] === field.uncheckedValue) {
|
178
178
|
ns[key] = []
|
179
179
|
} else if (!Array.isArray(ns[key])) {
|
180
180
|
ns[key] = [ns[key]]
|
181
181
|
}
|
182
182
|
|
183
|
-
value !==
|
183
|
+
value !== field.uncheckedValue && ns[key].unshift(value)
|
184
184
|
} else if (value !== undefined) {
|
185
185
|
ns[key] = value
|
186
186
|
}
|
@@ -235,11 +235,11 @@ class Container extends BaseContainer {
|
|
235
235
|
fields = await me.getFields(),
|
236
236
|
path, value;
|
237
237
|
|
238
|
-
fields.forEach(
|
239
|
-
path = me.getFieldPath(
|
238
|
+
fields.forEach(field => {
|
239
|
+
path = me.getFieldPath(field);
|
240
240
|
value = Neo.nsWithArrays(path, false, values);
|
241
241
|
|
242
|
-
|
242
|
+
field.reset(path ? value : null)
|
243
243
|
})
|
244
244
|
}
|
245
245
|
|
@@ -253,38 +253,47 @@ class Container extends BaseContainer {
|
|
253
253
|
fields = await me.getFields(),
|
254
254
|
fieldConfigs, isCheckBox, isRadio, path, value;
|
255
255
|
|
256
|
-
fields.forEach(
|
257
|
-
path = me.getFieldPath(
|
256
|
+
fields.forEach(field => {
|
257
|
+
path = me.getFieldPath(field);
|
258
258
|
fieldConfigs = Neo.nsWithArrays(path, false, configs);
|
259
259
|
|
260
260
|
if (fieldConfigs) {
|
261
261
|
if (suspendEvents) {
|
262
|
-
|
262
|
+
field.suspendEvents = true
|
263
263
|
}
|
264
264
|
|
265
|
-
isCheckBox = Neo.form.field?.CheckBox &&
|
265
|
+
isCheckBox = Neo.form.field?.CheckBox && field instanceof Neo.form.field.CheckBox;
|
266
|
+
isRadio = Neo.form.field?.Radio && field instanceof Neo.form.field.Radio;
|
266
267
|
value = fieldConfigs.value;
|
267
268
|
|
268
|
-
if (isCheckBox) {
|
269
|
-
|
270
|
-
|
271
|
-
|
269
|
+
if (isCheckBox || isRadio) {
|
270
|
+
/*
|
271
|
+
* we want to only change the checked state, in case a value is set.
|
272
|
+
* since fields of the same group might need it too, we are cloning the fieldConfigs
|
273
|
+
*/
|
274
|
+
if (Object.hasOwn(fieldConfigs, 'value')) {
|
275
|
+
fieldConfigs = Neo.clone(fieldConfigs, true);
|
276
|
+
|
277
|
+
if (isRadio) {
|
278
|
+
fieldConfigs.checked = field.value === value
|
279
|
+
} else if (isCheckBox) {
|
280
|
+
if (Neo.typeOf(value) === 'Array') {
|
281
|
+
if (value.includes(field.value)) {
|
282
|
+
fieldConfigs.checked = true
|
283
|
+
}
|
284
|
+
} else {
|
285
|
+
fieldConfigs.checked = field.value === value
|
286
|
+
}
|
272
287
|
}
|
273
|
-
} else {
|
274
|
-
fieldConfigs.checked = item.value === value
|
275
|
-
}
|
276
|
-
} else if (value !== undefined) {
|
277
|
-
isRadio = Neo.form.field?.Radio && item instanceof Neo.form.field.Radio;
|
278
288
|
|
279
|
-
|
280
|
-
fieldConfigs.checked = item.value === value
|
289
|
+
delete fieldConfigs.value
|
281
290
|
}
|
282
291
|
}
|
283
292
|
|
284
|
-
|
293
|
+
field.set(fieldConfigs)
|
285
294
|
|
286
295
|
if (suspendEvents) {
|
287
|
-
delete
|
296
|
+
delete field.suspendEvents
|
288
297
|
}
|
289
298
|
}
|
290
299
|
})
|
@@ -311,8 +320,8 @@ class Container extends BaseContainer {
|
|
311
320
|
fields = await this.getFields(),
|
312
321
|
validField;
|
313
322
|
|
314
|
-
fields.forEach(
|
315
|
-
validField =
|
323
|
+
fields.forEach(field => {
|
324
|
+
validField = field.validate?.(false);
|
316
325
|
|
317
326
|
if (!validField) {
|
318
327
|
isValid = false
|
@@ -0,0 +1,149 @@
|
|
1
|
+
import Base from '../../core/Base.mjs';
|
2
|
+
|
3
|
+
/**
|
4
|
+
* Helper class to include Google's Material Web Components into your neo.mjs app
|
5
|
+
* https://www.amcharts.com/docs/v4/
|
6
|
+
* @class Neo.main.addon.PrefixField
|
7
|
+
* @extends Neo.core.Base
|
8
|
+
* @singleton
|
9
|
+
*/
|
10
|
+
class PrefixField extends Base {
|
11
|
+
static config = {
|
12
|
+
/**
|
13
|
+
* @member {String} className='Neo.main.addon.PrefixField'
|
14
|
+
* @protected
|
15
|
+
*/
|
16
|
+
className: 'Neo.main.addon.PrefixField',
|
17
|
+
/**
|
18
|
+
* @member {Boolean} singleton=true
|
19
|
+
* @protected
|
20
|
+
*/
|
21
|
+
singleton: true,
|
22
|
+
/**
|
23
|
+
* Remote method access for other workers
|
24
|
+
* @member {Object} remote
|
25
|
+
* @protected
|
26
|
+
*/
|
27
|
+
remote: {
|
28
|
+
app: [
|
29
|
+
'initialize',
|
30
|
+
'destroy',
|
31
|
+
'updateAccept',
|
32
|
+
'updatePattern',
|
33
|
+
'updateSlots',
|
34
|
+
]
|
35
|
+
},
|
36
|
+
|
37
|
+
/**
|
38
|
+
* regex to calculate if entered value is acceptable
|
39
|
+
* Preset to numbers only
|
40
|
+
*
|
41
|
+
* @member {regex|null} accept
|
42
|
+
*/
|
43
|
+
accept_: null,
|
44
|
+
/**
|
45
|
+
* @member {String} pattern=null
|
46
|
+
*/
|
47
|
+
pattern_: null,
|
48
|
+
/**
|
49
|
+
* Only add a String. A Set will be automatically created
|
50
|
+
* @member {String|Set|null} slots=null
|
51
|
+
*/
|
52
|
+
slots_: null
|
53
|
+
}
|
54
|
+
|
55
|
+
destroy() {
|
56
|
+
|
57
|
+
}
|
58
|
+
|
59
|
+
elIds = new Map();
|
60
|
+
|
61
|
+
prev;
|
62
|
+
|
63
|
+
back = false;
|
64
|
+
|
65
|
+
/**
|
66
|
+
*
|
67
|
+
* @param {Object} data
|
68
|
+
* @param {String} data.elId
|
69
|
+
* @param {String} data.pattern
|
70
|
+
* @param {String} data.slots
|
71
|
+
* @param {String} data.accept
|
72
|
+
*/
|
73
|
+
initialize(data) {
|
74
|
+
const me = this;
|
75
|
+
|
76
|
+
me.elId = data.elId;
|
77
|
+
|
78
|
+
const el = me.el = document.getElementById(data.elId),
|
79
|
+
pattern = me.pattern = data.pattern,
|
80
|
+
slots = me.slots = new Set(data.slots || "_");
|
81
|
+
|
82
|
+
me.accept = data.accept;
|
83
|
+
me.prev = (j => Array.from(pattern, (c, i) => slots.has(c) ? j = i + 1 : j))(0);
|
84
|
+
me.first = [...pattern].findIndex(c => slots.has(c));
|
85
|
+
|
86
|
+
me.addListeners();
|
87
|
+
me.addCss();
|
88
|
+
}
|
89
|
+
|
90
|
+
addCss() {
|
91
|
+
this.el.classList.add('tiny-prefix-field-input');
|
92
|
+
}
|
93
|
+
|
94
|
+
addListeners() {
|
95
|
+
const me = this,
|
96
|
+
el = me.el,
|
97
|
+
formatFn = me.format.bind(me);
|
98
|
+
|
99
|
+
el.addEventListener("keypress", me.onKeyDown.bind(me));
|
100
|
+
el.addEventListener("input", formatFn);
|
101
|
+
el.addEventListener("focusin", formatFn);
|
102
|
+
el.addEventListener("focusout", me.onBlur.bind(me));
|
103
|
+
}
|
104
|
+
|
105
|
+
onBlur() {
|
106
|
+
const pattern = this.pattern,
|
107
|
+
el = this.el;
|
108
|
+
|
109
|
+
return el.value === pattern && (el.value = "");
|
110
|
+
}
|
111
|
+
|
112
|
+
onKeyDown(event) {
|
113
|
+
this.back = (event.key === "Backspace");
|
114
|
+
}
|
115
|
+
|
116
|
+
clean(input) {
|
117
|
+
const el = this.el,
|
118
|
+
accept = new RegExp(this.accept || "\\d", "g"),
|
119
|
+
pattern = this.pattern,
|
120
|
+
slots = this.slots;
|
121
|
+
|
122
|
+
input = input.match(accept) || [];
|
123
|
+
|
124
|
+
return Array.from(pattern, c =>
|
125
|
+
input[0] === c || slots.has(c) ? input.shift() || c : c
|
126
|
+
);
|
127
|
+
}
|
128
|
+
|
129
|
+
format() {
|
130
|
+
const me = this,
|
131
|
+
el = this.el,
|
132
|
+
prev = this.prev,
|
133
|
+
clean = this.clean.bind(this);
|
134
|
+
console.log(el.selectionStart, el.selectionEnd);
|
135
|
+
const [i, j] = [el.selectionStart, el.selectionEnd].map(i => {
|
136
|
+
i = clean(el.value.slice(0, i)).findIndex(c => me.slots.has(c));
|
137
|
+
return i < 0 ? prev[prev.length - 1] : me.back ? prev[i - 1] || me.first : i;
|
138
|
+
});
|
139
|
+
|
140
|
+
el.value = clean(el.value).join``;
|
141
|
+
el.setSelectionRange(i, j);
|
142
|
+
|
143
|
+
this.back = false;
|
144
|
+
}
|
145
|
+
}
|
146
|
+
|
147
|
+
let instance = Neo.applyClassConfig(PrefixField);
|
148
|
+
|
149
|
+
export default instance;
|
@@ -0,0 +1,194 @@
|
|
1
|
+
import Base from './Base.mjs';
|
2
|
+
import Container from '../container/Base.mjs'
|
3
|
+
import NeoArray from "../util/Array.mjs";
|
4
|
+
|
5
|
+
/**
|
6
|
+
* Popover usable as tooltip
|
7
|
+
* @class Neo.plugin.Popover
|
8
|
+
* @extends Neo.plugin.Base
|
9
|
+
*
|
10
|
+
* @example
|
11
|
+
*
|
12
|
+
* module : Button,
|
13
|
+
* width : 200,
|
14
|
+
* text : 'Click Me',
|
15
|
+
* plugins: [{
|
16
|
+
* module: PopoverPlugin,
|
17
|
+
* align : 'bc-tc',
|
18
|
+
* items : [{
|
19
|
+
* ntype : 'panel',
|
20
|
+
* headers: [{
|
21
|
+
* dock: 'top',
|
22
|
+
* html: 'HEADER'
|
23
|
+
* }],
|
24
|
+
* items : [{
|
25
|
+
* html: 'This is a comment about the button'
|
26
|
+
* }]
|
27
|
+
* }]
|
28
|
+
* }]
|
29
|
+
*/
|
30
|
+
class Popover extends Base {
|
31
|
+
/**
|
32
|
+
* Valid values for align
|
33
|
+
* @member {String[]} alignValues=['bc-tc','tc-bc','tl-tr','tr-tl','cl-cr','cr-cl',null]
|
34
|
+
* @protected
|
35
|
+
* @static
|
36
|
+
*
|
37
|
+
* todo add more
|
38
|
+
*/
|
39
|
+
static alignValues = ['bc-tc', 'tc-bc', 'tl-tr', 'tr-tl', 'cl-cr', 'cr-cl', null]
|
40
|
+
|
41
|
+
static config = {
|
42
|
+
/**
|
43
|
+
* @member {String} className='Neo.plugin.Popover'
|
44
|
+
* @protected
|
45
|
+
*/
|
46
|
+
className: 'Neo.plugin.Popover',
|
47
|
+
/**
|
48
|
+
* @member {String} ntype='popover'
|
49
|
+
* @protected
|
50
|
+
*/
|
51
|
+
ntype: 'plugin-popover',
|
52
|
+
|
53
|
+
|
54
|
+
/**
|
55
|
+
* Define popover to popovertarget alignment
|
56
|
+
* Defaults to bottom center of popover is aligned to top center of owner
|
57
|
+
* @type {string} align='bc-tc'
|
58
|
+
*/
|
59
|
+
align_: 'bc-tc',
|
60
|
+
/**
|
61
|
+
* Custom cls to add to the owner component
|
62
|
+
* @member {String} ownerCls='neo-prefixfield'
|
63
|
+
*/
|
64
|
+
popovertargetCls: 'neo-popover-target',
|
65
|
+
/**
|
66
|
+
* Custom cls to add to the owner component
|
67
|
+
* @member {String} ownerCls='neo-prefixfield'
|
68
|
+
*/
|
69
|
+
popoverBaseCls: 'neo-popover',
|
70
|
+
}
|
71
|
+
|
72
|
+
/**
|
73
|
+
* @param {Object} config
|
74
|
+
*/
|
75
|
+
construct(config) {
|
76
|
+
let me = this;
|
77
|
+
|
78
|
+
super.construct(config);
|
79
|
+
|
80
|
+
me.popoverId = Neo.getId('popover');
|
81
|
+
|
82
|
+
// prepare owner
|
83
|
+
me.preparePopoverTarget();
|
84
|
+
me.addPopover();
|
85
|
+
|
86
|
+
me.addListeners();
|
87
|
+
}
|
88
|
+
|
89
|
+
/**
|
90
|
+
* Add listeners
|
91
|
+
* @protected
|
92
|
+
*/
|
93
|
+
addListeners() {
|
94
|
+
const me = this;
|
95
|
+
|
96
|
+
me.owner.addDomListeners([
|
97
|
+
{mouseover: me.onTargetMouseOver, scope: me},
|
98
|
+
{mouseout: me.onTargetMouseOut, scope: me}
|
99
|
+
]);
|
100
|
+
}
|
101
|
+
|
102
|
+
/**
|
103
|
+
* Create the popover and add it to the parent component of the owner
|
104
|
+
* @protected
|
105
|
+
*/
|
106
|
+
addPopover() {
|
107
|
+
const me = this,
|
108
|
+
owner = me.owner,
|
109
|
+
parent = Neo.get(me.owner.parentId),
|
110
|
+
popover = {
|
111
|
+
module: Container,
|
112
|
+
id : me.popoverId,
|
113
|
+
|
114
|
+
baseCls: [me.popoverBaseCls],
|
115
|
+
cls : [me.align],
|
116
|
+
|
117
|
+
layout: 'base',
|
118
|
+
items : me.items || [],
|
119
|
+
|
120
|
+
vdom: {
|
121
|
+
// Possible Values are auto, manual.
|
122
|
+
popover: 'auto',
|
123
|
+
anchor : owner.id
|
124
|
+
}
|
125
|
+
};
|
126
|
+
|
127
|
+
parent.add(popover);
|
128
|
+
}
|
129
|
+
|
130
|
+
/**
|
131
|
+
* Checks if the new value for "align" is valid
|
132
|
+
* @param {String|null} value
|
133
|
+
* @param {String|null} oldValue
|
134
|
+
* @protected
|
135
|
+
* @returns {String|null} value
|
136
|
+
*/
|
137
|
+
beforeSetAlign(value, oldValue) {
|
138
|
+
return this.testInputValue(value, oldValue, 'alignValues', 'align');
|
139
|
+
}
|
140
|
+
|
141
|
+
/**
|
142
|
+
* @event mouseout
|
143
|
+
* @param {Object} data
|
144
|
+
* @protected
|
145
|
+
*/
|
146
|
+
onTargetMouseOut(data) {
|
147
|
+
Neo.main.addon.Popover.hide({id: data.component.id});
|
148
|
+
}
|
149
|
+
|
150
|
+
/**
|
151
|
+
* @event mouseover
|
152
|
+
* @param {Object} data
|
153
|
+
* @protected
|
154
|
+
*/
|
155
|
+
onTargetMouseOver(data) {
|
156
|
+
Neo.main.addon.Popover.show({id: data.component.id});
|
157
|
+
}
|
158
|
+
|
159
|
+
/**
|
160
|
+
* @protected
|
161
|
+
*/
|
162
|
+
preparePopoverTarget() {
|
163
|
+
const me = this,
|
164
|
+
target = me.owner,
|
165
|
+
targetVdom = target.vdom;
|
166
|
+
|
167
|
+
target.addCls(me.popovertargetCls);
|
168
|
+
targetVdom.popovertarget = me.popoverId;
|
169
|
+
}
|
170
|
+
|
171
|
+
/**
|
172
|
+
* Checks if the new value for propertyName is valid
|
173
|
+
* @param {String|null} value
|
174
|
+
* @param {String|null} oldValue
|
175
|
+
* @param {String} validValuesName
|
176
|
+
* @param {String} propertyName
|
177
|
+
* @protected
|
178
|
+
* @returns {String|null} value
|
179
|
+
*/
|
180
|
+
testInputValue(value, oldValue, validValuesName, propertyName) {
|
181
|
+
const validValues = this.getStaticConfig(validValuesName);
|
182
|
+
|
183
|
+
if (!NeoArray.hasItem(validValues, value)) {
|
184
|
+
Neo.logError(this.id + ' -> layout: supported values for "' + propertyName + '" are', validValues);
|
185
|
+
return oldValue;
|
186
|
+
}
|
187
|
+
|
188
|
+
return value;
|
189
|
+
}
|
190
|
+
}
|
191
|
+
|
192
|
+
Neo.applyClassConfig(Popover);
|
193
|
+
|
194
|
+
export default Popover;
|