react-multi-tab 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +190 -0
- package/dist/adapters/react-router.cjs +41 -0
- package/dist/adapters/react-router.cjs.map +1 -0
- package/dist/adapters/react-router.d.cts +33 -0
- package/dist/adapters/react-router.d.ts +33 -0
- package/dist/adapters/react-router.mjs +39 -0
- package/dist/adapters/react-router.mjs.map +1 -0
- package/dist/index.cjs +632 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +276 -0
- package/dist/index.d.ts +276 -0
- package/dist/index.mjs +618 -0
- package/dist/index.mjs.map +1 -0
- package/dist/types-Df_5I7eH.d.cts +103 -0
- package/dist/types-Df_5I7eH.d.ts +103 -0
- package/package.json +117 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/context.ts","../src/store.ts","../src/components/MultiTabProvider.tsx","../src/components/TabList.tsx","../src/hooks/useInternalContext.ts","../src/components/TabTrigger.tsx","../src/components/TabPanel.tsx","../src/context/TabInstanceContext.ts","../src/components/TabContent.tsx","../src/components/TabPanels.tsx","../src/components/TabCloseButton.tsx","../src/hooks/useMultiTab.ts","../src/hooks/useTabData.ts","../src/registry.ts","../src/adapters/memory.ts","../src/adapters/search-params.ts"],"names":["useRef","useCallback","jsx","createContext","useContext","useSyncExternalStore"],"mappings":"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","file":"index.mjs","sourcesContent":["import { createContext } from \"react\";\nimport type { MultiTabContextValue } from \"./types\";\n\n/**\n * Internal React context — not exported from the public API.\n * Consumers interact via `useMultiTab` and `useTabData` hooks.\n */\nexport const MultiTabContext = createContext<MultiTabContextValue | null>(null);\n","import type { MultiTabState, TabInstance } from \"./types\";\n\nexport type Listener = () => void;\n\nexport interface MultiTabStore {\n getState: () => MultiTabState;\n subscribe: (listener: Listener) => () => void;\n dispatch: (action: Action) => void;\n}\n\nexport type Action =\n | { type: \"OPEN_TAB\"; tab: TabInstance; activate: boolean }\n | { type: \"CLOSE_TAB\"; instanceId: string }\n | { type: \"ACTIVATE_TAB\"; instanceId: string }\n | { type: \"CLOSE_ALL\" }\n | { type: \"CLOSE_OTHERS\"; instanceId: string }\n | { type: \"SET_DATA\"; instanceId: string; data: Record<string, unknown> }\n | { type: \"REMOVE_DATA\"; instanceId: string }\n | { type: \"RESTORE\"; state: Partial<MultiTabState> };\n\nfunction reducer(state: MultiTabState, action: Action): MultiTabState {\n switch (action.type) {\n case \"OPEN_TAB\": {\n const activate = action.activate;\n const newActiveId = activate ? action.tab.instanceId : state.activeTabId;\n const newHistory = activate\n ? [\n ...state.activeTabHistory.filter(\n (id) => id !== action.tab.instanceId\n ),\n action.tab.instanceId,\n ]\n : state.activeTabHistory;\n\n return {\n ...state,\n tabs: [...state.tabs, action.tab],\n activeTabId: newActiveId,\n activeTabHistory: newHistory,\n };\n }\n case \"CLOSE_TAB\": {\n const newTabs = state.tabs.filter(\n (t) => t.instanceId !== action.instanceId\n );\n const newHistory = state.activeTabHistory.filter(\n (id) => id !== action.instanceId\n ); // Remove from history\n\n let newActive = state.activeTabId;\n if (state.activeTabId === action.instanceId) {\n // Find the last valid active tab from history\n const previousValidActive =\n newHistory.length > 0 ? newHistory[newHistory.length - 1] : null;\n newActive =\n previousValidActive ||\n (newTabs.length > 0 ? newTabs[newTabs.length - 1].instanceId : null);\n }\n\n const { [action.instanceId]: _removed, ...restData } = state.tabData;\n return {\n tabs: newTabs,\n activeTabId: newActive,\n tabData: restData,\n activeTabHistory: newHistory,\n };\n }\n case \"ACTIVATE_TAB\": {\n if (!state.tabs.some((t) => t.instanceId === action.instanceId))\n return state;\n\n const newHistory = [\n ...state.activeTabHistory.filter((id) => id !== action.instanceId),\n action.instanceId,\n ];\n return {\n ...state,\n activeTabId: action.instanceId,\n activeTabHistory: newHistory,\n };\n }\n case \"CLOSE_ALL\":\n return { tabs: [], activeTabId: null, tabData: {}, activeTabHistory: [] };\n case \"CLOSE_OTHERS\": {\n const kept = state.tabs.filter((t) => t.instanceId === action.instanceId);\n const keptIds = new Set(kept.map((t) => t.instanceId));\n const keptData: Record<string, Record<string, unknown>> = {};\n for (const id of keptIds) {\n if (state.tabData[id]) keptData[id] = state.tabData[id];\n }\n const newHistory = state.activeTabHistory.filter(\n (id) => id === action.instanceId\n );\n return {\n tabs: kept,\n activeTabId: action.instanceId,\n tabData: keptData,\n activeTabHistory: newHistory,\n };\n }\n case \"SET_DATA\":\n return {\n ...state,\n tabData: {\n ...state.tabData,\n [action.instanceId]: {\n ...(state.tabData[action.instanceId] ?? {}),\n ...action.data,\n },\n },\n };\n case \"REMOVE_DATA\": {\n const { [action.instanceId]: _removed, ...rest } = state.tabData;\n return { ...state, tabData: rest };\n }\n case \"RESTORE\": {\n const restoredActive = action.state.activeTabId;\n const newHistory = restoredActive\n ? [\n ...state.activeTabHistory.filter((id) => id !== restoredActive),\n restoredActive,\n ]\n : state.activeTabHistory;\n return { ...state, ...action.state, activeTabHistory: newHistory };\n }\n default:\n return state;\n }\n}\n\nexport function createMultiTabStore(\n initialState: MultiTabState\n): MultiTabStore {\n let state = initialState;\n const listeners = new Set<Listener>();\n\n const getState = () => state;\n\n const dispatch = (action: Action) => {\n state = reducer(state, action);\n listeners.forEach((listener) => listener());\n };\n\n const subscribe = (listener: Listener) => {\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n };\n };\n\n return {\n getState,\n dispatch,\n subscribe,\n };\n}\n","import { useCallback, useEffect, useMemo, useRef } from \"react\";\nimport { MultiTabContext } from \"../context\";\nimport { createMultiTabStore } from \"../store\";\nimport type {\n MultiTabProviderProps,\n OpenTabOptions,\n TabInstance,\n} from \"../types\";\n\n// ---------------------------------------------------------------------------\n// Instance ID generator\n// ---------------------------------------------------------------------------\n\nlet counter = 0;\n\nfunction generateInstanceId(pageId: string): string {\n counter += 1;\n return `${pageId}-${counter}`;\n}\n\n// ---------------------------------------------------------------------------\n// Provider\n// ---------------------------------------------------------------------------\n\nexport function MultiTabProvider({\n registry,\n adapter,\n defaultActiveTab,\n children,\n onTabOpen,\n onTabClose,\n onTabChange,\n}: MultiTabProviderProps) {\n // Initialize the external store exactly once\n const store = useRef(\n createMultiTabStore({\n tabs: [],\n activeTabId: defaultActiveTab ?? null,\n tabData: {},\n activeTabHistory: defaultActiveTab ? [defaultActiveTab] : [],\n })\n ).current;\n\n // Keep callback refs so they're always up-to-date\n const cbRef = useRef({ onTabOpen, onTabClose, onTabChange });\n cbRef.current = { onTabOpen, onTabClose, onTabChange };\n\n // Track previous active tab to fire `onTabChange` properly.\n // We'll use a local state or subscribe to store to detect active tab changes.\n const prevActiveRef = useRef<string | null>(defaultActiveTab ?? null);\n\n useEffect(\n () =>\n store.subscribe(() => {\n const state = store.getState();\n if (\n state.activeTabId !== null &&\n state.activeTabId !== prevActiveRef.current\n ) {\n cbRef.current.onTabChange?.(state.activeTabId);\n }\n prevActiveRef.current = state.activeTabId;\n }),\n [store]\n );\n\n // ---- Adapter: restore on mount -------------------------------------------\n const initialised = useRef(false);\n useEffect(() => {\n if (initialised.current || !adapter) return;\n initialised.current = true;\n\n const saved = adapter.read();\n if (!saved || saved.tabs.length === 0) return;\n\n const restoredTabs: TabInstance[] = saved.tabs\n .map((instanceId) => {\n const parts = instanceId.split(\"-\");\n for (let i = parts.length - 1; i >= 1; i--) {\n const candidateId = parts.slice(0, i).join(\"-\");\n const page = registry.getPage(candidateId);\n if (page) {\n return {\n instanceId,\n pageId: page.id,\n label: page.label,\n closable: page.closable !== false,\n } satisfies TabInstance;\n }\n }\n return null;\n })\n .filter((t): t is TabInstance => t !== null);\n\n if (restoredTabs.length > 0) {\n const activeTab =\n saved.activeTab &&\n restoredTabs.some((t) => t.instanceId === saved.activeTab)\n ? saved.activeTab\n : restoredTabs[restoredTabs.length - 1].instanceId;\n\n store.dispatch({\n type: \"RESTORE\",\n state: { tabs: restoredTabs, activeTabId: activeTab },\n });\n }\n }, [adapter, registry, store]);\n\n // ---- Adapter: persist on change ------------------------------------------\n useEffect(() => {\n if (!adapter) return;\n return store.subscribe(() => {\n const { tabs, activeTabId } = store.getState();\n adapter.write(tabs, activeTabId);\n });\n }, [adapter, store]);\n\n // ---- Adapter: subscribe to external changes ------------------------------\n useEffect(() => {\n if (!adapter?.subscribe) return;\n const unsubscribe = adapter.subscribe(() => {\n const saved = adapter.read();\n const currentState = store.getState();\n if (saved?.activeTab && saved.activeTab !== currentState.activeTabId) {\n store.dispatch({ type: \"ACTIVATE_TAB\", instanceId: saved.activeTab });\n }\n });\n return unsubscribe;\n }, [adapter, store]);\n\n // ---- Adapter: cleanup on unmount -----------------------------------------\n useEffect(() => () => adapter?.destroy?.(), [adapter]);\n\n // ---- Actions -------------------------------------------------------------\n\n const openTab = useCallback(\n (pageId: string, options?: OpenTabOptions): string => {\n const page = registry.getPage(pageId);\n if (!page) {\n throw new Error(\n `react-multi-tab: Page \"${pageId}\" not found in registry.`\n );\n }\n\n const instanceId = options?.instanceId ?? generateInstanceId(pageId);\n const tab: TabInstance = {\n instanceId,\n pageId,\n label: options?.label ?? page.label,\n closable: page.closable !== false,\n };\n\n store.dispatch({\n type: \"OPEN_TAB\",\n tab,\n activate: options?.activate !== false,\n });\n cbRef.current.onTabOpen?.(tab);\n\n return instanceId;\n },\n [registry, store]\n );\n\n const closeTab = useCallback(\n (instanceId: string) => {\n store.dispatch({ type: \"CLOSE_TAB\", instanceId });\n cbRef.current.onTabClose?.(instanceId);\n },\n [store]\n );\n\n const activateTab = useCallback(\n (instanceId: string) => {\n store.dispatch({ type: \"ACTIVATE_TAB\", instanceId });\n },\n [store]\n );\n\n const closeAllTabs = useCallback(() => {\n store.dispatch({ type: \"CLOSE_ALL\" });\n }, [store]);\n\n const closeOtherTabs = useCallback(\n (instanceId: string) => {\n store.dispatch({ type: \"CLOSE_OTHERS\", instanceId });\n },\n [store]\n );\n\n const setTabData = useCallback(\n (instanceId: string, data: Record<string, unknown>) => {\n store.dispatch({ type: \"SET_DATA\", instanceId, data });\n },\n [store]\n );\n\n const getTabData = useCallback(\n (instanceId: string): Record<string, unknown> =>\n store.getState().tabData[instanceId] ?? {},\n [store]\n );\n\n const removeTabData = useCallback(\n (instanceId: string) => {\n store.dispatch({ type: \"REMOVE_DATA\", instanceId });\n },\n [store]\n );\n\n // ---- Context value -------------------------------------------------------\n\n const contextValue = useMemo(\n () => ({\n store,\n registry,\n openTab,\n closeTab,\n activateTab,\n closeAllTabs,\n closeOtherTabs,\n setTabData,\n getTabData,\n removeTabData,\n }),\n [\n store,\n registry,\n openTab,\n closeTab,\n activateTab,\n closeAllTabs,\n closeOtherTabs,\n setTabData,\n getTabData,\n removeTabData,\n ]\n );\n\n return (\n <MultiTabContext.Provider value={contextValue}>\n {children}\n </MultiTabContext.Provider>\n );\n}\n","import { useCallback, useRef } from \"react\";\n\n/**\n * Props for {@link TabList}.\n */\nexport interface TabListProps {\n children: React.ReactNode;\n /** Accessible label for the tab list. */\n \"aria-label\"?: string;\n /** ID of an element that labels the tab list. */\n \"aria-labelledby\"?: string;\n className?: string;\n style?: React.CSSProperties;\n}\n\n/**\n * Accessible container for tab triggers.\n *\n * Implements the **WAI-ARIA Tabs** pattern:\n * - `role=\"tablist\"`\n * - `Arrow Left / Right` — move focus between tabs\n * - `Home / End` — jump to first / last tab\n *\n * @example\n * ```tsx\n * <TabList aria-label=\"Open pages\">\n * {tabs.map(t => <TabTrigger key={t.instanceId} instanceId={t.instanceId} />)}\n * </TabList>\n * ```\n */\nexport function TabList({\n children,\n className,\n style,\n ...ariaProps\n}: TabListProps) {\n const listRef = useRef<HTMLDivElement>(null);\n\n const handleKeyDown = useCallback(\n (e: React.KeyboardEvent<HTMLDivElement>) => {\n const tabElements =\n listRef.current?.querySelectorAll<HTMLElement>('[role=\"tab\"]');\n if (!tabElements || tabElements.length === 0) return;\n\n const tabs = Array.from(tabElements);\n const currentIndex = tabs.findIndex(\n (el) => el === document.activeElement\n );\n\n let nextIndex: number | null = null;\n\n switch (e.key) {\n case \"ArrowRight\":\n case \"ArrowDown\":\n e.preventDefault();\n nextIndex = currentIndex < tabs.length - 1 ? currentIndex + 1 : 0;\n break;\n\n case \"ArrowLeft\":\n case \"ArrowUp\":\n e.preventDefault();\n nextIndex = currentIndex > 0 ? currentIndex - 1 : tabs.length - 1;\n break;\n\n case \"Home\":\n e.preventDefault();\n nextIndex = 0;\n break;\n\n case \"End\":\n e.preventDefault();\n nextIndex = tabs.length - 1;\n break;\n\n default:\n return;\n }\n\n if (nextIndex !== null) {\n tabs[nextIndex].focus();\n }\n },\n []\n );\n\n return (\n <div\n ref={listRef}\n role=\"tablist\"\n className={className}\n style={style}\n onKeyDown={handleKeyDown}\n {...ariaProps}\n >\n {children}\n </div>\n );\n}\n","import { useContext } from \"react\";\nimport { MultiTabContext } from \"../context\";\nimport type { MultiTabContextValue } from \"../types\";\n\n/**\n * Internal hook — use `useMultiTab` or `useTabData` instead.\n *\n * @throws If called outside a `<MultiTabProvider>`.\n * @internal\n */\nexport function useInternalContext(): MultiTabContextValue {\n const ctx = useContext(MultiTabContext);\n if (!ctx) {\n throw new Error(\n \"react-multi-tab: Hooks must be used within a <MultiTabProvider>.\"\n );\n }\n return ctx;\n}\n","import { useCallback } from \"react\";\nimport { useSyncExternalStore } from \"react\";\nimport { useInternalContext } from \"../hooks/useInternalContext\";\n\n/**\n * Props for {@link TabTrigger}.\n */\nexport interface TabTriggerProps {\n /** Instance ID of the tab this trigger controls. */\n instanceId: string;\n children: React.ReactNode;\n className?: string;\n style?: React.CSSProperties;\n disabled?: boolean;\n}\n\n/**\n * Accessible tab trigger button.\n *\n * Implements the **WAI-ARIA** `tab` role:\n * - `role=\"tab\"`\n * - `aria-selected` reflects active state\n * - `aria-controls` points to the matching `tabpanel`\n * - Roving `tabIndex` (`0` for active, `-1` for inactive)\n * - `Enter` / `Space` activates the tab\n *\n * @example\n * ```tsx\n * <TabTrigger instanceId={tab.instanceId}>\n * {tab.label}\n * <TabCloseButton instanceId={tab.instanceId} />\n * </TabTrigger>\n * ```\n */\nexport function TabTrigger({\n instanceId,\n children,\n className,\n style,\n disabled = false,\n}: TabTriggerProps) {\n const { store, activateTab } = useInternalContext();\n const state = useSyncExternalStore(store.subscribe, store.getState);\n const isActive = state.activeTabId === instanceId;\n\n const handleClick = useCallback(() => {\n if (!disabled) activateTab(instanceId);\n }, [instanceId, disabled, activateTab]);\n\n const handleKeyDown = useCallback(\n (e: React.KeyboardEvent) => {\n if ((e.key === \"Enter\" || e.key === \" \") && !disabled) {\n e.preventDefault();\n activateTab(instanceId);\n }\n },\n [instanceId, disabled, activateTab]\n );\n\n return (\n <div\n role=\"tab\"\n id={`rmt-tab-${instanceId}`}\n aria-selected={isActive}\n aria-controls={`rmt-tabpanel-${instanceId}`}\n tabIndex={isActive ? 0 : -1}\n className={className}\n style={style}\n aria-disabled={disabled}\n onClick={handleClick}\n onKeyDown={handleKeyDown}\n data-state={isActive ? \"active\" : \"inactive\"}\n >\n {children}\n </div>\n );\n}\n","/**\n * Props for {@link TabPanel}.\n */\nexport interface TabPanelProps {\n /** Instance ID of the tab this panel belongs to. */\n instanceId: string;\n children: React.ReactNode;\n /** Whether the panel is hidden (inactive tab). */\n hidden?: boolean;\n className?: string;\n style?: React.CSSProperties;\n}\n\n/**\n * Accessible tab panel.\n *\n * Implements the **WAI-ARIA** `tabpanel` role:\n * - `role=\"tabpanel\"`\n * - `aria-labelledby` pointing to the matching `TabTrigger`\n * - `tabIndex={0}` for keyboard focusability\n * - Uses the `hidden` HTML attribute for inactive panels (keeps state\n * mounted — no re-mount on tab switch).\n *\n * @example\n * ```tsx\n * <TabPanel instanceId={tab.instanceId} hidden={!isActive}>\n * <MyPageComponent />\n * </TabPanel>\n * ```\n */\nexport function TabPanel({\n instanceId,\n children,\n hidden = false,\n className,\n style,\n}: TabPanelProps) {\n return (\n <div\n role=\"tabpanel\"\n id={`rmt-tabpanel-${instanceId}`}\n aria-labelledby={`rmt-tab-${instanceId}`}\n tabIndex={0}\n hidden={hidden}\n className={className}\n style={style}\n >\n {children}\n </div>\n );\n}\n","import { createContext, useContext } from \"react\";\n\n/**\n * Context that provides the current tab instance ID.\n * This is meant to be wrapped around each rendered tab's content.\n */\nexport const TabInstanceContext = createContext<string | null>(null);\n\n/**\n * Hook to retrieve the current tab's instance ID.\n * Throws an error if used outside a TabInstanceContext.\n * Extremely useful for integrating with Redux or when components\n * need to automatically know which tab they belong to.\n */\nexport function useTabInstanceId(): string {\n const instanceId = useContext(TabInstanceContext);\n if (!instanceId) {\n throw new Error(\n \"react-multi-tab: useTabInstanceId must be used within a component rendered inside a Tab.\"\n );\n }\n return instanceId;\n}\n","import { TabInstanceContext } from \"../context/TabInstanceContext\";\n\nexport interface TabContentProps {\n instanceId: string;\n children: React.ReactNode;\n}\n\n/**\n * A wrapper component that provides the `TabInstanceContext` to all children.\n * This allows hooks like `useTabData` and `useTabInstanceId` to automatically\n * resolve the active tab's instanceId without it being explicitly passed.\n *\n * @example\n * ```tsx\n * <TabContent instanceId={activeTab.instanceId}>\n * <ActivePageComponent />\n * </TabContent>\n * ```\n */\nexport function TabContent({ instanceId, children }: TabContentProps) {\n return (\n <TabInstanceContext.Provider value={instanceId}>\n {children}\n </TabInstanceContext.Provider>\n );\n}\n","import { useSyncExternalStore } from \"react\";\nimport { useInternalContext } from \"../hooks/useInternalContext\";\nimport { TabContent } from \"./TabContent\";\nimport { TabPanel } from \"./TabPanel\";\n\n/**\n * Props for {@link TabPanels}.\n */\nexport interface TabPanelsProps {\n className?: string;\n style?: React.CSSProperties;\n}\n\n/**\n * Renders **all** open tab panels and hides inactive ones.\n *\n * This is the simplest way to display tab content. Each page component\n * receives its `instanceId`, `data`, and `setData` as props automatically.\n *\n * Inactive panels stay mounted (using the `hidden` attribute) so their\n * state is preserved across tab switches.\n *\n * @example\n * ```tsx\n * <TabPanels />\n * ```\n */\nexport function TabPanels({ className, style }: TabPanelsProps) {\n const { store, registry, setTabData } = useInternalContext();\n const state = useSyncExternalStore(store.subscribe, store.getState);\n\n return (\n <div className={className} style={style}>\n {state.tabs.map((tab) => {\n const page = registry.getPage(tab.pageId);\n if (!page) return null;\n\n const isActive = state.activeTabId === tab.instanceId;\n const data = state.tabData[tab.instanceId] ?? {};\n const Component = page.component;\n\n return (\n <TabPanel\n key={tab.instanceId}\n instanceId={tab.instanceId}\n hidden={!isActive}\n >\n <TabContent instanceId={tab.instanceId}>\n <Component\n instanceId={tab.instanceId}\n data={data}\n setData={(update) =>\n setTabData(tab.instanceId, update as Record<string, unknown>)\n }\n />\n </TabContent>\n </TabPanel>\n );\n })}\n </div>\n );\n}\n","import { useCallback } from \"react\";\nimport { useSyncExternalStore } from \"react\";\nimport { useInternalContext } from \"../hooks/useInternalContext\";\n\n/**\n * Props for {@link TabCloseButton}.\n */\nexport interface TabCloseButtonProps {\n /** Instance ID of the tab to close. */\n instanceId: string;\n /** Custom content (default: `×`). */\n children?: React.ReactNode;\n className?: string;\n style?: React.CSSProperties;\n /** Override the auto-generated aria-label. */\n \"aria-label\"?: string;\n}\n\n/**\n * Button that closes a specific tab.\n *\n * Renders nothing if the tab's `closable` property is `false`.\n * Prevents the click from bubbling to `TabTrigger` so it doesn't\n * accidentally activate the tab.\n *\n * @example\n * ```tsx\n * <TabTrigger instanceId={tab.instanceId}>\n * {tab.label}\n * <TabCloseButton instanceId={tab.instanceId} />\n * </TabTrigger>\n * ```\n */\nexport function TabCloseButton({\n instanceId,\n children,\n className,\n style,\n \"aria-label\": ariaLabel,\n}: TabCloseButtonProps) {\n const { store, closeTab } = useInternalContext();\n const state = useSyncExternalStore(store.subscribe, store.getState);\n const tab = state.tabs.find((t) => t.instanceId === instanceId);\n\n const handleClick = useCallback(\n (e: React.MouseEvent) => {\n e.stopPropagation();\n closeTab(instanceId);\n },\n [instanceId, closeTab]\n );\n\n if (!tab || !tab.closable) return null;\n\n return (\n <button\n type=\"button\"\n aria-label={ariaLabel ?? `Close ${tab.label}`}\n className={className}\n style={style}\n onClick={handleClick}\n tabIndex={-1}\n >\n {children ?? \"×\"}\n </button>\n );\n}\n","import { useSyncExternalStore } from \"react\";\nimport type { OpenTabOptions, TabInstance } from \"../types\";\nimport { useInternalContext } from \"./useInternalContext\";\n\n/** Public return type of {@link useMultiTab}. */\nexport interface UseMultiTabReturn {\n /** Currently open tabs. */\n tabs: TabInstance[];\n /** Instance ID of the active tab, or `null` if none. */\n activeTabId: string | null;\n /** Open a new tab for the given page ID. Returns the instance ID. */\n openTab: (pageId: string, options?: OpenTabOptions) => string;\n /** Close a tab by instance ID. */\n closeTab: (instanceId: string) => void;\n /** Activate (switch to) a tab by instance ID. */\n activateTab: (instanceId: string) => void;\n /** Close every open tab. */\n closeAllTabs: () => void;\n /** Close all tabs except the specified one. */\n closeOtherTabs: (instanceId: string) => void;\n}\n\n/**\n * Primary hook for managing tabs.\n *\n * @example\n * ```tsx\n * const { tabs, activeTabId, openTab, closeTab } = useMultiTab();\n * ```\n */\nexport function useMultiTab(): UseMultiTabReturn {\n const {\n store,\n openTab,\n closeTab,\n activateTab,\n closeAllTabs,\n closeOtherTabs,\n } = useInternalContext();\n\n // Subscribe to the store to get the latest tabs and activeTabId\n const state = useSyncExternalStore(store.subscribe, store.getState);\n\n return {\n tabs: state.tabs,\n activeTabId: state.activeTabId,\n openTab,\n closeTab,\n activateTab,\n closeAllTabs,\n closeOtherTabs,\n };\n}\n","import { useCallback, useContext } from \"react\";\nimport { useSyncExternalStore } from \"react\";\nimport { TabInstanceContext } from \"../context/TabInstanceContext\";\nimport { useInternalContext } from \"./useInternalContext\";\n\nconst EMPTY_DATA = Object.freeze({});\n\n/**\n * Read and write per-tab data with full TypeScript generics.\n *\n * When no `instanceId` is provided, the hook automatically targets the\n * ID from the surrounding `TabContent`. If not inside a `TabContent`,\n * it falls back to the globally active tab.\n *\n * This hook is highly optimized using `useSyncExternalStore` to prevent\n * unnecessary re-renders. A component using this hook will ONLY re-render\n * when the specific data for its resolved `instanceId` changes!\n *\n * @typeParam TData – Shape of the data stored for this tab instance.\n *\n * @example\n * ```tsx\n * interface FormData { name: string; email: string }\n *\n * function MyPage() {\n * const [data, setData] = useTabData<FormData>();\n * // data : FormData\n * // setData: (update: Partial<FormData>) => void\n * }\n * ```\n */\nexport function useTabData<TData = Record<string, unknown>>(\n instanceId?: string\n): [TData, (update: Partial<TData>) => void] {\n const { store, setTabData } = useInternalContext();\n const contextId = useContext(TabInstanceContext);\n\n // Fallback requires us to check store state. We do it once per render safely\n // since activeTabId rarely changes without the store updating anyway.\n const fallbackId = store.getState().activeTabId;\n const resolvedId = instanceId ?? contextId ?? fallbackId;\n\n const getSnapshot = useCallback(() => {\n if (!resolvedId) return EMPTY_DATA as unknown as TData;\n const tabData = store.getState().tabData[resolvedId];\n return (tabData ?? EMPTY_DATA) as unknown as TData;\n }, [resolvedId, store]);\n\n const data = useSyncExternalStore(store.subscribe, getSnapshot);\n\n const setData = useCallback(\n (update: Partial<TData>) => {\n if (resolvedId) {\n setTabData(resolvedId, update as Record<string, unknown>);\n }\n },\n [resolvedId, setTabData]\n );\n\n return [data, setData];\n}\n","import type { PageDefinition, PageRegistry } from \"./types\";\n\n/**\n * Create an immutable page registry from an array of page definitions.\n *\n * @example\n * ```ts\n * const registry = createPageRegistry([\n * { id: 'dashboard', label: 'Dashboard', component: DashboardPage },\n * { id: 'settings', label: 'Settings', component: SettingsPage },\n * ]);\n * ```\n *\n * @throws If any page is missing `id`, `label`, or `component`.\n * @throws If duplicate `id` values are found.\n */\nexport function createPageRegistry(pages: PageDefinition[]): PageRegistry {\n const ids = new Set<string>();\n\n for (const page of pages) {\n if (!page.id) {\n throw new Error(\"react-multi-tab: Page definition must have an id.\");\n }\n if (!page.label) {\n throw new Error(`react-multi-tab: Page \"${page.id}\" must have a label.`);\n }\n if (!page.component) {\n throw new Error(\n `react-multi-tab: Page \"${page.id}\" must have a component.`\n );\n }\n if (ids.has(page.id)) {\n throw new Error(`react-multi-tab: Duplicate page id: \"${page.id}\".`);\n }\n ids.add(page.id);\n }\n\n // Shallow-copy so mutations to the original array don't affect the registry.\n const frozen = [...pages];\n\n return {\n pages: frozen,\n getPage: (id: string) => frozen.find((p) => p.id === id),\n };\n}\n","import type { URLAdapter } from \"../types\";\n\n/**\n * In-memory adapter — no URL synchronisation.\n *\n * This is the implicit default when no `adapter` prop is passed to\n * `MultiTabProvider`. Useful for embedded UIs or tests.\n */\nexport function memoryAdapter(): URLAdapter {\n return {\n read: () => null,\n write: () => {\n /* noop */\n },\n };\n}\n","import type { TabInstance, URLAdapter } from \"../types\";\n\n/** Options for {@link searchParamsAdapter}. */\nexport interface SearchParamsAdapterOptions {\n /** Query-string key for tab instance IDs. @default \"tabs\" */\n tabsParam?: string;\n /** Query-string key for the active tab. @default \"active\" */\n activeParam?: string;\n}\n\n/**\n * URL adapter that uses the vanilla browser `URLSearchParams` +\n * `history.replaceState` APIs.\n *\n * **No dependency on any router library** — works in any SPA.\n *\n * @example\n * ```tsx\n * import { searchParamsAdapter } from 'react-multi-tab';\n *\n * <MultiTabProvider adapter={searchParamsAdapter()} registry={registry}>\n * ```\n */\nexport function searchParamsAdapter(\n options?: SearchParamsAdapterOptions\n): URLAdapter {\n const tabsKey = options?.tabsParam ?? \"tabs\";\n const activeKey = options?.activeParam ?? \"active\";\n\n return {\n read() {\n if (typeof window === \"undefined\") return null;\n\n const params = new URLSearchParams(window.location.search);\n const tabsStr = params.get(tabsKey);\n const activeTab = params.get(activeKey);\n\n if (!tabsStr) return null;\n\n const tabs = tabsStr.split(\",\").filter(Boolean);\n return { tabs, activeTab };\n },\n\n write(tabs: TabInstance[], activeTabId: string | null) {\n if (typeof window === \"undefined\") return;\n\n const params = new URLSearchParams(window.location.search);\n\n if (tabs.length > 0) {\n params.set(tabsKey, tabs.map((t) => t.instanceId).join(\",\"));\n if (activeTabId) {\n params.set(activeKey, activeTabId);\n } else {\n params.delete(activeKey);\n }\n } else {\n params.delete(tabsKey);\n params.delete(activeKey);\n }\n\n const qs = params.toString().replace(/%2C/g, \",\");\n const newUrl = qs\n ? `${window.location.pathname}?${qs}`\n : window.location.pathname;\n\n window.history.replaceState(null, \"\", newUrl);\n },\n\n subscribe(callback: () => void) {\n window.addEventListener(\"popstate\", callback);\n return () => window.removeEventListener(\"popstate\", callback);\n },\n };\n}\n"]}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { ComponentType, ReactNode } from 'react';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Definition of a page type that can be opened as a tab.
|
|
5
|
+
*
|
|
6
|
+
* @typeParam TData – Shape of the per-tab data store for this page.
|
|
7
|
+
*/
|
|
8
|
+
interface PageDefinition<TData = Record<string, unknown>> {
|
|
9
|
+
/** Unique identifier for this page type. */
|
|
10
|
+
id: string;
|
|
11
|
+
/** Default display label shown on the tab. */
|
|
12
|
+
label: string;
|
|
13
|
+
/** React component rendered inside the tab panel. */
|
|
14
|
+
component: ComponentType<TabComponentProps<TData>>;
|
|
15
|
+
/** Optional icon element displayed next to the label. */
|
|
16
|
+
icon?: ReactNode;
|
|
17
|
+
/** Whether this tab can be closed by the user. @default true */
|
|
18
|
+
closable?: boolean;
|
|
19
|
+
}
|
|
20
|
+
/** Represents a single open tab at runtime. */
|
|
21
|
+
interface TabInstance {
|
|
22
|
+
/** Unique runtime identifier (auto-generated or user-provided). */
|
|
23
|
+
instanceId: string;
|
|
24
|
+
/** The `id` of the corresponding {@link PageDefinition}. */
|
|
25
|
+
pageId: string;
|
|
26
|
+
/** Display label for the tab. */
|
|
27
|
+
label: string;
|
|
28
|
+
/** Whether this tab can be closed. */
|
|
29
|
+
closable: boolean;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Props injected into every page component rendered inside a tab.
|
|
33
|
+
*
|
|
34
|
+
* @typeParam TData – Shape of the per-tab data store.
|
|
35
|
+
*/
|
|
36
|
+
interface TabComponentProps<TData = Record<string, unknown>> {
|
|
37
|
+
/** Unique instance identifier of the owning tab. */
|
|
38
|
+
instanceId: string;
|
|
39
|
+
/** Current data for this tab instance. */
|
|
40
|
+
data: TData;
|
|
41
|
+
/** Merge partial data into this tab's store. */
|
|
42
|
+
setData: (update: Partial<TData>) => void;
|
|
43
|
+
}
|
|
44
|
+
/** Options accepted by `openTab`. */
|
|
45
|
+
interface OpenTabOptions {
|
|
46
|
+
/** Custom label (overrides the page definition's label). */
|
|
47
|
+
label?: string;
|
|
48
|
+
/** Custom instance ID (default: auto-generated). */
|
|
49
|
+
instanceId?: string;
|
|
50
|
+
/** Whether to activate the tab immediately. @default true */
|
|
51
|
+
activate?: boolean;
|
|
52
|
+
}
|
|
53
|
+
/** Internal state managed by the provider's reducer. */
|
|
54
|
+
interface MultiTabState {
|
|
55
|
+
tabs: TabInstance[];
|
|
56
|
+
activeTabId: string | null;
|
|
57
|
+
tabData: Record<string, Record<string, unknown>>;
|
|
58
|
+
/** Tracks the history of active tabs to restore the previous one on close. */
|
|
59
|
+
activeTabHistory: string[];
|
|
60
|
+
}
|
|
61
|
+
/** Immutable page registry returned by {@link createPageRegistry}. */
|
|
62
|
+
interface PageRegistry {
|
|
63
|
+
/** Registered page definitions. */
|
|
64
|
+
pages: ReadonlyArray<PageDefinition>;
|
|
65
|
+
/** Look up a page by its `id`. Returns `undefined` if not found. */
|
|
66
|
+
getPage: (id: string) => PageDefinition | undefined;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Adapter interface for syncing tab state with an external store (URL, etc.).
|
|
70
|
+
*
|
|
71
|
+
* Implement this to integrate with any routing solution.
|
|
72
|
+
*/
|
|
73
|
+
interface URLAdapter {
|
|
74
|
+
/** Read initial / current tab state from the external store. */
|
|
75
|
+
read(): {
|
|
76
|
+
tabs: string[];
|
|
77
|
+
activeTab: string | null;
|
|
78
|
+
} | null;
|
|
79
|
+
/** Persist current tab state to the external store. */
|
|
80
|
+
write(tabs: TabInstance[], activeTabId: string | null): void;
|
|
81
|
+
/** Subscribe to external changes (e.g. browser back/forward). */
|
|
82
|
+
subscribe?(callback: () => void): () => void;
|
|
83
|
+
/** Cleanup resources. Called when the provider unmounts. */
|
|
84
|
+
destroy?(): void;
|
|
85
|
+
}
|
|
86
|
+
/** Props for {@link MultiTabProvider}. */
|
|
87
|
+
interface MultiTabProviderProps {
|
|
88
|
+
/** Page registry created via `createPageRegistry`. */
|
|
89
|
+
registry: PageRegistry;
|
|
90
|
+
/** Optional URL adapter for state persistence. */
|
|
91
|
+
adapter?: URLAdapter;
|
|
92
|
+
/** Tab to activate on first mount (instanceId). */
|
|
93
|
+
defaultActiveTab?: string;
|
|
94
|
+
children: ReactNode;
|
|
95
|
+
/** Fired after a new tab is opened. */
|
|
96
|
+
onTabOpen?: (instance: TabInstance) => void;
|
|
97
|
+
/** Fired after a tab is closed. */
|
|
98
|
+
onTabClose?: (instanceId: string) => void;
|
|
99
|
+
/** Fired when the active tab changes. */
|
|
100
|
+
onTabChange?: (instanceId: string) => void;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export type { MultiTabProviderProps as M, OpenTabOptions as O, PageDefinition as P, TabInstance as T, URLAdapter as U, PageRegistry as a, MultiTabState as b, TabComponentProps as c };
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { ComponentType, ReactNode } from 'react';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Definition of a page type that can be opened as a tab.
|
|
5
|
+
*
|
|
6
|
+
* @typeParam TData – Shape of the per-tab data store for this page.
|
|
7
|
+
*/
|
|
8
|
+
interface PageDefinition<TData = Record<string, unknown>> {
|
|
9
|
+
/** Unique identifier for this page type. */
|
|
10
|
+
id: string;
|
|
11
|
+
/** Default display label shown on the tab. */
|
|
12
|
+
label: string;
|
|
13
|
+
/** React component rendered inside the tab panel. */
|
|
14
|
+
component: ComponentType<TabComponentProps<TData>>;
|
|
15
|
+
/** Optional icon element displayed next to the label. */
|
|
16
|
+
icon?: ReactNode;
|
|
17
|
+
/** Whether this tab can be closed by the user. @default true */
|
|
18
|
+
closable?: boolean;
|
|
19
|
+
}
|
|
20
|
+
/** Represents a single open tab at runtime. */
|
|
21
|
+
interface TabInstance {
|
|
22
|
+
/** Unique runtime identifier (auto-generated or user-provided). */
|
|
23
|
+
instanceId: string;
|
|
24
|
+
/** The `id` of the corresponding {@link PageDefinition}. */
|
|
25
|
+
pageId: string;
|
|
26
|
+
/** Display label for the tab. */
|
|
27
|
+
label: string;
|
|
28
|
+
/** Whether this tab can be closed. */
|
|
29
|
+
closable: boolean;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Props injected into every page component rendered inside a tab.
|
|
33
|
+
*
|
|
34
|
+
* @typeParam TData – Shape of the per-tab data store.
|
|
35
|
+
*/
|
|
36
|
+
interface TabComponentProps<TData = Record<string, unknown>> {
|
|
37
|
+
/** Unique instance identifier of the owning tab. */
|
|
38
|
+
instanceId: string;
|
|
39
|
+
/** Current data for this tab instance. */
|
|
40
|
+
data: TData;
|
|
41
|
+
/** Merge partial data into this tab's store. */
|
|
42
|
+
setData: (update: Partial<TData>) => void;
|
|
43
|
+
}
|
|
44
|
+
/** Options accepted by `openTab`. */
|
|
45
|
+
interface OpenTabOptions {
|
|
46
|
+
/** Custom label (overrides the page definition's label). */
|
|
47
|
+
label?: string;
|
|
48
|
+
/** Custom instance ID (default: auto-generated). */
|
|
49
|
+
instanceId?: string;
|
|
50
|
+
/** Whether to activate the tab immediately. @default true */
|
|
51
|
+
activate?: boolean;
|
|
52
|
+
}
|
|
53
|
+
/** Internal state managed by the provider's reducer. */
|
|
54
|
+
interface MultiTabState {
|
|
55
|
+
tabs: TabInstance[];
|
|
56
|
+
activeTabId: string | null;
|
|
57
|
+
tabData: Record<string, Record<string, unknown>>;
|
|
58
|
+
/** Tracks the history of active tabs to restore the previous one on close. */
|
|
59
|
+
activeTabHistory: string[];
|
|
60
|
+
}
|
|
61
|
+
/** Immutable page registry returned by {@link createPageRegistry}. */
|
|
62
|
+
interface PageRegistry {
|
|
63
|
+
/** Registered page definitions. */
|
|
64
|
+
pages: ReadonlyArray<PageDefinition>;
|
|
65
|
+
/** Look up a page by its `id`. Returns `undefined` if not found. */
|
|
66
|
+
getPage: (id: string) => PageDefinition | undefined;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Adapter interface for syncing tab state with an external store (URL, etc.).
|
|
70
|
+
*
|
|
71
|
+
* Implement this to integrate with any routing solution.
|
|
72
|
+
*/
|
|
73
|
+
interface URLAdapter {
|
|
74
|
+
/** Read initial / current tab state from the external store. */
|
|
75
|
+
read(): {
|
|
76
|
+
tabs: string[];
|
|
77
|
+
activeTab: string | null;
|
|
78
|
+
} | null;
|
|
79
|
+
/** Persist current tab state to the external store. */
|
|
80
|
+
write(tabs: TabInstance[], activeTabId: string | null): void;
|
|
81
|
+
/** Subscribe to external changes (e.g. browser back/forward). */
|
|
82
|
+
subscribe?(callback: () => void): () => void;
|
|
83
|
+
/** Cleanup resources. Called when the provider unmounts. */
|
|
84
|
+
destroy?(): void;
|
|
85
|
+
}
|
|
86
|
+
/** Props for {@link MultiTabProvider}. */
|
|
87
|
+
interface MultiTabProviderProps {
|
|
88
|
+
/** Page registry created via `createPageRegistry`. */
|
|
89
|
+
registry: PageRegistry;
|
|
90
|
+
/** Optional URL adapter for state persistence. */
|
|
91
|
+
adapter?: URLAdapter;
|
|
92
|
+
/** Tab to activate on first mount (instanceId). */
|
|
93
|
+
defaultActiveTab?: string;
|
|
94
|
+
children: ReactNode;
|
|
95
|
+
/** Fired after a new tab is opened. */
|
|
96
|
+
onTabOpen?: (instance: TabInstance) => void;
|
|
97
|
+
/** Fired after a tab is closed. */
|
|
98
|
+
onTabClose?: (instanceId: string) => void;
|
|
99
|
+
/** Fired when the active tab changes. */
|
|
100
|
+
onTabChange?: (instanceId: string) => void;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export type { MultiTabProviderProps as M, OpenTabOptions as O, PageDefinition as P, TabInstance as T, URLAdapter as U, PageRegistry as a, MultiTabState as b, TabComponentProps as c };
|
package/package.json
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "react-multi-tab",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "A headless, accessible, router-agnostic multi-tab component library for React with TypeScript generics",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"author": "gevenci.arif@gmail.com",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "https://github.com/arifgevinci/react-multi-tab.git"
|
|
11
|
+
},
|
|
12
|
+
"keywords": [
|
|
13
|
+
"react",
|
|
14
|
+
"tabs",
|
|
15
|
+
"multi-tab",
|
|
16
|
+
"accessible",
|
|
17
|
+
"headless",
|
|
18
|
+
"typescript",
|
|
19
|
+
"a11y",
|
|
20
|
+
"wai-aria",
|
|
21
|
+
"router-agnostic"
|
|
22
|
+
],
|
|
23
|
+
"main": "./dist/index.cjs",
|
|
24
|
+
"module": "./dist/index.mjs",
|
|
25
|
+
"types": "./dist/index.d.ts",
|
|
26
|
+
"exports": {
|
|
27
|
+
".": {
|
|
28
|
+
"import": {
|
|
29
|
+
"types": "./dist/index.d.mts",
|
|
30
|
+
"default": "./dist/index.mjs"
|
|
31
|
+
},
|
|
32
|
+
"require": {
|
|
33
|
+
"types": "./dist/index.d.cts",
|
|
34
|
+
"default": "./dist/index.cjs"
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
"./adapters/react-router": {
|
|
38
|
+
"import": {
|
|
39
|
+
"types": "./dist/adapters/react-router.d.mts",
|
|
40
|
+
"default": "./dist/adapters/react-router.mjs"
|
|
41
|
+
},
|
|
42
|
+
"require": {
|
|
43
|
+
"types": "./dist/adapters/react-router.d.cts",
|
|
44
|
+
"default": "./dist/adapters/react-router.cjs"
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
"files": [
|
|
49
|
+
"dist",
|
|
50
|
+
"README.md",
|
|
51
|
+
"LICENSE"
|
|
52
|
+
],
|
|
53
|
+
"sideEffects": false,
|
|
54
|
+
"scripts": {
|
|
55
|
+
"build": "tsup",
|
|
56
|
+
"dev": "tsup --watch",
|
|
57
|
+
"dev:demo": "cd playground && npm run dev",
|
|
58
|
+
"test": "vitest run",
|
|
59
|
+
"test:watch": "vitest",
|
|
60
|
+
"test:coverage": "vitest run --coverage",
|
|
61
|
+
"typecheck": "tsc --noEmit",
|
|
62
|
+
"lint": "eslint src/",
|
|
63
|
+
"lint:fix": "eslint src/ --fix",
|
|
64
|
+
"format": "prettier --write 'src/**/*.{ts,tsx,json}'",
|
|
65
|
+
"prepublishOnly": "npm run typecheck && npm run test && npm run build",
|
|
66
|
+
"prepare": "husky"
|
|
67
|
+
},
|
|
68
|
+
"lint-staged": {
|
|
69
|
+
"**/*.{ts,tsx}": [
|
|
70
|
+
"eslint --fix",
|
|
71
|
+
"prettier --write"
|
|
72
|
+
]
|
|
73
|
+
},
|
|
74
|
+
"peerDependencies": {
|
|
75
|
+
"react": ">=18.0.0",
|
|
76
|
+
"react-dom": ">=18.0.0"
|
|
77
|
+
},
|
|
78
|
+
"peerDependenciesMeta": {
|
|
79
|
+
"react-router-dom": {
|
|
80
|
+
"optional": true
|
|
81
|
+
}
|
|
82
|
+
},
|
|
83
|
+
"devDependencies": {
|
|
84
|
+
"@eslint/js": "^9.21.0",
|
|
85
|
+
"@tailwindcss/postcss": "^4.3.0",
|
|
86
|
+
"@testing-library/dom": "^10.4.1",
|
|
87
|
+
"@testing-library/jest-dom": "^6.6.3",
|
|
88
|
+
"@testing-library/react": "^16.3.0",
|
|
89
|
+
"@testing-library/user-event": "^14.6.1",
|
|
90
|
+
"@trivago/prettier-plugin-sort-imports": "^5.2.2",
|
|
91
|
+
"@types/react": "^19.0.10",
|
|
92
|
+
"@types/react-dom": "^19.0.4",
|
|
93
|
+
"@vitejs/plugin-react": "^4.0.0",
|
|
94
|
+
"@vitest/coverage-v8": "^3.2.1",
|
|
95
|
+
"autoprefixer": "^10.5.0",
|
|
96
|
+
"eslint": "^9.21.0",
|
|
97
|
+
"eslint-config-prettier": "^10.1.1",
|
|
98
|
+
"eslint-plugin-react": "^7.37.4",
|
|
99
|
+
"eslint-plugin-react-hooks": "^5.2.0",
|
|
100
|
+
"globals": "^15.15.0",
|
|
101
|
+
"husky": "^9.1.7",
|
|
102
|
+
"jsdom": "^26.1.0",
|
|
103
|
+
"lint-staged": "^15.5.0",
|
|
104
|
+
"postcss": "^8.5.15",
|
|
105
|
+
"prettier": "^3.5.3",
|
|
106
|
+
"react": "^19.0.0",
|
|
107
|
+
"react-dom": "^19.0.0",
|
|
108
|
+
"react-router-dom": "^6.28.0",
|
|
109
|
+
"sass": "^1.99.0",
|
|
110
|
+
"tailwindcss": "^4.3.0",
|
|
111
|
+
"tsup": "^8.5.0",
|
|
112
|
+
"typescript": "~5.7.2",
|
|
113
|
+
"typescript-eslint": "^8.24.1",
|
|
114
|
+
"vite": "^5.0.0",
|
|
115
|
+
"vitest": "^3.2.1"
|
|
116
|
+
}
|
|
117
|
+
}
|