responsive-system 1.2.7 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "responsive-system",
3
- "version": "1.2.7",
3
+ "version": "1.3.0",
4
4
  "description": "Sistema de layout responsivo con auto-scaling para Tailwind CSS",
5
5
  "type": "module",
6
6
  "main": "./dist/responsive-system.cjs",
@@ -4,14 +4,18 @@
4
4
  * Script postinstall para automatizar la configuración inicial completa
5
5
  * - Instala React, TypeScript, Tailwind automáticamente
6
6
  * - Inicializa proyecto Vite si está vacío
7
- * - Configura Tailwind
8
- * - Crea página de ejemplo con layouts
7
+ * - Pregunta qué layout quiere (interactivo)
8
+ * - Copia solo los componentes necesarios
9
+ * - Copia el hook useResponsive como archivo local
10
+ * - Crea página de ejemplo en pages/
11
+ * - Configura App.tsx con el layout seleccionado
9
12
  */
10
13
 
11
14
  import fs from 'fs'
12
15
  import path from 'path'
13
16
  import { execSync } from 'child_process'
14
17
  import { fileURLToPath } from 'url'
18
+ import readline from 'readline'
15
19
 
16
20
  const __filename = fileURLToPath(import.meta.url)
17
21
  const __dirname = path.dirname(__filename)
@@ -19,8 +23,12 @@ const __dirname = path.dirname(__filename)
19
23
  const projectRoot = process.cwd()
20
24
  const packageJsonPath = path.join(projectRoot, 'package.json')
21
25
 
26
+ // Detectar si se ejecuta como postinstall o manualmente
27
+ const isPostinstall = process.env.npm_lifecycle_event === 'postinstall'
28
+ const isManual = process.argv[1].includes('postinstall.js') && !isPostinstall
29
+
22
30
  console.log('')
23
- console.log('📦 responsive-system: Iniciando postinstall...')
31
+ console.log('📦 responsive-system: Iniciando configuración...')
24
32
  console.log(` Directorio: ${projectRoot}`)
25
33
  console.log('')
26
34
 
@@ -43,24 +51,185 @@ const isProjectEmpty = !packageJson.dependencies ||
43
51
  Object.keys(packageJson.dependencies).length === 0 ||
44
52
  (Object.keys(packageJson.dependencies).length === 1 && packageJson.dependencies['responsive-system'])
45
53
 
46
- // Verificar qué está instalado - primero en package.json, luego en node_modules
54
+ // Verificar qué está instalado - SOLO en package.json (no en node_modules para evitar conflictos)
47
55
  const hasReactInPackageJson = (packageJson.dependencies && packageJson.dependencies.react) ||
48
56
  (packageJson.devDependencies && packageJson.devDependencies.react)
49
- const hasReactInNodeModules = fs.existsSync(path.join(projectRoot, 'node_modules', 'react'))
50
- const hasReact = hasReactInPackageJson || hasReactInNodeModules
51
-
52
57
  const hasVite = packageJson.devDependencies && packageJson.devDependencies.vite
53
-
54
58
  const tailwindInDevDeps = packageJson.devDependencies && packageJson.devDependencies.tailwindcss
55
59
  const typescriptInDevDeps = packageJson.devDependencies && packageJson.devDependencies.typescript
56
60
 
57
61
  let needsUpdate = false
58
62
 
63
+ // Función para preguntar al usuario qué layout quiere
64
+ async function askLayout() {
65
+ if (isPostinstall && !isManual) {
66
+ // Si es postinstall automático, usar 'default' por defecto
67
+ console.log(' ℹ️ Usando layout "default" por defecto')
68
+ console.log(' 💡 Ejecuta "npx responsive-system-setup" para cambiar el layout')
69
+ return 'default'
70
+ }
71
+
72
+ // Si es ejecución manual, preguntar interactivamente
73
+ const rl = readline.createInterface({
74
+ input: process.stdin,
75
+ output: process.stdout
76
+ })
77
+
78
+ return new Promise((resolve) => {
79
+ console.log('')
80
+ console.log('🎨 Selecciona el layout que quieres usar:')
81
+ console.log(' 1. default - Navigation + Footer')
82
+ console.log(' 2. sidebar - Sidebar lateral')
83
+ console.log(' 3. dashboard - Sidebar + Footer')
84
+ console.log(' 4. minimal - Sin componentes (solo contenido)')
85
+ console.log('')
86
+
87
+ rl.question(' Ingresa el número (1-4) o el nombre del layout: ', (answer) => {
88
+ rl.close()
89
+
90
+ const normalized = answer.trim().toLowerCase()
91
+
92
+ if (normalized === '1' || normalized === 'default') {
93
+ resolve('default')
94
+ } else if (normalized === '2' || normalized === 'sidebar') {
95
+ resolve('sidebar')
96
+ } else if (normalized === '3' || normalized === 'dashboard') {
97
+ resolve('dashboard')
98
+ } else if (normalized === '4' || normalized === 'minimal') {
99
+ resolve('minimal')
100
+ } else {
101
+ console.log(' ⚠️ Opción inválida, usando "default"')
102
+ resolve('default')
103
+ }
104
+ })
105
+ })
106
+ }
107
+
108
+ // Función para copiar archivo desde el paquete al proyecto
109
+ function copyFileFromPackage(relativePath, targetPath, isComponent = false) {
110
+ const sourcePath = path.join(__dirname, '..', relativePath)
111
+ const destPath = path.join(projectRoot, targetPath)
112
+
113
+ if (!fs.existsSync(sourcePath)) {
114
+ console.error(` ❌ No se encontró: ${relativePath}`)
115
+ return false
116
+ }
117
+
118
+ // Crear directorio destino si no existe
119
+ const destDir = path.dirname(destPath)
120
+ if (!fs.existsSync(destDir)) {
121
+ fs.mkdirSync(destDir, { recursive: true })
122
+ }
123
+
124
+ let content = fs.readFileSync(sourcePath, 'utf8')
125
+
126
+ // Si es un componente, reemplazar importaciones relativas por importaciones del paquete
127
+ if (isComponent) {
128
+ // Reemplazar importaciones de hooks
129
+ content = content.replace(
130
+ /from ['"]\.\.\/\.\.\/hooks['"]/g,
131
+ "from 'responsive-system'"
132
+ )
133
+ // Reemplazar importaciones de context
134
+ content = content.replace(
135
+ /from ['"]\.\.\/\.\.\/context['"]/g,
136
+ "from 'responsive-system'"
137
+ )
138
+ // Reemplazar importaciones de componentes/layout
139
+ content = content.replace(
140
+ /from ['"]\.\.\/components\/layout['"]/g,
141
+ "from 'responsive-system'"
142
+ )
143
+ }
144
+
145
+ fs.writeFileSync(destPath, content)
146
+ return true
147
+ }
148
+
149
+ // Función para copiar componentes según el layout seleccionado
150
+ function copyLayoutComponents(selectedLayout) {
151
+ const componentsDir = path.join(projectRoot, 'src', 'components', 'layout')
152
+
153
+ // Crear directorio si no existe
154
+ if (!fs.existsSync(componentsDir)) {
155
+ fs.mkdirSync(componentsDir, { recursive: true })
156
+ }
157
+
158
+ const componentsToCopy = {
159
+ default: ['Navigation', 'Footer'],
160
+ sidebar: ['Sidebar'],
161
+ dashboard: ['Sidebar', 'Footer'],
162
+ minimal: []
163
+ }
164
+
165
+ const components = componentsToCopy[selectedLayout] || []
166
+
167
+ if (components.length === 0) {
168
+ console.log(' ✅ Layout minimal: No se copian componentes')
169
+ return
170
+ }
171
+
172
+ console.log(` 📦 Copiando componentes para layout "${selectedLayout}":`)
173
+
174
+ for (const component of components) {
175
+ const sourceFile = `src/components/layout/${component}.tsx`
176
+ const targetFile = `src/components/layout/${component}.tsx`
177
+
178
+ if (copyFileFromPackage(sourceFile, targetFile, true)) {
179
+ console.log(` ✅ ${component}.tsx`)
180
+ }
181
+ }
182
+
183
+ // Crear index.ts para exportar los componentes
184
+ const indexContent = components.map(c => `export { default as ${c} } from './${c}'`).join('\n')
185
+ fs.writeFileSync(path.join(componentsDir, 'index.ts'), indexContent)
186
+ console.log(' ✅ index.ts')
187
+ }
188
+
189
+ // Función para copiar el hook useResponsive y sus dependencias
190
+ function copyUseResponsiveHook() {
191
+ console.log(' 📦 Copiando hook useResponsive y dependencias...')
192
+
193
+ // Crear directorio hooks
194
+ const hooksDir = path.join(projectRoot, 'src', 'hooks')
195
+ if (!fs.existsSync(hooksDir)) {
196
+ fs.mkdirSync(hooksDir, { recursive: true })
197
+ }
198
+
199
+ // Copiar tipos
200
+ const typesDir = path.join(projectRoot, 'src', 'types')
201
+ if (!fs.existsSync(typesDir)) {
202
+ fs.mkdirSync(typesDir, { recursive: true })
203
+ }
204
+ copyFileFromPackage('src/types/responsive.ts', 'src/types/responsive.ts')
205
+ console.log(' ✅ types/responsive.ts')
206
+
207
+ // Copiar constantes
208
+ const constantsDir = path.join(projectRoot, 'src', 'constants')
209
+ if (!fs.existsSync(constantsDir)) {
210
+ fs.mkdirSync(constantsDir, { recursive: true })
211
+ }
212
+ copyFileFromPackage('src/constants/breakpoints.ts', 'src/constants/breakpoints.ts')
213
+ console.log(' ✅ constants/breakpoints.ts')
214
+
215
+ // Copiar hook useResponsive
216
+ copyFileFromPackage('src/hooks/useResponsive.ts', 'src/hooks/useResponsive.ts')
217
+ console.log(' ✅ hooks/useResponsive.ts')
218
+
219
+ // Crear index.ts para exportar el hook
220
+ const indexContent = `export { useResponsive } from './useResponsive'
221
+ export type { ResponsiveState, Breakpoint, Orientation } from '../types/responsive'
222
+ export { DEFAULT_BREAKPOINTS, getCurrentBreakpoint, getBreakpointIndex, getBreakpointValue } from '../constants/breakpoints'
223
+ `
224
+ fs.writeFileSync(path.join(hooksDir, 'index.ts'), indexContent)
225
+ console.log(' ✅ hooks/index.ts')
226
+ }
227
+
59
228
  // ESTRATEGIA: Modificar directamente el package.json primero, luego instalar
60
229
  console.log('📦 responsive-system: Verificando dependencias...')
61
230
 
62
- // Agregar React a dependencies SOLO si no está en package.json ni en node_modules
63
- if (!hasReact) {
231
+ // Agregar React a dependencies SOLO si NO está en package.json
232
+ if (!hasReactInPackageJson) {
64
233
  console.log(' ➕ Agregando React a dependencies...')
65
234
  if (!packageJson.dependencies) {
66
235
  packageJson.dependencies = {}
@@ -68,31 +237,8 @@ if (!hasReact) {
68
237
  packageJson.dependencies['react'] = '^19.1.1'
69
238
  packageJson.dependencies['react-dom'] = '^19.1.1'
70
239
  needsUpdate = true
71
- } else if (hasReactInNodeModules && !hasReactInPackageJson) {
72
- // Si React está en node_modules pero no en package.json, agregarlo al package.json
73
- console.log(' ➕ React está instalado pero no en package.json, agregándolo...')
74
- if (!packageJson.dependencies) {
75
- packageJson.dependencies = {}
76
- }
77
- // Leer la versión instalada de node_modules
78
- try {
79
- const reactPackageJson = JSON.parse(fs.readFileSync(
80
- path.join(projectRoot, 'node_modules', 'react', 'package.json'),
81
- 'utf8'
82
- ))
83
- packageJson.dependencies['react'] = `^${reactPackageJson.version}`
84
-
85
- const reactDomPackageJson = JSON.parse(fs.readFileSync(
86
- path.join(projectRoot, 'node_modules', 'react-dom', 'package.json'),
87
- 'utf8'
88
- ))
89
- packageJson.dependencies['react-dom'] = `^${reactDomPackageJson.version}`
90
- } catch {
91
- // Si no se puede leer, usar versión por defecto
92
- packageJson.dependencies['react'] = '^19.1.1'
93
- packageJson.dependencies['react-dom'] = '^19.1.1'
94
- }
95
- needsUpdate = true
240
+ } else {
241
+ console.log(' ✅ React ya está en package.json, no se modificará')
96
242
  }
97
243
 
98
244
  // Agregar Vite si el proyecto está vacío
@@ -106,9 +252,9 @@ if (isProjectEmpty && !hasVite) {
106
252
  needsUpdate = true
107
253
  }
108
254
 
109
- // Agregar TypeScript y Tailwind a devDependencies
255
+ // Agregar Tailwind y sus dependencias a devDependencies
110
256
  if (!tailwindInDevDeps) {
111
- console.log(' ➕ Agregando Tailwind a devDependencies...')
257
+ console.log(' ➕ Agregando Tailwind y PostCSS a devDependencies...')
112
258
  if (!packageJson.devDependencies) {
113
259
  packageJson.devDependencies = {}
114
260
  }
@@ -119,6 +265,7 @@ if (!tailwindInDevDeps) {
119
265
  needsUpdate = true
120
266
  }
121
267
 
268
+ // Agregar TypeScript y sus tipos a devDependencies
122
269
  if (!typescriptInDevDeps) {
123
270
  console.log(' ➕ Agregando TypeScript a devDependencies...')
124
271
  if (!packageJson.devDependencies) {
@@ -165,16 +312,28 @@ if (isProjectEmpty) {
165
312
  console.log('')
166
313
  console.log('📦 responsive-system: Proyecto vacío detectado, creando estructura base...')
167
314
 
315
+ // Preguntar qué layout quiere
316
+ const selectedLayout = await askLayout()
317
+ console.log(` ✅ Layout seleccionado: "${selectedLayout}"`)
318
+ console.log('')
319
+
168
320
  // Crear estructura de directorios
169
- const dirs = ['src', 'src/components', 'src/pages', 'public']
321
+ const dirs = ['src', 'src/components', 'src/components/layout', 'src/pages', 'src/hooks', 'src/types', 'src/constants', 'public']
170
322
  dirs.forEach(dir => {
171
323
  const dirPath = path.join(projectRoot, dir)
172
324
  if (!fs.existsSync(dirPath)) {
173
325
  fs.mkdirSync(dirPath, { recursive: true })
174
- console.log(` ✅ Creado: ${dir}/`)
175
326
  }
176
327
  })
177
328
 
329
+ // Copiar componentes según layout seleccionado
330
+ copyLayoutComponents(selectedLayout)
331
+ console.log('')
332
+
333
+ // Copiar hook useResponsive
334
+ copyUseResponsiveHook()
335
+ console.log('')
336
+
178
337
  // Crear vite.config.ts
179
338
  const viteConfigPath = path.join(projectRoot, 'vite.config.ts')
180
339
  if (!fs.existsSync(viteConfigPath)) {
@@ -324,7 +483,7 @@ import './index.css'
324
483
 
325
484
  ReactDOM.createRoot(document.getElementById('root')!).render(
326
485
  <React.StrictMode>
327
- <ResponsiveLayoutProvider defaultLayout="default">
486
+ <ResponsiveLayoutProvider defaultLayout="${selectedLayout}">
328
487
  <MainLayout>
329
488
  <App />
330
489
  </MainLayout>
@@ -339,20 +498,21 @@ ReactDOM.createRoot(document.getElementById('root')!).render(
339
498
  // Crear src/index.css
340
499
  const indexCssPath = path.join(projectRoot, 'src', 'index.css')
341
500
  if (!fs.existsSync(indexCssPath)) {
342
- // Tailwind CSS v4 usa @import en lugar de @tailwind
343
501
  const indexCss = `@import "tailwindcss";
344
502
  `
345
503
  fs.writeFileSync(indexCssPath, indexCss)
346
504
  console.log(' ✅ Creado: src/index.css')
347
505
  }
348
506
 
349
- // Crear src/App.tsx con página de ejemplo
350
- const appTsxPath = path.join(projectRoot, 'src', 'App.tsx')
351
- if (!fs.existsSync(appTsxPath)) {
352
- const appTsx = `import { useResponsiveLayout, LayoutSwitcher } from 'responsive-system'
507
+ // Crear src/pages/HomePage.tsx con página de ejemplo
508
+ const homePagePath = path.join(projectRoot, 'src', 'pages', 'HomePage.tsx')
509
+ if (!fs.existsSync(homePagePath)) {
510
+ const homePage = `import { useResponsiveLayout, LayoutSwitcher } from 'responsive-system'
511
+ import { useResponsive } from '../hooks'
353
512
 
354
- function App() {
513
+ function HomePage() {
355
514
  const { breakpoint, isMobile, layout } = useResponsiveLayout()
515
+ const responsive = useResponsive()
356
516
 
357
517
  return (
358
518
  <div className="min-h-screen bg-gray-50 p-6">
@@ -395,6 +555,32 @@ function App() {
395
555
  ))}
396
556
  </div>
397
557
 
558
+ {/* Información sobre el hook useResponsive */}
559
+ <div className="bg-white rounded-lg shadow-lg p-6">
560
+ <h2 className="text-2xl font-bold mb-4">Hook useResponsive</h2>
561
+ <p className="text-gray-700 mb-4">
562
+ El hook <code className="bg-gray-100 px-2 py-1 rounded">useResponsive</code> está disponible localmente en <code className="bg-gray-100 px-2 py-1 rounded">src/hooks/useResponsive.ts</code>
563
+ </p>
564
+ <div className="grid grid-cols-2 md:grid-cols-4 gap-4">
565
+ <div className="p-3 bg-gray-50 rounded">
566
+ <p className="text-xs text-gray-600">Ancho</p>
567
+ <p className="text-lg font-bold">{responsive.width}px</p>
568
+ </div>
569
+ <div className="p-3 bg-gray-50 rounded">
570
+ <p className="text-xs text-gray-600">Alto</p>
571
+ <p className="text-lg font-bold">{responsive.height}px</p>
572
+ </div>
573
+ <div className="p-3 bg-gray-50 rounded">
574
+ <p className="text-xs text-gray-600">Orientación</p>
575
+ <p className="text-lg font-bold capitalize">{responsive.orientation}</p>
576
+ </div>
577
+ <div className="p-3 bg-gray-50 rounded">
578
+ <p className="text-xs text-gray-600">Desktop</p>
579
+ <p className="text-lg font-bold">{responsive.isDesktop ? 'Sí' : 'No'}</p>
580
+ </div>
581
+ </div>
582
+ </div>
583
+
398
584
  {/* Información */}
399
585
  <div className="bg-white rounded-lg shadow-lg p-6">
400
586
  <h2 className="text-2xl font-bold mb-4">Cómo funciona</h2>
@@ -402,7 +588,7 @@ function App() {
402
588
  <li>✅ <strong>Auto-scaling:</strong> Todo escala automáticamente (texto, espaciado, sombras)</li>
403
589
  <li>✅ <strong>Layouts:</strong> Cambia entre default, sidebar, dashboard y minimal</li>
404
590
  <li>✅ <strong>Responsive:</strong> Se adapta automáticamente a todos los tamaños de pantalla</li>
405
- <li>✅ <strong>Sin media queries:</strong> Usa el hook useResponsive para lógica condicional</li>
591
+ <li>✅ <strong>Hook useResponsive:</strong> Disponible localmente para configuración manual</li>
406
592
  </ul>
407
593
  </div>
408
594
  </div>
@@ -410,10 +596,25 @@ function App() {
410
596
  )
411
597
  }
412
598
 
599
+ export default HomePage
600
+ `
601
+ fs.writeFileSync(homePagePath, homePage)
602
+ console.log(' ✅ Creado: src/pages/HomePage.tsx')
603
+ }
604
+
605
+ // Crear src/App.tsx que importa la página
606
+ const appTsxPath = path.join(projectRoot, 'src', 'App.tsx')
607
+ if (!fs.existsSync(appTsxPath)) {
608
+ const appTsx = `import HomePage from './pages/HomePage'
609
+
610
+ function App() {
611
+ return <HomePage />
612
+ }
613
+
413
614
  export default App
414
615
  `
415
616
  fs.writeFileSync(appTsxPath, appTsx)
416
- console.log(' ✅ Creado: src/App.tsx con página de ejemplo')
617
+ console.log(' ✅ Creado: src/App.tsx')
417
618
  }
418
619
 
419
620
  // Actualizar package.json con scripts
@@ -440,6 +641,15 @@ export default App
440
641
  console.log(' 1. npm run dev')
441
642
  console.log(' 2. Abre http://localhost:5173')
442
643
  console.log('')
644
+ console.log(`Layout seleccionado: "${selectedLayout}"`)
645
+ console.log(' - Componentes copiados en src/components/layout/')
646
+ console.log(' - Hook useResponsive disponible en src/hooks/useResponsive.ts')
647
+ console.log(' - Página de ejemplo en src/pages/HomePage.tsx')
648
+ console.log('')
649
+ console.log('💡 Para cambiar el layout, ejecuta: npx responsive-system-setup')
650
+ console.log('')
651
+ } else {
652
+ console.log('✅ responsive-system: Proyecto ya inicializado')
443
653
  }
444
654
 
445
655
  console.log('')