@olundot/ui 0.20.0 → 0.20.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,37 @@
1
1
  # @olundot/ui
2
2
 
3
+ ## 0.20.2
4
+
5
+ ### Patch Changes
6
+
7
+ - 8a28822: `FilterToolbar layout="grid"`은 640px 미만에서 `filters`도 시트로 보낸다. 줄에는 검색, 접힌 필터 버튼, `actions`만 남고, 시트에는 `filters`를 순서대로 둔 뒤 `collapsible`을 둔다.
8
+
9
+ - 폭을 재기 전 첫 화면에서도 640px 미만이면 줄 필터를 펼치지 않는다. 컨테이너 쿼리로 숨기고, 폭을 잰 뒤에는 줄에 그리지 않는다.
10
+ - `collapsible` 없이 `filters`만 있어도 640px 미만에서 버튼과 시트가 생긴다.
11
+ - `activeLineFilterCount`: `filters` 중 적용한 조건 수. 640px 미만에서 버튼 배지, 버튼 이름, 적용 개수 줄의 개수에 더한다.
12
+ - `onClearLineFilters`: 640px 미만의 "해제"가 `onClearFilters`와 함께 부른다.
13
+ - 격자의 `filters`와 `collapsible` 컨트롤은 줄과 시트를 오가며 새로 그려지므로 값은 `value`로 넘긴다. `defaultValue`로 둔 값은 시트를 닫으면 남지 않는다.
14
+ - 두 prop을 주지 않으면 640px 미만 개수와 "해제"는 `collapsible` 조건만 다룬다. 640px 이상 격자와 `layout="flow"`는 그대로다.
15
+ - 격자에서 `collapseBelow="never"`도 `collapsible`을 줄에 펼치지 않는다.
16
+ - 격자는 툴바 폭을 컨테이너 쿼리와 같은 기준(내용 상자, CSS `zoom` 전)으로 잰다.
17
+
18
+ ## 0.20.1
19
+
20
+ ### Patch Changes
21
+
22
+ - 0ab7288: 구조형 `Dialog`에 `hideTitle`과 목록 영역(`list`, `listHeight`, `listFit`, `listLabel`)을 더했다.
23
+
24
+ - `hideTitle`: 제목을 화면에서 숨기고(`sr-only`) 다이얼로그의 접근 이름으로 둔다. 머리에는 닫기 버튼만 남는다. 상자 윗선에서 본문 첫 줄까지 간격, 닫기 버튼 자리, 1024px 미만의 머리 끌어내리기, 첫 포커스 규칙은 그대로다. `description`과 함께 줄 수 없다(타입 오류).
25
+ - `list`: 본문 아래, 푸터 위의 목록 영역. 목록 영역만 스크롤하고 본문은 고정된다. 안개는 본문과 같은 방식으로 목록 영역 위·아래 끝에 그린다. 폼 모드에서도 `form` 안에 있다. 목록 안 입력도 첫 포커스 대상이다.
26
+ - `listHeight`: 항목 부분 높이 `"sm"` 216px, `"md"` 460px(기본), `"lg"` 628px. 화면이 낮으면 상자 최대 높이 안에서 줄어들되 항목 부분은 112px(목록 영역 124px)에서 멈추고, 더 낮으면 본문이 스크롤한다. 머리와 푸터는 늘 상자 안에 있다.
27
+ - `listFit`: `"max"`(기본)는 항목만큼 커지다가 `listHeight`에서 멈추고, `"fixed"`는 항목 수와 관계없이 `listHeight`다.
28
+ - `listLabel`: 목록 안에 포커스 받는 요소가 없을 때 영역이 `tabIndex={0}`인 `region`이 되며 붙는 이름. 기본값은 `title`.
29
+ - 타입 `DialogListHeight`, `DialogListFit`을 내보낸다. `DialogProps`는 `hideTitle`과 `description`을 나누려고 interface에서 type으로 바뀌었다. `interface X extends DialogProps`로 확장하던 코드는 `type X = DialogProps & …`로 바꾼다.
30
+ - 첫 포커스는 탭으로 닿는 입력만 고른다. 숨겨지거나(`hidden`, `inert`, `aria-hidden` 조상) 비활성이거나 `tabIndex`가 음수인 입력은 건너뛴다. 폼 안 `Checkbox`가 남기는 전달용 입력이 여기에 든다. Dialog, Sheet, AlertDialog에 함께 적용된다.
31
+ - `hideTitle`과 `list` 없이 쓰는 Dialog의 모양은 그대로다. 동작은 위 첫 포커스 규칙만 바뀐다.
32
+
33
+ - e695c9d: `IconButton`에 `done`과 `tooltip`을 더했다. `done`은 이미 끝난 동작을 `success` 아이콘과 ✓ 배지로 보이고, 초점은 받지만 `onClick`을 부르지 않는다. `tooltip`은 아이콘만 있는 버튼에 말풍선을 붙인다. Provider가 없어도 되고 `title`을 대신한다. 배지 색에 `success`를 더했다.
34
+
3
35
  ## 0.20.0
4
36
 
5
37
  ### Minor Changes
package/dist/index.d.ts CHANGED
@@ -66,7 +66,7 @@ type GhostDefaultActionStyle = {
66
66
  declare const buttonCva: (props?: ({
67
67
  appearance?: "filled" | "outline" | "ghost" | "link" | null | undefined;
68
68
  tone?: "primary" | "neutral" | "danger" | null | undefined;
69
- size?: "xs" | "sm" | "md" | "lg" | "xl" | "default" | null | undefined;
69
+ size?: "default" | "md" | "xs" | "sm" | "lg" | "xl" | null | undefined;
70
70
  } & class_variance_authority_types.ClassProp) | undefined) => string;
71
71
  type ButtonVariantsOptions = Parameters<typeof buttonCva>[0];
72
72
  declare function buttonVariants(options?: ButtonVariantsOptions): string;
@@ -101,13 +101,13 @@ type CopyButtonProps = Omit<ButtonProps, 'appearance' | 'children' | 'loading' |
101
101
  declare const CopyButton: React.ForwardRefExoticComponent<CopyButtonProps & React.RefAttributes<HTMLButtonElement>>;
102
102
 
103
103
  declare const inputVariants: (props?: ({
104
- size?: "sm" | "md" | "lg" | null | undefined;
104
+ size?: "md" | "sm" | "lg" | null | undefined;
105
105
  fullWidth?: boolean | null | undefined;
106
106
  surface?: SurfaceLevel | null | undefined;
107
107
  } & class_variance_authority_types.ClassProp) | undefined) => string;
108
108
  declare const inputMessageVariants: (props?: ({
109
- state?: "error" | "success" | "default" | null | undefined;
110
- size?: "sm" | "md" | "lg" | null | undefined;
109
+ state?: "default" | "error" | "success" | null | undefined;
110
+ size?: "md" | "sm" | "lg" | null | undefined;
111
111
  } & class_variance_authority_types.ClassProp) | undefined) => string;
112
112
  type InputState = NonNullable<VariantProps<typeof inputMessageVariants>["state"]>;
113
113
  /** Canonical size type (0.2.0+). Use `size` prop with these values. */
@@ -155,11 +155,11 @@ interface InputProps extends Omit<React.InputHTMLAttributes<HTMLInputElement>, "
155
155
  declare const Input: React.ForwardRefExoticComponent<InputProps & React.RefAttributes<HTMLInputElement>>;
156
156
 
157
157
  declare const fileUploadZoneVariants: (props?: ({
158
- size?: "compact" | "default" | null | undefined;
158
+ size?: "default" | "compact" | null | undefined;
159
159
  surface?: SurfaceLevel | null | undefined;
160
160
  } & class_variance_authority_types.ClassProp) | undefined) => string;
161
161
  declare const fileUploadIconVariants: (props?: ({
162
- size?: "compact" | "default" | null | undefined;
162
+ size?: "default" | "compact" | null | undefined;
163
163
  } & class_variance_authority_types.ClassProp) | undefined) => string;
164
164
  interface FileUploadZoneProps extends Omit<React.LabelHTMLAttributes<HTMLLabelElement>, "onChange">, VariantProps<typeof fileUploadZoneVariants> {
165
165
  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?: "sm" | "md" | "lg" | null | undefined;
206
+ size?: "md" | "sm" | "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?: "error" | "success" | "default" | null | undefined;
211
+ state?: "default" | "error" | "success" | 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?: "sm" | "md" | "lg" | null | undefined;
260
+ size?: "md" | "sm" | "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?: "error" | "success" | "default" | null | undefined;
267
- size?: "sm" | "md" | "lg" | null | undefined;
266
+ state?: "default" | "error" | "success" | null | undefined;
267
+ size?: "md" | "sm" | "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. */
@@ -700,6 +700,10 @@ declare const FormLayout: React.ForwardRefExoticComponent<FormLayoutProps & Reac
700
700
  * blur (`--scrim-blur`). Without a preset (no prop, no `<html data-scrim>`) the scrim is `--scrim`.
701
701
  */
702
702
  type OverlayScrim = "blur" | "dim";
703
+ /** List area height preset (Dialog `listHeight`). */
704
+ type OverlayListHeight = "sm" | "md" | "lg";
705
+ /** `"max"`: as tall as the items, up to the preset. `"fixed"`: the preset whatever the item count. */
706
+ type OverlayListFit = "max" | "fixed";
703
707
 
704
708
  /**
705
709
  * @deprecated The structured Dialog (`title`) is a bottom sheet below 1024px on its own.
@@ -713,25 +717,36 @@ type OverlayScrim = "blur" | "dim";
713
717
  type DialogMobilePresentation = "center" | "sheet";
714
718
  /** Structured Dialog width: 420 · 560 · 720 · 960. */
715
719
  type DialogSize = "sm" | "md" | "lg" | "xl";
716
- /**
717
- * Structured Dialog (spec §3.2): a center window for forms, pickers and long content. X + footer,
718
- * outside click does not close, Escape does (not while submitting), the body scrolls inside the max
719
- * height, a bottom sheet below 1024px (OVERLAY_POLICY.dialog).
720
- */
721
- interface DialogProps {
720
+ /** List area height (`listHeight`): items up to 216 · 460 · 628px. */
721
+ type DialogListHeight = OverlayListHeight;
722
+ /** List area height mode (`listFit`). */
723
+ type DialogListFit = OverlayListFit;
724
+ interface DialogBaseProps {
722
725
  open: boolean;
723
726
  onOpenChange: (open: boolean) => void;
724
727
  /** Heading, also the dialog's accessible name. Its presence selects the structured mode. */
725
728
  title: string;
726
- /** Line under the title, also the accessible description. */
727
- description?: string;
728
729
  /**
729
730
  * Box width: 420 · 560 · 720 · 960. Default `"md"`. `"xl"` is for content 720 cannot hold, such as
730
731
  * two columns. Other widths are not supported.
731
732
  */
732
733
  size?: DialogSize;
733
- /** Body. Scrolls inside the box's max height. */
734
+ /** Body. Scrolls inside the box's max height; with `list` it stays still above the list area. */
734
735
  children: React.ReactNode;
736
+ /**
737
+ * List area under the body and above the footer, for items to pick from. Only this area scrolls;
738
+ * the body above it (filters, search, select all) stays still. Empty and loading states go here too.
739
+ */
740
+ list?: React.ReactNode;
741
+ /** List area height preset: items up to 216 · 460 · 628px. Default `"md"`. A low screen shrinks it inside the box max height. */
742
+ listHeight?: DialogListHeight;
743
+ /**
744
+ * `"max"` (default): as tall as the items, up to `listHeight`. `"fixed"`: `listHeight` whatever the
745
+ * item count, for lists a search or filter resizes.
746
+ */
747
+ listFit?: DialogListFit;
748
+ /** Accessible name of the list area, used when nothing inside takes focus. Default: `title`. */
749
+ listLabel?: string;
735
750
  /** Footer content (buttons). Ignored in form mode, which renders `[cancel][submit]`. */
736
751
  footer?: React.ReactNode;
737
752
  /**
@@ -754,6 +769,24 @@ interface DialogProps {
754
769
  /** Layout of the body content only. It never reaches the box: width comes from `size`. */
755
770
  className?: string;
756
771
  }
772
+ /**
773
+ * Structured Dialog (spec §3.2): a center window for forms, pickers and long content. X + footer,
774
+ * outside click does not close, Escape does (not while submitting), the body scrolls inside the max
775
+ * height, a bottom sheet below 1024px (OVERLAY_POLICY.dialog).
776
+ */
777
+ type DialogProps = DialogBaseProps & ({
778
+ /** Shows the title in the header. Default. */
779
+ hideTitle?: false;
780
+ /** Line under the title, also the accessible description. */
781
+ description?: string;
782
+ } | {
783
+ /**
784
+ * Hides the title on screen and keeps it as the accessible name; the header shows the X only.
785
+ * Use it only when the body's first line says what the dialog is about.
786
+ */
787
+ hideTitle: true;
788
+ description?: never;
789
+ });
757
790
  /** Legacy root: the Radix Dialog Root for `DialogContent` composition (no `title`). */
758
791
  type DialogRootProps = React.ComponentProps<typeof Dialog$1.Root> & {
759
792
  title?: never;
@@ -870,6 +903,10 @@ interface FilterToolbarProps extends React.HTMLAttributes<HTMLDivElement> {
870
903
  collapseBelow?: FilterToolbarCollapseBelow;
871
904
  activeFilterCount?: number;
872
905
  onClearFilters?: () => void;
906
+ /** grid: applied conditions among `filters`. Under 640px the count adds them. */
907
+ activeLineFilterCount?: number;
908
+ /** grid: under 640px 해제 calls it with `onClearFilters`. */
909
+ onClearLineFilters?: () => void;
873
910
  collapsedLabel?: string;
874
911
  resultSummary?: React.ReactNode;
875
912
  clearAction?: React.ReactNode;
@@ -1678,7 +1715,7 @@ interface StatusDotProps extends Omit<React.HTMLAttributes<HTMLSpanElement>, 'ch
1678
1715
  declare const StatusDot: React.ForwardRefExoticComponent<StatusDotProps & React.RefAttributes<HTMLSpanElement>>;
1679
1716
 
1680
1717
  declare const notificationBadgeVariants: (props?: ({
1681
- size?: "sm" | "md" | null | undefined;
1718
+ size?: "md" | "sm" | null | undefined;
1682
1719
  placement?: "none" | "inline" | "corner" | null | undefined;
1683
1720
  } & class_variance_authority_types.ClassProp) | undefined) => string;
1684
1721
  type NotificationBadgeSize = NonNullable<VariantProps<typeof notificationBadgeVariants>['size']>;
@@ -1691,13 +1728,13 @@ declare const NotificationBadge: React.ForwardRefExoticComponent<NotificationBad
1691
1728
 
1692
1729
  declare const chipVariants: (props?: ({
1693
1730
  variant?: "filter" | "choice" | "removable" | "status-filter" | null | undefined;
1694
- tone?: "muted" | "accent" | "warning" | "success" | "neutral" | "danger" | null | undefined;
1695
- size?: "sm" | "md" | "lg" | null | undefined;
1731
+ tone?: "accent" | "neutral" | "danger" | "success" | "warning" | "muted" | null | undefined;
1732
+ size?: "md" | "sm" | "lg" | null | undefined;
1696
1733
  pressed?: boolean | null | undefined;
1697
1734
  } & class_variance_authority_types.ClassProp) | undefined) => string;
1698
1735
  declare const chipCountVariants: (props?: ({
1699
- tone?: "muted" | "accent" | "warning" | "success" | "neutral" | "danger" | null | undefined;
1700
- size?: "sm" | "md" | "lg" | null | undefined;
1736
+ tone?: "accent" | "neutral" | "danger" | "success" | "warning" | "muted" | null | undefined;
1737
+ size?: "md" | "sm" | "lg" | null | undefined;
1701
1738
  } & class_variance_authority_types.ClassProp) | undefined) => string;
1702
1739
  type ChipVariant = NonNullable<VariantProps<typeof chipVariants>["variant"]>;
1703
1740
  type ChipTone = NonNullable<VariantProps<typeof chipVariants>["tone"]>;
@@ -1711,7 +1748,7 @@ interface ChipProps extends Omit<React.ButtonHTMLAttributes<HTMLButtonElement>,
1711
1748
  declare const Chip: React.ForwardRefExoticComponent<ChipProps & React.RefAttributes<HTMLButtonElement>>;
1712
1749
 
1713
1750
  declare const emptyStateBaseVariants: (props?: ({
1714
- size?: "sm" | "md" | "lg" | null | undefined;
1751
+ size?: "md" | "sm" | "lg" | null | undefined;
1715
1752
  bordered?: boolean | null | undefined;
1716
1753
  } & class_variance_authority_types.ClassProp) | undefined) => string;
1717
1754
  type EmptyStateTone = "neutral" | "muted" | "raised";
@@ -1723,7 +1760,7 @@ interface EmptyStateVariantOptions extends VariantProps<typeof emptyStateBaseVar
1723
1760
  }
1724
1761
  declare function emptyStateVariants({ size, tone, surface, bordered, className, }?: EmptyStateVariantOptions): string;
1725
1762
  declare const emptyStateIconVariants: (props?: ({
1726
- size?: "sm" | "md" | "lg" | null | undefined;
1763
+ size?: "md" | "sm" | "lg" | null | undefined;
1727
1764
  } & class_variance_authority_types.ClassProp) | undefined) => string;
1728
1765
  type EmptyStateSize = NonNullable<VariantProps<typeof emptyStateBaseVariants>["size"]>;
1729
1766
  interface EmptyStateProps extends Omit<React.HTMLAttributes<HTMLDivElement>, "title">, VariantProps<typeof emptyStateBaseVariants> {
@@ -1749,7 +1786,7 @@ interface EmptyStateProps extends Omit<React.HTMLAttributes<HTMLDivElement>, "ti
1749
1786
  declare const EmptyState: React.ForwardRefExoticComponent<EmptyStateProps & React.RefAttributes<HTMLDivElement>>;
1750
1787
 
1751
1788
  declare const errorStateVariants: (props?: ({
1752
- size?: "sm" | "md" | "lg" | null | undefined;
1789
+ size?: "md" | "sm" | "lg" | null | undefined;
1753
1790
  surface?: SurfaceLevel | null | undefined;
1754
1791
  } & class_variance_authority_types.ClassProp) | undefined) => string;
1755
1792
  interface ErrorStateProps extends Omit<React.HTMLAttributes<HTMLDivElement>, "title">, VariantProps<typeof errorStateVariants> {
@@ -1769,7 +1806,7 @@ declare const skeletonVariants: (props?: ({
1769
1806
  shape?: "text" | "block" | "circle" | null | undefined;
1770
1807
  } & class_variance_authority_types.ClassProp) | undefined) => string;
1771
1808
  declare const spinnerVariants: (props?: ({
1772
- size?: "sm" | "md" | "lg" | 16 | 24 | 32 | null | undefined;
1809
+ size?: "md" | "sm" | "lg" | 16 | 24 | 32 | null | undefined;
1773
1810
  } & class_variance_authority_types.ClassProp) | undefined) => string;
1774
1811
  interface SkeletonProps extends React.HTMLAttributes<HTMLDivElement>, VariantProps<typeof skeletonVariants> {
1775
1812
  }
@@ -1802,7 +1839,7 @@ interface SpinnerProps extends React.HTMLAttributes<HTMLSpanElement> {
1802
1839
  declare const Spinner: React.ForwardRefExoticComponent<SpinnerProps & React.RefAttributes<HTMLSpanElement>>;
1803
1840
 
1804
1841
  declare const iconButtonCva: (props?: ({
1805
- size?: "xs" | "sm" | "md" | "lg" | "xl" | null | undefined;
1842
+ size?: "md" | "xs" | "sm" | "lg" | "xl" | null | undefined;
1806
1843
  pressed?: boolean | null | undefined;
1807
1844
  appearance?: "filled" | "outline" | "ghost" | null | undefined;
1808
1845
  tone?: "primary" | "neutral" | "danger" | null | undefined;
@@ -1811,7 +1848,7 @@ type IconButtonSize = NonNullable<VariantProps<typeof iconButtonCva>["size"]>;
1811
1848
  type IconButtonVariantsOptions = NonNullable<Parameters<typeof iconButtonCva>[0]>;
1812
1849
  declare function iconButtonVariants(options?: IconButtonVariantsOptions): string;
1813
1850
  declare const iconButtonBadgeVariants: (props?: ({
1814
- tone?: "error" | "info" | null | undefined;
1851
+ tone?: "error" | "success" | "info" | null | undefined;
1815
1852
  } & class_variance_authority_types.ClassProp) | undefined) => string;
1816
1853
  type IconButtonBadgeTone = NonNullable<VariantProps<typeof iconButtonBadgeVariants>["tone"]>;
1817
1854
  type IconButtonIcon = React.ComponentType<React.SVGProps<SVGSVGElement> & {
@@ -1836,17 +1873,41 @@ type IconButtonTextEnd = {
1836
1873
  iconPosition?: "start";
1837
1874
  menuIndicator: true;
1838
1875
  };
1876
+ type IconButtonTooltipSide = "top" | "bottom";
1877
+ type IconButtonTooltipOn = {
1878
+ /**
1879
+ * Icon only. A tooltip on pointer hover and keyboard focus, in place of `title`: `true` shows the
1880
+ * string `aria-label`, a string shows that string. No Provider needed.
1881
+ */
1882
+ tooltip: true | string;
1883
+ /** Tooltip side. Default `top`. */
1884
+ tooltipSide?: IconButtonTooltipSide;
1885
+ title?: never;
1886
+ };
1887
+ type IconButtonTooltip = {
1888
+ tooltip?: undefined;
1889
+ tooltipSide?: never;
1890
+ } | (IconButtonTooltipOn & {
1891
+ tooltip: string;
1892
+ }) | (IconButtonTooltipOn & {
1893
+ tooltip: true;
1894
+ "aria-label": string;
1895
+ });
1896
+ type IconButtonNoTooltip = {
1897
+ tooltip?: never;
1898
+ tooltipSide?: never;
1899
+ };
1839
1900
  type IconButtonContent = (IconButtonAccessibleName & {
1840
1901
  label?: never;
1841
1902
  children?: never;
1842
1903
  menuIndicator?: never;
1843
- }) | (IconButtonOptionalName & {
1904
+ } & IconButtonTooltip) | (IconButtonOptionalName & {
1844
1905
  label: React.ReactNode;
1845
1906
  children?: never;
1846
- } & IconButtonTextEnd) | (IconButtonOptionalName & {
1907
+ } & IconButtonTextEnd & IconButtonNoTooltip) | (IconButtonOptionalName & {
1847
1908
  children: React.ReactNode;
1848
1909
  label?: never;
1849
- } & IconButtonTextEnd);
1910
+ } & IconButtonTextEnd & IconButtonNoTooltip);
1850
1911
  type IconButtonStyleProps = GhostDefaultActionStyle & {
1851
1912
  /** Button's scale: `xs` 28 · `sm` 30 · `md` 32 · `lg` 36 · `xl` 40. */
1852
1913
  size?: IconButtonSize | null;
@@ -1860,6 +1921,12 @@ type IconButtonProps = Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, "aria
1860
1921
  iconPosition?: "start" | "end";
1861
1922
  /** Like Button: the spinner takes the icon's place and the button is disabled. */
1862
1923
  loading?: boolean;
1924
+ /**
1925
+ * The action is already done: success icon, and a ✓ badge unless `badge` is given. Keeps
1926
+ * focus but never calls `onClick`. The accessible name says it is done. `disabled` and
1927
+ * `loading` win over it; it wins over `pressed`.
1928
+ */
1929
+ done?: boolean;
1863
1930
  /**
1864
1931
  * Trailing ▾ for a menu trigger, with text and a start icon only. The trigger (DropdownMenu
1865
1932
  * etc.) supplies `aria-haspopup`.
@@ -2018,7 +2085,7 @@ interface ActionFooterProps extends Omit<React.HTMLAttributes<HTMLDivElement>, '
2018
2085
  declare const ActionFooter: React.ForwardRefExoticComponent<ActionFooterProps & React.RefAttributes<HTMLDivElement>>;
2019
2086
 
2020
2087
  declare const toastVariants: (props?: ({
2021
- variant?: "error" | "warning" | "info" | "success" | "default" | null | undefined;
2088
+ variant?: "default" | "error" | "success" | "info" | "warning" | null | undefined;
2022
2089
  } & class_variance_authority_types.ClassProp) | undefined) => string;
2023
2090
  declare const toastViewportVariants: (props?: class_variance_authority_types.ClassProp | undefined) => string;
2024
2091
  type ToastVariant = NonNullable<VariantProps<typeof toastVariants>['variant']>;
@@ -2072,19 +2139,19 @@ interface ToastProviderProps extends React.HTMLAttributes<HTMLDivElement> {
2072
2139
  declare function ToastProvider({ children, maxToasts, className, ...props }: ToastProviderProps): react_jsx_runtime.JSX.Element;
2073
2140
 
2074
2141
  declare const otpFieldVariants: (props?: ({
2075
- size?: "compact" | "default" | null | undefined;
2142
+ size?: "default" | "compact" | null | undefined;
2076
2143
  } & class_variance_authority_types.ClassProp) | undefined) => string;
2077
2144
  declare const otpFieldGroupVariants: (props?: ({
2078
- size?: "compact" | "default" | null | undefined;
2145
+ size?: "default" | "compact" | null | undefined;
2079
2146
  fluid?: boolean | null | undefined;
2080
2147
  } & class_variance_authority_types.ClassProp) | undefined) => string;
2081
2148
  declare const otpCellVariants: (props?: ({
2082
- size?: "compact" | "default" | null | undefined;
2149
+ size?: "default" | "compact" | null | undefined;
2083
2150
  fluid?: boolean | null | undefined;
2084
2151
  surface?: SurfaceLevel | null | undefined;
2085
2152
  } & class_variance_authority_types.ClassProp) | undefined) => string;
2086
2153
  declare const otpFieldMessageVariants: (props?: ({
2087
- state?: "error" | "default" | null | undefined;
2154
+ state?: "default" | "error" | null | undefined;
2088
2155
  } & class_variance_authority_types.ClassProp) | undefined) => string;
2089
2156
  type OTPFieldSize = NonNullable<VariantProps<typeof otpCellVariants>["size"]>;
2090
2157
  interface OTPFieldProps extends Omit<React.FieldsetHTMLAttributes<HTMLFieldSetElement>, "onChange" | "children" | "defaultValue" | "value">, VariantProps<typeof otpFieldVariants> {
@@ -2111,10 +2178,10 @@ interface OTPFieldProps extends Omit<React.FieldsetHTMLAttributes<HTMLFieldSetEl
2111
2178
  declare const OTPField: React.ForwardRefExoticComponent<OTPFieldProps & React.RefAttributes<HTMLFieldSetElement>>;
2112
2179
 
2113
2180
  declare const searchFieldVariants: (props?: ({
2114
- size?: "sm" | "md" | "lg" | null | undefined;
2181
+ size?: "md" | "sm" | "lg" | null | undefined;
2115
2182
  } & class_variance_authority_types.ClassProp) | undefined) => string;
2116
2183
  declare const searchFieldInputVariants: (props?: ({
2117
- size?: "sm" | "md" | "lg" | null | undefined;
2184
+ size?: "md" | "sm" | "lg" | null | undefined;
2118
2185
  hasClear?: boolean | null | undefined;
2119
2186
  surface?: SurfaceLevel | null | undefined;
2120
2187
  } & class_variance_authority_types.ClassProp) | undefined) => string;
@@ -2138,7 +2205,7 @@ declare const checkboxVariants: (props?: ({
2138
2205
  } & class_variance_authority_types.ClassProp) | undefined) => string;
2139
2206
  declare const checkboxRootVariants: (props?: class_variance_authority_types.ClassProp | undefined) => string;
2140
2207
  declare const checkboxBoxVariants: (props?: ({
2141
- size?: "sm" | "md" | null | undefined;
2208
+ size?: "md" | "sm" | null | undefined;
2142
2209
  } & class_variance_authority_types.ClassProp) | undefined) => string;
2143
2210
  declare const checkboxGroupItemVariants: (props?: ({
2144
2211
  appearance?: "card" | "plain" | null | undefined;
@@ -2345,4 +2412,4 @@ interface OrganizationLogoProps extends Omit<SVGProps<SVGSVGElement>, "width" |
2345
2412
  }
2346
2413
  declare function OrganizationLogo({ organization, size, className, style, role, "aria-label": ariaLabel, "aria-hidden": ariaHidden, ...props }: OrganizationLogoProps): react_jsx_runtime.JSX.Element;
2347
2414
 
2348
- export { ActionFooter, type ActionFooterAction, type ActionFooterProps, type ActionFooterStatus, ActionMenu, type ActionMenuGroup, type ActionMenuItem, type ActionMenuProps, ActionSheet, type ActionSheetIconProps, type ActionSheetItem, type ActionSheetProps, Alert, AlertDialog, type AlertDialogProps, type AlertDialogTone, type AlertEmphasis, type AlertMarker, type AlertProps, type AlertVariant, Avatar, AvatarFallback, type AvatarFallbackProps, AvatarImage, Badge, type BadgeProps, type BadgeTone, Breadcrumb, type BreadcrumbItem, type BreadcrumbProps, BulkActionBar, type BulkActionBarAction, type BulkActionBarProps, Button, type ButtonProps, type ButtonSize, Card, CardContent, CardDescription, CardFooter, CardHeader, type CardProps, CardProse, CardTitle, type CardVariant, type CardVariantOptions, Checkbox, type CheckboxCheckedState, CheckboxGroup, type CheckboxGroupAppearance, type CheckboxGroupColumns, CheckboxGroupContext, type CheckboxGroupContextValue, type CheckboxGroupDensity, type CheckboxGroupOrientation, type CheckboxGroupProps, type CheckboxProps, type CheckboxSize, Chip, type ChipProps, type ChipSize, type ChipTone, type ChipVariant, ConfirmDialog, type ConfirmDialogProps, ContextMenu, type ContextMenuPosition, type ContextMenuProps, CopyButton, type CopyButtonProps, DataLayout, type DataLayoutProps, DataTable, type DataTableColumn, type DataTableLinkProps, type DataTableMobileCardContent, type DataTableMobileMetaLine, type DataTableProps, type DataTableRowActions, type DataTableSort, type DataTableVirtualHandle, type DataTableVirtualOptions, type DataTableVirtualRange, DetailLayout, type DetailLayoutProps, Dialog, DialogClose, DialogContent, DialogDescription, DialogEyebrow, DialogFooter, DialogHeader, type DialogMobilePresentation, DialogOverlay, DialogPortal, type DialogProps, DialogRawContent, type DialogSize, DialogTitle, DialogTrigger, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, EmptyState, type EmptyStateLayout, type EmptyStateProps, type EmptyStateSize, type EmptyStateTone, type EmptyStateVariantOptions, ErrorState, type ErrorStateProps, FileUploadZone, type FileUploadZoneProps, FilterToolbar, type FilterToolbarAppliedFilter, type FilterToolbarCollapseBelow, type FilterToolbarLayout, type FilterToolbarProps, FormField, type FormFieldProps, type FormFieldRenderProps, FormLayout, type FormLayoutProps, type FormLayoutSection, FormModal, type FormModalProps, IconButton, type IconButtonBadgeTone, type IconButtonProps, type IconButtonSize, InfoModal, type InfoModalProps, InlineAlert, type InlineAlertProps, Input, type InputProps, type InputSize, type InputSizeLegacy, type InputState, Label, type LabelProps, ListCard, type ListCardActions, type ListCardProps, ModeToggle, type ModeToggleOption, type ModeToggleProps, NotificationBadge, type NotificationBadgePlacement, type NotificationBadgeProps, type NotificationBadgeSize, OTPField, type OTPFieldProps, type OTPFieldSize, OlunLogo, OlunSymbol, type OlunTheme, type OlunThemeDuration, type OlunThemeState, OrganizationLogo, type OrganizationLogoId, type OrganizationLogoProps, type OverlayScrim, PageHeader, type PageHeaderProps, PageSizeSelect, type PageSizeSelectProps, type PageSizeSelectSize, Pagination, type PaginationProps, type PaginationSize, PermissionContext, type PermissionEvaluator, type PermissionFallback, PermissionGuard, type PermissionGuardProps, PermissionProvider, type PermissionProviderProps, type PermissionScope, type PermissionState, Popover, PopoverAnchor, PopoverArrow, PopoverClose, PopoverContent, type PopoverContentProps, type PopoverProps, PopoverTrigger, Progress, type ProgressProps, type ProgressStatus, Radio, RadioGroup, type RadioGroupAppearance, type RadioGroupColumns, type RadioGroupDensity, type RadioGroupOrientation, type RadioGroupProps, type RadioProps, ScrollArea, ScrollBar, SearchField, type SearchFieldProps, type SearchFieldSize, Select, type SelectAppearance, type SelectOption, type SelectProps, type SelectSize, type SelectState, type SelectVariant, type SemanticRole, Separator, type SetOlunThemeOptions, Sheet, SheetBody, SheetClose, SheetContent, SheetDescription, SheetDrawer, type SheetDrawerProps, SheetFooter, SheetHeader, type SheetMotionConfig, type SheetMotionDuration, type SheetMotionEasing, type SheetMotionType, type SheetProps, type SheetSize, SheetTitle, SheetTrigger, Skeleton, type SkeletonProps, SkeletonRow, type SkeletonRowColumn, type SkeletonRowProps, SkeletonText, type SkeletonTextProps, Spinner, type SpinnerProps, StatusBadge, type StatusBadgeEmphasis, type StatusBadgeProps, type StatusBadgeSize, type StatusBadgeStatus, StatusDot, type StatusDotProps, type StatusDotTone, type SurfaceLevel, Switch, type SwitchProps, TABLE_PAGE_SIZE_OPTIONS, type TabItem, TablePagination, type TablePaginationProps, type TablePaginationVisibility, Tabs, TabsContent, type TabsProps, type TabsVariant, Textarea, type TextareaProps, type TextareaSize, type TextareaSizeLegacy, type TextareaState, ThemeFab, type ThemeFabProps, ThemeToggle, Toast, type ToastAction, type ToastItem, type ToastOptions, ToastProvider, ToastRoot, type ToastVariant, ToastViewport, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, type TreeItemRowVariantProps, type TreeItemState, type TreeNode, TreeView, type TreeViewProps, TwoTierBadge, type TwoTierBadgeEmphasis, type TwoTierBadgeProps, type TwoTierBadgeSize, type TwoTierBadgeTone, type UseOlunThemeValue, WizardLayout, type WizardLayoutProps, type WizardStep, type WizardStepState, actionFooterStatusVariants, actionFooterVariants, buttonVariants, cardVariants, checkboxBoxVariants, checkboxFieldVariants, checkboxGroupBoxVariants, checkboxGroupItemVariants, checkboxGroupVariants, checkboxRootVariants, checkboxVariants, chipCountVariants, chipVariants, cn, dataTableRowVariants, dataTableVariants, emptyStateIconVariants, emptyStateVariants, errorStateVariants, evaluatePermission, fileUploadIconVariants, fileUploadZoneVariants, formFieldControlStackClassName, formFieldDescriptionClassName, formFieldErrorClassName, formFieldLabelClassName, formFieldRequiredClassName, formFieldVariants, getOlunThemeState, getToastDuration, iconButtonBadgeVariants, iconButtonVariants, inputMessageVariants, inputVariants, limitToasts, modeToggleItemVariants, modeToggleVariants, notificationBadgeVariants, otpCellVariants, otpFieldGroupVariants, otpFieldMessageVariants, otpFieldVariants, paginationControlVariants, paginationVariants, radioControlVariants, radioGroupVariants, radioVariants, searchFieldInputVariants, searchFieldVariants, selectMessageVariants, selectVariants, setOlunTheme, shouldHideTablePagination, skeletonVariants, spinnerVariants, tabsBadgeVariants, tabsListVariants, tabsRootVariants, tabsTriggerVariants, textareaMessageVariants, textareaVariants, toast, toastVariants, toastViewportVariants, treeItemRowVariants, treeViewVariants, useOlunTheme, usePermission };
2415
+ export { ActionFooter, type ActionFooterAction, type ActionFooterProps, type ActionFooterStatus, ActionMenu, type ActionMenuGroup, type ActionMenuItem, type ActionMenuProps, ActionSheet, type ActionSheetIconProps, type ActionSheetItem, type ActionSheetProps, Alert, AlertDialog, type AlertDialogProps, type AlertDialogTone, type AlertEmphasis, type AlertMarker, type AlertProps, type AlertVariant, Avatar, AvatarFallback, type AvatarFallbackProps, AvatarImage, Badge, type BadgeProps, type BadgeTone, Breadcrumb, type BreadcrumbItem, type BreadcrumbProps, BulkActionBar, type BulkActionBarAction, type BulkActionBarProps, Button, type ButtonProps, type ButtonSize, Card, CardContent, CardDescription, CardFooter, CardHeader, type CardProps, CardProse, CardTitle, type CardVariant, type CardVariantOptions, Checkbox, type CheckboxCheckedState, CheckboxGroup, type CheckboxGroupAppearance, type CheckboxGroupColumns, CheckboxGroupContext, type CheckboxGroupContextValue, type CheckboxGroupDensity, type CheckboxGroupOrientation, type CheckboxGroupProps, type CheckboxProps, type CheckboxSize, Chip, type ChipProps, type ChipSize, type ChipTone, type ChipVariant, ConfirmDialog, type ConfirmDialogProps, ContextMenu, type ContextMenuPosition, type ContextMenuProps, CopyButton, type CopyButtonProps, DataLayout, type DataLayoutProps, DataTable, type DataTableColumn, type DataTableLinkProps, type DataTableMobileCardContent, type DataTableMobileMetaLine, type DataTableProps, type DataTableRowActions, type DataTableSort, type DataTableVirtualHandle, type DataTableVirtualOptions, type DataTableVirtualRange, DetailLayout, type DetailLayoutProps, Dialog, DialogClose, DialogContent, DialogDescription, DialogEyebrow, DialogFooter, DialogHeader, type DialogListFit, type DialogListHeight, type DialogMobilePresentation, DialogOverlay, DialogPortal, type DialogProps, DialogRawContent, type DialogSize, DialogTitle, DialogTrigger, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, EmptyState, type EmptyStateLayout, type EmptyStateProps, type EmptyStateSize, type EmptyStateTone, type EmptyStateVariantOptions, ErrorState, type ErrorStateProps, FileUploadZone, type FileUploadZoneProps, FilterToolbar, type FilterToolbarAppliedFilter, type FilterToolbarCollapseBelow, type FilterToolbarLayout, type FilterToolbarProps, FormField, type FormFieldProps, type FormFieldRenderProps, FormLayout, type FormLayoutProps, type FormLayoutSection, FormModal, type FormModalProps, IconButton, type IconButtonBadgeTone, type IconButtonProps, type IconButtonSize, InfoModal, type InfoModalProps, InlineAlert, type InlineAlertProps, Input, type InputProps, type InputSize, type InputSizeLegacy, type InputState, Label, type LabelProps, ListCard, type ListCardActions, type ListCardProps, ModeToggle, type ModeToggleOption, type ModeToggleProps, NotificationBadge, type NotificationBadgePlacement, type NotificationBadgeProps, type NotificationBadgeSize, OTPField, type OTPFieldProps, type OTPFieldSize, OlunLogo, OlunSymbol, type OlunTheme, type OlunThemeDuration, type OlunThemeState, OrganizationLogo, type OrganizationLogoId, type OrganizationLogoProps, type OverlayScrim, PageHeader, type PageHeaderProps, PageSizeSelect, type PageSizeSelectProps, type PageSizeSelectSize, Pagination, type PaginationProps, type PaginationSize, PermissionContext, type PermissionEvaluator, type PermissionFallback, PermissionGuard, type PermissionGuardProps, PermissionProvider, type PermissionProviderProps, type PermissionScope, type PermissionState, Popover, PopoverAnchor, PopoverArrow, PopoverClose, PopoverContent, type PopoverContentProps, type PopoverProps, PopoverTrigger, Progress, type ProgressProps, type ProgressStatus, Radio, RadioGroup, type RadioGroupAppearance, type RadioGroupColumns, type RadioGroupDensity, type RadioGroupOrientation, type RadioGroupProps, type RadioProps, ScrollArea, ScrollBar, SearchField, type SearchFieldProps, type SearchFieldSize, Select, type SelectAppearance, type SelectOption, type SelectProps, type SelectSize, type SelectState, type SelectVariant, type SemanticRole, Separator, type SetOlunThemeOptions, Sheet, SheetBody, SheetClose, SheetContent, SheetDescription, SheetDrawer, type SheetDrawerProps, SheetFooter, SheetHeader, type SheetMotionConfig, type SheetMotionDuration, type SheetMotionEasing, type SheetMotionType, type SheetProps, type SheetSize, SheetTitle, SheetTrigger, Skeleton, type SkeletonProps, SkeletonRow, type SkeletonRowColumn, type SkeletonRowProps, SkeletonText, type SkeletonTextProps, Spinner, type SpinnerProps, StatusBadge, type StatusBadgeEmphasis, type StatusBadgeProps, type StatusBadgeSize, type StatusBadgeStatus, StatusDot, type StatusDotProps, type StatusDotTone, type SurfaceLevel, Switch, type SwitchProps, TABLE_PAGE_SIZE_OPTIONS, type TabItem, TablePagination, type TablePaginationProps, type TablePaginationVisibility, Tabs, TabsContent, type TabsProps, type TabsVariant, Textarea, type TextareaProps, type TextareaSize, type TextareaSizeLegacy, type TextareaState, ThemeFab, type ThemeFabProps, ThemeToggle, Toast, type ToastAction, type ToastItem, type ToastOptions, ToastProvider, ToastRoot, type ToastVariant, ToastViewport, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, type TreeItemRowVariantProps, type TreeItemState, type TreeNode, TreeView, type TreeViewProps, TwoTierBadge, type TwoTierBadgeEmphasis, type TwoTierBadgeProps, type TwoTierBadgeSize, type TwoTierBadgeTone, type UseOlunThemeValue, WizardLayout, type WizardLayoutProps, type WizardStep, type WizardStepState, actionFooterStatusVariants, actionFooterVariants, buttonVariants, cardVariants, checkboxBoxVariants, checkboxFieldVariants, checkboxGroupBoxVariants, checkboxGroupItemVariants, checkboxGroupVariants, checkboxRootVariants, checkboxVariants, chipCountVariants, chipVariants, cn, dataTableRowVariants, dataTableVariants, emptyStateIconVariants, emptyStateVariants, errorStateVariants, evaluatePermission, fileUploadIconVariants, fileUploadZoneVariants, formFieldControlStackClassName, formFieldDescriptionClassName, formFieldErrorClassName, formFieldLabelClassName, formFieldRequiredClassName, formFieldVariants, getOlunThemeState, getToastDuration, iconButtonBadgeVariants, iconButtonVariants, inputMessageVariants, inputVariants, limitToasts, modeToggleItemVariants, modeToggleVariants, notificationBadgeVariants, otpCellVariants, otpFieldGroupVariants, otpFieldMessageVariants, otpFieldVariants, paginationControlVariants, paginationVariants, radioControlVariants, radioGroupVariants, radioVariants, searchFieldInputVariants, searchFieldVariants, selectMessageVariants, selectVariants, setOlunTheme, shouldHideTablePagination, skeletonVariants, spinnerVariants, tabsBadgeVariants, tabsListVariants, tabsRootVariants, tabsTriggerVariants, textareaMessageVariants, textareaVariants, toast, toastVariants, toastViewportVariants, treeItemRowVariants, treeViewVariants, useOlunTheme, usePermission };
package/dist/index.js CHANGED
@@ -4053,9 +4053,31 @@ import { Dialog as DialogPrimitive } from "radix-ui";
4053
4053
  // src/components/ui/icon-button.tsx
4054
4054
  import * as React25 from "react";
4055
4055
  import { cva as cva10 } from "class-variance-authority";
4056
- import { ChevronDown as ChevronDown4, Loader2 as Loader23 } from "lucide-react";
4056
+ import { Check as Check3, ChevronDown as ChevronDown4, Loader2 as Loader23 } from "lucide-react";
4057
4057
  import { jsx as jsx22, jsxs as jsxs20 } from "react/jsx-runtime";
4058
4058
  var pressedNeutralClassName = "border-[var(--border-strong)] bg-[var(--action-selected)] text-foreground";
4059
+ var doneClassName = "cursor-default text-[var(--success)]";
4060
+ var doneRestPaint = (classes) => classes.split(" ").filter(
4061
+ (name) => !name.startsWith("hover:") && (!name.startsWith("aria-disabled:") || name.startsWith("aria-disabled:active:"))
4062
+ ).join(" ");
4063
+ var tabbing = false;
4064
+ var tabListeners = 0;
4065
+ var markTab = (event) => {
4066
+ if (event.key !== "Tab") return;
4067
+ tabbing = true;
4068
+ setTimeout(() => {
4069
+ tabbing = false;
4070
+ }, 0);
4071
+ };
4072
+ function useTabMark(enabled) {
4073
+ React25.useEffect(() => {
4074
+ if (!enabled) return;
4075
+ if (tabListeners++ === 0) document.addEventListener("keydown", markTab, true);
4076
+ return () => {
4077
+ if (--tabListeners === 0) document.removeEventListener("keydown", markTab, true);
4078
+ };
4079
+ }, [enabled]);
4080
+ }
4059
4081
  var iconButtonCva = cva10(
4060
4082
  [
4061
4083
  "olun-icon-button relative inline-flex cursor-pointer shrink-0 items-center justify-center before:absolute before:content-['']",
@@ -4123,7 +4145,8 @@ var iconButtonBadgeVariants = cva10(
4123
4145
  variants: {
4124
4146
  tone: {
4125
4147
  info: "bg-[var(--accent-pop)] text-[var(--accent-on-fill)]",
4126
- error: "bg-[var(--error)] text-[var(--error-contrast)]"
4148
+ error: "bg-[var(--error)] text-[var(--error-contrast)]",
4149
+ success: "bg-[var(--success)] text-[var(--success-contrast)]"
4127
4150
  }
4128
4151
  },
4129
4152
  defaultVariants: {
@@ -4145,21 +4168,35 @@ var IconButton = React25.forwardRef(
4145
4168
  title,
4146
4169
  "aria-label": ariaLabel,
4147
4170
  "aria-labelledby": ariaLabelledBy,
4171
+ "aria-describedby": ariaDescribedBy,
4148
4172
  iconPosition = "start",
4149
4173
  loading,
4174
+ done,
4150
4175
  menuIndicator,
4151
4176
  disabled,
4152
4177
  label,
4153
4178
  children,
4179
+ tooltip,
4180
+ tooltipSide = "top",
4181
+ onClick,
4154
4182
  ...props
4155
4183
  }, ref) => {
4156
4184
  const resolvedSize = size ?? "md";
4157
- const isToggle = pressed !== void 0;
4158
- const isPressed = pressed === true;
4159
- const hoverTitle = title ?? (typeof ariaLabel === "string" ? ariaLabel : void 0);
4185
+ const isDisabled = disabled || loading;
4186
+ const isDone = done === true && !isDisabled;
4187
+ const isToggle = pressed !== void 0 && !isDone;
4188
+ const isPressed = pressed === true && !isDone;
4189
+ const tooltipText = tooltip === true ? ariaLabel : tooltip;
4190
+ const hoverTitle = tooltip !== void 0 ? void 0 : title ?? (typeof ariaLabel === "string" ? ariaLabel : void 0);
4160
4191
  const text = label ?? children;
4161
4192
  const hasText = text !== void 0 && text !== null && text !== false;
4162
- const isDisabled = disabled || loading;
4193
+ const resolvedBadge = badge === void 0 && isDone ? /* @__PURE__ */ jsx22(Check3, { strokeWidth: 3 }) : badge;
4194
+ const resolvedBadgeTone = badge === void 0 && isDone ? "success" : badgeTone;
4195
+ const [tooltipOpen, setTooltipOpen] = React25.useState(false);
4196
+ useTabMark(tooltip !== void 0);
4197
+ React25.useEffect(() => {
4198
+ if (isDisabled) setTooltipOpen(false);
4199
+ }, [isDisabled]);
4163
4200
  const style = resolveActionStyle(appearance, tone, "ghost");
4164
4201
  const icon = loading ? /* @__PURE__ */ jsx22(
4165
4202
  Loader23,
@@ -4179,7 +4216,14 @@ var IconButton = React25.forwardRef(
4179
4216
  strokeWidth: axisIcon[resolvedSize].strokeWidth
4180
4217
  }
4181
4218
  );
4182
- return /* @__PURE__ */ jsxs20(
4219
+ const paint = hasText ? [
4220
+ "olun-icon-button",
4221
+ buttonVariants({ ...style, size: resolvedSize }),
4222
+ isPressed && style.tone === "neutral" && pressedNeutralClassName,
4223
+ // The badge sits on the corner; Button xs/sm are already relative.
4224
+ resolvedBadge !== void 0 && "relative"
4225
+ ] : iconButtonVariants({ appearance, tone, size: resolvedSize, pressed: isPressed });
4226
+ const button = /* @__PURE__ */ jsxs20(
4183
4227
  "button",
4184
4228
  {
4185
4229
  ref,
@@ -4191,24 +4235,24 @@ var IconButton = React25.forwardRef(
4191
4235
  "data-icon-position": hasText ? iconPosition : void 0,
4192
4236
  "data-pressed": isToggle ? String(isPressed) : void 0,
4193
4237
  "data-loading": loading ? "true" : void 0,
4238
+ "data-done": isDone ? "true" : void 0,
4194
4239
  "aria-busy": loading ? true : void 0,
4195
- "aria-disabled": isDisabled ? true : void 0,
4240
+ "aria-disabled": isDisabled || isDone ? true : void 0,
4196
4241
  disabled: isDisabled,
4197
4242
  "aria-label": ariaLabel,
4198
4243
  "aria-labelledby": ariaLabelledBy,
4244
+ "aria-describedby": ariaDescribedBy,
4199
4245
  "aria-pressed": isToggle ? isPressed : void 0,
4200
4246
  title: hoverTitle,
4201
- className: cn(
4202
- hasText ? [
4203
- "olun-icon-button",
4204
- buttonVariants({ ...style, size: resolvedSize }),
4205
- isPressed && style.tone === "neutral" && pressedNeutralClassName,
4206
- // The badge sits on the corner; Button xs/sm are already relative.
4207
- badge !== void 0 && "relative"
4208
- ] : iconButtonVariants({ appearance, tone, size: resolvedSize, pressed: isPressed }),
4209
- className
4210
- ),
4247
+ className: cn(isDone ? [doneRestPaint(cn(paint)), doneClassName] : paint, className),
4211
4248
  type: type ?? "button",
4249
+ onClick: (event) => {
4250
+ if (isDone) {
4251
+ event.preventDefault();
4252
+ return;
4253
+ }
4254
+ onClick?.(event);
4255
+ },
4212
4256
  children: [
4213
4257
  hasText && iconPosition === "end" ? null : icon,
4214
4258
  hasText ? text : null,
@@ -4222,19 +4266,41 @@ var IconButton = React25.forwardRef(
4222
4266
  strokeWidth: axisIcon[resolvedSize].strokeWidth
4223
4267
  }
4224
4268
  ) : null,
4225
- badge !== void 0 ? /* @__PURE__ */ jsx22(
4269
+ resolvedBadge !== void 0 ? /* @__PURE__ */ jsx22(
4226
4270
  "span",
4227
4271
  {
4228
4272
  "aria-hidden": "true",
4229
4273
  "data-slot": "icon-button-badge",
4230
- "data-badge-tone": badgeTone,
4231
- className: iconButtonBadgeVariants({ tone: badgeTone }),
4232
- children: badge
4274
+ "data-badge-tone": resolvedBadgeTone,
4275
+ className: iconButtonBadgeVariants({ tone: resolvedBadgeTone }),
4276
+ children: resolvedBadge
4233
4277
  }
4234
4278
  ) : null
4235
4279
  ]
4236
4280
  }
4237
4281
  );
4282
+ if (tooltip === void 0) return button;
4283
+ return /* @__PURE__ */ jsx22(TooltipProvider, { children: /* @__PURE__ */ jsxs20(
4284
+ Tooltip,
4285
+ {
4286
+ open: tooltipOpen && !isDisabled,
4287
+ onOpenChange: (open) => setTooltipOpen(open && !isDisabled),
4288
+ children: [
4289
+ /* @__PURE__ */ jsx22(
4290
+ TooltipTrigger,
4291
+ {
4292
+ asChild: true,
4293
+ onFocus: (event) => {
4294
+ const root = event.currentTarget.getRootNode();
4295
+ if (!tabbing || root.activeElement !== event.currentTarget) event.preventDefault();
4296
+ },
4297
+ children: button
4298
+ }
4299
+ ),
4300
+ /* @__PURE__ */ jsx22(TooltipContent, { side: tooltipSide, children: tooltipText })
4301
+ ]
4302
+ }
4303
+ ) });
4238
4304
  }
4239
4305
  );
4240
4306
  IconButton.displayName = "IconButton";
@@ -4362,14 +4428,32 @@ var OverlayScrimLayer = React26.forwardRef(
4362
4428
  }
4363
4429
  );
4364
4430
  OverlayScrimLayer.displayName = "OverlayScrimLayer";
4431
+ var TABBABLE_CANDIDATE = 'a[href], button, input, select, textarea, [tabindex], [contenteditable]:not([contenteditable="false"])';
4432
+ var INPUT_CANDIDATE = "input, textarea, select";
4433
+ function isTabbable(element) {
4434
+ if (element.matches(":disabled") || element instanceof HTMLInputElement && element.type === "hidden") return false;
4435
+ if (element.hasAttribute("tabindex") && element.tabIndex < 0) return false;
4436
+ if (element.closest('[hidden], [inert], [aria-hidden="true"]')) return false;
4437
+ if (typeof element.checkVisibility === "function") return element.checkVisibility({ visibilityProperty: true });
4438
+ const view = element.ownerDocument.defaultView;
4439
+ if (!view) return true;
4440
+ if (["hidden", "collapse"].includes(view.getComputedStyle(element).visibility)) return false;
4441
+ for (let node = element; node; node = node.parentElement) {
4442
+ if (view.getComputedStyle(node).display === "none") return false;
4443
+ }
4444
+ return true;
4445
+ }
4446
+ var firstTabbable = (root, selector) => [...root.querySelectorAll(selector)].find(isTabbable);
4365
4447
  function focusBodyInput(event) {
4366
4448
  const surface = event.target instanceof HTMLElement ? event.target : null;
4367
- const input = surface?.querySelector(
4368
- '[data-slot="overlay-body"] :is(input, textarea, select):not([disabled]):not([type="hidden"])'
4369
- );
4370
- if (input) {
4371
- event.preventDefault();
4449
+ if (!surface) return;
4450
+ for (const input of surface.querySelectorAll(`:is([data-slot="overlay-body"], [data-slot="overlay-list"]) :is(${INPUT_CANDIDATE})`)) {
4451
+ if (!isTabbable(input)) continue;
4372
4452
  input.focus();
4453
+ if (input.ownerDocument.activeElement === input) {
4454
+ event.preventDefault();
4455
+ return;
4456
+ }
4373
4457
  }
4374
4458
  }
4375
4459
  var headingClassName = {
@@ -4387,6 +4471,7 @@ function OverlayHeader({
4387
4471
  caption,
4388
4472
  actions,
4389
4473
  closeLabel = "\uB2EB\uAE30",
4474
+ titleHidden = false,
4390
4475
  dragHandleProps,
4391
4476
  id,
4392
4477
  className
@@ -4410,7 +4495,7 @@ function OverlayHeader({
4410
4495
  children: [
4411
4496
  /* @__PURE__ */ jsxs21("div", { className: "flex min-w-0 flex-1 flex-col gap-[var(--space-2)]", children: [
4412
4497
  caption != null ? /* @__PURE__ */ jsx24("div", { "data-slot": "overlay-caption", className: "flex flex-wrap items-center gap-[var(--space-2)]", children: caption }) : null,
4413
- /* @__PURE__ */ jsx24(Title, { "data-slot": "overlay-title", className: headingClassName[policy.heading], children: title }),
4498
+ /* @__PURE__ */ jsx24(Title, { "data-slot": "overlay-title", className: cn(headingClassName[policy.heading], titleHidden && "sr-only"), children: title }),
4414
4499
  description != null ? /* @__PURE__ */ jsx24(Description, { "data-slot": "overlay-description", className: descriptionClassName[policy.heading], children: description }) : null
4415
4500
  ] }),
4416
4501
  actions != null || policy.closeButton === "visible" ? /* @__PURE__ */ jsxs21("div", { "data-slot": "overlay-header-actions", className: "flex shrink-0 items-center gap-[var(--space-1)]", children: [
@@ -4439,28 +4524,18 @@ var fogClassName = cn(
4439
4524
  "pointer-events-none sticky z-[1] block h-[var(--space-6)] shrink-0 opacity-0",
4440
4525
  "from-[var(--bg-raised)] to-transparent"
4441
4526
  );
4442
- function OverlayBody({ kind, className, children, ...props }) {
4443
- const { scroll } = OVERLAY_POLICY[kind];
4444
- if (scroll !== "body") {
4445
- return /* @__PURE__ */ jsx24(
4446
- "div",
4447
- {
4448
- "data-slot": "overlay-body",
4449
- className: cn(
4450
- "min-w-0 px-[var(--space-6)] pt-[var(--space-4)] text-[14px] leading-relaxed text-text-secondary",
4451
- scroll === "list" && "min-h-0 flex-1 overflow-y-auto px-0",
4452
- className
4453
- ),
4454
- ...props,
4455
- children
4456
- }
4457
- );
4458
- }
4527
+ function FogScroller({
4528
+ scrollerRef,
4529
+ className,
4530
+ contentClassName,
4531
+ children,
4532
+ ...props
4533
+ }) {
4459
4534
  return /* @__PURE__ */ jsxs21(
4460
4535
  "div",
4461
4536
  {
4462
- "data-slot": "overlay-body",
4463
- className: "relative flex min-h-0 flex-1 flex-col overflow-y-auto [container-type:scroll-state]",
4537
+ ref: scrollerRef,
4538
+ className: cn("relative flex min-h-0 flex-col overflow-y-auto [container-type:scroll-state]", className),
4464
4539
  ...props,
4465
4540
  children: [
4466
4541
  /* @__PURE__ */ jsx24(
@@ -4471,7 +4546,7 @@ function OverlayBody({ kind, className, children, ...props }) {
4471
4546
  className: cn(fogClassName, "top-0 -mb-[var(--space-6)] bg-linear-to-b", "[@container_scroll-state(scrollable:top)]:opacity-100")
4472
4547
  }
4473
4548
  ),
4474
- /* @__PURE__ */ jsx24("div", { className: cn("min-w-0 px-[var(--space-6)] py-[var(--space-4)]", className), children }),
4549
+ /* @__PURE__ */ jsx24("div", { className: contentClassName, children }),
4475
4550
  /* @__PURE__ */ jsx24(
4476
4551
  "span",
4477
4552
  {
@@ -4484,6 +4559,90 @@ function OverlayBody({ kind, className, children, ...props }) {
4484
4559
  }
4485
4560
  );
4486
4561
  }
4562
+ function OverlayBody({ kind, fixed = false, className, children, ...props }) {
4563
+ const { scroll } = OVERLAY_POLICY[kind];
4564
+ if (fixed) {
4565
+ return /* @__PURE__ */ jsx24(
4566
+ "div",
4567
+ {
4568
+ "data-slot": "overlay-body",
4569
+ className: cn(
4570
+ "relative z-[2] -mb-[var(--space-1)] min-h-0 min-w-0 shrink overflow-y-auto px-[var(--space-6)] pt-[var(--space-4)] pb-[var(--space-1)]",
4571
+ className
4572
+ ),
4573
+ ...props,
4574
+ children
4575
+ }
4576
+ );
4577
+ }
4578
+ if (scroll !== "body") {
4579
+ return /* @__PURE__ */ jsx24(
4580
+ "div",
4581
+ {
4582
+ "data-slot": "overlay-body",
4583
+ className: cn(
4584
+ "min-w-0 px-[var(--space-6)] pt-[var(--space-4)] text-[14px] leading-relaxed text-text-secondary",
4585
+ scroll === "list" && "min-h-0 flex-1 overflow-y-auto px-0",
4586
+ className
4587
+ ),
4588
+ ...props,
4589
+ children
4590
+ }
4591
+ );
4592
+ }
4593
+ return /* @__PURE__ */ jsx24(FogScroller, { "data-slot": "overlay-body", className: "flex-1", contentClassName: cn("min-w-0 px-[var(--space-6)] py-[var(--space-4)]", className), ...props, children });
4594
+ }
4595
+ var listBoxClassName = {
4596
+ max: {
4597
+ sm: "basis-[content] max-h-[calc(216px+var(--space-3)+var(--space-4))]",
4598
+ md: "basis-[content] max-h-[calc(460px+var(--space-3)+var(--space-4))]",
4599
+ lg: "basis-[content] max-h-[calc(628px+var(--space-3)+var(--space-4))]"
4600
+ },
4601
+ fixed: {
4602
+ sm: "basis-[calc(216px+var(--space-3)+var(--space-4))]",
4603
+ md: "basis-[calc(460px+var(--space-3)+var(--space-4))]",
4604
+ lg: "basis-[calc(628px+var(--space-3)+var(--space-4))]"
4605
+ }
4606
+ };
4607
+ var listFloorClassName = "h-[calc(112px+var(--space-3))]";
4608
+ function OverlayList({ height = "md", fit = "max", label, children }) {
4609
+ const ref = React26.useRef(null);
4610
+ const [hasFocusable, setHasFocusable] = React26.useState(true);
4611
+ React26.useLayoutEffect(() => {
4612
+ const node = ref.current;
4613
+ if (!node) return;
4614
+ const check = () => setHasFocusable(firstTabbable(node, TABBABLE_CANDIDATE) != null);
4615
+ check();
4616
+ let frame = 0;
4617
+ const observer = new MutationObserver(() => {
4618
+ frame ||= requestAnimationFrame(() => {
4619
+ frame = 0;
4620
+ check();
4621
+ });
4622
+ });
4623
+ observer.observe(node, {
4624
+ childList: true,
4625
+ subtree: true,
4626
+ attributes: true,
4627
+ attributeFilter: ["aria-hidden", "class", "contenteditable", "disabled", "hidden", "href", "inert", "style", "tabindex", "type"]
4628
+ });
4629
+ return () => {
4630
+ observer.disconnect();
4631
+ cancelAnimationFrame(frame);
4632
+ };
4633
+ }, []);
4634
+ return /* @__PURE__ */ jsx24("div", { "data-slot": "overlay-list-box", className: cn("flex shrink-[10000] flex-col", listFloorClassName, listBoxClassName[fit][height]), children: /* @__PURE__ */ jsx24(
4635
+ FogScroller,
4636
+ {
4637
+ scrollerRef: ref,
4638
+ "data-slot": "overlay-list",
4639
+ className: "flex-auto outline-none focus-visible:inset-ring-2 focus-visible:inset-ring-[var(--focus-ring)]",
4640
+ contentClassName: "min-w-0 px-[var(--space-6)] pt-[var(--space-3)] pb-[var(--space-4)]",
4641
+ ...!hasFocusable && { tabIndex: 0, role: "region", "aria-label": label },
4642
+ children
4643
+ }
4644
+ ) });
4645
+ }
4487
4646
  function OverlayFooter({ kind, className, ...props }) {
4488
4647
  const { footerDivider } = OVERLAY_POLICY[kind];
4489
4648
  return /* @__PURE__ */ jsx24(
@@ -4840,8 +4999,13 @@ function StructuredDialog({
4840
4999
  onOpenChange,
4841
5000
  title,
4842
5001
  description,
5002
+ hideTitle = false,
4843
5003
  size = "md",
4844
5004
  children,
5005
+ list,
5006
+ listHeight = "md",
5007
+ listFit = "max",
5008
+ listLabel = title,
4845
5009
  footer,
4846
5010
  onSubmit,
4847
5011
  onError,
@@ -4863,7 +5027,13 @@ function StructuredDialog({
4863
5027
  const returnFocus = useReturnFocus();
4864
5028
  const surfaceRef = React28.useRef(null);
4865
5029
  const swipe = useSwipeDismiss({ enabled: policy.swipe, onDismiss: () => guardOpenChange(false), surfaceRef });
4866
- const body = /* @__PURE__ */ jsx26(OverlayBody, { kind: "dialog", className, children });
5030
+ const body = list != null ? (
5031
+ // Scrolls only on a screen too low for the body's 16 and the list floor; the footer stays.
5032
+ /* @__PURE__ */ jsxs23("div", { "data-slot": "overlay-list-frame", className: "flex min-h-0 flex-col overflow-y-auto", children: [
5033
+ /* @__PURE__ */ jsx26(OverlayBody, { kind: "dialog", fixed: true, className, children }),
5034
+ /* @__PURE__ */ jsx26(OverlayList, { height: listHeight, fit: listFit, label: listLabel, children: list })
5035
+ ] })
5036
+ ) : /* @__PURE__ */ jsx26(OverlayBody, { kind: "dialog", className, children });
4867
5037
  const footerRow = formMode ? /* @__PURE__ */ jsxs23(OverlayFooter, { kind: "dialog", children: [
4868
5038
  /* @__PURE__ */ jsx26(
4869
5039
  Button,
@@ -4902,6 +5072,7 @@ function StructuredDialog({
4902
5072
  kind: "dialog",
4903
5073
  title,
4904
5074
  description,
5075
+ titleHidden: hideTitle,
4905
5076
  closeLabel,
4906
5077
  dragHandleProps: swipe.handleProps
4907
5078
  }
@@ -5566,14 +5737,8 @@ var collapsibleClassNames = {
5566
5737
  trigger: "hidden"
5567
5738
  }
5568
5739
  };
5569
- var gridCollapsibleClassNames = {
5570
- sm: { inline: "hidden", trigger: "" },
5571
- md: { inline: "hidden", trigger: "" },
5572
- lg: { inline: "hidden", trigger: "" },
5573
- never: { inline: "flex @[640px]:hidden", trigger: "hidden @[640px]:inline" }
5574
- };
5575
5740
  var gridSearchClassName = "order-1 min-w-[min(160px,100%)] max-w-full flex-[1_1_0%] @[640px]:min-w-0 @[640px]:flex-[3_1_0%]";
5576
- var gridFiltersClassName = "order-3 flex min-w-0 flex-[1_0_100%] flex-wrap items-center gap-[var(--space-2)] @[640px]:contents";
5741
+ var gridFiltersClassName = "order-3 hidden min-w-0 flex-[1_0_100%] flex-wrap items-center gap-[var(--space-2)] @[640px]:contents";
5577
5742
  var gridCellClassName = cn(
5578
5743
  "contents @[640px]:block @[640px]:order-3 @[640px]:min-w-0 @[640px]:flex-[3_1_0%]",
5579
5744
  "@[640px]:[&:has([data-slot=select-trigger])]:flex-[2_1_0%]",
@@ -5595,6 +5760,10 @@ function filterCells(node, prefix = "") {
5595
5760
  return React31.isValidElement(child) && child.type === React31.Fragment ? filterCells(child.props.children, `${key}/`) : [{ key, cell: child }];
5596
5761
  });
5597
5762
  }
5763
+ function contentWidth(element) {
5764
+ const style = getComputedStyle(element);
5765
+ return element.clientWidth - (parseFloat(style.paddingLeft) || 0) - (parseFloat(style.paddingRight) || 0);
5766
+ }
5598
5767
  function warnGridFilters(count) {
5599
5768
  if (typeof process === "undefined" || process.env.NODE_ENV === "production") return;
5600
5769
  console.warn(
@@ -5610,6 +5779,8 @@ var FilterToolbar = React31.forwardRef(
5610
5779
  collapseBelow = "lg",
5611
5780
  activeFilterCount: activeFilterCountProp,
5612
5781
  onClearFilters,
5782
+ activeLineFilterCount,
5783
+ onClearLineFilters,
5613
5784
  collapsedLabel = "\uD544\uD130",
5614
5785
  resultSummary,
5615
5786
  clearAction,
@@ -5625,6 +5796,7 @@ var FilterToolbar = React31.forwardRef(
5625
5796
  null
5626
5797
  );
5627
5798
  const [overlayOpen, setOverlayOpen] = React31.useState(false);
5799
+ const rootRef = React31.useRef(null);
5628
5800
  const triggerRef = React31.useRef(null);
5629
5801
  const rowRef = React31.useRef(null);
5630
5802
  const chipsRef = React31.useRef(null);
@@ -5632,28 +5804,31 @@ var FilterToolbar = React31.forwardRef(
5632
5804
  const pendingFocusRef = React31.useRef(null);
5633
5805
  const setRootRefs = React31.useCallback(
5634
5806
  (element) => {
5807
+ rootRef.current = element;
5635
5808
  setRootElement(element);
5636
5809
  if (typeof ref === "function") ref(element);
5637
5810
  else if (ref) ref.current = element;
5638
5811
  },
5639
5812
  [ref]
5640
5813
  );
5814
+ const isGrid = layout === "grid";
5641
5815
  React31.useEffect(() => {
5642
5816
  if (!rootElement) return;
5643
- const updateWidth = () => {
5644
- setContainerWidth(rootElement.getBoundingClientRect().width);
5817
+ const updateWidth = (entry) => {
5818
+ setContainerWidth(
5819
+ isGrid ? entry?.contentBoxSize?.[0]?.inlineSize ?? contentWidth(rootElement) : rootElement.getBoundingClientRect().width
5820
+ );
5645
5821
  };
5646
5822
  updateWidth();
5647
5823
  if (typeof ResizeObserver === "undefined") return;
5648
- const observer = new ResizeObserver(updateWidth);
5824
+ const observer = new ResizeObserver(([entry]) => updateWidth(entry));
5649
5825
  observer.observe(rootElement);
5650
5826
  return () => observer.disconnect();
5651
- }, [rootElement]);
5827
+ }, [rootElement, isGrid]);
5652
5828
  const handleOverlayOpenChange = React31.useCallback((open) => {
5653
5829
  setOverlayOpen(open);
5654
5830
  if (!open) queueMicrotask(() => triggerRef.current?.focus());
5655
5831
  }, []);
5656
- const isGrid = layout === "grid";
5657
5832
  const gridCells = isGrid ? filterCells(filters) : [];
5658
5833
  const filterCellCount = gridCells.length;
5659
5834
  React31.useEffect(() => {
@@ -5695,33 +5870,67 @@ var FilterToolbar = React31.forwardRef(
5695
5870
  const hasAppliedFilters = activeFilterCount > 0;
5696
5871
  const hasAppliedChips = Boolean(appliedFilters && appliedFilters.length > 0);
5697
5872
  const usesSheet = containerWidth !== null && containerWidth < 640;
5698
- const collapseClasses = (isGrid ? gridCollapsibleClassNames : collapsibleClassNames)[collapseBelow];
5699
- const collapseTriggerLabel = hasAppliedFilters ? `${collapsedLabel}, ${activeFilterCount}\uAC1C \uC801\uC6A9` : collapsedLabel;
5700
- const clearFiltersButton = onClearFilters ? /* @__PURE__ */ jsx32(
5873
+ const gridSheet = isGrid && Boolean(filters);
5874
+ const linesInSheet = gridSheet && usesSheet;
5875
+ const lineFilterCount = gridSheet ? activeLineFilterCount ?? 0 : 0;
5876
+ const narrowFilterCount = activeFilterCount + lineFilterCount;
5877
+ const hasCollapsed = Boolean(collapsible) || gridSheet;
5878
+ const outgrownSheet = isGrid && !collapsible && containerWidth !== null && containerWidth >= 640 && overlayOpen;
5879
+ React31.useEffect(() => {
5880
+ if (!outgrownSheet) return;
5881
+ setOverlayOpen(false);
5882
+ const ownerDocument = rootElement?.ownerDocument;
5883
+ const active = ownerDocument?.activeElement;
5884
+ if (!ownerDocument || active && active !== ownerDocument.body && active.isConnected) return;
5885
+ const cell = rowRef.current?.querySelector('[data-slot="filter-toolbar-cell"]');
5886
+ [...cell?.querySelectorAll(focusableSelector) ?? []].find(canTakeFocus)?.focus();
5887
+ }, [outgrownSheet, rootElement]);
5888
+ const inlineClassName = isGrid ? "hidden" : collapsibleClassNames[collapseBelow].inline;
5889
+ const triggerClassName = isGrid ? collapsible ? "" : "@[640px]:hidden" : collapsibleClassNames[collapseBelow].trigger;
5890
+ const triggerCount = linesInSheet ? narrowFilterCount : activeFilterCount;
5891
+ const collapseTriggerLabel = triggerCount > 0 ? `${collapsedLabel}, ${triggerCount}\uAC1C \uC801\uC6A9` : collapsedLabel;
5892
+ const narrowOnlyCount = !hasAppliedFilters && narrowFilterCount > 0;
5893
+ const countText = narrowFilterCount === activeFilterCount || narrowOnlyCount ? narrowFilterCount : /* @__PURE__ */ jsxs28(Fragment14, { children: [
5894
+ /* @__PURE__ */ jsx32("span", { className: "@[640px]:hidden", children: narrowFilterCount }),
5895
+ /* @__PURE__ */ jsx32("span", { className: "hidden @[640px]:inline", children: activeFilterCount })
5896
+ ] });
5897
+ const clearLineFilters = gridSheet ? onClearLineFilters : void 0;
5898
+ const clearNarrow = onClearFilters && clearLineFilters ? () => {
5899
+ onClearFilters();
5900
+ clearLineFilters();
5901
+ } : onClearFilters ?? clearLineFilters;
5902
+ const clearFilters = () => {
5903
+ const width = containerWidth ?? (rootRef.current ? contentWidth(rootRef.current) : null);
5904
+ (width !== null && width < 640 ? clearNarrow : onClearFilters)?.();
5905
+ };
5906
+ const clearFiltersButton = (linesInSheet ? clearNarrow : onClearFilters) ? /* @__PURE__ */ jsx32(
5701
5907
  "button",
5702
5908
  {
5703
5909
  type: "button",
5704
5910
  "data-slot": "filter-toolbar-clear-filters",
5705
5911
  className: "cursor-pointer border-0 bg-transparent p-0 text-body-sm font-medium text-text-secondary no-underline",
5706
- onClick: onClearFilters,
5912
+ onClick: clearFilters,
5707
5913
  children: "\uD574\uC81C"
5708
5914
  }
5709
5915
  ) : null;
5710
- const collapsedContent = collapsible ? /* @__PURE__ */ jsx32(
5916
+ const collapsedContent = hasCollapsed ? /* @__PURE__ */ jsxs28(
5711
5917
  "div",
5712
5918
  {
5713
5919
  "data-slot": "filter-toolbar-collapsed-content",
5714
5920
  className: "flex flex-col gap-[var(--space-3)] [&>*]:w-full",
5715
- children: collapsible
5921
+ children: [
5922
+ linesInSheet ? filters : null,
5923
+ collapsible
5924
+ ]
5716
5925
  }
5717
5926
  ) : null;
5718
- const collapseTrigger = collapsible ? /* @__PURE__ */ jsxs28(
5927
+ const collapseTrigger = hasCollapsed ? /* @__PURE__ */ jsxs28(
5719
5928
  "span",
5720
5929
  {
5721
5930
  "data-slot": "filter-toolbar-collapse-trigger",
5722
5931
  className: cn(
5723
5932
  "relative order-2 shrink-0 @[640px]:order-4",
5724
- collapseClasses.trigger
5933
+ triggerClassName
5725
5934
  ),
5726
5935
  children: [
5727
5936
  /* @__PURE__ */ jsx32(
@@ -5736,16 +5945,17 @@ var FilterToolbar = React31.forwardRef(
5736
5945
  onClick: () => handleOverlayOpenChange(true)
5737
5946
  }
5738
5947
  ),
5739
- hasAppliedFilters ? /* @__PURE__ */ jsx32(
5948
+ narrowFilterCount > 0 ? /* @__PURE__ */ jsx32(
5740
5949
  "span",
5741
5950
  {
5742
5951
  "aria-hidden": "true",
5743
5952
  "data-slot": "filter-toolbar-collapse-count",
5744
5953
  className: cn(
5745
5954
  chipCountVariants({ tone: "accent", size: "md" }),
5746
- "pointer-events-none absolute right-0 top-0 translate-x-1/3 -translate-y-1/3"
5955
+ "pointer-events-none absolute right-0 top-0 translate-x-1/3 -translate-y-1/3",
5956
+ narrowOnlyCount && "@[640px]:hidden"
5747
5957
  ),
5748
- children: activeFilterCount
5958
+ children: countText
5749
5959
  }
5750
5960
  ) : null
5751
5961
  ]
@@ -5781,7 +5991,7 @@ var FilterToolbar = React31.forwardRef(
5781
5991
  children: search
5782
5992
  }
5783
5993
  ) : null,
5784
- collapsible && usesSheet ? collapseTrigger : null,
5994
+ hasCollapsed && (usesSheet || !collapsible) ? collapseTrigger : null,
5785
5995
  collapsible && !usesSheet ? /* @__PURE__ */ jsxs28(Popover, { open: overlayOpen, onOpenChange: handleOverlayOpenChange, children: [
5786
5996
  /* @__PURE__ */ jsx32(PopoverAnchor, { asChild: true, children: collapseTrigger }),
5787
5997
  overlayOpen ? /* @__PURE__ */ jsxs28(
@@ -5800,7 +6010,7 @@ var FilterToolbar = React31.forwardRef(
5800
6010
  }
5801
6011
  ) : null
5802
6012
  ] }) : null,
5803
- filters ? /* @__PURE__ */ jsx32(
6013
+ filters && !linesInSheet ? /* @__PURE__ */ jsx32(
5804
6014
  "div",
5805
6015
  {
5806
6016
  "data-slot": "filter-toolbar-filters",
@@ -5822,7 +6032,7 @@ var FilterToolbar = React31.forwardRef(
5822
6032
  "data-slot": "filter-toolbar-collapsible",
5823
6033
  className: cn(
5824
6034
  "order-5 min-w-0 items-center gap-[var(--space-2)]",
5825
- collapseClasses.inline
6035
+ inlineClassName
5826
6036
  ),
5827
6037
  children: collapsible
5828
6038
  }
@@ -5838,7 +6048,7 @@ var FilterToolbar = React31.forwardRef(
5838
6048
  ]
5839
6049
  }
5840
6050
  ) : null,
5841
- collapsible && usesSheet && overlayOpen ? /* @__PURE__ */ jsx32(
6051
+ hasCollapsed && usesSheet && overlayOpen ? /* @__PURE__ */ jsx32(
5842
6052
  Sheet,
5843
6053
  {
5844
6054
  open: overlayOpen,
@@ -5899,28 +6109,31 @@ var FilterToolbar = React31.forwardRef(
5899
6109
  ]
5900
6110
  }
5901
6111
  ) : null,
5902
- hasAppliedFilters ? /* @__PURE__ */ jsxs28(
6112
+ narrowFilterCount > 0 ? /* @__PURE__ */ jsxs28(
5903
6113
  "div",
5904
6114
  {
5905
6115
  "data-slot": "filter-toolbar-applied",
5906
6116
  className: cn(
5907
6117
  "flex items-center gap-[var(--space-1)] text-[11px] text-text-tertiary",
5908
- hasAppliedChips && "@[640px]:hidden"
6118
+ (hasAppliedChips || narrowOnlyCount) && "@[640px]:hidden"
5909
6119
  ),
5910
6120
  children: [
5911
6121
  /* @__PURE__ */ jsxs28("span", { children: [
5912
6122
  "\uD544\uD130 ",
5913
- activeFilterCount,
6123
+ countText,
5914
6124
  "\uAC1C \uC801\uC6A9"
5915
6125
  ] }),
5916
- onClearFilters ? /* @__PURE__ */ jsxs28(Fragment14, { children: [
5917
- /* @__PURE__ */ jsx32("span", { "aria-hidden": "true", children: " \xB7 " }),
6126
+ clearNarrow ? /* @__PURE__ */ jsxs28(Fragment14, { children: [
6127
+ /* @__PURE__ */ jsx32("span", { "aria-hidden": "true", className: onClearFilters ? void 0 : "@[640px]:hidden", children: " \xB7 " }),
5918
6128
  /* @__PURE__ */ jsx32(
5919
6129
  "button",
5920
6130
  {
5921
6131
  type: "button",
5922
- className: "cursor-pointer border-0 bg-transparent p-0 text-[11px] text-text-secondary no-underline",
5923
- onClick: onClearFilters,
6132
+ className: cn(
6133
+ "cursor-pointer border-0 bg-transparent p-0 text-[11px] text-text-secondary no-underline",
6134
+ !onClearFilters && "@[640px]:hidden"
6135
+ ),
6136
+ onClick: clearFilters,
5924
6137
  children: "\uD574\uC81C"
5925
6138
  }
5926
6139
  )
@@ -6473,7 +6686,7 @@ function ActionMenu({
6473
6686
  // src/components/ui/checkbox.tsx
6474
6687
  import * as React33 from "react";
6475
6688
  import { cva as cva12 } from "class-variance-authority";
6476
- import { Check as Check3, Minus } from "lucide-react";
6689
+ import { Check as Check4, Minus } from "lucide-react";
6477
6690
  import { Checkbox as CheckboxPrimitive } from "radix-ui";
6478
6691
  import { jsx as jsx35, jsxs as jsxs31 } from "react/jsx-runtime";
6479
6692
  var checkboxFieldVariants = cva12(
@@ -6667,7 +6880,7 @@ var Checkbox = React33.forwardRef(
6667
6880
  {
6668
6881
  "data-slot": "checkbox-indicator",
6669
6882
  className: "flex items-center justify-center",
6670
- children: resolvedChecked === "indeterminate" ? /* @__PURE__ */ jsx35(Minus, { "aria-hidden": "true", size: 14, strokeWidth: 2.5 }) : /* @__PURE__ */ jsx35(Check3, { "aria-hidden": "true", size: 14, strokeWidth: 2.5 })
6883
+ children: resolvedChecked === "indeterminate" ? /* @__PURE__ */ jsx35(Minus, { "aria-hidden": "true", size: 14, strokeWidth: 2.5 }) : /* @__PURE__ */ jsx35(Check4, { "aria-hidden": "true", size: 14, strokeWidth: 2.5 })
6671
6884
  }
6672
6885
  )
6673
6886
  }
@@ -6756,7 +6969,7 @@ var Checkbox = React33.forwardRef(
6756
6969
  {
6757
6970
  "data-slot": "checkbox-indicator",
6758
6971
  className: "flex items-center justify-center",
6759
- children: resolvedChecked === "indeterminate" ? /* @__PURE__ */ jsx35(Minus, { "aria-hidden": "true", size: 14, strokeWidth: 2.5 }) : /* @__PURE__ */ jsx35(Check3, { "aria-hidden": "true", size: 14, strokeWidth: 2.5 })
6972
+ children: resolvedChecked === "indeterminate" ? /* @__PURE__ */ jsx35(Minus, { "aria-hidden": "true", size: 14, strokeWidth: 2.5 }) : /* @__PURE__ */ jsx35(Check4, { "aria-hidden": "true", size: 14, strokeWidth: 2.5 })
6760
6973
  }
6761
6974
  )
6762
6975
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@olundot/ui",
3
- "version": "0.20.0",
3
+ "version": "0.20.2",
4
4
  "description": "OLUN. shared React UI components built on OLUN design tokens",
5
5
  "license": "MIT",
6
6
  "keywords": [