@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/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;