@grainular/router 2.0.0-next.2
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/LICENSE.md +9 -0
- package/README.md +15 -0
- package/dist/browser/index.global.js +3 -0
- package/dist/browser/index.global.js.map +1 -0
- package/dist/browser/index.js +3 -0
- package/dist/browser/index.js.map +23 -0
- package/dist/cjs/index.cjs +3 -0
- package/dist/cjs/index.cjs.map +1 -0
- package/dist/cjs/index.js +3 -0
- package/dist/cjs/index.js.map +23 -0
- package/dist/esm/index.js +3 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/types/index.d.cts +94 -0
- package/dist/types/index.d.ts +6 -0
- package/dist/types/lib/create-router.d.ts +19 -0
- package/dist/types/lib/directives/active.d.ts +3 -0
- package/dist/types/lib/fragments.d.ts +4 -0
- package/dist/types/lib/grains/params.d.ts +4 -0
- package/dist/types/lib/guards/guard-executor.d.ts +5 -0
- package/dist/types/lib/history/history-manager.d.ts +13 -0
- package/dist/types/lib/hooks/hooks.d.ts +13 -0
- package/dist/types/lib/hooks/resolve-hook.d.ts +2 -0
- package/dist/types/lib/navigate.d.ts +1 -0
- package/dist/types/lib/outlet.d.ts +7 -0
- package/dist/types/lib/params/get-path-param-group.d.ts +1 -0
- package/dist/types/lib/params/parameterized.d.ts +4 -0
- package/dist/types/lib/pattern-matcher.d.ts +5 -0
- package/dist/types/lib/route/redirect.d.ts +2 -0
- package/dist/types/lib/route/route.d.ts +3 -0
- package/dist/types/lib/route.d.ts +11 -0
- package/dist/types/lib/router/create-activated-route.d.ts +10 -0
- package/dist/types/lib/router/create-link-for-router.directive.d.ts +6 -0
- package/dist/types/lib/router/create-navigator.d.ts +16 -0
- package/dist/types/lib/router/create-outlet-struct.d.ts +3 -0
- package/dist/types/lib/router/create-route-matcher.d.ts +13 -0
- package/dist/types/lib/router/create-router-fragment.d.ts +7 -0
- package/dist/types/lib/router/create-router.d.ts +2 -0
- package/dist/types/lib/router/resolve-component.d.ts +3 -0
- package/dist/types/lib/transitions/prefabs.d.ts +4 -0
- package/dist/types/lib/transitions/transition.d.ts +8 -0
- package/dist/types/types/link-options.d.ts +7 -0
- package/dist/types/types/navigator-state.d.ts +3 -0
- package/dist/types/types/navigator.d.ts +2 -0
- package/dist/types/types/params.d.ts +1 -0
- package/dist/types/types/path-params.d.ts +12 -0
- package/dist/types/types/path.d.ts +1 -0
- package/dist/types/types/route-context.d.ts +22 -0
- package/dist/types/types/route-guard.d.ts +21 -0
- package/dist/types/types/route.d.ts +20 -0
- package/dist/types/types/router-state.d.ts +8 -0
- package/dist/types/types/router.d.ts +17 -0
- package/dist/types/utils/normalize-route.d.ts +2 -0
- package/package.json +49 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/lib/fragments.ts","../../src/lib/hooks/resolve-hook.ts","../../src/lib/params/get-path-param-group.ts","../../src/lib/params/parameterized.ts","../../src/lib/pattern-matcher.ts","../../src/lib/create-router.ts","../../src/lib/directives/active.ts","../../src/lib/hooks/hooks.ts","../../src/lib/navigate.ts","../../src/lib/outlet.ts","../../src/lib/transitions/transition.ts","../../src/lib/transitions/prefabs.ts"],"names":["getFragment","resolved","resolveHooks","hooks","context","handler","path","getPathParamGroup","result","_","value","parameterized","internal","read","readonly","fn","derived","createPatternMatcher","base","routes","registry","route","normalized","url","pattern","isRouterEvent","event","createRouter","params","grain","query","match","state","getURLState","setRouterState","next","matchedParams","matchedQuery","preHooks","hook","fragment","nextState","canNavigate","postHooks","current","active","cls","exact","createDirective","node","href","destination","pre","post","navigate","$outlet","router","transitionRef","currentComponent","html","name","component","target","keyframes","props","$render","transition","setup","crossFade","duration","slide","direction","axis","sign","scale","fade"],"mappings":"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","file":"index.js","sourcesContent":["import type { Route } from './route';\n\nexport const getFragment = (resolved: Awaited<ReturnType<Route['component']>>) => {\n if ('default' in resolved) {\n return resolved.default();\n }\n\n if (typeof resolved === 'function') {\n return resolved();\n }\n\n return resolved;\n};\n","import type { NavigationHook, NavigationHookContext } from './hooks';\n\nexport const resolveHooks = async (hooks: NavigationHook[], context: Omit<NavigationHookContext, 'navigate'>) => {\n for (const { handler } of hooks) {\n const result = await handler({\n ...context,\n navigate: (path: string) => {\n return navigation.navigate(path);\n },\n });\n\n // Bail if a hook returns false explicitly\n if (result === false) return false;\n }\n\n return true;\n};\n","export const getPathParamGroup = (result: URLPatternResult | null) => {\n return Object.fromEntries(\n Object.entries(result?.pathname?.groups ?? {}).filter(([_, value]) => {\n return value !== undefined;\n }),\n ) as Record<string, string>;\n};\n","import { derived, type Grain, readonly } from '@grainular/grains';\n\nexport const parameterized = (internal: Grain<Record<string, string>>) => {\n const read = readonly(internal);\n\n return Object.assign(read, {\n select: <K extends string>(fn: (params: Record<K, string>) => string) => {\n return derived(internal, fn as (params: Record<string, string>) => string);\n },\n });\n};\n","import type { Route } from './route';\n\nexport const createPatternMatcher = (base: string, routes: Route[]) => {\n const registry = new Map<URLPattern, Route>(\n routes.map((route) => {\n const normalized = new URL(route.path, `http://localhost${base}`);\n return [new URLPattern({ pathname: normalized.pathname }), route];\n }),\n );\n\n return {\n registry,\n match: (url: URL) => {\n return [...registry.entries()].find(([pattern]) => pattern.test(url)) ?? null;\n },\n };\n};\n","import { grain, readonly } from '@grainular/grains';\nimport type { ComponentFragment } from '@grainular/nord';\nimport { getFragment } from './fragments';\nimport { resolveHooks } from './hooks/resolve-hook';\nimport { getPathParamGroup } from './params/get-path-param-group';\nimport { parameterized } from './params/parameterized';\nimport { createPatternMatcher } from './pattern-matcher';\nimport type { Route } from './route';\n\ntype RouterStateSnapshot = {\n path: string | null;\n resolved: string | null;\n component: ComponentFragment | null;\n route: Route | null;\n};\n\nconst isRouterEvent = (event: NavigateEvent) => {\n return event.canIntercept && !event.hashChange && !event.downloadRequest && !event.formData;\n};\n\nexport const createRouter = (base: string, routes: Route[]) => {\n const params = grain({});\n const query = grain<Record<string, string>>({});\n\n const { match, registry } = createPatternMatcher(base, routes);\n const state = grain<RouterStateSnapshot>({ resolved: null, component: null, path: null, route: null });\n\n const getURLState = (url: URL) => {\n const [pattern, route] = match(url) ?? [];\n if (!pattern || !route) return null;\n\n return {\n params: getPathParamGroup(pattern.exec(url)),\n query: Object.fromEntries(url.searchParams),\n resolved: pattern.pathname,\n path: url.pathname,\n pattern: pattern,\n route: route,\n url: url,\n };\n };\n\n const setRouterState = async (next: NonNullable<ReturnType<typeof getURLState>>) => {\n const { pattern, route, params: matchedParams, query: matchedQuery, url } = next;\n\n // Run pre hooks\n const preHooks = (route.use ?? []).filter((hook) => hook.run === 'pre');\n const canNavigate = await resolveHooks(preHooks, next);\n\n if (!canNavigate) return;\n\n const fragment = getFragment(await route.component());\n params.set(matchedParams);\n query.set(matchedQuery);\n state.update(() => {\n return {\n route,\n component: fragment,\n resolved: pattern.pathname,\n path: url.pathname,\n };\n });\n };\n\n navigation.addEventListener('navigate', (event) => {\n if (!isRouterEvent(event)) return;\n const url = new URL(event.destination.url);\n\n const nextState = getURLState(url);\n if (!nextState) return;\n let canNavigate = true;\n\n event.intercept({\n scroll: 'after-transition',\n precommitHandler: async () => {\n // Run all post hooks of the previous route\n const postHooks = (state().route?.use ?? []).filter((hook) => hook.run === 'post');\n canNavigate = await resolveHooks(postHooks, { ...state(), params: params(), query: query() });\n\n // Run pre hooks\n const preHooks = (nextState.route.use ?? []).filter((hook) => hook.run === 'pre');\n canNavigate = await resolveHooks(preHooks, nextState);\n },\n handler: async () => {\n // Cancel navigation based on hook result\n if (!canNavigate) return;\n\n // update router state & post hooks\n const { pattern, route, params: matchedParams, query: matchedQuery } = nextState;\n const fragment = getFragment(await route.component());\n params.set(matchedParams);\n query.set(matchedQuery);\n state.update(() => {\n return {\n route,\n component: fragment,\n resolved: pattern.pathname,\n path: url.pathname,\n };\n });\n },\n });\n });\n\n // Get the initial route set and set the routerState\n const current = getURLState(new URL(navigation.currentEntry?.url ?? ''));\n if (current) setRouterState(current);\n\n return {\n params: parameterized(params),\n query: readonly(query),\n state: readonly(state),\n match,\n registry,\n base,\n };\n};\n\nexport type Router = Omit<ReturnType<typeof createRouter>, 'registry' | 'match' | 'params' | 'query'>;\n","import { createDirective } from '@grainular/nord';\n\nexport const active = (cls: string, { exact }: { exact: boolean } = { exact: true }) => {\n return createDirective((node) => {\n const handler = () => {\n const href = node.getAttribute('href');\n const destination = navigation.currentEntry?.url;\n\n // Should both be not possible, but alas.\n if (!destination || !href) return;\n const url = new URL(destination);\n node.classList.toggle(cls, exact ? url.pathname === href : url.pathname.startsWith(href));\n };\n\n navigation.addEventListener('navigatesuccess', handler);\n return () => navigation.removeEventListener('navigatesuccess', handler);\n });\n};\n","export type NavigationHookContext = {\n path: string | null;\n resolved: string | null;\n params: Record<string, string>;\n query: Record<string, string>;\n navigate: (path: string) => void;\n};\nexport type NavigationHook = {\n run: 'pre' | 'post';\n handler: (ctx: NavigationHookContext) => Promise<boolean | void>;\n};\n\nexport const pre = (handler: (ctx: NavigationHookContext) => Promise<boolean | void>): NavigationHook => {\n return { run: 'pre' as const, handler };\n};\n\nexport const post = (handler: (ctx: NavigationHookContext) => Promise<boolean | void>): NavigationHook => {\n return { run: 'post' as const, handler };\n};\n","export const navigate = (path: string) => navigation.navigate(path);\n","import { grain } from '@grainular/grains';\nimport { $render, type ComponentFragment, html, type Ref } from '@grainular/nord';\nimport type { Router } from './create-router';\n\ntype OutletConfig = {\n for: Router;\n transitionElement?: Ref<HTMLElement>; // user provides the element to transition\n};\n\nexport const $outlet = ({ for: router, transitionElement: transitionRef }: OutletConfig) => {\n const currentComponent = grain<ComponentFragment>(html``);\n const name = `outlet-${router.base.replace(/[^a-zA-Z0-9-_]/g, '') || 'root'}`;\n\n router.state.subscribe((state) => {\n const { component, route } = state;\n const next = component ?? html``;\n if (!route?.transition) return currentComponent.set(next);\n\n // Apply view-transition-name to user's element or fall back to documentElement\n const target = transitionRef?.current ?? document.documentElement;\n target.style.viewTransitionName = name;\n\n const viewTransition = document.startViewTransition(() => currentComponent.set(next));\n viewTransition.ready.then(() => {\n const animations = [route.transition ?? []].flat();\n animations.forEach(({ keyframes, ...props }) => {\n document.documentElement.animate(keyframes, {\n pseudoElement: props.element?.replace('(root)', `(${name})`)?.replace('(outlet)', `(${name})`),\n ...props,\n });\n });\n });\n });\n\n // No wrapper div at all — just $render directly\n return $render(currentComponent);\n};\n","export type Transition = {\n keyframes: Keyframe[];\n duration: number;\n delay?: number;\n easing?: string;\n element?: '::view-transition-new(root)' | '::view-transition-old(root)' | string;\n};\n\nexport const transition = (setup: Transition): Required<Transition> => {\n return {\n delay: 0,\n easing: 'linear',\n element: '::view-transition-new(root)',\n ...setup,\n };\n};\n","import { transition } from './transition';\n\nexport const crossFade = (duration: number) => {\n return [\n transition({\n keyframes: [{ opacity: 0 }, { opacity: 1 }],\n element: '::view-transition-new(root)',\n duration,\n }),\n transition({\n keyframes: [{ opacity: 1 }, { opacity: 0 }],\n element: '::view-transition-old(root)',\n duration,\n }),\n ];\n};\n\n// slide in from a direction\nexport const slide = (duration: number, direction: 'left' | 'right' | 'up' | 'down' = 'right') => {\n const axis = direction === 'left' || direction === 'right' ? 'X' : 'Y';\n const sign = direction === 'right' || direction === 'down' ? '' : '-';\n\n return [\n transition({\n keyframes: [{ transform: `translate${axis}(${sign}100%)` }, { transform: `translate${axis}(0)` }],\n element: '::view-transition-new(root)',\n duration,\n }),\n transition({\n keyframes: [\n { transform: `translate${axis}(0)` },\n { transform: `translate${axis}(${sign === '' ? '-' : ''}100%)` },\n ],\n element: '::view-transition-old(root)',\n duration,\n }),\n ];\n};\n\n// scale up, good for modals/detail views\nexport const scale = (duration: number) => {\n return [\n transition({\n keyframes: [\n { transform: 'scale(0.95)', opacity: 0 },\n { transform: 'scale(1)', opacity: 1 },\n ],\n element: '::view-transition-new(root)',\n duration,\n }),\n transition({\n keyframes: [\n { transform: 'scale(1)', opacity: 1 },\n { transform: 'scale(0.95)', opacity: 0 },\n ],\n element: '::view-transition-old(root)',\n duration,\n }),\n ];\n};\n\n// just fade the new in, old stays — subtle\nexport const fade = (duration: number) => {\n return [\n transition({\n keyframes: [{ opacity: 0 }, { opacity: 1 }],\n element: '::view-transition-new(root)',\n duration,\n }),\n ];\n};\n"]}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import * as _grainular_grains from '@grainular/grains';
|
|
2
|
+
import { ComponentFragment, Ref } from '@grainular/nord';
|
|
3
|
+
|
|
4
|
+
type NavigationHookContext = {
|
|
5
|
+
path: string | null;
|
|
6
|
+
resolved: string | null;
|
|
7
|
+
params: Record<string, string>;
|
|
8
|
+
query: Record<string, string>;
|
|
9
|
+
navigate: (path: string) => void;
|
|
10
|
+
};
|
|
11
|
+
type NavigationHook = {
|
|
12
|
+
run: 'pre' | 'post';
|
|
13
|
+
handler: (ctx: NavigationHookContext) => Promise<boolean | void>;
|
|
14
|
+
};
|
|
15
|
+
declare const pre: (handler: (ctx: NavigationHookContext) => Promise<boolean | void>) => NavigationHook;
|
|
16
|
+
declare const post: (handler: (ctx: NavigationHookContext) => Promise<boolean | void>) => NavigationHook;
|
|
17
|
+
|
|
18
|
+
type Transition = {
|
|
19
|
+
keyframes: Keyframe[];
|
|
20
|
+
duration: number;
|
|
21
|
+
delay?: number;
|
|
22
|
+
easing?: string;
|
|
23
|
+
element?: '::view-transition-new(root)' | '::view-transition-old(root)' | string;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
type Route = {
|
|
27
|
+
path: string;
|
|
28
|
+
component: () => Promise<() => ComponentFragment> | Promise<{
|
|
29
|
+
default: () => ComponentFragment;
|
|
30
|
+
}> | ComponentFragment;
|
|
31
|
+
use?: NavigationHook[];
|
|
32
|
+
transition?: Transition | Transition[];
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
type RouterStateSnapshot = {
|
|
36
|
+
path: string | null;
|
|
37
|
+
resolved: string | null;
|
|
38
|
+
component: ComponentFragment | null;
|
|
39
|
+
route: Route | null;
|
|
40
|
+
};
|
|
41
|
+
declare const createRouter: (base: string, routes: Route[]) => {
|
|
42
|
+
params: _grainular_grains.Grain<Record<string, string>> & {
|
|
43
|
+
select: <K extends string>(fn: (params: Record<K, string>) => string) => _grainular_grains.Grain<string>;
|
|
44
|
+
};
|
|
45
|
+
query: _grainular_grains.Grain<Record<string, string>>;
|
|
46
|
+
state: _grainular_grains.Grain<RouterStateSnapshot>;
|
|
47
|
+
match: (url: URL) => [URLPattern, Route] | null;
|
|
48
|
+
registry: Map<URLPattern, Route>;
|
|
49
|
+
base: string;
|
|
50
|
+
};
|
|
51
|
+
type Router = Omit<ReturnType<typeof createRouter>, 'registry' | 'match' | 'params' | 'query'>;
|
|
52
|
+
|
|
53
|
+
declare const active: (cls: string, { exact }?: {
|
|
54
|
+
exact: boolean;
|
|
55
|
+
}) => {
|
|
56
|
+
readonly fragmentId: ReturnType<() => {
|
|
57
|
+
fragmentId: boolean;
|
|
58
|
+
create: (idx: string) => void;
|
|
59
|
+
get: () => string;
|
|
60
|
+
}>;
|
|
61
|
+
resolve: () => string;
|
|
62
|
+
render: () => string;
|
|
63
|
+
hydrate: (target: Node, def?: {
|
|
64
|
+
binding?: (value: unknown) => void;
|
|
65
|
+
scope?: string;
|
|
66
|
+
}) => void;
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
declare const navigate: (path: string) => NavigationResult;
|
|
70
|
+
|
|
71
|
+
type OutletConfig = {
|
|
72
|
+
for: Router;
|
|
73
|
+
transitionElement?: Ref<HTMLElement>;
|
|
74
|
+
};
|
|
75
|
+
declare const $outlet: ({ for: router, transitionElement: transitionRef }: OutletConfig) => {
|
|
76
|
+
readonly fragmentId: ReturnType<() => {
|
|
77
|
+
fragmentId: boolean;
|
|
78
|
+
create: (idx: string) => void;
|
|
79
|
+
get: () => string;
|
|
80
|
+
}>;
|
|
81
|
+
resolve: () => string;
|
|
82
|
+
render: () => string;
|
|
83
|
+
hydrate: (target: Node, def?: {
|
|
84
|
+
binding?: (value: unknown) => void;
|
|
85
|
+
scope?: string;
|
|
86
|
+
}) => void;
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
declare const crossFade: (duration: number) => Required<Transition>[];
|
|
90
|
+
declare const slide: (duration: number, direction?: "left" | "right" | "up" | "down") => Required<Transition>[];
|
|
91
|
+
declare const scale: (duration: number) => Required<Transition>[];
|
|
92
|
+
declare const fade: (duration: number) => Required<Transition>[];
|
|
93
|
+
|
|
94
|
+
export { $outlet, type NavigationHook, type NavigationHookContext, active, createRouter, crossFade, fade, navigate, post, pre, scale, slide };
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { createRouter } from './lib/create-router';
|
|
2
|
+
export { active } from './lib/directives/active';
|
|
3
|
+
export { post, pre, type NavigationHook, type NavigationHookContext } from './lib/hooks/hooks';
|
|
4
|
+
export { navigate } from './lib/navigate';
|
|
5
|
+
export { $outlet } from './lib/outlet';
|
|
6
|
+
export { crossFade, fade, scale, slide } from './lib/transitions/prefabs';
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { ComponentFragment } from '@grainular/nord';
|
|
2
|
+
import type { Route } from './route';
|
|
3
|
+
type RouterStateSnapshot = {
|
|
4
|
+
path: string | null;
|
|
5
|
+
resolved: string | null;
|
|
6
|
+
component: ComponentFragment | null;
|
|
7
|
+
route: Route | null;
|
|
8
|
+
};
|
|
9
|
+
export declare const createRouter: (base: string, routes: Route[]) => {
|
|
10
|
+
params: import("@grainular/grains").Grain<Record<string, string>> & {
|
|
11
|
+
select: <K extends string>(fn: (params: Record<K, string>) => string) => import("@grainular/grains").Grain<string>;
|
|
12
|
+
};
|
|
13
|
+
query: import("@grainular/grains").Grain<Record<string, string>>;
|
|
14
|
+
state: import("@grainular/grains").Grain<RouterStateSnapshot>;
|
|
15
|
+
match: (url: URL) => any;
|
|
16
|
+
registry: Map<URLPattern, Route>;
|
|
17
|
+
};
|
|
18
|
+
export type Router = Omit<ReturnType<typeof createRouter>, 'registry' | 'match' | 'params' | 'query'>;
|
|
19
|
+
export {};
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { ComponentFragment } from '@grainular/nord';
|
|
2
|
+
import type { Route } from './route';
|
|
3
|
+
export declare const fragmentCache: Map<URLPattern, ComponentFragment>;
|
|
4
|
+
export declare const getFragment: (resolved: Awaited<ReturnType<Route["component"]>>) => ComponentFragment;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { Route } from '../../types/route';
|
|
2
|
+
import type { GuardContext, RouteGuard } from '../../types/route-guard';
|
|
3
|
+
export declare const executeGuards: (guards: RouteGuard[], ctx: GuardContext) => Promise<boolean>;
|
|
4
|
+
export declare const canDeactivateRoute: (route: Route | null, ctx: GuardContext) => Promise<boolean>;
|
|
5
|
+
export declare const canActivateRoute: (route: Route, ctx: GuardContext) => Promise<boolean>;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { Params } from '../../types/params';
|
|
2
|
+
export type HistoryState = {
|
|
3
|
+
params: Params;
|
|
4
|
+
search: Params;
|
|
5
|
+
serialized: string;
|
|
6
|
+
route: string;
|
|
7
|
+
[key: string]: unknown;
|
|
8
|
+
};
|
|
9
|
+
export declare const createHistoryManager: () => {
|
|
10
|
+
push: (path: string, state: HistoryState) => void;
|
|
11
|
+
onPopState: (handler: (route: string) => void) => () => void;
|
|
12
|
+
};
|
|
13
|
+
export type HistoryManager = ReturnType<typeof createHistoryManager>;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export type NavigationHookContext = {
|
|
2
|
+
path: string | null;
|
|
3
|
+
resolved: string | null;
|
|
4
|
+
params: Record<string, string>;
|
|
5
|
+
query: Record<string, string>;
|
|
6
|
+
navigate: (path: string) => void;
|
|
7
|
+
};
|
|
8
|
+
export type NavigationHook = {
|
|
9
|
+
run: 'pre' | 'post';
|
|
10
|
+
handler: (ctx: NavigationHookContext) => Promise<boolean | void>;
|
|
11
|
+
};
|
|
12
|
+
export declare const pre: (handler: (ctx: NavigationHookContext) => Promise<boolean | void>) => NavigationHook;
|
|
13
|
+
export declare const post: (handler: (ctx: NavigationHookContext) => Promise<boolean | void>) => NavigationHook;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const navigate: any;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const getPathParamGroup: (result: URLPatternResult | null) => Record<string, string>;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { ComponentFragment } from '@grainular/nord';
|
|
2
|
+
import type { NavigationHook } from './hooks/hooks';
|
|
3
|
+
import type { Transition } from './transitions/transition';
|
|
4
|
+
export type Route = {
|
|
5
|
+
path: string;
|
|
6
|
+
component: () => Promise<() => ComponentFragment> | Promise<{
|
|
7
|
+
default: () => ComponentFragment;
|
|
8
|
+
}> | ComponentFragment;
|
|
9
|
+
use?: NavigationHook[];
|
|
10
|
+
transition?: Transition | Transition[];
|
|
11
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type Grain } from '@grainular/grains';
|
|
2
|
+
import type { Silo } from '@grainular/silo';
|
|
3
|
+
import type { Params } from '../../types/params';
|
|
4
|
+
import type { RouterState } from '../../types/router-state';
|
|
5
|
+
export type ActivatedRoute = {
|
|
6
|
+
path: Grain<string>;
|
|
7
|
+
params: Grain<Params>;
|
|
8
|
+
query: Grain<Params>;
|
|
9
|
+
};
|
|
10
|
+
export declare const createActivatedRoute: (state: Silo<RouterState>) => ActivatedRoute;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { Grain } from '@grainular/grains';
|
|
2
|
+
import { type Fragment } from '@grainular/nord';
|
|
3
|
+
import type { LinkOptions } from '../../types/link-options';
|
|
4
|
+
import type { Navigator } from '../../types/navigator';
|
|
5
|
+
import type { Route } from '../../types/route';
|
|
6
|
+
export declare const createLinkForRouter: (navigate: Navigator, matched: Grain<[string, Route | null]>) => (options?: LinkOptions) => Fragment;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { Silo } from '@grainular/silo';
|
|
2
|
+
import type { Navigator } from '../../types/navigator';
|
|
3
|
+
import type { RouterState } from '../../types/router-state';
|
|
4
|
+
import type { HistoryManager } from '../history/history-manager';
|
|
5
|
+
import type { RouteMatcher } from './create-route-matcher';
|
|
6
|
+
type CreateNavigatorOptions = {
|
|
7
|
+
match: RouteMatcher['match'];
|
|
8
|
+
state: Silo<RouterState>;
|
|
9
|
+
history: HistoryManager;
|
|
10
|
+
};
|
|
11
|
+
type NavigatorResult = {
|
|
12
|
+
navigate: Navigator;
|
|
13
|
+
syncToRoute: (path: string) => Promise<void>;
|
|
14
|
+
};
|
|
15
|
+
export declare const createNavigator: ({ match, state, history }: CreateNavigatorOptions) => NavigatorResult;
|
|
16
|
+
export {};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { Params } from '../../types/params';
|
|
2
|
+
import type { Route } from '../../types/route';
|
|
3
|
+
type MatchResult = {
|
|
4
|
+
route: Route;
|
|
5
|
+
params: Params;
|
|
6
|
+
};
|
|
7
|
+
export type RouteMatcher = {
|
|
8
|
+
match: (route: string) => MatchResult;
|
|
9
|
+
};
|
|
10
|
+
export declare const createRouteMatcher: (routes: Route[]) => {
|
|
11
|
+
readonly match: (route: string) => MatchResult;
|
|
12
|
+
};
|
|
13
|
+
export {};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { Grain } from '@grainular/grains';
|
|
2
|
+
import type { ComponentFragment, Fragment } from '@grainular/nord';
|
|
3
|
+
/**
|
|
4
|
+
* Creates a fragment that dynamically renders ComponentFragments
|
|
5
|
+
* from a grain, properly hydrating and cleaning up on each change
|
|
6
|
+
*/
|
|
7
|
+
export declare const createRouterFragment: (fragmentGrain: Grain<ComponentFragment>) => Fragment;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export declare const crossFade: (duration: number) => Required<import("./transition").Transition>[];
|
|
2
|
+
export declare const slide: (duration: number, direction?: "left" | "right" | "up" | "down") => Required<import("./transition").Transition>[];
|
|
3
|
+
export declare const scale: (duration: number) => Required<import("./transition").Transition>[];
|
|
4
|
+
export declare const fade: (duration: number) => Required<import("./transition").Transition>[];
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export type Transition = {
|
|
2
|
+
keyframes: Keyframe[];
|
|
3
|
+
duration: number;
|
|
4
|
+
delay?: number;
|
|
5
|
+
easing?: string;
|
|
6
|
+
element?: '::view-transition-new(root)' | '::view-transition-old(root)' | string;
|
|
7
|
+
};
|
|
8
|
+
export declare const transition: (setup: Transition) => Required<Transition>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export type Params = Record<string, string>;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Extract parameter names from a path string
|
|
3
|
+
* @example ExtractParams<'/user/:id/post/:postId'> = 'id' | 'postId'
|
|
4
|
+
*/
|
|
5
|
+
export type ExtractParams<Path extends string> = Path extends `${infer _Start}:${infer Param}/${infer Rest}` ? Param | ExtractParams<`/${Rest}`> : Path extends `${infer _Start}:${infer Param}` ? Param : never;
|
|
6
|
+
/**
|
|
7
|
+
* Convert extracted param names to a typed object
|
|
8
|
+
* @example ParamsObject<'/user/:id/post/:postId'> = { id: string; postId: string }
|
|
9
|
+
*/
|
|
10
|
+
export type ParamsObject<Path extends string> = ExtractParams<Path> extends never ? Record<string, never> : {
|
|
11
|
+
[K in ExtractParams<Path>]: string;
|
|
12
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export type Path = string | [string, ...(string | boolean | number)[]];
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { ComponentFragment, Fragment } from '@grainular/nord';
|
|
2
|
+
import type { Silo } from '@grainular/silo';
|
|
3
|
+
import type { Params } from './params';
|
|
4
|
+
import type { ParamsObject } from './path-params';
|
|
5
|
+
import type { RouteGuard } from './route-guard';
|
|
6
|
+
export type RouteContext<Path extends string> = {
|
|
7
|
+
params: Silo<ParamsObject<Path>>;
|
|
8
|
+
query: Silo<Params>;
|
|
9
|
+
navigate: (path: string, init?: {
|
|
10
|
+
search?: Record<string, string>;
|
|
11
|
+
}) => Promise<void>;
|
|
12
|
+
link: (options?: {
|
|
13
|
+
activeClass?: string;
|
|
14
|
+
matchMode?: 'exact' | 'prefix';
|
|
15
|
+
}) => Fragment;
|
|
16
|
+
};
|
|
17
|
+
export type RouteComponent<Path extends string> = (ctx: RouteContext<Path>) => ComponentFragment;
|
|
18
|
+
export type RouteDefinition<Path extends string = string> = {
|
|
19
|
+
component: RouteComponent<Path>;
|
|
20
|
+
guards: RouteGuard[];
|
|
21
|
+
path: Path;
|
|
22
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { Params } from './params';
|
|
2
|
+
export type GuardContext = {
|
|
3
|
+
from: {
|
|
4
|
+
path: string;
|
|
5
|
+
params: Params;
|
|
6
|
+
query: Params;
|
|
7
|
+
} | null;
|
|
8
|
+
to: {
|
|
9
|
+
path: string;
|
|
10
|
+
params: Params;
|
|
11
|
+
query: Params;
|
|
12
|
+
};
|
|
13
|
+
navigate: (path: string, init?: {
|
|
14
|
+
search?: Record<string, string>;
|
|
15
|
+
}) => Promise<void>;
|
|
16
|
+
};
|
|
17
|
+
export type GuardResult = boolean | void | Promise<boolean | void>;
|
|
18
|
+
export type RouteGuard = {
|
|
19
|
+
run: 'pre' | 'post';
|
|
20
|
+
use: (ctx: GuardContext) => GuardResult;
|
|
21
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { ComponentFragment } from '@grainular/nord';
|
|
2
|
+
import type { RouteGuard } from './route-guard';
|
|
3
|
+
export type Route = {
|
|
4
|
+
path: `/${string}` | '**';
|
|
5
|
+
guards?: RouteGuard[];
|
|
6
|
+
} & ({
|
|
7
|
+
component: (() => ComponentFragment) | (() => Promise<ComponentFragment>) | (() => Promise<{
|
|
8
|
+
default: ComponentFragment;
|
|
9
|
+
}>);
|
|
10
|
+
children?: never;
|
|
11
|
+
redirect?: never;
|
|
12
|
+
} | {
|
|
13
|
+
children: Route[];
|
|
14
|
+
component?: never;
|
|
15
|
+
redirect?: never;
|
|
16
|
+
} | {
|
|
17
|
+
redirect: string;
|
|
18
|
+
component?: never;
|
|
19
|
+
children?: never;
|
|
20
|
+
});
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { ComponentFragment } from '@grainular/nord';
|
|
2
|
+
import type { Route } from './route';
|
|
3
|
+
export type RouterState = {
|
|
4
|
+
path: string;
|
|
5
|
+
route: Route | null;
|
|
6
|
+
fragment: ComponentFragment;
|
|
7
|
+
setRouterContext: (path: string, route: Route | null, fragment: ComponentFragment) => void;
|
|
8
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { Fragment } from '@grainular/nord';
|
|
2
|
+
import type { Silo } from '@grainular/silo';
|
|
3
|
+
import type { Params } from './params';
|
|
4
|
+
import type { RouteDefinition } from './route-context';
|
|
5
|
+
export type RouterConfig = Record<string, () => RouteDefinition>;
|
|
6
|
+
export type Router = {
|
|
7
|
+
navigate: (path: string, init?: {
|
|
8
|
+
search?: Record<string, string>;
|
|
9
|
+
}) => Promise<void>;
|
|
10
|
+
link: (options?: {
|
|
11
|
+
activeClass?: string;
|
|
12
|
+
matchMode?: 'exact' | 'prefix';
|
|
13
|
+
}) => Fragment;
|
|
14
|
+
$router: Fragment;
|
|
15
|
+
params: Silo<Params>;
|
|
16
|
+
query: Silo<Params>;
|
|
17
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@grainular/router",
|
|
3
|
+
"version": "2.0.0-next.2",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"packageManager": "bun@1.2.7",
|
|
6
|
+
"main": "./dist/cjs/index.js",
|
|
7
|
+
"module": "./dist/esm/index.js",
|
|
8
|
+
"types": "./dist/types/index.d.cts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/types/index.d.cts",
|
|
12
|
+
"import": "./dist/esm/index.js",
|
|
13
|
+
"require": "./dist/cjs/index.js"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"unpkg": "./dist/browser/index.js",
|
|
17
|
+
"devDependencies": {
|
|
18
|
+
"@repository/builder": "0.0.0",
|
|
19
|
+
"@repository/config": "0.0.0",
|
|
20
|
+
"@types/bun": "latest",
|
|
21
|
+
"tsup": "^8.5.1",
|
|
22
|
+
"typescript": "6.0.3"
|
|
23
|
+
},
|
|
24
|
+
"peerDependencies": {
|
|
25
|
+
"@grainular/nord": "2.0.0-next.2",
|
|
26
|
+
"@grainular/grains": "2.0.0-next.2"
|
|
27
|
+
},
|
|
28
|
+
"scripts": {
|
|
29
|
+
"dev": "bunx tsup --watch",
|
|
30
|
+
"build": "bunx tsup",
|
|
31
|
+
"prepublishOnly": "bunx tsup",
|
|
32
|
+
"test": "bun test --coverage"
|
|
33
|
+
},
|
|
34
|
+
"sideEffects": false,
|
|
35
|
+
"publishConfig": {
|
|
36
|
+
"access": "public",
|
|
37
|
+
"tag": "next"
|
|
38
|
+
},
|
|
39
|
+
"files": [
|
|
40
|
+
"dist",
|
|
41
|
+
"README.md",
|
|
42
|
+
"LICENSE"
|
|
43
|
+
],
|
|
44
|
+
"license": "MIT",
|
|
45
|
+
"repository": {
|
|
46
|
+
"type": "git",
|
|
47
|
+
"url": "git+https://github.com/grainular/nord.git"
|
|
48
|
+
}
|
|
49
|
+
}
|