Skip to content

Commit 1557951

Browse files
committed
feat: new navigation api * 2
1 parent fd74cb8 commit 1557951

File tree

5 files changed

+33
-13
lines changed

5 files changed

+33
-13
lines changed

src/components/actions/Link/Link.docs.mdx

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ Notes:
128128
```
129129

130130
Behavior:
131-
- Uses `navigate(delta)` from the navigation adapter
131+
- Uses `navigate(delta)` from the navigation adapter with overload signature
132132
- The default adapter calls `window.history.go(delta)`
133133

134134
---
@@ -140,21 +140,27 @@ Behavior:
140140
### Interface
141141

142142
```ts
143+
type NavigateLike = {
144+
(to: To, options?: NavigateOptions): void;
145+
(delta: number): void;
146+
};
147+
143148
interface NavigationAdapter {
144149
useHref: (to: To, opts?: { relative?: 'route' | 'path' }) => string;
145-
useNavigate: () => (to: To | number, opts?: NavigateOptions) => void;
150+
useNavigate: () => NavigateLike;
146151
}
147152
```
148153

149154
### React Router v6+ (Recommended)
150155

151-
React Router hooks are directly compatible with our `NavigationAdapter` interface:
156+
React Router v6+ hooks are directly compatible with our `NavigationAdapter` interface:
152157

153158
```jsx
154159
import { Root } from '@cube-dev/ui-kit';
155160
import { useHref, useNavigate } from 'react-router-dom';
156161

157162
const navigation = { useHref, useNavigate };
163+
// React Router's useNavigate supports both overloads: navigate(to, options) and navigate(delta)
158164

159165
export function App() {
160166
return (
@@ -179,13 +185,15 @@ function MyRouterAdapter() {
179185
return typeof to === 'string' ? to : `${to.pathname || ''}${to.search || ''}${to.hash || ''}`;
180186
},
181187
useNavigate: () => {
182-
return (to, options) => {
188+
// Return function with overload signatures
189+
const navigate = (to, options) => {
183190
if (typeof to === 'number') {
184191
window.history.go(to);
185192
} else {
186193
myRouter.navigate(to, options);
187194
}
188195
};
196+
return navigate;
189197
},
190198
};
191199
}

src/components/actions/use-action.ts

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ const BUTTON_PRESS_EVENT = 'Button Press';
2323

2424
// Re-export types for convenience
2525
export type {
26+
NavigateLike,
2627
Path,
2728
To,
2829
NavigateArg,
@@ -196,7 +197,7 @@ export function performClickHandler(
196197
}
197198

198199
// Handle new tab (via ! prefix or user modifiers)
199-
if (evt.shiftKey || evt.metaKey || newTab) {
200+
if (evt.shiftKey || evt.metaKey || (evt as any).ctrlKey || newTab) {
200201
openLink(resolvedHref, true);
201202
tracking.event(
202203
LINK_PRESS_EVENT,
@@ -263,11 +264,6 @@ export const useAction = function useAction(
263264
const navigation = useContext(UIKitContext).navigation;
264265
const isDisabled = props.isDisabled;
265266

266-
// Always call navigation hooks (using fallback when to is not provided)
267-
const fallbackTo = to || '.';
268-
const navigate = navigation.useNavigate();
269-
const resolvedHref = navigation.useHref(fallbackTo);
270-
271267
const {
272268
newTab,
273269
nativeRoute,
@@ -277,6 +273,13 @@ export const useAction = function useAction(
277273
isExternal,
278274
} = parseTo(to);
279275

276+
// Always call navigation hooks (using fallback when to is not provided)
277+
const fallbackTo = to || '.';
278+
const navigate = navigation.useNavigate();
279+
const resolvedHref = navigation.useHref(fallbackTo);
280+
// Always resolve cleanTo href to avoid conditional hook calls
281+
const cleanToHref = navigation.useHref(cleanTo || '.');
282+
280283
// Determine element type: 'a' for navigation, 'button' for actions
281284
as = to && !isHistoryNavigation ? 'a' : as || 'button';
282285

@@ -291,7 +294,7 @@ export const useAction = function useAction(
291294
href =
292295
typeof cleanTo === 'string' && isExternal
293296
? cleanTo // External URLs as-is
294-
: navigation.useHref(cleanTo);
297+
: cleanToHref;
295298
} else {
296299
// Regular navigation
297300
href = resolvedHref;

src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ export type { CubeRootProps } from './components/Root';
3737
// Navigation types and helpers
3838
export type {
3939
NavigationAdapter,
40+
NavigateLike,
4041
Path,
4142
To,
4243
NavigateArg,

src/providers/navigation.types.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,14 @@ export interface NavigateOptions {
1616
preventScrollReset?: boolean;
1717
}
1818

19+
// Overload-style type (NOT a union in the first arg)
20+
export type NavigateLike = {
21+
(to: To, options?: NavigateOptions): void;
22+
(delta: number): void;
23+
};
24+
1925
export interface NavigationAdapter {
2026
// React Router compatible hook signatures
2127
useHref: (to: To, options?: { relative?: RelativeRoutingType }) => string;
22-
useNavigate: () => (to: To | number, options?: NavigateOptions) => void;
28+
useNavigate: () => NavigateLike;
2329
}

src/providers/navigationAdapter.default.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import {
2+
NavigateLike,
23
NavigateOptions,
34
NavigationAdapter,
45
Path,
@@ -31,7 +32,7 @@ export const defaultNavigationAdapter: NavigationAdapter = {
3132
},
3233

3334
useNavigate: () => {
34-
return (to: To | number, options?: NavigateOptions) => {
35+
const navigate: NavigateLike = (to: any, options?: NavigateOptions) => {
3536
// Handle history navigation (numbers)
3637
if (typeof to === 'number') {
3738
if (typeof window !== 'undefined') {
@@ -51,5 +52,6 @@ export const defaultNavigationAdapter: NavigationAdapter = {
5152
}
5253
}
5354
};
55+
return navigate;
5456
},
5557
};

0 commit comments

Comments
 (0)