slicejs-web-framework 3.2.1 → 3.2.3
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/.opencode/opencode.json +14 -0
- package/LICENSE +21 -21
- package/README.md +174 -174
- package/Slice/Components/Structural/ContextManager/ContextManager.js +369 -369
- package/Slice/Components/Structural/ContextManager/ContextManagerDebugger.js +297 -297
- package/Slice/Components/Structural/Controller/Controller.js +1138 -1129
- package/Slice/Components/Structural/Controller/allowedValuesValidation.js +52 -0
- package/Slice/Components/Structural/Debugger/Debugger.css +619 -619
- package/Slice/Components/Structural/Debugger/Debugger.html +72 -72
- package/Slice/Components/Structural/Debugger/Debugger.js +1547 -1547
- package/Slice/Components/Structural/EventManager/EventManager.js +338 -338
- package/Slice/Components/Structural/EventManager/EventManagerDebugger.js +361 -361
- package/Slice/Components/Structural/Logger/Log.js +10 -10
- package/Slice/Components/Structural/Logger/Logger.js +146 -146
- package/Slice/Components/Structural/Router/Router.js +721 -721
- package/Slice/Components/Structural/StylesManager/StylesManager.js +78 -78
- package/Slice/Components/Structural/StylesManager/ThemeManager/ThemeManager.js +84 -84
- package/Slice/Slice.js +542 -542
- package/Slice/tests/build-bundled-component-without-category.test.js +103 -103
- package/Slice/tests/build-js-only-visual-components.test.js +144 -144
- package/Slice/tests/bundle-v2-runtime-contract.test.js +728 -728
- package/Slice/tests/props-allowed-values-validation.test.js +119 -0
- package/Slice/tests/public-env-runtime-accessors.test.js +44 -44
- package/Slice/tests/router-loading-finally.test.js +68 -68
- package/api/index.js +286 -286
- package/api/middleware/securityMiddleware.js +252 -252
- package/api/tests/public-env-resolver.test.js +193 -193
- package/api/utils/publicEnvResolver.js +117 -117
- package/package.json +38 -37
- package/sliceConfig.schema.json +109 -109
- package/src/App/index.html +22 -22
- package/src/App/index.js +23 -23
- package/src/App/style.css +40 -40
- package/src/Components/AppComponents/HomePage/HomePage.css +201 -201
- package/src/Components/AppComponents/HomePage/HomePage.html +37 -37
- package/src/Components/AppComponents/HomePage/HomePage.js +210 -210
- package/src/Components/AppComponents/Playground/Playground.css +11 -11
- package/src/Components/AppComponents/Playground/Playground.js +111 -111
- package/src/Components/Service/FetchManager/FetchManager.js +133 -133
- package/src/Components/Service/IndexedDbManager/IndexedDbManager.js +141 -141
- package/src/Components/Service/LocalStorageManager/LocalStorageManager.js +45 -45
- package/src/Components/Visual/Button/Button.css +47 -47
- package/src/Components/Visual/Button/Button.html +5 -5
- package/src/Components/Visual/Button/Button.js +92 -92
- package/src/Components/Visual/Card/Card.css +68 -68
- package/src/Components/Visual/Card/Card.html +7 -7
- package/src/Components/Visual/Card/Card.js +107 -107
- package/src/Components/Visual/Checkbox/Checkbox.css +87 -87
- package/src/Components/Visual/Checkbox/Checkbox.html +8 -8
- package/src/Components/Visual/Checkbox/Checkbox.js +86 -86
- package/src/Components/Visual/CodeVisualizer/CodeVisualizer.css +129 -129
- package/src/Components/Visual/CodeVisualizer/CodeVisualizer.html +3 -3
- package/src/Components/Visual/CodeVisualizer/CodeVisualizer.js +262 -262
- package/src/Components/Visual/Details/Details.css +70 -70
- package/src/Components/Visual/Details/Details.html +9 -9
- package/src/Components/Visual/Details/Details.js +76 -76
- package/src/Components/Visual/DropDown/DropDown.css +60 -60
- package/src/Components/Visual/DropDown/DropDown.html +5 -5
- package/src/Components/Visual/DropDown/DropDown.js +63 -63
- package/src/Components/Visual/Grid/Grid.css +7 -7
- package/src/Components/Visual/Grid/Grid.html +1 -1
- package/src/Components/Visual/Grid/Grid.js +57 -57
- package/src/Components/Visual/Icon/Icon.css +510 -510
- package/src/Components/Visual/Icon/Icon.js +89 -89
- package/src/Components/Visual/Icon/slc.json +554 -554
- package/src/Components/Visual/Icon/slc.styl +507 -507
- package/src/Components/Visual/Icon/slc.svg +1485 -1485
- package/src/Components/Visual/Icon/slc.symbol.svg +1058 -1058
- package/src/Components/Visual/Input/Input.css +91 -91
- package/src/Components/Visual/Input/Input.html +4 -4
- package/src/Components/Visual/Input/Input.js +215 -215
- package/src/Components/Visual/Layout/Layout.js +49 -49
- package/src/Components/Visual/Link/Link.css +8 -8
- package/src/Components/Visual/Link/Link.html +1 -1
- package/src/Components/Visual/Link/Link.js +63 -63
- package/src/Components/Visual/Loading/Loading.css +56 -56
- package/src/Components/Visual/Loading/Loading.html +83 -83
- package/src/Components/Visual/Loading/Loading.js +38 -38
- package/src/Components/Visual/MultiRoute/MultiRoute.js +93 -93
- package/src/Components/Visual/Navbar/Navbar.css +115 -115
- package/src/Components/Visual/Navbar/Navbar.html +44 -44
- package/src/Components/Visual/Navbar/Navbar.js +141 -141
- package/src/Components/Visual/NotFound/NotFound.css +116 -116
- package/src/Components/Visual/NotFound/NotFound.html +23 -23
- package/src/Components/Visual/NotFound/NotFound.js +16 -16
- package/src/Components/Visual/Route/Route.js +93 -93
- package/src/Components/Visual/Select/Select.css +84 -84
- package/src/Components/Visual/Select/Select.html +8 -8
- package/src/Components/Visual/Select/Select.js +195 -195
- package/src/Components/Visual/Switch/Switch.css +76 -76
- package/src/Components/Visual/Switch/Switch.html +8 -8
- package/src/Components/Visual/Switch/Switch.js +102 -102
- package/src/Components/Visual/TreeItem/TreeItem.css +36 -36
- package/src/Components/Visual/TreeItem/TreeItem.html +1 -1
- package/src/Components/Visual/TreeItem/TreeItem.js +126 -126
- package/src/Components/Visual/TreeView/TreeView.css +8 -8
- package/src/Components/Visual/TreeView/TreeView.html +1 -1
- package/src/Components/Visual/TreeView/TreeView.js +48 -48
- package/src/Components/components.js +27 -27
- package/src/Styles/sliceStyles.css +34 -34
- package/src/Themes/Dark.css +42 -42
- package/src/Themes/Light.css +31 -31
- package/src/Themes/Slice.css +47 -47
- package/src/routes.js +15 -15
- package/src/sliceConfig.json +73 -73
- package/src/testing.js +887 -887
- package/types/index.d.ts +207 -0
|
@@ -1,1140 +1,1149 @@
|
|
|
1
1
|
import components from '/Components/components.js';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
this.
|
|
7
|
-
this.
|
|
8
|
-
this.
|
|
9
|
-
this.
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
//
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
this.
|
|
18
|
-
this.
|
|
19
|
-
this.
|
|
20
|
-
this.
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
this.
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
* @
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
const
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
*
|
|
85
|
-
*
|
|
86
|
-
* @param {
|
|
87
|
-
* @
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
const
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
const
|
|
145
|
-
const
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
||
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|| registerResult
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
template
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
.replace(/export\s+
|
|
346
|
-
.replace(/export\s+
|
|
347
|
-
.replace(/export\s+
|
|
348
|
-
.replace(/export\s+
|
|
349
|
-
.replace(/export\s
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
.
|
|
353
|
-
|
|
354
|
-
const
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
window.
|
|
367
|
-
window,
|
|
368
|
-
window
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
console.warn(
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
window.
|
|
378
|
-
window,
|
|
379
|
-
window
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
.replace(/export\s+
|
|
413
|
-
.replace(/export\s+
|
|
414
|
-
.replace(/export\s+
|
|
415
|
-
.replace(/export\s
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
.
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
'
|
|
434
|
-
'
|
|
435
|
-
'
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
console.warn(
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
//
|
|
467
|
-
|
|
468
|
-
const
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
const
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
template
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
*
|
|
547
|
-
* @
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
if
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
//
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
let
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
*
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
if
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
*
|
|
746
|
-
* @
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
const
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
*
|
|
772
|
-
* @param {
|
|
773
|
-
* @param {
|
|
774
|
-
* @param {string} [
|
|
775
|
-
* @
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
*
|
|
834
|
-
* @param {
|
|
835
|
-
* @
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
const
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
component[prop] =
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
component[prop] =
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
2
|
+
import { collectInvalidAllowedValueProps, formatAllowedValuesForLog } from './allowedValuesValidation.js';
|
|
3
|
+
|
|
4
|
+
export default class Controller {
|
|
5
|
+
constructor() {
|
|
6
|
+
this.componentCategories = new Map(Object.entries(components));
|
|
7
|
+
this.templates = new Map();
|
|
8
|
+
this.classes = new Map();
|
|
9
|
+
this.requestedStyles = new Set(); // ✅ CRÍTICO: Para tracking de CSS cargados
|
|
10
|
+
this.activeComponents = new Map();
|
|
11
|
+
|
|
12
|
+
// 🚀 OPTIMIZACIÓN: Índice inverso para búsqueda rápida de hijos
|
|
13
|
+
// parentSliceId → Set<childSliceId>
|
|
14
|
+
this.childrenIndex = new Map();
|
|
15
|
+
|
|
16
|
+
// 📦 Bundle system
|
|
17
|
+
this.loadedBundles = new Set();
|
|
18
|
+
this.bundleConfig = null;
|
|
19
|
+
this.criticalBundleLoaded = false;
|
|
20
|
+
this.bundleImportPromises = new Map();
|
|
21
|
+
this.bundleLoadPromises = new Map();
|
|
22
|
+
|
|
23
|
+
this.idCounter = 0;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* 📦 Initializes bundle system (called automatically when config is loaded)
|
|
28
|
+
*/
|
|
29
|
+
initializeBundles(config = null) {
|
|
30
|
+
if (config) {
|
|
31
|
+
this.bundleConfig = config;
|
|
32
|
+
|
|
33
|
+
// Register critical bundle components if available
|
|
34
|
+
if (config.bundles?.critical) {
|
|
35
|
+
// Critical bundle will be loaded explicitly
|
|
36
|
+
}
|
|
37
|
+
this.criticalBundleLoaded = false;
|
|
38
|
+
} else {
|
|
39
|
+
// No bundles available, will use individual component loading
|
|
40
|
+
this.bundleConfig = null;
|
|
41
|
+
this.criticalBundleLoaded = false;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Import a bundle URL once per page session.
|
|
47
|
+
* Reuses the same Promise for concurrent callers.
|
|
48
|
+
* @param {string} bundlePath
|
|
49
|
+
* @returns {Promise<any>}
|
|
50
|
+
*/
|
|
51
|
+
importBundleOnce(bundlePath) {
|
|
52
|
+
if (!bundlePath) {
|
|
53
|
+
return Promise.reject(new Error('Bundle path is required'));
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (this.bundleImportPromises.has(bundlePath)) {
|
|
57
|
+
return this.bundleImportPromises.get(bundlePath);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const importPromise = import(bundlePath).catch((error) => {
|
|
61
|
+
this.bundleImportPromises.delete(bundlePath);
|
|
62
|
+
throw error;
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
this.bundleImportPromises.set(bundlePath, importPromise);
|
|
66
|
+
return importPromise;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
buildBundleImportPath(bundleInfo) {
|
|
70
|
+
if (!bundleInfo || typeof bundleInfo.file !== 'string' || bundleInfo.file.length === 0) {
|
|
71
|
+
throw new Error('Bundle file is required');
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const basePath = `/bundles/${bundleInfo.file}`;
|
|
75
|
+
const bundleHash = typeof bundleInfo.hash === 'string' ? bundleInfo.hash.trim() : '';
|
|
76
|
+
if (!bundleHash) {
|
|
77
|
+
return basePath;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return `${basePath}?v=${encodeURIComponent(bundleHash)}`;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Validate Bundling V2 module contract.
|
|
85
|
+
* Requires named exports: SLICE_BUNDLE_META and registerAll.
|
|
86
|
+
* @param {any} bundleModule
|
|
87
|
+
* @param {string} [bundleName]
|
|
88
|
+
* @returns {{metadata: object, registerAll: Function}}
|
|
89
|
+
*/
|
|
90
|
+
async validateBundleModule(bundleModule, bundleName = 'unknown') {
|
|
91
|
+
const metadata = bundleModule?.SLICE_BUNDLE_META;
|
|
92
|
+
const registerAll = bundleModule?.registerAll;
|
|
93
|
+
|
|
94
|
+
if (!metadata || typeof metadata !== 'object' || typeof registerAll !== 'function') {
|
|
95
|
+
throw new Error(
|
|
96
|
+
`Bundle "${bundleName}" missing Bundling V2 exports contract: requires SLICE_BUNDLE_META and registerAll`
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return { metadata, registerAll };
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* 📦 Loads a bundle by name or category
|
|
105
|
+
*/
|
|
106
|
+
async loadBundle(bundleName) {
|
|
107
|
+
const resolvedBundleName = this.resolveBundleName(bundleName);
|
|
108
|
+
if (this.loadedBundles.has(resolvedBundleName)) {
|
|
109
|
+
return; // Already loaded
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return this.loadBundleWithDependencies(resolvedBundleName, new Set());
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
async loadBundleWithDependencies(bundleName, loadingStack = new Set()) {
|
|
116
|
+
const resolvedBundleName = this.resolveBundleName(bundleName);
|
|
117
|
+
|
|
118
|
+
if (this.loadedBundles.has(resolvedBundleName)) {
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (loadingStack.has(resolvedBundleName)) {
|
|
123
|
+
throw new Error(`Circular bundle dependency detected: ${Array.from(loadingStack).join(' -> ')} -> ${resolvedBundleName}`);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if (this.bundleLoadPromises.has(resolvedBundleName)) {
|
|
127
|
+
return this.bundleLoadPromises.get(resolvedBundleName);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const loadPromise = (async () => {
|
|
131
|
+
loadingStack.add(resolvedBundleName);
|
|
132
|
+
try {
|
|
133
|
+
const bundleInfo = this.getBundleInfo(resolvedBundleName);
|
|
134
|
+
if (!bundleInfo) {
|
|
135
|
+
console.warn(`Bundle ${resolvedBundleName} not found in configuration`);
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const dependencies = this.getBundleDependencies(bundleInfo);
|
|
140
|
+
for (const dependencyName of dependencies) {
|
|
141
|
+
await this.loadBundleWithDependencies(dependencyName, loadingStack);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const bundlePath = this.buildBundleImportPath(bundleInfo);
|
|
145
|
+
const bundleModule = await this.importBundleOnce(bundlePath);
|
|
146
|
+
const { metadata, registerAll } = await this.validateBundleModule(bundleModule, resolvedBundleName);
|
|
147
|
+
|
|
148
|
+
const registerResult = await registerAll(this, slice.stylesManager);
|
|
149
|
+
this.registerVendorSharedDependencies(bundleModule, metadata, resolvedBundleName, registerResult);
|
|
150
|
+
|
|
151
|
+
this.loadedBundles.add(resolvedBundleName);
|
|
152
|
+
const loadedBundleKey = metadata.bundleKey;
|
|
153
|
+
if (loadedBundleKey && loadedBundleKey !== resolvedBundleName) {
|
|
154
|
+
this.loadedBundles.add(loadedBundleKey);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
if (metadata.type === 'critical' || resolvedBundleName === 'critical') {
|
|
158
|
+
this.criticalBundleLoaded = true;
|
|
159
|
+
}
|
|
160
|
+
} finally {
|
|
161
|
+
loadingStack.delete(resolvedBundleName);
|
|
162
|
+
}
|
|
163
|
+
})();
|
|
164
|
+
|
|
165
|
+
this.bundleLoadPromises.set(resolvedBundleName, loadPromise);
|
|
166
|
+
try {
|
|
167
|
+
return await loadPromise;
|
|
168
|
+
} finally {
|
|
169
|
+
this.bundleLoadPromises.delete(resolvedBundleName);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
resolveBundleName(bundleName) {
|
|
174
|
+
if (typeof bundleName !== 'string' || bundleName.length === 0) {
|
|
175
|
+
return bundleName;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
if (bundleName.toLowerCase() === 'critical') {
|
|
179
|
+
return 'critical';
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
if (this.isVendorSharedAlias(bundleName) && this.getVendorSharedBundleInfo()) {
|
|
183
|
+
return 'vendor-shared';
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
const routeBundleName = this.findBundleNameByAlias(this.bundleConfig?.bundles?.routes, bundleName);
|
|
187
|
+
if (routeBundleName) {
|
|
188
|
+
return routeBundleName;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
const sharedBundleName = this.findBundleNameByAlias(this.bundleConfig?.bundles?.shared, bundleName);
|
|
192
|
+
if (sharedBundleName) {
|
|
193
|
+
return sharedBundleName;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
return bundleName;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
findBundleNameByAlias(bundleRegistry, bundleName) {
|
|
200
|
+
if (!bundleRegistry || typeof bundleRegistry !== 'object') {
|
|
201
|
+
return null;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
if (bundleRegistry[bundleName]) {
|
|
205
|
+
return bundleName;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
const normalizedName = bundleName?.toLowerCase();
|
|
209
|
+
if (!normalizedName) {
|
|
210
|
+
return null;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
return Object.keys(bundleRegistry).find((key) => key.toLowerCase() === normalizedName) || null;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
getBundleDependencies(bundleInfo) {
|
|
217
|
+
if (!bundleInfo || !Array.isArray(bundleInfo.dependencies)) {
|
|
218
|
+
return [];
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
return bundleInfo.dependencies.filter((dependency) => typeof dependency === 'string' && dependency.length > 0);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
findBundleEntryByName(bundleRegistry, bundleName) {
|
|
225
|
+
if (!bundleRegistry || typeof bundleRegistry !== 'object') {
|
|
226
|
+
return null;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
if (bundleRegistry[bundleName]) {
|
|
230
|
+
return bundleRegistry[bundleName];
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
const normalizedName = bundleName?.toLowerCase();
|
|
234
|
+
if (!normalizedName) {
|
|
235
|
+
return null;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
const matchedKey = Object.keys(bundleRegistry).find((key) => key.toLowerCase() === normalizedName);
|
|
239
|
+
return matchedKey ? bundleRegistry[matchedKey] : null;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
getBundleInfo(bundleName) {
|
|
243
|
+
if (bundleName === 'critical') {
|
|
244
|
+
return this.bundleConfig?.bundles?.critical || null;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
if (this.isVendorSharedAlias(bundleName)) {
|
|
248
|
+
return this.getVendorSharedBundleInfo();
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
return (
|
|
252
|
+
this.findBundleEntryByName(this.bundleConfig?.bundles?.routes, bundleName)
|
|
253
|
+
|| this.findBundleEntryByName(this.bundleConfig?.bundles?.shared, bundleName)
|
|
254
|
+
);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
getVendorSharedBundleInfo() {
|
|
258
|
+
if (this.bundleConfig?.bundles?.vendorShared && typeof this.bundleConfig.bundles.vendorShared === 'object') {
|
|
259
|
+
return this.bundleConfig.bundles.vendorShared;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
return this.findBundleEntryByName(this.bundleConfig?.bundles?.shared, 'vendor-shared');
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
isVendorSharedAlias(bundleName) {
|
|
266
|
+
if (typeof bundleName !== 'string') {
|
|
267
|
+
return false;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
const normalized = bundleName.toLowerCase();
|
|
271
|
+
return normalized === 'vendor-shared' || normalized === 'vendorshared';
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
registerVendorSharedDependencies(bundleModule, metadata, bundleName, registerResult) {
|
|
275
|
+
const isVendorShared = this.isVendorSharedBundleName(metadata?.bundleKey)
|
|
276
|
+
|| this.isVendorSharedBundleName(bundleName)
|
|
277
|
+
|| metadata?.registerVendorSharedDependencies === true;
|
|
278
|
+
|
|
279
|
+
if (!isVendorShared) {
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
let sharedDeps = bundleModule?.SLICE_SHARED_DEPS;
|
|
284
|
+
if (!sharedDeps && registerResult && typeof registerResult === 'object') {
|
|
285
|
+
sharedDeps = registerResult.SLICE_SHARED_DEPS
|
|
286
|
+
|| registerResult.SLICE_BUNDLE_DEPENDENCIES
|
|
287
|
+
|| registerResult;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
if (!sharedDeps || typeof sharedDeps !== 'object') {
|
|
291
|
+
return;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
if (!window.__SLICE_SHARED_DEPS__ || typeof window.__SLICE_SHARED_DEPS__ !== 'object') {
|
|
295
|
+
window.__SLICE_SHARED_DEPS__ = {};
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
Object.assign(window.__SLICE_SHARED_DEPS__, sharedDeps);
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
isVendorSharedBundleName(bundleName) {
|
|
302
|
+
return typeof bundleName === 'string' && bundleName.toLowerCase() === 'vendor-shared';
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* 📦 Registers a bundle's components (called automatically by bundle files)
|
|
307
|
+
*/
|
|
308
|
+
registerBundleLegacy(bundle) {
|
|
309
|
+
const { components, metadata } = bundle;
|
|
310
|
+
|
|
311
|
+
console.log(`📦 Registering bundle: ${metadata.type} (${metadata.componentCount} components)`);
|
|
312
|
+
|
|
313
|
+
// Phase 1: Register templates and CSS for all components first
|
|
314
|
+
for (const [componentName, componentData] of Object.entries(components)) {
|
|
315
|
+
try {
|
|
316
|
+
// Register HTML template
|
|
317
|
+
if (componentData.html !== undefined && !this.templates.has(componentName)) {
|
|
318
|
+
const template = document.createElement('template');
|
|
319
|
+
template.innerHTML = componentData.html || '';
|
|
320
|
+
this.templates.set(componentName, template);
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// Register CSS styles
|
|
324
|
+
if (componentData.css !== undefined && !this.requestedStyles.has(componentName)) {
|
|
325
|
+
// Use the existing stylesManager to register component styles
|
|
326
|
+
if (window.slice && window.slice.stylesManager) {
|
|
327
|
+
window.slice.stylesManager.registerComponentStyles(componentName, componentData.css || '');
|
|
328
|
+
this.requestedStyles.add(componentName);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
} catch (error) {
|
|
332
|
+
console.warn(`❌ Failed to register assets for ${componentName}:`, error);
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
// Phase 2: Evaluate all external file dependencies
|
|
337
|
+
const processedDeps = new Set();
|
|
338
|
+
for (const [componentName, componentData] of Object.entries(components)) {
|
|
339
|
+
if (componentData.dependencies) {
|
|
340
|
+
for (const [depName, depContent] of Object.entries(componentData.dependencies)) {
|
|
341
|
+
if (!processedDeps.has(depName)) {
|
|
342
|
+
try {
|
|
343
|
+
// Convert ES6 exports to global assignments
|
|
344
|
+
let processedContent = depContent
|
|
345
|
+
.replace(/export\s+const\s+(\w+)\s*=/g, 'window.$1 =')
|
|
346
|
+
.replace(/export\s+let\s+(\w+)\s*=/g, 'window.$1 =')
|
|
347
|
+
.replace(/export\s+var\s+(\w+)\s*=/g, 'window.$1 =')
|
|
348
|
+
.replace(/export\s+function\s+(\w+)/g, 'window.$1 = function')
|
|
349
|
+
.replace(/export\s+default\s+/g, 'window.defaultExport =')
|
|
350
|
+
.replace(/export\s*{\s*([^}]+)\s*}/g, (match, exports) => {
|
|
351
|
+
return exports
|
|
352
|
+
.split(',')
|
|
353
|
+
.map((exp) => {
|
|
354
|
+
const cleanExp = exp.trim();
|
|
355
|
+
const varName = cleanExp.split(' as ')[0].trim();
|
|
356
|
+
return `window.${varName} = ${varName};`;
|
|
357
|
+
})
|
|
358
|
+
.join('\n');
|
|
359
|
+
})
|
|
360
|
+
// Remove any remaining export keywords
|
|
361
|
+
.replace(/^\s*export\s+/gm, '');
|
|
362
|
+
|
|
363
|
+
// Evaluate the dependency
|
|
364
|
+
try {
|
|
365
|
+
new Function('slice', 'customElements', 'window', 'document', processedContent)(
|
|
366
|
+
window.slice,
|
|
367
|
+
window.customElements,
|
|
368
|
+
window,
|
|
369
|
+
window.document
|
|
370
|
+
);
|
|
371
|
+
} catch (evalError) {
|
|
372
|
+
console.warn(`❌ Failed to evaluate processed dependency ${depName}:`, evalError);
|
|
373
|
+
console.warn('Processed content preview:', processedContent.substring(0, 200));
|
|
374
|
+
// Try evaluating the original content as fallback
|
|
375
|
+
try {
|
|
376
|
+
new Function('slice', 'customElements', 'window', 'document', depContent)(
|
|
377
|
+
window.slice,
|
|
378
|
+
window.customElements,
|
|
379
|
+
window,
|
|
380
|
+
window.document
|
|
381
|
+
);
|
|
382
|
+
console.log(`✅ Fallback evaluation succeeded for ${depName}`);
|
|
383
|
+
} catch (fallbackError) {
|
|
384
|
+
console.warn(`❌ Fallback evaluation also failed for ${depName}:`, fallbackError);
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
processedDeps.add(depName);
|
|
389
|
+
console.log(`📄 Dependency loaded: ${depName}`);
|
|
390
|
+
} catch (depError) {
|
|
391
|
+
console.warn(`⚠️ Failed to load dependency ${depName} for ${componentName}:`, depError);
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
// Phase 3: Evaluate all component classes (now that dependencies are available)
|
|
399
|
+
for (const [componentName, componentData] of Object.entries(components)) {
|
|
400
|
+
// For JavaScript classes, we need to evaluate the code
|
|
401
|
+
if (componentData.js && !this.classes.has(componentName)) {
|
|
402
|
+
try {
|
|
403
|
+
// Create evaluation context with dependencies
|
|
404
|
+
let evalCode = componentData.js;
|
|
405
|
+
|
|
406
|
+
// Prepend dependencies to make them available
|
|
407
|
+
if (componentData.dependencies) {
|
|
408
|
+
const depCode = Object.entries(componentData.dependencies)
|
|
409
|
+
.map(([depName, depContent]) => {
|
|
410
|
+
// Convert ES6 exports to global assignments
|
|
411
|
+
return depContent
|
|
412
|
+
.replace(/export\s+const\s+(\w+)\s*=/g, 'window.$1 =')
|
|
413
|
+
.replace(/export\s+let\s+(\w+)\s*=/g, 'window.$1 =')
|
|
414
|
+
.replace(/export\s+function\s+(\w+)/g, 'window.$1 = function')
|
|
415
|
+
.replace(/export\s+default\s+/g, 'window.defaultExport =')
|
|
416
|
+
.replace(/export\s*{\s*([^}]+)\s*}/g, (match, exports) => {
|
|
417
|
+
return exports
|
|
418
|
+
.split(',')
|
|
419
|
+
.map((exp) => {
|
|
420
|
+
const cleanExp = exp.trim();
|
|
421
|
+
return `window.${cleanExp} = ${cleanExp};`;
|
|
422
|
+
})
|
|
423
|
+
.join('\n');
|
|
424
|
+
});
|
|
425
|
+
})
|
|
426
|
+
.join('\n\n');
|
|
427
|
+
|
|
428
|
+
evalCode = depCode + '\n\n' + evalCode;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
// Evaluate the complete code
|
|
432
|
+
const componentClass = new Function(
|
|
433
|
+
'slice',
|
|
434
|
+
'customElements',
|
|
435
|
+
'window',
|
|
436
|
+
'document',
|
|
437
|
+
`
|
|
438
|
+
"use strict";
|
|
439
|
+
${evalCode}
|
|
440
|
+
return ${componentName};
|
|
441
|
+
`
|
|
442
|
+
)(window.slice, window.customElements, window, window.document);
|
|
443
|
+
|
|
444
|
+
if (componentClass) {
|
|
445
|
+
this.classes.set(componentName, componentClass);
|
|
446
|
+
console.log(`📝 Class registered for: ${componentName}`);
|
|
447
|
+
}
|
|
448
|
+
} catch (error) {
|
|
449
|
+
console.warn(`❌ Failed to evaluate class for ${componentName}:`, error);
|
|
450
|
+
console.warn('Code that failed:', componentData.js.substring(0, 200) + '...');
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
/**
|
|
457
|
+
* 📦 New bundle registration method (simplified and robust)
|
|
458
|
+
*/
|
|
459
|
+
registerBundle(bundle) {
|
|
460
|
+
const validation = this.validateBundle(bundle);
|
|
461
|
+
if (!validation.isValid) {
|
|
462
|
+
console.warn(`❌ Bundle validation failed: ${validation.error}`);
|
|
463
|
+
return Promise.resolve(false);
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
// Set tracking flags synchronously before any async work, so callers that
|
|
467
|
+
// await import() see the flags set immediately when the Promise resolves.
|
|
468
|
+
const { components, metadata } = bundle;
|
|
469
|
+
const bundleKey = metadata?.bundleKey;
|
|
470
|
+
if (bundleKey) {
|
|
471
|
+
this.loadedBundles.add(bundleKey);
|
|
472
|
+
if (metadata?.type === 'critical') {
|
|
473
|
+
this.criticalBundleLoaded = true;
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
console.log(`📦 Registering bundle: ${metadata.type} (${metadata.componentCount} components)`);
|
|
478
|
+
|
|
479
|
+
const entries = Object.entries(components);
|
|
480
|
+
const chunkSize = 50;
|
|
481
|
+
let index = 0;
|
|
482
|
+
|
|
483
|
+
return new Promise((resolve) => {
|
|
484
|
+
const processChunk = () => {
|
|
485
|
+
const sliceEntries = entries.slice(index, index + chunkSize);
|
|
486
|
+
|
|
487
|
+
for (const [componentName, componentData] of sliceEntries) {
|
|
488
|
+
try {
|
|
489
|
+
if (componentData.html !== undefined && !this.templates.has(componentName)) {
|
|
490
|
+
const template = document.createElement('template');
|
|
491
|
+
template.innerHTML = componentData.html || '';
|
|
492
|
+
this.templates.set(componentName, template);
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
if (componentData.css !== undefined && !this.requestedStyles.has(componentName)) {
|
|
496
|
+
if (window.slice && window.slice.stylesManager) {
|
|
497
|
+
window.slice.stylesManager.registerComponentStyles(componentName, componentData.css || '');
|
|
498
|
+
this.requestedStyles.add(componentName);
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
if (componentData.class && !this.classes.has(componentName)) {
|
|
503
|
+
const registeredName = componentData.isFramework
|
|
504
|
+
? `Framework/Structural/${componentName}`
|
|
505
|
+
: componentName;
|
|
506
|
+
this.classes.set(registeredName, componentData.class);
|
|
507
|
+
if (componentName === 'Loading') {
|
|
508
|
+
console.log('🔎 Bundle class registered: Loading', {
|
|
509
|
+
registeredName,
|
|
510
|
+
type: typeof componentData.class,
|
|
511
|
+
isFunction: typeof componentData.class === 'function'
|
|
512
|
+
});
|
|
513
|
+
}
|
|
514
|
+
if (componentName === 'InputSearchDocs' || componentName === 'MainMenu') {
|
|
515
|
+
console.log(`🔎 Bundle class registered: ${componentName}`, {
|
|
516
|
+
registeredName,
|
|
517
|
+
type: typeof componentData.class,
|
|
518
|
+
isFunction: typeof componentData.class === 'function'
|
|
519
|
+
});
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
} catch (error) {
|
|
523
|
+
console.warn(`❌ Failed to register component ${componentName}:`, error);
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
index += chunkSize;
|
|
528
|
+
if (index < entries.length) {
|
|
529
|
+
if (typeof requestIdleCallback === 'function') {
|
|
530
|
+
requestIdleCallback(processChunk);
|
|
531
|
+
} else {
|
|
532
|
+
setTimeout(processChunk, 0);
|
|
533
|
+
}
|
|
534
|
+
return;
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
console.log(`✅ Bundle registration completed: ${metadata.componentCount} components processed`);
|
|
538
|
+
resolve(true);
|
|
539
|
+
};
|
|
540
|
+
|
|
541
|
+
processChunk();
|
|
542
|
+
});
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
/**
|
|
546
|
+
* Validates bundle structure before registering.
|
|
547
|
+
* @param {object} bundle
|
|
548
|
+
* @returns {{isValid: boolean, error?: string}}
|
|
549
|
+
*/
|
|
550
|
+
validateBundle(bundle) {
|
|
551
|
+
if (!bundle || typeof bundle !== 'object') {
|
|
552
|
+
return { isValid: false, error: 'Bundle payload is invalid' };
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
if (!bundle.metadata || typeof bundle.metadata !== 'object') {
|
|
556
|
+
return { isValid: false, error: 'Bundle metadata missing' };
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
if (!bundle.components || typeof bundle.components !== 'object') {
|
|
560
|
+
return { isValid: false, error: 'Bundle components missing' };
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
if (typeof bundle.metadata.componentCount !== 'number') {
|
|
564
|
+
return { isValid: false, error: 'Bundle metadata missing componentCount' };
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
if (bundle.metadata.componentCount !== Object.keys(bundle.components).length) {
|
|
568
|
+
return { isValid: false, error: 'Bundle component count mismatch' };
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
const maxComponents = 5000;
|
|
572
|
+
if (bundle.metadata.componentCount > maxComponents) {
|
|
573
|
+
return { isValid: false, error: 'Bundle component count exceeds limit' };
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
return { isValid: true };
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
/**
|
|
580
|
+
* 📦 Determines which bundle to load for a component
|
|
581
|
+
*/
|
|
582
|
+
getBundleForComponent(componentName) {
|
|
583
|
+
if (!this.bundleConfig?.bundles) return null;
|
|
584
|
+
|
|
585
|
+
// Check if component is in critical bundle
|
|
586
|
+
if (this.bundleConfig.bundles.critical?.components?.includes(componentName)) {
|
|
587
|
+
return 'critical';
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
// Find component in route bundles
|
|
591
|
+
if (this.bundleConfig.bundles.routes) {
|
|
592
|
+
for (const [bundleName, bundleInfo] of Object.entries(this.bundleConfig.bundles.routes)) {
|
|
593
|
+
if (bundleInfo.components?.includes(componentName)) {
|
|
594
|
+
return bundleName;
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
return null;
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
/**
|
|
603
|
+
* 📦 Checks if a component is available from loaded bundles
|
|
604
|
+
*/
|
|
605
|
+
isComponentFromBundle(componentName) {
|
|
606
|
+
if (!this.bundleConfig?.bundles) return false;
|
|
607
|
+
|
|
608
|
+
// Check critical bundle
|
|
609
|
+
if (this.bundleConfig.bundles.critical?.components?.includes(componentName)) {
|
|
610
|
+
return this.criticalBundleLoaded;
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
// Check route bundles
|
|
614
|
+
if (this.bundleConfig.bundles.routes) {
|
|
615
|
+
for (const [bundleName, bundleInfo] of Object.entries(this.bundleConfig.bundles.routes)) {
|
|
616
|
+
if (bundleInfo.components?.includes(componentName)) {
|
|
617
|
+
return this.loadedBundles.has(bundleName);
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
return false;
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
/**
|
|
626
|
+
* 📦 Gets component data from loaded bundles
|
|
627
|
+
*/
|
|
628
|
+
getComponentFromBundle(componentName) {
|
|
629
|
+
if (!this.bundleConfig?.bundles) return null;
|
|
630
|
+
|
|
631
|
+
// Find component in any loaded bundle
|
|
632
|
+
const allBundles = [
|
|
633
|
+
{ name: 'critical', data: this.bundleConfig.bundles.critical },
|
|
634
|
+
...Object.entries(this.bundleConfig.bundles.routes || {}).map(([name, data]) => ({ name, data })),
|
|
635
|
+
];
|
|
636
|
+
|
|
637
|
+
for (const { name: bundleName, data: bundleData } of allBundles) {
|
|
638
|
+
if (bundleData?.components?.includes(componentName) && this.loadedBundles.has(bundleName)) {
|
|
639
|
+
// Find the bundle file and extract component data
|
|
640
|
+
// This is a simplified version - in practice you'd need to access the loaded bundle data
|
|
641
|
+
return { bundleName, componentName };
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
return null;
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
logActiveComponents() {
|
|
649
|
+
this.activeComponents.forEach((component) => {
|
|
650
|
+
let parent = component.parentComponent;
|
|
651
|
+
let parentName = parent ? parent.constructor.name : null;
|
|
652
|
+
});
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
getTopParentsLinkedToActiveComponents() {
|
|
656
|
+
let topParentsLinkedToActiveComponents = new Map();
|
|
657
|
+
this.activeComponents.forEach((component) => {
|
|
658
|
+
let parent = component.parentComponent;
|
|
659
|
+
while (parent && parent.parentComponent) {
|
|
660
|
+
parent = parent.parentComponent;
|
|
661
|
+
}
|
|
662
|
+
if (!topParentsLinkedToActiveComponents.has(parent)) {
|
|
663
|
+
topParentsLinkedToActiveComponents.set(parent, []);
|
|
664
|
+
}
|
|
665
|
+
topParentsLinkedToActiveComponents.get(parent).push(component);
|
|
666
|
+
});
|
|
667
|
+
return topParentsLinkedToActiveComponents;
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
verifyComponentIds(component) {
|
|
671
|
+
const htmlId = component.id;
|
|
672
|
+
|
|
673
|
+
if (htmlId && htmlId.trim() !== '') {
|
|
674
|
+
if (this.activeComponents.has(htmlId)) {
|
|
675
|
+
slice.logger.logError(
|
|
676
|
+
'Controller',
|
|
677
|
+
`A component with the same html id attribute is already registered: ${htmlId}`
|
|
678
|
+
);
|
|
679
|
+
return false;
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
let sliceId = component.sliceId;
|
|
684
|
+
|
|
685
|
+
if (sliceId && sliceId.trim() !== '') {
|
|
686
|
+
if (this.activeComponents.has(sliceId)) {
|
|
687
|
+
slice.logger.logError(
|
|
688
|
+
'Controller',
|
|
689
|
+
`A component with the same slice id attribute is already registered: ${sliceId}`
|
|
690
|
+
);
|
|
691
|
+
return false;
|
|
692
|
+
}
|
|
693
|
+
} else {
|
|
694
|
+
sliceId = `${component.constructor.name[0].toLowerCase()}${component.constructor.name.slice(1)}-${this.idCounter}`;
|
|
695
|
+
component.sliceId = sliceId;
|
|
696
|
+
this.idCounter++;
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
component.sliceId = sliceId;
|
|
700
|
+
return true;
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
/**
|
|
704
|
+
* Registra un componente y actualiza el índice de relaciones padre-hijo
|
|
705
|
+
* 🚀 OPTIMIZADO: Ahora mantiene childrenIndex y precalcula profundidad
|
|
706
|
+
*/
|
|
707
|
+
registerComponent(component, parent = null) {
|
|
708
|
+
component.parentComponent = parent;
|
|
709
|
+
|
|
710
|
+
// 🚀 OPTIMIZACIÓN: Precalcular y guardar profundidad
|
|
711
|
+
component._depth = parent ? (parent._depth || 0) + 1 : 0;
|
|
712
|
+
|
|
713
|
+
// Registrar en activeComponents
|
|
714
|
+
this.activeComponents.set(component.sliceId, component);
|
|
715
|
+
|
|
716
|
+
// 🚀 OPTIMIZACIÓN: Actualizar índice inverso de hijos
|
|
717
|
+
if (parent) {
|
|
718
|
+
if (!this.childrenIndex.has(parent.sliceId)) {
|
|
719
|
+
this.childrenIndex.set(parent.sliceId, new Set());
|
|
720
|
+
}
|
|
721
|
+
this.childrenIndex.get(parent.sliceId).add(component.sliceId);
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
return true;
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
registerComponentsRecursively(component, parent = null) {
|
|
728
|
+
// Assign parent if not already set
|
|
729
|
+
if (!component.parentComponent) {
|
|
730
|
+
component.parentComponent = parent;
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
// Recursively assign parent to children
|
|
734
|
+
component.querySelectorAll('*').forEach((child) => {
|
|
735
|
+
if (child.tagName.startsWith('SLICE-')) {
|
|
736
|
+
if (!child.parentComponent) {
|
|
737
|
+
child.parentComponent = component;
|
|
738
|
+
}
|
|
739
|
+
this.registerComponentsRecursively(child, component);
|
|
740
|
+
}
|
|
741
|
+
});
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
/**
|
|
745
|
+
* Get a registered component by sliceId.
|
|
746
|
+
* @param {string} sliceId
|
|
747
|
+
* @returns {HTMLElement|undefined}
|
|
748
|
+
*/
|
|
749
|
+
getComponent(sliceId) {
|
|
750
|
+
return this.activeComponents.get(sliceId);
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
loadTemplateToComponent(component) {
|
|
754
|
+
const className = component.constructor.name;
|
|
755
|
+
const template = this.templates.get(className);
|
|
756
|
+
|
|
757
|
+
if (!template) {
|
|
758
|
+
slice.logger.logError(`Template not found for component: ${className}`);
|
|
759
|
+
return;
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
component.innerHTML = template.innerHTML;
|
|
763
|
+
return component;
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
getComponentCategory(componentSliceId) {
|
|
767
|
+
return this.componentCategories.get(componentSliceId);
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
/**
|
|
771
|
+
* Fetch component resources (html, css, styles, theme).
|
|
772
|
+
* @param {string} componentName
|
|
773
|
+
* @param {'html'|'css'|'theme'|'styles'} resourceType
|
|
774
|
+
* @param {string} [componentCategory]
|
|
775
|
+
* @param {string} [customPath]
|
|
776
|
+
* @returns {Promise<string>}
|
|
777
|
+
*/
|
|
778
|
+
async fetchText(componentName, resourceType, componentCategory, customPath) {
|
|
779
|
+
try {
|
|
780
|
+
const baseUrl = window.location.origin;
|
|
781
|
+
let path;
|
|
782
|
+
|
|
783
|
+
if (!componentCategory) {
|
|
784
|
+
componentCategory = this.componentCategories.get(componentName);
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
let isVisual = resourceType === 'html' || resourceType === 'css';
|
|
788
|
+
|
|
789
|
+
if (isVisual) {
|
|
790
|
+
if (slice.paths.components[componentCategory]) {
|
|
791
|
+
path = `${baseUrl}${slice.paths.components[componentCategory].path}/${componentName}`;
|
|
792
|
+
resourceType === 'html' ? (path += `/${componentName}.html`) : (path += `/${componentName}.css`);
|
|
793
|
+
} else {
|
|
794
|
+
if (componentCategory === 'Structural') {
|
|
795
|
+
path = `${baseUrl}/Slice/Components/Structural/${componentName}`;
|
|
796
|
+
resourceType === 'html' ? (path += `/${componentName}.html`) : (path += `/${componentName}.css`);
|
|
797
|
+
} else {
|
|
798
|
+
throw new Error(`Component category '${componentCategory}' not found in paths configuration`);
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
if (resourceType === 'theme') {
|
|
804
|
+
path = `${baseUrl}${slice.paths.themes}/${componentName}.css`;
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
if (resourceType === 'styles') {
|
|
808
|
+
path = `${baseUrl}${slice.paths.styles}/${componentName}.css`;
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
if (customPath) {
|
|
812
|
+
path = customPath;
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
slice.logger.logInfo('Controller', `Fetching ${resourceType} from: ${path}`);
|
|
816
|
+
|
|
817
|
+
const response = await fetch(path);
|
|
818
|
+
|
|
819
|
+
if (!response.ok) {
|
|
820
|
+
throw new Error(`Failed to fetch ${path}: ${response.statusText}`);
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
const content = await response.text();
|
|
824
|
+
slice.logger.logInfo('Controller', `Successfully fetched ${resourceType} for ${componentName}`);
|
|
825
|
+
return content;
|
|
826
|
+
} catch (error) {
|
|
827
|
+
slice.logger.logError('Controller', `Error fetching ${resourceType} for component ${componentName}:`, error);
|
|
828
|
+
throw error;
|
|
829
|
+
}
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
/**
|
|
833
|
+
* Apply props to a component using static defaults and setters.
|
|
834
|
+
* @param {HTMLElement} component
|
|
835
|
+
* @param {Object} props
|
|
836
|
+
* @returns {void}
|
|
837
|
+
*/
|
|
838
|
+
setComponentProps(component, props) {
|
|
839
|
+
const ComponentClass = component.constructor;
|
|
840
|
+
const componentName = ComponentClass.name;
|
|
841
|
+
|
|
842
|
+
// Aplicar defaults si tiene static props
|
|
843
|
+
if (ComponentClass.props) {
|
|
844
|
+
this.applyDefaultProps(component, ComponentClass.props, props);
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
// Validar solo en desarrollo
|
|
848
|
+
if (ComponentClass.props && !slice.isProduction()) {
|
|
849
|
+
this.validatePropsInDevelopment(ComponentClass, props, componentName);
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
// Aplicar props
|
|
853
|
+
for (const prop in props) {
|
|
854
|
+
component[`_${prop}`] = null;
|
|
855
|
+
component[prop] = props[prop];
|
|
856
|
+
}
|
|
857
|
+
}
|
|
858
|
+
|
|
859
|
+
getComponentPropsForDebugger(component) {
|
|
860
|
+
const ComponentClass = component.constructor;
|
|
861
|
+
|
|
862
|
+
if (ComponentClass.props) {
|
|
863
|
+
return {
|
|
864
|
+
availableProps: Object.keys(ComponentClass.props),
|
|
865
|
+
propsConfig: ComponentClass.props,
|
|
866
|
+
usedProps: this.extractUsedProps(component, ComponentClass.props),
|
|
867
|
+
};
|
|
868
|
+
} else {
|
|
869
|
+
return {
|
|
870
|
+
availableProps: this.extractUsedProps(component),
|
|
871
|
+
propsConfig: null,
|
|
872
|
+
usedProps: this.extractUsedProps(component),
|
|
873
|
+
};
|
|
874
|
+
}
|
|
875
|
+
}
|
|
876
|
+
|
|
877
|
+
applyDefaultProps(component, staticProps, providedProps) {
|
|
878
|
+
Object.entries(staticProps).forEach(([prop, config]) => {
|
|
879
|
+
if (config.default !== undefined && !(prop in (providedProps || {}))) {
|
|
880
|
+
component[`_${prop}`] = null;
|
|
881
|
+
component[prop] = config.default;
|
|
882
|
+
}
|
|
883
|
+
});
|
|
884
|
+
}
|
|
885
|
+
|
|
885
886
|
validatePropsInDevelopment(ComponentClass, providedProps, componentName) {
|
|
886
887
|
const staticProps = ComponentClass.props;
|
|
887
888
|
const usedProps = Object.keys(providedProps || {});
|
|
888
|
-
|
|
889
|
-
const availableProps = Object.keys(staticProps);
|
|
890
|
-
const unknownProps = usedProps.filter((prop) => !availableProps.includes(prop));
|
|
891
|
-
|
|
892
|
-
if (unknownProps.length > 0) {
|
|
893
|
-
slice.logger.logWarning(
|
|
894
|
-
'PropsValidator',
|
|
895
|
-
`${componentName}: Unknown props [${unknownProps.join(', ')}]. Available: [${availableProps.join(', ')}]`
|
|
896
|
-
);
|
|
897
|
-
}
|
|
898
|
-
|
|
899
|
-
const requiredProps = Object.entries(staticProps)
|
|
900
|
-
.filter(([_, config]) => config.required)
|
|
901
|
-
.map(([prop, _]) => prop);
|
|
902
|
-
|
|
889
|
+
|
|
890
|
+
const availableProps = Object.keys(staticProps);
|
|
891
|
+
const unknownProps = usedProps.filter((prop) => !availableProps.includes(prop));
|
|
892
|
+
|
|
893
|
+
if (unknownProps.length > 0) {
|
|
894
|
+
slice.logger.logWarning(
|
|
895
|
+
'PropsValidator',
|
|
896
|
+
`${componentName}: Unknown props [${unknownProps.join(', ')}]. Available: [${availableProps.join(', ')}]`
|
|
897
|
+
);
|
|
898
|
+
}
|
|
899
|
+
|
|
900
|
+
const requiredProps = Object.entries(staticProps)
|
|
901
|
+
.filter(([_, config]) => config.required)
|
|
902
|
+
.map(([prop, _]) => prop);
|
|
903
|
+
|
|
903
904
|
const missingRequired = requiredProps.filter((prop) => !(prop in (providedProps || {})));
|
|
904
905
|
if (missingRequired.length > 0) {
|
|
905
906
|
slice.logger.logError(componentName, `Missing required props: [${missingRequired.join(', ')}]`);
|
|
906
907
|
}
|
|
907
|
-
}
|
|
908
|
-
|
|
909
|
-
extractUsedProps(component, staticProps = null) {
|
|
910
|
-
const usedProps = {};
|
|
911
|
-
|
|
912
|
-
if (staticProps) {
|
|
913
|
-
Object.keys(staticProps).forEach((prop) => {
|
|
914
|
-
if (component[prop] !== undefined) {
|
|
915
|
-
usedProps[prop] = component[prop];
|
|
916
|
-
}
|
|
917
|
-
});
|
|
918
|
-
} else {
|
|
919
|
-
Object.getOwnPropertyNames(component).forEach((key) => {
|
|
920
|
-
if (key.startsWith('_') && key !== '_isActive') {
|
|
921
|
-
const propName = key.substring(1);
|
|
922
|
-
usedProps[propName] = component[propName];
|
|
923
|
-
}
|
|
924
|
-
});
|
|
925
|
-
}
|
|
926
|
-
|
|
927
|
-
return usedProps;
|
|
928
|
-
}
|
|
929
|
-
|
|
930
|
-
// ============================================================================
|
|
931
|
-
// 🚀 MÉTODOS DE DESTRUCCIÓN OPTIMIZADOS
|
|
932
|
-
// ============================================================================
|
|
933
|
-
|
|
934
|
-
/**
|
|
935
|
-
* Encuentra recursivamente todos los hijos de un componente
|
|
936
|
-
* 🚀 OPTIMIZADO: O(m) en lugar de O(n*d) - usa childrenIndex
|
|
937
|
-
* @param {string} parentSliceId - sliceId del componente padre
|
|
938
|
-
* @param {Set<string>} collected - Set de sliceIds ya recolectados
|
|
939
|
-
* @returns {Set<string>} Set de todos los sliceIds de componentes hijos
|
|
940
|
-
*/
|
|
941
|
-
findAllChildComponents(parentSliceId, collected = new Set()) {
|
|
942
|
-
// 🚀 Buscar directamente en el índice: O(1)
|
|
943
|
-
const children = this.childrenIndex.get(parentSliceId);
|
|
944
|
-
|
|
945
|
-
if (!children) return collected;
|
|
946
|
-
|
|
947
|
-
// 🚀 Iterar solo los hijos directos: O(k) donde k = número de hijos
|
|
948
|
-
for (const childSliceId of children) {
|
|
949
|
-
collected.add(childSliceId);
|
|
950
|
-
// Recursión solo sobre hijos, no todos los componentes
|
|
951
|
-
this.findAllChildComponents(childSliceId, collected);
|
|
952
|
-
}
|
|
953
|
-
|
|
954
|
-
return collected;
|
|
955
|
-
}
|
|
956
|
-
|
|
957
|
-
/**
|
|
958
|
-
* Encuentra recursivamente todos los componentes dentro de un contenedor DOM
|
|
959
|
-
* Útil para destroyByContainer cuando no tenemos el sliceId del padre
|
|
960
|
-
* @param {HTMLElement} container - Elemento contenedor
|
|
961
|
-
* @param {Set<string>} collected - Set de sliceIds ya recolectados
|
|
962
|
-
* @returns {Set<string>} Set de todos los sliceIds encontrados
|
|
963
|
-
*/
|
|
964
|
-
findAllNestedComponentsInContainer(container, collected = new Set()) {
|
|
965
|
-
// Buscar todos los elementos con slice-id en el contenedor
|
|
966
|
-
const sliceComponents = container.querySelectorAll('[slice-id]');
|
|
967
|
-
|
|
968
|
-
sliceComponents.forEach((element) => {
|
|
969
|
-
const sliceId = element.getAttribute('slice-id') || element.sliceId;
|
|
970
|
-
if (sliceId && this.activeComponents.has(sliceId)) {
|
|
971
|
-
collected.add(sliceId);
|
|
972
|
-
// 🚀 Usar índice para buscar hijos recursivamente
|
|
973
|
-
this.findAllChildComponents(sliceId, collected);
|
|
974
|
-
}
|
|
975
|
-
});
|
|
976
|
-
|
|
977
|
-
return collected;
|
|
978
|
-
}
|
|
979
|
-
|
|
980
|
-
/**
|
|
981
|
-
* Destruye uno o múltiples componentes DE FORMA RECURSIVA
|
|
982
|
-
* 🚀 OPTIMIZADO: O(m log m) en lugar de O(n*d + m log m)
|
|
983
|
-
* @param {HTMLElement|Array<HTMLElement>|string|Array<string>} components
|
|
984
|
-
* @returns {number} Cantidad de componentes destruidos (incluyendo hijos)
|
|
985
|
-
*/
|
|
986
|
-
destroyComponent(components) {
|
|
987
|
-
const toDestroy = Array.isArray(components) ? components : [components];
|
|
988
|
-
const allSliceIdsToDestroy = new Set();
|
|
989
|
-
|
|
990
|
-
// PASO 1: Recolectar todos los componentes padres y sus hijos recursivamente
|
|
991
|
-
for (const item of toDestroy) {
|
|
992
|
-
let sliceId = null;
|
|
993
|
-
|
|
994
|
-
if (typeof item === 'string') {
|
|
995
|
-
if (!this.activeComponents.has(item)) {
|
|
996
|
-
slice.logger.logWarning('Controller', `Component with sliceId "${item}" not found`);
|
|
997
|
-
continue;
|
|
998
|
-
}
|
|
999
|
-
sliceId = item;
|
|
1000
|
-
} else if (item && item.sliceId) {
|
|
1001
|
-
sliceId = item.sliceId;
|
|
1002
|
-
} else {
|
|
1003
|
-
slice.logger.logWarning('Controller', `Invalid component or sliceId provided to destroyComponent`);
|
|
1004
|
-
continue;
|
|
1005
|
-
}
|
|
1006
|
-
|
|
1007
|
-
allSliceIdsToDestroy.add(sliceId);
|
|
1008
|
-
|
|
1009
|
-
// 🚀 OPTIMIZADO: Usa childrenIndex en lugar de recorrer todos los componentes
|
|
1010
|
-
this.findAllChildComponents(sliceId, allSliceIdsToDestroy);
|
|
1011
|
-
}
|
|
1012
|
-
|
|
1013
|
-
// PASO 2: Ordenar por profundidad (más profundos primero)
|
|
1014
|
-
// 🚀 OPTIMIZADO: Usa _depth precalculada en lugar de calcularla cada vez
|
|
1015
|
-
const sortedSliceIds = Array.from(allSliceIdsToDestroy).sort((a, b) => {
|
|
1016
|
-
const compA = this.activeComponents.get(a);
|
|
1017
|
-
const compB = this.activeComponents.get(b);
|
|
1018
|
-
|
|
1019
|
-
if (!compA || !compB) return 0;
|
|
1020
|
-
|
|
1021
|
-
// 🚀 O(1) en lugar de O(d) - usa profundidad precalculada
|
|
1022
|
-
return (compB._depth || 0) - (compA._depth || 0);
|
|
1023
|
-
});
|
|
1024
|
-
|
|
1025
|
-
let destroyedCount = 0;
|
|
1026
|
-
|
|
1027
|
-
// PASO 3: Destruir en orden correcto (hijos antes que padres)
|
|
1028
|
-
for (const sliceId of sortedSliceIds) {
|
|
1029
|
-
const component = this.activeComponents.get(sliceId);
|
|
1030
|
-
|
|
1031
|
-
if (!component) continue;
|
|
1032
|
-
|
|
1033
|
-
// Ejecutar hook beforeDestroy si existe
|
|
1034
|
-
if (typeof component.beforeDestroy === 'function') {
|
|
1035
|
-
try {
|
|
1036
|
-
component.beforeDestroy();
|
|
1037
|
-
} catch (error) {
|
|
1038
|
-
slice.logger.logError('Controller', `Error in beforeDestroy for ${sliceId}`, error);
|
|
1039
|
-
}
|
|
1040
|
-
}
|
|
1041
|
-
|
|
1042
|
-
// Limpiar suscripciones de eventos del componente
|
|
1043
|
-
if (slice.events) {
|
|
1044
|
-
slice.events.cleanupComponent(sliceId);
|
|
1045
|
-
}
|
|
1046
|
-
|
|
1047
|
-
// 🚀 Limpiar del índice de hijos
|
|
1048
|
-
this.childrenIndex.delete(sliceId);
|
|
1049
|
-
|
|
1050
|
-
// Si tiene padre, remover de la lista de hijos del padre
|
|
1051
|
-
if (component.parentComponent) {
|
|
1052
|
-
const parentChildren = this.childrenIndex.get(component.parentComponent.sliceId);
|
|
1053
|
-
if (parentChildren) {
|
|
1054
|
-
parentChildren.delete(sliceId);
|
|
1055
|
-
// Si el padre no tiene más hijos, eliminar entrada vacía
|
|
1056
|
-
if (parentChildren.size === 0) {
|
|
1057
|
-
this.childrenIndex.delete(component.parentComponent.sliceId);
|
|
1058
|
-
}
|
|
1059
|
-
}
|
|
1060
|
-
}
|
|
1061
|
-
|
|
1062
|
-
// Eliminar del mapa de componentes activos
|
|
1063
|
-
this.activeComponents.delete(sliceId);
|
|
1064
|
-
|
|
1065
|
-
// Remover del DOM si está conectado
|
|
1066
|
-
if (component.isConnected) {
|
|
1067
|
-
component.remove();
|
|
1068
|
-
}
|
|
1069
|
-
|
|
1070
|
-
destroyedCount++;
|
|
1071
|
-
}
|
|
1072
|
-
|
|
1073
|
-
if (destroyedCount > 0) {
|
|
1074
|
-
slice.logger.logInfo('Controller', `Destroyed ${destroyedCount} component(s) recursively`);
|
|
1075
|
-
}
|
|
1076
|
-
|
|
1077
|
-
return destroyedCount;
|
|
1078
|
-
}
|
|
1079
|
-
|
|
1080
|
-
/**
|
|
1081
|
-
* Destruye todos los componentes Slice dentro de un contenedor (RECURSIVO)
|
|
1082
|
-
* 🚀 OPTIMIZADO: Usa el índice inverso para búsqueda rápida
|
|
1083
|
-
* @param {HTMLElement} container - Elemento contenedor
|
|
1084
|
-
* @returns {number} Cantidad de componentes destruidos
|
|
1085
|
-
*/
|
|
1086
|
-
destroyByContainer(container) {
|
|
1087
|
-
if (!container) {
|
|
1088
|
-
slice.logger.logWarning('Controller', 'No container provided to destroyByContainer');
|
|
1089
|
-
return 0;
|
|
1090
|
-
}
|
|
1091
908
|
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
}
|
|
1098
|
-
|
|
1099
|
-
// Destruir usando el método principal optimizado
|
|
1100
|
-
const count = this.destroyComponent(Array.from(allSliceIds));
|
|
1101
|
-
|
|
1102
|
-
if (count > 0) {
|
|
1103
|
-
slice.logger.logInfo('Controller', `Destroyed ${count} component(s) from container (including nested)`);
|
|
1104
|
-
}
|
|
1105
|
-
|
|
1106
|
-
return count;
|
|
1107
|
-
}
|
|
1108
|
-
|
|
1109
|
-
/**
|
|
1110
|
-
* Destruye componentes cuyos sliceId coincidan con un patrón (RECURSIVO)
|
|
1111
|
-
* 🚀 OPTIMIZADO: Usa destrucción optimizada
|
|
1112
|
-
* @param {string|RegExp} pattern - Patrón a buscar
|
|
1113
|
-
* @returns {number} Cantidad de componentes destruidos
|
|
1114
|
-
*/
|
|
1115
|
-
destroyByPattern(pattern) {
|
|
1116
|
-
const componentsToDestroy = [];
|
|
1117
|
-
const regex = pattern instanceof RegExp ? pattern : new RegExp(pattern);
|
|
1118
|
-
|
|
1119
|
-
for (const [sliceId, component] of this.activeComponents) {
|
|
1120
|
-
if (regex.test(sliceId)) {
|
|
1121
|
-
componentsToDestroy.push(component);
|
|
1122
|
-
}
|
|
1123
|
-
}
|
|
1124
|
-
|
|
1125
|
-
if (componentsToDestroy.length === 0) {
|
|
1126
|
-
return 0;
|
|
1127
|
-
}
|
|
1128
|
-
|
|
1129
|
-
const count = this.destroyComponent(componentsToDestroy);
|
|
1130
|
-
|
|
1131
|
-
if (count > 0) {
|
|
1132
|
-
slice.logger.logInfo(
|
|
1133
|
-
'Controller',
|
|
1134
|
-
`Destroyed ${count} component(s) matching pattern: ${pattern} (including nested)`
|
|
909
|
+
const invalidAllowedValueProps = collectInvalidAllowedValueProps(staticProps, providedProps);
|
|
910
|
+
invalidAllowedValueProps.forEach(({ propName, value, allowedValues }) => {
|
|
911
|
+
slice.logger.logError(
|
|
912
|
+
componentName,
|
|
913
|
+
`Invalid value for prop "${propName}": ${JSON.stringify(value)}. Allowed values: ${formatAllowedValuesForLog(allowedValues)}`
|
|
1135
914
|
);
|
|
1136
|
-
}
|
|
1137
|
-
|
|
1138
|
-
return count;
|
|
915
|
+
});
|
|
1139
916
|
}
|
|
1140
|
-
|
|
917
|
+
|
|
918
|
+
extractUsedProps(component, staticProps = null) {
|
|
919
|
+
const usedProps = {};
|
|
920
|
+
|
|
921
|
+
if (staticProps) {
|
|
922
|
+
Object.keys(staticProps).forEach((prop) => {
|
|
923
|
+
if (component[prop] !== undefined) {
|
|
924
|
+
usedProps[prop] = component[prop];
|
|
925
|
+
}
|
|
926
|
+
});
|
|
927
|
+
} else {
|
|
928
|
+
Object.getOwnPropertyNames(component).forEach((key) => {
|
|
929
|
+
if (key.startsWith('_') && key !== '_isActive') {
|
|
930
|
+
const propName = key.substring(1);
|
|
931
|
+
usedProps[propName] = component[propName];
|
|
932
|
+
}
|
|
933
|
+
});
|
|
934
|
+
}
|
|
935
|
+
|
|
936
|
+
return usedProps;
|
|
937
|
+
}
|
|
938
|
+
|
|
939
|
+
// ============================================================================
|
|
940
|
+
// 🚀 MÉTODOS DE DESTRUCCIÓN OPTIMIZADOS
|
|
941
|
+
// ============================================================================
|
|
942
|
+
|
|
943
|
+
/**
|
|
944
|
+
* Encuentra recursivamente todos los hijos de un componente
|
|
945
|
+
* 🚀 OPTIMIZADO: O(m) en lugar de O(n*d) - usa childrenIndex
|
|
946
|
+
* @param {string} parentSliceId - sliceId del componente padre
|
|
947
|
+
* @param {Set<string>} collected - Set de sliceIds ya recolectados
|
|
948
|
+
* @returns {Set<string>} Set de todos los sliceIds de componentes hijos
|
|
949
|
+
*/
|
|
950
|
+
findAllChildComponents(parentSliceId, collected = new Set()) {
|
|
951
|
+
// 🚀 Buscar directamente en el índice: O(1)
|
|
952
|
+
const children = this.childrenIndex.get(parentSliceId);
|
|
953
|
+
|
|
954
|
+
if (!children) return collected;
|
|
955
|
+
|
|
956
|
+
// 🚀 Iterar solo los hijos directos: O(k) donde k = número de hijos
|
|
957
|
+
for (const childSliceId of children) {
|
|
958
|
+
collected.add(childSliceId);
|
|
959
|
+
// Recursión solo sobre hijos, no todos los componentes
|
|
960
|
+
this.findAllChildComponents(childSliceId, collected);
|
|
961
|
+
}
|
|
962
|
+
|
|
963
|
+
return collected;
|
|
964
|
+
}
|
|
965
|
+
|
|
966
|
+
/**
|
|
967
|
+
* Encuentra recursivamente todos los componentes dentro de un contenedor DOM
|
|
968
|
+
* Útil para destroyByContainer cuando no tenemos el sliceId del padre
|
|
969
|
+
* @param {HTMLElement} container - Elemento contenedor
|
|
970
|
+
* @param {Set<string>} collected - Set de sliceIds ya recolectados
|
|
971
|
+
* @returns {Set<string>} Set de todos los sliceIds encontrados
|
|
972
|
+
*/
|
|
973
|
+
findAllNestedComponentsInContainer(container, collected = new Set()) {
|
|
974
|
+
// Buscar todos los elementos con slice-id en el contenedor
|
|
975
|
+
const sliceComponents = container.querySelectorAll('[slice-id]');
|
|
976
|
+
|
|
977
|
+
sliceComponents.forEach((element) => {
|
|
978
|
+
const sliceId = element.getAttribute('slice-id') || element.sliceId;
|
|
979
|
+
if (sliceId && this.activeComponents.has(sliceId)) {
|
|
980
|
+
collected.add(sliceId);
|
|
981
|
+
// 🚀 Usar índice para buscar hijos recursivamente
|
|
982
|
+
this.findAllChildComponents(sliceId, collected);
|
|
983
|
+
}
|
|
984
|
+
});
|
|
985
|
+
|
|
986
|
+
return collected;
|
|
987
|
+
}
|
|
988
|
+
|
|
989
|
+
/**
|
|
990
|
+
* Destruye uno o múltiples componentes DE FORMA RECURSIVA
|
|
991
|
+
* 🚀 OPTIMIZADO: O(m log m) en lugar de O(n*d + m log m)
|
|
992
|
+
* @param {HTMLElement|Array<HTMLElement>|string|Array<string>} components
|
|
993
|
+
* @returns {number} Cantidad de componentes destruidos (incluyendo hijos)
|
|
994
|
+
*/
|
|
995
|
+
destroyComponent(components) {
|
|
996
|
+
const toDestroy = Array.isArray(components) ? components : [components];
|
|
997
|
+
const allSliceIdsToDestroy = new Set();
|
|
998
|
+
|
|
999
|
+
// PASO 1: Recolectar todos los componentes padres y sus hijos recursivamente
|
|
1000
|
+
for (const item of toDestroy) {
|
|
1001
|
+
let sliceId = null;
|
|
1002
|
+
|
|
1003
|
+
if (typeof item === 'string') {
|
|
1004
|
+
if (!this.activeComponents.has(item)) {
|
|
1005
|
+
slice.logger.logWarning('Controller', `Component with sliceId "${item}" not found`);
|
|
1006
|
+
continue;
|
|
1007
|
+
}
|
|
1008
|
+
sliceId = item;
|
|
1009
|
+
} else if (item && item.sliceId) {
|
|
1010
|
+
sliceId = item.sliceId;
|
|
1011
|
+
} else {
|
|
1012
|
+
slice.logger.logWarning('Controller', `Invalid component or sliceId provided to destroyComponent`);
|
|
1013
|
+
continue;
|
|
1014
|
+
}
|
|
1015
|
+
|
|
1016
|
+
allSliceIdsToDestroy.add(sliceId);
|
|
1017
|
+
|
|
1018
|
+
// 🚀 OPTIMIZADO: Usa childrenIndex en lugar de recorrer todos los componentes
|
|
1019
|
+
this.findAllChildComponents(sliceId, allSliceIdsToDestroy);
|
|
1020
|
+
}
|
|
1021
|
+
|
|
1022
|
+
// PASO 2: Ordenar por profundidad (más profundos primero)
|
|
1023
|
+
// 🚀 OPTIMIZADO: Usa _depth precalculada en lugar de calcularla cada vez
|
|
1024
|
+
const sortedSliceIds = Array.from(allSliceIdsToDestroy).sort((a, b) => {
|
|
1025
|
+
const compA = this.activeComponents.get(a);
|
|
1026
|
+
const compB = this.activeComponents.get(b);
|
|
1027
|
+
|
|
1028
|
+
if (!compA || !compB) return 0;
|
|
1029
|
+
|
|
1030
|
+
// 🚀 O(1) en lugar de O(d) - usa profundidad precalculada
|
|
1031
|
+
return (compB._depth || 0) - (compA._depth || 0);
|
|
1032
|
+
});
|
|
1033
|
+
|
|
1034
|
+
let destroyedCount = 0;
|
|
1035
|
+
|
|
1036
|
+
// PASO 3: Destruir en orden correcto (hijos antes que padres)
|
|
1037
|
+
for (const sliceId of sortedSliceIds) {
|
|
1038
|
+
const component = this.activeComponents.get(sliceId);
|
|
1039
|
+
|
|
1040
|
+
if (!component) continue;
|
|
1041
|
+
|
|
1042
|
+
// Ejecutar hook beforeDestroy si existe
|
|
1043
|
+
if (typeof component.beforeDestroy === 'function') {
|
|
1044
|
+
try {
|
|
1045
|
+
component.beforeDestroy();
|
|
1046
|
+
} catch (error) {
|
|
1047
|
+
slice.logger.logError('Controller', `Error in beforeDestroy for ${sliceId}`, error);
|
|
1048
|
+
}
|
|
1049
|
+
}
|
|
1050
|
+
|
|
1051
|
+
// Limpiar suscripciones de eventos del componente
|
|
1052
|
+
if (slice.events) {
|
|
1053
|
+
slice.events.cleanupComponent(sliceId);
|
|
1054
|
+
}
|
|
1055
|
+
|
|
1056
|
+
// 🚀 Limpiar del índice de hijos
|
|
1057
|
+
this.childrenIndex.delete(sliceId);
|
|
1058
|
+
|
|
1059
|
+
// Si tiene padre, remover de la lista de hijos del padre
|
|
1060
|
+
if (component.parentComponent) {
|
|
1061
|
+
const parentChildren = this.childrenIndex.get(component.parentComponent.sliceId);
|
|
1062
|
+
if (parentChildren) {
|
|
1063
|
+
parentChildren.delete(sliceId);
|
|
1064
|
+
// Si el padre no tiene más hijos, eliminar entrada vacía
|
|
1065
|
+
if (parentChildren.size === 0) {
|
|
1066
|
+
this.childrenIndex.delete(component.parentComponent.sliceId);
|
|
1067
|
+
}
|
|
1068
|
+
}
|
|
1069
|
+
}
|
|
1070
|
+
|
|
1071
|
+
// Eliminar del mapa de componentes activos
|
|
1072
|
+
this.activeComponents.delete(sliceId);
|
|
1073
|
+
|
|
1074
|
+
// Remover del DOM si está conectado
|
|
1075
|
+
if (component.isConnected) {
|
|
1076
|
+
component.remove();
|
|
1077
|
+
}
|
|
1078
|
+
|
|
1079
|
+
destroyedCount++;
|
|
1080
|
+
}
|
|
1081
|
+
|
|
1082
|
+
if (destroyedCount > 0) {
|
|
1083
|
+
slice.logger.logInfo('Controller', `Destroyed ${destroyedCount} component(s) recursively`);
|
|
1084
|
+
}
|
|
1085
|
+
|
|
1086
|
+
return destroyedCount;
|
|
1087
|
+
}
|
|
1088
|
+
|
|
1089
|
+
/**
|
|
1090
|
+
* Destruye todos los componentes Slice dentro de un contenedor (RECURSIVO)
|
|
1091
|
+
* 🚀 OPTIMIZADO: Usa el índice inverso para búsqueda rápida
|
|
1092
|
+
* @param {HTMLElement} container - Elemento contenedor
|
|
1093
|
+
* @returns {number} Cantidad de componentes destruidos
|
|
1094
|
+
*/
|
|
1095
|
+
destroyByContainer(container) {
|
|
1096
|
+
if (!container) {
|
|
1097
|
+
slice.logger.logWarning('Controller', 'No container provided to destroyByContainer');
|
|
1098
|
+
return 0;
|
|
1099
|
+
}
|
|
1100
|
+
|
|
1101
|
+
// 🚀 Recolectar componentes usando índice optimizado
|
|
1102
|
+
const allSliceIds = this.findAllNestedComponentsInContainer(container);
|
|
1103
|
+
|
|
1104
|
+
if (allSliceIds.size === 0) {
|
|
1105
|
+
return 0;
|
|
1106
|
+
}
|
|
1107
|
+
|
|
1108
|
+
// Destruir usando el método principal optimizado
|
|
1109
|
+
const count = this.destroyComponent(Array.from(allSliceIds));
|
|
1110
|
+
|
|
1111
|
+
if (count > 0) {
|
|
1112
|
+
slice.logger.logInfo('Controller', `Destroyed ${count} component(s) from container (including nested)`);
|
|
1113
|
+
}
|
|
1114
|
+
|
|
1115
|
+
return count;
|
|
1116
|
+
}
|
|
1117
|
+
|
|
1118
|
+
/**
|
|
1119
|
+
* Destruye componentes cuyos sliceId coincidan con un patrón (RECURSIVO)
|
|
1120
|
+
* 🚀 OPTIMIZADO: Usa destrucción optimizada
|
|
1121
|
+
* @param {string|RegExp} pattern - Patrón a buscar
|
|
1122
|
+
* @returns {number} Cantidad de componentes destruidos
|
|
1123
|
+
*/
|
|
1124
|
+
destroyByPattern(pattern) {
|
|
1125
|
+
const componentsToDestroy = [];
|
|
1126
|
+
const regex = pattern instanceof RegExp ? pattern : new RegExp(pattern);
|
|
1127
|
+
|
|
1128
|
+
for (const [sliceId, component] of this.activeComponents) {
|
|
1129
|
+
if (regex.test(sliceId)) {
|
|
1130
|
+
componentsToDestroy.push(component);
|
|
1131
|
+
}
|
|
1132
|
+
}
|
|
1133
|
+
|
|
1134
|
+
if (componentsToDestroy.length === 0) {
|
|
1135
|
+
return 0;
|
|
1136
|
+
}
|
|
1137
|
+
|
|
1138
|
+
const count = this.destroyComponent(componentsToDestroy);
|
|
1139
|
+
|
|
1140
|
+
if (count > 0) {
|
|
1141
|
+
slice.logger.logInfo(
|
|
1142
|
+
'Controller',
|
|
1143
|
+
`Destroyed ${count} component(s) matching pattern: ${pattern} (including nested)`
|
|
1144
|
+
);
|
|
1145
|
+
}
|
|
1146
|
+
|
|
1147
|
+
return count;
|
|
1148
|
+
}
|
|
1149
|
+
}
|