@microsoft/fast-element 2.0.0-beta.1 → 2.0.0-beta.11
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 +348 -0
- package/CHANGELOG.md +114 -1
- package/dist/dts/components/attributes.d.ts +10 -0
- package/dist/dts/components/{controller.d.ts → element-controller.d.ts} +49 -25
- package/dist/dts/components/fast-definitions.d.ts +43 -9
- package/dist/dts/components/fast-element.d.ts +15 -21
- package/dist/dts/context.d.ts +157 -0
- package/dist/dts/di/di.d.ts +899 -0
- package/dist/dts/index.d.ts +2 -2
- package/dist/dts/interfaces.d.ts +45 -14
- package/dist/dts/metadata.d.ts +25 -0
- package/dist/dts/observation/arrays.d.ts +1 -1
- package/dist/dts/observation/observable.d.ts +101 -75
- package/dist/dts/pending-task.d.ts +20 -0
- package/dist/dts/platform.d.ts +7 -0
- package/dist/dts/polyfills.d.ts +1 -8
- 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 +10 -17
- package/dist/dts/styles/host.d.ts +68 -0
- package/dist/dts/templating/binding-signal.d.ts +21 -0
- package/dist/dts/templating/binding-two-way.d.ts +39 -0
- package/dist/dts/templating/binding.d.ts +69 -294
- package/dist/dts/templating/children.d.ts +1 -1
- package/dist/dts/templating/compiler.d.ts +1 -2
- package/dist/dts/templating/html-directive.d.ts +93 -35
- 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 +272 -0
- package/dist/dts/templating/repeat.d.ts +20 -75
- package/dist/dts/templating/slotted.d.ts +1 -1
- package/dist/dts/templating/template.d.ts +12 -61
- package/dist/dts/templating/view.d.ts +77 -12
- package/dist/dts/templating/when.d.ts +3 -3
- package/dist/dts/testing/exports.d.ts +3 -0
- package/dist/dts/testing/fakes.d.ts +4 -0
- package/dist/dts/testing/fixture.d.ts +84 -0
- package/dist/dts/testing/timeout.d.ts +7 -0
- package/dist/{tsdoc-metadata.json → dts/tsdoc-metadata.json} +0 -0
- 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} +188 -109
- package/dist/esm/components/fast-definitions.js +38 -28
- package/dist/esm/components/fast-element.js +31 -12
- package/dist/esm/context.js +163 -0
- package/dist/esm/debug.js +36 -4
- package/dist/esm/di/di.js +1435 -0
- package/dist/esm/index.js +2 -1
- package/dist/esm/interfaces.js +4 -0
- package/dist/esm/metadata.js +60 -0
- package/dist/esm/observation/arrays.js +304 -3
- package/dist/esm/observation/observable.js +81 -87
- package/dist/esm/pending-task.js +16 -0
- package/dist/esm/platform.js +26 -1
- package/dist/esm/polyfills.js +1 -55
- 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/styles/element-styles.js +14 -33
- package/dist/esm/{observation/behavior.js → styles/host.js} +0 -0
- package/dist/esm/templating/binding-signal.js +83 -0
- package/dist/esm/templating/binding-two-way.js +103 -0
- package/dist/esm/templating/binding.js +134 -414
- package/dist/esm/templating/compiler.js +30 -7
- package/dist/esm/templating/html-directive.js +100 -28
- package/dist/esm/templating/node-observation.js +9 -8
- package/dist/esm/templating/ref.js +4 -12
- package/dist/esm/templating/render.js +391 -0
- package/dist/esm/templating/repeat.js +96 -72
- package/dist/esm/templating/template.js +11 -29
- package/dist/esm/templating/view.js +107 -29
- package/dist/esm/templating/when.js +5 -4
- package/dist/esm/testing/exports.js +3 -0
- package/dist/esm/testing/fakes.js +76 -0
- package/dist/esm/testing/fixture.js +86 -0
- package/dist/esm/testing/timeout.js +24 -0
- package/dist/esm/utilities.js +0 -95
- package/dist/fast-element.api.json +9278 -10745
- package/dist/fast-element.d.ts +707 -813
- package/dist/fast-element.debug.js +1229 -944
- package/dist/fast-element.debug.min.js +1 -1
- package/dist/fast-element.js +1191 -938
- package/dist/fast-element.min.js +1 -1
- package/dist/fast-element.untrimmed.d.ts +716 -824
- package/docs/api-report.md +265 -319
- package/package.json +39 -14
- package/dist/dts/hooks.d.ts +0 -20
- package/dist/dts/observation/behavior.d.ts +0 -19
- package/dist/dts/observation/splice-strategies.d.ts +0 -13
- package/dist/esm/hooks.js +0 -32
- package/dist/esm/observation/splice-strategies.js +0 -400
package/dist/esm/platform.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import "./polyfills.js";
|
|
1
2
|
// ensure FAST global - duplicated in polyfills.ts and debug.ts
|
|
2
3
|
const propConfig = {
|
|
3
4
|
configurable: false,
|
|
@@ -26,7 +27,7 @@ if (FAST.error === void 0) {
|
|
|
26
27
|
Object.assign(FAST, {
|
|
27
28
|
warn() { },
|
|
28
29
|
error(code) {
|
|
29
|
-
return new Error(`
|
|
30
|
+
return new Error(`Error ${code}`);
|
|
30
31
|
},
|
|
31
32
|
addMessages() { },
|
|
32
33
|
});
|
|
@@ -57,7 +58,31 @@ export function createTypeRegistry() {
|
|
|
57
58
|
return typeToDefinition.get(key);
|
|
58
59
|
},
|
|
59
60
|
getForInstance(object) {
|
|
61
|
+
if (object === null || object === void 0) {
|
|
62
|
+
return void 0;
|
|
63
|
+
}
|
|
60
64
|
return typeToDefinition.get(object.constructor);
|
|
61
65
|
},
|
|
62
66
|
});
|
|
63
67
|
}
|
|
68
|
+
/**
|
|
69
|
+
* Creates a function capable of locating metadata associated with a type.
|
|
70
|
+
* @returns A metadata locator function.
|
|
71
|
+
* @internal
|
|
72
|
+
*/
|
|
73
|
+
export function createMetadataLocator() {
|
|
74
|
+
const metadataLookup = new WeakMap();
|
|
75
|
+
return function (target) {
|
|
76
|
+
let metadata = metadataLookup.get(target);
|
|
77
|
+
if (metadata === void 0) {
|
|
78
|
+
let currentTarget = Reflect.getPrototypeOf(target);
|
|
79
|
+
while (metadata === void 0 && currentTarget !== null) {
|
|
80
|
+
metadata = metadataLookup.get(currentTarget);
|
|
81
|
+
currentTarget = Reflect.getPrototypeOf(currentTarget);
|
|
82
|
+
}
|
|
83
|
+
metadata = metadata === void 0 ? [] : metadata.slice(0);
|
|
84
|
+
metadataLookup.set(target, metadata);
|
|
85
|
+
}
|
|
86
|
+
return metadata;
|
|
87
|
+
};
|
|
88
|
+
}
|
package/dist/esm/polyfills.js
CHANGED
|
@@ -28,58 +28,4 @@ if (!globalThis.trustedTypes) {
|
|
|
28
28
|
createPolicy: (n, r) => r,
|
|
29
29
|
};
|
|
30
30
|
}
|
|
31
|
-
|
|
32
|
-
const propConfig = {
|
|
33
|
-
configurable: false,
|
|
34
|
-
enumerable: false,
|
|
35
|
-
writable: false,
|
|
36
|
-
};
|
|
37
|
-
if (globalThis.FAST === void 0) {
|
|
38
|
-
Reflect.defineProperty(globalThis, "FAST", Object.assign({ value: Object.create(null) }, propConfig));
|
|
39
|
-
}
|
|
40
|
-
const FAST = globalThis.FAST;
|
|
41
|
-
if (FAST.getById === void 0) {
|
|
42
|
-
const storage = Object.create(null);
|
|
43
|
-
Reflect.defineProperty(FAST, "getById", Object.assign({ value(id, initialize) {
|
|
44
|
-
let found = storage[id];
|
|
45
|
-
if (found === void 0) {
|
|
46
|
-
found = initialize ? (storage[id] = initialize()) : null;
|
|
47
|
-
}
|
|
48
|
-
return found;
|
|
49
|
-
} }, propConfig));
|
|
50
|
-
}
|
|
51
|
-
// duplicated from DOM
|
|
52
|
-
const supportsAdoptedStyleSheets = Array.isArray(document.adoptedStyleSheets) &&
|
|
53
|
-
"replace" in CSSStyleSheet.prototype;
|
|
54
|
-
function usableStyleTarget(target) {
|
|
55
|
-
return target === document ? document.body : target;
|
|
56
|
-
}
|
|
57
|
-
let id = 0;
|
|
58
|
-
const nextStyleId = () => `fast-${++id}`;
|
|
59
|
-
export class StyleElementStrategy {
|
|
60
|
-
constructor(styles) {
|
|
61
|
-
this.styles = styles;
|
|
62
|
-
this.styleClass = nextStyleId();
|
|
63
|
-
}
|
|
64
|
-
addStylesTo(target) {
|
|
65
|
-
target = usableStyleTarget(target);
|
|
66
|
-
const styles = this.styles;
|
|
67
|
-
const styleClass = this.styleClass;
|
|
68
|
-
for (let i = 0; i < styles.length; i++) {
|
|
69
|
-
const element = document.createElement("style");
|
|
70
|
-
element.innerHTML = styles[i];
|
|
71
|
-
element.className = styleClass;
|
|
72
|
-
target.append(element);
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
removeStylesFrom(target) {
|
|
76
|
-
const styles = target.querySelectorAll(`.${this.styleClass}`);
|
|
77
|
-
target = usableStyleTarget(target);
|
|
78
|
-
for (let i = 0, ii = styles.length; i < ii; ++i) {
|
|
79
|
-
target.removeChild(styles[i]);
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
if (!supportsAdoptedStyleSheets) {
|
|
84
|
-
FAST.getById(/* KernelServiceId.styleSheetStrategy */ 5, () => StyleElementStrategy);
|
|
85
|
-
}
|
|
31
|
+
export {};
|
|
@@ -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);
|
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
import { FAST } from "../platform.js";
|
|
2
|
-
import "../interfaces.js";
|
|
3
|
-
const styleSheetCache = new Map();
|
|
4
1
|
let DefaultStyleStrategy;
|
|
5
2
|
function reduceStyles(styles) {
|
|
6
3
|
return styles
|
|
@@ -71,39 +68,23 @@ export class ElementStyles {
|
|
|
71
68
|
static setDefaultStrategy(Strategy) {
|
|
72
69
|
DefaultStyleStrategy = Strategy;
|
|
73
70
|
}
|
|
71
|
+
/**
|
|
72
|
+
* Normalizes a set of composable style options.
|
|
73
|
+
* @param styles - The style options to normalize.
|
|
74
|
+
* @returns A singular ElementStyles instance or undefined.
|
|
75
|
+
*/
|
|
76
|
+
static normalize(styles) {
|
|
77
|
+
return styles === void 0
|
|
78
|
+
? void 0
|
|
79
|
+
: Array.isArray(styles)
|
|
80
|
+
? new ElementStyles(styles)
|
|
81
|
+
: styles instanceof ElementStyles
|
|
82
|
+
? styles
|
|
83
|
+
: new ElementStyles([styles]);
|
|
84
|
+
}
|
|
74
85
|
}
|
|
75
86
|
/**
|
|
76
87
|
* Indicates whether the DOM supports the adoptedStyleSheets feature.
|
|
77
88
|
*/
|
|
78
89
|
ElementStyles.supportsAdoptedStyleSheets = Array.isArray(document.adoptedStyleSheets) &&
|
|
79
90
|
"replace" in CSSStyleSheet.prototype;
|
|
80
|
-
/**
|
|
81
|
-
* https://wicg.github.io/construct-stylesheets/
|
|
82
|
-
* https://developers.google.com/web/updates/2019/02/constructable-stylesheets
|
|
83
|
-
*
|
|
84
|
-
* @internal
|
|
85
|
-
*/
|
|
86
|
-
export class AdoptedStyleSheetsStrategy {
|
|
87
|
-
constructor(styles) {
|
|
88
|
-
this.sheets = styles.map((x) => {
|
|
89
|
-
if (x instanceof CSSStyleSheet) {
|
|
90
|
-
return x;
|
|
91
|
-
}
|
|
92
|
-
let sheet = styleSheetCache.get(x);
|
|
93
|
-
if (sheet === void 0) {
|
|
94
|
-
sheet = new CSSStyleSheet();
|
|
95
|
-
sheet.replaceSync(x);
|
|
96
|
-
styleSheetCache.set(x, sheet);
|
|
97
|
-
}
|
|
98
|
-
return sheet;
|
|
99
|
-
});
|
|
100
|
-
}
|
|
101
|
-
addStylesTo(target) {
|
|
102
|
-
target.adoptedStyleSheets = [...target.adoptedStyleSheets, ...this.sheets];
|
|
103
|
-
}
|
|
104
|
-
removeStylesFrom(target) {
|
|
105
|
-
const sheets = this.sheets;
|
|
106
|
-
target.adoptedStyleSheets = target.adoptedStyleSheets.filter((x) => sheets.indexOf(x) === -1);
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
ElementStyles.setDefaultStrategy(FAST.getById(5 /* KernelServiceId.styleSheetStrategy */, () => AdoptedStyleSheetsStrategy));
|
|
File without changes
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { isString } from "../interfaces.js";
|
|
2
|
+
import { Binding } from "./html-directive.js";
|
|
3
|
+
const subscribers = Object.create(null);
|
|
4
|
+
export const Signal = Object.freeze({
|
|
5
|
+
subscribe(signal, subscriber) {
|
|
6
|
+
const found = subscribers[signal];
|
|
7
|
+
if (found) {
|
|
8
|
+
found instanceof Set
|
|
9
|
+
? found.add(subscriber)
|
|
10
|
+
: (subscribers[signal] = new Set([found, subscriber]));
|
|
11
|
+
}
|
|
12
|
+
else {
|
|
13
|
+
subscribers[signal] = subscriber;
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
unsubscribe(signal, subscriber) {
|
|
17
|
+
const found = subscribers[signal];
|
|
18
|
+
if (found && found instanceof Set) {
|
|
19
|
+
found.delete(subscriber);
|
|
20
|
+
}
|
|
21
|
+
else {
|
|
22
|
+
subscribers[signal] = void 0;
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
/**
|
|
26
|
+
* Sends the specified signal to signaled bindings.
|
|
27
|
+
* @param signal - The signal to send.
|
|
28
|
+
* @public
|
|
29
|
+
*/
|
|
30
|
+
send(signal) {
|
|
31
|
+
const found = subscribers[signal];
|
|
32
|
+
if (found) {
|
|
33
|
+
found instanceof Set
|
|
34
|
+
? found.forEach(x => x.handleChange(found, signal))
|
|
35
|
+
: found.handleChange(this, signal);
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
});
|
|
39
|
+
class SignalObserver {
|
|
40
|
+
constructor(dataBinding, subscriber) {
|
|
41
|
+
this.dataBinding = dataBinding;
|
|
42
|
+
this.subscriber = subscriber;
|
|
43
|
+
this.isNotBound = true;
|
|
44
|
+
}
|
|
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);
|
|
52
|
+
}
|
|
53
|
+
unbind(controller) {
|
|
54
|
+
this.isNotBound = true;
|
|
55
|
+
Signal.unsubscribe(this.getSignal(controller), this);
|
|
56
|
+
}
|
|
57
|
+
handleChange() {
|
|
58
|
+
this.subscriber.handleChange(this.dataBinding.evaluate, this);
|
|
59
|
+
}
|
|
60
|
+
getSignal(controller) {
|
|
61
|
+
const options = this.dataBinding.options;
|
|
62
|
+
return isString(options)
|
|
63
|
+
? options
|
|
64
|
+
: options(controller.source, controller.context);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
class SignalBinding extends Binding {
|
|
68
|
+
createObserver(directive, subscriber) {
|
|
69
|
+
return new SignalObserver(this, subscriber);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Creates a signal binding configuration with the supplied options.
|
|
74
|
+
* @param expression - The binding to refresh when signaled.
|
|
75
|
+
* @param options - The signal name or a binding to use to retrieve the signal name.
|
|
76
|
+
* @returns A binding configuration.
|
|
77
|
+
* @public
|
|
78
|
+
*/
|
|
79
|
+
export function signal(expression, options) {
|
|
80
|
+
const binding = new SignalBinding(expression);
|
|
81
|
+
binding.options = options;
|
|
82
|
+
return binding;
|
|
83
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { isString } from "../interfaces.js";
|
|
2
|
+
import { Observable, } from "../observation/observable.js";
|
|
3
|
+
import { FAST } from "../platform.js";
|
|
4
|
+
import { Binding } from "./html-directive.js";
|
|
5
|
+
const defaultOptions = {
|
|
6
|
+
fromView: v => v,
|
|
7
|
+
};
|
|
8
|
+
let twoWaySettings = {
|
|
9
|
+
determineChangeEvent() {
|
|
10
|
+
return "change";
|
|
11
|
+
},
|
|
12
|
+
};
|
|
13
|
+
export const TwoWaySettings = Object.freeze({
|
|
14
|
+
/**
|
|
15
|
+
* Configures two-way binding.
|
|
16
|
+
* @param settings - The settings to use for the two-way binding system.
|
|
17
|
+
*/
|
|
18
|
+
configure(settings) {
|
|
19
|
+
twoWaySettings = settings;
|
|
20
|
+
},
|
|
21
|
+
});
|
|
22
|
+
class TwoWayObserver {
|
|
23
|
+
constructor(directive, subscriber, dataBinding) {
|
|
24
|
+
this.directive = directive;
|
|
25
|
+
this.subscriber = subscriber;
|
|
26
|
+
this.dataBinding = dataBinding;
|
|
27
|
+
this.isNotBound = true;
|
|
28
|
+
this.notifier = Observable.binding(dataBinding.evaluate, this, dataBinding.isVolatile);
|
|
29
|
+
}
|
|
30
|
+
bind(controller) {
|
|
31
|
+
var _a;
|
|
32
|
+
if (!this.changeEvent) {
|
|
33
|
+
this.changeEvent =
|
|
34
|
+
(_a = this.dataBinding.options.changeEvent) !== null && _a !== void 0 ? _a : twoWaySettings.determineChangeEvent(this.directive, this.target);
|
|
35
|
+
}
|
|
36
|
+
if (this.isNotBound) {
|
|
37
|
+
this.target.addEventListener(this.changeEvent, this);
|
|
38
|
+
controller.onUnbind(this);
|
|
39
|
+
this.isNotBound = false;
|
|
40
|
+
}
|
|
41
|
+
return this.notifier.bind(controller);
|
|
42
|
+
}
|
|
43
|
+
unbind(controller) {
|
|
44
|
+
this.isNotBound = true;
|
|
45
|
+
this.target.removeEventListener(this.changeEvent, this);
|
|
46
|
+
}
|
|
47
|
+
handleChange(subject, args) {
|
|
48
|
+
this.subscriber.handleChange(this.dataBinding.evaluate, this);
|
|
49
|
+
}
|
|
50
|
+
handleEvent(event) {
|
|
51
|
+
const directive = this.directive;
|
|
52
|
+
const target = event.currentTarget;
|
|
53
|
+
const notifier = this.notifier;
|
|
54
|
+
const last = notifier.last; // using internal API!!!
|
|
55
|
+
if (!last) {
|
|
56
|
+
FAST.warn(1203 /* Message.twoWayBindingRequiresObservables */);
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
let value;
|
|
60
|
+
switch (directive.aspectType) {
|
|
61
|
+
case 1:
|
|
62
|
+
value = target.getAttribute(directive.targetAspect);
|
|
63
|
+
break;
|
|
64
|
+
case 2:
|
|
65
|
+
value = target.hasAttribute(directive.targetAspect);
|
|
66
|
+
break;
|
|
67
|
+
case 4:
|
|
68
|
+
value = target.innerText;
|
|
69
|
+
break;
|
|
70
|
+
default:
|
|
71
|
+
value = target[directive.targetAspect];
|
|
72
|
+
break;
|
|
73
|
+
}
|
|
74
|
+
last.propertySource[last.propertyName] = this.dataBinding.options.fromView(value);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
class TwoWayBinding extends Binding {
|
|
78
|
+
createObserver(directive, subscriber) {
|
|
79
|
+
return new TwoWayObserver(directive, subscriber, this);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Creates a default binding.
|
|
84
|
+
* @param expression - The binding to refresh when changed.
|
|
85
|
+
* @param optionsOrChangeEvent - The binding options or the name of the change event to use.
|
|
86
|
+
* @param isBindingVolatile - Indicates whether the binding is volatile or not.
|
|
87
|
+
* @returns A binding.
|
|
88
|
+
* @public
|
|
89
|
+
*/
|
|
90
|
+
export function twoWay(expression, optionsOrChangeEvent, isBindingVolatile = Observable.isVolatileBinding(expression)) {
|
|
91
|
+
if (isString(optionsOrChangeEvent)) {
|
|
92
|
+
optionsOrChangeEvent = { changeEvent: optionsOrChangeEvent };
|
|
93
|
+
}
|
|
94
|
+
if (!optionsOrChangeEvent) {
|
|
95
|
+
optionsOrChangeEvent = defaultOptions;
|
|
96
|
+
}
|
|
97
|
+
else if (!optionsOrChangeEvent.fromView) {
|
|
98
|
+
optionsOrChangeEvent.fromView = defaultOptions.fromView;
|
|
99
|
+
}
|
|
100
|
+
const binding = new TwoWayBinding(expression, isBindingVolatile);
|
|
101
|
+
binding.options = optionsOrChangeEvent;
|
|
102
|
+
return binding;
|
|
103
|
+
}
|