@mediaradar/design-components 0.20.0 → 0.21.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.cjs CHANGED
@@ -37,11 +37,13 @@ __export(index_exports, {
37
37
  DateRangeFilter: () => DateRangeFilter,
38
38
  DateRangePicker: () => DateRangePicker,
39
39
  Delta: () => Delta,
40
+ EmptyState: () => EmptyState,
40
41
  FilterChipBar: () => FilterChipBar,
41
42
  FilterDropdownFooter: () => FilterDropdownFooter,
42
43
  Freshness: () => Freshness,
43
44
  InfoTip: () => InfoTip,
44
45
  LineChartPlaceholder: () => LineChartPlaceholder,
46
+ Modal: () => Modal,
45
47
  MultiSelectFilter: () => MultiSelectFilter,
46
48
  NO_RESULTS_BODY: () => NO_RESULTS_BODY,
47
49
  NO_RESULTS_TITLE: () => NO_RESULTS_TITLE,
@@ -56,6 +58,7 @@ __export(index_exports, {
56
58
  SearchBar: () => SearchBar,
57
59
  SectionHeading: () => SectionHeading,
58
60
  SectionRail: () => SectionRail,
61
+ SegmentedControl: () => SegmentedControl,
59
62
  SelectAllVisible: () => SelectAllVisible,
60
63
  SelectFilter: () => SelectFilter,
61
64
  SelectedSection: () => SelectedSection,
@@ -63,7 +66,9 @@ __export(index_exports, {
63
66
  SortButton: () => SortButton,
64
67
  StackedColumnPlaceholder: () => StackedColumnPlaceholder,
65
68
  TablePagination: () => TablePagination,
69
+ Tabs: () => Tabs,
66
70
  Tag: () => Tag,
71
+ TextField: () => TextField,
67
72
  Toast: () => Toast,
68
73
  Toggle: () => Toggle,
69
74
  Tooltip: () => Tooltip,
@@ -312,7 +317,7 @@ function AdvertiserQuickFilter({
312
317
  "aria-label": caption,
313
318
  "data-testid": testIdPrefix && `${testIdPrefix}-popover`,
314
319
  children: [
315
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "border-b border-border-default p-2", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "flex h-9 items-center gap-2 rounded-control border border-border-strong bg-input px-2.5 focus-within:border-border-focus focus-within:ring-2 focus-within:ring-border-focus/30", children: [
320
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "border-b border-border-default p-2", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "flex h-8 items-center gap-2 rounded-control border border-border-strong bg-input px-2.5 focus-within:outline-2 focus-within:outline-offset-1 focus-within:outline-border-focus", children: [
316
321
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_lucide_react3.Search, { size: 14, className: "shrink-0 text-text-secondary", "aria-hidden": "true" }),
317
322
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
318
323
  "input",
@@ -991,6 +996,7 @@ function NavBtn({ children, onClick, ...rest }) {
991
996
  var import_react4 = require("react");
992
997
  var import_lucide_react7 = require("lucide-react");
993
998
  var import_jsx_runtime8 = require("react/jsx-runtime");
999
+ var HEIGHT = { sm: "h-8", md: "h-10", lg: "h-11" };
994
1000
  var DEFAULT_SEARCH_MATCH = { mode: "anyWord", wordJoin: "Or" };
995
1001
  var MODE_LABEL = {
996
1002
  anyWord: "Any Word",
@@ -1006,6 +1012,7 @@ function SearchBar({
1006
1012
  value,
1007
1013
  onChange,
1008
1014
  variant = "default",
1015
+ size = "lg",
1009
1016
  matchValue,
1010
1017
  onMatchChange,
1011
1018
  className,
@@ -1013,6 +1020,7 @@ function SearchBar({
1013
1020
  }) {
1014
1021
  const [open, setOpen] = (0, import_react4.useState)(false);
1015
1022
  const ref = (0, import_react4.useRef)(null);
1023
+ const input = (0, import_react4.useRef)(null);
1016
1024
  (0, import_react4.useEffect)(() => {
1017
1025
  if (!open) return;
1018
1026
  const onKey = (e) => e.key === "Escape" && setOpen(false);
@@ -1031,7 +1039,8 @@ function SearchBar({
1031
1039
  "div",
1032
1040
  {
1033
1041
  className: cx2(
1034
- "flex h-11 min-w-[200px] flex-1 items-stretch rounded-control border border-border-strong bg-input",
1042
+ "flex min-w-[200px] flex-1 items-stretch rounded-control border border-border-strong bg-input focus-within:outline-2 focus-within:outline-offset-1 focus-within:outline-border-focus",
1043
+ HEIGHT[size],
1035
1044
  className
1036
1045
  ),
1037
1046
  children: [
@@ -1040,16 +1049,31 @@ function SearchBar({
1040
1049
  /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1041
1050
  "input",
1042
1051
  {
1052
+ ref: input,
1043
1053
  type: "text",
1044
1054
  value,
1045
1055
  onChange: (e) => onChange?.(e.target.value),
1046
1056
  placeholder,
1047
1057
  "aria-label": placeholder,
1048
1058
  "data-testid": testIdPrefix && `${testIdPrefix}-search`,
1049
- className: "min-w-0 flex-1 bg-transparent text-[13px] text-text-primary outline-hidden placeholder:text-text-tertiary"
1059
+ className: "min-w-0 flex-1 bg-transparent text-dense text-text-primary outline-hidden placeholder:text-text-tertiary"
1050
1060
  }
1051
1061
  )
1052
1062
  ] }),
1063
+ value && onChange && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1064
+ "button",
1065
+ {
1066
+ type: "button",
1067
+ onClick: () => {
1068
+ onChange("");
1069
+ input.current?.focus();
1070
+ },
1071
+ "aria-label": "Clear search",
1072
+ "data-testid": testIdPrefix && `${testIdPrefix}-clear`,
1073
+ className: "mr-1.5 flex size-6 shrink-0 items-center justify-center self-center rounded-control text-text-tertiary hover:bg-subtle hover:text-text-primary focus-visible:outline-2 focus-visible:outline-border-focus",
1074
+ children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_lucide_react7.X, { size: 14, "aria-hidden": "true" })
1075
+ }
1076
+ ),
1053
1077
  showMatch && // border-border-default, not the bare `border-border` both source
1054
1078
  // implementations used -- confirmed by actually compiling against
1055
1079
  // @mediaradar/design-tokens' real published theme.css (not assumed):
@@ -2401,7 +2425,7 @@ function SelectionPicker({
2401
2425
  }) }),
2402
2426
  source === "search" && /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { className: "flex min-w-0 flex-col gap-3", children: [
2403
2427
  /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { className: "flex flex-wrap items-center gap-2", children: [
2404
- /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { className: "flex h-9 min-w-[200px] flex-1 items-center gap-2 rounded-control border border-border-strong bg-input px-3", children: [
2428
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { className: "flex h-8 min-w-[200px] flex-1 items-center gap-2 rounded-control border border-border-strong bg-input px-3 focus-within:outline-2 focus-within:outline-offset-1 focus-within:outline-border-focus", children: [
2405
2429
  /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_lucide_react15.Search, { size: 15, className: "text-text-secondary", "aria-hidden": "true" }),
2406
2430
  /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
2407
2431
  "input",
@@ -2715,7 +2739,7 @@ function FilterDropdown({
2715
2739
  onClick: () => setOpen((v) => !v),
2716
2740
  "aria-expanded": open,
2717
2741
  "data-testid": testIdPrefix && `${testIdPrefix}-filter-${filterSlug}`,
2718
- className: "inline-flex h-9 items-center gap-1.5 whitespace-nowrap rounded-control border border-border-strong bg-input px-3 text-[13px] font-medium text-text-secondary transition-colors hover:bg-subtle",
2742
+ className: "inline-flex h-8 items-center gap-1.5 whitespace-nowrap rounded-control border border-border-strong bg-input px-3 text-[13px] font-medium text-text-secondary transition-colors hover:bg-subtle",
2719
2743
  children: [
2720
2744
  label,
2721
2745
  /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_lucide_react15.ChevronDown, { size: 13, "aria-hidden": "true", className: cx8("transition-transform", open && "rotate-180") })
@@ -2921,7 +2945,10 @@ var buttonVariants = (0, import_class_variance_authority.cva)(
2921
2945
  size: {
2922
2946
  xs: "h-6 px-2 text-xs",
2923
2947
  sm: "h-8 px-3 text-sm",
2924
- md: "h-10 px-4 text-sm"
2948
+ md: "h-10 px-4 text-sm",
2949
+ // 44, the filter row's height, so a button at the end of a row of
2950
+ // filter dropdowns lines up with them. One control scale: 32 / 40 / 44.
2951
+ lg: "h-11 px-5 text-sm"
2925
2952
  },
2926
2953
  // Shape is orthogonal to size. As a fourth SIZE, `icon` could only ever
2927
2954
  // be one dimension, so an icon button never lined up with the labelled
@@ -2936,6 +2963,7 @@ var buttonVariants = (0, import_class_variance_authority.cva)(
2936
2963
  { size: "xs", iconOnly: true, class: "w-6" },
2937
2964
  { size: "sm", iconOnly: true, class: "w-8" },
2938
2965
  { size: "md", iconOnly: true, class: "w-10" },
2966
+ { size: "lg", iconOnly: true, class: "w-11" },
2939
2967
  // A boxed button is a target you aim at, so its label carries the weight
2940
2968
  // of one. `link` sits inline in a sentence, where semibold reads as
2941
2969
  // emphasis on the words rather than as a control.
@@ -3049,7 +3077,7 @@ function NameListModal({
3049
3077
  /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
3050
3078
  "div",
3051
3079
  {
3052
- className: "fixed inset-0 z-70 flex items-center justify-center bg-scrim px-4",
3080
+ className: "fixed inset-0 z-modal flex items-center justify-center bg-scrim px-4",
3053
3081
  onMouseDown: (e) => e.target === e.currentTarget && onCancel(),
3054
3082
  children: /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(
3055
3083
  "div",
@@ -3059,7 +3087,7 @@ function NameListModal({
3059
3087
  "aria-modal": "true",
3060
3088
  "aria-labelledby": titleId,
3061
3089
  onKeyDown: trapTab,
3062
- className: "w-full max-w-[420px] rounded-card border border-border-default bg-surface p-5 shadow-overlay",
3090
+ className: "w-full max-w-110 rounded-card border border-border-default bg-surface p-5 shadow-overlay",
3063
3091
  children: [
3064
3092
  /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("h2", { id: titleId, className: "text-[16px] font-semibold text-text-primary", children: title }),
3065
3093
  /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("div", { className: "mt-4 flex flex-col gap-1.5", children: [
@@ -3072,7 +3100,7 @@ function NameListModal({
3072
3100
  value: name,
3073
3101
  onChange: (e) => setName(e.target.value),
3074
3102
  placeholder,
3075
- className: "h-10 rounded-control border border-border-strong bg-input px-3 text-sm outline-hidden focus:border-brand-primaryAction"
3103
+ className: "h-10 rounded-control border border-border-strong bg-input px-3 text-dense focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-border-focus"
3076
3104
  }
3077
3105
  )
3078
3106
  ] }),
@@ -3617,7 +3645,7 @@ function MiniSelect({ value, onChange, options }) {
3617
3645
  const opt = options.find((o) => String(o.value) === e.target.value);
3618
3646
  if (opt) onChange(opt.value);
3619
3647
  },
3620
- className: "rounded-control border border-border-strong bg-input px-1.5 py-1 text-[12px] text-text-primary outline-hidden focus:border-border-focus",
3648
+ className: "rounded-control border border-border-strong bg-input px-1.5 py-1 text-[12px] text-text-primary focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-border-focus",
3621
3649
  children: options.map((o) => /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("option", { value: String(o.value), children: o.label }, String(o.value)))
3622
3650
  }
3623
3651
  );
@@ -3870,7 +3898,7 @@ function MultiSelectFilter({
3870
3898
  role: "dialog",
3871
3899
  "aria-label": caption,
3872
3900
  children: [
3873
- searchable && /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("div", { className: "border-b border-border-default p-2", children: /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("div", { className: "flex h-9 items-center gap-2 rounded-control border border-border-strong bg-input px-2.5", children: [
3901
+ searchable && /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("div", { className: "border-b border-border-default p-2", children: /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("div", { className: "flex h-8 items-center gap-2 rounded-control border border-border-strong bg-input px-2.5 focus-within:outline-2 focus-within:outline-offset-1 focus-within:outline-border-focus", children: [
3874
3902
  /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(import_lucide_react23.Search, { size: 14, className: "text-text-secondary", "aria-hidden": "true" }),
3875
3903
  /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
3876
3904
  "input",
@@ -4080,16 +4108,294 @@ function SectionHeading({ title, description, actions, as: Tag2 = "h2", id, clas
4080
4108
  }
4081
4109
 
4082
4110
  // src/PageHeader.tsx
4111
+ var import_lucide_react25 = require("lucide-react");
4083
4112
  var import_jsx_runtime43 = require("react/jsx-runtime");
4084
- function PageHeader({ title, description, actions, className }) {
4113
+ function PageHeader({ title, description, actions, back, className }) {
4085
4114
  return /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("header", { className: cn("flex flex-wrap items-center justify-between gap-x-6 gap-y-3", className), children: [
4086
4115
  /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("div", { className: "min-w-0", children: [
4116
+ back && /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)(
4117
+ "a",
4118
+ {
4119
+ href: back.href ?? "#",
4120
+ onClick: back.onClick,
4121
+ className: "mb-1.5 inline-flex items-center gap-1.5 rounded-control text-micro font-semibold uppercase tracking-wider text-text-tertiary hover:text-text-primary focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-border-focus",
4122
+ children: [
4123
+ /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(import_lucide_react25.ArrowLeft, { size: 13, "aria-hidden": "true" }),
4124
+ back.label
4125
+ ]
4126
+ }
4127
+ ),
4087
4128
  /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("h1", { className: "text-xl font-semibold tracking-tight text-text-primary", children: title }),
4088
4129
  description && /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("p", { className: "mt-0.5 max-w-2xl text-sm text-text-secondary", children: description })
4089
4130
  ] }),
4090
4131
  actions && /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("div", { className: "flex shrink-0 items-center gap-2", children: actions })
4091
4132
  ] });
4092
4133
  }
4134
+
4135
+ // src/Modal.tsx
4136
+ var import_react19 = require("react");
4137
+ var import_react_dom7 = require("react-dom");
4138
+ var import_lucide_react26 = require("lucide-react");
4139
+ var import_jsx_runtime44 = require("react/jsx-runtime");
4140
+ var WIDTH = {
4141
+ sm: "max-w-110",
4142
+ md: "max-w-160",
4143
+ lg: "max-w-220"
4144
+ };
4145
+ var FOCUSABLE = 'a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])';
4146
+ function Modal({ open, onClose, title, description, size = "md", footer, children, className }) {
4147
+ const titleId = (0, import_react19.useId)();
4148
+ const descId = (0, import_react19.useId)();
4149
+ const panel = (0, import_react19.useRef)(null);
4150
+ const close = (0, import_react19.useRef)(onClose);
4151
+ close.current = onClose;
4152
+ (0, import_react19.useEffect)(() => {
4153
+ if (!open) return;
4154
+ const opener = document.activeElement;
4155
+ const first = panel.current?.querySelector(`[data-modal-body] :is(${FOCUSABLE})`) ?? panel.current?.querySelector(`[data-modal-footer] :is(${FOCUSABLE})`);
4156
+ (first ?? panel.current)?.focus();
4157
+ const overflow = document.body.style.overflow;
4158
+ document.body.style.overflow = "hidden";
4159
+ const onKey = (e) => {
4160
+ if (e.key === "Escape") {
4161
+ e.stopPropagation();
4162
+ close.current();
4163
+ return;
4164
+ }
4165
+ if (e.key !== "Tab" || !panel.current) return;
4166
+ const items = Array.from(panel.current.querySelectorAll(FOCUSABLE));
4167
+ if (items.length === 0) return;
4168
+ const [head, tail] = [items[0], items[items.length - 1]];
4169
+ if (e.shiftKey && document.activeElement === head) {
4170
+ e.preventDefault();
4171
+ tail.focus();
4172
+ } else if (!e.shiftKey && document.activeElement === tail) {
4173
+ e.preventDefault();
4174
+ head.focus();
4175
+ }
4176
+ };
4177
+ document.addEventListener("keydown", onKey);
4178
+ return () => {
4179
+ document.removeEventListener("keydown", onKey);
4180
+ document.body.style.overflow = overflow;
4181
+ opener?.focus?.();
4182
+ };
4183
+ }, [open]);
4184
+ if (!open || typeof document === "undefined") return null;
4185
+ return (0, import_react_dom7.createPortal)(
4186
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
4187
+ "div",
4188
+ {
4189
+ className: "fixed inset-0 z-modal flex items-center justify-center bg-scrim px-4 py-6",
4190
+ onMouseDown: (e) => e.target === e.currentTarget && onClose(),
4191
+ children: /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)(
4192
+ "div",
4193
+ {
4194
+ ref: panel,
4195
+ role: "dialog",
4196
+ "aria-modal": "true",
4197
+ "aria-labelledby": titleId,
4198
+ "aria-describedby": description ? descId : void 0,
4199
+ tabIndex: -1,
4200
+ className: cn(
4201
+ "flex max-h-full w-full flex-col rounded-card border border-border-default bg-surface shadow-overlay outline-hidden",
4202
+ WIDTH[size],
4203
+ className
4204
+ ),
4205
+ children: [
4206
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("div", { className: "flex items-start gap-3 px-5 pt-5 pb-3", children: [
4207
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("div", { className: "min-w-0 flex-1", children: [
4208
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("h2", { id: titleId, className: "text-base font-semibold text-text-primary", children: title }),
4209
+ description && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("p", { id: descId, className: "mt-0.5 text-sm text-text-secondary", children: description })
4210
+ ] }),
4211
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
4212
+ "button",
4213
+ {
4214
+ type: "button",
4215
+ onClick: onClose,
4216
+ "aria-label": "Close",
4217
+ className: "-mr-1.5 -mt-1 flex size-8 shrink-0 items-center justify-center rounded-control text-text-tertiary hover:bg-subtle hover:text-text-primary focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-border-focus",
4218
+ children: /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(import_lucide_react26.X, { size: 16, "aria-hidden": "true" })
4219
+ }
4220
+ )
4221
+ ] }),
4222
+ children && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("div", { "data-modal-body": true, className: "min-h-0 flex-1 overflow-y-auto px-5 pb-5 text-sm text-text-primary", children }),
4223
+ footer && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("div", { "data-modal-footer": true, className: "flex items-center justify-end gap-2 border-t border-border-default px-5 py-3", children: footer })
4224
+ ]
4225
+ }
4226
+ )
4227
+ }
4228
+ ),
4229
+ document.body
4230
+ );
4231
+ }
4232
+
4233
+ // src/EmptyState.tsx
4234
+ var import_jsx_runtime45 = require("react/jsx-runtime");
4235
+ function EmptyState({ icon, title, description, action, size = "sm", className }) {
4236
+ const md = size === "md";
4237
+ return /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)(
4238
+ "div",
4239
+ {
4240
+ className: cn(
4241
+ "flex flex-col items-center text-center",
4242
+ md ? "gap-2 px-6 py-16" : "gap-1.5 px-4 py-8",
4243
+ className
4244
+ ),
4245
+ children: [
4246
+ icon && /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
4247
+ "span",
4248
+ {
4249
+ "aria-hidden": "true",
4250
+ className: cn(
4251
+ "flex items-center justify-center text-text-tertiary",
4252
+ md && "mb-1 size-11 rounded-full bg-subtle"
4253
+ ),
4254
+ children: icon
4255
+ }
4256
+ ),
4257
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("p", { className: cn("font-semibold text-text-primary", md ? "text-base" : "text-sm"), children: title }),
4258
+ description && /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("p", { className: cn("max-w-sm text-text-secondary", md ? "text-sm" : "text-xs"), children: description }),
4259
+ action && /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("div", { className: md ? "mt-3" : "mt-2", children: action })
4260
+ ]
4261
+ }
4262
+ );
4263
+ }
4264
+
4265
+ // src/Tabs.tsx
4266
+ var import_react20 = require("react");
4267
+ var import_jsx_runtime46 = require("react/jsx-runtime");
4268
+ function arrowKeys(e, row) {
4269
+ if (!row || !["ArrowLeft", "ArrowRight", "Home", "End"].includes(e.key)) return;
4270
+ const items = Array.from(row.querySelectorAll("button:not([disabled])"));
4271
+ const at = items.indexOf(document.activeElement);
4272
+ const next = e.key === "Home" ? 0 : e.key === "End" ? items.length - 1 : (at + (e.key === "ArrowRight" ? 1 : -1) + items.length) % items.length;
4273
+ e.preventDefault();
4274
+ items[next]?.focus();
4275
+ items[next]?.click();
4276
+ }
4277
+ function Tabs({ items, value, onChange, idPrefix = "tabs", className, ...rest }) {
4278
+ const row = (0, import_react20.useRef)(null);
4279
+ return /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
4280
+ "div",
4281
+ {
4282
+ ref: row,
4283
+ role: "tablist",
4284
+ "aria-label": rest["aria-label"],
4285
+ onKeyDown: (e) => arrowKeys(e, row.current),
4286
+ className: cn("flex gap-6 border-b border-border-default", className),
4287
+ children: items.map((item) => {
4288
+ const on = item.value === value;
4289
+ return /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)(
4290
+ "button",
4291
+ {
4292
+ type: "button",
4293
+ role: "tab",
4294
+ id: `${idPrefix}-tab-${item.value}`,
4295
+ "aria-controls": `${idPrefix}-panel-${item.value}`,
4296
+ "aria-selected": on,
4297
+ tabIndex: on ? 0 : -1,
4298
+ disabled: item.disabled,
4299
+ onClick: () => onChange(item.value),
4300
+ className: cn(
4301
+ "-mb-px flex items-center gap-1.5 rounded-control border-b-2 pt-1 pb-2.5 text-sm focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-border-focus disabled:opacity-50",
4302
+ on ? "border-brand-primaryAction font-semibold text-text-primary" : "border-transparent text-text-secondary hover:text-text-primary"
4303
+ ),
4304
+ children: [
4305
+ item.label,
4306
+ item.count !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("span", { className: "text-xs text-text-tertiary", children: item.count })
4307
+ ]
4308
+ },
4309
+ item.value
4310
+ );
4311
+ })
4312
+ }
4313
+ );
4314
+ }
4315
+ function SegmentedControl({
4316
+ options,
4317
+ value,
4318
+ onChange,
4319
+ size = "sm",
4320
+ className,
4321
+ ...rest
4322
+ }) {
4323
+ const row = (0, import_react20.useRef)(null);
4324
+ return /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
4325
+ "div",
4326
+ {
4327
+ ref: row,
4328
+ role: "radiogroup",
4329
+ "aria-label": rest["aria-label"],
4330
+ onKeyDown: (e) => arrowKeys(e, row.current),
4331
+ className: cn("inline-flex w-fit items-stretch gap-0.5 rounded-full bg-subtle p-1", size === "md" ? "h-10" : "h-8", className),
4332
+ children: options.map((o) => {
4333
+ const on = o.value === value;
4334
+ return /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
4335
+ "button",
4336
+ {
4337
+ type: "button",
4338
+ role: "radio",
4339
+ "aria-checked": on,
4340
+ tabIndex: on ? 0 : -1,
4341
+ onClick: () => onChange(o.value),
4342
+ className: cn(
4343
+ "rounded-full px-3 focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-border-focus",
4344
+ size === "md" ? "text-sm" : "text-xs",
4345
+ on ? "bg-surface font-semibold text-text-primary shadow-raised" : "text-text-secondary hover:text-text-primary"
4346
+ ),
4347
+ children: o.label
4348
+ },
4349
+ o.value
4350
+ );
4351
+ })
4352
+ }
4353
+ );
4354
+ }
4355
+
4356
+ // src/TextField.tsx
4357
+ var import_react21 = require("react");
4358
+ var import_jsx_runtime47 = require("react/jsx-runtime");
4359
+ var HEIGHT2 = { sm: "h-8", md: "h-10", lg: "h-11" };
4360
+ var TextField = (0, import_react21.forwardRef)(
4361
+ ({ label, hint, error, size = "md", icon, className, id, disabled, ...props }, ref) => {
4362
+ const auto = (0, import_react21.useId)();
4363
+ const fieldId = id ?? auto;
4364
+ const noteId = `${fieldId}-note`;
4365
+ const note = error ?? hint;
4366
+ return /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)("div", { className: cn("flex flex-col gap-1.5", className), children: [
4367
+ label && /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("label", { htmlFor: fieldId, className: "text-xs font-medium text-text-secondary", children: label }),
4368
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)(
4369
+ "div",
4370
+ {
4371
+ className: cn(
4372
+ "flex items-center gap-2 rounded-control border bg-input px-3 focus-within:outline-2 focus-within:outline-offset-1 focus-within:outline-border-focus",
4373
+ HEIGHT2[size],
4374
+ error ? "border-status-danger" : "border-border-strong",
4375
+ disabled && "opacity-50"
4376
+ ),
4377
+ children: [
4378
+ icon && /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("span", { "aria-hidden": "true", className: "flex shrink-0 text-text-tertiary", children: icon }),
4379
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
4380
+ "input",
4381
+ {
4382
+ ref,
4383
+ id: fieldId,
4384
+ disabled,
4385
+ "aria-invalid": error ? true : void 0,
4386
+ "aria-describedby": note ? noteId : void 0,
4387
+ className: "min-w-0 flex-1 bg-transparent text-dense text-text-primary outline-hidden placeholder:text-text-tertiary",
4388
+ ...props
4389
+ }
4390
+ )
4391
+ ]
4392
+ }
4393
+ ),
4394
+ note && /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("p", { id: noteId, className: cn("text-xs", error ? "text-text-danger" : "text-text-tertiary"), children: note })
4395
+ ] });
4396
+ }
4397
+ );
4398
+ TextField.displayName = "TextField";
4093
4399
  // Annotate the CommonJS export names for ESM import in node:
4094
4400
  0 && (module.exports = {
4095
4401
  AISpark,
@@ -4108,11 +4414,13 @@ function PageHeader({ title, description, actions, className }) {
4108
4414
  DateRangeFilter,
4109
4415
  DateRangePicker,
4110
4416
  Delta,
4417
+ EmptyState,
4111
4418
  FilterChipBar,
4112
4419
  FilterDropdownFooter,
4113
4420
  Freshness,
4114
4421
  InfoTip,
4115
4422
  LineChartPlaceholder,
4423
+ Modal,
4116
4424
  MultiSelectFilter,
4117
4425
  NO_RESULTS_BODY,
4118
4426
  NO_RESULTS_TITLE,
@@ -4127,6 +4435,7 @@ function PageHeader({ title, description, actions, className }) {
4127
4435
  SearchBar,
4128
4436
  SectionHeading,
4129
4437
  SectionRail,
4438
+ SegmentedControl,
4130
4439
  SelectAllVisible,
4131
4440
  SelectFilter,
4132
4441
  SelectedSection,
@@ -4134,7 +4443,9 @@ function PageHeader({ title, description, actions, className }) {
4134
4443
  SortButton,
4135
4444
  StackedColumnPlaceholder,
4136
4445
  TablePagination,
4446
+ Tabs,
4137
4447
  Tag,
4448
+ TextField,
4138
4449
  Toast,
4139
4450
  Toggle,
4140
4451
  Tooltip,