@cerberus-design/react 0.13.2-next-61259ca → 0.14.0-next-01a1b5e
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/build/legacy/_tsup-dts-rollup.d.cts +103 -107
- package/build/legacy/components/Tabs.client.cjs +104 -0
- package/build/legacy/components/Tabs.client.cjs.map +1 -0
- package/build/legacy/context/tabs.cjs +53 -24
- package/build/legacy/context/tabs.cjs.map +1 -1
- package/build/legacy/index.cjs +320 -371
- package/build/legacy/index.cjs.map +1 -1
- package/build/legacy/utils/index.cjs +21 -2
- package/build/legacy/utils/index.cjs.map +1 -1
- package/build/legacy/utils/localStorage.cjs +44 -0
- package/build/legacy/utils/localStorage.cjs.map +1 -0
- package/build/modern/_tsup-dts-rollup.d.ts +103 -107
- package/build/modern/{chunk-FRWNAVDM.js → chunk-77B4HFKT.js} +4 -4
- package/build/modern/{chunk-G7AGKII3.js → chunk-BHSYQCDV.js} +4 -4
- package/build/modern/{chunk-UTGEFJ3L.js → chunk-C5EHJUS5.js} +1 -1
- package/build/modern/{chunk-UTGEFJ3L.js.map → chunk-C5EHJUS5.js.map} +1 -1
- package/build/modern/chunk-FQLMPEYX.js +18 -0
- package/build/modern/chunk-FQLMPEYX.js.map +1 -0
- package/build/modern/chunk-N7O6D7BW.js +63 -0
- package/build/modern/chunk-N7O6D7BW.js.map +1 -0
- package/build/modern/chunk-RZ7NG77U.js +77 -0
- package/build/modern/chunk-RZ7NG77U.js.map +1 -0
- package/build/modern/{chunk-ECKJGCNH.js → chunk-T6U7AKKP.js} +4 -4
- package/build/modern/components/Tabs.client.js +18 -0
- package/build/modern/context/confirm-modal.js +2 -2
- package/build/modern/context/cta-modal.js +2 -2
- package/build/modern/context/prompt-modal.js +2 -2
- package/build/modern/context/tabs.js +2 -1
- package/build/modern/index.js +42 -40
- package/build/modern/index.js.map +1 -1
- package/build/modern/utils/index.js +8 -2
- package/build/modern/utils/localStorage.js +10 -0
- package/package.json +2 -2
- package/src/components/Tabs.client.tsx +113 -0
- package/src/context/tabs.tsx +66 -65
- package/src/index.ts +1 -4
- package/src/utils/index.ts +2 -0
- package/src/utils/localStorage.ts +28 -0
- package/build/legacy/aria-helpers/tabs.aria.cjs +0 -100
- package/build/legacy/aria-helpers/tabs.aria.cjs.map +0 -1
- package/build/legacy/components/Tab.cjs +0 -135
- package/build/legacy/components/Tab.cjs.map +0 -1
- package/build/legacy/components/TabList.cjs +0 -69
- package/build/legacy/components/TabList.cjs.map +0 -1
- package/build/legacy/components/TabPanel.cjs +0 -76
- package/build/legacy/components/TabPanel.cjs.map +0 -1
- package/build/modern/aria-helpers/tabs.aria.js +0 -9
- package/build/modern/chunk-AYIRV5CL.js +0 -32
- package/build/modern/chunk-AYIRV5CL.js.map +0 -1
- package/build/modern/chunk-ITOIXNJS.js +0 -64
- package/build/modern/chunk-ITOIXNJS.js.map +0 -1
- package/build/modern/chunk-RIFQSCHT.js +0 -58
- package/build/modern/chunk-RIFQSCHT.js.map +0 -1
- package/build/modern/chunk-SLF6SIPB.js +0 -46
- package/build/modern/chunk-SLF6SIPB.js.map +0 -1
- package/build/modern/chunk-UKPF7JOB.js +0 -33
- package/build/modern/chunk-UKPF7JOB.js.map +0 -1
- package/build/modern/components/Tab.js +0 -10
- package/build/modern/components/TabList.js +0 -9
- package/build/modern/components/TabList.js.map +0 -1
- package/build/modern/components/TabPanel.js +0 -10
- package/build/modern/components/TabPanel.js.map +0 -1
- package/src/aria-helpers/tabs.aria.ts +0 -70
- package/src/components/Tab.tsx +0 -68
- package/src/components/TabList.tsx +0 -51
- package/src/components/TabPanel.tsx +0 -49
- /package/build/modern/{chunk-FRWNAVDM.js.map → chunk-77B4HFKT.js.map} +0 -0
- /package/build/modern/{chunk-G7AGKII3.js.map → chunk-BHSYQCDV.js.map} +0 -0
- /package/build/modern/{chunk-ECKJGCNH.js.map → chunk-T6U7AKKP.js.map} +0 -0
- /package/build/modern/{aria-helpers/tabs.aria.js.map → components/Tabs.client.js.map} +0 -0
- /package/build/modern/{components/Tab.js.map → utils/localStorage.js.map} +0 -0
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
// src/context/tabs.tsx
|
|
2
|
-
import { tabs } from "@cerberus/styled-system/recipes";
|
|
3
|
-
import {
|
|
4
|
-
createContext,
|
|
5
|
-
useContext,
|
|
6
|
-
useEffect,
|
|
7
|
-
useMemo,
|
|
8
|
-
useRef,
|
|
9
|
-
useState
|
|
10
|
-
} from "react";
|
|
11
|
-
import { jsx } from "react/jsx-runtime";
|
|
12
|
-
var TabsContext = createContext(null);
|
|
13
|
-
function Tabs(props) {
|
|
14
|
-
const { cache, active, id, palette } = props;
|
|
15
|
-
const [activeTab, setActiveTab] = useState(() => cache ? "" : active ?? "");
|
|
16
|
-
const tabsList = useRef([]);
|
|
17
|
-
const uuid = useMemo(() => {
|
|
18
|
-
return id ? `cerberus-tabs-${id}` : "cerberus-tabs";
|
|
19
|
-
}, [id]);
|
|
20
|
-
const value = useMemo(
|
|
21
|
-
() => ({
|
|
22
|
-
tabs: tabsList,
|
|
23
|
-
id: uuid,
|
|
24
|
-
active: activeTab,
|
|
25
|
-
styles: tabs({ palette }),
|
|
26
|
-
onTabUpdate: setActiveTab
|
|
27
|
-
}),
|
|
28
|
-
[activeTab, setActiveTab, palette, uuid, tabsList]
|
|
29
|
-
);
|
|
30
|
-
useEffect(() => {
|
|
31
|
-
if (cache) {
|
|
32
|
-
const cachedTab = window.localStorage.getItem(uuid);
|
|
33
|
-
setActiveTab(
|
|
34
|
-
cache ? cachedTab || (props.active ?? "") : props.active ?? ""
|
|
35
|
-
);
|
|
36
|
-
}
|
|
37
|
-
}, [cache, active, uuid]);
|
|
38
|
-
useEffect(() => {
|
|
39
|
-
if (cache && activeTab) {
|
|
40
|
-
window.localStorage.setItem(uuid, activeTab);
|
|
41
|
-
}
|
|
42
|
-
}, [activeTab, cache]);
|
|
43
|
-
return /* @__PURE__ */ jsx(TabsContext.Provider, { value, children: props.children });
|
|
44
|
-
}
|
|
45
|
-
function useTabsContext() {
|
|
46
|
-
const context = useContext(TabsContext);
|
|
47
|
-
if (!context) {
|
|
48
|
-
throw new Error("useTabsContext must be used within a Tabs Provider.");
|
|
49
|
-
}
|
|
50
|
-
return context;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
export {
|
|
54
|
-
TabsContext,
|
|
55
|
-
Tabs,
|
|
56
|
-
useTabsContext
|
|
57
|
-
};
|
|
58
|
-
//# sourceMappingURL=chunk-RIFQSCHT.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/context/tabs.tsx"],"sourcesContent":["'use client'\n\nimport { tabs, type TabsVariantProps } from '@cerberus/styled-system/recipes'\nimport type { Pretty } from '@cerberus/styled-system/types'\nimport {\n createContext,\n useContext,\n useEffect,\n useMemo,\n useRef,\n useState,\n type MutableRefObject,\n type PropsWithChildren,\n} from 'react'\n\n/**\n * This module provides a Tabs component and a hook to access its context.\n * @module Tabs\n */\n\nexport interface TabsContextValue {\n /**\n * The ref for the tabs.\n */\n tabs: MutableRefObject<HTMLButtonElement[]>\n /**\n * The id of the tabs component.\n */\n id: string\n /**\n * The active tab id.\n */\n active: string\n /**\n * The styles for the tabs.\n */\n styles: Pretty<Record<'tabList' | 'tab' | 'tabPanel', string>>\n /**\n * Called when the active tab is updated.\n */\n onTabUpdate: (active: string) => void\n}\n\nexport const TabsContext = createContext<TabsContextValue | null>(null)\n\nexport interface TabsProps {\n /**\n * A unique identifier for the Tabs component. Typically used when there are\n * multiple Tabs components on the same page.\n */\n id?: string\n /**\n * The default active tab id.\n */\n active?: string\n /**\n * Whether to cache the active tab state in local storage.\n */\n cache?: boolean\n}\n\n/**\n * The Tabs component provides a context to manage tab state.\n * @see https://cerberus.digitalu.design/react/tabs\n * @example\n * ```tsx\n * <Tabs cache>\n * <TabList description=\"Button details\">\n * <Tab id=\"overview\">Overview</Tab>\n * <Tab id=\"guidelines\">Guidelines</Tab>\n * </TabList>\n * <TabPanels>\n * <TabPanel id=\"overview\">Overview content</TabPanel>\n * <TabPanel id=\"guidelines\">Guidelines content</TabPanel>\n * </TabPanels>\n * </Tabs>\n * ```\n */\nexport function Tabs(\n props: PropsWithChildren<TabsProps & TabsVariantProps>,\n): JSX.Element {\n const { cache, active, id, palette } = props\n const [activeTab, setActiveTab] = useState(() => (cache ? '' : active ?? ''))\n const tabsList = useRef<HTMLButtonElement[]>([])\n const uuid = useMemo(() => {\n return id ? `cerberus-tabs-${id}` : 'cerberus-tabs'\n }, [id])\n\n const value = useMemo(\n () => ({\n tabs: tabsList,\n id: uuid,\n active: activeTab,\n styles: tabs({ palette }),\n onTabUpdate: setActiveTab,\n }),\n [activeTab, setActiveTab, palette, uuid, tabsList],\n )\n\n // Get the active tab from local storage\n useEffect(() => {\n if (cache) {\n const cachedTab = window.localStorage.getItem(uuid)\n setActiveTab(\n cache ? cachedTab || (props.active ?? '') : props.active ?? '',\n )\n }\n }, [cache, active, uuid])\n\n // Update the active tab in local storage\n useEffect(() => {\n if (cache && activeTab) {\n window.localStorage.setItem(uuid, activeTab)\n }\n }, [activeTab, cache])\n\n return (\n <TabsContext.Provider value={value}>{props.children}</TabsContext.Provider>\n )\n}\n\n/**\n * Used to access the tabs context.\n * @returns The tabs context.\n */\nexport function useTabsContext(): TabsContextValue {\n const context = useContext(TabsContext)\n if (!context) {\n throw new Error('useTabsContext must be used within a Tabs Provider.')\n }\n return context\n}\n"],"mappings":";AAEA,SAAS,YAAmC;AAE5C;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAGK;AAwGH;AA1EG,IAAM,cAAc,cAAuC,IAAI;AAmC/D,SAAS,KACd,OACa;AACb,QAAM,EAAE,OAAO,QAAQ,IAAI,QAAQ,IAAI;AACvC,QAAM,CAAC,WAAW,YAAY,IAAI,SAAS,MAAO,QAAQ,KAAK,UAAU,EAAG;AAC5E,QAAM,WAAW,OAA4B,CAAC,CAAC;AAC/C,QAAM,OAAO,QAAQ,MAAM;AACzB,WAAO,KAAK,iBAAiB,EAAE,KAAK;AAAA,EACtC,GAAG,CAAC,EAAE,CAAC;AAEP,QAAM,QAAQ;AAAA,IACZ,OAAO;AAAA,MACL,MAAM;AAAA,MACN,IAAI;AAAA,MACJ,QAAQ;AAAA,MACR,QAAQ,KAAK,EAAE,QAAQ,CAAC;AAAA,MACxB,aAAa;AAAA,IACf;AAAA,IACA,CAAC,WAAW,cAAc,SAAS,MAAM,QAAQ;AAAA,EACnD;AAGA,YAAU,MAAM;AACd,QAAI,OAAO;AACT,YAAM,YAAY,OAAO,aAAa,QAAQ,IAAI;AAClD;AAAA,QACE,QAAQ,cAAc,MAAM,UAAU,MAAM,MAAM,UAAU;AAAA,MAC9D;AAAA,IACF;AAAA,EACF,GAAG,CAAC,OAAO,QAAQ,IAAI,CAAC;AAGxB,YAAU,MAAM;AACd,QAAI,SAAS,WAAW;AACtB,aAAO,aAAa,QAAQ,MAAM,SAAS;AAAA,IAC7C;AAAA,EACF,GAAG,CAAC,WAAW,KAAK,CAAC;AAErB,SACE,oBAAC,YAAY,UAAZ,EAAqB,OAAe,gBAAM,UAAS;AAExD;AAMO,SAAS,iBAAmC;AACjD,QAAM,UAAU,WAAW,WAAW;AACtC,MAAI,CAAC,SAAS;AACZ,UAAM,IAAI,MAAM,qDAAqD;AAAA,EACvE;AACA,SAAO;AACT;","names":[]}
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
useTabsKeyboardNavigation
|
|
3
|
-
} from "./chunk-ITOIXNJS.js";
|
|
4
|
-
import {
|
|
5
|
-
useTabsContext
|
|
6
|
-
} from "./chunk-RIFQSCHT.js";
|
|
7
|
-
|
|
8
|
-
// src/components/Tab.tsx
|
|
9
|
-
import {
|
|
10
|
-
useMemo,
|
|
11
|
-
useTransition
|
|
12
|
-
} from "react";
|
|
13
|
-
import { cx } from "@cerberus/styled-system/css";
|
|
14
|
-
import { jsx } from "react/jsx-runtime";
|
|
15
|
-
function Tab(props) {
|
|
16
|
-
const { value, ...nativeProps } = props;
|
|
17
|
-
const { active, onTabUpdate, styles } = useTabsContext();
|
|
18
|
-
const [isPending, startTransition] = useTransition();
|
|
19
|
-
const { ref } = useTabsKeyboardNavigation();
|
|
20
|
-
const isActive = useMemo(() => active === value, [active, value]);
|
|
21
|
-
function handleClick(e) {
|
|
22
|
-
props.onClick?.(e);
|
|
23
|
-
startTransition(() => onTabUpdate(e.currentTarget.value));
|
|
24
|
-
}
|
|
25
|
-
return /* @__PURE__ */ jsx(
|
|
26
|
-
"button",
|
|
27
|
-
{
|
|
28
|
-
...nativeProps,
|
|
29
|
-
...!isActive && { tabIndex: -1 },
|
|
30
|
-
"aria-controls": `panel:${value}`,
|
|
31
|
-
"aria-busy": isPending,
|
|
32
|
-
"aria-selected": isActive,
|
|
33
|
-
id: `tab:${value}`,
|
|
34
|
-
className: cx(nativeProps.className, styles.tab),
|
|
35
|
-
onClick: handleClick,
|
|
36
|
-
role: "tab",
|
|
37
|
-
ref,
|
|
38
|
-
value
|
|
39
|
-
}
|
|
40
|
-
);
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
export {
|
|
44
|
-
Tab
|
|
45
|
-
};
|
|
46
|
-
//# sourceMappingURL=chunk-SLF6SIPB.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/Tab.tsx"],"sourcesContent":["'use client'\n\nimport {\n useMemo,\n useTransition,\n type ButtonHTMLAttributes,\n type MouseEvent,\n} from 'react'\nimport { useTabsContext } from '../context/tabs'\nimport { cx } from '@cerberus/styled-system/css'\nimport { useTabsKeyboardNavigation } from '../aria-helpers/tabs.aria'\n\n/**\n * This module provides a Tab component.\n * @module\n */\n\nexport interface TabProps extends ButtonHTMLAttributes<HTMLButtonElement> {\n /**\n * The id of the tab that will be tracked as the active tab and used for aria\n * attributes.\n */\n value: string\n}\n\n/**\n * The Tab component provides a tab element to be used in a TabList.\n * @definition [ARIA Target Size](https://www.w3.org/WAI/WCAG21/Understanding/target-size.html#:~:text=Understanding%20SC%202.5.,%3ATarget%20Size%20(Level%20AAA)&text=The%20size%20of%20the%20target,Equivalent)\n * @see https://cerberus.digitalu.design/react/tabs\n * @memberof module:Tabs\n * @example\n * ```tsx\n * <Tabs>\n * <TabList description=\"Profile settings\">\n * <Tab value=\"overview\">Overview</Tab>\n * </TabList>\n * <TabPanel tab=\"overview\">...</TabPanel>\n * </Tabs>\n * ```\n */\nexport function Tab(props: TabProps) {\n const { value, ...nativeProps } = props\n const { active, onTabUpdate, styles } = useTabsContext()\n const [isPending, startTransition] = useTransition()\n const { ref } = useTabsKeyboardNavigation()\n const isActive = useMemo(() => active === value, [active, value])\n\n function handleClick(e: MouseEvent<HTMLButtonElement>) {\n props.onClick?.(e)\n startTransition(() => onTabUpdate(e.currentTarget.value))\n }\n\n return (\n <button\n {...nativeProps}\n {...(!isActive && { tabIndex: -1 })}\n aria-controls={`panel:${value}`}\n aria-busy={isPending}\n aria-selected={isActive}\n id={`tab:${value}`}\n className={cx(nativeProps.className, styles.tab)}\n onClick={handleClick}\n role=\"tab\"\n ref={ref}\n value={value}\n />\n )\n}\n"],"mappings":";;;;;;;;AAEA;AAAA,EACE;AAAA,EACA;AAAA,OAGK;AAEP,SAAS,UAAU;AA4Cf;AAbG,SAAS,IAAI,OAAiB;AACnC,QAAM,EAAE,OAAO,GAAG,YAAY,IAAI;AAClC,QAAM,EAAE,QAAQ,aAAa,OAAO,IAAI,eAAe;AACvD,QAAM,CAAC,WAAW,eAAe,IAAI,cAAc;AACnD,QAAM,EAAE,IAAI,IAAI,0BAA0B;AAC1C,QAAM,WAAW,QAAQ,MAAM,WAAW,OAAO,CAAC,QAAQ,KAAK,CAAC;AAEhE,WAAS,YAAY,GAAkC;AACrD,UAAM,UAAU,CAAC;AACjB,oBAAgB,MAAM,YAAY,EAAE,cAAc,KAAK,CAAC;AAAA,EAC1D;AAEA,SACE;AAAA,IAAC;AAAA;AAAA,MACE,GAAG;AAAA,MACH,GAAI,CAAC,YAAY,EAAE,UAAU,GAAG;AAAA,MACjC,iBAAe,SAAS,KAAK;AAAA,MAC7B,aAAW;AAAA,MACX,iBAAe;AAAA,MACf,IAAI,OAAO,KAAK;AAAA,MAChB,WAAW,GAAG,YAAY,WAAW,OAAO,GAAG;AAAA,MAC/C,SAAS;AAAA,MACT,MAAK;AAAA,MACL;AAAA,MACA;AAAA;AAAA,EACF;AAEJ;","names":[]}
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
useTabsContext
|
|
3
|
-
} from "./chunk-RIFQSCHT.js";
|
|
4
|
-
|
|
5
|
-
// src/components/TabList.tsx
|
|
6
|
-
import { cx } from "@cerberus/styled-system/css";
|
|
7
|
-
import { hstack } from "@cerberus/styled-system/patterns";
|
|
8
|
-
import { jsx } from "react/jsx-runtime";
|
|
9
|
-
function TabList(props) {
|
|
10
|
-
const { description, ...nativeProps } = props;
|
|
11
|
-
const { id, styles } = useTabsContext();
|
|
12
|
-
return /* @__PURE__ */ jsx(
|
|
13
|
-
"div",
|
|
14
|
-
{
|
|
15
|
-
...nativeProps,
|
|
16
|
-
"aria-label": description,
|
|
17
|
-
className: cx(
|
|
18
|
-
nativeProps.className,
|
|
19
|
-
hstack({
|
|
20
|
-
gap: "0"
|
|
21
|
-
}),
|
|
22
|
-
styles.tabList
|
|
23
|
-
),
|
|
24
|
-
id: id ?? nativeProps.id,
|
|
25
|
-
role: "tablist"
|
|
26
|
-
}
|
|
27
|
-
);
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export {
|
|
31
|
-
TabList
|
|
32
|
-
};
|
|
33
|
-
//# sourceMappingURL=chunk-UKPF7JOB.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/TabList.tsx"],"sourcesContent":["'use client'\n\nimport { cx } from '@cerberus/styled-system/css'\nimport { hstack } from '@cerberus/styled-system/patterns'\nimport type { HTMLAttributes, PropsWithChildren } from 'react'\nimport { useTabsContext } from '../context/tabs'\n\n/**\n * This module provides a TabList component.\n * @module\n */\n\nexport interface TabListProps extends HTMLAttributes<HTMLDivElement> {\n /**\n * A description of what the tab list contains. Required for accessibility.\n */\n description: string\n}\n\n/**\n * The TabList component provides a container for tab elements.\n * @see https://cerberus.digitalu.design/react/tabs\n * @memberof module:Tabs\n * @example\n * ```tsx\n * <TabList description=\"Button details\">\n * <Tab id=\"overview\">Overview</Tab>\n * <Tab id=\"guidelines\">Guidelines</Tab>\n * </TabList>\n * ```\n */\nexport function TabList(props: PropsWithChildren<TabListProps>) {\n const { description, ...nativeProps } = props\n const { id, styles } = useTabsContext()\n\n return (\n <div\n {...nativeProps}\n aria-label={description}\n className={cx(\n nativeProps.className,\n hstack({\n gap: '0',\n }),\n styles.tabList,\n )}\n id={id ?? nativeProps.id}\n role=\"tablist\"\n />\n )\n}\n"],"mappings":";;;;;AAEA,SAAS,UAAU;AACnB,SAAS,cAAc;AAiCnB;AALG,SAAS,QAAQ,OAAwC;AAC9D,QAAM,EAAE,aAAa,GAAG,YAAY,IAAI;AACxC,QAAM,EAAE,IAAI,OAAO,IAAI,eAAe;AAEtC,SACE;AAAA,IAAC;AAAA;AAAA,MACE,GAAG;AAAA,MACJ,cAAY;AAAA,MACZ,WAAW;AAAA,QACT,YAAY;AAAA,QACZ,OAAO;AAAA,UACL,KAAK;AAAA,QACP,CAAC;AAAA,QACD,OAAO;AAAA,MACT;AAAA,MACA,IAAI,MAAM,YAAY;AAAA,MACtB,MAAK;AAAA;AAAA,EACP;AAEJ;","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
'use client'
|
|
2
|
-
|
|
3
|
-
import { useEffect, useState } from 'react'
|
|
4
|
-
import { useTabsContext } from '../context/tabs'
|
|
5
|
-
|
|
6
|
-
function getNextIndex(index: number, length: number) {
|
|
7
|
-
return index === length - 1 ? 0 : index + 1
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
function getPrevIndex(index: number, length: number) {
|
|
11
|
-
return index === 0 ? length - 1 : index - 1
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export function useTabsKeyboardNavigation() {
|
|
15
|
-
const { tabs } = useTabsContext()
|
|
16
|
-
const [activeTab, setActiveTab] = useState(-1)
|
|
17
|
-
|
|
18
|
-
useEffect(() => {
|
|
19
|
-
const handleKeyDown = (event: KeyboardEvent) => {
|
|
20
|
-
const index =
|
|
21
|
-
activeTab === -1
|
|
22
|
-
? tabs.current.findIndex((tab) => tab.ariaSelected === 'true')
|
|
23
|
-
: activeTab
|
|
24
|
-
const nextIndex = getNextIndex(index, tabs.current.length)
|
|
25
|
-
const prevIndex = getPrevIndex(index, tabs.current.length)
|
|
26
|
-
|
|
27
|
-
// If the active tab is not found, do nothing
|
|
28
|
-
if (index === -1) return
|
|
29
|
-
|
|
30
|
-
switch (event.key) {
|
|
31
|
-
case 'ArrowLeft':
|
|
32
|
-
event.preventDefault()
|
|
33
|
-
setActiveTab(prevIndex)
|
|
34
|
-
tabs.current[prevIndex].focus()
|
|
35
|
-
break
|
|
36
|
-
case 'ArrowRight':
|
|
37
|
-
event.preventDefault()
|
|
38
|
-
setActiveTab(nextIndex)
|
|
39
|
-
tabs.current[nextIndex].focus()
|
|
40
|
-
break
|
|
41
|
-
case 'Home':
|
|
42
|
-
event.preventDefault()
|
|
43
|
-
setActiveTab(0)
|
|
44
|
-
tabs.current[0].focus()
|
|
45
|
-
break
|
|
46
|
-
case 'End':
|
|
47
|
-
event.preventDefault()
|
|
48
|
-
setActiveTab(tabs.current.length - 1)
|
|
49
|
-
tabs.current[tabs.current.length - 1].focus()
|
|
50
|
-
break
|
|
51
|
-
default:
|
|
52
|
-
break
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
document.addEventListener('keydown', handleKeyDown)
|
|
57
|
-
|
|
58
|
-
return () => {
|
|
59
|
-
document.removeEventListener('keydown', handleKeyDown)
|
|
60
|
-
}
|
|
61
|
-
}, [activeTab, tabs.current])
|
|
62
|
-
|
|
63
|
-
return {
|
|
64
|
-
ref: (tab: HTMLButtonElement) => {
|
|
65
|
-
if (tab && !tabs.current.includes(tab)) {
|
|
66
|
-
tabs.current.push(tab)
|
|
67
|
-
}
|
|
68
|
-
},
|
|
69
|
-
}
|
|
70
|
-
}
|
package/src/components/Tab.tsx
DELETED
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
'use client'
|
|
2
|
-
|
|
3
|
-
import {
|
|
4
|
-
useMemo,
|
|
5
|
-
useTransition,
|
|
6
|
-
type ButtonHTMLAttributes,
|
|
7
|
-
type MouseEvent,
|
|
8
|
-
} from 'react'
|
|
9
|
-
import { useTabsContext } from '../context/tabs'
|
|
10
|
-
import { cx } from '@cerberus/styled-system/css'
|
|
11
|
-
import { useTabsKeyboardNavigation } from '../aria-helpers/tabs.aria'
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* This module provides a Tab component.
|
|
15
|
-
* @module
|
|
16
|
-
*/
|
|
17
|
-
|
|
18
|
-
export interface TabProps extends ButtonHTMLAttributes<HTMLButtonElement> {
|
|
19
|
-
/**
|
|
20
|
-
* The id of the tab that will be tracked as the active tab and used for aria
|
|
21
|
-
* attributes.
|
|
22
|
-
*/
|
|
23
|
-
value: string
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* The Tab component provides a tab element to be used in a TabList.
|
|
28
|
-
* @definition [ARIA Target Size](https://www.w3.org/WAI/WCAG21/Understanding/target-size.html#:~:text=Understanding%20SC%202.5.,%3ATarget%20Size%20(Level%20AAA)&text=The%20size%20of%20the%20target,Equivalent)
|
|
29
|
-
* @see https://cerberus.digitalu.design/react/tabs
|
|
30
|
-
* @memberof module:Tabs
|
|
31
|
-
* @example
|
|
32
|
-
* ```tsx
|
|
33
|
-
* <Tabs>
|
|
34
|
-
* <TabList description="Profile settings">
|
|
35
|
-
* <Tab value="overview">Overview</Tab>
|
|
36
|
-
* </TabList>
|
|
37
|
-
* <TabPanel tab="overview">...</TabPanel>
|
|
38
|
-
* </Tabs>
|
|
39
|
-
* ```
|
|
40
|
-
*/
|
|
41
|
-
export function Tab(props: TabProps) {
|
|
42
|
-
const { value, ...nativeProps } = props
|
|
43
|
-
const { active, onTabUpdate, styles } = useTabsContext()
|
|
44
|
-
const [isPending, startTransition] = useTransition()
|
|
45
|
-
const { ref } = useTabsKeyboardNavigation()
|
|
46
|
-
const isActive = useMemo(() => active === value, [active, value])
|
|
47
|
-
|
|
48
|
-
function handleClick(e: MouseEvent<HTMLButtonElement>) {
|
|
49
|
-
props.onClick?.(e)
|
|
50
|
-
startTransition(() => onTabUpdate(e.currentTarget.value))
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
return (
|
|
54
|
-
<button
|
|
55
|
-
{...nativeProps}
|
|
56
|
-
{...(!isActive && { tabIndex: -1 })}
|
|
57
|
-
aria-controls={`panel:${value}`}
|
|
58
|
-
aria-busy={isPending}
|
|
59
|
-
aria-selected={isActive}
|
|
60
|
-
id={`tab:${value}`}
|
|
61
|
-
className={cx(nativeProps.className, styles.tab)}
|
|
62
|
-
onClick={handleClick}
|
|
63
|
-
role="tab"
|
|
64
|
-
ref={ref}
|
|
65
|
-
value={value}
|
|
66
|
-
/>
|
|
67
|
-
)
|
|
68
|
-
}
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
'use client'
|
|
2
|
-
|
|
3
|
-
import { cx } from '@cerberus/styled-system/css'
|
|
4
|
-
import { hstack } from '@cerberus/styled-system/patterns'
|
|
5
|
-
import type { HTMLAttributes, PropsWithChildren } from 'react'
|
|
6
|
-
import { useTabsContext } from '../context/tabs'
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* This module provides a TabList component.
|
|
10
|
-
* @module
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
export interface TabListProps extends HTMLAttributes<HTMLDivElement> {
|
|
14
|
-
/**
|
|
15
|
-
* A description of what the tab list contains. Required for accessibility.
|
|
16
|
-
*/
|
|
17
|
-
description: string
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* The TabList component provides a container for tab elements.
|
|
22
|
-
* @see https://cerberus.digitalu.design/react/tabs
|
|
23
|
-
* @memberof module:Tabs
|
|
24
|
-
* @example
|
|
25
|
-
* ```tsx
|
|
26
|
-
* <TabList description="Button details">
|
|
27
|
-
* <Tab id="overview">Overview</Tab>
|
|
28
|
-
* <Tab id="guidelines">Guidelines</Tab>
|
|
29
|
-
* </TabList>
|
|
30
|
-
* ```
|
|
31
|
-
*/
|
|
32
|
-
export function TabList(props: PropsWithChildren<TabListProps>) {
|
|
33
|
-
const { description, ...nativeProps } = props
|
|
34
|
-
const { id, styles } = useTabsContext()
|
|
35
|
-
|
|
36
|
-
return (
|
|
37
|
-
<div
|
|
38
|
-
{...nativeProps}
|
|
39
|
-
aria-label={description}
|
|
40
|
-
className={cx(
|
|
41
|
-
nativeProps.className,
|
|
42
|
-
hstack({
|
|
43
|
-
gap: '0',
|
|
44
|
-
}),
|
|
45
|
-
styles.tabList,
|
|
46
|
-
)}
|
|
47
|
-
id={id ?? nativeProps.id}
|
|
48
|
-
role="tablist"
|
|
49
|
-
/>
|
|
50
|
-
)
|
|
51
|
-
}
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
'use client'
|
|
2
|
-
|
|
3
|
-
import { cx } from '@cerberus/styled-system/css'
|
|
4
|
-
import { useMemo, type HTMLAttributes } from 'react'
|
|
5
|
-
import { useTabsContext } from '../context/tabs'
|
|
6
|
-
import { Show } from './Show'
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* This module provides a TabPanel component.
|
|
10
|
-
* @module
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
export interface TabPanelProps extends HTMLAttributes<HTMLDivElement> {
|
|
14
|
-
/**
|
|
15
|
-
* The unique value of the Tab that is associated with the TabPanel.
|
|
16
|
-
*/
|
|
17
|
-
tab: string
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* The TabPanel component provides a panel element to be used in a Tabs
|
|
22
|
-
* provider.
|
|
23
|
-
* @see https://cerberus.digitalu.design/react/tabs
|
|
24
|
-
* @memberof module:Tabs
|
|
25
|
-
* @example
|
|
26
|
-
* ```tsx
|
|
27
|
-
* <TabPanel tab="overview">
|
|
28
|
-
* Overview content
|
|
29
|
-
* </TabPanel>
|
|
30
|
-
* ```
|
|
31
|
-
*/
|
|
32
|
-
export function TabPanel(props: TabPanelProps) {
|
|
33
|
-
const { tab, ...nativeProps } = props
|
|
34
|
-
const { active, styles } = useTabsContext()
|
|
35
|
-
const isActive = useMemo(() => active === tab, [active, tab])
|
|
36
|
-
|
|
37
|
-
return (
|
|
38
|
-
<Show when={isActive}>
|
|
39
|
-
<div
|
|
40
|
-
{...nativeProps}
|
|
41
|
-
{...(isActive && { tabIndex: 0 })}
|
|
42
|
-
aria-labelledby={`tab:${tab}`}
|
|
43
|
-
className={cx(nativeProps.className, styles.tabPanel)}
|
|
44
|
-
id={`panel:${tab}`}
|
|
45
|
-
role="tabpanel"
|
|
46
|
-
/>
|
|
47
|
-
</Show>
|
|
48
|
-
)
|
|
49
|
-
}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|