UNPKG

15.5 kBPlain TextView Raw
1import type {
2 NavigationContainerRefWithCurrent,
3 NavigationState,
4 PartialState,
5} from '@react-navigation/core'
6import type { ReactNode } from 'react'
7import type { TextProps, GestureResponderEvent } from 'react-native'
8
9export namespace OneRouter {
10 export interface __routes<T extends string = string> extends Record<string, unknown> {}
11
12 // making a simple helper that gives you everything for a route
13 export type Route<Path> = {
14 Params: InputRouteParams<Path>
15 Props: { params: InputRouteParams<Path> }
16 Loader: (props: { params: InputRouteParams<Path> }) => any
17 }
18
19 type StaticRoutes = __routes extends { StaticRoutes: string } ? __routes['StaticRoutes'] : string
20
21 type DynamicRoutes<T extends string> = __routes<T> extends { DynamicRoutes: any }
22 ? T extends __routes<infer _>['DynamicRoutes']
23 ? T
24 : never
25 : string
26
27 export type DynamicRouteTemplate = __routes extends { DynamicRouteTemplate: string }
28 ? __routes['DynamicRouteTemplate']
29 : string
30
31 export type NavigationRef = NavigationContainerRefWithCurrent<ReactNavigation.RootParamList>
32
33 export type RelativePathString = `./${string}` | `../${string}` | '..'
34 export type AbsoluteRoute = DynamicRouteTemplate | StaticRoutes
35 export type ExternalPathString = `${string}:${string}`
36 export type OneRouterRoutes = DynamicRouteTemplate | StaticRoutes | RelativePathString
37 export type AllRoutes = OneRouterRoutes | ExternalPathString
38
39 export type LinkToOptions = {
40 scroll?: boolean
41 }
42
43 type SearchOrHash = `?${string}` | `#${string}`
44
45 export type UnknownInputParams = Record<
46 string,
47 string | number | undefined | null | (string | number)[]
48 >
49
50 type UnknownOutputParams = Record<string, string | string[]>
51
52 /**
53 * Return only the RoutePart of a string. If the string has multiple parts return never
54 *
55 * string | type
56 * ---------|------
57 * 123 | 123
58 * /123/abc | never
59 * 123?abc | never
60 * ./123 | never
61 * /123 | never
62 * 123/../ | never
63 */
64 export type SingleRoutePart<S extends string> = S extends `${string}/${string}`
65 ? never
66 : S extends `${string}${SearchOrHash}`
67 ? never
68 : S extends ''
69 ? never
70 : S extends `(${string})`
71 ? never
72 : S extends `[${string}]`
73 ? never
74 : S
75
76 /**
77 * Return only the CatchAll router part. If the string has search parameters or a hash return never
78 */
79 export type CatchAllRoutePart<S extends string> = S extends `${string}${SearchOrHash}`
80 ? never
81 : S extends ''
82 ? never
83 : S extends `${string}(${string})${string}`
84 ? never
85 : S extends `${string}[${string}]${string}`
86 ? never
87 : S
88
89 /**
90 * Return the name of a route parameter
91 * '[test]' -> 'test'
92 * 'test' -> never
93 * '[...test]' -> '...test'
94 */
95 type IsParameter<Part> = Part extends `[${infer ParamName}]` ? ParamName : never
96
97 /**
98 * Return a union of all raw parameter names. If there are no names return never
99 *
100 * This differs from ParameterNames as it returns the `...` for catch all parameters
101 *
102 * /[test] -> 'test'
103 * /[abc]/[...def] -> 'abc'|'...def'
104 */
105 type ParameterNames<Path> = Path extends `${infer PartA}/${infer PartB}`
106 ? IsParameter<PartA> | ParameterNames<PartB>
107 : IsParameter<Path>
108
109 /**
110 * Returns all segments of a route.
111 *
112 * /(group)/123/abc/[id]/[...rest] -> ['(group)', '123', 'abc', '[id]', '[...rest]'
113 */
114 type RouteSegments<Path> = Path extends `${infer PartA}/${infer PartB}`
115 ? PartA extends '' | '.'
116 ? [...RouteSegments<PartB>]
117 : [PartA, ...RouteSegments<PartB>]
118 : Path extends ''
119 ? []
120 : [Path]
121
122 type AllUngroupedRoutes<Path> = Path extends `(${infer PartA})/${infer PartB}`
123 ? `(${PartA})/${AllUngroupedRoutes<PartB>}` | AllUngroupedRoutes<PartB>
124 : Path
125
126 /**
127 * Returns a Record of the routes parameters as strings and CatchAll parameters
128 *
129 * There are two versions, input and output, as you can input 'string | number' but
130 * the output will always be 'string'
131 *
132 * /[id]/[...rest] -> { id: string, rest: string[] }
133 * /no-params -> {}
134 */
135 export type InputRouteParams<Path> = {
136 [Key in ParameterNames<Path> as Key extends `...${infer Name}`
137 ? Name
138 : Key]: Key extends `...${string}` ? string[] : string
139 }
140 // TODO @nate: i commented this out to get better types but we probably need to fix better
141 // & UnknownInputParams
142
143 type OutputRouteParams<Path> = {
144 [Key in ParameterNames<Path> as Key extends `...${infer Name}`
145 ? Name
146 : Key]: Key extends `...${string}` ? string[] : string
147 } & UnknownOutputParams
148
149 /**
150 * Returns the search parameters for a route.
151 */
152 export type SearchParams<T extends AllRoutes = never> = T extends DynamicRouteTemplate
153 ? OutputRouteParams<T>
154 : T extends StaticRoutes
155 ? never
156 : UnknownOutputParams
157
158 /*********
159 * Href *
160 *********/
161
162 export type DynamicRoutesHref = DynamicRouteString<{ __branded__: any }, DynamicRouteTemplate>
163 export type DynamicRoutesHref2 = DynamicRouteString<string, DynamicRouteTemplate>
164
165 /**
166 * The main routing type for One. Includes all available routes with strongly typed parameters.
167 */
168 export type Href<T extends string | object = { __branded__: any }> =
169 | StringRouteToType<AllUngroupedRoutes<StaticRoutes> | RelativePathString | ExternalPathString>
170 | DynamicRouteString<T, DynamicRouteTemplate>
171 | DynamicRouteObject<
172 StaticRoutes | RelativePathString | ExternalPathString | DynamicRouteTemplate
173 >
174
175 type StringRouteToType<T extends string> =
176 | T
177 | `${T}${SearchOrHash}`
178 | { pathname: T; params?: UnknownInputParams | never }
179
180 /**
181 * Converts a dynamic route template to a Href string type
182 */
183 type DynamicRouteString<
184 T extends string | object,
185 P = DynamicRouteTemplate,
186 > = '__branded__' extends keyof T
187 ? DynamicTemplateToHrefString<P>
188 : T extends string
189 ? DynamicRoutes<T>
190 : never
191
192 type DynamicTemplateToHrefString<Path> = Path extends `${infer PartA}/${infer PartB}`
193 ? // If the current segment (PartA) is dynamic, allow any string. This loop again with the next segment (PartB)
194 `${PartA extends `[${string}]` ? string : PartA}/${DynamicTemplateToHrefString<PartB>}`
195 : // Path is the last segment.
196 Path extends `[${string}]`
197 ? string
198 : Path
199
200 type DynamicRouteObject<T> = T extends DynamicRouteTemplate
201 ? {
202 pathname: T
203 params: InputRouteParams<T>
204 }
205 : never
206
207 export type LoadingState = 'loading' | 'loaded'
208
209 export type ResultState = PartialState<NavigationState> & {
210 state?: ResultState
211 linkOptions?: OneRouter.LinkToOptions
212 }
213
214 export type RootStateListener = (state: ResultState) => void
215 export type LoadingStateListener = (state: LoadingState) => void
216
217 /***********************
218 * One Exports *
219 ***********************/
220
221 export type InputRouteParamsBlank = Record<string, string | undefined | null>
222 export type InpurRouteParamsGeneric = InputRouteParamsBlank | InputRouteParams<any>
223
224 export type Router = {
225 /** Go back in the history. */
226 back: () => void
227 /** If there's history that supports invoking the `back` function. */
228 canGoBack: () => boolean
229 /** Navigate to the provided href using a push operation if possible. */
230 push: (href: Href, options?: LinkToOptions) => void
231 /** Navigate to the provided href. */
232 navigate: (href: Href, options?: LinkToOptions) => void
233 /** Navigate to route without appending to the history. */
234 replace: (href: Href, options?: LinkToOptions) => void
235 /** Navigate to the provided href using a push operation if possible. */
236 dismiss: (count?: number) => void
237 /** Navigate to first screen within the lowest stack. */
238 dismissAll: () => void
239 /** If there's history that supports invoking the `dismiss` and `dismissAll` function. */
240 canDismiss: () => boolean
241 /** Update the current route query params. */
242 setParams: <T = ''>(params?: T extends '' ? InputRouteParamsBlank : InputRouteParams<T>) => void
243 /** Subscribe to state updates from the router */
244 subscribe: (listener: RootStateListener) => () => void
245 /** Subscribe to loading state updates */
246 onLoadState: (listener: LoadingStateListener) => () => void
247 }
248
249 /** The imperative router. */
250 export declare const router: Router
251
252 /************
253 * <Link /> *
254 ************/
255 export interface WebAnchorProps {
256 /**
257 * **Web only:** Specifies where to open the `href`.
258 *
259 * - **_self**: the current tab.
260 * - **_blank**: opens in a new tab or window.
261 * - **_parent**: opens in the parent browsing context. If no parent, defaults to **_self**.
262 * - **_top**: opens in the highest browsing context ancestor. If no ancestors, defaults to **_self**.
263 *
264 * This property is passed to the underlying anchor (`<a>`) tag.
265 *
266 * @default '_self'
267 *
268 * @example
269 * <Link href="https://expo.dev" target="_blank">Go to Expo in new tab</Link>
270 */
271 target?: '_self' | '_blank' | '_parent' | '_top' | (string & object)
272
273 /**
274 * **Web only:** Specifies the relationship between the `href` and the current route.
275 *
276 * Common values:
277 * - **nofollow**: Indicates to search engines that they should not follow the `href`. This is often used for user-generated content or links that should not influence search engine rankings.
278 * - **noopener**: Suggests that the `href` should not have access to the opening window's `window.opener` object, which is a security measure to prevent potentially harmful behavior in cases of links that open new tabs or windows.
279 * - **noreferrer**: Requests that the browser not send the `Referer` HTTP header when navigating to the `href`. This can enhance user privacy.
280 *
281 * The `rel` property is primarily used for informational and instructive purposes, helping browsers and web
282 * crawlers make better decisions about how to handle and interpret the links on a web page. It is important
283 * to use appropriate `rel` values to ensure that links behave as intended and adhere to best practices for web
284 * development and SEO (Search Engine Optimization).
285 *
286 * This property is passed to the underlying anchor (`<a>`) tag.
287 *
288 * @example
289 * <Link href="https://expo.dev" rel="nofollow">Go to Expo</Link>
290 */
291 rel?: string
292
293 /**
294 * **Web only:** Specifies that the `href` should be downloaded when the user clicks on the link,
295 * instead of navigating to it. It is typically used for links that point to files that the user should download,
296 * such as PDFs, images, documents, etc.
297 *
298 * The value of the `download` property, which represents the filename for the downloaded file.
299 * This property is passed to the underlying anchor (`<a>`) tag.
300 *
301 * @example
302 * <Link href="/image.jpg" download="my-image.jpg">Download image</Link>
303 */
304 download?: string
305 }
306
307 export interface LinkProps<T extends string | object>
308 extends Omit<TextProps, 'href'>,
309 WebAnchorProps {
310 /** Path to route to. */
311 href: Href<T>
312
313 // TODO: This may need to be extracted for React Native style support.
314 /** Forward props to child component. Useful for custom buttons. */
315 asChild?: boolean
316
317 /** Should replace the current route without adding to the history. */
318 replace?: boolean
319 /** Should push the current route */
320 push?: boolean
321
322 /** On web, this sets the HTML `class` directly. On native, this can be used with CSS interop tools like Nativewind. */
323 className?: string
324
325 onPress?: (e: React.MouseEvent<HTMLAnchorElement, MouseEvent> | GestureResponderEvent) => void
326 }
327
328 export interface LinkComponent {
329 <T extends string | object>(props: React.PropsWithChildren<LinkProps<T>>): JSX.Element
330 /** Helper method to resolve an Href object into a string. */
331 resolveHref: (href: Href) => string
332 }
333
334 /**
335 * Component to render link to another route using a path.
336 * Uses an anchor tag on the web.
337 *
338 * @param props.href Absolute path to route (e.g. \`/feeds/hot\`).
339 * @param props.replace Should replace the current route without adding to the history.
340 * @param props.asChild Forward props to child component. Useful for custom buttons.
341 * @param props.children Child elements to render the content.
342 * @param props.className On web, this sets the HTML \`class\` directly. On native, this can be used with CSS interop tools like Nativewind.
343 */
344 export declare const Link: LinkComponent
345
346 /** Redirects to the href as soon as the component is mounted. */
347 export declare const Redirect: (props: React.PropsWithChildren<{ href: Href }>) => ReactNode
348 export type Redirect = typeof Redirect
349
350 /**
351 * Hooks
352 */
353
354 export declare function useRouter(): Router
355 type useRouter = typeof useRouter
356
357 /**
358 * Returns the URL search parameters for the contextually focused route. e.g. \`/acme?foo=bar\` -> \`{ foo: "bar" }\`.
359 * This is useful for stacks where you may push a new screen that changes the query parameters.
360 *
361 * To observe updates even when the invoking route is not focused, use \`useActiveParams()\`.
362 * @see \`useActiveParams\`
363 */
364 export declare function useParams<
365 TParams extends AllRoutes | UnknownOutputParams = UnknownOutputParams,
366 >(): TParams extends AllRoutes ? SearchParams<TParams> : TParams
367 type useParams = typeof useParams
368
369 export declare function useSearchParams<
370 TParams extends AllRoutes | UnknownOutputParams = UnknownOutputParams,
371 >(): TParams extends AllRoutes ? SearchParams<TParams> : TParams
372 type useSearchParams = typeof useSearchParams
373
374 /**
375 * Get the globally selected query parameters, including dynamic path segments. This function will update even when the route is not focused.
376 * Useful for analytics or other background operations that don't draw to the screen.
377 *
378 * When querying search params in a stack, opt-towards using \`useParams\` as these will only
379 * update when the route is focused.
380 *
381 * @see \`useParams\`
382 */
383 export declare function useActiveParams<
384 T extends AllRoutes | UnknownOutputParams = UnknownOutputParams,
385 >(): T extends AllRoutes ? SearchParams<T> : T
386 type useActiveParams = typeof useActiveParams
387
388 /**
389 * Get a list of selected file segments for the currently selected route. Segments are not normalized, so they will be the same as the file path. e.g. /[id]?id=normal -> ["[id]"]
390 *
391 * \`useSegments\` can be typed using an abstract.
392 * Consider the following file structure, and strictly typed \`useSegments\` function:
393 *
394 * \`\`\`md
395 * - app
396 * - [user]
397 * - index.js
398 * - followers.js
399 * - settings.js
400 * \`\`\`
401 * This can be strictly typed using the following abstract:
402 *
403 * \`\`\`ts
404 * const [first, second] = useSegments<['settings'] | ['[user]'] | ['[user]', 'followers']>()
405 * \`\`\`
406 */
407 export declare function useSegments<
408 T extends AbsoluteRoute | RouteSegments<AbsoluteRoute> | RelativePathString,
409 >(): T extends AbsoluteRoute ? RouteSegments<T> : T extends string ? string[] : T
410 type useSegments = typeof useSegments
411}
412
413// TEMP
414export namespace One {
415 export type Route<Path> = OneRouter.Route<Path>
416}
417
\No newline at end of file