@budarin/use-route 1.0.0 → 1.0.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/README.md +8 -0
- package/demo/dist/assets/index-CehTkyXl.css +1 -0
- package/demo/dist/assets/index-wDy-y7oj.js +49 -0
- package/demo/dist/index.html +13 -0
- package/demo/index.html +12 -0
- package/demo/node_modules/.bin/browserslist +21 -0
- package/demo/node_modules/.bin/tsc +21 -0
- package/demo/node_modules/.bin/tsserver +21 -0
- package/demo/node_modules/.bin/vite +21 -0
- package/demo/node_modules/.vite/deps/@budarin_use-route.js +372 -0
- package/demo/node_modules/.vite/deps/@budarin_use-route.js.map +7 -0
- package/demo/node_modules/.vite/deps/_metadata.json +52 -0
- package/demo/node_modules/.vite/deps/chunk-4BQM3FN6.js +278 -0
- package/demo/node_modules/.vite/deps/chunk-4BQM3FN6.js.map +7 -0
- package/demo/node_modules/.vite/deps/chunk-DBBEQ5LR.js +1028 -0
- package/demo/node_modules/.vite/deps/chunk-DBBEQ5LR.js.map +7 -0
- package/demo/node_modules/.vite/deps/package.json +3 -0
- package/demo/node_modules/.vite/deps/react-dom.js +5 -0
- package/demo/node_modules/.vite/deps/react-dom.js.map +7 -0
- package/demo/node_modules/.vite/deps/react-dom_client.js +20215 -0
- package/demo/node_modules/.vite/deps/react-dom_client.js.map +7 -0
- package/demo/node_modules/.vite/deps/react.js +4 -0
- package/demo/node_modules/.vite/deps/react.js.map +7 -0
- package/demo/node_modules/.vite/deps/react_jsx-dev-runtime.js +276 -0
- package/demo/node_modules/.vite/deps/react_jsx-dev-runtime.js.map +7 -0
- package/demo/node_modules/.vite/deps/react_jsx-runtime.js +287 -0
- package/demo/node_modules/.vite/deps/react_jsx-runtime.js.map +7 -0
- package/demo/package.json +23 -0
- package/demo/src/App.tsx +139 -0
- package/demo/src/components/Link.tsx +24 -0
- package/demo/src/index.css +160 -0
- package/demo/src/main.tsx +10 -0
- package/demo/src/pages/BaseDemo.tsx +39 -0
- package/demo/src/pages/CustomMatcher.tsx +45 -0
- package/demo/src/pages/History.tsx +36 -0
- package/demo/src/pages/Home.tsx +38 -0
- package/demo/src/pages/Posts.tsx +40 -0
- package/demo/src/pages/PushReplace.tsx +43 -0
- package/demo/src/pages/UserProfile.tsx +33 -0
- package/demo/src/pages/Users.tsx +24 -0
- package/demo/tsconfig.tsbuildinfo +1 -0
- package/demo/vite.config.ts +6 -0
- package/package.json +1 -1
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../node_modules/.pnpm/react@19.2.4/node_modules/react/cjs/react-jsx-runtime.development.js", "../../../../node_modules/.pnpm/react@19.2.4/node_modules/react/jsx-runtime.js"],
|
|
4
|
+
"sourcesContent": ["/**\n * @license React\n * react-jsx-runtime.development.js\n *\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * This source code is licensed under the MIT license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n\"use strict\";\n\"production\" !== process.env.NODE_ENV &&\n (function () {\n function getComponentNameFromType(type) {\n if (null == type) return null;\n if (\"function\" === typeof type)\n return type.$$typeof === REACT_CLIENT_REFERENCE\n ? null\n : type.displayName || type.name || null;\n if (\"string\" === typeof type) return type;\n switch (type) {\n case REACT_FRAGMENT_TYPE:\n return \"Fragment\";\n case REACT_PROFILER_TYPE:\n return \"Profiler\";\n case REACT_STRICT_MODE_TYPE:\n return \"StrictMode\";\n case REACT_SUSPENSE_TYPE:\n return \"Suspense\";\n case REACT_SUSPENSE_LIST_TYPE:\n return \"SuspenseList\";\n case REACT_ACTIVITY_TYPE:\n return \"Activity\";\n }\n if (\"object\" === typeof type)\n switch (\n (\"number\" === typeof type.tag &&\n console.error(\n \"Received an unexpected object in getComponentNameFromType(). This is likely a bug in React. Please file an issue.\"\n ),\n type.$$typeof)\n ) {\n case REACT_PORTAL_TYPE:\n return \"Portal\";\n case REACT_CONTEXT_TYPE:\n return type.displayName || \"Context\";\n case REACT_CONSUMER_TYPE:\n return (type._context.displayName || \"Context\") + \".Consumer\";\n case REACT_FORWARD_REF_TYPE:\n var innerType = type.render;\n type = type.displayName;\n type ||\n ((type = innerType.displayName || innerType.name || \"\"),\n (type = \"\" !== type ? \"ForwardRef(\" + type + \")\" : \"ForwardRef\"));\n return type;\n case REACT_MEMO_TYPE:\n return (\n (innerType = type.displayName || null),\n null !== innerType\n ? innerType\n : getComponentNameFromType(type.type) || \"Memo\"\n );\n case REACT_LAZY_TYPE:\n innerType = type._payload;\n type = type._init;\n try {\n return getComponentNameFromType(type(innerType));\n } catch (x) {}\n }\n return null;\n }\n function testStringCoercion(value) {\n return \"\" + value;\n }\n function checkKeyStringCoercion(value) {\n try {\n testStringCoercion(value);\n var JSCompiler_inline_result = !1;\n } catch (e) {\n JSCompiler_inline_result = !0;\n }\n if (JSCompiler_inline_result) {\n JSCompiler_inline_result = console;\n var JSCompiler_temp_const = JSCompiler_inline_result.error;\n var JSCompiler_inline_result$jscomp$0 =\n (\"function\" === typeof Symbol &&\n Symbol.toStringTag &&\n value[Symbol.toStringTag]) ||\n value.constructor.name ||\n \"Object\";\n JSCompiler_temp_const.call(\n JSCompiler_inline_result,\n \"The provided key is an unsupported type %s. This value must be coerced to a string before using it here.\",\n JSCompiler_inline_result$jscomp$0\n );\n return testStringCoercion(value);\n }\n }\n function getTaskName(type) {\n if (type === REACT_FRAGMENT_TYPE) return \"<>\";\n if (\n \"object\" === typeof type &&\n null !== type &&\n type.$$typeof === REACT_LAZY_TYPE\n )\n return \"<...>\";\n try {\n var name = getComponentNameFromType(type);\n return name ? \"<\" + name + \">\" : \"<...>\";\n } catch (x) {\n return \"<...>\";\n }\n }\n function getOwner() {\n var dispatcher = ReactSharedInternals.A;\n return null === dispatcher ? null : dispatcher.getOwner();\n }\n function UnknownOwner() {\n return Error(\"react-stack-top-frame\");\n }\n function hasValidKey(config) {\n if (hasOwnProperty.call(config, \"key\")) {\n var getter = Object.getOwnPropertyDescriptor(config, \"key\").get;\n if (getter && getter.isReactWarning) return !1;\n }\n return void 0 !== config.key;\n }\n function defineKeyPropWarningGetter(props, displayName) {\n function warnAboutAccessingKey() {\n specialPropKeyWarningShown ||\n ((specialPropKeyWarningShown = !0),\n console.error(\n \"%s: `key` is not a prop. Trying to access it will result in `undefined` being returned. If you need to access the same value within the child component, you should pass it as a different prop. (https://react.dev/link/special-props)\",\n displayName\n ));\n }\n warnAboutAccessingKey.isReactWarning = !0;\n Object.defineProperty(props, \"key\", {\n get: warnAboutAccessingKey,\n configurable: !0\n });\n }\n function elementRefGetterWithDeprecationWarning() {\n var componentName = getComponentNameFromType(this.type);\n didWarnAboutElementRef[componentName] ||\n ((didWarnAboutElementRef[componentName] = !0),\n console.error(\n \"Accessing element.ref was removed in React 19. ref is now a regular prop. It will be removed from the JSX Element type in a future release.\"\n ));\n componentName = this.props.ref;\n return void 0 !== componentName ? componentName : null;\n }\n function ReactElement(type, key, props, owner, debugStack, debugTask) {\n var refProp = props.ref;\n type = {\n $$typeof: REACT_ELEMENT_TYPE,\n type: type,\n key: key,\n props: props,\n _owner: owner\n };\n null !== (void 0 !== refProp ? refProp : null)\n ? Object.defineProperty(type, \"ref\", {\n enumerable: !1,\n get: elementRefGetterWithDeprecationWarning\n })\n : Object.defineProperty(type, \"ref\", { enumerable: !1, value: null });\n type._store = {};\n Object.defineProperty(type._store, \"validated\", {\n configurable: !1,\n enumerable: !1,\n writable: !0,\n value: 0\n });\n Object.defineProperty(type, \"_debugInfo\", {\n configurable: !1,\n enumerable: !1,\n writable: !0,\n value: null\n });\n Object.defineProperty(type, \"_debugStack\", {\n configurable: !1,\n enumerable: !1,\n writable: !0,\n value: debugStack\n });\n Object.defineProperty(type, \"_debugTask\", {\n configurable: !1,\n enumerable: !1,\n writable: !0,\n value: debugTask\n });\n Object.freeze && (Object.freeze(type.props), Object.freeze(type));\n return type;\n }\n function jsxDEVImpl(\n type,\n config,\n maybeKey,\n isStaticChildren,\n debugStack,\n debugTask\n ) {\n var children = config.children;\n if (void 0 !== children)\n if (isStaticChildren)\n if (isArrayImpl(children)) {\n for (\n isStaticChildren = 0;\n isStaticChildren < children.length;\n isStaticChildren++\n )\n validateChildKeys(children[isStaticChildren]);\n Object.freeze && Object.freeze(children);\n } else\n console.error(\n \"React.jsx: Static children should always be an array. You are likely explicitly calling React.jsxs or React.jsxDEV. Use the Babel transform instead.\"\n );\n else validateChildKeys(children);\n if (hasOwnProperty.call(config, \"key\")) {\n children = getComponentNameFromType(type);\n var keys = Object.keys(config).filter(function (k) {\n return \"key\" !== k;\n });\n isStaticChildren =\n 0 < keys.length\n ? \"{key: someKey, \" + keys.join(\": ..., \") + \": ...}\"\n : \"{key: someKey}\";\n didWarnAboutKeySpread[children + isStaticChildren] ||\n ((keys =\n 0 < keys.length ? \"{\" + keys.join(\": ..., \") + \": ...}\" : \"{}\"),\n console.error(\n 'A props object containing a \"key\" prop is being spread into JSX:\\n let props = %s;\\n <%s {...props} />\\nReact keys must be passed directly to JSX without using spread:\\n let props = %s;\\n <%s key={someKey} {...props} />',\n isStaticChildren,\n children,\n keys,\n children\n ),\n (didWarnAboutKeySpread[children + isStaticChildren] = !0));\n }\n children = null;\n void 0 !== maybeKey &&\n (checkKeyStringCoercion(maybeKey), (children = \"\" + maybeKey));\n hasValidKey(config) &&\n (checkKeyStringCoercion(config.key), (children = \"\" + config.key));\n if (\"key\" in config) {\n maybeKey = {};\n for (var propName in config)\n \"key\" !== propName && (maybeKey[propName] = config[propName]);\n } else maybeKey = config;\n children &&\n defineKeyPropWarningGetter(\n maybeKey,\n \"function\" === typeof type\n ? type.displayName || type.name || \"Unknown\"\n : type\n );\n return ReactElement(\n type,\n children,\n maybeKey,\n getOwner(),\n debugStack,\n debugTask\n );\n }\n function validateChildKeys(node) {\n isValidElement(node)\n ? node._store && (node._store.validated = 1)\n : \"object\" === typeof node &&\n null !== node &&\n node.$$typeof === REACT_LAZY_TYPE &&\n (\"fulfilled\" === node._payload.status\n ? isValidElement(node._payload.value) &&\n node._payload.value._store &&\n (node._payload.value._store.validated = 1)\n : node._store && (node._store.validated = 1));\n }\n function isValidElement(object) {\n return (\n \"object\" === typeof object &&\n null !== object &&\n object.$$typeof === REACT_ELEMENT_TYPE\n );\n }\n var React = require(\"react\"),\n REACT_ELEMENT_TYPE = Symbol.for(\"react.transitional.element\"),\n REACT_PORTAL_TYPE = Symbol.for(\"react.portal\"),\n REACT_FRAGMENT_TYPE = Symbol.for(\"react.fragment\"),\n REACT_STRICT_MODE_TYPE = Symbol.for(\"react.strict_mode\"),\n REACT_PROFILER_TYPE = Symbol.for(\"react.profiler\"),\n REACT_CONSUMER_TYPE = Symbol.for(\"react.consumer\"),\n REACT_CONTEXT_TYPE = Symbol.for(\"react.context\"),\n REACT_FORWARD_REF_TYPE = Symbol.for(\"react.forward_ref\"),\n REACT_SUSPENSE_TYPE = Symbol.for(\"react.suspense\"),\n REACT_SUSPENSE_LIST_TYPE = Symbol.for(\"react.suspense_list\"),\n REACT_MEMO_TYPE = Symbol.for(\"react.memo\"),\n REACT_LAZY_TYPE = Symbol.for(\"react.lazy\"),\n REACT_ACTIVITY_TYPE = Symbol.for(\"react.activity\"),\n REACT_CLIENT_REFERENCE = Symbol.for(\"react.client.reference\"),\n ReactSharedInternals =\n React.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE,\n hasOwnProperty = Object.prototype.hasOwnProperty,\n isArrayImpl = Array.isArray,\n createTask = console.createTask\n ? console.createTask\n : function () {\n return null;\n };\n React = {\n react_stack_bottom_frame: function (callStackForError) {\n return callStackForError();\n }\n };\n var specialPropKeyWarningShown;\n var didWarnAboutElementRef = {};\n var unknownOwnerDebugStack = React.react_stack_bottom_frame.bind(\n React,\n UnknownOwner\n )();\n var unknownOwnerDebugTask = createTask(getTaskName(UnknownOwner));\n var didWarnAboutKeySpread = {};\n exports.Fragment = REACT_FRAGMENT_TYPE;\n exports.jsx = function (type, config, maybeKey) {\n var trackActualOwner =\n 1e4 > ReactSharedInternals.recentlyCreatedOwnerStacks++;\n return jsxDEVImpl(\n type,\n config,\n maybeKey,\n !1,\n trackActualOwner\n ? Error(\"react-stack-top-frame\")\n : unknownOwnerDebugStack,\n trackActualOwner ? createTask(getTaskName(type)) : unknownOwnerDebugTask\n );\n };\n exports.jsxs = function (type, config, maybeKey) {\n var trackActualOwner =\n 1e4 > ReactSharedInternals.recentlyCreatedOwnerStacks++;\n return jsxDEVImpl(\n type,\n config,\n maybeKey,\n !0,\n trackActualOwner\n ? Error(\"react-stack-top-frame\")\n : unknownOwnerDebugStack,\n trackActualOwner ? createTask(getTaskName(type)) : unknownOwnerDebugTask\n );\n };\n })();\n", "'use strict';\n\nif (process.env.NODE_ENV === 'production') {\n module.exports = require('./cjs/react-jsx-runtime.production.js');\n} else {\n module.exports = require('./cjs/react-jsx-runtime.development.js');\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "use-route-demo",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"type": "module",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"dev": "vite",
|
|
8
|
+
"build": "tsc -b && vite build",
|
|
9
|
+
"preview": "vite preview"
|
|
10
|
+
},
|
|
11
|
+
"dependencies": {
|
|
12
|
+
"@budarin/use-route": "^1.0.1",
|
|
13
|
+
"react": "^19.0.0",
|
|
14
|
+
"react-dom": "^19.0.0"
|
|
15
|
+
},
|
|
16
|
+
"devDependencies": {
|
|
17
|
+
"@types/react": "^19.0.0",
|
|
18
|
+
"@types/react-dom": "^19.0.0",
|
|
19
|
+
"@vitejs/plugin-react": "^4.3.4",
|
|
20
|
+
"typescript": "^5.7.0",
|
|
21
|
+
"vite": "^6.0.0"
|
|
22
|
+
}
|
|
23
|
+
}
|
package/demo/src/App.tsx
ADDED
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { useRoute } from '@budarin/use-route';
|
|
2
|
+
import { Link } from './components/Link';
|
|
3
|
+
import { Home } from './pages/Home';
|
|
4
|
+
import { Users } from './pages/Users';
|
|
5
|
+
import { UserProfile } from './pages/UserProfile';
|
|
6
|
+
import { Posts } from './pages/Posts';
|
|
7
|
+
import { History } from './pages/History';
|
|
8
|
+
import { PushReplace } from './pages/PushReplace';
|
|
9
|
+
import { CustomMatcher } from './pages/CustomMatcher';
|
|
10
|
+
import { BaseDemo } from './pages/BaseDemo';
|
|
11
|
+
|
|
12
|
+
function Nav() {
|
|
13
|
+
const { pathname, searchParams, back, forward, canGoBack, canGoForward } = useRoute();
|
|
14
|
+
const base = pathname.startsWith('/base-demo') ? '/base-demo' : '';
|
|
15
|
+
const urlPath = pathname + (searchParams.toString() ? '?' + searchParams.toString() : '');
|
|
16
|
+
|
|
17
|
+
return (
|
|
18
|
+
<nav className="demo-nav">
|
|
19
|
+
<div className="demo-nav-url" title="Текущий путь (pathname + query)">
|
|
20
|
+
URL: <code>{urlPath || '/'}</code>
|
|
21
|
+
</div>
|
|
22
|
+
<div className="demo-nav-links">
|
|
23
|
+
<button type="button" onClick={() => back()} disabled={!canGoBack()} title="Назад">
|
|
24
|
+
←
|
|
25
|
+
</button>
|
|
26
|
+
<button
|
|
27
|
+
type="button"
|
|
28
|
+
onClick={() => forward()}
|
|
29
|
+
disabled={!canGoForward()}
|
|
30
|
+
title="Вперёд"
|
|
31
|
+
>
|
|
32
|
+
→
|
|
33
|
+
</button>
|
|
34
|
+
<span style={{ width: '0.5rem' }} />
|
|
35
|
+
<Link
|
|
36
|
+
to={base || '/'}
|
|
37
|
+
className={pathname === (base || '/') ? 'active' : ''}
|
|
38
|
+
title="Главная страница демо"
|
|
39
|
+
>
|
|
40
|
+
Главная
|
|
41
|
+
</Link>
|
|
42
|
+
<Link
|
|
43
|
+
to={(base || '') + '/users'}
|
|
44
|
+
className={pathname === (base || '') + '/users' ? 'active' : ''}
|
|
45
|
+
title="Параметры в URL: /users/123"
|
|
46
|
+
>
|
|
47
|
+
Пользователи
|
|
48
|
+
</Link>
|
|
49
|
+
<Link
|
|
50
|
+
to={(base || '') + '/posts'}
|
|
51
|
+
className={pathname.startsWith((base || '') + '/posts') ? 'active' : ''}
|
|
52
|
+
title="Параметры в строке запроса: ?page=2"
|
|
53
|
+
>
|
|
54
|
+
Посты
|
|
55
|
+
</Link>
|
|
56
|
+
<Link
|
|
57
|
+
to={(base || '') + '/history'}
|
|
58
|
+
className={pathname === (base || '') + '/history' ? 'active' : ''}
|
|
59
|
+
title="Назад / Вперёд по истории"
|
|
60
|
+
>
|
|
61
|
+
История
|
|
62
|
+
</Link>
|
|
63
|
+
<Link
|
|
64
|
+
to={(base || '') + '/push-replace'}
|
|
65
|
+
className={pathname.startsWith((base || '') + '/push-replace') ? 'active' : ''}
|
|
66
|
+
title="Добавить или заменить запись в истории"
|
|
67
|
+
>
|
|
68
|
+
Push/Replace
|
|
69
|
+
</Link>
|
|
70
|
+
<Link
|
|
71
|
+
to={(base || '') + '/products/books/1'}
|
|
72
|
+
className={pathname.startsWith((base || '') + '/products') ? 'active' : ''}
|
|
73
|
+
title="Свой разбор пути (PathMatcher)"
|
|
74
|
+
>
|
|
75
|
+
Товары
|
|
76
|
+
</Link>
|
|
77
|
+
{!base ? (
|
|
78
|
+
<Link
|
|
79
|
+
to="/base-demo"
|
|
80
|
+
className={pathname.startsWith('/base-demo') ? 'active' : ''}
|
|
81
|
+
title="Раздел с префиксом в URL"
|
|
82
|
+
>
|
|
83
|
+
Раздел (base)
|
|
84
|
+
</Link>
|
|
85
|
+
) : (
|
|
86
|
+
<Link to="/">Выйти из раздела</Link>
|
|
87
|
+
)}
|
|
88
|
+
</div>
|
|
89
|
+
</nav>
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function Router() {
|
|
94
|
+
const { pathname } = useRoute();
|
|
95
|
+
|
|
96
|
+
if (pathname.startsWith('/base-demo')) {
|
|
97
|
+
return <BaseDemo />;
|
|
98
|
+
}
|
|
99
|
+
if (pathname === '/') {
|
|
100
|
+
return <Home />;
|
|
101
|
+
}
|
|
102
|
+
if (pathname === '/users') {
|
|
103
|
+
return <Users />;
|
|
104
|
+
}
|
|
105
|
+
if (pathname.startsWith('/users/')) {
|
|
106
|
+
return <UserProfile />;
|
|
107
|
+
}
|
|
108
|
+
if (pathname.startsWith('/posts')) {
|
|
109
|
+
return <Posts />;
|
|
110
|
+
}
|
|
111
|
+
if (pathname === '/history') {
|
|
112
|
+
return <History />;
|
|
113
|
+
}
|
|
114
|
+
if (pathname.startsWith('/push-replace')) {
|
|
115
|
+
return <PushReplace />;
|
|
116
|
+
}
|
|
117
|
+
if (pathname.startsWith('/products/')) {
|
|
118
|
+
return <CustomMatcher />;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return (
|
|
122
|
+
<div className="demo-content">
|
|
123
|
+
<h1>404</h1>
|
|
124
|
+
<p>
|
|
125
|
+
Путь <code>{pathname}</code> не найден.
|
|
126
|
+
</p>
|
|
127
|
+
<Link to="/">На главную</Link>
|
|
128
|
+
</div>
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export function App() {
|
|
133
|
+
return (
|
|
134
|
+
<>
|
|
135
|
+
<Nav />
|
|
136
|
+
<Router />
|
|
137
|
+
</>
|
|
138
|
+
);
|
|
139
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { useRoute } from '@budarin/use-route';
|
|
2
|
+
import { useCallback, type ComponentPropsWithoutRef } from 'react';
|
|
3
|
+
|
|
4
|
+
interface LinkProps extends ComponentPropsWithoutRef<'a'> {
|
|
5
|
+
to: string;
|
|
6
|
+
replace?: boolean;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function Link({ to, replace = false, onClick, ...props }: LinkProps) {
|
|
10
|
+
const { navigate } = useRoute();
|
|
11
|
+
|
|
12
|
+
const handleClick = useCallback(
|
|
13
|
+
(e: React.MouseEvent<HTMLAnchorElement>) => {
|
|
14
|
+
onClick?.(e);
|
|
15
|
+
if (!e.defaultPrevented) {
|
|
16
|
+
e.preventDefault();
|
|
17
|
+
navigate(to, { history: replace ? 'replace' : 'push' });
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
[navigate, to, replace, onClick]
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
return <a {...props} href={to} onClick={handleClick} />;
|
|
24
|
+
}
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
*,
|
|
2
|
+
*::before,
|
|
3
|
+
*::after {
|
|
4
|
+
box-sizing: border-box;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
body {
|
|
8
|
+
margin: 0;
|
|
9
|
+
font-family:
|
|
10
|
+
system-ui,
|
|
11
|
+
-apple-system,
|
|
12
|
+
sans-serif;
|
|
13
|
+
line-height: 1.5;
|
|
14
|
+
color: #1a1a1a;
|
|
15
|
+
background: #f5f5f5;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
#root {
|
|
19
|
+
min-height: 100vh;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
a {
|
|
23
|
+
color: #0066cc;
|
|
24
|
+
text-decoration: none;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
a:hover {
|
|
28
|
+
text-decoration: underline;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
button {
|
|
32
|
+
font: inherit;
|
|
33
|
+
cursor: pointer;
|
|
34
|
+
padding: 0.4em 0.8em;
|
|
35
|
+
border: 1px solid #ccc;
|
|
36
|
+
border-radius: 4px;
|
|
37
|
+
background: #fff;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
button:hover:not(:disabled) {
|
|
41
|
+
background: #f0f0f0;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
button:disabled {
|
|
45
|
+
opacity: 0.6;
|
|
46
|
+
cursor: not-allowed;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
.demo-nav {
|
|
50
|
+
background: #fff;
|
|
51
|
+
padding: 0.75rem 1rem;
|
|
52
|
+
border-bottom: 1px solid #e0e0e0;
|
|
53
|
+
display: flex;
|
|
54
|
+
flex-direction: column;
|
|
55
|
+
gap: 0.5rem;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
.demo-nav-url {
|
|
59
|
+
font-size: 0.85rem;
|
|
60
|
+
color: #555;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
.demo-nav-url code {
|
|
64
|
+
background: #f0f0f0;
|
|
65
|
+
padding: 0.2em 0.4em;
|
|
66
|
+
border-radius: 4px;
|
|
67
|
+
word-break: break-all;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
.demo-nav-links {
|
|
71
|
+
display: flex;
|
|
72
|
+
flex-wrap: wrap;
|
|
73
|
+
align-items: center;
|
|
74
|
+
gap: 0.5rem 1rem;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
.demo-nav a,
|
|
78
|
+
.demo-nav .nav-link {
|
|
79
|
+
padding: 0.25em 0.5em;
|
|
80
|
+
border-radius: 4px;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
.demo-nav a.active,
|
|
84
|
+
.demo-nav .nav-link.active {
|
|
85
|
+
background: #e8f0fe;
|
|
86
|
+
color: #1967d2;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
.demo-content {
|
|
90
|
+
max-width: 640px;
|
|
91
|
+
margin: 0 auto;
|
|
92
|
+
padding: 1.5rem 1rem;
|
|
93
|
+
background: #fff;
|
|
94
|
+
min-height: calc(100vh - 52px);
|
|
95
|
+
border-radius: 0 0 8px 8px;
|
|
96
|
+
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
.demo-content h1 {
|
|
100
|
+
margin-top: 0;
|
|
101
|
+
font-size: 1.5rem;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
.demo-content .demo-lead {
|
|
105
|
+
color: #444;
|
|
106
|
+
margin-bottom: 1rem;
|
|
107
|
+
padding: 0.75rem;
|
|
108
|
+
background: #f8f9fa;
|
|
109
|
+
border-radius: 6px;
|
|
110
|
+
border-left: 3px solid #0066cc;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
.demo-content h2 {
|
|
114
|
+
font-size: 1.1rem;
|
|
115
|
+
margin: 1rem 0 0.5rem;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
.demo-content code {
|
|
119
|
+
background: #f0f0f0;
|
|
120
|
+
padding: 0.1em 0.3em;
|
|
121
|
+
border-radius: 3px;
|
|
122
|
+
font-size: 0.9em;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
.demo-content pre {
|
|
126
|
+
background: #f5f5f5;
|
|
127
|
+
padding: 0.75rem;
|
|
128
|
+
border-radius: 4px;
|
|
129
|
+
overflow-x: auto;
|
|
130
|
+
font-size: 0.85em;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
.demo-list {
|
|
134
|
+
list-style: none;
|
|
135
|
+
padding: 0;
|
|
136
|
+
margin: 0;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
.demo-list li {
|
|
140
|
+
margin: 0.5rem 0;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
.demo-list a {
|
|
144
|
+
display: inline-block;
|
|
145
|
+
padding: 0.35em 0.5em;
|
|
146
|
+
border-radius: 4px;
|
|
147
|
+
background: #f0f0f0;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
.demo-list a:hover {
|
|
151
|
+
background: #e0e0e0;
|
|
152
|
+
text-decoration: none;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
.demo-buttons {
|
|
156
|
+
display: flex;
|
|
157
|
+
flex-wrap: wrap;
|
|
158
|
+
gap: 0.5rem;
|
|
159
|
+
margin: 0.75rem 0;
|
|
160
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { useRoute } from '@budarin/use-route';
|
|
2
|
+
import { Link } from '../components/Link';
|
|
3
|
+
|
|
4
|
+
const BASE = '/base-demo';
|
|
5
|
+
|
|
6
|
+
export function BaseDemo() {
|
|
7
|
+
const { pathname, navigate } = useRoute({ base: BASE });
|
|
8
|
+
|
|
9
|
+
return (
|
|
10
|
+
<div className="demo-content">
|
|
11
|
+
<h1>Раздел с префиксом (локальный base)</h1>
|
|
12
|
+
<p className="demo-lead">
|
|
13
|
+
Когда приложение живёт не в корне сайта, а в подпапке (например <code>/app/</code>),
|
|
14
|
+
можно задать <code>base: '/base-demo'</code>. Тогда внутри раздела{' '}
|
|
15
|
+
<code>pathname</code>
|
|
16
|
+
приходит без префикса, а <code>navigate('/page1')</code> ведёт на{' '}
|
|
17
|
+
<code>/base-demo/page1</code>. Так удобно писать пути «относительно раздела». Ниже —
|
|
18
|
+
кнопки перехода внутри раздела и выход на главную.
|
|
19
|
+
</p>
|
|
20
|
+
<p>
|
|
21
|
+
Сейчас pathname внутри раздела: <code>{pathname || '/'}</code> (в полном адресе к
|
|
22
|
+
нему добавлен префикс <code>/base-demo</code>).
|
|
23
|
+
</p>
|
|
24
|
+
<h2>Действия</h2>
|
|
25
|
+
<div className="demo-buttons">
|
|
26
|
+
<button type="button" onClick={() => navigate('/')}>
|
|
27
|
+
В начало раздела (/base-demo/)
|
|
28
|
+
</button>
|
|
29
|
+
<button type="button" onClick={() => navigate('/page1')}>
|
|
30
|
+
Перейти на page1 (в адресе будет /base-demo/page1)
|
|
31
|
+
</button>
|
|
32
|
+
<Link to="/base-demo/page2">Перейти на page2</Link>
|
|
33
|
+
<button type="button" onClick={() => navigate('/', { base: '' })}>
|
|
34
|
+
Выйти из раздела — на главную страницу
|
|
35
|
+
</button>
|
|
36
|
+
</div>
|
|
37
|
+
</div>
|
|
38
|
+
);
|
|
39
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { useRoute, type PathMatcher, type RouteParams } from '@budarin/use-route';
|
|
2
|
+
import { Link } from '../components/Link';
|
|
3
|
+
|
|
4
|
+
const matchProducts: PathMatcher = (pathname): { matched: boolean; params: RouteParams } => {
|
|
5
|
+
const parts = pathname.split('/').filter(Boolean);
|
|
6
|
+
if (parts[0] !== 'products' || !parts[1] || !parts[2]) {
|
|
7
|
+
return { matched: false, params: {} };
|
|
8
|
+
}
|
|
9
|
+
return {
|
|
10
|
+
matched: true,
|
|
11
|
+
params: { category: parts[1], id: parts[2] },
|
|
12
|
+
};
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export function CustomMatcher() {
|
|
16
|
+
const { pathname, matched, params } = useRoute(matchProducts);
|
|
17
|
+
|
|
18
|
+
return (
|
|
19
|
+
<div className="demo-content">
|
|
20
|
+
<h1>Свой разбор пути (PathMatcher)</h1>
|
|
21
|
+
<p className="demo-lead">
|
|
22
|
+
Кроме шаблона строкой (как <code>/users/:id</code>) можно передать функцию, которая
|
|
23
|
+
сама разбирает путь и возвращает <code>matched</code> и <code>params</code>. Здесь
|
|
24
|
+
путь вида <code>/products/категория/id</code> разбивается на категорию и id —
|
|
25
|
+
например «книги» и «1». Выберите ссылку ниже, чтобы увидеть разные значения.
|
|
26
|
+
</p>
|
|
27
|
+
<p>
|
|
28
|
+
Адрес: <code>{pathname}</code>
|
|
29
|
+
{matched && (
|
|
30
|
+
<>
|
|
31
|
+
{' '}
|
|
32
|
+
→ категория: <strong>{params.category}</strong>, id:{' '}
|
|
33
|
+
<strong>{params.id}</strong>
|
|
34
|
+
</>
|
|
35
|
+
)}
|
|
36
|
+
</p>
|
|
37
|
+
<h2>Примеры ссылок</h2>
|
|
38
|
+
<div className="demo-buttons">
|
|
39
|
+
<Link to="/products/books/1">Книги, товар 1</Link>
|
|
40
|
+
<Link to="/products/books/2">Книги, товар 2</Link>
|
|
41
|
+
<Link to="/products/electronics/1">Электроника, товар 1</Link>
|
|
42
|
+
</div>
|
|
43
|
+
</div>
|
|
44
|
+
);
|
|
45
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { useRoute } from '@budarin/use-route';
|
|
2
|
+
|
|
3
|
+
export function History() {
|
|
4
|
+
const { historyIndex, back, forward, go, canGoBack, canGoForward } = useRoute();
|
|
5
|
+
|
|
6
|
+
return (
|
|
7
|
+
<div className="demo-content">
|
|
8
|
+
<h1>История браузера</h1>
|
|
9
|
+
<p className="demo-lead">
|
|
10
|
+
Хук даёт доступ к истории: переход «Назад», «Вперёд» и на N шагов (
|
|
11
|
+
<code>go(-2)</code>,<code>go(1)</code>). Перед переходом можно проверить{' '}
|
|
12
|
+
<code>canGoBack()</code> и <code>canGoForward()</code>, чтобы не нажимать в пустоту.
|
|
13
|
+
Сначала перейдите по нескольким ссылкам в меню, затем используйте кнопки ниже — они
|
|
14
|
+
работают так же, как стрелки в шапке.
|
|
15
|
+
</p>
|
|
16
|
+
<p>
|
|
17
|
+
Текущая позиция в истории: <strong>{historyIndex}</strong> (0 — самая новая).
|
|
18
|
+
</p>
|
|
19
|
+
<h2>Переход по истории</h2>
|
|
20
|
+
<div className="demo-buttons">
|
|
21
|
+
<button type="button" onClick={() => back()} disabled={!canGoBack()}>
|
|
22
|
+
← Назад (на 1 шаг)
|
|
23
|
+
</button>
|
|
24
|
+
<button type="button" onClick={() => go(-2)} disabled={!canGoBack(2)}>
|
|
25
|
+
← На 2 шага назад
|
|
26
|
+
</button>
|
|
27
|
+
<button type="button" onClick={() => go(1)} disabled={!canGoForward()}>
|
|
28
|
+
На 1 шаг вперёд →
|
|
29
|
+
</button>
|
|
30
|
+
<button type="button" onClick={() => forward()} disabled={!canGoForward()}>
|
|
31
|
+
Вперёд →
|
|
32
|
+
</button>
|
|
33
|
+
</div>
|
|
34
|
+
</div>
|
|
35
|
+
);
|
|
36
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { useRoute } from '@budarin/use-route';
|
|
2
|
+
import { Link } from '../components/Link';
|
|
3
|
+
|
|
4
|
+
export function Home() {
|
|
5
|
+
const { pathname, navigate } = useRoute();
|
|
6
|
+
|
|
7
|
+
return (
|
|
8
|
+
<div className="demo-content">
|
|
9
|
+
<h1>Демо: хук useRoute</h1>
|
|
10
|
+
<p className="demo-lead">
|
|
11
|
+
Это демо-приложение показывает, как работает хук <code>@budarin/use-route</code>:
|
|
12
|
+
навигация, параметры в URL, история браузера и другие возможности. Выберите раздел в
|
|
13
|
+
меню выше или кнопкой ниже — на каждой странице коротко объяснено, что именно там
|
|
14
|
+
показано.
|
|
15
|
+
</p>
|
|
16
|
+
<p>
|
|
17
|
+
Сейчас в адресной строке путь: <code>{pathname}</code>
|
|
18
|
+
</p>
|
|
19
|
+
<h2>Куда перейти</h2>
|
|
20
|
+
<div className="demo-buttons">
|
|
21
|
+
<button type="button" onClick={() => navigate('/users')}>
|
|
22
|
+
Пользователи — параметр в URL (например /users/123)
|
|
23
|
+
</button>
|
|
24
|
+
<button type="button" onClick={() => navigate('/posts')}>
|
|
25
|
+
Посты — параметры в строке запроса (?page=2)
|
|
26
|
+
</button>
|
|
27
|
+
<button type="button" onClick={() => navigate('/history')}>
|
|
28
|
+
История — кнопки «Назад» и «Вперёд»
|
|
29
|
+
</button>
|
|
30
|
+
<button type="button" onClick={() => navigate('/push-replace')}>
|
|
31
|
+
Push и Replace — как добавлять или заменять запись в истории
|
|
32
|
+
</button>
|
|
33
|
+
<Link to="/products/books/1">Товары — свой разбор пути (PathMatcher)</Link>
|
|
34
|
+
<Link to="/base-demo">Раздел с префиксом (локальный base)</Link>
|
|
35
|
+
</div>
|
|
36
|
+
</div>
|
|
37
|
+
);
|
|
38
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { useRoute } from '@budarin/use-route';
|
|
2
|
+
|
|
3
|
+
export function Posts() {
|
|
4
|
+
const { pathname, searchParams, navigate } = useRoute('/posts');
|
|
5
|
+
const pageParam = searchParams.get('page') ?? '1';
|
|
6
|
+
const page = Math.max(1, parseInt(pageParam, 10) || 1);
|
|
7
|
+
|
|
8
|
+
return (
|
|
9
|
+
<div className="demo-content">
|
|
10
|
+
<h1>Посты (параметры в строке запроса)</h1>
|
|
11
|
+
<p className="demo-lead">
|
|
12
|
+
Здесь демонстрируется работа с параметрами после <code>?</code> в URL (например{' '}
|
|
13
|
+
<code>?page=2</code>). Хук отдаёт их через <code>searchParams</code>. Кнопки «Пред.»
|
|
14
|
+
и «След.» меняют номер страницы в адресе.
|
|
15
|
+
</p>
|
|
16
|
+
<p>
|
|
17
|
+
Сейчас открыта страница: <strong>{page}</strong> (в адресе:{' '}
|
|
18
|
+
<code>
|
|
19
|
+
{pathname}
|
|
20
|
+
{page > 1 ? `?page=${page}` : ''}
|
|
21
|
+
</code>
|
|
22
|
+
)
|
|
23
|
+
</p>
|
|
24
|
+
<h2>Переключение страниц</h2>
|
|
25
|
+
<div className="demo-buttons">
|
|
26
|
+
<button
|
|
27
|
+
type="button"
|
|
28
|
+
onClick={() => navigate(`/posts?page=${page - 1}`)}
|
|
29
|
+
disabled={page <= 1}
|
|
30
|
+
>
|
|
31
|
+
← Предыдущая страница
|
|
32
|
+
</button>
|
|
33
|
+
<span>Страница {page}</span>
|
|
34
|
+
<button type="button" onClick={() => navigate(`/posts?page=${page + 1}`)}>
|
|
35
|
+
Следующая страница →
|
|
36
|
+
</button>
|
|
37
|
+
</div>
|
|
38
|
+
</div>
|
|
39
|
+
);
|
|
40
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { useRoute } from '@budarin/use-route';
|
|
2
|
+
import { Link } from '../components/Link';
|
|
3
|
+
|
|
4
|
+
export function PushReplace() {
|
|
5
|
+
const { pathname, navigate, replace } = useRoute();
|
|
6
|
+
|
|
7
|
+
return (
|
|
8
|
+
<div className="demo-content">
|
|
9
|
+
<h1>Push и Replace</h1>
|
|
10
|
+
<p className="demo-lead">
|
|
11
|
+
При переходе можно либо <strong>добавить</strong> новую запись в историю (push) —
|
|
12
|
+
тогда кнопка «Назад» вернёт на эту страницу, либо <strong>заменить</strong> текущую
|
|
13
|
+
запись (replace) — тогда «Назад» сюда не приведёт. Ниже три кнопки: первая добавляет
|
|
14
|
+
запись, вторая и третья заменяют. Поэкспериментируйте с «Назад» после каждого
|
|
15
|
+
нажатия.
|
|
16
|
+
</p>
|
|
17
|
+
<p>
|
|
18
|
+
Текущий адрес: <code>{pathname}</code>
|
|
19
|
+
</p>
|
|
20
|
+
<h2>Куда перейти</h2>
|
|
21
|
+
<div className="demo-buttons">
|
|
22
|
+
<button
|
|
23
|
+
type="button"
|
|
24
|
+
onClick={() => navigate('/push-replace/step-a', { history: 'push' })}
|
|
25
|
+
>
|
|
26
|
+
Перейти на «Шаг A» (добавить в историю — «Назад» вернёт сюда)
|
|
27
|
+
</button>
|
|
28
|
+
<button
|
|
29
|
+
type="button"
|
|
30
|
+
onClick={() => navigate('/push-replace/step-b', { history: 'replace' })}
|
|
31
|
+
>
|
|
32
|
+
Перейти на «Шаг B» (заменить текущую страницу — «Назад» сюда не вернёт)
|
|
33
|
+
</button>
|
|
34
|
+
<button type="button" onClick={() => replace('/push-replace/step-c')}>
|
|
35
|
+
Заменить на «Шаг C» (то же, что replace)
|
|
36
|
+
</button>
|
|
37
|
+
</div>
|
|
38
|
+
<p>
|
|
39
|
+
<Link to="/push-replace">Вернуться в начало этого раздела</Link>
|
|
40
|
+
</p>
|
|
41
|
+
</div>
|
|
42
|
+
);
|
|
43
|
+
}
|