@table-js/core 0.0.1 → 0.0.4
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.d.mts +9 -4
- package/dist/index.d.ts +9 -4
- package/dist/index.js +85 -6
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +84 -6
- package/dist/index.mjs.map +1 -1
- package/package.json +10 -10
package/dist/index.d.mts
CHANGED
|
@@ -80,13 +80,18 @@ declare function useRouter(): RouterContextValue;
|
|
|
80
80
|
declare function useParams<T extends Record<string, string>>(): T;
|
|
81
81
|
declare function useQuery<T extends Record<string, string | string[] | undefined>>(): T;
|
|
82
82
|
|
|
83
|
+
type RouteModule = {
|
|
84
|
+
default: React__default.ComponentType;
|
|
85
|
+
};
|
|
86
|
+
type RouteModuleLoader = () => Promise<RouteModule>;
|
|
83
87
|
type RouteInput = {
|
|
84
88
|
path: string;
|
|
85
|
-
component:
|
|
86
|
-
default: React__default.ComponentType;
|
|
87
|
-
}>;
|
|
89
|
+
component: RouteModuleLoader;
|
|
88
90
|
};
|
|
89
91
|
declare function defineRoutes(routes: RouteInput[]): RouteDefinition[];
|
|
92
|
+
declare function defineFileRoutes(routeModules: Record<string, RouteModuleLoader>, options?: {
|
|
93
|
+
appDir?: string;
|
|
94
|
+
}): RouteDefinition[];
|
|
90
95
|
|
|
91
96
|
declare class FocusManager {
|
|
92
97
|
private state;
|
|
@@ -175,4 +180,4 @@ interface FocusGroupProps extends UseFocusGroupOptions {
|
|
|
175
180
|
}
|
|
176
181
|
declare function FocusGroup({ children, as: Tag, className, style, ...options }: FocusGroupProps): react_jsx_runtime.JSX.Element;
|
|
177
182
|
|
|
178
|
-
export { FocusContext, type FocusDirection, FocusGroup, type FocusGroup$1 as FocusGroupType, FocusManager, type FocusManagerOptions, FocusProvider, type FocusState, Focusable, type FocusableId, type FocusableNode, KeyHandler, type NavigateOptions, type Route, type RouteDefinition, type RouteParams, type RouteQuery, type RouteSegment, Router, RouterContext, TableApp, type UseFocusGroupOptions, type UseFocusableOptions, type UseFocusableResult, defineRoutes, resolveKeyAction, useFocusContext, useFocusGroup, useFocusable, useParams, useQuery, useRouter };
|
|
183
|
+
export { FocusContext, type FocusDirection, FocusGroup, type FocusGroup$1 as FocusGroupType, FocusManager, type FocusManagerOptions, FocusProvider, type FocusState, Focusable, type FocusableId, type FocusableNode, KeyHandler, type NavigateOptions, type Route, type RouteDefinition, type RouteModule, type RouteModuleLoader, type RouteParams, type RouteQuery, type RouteSegment, Router, RouterContext, TableApp, type UseFocusGroupOptions, type UseFocusableOptions, type UseFocusableResult, defineFileRoutes, defineRoutes, resolveKeyAction, useFocusContext, useFocusGroup, useFocusable, useParams, useQuery, useRouter };
|
package/dist/index.d.ts
CHANGED
|
@@ -80,13 +80,18 @@ declare function useRouter(): RouterContextValue;
|
|
|
80
80
|
declare function useParams<T extends Record<string, string>>(): T;
|
|
81
81
|
declare function useQuery<T extends Record<string, string | string[] | undefined>>(): T;
|
|
82
82
|
|
|
83
|
+
type RouteModule = {
|
|
84
|
+
default: React__default.ComponentType;
|
|
85
|
+
};
|
|
86
|
+
type RouteModuleLoader = () => Promise<RouteModule>;
|
|
83
87
|
type RouteInput = {
|
|
84
88
|
path: string;
|
|
85
|
-
component:
|
|
86
|
-
default: React__default.ComponentType;
|
|
87
|
-
}>;
|
|
89
|
+
component: RouteModuleLoader;
|
|
88
90
|
};
|
|
89
91
|
declare function defineRoutes(routes: RouteInput[]): RouteDefinition[];
|
|
92
|
+
declare function defineFileRoutes(routeModules: Record<string, RouteModuleLoader>, options?: {
|
|
93
|
+
appDir?: string;
|
|
94
|
+
}): RouteDefinition[];
|
|
90
95
|
|
|
91
96
|
declare class FocusManager {
|
|
92
97
|
private state;
|
|
@@ -175,4 +180,4 @@ interface FocusGroupProps extends UseFocusGroupOptions {
|
|
|
175
180
|
}
|
|
176
181
|
declare function FocusGroup({ children, as: Tag, className, style, ...options }: FocusGroupProps): react_jsx_runtime.JSX.Element;
|
|
177
182
|
|
|
178
|
-
export { FocusContext, type FocusDirection, FocusGroup, type FocusGroup$1 as FocusGroupType, FocusManager, type FocusManagerOptions, FocusProvider, type FocusState, Focusable, type FocusableId, type FocusableNode, KeyHandler, type NavigateOptions, type Route, type RouteDefinition, type RouteParams, type RouteQuery, type RouteSegment, Router, RouterContext, TableApp, type UseFocusGroupOptions, type UseFocusableOptions, type UseFocusableResult, defineRoutes, resolveKeyAction, useFocusContext, useFocusGroup, useFocusable, useParams, useQuery, useRouter };
|
|
183
|
+
export { FocusContext, type FocusDirection, FocusGroup, type FocusGroup$1 as FocusGroupType, FocusManager, type FocusManagerOptions, FocusProvider, type FocusState, Focusable, type FocusableId, type FocusableNode, KeyHandler, type NavigateOptions, type Route, type RouteDefinition, type RouteModule, type RouteModuleLoader, type RouteParams, type RouteQuery, type RouteSegment, Router, RouterContext, TableApp, type UseFocusGroupOptions, type UseFocusableOptions, type UseFocusableResult, defineFileRoutes, defineRoutes, resolveKeyAction, useFocusContext, useFocusGroup, useFocusable, useParams, useQuery, useRouter };
|
package/dist/index.js
CHANGED
|
@@ -39,6 +39,7 @@ __export(index_exports, {
|
|
|
39
39
|
Router: () => Router,
|
|
40
40
|
RouterContext: () => RouterContext,
|
|
41
41
|
TableApp: () => TableApp,
|
|
42
|
+
defineFileRoutes: () => defineFileRoutes,
|
|
42
43
|
defineRoutes: () => defineRoutes,
|
|
43
44
|
resolveKeyAction: () => resolveKeyAction,
|
|
44
45
|
useFocusContext: () => useFocusContext,
|
|
@@ -95,6 +96,11 @@ var FocusManager = class {
|
|
|
95
96
|
this.state.focusedId = id;
|
|
96
97
|
node.el.focus({ preventScroll: true });
|
|
97
98
|
node.onFocus?.();
|
|
99
|
+
if (typeof window !== "undefined") {
|
|
100
|
+
window.requestAnimationFrame(() => show(node.el));
|
|
101
|
+
} else {
|
|
102
|
+
show(node.el);
|
|
103
|
+
}
|
|
98
104
|
this.options.onFocusChange?.(id);
|
|
99
105
|
}
|
|
100
106
|
move(direction) {
|
|
@@ -167,6 +173,16 @@ var FocusManager = class {
|
|
|
167
173
|
}).id;
|
|
168
174
|
}
|
|
169
175
|
};
|
|
176
|
+
function show(el) {
|
|
177
|
+
try {
|
|
178
|
+
el.scrollIntoView({
|
|
179
|
+
block: "nearest",
|
|
180
|
+
inline: "nearest"
|
|
181
|
+
});
|
|
182
|
+
} catch {
|
|
183
|
+
el.scrollIntoView();
|
|
184
|
+
}
|
|
185
|
+
}
|
|
170
186
|
function distance(from, to, direction) {
|
|
171
187
|
const fromCx = from.left + from.width / 2;
|
|
172
188
|
const fromCy = from.top + from.height / 2;
|
|
@@ -388,11 +404,16 @@ function FocusGroup({
|
|
|
388
404
|
...options
|
|
389
405
|
}) {
|
|
390
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
|
+
};
|
|
391
412
|
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
392
413
|
Tag,
|
|
393
414
|
{
|
|
394
415
|
className,
|
|
395
|
-
style,
|
|
416
|
+
style: css,
|
|
396
417
|
"data-focus-group": groupId,
|
|
397
418
|
children
|
|
398
419
|
}
|
|
@@ -573,11 +594,68 @@ function TableApp({ routes, initialPath, focus }) {
|
|
|
573
594
|
// src/router/defineRoutes.ts
|
|
574
595
|
var import_react7 = __toESM(require("react"));
|
|
575
596
|
function defineRoutes(routes) {
|
|
576
|
-
return routes.map((
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
597
|
+
return routes.map((route) => createRouteDefinition(route.path, route.component, route.path));
|
|
598
|
+
}
|
|
599
|
+
function defineFileRoutes(routeModules, options = {}) {
|
|
600
|
+
const appDir = options.appDir ?? "app";
|
|
601
|
+
return Object.entries(routeModules).map(
|
|
602
|
+
([filePath, component]) => createRouteDefinition(normalizeRoutePath(filePath, appDir), component, filePath)
|
|
603
|
+
).sort(compareRouteDefinitions);
|
|
604
|
+
}
|
|
605
|
+
function createRouteDefinition(path, component, filePath) {
|
|
606
|
+
return {
|
|
607
|
+
filePath,
|
|
608
|
+
segments: parseSegments(path),
|
|
609
|
+
component: import_react7.default.lazy(component)
|
|
610
|
+
};
|
|
611
|
+
}
|
|
612
|
+
function normalizeRoutePath(filePath, appDir) {
|
|
613
|
+
const normalizedPath = filePath.replace(/\\/g, "/").replace(/^\.\//, "");
|
|
614
|
+
const appDirPrefix = `${appDir}/`;
|
|
615
|
+
const appDirIndex = normalizedPath.indexOf(appDirPrefix);
|
|
616
|
+
if (appDirIndex === -1) {
|
|
617
|
+
throw new Error(`Route module "${filePath}" must be inside "${appDir}/"`);
|
|
618
|
+
}
|
|
619
|
+
const pagePath = normalizedPath.slice(appDirIndex + appDirPrefix.length);
|
|
620
|
+
const routePath = pagePath.replace(/(?:^|\/)page\.[^/.]+$/, "");
|
|
621
|
+
return routePath.length === 0 ? "/" : `/${routePath}`;
|
|
622
|
+
}
|
|
623
|
+
function compareRouteDefinitions(left, right) {
|
|
624
|
+
const segmentsOrder = compareSegments(left.segments, right.segments);
|
|
625
|
+
if (segmentsOrder !== 0) {
|
|
626
|
+
return segmentsOrder;
|
|
627
|
+
}
|
|
628
|
+
return left.filePath.localeCompare(right.filePath);
|
|
629
|
+
}
|
|
630
|
+
function compareSegments(left, right) {
|
|
631
|
+
const length = Math.max(left.length, right.length);
|
|
632
|
+
for (let index = 0; index < length; index++) {
|
|
633
|
+
const leftSegment = left[index];
|
|
634
|
+
const rightSegment = right[index];
|
|
635
|
+
if (leftSegment === void 0) return -1;
|
|
636
|
+
if (rightSegment === void 0) return 1;
|
|
637
|
+
const rankDifference = segmentRank(rightSegment) - segmentRank(leftSegment);
|
|
638
|
+
if (rankDifference !== 0) {
|
|
639
|
+
return rankDifference;
|
|
640
|
+
}
|
|
641
|
+
if (leftSegment.type === "static" && rightSegment.type === "static") {
|
|
642
|
+
const valueDifference = leftSegment.value.localeCompare(rightSegment.value);
|
|
643
|
+
if (valueDifference !== 0) {
|
|
644
|
+
return valueDifference;
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
return 0;
|
|
649
|
+
}
|
|
650
|
+
function segmentRank(segment) {
|
|
651
|
+
switch (segment.type) {
|
|
652
|
+
case "static":
|
|
653
|
+
return 3;
|
|
654
|
+
case "dynamic":
|
|
655
|
+
return 2;
|
|
656
|
+
case "catch-all":
|
|
657
|
+
return 1;
|
|
658
|
+
}
|
|
581
659
|
}
|
|
582
660
|
// Annotate the CommonJS export names for ESM import in node:
|
|
583
661
|
0 && (module.exports = {
|
|
@@ -590,6 +668,7 @@ function defineRoutes(routes) {
|
|
|
590
668
|
Router,
|
|
591
669
|
RouterContext,
|
|
592
670
|
TableApp,
|
|
671
|
+
defineFileRoutes,
|
|
593
672
|
defineRoutes,
|
|
594
673
|
resolveKeyAction,
|
|
595
674
|
useFocusContext,
|
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 { Router, RouterContext, useRouter, useParams, useQuery, defineRoutes } from './router'\n\nexport type {\n Route,\n RouteDefinition,\n RouteParams,\n RouteQuery,\n RouteSegment,\n NavigateOptions,\n} 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 } from './types'\n\ntype RouteInput = {\n path: string\n component: () => Promise<{ default: React.ComponentType }>\n}\n\nexport function defineRoutes(routes: RouteInput[]): RouteDefinition[] {\n return routes.map((r) => ({\n filePath: r.path,\n segments: parseSegments(r.path),\n component: React.lazy(r.component),\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
|
}
|
|
@@ -526,11 +546,68 @@ function TableApp({ routes, initialPath, focus }) {
|
|
|
526
546
|
// src/router/defineRoutes.ts
|
|
527
547
|
import React4 from "react";
|
|
528
548
|
function defineRoutes(routes) {
|
|
529
|
-
return routes.map((
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
549
|
+
return routes.map((route) => createRouteDefinition(route.path, route.component, route.path));
|
|
550
|
+
}
|
|
551
|
+
function defineFileRoutes(routeModules, options = {}) {
|
|
552
|
+
const appDir = options.appDir ?? "app";
|
|
553
|
+
return Object.entries(routeModules).map(
|
|
554
|
+
([filePath, component]) => createRouteDefinition(normalizeRoutePath(filePath, appDir), component, filePath)
|
|
555
|
+
).sort(compareRouteDefinitions);
|
|
556
|
+
}
|
|
557
|
+
function createRouteDefinition(path, component, filePath) {
|
|
558
|
+
return {
|
|
559
|
+
filePath,
|
|
560
|
+
segments: parseSegments(path),
|
|
561
|
+
component: React4.lazy(component)
|
|
562
|
+
};
|
|
563
|
+
}
|
|
564
|
+
function normalizeRoutePath(filePath, appDir) {
|
|
565
|
+
const normalizedPath = filePath.replace(/\\/g, "/").replace(/^\.\//, "");
|
|
566
|
+
const appDirPrefix = `${appDir}/`;
|
|
567
|
+
const appDirIndex = normalizedPath.indexOf(appDirPrefix);
|
|
568
|
+
if (appDirIndex === -1) {
|
|
569
|
+
throw new Error(`Route module "${filePath}" must be inside "${appDir}/"`);
|
|
570
|
+
}
|
|
571
|
+
const pagePath = normalizedPath.slice(appDirIndex + appDirPrefix.length);
|
|
572
|
+
const routePath = pagePath.replace(/(?:^|\/)page\.[^/.]+$/, "");
|
|
573
|
+
return routePath.length === 0 ? "/" : `/${routePath}`;
|
|
574
|
+
}
|
|
575
|
+
function compareRouteDefinitions(left, right) {
|
|
576
|
+
const segmentsOrder = compareSegments(left.segments, right.segments);
|
|
577
|
+
if (segmentsOrder !== 0) {
|
|
578
|
+
return segmentsOrder;
|
|
579
|
+
}
|
|
580
|
+
return left.filePath.localeCompare(right.filePath);
|
|
581
|
+
}
|
|
582
|
+
function compareSegments(left, right) {
|
|
583
|
+
const length = Math.max(left.length, right.length);
|
|
584
|
+
for (let index = 0; index < length; index++) {
|
|
585
|
+
const leftSegment = left[index];
|
|
586
|
+
const rightSegment = right[index];
|
|
587
|
+
if (leftSegment === void 0) return -1;
|
|
588
|
+
if (rightSegment === void 0) return 1;
|
|
589
|
+
const rankDifference = segmentRank(rightSegment) - segmentRank(leftSegment);
|
|
590
|
+
if (rankDifference !== 0) {
|
|
591
|
+
return rankDifference;
|
|
592
|
+
}
|
|
593
|
+
if (leftSegment.type === "static" && rightSegment.type === "static") {
|
|
594
|
+
const valueDifference = leftSegment.value.localeCompare(rightSegment.value);
|
|
595
|
+
if (valueDifference !== 0) {
|
|
596
|
+
return valueDifference;
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
return 0;
|
|
601
|
+
}
|
|
602
|
+
function segmentRank(segment) {
|
|
603
|
+
switch (segment.type) {
|
|
604
|
+
case "static":
|
|
605
|
+
return 3;
|
|
606
|
+
case "dynamic":
|
|
607
|
+
return 2;
|
|
608
|
+
case "catch-all":
|
|
609
|
+
return 1;
|
|
610
|
+
}
|
|
534
611
|
}
|
|
535
612
|
export {
|
|
536
613
|
FocusContext,
|
|
@@ -542,6 +619,7 @@ export {
|
|
|
542
619
|
Router,
|
|
543
620
|
RouterContext,
|
|
544
621
|
TableApp,
|
|
622
|
+
defineFileRoutes,
|
|
545
623
|
defineRoutes,
|
|
546
624
|
resolveKeyAction,
|
|
547
625
|
useFocusContext,
|
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 } from './types'\n\ntype RouteInput = {\n path: string\n component: () => Promise<{ default: React.ComponentType }>\n}\n\nexport function defineRoutes(routes: RouteInput[]): RouteDefinition[] {\n return routes.map((r) => ({\n filePath: r.path,\n segments: parseSegments(r.path),\n component: React.lazy(r.component),\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.4",
|
|
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",
|
|
@@ -15,12 +15,6 @@
|
|
|
15
15
|
"files": [
|
|
16
16
|
"dist"
|
|
17
17
|
],
|
|
18
|
-
"scripts": {
|
|
19
|
-
"build": "tsup",
|
|
20
|
-
"dev": "tsup --watch",
|
|
21
|
-
"typecheck": "tsc --noEmit",
|
|
22
|
-
"clean": "rm -rf dist"
|
|
23
|
-
},
|
|
24
18
|
"keywords": [
|
|
25
19
|
"smart-tv",
|
|
26
20
|
"tizen",
|
|
@@ -37,15 +31,21 @@
|
|
|
37
31
|
"url": "https://github.com/tablejs/tablejs"
|
|
38
32
|
},
|
|
39
33
|
"devDependencies": {
|
|
40
|
-
"@table/tsconfig": "workspace:*",
|
|
41
34
|
"@types/react": "^19.2.14",
|
|
42
35
|
"react": "^19.2.4",
|
|
43
36
|
"react-dom": "^19.2.4",
|
|
44
37
|
"tsup": "^8.5.1",
|
|
45
|
-
"typescript": "^6.0.2"
|
|
38
|
+
"typescript": "^6.0.2",
|
|
39
|
+
"@table-js/tsconfig": "0.0.4"
|
|
46
40
|
},
|
|
47
41
|
"peerDependencies": {
|
|
48
42
|
"react": "^18.0.0 || ^19.0.0",
|
|
49
43
|
"react-dom": "^18.0.0 || ^19.0.0"
|
|
44
|
+
},
|
|
45
|
+
"scripts": {
|
|
46
|
+
"build": "tsup",
|
|
47
|
+
"dev": "tsup --watch",
|
|
48
|
+
"typecheck": "tsc --noEmit",
|
|
49
|
+
"clean": "rm -rf dist"
|
|
50
50
|
}
|
|
51
|
-
}
|
|
51
|
+
}
|