@stevederico/skateboard-ui 2.9.5 → 2.9.8
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/App.jsx +2 -1
- package/CHANGELOG.md +15 -0
- package/README.md +28 -1
- package/core/Context.jsx +43 -0
- package/core/Utilities.js +32 -1
- package/package.json +1 -1
- package/views/SignInView.jsx +2 -3
- package/views/SignUpView.jsx +3 -4
package/App.jsx
CHANGED
|
@@ -159,15 +159,16 @@ export function createSkateboardApp({ constants, appRoutes, defaultRoute = appRo
|
|
|
159
159
|
<ThemeProvider attribute="class" defaultTheme="system" enableSystem>
|
|
160
160
|
<Toaster position="top-right" richColors closeButton />
|
|
161
161
|
<ContextProvider constants={constants}>
|
|
162
|
-
<AuthOverlay />
|
|
163
162
|
{Wrapper ? (
|
|
164
163
|
<Wrapper>
|
|
165
164
|
<Router>
|
|
165
|
+
<AuthOverlay />
|
|
166
166
|
<App constants={constants} appRoutes={appRoutes} defaultRoute={defaultRoute} landingPage={landingPage} overrides={overrides} />
|
|
167
167
|
</Router>
|
|
168
168
|
</Wrapper>
|
|
169
169
|
) : (
|
|
170
170
|
<Router>
|
|
171
|
+
<AuthOverlay />
|
|
171
172
|
<App constants={constants} appRoutes={appRoutes} defaultRoute={defaultRoute} landingPage={landingPage} overrides={overrides} />
|
|
172
173
|
</Router>
|
|
173
174
|
)}
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
# CHANGELOG
|
|
2
2
|
|
|
3
|
+
2.9.8
|
|
4
|
+
|
|
5
|
+
Add useUser hook
|
|
6
|
+
Add useDispatch hook
|
|
7
|
+
Update README Context docs
|
|
8
|
+
|
|
9
|
+
2.9.7
|
|
10
|
+
|
|
11
|
+
Add useSafeNavigate hook
|
|
12
|
+
Fix Router context error in auth views
|
|
13
|
+
|
|
14
|
+
2.9.6
|
|
15
|
+
|
|
16
|
+
Move AuthOverlay inside Router
|
|
17
|
+
|
|
3
18
|
2.9.5
|
|
4
19
|
|
|
5
20
|
Reuse SignInView in AuthOverlay
|
package/README.md
CHANGED
|
@@ -770,7 +770,7 @@ import ErrorBoundary from '@stevederico/skateboard-ui/ErrorBoundary';
|
|
|
770
770
|
## Context (State Management)
|
|
771
771
|
|
|
772
772
|
```javascript
|
|
773
|
-
import { getState } from '@stevederico/skateboard-ui/Context';
|
|
773
|
+
import { getState, useUser, useDispatch } from '@stevederico/skateboard-ui/Context';
|
|
774
774
|
|
|
775
775
|
function MyComponent() {
|
|
776
776
|
const { state, dispatch } = getState();
|
|
@@ -785,6 +785,33 @@ function MyComponent() {
|
|
|
785
785
|
}
|
|
786
786
|
```
|
|
787
787
|
|
|
788
|
+
### Optimized Hooks
|
|
789
|
+
|
|
790
|
+
Use these hooks to avoid unnecessary re-renders:
|
|
791
|
+
|
|
792
|
+
```javascript
|
|
793
|
+
import { useUser, useDispatch } from '@stevederico/skateboard-ui/Context';
|
|
794
|
+
|
|
795
|
+
// Only re-renders when user changes (not on sidebar/theme changes)
|
|
796
|
+
function ProfileCard() {
|
|
797
|
+
const user = useUser();
|
|
798
|
+
if (!user) return null;
|
|
799
|
+
return <div>{user.name}</div>;
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
// Stable dispatch reference, never causes re-renders
|
|
803
|
+
function SignOutButton() {
|
|
804
|
+
const dispatch = useDispatch();
|
|
805
|
+
return <button onClick={() => dispatch({ type: 'CLEAR_USER' })}>Sign Out</button>;
|
|
806
|
+
}
|
|
807
|
+
```
|
|
808
|
+
|
|
809
|
+
| Hook | Returns | Re-renders on |
|
|
810
|
+
|------|---------|---------------|
|
|
811
|
+
| `getState()` | `{ state, dispatch }` | Any state change |
|
|
812
|
+
| `useUser()` | `user` or `null` | User changes only |
|
|
813
|
+
| `useDispatch()` | `dispatch` | Never (stable) |
|
|
814
|
+
|
|
788
815
|
### State Shape
|
|
789
816
|
|
|
790
817
|
```javascript
|
package/core/Context.jsx
CHANGED
|
@@ -206,3 +206,46 @@ export function ContextProvider({ children, constants }) {
|
|
|
206
206
|
export function getState() {
|
|
207
207
|
return useContext(context);
|
|
208
208
|
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Hook to access only the current user.
|
|
212
|
+
*
|
|
213
|
+
* More efficient than getState() when you only need user data,
|
|
214
|
+
* as it avoids re-renders from unrelated state changes.
|
|
215
|
+
*
|
|
216
|
+
* @returns {Object|null} Current user object or null if not authenticated
|
|
217
|
+
*
|
|
218
|
+
* @example
|
|
219
|
+
* import { useUser } from '@stevederico/skateboard-ui/Context';
|
|
220
|
+
*
|
|
221
|
+
* function ProfileCard() {
|
|
222
|
+
* const user = useUser();
|
|
223
|
+
* if (!user) return null;
|
|
224
|
+
* return <div>{user.name}</div>;
|
|
225
|
+
* }
|
|
226
|
+
*/
|
|
227
|
+
export function useUser() {
|
|
228
|
+
const { state } = useContext(context);
|
|
229
|
+
return state.user;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Hook to access dispatch function.
|
|
234
|
+
*
|
|
235
|
+
* Use when you only need to dispatch actions without reading state.
|
|
236
|
+
* Avoids re-renders since dispatch is stable.
|
|
237
|
+
*
|
|
238
|
+
* @returns {Function} Dispatch function
|
|
239
|
+
*
|
|
240
|
+
* @example
|
|
241
|
+
* import { useDispatch } from '@stevederico/skateboard-ui/Context';
|
|
242
|
+
*
|
|
243
|
+
* function SignOutButton() {
|
|
244
|
+
* const dispatch = useDispatch();
|
|
245
|
+
* return <button onClick={() => dispatch({ type: 'CLEAR_USER' })}>Sign Out</button>;
|
|
246
|
+
* }
|
|
247
|
+
*/
|
|
248
|
+
export function useDispatch() {
|
|
249
|
+
const { dispatch } = useContext(context);
|
|
250
|
+
return dispatch;
|
|
251
|
+
}
|
package/core/Utilities.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { useEffect, useState } from 'react';
|
|
1
|
+
import { useEffect, useState, useContext } from 'react';
|
|
2
|
+
import { UNSAFE_NavigationContext } from 'react-router-dom';
|
|
2
3
|
import { getDispatch } from './Context.jsx';
|
|
3
4
|
|
|
4
5
|
// Constants will be initialized by the app shell
|
|
@@ -985,3 +986,33 @@ export function setUIVisibility({ sidebar, tabBar }) {
|
|
|
985
986
|
console.warn('setUIVisibility: Context not initialized');
|
|
986
987
|
}
|
|
987
988
|
}
|
|
989
|
+
|
|
990
|
+
/**
|
|
991
|
+
* Safe navigation hook that works with or without Router context.
|
|
992
|
+
*
|
|
993
|
+
* Uses UNSAFE_NavigationContext directly instead of useNavigate() to avoid
|
|
994
|
+
* throwing when rendered outside Router (e.g., in Portals or module duplication).
|
|
995
|
+
* Falls back to window.location.href if no Router context is available.
|
|
996
|
+
*
|
|
997
|
+
* @returns {function} Navigate function (path, options?) => void
|
|
998
|
+
*
|
|
999
|
+
* @example
|
|
1000
|
+
* const navigate = useSafeNavigate();
|
|
1001
|
+
* navigate('/app');
|
|
1002
|
+
* navigate('/app', { replace: true });
|
|
1003
|
+
*/
|
|
1004
|
+
export function useSafeNavigate() {
|
|
1005
|
+
const ctx = useContext(UNSAFE_NavigationContext);
|
|
1006
|
+
|
|
1007
|
+
if (!ctx) {
|
|
1008
|
+
return (path) => { window.location.href = path; };
|
|
1009
|
+
}
|
|
1010
|
+
|
|
1011
|
+
return (path, options = {}) => {
|
|
1012
|
+
if (options.replace) {
|
|
1013
|
+
ctx.navigator.replace(path, options.state);
|
|
1014
|
+
} else {
|
|
1015
|
+
ctx.navigator.push(path, options.state);
|
|
1016
|
+
}
|
|
1017
|
+
};
|
|
1018
|
+
}
|
package/package.json
CHANGED
package/views/SignInView.jsx
CHANGED
|
@@ -6,9 +6,8 @@ import { Label } from "../shadcn/ui/label"
|
|
|
6
6
|
import { Card, CardContent, CardHeader } from "../shadcn/ui/card"
|
|
7
7
|
import { Alert, AlertDescription } from "../shadcn/ui/alert"
|
|
8
8
|
import DynamicIcon from '../core/DynamicIcon';
|
|
9
|
-
import { useNavigate } from 'react-router-dom';
|
|
10
9
|
import { getState } from "../core/Context.jsx";
|
|
11
|
-
import { getBackendURL } from '../core/Utilities'
|
|
10
|
+
import { getBackendURL, useSafeNavigate } from '../core/Utilities'
|
|
12
11
|
|
|
13
12
|
/**
|
|
14
13
|
* Sign-in form component.
|
|
@@ -43,7 +42,7 @@ export default function LoginForm({
|
|
|
43
42
|
const [email, setEmail] = useState('');
|
|
44
43
|
const [password, setPassword] = useState('');
|
|
45
44
|
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
46
|
-
const navigate =
|
|
45
|
+
const navigate = useSafeNavigate();
|
|
47
46
|
const emailInputRef = useRef(null);
|
|
48
47
|
|
|
49
48
|
const [errorMessage, setErrorMessage] = useState('')
|
package/views/SignUpView.jsx
CHANGED
|
@@ -6,9 +6,8 @@ import { Label } from "../shadcn/ui/label"
|
|
|
6
6
|
import { Card, CardContent, CardHeader } from "../shadcn/ui/card"
|
|
7
7
|
import { Alert, AlertDescription } from "../shadcn/ui/alert"
|
|
8
8
|
import DynamicIcon from '../core/DynamicIcon';
|
|
9
|
-
import { useNavigate } from 'react-router-dom';
|
|
10
9
|
import { getState } from "../core/Context.jsx";
|
|
11
|
-
import { getBackendURL } from '../core/Utilities'
|
|
10
|
+
import { getBackendURL, useSafeNavigate } from '../core/Utilities'
|
|
12
11
|
|
|
13
12
|
/**
|
|
14
13
|
* Sign-up form component.
|
|
@@ -44,8 +43,8 @@ export default function LoginForm({
|
|
|
44
43
|
const [email, setEmail] = useState('');
|
|
45
44
|
const [password, setPassword] = useState('');
|
|
46
45
|
const [name, setName] = useState('');
|
|
47
|
-
const
|
|
48
|
-
const
|
|
46
|
+
const [errorMessage, setErrorMessage] = useState('');
|
|
47
|
+
const navigate = useSafeNavigate();
|
|
49
48
|
const nameInputRef = useRef(null);
|
|
50
49
|
|
|
51
50
|
// Focus the first input on mount
|