neo.mjs 6.5.1 → 6.5.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.
- package/apps/ServiceWorker.mjs +2 -2
- package/apps/form/view/FormContainerController.mjs +4 -6
- package/apps/form/view/pages/Page1.mjs +14 -0
- package/examples/ServiceWorker.mjs +2 -2
- package/package.json +4 -4
- package/src/DefaultConfig.mjs +2 -2
- package/src/core/Base.mjs +34 -0
- package/src/data/connection/WebSocket.mjs +4 -4
- package/src/form/Container.mjs +0 -1
- package/src/form/field/Base.mjs +35 -5
- package/src/form/field/CheckBox.mjs +3 -1
- package/src/form/field/Text.mjs +18 -4
- package/src/util/Function.mjs +78 -69
- package/src/util/Rectangle.mjs +2 -2
package/apps/ServiceWorker.mjs
CHANGED
@@ -19,12 +19,10 @@ class FormContainerController extends Component {
|
|
19
19
|
onComponentConstructed() {
|
20
20
|
super.onComponentConstructed();
|
21
21
|
|
22
|
-
this.component.on(
|
23
|
-
console.log('
|
24
|
-
|
25
|
-
|
26
|
-
this.component.on('fieldChange', data => {
|
27
|
-
console.log('fieldChange', data);
|
22
|
+
this.component.on({
|
23
|
+
fieldChange : data => console.log('fieldChange' , data),
|
24
|
+
fieldFocusLeave: data => console.log('fieldFocusLeave', data),
|
25
|
+
fieldUserChange: data => console.log('fieldUserChange', data)
|
28
26
|
})
|
29
27
|
}
|
30
28
|
|
@@ -1,3 +1,4 @@
|
|
1
|
+
import Button from '../../../../src/button/Base.mjs';
|
1
2
|
import FormPageContainer from '../FormPageContainer.mjs';
|
2
3
|
import TextField from '../../../../src/form/field/Text.mjs';
|
3
4
|
|
@@ -31,8 +32,21 @@ class Page1 extends FormPageContainer {
|
|
31
32
|
name : 'status',
|
32
33
|
readOnly : true,
|
33
34
|
value : 'Active'
|
35
|
+
}, {
|
36
|
+
module : Button,
|
37
|
+
handler: Page1.buttonHandler,
|
38
|
+
style : {marginTop: '2em', maxWidth: '300px'},
|
39
|
+
text : 'Change values'
|
34
40
|
}]
|
35
41
|
}
|
42
|
+
|
43
|
+
static buttonHandler(data) {
|
44
|
+
let container = data.component.up();
|
45
|
+
|
46
|
+
container.items[0].value = Math.random();
|
47
|
+
container.items[1].value = Math.random();
|
48
|
+
container.items[2].value = Math.random()
|
49
|
+
}
|
36
50
|
}
|
37
51
|
|
38
52
|
Neo.applyClassConfig(Page1);
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "neo.mjs",
|
3
|
-
"version": "6.5.
|
3
|
+
"version": "6.5.3",
|
4
4
|
"description": "The webworkers driven UI framework",
|
5
5
|
"type": "module",
|
6
6
|
"repository": {
|
@@ -53,11 +53,11 @@
|
|
53
53
|
"envinfo": "^7.10.0",
|
54
54
|
"fs-extra": "^11.1.1",
|
55
55
|
"highlightjs-line-numbers.js": "^2.8.0",
|
56
|
-
"inquirer": "^9.2.
|
56
|
+
"inquirer": "^9.2.11",
|
57
57
|
"neo-jsdoc": "1.0.1",
|
58
58
|
"neo-jsdoc-x": "1.0.5",
|
59
59
|
"postcss": "^8.4.29",
|
60
|
-
"sass": "^1.
|
60
|
+
"sass": "^1.67.0",
|
61
61
|
"showdown": "^2.1.0",
|
62
62
|
"webpack": "^5.88.2",
|
63
63
|
"webpack-cli": "^5.1.4",
|
@@ -67,7 +67,7 @@
|
|
67
67
|
},
|
68
68
|
"devDependencies": {
|
69
69
|
"siesta-lite": "5.5.2",
|
70
|
-
"url": "^0.11.
|
70
|
+
"url": "^0.11.2"
|
71
71
|
},
|
72
72
|
"funding": {
|
73
73
|
"type": "GitHub Sponsors",
|
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.5.
|
239
|
+
* @default '6.5.3'
|
240
240
|
* @memberOf! module:Neo
|
241
241
|
* @name config.version
|
242
242
|
* @type String
|
243
243
|
*/
|
244
|
-
version: '6.5.
|
244
|
+
version: '6.5.3'
|
245
245
|
};
|
246
246
|
|
247
247
|
Object.assign(DefaultConfig, {
|
package/src/core/Base.mjs
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
import {debounce} from '../util/Function.mjs';
|
1
2
|
import IdGenerator from './IdGenerator.mjs'
|
2
3
|
|
3
4
|
const configSymbol = Symbol.for('configSymbol'),
|
@@ -17,6 +18,20 @@ class Base {
|
|
17
18
|
* @static
|
18
19
|
*/
|
19
20
|
static methodNameRegex = /\n.*\n\s+at\s+.*\.(\w+)\s+.*/
|
21
|
+
/**
|
22
|
+
* You can define methods which should get delayed
|
23
|
+
* @example
|
24
|
+
* delayable: {
|
25
|
+
* fireChangeEvent: {
|
26
|
+
* type : 'debounce',
|
27
|
+
* timer: 300
|
28
|
+
* }
|
29
|
+
* }
|
30
|
+
* @member {Object} delayable={}
|
31
|
+
* @protected
|
32
|
+
* @static
|
33
|
+
*/
|
34
|
+
static delayable = {}
|
20
35
|
/**
|
21
36
|
* True automatically applies the core.Observable mixin
|
22
37
|
* @member {Boolean} observable=false
|
@@ -126,6 +141,8 @@ class Base {
|
|
126
141
|
value : true
|
127
142
|
});
|
128
143
|
|
144
|
+
me.applyDelayable();
|
145
|
+
|
129
146
|
me.remote && setTimeout(me.initRemote.bind(me), 1)
|
130
147
|
}
|
131
148
|
|
@@ -157,6 +174,23 @@ class Base {
|
|
157
174
|
}
|
158
175
|
}
|
159
176
|
|
177
|
+
/**
|
178
|
+
* Adjusts all methods inside static delayable
|
179
|
+
*/
|
180
|
+
applyDelayable() {
|
181
|
+
let me = this;
|
182
|
+
|
183
|
+
Object.entries(me.constructor.delayable).forEach(([key, value]) => {
|
184
|
+
let map = {
|
185
|
+
debounce() {
|
186
|
+
me[key] = new debounce(me[key], me, value.timer)
|
187
|
+
}
|
188
|
+
};
|
189
|
+
|
190
|
+
map[value.type]?.()
|
191
|
+
})
|
192
|
+
}
|
193
|
+
|
160
194
|
/**
|
161
195
|
* Applying overwrites and adding overwrittenMethods to the class constructors
|
162
196
|
* @param {Object} cfg
|
@@ -1,6 +1,6 @@
|
|
1
|
-
import Base
|
2
|
-
import
|
3
|
-
import Observable
|
1
|
+
import Base from '../../core/Base.mjs';
|
2
|
+
import {createInterceptor} from '../../util/Function.mjs';
|
3
|
+
import Observable from '../../core/Observable.mjs';
|
4
4
|
|
5
5
|
/**
|
6
6
|
* @class Neo.data.connection.WebSocket
|
@@ -120,7 +120,7 @@ class Socket extends Base {
|
|
120
120
|
onopen : me.onOpen .bind(me)
|
121
121
|
});
|
122
122
|
|
123
|
-
|
123
|
+
createInterceptor(value, 'send', me.beforeSend, me);
|
124
124
|
}
|
125
125
|
|
126
126
|
return value;
|
package/src/form/Container.mjs
CHANGED
@@ -33,7 +33,6 @@ class Container extends BaseContainer {
|
|
33
33
|
|
34
34
|
/**
|
35
35
|
* Helper function used by setValues() which wraps the leaves of a tree structure into a new property.
|
36
|
-
* The logic assumes that field config values must not be objects (separation between the key & value realm).
|
37
36
|
* @param {Object} values
|
38
37
|
* @param {String} configName
|
39
38
|
* @param {String[]} fieldPaths
|
package/src/form/field/Base.mjs
CHANGED
@@ -7,6 +7,16 @@ import ComponentManager from '../../manager/Component.mjs';
|
|
7
7
|
* @extends Neo.component.Base
|
8
8
|
*/
|
9
9
|
class Base extends Component {
|
10
|
+
/**
|
11
|
+
* @member {Object} delayable
|
12
|
+
* @protected
|
13
|
+
* @static
|
14
|
+
*/
|
15
|
+
static delayable = {
|
16
|
+
fireChangeEvent : {type: 'debounce', timer: 300},
|
17
|
+
fireUserChangeEvent: {type: 'debounce', timer: 300}
|
18
|
+
}
|
19
|
+
|
10
20
|
static config = {
|
11
21
|
/**
|
12
22
|
* @member {String} className='Neo.form.field.Base'
|
@@ -127,29 +137,49 @@ class Base extends Component {
|
|
127
137
|
|
128
138
|
/**
|
129
139
|
* Override this method as needed
|
130
|
-
* @param {*}
|
131
|
-
* @param {*}
|
140
|
+
* @param {*} value
|
141
|
+
* @param {*} oldValue
|
142
|
+
* @param {String} eventName
|
132
143
|
*/
|
133
|
-
|
144
|
+
doFireChangeEvent(value, oldValue, eventName) {
|
134
145
|
let me = this,
|
135
146
|
FormContainer = Neo.form?.Container,
|
147
|
+
formEvent = 'field' + Neo.capitalize(eventName),
|
136
148
|
opts = {component: me, oldValue, value};
|
137
149
|
|
138
150
|
if (Neo.isFunction(me.getGroupValue)) {
|
139
151
|
opts.groupValue = me.getGroupValue()
|
140
152
|
}
|
141
153
|
|
142
|
-
me.fire(
|
154
|
+
me.fire(eventName, opts);
|
143
155
|
|
144
156
|
if (!me.suspendEvents) {
|
145
157
|
ComponentManager.getParents(me).forEach(parent => {
|
146
158
|
if (FormContainer && parent instanceof FormContainer) {
|
147
|
-
parent.fire(
|
159
|
+
parent.fire(formEvent, opts)
|
148
160
|
}
|
149
161
|
})
|
150
162
|
}
|
151
163
|
}
|
152
164
|
|
165
|
+
/**
|
166
|
+
* Override this method as needed
|
167
|
+
* @param {*} value
|
168
|
+
* @param {*} oldValue
|
169
|
+
*/
|
170
|
+
fireChangeEvent(value, oldValue) {
|
171
|
+
this.doFireChangeEvent(value, oldValue, 'change')
|
172
|
+
}
|
173
|
+
|
174
|
+
/**
|
175
|
+
* Override this method as needed
|
176
|
+
* @param {*} value
|
177
|
+
* @param {*} oldValue
|
178
|
+
*/
|
179
|
+
fireUserChangeEvent(value, oldValue) {
|
180
|
+
this.doFireChangeEvent(value, oldValue, 'userChange')
|
181
|
+
}
|
182
|
+
|
153
183
|
/**
|
154
184
|
* Forms in neo can be nested. This method will return the closest parent which is a form.Container or null.
|
155
185
|
* @returns {Neo.form.Container|null}
|
@@ -525,7 +525,9 @@ class CheckBox extends Base {
|
|
525
525
|
// keep the vdom & vnode in sync for future updates
|
526
526
|
me.vnode.childNodes[0].childNodes[me.hideLabel ? 0 : 1].attributes.checked = `${checked}`;
|
527
527
|
|
528
|
-
me.checked = checked
|
528
|
+
me.checked = checked;
|
529
|
+
|
530
|
+
me.fireUserChangeEvent(me.getValue(), me.getOldValue())
|
529
531
|
}
|
530
532
|
|
531
533
|
/**
|
package/src/form/field/Text.mjs
CHANGED
@@ -17,6 +17,17 @@ class Text extends Base {
|
|
17
17
|
* @static
|
18
18
|
*/
|
19
19
|
static autoCapitalizeValues = ['characters', 'none', 'on', 'off', 'sentences', 'words']
|
20
|
+
/**
|
21
|
+
* @member {Object} delayable
|
22
|
+
* @protected
|
23
|
+
* @static
|
24
|
+
*/
|
25
|
+
static delayable = {
|
26
|
+
fireChangeEvent: {
|
27
|
+
type : 'debounce',
|
28
|
+
timer: 300
|
29
|
+
}
|
30
|
+
}
|
20
31
|
/**
|
21
32
|
* Valid values for labelPosition
|
22
33
|
* @member {String[]} labelPositions=['bottom','inline','left','right','top']
|
@@ -1231,16 +1242,19 @@ class Text extends Base {
|
|
1231
1242
|
* @protected
|
1232
1243
|
*/
|
1233
1244
|
onInputValueChange(data) {
|
1234
|
-
let me
|
1235
|
-
|
1236
|
-
|
1245
|
+
let me = this,
|
1246
|
+
oldValue = me.value,
|
1247
|
+
value = data.value,
|
1248
|
+
vnode = VNodeUtil.findChildVnode(me.vnode, {nodeName: 'input'});
|
1237
1249
|
|
1238
1250
|
if (vnode) {
|
1239
1251
|
// required for validation -> revert a wrong user input
|
1240
1252
|
vnode.vnode.attributes.value = value;
|
1241
1253
|
}
|
1242
1254
|
|
1243
|
-
me.value = me.inputValueAdjustor(value)
|
1255
|
+
me.value = me.inputValueAdjustor(value);
|
1256
|
+
|
1257
|
+
me.fireUserChangeEvent(value, oldValue)
|
1244
1258
|
}
|
1245
1259
|
|
1246
1260
|
/**
|
package/src/util/Function.mjs
CHANGED
@@ -1,84 +1,93 @@
|
|
1
|
-
import Base from '../core/Base.mjs';
|
2
|
-
|
3
1
|
/**
|
4
|
-
*
|
5
|
-
* @
|
2
|
+
* Append args instead of prepending them
|
3
|
+
* @param {Object} scope
|
4
|
+
* @returns {Function}
|
6
5
|
*/
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
className: 'Neo.util.Function'
|
6
|
+
export function bindAppend(scope) {
|
7
|
+
const fn = this,
|
8
|
+
args = [].slice.call(arguments).slice(1);
|
9
|
+
|
10
|
+
return function() {
|
11
|
+
return fn.apply(scope, [].slice.call(arguments).concat(args))
|
14
12
|
}
|
13
|
+
}
|
15
14
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
15
|
+
/**
|
16
|
+
* Intended for functions with 1 param where the interceptor can change the value
|
17
|
+
* @param {Object} target
|
18
|
+
* @param {String} targetMethodName
|
19
|
+
* @param {Function} interceptFunction
|
20
|
+
* @param {Object} scope=target
|
21
|
+
* @returns {Function}
|
22
|
+
*/
|
23
|
+
export function createInterceptor(target, targetMethodName, interceptFunction, scope) {
|
24
|
+
let targetMethod = target[targetMethodName];
|
24
25
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
26
|
+
return (target[targetMethodName] = function(value) {
|
27
|
+
return targetMethod.call(target, interceptFunction.call(scope || target, value))
|
28
|
+
})
|
29
|
+
}
|
29
30
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
let targetMethod = target[targetMethodName];
|
31
|
+
/**
|
32
|
+
* @param {Neo.core.Base} target
|
33
|
+
* @param {String} methodName
|
34
|
+
* @param {Function} fn
|
35
|
+
* @param {Object} scope
|
36
|
+
* @returns {Function}
|
37
|
+
*/
|
38
|
+
export function createSequence(target, methodName, fn, scope) {
|
39
|
+
let method = target[methodName] || Neo.emptyFn;
|
40
40
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
}
|
41
|
+
return (target[methodName] = function() {
|
42
|
+
method.apply(this, arguments);
|
43
|
+
return fn.apply(scope || this, arguments);
|
44
|
+
})
|
45
|
+
}
|
45
46
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
let method = target[methodName] || Neo.emptyFn;
|
47
|
+
/**
|
48
|
+
* @param {Function} func
|
49
|
+
* @param {Neo.core.Base} scope
|
50
|
+
* @param {Number} timeout=300
|
51
|
+
* @returns {Function}
|
52
|
+
*/
|
53
|
+
export function debounce(func, scope, timeout=300) {
|
54
|
+
let debounceTimer;
|
55
55
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
56
|
+
return function(...args) {
|
57
|
+
// leading edge => trigger the first call right away
|
58
|
+
if (!Neo.isNumber(debounceTimer)) {
|
59
|
+
// we need to check if the scope (instance) did not get destroyed yet
|
60
|
+
scope?.id && func.apply(scope, args);
|
61
61
|
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
* @param {Function} interceptFunction
|
67
|
-
* @param {Object} scope=target
|
68
|
-
* @param {*} preventedReturnValue=null The value to return in case the interceptFunction returns false
|
69
|
-
* @returns {Function}
|
70
|
-
*/
|
71
|
-
static intercept(target, targetMethodName, interceptFunction, scope, preventedReturnValue=null) {
|
72
|
-
let targetMethod = target[targetMethodName];
|
62
|
+
// we still want to start a timer, do delay the 2nd+ update
|
63
|
+
debounceTimer = setTimeout(() => {debounceTimer = null}, timeout)
|
64
|
+
} else {
|
65
|
+
clearTimeout(debounceTimer);
|
73
66
|
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
67
|
+
debounceTimer = setTimeout(() => {
|
68
|
+
// we need to check if the scope (instance) did not get destroyed yet
|
69
|
+
scope?.id && func.apply(scope, args);
|
70
|
+
debounceTimer = setTimeout(() => {debounceTimer = null}, timeout)
|
71
|
+
}, timeout)
|
72
|
+
}
|
79
73
|
}
|
80
74
|
}
|
81
75
|
|
82
|
-
|
76
|
+
/**
|
77
|
+
* The interceptor can prevent the targetMethod from getting executed in case it returns false.
|
78
|
+
* @param {Object} target
|
79
|
+
* @param {String} targetMethodName
|
80
|
+
* @param {Function} interceptFunction
|
81
|
+
* @param {Object} scope=target
|
82
|
+
* @param {*} preventedReturnValue=null The value to return in case the interceptFunction returns false
|
83
|
+
* @returns {Function}
|
84
|
+
*/
|
85
|
+
export function intercept(target, targetMethodName, interceptFunction, scope, preventedReturnValue=null) {
|
86
|
+
let targetMethod = target[targetMethodName];
|
83
87
|
|
84
|
-
|
88
|
+
return (target[targetMethodName] = function() {
|
89
|
+
return (interceptFunction.apply(scope || target, arguments) === false)
|
90
|
+
? preventedReturnValue
|
91
|
+
: targetMethod.apply(target, arguments)
|
92
|
+
})
|
93
|
+
}
|
package/src/util/Rectangle.mjs
CHANGED
@@ -451,11 +451,11 @@ export default class Rectangle extends DOMRect {
|
|
451
451
|
|
452
452
|
// Fall back to the other two zones.
|
453
453
|
zonesToTry.push({
|
454
|
-
zone : zone = (
|
454
|
+
zone : zone = (edges.theirEdgeZone + 1) % 4,
|
455
455
|
edgeAlign : `${oppositeEdge[zoneEdges[zone]]}-${zoneEdges[zone]}`
|
456
456
|
});
|
457
457
|
zonesToTry.push({
|
458
|
-
zone : zone = (
|
458
|
+
zone : zone = (edges.theirEdgeZone + 3) % 4,
|
459
459
|
edgeAlign : `${oppositeEdge[zoneEdges[zone]]}-${zoneEdges[zone]}`
|
460
460
|
});
|
461
461
|
}
|