@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.
Files changed (98) hide show
  1. package/CHANGELOG.json +348 -0
  2. package/CHANGELOG.md +114 -1
  3. package/dist/dts/components/attributes.d.ts +10 -0
  4. package/dist/dts/components/{controller.d.ts → element-controller.d.ts} +49 -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 +45 -14
  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 +7 -0
  16. package/dist/dts/polyfills.d.ts +1 -8
  17. package/dist/dts/state/exports.d.ts +3 -0
  18. package/dist/dts/state/reactive.d.ts +8 -0
  19. package/dist/dts/state/state.d.ts +141 -0
  20. package/dist/dts/state/visitor.d.ts +6 -0
  21. package/dist/dts/state/watch.d.ts +10 -0
  22. package/dist/dts/styles/css-directive.d.ts +2 -2
  23. package/dist/dts/styles/element-styles.d.ts +10 -17
  24. package/dist/dts/styles/host.d.ts +68 -0
  25. package/dist/dts/templating/binding-signal.d.ts +21 -0
  26. package/dist/dts/templating/binding-two-way.d.ts +39 -0
  27. package/dist/dts/templating/binding.d.ts +69 -294
  28. package/dist/dts/templating/children.d.ts +1 -1
  29. package/dist/dts/templating/compiler.d.ts +1 -2
  30. package/dist/dts/templating/html-directive.d.ts +93 -35
  31. package/dist/dts/templating/node-observation.d.ts +4 -5
  32. package/dist/dts/templating/ref.d.ts +5 -13
  33. package/dist/dts/templating/render.d.ts +272 -0
  34. package/dist/dts/templating/repeat.d.ts +20 -75
  35. package/dist/dts/templating/slotted.d.ts +1 -1
  36. package/dist/dts/templating/template.d.ts +12 -61
  37. package/dist/dts/templating/view.d.ts +77 -12
  38. package/dist/dts/templating/when.d.ts +3 -3
  39. package/dist/dts/testing/exports.d.ts +3 -0
  40. package/dist/dts/testing/fakes.d.ts +4 -0
  41. package/dist/dts/testing/fixture.d.ts +84 -0
  42. package/dist/dts/testing/timeout.d.ts +7 -0
  43. package/dist/{tsdoc-metadata.json → dts/tsdoc-metadata.json} +0 -0
  44. package/dist/dts/utilities.d.ts +0 -18
  45. package/dist/esm/components/attributes.js +13 -4
  46. package/dist/esm/components/{controller.js → element-controller.js} +188 -109
  47. package/dist/esm/components/fast-definitions.js +38 -28
  48. package/dist/esm/components/fast-element.js +31 -12
  49. package/dist/esm/context.js +163 -0
  50. package/dist/esm/debug.js +36 -4
  51. package/dist/esm/di/di.js +1435 -0
  52. package/dist/esm/index.js +2 -1
  53. package/dist/esm/interfaces.js +4 -0
  54. package/dist/esm/metadata.js +60 -0
  55. package/dist/esm/observation/arrays.js +304 -3
  56. package/dist/esm/observation/observable.js +81 -87
  57. package/dist/esm/pending-task.js +16 -0
  58. package/dist/esm/platform.js +26 -1
  59. package/dist/esm/polyfills.js +1 -55
  60. package/dist/esm/state/exports.js +3 -0
  61. package/dist/esm/state/reactive.js +34 -0
  62. package/dist/esm/state/state.js +148 -0
  63. package/dist/esm/state/visitor.js +28 -0
  64. package/dist/esm/state/watch.js +36 -0
  65. package/dist/esm/styles/css.js +4 -4
  66. package/dist/esm/styles/element-styles.js +14 -33
  67. package/dist/esm/{observation/behavior.js → styles/host.js} +0 -0
  68. package/dist/esm/templating/binding-signal.js +83 -0
  69. package/dist/esm/templating/binding-two-way.js +103 -0
  70. package/dist/esm/templating/binding.js +134 -414
  71. package/dist/esm/templating/compiler.js +30 -7
  72. package/dist/esm/templating/html-directive.js +100 -28
  73. package/dist/esm/templating/node-observation.js +9 -8
  74. package/dist/esm/templating/ref.js +4 -12
  75. package/dist/esm/templating/render.js +391 -0
  76. package/dist/esm/templating/repeat.js +96 -72
  77. package/dist/esm/templating/template.js +11 -29
  78. package/dist/esm/templating/view.js +107 -29
  79. package/dist/esm/templating/when.js +5 -4
  80. package/dist/esm/testing/exports.js +3 -0
  81. package/dist/esm/testing/fakes.js +76 -0
  82. package/dist/esm/testing/fixture.js +86 -0
  83. package/dist/esm/testing/timeout.js +24 -0
  84. package/dist/esm/utilities.js +0 -95
  85. package/dist/fast-element.api.json +9278 -10745
  86. package/dist/fast-element.d.ts +707 -813
  87. package/dist/fast-element.debug.js +1229 -944
  88. package/dist/fast-element.debug.min.js +1 -1
  89. package/dist/fast-element.js +1191 -938
  90. package/dist/fast-element.min.js +1 -1
  91. package/dist/fast-element.untrimmed.d.ts +716 -824
  92. package/docs/api-report.md +265 -319
  93. package/package.json +39 -14
  94. package/dist/dts/hooks.d.ts +0 -20
  95. package/dist/dts/observation/behavior.d.ts +0 -19
  96. package/dist/dts/observation/splice-strategies.d.ts +0 -13
  97. package/dist/esm/hooks.js +0 -32
  98. package/dist/esm/observation/splice-strategies.js +0 -400
@@ -1,24 +1,25 @@
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
+ import { ElementStyles } from "../styles/element-styles.js";
5
6
  import { FASTElementDefinition } from "./fast-definitions.js";
6
- const shadowRoots = new WeakMap();
7
7
  const defaultEventOptions = {
8
8
  bubbles: true,
9
9
  composed: true,
10
10
  cancelable: true,
11
11
  };
12
+ const isConnectedPropertyName = "isConnected";
13
+ const shadowRoots = new WeakMap();
12
14
  function getShadowRoot(element) {
13
15
  var _a, _b;
14
16
  return (_b = (_a = element.shadowRoot) !== null && _a !== void 0 ? _a : shadowRoots.get(element)) !== null && _b !== void 0 ? _b : null;
15
17
  }
16
- const isConnectedPropertyName = "isConnected";
17
18
  /**
18
19
  * Controls the lifecycle and rendering of a `FASTElement`.
19
20
  * @public
20
21
  */
21
- export class Controller extends PropertyChangeNotifier {
22
+ export class ElementController extends PropertyChangeNotifier {
22
23
  /**
23
24
  * Creates a Controller to control the specified element.
24
25
  * @param element - The element to be controlled by this controller.
@@ -29,12 +30,12 @@ export class Controller extends PropertyChangeNotifier {
29
30
  constructor(element, definition) {
30
31
  super(element);
31
32
  this.boundObservables = null;
32
- this.behaviors = null;
33
33
  this.needsInitialization = true;
34
34
  this.hasExistingShadowRoot = false;
35
35
  this._template = null;
36
- this._styles = null;
37
36
  this._isConnected = false;
37
+ this.behaviors = null;
38
+ this._mainStyles = null;
38
39
  /**
39
40
  * This allows Observable.getNotifier(...) to return the Controller
40
41
  * when the notifier for the Controller itself is being requested. The
@@ -50,7 +51,7 @@ export class Controller extends PropertyChangeNotifier {
50
51
  * If `null` then the element is managing its own rendering.
51
52
  */
52
53
  this.view = null;
53
- this.element = element;
54
+ this.source = element;
54
55
  this.definition = definition;
55
56
  const shadowOptions = definition.shadowOptions;
56
57
  if (shadowOptions !== void 0) {
@@ -104,9 +105,9 @@ export class Controller extends PropertyChangeNotifier {
104
105
  // 1. Template overrides take top precedence.
105
106
  if (this._template === null) {
106
107
  const definition = this.definition;
107
- if (this.element.resolveTemplate) {
108
+ if (this.source.resolveTemplate) {
108
109
  // 2. Allow for element instance overrides next.
109
- this._template = this.element.resolveTemplate();
110
+ this._template = this.source.resolveTemplate();
110
111
  }
111
112
  else if (definition.template) {
112
113
  // 3. Default to the static definition.
@@ -125,56 +126,102 @@ export class Controller extends PropertyChangeNotifier {
125
126
  }
126
127
  }
127
128
  /**
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.
129
+ * The main set of styles used for the component, independent
130
+ * of any dynamically added styles.
131
131
  */
132
- get styles() {
132
+ get mainStyles() {
133
133
  var _a;
134
134
  // 1. Styles overrides take top precedence.
135
- if (this._styles === null) {
135
+ if (this._mainStyles === null) {
136
136
  const definition = this.definition;
137
- if (this.element.resolveStyles) {
137
+ if (this.source.resolveStyles) {
138
138
  // 2. Allow for element instance overrides next.
139
- this._styles = this.element.resolveStyles();
139
+ this._mainStyles = this.source.resolveStyles();
140
140
  }
141
141
  else if (definition.styles) {
142
142
  // 3. Default to the static definition.
143
- this._styles = (_a = definition.styles) !== null && _a !== void 0 ? _a : null;
143
+ this._mainStyles = (_a = definition.styles) !== null && _a !== void 0 ? _a : null;
144
144
  }
145
145
  }
146
- return this._styles;
146
+ return this._mainStyles;
147
147
  }
148
- set styles(value) {
149
- if (this._styles === value) {
148
+ set mainStyles(value) {
149
+ if (this._mainStyles === value) {
150
150
  return;
151
151
  }
152
- if (this._styles !== null) {
153
- this.removeStyles(this._styles);
152
+ if (this._mainStyles !== null) {
153
+ this.removeStyles(this._mainStyles);
154
154
  }
155
- this._styles = value;
155
+ this._mainStyles = value;
156
156
  if (!this.needsInitialization) {
157
157
  this.addStyles(value);
158
158
  }
159
159
  }
160
+ /**
161
+ * Adds the behavior to the component.
162
+ * @param behavior - The behavior to add.
163
+ */
164
+ addBehavior(behavior) {
165
+ var _a, _b;
166
+ const targetBehaviors = (_a = this.behaviors) !== null && _a !== void 0 ? _a : (this.behaviors = new Map());
167
+ const count = (_b = targetBehaviors.get(behavior)) !== null && _b !== void 0 ? _b : 0;
168
+ if (count === 0) {
169
+ targetBehaviors.set(behavior, 1);
170
+ behavior.addedCallback && behavior.addedCallback(this);
171
+ if (behavior.connectedCallback && this.isConnected) {
172
+ behavior.connectedCallback(this);
173
+ }
174
+ }
175
+ else {
176
+ targetBehaviors.set(behavior, count + 1);
177
+ }
178
+ }
179
+ /**
180
+ * Removes the behavior from the component.
181
+ * @param behavior - The behavior to remove.
182
+ * @param force - Forces removal even if this behavior was added more than once.
183
+ */
184
+ removeBehavior(behavior, force = false) {
185
+ const targetBehaviors = this.behaviors;
186
+ if (targetBehaviors === null) {
187
+ return;
188
+ }
189
+ const count = targetBehaviors.get(behavior);
190
+ if (count === void 0) {
191
+ return;
192
+ }
193
+ if (count === 1 || force) {
194
+ targetBehaviors.delete(behavior);
195
+ if (behavior.disconnectedCallback && this.isConnected) {
196
+ behavior.disconnectedCallback(this);
197
+ }
198
+ behavior.removedCallback && behavior.removedCallback(this);
199
+ }
200
+ else {
201
+ targetBehaviors.set(behavior, count - 1);
202
+ }
203
+ }
160
204
  /**
161
205
  * Adds styles to this element. Providing an HTMLStyleElement will attach the element instance to the shadowRoot.
162
206
  * @param styles - The styles to add.
163
207
  */
164
208
  addStyles(styles) {
209
+ var _a;
165
210
  if (!styles) {
166
211
  return;
167
212
  }
168
- const target = getShadowRoot(this.element) ||
169
- this.element.getRootNode();
213
+ const source = this.source;
170
214
  if (styles instanceof HTMLElement) {
215
+ const target = (_a = getShadowRoot(source)) !== null && _a !== void 0 ? _a : this.source;
171
216
  target.append(styles);
172
217
  }
173
- else if (!styles.isAttachedTo(target)) {
218
+ else if (!styles.isAttachedTo(source)) {
174
219
  const sourceBehaviors = styles.behaviors;
175
- styles.addStylesTo(target);
220
+ styles.addStylesTo(source);
176
221
  if (sourceBehaviors !== null) {
177
- this.addBehaviors(sourceBehaviors);
222
+ for (let i = 0, ii = sourceBehaviors.length; i < ii; ++i) {
223
+ this.addBehavior(sourceBehaviors[i]);
224
+ }
178
225
  }
179
226
  }
180
227
  }
@@ -183,97 +230,42 @@ export class Controller extends PropertyChangeNotifier {
183
230
  * @param styles - the styles to remove.
184
231
  */
185
232
  removeStyles(styles) {
233
+ var _a;
186
234
  if (!styles) {
187
235
  return;
188
236
  }
189
- const target = getShadowRoot(this.element) ||
190
- this.element.getRootNode();
237
+ const source = this.source;
191
238
  if (styles instanceof HTMLElement) {
239
+ const target = (_a = getShadowRoot(source)) !== null && _a !== void 0 ? _a : source;
192
240
  target.removeChild(styles);
193
241
  }
194
- else if (styles.isAttachedTo(target)) {
242
+ else if (styles.isAttachedTo(source)) {
195
243
  const sourceBehaviors = styles.behaviors;
196
- styles.removeStylesFrom(target);
244
+ styles.removeStylesFrom(source);
197
245
  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);
246
+ for (let i = 0, ii = sourceBehaviors.length; i < ii; ++i) {
247
+ this.addBehavior(sourceBehaviors[i]);
248
+ }
255
249
  }
256
250
  }
257
251
  }
258
252
  /**
259
253
  * Runs connected lifecycle behavior on the associated element.
260
254
  */
261
- onConnectedCallback() {
255
+ connect() {
262
256
  if (this._isConnected) {
263
257
  return;
264
258
  }
265
- const element = this.element;
266
- const context = ExecutionContext.default;
267
259
  if (this.needsInitialization) {
268
260
  this.finishInitialization();
269
261
  }
270
262
  else if (this.view !== null) {
271
- this.view.bind(element, context);
263
+ this.view.bind(this.source);
272
264
  }
273
265
  const behaviors = this.behaviors;
274
266
  if (behaviors !== null) {
275
- for (const behavior of behaviors.keys()) {
276
- behavior.bind(element, context);
267
+ for (const key of behaviors.keys()) {
268
+ key.connectedCallback && key.connectedCallback(this);
277
269
  }
278
270
  }
279
271
  this.setIsConnected(true);
@@ -281,21 +273,18 @@ export class Controller extends PropertyChangeNotifier {
281
273
  /**
282
274
  * Runs disconnected lifecycle behavior on the associated element.
283
275
  */
284
- onDisconnectedCallback() {
276
+ disconnect() {
285
277
  if (!this._isConnected) {
286
278
  return;
287
279
  }
288
280
  this.setIsConnected(false);
289
- const view = this.view;
290
- if (view !== null) {
291
- view.unbind();
281
+ if (this.view !== null) {
282
+ this.view.unbind();
292
283
  }
293
284
  const behaviors = this.behaviors;
294
285
  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);
286
+ for (const key of behaviors.keys()) {
287
+ key.disconnectedCallback && key.disconnectedCallback(this);
299
288
  }
300
289
  }
301
290
  }
@@ -308,7 +297,7 @@ export class Controller extends PropertyChangeNotifier {
308
297
  onAttributeChangedCallback(name, oldValue, newValue) {
309
298
  const attrDef = this.definition.attributeLookup[name];
310
299
  if (attrDef !== void 0) {
311
- attrDef.onAttributeChangedCallback(this.element, newValue);
300
+ attrDef.onAttributeChangedCallback(this.source, newValue);
312
301
  }
313
302
  }
314
303
  /**
@@ -321,12 +310,12 @@ export class Controller extends PropertyChangeNotifier {
321
310
  */
322
311
  emit(type, detail, options) {
323
312
  if (this._isConnected) {
324
- return this.element.dispatchEvent(new CustomEvent(type, Object.assign(Object.assign({ detail }, defaultEventOptions), options)));
313
+ return this.source.dispatchEvent(new CustomEvent(type, Object.assign(Object.assign({ detail }, defaultEventOptions), options)));
325
314
  }
326
315
  return false;
327
316
  }
328
317
  finishInitialization() {
329
- const element = this.element;
318
+ const element = this.source;
330
319
  const boundObservables = this.boundObservables;
331
320
  // If we have any observables that were bound, re-apply their values.
332
321
  if (boundObservables !== null) {
@@ -338,15 +327,15 @@ export class Controller extends PropertyChangeNotifier {
338
327
  this.boundObservables = null;
339
328
  }
340
329
  this.renderTemplate(this.template);
341
- this.addStyles(this.styles);
330
+ this.addStyles(this.mainStyles);
342
331
  this.needsInitialization = false;
343
332
  }
344
333
  renderTemplate(template) {
345
334
  var _a;
346
- const element = this.element;
347
335
  // When getting the host to render to, we start by looking
348
336
  // up the shadow root. If there isn't one, then that means
349
337
  // we're doing a Light DOM render to the element's direct children.
338
+ const element = this.source;
350
339
  const host = (_a = getShadowRoot(element)) !== null && _a !== void 0 ? _a : element;
351
340
  if (this.view !== null) {
352
341
  // If there's already a view, we need to unbind and remove through dispose.
@@ -363,6 +352,8 @@ export class Controller extends PropertyChangeNotifier {
363
352
  if (template) {
364
353
  // If a new template was provided, render it.
365
354
  this.view = template.render(element, host, element);
355
+ this.view.sourceLifetime =
356
+ SourceLifetime.coupled;
366
357
  }
367
358
  }
368
359
  /**
@@ -382,6 +373,94 @@ export class Controller extends PropertyChangeNotifier {
382
373
  if (definition === void 0) {
383
374
  throw FAST.error(1401 /* Message.missingElementDefinition */);
384
375
  }
385
- return (element.$fastController = new Controller(element, definition));
376
+ return (element.$fastController = new ElementController(element, definition));
377
+ }
378
+ }
379
+ /**
380
+ * Converts a styleTarget into the operative target. When the provided target is an Element
381
+ * that is a FASTElement, the function will return the ShadowRoot for that element. Otherwise,
382
+ * it will return the root node for the element.
383
+ * @param target
384
+ * @returns
385
+ */
386
+ function normalizeStyleTarget(target) {
387
+ var _a;
388
+ if ("adoptedStyleSheets" in target) {
389
+ return target;
390
+ }
391
+ else {
392
+ return ((_a = getShadowRoot(target)) !== null && _a !== void 0 ? _a : target.getRootNode());
393
+ }
394
+ }
395
+ // Default StyleStrategy implementations are defined in this module because they
396
+ // require access to element shadowRoots, and we don't want to leak shadowRoot
397
+ // objects out of this module.
398
+ /**
399
+ * https://wicg.github.io/construct-stylesheets/
400
+ * https://developers.google.com/web/updates/2019/02/constructable-stylesheets
401
+ *
402
+ * @internal
403
+ */
404
+ export class AdoptedStyleSheetsStrategy {
405
+ constructor(styles) {
406
+ const styleSheetCache = AdoptedStyleSheetsStrategy.styleSheetCache;
407
+ this.sheets = styles.map((x) => {
408
+ if (x instanceof CSSStyleSheet) {
409
+ return x;
410
+ }
411
+ let sheet = styleSheetCache.get(x);
412
+ if (sheet === void 0) {
413
+ sheet = new CSSStyleSheet();
414
+ sheet.replaceSync(x);
415
+ styleSheetCache.set(x, sheet);
416
+ }
417
+ return sheet;
418
+ });
419
+ }
420
+ addStylesTo(target) {
421
+ const t = normalizeStyleTarget(target);
422
+ t.adoptedStyleSheets = [...t.adoptedStyleSheets, ...this.sheets];
423
+ }
424
+ removeStylesFrom(target) {
425
+ const t = normalizeStyleTarget(target);
426
+ const sheets = this.sheets;
427
+ t.adoptedStyleSheets = t.adoptedStyleSheets.filter((x) => sheets.indexOf(x) === -1);
428
+ }
429
+ }
430
+ AdoptedStyleSheetsStrategy.styleSheetCache = new Map();
431
+ let id = 0;
432
+ const nextStyleId = () => `fast-${++id}`;
433
+ function usableStyleTarget(target) {
434
+ return target === document ? document.body : target;
435
+ }
436
+ /**
437
+ * @internal
438
+ */
439
+ export class StyleElementStrategy {
440
+ constructor(styles) {
441
+ this.styles = styles;
442
+ this.styleClass = nextStyleId();
443
+ }
444
+ addStylesTo(target) {
445
+ target = usableStyleTarget(normalizeStyleTarget(target));
446
+ const styles = this.styles;
447
+ const styleClass = this.styleClass;
448
+ for (let i = 0; i < styles.length; i++) {
449
+ const element = document.createElement("style");
450
+ element.innerHTML = styles[i];
451
+ element.className = styleClass;
452
+ target.append(element);
453
+ }
454
+ }
455
+ removeStylesFrom(target) {
456
+ target = usableStyleTarget(normalizeStyleTarget(target));
457
+ const styles = target.querySelectorAll(`.${this.styleClass}`);
458
+ styles[0].parentNode;
459
+ for (let i = 0, ii = styles.length; i < ii; ++i) {
460
+ target.removeChild(styles[i]);
461
+ }
386
462
  }
387
463
  }
464
+ ElementStyles.setDefaultStrategy(ElementStyles.supportsAdoptedStyleSheets
465
+ ? AdoptedStyleSheetsStrategy
466
+ : StyleElementStrategy);
@@ -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
  }