slicejs-web-framework 2.4.4 → 2.4.6

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/Slice/Slice.js CHANGED
@@ -254,30 +254,20 @@ async function init() {
254
254
  return;
255
255
  }
256
256
 
257
- // 1. Resolve runtime mode via dev server endpoint.
258
- // In production the endpoint returns 404 (not registered), so catch/non-ok is expected.
259
- let envMode = null;
260
- try {
261
- const envRes = await fetch('/slice-env.json', { cache: 'no-store' });
262
- if (envRes.ok) {
263
- const env = await envRes.json();
264
- envMode = env.mode; // 'development' | 'production'
265
- }
266
- } catch (error) {
267
- // Endpoint not available — normal in production
268
- }
269
-
270
- // 2. Fetch bundle config (existing logic, unchanged)
257
+ // 1+2. Fetch mode endpoint and bundle config in parallel — both are independent.
258
+ // In production, /slice-env.json returns 404 (catch is expected and normal).
259
+ // bundleConfigJson.production serves as a mode fallback when env endpoint is absent.
271
260
  let frameworkClasses = null;
272
- let bundleConfigJson = null;
273
- try {
274
- const response = await fetch('/bundles/bundle.config.json', { cache: 'no-store' });
275
- if (response.ok) {
276
- bundleConfigJson = await response.json();
277
- }
278
- } catch (error) {
279
- // ignore
280
- }
261
+ const [envResult, configResult] = await Promise.all([
262
+ fetch('/slice-env.json', { cache: 'no-store' })
263
+ .then(r => r.ok ? r.json() : null)
264
+ .catch(() => null),
265
+ fetch('/bundles/bundle.config.json', { cache: 'no-store' })
266
+ .then(r => r.ok ? r.json() : null)
267
+ .catch(() => null)
268
+ ]);
269
+ const envMode = envResult?.mode ?? null;
270
+ const bundleConfigJson = configResult;
281
271
 
282
272
  // 3. Determine canonical mode: env endpoint takes precedence, then bundle config
283
273
  let resolvedMode;
@@ -289,6 +279,17 @@ async function init() {
289
279
  resolvedMode = 'development';
290
280
  }
291
281
 
282
+ // Pre-fetch critical bundle to warm the browser HTTP cache while the framework
283
+ // bundle is downloading and executing. fetch() downloads bytes without evaluating
284
+ // the module, so the auto-registration block runs safely later when window.slice
285
+ // already exists. Errors are silently ignored — import() will retry if needed.
286
+ const criticalBundleUrl = (resolvedMode === 'production' && bundleConfigJson?.bundles?.critical?.file)
287
+ ? `/bundles/${bundleConfigJson.bundles.critical.file}`
288
+ : null;
289
+ if (criticalBundleUrl) {
290
+ fetch(criticalBundleUrl).catch(() => {});
291
+ }
292
+
292
293
  // 4. Load framework classes.
293
294
  // In production the bundler generates slice-bundle.framework.js which
294
295
  // sets window.SLICE_FRAMEWORK_CLASSES. In dev mode always use individual
@@ -336,12 +337,18 @@ async function init() {
336
337
  if (window.slice.controller.bundleConfig) {
337
338
  const config = window.slice.controller.bundleConfig;
338
339
 
339
- const criticalFile = config?.bundles?.critical?.file;
340
- if (criticalFile) {
341
- // Bundle auto-registers itself on import via its own registration block.
342
- // No explicit registerBundle() call needed — flags are set inside registerBundle().
343
- await import(`/bundles/${criticalFile}`);
344
- }
340
+ const criticalFile = config?.bundles?.critical?.file;
341
+ if (criticalFile) {
342
+ // Bundle auto-registers itself on import via its own registration block.
343
+ // The block pushes its registerBundle() Promise to window.__slicePendingRegistrations
344
+ // so we can await full chunk processing before continuing to build('Loading').
345
+ // criticalBundleUrl was pre-fetched above — import() reuses the cached bytes.
346
+ await import(criticalBundleUrl);
347
+ if (window.__slicePendingRegistrations?.length) {
348
+ await Promise.all(window.__slicePendingRegistrations);
349
+ window.__slicePendingRegistrations = [];
350
+ }
351
+ }
345
352
 
346
353
  const routeBundles = config?.routeBundles || {};
347
354
  const initialPath = window.location.pathname || '/';
package/api/index.js CHANGED
@@ -199,6 +199,14 @@ if (runMode === 'development') {
199
199
  }
200
200
  return res.status(404).send('routes.js not found');
201
201
  });
202
+ app.get('/sliceConfig.json', (req, res) => {
203
+ const configPath = path.join(__dirname, `../${folderDeployed}`, 'sliceConfig.json');
204
+ if (fs.existsSync(configPath)) {
205
+ res.setHeader('Content-Type', 'application/json; charset=utf-8');
206
+ return res.send(fs.readFileSync(configPath, 'utf8'));
207
+ }
208
+ return res.status(404).send('sliceConfig.json not found');
209
+ });
202
210
  for (const folder of normalizedPublicFolders) {
203
211
  app.use(folder, express.static(path.join(__dirname, `../${folderDeployed}`, folder)));
204
212
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "slicejs-web-framework",
3
- "version": "2.4.4",
3
+ "version": "2.4.6",
4
4
  "description": "",
5
5
  "engines": {
6
6
  "node": ">=20"
@@ -6,200 +6,196 @@ slice-home-page {
6
6
  color: var(--font-primary-color);
7
7
  }
8
8
 
9
- /* Hero section styles */
9
+ /* ── HERO ── */
10
10
  .hero-section {
11
- position: relative;
12
11
  min-height: 80vh;
13
12
  display: flex;
14
13
  align-items: center;
15
14
  justify-content: center;
16
15
  text-align: center;
17
- padding: 2rem;
16
+ padding: 7rem 2rem 5rem;
18
17
  background-color: var(--primary-background-color);
18
+ position: relative;
19
19
  overflow: hidden;
20
20
  }
21
21
 
22
+ .hero-section::before {
23
+ content: '';
24
+ position: absolute;
25
+ top: -150px;
26
+ left: 50%;
27
+ transform: translateX(-50%);
28
+ width: 700px;
29
+ height: 700px;
30
+ background: radial-gradient(
31
+ circle,
32
+ rgba(var(--primary-color-rgb), 0.07) 0%,
33
+ transparent 70%
34
+ );
35
+ pointer-events: none;
36
+ }
37
+
22
38
  .hero-content {
23
- max-width: 800px;
39
+ max-width: 640px;
40
+ position: relative;
24
41
  z-index: 1;
25
42
  }
26
43
 
27
- .hero-logo-container {
44
+ .hero-badge {
45
+ display: inline-block;
46
+ background: rgba(var(--primary-color-rgb), 0.1);
47
+ color: var(--primary-color);
48
+ font-size: 0.75rem;
49
+ font-weight: 700;
50
+ letter-spacing: 0.08em;
51
+ text-transform: uppercase;
52
+ padding: 0.3rem 0.85rem;
53
+ border-radius: 20px;
28
54
  margin-bottom: 1.5rem;
29
55
  }
30
56
 
31
- .hero-logo {
32
- max-width: 150px;
33
- height: auto;
34
- /* Convertir el logo a una silueta */
35
- filter: brightness(0) drop-shadow(0 0 8px var(--primary-color));
36
- }
37
-
38
57
  .hero-title {
39
58
  font-size: 3rem;
40
- margin-bottom: 1rem;
59
+ font-weight: 800;
41
60
  color: var(--font-primary-color);
61
+ line-height: 1.1;
62
+ margin-bottom: 1rem;
63
+ letter-spacing: -0.03em;
42
64
  }
43
65
 
44
66
  .hero-title .highlight {
45
67
  color: var(--primary-color);
46
68
  }
47
69
 
48
- .hero-subtitle {
49
- font-size: 1.5rem;
50
- margin-bottom: 1.5rem;
70
+ .hero-sub {
71
+ font-size: 1.05rem;
51
72
  color: var(--font-secondary-color);
52
- }
53
-
54
- .hero-description {
55
- font-size: 1.1rem;
56
73
  margin-bottom: 2rem;
57
74
  line-height: 1.6;
58
75
  }
59
76
 
60
- .cta-buttons {
77
+ .hero-cta {
61
78
  display: flex;
62
- gap: 1rem;
79
+ gap: 0.75rem;
63
80
  justify-content: center;
64
- margin-top: 2rem;
81
+ margin-bottom: 2.5rem;
65
82
  }
66
83
 
67
- /* Features section styles */
84
+ .hero-chips {
85
+ display: flex;
86
+ gap: 0.5rem;
87
+ justify-content: center;
88
+ flex-wrap: wrap;
89
+ }
90
+
91
+ .hero-chip {
92
+ background: white;
93
+ border: 1px solid var(--disabled-color);
94
+ color: var(--primary-color);
95
+ font-size: 0.75rem;
96
+ font-weight: 600;
97
+ padding: 0.3rem 0.75rem;
98
+ border-radius: 20px;
99
+ box-shadow: var(--box-shadow-primary);
100
+ }
101
+
102
+ /* ── FEATURES ── */
68
103
  .features-section {
69
104
  padding: 5rem 2rem;
70
- background-color: var(--secondary-background-color);
105
+ background: white;
106
+ border-top: 1px solid var(--disabled-color);
71
107
  }
72
108
 
73
- .section-title {
109
+ .section-header {
74
110
  text-align: center;
75
- font-size: 2.5rem;
76
- margin-bottom: 3rem;
77
- color: var(--primary-color);
111
+ margin-bottom: 2.5rem;
112
+ }
113
+
114
+ .section-label {
115
+ font-size: 0.72rem;
116
+ font-weight: 700;
117
+ color: var(--accent-color);
118
+ text-transform: uppercase;
119
+ letter-spacing: 0.1em;
120
+ margin-bottom: 0.4rem;
121
+ }
122
+
123
+ .section-title {
124
+ font-size: 1.9rem;
125
+ font-weight: 800;
126
+ color: var(--font-primary-color);
127
+ letter-spacing: -0.02em;
78
128
  }
79
129
 
80
130
  .feature-grid {
81
131
  display: grid;
82
132
  grid-template-columns: repeat(3, 1fr);
83
- gap: 2rem;
84
- max-width: 1200px;
133
+ gap: 1.2rem;
134
+ max-width: 820px;
85
135
  margin: 0 auto;
86
136
  }
87
137
 
88
138
  .feature-item {
89
- padding: 2rem;
90
- background-color: var(--primary-background-color);
91
- border-radius: var(--border-radius-slice);
92
- box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
93
- transition: transform 0.3s ease, box-shadow 0.3s ease;
139
+ background: var(--primary-background-color);
140
+ border-radius: 10px;
141
+ padding: 1.4rem 1.2rem;
142
+ border: 1px solid var(--disabled-color);
143
+ transition: box-shadow 0.2s, transform 0.15s;
94
144
  }
95
145
 
96
146
  .feature-item:hover {
97
- transform: translateY(-5px);
98
- box-shadow: 0 8px 24px rgba(0, 0, 0, 0.15);
147
+ box-shadow: var(--box-shadow-primary);
148
+ transform: translateY(-2px);
99
149
  }
100
150
 
101
151
  .feature-title {
102
- font-size: 1.5rem;
103
- margin-bottom: 1rem;
104
- color: var(--primary-color);
152
+ font-size: 0.9rem;
153
+ font-weight: 700;
154
+ color: var(--font-primary-color);
155
+ margin-bottom: 0.35rem;
105
156
  }
106
157
 
107
158
  .feature-description {
108
- color: var(--font-secondary-color);
109
- line-height: 1.6;
159
+ font-size: 0.78rem;
160
+ color: var(--medium-color);
161
+ line-height: 1.5;
110
162
  }
111
163
 
112
- /* Example section styles */
113
- .example-section {
164
+ /* ── SHOWCASE ── */
165
+ .showcase-section {
114
166
  padding: 5rem 2rem;
115
- background-color: var(--primary-background-color);
167
+ background: var(--primary-background-color);
168
+ border-top: 1px solid var(--disabled-color);
116
169
  }
117
170
 
118
- .section-description {
119
- text-align: center;
120
- max-width: 800px;
121
- margin: 0 auto 3rem auto;
122
- color: var(--font-secondary-color);
123
- font-size: 1.2rem;
124
- }
125
-
126
- .examples-container {
171
+ .showcase-grid {
127
172
  display: grid;
128
- grid-template-columns: repeat(2, 1fr);
129
- gap: 3rem;
130
- max-width: 1000px;
131
- margin: 0 auto;
132
- }
133
-
134
- .example-item {
135
- display: flex;
136
- flex-direction: column;
173
+ grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
137
174
  gap: 1rem;
138
- padding: 2rem;
139
- background-color: var(--secondary-background-color);
140
- border-radius: var(--border-radius-slice);
141
- box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
142
- }
143
-
144
- .example-item h3 {
145
- margin: 0;
146
- font-size: 1.3rem;
147
- color: var(--primary-color);
175
+ max-width: 860px;
176
+ margin: 0 auto;
148
177
  }
149
178
 
150
- /* Getting started section styles */
151
- .getting-started-section {
152
- padding: 5rem 2rem;
153
- background-color: var(--secondary-background-color);
154
- text-align: center;
179
+ .comp-card {
180
+ background: white;
181
+ border-radius: 10px;
182
+ padding: 1.3rem 1.2rem;
183
+ border: 1px solid var(--disabled-color);
184
+ box-shadow: 0 2px 8px rgba(var(--primary-color-rgb), 0.06);
155
185
  }
156
186
 
157
- .code-sample {
158
- max-width: 800px;
159
- margin: 0 auto;
160
- text-align: left;
187
+ .comp-label {
188
+ font-size: 0.65rem;
189
+ font-weight: 700;
190
+ color: var(--medium-color);
191
+ text-transform: uppercase;
192
+ letter-spacing: 0.1em;
193
+ margin-bottom: 0.75rem;
161
194
  }
162
195
 
163
- /* Footer styles */
164
- .home-footer {
165
- padding: 3rem 2rem;
166
- text-align: center;
167
- background-color: var(--primary-color);
168
- color: var(--primary-color-contrast);
196
+ .comp-demo {
197
+ display: flex;
198
+ align-items: center;
199
+ gap: 0.6rem;
200
+ flex-wrap: wrap;
169
201
  }
170
-
171
- .copyright {
172
- margin-top: 1rem;
173
- font-size: 0.9rem;
174
- opacity: 0.8;
175
- }
176
-
177
- /* Responsive styles */
178
- @media (max-width: 992px) {
179
- .feature-grid {
180
- grid-template-columns: repeat(2, 1fr);
181
- }
182
-
183
- .examples-container {
184
- grid-template-columns: 1fr;
185
- }
186
- }
187
-
188
- @media (max-width: 768px) {
189
- .hero-title {
190
- font-size: 2.5rem;
191
- }
192
-
193
- .hero-subtitle {
194
- font-size: 1.3rem;
195
- }
196
-
197
- .feature-grid {
198
- grid-template-columns: 1fr;
199
- }
200
-
201
- .cta-buttons {
202
- flex-direction: column;
203
- align-items: center;
204
- }
205
- }
@@ -1,49 +1,37 @@
1
1
  <div class="home-page-container">
2
- <div class="hero-section">
2
+
3
+ <section class="hero-section">
3
4
  <div class="hero-content">
4
- <div class="hero-logo-container">
5
- <img src="/images/Slice.js-logo.png" alt="Slice.js Logo" class="hero-logo" />
5
+ <div class="hero-badge">v2.4 · Vanilla JS Framework</div>
6
+ <h1 class="hero-title">Build your web app<br>one <span class="highlight">Slice</span> at a time</h1>
7
+ <p class="hero-sub">A lightweight, component-based framework for building web applications with vanilla JavaScript and web standards.</p>
8
+ <div class="hero-cta"></div>
9
+ <div class="hero-chips">
10
+ <span class="hero-chip">Button</span>
11
+ <span class="hero-chip">Input</span>
12
+ <span class="hero-chip">Switch</span>
13
+ <span class="hero-chip">Navbar</span>
14
+ <span class="hero-chip">Grid</span>
15
+ <span class="hero-chip">Card</span>
16
+ <span class="hero-chip">+ more</span>
6
17
  </div>
7
- <h1 class="hero-title">Welcome to <span class="highlight">Slice.js</span></h1>
8
- <p class="hero-subtitle">Build Your Web App One Slice at a Time</p>
9
- <p class="hero-description">
10
- A modern, lightweight component-based framework for building web applications using vanilla JavaScript and web standards.
11
- </p>
12
- <div class="cta-buttons"></div>
13
18
  </div>
14
- </div>
19
+ </section>
15
20
 
16
21
  <section class="features-section">
17
- <h2 class="section-title">Why Choose Slice.js?</h2>
22
+ <div class="section-header">
23
+ <p class="section-label">Why Slice.js</p>
24
+ <h2 class="section-title">Everything you need, nothing you don't</h2>
25
+ </div>
18
26
  <div class="feature-grid"></div>
19
27
  </section>
20
28
 
21
- <section class="example-section">
22
- <h2 class="section-title">Component Showcase</h2>
23
- <p class="section-description">
24
- Explore some of the ready-to-use components that come with Slice.js
25
- </p>
26
- <div class="examples-container"></div>
27
- </section>
28
-
29
- <section class="getting-started-section">
30
- <h2 class="section-title">Get Started Now</h2>
31
- <div class="code-sample">
32
- <pre><code>
33
- // Initialize a new Slice.js project
34
- npm run slice:init
35
-
36
- // Create a new component
37
- npm run slice:create
38
-
39
- // Start your application
40
- npm run slice:start
41
- </code></pre>
29
+ <section class="showcase-section">
30
+ <div class="section-header">
31
+ <p class="section-label">Component Showcase</p>
32
+ <h2 class="section-title">Ready-to-use components</h2>
42
33
  </div>
34
+ <div class="showcase-grid"></div>
43
35
  </section>
44
36
 
45
- <footer class="home-footer">
46
- <p>Made with ❤️ using Slice.js</p>
47
- <p class="copyright">© 2025 Slice.js Framework</p>
48
- </footer>
49
- </div>
37
+ </div>
@@ -2,15 +2,20 @@ export default class HomePage extends HTMLElement {
2
2
  constructor(props) {
3
3
  super();
4
4
  slice.attachTemplate(this);
5
-
6
- this.$examplesContainer = this.querySelector('.examples-container');
7
-
8
5
  slice.controller.setComponentProps(this, props);
9
6
  this.debuggerProps = [];
10
7
  }
11
8
 
12
9
  async init() {
13
- // Crear la barra de navegación
10
+ await Promise.all([
11
+ this._buildNavbar(),
12
+ this._buildHeroCta(),
13
+ this._buildFeatures(),
14
+ this._buildShowcase(),
15
+ ]);
16
+ }
17
+
18
+ async _buildNavbar() {
14
19
  const navbar = await slice.build('Navbar', {
15
20
  position: 'fixed',
16
21
  logo: {
@@ -20,177 +25,186 @@ export default class HomePage extends HTMLElement {
20
25
  items: [
21
26
  { text: 'Home', path: '/' },
22
27
  { text: 'Playground', path: '/Playground' },
23
-
24
28
  ],
25
29
  buttons: [
26
30
  {
27
31
  value: 'Change Theme',
28
32
  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
- }
33
+ const current = slice.stylesManager.themeManager.currentTheme;
34
+ const next = current === 'Slice' ? 'Light'
35
+ : current === 'Light' ? 'Dark'
36
+ : 'Slice';
37
+ await slice.setTheme(next);
37
38
  },
38
39
  },
39
40
  ],
40
41
  });
41
-
42
- // Crear botones para la sección de llamada a la acción
43
- const docsButton = await slice.build('Button', {
42
+ this.insertBefore(navbar, this.firstChild);
43
+ }
44
+
45
+ async _buildHeroCta() {
46
+ const docsBtn = await slice.build('Button', {
44
47
  value: 'Documentation',
45
- onClickCallback: () => //redirect to https://slice-js-docs.vercel.app/Documentation
46
- window.open('https://slice-js-docs.vercel.app/Documentation', '_blank'),
48
+ onClickCallback: () =>
49
+ window.open('https://slice-js-docs.vercel.app/Documentation', '_blank'),
47
50
  customColor: {
48
51
  button: 'var(--primary-color)',
49
- label: 'var(--primary-color-contrast)'
50
- }
52
+ label: 'var(--primary-color-contrast)',
53
+ },
51
54
  });
52
-
53
- const componentsButton = await slice.build('Button', {
55
+
56
+ const componentsBtn = await slice.build('Button', {
54
57
  value: 'Components Library',
55
- onClickCallback: () => window.open('https://slice-js-docs.vercel.app/Documentation/Visual', '_blank'),
58
+ onClickCallback: () =>
59
+ window.open('https://slice-js-docs.vercel.app/Documentation/Visual', '_blank'),
56
60
  customColor: {
57
- button: 'var(--secondary-color)',
58
- label: 'var(--secondary-color-contrast)'
59
- }
61
+ button: 'var(--secondary-background-color)',
62
+ label: 'var(--primary-color)',
63
+ },
60
64
  });
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);
65
+
66
+ const cta = this.querySelector('.hero-cta');
67
+ cta.appendChild(docsBtn);
68
+ cta.appendChild(componentsBtn);
77
69
  }
78
-
79
- async createFeatures() {
80
- // Definir características
70
+
71
+ async _buildFeatures() {
81
72
  const features = [
82
73
  {
83
74
  title: 'Component-Based',
84
- description: 'Build your app using modular, reusable components following web standards.'
75
+ description: 'Build your app using modular, reusable components following web standards.',
85
76
  },
86
77
  {
87
- title: 'Zero Dependencies',
88
- description: 'Built with vanilla JavaScript. No external libraries required.'
78
+ title: 'Themeable',
79
+ description: 'Swap themes at runtime. Ships with Slice, Light, Dark and more.',
89
80
  },
90
81
  {
91
- title: 'Easy Routing',
92
- description: 'Simple and powerful routing system for single page applications.'
82
+ title: 'Lightweight',
83
+ description: 'No heavy runtime. Just vanilla JavaScript and web standards.',
93
84
  },
94
85
  {
95
- title: 'Theme System',
96
- description: 'Built-in theme support with easy customization through CSS variables.'
86
+ title: 'Built-in Router',
87
+ description: 'Client-side routing with MultiRoute no extra libraries needed.',
97
88
  },
98
89
  {
99
- title: 'Developer Tools',
100
- description: 'Integrated debugging and logging for faster development.'
90
+ title: 'CLI Tools',
91
+ description: 'Scaffold projects, create components and build bundles from the command line.',
101
92
  },
102
93
  {
103
- title: 'Performance Focused',
104
- description: 'Lightweight and optimized for fast loading and execution.'
105
- }
94
+ title: 'Services',
95
+ description: 'Built-in FetchManager, LocalStorage and IndexedDB integrations.',
96
+ },
106
97
  ];
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);
98
+
99
+ const grid = this.querySelector('.feature-grid');
100
+ for (const { title, description } of features) {
101
+ const item = document.createElement('div');
102
+ item.classList.add('feature-item');
103
+
104
+ const h3 = document.createElement('h3');
105
+ h3.classList.add('feature-title');
106
+ h3.textContent = title;
107
+
108
+ const p = document.createElement('p');
109
+ p.classList.add('feature-description');
110
+ p.textContent = description;
111
+
112
+ item.appendChild(h3);
113
+ item.appendChild(p);
114
+ grid.appendChild(item);
127
115
  }
128
116
  }
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'
117
+
118
+ async _buildShowcase() {
119
+ const grid = this.querySelector('.showcase-grid');
120
+
121
+ // Helper: wrap built components in a labeled card and append to grid
122
+ const addCard = (label, ...components) => {
123
+ const card = document.createElement('div');
124
+ card.classList.add('comp-card');
125
+
126
+ const labelEl = document.createElement('p');
127
+ labelEl.classList.add('comp-label');
128
+ labelEl.textContent = label;
129
+
130
+ const demo = document.createElement('div');
131
+ demo.classList.add('comp-demo');
132
+ components.forEach(c => demo.appendChild(c));
133
+
134
+ card.appendChild(labelEl);
135
+ card.appendChild(demo);
136
+ grid.appendChild(card);
137
+ };
138
+
139
+ // Button — primary + secondary variants
140
+ const [btnPrimary, btnSecondary] = await Promise.all([
141
+ slice.build('Button', {
142
+ value: 'Primary',
143
+ onClickCallback: () => {},
144
+ customColor: {
145
+ button: 'var(--primary-color)',
146
+ label: 'var(--primary-color-contrast)',
147
+ },
148
+ }),
149
+ slice.build('Button', {
150
+ value: 'Secondary',
151
+ onClickCallback: () => {},
152
+ customColor: {
153
+ button: 'var(--secondary-background-color)',
154
+ label: 'var(--primary-color)',
155
+ },
156
+ }),
157
+ ]);
158
+ addCard('Button', btnPrimary, btnSecondary);
159
+
160
+ // Input
161
+ const input = await slice.build('Input', {
162
+ placeholder: 'Type something...',
163
+ type: 'text',
135
164
  });
136
-
137
- const switchExample = await slice.build('Switch', {
165
+ addCard('Input', input);
166
+
167
+ // Switch
168
+ const sw = await slice.build('Switch', {
138
169
  label: 'Toggle me',
139
- checked: true
140
- });
141
-
142
- const checkboxExample = await slice.build('Checkbox', {
143
- label: 'Check me',
144
- labelPlacement: 'right'
170
+ checked: true,
145
171
  });
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 }
172
+ addCard('Switch', sw);
173
+
174
+ // Select theme chooser
175
+ const themeOptions = [
176
+ { name: 'Slice Theme' },
177
+ { name: 'Light Theme' },
178
+ { name: 'Dark Theme' },
179
+ { name: 'Purple Theme' },
158
180
  ];
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'
181
+ const select = await slice.build('Select', {
182
+ label: 'Pick a theme',
183
+ options: themeOptions,
184
+ visibleProp: 'name',
185
+ onOptionSelect: async (option) => {
186
+ if (!option) return;
187
+ const themeName = option.name.replace(' Theme', '');
188
+ await slice.setTheme(themeName);
189
+ },
190
+ });
191
+ addCard('Select', select);
192
+
193
+ // Loading — triggered by a demo button (Loading appends to document.body)
194
+ const loading = await slice.build('Loading', {});
195
+ const demoBtn = await slice.build('Button', {
196
+ value: 'Demo Loading',
197
+ onClickCallback: () => {
198
+ loading.start();
199
+ setTimeout(() => loading.stop(), 1500);
200
+ },
201
+ customColor: {
202
+ button: 'var(--primary-color)',
203
+ label: 'var(--primary-color-contrast)',
204
+ },
188
205
  });
189
-
190
- const codeSample = this.querySelector('.code-sample');
191
- codeSample.innerHTML = ''; // Clear the static code sample
192
- codeSample.appendChild(codeVisualizer);
206
+ addCard('Loading', demoBtn);
193
207
  }
194
208
  }
195
209
 
196
- customElements.define('slice-home-page', HomePage);
210
+ customElements.define('slice-home-page', HomePage);