@tollerud/ui 1.0.7 → 1.1.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 +4 -5
- package/dist/index.cjs +1182 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +251 -1
- package/dist/index.d.ts +251 -1
- package/dist/index.js +1163 -3
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
'use client';
|
|
1
2
|
'use strict';
|
|
2
3
|
|
|
3
4
|
var clsx = require('clsx');
|
|
@@ -2675,22 +2676,1190 @@ function MetricBadge({ label, value }) {
|
|
|
2675
2676
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "float-right text-tollerud-text-primary font-mono", children: value })
|
|
2676
2677
|
] });
|
|
2677
2678
|
}
|
|
2679
|
+
var Divider = React2.forwardRef(
|
|
2680
|
+
({ className, orientation = "horizontal", label, ...props }, ref) => {
|
|
2681
|
+
if (orientation === "vertical") {
|
|
2682
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
2683
|
+
"div",
|
|
2684
|
+
{
|
|
2685
|
+
ref,
|
|
2686
|
+
role: "separator",
|
|
2687
|
+
"aria-orientation": "vertical",
|
|
2688
|
+
className: cn("w-px self-stretch bg-tollerud-border", className),
|
|
2689
|
+
...props
|
|
2690
|
+
}
|
|
2691
|
+
);
|
|
2692
|
+
}
|
|
2693
|
+
if (label) {
|
|
2694
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
2695
|
+
"div",
|
|
2696
|
+
{
|
|
2697
|
+
ref,
|
|
2698
|
+
role: "separator",
|
|
2699
|
+
"aria-orientation": "horizontal",
|
|
2700
|
+
className: cn("flex items-center gap-3 text-xs text-tollerud-text-muted", className),
|
|
2701
|
+
...props,
|
|
2702
|
+
children: [
|
|
2703
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "h-px flex-1 bg-tollerud-border" }),
|
|
2704
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: label }),
|
|
2705
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "h-px flex-1 bg-tollerud-border" })
|
|
2706
|
+
]
|
|
2707
|
+
}
|
|
2708
|
+
);
|
|
2709
|
+
}
|
|
2710
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
2711
|
+
"div",
|
|
2712
|
+
{
|
|
2713
|
+
ref,
|
|
2714
|
+
role: "separator",
|
|
2715
|
+
"aria-orientation": "horizontal",
|
|
2716
|
+
className: cn("h-px w-full bg-tollerud-border", className),
|
|
2717
|
+
...props
|
|
2718
|
+
}
|
|
2719
|
+
);
|
|
2720
|
+
}
|
|
2721
|
+
);
|
|
2722
|
+
Divider.displayName = "Divider";
|
|
2723
|
+
var pillVariants = {
|
|
2724
|
+
outline: "bg-transparent border border-tollerud-border text-tollerud-text-secondary",
|
|
2725
|
+
solid: "bg-tollerud-surface-raised text-tollerud-text-primary",
|
|
2726
|
+
accent: "bg-tollerud-yellow/15 border border-tollerud-yellow/30 text-tollerud-yellow"
|
|
2727
|
+
};
|
|
2728
|
+
var Pill = React2.forwardRef(
|
|
2729
|
+
({ className, variant = "outline", ...props }, ref) => {
|
|
2730
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
2731
|
+
"span",
|
|
2732
|
+
{
|
|
2733
|
+
ref,
|
|
2734
|
+
className: cn(
|
|
2735
|
+
"inline-flex items-center gap-1 px-2.5 py-1 text-xs font-medium rounded-full leading-none",
|
|
2736
|
+
pillVariants[variant],
|
|
2737
|
+
className
|
|
2738
|
+
),
|
|
2739
|
+
...props
|
|
2740
|
+
}
|
|
2741
|
+
);
|
|
2742
|
+
}
|
|
2743
|
+
);
|
|
2744
|
+
Pill.displayName = "Pill";
|
|
2745
|
+
var avatarSizes = {
|
|
2746
|
+
sm: "h-6 w-6 text-[10px]",
|
|
2747
|
+
md: "h-8 w-8 text-xs",
|
|
2748
|
+
lg: "h-11 w-11 text-sm"
|
|
2749
|
+
};
|
|
2750
|
+
function initialsFrom(name) {
|
|
2751
|
+
const parts = name.trim().split(/\s+/).filter(Boolean);
|
|
2752
|
+
if (parts.length === 0) return "";
|
|
2753
|
+
if (parts.length === 1) return parts[0].slice(0, 2).toUpperCase();
|
|
2754
|
+
return (parts[0][0] + parts[parts.length - 1][0]).toUpperCase();
|
|
2755
|
+
}
|
|
2756
|
+
var Avatar = React2.forwardRef(
|
|
2757
|
+
({ className, src, name, fallback, size = "md", ...props }, ref) => {
|
|
2758
|
+
const [errored, setErrored] = React2.useState(false);
|
|
2759
|
+
const showImage = !!src && !errored;
|
|
2760
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
2761
|
+
"span",
|
|
2762
|
+
{
|
|
2763
|
+
ref,
|
|
2764
|
+
className: cn(
|
|
2765
|
+
"relative inline-flex shrink-0 items-center justify-center overflow-hidden rounded-full select-none",
|
|
2766
|
+
"bg-tollerud-surface-raised text-tollerud-text-secondary font-medium",
|
|
2767
|
+
"ring-1 ring-tollerud-border",
|
|
2768
|
+
avatarSizes[size],
|
|
2769
|
+
className
|
|
2770
|
+
),
|
|
2771
|
+
...props,
|
|
2772
|
+
children: showImage ? (
|
|
2773
|
+
// eslint-disable-next-line @next/next/no-img-element
|
|
2774
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2775
|
+
"img",
|
|
2776
|
+
{
|
|
2777
|
+
src,
|
|
2778
|
+
alt: name ?? "",
|
|
2779
|
+
className: "h-full w-full object-cover",
|
|
2780
|
+
onError: () => setErrored(true)
|
|
2781
|
+
}
|
|
2782
|
+
)
|
|
2783
|
+
) : /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": !!name, children: fallback ?? (name ? initialsFrom(name) : null) })
|
|
2784
|
+
}
|
|
2785
|
+
);
|
|
2786
|
+
}
|
|
2787
|
+
);
|
|
2788
|
+
Avatar.displayName = "Avatar";
|
|
2789
|
+
var AvatarGroup = React2.forwardRef(
|
|
2790
|
+
({ className, max, size = "md", children, ...props }, ref) => {
|
|
2791
|
+
const items = Array.isArray(children) ? children : [children];
|
|
2792
|
+
const visible = max ? items.slice(0, max) : items;
|
|
2793
|
+
const overflow = max ? items.length - max : 0;
|
|
2794
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
2795
|
+
"div",
|
|
2796
|
+
{
|
|
2797
|
+
ref,
|
|
2798
|
+
className: cn("flex items-center -space-x-2", className),
|
|
2799
|
+
...props,
|
|
2800
|
+
children: [
|
|
2801
|
+
visible.map((child, i) => /* @__PURE__ */ jsxRuntime.jsx("span", { className: "ring-2 ring-tollerud-surface rounded-full", children: child }, i)),
|
|
2802
|
+
overflow > 0 && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
2803
|
+
"span",
|
|
2804
|
+
{
|
|
2805
|
+
className: cn(
|
|
2806
|
+
"relative inline-flex shrink-0 items-center justify-center rounded-full select-none",
|
|
2807
|
+
"bg-tollerud-surface-raised text-tollerud-text-muted font-medium",
|
|
2808
|
+
"ring-2 ring-tollerud-surface",
|
|
2809
|
+
avatarSizes[size]
|
|
2810
|
+
),
|
|
2811
|
+
children: [
|
|
2812
|
+
"+",
|
|
2813
|
+
overflow
|
|
2814
|
+
]
|
|
2815
|
+
}
|
|
2816
|
+
)
|
|
2817
|
+
]
|
|
2818
|
+
}
|
|
2819
|
+
);
|
|
2820
|
+
}
|
|
2821
|
+
);
|
|
2822
|
+
AvatarGroup.displayName = "AvatarGroup";
|
|
2823
|
+
var Breadcrumb = React2.forwardRef(
|
|
2824
|
+
({ className, items, separator, ...props }, ref) => {
|
|
2825
|
+
return /* @__PURE__ */ jsxRuntime.jsx("nav", { ref, "aria-label": "Breadcrumb", className: cn("text-sm", className), ...props, children: /* @__PURE__ */ jsxRuntime.jsx("ol", { className: "flex items-center gap-1.5 flex-wrap", children: items.map((item, i) => {
|
|
2826
|
+
const isLast = i === items.length - 1;
|
|
2827
|
+
const content = !isLast && (item.href || item.onClick) ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
2828
|
+
"a",
|
|
2829
|
+
{
|
|
2830
|
+
href: item.href,
|
|
2831
|
+
onClick: item.onClick,
|
|
2832
|
+
className: "text-tollerud-text-secondary hover:text-tollerud-text-primary transition-colors duration-[150ms]",
|
|
2833
|
+
children: item.label
|
|
2834
|
+
}
|
|
2835
|
+
) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
2836
|
+
"span",
|
|
2837
|
+
{
|
|
2838
|
+
"aria-current": isLast ? "page" : void 0,
|
|
2839
|
+
className: isLast ? "text-tollerud-text-primary font-medium" : "text-tollerud-text-secondary",
|
|
2840
|
+
children: item.label
|
|
2841
|
+
}
|
|
2842
|
+
);
|
|
2843
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(React2.Fragment, { children: [
|
|
2844
|
+
/* @__PURE__ */ jsxRuntime.jsx("li", { className: "flex items-center gap-1.5", children: content }),
|
|
2845
|
+
!isLast && /* @__PURE__ */ jsxRuntime.jsx("li", { "aria-hidden": "true", className: "flex items-center text-tollerud-text-muted", children: separator ?? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronRight, { size: 14 }) })
|
|
2846
|
+
] }, i);
|
|
2847
|
+
}) }) });
|
|
2848
|
+
}
|
|
2849
|
+
);
|
|
2850
|
+
Breadcrumb.displayName = "Breadcrumb";
|
|
2851
|
+
function getPageRange(page, pageCount, siblingCount) {
|
|
2852
|
+
const totalVisible = siblingCount * 2 + 5;
|
|
2853
|
+
if (pageCount <= totalVisible) {
|
|
2854
|
+
return Array.from({ length: pageCount }, (_, i) => i + 1);
|
|
2855
|
+
}
|
|
2856
|
+
const left = Math.max(page - siblingCount, 1);
|
|
2857
|
+
const right = Math.min(page + siblingCount, pageCount);
|
|
2858
|
+
const range = [1];
|
|
2859
|
+
if (left > 2) range.push("ellipsis");
|
|
2860
|
+
for (let p = left === 1 ? 2 : left; p <= (right === pageCount ? pageCount - 1 : right); p++) {
|
|
2861
|
+
range.push(p);
|
|
2862
|
+
}
|
|
2863
|
+
if (right < pageCount - 1) range.push("ellipsis");
|
|
2864
|
+
if (pageCount > 1) range.push(pageCount);
|
|
2865
|
+
return range;
|
|
2866
|
+
}
|
|
2867
|
+
var navButtonClasses = "inline-flex h-8 min-w-8 items-center justify-center rounded px-2 text-sm transition-colors duration-[150ms] disabled:opacity-40 disabled:pointer-events-none";
|
|
2868
|
+
var Pagination = React2.forwardRef(
|
|
2869
|
+
({ className, page, pageCount, onChange, siblingCount = 1, ...props }, ref) => {
|
|
2870
|
+
const range = getPageRange(page, pageCount, siblingCount);
|
|
2871
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("nav", { ref, "aria-label": "Pagination", className: cn("flex items-center gap-1", className), ...props, children: [
|
|
2872
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2873
|
+
"button",
|
|
2874
|
+
{
|
|
2875
|
+
type: "button",
|
|
2876
|
+
"aria-label": "Previous page",
|
|
2877
|
+
disabled: page <= 1,
|
|
2878
|
+
onClick: () => onChange(page - 1),
|
|
2879
|
+
className: cn(navButtonClasses, "text-tollerud-text-secondary hover:bg-tollerud-surface-hover"),
|
|
2880
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronLeft, { size: 16 })
|
|
2881
|
+
}
|
|
2882
|
+
),
|
|
2883
|
+
range.map(
|
|
2884
|
+
(item, i) => item === "ellipsis" ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "inline-flex h-8 w-8 items-center justify-center text-tollerud-text-muted", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.MoreHorizontal, { size: 16 }) }, `e-${i}`) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
2885
|
+
"button",
|
|
2886
|
+
{
|
|
2887
|
+
type: "button",
|
|
2888
|
+
"aria-current": item === page ? "page" : void 0,
|
|
2889
|
+
onClick: () => onChange(item),
|
|
2890
|
+
className: cn(
|
|
2891
|
+
navButtonClasses,
|
|
2892
|
+
item === page ? "bg-tollerud-yellow text-tollerud-noir-black font-medium" : "text-tollerud-text-secondary hover:bg-tollerud-surface-hover"
|
|
2893
|
+
),
|
|
2894
|
+
children: item
|
|
2895
|
+
},
|
|
2896
|
+
item
|
|
2897
|
+
)
|
|
2898
|
+
),
|
|
2899
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2900
|
+
"button",
|
|
2901
|
+
{
|
|
2902
|
+
type: "button",
|
|
2903
|
+
"aria-label": "Next page",
|
|
2904
|
+
disabled: page >= pageCount,
|
|
2905
|
+
onClick: () => onChange(page + 1),
|
|
2906
|
+
className: cn(navButtonClasses, "text-tollerud-text-secondary hover:bg-tollerud-surface-hover"),
|
|
2907
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronRight, { size: 16 })
|
|
2908
|
+
}
|
|
2909
|
+
)
|
|
2910
|
+
] });
|
|
2911
|
+
}
|
|
2912
|
+
);
|
|
2913
|
+
Pagination.displayName = "Pagination";
|
|
2914
|
+
var Segmented = React2.forwardRef(
|
|
2915
|
+
({ className, options, value, onChange, size = "md", ...props }, ref) => {
|
|
2916
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
2917
|
+
"div",
|
|
2918
|
+
{
|
|
2919
|
+
ref,
|
|
2920
|
+
role: "radiogroup",
|
|
2921
|
+
className: cn(
|
|
2922
|
+
"inline-flex items-center gap-0.5 rounded-lg p-1 bg-tollerud-surface-raised border border-tollerud-border",
|
|
2923
|
+
className
|
|
2924
|
+
),
|
|
2925
|
+
...props,
|
|
2926
|
+
children: options.map((opt) => {
|
|
2927
|
+
const active = opt.value === value;
|
|
2928
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
2929
|
+
"button",
|
|
2930
|
+
{
|
|
2931
|
+
type: "button",
|
|
2932
|
+
role: "radio",
|
|
2933
|
+
"aria-checked": active,
|
|
2934
|
+
disabled: opt.disabled,
|
|
2935
|
+
onClick: () => !opt.disabled && onChange(opt.value),
|
|
2936
|
+
className: cn(
|
|
2937
|
+
"rounded-md font-medium transition-colors duration-[150ms]",
|
|
2938
|
+
size === "sm" ? "px-2.5 py-1 text-xs" : "px-3.5 py-1.5 text-sm",
|
|
2939
|
+
active ? "bg-tollerud-yellow text-tollerud-noir-black" : "text-tollerud-text-secondary hover:text-tollerud-text-primary",
|
|
2940
|
+
opt.disabled && "opacity-40 pointer-events-none"
|
|
2941
|
+
),
|
|
2942
|
+
children: opt.label
|
|
2943
|
+
},
|
|
2944
|
+
opt.value
|
|
2945
|
+
);
|
|
2946
|
+
})
|
|
2947
|
+
}
|
|
2948
|
+
);
|
|
2949
|
+
}
|
|
2950
|
+
);
|
|
2951
|
+
Segmented.displayName = "Segmented";
|
|
2952
|
+
var Stepper = React2.forwardRef(
|
|
2953
|
+
({ className, steps, current, orientation = "horizontal", ...props }, ref) => {
|
|
2954
|
+
const vertical = orientation === "vertical";
|
|
2955
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
2956
|
+
"ol",
|
|
2957
|
+
{
|
|
2958
|
+
ref,
|
|
2959
|
+
className: cn("flex", vertical ? "flex-col gap-0" : "items-start gap-0 w-full", className),
|
|
2960
|
+
...props,
|
|
2961
|
+
children: steps.map((step, i) => {
|
|
2962
|
+
const status = i < current ? "complete" : i === current ? "active" : "upcoming";
|
|
2963
|
+
const isLast = i === steps.length - 1;
|
|
2964
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
2965
|
+
"li",
|
|
2966
|
+
{
|
|
2967
|
+
className: cn(
|
|
2968
|
+
"flex",
|
|
2969
|
+
vertical ? "flex-row gap-3" : "flex-col flex-1 gap-2",
|
|
2970
|
+
!vertical && !isLast && "pr-2"
|
|
2971
|
+
),
|
|
2972
|
+
children: [
|
|
2973
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("flex items-center", vertical ? "flex-col" : "flex-row gap-2 w-full"), children: [
|
|
2974
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2975
|
+
"span",
|
|
2976
|
+
{
|
|
2977
|
+
className: cn(
|
|
2978
|
+
"flex h-7 w-7 shrink-0 items-center justify-center rounded-full text-xs font-medium transition-colors duration-[150ms]",
|
|
2979
|
+
status === "complete" && "bg-tollerud-yellow text-tollerud-noir-black",
|
|
2980
|
+
status === "active" && "border-2 border-tollerud-yellow text-tollerud-yellow",
|
|
2981
|
+
status === "upcoming" && "border border-tollerud-border text-tollerud-text-muted"
|
|
2982
|
+
),
|
|
2983
|
+
children: status === "complete" ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Check, { size: 14 }) : i + 1
|
|
2984
|
+
}
|
|
2985
|
+
),
|
|
2986
|
+
!isLast && /* @__PURE__ */ jsxRuntime.jsx(
|
|
2987
|
+
"span",
|
|
2988
|
+
{
|
|
2989
|
+
"aria-hidden": "true",
|
|
2990
|
+
className: cn(
|
|
2991
|
+
"bg-tollerud-border",
|
|
2992
|
+
vertical ? "w-px flex-1 my-1" : "h-px flex-1",
|
|
2993
|
+
status === "complete" && "bg-tollerud-yellow"
|
|
2994
|
+
)
|
|
2995
|
+
}
|
|
2996
|
+
)
|
|
2997
|
+
] }),
|
|
2998
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("flex flex-col", vertical && "pb-6"), children: [
|
|
2999
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3000
|
+
"span",
|
|
3001
|
+
{
|
|
3002
|
+
className: cn(
|
|
3003
|
+
"text-sm font-medium",
|
|
3004
|
+
status === "upcoming" ? "text-tollerud-text-muted" : "text-tollerud-text-primary"
|
|
3005
|
+
),
|
|
3006
|
+
children: step.label
|
|
3007
|
+
}
|
|
3008
|
+
),
|
|
3009
|
+
step.description && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-tollerud-text-muted", children: step.description })
|
|
3010
|
+
] })
|
|
3011
|
+
]
|
|
3012
|
+
},
|
|
3013
|
+
i
|
|
3014
|
+
);
|
|
3015
|
+
})
|
|
3016
|
+
}
|
|
3017
|
+
);
|
|
3018
|
+
}
|
|
3019
|
+
);
|
|
3020
|
+
Stepper.displayName = "Stepper";
|
|
3021
|
+
var Panel = React2.forwardRef(
|
|
3022
|
+
({ className, title, description, actions, children, ...props }, ref) => {
|
|
3023
|
+
const hasHeader = title || description || actions;
|
|
3024
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3025
|
+
"div",
|
|
3026
|
+
{
|
|
3027
|
+
ref,
|
|
3028
|
+
className: cn(
|
|
3029
|
+
"rounded-lg border border-tollerud-border bg-tollerud-surface overflow-hidden",
|
|
3030
|
+
className
|
|
3031
|
+
),
|
|
3032
|
+
...props,
|
|
3033
|
+
children: [
|
|
3034
|
+
hasHeader && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start justify-between gap-4 px-5 py-4 border-b border-tollerud-border", children: [
|
|
3035
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-0.5", children: [
|
|
3036
|
+
title && /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-sm font-medium text-tollerud-text-primary", children: title }),
|
|
3037
|
+
description && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-tollerud-text-muted", children: description })
|
|
3038
|
+
] }),
|
|
3039
|
+
actions && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-2 shrink-0", children: actions })
|
|
3040
|
+
] }),
|
|
3041
|
+
children && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-5 py-4", children })
|
|
3042
|
+
]
|
|
3043
|
+
}
|
|
3044
|
+
);
|
|
3045
|
+
}
|
|
3046
|
+
);
|
|
3047
|
+
Panel.displayName = "Panel";
|
|
3048
|
+
var meterTones = {
|
|
3049
|
+
default: "bg-tollerud-yellow",
|
|
3050
|
+
success: "bg-tollerud-success",
|
|
3051
|
+
warning: "bg-tollerud-warning",
|
|
3052
|
+
error: "bg-tollerud-error"
|
|
3053
|
+
};
|
|
3054
|
+
var Meter = React2.forwardRef(
|
|
3055
|
+
({ className, value, max = 100, label, showValue, tone = "default", ...props }, ref) => {
|
|
3056
|
+
const pct = Math.min(100, Math.max(0, value / max * 100));
|
|
3057
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { ref, className: cn("flex flex-col gap-1.5", className), ...props, children: [
|
|
3058
|
+
(label || showValue) && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between text-xs", children: [
|
|
3059
|
+
label && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-tollerud-text-secondary", children: label }),
|
|
3060
|
+
showValue && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-tollerud-text-muted tabular-nums", children: [
|
|
3061
|
+
Math.round(pct),
|
|
3062
|
+
"%"
|
|
3063
|
+
] })
|
|
3064
|
+
] }),
|
|
3065
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3066
|
+
"div",
|
|
3067
|
+
{
|
|
3068
|
+
role: "meter",
|
|
3069
|
+
"aria-valuenow": value,
|
|
3070
|
+
"aria-valuemin": 0,
|
|
3071
|
+
"aria-valuemax": max,
|
|
3072
|
+
className: "h-1.5 w-full overflow-hidden rounded-full bg-tollerud-surface-raised",
|
|
3073
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
3074
|
+
"div",
|
|
3075
|
+
{
|
|
3076
|
+
className: cn("h-full rounded-full transition-[width] duration-300", meterTones[tone]),
|
|
3077
|
+
style: { width: `${pct}%` }
|
|
3078
|
+
}
|
|
3079
|
+
)
|
|
3080
|
+
}
|
|
3081
|
+
)
|
|
3082
|
+
] });
|
|
3083
|
+
}
|
|
3084
|
+
);
|
|
3085
|
+
Meter.displayName = "Meter";
|
|
3086
|
+
var FormRow = React2.forwardRef(
|
|
3087
|
+
({ className, label, description, error, required, htmlFor, children, ...props }, ref) => {
|
|
3088
|
+
const autoId = React2.useId();
|
|
3089
|
+
const descriptionId = description ? `${autoId}-description` : void 0;
|
|
3090
|
+
const errorId = error ? `${autoId}-error` : void 0;
|
|
3091
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { ref, className: cn("flex flex-col gap-1.5", className), ...props, children: [
|
|
3092
|
+
label && /* @__PURE__ */ jsxRuntime.jsxs("label", { htmlFor, className: "text-sm font-medium text-tollerud-text-primary", children: [
|
|
3093
|
+
label,
|
|
3094
|
+
required && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "ml-0.5 text-tollerud-error", children: "*" })
|
|
3095
|
+
] }),
|
|
3096
|
+
description && /* @__PURE__ */ jsxRuntime.jsx("p", { id: descriptionId, className: "text-xs text-tollerud-text-muted", children: description }),
|
|
3097
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { "aria-describedby": [descriptionId, errorId].filter(Boolean).join(" ") || void 0, children }),
|
|
3098
|
+
error && /* @__PURE__ */ jsxRuntime.jsx("p", { id: errorId, className: "text-xs text-tollerud-error", children: error })
|
|
3099
|
+
] });
|
|
3100
|
+
}
|
|
3101
|
+
);
|
|
3102
|
+
FormRow.displayName = "FormRow";
|
|
3103
|
+
var PricingCard = React2.forwardRef(
|
|
3104
|
+
({
|
|
3105
|
+
className,
|
|
3106
|
+
name,
|
|
3107
|
+
price,
|
|
3108
|
+
period,
|
|
3109
|
+
description,
|
|
3110
|
+
features = [],
|
|
3111
|
+
ctaLabel = "Get started",
|
|
3112
|
+
onCtaClick,
|
|
3113
|
+
featured,
|
|
3114
|
+
badge,
|
|
3115
|
+
...props
|
|
3116
|
+
}, ref) => {
|
|
3117
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3118
|
+
"div",
|
|
3119
|
+
{
|
|
3120
|
+
ref,
|
|
3121
|
+
className: cn(
|
|
3122
|
+
"flex flex-col gap-5 rounded-xl border p-6",
|
|
3123
|
+
featured ? "border-tollerud-yellow bg-tollerud-yellow/[0.04] shadow-[0_0_0_1px_rgba(255,255,0,0.15)]" : "border-tollerud-border bg-tollerud-surface",
|
|
3124
|
+
className
|
|
3125
|
+
),
|
|
3126
|
+
...props,
|
|
3127
|
+
children: [
|
|
3128
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-1", children: [
|
|
3129
|
+
badge && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "inline-flex w-fit items-center rounded-full bg-tollerud-yellow/15 px-2.5 py-0.5 text-xs font-medium text-tollerud-yellow", children: badge }),
|
|
3130
|
+
/* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-base font-medium text-tollerud-text-primary", children: name }),
|
|
3131
|
+
description && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-tollerud-text-muted", children: description })
|
|
3132
|
+
] }),
|
|
3133
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-baseline gap-1", children: [
|
|
3134
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-3xl font-semibold tracking-tight text-tollerud-text-primary", children: price }),
|
|
3135
|
+
period && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm text-tollerud-text-muted", children: period })
|
|
3136
|
+
] }),
|
|
3137
|
+
features.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("ul", { className: "flex flex-col gap-2.5", children: features.map((feature, i) => /* @__PURE__ */ jsxRuntime.jsxs("li", { className: "flex items-start gap-2.5 text-sm text-tollerud-text-secondary", children: [
|
|
3138
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Check, { size: 16, className: "mt-0.5 shrink-0 text-tollerud-yellow" }),
|
|
3139
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: feature })
|
|
3140
|
+
] }, i)) }),
|
|
3141
|
+
/* @__PURE__ */ jsxRuntime.jsx(Button, { variant: featured ? "primary" : "secondary", onClick: onCtaClick, className: "mt-auto w-full", children: ctaLabel })
|
|
3142
|
+
]
|
|
3143
|
+
}
|
|
3144
|
+
);
|
|
3145
|
+
}
|
|
3146
|
+
);
|
|
3147
|
+
PricingCard.displayName = "PricingCard";
|
|
3148
|
+
var AccordionContext = React2.createContext(null);
|
|
3149
|
+
function useAccordionContext(component) {
|
|
3150
|
+
const ctx = React2.useContext(AccordionContext);
|
|
3151
|
+
if (!ctx) throw new Error(`<${component}> must be used within <Accordion>`);
|
|
3152
|
+
return ctx;
|
|
3153
|
+
}
|
|
3154
|
+
var Accordion = React2.forwardRef(
|
|
3155
|
+
({ className, multiple = false, defaultOpen, children, ...props }, ref) => {
|
|
3156
|
+
const [openItems, setOpenItems] = React2.useState(() => {
|
|
3157
|
+
if (!defaultOpen) return /* @__PURE__ */ new Set();
|
|
3158
|
+
return new Set(Array.isArray(defaultOpen) ? defaultOpen : [defaultOpen]);
|
|
3159
|
+
});
|
|
3160
|
+
const toggle = (value) => {
|
|
3161
|
+
setOpenItems((prev) => {
|
|
3162
|
+
const next = multiple ? new Set(prev) : /* @__PURE__ */ new Set();
|
|
3163
|
+
if (prev.has(value)) {
|
|
3164
|
+
next.delete(value);
|
|
3165
|
+
} else {
|
|
3166
|
+
next.add(value);
|
|
3167
|
+
}
|
|
3168
|
+
return next;
|
|
3169
|
+
});
|
|
3170
|
+
};
|
|
3171
|
+
return /* @__PURE__ */ jsxRuntime.jsx(AccordionContext.Provider, { value: { openItems, toggle }, children: /* @__PURE__ */ jsxRuntime.jsx("div", { ref, className: cn("flex flex-col divide-y divide-tollerud-border rounded-lg border border-tollerud-border", className), ...props, children }) });
|
|
3172
|
+
}
|
|
3173
|
+
);
|
|
3174
|
+
Accordion.displayName = "Accordion";
|
|
3175
|
+
var AccordionItemContext = React2.createContext(null);
|
|
3176
|
+
var AccordionItem = React2.forwardRef(
|
|
3177
|
+
({ className, value, children, ...props }, ref) => {
|
|
3178
|
+
const { openItems } = useAccordionContext("AccordionItem");
|
|
3179
|
+
const autoId = React2.useId();
|
|
3180
|
+
const open = openItems.has(value);
|
|
3181
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
3182
|
+
AccordionItemContext.Provider,
|
|
3183
|
+
{
|
|
3184
|
+
value: { value, open, triggerId: `${autoId}-trigger`, contentId: `${autoId}-content` },
|
|
3185
|
+
children: /* @__PURE__ */ jsxRuntime.jsx("div", { ref, className: cn("first:rounded-t-lg last:rounded-b-lg", className), ...props, children })
|
|
3186
|
+
}
|
|
3187
|
+
);
|
|
3188
|
+
}
|
|
3189
|
+
);
|
|
3190
|
+
AccordionItem.displayName = "AccordionItem";
|
|
3191
|
+
function useAccordionItemContext(component) {
|
|
3192
|
+
const ctx = React2.useContext(AccordionItemContext);
|
|
3193
|
+
if (!ctx) throw new Error(`<${component}> must be used within <AccordionItem>`);
|
|
3194
|
+
return ctx;
|
|
3195
|
+
}
|
|
3196
|
+
var AccordionTrigger = React2.forwardRef(
|
|
3197
|
+
({ className, children, ...props }, ref) => {
|
|
3198
|
+
const { toggle } = useAccordionContext("AccordionTrigger");
|
|
3199
|
+
const { value, open, triggerId, contentId } = useAccordionItemContext("AccordionTrigger");
|
|
3200
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3201
|
+
"button",
|
|
3202
|
+
{
|
|
3203
|
+
ref,
|
|
3204
|
+
id: triggerId,
|
|
3205
|
+
type: "button",
|
|
3206
|
+
"aria-expanded": open,
|
|
3207
|
+
"aria-controls": contentId,
|
|
3208
|
+
onClick: () => toggle(value),
|
|
3209
|
+
className: cn(
|
|
3210
|
+
"flex w-full items-center justify-between gap-4 px-4 py-3.5 text-left text-sm font-medium",
|
|
3211
|
+
"text-tollerud-text-primary hover:bg-tollerud-surface-hover transition-colors duration-[150ms]",
|
|
3212
|
+
className
|
|
3213
|
+
),
|
|
3214
|
+
...props,
|
|
3215
|
+
children: [
|
|
3216
|
+
children,
|
|
3217
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3218
|
+
lucideReact.ChevronDown,
|
|
3219
|
+
{
|
|
3220
|
+
size: 16,
|
|
3221
|
+
className: cn("shrink-0 text-tollerud-text-muted transition-transform duration-[200ms]", open && "rotate-180")
|
|
3222
|
+
}
|
|
3223
|
+
)
|
|
3224
|
+
]
|
|
3225
|
+
}
|
|
3226
|
+
);
|
|
3227
|
+
}
|
|
3228
|
+
);
|
|
3229
|
+
AccordionTrigger.displayName = "AccordionTrigger";
|
|
3230
|
+
var AccordionContent = React2.forwardRef(
|
|
3231
|
+
({ className, children, ...props }, ref) => {
|
|
3232
|
+
const { open, triggerId, contentId } = useAccordionItemContext("AccordionContent");
|
|
3233
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
3234
|
+
"div",
|
|
3235
|
+
{
|
|
3236
|
+
ref,
|
|
3237
|
+
id: contentId,
|
|
3238
|
+
role: "region",
|
|
3239
|
+
"aria-labelledby": triggerId,
|
|
3240
|
+
hidden: !open,
|
|
3241
|
+
className: cn("px-4 pb-4 text-sm text-tollerud-text-secondary", className),
|
|
3242
|
+
...props,
|
|
3243
|
+
children
|
|
3244
|
+
}
|
|
3245
|
+
);
|
|
3246
|
+
}
|
|
3247
|
+
);
|
|
3248
|
+
AccordionContent.displayName = "AccordionContent";
|
|
3249
|
+
var Slider = React2.forwardRef(
|
|
3250
|
+
({ className, label, showValue, id: idProp, value, defaultValue, min = 0, max = 100, onChange, ...props }, ref) => {
|
|
3251
|
+
const autoId = React2.useId();
|
|
3252
|
+
const id = idProp ?? autoId;
|
|
3253
|
+
const current = value ?? defaultValue ?? min;
|
|
3254
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-1.5", children: [
|
|
3255
|
+
(label || showValue) && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between text-xs", children: [
|
|
3256
|
+
label && /* @__PURE__ */ jsxRuntime.jsx("label", { htmlFor: id, className: "font-medium text-tollerud-text-muted", children: label }),
|
|
3257
|
+
showValue && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-tollerud-text-secondary tabular-nums", children: current })
|
|
3258
|
+
] }),
|
|
3259
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3260
|
+
"input",
|
|
3261
|
+
{
|
|
3262
|
+
ref,
|
|
3263
|
+
id,
|
|
3264
|
+
type: "range",
|
|
3265
|
+
min,
|
|
3266
|
+
max,
|
|
3267
|
+
value,
|
|
3268
|
+
defaultValue,
|
|
3269
|
+
onChange: (e) => onChange?.(Number(e.target.value)),
|
|
3270
|
+
className: cn(
|
|
3271
|
+
"h-1.5 w-full cursor-pointer appearance-none rounded-full bg-tollerud-surface-raised accent-tollerud-yellow",
|
|
3272
|
+
"[&::-webkit-slider-thumb]:appearance-none [&::-webkit-slider-thumb]:h-4 [&::-webkit-slider-thumb]:w-4",
|
|
3273
|
+
"[&::-webkit-slider-thumb]:rounded-full [&::-webkit-slider-thumb]:bg-tollerud-yellow [&::-webkit-slider-thumb]:cursor-pointer",
|
|
3274
|
+
"[&::-webkit-slider-thumb]:border-2 [&::-webkit-slider-thumb]:border-tollerud-noir-black",
|
|
3275
|
+
"[&::-moz-range-thumb]:h-4 [&::-moz-range-thumb]:w-4 [&::-moz-range-thumb]:rounded-full",
|
|
3276
|
+
"[&::-moz-range-thumb]:bg-tollerud-yellow [&::-moz-range-thumb]:border-2 [&::-moz-range-thumb]:border-tollerud-noir-black [&::-moz-range-thumb]:cursor-pointer",
|
|
3277
|
+
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-tollerud-yellow/50",
|
|
3278
|
+
"disabled:opacity-40 disabled:pointer-events-none",
|
|
3279
|
+
className
|
|
3280
|
+
),
|
|
3281
|
+
...props
|
|
3282
|
+
}
|
|
3283
|
+
)
|
|
3284
|
+
] });
|
|
3285
|
+
}
|
|
3286
|
+
);
|
|
3287
|
+
Slider.displayName = "Slider";
|
|
3288
|
+
var PasswordInput = React2.forwardRef(
|
|
3289
|
+
({ className, label, error, id, ...props }, ref) => {
|
|
3290
|
+
const [visible, setVisible] = React2.useState(false);
|
|
3291
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-1", children: [
|
|
3292
|
+
label && /* @__PURE__ */ jsxRuntime.jsx("label", { htmlFor: id, className: "text-xs font-medium text-tollerud-text-muted", children: label }),
|
|
3293
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", children: [
|
|
3294
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3295
|
+
"input",
|
|
3296
|
+
{
|
|
3297
|
+
ref,
|
|
3298
|
+
id,
|
|
3299
|
+
type: visible ? "text" : "password",
|
|
3300
|
+
className: cn(
|
|
3301
|
+
"w-full font-sans text-base px-3 py-2 pr-10 rounded",
|
|
3302
|
+
"bg-tollerud-surface-raised border",
|
|
3303
|
+
"text-tollerud-text-primary",
|
|
3304
|
+
"placeholder:text-tollerud-text-muted",
|
|
3305
|
+
"transition-[border-color] duration-[150ms]",
|
|
3306
|
+
"focus:outline-none focus:border-tollerud-yellow focus:shadow-[0_0_0_1px_#E8D500]",
|
|
3307
|
+
error ? "border-tollerud-error" : "border-tollerud-border",
|
|
3308
|
+
className
|
|
3309
|
+
),
|
|
3310
|
+
...props
|
|
3311
|
+
}
|
|
3312
|
+
),
|
|
3313
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3314
|
+
"button",
|
|
3315
|
+
{
|
|
3316
|
+
type: "button",
|
|
3317
|
+
onClick: () => setVisible((v) => !v),
|
|
3318
|
+
"aria-label": visible ? "Hide password" : "Show password",
|
|
3319
|
+
"aria-pressed": visible,
|
|
3320
|
+
className: "absolute right-2.5 top-1/2 -translate-y-1/2 text-tollerud-text-muted hover:text-tollerud-text-secondary transition-colors duration-[150ms]",
|
|
3321
|
+
children: visible ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.EyeOff, { size: 16 }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Eye, { size: 16 })
|
|
3322
|
+
}
|
|
3323
|
+
)
|
|
3324
|
+
] }),
|
|
3325
|
+
error && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-tollerud-error mt-0.5", children: error })
|
|
3326
|
+
] });
|
|
3327
|
+
}
|
|
3328
|
+
);
|
|
3329
|
+
PasswordInput.displayName = "PasswordInput";
|
|
3330
|
+
var defaultFilter = (option, query) => option.label.toLowerCase().includes(query.toLowerCase());
|
|
3331
|
+
function Combobox({
|
|
3332
|
+
options,
|
|
3333
|
+
value: valueProp,
|
|
3334
|
+
defaultValue,
|
|
3335
|
+
onChange,
|
|
3336
|
+
placeholder = "Search\u2026",
|
|
3337
|
+
label,
|
|
3338
|
+
error,
|
|
3339
|
+
filter = defaultFilter,
|
|
3340
|
+
className,
|
|
3341
|
+
disabled
|
|
3342
|
+
}) {
|
|
3343
|
+
const id = React2.useId();
|
|
3344
|
+
const rootRef = React2.useRef(null);
|
|
3345
|
+
const isControlled = valueProp !== void 0;
|
|
3346
|
+
const [internalValue, setInternalValue] = React2.useState(defaultValue);
|
|
3347
|
+
const value = isControlled ? valueProp : internalValue;
|
|
3348
|
+
const [open, setOpen] = React2.useState(false);
|
|
3349
|
+
const [query, setQuery] = React2.useState("");
|
|
3350
|
+
const [activeIndex, setActiveIndex] = React2.useState(0);
|
|
3351
|
+
const selected = options.find((o) => o.value === value);
|
|
3352
|
+
const filtered = React2.useMemo(() => {
|
|
3353
|
+
if (!query) return options;
|
|
3354
|
+
return options.filter((o) => filter(o, query));
|
|
3355
|
+
}, [options, query, filter]);
|
|
3356
|
+
React2.useEffect(() => {
|
|
3357
|
+
if (!open) return;
|
|
3358
|
+
function onClickOutside(e) {
|
|
3359
|
+
if (rootRef.current && !rootRef.current.contains(e.target)) {
|
|
3360
|
+
setOpen(false);
|
|
3361
|
+
setQuery("");
|
|
3362
|
+
}
|
|
3363
|
+
}
|
|
3364
|
+
function onResize() {
|
|
3365
|
+
setOpen(false);
|
|
3366
|
+
setQuery("");
|
|
3367
|
+
}
|
|
3368
|
+
document.addEventListener("mousedown", onClickOutside);
|
|
3369
|
+
window.addEventListener("resize", onResize);
|
|
3370
|
+
return () => {
|
|
3371
|
+
document.removeEventListener("mousedown", onClickOutside);
|
|
3372
|
+
window.removeEventListener("resize", onResize);
|
|
3373
|
+
};
|
|
3374
|
+
}, [open]);
|
|
3375
|
+
const commit = (option) => {
|
|
3376
|
+
if (option.disabled) return;
|
|
3377
|
+
if (!isControlled) setInternalValue(option.value);
|
|
3378
|
+
onChange?.(option.value);
|
|
3379
|
+
setOpen(false);
|
|
3380
|
+
setQuery("");
|
|
3381
|
+
};
|
|
3382
|
+
const onKeyDown = (e) => {
|
|
3383
|
+
if (e.key === "ArrowDown") {
|
|
3384
|
+
e.preventDefault();
|
|
3385
|
+
setOpen(true);
|
|
3386
|
+
setActiveIndex((i) => Math.min(i + 1, filtered.length - 1));
|
|
3387
|
+
} else if (e.key === "ArrowUp") {
|
|
3388
|
+
e.preventDefault();
|
|
3389
|
+
setActiveIndex((i) => Math.max(i - 1, 0));
|
|
3390
|
+
} else if (e.key === "Enter") {
|
|
3391
|
+
e.preventDefault();
|
|
3392
|
+
const opt = filtered[activeIndex];
|
|
3393
|
+
if (opt) commit(opt);
|
|
3394
|
+
} else if (e.key === "Escape") {
|
|
3395
|
+
setOpen(false);
|
|
3396
|
+
setQuery("");
|
|
3397
|
+
}
|
|
3398
|
+
};
|
|
3399
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { ref: rootRef, className: cn("relative flex flex-col gap-1", className), children: [
|
|
3400
|
+
label && /* @__PURE__ */ jsxRuntime.jsx("label", { htmlFor: id, className: "text-xs font-medium text-tollerud-text-muted", children: label }),
|
|
3401
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", children: [
|
|
3402
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3403
|
+
"input",
|
|
3404
|
+
{
|
|
3405
|
+
id,
|
|
3406
|
+
role: "combobox",
|
|
3407
|
+
"aria-expanded": open,
|
|
3408
|
+
"aria-autocomplete": "list",
|
|
3409
|
+
"aria-controls": `${id}-listbox`,
|
|
3410
|
+
disabled,
|
|
3411
|
+
value: open ? query : selected?.label ?? "",
|
|
3412
|
+
placeholder: selected ? selected.label : placeholder,
|
|
3413
|
+
onFocus: () => {
|
|
3414
|
+
setOpen(true);
|
|
3415
|
+
setActiveIndex(0);
|
|
3416
|
+
},
|
|
3417
|
+
onChange: (e) => {
|
|
3418
|
+
setQuery(e.target.value);
|
|
3419
|
+
setActiveIndex(0);
|
|
3420
|
+
if (!open) setOpen(true);
|
|
3421
|
+
},
|
|
3422
|
+
onKeyDown,
|
|
3423
|
+
className: cn(
|
|
3424
|
+
"w-full font-sans text-base px-3 py-2 pr-9 rounded",
|
|
3425
|
+
"bg-tollerud-surface-raised border",
|
|
3426
|
+
"text-tollerud-text-primary placeholder:text-tollerud-text-muted",
|
|
3427
|
+
"transition-[border-color] duration-[150ms]",
|
|
3428
|
+
"focus:outline-none focus:border-tollerud-yellow focus:shadow-[0_0_0_1px_#E8D500]",
|
|
3429
|
+
error ? "border-tollerud-error" : "border-tollerud-border",
|
|
3430
|
+
disabled && "opacity-50 pointer-events-none"
|
|
3431
|
+
)
|
|
3432
|
+
}
|
|
3433
|
+
),
|
|
3434
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3435
|
+
lucideReact.ChevronDown,
|
|
3436
|
+
{
|
|
3437
|
+
size: 15,
|
|
3438
|
+
className: cn(
|
|
3439
|
+
"pointer-events-none absolute right-3 top-1/2 -translate-y-1/2 text-tollerud-text-muted transition-transform duration-[150ms]",
|
|
3440
|
+
open && "rotate-180"
|
|
3441
|
+
)
|
|
3442
|
+
}
|
|
3443
|
+
)
|
|
3444
|
+
] }),
|
|
3445
|
+
open && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3446
|
+
"ul",
|
|
3447
|
+
{
|
|
3448
|
+
id: `${id}-listbox`,
|
|
3449
|
+
role: "listbox",
|
|
3450
|
+
className: "absolute top-full z-20 mt-1 max-h-64 w-full overflow-auto rounded-lg border border-tollerud-border bg-tollerud-surface-overlay py-1 shadow-lg",
|
|
3451
|
+
children: [
|
|
3452
|
+
filtered.length === 0 && /* @__PURE__ */ jsxRuntime.jsx("li", { className: "px-3 py-2 text-sm text-tollerud-text-muted", children: "No results" }),
|
|
3453
|
+
filtered.map((option, i) => {
|
|
3454
|
+
const isSelected = option.value === value;
|
|
3455
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3456
|
+
"li",
|
|
3457
|
+
{
|
|
3458
|
+
role: "option",
|
|
3459
|
+
"aria-selected": isSelected,
|
|
3460
|
+
onMouseDown: (e) => {
|
|
3461
|
+
e.preventDefault();
|
|
3462
|
+
commit(option);
|
|
3463
|
+
},
|
|
3464
|
+
onMouseEnter: () => setActiveIndex(i),
|
|
3465
|
+
className: cn(
|
|
3466
|
+
"flex items-center justify-between gap-2 px-3 py-2 text-sm cursor-pointer",
|
|
3467
|
+
i === activeIndex ? "bg-tollerud-surface-hover text-tollerud-text-primary" : "text-tollerud-text-secondary",
|
|
3468
|
+
option.disabled && "opacity-40 pointer-events-none"
|
|
3469
|
+
),
|
|
3470
|
+
children: [
|
|
3471
|
+
option.label,
|
|
3472
|
+
isSelected && /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Check, { size: 14, className: "text-tollerud-yellow" })
|
|
3473
|
+
]
|
|
3474
|
+
},
|
|
3475
|
+
option.value
|
|
3476
|
+
);
|
|
3477
|
+
})
|
|
3478
|
+
]
|
|
3479
|
+
}
|
|
3480
|
+
),
|
|
3481
|
+
error && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-tollerud-error mt-0.5", children: error })
|
|
3482
|
+
] });
|
|
3483
|
+
}
|
|
3484
|
+
Combobox.displayName = "Combobox";
|
|
3485
|
+
var defaultFormat = (date) => date.toLocaleDateString(void 0, { year: "numeric", month: "short", day: "numeric" });
|
|
3486
|
+
function isSameDay(a, b) {
|
|
3487
|
+
return a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth() && a.getDate() === b.getDate();
|
|
3488
|
+
}
|
|
3489
|
+
function startOfMonth(date) {
|
|
3490
|
+
return new Date(date.getFullYear(), date.getMonth(), 1);
|
|
3491
|
+
}
|
|
3492
|
+
function buildCalendarGrid(monthDate) {
|
|
3493
|
+
const first = startOfMonth(monthDate);
|
|
3494
|
+
const startWeekday = first.getDay();
|
|
3495
|
+
const daysInMonth = new Date(monthDate.getFullYear(), monthDate.getMonth() + 1, 0).getDate();
|
|
3496
|
+
const cells = [];
|
|
3497
|
+
for (let i = 0; i < startWeekday; i++) cells.push(null);
|
|
3498
|
+
for (let d = 1; d <= daysInMonth; d++) cells.push(new Date(monthDate.getFullYear(), monthDate.getMonth(), d));
|
|
3499
|
+
return cells;
|
|
3500
|
+
}
|
|
3501
|
+
var WEEKDAYS = ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"];
|
|
3502
|
+
function DatePicker({
|
|
3503
|
+
value: valueProp,
|
|
3504
|
+
defaultValue = null,
|
|
3505
|
+
onChange,
|
|
3506
|
+
label,
|
|
3507
|
+
error,
|
|
3508
|
+
placeholder = "Select a date",
|
|
3509
|
+
formatDate = defaultFormat,
|
|
3510
|
+
className,
|
|
3511
|
+
disabled
|
|
3512
|
+
}) {
|
|
3513
|
+
const id = React2.useId();
|
|
3514
|
+
const rootRef = React2.useRef(null);
|
|
3515
|
+
const isControlled = valueProp !== void 0;
|
|
3516
|
+
const [internalValue, setInternalValue] = React2.useState(defaultValue);
|
|
3517
|
+
const value = isControlled ? valueProp ?? null : internalValue;
|
|
3518
|
+
const [open, setOpen] = React2.useState(false);
|
|
3519
|
+
const [viewMonth, setViewMonth] = React2.useState(() => startOfMonth(value ?? /* @__PURE__ */ new Date()));
|
|
3520
|
+
const cells = React2.useMemo(() => buildCalendarGrid(viewMonth), [viewMonth]);
|
|
3521
|
+
React2.useEffect(() => {
|
|
3522
|
+
if (!open) return;
|
|
3523
|
+
function onClickOutside(e) {
|
|
3524
|
+
if (rootRef.current && !rootRef.current.contains(e.target)) setOpen(false);
|
|
3525
|
+
}
|
|
3526
|
+
function onResize() {
|
|
3527
|
+
setOpen(false);
|
|
3528
|
+
}
|
|
3529
|
+
document.addEventListener("mousedown", onClickOutside);
|
|
3530
|
+
window.addEventListener("resize", onResize);
|
|
3531
|
+
return () => {
|
|
3532
|
+
document.removeEventListener("mousedown", onClickOutside);
|
|
3533
|
+
window.removeEventListener("resize", onResize);
|
|
3534
|
+
};
|
|
3535
|
+
}, [open]);
|
|
3536
|
+
const select = (date) => {
|
|
3537
|
+
if (!isControlled) setInternalValue(date);
|
|
3538
|
+
onChange?.(date);
|
|
3539
|
+
setOpen(false);
|
|
3540
|
+
};
|
|
3541
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { ref: rootRef, className: cn("relative flex flex-col gap-1", className), children: [
|
|
3542
|
+
label && /* @__PURE__ */ jsxRuntime.jsx("label", { htmlFor: id, className: "text-xs font-medium text-tollerud-text-muted", children: label }),
|
|
3543
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
3544
|
+
"button",
|
|
3545
|
+
{
|
|
3546
|
+
id,
|
|
3547
|
+
type: "button",
|
|
3548
|
+
disabled,
|
|
3549
|
+
onClick: () => {
|
|
3550
|
+
setViewMonth(startOfMonth(value ?? /* @__PURE__ */ new Date()));
|
|
3551
|
+
setOpen((o) => !o);
|
|
3552
|
+
},
|
|
3553
|
+
"aria-haspopup": "dialog",
|
|
3554
|
+
"aria-expanded": open,
|
|
3555
|
+
className: cn(
|
|
3556
|
+
"flex w-full items-center justify-between gap-2 rounded px-3 py-2 text-left text-base",
|
|
3557
|
+
"bg-tollerud-surface-raised border",
|
|
3558
|
+
"transition-[border-color] duration-[150ms]",
|
|
3559
|
+
"focus:outline-none focus:border-tollerud-yellow focus:shadow-[0_0_0_1px_#E8D500]",
|
|
3560
|
+
error ? "border-tollerud-error" : "border-tollerud-border",
|
|
3561
|
+
value ? "text-tollerud-text-primary" : "text-tollerud-text-muted",
|
|
3562
|
+
disabled && "opacity-50 pointer-events-none"
|
|
3563
|
+
),
|
|
3564
|
+
children: [
|
|
3565
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: value ? formatDate(value) : placeholder }),
|
|
3566
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Calendar, { size: 15, className: "text-tollerud-text-muted" })
|
|
3567
|
+
]
|
|
3568
|
+
}
|
|
3569
|
+
),
|
|
3570
|
+
open && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3571
|
+
"div",
|
|
3572
|
+
{
|
|
3573
|
+
role: "dialog",
|
|
3574
|
+
"aria-label": "Choose date",
|
|
3575
|
+
className: "absolute top-full z-20 mt-1 w-72 rounded-lg border border-tollerud-border bg-tollerud-surface-overlay p-3 shadow-lg",
|
|
3576
|
+
children: [
|
|
3577
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mb-2 flex items-center justify-between", children: [
|
|
3578
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3579
|
+
"button",
|
|
3580
|
+
{
|
|
3581
|
+
type: "button",
|
|
3582
|
+
"aria-label": "Previous month",
|
|
3583
|
+
onClick: () => setViewMonth((m) => new Date(m.getFullYear(), m.getMonth() - 1, 1)),
|
|
3584
|
+
className: "rounded p-1 text-tollerud-text-secondary hover:bg-tollerud-surface-hover",
|
|
3585
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronLeft, { size: 16 })
|
|
3586
|
+
}
|
|
3587
|
+
),
|
|
3588
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm font-medium text-tollerud-text-primary", children: viewMonth.toLocaleDateString(void 0, { month: "long", year: "numeric" }) }),
|
|
3589
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3590
|
+
"button",
|
|
3591
|
+
{
|
|
3592
|
+
type: "button",
|
|
3593
|
+
"aria-label": "Next month",
|
|
3594
|
+
onClick: () => setViewMonth((m) => new Date(m.getFullYear(), m.getMonth() + 1, 1)),
|
|
3595
|
+
className: "rounded p-1 text-tollerud-text-secondary hover:bg-tollerud-surface-hover",
|
|
3596
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronRight, { size: 16 })
|
|
3597
|
+
}
|
|
3598
|
+
)
|
|
3599
|
+
] }),
|
|
3600
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-7 gap-1 text-center", children: [
|
|
3601
|
+
WEEKDAYS.map((d) => /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[11px] font-medium text-tollerud-text-muted py-1", children: d }, d)),
|
|
3602
|
+
cells.map((date, i) => {
|
|
3603
|
+
if (!date) return /* @__PURE__ */ jsxRuntime.jsx("span", {}, i);
|
|
3604
|
+
const selected = value ? isSameDay(date, value) : false;
|
|
3605
|
+
const today = isSameDay(date, /* @__PURE__ */ new Date());
|
|
3606
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
3607
|
+
"button",
|
|
3608
|
+
{
|
|
3609
|
+
type: "button",
|
|
3610
|
+
onClick: () => select(date),
|
|
3611
|
+
className: cn(
|
|
3612
|
+
"h-8 w-8 rounded-full text-sm transition-colors duration-[150ms]",
|
|
3613
|
+
selected ? "bg-tollerud-yellow text-tollerud-noir-black font-medium" : "text-tollerud-text-secondary hover:bg-tollerud-surface-hover",
|
|
3614
|
+
!selected && today && "ring-1 ring-tollerud-yellow/40"
|
|
3615
|
+
),
|
|
3616
|
+
children: date.getDate()
|
|
3617
|
+
},
|
|
3618
|
+
i
|
|
3619
|
+
);
|
|
3620
|
+
})
|
|
3621
|
+
] })
|
|
3622
|
+
]
|
|
3623
|
+
}
|
|
3624
|
+
),
|
|
3625
|
+
error && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-tollerud-error mt-0.5", children: error })
|
|
3626
|
+
] });
|
|
3627
|
+
}
|
|
3628
|
+
DatePicker.displayName = "DatePicker";
|
|
3629
|
+
function formatBytes(bytes) {
|
|
3630
|
+
if (bytes < 1024) return `${bytes} B`;
|
|
3631
|
+
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
3632
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
3633
|
+
}
|
|
3634
|
+
function FileUpload({
|
|
3635
|
+
label,
|
|
3636
|
+
description,
|
|
3637
|
+
error,
|
|
3638
|
+
accept,
|
|
3639
|
+
multiple,
|
|
3640
|
+
onFilesChange,
|
|
3641
|
+
className,
|
|
3642
|
+
disabled
|
|
3643
|
+
}) {
|
|
3644
|
+
const id = React2.useId();
|
|
3645
|
+
const inputRef = React2.useRef(null);
|
|
3646
|
+
const [files, setFiles] = React2.useState([]);
|
|
3647
|
+
const [dragging, setDragging] = React2.useState(false);
|
|
3648
|
+
const setAndNotify = (next) => {
|
|
3649
|
+
setFiles(next);
|
|
3650
|
+
onFilesChange?.(next);
|
|
3651
|
+
};
|
|
3652
|
+
const addFiles = (incoming) => {
|
|
3653
|
+
if (!incoming || incoming.length === 0) return;
|
|
3654
|
+
const incomingArr = Array.from(incoming);
|
|
3655
|
+
setAndNotify(multiple ? [...files, ...incomingArr] : [incomingArr[0]]);
|
|
3656
|
+
};
|
|
3657
|
+
const removeAt = (index) => {
|
|
3658
|
+
setAndNotify(files.filter((_, i) => i !== index));
|
|
3659
|
+
};
|
|
3660
|
+
const onDrop = (e) => {
|
|
3661
|
+
e.preventDefault();
|
|
3662
|
+
setDragging(false);
|
|
3663
|
+
if (disabled) return;
|
|
3664
|
+
addFiles(e.dataTransfer.files);
|
|
3665
|
+
};
|
|
3666
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("flex flex-col gap-1.5", className), children: [
|
|
3667
|
+
label && /* @__PURE__ */ jsxRuntime.jsx("label", { htmlFor: id, className: "text-xs font-medium text-tollerud-text-muted", children: label }),
|
|
3668
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
3669
|
+
"div",
|
|
3670
|
+
{
|
|
3671
|
+
role: "button",
|
|
3672
|
+
tabIndex: disabled ? -1 : 0,
|
|
3673
|
+
"aria-disabled": disabled,
|
|
3674
|
+
onClick: () => !disabled && inputRef.current?.click(),
|
|
3675
|
+
onKeyDown: (e) => {
|
|
3676
|
+
if (!disabled && (e.key === "Enter" || e.key === " ")) {
|
|
3677
|
+
e.preventDefault();
|
|
3678
|
+
inputRef.current?.click();
|
|
3679
|
+
}
|
|
3680
|
+
},
|
|
3681
|
+
onDragOver: (e) => {
|
|
3682
|
+
e.preventDefault();
|
|
3683
|
+
if (!disabled) setDragging(true);
|
|
3684
|
+
},
|
|
3685
|
+
onDragLeave: () => setDragging(false),
|
|
3686
|
+
onDrop,
|
|
3687
|
+
className: cn(
|
|
3688
|
+
"flex flex-col items-center justify-center gap-2 rounded-lg border border-dashed px-6 py-8 text-center cursor-pointer transition-colors duration-[150ms]",
|
|
3689
|
+
dragging ? "border-tollerud-yellow bg-tollerud-yellow/[0.06]" : "border-tollerud-border bg-tollerud-surface-raised hover:border-tollerud-text-secondary",
|
|
3690
|
+
error && "border-tollerud-error",
|
|
3691
|
+
disabled && "opacity-50 pointer-events-none"
|
|
3692
|
+
),
|
|
3693
|
+
children: [
|
|
3694
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Upload, { size: 20, className: "text-tollerud-text-muted" }),
|
|
3695
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-sm text-tollerud-text-secondary", children: [
|
|
3696
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium text-tollerud-yellow", children: "Click to upload" }),
|
|
3697
|
+
" or drag and drop"
|
|
3698
|
+
] }),
|
|
3699
|
+
description && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-tollerud-text-muted", children: description }),
|
|
3700
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3701
|
+
"input",
|
|
3702
|
+
{
|
|
3703
|
+
ref: inputRef,
|
|
3704
|
+
id,
|
|
3705
|
+
type: "file",
|
|
3706
|
+
accept,
|
|
3707
|
+
multiple,
|
|
3708
|
+
disabled,
|
|
3709
|
+
onChange: (e) => addFiles(e.target.files),
|
|
3710
|
+
className: "sr-only"
|
|
3711
|
+
}
|
|
3712
|
+
)
|
|
3713
|
+
]
|
|
3714
|
+
}
|
|
3715
|
+
),
|
|
3716
|
+
files.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("ul", { className: "flex flex-col gap-1.5", children: files.map((file, i) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3717
|
+
"li",
|
|
3718
|
+
{
|
|
3719
|
+
className: "flex items-center gap-2.5 rounded-md border border-tollerud-border bg-tollerud-surface-raised px-3 py-2 text-sm",
|
|
3720
|
+
children: [
|
|
3721
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.File, { size: 15, className: "shrink-0 text-tollerud-text-muted" }),
|
|
3722
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex-1 truncate text-tollerud-text-primary", children: file.name }),
|
|
3723
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "shrink-0 text-xs text-tollerud-text-muted", children: formatBytes(file.size) }),
|
|
3724
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3725
|
+
"button",
|
|
3726
|
+
{
|
|
3727
|
+
type: "button",
|
|
3728
|
+
"aria-label": `Remove ${file.name}`,
|
|
3729
|
+
onClick: () => removeAt(i),
|
|
3730
|
+
className: "shrink-0 text-tollerud-text-muted hover:text-tollerud-text-primary transition-colors duration-[150ms]",
|
|
3731
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { size: 14 })
|
|
3732
|
+
}
|
|
3733
|
+
)
|
|
3734
|
+
]
|
|
3735
|
+
},
|
|
3736
|
+
`${file.name}-${i}`
|
|
3737
|
+
)) }),
|
|
3738
|
+
error && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-tollerud-error", children: error })
|
|
3739
|
+
] });
|
|
3740
|
+
}
|
|
3741
|
+
FileUpload.displayName = "FileUpload";
|
|
3742
|
+
function TagInput({
|
|
3743
|
+
value: valueProp,
|
|
3744
|
+
defaultValue = [],
|
|
3745
|
+
onChange,
|
|
3746
|
+
label,
|
|
3747
|
+
error,
|
|
3748
|
+
placeholder = "Add a tag\u2026",
|
|
3749
|
+
max,
|
|
3750
|
+
className,
|
|
3751
|
+
disabled
|
|
3752
|
+
}) {
|
|
3753
|
+
const id = React2.useId();
|
|
3754
|
+
const isControlled = valueProp !== void 0;
|
|
3755
|
+
const [internalTags, setInternalTags] = React2.useState(defaultValue);
|
|
3756
|
+
const tags = isControlled ? valueProp : internalTags;
|
|
3757
|
+
const [draft, setDraft] = React2.useState("");
|
|
3758
|
+
const setAndNotify = (next) => {
|
|
3759
|
+
if (!isControlled) setInternalTags(next);
|
|
3760
|
+
onChange?.(next);
|
|
3761
|
+
};
|
|
3762
|
+
const addTag = (raw) => {
|
|
3763
|
+
const tag = raw.trim();
|
|
3764
|
+
if (!tag || tags.includes(tag)) return;
|
|
3765
|
+
if (max && tags.length >= max) return;
|
|
3766
|
+
setAndNotify([...tags, tag]);
|
|
3767
|
+
setDraft("");
|
|
3768
|
+
};
|
|
3769
|
+
const removeTag = (index) => {
|
|
3770
|
+
setAndNotify(tags.filter((_, i) => i !== index));
|
|
3771
|
+
};
|
|
3772
|
+
const onKeyDown = (e) => {
|
|
3773
|
+
if (e.key === "Enter" || e.key === ",") {
|
|
3774
|
+
e.preventDefault();
|
|
3775
|
+
addTag(draft);
|
|
3776
|
+
} else if (e.key === "Backspace" && draft === "" && tags.length > 0) {
|
|
3777
|
+
removeTag(tags.length - 1);
|
|
3778
|
+
}
|
|
3779
|
+
};
|
|
3780
|
+
const atMax = max ? tags.length >= max : false;
|
|
3781
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("flex flex-col gap-1", className), children: [
|
|
3782
|
+
label && /* @__PURE__ */ jsxRuntime.jsx("label", { htmlFor: id, className: "text-xs font-medium text-tollerud-text-muted", children: label }),
|
|
3783
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
3784
|
+
"div",
|
|
3785
|
+
{
|
|
3786
|
+
className: cn(
|
|
3787
|
+
"flex flex-wrap items-center gap-1.5 rounded px-2.5 py-1.5",
|
|
3788
|
+
"bg-tollerud-surface-raised border",
|
|
3789
|
+
"transition-[border-color] duration-[150ms]",
|
|
3790
|
+
"focus-within:border-tollerud-yellow focus-within:shadow-[0_0_0_1px_#E8D500]",
|
|
3791
|
+
error ? "border-tollerud-error" : "border-tollerud-border",
|
|
3792
|
+
disabled && "opacity-50 pointer-events-none"
|
|
3793
|
+
),
|
|
3794
|
+
children: [
|
|
3795
|
+
tags.map((tag, i) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3796
|
+
"span",
|
|
3797
|
+
{
|
|
3798
|
+
className: "inline-flex items-center gap-1 rounded-full bg-tollerud-surface-hover px-2.5 py-0.5 text-xs font-medium text-tollerud-text-primary",
|
|
3799
|
+
children: [
|
|
3800
|
+
tag,
|
|
3801
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3802
|
+
"button",
|
|
3803
|
+
{
|
|
3804
|
+
type: "button",
|
|
3805
|
+
"aria-label": `Remove ${tag}`,
|
|
3806
|
+
onClick: () => removeTag(i),
|
|
3807
|
+
className: "text-tollerud-text-muted hover:text-tollerud-text-primary transition-colors duration-[150ms]",
|
|
3808
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { size: 12 })
|
|
3809
|
+
}
|
|
3810
|
+
)
|
|
3811
|
+
]
|
|
3812
|
+
},
|
|
3813
|
+
`${tag}-${i}`
|
|
3814
|
+
)),
|
|
3815
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3816
|
+
"input",
|
|
3817
|
+
{
|
|
3818
|
+
id,
|
|
3819
|
+
value: draft,
|
|
3820
|
+
disabled: disabled || atMax,
|
|
3821
|
+
onChange: (e) => setDraft(e.target.value),
|
|
3822
|
+
onKeyDown,
|
|
3823
|
+
onBlur: () => addTag(draft),
|
|
3824
|
+
placeholder: atMax ? "" : placeholder,
|
|
3825
|
+
className: cn(
|
|
3826
|
+
"min-w-[6rem] flex-1 bg-transparent py-0.5 text-sm text-tollerud-text-primary",
|
|
3827
|
+
"placeholder:text-tollerud-text-muted focus:outline-none"
|
|
3828
|
+
)
|
|
3829
|
+
}
|
|
3830
|
+
)
|
|
3831
|
+
]
|
|
3832
|
+
}
|
|
3833
|
+
),
|
|
3834
|
+
error && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-tollerud-error mt-0.5", children: error })
|
|
3835
|
+
] });
|
|
3836
|
+
}
|
|
3837
|
+
TagInput.displayName = "TagInput";
|
|
2678
3838
|
|
|
3839
|
+
exports.Accordion = Accordion;
|
|
3840
|
+
exports.AccordionContent = AccordionContent;
|
|
3841
|
+
exports.AccordionItem = AccordionItem;
|
|
3842
|
+
exports.AccordionTrigger = AccordionTrigger;
|
|
2679
3843
|
exports.ActionDiff = ActionDiff;
|
|
2680
3844
|
exports.ActionRow = ActionRow;
|
|
2681
3845
|
exports.Alert = Alert;
|
|
2682
3846
|
exports.AlertInbox = AlertInbox;
|
|
2683
3847
|
exports.ApprovalCard = ApprovalCard;
|
|
3848
|
+
exports.Avatar = Avatar;
|
|
3849
|
+
exports.AvatarGroup = AvatarGroup;
|
|
2684
3850
|
exports.BackupStatusPanel = BackupStatusPanel;
|
|
2685
3851
|
exports.Badge = Badge;
|
|
2686
3852
|
exports.BentoDashboard = BentoDashboard;
|
|
3853
|
+
exports.Breadcrumb = Breadcrumb;
|
|
2687
3854
|
exports.Button = Button;
|
|
2688
3855
|
exports.Card = Card;
|
|
2689
3856
|
exports.Checkbox = Checkbox;
|
|
2690
3857
|
exports.CodeBlock = CodeBlock;
|
|
3858
|
+
exports.Combobox = Combobox;
|
|
2691
3859
|
exports.CommandMenu = CommandMenu;
|
|
2692
3860
|
exports.Container = Container;
|
|
2693
3861
|
exports.DataTable = DataTable;
|
|
3862
|
+
exports.DatePicker = DatePicker;
|
|
2694
3863
|
exports.Dialog = Dialog;
|
|
2695
3864
|
exports.DialogClose = DialogClose;
|
|
2696
3865
|
exports.DialogContent = DialogContent;
|
|
@@ -2701,6 +3870,7 @@ exports.DialogOverlay = DialogOverlay;
|
|
|
2701
3870
|
exports.DialogPortal = DialogPortal;
|
|
2702
3871
|
exports.DialogTitle = DialogTitle;
|
|
2703
3872
|
exports.DialogTrigger = DialogTrigger;
|
|
3873
|
+
exports.Divider = Divider;
|
|
2704
3874
|
exports.DockerStackCard = DockerStackCard;
|
|
2705
3875
|
exports.DropdownMenu = DropdownMenu;
|
|
2706
3876
|
exports.DropdownMenuContent = DropdownMenuContent;
|
|
@@ -2714,18 +3884,27 @@ exports.EmptyDescription = EmptyDescription;
|
|
|
2714
3884
|
exports.EmptyHeader = EmptyHeader;
|
|
2715
3885
|
exports.EmptyIcon = EmptyIcon;
|
|
2716
3886
|
exports.EmptyTitle = EmptyTitle;
|
|
3887
|
+
exports.FileUpload = FileUpload;
|
|
2717
3888
|
exports.Footer = Footer;
|
|
3889
|
+
exports.FormRow = FormRow;
|
|
2718
3890
|
exports.GlowCard = GlowCard;
|
|
2719
3891
|
exports.HostCard = HostCard;
|
|
2720
3892
|
exports.IncidentCard = IncidentCard;
|
|
2721
3893
|
exports.Input = Input;
|
|
2722
3894
|
exports.Kbd = Kbd;
|
|
2723
3895
|
exports.LogViewer = LogViewer;
|
|
3896
|
+
exports.Meter = Meter;
|
|
2724
3897
|
exports.NoirGlowBackground = NoirGlowBackground;
|
|
3898
|
+
exports.Pagination = Pagination;
|
|
3899
|
+
exports.Panel = Panel;
|
|
3900
|
+
exports.PasswordInput = PasswordInput;
|
|
3901
|
+
exports.Pill = Pill;
|
|
3902
|
+
exports.PricingCard = PricingCard;
|
|
2725
3903
|
exports.Progress = Progress;
|
|
2726
3904
|
exports.Radio = Radio;
|
|
2727
3905
|
exports.RadioGroup = RadioGroup;
|
|
2728
3906
|
exports.RollbackPlan = RollbackPlan;
|
|
3907
|
+
exports.Segmented = Segmented;
|
|
2729
3908
|
exports.Select = Select;
|
|
2730
3909
|
exports.ServiceHealthCard = ServiceHealthCard;
|
|
2731
3910
|
exports.Sheet = Sheet;
|
|
@@ -2736,13 +3915,16 @@ exports.SheetHeader = SheetHeader;
|
|
|
2736
3915
|
exports.SheetTitle = SheetTitle;
|
|
2737
3916
|
exports.SheetTrigger = SheetTrigger;
|
|
2738
3917
|
exports.Skeleton = Skeleton;
|
|
3918
|
+
exports.Slider = Slider;
|
|
2739
3919
|
exports.StatCard = StatCard;
|
|
2740
3920
|
exports.StatusDot = StatusDot;
|
|
3921
|
+
exports.Stepper = Stepper;
|
|
2741
3922
|
exports.Switch = Switch;
|
|
2742
3923
|
exports.Tabs = Tabs;
|
|
2743
3924
|
exports.TabsContent = TabsContent;
|
|
2744
3925
|
exports.TabsList = TabsList;
|
|
2745
3926
|
exports.TabsTrigger = TabsTrigger;
|
|
3927
|
+
exports.TagInput = TagInput;
|
|
2746
3928
|
exports.Textarea = Textarea;
|
|
2747
3929
|
exports.Timeline = Timeline;
|
|
2748
3930
|
exports.Toaster = Toaster;
|