@mediaradar/design-components 0.1.0 → 0.5.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
@@ -20,7 +20,34 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // src/index.ts
21
21
  var index_exports = {};
22
22
  __export(index_exports, {
23
- InfoTip: () => InfoTip
23
+ AdvertiserNoResults: () => AdvertiserNoResults,
24
+ AdvertiserQuickFilter: () => AdvertiserQuickFilter,
25
+ AdvertiserSearchPrompt: () => AdvertiserSearchPrompt,
26
+ BarChartPlaceholder: () => BarChartPlaceholder,
27
+ Card: () => Card,
28
+ CheckBox: () => CheckBox,
29
+ DEFAULT_SEARCH_MATCH: () => DEFAULT_SEARCH_MATCH,
30
+ DataScopeFilter: () => DataScopeFilter,
31
+ DateRangeFilter: () => DateRangeFilter,
32
+ InfoTip: () => InfoTip,
33
+ LineChartPlaceholder: () => LineChartPlaceholder,
34
+ NO_RESULTS_BODY: () => NO_RESULTS_BODY,
35
+ NO_RESULTS_TITLE: () => NO_RESULTS_TITLE,
36
+ NoResultsEmptyState: () => NoResultsEmptyState,
37
+ PRIMARY_ACTION_CLASS: () => PRIMARY_ACTION_CLASS,
38
+ PieChartPlaceholder: () => PieChartPlaceholder,
39
+ PublisherBreakdownList: () => PublisherBreakdownList,
40
+ QUIET_ACTION_CLASS: () => QUIET_ACTION_CLASS,
41
+ SearchBar: () => SearchBar,
42
+ SectionRail: () => SectionRail,
43
+ SelectAllVisible: () => SelectAllVisible,
44
+ SelectFilter: () => SelectFilter,
45
+ SelectedSection: () => SelectedSection,
46
+ SelectionPicker: () => SelectionPicker,
47
+ StackedColumnPlaceholder: () => StackedColumnPlaceholder,
48
+ Tooltip: () => Tooltip,
49
+ WorkspaceSelectedCard: () => WorkspaceSelectedCard,
50
+ noResultsBody: () => noResultsBody
24
51
  });
25
52
  module.exports = __toCommonJS(index_exports);
26
53
 
@@ -96,8 +123,2511 @@ function InfoTip({ text, size = 11, label = "More information", trigger = "click
96
123
  )
97
124
  ] });
98
125
  }
126
+
127
+ // src/AdvertiserQuickFilter.tsx
128
+ var import_react2 = require("react");
129
+ var import_lucide_react3 = require("lucide-react");
130
+
131
+ // src/AdvertiserSearchPrompt.tsx
132
+ var import_lucide_react2 = require("lucide-react");
133
+ var import_jsx_runtime2 = require("react/jsx-runtime");
134
+ function AdvertiserSearchPrompt({ label = "an advertiser name" }) {
135
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex flex-col items-center gap-2 px-4 py-8 text-center", children: [
136
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "flex h-9 w-9 items-center justify-center rounded-full bg-subtle text-text-tertiary", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_lucide_react2.Search, { size: 16, "aria-hidden": "true" }) }),
137
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("p", { className: "text-[12px] leading-snug text-text-tertiary", children: [
138
+ "Start typing ",
139
+ label,
140
+ " to filter."
141
+ ] })
142
+ ] });
143
+ }
144
+
145
+ // src/AdvertiserQuickFilter.tsx
146
+ var import_jsx_runtime3 = require("react/jsx-runtime");
147
+ function cx(...parts) {
148
+ return parts.filter(Boolean).join(" ");
149
+ }
150
+ var SELECT_ALL_THRESHOLD = 5;
151
+ function slug(value) {
152
+ return value.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/(^-|-$)/g, "");
153
+ }
154
+ function AdvertiserQuickFilter({
155
+ caption = "Advertiser",
156
+ options,
157
+ selected,
158
+ onChange,
159
+ align = "left",
160
+ icon,
161
+ showFooter = false,
162
+ clearValue = [],
163
+ parked = [],
164
+ searchPlaceholder = "Search for an advertiser",
165
+ promptLabel = "an advertiser name",
166
+ noResultsHint = "Try checking your spelling or searching for a broader brand name.",
167
+ clearLabel = "Clear advertisers",
168
+ testIdPrefix
169
+ }) {
170
+ const [open, setOpen] = (0, import_react2.useState)(false);
171
+ const [q, setQ] = (0, import_react2.useState)("");
172
+ const [draft, setDraft] = (0, import_react2.useState)(selected);
173
+ const [selectedOpen, setSelectedOpen] = (0, import_react2.useState)(true);
174
+ const ref = (0, import_react2.useRef)(null);
175
+ const inputRef = (0, import_react2.useRef)(null);
176
+ const clearSearch = () => {
177
+ setQ("");
178
+ setSelectedOpen(false);
179
+ };
180
+ const close = (0, import_react2.useCallback)(() => {
181
+ setOpen(false);
182
+ setQ("");
183
+ if (showFooter) return;
184
+ if (draft.length !== selected.length || draft.some((id) => !selected.includes(id))) {
185
+ onChange(draft);
186
+ }
187
+ }, [draft, selected, onChange, showFooter]);
188
+ const openTray = () => {
189
+ setDraft(selected);
190
+ setSelectedOpen(true);
191
+ setOpen(true);
192
+ };
193
+ const apply = () => {
194
+ onChange(draft);
195
+ setOpen(false);
196
+ setQ("");
197
+ };
198
+ (0, import_react2.useEffect)(() => {
199
+ if (!open) return;
200
+ inputRef.current?.focus();
201
+ const onKey = (e) => e.key === "Escape" && close();
202
+ const onClick = (e) => {
203
+ if (ref.current && !ref.current.contains(e.target)) close();
204
+ };
205
+ window.addEventListener("keydown", onKey);
206
+ window.addEventListener("mousedown", onClick);
207
+ return () => {
208
+ window.removeEventListener("keydown", onKey);
209
+ window.removeEventListener("mousedown", onClick);
210
+ };
211
+ }, [open, close]);
212
+ const toggle = (id) => setDraft((cur) => cur.includes(id) ? cur.filter((x) => x !== id) : [...cur, id]);
213
+ const ql = q.trim().toLowerCase();
214
+ const selectedOptions = (0, import_react2.useMemo)(
215
+ () => draft.map((id) => options.find((o) => o.id === id)).filter(Boolean),
216
+ [draft, options]
217
+ );
218
+ const results = (0, import_react2.useMemo)(
219
+ () => ql ? options.filter((o) => !draft.includes(o.id) && o.label.toLowerCase().includes(ql)) : [],
220
+ [ql, options, draft]
221
+ );
222
+ const visibleIds = results.map((o) => o.id);
223
+ const allVisibleSelected = visibleIds.length > 0 && visibleIds.every((id) => draft.includes(id));
224
+ const toggleAllVisible = () => setDraft(
225
+ (cur) => allVisibleSelected ? cur.filter((id) => !visibleIds.includes(id)) : [.../* @__PURE__ */ new Set([...cur, ...visibleIds])]
226
+ );
227
+ const shown = open && !showFooter ? draft : selected;
228
+ const counted = parked.length ? shown.filter((id) => !parked.includes(id)) : shown;
229
+ const value = counted.length === 0 ? "All" : counted.length === 1 ? options.find((o) => o.id === counted[0])?.label ?? "1 selected" : `${counted.length} selected`;
230
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "relative", ref, children: [
231
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
232
+ "button",
233
+ {
234
+ type: "button",
235
+ "data-testid": testIdPrefix && `${testIdPrefix}-trigger`,
236
+ onClick: () => open ? close() : openTray(),
237
+ "aria-haspopup": "dialog",
238
+ "aria-expanded": open,
239
+ className: "flex h-11 items-center gap-2 rounded-control border border-border-strong bg-input px-2.5 text-text-secondary transition-colors hover:bg-subtle hover:text-text-primary focus-visible:outline-solid focus-visible:outline-2 focus-visible:outline-border-focus",
240
+ children: [
241
+ icon && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "shrink-0 text-text-tertiary", children: icon }),
242
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { className: "flex flex-col gap-1 text-left leading-none", children: [
243
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "text-[10px] uppercase tracking-wide text-text-tertiary", children: caption }),
244
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "text-[12px] font-medium text-text-primary", children: value })
245
+ ] }),
246
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
247
+ import_lucide_react3.ChevronDown,
248
+ {
249
+ size: 14,
250
+ "aria-hidden": "true",
251
+ className: cx("shrink-0 text-text-secondary transition-transform", open && "rotate-180")
252
+ }
253
+ )
254
+ ]
255
+ }
256
+ ),
257
+ open && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
258
+ "div",
259
+ {
260
+ className: cx(
261
+ "absolute top-[calc(100%+8px)] z-30 flex w-72 flex-col overflow-hidden rounded-card border border-border-default bg-surface shadow-overlay",
262
+ align === "right" ? "right-0" : "left-0"
263
+ ),
264
+ role: "dialog",
265
+ "aria-label": caption,
266
+ "data-testid": testIdPrefix && `${testIdPrefix}-popover`,
267
+ children: [
268
+ /* @__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-default bg-input px-2.5 focus-within:border-border-focus focus-within:ring-2 focus-within:ring-border-focus/30", children: [
269
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_lucide_react3.Search, { size: 14, className: "shrink-0 text-text-secondary", "aria-hidden": "true" }),
270
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
271
+ "input",
272
+ {
273
+ ref: inputRef,
274
+ type: "text",
275
+ value: q,
276
+ onChange: (e) => setQ(e.target.value),
277
+ onMouseDown: () => setSelectedOpen(false),
278
+ placeholder: searchPlaceholder,
279
+ "aria-label": searchPlaceholder,
280
+ "data-testid": testIdPrefix && `${testIdPrefix}-search`,
281
+ className: "h-full min-w-0 flex-1 bg-transparent text-[13px] outline-hidden placeholder:text-text-tertiary"
282
+ }
283
+ ),
284
+ q && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
285
+ "button",
286
+ {
287
+ type: "button",
288
+ onClick: clearSearch,
289
+ "aria-label": "Clear search",
290
+ className: "shrink-0 text-text-tertiary transition-colors hover:text-text-primary",
291
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_lucide_react3.X, { size: 13 })
292
+ }
293
+ )
294
+ ] }) }),
295
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "max-h-[300px] overflow-y-auto p-1", children: [
296
+ selectedOptions.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "mb-1 border-b border-border-default pb-1", children: [
297
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
298
+ "button",
299
+ {
300
+ type: "button",
301
+ "data-testid": testIdPrefix && `${testIdPrefix}-selected-toggle`,
302
+ onClick: () => setSelectedOpen((v) => !v),
303
+ "aria-expanded": selectedOpen,
304
+ className: "flex w-full items-center gap-1.5 rounded-control px-2 py-1 text-left text-[11px] font-semibold uppercase tracking-wide text-text-tertiary transition-colors hover:text-text-primary",
305
+ children: [
306
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
307
+ import_lucide_react3.ChevronDown,
308
+ {
309
+ size: 13,
310
+ "aria-hidden": "true",
311
+ className: cx("transition-transform", !selectedOpen && "-rotate-90")
312
+ }
313
+ ),
314
+ "Selected (",
315
+ selectedOptions.length,
316
+ ")"
317
+ ]
318
+ }
319
+ ),
320
+ selectedOpen && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "flex flex-col gap-0.5", children: selectedOptions.map((o) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
321
+ OptionRow,
322
+ {
323
+ label: o.label,
324
+ checked: true,
325
+ onToggle: () => toggle(o.id),
326
+ testId: testIdPrefix && `${testIdPrefix}-option-${slug(o.id)}`
327
+ },
328
+ o.id
329
+ )) })
330
+ ] }),
331
+ !ql ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(AdvertiserSearchPrompt, { label: promptLabel }) : results.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "flex flex-col items-center gap-1.5 px-4 py-8 text-center", children: [
332
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("p", { className: "text-[13px] font-medium text-text-primary", children: [
333
+ "No results found for \u201C",
334
+ q.trim(),
335
+ "\u201D"
336
+ ] }),
337
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "text-[12px] leading-snug text-text-tertiary", children: noResultsHint })
338
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
339
+ results.length > SELECT_ALL_THRESHOLD && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
340
+ "button",
341
+ {
342
+ type: "button",
343
+ "data-testid": testIdPrefix && `${testIdPrefix}-select-all-visible`,
344
+ onClick: toggleAllVisible,
345
+ className: "flex w-full items-center gap-2 rounded-control px-2 py-1.5 text-left text-[12px] font-semibold text-text-link transition-colors hover:bg-subtle",
346
+ children: [
347
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_lucide_react3.ListChecks, { size: 14, "aria-hidden": "true" }),
348
+ allVisibleSelected ? `Deselect ${results.length} visible` : `Select all ${results.length} visible`
349
+ ]
350
+ }
351
+ ),
352
+ results.map((o) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
353
+ OptionRow,
354
+ {
355
+ label: o.label,
356
+ checked: false,
357
+ onToggle: () => toggle(o.id),
358
+ testId: testIdPrefix && `${testIdPrefix}-option-${slug(o.id)}`
359
+ },
360
+ o.id
361
+ ))
362
+ ] })
363
+ ] }),
364
+ showFooter ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "flex items-center justify-between border-t border-border-default px-3 py-2.5", children: [
365
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
366
+ "button",
367
+ {
368
+ type: "button",
369
+ "data-testid": testIdPrefix && `${testIdPrefix}-clear-all`,
370
+ onClick: () => setDraft(clearValue),
371
+ disabled: draft.length === 0,
372
+ className: "text-[13px] font-medium text-text-secondary transition-colors hover:text-text-primary disabled:opacity-40",
373
+ children: "Clear all"
374
+ }
375
+ ),
376
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
377
+ "button",
378
+ {
379
+ type: "button",
380
+ "data-testid": testIdPrefix && `${testIdPrefix}-apply`,
381
+ onClick: apply,
382
+ className: "rounded-control bg-brand-primaryAction px-5 py-2 text-[13px] font-semibold text-brand-onprimary transition hover:brightness-110",
383
+ children: "Apply"
384
+ }
385
+ )
386
+ ] }) : draft.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "flex items-center justify-between border-t border-border-default p-2", children: [
387
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { className: "text-[11px] text-text-tertiary", children: [
388
+ draft.length,
389
+ " selected"
390
+ ] }),
391
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
392
+ "button",
393
+ {
394
+ type: "button",
395
+ onClick: () => setDraft([]),
396
+ className: "text-[12px] font-medium text-text-link hover:underline",
397
+ children: clearLabel
398
+ }
399
+ )
400
+ ] })
401
+ ]
402
+ }
403
+ )
404
+ ] });
405
+ }
406
+ function OptionRow({
407
+ label,
408
+ checked,
409
+ onToggle,
410
+ testId
411
+ }) {
412
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
413
+ "div",
414
+ {
415
+ onClick: onToggle,
416
+ className: "flex cursor-pointer items-center gap-2.5 rounded-control px-2 py-2 transition-colors hover:bg-subtle",
417
+ children: [
418
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
419
+ "button",
420
+ {
421
+ type: "button",
422
+ role: "checkbox",
423
+ "aria-checked": checked,
424
+ "aria-label": label,
425
+ "data-testid": testId,
426
+ onClick: (e) => {
427
+ e.stopPropagation();
428
+ onToggle();
429
+ },
430
+ className: cx(
431
+ "flex h-[18px] w-[18px] shrink-0 items-center justify-center rounded-[5px] border transition-colors",
432
+ "focus-visible:outline-solid focus-visible:outline-2 focus-visible:outline-border-focus",
433
+ checked ? "border-transparent bg-brand-primaryAction text-brand-onprimary" : "border-border-interactive bg-surface hover:border-brand-primaryAction"
434
+ ),
435
+ children: checked && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_lucide_react3.Check, { size: 13, strokeWidth: 3 })
436
+ }
437
+ ),
438
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "flex-1 text-[14px] text-text-primary", children: label })
439
+ ]
440
+ }
441
+ );
442
+ }
443
+
444
+ // src/CheckBox.tsx
445
+ var import_lucide_react4 = require("lucide-react");
446
+ var import_jsx_runtime4 = require("react/jsx-runtime");
447
+ function CheckBox({ checked, indeterminate, onChange, ariaLabel, disabled, testId, title }) {
448
+ const on = checked || indeterminate;
449
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
450
+ "button",
451
+ {
452
+ type: "button",
453
+ role: "checkbox",
454
+ "aria-checked": indeterminate ? "mixed" : checked,
455
+ "aria-label": ariaLabel,
456
+ title,
457
+ disabled,
458
+ "data-testid": testId,
459
+ onClick: (e) => {
460
+ e.stopPropagation();
461
+ onChange();
462
+ },
463
+ className: [
464
+ "flex h-[18px] w-[18px] shrink-0 items-center justify-center rounded-[5px] border transition-colors",
465
+ "focus-visible:outline-solid focus-visible:outline-2 focus-visible:outline-border-focus",
466
+ on ? "border-transparent bg-brand-primaryAction text-white" : "border-border-interactive bg-input hover:border-brand-primaryAction",
467
+ disabled && "cursor-not-allowed opacity-50"
468
+ ].filter(Boolean).join(" "),
469
+ children: indeterminate ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_lucide_react4.Minus, { size: 13, strokeWidth: 3 }) : checked ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_lucide_react4.Check, { size: 13, strokeWidth: 3 }) : null
470
+ }
471
+ );
472
+ }
473
+
474
+ // src/DateRangeFilter.tsx
475
+ var import_react3 = require("react");
476
+ var import_lucide_react5 = require("lucide-react");
477
+ var import_jsx_runtime5 = require("react/jsx-runtime");
478
+ var DEFAULT_SHORTCUTS = [
479
+ "Year to date",
480
+ "Rolling 3 months",
481
+ "Rolling 6 months",
482
+ "Rolling 12 months",
483
+ "Previous quarter",
484
+ "Current quarter to date",
485
+ "Previous month",
486
+ "Current month",
487
+ "Custom"
488
+ ];
489
+ var CREATIVE_INTEL_SHORTCUTS = [
490
+ "Today",
491
+ "Yesterday",
492
+ "Last 7 days",
493
+ "Last 14 days",
494
+ "Last month",
495
+ "Rolling 3 months",
496
+ "Rolling 6 months",
497
+ "Rolling 12 months",
498
+ "Year to date",
499
+ "Previous year",
500
+ "All available history",
501
+ "Custom"
502
+ ];
503
+ var WEEKDAYS = ["Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"];
504
+ var MONTHS = [
505
+ "January",
506
+ "February",
507
+ "March",
508
+ "April",
509
+ "May",
510
+ "June",
511
+ "July",
512
+ "August",
513
+ "September",
514
+ "October",
515
+ "November",
516
+ "December"
517
+ ];
518
+ var startOfDay = (d) => new Date(d.getFullYear(), d.getMonth(), d.getDate());
519
+ var addDays = (d, n) => {
520
+ const x = new Date(d);
521
+ x.setDate(x.getDate() + n);
522
+ return x;
523
+ };
524
+ var addMonths = (d, n) => new Date(d.getFullYear(), d.getMonth() + n, 1);
525
+ var sameDay = (a, b) => a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth() && a.getDate() === b.getDate();
526
+ var fmt = (d) => `${MONTHS[d.getMonth()].slice(0, 3)} ${d.getDate()}, ${d.getFullYear()}`;
527
+ var fmtFull = (d) => `${MONTHS[d.getMonth()]} ${d.getDate()}, ${d.getFullYear()}`;
528
+ var fullRangeLabel = (s, e) => `${fmtFull(s)} - ${fmtFull(e)}`;
529
+ var presetLabel = (sc) => sc === "Custom" ? "Custom range" : sc;
530
+ function presetRange(key, today) {
531
+ const t = startOfDay(today);
532
+ const qMonth = Math.floor(t.getMonth() / 3) * 3;
533
+ switch (key) {
534
+ case "Year to date":
535
+ return [new Date(t.getFullYear(), 0, 1), t];
536
+ case "Rolling 3 months":
537
+ return [addMonths(t, -3), t];
538
+ case "Rolling 6 months":
539
+ return [addMonths(t, -6), t];
540
+ case "Rolling 12 months":
541
+ return [addMonths(t, -12), t];
542
+ case "Previous quarter": {
543
+ const s = new Date(t.getFullYear(), qMonth - 3, 1);
544
+ const e = new Date(t.getFullYear(), qMonth, 0);
545
+ return [s, e];
546
+ }
547
+ case "Current quarter to date":
548
+ return [new Date(t.getFullYear(), qMonth, 1), t];
549
+ case "Previous month": {
550
+ const s = new Date(t.getFullYear(), t.getMonth() - 1, 1);
551
+ const e = new Date(t.getFullYear(), t.getMonth(), 0);
552
+ return [s, e];
553
+ }
554
+ case "Current month":
555
+ return [new Date(t.getFullYear(), t.getMonth(), 1), t];
556
+ case "Today":
557
+ return [t, t];
558
+ case "Yesterday": {
559
+ const y = addDays(t, -1);
560
+ return [y, y];
561
+ }
562
+ case "Last 7 days":
563
+ return [addDays(t, -6), t];
564
+ case "Last 14 days":
565
+ return [addDays(t, -13), t];
566
+ case "Last month": {
567
+ const s = new Date(t.getFullYear(), t.getMonth() - 1, 1);
568
+ const e = new Date(t.getFullYear(), t.getMonth(), 0);
569
+ return [s, e];
570
+ }
571
+ case "Previous year":
572
+ return [new Date(t.getFullYear() - 1, 0, 1), new Date(t.getFullYear() - 1, 11, 31)];
573
+ case "All available history":
574
+ return [new Date(2e3, 0, 1), t];
575
+ default:
576
+ return null;
577
+ }
578
+ }
579
+ function monthGrid(year, month) {
580
+ const first = new Date(year, month, 1);
581
+ const dow = (first.getDay() + 6) % 7;
582
+ const gridStart = addDays(first, -dow);
583
+ return Array.from({ length: 42 }, (_, i) => addDays(gridStart, i));
584
+ }
585
+ function matchShortcut(shortcuts, label) {
586
+ if (!label) return "Rolling 12 months";
587
+ const hit = shortcuts.find((s) => s.toLowerCase() === label.toLowerCase());
588
+ return hit || "Custom";
589
+ }
590
+ function slug2(value) {
591
+ return value.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/(^-|-$)/g, "");
592
+ }
593
+ function DateRangeFilter({
594
+ caption = "Date Range",
595
+ defaultValue,
596
+ value: controlledValue,
597
+ range: controlledRange,
598
+ align = "left",
599
+ onChange,
600
+ onRangeChange,
601
+ showDates = false,
602
+ shortDates = false,
603
+ icon,
604
+ presetSet = "default",
605
+ testIdPrefix
606
+ }) {
607
+ const controlled = controlledValue !== void 0 && controlledRange !== void 0;
608
+ const SHORTCUTS = presetSet === "creativeIntel" ? CREATIVE_INTEL_SHORTCUTS : DEFAULT_SHORTCUTS;
609
+ const today = (0, import_react3.useMemo)(() => /* @__PURE__ */ new Date(), []);
610
+ const initShortcut = matchShortcut(SHORTCUTS, controlled ? controlledValue : defaultValue);
611
+ const initRange = controlled ? controlledRange : presetRange(initShortcut === "Custom" ? "Rolling 12 months" : initShortcut, today);
612
+ const dateLine = (s, e) => shortDates ? `${fmt(s)} \u2013 ${fmt(e)}` : fullRangeLabel(s, e);
613
+ const [open, setOpen] = (0, import_react3.useState)(false);
614
+ const [uncontrolledValue, setUncontrolledValue] = (0, import_react3.useState)(
615
+ showDates ? presetLabel(initShortcut) : defaultValue || "Last 7 days"
616
+ );
617
+ const [uncontrolledDatesLine, setUncontrolledDatesLine] = (0, import_react3.useState)(dateLine(initRange[0], initRange[1]));
618
+ const committed = (0, import_react3.useRef)({ shortcut: initShortcut, start: initRange[0], end: initRange[1] });
619
+ const displayValue = controlled ? showDates ? presetLabel(initShortcut) : controlledValue : uncontrolledValue;
620
+ const displayDatesLine = controlled ? dateLine(initRange[0], initRange[1]) : uncontrolledDatesLine;
621
+ const [shortcut, setShortcut] = (0, import_react3.useState)(initShortcut);
622
+ const [start, setStart] = (0, import_react3.useState)(initRange[0]);
623
+ const [end, setEnd] = (0, import_react3.useState)(initRange[1]);
624
+ const [leftMonth, setLeftMonth] = (0, import_react3.useState)(new Date(initRange[0].getFullYear(), initRange[0].getMonth(), 1));
625
+ const ref = (0, import_react3.useRef)(null);
626
+ (0, import_react3.useEffect)(() => {
627
+ if (controlled) return;
628
+ onRangeChange?.(committed.current.start, committed.current.end);
629
+ }, []);
630
+ (0, import_react3.useEffect)(() => {
631
+ if (!open) return;
632
+ const committedShortcut = controlled ? initShortcut : committed.current.shortcut;
633
+ const committedStart = controlled ? initRange[0] : committed.current.start;
634
+ const committedEnd = controlled ? initRange[1] : committed.current.end;
635
+ setShortcut(committedShortcut);
636
+ setStart(committedStart);
637
+ setEnd(committedEnd);
638
+ setLeftMonth(new Date(committedStart.getFullYear(), committedStart.getMonth(), 1));
639
+ const onKey = (e) => e.key === "Escape" && setOpen(false);
640
+ const onClick = (e) => {
641
+ if (ref.current && !ref.current.contains(e.target)) setOpen(false);
642
+ };
643
+ window.addEventListener("keydown", onKey);
644
+ window.addEventListener("mousedown", onClick);
645
+ return () => {
646
+ window.removeEventListener("keydown", onKey);
647
+ window.removeEventListener("mousedown", onClick);
648
+ };
649
+ }, [open]);
650
+ const choosePreset = (key) => {
651
+ setShortcut(key);
652
+ if (key === "Custom") return;
653
+ const r = presetRange(key, today);
654
+ if (r) {
655
+ setStart(r[0]);
656
+ setEnd(r[1]);
657
+ setLeftMonth(new Date(r[0].getFullYear(), r[0].getMonth(), 1));
658
+ }
659
+ };
660
+ const pickDay = (d) => {
661
+ setShortcut("Custom");
662
+ if (!start || start && end) {
663
+ setStart(d);
664
+ setEnd(null);
665
+ } else if (d < start) {
666
+ setEnd(start);
667
+ setStart(d);
668
+ } else setEnd(d);
669
+ };
670
+ const apply = () => {
671
+ const s = start || initRange[0];
672
+ const e = end || s;
673
+ const v = showDates ? presetLabel(shortcut) : shortcut === "Custom" ? `${fmt(s)} \u2014 ${fmt(e)}` : shortcut;
674
+ if (!controlled) {
675
+ committed.current = { shortcut, start: s, end: e };
676
+ setUncontrolledValue(v);
677
+ setUncontrolledDatesLine(dateLine(s, e));
678
+ }
679
+ onChange?.(v);
680
+ onRangeChange?.(s, e);
681
+ setOpen(false);
682
+ };
683
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "relative", ref, children: [
684
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
685
+ "button",
686
+ {
687
+ type: "button",
688
+ "data-testid": testIdPrefix && `${testIdPrefix}-trigger`,
689
+ onClick: () => setOpen((v) => !v),
690
+ "aria-expanded": open,
691
+ className: "flex h-11 items-center gap-2 rounded-control border border-border-strong bg-input px-2.5 text-text-secondary transition-colors hover:bg-subtle hover:text-text-primary focus-visible:outline-solid focus-visible:outline-2 focus-visible:outline-border-focus",
692
+ children: [
693
+ icon && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "shrink-0 text-text-tertiary", children: icon }),
694
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { className: "flex flex-col gap-1 text-left leading-none", children: [
695
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: showDates ? "text-[11px] text-text-tertiary" : "text-[10px] uppercase tracking-wide text-text-tertiary", children: showDates ? displayDatesLine : caption }),
696
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "text-[12px] font-medium text-text-primary", children: displayValue })
697
+ ] }),
698
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_lucide_react5.ChevronDown, { size: 14, "aria-hidden": "true", className: `shrink-0 text-text-secondary transition-transform ${open ? "rotate-180" : ""}` })
699
+ ]
700
+ }
701
+ ),
702
+ open && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
703
+ "div",
704
+ {
705
+ "data-testid": testIdPrefix && `${testIdPrefix}-popover`,
706
+ className: `absolute top-[calc(100%+8px)] z-30 flex overflow-hidden rounded-card border border-border-default bg-surface shadow-overlay ${align === "right" ? "right-0" : "left-0"}`,
707
+ role: "dialog",
708
+ "aria-label": "Select date range",
709
+ children: [
710
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "flex w-44 shrink-0 flex-col gap-0.5 border-r border-border-default p-2", children: SHORTCUTS.map((s) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
711
+ "button",
712
+ {
713
+ type: "button",
714
+ "data-testid": testIdPrefix && `${testIdPrefix}-option-${slug2(s)}`,
715
+ onClick: () => choosePreset(s),
716
+ className: `rounded-control px-3 py-2 text-left text-[13px] font-medium transition-colors ${shortcut === s ? "bg-subtle text-text-primary" : "text-text-secondary hover:bg-subtle hover:text-text-primary"}`,
717
+ children: s
718
+ },
719
+ s
720
+ )) }),
721
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex flex-col p-4", children: [
722
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex items-end gap-3", children: [
723
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Field, { label: "Start Date", value: start ? fmt(start) : "\u2014" }),
724
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "pb-2.5 text-text-tertiary", children: "\u2014" }),
725
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Field, { label: "End Date", value: end ? fmt(end) : start ? fmt(start) : "\u2014" })
726
+ ] }),
727
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "my-4 border-t border-border-default" }),
728
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "flex gap-8", children: [0, 1].map((offset) => {
729
+ const m = addMonths(leftMonth, offset);
730
+ const grid = monthGrid(m.getFullYear(), m.getMonth());
731
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "w-[252px]", children: [
732
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "mb-2 flex items-center justify-between", children: [
733
+ offset === 0 ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(NavBtn, { onClick: () => setLeftMonth(addMonths(leftMonth, -1)), "aria-label": "Previous month", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_lucide_react5.ChevronLeft, { size: 16 }) }) : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "h-7 w-7" }),
734
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { className: "text-[15px] font-semibold text-text-primary", children: [
735
+ MONTHS[m.getMonth()],
736
+ " ",
737
+ m.getFullYear()
738
+ ] }),
739
+ offset === 1 ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(NavBtn, { onClick: () => setLeftMonth(addMonths(leftMonth, 1)), "aria-label": "Next month", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_lucide_react5.ChevronRight, { size: 16 }) }) : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "h-7 w-7" })
740
+ ] }),
741
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "grid grid-cols-7", children: [
742
+ WEEKDAYS.map((w) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "flex h-8 items-center justify-center text-[12px] font-semibold text-text-secondary", children: w }, w)),
743
+ grid.map((d, i) => {
744
+ const inMonth = d.getMonth() === m.getMonth();
745
+ const hasRange = !!(start && end);
746
+ const isStart = !!start && sameDay(d, start);
747
+ const isEnd = end ? sameDay(d, end) : isStart && !end;
748
+ const inRange = hasRange && d >= startOfDay(start) && d <= startOfDay(end);
749
+ const single = isStart && isEnd;
750
+ const endpoint = isStart || !!end && isEnd;
751
+ let band = "";
752
+ if (inRange && !single) {
753
+ if (isStart) band = "left-1/2 right-0";
754
+ else if (end && isEnd) band = "left-0 right-1/2";
755
+ else band = "left-0 right-0";
756
+ }
757
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("button", { type: "button", onClick: () => pickDay(d), className: "relative flex h-9 items-center justify-center", children: [
758
+ band && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: `absolute inset-y-[3px] bg-ai-tint ${band}`, "aria-hidden": "true" }),
759
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
760
+ "span",
761
+ {
762
+ className: `relative z-10 flex h-8 w-8 items-center justify-center rounded-full text-[13px] tabular-nums ${endpoint ? "bg-brand-primaryAction font-semibold text-white" : inMonth ? "text-text-primary hover:bg-subtle" : "text-text-tertiary"}`,
763
+ children: d.getDate()
764
+ }
765
+ )
766
+ ] }, i);
767
+ })
768
+ ] })
769
+ ] }, offset);
770
+ }) }),
771
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "mt-4 flex justify-end gap-2", children: [
772
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { type: "button", "data-testid": testIdPrefix && `${testIdPrefix}-cancel`, onClick: () => setOpen(false), className: "rounded-control border border-border-default bg-surface px-5 py-2 text-[13px] font-semibold text-text-primary transition-colors hover:bg-subtle", children: "Cancel" }),
773
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { type: "button", "data-testid": testIdPrefix && `${testIdPrefix}-apply`, onClick: apply, className: "rounded-control bg-brand-primaryAction px-5 py-2 text-[13px] font-semibold text-white transition-[filter] hover:brightness-110", children: "Apply" })
774
+ ] })
775
+ ] })
776
+ ]
777
+ }
778
+ )
779
+ ] });
780
+ }
781
+ function Field({ label, value }) {
782
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex flex-col gap-1.5", children: [
783
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "text-[12px] font-medium text-text-secondary", children: label }),
784
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "flex h-10 min-w-[140px] items-center rounded-control border border-border-default bg-input px-3 text-[14px] font-medium text-text-primary", children: value })
785
+ ] });
786
+ }
787
+ function NavBtn({ children, onClick, ...rest }) {
788
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { type: "button", onClick, className: "flex h-7 w-7 items-center justify-center rounded-full text-text-secondary transition-colors hover:bg-subtle hover:text-text-primary", ...rest, children });
789
+ }
790
+
791
+ // src/SearchBar.tsx
792
+ var import_react4 = require("react");
793
+ var import_lucide_react6 = require("lucide-react");
794
+ var import_jsx_runtime6 = require("react/jsx-runtime");
795
+ var DEFAULT_SEARCH_MATCH = { mode: "anyWord", wordJoin: "Or" };
796
+ var MODE_LABEL = {
797
+ anyWord: "Any Word",
798
+ exactMatch: "Exact Match",
799
+ contextual: "Contextual",
800
+ adCode: "Ad Code"
801
+ };
802
+ function cx2(...classes) {
803
+ return classes.filter(Boolean).join(" ");
804
+ }
805
+ function SearchBar({
806
+ placeholder = "Search\u2026",
807
+ value,
808
+ onChange,
809
+ variant = "default",
810
+ matchValue,
811
+ onMatchChange,
812
+ className,
813
+ testIdPrefix
814
+ }) {
815
+ const [open, setOpen] = (0, import_react4.useState)(false);
816
+ const ref = (0, import_react4.useRef)(null);
817
+ (0, import_react4.useEffect)(() => {
818
+ if (!open) return;
819
+ const onKey = (e) => e.key === "Escape" && setOpen(false);
820
+ const onClick = (e) => {
821
+ if (ref.current && !ref.current.contains(e.target)) setOpen(false);
822
+ };
823
+ window.addEventListener("keydown", onKey);
824
+ window.addEventListener("mousedown", onClick);
825
+ return () => {
826
+ window.removeEventListener("keydown", onKey);
827
+ window.removeEventListener("mousedown", onClick);
828
+ };
829
+ }, [open]);
830
+ const showMatch = variant === "filterable" && matchValue;
831
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
832
+ "div",
833
+ {
834
+ className: cx2(
835
+ "flex h-11 min-w-[200px] flex-1 items-stretch rounded-control border border-border-strong bg-input",
836
+ className
837
+ ),
838
+ children: [
839
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("label", { className: "flex min-w-0 flex-1 items-center gap-2 px-2.5", children: [
840
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_lucide_react6.Search, { size: 15, "aria-hidden": "true", className: "shrink-0 text-text-tertiary" }),
841
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
842
+ "input",
843
+ {
844
+ type: "text",
845
+ value,
846
+ onChange: (e) => onChange?.(e.target.value),
847
+ placeholder,
848
+ "aria-label": placeholder,
849
+ "data-testid": testIdPrefix && `${testIdPrefix}-search`,
850
+ className: "min-w-0 flex-1 bg-transparent text-[13px] text-text-primary outline-hidden placeholder:text-text-tertiary"
851
+ }
852
+ )
853
+ ] }),
854
+ showMatch && // border-border-default, not the bare `border-border` both source
855
+ // implementations used -- confirmed by actually compiling against
856
+ // @mediaradar/design-tokens' real published theme.css (not assumed):
857
+ // there is no bare `--color-border` in that file, only the
858
+ // -default/-interactive/-focus/-focusSoft/-strong suffixed variants,
859
+ // so `border-border` silently generates no utility at all there. It
860
+ // only "worked" in each source app because each one separately
861
+ // defines its own local `border-border` alias outside the shared
862
+ // token package (insights-studio-prototype's own src/index.css;
863
+ // mr-clientapps' own tailwind.config.ts `colors.border.DEFAULT`) --
864
+ // an alias this shared component can't rely on existing.
865
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "relative shrink-0 border-l border-border-default", ref, children: [
866
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
867
+ "button",
868
+ {
869
+ type: "button",
870
+ "data-testid": testIdPrefix && `${testIdPrefix}-mode-trigger`,
871
+ onClick: () => setOpen((o) => !o),
872
+ "aria-expanded": open,
873
+ "aria-haspopup": "dialog",
874
+ className: "flex h-full items-center gap-1 px-2.5 text-[13px] font-medium text-text-primary transition-colors hover:bg-subtle",
875
+ children: [
876
+ matchValue.mode === "anyWord" ? `${MODE_LABEL.anyWord} (${matchValue.wordJoin})` : MODE_LABEL[matchValue.mode],
877
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
878
+ import_lucide_react6.ChevronDown,
879
+ {
880
+ size: 14,
881
+ "aria-hidden": "true",
882
+ className: cx2("shrink-0 text-text-secondary transition-transform", open && "rotate-180")
883
+ }
884
+ )
885
+ ]
886
+ }
887
+ ),
888
+ open && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
889
+ "div",
890
+ {
891
+ role: "dialog",
892
+ "aria-label": "Search match type",
893
+ "data-testid": testIdPrefix && `${testIdPrefix}-mode-popover`,
894
+ className: "absolute right-0 top-[calc(100%+8px)] z-50 w-[400px] rounded-card border border-border-default bg-surface p-3 shadow-overlay",
895
+ children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { role: "radiogroup", "aria-label": "Search match type", className: "flex flex-col", children: [
896
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
897
+ ModeRow,
898
+ {
899
+ title: "Any Word Text Search",
900
+ selected: matchValue.mode === "anyWord",
901
+ onSelect: () => onMatchChange?.({ mode: "anyWord", wordJoin: matchValue.wordJoin }),
902
+ testId: testIdPrefix && `${testIdPrefix}-mode-anyWord`,
903
+ description: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_jsx_runtime6.Fragment, { children: "Find creatives that contain your keyword(s) using an \u201COr\u201D (match any) or \u201CAnd\u201D (match all) conditional in any order." }),
904
+ infoText: "The Or/And choice applies only when Any Word is the active search mode.",
905
+ extra: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "flex items-center gap-3", children: [
906
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
907
+ RadioOption,
908
+ {
909
+ label: "Or",
910
+ checked: matchValue.wordJoin === "Or",
911
+ onSelect: () => onMatchChange?.({ mode: "anyWord", wordJoin: "Or" }),
912
+ testId: testIdPrefix && `${testIdPrefix}-word-join-or`
913
+ }
914
+ ),
915
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
916
+ RadioOption,
917
+ {
918
+ label: "And",
919
+ checked: matchValue.wordJoin === "And",
920
+ onSelect: () => onMatchChange?.({ mode: "anyWord", wordJoin: "And" }),
921
+ testId: testIdPrefix && `${testIdPrefix}-word-join-and`
922
+ }
923
+ )
924
+ ] })
925
+ }
926
+ ),
927
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Divider, {}),
928
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
929
+ ModeRow,
930
+ {
931
+ title: "Exact Match Text Search",
932
+ selected: matchValue.mode === "exactMatch",
933
+ onSelect: () => onMatchChange?.({ mode: "exactMatch", wordJoin: matchValue.wordJoin }),
934
+ testId: testIdPrefix && `${testIdPrefix}-mode-exactMatch`,
935
+ description: "Find creatives that mention your exact keyword phrase.",
936
+ infoText: "Keywords are matched as a single contiguous phrase, in the order you typed them."
937
+ }
938
+ ),
939
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Divider, {}),
940
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
941
+ ModeRow,
942
+ {
943
+ title: "Contextual Search",
944
+ badge: "Beta",
945
+ selected: matchValue.mode === "contextual",
946
+ onSelect: () => onMatchChange?.({ mode: "contextual", wordJoin: matchValue.wordJoin }),
947
+ testId: testIdPrefix && `${testIdPrefix}-mode-contextual`,
948
+ description: "Find creatives that may contain OR have similar meaning as your keyword(s).",
949
+ infoText: "Uses semantic matching, so results can include related terms even when the exact keyword isn't present."
950
+ }
951
+ ),
952
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Divider, {}),
953
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
954
+ ModeRow,
955
+ {
956
+ title: "Ad Code Search",
957
+ selected: matchValue.mode === "adCode",
958
+ onSelect: () => onMatchChange?.({ mode: "adCode", wordJoin: matchValue.wordJoin }),
959
+ testId: testIdPrefix && `${testIdPrefix}-mode-adCode`,
960
+ description: "Find creatives based on a list of one or more ad codes that you provide.",
961
+ infoText: "Separate multiple ad codes with a comma or a new line."
962
+ }
963
+ )
964
+ ] })
965
+ }
966
+ )
967
+ ] })
968
+ ]
969
+ }
970
+ );
971
+ }
972
+ function Divider() {
973
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "my-2.5 border-t border-border-default", "aria-hidden": "true" });
974
+ }
975
+ function ModeRow({
976
+ title,
977
+ badge,
978
+ extra,
979
+ description,
980
+ infoText,
981
+ selected,
982
+ onSelect,
983
+ testId
984
+ }) {
985
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
986
+ "div",
987
+ {
988
+ role: "radio",
989
+ tabIndex: 0,
990
+ "aria-checked": selected,
991
+ "data-testid": testId,
992
+ onClick: onSelect,
993
+ onKeyDown: (e) => {
994
+ if (e.key === "Enter" || e.key === " ") {
995
+ e.preventDefault();
996
+ onSelect();
997
+ }
998
+ },
999
+ className: "flex cursor-pointer flex-col items-stretch gap-1 rounded-control p-1.5 text-left transition-colors hover:bg-subtle focus-visible:outline-solid focus-visible:outline-2 focus-visible:outline-border-focus",
1000
+ children: [
1001
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "flex items-center justify-between gap-2", children: [
1002
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "flex flex-wrap items-center gap-3", children: [
1003
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "flex items-center gap-1", children: [
1004
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "text-[14px] font-semibold text-text-primary", children: title }),
1005
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(RowInfoTip, { text: infoText })
1006
+ ] }),
1007
+ badge && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "rounded-full bg-subtle px-2 py-0.5 text-[10px] font-semibold uppercase tracking-wide text-text-secondary", children: badge }),
1008
+ extra
1009
+ ] }),
1010
+ selected && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_lucide_react6.Check, { size: 16, strokeWidth: 2.5, "aria-hidden": "true", className: "shrink-0 text-brand-primaryAction" })
1011
+ ] }),
1012
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "block text-[12.5px] leading-snug text-text-secondary", children: description })
1013
+ ]
1014
+ }
1015
+ );
1016
+ }
1017
+ function RadioOption({
1018
+ label,
1019
+ checked,
1020
+ onSelect,
1021
+ testId
1022
+ }) {
1023
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
1024
+ "button",
1025
+ {
1026
+ type: "button",
1027
+ "data-testid": testId,
1028
+ onClick: (e) => {
1029
+ e.stopPropagation();
1030
+ onSelect();
1031
+ },
1032
+ role: "radio",
1033
+ "aria-checked": checked,
1034
+ className: "flex items-center gap-1.5 text-[13px] font-medium text-text-primary",
1035
+ children: [
1036
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1037
+ "span",
1038
+ {
1039
+ className: cx2(
1040
+ "flex h-4 w-4 shrink-0 items-center justify-center rounded-full border-2 transition-colors",
1041
+ checked ? "border-brand-primaryAction" : "border-border-interactive"
1042
+ ),
1043
+ children: checked && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "h-2 w-2 rounded-full bg-brand-primaryAction" })
1044
+ }
1045
+ ),
1046
+ label
1047
+ ]
1048
+ }
1049
+ );
1050
+ }
1051
+ function RowInfoTip({ text }) {
1052
+ const [open, setOpen] = (0, import_react4.useState)(false);
1053
+ const ref = (0, import_react4.useRef)(null);
1054
+ (0, import_react4.useEffect)(() => {
1055
+ if (!open) return;
1056
+ const onDown = (e) => {
1057
+ if (ref.current && !ref.current.contains(e.target)) setOpen(false);
1058
+ };
1059
+ window.addEventListener("mousedown", onDown);
1060
+ return () => window.removeEventListener("mousedown", onDown);
1061
+ }, [open]);
1062
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "relative inline-flex shrink-0 items-center", ref, children: [
1063
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1064
+ "button",
1065
+ {
1066
+ type: "button",
1067
+ onClick: (e) => {
1068
+ e.stopPropagation();
1069
+ setOpen((o) => !o);
1070
+ },
1071
+ "aria-label": "More information",
1072
+ "aria-expanded": open,
1073
+ className: "inline-flex text-text-tertiary transition-colors hover:text-text-primary",
1074
+ children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_lucide_react6.AlertCircle, { size: 13, "aria-hidden": "true" })
1075
+ }
1076
+ ),
1077
+ open && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1078
+ "span",
1079
+ {
1080
+ role: "tooltip",
1081
+ className: "absolute left-0 top-[calc(100%+6px)] z-40 w-[220px] rounded-control border border-border-default bg-surface px-2.5 py-1.5 text-[11.5px] font-normal leading-snug text-text-secondary shadow-overlay",
1082
+ children: text
1083
+ }
1084
+ )
1085
+ ] });
1086
+ }
1087
+
1088
+ // src/SectionRail.tsx
1089
+ var import_react5 = require("react");
1090
+ var import_lucide_react7 = require("lucide-react");
1091
+ var import_jsx_runtime7 = require("react/jsx-runtime");
1092
+ function SectionRail({
1093
+ title,
1094
+ titleSerif,
1095
+ subtitle,
1096
+ viewAllLabel,
1097
+ viewAllHref,
1098
+ children
1099
+ }) {
1100
+ const scrollRef = (0, import_react5.useRef)(null);
1101
+ const scroll = (direction) => {
1102
+ scrollRef.current?.scrollBy({ left: direction * 320, behavior: "smooth" });
1103
+ };
1104
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("section", { className: "flex flex-col gap-4", children: [
1105
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("header", { className: "flex flex-col gap-3 sm:flex-row sm:items-end sm:justify-between", children: [
1106
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { children: [
1107
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1108
+ "h2",
1109
+ {
1110
+ className: titleSerif ? "font-serif text-2xl font-normal tracking-tight text-text-primary sm:text-[28px]" : "text-xl font-medium tracking-tight text-text-primary sm:text-2xl",
1111
+ children: title
1112
+ }
1113
+ ),
1114
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("p", { className: "mt-1 text-sm text-text-secondary", children: subtitle })
1115
+ ] }),
1116
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex items-center gap-2", children: [
1117
+ viewAllLabel && viewAllHref && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
1118
+ "a",
1119
+ {
1120
+ href: viewAllHref,
1121
+ className: "text-sm font-medium text-text-link transition-colors hover:underline focus-visible:rounded-sm focus-visible:outline-solid focus-visible:outline-2 focus-visible:outline-border-focus",
1122
+ children: [
1123
+ viewAllLabel,
1124
+ " \u2192"
1125
+ ]
1126
+ }
1127
+ ),
1128
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1129
+ "button",
1130
+ {
1131
+ type: "button",
1132
+ onClick: () => scroll(-1),
1133
+ "aria-label": "Previous",
1134
+ className: "hidden h-10 w-10 items-center justify-center rounded-control border border-border text-text-secondary transition-colors hover:bg-subtle hover:text-text-primary sm:flex lg:hidden",
1135
+ children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_lucide_react7.ChevronLeft, { size: 16 })
1136
+ }
1137
+ ),
1138
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1139
+ "button",
1140
+ {
1141
+ type: "button",
1142
+ onClick: () => scroll(1),
1143
+ "aria-label": "Next",
1144
+ className: "hidden h-10 w-10 items-center justify-center rounded-control border border-border text-text-secondary transition-colors hover:bg-subtle hover:text-text-primary sm:flex lg:hidden",
1145
+ children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_lucide_react7.ChevronRight, { size: 16 })
1146
+ }
1147
+ )
1148
+ ] })
1149
+ ] }),
1150
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1151
+ "div",
1152
+ {
1153
+ ref: scrollRef,
1154
+ className: "flex gap-4 overflow-x-auto scroll-smooth pb-2 snap-x snap-mandatory scrollbar-none [&::-webkit-scrollbar]:hidden -mx-4 px-4 sm:mx-0 sm:px-0 *:w-[268px] *:shrink-0 *:snap-start lg:grid lg:grid-cols-[repeat(4,minmax(0,320px))] lg:gap-5 lg:overflow-visible lg:justify-start lg:*:w-auto lg:*:shrink lg:*:snap-none",
1155
+ children
1156
+ }
1157
+ )
1158
+ ] });
1159
+ }
1160
+
1161
+ // src/DataScopeFilter.tsx
1162
+ var import_react6 = require("react");
1163
+ var import_lucide_react8 = require("lucide-react");
1164
+ var import_jsx_runtime8 = require("react/jsx-runtime");
1165
+ var FINAL_ONLY_TEXT = "Trims time periods to data that has finalized across the selected media formats. Some formats (e.g. Network TV and Cinema) finalize later, so the most recent periods may still change. Turn on for stable, report-ready numbers.";
1166
+ function cx3(...classes) {
1167
+ return classes.filter(Boolean).join(" ");
1168
+ }
1169
+ function DataScopeFilter({ value, onChange, align = "right" }) {
1170
+ const [open, setOpen] = (0, import_react6.useState)(false);
1171
+ const ref = (0, import_react6.useRef)(null);
1172
+ const display = value ? "Final only" : "All";
1173
+ (0, import_react6.useEffect)(() => {
1174
+ if (!open) return;
1175
+ const onDown = (e) => {
1176
+ if (ref.current && !ref.current.contains(e.target)) setOpen(false);
1177
+ };
1178
+ const onKey = (e) => e.key === "Escape" && setOpen(false);
1179
+ window.addEventListener("mousedown", onDown);
1180
+ window.addEventListener("keydown", onKey);
1181
+ return () => {
1182
+ window.removeEventListener("mousedown", onDown);
1183
+ window.removeEventListener("keydown", onKey);
1184
+ };
1185
+ }, [open]);
1186
+ const choose = (v) => {
1187
+ onChange(v);
1188
+ setOpen(false);
1189
+ };
1190
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "relative", ref, children: [
1191
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
1192
+ "button",
1193
+ {
1194
+ type: "button",
1195
+ onClick: () => setOpen((o) => !o),
1196
+ "aria-expanded": open,
1197
+ "aria-haspopup": "listbox",
1198
+ className: "flex h-11 items-center gap-2 rounded-control border border-border-strong bg-input px-2.5 text-text-secondary transition-colors hover:bg-subtle hover:text-text-primary focus-visible:outline-solid focus-visible:outline-2 focus-visible:outline-border-focus",
1199
+ children: [
1200
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("span", { className: "flex flex-col gap-1 text-left leading-none", children: [
1201
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "text-[10px] uppercase tracking-wide text-text-tertiary", children: "Data" }),
1202
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "text-[12px] font-medium text-text-primary", children: display })
1203
+ ] }),
1204
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1205
+ import_lucide_react8.ChevronDown,
1206
+ {
1207
+ size: 14,
1208
+ "aria-hidden": "true",
1209
+ className: cx3("shrink-0 text-text-secondary transition-transform", open && "rotate-180")
1210
+ }
1211
+ )
1212
+ ]
1213
+ }
1214
+ ),
1215
+ open && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
1216
+ "div",
1217
+ {
1218
+ role: "listbox",
1219
+ "aria-label": "Data",
1220
+ className: cx3(
1221
+ "absolute top-[calc(100%+8px)] z-30 min-w-[220px] overflow-hidden rounded-card border border-border-default bg-surface p-1 shadow-overlay",
1222
+ align === "right" ? "right-0" : "left-0"
1223
+ ),
1224
+ children: [
1225
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Option, { label: "All", selected: !value, onClick: () => choose(false) }),
1226
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Option, { label: "Final only", selected: value, onClick: () => choose(true), hint: FINAL_ONLY_TEXT })
1227
+ ]
1228
+ }
1229
+ )
1230
+ ] });
1231
+ }
1232
+ function Option({
1233
+ label,
1234
+ selected,
1235
+ onClick,
1236
+ hint
1237
+ }) {
1238
+ const option = /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
1239
+ "button",
1240
+ {
1241
+ type: "button",
1242
+ role: "option",
1243
+ "aria-selected": selected,
1244
+ onClick,
1245
+ className: cx3(
1246
+ "flex items-center gap-2 rounded-control px-2.5 py-2 text-left text-[13px] transition-colors hover:bg-subtle",
1247
+ hint ? "flex-1" : "w-full",
1248
+ selected ? "font-semibold text-text-primary" : "text-text-secondary"
1249
+ ),
1250
+ children: [
1251
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_lucide_react8.Check, { size: 14, "aria-hidden": "true", className: cx3("shrink-0 text-brand-primaryAction", !selected && "invisible") }),
1252
+ label
1253
+ ]
1254
+ }
1255
+ );
1256
+ if (!hint) return option;
1257
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex items-center pr-2.5", children: [
1258
+ option,
1259
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(InfoTip, { text: hint, trigger: "hover", label: `About ${label}` })
1260
+ ] });
1261
+ }
1262
+
1263
+ // src/SelectFilter.tsx
1264
+ var import_react7 = require("react");
1265
+ var import_react_dom2 = require("react-dom");
1266
+ var import_lucide_react9 = require("lucide-react");
1267
+ var import_jsx_runtime9 = require("react/jsx-runtime");
1268
+ function cx4(...classes) {
1269
+ return classes.filter(Boolean).join(" ");
1270
+ }
1271
+ function slug3(value) {
1272
+ return value.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/(^-|-$)/g, "");
1273
+ }
1274
+ function SelectFilter({
1275
+ caption,
1276
+ value,
1277
+ options,
1278
+ onChange,
1279
+ align = "left",
1280
+ fullWidth = false,
1281
+ className,
1282
+ testIdPrefix
1283
+ }) {
1284
+ const [open, setOpen] = (0, import_react7.useState)(false);
1285
+ const [pos, setPos] = (0, import_react7.useState)(null);
1286
+ const ref = (0, import_react7.useRef)(null);
1287
+ const popoverRef = (0, import_react7.useRef)(null);
1288
+ const close = (0, import_react7.useCallback)(() => setOpen(false), []);
1289
+ const reposition = (0, import_react7.useCallback)(() => {
1290
+ if (!ref.current) return;
1291
+ const r = ref.current.getBoundingClientRect();
1292
+ setPos({ top: r.bottom + 8, left: align === "right" ? r.right - r.width : r.left, width: r.width });
1293
+ }, [align]);
1294
+ (0, import_react7.useLayoutEffect)(() => {
1295
+ if (!open) return;
1296
+ reposition();
1297
+ }, [open, reposition]);
1298
+ (0, import_react7.useEffect)(() => {
1299
+ if (!open) return;
1300
+ const onKey = (e) => e.key === "Escape" && close();
1301
+ const onClick = (e) => {
1302
+ const t = e.target;
1303
+ if (ref.current?.contains(t) || popoverRef.current?.contains(t)) return;
1304
+ close();
1305
+ };
1306
+ window.addEventListener("keydown", onKey);
1307
+ window.addEventListener("mousedown", onClick);
1308
+ window.addEventListener("resize", reposition);
1309
+ window.addEventListener("scroll", reposition, true);
1310
+ return () => {
1311
+ window.removeEventListener("keydown", onKey);
1312
+ window.removeEventListener("mousedown", onClick);
1313
+ window.removeEventListener("resize", reposition);
1314
+ window.removeEventListener("scroll", reposition, true);
1315
+ };
1316
+ }, [open, close, reposition]);
1317
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: cx4("relative", fullWidth && "w-full"), ref, children: [
1318
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
1319
+ "button",
1320
+ {
1321
+ type: "button",
1322
+ "data-testid": testIdPrefix && `${testIdPrefix}-trigger`,
1323
+ onClick: () => setOpen((o) => !o),
1324
+ "aria-expanded": open,
1325
+ "aria-haspopup": "listbox",
1326
+ className: cx4(
1327
+ "flex h-11 items-center gap-2 rounded-control border border-border-strong bg-input px-2.5 text-text-secondary transition-colors hover:bg-subtle hover:text-text-primary focus-visible:outline-solid focus-visible:outline-2 focus-visible:outline-border-focus",
1328
+ fullWidth && "w-full justify-between",
1329
+ className
1330
+ ),
1331
+ children: [
1332
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("span", { className: "flex flex-col gap-1 text-left leading-none", children: [
1333
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "text-[10px] uppercase tracking-wide text-text-tertiary", children: caption }),
1334
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "text-[12px] font-medium text-text-primary", children: value })
1335
+ ] }),
1336
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1337
+ import_lucide_react9.ChevronDown,
1338
+ {
1339
+ size: 14,
1340
+ "aria-hidden": "true",
1341
+ className: cx4("shrink-0 text-text-secondary transition-transform", open && "rotate-180")
1342
+ }
1343
+ )
1344
+ ]
1345
+ }
1346
+ ),
1347
+ open && pos && (0, import_react_dom2.createPortal)(
1348
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1349
+ "div",
1350
+ {
1351
+ ref: popoverRef,
1352
+ "data-testid": testIdPrefix && `${testIdPrefix}-popover`,
1353
+ style: { position: "fixed", top: pos.top, left: pos.left, width: fullWidth ? pos.width : void 0 },
1354
+ className: cx4(
1355
+ "z-[65] flex flex-col overflow-hidden rounded-card border border-border-default bg-surface shadow-overlay",
1356
+ !fullWidth && "w-56"
1357
+ ),
1358
+ role: "listbox",
1359
+ "aria-label": caption,
1360
+ children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "max-h-[280px] overflow-y-auto p-1", children: options.map((o) => {
1361
+ const selected = o === value;
1362
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
1363
+ "button",
1364
+ {
1365
+ type: "button",
1366
+ role: "option",
1367
+ "aria-selected": selected,
1368
+ "data-testid": testIdPrefix && `${testIdPrefix}-option-${slug3(o)}`,
1369
+ onClick: () => {
1370
+ onChange(o);
1371
+ close();
1372
+ },
1373
+ className: "flex w-full items-center gap-2.5 rounded-control px-2 py-2 text-left transition-colors hover:bg-subtle",
1374
+ children: [
1375
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "flex h-4 w-4 shrink-0 items-center justify-center text-brand-primaryAction", children: selected && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_lucide_react9.Check, { size: 15, strokeWidth: 3 }) }),
1376
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1377
+ "span",
1378
+ {
1379
+ className: cx4("flex-1 text-[14px]", selected ? "font-semibold text-text-primary" : "text-text-primary"),
1380
+ children: o
1381
+ }
1382
+ )
1383
+ ]
1384
+ },
1385
+ o
1386
+ );
1387
+ }) })
1388
+ }
1389
+ ),
1390
+ document.body
1391
+ )
1392
+ ] });
1393
+ }
1394
+
1395
+ // src/Card.tsx
1396
+ var import_react8 = require("react");
1397
+ var import_jsx_runtime10 = require("react/jsx-runtime");
1398
+ var Card = (0, import_react8.forwardRef)(function Card2({ staticCard, title, subtitle, action, className, children, ...rest }, ref) {
1399
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1400
+ "article",
1401
+ {
1402
+ ref,
1403
+ className: [
1404
+ "flex h-full flex-col overflow-hidden rounded-card border border-border-default bg-surface shadow-raised",
1405
+ // Interactive hover: deeper drop shadow only -- no elevation/transform.
1406
+ !staticCard && "group transition-shadow hover:shadow-overlay",
1407
+ className
1408
+ ].filter(Boolean).join(" "),
1409
+ ...rest,
1410
+ children: title ? /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex flex-1 flex-col p-card-padding", children: [
1411
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex items-start justify-between gap-2", children: [
1412
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { children: [
1413
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("h3", { className: "text-[15px] font-semibold leading-snug text-text-primary", children: title }),
1414
+ subtitle && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { className: "mt-0.5 text-[12px] leading-snug text-text-tertiary", children: subtitle })
1415
+ ] }),
1416
+ action && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "shrink-0 text-text-tertiary", children: action })
1417
+ ] }),
1418
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "mt-4 flex flex-1 flex-col", children })
1419
+ ] }) : children
1420
+ }
1421
+ );
1422
+ });
1423
+
1424
+ // src/NoResultsEmptyState.tsx
1425
+ var import_lucide_react10 = require("lucide-react");
1426
+ var import_jsx_runtime11 = require("react/jsx-runtime");
1427
+ var NO_RESULTS_TITLE = "No results match your filters";
1428
+ var NO_RESULTS_BODY = "Your filters may be too narrow, conflict with each other, or have no activity in this period.";
1429
+ function noResultsBody(workspaceName) {
1430
+ return workspaceName ? `Nothing in the \u201C${workspaceName}\u201D workspace matches. ${NO_RESULTS_BODY}` : NO_RESULTS_BODY;
1431
+ }
1432
+ var PRIMARY_ACTION_CLASS = "rounded-control bg-brand-primaryAction px-3.5 py-2 text-[13px] font-semibold text-white transition-colors hover:opacity-90";
1433
+ var QUIET_ACTION_CLASS = "rounded-control text-[13px] font-medium text-text-link transition-colors hover:underline focus-visible:outline-solid focus-visible:outline-2 focus-visible:outline-border-focus";
1434
+ function NoResultsEmptyState({
1435
+ title = NO_RESULTS_TITLE,
1436
+ body,
1437
+ workspaceName,
1438
+ hasFilters,
1439
+ onClearFilters,
1440
+ onEditFilters,
1441
+ actions,
1442
+ testIdPrefix
1443
+ }) {
1444
+ const bodyText = body ?? noResultsBody(workspaceName);
1445
+ const showClear = hasFilters !== false && Boolean(onClearFilters);
1446
+ const showEdit = Boolean(onEditFilters);
1447
+ const hasActions = showClear || showEdit || Boolean(actions && actions.length > 0);
1448
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "flex flex-col items-center justify-center gap-3 text-center", "data-testid": testIdPrefix, children: [
1449
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "flex h-11 w-11 items-center justify-center rounded-full bg-subtle text-text-tertiary", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_lucide_react10.FilterX, { size: 20, "aria-hidden": "true" }) }),
1450
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("h2", { className: "text-base font-semibold text-text-primary", children: title }),
1451
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { className: "max-w-[420px] text-[13px] leading-relaxed text-text-secondary", children: bodyText }),
1452
+ hasActions && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "mt-1 flex flex-col items-center gap-3", children: [
1453
+ showClear && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1454
+ "button",
1455
+ {
1456
+ type: "button",
1457
+ onClick: onClearFilters,
1458
+ "data-testid": testIdPrefix && `${testIdPrefix}-clear`,
1459
+ className: PRIMARY_ACTION_CLASS,
1460
+ children: "Clear all filters"
1461
+ }
1462
+ ),
1463
+ showEdit && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1464
+ "button",
1465
+ {
1466
+ type: "button",
1467
+ onClick: onEditFilters,
1468
+ "data-testid": testIdPrefix && `${testIdPrefix}-edit`,
1469
+ className: QUIET_ACTION_CLASS,
1470
+ children: "Edit filters"
1471
+ }
1472
+ ),
1473
+ actions?.map((action, i) => /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1474
+ "button",
1475
+ {
1476
+ type: "button",
1477
+ onClick: action.onClick,
1478
+ className: action.primary ? PRIMARY_ACTION_CLASS : QUIET_ACTION_CLASS,
1479
+ children: action.label
1480
+ },
1481
+ `${i}-${action.label}`
1482
+ ))
1483
+ ] })
1484
+ ] });
1485
+ }
1486
+
1487
+ // src/WorkspaceSelectedCard.tsx
1488
+ var import_react9 = require("react");
1489
+ var import_lucide_react11 = require("lucide-react");
1490
+ var import_jsx_runtime12 = require("react/jsx-runtime");
1491
+ function cx5(...parts) {
1492
+ return parts.filter(Boolean).join(" ");
1493
+ }
1494
+ function WorkspaceSelectedCard({ groups, testIdPrefix }) {
1495
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("aside", { className: "sticky top-4 flex min-w-0 flex-col gap-4 self-start rounded-card border border-border bg-surface px-5 pb-5 pt-4 shadow-raised sm:px-6 sm:pb-6", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "flex flex-col divide-y divide-border", children: groups.map((g) => /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(SummaryGroupItem, { group: g, testIdPrefix }, g.id)) }) });
1496
+ }
1497
+ function SummaryGroupItem({
1498
+ group,
1499
+ nested = false,
1500
+ testIdPrefix
1501
+ }) {
1502
+ const ownCount = group.items.length;
1503
+ const subCount = (group.subGroups ?? []).reduce((n, sg) => n + sg.items.length, 0);
1504
+ const expandCount = ownCount + subCount;
1505
+ const [open, setOpen] = (0, import_react9.useState)(expandCount > 0);
1506
+ const wasEmpty = (0, import_react9.useRef)(expandCount === 0);
1507
+ (0, import_react9.useEffect)(() => {
1508
+ if (wasEmpty.current && expandCount > 0) setOpen(true);
1509
+ else if (expandCount === 0) setOpen(false);
1510
+ wasEmpty.current = expandCount === 0;
1511
+ }, [expandCount]);
1512
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: nested ? "" : "py-3 first:pt-0 last:pb-0", children: [
1513
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
1514
+ "button",
1515
+ {
1516
+ type: "button",
1517
+ onClick: () => setOpen((v) => !v),
1518
+ "aria-expanded": open,
1519
+ "data-testid": testIdPrefix && `${testIdPrefix}-group-${group.id}`,
1520
+ className: "flex w-full min-w-0 items-center gap-2 text-left",
1521
+ children: [
1522
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1523
+ import_lucide_react11.ChevronDown,
1524
+ {
1525
+ size: 14,
1526
+ "aria-hidden": "true",
1527
+ className: cx5("shrink-0 text-text-secondary transition-transform", !open && "-rotate-90")
1528
+ }
1529
+ ),
1530
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: cx5("truncate font-semibold text-text-primary", nested ? "text-[12px]" : "text-[13px]"), children: group.label }),
1531
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "shrink-0 rounded-full bg-subtle px-1.5 py-0.5 text-[11px] font-semibold text-text-secondary", children: ownCount })
1532
+ ]
1533
+ }
1534
+ ),
1535
+ open && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "pt-2.5", children: ownCount === 0 && subCount === 0 ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { className: "text-[12px] text-text-secondary", children: "Nothing selected." }) : /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "flex flex-col gap-2.5", children: [
1536
+ ownCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "flex flex-col gap-1.5", children: [
1537
+ group.items.map((item) => /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
1538
+ "div",
1539
+ {
1540
+ className: "flex items-center justify-between gap-2 rounded-control border border-border bg-canvas px-2 py-1",
1541
+ children: [
1542
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("span", { className: "min-w-0 flex-1", children: [
1543
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "block truncate text-[12px] text-text-primary", children: item.name }),
1544
+ item.meta && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "block truncate text-[11px] text-text-secondary", children: item.meta })
1545
+ ] }),
1546
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1547
+ "button",
1548
+ {
1549
+ type: "button",
1550
+ "aria-label": `Remove ${item.name}`,
1551
+ onClick: () => group.onRemove(item.id),
1552
+ className: "flex h-5 w-5 shrink-0 items-center justify-center rounded-control text-text-secondary hover:bg-subtle hover:text-text-danger",
1553
+ children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_lucide_react11.X, { size: 12, "aria-hidden": "true" })
1554
+ }
1555
+ )
1556
+ ]
1557
+ },
1558
+ item.id
1559
+ )),
1560
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1561
+ "button",
1562
+ {
1563
+ type: "button",
1564
+ onClick: group.onRemoveAll,
1565
+ "data-testid": testIdPrefix && `${testIdPrefix}-remove-all-${group.id}`,
1566
+ className: "mt-0.5 self-start text-[11px] font-medium text-text-link hover:underline",
1567
+ children: "Remove all"
1568
+ }
1569
+ )
1570
+ ] }),
1571
+ group.subGroups?.map((sg) => /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(SummaryGroupItem, { group: sg, nested: true, testIdPrefix }, sg.id))
1572
+ ] }) })
1573
+ ] });
1574
+ }
1575
+
1576
+ // src/chart-placeholder-shared.tsx
1577
+ var import_jsx_runtime13 = require("react/jsx-runtime");
1578
+ var GRID_ROWS = [0, 25, 50, 75, 100];
1579
+ var Y_TICK_WIDTHS = [26, 30, 30, 26, 20];
1580
+ var Y_AXIS_W = 48;
1581
+ var X_AXIS_H = 22;
1582
+ var LEGEND_WIDTHS = [58, 44, 66, 50, 38, 62, 46, 54];
1583
+ function AxisFrame({
1584
+ height,
1585
+ xTicks,
1586
+ children
1587
+ }) {
1588
+ const gutter = xTicks > 0 ? X_AXIS_H : 0;
1589
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "w-full", style: { height }, role: "presentation", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "relative h-full w-full", children: [
1590
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "absolute inset-y-0 right-0", style: { left: Y_AXIS_W, bottom: gutter }, children: [
1591
+ GRID_ROWS.map((pct) => /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
1592
+ "div",
1593
+ {
1594
+ className: "absolute inset-x-0 border-t border-dashed",
1595
+ style: { top: `${pct}%`, borderColor: "var(--color-border-default)" }
1596
+ },
1597
+ pct
1598
+ )),
1599
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "absolute inset-0", children })
1600
+ ] }),
1601
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "absolute left-0 top-0", style: { width: Y_AXIS_W, bottom: gutter }, children: GRID_ROWS.map((pct, i) => /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
1602
+ "div",
1603
+ {
1604
+ className: "absolute right-2 h-2 -translate-y-1/2 rounded-xs bg-subtle",
1605
+ style: { top: `${pct}%`, width: Y_TICK_WIDTHS[i] }
1606
+ },
1607
+ pct
1608
+ )) }),
1609
+ xTicks > 0 && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
1610
+ "div",
1611
+ {
1612
+ className: "absolute bottom-0 right-0 flex items-center",
1613
+ style: { left: Y_AXIS_W, height: X_AXIS_H },
1614
+ children: Array.from({ length: xTicks }, (_, i) => /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "flex flex-1 justify-center", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "h-2 w-full max-w-[26px] rounded-xs bg-subtle" }) }, i))
1615
+ }
1616
+ )
1617
+ ] }) });
1618
+ }
1619
+ function BlankLegend({ rows, reverse = false }) {
1620
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
1621
+ "ul",
1622
+ {
1623
+ className: `flex flex-1 justify-center gap-1.5 ${reverse ? "flex-col-reverse" : "flex-col"}`,
1624
+ role: "presentation",
1625
+ "aria-hidden": "true",
1626
+ children: Array.from({ length: rows }, (_, i) => /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("li", { className: "flex items-center justify-between gap-2", children: [
1627
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("span", { className: "flex items-center gap-1.5", children: [
1628
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "h-2.5 w-2.5 rounded-xs bg-subtle" }),
1629
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
1630
+ "span",
1631
+ {
1632
+ className: "h-2 rounded-xs bg-subtle",
1633
+ style: { width: LEGEND_WIDTHS[i % LEGEND_WIDTHS.length] }
1634
+ }
1635
+ )
1636
+ ] }),
1637
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "h-2 w-6 rounded-xs bg-subtle" })
1638
+ ] }, i))
1639
+ }
1640
+ );
1641
+ }
1642
+
1643
+ // src/BarChartPlaceholder.tsx
1644
+ var import_jsx_runtime14 = require("react/jsx-runtime");
1645
+ var BAR_PROFILE = [92, 86, 76, 65, 57, 40, 30, 24, 18, 13];
1646
+ function BarChartPlaceholder({ height = 220 }) {
1647
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(AxisFrame, { height, xTicks: BAR_PROFILE.length, children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "flex h-full w-full items-end gap-2", children: BAR_PROFILE.map((pct, i) => /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "flex-1 rounded-t-[3px] bg-subtle", style: { height: `${pct}%` } }, i)) }) });
1648
+ }
1649
+
1650
+ // src/LineChartPlaceholder.tsx
1651
+ var import_jsx_runtime15 = require("react/jsx-runtime");
1652
+ var LINE_SERIES = [
1653
+ [58, 52, 55, 41, 45, 33, 36, 25, 28, 17],
1654
+ [74, 71, 66, 68, 59, 61, 54, 56, 48, 45],
1655
+ [40, 44, 37, 41, 34, 38, 31, 34, 27, 26],
1656
+ [86, 82, 84, 77, 79, 71, 73, 66, 68, 61]
1657
+ ];
1658
+ var LINE_FORECAST_FROM = 7;
1659
+ function LineChartPlaceholder({ height = 280 }) {
1660
+ const toPath = (pts, from, to) => pts.slice(from, to).map((y, i) => `${(i + from) / (pts.length - 1) * 100},${y}`).join(" ");
1661
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(AxisFrame, { height, xTicks: LINE_SERIES[0].length, children: [
1662
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("svg", { viewBox: "0 0 100 100", preserveAspectRatio: "none", className: "absolute inset-0 h-full w-full", children: LINE_SERIES.map((pts, s) => /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("g", { children: [
1663
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
1664
+ "polyline",
1665
+ {
1666
+ points: toPath(pts, 0, LINE_FORECAST_FROM + 1),
1667
+ fill: "none",
1668
+ stroke: "var(--color-border-strong)",
1669
+ strokeWidth: "2",
1670
+ vectorEffect: "non-scaling-stroke",
1671
+ strokeLinecap: "round",
1672
+ strokeLinejoin: "round"
1673
+ }
1674
+ ),
1675
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
1676
+ "polyline",
1677
+ {
1678
+ points: toPath(pts, LINE_FORECAST_FROM, pts.length),
1679
+ fill: "none",
1680
+ stroke: "var(--color-border-strong)",
1681
+ strokeWidth: "2",
1682
+ strokeDasharray: "5 4",
1683
+ vectorEffect: "non-scaling-stroke",
1684
+ strokeLinecap: "round",
1685
+ strokeLinejoin: "round"
1686
+ }
1687
+ )
1688
+ ] }, s)) }),
1689
+ LINE_SERIES.map(
1690
+ (pts, s) => pts.map((y, i) => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
1691
+ "div",
1692
+ {
1693
+ className: "absolute h-1.5 w-1.5 -translate-x-1/2 -translate-y-1/2 rounded-full",
1694
+ style: {
1695
+ left: `${i / (pts.length - 1) * 100}%`,
1696
+ top: `${y}%`,
1697
+ backgroundColor: "var(--color-border-strong)"
1698
+ }
1699
+ },
1700
+ `${s}-${i}`
1701
+ ))
1702
+ )
1703
+ ] });
1704
+ }
1705
+
1706
+ // src/PieChartPlaceholder.tsx
1707
+ var import_jsx_runtime16 = require("react/jsx-runtime");
1708
+ var PIE_SEAMS = [0, 115, 180, 230, 262, 288, 310, 332];
1709
+ function PieChartPlaceholder({ height = 220, legendRows = 8 }) {
1710
+ const r = 45;
1711
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "flex w-full items-center gap-3", style: { height }, role: "presentation", "aria-hidden": "true", children: [
1712
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: "flex h-full items-center justify-center", style: { width: "60%" }, children: /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("svg", { viewBox: "0 0 100 100", className: "h-full", style: { aspectRatio: "1 / 1" }, children: [
1713
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("circle", { cx: "50", cy: "50", r, fill: "var(--color-subtle)" }),
1714
+ PIE_SEAMS.map((deg) => {
1715
+ const rad = (deg - 90) * Math.PI / 180;
1716
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
1717
+ "line",
1718
+ {
1719
+ x1: "50",
1720
+ y1: "50",
1721
+ x2: 50 + r * Math.cos(rad),
1722
+ y2: 50 + r * Math.sin(rad),
1723
+ stroke: "var(--color-border-default)",
1724
+ strokeWidth: "1"
1725
+ },
1726
+ deg
1727
+ );
1728
+ })
1729
+ ] }) }),
1730
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(BlankLegend, { rows: legendRows })
1731
+ ] });
1732
+ }
1733
+
1734
+ // src/StackedColumnPlaceholder.tsx
1735
+ var import_jsx_runtime17 = require("react/jsx-runtime");
1736
+ var COLUMN_WEIGHTS = [20, 30, 15, 35, 24, 12, 28, 18];
1737
+ function columnBands(count) {
1738
+ const weights = Array.from({ length: count }, (_, i) => COLUMN_WEIGHTS[i % COLUMN_WEIGHTS.length]);
1739
+ const total = weights.reduce((a, b) => a + b, 0);
1740
+ return weights.map((w) => w / total * 100);
1741
+ }
1742
+ function StackedColumnPlaceholder({
1743
+ height = 220,
1744
+ width = 96,
1745
+ legendRows = 4,
1746
+ axis = false
1747
+ }) {
1748
+ const column = /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "flex h-full flex-col overflow-hidden rounded-control bg-subtle", style: { width }, children: columnBands(legendRows).map((pct, i) => /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
1749
+ "div",
1750
+ {
1751
+ style: {
1752
+ height: `${pct}%`,
1753
+ /* border-strong, not border-default: on a subtle fill the softer
1754
+ seam disappears and the stack reads as one undivided block. */
1755
+ borderTop: i === 0 ? void 0 : "1px solid var(--color-border-strong)"
1756
+ }
1757
+ },
1758
+ i
1759
+ )) });
1760
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "flex w-full gap-4", style: { height }, role: "presentation", "aria-hidden": "true", children: [
1761
+ axis ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { style: { width: "55%" }, children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(AxisFrame, { height, xTicks: 0, children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "flex h-full items-stretch justify-center", children: column }) }) }) : /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "flex items-stretch py-1", children: column }),
1762
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(BlankLegend, { rows: legendRows, reverse: !axis })
1763
+ ] });
1764
+ }
1765
+
1766
+ // src/Tooltip.tsx
1767
+ var import_react10 = require("react");
1768
+ var import_react_dom3 = require("react-dom");
1769
+ var import_jsx_runtime18 = require("react/jsx-runtime");
1770
+ function cx6(...parts) {
1771
+ return parts.filter(Boolean).join(" ");
1772
+ }
1773
+ function Tooltip({
1774
+ label,
1775
+ children,
1776
+ anchorRef,
1777
+ className,
1778
+ whenTruncated = false,
1779
+ interactive = false,
1780
+ placement = "top",
1781
+ align = "center",
1782
+ delay = 0,
1783
+ variant = "surface",
1784
+ testIdPrefix
1785
+ }) {
1786
+ const [anchor, setAnchor] = (0, import_react10.useState)(null);
1787
+ const [boxWidth, setBoxWidth] = (0, import_react10.useState)(null);
1788
+ const ref = (0, import_react10.useRef)(null);
1789
+ const boxRef = (0, import_react10.useRef)(null);
1790
+ const hideTimer = (0, import_react10.useRef)(null);
1791
+ const showTimer = (0, import_react10.useRef)(null);
1792
+ const id = (0, import_react10.useId)();
1793
+ const clearHideTimer = () => {
1794
+ if (hideTimer.current) {
1795
+ clearTimeout(hideTimer.current);
1796
+ hideTimer.current = null;
1797
+ }
1798
+ };
1799
+ const clearShowTimer = () => {
1800
+ if (showTimer.current) {
1801
+ clearTimeout(showTimer.current);
1802
+ showTimer.current = null;
1803
+ }
1804
+ };
1805
+ (0, import_react10.useEffect)(
1806
+ () => () => {
1807
+ clearHideTimer();
1808
+ clearShowTimer();
1809
+ },
1810
+ []
1811
+ );
1812
+ const reveal = () => {
1813
+ const el = anchorRef ? anchorRef.current : ref.current;
1814
+ if (!el) return;
1815
+ if (whenTruncated && el.scrollWidth <= el.clientWidth) return;
1816
+ const r = el.getBoundingClientRect();
1817
+ setBoxWidth(null);
1818
+ setAnchor({ top: r.top, left: r.left, width: r.width, height: r.height, bottom: r.bottom, right: r.right });
1819
+ if (anchorRef) el.setAttribute("aria-describedby", id);
1820
+ };
1821
+ const show = () => {
1822
+ clearHideTimer();
1823
+ if (delay > 0) {
1824
+ showTimer.current = setTimeout(reveal, delay);
1825
+ } else {
1826
+ reveal();
1827
+ }
1828
+ };
1829
+ const hideNow = () => {
1830
+ clearShowTimer();
1831
+ clearHideTimer();
1832
+ setAnchor(null);
1833
+ setBoxWidth(null);
1834
+ if (anchorRef?.current) anchorRef.current.removeAttribute("aria-describedby");
1835
+ };
1836
+ const hide = () => {
1837
+ clearShowTimer();
1838
+ if (!interactive) {
1839
+ hideNow();
1840
+ return;
1841
+ }
1842
+ clearHideTimer();
1843
+ hideTimer.current = setTimeout(hideNow, 120);
1844
+ };
1845
+ (0, import_react10.useEffect)(() => {
1846
+ if (!anchorRef) return;
1847
+ const el = anchorRef.current;
1848
+ if (!el) return;
1849
+ el.addEventListener("mouseenter", show);
1850
+ el.addEventListener("mouseleave", hide);
1851
+ el.addEventListener("focus", show);
1852
+ el.addEventListener("blur", hide);
1853
+ return () => {
1854
+ el.removeEventListener("mouseenter", show);
1855
+ el.removeEventListener("mouseleave", hide);
1856
+ el.removeEventListener("focus", show);
1857
+ el.removeEventListener("blur", hide);
1858
+ };
1859
+ }, [anchorRef, placement, delay]);
1860
+ (0, import_react10.useLayoutEffect)(() => {
1861
+ if (!anchor) return;
1862
+ const el = boxRef.current;
1863
+ if (!el) return;
1864
+ setBoxWidth(el.getBoundingClientRect().width);
1865
+ }, [anchor, label]);
1866
+ const margin = 8;
1867
+ let style = { top: 0, left: 0 };
1868
+ let translate = "";
1869
+ if (anchor) {
1870
+ if (placement === "right") {
1871
+ style = { top: anchor.top + anchor.height / 2, left: anchor.right + 8 };
1872
+ translate = "-translate-y-1/2";
1873
+ } else if (placement === "bottom-end") {
1874
+ style = { top: anchor.bottom + 8, right: window.innerWidth - anchor.right };
1875
+ } else {
1876
+ const w = boxWidth ?? anchor.width;
1877
+ const desired = align === "start" ? anchor.left : anchor.left + anchor.width / 2 - w / 2;
1878
+ const left = Math.min(Math.max(desired, margin), Math.max(margin, window.innerWidth - w - margin));
1879
+ style = { left, top: placement === "bottom" ? anchor.bottom : anchor.top };
1880
+ translate = placement === "bottom" ? "translate-y-2" : "-translate-y-[calc(100%+8px)]";
1881
+ }
1882
+ }
1883
+ const floating = anchor != null && typeof document !== "undefined" ? (0, import_react_dom3.createPortal)(
1884
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
1885
+ "span",
1886
+ {
1887
+ ref: boxRef,
1888
+ role: "tooltip",
1889
+ id,
1890
+ "data-testid": testIdPrefix && `${testIdPrefix}-tooltip`,
1891
+ style: { top: style.top, left: style.left, right: style.right, visibility: boxWidth == null ? "hidden" : "visible" },
1892
+ onMouseEnter: interactive ? clearHideTimer : void 0,
1893
+ onMouseLeave: interactive ? hide : void 0,
1894
+ className: cx6(
1895
+ "fixed z-[90] w-max max-w-xs whitespace-normal break-words px-2.5 py-1.5 text-xs leading-snug",
1896
+ interactive ? "pointer-events-auto" : "pointer-events-none",
1897
+ variant === "inverted" ? "rounded-control bg-text-primary text-surface" : "rounded-control border border-border-default bg-surface text-text-primary shadow-overlay",
1898
+ translate
1899
+ ),
1900
+ children: label
1901
+ }
1902
+ ),
1903
+ document.body
1904
+ ) : null;
1905
+ if (anchorRef) return floating;
1906
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
1907
+ "span",
1908
+ {
1909
+ ref,
1910
+ className,
1911
+ onMouseEnter: show,
1912
+ onMouseLeave: hide,
1913
+ onFocus: show,
1914
+ onBlur: hide,
1915
+ "aria-describedby": anchor ? id : void 0,
1916
+ children: [
1917
+ children,
1918
+ floating
1919
+ ]
1920
+ }
1921
+ );
1922
+ }
1923
+
1924
+ // src/AdvertiserNoResults.tsx
1925
+ var import_jsx_runtime19 = require("react/jsx-runtime");
1926
+ function AdvertiserNoResults({
1927
+ query,
1928
+ hint = "Try checking your spelling or searching for a broader brand name."
1929
+ }) {
1930
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "flex flex-col items-center gap-1.5 px-4 py-8 text-center", children: [
1931
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("p", { className: "text-[13px] font-medium text-text-primary", children: [
1932
+ "No results found for \u201C",
1933
+ query,
1934
+ "\u201D"
1935
+ ] }),
1936
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("p", { className: "text-[12px] leading-snug text-text-tertiary", children: hint })
1937
+ ] });
1938
+ }
1939
+
1940
+ // src/SelectAllVisible.tsx
1941
+ var import_lucide_react12 = require("lucide-react");
1942
+ var import_jsx_runtime20 = require("react/jsx-runtime");
1943
+ function SelectAllVisible({ count, allSelected, onToggle, testIdPrefix }) {
1944
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
1945
+ "button",
1946
+ {
1947
+ type: "button",
1948
+ "data-testid": testIdPrefix && `${testIdPrefix}-select-all-visible`,
1949
+ onClick: onToggle,
1950
+ className: "flex w-full items-center gap-2 rounded-control px-2 py-1.5 text-left text-[12px] font-semibold text-text-link transition-colors hover:bg-subtle",
1951
+ children: [
1952
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_lucide_react12.ListChecks, { size: 14, "aria-hidden": "true" }),
1953
+ allSelected ? `Deselect ${count} visible` : `Select all ${count} visible`
1954
+ ]
1955
+ }
1956
+ );
1957
+ }
1958
+
1959
+ // src/SelectedSection.tsx
1960
+ var import_lucide_react13 = require("lucide-react");
1961
+ var import_jsx_runtime21 = require("react/jsx-runtime");
1962
+ function cx7(...parts) {
1963
+ return parts.filter(Boolean).join(" ");
1964
+ }
1965
+ function SelectedSection({ count, open, onToggle, children, testIdPrefix }) {
1966
+ if (count === 0) return null;
1967
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "flex flex-col", children: [
1968
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
1969
+ "button",
1970
+ {
1971
+ type: "button",
1972
+ "data-testid": testIdPrefix && `${testIdPrefix}-selected-toggle`,
1973
+ onClick: onToggle,
1974
+ "aria-expanded": open,
1975
+ className: "flex w-full items-center gap-1.5 rounded-control px-2 py-1 text-left text-[11px] font-semibold uppercase tracking-wide text-text-tertiary transition-colors hover:text-text-primary",
1976
+ children: [
1977
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react13.ChevronDown, { size: 13, "aria-hidden": "true", className: cx7("transition-transform", !open && "-rotate-90") }),
1978
+ "Selected (",
1979
+ count,
1980
+ ")"
1981
+ ]
1982
+ }
1983
+ ),
1984
+ open && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: "flex flex-col gap-0.5", children })
1985
+ ] });
1986
+ }
1987
+
1988
+ // src/SelectionPicker.tsx
1989
+ var import_react11 = require("react");
1990
+ var import_react_dom4 = require("react-dom");
1991
+ var import_lucide_react14 = require("lucide-react");
1992
+ var import_jsx_runtime22 = require("react/jsx-runtime");
1993
+ function cx8(...parts) {
1994
+ return parts.filter(Boolean).join(" ");
1995
+ }
1996
+ function slug4(value) {
1997
+ return value.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/(^-|-$)/g, "");
1998
+ }
1999
+ function SelectionPicker({
2000
+ catalog = [],
2001
+ selected,
2002
+ onChange,
2003
+ columns,
2004
+ filters,
2005
+ searchPlaceholder = "Search\u2026",
2006
+ getChildren,
2007
+ helper,
2008
+ showInfoColumn = true,
2009
+ defaultFilters,
2010
+ requireQuery = false,
2011
+ showResultsWithoutSearch = false,
2012
+ accountLists,
2013
+ selectedListIds,
2014
+ onToggleList,
2015
+ lockedIds,
2016
+ results,
2017
+ onQueryChange,
2018
+ loading = false,
2019
+ error = false,
2020
+ errorMessage = "Couldn't load options. Please try again.",
2021
+ hasMore = false,
2022
+ loadingMore = false,
2023
+ onLoadMore,
2024
+ toolbarExtra,
2025
+ testIdPrefix
2026
+ }) {
2027
+ const hasAccountLists = !!accountLists;
2028
+ const [source, setSource] = (0, import_react11.useState)("search");
2029
+ const [query, setQuery] = (0, import_react11.useState)("");
2030
+ const [activeFilters, setActiveFilters] = (0, import_react11.useState)(() => {
2031
+ const init = {};
2032
+ if (defaultFilters) for (const [label, opts] of Object.entries(defaultFilters)) init[label] = new Set(opts);
2033
+ return init;
2034
+ });
2035
+ const [expanded, setExpanded] = (0, import_react11.useState)(/* @__PURE__ */ new Set());
2036
+ const selectedIds = (0, import_react11.useMemo)(() => new Set(selected.map((s) => s.id)), [selected]);
2037
+ const toggleFilter = (label, opt) => {
2038
+ setActiveFilters((prev) => {
2039
+ const next = { ...prev };
2040
+ const set = new Set(next[label] ?? []);
2041
+ set.has(opt) ? set.delete(opt) : set.add(opt);
2042
+ next[label] = set;
2043
+ return next;
2044
+ });
2045
+ };
2046
+ const activeFilterCount = filters.reduce((n, f) => n + (activeFilters[f.label]?.size ?? 0), 0);
2047
+ const hasSearched = showResultsWithoutSearch || query.trim().length > 0 || !requireQuery && activeFilterCount > 0;
2048
+ const filteredCatalog = (0, import_react11.useMemo)(() => {
2049
+ if (!hasSearched || results !== void 0) return [];
2050
+ const q = query.trim().toLowerCase();
2051
+ return catalog.filter((row) => {
2052
+ if (q && !row.name.toLowerCase().includes(q)) return false;
2053
+ for (const f of filters) {
2054
+ const active = activeFilters[f.label];
2055
+ if (active && active.size > 0) {
2056
+ const v = f.value(row);
2057
+ if (!v || !active.has(v)) return false;
2058
+ }
2059
+ }
2060
+ return true;
2061
+ });
2062
+ }, [hasSearched, results, catalog, query, filters, activeFilters]);
2063
+ const rows = results !== void 0 ? results : filteredCatalog;
2064
+ const addRows = (add) => {
2065
+ const toAdd = add.filter((r) => !selectedIds.has(r.id));
2066
+ if (toAdd.length) onChange([...selected, ...toAdd]);
2067
+ };
2068
+ const toggleRow = (row) => {
2069
+ if (selectedIds.has(row.id)) {
2070
+ onChange(selected.filter((s) => s.id !== row.id));
2071
+ } else {
2072
+ addRows([row]);
2073
+ }
2074
+ };
2075
+ const toggleable = lockedIds ? rows.filter((r) => !lockedIds.has(r.id)) : rows;
2076
+ const allSelected = toggleable.length > 0 && toggleable.every((r) => selectedIds.has(r.id));
2077
+ const someSelected = toggleable.some((r) => selectedIds.has(r.id));
2078
+ const toggleAll = () => {
2079
+ if (allSelected) {
2080
+ const rowIds = new Set(toggleable.map((r) => r.id));
2081
+ onChange(selected.filter((s) => !rowIds.has(s.id)));
2082
+ } else {
2083
+ addRows(toggleable);
2084
+ }
2085
+ };
2086
+ const rowGridCols = showInfoColumn ? "grid-cols-[36px_1fr_44px]" : "grid-cols-[36px_1fr]";
2087
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className: "flex flex-col gap-4", children: [
2088
+ helper && /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(import_jsx_runtime22.Fragment, { children: [
2089
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("p", { className: "text-[13px] text-text-secondary", children: helper }),
2090
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("hr", { className: "-mx-5 -mt-2 border-border-default sm:-mx-6" })
2091
+ ] }),
2092
+ hasAccountLists && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: "inline-flex gap-0.5 self-start rounded-control border border-border-default bg-canvas p-0.5", children: [
2093
+ { id: "search", label: "Search", icon: import_lucide_react14.Search },
2094
+ { id: "lists", label: "Account lists", icon: import_lucide_react14.List }
2095
+ ].map((t) => {
2096
+ const on = source === t.id;
2097
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
2098
+ "button",
2099
+ {
2100
+ type: "button",
2101
+ onClick: () => setSource(t.id),
2102
+ "data-testid": testIdPrefix && `${testIdPrefix}-source-${t.id}`,
2103
+ className: cx8(
2104
+ "inline-flex items-center gap-1.5 rounded-[6px] px-3 py-1.5 text-[13px] font-medium transition-colors",
2105
+ on ? "bg-surface text-text-primary shadow-raised" : "text-text-secondary hover:text-text-primary"
2106
+ ),
2107
+ children: [
2108
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(t.icon, { size: 14, "aria-hidden": "true" }),
2109
+ t.label
2110
+ ]
2111
+ },
2112
+ t.id
2113
+ );
2114
+ }) }),
2115
+ source === "search" && /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className: "flex min-w-0 flex-col gap-3", children: [
2116
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className: "flex flex-wrap items-center gap-2", children: [
2117
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className: "flex h-9 min-w-[200px] flex-1 items-center gap-2 rounded-control border border-border-default bg-input px-3", children: [
2118
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_lucide_react14.Search, { size: 15, className: "text-text-secondary", "aria-hidden": "true" }),
2119
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
2120
+ "input",
2121
+ {
2122
+ value: query,
2123
+ onChange: (e) => {
2124
+ const next = e.target.value;
2125
+ setQuery(next);
2126
+ onQueryChange?.(next);
2127
+ },
2128
+ placeholder: searchPlaceholder,
2129
+ "aria-label": searchPlaceholder,
2130
+ "data-testid": testIdPrefix && `${testIdPrefix}-search`,
2131
+ className: "h-full flex-1 bg-transparent text-sm outline-hidden placeholder:text-text-tertiary"
2132
+ }
2133
+ )
2134
+ ] }),
2135
+ filters.map((f) => /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
2136
+ FilterDropdown,
2137
+ {
2138
+ filter: f,
2139
+ active: activeFilters[f.label] ?? /* @__PURE__ */ new Set(),
2140
+ onToggle: (opt) => toggleFilter(f.label, opt),
2141
+ testIdPrefix
2142
+ },
2143
+ f.label
2144
+ )),
2145
+ toolbarExtra
2146
+ ] }),
2147
+ !hasSearched ? /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className: "flex flex-col items-center gap-3 rounded-card border border-dashed border-border-default py-10 text-center", children: [
2148
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: "flex h-12 w-12 items-center justify-center rounded-full bg-subtle", children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_lucide_react14.Search, { size: 20, className: "text-text-tertiary", "aria-hidden": "true" }) }),
2149
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className: "flex flex-col gap-1", children: [
2150
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("p", { className: "text-[14px] font-semibold text-text-primary", children: "Search to get started" }),
2151
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("p", { className: "text-[13px] text-text-tertiary", children: "Use the search or filters above to find and add options." })
2152
+ ] })
2153
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className: "overflow-hidden rounded-control border border-border-default", children: [
2154
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
2155
+ "div",
2156
+ {
2157
+ className: cx8(
2158
+ "grid items-center gap-2 border-b border-border-default bg-canvas px-3 py-2 text-[11px] font-semibold uppercase tracking-[0.06em] text-text-secondary",
2159
+ rowGridCols
2160
+ ),
2161
+ children: [
2162
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
2163
+ CheckBox,
2164
+ {
2165
+ checked: allSelected,
2166
+ indeterminate: !allSelected && someSelected,
2167
+ disabled: toggleable.length === 0,
2168
+ onChange: toggleAll,
2169
+ ariaLabel: "Select all",
2170
+ testId: testIdPrefix && `${testIdPrefix}-select-all`
2171
+ }
2172
+ ),
2173
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: cx8("grid gap-2", gridForColumns(columns)), children: columns.map((c) => /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { className: c.className, children: c.header }, c.key)) }),
2174
+ showInfoColumn && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { className: "text-right", children: "Info" })
2175
+ ]
2176
+ }
2177
+ ),
2178
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: "max-h-[360px] overflow-y-auto", children: loading ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: "flex items-center justify-center py-6", role: "status", "aria-label": "Loading", children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_lucide_react14.Loader2, { size: 18, className: "animate-spin text-text-tertiary", "aria-hidden": "true" }) }) : error ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("p", { className: "px-3 py-8 text-center text-[13px] text-text-tertiary", children: errorMessage }) : rows.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: "px-3 py-8 text-center text-[13px] text-text-tertiary", children: "No matches. Try a different search or filter." }) : rows.map((row) => {
2179
+ const isSel = selectedIds.has(row.id);
2180
+ const isLocked = lockedIds?.has(row.id) ?? false;
2181
+ const kids = getChildren?.(row);
2182
+ const isOpen = expanded.has(row.id);
2183
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className: "border-t border-border-default/60 first:border-t-0", children: [
2184
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
2185
+ "div",
2186
+ {
2187
+ onClick: isLocked ? void 0 : () => toggleRow(row),
2188
+ "aria-disabled": isLocked || void 0,
2189
+ className: cx8(
2190
+ "grid items-center gap-2 px-3 py-2.5",
2191
+ rowGridCols,
2192
+ isLocked ? "cursor-default opacity-50" : "cursor-pointer hover:bg-subtle",
2193
+ !isLocked && isSel && "opacity-60"
2194
+ ),
2195
+ children: [
2196
+ isLocked ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { "aria-hidden": "true" }) : /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
2197
+ CheckBox,
2198
+ {
2199
+ checked: isSel,
2200
+ onChange: () => toggleRow(row),
2201
+ ariaLabel: row.name,
2202
+ testId: testIdPrefix && `${testIdPrefix}-row-${slug4(row.id)}`
2203
+ }
2204
+ ),
2205
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: cx8("grid gap-2 text-[13px] text-text-primary", gridForColumns(columns)), children: columns.map((c) => /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { className: cx8("truncate", c.className), children: c.render(row) }, c.key)) }),
2206
+ showInfoColumn && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: "flex items-center justify-end", children: kids && kids.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
2207
+ "button",
2208
+ {
2209
+ type: "button",
2210
+ "aria-label": "Show details",
2211
+ onClick: (e) => {
2212
+ e.stopPropagation();
2213
+ setExpanded((p) => {
2214
+ const n = new Set(p);
2215
+ n.has(row.id) ? n.delete(row.id) : n.add(row.id);
2216
+ return n;
2217
+ });
2218
+ },
2219
+ className: "flex h-7 w-7 items-center justify-center rounded-control text-text-secondary hover:bg-canvas hover:text-text-primary",
2220
+ children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_lucide_react14.ChevronRight, { size: 15, className: cx8("transition-transform", isOpen && "rotate-90") })
2221
+ }
2222
+ ) : /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
2223
+ "span",
2224
+ {
2225
+ className: "flex h-7 w-7 items-center justify-center text-text-tertiary",
2226
+ title: "No additional details",
2227
+ children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_lucide_react14.Info, { size: 14 })
2228
+ }
2229
+ ) })
2230
+ ]
2231
+ }
2232
+ ),
2233
+ isOpen && kids && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: "bg-canvas/60 pb-1.5 pl-10 pr-3", children: kids.map((kid) => {
2234
+ const kidSel = selectedIds.has(kid.id);
2235
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
2236
+ "div",
2237
+ {
2238
+ className: "flex items-center justify-between gap-2 border-t border-border-default/40 py-2 first:border-t-0",
2239
+ children: [
2240
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { className: "truncate text-[13px] text-text-secondary", children: kid.name }),
2241
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
2242
+ "button",
2243
+ {
2244
+ type: "button",
2245
+ disabled: kidSel,
2246
+ onClick: () => addRows([kid]),
2247
+ className: cx8(
2248
+ "inline-flex items-center gap-1 rounded-control px-2 py-1 text-[12px] font-medium",
2249
+ kidSel ? "text-text-tertiary" : "text-text-link hover:bg-subtle"
2250
+ ),
2251
+ children: [
2252
+ kidSel ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_lucide_react14.Check, { size: 13 }) : /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_lucide_react14.Plus, { size: 13 }),
2253
+ kidSel ? "Added" : "Add"
2254
+ ]
2255
+ }
2256
+ )
2257
+ ]
2258
+ },
2259
+ kid.id
2260
+ );
2261
+ }) })
2262
+ ] }, row.id);
2263
+ }) }),
2264
+ !loading && !error && hasMore && /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
2265
+ "button",
2266
+ {
2267
+ type: "button",
2268
+ onClick: onLoadMore,
2269
+ disabled: loadingMore,
2270
+ "data-testid": testIdPrefix && `${testIdPrefix}-load-more`,
2271
+ className: "flex w-full items-center justify-center gap-2 border-t border-border-default px-2 py-1.5 text-[13px] text-brand-primaryAction transition-colors hover:bg-subtle disabled:opacity-50",
2272
+ children: [
2273
+ loadingMore ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_lucide_react14.Loader2, { size: 13, className: "animate-spin", "aria-hidden": "true" }) : null,
2274
+ loadingMore ? "Loading\u2026" : "Load more"
2275
+ ]
2276
+ }
2277
+ )
2278
+ ] })
2279
+ ] }),
2280
+ source === "lists" && /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className: "flex min-w-0 flex-col gap-3", children: [
2281
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("p", { className: "text-[13px] text-text-secondary", children: "Select an account list to add all of its accounts at once. Your workspace stays in sync as the list is updated." }),
2282
+ accountLists && accountLists.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(import_jsx_runtime22.Fragment, { children: [
2283
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(ScrollFade, { className: "flex max-h-[396px] flex-col gap-2 overflow-y-auto pr-0.5", children: accountLists.map((list) => {
2284
+ const on = (selectedListIds ?? []).includes(list.id);
2285
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
2286
+ "button",
2287
+ {
2288
+ type: "button",
2289
+ onClick: () => onToggleList?.(list.id),
2290
+ "aria-pressed": on,
2291
+ "data-testid": testIdPrefix && `${testIdPrefix}-list-${list.id}`,
2292
+ className: cx8(
2293
+ "flex items-center gap-3 rounded-control border px-3.5 py-3 text-left transition-colors",
2294
+ on ? "border-brand-primaryAction bg-brand-primarySoft" : "border-border-default bg-surface hover:bg-subtle"
2295
+ ),
2296
+ children: [
2297
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
2298
+ "span",
2299
+ {
2300
+ className: cx8(
2301
+ "flex h-5 w-5 shrink-0 items-center justify-center rounded-full",
2302
+ on ? "text-brand-primaryAction" : "text-text-secondary"
2303
+ ),
2304
+ children: on ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_lucide_react14.Check, { size: 16 }) : /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_lucide_react14.Plus, { size: 16 })
2305
+ }
2306
+ ),
2307
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("span", { className: "min-w-0 flex-1", children: [
2308
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { className: "flex min-w-0 items-center gap-2", children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { className: "truncate text-[14px] font-medium text-text-primary", children: list.name }) }),
2309
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("span", { className: "block truncate text-[12px] text-text-secondary", children: [
2310
+ list.count,
2311
+ " accounts \xB7 ",
2312
+ list.entityTypes.join(", "),
2313
+ " \xB7 Updated ",
2314
+ formatListDate(list.updatedAt)
2315
+ ] })
2316
+ ] }),
2317
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { className: cx8("shrink-0 text-[12px] font-medium", on ? "text-brand-primaryAction" : "text-text-secondary"), children: on ? "Added" : "Add" })
2318
+ ]
2319
+ },
2320
+ list.id
2321
+ );
2322
+ }) }),
2323
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
2324
+ "p",
2325
+ {
2326
+ className: "border-t border-border-default pt-2 text-right text-[12px] font-medium text-text-tertiary",
2327
+ "data-testid": testIdPrefix && `${testIdPrefix}-list-count`,
2328
+ children: [
2329
+ accountLists.length,
2330
+ " ",
2331
+ accountLists.length === 1 ? "list" : "lists"
2332
+ ]
2333
+ }
2334
+ )
2335
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className: "flex flex-col items-center gap-3 rounded-card border border-dashed border-border-default py-10 text-center", children: [
2336
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: "flex h-12 w-12 items-center justify-center rounded-full bg-subtle", children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_lucide_react14.List, { size: 20, className: "text-text-tertiary", "aria-hidden": "true" }) }),
2337
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className: "flex flex-col gap-1", children: [
2338
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("p", { className: "text-[14px] font-semibold text-text-primary", children: "No account lists yet" }),
2339
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("p", { className: "text-[13px] text-text-tertiary", children: "Account lists set up for you will appear here." })
2340
+ ] })
2341
+ ] })
2342
+ ] })
2343
+ ] });
2344
+ }
2345
+ var EDGE_FADE = 44;
2346
+ function ScrollFade({ className, children }) {
2347
+ const elRef = (0, import_react11.useRef)(null);
2348
+ const [edges, setEdges] = (0, import_react11.useState)({ top: false, bottom: false });
2349
+ const sync = (0, import_react11.useCallback)(() => {
2350
+ const el = elRef.current;
2351
+ if (!el) return;
2352
+ const top = el.scrollTop > 2;
2353
+ const bottom = el.scrollTop + el.clientHeight < el.scrollHeight - 2;
2354
+ setEdges((prev) => prev.top === top && prev.bottom === bottom ? prev : { top, bottom });
2355
+ }, []);
2356
+ const attach = (0, import_react11.useCallback)(
2357
+ (el) => {
2358
+ elRef.current = el;
2359
+ if (!el) return;
2360
+ sync();
2361
+ const ro = new ResizeObserver(sync);
2362
+ ro.observe(el);
2363
+ return () => {
2364
+ ro.disconnect();
2365
+ elRef.current = null;
2366
+ };
2367
+ },
2368
+ [sync]
2369
+ );
2370
+ const mask = edges.top || edges.bottom ? `linear-gradient(to bottom, transparent 0, #000 ${edges.top ? EDGE_FADE : 0}px, #000 calc(100% - ${edges.bottom ? EDGE_FADE : 0}px), transparent 100%)` : void 0;
2371
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { ref: attach, onScroll: sync, className, style: { maskImage: mask, WebkitMaskImage: mask }, children });
2372
+ }
2373
+ function formatListDate(iso) {
2374
+ try {
2375
+ return new Date(iso).toLocaleDateString("en-US", { month: "short", day: "numeric" });
2376
+ } catch {
2377
+ return iso;
2378
+ }
2379
+ }
2380
+ function FilterDropdown({
2381
+ filter,
2382
+ active,
2383
+ onToggle,
2384
+ testIdPrefix
2385
+ }) {
2386
+ const [open, setOpen] = (0, import_react11.useState)(false);
2387
+ const [q, setQ] = (0, import_react11.useState)("");
2388
+ const btnRef = (0, import_react11.useRef)(null);
2389
+ const menuRef = (0, import_react11.useRef)(null);
2390
+ const [pos, setPos] = (0, import_react11.useState)(null);
2391
+ const searchable = filter.options.length > 8;
2392
+ const shownOptions = searchable && q.trim() ? filter.options.filter((o) => o.toLowerCase().includes(q.trim().toLowerCase())) : filter.options;
2393
+ (0, import_react11.useLayoutEffect)(() => {
2394
+ if (!open || !btnRef.current) return;
2395
+ const r = btnRef.current.getBoundingClientRect();
2396
+ setPos({ top: r.bottom + 6, left: r.left });
2397
+ }, [open]);
2398
+ (0, import_react11.useEffect)(() => {
2399
+ if (!open) {
2400
+ setQ("");
2401
+ return;
2402
+ }
2403
+ const onDown = (e) => {
2404
+ const t = e.target;
2405
+ if (!btnRef.current?.contains(t) && !menuRef.current?.contains(t)) setOpen(false);
2406
+ };
2407
+ const onKey = (e) => e.key === "Escape" && setOpen(false);
2408
+ const onScroll = () => setOpen(false);
2409
+ window.addEventListener("mousedown", onDown);
2410
+ window.addEventListener("keydown", onKey);
2411
+ window.addEventListener("scroll", onScroll, true);
2412
+ return () => {
2413
+ window.removeEventListener("mousedown", onDown);
2414
+ window.removeEventListener("keydown", onKey);
2415
+ window.removeEventListener("scroll", onScroll, true);
2416
+ };
2417
+ }, [open]);
2418
+ const count = active.size;
2419
+ const label = count === 0 ? filter.label : count === 1 ? [...active][0] : "Multiple";
2420
+ const filterSlug = slug4(filter.label);
2421
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className: "relative", children: [
2422
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
2423
+ "button",
2424
+ {
2425
+ ref: btnRef,
2426
+ type: "button",
2427
+ onClick: () => setOpen((v) => !v),
2428
+ "aria-haspopup": "menu",
2429
+ "aria-expanded": open,
2430
+ "data-testid": testIdPrefix && `${testIdPrefix}-filter-${filterSlug}`,
2431
+ className: "inline-flex h-9 items-center gap-1.5 whitespace-nowrap rounded-control border border-border-default bg-input px-3 text-[13px] font-medium text-text-secondary transition-colors hover:bg-subtle",
2432
+ children: [
2433
+ label,
2434
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_lucide_react14.ChevronDown, { size: 13, "aria-hidden": "true", className: cx8("transition-transform", open && "rotate-180") })
2435
+ ]
2436
+ }
2437
+ ),
2438
+ open && pos && (0, import_react_dom4.createPortal)(
2439
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
2440
+ "div",
2441
+ {
2442
+ ref: menuRef,
2443
+ role: "menu",
2444
+ "aria-label": filter.label,
2445
+ style: { top: pos.top, left: pos.left },
2446
+ className: "fixed z-[80] min-w-[220px] rounded-card border border-border-default bg-surface p-2 shadow-overlay",
2447
+ children: [
2448
+ searchable && /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className: "mb-1.5 flex h-8 items-center gap-2 rounded-control border border-border-default bg-input px-2.5", children: [
2449
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_lucide_react14.Search, { size: 14, className: "shrink-0 text-text-secondary", "aria-hidden": "true" }),
2450
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
2451
+ "input",
2452
+ {
2453
+ autoFocus: true,
2454
+ value: q,
2455
+ onChange: (e) => setQ(e.target.value),
2456
+ placeholder: `Search ${filter.label.toLowerCase()}\u2026`,
2457
+ className: "h-full flex-1 bg-transparent text-[13px] outline-hidden placeholder:text-text-tertiary"
2458
+ }
2459
+ )
2460
+ ] }),
2461
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: cx8("flex flex-col gap-0.5", searchable && "max-h-[240px] overflow-y-auto"), children: shownOptions.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("p", { className: "px-2 py-3 text-center text-[12px] text-text-secondary", children: "No matches." }) : shownOptions.map((opt) => {
2462
+ const on = active.has(opt);
2463
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
2464
+ "label",
2465
+ {
2466
+ className: "flex cursor-pointer items-center gap-2 rounded-control px-2 py-1.5 text-[13px] text-text-primary hover:bg-subtle",
2467
+ children: [
2468
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
2469
+ CheckBox,
2470
+ {
2471
+ checked: on,
2472
+ onChange: () => onToggle(opt),
2473
+ ariaLabel: opt,
2474
+ testId: testIdPrefix && `${testIdPrefix}-filter-${filterSlug}-${slug4(opt)}`
2475
+ }
2476
+ ),
2477
+ opt
2478
+ ]
2479
+ },
2480
+ opt
2481
+ );
2482
+ }) })
2483
+ ]
2484
+ }
2485
+ ),
2486
+ document.body
2487
+ )
2488
+ ] });
2489
+ }
2490
+ function gridForColumns(columns) {
2491
+ if (columns.length <= 1) return "grid-cols-1";
2492
+ if (columns.length === 2) return "grid-cols-[1.6fr_1fr]";
2493
+ if (columns.length === 3) return "grid-cols-[1.6fr_1fr_1fr]";
2494
+ return "grid-cols-[1.6fr_1fr_1fr_1fr]";
2495
+ }
2496
+
2497
+ // src/PublisherBreakdownList.tsx
2498
+ var import_react12 = require("react");
2499
+ var import_jsx_runtime23 = require("react/jsx-runtime");
2500
+ function cx9(...parts) {
2501
+ return parts.filter(Boolean).join(" ");
2502
+ }
2503
+ var fmtM = (v) => v >= 100 ? `$${Math.round(v)}M` : `$${v.toFixed(1)}M`;
2504
+ function priorYearAmt(spendN, yoy) {
2505
+ if (yoy === "NEW" || yoy <= -100) return null;
2506
+ return fmtM(spendN / (1 + yoy / 100));
2507
+ }
2508
+ function initials(name) {
2509
+ const words = name.replace(/[^a-zA-Z0-9 ]/g, " ").split(/\s+/).filter(Boolean);
2510
+ if (words.length >= 2) return (words[0][0] + words[1][0]).toUpperCase();
2511
+ return name.replace(/[^a-zA-Z0-9]/g, "").slice(0, 2).toUpperCase();
2512
+ }
2513
+ function Monogram({ name, size = 28 }) {
2514
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
2515
+ "span",
2516
+ {
2517
+ "aria-hidden": "true",
2518
+ className: "flex shrink-0 items-center justify-center rounded-[6px] bg-subtle font-bold leading-none text-text-secondary",
2519
+ style: { width: size, height: size, fontSize: Math.round(size * 0.36) },
2520
+ children: initials(name)
2521
+ }
2522
+ );
2523
+ }
2524
+ function BreakdownTag({ children }) {
2525
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { className: "inline-flex items-center whitespace-nowrap rounded-full bg-subtle px-2 py-0.5 text-[11px] font-medium text-text-secondary", children });
2526
+ }
2527
+ function DelayedTip({ label, children }) {
2528
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("span", { className: "group/dt relative inline-flex", tabIndex: 0, "aria-label": label, children: [
2529
+ children,
2530
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
2531
+ "span",
2532
+ {
2533
+ role: "tooltip",
2534
+ className: "pointer-events-none invisible absolute bottom-[calc(100%+6px)] left-1/2 z-40 w-[160px] -translate-x-1/2 rounded-control border border-border-default bg-surface px-2 py-1.5 text-center text-[11px] font-medium leading-snug text-text-secondary opacity-0 shadow-overlay transition-[opacity,visibility] delay-1000 duration-150 group-hover/dt:visible group-hover/dt:opacity-100 group-focus/dt:visible group-focus/dt:opacity-100",
2535
+ children: label
2536
+ }
2537
+ )
2538
+ ] });
2539
+ }
2540
+ var COL_CURRENT = 76;
2541
+ var COL_PREVIOUS = 76;
2542
+ var COL_YOY = 44;
2543
+ function MetricListHeader({ className }) {
2544
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: cx9("flex items-baseline gap-2 pb-2", className), children: [
2545
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { className: "flex-1" }),
2546
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("span", { className: "flex shrink-0 items-center gap-2 pr-1.5", children: [
2547
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { className: "text-right text-[11px] font-medium text-text-tertiary", style: { width: COL_CURRENT }, children: "Current" }),
2548
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(DelayedTip, { label: "Spend during the same time period one year ago.", children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { className: "text-right text-[11px] font-medium text-text-tertiary", style: { width: COL_PREVIOUS }, children: "Previous" }) }),
2549
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(DelayedTip, { label: "Percent change from this time period vs. the same time one year ago.", children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { className: "text-right text-[11px] font-medium text-text-tertiary", style: { width: COL_YOY }, children: "YoY" }) })
2550
+ ] })
2551
+ ] });
2552
+ }
2553
+ function MetricCells({ spendN, yoy }) {
2554
+ const prior = priorYearAmt(spendN, yoy);
2555
+ const up = yoy !== "NEW" && yoy >= 0;
2556
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("span", { className: "flex shrink-0 items-center gap-2 pr-1.5", children: [
2557
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { className: "text-right text-[12px] font-bold tabular-nums text-text-primary", style: { width: COL_CURRENT }, children: fmtM(spendN) }),
2558
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { className: "text-right text-[12px] font-normal tabular-nums text-text-primary", style: { width: COL_PREVIOUS }, children: prior ?? "\u2014" }),
2559
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { className: "flex justify-end", style: { width: COL_YOY }, children: yoy === "NEW" ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { className: "rounded-full bg-status-warning/15 px-1.5 text-[10px] font-semibold text-text-warning", children: "NEW" }) : /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("span", { className: cx9("text-right text-[12px] font-semibold tabular-nums", up ? "text-text-success" : "text-text-danger"), children: [
2560
+ up ? "+" : "\u2212",
2561
+ Math.abs(yoy),
2562
+ "%"
2563
+ ] }) })
2564
+ ] });
2565
+ }
2566
+ function PublisherBreakdownList({ rows, defaultVisibleCount = 10, padded = true, viewAllHref, renderLogo }) {
2567
+ const [showAll, setShowAll] = (0, import_react12.useState)(false);
2568
+ const visible = showAll ? rows : rows.slice(0, defaultVisibleCount);
2569
+ const rowPad = padded ? "px-5" : "";
2570
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "flex flex-col", children: [
2571
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "flex flex-col", children: [
2572
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(MetricListHeader, { className: rowPad }),
2573
+ visible.map((p) => /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: cx9("flex items-center justify-between gap-3 border-t border-border-default/60 py-2.5 first:border-t-0", rowPad), children: [
2574
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "flex min-w-0 items-center gap-2.5", children: [
2575
+ renderLogo ? renderLogo(p) : /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(Monogram, { name: p.brand, size: 28 }),
2576
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "min-w-0", children: [
2577
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "flex flex-wrap items-center gap-1.5", children: [
2578
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { className: "text-[14px] font-semibold text-text-primary", children: p.brand }),
2579
+ p.tags.map((t) => /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(BreakdownTag, { children: t }, t))
2580
+ ] }),
2581
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { className: "truncate text-[11.5px] text-text-tertiary", children: p.owner })
2582
+ ] })
2583
+ ] }),
2584
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(MetricCells, { spendN: p.spendN, yoy: p.yoy })
2585
+ ] }, p.brand))
2586
+ ] }),
2587
+ rows.length > defaultVisibleCount && (viewAllHref ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { className: cx9("border-t border-border-default py-3", rowPad), children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("a", { href: viewAllHref, className: "text-[12px] font-semibold text-text-link hover:underline", children: "See all publishers" }) }) : /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: cx9("flex items-center gap-2 border-t border-border-default py-3 text-[12px] text-text-tertiary", rowPad), children: [
2588
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("span", { children: [
2589
+ "Showing ",
2590
+ visible.length,
2591
+ " of ",
2592
+ rows.length
2593
+ ] }),
2594
+ !showAll ? /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("button", { type: "button", onClick: () => setShowAll(true), className: "font-semibold text-text-link hover:underline", children: [
2595
+ "See all (",
2596
+ rows.length - defaultVisibleCount,
2597
+ " more)"
2598
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("button", { type: "button", onClick: () => setShowAll(false), className: "font-semibold text-text-link hover:underline", children: "Show less" })
2599
+ ] }))
2600
+ ] });
2601
+ }
99
2602
  // Annotate the CommonJS export names for ESM import in node:
100
2603
  0 && (module.exports = {
101
- InfoTip
2604
+ AdvertiserNoResults,
2605
+ AdvertiserQuickFilter,
2606
+ AdvertiserSearchPrompt,
2607
+ BarChartPlaceholder,
2608
+ Card,
2609
+ CheckBox,
2610
+ DEFAULT_SEARCH_MATCH,
2611
+ DataScopeFilter,
2612
+ DateRangeFilter,
2613
+ InfoTip,
2614
+ LineChartPlaceholder,
2615
+ NO_RESULTS_BODY,
2616
+ NO_RESULTS_TITLE,
2617
+ NoResultsEmptyState,
2618
+ PRIMARY_ACTION_CLASS,
2619
+ PieChartPlaceholder,
2620
+ PublisherBreakdownList,
2621
+ QUIET_ACTION_CLASS,
2622
+ SearchBar,
2623
+ SectionRail,
2624
+ SelectAllVisible,
2625
+ SelectFilter,
2626
+ SelectedSection,
2627
+ SelectionPicker,
2628
+ StackedColumnPlaceholder,
2629
+ Tooltip,
2630
+ WorkspaceSelectedCard,
2631
+ noResultsBody
102
2632
  });
103
2633
  //# sourceMappingURL=index.cjs.map