@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.
- package/dist/component.d.ts +91 -17
- package/dist/component.js +110 -39
- package/dist/component.js.map +1 -1
- package/dist/decorators/attribute.d.ts +15 -15
- package/dist/decorators/attribute.js +27 -8
- package/dist/decorators/attribute.js.map +1 -1
- package/dist/decorators/attribute.test.js +58 -6
- package/dist/decorators/attribute.test.js.map +1 -1
- package/dist/decorators/connect.d.ts +2 -0
- package/dist/decorators/connect.js.map +1 -1
- package/dist/decorators/require.test.js +1 -1
- package/dist/decorators/require.test.js.map +1 -1
- package/dist/dom.d.ts +12 -2
- package/dist/dom.js +34 -18
- package/dist/dom.js.map +1 -1
- package/dist/dom.test.d.ts +1 -0
- package/dist/dom.test.js +20 -0
- package/dist/dom.test.js.map +1 -0
- package/dist/emitter.d.ts +6 -0
- package/dist/emitter.js +4 -0
- package/dist/emitter.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/test.d.ts +17 -0
- package/dist/test.js +34 -0
- package/dist/test.js.map +1 -0
- package/package.json +25 -19
- package/src/component.ts +146 -47
- package/src/decorators/attribute.test.ts +41 -14
- package/src/decorators/attribute.ts +56 -28
- package/src/decorators/require.test.ts +1 -1
- package/src/dom.test.ts +23 -0
- package/src/dom.ts +55 -16
- package/src/emitter.ts +6 -0
- package/src/index.ts +2 -1
- package/src/test.ts +38 -0
- package/src/decorators/connect.test.ts +0 -119
- package/src/decorators/connect.ts +0 -85
package/dist/component.d.ts
CHANGED
|
@@ -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
|
-
|
|
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 {
|
|
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 {
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
571
|
+
this.#finalCleanupContext.dispose();
|
|
518
572
|
}
|
|
519
|
-
|
|
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");
|