@mediaradar/design-components 0.6.0 → 0.8.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/README.md +12 -3
- package/dist/index.cjs +1013 -9
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +486 -2
- package/dist/index.d.ts +486 -2
- package/dist/index.js +992 -8
- package/dist/index.js.map +1 -1
- package/package.json +6 -2
- package/scripts/tailwind-coverage.mjs +3 -0
package/dist/index.js
CHANGED
|
@@ -1545,7 +1545,7 @@ function AxisFrame({
|
|
|
1545
1545
|
"div",
|
|
1546
1546
|
{
|
|
1547
1547
|
className: "absolute inset-x-0 border-t border-dashed",
|
|
1548
|
-
style: { top: `${pct}%`, borderColor: "var(--color-border-default)" }
|
|
1548
|
+
style: { top: `${pct}%`, borderColor: "var(--mr-color-border-default)" }
|
|
1549
1549
|
},
|
|
1550
1550
|
pct
|
|
1551
1551
|
)),
|
|
@@ -1618,7 +1618,7 @@ function LineChartPlaceholder({ height = 280 }) {
|
|
|
1618
1618
|
{
|
|
1619
1619
|
points: toPath(pts, 0, LINE_FORECAST_FROM + 1),
|
|
1620
1620
|
fill: "none",
|
|
1621
|
-
stroke: "var(--color-border-strong)",
|
|
1621
|
+
stroke: "var(--mr-color-border-strong)",
|
|
1622
1622
|
strokeWidth: "2",
|
|
1623
1623
|
vectorEffect: "non-scaling-stroke",
|
|
1624
1624
|
strokeLinecap: "round",
|
|
@@ -1630,7 +1630,7 @@ function LineChartPlaceholder({ height = 280 }) {
|
|
|
1630
1630
|
{
|
|
1631
1631
|
points: toPath(pts, LINE_FORECAST_FROM, pts.length),
|
|
1632
1632
|
fill: "none",
|
|
1633
|
-
stroke: "var(--color-border-strong)",
|
|
1633
|
+
stroke: "var(--mr-color-border-strong)",
|
|
1634
1634
|
strokeWidth: "2",
|
|
1635
1635
|
strokeDasharray: "5 4",
|
|
1636
1636
|
vectorEffect: "non-scaling-stroke",
|
|
@@ -1647,7 +1647,7 @@ function LineChartPlaceholder({ height = 280 }) {
|
|
|
1647
1647
|
style: {
|
|
1648
1648
|
left: `${i / (pts.length - 1) * 100}%`,
|
|
1649
1649
|
top: `${y}%`,
|
|
1650
|
-
backgroundColor: "var(--color-border-strong)"
|
|
1650
|
+
backgroundColor: "var(--mr-color-border-strong)"
|
|
1651
1651
|
}
|
|
1652
1652
|
},
|
|
1653
1653
|
`${s}-${i}`
|
|
@@ -1663,7 +1663,7 @@ function PieChartPlaceholder({ height = 220, legendRows = 8 }) {
|
|
|
1663
1663
|
const r = 45;
|
|
1664
1664
|
return /* @__PURE__ */ jsxs14("div", { className: "flex w-full items-center gap-3", style: { height }, role: "presentation", "aria-hidden": "true", children: [
|
|
1665
1665
|
/* @__PURE__ */ jsx16("div", { className: "flex h-full items-center justify-center", style: { width: "60%" }, children: /* @__PURE__ */ jsxs14("svg", { viewBox: "0 0 100 100", className: "h-full", style: { aspectRatio: "1 / 1" }, children: [
|
|
1666
|
-
/* @__PURE__ */ jsx16("circle", { cx: "50", cy: "50", r, fill: "var(--color-subtle)" }),
|
|
1666
|
+
/* @__PURE__ */ jsx16("circle", { cx: "50", cy: "50", r, fill: "var(--mr-color-subtle)" }),
|
|
1667
1667
|
PIE_SEAMS.map((deg) => {
|
|
1668
1668
|
const rad = (deg - 90) * Math.PI / 180;
|
|
1669
1669
|
return /* @__PURE__ */ jsx16(
|
|
@@ -1673,7 +1673,7 @@ function PieChartPlaceholder({ height = 220, legendRows = 8 }) {
|
|
|
1673
1673
|
y1: "50",
|
|
1674
1674
|
x2: 50 + r * Math.cos(rad),
|
|
1675
1675
|
y2: 50 + r * Math.sin(rad),
|
|
1676
|
-
stroke: "var(--color-border-default)",
|
|
1676
|
+
stroke: "var(--mr-color-border-default)",
|
|
1677
1677
|
strokeWidth: "1"
|
|
1678
1678
|
},
|
|
1679
1679
|
deg
|
|
@@ -1705,7 +1705,7 @@ function StackedColumnPlaceholder({
|
|
|
1705
1705
|
height: `${pct}%`,
|
|
1706
1706
|
/* border-strong, not border-default: on a subtle fill the softer
|
|
1707
1707
|
seam disappears and the stack reads as one undivided block. */
|
|
1708
|
-
borderTop: i === 0 ? void 0 : "1px solid var(--color-border-strong)"
|
|
1708
|
+
borderTop: i === 0 ? void 0 : "1px solid var(--mr-color-border-strong)"
|
|
1709
1709
|
}
|
|
1710
1710
|
},
|
|
1711
1711
|
i
|
|
@@ -2552,22 +2552,996 @@ function PublisherBreakdownList({ rows, defaultVisibleCount = 10, padded = true,
|
|
|
2552
2552
|
] }))
|
|
2553
2553
|
] });
|
|
2554
2554
|
}
|
|
2555
|
+
|
|
2556
|
+
// src/Button.tsx
|
|
2557
|
+
import { forwardRef as forwardRef2 } from "react";
|
|
2558
|
+
import { Slot } from "@radix-ui/react-slot";
|
|
2559
|
+
import { cva } from "class-variance-authority";
|
|
2560
|
+
import { Loader2 as Loader22 } from "lucide-react";
|
|
2561
|
+
|
|
2562
|
+
// src/cn.ts
|
|
2563
|
+
import { clsx } from "clsx";
|
|
2564
|
+
import { twMerge } from "tailwind-merge";
|
|
2565
|
+
function cn(...inputs) {
|
|
2566
|
+
return twMerge(clsx(inputs));
|
|
2567
|
+
}
|
|
2568
|
+
|
|
2569
|
+
// src/Button.tsx
|
|
2570
|
+
import { jsx as jsx24, jsxs as jsxs22 } from "react/jsx-runtime";
|
|
2571
|
+
var buttonVariants = cva(
|
|
2572
|
+
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-control font-medium transition-all duration-(--mr-motion-duration-interactive) ease-interactive disabled:pointer-events-none disabled:opacity-40 focus-visible:outline-hidden focus-visible:ring-0",
|
|
2573
|
+
{
|
|
2574
|
+
variants: {
|
|
2575
|
+
variant: {
|
|
2576
|
+
primary: "bg-brand-primaryAction text-brand-onprimary hover:brightness-110",
|
|
2577
|
+
secondary: "bg-surface text-text-primary border border-border-interactive hover:bg-subtle",
|
|
2578
|
+
ghost: "text-text-primary hover:bg-subtle",
|
|
2579
|
+
danger: "bg-status-danger text-status-onstatus hover:brightness-110",
|
|
2580
|
+
link: "text-text-link underline-offset-4 hover:underline px-0"
|
|
2581
|
+
},
|
|
2582
|
+
size: {
|
|
2583
|
+
sm: "h-8 px-3 text-sm",
|
|
2584
|
+
md: "h-10 px-4 text-sm",
|
|
2585
|
+
lg: "h-11 px-6 text-sm",
|
|
2586
|
+
icon: "h-9 w-9 p-0"
|
|
2587
|
+
}
|
|
2588
|
+
},
|
|
2589
|
+
defaultVariants: {
|
|
2590
|
+
variant: "primary",
|
|
2591
|
+
size: "md"
|
|
2592
|
+
}
|
|
2593
|
+
}
|
|
2594
|
+
);
|
|
2595
|
+
var Button = forwardRef2(
|
|
2596
|
+
({ className, variant, size, asChild = false, loading = false, disabled, children, ...props }, ref) => {
|
|
2597
|
+
const Comp = asChild ? Slot : "button";
|
|
2598
|
+
return /* @__PURE__ */ jsxs22(
|
|
2599
|
+
Comp,
|
|
2600
|
+
{
|
|
2601
|
+
className: cn(buttonVariants({ variant, size, className })),
|
|
2602
|
+
ref,
|
|
2603
|
+
disabled: disabled || loading,
|
|
2604
|
+
"aria-busy": loading || void 0,
|
|
2605
|
+
...props,
|
|
2606
|
+
children: [
|
|
2607
|
+
loading && /* @__PURE__ */ jsx24(Loader22, { className: "size-4 shrink-0 animate-spin", "aria-hidden": "true" }),
|
|
2608
|
+
children
|
|
2609
|
+
]
|
|
2610
|
+
}
|
|
2611
|
+
);
|
|
2612
|
+
}
|
|
2613
|
+
);
|
|
2614
|
+
Button.displayName = "Button";
|
|
2615
|
+
|
|
2616
|
+
// src/FilterDropdownFooter.tsx
|
|
2617
|
+
import { jsx as jsx25, jsxs as jsxs23 } from "react/jsx-runtime";
|
|
2618
|
+
function FilterDropdownFooter({
|
|
2619
|
+
onClear,
|
|
2620
|
+
onApply,
|
|
2621
|
+
clearLabel = "Clear all",
|
|
2622
|
+
applyLabel = "Apply"
|
|
2623
|
+
}) {
|
|
2624
|
+
return /* @__PURE__ */ jsxs23(
|
|
2625
|
+
"div",
|
|
2626
|
+
{
|
|
2627
|
+
className: cn(
|
|
2628
|
+
"flex items-center gap-2 border-t border-border-default p-2",
|
|
2629
|
+
onClear ? "justify-between" : "justify-end"
|
|
2630
|
+
),
|
|
2631
|
+
children: [
|
|
2632
|
+
onClear && /* @__PURE__ */ jsx25(Button, { type: "button", variant: "ghost", size: "sm", onClick: onClear, children: clearLabel }),
|
|
2633
|
+
/* @__PURE__ */ jsx25(Button, { type: "button", variant: "primary", size: "sm", onClick: onApply, children: applyLabel })
|
|
2634
|
+
]
|
|
2635
|
+
}
|
|
2636
|
+
);
|
|
2637
|
+
}
|
|
2638
|
+
|
|
2639
|
+
// src/NameListModal.tsx
|
|
2640
|
+
import { useEffect as useEffect10, useState as useState11 } from "react";
|
|
2641
|
+
import { createPortal as createPortal5 } from "react-dom";
|
|
2642
|
+
import { jsx as jsx26, jsxs as jsxs24 } from "react/jsx-runtime";
|
|
2643
|
+
function NameListModal({ title, initialName, submitLabel, onCancel, onSubmit }) {
|
|
2644
|
+
const [name, setName] = useState11(initialName);
|
|
2645
|
+
useEffect10(() => {
|
|
2646
|
+
const onKey = (e) => e.key === "Escape" && onCancel();
|
|
2647
|
+
window.addEventListener("keydown", onKey);
|
|
2648
|
+
return () => window.removeEventListener("keydown", onKey);
|
|
2649
|
+
}, [onCancel]);
|
|
2650
|
+
return createPortal5(
|
|
2651
|
+
// The scrim is a literal: @mediaradar/design-tokens has no scrim token yet.
|
|
2652
|
+
// Same light/dark values as insights-studio-prototype's local --color-scrim;
|
|
2653
|
+
// the dark one keys off data-theme, the attribute design-tokens' dark.css uses.
|
|
2654
|
+
/* @__PURE__ */ jsx26(
|
|
2655
|
+
"div",
|
|
2656
|
+
{
|
|
2657
|
+
className: "fixed inset-0 z-70 flex items-center justify-center bg-[rgba(20,17,13,0.55)] in-data-[theme=dark]:bg-[rgba(0,0,0,0.65)] px-4",
|
|
2658
|
+
onMouseDown: (e) => e.target === e.currentTarget && onCancel(),
|
|
2659
|
+
children: /* @__PURE__ */ jsxs24(
|
|
2660
|
+
"div",
|
|
2661
|
+
{
|
|
2662
|
+
role: "dialog",
|
|
2663
|
+
"aria-modal": "true",
|
|
2664
|
+
"aria-label": title,
|
|
2665
|
+
className: "w-full max-w-[420px] rounded-card border border-border-default bg-surface p-5 shadow-overlay",
|
|
2666
|
+
children: [
|
|
2667
|
+
/* @__PURE__ */ jsx26("h2", { className: "text-[16px] font-semibold text-text-primary", children: title }),
|
|
2668
|
+
/* @__PURE__ */ jsxs24("div", { className: "mt-4 flex flex-col gap-1.5", children: [
|
|
2669
|
+
/* @__PURE__ */ jsx26("label", { htmlFor: "account-list-name", className: "text-[12px] font-medium text-text-secondary", children: "List name" }),
|
|
2670
|
+
/* @__PURE__ */ jsx26(
|
|
2671
|
+
"input",
|
|
2672
|
+
{
|
|
2673
|
+
id: "account-list-name",
|
|
2674
|
+
autoFocus: true,
|
|
2675
|
+
value: name,
|
|
2676
|
+
onChange: (e) => setName(e.target.value),
|
|
2677
|
+
placeholder: "e.g. Q4 prospecting targets",
|
|
2678
|
+
className: "h-10 rounded-control border border-border-default bg-input px-3 text-sm outline-hidden focus:border-brand-primaryAction"
|
|
2679
|
+
}
|
|
2680
|
+
)
|
|
2681
|
+
] }),
|
|
2682
|
+
/* @__PURE__ */ jsxs24("div", { className: "mt-5 flex items-center justify-end gap-2", children: [
|
|
2683
|
+
/* @__PURE__ */ jsx26(Button, { variant: "secondary", onClick: onCancel, children: "Cancel" }),
|
|
2684
|
+
/* @__PURE__ */ jsx26(Button, { disabled: !name.trim(), onClick: () => onSubmit(name.trim()), children: submitLabel })
|
|
2685
|
+
] })
|
|
2686
|
+
]
|
|
2687
|
+
}
|
|
2688
|
+
)
|
|
2689
|
+
}
|
|
2690
|
+
),
|
|
2691
|
+
document.body
|
|
2692
|
+
);
|
|
2693
|
+
}
|
|
2694
|
+
|
|
2695
|
+
// src/ChipTrigger.tsx
|
|
2696
|
+
import { jsx as jsx27, jsxs as jsxs25 } from "react/jsx-runtime";
|
|
2697
|
+
function chipTriggerClass() {
|
|
2698
|
+
return cn(
|
|
2699
|
+
"flex h-11 items-center gap-2 rounded-control border 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",
|
|
2700
|
+
// Sits on the input fill, with a stroke dark enough to still read as a
|
|
2701
|
+
// control against the warm canvas.
|
|
2702
|
+
"border-border-strong"
|
|
2703
|
+
);
|
|
2704
|
+
}
|
|
2705
|
+
function ChipContent({ caption, value, icon }) {
|
|
2706
|
+
const stack = /* @__PURE__ */ jsxs25("span", { className: "flex flex-col gap-1 text-left leading-none", children: [
|
|
2707
|
+
/* @__PURE__ */ jsx27("span", { className: "text-[10px] uppercase tracking-wide text-text-tertiary", children: caption }),
|
|
2708
|
+
/* @__PURE__ */ jsx27("span", { className: "text-[12px] font-medium text-text-primary", children: value })
|
|
2709
|
+
] });
|
|
2710
|
+
if (icon) {
|
|
2711
|
+
return /* @__PURE__ */ jsxs25("span", { className: "flex items-center gap-2", children: [
|
|
2712
|
+
/* @__PURE__ */ jsx27("span", { className: "shrink-0 text-text-tertiary", children: icon }),
|
|
2713
|
+
stack
|
|
2714
|
+
] });
|
|
2715
|
+
}
|
|
2716
|
+
return stack;
|
|
2717
|
+
}
|
|
2718
|
+
|
|
2719
|
+
// src/FilterChipBar.tsx
|
|
2720
|
+
import { CircleSlash, X as X3 } from "lucide-react";
|
|
2721
|
+
import { jsx as jsx28, jsxs as jsxs26 } from "react/jsx-runtime";
|
|
2722
|
+
function FilterChipBar({ chips, onRemove, onClearAll, parkedKeys }) {
|
|
2723
|
+
if (chips.length === 0) return null;
|
|
2724
|
+
return /* @__PURE__ */ jsxs26("div", { className: "flex flex-wrap items-center gap-1.5 border-t border-border-default pt-2.5", children: [
|
|
2725
|
+
chips.map((c) => /* @__PURE__ */ jsx28(
|
|
2726
|
+
Chip,
|
|
2727
|
+
{
|
|
2728
|
+
label: c.label,
|
|
2729
|
+
groupLabel: c.groupLabel,
|
|
2730
|
+
parked: !!parkedKeys?.has(c.key),
|
|
2731
|
+
onRemove: () => onRemove(c)
|
|
2732
|
+
},
|
|
2733
|
+
c.key
|
|
2734
|
+
)),
|
|
2735
|
+
/* @__PURE__ */ jsx28("button", { type: "button", onClick: onClearAll, className: "ml-1 text-[12px] font-medium text-text-link hover:underline", children: "Clear all" })
|
|
2736
|
+
] });
|
|
2737
|
+
}
|
|
2738
|
+
function Chip({
|
|
2739
|
+
label,
|
|
2740
|
+
groupLabel,
|
|
2741
|
+
parked,
|
|
2742
|
+
onRemove
|
|
2743
|
+
}) {
|
|
2744
|
+
return /* @__PURE__ */ jsxs26("span", { className: "group relative inline-flex", children: [
|
|
2745
|
+
/* @__PURE__ */ jsxs26(
|
|
2746
|
+
"span",
|
|
2747
|
+
{
|
|
2748
|
+
className: cn(
|
|
2749
|
+
"flex items-center gap-1 rounded-full px-2.5 py-1 text-[12px] font-medium ring-1 transition-colors",
|
|
2750
|
+
parked ? (
|
|
2751
|
+
// No fill, dashed edge, muted label: reads as "held, not acting on
|
|
2752
|
+
// anything" against the solid applied chips beside it.
|
|
2753
|
+
"border border-dashed border-border-default bg-transparent text-text-tertiary ring-transparent group-hover:text-text-secondary"
|
|
2754
|
+
) : "bg-subtle text-text-secondary ring-transparent group-hover:bg-border-default group-hover:text-text-primary group-hover:ring-border-interactive"
|
|
2755
|
+
),
|
|
2756
|
+
children: [
|
|
2757
|
+
parked && /* @__PURE__ */ jsx28(CircleSlash, { size: 11, "aria-hidden": "true", className: "shrink-0 text-text-tertiary" }),
|
|
2758
|
+
label,
|
|
2759
|
+
/* @__PURE__ */ jsx28(
|
|
2760
|
+
"button",
|
|
2761
|
+
{
|
|
2762
|
+
type: "button",
|
|
2763
|
+
onClick: onRemove,
|
|
2764
|
+
"aria-label": `Remove ${groupLabel}: ${label}`,
|
|
2765
|
+
className: "text-text-tertiary transition-colors hover:text-text-primary",
|
|
2766
|
+
children: /* @__PURE__ */ jsx28(X3, { size: 12 })
|
|
2767
|
+
}
|
|
2768
|
+
)
|
|
2769
|
+
]
|
|
2770
|
+
}
|
|
2771
|
+
),
|
|
2772
|
+
/* @__PURE__ */ jsx28(
|
|
2773
|
+
"span",
|
|
2774
|
+
{
|
|
2775
|
+
role: "tooltip",
|
|
2776
|
+
className: "pointer-events-none absolute bottom-full left-1/2 z-20 mb-1.5 -translate-x-1/2 whitespace-nowrap rounded-md bg-text-primary px-2 py-1 text-[11px] font-medium text-surface opacity-0 shadow-raised transition-opacity duration-150 group-hover:opacity-100",
|
|
2777
|
+
children: parked ? "Not in this workspace" : groupLabel
|
|
2778
|
+
}
|
|
2779
|
+
)
|
|
2780
|
+
] });
|
|
2781
|
+
}
|
|
2782
|
+
|
|
2783
|
+
// src/SortButton.tsx
|
|
2784
|
+
import { ArrowUp, ArrowDown, ArrowUpDown } from "lucide-react";
|
|
2785
|
+
import { jsx as jsx29, jsxs as jsxs27 } from "react/jsx-runtime";
|
|
2786
|
+
function SortButton({ label, sortKey, sort, onSort, disabled = false }) {
|
|
2787
|
+
const active = !disabled && sort.key === sortKey;
|
|
2788
|
+
return /* @__PURE__ */ jsxs27(
|
|
2789
|
+
"button",
|
|
2790
|
+
{
|
|
2791
|
+
type: "button",
|
|
2792
|
+
disabled,
|
|
2793
|
+
onClick: () => onSort(sortKey),
|
|
2794
|
+
"aria-sort": active ? sort.dir === "asc" ? "ascending" : "descending" : "none",
|
|
2795
|
+
className: cn(
|
|
2796
|
+
// text-left overrides the UA default on <button>, which would centre a
|
|
2797
|
+
// label that wraps onto two lines, out of step with the column under it.
|
|
2798
|
+
"group inline-flex items-center gap-1 text-left uppercase tracking-wide transition-colors",
|
|
2799
|
+
!disabled && "hover:text-text-primary"
|
|
2800
|
+
),
|
|
2801
|
+
children: [
|
|
2802
|
+
label,
|
|
2803
|
+
!disabled && (active ? sort.dir === "asc" ? /* @__PURE__ */ jsx29(ArrowUp, { size: 12, className: "shrink-0 text-brand-primaryAction" }) : /* @__PURE__ */ jsx29(ArrowDown, { size: 12, className: "shrink-0 text-brand-primaryAction" }) : /* @__PURE__ */ jsx29(
|
|
2804
|
+
ArrowUpDown,
|
|
2805
|
+
{
|
|
2806
|
+
size: 12,
|
|
2807
|
+
className: "shrink-0 text-text-tertiary opacity-50 transition-opacity group-hover:opacity-100"
|
|
2808
|
+
}
|
|
2809
|
+
))
|
|
2810
|
+
]
|
|
2811
|
+
}
|
|
2812
|
+
);
|
|
2813
|
+
}
|
|
2814
|
+
|
|
2815
|
+
// src/tableSort.ts
|
|
2816
|
+
function cycleSort(current, key, firstDir = "desc") {
|
|
2817
|
+
if (!current || current.key !== key) return { key, dir: firstDir };
|
|
2818
|
+
if (current.dir === firstDir) return { key, dir: firstDir === "desc" ? "asc" : "desc" };
|
|
2819
|
+
return null;
|
|
2820
|
+
}
|
|
2821
|
+
function sortProp(sort) {
|
|
2822
|
+
return sort ?? { key: null, dir: "desc" };
|
|
2823
|
+
}
|
|
2824
|
+
|
|
2825
|
+
// src/Pager.tsx
|
|
2826
|
+
import { ChevronLeft as ChevronLeft3, ChevronRight as ChevronRight4 } from "lucide-react";
|
|
2827
|
+
import { jsx as jsx30, jsxs as jsxs28 } from "react/jsx-runtime";
|
|
2828
|
+
function pageWindow(current, total) {
|
|
2829
|
+
if (total <= 7) return Array.from({ length: total }, (_, i) => i + 1);
|
|
2830
|
+
const keep = /* @__PURE__ */ new Set([1, total, current, current - 1, current + 1]);
|
|
2831
|
+
const pages = [...keep].filter((p) => p >= 1 && p <= total).sort((a, b) => a - b);
|
|
2832
|
+
const out = [];
|
|
2833
|
+
let prev = 0;
|
|
2834
|
+
for (const p of pages) {
|
|
2835
|
+
if (p - prev > 1) out.push(`gap-${p}`);
|
|
2836
|
+
out.push(p);
|
|
2837
|
+
prev = p;
|
|
2838
|
+
}
|
|
2839
|
+
return out;
|
|
2840
|
+
}
|
|
2841
|
+
function Pager({ page, totalPages, onPage }) {
|
|
2842
|
+
const btn = "flex h-8 min-w-8 items-center justify-center rounded-control px-2 text-[13px] transition-colors";
|
|
2843
|
+
return /* @__PURE__ */ jsxs28("div", { className: "flex items-center gap-1", children: [
|
|
2844
|
+
/* @__PURE__ */ jsx30(
|
|
2845
|
+
"button",
|
|
2846
|
+
{
|
|
2847
|
+
type: "button",
|
|
2848
|
+
onClick: () => onPage(page - 1),
|
|
2849
|
+
disabled: page === 1,
|
|
2850
|
+
"aria-label": "Previous page",
|
|
2851
|
+
className: cn(btn, "text-text-secondary hover:bg-subtle hover:text-text-primary disabled:pointer-events-none disabled:opacity-30"),
|
|
2852
|
+
children: /* @__PURE__ */ jsx30(ChevronLeft3, { size: 16 })
|
|
2853
|
+
}
|
|
2854
|
+
),
|
|
2855
|
+
pageWindow(page, totalPages).map(
|
|
2856
|
+
(p) => typeof p === "string" ? /* @__PURE__ */ jsx30("span", { className: "px-1 text-[13px] text-text-tertiary", children: "\u2026" }, p) : /* @__PURE__ */ jsx30(
|
|
2857
|
+
"button",
|
|
2858
|
+
{
|
|
2859
|
+
type: "button",
|
|
2860
|
+
onClick: () => onPage(p),
|
|
2861
|
+
"aria-current": p === page ? "page" : void 0,
|
|
2862
|
+
className: cn(
|
|
2863
|
+
btn,
|
|
2864
|
+
p === page ? "bg-brand-primaryAction font-semibold text-brand-onprimary" : "text-text-secondary hover:bg-subtle hover:text-text-primary"
|
|
2865
|
+
),
|
|
2866
|
+
children: p
|
|
2867
|
+
},
|
|
2868
|
+
p
|
|
2869
|
+
)
|
|
2870
|
+
),
|
|
2871
|
+
/* @__PURE__ */ jsx30(
|
|
2872
|
+
"button",
|
|
2873
|
+
{
|
|
2874
|
+
type: "button",
|
|
2875
|
+
onClick: () => onPage(page + 1),
|
|
2876
|
+
disabled: page === totalPages,
|
|
2877
|
+
"aria-label": "Next page",
|
|
2878
|
+
className: cn(btn, "text-text-secondary hover:bg-subtle hover:text-text-primary disabled:pointer-events-none disabled:opacity-30"),
|
|
2879
|
+
children: /* @__PURE__ */ jsx30(ChevronRight4, { size: 16 })
|
|
2880
|
+
}
|
|
2881
|
+
)
|
|
2882
|
+
] });
|
|
2883
|
+
}
|
|
2884
|
+
|
|
2885
|
+
// src/WorkspaceAvatar.tsx
|
|
2886
|
+
import { jsx as jsx31 } from "react/jsx-runtime";
|
|
2887
|
+
function workspaceInitials(name) {
|
|
2888
|
+
const words = name.trim().split(/\s+/).filter(Boolean);
|
|
2889
|
+
if (words.length === 0) return "\xB7";
|
|
2890
|
+
if (words.length === 1) return words[0].slice(0, 2).toUpperCase();
|
|
2891
|
+
return (words[0][0] + words[1][0]).toUpperCase();
|
|
2892
|
+
}
|
|
2893
|
+
var SIZES = {
|
|
2894
|
+
/** Matches a 15px lucide icon exactly, so a workspace name in a menu row
|
|
2895
|
+
* left-aligns with labels in icon-led rows above and below it. */
|
|
2896
|
+
xs: "h-[15px] w-[15px] rounded-[4px] text-[8px] tracking-tight",
|
|
2897
|
+
sm: "h-6 w-6 text-[10px]",
|
|
2898
|
+
md: "h-7 w-7 text-[11px]",
|
|
2899
|
+
lg: "h-11 w-11 text-[15px]"
|
|
2900
|
+
};
|
|
2901
|
+
function WorkspaceAvatar({ name, size = "md" }) {
|
|
2902
|
+
return /* @__PURE__ */ jsx31(
|
|
2903
|
+
"span",
|
|
2904
|
+
{
|
|
2905
|
+
"aria-hidden": "true",
|
|
2906
|
+
className: cn(
|
|
2907
|
+
"flex shrink-0 items-center justify-center rounded-control bg-subtle font-semibold leading-none text-text-secondary",
|
|
2908
|
+
SIZES[size]
|
|
2909
|
+
),
|
|
2910
|
+
children: workspaceInitials(name ?? "All data")
|
|
2911
|
+
}
|
|
2912
|
+
);
|
|
2913
|
+
}
|
|
2914
|
+
|
|
2915
|
+
// src/Tag.tsx
|
|
2916
|
+
import { jsx as jsx32 } from "react/jsx-runtime";
|
|
2917
|
+
function Tag({ children, className }) {
|
|
2918
|
+
return /* @__PURE__ */ jsx32(
|
|
2919
|
+
"span",
|
|
2920
|
+
{
|
|
2921
|
+
className: cn(
|
|
2922
|
+
"inline-flex items-center whitespace-nowrap rounded-full bg-subtle px-2 py-0.5 text-[11px] font-medium text-text-secondary",
|
|
2923
|
+
className
|
|
2924
|
+
),
|
|
2925
|
+
children
|
|
2926
|
+
}
|
|
2927
|
+
);
|
|
2928
|
+
}
|
|
2929
|
+
|
|
2930
|
+
// src/TablePagination.tsx
|
|
2931
|
+
import { ChevronDown as ChevronDown9, ChevronFirst, ChevronLast, ChevronLeft as ChevronLeft4, ChevronRight as ChevronRight5 } from "lucide-react";
|
|
2932
|
+
import { jsx as jsx33, jsxs as jsxs29 } from "react/jsx-runtime";
|
|
2933
|
+
function TablePagination({
|
|
2934
|
+
page,
|
|
2935
|
+
perPage,
|
|
2936
|
+
totalItems,
|
|
2937
|
+
onPageChange,
|
|
2938
|
+
onPerPageChange,
|
|
2939
|
+
perPageOptions = [25, 50, 100],
|
|
2940
|
+
itemLabel = "results",
|
|
2941
|
+
className
|
|
2942
|
+
}) {
|
|
2943
|
+
const totalPages = Math.max(1, Math.ceil(totalItems / perPage));
|
|
2944
|
+
const current = Math.min(page, totalPages);
|
|
2945
|
+
const rangeStart = totalItems === 0 ? 0 : (current - 1) * perPage + 1;
|
|
2946
|
+
const rangeEnd = Math.min(current * perPage, totalItems);
|
|
2947
|
+
const btn = "flex h-8 min-w-8 items-center justify-center rounded-control px-2 text-[13px] transition-colors";
|
|
2948
|
+
const stepBtn = cn(
|
|
2949
|
+
btn,
|
|
2950
|
+
"text-text-secondary hover:bg-subtle hover:text-text-primary disabled:pointer-events-none disabled:opacity-30"
|
|
2951
|
+
);
|
|
2952
|
+
return /* @__PURE__ */ jsxs29("div", { className: cn("flex flex-wrap items-center justify-between gap-x-4 gap-y-2", className), children: [
|
|
2953
|
+
/* @__PURE__ */ jsxs29("label", { className: "flex items-center gap-2 text-[13px] text-text-secondary", children: [
|
|
2954
|
+
/* @__PURE__ */ jsxs29("span", { className: "relative", children: [
|
|
2955
|
+
/* @__PURE__ */ jsx33(
|
|
2956
|
+
"select",
|
|
2957
|
+
{
|
|
2958
|
+
value: perPage,
|
|
2959
|
+
onChange: (e) => onPerPageChange(Number(e.target.value)),
|
|
2960
|
+
"aria-label": "Items per page",
|
|
2961
|
+
className: "appearance-none rounded-control border border-border-default bg-input py-1 pl-2.5 pr-7 text-[13px] font-medium text-text-primary focus:outline focus:outline-2 focus:outline-border-focus",
|
|
2962
|
+
children: perPageOptions.map((n) => /* @__PURE__ */ jsx33("option", { value: n, children: n }, n))
|
|
2963
|
+
}
|
|
2964
|
+
),
|
|
2965
|
+
/* @__PURE__ */ jsx33(
|
|
2966
|
+
ChevronDown9,
|
|
2967
|
+
{
|
|
2968
|
+
size: 13,
|
|
2969
|
+
"aria-hidden": "true",
|
|
2970
|
+
className: "pointer-events-none absolute right-2 top-1/2 -translate-y-1/2 text-text-secondary"
|
|
2971
|
+
}
|
|
2972
|
+
)
|
|
2973
|
+
] }),
|
|
2974
|
+
"items per page"
|
|
2975
|
+
] }),
|
|
2976
|
+
/* @__PURE__ */ jsxs29("nav", { className: "flex items-center gap-1", "aria-label": "Pagination", children: [
|
|
2977
|
+
/* @__PURE__ */ jsx33(
|
|
2978
|
+
"button",
|
|
2979
|
+
{
|
|
2980
|
+
type: "button",
|
|
2981
|
+
onClick: () => onPageChange(1),
|
|
2982
|
+
disabled: current === 1,
|
|
2983
|
+
"aria-label": "First page",
|
|
2984
|
+
className: stepBtn,
|
|
2985
|
+
children: /* @__PURE__ */ jsx33(ChevronFirst, { size: 16, "aria-hidden": "true" })
|
|
2986
|
+
}
|
|
2987
|
+
),
|
|
2988
|
+
/* @__PURE__ */ jsx33(
|
|
2989
|
+
"button",
|
|
2990
|
+
{
|
|
2991
|
+
type: "button",
|
|
2992
|
+
onClick: () => onPageChange(current - 1),
|
|
2993
|
+
disabled: current === 1,
|
|
2994
|
+
"aria-label": "Previous page",
|
|
2995
|
+
className: stepBtn,
|
|
2996
|
+
children: /* @__PURE__ */ jsx33(ChevronLeft4, { size: 16, "aria-hidden": "true" })
|
|
2997
|
+
}
|
|
2998
|
+
),
|
|
2999
|
+
pageWindow(current, totalPages).map(
|
|
3000
|
+
(p) => typeof p === "string" ? /* @__PURE__ */ jsx33("span", { className: "px-1 text-[13px] text-text-tertiary", children: "\u2026" }, p) : /* @__PURE__ */ jsx33(
|
|
3001
|
+
"button",
|
|
3002
|
+
{
|
|
3003
|
+
type: "button",
|
|
3004
|
+
onClick: () => onPageChange(p),
|
|
3005
|
+
"aria-current": p === current ? "page" : void 0,
|
|
3006
|
+
className: cn(
|
|
3007
|
+
btn,
|
|
3008
|
+
p === current ? "bg-brand-primaryAction font-semibold text-brand-onprimary" : "text-text-secondary hover:bg-subtle hover:text-text-primary"
|
|
3009
|
+
),
|
|
3010
|
+
children: p
|
|
3011
|
+
},
|
|
3012
|
+
p
|
|
3013
|
+
)
|
|
3014
|
+
),
|
|
3015
|
+
/* @__PURE__ */ jsx33(
|
|
3016
|
+
"button",
|
|
3017
|
+
{
|
|
3018
|
+
type: "button",
|
|
3019
|
+
onClick: () => onPageChange(current + 1),
|
|
3020
|
+
disabled: current === totalPages,
|
|
3021
|
+
"aria-label": "Next page",
|
|
3022
|
+
className: stepBtn,
|
|
3023
|
+
children: /* @__PURE__ */ jsx33(ChevronRight5, { size: 16, "aria-hidden": "true" })
|
|
3024
|
+
}
|
|
3025
|
+
),
|
|
3026
|
+
/* @__PURE__ */ jsx33(
|
|
3027
|
+
"button",
|
|
3028
|
+
{
|
|
3029
|
+
type: "button",
|
|
3030
|
+
onClick: () => onPageChange(totalPages),
|
|
3031
|
+
disabled: current === totalPages,
|
|
3032
|
+
"aria-label": "Last page",
|
|
3033
|
+
className: stepBtn,
|
|
3034
|
+
children: /* @__PURE__ */ jsx33(ChevronLast, { size: 16, "aria-hidden": "true" })
|
|
3035
|
+
}
|
|
3036
|
+
)
|
|
3037
|
+
] }),
|
|
3038
|
+
/* @__PURE__ */ jsxs29("span", { className: "text-[13px] text-text-tertiary tabular-nums", children: [
|
|
3039
|
+
rangeStart.toLocaleString(),
|
|
3040
|
+
" \u2013 ",
|
|
3041
|
+
rangeEnd.toLocaleString(),
|
|
3042
|
+
" of ",
|
|
3043
|
+
totalItems.toLocaleString(),
|
|
3044
|
+
" ",
|
|
3045
|
+
itemLabel
|
|
3046
|
+
] })
|
|
3047
|
+
] });
|
|
3048
|
+
}
|
|
3049
|
+
|
|
3050
|
+
// src/DateRangePicker.tsx
|
|
3051
|
+
import { useEffect as useEffect11, useMemo as useMemo4, useRef as useRef11, useState as useState12 } from "react";
|
|
3052
|
+
import { ChevronDown as ChevronDown10 } from "lucide-react";
|
|
3053
|
+
import { jsx as jsx34, jsxs as jsxs30 } from "react/jsx-runtime";
|
|
3054
|
+
var MONTH_ABBR = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
|
|
3055
|
+
var PRESETS = ["Rolling 3 months", "Rolling 6 months", "Rolling 12 months"];
|
|
3056
|
+
var startOfToday = () => {
|
|
3057
|
+
const d = /* @__PURE__ */ new Date();
|
|
3058
|
+
return new Date(d.getFullYear(), d.getMonth(), d.getDate());
|
|
3059
|
+
};
|
|
3060
|
+
var fmtDate = (d) => d.toLocaleDateString("en-US", { month: "short", day: "numeric", year: "numeric" });
|
|
3061
|
+
function resolveRange(label, today = startOfToday()) {
|
|
3062
|
+
const end = today;
|
|
3063
|
+
const mMonths = label.match(/Rolling (\d+) months/);
|
|
3064
|
+
if (mMonths) {
|
|
3065
|
+
return [new Date(end.getFullYear(), end.getMonth() - Number(mMonths[1]), 1), end];
|
|
3066
|
+
}
|
|
3067
|
+
const mQuarters = label.match(/Rolling (\d+) quarters/);
|
|
3068
|
+
if (mQuarters) {
|
|
3069
|
+
return [new Date(end.getFullYear(), end.getMonth() - Number(mQuarters[1]) * 3, 1), end];
|
|
3070
|
+
}
|
|
3071
|
+
const mYears = label.match(/Rolling (\d+) years/);
|
|
3072
|
+
if (mYears) {
|
|
3073
|
+
return [new Date(end.getFullYear() - Number(mYears[1]), end.getMonth(), 1), end];
|
|
3074
|
+
}
|
|
3075
|
+
const mCustom = label.match(/([A-Za-z]{3})\s+(\d{4}).*?([A-Za-z]{3})\s+(\d{4})/);
|
|
3076
|
+
if (mCustom) {
|
|
3077
|
+
const s = new Date(Number(mCustom[2]), MONTH_ABBR.indexOf(mCustom[1]), 1);
|
|
3078
|
+
const e = new Date(Number(mCustom[4]), MONTH_ABBR.indexOf(mCustom[3]) + 1, 0);
|
|
3079
|
+
return [s, e];
|
|
3080
|
+
}
|
|
3081
|
+
return [new Date(end.getFullYear(), end.getMonth() - 6, 1), end];
|
|
3082
|
+
}
|
|
3083
|
+
function spanLabel(label, today) {
|
|
3084
|
+
const [s, e] = resolveRange(label, today);
|
|
3085
|
+
return `${fmtDate(s)} \u2013 ${fmtDate(e)}`;
|
|
3086
|
+
}
|
|
3087
|
+
function MiniSelect({ value, onChange, options }) {
|
|
3088
|
+
return /* @__PURE__ */ jsx34(
|
|
3089
|
+
"select",
|
|
3090
|
+
{
|
|
3091
|
+
value: String(value),
|
|
3092
|
+
onChange: (e) => {
|
|
3093
|
+
const opt = options.find((o) => String(o.value) === e.target.value);
|
|
3094
|
+
if (opt) onChange(opt.value);
|
|
3095
|
+
},
|
|
3096
|
+
className: "rounded-control border border-border-default bg-input px-1.5 py-1 text-[12px] text-text-primary outline-hidden focus:border-border-focus",
|
|
3097
|
+
children: options.map((o) => /* @__PURE__ */ jsx34("option", { value: String(o.value), children: o.label }, String(o.value)))
|
|
3098
|
+
}
|
|
3099
|
+
);
|
|
3100
|
+
}
|
|
3101
|
+
function DateRangePicker({
|
|
3102
|
+
defaultValue = "Rolling 6 months",
|
|
3103
|
+
align = "left",
|
|
3104
|
+
presets = PRESETS,
|
|
3105
|
+
onChange,
|
|
3106
|
+
onRangeChange,
|
|
3107
|
+
showFooter = false,
|
|
3108
|
+
today,
|
|
3109
|
+
years
|
|
3110
|
+
}) {
|
|
3111
|
+
const end = useMemo4(() => today ?? startOfToday(), [today]);
|
|
3112
|
+
const yearOptions = years ?? [3, 2, 1, 0].map((n) => end.getFullYear() - n);
|
|
3113
|
+
const [open, setOpen] = useState12(false);
|
|
3114
|
+
const [value, setValue] = useState12(defaultValue);
|
|
3115
|
+
const [draft, setDraft] = useState12(defaultValue);
|
|
3116
|
+
const ref = useRef11(null);
|
|
3117
|
+
const [fromMo, setFromMo] = useState12(end.getMonth());
|
|
3118
|
+
const [fromYr, setFromYr] = useState12(end.getFullYear() - 1);
|
|
3119
|
+
const [toMo, setToMo] = useState12(end.getMonth());
|
|
3120
|
+
const [toYr, setToYr] = useState12(end.getFullYear());
|
|
3121
|
+
useEffect11(() => {
|
|
3122
|
+
const [s, e] = resolveRange(value, end);
|
|
3123
|
+
onRangeChange?.(s, e);
|
|
3124
|
+
}, [value]);
|
|
3125
|
+
const toggleOpen = () => {
|
|
3126
|
+
if (!open) setDraft(value);
|
|
3127
|
+
setOpen((o) => !o);
|
|
3128
|
+
};
|
|
3129
|
+
useEffect11(() => {
|
|
3130
|
+
if (!open) return;
|
|
3131
|
+
const onDown = (e) => {
|
|
3132
|
+
if (ref.current && !ref.current.contains(e.target)) setOpen(false);
|
|
3133
|
+
};
|
|
3134
|
+
const onKey = (e) => e.key === "Escape" && setOpen(false);
|
|
3135
|
+
window.addEventListener("mousedown", onDown);
|
|
3136
|
+
window.addEventListener("keydown", onKey);
|
|
3137
|
+
return () => {
|
|
3138
|
+
window.removeEventListener("mousedown", onDown);
|
|
3139
|
+
window.removeEventListener("keydown", onKey);
|
|
3140
|
+
};
|
|
3141
|
+
}, [open]);
|
|
3142
|
+
const commit = (label) => {
|
|
3143
|
+
setValue(label);
|
|
3144
|
+
onChange?.(label);
|
|
3145
|
+
setOpen(false);
|
|
3146
|
+
};
|
|
3147
|
+
const span = toYr * 12 + toMo - (fromYr * 12 + fromMo) + 1;
|
|
3148
|
+
const valid = span >= 1;
|
|
3149
|
+
const applyCustom = () => {
|
|
3150
|
+
if (!valid) return;
|
|
3151
|
+
const label = `${MONTH_ABBR[fromMo]} ${fromYr} \u2013 ${MONTH_ABBR[toMo]} ${toYr}`;
|
|
3152
|
+
if (showFooter) setDraft(label);
|
|
3153
|
+
else commit(label);
|
|
3154
|
+
};
|
|
3155
|
+
return /* @__PURE__ */ jsxs30("div", { className: "relative", ref, children: [
|
|
3156
|
+
/* @__PURE__ */ jsxs30(
|
|
3157
|
+
"button",
|
|
3158
|
+
{
|
|
3159
|
+
type: "button",
|
|
3160
|
+
onClick: toggleOpen,
|
|
3161
|
+
"aria-expanded": open,
|
|
3162
|
+
"aria-haspopup": "listbox",
|
|
3163
|
+
className: chipTriggerClass(),
|
|
3164
|
+
children: [
|
|
3165
|
+
/* @__PURE__ */ jsxs30("span", { className: "flex flex-col gap-1 text-left leading-none", children: [
|
|
3166
|
+
/* @__PURE__ */ jsx34("span", { className: "text-[10px] text-text-tertiary", children: spanLabel(value, end) }),
|
|
3167
|
+
/* @__PURE__ */ jsx34("span", { className: "text-[12px] font-medium text-text-primary", children: value })
|
|
3168
|
+
] }),
|
|
3169
|
+
/* @__PURE__ */ jsx34(ChevronDown10, { size: 14, "aria-hidden": "true", className: cn("shrink-0 text-text-secondary transition-transform", open && "rotate-180") })
|
|
3170
|
+
]
|
|
3171
|
+
}
|
|
3172
|
+
),
|
|
3173
|
+
open && /* @__PURE__ */ jsxs30("div", { className: cn("absolute top-[calc(100%+8px)] z-30 w-[280px] overflow-hidden rounded-card border border-border-default bg-surface py-1 shadow-overlay", align === "right" ? "right-0" : "left-0"), children: [
|
|
3174
|
+
presets.map((p) => /* @__PURE__ */ jsx34(
|
|
3175
|
+
"button",
|
|
3176
|
+
{
|
|
3177
|
+
type: "button",
|
|
3178
|
+
onClick: () => showFooter ? setDraft(p) : commit(p),
|
|
3179
|
+
className: cn("block w-full px-3 py-2 text-left text-[13px] transition-colors hover:bg-subtle", (showFooter ? draft : value) === p ? "font-semibold text-brand-primaryAction" : "text-text-secondary"),
|
|
3180
|
+
children: p
|
|
3181
|
+
},
|
|
3182
|
+
p
|
|
3183
|
+
)),
|
|
3184
|
+
/* @__PURE__ */ jsxs30("div", { className: "mt-1 border-t border-border-default px-3 pb-2 pt-2", children: [
|
|
3185
|
+
/* @__PURE__ */ jsx34("div", { className: "text-[13px] font-semibold text-text-primary", children: "Custom Range" }),
|
|
3186
|
+
/* @__PURE__ */ jsxs30("div", { className: "mt-2 flex flex-col gap-2", children: [
|
|
3187
|
+
/* @__PURE__ */ jsxs30("label", { className: "flex items-center justify-between gap-2 text-[12px] text-text-secondary", children: [
|
|
3188
|
+
"From",
|
|
3189
|
+
/* @__PURE__ */ jsxs30("span", { className: "flex gap-1", children: [
|
|
3190
|
+
/* @__PURE__ */ jsx34(MiniSelect, { value: fromMo, onChange: setFromMo, options: MONTH_ABBR.map((mm, i) => ({ label: mm, value: i })) }),
|
|
3191
|
+
/* @__PURE__ */ jsx34(MiniSelect, { value: fromYr, onChange: setFromYr, options: yearOptions.map((y) => ({ label: String(y), value: y })) })
|
|
3192
|
+
] })
|
|
3193
|
+
] }),
|
|
3194
|
+
/* @__PURE__ */ jsxs30("label", { className: "flex items-center justify-between gap-2 text-[12px] text-text-secondary", children: [
|
|
3195
|
+
"To",
|
|
3196
|
+
/* @__PURE__ */ jsxs30("span", { className: "flex gap-1", children: [
|
|
3197
|
+
/* @__PURE__ */ jsx34(MiniSelect, { value: toMo, onChange: setToMo, options: MONTH_ABBR.map((mm, i) => ({ label: mm, value: i })) }),
|
|
3198
|
+
/* @__PURE__ */ jsx34(MiniSelect, { value: toYr, onChange: setToYr, options: yearOptions.map((y) => ({ label: String(y), value: y })) })
|
|
3199
|
+
] })
|
|
3200
|
+
] }),
|
|
3201
|
+
/* @__PURE__ */ jsx34(
|
|
3202
|
+
"button",
|
|
3203
|
+
{
|
|
3204
|
+
type: "button",
|
|
3205
|
+
onClick: applyCustom,
|
|
3206
|
+
disabled: !valid,
|
|
3207
|
+
className: "self-start text-[13px] font-semibold text-brand-primaryAction transition-opacity hover:opacity-80 disabled:cursor-not-allowed disabled:opacity-40",
|
|
3208
|
+
children: showFooter ? "Use custom range" : "Apply"
|
|
3209
|
+
}
|
|
3210
|
+
)
|
|
3211
|
+
] })
|
|
3212
|
+
] }),
|
|
3213
|
+
showFooter && /* @__PURE__ */ jsx34(FilterDropdownFooter, { onApply: () => commit(draft) })
|
|
3214
|
+
] })
|
|
3215
|
+
] });
|
|
3216
|
+
}
|
|
3217
|
+
|
|
3218
|
+
// src/MultiSelectFilter.tsx
|
|
3219
|
+
import { useCallback as useCallback4, useEffect as useEffect12, useRef as useRef12, useState as useState13 } from "react";
|
|
3220
|
+
import { ChevronDown as ChevronDown11, Check as Check7, Minus as Minus2, Search as Search5, X as X4 } from "lucide-react";
|
|
3221
|
+
import { jsx as jsx35, jsxs as jsxs31 } from "react/jsx-runtime";
|
|
3222
|
+
function MultiSelectFilter({
|
|
3223
|
+
caption,
|
|
3224
|
+
options = [],
|
|
3225
|
+
groups,
|
|
3226
|
+
selected,
|
|
3227
|
+
onChange,
|
|
3228
|
+
allLabel = "All",
|
|
3229
|
+
searchable = false,
|
|
3230
|
+
searchPlaceholder,
|
|
3231
|
+
align = "left",
|
|
3232
|
+
fullIsAll = false,
|
|
3233
|
+
min = 0,
|
|
3234
|
+
icon,
|
|
3235
|
+
showFooter = false,
|
|
3236
|
+
clearValue = [],
|
|
3237
|
+
menuWidthClass = "w-60",
|
|
3238
|
+
plainParent = false,
|
|
3239
|
+
fullWidth = false
|
|
3240
|
+
}) {
|
|
3241
|
+
const groupLeaves = (g) => g.children.length ? g.children : [{ id: g.id, label: g.label }];
|
|
3242
|
+
const leafOptions = groups ? groups.flatMap(groupLeaves) : options;
|
|
3243
|
+
const [open, setOpen] = useState13(false);
|
|
3244
|
+
const [q, setQ] = useState13("");
|
|
3245
|
+
const [draft, setDraft] = useState13(selected);
|
|
3246
|
+
const ref = useRef12(null);
|
|
3247
|
+
const close = useCallback4(() => {
|
|
3248
|
+
setOpen(false);
|
|
3249
|
+
setQ("");
|
|
3250
|
+
if (showFooter) return;
|
|
3251
|
+
if (draft.length !== selected.length || draft.some((id) => !selected.includes(id))) {
|
|
3252
|
+
onChange(draft);
|
|
3253
|
+
}
|
|
3254
|
+
}, [draft, selected, onChange, showFooter]);
|
|
3255
|
+
const openTray = () => {
|
|
3256
|
+
setDraft(selected);
|
|
3257
|
+
setOpen(true);
|
|
3258
|
+
};
|
|
3259
|
+
const apply = () => {
|
|
3260
|
+
onChange(draft);
|
|
3261
|
+
setOpen(false);
|
|
3262
|
+
setQ("");
|
|
3263
|
+
};
|
|
3264
|
+
useEffect12(() => {
|
|
3265
|
+
if (!open) return;
|
|
3266
|
+
const onKey = (e) => e.key === "Escape" && close();
|
|
3267
|
+
const onClick = (e) => {
|
|
3268
|
+
if (ref.current && !ref.current.contains(e.target)) close();
|
|
3269
|
+
};
|
|
3270
|
+
window.addEventListener("keydown", onKey);
|
|
3271
|
+
window.addEventListener("mousedown", onClick);
|
|
3272
|
+
return () => {
|
|
3273
|
+
window.removeEventListener("keydown", onKey);
|
|
3274
|
+
window.removeEventListener("mousedown", onClick);
|
|
3275
|
+
};
|
|
3276
|
+
}, [open, close]);
|
|
3277
|
+
const toggle = (id) => setDraft((cur) => {
|
|
3278
|
+
const next = cur.includes(id) ? cur.filter((x) => x !== id) : [...cur, id];
|
|
3279
|
+
if (next.length < min) return cur;
|
|
3280
|
+
return next;
|
|
3281
|
+
});
|
|
3282
|
+
const toggleGroup = (g) => setDraft((cur) => {
|
|
3283
|
+
const ids = groupLeaves(g).map((c) => c.id);
|
|
3284
|
+
const allSel = ids.every((id) => cur.includes(id));
|
|
3285
|
+
const next = allSel ? cur.filter((id) => !ids.includes(id)) : [.../* @__PURE__ */ new Set([...cur, ...ids])];
|
|
3286
|
+
if (next.length < min) return cur;
|
|
3287
|
+
return next;
|
|
3288
|
+
});
|
|
3289
|
+
const groupState = (g) => {
|
|
3290
|
+
const ids = groupLeaves(g).map((c) => c.id);
|
|
3291
|
+
const sel = ids.filter((id) => draft.includes(id)).length;
|
|
3292
|
+
return sel === 0 ? "none" : sel === ids.length ? "all" : "some";
|
|
3293
|
+
};
|
|
3294
|
+
const shown = open && !showFooter ? draft : selected;
|
|
3295
|
+
const isAll = shown.length === 0 || fullIsAll && shown.length === leafOptions.length;
|
|
3296
|
+
const value = isAll ? allLabel : shown.length === 1 ? leafOptions.find((o) => o.id === shown[0])?.label ?? `${shown.length} selected` : `${shown.length} selected`;
|
|
3297
|
+
const ql = q.trim().toLowerCase();
|
|
3298
|
+
const items = options.filter((o) => !ql || o.label.toLowerCase().includes(ql));
|
|
3299
|
+
const visibleGroups = groups ? groups.map((g) => {
|
|
3300
|
+
if (!ql || g.label.toLowerCase().includes(ql)) return g;
|
|
3301
|
+
const kids = g.children.filter((c) => c.label.toLowerCase().includes(ql));
|
|
3302
|
+
return kids.length ? { ...g, children: kids } : null;
|
|
3303
|
+
}).filter((g) => g !== null) : [];
|
|
3304
|
+
return /* @__PURE__ */ jsxs31("div", { className: cn("relative", fullWidth && "w-full"), ref, children: [
|
|
3305
|
+
/* @__PURE__ */ jsxs31(
|
|
3306
|
+
"button",
|
|
3307
|
+
{
|
|
3308
|
+
type: "button",
|
|
3309
|
+
onClick: () => open ? close() : openTray(),
|
|
3310
|
+
"aria-expanded": open,
|
|
3311
|
+
className: cn(chipTriggerClass(), fullWidth && "w-full justify-between"),
|
|
3312
|
+
children: [
|
|
3313
|
+
/* @__PURE__ */ jsx35(ChipContent, { caption, value, icon }),
|
|
3314
|
+
/* @__PURE__ */ jsx35(
|
|
3315
|
+
ChevronDown11,
|
|
3316
|
+
{
|
|
3317
|
+
size: 14,
|
|
3318
|
+
"aria-hidden": "true",
|
|
3319
|
+
className: cn("shrink-0 text-text-secondary transition-transform", open && "rotate-180")
|
|
3320
|
+
}
|
|
3321
|
+
)
|
|
3322
|
+
]
|
|
3323
|
+
}
|
|
3324
|
+
),
|
|
3325
|
+
open && /* @__PURE__ */ jsxs31(
|
|
3326
|
+
"div",
|
|
3327
|
+
{
|
|
3328
|
+
className: cn(
|
|
3329
|
+
"absolute top-[calc(100%+8px)] z-30 flex flex-col overflow-hidden rounded-card border border-border-default bg-surface shadow-overlay",
|
|
3330
|
+
menuWidthClass,
|
|
3331
|
+
align === "right" ? "right-0" : "left-0"
|
|
3332
|
+
),
|
|
3333
|
+
role: "dialog",
|
|
3334
|
+
"aria-label": caption,
|
|
3335
|
+
children: [
|
|
3336
|
+
searchable && /* @__PURE__ */ jsx35("div", { className: "border-b border-border-default p-2", children: /* @__PURE__ */ jsxs31("div", { className: "flex h-9 items-center gap-2 rounded-control border border-border-default bg-input px-2.5", children: [
|
|
3337
|
+
/* @__PURE__ */ jsx35(Search5, { size: 14, className: "text-text-secondary", "aria-hidden": "true" }),
|
|
3338
|
+
/* @__PURE__ */ jsx35(
|
|
3339
|
+
"input",
|
|
3340
|
+
{
|
|
3341
|
+
type: "text",
|
|
3342
|
+
value: q,
|
|
3343
|
+
onChange: (e) => setQ(e.target.value),
|
|
3344
|
+
placeholder: searchPlaceholder ?? `Search ${caption.toLowerCase()}`,
|
|
3345
|
+
className: "h-full flex-1 bg-transparent text-[13px] outline-hidden placeholder:text-text-tertiary"
|
|
3346
|
+
}
|
|
3347
|
+
),
|
|
3348
|
+
q && /* @__PURE__ */ jsx35("button", { type: "button", onClick: () => setQ(""), "aria-label": "Clear search", className: "text-text-tertiary hover:text-text-primary", children: /* @__PURE__ */ jsx35(X4, { size: 13 }) })
|
|
3349
|
+
] }) }),
|
|
3350
|
+
/* @__PURE__ */ jsxs31("div", { className: "max-h-[280px] overflow-y-auto p-1", children: [
|
|
3351
|
+
groups ? visibleGroups.map((g) => {
|
|
3352
|
+
const state = groupState(g);
|
|
3353
|
+
return /* @__PURE__ */ jsxs31("div", { children: [
|
|
3354
|
+
/* @__PURE__ */ jsxs31(
|
|
3355
|
+
"button",
|
|
3356
|
+
{
|
|
3357
|
+
type: "button",
|
|
3358
|
+
onClick: () => toggleGroup(g),
|
|
3359
|
+
className: "flex w-full items-center gap-2.5 rounded-control px-2 py-2 text-left transition-colors hover:bg-subtle",
|
|
3360
|
+
children: [
|
|
3361
|
+
/* @__PURE__ */ jsx35(TriCheck, { state }),
|
|
3362
|
+
/* @__PURE__ */ jsx35("span", { className: cn("flex-1 text-[14px] text-text-primary", !plainParent && "font-semibold"), children: g.label })
|
|
3363
|
+
]
|
|
3364
|
+
}
|
|
3365
|
+
),
|
|
3366
|
+
g.children.map((c) => {
|
|
3367
|
+
const checked = draft.includes(c.id);
|
|
3368
|
+
return /* @__PURE__ */ jsxs31(
|
|
3369
|
+
"button",
|
|
3370
|
+
{
|
|
3371
|
+
type: "button",
|
|
3372
|
+
onClick: () => toggle(c.id),
|
|
3373
|
+
className: "flex w-full items-center gap-2.5 rounded-control py-2 pl-9 pr-2 text-left transition-colors hover:bg-subtle",
|
|
3374
|
+
children: [
|
|
3375
|
+
/* @__PURE__ */ jsx35(TriCheck, { state: checked ? "all" : "none" }),
|
|
3376
|
+
/* @__PURE__ */ jsx35("span", { className: "flex-1 text-[14px] text-text-primary", children: c.label })
|
|
3377
|
+
]
|
|
3378
|
+
},
|
|
3379
|
+
c.id
|
|
3380
|
+
);
|
|
3381
|
+
})
|
|
3382
|
+
] }, g.id);
|
|
3383
|
+
}) : items.map((o) => {
|
|
3384
|
+
const checked = draft.includes(o.id);
|
|
3385
|
+
return /* @__PURE__ */ jsxs31(
|
|
3386
|
+
"button",
|
|
3387
|
+
{
|
|
3388
|
+
type: "button",
|
|
3389
|
+
onClick: () => toggle(o.id),
|
|
3390
|
+
className: "flex w-full items-center gap-2.5 rounded-control px-2 py-2 text-left transition-colors hover:bg-subtle",
|
|
3391
|
+
children: [
|
|
3392
|
+
/* @__PURE__ */ jsx35(
|
|
3393
|
+
"span",
|
|
3394
|
+
{
|
|
3395
|
+
className: cn(
|
|
3396
|
+
"flex h-[18px] w-[18px] shrink-0 items-center justify-center rounded-[5px] border transition-colors",
|
|
3397
|
+
checked ? "border-transparent bg-brand-primaryAction text-brand-onprimary" : "border-border-interactive bg-input"
|
|
3398
|
+
),
|
|
3399
|
+
children: checked && /* @__PURE__ */ jsx35(Check7, { size: 13, strokeWidth: 3 })
|
|
3400
|
+
}
|
|
3401
|
+
),
|
|
3402
|
+
/* @__PURE__ */ jsx35("span", { className: "flex-1 text-[14px] text-text-primary", children: o.label })
|
|
3403
|
+
]
|
|
3404
|
+
},
|
|
3405
|
+
o.id
|
|
3406
|
+
);
|
|
3407
|
+
}),
|
|
3408
|
+
(groups && !visibleGroups.length || !groups && !items.length) && /* @__PURE__ */ jsx35("div", { className: "py-6 text-center text-[13px] text-text-tertiary", children: "No matches." })
|
|
3409
|
+
] }),
|
|
3410
|
+
showFooter ? /* @__PURE__ */ jsx35(FilterDropdownFooter, { onClear: () => setDraft(clearValue), onApply: apply }) : min === 0 && draft.length > 0 && /* @__PURE__ */ jsx35("div", { className: "border-t border-border-default p-2", children: /* @__PURE__ */ jsxs31(
|
|
3411
|
+
"button",
|
|
3412
|
+
{
|
|
3413
|
+
type: "button",
|
|
3414
|
+
onClick: () => setDraft([]),
|
|
3415
|
+
className: "text-[12px] font-medium text-text-link hover:underline",
|
|
3416
|
+
children: [
|
|
3417
|
+
"Clear ",
|
|
3418
|
+
caption.toLowerCase()
|
|
3419
|
+
]
|
|
3420
|
+
}
|
|
3421
|
+
) })
|
|
3422
|
+
]
|
|
3423
|
+
}
|
|
3424
|
+
)
|
|
3425
|
+
] });
|
|
3426
|
+
}
|
|
3427
|
+
function TriCheck({ state }) {
|
|
3428
|
+
const filled = state !== "none";
|
|
3429
|
+
return /* @__PURE__ */ jsxs31(
|
|
3430
|
+
"span",
|
|
3431
|
+
{
|
|
3432
|
+
className: cn(
|
|
3433
|
+
"flex h-[18px] w-[18px] shrink-0 items-center justify-center rounded-[5px] border transition-colors",
|
|
3434
|
+
filled ? "border-transparent bg-brand-primaryAction text-brand-onprimary" : "border-border-interactive bg-input"
|
|
3435
|
+
),
|
|
3436
|
+
children: [
|
|
3437
|
+
state === "all" && /* @__PURE__ */ jsx35(Check7, { size: 13, strokeWidth: 3 }),
|
|
3438
|
+
state === "some" && /* @__PURE__ */ jsx35(Minus2, { size: 13, strokeWidth: 3 })
|
|
3439
|
+
]
|
|
3440
|
+
}
|
|
3441
|
+
);
|
|
3442
|
+
}
|
|
3443
|
+
|
|
3444
|
+
// src/Toggle.tsx
|
|
3445
|
+
import { jsx as jsx36 } from "react/jsx-runtime";
|
|
3446
|
+
var SIZES2 = {
|
|
3447
|
+
sm: { track: "h-5 w-9", knob: "h-4 w-4", on: "translate-x-[18px]" },
|
|
3448
|
+
md: { track: "h-6 w-11", knob: "h-5 w-5", on: "translate-x-[22px]" }
|
|
3449
|
+
};
|
|
3450
|
+
function Toggle({ checked, onChange, label, size = "md", disabled, className }) {
|
|
3451
|
+
const s = SIZES2[size];
|
|
3452
|
+
return /* @__PURE__ */ jsx36(
|
|
3453
|
+
"button",
|
|
3454
|
+
{
|
|
3455
|
+
type: "button",
|
|
3456
|
+
role: "switch",
|
|
3457
|
+
"aria-checked": checked,
|
|
3458
|
+
"aria-label": label,
|
|
3459
|
+
disabled,
|
|
3460
|
+
onClick: () => onChange(!checked),
|
|
3461
|
+
className: cn(
|
|
3462
|
+
"relative inline-flex shrink-0 items-center rounded-full transition-colors focus-visible:outline-solid focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-border-focus",
|
|
3463
|
+
s.track,
|
|
3464
|
+
checked ? "bg-brand-primaryAction" : "bg-border-interactive",
|
|
3465
|
+
disabled && "cursor-not-allowed opacity-50",
|
|
3466
|
+
className
|
|
3467
|
+
),
|
|
3468
|
+
children: /* @__PURE__ */ jsx36(
|
|
3469
|
+
"span",
|
|
3470
|
+
{
|
|
3471
|
+
className: cn(
|
|
3472
|
+
"inline-block transform rounded-full bg-surface shadow-raised transition-transform",
|
|
3473
|
+
s.knob,
|
|
3474
|
+
checked ? s.on : "translate-x-0.5"
|
|
3475
|
+
)
|
|
3476
|
+
}
|
|
3477
|
+
)
|
|
3478
|
+
}
|
|
3479
|
+
);
|
|
3480
|
+
}
|
|
3481
|
+
|
|
3482
|
+
// src/AISpark.tsx
|
|
3483
|
+
import { useId as useId2 } from "react";
|
|
3484
|
+
import { Sparkles } from "lucide-react";
|
|
3485
|
+
import { Fragment as Fragment5, jsx as jsx37, jsxs as jsxs32 } from "react/jsx-runtime";
|
|
3486
|
+
function AISpark({ size = 16, className }) {
|
|
3487
|
+
const id = useId2();
|
|
3488
|
+
return /* @__PURE__ */ jsxs32(Fragment5, { children: [
|
|
3489
|
+
/* @__PURE__ */ jsx37("svg", { width: "0", height: "0", "aria-hidden": "true", style: { position: "absolute" }, children: /* @__PURE__ */ jsx37("defs", { children: /* @__PURE__ */ jsxs32("linearGradient", { id, x1: "0", y1: "0", x2: "1", y2: "1", children: [
|
|
3490
|
+
/* @__PURE__ */ jsx37("stop", { offset: "0%", stopColor: "var(--mr-color-brand-primary)" }),
|
|
3491
|
+
/* @__PURE__ */ jsx37("stop", { offset: "100%", className: "[stop-color:#9474ff] in-data-[theme=dark]:[stop-color:#a488ff]" })
|
|
3492
|
+
] }) }) }),
|
|
3493
|
+
/* @__PURE__ */ jsx37(Sparkles, { size, stroke: `url(#${id})`, className, "aria-hidden": "true" })
|
|
3494
|
+
] });
|
|
3495
|
+
}
|
|
3496
|
+
|
|
3497
|
+
// src/Freshness.tsx
|
|
3498
|
+
import { jsx as jsx38 } from "react/jsx-runtime";
|
|
3499
|
+
var dotColor = {
|
|
3500
|
+
fresh: "bg-status-success",
|
|
3501
|
+
recent: "bg-status-warning",
|
|
3502
|
+
stale: "bg-text-tertiary"
|
|
3503
|
+
};
|
|
3504
|
+
var srLabel = {
|
|
3505
|
+
fresh: "Fresh (under 4 hours)",
|
|
3506
|
+
recent: "Recent (today)",
|
|
3507
|
+
stale: "Older than a day"
|
|
3508
|
+
};
|
|
3509
|
+
function Freshness({ level }) {
|
|
3510
|
+
return /* @__PURE__ */ jsx38(
|
|
3511
|
+
"span",
|
|
3512
|
+
{
|
|
3513
|
+
className: cn("inline-block h-1.5 w-1.5 shrink-0 rounded-full", dotColor[level]),
|
|
3514
|
+
role: "img",
|
|
3515
|
+
"aria-label": srLabel[level]
|
|
3516
|
+
}
|
|
3517
|
+
);
|
|
3518
|
+
}
|
|
2555
3519
|
export {
|
|
3520
|
+
AISpark,
|
|
2556
3521
|
AdvertiserNoResults,
|
|
2557
3522
|
AdvertiserQuickFilter,
|
|
2558
3523
|
AdvertiserSearchPrompt,
|
|
2559
3524
|
BarChartPlaceholder,
|
|
3525
|
+
Button,
|
|
2560
3526
|
Card,
|
|
2561
3527
|
CheckBox,
|
|
3528
|
+
ChipContent,
|
|
2562
3529
|
DEFAULT_SEARCH_MATCH,
|
|
2563
3530
|
DataScopeFilter,
|
|
2564
3531
|
DateRangeFilter,
|
|
3532
|
+
DateRangePicker,
|
|
3533
|
+
FilterChipBar,
|
|
3534
|
+
FilterDropdownFooter,
|
|
3535
|
+
Freshness,
|
|
2565
3536
|
InfoTip,
|
|
2566
3537
|
LineChartPlaceholder,
|
|
3538
|
+
MultiSelectFilter,
|
|
2567
3539
|
NO_RESULTS_BODY,
|
|
2568
3540
|
NO_RESULTS_TITLE,
|
|
3541
|
+
NameListModal,
|
|
2569
3542
|
NoResultsEmptyState,
|
|
2570
3543
|
PRIMARY_ACTION_CLASS,
|
|
3544
|
+
Pager,
|
|
2571
3545
|
PieChartPlaceholder,
|
|
2572
3546
|
PublisherBreakdownList,
|
|
2573
3547
|
QUIET_ACTION_CLASS,
|
|
@@ -2577,9 +3551,19 @@ export {
|
|
|
2577
3551
|
SelectFilter,
|
|
2578
3552
|
SelectedSection,
|
|
2579
3553
|
SelectionPicker,
|
|
3554
|
+
SortButton,
|
|
2580
3555
|
StackedColumnPlaceholder,
|
|
3556
|
+
TablePagination,
|
|
3557
|
+
Tag,
|
|
3558
|
+
Toggle,
|
|
2581
3559
|
Tooltip,
|
|
3560
|
+
WorkspaceAvatar,
|
|
2582
3561
|
WorkspaceSelectedCard,
|
|
2583
|
-
|
|
3562
|
+
buttonVariants,
|
|
3563
|
+
chipTriggerClass,
|
|
3564
|
+
cycleSort,
|
|
3565
|
+
noResultsBody,
|
|
3566
|
+
resolveRange,
|
|
3567
|
+
sortProp
|
|
2584
3568
|
};
|
|
2585
3569
|
//# sourceMappingURL=index.js.map
|