@olundot/ui 0.20.0-next.9 → 0.20.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1019,13 +1019,38 @@ var MARKER_ICON = {
1019
1019
  error: CircleAlert
1020
1020
  };
1021
1021
  var TITLE_LINE_HEIGHT = 21;
1022
- var defaultEmphasis = (v) => v === "error" ? "strong" : "subtle";
1022
+ var actionPlacement = {
1023
+ oneLine: "col-start-2 row-start-2 justify-self-start mt-[var(--space-1)] @[344px]:col-start-3 @[344px]:row-start-1 @[344px]:mt-0 @[344px]:ms-[var(--space-2)]",
1024
+ twoLine: "col-start-2 row-start-3 justify-self-start mt-[var(--space-1)] @[344px]:col-start-3 @[344px]:row-[1/span_2] @[344px]:mt-0 @[344px]:ms-[var(--space-2)]"
1025
+ };
1026
+ var textWrap = { wordBreak: "keep-all", overflowWrap: "break-word", minWidth: 0 };
1027
+ var isEmpty = (node) => {
1028
+ if (node == null || typeof node === "boolean" || node === "") return true;
1029
+ if (Array.isArray(node)) return node.every(isEmpty);
1030
+ if (React6.isValidElement(node) && node.type === React6.Fragment) {
1031
+ return isEmpty(node.props.children);
1032
+ }
1033
+ return false;
1034
+ };
1023
1035
  var Alert = React6.forwardRef(
1024
- ({ variant = "info", emphasis, marker = "icon", title, children, onDismiss, style, ...rest }, ref) => {
1025
- const weight = emphasis ?? defaultEmphasis(variant);
1026
- const strong = weight === "strong";
1036
+ ({
1037
+ variant = "info",
1038
+ emphasis = "subtle",
1039
+ marker = "icon",
1040
+ title,
1041
+ children,
1042
+ action,
1043
+ onDismiss,
1044
+ className,
1045
+ style,
1046
+ ...rest
1047
+ }, ref) => {
1048
+ const strong = emphasis === "strong";
1027
1049
  const fg = SEM_FG[variant];
1028
1050
  const MarkerIcon = MARKER_ICON[variant];
1051
+ const hasBody = !isEmpty(children);
1052
+ const hasAction = !isEmpty(action);
1053
+ const controlRow = hasBody ? "1 / span 2" : 1;
1029
1054
  return /* @__PURE__ */ jsxs5(
1030
1055
  "div",
1031
1056
  {
@@ -1034,12 +1059,14 @@ var Alert = React6.forwardRef(
1034
1059
  "aria-live": variant === "error" ? "assertive" : "polite",
1035
1060
  "data-slot": "alert",
1036
1061
  "data-variant": variant,
1037
- "data-emphasis": weight,
1062
+ "data-emphasis": emphasis,
1038
1063
  "data-marker": marker,
1064
+ className: cn(hasAction && "@container w-full", className),
1039
1065
  style: {
1040
- display: "flex",
1041
- alignItems: "flex-start",
1042
- gap: "var(--space-2)",
1066
+ display: "grid",
1067
+ gridTemplateColumns: "auto minmax(0, 1fr) auto auto",
1068
+ rowGap: 2,
1069
+ alignItems: hasBody ? "start" : "center",
1043
1070
  borderRadius: "var(--radius-md)",
1044
1071
  border: "1px solid " + (strong ? "color-mix(in srgb, " + fg + " 26%, transparent)" : "var(--border)"),
1045
1072
  background: strong ? SEM_BG[variant] : "var(--bg-surface)",
@@ -1055,9 +1082,11 @@ var Alert = React6.forwardRef(
1055
1082
  "aria-hidden": "true",
1056
1083
  "data-slot": "alert-marker",
1057
1084
  style: {
1085
+ gridColumn: 1,
1086
+ gridRow: 1,
1058
1087
  display: "inline-flex",
1059
1088
  height: TITLE_LINE_HEIGHT,
1060
- flexShrink: 0,
1089
+ marginInlineEnd: "var(--space-2)",
1061
1090
  alignItems: "center",
1062
1091
  justifyContent: "center",
1063
1092
  color: fg
@@ -1071,37 +1100,49 @@ var Alert = React6.forwardRef(
1071
1100
  ) : /* @__PURE__ */ jsx5(MarkerIcon, { "data-slot": "alert-icon", size: 16, strokeWidth: 2.25 })
1072
1101
  }
1073
1102
  ),
1074
- /* @__PURE__ */ jsxs5("div", { "data-slot": "alert-content", style: { flex: 1, minWidth: 0 }, children: [
1075
- /* @__PURE__ */ jsx5(
1076
- "div",
1077
- {
1078
- "data-slot": "alert-title",
1079
- style: {
1080
- fontFamily: "var(--font-sans)",
1081
- fontSize: 14,
1082
- fontWeight: "var(--fw-medium)",
1083
- lineHeight: TITLE_LINE_HEIGHT + "px",
1084
- color: strong ? fg : "var(--text-primary)"
1085
- },
1086
- children: title
1087
- }
1088
- ),
1089
- /* @__PURE__ */ jsx5(
1090
- "div",
1091
- {
1092
- "data-slot": "alert-body",
1093
- style: {
1094
- marginTop: 2,
1095
- fontFamily: "var(--font-sans)",
1096
- fontSize: "var(--fs-sm)",
1097
- lineHeight: "var(--lh-normal)",
1098
- color: strong ? "color-mix(in srgb, " + fg + " 88%, var(--text-primary))" : "var(--text-secondary)"
1099
- },
1100
- children
1101
- }
1102
- )
1103
- ] }),
1104
- onDismiss ? /* @__PURE__ */ jsx5(DismissButton, { onDismiss, strong, fg }) : null
1103
+ /* @__PURE__ */ jsx5(
1104
+ "div",
1105
+ {
1106
+ "data-slot": "alert-title",
1107
+ style: {
1108
+ gridColumn: 2,
1109
+ gridRow: 1,
1110
+ ...textWrap,
1111
+ fontFamily: "var(--font-sans)",
1112
+ fontSize: 14,
1113
+ fontWeight: "var(--fw-medium)",
1114
+ lineHeight: TITLE_LINE_HEIGHT + "px",
1115
+ color: strong ? fg : "var(--text-primary)"
1116
+ },
1117
+ children: title
1118
+ }
1119
+ ),
1120
+ hasBody ? /* @__PURE__ */ jsx5(
1121
+ "div",
1122
+ {
1123
+ "data-slot": "alert-body",
1124
+ style: {
1125
+ gridColumn: 2,
1126
+ gridRow: 2,
1127
+ ...textWrap,
1128
+ fontFamily: "var(--font-sans)",
1129
+ fontSize: "var(--fs-sm)",
1130
+ lineHeight: "var(--lh-normal)",
1131
+ color: strong ? "color-mix(in srgb, " + fg + " 88%, var(--text-primary))" : "var(--text-secondary)"
1132
+ },
1133
+ children
1134
+ }
1135
+ ) : null,
1136
+ hasAction ? /* @__PURE__ */ jsx5(
1137
+ "div",
1138
+ {
1139
+ "data-slot": "alert-action",
1140
+ className: hasBody ? actionPlacement.twoLine : actionPlacement.oneLine,
1141
+ style: { display: "flex", alignItems: "center" },
1142
+ children: action
1143
+ }
1144
+ ) : null,
1145
+ onDismiss ? /* @__PURE__ */ jsx5(DismissButton, { onDismiss, strong, fg, row: controlRow }) : null
1105
1146
  ]
1106
1147
  }
1107
1148
  );
@@ -1111,7 +1152,8 @@ Alert.displayName = "Alert";
1111
1152
  function DismissButton({
1112
1153
  onDismiss,
1113
1154
  strong,
1114
- fg
1155
+ fg,
1156
+ row
1115
1157
  }) {
1116
1158
  const [hover, setHover] = React6.useState(false);
1117
1159
  const [focus, setFocus] = React6.useState(false);
@@ -1126,8 +1168,11 @@ function DismissButton({
1126
1168
  onMouseLeave: () => setHover(false),
1127
1169
  onFocus: (e) => setFocus(e.currentTarget.matches(":focus-visible")),
1128
1170
  onBlur: () => setFocus(false),
1171
+ className: "relative before:absolute before:-inset-[9px] before:content-['']",
1129
1172
  style: {
1130
- flexShrink: 0,
1173
+ gridColumn: 4,
1174
+ gridRow: row,
1175
+ marginInlineStart: "var(--space-2)",
1131
1176
  display: "inline-flex",
1132
1177
  width: 28,
1133
1178
  height: 28,
@@ -2007,7 +2052,7 @@ import * as React11 from "react";
2007
2052
  import { cva as cva7 } from "class-variance-authority";
2008
2053
  import { Loader2 as Loader22 } from "lucide-react";
2009
2054
  import { Switch as SwitchPrimitive } from "radix-ui";
2010
- import { Fragment, jsx as jsx10, jsxs as jsxs10 } from "react/jsx-runtime";
2055
+ import { Fragment as Fragment2, jsx as jsx10, jsxs as jsxs10 } from "react/jsx-runtime";
2011
2056
  var switchFieldVariants = cva7("", {
2012
2057
  variants: {
2013
2058
  labelPlacement: {
@@ -2132,13 +2177,13 @@ var Switch = React11.forwardRef(
2132
2177
  "data-slot": "switch-field",
2133
2178
  "data-disabled": isDisabled ? "" : void 0,
2134
2179
  className: switchFieldVariants({ labelPlacement, fullWidth }),
2135
- children: isStart ? /* @__PURE__ */ jsxs10(Fragment, { children: [
2180
+ children: isStart ? /* @__PURE__ */ jsxs10(Fragment2, { children: [
2136
2181
  /* @__PURE__ */ jsxs10("div", { className: "flex min-w-0 grow flex-col gap-[var(--space-1)]", children: [
2137
2182
  labelNode,
2138
2183
  descriptionNode
2139
2184
  ] }),
2140
2185
  track
2141
- ] }) : /* @__PURE__ */ jsxs10(Fragment, { children: [
2186
+ ] }) : /* @__PURE__ */ jsxs10(Fragment2, { children: [
2142
2187
  /* @__PURE__ */ jsxs10("div", { className: "inline-flex items-center gap-[var(--space-3)]", children: [
2143
2188
  track,
2144
2189
  labelNode
@@ -2250,7 +2295,7 @@ function TooltipContent({
2250
2295
  }
2251
2296
 
2252
2297
  // src/components/ui/permission-guard.tsx
2253
- import { Fragment as Fragment3, jsx as jsx12, jsxs as jsxs12 } from "react/jsx-runtime";
2298
+ import { Fragment as Fragment4, jsx as jsx12, jsxs as jsxs12 } from "react/jsx-runtime";
2254
2299
  var defaultDisabledReason = "\uAD8C\uD55C\uC774 \uC5C6\uC2B5\uB2C8\uB2E4";
2255
2300
  var PermissionContext = React13.createContext(
2256
2301
  void 0
@@ -2297,7 +2342,7 @@ function PermissionGuard({
2297
2342
  const state = React13.useContext(PermissionContext);
2298
2343
  const allowed = evaluatePermission(permission, permissions ?? state, scope);
2299
2344
  if (allowed) {
2300
- return /* @__PURE__ */ jsx12(Fragment3, { children });
2345
+ return /* @__PURE__ */ jsx12(Fragment4, { children });
2301
2346
  }
2302
2347
  if (fallback === "hide" || fallback == null) {
2303
2348
  return null;
@@ -2305,7 +2350,7 @@ function PermissionGuard({
2305
2350
  if (fallback === "disabled") {
2306
2351
  return renderDisabledFallback(children, disabledReason);
2307
2352
  }
2308
- return /* @__PURE__ */ jsx12(Fragment3, { children: fallback });
2353
+ return /* @__PURE__ */ jsx12(Fragment4, { children: fallback });
2309
2354
  }
2310
2355
  function getPermissionCandidates(permission, scope) {
2311
2356
  const candidates = /* @__PURE__ */ new Set([permission]);
@@ -2732,7 +2777,7 @@ var DataLayout = React17.forwardRef(
2732
2777
  "div",
2733
2778
  {
2734
2779
  "data-slot": "data-layout-bulkbar",
2735
- className: "pointer-events-none sticky bottom-[var(--space-4)] z-20 mt-[var(--space-2)] flex justify-center",
2780
+ className: "pointer-events-none sticky bottom-[calc(var(--space-4)+env(safe-area-inset-bottom))] z-20 mt-[var(--space-2)] flex justify-center",
2736
2781
  children: /* @__PURE__ */ jsx16("div", { className: "flex w-full flex-col items-center [&>*:has([data-slot=bulk-action-bar])]:self-stretch [&>*:not(:has([data-slot=bulk-action-bar]))]:pointer-events-auto [&>*]:max-w-full [&_[data-slot=bulk-action-bar]]:pointer-events-auto", children: bulkBar })
2737
2782
  }
2738
2783
  ) : null
@@ -2749,7 +2794,7 @@ DataLayout.displayName = "DataLayout";
2749
2794
  // src/components/ui/wizard-layout.tsx
2750
2795
  import * as React18 from "react";
2751
2796
  import { Check as Check2 } from "lucide-react";
2752
- import { Fragment as Fragment6, jsx as jsx17, jsxs as jsxs16 } from "react/jsx-runtime";
2797
+ import { Fragment as Fragment7, jsx as jsx17, jsxs as jsxs16 } from "react/jsx-runtime";
2753
2798
  var circleBaseClassName = [
2754
2799
  "flex items-center justify-center rounded-[var(--radius-full)] border text-[12px] font-semibold leading-none",
2755
2800
  "transition-[background-color,border-color,color] motion-reduce:transition-none"
@@ -3040,7 +3085,7 @@ var WizardLayout = React18.forwardRef(
3040
3085
  ) : null
3041
3086
  ]
3042
3087
  }
3043
- ) : /* @__PURE__ */ jsxs16(Fragment6, { children: [
3088
+ ) : /* @__PURE__ */ jsxs16(Fragment7, { children: [
3044
3089
  /* @__PURE__ */ jsx17(
3045
3090
  "span",
3046
3091
  {
@@ -3109,7 +3154,7 @@ import * as React19 from "react";
3109
3154
  import { Tabs as TabsPrimitive } from "radix-ui";
3110
3155
  import { cva as cva9 } from "class-variance-authority";
3111
3156
  import { Lock } from "lucide-react";
3112
- import { Fragment as Fragment7, jsx as jsx18, jsxs as jsxs17 } from "react/jsx-runtime";
3157
+ import { Fragment as Fragment8, jsx as jsx18, jsxs as jsxs17 } from "react/jsx-runtime";
3113
3158
  var tabsRootVariants = cva9("w-full", {
3114
3159
  variants: {
3115
3160
  orientation: {
@@ -3376,7 +3421,7 @@ var Tabs = React19.forwardRef(
3376
3421
  className: cn("group", tabsTriggerVariants({ variant, orientation }), triggerClassName),
3377
3422
  children: [
3378
3423
  isLine ? /* @__PURE__ */ jsx18("span", { "data-label": tab.label, className: tabsLabelReserveClassName, children: tab.label }) : /* @__PURE__ */ jsx18("span", { children: tab.label }),
3379
- isShownTabBadge(tab.badge) ? /* @__PURE__ */ jsxs17(Fragment7, { children: [
3424
+ isShownTabBadge(tab.badge) ? /* @__PURE__ */ jsxs17(Fragment8, { children: [
3380
3425
  /* @__PURE__ */ jsx18("span", { "aria-hidden": "true", "data-slot": "tabs-badge", className: tabsBadgeVariants(), children: formatTabBadge(tab.badge) }),
3381
3426
  /* @__PURE__ */ jsx18("span", { className: "sr-only", children: ` ${tab.badge}` })
3382
3427
  ] }) : null,
@@ -3435,7 +3480,7 @@ var TabsContent = React19.forwardRef(({ className, tabKey, baseId, ...props }, r
3435
3480
  TabsContent.displayName = TabsPrimitive.Content.displayName;
3436
3481
 
3437
3482
  // src/components/ui/detail-layout.tsx
3438
- import { Fragment as Fragment8, jsx as jsx19, jsxs as jsxs18 } from "react/jsx-runtime";
3483
+ import { Fragment as Fragment9, jsx as jsx19, jsxs as jsxs18 } from "react/jsx-runtime";
3439
3484
  function DetailLayoutBody({
3440
3485
  children,
3441
3486
  aside,
@@ -3488,7 +3533,7 @@ function appendFooterToActiveTabsContent(children, activeTab, footer) {
3488
3533
  return React20.cloneElement(
3489
3534
  child,
3490
3535
  void 0,
3491
- /* @__PURE__ */ jsxs18(Fragment8, { children: [
3536
+ /* @__PURE__ */ jsxs18(Fragment9, { children: [
3492
3537
  child.props.children,
3493
3538
  /* @__PURE__ */ jsx19("div", { "data-slot": "detail-layout-footer", className: "min-w-0", children: footer })
3494
3539
  ] })
@@ -3570,7 +3615,7 @@ var DetailLayout = React20.forwardRef(
3570
3615
  baseId,
3571
3616
  forceMount: true,
3572
3617
  hidden: tab.key !== activeTab,
3573
- children: tab.key === activeTab ? /* @__PURE__ */ jsxs18(Fragment8, { children: [
3618
+ children: tab.key === activeTab ? /* @__PURE__ */ jsxs18(Fragment9, { children: [
3574
3619
  body,
3575
3620
  footer ? /* @__PURE__ */ jsx19("div", { "data-slot": "detail-layout-footer", className: "min-w-0", children: footer }) : null
3576
3621
  ] }) : null
@@ -3578,7 +3623,7 @@ var DetailLayout = React20.forwardRef(
3578
3623
  tab.key
3579
3624
  ))
3580
3625
  }
3581
- ) : /* @__PURE__ */ jsxs18(Fragment8, { children: [
3626
+ ) : /* @__PURE__ */ jsxs18(Fragment9, { children: [
3582
3627
  body,
3583
3628
  footer ? /* @__PURE__ */ jsx19("div", { "data-slot": "detail-layout-footer", className: "min-w-0", children: footer }) : null
3584
3629
  ] })
@@ -4008,9 +4053,31 @@ import { Dialog as DialogPrimitive } from "radix-ui";
4008
4053
  // src/components/ui/icon-button.tsx
4009
4054
  import * as React25 from "react";
4010
4055
  import { cva as cva10 } from "class-variance-authority";
4011
- import { ChevronDown as ChevronDown4, Loader2 as Loader23 } from "lucide-react";
4056
+ import { Check as Check3, ChevronDown as ChevronDown4, Loader2 as Loader23 } from "lucide-react";
4012
4057
  import { jsx as jsx22, jsxs as jsxs20 } from "react/jsx-runtime";
4013
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
+ }
4014
4081
  var iconButtonCva = cva10(
4015
4082
  [
4016
4083
  "olun-icon-button relative inline-flex cursor-pointer shrink-0 items-center justify-center before:absolute before:content-['']",
@@ -4078,7 +4145,8 @@ var iconButtonBadgeVariants = cva10(
4078
4145
  variants: {
4079
4146
  tone: {
4080
4147
  info: "bg-[var(--accent-pop)] text-[var(--accent-on-fill)]",
4081
- 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)]"
4082
4150
  }
4083
4151
  },
4084
4152
  defaultVariants: {
@@ -4100,21 +4168,35 @@ var IconButton = React25.forwardRef(
4100
4168
  title,
4101
4169
  "aria-label": ariaLabel,
4102
4170
  "aria-labelledby": ariaLabelledBy,
4171
+ "aria-describedby": ariaDescribedBy,
4103
4172
  iconPosition = "start",
4104
4173
  loading,
4174
+ done,
4105
4175
  menuIndicator,
4106
4176
  disabled,
4107
4177
  label,
4108
4178
  children,
4179
+ tooltip,
4180
+ tooltipSide = "top",
4181
+ onClick,
4109
4182
  ...props
4110
4183
  }, ref) => {
4111
4184
  const resolvedSize = size ?? "md";
4112
- const isToggle = pressed !== void 0;
4113
- const isPressed = pressed === true;
4114
- 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);
4115
4191
  const text = label ?? children;
4116
4192
  const hasText = text !== void 0 && text !== null && text !== false;
4117
- 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]);
4118
4200
  const style = resolveActionStyle(appearance, tone, "ghost");
4119
4201
  const icon = loading ? /* @__PURE__ */ jsx22(
4120
4202
  Loader23,
@@ -4134,7 +4216,14 @@ var IconButton = React25.forwardRef(
4134
4216
  strokeWidth: axisIcon[resolvedSize].strokeWidth
4135
4217
  }
4136
4218
  );
4137
- 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(
4138
4227
  "button",
4139
4228
  {
4140
4229
  ref,
@@ -4146,24 +4235,24 @@ var IconButton = React25.forwardRef(
4146
4235
  "data-icon-position": hasText ? iconPosition : void 0,
4147
4236
  "data-pressed": isToggle ? String(isPressed) : void 0,
4148
4237
  "data-loading": loading ? "true" : void 0,
4238
+ "data-done": isDone ? "true" : void 0,
4149
4239
  "aria-busy": loading ? true : void 0,
4150
- "aria-disabled": isDisabled ? true : void 0,
4240
+ "aria-disabled": isDisabled || isDone ? true : void 0,
4151
4241
  disabled: isDisabled,
4152
4242
  "aria-label": ariaLabel,
4153
4243
  "aria-labelledby": ariaLabelledBy,
4244
+ "aria-describedby": ariaDescribedBy,
4154
4245
  "aria-pressed": isToggle ? isPressed : void 0,
4155
4246
  title: hoverTitle,
4156
- className: cn(
4157
- hasText ? [
4158
- "olun-icon-button",
4159
- buttonVariants({ ...style, size: resolvedSize }),
4160
- isPressed && style.tone === "neutral" && pressedNeutralClassName,
4161
- // The badge sits on the corner; Button xs/sm are already relative.
4162
- badge !== void 0 && "relative"
4163
- ] : iconButtonVariants({ appearance, tone, size: resolvedSize, pressed: isPressed }),
4164
- className
4165
- ),
4247
+ className: cn(isDone ? [doneRestPaint(cn(paint)), doneClassName] : paint, className),
4166
4248
  type: type ?? "button",
4249
+ onClick: (event) => {
4250
+ if (isDone) {
4251
+ event.preventDefault();
4252
+ return;
4253
+ }
4254
+ onClick?.(event);
4255
+ },
4167
4256
  children: [
4168
4257
  hasText && iconPosition === "end" ? null : icon,
4169
4258
  hasText ? text : null,
@@ -4177,19 +4266,41 @@ var IconButton = React25.forwardRef(
4177
4266
  strokeWidth: axisIcon[resolvedSize].strokeWidth
4178
4267
  }
4179
4268
  ) : null,
4180
- badge !== void 0 ? /* @__PURE__ */ jsx22(
4269
+ resolvedBadge !== void 0 ? /* @__PURE__ */ jsx22(
4181
4270
  "span",
4182
4271
  {
4183
4272
  "aria-hidden": "true",
4184
4273
  "data-slot": "icon-button-badge",
4185
- "data-badge-tone": badgeTone,
4186
- className: iconButtonBadgeVariants({ tone: badgeTone }),
4187
- children: badge
4274
+ "data-badge-tone": resolvedBadgeTone,
4275
+ className: iconButtonBadgeVariants({ tone: resolvedBadgeTone }),
4276
+ children: resolvedBadge
4188
4277
  }
4189
4278
  ) : null
4190
4279
  ]
4191
4280
  }
4192
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
+ ) });
4193
4304
  }
4194
4305
  );
4195
4306
  IconButton.displayName = "IconButton";
@@ -4248,7 +4359,8 @@ var overlaySize = {
4248
4359
  dialog: {
4249
4360
  sm: "max-w-[420px]",
4250
4361
  md: "max-w-[560px]",
4251
- lg: "max-w-[720px]"
4362
+ lg: "max-w-[720px]",
4363
+ xl: "max-w-[960px]"
4252
4364
  },
4253
4365
  sheet: {
4254
4366
  md: "lg:w-[480px] lg:max-w-[480px]",
@@ -4316,14 +4428,32 @@ var OverlayScrimLayer = React26.forwardRef(
4316
4428
  }
4317
4429
  );
4318
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);
4319
4447
  function focusBodyInput(event) {
4320
4448
  const surface = event.target instanceof HTMLElement ? event.target : null;
4321
- const input = surface?.querySelector(
4322
- '[data-slot="overlay-body"] :is(input, textarea, select):not([disabled]):not([type="hidden"])'
4323
- );
4324
- if (input) {
4325
- 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;
4326
4452
  input.focus();
4453
+ if (input.ownerDocument.activeElement === input) {
4454
+ event.preventDefault();
4455
+ return;
4456
+ }
4327
4457
  }
4328
4458
  }
4329
4459
  var headingClassName = {
@@ -4341,6 +4471,7 @@ function OverlayHeader({
4341
4471
  caption,
4342
4472
  actions,
4343
4473
  closeLabel = "\uB2EB\uAE30",
4474
+ titleHidden = false,
4344
4475
  dragHandleProps,
4345
4476
  id,
4346
4477
  className
@@ -4364,7 +4495,7 @@ function OverlayHeader({
4364
4495
  children: [
4365
4496
  /* @__PURE__ */ jsxs21("div", { className: "flex min-w-0 flex-1 flex-col gap-[var(--space-2)]", children: [
4366
4497
  caption != null ? /* @__PURE__ */ jsx24("div", { "data-slot": "overlay-caption", className: "flex flex-wrap items-center gap-[var(--space-2)]", children: caption }) : null,
4367
- /* @__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 }),
4368
4499
  description != null ? /* @__PURE__ */ jsx24(Description, { "data-slot": "overlay-description", className: descriptionClassName[policy.heading], children: description }) : null
4369
4500
  ] }),
4370
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: [
@@ -4393,28 +4524,18 @@ var fogClassName = cn(
4393
4524
  "pointer-events-none sticky z-[1] block h-[var(--space-6)] shrink-0 opacity-0",
4394
4525
  "from-[var(--bg-raised)] to-transparent"
4395
4526
  );
4396
- function OverlayBody({ kind, className, children, ...props }) {
4397
- const { scroll } = OVERLAY_POLICY[kind];
4398
- if (scroll !== "body") {
4399
- return /* @__PURE__ */ jsx24(
4400
- "div",
4401
- {
4402
- "data-slot": "overlay-body",
4403
- className: cn(
4404
- "min-w-0 px-[var(--space-6)] pt-[var(--space-4)] text-[14px] leading-relaxed text-text-secondary",
4405
- scroll === "list" && "min-h-0 flex-1 overflow-y-auto px-0",
4406
- className
4407
- ),
4408
- ...props,
4409
- children
4410
- }
4411
- );
4412
- }
4527
+ function FogScroller({
4528
+ scrollerRef,
4529
+ className,
4530
+ contentClassName,
4531
+ children,
4532
+ ...props
4533
+ }) {
4413
4534
  return /* @__PURE__ */ jsxs21(
4414
4535
  "div",
4415
4536
  {
4416
- "data-slot": "overlay-body",
4417
- 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),
4418
4539
  ...props,
4419
4540
  children: [
4420
4541
  /* @__PURE__ */ jsx24(
@@ -4425,7 +4546,7 @@ function OverlayBody({ kind, className, children, ...props }) {
4425
4546
  className: cn(fogClassName, "top-0 -mb-[var(--space-6)] bg-linear-to-b", "[@container_scroll-state(scrollable:top)]:opacity-100")
4426
4547
  }
4427
4548
  ),
4428
- /* @__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 }),
4429
4550
  /* @__PURE__ */ jsx24(
4430
4551
  "span",
4431
4552
  {
@@ -4438,6 +4559,90 @@ function OverlayBody({ kind, className, children, ...props }) {
4438
4559
  }
4439
4560
  );
4440
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
+ }
4441
4646
  function OverlayFooter({ kind, className, ...props }) {
4442
4647
  const { footerDivider } = OVERLAY_POLICY[kind];
4443
4648
  return /* @__PURE__ */ jsx24(
@@ -4779,7 +4984,7 @@ function SheetDescription({
4779
4984
  }
4780
4985
 
4781
4986
  // src/components/ui/dialog.tsx
4782
- import { Fragment as Fragment9, jsx as jsx26, jsxs as jsxs23 } from "react/jsx-runtime";
4987
+ import { Fragment as Fragment10, jsx as jsx26, jsxs as jsxs23 } from "react/jsx-runtime";
4783
4988
  function dialogMobileSheetStyle(style) {
4784
4989
  return getDefaultSheetMotionStyle(style);
4785
4990
  }
@@ -4794,8 +4999,13 @@ function StructuredDialog({
4794
4999
  onOpenChange,
4795
5000
  title,
4796
5001
  description,
5002
+ hideTitle = false,
4797
5003
  size = "md",
4798
5004
  children,
5005
+ list,
5006
+ listHeight = "md",
5007
+ listFit = "max",
5008
+ listLabel = title,
4799
5009
  footer,
4800
5010
  onSubmit,
4801
5011
  onError,
@@ -4817,7 +5027,13 @@ function StructuredDialog({
4817
5027
  const returnFocus = useReturnFocus();
4818
5028
  const surfaceRef = React28.useRef(null);
4819
5029
  const swipe = useSwipeDismiss({ enabled: policy.swipe, onDismiss: () => guardOpenChange(false), surfaceRef });
4820
- 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 });
4821
5037
  const footerRow = formMode ? /* @__PURE__ */ jsxs23(OverlayFooter, { kind: "dialog", children: [
4822
5038
  /* @__PURE__ */ jsx26(
4823
5039
  Button,
@@ -4856,6 +5072,7 @@ function StructuredDialog({
4856
5072
  kind: "dialog",
4857
5073
  title,
4858
5074
  description,
5075
+ titleHidden: hideTitle,
4859
5076
  closeLabel,
4860
5077
  dragHandleProps: swipe.handleProps
4861
5078
  }
@@ -4873,7 +5090,7 @@ function StructuredDialog({
4873
5090
  footerRow
4874
5091
  ]
4875
5092
  }
4876
- ) : /* @__PURE__ */ jsxs23(Fragment9, { children: [
5093
+ ) : /* @__PURE__ */ jsxs23(Fragment10, { children: [
4877
5094
  body,
4878
5095
  footerRow
4879
5096
  ] })
@@ -5077,7 +5294,7 @@ function FormModal({
5077
5294
 
5078
5295
  // src/components/ui/alert-dialog.tsx
5079
5296
  import { AlertDialog as AlertDialogPrimitive2 } from "radix-ui";
5080
- import { Fragment as Fragment10, jsx as jsx28, jsxs as jsxs25 } from "react/jsx-runtime";
5297
+ import { Fragment as Fragment11, jsx as jsx28, jsxs as jsxs25 } from "react/jsx-runtime";
5081
5298
  function AlertDialogFrame({
5082
5299
  open,
5083
5300
  onOpenChange,
@@ -5141,7 +5358,7 @@ function AlertDialog({
5141
5358
  disabled: confirmDisabled,
5142
5359
  onError
5143
5360
  });
5144
- const footer = confirmLabel != null ? /* @__PURE__ */ jsxs25(Fragment10, { children: [
5361
+ const footer = confirmLabel != null ? /* @__PURE__ */ jsxs25(Fragment11, { children: [
5145
5362
  /* @__PURE__ */ jsx28(AlertDialogPrimitive2.Cancel, { asChild: true, children: /* @__PURE__ */ jsx28(Button, { appearance: "outline", tone: "neutral", size: "lg", className: footerButtonHitAreaClassName, disabled: isBusy, children: cancelLabel }) }),
5146
5363
  /* @__PURE__ */ jsx28(
5147
5364
  Button,
@@ -5219,7 +5436,7 @@ import * as React29 from "react";
5219
5436
  import { cva as cva11 } from "class-variance-authority";
5220
5437
  import { X as X3 } from "lucide-react";
5221
5438
  import { Slot as Slot2 } from "radix-ui";
5222
- import { Fragment as Fragment11, jsx as jsx30, jsxs as jsxs26 } from "react/jsx-runtime";
5439
+ import { Fragment as Fragment12, jsx as jsx30, jsxs as jsxs26 } from "react/jsx-runtime";
5223
5440
  var chipVariants = cva11(
5224
5441
  [
5225
5442
  "inline-flex w-fit shrink-0 items-center justify-center whitespace-nowrap",
@@ -5318,7 +5535,7 @@ function ChipCount({
5318
5535
  tone,
5319
5536
  size
5320
5537
  }) {
5321
- return /* @__PURE__ */ jsxs26(Fragment11, { children: [
5538
+ return /* @__PURE__ */ jsxs26(Fragment12, { children: [
5322
5539
  /* @__PURE__ */ jsx30(
5323
5540
  "span",
5324
5541
  {
@@ -5501,7 +5718,7 @@ var PopoverArrow = React30.forwardRef(({ className, ...props }, ref) => /* @__PU
5501
5718
  PopoverArrow.displayName = PopoverPrimitive.Arrow.displayName;
5502
5719
 
5503
5720
  // src/components/ui/filter-toolbar.tsx
5504
- import { Fragment as Fragment12, jsx as jsx32, jsxs as jsxs28 } from "react/jsx-runtime";
5721
+ import { Fragment as Fragment14, jsx as jsx32, jsxs as jsxs28 } from "react/jsx-runtime";
5505
5722
  var collapsibleClassNames = {
5506
5723
  sm: {
5507
5724
  inline: "hidden @[640px]:flex",
@@ -5520,6 +5737,41 @@ var collapsibleClassNames = {
5520
5737
  trigger: "hidden"
5521
5738
  }
5522
5739
  };
5740
+ var gridCollapsibleClassNames = {
5741
+ sm: { inline: "hidden", trigger: "" },
5742
+ md: { inline: "hidden", trigger: "" },
5743
+ lg: { inline: "hidden", trigger: "" },
5744
+ never: { inline: "flex @[640px]:hidden", trigger: "hidden @[640px]:inline" }
5745
+ };
5746
+ 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%]";
5747
+ var gridFiltersClassName = "order-3 flex min-w-0 flex-[1_0_100%] flex-wrap items-center gap-[var(--space-2)] @[640px]:contents";
5748
+ var gridCellClassName = cn(
5749
+ "contents @[640px]:block @[640px]:order-3 @[640px]:min-w-0 @[640px]:flex-[3_1_0%]",
5750
+ "@[640px]:[&:has([data-slot=select-trigger])]:flex-[2_1_0%]",
5751
+ "@[640px]:[&:has(>[data-slot=chip])]:flex-none",
5752
+ // A Chip group is named: a role="group" child that holds the Chips. A control that shows its
5753
+ // values as Chips keeps the default share.
5754
+ "@[640px]:[&:has(>[role=group]>[data-slot=chip])]:flex-none",
5755
+ // The control fills its cell, through a width the consumer put on a wrapper.
5756
+ "@[640px]:[&>:not([data-slot=chip])]:w-full @[640px]:[&_[data-slot=select-trigger]]:w-full"
5757
+ );
5758
+ var MAX_GRID_FILTERS = 3;
5759
+ var focusableSelector = "input, button, [tabindex]:not([tabindex='-1'])";
5760
+ function canTakeFocus(element) {
5761
+ return Boolean(element && element.getClientRects().length > 0 && !element.matches(":disabled"));
5762
+ }
5763
+ function filterCells(node, prefix = "") {
5764
+ return React31.Children.toArray(node).flatMap((child, index) => {
5765
+ const key = `${prefix}${React31.isValidElement(child) ? child.key : index}`;
5766
+ return React31.isValidElement(child) && child.type === React31.Fragment ? filterCells(child.props.children, `${key}/`) : [{ key, cell: child }];
5767
+ });
5768
+ }
5769
+ function warnGridFilters(count) {
5770
+ if (typeof process === "undefined" || process.env.NODE_ENV === "production") return;
5771
+ console.warn(
5772
+ `FilterToolbar: layout="grid" fits ${MAX_GRID_FILTERS} filters on the line, got ${count}. Move the rest to \`collapsible\`.`
5773
+ );
5774
+ }
5523
5775
  var FilterToolbar = React31.forwardRef(
5524
5776
  ({
5525
5777
  className,
@@ -5527,12 +5779,14 @@ var FilterToolbar = React31.forwardRef(
5527
5779
  filters,
5528
5780
  collapsible,
5529
5781
  collapseBelow = "lg",
5530
- activeFilterCount = 0,
5782
+ activeFilterCount: activeFilterCountProp,
5531
5783
  onClearFilters,
5532
5784
  collapsedLabel = "\uD544\uD130",
5533
5785
  resultSummary,
5534
5786
  clearAction,
5535
5787
  actions,
5788
+ layout = "flow",
5789
+ appliedFilters,
5536
5790
  ...props
5537
5791
  }, ref) => {
5538
5792
  const [rootElement, setRootElement] = React31.useState(
@@ -5543,6 +5797,10 @@ var FilterToolbar = React31.forwardRef(
5543
5797
  );
5544
5798
  const [overlayOpen, setOverlayOpen] = React31.useState(false);
5545
5799
  const triggerRef = React31.useRef(null);
5800
+ const rowRef = React31.useRef(null);
5801
+ const chipsRef = React31.useRef(null);
5802
+ const chipRemoveRefs = React31.useRef(/* @__PURE__ */ new Map());
5803
+ const pendingFocusRef = React31.useRef(null);
5546
5804
  const setRootRefs = React31.useCallback(
5547
5805
  (element) => {
5548
5806
  setRootElement(element);
@@ -5566,13 +5824,49 @@ var FilterToolbar = React31.forwardRef(
5566
5824
  setOverlayOpen(open);
5567
5825
  if (!open) queueMicrotask(() => triggerRef.current?.focus());
5568
5826
  }, []);
5827
+ const isGrid = layout === "grid";
5828
+ const gridCells = isGrid ? filterCells(filters) : [];
5829
+ const filterCellCount = gridCells.length;
5830
+ React31.useEffect(() => {
5831
+ if (filterCellCount > MAX_GRID_FILTERS) warnGridFilters(filterCellCount);
5832
+ }, [filterCellCount]);
5833
+ React31.useEffect(() => {
5834
+ const ownerDocument = rootElement?.ownerDocument;
5835
+ if (!ownerDocument) return;
5836
+ const dropOutside = (event) => {
5837
+ if (!chipsRef.current?.contains(event.target)) pendingFocusRef.current = null;
5838
+ };
5839
+ ownerDocument.addEventListener("focusin", dropOutside);
5840
+ ownerDocument.addEventListener("pointerdown", dropOutside, true);
5841
+ return () => {
5842
+ ownerDocument.removeEventListener("focusin", dropOutside);
5843
+ ownerDocument.removeEventListener("pointerdown", dropOutside, true);
5844
+ };
5845
+ }, [rootElement]);
5846
+ React31.useEffect(() => {
5847
+ const request = pendingFocusRef.current;
5848
+ if (!request) return;
5849
+ const present = new Set(appliedFilters?.map((filter) => filter.key));
5850
+ if (request.removed.some((key) => present.has(key))) return;
5851
+ pendingFocusRef.current = null;
5852
+ const active = rootElement?.ownerDocument.activeElement;
5853
+ if (active && active !== rootElement.ownerDocument.body) return;
5854
+ const candidates = [
5855
+ ...request.next.map((key) => chipRemoveRefs.current.get(key)),
5856
+ triggerRef.current,
5857
+ ...rowRef.current?.querySelectorAll(focusableSelector) ?? []
5858
+ ];
5859
+ candidates.find(canTakeFocus)?.focus();
5860
+ }, [appliedFilters, rootElement]);
5569
5861
  const hasControls = Boolean(search || filters || collapsible);
5570
5862
  const hasMeta = Boolean(
5571
5863
  resultSummary || clearAction || actions && !hasControls
5572
5864
  );
5865
+ const activeFilterCount = activeFilterCountProp ?? appliedFilters?.length ?? 0;
5573
5866
  const hasAppliedFilters = activeFilterCount > 0;
5867
+ const hasAppliedChips = Boolean(appliedFilters && appliedFilters.length > 0);
5574
5868
  const usesSheet = containerWidth !== null && containerWidth < 640;
5575
- const collapseClasses = collapsibleClassNames[collapseBelow];
5869
+ const collapseClasses = (isGrid ? gridCollapsibleClassNames : collapsibleClassNames)[collapseBelow];
5576
5870
  const collapseTriggerLabel = hasAppliedFilters ? `${collapsedLabel}, ${activeFilterCount}\uAC1C \uC801\uC6A9` : collapsedLabel;
5577
5871
  const clearFiltersButton = onClearFilters ? /* @__PURE__ */ jsx32(
5578
5872
  "button",
@@ -5643,14 +5937,18 @@ var FilterToolbar = React31.forwardRef(
5643
5937
  hasControls ? /* @__PURE__ */ jsxs28(
5644
5938
  "div",
5645
5939
  {
5940
+ ref: rowRef,
5646
5941
  "data-slot": "filter-toolbar-row",
5647
- className: "flex min-h-[32px] w-full flex-wrap items-center justify-start gap-x-[var(--space-2)] gap-y-[var(--space-2)]",
5942
+ className: cn(
5943
+ "flex min-h-[32px] w-full flex-wrap items-center justify-start gap-x-[var(--space-2)] gap-y-[var(--space-2)]",
5944
+ isGrid && "@[640px]:flex-nowrap"
5945
+ ),
5648
5946
  children: [
5649
5947
  search ? /* @__PURE__ */ jsx32(
5650
5948
  "div",
5651
5949
  {
5652
5950
  "data-slot": "filter-toolbar-search",
5653
- className: "order-1 min-w-[min(160px,100%)] max-w-full flex-[1_1_0%] @[640px]:min-w-0 @[640px]:flex-[1_1_280px]",
5951
+ className: isGrid ? gridSearchClassName : "order-1 min-w-[min(160px,100%)] max-w-full flex-[1_1_0%] @[640px]:min-w-0 @[640px]:flex-[1_1_280px]",
5654
5952
  children: search
5655
5953
  }
5656
5954
  ) : null,
@@ -5677,8 +5975,16 @@ var FilterToolbar = React31.forwardRef(
5677
5975
  "div",
5678
5976
  {
5679
5977
  "data-slot": "filter-toolbar-filters",
5680
- className: "order-3 flex min-w-0 flex-[1_0_100%] flex-wrap items-center gap-[var(--space-2)] @[640px]:flex-[2_1_auto]",
5681
- children: filters
5978
+ className: isGrid ? gridFiltersClassName : "order-3 flex min-w-0 flex-[1_0_100%] flex-wrap items-center gap-[var(--space-2)] @[640px]:flex-[2_1_auto]",
5979
+ children: isGrid ? gridCells.map(({ key, cell }) => /* @__PURE__ */ jsx32(
5980
+ "div",
5981
+ {
5982
+ "data-slot": "filter-toolbar-cell",
5983
+ className: gridCellClassName,
5984
+ children: cell
5985
+ },
5986
+ key
5987
+ )) : filters
5682
5988
  }
5683
5989
  ) : null,
5684
5990
  collapsible && !overlayOpen ? /* @__PURE__ */ jsx32(
@@ -5713,18 +6019,72 @@ var FilterToolbar = React31.forwardRef(
5713
6019
  children: collapsedContent
5714
6020
  }
5715
6021
  ) : null,
6022
+ hasAppliedChips ? /* @__PURE__ */ jsxs28(
6023
+ "div",
6024
+ {
6025
+ ref: chipsRef,
6026
+ role: "group",
6027
+ "aria-label": collapsedLabel,
6028
+ "data-slot": "filter-toolbar-applied-chips",
6029
+ className: "hidden flex-wrap items-center gap-[var(--space-2)] @[640px]:flex",
6030
+ children: [
6031
+ appliedFilters.map((filter, index) => /* @__PURE__ */ jsx32(
6032
+ Chip,
6033
+ {
6034
+ ref: (button) => {
6035
+ if (button) chipRemoveRefs.current.set(filter.key, button);
6036
+ else chipRemoveRefs.current.delete(filter.key);
6037
+ },
6038
+ variant: "removable",
6039
+ size: "md",
6040
+ removeLabel: `${filter.label} \uC81C\uAC70`,
6041
+ onClick: () => {
6042
+ pendingFocusRef.current = {
6043
+ removed: [filter.key],
6044
+ next: [appliedFilters[index + 1]?.key, appliedFilters[index - 1]?.key].filter(
6045
+ (key) => key !== void 0
6046
+ )
6047
+ };
6048
+ filter.onRemove();
6049
+ },
6050
+ children: filter.label
6051
+ },
6052
+ filter.key
6053
+ )),
6054
+ onClearFilters ? /* @__PURE__ */ jsx32(
6055
+ "button",
6056
+ {
6057
+ type: "button",
6058
+ "data-slot": "filter-toolbar-clear-filters",
6059
+ className: "cursor-pointer border-0 bg-transparent p-0 text-body-sm font-medium text-text-secondary no-underline",
6060
+ onClick: () => {
6061
+ pendingFocusRef.current = {
6062
+ removed: appliedFilters.map((filter) => filter.key),
6063
+ next: []
6064
+ };
6065
+ onClearFilters();
6066
+ },
6067
+ children: "\uD574\uC81C"
6068
+ }
6069
+ ) : null
6070
+ ]
6071
+ }
6072
+ ) : null,
5716
6073
  hasAppliedFilters ? /* @__PURE__ */ jsxs28(
5717
6074
  "div",
5718
6075
  {
5719
6076
  "data-slot": "filter-toolbar-applied",
5720
- className: "flex items-center gap-[var(--space-1)] text-[11px] text-text-tertiary",
6077
+ className: cn(
6078
+ "flex items-center gap-[var(--space-1)] text-[11px] text-text-tertiary",
6079
+ hasAppliedChips && "@[640px]:hidden"
6080
+ ),
5721
6081
  children: [
5722
6082
  /* @__PURE__ */ jsxs28("span", { children: [
5723
6083
  "\uD544\uD130 ",
5724
6084
  activeFilterCount,
5725
6085
  "\uAC1C \uC801\uC6A9"
5726
6086
  ] }),
5727
- onClearFilters ? /* @__PURE__ */ jsxs28(Fragment12, { children: [
6087
+ onClearFilters ? /* @__PURE__ */ jsxs28(Fragment14, { children: [
5728
6088
  /* @__PURE__ */ jsx32("span", { "aria-hidden": "true", children: " \xB7 " }),
5729
6089
  /* @__PURE__ */ jsx32(
5730
6090
  "button",
@@ -6032,16 +6392,19 @@ function DropdownMenuSubContent({
6032
6392
  }
6033
6393
 
6034
6394
  // src/components/ui/action-menu.tsx
6035
- import { Fragment as Fragment14, jsx as jsx34, jsxs as jsxs30 } from "react/jsx-runtime";
6395
+ import { Fragment as Fragment16, jsx as jsx34, jsxs as jsxs30 } from "react/jsx-runtime";
6036
6396
  var sheetRowClassName = cn(
6037
6397
  "group flex min-h-12 w-full cursor-pointer items-center gap-[var(--space-3)] px-[var(--space-4)] py-[var(--space-2)] text-left",
6038
6398
  "border-t border-[var(--border-subtle)] text-[13px] font-medium leading-normal text-text-primary",
6039
6399
  "transition-colors motion-reduce:transition-none hover:bg-[var(--action-hover)]",
6040
6400
  "focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-[var(--focus-ring)]",
6041
6401
  "enabled:data-[destructive]:text-[var(--semantic-error-fg)]",
6042
- "disabled:cursor-not-allowed disabled:bg-[var(--action-disabled-bg)] disabled:text-text-disabled"
6402
+ "disabled:cursor-not-allowed disabled:bg-[var(--action-disabled-bg)] disabled:text-text-disabled",
6403
+ "aria-disabled:cursor-not-allowed"
6043
6404
  );
6044
6405
  var sheetRowDescriptionClassName = "block text-[12px] font-normal text-text-secondary group-disabled:text-text-disabled";
6406
+ var unavailableClassName = "text-text-disabled";
6407
+ var reasonClassName = "block text-[12px] font-normal leading-normal text-text-tertiary";
6045
6408
  var sheetGroupLabelClassName = "px-[var(--space-4)] pt-[var(--space-3)] pb-[var(--space-2)] text-[12px] font-semibold leading-normal text-text-tertiary";
6046
6409
  var sheetGroupSeparatorClassName = "h-[var(--space-2)] bg-[var(--bg-inset)]";
6047
6410
  function ActionMenuSheet({
@@ -6111,23 +6474,34 @@ function ActionMenuSheet({
6111
6474
  index > 0 ? /* @__PURE__ */ jsx34("div", { role: "separator", className: sheetGroupSeparatorClassName }) : null,
6112
6475
  /* @__PURE__ */ jsxs30("div", { role: "group", "aria-labelledby": group.label ? `${labelId}-${index}` : void 0, children: [
6113
6476
  group.label ? /* @__PURE__ */ jsx34("div", { id: `${labelId}-${index}`, className: sheetGroupLabelClassName, children: group.label }) : null,
6114
- group.items.map((item) => {
6477
+ group.items.map((item, itemIndex) => {
6115
6478
  const Icon = item.icon;
6479
+ const reason = item.disabledReason || void 0;
6480
+ const reasonId = `${labelId}-${index}-${itemIndex}-reason`;
6481
+ const itemLabelId = `${labelId}-${index}-${itemIndex}-label`;
6116
6482
  return /* @__PURE__ */ jsxs30(
6117
6483
  "button",
6118
6484
  {
6119
6485
  type: "button",
6120
6486
  "data-slot": "action-menu-item",
6121
- "data-destructive": item.destructive ? "" : void 0,
6487
+ "data-destructive": item.destructive && !reason ? "" : void 0,
6122
6488
  className: sheetRowClassName,
6123
- disabled: item.disabled,
6124
- onClick: () => handleSelect(item),
6489
+ disabled: item.disabled && !reason,
6490
+ "aria-disabled": reason ? true : void 0,
6491
+ "aria-labelledby": reason ? itemLabelId : void 0,
6492
+ "aria-describedby": reason ? reasonId : void 0,
6493
+ onClick: () => {
6494
+ if (!reason) handleSelect(item);
6495
+ },
6125
6496
  children: [
6126
- Icon ? /* @__PURE__ */ jsx34(Icon, { "aria-hidden": true, size: 17, strokeWidth: 1.75, className: "shrink-0" }) : null,
6127
- /* @__PURE__ */ jsxs30("span", { className: "min-w-0 flex-1 break-keep", children: [
6497
+ Icon ? /* @__PURE__ */ jsx34(Icon, { "aria-hidden": true, size: 17, strokeWidth: 1.75, className: cn("shrink-0", reason && unavailableClassName) }) : null,
6498
+ /* @__PURE__ */ jsx34("span", { className: "min-w-0 flex-1 break-keep", children: reason ? /* @__PURE__ */ jsxs30(Fragment16, { children: [
6499
+ /* @__PURE__ */ jsx34("span", { id: itemLabelId, className: unavailableClassName, children: item.label }),
6500
+ /* @__PURE__ */ jsx34("span", { id: reasonId, className: reasonClassName, children: reason })
6501
+ ] }) : /* @__PURE__ */ jsxs30(Fragment16, { children: [
6128
6502
  item.label,
6129
6503
  item.description ? /* @__PURE__ */ jsx34("span", { className: sheetRowDescriptionClassName, children: item.description }) : null
6130
- ] })
6504
+ ] }) })
6131
6505
  ]
6132
6506
  },
6133
6507
  item.id
@@ -6140,6 +6514,48 @@ function ActionMenuSheet({
6140
6514
  )
6141
6515
  ] }) }) });
6142
6516
  }
6517
+ function ActionMenuDropdownItem({
6518
+ item,
6519
+ onPick
6520
+ }) {
6521
+ const id = React32.useId();
6522
+ const labelId = `${id}-label`;
6523
+ const reasonId = `${id}-reason`;
6524
+ const Icon = item.icon;
6525
+ const reason = item.disabledReason || void 0;
6526
+ if (!reason) {
6527
+ return /* @__PURE__ */ jsxs30(
6528
+ DropdownMenuItem,
6529
+ {
6530
+ variant: item.destructive ? "destructive" : "default",
6531
+ disabled: item.disabled,
6532
+ onSelect: () => onPick(item),
6533
+ children: [
6534
+ Icon ? /* @__PURE__ */ jsx34(Icon, { "aria-hidden": true, size: 16, strokeWidth: 1.75 }) : null,
6535
+ item.label
6536
+ ]
6537
+ }
6538
+ );
6539
+ }
6540
+ return /* @__PURE__ */ jsxs30(
6541
+ DropdownMenuItem,
6542
+ {
6543
+ "aria-disabled": true,
6544
+ "aria-labelledby": labelId,
6545
+ "aria-describedby": reasonId,
6546
+ textValue: item.label,
6547
+ className: "aria-disabled:cursor-not-allowed",
6548
+ onSelect: (event) => event.preventDefault(),
6549
+ children: [
6550
+ Icon ? /* @__PURE__ */ jsx34(Icon, { "aria-hidden": true, size: 16, strokeWidth: 1.75, className: unavailableClassName }) : null,
6551
+ /* @__PURE__ */ jsxs30("span", { className: "min-w-0 flex-1 break-keep", children: [
6552
+ /* @__PURE__ */ jsx34("span", { id: labelId, className: unavailableClassName, children: item.label }),
6553
+ /* @__PURE__ */ jsx34("span", { id: reasonId, className: cn(reasonClassName, "max-w-64"), children: reason })
6554
+ ] })
6555
+ ]
6556
+ }
6557
+ );
6558
+ }
6143
6559
  var readPresentation = () => window.matchMedia?.(OVERLAY_DESKTOP_QUERY).matches ?? true ? "dropdown" : "sheet";
6144
6560
  var subscribeNothing = () => () => void 0;
6145
6561
  var useHydrated = () => React32.useSyncExternalStore(
@@ -6190,31 +6606,23 @@ function ActionMenu({
6190
6606
  triggerRef.current?.focus();
6191
6607
  };
6192
6608
  const triggerLabel = trigger.props["aria-label"];
6193
- return /* @__PURE__ */ jsxs30(Fragment14, { children: [
6609
+ return /* @__PURE__ */ jsxs30(Fragment16, { children: [
6194
6610
  /* @__PURE__ */ jsxs30(DropdownMenu, { open: open && presentation === "dropdown", onOpenChange: setOpen, children: [
6195
6611
  /* @__PURE__ */ jsx34(DropdownMenuTrigger, { asChild: true, "aria-expanded": open, children: /* @__PURE__ */ jsx34(Slot3.Root, { ref: triggerRef, children: trigger }) }),
6196
6612
  /* @__PURE__ */ jsx34(DropdownMenuContent, { align: "end", onCloseAutoFocus: handleDropdownCloseAutoFocus, children: menuGroups.map((group, index) => /* @__PURE__ */ jsxs30(React32.Fragment, { children: [
6197
6613
  index > 0 ? /* @__PURE__ */ jsx34(DropdownMenuSeparator, {}) : null,
6198
6614
  /* @__PURE__ */ jsxs30(DropdownMenuGroup, { children: [
6199
6615
  group.label ? /* @__PURE__ */ jsx34(DropdownMenuLabel, { children: group.label }) : null,
6200
- group.items.map((item) => {
6201
- const Icon = item.icon;
6202
- return /* @__PURE__ */ jsxs30(
6203
- DropdownMenuItem,
6204
- {
6205
- variant: item.destructive ? "destructive" : "default",
6206
- disabled: item.disabled,
6207
- onSelect: () => {
6208
- pendingRef.current = item;
6209
- },
6210
- children: [
6211
- Icon ? /* @__PURE__ */ jsx34(Icon, { "aria-hidden": true, size: 16, strokeWidth: 1.75 }) : null,
6212
- item.label
6213
- ]
6214
- },
6215
- item.id
6216
- );
6217
- })
6616
+ group.items.map((item) => /* @__PURE__ */ jsx34(
6617
+ ActionMenuDropdownItem,
6618
+ {
6619
+ item,
6620
+ onPick: (picked) => {
6621
+ pendingRef.current = picked;
6622
+ }
6623
+ },
6624
+ item.id
6625
+ ))
6218
6626
  ] })
6219
6627
  ] }, group.id)) })
6220
6628
  ] }),
@@ -6236,7 +6644,7 @@ function ActionMenu({
6236
6644
  // src/components/ui/checkbox.tsx
6237
6645
  import * as React33 from "react";
6238
6646
  import { cva as cva12 } from "class-variance-authority";
6239
- import { Check as Check3, Minus } from "lucide-react";
6647
+ import { Check as Check4, Minus } from "lucide-react";
6240
6648
  import { Checkbox as CheckboxPrimitive } from "radix-ui";
6241
6649
  import { jsx as jsx35, jsxs as jsxs31 } from "react/jsx-runtime";
6242
6650
  var checkboxFieldVariants = cva12(
@@ -6430,7 +6838,7 @@ var Checkbox = React33.forwardRef(
6430
6838
  {
6431
6839
  "data-slot": "checkbox-indicator",
6432
6840
  className: "flex items-center justify-center",
6433
- 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 })
6841
+ 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 })
6434
6842
  }
6435
6843
  )
6436
6844
  }
@@ -6519,7 +6927,7 @@ var Checkbox = React33.forwardRef(
6519
6927
  {
6520
6928
  "data-slot": "checkbox-indicator",
6521
6929
  className: "flex items-center justify-center",
6522
- 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 })
6930
+ 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 })
6523
6931
  }
6524
6932
  )
6525
6933
  }
@@ -6610,7 +7018,7 @@ function ContextMenu({
6610
7018
  // src/components/ui/list-card.tsx
6611
7019
  import * as React34 from "react";
6612
7020
  import { ChevronRight as ChevronRight2, MoreVertical } from "lucide-react";
6613
- import { Fragment as Fragment15, jsx as jsx37, jsxs as jsxs33 } from "react/jsx-runtime";
7021
+ import { Fragment as Fragment17, jsx as jsx37, jsxs as jsxs33 } from "react/jsx-runtime";
6614
7022
  function lineTokens(value) {
6615
7023
  return React34.Children.toArray(value).filter((token) => token !== "");
6616
7024
  }
@@ -6697,7 +7105,7 @@ function ListCard({
6697
7105
  children: overlay && href != null ? React34.createElement(linkComponent ?? "a", { ...linkProps, href, className: overlayClassName }, title) : title
6698
7106
  }
6699
7107
  );
6700
- const content = /* @__PURE__ */ jsxs33(Fragment15, { children: [
7108
+ const content = /* @__PURE__ */ jsxs33(Fragment17, { children: [
6701
7109
  leading != null ? /* @__PURE__ */ jsx37("span", { className: "flex min-h-[18px] items-center", children: leading }) : null,
6702
7110
  /* @__PURE__ */ jsxs33("span", { className: "min-w-0", children: [
6703
7111
  overlay && href == null ? (
@@ -6728,7 +7136,7 @@ function ListCard({
6728
7136
  ),
6729
7137
  children: [
6730
7138
  hasValue ? /* @__PURE__ */ jsx37("span", { "data-slot": `${slot}-value`, className: "text-[13px] leading-[20px] text-text-secondary tabular-nums", children: value }) : null,
6731
- actions != null ? /* @__PURE__ */ jsx37("span", { className: "relative z-[1] flex", children: /* @__PURE__ */ jsx37(
7139
+ actions != null ? /* @__PURE__ */ jsx37("span", { "data-slot": `${slot}-actions`, className: "relative z-[1] flex", children: /* @__PURE__ */ jsx37(
6732
7140
  ActionMenu,
6733
7141
  {
6734
7142
  title: typeof title === "string" ? title : void 0,
@@ -6762,7 +7170,7 @@ function ListCard({
6762
7170
  }
6763
7171
 
6764
7172
  // src/components/ui/data-table.tsx
6765
- import { Fragment as Fragment17, jsx as jsx38, jsxs as jsxs34 } from "react/jsx-runtime";
7173
+ import { Fragment as Fragment19, jsx as jsx38, jsxs as jsxs34 } from "react/jsx-runtime";
6766
7174
  var dataTableVariants = cva13(
6767
7175
  [
6768
7176
  "relative overflow-hidden rounded-[var(--radius-md)] border border-[var(--border-subtle)]",
@@ -6885,25 +7293,24 @@ function RowActionsCell({
6885
7293
  );
6886
7294
  }
6887
7295
  function RowContextMenu({
6888
- openKey,
7296
+ menuKey,
6889
7297
  position,
6890
7298
  groups,
7299
+ cards,
6891
7300
  onClose
6892
7301
  }) {
6893
- const [open, setOpen] = React35.useState(false);
6894
- const pendingRef = React35.useRef(null);
6895
- const outsideRef = React35.useRef(false);
7302
+ const [open, setOpen] = React35.useState(true);
7303
+ const closeRef = React35.useRef(null);
7304
+ const pressedOutside = React35.useRef(false);
7305
+ const close = (reason) => {
7306
+ closeRef.current ??= reason;
7307
+ setOpen(false);
7308
+ };
6896
7309
  const shownGroups = React35.useRef([]);
6897
7310
  if (groups) shownGroups.current = groups;
6898
- const gone = openKey !== null && groups === null;
7311
+ const gone = groups === null;
6899
7312
  const contentRef = React35.useRef(null);
6900
7313
  const focusInsideRef = React35.useRef(false);
6901
- React35.useEffect(() => {
6902
- pendingRef.current = null;
6903
- outsideRef.current = false;
6904
- focusInsideRef.current = false;
6905
- setOpen(openKey !== null);
6906
- }, [openKey]);
6907
7314
  useIsomorphicLayoutEffect(() => {
6908
7315
  const content = contentRef.current;
6909
7316
  if (!open || !focusInsideRef.current || !content?.isConnected) return;
@@ -6913,13 +7320,20 @@ function RowContextMenu({
6913
7320
  next.focus();
6914
7321
  });
6915
7322
  React35.useEffect(() => {
6916
- if (gone) setOpen(false);
7323
+ if (gone) close({ by: "dismiss", outside: false });
6917
7324
  }, [gone]);
7325
+ useIsomorphicLayoutEffect(() => {
7326
+ if (!cards || !open || closeRef.current) return;
7327
+ const active = document.activeElement;
7328
+ close({ by: "layout", focusInside: Boolean(active && contentRef.current?.contains(active)) });
7329
+ }, [cards, open]);
6918
7330
  return /* @__PURE__ */ jsx38(
6919
7331
  ContextMenu,
6920
7332
  {
6921
7333
  open: open && !gone,
6922
- onOpenChange: setOpen,
7334
+ onOpenChange: (next) => {
7335
+ if (!next) close({ by: "dismiss", outside: false });
7336
+ },
6923
7337
  position,
6924
7338
  contentProps: {
6925
7339
  align: "start",
@@ -6933,37 +7347,31 @@ function RowContextMenu({
6933
7347
  }
6934
7348
  },
6935
7349
  onInteractOutside: () => {
6936
- outsideRef.current = true;
7350
+ pressedOutside.current = true;
7351
+ close({ by: "dismiss", outside: true });
6937
7352
  },
6938
7353
  onCloseAutoFocus: (event) => {
6939
7354
  event.preventDefault();
6940
- const item = pendingRef.current;
6941
- pendingRef.current = null;
6942
- onClose(item, outsideRef.current);
7355
+ let reason = closeRef.current ?? { by: "dismiss", outside: false };
7356
+ if (pressedOutside.current && reason.by === "dismiss") reason = { by: "dismiss", outside: true };
7357
+ if (pressedOutside.current && reason.by === "layout") reason = { by: "layout", focusInside: false };
7358
+ onClose(menuKey, reason);
6943
7359
  }
6944
7360
  },
6945
7361
  children: shownGroups.current.map((group, index) => /* @__PURE__ */ jsxs34(React35.Fragment, { children: [
6946
7362
  index > 0 ? /* @__PURE__ */ jsx38(DropdownMenuSeparator, {}) : null,
6947
7363
  /* @__PURE__ */ jsxs34(DropdownMenuGroup, { children: [
6948
7364
  group.label ? /* @__PURE__ */ jsx38(DropdownMenuLabel, { children: group.label }) : null,
6949
- group.items.map((item) => {
6950
- const Icon = item.icon;
6951
- return /* @__PURE__ */ jsxs34(
6952
- DropdownMenuItem,
6953
- {
6954
- variant: item.destructive ? "destructive" : "default",
6955
- disabled: item.disabled,
6956
- onSelect: () => {
6957
- pendingRef.current = item;
6958
- },
6959
- children: [
6960
- Icon ? /* @__PURE__ */ jsx38(Icon, { "aria-hidden": true, size: 16, strokeWidth: 1.75 }) : null,
6961
- item.label
6962
- ]
6963
- },
6964
- item.id
6965
- );
6966
- })
7365
+ group.items.map((item) => /* @__PURE__ */ jsx38(
7366
+ ActionMenuDropdownItem,
7367
+ {
7368
+ item,
7369
+ onPick: (picked) => {
7370
+ closeRef.current ??= { by: "item", item: picked };
7371
+ }
7372
+ },
7373
+ item.id
7374
+ ))
6967
7375
  ] })
6968
7376
  ] }, group.id))
6969
7377
  }
@@ -7044,7 +7452,7 @@ function MobileSortMenu({
7044
7452
  size: "sm",
7045
7453
  className: "ml-auto min-w-0",
7046
7454
  children: [
7047
- active && sortBy ? /* @__PURE__ */ jsxs34(Fragment17, { children: [
7455
+ active && sortBy ? /* @__PURE__ */ jsxs34(Fragment19, { children: [
7048
7456
  /* @__PURE__ */ jsx38("span", { className: "sr-only", children: "\uC815\uB82C " }),
7049
7457
  /* @__PURE__ */ jsx38("span", { className: "truncate", children: active.header }),
7050
7458
  /* @__PURE__ */ jsxs34("span", { className: "sr-only", children: [
@@ -7317,7 +7725,7 @@ function DataTableRowView({
7317
7725
  if (!expand) {
7318
7726
  return dataRow;
7319
7727
  }
7320
- return /* @__PURE__ */ jsxs34(Fragment17, { children: [
7728
+ return /* @__PURE__ */ jsxs34(Fragment19, { children: [
7321
7729
  dataRow,
7322
7730
  /* @__PURE__ */ jsx38(
7323
7731
  "div",
@@ -7929,15 +8337,22 @@ function DataTableInner({
7929
8337
  contextMenuKey.current += 1;
7930
8338
  setContextMenu({ rowId, position: { x: event.clientX, y: event.clientY }, key: contextMenuKey.current });
7931
8339
  };
7932
- const closeRowContextMenu = (item, outside) => {
8340
+ const closeRowContextMenu = (key, close) => {
8341
+ if (key !== contextMenuKey.current) {
8342
+ return;
8343
+ }
7933
8344
  const rowId = contextMenu?.rowId;
7934
- if (rowId !== void 0 && (item || !outside)) {
7935
- const list = desktopListRef.current;
7936
- const row = Array.from(list?.querySelectorAll('[data-slot="data-table-row"]') ?? []).find(
7937
- (node) => node.getAttribute("data-row-id") === rowId
8345
+ const refocus = close.by === "item" || (close.by === "dismiss" ? !close.outside : close.focusInside);
8346
+ if (rowId !== void 0 && refocus) {
8347
+ const cards = readLayout(desktopRef.current) === "mobile";
8348
+ const list = cards ? mobileListRef.current : desktopListRef.current;
8349
+ const row = Array.from(
8350
+ list?.querySelectorAll(cards ? '[data-slot="data-table-mobile-card"]' : '[data-slot="data-table-row"]') ?? []
8351
+ ).find((node) => node.getAttribute("data-row-id") === rowId);
8352
+ const trigger = row?.querySelector(
8353
+ cards ? '[data-slot="list-card-actions"] button' : '[data-slot="data-table-row-actions"] button'
7938
8354
  );
7939
- const trigger = row?.querySelector('[data-slot="data-table-row-actions"] button');
7940
- const target = trigger ?? (row?.hasAttribute("tabindex") ? row : null) ?? list ?? desktopRef.current;
8355
+ const target = trigger ?? (row && (cards || row.hasAttribute("tabindex")) ? row : null) ?? list ?? (cards ? mobileRef : desktopRef).current;
7941
8356
  if (target) {
7942
8357
  if (!target.hasAttribute("tabindex") && !target.matches("a[href],button,input")) {
7943
8358
  target.tabIndex = -1;
@@ -7946,7 +8361,7 @@ function DataTableInner({
7946
8361
  }
7947
8362
  }
7948
8363
  setContextMenu(null);
7949
- item?.onSelect();
8364
+ if (close.by === "item") close.item.onSelect();
7950
8365
  };
7951
8366
  const handleRowContextMenu = rowActions ? openRowContextMenu : onRowContextMenu;
7952
8367
  const latestHandlers = React35.useRef({ toggleRow, activateRow, onRowContextMenu, handleRowContextMenu });
@@ -8198,14 +8613,16 @@ function DataTableInner({
8198
8613
  )
8199
8614
  ] }),
8200
8615
  footer != null ? /* @__PURE__ */ jsx38("div", { "data-slot": "data-table-footer", className: "border-t border-[var(--border-subtle)] empty:hidden", children: footer }) : null,
8201
- hasRowActions ? /* @__PURE__ */ jsx38(
8616
+ hasRowActions && contextMenu ? /* @__PURE__ */ jsx38(
8202
8617
  RowContextMenu,
8203
8618
  {
8204
- openKey: contextMenu?.key ?? null,
8205
- position: contextMenu?.position ?? null,
8619
+ menuKey: contextMenu.key,
8620
+ position: contextMenu.position,
8206
8621
  groups: contextGroups,
8622
+ cards: activeLayout === "mobile",
8207
8623
  onClose: closeRowContextMenu
8208
- }
8624
+ },
8625
+ contextMenu.key
8209
8626
  ) : null
8210
8627
  ]
8211
8628
  }
@@ -8263,6 +8680,35 @@ function ActionButton({ action }) {
8263
8680
  }
8264
8681
  );
8265
8682
  }
8683
+ function CardActionButton({ action }) {
8684
+ const Icon = action.icon;
8685
+ return /* @__PURE__ */ jsxs35(
8686
+ Button,
8687
+ {
8688
+ appearance: "outline",
8689
+ tone: action.variant === "destructive" ? "danger" : "neutral",
8690
+ size: "sm",
8691
+ className: "before:-inset-y-[5px]",
8692
+ "data-slot": "bulk-action-bar-action",
8693
+ "data-variant": action.variant ?? "default",
8694
+ disabled: action.disabled,
8695
+ title: action.title,
8696
+ onClick: action.onClick,
8697
+ children: [
8698
+ Icon ? /* @__PURE__ */ jsx39(
8699
+ Icon,
8700
+ {
8701
+ "aria-hidden": "true",
8702
+ "data-slot": "bulk-action-bar-action-icon",
8703
+ size: 16,
8704
+ strokeWidth: 1.75
8705
+ }
8706
+ ) : null,
8707
+ action.label
8708
+ ]
8709
+ }
8710
+ );
8711
+ }
8266
8712
  function Divider() {
8267
8713
  return /* @__PURE__ */ jsx39(
8268
8714
  "span",
@@ -8305,24 +8751,24 @@ function ClearButton({ onClear }) {
8305
8751
  }
8306
8752
  );
8307
8753
  }
8754
+ var CLEAR_CLEARANCE = 32;
8308
8755
  var INLINE_HEIGHT = 40;
8309
8756
  var HIT_AREA = 44;
8310
8757
  var HIT_INSET = 8;
8758
+ var FLOATING_BOTTOM = "calc(var(--space-4) + env(safe-area-inset-bottom))";
8311
8759
  var floatingStyle = {
8312
8760
  position: "sticky",
8313
- bottom: "calc(var(--space-4) + env(safe-area-inset-bottom))",
8761
+ bottom: FLOATING_BOTTOM,
8314
8762
  zIndex: 1100,
8315
8763
  margin: "0 auto",
8316
8764
  display: "flex",
8317
- minHeight: 32,
8318
- width: "fit-content",
8319
- maxWidth: "calc(100% - var(--space-8))",
8320
- alignItems: "center",
8321
- gap: "var(--space-2)",
8322
- borderRadius: 9999,
8765
+ boxSizing: "border-box",
8766
+ width: "min(360px, calc(100% - var(--space-8)))",
8767
+ flexDirection: "column",
8768
+ borderRadius: "var(--radius-xl)",
8323
8769
  border: "1px solid var(--border)",
8324
8770
  background: "var(--bg-raised)",
8325
- padding: "var(--space-1) var(--space-2)",
8771
+ padding: "var(--space-2) var(--space-4) var(--space-3)",
8326
8772
  boxShadow: "var(--shadow-8)"
8327
8773
  };
8328
8774
  var inlineStyle = {
@@ -8364,7 +8810,117 @@ function warnInlineOnce() {
8364
8810
  warnedInline = true;
8365
8811
  console.warn('BulkActionBar: placement="inline" is deprecated and removed in the next minor. Use "floating".');
8366
8812
  }
8813
+ var warnedTrailing = false;
8814
+ function warnTrailingOnce() {
8815
+ if (warnedTrailing || typeof process === "undefined" || process.env.NODE_ENV === "production") {
8816
+ return;
8817
+ }
8818
+ warnedTrailing = true;
8819
+ console.warn("BulkActionBar: the floating card ignores `trailing`. Put edit-mode controls at the right end of the toolbar.");
8820
+ }
8821
+ var OFFSET_PROPERTY = "--olun-bulk-bar-offset";
8822
+ var LIFT_ZONE = 204;
8823
+ var cardOffsets = /* @__PURE__ */ new Map();
8824
+ function writeOffset() {
8825
+ const root = document.documentElement;
8826
+ const offset = Math.max(0, ...cardOffsets.values());
8827
+ const value = offset > 0 ? `${offset}px` : "";
8828
+ if (root.style.getPropertyValue(OFFSET_PROPERTY) === value) return;
8829
+ if (value) root.style.setProperty(OFFSET_PROPERTY, value);
8830
+ else root.style.removeProperty(OFFSET_PROPERTY);
8831
+ }
8832
+ function reportOffset(key, card) {
8833
+ let offset = 0;
8834
+ if (card.offsetHeight > 0) {
8835
+ const { top, bottom } = card.getBoundingClientRect();
8836
+ if (window.innerHeight - bottom <= LIFT_ZONE) offset = Math.max(0, Math.round(window.innerHeight - top));
8837
+ }
8838
+ if (offset === (cardOffsets.get(key) ?? 0)) return;
8839
+ if (offset > 0) cardOffsets.set(key, offset);
8840
+ else cardOffsets.delete(key);
8841
+ writeOffset();
8842
+ }
8367
8843
  var useIsomorphicLayoutEffect2 = typeof window === "undefined" ? React36.useEffect : React36.useLayoutEffect;
8844
+ function useBottomOffset(card) {
8845
+ const [key] = React36.useState(() => ({}));
8846
+ React36.useEffect(() => {
8847
+ if (!card) return;
8848
+ let frame = 0;
8849
+ let stopped = false;
8850
+ let listening = false;
8851
+ const schedule = () => {
8852
+ if (frame || stopped) return;
8853
+ frame = requestAnimationFrame(measure);
8854
+ };
8855
+ const listen = (on) => {
8856
+ if (on === listening) return;
8857
+ listening = on;
8858
+ if (on) {
8859
+ window.addEventListener("resize", schedule);
8860
+ window.addEventListener("scroll", schedule, { capture: true, passive: true });
8861
+ } else {
8862
+ window.removeEventListener("resize", schedule);
8863
+ window.removeEventListener("scroll", schedule, { capture: true });
8864
+ }
8865
+ };
8866
+ let watcher = null;
8867
+ let watched = "";
8868
+ const arm = (margin, threshold) => {
8869
+ let first = true;
8870
+ watcher = new IntersectionObserver(
8871
+ ([entry]) => {
8872
+ if (stopped) return;
8873
+ if (first) {
8874
+ first = false;
8875
+ if (entry.intersectionRatio === threshold) return;
8876
+ watcher?.disconnect();
8877
+ arm(margin, entry.intersectionRatio);
8878
+ }
8879
+ schedule();
8880
+ },
8881
+ { root: document, rootMargin: margin, threshold }
8882
+ );
8883
+ watcher.observe(card);
8884
+ };
8885
+ const watch = (on) => {
8886
+ let margin = "";
8887
+ if (on && typeof IntersectionObserver !== "undefined") {
8888
+ const { top, right, bottom, left } = card.getBoundingClientRect();
8889
+ const { clientWidth, clientHeight } = document.documentElement;
8890
+ margin = [top, clientWidth - right, clientHeight - bottom, left].map((inset) => `${-Math.floor(inset)}px`).join(" ");
8891
+ }
8892
+ if (margin === watched) return;
8893
+ watched = margin;
8894
+ watcher?.disconnect();
8895
+ watcher = null;
8896
+ if (margin) arm(margin, 1);
8897
+ };
8898
+ const observer = typeof ResizeObserver === "undefined" ? null : new ResizeObserver(() => schedule());
8899
+ const measure = () => {
8900
+ frame = 0;
8901
+ reportOffset(key, card);
8902
+ const shown = card.offsetHeight > 0;
8903
+ listen(shown || !observer);
8904
+ watch(shown);
8905
+ };
8906
+ measure();
8907
+ observer?.observe(card);
8908
+ card.addEventListener("animationend", schedule);
8909
+ return () => {
8910
+ stopped = true;
8911
+ cancelAnimationFrame(frame);
8912
+ listen(false);
8913
+ watcher?.disconnect();
8914
+ observer?.disconnect();
8915
+ card.removeEventListener("animationend", schedule);
8916
+ cardOffsets.delete(key);
8917
+ writeOffset();
8918
+ };
8919
+ }, [key, card]);
8920
+ useIsomorphicLayoutEffect2(() => {
8921
+ if (card && typeof ResizeObserver === "undefined") reportOffset(key, card);
8922
+ });
8923
+ }
8368
8924
  function useCompactSummary(slot, enabled) {
8369
8925
  const [fullWidth, setFullWidth] = React36.useState(null);
8370
8926
  const measure = React36.useCallback(() => {
@@ -8402,7 +8958,9 @@ function BulkActionBar({
8402
8958
  }) {
8403
8959
  const inline = placement === "inline";
8404
8960
  if (inline) warnInlineOnce();
8405
- const trailingSlot = trailing !== void 0 && trailing !== null ? /* @__PURE__ */ jsx39(
8961
+ const hasTrailing = trailing !== void 0 && trailing !== null;
8962
+ if (!inline && hasTrailing) warnTrailingOnce();
8963
+ const trailingSlot = inline && hasTrailing ? /* @__PURE__ */ jsx39(
8406
8964
  "div",
8407
8965
  {
8408
8966
  "data-slot": "bulk-action-bar-trailing",
@@ -8420,6 +8978,8 @@ function BulkActionBar({
8420
8978
  summaryElement,
8421
8979
  inline && compactSummary !== void 0 && compactSummary !== null
8422
8980
  );
8981
+ const [card, setCard] = React36.useState(null);
8982
+ useBottomOffset(card);
8423
8983
  if (count === 0 && idleContent !== void 0 && trailingSlot) {
8424
8984
  return /* @__PURE__ */ jsxs35(
8425
8985
  "div",
@@ -8474,16 +9034,31 @@ function BulkActionBar({
8474
9034
  "span",
8475
9035
  {
8476
9036
  "data-slot": "bulk-action-bar-count",
8477
- style: {
8478
- paddingLeft: inline ? void 0 : "var(--space-2)",
9037
+ style: inline ? {
8479
9038
  paddingRight: "var(--space-1)",
8480
9039
  fontFamily: "var(--font-sans)",
8481
9040
  fontSize: 13,
8482
- fontWeight: inline ? 600 : 500,
9041
+ fontWeight: 600,
8483
9042
  fontVariantNumeric: "tabular-nums",
8484
9043
  color: "var(--text-primary)",
8485
9044
  whiteSpace: "nowrap"
8486
- },
9045
+ } : (
9046
+ // First row, 32px like the ✕ beside it; the right padding keeps text clear of it,
9047
+ // and a long unbroken unit or hint breaks instead of running under it.
9048
+ {
9049
+ display: "flex",
9050
+ minHeight: 32,
9051
+ alignItems: "center",
9052
+ paddingRight: CLEAR_CLEARANCE,
9053
+ fontFamily: "var(--font-sans)",
9054
+ fontSize: 13,
9055
+ fontWeight: 500,
9056
+ fontVariantNumeric: "tabular-nums",
9057
+ color: "var(--text-primary)",
9058
+ wordBreak: "keep-all",
9059
+ overflowWrap: "anywhere"
9060
+ }
9061
+ ),
8487
9062
  children: count > 0 ? `${count.toLocaleString("ko-KR")}${unit} \uC120\uD0DD\uB428` : emptyHint
8488
9063
  }
8489
9064
  );
@@ -8493,7 +9068,7 @@ function BulkActionBar({
8493
9068
  ref: setSummaryElement,
8494
9069
  "data-slot": "bulk-action-bar-summary",
8495
9070
  style: {
8496
- display: inline ? "flex" : "inline-flex",
9071
+ display: "flex",
8497
9072
  minWidth: 0,
8498
9073
  alignItems: "center",
8499
9074
  columnGap: "var(--space-3)",
@@ -8516,27 +9091,30 @@ function BulkActionBar({
8516
9091
  whiteSpace: "nowrap",
8517
9092
  contain: "inline-size"
8518
9093
  }
8519
- ) : { flexWrap: "wrap", wordBreak: "keep-all" }
9094
+ ) : (
9095
+ // Its own row at the card's full inner width; wraps, never clips.
9096
+ { flexWrap: "wrap", lineHeight: 1.5, wordBreak: "keep-all", overflowWrap: "anywhere" }
9097
+ )
8520
9098
  },
8521
9099
  children: compact ? compactSummary : summary
8522
9100
  }
8523
9101
  ) : null;
8524
- const actionGroup = /* @__PURE__ */ jsx39(
8525
- "div",
8526
- {
8527
- "data-slot": "bulk-action-bar-actions",
8528
- style: {
8529
- display: "flex",
8530
- minWidth: 0,
8531
- flexWrap: inline ? "nowrap" : "wrap",
8532
- alignItems: "center",
8533
- justifyContent: "flex-end",
8534
- gap: 2
8535
- },
8536
- children: actions.map((action) => /* @__PURE__ */ jsx39(ActionButton, { action }, action.key))
8537
- }
8538
- );
8539
9102
  if (inline) {
9103
+ const actionGroup = /* @__PURE__ */ jsx39(
9104
+ "div",
9105
+ {
9106
+ "data-slot": "bulk-action-bar-actions",
9107
+ style: {
9108
+ display: "flex",
9109
+ minWidth: 0,
9110
+ flexWrap: "nowrap",
9111
+ alignItems: "center",
9112
+ justifyContent: "flex-end",
9113
+ gap: 2
9114
+ },
9115
+ children: actions.map((action) => /* @__PURE__ */ jsx39(ActionButton, { action }, action.key))
9116
+ }
9117
+ );
8540
9118
  return /* @__PURE__ */ jsxs35(
8541
9119
  "div",
8542
9120
  {
@@ -8601,6 +9179,7 @@ function BulkActionBar({
8601
9179
  "animate-[olun-bulk-action-bar-in_var(--dur-fast)_var(--ease-out)_both] motion-reduce:animate-none",
8602
9180
  className
8603
9181
  ),
9182
+ ref: setCard,
8604
9183
  style: { ...floatingStyle, ...style },
8605
9184
  ...props,
8606
9185
  children: [
@@ -8612,8 +9191,8 @@ function BulkActionBar({
8612
9191
  style: {
8613
9192
  display: "flex",
8614
9193
  minWidth: 0,
8615
- alignItems: "center",
8616
- gap: "var(--space-2)"
9194
+ flexDirection: "column",
9195
+ rowGap: "var(--space-2)"
8617
9196
  },
8618
9197
  children: [
8619
9198
  countLabel,
@@ -8621,11 +9200,29 @@ function BulkActionBar({
8621
9200
  ]
8622
9201
  }
8623
9202
  ),
8624
- count > 0 && actions.length > 0 ? /* @__PURE__ */ jsx39(Divider, {}) : null,
8625
- actionGroup,
8626
- /* @__PURE__ */ jsx39(Divider, {}),
8627
- /* @__PURE__ */ jsx39(ClearButton, { onClear }),
8628
- trailingSlot
9203
+ actions.length > 0 ? /* @__PURE__ */ jsx39(
9204
+ "div",
9205
+ {
9206
+ "data-slot": "bulk-action-bar-actions",
9207
+ style: {
9208
+ display: "flex",
9209
+ flexWrap: "wrap",
9210
+ justifyContent: "flex-end",
9211
+ gap: "var(--space-2)",
9212
+ marginTop: "var(--space-3)"
9213
+ },
9214
+ children: actions.map((action) => /* @__PURE__ */ jsx39(CardActionButton, { action }, action.key))
9215
+ }
9216
+ ) : null,
9217
+ /* @__PURE__ */ jsx39(
9218
+ IconButton,
9219
+ {
9220
+ icon: X4,
9221
+ "aria-label": "\uC120\uD0DD \uD574\uC81C",
9222
+ onClick: onClear,
9223
+ style: { position: "absolute", top: "var(--space-2)", right: "var(--space-2)" }
9224
+ }
9225
+ )
8629
9226
  ]
8630
9227
  }
8631
9228
  );
@@ -9957,7 +10554,7 @@ function TablePagination({
9957
10554
 
9958
10555
  // src/components/ui/mode-toggle.tsx
9959
10556
  import { cva as cva19 } from "class-variance-authority";
9960
- import { jsx as jsx56, jsxs as jsxs45 } from "react/jsx-runtime";
10557
+ import { Fragment as Fragment20, jsx as jsx56, jsxs as jsxs45 } from "react/jsx-runtime";
9961
10558
  var modeToggleVariants = cva19(
9962
10559
  [
9963
10560
  "inline-flex items-center rounded-[var(--radius-md)] border border-[var(--border-subtle)]",
@@ -9980,6 +10577,17 @@ var modeToggleItemVariants = cva19(
9980
10577
  "[&_svg]:pointer-events-none [&_svg]:size-[14px] [&_svg]:shrink-0 [&_svg]:stroke-[1.75]"
9981
10578
  ].join(" ")
9982
10579
  );
10580
+ function formatModeToggleCount(value) {
10581
+ return value > 99 ? "99+" : String(value);
10582
+ }
10583
+ function isShownModeToggleCount(value) {
10584
+ if (value === void 0) return false;
10585
+ if (Number.isInteger(value) && value >= 0) return true;
10586
+ if (typeof process !== "undefined" && process.env.NODE_ENV !== "production") {
10587
+ console.warn(`ModeToggle: count must be an integer 0 or greater (got ${value}). The count is not shown.`);
10588
+ }
10589
+ return false;
10590
+ }
9983
10591
  function ModeToggle({
9984
10592
  className,
9985
10593
  itemClassName,
@@ -10020,7 +10628,11 @@ function ModeToggle({
10020
10628
  },
10021
10629
  children: [
10022
10630
  Icon ? /* @__PURE__ */ jsx56(Icon, { "aria-hidden": "true", "data-slot": "mode-toggle-icon" }) : null,
10023
- /* @__PURE__ */ jsx56("span", { children: option.label })
10631
+ /* @__PURE__ */ jsx56("span", { children: option.label }),
10632
+ isShownModeToggleCount(option.count) ? /* @__PURE__ */ jsxs45(Fragment20, { children: [
10633
+ /* @__PURE__ */ jsx56("span", { "aria-hidden": "true", "data-slot": "mode-toggle-count", className: tabsBadgeVariants(), children: formatModeToggleCount(option.count) }),
10634
+ /* @__PURE__ */ jsx56("span", { className: "sr-only", children: ` ${option.count}` })
10635
+ ] }) : null
10024
10636
  ]
10025
10637
  },
10026
10638
  option.value
@@ -10236,7 +10848,8 @@ var toastVariants = cva21(
10236
10848
  );
10237
10849
  var toastViewportVariants = cva21(
10238
10850
  [
10239
- "fixed bottom-[var(--space-6)] right-[var(--space-6)] z-[var(--z-toast,50)]",
10851
+ // A floating BulkActionBar publishes --olun-bulk-bar-offset while it is up; sit 8px above it.
10852
+ "fixed bottom-[max(var(--space-6),calc(var(--olun-bulk-bar-offset,0px)+var(--space-2)))] right-[var(--space-6)] z-[var(--z-toast,50)]",
10240
10853
  "flex w-[min(420px,calc(100vw-var(--space-12)))] list-none flex-col gap-[var(--space-2)]",
10241
10854
  "pointer-events-none outline-none"
10242
10855
  ].join(" ")
@@ -10468,7 +11081,7 @@ function ToastProvider({ children, maxToasts = 3, className, ...props }) {
10468
11081
  import * as React48 from "react";
10469
11082
  import { cva as cva22 } from "class-variance-authority";
10470
11083
  import { AlertCircle as AlertCircle4 } from "lucide-react";
10471
- import { Fragment as Fragment18, jsx as jsx59, jsxs as jsxs48 } from "react/jsx-runtime";
11084
+ import { Fragment as Fragment21, jsx as jsx59, jsxs as jsxs48 } from "react/jsx-runtime";
10472
11085
  var otpFieldVariants = cva22("flex w-full min-w-0 flex-col gap-[var(--space-3)]", {
10473
11086
  variants: {
10474
11087
  size: {
@@ -10798,7 +11411,7 @@ var OTPField = React48.forwardRef(
10798
11411
  "data-slot": "otp-field-message",
10799
11412
  "data-kind": errorText ? "error" : "helper",
10800
11413
  className: otpFieldMessageVariants({ state: resolvedState }),
10801
- children: errorText ? /* @__PURE__ */ jsxs48(Fragment18, { children: [
11414
+ children: errorText ? /* @__PURE__ */ jsxs48(Fragment21, { children: [
10802
11415
  /* @__PURE__ */ jsx59(
10803
11416
  AlertCircle4,
10804
11417
  {
@@ -11686,7 +12299,7 @@ function TreeView({
11686
12299
 
11687
12300
  // src/components/branding/olun-logo.tsx
11688
12301
  import { OLUN_MASTER, OLUN_SYMBOL, OLUN_WORDMARK } from "@olundot/tokens/brand";
11689
- import { Fragment as Fragment19, jsx as jsx66, jsxs as jsxs53 } from "react/jsx-runtime";
12302
+ import { Fragment as Fragment22, jsx as jsx66, jsxs as jsxs53 } from "react/jsx-runtime";
11690
12303
  var ACCENT_FILL = "var(--accent-pop)";
11691
12304
  var INK = { color: "var(--text-primary)" };
11692
12305
  var INK_TIGHT = { ...INK, display: "block", overflow: "visible" };
@@ -11696,7 +12309,7 @@ function symbolGeometry(size) {
11696
12309
  function SymbolCircles({ size }) {
11697
12310
  const { ring } = OLUN_SYMBOL;
11698
12311
  const g = symbolGeometry(size);
11699
- return /* @__PURE__ */ jsxs53(Fragment19, { children: [
12312
+ return /* @__PURE__ */ jsxs53(Fragment22, { children: [
11700
12313
  /* @__PURE__ */ jsx66(
11701
12314
  "circle",
11702
12315
  {