@table-js/core 0.0.3 → 0.0.5
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/dist/index.js +21 -1
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +21 -1
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -96,6 +96,11 @@ var FocusManager = class {
|
|
|
96
96
|
this.state.focusedId = id;
|
|
97
97
|
node.el.focus({ preventScroll: true });
|
|
98
98
|
node.onFocus?.();
|
|
99
|
+
if (typeof window !== "undefined") {
|
|
100
|
+
window.requestAnimationFrame(() => show(node.el));
|
|
101
|
+
} else {
|
|
102
|
+
show(node.el);
|
|
103
|
+
}
|
|
99
104
|
this.options.onFocusChange?.(id);
|
|
100
105
|
}
|
|
101
106
|
move(direction) {
|
|
@@ -168,6 +173,16 @@ var FocusManager = class {
|
|
|
168
173
|
}).id;
|
|
169
174
|
}
|
|
170
175
|
};
|
|
176
|
+
function show(el) {
|
|
177
|
+
try {
|
|
178
|
+
el.scrollIntoView({
|
|
179
|
+
block: "nearest",
|
|
180
|
+
inline: "nearest"
|
|
181
|
+
});
|
|
182
|
+
} catch {
|
|
183
|
+
el.scrollIntoView();
|
|
184
|
+
}
|
|
185
|
+
}
|
|
171
186
|
function distance(from, to, direction) {
|
|
172
187
|
const fromCx = from.left + from.width / 2;
|
|
173
188
|
const fromCy = from.top + from.height / 2;
|
|
@@ -389,11 +404,16 @@ function FocusGroup({
|
|
|
389
404
|
...options
|
|
390
405
|
}) {
|
|
391
406
|
const { groupId } = useFocusGroup(options);
|
|
407
|
+
const css = options.orientation === "grid" ? style : {
|
|
408
|
+
display: "flex",
|
|
409
|
+
flexDirection: options.orientation === "vertical" ? "column" : "row",
|
|
410
|
+
...style
|
|
411
|
+
};
|
|
392
412
|
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
393
413
|
Tag,
|
|
394
414
|
{
|
|
395
415
|
className,
|
|
396
|
-
style,
|
|
416
|
+
style: css,
|
|
397
417
|
"data-focus-group": groupId,
|
|
398
418
|
children
|
|
399
419
|
}
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/focus/FocusManager.ts","../src/focus/FocusProvider.tsx","../src/focus/context.ts","../src/focus/KeyHandler.ts","../src/focus/hooks.ts","../src/focus/components.tsx","../src/router/Router.tsx","../src/router/matcher.ts","../src/router/context.ts","../src/TableApp.tsx","../src/router/defineRoutes.ts"],"sourcesContent":["export { TableApp } from './TableApp'\n\nexport {\n Router,\n RouterContext,\n useRouter,\n useParams,\n useQuery,\n defineRoutes,\n defineFileRoutes,\n} from './router'\n\nexport type {\n Route,\n RouteDefinition,\n RouteParams,\n RouteQuery,\n RouteSegment,\n NavigateOptions,\n} from './router'\nexport type { RouteModule, RouteModuleLoader } from './router'\n\nexport {\n FocusProvider,\n FocusContext,\n FocusManager,\n KeyHandler,\n Focusable,\n FocusGroup,\n useFocusable,\n useFocusGroup,\n useFocusContext,\n resolveKeyAction,\n} from './focus'\n\nexport type {\n FocusableId,\n FocusableNode,\n FocusDirection,\n FocusGroupType,\n FocusManagerOptions,\n FocusState,\n UseFocusableOptions,\n UseFocusableResult,\n UseFocusGroupOptions,\n} from './focus'\n","import type {\n FocusableId,\n FocusableNode,\n FocusDirection,\n FocusGroup,\n FocusManagerOptions,\n FocusState,\n} from './types'\n\nexport class FocusManager {\n private state: FocusState = {\n focusedId: null,\n nodes: new Map(),\n groups: new Map(),\n }\n\n private options: FocusManagerOptions\n\n constructor(options: FocusManagerOptions = {}) {\n this.options = options\n }\n\n registerNode(node: FocusableNode): void {\n this.state.nodes.set(node.id, node)\n if (\n this.state.focusedId === null &&\n this.options.initialFocusId === node.id\n ) {\n this.setFocus(node.id)\n }\n }\n\n unregisterNode(id: FocusableId): void {\n this.state.nodes.delete(id)\n if (this.state.focusedId === id) {\n this.state.focusedId = null\n this.options.onFocusChange?.(null)\n }\n }\n\n registerGroup(group: FocusGroup): void {\n this.state.groups.set(group.id, group)\n }\n\n unregisterGroup(id: FocusableId): void {\n this.state.groups.delete(id)\n }\n\n setFocus(id: FocusableId): void {\n const node = this.state.nodes.get(id)\n if (!node || node.disabled) return\n\n const prev = this.state.focusedId\n if (prev) {\n const prevNode = this.state.nodes.get(prev)\n prevNode?.onBlur?.()\n prevNode?.el.blur()\n\n if (prevNode?.groupId) {\n const group = this.state.groups.get(prevNode.groupId)\n if (group) group.lastFocusedId = prev\n }\n }\n\n this.state.focusedId = id\n node.el.focus({ preventScroll: true })\n node.onFocus?.()\n this.options.onFocusChange?.(id)\n }\n\n move(direction: FocusDirection): void {\n const currentId = this.state.focusedId\n if (!currentId) {\n this.focusFirst()\n return\n }\n\n const current = this.state.nodes.get(currentId)\n if (!current) return\n\n const groupId = current.groupId\n const group = groupId ? this.state.groups.get(groupId) : null\n\n const next = group\n ? this.findNextInGroup(currentId, direction, group)\n : this.findNextSpatial(currentId, direction)\n\n if (next) this.setFocus(next)\n }\n\n select(): void {\n const node = this.state.focusedId\n ? this.state.nodes.get(this.state.focusedId)\n : null\n node?.onSelect?.()\n node?.el.click()\n }\n\n focusFirst(): void {\n const first = this.state.nodes.values().next().value as FocusableNode | undefined\n if (first) this.setFocus(first.id)\n }\n\n getFocusedId(): FocusableId | null {\n return this.state.focusedId\n }\n\n private findNextInGroup(\n currentId: FocusableId,\n direction: FocusDirection,\n group: FocusGroup,\n ): FocusableId | null {\n const siblings = [...this.state.nodes.values()].filter(\n (n) => n.groupId === group.id && !n.disabled,\n )\n const index = siblings.findIndex((n) => n.id === currentId)\n if (index === -1) return null\n\n const isForward =\n (group.orientation === 'horizontal' && direction === 'right') ||\n (group.orientation === 'vertical' && direction === 'down')\n\n const isBackward =\n (group.orientation === 'horizontal' && direction === 'left') ||\n (group.orientation === 'vertical' && direction === 'up')\n\n if (isForward) {\n if (index < siblings.length - 1) return siblings[index + 1]?.id ?? null\n if (group.loop) return siblings[0]?.id ?? null\n return null\n }\n\n if (isBackward) {\n if (index > 0) return siblings[index - 1]?.id ?? null\n if (group.loop) return siblings[siblings.length - 1]?.id ?? null\n return null\n }\n\n return this.findNextSpatial(currentId, direction)\n }\n\n private findNextSpatial(currentId: FocusableId, direction: FocusDirection): FocusableId | null {\n const current = this.state.nodes.get(currentId)\n if (!current) return null\n\n const currentRect = current.el.getBoundingClientRect()\n const candidates = [...this.state.nodes.values()].filter(\n (n) => n.id !== currentId && !n.disabled,\n )\n\n const inDirection = candidates.filter((n) => {\n const rect = n.el.getBoundingClientRect()\n if (direction === 'right') return rect.left > currentRect.right - 1\n if (direction === 'left') return rect.right < currentRect.left + 1\n if (direction === 'down') return rect.top > currentRect.bottom - 1\n if (direction === 'up') return rect.bottom < currentRect.top + 1\n return false\n })\n\n if (inDirection.length === 0) return null\n\n return inDirection.reduce((best, node) => {\n const a = node.el.getBoundingClientRect()\n const b = best.el.getBoundingClientRect()\n const distA = distance(currentRect, a, direction)\n const distB = distance(currentRect, b, direction)\n return distA < distB ? node : best\n }).id\n }\n}\n\n\nfunction distance(\n from: DOMRect,\n to: DOMRect,\n direction: FocusDirection,\n): number {\n const fromCx = from.left + from.width / 2\n const fromCy = from.top + from.height / 2\n const toCx = to.left + to.width / 2\n const toCy = to.top + to.height / 2\n\n const primary =\n direction === 'right' || direction === 'left'\n ? Math.abs(toCx - fromCx)\n : Math.abs(toCy - fromCy)\n\n const secondary =\n direction === 'right' || direction === 'left'\n ? Math.abs(toCy - fromCy)\n : Math.abs(toCx - fromCx)\n\n return primary + secondary * 0.5\n}","import React, { useEffect, useMemo, useRef } from 'react'\nimport { FocusContext } from './context'\nimport { FocusManager } from './FocusManager'\nimport { KeyHandler } from './KeyHandler'\nimport type { FocusManagerOptions } from './types'\n\ninterface FocusProviderProps {\n children: React.ReactNode\n options?: FocusManagerOptions\n}\n\nexport function FocusProvider({ children, options }: FocusProviderProps) {\n const managerRef = useRef<FocusManager | null>(null)\n const keyHandlerRef = useRef<KeyHandler | null>(null)\n\n if (!managerRef.current) {\n managerRef.current = new FocusManager(options ?? {})\n }\n if (!keyHandlerRef.current) {\n keyHandlerRef.current = new KeyHandler()\n }\n\n const manager = managerRef.current\n const keyHandler = keyHandlerRef.current\n\n useEffect(() => {\n keyHandler.mount()\n\n const unsub = keyHandler.subscribe((action) => {\n if (action.type === 'move') {\n manager.move(action.direction)\n }\n if (action.type === 'select') {\n manager.select()\n }\n })\n\n return () => {\n unsub()\n keyHandler.unmount()\n }\n }, [manager, keyHandler])\n\n const value = useMemo(\n () => ({ manager, keyHandler }),\n [manager, keyHandler],\n )\n\n return (\n <FocusContext.Provider value={value}>\n {children}\n </FocusContext.Provider>\n )\n}","import { createContext, useContext } from 'react'\nimport type { FocusManager } from './FocusManager'\nimport type { KeyHandler } from './KeyHandler'\n\nexport interface FocusContextValue {\n manager: FocusManager\n keyHandler: KeyHandler\n}\n\nexport const FocusContext = createContext<FocusContextValue | null>(null)\n\nexport function useFocusContext(): FocusContextValue {\n const ctx = useContext(FocusContext)\n if (ctx === null) {\n throw new Error('useFocusContext must be used within a TableApp')\n }\n return ctx\n}","import type { FocusDirection } from './types'\n\nexport type KeyAction =\n | { type: 'move'; direction: FocusDirection }\n | { type: 'select' }\n | { type: 'back' }\n | { type: 'unknown' }\n\nconst KEY_MAP: Record<string, KeyAction> = {\n ArrowUp: { type: 'move', direction: 'up' },\n ArrowDown: { type: 'move', direction: 'down' },\n ArrowLeft: { type: 'move', direction: 'left' },\n ArrowRight: { type: 'move', direction: 'right' },\n Enter: { type: 'select' },\n ' ': { type: 'select' },\n Backspace: { type: 'back' },\n Escape: { type: 'back' },\n}\n\nconst TIZEN_KEY_MAP: Record<number, KeyAction> = {\n 38: { type: 'move', direction: 'up' },\n 40: { type: 'move', direction: 'down' },\n 37: { type: 'move', direction: 'left' },\n 39: { type: 'move', direction: 'right' },\n 13: { type: 'select' },\n 10009: { type: 'back' },\n}\n\nconst WEBOS_KEY_MAP: Record<number, KeyAction> = {\n 38: { type: 'move', direction: 'up' },\n 40: { type: 'move', direction: 'down' },\n 37: { type: 'move', direction: 'left' },\n 39: { type: 'move', direction: 'right' },\n 13: { type: 'select' },\n 461: { type: 'back' },\n}\n\nconst ANDROID_TV_KEY_MAP: Record<number, KeyAction> = {\n 38: { type: 'move', direction: 'up' },\n 40: { type: 'move', direction: 'down' },\n 37: { type: 'move', direction: 'left' },\n 39: { type: 'move', direction: 'right' },\n 13: { type: 'select' },\n 85: { type: 'select' },\n 4: { type: 'back' },\n}\n\nfunction detectPlatform(): 'tizen' | 'webos' | 'android' | 'web' {\n if (typeof window === 'undefined') return 'web'\n const ua = navigator.userAgent\n if (ua.includes('Tizen')) return 'tizen'\n if (ua.includes('Web0S') || ua.includes('webOS')) return 'webos'\n if (ua.includes('Android')) return 'android'\n return 'web'\n}\n\nexport function resolveKeyAction(event: KeyboardEvent): KeyAction {\n const byKey = KEY_MAP[event.key]\n if (byKey) return byKey\n\n const platform = detectPlatform()\n\n const platformMap =\n platform === 'tizen' ? TIZEN_KEY_MAP :\n platform === 'webos' ? WEBOS_KEY_MAP :\n platform === 'android' ? ANDROID_TV_KEY_MAP :\n {}\n\n return platformMap[event.keyCode] ?? { type: 'unknown' }\n}\n\nexport class KeyHandler {\n private listeners = new Set<(action: KeyAction) => void>()\n private bound: ((e: KeyboardEvent) => void) | null = null\n\n mount(): void {\n this.bound = (e: KeyboardEvent) => {\n const action = resolveKeyAction(e)\n if (action.type === 'unknown') return\n e.preventDefault()\n this.listeners.forEach((fn) => fn(action))\n }\n window.addEventListener('keydown', this.bound)\n }\n\n unmount(): void {\n if (this.bound) {\n window.removeEventListener('keydown', this.bound)\n this.bound = null\n }\n }\n\n subscribe(fn: (action: KeyAction) => void): () => void {\n this.listeners.add(fn)\n return () => this.listeners.delete(fn)\n }\n}","import {\n useCallback,\n useEffect,\n useId,\n useRef,\n useState,\n} from 'react'\nimport { useFocusContext } from './context'\nimport type { FocusGroup } from './types'\n\nexport interface UseFocusableOptions {\n groupId?: string\n disabled?: boolean\n onFocus?: () => void\n onBlur?: () => void\n onSelect?: () => void\n autoFocus?: boolean\n}\n\nexport interface UseFocusableResult {\n ref: React.RefObject<HTMLElement>\n focused: boolean\n focusableProps: {\n 'data-focusable': string\n 'data-focused': boolean\n tabIndex: number\n }\n}\n\nexport function useFocusable(options: UseFocusableOptions = {}): UseFocusableResult {\n const id = useId()\n const ref = useRef<HTMLElement>(null)\n const [focused, setFocused] = useState(false)\n const { manager } = useFocusContext()\n\n const { groupId, disabled = false, onFocus, onBlur, onSelect, autoFocus } = options\n\n useEffect(() => {\n const el = ref.current\n if (!el) return\n\n manager.registerNode({\n id,\n el,\n groupId: groupId ?? null,\n disabled,\n onFocus: () => {\n setFocused(true)\n onFocus?.()\n },\n onBlur: () => {\n setFocused(false)\n onBlur?.()\n },\n onSelect: onSelect || (() => {}),\n })\n\n if (autoFocus) {\n manager.setFocus(id)\n }\n\n return () => {\n manager.unregisterNode(id)\n }\n }, [id, groupId, disabled, autoFocus, manager, onFocus, onBlur, onSelect])\n\n const focusableProps = {\n 'data-focusable': id,\n 'data-focused': focused,\n tabIndex: focused ? 0 : -1,\n }\n\n return { ref: ref as React.RefObject<HTMLElement>, focused, focusableProps }\n}\n\nexport type UseFocusGroupOptions = Omit<FocusGroup, 'id' | 'lastFocusedId' | 'parentId'>\n\nexport function useFocusGroup(options: UseFocusGroupOptions) {\n const id = useId()\n const { manager } = useFocusContext()\n\n useEffect(() => {\n manager.registerGroup({\n id,\n parentId: null,\n lastFocusedId: null,\n ...options,\n })\n return () => {\n manager.unregisterGroup(id)\n }\n }, [id, manager, options.orientation, options.loop, options.rememberFocus])\n\n return { groupId: id }\n}","import React, { forwardRef } from 'react'\nimport type { JSX } from 'react'\nimport { useFocusable, useFocusGroup } from './hooks'\nimport type { UseFocusableOptions, UseFocusGroupOptions } from './hooks'\n\ninterface FocusableProps extends UseFocusableOptions {\n children: React.ReactNode | ((focused: boolean) => React.ReactNode)\n as?: React.ElementType\n className?: string\n style?: React.CSSProperties\n}\n\nexport const Focusable = forwardRef<HTMLElement, FocusableProps>(\n ({ children, as: Tag = 'div', className, style, ...options }, _ref) => {\n const { ref, focused, focusableProps } = useFocusable(options)\n\n return (\n <Tag\n ref={ref as never}\n className={className}\n style={style}\n {...focusableProps}\n >\n {typeof children === 'function' ? children(focused) : children}\n </Tag>\n )\n },\n)\n\nFocusable.displayName = 'Focusable'\n\ninterface FocusGroupProps extends UseFocusGroupOptions {\n children: React.ReactNode\n as?: React.ElementType\n className?: string\n style?: React.CSSProperties\n}\n\nexport function FocusGroup({\n children,\n as: Tag = 'div',\n className,\n style,\n ...options\n}: FocusGroupProps) {\n const { groupId } = useFocusGroup(options)\n\n return (\n <Tag\n className={className}\n style={style}\n data-focus-group={groupId}\n >\n {children}\n </Tag>\n )\n}","import React, { useCallback, useEffect, useState, useTransition } from 'react'\nimport type { NavigateOptions, Route, RouteDefinition, RouteQuery } from './types'\nimport { matchRoute } from './matcher'\nimport { RouterContext } from './context'\n\ninterface RouterProps {\n routes: RouteDefinition[]\n initialPath?: string\n}\n\nfunction parseQuery(search: string): RouteQuery {\n const query: RouteQuery = {}\n const params = new URLSearchParams(search)\n params.forEach((value, key) => {\n const existing = query[key]\n if (existing === undefined) {\n query[key] = value\n } else if (Array.isArray(existing)) {\n existing.push(value)\n } else {\n query[key] = [existing, value]\n }\n })\n return query\n}\n\nfunction buildRoute(pathname: string, search: string, params: Record<string, string>): Route {\n return {\n path: pathname,\n params,\n query: parseQuery(search),\n }\n}\n\nexport function Router({ routes, initialPath = '/' }: RouterProps) {\n const [, startTransition] = useTransition()\n const [currentPath, setCurrentPath] = useState(() => {\n if (typeof window !== 'undefined') return window.location.pathname\n return initialPath\n })\n const [search, setSearch] = useState(() => {\n if (typeof window !== 'undefined') return window.location.search\n return ''\n })\n const [history, setHistory] = useState<Route[]>([])\n\n const match = matchRoute(currentPath, routes)\n const route = match\n ? buildRoute(currentPath, search, match.params)\n : buildRoute(currentPath, search, {})\n\n const navigate = useCallback(\n (path: string, options: NavigateOptions = {}) => {\n const [pathname, queryString] = path.split('?')\n const nextSearch = queryString ? `?${queryString}` : ''\n\n startTransition(() => {\n if (options.replace) {\n window.history.replaceState(null, '', path)\n } else {\n window.history.pushState(null, '', path)\n setHistory((prev) => [...prev, route])\n }\n setCurrentPath(pathname ?? '/')\n setSearch(nextSearch)\n })\n },\n [route],\n )\n\n const back = useCallback(() => {\n if (history.length === 0) return\n const prev = history[history.length - 1]\n if (!prev) return\n setHistory((h) => h.slice(0, -1))\n window.history.back()\n setCurrentPath(prev.path)\n setSearch('')\n }, [history])\n\n const prefetch = useCallback(\n (path: string) => {\n const [pathname] = path.split('?')\n if (!pathname) return\n const result = matchRoute(pathname, routes)\n if (result) {\n void result.route.component\n }\n },\n [routes],\n )\n\n useEffect(() => {\n const onPopState = () => {\n setCurrentPath(window.location.pathname)\n setSearch(window.location.search)\n }\n\n window.addEventListener('popstate', onPopState)\n return () => window.removeEventListener('popstate', onPopState)\n }, [])\n\n if (!match) {\n return <NotFound path={currentPath} />\n }\n\n const { component: Page } = match.route\n\n return (\n <RouterContext.Provider value={{ route, navigate, back, prefetch }}>\n <React.Suspense fallback={<PageLoader />}>\n <Page />\n </React.Suspense>\n </RouterContext.Provider>\n )\n}\n\nfunction NotFound({ path }: { path: string }) {\n return (\n <div data-table-not-found>\n <span>404 — {path} not found</span>\n </div>\n )\n}\n\nfunction PageLoader() {\n return <div data-table-loader />\n}\n","import type { RouteDefinition, RouteParams, RouteSegment } from './types'\n\nexport function parseSegments(path: string): RouteSegment[] {\n return path\n .split('/')\n .filter(Boolean)\n .map((segment) => {\n if (segment.startsWith('[...') && segment.endsWith(']')) {\n return { type: 'catch-all', value: segment.slice(4, -1) }\n }\n if (segment.startsWith('[') && segment.endsWith(']')) {\n return { type: 'dynamic', value: segment.slice(1, -1) }\n }\n return { type: 'static', value: segment }\n })\n}\n\nexport function matchRoute(\n pathname: string,\n routes: RouteDefinition[],\n): { route: RouteDefinition; params: RouteParams } | null {\n const incomingSegments = pathname.split(\"/\").filter(Boolean)\n\n for (const route of routes) {\n const result = matchSegments(incomingSegments, route.segments)\n if (result !== null) {\n return { route, params: result }\n }\n }\n\n return null\n}\n\nfunction matchSegments(incoming: string[], segments: RouteSegment[]): RouteParams | null {\n const params: RouteParams = {}\n let i = 0\n\n for (const segment of segments) {\n if (segment.type === 'catch-all') {\n params[segment.value] = incoming.slice(i).join('/')\n return params\n }\n\n const part = incoming[i]\n if (part === undefined) return null\n\n if (segment.type === 'static') {\n if (part !== segment.value) return null\n }\n\n if (segment.type === 'dynamic') {\n params[segment.value] = part\n }\n\n i++\n }\n\n if (i !== incoming.length) return null\n\n return params\n}\n","import { createContext, useContext } from 'react'\nimport type { NavigateOptions, Route } from './types'\n\nexport interface RouterContextValue {\n route: Route\n navigate: (path: string, options?: NavigateOptions) => void\n back: () => void\n prefetch: (path: string) => void\n}\n\nexport const RouterContext = createContext<RouterContextValue | null>(null)\n\nexport function useRouter(): RouterContextValue {\n const ctx = useContext(RouterContext)\n if (ctx === null) {\n throw new Error('useRouter must be used within a TableApp')\n }\n return ctx\n}\n\nexport function useParams<T extends Record<string, string>>(): T {\n const { route } = useRouter()\n return route.params as T\n}\n\nexport function useQuery<T extends Record<string, string | string[] | undefined>>(): T {\n const { route } = useRouter()\n return route.query as T\n}\n","import type { RouteDefinition } from './router/types'\nimport type { FocusManagerOptions } from './focus/types'\nimport { FocusProvider } from './focus'\nimport { Router } from './router/Router'\n\nexport interface TableAppProps {\n routes: RouteDefinition[]\n initialPath?: string\n focus?: FocusManagerOptions\n}\n\nexport function TableApp({ routes, initialPath, focus }: TableAppProps) {\n return (\n <FocusProvider {...(focus && { options: focus })}>\n <Router routes={routes} {...(initialPath && { initialPath })} />\n </FocusProvider>\n )\n}","import React from 'react'\nimport { parseSegments } from './matcher'\nimport type { RouteDefinition, RouteSegment } from './types'\n\nexport type RouteModule = {\n default: React.ComponentType\n}\n\nexport type RouteModuleLoader = () => Promise<RouteModule>\n\ntype RouteInput = {\n path: string\n component: RouteModuleLoader\n}\n\nexport function defineRoutes(routes: RouteInput[]): RouteDefinition[] {\n return routes.map((route) => createRouteDefinition(route.path, route.component, route.path))\n}\n\nexport function defineFileRoutes(\n routeModules: Record<string, RouteModuleLoader>,\n options: { appDir?: string } = {},\n): RouteDefinition[] {\n const appDir = options.appDir ?? 'app'\n\n return Object.entries(routeModules)\n .map(([filePath, component]) =>\n createRouteDefinition(normalizeRoutePath(filePath, appDir), component, filePath),\n )\n .sort(compareRouteDefinitions)\n}\n\nfunction createRouteDefinition(\n path: string,\n component: RouteModuleLoader,\n filePath: string,\n): RouteDefinition {\n return {\n filePath,\n segments: parseSegments(path),\n component: React.lazy(component),\n }\n}\n\nfunction normalizeRoutePath(filePath: string, appDir: string): string {\n const normalizedPath = filePath.replace(/\\\\/g, '/').replace(/^\\.\\//, '')\n const appDirPrefix = `${appDir}/`\n const appDirIndex = normalizedPath.indexOf(appDirPrefix)\n\n if (appDirIndex === -1) {\n throw new Error(`Route module \"${filePath}\" must be inside \"${appDir}/\"`)\n }\n\n const pagePath = normalizedPath.slice(appDirIndex + appDirPrefix.length)\n const routePath = pagePath.replace(/(?:^|\\/)page\\.[^/.]+$/, '')\n\n return routePath.length === 0 ? '/' : `/${routePath}`\n}\n\nfunction compareRouteDefinitions(left: RouteDefinition, right: RouteDefinition): number {\n const segmentsOrder = compareSegments(left.segments, right.segments)\n if (segmentsOrder !== 0) {\n return segmentsOrder\n }\n\n return left.filePath.localeCompare(right.filePath)\n}\n\nfunction compareSegments(left: RouteSegment[], right: RouteSegment[]): number {\n const length = Math.max(left.length, right.length)\n\n for (let index = 0; index < length; index++) {\n const leftSegment = left[index]\n const rightSegment = right[index]\n\n if (leftSegment === undefined) return -1\n if (rightSegment === undefined) return 1\n\n const rankDifference = segmentRank(rightSegment) - segmentRank(leftSegment)\n if (rankDifference !== 0) {\n return rankDifference\n }\n\n if (leftSegment.type === 'static' && rightSegment.type === 'static') {\n const valueDifference = leftSegment.value.localeCompare(rightSegment.value)\n if (valueDifference !== 0) {\n return valueDifference\n }\n }\n }\n\n return 0\n}\n\nfunction segmentRank(segment: RouteSegment): number {\n switch (segment.type) {\n case 'static':\n return 3\n case 'dynamic':\n return 2\n case 'catch-all':\n return 1\n }\n}\n"],"mappings":"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","names":["import_react","import_react","import_react","import_jsx_runtime","import_react","import_react","import_jsx_runtime","React","import_jsx_runtime","import_react","React"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/focus/FocusManager.ts","../src/focus/FocusProvider.tsx","../src/focus/context.ts","../src/focus/KeyHandler.ts","../src/focus/hooks.ts","../src/focus/components.tsx","../src/router/Router.tsx","../src/router/matcher.ts","../src/router/context.ts","../src/TableApp.tsx","../src/router/defineRoutes.ts"],"sourcesContent":["export { TableApp } from './TableApp'\n\nexport {\n Router,\n RouterContext,\n useRouter,\n useParams,\n useQuery,\n defineRoutes,\n defineFileRoutes,\n} from './router'\n\nexport type {\n Route,\n RouteDefinition,\n RouteParams,\n RouteQuery,\n RouteSegment,\n NavigateOptions,\n} from './router'\nexport type { RouteModule, RouteModuleLoader } from './router'\n\nexport {\n FocusProvider,\n FocusContext,\n FocusManager,\n KeyHandler,\n Focusable,\n FocusGroup,\n useFocusable,\n useFocusGroup,\n useFocusContext,\n resolveKeyAction,\n} from './focus'\n\nexport type {\n FocusableId,\n FocusableNode,\n FocusDirection,\n FocusGroupType,\n FocusManagerOptions,\n FocusState,\n UseFocusableOptions,\n UseFocusableResult,\n UseFocusGroupOptions,\n} from './focus'\n","import type {\n FocusableId,\n FocusableNode,\n FocusDirection,\n FocusGroup,\n FocusManagerOptions,\n FocusState,\n} from './types'\n\nexport class FocusManager {\n private state: FocusState = {\n focusedId: null,\n nodes: new Map(),\n groups: new Map(),\n }\n\n private options: FocusManagerOptions\n\n constructor(options: FocusManagerOptions = {}) {\n this.options = options\n }\n\n registerNode(node: FocusableNode): void {\n this.state.nodes.set(node.id, node)\n if (\n this.state.focusedId === null &&\n this.options.initialFocusId === node.id\n ) {\n this.setFocus(node.id)\n }\n }\n\n unregisterNode(id: FocusableId): void {\n this.state.nodes.delete(id)\n if (this.state.focusedId === id) {\n this.state.focusedId = null\n this.options.onFocusChange?.(null)\n }\n }\n\n registerGroup(group: FocusGroup): void {\n this.state.groups.set(group.id, group)\n }\n\n unregisterGroup(id: FocusableId): void {\n this.state.groups.delete(id)\n }\n\n setFocus(id: FocusableId): void {\n const node = this.state.nodes.get(id)\n if (!node || node.disabled) return\n\n const prev = this.state.focusedId\n if (prev) {\n const prevNode = this.state.nodes.get(prev)\n prevNode?.onBlur?.()\n prevNode?.el.blur()\n\n if (prevNode?.groupId) {\n const group = this.state.groups.get(prevNode.groupId)\n if (group) group.lastFocusedId = prev\n }\n }\n\n this.state.focusedId = id\n node.el.focus({ preventScroll: true })\n node.onFocus?.()\n if (typeof window !== 'undefined') {\n window.requestAnimationFrame(() => show(node.el))\n } else {\n show(node.el)\n }\n this.options.onFocusChange?.(id)\n }\n\n move(direction: FocusDirection): void {\n const currentId = this.state.focusedId\n if (!currentId) {\n this.focusFirst()\n return\n }\n\n const current = this.state.nodes.get(currentId)\n if (!current) return\n\n const groupId = current.groupId\n const group = groupId ? this.state.groups.get(groupId) : null\n\n const next = group\n ? this.findNextInGroup(currentId, direction, group)\n : this.findNextSpatial(currentId, direction)\n\n if (next) this.setFocus(next)\n }\n\n select(): void {\n const node = this.state.focusedId\n ? this.state.nodes.get(this.state.focusedId)\n : null\n node?.onSelect?.()\n node?.el.click()\n }\n\n focusFirst(): void {\n const first = this.state.nodes.values().next().value as FocusableNode | undefined\n if (first) this.setFocus(first.id)\n }\n\n getFocusedId(): FocusableId | null {\n return this.state.focusedId\n }\n\n private findNextInGroup(\n currentId: FocusableId,\n direction: FocusDirection,\n group: FocusGroup,\n ): FocusableId | null {\n const siblings = [...this.state.nodes.values()].filter(\n (n) => n.groupId === group.id && !n.disabled,\n )\n const index = siblings.findIndex((n) => n.id === currentId)\n if (index === -1) return null\n\n const isForward =\n (group.orientation === 'horizontal' && direction === 'right') ||\n (group.orientation === 'vertical' && direction === 'down')\n\n const isBackward =\n (group.orientation === 'horizontal' && direction === 'left') ||\n (group.orientation === 'vertical' && direction === 'up')\n\n if (isForward) {\n if (index < siblings.length - 1) return siblings[index + 1]?.id ?? null\n if (group.loop) return siblings[0]?.id ?? null\n return null\n }\n\n if (isBackward) {\n if (index > 0) return siblings[index - 1]?.id ?? null\n if (group.loop) return siblings[siblings.length - 1]?.id ?? null\n return null\n }\n\n return this.findNextSpatial(currentId, direction)\n }\n\n private findNextSpatial(currentId: FocusableId, direction: FocusDirection): FocusableId | null {\n const current = this.state.nodes.get(currentId)\n if (!current) return null\n\n const currentRect = current.el.getBoundingClientRect()\n const candidates = [...this.state.nodes.values()].filter(\n (n) => n.id !== currentId && !n.disabled,\n )\n\n const inDirection = candidates.filter((n) => {\n const rect = n.el.getBoundingClientRect()\n if (direction === 'right') return rect.left > currentRect.right - 1\n if (direction === 'left') return rect.right < currentRect.left + 1\n if (direction === 'down') return rect.top > currentRect.bottom - 1\n if (direction === 'up') return rect.bottom < currentRect.top + 1\n return false\n })\n\n if (inDirection.length === 0) return null\n\n return inDirection.reduce((best, node) => {\n const a = node.el.getBoundingClientRect()\n const b = best.el.getBoundingClientRect()\n const distA = distance(currentRect, a, direction)\n const distB = distance(currentRect, b, direction)\n return distA < distB ? node : best\n }).id\n }\n}\n\nfunction show(el: HTMLElement) {\n try {\n el.scrollIntoView({\n block: 'nearest',\n inline: 'nearest',\n })\n } catch {\n el.scrollIntoView()\n }\n}\n\nfunction distance(\n from: DOMRect,\n to: DOMRect,\n direction: FocusDirection,\n): number {\n const fromCx = from.left + from.width / 2\n const fromCy = from.top + from.height / 2\n const toCx = to.left + to.width / 2\n const toCy = to.top + to.height / 2\n\n const primary =\n direction === 'right' || direction === 'left'\n ? Math.abs(toCx - fromCx)\n : Math.abs(toCy - fromCy)\n\n const secondary =\n direction === 'right' || direction === 'left'\n ? Math.abs(toCy - fromCy)\n : Math.abs(toCx - fromCx)\n\n return primary + secondary * 0.5\n}\n","import React, { useEffect, useMemo, useRef } from 'react'\nimport { FocusContext } from './context'\nimport { FocusManager } from './FocusManager'\nimport { KeyHandler } from './KeyHandler'\nimport type { FocusManagerOptions } from './types'\n\ninterface FocusProviderProps {\n children: React.ReactNode\n options?: FocusManagerOptions\n}\n\nexport function FocusProvider({ children, options }: FocusProviderProps) {\n const managerRef = useRef<FocusManager | null>(null)\n const keyHandlerRef = useRef<KeyHandler | null>(null)\n\n if (!managerRef.current) {\n managerRef.current = new FocusManager(options ?? {})\n }\n if (!keyHandlerRef.current) {\n keyHandlerRef.current = new KeyHandler()\n }\n\n const manager = managerRef.current\n const keyHandler = keyHandlerRef.current\n\n useEffect(() => {\n keyHandler.mount()\n\n const unsub = keyHandler.subscribe((action) => {\n if (action.type === 'move') {\n manager.move(action.direction)\n }\n if (action.type === 'select') {\n manager.select()\n }\n })\n\n return () => {\n unsub()\n keyHandler.unmount()\n }\n }, [manager, keyHandler])\n\n const value = useMemo(\n () => ({ manager, keyHandler }),\n [manager, keyHandler],\n )\n\n return (\n <FocusContext.Provider value={value}>\n {children}\n </FocusContext.Provider>\n )\n}","import { createContext, useContext } from 'react'\nimport type { FocusManager } from './FocusManager'\nimport type { KeyHandler } from './KeyHandler'\n\nexport interface FocusContextValue {\n manager: FocusManager\n keyHandler: KeyHandler\n}\n\nexport const FocusContext = createContext<FocusContextValue | null>(null)\n\nexport function useFocusContext(): FocusContextValue {\n const ctx = useContext(FocusContext)\n if (ctx === null) {\n throw new Error('useFocusContext must be used within a TableApp')\n }\n return ctx\n}","import type { FocusDirection } from './types'\n\nexport type KeyAction =\n | { type: 'move'; direction: FocusDirection }\n | { type: 'select' }\n | { type: 'back' }\n | { type: 'unknown' }\n\nconst KEY_MAP: Record<string, KeyAction> = {\n ArrowUp: { type: 'move', direction: 'up' },\n ArrowDown: { type: 'move', direction: 'down' },\n ArrowLeft: { type: 'move', direction: 'left' },\n ArrowRight: { type: 'move', direction: 'right' },\n Enter: { type: 'select' },\n ' ': { type: 'select' },\n Backspace: { type: 'back' },\n Escape: { type: 'back' },\n}\n\nconst TIZEN_KEY_MAP: Record<number, KeyAction> = {\n 38: { type: 'move', direction: 'up' },\n 40: { type: 'move', direction: 'down' },\n 37: { type: 'move', direction: 'left' },\n 39: { type: 'move', direction: 'right' },\n 13: { type: 'select' },\n 10009: { type: 'back' },\n}\n\nconst WEBOS_KEY_MAP: Record<number, KeyAction> = {\n 38: { type: 'move', direction: 'up' },\n 40: { type: 'move', direction: 'down' },\n 37: { type: 'move', direction: 'left' },\n 39: { type: 'move', direction: 'right' },\n 13: { type: 'select' },\n 461: { type: 'back' },\n}\n\nconst ANDROID_TV_KEY_MAP: Record<number, KeyAction> = {\n 38: { type: 'move', direction: 'up' },\n 40: { type: 'move', direction: 'down' },\n 37: { type: 'move', direction: 'left' },\n 39: { type: 'move', direction: 'right' },\n 13: { type: 'select' },\n 85: { type: 'select' },\n 4: { type: 'back' },\n}\n\nfunction detectPlatform(): 'tizen' | 'webos' | 'android' | 'web' {\n if (typeof window === 'undefined') return 'web'\n const ua = navigator.userAgent\n if (ua.includes('Tizen')) return 'tizen'\n if (ua.includes('Web0S') || ua.includes('webOS')) return 'webos'\n if (ua.includes('Android')) return 'android'\n return 'web'\n}\n\nexport function resolveKeyAction(event: KeyboardEvent): KeyAction {\n const byKey = KEY_MAP[event.key]\n if (byKey) return byKey\n\n const platform = detectPlatform()\n\n const platformMap =\n platform === 'tizen' ? TIZEN_KEY_MAP :\n platform === 'webos' ? WEBOS_KEY_MAP :\n platform === 'android' ? ANDROID_TV_KEY_MAP :\n {}\n\n return platformMap[event.keyCode] ?? { type: 'unknown' }\n}\n\nexport class KeyHandler {\n private listeners = new Set<(action: KeyAction) => void>()\n private bound: ((e: KeyboardEvent) => void) | null = null\n\n mount(): void {\n this.bound = (e: KeyboardEvent) => {\n const action = resolveKeyAction(e)\n if (action.type === 'unknown') return\n e.preventDefault()\n this.listeners.forEach((fn) => fn(action))\n }\n window.addEventListener('keydown', this.bound)\n }\n\n unmount(): void {\n if (this.bound) {\n window.removeEventListener('keydown', this.bound)\n this.bound = null\n }\n }\n\n subscribe(fn: (action: KeyAction) => void): () => void {\n this.listeners.add(fn)\n return () => this.listeners.delete(fn)\n }\n}","import {\n useCallback,\n useEffect,\n useId,\n useRef,\n useState,\n} from 'react'\nimport { useFocusContext } from './context'\nimport type { FocusGroup } from './types'\n\nexport interface UseFocusableOptions {\n groupId?: string\n disabled?: boolean\n onFocus?: () => void\n onBlur?: () => void\n onSelect?: () => void\n autoFocus?: boolean\n}\n\nexport interface UseFocusableResult {\n ref: React.RefObject<HTMLElement>\n focused: boolean\n focusableProps: {\n 'data-focusable': string\n 'data-focused': boolean\n tabIndex: number\n }\n}\n\nexport function useFocusable(options: UseFocusableOptions = {}): UseFocusableResult {\n const id = useId()\n const ref = useRef<HTMLElement>(null)\n const [focused, setFocused] = useState(false)\n const { manager } = useFocusContext()\n\n const { groupId, disabled = false, onFocus, onBlur, onSelect, autoFocus } = options\n\n useEffect(() => {\n const el = ref.current\n if (!el) return\n\n manager.registerNode({\n id,\n el,\n groupId: groupId ?? null,\n disabled,\n onFocus: () => {\n setFocused(true)\n onFocus?.()\n },\n onBlur: () => {\n setFocused(false)\n onBlur?.()\n },\n onSelect: onSelect || (() => {}),\n })\n\n if (autoFocus) {\n manager.setFocus(id)\n }\n\n return () => {\n manager.unregisterNode(id)\n }\n }, [id, groupId, disabled, autoFocus, manager, onFocus, onBlur, onSelect])\n\n const focusableProps = {\n 'data-focusable': id,\n 'data-focused': focused,\n tabIndex: focused ? 0 : -1,\n }\n\n return { ref: ref as React.RefObject<HTMLElement>, focused, focusableProps }\n}\n\nexport type UseFocusGroupOptions = Omit<FocusGroup, 'id' | 'lastFocusedId' | 'parentId'>\n\nexport function useFocusGroup(options: UseFocusGroupOptions) {\n const id = useId()\n const { manager } = useFocusContext()\n\n useEffect(() => {\n manager.registerGroup({\n id,\n parentId: null,\n lastFocusedId: null,\n ...options,\n })\n return () => {\n manager.unregisterGroup(id)\n }\n }, [id, manager, options.orientation, options.loop, options.rememberFocus])\n\n return { groupId: id }\n}","import React, { forwardRef } from 'react'\nimport type { JSX } from 'react'\nimport { useFocusable, useFocusGroup } from './hooks'\nimport type { UseFocusableOptions, UseFocusGroupOptions } from './hooks'\n\ninterface FocusableProps extends UseFocusableOptions {\n children: React.ReactNode | ((focused: boolean) => React.ReactNode)\n as?: React.ElementType\n className?: string\n style?: React.CSSProperties\n}\n\nexport const Focusable = forwardRef<HTMLElement, FocusableProps>(\n ({ children, as: Tag = 'div', className, style, ...options }, _ref) => {\n const { ref, focused, focusableProps } = useFocusable(options)\n\n return (\n <Tag\n ref={ref as never}\n className={className}\n style={style}\n {...focusableProps}\n >\n {typeof children === 'function' ? children(focused) : children}\n </Tag>\n )\n },\n)\n\nFocusable.displayName = 'Focusable'\n\ninterface FocusGroupProps extends UseFocusGroupOptions {\n children: React.ReactNode\n as?: React.ElementType\n className?: string\n style?: React.CSSProperties\n}\n\nexport function FocusGroup({\n children,\n as: Tag = 'div',\n className,\n style,\n ...options\n}: FocusGroupProps) {\n const { groupId } = useFocusGroup(options)\n const css =\n options.orientation === 'grid'\n ? style\n : {\n display: 'flex',\n flexDirection: options.orientation === 'vertical' ? 'column' : 'row',\n ...style,\n }\n\n return (\n <Tag\n className={className}\n style={css}\n data-focus-group={groupId}\n >\n {children}\n </Tag>\n )\n}\n","import React, { useCallback, useEffect, useState, useTransition } from 'react'\nimport type { NavigateOptions, Route, RouteDefinition, RouteQuery } from './types'\nimport { matchRoute } from './matcher'\nimport { RouterContext } from './context'\n\ninterface RouterProps {\n routes: RouteDefinition[]\n initialPath?: string\n}\n\nfunction parseQuery(search: string): RouteQuery {\n const query: RouteQuery = {}\n const params = new URLSearchParams(search)\n params.forEach((value, key) => {\n const existing = query[key]\n if (existing === undefined) {\n query[key] = value\n } else if (Array.isArray(existing)) {\n existing.push(value)\n } else {\n query[key] = [existing, value]\n }\n })\n return query\n}\n\nfunction buildRoute(pathname: string, search: string, params: Record<string, string>): Route {\n return {\n path: pathname,\n params,\n query: parseQuery(search),\n }\n}\n\nexport function Router({ routes, initialPath = '/' }: RouterProps) {\n const [, startTransition] = useTransition()\n const [currentPath, setCurrentPath] = useState(() => {\n if (typeof window !== 'undefined') return window.location.pathname\n return initialPath\n })\n const [search, setSearch] = useState(() => {\n if (typeof window !== 'undefined') return window.location.search\n return ''\n })\n const [history, setHistory] = useState<Route[]>([])\n\n const match = matchRoute(currentPath, routes)\n const route = match\n ? buildRoute(currentPath, search, match.params)\n : buildRoute(currentPath, search, {})\n\n const navigate = useCallback(\n (path: string, options: NavigateOptions = {}) => {\n const [pathname, queryString] = path.split('?')\n const nextSearch = queryString ? `?${queryString}` : ''\n\n startTransition(() => {\n if (options.replace) {\n window.history.replaceState(null, '', path)\n } else {\n window.history.pushState(null, '', path)\n setHistory((prev) => [...prev, route])\n }\n setCurrentPath(pathname ?? '/')\n setSearch(nextSearch)\n })\n },\n [route],\n )\n\n const back = useCallback(() => {\n if (history.length === 0) return\n const prev = history[history.length - 1]\n if (!prev) return\n setHistory((h) => h.slice(0, -1))\n window.history.back()\n setCurrentPath(prev.path)\n setSearch('')\n }, [history])\n\n const prefetch = useCallback(\n (path: string) => {\n const [pathname] = path.split('?')\n if (!pathname) return\n const result = matchRoute(pathname, routes)\n if (result) {\n void result.route.component\n }\n },\n [routes],\n )\n\n useEffect(() => {\n const onPopState = () => {\n setCurrentPath(window.location.pathname)\n setSearch(window.location.search)\n }\n\n window.addEventListener('popstate', onPopState)\n return () => window.removeEventListener('popstate', onPopState)\n }, [])\n\n if (!match) {\n return <NotFound path={currentPath} />\n }\n\n const { component: Page } = match.route\n\n return (\n <RouterContext.Provider value={{ route, navigate, back, prefetch }}>\n <React.Suspense fallback={<PageLoader />}>\n <Page />\n </React.Suspense>\n </RouterContext.Provider>\n )\n}\n\nfunction NotFound({ path }: { path: string }) {\n return (\n <div data-table-not-found>\n <span>404 — {path} not found</span>\n </div>\n )\n}\n\nfunction PageLoader() {\n return <div data-table-loader />\n}\n","import type { RouteDefinition, RouteParams, RouteSegment } from './types'\n\nexport function parseSegments(path: string): RouteSegment[] {\n return path\n .split('/')\n .filter(Boolean)\n .map((segment) => {\n if (segment.startsWith('[...') && segment.endsWith(']')) {\n return { type: 'catch-all', value: segment.slice(4, -1) }\n }\n if (segment.startsWith('[') && segment.endsWith(']')) {\n return { type: 'dynamic', value: segment.slice(1, -1) }\n }\n return { type: 'static', value: segment }\n })\n}\n\nexport function matchRoute(\n pathname: string,\n routes: RouteDefinition[],\n): { route: RouteDefinition; params: RouteParams } | null {\n const incomingSegments = pathname.split(\"/\").filter(Boolean)\n\n for (const route of routes) {\n const result = matchSegments(incomingSegments, route.segments)\n if (result !== null) {\n return { route, params: result }\n }\n }\n\n return null\n}\n\nfunction matchSegments(incoming: string[], segments: RouteSegment[]): RouteParams | null {\n const params: RouteParams = {}\n let i = 0\n\n for (const segment of segments) {\n if (segment.type === 'catch-all') {\n params[segment.value] = incoming.slice(i).join('/')\n return params\n }\n\n const part = incoming[i]\n if (part === undefined) return null\n\n if (segment.type === 'static') {\n if (part !== segment.value) return null\n }\n\n if (segment.type === 'dynamic') {\n params[segment.value] = part\n }\n\n i++\n }\n\n if (i !== incoming.length) return null\n\n return params\n}\n","import { createContext, useContext } from 'react'\nimport type { NavigateOptions, Route } from './types'\n\nexport interface RouterContextValue {\n route: Route\n navigate: (path: string, options?: NavigateOptions) => void\n back: () => void\n prefetch: (path: string) => void\n}\n\nexport const RouterContext = createContext<RouterContextValue | null>(null)\n\nexport function useRouter(): RouterContextValue {\n const ctx = useContext(RouterContext)\n if (ctx === null) {\n throw new Error('useRouter must be used within a TableApp')\n }\n return ctx\n}\n\nexport function useParams<T extends Record<string, string>>(): T {\n const { route } = useRouter()\n return route.params as T\n}\n\nexport function useQuery<T extends Record<string, string | string[] | undefined>>(): T {\n const { route } = useRouter()\n return route.query as T\n}\n","import type { RouteDefinition } from './router/types'\nimport type { FocusManagerOptions } from './focus/types'\nimport { FocusProvider } from './focus'\nimport { Router } from './router/Router'\n\nexport interface TableAppProps {\n routes: RouteDefinition[]\n initialPath?: string\n focus?: FocusManagerOptions\n}\n\nexport function TableApp({ routes, initialPath, focus }: TableAppProps) {\n return (\n <FocusProvider {...(focus && { options: focus })}>\n <Router routes={routes} {...(initialPath && { initialPath })} />\n </FocusProvider>\n )\n}","import React from 'react'\nimport { parseSegments } from './matcher'\nimport type { RouteDefinition, RouteSegment } from './types'\n\nexport type RouteModule = {\n default: React.ComponentType\n}\n\nexport type RouteModuleLoader = () => Promise<RouteModule>\n\ntype RouteInput = {\n path: string\n component: RouteModuleLoader\n}\n\nexport function defineRoutes(routes: RouteInput[]): RouteDefinition[] {\n return routes.map((route) => createRouteDefinition(route.path, route.component, route.path))\n}\n\nexport function defineFileRoutes(\n routeModules: Record<string, RouteModuleLoader>,\n options: { appDir?: string } = {},\n): RouteDefinition[] {\n const appDir = options.appDir ?? 'app'\n\n return Object.entries(routeModules)\n .map(([filePath, component]) =>\n createRouteDefinition(normalizeRoutePath(filePath, appDir), component, filePath),\n )\n .sort(compareRouteDefinitions)\n}\n\nfunction createRouteDefinition(\n path: string,\n component: RouteModuleLoader,\n filePath: string,\n): RouteDefinition {\n return {\n filePath,\n segments: parseSegments(path),\n component: React.lazy(component),\n }\n}\n\nfunction normalizeRoutePath(filePath: string, appDir: string): string {\n const normalizedPath = filePath.replace(/\\\\/g, '/').replace(/^\\.\\//, '')\n const appDirPrefix = `${appDir}/`\n const appDirIndex = normalizedPath.indexOf(appDirPrefix)\n\n if (appDirIndex === -1) {\n throw new Error(`Route module \"${filePath}\" must be inside \"${appDir}/\"`)\n }\n\n const pagePath = normalizedPath.slice(appDirIndex + appDirPrefix.length)\n const routePath = pagePath.replace(/(?:^|\\/)page\\.[^/.]+$/, '')\n\n return routePath.length === 0 ? '/' : `/${routePath}`\n}\n\nfunction compareRouteDefinitions(left: RouteDefinition, right: RouteDefinition): number {\n const segmentsOrder = compareSegments(left.segments, right.segments)\n if (segmentsOrder !== 0) {\n return segmentsOrder\n }\n\n return left.filePath.localeCompare(right.filePath)\n}\n\nfunction compareSegments(left: RouteSegment[], right: RouteSegment[]): number {\n const length = Math.max(left.length, right.length)\n\n for (let index = 0; index < length; index++) {\n const leftSegment = left[index]\n const rightSegment = right[index]\n\n if (leftSegment === undefined) return -1\n if (rightSegment === undefined) return 1\n\n const rankDifference = segmentRank(rightSegment) - segmentRank(leftSegment)\n if (rankDifference !== 0) {\n return rankDifference\n }\n\n if (leftSegment.type === 'static' && rightSegment.type === 'static') {\n const valueDifference = leftSegment.value.localeCompare(rightSegment.value)\n if (valueDifference !== 0) {\n return valueDifference\n }\n }\n }\n\n return 0\n}\n\nfunction segmentRank(segment: RouteSegment): number {\n switch (segment.type) {\n case 'static':\n return 3\n case 'dynamic':\n return 2\n case 'catch-all':\n return 1\n }\n}\n"],"mappings":"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","names":["import_react","import_react","import_react","import_jsx_runtime","import_react","import_react","import_jsx_runtime","React","import_jsx_runtime","import_react","React"]}
|
package/dist/index.mjs
CHANGED
|
@@ -43,6 +43,11 @@ var FocusManager = class {
|
|
|
43
43
|
this.state.focusedId = id;
|
|
44
44
|
node.el.focus({ preventScroll: true });
|
|
45
45
|
node.onFocus?.();
|
|
46
|
+
if (typeof window !== "undefined") {
|
|
47
|
+
window.requestAnimationFrame(() => show(node.el));
|
|
48
|
+
} else {
|
|
49
|
+
show(node.el);
|
|
50
|
+
}
|
|
46
51
|
this.options.onFocusChange?.(id);
|
|
47
52
|
}
|
|
48
53
|
move(direction) {
|
|
@@ -115,6 +120,16 @@ var FocusManager = class {
|
|
|
115
120
|
}).id;
|
|
116
121
|
}
|
|
117
122
|
};
|
|
123
|
+
function show(el) {
|
|
124
|
+
try {
|
|
125
|
+
el.scrollIntoView({
|
|
126
|
+
block: "nearest",
|
|
127
|
+
inline: "nearest"
|
|
128
|
+
});
|
|
129
|
+
} catch {
|
|
130
|
+
el.scrollIntoView();
|
|
131
|
+
}
|
|
132
|
+
}
|
|
118
133
|
function distance(from, to, direction) {
|
|
119
134
|
const fromCx = from.left + from.width / 2;
|
|
120
135
|
const fromCy = from.top + from.height / 2;
|
|
@@ -341,11 +356,16 @@ function FocusGroup({
|
|
|
341
356
|
...options
|
|
342
357
|
}) {
|
|
343
358
|
const { groupId } = useFocusGroup(options);
|
|
359
|
+
const css = options.orientation === "grid" ? style : {
|
|
360
|
+
display: "flex",
|
|
361
|
+
flexDirection: options.orientation === "vertical" ? "column" : "row",
|
|
362
|
+
...style
|
|
363
|
+
};
|
|
344
364
|
return /* @__PURE__ */ jsx2(
|
|
345
365
|
Tag,
|
|
346
366
|
{
|
|
347
367
|
className,
|
|
348
|
-
style,
|
|
368
|
+
style: css,
|
|
349
369
|
"data-focus-group": groupId,
|
|
350
370
|
children
|
|
351
371
|
}
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/focus/FocusManager.ts","../src/focus/FocusProvider.tsx","../src/focus/context.ts","../src/focus/KeyHandler.ts","../src/focus/hooks.ts","../src/focus/components.tsx","../src/router/Router.tsx","../src/router/matcher.ts","../src/router/context.ts","../src/TableApp.tsx","../src/router/defineRoutes.ts"],"sourcesContent":["import type {\n FocusableId,\n FocusableNode,\n FocusDirection,\n FocusGroup,\n FocusManagerOptions,\n FocusState,\n} from './types'\n\nexport class FocusManager {\n private state: FocusState = {\n focusedId: null,\n nodes: new Map(),\n groups: new Map(),\n }\n\n private options: FocusManagerOptions\n\n constructor(options: FocusManagerOptions = {}) {\n this.options = options\n }\n\n registerNode(node: FocusableNode): void {\n this.state.nodes.set(node.id, node)\n if (\n this.state.focusedId === null &&\n this.options.initialFocusId === node.id\n ) {\n this.setFocus(node.id)\n }\n }\n\n unregisterNode(id: FocusableId): void {\n this.state.nodes.delete(id)\n if (this.state.focusedId === id) {\n this.state.focusedId = null\n this.options.onFocusChange?.(null)\n }\n }\n\n registerGroup(group: FocusGroup): void {\n this.state.groups.set(group.id, group)\n }\n\n unregisterGroup(id: FocusableId): void {\n this.state.groups.delete(id)\n }\n\n setFocus(id: FocusableId): void {\n const node = this.state.nodes.get(id)\n if (!node || node.disabled) return\n\n const prev = this.state.focusedId\n if (prev) {\n const prevNode = this.state.nodes.get(prev)\n prevNode?.onBlur?.()\n prevNode?.el.blur()\n\n if (prevNode?.groupId) {\n const group = this.state.groups.get(prevNode.groupId)\n if (group) group.lastFocusedId = prev\n }\n }\n\n this.state.focusedId = id\n node.el.focus({ preventScroll: true })\n node.onFocus?.()\n this.options.onFocusChange?.(id)\n }\n\n move(direction: FocusDirection): void {\n const currentId = this.state.focusedId\n if (!currentId) {\n this.focusFirst()\n return\n }\n\n const current = this.state.nodes.get(currentId)\n if (!current) return\n\n const groupId = current.groupId\n const group = groupId ? this.state.groups.get(groupId) : null\n\n const next = group\n ? this.findNextInGroup(currentId, direction, group)\n : this.findNextSpatial(currentId, direction)\n\n if (next) this.setFocus(next)\n }\n\n select(): void {\n const node = this.state.focusedId\n ? this.state.nodes.get(this.state.focusedId)\n : null\n node?.onSelect?.()\n node?.el.click()\n }\n\n focusFirst(): void {\n const first = this.state.nodes.values().next().value as FocusableNode | undefined\n if (first) this.setFocus(first.id)\n }\n\n getFocusedId(): FocusableId | null {\n return this.state.focusedId\n }\n\n private findNextInGroup(\n currentId: FocusableId,\n direction: FocusDirection,\n group: FocusGroup,\n ): FocusableId | null {\n const siblings = [...this.state.nodes.values()].filter(\n (n) => n.groupId === group.id && !n.disabled,\n )\n const index = siblings.findIndex((n) => n.id === currentId)\n if (index === -1) return null\n\n const isForward =\n (group.orientation === 'horizontal' && direction === 'right') ||\n (group.orientation === 'vertical' && direction === 'down')\n\n const isBackward =\n (group.orientation === 'horizontal' && direction === 'left') ||\n (group.orientation === 'vertical' && direction === 'up')\n\n if (isForward) {\n if (index < siblings.length - 1) return siblings[index + 1]?.id ?? null\n if (group.loop) return siblings[0]?.id ?? null\n return null\n }\n\n if (isBackward) {\n if (index > 0) return siblings[index - 1]?.id ?? null\n if (group.loop) return siblings[siblings.length - 1]?.id ?? null\n return null\n }\n\n return this.findNextSpatial(currentId, direction)\n }\n\n private findNextSpatial(currentId: FocusableId, direction: FocusDirection): FocusableId | null {\n const current = this.state.nodes.get(currentId)\n if (!current) return null\n\n const currentRect = current.el.getBoundingClientRect()\n const candidates = [...this.state.nodes.values()].filter(\n (n) => n.id !== currentId && !n.disabled,\n )\n\n const inDirection = candidates.filter((n) => {\n const rect = n.el.getBoundingClientRect()\n if (direction === 'right') return rect.left > currentRect.right - 1\n if (direction === 'left') return rect.right < currentRect.left + 1\n if (direction === 'down') return rect.top > currentRect.bottom - 1\n if (direction === 'up') return rect.bottom < currentRect.top + 1\n return false\n })\n\n if (inDirection.length === 0) return null\n\n return inDirection.reduce((best, node) => {\n const a = node.el.getBoundingClientRect()\n const b = best.el.getBoundingClientRect()\n const distA = distance(currentRect, a, direction)\n const distB = distance(currentRect, b, direction)\n return distA < distB ? node : best\n }).id\n }\n}\n\n\nfunction distance(\n from: DOMRect,\n to: DOMRect,\n direction: FocusDirection,\n): number {\n const fromCx = from.left + from.width / 2\n const fromCy = from.top + from.height / 2\n const toCx = to.left + to.width / 2\n const toCy = to.top + to.height / 2\n\n const primary =\n direction === 'right' || direction === 'left'\n ? Math.abs(toCx - fromCx)\n : Math.abs(toCy - fromCy)\n\n const secondary =\n direction === 'right' || direction === 'left'\n ? Math.abs(toCy - fromCy)\n : Math.abs(toCx - fromCx)\n\n return primary + secondary * 0.5\n}","import React, { useEffect, useMemo, useRef } from 'react'\nimport { FocusContext } from './context'\nimport { FocusManager } from './FocusManager'\nimport { KeyHandler } from './KeyHandler'\nimport type { FocusManagerOptions } from './types'\n\ninterface FocusProviderProps {\n children: React.ReactNode\n options?: FocusManagerOptions\n}\n\nexport function FocusProvider({ children, options }: FocusProviderProps) {\n const managerRef = useRef<FocusManager | null>(null)\n const keyHandlerRef = useRef<KeyHandler | null>(null)\n\n if (!managerRef.current) {\n managerRef.current = new FocusManager(options ?? {})\n }\n if (!keyHandlerRef.current) {\n keyHandlerRef.current = new KeyHandler()\n }\n\n const manager = managerRef.current\n const keyHandler = keyHandlerRef.current\n\n useEffect(() => {\n keyHandler.mount()\n\n const unsub = keyHandler.subscribe((action) => {\n if (action.type === 'move') {\n manager.move(action.direction)\n }\n if (action.type === 'select') {\n manager.select()\n }\n })\n\n return () => {\n unsub()\n keyHandler.unmount()\n }\n }, [manager, keyHandler])\n\n const value = useMemo(\n () => ({ manager, keyHandler }),\n [manager, keyHandler],\n )\n\n return (\n <FocusContext.Provider value={value}>\n {children}\n </FocusContext.Provider>\n )\n}","import { createContext, useContext } from 'react'\nimport type { FocusManager } from './FocusManager'\nimport type { KeyHandler } from './KeyHandler'\n\nexport interface FocusContextValue {\n manager: FocusManager\n keyHandler: KeyHandler\n}\n\nexport const FocusContext = createContext<FocusContextValue | null>(null)\n\nexport function useFocusContext(): FocusContextValue {\n const ctx = useContext(FocusContext)\n if (ctx === null) {\n throw new Error('useFocusContext must be used within a TableApp')\n }\n return ctx\n}","import type { FocusDirection } from './types'\n\nexport type KeyAction =\n | { type: 'move'; direction: FocusDirection }\n | { type: 'select' }\n | { type: 'back' }\n | { type: 'unknown' }\n\nconst KEY_MAP: Record<string, KeyAction> = {\n ArrowUp: { type: 'move', direction: 'up' },\n ArrowDown: { type: 'move', direction: 'down' },\n ArrowLeft: { type: 'move', direction: 'left' },\n ArrowRight: { type: 'move', direction: 'right' },\n Enter: { type: 'select' },\n ' ': { type: 'select' },\n Backspace: { type: 'back' },\n Escape: { type: 'back' },\n}\n\nconst TIZEN_KEY_MAP: Record<number, KeyAction> = {\n 38: { type: 'move', direction: 'up' },\n 40: { type: 'move', direction: 'down' },\n 37: { type: 'move', direction: 'left' },\n 39: { type: 'move', direction: 'right' },\n 13: { type: 'select' },\n 10009: { type: 'back' },\n}\n\nconst WEBOS_KEY_MAP: Record<number, KeyAction> = {\n 38: { type: 'move', direction: 'up' },\n 40: { type: 'move', direction: 'down' },\n 37: { type: 'move', direction: 'left' },\n 39: { type: 'move', direction: 'right' },\n 13: { type: 'select' },\n 461: { type: 'back' },\n}\n\nconst ANDROID_TV_KEY_MAP: Record<number, KeyAction> = {\n 38: { type: 'move', direction: 'up' },\n 40: { type: 'move', direction: 'down' },\n 37: { type: 'move', direction: 'left' },\n 39: { type: 'move', direction: 'right' },\n 13: { type: 'select' },\n 85: { type: 'select' },\n 4: { type: 'back' },\n}\n\nfunction detectPlatform(): 'tizen' | 'webos' | 'android' | 'web' {\n if (typeof window === 'undefined') return 'web'\n const ua = navigator.userAgent\n if (ua.includes('Tizen')) return 'tizen'\n if (ua.includes('Web0S') || ua.includes('webOS')) return 'webos'\n if (ua.includes('Android')) return 'android'\n return 'web'\n}\n\nexport function resolveKeyAction(event: KeyboardEvent): KeyAction {\n const byKey = KEY_MAP[event.key]\n if (byKey) return byKey\n\n const platform = detectPlatform()\n\n const platformMap =\n platform === 'tizen' ? TIZEN_KEY_MAP :\n platform === 'webos' ? WEBOS_KEY_MAP :\n platform === 'android' ? ANDROID_TV_KEY_MAP :\n {}\n\n return platformMap[event.keyCode] ?? { type: 'unknown' }\n}\n\nexport class KeyHandler {\n private listeners = new Set<(action: KeyAction) => void>()\n private bound: ((e: KeyboardEvent) => void) | null = null\n\n mount(): void {\n this.bound = (e: KeyboardEvent) => {\n const action = resolveKeyAction(e)\n if (action.type === 'unknown') return\n e.preventDefault()\n this.listeners.forEach((fn) => fn(action))\n }\n window.addEventListener('keydown', this.bound)\n }\n\n unmount(): void {\n if (this.bound) {\n window.removeEventListener('keydown', this.bound)\n this.bound = null\n }\n }\n\n subscribe(fn: (action: KeyAction) => void): () => void {\n this.listeners.add(fn)\n return () => this.listeners.delete(fn)\n }\n}","import {\n useCallback,\n useEffect,\n useId,\n useRef,\n useState,\n} from 'react'\nimport { useFocusContext } from './context'\nimport type { FocusGroup } from './types'\n\nexport interface UseFocusableOptions {\n groupId?: string\n disabled?: boolean\n onFocus?: () => void\n onBlur?: () => void\n onSelect?: () => void\n autoFocus?: boolean\n}\n\nexport interface UseFocusableResult {\n ref: React.RefObject<HTMLElement>\n focused: boolean\n focusableProps: {\n 'data-focusable': string\n 'data-focused': boolean\n tabIndex: number\n }\n}\n\nexport function useFocusable(options: UseFocusableOptions = {}): UseFocusableResult {\n const id = useId()\n const ref = useRef<HTMLElement>(null)\n const [focused, setFocused] = useState(false)\n const { manager } = useFocusContext()\n\n const { groupId, disabled = false, onFocus, onBlur, onSelect, autoFocus } = options\n\n useEffect(() => {\n const el = ref.current\n if (!el) return\n\n manager.registerNode({\n id,\n el,\n groupId: groupId ?? null,\n disabled,\n onFocus: () => {\n setFocused(true)\n onFocus?.()\n },\n onBlur: () => {\n setFocused(false)\n onBlur?.()\n },\n onSelect: onSelect || (() => {}),\n })\n\n if (autoFocus) {\n manager.setFocus(id)\n }\n\n return () => {\n manager.unregisterNode(id)\n }\n }, [id, groupId, disabled, autoFocus, manager, onFocus, onBlur, onSelect])\n\n const focusableProps = {\n 'data-focusable': id,\n 'data-focused': focused,\n tabIndex: focused ? 0 : -1,\n }\n\n return { ref: ref as React.RefObject<HTMLElement>, focused, focusableProps }\n}\n\nexport type UseFocusGroupOptions = Omit<FocusGroup, 'id' | 'lastFocusedId' | 'parentId'>\n\nexport function useFocusGroup(options: UseFocusGroupOptions) {\n const id = useId()\n const { manager } = useFocusContext()\n\n useEffect(() => {\n manager.registerGroup({\n id,\n parentId: null,\n lastFocusedId: null,\n ...options,\n })\n return () => {\n manager.unregisterGroup(id)\n }\n }, [id, manager, options.orientation, options.loop, options.rememberFocus])\n\n return { groupId: id }\n}","import React, { forwardRef } from 'react'\nimport type { JSX } from 'react'\nimport { useFocusable, useFocusGroup } from './hooks'\nimport type { UseFocusableOptions, UseFocusGroupOptions } from './hooks'\n\ninterface FocusableProps extends UseFocusableOptions {\n children: React.ReactNode | ((focused: boolean) => React.ReactNode)\n as?: React.ElementType\n className?: string\n style?: React.CSSProperties\n}\n\nexport const Focusable = forwardRef<HTMLElement, FocusableProps>(\n ({ children, as: Tag = 'div', className, style, ...options }, _ref) => {\n const { ref, focused, focusableProps } = useFocusable(options)\n\n return (\n <Tag\n ref={ref as never}\n className={className}\n style={style}\n {...focusableProps}\n >\n {typeof children === 'function' ? children(focused) : children}\n </Tag>\n )\n },\n)\n\nFocusable.displayName = 'Focusable'\n\ninterface FocusGroupProps extends UseFocusGroupOptions {\n children: React.ReactNode\n as?: React.ElementType\n className?: string\n style?: React.CSSProperties\n}\n\nexport function FocusGroup({\n children,\n as: Tag = 'div',\n className,\n style,\n ...options\n}: FocusGroupProps) {\n const { groupId } = useFocusGroup(options)\n\n return (\n <Tag\n className={className}\n style={style}\n data-focus-group={groupId}\n >\n {children}\n </Tag>\n )\n}","import React, { useCallback, useEffect, useState, useTransition } from 'react'\nimport type { NavigateOptions, Route, RouteDefinition, RouteQuery } from './types'\nimport { matchRoute } from './matcher'\nimport { RouterContext } from './context'\n\ninterface RouterProps {\n routes: RouteDefinition[]\n initialPath?: string\n}\n\nfunction parseQuery(search: string): RouteQuery {\n const query: RouteQuery = {}\n const params = new URLSearchParams(search)\n params.forEach((value, key) => {\n const existing = query[key]\n if (existing === undefined) {\n query[key] = value\n } else if (Array.isArray(existing)) {\n existing.push(value)\n } else {\n query[key] = [existing, value]\n }\n })\n return query\n}\n\nfunction buildRoute(pathname: string, search: string, params: Record<string, string>): Route {\n return {\n path: pathname,\n params,\n query: parseQuery(search),\n }\n}\n\nexport function Router({ routes, initialPath = '/' }: RouterProps) {\n const [, startTransition] = useTransition()\n const [currentPath, setCurrentPath] = useState(() => {\n if (typeof window !== 'undefined') return window.location.pathname\n return initialPath\n })\n const [search, setSearch] = useState(() => {\n if (typeof window !== 'undefined') return window.location.search\n return ''\n })\n const [history, setHistory] = useState<Route[]>([])\n\n const match = matchRoute(currentPath, routes)\n const route = match\n ? buildRoute(currentPath, search, match.params)\n : buildRoute(currentPath, search, {})\n\n const navigate = useCallback(\n (path: string, options: NavigateOptions = {}) => {\n const [pathname, queryString] = path.split('?')\n const nextSearch = queryString ? `?${queryString}` : ''\n\n startTransition(() => {\n if (options.replace) {\n window.history.replaceState(null, '', path)\n } else {\n window.history.pushState(null, '', path)\n setHistory((prev) => [...prev, route])\n }\n setCurrentPath(pathname ?? '/')\n setSearch(nextSearch)\n })\n },\n [route],\n )\n\n const back = useCallback(() => {\n if (history.length === 0) return\n const prev = history[history.length - 1]\n if (!prev) return\n setHistory((h) => h.slice(0, -1))\n window.history.back()\n setCurrentPath(prev.path)\n setSearch('')\n }, [history])\n\n const prefetch = useCallback(\n (path: string) => {\n const [pathname] = path.split('?')\n if (!pathname) return\n const result = matchRoute(pathname, routes)\n if (result) {\n void result.route.component\n }\n },\n [routes],\n )\n\n useEffect(() => {\n const onPopState = () => {\n setCurrentPath(window.location.pathname)\n setSearch(window.location.search)\n }\n\n window.addEventListener('popstate', onPopState)\n return () => window.removeEventListener('popstate', onPopState)\n }, [])\n\n if (!match) {\n return <NotFound path={currentPath} />\n }\n\n const { component: Page } = match.route\n\n return (\n <RouterContext.Provider value={{ route, navigate, back, prefetch }}>\n <React.Suspense fallback={<PageLoader />}>\n <Page />\n </React.Suspense>\n </RouterContext.Provider>\n )\n}\n\nfunction NotFound({ path }: { path: string }) {\n return (\n <div data-table-not-found>\n <span>404 — {path} not found</span>\n </div>\n )\n}\n\nfunction PageLoader() {\n return <div data-table-loader />\n}\n","import type { RouteDefinition, RouteParams, RouteSegment } from './types'\n\nexport function parseSegments(path: string): RouteSegment[] {\n return path\n .split('/')\n .filter(Boolean)\n .map((segment) => {\n if (segment.startsWith('[...') && segment.endsWith(']')) {\n return { type: 'catch-all', value: segment.slice(4, -1) }\n }\n if (segment.startsWith('[') && segment.endsWith(']')) {\n return { type: 'dynamic', value: segment.slice(1, -1) }\n }\n return { type: 'static', value: segment }\n })\n}\n\nexport function matchRoute(\n pathname: string,\n routes: RouteDefinition[],\n): { route: RouteDefinition; params: RouteParams } | null {\n const incomingSegments = pathname.split(\"/\").filter(Boolean)\n\n for (const route of routes) {\n const result = matchSegments(incomingSegments, route.segments)\n if (result !== null) {\n return { route, params: result }\n }\n }\n\n return null\n}\n\nfunction matchSegments(incoming: string[], segments: RouteSegment[]): RouteParams | null {\n const params: RouteParams = {}\n let i = 0\n\n for (const segment of segments) {\n if (segment.type === 'catch-all') {\n params[segment.value] = incoming.slice(i).join('/')\n return params\n }\n\n const part = incoming[i]\n if (part === undefined) return null\n\n if (segment.type === 'static') {\n if (part !== segment.value) return null\n }\n\n if (segment.type === 'dynamic') {\n params[segment.value] = part\n }\n\n i++\n }\n\n if (i !== incoming.length) return null\n\n return params\n}\n","import { createContext, useContext } from 'react'\nimport type { NavigateOptions, Route } from './types'\n\nexport interface RouterContextValue {\n route: Route\n navigate: (path: string, options?: NavigateOptions) => void\n back: () => void\n prefetch: (path: string) => void\n}\n\nexport const RouterContext = createContext<RouterContextValue | null>(null)\n\nexport function useRouter(): RouterContextValue {\n const ctx = useContext(RouterContext)\n if (ctx === null) {\n throw new Error('useRouter must be used within a TableApp')\n }\n return ctx\n}\n\nexport function useParams<T extends Record<string, string>>(): T {\n const { route } = useRouter()\n return route.params as T\n}\n\nexport function useQuery<T extends Record<string, string | string[] | undefined>>(): T {\n const { route } = useRouter()\n return route.query as T\n}\n","import type { RouteDefinition } from './router/types'\nimport type { FocusManagerOptions } from './focus/types'\nimport { FocusProvider } from './focus'\nimport { Router } from './router/Router'\n\nexport interface TableAppProps {\n routes: RouteDefinition[]\n initialPath?: string\n focus?: FocusManagerOptions\n}\n\nexport function TableApp({ routes, initialPath, focus }: TableAppProps) {\n return (\n <FocusProvider {...(focus && { options: focus })}>\n <Router routes={routes} {...(initialPath && { initialPath })} />\n </FocusProvider>\n )\n}","import React from 'react'\nimport { parseSegments } from './matcher'\nimport type { RouteDefinition, RouteSegment } from './types'\n\nexport type RouteModule = {\n default: React.ComponentType\n}\n\nexport type RouteModuleLoader = () => Promise<RouteModule>\n\ntype RouteInput = {\n path: string\n component: RouteModuleLoader\n}\n\nexport function defineRoutes(routes: RouteInput[]): RouteDefinition[] {\n return routes.map((route) => createRouteDefinition(route.path, route.component, route.path))\n}\n\nexport function defineFileRoutes(\n routeModules: Record<string, RouteModuleLoader>,\n options: { appDir?: string } = {},\n): RouteDefinition[] {\n const appDir = options.appDir ?? 'app'\n\n return Object.entries(routeModules)\n .map(([filePath, component]) =>\n createRouteDefinition(normalizeRoutePath(filePath, appDir), component, filePath),\n )\n .sort(compareRouteDefinitions)\n}\n\nfunction createRouteDefinition(\n path: string,\n component: RouteModuleLoader,\n filePath: string,\n): RouteDefinition {\n return {\n filePath,\n segments: parseSegments(path),\n component: React.lazy(component),\n }\n}\n\nfunction normalizeRoutePath(filePath: string, appDir: string): string {\n const normalizedPath = filePath.replace(/\\\\/g, '/').replace(/^\\.\\//, '')\n const appDirPrefix = `${appDir}/`\n const appDirIndex = normalizedPath.indexOf(appDirPrefix)\n\n if (appDirIndex === -1) {\n throw new Error(`Route module \"${filePath}\" must be inside \"${appDir}/\"`)\n }\n\n const pagePath = normalizedPath.slice(appDirIndex + appDirPrefix.length)\n const routePath = pagePath.replace(/(?:^|\\/)page\\.[^/.]+$/, '')\n\n return routePath.length === 0 ? '/' : `/${routePath}`\n}\n\nfunction compareRouteDefinitions(left: RouteDefinition, right: RouteDefinition): number {\n const segmentsOrder = compareSegments(left.segments, right.segments)\n if (segmentsOrder !== 0) {\n return segmentsOrder\n }\n\n return left.filePath.localeCompare(right.filePath)\n}\n\nfunction compareSegments(left: RouteSegment[], right: RouteSegment[]): number {\n const length = Math.max(left.length, right.length)\n\n for (let index = 0; index < length; index++) {\n const leftSegment = left[index]\n const rightSegment = right[index]\n\n if (leftSegment === undefined) return -1\n if (rightSegment === undefined) return 1\n\n const rankDifference = segmentRank(rightSegment) - segmentRank(leftSegment)\n if (rankDifference !== 0) {\n return rankDifference\n }\n\n if (leftSegment.type === 'static' && rightSegment.type === 'static') {\n const valueDifference = leftSegment.value.localeCompare(rightSegment.value)\n if (valueDifference !== 0) {\n return valueDifference\n }\n }\n }\n\n return 0\n}\n\nfunction segmentRank(segment: RouteSegment): number {\n switch (segment.type) {\n case 'static':\n return 3\n case 'dynamic':\n return 2\n case 'catch-all':\n return 1\n }\n}\n"],"mappings":"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","names":["useEffect","useRef","useRef","useEffect","jsx","React","useCallback","useEffect","useState","createContext","useContext","jsx","useState","useCallback","useEffect","React","jsx","React","React"]}
|
|
1
|
+
{"version":3,"sources":["../src/focus/FocusManager.ts","../src/focus/FocusProvider.tsx","../src/focus/context.ts","../src/focus/KeyHandler.ts","../src/focus/hooks.ts","../src/focus/components.tsx","../src/router/Router.tsx","../src/router/matcher.ts","../src/router/context.ts","../src/TableApp.tsx","../src/router/defineRoutes.ts"],"sourcesContent":["import type {\n FocusableId,\n FocusableNode,\n FocusDirection,\n FocusGroup,\n FocusManagerOptions,\n FocusState,\n} from './types'\n\nexport class FocusManager {\n private state: FocusState = {\n focusedId: null,\n nodes: new Map(),\n groups: new Map(),\n }\n\n private options: FocusManagerOptions\n\n constructor(options: FocusManagerOptions = {}) {\n this.options = options\n }\n\n registerNode(node: FocusableNode): void {\n this.state.nodes.set(node.id, node)\n if (\n this.state.focusedId === null &&\n this.options.initialFocusId === node.id\n ) {\n this.setFocus(node.id)\n }\n }\n\n unregisterNode(id: FocusableId): void {\n this.state.nodes.delete(id)\n if (this.state.focusedId === id) {\n this.state.focusedId = null\n this.options.onFocusChange?.(null)\n }\n }\n\n registerGroup(group: FocusGroup): void {\n this.state.groups.set(group.id, group)\n }\n\n unregisterGroup(id: FocusableId): void {\n this.state.groups.delete(id)\n }\n\n setFocus(id: FocusableId): void {\n const node = this.state.nodes.get(id)\n if (!node || node.disabled) return\n\n const prev = this.state.focusedId\n if (prev) {\n const prevNode = this.state.nodes.get(prev)\n prevNode?.onBlur?.()\n prevNode?.el.blur()\n\n if (prevNode?.groupId) {\n const group = this.state.groups.get(prevNode.groupId)\n if (group) group.lastFocusedId = prev\n }\n }\n\n this.state.focusedId = id\n node.el.focus({ preventScroll: true })\n node.onFocus?.()\n if (typeof window !== 'undefined') {\n window.requestAnimationFrame(() => show(node.el))\n } else {\n show(node.el)\n }\n this.options.onFocusChange?.(id)\n }\n\n move(direction: FocusDirection): void {\n const currentId = this.state.focusedId\n if (!currentId) {\n this.focusFirst()\n return\n }\n\n const current = this.state.nodes.get(currentId)\n if (!current) return\n\n const groupId = current.groupId\n const group = groupId ? this.state.groups.get(groupId) : null\n\n const next = group\n ? this.findNextInGroup(currentId, direction, group)\n : this.findNextSpatial(currentId, direction)\n\n if (next) this.setFocus(next)\n }\n\n select(): void {\n const node = this.state.focusedId\n ? this.state.nodes.get(this.state.focusedId)\n : null\n node?.onSelect?.()\n node?.el.click()\n }\n\n focusFirst(): void {\n const first = this.state.nodes.values().next().value as FocusableNode | undefined\n if (first) this.setFocus(first.id)\n }\n\n getFocusedId(): FocusableId | null {\n return this.state.focusedId\n }\n\n private findNextInGroup(\n currentId: FocusableId,\n direction: FocusDirection,\n group: FocusGroup,\n ): FocusableId | null {\n const siblings = [...this.state.nodes.values()].filter(\n (n) => n.groupId === group.id && !n.disabled,\n )\n const index = siblings.findIndex((n) => n.id === currentId)\n if (index === -1) return null\n\n const isForward =\n (group.orientation === 'horizontal' && direction === 'right') ||\n (group.orientation === 'vertical' && direction === 'down')\n\n const isBackward =\n (group.orientation === 'horizontal' && direction === 'left') ||\n (group.orientation === 'vertical' && direction === 'up')\n\n if (isForward) {\n if (index < siblings.length - 1) return siblings[index + 1]?.id ?? null\n if (group.loop) return siblings[0]?.id ?? null\n return null\n }\n\n if (isBackward) {\n if (index > 0) return siblings[index - 1]?.id ?? null\n if (group.loop) return siblings[siblings.length - 1]?.id ?? null\n return null\n }\n\n return this.findNextSpatial(currentId, direction)\n }\n\n private findNextSpatial(currentId: FocusableId, direction: FocusDirection): FocusableId | null {\n const current = this.state.nodes.get(currentId)\n if (!current) return null\n\n const currentRect = current.el.getBoundingClientRect()\n const candidates = [...this.state.nodes.values()].filter(\n (n) => n.id !== currentId && !n.disabled,\n )\n\n const inDirection = candidates.filter((n) => {\n const rect = n.el.getBoundingClientRect()\n if (direction === 'right') return rect.left > currentRect.right - 1\n if (direction === 'left') return rect.right < currentRect.left + 1\n if (direction === 'down') return rect.top > currentRect.bottom - 1\n if (direction === 'up') return rect.bottom < currentRect.top + 1\n return false\n })\n\n if (inDirection.length === 0) return null\n\n return inDirection.reduce((best, node) => {\n const a = node.el.getBoundingClientRect()\n const b = best.el.getBoundingClientRect()\n const distA = distance(currentRect, a, direction)\n const distB = distance(currentRect, b, direction)\n return distA < distB ? node : best\n }).id\n }\n}\n\nfunction show(el: HTMLElement) {\n try {\n el.scrollIntoView({\n block: 'nearest',\n inline: 'nearest',\n })\n } catch {\n el.scrollIntoView()\n }\n}\n\nfunction distance(\n from: DOMRect,\n to: DOMRect,\n direction: FocusDirection,\n): number {\n const fromCx = from.left + from.width / 2\n const fromCy = from.top + from.height / 2\n const toCx = to.left + to.width / 2\n const toCy = to.top + to.height / 2\n\n const primary =\n direction === 'right' || direction === 'left'\n ? Math.abs(toCx - fromCx)\n : Math.abs(toCy - fromCy)\n\n const secondary =\n direction === 'right' || direction === 'left'\n ? Math.abs(toCy - fromCy)\n : Math.abs(toCx - fromCx)\n\n return primary + secondary * 0.5\n}\n","import React, { useEffect, useMemo, useRef } from 'react'\nimport { FocusContext } from './context'\nimport { FocusManager } from './FocusManager'\nimport { KeyHandler } from './KeyHandler'\nimport type { FocusManagerOptions } from './types'\n\ninterface FocusProviderProps {\n children: React.ReactNode\n options?: FocusManagerOptions\n}\n\nexport function FocusProvider({ children, options }: FocusProviderProps) {\n const managerRef = useRef<FocusManager | null>(null)\n const keyHandlerRef = useRef<KeyHandler | null>(null)\n\n if (!managerRef.current) {\n managerRef.current = new FocusManager(options ?? {})\n }\n if (!keyHandlerRef.current) {\n keyHandlerRef.current = new KeyHandler()\n }\n\n const manager = managerRef.current\n const keyHandler = keyHandlerRef.current\n\n useEffect(() => {\n keyHandler.mount()\n\n const unsub = keyHandler.subscribe((action) => {\n if (action.type === 'move') {\n manager.move(action.direction)\n }\n if (action.type === 'select') {\n manager.select()\n }\n })\n\n return () => {\n unsub()\n keyHandler.unmount()\n }\n }, [manager, keyHandler])\n\n const value = useMemo(\n () => ({ manager, keyHandler }),\n [manager, keyHandler],\n )\n\n return (\n <FocusContext.Provider value={value}>\n {children}\n </FocusContext.Provider>\n )\n}","import { createContext, useContext } from 'react'\nimport type { FocusManager } from './FocusManager'\nimport type { KeyHandler } from './KeyHandler'\n\nexport interface FocusContextValue {\n manager: FocusManager\n keyHandler: KeyHandler\n}\n\nexport const FocusContext = createContext<FocusContextValue | null>(null)\n\nexport function useFocusContext(): FocusContextValue {\n const ctx = useContext(FocusContext)\n if (ctx === null) {\n throw new Error('useFocusContext must be used within a TableApp')\n }\n return ctx\n}","import type { FocusDirection } from './types'\n\nexport type KeyAction =\n | { type: 'move'; direction: FocusDirection }\n | { type: 'select' }\n | { type: 'back' }\n | { type: 'unknown' }\n\nconst KEY_MAP: Record<string, KeyAction> = {\n ArrowUp: { type: 'move', direction: 'up' },\n ArrowDown: { type: 'move', direction: 'down' },\n ArrowLeft: { type: 'move', direction: 'left' },\n ArrowRight: { type: 'move', direction: 'right' },\n Enter: { type: 'select' },\n ' ': { type: 'select' },\n Backspace: { type: 'back' },\n Escape: { type: 'back' },\n}\n\nconst TIZEN_KEY_MAP: Record<number, KeyAction> = {\n 38: { type: 'move', direction: 'up' },\n 40: { type: 'move', direction: 'down' },\n 37: { type: 'move', direction: 'left' },\n 39: { type: 'move', direction: 'right' },\n 13: { type: 'select' },\n 10009: { type: 'back' },\n}\n\nconst WEBOS_KEY_MAP: Record<number, KeyAction> = {\n 38: { type: 'move', direction: 'up' },\n 40: { type: 'move', direction: 'down' },\n 37: { type: 'move', direction: 'left' },\n 39: { type: 'move', direction: 'right' },\n 13: { type: 'select' },\n 461: { type: 'back' },\n}\n\nconst ANDROID_TV_KEY_MAP: Record<number, KeyAction> = {\n 38: { type: 'move', direction: 'up' },\n 40: { type: 'move', direction: 'down' },\n 37: { type: 'move', direction: 'left' },\n 39: { type: 'move', direction: 'right' },\n 13: { type: 'select' },\n 85: { type: 'select' },\n 4: { type: 'back' },\n}\n\nfunction detectPlatform(): 'tizen' | 'webos' | 'android' | 'web' {\n if (typeof window === 'undefined') return 'web'\n const ua = navigator.userAgent\n if (ua.includes('Tizen')) return 'tizen'\n if (ua.includes('Web0S') || ua.includes('webOS')) return 'webos'\n if (ua.includes('Android')) return 'android'\n return 'web'\n}\n\nexport function resolveKeyAction(event: KeyboardEvent): KeyAction {\n const byKey = KEY_MAP[event.key]\n if (byKey) return byKey\n\n const platform = detectPlatform()\n\n const platformMap =\n platform === 'tizen' ? TIZEN_KEY_MAP :\n platform === 'webos' ? WEBOS_KEY_MAP :\n platform === 'android' ? ANDROID_TV_KEY_MAP :\n {}\n\n return platformMap[event.keyCode] ?? { type: 'unknown' }\n}\n\nexport class KeyHandler {\n private listeners = new Set<(action: KeyAction) => void>()\n private bound: ((e: KeyboardEvent) => void) | null = null\n\n mount(): void {\n this.bound = (e: KeyboardEvent) => {\n const action = resolveKeyAction(e)\n if (action.type === 'unknown') return\n e.preventDefault()\n this.listeners.forEach((fn) => fn(action))\n }\n window.addEventListener('keydown', this.bound)\n }\n\n unmount(): void {\n if (this.bound) {\n window.removeEventListener('keydown', this.bound)\n this.bound = null\n }\n }\n\n subscribe(fn: (action: KeyAction) => void): () => void {\n this.listeners.add(fn)\n return () => this.listeners.delete(fn)\n }\n}","import {\n useCallback,\n useEffect,\n useId,\n useRef,\n useState,\n} from 'react'\nimport { useFocusContext } from './context'\nimport type { FocusGroup } from './types'\n\nexport interface UseFocusableOptions {\n groupId?: string\n disabled?: boolean\n onFocus?: () => void\n onBlur?: () => void\n onSelect?: () => void\n autoFocus?: boolean\n}\n\nexport interface UseFocusableResult {\n ref: React.RefObject<HTMLElement>\n focused: boolean\n focusableProps: {\n 'data-focusable': string\n 'data-focused': boolean\n tabIndex: number\n }\n}\n\nexport function useFocusable(options: UseFocusableOptions = {}): UseFocusableResult {\n const id = useId()\n const ref = useRef<HTMLElement>(null)\n const [focused, setFocused] = useState(false)\n const { manager } = useFocusContext()\n\n const { groupId, disabled = false, onFocus, onBlur, onSelect, autoFocus } = options\n\n useEffect(() => {\n const el = ref.current\n if (!el) return\n\n manager.registerNode({\n id,\n el,\n groupId: groupId ?? null,\n disabled,\n onFocus: () => {\n setFocused(true)\n onFocus?.()\n },\n onBlur: () => {\n setFocused(false)\n onBlur?.()\n },\n onSelect: onSelect || (() => {}),\n })\n\n if (autoFocus) {\n manager.setFocus(id)\n }\n\n return () => {\n manager.unregisterNode(id)\n }\n }, [id, groupId, disabled, autoFocus, manager, onFocus, onBlur, onSelect])\n\n const focusableProps = {\n 'data-focusable': id,\n 'data-focused': focused,\n tabIndex: focused ? 0 : -1,\n }\n\n return { ref: ref as React.RefObject<HTMLElement>, focused, focusableProps }\n}\n\nexport type UseFocusGroupOptions = Omit<FocusGroup, 'id' | 'lastFocusedId' | 'parentId'>\n\nexport function useFocusGroup(options: UseFocusGroupOptions) {\n const id = useId()\n const { manager } = useFocusContext()\n\n useEffect(() => {\n manager.registerGroup({\n id,\n parentId: null,\n lastFocusedId: null,\n ...options,\n })\n return () => {\n manager.unregisterGroup(id)\n }\n }, [id, manager, options.orientation, options.loop, options.rememberFocus])\n\n return { groupId: id }\n}","import React, { forwardRef } from 'react'\nimport type { JSX } from 'react'\nimport { useFocusable, useFocusGroup } from './hooks'\nimport type { UseFocusableOptions, UseFocusGroupOptions } from './hooks'\n\ninterface FocusableProps extends UseFocusableOptions {\n children: React.ReactNode | ((focused: boolean) => React.ReactNode)\n as?: React.ElementType\n className?: string\n style?: React.CSSProperties\n}\n\nexport const Focusable = forwardRef<HTMLElement, FocusableProps>(\n ({ children, as: Tag = 'div', className, style, ...options }, _ref) => {\n const { ref, focused, focusableProps } = useFocusable(options)\n\n return (\n <Tag\n ref={ref as never}\n className={className}\n style={style}\n {...focusableProps}\n >\n {typeof children === 'function' ? children(focused) : children}\n </Tag>\n )\n },\n)\n\nFocusable.displayName = 'Focusable'\n\ninterface FocusGroupProps extends UseFocusGroupOptions {\n children: React.ReactNode\n as?: React.ElementType\n className?: string\n style?: React.CSSProperties\n}\n\nexport function FocusGroup({\n children,\n as: Tag = 'div',\n className,\n style,\n ...options\n}: FocusGroupProps) {\n const { groupId } = useFocusGroup(options)\n const css =\n options.orientation === 'grid'\n ? style\n : {\n display: 'flex',\n flexDirection: options.orientation === 'vertical' ? 'column' : 'row',\n ...style,\n }\n\n return (\n <Tag\n className={className}\n style={css}\n data-focus-group={groupId}\n >\n {children}\n </Tag>\n )\n}\n","import React, { useCallback, useEffect, useState, useTransition } from 'react'\nimport type { NavigateOptions, Route, RouteDefinition, RouteQuery } from './types'\nimport { matchRoute } from './matcher'\nimport { RouterContext } from './context'\n\ninterface RouterProps {\n routes: RouteDefinition[]\n initialPath?: string\n}\n\nfunction parseQuery(search: string): RouteQuery {\n const query: RouteQuery = {}\n const params = new URLSearchParams(search)\n params.forEach((value, key) => {\n const existing = query[key]\n if (existing === undefined) {\n query[key] = value\n } else if (Array.isArray(existing)) {\n existing.push(value)\n } else {\n query[key] = [existing, value]\n }\n })\n return query\n}\n\nfunction buildRoute(pathname: string, search: string, params: Record<string, string>): Route {\n return {\n path: pathname,\n params,\n query: parseQuery(search),\n }\n}\n\nexport function Router({ routes, initialPath = '/' }: RouterProps) {\n const [, startTransition] = useTransition()\n const [currentPath, setCurrentPath] = useState(() => {\n if (typeof window !== 'undefined') return window.location.pathname\n return initialPath\n })\n const [search, setSearch] = useState(() => {\n if (typeof window !== 'undefined') return window.location.search\n return ''\n })\n const [history, setHistory] = useState<Route[]>([])\n\n const match = matchRoute(currentPath, routes)\n const route = match\n ? buildRoute(currentPath, search, match.params)\n : buildRoute(currentPath, search, {})\n\n const navigate = useCallback(\n (path: string, options: NavigateOptions = {}) => {\n const [pathname, queryString] = path.split('?')\n const nextSearch = queryString ? `?${queryString}` : ''\n\n startTransition(() => {\n if (options.replace) {\n window.history.replaceState(null, '', path)\n } else {\n window.history.pushState(null, '', path)\n setHistory((prev) => [...prev, route])\n }\n setCurrentPath(pathname ?? '/')\n setSearch(nextSearch)\n })\n },\n [route],\n )\n\n const back = useCallback(() => {\n if (history.length === 0) return\n const prev = history[history.length - 1]\n if (!prev) return\n setHistory((h) => h.slice(0, -1))\n window.history.back()\n setCurrentPath(prev.path)\n setSearch('')\n }, [history])\n\n const prefetch = useCallback(\n (path: string) => {\n const [pathname] = path.split('?')\n if (!pathname) return\n const result = matchRoute(pathname, routes)\n if (result) {\n void result.route.component\n }\n },\n [routes],\n )\n\n useEffect(() => {\n const onPopState = () => {\n setCurrentPath(window.location.pathname)\n setSearch(window.location.search)\n }\n\n window.addEventListener('popstate', onPopState)\n return () => window.removeEventListener('popstate', onPopState)\n }, [])\n\n if (!match) {\n return <NotFound path={currentPath} />\n }\n\n const { component: Page } = match.route\n\n return (\n <RouterContext.Provider value={{ route, navigate, back, prefetch }}>\n <React.Suspense fallback={<PageLoader />}>\n <Page />\n </React.Suspense>\n </RouterContext.Provider>\n )\n}\n\nfunction NotFound({ path }: { path: string }) {\n return (\n <div data-table-not-found>\n <span>404 — {path} not found</span>\n </div>\n )\n}\n\nfunction PageLoader() {\n return <div data-table-loader />\n}\n","import type { RouteDefinition, RouteParams, RouteSegment } from './types'\n\nexport function parseSegments(path: string): RouteSegment[] {\n return path\n .split('/')\n .filter(Boolean)\n .map((segment) => {\n if (segment.startsWith('[...') && segment.endsWith(']')) {\n return { type: 'catch-all', value: segment.slice(4, -1) }\n }\n if (segment.startsWith('[') && segment.endsWith(']')) {\n return { type: 'dynamic', value: segment.slice(1, -1) }\n }\n return { type: 'static', value: segment }\n })\n}\n\nexport function matchRoute(\n pathname: string,\n routes: RouteDefinition[],\n): { route: RouteDefinition; params: RouteParams } | null {\n const incomingSegments = pathname.split(\"/\").filter(Boolean)\n\n for (const route of routes) {\n const result = matchSegments(incomingSegments, route.segments)\n if (result !== null) {\n return { route, params: result }\n }\n }\n\n return null\n}\n\nfunction matchSegments(incoming: string[], segments: RouteSegment[]): RouteParams | null {\n const params: RouteParams = {}\n let i = 0\n\n for (const segment of segments) {\n if (segment.type === 'catch-all') {\n params[segment.value] = incoming.slice(i).join('/')\n return params\n }\n\n const part = incoming[i]\n if (part === undefined) return null\n\n if (segment.type === 'static') {\n if (part !== segment.value) return null\n }\n\n if (segment.type === 'dynamic') {\n params[segment.value] = part\n }\n\n i++\n }\n\n if (i !== incoming.length) return null\n\n return params\n}\n","import { createContext, useContext } from 'react'\nimport type { NavigateOptions, Route } from './types'\n\nexport interface RouterContextValue {\n route: Route\n navigate: (path: string, options?: NavigateOptions) => void\n back: () => void\n prefetch: (path: string) => void\n}\n\nexport const RouterContext = createContext<RouterContextValue | null>(null)\n\nexport function useRouter(): RouterContextValue {\n const ctx = useContext(RouterContext)\n if (ctx === null) {\n throw new Error('useRouter must be used within a TableApp')\n }\n return ctx\n}\n\nexport function useParams<T extends Record<string, string>>(): T {\n const { route } = useRouter()\n return route.params as T\n}\n\nexport function useQuery<T extends Record<string, string | string[] | undefined>>(): T {\n const { route } = useRouter()\n return route.query as T\n}\n","import type { RouteDefinition } from './router/types'\nimport type { FocusManagerOptions } from './focus/types'\nimport { FocusProvider } from './focus'\nimport { Router } from './router/Router'\n\nexport interface TableAppProps {\n routes: RouteDefinition[]\n initialPath?: string\n focus?: FocusManagerOptions\n}\n\nexport function TableApp({ routes, initialPath, focus }: TableAppProps) {\n return (\n <FocusProvider {...(focus && { options: focus })}>\n <Router routes={routes} {...(initialPath && { initialPath })} />\n </FocusProvider>\n )\n}","import React from 'react'\nimport { parseSegments } from './matcher'\nimport type { RouteDefinition, RouteSegment } from './types'\n\nexport type RouteModule = {\n default: React.ComponentType\n}\n\nexport type RouteModuleLoader = () => Promise<RouteModule>\n\ntype RouteInput = {\n path: string\n component: RouteModuleLoader\n}\n\nexport function defineRoutes(routes: RouteInput[]): RouteDefinition[] {\n return routes.map((route) => createRouteDefinition(route.path, route.component, route.path))\n}\n\nexport function defineFileRoutes(\n routeModules: Record<string, RouteModuleLoader>,\n options: { appDir?: string } = {},\n): RouteDefinition[] {\n const appDir = options.appDir ?? 'app'\n\n return Object.entries(routeModules)\n .map(([filePath, component]) =>\n createRouteDefinition(normalizeRoutePath(filePath, appDir), component, filePath),\n )\n .sort(compareRouteDefinitions)\n}\n\nfunction createRouteDefinition(\n path: string,\n component: RouteModuleLoader,\n filePath: string,\n): RouteDefinition {\n return {\n filePath,\n segments: parseSegments(path),\n component: React.lazy(component),\n }\n}\n\nfunction normalizeRoutePath(filePath: string, appDir: string): string {\n const normalizedPath = filePath.replace(/\\\\/g, '/').replace(/^\\.\\//, '')\n const appDirPrefix = `${appDir}/`\n const appDirIndex = normalizedPath.indexOf(appDirPrefix)\n\n if (appDirIndex === -1) {\n throw new Error(`Route module \"${filePath}\" must be inside \"${appDir}/\"`)\n }\n\n const pagePath = normalizedPath.slice(appDirIndex + appDirPrefix.length)\n const routePath = pagePath.replace(/(?:^|\\/)page\\.[^/.]+$/, '')\n\n return routePath.length === 0 ? '/' : `/${routePath}`\n}\n\nfunction compareRouteDefinitions(left: RouteDefinition, right: RouteDefinition): number {\n const segmentsOrder = compareSegments(left.segments, right.segments)\n if (segmentsOrder !== 0) {\n return segmentsOrder\n }\n\n return left.filePath.localeCompare(right.filePath)\n}\n\nfunction compareSegments(left: RouteSegment[], right: RouteSegment[]): number {\n const length = Math.max(left.length, right.length)\n\n for (let index = 0; index < length; index++) {\n const leftSegment = left[index]\n const rightSegment = right[index]\n\n if (leftSegment === undefined) return -1\n if (rightSegment === undefined) return 1\n\n const rankDifference = segmentRank(rightSegment) - segmentRank(leftSegment)\n if (rankDifference !== 0) {\n return rankDifference\n }\n\n if (leftSegment.type === 'static' && rightSegment.type === 'static') {\n const valueDifference = leftSegment.value.localeCompare(rightSegment.value)\n if (valueDifference !== 0) {\n return valueDifference\n }\n }\n }\n\n return 0\n}\n\nfunction segmentRank(segment: RouteSegment): number {\n switch (segment.type) {\n case 'static':\n return 3\n case 'dynamic':\n return 2\n case 'catch-all':\n return 1\n }\n}\n"],"mappings":"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","names":["useEffect","useRef","useRef","useEffect","jsx","React","useCallback","useEffect","useState","createContext","useContext","jsx","useState","useCallback","useEffect","React","jsx","React","React"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@table-js/core",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.5",
|
|
4
4
|
"description": "Table.js core runtime - router, focus, navigation for Smart TV",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.mjs",
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
"react-dom": "^19.2.4",
|
|
37
37
|
"tsup": "^8.5.1",
|
|
38
38
|
"typescript": "^6.0.2",
|
|
39
|
-
"@table-js/tsconfig": "0.0.
|
|
39
|
+
"@table-js/tsconfig": "0.0.5"
|
|
40
40
|
},
|
|
41
41
|
"peerDependencies": {
|
|
42
42
|
"react": "^18.0.0 || ^19.0.0",
|