@bodil/dom 0.1.10 → 0.2.1

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.
@@ -11,7 +11,6 @@ import { EmitterElement } from "./emitter";
11
11
  import { type QuerySlotOptions } from "./slot";
12
12
  export type { ReactiveController, ReactiveControllerHost } from "lit";
13
13
  export { attribute, attributeGetter, attributeSetter, type AttributeOptions, type AttributeGetterSetterOptions, type AttributeType, } from "./decorators/attribute";
14
- export { connect, connectEffect, type ConnectFunction, type ConnectFunctionReturnValue, } from "./decorators/connect";
15
14
  export { reactive } from "./decorators/reactive";
16
15
  export { require } from "./decorators/require";
17
16
  export { EmitterElement } from "./emitter";
@@ -21,6 +20,7 @@ export type UpdateConfig = {
21
20
  viewTransition?: boolean;
22
21
  };
23
22
  declare const finalised: unique symbol;
23
+ export type Disposables = Disposifiable | Iterable<Disposifiable | undefined> | undefined | void;
24
24
  export type Deps = Array<typeof HTMLElement>;
25
25
  export type CSSStyleSpec = CSSResult | CSSStyleSheet | string;
26
26
  export type CSSStyleSpecArray = Array<CSSStyleSpec | CSSStyleSpecArray>;
@@ -47,6 +47,8 @@ export declare abstract class Component extends EmitterElement implements Reacti
47
47
  * class MyComponent extends Component {
48
48
  * static deps: Deps = [ MySubcomponent, MyOtherSubcomponent ];
49
49
  * }
50
+ *
51
+ * @category Declarations
50
52
  */
51
53
  static deps: Deps;
52
54
  /**
@@ -66,8 +68,13 @@ export declare abstract class Component extends EmitterElement implements Reacti
66
68
  * }
67
69
  * `;
68
70
  * }
71
+ *
72
+ * @category Declarations
69
73
  */
70
74
  static styles?: CSSStyleSpecDeclaration;
75
+ /**
76
+ * @category Advanced
77
+ */
71
78
  static shadowRootOptions: ShadowRootInit;
72
79
  private static initialisers;
73
80
  /** @ignore */
@@ -77,13 +84,28 @@ export declare abstract class Component extends EmitterElement implements Reacti
77
84
  /** @ignore */
78
85
  protected static requiredProperties: Set<string | symbol>;
79
86
  private static [finalised];
87
+ /**
88
+ * @category Advanced
89
+ */
80
90
  renderOptions: RenderOptions;
91
+ /**
92
+ * {@inheritDoc EmitterElement.emits}
93
+ * @category Declarations
94
+ */
81
95
  emits: object;
96
+ /**
97
+ * @category Advanced
98
+ */
82
99
  readonly renderRoot: ShadowRoot | HTMLElement;
83
100
  /**
84
101
  * True if this component had scheduled an update which has not yet started.
102
+ *
103
+ * @category Render
85
104
  */
86
105
  get isUpdatePending(): boolean;
106
+ /**
107
+ * @category Render
108
+ */
87
109
  get updateComplete(): Promise<boolean>;
88
110
  /**
89
111
  * A {@link Promise} which resolves when this component has completed its
@@ -92,6 +114,8 @@ export declare abstract class Component extends EmitterElement implements Reacti
92
114
  *
93
115
  * By default, a component considers itself stabilised when all of its
94
116
  * children which are also {@link Component}s are reporting as stabilised.
117
+ *
118
+ * @category Render
95
119
  */
96
120
  get hasStabilised(): Promise<void>;
97
121
  /**
@@ -101,11 +125,15 @@ export declare abstract class Component extends EmitterElement implements Reacti
101
125
  * This can be passed along to asynchronous tasks such as {@link fetch}
102
126
  * initiated by this component, which will then be aborted automatically if
103
127
  * the component is removed from the DOM.
128
+ *
129
+ * @category Advanced
104
130
  */
105
131
  get abortSignal(): AbortSignal;
106
132
  /**
107
133
  * Register a function which will be executed whenever an instance of this
108
134
  * class is constructed.
135
+ *
136
+ * @category Advanced
109
137
  */
110
138
  static addInitialiser(init: (this: Component) => void): void;
111
139
  /**
@@ -117,22 +145,8 @@ export declare abstract class Component extends EmitterElement implements Reacti
117
145
  private static finalise;
118
146
  /** @ignore */
119
147
  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
- */
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
- */
135
- protected disconnectedCallback(): void;
148
+ private connectedCallback;
149
+ private disconnectedCallback;
136
150
  /**
137
151
  * @internal
138
152
  */
@@ -147,7 +161,16 @@ export declare abstract class Component extends EmitterElement implements Reacti
147
161
  * @ignore
148
162
  */
149
163
  $signal<K extends keyof this & string, V extends this[K]>(prop: K): Signal.Computed<V>;
164
+ /**
165
+ * Adds a controller to the host, which sets up the controller's lifecycle
166
+ * methods to be called with the host's lifecycle.
167
+ * @category Advanced
168
+ */
150
169
  addController(controller: ReactiveController): void;
170
+ /**
171
+ * Removes a controller from the host.
172
+ * @category Advanced
173
+ */
151
174
  removeController(controller: ReactiveController): void;
152
175
  /**
153
176
  * Create the component's render root.
@@ -158,10 +181,20 @@ export declare abstract class Component extends EmitterElement implements Reacti
158
181
  * If you don't want to use a shadow DOM, you can override this method to
159
182
  * just `return this`, which causes the component's contents to render as
160
183
  * direct children of the component itself.
184
+ *
185
+ * @category Advanced
161
186
  */
162
187
  protected createRenderRoot(): HTMLElement | ShadowRoot;
188
+ /**
189
+ * Add a style sheet to the component's shadow root.
190
+ *
191
+ * @category Advanced
192
+ */
193
+ addStyleSheet(spec: CSSStyleSpecDeclaration): void;
163
194
  /**
164
195
  * Ask this component to update itself.
196
+ *
197
+ * @category Render
165
198
  */
166
199
  requestUpdate(opts?: UpdateConfig): void;
167
200
  /**
@@ -175,6 +208,8 @@ export declare abstract class Component extends EmitterElement implements Reacti
175
208
  /**
176
209
  * Return an iterator over this component's children which are also
177
210
  * {@link Component}s.
211
+ *
212
+ * @category Queries
178
213
  */
179
214
  protected findChildComponents(root?: Element | ShadowRoot): IteratorObject<Component>;
180
215
  /**
@@ -183,15 +218,31 @@ export declare abstract class Component extends EmitterElement implements Reacti
183
218
  *
184
219
  * The default implementation waits for any children which are also
185
220
  * {@link Component}s to report that they have also stabilised.
221
+ *
222
+ * @category Render
186
223
  */
187
224
  protected stabilise(): Promise<void>;
225
+ /**
226
+ * This lifecycle callback is called each time this component is connected
227
+ * to the DOM.
228
+ * @category Lifecycle
229
+ */
230
+ protected connected(): void;
231
+ /**
232
+ * This lifecycle callback is called each time this component is
233
+ * disconnected from the DOM.
234
+ * @category Lifecycle
235
+ */
236
+ protected disconnected(): void;
188
237
  /**
189
238
  * This lifecycle callback is called each time an update has completed.
239
+ * @category Lifecycle
190
240
  */
191
241
  protected updated(): void;
192
242
  /**
193
243
  * This lifecycle callback is called after the component's first update has
194
244
  * completed, and before {@link Component.updated}.
245
+ * @category Lifecycle
195
246
  */
196
247
  protected firstUpdated(): void;
197
248
  /**
@@ -199,18 +250,21 @@ export declare abstract class Component extends EmitterElement implements Reacti
199
250
  * stabilised after its first update.
200
251
  *
201
252
  * @see {@link Component.hasStabilised}
253
+ * @category Lifecycle
202
254
  */
203
255
  protected stabilised(): void;
204
256
  /**
205
257
  * This lifecycle callback is called every time a property decorated with
206
258
  * the @{@link require} decorator has changed, but only when every property
207
259
  * marked as such is not `undefined`.
260
+ * @category Lifecycle
208
261
  */
209
262
  protected initialised(): void;
210
263
  /**
211
264
  * This lifecycle callback is called the first time every property decorated
212
265
  * with @{@link require} has been defined, and before
213
266
  * {@link Component.initialised}.
267
+ * @category Lifecycle
214
268
  */
215
269
  protected firstInitialised(): void;
216
270
  /**
@@ -228,9 +282,22 @@ export declare abstract class Component extends EmitterElement implements Reacti
228
282
  * `;
229
283
  * }
230
284
  * }
285
+ *
286
+ * @category Render
231
287
  */
232
288
  protected render(): unknown;
289
+ /** @internal */
233
290
  [Symbol.dispose](): void;
291
+ /**
292
+ * Register a {@link Disposable} for automatic disposal when this component
293
+ * is disposed.
294
+ * @category Resources
295
+ */
296
+ use(disposifiable: undefined): undefined;
297
+ use(disposifiable: null): null;
298
+ use(disposifiable: Disposifiable): Disposable;
299
+ use(disposifiable: Disposifiable | undefined): Disposable | undefined;
300
+ use(disposifiable: Disposifiable | null | undefined): Disposable | null | undefined;
234
301
  /**
235
302
  * Attach a {@link Disposable} to the component's connected/disconnected
236
303
  * lifecycle state.
@@ -246,7 +313,9 @@ export declare abstract class Component extends EmitterElement implements Reacti
246
313
  * super.connectedCallback();
247
314
  * this.useWhileConnected(this.on("click", this.handleClick.bind(this)));
248
315
  * }
316
+ * @category Resources
249
317
  */
318
+ useWhileConnected(disposifiable: Disposifiable, secondDisposable: Disposifiable, ...disposifiables: Array<Disposifiable>): Array<Disposable>;
250
319
  useWhileConnected(disposifiable: undefined): undefined;
251
320
  useWhileConnected(disposifiable: null): null;
252
321
  useWhileConnected(disposifiable: Disposifiable): Disposable;
@@ -256,12 +325,14 @@ export declare abstract class Component extends EmitterElement implements Reacti
256
325
  * Attach an event listener to an event on this component.
257
326
  *
258
327
  * @returns A {@link Disposable} to subsequently detach the event listener.
328
+ * @category Events
259
329
  */
260
330
  on<K extends keyof HTMLElementEventMap>(type: K, callback: (event: HTMLElementEventMap[K]) => void, options?: AddEventListenerOptions | boolean): Disposable;
261
331
  /**
262
332
  * If an element that is either inside this element's shadow root or is a
263
333
  * child of this element (ie. slotted) currently has focus, return that
264
334
  * element. Otherwise, return null.
335
+ * @category Queries
265
336
  */
266
337
  getFocusedElement(): Element | null;
267
338
  /**
@@ -284,6 +355,7 @@ export declare abstract class Component extends EmitterElement implements Reacti
284
355
  * return html`<button>I am a button</button>`;
285
356
  * }
286
357
  * }
358
+ * @category Queries
287
359
  */
288
360
  query<El extends keyof HTMLElementTagNameMap>(selector: El): HTMLElementTagNameMap[El] | null;
289
361
  query<El extends keyof SVGElementTagNameMap>(selector: El): SVGElementTagNameMap[El] | null;
@@ -314,6 +386,7 @@ export declare abstract class Component extends EmitterElement implements Reacti
314
386
  * `;
315
387
  * }
316
388
  * }
389
+ * @category Queries
317
390
  */
318
391
  queryAll<El extends keyof HTMLElementTagNameMap>(selector: El): NodeListOf<HTMLElementTagNameMap[El]>;
319
392
  queryAll<El extends keyof SVGElementTagNameMap>(selector: El): NodeListOf<SVGElementTagNameMap[El]>;
@@ -327,6 +400,7 @@ export declare abstract class Component extends EmitterElement implements Reacti
327
400
  *
328
401
  * If you include `reactive: true` in your query, the result will be a
329
402
  * signal which updates with the contents of the slot.
403
+ * @category Queries
330
404
  */
331
405
  querySlot(options: QuerySlotOptions & {
332
406
  nodes: true;
package/dist/component.js CHANGED
@@ -3,21 +3,19 @@
3
3
  * @module
4
4
  */
5
5
  var _a;
6
- import { isIterable, isNullish } from "@bodil/core/assert";
6
+ import { isNullish, present } from "@bodil/core/assert";
7
7
  import { DisposableContext, toDisposable } from "@bodil/core/disposable";
8
8
  import { Signal } from "@bodil/signal";
9
9
  import { adoptStyles, getCompatibleStyle, nothing, render, unsafeCSS, } from "lit";
10
10
  import { attributeConfig, fromAttribute, toAttribute, } from "./decorators/attribute";
11
- import { connectedJobs } from "./decorators/connect";
12
11
  import { reactiveFields, signalForObject } from "./decorators/reactive";
13
12
  import { requiredProperties } from "./decorators/require";
14
- import { childElements } from "./dom";
13
+ import { findDescendants } from "./dom";
15
14
  import { EmitterElement } from "./emitter";
16
15
  import { eventListener } from "./event";
17
16
  import { scheduler } from "./scheduler";
18
17
  import { findSlot, getOrSetQuery, getOrSetSignal, listsEqual, SlotChangeController, } from "./slot";
19
18
  export { attribute, attributeGetter, attributeSetter, } from "./decorators/attribute";
20
- export { connect, connectEffect, } from "./decorators/connect";
21
19
  export { reactive } from "./decorators/reactive";
22
20
  export { require } from "./decorators/require";
23
21
  export { EmitterElement } from "./emitter";
@@ -50,8 +48,13 @@ export class Component extends EmitterElement {
50
48
  * class MyComponent extends Component {
51
49
  * static deps: Deps = [ MySubcomponent, MyOtherSubcomponent ];
52
50
  * }
51
+ *
52
+ * @category Declarations
53
53
  */
54
54
  static { this.deps = []; }
55
+ /**
56
+ * @category Advanced
57
+ */
55
58
  static { this.shadowRootOptions = { mode: "open" }; }
56
59
  static { this.initialisers = new Set(); }
57
60
  /** @ignore */
@@ -63,6 +66,7 @@ export class Component extends EmitterElement {
63
66
  static { this[_a] = true; }
64
67
  #controllers;
65
68
  #connectedContext;
69
+ #finalCleanupContext;
66
70
  #dirty;
67
71
  #isUpdatePending;
68
72
  #updateSignal;
@@ -76,10 +80,15 @@ export class Component extends EmitterElement {
76
80
  #ignoreAttributeUpdates;
77
81
  /**
78
82
  * True if this component had scheduled an update which has not yet started.
83
+ *
84
+ * @category Render
79
85
  */
80
86
  get isUpdatePending() {
81
87
  return this.#isUpdatePending;
82
88
  }
89
+ /**
90
+ * @category Render
91
+ */
83
92
  get updateComplete() {
84
93
  return this.#updateResult.promise;
85
94
  }
@@ -90,6 +99,8 @@ export class Component extends EmitterElement {
90
99
  *
91
100
  * By default, a component considers itself stabilised when all of its
92
101
  * children which are also {@link Component}s are reporting as stabilised.
102
+ *
103
+ * @category Render
93
104
  */
94
105
  get hasStabilised() {
95
106
  return this.#stabilised.promise;
@@ -101,6 +112,8 @@ export class Component extends EmitterElement {
101
112
  * This can be passed along to asynchronous tasks such as {@link fetch}
102
113
  * initiated by this component, which will then be aborted automatically if
103
114
  * the component is removed from the DOM.
115
+ *
116
+ * @category Advanced
104
117
  */
105
118
  get abortSignal() {
106
119
  return this.#abortController.signal;
@@ -109,6 +122,8 @@ export class Component extends EmitterElement {
109
122
  /**
110
123
  * Register a function which will be executed whenever an instance of this
111
124
  * class is constructed.
125
+ *
126
+ * @category Advanced
112
127
  */
113
128
  static addInitialiser(init) {
114
129
  if (!Object.hasOwn(this, "initialisers")) {
@@ -159,9 +174,13 @@ export class Component extends EmitterElement {
159
174
  /** @ignore */
160
175
  constructor() {
161
176
  super();
177
+ /**
178
+ * @category Advanced
179
+ */
162
180
  this.renderOptions = { host: this };
163
181
  this.#controllers = new Set();
164
182
  this.#connectedContext = new DisposableContext();
183
+ this.#finalCleanupContext = new DisposableContext();
165
184
  this.#dirty = false;
166
185
  this.#isUpdatePending = false;
167
186
  this.#updateSignalWatcher = new Signal.subtle.Watcher(() => this.requestUpdate());
@@ -170,6 +189,9 @@ export class Component extends EmitterElement {
170
189
  this.#firstUpdate = false;
171
190
  this.#initialised = false;
172
191
  this.#viewTransitionRequested = false;
192
+ /**
193
+ * @category Advanced
194
+ */
173
195
  this.renderRoot = this.createRenderRoot();
174
196
  this.#abortController = new AbortController();
175
197
  this.#ignoreAttributeUpdates = 1;
@@ -211,26 +233,11 @@ export class Component extends EmitterElement {
211
233
  });
212
234
  }
213
235
  }
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
- */
221
236
  connectedCallback() {
222
237
  this.#connectedContext.dispose();
223
238
  this.#controllers.forEach((c) => c.hostConnected?.());
224
239
  this.#rootPart?.setConnected(true);
225
- for (const job of connectedJobs(this)) {
226
- const result = job.call(this);
227
- const items = isNullish(result)
228
- ? Iterator.from([])
229
- : isIterable(result)
230
- ? Iterator.from(result)
231
- : Iterator.from([result]);
232
- items.filter((i) => i !== undefined).forEach(this.useWhileConnected.bind(this));
233
- }
240
+ this.connected();
234
241
  this.useWhileConnected(Signal.effect(() => {
235
242
  if (this.hasRequiredProperties().get()) {
236
243
  if (!this.#initialised) {
@@ -243,19 +250,13 @@ export class Component extends EmitterElement {
243
250
  }));
244
251
  this.requestUpdate();
245
252
  }
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
- */
253
253
  disconnectedCallback() {
254
254
  if (this.#updateSignal !== undefined) {
255
255
  this.#updateSignalWatcher.unwatch(this.#updateSignal);
256
256
  this.#updateSignal = undefined;
257
257
  }
258
258
  this.#isUpdatePending = false;
259
+ this.disconnected();
259
260
  this.#abortController.abort(new DOMException(`${this.tagName} element disconnected`, "AbortError"));
260
261
  this.#abortController = new AbortController();
261
262
  this.#rootPart?.setConnected(false);
@@ -285,7 +286,11 @@ export class Component extends EmitterElement {
285
286
  const attrConfig = this.constructor.attributeConfig.get(name);
286
287
  if (attrConfig !== undefined) {
287
288
  const value = fromAttribute(valueString, attrConfig.type);
288
- this[attrConfig.property] = value;
289
+ try {
290
+ this[attrConfig.property] = value;
291
+ // Silently swallow any write errors.
292
+ }
293
+ catch (_e) { }
289
294
  }
290
295
  }
291
296
  /**
@@ -299,12 +304,21 @@ export class Component extends EmitterElement {
299
304
  throw new TypeError(`Object has no reactive property ${JSON.stringify(prop)}`);
300
305
  });
301
306
  }
307
+ /**
308
+ * Adds a controller to the host, which sets up the controller's lifecycle
309
+ * methods to be called with the host's lifecycle.
310
+ * @category Advanced
311
+ */
302
312
  addController(controller) {
303
313
  this.#controllers.add(controller);
304
314
  if (this.renderRoot !== undefined && this.isConnected) {
305
315
  controller.hostConnected?.();
306
316
  }
307
317
  }
318
+ /**
319
+ * Removes a controller from the host.
320
+ * @category Advanced
321
+ */
308
322
  removeController(controller) {
309
323
  this.#controllers.delete(controller);
310
324
  }
@@ -317,6 +331,8 @@ export class Component extends EmitterElement {
317
331
  * If you don't want to use a shadow DOM, you can override this method to
318
332
  * just `return this`, which causes the component's contents to render as
319
333
  * direct children of the component itself.
334
+ *
335
+ * @category Advanced
320
336
  */
321
337
  createRenderRoot() {
322
338
  const renderRoot = this.shadowRoot ??
@@ -325,8 +341,24 @@ export class Component extends EmitterElement {
325
341
  this.renderOptions.renderBefore ??= renderRoot.firstChild;
326
342
  return renderRoot;
327
343
  }
344
+ /**
345
+ * Add a style sheet to the component's shadow root.
346
+ *
347
+ * @category Advanced
348
+ */
349
+ addStyleSheet(spec) {
350
+ if (!(this.renderRoot instanceof ShadowRoot)) {
351
+ throw new Error("Component needs a shadow root in order to add a style sheet");
352
+ }
353
+ const sheets = (Array.isArray(spec)
354
+ ? spec.flat(Infinity)
355
+ : [spec]).map(processCSSStyleSpec);
356
+ adoptStyles(this.renderRoot, sheets);
357
+ }
328
358
  /**
329
359
  * Ask this component to update itself.
360
+ *
361
+ * @category Render
330
362
  */
331
363
  requestUpdate(opts) {
332
364
  this.#dirty = true;
@@ -348,9 +380,7 @@ export class Component extends EmitterElement {
348
380
  this.#hasRequiredProperties = Signal.computed(() => {
349
381
  return requiredProperties.size === 0
350
382
  ? true
351
- : requiredProperties
352
- .keys()
353
- .every((key) => this[key] !== undefined);
383
+ : requiredProperties.keys().every((key) => !isNullish(this[key]));
354
384
  },
355
385
  // every signal update is relevant, even if the signal value doesn't
356
386
  // change, because the required values probably did.
@@ -428,14 +458,11 @@ export class Component extends EmitterElement {
428
458
  /**
429
459
  * Return an iterator over this component's children which are also
430
460
  * {@link Component}s.
461
+ *
462
+ * @category Queries
431
463
  */
432
464
  findChildComponents(root = this.renderRoot) {
433
- let all = childElements(root).toArray();
434
- const top = [...all];
435
- for (const el of top) {
436
- all = [...all, ...this.findChildComponents(el)];
437
- }
438
- return Iterator.from(all).filter((el) => el instanceof Component);
465
+ return findDescendants(root, (el) => el instanceof Component);
439
466
  }
440
467
  /**
441
468
  * Return a {@link Promise} which resolves when this component considers
@@ -443,6 +470,8 @@ export class Component extends EmitterElement {
443
470
  *
444
471
  * The default implementation waits for any children which are also
445
472
  * {@link Component}s to report that they have also stabilised.
473
+ *
474
+ * @category Render
446
475
  */
447
476
  async stabilise() {
448
477
  // wait for children to stabilise
@@ -450,8 +479,25 @@ export class Component extends EmitterElement {
450
479
  // eslint-disable-next-line no-console
451
480
  await Promise.all(children.map((child) => child.hasStabilised)).catch(console.error);
452
481
  }
482
+ /**
483
+ * This lifecycle callback is called each time this component is connected
484
+ * to the DOM.
485
+ * @category Lifecycle
486
+ */
487
+ connected() {
488
+ //
489
+ }
490
+ /**
491
+ * This lifecycle callback is called each time this component is
492
+ * disconnected from the DOM.
493
+ * @category Lifecycle
494
+ */
495
+ disconnected() {
496
+ //
497
+ }
453
498
  /**
454
499
  * This lifecycle callback is called each time an update has completed.
500
+ * @category Lifecycle
455
501
  */
456
502
  updated() {
457
503
  //
@@ -459,6 +505,7 @@ export class Component extends EmitterElement {
459
505
  /**
460
506
  * This lifecycle callback is called after the component's first update has
461
507
  * completed, and before {@link Component.updated}.
508
+ * @category Lifecycle
462
509
  */
463
510
  firstUpdated() {
464
511
  //
@@ -468,6 +515,7 @@ export class Component extends EmitterElement {
468
515
  * stabilised after its first update.
469
516
  *
470
517
  * @see {@link Component.hasStabilised}
518
+ * @category Lifecycle
471
519
  */
472
520
  stabilised() {
473
521
  //
@@ -476,6 +524,7 @@ export class Component extends EmitterElement {
476
524
  * This lifecycle callback is called every time a property decorated with
477
525
  * the @{@link require} decorator has changed, but only when every property
478
526
  * marked as such is not `undefined`.
527
+ * @category Lifecycle
479
528
  */
480
529
  initialised() {
481
530
  //
@@ -484,6 +533,7 @@ export class Component extends EmitterElement {
484
533
  * This lifecycle callback is called the first time every property decorated
485
534
  * with @{@link require} has been defined, and before
486
535
  * {@link Component.initialised}.
536
+ * @category Lifecycle
487
537
  */
488
538
  firstInitialised() {
489
539
  //
@@ -503,20 +553,39 @@ export class Component extends EmitterElement {
503
553
  * `;
504
554
  * }
505
555
  * }
556
+ *
557
+ * @category Render
506
558
  */
507
559
  render() {
508
560
  return nothing;
509
561
  }
562
+ /** @internal */
510
563
  [(_a = finalised, Symbol.dispose)]() {
511
564
  for (const child of this.findChildComponents()) {
512
565
  child[Symbol.dispose]();
513
566
  }
567
+ this.remove();
514
568
  this.disconnectedCallback();
515
569
  this.#rootPart?._$clear?.();
516
570
  this.#rootPart = undefined;
517
- this.remove();
571
+ this.#finalCleanupContext.dispose();
518
572
  }
519
- useWhileConnected(disposifiable) {
573
+ use(disposifiable) {
574
+ if (isNullish(disposifiable)) {
575
+ return disposifiable;
576
+ }
577
+ const disposable = toDisposable(disposifiable);
578
+ this.#finalCleanupContext.use(disposable);
579
+ return disposable;
580
+ }
581
+ useWhileConnected(disposifiable, ...disposifiables) {
582
+ if (disposifiables.length > 0) {
583
+ return [disposifiable, ...disposifiables].map((d) => {
584
+ const disposable = toDisposable(present(d));
585
+ this.#connectedContext.use(disposable);
586
+ return disposable;
587
+ });
588
+ }
520
589
  if (isNullish(disposifiable)) {
521
590
  return disposifiable;
522
591
  }
@@ -528,6 +597,7 @@ export class Component extends EmitterElement {
528
597
  * Attach an event listener to an event on this component.
529
598
  *
530
599
  * @returns A {@link Disposable} to subsequently detach the event listener.
600
+ * @category Events
531
601
  */
532
602
  on(type, callback, options) {
533
603
  return eventListener(this, type, callback, options);
@@ -536,6 +606,7 @@ export class Component extends EmitterElement {
536
606
  * If an element that is either inside this element's shadow root or is a
537
607
  * child of this element (ie. slotted) currently has focus, return that
538
608
  * element. Otherwise, return null.
609
+ * @category Queries
539
610
  */
540
611
  getFocusedElement() {
541
612
  return this.shadowRoot?.activeElement ?? this.querySelector(":focus");