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

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 (96) hide show
  1. package/CHANGELOG.json +333 -0
  2. package/CHANGELOG.md +106 -1
  3. package/dist/dts/components/attributes.d.ts +10 -0
  4. package/dist/dts/components/{controller.d.ts → element-controller.d.ts} +24 -25
  5. package/dist/dts/components/fast-definitions.d.ts +43 -9
  6. package/dist/dts/components/fast-element.d.ts +15 -21
  7. package/dist/dts/context.d.ts +157 -0
  8. package/dist/dts/di/di.d.ts +899 -0
  9. package/dist/dts/index.d.ts +2 -2
  10. package/dist/dts/interfaces.d.ts +44 -12
  11. package/dist/dts/metadata.d.ts +25 -0
  12. package/dist/dts/observation/arrays.d.ts +1 -1
  13. package/dist/dts/observation/observable.d.ts +101 -75
  14. package/dist/dts/pending-task.d.ts +20 -0
  15. package/dist/dts/platform.d.ts +6 -0
  16. package/dist/dts/state/exports.d.ts +3 -0
  17. package/dist/dts/state/reactive.d.ts +8 -0
  18. package/dist/dts/state/state.d.ts +141 -0
  19. package/dist/dts/state/visitor.d.ts +6 -0
  20. package/dist/dts/state/watch.d.ts +10 -0
  21. package/dist/dts/styles/css-directive.d.ts +2 -2
  22. package/dist/dts/styles/element-styles.d.ts +9 -3
  23. package/dist/dts/styles/host.d.ts +68 -0
  24. package/dist/dts/templating/binding-signal.d.ts +21 -0
  25. package/dist/dts/templating/binding-two-way.d.ts +39 -0
  26. package/dist/dts/templating/binding.d.ts +69 -294
  27. package/dist/dts/templating/children.d.ts +1 -1
  28. package/dist/dts/templating/compiler.d.ts +1 -2
  29. package/dist/dts/templating/html-directive.d.ts +93 -35
  30. package/dist/dts/templating/node-observation.d.ts +4 -5
  31. package/dist/dts/templating/ref.d.ts +5 -13
  32. package/dist/dts/templating/render.d.ts +272 -0
  33. package/dist/dts/templating/repeat.d.ts +20 -75
  34. package/dist/dts/templating/slotted.d.ts +1 -1
  35. package/dist/dts/templating/template.d.ts +12 -61
  36. package/dist/dts/templating/view.d.ts +77 -12
  37. package/dist/dts/templating/when.d.ts +3 -3
  38. package/dist/dts/testing/exports.d.ts +3 -0
  39. package/dist/dts/testing/fakes.d.ts +4 -0
  40. package/dist/dts/testing/fixture.d.ts +84 -0
  41. package/dist/dts/testing/timeout.d.ts +7 -0
  42. package/dist/{tsdoc-metadata.json → dts/tsdoc-metadata.json} +0 -0
  43. package/dist/dts/utilities.d.ts +0 -18
  44. package/dist/esm/components/attributes.js +13 -4
  45. package/dist/esm/components/{controller.js → element-controller.js} +95 -105
  46. package/dist/esm/components/fast-definitions.js +38 -28
  47. package/dist/esm/components/fast-element.js +31 -12
  48. package/dist/esm/context.js +163 -0
  49. package/dist/esm/debug.js +36 -4
  50. package/dist/esm/di/di.js +1435 -0
  51. package/dist/esm/index.js +2 -1
  52. package/dist/esm/interfaces.js +4 -0
  53. package/dist/esm/metadata.js +60 -0
  54. package/dist/esm/observation/arrays.js +304 -3
  55. package/dist/esm/observation/observable.js +81 -87
  56. package/dist/esm/pending-task.js +16 -0
  57. package/dist/esm/platform.js +25 -1
  58. package/dist/esm/state/exports.js +3 -0
  59. package/dist/esm/state/reactive.js +34 -0
  60. package/dist/esm/state/state.js +148 -0
  61. package/dist/esm/state/visitor.js +28 -0
  62. package/dist/esm/state/watch.js +36 -0
  63. package/dist/esm/styles/css.js +4 -4
  64. package/dist/esm/styles/element-styles.js +14 -0
  65. package/dist/esm/{observation/behavior.js → styles/host.js} +0 -0
  66. package/dist/esm/templating/binding-signal.js +83 -0
  67. package/dist/esm/templating/binding-two-way.js +103 -0
  68. package/dist/esm/templating/binding.js +134 -414
  69. package/dist/esm/templating/compiler.js +30 -7
  70. package/dist/esm/templating/html-directive.js +100 -28
  71. package/dist/esm/templating/node-observation.js +9 -8
  72. package/dist/esm/templating/ref.js +4 -12
  73. package/dist/esm/templating/render.js +391 -0
  74. package/dist/esm/templating/repeat.js +96 -72
  75. package/dist/esm/templating/template.js +11 -29
  76. package/dist/esm/templating/view.js +107 -29
  77. package/dist/esm/templating/when.js +5 -4
  78. package/dist/esm/testing/exports.js +3 -0
  79. package/dist/esm/testing/fakes.js +76 -0
  80. package/dist/esm/testing/fixture.js +86 -0
  81. package/dist/esm/testing/timeout.js +24 -0
  82. package/dist/esm/utilities.js +0 -95
  83. package/dist/fast-element.api.json +9034 -10524
  84. package/dist/fast-element.d.ts +707 -811
  85. package/dist/fast-element.debug.js +1133 -850
  86. package/dist/fast-element.debug.min.js +1 -1
  87. package/dist/fast-element.js +1097 -846
  88. package/dist/fast-element.min.js +1 -1
  89. package/dist/fast-element.untrimmed.d.ts +724 -818
  90. package/docs/api-report.md +264 -305
  91. package/package.json +39 -10
  92. package/dist/dts/hooks.d.ts +0 -20
  93. package/dist/dts/observation/behavior.d.ts +0 -19
  94. package/dist/dts/observation/splice-strategies.d.ts +0 -13
  95. package/dist/esm/hooks.js +0 -32
  96. package/dist/esm/observation/splice-strategies.js +0 -400
@@ -1,24 +1,24 @@
1
1
  import "../interfaces.js";
2
2
  import { PropertyChangeNotifier } from "../observation/notifier.js";
3
- import { ExecutionContext, Observable } from "../observation/observable.js";
3
+ import { Observable, SourceLifetime } from "../observation/observable.js";
4
4
  import { FAST } from "../platform.js";
5
5
  import { FASTElementDefinition } from "./fast-definitions.js";
6
- const shadowRoots = new WeakMap();
7
6
  const defaultEventOptions = {
8
7
  bubbles: true,
9
8
  composed: true,
10
9
  cancelable: true,
11
10
  };
11
+ const isConnectedPropertyName = "isConnected";
12
+ const shadowRoots = new WeakMap();
12
13
  function getShadowRoot(element) {
13
14
  var _a, _b;
14
15
  return (_b = (_a = element.shadowRoot) !== null && _a !== void 0 ? _a : shadowRoots.get(element)) !== null && _b !== void 0 ? _b : null;
15
16
  }
16
- const isConnectedPropertyName = "isConnected";
17
17
  /**
18
18
  * Controls the lifecycle and rendering of a `FASTElement`.
19
19
  * @public
20
20
  */
21
- export class Controller extends PropertyChangeNotifier {
21
+ export class ElementController extends PropertyChangeNotifier {
22
22
  /**
23
23
  * Creates a Controller to control the specified element.
24
24
  * @param element - The element to be controlled by this controller.
@@ -29,12 +29,12 @@ export class Controller extends PropertyChangeNotifier {
29
29
  constructor(element, definition) {
30
30
  super(element);
31
31
  this.boundObservables = null;
32
- this.behaviors = null;
33
32
  this.needsInitialization = true;
34
33
  this.hasExistingShadowRoot = false;
35
34
  this._template = null;
36
- this._styles = null;
37
35
  this._isConnected = false;
36
+ this.behaviors = null;
37
+ this._mainStyles = null;
38
38
  /**
39
39
  * This allows Observable.getNotifier(...) to return the Controller
40
40
  * when the notifier for the Controller itself is being requested. The
@@ -50,7 +50,7 @@ export class Controller extends PropertyChangeNotifier {
50
50
  * If `null` then the element is managing its own rendering.
51
51
  */
52
52
  this.view = null;
53
- this.element = element;
53
+ this.source = element;
54
54
  this.definition = definition;
55
55
  const shadowOptions = definition.shadowOptions;
56
56
  if (shadowOptions !== void 0) {
@@ -104,9 +104,9 @@ export class Controller extends PropertyChangeNotifier {
104
104
  // 1. Template overrides take top precedence.
105
105
  if (this._template === null) {
106
106
  const definition = this.definition;
107
- if (this.element.resolveTemplate) {
107
+ if (this.source.resolveTemplate) {
108
108
  // 2. Allow for element instance overrides next.
109
- this._template = this.element.resolveTemplate();
109
+ this._template = this.source.resolveTemplate();
110
110
  }
111
111
  else if (definition.template) {
112
112
  // 3. Default to the static definition.
@@ -125,48 +125,92 @@ export class Controller extends PropertyChangeNotifier {
125
125
  }
126
126
  }
127
127
  /**
128
- * Gets/sets the primary styles used for the component.
129
- * @remarks
130
- * This value can only be accurately read after connect but can be set at any time.
128
+ * The main set of styles used for the component, independent
129
+ * of any dynamically added styles.
131
130
  */
132
- get styles() {
131
+ get mainStyles() {
133
132
  var _a;
134
133
  // 1. Styles overrides take top precedence.
135
- if (this._styles === null) {
134
+ if (this._mainStyles === null) {
136
135
  const definition = this.definition;
137
- if (this.element.resolveStyles) {
136
+ if (this.source.resolveStyles) {
138
137
  // 2. Allow for element instance overrides next.
139
- this._styles = this.element.resolveStyles();
138
+ this._mainStyles = this.source.resolveStyles();
140
139
  }
141
140
  else if (definition.styles) {
142
141
  // 3. Default to the static definition.
143
- this._styles = (_a = definition.styles) !== null && _a !== void 0 ? _a : null;
142
+ this._mainStyles = (_a = definition.styles) !== null && _a !== void 0 ? _a : null;
144
143
  }
145
144
  }
146
- return this._styles;
145
+ return this._mainStyles;
147
146
  }
148
- set styles(value) {
149
- if (this._styles === value) {
147
+ set mainStyles(value) {
148
+ if (this._mainStyles === value) {
150
149
  return;
151
150
  }
152
- if (this._styles !== null) {
153
- this.removeStyles(this._styles);
151
+ if (this._mainStyles !== null) {
152
+ this.removeStyles(this._mainStyles);
154
153
  }
155
- this._styles = value;
154
+ this._mainStyles = value;
156
155
  if (!this.needsInitialization) {
157
156
  this.addStyles(value);
158
157
  }
159
158
  }
159
+ /**
160
+ * Adds the behavior to the component.
161
+ * @param behavior - The behavior to add.
162
+ */
163
+ addBehavior(behavior) {
164
+ var _a, _b;
165
+ const targetBehaviors = (_a = this.behaviors) !== null && _a !== void 0 ? _a : (this.behaviors = new Map());
166
+ const count = (_b = targetBehaviors.get(behavior)) !== null && _b !== void 0 ? _b : 0;
167
+ if (count === 0) {
168
+ targetBehaviors.set(behavior, 1);
169
+ behavior.addedCallback && behavior.addedCallback(this);
170
+ if (behavior.connectedCallback && this.isConnected) {
171
+ behavior.connectedCallback(this);
172
+ }
173
+ }
174
+ else {
175
+ targetBehaviors.set(behavior, count + 1);
176
+ }
177
+ }
178
+ /**
179
+ * Removes the behavior from the component.
180
+ * @param behavior - The behavior to remove.
181
+ * @param force - Forces removal even if this behavior was added more than once.
182
+ */
183
+ removeBehavior(behavior, force = false) {
184
+ const targetBehaviors = this.behaviors;
185
+ if (targetBehaviors === null) {
186
+ return;
187
+ }
188
+ const count = targetBehaviors.get(behavior);
189
+ if (count === void 0) {
190
+ return;
191
+ }
192
+ if (count === 1 || force) {
193
+ targetBehaviors.delete(behavior);
194
+ if (behavior.disconnectedCallback && this.isConnected) {
195
+ behavior.disconnectedCallback(this);
196
+ }
197
+ behavior.removedCallback && behavior.removedCallback(this);
198
+ }
199
+ else {
200
+ targetBehaviors.set(behavior, count - 1);
201
+ }
202
+ }
160
203
  /**
161
204
  * Adds styles to this element. Providing an HTMLStyleElement will attach the element instance to the shadowRoot.
162
205
  * @param styles - The styles to add.
163
206
  */
164
207
  addStyles(styles) {
208
+ var _a;
165
209
  if (!styles) {
166
210
  return;
167
211
  }
168
- const target = getShadowRoot(this.element) ||
169
- this.element.getRootNode();
212
+ const source = this.source;
213
+ const target = (_a = getShadowRoot(source)) !== null && _a !== void 0 ? _a : source.getRootNode();
170
214
  if (styles instanceof HTMLElement) {
171
215
  target.append(styles);
172
216
  }
@@ -174,7 +218,9 @@ export class Controller extends PropertyChangeNotifier {
174
218
  const sourceBehaviors = styles.behaviors;
175
219
  styles.addStylesTo(target);
176
220
  if (sourceBehaviors !== null) {
177
- this.addBehaviors(sourceBehaviors);
221
+ for (let i = 0, ii = sourceBehaviors.length; i < ii; ++i) {
222
+ this.addBehavior(sourceBehaviors[i]);
223
+ }
178
224
  }
179
225
  }
180
226
  }
@@ -183,11 +229,12 @@ export class Controller extends PropertyChangeNotifier {
183
229
  * @param styles - the styles to remove.
184
230
  */
185
231
  removeStyles(styles) {
232
+ var _a;
186
233
  if (!styles) {
187
234
  return;
188
235
  }
189
- const target = getShadowRoot(this.element) ||
190
- this.element.getRootNode();
236
+ const source = this.source;
237
+ const target = (_a = getShadowRoot(source)) !== null && _a !== void 0 ? _a : source.getRootNode();
191
238
  if (styles instanceof HTMLElement) {
192
239
  target.removeChild(styles);
193
240
  }
@@ -195,85 +242,29 @@ export class Controller extends PropertyChangeNotifier {
195
242
  const sourceBehaviors = styles.behaviors;
196
243
  styles.removeStylesFrom(target);
197
244
  if (sourceBehaviors !== null) {
198
- this.removeBehaviors(sourceBehaviors);
199
- }
200
- }
201
- }
202
- /**
203
- * Adds behaviors to this element.
204
- * @param behaviors - The behaviors to add.
205
- */
206
- addBehaviors(behaviors) {
207
- var _a;
208
- const targetBehaviors = (_a = this.behaviors) !== null && _a !== void 0 ? _a : (this.behaviors = new Map());
209
- const length = behaviors.length;
210
- const behaviorsToBind = [];
211
- for (let i = 0; i < length; ++i) {
212
- const behavior = behaviors[i];
213
- if (targetBehaviors.has(behavior)) {
214
- targetBehaviors.set(behavior, targetBehaviors.get(behavior) + 1);
215
- }
216
- else {
217
- targetBehaviors.set(behavior, 1);
218
- behaviorsToBind.push(behavior);
219
- }
220
- }
221
- if (this._isConnected) {
222
- const element = this.element;
223
- const context = ExecutionContext.default;
224
- for (let i = 0; i < behaviorsToBind.length; ++i) {
225
- behaviorsToBind[i].bind(element, context);
226
- }
227
- }
228
- }
229
- /**
230
- * Removes behaviors from this element.
231
- * @param behaviors - The behaviors to remove.
232
- * @param force - Forces unbinding of behaviors.
233
- */
234
- removeBehaviors(behaviors, force = false) {
235
- const targetBehaviors = this.behaviors;
236
- if (targetBehaviors === null) {
237
- return;
238
- }
239
- const length = behaviors.length;
240
- const behaviorsToUnbind = [];
241
- for (let i = 0; i < length; ++i) {
242
- const behavior = behaviors[i];
243
- if (targetBehaviors.has(behavior)) {
244
- const count = targetBehaviors.get(behavior) - 1;
245
- count === 0 || force
246
- ? targetBehaviors.delete(behavior) && behaviorsToUnbind.push(behavior)
247
- : targetBehaviors.set(behavior, count);
248
- }
249
- }
250
- if (this._isConnected) {
251
- const element = this.element;
252
- const context = ExecutionContext.default;
253
- for (let i = 0; i < behaviorsToUnbind.length; ++i) {
254
- behaviorsToUnbind[i].unbind(element, context);
245
+ for (let i = 0, ii = sourceBehaviors.length; i < ii; ++i) {
246
+ this.addBehavior(sourceBehaviors[i]);
247
+ }
255
248
  }
256
249
  }
257
250
  }
258
251
  /**
259
252
  * Runs connected lifecycle behavior on the associated element.
260
253
  */
261
- onConnectedCallback() {
254
+ connect() {
262
255
  if (this._isConnected) {
263
256
  return;
264
257
  }
265
- const element = this.element;
266
- const context = ExecutionContext.default;
267
258
  if (this.needsInitialization) {
268
259
  this.finishInitialization();
269
260
  }
270
261
  else if (this.view !== null) {
271
- this.view.bind(element, context);
262
+ this.view.bind(this.source);
272
263
  }
273
264
  const behaviors = this.behaviors;
274
265
  if (behaviors !== null) {
275
- for (const behavior of behaviors.keys()) {
276
- behavior.bind(element, context);
266
+ for (const key of behaviors.keys()) {
267
+ key.connectedCallback && key.connectedCallback(this);
277
268
  }
278
269
  }
279
270
  this.setIsConnected(true);
@@ -281,21 +272,18 @@ export class Controller extends PropertyChangeNotifier {
281
272
  /**
282
273
  * Runs disconnected lifecycle behavior on the associated element.
283
274
  */
284
- onDisconnectedCallback() {
275
+ disconnect() {
285
276
  if (!this._isConnected) {
286
277
  return;
287
278
  }
288
279
  this.setIsConnected(false);
289
- const view = this.view;
290
- if (view !== null) {
291
- view.unbind();
280
+ if (this.view !== null) {
281
+ this.view.unbind();
292
282
  }
293
283
  const behaviors = this.behaviors;
294
284
  if (behaviors !== null) {
295
- const element = this.element;
296
- const context = ExecutionContext.default;
297
- for (const behavior of behaviors.keys()) {
298
- behavior.unbind(element, context);
285
+ for (const key of behaviors.keys()) {
286
+ key.disconnectedCallback && key.disconnectedCallback(this);
299
287
  }
300
288
  }
301
289
  }
@@ -308,7 +296,7 @@ export class Controller extends PropertyChangeNotifier {
308
296
  onAttributeChangedCallback(name, oldValue, newValue) {
309
297
  const attrDef = this.definition.attributeLookup[name];
310
298
  if (attrDef !== void 0) {
311
- attrDef.onAttributeChangedCallback(this.element, newValue);
299
+ attrDef.onAttributeChangedCallback(this.source, newValue);
312
300
  }
313
301
  }
314
302
  /**
@@ -321,12 +309,12 @@ export class Controller extends PropertyChangeNotifier {
321
309
  */
322
310
  emit(type, detail, options) {
323
311
  if (this._isConnected) {
324
- return this.element.dispatchEvent(new CustomEvent(type, Object.assign(Object.assign({ detail }, defaultEventOptions), options)));
312
+ return this.source.dispatchEvent(new CustomEvent(type, Object.assign(Object.assign({ detail }, defaultEventOptions), options)));
325
313
  }
326
314
  return false;
327
315
  }
328
316
  finishInitialization() {
329
- const element = this.element;
317
+ const element = this.source;
330
318
  const boundObservables = this.boundObservables;
331
319
  // If we have any observables that were bound, re-apply their values.
332
320
  if (boundObservables !== null) {
@@ -338,15 +326,15 @@ export class Controller extends PropertyChangeNotifier {
338
326
  this.boundObservables = null;
339
327
  }
340
328
  this.renderTemplate(this.template);
341
- this.addStyles(this.styles);
329
+ this.addStyles(this.mainStyles);
342
330
  this.needsInitialization = false;
343
331
  }
344
332
  renderTemplate(template) {
345
333
  var _a;
346
- const element = this.element;
347
334
  // When getting the host to render to, we start by looking
348
335
  // up the shadow root. If there isn't one, then that means
349
336
  // we're doing a Light DOM render to the element's direct children.
337
+ const element = this.source;
350
338
  const host = (_a = getShadowRoot(element)) !== null && _a !== void 0 ? _a : element;
351
339
  if (this.view !== null) {
352
340
  // If there's already a view, we need to unbind and remove through dispose.
@@ -363,6 +351,8 @@ export class Controller extends PropertyChangeNotifier {
363
351
  if (template) {
364
352
  // If a new template was provided, render it.
365
353
  this.view = template.render(element, host, element);
354
+ this.view.sourceLifetime =
355
+ SourceLifetime.coupled;
366
356
  }
367
357
  }
368
358
  /**
@@ -382,6 +372,6 @@ export class Controller extends PropertyChangeNotifier {
382
372
  if (definition === void 0) {
383
373
  throw FAST.error(1401 /* Message.missingElementDefinition */);
384
374
  }
385
- return (element.$fastController = new Controller(element, definition));
375
+ return (element.$fastController = new ElementController(element, definition));
386
376
  }
387
377
  }
@@ -5,25 +5,24 @@ import { ElementStyles } from "../styles/element-styles.js";
5
5
  import { AttributeDefinition } from "./attributes.js";
6
6
  const defaultShadowOptions = { mode: "open" };
7
7
  const defaultElementOptions = {};
8
+ const fastElementBaseTypes = new Set();
8
9
  const fastElementRegistry = FAST.getById(4 /* KernelServiceId.elementRegistry */, () => createTypeRegistry());
9
10
  /**
10
11
  * Defines metadata for a FASTElement.
11
12
  * @public
12
13
  */
13
14
  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
15
  constructor(type, nameOrConfig = type.definition) {
16
+ var _a;
17
+ this.platformDefined = false;
21
18
  if (isString(nameOrConfig)) {
22
19
  nameOrConfig = { name: nameOrConfig };
23
20
  }
24
21
  this.type = type;
25
22
  this.name = nameOrConfig.name;
26
23
  this.template = nameOrConfig.template;
24
+ this.registry = (_a = nameOrConfig.registry) !== null && _a !== void 0 ? _a : customElements;
25
+ const proto = type.prototype;
27
26
  const attributes = AttributeDefinition.collect(type, nameOrConfig.attributes);
28
27
  const observedAttributes = new Array(attributes.length);
29
28
  const propertyLookup = {};
@@ -33,9 +32,13 @@ export class FASTElementDefinition {
33
32
  observedAttributes[i] = current.attribute;
34
33
  propertyLookup[current.name] = current;
35
34
  attributeLookup[current.attribute] = current;
35
+ Observable.defineProperty(proto, current);
36
36
  }
37
+ Reflect.defineProperty(type, "observedAttributes", {
38
+ value: observedAttributes,
39
+ enumerable: true,
40
+ });
37
41
  this.attributes = attributes;
38
- this.observedAttributes = observedAttributes;
39
42
  this.propertyLookup = propertyLookup;
40
43
  this.attributeLookup = attributeLookup;
41
44
  this.shadowOptions =
@@ -48,43 +51,50 @@ export class FASTElementDefinition {
48
51
  nameOrConfig.elementOptions === void 0
49
52
  ? defaultElementOptions
50
53
  : 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]);
54
+ this.styles = ElementStyles.normalize(nameOrConfig.styles);
55
+ fastElementRegistry.register(this);
59
56
  }
60
57
  /**
61
58
  * Indicates if this element has been defined in at least one registry.
62
59
  */
63
60
  get isDefined() {
64
- return !!fastElementRegistry.getByType(this.type);
61
+ return this.platformDefined;
65
62
  }
66
63
  /**
67
64
  * Defines a custom element based on this definition.
68
65
  * @param registry - The element registry to define the element in.
66
+ * @remarks
67
+ * This operation is idempotent per registry.
69
68
  */
70
- define(registry = customElements) {
69
+ define(registry = this.registry) {
71
70
  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
71
  if (!registry.get(this.name)) {
72
+ this.platformDefined = true;
84
73
  registry.define(this.name, type, this.elementOptions);
85
74
  }
86
75
  return this;
87
76
  }
77
+ /**
78
+ * Creates an instance of FASTElementDefinition.
79
+ * @param type - The type this definition is being created for.
80
+ * @param nameOrDef - The name of the element to define or a config object
81
+ * that describes the element to define.
82
+ */
83
+ static compose(type, nameOrDef) {
84
+ if (fastElementBaseTypes.has(type) || fastElementRegistry.getByType(type)) {
85
+ return new FASTElementDefinition(class extends type {
86
+ }, nameOrDef);
87
+ }
88
+ return new FASTElementDefinition(type, nameOrDef);
89
+ }
90
+ /**
91
+ * Registers a FASTElement base type.
92
+ * @param type - The type to register as a base type.
93
+ * @internal
94
+ */
95
+ static registerBaseType(type) {
96
+ fastElementBaseTypes.add(type);
97
+ }
88
98
  }
89
99
  /**
90
100
  * Gets the element definition associated with the specified type.
@@ -1,26 +1,44 @@
1
- import { Controller } from "./controller.js";
1
+ import { isFunction } from "../interfaces.js";
2
+ import { ElementController } from "./element-controller.js";
2
3
  import { FASTElementDefinition, } from "./fast-definitions.js";
3
4
  /* eslint-disable-next-line @typescript-eslint/explicit-function-return-type */
4
5
  function createFASTElement(BaseType) {
5
- return class extends BaseType {
6
+ const type = class extends BaseType {
6
7
  constructor() {
7
8
  /* eslint-disable-next-line */
8
9
  super();
9
- Controller.forCustomElement(this);
10
+ ElementController.forCustomElement(this);
10
11
  }
11
12
  $emit(type, detail, options) {
12
13
  return this.$fastController.emit(type, detail, options);
13
14
  }
14
15
  connectedCallback() {
15
- this.$fastController.onConnectedCallback();
16
+ this.$fastController.connect();
16
17
  }
17
18
  disconnectedCallback() {
18
- this.$fastController.onDisconnectedCallback();
19
+ this.$fastController.disconnect();
19
20
  }
20
21
  attributeChangedCallback(name, oldValue, newValue) {
21
22
  this.$fastController.onAttributeChangedCallback(name, oldValue, newValue);
22
23
  }
23
24
  };
25
+ FASTElementDefinition.registerBaseType(type);
26
+ return type;
27
+ }
28
+ function compose(type, nameOrDef) {
29
+ if (isFunction(type)) {
30
+ return FASTElementDefinition.compose(type, nameOrDef);
31
+ }
32
+ return FASTElementDefinition.compose(this, type);
33
+ }
34
+ function define(type, nameOrDef) {
35
+ if (isFunction(type)) {
36
+ return FASTElementDefinition.compose(type, nameOrDef).define().type;
37
+ }
38
+ return FASTElementDefinition.compose(this, type).define().type;
39
+ }
40
+ function from(BaseType) {
41
+ return createFASTElement(BaseType);
24
42
  }
25
43
  /**
26
44
  * A minimal base class for FASTElements that also provides
@@ -33,18 +51,19 @@ export const FASTElement = Object.assign(createFASTElement(HTMLElement), {
33
51
  * provided base type.
34
52
  * @param BaseType - The base element type to inherit from.
35
53
  */
36
- from(BaseType) {
37
- return createFASTElement(BaseType);
38
- },
54
+ from,
39
55
  /**
40
56
  * Defines a platform custom element based on the provided type and definition.
41
57
  * @param type - The custom element type to define.
42
58
  * @param nameOrDef - The name of the element to define or a definition object
43
59
  * that describes the element to define.
44
60
  */
45
- define(type, nameOrDef) {
46
- return new FASTElementDefinition(type, nameOrDef).define().type;
47
- },
61
+ define,
62
+ /**
63
+ * Defines metadata for a FASTElement which can be used to later define the element.
64
+ * @public
65
+ */
66
+ compose,
48
67
  });
49
68
  /**
50
69
  * Decorator: Defines a platform custom element based on `FASTElement`.
@@ -55,6 +74,6 @@ export const FASTElement = Object.assign(createFASTElement(HTMLElement), {
55
74
  export function customElement(nameOrDef) {
56
75
  /* eslint-disable-next-line @typescript-eslint/explicit-function-return-type */
57
76
  return function (type) {
58
- new FASTElementDefinition(type, nameOrDef).define();
77
+ define(type, nameOrDef);
59
78
  };
60
79
  }