@olundot/ui 0.20.0-next.9 → 0.20.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/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
  ] })
@@ -4248,7 +4293,8 @@ var overlaySize = {
4248
4293
  dialog: {
4249
4294
  sm: "max-w-[420px]",
4250
4295
  md: "max-w-[560px]",
4251
- lg: "max-w-[720px]"
4296
+ lg: "max-w-[720px]",
4297
+ xl: "max-w-[960px]"
4252
4298
  },
4253
4299
  sheet: {
4254
4300
  md: "lg:w-[480px] lg:max-w-[480px]",
@@ -4779,7 +4825,7 @@ function SheetDescription({
4779
4825
  }
4780
4826
 
4781
4827
  // src/components/ui/dialog.tsx
4782
- import { Fragment as Fragment9, jsx as jsx26, jsxs as jsxs23 } from "react/jsx-runtime";
4828
+ import { Fragment as Fragment10, jsx as jsx26, jsxs as jsxs23 } from "react/jsx-runtime";
4783
4829
  function dialogMobileSheetStyle(style) {
4784
4830
  return getDefaultSheetMotionStyle(style);
4785
4831
  }
@@ -4873,7 +4919,7 @@ function StructuredDialog({
4873
4919
  footerRow
4874
4920
  ]
4875
4921
  }
4876
- ) : /* @__PURE__ */ jsxs23(Fragment9, { children: [
4922
+ ) : /* @__PURE__ */ jsxs23(Fragment10, { children: [
4877
4923
  body,
4878
4924
  footerRow
4879
4925
  ] })
@@ -5077,7 +5123,7 @@ function FormModal({
5077
5123
 
5078
5124
  // src/components/ui/alert-dialog.tsx
5079
5125
  import { AlertDialog as AlertDialogPrimitive2 } from "radix-ui";
5080
- import { Fragment as Fragment10, jsx as jsx28, jsxs as jsxs25 } from "react/jsx-runtime";
5126
+ import { Fragment as Fragment11, jsx as jsx28, jsxs as jsxs25 } from "react/jsx-runtime";
5081
5127
  function AlertDialogFrame({
5082
5128
  open,
5083
5129
  onOpenChange,
@@ -5141,7 +5187,7 @@ function AlertDialog({
5141
5187
  disabled: confirmDisabled,
5142
5188
  onError
5143
5189
  });
5144
- const footer = confirmLabel != null ? /* @__PURE__ */ jsxs25(Fragment10, { children: [
5190
+ const footer = confirmLabel != null ? /* @__PURE__ */ jsxs25(Fragment11, { children: [
5145
5191
  /* @__PURE__ */ jsx28(AlertDialogPrimitive2.Cancel, { asChild: true, children: /* @__PURE__ */ jsx28(Button, { appearance: "outline", tone: "neutral", size: "lg", className: footerButtonHitAreaClassName, disabled: isBusy, children: cancelLabel }) }),
5146
5192
  /* @__PURE__ */ jsx28(
5147
5193
  Button,
@@ -5219,7 +5265,7 @@ import * as React29 from "react";
5219
5265
  import { cva as cva11 } from "class-variance-authority";
5220
5266
  import { X as X3 } from "lucide-react";
5221
5267
  import { Slot as Slot2 } from "radix-ui";
5222
- import { Fragment as Fragment11, jsx as jsx30, jsxs as jsxs26 } from "react/jsx-runtime";
5268
+ import { Fragment as Fragment12, jsx as jsx30, jsxs as jsxs26 } from "react/jsx-runtime";
5223
5269
  var chipVariants = cva11(
5224
5270
  [
5225
5271
  "inline-flex w-fit shrink-0 items-center justify-center whitespace-nowrap",
@@ -5318,7 +5364,7 @@ function ChipCount({
5318
5364
  tone,
5319
5365
  size
5320
5366
  }) {
5321
- return /* @__PURE__ */ jsxs26(Fragment11, { children: [
5367
+ return /* @__PURE__ */ jsxs26(Fragment12, { children: [
5322
5368
  /* @__PURE__ */ jsx30(
5323
5369
  "span",
5324
5370
  {
@@ -5501,7 +5547,7 @@ var PopoverArrow = React30.forwardRef(({ className, ...props }, ref) => /* @__PU
5501
5547
  PopoverArrow.displayName = PopoverPrimitive.Arrow.displayName;
5502
5548
 
5503
5549
  // src/components/ui/filter-toolbar.tsx
5504
- import { Fragment as Fragment12, jsx as jsx32, jsxs as jsxs28 } from "react/jsx-runtime";
5550
+ import { Fragment as Fragment14, jsx as jsx32, jsxs as jsxs28 } from "react/jsx-runtime";
5505
5551
  var collapsibleClassNames = {
5506
5552
  sm: {
5507
5553
  inline: "hidden @[640px]:flex",
@@ -5520,6 +5566,41 @@ var collapsibleClassNames = {
5520
5566
  trigger: "hidden"
5521
5567
  }
5522
5568
  };
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
+ 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";
5577
+ var gridCellClassName = cn(
5578
+ "contents @[640px]:block @[640px]:order-3 @[640px]:min-w-0 @[640px]:flex-[3_1_0%]",
5579
+ "@[640px]:[&:has([data-slot=select-trigger])]:flex-[2_1_0%]",
5580
+ "@[640px]:[&:has(>[data-slot=chip])]:flex-none",
5581
+ // A Chip group is named: a role="group" child that holds the Chips. A control that shows its
5582
+ // values as Chips keeps the default share.
5583
+ "@[640px]:[&:has(>[role=group]>[data-slot=chip])]:flex-none",
5584
+ // The control fills its cell, through a width the consumer put on a wrapper.
5585
+ "@[640px]:[&>:not([data-slot=chip])]:w-full @[640px]:[&_[data-slot=select-trigger]]:w-full"
5586
+ );
5587
+ var MAX_GRID_FILTERS = 3;
5588
+ var focusableSelector = "input, button, [tabindex]:not([tabindex='-1'])";
5589
+ function canTakeFocus(element) {
5590
+ return Boolean(element && element.getClientRects().length > 0 && !element.matches(":disabled"));
5591
+ }
5592
+ function filterCells(node, prefix = "") {
5593
+ return React31.Children.toArray(node).flatMap((child, index) => {
5594
+ const key = `${prefix}${React31.isValidElement(child) ? child.key : index}`;
5595
+ return React31.isValidElement(child) && child.type === React31.Fragment ? filterCells(child.props.children, `${key}/`) : [{ key, cell: child }];
5596
+ });
5597
+ }
5598
+ function warnGridFilters(count) {
5599
+ if (typeof process === "undefined" || process.env.NODE_ENV === "production") return;
5600
+ console.warn(
5601
+ `FilterToolbar: layout="grid" fits ${MAX_GRID_FILTERS} filters on the line, got ${count}. Move the rest to \`collapsible\`.`
5602
+ );
5603
+ }
5523
5604
  var FilterToolbar = React31.forwardRef(
5524
5605
  ({
5525
5606
  className,
@@ -5527,12 +5608,14 @@ var FilterToolbar = React31.forwardRef(
5527
5608
  filters,
5528
5609
  collapsible,
5529
5610
  collapseBelow = "lg",
5530
- activeFilterCount = 0,
5611
+ activeFilterCount: activeFilterCountProp,
5531
5612
  onClearFilters,
5532
5613
  collapsedLabel = "\uD544\uD130",
5533
5614
  resultSummary,
5534
5615
  clearAction,
5535
5616
  actions,
5617
+ layout = "flow",
5618
+ appliedFilters,
5536
5619
  ...props
5537
5620
  }, ref) => {
5538
5621
  const [rootElement, setRootElement] = React31.useState(
@@ -5543,6 +5626,10 @@ var FilterToolbar = React31.forwardRef(
5543
5626
  );
5544
5627
  const [overlayOpen, setOverlayOpen] = React31.useState(false);
5545
5628
  const triggerRef = React31.useRef(null);
5629
+ const rowRef = React31.useRef(null);
5630
+ const chipsRef = React31.useRef(null);
5631
+ const chipRemoveRefs = React31.useRef(/* @__PURE__ */ new Map());
5632
+ const pendingFocusRef = React31.useRef(null);
5546
5633
  const setRootRefs = React31.useCallback(
5547
5634
  (element) => {
5548
5635
  setRootElement(element);
@@ -5566,13 +5653,49 @@ var FilterToolbar = React31.forwardRef(
5566
5653
  setOverlayOpen(open);
5567
5654
  if (!open) queueMicrotask(() => triggerRef.current?.focus());
5568
5655
  }, []);
5656
+ const isGrid = layout === "grid";
5657
+ const gridCells = isGrid ? filterCells(filters) : [];
5658
+ const filterCellCount = gridCells.length;
5659
+ React31.useEffect(() => {
5660
+ if (filterCellCount > MAX_GRID_FILTERS) warnGridFilters(filterCellCount);
5661
+ }, [filterCellCount]);
5662
+ React31.useEffect(() => {
5663
+ const ownerDocument = rootElement?.ownerDocument;
5664
+ if (!ownerDocument) return;
5665
+ const dropOutside = (event) => {
5666
+ if (!chipsRef.current?.contains(event.target)) pendingFocusRef.current = null;
5667
+ };
5668
+ ownerDocument.addEventListener("focusin", dropOutside);
5669
+ ownerDocument.addEventListener("pointerdown", dropOutside, true);
5670
+ return () => {
5671
+ ownerDocument.removeEventListener("focusin", dropOutside);
5672
+ ownerDocument.removeEventListener("pointerdown", dropOutside, true);
5673
+ };
5674
+ }, [rootElement]);
5675
+ React31.useEffect(() => {
5676
+ const request = pendingFocusRef.current;
5677
+ if (!request) return;
5678
+ const present = new Set(appliedFilters?.map((filter) => filter.key));
5679
+ if (request.removed.some((key) => present.has(key))) return;
5680
+ pendingFocusRef.current = null;
5681
+ const active = rootElement?.ownerDocument.activeElement;
5682
+ if (active && active !== rootElement.ownerDocument.body) return;
5683
+ const candidates = [
5684
+ ...request.next.map((key) => chipRemoveRefs.current.get(key)),
5685
+ triggerRef.current,
5686
+ ...rowRef.current?.querySelectorAll(focusableSelector) ?? []
5687
+ ];
5688
+ candidates.find(canTakeFocus)?.focus();
5689
+ }, [appliedFilters, rootElement]);
5569
5690
  const hasControls = Boolean(search || filters || collapsible);
5570
5691
  const hasMeta = Boolean(
5571
5692
  resultSummary || clearAction || actions && !hasControls
5572
5693
  );
5694
+ const activeFilterCount = activeFilterCountProp ?? appliedFilters?.length ?? 0;
5573
5695
  const hasAppliedFilters = activeFilterCount > 0;
5696
+ const hasAppliedChips = Boolean(appliedFilters && appliedFilters.length > 0);
5574
5697
  const usesSheet = containerWidth !== null && containerWidth < 640;
5575
- const collapseClasses = collapsibleClassNames[collapseBelow];
5698
+ const collapseClasses = (isGrid ? gridCollapsibleClassNames : collapsibleClassNames)[collapseBelow];
5576
5699
  const collapseTriggerLabel = hasAppliedFilters ? `${collapsedLabel}, ${activeFilterCount}\uAC1C \uC801\uC6A9` : collapsedLabel;
5577
5700
  const clearFiltersButton = onClearFilters ? /* @__PURE__ */ jsx32(
5578
5701
  "button",
@@ -5643,14 +5766,18 @@ var FilterToolbar = React31.forwardRef(
5643
5766
  hasControls ? /* @__PURE__ */ jsxs28(
5644
5767
  "div",
5645
5768
  {
5769
+ ref: rowRef,
5646
5770
  "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)]",
5771
+ className: cn(
5772
+ "flex min-h-[32px] w-full flex-wrap items-center justify-start gap-x-[var(--space-2)] gap-y-[var(--space-2)]",
5773
+ isGrid && "@[640px]:flex-nowrap"
5774
+ ),
5648
5775
  children: [
5649
5776
  search ? /* @__PURE__ */ jsx32(
5650
5777
  "div",
5651
5778
  {
5652
5779
  "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]",
5780
+ 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
5781
  children: search
5655
5782
  }
5656
5783
  ) : null,
@@ -5677,8 +5804,16 @@ var FilterToolbar = React31.forwardRef(
5677
5804
  "div",
5678
5805
  {
5679
5806
  "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
5807
+ 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]",
5808
+ children: isGrid ? gridCells.map(({ key, cell }) => /* @__PURE__ */ jsx32(
5809
+ "div",
5810
+ {
5811
+ "data-slot": "filter-toolbar-cell",
5812
+ className: gridCellClassName,
5813
+ children: cell
5814
+ },
5815
+ key
5816
+ )) : filters
5682
5817
  }
5683
5818
  ) : null,
5684
5819
  collapsible && !overlayOpen ? /* @__PURE__ */ jsx32(
@@ -5713,18 +5848,72 @@ var FilterToolbar = React31.forwardRef(
5713
5848
  children: collapsedContent
5714
5849
  }
5715
5850
  ) : null,
5851
+ hasAppliedChips ? /* @__PURE__ */ jsxs28(
5852
+ "div",
5853
+ {
5854
+ ref: chipsRef,
5855
+ role: "group",
5856
+ "aria-label": collapsedLabel,
5857
+ "data-slot": "filter-toolbar-applied-chips",
5858
+ className: "hidden flex-wrap items-center gap-[var(--space-2)] @[640px]:flex",
5859
+ children: [
5860
+ appliedFilters.map((filter, index) => /* @__PURE__ */ jsx32(
5861
+ Chip,
5862
+ {
5863
+ ref: (button) => {
5864
+ if (button) chipRemoveRefs.current.set(filter.key, button);
5865
+ else chipRemoveRefs.current.delete(filter.key);
5866
+ },
5867
+ variant: "removable",
5868
+ size: "md",
5869
+ removeLabel: `${filter.label} \uC81C\uAC70`,
5870
+ onClick: () => {
5871
+ pendingFocusRef.current = {
5872
+ removed: [filter.key],
5873
+ next: [appliedFilters[index + 1]?.key, appliedFilters[index - 1]?.key].filter(
5874
+ (key) => key !== void 0
5875
+ )
5876
+ };
5877
+ filter.onRemove();
5878
+ },
5879
+ children: filter.label
5880
+ },
5881
+ filter.key
5882
+ )),
5883
+ onClearFilters ? /* @__PURE__ */ jsx32(
5884
+ "button",
5885
+ {
5886
+ type: "button",
5887
+ "data-slot": "filter-toolbar-clear-filters",
5888
+ className: "cursor-pointer border-0 bg-transparent p-0 text-body-sm font-medium text-text-secondary no-underline",
5889
+ onClick: () => {
5890
+ pendingFocusRef.current = {
5891
+ removed: appliedFilters.map((filter) => filter.key),
5892
+ next: []
5893
+ };
5894
+ onClearFilters();
5895
+ },
5896
+ children: "\uD574\uC81C"
5897
+ }
5898
+ ) : null
5899
+ ]
5900
+ }
5901
+ ) : null,
5716
5902
  hasAppliedFilters ? /* @__PURE__ */ jsxs28(
5717
5903
  "div",
5718
5904
  {
5719
5905
  "data-slot": "filter-toolbar-applied",
5720
- className: "flex items-center gap-[var(--space-1)] text-[11px] text-text-tertiary",
5906
+ className: cn(
5907
+ "flex items-center gap-[var(--space-1)] text-[11px] text-text-tertiary",
5908
+ hasAppliedChips && "@[640px]:hidden"
5909
+ ),
5721
5910
  children: [
5722
5911
  /* @__PURE__ */ jsxs28("span", { children: [
5723
5912
  "\uD544\uD130 ",
5724
5913
  activeFilterCount,
5725
5914
  "\uAC1C \uC801\uC6A9"
5726
5915
  ] }),
5727
- onClearFilters ? /* @__PURE__ */ jsxs28(Fragment12, { children: [
5916
+ onClearFilters ? /* @__PURE__ */ jsxs28(Fragment14, { children: [
5728
5917
  /* @__PURE__ */ jsx32("span", { "aria-hidden": "true", children: " \xB7 " }),
5729
5918
  /* @__PURE__ */ jsx32(
5730
5919
  "button",
@@ -6032,16 +6221,19 @@ function DropdownMenuSubContent({
6032
6221
  }
6033
6222
 
6034
6223
  // src/components/ui/action-menu.tsx
6035
- import { Fragment as Fragment14, jsx as jsx34, jsxs as jsxs30 } from "react/jsx-runtime";
6224
+ import { Fragment as Fragment16, jsx as jsx34, jsxs as jsxs30 } from "react/jsx-runtime";
6036
6225
  var sheetRowClassName = cn(
6037
6226
  "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
6227
  "border-t border-[var(--border-subtle)] text-[13px] font-medium leading-normal text-text-primary",
6039
6228
  "transition-colors motion-reduce:transition-none hover:bg-[var(--action-hover)]",
6040
6229
  "focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-[var(--focus-ring)]",
6041
6230
  "enabled:data-[destructive]:text-[var(--semantic-error-fg)]",
6042
- "disabled:cursor-not-allowed disabled:bg-[var(--action-disabled-bg)] disabled:text-text-disabled"
6231
+ "disabled:cursor-not-allowed disabled:bg-[var(--action-disabled-bg)] disabled:text-text-disabled",
6232
+ "aria-disabled:cursor-not-allowed"
6043
6233
  );
6044
6234
  var sheetRowDescriptionClassName = "block text-[12px] font-normal text-text-secondary group-disabled:text-text-disabled";
6235
+ var unavailableClassName = "text-text-disabled";
6236
+ var reasonClassName = "block text-[12px] font-normal leading-normal text-text-tertiary";
6045
6237
  var sheetGroupLabelClassName = "px-[var(--space-4)] pt-[var(--space-3)] pb-[var(--space-2)] text-[12px] font-semibold leading-normal text-text-tertiary";
6046
6238
  var sheetGroupSeparatorClassName = "h-[var(--space-2)] bg-[var(--bg-inset)]";
6047
6239
  function ActionMenuSheet({
@@ -6111,23 +6303,34 @@ function ActionMenuSheet({
6111
6303
  index > 0 ? /* @__PURE__ */ jsx34("div", { role: "separator", className: sheetGroupSeparatorClassName }) : null,
6112
6304
  /* @__PURE__ */ jsxs30("div", { role: "group", "aria-labelledby": group.label ? `${labelId}-${index}` : void 0, children: [
6113
6305
  group.label ? /* @__PURE__ */ jsx34("div", { id: `${labelId}-${index}`, className: sheetGroupLabelClassName, children: group.label }) : null,
6114
- group.items.map((item) => {
6306
+ group.items.map((item, itemIndex) => {
6115
6307
  const Icon = item.icon;
6308
+ const reason = item.disabledReason || void 0;
6309
+ const reasonId = `${labelId}-${index}-${itemIndex}-reason`;
6310
+ const itemLabelId = `${labelId}-${index}-${itemIndex}-label`;
6116
6311
  return /* @__PURE__ */ jsxs30(
6117
6312
  "button",
6118
6313
  {
6119
6314
  type: "button",
6120
6315
  "data-slot": "action-menu-item",
6121
- "data-destructive": item.destructive ? "" : void 0,
6316
+ "data-destructive": item.destructive && !reason ? "" : void 0,
6122
6317
  className: sheetRowClassName,
6123
- disabled: item.disabled,
6124
- onClick: () => handleSelect(item),
6318
+ disabled: item.disabled && !reason,
6319
+ "aria-disabled": reason ? true : void 0,
6320
+ "aria-labelledby": reason ? itemLabelId : void 0,
6321
+ "aria-describedby": reason ? reasonId : void 0,
6322
+ onClick: () => {
6323
+ if (!reason) handleSelect(item);
6324
+ },
6125
6325
  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: [
6326
+ Icon ? /* @__PURE__ */ jsx34(Icon, { "aria-hidden": true, size: 17, strokeWidth: 1.75, className: cn("shrink-0", reason && unavailableClassName) }) : null,
6327
+ /* @__PURE__ */ jsx34("span", { className: "min-w-0 flex-1 break-keep", children: reason ? /* @__PURE__ */ jsxs30(Fragment16, { children: [
6328
+ /* @__PURE__ */ jsx34("span", { id: itemLabelId, className: unavailableClassName, children: item.label }),
6329
+ /* @__PURE__ */ jsx34("span", { id: reasonId, className: reasonClassName, children: reason })
6330
+ ] }) : /* @__PURE__ */ jsxs30(Fragment16, { children: [
6128
6331
  item.label,
6129
6332
  item.description ? /* @__PURE__ */ jsx34("span", { className: sheetRowDescriptionClassName, children: item.description }) : null
6130
- ] })
6333
+ ] }) })
6131
6334
  ]
6132
6335
  },
6133
6336
  item.id
@@ -6140,6 +6343,48 @@ function ActionMenuSheet({
6140
6343
  )
6141
6344
  ] }) }) });
6142
6345
  }
6346
+ function ActionMenuDropdownItem({
6347
+ item,
6348
+ onPick
6349
+ }) {
6350
+ const id = React32.useId();
6351
+ const labelId = `${id}-label`;
6352
+ const reasonId = `${id}-reason`;
6353
+ const Icon = item.icon;
6354
+ const reason = item.disabledReason || void 0;
6355
+ if (!reason) {
6356
+ return /* @__PURE__ */ jsxs30(
6357
+ DropdownMenuItem,
6358
+ {
6359
+ variant: item.destructive ? "destructive" : "default",
6360
+ disabled: item.disabled,
6361
+ onSelect: () => onPick(item),
6362
+ children: [
6363
+ Icon ? /* @__PURE__ */ jsx34(Icon, { "aria-hidden": true, size: 16, strokeWidth: 1.75 }) : null,
6364
+ item.label
6365
+ ]
6366
+ }
6367
+ );
6368
+ }
6369
+ return /* @__PURE__ */ jsxs30(
6370
+ DropdownMenuItem,
6371
+ {
6372
+ "aria-disabled": true,
6373
+ "aria-labelledby": labelId,
6374
+ "aria-describedby": reasonId,
6375
+ textValue: item.label,
6376
+ className: "aria-disabled:cursor-not-allowed",
6377
+ onSelect: (event) => event.preventDefault(),
6378
+ children: [
6379
+ Icon ? /* @__PURE__ */ jsx34(Icon, { "aria-hidden": true, size: 16, strokeWidth: 1.75, className: unavailableClassName }) : null,
6380
+ /* @__PURE__ */ jsxs30("span", { className: "min-w-0 flex-1 break-keep", children: [
6381
+ /* @__PURE__ */ jsx34("span", { id: labelId, className: unavailableClassName, children: item.label }),
6382
+ /* @__PURE__ */ jsx34("span", { id: reasonId, className: cn(reasonClassName, "max-w-64"), children: reason })
6383
+ ] })
6384
+ ]
6385
+ }
6386
+ );
6387
+ }
6143
6388
  var readPresentation = () => window.matchMedia?.(OVERLAY_DESKTOP_QUERY).matches ?? true ? "dropdown" : "sheet";
6144
6389
  var subscribeNothing = () => () => void 0;
6145
6390
  var useHydrated = () => React32.useSyncExternalStore(
@@ -6190,31 +6435,23 @@ function ActionMenu({
6190
6435
  triggerRef.current?.focus();
6191
6436
  };
6192
6437
  const triggerLabel = trigger.props["aria-label"];
6193
- return /* @__PURE__ */ jsxs30(Fragment14, { children: [
6438
+ return /* @__PURE__ */ jsxs30(Fragment16, { children: [
6194
6439
  /* @__PURE__ */ jsxs30(DropdownMenu, { open: open && presentation === "dropdown", onOpenChange: setOpen, children: [
6195
6440
  /* @__PURE__ */ jsx34(DropdownMenuTrigger, { asChild: true, "aria-expanded": open, children: /* @__PURE__ */ jsx34(Slot3.Root, { ref: triggerRef, children: trigger }) }),
6196
6441
  /* @__PURE__ */ jsx34(DropdownMenuContent, { align: "end", onCloseAutoFocus: handleDropdownCloseAutoFocus, children: menuGroups.map((group, index) => /* @__PURE__ */ jsxs30(React32.Fragment, { children: [
6197
6442
  index > 0 ? /* @__PURE__ */ jsx34(DropdownMenuSeparator, {}) : null,
6198
6443
  /* @__PURE__ */ jsxs30(DropdownMenuGroup, { children: [
6199
6444
  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
- })
6445
+ group.items.map((item) => /* @__PURE__ */ jsx34(
6446
+ ActionMenuDropdownItem,
6447
+ {
6448
+ item,
6449
+ onPick: (picked) => {
6450
+ pendingRef.current = picked;
6451
+ }
6452
+ },
6453
+ item.id
6454
+ ))
6218
6455
  ] })
6219
6456
  ] }, group.id)) })
6220
6457
  ] }),
@@ -6610,7 +6847,7 @@ function ContextMenu({
6610
6847
  // src/components/ui/list-card.tsx
6611
6848
  import * as React34 from "react";
6612
6849
  import { ChevronRight as ChevronRight2, MoreVertical } from "lucide-react";
6613
- import { Fragment as Fragment15, jsx as jsx37, jsxs as jsxs33 } from "react/jsx-runtime";
6850
+ import { Fragment as Fragment17, jsx as jsx37, jsxs as jsxs33 } from "react/jsx-runtime";
6614
6851
  function lineTokens(value) {
6615
6852
  return React34.Children.toArray(value).filter((token) => token !== "");
6616
6853
  }
@@ -6697,7 +6934,7 @@ function ListCard({
6697
6934
  children: overlay && href != null ? React34.createElement(linkComponent ?? "a", { ...linkProps, href, className: overlayClassName }, title) : title
6698
6935
  }
6699
6936
  );
6700
- const content = /* @__PURE__ */ jsxs33(Fragment15, { children: [
6937
+ const content = /* @__PURE__ */ jsxs33(Fragment17, { children: [
6701
6938
  leading != null ? /* @__PURE__ */ jsx37("span", { className: "flex min-h-[18px] items-center", children: leading }) : null,
6702
6939
  /* @__PURE__ */ jsxs33("span", { className: "min-w-0", children: [
6703
6940
  overlay && href == null ? (
@@ -6728,7 +6965,7 @@ function ListCard({
6728
6965
  ),
6729
6966
  children: [
6730
6967
  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(
6968
+ actions != null ? /* @__PURE__ */ jsx37("span", { "data-slot": `${slot}-actions`, className: "relative z-[1] flex", children: /* @__PURE__ */ jsx37(
6732
6969
  ActionMenu,
6733
6970
  {
6734
6971
  title: typeof title === "string" ? title : void 0,
@@ -6762,7 +6999,7 @@ function ListCard({
6762
6999
  }
6763
7000
 
6764
7001
  // src/components/ui/data-table.tsx
6765
- import { Fragment as Fragment17, jsx as jsx38, jsxs as jsxs34 } from "react/jsx-runtime";
7002
+ import { Fragment as Fragment19, jsx as jsx38, jsxs as jsxs34 } from "react/jsx-runtime";
6766
7003
  var dataTableVariants = cva13(
6767
7004
  [
6768
7005
  "relative overflow-hidden rounded-[var(--radius-md)] border border-[var(--border-subtle)]",
@@ -6885,25 +7122,24 @@ function RowActionsCell({
6885
7122
  );
6886
7123
  }
6887
7124
  function RowContextMenu({
6888
- openKey,
7125
+ menuKey,
6889
7126
  position,
6890
7127
  groups,
7128
+ cards,
6891
7129
  onClose
6892
7130
  }) {
6893
- const [open, setOpen] = React35.useState(false);
6894
- const pendingRef = React35.useRef(null);
6895
- const outsideRef = React35.useRef(false);
7131
+ const [open, setOpen] = React35.useState(true);
7132
+ const closeRef = React35.useRef(null);
7133
+ const pressedOutside = React35.useRef(false);
7134
+ const close = (reason) => {
7135
+ closeRef.current ??= reason;
7136
+ setOpen(false);
7137
+ };
6896
7138
  const shownGroups = React35.useRef([]);
6897
7139
  if (groups) shownGroups.current = groups;
6898
- const gone = openKey !== null && groups === null;
7140
+ const gone = groups === null;
6899
7141
  const contentRef = React35.useRef(null);
6900
7142
  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
7143
  useIsomorphicLayoutEffect(() => {
6908
7144
  const content = contentRef.current;
6909
7145
  if (!open || !focusInsideRef.current || !content?.isConnected) return;
@@ -6913,13 +7149,20 @@ function RowContextMenu({
6913
7149
  next.focus();
6914
7150
  });
6915
7151
  React35.useEffect(() => {
6916
- if (gone) setOpen(false);
7152
+ if (gone) close({ by: "dismiss", outside: false });
6917
7153
  }, [gone]);
7154
+ useIsomorphicLayoutEffect(() => {
7155
+ if (!cards || !open || closeRef.current) return;
7156
+ const active = document.activeElement;
7157
+ close({ by: "layout", focusInside: Boolean(active && contentRef.current?.contains(active)) });
7158
+ }, [cards, open]);
6918
7159
  return /* @__PURE__ */ jsx38(
6919
7160
  ContextMenu,
6920
7161
  {
6921
7162
  open: open && !gone,
6922
- onOpenChange: setOpen,
7163
+ onOpenChange: (next) => {
7164
+ if (!next) close({ by: "dismiss", outside: false });
7165
+ },
6923
7166
  position,
6924
7167
  contentProps: {
6925
7168
  align: "start",
@@ -6933,37 +7176,31 @@ function RowContextMenu({
6933
7176
  }
6934
7177
  },
6935
7178
  onInteractOutside: () => {
6936
- outsideRef.current = true;
7179
+ pressedOutside.current = true;
7180
+ close({ by: "dismiss", outside: true });
6937
7181
  },
6938
7182
  onCloseAutoFocus: (event) => {
6939
7183
  event.preventDefault();
6940
- const item = pendingRef.current;
6941
- pendingRef.current = null;
6942
- onClose(item, outsideRef.current);
7184
+ let reason = closeRef.current ?? { by: "dismiss", outside: false };
7185
+ if (pressedOutside.current && reason.by === "dismiss") reason = { by: "dismiss", outside: true };
7186
+ if (pressedOutside.current && reason.by === "layout") reason = { by: "layout", focusInside: false };
7187
+ onClose(menuKey, reason);
6943
7188
  }
6944
7189
  },
6945
7190
  children: shownGroups.current.map((group, index) => /* @__PURE__ */ jsxs34(React35.Fragment, { children: [
6946
7191
  index > 0 ? /* @__PURE__ */ jsx38(DropdownMenuSeparator, {}) : null,
6947
7192
  /* @__PURE__ */ jsxs34(DropdownMenuGroup, { children: [
6948
7193
  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
- })
7194
+ group.items.map((item) => /* @__PURE__ */ jsx38(
7195
+ ActionMenuDropdownItem,
7196
+ {
7197
+ item,
7198
+ onPick: (picked) => {
7199
+ closeRef.current ??= { by: "item", item: picked };
7200
+ }
7201
+ },
7202
+ item.id
7203
+ ))
6967
7204
  ] })
6968
7205
  ] }, group.id))
6969
7206
  }
@@ -7044,7 +7281,7 @@ function MobileSortMenu({
7044
7281
  size: "sm",
7045
7282
  className: "ml-auto min-w-0",
7046
7283
  children: [
7047
- active && sortBy ? /* @__PURE__ */ jsxs34(Fragment17, { children: [
7284
+ active && sortBy ? /* @__PURE__ */ jsxs34(Fragment19, { children: [
7048
7285
  /* @__PURE__ */ jsx38("span", { className: "sr-only", children: "\uC815\uB82C " }),
7049
7286
  /* @__PURE__ */ jsx38("span", { className: "truncate", children: active.header }),
7050
7287
  /* @__PURE__ */ jsxs34("span", { className: "sr-only", children: [
@@ -7317,7 +7554,7 @@ function DataTableRowView({
7317
7554
  if (!expand) {
7318
7555
  return dataRow;
7319
7556
  }
7320
- return /* @__PURE__ */ jsxs34(Fragment17, { children: [
7557
+ return /* @__PURE__ */ jsxs34(Fragment19, { children: [
7321
7558
  dataRow,
7322
7559
  /* @__PURE__ */ jsx38(
7323
7560
  "div",
@@ -7929,15 +8166,22 @@ function DataTableInner({
7929
8166
  contextMenuKey.current += 1;
7930
8167
  setContextMenu({ rowId, position: { x: event.clientX, y: event.clientY }, key: contextMenuKey.current });
7931
8168
  };
7932
- const closeRowContextMenu = (item, outside) => {
8169
+ const closeRowContextMenu = (key, close) => {
8170
+ if (key !== contextMenuKey.current) {
8171
+ return;
8172
+ }
7933
8173
  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
8174
+ const refocus = close.by === "item" || (close.by === "dismiss" ? !close.outside : close.focusInside);
8175
+ if (rowId !== void 0 && refocus) {
8176
+ const cards = readLayout(desktopRef.current) === "mobile";
8177
+ const list = cards ? mobileListRef.current : desktopListRef.current;
8178
+ const row = Array.from(
8179
+ list?.querySelectorAll(cards ? '[data-slot="data-table-mobile-card"]' : '[data-slot="data-table-row"]') ?? []
8180
+ ).find((node) => node.getAttribute("data-row-id") === rowId);
8181
+ const trigger = row?.querySelector(
8182
+ cards ? '[data-slot="list-card-actions"] button' : '[data-slot="data-table-row-actions"] button'
7938
8183
  );
7939
- const trigger = row?.querySelector('[data-slot="data-table-row-actions"] button');
7940
- const target = trigger ?? (row?.hasAttribute("tabindex") ? row : null) ?? list ?? desktopRef.current;
8184
+ const target = trigger ?? (row && (cards || row.hasAttribute("tabindex")) ? row : null) ?? list ?? (cards ? mobileRef : desktopRef).current;
7941
8185
  if (target) {
7942
8186
  if (!target.hasAttribute("tabindex") && !target.matches("a[href],button,input")) {
7943
8187
  target.tabIndex = -1;
@@ -7946,7 +8190,7 @@ function DataTableInner({
7946
8190
  }
7947
8191
  }
7948
8192
  setContextMenu(null);
7949
- item?.onSelect();
8193
+ if (close.by === "item") close.item.onSelect();
7950
8194
  };
7951
8195
  const handleRowContextMenu = rowActions ? openRowContextMenu : onRowContextMenu;
7952
8196
  const latestHandlers = React35.useRef({ toggleRow, activateRow, onRowContextMenu, handleRowContextMenu });
@@ -8198,14 +8442,16 @@ function DataTableInner({
8198
8442
  )
8199
8443
  ] }),
8200
8444
  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(
8445
+ hasRowActions && contextMenu ? /* @__PURE__ */ jsx38(
8202
8446
  RowContextMenu,
8203
8447
  {
8204
- openKey: contextMenu?.key ?? null,
8205
- position: contextMenu?.position ?? null,
8448
+ menuKey: contextMenu.key,
8449
+ position: contextMenu.position,
8206
8450
  groups: contextGroups,
8451
+ cards: activeLayout === "mobile",
8207
8452
  onClose: closeRowContextMenu
8208
- }
8453
+ },
8454
+ contextMenu.key
8209
8455
  ) : null
8210
8456
  ]
8211
8457
  }
@@ -8263,6 +8509,35 @@ function ActionButton({ action }) {
8263
8509
  }
8264
8510
  );
8265
8511
  }
8512
+ function CardActionButton({ action }) {
8513
+ const Icon = action.icon;
8514
+ return /* @__PURE__ */ jsxs35(
8515
+ Button,
8516
+ {
8517
+ appearance: "outline",
8518
+ tone: action.variant === "destructive" ? "danger" : "neutral",
8519
+ size: "sm",
8520
+ className: "before:-inset-y-[5px]",
8521
+ "data-slot": "bulk-action-bar-action",
8522
+ "data-variant": action.variant ?? "default",
8523
+ disabled: action.disabled,
8524
+ title: action.title,
8525
+ onClick: action.onClick,
8526
+ children: [
8527
+ Icon ? /* @__PURE__ */ jsx39(
8528
+ Icon,
8529
+ {
8530
+ "aria-hidden": "true",
8531
+ "data-slot": "bulk-action-bar-action-icon",
8532
+ size: 16,
8533
+ strokeWidth: 1.75
8534
+ }
8535
+ ) : null,
8536
+ action.label
8537
+ ]
8538
+ }
8539
+ );
8540
+ }
8266
8541
  function Divider() {
8267
8542
  return /* @__PURE__ */ jsx39(
8268
8543
  "span",
@@ -8305,24 +8580,24 @@ function ClearButton({ onClear }) {
8305
8580
  }
8306
8581
  );
8307
8582
  }
8583
+ var CLEAR_CLEARANCE = 32;
8308
8584
  var INLINE_HEIGHT = 40;
8309
8585
  var HIT_AREA = 44;
8310
8586
  var HIT_INSET = 8;
8587
+ var FLOATING_BOTTOM = "calc(var(--space-4) + env(safe-area-inset-bottom))";
8311
8588
  var floatingStyle = {
8312
8589
  position: "sticky",
8313
- bottom: "calc(var(--space-4) + env(safe-area-inset-bottom))",
8590
+ bottom: FLOATING_BOTTOM,
8314
8591
  zIndex: 1100,
8315
8592
  margin: "0 auto",
8316
8593
  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,
8594
+ boxSizing: "border-box",
8595
+ width: "min(360px, calc(100% - var(--space-8)))",
8596
+ flexDirection: "column",
8597
+ borderRadius: "var(--radius-xl)",
8323
8598
  border: "1px solid var(--border)",
8324
8599
  background: "var(--bg-raised)",
8325
- padding: "var(--space-1) var(--space-2)",
8600
+ padding: "var(--space-2) var(--space-4) var(--space-3)",
8326
8601
  boxShadow: "var(--shadow-8)"
8327
8602
  };
8328
8603
  var inlineStyle = {
@@ -8364,7 +8639,117 @@ function warnInlineOnce() {
8364
8639
  warnedInline = true;
8365
8640
  console.warn('BulkActionBar: placement="inline" is deprecated and removed in the next minor. Use "floating".');
8366
8641
  }
8642
+ var warnedTrailing = false;
8643
+ function warnTrailingOnce() {
8644
+ if (warnedTrailing || typeof process === "undefined" || process.env.NODE_ENV === "production") {
8645
+ return;
8646
+ }
8647
+ warnedTrailing = true;
8648
+ console.warn("BulkActionBar: the floating card ignores `trailing`. Put edit-mode controls at the right end of the toolbar.");
8649
+ }
8650
+ var OFFSET_PROPERTY = "--olun-bulk-bar-offset";
8651
+ var LIFT_ZONE = 204;
8652
+ var cardOffsets = /* @__PURE__ */ new Map();
8653
+ function writeOffset() {
8654
+ const root = document.documentElement;
8655
+ const offset = Math.max(0, ...cardOffsets.values());
8656
+ const value = offset > 0 ? `${offset}px` : "";
8657
+ if (root.style.getPropertyValue(OFFSET_PROPERTY) === value) return;
8658
+ if (value) root.style.setProperty(OFFSET_PROPERTY, value);
8659
+ else root.style.removeProperty(OFFSET_PROPERTY);
8660
+ }
8661
+ function reportOffset(key, card) {
8662
+ let offset = 0;
8663
+ if (card.offsetHeight > 0) {
8664
+ const { top, bottom } = card.getBoundingClientRect();
8665
+ if (window.innerHeight - bottom <= LIFT_ZONE) offset = Math.max(0, Math.round(window.innerHeight - top));
8666
+ }
8667
+ if (offset === (cardOffsets.get(key) ?? 0)) return;
8668
+ if (offset > 0) cardOffsets.set(key, offset);
8669
+ else cardOffsets.delete(key);
8670
+ writeOffset();
8671
+ }
8367
8672
  var useIsomorphicLayoutEffect2 = typeof window === "undefined" ? React36.useEffect : React36.useLayoutEffect;
8673
+ function useBottomOffset(card) {
8674
+ const [key] = React36.useState(() => ({}));
8675
+ React36.useEffect(() => {
8676
+ if (!card) return;
8677
+ let frame = 0;
8678
+ let stopped = false;
8679
+ let listening = false;
8680
+ const schedule = () => {
8681
+ if (frame || stopped) return;
8682
+ frame = requestAnimationFrame(measure);
8683
+ };
8684
+ const listen = (on) => {
8685
+ if (on === listening) return;
8686
+ listening = on;
8687
+ if (on) {
8688
+ window.addEventListener("resize", schedule);
8689
+ window.addEventListener("scroll", schedule, { capture: true, passive: true });
8690
+ } else {
8691
+ window.removeEventListener("resize", schedule);
8692
+ window.removeEventListener("scroll", schedule, { capture: true });
8693
+ }
8694
+ };
8695
+ let watcher = null;
8696
+ let watched = "";
8697
+ const arm = (margin, threshold) => {
8698
+ let first = true;
8699
+ watcher = new IntersectionObserver(
8700
+ ([entry]) => {
8701
+ if (stopped) return;
8702
+ if (first) {
8703
+ first = false;
8704
+ if (entry.intersectionRatio === threshold) return;
8705
+ watcher?.disconnect();
8706
+ arm(margin, entry.intersectionRatio);
8707
+ }
8708
+ schedule();
8709
+ },
8710
+ { root: document, rootMargin: margin, threshold }
8711
+ );
8712
+ watcher.observe(card);
8713
+ };
8714
+ const watch = (on) => {
8715
+ let margin = "";
8716
+ if (on && typeof IntersectionObserver !== "undefined") {
8717
+ const { top, right, bottom, left } = card.getBoundingClientRect();
8718
+ const { clientWidth, clientHeight } = document.documentElement;
8719
+ margin = [top, clientWidth - right, clientHeight - bottom, left].map((inset) => `${-Math.floor(inset)}px`).join(" ");
8720
+ }
8721
+ if (margin === watched) return;
8722
+ watched = margin;
8723
+ watcher?.disconnect();
8724
+ watcher = null;
8725
+ if (margin) arm(margin, 1);
8726
+ };
8727
+ const observer = typeof ResizeObserver === "undefined" ? null : new ResizeObserver(() => schedule());
8728
+ const measure = () => {
8729
+ frame = 0;
8730
+ reportOffset(key, card);
8731
+ const shown = card.offsetHeight > 0;
8732
+ listen(shown || !observer);
8733
+ watch(shown);
8734
+ };
8735
+ measure();
8736
+ observer?.observe(card);
8737
+ card.addEventListener("animationend", schedule);
8738
+ return () => {
8739
+ stopped = true;
8740
+ cancelAnimationFrame(frame);
8741
+ listen(false);
8742
+ watcher?.disconnect();
8743
+ observer?.disconnect();
8744
+ card.removeEventListener("animationend", schedule);
8745
+ cardOffsets.delete(key);
8746
+ writeOffset();
8747
+ };
8748
+ }, [key, card]);
8749
+ useIsomorphicLayoutEffect2(() => {
8750
+ if (card && typeof ResizeObserver === "undefined") reportOffset(key, card);
8751
+ });
8752
+ }
8368
8753
  function useCompactSummary(slot, enabled) {
8369
8754
  const [fullWidth, setFullWidth] = React36.useState(null);
8370
8755
  const measure = React36.useCallback(() => {
@@ -8402,7 +8787,9 @@ function BulkActionBar({
8402
8787
  }) {
8403
8788
  const inline = placement === "inline";
8404
8789
  if (inline) warnInlineOnce();
8405
- const trailingSlot = trailing !== void 0 && trailing !== null ? /* @__PURE__ */ jsx39(
8790
+ const hasTrailing = trailing !== void 0 && trailing !== null;
8791
+ if (!inline && hasTrailing) warnTrailingOnce();
8792
+ const trailingSlot = inline && hasTrailing ? /* @__PURE__ */ jsx39(
8406
8793
  "div",
8407
8794
  {
8408
8795
  "data-slot": "bulk-action-bar-trailing",
@@ -8420,6 +8807,8 @@ function BulkActionBar({
8420
8807
  summaryElement,
8421
8808
  inline && compactSummary !== void 0 && compactSummary !== null
8422
8809
  );
8810
+ const [card, setCard] = React36.useState(null);
8811
+ useBottomOffset(card);
8423
8812
  if (count === 0 && idleContent !== void 0 && trailingSlot) {
8424
8813
  return /* @__PURE__ */ jsxs35(
8425
8814
  "div",
@@ -8474,16 +8863,31 @@ function BulkActionBar({
8474
8863
  "span",
8475
8864
  {
8476
8865
  "data-slot": "bulk-action-bar-count",
8477
- style: {
8478
- paddingLeft: inline ? void 0 : "var(--space-2)",
8866
+ style: inline ? {
8479
8867
  paddingRight: "var(--space-1)",
8480
8868
  fontFamily: "var(--font-sans)",
8481
8869
  fontSize: 13,
8482
- fontWeight: inline ? 600 : 500,
8870
+ fontWeight: 600,
8483
8871
  fontVariantNumeric: "tabular-nums",
8484
8872
  color: "var(--text-primary)",
8485
8873
  whiteSpace: "nowrap"
8486
- },
8874
+ } : (
8875
+ // First row, 32px like the ✕ beside it; the right padding keeps text clear of it,
8876
+ // and a long unbroken unit or hint breaks instead of running under it.
8877
+ {
8878
+ display: "flex",
8879
+ minHeight: 32,
8880
+ alignItems: "center",
8881
+ paddingRight: CLEAR_CLEARANCE,
8882
+ fontFamily: "var(--font-sans)",
8883
+ fontSize: 13,
8884
+ fontWeight: 500,
8885
+ fontVariantNumeric: "tabular-nums",
8886
+ color: "var(--text-primary)",
8887
+ wordBreak: "keep-all",
8888
+ overflowWrap: "anywhere"
8889
+ }
8890
+ ),
8487
8891
  children: count > 0 ? `${count.toLocaleString("ko-KR")}${unit} \uC120\uD0DD\uB428` : emptyHint
8488
8892
  }
8489
8893
  );
@@ -8493,7 +8897,7 @@ function BulkActionBar({
8493
8897
  ref: setSummaryElement,
8494
8898
  "data-slot": "bulk-action-bar-summary",
8495
8899
  style: {
8496
- display: inline ? "flex" : "inline-flex",
8900
+ display: "flex",
8497
8901
  minWidth: 0,
8498
8902
  alignItems: "center",
8499
8903
  columnGap: "var(--space-3)",
@@ -8516,27 +8920,30 @@ function BulkActionBar({
8516
8920
  whiteSpace: "nowrap",
8517
8921
  contain: "inline-size"
8518
8922
  }
8519
- ) : { flexWrap: "wrap", wordBreak: "keep-all" }
8923
+ ) : (
8924
+ // Its own row at the card's full inner width; wraps, never clips.
8925
+ { flexWrap: "wrap", lineHeight: 1.5, wordBreak: "keep-all", overflowWrap: "anywhere" }
8926
+ )
8520
8927
  },
8521
8928
  children: compact ? compactSummary : summary
8522
8929
  }
8523
8930
  ) : 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
8931
  if (inline) {
8932
+ const actionGroup = /* @__PURE__ */ jsx39(
8933
+ "div",
8934
+ {
8935
+ "data-slot": "bulk-action-bar-actions",
8936
+ style: {
8937
+ display: "flex",
8938
+ minWidth: 0,
8939
+ flexWrap: "nowrap",
8940
+ alignItems: "center",
8941
+ justifyContent: "flex-end",
8942
+ gap: 2
8943
+ },
8944
+ children: actions.map((action) => /* @__PURE__ */ jsx39(ActionButton, { action }, action.key))
8945
+ }
8946
+ );
8540
8947
  return /* @__PURE__ */ jsxs35(
8541
8948
  "div",
8542
8949
  {
@@ -8601,6 +9008,7 @@ function BulkActionBar({
8601
9008
  "animate-[olun-bulk-action-bar-in_var(--dur-fast)_var(--ease-out)_both] motion-reduce:animate-none",
8602
9009
  className
8603
9010
  ),
9011
+ ref: setCard,
8604
9012
  style: { ...floatingStyle, ...style },
8605
9013
  ...props,
8606
9014
  children: [
@@ -8612,8 +9020,8 @@ function BulkActionBar({
8612
9020
  style: {
8613
9021
  display: "flex",
8614
9022
  minWidth: 0,
8615
- alignItems: "center",
8616
- gap: "var(--space-2)"
9023
+ flexDirection: "column",
9024
+ rowGap: "var(--space-2)"
8617
9025
  },
8618
9026
  children: [
8619
9027
  countLabel,
@@ -8621,11 +9029,29 @@ function BulkActionBar({
8621
9029
  ]
8622
9030
  }
8623
9031
  ),
8624
- count > 0 && actions.length > 0 ? /* @__PURE__ */ jsx39(Divider, {}) : null,
8625
- actionGroup,
8626
- /* @__PURE__ */ jsx39(Divider, {}),
8627
- /* @__PURE__ */ jsx39(ClearButton, { onClear }),
8628
- trailingSlot
9032
+ actions.length > 0 ? /* @__PURE__ */ jsx39(
9033
+ "div",
9034
+ {
9035
+ "data-slot": "bulk-action-bar-actions",
9036
+ style: {
9037
+ display: "flex",
9038
+ flexWrap: "wrap",
9039
+ justifyContent: "flex-end",
9040
+ gap: "var(--space-2)",
9041
+ marginTop: "var(--space-3)"
9042
+ },
9043
+ children: actions.map((action) => /* @__PURE__ */ jsx39(CardActionButton, { action }, action.key))
9044
+ }
9045
+ ) : null,
9046
+ /* @__PURE__ */ jsx39(
9047
+ IconButton,
9048
+ {
9049
+ icon: X4,
9050
+ "aria-label": "\uC120\uD0DD \uD574\uC81C",
9051
+ onClick: onClear,
9052
+ style: { position: "absolute", top: "var(--space-2)", right: "var(--space-2)" }
9053
+ }
9054
+ )
8629
9055
  ]
8630
9056
  }
8631
9057
  );
@@ -9957,7 +10383,7 @@ function TablePagination({
9957
10383
 
9958
10384
  // src/components/ui/mode-toggle.tsx
9959
10385
  import { cva as cva19 } from "class-variance-authority";
9960
- import { jsx as jsx56, jsxs as jsxs45 } from "react/jsx-runtime";
10386
+ import { Fragment as Fragment20, jsx as jsx56, jsxs as jsxs45 } from "react/jsx-runtime";
9961
10387
  var modeToggleVariants = cva19(
9962
10388
  [
9963
10389
  "inline-flex items-center rounded-[var(--radius-md)] border border-[var(--border-subtle)]",
@@ -9980,6 +10406,17 @@ var modeToggleItemVariants = cva19(
9980
10406
  "[&_svg]:pointer-events-none [&_svg]:size-[14px] [&_svg]:shrink-0 [&_svg]:stroke-[1.75]"
9981
10407
  ].join(" ")
9982
10408
  );
10409
+ function formatModeToggleCount(value) {
10410
+ return value > 99 ? "99+" : String(value);
10411
+ }
10412
+ function isShownModeToggleCount(value) {
10413
+ if (value === void 0) return false;
10414
+ if (Number.isInteger(value) && value >= 0) return true;
10415
+ if (typeof process !== "undefined" && process.env.NODE_ENV !== "production") {
10416
+ console.warn(`ModeToggle: count must be an integer 0 or greater (got ${value}). The count is not shown.`);
10417
+ }
10418
+ return false;
10419
+ }
9983
10420
  function ModeToggle({
9984
10421
  className,
9985
10422
  itemClassName,
@@ -10020,7 +10457,11 @@ function ModeToggle({
10020
10457
  },
10021
10458
  children: [
10022
10459
  Icon ? /* @__PURE__ */ jsx56(Icon, { "aria-hidden": "true", "data-slot": "mode-toggle-icon" }) : null,
10023
- /* @__PURE__ */ jsx56("span", { children: option.label })
10460
+ /* @__PURE__ */ jsx56("span", { children: option.label }),
10461
+ isShownModeToggleCount(option.count) ? /* @__PURE__ */ jsxs45(Fragment20, { children: [
10462
+ /* @__PURE__ */ jsx56("span", { "aria-hidden": "true", "data-slot": "mode-toggle-count", className: tabsBadgeVariants(), children: formatModeToggleCount(option.count) }),
10463
+ /* @__PURE__ */ jsx56("span", { className: "sr-only", children: ` ${option.count}` })
10464
+ ] }) : null
10024
10465
  ]
10025
10466
  },
10026
10467
  option.value
@@ -10236,7 +10677,8 @@ var toastVariants = cva21(
10236
10677
  );
10237
10678
  var toastViewportVariants = cva21(
10238
10679
  [
10239
- "fixed bottom-[var(--space-6)] right-[var(--space-6)] z-[var(--z-toast,50)]",
10680
+ // A floating BulkActionBar publishes --olun-bulk-bar-offset while it is up; sit 8px above it.
10681
+ "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
10682
  "flex w-[min(420px,calc(100vw-var(--space-12)))] list-none flex-col gap-[var(--space-2)]",
10241
10683
  "pointer-events-none outline-none"
10242
10684
  ].join(" ")
@@ -10468,7 +10910,7 @@ function ToastProvider({ children, maxToasts = 3, className, ...props }) {
10468
10910
  import * as React48 from "react";
10469
10911
  import { cva as cva22 } from "class-variance-authority";
10470
10912
  import { AlertCircle as AlertCircle4 } from "lucide-react";
10471
- import { Fragment as Fragment18, jsx as jsx59, jsxs as jsxs48 } from "react/jsx-runtime";
10913
+ import { Fragment as Fragment21, jsx as jsx59, jsxs as jsxs48 } from "react/jsx-runtime";
10472
10914
  var otpFieldVariants = cva22("flex w-full min-w-0 flex-col gap-[var(--space-3)]", {
10473
10915
  variants: {
10474
10916
  size: {
@@ -10798,7 +11240,7 @@ var OTPField = React48.forwardRef(
10798
11240
  "data-slot": "otp-field-message",
10799
11241
  "data-kind": errorText ? "error" : "helper",
10800
11242
  className: otpFieldMessageVariants({ state: resolvedState }),
10801
- children: errorText ? /* @__PURE__ */ jsxs48(Fragment18, { children: [
11243
+ children: errorText ? /* @__PURE__ */ jsxs48(Fragment21, { children: [
10802
11244
  /* @__PURE__ */ jsx59(
10803
11245
  AlertCircle4,
10804
11246
  {
@@ -11686,7 +12128,7 @@ function TreeView({
11686
12128
 
11687
12129
  // src/components/branding/olun-logo.tsx
11688
12130
  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";
12131
+ import { Fragment as Fragment22, jsx as jsx66, jsxs as jsxs53 } from "react/jsx-runtime";
11690
12132
  var ACCENT_FILL = "var(--accent-pop)";
11691
12133
  var INK = { color: "var(--text-primary)" };
11692
12134
  var INK_TIGHT = { ...INK, display: "block", overflow: "visible" };
@@ -11696,7 +12138,7 @@ function symbolGeometry(size) {
11696
12138
  function SymbolCircles({ size }) {
11697
12139
  const { ring } = OLUN_SYMBOL;
11698
12140
  const g = symbolGeometry(size);
11699
- return /* @__PURE__ */ jsxs53(Fragment19, { children: [
12141
+ return /* @__PURE__ */ jsxs53(Fragment22, { children: [
11700
12142
  /* @__PURE__ */ jsx66(
11701
12143
  "circle",
11702
12144
  {