@bromscandium/router 1.0.0

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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 bromscandium
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,91 @@
1
+ # @bromscandium/router
2
+
3
+ Client-side router for BromiumJS with file-based routing support.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @bromscandium/router
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ```tsx
14
+ import { createRouter, Link, RouterView, useParams } from '@bromscandium/router';
15
+
16
+ const router = createRouter({
17
+ routes: [
18
+ { path: '/', component: Home },
19
+ { path: '/users/:id', component: UserProfile },
20
+ ],
21
+ });
22
+
23
+ function App() {
24
+ return (
25
+ <div>
26
+ <nav>
27
+ <Link to="/">Home</Link>
28
+ <Link to="/users/123">User</Link>
29
+ </nav>
30
+ <RouterView />
31
+ </div>
32
+ );
33
+ }
34
+
35
+ function UserProfile() {
36
+ const params = useParams();
37
+ return <div>User ID: {params.value.id}</div>;
38
+ }
39
+ ```
40
+
41
+ ### Router Hooks
42
+
43
+ ```tsx
44
+ import { useRouter, useRoute, useParams, useQuery, useNavigate } from '@bromscandium/router';
45
+
46
+ function Component() {
47
+ const router = useRouter();
48
+ const route = useRoute();
49
+ const params = useParams();
50
+ const query = useQuery();
51
+ const nav = useNavigate();
52
+
53
+ const goHome = () => nav.push('/');
54
+ const goBack = () => nav.back();
55
+
56
+ return <button onClick={goHome}>Home</button>;
57
+ }
58
+ ```
59
+
60
+ ### Link Component
61
+
62
+ ```tsx
63
+ <Link to="/about">About</Link>
64
+
65
+ <Link
66
+ to="/about"
67
+ activeClassName="active"
68
+ exactActiveClassName="exact-active"
69
+ >
70
+ About
71
+ </Link>
72
+ ```
73
+
74
+ ## API
75
+
76
+ | Export | Description |
77
+ |--------|-------------|
78
+ | `createRouter(options)` | Create router instance |
79
+ | `Link` | Navigation link component |
80
+ | `RouterView` | Route outlet component |
81
+ | `Navigate` | Programmatic navigation component |
82
+ | `Redirect` | Redirect component |
83
+ | `useRouter()` | Access router instance |
84
+ | `useRoute()` | Access current route (Ref) |
85
+ | `useParams()` | Access route parameters (ComputedRef) |
86
+ | `useQuery()` | Access query parameters (ComputedRef) |
87
+ | `useNavigate()` | Get navigation methods |
88
+
89
+ ## License
90
+
91
+ MIT
@@ -0,0 +1,129 @@
1
+ /**
2
+ * Router components: Link, RouterView, Redirect, and Navigate.
3
+ * @module
4
+ */
5
+ import { VNode } from '@bromscandium/runtime';
6
+ import { NavigationTarget } from './router.js';
7
+ /**
8
+ * Props for the Link component.
9
+ */
10
+ interface LinkProps {
11
+ /** The target path or navigation object */
12
+ to: string | NavigationTarget;
13
+ /** Child elements to render inside the link */
14
+ children?: any;
15
+ /** CSS class name */
16
+ className?: string;
17
+ /** Class name to add when route is active (includes nested) */
18
+ activeClassName?: string;
19
+ /** Class name to add when route exactly matches */
20
+ exactActiveClassName?: string;
21
+ /** If true, replace current history entry instead of pushing */
22
+ replace?: boolean;
23
+ /** HTML target attribute (e.g., "_blank") */
24
+ target?: string;
25
+ /** HTML rel attribute for external links */
26
+ rel?: string;
27
+ /** Additional click handler */
28
+ onClick?: (e: MouseEvent) => void;
29
+ /** Additional attributes passed to the anchor element */
30
+ [key: string]: any;
31
+ }
32
+ /**
33
+ * A navigation link component that integrates with the router.
34
+ * Handles click events to perform client-side navigation.
35
+ *
36
+ * @param props - Link component props
37
+ * @returns An anchor element VNode
38
+ *
39
+ * @example
40
+ * ```tsx
41
+ * <Link to="/about">About</Link>
42
+ *
43
+ * <Link to="/dashboard" activeClassName="active">Dashboard</Link>
44
+ *
45
+ * <Link to={{ path: '/user', query: { id: '123' } }}>User</Link>
46
+ * ```
47
+ */
48
+ export declare function Link(props: LinkProps): VNode;
49
+ /**
50
+ * Props for the RouterView component.
51
+ */
52
+ interface RouterViewProps {
53
+ /** The nesting depth for nested RouterViews (usually auto-detected) */
54
+ depth?: number;
55
+ }
56
+ /**
57
+ * Renders the component for the current matched route.
58
+ * Handles lazy loading and layout wrapping automatically.
59
+ *
60
+ * @param props - RouterView component props
61
+ * @returns The rendered route component or null
62
+ *
63
+ * @example
64
+ * ```tsx
65
+ * function App() {
66
+ * return (
67
+ * <div>
68
+ * <nav>...</nav>
69
+ * <RouterView />
70
+ * </div>
71
+ * );
72
+ * }
73
+ * ```
74
+ */
75
+ export declare function RouterView(props: RouterViewProps): VNode | null;
76
+ /**
77
+ * Props for the Redirect component.
78
+ */
79
+ interface RedirectProps {
80
+ /** The target path or navigation object */
81
+ to: string | NavigationTarget;
82
+ /** If true, replace current history entry (default: true) */
83
+ replace?: boolean;
84
+ }
85
+ /**
86
+ * A component that performs a redirect when rendered.
87
+ * Useful for redirecting from one route to another.
88
+ *
89
+ * @param props - Redirect component props
90
+ * @returns null (performs navigation as side effect)
91
+ *
92
+ * @example
93
+ * ```tsx
94
+ * // In a route component
95
+ * if (!isAuthenticated) {
96
+ * return <Redirect to="/login" />;
97
+ * }
98
+ * ```
99
+ */
100
+ export declare function Redirect(props: RedirectProps): null;
101
+ /**
102
+ * Props for the Navigate component.
103
+ */
104
+ interface NavigateProps {
105
+ /** The target path or navigation object */
106
+ to: string | NavigationTarget;
107
+ /** If true, replace current history entry instead of pushing */
108
+ replace?: boolean;
109
+ }
110
+ /**
111
+ * A component that performs navigation when rendered.
112
+ * Alternative to using the router imperatively.
113
+ *
114
+ * @param props - Navigate component props
115
+ * @returns null (performs navigation as side effect)
116
+ *
117
+ * @example
118
+ * ```tsx
119
+ * function AfterSubmit({ success }) {
120
+ * if (success) {
121
+ * return <Navigate to="/success" />;
122
+ * }
123
+ * return <div>Form</div>;
124
+ * }
125
+ * ```
126
+ */
127
+ export declare function Navigate(props: NavigateProps): null;
128
+ export {};
129
+ //# sourceMappingURL=components.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"components.d.ts","sourceRoot":"","sources":["../src/components.tsx"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAM,KAAK,EAAC,MAAM,uBAAuB,CAAC;AAEjD,OAAO,EAAC,gBAAgB,EAAC,MAAM,aAAa,CAAC;AAE7C;;GAEG;AACH,UAAU,SAAS;IACjB,2CAA2C;IAC3C,EAAE,EAAE,MAAM,GAAG,gBAAgB,CAAC;IAC9B,+CAA+C;IAC/C,QAAQ,CAAC,EAAE,GAAG,CAAC;IACf,qBAAqB;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,+DAA+D;IAC/D,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,mDAAmD;IACnD,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,gEAAgE;IAChE,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,6CAA6C;IAC7C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,4CAA4C;IAC5C,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,+BAA+B;IAC/B,OAAO,CAAC,EAAE,CAAC,CAAC,EAAE,UAAU,KAAK,IAAI,CAAC;IAClC,yDAAyD;IACzD,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;CACpB;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,IAAI,CAAC,KAAK,EAAE,SAAS,GAAG,KAAK,CA6D5C;AAED;;GAEG;AACH,UAAU,eAAe;IACvB,uEAAuE;IACvE,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAYD;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,eAAe,GAAG,KAAK,GAAG,IAAI,CAkC/D;AAqJD;;GAEG;AACH,UAAU,aAAa;IACrB,2CAA2C;IAC3C,EAAE,EAAE,MAAM,GAAG,gBAAgB,CAAC;IAC9B,6DAA6D;IAC7D,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,aAAa,GAAG,IAAI,CAWnD;AAED;;GAEG;AACH,UAAU,aAAa;IACrB,2CAA2C;IAC3C,EAAE,EAAE,MAAM,GAAG,gBAAgB,CAAC;IAC9B,gEAAgE;IAChE,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,aAAa,GAAG,IAAI,CAUnD"}
@@ -0,0 +1,287 @@
1
+ /**
2
+ * Router components: Link, RouterView, Redirect, and Navigate.
3
+ * @module
4
+ */
5
+ import { ref } from '@bromscandium/core';
6
+ import { jsx } from '@bromscandium/runtime';
7
+ import { useRoute, useRouter } from './hooks.js';
8
+ /**
9
+ * A navigation link component that integrates with the router.
10
+ * Handles click events to perform client-side navigation.
11
+ *
12
+ * @param props - Link component props
13
+ * @returns An anchor element VNode
14
+ *
15
+ * @example
16
+ * ```tsx
17
+ * <Link to="/about">About</Link>
18
+ *
19
+ * <Link to="/dashboard" activeClassName="active">Dashboard</Link>
20
+ *
21
+ * <Link to={{ path: '/user', query: { id: '123' } }}>User</Link>
22
+ * ```
23
+ */
24
+ export function Link(props) {
25
+ const router = useRouter();
26
+ const route = useRoute();
27
+ const { to, children, className = '', activeClassName = '', exactActiveClassName = '', replace = false, target, rel, onClick, ...rest } = props;
28
+ const href = typeof to === 'string' ? to : (to.path || '/');
29
+ const currentPath = route.value.path;
30
+ const base = router.base || '';
31
+ const comparePath = base ? href.replace(new RegExp(`^${base}`), '') || '/' : href;
32
+ const isExactActive = currentPath === comparePath;
33
+ const isActive = isExactActive || currentPath.startsWith(comparePath + '/');
34
+ let finalClassName = className;
35
+ if (isActive && activeClassName) {
36
+ finalClassName = `${finalClassName} ${activeClassName}`.trim();
37
+ }
38
+ if (isExactActive && exactActiveClassName) {
39
+ finalClassName = `${finalClassName} ${exactActiveClassName}`.trim();
40
+ }
41
+ function handleClick(e) {
42
+ onClick?.(e);
43
+ if (e.defaultPrevented)
44
+ return;
45
+ if (e.metaKey || e.altKey || e.ctrlKey || e.shiftKey)
46
+ return;
47
+ if (target)
48
+ return;
49
+ e.preventDefault();
50
+ if (replace) {
51
+ router.replace(to);
52
+ }
53
+ else {
54
+ router.push(to);
55
+ }
56
+ }
57
+ return jsx('a', {
58
+ href,
59
+ className: finalClassName || undefined,
60
+ target,
61
+ rel,
62
+ onClick: handleClick,
63
+ children,
64
+ ...rest,
65
+ });
66
+ }
67
+ let routerViewDepth = 0;
68
+ const componentCache = new Map();
69
+ const layoutCache = new Map();
70
+ const pendingLoads = new Map();
71
+ const renderVersion = ref(0);
72
+ /**
73
+ * Renders the component for the current matched route.
74
+ * Handles lazy loading and layout wrapping automatically.
75
+ *
76
+ * @param props - RouterView component props
77
+ * @returns The rendered route component or null
78
+ *
79
+ * @example
80
+ * ```tsx
81
+ * function App() {
82
+ * return (
83
+ * <div>
84
+ * <nav>...</nav>
85
+ * <RouterView />
86
+ * </div>
87
+ * );
88
+ * }
89
+ * ```
90
+ */
91
+ export function RouterView(props) {
92
+ const { depth: explicitDepth } = props;
93
+ const route = useRoute();
94
+ void renderVersion.value;
95
+ const currentDepth = explicitDepth ?? routerViewDepth;
96
+ const matched = route.value.matched[currentDepth];
97
+ if (!matched) {
98
+ return null;
99
+ }
100
+ const routePath = matched.path;
101
+ const routeConfig = matched.route;
102
+ if (routeConfig.layout) {
103
+ const layoutKey = `layout:${routePath}`;
104
+ return loadAndRenderLayoutWithPage(routeConfig.layout, layoutKey, routeConfig.component, routePath, route.value.fullPath, currentDepth);
105
+ }
106
+ return loadAndRenderComponent(routeConfig.component, routePath, route.value.fullPath, currentDepth);
107
+ }
108
+ function loadAndRenderComponent(componentModule, routePath, fullPath, currentDepth) {
109
+ if (componentCache.has(routePath)) {
110
+ const Component = componentCache.get(routePath);
111
+ routerViewDepth = currentDepth + 1;
112
+ try {
113
+ return jsx(Component, {
114
+ key: fullPath,
115
+ });
116
+ }
117
+ finally {
118
+ routerViewDepth = currentDepth;
119
+ }
120
+ }
121
+ if (typeof componentModule === 'function') {
122
+ if (!pendingLoads.has(routePath)) {
123
+ const loadPromise = componentModule();
124
+ if (loadPromise && typeof loadPromise.then === 'function') {
125
+ pendingLoads.set(routePath, loadPromise);
126
+ loadPromise.then((result) => {
127
+ const Component = result.default || result;
128
+ componentCache.set(routePath, Component);
129
+ pendingLoads.delete(routePath);
130
+ renderVersion.value++;
131
+ }).catch((error) => {
132
+ console.error('Failed to load route component:', error);
133
+ pendingLoads.delete(routePath);
134
+ });
135
+ return jsx('div', {
136
+ className: 'router-loading',
137
+ children: '',
138
+ });
139
+ }
140
+ else {
141
+ const Component = loadPromise.default || loadPromise;
142
+ componentCache.set(routePath, Component);
143
+ routerViewDepth = currentDepth + 1;
144
+ try {
145
+ return jsx(Component, {
146
+ key: fullPath,
147
+ });
148
+ }
149
+ finally {
150
+ routerViewDepth = currentDepth;
151
+ }
152
+ }
153
+ }
154
+ else {
155
+ return jsx('div', {
156
+ className: 'router-loading',
157
+ children: '',
158
+ });
159
+ }
160
+ }
161
+ else {
162
+ const Component = componentModule.default || componentModule;
163
+ componentCache.set(routePath, Component);
164
+ routerViewDepth = currentDepth + 1;
165
+ try {
166
+ return jsx(Component, {
167
+ key: fullPath,
168
+ });
169
+ }
170
+ finally {
171
+ routerViewDepth = currentDepth;
172
+ }
173
+ }
174
+ return null;
175
+ }
176
+ function loadAndRenderLayoutWithPage(layoutModule, layoutKey, pageModule, routePath, fullPath, currentDepth) {
177
+ const layoutLoaded = layoutCache.has(layoutKey);
178
+ const pageLoaded = componentCache.has(routePath);
179
+ if (layoutLoaded && pageLoaded) {
180
+ const Layout = layoutCache.get(layoutKey);
181
+ const PageComponent = componentCache.get(routePath);
182
+ routerViewDepth = currentDepth + 1;
183
+ try {
184
+ const pageElement = jsx(PageComponent, { key: fullPath });
185
+ return jsx(Layout, { children: pageElement });
186
+ }
187
+ finally {
188
+ routerViewDepth = currentDepth;
189
+ }
190
+ }
191
+ if (!layoutLoaded && typeof layoutModule === 'function' && !pendingLoads.has(layoutKey)) {
192
+ const loadPromise = layoutModule();
193
+ if (loadPromise && typeof loadPromise.then === 'function') {
194
+ pendingLoads.set(layoutKey, loadPromise);
195
+ loadPromise.then((result) => {
196
+ const Layout = result.default || result;
197
+ layoutCache.set(layoutKey, Layout);
198
+ pendingLoads.delete(layoutKey);
199
+ renderVersion.value++;
200
+ }).catch((error) => {
201
+ console.error('Failed to load layout:', error);
202
+ pendingLoads.delete(layoutKey);
203
+ });
204
+ }
205
+ else {
206
+ const Layout = loadPromise.default || loadPromise;
207
+ layoutCache.set(layoutKey, Layout);
208
+ }
209
+ }
210
+ if (!pageLoaded && typeof pageModule === 'function' && !pendingLoads.has(routePath)) {
211
+ const loadPromise = pageModule();
212
+ if (loadPromise && typeof loadPromise.then === 'function') {
213
+ pendingLoads.set(routePath, loadPromise);
214
+ loadPromise.then((result) => {
215
+ const PageComponent = result.default || result;
216
+ componentCache.set(routePath, PageComponent);
217
+ pendingLoads.delete(routePath);
218
+ renderVersion.value++;
219
+ }).catch((error) => {
220
+ console.error('Failed to load page component:', error);
221
+ pendingLoads.delete(routePath);
222
+ });
223
+ }
224
+ else {
225
+ const PageComponent = loadPromise.default || loadPromise;
226
+ componentCache.set(routePath, PageComponent);
227
+ }
228
+ }
229
+ return jsx('div', {
230
+ className: 'router-loading',
231
+ children: '',
232
+ });
233
+ }
234
+ /**
235
+ * A component that performs a redirect when rendered.
236
+ * Useful for redirecting from one route to another.
237
+ *
238
+ * @param props - Redirect component props
239
+ * @returns null (performs navigation as side effect)
240
+ *
241
+ * @example
242
+ * ```tsx
243
+ * // In a route component
244
+ * if (!isAuthenticated) {
245
+ * return <Redirect to="/login" />;
246
+ * }
247
+ * ```
248
+ */
249
+ export function Redirect(props) {
250
+ const router = useRouter();
251
+ const { to, replace = true } = props;
252
+ if (replace) {
253
+ router.replace(to);
254
+ }
255
+ else {
256
+ router.push(to);
257
+ }
258
+ return null;
259
+ }
260
+ /**
261
+ * A component that performs navigation when rendered.
262
+ * Alternative to using the router imperatively.
263
+ *
264
+ * @param props - Navigate component props
265
+ * @returns null (performs navigation as side effect)
266
+ *
267
+ * @example
268
+ * ```tsx
269
+ * function AfterSubmit({ success }) {
270
+ * if (success) {
271
+ * return <Navigate to="/success" />;
272
+ * }
273
+ * return <div>Form</div>;
274
+ * }
275
+ * ```
276
+ */
277
+ export function Navigate(props) {
278
+ const router = useRouter();
279
+ if (props.replace) {
280
+ router.replace(props.to);
281
+ }
282
+ else {
283
+ router.push(props.to);
284
+ }
285
+ return null;
286
+ }
287
+ //# sourceMappingURL=components.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"components.js","sourceRoot":"","sources":["../src/components.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,136 @@
1
+ /**
2
+ * Router hooks for components.
3
+ * @module
4
+ */
5
+ import { ComputedRef, Ref } from '@bromscandium/core';
6
+ import { Router, RouteLocation } from './router.js';
7
+ /**
8
+ * Returns the router instance.
9
+ * Must be called within a component where a router has been created.
10
+ *
11
+ * @returns The router instance
12
+ * @throws Error if called outside of router context
13
+ *
14
+ * @example
15
+ * ```ts
16
+ * function MyComponent() {
17
+ * const router = useRouter();
18
+ *
19
+ * function handleClick() {
20
+ * router.push('/dashboard');
21
+ * }
22
+ *
23
+ * return <button onClick={handleClick}>Go to Dashboard</button>;
24
+ * }
25
+ * ```
26
+ */
27
+ export declare function useRouter(): Router;
28
+ /**
29
+ * Returns a reactive reference to the current route location.
30
+ *
31
+ * @returns A ref containing the current RouteLocation
32
+ *
33
+ * @example
34
+ * ```ts
35
+ * function Breadcrumb() {
36
+ * const route = useRoute();
37
+ *
38
+ * return <span>Current path: {route.value.path}</span>;
39
+ * }
40
+ * ```
41
+ */
42
+ export declare function useRoute(): Ref<RouteLocation>;
43
+ /**
44
+ * Returns a computed ref of the current route parameters.
45
+ *
46
+ * @returns A computed ref containing route params
47
+ *
48
+ * @example
49
+ * ```ts
50
+ * // For route /users/:id
51
+ * function UserProfile() {
52
+ * const params = useParams();
53
+ *
54
+ * return <div>User ID: {params.value.id}</div>;
55
+ * }
56
+ * ```
57
+ */
58
+ export declare function useParams(): ComputedRef<Record<string, string>>;
59
+ /**
60
+ * Returns a computed ref of the current query string parameters.
61
+ *
62
+ * @returns A computed ref containing query params
63
+ *
64
+ * @example
65
+ * ```ts
66
+ * // For URL /search?q=hello
67
+ * function SearchResults() {
68
+ * const query = useQuery();
69
+ *
70
+ * return <div>Searching for: {query.value.q}</div>;
71
+ * }
72
+ * ```
73
+ */
74
+ export declare function useQuery(): ComputedRef<Record<string, string>>;
75
+ /**
76
+ * Returns a computed ref of the merged metadata from all matched routes.
77
+ *
78
+ * @returns A computed ref containing route metadata
79
+ *
80
+ * @example
81
+ * ```ts
82
+ * function PageTitle() {
83
+ * const meta = useRouteMeta();
84
+ *
85
+ * return <title>{meta.value.title || 'My App'}</title>;
86
+ * }
87
+ * ```
88
+ */
89
+ export declare function useRouteMeta(): ComputedRef<Record<string, any>>;
90
+ /**
91
+ * Returns navigation helper functions from the router.
92
+ *
93
+ * @returns An object with push, replace, back, forward, and go methods
94
+ *
95
+ * @example
96
+ * ```ts
97
+ * function Navigation() {
98
+ * const { push, back } = useNavigate();
99
+ *
100
+ * return (
101
+ * <>
102
+ * <button onClick={back}>Back</button>
103
+ * <button onClick={() => push('/home')}>Home</button>
104
+ * </>
105
+ * );
106
+ * }
107
+ * ```
108
+ */
109
+ export declare function useNavigate(): {
110
+ push: (to: string | import("./router.js").NavigationTarget) => Promise<void>;
111
+ replace: (to: string | import("./router.js").NavigationTarget) => Promise<void>;
112
+ back: () => void;
113
+ forward: () => void;
114
+ go: (delta: number) => void;
115
+ };
116
+ /**
117
+ * Returns a computed boolean indicating if the current route matches a path.
118
+ *
119
+ * @param path - A string path or RegExp to match against
120
+ * @returns A computed ref that is true when the route matches
121
+ *
122
+ * @example
123
+ * ```ts
124
+ * function NavLink({ to, children }) {
125
+ * const isActive = useRouteMatch(to);
126
+ *
127
+ * return (
128
+ * <a href={to} className={isActive.value ? 'active' : ''}>
129
+ * {children}
130
+ * </a>
131
+ * );
132
+ * }
133
+ * ```
134
+ */
135
+ export declare function useRouteMatch(path: string | RegExp): ComputedRef<boolean>;
136
+ //# sourceMappingURL=hooks.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hooks.d.ts","sourceRoot":"","sources":["../src/hooks.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAY,WAAW,EAAE,GAAG,EAAE,MAAM,oBAAoB,CAAC;AAChE,OAAO,EAAa,MAAM,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE/D;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,SAAS,IAAI,MAAM,CASlC;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,QAAQ,IAAI,GAAG,CAAC,aAAa,CAAC,CAG7C;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,SAAS,IAAI,WAAW,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAG/D;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,QAAQ,IAAI,WAAW,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAG9D;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,YAAY,IAAI,WAAW,CAAC,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,CAG/D;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,WAAW;;;;;;EAU1B;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,WAAW,CAAC,OAAO,CAAC,CAYzE"}