@joist/element 4.2.3-next.5 → 4.2.3-next.7

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 (76) hide show
  1. package/package.json +2 -5
  2. package/src/lib/element.ts +13 -23
  3. package/src/lib/listen.test.ts +75 -0
  4. package/target/lib/element.js +12 -14
  5. package/target/lib/element.js.map +1 -1
  6. package/target/lib/listen.test.js +97 -0
  7. package/target/lib/listen.test.js.map +1 -1
  8. package/src/lib/templating/README.md +0 -406
  9. package/src/lib/templating/bind.ts +0 -40
  10. package/src/lib/templating/define.ts +0 -5
  11. package/src/lib/templating/elements/async.element.test.ts +0 -90
  12. package/src/lib/templating/elements/async.element.ts +0 -122
  13. package/src/lib/templating/elements/for.element.test.ts +0 -221
  14. package/src/lib/templating/elements/for.element.ts +0 -188
  15. package/src/lib/templating/elements/if.element.test.ts +0 -90
  16. package/src/lib/templating/elements/if.element.ts +0 -93
  17. package/src/lib/templating/elements/props.element.test.ts +0 -62
  18. package/src/lib/templating/elements/props.element.ts +0 -80
  19. package/src/lib/templating/elements/scope.ts +0 -45
  20. package/src/lib/templating/elements/value.element.test.ts +0 -20
  21. package/src/lib/templating/elements/value.element.ts +0 -41
  22. package/src/lib/templating/events.ts +0 -21
  23. package/src/lib/templating/token.test.ts +0 -74
  24. package/src/lib/templating/token.ts +0 -34
  25. package/src/lib/templating.ts +0 -2
  26. package/target/lib/templating/bind.d.ts +0 -1
  27. package/target/lib/templating/bind.js +0 -30
  28. package/target/lib/templating/bind.js.map +0 -1
  29. package/target/lib/templating/define.d.ts +0 -5
  30. package/target/lib/templating/define.js +0 -6
  31. package/target/lib/templating/define.js.map +0 -1
  32. package/target/lib/templating/elements/async.element.d.ts +0 -17
  33. package/target/lib/templating/elements/async.element.js +0 -115
  34. package/target/lib/templating/elements/async.element.js.map +0 -1
  35. package/target/lib/templating/elements/async.element.test.d.ts +0 -1
  36. package/target/lib/templating/elements/async.element.test.js +0 -75
  37. package/target/lib/templating/elements/async.element.test.js.map +0 -1
  38. package/target/lib/templating/elements/for.element.d.ts +0 -24
  39. package/target/lib/templating/elements/for.element.js +0 -189
  40. package/target/lib/templating/elements/for.element.js.map +0 -1
  41. package/target/lib/templating/elements/for.element.test.d.ts +0 -2
  42. package/target/lib/templating/elements/for.element.test.js +0 -153
  43. package/target/lib/templating/elements/for.element.test.js.map +0 -1
  44. package/target/lib/templating/elements/if.element.d.ts +0 -12
  45. package/target/lib/templating/elements/if.element.js +0 -85
  46. package/target/lib/templating/elements/if.element.js.map +0 -1
  47. package/target/lib/templating/elements/if.element.test.d.ts +0 -1
  48. package/target/lib/templating/elements/if.element.test.js +0 -78
  49. package/target/lib/templating/elements/if.element.test.js.map +0 -1
  50. package/target/lib/templating/elements/props.element.d.ts +0 -11
  51. package/target/lib/templating/elements/props.element.js +0 -92
  52. package/target/lib/templating/elements/props.element.js.map +0 -1
  53. package/target/lib/templating/elements/props.element.test.d.ts +0 -1
  54. package/target/lib/templating/elements/props.element.test.js +0 -53
  55. package/target/lib/templating/elements/props.element.test.js.map +0 -1
  56. package/target/lib/templating/elements/scope.d.ts +0 -13
  57. package/target/lib/templating/elements/scope.js +0 -59
  58. package/target/lib/templating/elements/scope.js.map +0 -1
  59. package/target/lib/templating/elements/value.element.d.ts +0 -9
  60. package/target/lib/templating/elements/value.element.js +0 -56
  61. package/target/lib/templating/elements/value.element.js.map +0 -1
  62. package/target/lib/templating/elements/value.element.test.d.ts +0 -1
  63. package/target/lib/templating/elements/value.element.test.js +0 -16
  64. package/target/lib/templating/elements/value.element.test.js.map +0 -1
  65. package/target/lib/templating/events.d.ts +0 -12
  66. package/target/lib/templating/events.js +0 -10
  67. package/target/lib/templating/events.js.map +0 -1
  68. package/target/lib/templating/token.d.ts +0 -8
  69. package/target/lib/templating/token.js +0 -27
  70. package/target/lib/templating/token.js.map +0 -1
  71. package/target/lib/templating/token.test.d.ts +0 -1
  72. package/target/lib/templating/token.test.js +0 -56
  73. package/target/lib/templating/token.test.js.map +0 -1
  74. package/target/lib/templating.d.ts +0 -2
  75. package/target/lib/templating.js +0 -3
  76. package/target/lib/templating.js.map +0 -1
@@ -1,406 +0,0 @@
1
- # Joist Templating System
2
-
3
- The Joist templating system provides a powerful and flexible way to handle data binding and templating in web components. This documentation covers the core components and their usage.
4
-
5
- ## Core Components
6
-
7
- ### Bind Decorator (`bind.ts`)
8
-
9
- The `bind` decorator enables reactive data binding for web component properties. It integrates with Joist's observable system to automatically track and propagate changes.
10
-
11
- ```typescript
12
- import { bind } from '@joist/element/templating.js';
13
-
14
- class MyElement extends HTMLElement {
15
- @bind()
16
- myProperty: string;
17
- }
18
- ```
19
-
20
- The decorator:
21
- - Creates a two-way binding between component properties and templates
22
- - Automatically handles value propagation through the `joist::value` event
23
- - Integrates with Joist's observable system for efficient change detection
24
-
25
- ### Token System (`token.ts`)
26
-
27
- The `JToken` class handles parsing and evaluation of binding expressions. It supports:
28
-
29
- - Simple property bindings: `propertyName`
30
- - Nested property access: `user.profile.name`
31
- - Negation operator: `!isVisible`
32
-
33
- Example usage:
34
- ```typescript
35
- const token = new JToken('user.name');
36
- const value = token.readTokenValueFrom(context);
37
- ```
38
-
39
- ### Events (`events.ts`)
40
-
41
- The system uses custom events for value propagation:
42
-
43
- - `JoistValueEvent`: A custom event that handles value updates in the templating system
44
- - Bubbles through the DOM tree
45
- - Carries both the token and update mechanism
46
-
47
- ## Built-in Template Elements
48
-
49
- Joist provides several built-in template elements for common templating needs:
50
-
51
- ### Conditional Rendering (`j-if`)
52
-
53
- Conditionally renders content based on a boolean expression:
54
-
55
- ```html
56
- <j-if bind="isVisible">
57
- <template>
58
- <div>This content is only shown when isVisible is true</div>
59
- </template>
60
- </j-if>
61
-
62
- <!-- With negation -->
63
- <j-if bind="!isHidden">
64
- <template>
65
- <div>This content is shown when isHidden is false</div>
66
- </template>
67
- </j-if>
68
-
69
- <!-- With else template -->
70
- <j-if bind="isLoggedIn">
71
- <template>
72
- <div>Welcome back!</div>
73
- </template>
74
- <template else>
75
- <div>Please log in</div>
76
- </template>
77
- </j-if>
78
- ```
79
-
80
- The `j-if` element supports:
81
- - Boolean expressions for conditional rendering
82
- - Negation operator (`!`) for inverse conditions
83
- - Optional `else` template for fallback content
84
- - Automatic cleanup of removed content
85
-
86
- Common use cases:
87
- - Toggling visibility of UI elements
88
- - Conditional form fields
89
- - Feature flags
90
- - Authentication states
91
- - Loading states
92
-
93
- ### Property Binding (`j-props`)
94
-
95
- Binds values to element properties (rather than attributes). This is particularly useful for boolean properties, form inputs, and other cases where attribute binding isn't sufficient:
96
-
97
- ```html
98
- <j-props>
99
- <!-- Bind to boolean properties -->
100
- <input type="checkbox" $.checked="isComplete">
101
-
102
- <!-- Bind to form input values -->
103
- <input type="text" $.value="userName">
104
-
105
- <!-- Bind to custom element properties -->
106
- <my-element $.data="complexObject">
107
- </j-props>
108
- ```
109
-
110
- Note the `$.` prefix for property bindings. This distinguishes property bindings from attribute bindings.
111
-
112
- Common use cases:
113
- - Form input states (`checked`, `value`, `disabled`)
114
- - Boolean properties that don't work well as attributes
115
- - Complex objects that need to be passed as properties
116
- - Custom element properties
117
-
118
- ### List Rendering (`j-for`)
119
-
120
- Renders lists of items with support for keyed updates:
121
-
122
- ```html
123
- <j-for bind="todos" key="id">
124
- <template>
125
- <j-props>
126
- <div
127
- class="todo-item"
128
- $.dataset.id="each.value.id"
129
- $.dataset.completed="each.value.completed"
130
- >
131
- <j-props>
132
- <input type="checkbox" $.checked="each.value.completed">
133
- </j-props>
134
-
135
- <j-value bind="each.value.text"></j-value>
136
-
137
- <j-props>
138
- <button $.disabled="!each.value.text">×</button>
139
- </j-props>
140
- </div>
141
- </j-props>
142
- </template>
143
- </j-for>
144
- ```
145
-
146
- The `j-for` element provides context variables:
147
- - `each.value`: The current item
148
- - `each.index`: Zero-based index
149
- - `each.position`: One-based position
150
-
151
- ### Value Display (`j-value`)
152
-
153
- Displays a bound value as text content:
154
-
155
- ```html
156
- <j-value bind="user.name"></j-value>
157
- <j-value bind="formattedPrice"></j-value>
158
- ```
159
-
160
- ### Async State Handling (`j-async`)
161
-
162
- Handles asynchronous operations and state management with loading, success, and error states:
163
-
164
- ```html
165
- <j-async bind="userPromise">
166
- <template loading>Loading user data...</template>
167
- <template success>
168
- <div>Welcome, <j-value bind="state.data.name"></j-value>!</div>
169
- </template>
170
- <template error>
171
- <div>Error loading user data: <j-value bind="state.error"></j-value></div>
172
- </template>
173
- </j-async>
174
- ```
175
-
176
- The `j-async` element supports:
177
- - Promise handling with automatic state transitions
178
- - Loading, success, and error templates
179
- - Automatic cleanup on disconnection
180
-
181
- Example usage:
182
- ```typescript
183
- // In your component
184
- @bind()
185
- accessor userPromise = fetch('/api/user').then(r => r.json());
186
- ```
187
-
188
- ```html
189
- <j-async bind="userPromise">
190
- <template loading>Loading...</template>
191
-
192
- <template success>
193
- <div>Welcome, <j-value bind="state.data.name"></j-value>!</div>
194
- </template>
195
-
196
- <template error>
197
- <div>Error: <j-value bind="state.error"></j-value></div>
198
- </template>
199
- </j-async>
200
- ```
201
-
202
- ## Complete Example
203
-
204
- Here's a complete todo application in a single component:
205
-
206
- ```typescript
207
- import { bind } from '@joist/element/templating.js';
208
- import { element, html, css, listen, query } from '@joist/element';
209
-
210
- interface Todo {
211
- id: number;
212
- text: string;
213
- completed: boolean;
214
- }
215
-
216
- @element({
217
- tagName: 'todo-list',
218
- shadowDom: [
219
- css`
220
- :host {
221
- display: block;
222
- max-width: 600px;
223
- margin: 2rem auto;
224
- }
225
- .form {
226
- display: flex;
227
- gap: 1rem;
228
- }
229
- .todo-item {
230
- display: flex;
231
- gap: 0.5rem;
232
- margin: 0.5rem 0;
233
- }
234
- .todo-text {
235
- flex: 1;
236
- }
237
- [data-completed="true"] .todo-text {
238
- text-decoration: line-through;
239
- opacity: 0.6;
240
- }
241
- `,
242
- html`
243
- <form class="form">
244
- <input type="text" placeholder="What needs to be done?">
245
- <button type="submit">Add</button>
246
- </form>
247
-
248
- <j-if bind="!todos.length">
249
- <template>
250
- <p>No todos yet!</p>
251
- </template>
252
- </j-if>
253
-
254
- <j-for id="todos" bind="todos" key="id">
255
- <template>
256
- <j-props class="todo-item" $.dataset.completed="each.value.completed">
257
- <j-props>
258
- <input type="checkbox" $.id="each.value.id" $.checked="each.value.completed">
259
- <j-value class="todo-text" bind="each.value.text"></j-value>
260
- <button $.id="each.value.id" $.disabled="!each.value.text">×</button>
261
- </j-props>
262
- </j-props>
263
- </template>
264
- </j-for>
265
-
266
- <j-value bind="stats.remaining"></j-value> remaining
267
- `
268
- ]
269
- })
270
- export class TodoList extends HTMLElement {
271
- @bind()
272
- accessor todos: Todo[] = [];
273
-
274
- @bind()
275
- accessor stats = {
276
- remaining: 0,
277
- completed: 0
278
- };
279
-
280
- #nextId = 1;
281
- #input = query('input');
282
-
283
- #updateStats() {
284
- this.stats = {
285
- completed: this.todos.filter(todo => todo.completed).length,
286
- remaining: this.todos.length - this.stats.completed
287
- };
288
- }
289
-
290
- @listen('submit', 'form')
291
- onSubmit(e: SubmitEvent) {
292
- e.preventDefault();
293
- const input = this.#input();
294
- const text = input.value.trim();
295
-
296
- if (text) {
297
- this.todos = [
298
- ...this.todos,
299
- { id: this.#nextId++, text, completed: false }
300
- ];
301
-
302
- input.value = '';
303
-
304
- this.#updateStats();
305
- }
306
- }
307
-
308
- @listen('change', '#todos')
309
- onToggle(e: Event) {
310
- if(e.target instanceof HTMLInputElement) {
311
- const id = Number(e.target.id);
312
-
313
- this.todos = this.todos.map(todo => {
314
- if (todo.id === id) {
315
- return { ...todo, completed: checkbox.checked };
316
- }
317
-
318
- return todo;
319
- });
320
-
321
- this.#updateStats();
322
- }
323
- }
324
-
325
- @listen('click', '#todos')
326
- onDelete(e: Event) {
327
- if(e.target instanceof HTMLElement) {
328
- const id = Number(e.target.id);
329
-
330
- this.todos = this.todos.filter(todo => todo.id !== id);
331
-
332
- this.#updateStats();
333
- }
334
- }
335
- }
336
- ```
337
-
338
- ## Usage
339
-
340
- 1. Use the `@bind()` decorator on properties you want to make bindable
341
- 2. Properties will automatically integrate with the templating system
342
- 3. Changes are propagated through the component tree using the custom event system
343
-
344
- ## Integration with Observable
345
-
346
- The templating system is built on top of Joist's observable system (`@joist/observable`), providing:
347
- - Automatic change detection
348
- - Efficient updates
349
- - Integration with the component lifecycle
350
-
351
- ## Best Practices
352
-
353
- 1. Use the `@bind()` decorator only on properties that need reactivity
354
- 2. Keep binding expressions simple and avoid deep nesting
355
- 3. Consider performance implications when binding to frequently changing values
356
- 4. Always use a `key` attribute with `j-for` when items can be reordered
357
- 5. Place template content directly inside `j-if` and `j-for` elements
358
-
359
- ## Manual Value Handling
360
-
361
- You can manually handle value requests and updates by listening for the `joist::value` event. This is useful when you need more control over the binding process or want to implement custom binding logic:
362
-
363
- ```typescript
364
- class MyElement extends HTMLElement {
365
- connectedCallback() {
366
- // Listen for value requests
367
- this.addEventListener('joist::value', (e: JoistValueEvent) => {
368
- const token = e.token;
369
-
370
- // Handle the value request
371
- if (token.bindTo === 'myValue') {
372
- // Update the value
373
- e.update({
374
- oldValue: this.myValue,
375
- newValue: this.myValue
376
- });
377
- }
378
- });
379
- }
380
- }
381
- ```
382
-
383
- Example with async value handling:
384
- ```typescript
385
- class MyElement extends HTMLElement {
386
- connectedCallback() {
387
- this.addEventListener('joist::value', (e: JoistValueEvent) => {
388
- const token = e.token;
389
-
390
- if (token.bindTo === 'userData') {
391
- e.update({
392
- oldValue: this.userData,
393
- newValue: data
394
- });
395
- }
396
- });
397
- }
398
- }
399
- ```
400
-
401
- Common use cases for manual value handling:
402
- - Custom data transformation before binding
403
- - Async data loading and caching
404
- - Complex state management
405
- - Integration with external data sources
406
- - Custom validation or error handling
@@ -1,40 +0,0 @@
1
- import { instanceMetadataStore, observe } from "@joist/observable";
2
-
3
- export function bind() {
4
- return function bindDecorator<This extends HTMLElement, Value>(
5
- base: ClassAccessorDecoratorTarget<This, Value>,
6
- ctx: ClassAccessorDecoratorContext<This, Value>,
7
- ): ClassAccessorDecoratorResult<This, Value> {
8
- const internalObserve = observe()(base, ctx);
9
-
10
- return {
11
- init(value) {
12
- this.addEventListener("joist::value", (e) => {
13
- if (e.token.bindTo === ctx.name) {
14
- const instanceMeta = instanceMetadataStore.read<This>(this);
15
-
16
- e.stopPropagation();
17
-
18
- e.update({ oldValue: null, newValue: ctx.access.get(this) });
19
-
20
- instanceMeta.bindings.add((changes) => {
21
- const key = ctx.name as keyof This;
22
- const res = changes.get(key);
23
-
24
- if (res) {
25
- e.update(res);
26
- }
27
- });
28
- }
29
- });
30
-
31
- if (internalObserve.init) {
32
- return internalObserve.init.call(this, value);
33
- }
34
-
35
- return value;
36
- },
37
- set: internalObserve.set,
38
- };
39
- };
40
- }
@@ -1,5 +0,0 @@
1
- import "./elements/async.element.js";
2
- import "./elements/for.element.js";
3
- import "./elements/if.element.js";
4
- import "./elements/props.element.js";
5
- import "./elements/value.element.js";
@@ -1,90 +0,0 @@
1
- import "./async.element.js";
2
-
3
- import { fixtureSync, html } from "@open-wc/testing";
4
- import { assert } from "chai";
5
-
6
- import type { JoistValueEvent } from "../events.js";
7
-
8
- it("should show loading template when promise is pending", async () => {
9
- const element = fixtureSync(html`
10
- <div
11
- @joist::value=${(e: JoistValueEvent) => {
12
- e.update({ oldValue: null, newValue: new Promise(() => {}) });
13
- }}
14
- >
15
- <j-async bind="test">
16
- <template loading>Loading...</template>
17
- <template success>Success!</template>
18
- <template error>Error!</template>
19
- </j-async>
20
- </div>
21
- `);
22
-
23
- assert.equal(element.textContent?.trim(), "Loading...");
24
- });
25
-
26
- it("should show success template when promise resolves", async () => {
27
- const element = fixtureSync(html`
28
- <div
29
- @joist::value=${(e: JoistValueEvent) => {
30
- e.update({ oldValue: null, newValue: Promise.resolve("data") });
31
- }}
32
- >
33
- <j-async bind="test">
34
- <template loading>Loading...</template>
35
- <template success>Success!</template>
36
- <template error>Error!</template>
37
- </j-async>
38
- </div>
39
- `);
40
-
41
- // Wait for promise to resolve
42
- await new Promise((resolve) => setTimeout(resolve, 0));
43
- assert.equal(element.textContent?.trim(), "Success!");
44
- });
45
-
46
- it("should show error template when promise rejects", async () => {
47
- const element = fixtureSync(html`
48
- <div
49
- @joist::value=${(e: JoistValueEvent) => {
50
- e.update({ oldValue: null, newValue: Promise.reject("error") });
51
- }}
52
- >
53
- <j-async bind="test">
54
- <template loading>Loading...</template>
55
- <template success>Success!</template>
56
- <template error>Error!</template>
57
- </j-async>
58
- </div>
59
- `);
60
-
61
- // Wait for promise to reject
62
- await new Promise((resolve) => setTimeout(resolve, 0));
63
- assert.equal(element.textContent?.trim(), "Error!");
64
- });
65
-
66
- it("should handle state transitions", async () => {
67
- const element = fixtureSync(html`
68
- <div
69
- @joist::value=${(e: JoistValueEvent) => {
70
- const promise = new Promise((resolve) => {
71
- setTimeout(() => resolve("data"), 100);
72
- });
73
- e.update({ oldValue: null, newValue: promise });
74
- }}
75
- >
76
- <j-async bind="test">
77
- <template loading>Loading...</template>
78
- <template success>Success!</template>
79
- <template error>Error!</template>
80
- </j-async>
81
- </div>
82
- `);
83
-
84
- // Initially should show loading
85
- assert.equal(element.textContent?.trim(), "Loading...");
86
-
87
- // Wait for promise to resolve
88
- await new Promise((resolve) => setTimeout(resolve, 150));
89
- assert.equal(element.textContent?.trim(), "Success!");
90
- });
@@ -1,122 +0,0 @@
1
- import { attr } from "../../attr.js";
2
- import { element } from "../../element.js";
3
- import { queryAll } from "../../query-all.js";
4
- import { css, html } from "../../tags.js";
5
- import { bind } from "../bind.js";
6
-
7
- import { JoistValueEvent } from "../events.js";
8
- import { JToken } from "../token.js";
9
-
10
- declare global {
11
- interface HTMLElementTagNameMap {
12
- "j-async": JoistAsyncElement;
13
- }
14
- }
15
-
16
- export type AsyncState<T = unknown, E = unknown> = {
17
- status: "loading" | "error" | "success";
18
- data?: T;
19
- error?: E;
20
- };
21
-
22
- @element({
23
- tagName: "j-async",
24
- // prettier-ignore
25
- shadowDom: [css`:host{display: contents;}`, html`<slot></slot>`],
26
- })
27
- export class JoistAsyncElement extends HTMLElement {
28
- @attr()
29
- accessor bind = "";
30
-
31
- @bind()
32
- accessor state: AsyncState | null = null;
33
-
34
- #templates = queryAll<HTMLTemplateElement>("template", this);
35
- #currentNodes: Node[] = [];
36
- #cachedTemplates: {
37
- loading?: HTMLTemplateElement;
38
- error?: HTMLTemplateElement;
39
- success?: HTMLTemplateElement;
40
- } = {
41
- loading: undefined,
42
- error: undefined,
43
- success: undefined,
44
- };
45
-
46
- connectedCallback(): void {
47
- this.#clean();
48
-
49
- // Cache all templates
50
- const templates = Array.from(this.#templates());
51
-
52
- this.#cachedTemplates = {
53
- loading: templates.find((t) => t.hasAttribute("loading")),
54
- error: templates.find((t) => t.hasAttribute("error")),
55
- success: templates.find((t) => t.hasAttribute("success")),
56
- };
57
-
58
- const token = new JToken(this.bind);
59
-
60
- this.dispatchEvent(
61
- new JoistValueEvent(token, ({ newValue, oldValue }) => {
62
- if (newValue !== oldValue) {
63
- if (newValue instanceof Promise) {
64
- this.#handlePromise(newValue);
65
- } else {
66
- console.warn("j-async bind value must be a Promise or AsyncState");
67
- }
68
- }
69
- }),
70
- );
71
- }
72
-
73
- async #handlePromise(promise: Promise<unknown>): Promise<void> {
74
- try {
75
- this.#handleState({ status: "loading" });
76
- const data = await promise;
77
- this.#handleState({ status: "success", data });
78
- } catch (error) {
79
- this.#handleState({ status: "error", error });
80
- }
81
- }
82
-
83
- #handleState(state: AsyncState): void {
84
- this.#clean();
85
-
86
- let template: HTMLTemplateElement | undefined = undefined;
87
-
88
- this.state = state;
89
-
90
- switch (state.status) {
91
- case "loading":
92
- template = this.#cachedTemplates.loading;
93
- break;
94
-
95
- case "error":
96
- template = this.#cachedTemplates.error;
97
- break;
98
-
99
- case "success":
100
- template = this.#cachedTemplates.success;
101
- break;
102
- }
103
-
104
- if (template) {
105
- const content = document.importNode(template.content, true);
106
- const nodes = Array.from(content.childNodes);
107
- this.appendChild(content);
108
- this.#currentNodes = nodes;
109
- }
110
- }
111
-
112
- #clean(): void {
113
- for (const node of this.#currentNodes) {
114
- node.parentNode?.removeChild(node);
115
- }
116
- this.#currentNodes = [];
117
- }
118
-
119
- disconnectedCallback(): void {
120
- this.#clean();
121
- }
122
- }