vasille 3.2.0 → 4.0.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.
- package/README.md +5 -14
- package/eslint.config.js +25 -0
- package/lib/core/core.js +8 -94
- package/lib/core/destroyable.js +1 -10
- package/lib/core/ivalue.js +3 -5
- package/lib/functional/safety.js +1 -1
- package/lib/index.js +18 -20
- package/lib/models/array-model.js +119 -0
- package/lib/models/listener.js +64 -0
- package/lib/models/map-model.js +63 -0
- package/lib/models/model.js +1 -0
- package/lib/models/set-model.js +57 -0
- package/lib/node/app.js +7 -5
- package/lib/node/node.js +80 -95
- package/lib/node/watch.js +24 -8
- package/lib/runner/web/binding/binding.js +4 -6
- package/lib/runner/web/binding/class.js +19 -6
- package/lib/runner/web/binding/property.js +20 -0
- package/lib/runner/web/runner.js +31 -22
- package/lib/value/expression.js +29 -21
- package/lib/value/pointer.js +45 -54
- package/lib/value/reference.js +21 -17
- package/lib/views/array-view.js +4 -4
- package/lib/views/base-view.js +19 -8
- package/lib/views/map-view.js +2 -2
- package/lib/views/repeat-node.js +12 -11
- package/lib/views/set-view.js +3 -3
- package/package.json +18 -9
- package/types/core/core.d.ts +3 -47
- package/types/core/destroyable.d.ts +2 -12
- package/types/core/ivalue.d.ts +3 -5
- package/types/index.d.ts +21 -22
- package/types/models/array-model.d.ts +55 -0
- package/types/models/listener.d.ts +48 -0
- package/types/models/map-model.d.ts +36 -0
- package/types/models/model.d.ts +9 -0
- package/types/models/set-model.d.ts +33 -0
- package/types/node/app.d.ts +6 -6
- package/types/node/node.d.ts +21 -49
- package/types/node/runner.d.ts +3 -0
- package/types/node/watch.d.ts +5 -1
- package/types/runner/web/binding/binding.d.ts +1 -2
- package/types/runner/web/binding/class.d.ts +2 -0
- package/types/runner/web/binding/property.d.ts +17 -0
- package/types/runner/web/runner.d.ts +1 -1
- package/types/value/expression.d.ts +7 -12
- package/types/value/pointer.d.ts +32 -22
- package/types/value/reference.d.ts +2 -4
- package/types/views/array-view.d.ts +2 -2
- package/types/views/base-view.d.ts +2 -3
- package/types/views/repeat-node.d.ts +4 -3
- package/lib/tsconfig.tsbuildinfo +0 -1
- package/types/tsconfig-types.tsbuildinfo +0 -1
package/lib/node/node.js
CHANGED
|
@@ -2,15 +2,20 @@ import { Reactive } from "../core/core.js";
|
|
|
2
2
|
import { IValue } from "../core/ivalue.js";
|
|
3
3
|
import { SetModel } from "../models/set-model.js";
|
|
4
4
|
import { Reference } from "../value/reference.js";
|
|
5
|
-
import { userError } from "../core/errors.js";
|
|
6
5
|
/**
|
|
7
6
|
* This class is symbolic
|
|
8
7
|
* @extends Reactive
|
|
9
8
|
*/
|
|
10
9
|
export class Root extends Reactive {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
10
|
+
/**
|
|
11
|
+
* The children list
|
|
12
|
+
* @type Array
|
|
13
|
+
*/
|
|
14
|
+
children;
|
|
15
|
+
runner;
|
|
16
|
+
lastChild = undefined;
|
|
17
|
+
constructor(runner) {
|
|
18
|
+
super();
|
|
14
19
|
this.runner = runner;
|
|
15
20
|
this.children = runner.debugUi ? new SetModel() : new Set();
|
|
16
21
|
}
|
|
@@ -31,19 +36,14 @@ export class Root extends Reactive {
|
|
|
31
36
|
*/
|
|
32
37
|
findFirstChild() {
|
|
33
38
|
let first;
|
|
34
|
-
|
|
35
|
-
first = child.findFirstChild();
|
|
36
|
-
|
|
37
|
-
if (first) {
|
|
38
|
-
break;
|
|
39
|
-
}
|
|
40
|
-
}
|
|
39
|
+
this.children.forEach(child => {
|
|
40
|
+
first = first ?? child.findFirstChild();
|
|
41
|
+
});
|
|
41
42
|
return first;
|
|
42
43
|
}
|
|
43
44
|
/**
|
|
44
45
|
* Defines a text fragment
|
|
45
46
|
* @param text {String | IValue} A text fragment string
|
|
46
|
-
* @param cb {function (TextNode)} Callback if previous is slot name
|
|
47
47
|
*/
|
|
48
48
|
text(text) {
|
|
49
49
|
const node = this.runner.textNode(text);
|
|
@@ -76,49 +76,6 @@ export class Root extends Reactive {
|
|
|
76
76
|
node.compose();
|
|
77
77
|
callback?.(node);
|
|
78
78
|
}
|
|
79
|
-
/**
|
|
80
|
-
* Defines an if node
|
|
81
|
-
* @param cond {IValue} condition
|
|
82
|
-
* @param cb {function(Fragment)} callback to run on true
|
|
83
|
-
* @return {this}
|
|
84
|
-
*/
|
|
85
|
-
if(cond, cb) {
|
|
86
|
-
const node = new SwitchedNode(this.runner);
|
|
87
|
-
this.pushNode(node);
|
|
88
|
-
node.addCase(this.case(cond, cb));
|
|
89
|
-
}
|
|
90
|
-
else(cb) {
|
|
91
|
-
if (this.lastChild instanceof SwitchedNode) {
|
|
92
|
-
this.lastChild.addCase(this.default(cb));
|
|
93
|
-
}
|
|
94
|
-
else {
|
|
95
|
-
throw userError("wrong `else` function use", "logic-error");
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
elif(cond, cb) {
|
|
99
|
-
if (this.lastChild instanceof SwitchedNode) {
|
|
100
|
-
this.lastChild.addCase(this.case(cond, cb));
|
|
101
|
-
}
|
|
102
|
-
else {
|
|
103
|
-
throw userError("wrong `elif` function use", "logic-error");
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
/**
|
|
107
|
-
* Create a case for switch
|
|
108
|
-
* @param cond {IValue<boolean>}
|
|
109
|
-
* @param cb {function(Fragment) : void}
|
|
110
|
-
* @return {{cond : IValue, cb : (function(Fragment) : void)}}
|
|
111
|
-
*/
|
|
112
|
-
case(cond, cb) {
|
|
113
|
-
return { cond, cb };
|
|
114
|
-
}
|
|
115
|
-
/**
|
|
116
|
-
* @param cb {(function(Fragment) : void)}
|
|
117
|
-
* @return {{cond : IValue, cb : (function(Fragment) : void)}}
|
|
118
|
-
*/
|
|
119
|
-
default(cb) {
|
|
120
|
-
return { cond: trueIValue, cb };
|
|
121
|
-
}
|
|
122
79
|
destroy() {
|
|
123
80
|
this.children.forEach(child => child.destroy());
|
|
124
81
|
this.children.clear();
|
|
@@ -127,10 +84,20 @@ export class Root extends Reactive {
|
|
|
127
84
|
}
|
|
128
85
|
}
|
|
129
86
|
export class Fragment extends Root {
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
87
|
+
parent;
|
|
88
|
+
constructor(runner) {
|
|
89
|
+
super(runner);
|
|
133
90
|
}
|
|
91
|
+
/**
|
|
92
|
+
* Next node
|
|
93
|
+
* @type {?Fragment}
|
|
94
|
+
*/
|
|
95
|
+
next;
|
|
96
|
+
/**
|
|
97
|
+
* Previous node
|
|
98
|
+
* @type {?Fragment}
|
|
99
|
+
*/
|
|
100
|
+
prev;
|
|
134
101
|
/**
|
|
135
102
|
* Pushes a node to children immediately
|
|
136
103
|
* @param node {Fragment} A node to push
|
|
@@ -204,18 +171,20 @@ export class Fragment extends Root {
|
|
|
204
171
|
super.destroy();
|
|
205
172
|
}
|
|
206
173
|
}
|
|
207
|
-
const trueIValue = new Reference(true);
|
|
208
174
|
/**
|
|
209
175
|
* Represents a text node
|
|
210
176
|
* @class TextNode
|
|
211
177
|
* @extends Fragment
|
|
212
178
|
*/
|
|
213
179
|
export class TextNode extends Fragment {
|
|
180
|
+
handler;
|
|
181
|
+
data;
|
|
214
182
|
constructor(input, runner) {
|
|
215
|
-
super(
|
|
183
|
+
super(runner);
|
|
184
|
+
this.data = input.text;
|
|
216
185
|
}
|
|
217
186
|
destroy() {
|
|
218
|
-
const text = this.
|
|
187
|
+
const text = this.data;
|
|
219
188
|
if (text instanceof IValue && this.handler) {
|
|
220
189
|
text.off(this.handler);
|
|
221
190
|
}
|
|
@@ -228,6 +197,11 @@ export class TextNode extends Fragment {
|
|
|
228
197
|
* @extends Fragment
|
|
229
198
|
*/
|
|
230
199
|
export class INode extends Fragment {
|
|
200
|
+
/**
|
|
201
|
+
* The element of vasille node
|
|
202
|
+
* @type Element
|
|
203
|
+
*/
|
|
204
|
+
node;
|
|
231
205
|
get element() {
|
|
232
206
|
return this.node;
|
|
233
207
|
}
|
|
@@ -241,8 +215,12 @@ export class INode extends Fragment {
|
|
|
241
215
|
* @extends INode
|
|
242
216
|
*/
|
|
243
217
|
export class Tag extends INode {
|
|
244
|
-
|
|
245
|
-
|
|
218
|
+
name;
|
|
219
|
+
options;
|
|
220
|
+
constructor(options, runner, tagName) {
|
|
221
|
+
super(runner);
|
|
222
|
+
this.options = options;
|
|
223
|
+
this.name = tagName;
|
|
246
224
|
}
|
|
247
225
|
findFirstChild() {
|
|
248
226
|
return this.node;
|
|
@@ -251,27 +229,37 @@ export class Tag extends INode {
|
|
|
251
229
|
this.runner.appendChild(this.node, node);
|
|
252
230
|
}
|
|
253
231
|
}
|
|
232
|
+
const alwaysTrue = new Reference(true);
|
|
254
233
|
/**
|
|
255
234
|
* Defines a node which can switch its children conditionally
|
|
256
235
|
*/
|
|
257
236
|
export class SwitchedNode extends Fragment {
|
|
237
|
+
/**
|
|
238
|
+
* Index of current true condition
|
|
239
|
+
* @type number
|
|
240
|
+
*/
|
|
241
|
+
index;
|
|
242
|
+
/**
|
|
243
|
+
* Array of possible cases
|
|
244
|
+
* @type {Array<{cond : IValue<unknown>, cb : function(Fragment)}>}
|
|
245
|
+
*/
|
|
246
|
+
cases;
|
|
247
|
+
/**
|
|
248
|
+
* A function that syncs index and content will be bounded to each condition
|
|
249
|
+
* @type {Function}
|
|
250
|
+
*/
|
|
251
|
+
sync;
|
|
258
252
|
/**
|
|
259
253
|
* Constructs a switch node and define a sync function
|
|
260
254
|
*/
|
|
261
|
-
constructor(runner) {
|
|
262
|
-
super(
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
this.cases = [];
|
|
255
|
+
constructor(runner, cases, _default) {
|
|
256
|
+
super(runner);
|
|
257
|
+
if (_default) {
|
|
258
|
+
cases.push({ $case: alwaysTrue, slot: _default });
|
|
259
|
+
}
|
|
260
|
+
this.cases = cases;
|
|
268
261
|
this.sync = () => {
|
|
269
|
-
let i =
|
|
270
|
-
for (; i < this.cases.length; i++) {
|
|
271
|
-
if (this.cases[i].cond.$) {
|
|
272
|
-
break;
|
|
273
|
-
}
|
|
274
|
-
}
|
|
262
|
+
let i = this.cases.findIndex(item => item.$case.V);
|
|
275
263
|
if (i === this.index) {
|
|
276
264
|
return;
|
|
277
265
|
}
|
|
@@ -280,34 +268,28 @@ export class SwitchedNode extends Fragment {
|
|
|
280
268
|
this.children.clear();
|
|
281
269
|
this.lastChild = undefined;
|
|
282
270
|
}
|
|
283
|
-
if (i !==
|
|
271
|
+
if (i !== -1) {
|
|
272
|
+
const node = new Fragment(this.runner);
|
|
273
|
+
node.parent = this;
|
|
274
|
+
this.lastChild = node;
|
|
275
|
+
this.children.add(node);
|
|
284
276
|
this.index = i;
|
|
285
|
-
this.
|
|
277
|
+
this.cases[i].slot(node);
|
|
286
278
|
}
|
|
287
279
|
else {
|
|
288
280
|
this.index = -1;
|
|
289
281
|
}
|
|
290
282
|
};
|
|
283
|
+
cases.forEach(_case => {
|
|
284
|
+
_case.$case.on(this.sync);
|
|
285
|
+
});
|
|
291
286
|
}
|
|
292
|
-
|
|
293
|
-
this.cases.push(case_);
|
|
294
|
-
case_.cond.on(this.sync);
|
|
287
|
+
compose() {
|
|
295
288
|
this.sync();
|
|
296
289
|
}
|
|
297
|
-
/**
|
|
298
|
-
* Creates a child node
|
|
299
|
-
* @param cb {function(Fragment)} Call-back
|
|
300
|
-
*/
|
|
301
|
-
createChild(cb) {
|
|
302
|
-
const node = new Fragment({}, this.runner, ":case");
|
|
303
|
-
node.parent = this;
|
|
304
|
-
this.lastChild = node;
|
|
305
|
-
this.children.add(node);
|
|
306
|
-
cb(node);
|
|
307
|
-
}
|
|
308
290
|
destroy() {
|
|
309
291
|
this.cases.forEach(c => {
|
|
310
|
-
c.
|
|
292
|
+
c.$case.off(this.sync);
|
|
311
293
|
});
|
|
312
294
|
this.cases.splice(0);
|
|
313
295
|
super.destroy();
|
|
@@ -319,13 +301,16 @@ export class SwitchedNode extends Fragment {
|
|
|
319
301
|
* @extends Fragment
|
|
320
302
|
*/
|
|
321
303
|
export class DebugNode extends Fragment {
|
|
304
|
+
handler;
|
|
305
|
+
data;
|
|
322
306
|
constructor(input, runner) {
|
|
323
|
-
super(
|
|
307
|
+
super(runner);
|
|
308
|
+
this.data = input.text;
|
|
324
309
|
}
|
|
325
310
|
destroy() {
|
|
326
311
|
/* istanbul ignore else */
|
|
327
312
|
if (this.handler) {
|
|
328
|
-
this.
|
|
313
|
+
this.data.off(this.handler);
|
|
329
314
|
}
|
|
330
315
|
super.destroy();
|
|
331
316
|
}
|
package/lib/node/watch.js
CHANGED
|
@@ -5,17 +5,33 @@ import { Fragment } from "./node.js";
|
|
|
5
5
|
* @extends Fragment
|
|
6
6
|
*/
|
|
7
7
|
export class Watch extends Fragment {
|
|
8
|
+
model;
|
|
9
|
+
slot;
|
|
10
|
+
handler;
|
|
8
11
|
constructor(input, runner) {
|
|
9
|
-
super(
|
|
12
|
+
super(runner);
|
|
13
|
+
this.model = input.model;
|
|
14
|
+
this.slot = input.slot;
|
|
10
15
|
}
|
|
11
16
|
compose() {
|
|
12
|
-
this.
|
|
13
|
-
|
|
14
|
-
|
|
17
|
+
const slot = this.slot;
|
|
18
|
+
if (slot) {
|
|
19
|
+
const handler = (this.handler = value => {
|
|
20
|
+
this.children.forEach(child => {
|
|
21
|
+
child.destroy();
|
|
22
|
+
});
|
|
23
|
+
this.children.clear();
|
|
24
|
+
this.lastChild = undefined;
|
|
25
|
+
slot(this, value);
|
|
15
26
|
});
|
|
16
|
-
this.
|
|
17
|
-
this.
|
|
18
|
-
|
|
19
|
-
|
|
27
|
+
this.model.on(handler);
|
|
28
|
+
handler(this.model.V);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
destroy() {
|
|
32
|
+
if (this.handler) {
|
|
33
|
+
this.model.off(this.handler);
|
|
34
|
+
}
|
|
35
|
+
super.destroy();
|
|
20
36
|
}
|
|
21
37
|
}
|
|
@@ -1,28 +1,26 @@
|
|
|
1
|
-
import { Destroyable } from "../../../core/destroyable.js";
|
|
2
1
|
/**
|
|
3
2
|
* Describe a common binding logic
|
|
4
3
|
* @class Binding
|
|
5
|
-
* @extends Destroyable
|
|
6
4
|
*/
|
|
7
|
-
export class Binding
|
|
5
|
+
export class Binding {
|
|
6
|
+
binding;
|
|
7
|
+
func;
|
|
8
8
|
/**
|
|
9
9
|
* Constructs a common binding logic
|
|
10
10
|
* @param value {IValue} the value to bind
|
|
11
11
|
*/
|
|
12
12
|
constructor(value) {
|
|
13
|
-
super();
|
|
14
13
|
this.binding = value;
|
|
15
14
|
}
|
|
16
15
|
init(bounded) {
|
|
17
16
|
this.func = bounded;
|
|
18
17
|
this.binding.on(this.func);
|
|
19
|
-
this.func(this.binding
|
|
18
|
+
this.func(this.binding.V);
|
|
20
19
|
}
|
|
21
20
|
/**
|
|
22
21
|
* Just clear bindings
|
|
23
22
|
*/
|
|
24
23
|
destroy() {
|
|
25
24
|
this.binding.off(this.func);
|
|
26
|
-
super.destroy();
|
|
27
25
|
}
|
|
28
26
|
}
|
|
@@ -1,14 +1,27 @@
|
|
|
1
1
|
import { Binding } from "./binding.js";
|
|
2
|
-
function addClass(node, cl) {
|
|
3
|
-
node.element.classList
|
|
2
|
+
export function addClass(node, cl) {
|
|
3
|
+
if (process.env.VASILLE_TARGET === "es5" && !node.element.classList) {
|
|
4
|
+
node.element.className = [...node.element.className.split(" "), cl].filter(item => !!item).join(" ");
|
|
5
|
+
}
|
|
6
|
+
else {
|
|
7
|
+
node.element.classList.add(cl);
|
|
8
|
+
}
|
|
4
9
|
}
|
|
5
|
-
function removeClass(node, cl) {
|
|
6
|
-
node.element.classList
|
|
10
|
+
export function removeClass(node, cl) {
|
|
11
|
+
if (process.env.VASILLE_TARGET === "es5" && !node.element.classList) {
|
|
12
|
+
node.element.className = node.element.className
|
|
13
|
+
.split(" ")
|
|
14
|
+
.filter(name => name !== cl)
|
|
15
|
+
.join(" ");
|
|
16
|
+
}
|
|
17
|
+
else {
|
|
18
|
+
node.element.classList.remove(cl);
|
|
19
|
+
}
|
|
7
20
|
}
|
|
8
21
|
export class StaticClassBinding extends Binding {
|
|
22
|
+
current = false;
|
|
9
23
|
constructor(node, name, value) {
|
|
10
24
|
super(value);
|
|
11
|
-
this.current = false;
|
|
12
25
|
this.init((value) => {
|
|
13
26
|
if (value !== this.current) {
|
|
14
27
|
if (value) {
|
|
@@ -23,9 +36,9 @@ export class StaticClassBinding extends Binding {
|
|
|
23
36
|
}
|
|
24
37
|
}
|
|
25
38
|
export class DynamicalClassBinding extends Binding {
|
|
39
|
+
current = "";
|
|
26
40
|
constructor(node, value) {
|
|
27
41
|
super(value);
|
|
28
|
-
this.current = "";
|
|
29
42
|
this.init((value) => {
|
|
30
43
|
/* istanbul ignore else */
|
|
31
44
|
if (this.current != value) {
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { Binding } from "./binding.js";
|
|
2
|
+
/**
|
|
3
|
+
* Represents a property binding
|
|
4
|
+
* @class PropertyBinding
|
|
5
|
+
* @extends Binding
|
|
6
|
+
*/
|
|
7
|
+
export class PropertyBinding extends Binding {
|
|
8
|
+
/**
|
|
9
|
+
* Constructs a property binding description
|
|
10
|
+
* @param node the vasille node
|
|
11
|
+
* @param name the name of property
|
|
12
|
+
* @param value the value of property
|
|
13
|
+
*/
|
|
14
|
+
constructor(node, name, value) {
|
|
15
|
+
super(value);
|
|
16
|
+
this.init(value => {
|
|
17
|
+
node.element[name] = value;
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
}
|
package/lib/runner/web/runner.js
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import { TextNode as AbstractTextNode, DebugNode as AbstractDebugNode, Tag as AbstractTag, IValue, } from "../../index.js";
|
|
2
2
|
import { internalError } from "../../core/errors.js";
|
|
3
3
|
import { AttributeBinding } from "./binding/attribute.js";
|
|
4
|
-
import { DynamicalClassBinding, StaticClassBinding } from "./binding/class.js";
|
|
4
|
+
import { addClass, DynamicalClassBinding, removeClass, StaticClassBinding } from "./binding/class.js";
|
|
5
|
+
import { PropertyBinding } from "./binding/property.js";
|
|
5
6
|
import { stringifyStyleValue, StyleBinding } from "./binding/style.js";
|
|
6
7
|
export class TextNode extends AbstractTextNode {
|
|
8
|
+
node;
|
|
7
9
|
compose() {
|
|
8
|
-
const text = this.
|
|
9
|
-
this.node = this.runner.document.createTextNode((text instanceof IValue ? text
|
|
10
|
+
const text = this.data;
|
|
11
|
+
this.node = this.runner.document.createTextNode((text instanceof IValue ? text.V : text)?.toString() ?? "");
|
|
10
12
|
if (text instanceof IValue) {
|
|
11
13
|
this.handler = (v) => {
|
|
12
14
|
this.node.replaceData(0, -1, v?.toString() ?? "");
|
|
@@ -24,9 +26,10 @@ export class TextNode extends AbstractTextNode {
|
|
|
24
26
|
}
|
|
25
27
|
}
|
|
26
28
|
export class DebugNode extends AbstractDebugNode {
|
|
29
|
+
node;
|
|
27
30
|
compose() {
|
|
28
|
-
const text = this.
|
|
29
|
-
this.node = this.runner.document.createComment(text
|
|
31
|
+
const text = this.data;
|
|
32
|
+
this.node = this.runner.document.createComment(text.V?.toString() ?? "");
|
|
30
33
|
this.handler = (v) => {
|
|
31
34
|
this.node.replaceData(0, -1, v?.toString() ?? "");
|
|
32
35
|
};
|
|
@@ -48,10 +51,10 @@ export class Tag extends AbstractTag {
|
|
|
48
51
|
}
|
|
49
52
|
const node = this.runner.document.createElement(this.name);
|
|
50
53
|
this.node = node;
|
|
51
|
-
this.applyOptions(this.
|
|
54
|
+
this.applyOptions(this.options);
|
|
52
55
|
this.parent.appendNode(node);
|
|
53
|
-
this.
|
|
54
|
-
this.
|
|
56
|
+
this.options.callback?.(this.node);
|
|
57
|
+
this.options.slot?.(this);
|
|
55
58
|
}
|
|
56
59
|
destroy() {
|
|
57
60
|
this.node.remove();
|
|
@@ -62,7 +65,7 @@ export class Tag extends AbstractTag {
|
|
|
62
65
|
for (const name in options.attr) {
|
|
63
66
|
const value = options.attr[name];
|
|
64
67
|
if (value instanceof IValue) {
|
|
65
|
-
this.
|
|
68
|
+
this.bind(new AttributeBinding(this, name, value));
|
|
66
69
|
}
|
|
67
70
|
else {
|
|
68
71
|
/* istanbul ignore else */
|
|
@@ -79,34 +82,34 @@ export class Tag extends AbstractTag {
|
|
|
79
82
|
}
|
|
80
83
|
}
|
|
81
84
|
if (options.class) {
|
|
82
|
-
|
|
85
|
+
options.class.forEach(item => {
|
|
83
86
|
if (item instanceof IValue) {
|
|
84
|
-
this.
|
|
87
|
+
this.bind(new DynamicalClassBinding(this, item));
|
|
85
88
|
}
|
|
86
89
|
else if (typeof item == "string") {
|
|
87
|
-
this
|
|
90
|
+
addClass(this, item);
|
|
88
91
|
}
|
|
89
92
|
else {
|
|
90
93
|
for (const name in item) {
|
|
91
94
|
const value = item[name];
|
|
92
95
|
if (value instanceof IValue) {
|
|
93
|
-
this.
|
|
96
|
+
this.bind(new StaticClassBinding(this, name, value));
|
|
94
97
|
}
|
|
95
98
|
else if (value) {
|
|
96
|
-
this
|
|
99
|
+
addClass(this, name);
|
|
97
100
|
}
|
|
98
101
|
else {
|
|
99
|
-
this
|
|
102
|
+
removeClass(this, name);
|
|
100
103
|
}
|
|
101
104
|
}
|
|
102
105
|
}
|
|
103
|
-
}
|
|
106
|
+
});
|
|
104
107
|
}
|
|
105
108
|
if (options.style && this.node instanceof HTMLElement) {
|
|
106
109
|
for (const name in options.style) {
|
|
107
110
|
const value = options.style[name];
|
|
108
111
|
if (value instanceof IValue) {
|
|
109
|
-
this.
|
|
112
|
+
this.bind(new StyleBinding(this, name, value));
|
|
110
113
|
}
|
|
111
114
|
else {
|
|
112
115
|
this.node.style.setProperty(name, stringifyStyleValue(value));
|
|
@@ -115,7 +118,13 @@ export class Tag extends AbstractTag {
|
|
|
115
118
|
}
|
|
116
119
|
if (options.events) {
|
|
117
120
|
for (const name in options.events) {
|
|
118
|
-
|
|
121
|
+
const event = options.events[name];
|
|
122
|
+
if (event instanceof Array) {
|
|
123
|
+
this.node.addEventListener(name, event[0], event[1]);
|
|
124
|
+
}
|
|
125
|
+
else {
|
|
126
|
+
this.node.addEventListener(name, event);
|
|
127
|
+
}
|
|
119
128
|
}
|
|
120
129
|
}
|
|
121
130
|
if (options.bind) {
|
|
@@ -126,16 +135,16 @@ export class Tag extends AbstractTag {
|
|
|
126
135
|
node[k] = value;
|
|
127
136
|
}
|
|
128
137
|
else {
|
|
129
|
-
node[k] = value
|
|
130
|
-
this.
|
|
131
|
-
node[k] = v;
|
|
132
|
-
}, [value]);
|
|
138
|
+
node[k] = value.V;
|
|
139
|
+
this.bind(new PropertyBinding(this, k, value));
|
|
133
140
|
}
|
|
134
141
|
}
|
|
135
142
|
}
|
|
136
143
|
}
|
|
137
144
|
}
|
|
138
145
|
export class Runner {
|
|
146
|
+
debugUi;
|
|
147
|
+
document;
|
|
139
148
|
constructor(debugUi, document) {
|
|
140
149
|
this.debugUi = debugUi;
|
|
141
150
|
this.document = document;
|
package/lib/value/expression.js
CHANGED
|
@@ -6,43 +6,52 @@ import { IValue } from "../core/ivalue.js";
|
|
|
6
6
|
* @extends IValue
|
|
7
7
|
*/
|
|
8
8
|
export class Expression extends IValue {
|
|
9
|
+
/**
|
|
10
|
+
* The array of value which will trigger recalculation
|
|
11
|
+
* @type {Array}
|
|
12
|
+
*/
|
|
13
|
+
values;
|
|
14
|
+
/**
|
|
15
|
+
* Cache the values of expression variables
|
|
16
|
+
* @type {Array}
|
|
17
|
+
*/
|
|
18
|
+
valuesCache;
|
|
19
|
+
/**
|
|
20
|
+
* Expression will link different handler for each value of the list
|
|
21
|
+
*/
|
|
22
|
+
linkedFunc = [];
|
|
23
|
+
/**
|
|
24
|
+
* The buffer to keep the last calculated value
|
|
25
|
+
*/
|
|
26
|
+
sync;
|
|
9
27
|
/**
|
|
10
28
|
* Creates a function bounded to N values
|
|
11
|
-
* @param func {Function} the function to bound
|
|
12
|
-
* @param values
|
|
13
|
-
* @param link {Boolean} links immediately if true
|
|
14
29
|
*/
|
|
15
|
-
constructor(func, values) {
|
|
30
|
+
constructor(func, values, ctx) {
|
|
16
31
|
super();
|
|
17
|
-
/**
|
|
18
|
-
* Expression will link different handler for each value of the list
|
|
19
|
-
*/
|
|
20
|
-
this.linkedFunc = [];
|
|
21
32
|
const handler = (i) => {
|
|
22
33
|
/* istanbul ignore else */
|
|
23
34
|
if (typeof i === "number") {
|
|
24
|
-
this.valuesCache[i] = this.values[i]
|
|
35
|
+
this.valuesCache[i] = this.values[i]?.V;
|
|
25
36
|
}
|
|
26
|
-
this.sync
|
|
37
|
+
this.sync.V = func.apply(this, this.valuesCache);
|
|
27
38
|
};
|
|
28
|
-
|
|
29
|
-
// @ts-ignore
|
|
30
|
-
this.valuesCache = values.map(item => item.$);
|
|
39
|
+
this.valuesCache = values.map(item => item?.V);
|
|
31
40
|
this.sync = new Reference(func.apply(this, this.valuesCache));
|
|
32
41
|
let i = 0;
|
|
33
42
|
values.forEach(value => {
|
|
34
43
|
const updater = handler.bind(this, Number(i++));
|
|
35
44
|
this.linkedFunc.push(updater);
|
|
36
|
-
value
|
|
45
|
+
value?.on(updater);
|
|
37
46
|
});
|
|
38
47
|
this.values = values;
|
|
39
|
-
this
|
|
48
|
+
ctx?.bind(this);
|
|
40
49
|
}
|
|
41
|
-
get
|
|
42
|
-
return this.sync
|
|
50
|
+
get V() {
|
|
51
|
+
return this.sync.V;
|
|
43
52
|
}
|
|
44
|
-
set
|
|
45
|
-
this.sync
|
|
53
|
+
set V(value) {
|
|
54
|
+
this.sync.V = value;
|
|
46
55
|
}
|
|
47
56
|
on(handler) {
|
|
48
57
|
this.sync.on(handler);
|
|
@@ -52,11 +61,10 @@ export class Expression extends IValue {
|
|
|
52
61
|
}
|
|
53
62
|
destroy() {
|
|
54
63
|
for (let i = 0; i < this.values.length; i++) {
|
|
55
|
-
this.values[i]
|
|
64
|
+
this.values[i]?.off(this.linkedFunc[i]);
|
|
56
65
|
}
|
|
57
66
|
this.values.splice(0);
|
|
58
67
|
this.valuesCache.splice(0);
|
|
59
68
|
this.linkedFunc.splice(0);
|
|
60
|
-
super.destroy();
|
|
61
69
|
}
|
|
62
70
|
}
|