@bodil/dom 0.1.8 → 0.1.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 (48) hide show
  1. package/dist/component.d.ts +218 -6
  2. package/dist/component.js +154 -13
  3. package/dist/component.js.map +1 -1
  4. package/dist/css.d.ts +4 -0
  5. package/dist/css.js +4 -0
  6. package/dist/css.js.map +1 -1
  7. package/dist/decorators/attribute.js +1 -1
  8. package/dist/decorators/attribute.js.map +1 -1
  9. package/dist/decorators/connect.test.js +1 -1
  10. package/dist/decorators/connect.test.js.map +1 -1
  11. package/dist/decorators/reactive.d.ts +1 -1
  12. package/dist/decorators/reactive.js +1 -1
  13. package/dist/decorators/reactive.js.map +1 -1
  14. package/dist/decorators/reactive.test.js +1 -1
  15. package/dist/decorators/reactive.test.js.map +1 -1
  16. package/dist/dom.d.ts +32 -0
  17. package/dist/dom.js +32 -0
  18. package/dist/dom.js.map +1 -1
  19. package/dist/emitter.d.ts +8 -0
  20. package/dist/emitter.js.map +1 -1
  21. package/dist/event.d.ts +4 -0
  22. package/dist/event.js.map +1 -1
  23. package/dist/geometry.d.ts +7 -0
  24. package/dist/geometry.js +4 -0
  25. package/dist/geometry.js.map +1 -1
  26. package/dist/index.d.ts +2 -1
  27. package/dist/index.js +2 -1
  28. package/dist/index.js.map +1 -1
  29. package/dist/signal.d.ts +29 -0
  30. package/dist/signal.js +78 -0
  31. package/dist/signal.js.map +1 -0
  32. package/dist/signal.test.d.ts +1 -0
  33. package/dist/signal.test.js +135 -0
  34. package/dist/signal.test.js.map +1 -0
  35. package/package.json +16 -8
  36. package/src/component.ts +237 -20
  37. package/src/css.ts +5 -0
  38. package/src/decorators/attribute.ts +1 -1
  39. package/src/decorators/connect.test.ts +1 -1
  40. package/src/decorators/reactive.test.ts +1 -1
  41. package/src/decorators/reactive.ts +4 -4
  42. package/src/dom.ts +33 -0
  43. package/src/emitter.ts +8 -0
  44. package/src/event.ts +4 -0
  45. package/src/geometry.ts +8 -0
  46. package/src/index.ts +2 -1
  47. package/src/signal.test.ts +89 -0
  48. package/src/signal.ts +109 -0
@@ -1,3 +1,7 @@
1
+ /**
2
+ * Web component base class.
3
+ * @module
4
+ */
1
5
  import { type Disposifiable } from "@bodil/core/disposable";
2
6
  import { Signal } from "@bodil/signal";
3
7
  import { type CSSResult, type CSSResultOrNative, type ReactiveController, type ReactiveControllerHost, type RenderOptions } from "lit";
@@ -21,15 +25,51 @@ export type Deps = Array<typeof HTMLElement>;
21
25
  export type CSSStyleSpec = CSSResult | CSSStyleSheet | string;
22
26
  export type CSSStyleSpecArray = Array<CSSStyleSpec | CSSStyleSpecArray>;
23
27
  export type CSSStyleSpecDeclaration = CSSStyleSpec | CSSStyleSpecArray;
28
+ /**
29
+ * Error thrown when a {@link Component.render} method causes infinite
30
+ * re-renders.
31
+ */
24
32
  export declare class ComponentUpdateLoopError extends Error {
25
33
  constructor(message?: string, options?: ErrorOptions);
26
34
  }
35
+ /**
36
+ * Base class for web components.
37
+ */
27
38
  export declare abstract class Component extends EmitterElement implements ReactiveControllerHost, Disposable {
28
39
  #private;
40
+ /**
41
+ * Declare the web components this element will be using.
42
+ *
43
+ * This is a convenient place to make sure these web components will be
44
+ * defined when your component is rendered. It has no effect otherwise.
45
+ *
46
+ * @example
47
+ * class MyComponent extends Component {
48
+ * static deps: Deps = [ MySubcomponent, MyOtherSubcomponent ];
49
+ * }
50
+ */
29
51
  static deps: Deps;
52
+ /**
53
+ * Declare CSS style sheets which will be installed for this web component.
54
+ *
55
+ * Style sheets can be either {@link CSSStyleSheet} objects,
56
+ * Lit template {@link CSSResult}s, or strings.
57
+ *
58
+ * If a superclass also declares the `styles` property, this list will be
59
+ * appended to the superclass's list.
60
+ *
61
+ * @example
62
+ * class MyComponent extends Component {
63
+ * static styles = css`
64
+ * :host {
65
+ * border: 2px solid red;
66
+ * }
67
+ * `;
68
+ * }
69
+ */
30
70
  static styles?: CSSStyleSpecDeclaration;
31
71
  static shadowRootOptions: ShadowRootInit;
32
- static initialisers: Set<() => void>;
72
+ private static initialisers;
33
73
  /** @ignore */
34
74
  protected static attributeConfig: Map<string, AttributeConfig>;
35
75
  /** @ignore */
@@ -40,33 +80,155 @@ export declare abstract class Component extends EmitterElement implements Reacti
40
80
  renderOptions: RenderOptions;
41
81
  emits: object;
42
82
  readonly renderRoot: ShadowRoot | HTMLElement;
83
+ /**
84
+ * True if this component had scheduled an update which has not yet started.
85
+ */
43
86
  get isUpdatePending(): boolean;
44
87
  get updateComplete(): Promise<boolean>;
88
+ /**
89
+ * A {@link Promise} which resolves when this component has completed its
90
+ * first update and considers itself stabilised, according to the
91
+ * {@link Component.stabilise} method.
92
+ *
93
+ * By default, a component considers itself stabilised when all of its
94
+ * children which are also {@link Component}s are reporting as stabilised.
95
+ */
45
96
  get hasStabilised(): Promise<void>;
97
+ /**
98
+ * An {@link AbortSignal} which will trigger when this component is
99
+ * disconnected from the DOM.
100
+ *
101
+ * This can be passed along to asynchronous tasks such as {@link fetch}
102
+ * initiated by this component, which will then be aborted automatically if
103
+ * the component is removed from the DOM.
104
+ */
46
105
  get abortSignal(): AbortSignal;
47
- static addInitialiser(init: () => void): void;
48
- static get observedAttributes(): Array<string>;
106
+ /**
107
+ * Register a function which will be executed whenever an instance of this
108
+ * class is constructed.
109
+ */
110
+ static addInitialiser(init: (this: Component) => void): void;
111
+ /**
112
+ * A list of the attributes this element has declared.
113
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_custom_elements#responding_to_attribute_changes
114
+ * @internal
115
+ */
116
+ static get observedAttributes(): ReadonlyArray<string>;
49
117
  private static finalise;
118
+ /** @ignore */
50
119
  constructor();
120
+ /**
121
+ * This lifecycle callback is called when the component is attached to the
122
+ * DOM.
123
+ *
124
+ * Generally, prefer using methods with @{@link connect} decorators to
125
+ * overriding this method.
126
+ */
51
127
  protected connectedCallback(): void;
128
+ /**
129
+ * This lifecycle callback is called when the component is removed from the
130
+ * DOM.
131
+ *
132
+ * Generally, prefer using methods with @{@link connect} decorators to
133
+ * overriding this method.
134
+ */
52
135
  protected disconnectedCallback(): void;
136
+ /**
137
+ * @internal
138
+ */
53
139
  setAttributeQuietly(name: string, value: string | null): void;
140
+ /**
141
+ * @internal
142
+ */
54
143
  protected attributeChangedCallback(name: string, old: string | null, valueString: string | null): void;
55
- $signal<K extends keyof this & string, V extends this[K]>(prop: K): Signal<V>;
144
+ /**
145
+ * Typedoc does *not* like this type signature. TODO figure out how to
146
+ * document it properly.
147
+ * @ignore
148
+ */
149
+ $signal<K extends keyof this & string, V extends this[K]>(prop: K): Signal.Computed<V>;
56
150
  addController(controller: ReactiveController): void;
57
151
  removeController(controller: ReactiveController): void;
152
+ /**
153
+ * Create the component's render root.
154
+ *
155
+ * By default, this creates a {@link ShadowRoot} and attaches it to the
156
+ * component.
157
+ *
158
+ * If you don't want to use a shadow DOM, you can override this method to
159
+ * just `return this`, which causes the component's contents to render as
160
+ * direct children of the component itself.
161
+ */
58
162
  protected createRenderRoot(): HTMLElement | ShadowRoot;
163
+ /**
164
+ * Ask this component to update itself.
165
+ */
59
166
  requestUpdate(opts?: UpdateConfig): void;
167
+ /**
168
+ * @internal
169
+ */
60
170
  hasRequiredProperties(): Signal.Computed<boolean>;
171
+ /** @internal */
61
172
  protected performUpdate(): Promise<void>;
173
+ /** @internal */
62
174
  protected update(): void;
175
+ /**
176
+ * Return an iterator over this component's children which are also
177
+ * {@link Component}s.
178
+ */
63
179
  protected findChildComponents(root?: Element | ShadowRoot): IteratorObject<Component>;
180
+ /**
181
+ * Return a {@link Promise} which resolves when this component considers
182
+ * itself to have stabilised.
183
+ *
184
+ * The default implementation waits for any children which are also
185
+ * {@link Component}s to report that they have also stabilised.
186
+ */
64
187
  protected stabilise(): Promise<void>;
188
+ /**
189
+ * This lifecycle callback is called each time an update has completed.
190
+ */
65
191
  protected updated(): void;
192
+ /**
193
+ * This lifecycle callback is called after the component's first update has
194
+ * completed, and before {@link Component.updated}.
195
+ */
66
196
  protected firstUpdated(): void;
197
+ /**
198
+ * This lifecycle callback is called when the component considers itself
199
+ * stabilised after its first update.
200
+ *
201
+ * @see {@link Component.hasStabilised}
202
+ */
67
203
  protected stabilised(): void;
204
+ /**
205
+ * This lifecycle callback is called every time a property decorated with
206
+ * the @{@link require} decorator has changed, but only when every property
207
+ * marked as such is not `undefined`.
208
+ */
68
209
  protected initialised(): void;
210
+ /**
211
+ * This lifecycle callback is called the first time every property decorated
212
+ * with @{@link require} has been defined, and before
213
+ * {@link Component.initialised}.
214
+ */
69
215
  protected firstInitialised(): void;
216
+ /**
217
+ * Render the component's contents.
218
+ *
219
+ * This function should return a Lit [renderable
220
+ * value](https://lit.dev/docs/templates/expressions/#child-expressions),
221
+ * usually an {@link html} template.
222
+ *
223
+ * @example
224
+ * class MyComponent extends Component {
225
+ * protected override render() {
226
+ * return html`
227
+ * <h1>Hello Joe!</h1>
228
+ * `;
229
+ * }
230
+ * }
231
+ */
70
232
  protected render(): unknown;
71
233
  [Symbol.dispose](): void;
72
234
  /**
@@ -102,18 +264,70 @@ export declare abstract class Component extends EmitterElement implements Reacti
102
264
  * element. Otherwise, return null.
103
265
  */
104
266
  getFocusedElement(): Element | null;
267
+ /**
268
+ * Find the first element in the component's {@link Component.renderRoot}
269
+ * matching the provided CSS selector.
270
+ *
271
+ * If you need to query the component's direct child elements instead, use
272
+ * {@link Component.querySlot}.
273
+ *
274
+ * If you were looking for Lit's `@query` decorator, use this as a getter
275
+ * instead, as in the example below.
276
+ *
277
+ * @example
278
+ * class MyComponent extends Component {
279
+ * get button(): HTMLButtonElement {
280
+ * return this.query("button");
281
+ * }
282
+ *
283
+ * protected override render() {
284
+ * return html`<button>I am a button</button>`;
285
+ * }
286
+ * }
287
+ */
105
288
  query<El extends keyof HTMLElementTagNameMap>(selector: El): HTMLElementTagNameMap[El] | null;
106
289
  query<El extends keyof SVGElementTagNameMap>(selector: El): SVGElementTagNameMap[El] | null;
107
290
  query<El extends keyof MathMLElementTagNameMap>(selector: El): MathMLElementTagNameMap[El] | null;
108
291
  /** @deprecated */
109
292
  query<El extends keyof HTMLElementDeprecatedTagNameMap>(selector: El): HTMLElementDeprecatedTagNameMap[El] | null;
110
293
  query(selector: string): Element | null;
294
+ /**
295
+ * Find all elements in the component's {@link Component.renderRoot}
296
+ * matching the provided CSS selector.
297
+ *
298
+ * If you need to query the component's direct child elements instead, use
299
+ * {@link Component.querySlot}.
300
+ *
301
+ * If you were looking for Lit's `@queryAll` decorator, use this as a getter
302
+ * instead, as in the example below.
303
+ *
304
+ * @example
305
+ * class MyComponent extends Component {
306
+ * get buttons(): NodeListOf<HTMLButtonElement> {
307
+ * return this.queryAll("button");
308
+ * }
309
+ *
310
+ * protected override render() {
311
+ * return html`
312
+ * <button>I am a button</button>
313
+ * <button>I am also a button</button>
314
+ * `;
315
+ * }
316
+ * }
317
+ */
111
318
  queryAll<El extends keyof HTMLElementTagNameMap>(selector: El): NodeListOf<HTMLElementTagNameMap[El]>;
112
319
  queryAll<El extends keyof SVGElementTagNameMap>(selector: El): NodeListOf<SVGElementTagNameMap[El]>;
113
320
  queryAll<El extends keyof MathMLElementTagNameMap>(selector: El): NodeListOf<MathMLElementTagNameMap[El]>;
114
321
  /** @deprecated */
115
322
  queryAll<El extends keyof HTMLElementDeprecatedTagNameMap>(selector: El): NodeListOf<HTMLElementDeprecatedTagNameMap[El]>;
116
323
  queryAll(selector: string): NodeListOf<Element>;
324
+ /**
325
+ * Find the elements attached to a given slot on this component, according
326
+ * to the provided {@link QuerySlotOptions}.
327
+ *
328
+ * If you include `reactive: true` in your query, the result will be a
329
+ * signal which updates with the contents of the slot.
330
+ */
117
331
  querySlot(options: QuerySlotOptions & {
118
332
  nodes: true;
119
333
  reactive: true;
@@ -152,12 +366,10 @@ export declare abstract class Component extends EmitterElement implements Reacti
152
366
  querySlot<El extends keyof MathMLElementTagNameMap>(options: QuerySlotOptions & {
153
367
  selector: El;
154
368
  }): Array<MathMLElementTagNameMap[El]>;
155
- /** @deprecated */
156
369
  querySlot<El extends keyof HTMLElementDeprecatedTagNameMap>(options: QuerySlotOptions & {
157
370
  reactive: true;
158
371
  selector: El;
159
372
  }): Signal.Computed<Array<HTMLElementDeprecatedTagNameMap[El]>>;
160
- /** @deprecated */
161
373
  querySlot<El extends keyof HTMLElementDeprecatedTagNameMap>(options: QuerySlotOptions & {
162
374
  selector: El;
163
375
  }): Array<HTMLElementDeprecatedTagNameMap[El]>;
package/dist/component.js CHANGED
@@ -1,3 +1,7 @@
1
+ /**
2
+ * Web component base class.
3
+ * @module
4
+ */
1
5
  var _a;
2
6
  import { isIterable, isNullish } from "@bodil/core/assert";
3
7
  import { DisposableContext, toDisposable } from "@bodil/core/disposable";
@@ -22,13 +26,31 @@ const finalised = Symbol("finalised");
22
26
  function processCSSStyleSpec(spec) {
23
27
  return getCompatibleStyle(typeof spec === "string" ? unsafeCSS(spec) : spec);
24
28
  }
29
+ /**
30
+ * Error thrown when a {@link Component.render} method causes infinite
31
+ * re-renders.
32
+ */
25
33
  export class ComponentUpdateLoopError extends Error {
26
34
  constructor(message, options) {
27
35
  super(message, options);
28
36
  this.name = "ComponentUpdateLoopError";
29
37
  }
30
38
  }
39
+ /**
40
+ * Base class for web components.
41
+ */
31
42
  export class Component extends EmitterElement {
43
+ /**
44
+ * Declare the web components this element will be using.
45
+ *
46
+ * This is a convenient place to make sure these web components will be
47
+ * defined when your component is rendered. It has no effect otherwise.
48
+ *
49
+ * @example
50
+ * class MyComponent extends Component {
51
+ * static deps: Deps = [ MySubcomponent, MyOtherSubcomponent ];
52
+ * }
53
+ */
32
54
  static { this.deps = []; }
33
55
  static { this.shadowRootOptions = { mode: "open" }; }
34
56
  static { this.initialisers = new Set(); }
@@ -52,25 +74,53 @@ export class Component extends EmitterElement {
52
74
  #initialised;
53
75
  #viewTransitionRequested;
54
76
  #ignoreAttributeUpdates;
77
+ /**
78
+ * True if this component had scheduled an update which has not yet started.
79
+ */
55
80
  get isUpdatePending() {
56
81
  return this.#isUpdatePending;
57
82
  }
58
83
  get updateComplete() {
59
84
  return this.#updateResult.promise;
60
85
  }
86
+ /**
87
+ * A {@link Promise} which resolves when this component has completed its
88
+ * first update and considers itself stabilised, according to the
89
+ * {@link Component.stabilise} method.
90
+ *
91
+ * By default, a component considers itself stabilised when all of its
92
+ * children which are also {@link Component}s are reporting as stabilised.
93
+ */
61
94
  get hasStabilised() {
62
95
  return this.#stabilised.promise;
63
96
  }
64
- #abortController;
97
+ /**
98
+ * An {@link AbortSignal} which will trigger when this component is
99
+ * disconnected from the DOM.
100
+ *
101
+ * This can be passed along to asynchronous tasks such as {@link fetch}
102
+ * initiated by this component, which will then be aborted automatically if
103
+ * the component is removed from the DOM.
104
+ */
65
105
  get abortSignal() {
66
106
  return this.#abortController.signal;
67
107
  }
108
+ #abortController;
109
+ /**
110
+ * Register a function which will be executed whenever an instance of this
111
+ * class is constructed.
112
+ */
68
113
  static addInitialiser(init) {
69
114
  if (!Object.hasOwn(this, "initialisers")) {
70
115
  this.initialisers = new Set();
71
116
  }
72
117
  this.initialisers.add(init);
73
118
  }
119
+ /**
120
+ * A list of the attributes this element has declared.
121
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_custom_elements#responding_to_attribute_changes
122
+ * @internal
123
+ */
74
124
  static get observedAttributes() {
75
125
  this.finalise();
76
126
  return this.attributeConfig.keys().toArray();
@@ -106,6 +156,7 @@ export class Component extends EmitterElement {
106
156
  }
107
157
  }
108
158
  }
159
+ /** @ignore */
109
160
  constructor() {
110
161
  super();
111
162
  this.renderOptions = { host: this };
@@ -125,7 +176,7 @@ export class Component extends EmitterElement {
125
176
  // set up reactive fields, because decorators can't do this themselves
126
177
  const fields = this.constructor[Symbol.metadata]?.[reactiveFields] ?? [];
127
178
  for (const field of fields) {
128
- const sig = signalForObject(this, field, () => Signal(undefined, { equals: Object.is }));
179
+ const sig = signalForObject(this, field, () => Signal.from(undefined, { equals: Object.is }));
129
180
  Object.defineProperty(this, field, {
130
181
  get() {
131
182
  return sig.get();
@@ -160,6 +211,13 @@ export class Component extends EmitterElement {
160
211
  });
161
212
  }
162
213
  }
214
+ /**
215
+ * This lifecycle callback is called when the component is attached to the
216
+ * DOM.
217
+ *
218
+ * Generally, prefer using methods with @{@link connect} decorators to
219
+ * overriding this method.
220
+ */
163
221
  connectedCallback() {
164
222
  this.#connectedContext.dispose();
165
223
  this.#controllers.forEach((c) => c.hostConnected?.());
@@ -185,6 +243,13 @@ export class Component extends EmitterElement {
185
243
  }));
186
244
  this.requestUpdate();
187
245
  }
246
+ /**
247
+ * This lifecycle callback is called when the component is removed from the
248
+ * DOM.
249
+ *
250
+ * Generally, prefer using methods with @{@link connect} decorators to
251
+ * overriding this method.
252
+ */
188
253
  disconnectedCallback() {
189
254
  if (this.#updateSignal !== undefined) {
190
255
  this.#updateSignalWatcher.unwatch(this.#updateSignal);
@@ -197,6 +262,9 @@ export class Component extends EmitterElement {
197
262
  this.#controllers.forEach((c) => c.hostDisconnected?.());
198
263
  this.#connectedContext.dispose();
199
264
  }
265
+ /**
266
+ * @internal
267
+ */
200
268
  setAttributeQuietly(name, value) {
201
269
  this.#ignoreAttributeUpdates++;
202
270
  if (value === null) {
@@ -207,6 +275,9 @@ export class Component extends EmitterElement {
207
275
  }
208
276
  this.#ignoreAttributeUpdates--;
209
277
  }
278
+ /**
279
+ * @internal
280
+ */
210
281
  attributeChangedCallback(name, old, valueString) {
211
282
  if (this.#ignoreAttributeUpdates > 0 || old === valueString) {
212
283
  return;
@@ -217,6 +288,11 @@ export class Component extends EmitterElement {
217
288
  this[attrConfig.property] = value;
218
289
  }
219
290
  }
291
+ /**
292
+ * Typedoc does *not* like this type signature. TODO figure out how to
293
+ * document it properly.
294
+ * @ignore
295
+ */
220
296
  $signal(prop) {
221
297
  const _value = this[prop];
222
298
  return signalForObject(this, prop, () => {
@@ -232,6 +308,16 @@ export class Component extends EmitterElement {
232
308
  removeController(controller) {
233
309
  this.#controllers.delete(controller);
234
310
  }
311
+ /**
312
+ * Create the component's render root.
313
+ *
314
+ * By default, this creates a {@link ShadowRoot} and attaches it to the
315
+ * component.
316
+ *
317
+ * If you don't want to use a shadow DOM, you can override this method to
318
+ * just `return this`, which causes the component's contents to render as
319
+ * direct children of the component itself.
320
+ */
235
321
  createRenderRoot() {
236
322
  const renderRoot = this.shadowRoot ??
237
323
  this.attachShadow(this.constructor.shadowRootOptions);
@@ -239,6 +325,9 @@ export class Component extends EmitterElement {
239
325
  this.renderOptions.renderBefore ??= renderRoot.firstChild;
240
326
  return renderRoot;
241
327
  }
328
+ /**
329
+ * Ask this component to update itself.
330
+ */
242
331
  requestUpdate(opts) {
243
332
  this.#dirty = true;
244
333
  if (opts?.viewTransition === true) {
@@ -250,7 +339,9 @@ export class Component extends EmitterElement {
250
339
  scheduler(this);
251
340
  }
252
341
  }
253
- #hasRequiredProperties;
342
+ /**
343
+ * @internal
344
+ */
254
345
  hasRequiredProperties() {
255
346
  if (this.#hasRequiredProperties === undefined) {
256
347
  const requiredProperties = this.constructor.requiredProperties;
@@ -267,6 +358,8 @@ export class Component extends EmitterElement {
267
358
  }
268
359
  return this.#hasRequiredProperties;
269
360
  }
361
+ #hasRequiredProperties;
362
+ /** @internal */
270
363
  async performUpdate() {
271
364
  if (!this.isConnected || !this.isUpdatePending) {
272
365
  return;
@@ -320,6 +413,7 @@ export class Component extends EmitterElement {
320
413
  }
321
414
  }
322
415
  }
416
+ /** @internal */
323
417
  update() {
324
418
  if (this.#updateSignal !== undefined) {
325
419
  this.#updateSignalWatcher.unwatch(this.#updateSignal);
@@ -331,6 +425,10 @@ export class Component extends EmitterElement {
331
425
  this.#updateSignalWatcher.watch(this.#updateSignal);
332
426
  this.#updateSignal.get();
333
427
  }
428
+ /**
429
+ * Return an iterator over this component's children which are also
430
+ * {@link Component}s.
431
+ */
334
432
  findChildComponents(root = this.renderRoot) {
335
433
  let all = childElements(root).toArray();
336
434
  const top = [...all];
@@ -339,30 +437,73 @@ export class Component extends EmitterElement {
339
437
  }
340
438
  return Iterator.from(all).filter((el) => el instanceof Component);
341
439
  }
440
+ /**
441
+ * Return a {@link Promise} which resolves when this component considers
442
+ * itself to have stabilised.
443
+ *
444
+ * The default implementation waits for any children which are also
445
+ * {@link Component}s to report that they have also stabilised.
446
+ */
342
447
  async stabilise() {
343
448
  // wait for children to stabilise
344
449
  const children = this.findChildComponents();
345
450
  // eslint-disable-next-line no-console
346
451
  await Promise.all(children.map((child) => child.hasStabilised)).catch(console.error);
347
452
  }
453
+ /**
454
+ * This lifecycle callback is called each time an update has completed.
455
+ */
348
456
  updated() {
349
- // called when an update has completed
457
+ //
350
458
  }
459
+ /**
460
+ * This lifecycle callback is called after the component's first update has
461
+ * completed, and before {@link Component.updated}.
462
+ */
351
463
  firstUpdated() {
352
- // called when the first update has completed, and before
353
- // {@link Component.updated}.
464
+ //
354
465
  }
466
+ /**
467
+ * This lifecycle callback is called when the component considers itself
468
+ * stabilised after its first update.
469
+ *
470
+ * @see {@link Component.hasStabilised}
471
+ */
355
472
  stabilised() {
356
- // called when the component has stabilised after its first update
473
+ //
357
474
  }
475
+ /**
476
+ * This lifecycle callback is called every time a property decorated with
477
+ * the @{@link require} decorator has changed, but only when every property
478
+ * marked as such is not `undefined`.
479
+ */
358
480
  initialised() {
359
- // called when a property marked `@require` has changed and every
360
- // such property is non-`undefined`.
481
+ //
361
482
  }
483
+ /**
484
+ * This lifecycle callback is called the first time every property decorated
485
+ * with @{@link require} has been defined, and before
486
+ * {@link Component.initialised}.
487
+ */
362
488
  firstInitialised() {
363
- // called the first time all properties marked `@require` become
364
- // defined, and before {@link Component.initialised}.
489
+ //
365
490
  }
491
+ /**
492
+ * Render the component's contents.
493
+ *
494
+ * This function should return a Lit [renderable
495
+ * value](https://lit.dev/docs/templates/expressions/#child-expressions),
496
+ * usually an {@link html} template.
497
+ *
498
+ * @example
499
+ * class MyComponent extends Component {
500
+ * protected override render() {
501
+ * return html`
502
+ * <h1>Hello Joe!</h1>
503
+ * `;
504
+ * }
505
+ * }
506
+ */
366
507
  render() {
367
508
  return nothing;
368
509
  }
@@ -425,9 +566,9 @@ export class Component extends EmitterElement {
425
566
  return query(slotEl);
426
567
  }
427
568
  const sig = getOrSetSignal(this, query, () => {
428
- const sig = Signal(query(slotEl), { equals: listsEqual });
569
+ const sig = Signal.from(query(slotEl), { equals: listsEqual });
429
570
  this.addController(new SlotChangeController(this, slot, sig, query));
430
- return sig.readOnly();
571
+ return sig.readOnly;
431
572
  });
432
573
  return sig;
433
574
  }