@olundot/ui 0.20.0-next.1 → 0.20.0-next.2

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/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # @olundot/ui
2
2
 
3
+ ## 0.20.0-next.2
4
+
5
+ ### Patch Changes
6
+
7
+ - 48c5a4d: `Tabs` `line`의 선택 표시가 다른 탭을 고를 때 그 탭으로 미끄러진다(`--dur-base`, `--ease-standard`). 처음 그릴 때와 크기가 바뀔 때는 바로 놓이고, 동작 줄이기 설정에서는 움직이지 않는다. 가로와 세로 모두 해당하며 `contained`는 그대로다.
8
+ - 목록 안에 `data-slot="tabs-indicator"` 요소(`aria-hidden`) 하나가 생긴다. 하이드레이션 전에는 기존 `::after` 밑줄이 보이고, 강조선이 자리를 잡으면 숨는다.
9
+ - 선택된 탭 라벨이 굵어져도 탭 폭이 바뀌지 않는다. 라벨에 굵기별 숨은 사본을 두어 폭을 미리 잡는다. 탭을 바꿔도 다른 탭이 밀리지 않는다.
10
+
3
11
  ## 0.20.0-next.1
4
12
 
5
13
  ### Minor Changes
package/dist/index.d.ts CHANGED
@@ -52,10 +52,10 @@ type GhostDefaultActionStyle = {
52
52
  };
53
53
 
54
54
  declare const buttonCva: (props?: ({
55
- size?: "default" | "md" | "xs" | "sm" | "lg" | "xl" | "icon" | "icon-xs" | null | undefined;
55
+ size?: "xs" | "sm" | "md" | "lg" | "xl" | "default" | "icon" | "icon-xs" | null | undefined;
56
56
  appearance?: "filled" | "outline" | "ghost" | null | undefined;
57
57
  tone?: "primary" | "neutral" | "danger" | null | undefined;
58
- variant?: "default" | "outline" | "ghost" | "primary" | "danger" | "emergency" | "subtle" | "destructive" | "secondary" | "link" | null | undefined;
58
+ variant?: "primary" | "secondary" | "destructive" | "default" | "outline" | "ghost" | "danger" | "emergency" | "subtle" | "link" | null | undefined;
59
59
  } & class_variance_authority_types.ClassProp) | undefined) => string;
60
60
  type ButtonVariantsOptions = Parameters<typeof buttonCva>[0];
61
61
  declare function buttonVariants(options?: ButtonVariantsOptions): string;
@@ -96,7 +96,7 @@ type CopyButtonProps = Omit<ButtonProps, 'children' | 'leadingIcon' | 'loading'
96
96
  variant?: ButtonVariant;
97
97
  duration?: number;
98
98
  };
99
- declare const CopyButton: React.ForwardRefExoticComponent<Omit<ButtonProps, "size" | "variant" | "children" | "leadingIcon" | "trailingIcon" | "loading"> & {
99
+ declare const CopyButton: React.ForwardRefExoticComponent<Omit<ButtonProps, "variant" | "size" | "children" | "leadingIcon" | "trailingIcon" | "loading"> & {
100
100
  value: string;
101
101
  label?: React.ReactNode;
102
102
  copiedLabel?: React.ReactNode;
@@ -107,13 +107,13 @@ declare const CopyButton: React.ForwardRefExoticComponent<Omit<ButtonProps, "siz
107
107
  } & React.RefAttributes<HTMLButtonElement>>;
108
108
 
109
109
  declare const inputVariants: (props?: ({
110
- size?: "md" | "sm" | "lg" | null | undefined;
110
+ size?: "sm" | "md" | "lg" | null | undefined;
111
111
  fullWidth?: boolean | null | undefined;
112
112
  surface?: SurfaceLevel | null | undefined;
113
113
  } & class_variance_authority_types.ClassProp) | undefined) => string;
114
114
  declare const inputMessageVariants: (props?: ({
115
- state?: "default" | "error" | "success" | null | undefined;
116
- size?: "md" | "sm" | "lg" | null | undefined;
115
+ state?: "error" | "success" | "default" | null | undefined;
116
+ size?: "sm" | "md" | "lg" | null | undefined;
117
117
  } & class_variance_authority_types.ClassProp) | undefined) => string;
118
118
  type InputState = NonNullable<VariantProps<typeof inputMessageVariants>["state"]>;
119
119
  /** Canonical size type (0.2.0+). Use `size` prop with these values. */
@@ -161,11 +161,11 @@ interface InputProps extends Omit<React.InputHTMLAttributes<HTMLInputElement>, "
161
161
  declare const Input: React.ForwardRefExoticComponent<InputProps & React.RefAttributes<HTMLInputElement>>;
162
162
 
163
163
  declare const fileUploadZoneVariants: (props?: ({
164
- size?: "default" | "compact" | null | undefined;
164
+ size?: "compact" | "default" | null | undefined;
165
165
  surface?: SurfaceLevel | null | undefined;
166
166
  } & class_variance_authority_types.ClassProp) | undefined) => string;
167
167
  declare const fileUploadIconVariants: (props?: ({
168
- size?: "default" | "compact" | null | undefined;
168
+ size?: "compact" | "default" | null | undefined;
169
169
  } & class_variance_authority_types.ClassProp) | undefined) => string;
170
170
  interface FileUploadZoneProps extends Omit<React.LabelHTMLAttributes<HTMLLabelElement>, "onChange">, VariantProps<typeof fileUploadZoneVariants> {
171
171
  accept?: string;
@@ -203,12 +203,12 @@ interface InlineAlertProps extends Omit<HTMLAttributes<HTMLDivElement>, 'childre
203
203
  declare const InlineAlert: React.ForwardRefExoticComponent<InlineAlertProps & React.RefAttributes<HTMLDivElement>>;
204
204
 
205
205
  declare const textareaVariants: (props?: ({
206
- size?: "md" | "sm" | "lg" | null | undefined;
206
+ size?: "sm" | "md" | "lg" | null | undefined;
207
207
  fullWidth?: boolean | null | undefined;
208
208
  surface?: SurfaceLevel | null | undefined;
209
209
  } & class_variance_authority_types.ClassProp) | undefined) => string;
210
210
  declare const textareaMessageVariants: (props?: ({
211
- state?: "default" | "error" | "success" | null | undefined;
211
+ state?: "error" | "success" | "default" | null | undefined;
212
212
  } & class_variance_authority_types.ClassProp) | undefined) => string;
213
213
  type TextareaState = NonNullable<VariantProps<typeof textareaMessageVariants>["state"]>;
214
214
  /** Canonical size type (0.2.0+). Use `size` prop with these values. */
@@ -257,14 +257,14 @@ interface TextareaProps extends Omit<React.TextareaHTMLAttributes<HTMLTextAreaEl
257
257
  declare const Textarea: React.ForwardRefExoticComponent<TextareaProps & React.RefAttributes<HTMLTextAreaElement>>;
258
258
 
259
259
  declare const selectVariants: (props?: ({
260
- size?: "md" | "sm" | "lg" | null | undefined;
260
+ size?: "sm" | "md" | "lg" | null | undefined;
261
261
  fullWidth?: boolean | null | undefined;
262
262
  appearance?: "inline" | "field" | null | undefined;
263
263
  surface?: SurfaceLevel | null | undefined;
264
264
  } & class_variance_authority_types.ClassProp) | undefined) => string;
265
265
  declare const selectMessageVariants: (props?: ({
266
- state?: "default" | "error" | "success" | null | undefined;
267
- size?: "md" | "sm" | "lg" | null | undefined;
266
+ state?: "error" | "success" | "default" | null | undefined;
267
+ size?: "sm" | "md" | "lg" | null | undefined;
268
268
  } & class_variance_authority_types.ClassProp) | undefined) => string;
269
269
  type SelectState = NonNullable<VariantProps<typeof selectMessageVariants>["state"]>;
270
270
  /** Canonical size type (0.2.0+). Use `size` prop with these values. */
@@ -1475,7 +1475,7 @@ interface StatusDotProps extends Omit<React.HTMLAttributes<HTMLSpanElement>, 'ch
1475
1475
  declare const StatusDot: React.ForwardRefExoticComponent<StatusDotProps & React.RefAttributes<HTMLSpanElement>>;
1476
1476
 
1477
1477
  declare const notificationBadgeVariants: (props?: ({
1478
- size?: "md" | "sm" | null | undefined;
1478
+ size?: "sm" | "md" | null | undefined;
1479
1479
  placement?: "none" | "inline" | "corner" | null | undefined;
1480
1480
  } & class_variance_authority_types.ClassProp) | undefined) => string;
1481
1481
  type NotificationBadgeSize = NonNullable<VariantProps<typeof notificationBadgeVariants>['size']>;
@@ -1488,13 +1488,13 @@ declare const NotificationBadge: React.ForwardRefExoticComponent<NotificationBad
1488
1488
 
1489
1489
  declare const chipVariants: (props?: ({
1490
1490
  variant?: "filter" | "choice" | "removable" | "status-filter" | null | undefined;
1491
- tone?: "accent" | "neutral" | "danger" | "success" | "warning" | "muted" | null | undefined;
1492
- size?: "md" | "sm" | "lg" | null | undefined;
1491
+ tone?: "muted" | "accent" | "warning" | "success" | "neutral" | "danger" | null | undefined;
1492
+ size?: "sm" | "md" | "lg" | null | undefined;
1493
1493
  pressed?: boolean | null | undefined;
1494
1494
  } & class_variance_authority_types.ClassProp) | undefined) => string;
1495
1495
  declare const chipCountVariants: (props?: ({
1496
- tone?: "accent" | "neutral" | "danger" | "success" | "warning" | "muted" | null | undefined;
1497
- size?: "md" | "sm" | "lg" | null | undefined;
1496
+ tone?: "muted" | "accent" | "warning" | "success" | "neutral" | "danger" | null | undefined;
1497
+ size?: "sm" | "md" | "lg" | null | undefined;
1498
1498
  } & class_variance_authority_types.ClassProp) | undefined) => string;
1499
1499
  type ChipVariant = NonNullable<VariantProps<typeof chipVariants>["variant"]>;
1500
1500
  type ChipTone = NonNullable<VariantProps<typeof chipVariants>["tone"]>;
@@ -1508,7 +1508,7 @@ interface ChipProps extends Omit<React.ButtonHTMLAttributes<HTMLButtonElement>,
1508
1508
  declare const Chip: React.ForwardRefExoticComponent<ChipProps & React.RefAttributes<HTMLButtonElement>>;
1509
1509
 
1510
1510
  declare const emptyStateBaseVariants: (props?: ({
1511
- size?: "md" | "sm" | "lg" | null | undefined;
1511
+ size?: "sm" | "md" | "lg" | null | undefined;
1512
1512
  bordered?: boolean | null | undefined;
1513
1513
  } & class_variance_authority_types.ClassProp) | undefined) => string;
1514
1514
  type EmptyStateTone = "neutral" | "muted" | "raised";
@@ -1519,7 +1519,7 @@ interface EmptyStateVariantOptions extends VariantProps<typeof emptyStateBaseVar
1519
1519
  }
1520
1520
  declare function emptyStateVariants({ size, tone, surface, bordered, className, }?: EmptyStateVariantOptions): string;
1521
1521
  declare const emptyStateIconVariants: (props?: ({
1522
- size?: "md" | "sm" | "lg" | null | undefined;
1522
+ size?: "sm" | "md" | "lg" | null | undefined;
1523
1523
  } & class_variance_authority_types.ClassProp) | undefined) => string;
1524
1524
  type EmptyStateSize = NonNullable<VariantProps<typeof emptyStateBaseVariants>["size"]>;
1525
1525
  interface EmptyStateProps extends Omit<React.HTMLAttributes<HTMLDivElement>, "title">, VariantProps<typeof emptyStateBaseVariants> {
@@ -1538,7 +1538,7 @@ interface EmptyStateProps extends Omit<React.HTMLAttributes<HTMLDivElement>, "ti
1538
1538
  declare const EmptyState: React.ForwardRefExoticComponent<EmptyStateProps & React.RefAttributes<HTMLDivElement>>;
1539
1539
 
1540
1540
  declare const errorStateVariants: (props?: ({
1541
- size?: "md" | "sm" | "lg" | null | undefined;
1541
+ size?: "sm" | "md" | "lg" | null | undefined;
1542
1542
  surface?: SurfaceLevel | null | undefined;
1543
1543
  } & class_variance_authority_types.ClassProp) | undefined) => string;
1544
1544
  interface ErrorStateProps extends Omit<React.HTMLAttributes<HTMLDivElement>, "title">, VariantProps<typeof errorStateVariants> {
@@ -1558,7 +1558,7 @@ declare const skeletonVariants: (props?: ({
1558
1558
  shape?: "text" | "block" | "circle" | null | undefined;
1559
1559
  } & class_variance_authority_types.ClassProp) | undefined) => string;
1560
1560
  declare const spinnerVariants: (props?: ({
1561
- size?: "md" | "sm" | "lg" | 16 | 24 | 32 | null | undefined;
1561
+ size?: "sm" | "md" | "lg" | 16 | 24 | 32 | null | undefined;
1562
1562
  } & class_variance_authority_types.ClassProp) | undefined) => string;
1563
1563
  interface SkeletonProps extends React.HTMLAttributes<HTMLDivElement>, VariantProps<typeof skeletonVariants> {
1564
1564
  }
@@ -1591,7 +1591,7 @@ interface SpinnerProps extends React.HTMLAttributes<HTMLSpanElement> {
1591
1591
  declare const Spinner: React.ForwardRefExoticComponent<SpinnerProps & React.RefAttributes<HTMLSpanElement>>;
1592
1592
 
1593
1593
  declare const iconButtonCva: (props?: ({
1594
- size?: "md" | "sm" | "lg" | null | undefined;
1594
+ size?: "sm" | "md" | "lg" | null | undefined;
1595
1595
  pressed?: boolean | null | undefined;
1596
1596
  appearance?: "filled" | "outline" | "ghost" | null | undefined;
1597
1597
  tone?: "primary" | "neutral" | "danger" | null | undefined;
@@ -1781,7 +1781,7 @@ interface ActionFooterProps extends Omit<React.HTMLAttributes<HTMLDivElement>, '
1781
1781
  declare const ActionFooter: React.ForwardRefExoticComponent<ActionFooterProps & React.RefAttributes<HTMLDivElement>>;
1782
1782
 
1783
1783
  declare const toastVariants: (props?: ({
1784
- variant?: "default" | "error" | "success" | "info" | "warning" | null | undefined;
1784
+ variant?: "error" | "warning" | "info" | "success" | "default" | null | undefined;
1785
1785
  } & class_variance_authority_types.ClassProp) | undefined) => string;
1786
1786
  declare const toastViewportVariants: (props?: class_variance_authority_types.ClassProp | undefined) => string;
1787
1787
  type ToastVariant = NonNullable<VariantProps<typeof toastVariants>['variant']>;
@@ -1835,19 +1835,19 @@ interface ToastProviderProps extends React.HTMLAttributes<HTMLDivElement> {
1835
1835
  declare function ToastProvider({ children, maxToasts, className, ...props }: ToastProviderProps): react_jsx_runtime.JSX.Element;
1836
1836
 
1837
1837
  declare const otpFieldVariants: (props?: ({
1838
- size?: "default" | "compact" | null | undefined;
1838
+ size?: "compact" | "default" | null | undefined;
1839
1839
  } & class_variance_authority_types.ClassProp) | undefined) => string;
1840
1840
  declare const otpFieldGroupVariants: (props?: ({
1841
- size?: "default" | "compact" | null | undefined;
1841
+ size?: "compact" | "default" | null | undefined;
1842
1842
  fluid?: boolean | null | undefined;
1843
1843
  } & class_variance_authority_types.ClassProp) | undefined) => string;
1844
1844
  declare const otpCellVariants: (props?: ({
1845
- size?: "default" | "compact" | null | undefined;
1845
+ size?: "compact" | "default" | null | undefined;
1846
1846
  fluid?: boolean | null | undefined;
1847
1847
  surface?: SurfaceLevel | null | undefined;
1848
1848
  } & class_variance_authority_types.ClassProp) | undefined) => string;
1849
1849
  declare const otpFieldMessageVariants: (props?: ({
1850
- state?: "default" | "error" | null | undefined;
1850
+ state?: "error" | "default" | null | undefined;
1851
1851
  } & class_variance_authority_types.ClassProp) | undefined) => string;
1852
1852
  type OTPFieldSize = NonNullable<VariantProps<typeof otpCellVariants>["size"]>;
1853
1853
  interface OTPFieldProps extends Omit<React.FieldsetHTMLAttributes<HTMLFieldSetElement>, "onChange" | "children" | "defaultValue" | "value">, VariantProps<typeof otpFieldVariants> {
@@ -1874,10 +1874,10 @@ interface OTPFieldProps extends Omit<React.FieldsetHTMLAttributes<HTMLFieldSetEl
1874
1874
  declare const OTPField: React.ForwardRefExoticComponent<OTPFieldProps & React.RefAttributes<HTMLFieldSetElement>>;
1875
1875
 
1876
1876
  declare const searchFieldVariants: (props?: ({
1877
- size?: "md" | "sm" | "lg" | null | undefined;
1877
+ size?: "sm" | "md" | "lg" | null | undefined;
1878
1878
  } & class_variance_authority_types.ClassProp) | undefined) => string;
1879
1879
  declare const searchFieldInputVariants: (props?: ({
1880
- size?: "md" | "sm" | "lg" | null | undefined;
1880
+ size?: "sm" | "md" | "lg" | null | undefined;
1881
1881
  hasClear?: boolean | null | undefined;
1882
1882
  surface?: SurfaceLevel | null | undefined;
1883
1883
  } & class_variance_authority_types.ClassProp) | undefined) => string;
@@ -1901,14 +1901,14 @@ declare const checkboxVariants: (props?: ({
1901
1901
  } & class_variance_authority_types.ClassProp) | undefined) => string;
1902
1902
  declare const checkboxRootVariants: (props?: class_variance_authority_types.ClassProp | undefined) => string;
1903
1903
  declare const checkboxBoxVariants: (props?: ({
1904
- size?: "md" | "sm" | null | undefined;
1904
+ size?: "sm" | "md" | null | undefined;
1905
1905
  } & class_variance_authority_types.ClassProp) | undefined) => string;
1906
1906
  declare const checkboxGroupItemVariants: (props?: ({
1907
- appearance?: "plain" | "card" | null | undefined;
1907
+ appearance?: "card" | "plain" | null | undefined;
1908
1908
  density?: "compact" | "comfortable" | null | undefined;
1909
1909
  } & class_variance_authority_types.ClassProp) | undefined) => string;
1910
1910
  declare const checkboxGroupBoxVariants: (props?: ({
1911
- appearance?: "plain" | "card" | null | undefined;
1911
+ appearance?: "card" | "plain" | null | undefined;
1912
1912
  } & class_variance_authority_types.ClassProp) | undefined) => string;
1913
1913
  type CheckboxSize = NonNullable<VariantProps<typeof checkboxBoxVariants>["size"]>;
1914
1914
  type CheckboxCheckedState = React.ComponentProps<typeof Checkbox$1.Root>["checked"];
@@ -1933,7 +1933,7 @@ interface CheckboxProps extends Omit<React.ComponentPropsWithoutRef<typeof Check
1933
1933
  declare const Checkbox: React.ForwardRefExoticComponent<CheckboxProps & React.RefAttributes<HTMLButtonElement>>;
1934
1934
 
1935
1935
  declare const checkboxGroupVariants: (props?: ({
1936
- appearance?: "plain" | "card" | null | undefined;
1936
+ appearance?: "card" | "plain" | null | undefined;
1937
1937
  orientation?: "horizontal" | "vertical" | null | undefined;
1938
1938
  columns?: "one" | "two" | "three" | null | undefined;
1939
1939
  } & class_variance_authority_types.ClassProp) | undefined) => string;
@@ -1954,16 +1954,16 @@ interface CheckboxGroupProps extends Omit<React.HTMLAttributes<HTMLDivElement>,
1954
1954
  declare const CheckboxGroup: React.ForwardRefExoticComponent<CheckboxGroupProps & React.RefAttributes<HTMLDivElement>>;
1955
1955
 
1956
1956
  declare const radioGroupVariants: (props?: ({
1957
- appearance?: "plain" | "card" | null | undefined;
1957
+ appearance?: "card" | "plain" | null | undefined;
1958
1958
  orientation?: "horizontal" | "vertical" | null | undefined;
1959
1959
  columns?: "one" | "two" | "three" | null | undefined;
1960
1960
  } & class_variance_authority_types.ClassProp) | undefined) => string;
1961
1961
  declare const radioVariants: (props?: ({
1962
- appearance?: "plain" | "card" | null | undefined;
1962
+ appearance?: "card" | "plain" | null | undefined;
1963
1963
  density?: "compact" | "comfortable" | null | undefined;
1964
1964
  } & class_variance_authority_types.ClassProp) | undefined) => string;
1965
1965
  declare const radioControlVariants: (props?: ({
1966
- appearance?: "plain" | "card" | null | undefined;
1966
+ appearance?: "card" | "plain" | null | undefined;
1967
1967
  } & class_variance_authority_types.ClassProp) | undefined) => string;
1968
1968
  type RadioGroupAppearance = NonNullable<VariantProps<typeof radioGroupVariants>["appearance"]>;
1969
1969
  type RadioGroupColumns = NonNullable<VariantProps<typeof radioGroupVariants>["columns"]>;
package/dist/index.js CHANGED
@@ -3020,12 +3020,12 @@ var tabsListVariants = cva8(
3020
3020
  {
3021
3021
  variant: "line",
3022
3022
  orientation: "horizontal",
3023
- className: "border-b border-[var(--border-subtle)] gap-[var(--space-6)] overflow-x-auto overflow-y-hidden"
3023
+ className: "group/tabs-list relative border-b border-[var(--border-subtle)] gap-[var(--space-6)] overflow-x-auto overflow-y-hidden"
3024
3024
  },
3025
3025
  {
3026
3026
  variant: "line",
3027
3027
  orientation: "vertical",
3028
- className: "border-r border-[var(--border-subtle)] gap-[var(--space-2)] pr-[var(--space-4)] max-sm:flex-row max-sm:gap-[var(--space-6)] max-sm:overflow-x-auto max-sm:overflow-y-hidden max-sm:border-r-0 max-sm:border-b max-sm:pr-0"
3028
+ className: "group/tabs-list relative border-r border-[var(--border-subtle)] gap-[var(--space-2)] pr-[var(--space-4)] max-sm:flex-row max-sm:gap-[var(--space-6)] max-sm:overflow-x-auto max-sm:overflow-y-hidden max-sm:border-r-0 max-sm:border-b max-sm:pr-0"
3029
3029
  },
3030
3030
  {
3031
3031
  variant: "contained",
@@ -3080,6 +3080,12 @@ var tabsTriggerVariants = cva8(
3080
3080
  orientation: "horizontal",
3081
3081
  className: "mx-[-2px] px-[2px] first:ml-0 last:mr-0 focus-visible:ring-0 focus-visible:inset-ring-2 focus-visible:inset-ring-[var(--focus-ring)]"
3082
3082
  },
3083
+ // Once the list's indicator has measured the active trigger, it replaces the static ::after
3084
+ // underline. Before hydration (or without the indicator) the ::after stays.
3085
+ {
3086
+ variant: "line",
3087
+ className: "group-data-[indicator=ready]/tabs-list:after:hidden"
3088
+ },
3083
3089
  {
3084
3090
  variant: "line",
3085
3091
  orientation: "vertical",
@@ -3092,6 +3098,35 @@ var tabsTriggerVariants = cva8(
3092
3098
  }
3093
3099
  }
3094
3100
  );
3101
+ var tabsIndicatorVariants = cva8(
3102
+ [
3103
+ "pointer-events-none absolute invisible bg-[var(--text-primary)]",
3104
+ // transition-property stays none (duration alone would animate every property, visibility included)
3105
+ // until data-motion turns on the per-orientation transition below.
3106
+ "transition-none duration-[var(--dur-base)] ease-[var(--ease-standard)]",
3107
+ "group-data-[indicator=ready]/tabs-list:visible"
3108
+ ].join(" "),
3109
+ {
3110
+ variants: {
3111
+ orientation: {
3112
+ horizontal: "bottom-[-1px] left-0 h-[2px] w-[var(--tabs-indicator-width)] [transform:translateX(var(--tabs-indicator-left))] motion-safe:data-[motion=on]:transition-[transform,width]",
3113
+ // Below `sm` the vertical list becomes a horizontal row, so the indicator follows the horizontal rule.
3114
+ vertical: [
3115
+ "max-sm:bottom-[-1px] max-sm:left-0 max-sm:h-[2px] max-sm:w-[var(--tabs-indicator-width)] max-sm:[transform:translateX(var(--tabs-indicator-left))] max-sm:motion-safe:data-[motion=on]:transition-[transform,width]",
3116
+ "sm:top-0 sm:right-[-1px] sm:h-[var(--tabs-indicator-height)] sm:w-[2px] sm:[transform:translateY(var(--tabs-indicator-top))] sm:motion-safe:data-[motion=on]:transition-[transform,height]"
3117
+ ].join(" ")
3118
+ }
3119
+ },
3120
+ defaultVariants: {
3121
+ orientation: "horizontal"
3122
+ }
3123
+ }
3124
+ );
3125
+ var tabsLabelReserveClassName = [
3126
+ "inline-flex flex-col",
3127
+ "before:invisible before:h-0 before:overflow-hidden before:font-medium before:content-[attr(data-label)]",
3128
+ "after:invisible after:h-0 after:overflow-hidden after:font-bold after:content-[attr(data-label)]"
3129
+ ].join(" ");
3095
3130
  var tabsBadgeVariants = cva8(
3096
3131
  [
3097
3132
  "inline-flex h-[18px] min-w-[18px] items-center justify-center",
@@ -3106,6 +3141,62 @@ var tabsBadgeVariants = cva8(
3106
3141
  "group-data-[state=active]:border-[var(--border-strong)]"
3107
3142
  ].join(" ")
3108
3143
  );
3144
+ function useTabsIndicator(enabled, activeKey, tabsKey, listRef, indicatorRef) {
3145
+ const placedRef = React17.useRef(null);
3146
+ const previousKeyRef = React17.useRef(null);
3147
+ const place = React17.useCallback(
3148
+ (animate) => {
3149
+ const list = listRef.current;
3150
+ const indicator = indicatorRef.current;
3151
+ if (!list || !indicator) return;
3152
+ const trigger = list.querySelector(':scope > [data-slot="tabs-trigger"][data-state="active"]');
3153
+ if (!trigger) {
3154
+ delete list.dataset.indicator;
3155
+ placedRef.current = null;
3156
+ return;
3157
+ }
3158
+ const { offsetLeft, offsetWidth, offsetTop, offsetHeight } = trigger;
3159
+ const placed = `${offsetLeft} ${offsetWidth} ${offsetTop} ${offsetHeight}`;
3160
+ if (placed === placedRef.current) return;
3161
+ placedRef.current = placed;
3162
+ if (animate && list.dataset.indicator === "ready") indicator.dataset.motion = "on";
3163
+ else delete indicator.dataset.motion;
3164
+ indicator.style.setProperty("--tabs-indicator-left", `${offsetLeft}px`);
3165
+ indicator.style.setProperty("--tabs-indicator-width", `${offsetWidth}px`);
3166
+ indicator.style.setProperty("--tabs-indicator-top", `${offsetTop}px`);
3167
+ indicator.style.setProperty("--tabs-indicator-height", `${offsetHeight}px`);
3168
+ list.dataset.indicator = "ready";
3169
+ },
3170
+ [listRef, indicatorRef]
3171
+ );
3172
+ React17.useLayoutEffect(() => {
3173
+ if (!enabled) {
3174
+ placedRef.current = null;
3175
+ previousKeyRef.current = null;
3176
+ return;
3177
+ }
3178
+ const previousKey = previousKeyRef.current;
3179
+ previousKeyRef.current = activeKey;
3180
+ place(previousKey !== null && previousKey !== activeKey);
3181
+ }, [enabled, activeKey, tabsKey, place]);
3182
+ React17.useEffect(() => {
3183
+ const list = listRef.current;
3184
+ if (!enabled || !list) return;
3185
+ let cancelled = false;
3186
+ const observer = typeof ResizeObserver === "undefined" ? null : new ResizeObserver(() => place(false));
3187
+ if (observer) {
3188
+ observer.observe(list);
3189
+ list.querySelectorAll(':scope > [data-slot="tabs-trigger"]').forEach((trigger) => observer.observe(trigger));
3190
+ }
3191
+ document.fonts?.ready.then(() => {
3192
+ if (!cancelled) place(false);
3193
+ });
3194
+ return () => {
3195
+ cancelled = true;
3196
+ observer?.disconnect();
3197
+ };
3198
+ }, [enabled, tabsKey, place, listRef]);
3199
+ }
3109
3200
  function formatTabBadge(value) {
3110
3201
  return value > 99 ? "99+" : String(value);
3111
3202
  }
@@ -3128,55 +3219,77 @@ var Tabs = React17.forwardRef(
3128
3219
  orientation = "horizontal",
3129
3220
  variant = "line",
3130
3221
  ...props
3131
- }, ref) => /* @__PURE__ */ jsxs17(
3132
- TabsPrimitive.Root,
3133
- {
3134
- ref,
3135
- "data-slot": "tabs",
3136
- value: activeKey,
3137
- onValueChange: onChange,
3138
- orientation,
3139
- className: cn(tabsRootVariants({ orientation }), className),
3140
- ...props,
3141
- children: [
3142
- /* @__PURE__ */ jsx18(
3143
- TabsPrimitive.List,
3144
- {
3145
- "data-slot": "tabs-list",
3146
- "data-variant": variant,
3147
- className: cn(tabsListVariants({ variant, orientation }), listClassName),
3148
- children: tabs.map((tab) => /* @__PURE__ */ jsxs17(
3149
- TabsPrimitive.Trigger,
3150
- {
3151
- id: getTabId(baseId, tab.key),
3152
- value: tab.key,
3153
- disabled: tab.disabled,
3154
- "aria-controls": getPanelId(baseId, tab.key),
3155
- "data-slot": "tabs-trigger",
3156
- "data-variant": variant,
3157
- className: cn("group", tabsTriggerVariants({ variant, orientation }), triggerClassName),
3158
- children: [
3159
- /* @__PURE__ */ jsx18("span", { children: tab.label }),
3160
- tab.badge !== void 0 && tab.badge > 0 ? /* @__PURE__ */ jsx18("span", { "aria-hidden": "true", "data-slot": "tabs-badge", className: tabsBadgeVariants(), children: formatTabBadge(tab.badge) }) : null,
3161
- tab.disabled ? /* @__PURE__ */ jsx18(
3162
- Lock,
3163
- {
3164
- "aria-hidden": "true",
3165
- "data-slot": "tabs-disabled-icon",
3166
- className: "size-[14px] shrink-0 text-text-disabled",
3167
- strokeWidth: 2
3222
+ }, ref) => {
3223
+ const listRef = React17.useRef(null);
3224
+ const indicatorRef = React17.useRef(null);
3225
+ const isLine = variant === "line";
3226
+ const tabsKey = tabs.map((tab) => tab.key).join("\0");
3227
+ useTabsIndicator(isLine, activeKey, tabsKey, listRef, indicatorRef);
3228
+ return /* @__PURE__ */ jsxs17(
3229
+ TabsPrimitive.Root,
3230
+ {
3231
+ ref,
3232
+ "data-slot": "tabs",
3233
+ value: activeKey,
3234
+ onValueChange: onChange,
3235
+ orientation,
3236
+ className: cn(tabsRootVariants({ orientation }), className),
3237
+ ...props,
3238
+ children: [
3239
+ /* @__PURE__ */ jsxs17(
3240
+ TabsPrimitive.List,
3241
+ {
3242
+ ref: listRef,
3243
+ "data-slot": "tabs-list",
3244
+ "data-variant": variant,
3245
+ className: cn(tabsListVariants({ variant, orientation }), listClassName),
3246
+ children: [
3247
+ tabs.map((tab) => /* @__PURE__ */ jsxs17(
3248
+ TabsPrimitive.Trigger,
3249
+ {
3250
+ id: getTabId(baseId, tab.key),
3251
+ value: tab.key,
3252
+ disabled: tab.disabled,
3253
+ "aria-controls": getPanelId(baseId, tab.key),
3254
+ "data-slot": "tabs-trigger",
3255
+ "data-variant": variant,
3256
+ className: cn("group", tabsTriggerVariants({ variant, orientation }), triggerClassName),
3257
+ children: [
3258
+ isLine ? /* @__PURE__ */ jsx18("span", { "data-label": tab.label, className: tabsLabelReserveClassName, children: tab.label }) : /* @__PURE__ */ jsx18("span", { children: tab.label }),
3259
+ tab.badge !== void 0 && tab.badge > 0 ? /* @__PURE__ */ jsx18("span", { "aria-hidden": "true", "data-slot": "tabs-badge", className: tabsBadgeVariants(), children: formatTabBadge(tab.badge) }) : null,
3260
+ tab.disabled ? /* @__PURE__ */ jsx18(
3261
+ Lock,
3262
+ {
3263
+ "aria-hidden": "true",
3264
+ "data-slot": "tabs-disabled-icon",
3265
+ className: "size-[14px] shrink-0 text-text-disabled",
3266
+ strokeWidth: 2
3267
+ }
3268
+ ) : null
3269
+ ]
3270
+ },
3271
+ tab.key
3272
+ )),
3273
+ isLine ? /* @__PURE__ */ jsx18(
3274
+ "span",
3275
+ {
3276
+ ref: indicatorRef,
3277
+ "aria-hidden": "true",
3278
+ "data-slot": "tabs-indicator",
3279
+ className: tabsIndicatorVariants({ orientation }),
3280
+ onTransitionEnd: (event) => {
3281
+ if (event.target === event.currentTarget) delete event.currentTarget.dataset.motion;
3168
3282
  }
3169
- ) : null
3170
- ]
3171
- },
3172
- tab.key
3173
- ))
3174
- }
3175
- ),
3176
- children
3177
- ]
3178
- }
3179
- )
3283
+ }
3284
+ ) : null
3285
+ ]
3286
+ }
3287
+ ),
3288
+ children
3289
+ ]
3290
+ }
3291
+ );
3292
+ }
3180
3293
  );
3181
3294
  Tabs.displayName = "Tabs";
3182
3295
  var TabsContent = React17.forwardRef(({ className, tabKey, baseId, ...props }, ref) => /* @__PURE__ */ jsx18(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@olundot/ui",
3
- "version": "0.20.0-next.1",
3
+ "version": "0.20.0-next.2",
4
4
  "description": "OLUN. shared React UI components built on OLUN design tokens",
5
5
  "license": "MIT",
6
6
  "keywords": [
@@ -62,6 +62,7 @@
62
62
  "@types/node": "^24.10.0",
63
63
  "@types/react": "^19.2.14",
64
64
  "@types/react-dom": "^19.2.3",
65
+ "esbuild": "^0.27.7",
65
66
  "jsdom": "^30.1.1",
66
67
  "react": "^19.2.6",
67
68
  "react-dom": "^19.2.6",