@fanvue/ui 2.7.0 → 2.8.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/dist/cjs/components/Alert/Alert.cjs +1 -1
- package/dist/cjs/components/Alert/Alert.cjs.map +1 -1
- package/dist/cjs/components/Tabs/TabsList.cjs +16 -2
- package/dist/cjs/components/Tabs/TabsList.cjs.map +1 -1
- package/dist/components/Alert/Alert.mjs +1 -1
- package/dist/components/Alert/Alert.mjs.map +1 -1
- package/dist/components/Tabs/TabsList.mjs +16 -2
- package/dist/components/Tabs/TabsList.mjs.map +1 -1
- package/dist/index.d.ts +17 -0
- package/package.json +1 -1
|
@@ -73,7 +73,7 @@ const Alert = React__namespace.forwardRef(
|
|
|
73
73
|
),
|
|
74
74
|
...props,
|
|
75
75
|
children: [
|
|
76
|
-
resolvedIcon && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex shrink-0 items-start", "aria-hidden": "true", children: resolvedIcon }),
|
|
76
|
+
resolvedIcon && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex shrink-0 items-start h-full", "aria-hidden": "true", children: resolvedIcon }),
|
|
77
77
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex min-w-0 flex-col gap-2", children: [
|
|
78
78
|
title && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "typography-semibold-body-md text-content-primary", children: title }),
|
|
79
79
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "typography-regular-body-md text-content-primary", children })
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Alert.cjs","sources":["../../../../src/components/Alert/Alert.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\nimport { Button } from \"../Button/Button\";\nimport { CheckCircleIcon } from \"../Icons/CheckCircleIcon\";\nimport { CrossIcon } from \"../Icons/CrossIcon\";\nimport { ErrorCircleIcon } from \"../Icons/ErrorCircleIcon\";\nimport { InfoCircleIcon } from \"../Icons/InfoCircleIcon\";\nimport { WarningTriangleIcon } from \"../Icons/WarningTriangleIcon\";\n\n/** Visual style variant of the alert. */\nexport type AlertVariant = \"info\" | \"success\" | \"warning\" | \"error\";\n\nconst DEFAULT_ICONS: Record<AlertVariant, React.ReactNode> = {\n info: <InfoCircleIcon />,\n success: <CheckCircleIcon />,\n warning: <WarningTriangleIcon />,\n error: <ErrorCircleIcon />,\n};\n\nexport interface AlertProps extends React.HTMLAttributes<HTMLDivElement> {\n /** Visual style variant of the alert. @default \"info\" */\n variant?: AlertVariant;\n /** Optional title text displayed in bold above the description. */\n title?: string;\n /** Custom icon override. Pass `null` to hide the icon entirely. Each variant shows a default icon when left `undefined`. */\n icon?: React.ReactNode | null;\n /** Whether to show the close button. @default false */\n closable?: boolean;\n /** Callback fired when the close button is clicked. */\n onClose?: () => void;\n /** Accessible label for the close button. @default \"Close alert\" */\n closeLabel?: string;\n}\n\nconst CLOSE_BUTTON_CLASSES: Record<AlertVariant, string> = {\n info: \"hover:bg-info-content/10 active:bg-info-content/20 text-info-content motion-safe:transition-colors motion-safe:duration-150\",\n success:\n \"hover:bg-success-content/10 active:bg-success-content/20 text-success-content motion-safe:transition-colors motion-safe:duration-150\",\n warning:\n \"hover:bg-warning-content/10 active:bg-warning-content/20 text-warning-content motion-safe:transition-colors motion-safe:duration-150\",\n error:\n \"hover:bg-error-content/10 active:bg-error-content/20 text-error-content motion-safe:transition-colors motion-safe:duration-150\",\n};\n\n/**\n * Displays a contextual feedback message to the user.\n *\n * Supports `info`, `success`, `warning`, and `error` variants with a default\n * icon per variant, optional title, description, and dismiss button.\n *\n * Each variant renders a default icon automatically. Pass a custom `icon` to\n * override, or `icon={null}` to hide the icon entirely.\n *\n * @example\n * ```tsx\n * <Alert variant=\"success\" title=\"Saved\" closable onClose={handleClose}>\n * Your changes have been saved.\n * </Alert>\n * ```\n */\nexport const Alert = React.forwardRef<HTMLDivElement, AlertProps>(\n (\n {\n className,\n variant = \"info\",\n title,\n icon,\n closable = false,\n onClose,\n closeLabel = \"Close alert\",\n children,\n ...props\n },\n ref,\n ) => {\n const resolvedIcon = icon === null ? null : (icon ?? DEFAULT_ICONS[variant]);\n\n return (\n <div\n ref={ref}\n role=\"alert\"\n data-testid=\"alert\"\n className={cn(\n \"grid gap-x-3 rounded-xs p-4 text-sm leading-[18px]\",\n resolvedIcon && closable && \"grid-cols-[auto_1fr_auto]\",\n resolvedIcon && !closable && \"grid-cols-[auto_1fr]\",\n !resolvedIcon && closable && \"grid-cols-[1fr_auto]\",\n !resolvedIcon && !closable && \"grid-cols-[1fr]\",\n title && children ? \"items-start\" : \"items-center\",\n variant === \"info\" && \"bg-info-surface text-info-content\",\n variant === \"success\" && \"bg-success-surface text-success-content\",\n variant === \"warning\" && \"bg-warning-surface text-warning-content\",\n variant === \"error\" && \"bg-error-surface text-error-content\",\n className,\n )}\n {...props}\n >\n {resolvedIcon && (\n <span className=\"flex shrink-0 items-start\" aria-hidden=\"true\">\n {resolvedIcon}\n </span>\n )}\n\n <div className=\"flex min-w-0 flex-col gap-2\">\n {title && <div className=\"typography-semibold-body-md text-content-primary\">{title}</div>}\n <div className=\"typography-regular-body-md text-content-primary\">{children}</div>\n </div>\n\n {closable && (\n <Button\n variant=\"tertiary\"\n size=\"24\"\n onClick={onClose}\n className={cn(\"self-start px-0\", CLOSE_BUTTON_CLASSES[variant])}\n aria-label={closeLabel}\n >\n <CrossIcon />\n </Button>\n )}\n </div>\n );\n },\n);\n\nAlert.displayName = \"Alert\";\n"],"names":["InfoCircleIcon","CheckCircleIcon","WarningTriangleIcon","ErrorCircleIcon","React","jsxs","cn","jsx","Button","CrossIcon"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAYA,MAAM,gBAAuD;AAAA,EAC3D,qCAAOA,eAAAA,gBAAA,EAAe;AAAA,EACtB,wCAAUC,gBAAAA,iBAAA,EAAgB;AAAA,EAC1B,wCAAUC,oBAAAA,qBAAA,EAAoB;AAAA,EAC9B,sCAAQC,gBAAAA,iBAAA,CAAA,CAAgB;AAC1B;AAiBA,MAAM,uBAAqD;AAAA,EACzD,MAAM;AAAA,EACN,SACE;AAAA,EACF,SACE;AAAA,EACF,OACE;AACJ;AAkBO,MAAM,QAAQC,iBAAM;AAAA,EACzB,CACE;AAAA,IACE;AAAA,IACA,UAAU;AAAA,IACV;AAAA,IACA;AAAA,IACA,WAAW;AAAA,IACX;AAAA,IACA,aAAa;AAAA,IACb;AAAA,IACA,GAAG;AAAA,EAAA,GAEL,QACG;AACH,UAAM,eAAe,SAAS,OAAO,OAAQ,QAAQ,cAAc,OAAO;AAE1E,WACEC,2BAAAA;AAAAA,MAAC;AAAA,MAAA;AAAA,QACC;AAAA,QACA,MAAK;AAAA,QACL,eAAY;AAAA,QACZ,WAAWC,GAAAA;AAAAA,UACT;AAAA,UACA,gBAAgB,YAAY;AAAA,UAC5B,gBAAgB,CAAC,YAAY;AAAA,UAC7B,CAAC,gBAAgB,YAAY;AAAA,UAC7B,CAAC,gBAAgB,CAAC,YAAY;AAAA,UAC9B,SAAS,WAAW,gBAAgB;AAAA,UACpC,YAAY,UAAU;AAAA,UACtB,YAAY,aAAa;AAAA,UACzB,YAAY,aAAa;AAAA,UACzB,YAAY,WAAW;AAAA,UACvB;AAAA,QAAA;AAAA,QAED,GAAG;AAAA,QAEH,UAAA;AAAA,UAAA,+CACE,QAAA,EAAK,WAAU,
|
|
1
|
+
{"version":3,"file":"Alert.cjs","sources":["../../../../src/components/Alert/Alert.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\nimport { Button } from \"../Button/Button\";\nimport { CheckCircleIcon } from \"../Icons/CheckCircleIcon\";\nimport { CrossIcon } from \"../Icons/CrossIcon\";\nimport { ErrorCircleIcon } from \"../Icons/ErrorCircleIcon\";\nimport { InfoCircleIcon } from \"../Icons/InfoCircleIcon\";\nimport { WarningTriangleIcon } from \"../Icons/WarningTriangleIcon\";\n\n/** Visual style variant of the alert. */\nexport type AlertVariant = \"info\" | \"success\" | \"warning\" | \"error\";\n\nconst DEFAULT_ICONS: Record<AlertVariant, React.ReactNode> = {\n info: <InfoCircleIcon />,\n success: <CheckCircleIcon />,\n warning: <WarningTriangleIcon />,\n error: <ErrorCircleIcon />,\n};\n\nexport interface AlertProps extends React.HTMLAttributes<HTMLDivElement> {\n /** Visual style variant of the alert. @default \"info\" */\n variant?: AlertVariant;\n /** Optional title text displayed in bold above the description. */\n title?: string;\n /** Custom icon override. Pass `null` to hide the icon entirely. Each variant shows a default icon when left `undefined`. */\n icon?: React.ReactNode | null;\n /** Whether to show the close button. @default false */\n closable?: boolean;\n /** Callback fired when the close button is clicked. */\n onClose?: () => void;\n /** Accessible label for the close button. @default \"Close alert\" */\n closeLabel?: string;\n}\n\nconst CLOSE_BUTTON_CLASSES: Record<AlertVariant, string> = {\n info: \"hover:bg-info-content/10 active:bg-info-content/20 text-info-content motion-safe:transition-colors motion-safe:duration-150\",\n success:\n \"hover:bg-success-content/10 active:bg-success-content/20 text-success-content motion-safe:transition-colors motion-safe:duration-150\",\n warning:\n \"hover:bg-warning-content/10 active:bg-warning-content/20 text-warning-content motion-safe:transition-colors motion-safe:duration-150\",\n error:\n \"hover:bg-error-content/10 active:bg-error-content/20 text-error-content motion-safe:transition-colors motion-safe:duration-150\",\n};\n\n/**\n * Displays a contextual feedback message to the user.\n *\n * Supports `info`, `success`, `warning`, and `error` variants with a default\n * icon per variant, optional title, description, and dismiss button.\n *\n * Each variant renders a default icon automatically. Pass a custom `icon` to\n * override, or `icon={null}` to hide the icon entirely.\n *\n * @example\n * ```tsx\n * <Alert variant=\"success\" title=\"Saved\" closable onClose={handleClose}>\n * Your changes have been saved.\n * </Alert>\n * ```\n */\nexport const Alert = React.forwardRef<HTMLDivElement, AlertProps>(\n (\n {\n className,\n variant = \"info\",\n title,\n icon,\n closable = false,\n onClose,\n closeLabel = \"Close alert\",\n children,\n ...props\n },\n ref,\n ) => {\n const resolvedIcon = icon === null ? null : (icon ?? DEFAULT_ICONS[variant]);\n\n return (\n <div\n ref={ref}\n role=\"alert\"\n data-testid=\"alert\"\n className={cn(\n \"grid gap-x-3 rounded-xs p-4 text-sm leading-[18px]\",\n resolvedIcon && closable && \"grid-cols-[auto_1fr_auto]\",\n resolvedIcon && !closable && \"grid-cols-[auto_1fr]\",\n !resolvedIcon && closable && \"grid-cols-[1fr_auto]\",\n !resolvedIcon && !closable && \"grid-cols-[1fr]\",\n title && children ? \"items-start\" : \"items-center\",\n variant === \"info\" && \"bg-info-surface text-info-content\",\n variant === \"success\" && \"bg-success-surface text-success-content\",\n variant === \"warning\" && \"bg-warning-surface text-warning-content\",\n variant === \"error\" && \"bg-error-surface text-error-content\",\n className,\n )}\n {...props}\n >\n {resolvedIcon && (\n <span className=\"flex shrink-0 items-start h-full\" aria-hidden=\"true\">\n {resolvedIcon}\n </span>\n )}\n\n <div className=\"flex min-w-0 flex-col gap-2\">\n {title && <div className=\"typography-semibold-body-md text-content-primary\">{title}</div>}\n <div className=\"typography-regular-body-md text-content-primary\">{children}</div>\n </div>\n\n {closable && (\n <Button\n variant=\"tertiary\"\n size=\"24\"\n onClick={onClose}\n className={cn(\"self-start px-0\", CLOSE_BUTTON_CLASSES[variant])}\n aria-label={closeLabel}\n >\n <CrossIcon />\n </Button>\n )}\n </div>\n );\n },\n);\n\nAlert.displayName = \"Alert\";\n"],"names":["InfoCircleIcon","CheckCircleIcon","WarningTriangleIcon","ErrorCircleIcon","React","jsxs","cn","jsx","Button","CrossIcon"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAYA,MAAM,gBAAuD;AAAA,EAC3D,qCAAOA,eAAAA,gBAAA,EAAe;AAAA,EACtB,wCAAUC,gBAAAA,iBAAA,EAAgB;AAAA,EAC1B,wCAAUC,oBAAAA,qBAAA,EAAoB;AAAA,EAC9B,sCAAQC,gBAAAA,iBAAA,CAAA,CAAgB;AAC1B;AAiBA,MAAM,uBAAqD;AAAA,EACzD,MAAM;AAAA,EACN,SACE;AAAA,EACF,SACE;AAAA,EACF,OACE;AACJ;AAkBO,MAAM,QAAQC,iBAAM;AAAA,EACzB,CACE;AAAA,IACE;AAAA,IACA,UAAU;AAAA,IACV;AAAA,IACA;AAAA,IACA,WAAW;AAAA,IACX;AAAA,IACA,aAAa;AAAA,IACb;AAAA,IACA,GAAG;AAAA,EAAA,GAEL,QACG;AACH,UAAM,eAAe,SAAS,OAAO,OAAQ,QAAQ,cAAc,OAAO;AAE1E,WACEC,2BAAAA;AAAAA,MAAC;AAAA,MAAA;AAAA,QACC;AAAA,QACA,MAAK;AAAA,QACL,eAAY;AAAA,QACZ,WAAWC,GAAAA;AAAAA,UACT;AAAA,UACA,gBAAgB,YAAY;AAAA,UAC5B,gBAAgB,CAAC,YAAY;AAAA,UAC7B,CAAC,gBAAgB,YAAY;AAAA,UAC7B,CAAC,gBAAgB,CAAC,YAAY;AAAA,UAC9B,SAAS,WAAW,gBAAgB;AAAA,UACpC,YAAY,UAAU;AAAA,UACtB,YAAY,aAAa;AAAA,UACzB,YAAY,aAAa;AAAA,UACzB,YAAY,WAAW;AAAA,UACvB;AAAA,QAAA;AAAA,QAED,GAAG;AAAA,QAEH,UAAA;AAAA,UAAA,+CACE,QAAA,EAAK,WAAU,oCAAmC,eAAY,QAC5D,UAAA,cACH;AAAA,UAGFD,2BAAAA,KAAC,OAAA,EAAI,WAAU,+BACZ,UAAA;AAAA,YAAA,SAASE,2BAAAA,IAAC,OAAA,EAAI,WAAU,oDAAoD,UAAA,OAAM;AAAA,YACnFA,2BAAAA,IAAC,OAAA,EAAI,WAAU,mDAAmD,SAAA,CAAS;AAAA,UAAA,GAC7E;AAAA,UAEC,YACCA,2BAAAA;AAAAA,YAACC,OAAAA;AAAAA,YAAA;AAAA,cACC,SAAQ;AAAA,cACR,MAAK;AAAA,cACL,SAAS;AAAA,cACT,WAAWF,GAAAA,GAAG,mBAAmB,qBAAqB,OAAO,CAAC;AAAA,cAC9D,cAAY;AAAA,cAEZ,yCAACG,UAAAA,WAAA,CAAA,CAAU;AAAA,YAAA;AAAA,UAAA;AAAA,QACb;AAAA,MAAA;AAAA,IAAA;AAAA,EAIR;AACF;AAEA,MAAM,cAAc;;"}
|
|
@@ -23,7 +23,21 @@ function _interopNamespaceDefault(e) {
|
|
|
23
23
|
}
|
|
24
24
|
const TabsPrimitive__namespace = /* @__PURE__ */ _interopNamespaceDefault(TabsPrimitive);
|
|
25
25
|
const React__namespace = /* @__PURE__ */ _interopNamespaceDefault(React);
|
|
26
|
-
const
|
|
26
|
+
const alignLeftClasses = {
|
|
27
|
+
always: "[&>[role=tab]]:flex-initial",
|
|
28
|
+
sm: "[&>[role=tab]]:sm:flex-initial",
|
|
29
|
+
md: "[&>[role=tab]]:md:flex-initial",
|
|
30
|
+
lg: "[&>[role=tab]]:lg:flex-initial",
|
|
31
|
+
xl: "[&>[role=tab]]:xl:flex-initial"
|
|
32
|
+
};
|
|
33
|
+
function getLayoutClass(fullWidth, alignLeft) {
|
|
34
|
+
if (!fullWidth) return "inline-flex";
|
|
35
|
+
const base = "flex w-full [&>[role=tab]]:flex-1";
|
|
36
|
+
if (alignLeft === true) return `${base} ${alignLeftClasses.always}`;
|
|
37
|
+
if (typeof alignLeft === "string") return `${base} ${alignLeftClasses[alignLeft]}`;
|
|
38
|
+
return base;
|
|
39
|
+
}
|
|
40
|
+
const TabsList = React__namespace.forwardRef(({ className, children, fullWidth = true, alignLeft, ...props }, ref) => {
|
|
27
41
|
const innerRef = React__namespace.useRef(null);
|
|
28
42
|
const indicatorRef = React__namespace.useRef(null);
|
|
29
43
|
React__namespace.useImperativeHandle(ref, () => innerRef.current);
|
|
@@ -78,7 +92,7 @@ const TabsList = React__namespace.forwardRef(({ className, children, fullWidth =
|
|
|
78
92
|
ref: innerRef,
|
|
79
93
|
className: cn.cn(
|
|
80
94
|
"relative",
|
|
81
|
-
fullWidth
|
|
95
|
+
getLayoutClass(fullWidth, alignLeft),
|
|
82
96
|
"data-[orientation=horizontal]:items-center data-[orientation=horizontal]:shadow-[inset_0_-1px_0_0_var(--color-neutral-alphas-200)]",
|
|
83
97
|
"data-[orientation=vertical]:flex-col data-[orientation=vertical]:shadow-[inset_-1px_0_0_0_var(--color-neutral-alphas-200)]",
|
|
84
98
|
className
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TabsList.cjs","sources":["../../../../src/components/Tabs/TabsList.tsx"],"sourcesContent":["import * as TabsPrimitive from \"@radix-ui/react-tabs\";\nimport * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\n\n/** Props for the {@link TabsList} component. */\nexport type TabsListProps = React.ComponentPropsWithoutRef<typeof TabsPrimitive.List> & {\n /** When `true` (the default), the tab list spans the full width of its container and each tab grows equally. Set to `false` for inline sizing. */\n fullWidth?: boolean;\n};\n\n/** Container for {@link TabsTrigger} elements. Renders a sliding active-tab indicator that animates between tabs. */\nexport const TabsList = React.forwardRef<\n React.ComponentRef<typeof TabsPrimitive.List>,\n TabsListProps\n>(({ className, children, fullWidth = true, ...props }, ref) => {\n const innerRef = React.useRef<HTMLDivElement>(null);\n const indicatorRef = React.useRef<HTMLSpanElement>(null);\n\n React.useImperativeHandle(ref, () => innerRef.current as HTMLDivElement);\n\n const updateIndicator = React.useCallback(() => {\n const list = innerRef.current;\n const indicator = indicatorRef.current;\n if (!list || !indicator) return;\n\n const activeTab = list.querySelector<HTMLElement>('[data-state=\"active\"]');\n if (!activeTab) {\n indicator.style.opacity = \"0\";\n return;\n }\n\n const isVertical = list.dataset.orientation === \"vertical\";\n\n indicator.style.opacity = \"1\";\n\n if (isVertical) {\n indicator.style.inset = `0 0 auto auto`;\n indicator.style.width = \"4px\";\n indicator.style.height = `${activeTab.offsetHeight}px`;\n indicator.style.transform = `translateY(${activeTab.offsetTop}px)`;\n } else {\n indicator.style.inset = `auto auto 0 0`;\n indicator.style.height = \"4px\";\n indicator.style.width = `${activeTab.offsetWidth}px`;\n indicator.style.transform = `translateX(${activeTab.offsetLeft}px)`;\n }\n }, []);\n\n React.useLayoutEffect(() => {\n const list = innerRef.current;\n const indicator = indicatorRef.current;\n if (!list || !indicator) return;\n\n indicator.style.transitionDuration = \"0s\";\n updateIndicator();\n indicator.getBoundingClientRect();\n indicator.style.transitionDuration = \"\";\n\n const mutationObserver = new MutationObserver(updateIndicator);\n mutationObserver.observe(list, {\n attributes: true,\n attributeFilter: [\"data-state\"],\n childList: true,\n subtree: true,\n });\n\n const resizeObserver = new ResizeObserver(updateIndicator);\n resizeObserver.observe(list);\n\n return () => {\n mutationObserver.disconnect();\n resizeObserver.disconnect();\n };\n }, [updateIndicator]);\n\n return (\n <TabsPrimitive.List\n ref={innerRef}\n className={cn(\n \"relative\",\n fullWidth
|
|
1
|
+
{"version":3,"file":"TabsList.cjs","sources":["../../../../src/components/Tabs/TabsList.tsx"],"sourcesContent":["import * as TabsPrimitive from \"@radix-ui/react-tabs\";\nimport * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\n\n/** Breakpoint values for responsive props. */\ntype Breakpoint = \"sm\" | \"md\" | \"lg\" | \"xl\";\n\nconst alignLeftClasses: Record<Breakpoint | \"always\", string> = {\n always: \"[&>[role=tab]]:flex-initial\",\n sm: \"[&>[role=tab]]:sm:flex-initial\",\n md: \"[&>[role=tab]]:md:flex-initial\",\n lg: \"[&>[role=tab]]:lg:flex-initial\",\n xl: \"[&>[role=tab]]:xl:flex-initial\",\n};\n\nfunction getLayoutClass(fullWidth: boolean, alignLeft?: boolean | Breakpoint): string {\n if (!fullWidth) return \"inline-flex\";\n\n const base = \"flex w-full [&>[role=tab]]:flex-1\";\n if (alignLeft === true) return `${base} ${alignLeftClasses.always}`;\n if (typeof alignLeft === \"string\") return `${base} ${alignLeftClasses[alignLeft]}`;\n return base;\n}\n\n/** Props for the {@link TabsList} component. */\nexport type TabsListProps = React.ComponentPropsWithoutRef<typeof TabsPrimitive.List> & {\n /** When `true` (the default), the tab list spans the full width of its container and each tab grows equally. Set to `false` for inline sizing. */\n fullWidth?: boolean;\n /**\n * Controls tab alignment within a full-width container.\n * - `false` (default): tabs spread evenly\n * - `true`: tabs left-aligned, sized to content\n * - `\"md\"` (breakpoint): spread on mobile, left-aligned at breakpoint and up\n */\n alignLeft?: boolean | Breakpoint;\n};\n\n/** Container for {@link TabsTrigger} elements. Renders a sliding active-tab indicator that animates between tabs. */\nexport const TabsList = React.forwardRef<\n React.ComponentRef<typeof TabsPrimitive.List>,\n TabsListProps\n>(({ className, children, fullWidth = true, alignLeft, ...props }, ref) => {\n const innerRef = React.useRef<HTMLDivElement>(null);\n const indicatorRef = React.useRef<HTMLSpanElement>(null);\n\n React.useImperativeHandle(ref, () => innerRef.current as HTMLDivElement);\n\n const updateIndicator = React.useCallback(() => {\n const list = innerRef.current;\n const indicator = indicatorRef.current;\n if (!list || !indicator) return;\n\n const activeTab = list.querySelector<HTMLElement>('[data-state=\"active\"]');\n if (!activeTab) {\n indicator.style.opacity = \"0\";\n return;\n }\n\n const isVertical = list.dataset.orientation === \"vertical\";\n\n indicator.style.opacity = \"1\";\n\n if (isVertical) {\n indicator.style.inset = `0 0 auto auto`;\n indicator.style.width = \"4px\";\n indicator.style.height = `${activeTab.offsetHeight}px`;\n indicator.style.transform = `translateY(${activeTab.offsetTop}px)`;\n } else {\n indicator.style.inset = `auto auto 0 0`;\n indicator.style.height = \"4px\";\n indicator.style.width = `${activeTab.offsetWidth}px`;\n indicator.style.transform = `translateX(${activeTab.offsetLeft}px)`;\n }\n }, []);\n\n React.useLayoutEffect(() => {\n const list = innerRef.current;\n const indicator = indicatorRef.current;\n if (!list || !indicator) return;\n\n indicator.style.transitionDuration = \"0s\";\n updateIndicator();\n indicator.getBoundingClientRect();\n indicator.style.transitionDuration = \"\";\n\n const mutationObserver = new MutationObserver(updateIndicator);\n mutationObserver.observe(list, {\n attributes: true,\n attributeFilter: [\"data-state\"],\n childList: true,\n subtree: true,\n });\n\n const resizeObserver = new ResizeObserver(updateIndicator);\n resizeObserver.observe(list);\n\n return () => {\n mutationObserver.disconnect();\n resizeObserver.disconnect();\n };\n }, [updateIndicator]);\n\n return (\n <TabsPrimitive.List\n ref={innerRef}\n className={cn(\n \"relative\",\n getLayoutClass(fullWidth, alignLeft),\n \"data-[orientation=horizontal]:items-center data-[orientation=horizontal]:shadow-[inset_0_-1px_0_0_var(--color-neutral-alphas-200)]\",\n \"data-[orientation=vertical]:flex-col data-[orientation=vertical]:shadow-[inset_-1px_0_0_0_var(--color-neutral-alphas-200)]\",\n className,\n )}\n {...props}\n >\n {children}\n <span\n ref={indicatorRef}\n aria-hidden\n className=\"pointer-events-none absolute rounded-full bg-brand-primary-default motion-safe:transition-[transform,width,height] motion-safe:duration-200 motion-safe:ease-in-out\"\n style={{ opacity: 0 }}\n />\n </TabsPrimitive.List>\n );\n});\n\nTabsList.displayName = \"TabsList\";\n"],"names":["React","jsxs","TabsPrimitive","cn","jsx"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AAOA,MAAM,mBAA0D;AAAA,EAC9D,QAAQ;AAAA,EACR,IAAI;AAAA,EACJ,IAAI;AAAA,EACJ,IAAI;AAAA,EACJ,IAAI;AACN;AAEA,SAAS,eAAe,WAAoB,WAA0C;AACpF,MAAI,CAAC,UAAW,QAAO;AAEvB,QAAM,OAAO;AACb,MAAI,cAAc,KAAM,QAAO,GAAG,IAAI,IAAI,iBAAiB,MAAM;AACjE,MAAI,OAAO,cAAc,SAAU,QAAO,GAAG,IAAI,IAAI,iBAAiB,SAAS,CAAC;AAChF,SAAO;AACT;AAgBO,MAAM,WAAWA,iBAAM,WAG5B,CAAC,EAAE,WAAW,UAAU,YAAY,MAAM,WAAW,GAAG,MAAA,GAAS,QAAQ;AACzE,QAAM,WAAWA,iBAAM,OAAuB,IAAI;AAClD,QAAM,eAAeA,iBAAM,OAAwB,IAAI;AAEvDA,mBAAM,oBAAoB,KAAK,MAAM,SAAS,OAAyB;AAEvE,QAAM,kBAAkBA,iBAAM,YAAY,MAAM;AAC9C,UAAM,OAAO,SAAS;AACtB,UAAM,YAAY,aAAa;AAC/B,QAAI,CAAC,QAAQ,CAAC,UAAW;AAEzB,UAAM,YAAY,KAAK,cAA2B,uBAAuB;AACzE,QAAI,CAAC,WAAW;AACd,gBAAU,MAAM,UAAU;AAC1B;AAAA,IACF;AAEA,UAAM,aAAa,KAAK,QAAQ,gBAAgB;AAEhD,cAAU,MAAM,UAAU;AAE1B,QAAI,YAAY;AACd,gBAAU,MAAM,QAAQ;AACxB,gBAAU,MAAM,QAAQ;AACxB,gBAAU,MAAM,SAAS,GAAG,UAAU,YAAY;AAClD,gBAAU,MAAM,YAAY,cAAc,UAAU,SAAS;AAAA,IAC/D,OAAO;AACL,gBAAU,MAAM,QAAQ;AACxB,gBAAU,MAAM,SAAS;AACzB,gBAAU,MAAM,QAAQ,GAAG,UAAU,WAAW;AAChD,gBAAU,MAAM,YAAY,cAAc,UAAU,UAAU;AAAA,IAChE;AAAA,EACF,GAAG,CAAA,CAAE;AAELA,mBAAM,gBAAgB,MAAM;AAC1B,UAAM,OAAO,SAAS;AACtB,UAAM,YAAY,aAAa;AAC/B,QAAI,CAAC,QAAQ,CAAC,UAAW;AAEzB,cAAU,MAAM,qBAAqB;AACrC,oBAAA;AACA,cAAU,sBAAA;AACV,cAAU,MAAM,qBAAqB;AAErC,UAAM,mBAAmB,IAAI,iBAAiB,eAAe;AAC7D,qBAAiB,QAAQ,MAAM;AAAA,MAC7B,YAAY;AAAA,MACZ,iBAAiB,CAAC,YAAY;AAAA,MAC9B,WAAW;AAAA,MACX,SAAS;AAAA,IAAA,CACV;AAED,UAAM,iBAAiB,IAAI,eAAe,eAAe;AACzD,mBAAe,QAAQ,IAAI;AAE3B,WAAO,MAAM;AACX,uBAAiB,WAAA;AACjB,qBAAe,WAAA;AAAA,IACjB;AAAA,EACF,GAAG,CAAC,eAAe,CAAC;AAEpB,SACEC,2BAAAA;AAAAA,IAACC,yBAAc;AAAA,IAAd;AAAA,MACC,KAAK;AAAA,MACL,WAAWC,GAAAA;AAAAA,QACT;AAAA,QACA,eAAe,WAAW,SAAS;AAAA,QACnC;AAAA,QACA;AAAA,QACA;AAAA,MAAA;AAAA,MAED,GAAG;AAAA,MAEH,UAAA;AAAA,QAAA;AAAA,QACDC,2BAAAA;AAAAA,UAAC;AAAA,UAAA;AAAA,YACC,KAAK;AAAA,YACL,eAAW;AAAA,YACX,WAAU;AAAA,YACV,OAAO,EAAE,SAAS,EAAA;AAAA,UAAE;AAAA,QAAA;AAAA,MACtB;AAAA,IAAA;AAAA,EAAA;AAGN,CAAC;AAED,SAAS,cAAc;;"}
|
|
@@ -54,7 +54,7 @@ const Alert = React.forwardRef(
|
|
|
54
54
|
),
|
|
55
55
|
...props,
|
|
56
56
|
children: [
|
|
57
|
-
resolvedIcon && /* @__PURE__ */ jsx("span", { className: "flex shrink-0 items-start", "aria-hidden": "true", children: resolvedIcon }),
|
|
57
|
+
resolvedIcon && /* @__PURE__ */ jsx("span", { className: "flex shrink-0 items-start h-full", "aria-hidden": "true", children: resolvedIcon }),
|
|
58
58
|
/* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-col gap-2", children: [
|
|
59
59
|
title && /* @__PURE__ */ jsx("div", { className: "typography-semibold-body-md text-content-primary", children: title }),
|
|
60
60
|
/* @__PURE__ */ jsx("div", { className: "typography-regular-body-md text-content-primary", children })
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Alert.mjs","sources":["../../../src/components/Alert/Alert.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\nimport { Button } from \"../Button/Button\";\nimport { CheckCircleIcon } from \"../Icons/CheckCircleIcon\";\nimport { CrossIcon } from \"../Icons/CrossIcon\";\nimport { ErrorCircleIcon } from \"../Icons/ErrorCircleIcon\";\nimport { InfoCircleIcon } from \"../Icons/InfoCircleIcon\";\nimport { WarningTriangleIcon } from \"../Icons/WarningTriangleIcon\";\n\n/** Visual style variant of the alert. */\nexport type AlertVariant = \"info\" | \"success\" | \"warning\" | \"error\";\n\nconst DEFAULT_ICONS: Record<AlertVariant, React.ReactNode> = {\n info: <InfoCircleIcon />,\n success: <CheckCircleIcon />,\n warning: <WarningTriangleIcon />,\n error: <ErrorCircleIcon />,\n};\n\nexport interface AlertProps extends React.HTMLAttributes<HTMLDivElement> {\n /** Visual style variant of the alert. @default \"info\" */\n variant?: AlertVariant;\n /** Optional title text displayed in bold above the description. */\n title?: string;\n /** Custom icon override. Pass `null` to hide the icon entirely. Each variant shows a default icon when left `undefined`. */\n icon?: React.ReactNode | null;\n /** Whether to show the close button. @default false */\n closable?: boolean;\n /** Callback fired when the close button is clicked. */\n onClose?: () => void;\n /** Accessible label for the close button. @default \"Close alert\" */\n closeLabel?: string;\n}\n\nconst CLOSE_BUTTON_CLASSES: Record<AlertVariant, string> = {\n info: \"hover:bg-info-content/10 active:bg-info-content/20 text-info-content motion-safe:transition-colors motion-safe:duration-150\",\n success:\n \"hover:bg-success-content/10 active:bg-success-content/20 text-success-content motion-safe:transition-colors motion-safe:duration-150\",\n warning:\n \"hover:bg-warning-content/10 active:bg-warning-content/20 text-warning-content motion-safe:transition-colors motion-safe:duration-150\",\n error:\n \"hover:bg-error-content/10 active:bg-error-content/20 text-error-content motion-safe:transition-colors motion-safe:duration-150\",\n};\n\n/**\n * Displays a contextual feedback message to the user.\n *\n * Supports `info`, `success`, `warning`, and `error` variants with a default\n * icon per variant, optional title, description, and dismiss button.\n *\n * Each variant renders a default icon automatically. Pass a custom `icon` to\n * override, or `icon={null}` to hide the icon entirely.\n *\n * @example\n * ```tsx\n * <Alert variant=\"success\" title=\"Saved\" closable onClose={handleClose}>\n * Your changes have been saved.\n * </Alert>\n * ```\n */\nexport const Alert = React.forwardRef<HTMLDivElement, AlertProps>(\n (\n {\n className,\n variant = \"info\",\n title,\n icon,\n closable = false,\n onClose,\n closeLabel = \"Close alert\",\n children,\n ...props\n },\n ref,\n ) => {\n const resolvedIcon = icon === null ? null : (icon ?? DEFAULT_ICONS[variant]);\n\n return (\n <div\n ref={ref}\n role=\"alert\"\n data-testid=\"alert\"\n className={cn(\n \"grid gap-x-3 rounded-xs p-4 text-sm leading-[18px]\",\n resolvedIcon && closable && \"grid-cols-[auto_1fr_auto]\",\n resolvedIcon && !closable && \"grid-cols-[auto_1fr]\",\n !resolvedIcon && closable && \"grid-cols-[1fr_auto]\",\n !resolvedIcon && !closable && \"grid-cols-[1fr]\",\n title && children ? \"items-start\" : \"items-center\",\n variant === \"info\" && \"bg-info-surface text-info-content\",\n variant === \"success\" && \"bg-success-surface text-success-content\",\n variant === \"warning\" && \"bg-warning-surface text-warning-content\",\n variant === \"error\" && \"bg-error-surface text-error-content\",\n className,\n )}\n {...props}\n >\n {resolvedIcon && (\n <span className=\"flex shrink-0 items-start\" aria-hidden=\"true\">\n {resolvedIcon}\n </span>\n )}\n\n <div className=\"flex min-w-0 flex-col gap-2\">\n {title && <div className=\"typography-semibold-body-md text-content-primary\">{title}</div>}\n <div className=\"typography-regular-body-md text-content-primary\">{children}</div>\n </div>\n\n {closable && (\n <Button\n variant=\"tertiary\"\n size=\"24\"\n onClick={onClose}\n className={cn(\"self-start px-0\", CLOSE_BUTTON_CLASSES[variant])}\n aria-label={closeLabel}\n >\n <CrossIcon />\n </Button>\n )}\n </div>\n );\n },\n);\n\nAlert.displayName = \"Alert\";\n"],"names":[],"mappings":";;;;;;;;;;AAYA,MAAM,gBAAuD;AAAA,EAC3D,0BAAO,gBAAA,EAAe;AAAA,EACtB,6BAAU,iBAAA,EAAgB;AAAA,EAC1B,6BAAU,qBAAA,EAAoB;AAAA,EAC9B,2BAAQ,iBAAA,CAAA,CAAgB;AAC1B;AAiBA,MAAM,uBAAqD;AAAA,EACzD,MAAM;AAAA,EACN,SACE;AAAA,EACF,SACE;AAAA,EACF,OACE;AACJ;AAkBO,MAAM,QAAQ,MAAM;AAAA,EACzB,CACE;AAAA,IACE;AAAA,IACA,UAAU;AAAA,IACV;AAAA,IACA;AAAA,IACA,WAAW;AAAA,IACX;AAAA,IACA,aAAa;AAAA,IACb;AAAA,IACA,GAAG;AAAA,EAAA,GAEL,QACG;AACH,UAAM,eAAe,SAAS,OAAO,OAAQ,QAAQ,cAAc,OAAO;AAE1E,WACE;AAAA,MAAC;AAAA,MAAA;AAAA,QACC;AAAA,QACA,MAAK;AAAA,QACL,eAAY;AAAA,QACZ,WAAW;AAAA,UACT;AAAA,UACA,gBAAgB,YAAY;AAAA,UAC5B,gBAAgB,CAAC,YAAY;AAAA,UAC7B,CAAC,gBAAgB,YAAY;AAAA,UAC7B,CAAC,gBAAgB,CAAC,YAAY;AAAA,UAC9B,SAAS,WAAW,gBAAgB;AAAA,UACpC,YAAY,UAAU;AAAA,UACtB,YAAY,aAAa;AAAA,UACzB,YAAY,aAAa;AAAA,UACzB,YAAY,WAAW;AAAA,UACvB;AAAA,QAAA;AAAA,QAED,GAAG;AAAA,QAEH,UAAA;AAAA,UAAA,oCACE,QAAA,EAAK,WAAU,
|
|
1
|
+
{"version":3,"file":"Alert.mjs","sources":["../../../src/components/Alert/Alert.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\nimport { Button } from \"../Button/Button\";\nimport { CheckCircleIcon } from \"../Icons/CheckCircleIcon\";\nimport { CrossIcon } from \"../Icons/CrossIcon\";\nimport { ErrorCircleIcon } from \"../Icons/ErrorCircleIcon\";\nimport { InfoCircleIcon } from \"../Icons/InfoCircleIcon\";\nimport { WarningTriangleIcon } from \"../Icons/WarningTriangleIcon\";\n\n/** Visual style variant of the alert. */\nexport type AlertVariant = \"info\" | \"success\" | \"warning\" | \"error\";\n\nconst DEFAULT_ICONS: Record<AlertVariant, React.ReactNode> = {\n info: <InfoCircleIcon />,\n success: <CheckCircleIcon />,\n warning: <WarningTriangleIcon />,\n error: <ErrorCircleIcon />,\n};\n\nexport interface AlertProps extends React.HTMLAttributes<HTMLDivElement> {\n /** Visual style variant of the alert. @default \"info\" */\n variant?: AlertVariant;\n /** Optional title text displayed in bold above the description. */\n title?: string;\n /** Custom icon override. Pass `null` to hide the icon entirely. Each variant shows a default icon when left `undefined`. */\n icon?: React.ReactNode | null;\n /** Whether to show the close button. @default false */\n closable?: boolean;\n /** Callback fired when the close button is clicked. */\n onClose?: () => void;\n /** Accessible label for the close button. @default \"Close alert\" */\n closeLabel?: string;\n}\n\nconst CLOSE_BUTTON_CLASSES: Record<AlertVariant, string> = {\n info: \"hover:bg-info-content/10 active:bg-info-content/20 text-info-content motion-safe:transition-colors motion-safe:duration-150\",\n success:\n \"hover:bg-success-content/10 active:bg-success-content/20 text-success-content motion-safe:transition-colors motion-safe:duration-150\",\n warning:\n \"hover:bg-warning-content/10 active:bg-warning-content/20 text-warning-content motion-safe:transition-colors motion-safe:duration-150\",\n error:\n \"hover:bg-error-content/10 active:bg-error-content/20 text-error-content motion-safe:transition-colors motion-safe:duration-150\",\n};\n\n/**\n * Displays a contextual feedback message to the user.\n *\n * Supports `info`, `success`, `warning`, and `error` variants with a default\n * icon per variant, optional title, description, and dismiss button.\n *\n * Each variant renders a default icon automatically. Pass a custom `icon` to\n * override, or `icon={null}` to hide the icon entirely.\n *\n * @example\n * ```tsx\n * <Alert variant=\"success\" title=\"Saved\" closable onClose={handleClose}>\n * Your changes have been saved.\n * </Alert>\n * ```\n */\nexport const Alert = React.forwardRef<HTMLDivElement, AlertProps>(\n (\n {\n className,\n variant = \"info\",\n title,\n icon,\n closable = false,\n onClose,\n closeLabel = \"Close alert\",\n children,\n ...props\n },\n ref,\n ) => {\n const resolvedIcon = icon === null ? null : (icon ?? DEFAULT_ICONS[variant]);\n\n return (\n <div\n ref={ref}\n role=\"alert\"\n data-testid=\"alert\"\n className={cn(\n \"grid gap-x-3 rounded-xs p-4 text-sm leading-[18px]\",\n resolvedIcon && closable && \"grid-cols-[auto_1fr_auto]\",\n resolvedIcon && !closable && \"grid-cols-[auto_1fr]\",\n !resolvedIcon && closable && \"grid-cols-[1fr_auto]\",\n !resolvedIcon && !closable && \"grid-cols-[1fr]\",\n title && children ? \"items-start\" : \"items-center\",\n variant === \"info\" && \"bg-info-surface text-info-content\",\n variant === \"success\" && \"bg-success-surface text-success-content\",\n variant === \"warning\" && \"bg-warning-surface text-warning-content\",\n variant === \"error\" && \"bg-error-surface text-error-content\",\n className,\n )}\n {...props}\n >\n {resolvedIcon && (\n <span className=\"flex shrink-0 items-start h-full\" aria-hidden=\"true\">\n {resolvedIcon}\n </span>\n )}\n\n <div className=\"flex min-w-0 flex-col gap-2\">\n {title && <div className=\"typography-semibold-body-md text-content-primary\">{title}</div>}\n <div className=\"typography-regular-body-md text-content-primary\">{children}</div>\n </div>\n\n {closable && (\n <Button\n variant=\"tertiary\"\n size=\"24\"\n onClick={onClose}\n className={cn(\"self-start px-0\", CLOSE_BUTTON_CLASSES[variant])}\n aria-label={closeLabel}\n >\n <CrossIcon />\n </Button>\n )}\n </div>\n );\n },\n);\n\nAlert.displayName = \"Alert\";\n"],"names":[],"mappings":";;;;;;;;;;AAYA,MAAM,gBAAuD;AAAA,EAC3D,0BAAO,gBAAA,EAAe;AAAA,EACtB,6BAAU,iBAAA,EAAgB;AAAA,EAC1B,6BAAU,qBAAA,EAAoB;AAAA,EAC9B,2BAAQ,iBAAA,CAAA,CAAgB;AAC1B;AAiBA,MAAM,uBAAqD;AAAA,EACzD,MAAM;AAAA,EACN,SACE;AAAA,EACF,SACE;AAAA,EACF,OACE;AACJ;AAkBO,MAAM,QAAQ,MAAM;AAAA,EACzB,CACE;AAAA,IACE;AAAA,IACA,UAAU;AAAA,IACV;AAAA,IACA;AAAA,IACA,WAAW;AAAA,IACX;AAAA,IACA,aAAa;AAAA,IACb;AAAA,IACA,GAAG;AAAA,EAAA,GAEL,QACG;AACH,UAAM,eAAe,SAAS,OAAO,OAAQ,QAAQ,cAAc,OAAO;AAE1E,WACE;AAAA,MAAC;AAAA,MAAA;AAAA,QACC;AAAA,QACA,MAAK;AAAA,QACL,eAAY;AAAA,QACZ,WAAW;AAAA,UACT;AAAA,UACA,gBAAgB,YAAY;AAAA,UAC5B,gBAAgB,CAAC,YAAY;AAAA,UAC7B,CAAC,gBAAgB,YAAY;AAAA,UAC7B,CAAC,gBAAgB,CAAC,YAAY;AAAA,UAC9B,SAAS,WAAW,gBAAgB;AAAA,UACpC,YAAY,UAAU;AAAA,UACtB,YAAY,aAAa;AAAA,UACzB,YAAY,aAAa;AAAA,UACzB,YAAY,WAAW;AAAA,UACvB;AAAA,QAAA;AAAA,QAED,GAAG;AAAA,QAEH,UAAA;AAAA,UAAA,oCACE,QAAA,EAAK,WAAU,oCAAmC,eAAY,QAC5D,UAAA,cACH;AAAA,UAGF,qBAAC,OAAA,EAAI,WAAU,+BACZ,UAAA;AAAA,YAAA,SAAS,oBAAC,OAAA,EAAI,WAAU,oDAAoD,UAAA,OAAM;AAAA,YACnF,oBAAC,OAAA,EAAI,WAAU,mDAAmD,SAAA,CAAS;AAAA,UAAA,GAC7E;AAAA,UAEC,YACC;AAAA,YAAC;AAAA,YAAA;AAAA,cACC,SAAQ;AAAA,cACR,MAAK;AAAA,cACL,SAAS;AAAA,cACT,WAAW,GAAG,mBAAmB,qBAAqB,OAAO,CAAC;AAAA,cAC9D,cAAY;AAAA,cAEZ,8BAAC,WAAA,CAAA,CAAU;AAAA,YAAA;AAAA,UAAA;AAAA,QACb;AAAA,MAAA;AAAA,IAAA;AAAA,EAIR;AACF;AAEA,MAAM,cAAc;"}
|
|
@@ -3,7 +3,21 @@ import { jsxs, jsx } from "react/jsx-runtime";
|
|
|
3
3
|
import * as TabsPrimitive from "@radix-ui/react-tabs";
|
|
4
4
|
import * as React from "react";
|
|
5
5
|
import { cn } from "../../utils/cn.mjs";
|
|
6
|
-
const
|
|
6
|
+
const alignLeftClasses = {
|
|
7
|
+
always: "[&>[role=tab]]:flex-initial",
|
|
8
|
+
sm: "[&>[role=tab]]:sm:flex-initial",
|
|
9
|
+
md: "[&>[role=tab]]:md:flex-initial",
|
|
10
|
+
lg: "[&>[role=tab]]:lg:flex-initial",
|
|
11
|
+
xl: "[&>[role=tab]]:xl:flex-initial"
|
|
12
|
+
};
|
|
13
|
+
function getLayoutClass(fullWidth, alignLeft) {
|
|
14
|
+
if (!fullWidth) return "inline-flex";
|
|
15
|
+
const base = "flex w-full [&>[role=tab]]:flex-1";
|
|
16
|
+
if (alignLeft === true) return `${base} ${alignLeftClasses.always}`;
|
|
17
|
+
if (typeof alignLeft === "string") return `${base} ${alignLeftClasses[alignLeft]}`;
|
|
18
|
+
return base;
|
|
19
|
+
}
|
|
20
|
+
const TabsList = React.forwardRef(({ className, children, fullWidth = true, alignLeft, ...props }, ref) => {
|
|
7
21
|
const innerRef = React.useRef(null);
|
|
8
22
|
const indicatorRef = React.useRef(null);
|
|
9
23
|
React.useImperativeHandle(ref, () => innerRef.current);
|
|
@@ -58,7 +72,7 @@ const TabsList = React.forwardRef(({ className, children, fullWidth = true, ...p
|
|
|
58
72
|
ref: innerRef,
|
|
59
73
|
className: cn(
|
|
60
74
|
"relative",
|
|
61
|
-
fullWidth
|
|
75
|
+
getLayoutClass(fullWidth, alignLeft),
|
|
62
76
|
"data-[orientation=horizontal]:items-center data-[orientation=horizontal]:shadow-[inset_0_-1px_0_0_var(--color-neutral-alphas-200)]",
|
|
63
77
|
"data-[orientation=vertical]:flex-col data-[orientation=vertical]:shadow-[inset_-1px_0_0_0_var(--color-neutral-alphas-200)]",
|
|
64
78
|
className
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TabsList.mjs","sources":["../../../src/components/Tabs/TabsList.tsx"],"sourcesContent":["import * as TabsPrimitive from \"@radix-ui/react-tabs\";\nimport * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\n\n/** Props for the {@link TabsList} component. */\nexport type TabsListProps = React.ComponentPropsWithoutRef<typeof TabsPrimitive.List> & {\n /** When `true` (the default), the tab list spans the full width of its container and each tab grows equally. Set to `false` for inline sizing. */\n fullWidth?: boolean;\n};\n\n/** Container for {@link TabsTrigger} elements. Renders a sliding active-tab indicator that animates between tabs. */\nexport const TabsList = React.forwardRef<\n React.ComponentRef<typeof TabsPrimitive.List>,\n TabsListProps\n>(({ className, children, fullWidth = true, ...props }, ref) => {\n const innerRef = React.useRef<HTMLDivElement>(null);\n const indicatorRef = React.useRef<HTMLSpanElement>(null);\n\n React.useImperativeHandle(ref, () => innerRef.current as HTMLDivElement);\n\n const updateIndicator = React.useCallback(() => {\n const list = innerRef.current;\n const indicator = indicatorRef.current;\n if (!list || !indicator) return;\n\n const activeTab = list.querySelector<HTMLElement>('[data-state=\"active\"]');\n if (!activeTab) {\n indicator.style.opacity = \"0\";\n return;\n }\n\n const isVertical = list.dataset.orientation === \"vertical\";\n\n indicator.style.opacity = \"1\";\n\n if (isVertical) {\n indicator.style.inset = `0 0 auto auto`;\n indicator.style.width = \"4px\";\n indicator.style.height = `${activeTab.offsetHeight}px`;\n indicator.style.transform = `translateY(${activeTab.offsetTop}px)`;\n } else {\n indicator.style.inset = `auto auto 0 0`;\n indicator.style.height = \"4px\";\n indicator.style.width = `${activeTab.offsetWidth}px`;\n indicator.style.transform = `translateX(${activeTab.offsetLeft}px)`;\n }\n }, []);\n\n React.useLayoutEffect(() => {\n const list = innerRef.current;\n const indicator = indicatorRef.current;\n if (!list || !indicator) return;\n\n indicator.style.transitionDuration = \"0s\";\n updateIndicator();\n indicator.getBoundingClientRect();\n indicator.style.transitionDuration = \"\";\n\n const mutationObserver = new MutationObserver(updateIndicator);\n mutationObserver.observe(list, {\n attributes: true,\n attributeFilter: [\"data-state\"],\n childList: true,\n subtree: true,\n });\n\n const resizeObserver = new ResizeObserver(updateIndicator);\n resizeObserver.observe(list);\n\n return () => {\n mutationObserver.disconnect();\n resizeObserver.disconnect();\n };\n }, [updateIndicator]);\n\n return (\n <TabsPrimitive.List\n ref={innerRef}\n className={cn(\n \"relative\",\n fullWidth
|
|
1
|
+
{"version":3,"file":"TabsList.mjs","sources":["../../../src/components/Tabs/TabsList.tsx"],"sourcesContent":["import * as TabsPrimitive from \"@radix-ui/react-tabs\";\nimport * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\n\n/** Breakpoint values for responsive props. */\ntype Breakpoint = \"sm\" | \"md\" | \"lg\" | \"xl\";\n\nconst alignLeftClasses: Record<Breakpoint | \"always\", string> = {\n always: \"[&>[role=tab]]:flex-initial\",\n sm: \"[&>[role=tab]]:sm:flex-initial\",\n md: \"[&>[role=tab]]:md:flex-initial\",\n lg: \"[&>[role=tab]]:lg:flex-initial\",\n xl: \"[&>[role=tab]]:xl:flex-initial\",\n};\n\nfunction getLayoutClass(fullWidth: boolean, alignLeft?: boolean | Breakpoint): string {\n if (!fullWidth) return \"inline-flex\";\n\n const base = \"flex w-full [&>[role=tab]]:flex-1\";\n if (alignLeft === true) return `${base} ${alignLeftClasses.always}`;\n if (typeof alignLeft === \"string\") return `${base} ${alignLeftClasses[alignLeft]}`;\n return base;\n}\n\n/** Props for the {@link TabsList} component. */\nexport type TabsListProps = React.ComponentPropsWithoutRef<typeof TabsPrimitive.List> & {\n /** When `true` (the default), the tab list spans the full width of its container and each tab grows equally. Set to `false` for inline sizing. */\n fullWidth?: boolean;\n /**\n * Controls tab alignment within a full-width container.\n * - `false` (default): tabs spread evenly\n * - `true`: tabs left-aligned, sized to content\n * - `\"md\"` (breakpoint): spread on mobile, left-aligned at breakpoint and up\n */\n alignLeft?: boolean | Breakpoint;\n};\n\n/** Container for {@link TabsTrigger} elements. Renders a sliding active-tab indicator that animates between tabs. */\nexport const TabsList = React.forwardRef<\n React.ComponentRef<typeof TabsPrimitive.List>,\n TabsListProps\n>(({ className, children, fullWidth = true, alignLeft, ...props }, ref) => {\n const innerRef = React.useRef<HTMLDivElement>(null);\n const indicatorRef = React.useRef<HTMLSpanElement>(null);\n\n React.useImperativeHandle(ref, () => innerRef.current as HTMLDivElement);\n\n const updateIndicator = React.useCallback(() => {\n const list = innerRef.current;\n const indicator = indicatorRef.current;\n if (!list || !indicator) return;\n\n const activeTab = list.querySelector<HTMLElement>('[data-state=\"active\"]');\n if (!activeTab) {\n indicator.style.opacity = \"0\";\n return;\n }\n\n const isVertical = list.dataset.orientation === \"vertical\";\n\n indicator.style.opacity = \"1\";\n\n if (isVertical) {\n indicator.style.inset = `0 0 auto auto`;\n indicator.style.width = \"4px\";\n indicator.style.height = `${activeTab.offsetHeight}px`;\n indicator.style.transform = `translateY(${activeTab.offsetTop}px)`;\n } else {\n indicator.style.inset = `auto auto 0 0`;\n indicator.style.height = \"4px\";\n indicator.style.width = `${activeTab.offsetWidth}px`;\n indicator.style.transform = `translateX(${activeTab.offsetLeft}px)`;\n }\n }, []);\n\n React.useLayoutEffect(() => {\n const list = innerRef.current;\n const indicator = indicatorRef.current;\n if (!list || !indicator) return;\n\n indicator.style.transitionDuration = \"0s\";\n updateIndicator();\n indicator.getBoundingClientRect();\n indicator.style.transitionDuration = \"\";\n\n const mutationObserver = new MutationObserver(updateIndicator);\n mutationObserver.observe(list, {\n attributes: true,\n attributeFilter: [\"data-state\"],\n childList: true,\n subtree: true,\n });\n\n const resizeObserver = new ResizeObserver(updateIndicator);\n resizeObserver.observe(list);\n\n return () => {\n mutationObserver.disconnect();\n resizeObserver.disconnect();\n };\n }, [updateIndicator]);\n\n return (\n <TabsPrimitive.List\n ref={innerRef}\n className={cn(\n \"relative\",\n getLayoutClass(fullWidth, alignLeft),\n \"data-[orientation=horizontal]:items-center data-[orientation=horizontal]:shadow-[inset_0_-1px_0_0_var(--color-neutral-alphas-200)]\",\n \"data-[orientation=vertical]:flex-col data-[orientation=vertical]:shadow-[inset_-1px_0_0_0_var(--color-neutral-alphas-200)]\",\n className,\n )}\n {...props}\n >\n {children}\n <span\n ref={indicatorRef}\n aria-hidden\n className=\"pointer-events-none absolute rounded-full bg-brand-primary-default motion-safe:transition-[transform,width,height] motion-safe:duration-200 motion-safe:ease-in-out\"\n style={{ opacity: 0 }}\n />\n </TabsPrimitive.List>\n );\n});\n\nTabsList.displayName = \"TabsList\";\n"],"names":[],"mappings":";;;;;AAOA,MAAM,mBAA0D;AAAA,EAC9D,QAAQ;AAAA,EACR,IAAI;AAAA,EACJ,IAAI;AAAA,EACJ,IAAI;AAAA,EACJ,IAAI;AACN;AAEA,SAAS,eAAe,WAAoB,WAA0C;AACpF,MAAI,CAAC,UAAW,QAAO;AAEvB,QAAM,OAAO;AACb,MAAI,cAAc,KAAM,QAAO,GAAG,IAAI,IAAI,iBAAiB,MAAM;AACjE,MAAI,OAAO,cAAc,SAAU,QAAO,GAAG,IAAI,IAAI,iBAAiB,SAAS,CAAC;AAChF,SAAO;AACT;AAgBO,MAAM,WAAW,MAAM,WAG5B,CAAC,EAAE,WAAW,UAAU,YAAY,MAAM,WAAW,GAAG,MAAA,GAAS,QAAQ;AACzE,QAAM,WAAW,MAAM,OAAuB,IAAI;AAClD,QAAM,eAAe,MAAM,OAAwB,IAAI;AAEvD,QAAM,oBAAoB,KAAK,MAAM,SAAS,OAAyB;AAEvE,QAAM,kBAAkB,MAAM,YAAY,MAAM;AAC9C,UAAM,OAAO,SAAS;AACtB,UAAM,YAAY,aAAa;AAC/B,QAAI,CAAC,QAAQ,CAAC,UAAW;AAEzB,UAAM,YAAY,KAAK,cAA2B,uBAAuB;AACzE,QAAI,CAAC,WAAW;AACd,gBAAU,MAAM,UAAU;AAC1B;AAAA,IACF;AAEA,UAAM,aAAa,KAAK,QAAQ,gBAAgB;AAEhD,cAAU,MAAM,UAAU;AAE1B,QAAI,YAAY;AACd,gBAAU,MAAM,QAAQ;AACxB,gBAAU,MAAM,QAAQ;AACxB,gBAAU,MAAM,SAAS,GAAG,UAAU,YAAY;AAClD,gBAAU,MAAM,YAAY,cAAc,UAAU,SAAS;AAAA,IAC/D,OAAO;AACL,gBAAU,MAAM,QAAQ;AACxB,gBAAU,MAAM,SAAS;AACzB,gBAAU,MAAM,QAAQ,GAAG,UAAU,WAAW;AAChD,gBAAU,MAAM,YAAY,cAAc,UAAU,UAAU;AAAA,IAChE;AAAA,EACF,GAAG,CAAA,CAAE;AAEL,QAAM,gBAAgB,MAAM;AAC1B,UAAM,OAAO,SAAS;AACtB,UAAM,YAAY,aAAa;AAC/B,QAAI,CAAC,QAAQ,CAAC,UAAW;AAEzB,cAAU,MAAM,qBAAqB;AACrC,oBAAA;AACA,cAAU,sBAAA;AACV,cAAU,MAAM,qBAAqB;AAErC,UAAM,mBAAmB,IAAI,iBAAiB,eAAe;AAC7D,qBAAiB,QAAQ,MAAM;AAAA,MAC7B,YAAY;AAAA,MACZ,iBAAiB,CAAC,YAAY;AAAA,MAC9B,WAAW;AAAA,MACX,SAAS;AAAA,IAAA,CACV;AAED,UAAM,iBAAiB,IAAI,eAAe,eAAe;AACzD,mBAAe,QAAQ,IAAI;AAE3B,WAAO,MAAM;AACX,uBAAiB,WAAA;AACjB,qBAAe,WAAA;AAAA,IACjB;AAAA,EACF,GAAG,CAAC,eAAe,CAAC;AAEpB,SACE;AAAA,IAAC,cAAc;AAAA,IAAd;AAAA,MACC,KAAK;AAAA,MACL,WAAW;AAAA,QACT;AAAA,QACA,eAAe,WAAW,SAAS;AAAA,QACnC;AAAA,QACA;AAAA,QACA;AAAA,MAAA;AAAA,MAED,GAAG;AAAA,MAEH,UAAA;AAAA,QAAA;AAAA,QACD;AAAA,UAAC;AAAA,UAAA;AAAA,YACC,KAAK;AAAA,YACL,eAAW;AAAA,YACX,WAAU;AAAA,YACV,OAAO,EAAE,SAAS,EAAA;AAAA,UAAE;AAAA,QAAA;AAAA,MACtB;AAAA,IAAA;AAAA,EAAA;AAGN,CAAC;AAED,SAAS,cAAc;"}
|
package/dist/index.d.ts
CHANGED
|
@@ -522,6 +522,9 @@ export declare const BreadcrumbSeparator: React_2.ForwardRefExoticComponent<Brea
|
|
|
522
522
|
export declare interface BreadcrumbSeparatorProps extends React_2.ComponentPropsWithoutRef<"li"> {
|
|
523
523
|
}
|
|
524
524
|
|
|
525
|
+
/** Breakpoint values for responsive props. */
|
|
526
|
+
declare type Breakpoint = "sm" | "md" | "lg" | "xl";
|
|
527
|
+
|
|
525
528
|
export declare const BulbIcon: React_2.ForwardRefExoticComponent<React_2.SVGAttributes<SVGSVGElement> & {
|
|
526
529
|
className?: string;
|
|
527
530
|
} & React_2.RefAttributes<SVGSVGElement>>;
|
|
@@ -2691,12 +2694,26 @@ export declare type TabsContentProps = React_2.ComponentPropsWithoutRef<typeof T
|
|
|
2691
2694
|
export declare const TabsList: React_2.ForwardRefExoticComponent<Omit<TabsPrimitive.TabsListProps & React_2.RefAttributes<HTMLDivElement>, "ref"> & {
|
|
2692
2695
|
/** When `true` (the default), the tab list spans the full width of its container and each tab grows equally. Set to `false` for inline sizing. */
|
|
2693
2696
|
fullWidth?: boolean;
|
|
2697
|
+
/**
|
|
2698
|
+
* Controls tab alignment within a full-width container.
|
|
2699
|
+
* - `false` (default): tabs spread evenly
|
|
2700
|
+
* - `true`: tabs left-aligned, sized to content
|
|
2701
|
+
* - `"md"` (breakpoint): spread on mobile, left-aligned at breakpoint and up
|
|
2702
|
+
*/
|
|
2703
|
+
alignLeft?: boolean | Breakpoint;
|
|
2694
2704
|
} & React_2.RefAttributes<HTMLDivElement>>;
|
|
2695
2705
|
|
|
2696
2706
|
/** Props for the {@link TabsList} component. */
|
|
2697
2707
|
export declare type TabsListProps = React_2.ComponentPropsWithoutRef<typeof TabsPrimitive.List> & {
|
|
2698
2708
|
/** When `true` (the default), the tab list spans the full width of its container and each tab grows equally. Set to `false` for inline sizing. */
|
|
2699
2709
|
fullWidth?: boolean;
|
|
2710
|
+
/**
|
|
2711
|
+
* Controls tab alignment within a full-width container.
|
|
2712
|
+
* - `false` (default): tabs spread evenly
|
|
2713
|
+
* - `true`: tabs left-aligned, sized to content
|
|
2714
|
+
* - `"md"` (breakpoint): spread on mobile, left-aligned at breakpoint and up
|
|
2715
|
+
*/
|
|
2716
|
+
alignLeft?: boolean | Breakpoint;
|
|
2700
2717
|
};
|
|
2701
2718
|
|
|
2702
2719
|
/** Props for the {@link Tabs} root component. Extends Radix `Tabs.Root` props. */
|