slicejs-web-framework 2.2.2 → 2.2.5

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.
@@ -5,8 +5,13 @@ export default class Controller {
5
5
  this.componentCategories = new Map(Object.entries(components));
6
6
  this.templates = new Map();
7
7
  this.classes = new Map();
8
- this.requestedStyles = new Set();
8
+ this.requestedStyles = new Set(); // ✅ CRÍTICO: Para tracking de CSS cargados
9
9
  this.activeComponents = new Map();
10
+
11
+ // 🚀 OPTIMIZACIÓN: Índice inverso para búsqueda rápida de hijos
12
+ // parentSliceId → Set<childSliceId>
13
+ this.childrenIndex = new Map();
14
+
10
15
  this.idCounter = 0;
11
16
  }
12
17
 
@@ -65,9 +70,27 @@ export default class Controller {
65
70
  return true;
66
71
  }
67
72
 
73
+ /**
74
+ * Registra un componente y actualiza el índice de relaciones padre-hijo
75
+ * 🚀 OPTIMIZADO: Ahora mantiene childrenIndex y precalcula profundidad
76
+ */
68
77
  registerComponent(component, parent = null) {
69
78
  component.parentComponent = parent;
79
+
80
+ // 🚀 OPTIMIZACIÓN: Precalcular y guardar profundidad
81
+ component._depth = parent ? (parent._depth || 0) + 1 : 0;
82
+
83
+ // Registrar en activeComponents
70
84
  this.activeComponents.set(component.sliceId, component);
85
+
86
+ // 🚀 OPTIMIZACIÓN: Actualizar índice inverso de hijos
87
+ if (parent) {
88
+ if (!this.childrenIndex.has(parent.sliceId)) {
89
+ this.childrenIndex.set(parent.sliceId, new Set());
90
+ }
91
+ this.childrenIndex.get(parent.sliceId).add(component.sliceId);
92
+ }
93
+
71
94
  return true;
72
95
  }
73
96
 
@@ -92,7 +115,6 @@ export default class Controller {
92
115
  return this.activeComponents.get(sliceId);
93
116
  }
94
117
 
95
- //Attach template to component
96
118
  loadTemplateToComponent(component) {
97
119
  const className = component.constructor.name;
98
120
  const template = this.templates.get(className);
@@ -110,14 +132,6 @@ export default class Controller {
110
132
  return this.componentCategories.get(componentSliceId);
111
133
  }
112
134
 
113
- /*
114
- Fetches a text resource (HTML, CSS, theme, or styles) for a component.
115
- @param {string} componentName - The name of the component.
116
- @param {string} resourceType - The type of resource to fetch ('html', 'css', 'theme', or 'styles').
117
- @param {string} [componentCategory] - Optional category of the component.
118
- @param {string} [customPath] - Optional custom path to fetch the resource from.
119
- @returns {Promise<string>} - A promise that resolves to the fetched text.
120
- */
121
135
  async fetchText(componentName, resourceType, componentCategory, customPath) {
122
136
  try {
123
137
  const baseUrl = window.location.origin;
@@ -130,12 +144,10 @@ export default class Controller {
130
144
  let isVisual = resourceType === 'html' || resourceType === 'css';
131
145
 
132
146
  if (isVisual) {
133
- // ✅ CORREGIDO: Verificar que la categoría existe y agregar baseUrl
134
147
  if (slice.paths.components[componentCategory]) {
135
148
  path = `${baseUrl}${slice.paths.components[componentCategory].path}/${componentName}`;
136
149
  resourceType === 'html' ? path += `/${componentName}.html` : path += `/${componentName}.css`;
137
150
  } else {
138
- // ✅ FALLBACK: Para componentes Structural o categorías no configuradas
139
151
  if (componentCategory === 'Structural') {
140
152
  path = `${baseUrl}/Slice/Components/Structural/${componentName}`;
141
153
  resourceType === 'html' ? path += `/${componentName}.html` : path += `/${componentName}.css`;
@@ -157,7 +169,6 @@ export default class Controller {
157
169
  path = customPath;
158
170
  }
159
171
 
160
- // ✅ MEJORADO: Logging para debugging
161
172
  slice.logger.logInfo('Controller', `Fetching ${resourceType} from: ${path}`);
162
173
 
163
174
  const response = await fetch(path);
@@ -171,7 +182,6 @@ export default class Controller {
171
182
  return content;
172
183
  } catch (error) {
173
184
  slice.logger.logError('Controller', `Error fetching ${resourceType} for component ${componentName}:`, error);
174
- // ✅ CORREGIDO: Re-lanzar el error para que el debugger pueda manejarlo
175
185
  throw error;
176
186
  }
177
187
  }
@@ -180,17 +190,17 @@ export default class Controller {
180
190
  const ComponentClass = component.constructor;
181
191
  const componentName = ComponentClass.name;
182
192
 
183
- // Aplicar defaults si tiene static props
193
+ // Aplicar defaults si tiene static props
184
194
  if (ComponentClass.props) {
185
195
  this.applyDefaultProps(component, ComponentClass.props, props);
186
196
  }
187
197
 
188
- // Validar solo en desarrollo
198
+ // Validar solo en desarrollo
189
199
  if (ComponentClass.props && !slice.isProduction()) {
190
200
  this.validatePropsInDevelopment(ComponentClass, props, componentName);
191
201
  }
192
202
 
193
- // ✅ CÓDIGO EXISTENTE: Aplicar props
203
+ // Aplicar props
194
204
  for (const prop in props) {
195
205
  component[`_${prop}`] = null;
196
206
  component[prop] = props[prop];
@@ -201,14 +211,12 @@ export default class Controller {
201
211
  const ComponentClass = component.constructor;
202
212
 
203
213
  if (ComponentClass.props) {
204
- // Si tiene props estáticos, usar esos como referencia
205
214
  return {
206
215
  availableProps: Object.keys(ComponentClass.props),
207
216
  propsConfig: ComponentClass.props,
208
217
  usedProps: this.extractUsedProps(component, ComponentClass.props)
209
218
  };
210
219
  } else {
211
- // Si no tiene props estáticos, usar modo legacy
212
220
  return {
213
221
  availableProps: this.extractUsedProps(component),
214
222
  propsConfig: null,
@@ -257,14 +265,12 @@ export default class Controller {
257
265
  const usedProps = {};
258
266
 
259
267
  if (staticProps) {
260
- // Si tiene props estáticos, buscar solo esos
261
268
  Object.keys(staticProps).forEach(prop => {
262
269
  if (component[prop] !== undefined) {
263
270
  usedProps[prop] = component[prop];
264
271
  }
265
272
  });
266
273
  } else {
267
- // Modo legacy: buscar cualquier prop que empiece con _
268
274
  Object.getOwnPropertyNames(component).forEach(key => {
269
275
  if (key.startsWith('_') && key !== '_isActive') {
270
276
  const propName = key.substring(1);
@@ -276,52 +282,135 @@ export default class Controller {
276
282
  return usedProps;
277
283
  }
278
284
 
285
+ // ============================================================================
286
+ // 🚀 MÉTODOS DE DESTRUCCIÓN OPTIMIZADOS
287
+ // ============================================================================
288
+
289
+ /**
290
+ * Encuentra recursivamente todos los hijos de un componente
291
+ * 🚀 OPTIMIZADO: O(m) en lugar de O(n*d) - usa childrenIndex
292
+ * @param {string} parentSliceId - sliceId del componente padre
293
+ * @param {Set<string>} collected - Set de sliceIds ya recolectados
294
+ * @returns {Set<string>} Set de todos los sliceIds de componentes hijos
295
+ */
296
+ findAllChildComponents(parentSliceId, collected = new Set()) {
297
+ // 🚀 Buscar directamente en el índice: O(1)
298
+ const children = this.childrenIndex.get(parentSliceId);
299
+
300
+ if (!children) return collected;
301
+
302
+ // 🚀 Iterar solo los hijos directos: O(k) donde k = número de hijos
303
+ for (const childSliceId of children) {
304
+ collected.add(childSliceId);
305
+ // Recursión solo sobre hijos, no todos los componentes
306
+ this.findAllChildComponents(childSliceId, collected);
307
+ }
308
+
309
+ return collected;
310
+ }
311
+
312
+ /**
313
+ * Encuentra recursivamente todos los componentes dentro de un contenedor DOM
314
+ * Útil para destroyByContainer cuando no tenemos el sliceId del padre
315
+ * @param {HTMLElement} container - Elemento contenedor
316
+ * @param {Set<string>} collected - Set de sliceIds ya recolectados
317
+ * @returns {Set<string>} Set de todos los sliceIds encontrados
318
+ */
319
+ findAllNestedComponentsInContainer(container, collected = new Set()) {
320
+ // Buscar todos los elementos con slice-id en el contenedor
321
+ const sliceComponents = container.querySelectorAll('[slice-id]');
322
+
323
+ sliceComponents.forEach(element => {
324
+ const sliceId = element.getAttribute('slice-id') || element.sliceId;
325
+ if (sliceId && this.activeComponents.has(sliceId)) {
326
+ collected.add(sliceId);
327
+ // 🚀 Usar índice para buscar hijos recursivamente
328
+ this.findAllChildComponents(sliceId, collected);
329
+ }
330
+ });
331
+
332
+ return collected;
333
+ }
334
+
279
335
  /**
280
- * Destruye uno o múltiples componentes
336
+ * Destruye uno o múltiples componentes DE FORMA RECURSIVA
337
+ * 🚀 OPTIMIZADO: O(m log m) en lugar de O(n*d + m log m)
281
338
  * @param {HTMLElement|Array<HTMLElement>|string|Array<string>} components
282
- * - Componente individual (HTMLElement)
283
- * - Array de componentes
284
- * - sliceId individual (string)
285
- * - Array de sliceIds
286
- * @returns {number} Cantidad de componentes destruidos
339
+ * @returns {number} Cantidad de componentes destruidos (incluyendo hijos)
287
340
  */
288
341
  destroyComponent(components) {
289
- // Normalizar entrada a array
290
342
  const toDestroy = Array.isArray(components) ? components : [components];
291
- let destroyedCount = 0;
343
+ const allSliceIdsToDestroy = new Set();
292
344
 
345
+ // PASO 1: Recolectar todos los componentes padres y sus hijos recursivamente
293
346
  for (const item of toDestroy) {
294
- let component = null;
347
+ let sliceId = null;
295
348
 
296
- // Si es string, buscar el componente por sliceId
297
349
  if (typeof item === 'string') {
298
- component = this.activeComponents.get(item);
299
-
300
- if (!component) {
350
+ if (!this.activeComponents.has(item)) {
301
351
  slice.logger.logWarning('Controller', `Component with sliceId "${item}" not found`);
302
352
  continue;
303
353
  }
304
- }
305
- // Si es un componente directamente
306
- else if (item && item.sliceId) {
307
- component = item;
308
- }
309
- else {
354
+ sliceId = item;
355
+ } else if (item && item.sliceId) {
356
+ sliceId = item.sliceId;
357
+ } else {
310
358
  slice.logger.logWarning('Controller', `Invalid component or sliceId provided to destroyComponent`);
311
359
  continue;
312
360
  }
313
361
 
362
+ allSliceIdsToDestroy.add(sliceId);
363
+
364
+ // 🚀 OPTIMIZADO: Usa childrenIndex en lugar de recorrer todos los componentes
365
+ this.findAllChildComponents(sliceId, allSliceIdsToDestroy);
366
+ }
367
+
368
+ // PASO 2: Ordenar por profundidad (más profundos primero)
369
+ // 🚀 OPTIMIZADO: Usa _depth precalculada en lugar de calcularla cada vez
370
+ const sortedSliceIds = Array.from(allSliceIdsToDestroy).sort((a, b) => {
371
+ const compA = this.activeComponents.get(a);
372
+ const compB = this.activeComponents.get(b);
373
+
374
+ if (!compA || !compB) return 0;
375
+
376
+ // 🚀 O(1) en lugar de O(d) - usa profundidad precalculada
377
+ return (compB._depth || 0) - (compA._depth || 0);
378
+ });
379
+
380
+ let destroyedCount = 0;
381
+
382
+ // PASO 3: Destruir en orden correcto (hijos antes que padres)
383
+ for (const sliceId of sortedSliceIds) {
384
+ const component = this.activeComponents.get(sliceId);
385
+
386
+ if (!component) continue;
387
+
314
388
  // Ejecutar hook beforeDestroy si existe
315
389
  if (typeof component.beforeDestroy === 'function') {
316
390
  try {
317
391
  component.beforeDestroy();
318
392
  } catch (error) {
319
- slice.logger.logError('Controller', `Error in beforeDestroy for ${component.sliceId}`, error);
393
+ slice.logger.logError('Controller', `Error in beforeDestroy for ${sliceId}`, error);
394
+ }
395
+ }
396
+
397
+ // 🚀 Limpiar del índice de hijos
398
+ this.childrenIndex.delete(sliceId);
399
+
400
+ // Si tiene padre, remover de la lista de hijos del padre
401
+ if (component.parentComponent) {
402
+ const parentChildren = this.childrenIndex.get(component.parentComponent.sliceId);
403
+ if (parentChildren) {
404
+ parentChildren.delete(sliceId);
405
+ // Si el padre no tiene más hijos, eliminar entrada vacía
406
+ if (parentChildren.size === 0) {
407
+ this.childrenIndex.delete(component.parentComponent.sliceId);
408
+ }
320
409
  }
321
410
  }
322
411
 
323
412
  // Eliminar del mapa de componentes activos
324
- this.activeComponents.delete(component.sliceId);
413
+ this.activeComponents.delete(sliceId);
325
414
 
326
415
  // Remover del DOM si está conectado
327
416
  if (component.isConnected) {
@@ -332,14 +421,15 @@ export default class Controller {
332
421
  }
333
422
 
334
423
  if (destroyedCount > 0) {
335
- slice.logger.logInfo('Controller', `Destroyed ${destroyedCount} component(s)`);
424
+ slice.logger.logInfo('Controller', `Destroyed ${destroyedCount} component(s) recursively`);
336
425
  }
337
426
 
338
427
  return destroyedCount;
339
428
  }
340
429
 
341
430
  /**
342
- * Destruye todos los componentes Slice dentro de un contenedor
431
+ * Destruye todos los componentes Slice dentro de un contenedor (RECURSIVO)
432
+ * 🚀 OPTIMIZADO: Usa el índice inverso para búsqueda rápida
343
433
  * @param {HTMLElement} container - Elemento contenedor
344
434
  * @returns {number} Cantidad de componentes destruidos
345
435
  */
@@ -349,56 +439,49 @@ export default class Controller {
349
439
  return 0;
350
440
  }
351
441
 
352
- // Buscar todos los elementos que sean componentes Slice
353
- const sliceComponents = container.querySelectorAll('[slice-id]');
354
- const sliceIdsToDestroy = [];
355
-
356
- sliceComponents.forEach(element => {
357
- const sliceId = element.getAttribute('slice-id') || element.sliceId;
358
- if (sliceId && this.activeComponents.has(sliceId)) {
359
- sliceIdsToDestroy.push(sliceId);
360
- }
361
- });
442
+ // 🚀 Recolectar componentes usando índice optimizado
443
+ const allSliceIds = this.findAllNestedComponentsInContainer(container);
444
+
445
+ if (allSliceIds.size === 0) {
446
+ return 0;
447
+ }
362
448
 
363
- // Destruir usando el método principal
364
- const count = this.destroyComponent(sliceIdsToDestroy);
449
+ // Destruir usando el método principal optimizado
450
+ const count = this.destroyComponent(Array.from(allSliceIds));
365
451
 
366
452
  if (count > 0) {
367
- slice.logger.logInfo('Controller', `Destroyed ${count} component(s) from container`);
453
+ slice.logger.logInfo('Controller', `Destroyed ${count} component(s) from container (including nested)`);
368
454
  }
369
455
 
370
456
  return count;
371
457
  }
372
458
 
373
459
  /**
374
- * Destruye componentes cuyos sliceId coincidan con un patrón
375
- * @param {string|RegExp} pattern - Patrón a buscar (string o expresión regular)
460
+ * Destruye componentes cuyos sliceId coincidan con un patrón (RECURSIVO)
461
+ * 🚀 OPTIMIZADO: Usa destrucción optimizada
462
+ * @param {string|RegExp} pattern - Patrón a buscar
376
463
  * @returns {number} Cantidad de componentes destruidos
377
464
  */
378
465
  destroyByPattern(pattern) {
379
466
  const componentsToDestroy = [];
380
-
381
- // Convertir string a RegExp si es necesario
382
467
  const regex = pattern instanceof RegExp ? pattern : new RegExp(pattern);
383
468
 
384
- // Buscar componentes que coincidan con el patrón
385
469
  for (const [sliceId, component] of this.activeComponents) {
386
470
  if (regex.test(sliceId)) {
387
471
  componentsToDestroy.push(component);
388
472
  }
389
473
  }
390
474
 
391
- // Destruir usando el método principal
475
+ if (componentsToDestroy.length === 0) {
476
+ return 0;
477
+ }
478
+
392
479
  const count = this.destroyComponent(componentsToDestroy);
393
480
 
394
481
  if (count > 0) {
395
- slice.logger.logInfo('Controller', `Destroyed ${count} component(s) matching pattern: ${pattern}`);
482
+ slice.logger.logInfo('Controller', `Destroyed ${count} component(s) matching pattern: ${pattern} (including nested)`);
396
483
  }
397
484
 
398
485
  return count;
399
486
  }
400
- }
401
-
402
- function getRelativePath(levels) {
403
- return '../'.repeat(levels);
404
487
  }
@@ -139,7 +139,7 @@ export default class Router {
139
139
  * Ejecuta el beforeEach guard si existe
140
140
  * @param {Object} to - Información de ruta destino
141
141
  * @param {Object} from - Información de ruta origen
142
- * @returns {String|null} Path de redirección o null si continúa
142
+ * @returns {Object|null} Objeto con redirectPath y options, o null si continúa
143
143
  */
144
144
  async _executeBeforeEachGuard(to, from) {
145
145
  if (!this._beforeEachGuard) {
@@ -147,18 +147,45 @@ export default class Router {
147
147
  }
148
148
 
149
149
  let redirectPath = null;
150
+ let redirectOptions = {};
150
151
  let nextCalled = false;
151
152
 
152
- const next = (path) => {
153
+ const next = (arg) => {
153
154
  if (nextCalled) {
154
155
  slice.logger.logWarning('Router', 'next() called multiple times in guard');
155
156
  return;
156
157
  }
157
158
  nextCalled = true;
158
159
 
159
- if (path && typeof path === 'string') {
160
- redirectPath = path;
160
+ // Caso 1: Sin argumentos - continuar navegación
161
+ if (arg === undefined) {
162
+ return;
163
+ }
164
+
165
+ // Caso 2: false - cancelar navegación
166
+ if (arg === false) {
167
+ redirectPath = false;
168
+ return;
169
+ }
170
+
171
+ // Caso 3: String - redirección simple (backward compatibility)
172
+ if (typeof arg === 'string') {
173
+ redirectPath = arg;
174
+ redirectOptions = { replace: false };
175
+ return;
176
+ }
177
+
178
+ // Caso 4: Objeto - redirección con opciones
179
+ if (typeof arg === 'object' && arg.path) {
180
+ redirectPath = arg.path;
181
+ redirectOptions = {
182
+ replace: arg.replace || false
183
+ };
184
+ return;
161
185
  }
186
+
187
+ // Argumento inválido
188
+ slice.logger.logError('Router', 'Invalid argument passed to next(). Expected string, object with path, false, or undefined.');
162
189
  };
163
190
 
164
191
  try {
@@ -172,7 +199,8 @@ export default class Router {
172
199
  );
173
200
  }
174
201
 
175
- return redirectPath;
202
+ // Retornar tanto el path como las opciones
203
+ return redirectPath ? { path: redirectPath, options: redirectOptions } : null;
176
204
  } catch (error) {
177
205
  slice.logger.logError('Router', 'Error in beforeEach guard', error);
178
206
  return null; // En caso de error, continuar con la navegación
@@ -200,7 +228,7 @@ export default class Router {
200
228
  // ROUTING CORE (MODIFICADO CON GUARDS)
201
229
  // ============================================
202
230
 
203
- async navigate(path, _redirectChain = []) {
231
+ async navigate(path, _redirectChain = [], _options = {}) {
204
232
  const currentPath = window.location.pathname;
205
233
 
206
234
 
@@ -228,21 +256,32 @@ export default class Router {
228
256
 
229
257
  // Obtener información de ruta destino
230
258
  const { route: toRoute, params: toParams } = this.matchRoute(path);
231
- const to = this._createRouteInfo(toRoute, toParams, path); // ← PASAR EL PATH AQUÍ
259
+ const to = this._createRouteInfo(toRoute, toParams, path);
232
260
 
233
261
 
234
262
  // EJECUTAR BEFORE EACH GUARD
235
- const redirectPath = await this._executeBeforeEachGuard(to, from);
263
+ const guardResult = await this._executeBeforeEachGuard(to, from);
236
264
 
237
- // Si el guard redirige, agregar ruta actual a la cadena y navegar a la nueva ruta
238
- if (redirectPath) {
265
+ // Si el guard redirige
266
+ if (guardResult && guardResult.path) {
239
267
  const newChain = [..._redirectChain, path];
268
+ return this.navigate(guardResult.path, newChain, guardResult.options);
269
+ }
240
270
 
241
- return this.navigate(redirectPath, newChain);
271
+ // Si el guard cancela la navegación (next(false))
272
+ if (guardResult && guardResult.path === false) {
273
+ slice.logger.logInfo('Router', 'Navigation cancelled by guard');
274
+ return;
242
275
  }
243
276
 
244
277
  // No hay redirección - continuar con la navegación normal
245
- window.history.pushState({}, path, window.location.origin + path);
278
+ // Usar replace o push según las opciones
279
+ if (_options.replace) {
280
+ window.history.replaceState({}, path, window.location.origin + path);
281
+ } else {
282
+ window.history.pushState({}, path, window.location.origin + path);
283
+ }
284
+
246
285
  await this._performNavigation(to, from);
247
286
  }
248
287
 
@@ -323,19 +362,25 @@ export default class Router {
323
362
  slice.router.activeRoute = route;
324
363
  }
325
364
 
326
- async loadInitialRoute() {
365
+ async loadInitialRoute() {
327
366
  const path = window.location.pathname;
328
367
  const { route, params } = this.matchRoute(path);
329
368
 
330
369
  // Para la carga inicial, también ejecutar guards
331
370
  const from = this._createRouteInfo(null, {}, null);
332
- const to = this._createRouteInfo(route, params, path); // ← PASAR EL PATH AQUÍ
371
+ const to = this._createRouteInfo(route, params, path);
333
372
 
334
373
  // EJECUTAR BEFORE EACH GUARD en carga inicial
335
- const redirectPath = await this._executeBeforeEachGuard(to, from);
374
+ const guardResult = await this._executeBeforeEachGuard(to, from);
336
375
 
337
- if (redirectPath) {
338
- return this.navigate(redirectPath);
376
+ if (guardResult && guardResult.path) {
377
+ return this.navigate(guardResult.path, [], guardResult.options);
378
+ }
379
+
380
+ // Si el guard cancela la navegación inicial (caso raro pero posible)
381
+ if (guardResult && guardResult.path === false) {
382
+ slice.logger.logWarning('Router', 'Initial route navigation cancelled by guard');
383
+ return;
339
384
  }
340
385
 
341
386
  await this.handleRoute(route, params);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "slicejs-web-framework",
3
- "version": "2.2.2",
3
+ "version": "2.2.5",
4
4
  "description": "",
5
5
  "engines": {
6
6
  "node": ">=20"
package/src/App/index.js CHANGED
@@ -7,7 +7,7 @@ slice.router.beforeEach(async (to, from, next) => {
7
7
  if(to.metadata.private){
8
8
  const isAuthenticated = await //fetchlogic for validation
9
9
  if(!isAuthenticated){
10
- return next({ path: '/login' });
10
+ return next({ path: '/login', replace: true });
11
11
  }
12
12
  return next();
13
13
  }
@@ -1,196 +1,196 @@
1
- export default class HomePage extends HTMLElement {
2
- constructor(props) {
3
- super();
4
- slice.attachTemplate(this);
5
-
6
- this.$examplesContainer = this.querySelector('.examples-container');
7
-
8
- slice.controller.setComponentProps(this, props);
9
- this.debuggerProps = [];
10
- }
11
-
12
- async init() {
13
- // Crear la barra de navegación
14
- const navbar = await slice.build('Navbar', {
15
- position: 'fixed',
16
- logo: {
17
- src: '/images/Slice.js-logo.png',
18
- path: '/',
19
- },
20
- items: [
21
- { text: 'Home', path: '/' },
22
- { text: 'Playground', path: '/Playground' },
23
-
24
- ],
25
- buttons: [
26
- {
27
- value: 'Change Theme',
28
- onClickCallback: async () => {
29
- const currentTheme = slice.stylesManager.themeManager.currentTheme;
30
- if (currentTheme === 'Slice') {
31
- await slice.setTheme('Light');
32
- } else if (currentTheme === 'Light') {
33
- await slice.setTheme('Dark');
34
- } else {
35
- await slice.setTheme('Slice');
36
- }
37
- },
38
- },
39
- ],
40
- });
41
-
42
- // Crear botones para la sección de llamada a la acción
43
- const docsButton = await slice.build('Button', {
44
- value: 'Documentation',
45
- onClickCallback: () => //redirect to https://slice-js-docs.vercel.app/Documentation
46
- window.open('https://slice-js-docs.vercel.app/Documentation', '_blank'),
47
- customColor: {
48
- button: 'var(--primary-color)',
49
- label: 'var(--primary-color-contrast)'
50
- }
51
- });
52
-
53
- const componentsButton = await slice.build('Button', {
54
- value: 'Components Library',
55
- onClickCallback: () => window.open('https://slice-js-docs.vercel.app/Documentation/Visual', '_blank'),
56
- customColor: {
57
- button: 'var(--secondary-color)',
58
- label: 'var(--secondary-color-contrast)'
59
- }
60
- });
61
-
62
- // Añadir botones a la sección CTA
63
- this.querySelector('.cta-buttons').appendChild(docsButton);
64
- this.querySelector('.cta-buttons').appendChild(componentsButton);
65
-
66
- // Crear features section con un enfoque diferente (sin usar Cards)
67
- await this.createFeatures();
68
-
69
- // Crear ejemplos de componentes
70
- await this.createComponentExamples();
71
-
72
- // Configurar la sección de código de inicio
73
- await this.setupGettingStartedSection();
74
-
75
- // Añadir la barra de navegación al inicio del componente
76
- this.insertBefore(navbar, this.firstChild);
77
- }
78
-
79
- async createFeatures() {
80
- // Definir características
81
- const features = [
82
- {
83
- title: 'Component-Based',
84
- description: 'Build your app using modular, reusable components following web standards.'
85
- },
86
- {
87
- title: 'Zero Dependencies',
88
- description: 'Built with vanilla JavaScript. No external libraries required.'
89
- },
90
- {
91
- title: 'Easy Routing',
92
- description: 'Simple and powerful routing system for single page applications.'
93
- },
94
- {
95
- title: 'Theme System',
96
- description: 'Built-in theme support with easy customization through CSS variables.'
97
- },
98
- {
99
- title: 'Developer Tools',
100
- description: 'Integrated debugging and logging for faster development.'
101
- },
102
- {
103
- title: 'Performance Focused',
104
- description: 'Lightweight and optimized for fast loading and execution.'
105
- }
106
- ];
107
-
108
- const featureGrid = this.querySelector('.feature-grid');
109
-
110
- // Crear y añadir cada feature como un elemento HTML simple
111
- for (const feature of features) {
112
- const featureElement = document.createElement('div');
113
- featureElement.classList.add('feature-item');
114
-
115
- const featureTitle = document.createElement('h3');
116
- featureTitle.textContent = feature.title;
117
- featureTitle.classList.add('feature-title');
118
-
119
- const featureDescription = document.createElement('p');
120
- featureDescription.textContent = feature.description;
121
- featureDescription.classList.add('feature-description');
122
-
123
- featureElement.appendChild(featureTitle);
124
- featureElement.appendChild(featureDescription);
125
-
126
- featureGrid.appendChild(featureElement);
127
- }
128
- }
129
-
130
- async createComponentExamples() {
131
- // Crear ejemplos para demostrar componentes
132
- const inputExample = await slice.build('Input', {
133
- placeholder: 'Try typing here...',
134
- type: 'text'
135
- });
136
-
137
- const switchExample = await slice.build('Switch', {
138
- label: 'Toggle me',
139
- checked: true
140
- });
141
-
142
- const checkboxExample = await slice.build('Checkbox', {
143
- label: 'Check me',
144
- labelPlacement: 'right'
145
- });
146
-
147
- const detailsExample = await slice.build('Details', {
148
- title: 'Click to expand',
149
- text: 'This is a collapsible details component that can contain any content.'
150
- });
151
-
152
- // Crear sección para cada ejemplo
153
- const exampleSections = [
154
- { title: 'Input Component', component: inputExample },
155
- { title: 'Switch Component', component: switchExample },
156
- { title: 'Checkbox Component', component: checkboxExample },
157
- { title: 'Details Component', component: detailsExample }
158
- ];
159
-
160
- // Añadir cada ejemplo a la sección de ejemplos
161
- for (const section of exampleSections) {
162
- const container = document.createElement('div');
163
- container.classList.add('example-item');
164
-
165
- const title = document.createElement('h3');
166
- title.textContent = section.title;
167
-
168
- container.appendChild(title);
169
- container.appendChild(section.component);
170
-
171
- this.$examplesContainer.appendChild(container);
172
- }
173
- }
174
-
175
- async setupGettingStartedSection() {
176
- // Opcionalmente podríamos mejorar esta sección usando el CodeVisualizer component
177
- // en lugar del código HTML estático en el template
178
- const codeVisualizer = await slice.build('CodeVisualizer', {
179
- value: `// Initialize a new Slice.js project
180
- npm run slice:init
181
-
182
- // Create a new component
183
- npm run slice:create
184
-
185
- // Start your application
186
- npm run slice:start`,
187
- language: 'bash'
188
- });
189
-
190
- const codeSample = this.querySelector('.code-sample');
191
- codeSample.innerHTML = ''; // Clear the static code sample
192
- codeSample.appendChild(codeVisualizer);
193
- }
194
- }
195
-
1
+ export default class HomePage extends HTMLElement {
2
+ constructor(props) {
3
+ super();
4
+ slice.attachTemplate(this);
5
+
6
+ this.$examplesContainer = this.querySelector('.examples-container');
7
+
8
+ slice.controller.setComponentProps(this, props);
9
+ this.debuggerProps = [];
10
+ }
11
+
12
+ async init() {
13
+ // Crear la barra de navegación
14
+ const navbar = await slice.build('Navbar', {
15
+ position: 'fixed',
16
+ logo: {
17
+ src: '/images/Slice.js-logo.png',
18
+ path: '/',
19
+ },
20
+ items: [
21
+ { text: 'Home', path: '/' },
22
+ { text: 'Playground', path: '/Playground' },
23
+
24
+ ],
25
+ buttons: [
26
+ {
27
+ value: 'Change Theme',
28
+ onClickCallback: async () => {
29
+ const currentTheme = slice.stylesManager.themeManager.currentTheme;
30
+ if (currentTheme === 'Slice') {
31
+ await slice.setTheme('Light');
32
+ } else if (currentTheme === 'Light') {
33
+ await slice.setTheme('Dark');
34
+ } else {
35
+ await slice.setTheme('Slice');
36
+ }
37
+ },
38
+ },
39
+ ],
40
+ });
41
+
42
+ // Crear botones para la sección de llamada a la acción
43
+ const docsButton = await slice.build('Button', {
44
+ value: 'Documentation',
45
+ onClickCallback: () => //redirect to https://slice-js-docs.vercel.app/Documentation
46
+ window.open('https://slice-js-docs.vercel.app/Documentation', '_blank'),
47
+ customColor: {
48
+ button: 'var(--primary-color)',
49
+ label: 'var(--primary-color-contrast)'
50
+ }
51
+ });
52
+
53
+ const componentsButton = await slice.build('Button', {
54
+ value: 'Components Library',
55
+ onClickCallback: () => window.open('https://slice-js-docs.vercel.app/Documentation/Visual', '_blank'),
56
+ customColor: {
57
+ button: 'var(--secondary-color)',
58
+ label: 'var(--secondary-color-contrast)'
59
+ }
60
+ });
61
+
62
+ // Añadir botones a la sección CTA
63
+ this.querySelector('.cta-buttons').appendChild(docsButton);
64
+ this.querySelector('.cta-buttons').appendChild(componentsButton);
65
+
66
+ // Crear features section con un enfoque diferente (sin usar Cards)
67
+ await this.createFeatures();
68
+
69
+ // Crear ejemplos de componentes
70
+ await this.createComponentExamples();
71
+
72
+ // Configurar la sección de código de inicio
73
+ await this.setupGettingStartedSection();
74
+
75
+ // Añadir la barra de navegación al inicio del componente
76
+ this.insertBefore(navbar, this.firstChild);
77
+ }
78
+
79
+ async createFeatures() {
80
+ // Definir características
81
+ const features = [
82
+ {
83
+ title: 'Component-Based',
84
+ description: 'Build your app using modular, reusable components following web standards.'
85
+ },
86
+ {
87
+ title: 'Zero Dependencies',
88
+ description: 'Built with vanilla JavaScript. No external libraries required.'
89
+ },
90
+ {
91
+ title: 'Easy Routing',
92
+ description: 'Simple and powerful routing system for single page applications.'
93
+ },
94
+ {
95
+ title: 'Theme System',
96
+ description: 'Built-in theme support with easy customization through CSS variables.'
97
+ },
98
+ {
99
+ title: 'Developer Tools',
100
+ description: 'Integrated debugging and logging for faster development.'
101
+ },
102
+ {
103
+ title: 'Performance Focused',
104
+ description: 'Lightweight and optimized for fast loading and execution.'
105
+ }
106
+ ];
107
+
108
+ const featureGrid = this.querySelector('.feature-grid');
109
+
110
+ // Crear y añadir cada feature como un elemento HTML simple
111
+ for (const feature of features) {
112
+ const featureElement = document.createElement('div');
113
+ featureElement.classList.add('feature-item');
114
+
115
+ const featureTitle = document.createElement('h3');
116
+ featureTitle.textContent = feature.title;
117
+ featureTitle.classList.add('feature-title');
118
+
119
+ const featureDescription = document.createElement('p');
120
+ featureDescription.textContent = feature.description;
121
+ featureDescription.classList.add('feature-description');
122
+
123
+ featureElement.appendChild(featureTitle);
124
+ featureElement.appendChild(featureDescription);
125
+
126
+ featureGrid.appendChild(featureElement);
127
+ }
128
+ }
129
+
130
+ async createComponentExamples() {
131
+ // Crear ejemplos para demostrar componentes
132
+ const inputExample = await slice.build('Input', {
133
+ placeholder: 'Try typing here...',
134
+ type: 'text'
135
+ });
136
+
137
+ const switchExample = await slice.build('Switch', {
138
+ label: 'Toggle me',
139
+ checked: true
140
+ });
141
+
142
+ const checkboxExample = await slice.build('Checkbox', {
143
+ label: 'Check me',
144
+ labelPlacement: 'right'
145
+ });
146
+
147
+ const detailsExample = await slice.build('Details', {
148
+ title: 'Click to expand',
149
+ text: 'This is a collapsible details component that can contain any content.'
150
+ });
151
+
152
+ // Crear sección para cada ejemplo
153
+ const exampleSections = [
154
+ { title: 'Input Component', component: inputExample },
155
+ { title: 'Switch Component', component: switchExample },
156
+ { title: 'Checkbox Component', component: checkboxExample },
157
+ { title: 'Details Component', component: detailsExample }
158
+ ];
159
+
160
+ // Añadir cada ejemplo a la sección de ejemplos
161
+ for (const section of exampleSections) {
162
+ const container = document.createElement('div');
163
+ container.classList.add('example-item');
164
+
165
+ const title = document.createElement('h3');
166
+ title.textContent = section.title;
167
+
168
+ container.appendChild(title);
169
+ container.appendChild(section.component);
170
+
171
+ this.$examplesContainer.appendChild(container);
172
+ }
173
+ }
174
+
175
+ async setupGettingStartedSection() {
176
+ // Opcionalmente podríamos mejorar esta sección usando el CodeVisualizer component
177
+ // en lugar del código HTML estático en el template
178
+ const codeVisualizer = await slice.build('CodeVisualizer', {
179
+ value: `// Initialize a new Slice.js project
180
+ npm run slice:init
181
+
182
+ // Create a new component
183
+ npm run slice:create
184
+
185
+ // Start your application
186
+ npm run slice:start`,
187
+ language: 'bash'
188
+ });
189
+
190
+ const codeSample = this.querySelector('.code-sample');
191
+ codeSample.innerHTML = ''; // Clear the static code sample
192
+ codeSample.appendChild(codeVisualizer);
193
+ }
194
+ }
195
+
196
196
  customElements.define('slice-home-page', HomePage);
@@ -1,28 +1,28 @@
1
- const components = {
2
- "HomePage": "AppComponents",
3
- "Playground": "AppComponents",
4
- "Button": "Visual",
5
- "Card": "Visual",
6
- "Checkbox": "Visual",
7
- "CodeVisualizer": "Visual",
8
- "Details": "Visual",
9
- "DropDown": "Visual",
10
- "Grid": "Visual",
11
- "Icon": "Visual",
12
- "Input": "Visual",
13
- "Layout": "Visual",
14
- "Loading": "Visual",
15
- "MultiRoute": "Visual",
16
- "Navbar": "Visual",
17
- "NotFound": "Visual",
18
- "Route": "Visual",
19
- "Select": "Visual",
20
- "Switch": "Visual",
21
- "TreeItem": "Visual",
22
- "TreeView": "Visual",
23
- "FetchManager": "Service",
24
- "IndexedDbManager": "Service",
25
- "Link": "Service",
26
- "LocalStorageManager": "Service",
27
- "Translator": "Service"
1
+ const components = {
2
+ "HomePage": "AppComponents",
3
+ "Playground": "AppComponents",
4
+ "Button": "Visual",
5
+ "Card": "Visual",
6
+ "Checkbox": "Visual",
7
+ "CodeVisualizer": "Visual",
8
+ "Details": "Visual",
9
+ "DropDown": "Visual",
10
+ "Grid": "Visual",
11
+ "Icon": "Visual",
12
+ "Input": "Visual",
13
+ "Layout": "Visual",
14
+ "Loading": "Visual",
15
+ "MultiRoute": "Visual",
16
+ "Navbar": "Visual",
17
+ "NotFound": "Visual",
18
+ "Route": "Visual",
19
+ "Select": "Visual",
20
+ "Switch": "Visual",
21
+ "TreeItem": "Visual",
22
+ "TreeView": "Visual",
23
+ "FetchManager": "Service",
24
+ "IndexedDbManager": "Service",
25
+ "Link": "Service",
26
+ "LocalStorageManager": "Service",
27
+ "Translator": "Service"
28
28
  }; export default components;