wu-framework 1.0.6 → 1.1.0
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 +773 -366
- package/package.json +34 -9
- package/src/adapters/angular.d.ts +154 -0
- package/src/adapters/angular.js +642 -0
- package/src/adapters/index.js +157 -0
- package/src/adapters/lit.d.ts +120 -0
- package/src/adapters/lit.js +726 -0
- package/src/adapters/preact.d.ts +108 -0
- package/src/adapters/preact.js +665 -0
- package/src/adapters/react.d.ts +212 -0
- package/src/adapters/react.js +513 -0
- package/src/adapters/solid.d.ts +101 -0
- package/src/adapters/solid.js +591 -0
- package/src/adapters/svelte.d.ts +166 -0
- package/src/adapters/svelte.js +803 -0
- package/src/adapters/vanilla.d.ts +179 -0
- package/src/adapters/vanilla.js +791 -0
- package/src/adapters/vue.d.ts +299 -0
- package/src/adapters/vue.js +570 -0
|
@@ -0,0 +1,726 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 🚀 WU-FRAMEWORK LIT ADAPTER
|
|
3
|
+
*
|
|
4
|
+
* Simplifica la integración de Lit (Web Components) con Wu Framework.
|
|
5
|
+
* Aprovecha los Web Components nativos con Shadow DOM incluido.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* // Microfrontend (main.js)
|
|
9
|
+
* import { wuLit } from 'wu-framework/adapters/lit';
|
|
10
|
+
* import { MyApp } from './my-app';
|
|
11
|
+
*
|
|
12
|
+
* wuLit.register('my-app', MyApp);
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* // Usando LitElement
|
|
16
|
+
* import { LitElement, html, css } from 'lit';
|
|
17
|
+
*
|
|
18
|
+
* class MyApp extends LitElement {
|
|
19
|
+
* static styles = css`:host { display: block; }`;
|
|
20
|
+
*
|
|
21
|
+
* render() {
|
|
22
|
+
* return html`<h1>Hello from Lit!</h1>`;
|
|
23
|
+
* }
|
|
24
|
+
* }
|
|
25
|
+
*
|
|
26
|
+
* wuLit.register('my-app', MyApp);
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
// Estado global del adapter
|
|
30
|
+
const adapterState = {
|
|
31
|
+
apps: new Map(),
|
|
32
|
+
elements: new Map(),
|
|
33
|
+
lit: null,
|
|
34
|
+
initialized: false
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Obtiene la instancia de Wu Framework
|
|
39
|
+
*/
|
|
40
|
+
function getWuInstance() {
|
|
41
|
+
if (typeof window === 'undefined') return null;
|
|
42
|
+
|
|
43
|
+
return window.wu
|
|
44
|
+
|| window.parent?.wu
|
|
45
|
+
|| window.top?.wu
|
|
46
|
+
|| null;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Espera a que Wu Framework esté disponible
|
|
51
|
+
*/
|
|
52
|
+
function waitForWu(timeout = 5000) {
|
|
53
|
+
return new Promise((resolve, reject) => {
|
|
54
|
+
const wu = getWuInstance();
|
|
55
|
+
if (wu) {
|
|
56
|
+
resolve(wu);
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const startTime = Date.now();
|
|
61
|
+
|
|
62
|
+
const handleWuReady = () => {
|
|
63
|
+
cleanup();
|
|
64
|
+
resolve(getWuInstance());
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
window.addEventListener('wu:ready', handleWuReady);
|
|
68
|
+
window.addEventListener('wu:app:ready', handleWuReady);
|
|
69
|
+
|
|
70
|
+
const checkInterval = setInterval(() => {
|
|
71
|
+
const wu = getWuInstance();
|
|
72
|
+
if (wu) {
|
|
73
|
+
cleanup();
|
|
74
|
+
resolve(wu);
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (Date.now() - startTime > timeout) {
|
|
79
|
+
cleanup();
|
|
80
|
+
reject(new Error(`Wu Framework not found after ${timeout}ms`));
|
|
81
|
+
}
|
|
82
|
+
}, 200);
|
|
83
|
+
|
|
84
|
+
function cleanup() {
|
|
85
|
+
clearInterval(checkInterval);
|
|
86
|
+
window.removeEventListener('wu:ready', handleWuReady);
|
|
87
|
+
window.removeEventListener('wu:app:ready', handleWuReady);
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Genera un nombre de tag válido para Custom Elements
|
|
94
|
+
*/
|
|
95
|
+
function generateTagName(appName) {
|
|
96
|
+
// Custom elements deben tener un guión
|
|
97
|
+
if (appName.includes('-')) {
|
|
98
|
+
return `wu-${appName}`;
|
|
99
|
+
}
|
|
100
|
+
return `wu-app-${appName}`;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Registra un LitElement como microfrontend
|
|
105
|
+
*
|
|
106
|
+
* @param {string} appName - Nombre único del microfrontend
|
|
107
|
+
* @param {typeof LitElement} ElementClass - Clase que extiende LitElement
|
|
108
|
+
* @param {Object} options - Opciones adicionales
|
|
109
|
+
* @param {string} options.tagName - Nombre del custom element (auto-generado si no se provee)
|
|
110
|
+
* @param {Object} options.properties - Propiedades iniciales
|
|
111
|
+
* @param {Function} options.onMount - Callback después de montar
|
|
112
|
+
* @param {Function} options.onUnmount - Callback antes de desmontar
|
|
113
|
+
* @param {boolean} options.standalone - Permitir ejecución standalone (default: true)
|
|
114
|
+
* @param {string} options.standaloneContainer - Selector para modo standalone (default: '#root')
|
|
115
|
+
*
|
|
116
|
+
* @example
|
|
117
|
+
* import { LitElement, html } from 'lit';
|
|
118
|
+
*
|
|
119
|
+
* class HeaderApp extends LitElement {
|
|
120
|
+
* render() {
|
|
121
|
+
* return html`<header><h1>My Header</h1></header>`;
|
|
122
|
+
* }
|
|
123
|
+
* }
|
|
124
|
+
*
|
|
125
|
+
* wuLit.register('header', HeaderApp);
|
|
126
|
+
*/
|
|
127
|
+
async function register(appName, ElementClass, options = {}) {
|
|
128
|
+
const {
|
|
129
|
+
tagName = null,
|
|
130
|
+
properties = {},
|
|
131
|
+
onMount = null,
|
|
132
|
+
onUnmount = null,
|
|
133
|
+
standalone = true,
|
|
134
|
+
standaloneContainer = '#root'
|
|
135
|
+
} = options;
|
|
136
|
+
|
|
137
|
+
// Generar nombre de tag
|
|
138
|
+
const customTagName = tagName || generateTagName(appName);
|
|
139
|
+
|
|
140
|
+
// Registrar el Custom Element si no existe
|
|
141
|
+
if (!customElements.get(customTagName)) {
|
|
142
|
+
try {
|
|
143
|
+
customElements.define(customTagName, ElementClass);
|
|
144
|
+
console.log(`[WuLit] Custom element <${customTagName}> defined`);
|
|
145
|
+
} catch (error) {
|
|
146
|
+
console.error(`[WuLit] Failed to define custom element:`, error);
|
|
147
|
+
throw error;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Guardar referencia de la clase
|
|
152
|
+
adapterState.elements.set(appName, {
|
|
153
|
+
ElementClass,
|
|
154
|
+
tagName: customTagName
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
// Función de mount
|
|
158
|
+
const mountApp = (container) => {
|
|
159
|
+
if (!container) {
|
|
160
|
+
console.error(`[WuLit] Mount failed for ${appName}: container is null`);
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Evitar doble mount
|
|
165
|
+
if (adapterState.apps.has(appName)) {
|
|
166
|
+
console.warn(`[WuLit] ${appName} already mounted, unmounting first`);
|
|
167
|
+
unmountApp();
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
try {
|
|
171
|
+
// Limpiar container
|
|
172
|
+
container.innerHTML = '';
|
|
173
|
+
|
|
174
|
+
// Crear elemento
|
|
175
|
+
const element = document.createElement(customTagName);
|
|
176
|
+
|
|
177
|
+
// Aplicar propiedades
|
|
178
|
+
Object.entries(properties).forEach(([key, value]) => {
|
|
179
|
+
element[key] = value;
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
// Inyectar información de Wu
|
|
183
|
+
element.wuAppName = appName;
|
|
184
|
+
element.wuInstance = getWuInstance();
|
|
185
|
+
|
|
186
|
+
// Agregar al container
|
|
187
|
+
container.appendChild(element);
|
|
188
|
+
|
|
189
|
+
// Guardar referencia
|
|
190
|
+
adapterState.apps.set(appName, {
|
|
191
|
+
element,
|
|
192
|
+
container,
|
|
193
|
+
tagName: customTagName
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
console.log(`[WuLit] ✅ ${appName} (<${customTagName}>) mounted successfully`);
|
|
197
|
+
|
|
198
|
+
if (onMount) {
|
|
199
|
+
onMount(container, element);
|
|
200
|
+
}
|
|
201
|
+
} catch (error) {
|
|
202
|
+
console.error(`[WuLit] Mount error for ${appName}:`, error);
|
|
203
|
+
throw error;
|
|
204
|
+
}
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
// Función de unmount
|
|
208
|
+
const unmountApp = (container) => {
|
|
209
|
+
const appData = adapterState.apps.get(appName);
|
|
210
|
+
|
|
211
|
+
if (appData) {
|
|
212
|
+
try {
|
|
213
|
+
if (onUnmount) {
|
|
214
|
+
onUnmount(appData.container, appData.element);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// Remover elemento
|
|
218
|
+
if (appData.element && appData.element.parentNode) {
|
|
219
|
+
appData.element.remove();
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// Limpiar container
|
|
223
|
+
appData.container.innerHTML = '';
|
|
224
|
+
|
|
225
|
+
adapterState.apps.delete(appName);
|
|
226
|
+
|
|
227
|
+
console.log(`[WuLit] ✅ ${appName} unmounted successfully`);
|
|
228
|
+
} catch (error) {
|
|
229
|
+
console.error(`[WuLit] Unmount error for ${appName}:`, error);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
if (container) {
|
|
234
|
+
container.innerHTML = '';
|
|
235
|
+
}
|
|
236
|
+
};
|
|
237
|
+
|
|
238
|
+
// Intentar registrar con Wu Framework
|
|
239
|
+
try {
|
|
240
|
+
const wu = await waitForWu(3000);
|
|
241
|
+
|
|
242
|
+
wu.define(appName, {
|
|
243
|
+
mount: mountApp,
|
|
244
|
+
unmount: unmountApp
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
console.log(`[WuLit] ✅ ${appName} registered with Wu Framework`);
|
|
248
|
+
return true;
|
|
249
|
+
|
|
250
|
+
} catch (error) {
|
|
251
|
+
console.warn(`[WuLit] Wu Framework not available for ${appName}`);
|
|
252
|
+
|
|
253
|
+
if (standalone) {
|
|
254
|
+
const containerElement = document.querySelector(standaloneContainer);
|
|
255
|
+
|
|
256
|
+
if (containerElement) {
|
|
257
|
+
console.log(`[WuLit] Running ${appName} in standalone mode`);
|
|
258
|
+
mountApp(containerElement);
|
|
259
|
+
return true;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
return false;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Registra un Web Component vanilla (sin Lit) como microfrontend
|
|
269
|
+
*
|
|
270
|
+
* @param {string} appName - Nombre del microfrontend
|
|
271
|
+
* @param {typeof HTMLElement} ElementClass - Clase que extiende HTMLElement
|
|
272
|
+
* @param {Object} options - Opciones
|
|
273
|
+
*
|
|
274
|
+
* @example
|
|
275
|
+
* class MyWebComponent extends HTMLElement {
|
|
276
|
+
* connectedCallback() {
|
|
277
|
+
* this.attachShadow({ mode: 'open' });
|
|
278
|
+
* this.shadowRoot.innerHTML = '<h1>Hello!</h1>';
|
|
279
|
+
* }
|
|
280
|
+
* }
|
|
281
|
+
*
|
|
282
|
+
* wuLit.registerWebComponent('my-component', MyWebComponent);
|
|
283
|
+
*/
|
|
284
|
+
async function registerWebComponent(appName, ElementClass, options = {}) {
|
|
285
|
+
// Usar el mismo registro pero para HTMLElement vanilla
|
|
286
|
+
return register(appName, ElementClass, options);
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* Crea un LitElement wrapper que carga un microfrontend
|
|
291
|
+
*
|
|
292
|
+
* @example
|
|
293
|
+
* import { html, LitElement } from 'lit';
|
|
294
|
+
* import { createWuSlotElement } from 'wu-framework/adapters/lit';
|
|
295
|
+
*
|
|
296
|
+
* const WuSlot = createWuSlotElement(LitElement, html);
|
|
297
|
+
*
|
|
298
|
+
* // Uso en otro componente
|
|
299
|
+
* render() {
|
|
300
|
+
* return html`<wu-slot name="header" url="http://localhost:3001"></wu-slot>`;
|
|
301
|
+
* }
|
|
302
|
+
*/
|
|
303
|
+
function createWuSlotElement(LitElement, html, css) {
|
|
304
|
+
class WuSlotElement extends LitElement {
|
|
305
|
+
static properties = {
|
|
306
|
+
name: { type: String },
|
|
307
|
+
url: { type: String },
|
|
308
|
+
appName: { type: String, attribute: 'app-name' },
|
|
309
|
+
fallbackText: { type: String, attribute: 'fallback-text' },
|
|
310
|
+
loading: { type: Boolean, state: true },
|
|
311
|
+
error: { type: String, state: true }
|
|
312
|
+
};
|
|
313
|
+
|
|
314
|
+
static styles = css ? css`
|
|
315
|
+
:host {
|
|
316
|
+
display: block;
|
|
317
|
+
min-height: 100px;
|
|
318
|
+
position: relative;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
.wu-slot-loading {
|
|
322
|
+
display: flex;
|
|
323
|
+
align-items: center;
|
|
324
|
+
justify-content: center;
|
|
325
|
+
padding: 2rem;
|
|
326
|
+
color: #666;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
.wu-slot-error {
|
|
330
|
+
padding: 1rem;
|
|
331
|
+
border: 1px solid #f5c6cb;
|
|
332
|
+
border-radius: 4px;
|
|
333
|
+
background: #f8d7da;
|
|
334
|
+
color: #721c24;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
.wu-slot-error strong {
|
|
338
|
+
display: block;
|
|
339
|
+
margin-bottom: 0.5rem;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
.wu-slot-content {
|
|
343
|
+
width: 100%;
|
|
344
|
+
height: 100%;
|
|
345
|
+
}
|
|
346
|
+
` : [];
|
|
347
|
+
|
|
348
|
+
constructor() {
|
|
349
|
+
super();
|
|
350
|
+
this.name = '';
|
|
351
|
+
this.url = '';
|
|
352
|
+
this.appName = null;
|
|
353
|
+
this.fallbackText = null;
|
|
354
|
+
this.loading = true;
|
|
355
|
+
this.error = null;
|
|
356
|
+
this._appInstance = null;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
get actualAppName() {
|
|
360
|
+
return this.appName || this.name;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
async connectedCallback() {
|
|
364
|
+
super.connectedCallback();
|
|
365
|
+
await this.mountMicrofrontend();
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
disconnectedCallback() {
|
|
369
|
+
super.disconnectedCallback();
|
|
370
|
+
this.unmountMicrofrontend();
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
async mountMicrofrontend() {
|
|
374
|
+
try {
|
|
375
|
+
this.loading = true;
|
|
376
|
+
this.error = null;
|
|
377
|
+
|
|
378
|
+
const wu = getWuInstance();
|
|
379
|
+
if (!wu) {
|
|
380
|
+
throw new Error('Wu Framework not initialized');
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
// Esperar a que el componente se renderice
|
|
384
|
+
await this.updateComplete;
|
|
385
|
+
|
|
386
|
+
const contentSlot = this.shadowRoot.querySelector('.wu-slot-content');
|
|
387
|
+
if (!contentSlot) return;
|
|
388
|
+
|
|
389
|
+
const containerId = `wu-slot-${this.actualAppName}-${Date.now()}`;
|
|
390
|
+
const innerContainer = document.createElement('div');
|
|
391
|
+
innerContainer.id = containerId;
|
|
392
|
+
innerContainer.style.cssText = 'width: 100%; height: 100%;';
|
|
393
|
+
|
|
394
|
+
contentSlot.innerHTML = '';
|
|
395
|
+
contentSlot.appendChild(innerContainer);
|
|
396
|
+
|
|
397
|
+
const app = wu.app(this.actualAppName, {
|
|
398
|
+
url: this.url,
|
|
399
|
+
container: `#${containerId}`,
|
|
400
|
+
autoInit: true
|
|
401
|
+
});
|
|
402
|
+
|
|
403
|
+
this._appInstance = app;
|
|
404
|
+
await app.mount();
|
|
405
|
+
|
|
406
|
+
this.loading = false;
|
|
407
|
+
this.dispatchEvent(new CustomEvent('wu-load', {
|
|
408
|
+
detail: { name: this.actualAppName, url: this.url },
|
|
409
|
+
bubbles: true,
|
|
410
|
+
composed: true
|
|
411
|
+
}));
|
|
412
|
+
|
|
413
|
+
} catch (err) {
|
|
414
|
+
console.error(`[WuSlot] Error loading ${this.actualAppName}:`, err);
|
|
415
|
+
this.error = err.message || 'Failed to load microfrontend';
|
|
416
|
+
this.loading = false;
|
|
417
|
+
this.dispatchEvent(new CustomEvent('wu-error', {
|
|
418
|
+
detail: err,
|
|
419
|
+
bubbles: true,
|
|
420
|
+
composed: true
|
|
421
|
+
}));
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
async unmountMicrofrontend() {
|
|
426
|
+
if (this._appInstance) {
|
|
427
|
+
this.dispatchEvent(new CustomEvent('wu-unmount', {
|
|
428
|
+
detail: { name: this.actualAppName },
|
|
429
|
+
bubbles: true,
|
|
430
|
+
composed: true
|
|
431
|
+
}));
|
|
432
|
+
|
|
433
|
+
try {
|
|
434
|
+
await this._appInstance.unmount();
|
|
435
|
+
} catch (e) {}
|
|
436
|
+
|
|
437
|
+
this._appInstance = null;
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
render() {
|
|
442
|
+
if (this.error) {
|
|
443
|
+
return html`
|
|
444
|
+
<div class="wu-slot-error">
|
|
445
|
+
<strong>Error loading ${this.name}</strong>
|
|
446
|
+
<span>${this.error}</span>
|
|
447
|
+
</div>
|
|
448
|
+
`;
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
if (this.loading) {
|
|
452
|
+
return html`
|
|
453
|
+
<div class="wu-slot-loading">
|
|
454
|
+
${this.fallbackText || `Loading ${this.name}...`}
|
|
455
|
+
</div>
|
|
456
|
+
<div class="wu-slot-content"></div>
|
|
457
|
+
`;
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
return html`<div class="wu-slot-content"></div>`;
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
// Registrar el elemento
|
|
465
|
+
if (!customElements.get('wu-slot')) {
|
|
466
|
+
customElements.define('wu-slot', WuSlotElement);
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
return WuSlotElement;
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
/**
|
|
473
|
+
* Mixin para agregar capacidades de Wu a cualquier LitElement
|
|
474
|
+
*
|
|
475
|
+
* @example
|
|
476
|
+
* import { LitElement } from 'lit';
|
|
477
|
+
* import { WuMixin } from 'wu-framework/adapters/lit';
|
|
478
|
+
*
|
|
479
|
+
* class MyElement extends WuMixin(LitElement) {
|
|
480
|
+
* connectedCallback() {
|
|
481
|
+
* super.connectedCallback();
|
|
482
|
+
*
|
|
483
|
+
* // Usar eventos de Wu
|
|
484
|
+
* this.wuOn('user:login', (data) => {
|
|
485
|
+
* console.log('User logged in:', data);
|
|
486
|
+
* });
|
|
487
|
+
* }
|
|
488
|
+
*
|
|
489
|
+
* handleClick() {
|
|
490
|
+
* this.wuEmit('button:clicked', { id: this.id });
|
|
491
|
+
* }
|
|
492
|
+
* }
|
|
493
|
+
*/
|
|
494
|
+
function WuMixin(Base) {
|
|
495
|
+
return class extends Base {
|
|
496
|
+
constructor() {
|
|
497
|
+
super();
|
|
498
|
+
this._wuSubscriptions = [];
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
get wu() {
|
|
502
|
+
return getWuInstance();
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
// Event Bus methods
|
|
506
|
+
wuEmit(event, data, options) {
|
|
507
|
+
const wu = this.wu;
|
|
508
|
+
if (wu?.eventBus) {
|
|
509
|
+
wu.eventBus.emit(event, data, options);
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
wuOn(event, callback) {
|
|
514
|
+
const wu = this.wu;
|
|
515
|
+
if (wu?.eventBus) {
|
|
516
|
+
const unsubscribe = wu.eventBus.on(event, callback);
|
|
517
|
+
this._wuSubscriptions.push(unsubscribe);
|
|
518
|
+
return unsubscribe;
|
|
519
|
+
}
|
|
520
|
+
return () => {};
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
wuOnce(event, callback) {
|
|
524
|
+
const wu = this.wu;
|
|
525
|
+
if (wu?.eventBus) {
|
|
526
|
+
return wu.eventBus.once(event, callback);
|
|
527
|
+
}
|
|
528
|
+
return () => {};
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
// Store methods
|
|
532
|
+
wuGetState(path) {
|
|
533
|
+
const wu = this.wu;
|
|
534
|
+
return wu?.store?.get(path) || null;
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
wuSetState(path, value) {
|
|
538
|
+
const wu = this.wu;
|
|
539
|
+
if (wu?.store) {
|
|
540
|
+
wu.store.set(path, value);
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
wuOnStateChange(pattern, callback) {
|
|
545
|
+
const wu = this.wu;
|
|
546
|
+
if (wu?.store) {
|
|
547
|
+
const unsubscribe = wu.store.on(pattern, callback);
|
|
548
|
+
this._wuSubscriptions.push(unsubscribe);
|
|
549
|
+
return unsubscribe;
|
|
550
|
+
}
|
|
551
|
+
return () => {};
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
// Cleanup
|
|
555
|
+
disconnectedCallback() {
|
|
556
|
+
super.disconnectedCallback();
|
|
557
|
+
this._wuSubscriptions.forEach(unsub => unsub());
|
|
558
|
+
this._wuSubscriptions = [];
|
|
559
|
+
}
|
|
560
|
+
};
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
/**
|
|
564
|
+
* Decorador reactivo para propiedades conectadas al store de Wu
|
|
565
|
+
*
|
|
566
|
+
* @example
|
|
567
|
+
* import { LitElement } from 'lit';
|
|
568
|
+
* import { wuProperty } from 'wu-framework/adapters/lit';
|
|
569
|
+
*
|
|
570
|
+
* class MyElement extends LitElement {
|
|
571
|
+
* @wuProperty('user.name')
|
|
572
|
+
* userName;
|
|
573
|
+
*
|
|
574
|
+
* render() {
|
|
575
|
+
* return html`<p>Hello, ${this.userName}</p>`;
|
|
576
|
+
* }
|
|
577
|
+
* }
|
|
578
|
+
*/
|
|
579
|
+
function wuProperty(storePath) {
|
|
580
|
+
return function(target, propertyKey) {
|
|
581
|
+
const privateKey = `_wu_${propertyKey}`;
|
|
582
|
+
let unsubscribe = null;
|
|
583
|
+
|
|
584
|
+
Object.defineProperty(target, propertyKey, {
|
|
585
|
+
get() {
|
|
586
|
+
return this[privateKey];
|
|
587
|
+
},
|
|
588
|
+
set(value) {
|
|
589
|
+
const wu = getWuInstance();
|
|
590
|
+
if (wu?.store) {
|
|
591
|
+
wu.store.set(storePath, value);
|
|
592
|
+
}
|
|
593
|
+
},
|
|
594
|
+
configurable: true,
|
|
595
|
+
enumerable: true
|
|
596
|
+
});
|
|
597
|
+
|
|
598
|
+
// Hook into connectedCallback
|
|
599
|
+
const originalConnected = target.connectedCallback;
|
|
600
|
+
target.connectedCallback = function() {
|
|
601
|
+
if (originalConnected) originalConnected.call(this);
|
|
602
|
+
|
|
603
|
+
const wu = getWuInstance();
|
|
604
|
+
if (wu?.store) {
|
|
605
|
+
// Set initial value
|
|
606
|
+
this[privateKey] = wu.store.get(storePath);
|
|
607
|
+
|
|
608
|
+
// Subscribe to changes
|
|
609
|
+
unsubscribe = wu.store.on(storePath, (value) => {
|
|
610
|
+
this[privateKey] = value;
|
|
611
|
+
this.requestUpdate();
|
|
612
|
+
});
|
|
613
|
+
}
|
|
614
|
+
};
|
|
615
|
+
|
|
616
|
+
// Hook into disconnectedCallback
|
|
617
|
+
const originalDisconnected = target.disconnectedCallback;
|
|
618
|
+
target.disconnectedCallback = function() {
|
|
619
|
+
if (originalDisconnected) originalDisconnected.call(this);
|
|
620
|
+
if (unsubscribe) {
|
|
621
|
+
unsubscribe();
|
|
622
|
+
unsubscribe = null;
|
|
623
|
+
}
|
|
624
|
+
};
|
|
625
|
+
};
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
/**
|
|
629
|
+
* Helper para crear un Web Component simple sin Lit
|
|
630
|
+
*
|
|
631
|
+
* @example
|
|
632
|
+
* const MyComponent = createSimpleElement({
|
|
633
|
+
* name: 'my-component',
|
|
634
|
+
* template: '<h1>Hello!</h1>',
|
|
635
|
+
* styles: ':host { display: block; color: blue; }',
|
|
636
|
+
* connectedCallback() {
|
|
637
|
+
* console.log('Connected!');
|
|
638
|
+
* }
|
|
639
|
+
* });
|
|
640
|
+
*/
|
|
641
|
+
function createSimpleElement(config) {
|
|
642
|
+
const {
|
|
643
|
+
name,
|
|
644
|
+
template,
|
|
645
|
+
styles = '',
|
|
646
|
+
shadow = true,
|
|
647
|
+
...callbacks
|
|
648
|
+
} = config;
|
|
649
|
+
|
|
650
|
+
class SimpleElement extends HTMLElement {
|
|
651
|
+
constructor() {
|
|
652
|
+
super();
|
|
653
|
+
if (shadow) {
|
|
654
|
+
this.attachShadow({ mode: 'open' });
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
connectedCallback() {
|
|
659
|
+
const root = this.shadowRoot || this;
|
|
660
|
+
|
|
661
|
+
if (styles) {
|
|
662
|
+
const styleEl = document.createElement('style');
|
|
663
|
+
styleEl.textContent = styles;
|
|
664
|
+
root.appendChild(styleEl);
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
if (typeof template === 'function') {
|
|
668
|
+
root.innerHTML += template(this);
|
|
669
|
+
} else {
|
|
670
|
+
root.innerHTML += template;
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
if (callbacks.connectedCallback) {
|
|
674
|
+
callbacks.connectedCallback.call(this);
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
disconnectedCallback() {
|
|
679
|
+
if (callbacks.disconnectedCallback) {
|
|
680
|
+
callbacks.disconnectedCallback.call(this);
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
attributeChangedCallback(name, oldVal, newVal) {
|
|
685
|
+
if (callbacks.attributeChangedCallback) {
|
|
686
|
+
callbacks.attributeChangedCallback.call(this, name, oldVal, newVal);
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
if (callbacks.observedAttributes) {
|
|
692
|
+
SimpleElement.observedAttributes = callbacks.observedAttributes;
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
if (!customElements.get(name)) {
|
|
696
|
+
customElements.define(name, SimpleElement);
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
return SimpleElement;
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
// API pública del adapter
|
|
703
|
+
export const wuLit = {
|
|
704
|
+
register,
|
|
705
|
+
registerWebComponent,
|
|
706
|
+
createWuSlotElement,
|
|
707
|
+
WuMixin,
|
|
708
|
+
wuProperty,
|
|
709
|
+
createSimpleElement,
|
|
710
|
+
getWuInstance,
|
|
711
|
+
waitForWu
|
|
712
|
+
};
|
|
713
|
+
|
|
714
|
+
// Named exports
|
|
715
|
+
export {
|
|
716
|
+
register,
|
|
717
|
+
registerWebComponent,
|
|
718
|
+
createWuSlotElement,
|
|
719
|
+
WuMixin,
|
|
720
|
+
wuProperty,
|
|
721
|
+
createSimpleElement,
|
|
722
|
+
getWuInstance,
|
|
723
|
+
waitForWu
|
|
724
|
+
};
|
|
725
|
+
|
|
726
|
+
export default wuLit;
|