|
1 | 1 | import React, { forwardRef } from "react"; |
2 | 2 | import { join, mergeUrlEntities } from "../utils/utils"; |
3 | 3 | import { useBasename, useHref, useNavigation } from "../brouther/brouther"; |
4 | | -import type { Paths } from "../types/paths"; |
5 | 4 | import type { QueryString } from "../types/query-string"; |
6 | 5 | import { AnyJson } from "../types"; |
| 6 | +import { Paths } from "../types/paths"; |
| 7 | +import { TextFragment } from "../utils/text-fragment"; |
| 8 | +import { QueryStringMapper } from "../utils/mappers"; |
7 | 9 |
|
8 | 10 | const isLeftClick = (e: React.MouseEvent) => e.button === 0; |
9 | 11 |
|
10 | 12 | const isMod = (event: React.MouseEvent): boolean => event.metaKey || event.altKey || event.ctrlKey || event.shiftKey; |
11 | 13 |
|
12 | 14 | type AnchorProps = React.DetailedHTMLProps<React.AnchorHTMLAttributes<HTMLAnchorElement>, HTMLAnchorElement>; |
13 | 15 |
|
14 | | -export type LinkProps<Path extends string> = AnchorProps & { |
| 16 | +type QueryAndPaths<Path extends string> = (Paths.Has<Path> extends true ? { paths: Paths.Parse<Path> } : { paths?: never }) & |
| 17 | + (QueryString.Has<Path> extends true ? { query: NonNullable<QueryString.Parse<Path>> } : { query?: never }); |
| 18 | + |
| 19 | +export type LinkProps<Path extends string> = Omit<AnchorProps, "href" | "onClick"> & { |
| 20 | + fragments?: TextFragment[]; |
15 | 21 | href: Path; |
16 | | - state?: AnyJson; |
| 22 | + onClick?: (event: Parameters<NonNullable<AnchorProps["onClick"]>>[0], pathAndQuery: QueryAndPaths<Path>) => void; |
| 23 | + parsers?: Partial<QueryStringMapper<string>>; |
17 | 24 | replace?: boolean; |
18 | | -} & (Paths.Parse<Paths.Pathname<Path>> extends null |
19 | | - ? { paths?: undefined } |
20 | | - : Omit<Path, string> extends string |
21 | | - ? { paths: Paths.Parse<Paths.Pathname<Path>> } |
22 | | - : { paths?: Paths.Parse<Paths.Pathname<Path>> }) & |
23 | | - (QueryString.Has<Path> extends false |
24 | | - ? { query?: undefined } |
25 | | - : QueryString.HasRequired<Path> extends true |
26 | | - ? { query: QueryString.Parse<Path> } |
27 | | - : { query?: QueryString.Parse<Path> }); |
| 25 | + state?: AnyJson; |
| 26 | +} & QueryAndPaths<Path>; |
28 | 27 |
|
29 | 28 | const httpRegex = /^https?:\/\//; |
30 | 29 |
|
31 | | -export const Link: <TPath extends string>(props: LinkProps<TPath>, ref: React.MutableRefObject<HTMLAnchorElement>) => JSX.Element = forwardRef( |
32 | | - <TPath extends string>({ href, state, replace = false, onClick, query, paths, ...props }: LinkProps<TPath>, ref: any) => { |
| 30 | +export const Link: <TPath extends string>(props: LinkProps<TPath>) => React.ReactElement = forwardRef( |
| 31 | + <TPath extends string>( |
| 32 | + { href, state, replace = false, onClick, parsers, query, paths, fragments, ...props }: LinkProps<TPath>, |
| 33 | + ref: React.Ref<HTMLAnchorElement> |
| 34 | + ) => { |
33 | 35 | const { push, replace: _replace } = useNavigation(); |
34 | 36 | const contextHref = useHref(); |
35 | 37 | const basename = useBasename(); |
36 | | - const _href = httpRegex.test(href) ? href : join(basename, mergeUrlEntities(href, paths, query)); |
37 | | - const _onClick: NonNullable<typeof onClick> = (event) => { |
| 38 | + const _href = httpRegex.test(href) ? href : join(basename, mergeUrlEntities(href, paths, query, parsers, fragments)); |
| 39 | + const _onClick: NonNullable<AnchorProps["onClick"]> = (event) => { |
38 | 40 | if (props.target === undefined && props.target !== "_self") event.preventDefault(); |
39 | 41 | if (_href === contextHref) return; |
40 | 42 | if (!isLeftClick(event)) return; |
41 | 43 | if (isMod(event)) return; |
42 | | - onClick?.(event); |
| 44 | + onClick?.(event, { query, paths } as QueryAndPaths<TPath>); |
43 | 45 | return replace ? _replace(_href, state) : push(_href, state); |
44 | 46 | }; |
45 | 47 | return <a {...props} href={_href} onClick={_onClick} ref={ref} />; |
|
0 commit comments