@spear-ai/spectral 1.15.1 → 1.15.3
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/Accordion.js +3 -3
- package/dist/Accordion.js.map +1 -1
- package/dist/Alert/AlertBase.js +1 -1
- package/dist/Alert/AlertBase.js.map +1 -1
- package/dist/Alert.js +3 -3
- package/dist/Alert.js.map +1 -1
- package/dist/Avatar.d.ts.map +1 -1
- package/dist/Avatar.js +17 -10
- package/dist/Avatar.js.map +1 -1
- package/dist/ButtonGroup/ButtonGroupButton.js +1 -1
- package/dist/ButtonGroup/ButtonGroupButton.js.map +1 -1
- package/dist/ControlGroup.js +1 -1
- package/dist/ControlGroup.js.map +1 -1
- package/dist/DateTimePicker/Calendar.js +1 -1
- package/dist/DateTimePicker/Calendar.js.map +1 -1
- package/dist/Dialog.d.ts +1 -0
- package/dist/Dialog.d.ts.map +1 -1
- package/dist/Dialog.js +7 -6
- package/dist/Dialog.js.map +1 -1
- package/dist/Drawer.d.ts.map +1 -1
- package/dist/Drawer.js +27 -23
- package/dist/Drawer.js.map +1 -1
- package/dist/Icons/SyncIcon.d.ts +18 -0
- package/dist/Icons/SyncIcon.d.ts.map +1 -0
- package/dist/Icons/SyncIcon.js +33 -0
- package/dist/Icons/SyncIcon.js.map +1 -0
- package/dist/Icons/SyncOffIcon.d.ts +18 -0
- package/dist/Icons/SyncOffIcon.d.ts.map +1 -0
- package/dist/Icons/SyncOffIcon.js +43 -0
- package/dist/Icons/SyncOffIcon.js.map +1 -0
- package/dist/Icons.d.ts +3 -1
- package/dist/Icons.js +3 -1
- package/dist/Input.js +1 -1
- package/dist/Input.js.map +1 -1
- package/dist/MultiSelect/MultiSelectBase.js +3 -2
- package/dist/MultiSelect/MultiSelectBase.js.map +1 -1
- package/dist/RadioGroup.js +1 -1
- package/dist/RadioGroup.js.map +1 -1
- package/dist/Switch.js +4 -4
- package/dist/Switch.js.map +1 -1
- package/dist/Tabs/TabsBase.d.ts.map +1 -1
- package/dist/Tabs/TabsBase.js +10 -56
- package/dist/Tabs/TabsBase.js.map +1 -1
- package/dist/Tabs.js +1 -1
- package/dist/Tabs.js.map +1 -1
- package/dist/ToggleGroup/ToggleGroupItem.js +1 -1
- package/dist/ToggleGroup/ToggleGroupItem.js.map +1 -1
- package/dist/Tooltip.js +1 -1
- package/dist/Tooltip.js.map +1 -1
- package/dist/Tray.d.ts.map +1 -1
- package/dist/Tray.js +11 -49
- package/dist/Tray.js.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +3 -1
- package/dist/styles/horizon/base.css +18 -7
- package/dist/styles/horizon/colors.css +3 -1
- package/dist/styles/spectral.css +1 -1
- package/package.json +1 -1
package/dist/Switch.js
CHANGED
|
@@ -47,17 +47,17 @@ const Switch = ({ className, disabled, errorMessage, hideLabel = false, id, labe
|
|
|
47
47
|
required,
|
|
48
48
|
value,
|
|
49
49
|
...props,
|
|
50
|
-
children: /* @__PURE__ */ jsx(SwitchThumb, { className: cn(thumbBase, "size-6.5 shadow-xs relative z-10 duration-
|
|
50
|
+
children: /* @__PURE__ */ jsx(SwitchThumb, { className: cn(thumbBase, "size-6.5 shadow-xs relative z-10 duration-200 ease-out data-[state=checked]:translate-x-[26px] motion-reduce:duration-0 data-[state=checked]:rtl:translate-x-[-26px]") })
|
|
51
51
|
}),
|
|
52
52
|
/* @__PURE__ */ jsx("span", {
|
|
53
|
-
className: "ml-0.5 min-w-8 peer-data-[state=unchecked]:translate-x-6 peer-data-[state=unchecked]:rtl:-translate-x-6 motion-reduce:translate-x-0 pointer-events-none relative flex items-center justify-center text-center transition-transform duration-
|
|
53
|
+
className: "ml-0.5 min-w-8 peer-data-[state=unchecked]:translate-x-6 peer-data-[state=unchecked]:rtl:-translate-x-6 motion-reduce:translate-x-0 pointer-events-none relative flex items-center justify-center text-center transition-transform duration-200 ease-out peer-data-[state=checked]:invisible motion-reduce:transition-none motion-reduce:duration-0",
|
|
54
54
|
children: /* @__PURE__ */ jsx(XIcon, {
|
|
55
55
|
"aria-hidden": "true",
|
|
56
56
|
className: "size-4"
|
|
57
57
|
})
|
|
58
58
|
}),
|
|
59
59
|
/* @__PURE__ */ jsx("span", {
|
|
60
|
-
className: "min-w-8 motion-reduce:translate-x-0 pointer-events-none relative flex items-center justify-center text-center transition-transform duration-
|
|
60
|
+
className: "min-w-8 motion-reduce:translate-x-0 pointer-events-none relative flex items-center justify-center text-center transition-transform duration-200 ease-out peer-data-[state=checked]:-translate-x-full peer-data-[state=checked]:text-background peer-data-[state=unchecked]:invisible motion-reduce:transition-none motion-reduce:duration-0 peer-data-[state=checked]:rtl:translate-x-full",
|
|
61
61
|
children: /* @__PURE__ */ jsx(CheckIcon, {
|
|
62
62
|
"aria-hidden": "true",
|
|
63
63
|
className: "size-4"
|
|
@@ -69,7 +69,7 @@ const Switch = ({ className, disabled, errorMessage, hideLabel = false, id, labe
|
|
|
69
69
|
"aria-describedby": [messageId, ariaDescribedBy].filter(Boolean).join(" ") || void 0,
|
|
70
70
|
"aria-invalid": state === "error" ? true : void 0,
|
|
71
71
|
"aria-label": ariaLabel ?? (hideLabel ? resolvedLabelText : void 0),
|
|
72
|
-
className: cn(isSquared ? "peer h-6 w-10 rounded-sm focus-visible:ring-black inline-flex shrink-0 items-center border-2 border-transparent transition-
|
|
72
|
+
className: cn(isSquared ? "peer h-6 w-10 rounded-sm focus-visible:ring-black inline-flex shrink-0 items-center border-2 border-transparent transition-colors outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-switch-bg--checked data-[state=unchecked]:bg-switch-bg [&_span]:rounded-[4px]" : "focus-visible:ring-ring peer h-6 w-10 shadow-2xs inline-flex shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent transition-colors focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-offset-background focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-switch-bg--checked data-[state=unchecked]:bg-switch-bg", className),
|
|
73
73
|
"data-testid": "spectral-switch",
|
|
74
74
|
disabled: isDisabled,
|
|
75
75
|
id: switchId,
|
package/dist/Switch.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Switch.js","names":["SwitchBase"],"sources":["../src/components/Switch/Switch.tsx"],"sourcesContent":["import { Label } from '@components/Label/Label'\nimport { ErrorMessage, WarningMessage, useFormFieldId, type BaseFormFieldProps, type FormFieldState } from '@utils/formFieldUtils'\nimport { cn } from '@utils/twUtils'\nimport { CheckIcon, XIcon } from 'lucide-react'\nimport { type Ref } from 'react'\nimport { Switch as SwitchBase, SwitchThumb, type SwitchProps as SwitchBaseProps } from './SwitchBase'\n\ntype SwitchVisualState = Exclude<FormFieldState, 'disabled'>\n\nexport type SwitchProps = Omit<SwitchBaseProps, 'onCheckedChange'> & {\n 'aria-describedby'?: string\n 'aria-label'?: string\n errorMessage?: BaseFormFieldProps['errorMessage']\n hideLabel?: boolean\n id?: string\n label?: string\n labelPosition?: 'left' | 'right'\n labelText?: string\n messageReserveLines?: number\n messageReserveSpace?: boolean\n onChange?: (checked: boolean) => void\n ref?: Ref<HTMLButtonElement>\n required?: boolean\n state?: SwitchVisualState\n value?: string\n variant?: 'default' | 'squared' | 'permanent-indicator'\n warningMessage?: BaseFormFieldProps['errorMessage']\n}\n\nconst thumbBase = 'bg-switch-thumb--checked data-[state=checked]:bg-switch-thumb pointer-events-none block rounded-full ring-0 transition-transform motion-reduce:transition-none'\n\nexport const Switch = ({\n className,\n disabled,\n errorMessage,\n hideLabel = false,\n id,\n label,\n labelPosition = 'right',\n labelText,\n messageReserveLines = 1,\n messageReserveSpace = false,\n name,\n onChange,\n ref,\n required,\n state = 'default',\n value,\n variant,\n warningMessage,\n 'aria-describedby': ariaDescribedBy,\n 'aria-label': ariaLabel,\n ...props\n}: SwitchProps) => {\n const switchId = useFormFieldId(id, name)\n const isDisabled = Boolean(disabled)\n const resolvedLabelText = labelText ?? label\n const errorMessageId = `${switchId}-error`\n const warningMessageId = `${switchId}-warning`\n const messageId = state === 'error' && errorMessage && errorMessageId ? errorMessageId : state === 'warning' && warningMessage && warningMessageId ? warningMessageId : undefined\n const isSquared = variant === 'squared'\n const isPermanentIndicator = variant === 'permanent-indicator'\n\n return (\n <div\n className='flex items-center'\n data-testid='spectral-switch-container'\n data-state={state}\n >\n {labelPosition === 'left' && !hideLabel && resolvedLabelText && (\n <Label\n className='mr-2'\n data-testid='spectral-switch-label-left'\n htmlFor={switchId}\n >\n {resolvedLabelText}\n </Label>\n )}\n\n {isPermanentIndicator ? (\n <div className='h-7 text-sm font-medium relative inline-grid grid-cols-[1fr_1fr] items-center'>\n <SwitchBase\n aria-required={required}\n aria-describedby={[messageId, ariaDescribedBy].filter(Boolean).join(' ') || undefined}\n aria-invalid={state === 'error' ? true : undefined}\n aria-label={ariaLabel ?? (hideLabel ? resolvedLabelText : undefined)}\n className={cn(\n 'peer inset-0 w-14 focus-visible:ring-ring absolute inline-flex h-[inherit] items-center focus-visible:ring-offset-background data-[state=checked]:bg-switch-bg--checked data-[state=unchecked]:bg-switch-bg/50',\n 'shadow-2xs cursor-pointer rounded-full border-2 border-transparent transition-colors focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:outline-none',\n 'disabled:cursor-not-allowed disabled:opacity-50',\n className,\n )}\n data-testid='spectral-switch'\n disabled={isDisabled}\n id={switchId}\n name={name}\n onCheckedChange={onChange}\n ref={ref}\n required={required}\n value={value}\n {...props}\n >\n <SwitchThumb className={cn(thumbBase, 'size-6.5 shadow-xs relative z-10 duration-300 ease-[cubic-bezier(0.16,1,0.3,1)] data-[state=checked]:translate-x-[26px] motion-reduce:duration-0 data-[state=checked]:rtl:translate-x-[-26px]')} />\n </SwitchBase>\n <span className='ml-0.5 min-w-8 peer-data-[state=unchecked]:translate-x-6 peer-data-[state=unchecked]:rtl:-translate-x-6 motion-reduce:translate-x-0 pointer-events-none relative flex items-center justify-center text-center transition-transform duration-300 ease-[cubic-bezier(0.16,1,0.3,1)] peer-data-[state=checked]:invisible motion-reduce:transition-none motion-reduce:duration-0'>\n <XIcon\n aria-hidden='true'\n className='size-4'\n />\n </span>\n <span className='min-w-8 motion-reduce:translate-x-0 pointer-events-none relative flex items-center justify-center text-center transition-transform duration-300 ease-[cubic-bezier(0.16,1,0.3,1)] peer-data-[state=checked]:-translate-x-full peer-data-[state=checked]:text-background peer-data-[state=unchecked]:invisible motion-reduce:transition-none motion-reduce:duration-0 peer-data-[state=checked]:rtl:translate-x-full'>\n <CheckIcon\n aria-hidden='true'\n className='size-4'\n />\n </span>\n </div>\n ) : (\n <SwitchBase\n aria-required={required}\n aria-describedby={[messageId, ariaDescribedBy].filter(Boolean).join(' ') || undefined}\n aria-invalid={state === 'error' ? true : undefined}\n aria-label={ariaLabel ?? (hideLabel ? resolvedLabelText : undefined)}\n className={cn(\n isSquared\n ? 'peer h-6 w-10 rounded-sm focus-visible:ring-black inline-flex shrink-0 items-center border-2 border-transparent transition-all outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-switch-bg--checked data-[state=unchecked]:bg-switch-bg [&_span]:rounded-[4px]'\n : 'focus-visible:ring-ring peer h-6 w-10 shadow-2xs inline-flex shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent transition-colors focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-offset-background focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-switch-bg--checked data-[state=unchecked]:bg-switch-bg',\n className,\n )}\n data-testid='spectral-switch'\n disabled={isDisabled}\n id={switchId}\n name={name}\n onCheckedChange={onChange}\n ref={ref}\n required={required}\n value={value}\n {...props}\n >\n <SwitchThumb className={cn(thumbBase, isSquared ? 'size-5 shadow-xs data-[state=checked]:translate-x-4 data-[state=checked]:rtl:-translate-x-4' : 'h-5 w-5 shadow-lg data-[state=checked]:translate-x-4')} />\n </SwitchBase>\n )}\n\n {labelPosition === 'right' && !hideLabel && resolvedLabelText && (\n <Label\n className='ml-2'\n data-testid='spectral-switch-label-right'\n htmlFor={switchId}\n id={`${switchId}-label`}\n >\n {resolvedLabelText}\n </Label>\n )}\n\n {hideLabel && resolvedLabelText && (\n <Label\n className='sr-only'\n data-testid='spectral-switch-label-hidden'\n htmlFor={switchId}\n >\n {resolvedLabelText}\n </Label>\n )}\n <ErrorMessage\n dataTestId='spectral-switch-error-message'\n id={errorMessageId}\n message={state === 'error' ? (errorMessage ?? null) : null}\n messageReserveLines={messageReserveLines}\n messageReserveSpace={messageReserveSpace && state === 'error'}\n />\n <WarningMessage\n dataTestId='spectral-switch-warning-message'\n id={warningMessageId}\n message={state === 'warning' ? (warningMessage ?? null) : null}\n messageReserveLines={messageReserveLines}\n messageReserveSpace={messageReserveSpace && state === 'warning'}\n />\n </div>\n )\n}\nSwitch.displayName = 'Switch'\n"],"mappings":";;;;;;;;;;;AA6BA,MAAM,YAAY;AAElB,MAAa,UAAU,EACrB,WACA,UACA,cACA,YAAY,OACZ,IACA,OACA,gBAAgB,SAChB,WACA,sBAAsB,GACtB,sBAAsB,OACtB,MACA,UACA,KACA,UACA,QAAQ,WACR,OACA,SACA,gBACA,oBAAoB,iBACpB,cAAc,WACd,GAAG,YACc;CACjB,MAAM,WAAW,eAAe,IAAI,KAAK;CACzC,MAAM,aAAa,QAAQ,SAAS;CACpC,MAAM,oBAAoB,aAAa;CACvC,MAAM,iBAAiB,GAAG,SAAS;CACnC,MAAM,mBAAmB,GAAG,SAAS;CACrC,MAAM,YAAY,UAAU,WAAW,gBAAgB,iBAAiB,iBAAiB,UAAU,aAAa,kBAAkB,mBAAmB,mBAAmB;CACxK,MAAM,YAAY,YAAY;AAG9B,QACE,qBAAC,OAAD;EACE,WAAU;EACV,eAAY;EACZ,cAAY;YAHd;GAKG,kBAAkB,UAAU,CAAC,aAAa,qBACzC,oBAAC,OAAD;IACE,WAAU;IACV,eAAY;IACZ,SAAS;cAER;IACK;GAfe,YAAY,wBAmBnC,qBAAC,OAAD;IAAK,WAAU;cAAf;KACE,oBAACA,UAAD;MACE,iBAAe;MACf,oBAAkB,CAAC,WAAW,gBAAgB,CAAC,OAAO,QAAQ,CAAC,KAAK,IAAI,IAAI;MAC5E,gBAAc,UAAU,UAAU,OAAO;MACzC,cAAY,cAAc,YAAY,oBAAoB;MAC1D,WAAW,GACT,kNACA,oKACA,mDACA,UACD;MACD,eAAY;MACZ,UAAU;MACV,IAAI;MACE;MACN,iBAAiB;MACZ;MACK;MACH;MACP,GAAI;gBAEJ,oBAAC,aAAD,EAAa,WAAW,GAAG,WAAW,gMAAgM,EAAI;MAC/N;KACb,oBAAC,QAAD;MAAM,WAAU;gBACd,oBAAC,OAAD;OACE,eAAY;OACZ,WAAU;OACV;MACG;KACP,oBAAC,QAAD;MAAM,WAAU;gBACd,oBAAC,WAAD;OACE,eAAY;OACZ,WAAU;OACV;MACG;KACH;QAEN,oBAACA,UAAD;IACE,iBAAe;IACf,oBAAkB,CAAC,WAAW,gBAAgB,CAAC,OAAO,QAAQ,CAAC,KAAK,IAAI,IAAI;IAC5E,gBAAc,UAAU,UAAU,OAAO;IACzC,cAAY,cAAc,YAAY,oBAAoB;IAC1D,WAAW,GACT,YACI,+TACA,gZACJ,UACD;IACD,eAAY;IACZ,UAAU;IACV,IAAI;IACE;IACN,iBAAiB;IACZ;IACK;IACH;IACP,GAAI;cAEJ,oBAAC,aAAD,EAAa,WAAW,GAAG,WAAW,YAAY,gGAAgG,uDAAuD,EAAI;IAClM;GAGd,kBAAkB,WAAW,CAAC,aAAa,qBAC1C,oBAAC,OAAD;IACE,WAAU;IACV,eAAY;IACZ,SAAS;IACT,IAAI,GAAG,SAAS;cAEf;IACK;GAGT,aAAa,qBACZ,oBAAC,OAAD;IACE,WAAU;IACV,eAAY;IACZ,SAAS;cAER;IACK;GAEV,oBAAC,cAAD;IACE,YAAW;IACX,IAAI;IACJ,SAAS,UAAU,UAAW,gBAAgB,OAAQ;IACjC;IACrB,qBAAqB,uBAAuB,UAAU;IACtD;GACF,oBAAC,gBAAD;IACE,YAAW;IACX,IAAI;IACJ,SAAS,UAAU,YAAa,kBAAkB,OAAQ;IACrC;IACrB,qBAAqB,uBAAuB,UAAU;IACtD;GACE;;;AAGV,OAAO,cAAc"}
|
|
1
|
+
{"version":3,"file":"Switch.js","names":["SwitchBase"],"sources":["../src/components/Switch/Switch.tsx"],"sourcesContent":["import { Label } from '@components/Label/Label'\nimport { ErrorMessage, WarningMessage, useFormFieldId, type BaseFormFieldProps, type FormFieldState } from '@utils/formFieldUtils'\nimport { cn } from '@utils/twUtils'\nimport { CheckIcon, XIcon } from 'lucide-react'\nimport { type Ref } from 'react'\nimport { Switch as SwitchBase, SwitchThumb, type SwitchProps as SwitchBaseProps } from './SwitchBase'\n\ntype SwitchVisualState = Exclude<FormFieldState, 'disabled'>\n\nexport type SwitchProps = Omit<SwitchBaseProps, 'onCheckedChange'> & {\n 'aria-describedby'?: string\n 'aria-label'?: string\n errorMessage?: BaseFormFieldProps['errorMessage']\n hideLabel?: boolean\n id?: string\n label?: string\n labelPosition?: 'left' | 'right'\n labelText?: string\n messageReserveLines?: number\n messageReserveSpace?: boolean\n onChange?: (checked: boolean) => void\n ref?: Ref<HTMLButtonElement>\n required?: boolean\n state?: SwitchVisualState\n value?: string\n variant?: 'default' | 'squared' | 'permanent-indicator'\n warningMessage?: BaseFormFieldProps['errorMessage']\n}\n\nconst thumbBase = 'bg-switch-thumb--checked data-[state=checked]:bg-switch-thumb pointer-events-none block rounded-full ring-0 transition-transform motion-reduce:transition-none'\n\nexport const Switch = ({\n className,\n disabled,\n errorMessage,\n hideLabel = false,\n id,\n label,\n labelPosition = 'right',\n labelText,\n messageReserveLines = 1,\n messageReserveSpace = false,\n name,\n onChange,\n ref,\n required,\n state = 'default',\n value,\n variant,\n warningMessage,\n 'aria-describedby': ariaDescribedBy,\n 'aria-label': ariaLabel,\n ...props\n}: SwitchProps) => {\n const switchId = useFormFieldId(id, name)\n const isDisabled = Boolean(disabled)\n const resolvedLabelText = labelText ?? label\n const errorMessageId = `${switchId}-error`\n const warningMessageId = `${switchId}-warning`\n const messageId = state === 'error' && errorMessage && errorMessageId ? errorMessageId : state === 'warning' && warningMessage && warningMessageId ? warningMessageId : undefined\n const isSquared = variant === 'squared'\n const isPermanentIndicator = variant === 'permanent-indicator'\n\n return (\n <div\n className='flex items-center'\n data-testid='spectral-switch-container'\n data-state={state}\n >\n {labelPosition === 'left' && !hideLabel && resolvedLabelText && (\n <Label\n className='mr-2'\n data-testid='spectral-switch-label-left'\n htmlFor={switchId}\n >\n {resolvedLabelText}\n </Label>\n )}\n\n {isPermanentIndicator ? (\n <div className='h-7 text-sm font-medium relative inline-grid grid-cols-[1fr_1fr] items-center'>\n <SwitchBase\n aria-required={required}\n aria-describedby={[messageId, ariaDescribedBy].filter(Boolean).join(' ') || undefined}\n aria-invalid={state === 'error' ? true : undefined}\n aria-label={ariaLabel ?? (hideLabel ? resolvedLabelText : undefined)}\n className={cn(\n 'peer inset-0 w-14 focus-visible:ring-ring absolute inline-flex h-[inherit] items-center focus-visible:ring-offset-background data-[state=checked]:bg-switch-bg--checked data-[state=unchecked]:bg-switch-bg/50',\n 'shadow-2xs cursor-pointer rounded-full border-2 border-transparent transition-colors focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:outline-none',\n 'disabled:cursor-not-allowed disabled:opacity-50',\n className,\n )}\n data-testid='spectral-switch'\n disabled={isDisabled}\n id={switchId}\n name={name}\n onCheckedChange={onChange}\n ref={ref}\n required={required}\n value={value}\n {...props}\n >\n <SwitchThumb className={cn(thumbBase, 'size-6.5 shadow-xs relative z-10 duration-200 ease-out data-[state=checked]:translate-x-[26px] motion-reduce:duration-0 data-[state=checked]:rtl:translate-x-[-26px]')} />\n </SwitchBase>\n <span className='ml-0.5 min-w-8 peer-data-[state=unchecked]:translate-x-6 peer-data-[state=unchecked]:rtl:-translate-x-6 motion-reduce:translate-x-0 pointer-events-none relative flex items-center justify-center text-center transition-transform duration-200 ease-out peer-data-[state=checked]:invisible motion-reduce:transition-none motion-reduce:duration-0'>\n <XIcon\n aria-hidden='true'\n className='size-4'\n />\n </span>\n <span className='min-w-8 motion-reduce:translate-x-0 pointer-events-none relative flex items-center justify-center text-center transition-transform duration-200 ease-out peer-data-[state=checked]:-translate-x-full peer-data-[state=checked]:text-background peer-data-[state=unchecked]:invisible motion-reduce:transition-none motion-reduce:duration-0 peer-data-[state=checked]:rtl:translate-x-full'>\n <CheckIcon\n aria-hidden='true'\n className='size-4'\n />\n </span>\n </div>\n ) : (\n <SwitchBase\n aria-required={required}\n aria-describedby={[messageId, ariaDescribedBy].filter(Boolean).join(' ') || undefined}\n aria-invalid={state === 'error' ? true : undefined}\n aria-label={ariaLabel ?? (hideLabel ? resolvedLabelText : undefined)}\n className={cn(\n isSquared\n ? 'peer h-6 w-10 rounded-sm focus-visible:ring-black inline-flex shrink-0 items-center border-2 border-transparent transition-colors outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-switch-bg--checked data-[state=unchecked]:bg-switch-bg [&_span]:rounded-[4px]'\n : 'focus-visible:ring-ring peer h-6 w-10 shadow-2xs inline-flex shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent transition-colors focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-offset-background focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-switch-bg--checked data-[state=unchecked]:bg-switch-bg',\n className,\n )}\n data-testid='spectral-switch'\n disabled={isDisabled}\n id={switchId}\n name={name}\n onCheckedChange={onChange}\n ref={ref}\n required={required}\n value={value}\n {...props}\n >\n <SwitchThumb className={cn(thumbBase, isSquared ? 'size-5 shadow-xs data-[state=checked]:translate-x-4 data-[state=checked]:rtl:-translate-x-4' : 'h-5 w-5 shadow-lg data-[state=checked]:translate-x-4')} />\n </SwitchBase>\n )}\n\n {labelPosition === 'right' && !hideLabel && resolvedLabelText && (\n <Label\n className='ml-2'\n data-testid='spectral-switch-label-right'\n htmlFor={switchId}\n id={`${switchId}-label`}\n >\n {resolvedLabelText}\n </Label>\n )}\n\n {hideLabel && resolvedLabelText && (\n <Label\n className='sr-only'\n data-testid='spectral-switch-label-hidden'\n htmlFor={switchId}\n >\n {resolvedLabelText}\n </Label>\n )}\n <ErrorMessage\n dataTestId='spectral-switch-error-message'\n id={errorMessageId}\n message={state === 'error' ? (errorMessage ?? null) : null}\n messageReserveLines={messageReserveLines}\n messageReserveSpace={messageReserveSpace && state === 'error'}\n />\n <WarningMessage\n dataTestId='spectral-switch-warning-message'\n id={warningMessageId}\n message={state === 'warning' ? (warningMessage ?? null) : null}\n messageReserveLines={messageReserveLines}\n messageReserveSpace={messageReserveSpace && state === 'warning'}\n />\n </div>\n )\n}\nSwitch.displayName = 'Switch'\n"],"mappings":";;;;;;;;;;;AA6BA,MAAM,YAAY;AAElB,MAAa,UAAU,EACrB,WACA,UACA,cACA,YAAY,OACZ,IACA,OACA,gBAAgB,SAChB,WACA,sBAAsB,GACtB,sBAAsB,OACtB,MACA,UACA,KACA,UACA,QAAQ,WACR,OACA,SACA,gBACA,oBAAoB,iBACpB,cAAc,WACd,GAAG,YACc;CACjB,MAAM,WAAW,eAAe,IAAI,KAAK;CACzC,MAAM,aAAa,QAAQ,SAAS;CACpC,MAAM,oBAAoB,aAAa;CACvC,MAAM,iBAAiB,GAAG,SAAS;CACnC,MAAM,mBAAmB,GAAG,SAAS;CACrC,MAAM,YAAY,UAAU,WAAW,gBAAgB,iBAAiB,iBAAiB,UAAU,aAAa,kBAAkB,mBAAmB,mBAAmB;CACxK,MAAM,YAAY,YAAY;AAG9B,QACE,qBAAC,OAAD;EACE,WAAU;EACV,eAAY;EACZ,cAAY;YAHd;GAKG,kBAAkB,UAAU,CAAC,aAAa,qBACzC,oBAAC,OAAD;IACE,WAAU;IACV,eAAY;IACZ,SAAS;cAER;IACK;GAfe,YAAY,wBAmBnC,qBAAC,OAAD;IAAK,WAAU;cAAf;KACE,oBAACA,UAAD;MACE,iBAAe;MACf,oBAAkB,CAAC,WAAW,gBAAgB,CAAC,OAAO,QAAQ,CAAC,KAAK,IAAI,IAAI;MAC5E,gBAAc,UAAU,UAAU,OAAO;MACzC,cAAY,cAAc,YAAY,oBAAoB;MAC1D,WAAW,GACT,kNACA,oKACA,mDACA,UACD;MACD,eAAY;MACZ,UAAU;MACV,IAAI;MACE;MACN,iBAAiB;MACZ;MACK;MACH;MACP,GAAI;gBAEJ,oBAAC,aAAD,EAAa,WAAW,GAAG,WAAW,uKAAuK,EAAI;MACtM;KACb,oBAAC,QAAD;MAAM,WAAU;gBACd,oBAAC,OAAD;OACE,eAAY;OACZ,WAAU;OACV;MACG;KACP,oBAAC,QAAD;MAAM,WAAU;gBACd,oBAAC,WAAD;OACE,eAAY;OACZ,WAAU;OACV;MACG;KACH;QAEN,oBAACA,UAAD;IACE,iBAAe;IACf,oBAAkB,CAAC,WAAW,gBAAgB,CAAC,OAAO,QAAQ,CAAC,KAAK,IAAI,IAAI;IAC5E,gBAAc,UAAU,UAAU,OAAO;IACzC,cAAY,cAAc,YAAY,oBAAoB;IAC1D,WAAW,GACT,YACI,kUACA,gZACJ,UACD;IACD,eAAY;IACZ,UAAU;IACV,IAAI;IACE;IACN,iBAAiB;IACZ;IACK;IACH;IACP,GAAI;cAEJ,oBAAC,aAAD,EAAa,WAAW,GAAG,WAAW,YAAY,gGAAgG,uDAAuD,EAAI;IAClM;GAGd,kBAAkB,WAAW,CAAC,aAAa,qBAC1C,oBAAC,OAAD;IACE,WAAU;IACV,eAAY;IACZ,SAAS;IACT,IAAI,GAAG,SAAS;cAEf;IACK;GAGT,aAAa,qBACZ,oBAAC,OAAD;IACE,WAAU;IACV,eAAY;IACZ,SAAS;cAER;IACK;GAEV,oBAAC,cAAD;IACE,YAAW;IACX,IAAI;IACJ,SAAS,UAAU,UAAW,gBAAgB,OAAQ;IACjC;IACrB,qBAAqB,uBAAuB,UAAU;IACtD;GACF,oBAAC,gBAAD;IACE,YAAW;IACX,IAAI;IACJ,SAAS,UAAU,YAAa,kBAAkB,OAAQ;IACrC;IACrB,qBAAqB,uBAAuB,UAAU;IACtD;GACE;;;AAGV,OAAO,cAAc"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TabsBase.d.ts","names":[],"sources":["../../src/components/Tabs/TabsBase.tsx"],"mappings":";;;;;;
|
|
1
|
+
{"version":3,"file":"TabsBase.d.ts","names":[],"sources":["../../src/components/Tabs/TabsBase.tsx"],"mappings":";;;;;;UAKiB,gBAAA;EACf,cAAA;EACA,MAAA;EACA,GAAA;EACA,QAAA;EACA,aAAA,GAAgB,KAAA;EAChB,WAAA;EACA,SAAA,GAAY,SAAA;EACZ,KAAA;AAAA;AAAA,cAGW,WAAA,EAAW,OAAA,CAAA,OAAA,CAAA,gBAAA;AAAA,cAEX,cAAA,GAAc,aAAA,cAAqC,gBAAA;AAAA,UAQ/C,aAAA,SAAsB,IAAA,CAAK,wBAAA;EAC1C,aAAA,IAAiB,KAAA;EACjB,cAAA;EACA,YAAA;EACA,QAAA,EAAU,SAAA;EACV,GAAA;EACA,QAAA;EACA,WAAA;EACA,SAAA,GAAY,SAAA;EACZ,KAAA;AAAA;AAAA,cAGW,QAAA;EAAQ,GAAA;EAAA,cAAA;EAAA,QAAA;EAAA,YAAA;EAAA,GAAA;EAAA,QAAA;EAAA,aAAA;EAAA,WAAA;EAAA,SAAA;EAAA,KAAA,EAAA,eAAA;EAAA,GAAA;AAAA,GAYlB,aAAA;EACD,GAAA,GAAM,GAAA,CAAI,WAAA;AAAA,MACX,oBAAA,CAAA,GAAA,CAAA,OAAA;AAAA,UA6CgB,aAAA,SAAsB,IAAA,CAAK,wBAAA;EAC1C,QAAA,GAAW,SAAA;EACX,IAAA;EACA,SAAA,GAAY,SAAA;AAAA;AAAA,cAGD,QAAA;EAAQ,GAAA;EAAA,QAAA;EAAA,SAAA;EAAA,IAAA;EAAA,SAAA;EAAA,GAAA;AAAA,GAOlB,aAAA;EACD,GAAA,GAAM,GAAA,CAAI,WAAA;AAAA,MACX,oBAAA,CAAA,GAAA,CAAA,OAAA;AAAA,UA2IgB,gBAAA,SAAyB,wBAAA;EACxC,KAAA;EACA,QAAA;AAAA;AAAA,cAGW,WAAA;EAAW,GAAA;EAAA,SAAA;EAAA,KAAA,EAAA,YAAA;EAAA,OAAA;EAAA,SAAA;EAAA,QAAA,EAAA,eAAA;EAAA,GAAA;AAAA,GAQrB,gBAAA;EACD,GAAA,GAAM,GAAA,CAAI,WAAA;AAAA,MACX,oBAAA,CAAA,GAAA,CAAA,OAAA;AAAA,UAsEgB,yBAAA;EACf,QAAA,EAAU,SAAA;EACV,SAAA;AAAA;AAAA,cAGW,oBAAA;EAAoB,GAAA;EAAA,QAAA;EAAA;AAAA,GAI9B,yBAAA;EACD,GAAA,GAAM,GAAA,CAAI,cAAA;AAAA,MACX,oBAAA,CAAA,GAAA,CAAA,OAAA;AAAA,UAmCgB,gBAAA,SAAyB,wBAAA;EACxC,KAAA;EACA,UAAA;AAAA;AAAA,cAGW,WAAA;EAAW,GAAA;EAAA,SAAA;EAAA,KAAA,EAAA,YAAA;EAAA,UAAA;EAAA,GAAA;AAAA,GAMrB,gBAAA;EACD,GAAA,GAAM,GAAA,CAAI,WAAA;AAAA,MACX,oBAAA,CAAA,GAAA,CAAA,OAAA"}
|
package/dist/Tabs/TabsBase.js
CHANGED
|
@@ -4,7 +4,6 @@ import { calculateIndicatorStyle, debounce, generateId, prefersReducedMotion } f
|
|
|
4
4
|
import { createContext, useCallback, useContext, useEffect, useId, useMemo, useRef, useState } from "react";
|
|
5
5
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
6
6
|
import { AnimatePresence, motion } from "motion/react";
|
|
7
|
-
import useMeasure from "react-use-measure";
|
|
8
7
|
|
|
9
8
|
//#region src/components/Tabs/TabsBase.tsx
|
|
10
9
|
const TabsContext = createContext(null);
|
|
@@ -53,7 +52,6 @@ const TabsList = function TabsList({ ref, children, className, loop = true, righ
|
|
|
53
52
|
const { orientation, disabled, value } = useTabsContext("TabsList");
|
|
54
53
|
const [indicatorStyle, setIndicatorStyle] = useState(null);
|
|
55
54
|
const tabsListRef = useRef(null);
|
|
56
|
-
const reducedMotion = prefersReducedMotion();
|
|
57
55
|
const calculatePosition = useCallback(() => {
|
|
58
56
|
const newStyle = calculateIndicatorStyle(tabsListRef);
|
|
59
57
|
if (newStyle.width > 0 || newStyle.height > 0) setIndicatorStyle(newStyle);
|
|
@@ -128,42 +126,15 @@ const TabsList = function TabsList({ ref, children, className, loop = true, righ
|
|
|
128
126
|
className: "z-10 flex items-center",
|
|
129
127
|
children: rightSlot
|
|
130
128
|
})]
|
|
131
|
-
}), indicatorStyle && /* @__PURE__ */ jsx(
|
|
132
|
-
className: cn("top-0 left-0 pointer-events-none absolute
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
129
|
+
}), indicatorStyle && /* @__PURE__ */ jsx("div", {
|
|
130
|
+
className: cn("top-0 left-0 pointer-events-none absolute select-none", indicatorStyle.orientation === "horizontal" && !indicatorStyle.isEnclosed && `after:bottom-0 after:left-0 bottom-px z-10 after:absolute after:w-full after:rounded-t-[0.3rem] after:border-b-[0.3rem] after:border-tabs-indicator after:content-['']`, indicatorStyle.orientation === "vertical" && !indicatorStyle.isEnclosed && `after:right-0 after:top-0 after:w-1 right-px z-10 after:absolute after:h-full after:rounded-l-[0.3rem] after:border-r-[0.3rem] after:border-tabs-indicator after:content-['']`, indicatorStyle.orientation === "horizontal" && indicatorStyle.isEnclosed && "rounded-lg shadow-lg z-0 bg-tabs-enclosed-indicator", indicatorStyle.orientation === "vertical" && indicatorStyle.isEnclosed && "rounded-lg shadow-lg z-0 bg-tabs-enclosed-indicator"),
|
|
131
|
+
"aria-hidden": "true",
|
|
132
|
+
style: {
|
|
133
|
+
left: indicatorStyle.left,
|
|
134
|
+
top: indicatorStyle.isEnclosed ? indicatorStyle.top + indicatorStyle.height * .1 : indicatorStyle.top,
|
|
137
135
|
width: indicatorStyle.width,
|
|
138
136
|
height: indicatorStyle.isEnclosed ? indicatorStyle.orientation === "vertical" ? 40 : indicatorStyle.height * .8 : indicatorStyle.height
|
|
139
|
-
}
|
|
140
|
-
transition: reducedMotion ? { duration: 0 } : {
|
|
141
|
-
x: {
|
|
142
|
-
type: "spring",
|
|
143
|
-
stiffness: 500,
|
|
144
|
-
damping: 40,
|
|
145
|
-
mass: 1
|
|
146
|
-
},
|
|
147
|
-
y: {
|
|
148
|
-
type: "spring",
|
|
149
|
-
stiffness: 500,
|
|
150
|
-
damping: 40,
|
|
151
|
-
mass: 1
|
|
152
|
-
},
|
|
153
|
-
width: {
|
|
154
|
-
type: "spring",
|
|
155
|
-
stiffness: 500,
|
|
156
|
-
damping: 40,
|
|
157
|
-
mass: 1
|
|
158
|
-
},
|
|
159
|
-
height: {
|
|
160
|
-
type: "spring",
|
|
161
|
-
stiffness: 500,
|
|
162
|
-
damping: 40,
|
|
163
|
-
mass: 1
|
|
164
|
-
}
|
|
165
|
-
},
|
|
166
|
-
"aria-hidden": "true"
|
|
137
|
+
}
|
|
167
138
|
})]
|
|
168
139
|
});
|
|
169
140
|
};
|
|
@@ -235,27 +206,15 @@ const TabsTrigger = function TabsTrigger({ ref, className, value: triggerValue,
|
|
|
235
206
|
};
|
|
236
207
|
const TabsContentContainer = function TabsContentContainer({ ref, children, className }) {
|
|
237
208
|
const { value } = useTabsContext("TabsContentContainer");
|
|
238
|
-
const [measureRef, bounds] = useMeasure();
|
|
239
209
|
const reducedMotion = prefersReducedMotion();
|
|
240
|
-
return /* @__PURE__ */ jsx(
|
|
210
|
+
return /* @__PURE__ */ jsx("div", {
|
|
241
211
|
ref,
|
|
242
212
|
className: cn("overflow-hidden", className),
|
|
243
213
|
"data-testid": "spectral-tabs-content-container",
|
|
244
|
-
animate: { height: bounds.height || "auto" },
|
|
245
|
-
transition: reducedMotion ? { duration: 0 } : {
|
|
246
|
-
duration: .3,
|
|
247
|
-
ease: [
|
|
248
|
-
.25,
|
|
249
|
-
1,
|
|
250
|
-
.5,
|
|
251
|
-
1
|
|
252
|
-
]
|
|
253
|
-
},
|
|
254
214
|
children: /* @__PURE__ */ jsx(AnimatePresence, {
|
|
255
215
|
mode: "wait",
|
|
256
216
|
initial: false,
|
|
257
217
|
children: /* @__PURE__ */ jsx(motion.div, {
|
|
258
|
-
ref: measureRef,
|
|
259
218
|
initial: reducedMotion ? false : {
|
|
260
219
|
opacity: 0,
|
|
261
220
|
y: 8
|
|
@@ -270,12 +229,7 @@ const TabsContentContainer = function TabsContentContainer({ ref, children, clas
|
|
|
270
229
|
},
|
|
271
230
|
transition: reducedMotion ? { duration: 0 } : {
|
|
272
231
|
duration: .2,
|
|
273
|
-
ease:
|
|
274
|
-
.25,
|
|
275
|
-
1,
|
|
276
|
-
.5,
|
|
277
|
-
1
|
|
278
|
-
]
|
|
232
|
+
ease: "easeOut"
|
|
279
233
|
},
|
|
280
234
|
children
|
|
281
235
|
}, value)
|
|
@@ -290,7 +244,7 @@ const TabsContent = function TabsContent({ ref, className, value: contentValue,
|
|
|
290
244
|
if (!forceMount && !isActive) return null;
|
|
291
245
|
return /* @__PURE__ */ jsx("div", {
|
|
292
246
|
"aria-labelledby": triggerId,
|
|
293
|
-
className: cn("tabs-content focus:outline-
|
|
247
|
+
className: cn("tabs-content focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-accent", className),
|
|
294
248
|
"data-state": isActive ? "active" : "inactive",
|
|
295
249
|
"data-testid": "spectral-tabs-content",
|
|
296
250
|
hidden: !isActive,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TabsBase.js","names":[],"sources":["../../src/components/Tabs/TabsBase.tsx"],"sourcesContent":["import { cn } from '@utils/twUtils'\nimport { AnimatePresence, motion } from 'motion/react'\nimport { createContext, useCallback, useContext, useEffect, useId, useMemo, useRef, useState, type ComponentPropsWithoutRef, type KeyboardEvent, type MouseEvent, type ReactNode, type Ref } from 'react'\nimport useMeasure from 'react-use-measure'\nimport { calculateIndicatorStyle, debounce, generateId, prefersReducedMotion, type IndicatorStyle } from './tabsUtils'\n\nexport interface TabsContextValue {\n activationMode: 'automatic' | 'manual'\n baseId: string\n dir: 'ltr' | 'rtl'\n disabled: boolean\n onValueChange: (value: string) => void\n orientation: 'horizontal' | 'vertical'\n rightSlot?: ReactNode | undefined\n value: string\n}\n\nexport const TabsContext = createContext<TabsContextValue | null>(null)\n\nexport const useTabsContext = (componentName: string = 'Tabs'): TabsContextValue => {\n const context = useContext(TabsContext)\n if (context === null) {\n throw new Error(`${componentName} components must be used within a Tabs.Root`)\n }\n return context\n}\n\nexport interface TabsBaseProps extends Omit<ComponentPropsWithoutRef<'div'>, 'dir' | 'defaultValue'> {\n onValueChange?: (value: string) => void\n activationMode?: 'automatic' | 'manual'\n defaultValue?: string\n children: ReactNode\n dir?: 'ltr' | 'rtl'\n disabled?: boolean\n orientation?: 'horizontal' | 'vertical'\n rightSlot?: ReactNode | undefined\n value?: string\n}\n\nexport const TabsBase = function Tabs({\n ref,\n activationMode = 'automatic',\n children,\n defaultValue,\n dir = 'ltr',\n disabled = false,\n onValueChange,\n orientation = 'horizontal',\n rightSlot,\n value: controlledValue,\n ...props\n}: TabsBaseProps & {\n ref?: Ref<HTMLElement>\n}) {\n const [uncontrolledValue, setUncontrolledValue] = useState<string>(defaultValue ?? '')\n const baseId = useId() ?? generateId('tabs')\n const isControlled = controlledValue !== undefined\n const value = isControlled ? controlledValue : uncontrolledValue\n\n const handleValueChange = useCallback(\n (newValue: string): void => {\n if (disabled) return\n\n if (!isControlled) {\n setUncontrolledValue(newValue)\n }\n onValueChange?.(newValue)\n },\n [isControlled, onValueChange, disabled],\n )\n\n const contextValue = {\n activationMode,\n baseId,\n dir,\n disabled,\n onValueChange: handleValueChange,\n orientation,\n rightSlot,\n value,\n } as const satisfies TabsContextValue\n\n return (\n <TabsContext.Provider value={contextValue}>\n <div\n ref={ref as Ref<HTMLDivElement>}\n data-testid='spectral-tabs'\n data-orientation={orientation}\n data-disabled={disabled ? '' : undefined}\n dir={dir}\n {...props}\n >\n {children}\n </div>\n </TabsContext.Provider>\n )\n}\n\nexport interface TabsListProps extends Omit<ComponentPropsWithoutRef<'div'>, 'children'> {\n children?: ReactNode\n loop?: boolean\n rightSlot?: ReactNode | undefined\n}\n\nexport const TabsList = function TabsList({\n ref,\n children,\n className,\n loop = true,\n rightSlot,\n ...props\n}: TabsListProps & {\n ref?: Ref<HTMLElement>\n}) {\n const { orientation, disabled, value } = useTabsContext('TabsList')\n const [indicatorStyle, setIndicatorStyle] = useState<IndicatorStyle | null>(null)\n const tabsListRef = useRef<HTMLDivElement>(null)\n const reducedMotion = prefersReducedMotion()\n\n // Calculate indicator position\n const calculatePosition = useCallback(() => {\n const newStyle = calculateIndicatorStyle(tabsListRef)\n // Only update if we have a valid position (width/height > 0)\n if (newStyle.width > 0 || newStyle.height > 0) {\n setIndicatorStyle(newStyle)\n }\n }, [])\n\n // Debounced version for resize/mutation observers\n const debouncedCalculatePosition = useMemo(() => debounce(calculatePosition, 16), [calculatePosition])\n\n // Initial calculation and observer setup\n useEffect(() => {\n // Immediate calculation on mount\n calculatePosition()\n\n // Only set up observers if browser APIs are available\n if (typeof ResizeObserver === 'undefined' || typeof MutationObserver === 'undefined') {\n return\n }\n\n const resizeObserver = new ResizeObserver(debouncedCalculatePosition)\n const mutationObserver = new MutationObserver(debouncedCalculatePosition)\n\n const currentRef = tabsListRef.current\n if (currentRef) {\n resizeObserver.observe(currentRef)\n mutationObserver.observe(currentRef, {\n attributes: true,\n childList: true,\n subtree: true,\n })\n }\n\n return () => {\n resizeObserver.disconnect()\n mutationObserver.disconnect()\n debouncedCalculatePosition.cancel()\n }\n }, [calculatePosition, debouncedCalculatePosition])\n\n // Recalculate when value changes\n useEffect(() => {\n calculatePosition()\n }, [value, calculatePosition])\n\n const handleKeyDown = useCallback(\n (event: KeyboardEvent<HTMLDivElement>): void => {\n if (disabled) return\n\n const triggers = Array.from(tabsListRef.current?.querySelectorAll<HTMLElement>(`[role='tab']:not([disabled]):not([aria-disabled='true'])`) ?? [])\n\n if (triggers.length === 0) return\n\n const currentIndex = triggers.findIndex((trigger) => trigger === document.activeElement)\n if (currentIndex === -1) return\n\n let nextIndex: number | null = null\n\n const { key, ctrlKey, metaKey } = event\n const isModified = ctrlKey || metaKey\n\n if (orientation === 'horizontal') {\n if (key === 'ArrowLeft' || (key === 'ArrowUp' && isModified)) {\n nextIndex = currentIndex > 0 ? currentIndex - 1 : loop ? triggers.length - 1 : currentIndex\n } else if (key === 'ArrowRight' || (key === 'ArrowDown' && isModified)) {\n nextIndex = currentIndex < triggers.length - 1 ? currentIndex + 1 : loop ? 0 : currentIndex\n }\n } else {\n if (key === 'ArrowUp' || (key === 'ArrowLeft' && isModified)) {\n nextIndex = currentIndex > 0 ? currentIndex - 1 : loop ? triggers.length - 1 : currentIndex\n } else if (key === 'ArrowDown' || (key === 'ArrowRight' && isModified)) {\n nextIndex = currentIndex < triggers.length - 1 ? currentIndex + 1 : loop ? 0 : currentIndex\n }\n }\n\n if (key === 'Home') {\n nextIndex = 0\n } else if (key === 'End') {\n nextIndex = triggers.length - 1\n }\n\n if (nextIndex !== null && nextIndex !== currentIndex) {\n event.preventDefault()\n event.stopPropagation()\n ;(triggers[nextIndex] as HTMLButtonElement)?.focus()\n }\n },\n [disabled, orientation, loop],\n )\n\n return (\n <div\n className='relative'\n ref={tabsListRef}\n >\n <div\n ref={ref as Ref<HTMLDivElement>}\n role='tablist'\n tabIndex={-1}\n data-testid='spectral-tabs-list'\n aria-orientation={orientation}\n aria-disabled={disabled}\n className={cn('tabs-list flex', orientation === 'horizontal' && 'items-center justify-between', orientation === 'vertical' && 'flex-col', className)}\n onKeyDown={handleKeyDown}\n {...props}\n >\n <div className={cn('flex', orientation === 'horizontal' && 'items-center', orientation === 'vertical' && 'flex-col')}>{children}</div>\n {rightSlot && <div className='z-10 flex items-center'>{rightSlot}</div>}\n </div>\n {/* Animated indicator - only rendered after first measurement */}\n {indicatorStyle && (\n <motion.div\n className={cn(\n 'top-0 left-0 pointer-events-none absolute will-change-transform select-none',\n indicatorStyle.orientation === 'horizontal' && !indicatorStyle.isEnclosed && `after:bottom-0 after:left-0 bottom-px z-10 after:absolute after:w-full after:rounded-t-[0.3rem] after:border-b-[0.3rem] after:border-tabs-indicator after:content-['']`,\n indicatorStyle.orientation === 'vertical' && !indicatorStyle.isEnclosed && `after:right-0 after:top-0 after:w-1 right-px z-10 after:absolute after:h-full after:rounded-l-[0.3rem] after:border-r-[0.3rem] after:border-tabs-indicator after:content-['']`,\n indicatorStyle.orientation === 'horizontal' && indicatorStyle.isEnclosed && 'rounded-lg shadow-lg z-0 bg-tabs-enclosed-indicator',\n indicatorStyle.orientation === 'vertical' && indicatorStyle.isEnclosed && 'rounded-lg shadow-lg z-0 bg-tabs-enclosed-indicator',\n )}\n // Don't animate on initial mount - prevents sliding from (0,0)\n initial={false}\n animate={{\n // Use x/y (translateX/translateY) for position - compositor-friendly, no reflows\n x: indicatorStyle.left,\n y: indicatorStyle.isEnclosed ? indicatorStyle.top + indicatorStyle.height * 0.1 : indicatorStyle.top,\n // Width/height required for correct rendering: scaleX/Y would distort\n // border-radius on enclosed variant and border widths on default variant\n width: indicatorStyle.width,\n height: indicatorStyle.isEnclosed ? (indicatorStyle.orientation === 'vertical' ? 40 : indicatorStyle.height * 0.8) : indicatorStyle.height,\n }}\n transition={\n reducedMotion\n ? { duration: 0 }\n : {\n // Separate transition configs: spring for transforms, tween for layout\n x: { type: 'spring', stiffness: 500, damping: 40, mass: 1 },\n y: { type: 'spring', stiffness: 500, damping: 40, mass: 1 },\n width: { type: 'spring', stiffness: 500, damping: 40, mass: 1 },\n height: { type: 'spring', stiffness: 500, damping: 40, mass: 1 },\n }\n }\n aria-hidden='true'\n />\n )}\n </div>\n )\n}\n\nexport interface TabsTriggerProps extends ComponentPropsWithoutRef<'button'> {\n value: string\n disabled?: boolean\n}\n\nexport const TabsTrigger = function TabsTrigger({\n ref,\n className,\n value: triggerValue,\n onClick,\n onKeyDown,\n disabled: triggerDisabled = false,\n ...props\n}: TabsTriggerProps & {\n ref?: Ref<HTMLElement>\n}) {\n const { value, onValueChange, activationMode, disabled: contextDisabled, baseId } = useTabsContext('TabsTrigger')\n\n const triggerId = `${baseId}-trigger-${triggerValue}`\n const contentId = `${baseId}-content-${triggerValue}`\n const isActive = value === triggerValue\n const isDisabled = contextDisabled || triggerDisabled\n\n const handleClick = useCallback(\n (event: MouseEvent<HTMLButtonElement>): void => {\n if (isDisabled) {\n event.preventDefault()\n return\n }\n onValueChange(triggerValue)\n onClick?.(event)\n },\n [triggerValue, onValueChange, onClick, isDisabled],\n )\n\n const handleKeyDown = useCallback(\n (event: KeyboardEvent<HTMLButtonElement>): void => {\n if (isDisabled) {\n event.preventDefault()\n return\n }\n\n const { key } = event\n\n if (key === 'Enter' || key === ' ') {\n event.preventDefault()\n event.stopPropagation()\n onValueChange(triggerValue)\n }\n\n onKeyDown?.(event)\n },\n [triggerValue, onValueChange, onKeyDown, isDisabled],\n )\n\n const handleFocus = useCallback((): void => {\n if (isDisabled) return\n if (activationMode === 'automatic') {\n onValueChange(triggerValue)\n }\n }, [activationMode, onValueChange, triggerValue, isDisabled])\n\n return (\n <button\n aria-controls={contentId}\n aria-disabled={isDisabled}\n aria-selected={isActive}\n className={cn('tabs-trigger z-10 hover:cursor-pointer focus:outline-none focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-accent', className)}\n data-disabled={isDisabled ? '' : undefined}\n data-state={isActive ? 'active' : 'inactive'}\n data-testid='spectral-tabs-trigger'\n disabled={isDisabled}\n id={triggerId}\n onClick={handleClick}\n onFocus={handleFocus}\n onKeyDown={handleKeyDown}\n ref={ref as Ref<HTMLButtonElement>}\n role='tab'\n tabIndex={isActive ? 0 : -1}\n type='button'\n {...props}\n />\n )\n}\n\nexport interface TabsContentContainerProps {\n children: ReactNode\n className?: string\n}\n\nexport const TabsContentContainer = function TabsContentContainer({\n ref,\n children,\n className,\n}: TabsContentContainerProps & {\n ref?: Ref<HTMLDivElement>\n}) {\n const { value } = useTabsContext('TabsContentContainer')\n const [measureRef, bounds] = useMeasure()\n const reducedMotion = prefersReducedMotion()\n\n return (\n <motion.div\n ref={ref}\n className={cn('overflow-hidden', className)}\n data-testid='spectral-tabs-content-container'\n animate={{\n height: bounds.height || 'auto',\n }}\n transition={\n reducedMotion\n ? { duration: 0 }\n : {\n duration: 0.3,\n ease: [0.25, 1, 0.5, 1],\n }\n }\n >\n <AnimatePresence\n mode='wait'\n initial={false}\n >\n <motion.div\n key={value}\n ref={measureRef}\n initial={reducedMotion ? false : { opacity: 0, y: 8 }}\n animate={{ opacity: 1, y: 0 }}\n exit={reducedMotion ? undefined : { opacity: 0, y: -8 }}\n transition={\n reducedMotion\n ? { duration: 0 }\n : {\n duration: 0.2,\n ease: [0.25, 1, 0.5, 1],\n }\n }\n >\n {children}\n </motion.div>\n </AnimatePresence>\n </motion.div>\n )\n}\n\nexport interface TabsContentProps extends ComponentPropsWithoutRef<'div'> {\n value: string\n forceMount?: boolean\n}\n\nexport const TabsContent = function TabsContent({\n ref,\n className,\n value: contentValue,\n forceMount = false,\n ...props\n}: TabsContentProps & {\n ref?: Ref<HTMLElement>\n}) {\n const { value, baseId } = useTabsContext('TabsContent')\n const contentId = `${baseId}-content-${contentValue}`\n const triggerId = `${baseId}-trigger-${contentValue}`\n const isActive = value === contentValue\n\n if (!forceMount && !isActive) {\n return null\n }\n\n return (\n <div\n aria-labelledby={triggerId}\n className={cn('tabs-content focus:outline-none focus-visible:outline-none', className)}\n data-state={isActive ? 'active' : 'inactive'}\n data-testid='spectral-tabs-content'\n hidden={!isActive}\n id={contentId}\n ref={ref as Ref<HTMLDivElement>}\n role='tabpanel'\n tabIndex={0}\n {...props}\n />\n )\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"TabsBase.js","names":[],"sources":["../../src/components/Tabs/TabsBase.tsx"],"sourcesContent":["import { cn } from '@utils/twUtils'\nimport { AnimatePresence, motion } from 'motion/react'\nimport { createContext, useCallback, useContext, useEffect, useId, useMemo, useRef, useState, type ComponentPropsWithoutRef, type KeyboardEvent, type MouseEvent, type ReactNode, type Ref } from 'react'\nimport { calculateIndicatorStyle, debounce, generateId, prefersReducedMotion, type IndicatorStyle } from './tabsUtils'\n\nexport interface TabsContextValue {\n activationMode: 'automatic' | 'manual'\n baseId: string\n dir: 'ltr' | 'rtl'\n disabled: boolean\n onValueChange: (value: string) => void\n orientation: 'horizontal' | 'vertical'\n rightSlot?: ReactNode | undefined\n value: string\n}\n\nexport const TabsContext = createContext<TabsContextValue | null>(null)\n\nexport const useTabsContext = (componentName: string = 'Tabs'): TabsContextValue => {\n const context = useContext(TabsContext)\n if (context === null) {\n throw new Error(`${componentName} components must be used within a Tabs.Root`)\n }\n return context\n}\n\nexport interface TabsBaseProps extends Omit<ComponentPropsWithoutRef<'div'>, 'dir' | 'defaultValue'> {\n onValueChange?: (value: string) => void\n activationMode?: 'automatic' | 'manual'\n defaultValue?: string\n children: ReactNode\n dir?: 'ltr' | 'rtl'\n disabled?: boolean\n orientation?: 'horizontal' | 'vertical'\n rightSlot?: ReactNode | undefined\n value?: string\n}\n\nexport const TabsBase = function Tabs({\n ref,\n activationMode = 'automatic',\n children,\n defaultValue,\n dir = 'ltr',\n disabled = false,\n onValueChange,\n orientation = 'horizontal',\n rightSlot,\n value: controlledValue,\n ...props\n}: TabsBaseProps & {\n ref?: Ref<HTMLElement>\n}) {\n const [uncontrolledValue, setUncontrolledValue] = useState<string>(defaultValue ?? '')\n const baseId = useId() ?? generateId('tabs')\n const isControlled = controlledValue !== undefined\n const value = isControlled ? controlledValue : uncontrolledValue\n\n const handleValueChange = useCallback(\n (newValue: string): void => {\n if (disabled) return\n\n if (!isControlled) {\n setUncontrolledValue(newValue)\n }\n onValueChange?.(newValue)\n },\n [isControlled, onValueChange, disabled],\n )\n\n const contextValue = {\n activationMode,\n baseId,\n dir,\n disabled,\n onValueChange: handleValueChange,\n orientation,\n rightSlot,\n value,\n } as const satisfies TabsContextValue\n\n return (\n <TabsContext.Provider value={contextValue}>\n <div\n ref={ref as Ref<HTMLDivElement>}\n data-testid='spectral-tabs'\n data-orientation={orientation}\n data-disabled={disabled ? '' : undefined}\n dir={dir}\n {...props}\n >\n {children}\n </div>\n </TabsContext.Provider>\n )\n}\n\nexport interface TabsListProps extends Omit<ComponentPropsWithoutRef<'div'>, 'children'> {\n children?: ReactNode\n loop?: boolean\n rightSlot?: ReactNode | undefined\n}\n\nexport const TabsList = function TabsList({\n ref,\n children,\n className,\n loop = true,\n rightSlot,\n ...props\n}: TabsListProps & {\n ref?: Ref<HTMLElement>\n}) {\n const { orientation, disabled, value } = useTabsContext('TabsList')\n const [indicatorStyle, setIndicatorStyle] = useState<IndicatorStyle | null>(null)\n const tabsListRef = useRef<HTMLDivElement>(null)\n\n // Calculate indicator position\n const calculatePosition = useCallback(() => {\n const newStyle = calculateIndicatorStyle(tabsListRef)\n // Only update if we have a valid position (width/height > 0)\n if (newStyle.width > 0 || newStyle.height > 0) {\n setIndicatorStyle(newStyle)\n }\n }, [])\n\n // Debounced version for resize/mutation observers\n const debouncedCalculatePosition = useMemo(() => debounce(calculatePosition, 16), [calculatePosition])\n\n // Initial calculation and observer setup\n useEffect(() => {\n // Immediate calculation on mount\n calculatePosition()\n\n // Only set up observers if browser APIs are available\n if (typeof ResizeObserver === 'undefined' || typeof MutationObserver === 'undefined') {\n return\n }\n\n const resizeObserver = new ResizeObserver(debouncedCalculatePosition)\n const mutationObserver = new MutationObserver(debouncedCalculatePosition)\n\n const currentRef = tabsListRef.current\n if (currentRef) {\n resizeObserver.observe(currentRef)\n mutationObserver.observe(currentRef, {\n attributes: true,\n childList: true,\n subtree: true,\n })\n }\n\n return () => {\n resizeObserver.disconnect()\n mutationObserver.disconnect()\n debouncedCalculatePosition.cancel()\n }\n }, [calculatePosition, debouncedCalculatePosition])\n\n // Recalculate when value changes\n useEffect(() => {\n calculatePosition()\n }, [value, calculatePosition])\n\n const handleKeyDown = useCallback(\n (event: KeyboardEvent<HTMLDivElement>): void => {\n if (disabled) return\n\n const triggers = Array.from(tabsListRef.current?.querySelectorAll<HTMLElement>(`[role='tab']:not([disabled]):not([aria-disabled='true'])`) ?? [])\n\n if (triggers.length === 0) return\n\n const currentIndex = triggers.findIndex((trigger) => trigger === document.activeElement)\n if (currentIndex === -1) return\n\n let nextIndex: number | null = null\n\n const { key, ctrlKey, metaKey } = event\n const isModified = ctrlKey || metaKey\n\n if (orientation === 'horizontal') {\n if (key === 'ArrowLeft' || (key === 'ArrowUp' && isModified)) {\n nextIndex = currentIndex > 0 ? currentIndex - 1 : loop ? triggers.length - 1 : currentIndex\n } else if (key === 'ArrowRight' || (key === 'ArrowDown' && isModified)) {\n nextIndex = currentIndex < triggers.length - 1 ? currentIndex + 1 : loop ? 0 : currentIndex\n }\n } else {\n if (key === 'ArrowUp' || (key === 'ArrowLeft' && isModified)) {\n nextIndex = currentIndex > 0 ? currentIndex - 1 : loop ? triggers.length - 1 : currentIndex\n } else if (key === 'ArrowDown' || (key === 'ArrowRight' && isModified)) {\n nextIndex = currentIndex < triggers.length - 1 ? currentIndex + 1 : loop ? 0 : currentIndex\n }\n }\n\n if (key === 'Home') {\n nextIndex = 0\n } else if (key === 'End') {\n nextIndex = triggers.length - 1\n }\n\n if (nextIndex !== null && nextIndex !== currentIndex) {\n event.preventDefault()\n event.stopPropagation()\n ;(triggers[nextIndex] as HTMLButtonElement)?.focus()\n }\n },\n [disabled, orientation, loop],\n )\n\n return (\n <div\n className='relative'\n ref={tabsListRef}\n >\n <div\n ref={ref as Ref<HTMLDivElement>}\n role='tablist'\n tabIndex={-1}\n data-testid='spectral-tabs-list'\n aria-orientation={orientation}\n aria-disabled={disabled}\n className={cn('tabs-list flex', orientation === 'horizontal' && 'items-center justify-between', orientation === 'vertical' && 'flex-col', className)}\n onKeyDown={handleKeyDown}\n {...props}\n >\n <div className={cn('flex', orientation === 'horizontal' && 'items-center', orientation === 'vertical' && 'flex-col')}>{children}</div>\n {rightSlot && <div className='z-10 flex items-center'>{rightSlot}</div>}\n </div>\n {/* Animated indicator - only rendered after first measurement */}\n {indicatorStyle && (\n <div\n className={cn(\n 'top-0 left-0 pointer-events-none absolute select-none',\n indicatorStyle.orientation === 'horizontal' && !indicatorStyle.isEnclosed && `after:bottom-0 after:left-0 bottom-px z-10 after:absolute after:w-full after:rounded-t-[0.3rem] after:border-b-[0.3rem] after:border-tabs-indicator after:content-['']`,\n indicatorStyle.orientation === 'vertical' && !indicatorStyle.isEnclosed && `after:right-0 after:top-0 after:w-1 right-px z-10 after:absolute after:h-full after:rounded-l-[0.3rem] after:border-r-[0.3rem] after:border-tabs-indicator after:content-['']`,\n indicatorStyle.orientation === 'horizontal' && indicatorStyle.isEnclosed && 'rounded-lg shadow-lg z-0 bg-tabs-enclosed-indicator',\n indicatorStyle.orientation === 'vertical' && indicatorStyle.isEnclosed && 'rounded-lg shadow-lg z-0 bg-tabs-enclosed-indicator',\n )}\n aria-hidden='true'\n style={{\n left: indicatorStyle.left,\n top: indicatorStyle.isEnclosed ? indicatorStyle.top + indicatorStyle.height * 0.1 : indicatorStyle.top,\n width: indicatorStyle.width,\n height: indicatorStyle.isEnclosed ? (indicatorStyle.orientation === 'vertical' ? 40 : indicatorStyle.height * 0.8) : indicatorStyle.height,\n }}\n />\n )}\n </div>\n )\n}\n\nexport interface TabsTriggerProps extends ComponentPropsWithoutRef<'button'> {\n value: string\n disabled?: boolean\n}\n\nexport const TabsTrigger = function TabsTrigger({\n ref,\n className,\n value: triggerValue,\n onClick,\n onKeyDown,\n disabled: triggerDisabled = false,\n ...props\n}: TabsTriggerProps & {\n ref?: Ref<HTMLElement>\n}) {\n const { value, onValueChange, activationMode, disabled: contextDisabled, baseId } = useTabsContext('TabsTrigger')\n\n const triggerId = `${baseId}-trigger-${triggerValue}`\n const contentId = `${baseId}-content-${triggerValue}`\n const isActive = value === triggerValue\n const isDisabled = contextDisabled || triggerDisabled\n\n const handleClick = useCallback(\n (event: MouseEvent<HTMLButtonElement>): void => {\n if (isDisabled) {\n event.preventDefault()\n return\n }\n onValueChange(triggerValue)\n onClick?.(event)\n },\n [triggerValue, onValueChange, onClick, isDisabled],\n )\n\n const handleKeyDown = useCallback(\n (event: KeyboardEvent<HTMLButtonElement>): void => {\n if (isDisabled) {\n event.preventDefault()\n return\n }\n\n const { key } = event\n\n if (key === 'Enter' || key === ' ') {\n event.preventDefault()\n event.stopPropagation()\n onValueChange(triggerValue)\n }\n\n onKeyDown?.(event)\n },\n [triggerValue, onValueChange, onKeyDown, isDisabled],\n )\n\n const handleFocus = useCallback((): void => {\n if (isDisabled) return\n if (activationMode === 'automatic') {\n onValueChange(triggerValue)\n }\n }, [activationMode, onValueChange, triggerValue, isDisabled])\n\n return (\n <button\n aria-controls={contentId}\n aria-disabled={isDisabled}\n aria-selected={isActive}\n className={cn('tabs-trigger z-10 hover:cursor-pointer focus:outline-none focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-accent', className)}\n data-disabled={isDisabled ? '' : undefined}\n data-state={isActive ? 'active' : 'inactive'}\n data-testid='spectral-tabs-trigger'\n disabled={isDisabled}\n id={triggerId}\n onClick={handleClick}\n onFocus={handleFocus}\n onKeyDown={handleKeyDown}\n ref={ref as Ref<HTMLButtonElement>}\n role='tab'\n tabIndex={isActive ? 0 : -1}\n type='button'\n {...props}\n />\n )\n}\n\nexport interface TabsContentContainerProps {\n children: ReactNode\n className?: string\n}\n\nexport const TabsContentContainer = function TabsContentContainer({\n ref,\n children,\n className,\n}: TabsContentContainerProps & {\n ref?: Ref<HTMLDivElement>\n}) {\n const { value } = useTabsContext('TabsContentContainer')\n const reducedMotion = prefersReducedMotion()\n\n return (\n <div\n ref={ref}\n className={cn('overflow-hidden', className)}\n data-testid='spectral-tabs-content-container'\n >\n <AnimatePresence\n mode='wait'\n initial={false}\n >\n <motion.div\n key={value}\n initial={reducedMotion ? false : { opacity: 0, y: 8 }}\n animate={{ opacity: 1, y: 0 }}\n exit={reducedMotion ? undefined : { opacity: 0, y: -8 }}\n transition={\n reducedMotion\n ? { duration: 0 }\n : {\n duration: 0.2,\n ease: 'easeOut',\n }\n }\n >\n {children}\n </motion.div>\n </AnimatePresence>\n </div>\n )\n}\n\nexport interface TabsContentProps extends ComponentPropsWithoutRef<'div'> {\n value: string\n forceMount?: boolean\n}\n\nexport const TabsContent = function TabsContent({\n ref,\n className,\n value: contentValue,\n forceMount = false,\n ...props\n}: TabsContentProps & {\n ref?: Ref<HTMLElement>\n}) {\n const { value, baseId } = useTabsContext('TabsContent')\n const contentId = `${baseId}-content-${contentValue}`\n const triggerId = `${baseId}-trigger-${contentValue}`\n const isActive = value === contentValue\n\n if (!forceMount && !isActive) {\n return null\n }\n\n return (\n <div\n aria-labelledby={triggerId}\n className={cn('tabs-content focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-accent', className)}\n data-state={isActive ? 'active' : 'inactive'}\n data-testid='spectral-tabs-content'\n hidden={!isActive}\n id={contentId}\n ref={ref as Ref<HTMLDivElement>}\n role='tabpanel'\n tabIndex={0}\n {...props}\n />\n )\n}\n"],"mappings":"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"}
|
package/dist/Tabs.js
CHANGED
|
@@ -78,7 +78,7 @@ const Tabs = ({ activationMode = "automatic", ariaLabel, className, hideContent
|
|
|
78
78
|
disabled: tab.disabled,
|
|
79
79
|
"aria-label": tab["aria-label"],
|
|
80
80
|
"aria-describedby": tab["aria-describedby"],
|
|
81
|
-
className: cn("font-semibold relative whitespace-nowrap transition-
|
|
81
|
+
className: cn("font-semibold relative whitespace-nowrap transition-colors duration-200 ease-out", orientation === "horizontal" && variant === "default" && "py-2 px-4 hover:bg-tabs-bg--hover text-text-primary hover:text-accent", orientation === "vertical" && variant === "default" && "h-10 px-4 py-2 hover:bg-tabs-bg--hover flex items-center text-text-primary hover:text-accent data-[state=active]:text-accent", orientation === "horizontal" && variant === "enclosed" && "px-4 py-2 hover:bg-tabs-bg--hover z-[1] inline-flex items-center justify-center text-text-primary hover:text-accent data-[state=active]:text-accent", orientation === "vertical" && variant === "enclosed" && "px-4 py-2 hover:bg-tabs-bg--hover z-1 text-text-primary hover:text-accent data-[state=active]:text-accent", "disabled:cursor-not-allowed disabled:opacity-50 disabled:hover:bg-transparent disabled:hover:text-text-primary"),
|
|
82
82
|
children: tab.label
|
|
83
83
|
}, tab.key))
|
|
84
84
|
}), !hideContent && /* @__PURE__ */ jsx(TabsContentContainer, {
|
package/dist/Tabs.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Tabs.js","names":[],"sources":["../src/components/Tabs/Tabs.tsx"],"sourcesContent":["import { cn } from '@utils/twUtils'\nimport { type ReactNode } from 'react'\nimport { TabsBase, TabsContent, TabsContentContainer, TabsList, TabsTrigger, type TabsBaseProps } from './TabsBase'\nimport { getTabKeys, validateTabValues } from './tabsUtils'\n\nexport interface TabValue {\n children: ReactNode\n disabled?: boolean | undefined\n key: string | undefined\n label: ReactNode\n 'aria-label'?: string | undefined\n 'aria-describedby'?: string | undefined\n}\n\nexport interface TabsProps extends Omit<TabsBaseProps, 'children' | 'defaultValue' | 'onError' | 'disabled' | 'value'> {\n activationMode?: 'automatic' | 'manual' | undefined\n ariaLabel?: string | undefined\n className?: string | undefined\n /** Hide the content panel, useful when managing content display externally */\n hideContent?: boolean | undefined\n onBeforeChange?: (newValue: string, oldValue: string) => boolean | Promise<boolean> | undefined\n onError?: ((error: Error) => void) | undefined\n loading?: boolean | undefined\n openOnLoad?: string | undefined\n rightSlot?: ReactNode | undefined\n tabValues: TabValue[] | undefined\n /**\n * Controlled value - the currently active tab key.\n * When provided, the component operates in controlled mode and the parent\n * must manage the active tab state via `onValueChange`.\n * Mutually exclusive with `openOnLoad` - use one or the other.\n */\n value?: string | undefined\n variant?: 'enclosed' | 'default'\n}\n\nexport const Tabs = ({\n activationMode = 'automatic',\n ariaLabel,\n className,\n hideContent = false,\n loading = false,\n onBeforeChange,\n onError,\n onValueChange,\n openOnLoad,\n orientation = 'horizontal',\n rightSlot,\n tabValues = [],\n value: controlledValue,\n variant = 'default',\n ...props\n}: TabsProps) => {\n if (loading) {\n return (\n <div\n className='p-4 relative flex w-full flex-col text-center text-text-secondary'\n role='status'\n aria-live='polite'\n >\n <p>Loading tabs…</p>\n </div>\n )\n }\n\n const validTabs = tabValues.filter((tab): tab is TabValue & { key: string } => tab.key !== undefined)\n const isControlled = controlledValue !== undefined\n\n if (!validateTabValues(validTabs as { key: string; label: unknown; children: unknown; [K: string]: unknown }[])) {\n const error = new Error('Invalid tabValues provided to Tabs component')\n onError?.(error)\n\n return (\n <div\n className='p-4 text-gray-500 relative flex w-full flex-col text-center'\n role='alert'\n aria-live='polite'\n >\n <p>No valid tabs provided</p>\n </div>\n )\n }\n\n const validKeys = getTabKeys(validTabs)\n\n // Compute effective value for controlled mode (fallback if invalid)\n let effectiveValue: string | undefined\n if (isControlled) {\n if (validKeys.includes(controlledValue)) {\n effectiveValue = controlledValue\n } else {\n console.warn(`Tabs: value '${controlledValue}' is not a valid tab key, falling back to first tab`)\n effectiveValue = validKeys[0]\n }\n }\n\n // For uncontrolled mode, determine the default value\n let defaultValue: string | undefined\n if (!isControlled) {\n defaultValue = openOnLoad ?? validKeys[0]\n\n if (openOnLoad && !validKeys.includes(openOnLoad)) {\n console.warn(`Tabs: openOnLoad value '${openOnLoad}' is not a valid tab key`)\n defaultValue = validKeys[0]\n }\n\n if (!defaultValue) {\n const error = new Error('No valid default tab available')\n onError?.(error)\n return (\n <div\n className='p-4 relative flex w-full flex-col text-center text-text-secondary'\n role='alert'\n aria-live='polite'\n >\n <p>Unable to render tabs</p>\n </div>\n )\n }\n }\n\n const handleValueChange = async (newValue: string) => {\n if (onBeforeChange && onValueChange) {\n const currentValue = isControlled ? (effectiveValue ?? '') : (defaultValue ?? '')\n try {\n const canChange = await onBeforeChange(newValue, currentValue)\n if (canChange === false) return\n } catch (error) {\n onError?.(error instanceof Error ? error : new Error('Before change callback failed'))\n return\n }\n }\n\n onValueChange?.(newValue)\n }\n\n return (\n <div className='relative flex w-full flex-col'>\n <TabsBase\n aria-label={ariaLabel}\n className={cn('relative w-full data-[orientation=vertical]:flex', variant, className)}\n {...(isControlled ? { value: effectiveValue } : { defaultValue })}\n orientation={orientation}\n activationMode={activationMode}\n onValueChange={handleValueChange}\n {...props}\n >\n <TabsList\n className={cn(\n 'inline-flex w-full items-center justify-between',\n orientation === 'horizontal' && variant === 'default' && 'border-b border-tabs-border',\n orientation === 'vertical' && variant === 'default' && 'm-0 py-1 relative h-auto w-auto flex-col items-stretch border-r border-tabs-border',\n orientation === 'horizontal' && variant === 'enclosed' && 'rounded-lg p-1 relative border-0 bg-tabs-group-bg',\n orientation === 'vertical' && variant === 'enclosed' && 'rounded-lg p-1 w-auto flex-col border-0 bg-tabs-group-bg',\n )}\n rightSlot={rightSlot}\n >\n {validTabs.map((tab) => (\n <TabsTrigger\n key={tab.key}\n value={tab.key}\n disabled={tab.disabled}\n aria-label={tab['aria-label']}\n aria-describedby={tab['aria-describedby']}\n className={cn(\n 'font-semibold relative whitespace-nowrap transition-all duration-500 ease-in-out focus:outline-none focus-visible:outline-none',\n orientation === 'horizontal' && variant === 'default' && 'py-2 px-4 hover:bg-tabs-bg--hover text-text-primary hover:text-accent',\n orientation === 'vertical' && variant === 'default' && 'h-10 px-4 py-2 hover:bg-tabs-bg--hover flex items-center text-text-primary hover:text-accent data-[state=active]:text-accent',\n orientation === 'horizontal' && variant === 'enclosed' && 'px-4 py-2 hover:bg-tabs-bg--hover z-[1] inline-flex items-center justify-center text-text-primary hover:text-accent data-[state=active]:text-accent',\n orientation === 'vertical' && variant === 'enclosed' && 'px-4 py-2 hover:bg-tabs-bg--hover z-1 text-text-primary hover:text-accent data-[state=active]:text-accent',\n 'disabled:cursor-not-allowed disabled:opacity-50 disabled:hover:bg-transparent disabled:hover:text-text-primary',\n )}\n >\n {tab.label}\n </TabsTrigger>\n ))}\n </TabsList>\n {!hideContent && (\n <TabsContentContainer className={cn(orientation === 'vertical' && 'flex-1')}>\n {validTabs.map((tab) => (\n <TabsContent\n key={tab.key}\n value={tab.key}\n className={cn('text-text-primary focus:outline-none focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-accent', orientation === 'horizontal' && 'pb-4', orientation === 'vertical' && 'py-2 px-4')}\n >\n {tab.children}\n </TabsContent>\n ))}\n </TabsContentContainer>\n )}\n </TabsBase>\n </div>\n )\n}\n\nexport { TabsContent, TabsContentContainer, TabsList, TabsBase as TabsRoot, TabsTrigger }\n\nexport type { TabsBaseProps, TabsContentContainerProps, TabsContentProps, TabsListProps, TabsTriggerProps } from './TabsBase'\n\nexport type TabKeys<T extends TabValue[]> = T[number]['key']\nexport type TabValueWithKey<TKey extends string> = TabValue & { key: TKey }\n"],"mappings":";;;;;;;;AAoCA,MAAa,QAAQ,EACnB,iBAAiB,aACjB,WACA,WACA,cAAc,OACd,UAAU,OACV,gBACA,SACA,eACA,YACA,cAAc,cACd,WACA,YAAY,EAAE,EACd,OAAO,iBACP,UAAU,WACV,GAAG,YACY;AACf,KAAI,QACF,QACE,oBAAC,OAAD;EACE,WAAU;EACV,MAAK;EACL,aAAU;YAEV,oBAAC,KAAD,YAAG,iBAAiB;EAChB;CAIV,MAAM,YAAY,UAAU,QAAQ,QAA2C,IAAI,QAAQ,OAAU;CACrG,MAAM,eAAe,oBAAoB;AAEzC,KAAI,CAAC,kBAAkB,UAAwF,EAAE;AAE/G,4BAAU,IADQ,MAAM,+CACT,CAAC;AAEhB,SACE,oBAAC,OAAD;GACE,WAAU;GACV,MAAK;GACL,aAAU;aAEV,oBAAC,KAAD,YAAG,0BAA0B;GACzB;;CAIV,MAAM,YAAY,WAAW,UAAU;CAGvC,IAAI;AACJ,KAAI,aACF,KAAI,UAAU,SAAS,gBAAgB,CACrC,kBAAiB;MACZ;AACL,UAAQ,KAAK,gBAAgB,gBAAgB,qDAAqD;AAClG,mBAAiB,UAAU;;CAK/B,IAAI;AACJ,KAAI,CAAC,cAAc;AACjB,iBAAe,cAAc,UAAU;AAEvC,MAAI,cAAc,CAAC,UAAU,SAAS,WAAW,EAAE;AACjD,WAAQ,KAAK,2BAA2B,WAAW,0BAA0B;AAC7E,kBAAe,UAAU;;AAG3B,MAAI,CAAC,cAAc;AAEjB,6BAAU,IADQ,MAAM,iCACT,CAAC;AAChB,UACE,oBAAC,OAAD;IACE,WAAU;IACV,MAAK;IACL,aAAU;cAEV,oBAAC,KAAD,YAAG,yBAAyB;IACxB;;;CAKZ,MAAM,oBAAoB,OAAO,aAAqB;AACpD,MAAI,kBAAkB,eAAe;GACnC,MAAM,eAAe,eAAgB,kBAAkB,KAAO,gBAAgB;AAC9E,OAAI;AAEF,QAAI,MADoB,eAAe,UAAU,aAAa,KAC5C,MAAO;YAClB,OAAO;AACd,cAAU,iBAAiB,QAAQ,wBAAQ,IAAI,MAAM,gCAAgC,CAAC;AACtF;;;AAIJ,kBAAgB,SAAS;;AAG3B,QACE,oBAAC,OAAD;EAAK,WAAU;YACb,qBAAC,UAAD;GACE,cAAY;GACZ,WAAW,GAAG,oDAAoD,SAAS,UAAU;GACrF,GAAK,eAAe,EAAE,OAAO,gBAAgB,GAAG,EAAE,cAAc;GACnD;GACG;GAChB,eAAe;GACf,GAAI;aAPN,CASE,oBAAC,UAAD;IACE,WAAW,GACT,mDACA,gBAAgB,gBAAgB,YAAY,aAAa,+BACzD,gBAAgB,cAAc,YAAY,aAAa,sFACvD,gBAAgB,gBAAgB,YAAY,cAAc,qDAC1D,gBAAgB,cAAc,YAAY,cAAc,2DACzD;IACU;cAEV,UAAU,KAAK,QACd,oBAAC,aAAD;KAEE,OAAO,IAAI;KACX,UAAU,IAAI;KACd,cAAY,IAAI;KAChB,oBAAkB,IAAI;KACtB,WAAW,GACT,kIACA,gBAAgB,gBAAgB,YAAY,aAAa,yEACzD,gBAAgB,cAAc,YAAY,aAAa,gIACvD,gBAAgB,gBAAgB,YAAY,cAAc,uJAC1D,gBAAgB,cAAc,YAAY,cAAc,6GACxD,iHACD;eAEA,IAAI;KACO,EAfP,IAAI,IAeG,CACd;IACO,GACV,CAAC,eACA,oBAAC,sBAAD;IAAsB,WAAW,GAAG,gBAAgB,cAAc,SAAS;cACxE,UAAU,KAAK,QACd,oBAAC,aAAD;KAEE,OAAO,IAAI;KACX,WAAW,GAAG,4HAA4H,gBAAgB,gBAAgB,QAAQ,gBAAgB,cAAc,YAAY;eAE3N,IAAI;KACO,EALP,IAAI,IAKG,CACd;IACmB,EAEhB;;EACP"}
|
|
1
|
+
{"version":3,"file":"Tabs.js","names":[],"sources":["../src/components/Tabs/Tabs.tsx"],"sourcesContent":["import { cn } from '@utils/twUtils'\nimport { type ReactNode } from 'react'\nimport { TabsBase, TabsContent, TabsContentContainer, TabsList, TabsTrigger, type TabsBaseProps } from './TabsBase'\nimport { getTabKeys, validateTabValues } from './tabsUtils'\n\nexport interface TabValue {\n children: ReactNode\n disabled?: boolean | undefined\n key: string | undefined\n label: ReactNode\n 'aria-label'?: string | undefined\n 'aria-describedby'?: string | undefined\n}\n\nexport interface TabsProps extends Omit<TabsBaseProps, 'children' | 'defaultValue' | 'onError' | 'disabled' | 'value'> {\n activationMode?: 'automatic' | 'manual' | undefined\n ariaLabel?: string | undefined\n className?: string | undefined\n /** Hide the content panel, useful when managing content display externally */\n hideContent?: boolean | undefined\n onBeforeChange?: (newValue: string, oldValue: string) => boolean | Promise<boolean> | undefined\n onError?: ((error: Error) => void) | undefined\n loading?: boolean | undefined\n openOnLoad?: string | undefined\n rightSlot?: ReactNode | undefined\n tabValues: TabValue[] | undefined\n /**\n * Controlled value - the currently active tab key.\n * When provided, the component operates in controlled mode and the parent\n * must manage the active tab state via `onValueChange`.\n * Mutually exclusive with `openOnLoad` - use one or the other.\n */\n value?: string | undefined\n variant?: 'enclosed' | 'default'\n}\n\nexport const Tabs = ({\n activationMode = 'automatic',\n ariaLabel,\n className,\n hideContent = false,\n loading = false,\n onBeforeChange,\n onError,\n onValueChange,\n openOnLoad,\n orientation = 'horizontal',\n rightSlot,\n tabValues = [],\n value: controlledValue,\n variant = 'default',\n ...props\n}: TabsProps) => {\n if (loading) {\n return (\n <div\n className='p-4 relative flex w-full flex-col text-center text-text-secondary'\n role='status'\n aria-live='polite'\n >\n <p>Loading tabs…</p>\n </div>\n )\n }\n\n const validTabs = tabValues.filter((tab): tab is TabValue & { key: string } => tab.key !== undefined)\n const isControlled = controlledValue !== undefined\n\n if (!validateTabValues(validTabs as { key: string; label: unknown; children: unknown; [K: string]: unknown }[])) {\n const error = new Error('Invalid tabValues provided to Tabs component')\n onError?.(error)\n\n return (\n <div\n className='p-4 text-gray-500 relative flex w-full flex-col text-center'\n role='alert'\n aria-live='polite'\n >\n <p>No valid tabs provided</p>\n </div>\n )\n }\n\n const validKeys = getTabKeys(validTabs)\n\n // Compute effective value for controlled mode (fallback if invalid)\n let effectiveValue: string | undefined\n if (isControlled) {\n if (validKeys.includes(controlledValue)) {\n effectiveValue = controlledValue\n } else {\n console.warn(`Tabs: value '${controlledValue}' is not a valid tab key, falling back to first tab`)\n effectiveValue = validKeys[0]\n }\n }\n\n // For uncontrolled mode, determine the default value\n let defaultValue: string | undefined\n if (!isControlled) {\n defaultValue = openOnLoad ?? validKeys[0]\n\n if (openOnLoad && !validKeys.includes(openOnLoad)) {\n console.warn(`Tabs: openOnLoad value '${openOnLoad}' is not a valid tab key`)\n defaultValue = validKeys[0]\n }\n\n if (!defaultValue) {\n const error = new Error('No valid default tab available')\n onError?.(error)\n return (\n <div\n className='p-4 relative flex w-full flex-col text-center text-text-secondary'\n role='alert'\n aria-live='polite'\n >\n <p>Unable to render tabs</p>\n </div>\n )\n }\n }\n\n const handleValueChange = async (newValue: string) => {\n if (onBeforeChange && onValueChange) {\n const currentValue = isControlled ? (effectiveValue ?? '') : (defaultValue ?? '')\n try {\n const canChange = await onBeforeChange(newValue, currentValue)\n if (canChange === false) return\n } catch (error) {\n onError?.(error instanceof Error ? error : new Error('Before change callback failed'))\n return\n }\n }\n\n onValueChange?.(newValue)\n }\n\n return (\n <div className='relative flex w-full flex-col'>\n <TabsBase\n aria-label={ariaLabel}\n className={cn('relative w-full data-[orientation=vertical]:flex', variant, className)}\n {...(isControlled ? { value: effectiveValue } : { defaultValue })}\n orientation={orientation}\n activationMode={activationMode}\n onValueChange={handleValueChange}\n {...props}\n >\n <TabsList\n className={cn(\n 'inline-flex w-full items-center justify-between',\n orientation === 'horizontal' && variant === 'default' && 'border-b border-tabs-border',\n orientation === 'vertical' && variant === 'default' && 'm-0 py-1 relative h-auto w-auto flex-col items-stretch border-r border-tabs-border',\n orientation === 'horizontal' && variant === 'enclosed' && 'rounded-lg p-1 relative border-0 bg-tabs-group-bg',\n orientation === 'vertical' && variant === 'enclosed' && 'rounded-lg p-1 w-auto flex-col border-0 bg-tabs-group-bg',\n )}\n rightSlot={rightSlot}\n >\n {validTabs.map((tab) => (\n <TabsTrigger\n key={tab.key}\n value={tab.key}\n disabled={tab.disabled}\n aria-label={tab['aria-label']}\n aria-describedby={tab['aria-describedby']}\n className={cn(\n 'font-semibold relative whitespace-nowrap transition-colors duration-200 ease-out',\n orientation === 'horizontal' && variant === 'default' && 'py-2 px-4 hover:bg-tabs-bg--hover text-text-primary hover:text-accent',\n orientation === 'vertical' && variant === 'default' && 'h-10 px-4 py-2 hover:bg-tabs-bg--hover flex items-center text-text-primary hover:text-accent data-[state=active]:text-accent',\n orientation === 'horizontal' && variant === 'enclosed' && 'px-4 py-2 hover:bg-tabs-bg--hover z-[1] inline-flex items-center justify-center text-text-primary hover:text-accent data-[state=active]:text-accent',\n orientation === 'vertical' && variant === 'enclosed' && 'px-4 py-2 hover:bg-tabs-bg--hover z-1 text-text-primary hover:text-accent data-[state=active]:text-accent',\n 'disabled:cursor-not-allowed disabled:opacity-50 disabled:hover:bg-transparent disabled:hover:text-text-primary',\n )}\n >\n {tab.label}\n </TabsTrigger>\n ))}\n </TabsList>\n {!hideContent && (\n <TabsContentContainer className={cn(orientation === 'vertical' && 'flex-1')}>\n {validTabs.map((tab) => (\n <TabsContent\n key={tab.key}\n value={tab.key}\n className={cn('text-text-primary focus:outline-none focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-accent', orientation === 'horizontal' && 'pb-4', orientation === 'vertical' && 'py-2 px-4')}\n >\n {tab.children}\n </TabsContent>\n ))}\n </TabsContentContainer>\n )}\n </TabsBase>\n </div>\n )\n}\n\nexport { TabsContent, TabsContentContainer, TabsList, TabsBase as TabsRoot, TabsTrigger }\n\nexport type { TabsBaseProps, TabsContentContainerProps, TabsContentProps, TabsListProps, TabsTriggerProps } from './TabsBase'\n\nexport type TabKeys<T extends TabValue[]> = T[number]['key']\nexport type TabValueWithKey<TKey extends string> = TabValue & { key: TKey }\n"],"mappings":";;;;;;;;AAoCA,MAAa,QAAQ,EACnB,iBAAiB,aACjB,WACA,WACA,cAAc,OACd,UAAU,OACV,gBACA,SACA,eACA,YACA,cAAc,cACd,WACA,YAAY,EAAE,EACd,OAAO,iBACP,UAAU,WACV,GAAG,YACY;AACf,KAAI,QACF,QACE,oBAAC,OAAD;EACE,WAAU;EACV,MAAK;EACL,aAAU;YAEV,oBAAC,KAAD,YAAG,iBAAiB;EAChB;CAIV,MAAM,YAAY,UAAU,QAAQ,QAA2C,IAAI,QAAQ,OAAU;CACrG,MAAM,eAAe,oBAAoB;AAEzC,KAAI,CAAC,kBAAkB,UAAwF,EAAE;AAE/G,4BAAU,IADQ,MAAM,+CACT,CAAC;AAEhB,SACE,oBAAC,OAAD;GACE,WAAU;GACV,MAAK;GACL,aAAU;aAEV,oBAAC,KAAD,YAAG,0BAA0B;GACzB;;CAIV,MAAM,YAAY,WAAW,UAAU;CAGvC,IAAI;AACJ,KAAI,aACF,KAAI,UAAU,SAAS,gBAAgB,CACrC,kBAAiB;MACZ;AACL,UAAQ,KAAK,gBAAgB,gBAAgB,qDAAqD;AAClG,mBAAiB,UAAU;;CAK/B,IAAI;AACJ,KAAI,CAAC,cAAc;AACjB,iBAAe,cAAc,UAAU;AAEvC,MAAI,cAAc,CAAC,UAAU,SAAS,WAAW,EAAE;AACjD,WAAQ,KAAK,2BAA2B,WAAW,0BAA0B;AAC7E,kBAAe,UAAU;;AAG3B,MAAI,CAAC,cAAc;AAEjB,6BAAU,IADQ,MAAM,iCACT,CAAC;AAChB,UACE,oBAAC,OAAD;IACE,WAAU;IACV,MAAK;IACL,aAAU;cAEV,oBAAC,KAAD,YAAG,yBAAyB;IACxB;;;CAKZ,MAAM,oBAAoB,OAAO,aAAqB;AACpD,MAAI,kBAAkB,eAAe;GACnC,MAAM,eAAe,eAAgB,kBAAkB,KAAO,gBAAgB;AAC9E,OAAI;AAEF,QAAI,MADoB,eAAe,UAAU,aAAa,KAC5C,MAAO;YAClB,OAAO;AACd,cAAU,iBAAiB,QAAQ,wBAAQ,IAAI,MAAM,gCAAgC,CAAC;AACtF;;;AAIJ,kBAAgB,SAAS;;AAG3B,QACE,oBAAC,OAAD;EAAK,WAAU;YACb,qBAAC,UAAD;GACE,cAAY;GACZ,WAAW,GAAG,oDAAoD,SAAS,UAAU;GACrF,GAAK,eAAe,EAAE,OAAO,gBAAgB,GAAG,EAAE,cAAc;GACnD;GACG;GAChB,eAAe;GACf,GAAI;aAPN,CASE,oBAAC,UAAD;IACE,WAAW,GACT,mDACA,gBAAgB,gBAAgB,YAAY,aAAa,+BACzD,gBAAgB,cAAc,YAAY,aAAa,sFACvD,gBAAgB,gBAAgB,YAAY,cAAc,qDAC1D,gBAAgB,cAAc,YAAY,cAAc,2DACzD;IACU;cAEV,UAAU,KAAK,QACd,oBAAC,aAAD;KAEE,OAAO,IAAI;KACX,UAAU,IAAI;KACd,cAAY,IAAI;KAChB,oBAAkB,IAAI;KACtB,WAAW,GACT,oFACA,gBAAgB,gBAAgB,YAAY,aAAa,yEACzD,gBAAgB,cAAc,YAAY,aAAa,gIACvD,gBAAgB,gBAAgB,YAAY,cAAc,uJAC1D,gBAAgB,cAAc,YAAY,cAAc,6GACxD,iHACD;eAEA,IAAI;KACO,EAfP,IAAI,IAeG,CACd;IACO,GACV,CAAC,eACA,oBAAC,sBAAD;IAAsB,WAAW,GAAG,gBAAgB,cAAc,SAAS;cACxE,UAAU,KAAK,QACd,oBAAC,aAAD;KAEE,OAAO,IAAI;KACX,WAAW,GAAG,4HAA4H,gBAAgB,gBAAgB,QAAQ,gBAAgB,cAAc,YAAY;eAE3N,IAAI;KACO,EALP,IAAI,IAKG,CACd;IACmB,EAEhB;;EACP"}
|
|
@@ -21,7 +21,7 @@ const ToggleGroupItem = ({ activeColor, activeTextColor, children, className, la
|
|
|
21
21
|
variant: resolvedVariant,
|
|
22
22
|
size: resolvedSize,
|
|
23
23
|
layout: resolvedLayout
|
|
24
|
-
}), "group data-[variant=separated]:rounded-md rounded-none shadow-none focus:z-10 focus-visible:z-10 data-[variant=outline]:border-toggle-border data-[variant=outline]:hover:border-toggle-border data-[variant=outline]:data-[state=on]:border-toggle-border--active data-[variant=outline]:data-[state=on]:hover:border-toggle-border--active data-[variant=separated]:border-toggle-border data-[variant=separated]:hover:border-toggle-border--hover data-[variant=separated]:data-[state=on]:border-toggle-border--active data-[variant=separated]:data-[state=on]:bg-toggle-bg--active data-[variant=separated]:data-[state=on]:hover:border-toggle-border--active data-[variant=separated]:data-[state=on]:hover:bg-toggle-bg--active", className),
|
|
24
|
+
}), "group data-[variant=separated]:rounded-md rounded-none shadow-none focus:z-10 focus-visible:z-10 data-[variant=outline]:border-toggle-border data-[variant=outline]:hover:border-toggle-border", "data-[variant=outline]:data-[state=on]:border-toggle-border--active data-[variant=outline]:data-[state=on]:hover:border-toggle-border--active data-[variant=separated]:border-toggle-border", "data-[variant=separated]:hover:border-toggle-border--hover data-[variant=separated]:data-[state=on]:border-toggle-border--active data-[variant=separated]:data-[state=on]:bg-toggle-bg--active", "data-[variant=separated]:data-[state=on]:hover:border-toggle-border--active data-[variant=separated]:data-[state=on]:hover:bg-toggle-bg--active", className),
|
|
25
25
|
"data-layout": resolvedLayout,
|
|
26
26
|
"data-size": resolvedSize,
|
|
27
27
|
"data-slot": "toggle-group-item",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ToggleGroupItem.js","names":[],"sources":["../../src/components/ToggleGroup/ToggleGroupItem.tsx"],"sourcesContent":["import { toggleVariants } from '@components/Toggle/Toggle'\nimport { getActiveColorStyle, type ActiveColor, type ActiveTextColor } from '@utils/activeColorStyle'\nimport { cn } from '@utils/twUtils'\nimport { type VariantProps } from 'class-variance-authority'\nimport { useContext, type ComponentProps, type Ref } from 'react'\nimport { ToggleGroupContext } from './ToggleGroup.context'\nimport { ToggleGroupItemBase } from './ToggleGroupBase'\n\nexport type ToggleGroupItemProps = ComponentProps<typeof ToggleGroupItemBase> &\n VariantProps<typeof toggleVariants> & {\n activeColor?: ActiveColor\n activeTextColor?: ActiveTextColor\n }\n\nexport const ToggleGroupItem = ({\n activeColor,\n activeTextColor,\n children,\n className,\n layout,\n ref,\n size,\n style,\n value,\n variant,\n ...props\n}: ToggleGroupItemProps & {\n ref?: Ref<HTMLButtonElement>\n}) => {\n const context = useContext(ToggleGroupContext)\n const resolvedVariant = variant ?? context.variant\n const resolvedSize = size ?? context.size\n const resolvedLayout = layout ?? context.layout\n const resolvedActiveColor = activeColor ?? context.activeColor\n const resolvedActiveTextColor = activeTextColor ?? context.activeTextColor\n\n return (\n <ToggleGroupItemBase\n className={cn(\n toggleVariants({\n variant: resolvedVariant,\n size: resolvedSize,\n layout: resolvedLayout,\n }),\n 'group data-[variant=separated]:rounded-md rounded-none shadow-none focus:z-10 focus-visible:z-10 data-[variant=outline]:border-toggle-border data-[variant=outline]:hover:border-toggle-border
|
|
1
|
+
{"version":3,"file":"ToggleGroupItem.js","names":[],"sources":["../../src/components/ToggleGroup/ToggleGroupItem.tsx"],"sourcesContent":["import { toggleVariants } from '@components/Toggle/Toggle'\nimport { getActiveColorStyle, type ActiveColor, type ActiveTextColor } from '@utils/activeColorStyle'\nimport { cn } from '@utils/twUtils'\nimport { type VariantProps } from 'class-variance-authority'\nimport { useContext, type ComponentProps, type Ref } from 'react'\nimport { ToggleGroupContext } from './ToggleGroup.context'\nimport { ToggleGroupItemBase } from './ToggleGroupBase'\n\nexport type ToggleGroupItemProps = ComponentProps<typeof ToggleGroupItemBase> &\n VariantProps<typeof toggleVariants> & {\n activeColor?: ActiveColor\n activeTextColor?: ActiveTextColor\n }\n\nexport const ToggleGroupItem = ({\n activeColor,\n activeTextColor,\n children,\n className,\n layout,\n ref,\n size,\n style,\n value,\n variant,\n ...props\n}: ToggleGroupItemProps & {\n ref?: Ref<HTMLButtonElement>\n}) => {\n const context = useContext(ToggleGroupContext)\n const resolvedVariant = variant ?? context.variant\n const resolvedSize = size ?? context.size\n const resolvedLayout = layout ?? context.layout\n const resolvedActiveColor = activeColor ?? context.activeColor\n const resolvedActiveTextColor = activeTextColor ?? context.activeTextColor\n\n return (\n <ToggleGroupItemBase\n className={cn(\n toggleVariants({\n variant: resolvedVariant,\n size: resolvedSize,\n layout: resolvedLayout,\n }),\n 'group data-[variant=separated]:rounded-md rounded-none shadow-none focus:z-10 focus-visible:z-10 data-[variant=outline]:border-toggle-border data-[variant=outline]:hover:border-toggle-border',\n 'data-[variant=outline]:data-[state=on]:border-toggle-border--active data-[variant=outline]:data-[state=on]:hover:border-toggle-border--active data-[variant=separated]:border-toggle-border',\n 'data-[variant=separated]:hover:border-toggle-border--hover data-[variant=separated]:data-[state=on]:border-toggle-border--active data-[variant=separated]:data-[state=on]:bg-toggle-bg--active',\n 'data-[variant=separated]:data-[state=on]:hover:border-toggle-border--active data-[variant=separated]:data-[state=on]:hover:bg-toggle-bg--active',\n className,\n )}\n data-layout={resolvedLayout}\n data-size={resolvedSize}\n data-slot='toggle-group-item'\n data-testid='spectral-toggle-group-item'\n data-variant={resolvedVariant}\n ref={ref}\n style={{ ...getActiveColorStyle(resolvedActiveColor, resolvedActiveTextColor), ...style }}\n value={value}\n {...props}\n >\n {children}\n </ToggleGroupItemBase>\n )\n}\n\nToggleGroupItem.displayName = 'ToggleGroupItem'\n"],"mappings":";;;;;;;;;;;AAcA,MAAa,mBAAmB,EAC9B,aACA,iBACA,UACA,WACA,QACA,KACA,MACA,OACA,OACA,SACA,GAAG,YAGC;CACJ,MAAM,UAAU,WAAW,mBAAmB;CAC9C,MAAM,kBAAkB,WAAW,QAAQ;CAC3C,MAAM,eAAe,QAAQ,QAAQ;CACrC,MAAM,iBAAiB,UAAU,QAAQ;CACzC,MAAM,sBAAsB,eAAe,QAAQ;CACnD,MAAM,0BAA0B,mBAAmB,QAAQ;AAE3D,QACE,oBAAC,qBAAD;EACE,WAAW,GACT,eAAe;GACb,SAAS;GACT,MAAM;GACN,QAAQ;GACT,CAAC,EACF,kMACA,+LACA,kMACA,mJACA,UACD;EACD,eAAa;EACb,aAAW;EACX,aAAU;EACV,eAAY;EACZ,gBAAc;EACT;EACL,OAAO;GAAE,GAAG,oBAAoB,qBAAqB,wBAAwB;GAAE,GAAG;GAAO;EAClF;EACP,GAAI;EAEH;EACmB;;AAI1B,gBAAgB,cAAc"}
|
package/dist/Tooltip.js
CHANGED
|
@@ -30,7 +30,7 @@ const TooltipTrigger = ({ asChild, children, ...props }) => {
|
|
|
30
30
|
children
|
|
31
31
|
});
|
|
32
32
|
};
|
|
33
|
-
const tooltipVariants = cva(`tooltip-effects rounded-md px-3 py-1.5 text-xs pointer-events-auto z-50 w-fit bg-tooltip-bg text-text-primary
|
|
33
|
+
const tooltipVariants = cva(`tooltip-effects rounded-md px-3 py-1.5 text-xs pointer-events-auto z-50 w-fit bg-tooltip-bg text-text-primary outline-none select-none motion-safe:data-[side=bottom]:slide-in-from-top-2 motion-safe:data-[side=left]:slide-in-from-right-2 motion-safe:data-[side=right]:slide-in-from-left-2 motion-safe:data-[side=top]:slide-in-from-bottom-2 motion-safe:data-[state=closed]:animate-out motion-safe:data-[state=closed]:fade-out-0 motion-safe:data-[state=closed]:zoom-out-95 motion-safe:data-[state=open]:animate-in motion-safe:data-[state=open]:fade-in-0 motion-safe:data-[state=open]:zoom-in-95`, {
|
|
34
34
|
variants: { variant: {
|
|
35
35
|
default: "",
|
|
36
36
|
outline: "border-2 border-tooltip-outline-border"
|
package/dist/Tooltip.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Tooltip.js","names":[],"sources":["../src/components/Tooltip/Tooltip.tsx"],"sourcesContent":["import * as TooltipPrimitive from '@radix-ui/react-tooltip'\nimport { cn } from '@utils/twUtils'\nimport { cva, type VariantProps } from 'class-variance-authority'\nimport { type ComponentProps } from 'react'\n\nexport const TooltipProvider = ({ delayDuration = 0, ...props }: ComponentProps<typeof TooltipPrimitive.Provider>) => {\n return (\n <TooltipPrimitive.Provider\n data-slot='tooltip-provider'\n delayDuration={delayDuration}\n {...props}\n />\n )\n}\n\nexport const Tooltip = ({ ...props }: ComponentProps<typeof TooltipPrimitive.Root>) => {\n return (\n <TooltipProvider>\n <TooltipPrimitive.Root\n data-slot='tooltip'\n data-testid='spectral-tooltip'\n {...props}\n />\n </TooltipProvider>\n )\n}\n\nexport const TooltipTrigger = ({ asChild, children, ...props }: ComponentProps<typeof TooltipPrimitive.Trigger>) => {\n return (\n <TooltipPrimitive.Trigger\n asChild={asChild}\n className='w-fit'\n data-slot='tooltip-trigger'\n data-testid='spectral-tooltip-trigger'\n {...props}\n >\n {children}\n </TooltipPrimitive.Trigger>\n )\n}\n\nconst tooltipVariants = cva(\n `tooltip-effects rounded-md px-3 py-1.5 text-xs pointer-events-auto z-50 w-fit bg-tooltip-bg text-text-primary
|
|
1
|
+
{"version":3,"file":"Tooltip.js","names":[],"sources":["../src/components/Tooltip/Tooltip.tsx"],"sourcesContent":["import * as TooltipPrimitive from '@radix-ui/react-tooltip'\nimport { cn } from '@utils/twUtils'\nimport { cva, type VariantProps } from 'class-variance-authority'\nimport { type ComponentProps } from 'react'\n\nexport const TooltipProvider = ({ delayDuration = 0, ...props }: ComponentProps<typeof TooltipPrimitive.Provider>) => {\n return (\n <TooltipPrimitive.Provider\n data-slot='tooltip-provider'\n delayDuration={delayDuration}\n {...props}\n />\n )\n}\n\nexport const Tooltip = ({ ...props }: ComponentProps<typeof TooltipPrimitive.Root>) => {\n return (\n <TooltipProvider>\n <TooltipPrimitive.Root\n data-slot='tooltip'\n data-testid='spectral-tooltip'\n {...props}\n />\n </TooltipProvider>\n )\n}\n\nexport const TooltipTrigger = ({ asChild, children, ...props }: ComponentProps<typeof TooltipPrimitive.Trigger>) => {\n return (\n <TooltipPrimitive.Trigger\n asChild={asChild}\n className='w-fit'\n data-slot='tooltip-trigger'\n data-testid='spectral-tooltip-trigger'\n {...props}\n >\n {children}\n </TooltipPrimitive.Trigger>\n )\n}\n\nconst tooltipVariants = cva(\n `tooltip-effects rounded-md px-3 py-1.5 text-xs pointer-events-auto z-50 w-fit bg-tooltip-bg text-text-primary outline-none select-none motion-safe:data-[side=bottom]:slide-in-from-top-2 motion-safe:data-[side=left]:slide-in-from-right-2 motion-safe:data-[side=right]:slide-in-from-left-2 motion-safe:data-[side=top]:slide-in-from-bottom-2 motion-safe:data-[state=closed]:animate-out motion-safe:data-[state=closed]:fade-out-0 motion-safe:data-[state=closed]:zoom-out-95 motion-safe:data-[state=open]:animate-in motion-safe:data-[state=open]:fade-in-0 motion-safe:data-[state=open]:zoom-in-95`,\n {\n variants: {\n variant: {\n default: '',\n outline: 'border-2 border-tooltip-outline-border',\n },\n },\n defaultVariants: {\n variant: 'default',\n },\n },\n)\n\nconst tooltipArrowVariants = cva('size-2.5 z-[-1] rotate-45 rounded-[2px] bg-tooltip-arrow fill-tooltip-arrow', {\n variants: {\n variant: {\n default: '-translate-y-[calc(50%+1px)]',\n outline: '-translate-y-[calc(50%-1.5px)] border-2 border-t-0 border-l-0 border-tooltip-outline-arrow',\n },\n },\n defaultVariants: {\n variant: 'default',\n },\n})\n\nexport const TooltipContent = ({\n children,\n className,\n showArrow = false,\n side,\n sideOffset = 0,\n variant = 'default',\n ...props\n}: ComponentProps<typeof TooltipPrimitive.Content> &\n VariantProps<typeof tooltipVariants> & {\n showArrow?: boolean\n }) => {\n return (\n <TooltipPrimitive.Portal\n data-testid='spectral-tooltip-portal'\n data-slot='tooltip-portal'\n >\n <TooltipPrimitive.Content\n className={cn(tooltipVariants({ variant }), className)}\n data-slot='tooltip-content'\n data-testid='spectral-tooltip-content'\n side={side}\n sideOffset={sideOffset}\n {...props}\n >\n {children}\n {showArrow && (\n <TooltipPrimitive.Arrow\n className={cn(tooltipArrowVariants({ variant }))}\n height={8}\n />\n )}\n </TooltipPrimitive.Content>\n </TooltipPrimitive.Portal>\n )\n}\n"],"mappings":";;;;;;;;AAKA,MAAa,mBAAmB,EAAE,gBAAgB,GAAG,GAAG,YAA8D;AACpH,QACE,oBAAC,iBAAiB,UAAlB;EACE,aAAU;EACK;EACf,GAAI;EACJ;;AAIN,MAAa,WAAW,EAAE,GAAG,YAA0D;AACrF,QACE,oBAAC,iBAAD,YACE,oBAAC,iBAAiB,MAAlB;EACE,aAAU;EACV,eAAY;EACZ,GAAI;EACJ,GACc;;AAItB,MAAa,kBAAkB,EAAE,SAAS,UAAU,GAAG,YAA6D;AAClH,QACE,oBAAC,iBAAiB,SAAlB;EACW;EACT,WAAU;EACV,aAAU;EACV,eAAY;EACZ,GAAI;EAEH;EACwB;;AAI/B,MAAM,kBAAkB,IACtB,mlBACA;CACE,UAAU,EACR,SAAS;EACP,SAAS;EACT,SAAS;EACV,EACF;CACD,iBAAiB,EACf,SAAS,WACV;CACF,CACF;AAED,MAAM,uBAAuB,IAAI,+EAA+E;CAC9G,UAAU,EACR,SAAS;EACP,SAAS;EACT,SAAS;EACV,EACF;CACD,iBAAiB,EACf,SAAS,WACV;CACF,CAAC;AAEF,MAAa,kBAAkB,EAC7B,UACA,WACA,YAAY,OACZ,MACA,aAAa,GACb,UAAU,WACV,GAAG,YAIG;AACN,QACE,oBAAC,iBAAiB,QAAlB;EACE,eAAY;EACZ,aAAU;YAEV,qBAAC,iBAAiB,SAAlB;GACE,WAAW,GAAG,gBAAgB,EAAE,SAAS,CAAC,EAAE,UAAU;GACtD,aAAU;GACV,eAAY;GACN;GACM;GACZ,GAAI;aANN,CAQG,UACA,aACC,oBAAC,iBAAiB,OAAlB;IACE,WAAW,GAAG,qBAAqB,EAAE,SAAS,CAAC,CAAC;IAChD,QAAQ;IACR,EAEqB;;EACH"}
|
package/dist/Tray.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Tray.d.ts","names":[],"sources":["../src/components/Tray/Tray.tsx"],"mappings":";;;;;;;;
|
|
1
|
+
{"version":3,"file":"Tray.d.ts","names":[],"sources":["../src/components/Tray/Tray.tsx"],"mappings":";;;;;;;;cAQM,mBAAA,GAAmB,KAAA;;IAYvB,iCAAA,CAAA,SAAA;AAAA,KAkBU,aAAA,GAAgB,wBAAA,QAAgC,MAAA,CAAS,IAAA;EACnE,QAAA,GAAW,SAAA;EACX,IAAA;AAAA;AAAA;EAGqB,QAAA;EAAU,IAAA;EAAA,GAAgB;AAAA,GAAS,aAAA,GAAa,oBAAA,CAAA,GAAA,CAAA,OAAA;AAAA;;;;EA4BrE,SAAA;EACA,GAAA;EAAA,GACG;AAAA,GACF,wBAAA,QAAgC,MAAA,CAAS,OAAA;EAC1C,GAAA,GAAM,GAAA,CAAI,iBAAA;AAAA,IACX,oBAAA,CAAA,GAAA,CAAA,OAAA;AAAA;;;;EAYC,SAAA;EACA,GAAA;EAAA,GACG;AAAA,GACF,wBAAA,QAAgC,MAAA,CAAS,KAAA;EAC1C,GAAA,GAAM,GAAA,CAAI,iBAAA;AAAA,IACX,oBAAA,CAAA,GAAA,CAAA,OAAA;AAAA;;;KA6BW,gBAAA,GAAmB,wBAAA,QAAgC,MAAA,CAAS,OAAA,IACtE,YAAA,QAAoB,mBAAA;sFAElB,YAAA;EACA,QAAA,GAAW,SAAA;AAAA;AAAA;gBAIC,SAAA;EACd,QAAA;EACA,SAAA;EACA,GAAA;EACA,IAAA,EAAM,QAAA;EAAA,GACH;AAAA,GACF,gBAAA;EACD,GAAA,GAAM,GAAA,CAAI,cAAA;AAAA,IACX,oBAAA,CAAA,GAAA,CAAA,OAAA;AAAA;;;;EA0BC,QAAA;EACA,SAAA;EACA,GAAA;EAAA,GACG;AAAA,GACF,wBAAA;EACD,GAAA,GAAM,GAAA,CAAI,cAAA;AAAA,IACX,oBAAA,CAAA,GAAA,CAAA,OAAA;AAAA;;;;EAoBC,QAAA;EACA,SAAA;EACA,GAAA;EAAA,GACG;AAAA,GACF,wBAAA;EACD,GAAA,GAAM,GAAA,CAAI,kBAAA;AAAA,IACX,oBAAA,CAAA,GAAA,CAAA,OAAA;AAAA;;;;EAaC,SAAA;EACA,GAAA;EAAA,GACG;AAAA,GACF,wBAAA;EACD,GAAA,GAAM,GAAA,CAAI,oBAAA;AAAA,IACX,oBAAA,CAAA,GAAA,CAAA,OAAA;AAAA;;;KAUW,aAAA,GAAgB,wBAAA;EAC1B,YAAA;AAAA;AAAA;EAIA,YAAA,EAAc,OAAA;EACd,QAAA;EACA,SAAA;EACA,GAAA;EAAA,GACG;AAAA,GACF,aAAA;EACD,GAAA,GAAM,GAAA,CAAI,cAAA;AAAA,IACX,oBAAA,CAAA,GAAA,CAAA,OAAA;AAAA"}
|
package/dist/Tray.js
CHANGED
|
@@ -7,7 +7,6 @@ import { jsx, jsxs } from "react/jsx-runtime";
|
|
|
7
7
|
import { cva } from "class-variance-authority";
|
|
8
8
|
import { Drawer } from "vaul";
|
|
9
9
|
import { AnimatePresence, motion } from "motion/react";
|
|
10
|
-
import useMeasure from "react-use-measure";
|
|
11
10
|
|
|
12
11
|
//#region src/components/Tray/Tray.tsx
|
|
13
12
|
const TrayContentVariants = cva("bottom-4 rounded-2xl sm:max-md:max-w-full fixed left-1/2 z-10 w-full -translate-x-1/2 overflow-hidden bg-drawer-bg outline-none", {
|
|
@@ -71,7 +70,6 @@ TrayOverlay.displayName = "TrayOverlay";
|
|
|
71
70
|
const TrayContent = ({ "aria-label": ariaLabel, children, className, ref, size: sizeProp, ...props }) => {
|
|
72
71
|
const { size: contextSize } = useTrayContext();
|
|
73
72
|
const size = sizeProp ?? contextSize;
|
|
74
|
-
const [elementRef, bounds] = useMeasure();
|
|
75
73
|
return /* @__PURE__ */ jsxs(Drawer.Portal, { children: [/* @__PURE__ */ jsx(TrayOverlay, {}), /* @__PURE__ */ jsx(Drawer.Content, {
|
|
76
74
|
"aria-describedby": void 0,
|
|
77
75
|
asChild: true,
|
|
@@ -79,33 +77,18 @@ const TrayContent = ({ "aria-label": ariaLabel, children, className, ref, size:
|
|
|
79
77
|
"data-testid": "spectral-tray-content",
|
|
80
78
|
ref,
|
|
81
79
|
...props,
|
|
82
|
-
children: /* @__PURE__ */ jsxs(
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
1,
|
|
90
|
-
.5,
|
|
91
|
-
1
|
|
92
|
-
]
|
|
93
|
-
}
|
|
94
|
-
},
|
|
95
|
-
children: [/* @__PURE__ */ jsx(Drawer.Title, {
|
|
96
|
-
className: "sr-only",
|
|
97
|
-
children: ariaLabel ?? "Tray"
|
|
98
|
-
}), /* @__PURE__ */ jsx("div", {
|
|
99
|
-
ref: elementRef,
|
|
100
|
-
className: "px-6 pb-6 pt-2.5 antialiased",
|
|
101
|
-
children
|
|
102
|
-
})]
|
|
103
|
-
})
|
|
80
|
+
children: /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx(Drawer.Title, {
|
|
81
|
+
className: "sr-only",
|
|
82
|
+
children: ariaLabel ?? "Tray"
|
|
83
|
+
}), /* @__PURE__ */ jsx("div", {
|
|
84
|
+
className: "px-6 pb-6 pt-2.5 antialiased",
|
|
85
|
+
children
|
|
86
|
+
})] })
|
|
104
87
|
})] });
|
|
105
88
|
};
|
|
106
89
|
TrayContent.displayName = "TrayContent";
|
|
107
90
|
const TrayHeader = ({ children, className, ref, ...props }) => /* @__PURE__ */ jsxs("div", {
|
|
108
|
-
className: cn("
|
|
91
|
+
className: cn("gap-1.5 pb-4 relative flex flex-col text-start", className),
|
|
109
92
|
"data-testid": "spectral-tray-header",
|
|
110
93
|
ref,
|
|
111
94
|
...props,
|
|
@@ -137,21 +120,6 @@ TraySubtitle.displayName = "TraySubtitle";
|
|
|
137
120
|
const TrayBody = ({ animationKey: keyProp, children, className, ref, ...props }) => {
|
|
138
121
|
const { animationKey: contextKey } = useTrayContext();
|
|
139
122
|
const key = keyProp ?? contextKey;
|
|
140
|
-
const previousHeightRef = useRef(null);
|
|
141
|
-
const [elementRef, bounds] = useMeasure();
|
|
142
|
-
const opacityDuration = useMemo(() => {
|
|
143
|
-
const currentHeight = bounds.height;
|
|
144
|
-
const previousHeight = previousHeightRef.current;
|
|
145
|
-
const MAX_DURATION = .27;
|
|
146
|
-
const MIN_DURATION = .15;
|
|
147
|
-
if (!previousHeightRef.current) {
|
|
148
|
-
previousHeightRef.current = currentHeight;
|
|
149
|
-
return MIN_DURATION;
|
|
150
|
-
}
|
|
151
|
-
const heightDifference = Math.abs(currentHeight - (previousHeight ?? 0));
|
|
152
|
-
previousHeightRef.current = currentHeight;
|
|
153
|
-
return Math.min(Math.max(heightDifference / 500, MIN_DURATION), MAX_DURATION);
|
|
154
|
-
}, [bounds.height]);
|
|
155
123
|
return /* @__PURE__ */ jsx("div", {
|
|
156
124
|
ref,
|
|
157
125
|
"data-testid": "spectral-tray-body",
|
|
@@ -159,7 +127,7 @@ const TrayBody = ({ animationKey: keyProp, children, className, ref, ...props })
|
|
|
159
127
|
...props,
|
|
160
128
|
children: /* @__PURE__ */ jsx(AnimatePresence, {
|
|
161
129
|
initial: false,
|
|
162
|
-
mode: "
|
|
130
|
+
mode: "wait",
|
|
163
131
|
custom: key,
|
|
164
132
|
children: /* @__PURE__ */ jsx(motion.div, {
|
|
165
133
|
animate: {
|
|
@@ -175,15 +143,9 @@ const TrayBody = ({ animationKey: keyProp, children, className, ref, ...props })
|
|
|
175
143
|
opacity: 0,
|
|
176
144
|
scale: .96
|
|
177
145
|
},
|
|
178
|
-
ref: elementRef,
|
|
179
146
|
transition: {
|
|
180
|
-
duration:
|
|
181
|
-
ease:
|
|
182
|
-
.26,
|
|
183
|
-
.08,
|
|
184
|
-
.25,
|
|
185
|
-
1
|
|
186
|
-
]
|
|
147
|
+
duration: .2,
|
|
148
|
+
ease: "easeOut"
|
|
187
149
|
},
|
|
188
150
|
children
|
|
189
151
|
}, key)
|