bertui 1.0.2 → 1.1.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.
@@ -1,12 +1,27 @@
1
+ // src/router/Router.js - SSR COMPATIBLE VERSION
1
2
  import { useState, useEffect, createContext, useContext } from 'react';
2
3
 
3
4
  const RouterContext = createContext(null);
4
5
 
6
+ // ✅ FIX: SSR-safe useRouter
5
7
  export function useRouter() {
6
8
  const context = useContext(RouterContext);
9
+
10
+ // During SSR (when window doesn't exist), return a mock router
11
+ if (typeof window === 'undefined') {
12
+ return {
13
+ pathname: '/',
14
+ params: {},
15
+ navigate: () => {},
16
+ currentRoute: null,
17
+ isSSR: true
18
+ };
19
+ }
20
+
7
21
  if (!context) {
8
22
  throw new Error('useRouter must be used within a Router component');
9
23
  }
24
+
10
25
  return context;
11
26
  }
12
27
 
@@ -62,15 +77,18 @@ export function Router({ routes }) {
62
77
  }
63
78
 
64
79
  function navigate(path) {
65
- window.history.pushState({}, '', path);
66
- matchAndSetRoute(path);
80
+ if (typeof window !== 'undefined') {
81
+ window.history.pushState({}, '', path);
82
+ matchAndSetRoute(path);
83
+ }
67
84
  }
68
85
 
69
86
  const routerValue = {
70
87
  currentRoute,
71
88
  params,
72
89
  navigate,
73
- pathname: window.location.pathname
90
+ pathname: typeof window !== 'undefined' ? window.location.pathname : '/',
91
+ isSSR: typeof window === 'undefined'
74
92
  };
75
93
 
76
94
  const Component = currentRoute?.component;
@@ -82,12 +100,27 @@ export function Router({ routes }) {
82
100
  );
83
101
  }
84
102
 
103
+ // ✅ FIX: SSR-safe Link component
85
104
  export function Link({ to, children, ...props }) {
86
- const { navigate } = useRouter();
105
+ // Try to get router, but don't fail if it doesn't exist
106
+ let router;
107
+ try {
108
+ router = useRouter();
109
+ } catch (e) {
110
+ // During SSR, router might not be available
111
+ router = null;
112
+ }
87
113
 
88
114
  function handleClick(e) {
115
+ // During SSR, just use normal link behavior
116
+ if (typeof window === 'undefined') return;
117
+
118
+ // If no router or navigate function, use normal link
119
+ if (!router || !router.navigate) return;
120
+
121
+ // Only prevent default if we have client-side routing
89
122
  e.preventDefault();
90
- navigate(to);
123
+ router.navigate(to);
91
124
  }
92
125
 
93
126
  return (
@@ -0,0 +1,156 @@
1
+ // src/router/SSRRouter.jsx
2
+ // SSR-Compatible Router for BertUI - Works during build AND runtime
3
+ import React, { useState, useEffect, createContext, useContext } from 'react';
4
+
5
+ const RouterContext = createContext(null);
6
+
7
+ // ✅ SSR-safe useRouter hook
8
+ export function useRouter() {
9
+ const context = useContext(RouterContext);
10
+
11
+ // During SSR, provide a mock router
12
+ if (!context) {
13
+ return {
14
+ pathname: '/',
15
+ params: {},
16
+ navigate: () => {},
17
+ isSSR: true
18
+ };
19
+ }
20
+
21
+ return context;
22
+ }
23
+
24
+ // ✅ SSR-safe Router component
25
+ export function Router({ routes, initialPath = '/' }) {
26
+ const [currentRoute, setCurrentRoute] = useState(null);
27
+ const [params, setParams] = useState({});
28
+ const [isClient, setIsClient] = useState(false);
29
+
30
+ // Detect if we're in the browser
31
+ useEffect(() => {
32
+ setIsClient(true);
33
+ matchAndSetRoute(window.location.pathname);
34
+
35
+ const handlePopState = () => {
36
+ matchAndSetRoute(window.location.pathname);
37
+ };
38
+
39
+ window.addEventListener('popstate', handlePopState);
40
+ return () => window.removeEventListener('popstate', handlePopState);
41
+ }, [routes]);
42
+
43
+ // Match route on server-side (SSR)
44
+ if (!isClient && !currentRoute) {
45
+ const matched = matchRoute(initialPath, routes);
46
+ if (matched) {
47
+ return React.createElement(
48
+ RouterContext.Provider,
49
+ { value: { pathname: initialPath, params: matched.params, navigate: () => {}, isSSR: true } },
50
+ React.createElement(matched.route.component, { params: matched.params })
51
+ );
52
+ }
53
+ }
54
+
55
+ function matchRoute(pathname, routesList) {
56
+ // Try static routes first
57
+ for (const route of routesList) {
58
+ if (route.type === 'static' && route.path === pathname) {
59
+ return { route, params: {} };
60
+ }
61
+ }
62
+
63
+ // Try dynamic routes
64
+ for (const route of routesList) {
65
+ if (route.type === 'dynamic') {
66
+ const pattern = route.path.replace(/\[([^\]]+)\]/g, '([^/]+)');
67
+ const regex = new RegExp('^' + pattern + '$');
68
+ const match = pathname.match(regex);
69
+
70
+ if (match) {
71
+ const paramNames = [...route.path.matchAll(/\[([^\]]+)\]/g)].map(m => m[1]);
72
+ const extractedParams = {};
73
+ paramNames.forEach((name, i) => {
74
+ extractedParams[name] = match[i + 1];
75
+ });
76
+
77
+ return { route, params: extractedParams };
78
+ }
79
+ }
80
+ }
81
+
82
+ return null;
83
+ }
84
+
85
+ function matchAndSetRoute(pathname) {
86
+ const matched = matchRoute(pathname, routes);
87
+
88
+ if (matched) {
89
+ setCurrentRoute(matched.route);
90
+ setParams(matched.params);
91
+ } else {
92
+ setCurrentRoute(null);
93
+ setParams({});
94
+ }
95
+ }
96
+
97
+ function navigate(path) {
98
+ if (typeof window !== 'undefined') {
99
+ window.history.pushState({}, '', path);
100
+ matchAndSetRoute(path);
101
+ }
102
+ }
103
+
104
+ const routerValue = {
105
+ currentRoute,
106
+ params,
107
+ navigate,
108
+ pathname: typeof window !== 'undefined' ? window.location.pathname : initialPath,
109
+ isSSR: !isClient
110
+ };
111
+
112
+ const Component = currentRoute?.component;
113
+
114
+ return React.createElement(
115
+ RouterContext.Provider,
116
+ { value: routerValue },
117
+ Component ? React.createElement(Component, { params }) : React.createElement(NotFound, null)
118
+ );
119
+ }
120
+
121
+ // ✅ SSR-safe Link component
122
+ export function Link({ to, children, ...props }) {
123
+ const { navigate, isSSR } = useRouter();
124
+
125
+ function handleClick(e) {
126
+ // Don't prevent default during SSR
127
+ if (isSSR) return;
128
+
129
+ e.preventDefault();
130
+ navigate(to);
131
+ }
132
+
133
+ return React.createElement('a', { href: to, onClick: handleClick, ...props }, children);
134
+ }
135
+
136
+ function NotFound() {
137
+ return React.createElement(
138
+ 'div',
139
+ {
140
+ style: {
141
+ display: 'flex',
142
+ flexDirection: 'column',
143
+ alignItems: 'center',
144
+ justifyContent: 'center',
145
+ minHeight: '100vh',
146
+ fontFamily: 'system-ui'
147
+ }
148
+ },
149
+ React.createElement('h1', { style: { fontSize: '6rem', margin: 0 } }, '404'),
150
+ React.createElement('p', { style: { fontSize: '1.5rem', color: '#666' } }, 'Page not found'),
151
+ React.createElement('a', {
152
+ href: '/',
153
+ style: { color: '#10b981', textDecoration: 'none', fontSize: '1.2rem' }
154
+ }, 'Go home')
155
+ );
156
+ }