@dooboostore/simple-web-component 1.0.17 → 1.0.18
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/README.md +75 -107
- package/dist/cjs/decorators/appendChild.js +48 -0
- package/dist/cjs/decorators/appendChild.js.map +7 -0
- package/dist/cjs/decorators/elementDefine.js +85 -7
- package/dist/cjs/decorators/elementDefine.js.map +2 -2
- package/dist/cjs/decorators/index.js +2 -1
- package/dist/cjs/decorators/index.js.map +2 -2
- package/dist/cjs/decorators/{innerHtml.js → onConnectedInnerHtml.js} +8 -8
- package/dist/cjs/decorators/onConnectedInnerHtml.js.map +7 -0
- package/dist/cjs/decorators/query.js +16 -0
- package/dist/cjs/decorators/query.js.map +2 -2
- package/dist/cjs/decorators/queryAll.js +18 -0
- package/dist/cjs/decorators/queryAll.js.map +2 -2
- package/dist/cjs/decorators/replaceChildren.js +53 -0
- package/dist/cjs/decorators/replaceChildren.js.map +7 -0
- package/dist/cjs/index.js +11 -1
- package/dist/cjs/index.js.map +2 -2
- package/dist/esm/decorators/appendChild.js +29 -0
- package/dist/esm/decorators/appendChild.js.map +7 -0
- package/dist/esm/decorators/elementDefine.js +85 -7
- package/dist/esm/decorators/elementDefine.js.map +2 -2
- package/dist/esm/decorators/index.js +2 -1
- package/dist/esm/decorators/index.js.map +2 -2
- package/dist/esm/decorators/{innerHtml.js → onConnectedInnerHtml.js} +5 -5
- package/dist/esm/decorators/onConnectedInnerHtml.js.map +7 -0
- package/dist/esm/decorators/query.js +16 -0
- package/dist/esm/decorators/query.js.map +2 -2
- package/dist/esm/decorators/queryAll.js +18 -0
- package/dist/esm/decorators/queryAll.js.map +2 -2
- package/dist/esm/decorators/replaceChildren.js +34 -0
- package/dist/esm/decorators/replaceChildren.js.map +7 -0
- package/dist/esm/index.js +7 -1
- package/dist/esm/index.js.map +2 -2
- package/dist/esm-bundle/dooboostore-simple-web-component.esm.js +181 -11
- package/dist/esm-bundle/dooboostore-simple-web-component.esm.js.map +3 -3
- package/dist/types/decorators/appendChild.d.ts +17 -0
- package/dist/types/decorators/appendChild.d.ts.map +1 -0
- package/dist/types/decorators/elementDefine.d.ts.map +1 -1
- package/dist/types/decorators/index.d.ts +2 -1
- package/dist/types/decorators/index.d.ts.map +1 -1
- package/dist/types/decorators/onConnectedInnerHtml.d.ts +15 -0
- package/dist/types/decorators/onConnectedInnerHtml.d.ts.map +1 -0
- package/dist/types/decorators/query.d.ts.map +1 -1
- package/dist/types/decorators/queryAll.d.ts.map +1 -1
- package/dist/types/decorators/replaceChildren.d.ts +9 -0
- package/dist/types/decorators/replaceChildren.d.ts.map +1 -0
- package/dist/types/index.d.ts +4 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/umd-bundle/dooboostore-simple-web-component.umd.js +181 -11
- package/dist/umd-bundle/dooboostore-simple-web-component.umd.js.map +3 -3
- package/package.json +1 -1
- package/src/decorators/appendChild.ts +42 -0
- package/src/decorators/elementDefine.ts +90 -7
- package/src/decorators/index.ts +2 -1
- package/src/decorators/{innerHtml.ts → onConnectedInnerHtml.ts} +7 -6
- package/src/decorators/query.ts +12 -0
- package/src/decorators/queryAll.ts +16 -0
- package/src/decorators/replaceChildren.ts +35 -0
- package/src/index.ts +4 -1
- package/dist/cjs/decorators/innerHtml.js.map +0 -7
- package/dist/esm/decorators/innerHtml.js.map +0 -7
- package/dist/types/decorators/innerHtml.d.ts +0 -12
- package/dist/types/decorators/innerHtml.d.ts.map +0 -1
package/README.md
CHANGED
|
@@ -42,13 +42,13 @@ Registers the class as a custom element.
|
|
|
42
42
|
| `observedAttributes` | `string[]` | (Optional) Extra attributes to observe. |
|
|
43
43
|
| `window` | `Window` | (Optional) Custom window object (useful for SSR). |
|
|
44
44
|
|
|
45
|
-
### 2. `@
|
|
46
|
-
Defines the template for the component.
|
|
45
|
+
### 2. `@onConnectedInnerHtml(options?)`
|
|
46
|
+
Defines the template for the component. Executed when the component is connected to the DOM.
|
|
47
47
|
|
|
48
48
|
```typescript
|
|
49
49
|
@elementDefine('my-el')
|
|
50
50
|
class MyEl extends HTMLElement {
|
|
51
|
-
@
|
|
51
|
+
@onConnectedInnerHtml({ useShadow: true })
|
|
52
52
|
render() {
|
|
53
53
|
return `<style>:host { color: blue; }</style><div>Hello World</div>`;
|
|
54
54
|
}
|
|
@@ -80,7 +80,7 @@ class MyCounter extends HTMLElement {
|
|
|
80
80
|
```
|
|
81
81
|
|
|
82
82
|
### 4. Powerful Queries (`@query`, `@queryAll`)
|
|
83
|
-
Inject internal elements directly into properties. Supports the **`:host`** selector.
|
|
83
|
+
Inject internal elements directly into properties. Supports the **`:host`** selector and precise root control. Assigning `null` or `[]` to these properties will surgically remove the target elements from the DOM.
|
|
84
84
|
|
|
85
85
|
| Root Option | Description |
|
|
86
86
|
|---|---|
|
|
@@ -89,24 +89,14 @@ Inject internal elements directly into properties. Supports the **`:host`** sele
|
|
|
89
89
|
| `light` | **Strictly** search in Light DOM (`this`). |
|
|
90
90
|
| `all` | Search in **both** Shadow and Light DOM. |
|
|
91
91
|
|
|
92
|
-
Inject elements directly into properties. Supports `:host` and scoped root control.
|
|
93
|
-
|
|
94
92
|
```typescript
|
|
95
|
-
@
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
@queryAll('.items') list!: NodeListOf<HTMLElement>;
|
|
99
|
-
|
|
100
|
-
// Reference the component itself
|
|
101
|
-
@query(':host') self!: HTMLElement;
|
|
102
|
-
|
|
103
|
-
// Root options: 'light' | 'shadow' | 'all' | 'auto' (default)
|
|
104
|
-
@query('.btn', { root: 'shadow' }) shadowBtn!: HTMLButtonElement;
|
|
105
|
-
}
|
|
93
|
+
@query('.title') titleEl!: HTMLElement;
|
|
94
|
+
@query('.btn', { root: 'shadow' }) shadowBtn!: HTMLButtonElement;
|
|
95
|
+
@query(':host') self!: HTMLElement; // References the component itself
|
|
106
96
|
```
|
|
107
97
|
|
|
108
98
|
### 5. Smart Event Binding (`@addEventListener`)
|
|
109
|
-
Bind events declaratively with support for **Event Delegation
|
|
99
|
+
Bind events declaratively with support for **Event Delegation** and special targets like `:window` and `:document`.
|
|
110
100
|
|
|
111
101
|
```typescript
|
|
112
102
|
@elementDefine('event-demo')
|
|
@@ -115,11 +105,55 @@ class EventDemo extends HTMLElement {
|
|
|
115
105
|
@addEventListener('.static-btn', 'click')
|
|
116
106
|
onStaticClick(event: MouseEvent) { ... }
|
|
117
107
|
|
|
118
|
-
// 2. Event Delegation (
|
|
108
|
+
// 2. Event Delegation (Handles dynamic nodes)
|
|
119
109
|
@addEventListener('.item', 'click', { delegate: true })
|
|
120
110
|
onItemClick(event: MouseEvent, { $host }: HostSet, target: HTMLElement) {
|
|
121
111
|
console.log('Clicked dynamic item:', target.textContent);
|
|
122
112
|
}
|
|
113
|
+
|
|
114
|
+
// 3. Global Events with Auto-Cleanup
|
|
115
|
+
@addEventListener(':window', 'resize', { removeOnDisconnected: true })
|
|
116
|
+
onResize() { ... }
|
|
117
|
+
}
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### 6. `@replaceChildren(selector)`
|
|
121
|
+
Surgically replaces the children of a target element with the method's return value (`string`, `Node`, or `Array<Node|string>`).
|
|
122
|
+
|
|
123
|
+
```typescript
|
|
124
|
+
@elementDefine('surgical-demo')
|
|
125
|
+
class SurgicalDemo extends HTMLElement {
|
|
126
|
+
// Replaces all children of #display-area with the new content
|
|
127
|
+
@replaceChildren('#display-area')
|
|
128
|
+
updateView(title: string) {
|
|
129
|
+
return `<h2>${title}</h2><p>Updated successfully.</p>`;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### 7. `@appendChild(selector, options?)`
|
|
135
|
+
Appends or inserts the method's return value relative to a target element.
|
|
136
|
+
- **position**: `'lastChild'` (default), `'firstChild'`, `'nextSibling'`, `'previousSibling'`.
|
|
137
|
+
|
|
138
|
+
```typescript
|
|
139
|
+
@elementDefine('list-manager')
|
|
140
|
+
class ListManager extends HTMLElement {
|
|
141
|
+
// Prepends a new item to the top of the #my-list element
|
|
142
|
+
@appendChild('#my-list', { position: 'firstChild' })
|
|
143
|
+
addNewItem(text: string) {
|
|
144
|
+
return `<li>${text}</li>`;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
```typescript
|
|
150
|
+
@elementDefine('list-manager')
|
|
151
|
+
class ListManager extends HTMLElement {
|
|
152
|
+
// Prepends a new item to the top of the #my-list element
|
|
153
|
+
@appendChild('#my-list', { position: 'firstChild' })
|
|
154
|
+
addNewItem(text: string) {
|
|
155
|
+
return `<li>${text}</li>`;
|
|
156
|
+
}
|
|
123
157
|
}
|
|
124
158
|
```
|
|
125
159
|
|
|
@@ -130,14 +164,13 @@ class EventDemo extends HTMLElement {
|
|
|
130
164
|
SWC provides a revolutionary way to build SPAs where **each component scope is an independent application**.
|
|
131
165
|
|
|
132
166
|
### 1. Isolated App Scopes (Micro-Frontends)
|
|
133
|
-
Use the `<swc-app>` tag to create an isolated universe. Each instance has its own **DI Container** and **Router**.
|
|
167
|
+
Use the `<swc-app>` tag to create an isolated universe. Each instance has its own **DI Container** and **Router**.
|
|
134
168
|
|
|
135
169
|
```html
|
|
136
170
|
<!-- index.html -->
|
|
137
171
|
<swc-app swc-get-application-config="myAppConfig(this)"></swc-app>
|
|
138
172
|
|
|
139
173
|
<script>
|
|
140
|
-
// This function returns the configuration needed to bootstrap the SWC App
|
|
141
174
|
window.myAppConfig = (host) => {
|
|
142
175
|
return {
|
|
143
176
|
rootRouter: RootRouter, // The entry point class (must be @Sim)
|
|
@@ -147,35 +180,24 @@ Use the `<swc-app>` tag to create an isolated universe. Each instance has its ow
|
|
|
147
180
|
};
|
|
148
181
|
};
|
|
149
182
|
|
|
150
|
-
//
|
|
183
|
+
// External Control
|
|
151
184
|
function navigateFromOutside() {
|
|
152
185
|
const app = document.querySelector('swc-app');
|
|
153
|
-
// Programmatically trigger routing from outside the component
|
|
154
186
|
app.routing('/users/detail/2');
|
|
155
187
|
}
|
|
156
188
|
</script>
|
|
157
|
-
|
|
158
|
-
<button onclick="navigateFromOutside()">External Link to User 2</button>
|
|
159
189
|
```
|
|
160
190
|
|
|
161
191
|
### 2. DI-Powered Routing
|
|
162
|
-
Define routes using decorators. Components are automatically instantiated and managed by the DI container.
|
|
163
|
-
|
|
164
192
|
```typescript
|
|
165
|
-
import { Router } from '@dooboostore/simple-boot/decorators/route/Router';
|
|
166
|
-
import { Sim } from '@dooboostore/simple-boot/decorators/SimDecorator';
|
|
167
|
-
|
|
168
193
|
@Sim
|
|
169
194
|
@Router({
|
|
170
195
|
path: '/users',
|
|
171
|
-
route: {
|
|
172
|
-
'/list': UserList,
|
|
173
|
-
'/detail/:id': UserDetail
|
|
174
|
-
}
|
|
196
|
+
route: { '/list': UserList, '/detail/:id': UserDetail }
|
|
175
197
|
})
|
|
176
198
|
@elementDefine('user-router')
|
|
177
199
|
class UserRouter extends HTMLElement implements RouterAction.CanActivate {
|
|
178
|
-
@
|
|
200
|
+
@onConnectedInnerHtml({ useShadow: true })
|
|
179
201
|
render() {
|
|
180
202
|
return `<header>User System</header><main><slot></slot></main>`;
|
|
181
203
|
}
|
|
@@ -185,21 +207,17 @@ class UserRouter extends HTMLElement implements RouterAction.CanActivate {
|
|
|
185
207
|
}
|
|
186
208
|
}
|
|
187
209
|
|
|
188
|
-
// Pure Service (Business Logic)
|
|
189
210
|
@Sim
|
|
190
211
|
class UserService {
|
|
191
|
-
async getProfile(id: string) {
|
|
192
|
-
return { id, name: 'Standard User' };
|
|
193
|
-
}
|
|
212
|
+
async getProfile(id: string) { return { id, name: 'Standard User' }; }
|
|
194
213
|
}
|
|
195
214
|
|
|
196
|
-
// Example Page Components
|
|
197
215
|
@Sim
|
|
198
216
|
@elementDefine('user-list')
|
|
199
217
|
class UserList extends HTMLElement {
|
|
200
218
|
constructor(private router: Router, private userService: UserService) { super(); }
|
|
201
219
|
|
|
202
|
-
@
|
|
220
|
+
@onConnectedInnerHtml
|
|
203
221
|
render() {
|
|
204
222
|
return `
|
|
205
223
|
<ul><li>User A</li><li>User B</li></ul>
|
|
@@ -220,22 +238,18 @@ class UserDetail extends HTMLElement {
|
|
|
220
238
|
console.log('Viewing User:', profile.name);
|
|
221
239
|
}
|
|
222
240
|
|
|
223
|
-
@
|
|
241
|
+
@onConnectedInnerHtml
|
|
224
242
|
render() { return `<div>User Detail View <button swc-on-click="$host.router.back()">Back</button></div>`; }
|
|
225
243
|
}
|
|
226
244
|
```
|
|
227
245
|
|
|
228
246
|
### 3. Lifecycle @Inject
|
|
229
|
-
Inject services or component context directly into lifecycle methods. This is the ultimate way to handle side effects cleanly.
|
|
230
|
-
|
|
231
247
|
```typescript
|
|
232
248
|
import { Inject } from '@dooboostore/simple-boot/decorators/inject/Inject';
|
|
233
249
|
import { InjectSituationType, onConnected, HostSet } from '@dooboostore/simple-web-component';
|
|
234
|
-
import { Router } from '@dooboostore/core-web/routers/Router';
|
|
235
250
|
|
|
236
251
|
@elementDefine('profile-page')
|
|
237
252
|
class ProfilePage extends HTMLElement {
|
|
238
|
-
|
|
239
253
|
@onConnected
|
|
240
254
|
async init(
|
|
241
255
|
@Inject({ situationType: InjectSituationType.HOST_SET }) hostSet: HostSet,
|
|
@@ -244,7 +258,6 @@ class ProfilePage extends HTMLElement {
|
|
|
244
258
|
) {
|
|
245
259
|
console.log('App scope:', hostSet.$appHost);
|
|
246
260
|
const user = await api.getProfile();
|
|
247
|
-
// Navigate programmatically using injected router
|
|
248
261
|
if (!user) router.go('/login');
|
|
249
262
|
}
|
|
250
263
|
}
|
|
@@ -271,36 +284,25 @@ Executed in **Parent -> Child** order.
|
|
|
271
284
|
|
|
272
285
|
---
|
|
273
286
|
|
|
274
|
-
## ⚡ Performance
|
|
275
|
-
- **Zero "Deep Scan"**: Listeners are only bound to specific selectors.
|
|
276
|
-
- **Surgical Updates**: Manipulate DOM directly in `@changedAttribute` instead of full re-renders.
|
|
277
|
-
- **Zero Background Overhead**: No polling, no background observers.
|
|
278
|
-
|
|
279
|
-
---
|
|
280
|
-
|
|
281
|
-
---
|
|
282
|
-
|
|
283
287
|
## 🔄 Structural Directives (Advanced)
|
|
284
288
|
|
|
285
|
-
SWC provides 4 core templates
|
|
289
|
+
SWC provides 4 core templates: `swc-loop`, `swc-if`, `swc-choose`, and `swc-async`.
|
|
286
290
|
|
|
287
291
|
### Common Attributes:
|
|
288
|
-
- **`on-get-value`**: Script to fetch data
|
|
289
|
-
- **`on-clone-nodes`**: Callback after a group of nodes is cloned
|
|
290
|
-
- **`on-clone-node`**: Callback for each individual node
|
|
291
|
-
- **`on-get-portal`**: Script that returns an `Element` or selector
|
|
292
|
+
- **`on-get-value`**: Script to fetch data.
|
|
293
|
+
- **`on-clone-nodes`**: Callback after a group of nodes is cloned.
|
|
294
|
+
- **`on-clone-node`**: Callback for each individual node.
|
|
295
|
+
- **`on-get-portal`**: Script that returns an `Element` or selector for portal rendering.
|
|
292
296
|
|
|
293
297
|
### 1. `swc-if` (Conditional Rendering)
|
|
294
|
-
Renders content only when the condition is truthy. Supports a fallback `swc-default` state.
|
|
295
298
|
```html
|
|
296
299
|
<template is="swc-if" on-get-value="isAdmin">
|
|
297
|
-
<template is="swc-default"><p>Please login
|
|
298
|
-
<div class="admin-panel">Welcome
|
|
300
|
+
<template is="swc-default"><p>Please login.</p></template>
|
|
301
|
+
<div class="admin-panel">Welcome!</div>
|
|
299
302
|
</template>
|
|
300
303
|
```
|
|
301
304
|
|
|
302
305
|
### 2. `swc-loop` (High-Performance Iteration)
|
|
303
|
-
Efficiently renders lists with surgical updates and index preservation.
|
|
304
306
|
```html
|
|
305
307
|
<template is="swc-loop" on-get-value="users"
|
|
306
308
|
on-clone-node="if($isElement) $node.querySelector('.name').textContent = $item.name">
|
|
@@ -310,64 +312,30 @@ Efficiently renders lists with surgical updates and index preservation.
|
|
|
310
312
|
```
|
|
311
313
|
|
|
312
314
|
### 3. `swc-choose` (Switch-Case Logic)
|
|
313
|
-
Selects the first matching `swc-when` or falls back to `swc-otherwise`.
|
|
314
315
|
```html
|
|
315
316
|
<template is="swc-choose" on-get-value="user.role">
|
|
316
|
-
<template is="swc-default">
|
|
317
|
+
<template is="swc-default">Select a role.</template>
|
|
317
318
|
<template is="swc-when" value="admin">Admin Dashboard</template>
|
|
318
|
-
<template is="swc-when" value="user">User Home</template>
|
|
319
319
|
<template is="swc-otherwise">Access Denied</template>
|
|
320
320
|
</template>
|
|
321
321
|
```
|
|
322
322
|
|
|
323
323
|
### 4. `swc-async` (Declarative Promises)
|
|
324
|
-
Handles Promise states (Loading, Error, Success) without manual state management.
|
|
325
324
|
```html
|
|
326
|
-
<template is="swc-async" on-get-value="fetchData()"
|
|
325
|
+
<template is="swc-async" on-get-value="fetchData()"
|
|
326
|
+
on-clone-node="if($isElement && $node.className === 'content') $node.textContent = $value?.title || $error?.message">
|
|
327
327
|
<template is="swc-loading">🌀 Loading...</template>
|
|
328
|
-
<template is="swc-error"
|
|
329
|
-
<template is="swc-success"
|
|
330
|
-
</template>
|
|
331
|
-
```
|
|
332
|
-
|
|
333
|
-
### Advanced: Integrated Portal Support
|
|
334
|
-
All directives above support the **`on-get-portal`** attribute to render content elsewhere.
|
|
335
|
-
```html
|
|
336
|
-
<template is="swc-if" on-get-value="showModal" on-get-portal="'body'">
|
|
337
|
-
<div class="modal-overlay">I am rendered at the end of <body>!</div>
|
|
328
|
+
<template is="swc-error"><div class="content"></div></template>
|
|
329
|
+
<template is="swc-success"><div class="content"></div></template>
|
|
338
330
|
</template>
|
|
339
331
|
```
|
|
340
332
|
|
|
341
333
|
---
|
|
342
334
|
|
|
343
|
-
## ⚡
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
| Selector | Target Description |
|
|
348
|
-
|---|---|
|
|
349
|
-
| `:host` | The component itself (default for empty selector). |
|
|
350
|
-
| `:parentHost` | The immediate parent SWC component. |
|
|
351
|
-
| `:appHost` | The nearest parent `<swc-app>` instance. |
|
|
352
|
-
| `:window` | The environment-aware global `window` object. |
|
|
353
|
-
| `:document` | The environment-aware global `document` object. |
|
|
354
|
-
| `:hosts` | Array of all SWC ancestors (Root -> Self). |
|
|
355
|
-
|
|
356
|
-
### Global Event Example
|
|
357
|
-
```typescript
|
|
358
|
-
@elementDefine('auto-cleanup-demo')
|
|
359
|
-
class CleanupDemo extends HTMLElement {
|
|
360
|
-
// Automatically attaches to window and REMOVES on disconnectedCallback
|
|
361
|
-
@addEventListener(':window', 'resize', { removeOnDisconnected: true })
|
|
362
|
-
onResize(event: UIEvent) {
|
|
363
|
-
console.log('Window resized!', this.id);
|
|
364
|
-
}
|
|
365
|
-
}
|
|
366
|
-
```
|
|
367
|
-
|
|
368
|
-
## ⚡ Performance Features
|
|
335
|
+
## ⚡ Performance
|
|
336
|
+
- **Zero "Deep Scan"**: Listeners are only bound to specific selectors.
|
|
337
|
+
- **Surgical Updates**: Manipulate DOM directly in `@changedAttribute` instead of full re-renders.
|
|
369
338
|
- **Smart Delegation**: Host-level delegation with `__swc_handled` duplicate protection.
|
|
370
|
-
- **Index-Preserving Removal**: `SwcLoop` maintains stable indices during item removal for maximum UI consistency.
|
|
371
339
|
|
|
372
340
|
---
|
|
373
341
|
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
3
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
5
|
+
var __export = (target, all) => {
|
|
6
|
+
for (var name in all)
|
|
7
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
8
|
+
};
|
|
9
|
+
var __copyProps = (to, from, except, desc) => {
|
|
10
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
11
|
+
for (let key of __getOwnPropNames(from))
|
|
12
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
13
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
14
|
+
}
|
|
15
|
+
return to;
|
|
16
|
+
};
|
|
17
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
18
|
+
var appendChild_exports = {};
|
|
19
|
+
__export(appendChild_exports, {
|
|
20
|
+
APPEND_CHILD_METADATA_KEY: () => APPEND_CHILD_METADATA_KEY,
|
|
21
|
+
appendChild: () => appendChild,
|
|
22
|
+
findAllAppendChildMetadata: () => findAllAppendChildMetadata
|
|
23
|
+
});
|
|
24
|
+
module.exports = __toCommonJS(appendChild_exports);
|
|
25
|
+
var import_ReflectUtils = require("@dooboostore/core/reflect/ReflectUtils");
|
|
26
|
+
const APPEND_CHILD_METADATA_KEY = Symbol("simple-web-component:append-child");
|
|
27
|
+
function appendChild(selector, options = { position: "lastChild" }) {
|
|
28
|
+
return (target, propertyKey) => {
|
|
29
|
+
const constructor = target.constructor;
|
|
30
|
+
let meta = import_ReflectUtils.ReflectUtils.getOwnMetadata(APPEND_CHILD_METADATA_KEY, constructor);
|
|
31
|
+
if (!meta) {
|
|
32
|
+
meta = /* @__PURE__ */ new Map();
|
|
33
|
+
import_ReflectUtils.ReflectUtils.defineMetadata(APPEND_CHILD_METADATA_KEY, meta, constructor);
|
|
34
|
+
}
|
|
35
|
+
meta.set(propertyKey, { propertyKey, selector, options });
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
const findAllAppendChildMetadata = (target) => {
|
|
39
|
+
const result = /* @__PURE__ */ new Map();
|
|
40
|
+
const maps = import_ReflectUtils.ReflectUtils.findAllMetadata(APPEND_CHILD_METADATA_KEY, target);
|
|
41
|
+
maps.forEach((map) => {
|
|
42
|
+
if (map instanceof Map) {
|
|
43
|
+
map.forEach((v, k) => result.set(k, v));
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
return result;
|
|
47
|
+
};
|
|
48
|
+
//# sourceMappingURL=appendChild.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/decorators/appendChild.ts"],
|
|
4
|
+
"sourcesContent": ["import { ReflectUtils } from '@dooboostore/core/reflect/ReflectUtils';\n\nexport type AppendChildPosition = 'firstChild' | 'lastChild' | 'nextSibling' | 'previousSibling';\n\nexport interface AppendChildOptions {\n position?: AppendChildPosition;\n}\n\nexport interface AppendChildMetadata {\n propertyKey: string | symbol;\n selector: string;\n options: AppendChildOptions;\n}\n\nexport const APPEND_CHILD_METADATA_KEY = Symbol('simple-web-component:append-child');\n\n/**\n * @appendChild decorator to surgically add nodes to a target element.\n * Usage: @appendChild('#list') addItem() { return '<li>New Item</li>'; }\n */\nexport function appendChild(selector: string, options: AppendChildOptions = { position: 'lastChild' }): MethodDecorator {\n return (target: Object, propertyKey: string | symbol) => {\n const constructor = target.constructor;\n let meta = ReflectUtils.getOwnMetadata(APPEND_CHILD_METADATA_KEY, constructor) as Map<string | symbol, AppendChildMetadata>;\n if (!meta) {\n meta = new Map<string | symbol, AppendChildMetadata>();\n ReflectUtils.defineMetadata(APPEND_CHILD_METADATA_KEY, meta, constructor);\n }\n meta.set(propertyKey, { propertyKey, selector, options });\n };\n}\n\nexport const findAllAppendChildMetadata = (target: any): Map<string | symbol, AppendChildMetadata> => {\n const result = new Map<string | symbol, AppendChildMetadata>();\n const maps = ReflectUtils.findAllMetadata<Map<string | symbol, AppendChildMetadata>>(APPEND_CHILD_METADATA_KEY, target);\n maps.forEach(map => {\n if (map instanceof Map) {\n map.forEach((v, k) => result.set(k, v));\n }\n });\n return result;\n};\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,0BAA6B;AActB,MAAM,4BAA4B,OAAO,mCAAmC;AAM5E,SAAS,YAAY,UAAkB,UAA8B,EAAE,UAAU,YAAY,GAAoB;AACtH,SAAO,CAAC,QAAgB,gBAAiC;AACvD,UAAM,cAAc,OAAO;AAC3B,QAAI,OAAO,iCAAa,eAAe,2BAA2B,WAAW;AAC7E,QAAI,CAAC,MAAM;AACT,aAAO,oBAAI,IAA0C;AACrD,uCAAa,eAAe,2BAA2B,MAAM,WAAW;AAAA,IAC1E;AACA,SAAK,IAAI,aAAa,EAAE,aAAa,UAAU,QAAQ,CAAC;AAAA,EAC1D;AACF;AAEO,MAAM,6BAA6B,CAAC,WAA2D;AACpG,QAAM,SAAS,oBAAI,IAA0C;AAC7D,QAAM,OAAO,iCAAa,gBAA2D,2BAA2B,MAAM;AACtH,OAAK,QAAQ,SAAO;AAClB,QAAI,eAAe,KAAK;AACtB,UAAI,QAAQ,CAAC,GAAG,MAAM,OAAO,IAAI,GAAG,CAAC,CAAC;AAAA,IACxC;AAAA,EACF,CAAC;AACD,SAAO;AACT;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -24,12 +24,14 @@ __export(elementDefine_exports, {
|
|
|
24
24
|
module.exports = __toCommonJS(elementDefine_exports);
|
|
25
25
|
var import_ReflectUtils = require("@dooboostore/core/reflect/ReflectUtils");
|
|
26
26
|
var import_FunctionUtils = require("@dooboostore/core/function/FunctionUtils");
|
|
27
|
-
var
|
|
27
|
+
var import_onConnectedInnerHtml = require("./onConnectedInnerHtml");
|
|
28
28
|
var import_addEventListener = require("./addEventListener");
|
|
29
29
|
var import_lifecycles = require("./lifecycles");
|
|
30
30
|
var import_emitCustomEvent = require("./emitCustomEvent");
|
|
31
31
|
var import_changedAttribute = require("./changedAttribute");
|
|
32
32
|
var import_setAttribute = require("./setAttribute");
|
|
33
|
+
var import_replaceChildren = require("./replaceChildren");
|
|
34
|
+
var import_appendChild = require("./appendChild");
|
|
33
35
|
var import_Utils = require("../utils/Utils");
|
|
34
36
|
var import_config = require("../config/config");
|
|
35
37
|
var import_Inject = require("@dooboostore/simple-boot/decorators/inject/Inject");
|
|
@@ -71,10 +73,12 @@ const elementDefine = (inConfig) => (constructor) => {
|
|
|
71
73
|
const addEventListenerList = (0, import_addEventListener.getAddEventListenerMetadata)(constructor) || [];
|
|
72
74
|
const attrChangeMap = (0, import_changedAttribute.findAllAttributeChangedMetadata)(constructor);
|
|
73
75
|
const setAttrMap = (0, import_setAttribute.findAllSetAttributeMetadata)(constructor);
|
|
76
|
+
const replaceChildrenMap = (0, import_replaceChildren.findAllReplaceChildrenMetadata)(constructor);
|
|
77
|
+
const appendChildMap = (0, import_appendChild.findAllAppendChildMetadata)(constructor);
|
|
74
78
|
const swcLifecycleAttributes = ["swc-on-constructor", "swc-on-connected", "swc-on-disconnected", "swc-on-before-connected", "swc-on-after-connected", "swc-on-before-disconnected", "swc-on-after-disconnected", "swc-on-before-adopted", "swc-on-after-adopted", "swc-on-attribute-changed"];
|
|
75
79
|
const swcOnEvents = import_config.DOM_EVENT_NAMES.map((e) => `swc-on-${e}`);
|
|
76
80
|
const mergedObservedAttributes = [.../* @__PURE__ */ new Set([...config.observedAttributes ?? [], ...attrChangeMap.keys(), ...setAttrMap.keys(), ...emitCustomEventList.map((it) => it.options.attributeName), ...swcLifecycleAttributes, ...swcOnEvents])];
|
|
77
|
-
const
|
|
81
|
+
const connectedInnerHtmlList = (0, import_onConnectedInnerHtml.getOnConnectedInnerHtmlMetadata)(constructor) || [];
|
|
78
82
|
const NewClass = class extends constructor {
|
|
79
83
|
_executeSwcScript(attrName, hostSet, extraArgs = {}) {
|
|
80
84
|
const script = this.getAttribute(attrName);
|
|
@@ -141,7 +145,7 @@ const elementDefine = (inConfig) => (constructor) => {
|
|
|
141
145
|
this._boundListeners = [];
|
|
142
146
|
const hostSet = import_Utils.SwcUtils.getHostSet(this);
|
|
143
147
|
this._executeSwcScript("swc-on-constructor", hostSet);
|
|
144
|
-
if (
|
|
148
|
+
if (connectedInnerHtmlList.some((it) => it.options.useShadow === true) && !this.shadowRoot) {
|
|
145
149
|
this.attachShadow({ mode: "open" });
|
|
146
150
|
}
|
|
147
151
|
}
|
|
@@ -161,9 +165,9 @@ const elementDefine = (inConfig) => (constructor) => {
|
|
|
161
165
|
}
|
|
162
166
|
if (typeof this.initCore === "function")
|
|
163
167
|
this.initCore();
|
|
164
|
-
if (
|
|
168
|
+
if (connectedInnerHtmlList.length > 0) {
|
|
165
169
|
let sContent = "", lContent = "";
|
|
166
|
-
for (const meta of
|
|
170
|
+
for (const meta of connectedInnerHtmlList) {
|
|
167
171
|
const res = await this[meta.propertyKey]();
|
|
168
172
|
if (res !== void 0) {
|
|
169
173
|
if (meta.options.useShadow)
|
|
@@ -207,8 +211,9 @@ const elementDefine = (inConfig) => (constructor) => {
|
|
|
207
211
|
}
|
|
208
212
|
};
|
|
209
213
|
import_config.DOM_EVENT_NAMES.forEach((type) => {
|
|
210
|
-
|
|
211
|
-
this.
|
|
214
|
+
const handler = createDelegationHandler(this);
|
|
215
|
+
this.addEventListener(type, handler);
|
|
216
|
+
this._boundListeners.push({ target: this, type, handler });
|
|
212
217
|
});
|
|
213
218
|
const hasParentHost = import_Utils.SwcUtils.findAllSwcHosts(this).length > 0;
|
|
214
219
|
if (!hasParentHost) {
|
|
@@ -410,6 +415,79 @@ const elementDefine = (inConfig) => (constructor) => {
|
|
|
410
415
|
};
|
|
411
416
|
}
|
|
412
417
|
});
|
|
418
|
+
replaceChildrenMap.forEach((selector, methodName) => {
|
|
419
|
+
const original = NewClass.prototype[methodName];
|
|
420
|
+
if (typeof original === "function") {
|
|
421
|
+
NewClass.prototype[methodName] = function(...args) {
|
|
422
|
+
const res = original.apply(this, args);
|
|
423
|
+
if (res !== void 0) {
|
|
424
|
+
const conf = getElementConfig(this);
|
|
425
|
+
const currentWin = conf?.window || window;
|
|
426
|
+
const currentDoc = currentWin.document;
|
|
427
|
+
let targetEl = null;
|
|
428
|
+
if (selector === ":host" || !selector) {
|
|
429
|
+
targetEl = this;
|
|
430
|
+
} else {
|
|
431
|
+
targetEl = (this.shadowRoot || this).querySelector(selector);
|
|
432
|
+
}
|
|
433
|
+
if (targetEl) {
|
|
434
|
+
const nodes = Array.isArray(res) ? res : [res];
|
|
435
|
+
const normalizedNodes = nodes.map((n) => {
|
|
436
|
+
if (typeof n === "string") {
|
|
437
|
+
const t = currentDoc.createElement("template");
|
|
438
|
+
t.innerHTML = n;
|
|
439
|
+
return t.content;
|
|
440
|
+
}
|
|
441
|
+
return n;
|
|
442
|
+
});
|
|
443
|
+
targetEl.replaceChildren(...normalizedNodes);
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
return res;
|
|
447
|
+
};
|
|
448
|
+
}
|
|
449
|
+
});
|
|
450
|
+
appendChildMap.forEach((meta, methodName) => {
|
|
451
|
+
const { selector, options } = meta;
|
|
452
|
+
const original = NewClass.prototype[methodName];
|
|
453
|
+
if (typeof original === "function") {
|
|
454
|
+
NewClass.prototype[methodName] = function(...args) {
|
|
455
|
+
const res = original.apply(this, args);
|
|
456
|
+
if (res !== void 0) {
|
|
457
|
+
const conf = getElementConfig(this);
|
|
458
|
+
const currentWin = conf?.window || window;
|
|
459
|
+
const currentDoc = currentWin.document;
|
|
460
|
+
let targetEl = null;
|
|
461
|
+
if (selector === ":host" || !selector) {
|
|
462
|
+
targetEl = this;
|
|
463
|
+
} else {
|
|
464
|
+
targetEl = (this.shadowRoot || this).querySelector(selector);
|
|
465
|
+
}
|
|
466
|
+
if (targetEl) {
|
|
467
|
+
const nodes = Array.isArray(res) ? res : [res];
|
|
468
|
+
const normalizedNodes = nodes.map((n) => {
|
|
469
|
+
if (typeof n === "string") {
|
|
470
|
+
const t = currentDoc.createElement("template");
|
|
471
|
+
t.innerHTML = n;
|
|
472
|
+
return t.content;
|
|
473
|
+
}
|
|
474
|
+
return n;
|
|
475
|
+
});
|
|
476
|
+
const pos = options.position || "lastChild";
|
|
477
|
+
if (pos === "lastChild")
|
|
478
|
+
targetEl.append(...normalizedNodes);
|
|
479
|
+
else if (pos === "firstChild")
|
|
480
|
+
targetEl.prepend(...normalizedNodes);
|
|
481
|
+
else if (pos === "nextSibling")
|
|
482
|
+
targetEl.after(...normalizedNodes);
|
|
483
|
+
else if (pos === "previousSibling")
|
|
484
|
+
targetEl.before(...normalizedNodes);
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
return res;
|
|
488
|
+
};
|
|
489
|
+
}
|
|
490
|
+
});
|
|
413
491
|
const originalProto = constructor.prototype;
|
|
414
492
|
const newProto = NewClass.prototype;
|
|
415
493
|
Object.getOwnPropertyNames(originalProto).forEach((name) => {
|