@microsoft/fast-element 2.0.0-beta.1 → 2.0.0-beta.4

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.
Files changed (58) hide show
  1. package/CHANGELOG.json +147 -0
  2. package/CHANGELOG.md +42 -1
  3. package/dist/dts/components/fast-definitions.d.ts +11 -8
  4. package/dist/dts/components/fast-element.d.ts +13 -3
  5. package/dist/dts/context.d.ts +157 -0
  6. package/dist/dts/di/di.d.ts +854 -0
  7. package/dist/dts/hooks.d.ts +2 -2
  8. package/dist/dts/interfaces.d.ts +39 -7
  9. package/dist/dts/metadata.d.ts +25 -0
  10. package/dist/dts/observation/arrays.d.ts +1 -1
  11. package/dist/dts/observation/behavior.d.ts +4 -4
  12. package/dist/dts/observation/observable.d.ts +59 -72
  13. package/dist/dts/styles/element-styles.d.ts +6 -0
  14. package/dist/dts/templating/binding-signal.d.ts +21 -0
  15. package/dist/dts/templating/binding-two-way.d.ts +31 -0
  16. package/dist/dts/templating/binding.d.ts +74 -201
  17. package/dist/dts/templating/compiler.d.ts +1 -2
  18. package/dist/dts/templating/html-directive.d.ts +31 -3
  19. package/dist/dts/templating/render.d.ts +277 -0
  20. package/dist/dts/templating/repeat.d.ts +13 -63
  21. package/dist/dts/templating/template.d.ts +11 -60
  22. package/dist/dts/templating/view.d.ts +9 -9
  23. package/dist/dts/templating/when.d.ts +3 -3
  24. package/dist/dts/testing/exports.d.ts +2 -0
  25. package/dist/dts/testing/fixture.d.ts +90 -0
  26. package/dist/dts/testing/timeout.d.ts +7 -0
  27. package/dist/{tsdoc-metadata.json → dts/tsdoc-metadata.json} +0 -0
  28. package/dist/esm/components/fast-definitions.js +27 -27
  29. package/dist/esm/components/fast-element.js +20 -4
  30. package/dist/esm/context.js +163 -0
  31. package/dist/esm/debug.js +35 -4
  32. package/dist/esm/di/di.js +1349 -0
  33. package/dist/esm/metadata.js +60 -0
  34. package/dist/esm/observation/arrays.js +1 -1
  35. package/dist/esm/observation/observable.js +73 -21
  36. package/dist/esm/platform.js +1 -1
  37. package/dist/esm/styles/element-styles.js +14 -0
  38. package/dist/esm/templating/binding-signal.js +79 -0
  39. package/dist/esm/templating/binding-two-way.js +98 -0
  40. package/dist/esm/templating/binding.js +137 -313
  41. package/dist/esm/templating/compiler.js +30 -7
  42. package/dist/esm/templating/html-directive.js +16 -2
  43. package/dist/esm/templating/render.js +392 -0
  44. package/dist/esm/templating/repeat.js +60 -38
  45. package/dist/esm/templating/template.js +9 -26
  46. package/dist/esm/templating/when.js +5 -4
  47. package/dist/esm/testing/exports.js +2 -0
  48. package/dist/esm/testing/fixture.js +88 -0
  49. package/dist/esm/testing/timeout.js +24 -0
  50. package/dist/fast-element.api.json +8509 -10358
  51. package/dist/fast-element.d.ts +315 -522
  52. package/dist/fast-element.debug.js +417 -438
  53. package/dist/fast-element.debug.min.js +1 -1
  54. package/dist/fast-element.js +382 -434
  55. package/dist/fast-element.min.js +1 -1
  56. package/dist/fast-element.untrimmed.d.ts +324 -529
  57. package/docs/api-report.md +124 -232
  58. package/package.json +32 -4
@@ -0,0 +1,90 @@
1
+ import type { Constructable } from "../interfaces.js";
2
+ import { ExecutionContext } from "../observation/observable.js";
3
+ import { ViewTemplate } from "../templating/template.js";
4
+ import type { HTMLView } from "../templating/view.js";
5
+ /**
6
+ * Options used to customize the creation of the test fixture.
7
+ * @public
8
+ */
9
+ export interface FixtureOptions {
10
+ /**
11
+ * The document to run the fixture in.
12
+ * @defaultValue `globalThis.document`
13
+ */
14
+ document?: Document;
15
+ /**
16
+ * The parent element to append the fixture to.
17
+ * @defaultValue An instance of `HTMLDivElement`.
18
+ */
19
+ parent?: HTMLElement;
20
+ /**
21
+ * The data source to bind the HTML to.
22
+ * @defaultValue An empty object.
23
+ */
24
+ source?: any;
25
+ /**
26
+ * The execution context to use during binding.
27
+ * @defaultValue {@link @microsoft/fast-element#ExecutionContext}
28
+ */
29
+ context?: ExecutionContext;
30
+ }
31
+ /**
32
+ * A test fixture.
33
+ * @public
34
+ */
35
+ export interface Fixture<TElement = HTMLElement> {
36
+ /**
37
+ * The document the fixture is running in.
38
+ */
39
+ document: Document;
40
+ /**
41
+ * The template the fixture was created from.
42
+ */
43
+ template: ViewTemplate;
44
+ /**
45
+ * The view that was created from the fixture's template.
46
+ */
47
+ view: HTMLView;
48
+ /**
49
+ * The parent element that the view was appended to.
50
+ * @remarks
51
+ * This element will be appended to the DOM only
52
+ * after {@link Fixture.connect} has been called.
53
+ */
54
+ parent: HTMLElement;
55
+ /**
56
+ * The first element in the {@link Fixture.view}.
57
+ */
58
+ element: TElement;
59
+ /**
60
+ * Adds the {@link Fixture.parent} to the DOM, causing the
61
+ * connect lifecycle to begin.
62
+ * @remarks
63
+ * Yields control to the caller one Microtask later, in order to
64
+ * ensure that the DOM has settled.
65
+ */
66
+ connect: () => Promise<void>;
67
+ /**
68
+ * Removes the {@link Fixture.parent} from the DOM, causing the
69
+ * disconnect lifecycle to begin.
70
+ * @remarks
71
+ * Yields control to the caller one Microtask later, in order to
72
+ * ensure that the DOM has settled.
73
+ */
74
+ disconnect: () => Promise<void>;
75
+ }
76
+ /**
77
+ * Creates a random, unique name suitable for use as a Custom Element name.
78
+ * @public
79
+ */
80
+ export declare function uniqueElementName(prefix?: string): string;
81
+ /**
82
+ * Creates a test fixture suitable for testing custom elements, templates, and bindings.
83
+ * @param templateNameOrType An HTML template or single element name to create the fixture for.
84
+ * @param options Enables customizing fixture creation behavior.
85
+ * @remarks
86
+ * Yields control to the caller one Microtask later, in order to
87
+ * ensure that the DOM has settled.
88
+ * @public
89
+ */
90
+ export declare function fixture<TElement = HTMLElement>(templateNameOrType: ViewTemplate | string | Constructable<TElement>, options?: FixtureOptions): Promise<Fixture<TElement>>;
@@ -0,0 +1,7 @@
1
+ /**
2
+ * A timeout helper for use in tests.
3
+ * @param timeout The length of the timeout.
4
+ * @returns A promise that resolves once the configured time has elapsed.
5
+ * @public
6
+ */
7
+ export declare function timeout(timeout?: number): Promise<void>;
@@ -11,19 +11,15 @@ const fastElementRegistry = FAST.getById(4 /* KernelServiceId.elementRegistry */
11
11
  * @public
12
12
  */
13
13
  export class FASTElementDefinition {
14
- /**
15
- * Creates an instance of FASTElementDefinition.
16
- * @param type - The type this definition is being created for.
17
- * @param nameOrConfig - The name of the element to define or a config object
18
- * that describes the element to define.
19
- */
20
14
  constructor(type, nameOrConfig = type.definition) {
15
+ this.platformDefined = false;
21
16
  if (isString(nameOrConfig)) {
22
17
  nameOrConfig = { name: nameOrConfig };
23
18
  }
24
19
  this.type = type;
25
20
  this.name = nameOrConfig.name;
26
21
  this.template = nameOrConfig.template;
22
+ const proto = type.prototype;
27
23
  const attributes = AttributeDefinition.collect(type, nameOrConfig.attributes);
28
24
  const observedAttributes = new Array(attributes.length);
29
25
  const propertyLookup = {};
@@ -33,9 +29,13 @@ export class FASTElementDefinition {
33
29
  observedAttributes[i] = current.attribute;
34
30
  propertyLookup[current.name] = current;
35
31
  attributeLookup[current.attribute] = current;
32
+ Observable.defineProperty(proto, current);
36
33
  }
34
+ Reflect.defineProperty(type, "observedAttributes", {
35
+ value: observedAttributes,
36
+ enumerable: true,
37
+ });
37
38
  this.attributes = attributes;
38
- this.observedAttributes = observedAttributes;
39
39
  this.propertyLookup = propertyLookup;
40
40
  this.attributeLookup = attributeLookup;
41
41
  this.shadowOptions =
@@ -48,43 +48,43 @@ export class FASTElementDefinition {
48
48
  nameOrConfig.elementOptions === void 0
49
49
  ? defaultElementOptions
50
50
  : Object.assign(Object.assign({}, defaultElementOptions), nameOrConfig.elementOptions);
51
- this.styles =
52
- nameOrConfig.styles === void 0
53
- ? void 0
54
- : Array.isArray(nameOrConfig.styles)
55
- ? new ElementStyles(nameOrConfig.styles)
56
- : nameOrConfig.styles instanceof ElementStyles
57
- ? nameOrConfig.styles
58
- : new ElementStyles([nameOrConfig.styles]);
51
+ this.styles = ElementStyles.normalize(nameOrConfig.styles);
52
+ fastElementRegistry.register(this);
59
53
  }
60
54
  /**
61
55
  * Indicates if this element has been defined in at least one registry.
62
56
  */
63
57
  get isDefined() {
64
- return !!fastElementRegistry.getByType(this.type);
58
+ return this.platformDefined;
65
59
  }
66
60
  /**
67
61
  * Defines a custom element based on this definition.
68
62
  * @param registry - The element registry to define the element in.
63
+ * @remarks
64
+ * This operation is idempotent per registry.
69
65
  */
70
66
  define(registry = customElements) {
71
67
  const type = this.type;
72
- if (fastElementRegistry.register(this)) {
73
- const attributes = this.attributes;
74
- const proto = type.prototype;
75
- for (let i = 0, ii = attributes.length; i < ii; ++i) {
76
- Observable.defineProperty(proto, attributes[i]);
77
- }
78
- Reflect.defineProperty(type, "observedAttributes", {
79
- value: this.observedAttributes,
80
- enumerable: true,
81
- });
82
- }
83
68
  if (!registry.get(this.name)) {
69
+ this.platformDefined = true;
84
70
  registry.define(this.name, type, this.elementOptions);
85
71
  }
86
72
  return this;
87
73
  }
74
+ /**
75
+ * Creates an instance of FASTElementDefinition.
76
+ * @param type - The type this definition is being created for.
77
+ * @param nameOrDef - The name of the element to define or a config object
78
+ * that describes the element to define.
79
+ */
80
+ static compose(type, nameOrDef) {
81
+ const found = fastElementRegistry.getByType(type);
82
+ if (found) {
83
+ return new FASTElementDefinition(class extends type {
84
+ }, nameOrDef);
85
+ }
86
+ return new FASTElementDefinition(type, nameOrDef);
87
+ }
88
88
  }
89
89
  /**
90
90
  * Gets the element definition associated with the specified type.
@@ -1,3 +1,4 @@
1
+ import { isFunction } from "../interfaces.js";
1
2
  import { Controller } from "./controller.js";
2
3
  import { FASTElementDefinition, } from "./fast-definitions.js";
3
4
  /* eslint-disable-next-line @typescript-eslint/explicit-function-return-type */
@@ -22,6 +23,18 @@ function createFASTElement(BaseType) {
22
23
  }
23
24
  };
24
25
  }
26
+ function compose(type, nameOrDef) {
27
+ if (isFunction(type)) {
28
+ return FASTElementDefinition.compose(type, nameOrDef);
29
+ }
30
+ return FASTElementDefinition.compose(this, type);
31
+ }
32
+ function define(type, nameOrDef) {
33
+ if (isFunction(type)) {
34
+ return FASTElementDefinition.compose(type, nameOrDef).define().type;
35
+ }
36
+ return FASTElementDefinition.compose(this, type).define().type;
37
+ }
25
38
  /**
26
39
  * A minimal base class for FASTElements that also provides
27
40
  * static helpers for working with FASTElements.
@@ -42,9 +55,12 @@ export const FASTElement = Object.assign(createFASTElement(HTMLElement), {
42
55
  * @param nameOrDef - The name of the element to define or a definition object
43
56
  * that describes the element to define.
44
57
  */
45
- define(type, nameOrDef) {
46
- return new FASTElementDefinition(type, nameOrDef).define().type;
47
- },
58
+ define,
59
+ /**
60
+ * Defines metadata for a FASTElement which can be used to later define the element.
61
+ * @public
62
+ */
63
+ compose,
48
64
  });
49
65
  /**
50
66
  * Decorator: Defines a platform custom element based on `FASTElement`.
@@ -55,6 +71,6 @@ export const FASTElement = Object.assign(createFASTElement(HTMLElement), {
55
71
  export function customElement(nameOrDef) {
56
72
  /* eslint-disable-next-line @typescript-eslint/explicit-function-return-type */
57
73
  return function (type) {
58
- new FASTElementDefinition(type, nameOrDef).define();
74
+ define(type, nameOrDef);
59
75
  };
60
76
  }
@@ -0,0 +1,163 @@
1
+ import "./interfaces.js";
2
+ import { Metadata } from "./metadata.js";
3
+ import { FAST } from "./platform.js";
4
+ const contextEventType = "context-request";
5
+ let requestStrategy;
6
+ /**
7
+ * Enables using the {@link https://github.com/webcomponents-cg/community-protocols/blob/main/proposals/context.md | W3C Community Context protocol.}
8
+ * @beta
9
+ */
10
+ export const Context = Object.freeze({
11
+ /**
12
+ * The event type used for W3C Context Protocol requests.
13
+ */
14
+ eventType: contextEventType,
15
+ /**
16
+ * Creates a W3C Community Protocol-based Context object to use in requesting/providing
17
+ * context through the DOM.
18
+ * @param name - The name to use for the connext. Useful in debugging.
19
+ * @param initialValue - An optional initial value to use if a context handler isn't found.
20
+ */
21
+ create(name, initialValue) {
22
+ const Interface = function (target, property, index) {
23
+ if (target == null || new.target !== undefined) {
24
+ throw FAST.error(1501 /* Message.noRegistrationForContext */, {
25
+ name: Interface.name,
26
+ });
27
+ }
28
+ if (property) {
29
+ Context.defineProperty(target, property, Interface);
30
+ }
31
+ else {
32
+ const types = Metadata.getOrCreateAnnotationParamTypes(target);
33
+ types[index] = Interface;
34
+ }
35
+ };
36
+ Interface.$isInterface = true;
37
+ Interface.initialValue = initialValue;
38
+ Reflect.defineProperty(Interface, "name", { value: name });
39
+ Interface.handle = (target, callback) => Context.handle(target, callback, Interface);
40
+ Interface.provide = (target, value) => Context.provide(target, Interface, value);
41
+ Interface.get = (target) => Context.get(target, Interface);
42
+ Interface.request = (target, callback, multiple) => Context.request(target, Interface, callback, multiple);
43
+ Interface.toString = () => `Context<${Interface.name}>`;
44
+ return Interface;
45
+ },
46
+ /**
47
+ * Sets the strategy used by all FAST-specific context requests made through the
48
+ * Context.request, Context.get, Context.defineProperty, and ContextDecorator APIs.
49
+ * @param strategy - The strategy to use. By default, the strategy is Context.dispatch.
50
+ */
51
+ setDefaultRequestStrategy(strategy) {
52
+ requestStrategy = strategy;
53
+ },
54
+ /**
55
+ * Gets the context value for the target node or returns the initial value if
56
+ * a context handler is not found.
57
+ * @param target - The target to get the context for.
58
+ * @param context - The context to locate.
59
+ * @returns The context value.
60
+ * @remarks
61
+ * Uses the default request strategy to locate the context. If no context is found
62
+ * then the initial value of the context is returned.
63
+ */
64
+ get(target, context) {
65
+ var _a;
66
+ let value;
67
+ requestStrategy(target, context, found => (value = found), false);
68
+ return (_a = value) !== null && _a !== void 0 ? _a : context.initialValue;
69
+ },
70
+ /**
71
+ * Requests the context value for the target node.
72
+ * @param target - The target to request the context for.
73
+ * @param context - The context to locate.
74
+ * @param callback - A callback to invoke with the context value.
75
+ * @param multiple - Whether the context requestor expects to handle updates
76
+ * to the context value after the initial request.
77
+ * @remarks
78
+ * Uses the default request strategy to locate the context.
79
+ */
80
+ request(target, context, callback, multiple = false) {
81
+ requestStrategy(target, context, callback, multiple);
82
+ },
83
+ /**
84
+ *
85
+ * @param target - The target to dispatch the context event on.
86
+ * @param context - The context to locate.
87
+ * @param callback - The callback to invoke with the context value.
88
+ * @param multiple - Whether the context requestor expects to handle updates
89
+ * to the context value after the initial request.
90
+ * @remarks
91
+ * This API does NOT use the default request strategy. It always dispatches
92
+ * an event through the DOM.
93
+ */
94
+ dispatch(target, context, callback, multiple = false) {
95
+ target.dispatchEvent(new ContextEvent(context, callback, multiple));
96
+ },
97
+ provide(target, context, value) {
98
+ this.handle(target, (event) => {
99
+ event.stopImmediatePropagation();
100
+ event.callback(value);
101
+ }, context);
102
+ },
103
+ /**
104
+ *
105
+ * @param target - The target on which to handle context requests.
106
+ * @param callback - The callback to invoke when a context request is received.
107
+ * @param context - The context to handle requests for.
108
+ * @remarks
109
+ * If a context is not provided then the callback will be invoked for all context
110
+ * requests that are received on the target.
111
+ */
112
+ handle(target, callback, context) {
113
+ if (context) {
114
+ target.addEventListener(contextEventType, (event) => {
115
+ if (event.context === context) {
116
+ callback(event);
117
+ }
118
+ });
119
+ }
120
+ else {
121
+ target.addEventListener(contextEventType, callback);
122
+ }
123
+ },
124
+ /**
125
+ * Defines a getter-only property on the target that will return the context
126
+ * value for the target.
127
+ * @param target The target to define the property on.
128
+ * @param propertyName The name of the property to define.
129
+ * @param context The context that will be used to retrieve the property value.
130
+ * @remarks
131
+ * Uses the default request strategy to locate the context and will return the
132
+ * initialValue if the context isn't handled.
133
+ */
134
+ defineProperty(target, propertyName, context) {
135
+ const field = `$di_${propertyName}`;
136
+ Reflect.defineProperty(target, propertyName, {
137
+ get: function () {
138
+ var _a;
139
+ return (_a = this[field]) !== null && _a !== void 0 ? _a : (this[field] = Context.get(this, context));
140
+ },
141
+ });
142
+ },
143
+ });
144
+ Context.setDefaultRequestStrategy(Context.dispatch);
145
+ /**
146
+ * An event fired by a context requester to signal it desires a named context.
147
+ *
148
+ * A provider should inspect the `context` property of the event to determine if it has a value that can
149
+ * satisfy the request, calling the `callback` with the requested value if so.
150
+ *
151
+ * If the requested context event contains a truthy `multiple` value, then a provider can call the callback
152
+ * multiple times if the value is changed, if this is the case the provider should pass a `dispose`
153
+ * method to the callback which requesters can invoke to indicate they no longer wish to receive these updates.
154
+ * @public
155
+ */
156
+ export class ContextEvent extends Event {
157
+ constructor(context, callback, multiple) {
158
+ super(contextEventType, { bubbles: true, composed: true });
159
+ this.context = context;
160
+ this.callback = callback;
161
+ this.multiple = multiple;
162
+ }
163
+ }
package/dist/esm/debug.js CHANGED
@@ -11,19 +11,50 @@ const debugMessages = {
11
11
  [1101 /* needsArrayObservation */]: "Must call enableArrayObservation before observing arrays.",
12
12
  [1201 /* onlySetHTMLPolicyOnce */]: "The HTML policy can only be set once.",
13
13
  [1202 /* bindingInnerHTMLRequiresTrustedTypes */]: "To bind innerHTML, you must use a TrustedTypesPolicy.",
14
+ [1203 /* twoWayBindingRequiresObservables */]: "View=>Model update skipped. To use twoWay binding, the target property must be observable.",
15
+ [1204 /* hostBindingWithoutHost */]: "No host element is present. Cannot bind host with ${name}.",
16
+ [1205 /* unsupportedBindingBehavior */]: "The requested binding behavior is not supported by the binding engine.",
14
17
  [1401 /* missingElementDefinition */]: "Missing FASTElement definition.",
18
+ [1501 /* noRegistrationForContext */]: "No registration for Context/Interface '${name}'.",
19
+ [1502 /* noFactoryForResolver */]: "Dependency injection resolver for '${key}' returned a null factory.",
20
+ [1503 /* invalidResolverStrategy */]: "Invalid dependency injection resolver strategy specified '${strategy}'.",
21
+ [1504 /* cannotAutoregisterDependency */]: "Unable to autoregister dependency.",
22
+ [1505 /* cannotResolveKey */]: "Unable to resolve dependency injection key '${key}'.",
23
+ [1506 /* cannotConstructNativeFunction */]: "'${name}' is a native function and therefore cannot be safely constructed by DI. If this is intentional, please use a callback or cachedCallback resolver.",
24
+ [1507 /* cannotJITRegisterNonConstructor */]: "Attempted to jitRegister something that is not a constructor '${value}'. Did you forget to register this dependency?",
25
+ [1508 /* cannotJITRegisterIntrinsic */]: "Attempted to jitRegister an intrinsic type '${value}'. Did you forget to add @inject(Key)?",
26
+ [1509 /* cannotJITRegisterInterface */]: "Attempted to jitRegister an interface '${value}'.",
27
+ [1510 /* invalidResolver */]: "A valid resolver was not returned from the register method.",
28
+ [1511 /* invalidKey */]: "Key/value cannot be null or undefined. Are you trying to inject/register something that doesn't exist with DI?",
29
+ [1512 /* noDefaultResolver */]: "'${key}' not registered. Did you forget to add @singleton()?",
30
+ [1513 /* cyclicDependency */]: "Cyclic dependency found '${name}'.",
15
31
  };
32
+ const allPlaceholders = /(\$\{\w+?})/g;
33
+ const placeholder = /\$\{(\w+?)}/g;
34
+ const noValues = Object.freeze({});
35
+ function formatMessage(message, values) {
36
+ return message
37
+ .split(allPlaceholders)
38
+ .map(v => {
39
+ var _a;
40
+ const replaced = v.replace(placeholder, "$1");
41
+ return String((_a = values[replaced]) !== null && _a !== void 0 ? _a : v);
42
+ })
43
+ .join("");
44
+ }
16
45
  Object.assign(FAST, {
17
46
  addMessages(messages) {
18
47
  Object.assign(debugMessages, messages);
19
48
  },
20
- warn(code, ...args) {
49
+ warn(code, values = noValues) {
21
50
  var _a;
22
- console.warn((_a = debugMessages[code]) !== null && _a !== void 0 ? _a : "Unknown Warning");
51
+ const message = (_a = debugMessages[code]) !== null && _a !== void 0 ? _a : "Unknown Warning";
52
+ console.warn(formatMessage(message, values));
23
53
  },
24
- error(code, ...args) {
54
+ error(code, values = noValues) {
25
55
  var _a;
26
- return new Error((_a = debugMessages[code]) !== null && _a !== void 0 ? _a : "Unknown Error");
56
+ const message = (_a = debugMessages[code]) !== null && _a !== void 0 ? _a : "Unknown Error";
57
+ return new Error(formatMessage(message, values));
27
58
  },
28
59
  });
29
60
  export {};