responsive-system 1.1.4 → 1.2.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.
Files changed (36) hide show
  1. package/package.json +5 -1
  2. package/scripts/postinstall.js +8 -1
  3. package/src/App.css +42 -0
  4. package/src/App.tsx +29 -0
  5. package/src/assets/react.svg +1 -0
  6. package/src/components/LayoutSwitcher.tsx +62 -0
  7. package/src/components/ResponsiveDemo.tsx +282 -0
  8. package/src/components/layout/Footer.tsx +90 -0
  9. package/src/components/layout/Header.tsx +105 -0
  10. package/src/components/layout/Navigation.tsx +96 -0
  11. package/src/components/layout/Sidebar.tsx +108 -0
  12. package/src/components/layout/index.ts +4 -0
  13. package/src/config/layout.ts +61 -0
  14. package/src/constants/breakpoints.ts +48 -0
  15. package/src/context/NavigationContext.tsx +32 -0
  16. package/src/context/ResponsiveLayoutContext.tsx +37 -0
  17. package/src/context/SidebarContext.tsx +26 -0
  18. package/src/context/index.ts +4 -0
  19. package/src/hooks/index.ts +4 -0
  20. package/src/hooks/useLayout.ts +27 -0
  21. package/src/hooks/useResponsive.ts +189 -0
  22. package/src/hooks/useResponsiveLayout.ts +51 -0
  23. package/src/index.css +1 -0
  24. package/src/index.ts +100 -0
  25. package/src/layouts/DashboardLayout.tsx +76 -0
  26. package/src/layouts/DefaultLayout.tsx +30 -0
  27. package/src/layouts/MainLayout.tsx +38 -0
  28. package/src/layouts/MinimalLayout.tsx +20 -0
  29. package/src/layouts/SidebarLayout.tsx +36 -0
  30. package/src/layouts/index.ts +5 -0
  31. package/src/main.tsx +10 -0
  32. package/src/pages/ResponsiveTestPage.tsx +400 -0
  33. package/src/providers/ResponsiveLayoutProvider.tsx +92 -0
  34. package/src/providers/ResponsiveProvider.tsx +18 -0
  35. package/src/providers/index.ts +3 -0
  36. package/src/types/responsive.ts +64 -0
@@ -0,0 +1,189 @@
1
+ import { useState, useEffect, useCallback, useMemo } from 'react'
2
+ import type {
3
+ ResponsiveState,
4
+ Breakpoint
5
+ } from '../types/responsive'
6
+ import {
7
+ getCurrentBreakpoint,
8
+ getBreakpointIndex
9
+ } from '../constants/breakpoints'
10
+
11
+ /**
12
+ * Debounce utility
13
+ */
14
+ function debounce<T extends (...args: unknown[]) => void>(
15
+ func: T,
16
+ wait: number
17
+ ): (...args: Parameters<T>) => void {
18
+ let timeout: ReturnType<typeof setTimeout> | null = null
19
+ return (...args: Parameters<T>) => {
20
+ if (timeout) clearTimeout(timeout)
21
+ timeout = setTimeout(() => func(...args), wait)
22
+ }
23
+ }
24
+
25
+ /**
26
+ * Get orientation based on dimensions
27
+ */
28
+ function getOrientation(width: number, height: number): 'landscape' | 'portrait' {
29
+ return width >= height ? 'landscape' : 'portrait'
30
+ }
31
+
32
+ /**
33
+ * Hook principal useResponsive
34
+ * Provee información sobre el breakpoint actual y helpers para responsive
35
+ */
36
+ export const useResponsive = (): ResponsiveState => {
37
+ const [dimensions, setDimensions] = useState({
38
+ width: typeof window !== 'undefined' ? window.innerWidth : 1024,
39
+ height: typeof window !== 'undefined' ? window.innerHeight : 768
40
+ })
41
+
42
+ // Función para actualizar dimensiones
43
+ const updateDimensions = useCallback(() => {
44
+ setDimensions({
45
+ width: window.innerWidth,
46
+ height: window.innerHeight
47
+ })
48
+ }, [])
49
+
50
+ // Debounced update para optimizar performance
51
+ const debouncedUpdateDimensions = useMemo(
52
+ () => debounce(updateDimensions, 100),
53
+ [updateDimensions]
54
+ )
55
+
56
+ // Effect para escuchar cambios de tamaño
57
+ useEffect(() => {
58
+ if (typeof window === 'undefined') return
59
+
60
+ window.addEventListener('resize', debouncedUpdateDimensions)
61
+
62
+ return () => {
63
+ window.removeEventListener('resize', debouncedUpdateDimensions)
64
+ }
65
+ }, [debouncedUpdateDimensions])
66
+
67
+ const { width, height } = dimensions
68
+
69
+ // Calcular breakpoint actual
70
+ const breakpoint = useMemo(() => getCurrentBreakpoint(width), [width])
71
+
72
+ // Calcular orientación
73
+ const orientation = useMemo(() => getOrientation(width, height), [width, height])
74
+
75
+ // Helpers booleanos por breakpoint específico
76
+ const isXs = breakpoint === 'xs'
77
+ const isSm = breakpoint === 'sm'
78
+ const isMd = breakpoint === 'md'
79
+ const isLg = breakpoint === 'lg'
80
+ const isXl = breakpoint === 'xl'
81
+ const is2Xl = breakpoint === '2xl'
82
+ const is3Xl = breakpoint === '3xl'
83
+ const is4Xl = breakpoint === '4xl'
84
+ const is5Xl = breakpoint === '5xl'
85
+
86
+ // Helpers booleanos agrupados
87
+ const isMobile = isXs || isSm
88
+ const isTablet = isMd
89
+ const isDesktop = isLg || isXl || is2Xl || is3Xl || is4Xl || is5Xl
90
+ const isSmall = isXs || isSm || isMd
91
+ const isLarge = isLg || isXl || is2Xl || is3Xl || is4Xl || is5Xl
92
+ const isUltraWide = is3Xl || is4Xl || is5Xl
93
+ const is4K = is4Xl || is5Xl
94
+ const is5K = is5Xl
95
+
96
+ // Helpers de orientación
97
+ const isPortrait = orientation === 'portrait'
98
+ const isLandscape = orientation === 'landscape'
99
+
100
+ // Funciones de comparación de breakpoints
101
+ const isBreakpointUp = useCallback((bp: Breakpoint): boolean => {
102
+ return getBreakpointIndex(breakpoint) >= getBreakpointIndex(bp)
103
+ }, [breakpoint])
104
+
105
+ const isBreakpointDown = useCallback((bp: Breakpoint): boolean => {
106
+ return getBreakpointIndex(breakpoint) <= getBreakpointIndex(bp)
107
+ }, [breakpoint])
108
+
109
+ const isBreakpointBetween = useCallback((min: Breakpoint, max: Breakpoint): boolean => {
110
+ const current = getBreakpointIndex(breakpoint)
111
+ return current >= getBreakpointIndex(min) && current <= getBreakpointIndex(max)
112
+ }, [breakpoint])
113
+
114
+ // Funciones de comparación de ancho
115
+ const isWidthUp = useCallback((minWidth: number): boolean => {
116
+ return width >= minWidth
117
+ }, [width])
118
+
119
+ const isWidthDown = useCallback((maxWidth: number): boolean => {
120
+ return width <= maxWidth
121
+ }, [width])
122
+
123
+ const isWidthBetween = useCallback((minWidth: number, maxWidth: number): boolean => {
124
+ return width >= minWidth && width <= maxWidth
125
+ }, [width])
126
+
127
+ // Funciones de comparación de altura
128
+ const isHeightUp = useCallback((minHeight: number): boolean => {
129
+ return height >= minHeight
130
+ }, [height])
131
+
132
+ const isHeightDown = useCallback((maxHeight: number): boolean => {
133
+ return height <= maxHeight
134
+ }, [height])
135
+
136
+ const isHeightBetween = useCallback((minHeight: number, maxHeight: number): boolean => {
137
+ return height >= minHeight && height <= maxHeight
138
+ }, [height])
139
+
140
+ // Debug mode
141
+ const debug = false
142
+
143
+ return {
144
+ // Estado básico
145
+ breakpoint,
146
+ width,
147
+ height,
148
+ orientation,
149
+
150
+ // Helpers booleanos específicos
151
+ isXs,
152
+ isSm,
153
+ isMd,
154
+ isLg,
155
+ isXl,
156
+ is2Xl,
157
+ is3Xl,
158
+ is4Xl,
159
+ is5Xl,
160
+
161
+ // Helpers booleanos agrupados
162
+ isMobile,
163
+ isTablet,
164
+ isDesktop,
165
+ isSmall,
166
+ isLarge,
167
+ isUltraWide,
168
+ is4K,
169
+ is5K,
170
+
171
+ // Helpers de orientación
172
+ isPortrait,
173
+ isLandscape,
174
+
175
+ // Funciones de comparación
176
+ isBreakpointUp,
177
+ isBreakpointDown,
178
+ isBreakpointBetween,
179
+ isWidthUp,
180
+ isWidthDown,
181
+ isWidthBetween,
182
+ isHeightUp,
183
+ isHeightDown,
184
+ isHeightBetween,
185
+
186
+ // Debug
187
+ debug
188
+ }
189
+ }
@@ -0,0 +1,51 @@
1
+ import { useResponsiveLayoutContext } from '../context'
2
+
3
+ export const useResponsiveLayout = () => {
4
+ const context = useResponsiveLayoutContext()
5
+
6
+ return {
7
+ // Todo el sistema responsivo original
8
+ ...context.responsive,
9
+
10
+ // Sistema de layout
11
+ layout: {
12
+ current: context.layout.current,
13
+ config: context.layout.config,
14
+ setLayout: context.layout.setLayout,
15
+ },
16
+
17
+ // Utilidades de layout
18
+ layoutUtils: context.layoutUtils,
19
+
20
+ // Helpers específicos del layout
21
+ isDefaultLayout: () => context.layout.current === 'default',
22
+ isSidebarLayout: () => context.layout.current === 'sidebar',
23
+ isDashboardLayout: () => context.layout.current === 'dashboard',
24
+ isMinimalLayout: () => context.layout.current === 'minimal',
25
+
26
+ // Grid helpers que usan el sistema auto-escalable
27
+ grid: {
28
+ auto: (minWidth = 'md') => `grid-cols-auto-${minWidth}`,
29
+ responsive: (breakpoints: Record<string, number>) => {
30
+ const classes: string[] = []
31
+ Object.entries(breakpoints).forEach(([breakpoint, cols]) => {
32
+ if (breakpoint === 'base') {
33
+ classes.push(`grid-cols-${cols}`)
34
+ } else {
35
+ classes.push(`${breakpoint}:grid-cols-${cols}`)
36
+ }
37
+ })
38
+ return classes.join(' ')
39
+ },
40
+ fixed: (cols: number) => `grid-cols-${cols}`,
41
+ },
42
+
43
+ // Spacing helpers que escalan automáticamente
44
+ spacing: {
45
+ container: context.layoutUtils.getContainerClass(),
46
+ section: 'mb-6',
47
+ card: 'p-6',
48
+ gap: 'gap-4',
49
+ },
50
+ }
51
+ }
package/src/index.css ADDED
@@ -0,0 +1 @@
1
+ @import "tailwindcss";
package/src/index.ts ADDED
@@ -0,0 +1,100 @@
1
+ // ========================================
2
+ // SISTEMA RESPONSIVE AUTO-SCALING + LAYOUTS
3
+ // ========================================
4
+
5
+ /**
6
+ * CÓMO USAR ESTE SISTEMA:
7
+ *
8
+ * 1. Instala el plugin en tailwind.config.js:
9
+ * import responsiveScalePlugin from './src/plugin/responsiveScalePlugin.js'
10
+ * plugins: [responsiveScalePlugin()]
11
+ *
12
+ * 2. Usa el ResponsiveLayoutProvider + MainLayout:
13
+ * <ResponsiveLayoutProvider defaultLayout="default">
14
+ * <MainLayout>
15
+ * <App />
16
+ * </MainLayout>
17
+ * </ResponsiveLayoutProvider>
18
+ *
19
+ * 3. Selección de layout (múltiples opciones):
20
+ * - Por prop: <MainLayout layout="dashboard">...</MainLayout>
21
+ * - Por contexto: const { setLayout } = useResponsiveLayout(); setLayout('sidebar')
22
+ * - Por defaultLayout: <ResponsiveLayoutProvider defaultLayout="dashboard">
23
+ *
24
+ * 4. Hook responsivo personalizado (opcional):
25
+ * <ResponsiveLayoutProvider useResponsiveHook={tuHookPersonalizado}>
26
+ * ...
27
+ * </ResponsiveLayoutProvider>
28
+ *
29
+ * 5. Usa Tailwind NORMAL en tus páginas:
30
+ * <div className="p-6 text-base">
31
+ * TODO escala automáticamente + layout consistente
32
+ * </div>
33
+ *
34
+ * 6. (Opcional) Usa hooks para casos avanzados:
35
+ * const { layout, responsive } = useResponsiveLayout()
36
+ */
37
+
38
+ // ========================================
39
+ // EXPORTS PRINCIPALES
40
+ // ========================================
41
+
42
+ // Providers
43
+ export { ResponsiveLayoutProvider, ResponsiveProvider } from './providers'
44
+
45
+ // Layouts
46
+ export {
47
+ MainLayout,
48
+ DefaultLayout,
49
+ SidebarLayout,
50
+ DashboardLayout,
51
+ MinimalLayout
52
+ } from './layouts'
53
+
54
+ // Hooks
55
+ export { useResponsiveLayout, useLayout, useResponsive } from './hooks'
56
+
57
+ // Componentes de layout
58
+ export { Header, Sidebar, Footer, Navigation } from './components/layout'
59
+
60
+ // LayoutSwitcher
61
+ export { default as LayoutSwitcher } from './components/LayoutSwitcher'
62
+
63
+ // Context (para casos avanzados)
64
+ export {
65
+ useResponsiveLayoutContext,
66
+ SidebarProvider,
67
+ useSidebar,
68
+ NavigationProvider,
69
+ useNavigation
70
+ } from './context'
71
+
72
+ // Tipos TypeScript
73
+ export type {
74
+ Breakpoint,
75
+ Orientation,
76
+ ResponsiveState,
77
+ ResponsiveProviderProps
78
+ } from './types/responsive'
79
+
80
+ // Constantes (para casos avanzados)
81
+ export {
82
+ DEFAULT_BREAKPOINTS,
83
+ getCurrentBreakpoint,
84
+ getBreakpointIndex,
85
+ getBreakpointValue
86
+ } from './constants/breakpoints'
87
+
88
+ // Configuración de layouts
89
+ export { LAYOUT_CONFIG, DEFAULT_LAYOUT, AVAILABLE_LAYOUTS } from './config/layout'
90
+ export type { LayoutConfig } from './config/layout'
91
+
92
+ // Plugin de Tailwind (importar directamente en tailwind.config.js)
93
+ // import responsiveScalePlugin from './src/plugin/responsiveScalePlugin.js'
94
+
95
+ // ========================================
96
+ // EJEMPLOS (Solo para testing en este proyecto - NO exportados en npm)
97
+ // ========================================
98
+
99
+ // Los ejemplos no se exportan en el paquete npm
100
+ // Solo están disponibles durante el desarrollo local
@@ -0,0 +1,76 @@
1
+ import React from 'react'
2
+ import { useResponsiveLayout } from '../hooks'
3
+ import { Sidebar, Footer } from '../components/layout'
4
+ import { SidebarProvider, useSidebar } from '../context'
5
+
6
+ interface DashboardLayoutProps {
7
+ children: React.ReactNode
8
+ }
9
+
10
+ const DashboardLayoutContent: React.FC<{ children: React.ReactNode }> = ({ children }) => {
11
+ const { layoutUtils } = useResponsiveLayout()
12
+ const { setSidebarOpen } = useSidebar()
13
+
14
+ return (
15
+ <div className="min-h-screen bg-black flex flex-col">
16
+ {/* Navbar para móvil (igual que SidebarLayout) */}
17
+ <div className="sticky top-0 z-50">
18
+ <nav className="bg-gradient-to-r from-gray-900 via-black to-gray-900 border-b border-cyan-500/20 shadow-2xl relative">
19
+ <div className="w-full">
20
+ <div className="px-4 py-4">
21
+ <div className="flex items-center justify-between">
22
+ <div className="flex items-center space-x-2">
23
+ {/* Hamburger button para móvil - A LA IZQUIERDA */}
24
+ <button
25
+ onClick={() => setSidebarOpen(true)}
26
+ className="p-2 rounded-lg text-gray-300 hover:text-cyan-400 hover:bg-cyan-500/10 transition-colors"
27
+ >
28
+ <svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
29
+ <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 6h16M4 12h16M4 18h16" />
30
+ </svg>
31
+ </button>
32
+
33
+ <div className="flex items-center space-x-2">
34
+ <div className="w-1.5 h-1.5 bg-cyan-400 rounded-full shadow-lg shadow-cyan-400/50 animate-pulse"></div>
35
+ <h3 className="text-base font-black text-white tracking-tight">
36
+ Sistema Responsivo
37
+ </h3>
38
+ </div>
39
+ <div className="px-2 py-0.5 text-cyan-400 font-mono bg-black/50 border border-cyan-500/30 rounded text-xs font-bold tracking-widest">
40
+ DASHBOARD
41
+ </div>
42
+ </div>
43
+ </div>
44
+ </div>
45
+ </div>
46
+ </nav>
47
+ </div>
48
+
49
+ {/* Content area con sidebar */}
50
+ <div className="flex flex-1">
51
+ {/* Sidebar */}
52
+ <Sidebar />
53
+
54
+ {/* Main content */}
55
+ <main className="flex-1 overflow-auto">
56
+ <div className={layoutUtils.getContainerClass()}>
57
+ {children}
58
+ </div>
59
+ </main>
60
+ </div>
61
+
62
+ {/* Footer */}
63
+ <Footer />
64
+ </div>
65
+ )
66
+ }
67
+
68
+ const DashboardLayout: React.FC<DashboardLayoutProps> = ({ children }) => {
69
+ return (
70
+ <SidebarProvider>
71
+ <DashboardLayoutContent>{children}</DashboardLayoutContent>
72
+ </SidebarProvider>
73
+ )
74
+ }
75
+
76
+ export default DashboardLayout
@@ -0,0 +1,30 @@
1
+ import React from 'react'
2
+ import { Navigation, Footer } from '../components/layout'
3
+ import { useResponsiveLayout } from '../hooks'
4
+
5
+ interface DefaultLayoutProps {
6
+ children: React.ReactNode
7
+ }
8
+
9
+ const DefaultLayout: React.FC<DefaultLayoutProps> = ({ children }) => {
10
+ const { layoutUtils } = useResponsiveLayout()
11
+
12
+ return (
13
+ <div className="min-h-screen bg-black flex flex-col">
14
+ {/* Navigation fijo arriba */}
15
+ <Navigation />
16
+
17
+ {/* Main content con padding-top para la navigation */}
18
+ <main className="flex-1">
19
+ <div className={layoutUtils.getContainerClass()}>
20
+ {children}
21
+ </div>
22
+ </main>
23
+
24
+ {/* Footer fijo abajo */}
25
+ <Footer />
26
+ </div>
27
+ )
28
+ }
29
+
30
+ export default DefaultLayout
@@ -0,0 +1,38 @@
1
+ import React from 'react'
2
+ import { useResponsiveLayout } from '../hooks'
3
+ import {
4
+ DefaultLayout,
5
+ SidebarLayout,
6
+ DashboardLayout,
7
+ MinimalLayout
8
+ } from './index'
9
+
10
+ interface MainLayoutProps {
11
+ children: React.ReactNode
12
+ /**
13
+ * Layout específico a usar. Si se proporciona, sobrescribe el layout del contexto.
14
+ * Valores posibles: 'default', 'sidebar', 'dashboard', 'minimal'
15
+ */
16
+ layout?: 'default' | 'sidebar' | 'dashboard' | 'minimal'
17
+ }
18
+
19
+ const MainLayout: React.FC<MainLayoutProps> = ({ children, layout: layoutProp }) => {
20
+ const { layout } = useResponsiveLayout()
21
+
22
+ // Usar el layout del prop si se proporciona, sino usar el del contexto
23
+ const currentLayout = layoutProp || layout.current
24
+
25
+ // Seleccionar el layout apropiado basado en el estado del contexto o prop
26
+ const layouts = {
27
+ default: DefaultLayout,
28
+ sidebar: SidebarLayout,
29
+ dashboard: DashboardLayout,
30
+ minimal: MinimalLayout,
31
+ }
32
+
33
+ const LayoutComponent = layouts[currentLayout as keyof typeof layouts] || DefaultLayout
34
+
35
+ return <LayoutComponent>{children}</LayoutComponent>
36
+ }
37
+
38
+ export default MainLayout
@@ -0,0 +1,20 @@
1
+ import React from 'react'
2
+ import { useResponsiveLayout } from '../hooks'
3
+
4
+ interface MinimalLayoutProps {
5
+ children: React.ReactNode
6
+ }
7
+
8
+ const MinimalLayout: React.FC<MinimalLayoutProps> = ({ children }) => {
9
+ const { layoutUtils } = useResponsiveLayout()
10
+
11
+ return (
12
+ <div className="min-h-screen bg-black">
13
+ <main className={layoutUtils.getContainerClass()}>
14
+ {children}
15
+ </main>
16
+ </div>
17
+ )
18
+ }
19
+
20
+ export default MinimalLayout
@@ -0,0 +1,36 @@
1
+ import React from 'react'
2
+ import { useResponsiveLayout } from '../hooks'
3
+ import { Sidebar } from '../components/layout'
4
+ import { SidebarProvider } from '../context'
5
+
6
+ interface SidebarLayoutProps {
7
+ children: React.ReactNode
8
+ }
9
+
10
+ const SidebarLayoutContent: React.FC<{ children: React.ReactNode }> = ({ children }) => {
11
+ const { layoutUtils } = useResponsiveLayout()
12
+
13
+ return (
14
+ <div className="min-h-screen bg-black flex">
15
+ {/* Sidebar */}
16
+ <Sidebar />
17
+
18
+ {/* Main content */}
19
+ <main className="flex-1 overflow-auto">
20
+ <div className={layoutUtils.getContainerClass()}>
21
+ {children}
22
+ </div>
23
+ </main>
24
+ </div>
25
+ )
26
+ }
27
+
28
+ const SidebarLayout: React.FC<SidebarLayoutProps> = ({ children }) => {
29
+ return (
30
+ <SidebarProvider>
31
+ <SidebarLayoutContent>{children}</SidebarLayoutContent>
32
+ </SidebarProvider>
33
+ )
34
+ }
35
+
36
+ export default SidebarLayout
@@ -0,0 +1,5 @@
1
+ export { default as MainLayout } from './MainLayout'
2
+ export { default as DefaultLayout } from './DefaultLayout'
3
+ export { default as SidebarLayout } from './SidebarLayout'
4
+ export { default as DashboardLayout } from './DashboardLayout'
5
+ export { default as MinimalLayout } from './MinimalLayout'
package/src/main.tsx ADDED
@@ -0,0 +1,10 @@
1
+ import { StrictMode } from 'react'
2
+ import { createRoot } from 'react-dom/client'
3
+ import './index.css'
4
+ import App from './App.tsx'
5
+
6
+ createRoot(document.getElementById('root')!).render(
7
+ <StrictMode>
8
+ <App />
9
+ </StrictMode>,
10
+ )