UNPKG

@itznevikat/router

Version:

📦 Многофункциональный роутер для приложений на React и VKUI

1 lines 28.2 kB
{"version":3,"file":"index.modern.mjs","sources":["../src/utils/history.ts","../src/router.ts","../src/utils/node.ts","../src/utils/deserialize.ts","../src/hooks.ts","../src/components.tsx","../src/match.tsx","../src/utils/navs.ts","../src/utils/bridge.ts"],"sourcesContent":["import { createHashHistory, HashHistory } from 'history';\n\nimport { AnyDict } from '../types';\n\nexport type State<T extends AnyDict> = {\n force?: boolean;\n meta?: T;\n};\n\nexport type FallbackMeta<T extends AnyDict> = {\n from: string;\n retry: number;\n meta: T;\n};\n\nexport let history: HashHistory = createHashHistory({ window });\n","import { Blocker, Listener } from 'history';\nimport { AnyDict } from './types';\nimport { history, State } from './utils/history';\n\n/**\n * Переход к следующей странице\n * @param to URL новой страницы\n * @param meta метаданные\n */\nexport function push<T extends AnyDict>(to: string, meta?: T): void {\n return history.push(to, { meta } as State<T>);\n}\n\n/**\n * Переход к следующей странице и удаление старой из истории\n * @param to URL новой страницы\n * @param meta метаданные\n */\nexport function replace<T extends AnyDict>(to: string, meta?: T): void {\n return history.replace(to, { meta } as State<T>);\n}\n\n/**\n * Переход назад\n */\nexport function back(): void {\n return history.back();\n}\n\n/**\n * Переход впёред\n */\nexport function forward(): void {\n return history.forward();\n}\n\n/**\n * Переход на ±delta шагов вперёд/назад, в зависимости от знака\n * @param delta число шагов\n */\nexport function go(delta: number): void {\n return history.go(delta);\n}\n\n/**\n * Предотвращает изменение текущей локации и устанавливает слушатель, который будет вызываться вместо глобального\n * @param blocker функция, которая будет вызвана во время попытки покинуть локацию\n * @returns функция для раблокировки\n */\nexport function block(blocker: Blocker): VoidFunction {\n return history.block(blocker);\n}\n\n/**\n * Прослушка событий навигации\n * @param listener слушатель\n * @returns функция для остановки слушателя\n */\nexport function listen(listener: Listener): VoidFunction {\n return history.listen(listener);\n}\n","import { isValidElement, ReactNode } from 'react';\nimport { getNavId } from '@vkontakte/vkui/dist/lib/getNavId';\n\nexport const NODE_ID_ATTRIBUTE: string = 'data-node-id';\n\nexport function getNodeID(node: ReactNode): string | undefined {\n if (!isValidElement(node)) return;\n\n return node.props[NODE_ID_ATTRIBUTE] as string;\n}\n\nexport function getNavID(node: ReactNode): string | undefined {\n if (!isValidElement(node)) return;\n\n return getNavId(node.props);\n}\n","import { Children, isValidElement, ReactNode } from 'react';\n\nimport { StringDict } from '../types';\nimport { Nav } from './navs';\nimport { getNavID, getNodeID } from './node';\n\nexport function deserialize(\n root: ReactNode,\n navs: Nav[],\n pathname: string\n): StringDict {\n let deserialized: Record<string, string> = {};\n let chunks: string[] = pathname.split(/(?=\\/)/);\n\n let currentWorkID: string | undefined = chunks.shift();\n\n function loop(node: ReactNode, parent: ReactNode): void {\n if (!isValidElement(node) || !isValidElement(parent)) return;\n\n let navID: string | undefined = getNavID(node);\n let parentNodeID: string | undefined = getNodeID(parent);\n\n if (navID && parentNodeID && currentWorkID === navID) {\n let parentNav: Nav | undefined = navs.find(\n (nav) => nav.nodeID === parentNodeID\n );\n\n if (parentNav?.availableTransitionIDs.includes(navID)) {\n deserialized[parentNodeID] = navID;\n currentWorkID = chunks.shift();\n }\n }\n\n if (node.props.children)\n Children.toArray(node.props.children).forEach((child) =>\n loop(child, navID ? node : parent)\n );\n }\n loop(root, root);\n\n return deserialized;\n}\n","import {\n MouseEvent,\n MouseEventHandler,\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useState\n} from 'react';\nimport { Action, Location, Update } from 'history';\nimport { Platform, ConfigProviderContext } from '@vkontakte/vkui';\n\nlet initialLocation: globalThis.Location = location;\n\nimport { AnyDict, StringDict } from './types';\nimport { MatchContext, Style } from './match';\nimport { history, State } from './utils/history';\nimport { deserialize } from './utils/deserialize';\n\nfunction getParams<T>(): T {\n return (Object.fromEntries(\n new URLSearchParams(\n history.location.search.slice(1)\n ) as unknown as Iterable<any>\n ) ?? {}) as T;\n}\n\n/**\n * Хук для получения параметров\n */\nexport function useParams<T extends StringDict>(): T {\n let [params, setParams] = useState<StringDict>(getParams);\n\n useEffect(() => {\n return history.listen(() => setParams(getParams));\n }, []);\n\n return params as T;\n}\n\nfunction getMeta<T>(): T {\n return ((history.location.state as State<T>)?.meta ?? {}) as T;\n}\n\n/**\n * Хук для получения метаданных\n */\nexport function useMeta<T extends AnyDict>(): T {\n let [meta, setMeta] = useState<AnyDict>(getMeta);\n\n useEffect(() => {\n return history.listen(({ action }: Update) => {\n if ([Action.Push, Action.Replace].includes(action)) setMeta(getMeta);\n });\n }, []);\n\n return meta as T;\n}\n\nfunction getLocation(): Location {\n return history.location;\n}\n\n/**\n * Хук для получения текущей локации\n */\nexport function useLocation(): Location {\n let [location, setLocation] = useState<Location>(getLocation);\n\n useEffect(() => {\n return history.listen(() => setLocation(getLocation));\n }, []);\n\n return location;\n}\n\n/**\n * Хук для получения начальной локации при запуске\n */\nexport function useInitialLocation(): globalThis.Location {\n return initialLocation;\n}\n\n/**\n * Хук для получения активных свойств слоёв навигации\n */\nexport function useDeserialized(): Record<'view' | 'panel' | string, string> {\n let { root, navs } = useContext(MatchContext);\n\n let deserialized: StringDict = useMemo(\n () => deserialize(root, navs, history.location.pathname),\n [root, history.location.pathname]\n );\n\n let { view, panel } = useMemo(() => {\n let rootNodeID: string =\n navs.find(({ type }) => type === 'root' || type === 'epic')?.nodeID ??\n '/';\n let view: string = deserialized[rootNodeID] ?? '/';\n\n let viewNodeID: string =\n navs.find(({ type, navID }) => type === 'view' && navID === view)\n ?.nodeID ?? '/';\n let panel: string = deserialized[viewNodeID] ?? '/';\n\n return { view, panel };\n }, [root, navs, history.location.pathname]);\n\n return {\n ...deserialized,\n view,\n panel\n };\n}\n\nlet actionRef: Element | null = null;\n\n/**\n * Хук для удобной работы с рефами при использовании ActionSheet\n * @param handler обработчик при установке рефа, в нём нужно делать переход к ActionSheet. Не используется в самом ActionSheet при получении рефа\n */\nexport function useActionRef(handler?: (e: Element | null) => void) {\n let setActionRef = useCallback(\n (el: Element | null) => {\n actionRef = el;\n if (handler) handler(actionRef);\n },\n [handler]\n );\n\n let setActionRefHandler: MouseEventHandler<HTMLElement> = useCallback(\n (e: MouseEvent<HTMLElement> | undefined) => {\n actionRef = e?.target as Element;\n if (handler) handler(actionRef);\n },\n [handler]\n );\n\n return { actionRef, setActionRef, setActionRefHandler };\n}\n\n/**\n * Хук для получения текущего стиля навигации\n */\nexport function useStyle(): Style {\n let { platform } = useContext(ConfigProviderContext);\n\n return platform === Platform.VKCOM ? Style.DESKTOP : Style.MOBILE;\n}\n","import React, { FC } from 'react';\nimport {\n View as VKUIView,\n ViewProps,\n Root as VKUIRoot,\n RootProps,\n Epic as VKUIEpic,\n EpicProps,\n ModalRoot as VKUIModalRoot,\n ModalRootProps\n} from '@vkontakte/vkui';\nimport { NavIdProps } from '@vkontakte/vkui/dist/lib/getNavId';\n\nimport { useParams } from './hooks';\nimport { back } from './router';\n\n// nav or id prop required\ntype NavIdRequiredProps = Required<\n Pick<NavIdProps, 'nav'> | Pick<NavIdProps, 'id'>\n>;\n\nexport const View: FC<\n Omit<ViewProps, 'activePanel' | 'history' | 'onSwipeback'> &\n NavIdRequiredProps\n> = (props) => (\n // eslint-disable-next-line @typescript-eslint/ban-ts-comment\n // @ts-ignore\n <VKUIView {...props}>{props.children}</VKUIView>\n);\n\nexport const Root: FC<Omit<RootProps, 'activeView'> & NavIdRequiredProps> = (\n props\n) => (\n // eslint-disable-next-line @typescript-eslint/ban-ts-comment\n // @ts-ignore\n <VKUIRoot {...props}>{props.children}</VKUIRoot>\n);\n\nexport const Epic: FC<Omit<EpicProps, 'activeStory'> & NavIdRequiredProps> = (\n props\n) => (\n // eslint-disable-next-line @typescript-eslint/ban-ts-comment\n // @ts-ignore\n <VKUIEpic {...props}>{props.children}</VKUIEpic>\n);\n\n// modals\nexport const ModalRoot: FC<ModalRootProps> = (props) => {\n let { modal = null } = useParams();\n\n return (\n <VKUIModalRoot activeModal={modal} onClose={back} {...props}>\n {props.children}\n </VKUIModalRoot>\n );\n};\n","import React, {\n Children,\n cloneElement,\n createContext,\n FC,\n isValidElement,\n ReactElement,\n ReactNode,\n useEffect,\n useMemo,\n useRef,\n useState\n} from 'react';\nimport { deepForEach, deepMap } from 'react-children-utilities';\nimport { Action, Listener, Update, createPath } from 'history';\nimport {\n ViewProps,\n View as VKUIView,\n Root as VKUIRoot,\n Epic as VKUIEpic\n} from '@vkontakte/vkui';\n\nimport { View, Root, Epic } from './components';\nimport { useStyle } from './hooks';\nimport { AnyDict, StringDict } from './types';\nimport { deserialize } from './utils/deserialize';\nimport { getNavID, getNodeID, NODE_ID_ATTRIBUTE } from './utils/node';\nimport { createNav, Nav, NavType } from './utils/navs';\nimport { history, State, FallbackMeta } from './utils/history';\nimport { setLocation } from './utils/bridge';\n\nfunction createNodeID(node: ReactNode): string {\n let key: string = '';\n\n deepForEach(node, (node) => {\n // eslint-disable-next-line @typescript-eslint/ban-ts-comment\n // @ts-ignore\n if (isValidElement(node)) key += node.key ?? node.type.name;\n });\n\n return key;\n}\n\nfunction markNodeIDs(root: ReactNode): ReactNode {\n return deepMap(root, (node: ReactNode) => {\n if (!isValidElement(node)) return node;\n\n let navID: string | undefined = getNavID(node);\n\n // mark only VKUI elements\n if (!navID) {\n switch (node.type) {\n case View:\n case Root:\n case Epic:\n console.warn(\n '[router] found known navigation layout but no `nav` property. Maybe you forgot about it?'\n );\n break;\n\n case VKUIView:\n case VKUIRoot:\n case VKUIEpic:\n console.warn(\n '[router] use View, Root and Epic imported from the router to work correctly.'\n );\n break;\n }\n\n return node;\n }\n\n return cloneElement(node, {\n ...node.props,\n [NODE_ID_ATTRIBUTE]: createNodeID(node)\n });\n })[0];\n}\n\nfunction extractLayoutsAsNavs(root: ReactNode): Nav[] {\n let items: Nav[] = [];\n\n deepForEach(root, (node: ReactNode) => {\n if (!isValidElement(node)) return;\n\n let navID: string | undefined = getNavID(node);\n if (!navID) return node;\n\n let type: NavType;\n switch (node.type) {\n case View:\n type = 'view';\n break;\n\n case Root:\n type = 'root';\n break;\n\n case Epic:\n type = 'epic';\n break;\n\n default:\n return node;\n }\n\n let availableTransitionIDs: string[] = Children.toArray(node.props.children)\n .map((child) => isValidElement(child) && getNavID(child))\n .filter((navID) => !!navID) as string[];\n let nodeID: string = node.props[NODE_ID_ATTRIBUTE];\n\n items.unshift(createNav(type, navID, availableTransitionIDs, nodeID));\n });\n\n return items;\n}\n\nfunction renderRoute(\n pathname: string,\n root: ReactNode,\n navs: Nav[],\n style: Style\n): ReactNode {\n let deserialized: StringDict = deserialize(root, navs, pathname);\n\n return deepMap(root, (node: ReactNode) => {\n if (!isValidElement(node)) return node;\n\n let nodeID: string | undefined = getNodeID(node);\n if (!nodeID) return node;\n\n let active: string = deserialized[nodeID] ?? '/';\n let props: AnyDict = {\n ...node.props\n };\n\n switch (node.type) {\n case View:\n props.activePanel = active;\n\n // swipeback on mobile\n if (style === Style.MOBILE) {\n let nav: Nav = navs.find((nav) => nav.nodeID === nodeID)!;\n\n (props as ViewProps).history = nav.transitions;\n (props as ViewProps).onSwipeBack = history.back;\n }\n\n break;\n\n case Root:\n props.activeView = active;\n break;\n\n case Epic:\n props.activeStory = active;\n break;\n\n default:\n return node;\n }\n\n return cloneElement(node, props);\n })[0];\n}\n\nexport type MatchContextValue = MatchConfig & {\n /**\n * Промаркированные дети\n */\n root: ReactNode;\n\n /**\n * Слои навигации\n */\n navs: Nav[];\n\n /**\n * Стиль навигации\n */\n style: Style;\n};\n\n/**\n * Контекст с конфигом компонента Match и внутренними значениями роутера\n */\nexport const MatchContext = createContext<MatchContextValue>(\n {} as MatchContextValue\n);\n\n/**\n * Стиль навигации\n */\nexport enum Style {\n MOBILE = 'MOBILE',\n DESKTOP = 'DESKTOP'\n}\n\n/**\n * Конфиг для компонента Match\n */\nexport type MatchConfig = {\n /**\n * Стиль навигации.\n * По умолчанию берётся платформа, прокинутая через ConfigProvider\n */\n style?: Style;\n\n /**\n * Начальная страница.\n * Если не указана, то при запуске не будет совершён переход\n */\n initialURL?: string;\n\n /**\n * Страница 404.\n * Будет использована, если страница при переходе не найдена\n */\n fallbackURL?: string;\n\n /**\n * Отключает отправку события VKWebAppSetLocation для установки хэша вне фрейма.\n * По умолчанию выключено, событие отправляется\n */\n disableSetLocation?: boolean;\n};\n\n/**\n * Главный компонент роутера, в него оборачивается вся структура\n */\nexport const Match: FC<MatchConfig> = ({\n children,\n style: _style,\n initialURL,\n fallbackURL,\n disableSetLocation\n}) => {\n let rerender = useState<unknown>()[1];\n let frender = useRef(true);\n\n let root: ReactNode = useMemo(() => markNodeIDs(children), [children]);\n let navs: Nav[] = useMemo(() => extractLayoutsAsNavs(root), []);\n\n let route: string = frender.current\n ? initialURL ?? history.location.pathname\n : history.location.pathname;\n\n // set or detect style\n let style: Style = _style ?? useStyle();\n\n // listen events and rerender\n useEffect(() => {\n let listener: Listener = ({ location, action }: Update) => {\n let state: State<any> | undefined = location.state as\n | State<any>\n | undefined;\n let deserialized: StringDict = deserialize(root, navs, location.pathname);\n let keys: string[] = Object.keys(deserialized);\n\n // not found\n if (keys.length === 0) {\n console.warn('[router] route not found.');\n\n if (fallbackURL) {\n if (state?.meta?.retry > 0)\n return console.error('[router] fallback route not found.');\n\n return history.replace(fallbackURL, {\n force: true,\n meta: {\n from: createPath(location),\n retry: state?.meta?.retry ? state?.meta?.retry + 1 : 1,\n meta: state?.meta\n }\n } as State<FallbackMeta<any>>);\n }\n }\n\n if (state?.force) action = Action.Push;\n\n navs.forEach(({ nodeID, transitions }) => {\n let activeNavID: string = deserialized[nodeID] ?? '/';\n if (transitions[transitions.length - 1] === activeNavID) return;\n\n if (action === Action.Push) transitions.push(activeNavID);\n if (action === Action.Replace) transitions.splice(-1, 1, activeNavID);\n\n if (action === Action.Pop) {\n // TODO: back and forward for delta < -1 and delta > 1\n let back: boolean = transitions.includes(activeNavID);\n\n if (back) {\n // back\n transitions.pop();\n } else {\n // forward\n transitions.push(activeNavID);\n }\n }\n });\n\n // set parent page location hash with vk bridge\n if (!disableSetLocation) setLocation(location);\n\n rerender({});\n };\n\n let unlisten: VoidFunction = history.listen(listener);\n\n let nextURL: string = createPath(history.location);\n if (initialURL) {\n if (initialURL !== nextURL) {\n route = initialURL;\n history.replace(initialURL);\n }\n } else if (history.location.search.slice(1)) {\n history.replace(history.location.pathname);\n history.push(nextURL);\n } else if (history.location.pathname.slice(1)) {\n history.replace('/');\n history.push(nextURL);\n }\n\n // set is first render to false\n frender.current = false;\n\n // history.listen returns unlisten function\n return unlisten;\n }, []);\n\n // provider for match context\n return (\n <MatchContext.Provider\n value={\n {\n root,\n navs,\n style,\n initialURL,\n fallbackURL,\n disableSetLocation\n } as MatchContextValue\n }\n >\n {/* render current route */}\n {renderRoute(route, root, navs, style)}\n </MatchContext.Provider>\n );\n};\n\n/**\n * Получение активного попаута по nav/id свойству\n * @param popout имя активного попаута\n * @param elements возможные попауты\n * @returns активный попаут\n */\nexport function matchPopout(\n popout: string | null,\n elements: ReactElement[]\n): ReactElement | null {\n return popout\n ? elements.find((node) => getNavID(node) === popout) ?? null\n : null;\n}\n","export type NavType = 'view' | 'root' | 'epic';\n\nexport type Nav = {\n type: NavType;\n navID: string;\n nodeID: string;\n availableTransitionIDs: string[];\n transitions: string[];\n};\n\nexport function createNav(\n type: NavType,\n navID: string,\n availableTransitionIDs: string[],\n nodeID: string\n): Nav {\n let firstTransition: string = availableTransitionIDs[0];\n\n return {\n type,\n navID,\n nodeID,\n availableTransitionIDs,\n transitions: [firstTransition]\n };\n}\n","import bridge from '@vkontakte/vk-bridge';\nimport { createPath, Location } from 'history';\n\nexport function setLocation(location: Location) {\n return bridge.send('VKWebAppSetLocation', {\n location: createPath(location),\n // eslint-disable-next-line @typescript-eslint/ban-ts-comment\n // @ts-ignore\n replace_state: true\n });\n}\n"],"names":["history","createHashHistory","window","push","to","meta","replace","back","forward","go","delta","block","blocker","listen","listener","getNodeID","node","isValidElement","props","getNavID","getNavId","deserialize","root","navs","pathname","deserialized","chunks","split","currentWorkID","shift","loop","parent","navID","parentNodeID","parentNav","find","nav","nodeID","availableTransitionIDs","includes","children","Children","toArray","forEach","child","initialLocation","location","getParams","Object","fromEntries","URLSearchParams","search","slice","useParams","params","setParams","useState","useEffect","getMeta","state","_history$location$sta2","useMeta","setMeta","action","Action","Push","Replace","getLocation","useLocation","setLocation","useInitialLocation","useDeserialized","useContext","MatchContext","useMemo","view","panel","rootNodeID","type","_navs$find","viewNodeID","_navs$find2","actionRef","useActionRef","handler","setActionRef","useCallback","el","setActionRefHandler","e","target","useStyle","platform","ConfigProviderContext","Platform","VKCOM","Style","DESKTOP","MOBILE","View","React","VKUIView","Root","VKUIRoot","Epic","VKUIEpic","ModalRoot","modal","VKUIModalRoot","activeModal","onClose","createNodeID","key","deepForEach","name","createContext","Match","style","_style","initialURL","fallbackURL","disableSetLocation","rerender","frender","useRef","deepMap","console","warn","cloneElement","markNodeIDs","items","map","filter","unshift","transitions","createNav","extractLayoutsAsNavs","route","current","unlisten","keys","length","retry","error","force","from","createPath","activeNavID","splice","Pop","pop","bridge","send","replace_state","nextURL","Provider","value","active","activePanel","onSwipeBack","activeView","activeStory","renderRoute","matchPopout","popout","elements"],"mappings":"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"}