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