@microsoft/fast-element 2.0.0-beta.5 → 2.0.0-beta.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.
- package/CHANGELOG.json +72 -0
- package/CHANGELOG.md +24 -1
- package/dist/dts/components/attributes.d.ts +10 -0
- package/dist/dts/components/{controller.d.ts → element-controller.d.ts} +24 -25
- package/dist/dts/components/fast-definitions.d.ts +28 -3
- package/dist/dts/components/fast-element.d.ts +2 -2
- package/dist/dts/di/di.d.ts +41 -0
- package/dist/dts/index.d.ts +2 -2
- package/dist/dts/interfaces.d.ts +4 -0
- package/dist/dts/observation/observable.d.ts +86 -47
- package/dist/dts/pending-task.d.ts +20 -0
- package/dist/dts/platform.d.ts +6 -0
- package/dist/dts/state/exports.d.ts +3 -0
- package/dist/dts/state/reactive.d.ts +8 -0
- package/dist/dts/state/state.d.ts +141 -0
- package/dist/dts/state/visitor.d.ts +6 -0
- package/dist/dts/state/watch.d.ts +10 -0
- package/dist/dts/styles/css-directive.d.ts +2 -2
- package/dist/dts/styles/element-styles.d.ts +3 -3
- package/dist/dts/styles/host.d.ts +68 -0
- package/dist/dts/templating/binding-signal.d.ts +2 -2
- package/dist/dts/templating/binding-two-way.d.ts +11 -3
- package/dist/dts/templating/binding.d.ts +21 -119
- package/dist/dts/templating/children.d.ts +1 -1
- package/dist/dts/templating/html-directive.d.ts +69 -39
- package/dist/dts/templating/node-observation.d.ts +4 -5
- package/dist/dts/templating/ref.d.ts +5 -13
- package/dist/dts/templating/render.d.ts +15 -20
- package/dist/dts/templating/repeat.d.ts +11 -16
- package/dist/dts/templating/slotted.d.ts +1 -1
- package/dist/dts/templating/template.d.ts +4 -4
- package/dist/dts/templating/view.d.ts +68 -9
- package/dist/dts/templating/when.d.ts +1 -1
- package/dist/dts/testing/exports.d.ts +1 -0
- package/dist/dts/testing/fakes.d.ts +4 -0
- package/dist/dts/testing/fixture.d.ts +0 -6
- package/dist/dts/utilities.d.ts +0 -18
- package/dist/esm/components/attributes.js +13 -4
- package/dist/esm/components/{controller.js → element-controller.js} +95 -105
- package/dist/esm/components/fast-definitions.js +3 -1
- package/dist/esm/components/fast-element.js +4 -4
- package/dist/esm/di/di.js +87 -3
- package/dist/esm/index.js +2 -1
- package/dist/esm/interfaces.js +4 -0
- package/dist/esm/observation/observable.js +59 -126
- package/dist/esm/pending-task.js +16 -0
- package/dist/esm/platform.js +21 -0
- package/dist/esm/state/exports.js +3 -0
- package/dist/esm/state/reactive.js +34 -0
- package/dist/esm/state/state.js +148 -0
- package/dist/esm/state/visitor.js +28 -0
- package/dist/esm/state/watch.js +36 -0
- package/dist/esm/styles/css.js +4 -4
- package/dist/esm/{observation/behavior.js → styles/host.js} +0 -0
- package/dist/esm/templating/binding-signal.js +21 -17
- package/dist/esm/templating/binding-two-way.js +32 -27
- package/dist/esm/templating/binding.js +73 -177
- package/dist/esm/templating/html-directive.js +78 -7
- package/dist/esm/templating/node-observation.js +9 -8
- package/dist/esm/templating/ref.js +4 -12
- package/dist/esm/templating/render.js +30 -31
- package/dist/esm/templating/repeat.js +37 -38
- package/dist/esm/templating/template.js +3 -4
- package/dist/esm/templating/view.js +96 -24
- package/dist/esm/testing/exports.js +1 -0
- package/dist/esm/testing/fakes.js +76 -0
- package/dist/esm/testing/fixture.js +1 -3
- package/dist/esm/utilities.js +0 -95
- package/dist/fast-element.api.json +5720 -5385
- package/dist/fast-element.d.ts +510 -399
- package/dist/fast-element.debug.js +492 -509
- package/dist/fast-element.debug.min.js +1 -1
- package/dist/fast-element.js +492 -509
- package/dist/fast-element.min.js +1 -1
- package/dist/fast-element.untrimmed.d.ts +519 -405
- package/docs/api-report.md +197 -129
- package/package.json +8 -4
- package/dist/dts/hooks.d.ts +0 -20
- package/dist/dts/observation/behavior.d.ts +0 -19
- package/dist/esm/hooks.js +0 -32
|
@@ -1,7 +1,22 @@
|
|
|
1
1
|
import { isFunction, isString, } from "../interfaces.js";
|
|
2
|
-
import { FAST } from "../platform.js";
|
|
2
|
+
import { createMetadataLocator, FAST } from "../platform.js";
|
|
3
3
|
import { Updates } from "./update-queue.js";
|
|
4
4
|
import { PropertyChangeNotifier, SubscriberSet } from "./notifier.js";
|
|
5
|
+
/**
|
|
6
|
+
* Describes how the source's lifetime relates to its controller's lifetime.
|
|
7
|
+
* @public
|
|
8
|
+
*/
|
|
9
|
+
export const SourceLifetime = Object.freeze({
|
|
10
|
+
/**
|
|
11
|
+
* The source to controller lifetime relationship is unknown.
|
|
12
|
+
*/
|
|
13
|
+
unknown: void 0,
|
|
14
|
+
/**
|
|
15
|
+
* The source and controller lifetimes are coupled to one another.
|
|
16
|
+
* They can/will be GC'd together.
|
|
17
|
+
*/
|
|
18
|
+
coupled: 1,
|
|
19
|
+
});
|
|
5
20
|
/**
|
|
6
21
|
* Common Observable APIs.
|
|
7
22
|
* @public
|
|
@@ -10,7 +25,6 @@ export const Observable = FAST.getById(2 /* KernelServiceId.observable */, () =>
|
|
|
10
25
|
const queueUpdate = Updates.enqueue;
|
|
11
26
|
const volatileRegex = /(:|&&|\|\||if)/;
|
|
12
27
|
const notifierLookup = new WeakMap();
|
|
13
|
-
const accessorLookup = new WeakMap();
|
|
14
28
|
let watcher = void 0;
|
|
15
29
|
let createArrayObserver = (array) => {
|
|
16
30
|
throw FAST.error(1101 /* Message.needsArrayObservation */);
|
|
@@ -25,19 +39,7 @@ export const Observable = FAST.getById(2 /* KernelServiceId.observable */, () =>
|
|
|
25
39
|
}
|
|
26
40
|
return found;
|
|
27
41
|
}
|
|
28
|
-
|
|
29
|
-
let accessors = accessorLookup.get(target);
|
|
30
|
-
if (accessors === void 0) {
|
|
31
|
-
let currentTarget = Reflect.getPrototypeOf(target);
|
|
32
|
-
while (accessors === void 0 && currentTarget !== null) {
|
|
33
|
-
accessors = accessorLookup.get(currentTarget);
|
|
34
|
-
currentTarget = Reflect.getPrototypeOf(currentTarget);
|
|
35
|
-
}
|
|
36
|
-
accessors = accessors === void 0 ? [] : accessors.slice(0);
|
|
37
|
-
accessorLookup.set(target, accessors);
|
|
38
|
-
}
|
|
39
|
-
return accessors;
|
|
40
|
-
}
|
|
42
|
+
const getAccessors = createMetadataLocator();
|
|
41
43
|
class DefaultObservableAccessor {
|
|
42
44
|
constructor(name) {
|
|
43
45
|
this.name = name;
|
|
@@ -81,6 +83,22 @@ export const Observable = FAST.getById(2 /* KernelServiceId.observable */, () =>
|
|
|
81
83
|
setMode(isAsync) {
|
|
82
84
|
this.isAsync = this.needsQueue = isAsync;
|
|
83
85
|
}
|
|
86
|
+
bind(controller) {
|
|
87
|
+
this.controller = controller;
|
|
88
|
+
const value = this.observe(controller.source, controller.context);
|
|
89
|
+
if (!controller.isBound && this.requiresUnbind(controller)) {
|
|
90
|
+
controller.onUnbind(this);
|
|
91
|
+
}
|
|
92
|
+
return value;
|
|
93
|
+
}
|
|
94
|
+
requiresUnbind(controller) {
|
|
95
|
+
return (controller.sourceLifetime !== SourceLifetime.coupled ||
|
|
96
|
+
this.first !== this.last ||
|
|
97
|
+
this.first.propertySource !== controller.source);
|
|
98
|
+
}
|
|
99
|
+
unbind(controller) {
|
|
100
|
+
this.dispose();
|
|
101
|
+
}
|
|
84
102
|
observe(source, context) {
|
|
85
103
|
if (this.needsRefresh && this.last !== null) {
|
|
86
104
|
this.dispose();
|
|
@@ -90,7 +108,7 @@ export const Observable = FAST.getById(2 /* KernelServiceId.observable */, () =>
|
|
|
90
108
|
this.needsRefresh = this.isVolatileBinding;
|
|
91
109
|
let result;
|
|
92
110
|
try {
|
|
93
|
-
result = this.binding(source, context
|
|
111
|
+
result = this.binding(source, context);
|
|
94
112
|
}
|
|
95
113
|
finally {
|
|
96
114
|
watcher = previousWatcher;
|
|
@@ -281,120 +299,35 @@ const contextEvent = FAST.getById(3 /* KernelServiceId.contextEvent */, () => {
|
|
|
281
299
|
* Provides additional contextual information available to behaviors and expressions.
|
|
282
300
|
* @public
|
|
283
301
|
*/
|
|
284
|
-
export
|
|
285
|
-
constructor(parentSource = null, parentContext = null) {
|
|
286
|
-
/**
|
|
287
|
-
* The index of the current item within a repeat context.
|
|
288
|
-
*/
|
|
289
|
-
this.index = 0;
|
|
290
|
-
/**
|
|
291
|
-
* The length of the current collection within a repeat context.
|
|
292
|
-
*/
|
|
293
|
-
this.length = 0;
|
|
294
|
-
this.parent = parentSource;
|
|
295
|
-
this.parentContext = parentContext;
|
|
296
|
-
}
|
|
297
|
-
/**
|
|
298
|
-
* The current event within an event handler.
|
|
299
|
-
*/
|
|
300
|
-
get event() {
|
|
301
|
-
return contextEvent.get();
|
|
302
|
-
}
|
|
303
|
-
/**
|
|
304
|
-
* Indicates whether the current item within a repeat context
|
|
305
|
-
* has an even index.
|
|
306
|
-
*/
|
|
307
|
-
get isEven() {
|
|
308
|
-
return this.index % 2 === 0;
|
|
309
|
-
}
|
|
310
|
-
/**
|
|
311
|
-
* Indicates whether the current item within a repeat context
|
|
312
|
-
* has an odd index.
|
|
313
|
-
*/
|
|
314
|
-
get isOdd() {
|
|
315
|
-
return this.index % 2 !== 0;
|
|
316
|
-
}
|
|
317
|
-
/**
|
|
318
|
-
* Indicates whether the current item within a repeat context
|
|
319
|
-
* is the first item in the collection.
|
|
320
|
-
*/
|
|
321
|
-
get isFirst() {
|
|
322
|
-
return this.index === 0;
|
|
323
|
-
}
|
|
324
|
-
/**
|
|
325
|
-
* Indicates whether the current item within a repeat context
|
|
326
|
-
* is somewhere in the middle of the collection.
|
|
327
|
-
*/
|
|
328
|
-
get isInMiddle() {
|
|
329
|
-
return !this.isFirst && !this.isLast;
|
|
330
|
-
}
|
|
331
|
-
/**
|
|
332
|
-
* Indicates whether the current item within a repeat context
|
|
333
|
-
* is the last item in the collection.
|
|
334
|
-
*/
|
|
335
|
-
get isLast() {
|
|
336
|
-
return this.index === this.length - 1;
|
|
337
|
-
}
|
|
302
|
+
export const ExecutionContext = Object.freeze({
|
|
338
303
|
/**
|
|
339
|
-
*
|
|
304
|
+
* A default execution context.
|
|
340
305
|
*/
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
*/
|
|
355
|
-
updatePosition(index, length) {
|
|
356
|
-
this.index = index;
|
|
357
|
-
this.length = length;
|
|
358
|
-
}
|
|
359
|
-
/**
|
|
360
|
-
* Creates a new execution context descendent from the current context.
|
|
361
|
-
* @param source - The source for the context if different than the parent.
|
|
362
|
-
* @returns A child execution context.
|
|
363
|
-
*/
|
|
364
|
-
createChildContext(parentSource) {
|
|
365
|
-
return new ExecutionContext(parentSource, this);
|
|
366
|
-
}
|
|
306
|
+
default: {
|
|
307
|
+
index: 0,
|
|
308
|
+
length: 0,
|
|
309
|
+
get event() {
|
|
310
|
+
return ExecutionContext.getEvent();
|
|
311
|
+
},
|
|
312
|
+
eventDetail() {
|
|
313
|
+
return this.event.detail;
|
|
314
|
+
},
|
|
315
|
+
eventTarget() {
|
|
316
|
+
return this.event.target;
|
|
317
|
+
},
|
|
318
|
+
},
|
|
367
319
|
/**
|
|
368
|
-
*
|
|
369
|
-
* @
|
|
370
|
-
* @param index - The index of the item in the list.
|
|
371
|
-
* @param length - The length of the list.
|
|
320
|
+
* Gets the current event.
|
|
321
|
+
* @returns An event object.
|
|
372
322
|
*/
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
childContext.length = length;
|
|
377
|
-
return childContext;
|
|
378
|
-
}
|
|
323
|
+
getEvent() {
|
|
324
|
+
return contextEvent.get();
|
|
325
|
+
},
|
|
379
326
|
/**
|
|
380
|
-
* Sets the
|
|
381
|
-
* @param event -
|
|
382
|
-
* @internal
|
|
327
|
+
* Sets the current event.
|
|
328
|
+
* @param event - An event object.
|
|
383
329
|
*/
|
|
384
|
-
|
|
330
|
+
setEvent(event) {
|
|
385
331
|
contextEvent.set(event);
|
|
386
|
-
}
|
|
387
|
-
|
|
388
|
-
* Creates a new root execution context.
|
|
389
|
-
* @returns A new execution context.
|
|
390
|
-
*/
|
|
391
|
-
static create() {
|
|
392
|
-
return new ExecutionContext();
|
|
393
|
-
}
|
|
394
|
-
}
|
|
395
|
-
/**
|
|
396
|
-
* The default execution context.
|
|
397
|
-
*/
|
|
398
|
-
ExecutionContext.default = new ExecutionContext();
|
|
399
|
-
Observable.defineProperty(ExecutionContext.prototype, "index");
|
|
400
|
-
Observable.defineProperty(ExecutionContext.prototype, "length");
|
|
332
|
+
},
|
|
333
|
+
});
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A concrete implementation of {@link PendingTask}
|
|
3
|
+
* @beta
|
|
4
|
+
*/
|
|
5
|
+
export class PendingTaskEvent extends Event {
|
|
6
|
+
constructor(complete) {
|
|
7
|
+
super(PendingTaskEvent.type, { bubbles: true, composed: true });
|
|
8
|
+
this.complete = complete;
|
|
9
|
+
}
|
|
10
|
+
static isPendingTask(value) {
|
|
11
|
+
var _a;
|
|
12
|
+
return (value.type === PendingTaskEvent.type &&
|
|
13
|
+
typeof ((_a = value.complete) === null || _a === void 0 ? void 0 : _a.then) === "function");
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
PendingTaskEvent.type = "pending-task";
|
package/dist/esm/platform.js
CHANGED
|
@@ -61,3 +61,24 @@ export function createTypeRegistry() {
|
|
|
61
61
|
},
|
|
62
62
|
});
|
|
63
63
|
}
|
|
64
|
+
/**
|
|
65
|
+
* Creates a function capable of locating metadata associated with a type.
|
|
66
|
+
* @returns A metadata locator function.
|
|
67
|
+
* @internal
|
|
68
|
+
*/
|
|
69
|
+
export function createMetadataLocator() {
|
|
70
|
+
const metadataLookup = new WeakMap();
|
|
71
|
+
return function (target) {
|
|
72
|
+
let metadata = metadataLookup.get(target);
|
|
73
|
+
if (metadata === void 0) {
|
|
74
|
+
let currentTarget = Reflect.getPrototypeOf(target);
|
|
75
|
+
while (metadata === void 0 && currentTarget !== null) {
|
|
76
|
+
metadata = metadataLookup.get(currentTarget);
|
|
77
|
+
currentTarget = Reflect.getPrototypeOf(currentTarget);
|
|
78
|
+
}
|
|
79
|
+
metadata = metadata === void 0 ? [] : metadata.slice(0);
|
|
80
|
+
metadataLookup.set(target, metadata);
|
|
81
|
+
}
|
|
82
|
+
return metadata;
|
|
83
|
+
};
|
|
84
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { noop } from "../interfaces.js";
|
|
2
|
+
import { Observable } from "../observation/observable.js";
|
|
3
|
+
import { visitObject } from "./visitor.js";
|
|
4
|
+
const observed = new WeakSet();
|
|
5
|
+
const makeObserverVisitor = {
|
|
6
|
+
visitObject: noop,
|
|
7
|
+
visitArray: noop,
|
|
8
|
+
visitProperty(object, propertyName, value) {
|
|
9
|
+
Reflect.defineProperty(object, propertyName, {
|
|
10
|
+
enumerable: true,
|
|
11
|
+
get() {
|
|
12
|
+
Observable.track(object, propertyName);
|
|
13
|
+
return value;
|
|
14
|
+
},
|
|
15
|
+
set(newValue) {
|
|
16
|
+
if (value !== newValue) {
|
|
17
|
+
value = newValue;
|
|
18
|
+
Observable.notify(object, propertyName);
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
});
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* Converts a plain object to a reactive, observable object.
|
|
26
|
+
* @param object - The object to make reactive.
|
|
27
|
+
* @param deep - Indicates whether or not to deeply convert the oject.
|
|
28
|
+
* @returns The converted object.
|
|
29
|
+
* @beta
|
|
30
|
+
*/
|
|
31
|
+
export function reactive(object, deep = false) {
|
|
32
|
+
visitObject(object, deep, makeObserverVisitor, void 0, observed);
|
|
33
|
+
return object;
|
|
34
|
+
}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
// Inspired by https://www.starbeamjs.com/
|
|
2
|
+
import { isFunction, isString } from "../interfaces.js";
|
|
3
|
+
import { Observable } from "../observation/observable.js";
|
|
4
|
+
import { reactive } from "./reactive.js";
|
|
5
|
+
const defaultStateOptions = {
|
|
6
|
+
deep: false,
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* Creates a reactive state value.
|
|
10
|
+
* @param value - The initial state value.
|
|
11
|
+
* @param options - Options to customize the state or a friendly name.
|
|
12
|
+
* @returns A State instance.
|
|
13
|
+
* @beta
|
|
14
|
+
*/
|
|
15
|
+
export function state(value, options = defaultStateOptions) {
|
|
16
|
+
var _a;
|
|
17
|
+
if (isString(options)) {
|
|
18
|
+
options = { deep: false, name: options };
|
|
19
|
+
}
|
|
20
|
+
const host = reactive({ value }, options.deep);
|
|
21
|
+
const state = (() => host.value);
|
|
22
|
+
Object.defineProperty(state, "current", {
|
|
23
|
+
get: () => host.value,
|
|
24
|
+
set: (value) => (host.value = value),
|
|
25
|
+
});
|
|
26
|
+
Object.defineProperty(state, "name", {
|
|
27
|
+
value: (_a = options.name) !== null && _a !== void 0 ? _a : "SharedState",
|
|
28
|
+
});
|
|
29
|
+
state.set = (value) => (host.value = value);
|
|
30
|
+
state.asReadonly = () => {
|
|
31
|
+
const readonlyState = (() => host.value);
|
|
32
|
+
Object.defineProperty(readonlyState, "current", {
|
|
33
|
+
get: () => host.value,
|
|
34
|
+
});
|
|
35
|
+
Object.defineProperty(readonlyState, "name", {
|
|
36
|
+
value: `${state.name} (Readonly)`,
|
|
37
|
+
});
|
|
38
|
+
return Object.freeze(readonlyState);
|
|
39
|
+
};
|
|
40
|
+
return state;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Creates a reactive state that has its value associated with a specific owner.
|
|
44
|
+
* @param value - The initial value or a factory that provides an initial value for each owner.
|
|
45
|
+
* @param options - Options to customize the state or a friendly name.
|
|
46
|
+
* @returns An OwnedState instance.
|
|
47
|
+
* @beta
|
|
48
|
+
*/
|
|
49
|
+
export function ownedState(value, options = defaultStateOptions) {
|
|
50
|
+
var _a;
|
|
51
|
+
if (isString(options)) {
|
|
52
|
+
options = { deep: false, name: options };
|
|
53
|
+
}
|
|
54
|
+
if (!isFunction(value)) {
|
|
55
|
+
const v = value;
|
|
56
|
+
value = () => v;
|
|
57
|
+
}
|
|
58
|
+
const storage = new WeakMap();
|
|
59
|
+
const getHost = (owner) => {
|
|
60
|
+
let host = storage.get(owner);
|
|
61
|
+
if (host === void 0) {
|
|
62
|
+
host = reactive({ value: value() }, options.deep);
|
|
63
|
+
storage.set(owner, host);
|
|
64
|
+
}
|
|
65
|
+
return host;
|
|
66
|
+
};
|
|
67
|
+
const state = ((owner) => getHost(owner).value);
|
|
68
|
+
Object.defineProperty(state, "name", {
|
|
69
|
+
value: (_a = options.name) !== null && _a !== void 0 ? _a : "OwnedState",
|
|
70
|
+
});
|
|
71
|
+
state.set = (owner, value) => (getHost(owner).value = value);
|
|
72
|
+
state.asReadonly = () => {
|
|
73
|
+
const readonlyState = ((owner) => getHost(owner).value);
|
|
74
|
+
Object.defineProperty(readonlyState, "name", {
|
|
75
|
+
value: `${state.name} (Readonly)`,
|
|
76
|
+
});
|
|
77
|
+
return Object.freeze(readonlyState);
|
|
78
|
+
};
|
|
79
|
+
return state;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Creates a ComputedState.
|
|
83
|
+
* @param initialize - The initialization callback.
|
|
84
|
+
* @param name - A friendly name for this computation.
|
|
85
|
+
* @returns A ComputedState
|
|
86
|
+
* @beta
|
|
87
|
+
*/
|
|
88
|
+
export function computedState(initialize, name = "ComputedState") {
|
|
89
|
+
let setupCallback = null;
|
|
90
|
+
const builder = {
|
|
91
|
+
on: {
|
|
92
|
+
setup(callback) {
|
|
93
|
+
setupCallback = callback;
|
|
94
|
+
},
|
|
95
|
+
},
|
|
96
|
+
};
|
|
97
|
+
const computer = initialize(builder);
|
|
98
|
+
const host = reactive({ value: null }, false);
|
|
99
|
+
const output = (() => host.value);
|
|
100
|
+
Object.defineProperty(output, "current", {
|
|
101
|
+
get: () => host.value,
|
|
102
|
+
});
|
|
103
|
+
Object.defineProperty(output, "name", {
|
|
104
|
+
value: name,
|
|
105
|
+
});
|
|
106
|
+
// eslint-disable-next-line prefer-const
|
|
107
|
+
let computedNotifier;
|
|
108
|
+
const computedSubscriber = {
|
|
109
|
+
handleChange() {
|
|
110
|
+
host.value = computedNotifier.observe(null);
|
|
111
|
+
},
|
|
112
|
+
};
|
|
113
|
+
computedNotifier = Observable.binding(computer, computedSubscriber);
|
|
114
|
+
computedNotifier.setMode(false);
|
|
115
|
+
let cleanup;
|
|
116
|
+
let setupNotifier;
|
|
117
|
+
if (setupCallback) {
|
|
118
|
+
const setupSubscriber = {
|
|
119
|
+
handleChange() {
|
|
120
|
+
if (cleanup) {
|
|
121
|
+
cleanup();
|
|
122
|
+
}
|
|
123
|
+
cleanup = setupNotifier.observe(null);
|
|
124
|
+
host.value = computer();
|
|
125
|
+
},
|
|
126
|
+
};
|
|
127
|
+
setupNotifier = Observable.binding(setupCallback, setupSubscriber);
|
|
128
|
+
setupNotifier.setMode(false);
|
|
129
|
+
cleanup = setupNotifier.observe(null);
|
|
130
|
+
}
|
|
131
|
+
host.value = computedNotifier.observe(null);
|
|
132
|
+
output.dispose = () => {
|
|
133
|
+
if (cleanup) {
|
|
134
|
+
cleanup();
|
|
135
|
+
}
|
|
136
|
+
if (setupNotifier) {
|
|
137
|
+
setupNotifier.dispose();
|
|
138
|
+
}
|
|
139
|
+
computedNotifier.dispose();
|
|
140
|
+
};
|
|
141
|
+
output.subscribe = (subscriber) => {
|
|
142
|
+
computedNotifier.subscribe(subscriber);
|
|
143
|
+
};
|
|
144
|
+
output.unsubscribe = (subscriber) => {
|
|
145
|
+
computedNotifier.unsubscribe(subscriber);
|
|
146
|
+
};
|
|
147
|
+
return output;
|
|
148
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
function shouldTraverse(value, traversed) {
|
|
2
|
+
return (value !== null &&
|
|
3
|
+
value !== void 0 &&
|
|
4
|
+
typeof value === "object" &&
|
|
5
|
+
!traversed.has(value));
|
|
6
|
+
}
|
|
7
|
+
export function visitObject(object, deep, visitor, data, traversed) {
|
|
8
|
+
if (!shouldTraverse(object, traversed)) {
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
11
|
+
traversed.add(object);
|
|
12
|
+
if (Array.isArray(object)) {
|
|
13
|
+
visitor.visitArray(object, data);
|
|
14
|
+
for (const item of object) {
|
|
15
|
+
visitObject(item, deep, visitor, data, traversed);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
else {
|
|
19
|
+
visitor.visitObject(object, data);
|
|
20
|
+
for (const key in object) {
|
|
21
|
+
const value = object[key];
|
|
22
|
+
visitor.visitProperty(object, key, value, data);
|
|
23
|
+
if (deep) {
|
|
24
|
+
visitObject(value, deep, visitor, data, traversed);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { isFunction, noop } from "../interfaces.js";
|
|
2
|
+
import { ArrayObserver } from "../observation/arrays.js";
|
|
3
|
+
import { Observable } from "../observation/observable.js";
|
|
4
|
+
import { visitObject } from "./visitor.js";
|
|
5
|
+
function watchObject(object, data) {
|
|
6
|
+
const notifier = Observable.getNotifier(object);
|
|
7
|
+
notifier.subscribe(data.subscriber);
|
|
8
|
+
data.notifiers.push(notifier);
|
|
9
|
+
}
|
|
10
|
+
const watchVisitor = {
|
|
11
|
+
visitProperty: noop,
|
|
12
|
+
visitObject: watchObject,
|
|
13
|
+
visitArray: watchObject,
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* Deeply subscribes to changes in existing observable objects.
|
|
17
|
+
* @param object - The observable object to watch.
|
|
18
|
+
* @param subscriber - The handler to call when changes are made to the object.
|
|
19
|
+
* @returns A disposable that can be used to unsubscribe from change updates.
|
|
20
|
+
* @beta
|
|
21
|
+
*/
|
|
22
|
+
export function watch(object, subscriber) {
|
|
23
|
+
const data = {
|
|
24
|
+
notifiers: [],
|
|
25
|
+
subscriber: isFunction(subscriber) ? { handleChange: subscriber } : subscriber,
|
|
26
|
+
};
|
|
27
|
+
ArrayObserver.enable();
|
|
28
|
+
visitObject(object, true, watchVisitor, data, new Set());
|
|
29
|
+
return {
|
|
30
|
+
dispose() {
|
|
31
|
+
for (const n of data.notifiers) {
|
|
32
|
+
n.unsubscribe(data.subscriber);
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
}
|
package/dist/esm/styles/css.js
CHANGED
|
@@ -71,11 +71,11 @@ class CSSPartial {
|
|
|
71
71
|
}
|
|
72
72
|
return this.css;
|
|
73
73
|
}
|
|
74
|
-
|
|
75
|
-
|
|
74
|
+
addedCallback(controller) {
|
|
75
|
+
controller.addStyles(this.styles);
|
|
76
76
|
}
|
|
77
|
-
|
|
78
|
-
|
|
77
|
+
removedCallback(controller) {
|
|
78
|
+
controller.removeStyles(this.styles);
|
|
79
79
|
}
|
|
80
80
|
}
|
|
81
81
|
CSSDirective.define(CSSPartial);
|
|
File without changes
|
|
@@ -31,7 +31,7 @@ export const Signal = Object.freeze({
|
|
|
31
31
|
const found = subscribers[signal];
|
|
32
32
|
if (found) {
|
|
33
33
|
found instanceof Set
|
|
34
|
-
? found.forEach(x => x.handleChange(
|
|
34
|
+
? found.forEach(x => x.handleChange(found, signal))
|
|
35
35
|
: found.handleChange(this, signal);
|
|
36
36
|
}
|
|
37
37
|
},
|
|
@@ -40,40 +40,44 @@ class SignalObserver {
|
|
|
40
40
|
constructor(dataBinding, subscriber) {
|
|
41
41
|
this.dataBinding = dataBinding;
|
|
42
42
|
this.subscriber = subscriber;
|
|
43
|
+
this.isNotBound = true;
|
|
43
44
|
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
45
|
+
bind(controller) {
|
|
46
|
+
if (this.isNotBound) {
|
|
47
|
+
Signal.subscribe(this.getSignal(controller), this);
|
|
48
|
+
controller.onUnbind(this);
|
|
49
|
+
this.isNotBound = false;
|
|
50
|
+
}
|
|
51
|
+
return this.dataBinding.evaluate(controller.source, controller.context);
|
|
48
52
|
}
|
|
49
|
-
|
|
50
|
-
|
|
53
|
+
unbind(controller) {
|
|
54
|
+
this.isNotBound = true;
|
|
55
|
+
Signal.unsubscribe(this.getSignal(controller), this);
|
|
51
56
|
}
|
|
52
57
|
handleChange() {
|
|
53
58
|
this.subscriber.handleChange(this.dataBinding.evaluate, this);
|
|
54
59
|
}
|
|
55
|
-
getSignal(
|
|
60
|
+
getSignal(controller) {
|
|
56
61
|
const options = this.dataBinding.options;
|
|
57
|
-
return isString(options)
|
|
62
|
+
return isString(options)
|
|
63
|
+
? options
|
|
64
|
+
: options(controller.source, controller.context);
|
|
58
65
|
}
|
|
59
66
|
}
|
|
60
67
|
class SignalBinding extends Binding {
|
|
61
|
-
constructor(evaluate, options) {
|
|
62
|
-
super();
|
|
63
|
-
this.evaluate = evaluate;
|
|
64
|
-
this.options = options;
|
|
65
|
-
}
|
|
66
68
|
createObserver(directive, subscriber) {
|
|
67
69
|
return new SignalObserver(this, subscriber);
|
|
68
70
|
}
|
|
69
71
|
}
|
|
70
72
|
/**
|
|
71
73
|
* Creates a signal binding configuration with the supplied options.
|
|
72
|
-
* @param
|
|
74
|
+
* @param expression - The binding to refresh when signaled.
|
|
73
75
|
* @param options - The signal name or a binding to use to retrieve the signal name.
|
|
74
76
|
* @returns A binding configuration.
|
|
75
77
|
* @public
|
|
76
78
|
*/
|
|
77
|
-
export function signal(
|
|
78
|
-
|
|
79
|
+
export function signal(expression, options) {
|
|
80
|
+
const binding = new SignalBinding(expression);
|
|
81
|
+
binding.options = options;
|
|
82
|
+
return binding;
|
|
79
83
|
}
|