defuss-shadcn 0.2.0 → 0.3.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
@@ -17,47 +17,6 @@ function clsx() {
17
17
  return n;
18
18
  }
19
19
 
20
- const falsyToString = (value) => typeof value === "boolean" ? `${value}` : value === 0 ? "0" : value;
21
- const cx = clsx;
22
- const cva = (base, config) => (props) => {
23
- var _config_compoundVariants;
24
- if ((config === null || config === void 0 ? void 0 : config.variants) == null) return cx(base, props === null || props === void 0 ? void 0 : props.class, props === null || props === void 0 ? void 0 : props.className);
25
- const { variants, defaultVariants } = config;
26
- const getVariantClassNames = Object.keys(variants).map((variant) => {
27
- const variantProp = props === null || props === void 0 ? void 0 : props[variant];
28
- const defaultVariantProp = defaultVariants === null || defaultVariants === void 0 ? void 0 : defaultVariants[variant];
29
- if (variantProp === null) return null;
30
- const variantKey = falsyToString(variantProp) || falsyToString(defaultVariantProp);
31
- return variants[variant][variantKey];
32
- });
33
- const propsWithoutUndefined = props && Object.entries(props).reduce((acc, param) => {
34
- let [key, value] = param;
35
- if (value === void 0) {
36
- return acc;
37
- }
38
- acc[key] = value;
39
- return acc;
40
- }, {});
41
- const getCompoundVariantClassNames = config === null || config === void 0 ? void 0 : (_config_compoundVariants = config.compoundVariants) === null || _config_compoundVariants === void 0 ? void 0 : _config_compoundVariants.reduce((acc, param) => {
42
- let { class: cvClass, className: cvClassName, ...compoundVariantOptions } = param;
43
- return Object.entries(compoundVariantOptions).every((param2) => {
44
- let [key, value] = param2;
45
- return Array.isArray(value) ? value.includes({
46
- ...defaultVariants,
47
- ...propsWithoutUndefined
48
- }[key]) : {
49
- ...defaultVariants,
50
- ...propsWithoutUndefined
51
- }[key] === value;
52
- }) ? [
53
- ...acc,
54
- cvClass,
55
- cvClassName
56
- ] : acc;
57
- }, []);
58
- return cx(base, getVariantClassNames, getCompoundVariantClassNames, props === null || props === void 0 ? void 0 : props.class, props === null || props === void 0 ? void 0 : props.className);
59
- };
60
-
61
20
  const concatArrays = (array1, array2) => {
62
21
  const combinedArray = new Array(array1.length + array2.length);
63
22
  for (let i = 0; i < array1.length; i++) {
@@ -3068,55 +3027,100 @@ const twMerge = /* @__PURE__ */ createTailwindMerge(getDefaultConfig);
3068
3027
 
3069
3028
  const cn = (...inputs) => twMerge(clsx(inputs)).trim().split(/\s+/).filter((className, index, array) => index === 0 || className !== array[index - 1]).join(" ");
3070
3029
 
3071
- const buttonVariants = cva(
3072
- "",
3073
- {
3074
- variants: {
3075
- variant: {
3076
- default: "btn",
3077
- outline: "btn-outline",
3078
- secondary: "btn-secondary",
3079
- ghost: "btn-ghost",
3080
- destructive: "btn-destructive",
3081
- link: "btn-link"
3082
- },
3083
- size: {
3084
- default: "",
3085
- xs: "btn-xs",
3086
- sm: "btn-sm",
3087
- lg: "btn-lg",
3088
- icon: "btn-icon",
3089
- "icon-xs": "btn-icon-xs",
3090
- "icon-sm": "btn-icon-sm",
3091
- "icon-lg": "btn-icon-lg"
3092
- }
3093
- },
3094
- defaultVariants: {
3095
- variant: "default",
3096
- size: "default"
3030
+ const initAccordion = (accordion, type, collapsible) => {
3031
+ accordion.addEventListener("click", (event) => {
3032
+ if (type !== "single") {
3033
+ return;
3097
3034
  }
3098
- }
3099
- );
3100
- const Button = ({ variant, size, className, children, ...props }) => {
3035
+ const summary = event.target.closest("summary");
3036
+ if (!summary) return;
3037
+ const details = summary.closest("details");
3038
+ if (!details) return;
3039
+ const isOpen = details.hasAttribute("open");
3040
+ if (isOpen && !collapsible) {
3041
+ event.preventDefault();
3042
+ return;
3043
+ }
3044
+ accordion.querySelectorAll("details").forEach((detailsEl) => {
3045
+ if (detailsEl !== details) {
3046
+ detailsEl.removeAttribute("open");
3047
+ }
3048
+ });
3049
+ });
3050
+ };
3051
+ const Accordion = ({
3052
+ className,
3053
+ children,
3054
+ type = "single",
3055
+ collapsible = false,
3056
+ ...props
3057
+ }) => {
3058
+ const ref = defuss.createRef();
3101
3059
  return /* @__PURE__ */ jsxRuntime.jsx(
3102
- "button",
3060
+ "section",
3103
3061
  {
3104
- class: cn(buttonVariants({ variant, size }), className),
3062
+ ref,
3063
+ class: cn("accordion", className),
3064
+ onMount: () => initAccordion(ref.current, type, collapsible),
3105
3065
  ...props,
3106
3066
  children
3107
3067
  }
3108
3068
  );
3109
3069
  };
3070
+ const AccordionItem = ({ className, children, ...props }) => /* @__PURE__ */ jsxRuntime.jsx("details", { class: cn("group border-b last:border-b-0", className), ...props, children });
3071
+ const AccordionTrigger = ({ className, children, ...props }) => /* @__PURE__ */ jsxRuntime.jsx("summary", { class: cn("list-none [&::-webkit-details-marker]:hidden w-full cursor-pointer px-3 py-3 text-left text-sm font-medium focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] transition-all outline-none rounded-md", className), ...props, children });
3072
+ const AccordionContent = ({ className, children, ...props }) => /* @__PURE__ */ jsxRuntime.jsx("section", { class: cn("px-3 pb-3 pt-0 text-sm text-muted-foreground", className), ...props, children });
3110
3073
 
3111
- const badgeVariants = cva(
3112
- "",
3074
+ const falsyToString = (value) => typeof value === "boolean" ? `${value}` : value === 0 ? "0" : value;
3075
+ const cx = clsx;
3076
+ const cva = (base, config) => (props) => {
3077
+ var _config_compoundVariants;
3078
+ if ((config === null || config === void 0 ? void 0 : config.variants) == null) return cx(base, props === null || props === void 0 ? void 0 : props.class, props === null || props === void 0 ? void 0 : props.className);
3079
+ const { variants, defaultVariants } = config;
3080
+ const getVariantClassNames = Object.keys(variants).map((variant) => {
3081
+ const variantProp = props === null || props === void 0 ? void 0 : props[variant];
3082
+ const defaultVariantProp = defaultVariants === null || defaultVariants === void 0 ? void 0 : defaultVariants[variant];
3083
+ if (variantProp === null) return null;
3084
+ const variantKey = falsyToString(variantProp) || falsyToString(defaultVariantProp);
3085
+ return variants[variant][variantKey];
3086
+ });
3087
+ const propsWithoutUndefined = props && Object.entries(props).reduce((acc, param) => {
3088
+ let [key, value] = param;
3089
+ if (value === void 0) {
3090
+ return acc;
3091
+ }
3092
+ acc[key] = value;
3093
+ return acc;
3094
+ }, {});
3095
+ const getCompoundVariantClassNames = config === null || config === void 0 ? void 0 : (_config_compoundVariants = config.compoundVariants) === null || _config_compoundVariants === void 0 ? void 0 : _config_compoundVariants.reduce((acc, param) => {
3096
+ let { class: cvClass, className: cvClassName, ...compoundVariantOptions } = param;
3097
+ return Object.entries(compoundVariantOptions).every((param2) => {
3098
+ let [key, value] = param2;
3099
+ return Array.isArray(value) ? value.includes({
3100
+ ...defaultVariants,
3101
+ ...propsWithoutUndefined
3102
+ }[key]) : {
3103
+ ...defaultVariants,
3104
+ ...propsWithoutUndefined
3105
+ }[key] === value;
3106
+ }) ? [
3107
+ ...acc,
3108
+ cvClass,
3109
+ cvClassName
3110
+ ] : acc;
3111
+ }, []);
3112
+ return cx(base, getVariantClassNames, getCompoundVariantClassNames, props === null || props === void 0 ? void 0 : props.class, props === null || props === void 0 ? void 0 : props.className);
3113
+ };
3114
+
3115
+ const alertVariants = cva(
3116
+ "alert w-full relative",
3113
3117
  {
3114
3118
  variants: {
3115
3119
  variant: {
3116
- default: "badge",
3117
- secondary: "badge-secondary",
3118
- destructive: "badge-destructive",
3119
- outline: "badge-outline"
3120
+ default: "",
3121
+ destructive: "alert-destructive",
3122
+ warning: "alert-warning",
3123
+ info: "alert-info"
3120
3124
  }
3121
3125
  },
3122
3126
  defaultVariants: {
@@ -3124,46 +3128,67 @@ const badgeVariants = cva(
3124
3128
  }
3125
3129
  }
3126
3130
  );
3127
- const Badge = ({ className, variant, children, ...props }) => {
3128
- return /* @__PURE__ */ jsxRuntime.jsx("span", { class: cn(badgeVariants({ variant }), className), ...props, children });
3131
+ const Alert = ({ className, variant, children, ...props }) => {
3132
+ return /* @__PURE__ */ jsxRuntime.jsx(
3133
+ "div",
3134
+ {
3135
+ role: "alert",
3136
+ class: cn(alertVariants({ variant }), className),
3137
+ ...props,
3138
+ children
3139
+ }
3140
+ );
3129
3141
  };
3130
-
3131
- const Card = ({ className, children, ...props }) => /* @__PURE__ */ jsxRuntime.jsx("div", { class: cn("card", className), ...props, children });
3132
- const CardHeader = ({ className, children, ...props }) => /* @__PURE__ */ jsxRuntime.jsx("header", { class: cn(className), ...props, children });
3133
- const CardTitle = ({ className, children, ...props }) => /* @__PURE__ */ jsxRuntime.jsx("h2", { class: cn(className), ...props, children });
3134
- const CardDescription = ({ className, children, ...props }) => /* @__PURE__ */ jsxRuntime.jsx("p", { class: cn(className), ...props, children });
3135
- const CardContent = ({ className, children, ...props }) => /* @__PURE__ */ jsxRuntime.jsx("section", { class: cn(className), ...props, children });
3136
- const CardFooter = ({ className, children, ...props }) => /* @__PURE__ */ jsxRuntime.jsx("footer", { class: cn(className), ...props, children });
3137
-
3138
- const initAccordion = (accordion) => {
3139
- accordion.addEventListener("click", (event) => {
3140
- const summary = event.target.closest("summary");
3141
- if (!summary) return;
3142
- const details = summary.closest("details");
3143
- if (!details) return;
3144
- accordion.querySelectorAll("details").forEach((detailsEl) => {
3145
- if (detailsEl !== details) {
3146
- detailsEl.removeAttribute("open");
3147
- }
3148
- });
3149
- });
3142
+ const AlertTitle = ({ className, children, ...props }) => {
3143
+ return /* @__PURE__ */ jsxRuntime.jsx(
3144
+ "h2",
3145
+ {
3146
+ class: cn(className),
3147
+ ...props,
3148
+ children
3149
+ }
3150
+ );
3150
3151
  };
3151
- const Accordion = ({ className, children, ...props }) => {
3152
- const ref = defuss.createRef();
3152
+ const AlertDescription = ({ className, children, ...props }) => {
3153
3153
  return /* @__PURE__ */ jsxRuntime.jsx(
3154
3154
  "section",
3155
3155
  {
3156
- ref,
3157
- class: cn("accordion", className),
3158
- onMount: () => initAccordion(ref.current),
3156
+ class: cn(className),
3159
3157
  ...props,
3160
3158
  children
3161
3159
  }
3162
3160
  );
3163
3161
  };
3164
- const AccordionItem = ({ className, children, ...props }) => /* @__PURE__ */ jsxRuntime.jsx("details", { class: cn("group border-b last:border-b-0", className), ...props, children });
3165
- const AccordionTrigger = ({ className, children, ...props }) => /* @__PURE__ */ jsxRuntime.jsx("summary", { class: cn("list-none [&::-webkit-details-marker]:hidden w-full focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] transition-all outline-none rounded-md", className), ...props, children });
3166
- const AccordionContent = ({ className, children, ...props }) => /* @__PURE__ */ jsxRuntime.jsx("section", { class: cn(className), ...props, children });
3162
+ const AlertAction = ({ className, children, ...props }) => {
3163
+ return /* @__PURE__ */ jsxRuntime.jsx(
3164
+ "footer",
3165
+ {
3166
+ class: cn("col-start-2 mt-1", className),
3167
+ ...props,
3168
+ children
3169
+ }
3170
+ );
3171
+ };
3172
+
3173
+ const AlertDialog = ({ className, children, ...props }) => /* @__PURE__ */ jsxRuntime.jsx("dialog", { class: cn("dialog", className), ...props, children });
3174
+ const AlertDialogTrigger = ({ dialogId, className, children, ...props }) => /* @__PURE__ */ jsxRuntime.jsx(
3175
+ "button",
3176
+ {
3177
+ type: "button",
3178
+ class: cn(className),
3179
+ onClick: () => {
3180
+ const dialog = document.getElementById(dialogId);
3181
+ dialog?.showModal();
3182
+ },
3183
+ ...props,
3184
+ children
3185
+ }
3186
+ );
3187
+ const AlertDialogContent = ({ className, children, ...props }) => /* @__PURE__ */ jsxRuntime.jsx("div", { class: cn(className), ...props, children });
3188
+ const AlertDialogHeader = ({ className, children, ...props }) => /* @__PURE__ */ jsxRuntime.jsx("header", { class: cn(className), ...props, children });
3189
+ const AlertDialogTitle = ({ className, children, ...props }) => /* @__PURE__ */ jsxRuntime.jsx("h2", { class: cn(className), ...props, children });
3190
+ const AlertDialogDescription = ({ className, children, ...props }) => /* @__PURE__ */ jsxRuntime.jsx("p", { class: cn(className), ...props, children });
3191
+ const AlertDialogFooter = ({ className, children, ...props }) => /* @__PURE__ */ jsxRuntime.jsx("footer", { class: cn(className), ...props, children });
3167
3192
 
3168
3193
  const Avatar = ({ className, ...props }) => /* @__PURE__ */ jsxRuntime.jsx(
3169
3194
  "img",
@@ -3189,6 +3214,26 @@ const AvatarGroup = ({ className, children, ...props }) => /* @__PURE__ */ jsxRu
3189
3214
  }
3190
3215
  );
3191
3216
 
3217
+ const badgeVariants = cva(
3218
+ "",
3219
+ {
3220
+ variants: {
3221
+ variant: {
3222
+ default: "badge",
3223
+ secondary: "badge-secondary",
3224
+ destructive: "badge-destructive",
3225
+ outline: "badge-outline"
3226
+ }
3227
+ },
3228
+ defaultVariants: {
3229
+ variant: "default"
3230
+ }
3231
+ }
3232
+ );
3233
+ const Badge = ({ className, variant, children, ...props }) => {
3234
+ return /* @__PURE__ */ jsxRuntime.jsx("span", { class: cn(badgeVariants({ variant }), className), ...props, children });
3235
+ };
3236
+
3192
3237
  const Breadcrumb = ({ className, children, ...props }) => /* @__PURE__ */ jsxRuntime.jsx("nav", { class: cn(className), "aria-label": "Breadcrumb", ...props, children });
3193
3238
  const BreadcrumbList = ({ className, children, ...props }) => /* @__PURE__ */ jsxRuntime.jsx("ol", { class: cn("text-muted-foreground flex flex-wrap items-center gap-1.5 text-sm break-words sm:gap-2.5", className), ...props, children });
3194
3239
  const BreadcrumbItem = ({ className, children, ...props }) => /* @__PURE__ */ jsxRuntime.jsx("li", { class: cn("inline-flex items-center gap-1.5", className), ...props, children });
@@ -3196,232 +3241,52 @@ const BreadcrumbLink = ({ className, children, ...props }) => /* @__PURE__ */ js
3196
3241
  const BreadcrumbPage = ({ className, children, ...props }) => /* @__PURE__ */ jsxRuntime.jsx("span", { class: cn("text-foreground font-normal", className), "aria-current": "page", ...props, children });
3197
3242
  const BreadcrumbSeparator = ({ className, children, ...props }) => /* @__PURE__ */ jsxRuntime.jsx("li", { class: cn(className), "aria-hidden": "true", ...props, children: children || "\u203A" });
3198
3243
 
3199
- const Input = ({ className, type, ...props }) => {
3200
- return /* @__PURE__ */ jsxRuntime.jsx(
3201
- "input",
3202
- {
3203
- type,
3204
- class: cn(
3205
- "input",
3206
- className
3207
- ),
3208
- ...props
3209
- }
3210
- );
3211
- };
3212
-
3213
- const Label = ({ className, children, ...props }) => {
3214
- return /* @__PURE__ */ jsxRuntime.jsx("label", { class: cn("label", className), ...props, children });
3215
- };
3216
-
3217
- const Textarea = ({ className, ...props }) => {
3218
- return /* @__PURE__ */ jsxRuntime.jsx("textarea", { class: cn("textarea", className), ...props });
3219
- };
3220
-
3221
- const Progress = ({ className, value = 0, max = 100, ...props }) => {
3222
- const percentage = Math.max(0, Math.min(100, value / max * 100));
3223
- return /* @__PURE__ */ jsxRuntime.jsx(
3224
- "div",
3225
- {
3226
- class: cn("bg-primary/20 relative h-2 w-full overflow-hidden rounded-full", className),
3227
- role: "progressbar",
3228
- "aria-valuemin": 0,
3229
- "aria-valuemax": max,
3230
- "aria-valuenow": value,
3231
- ...props,
3232
- children: /* @__PURE__ */ jsxRuntime.jsx("div", { class: "bg-primary h-full w-full flex-1 transition-all", style: `width: ${percentage}%` })
3233
- }
3234
- );
3235
- };
3236
-
3237
- const Skeleton = ({ className, ...props }) => /* @__PURE__ */ jsxRuntime.jsx("div", { class: cn("bg-accent animate-pulse rounded-md", className), ...props });
3238
-
3239
- const itemVariants = cva(
3240
- "group/item flex items-center border text-sm rounded-md transition-colors [a]:hover:bg-accent/50 [a]:transition-colors duration-100 flex-wrap outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] gap-4",
3244
+ const buttonVariants = cva(
3245
+ "",
3241
3246
  {
3242
3247
  variants: {
3243
3248
  variant: {
3244
- default: "border-transparent",
3245
- outline: "border-border",
3246
- muted: "border-transparent bg-muted/50"
3249
+ default: "btn",
3250
+ outline: "btn-outline",
3251
+ secondary: "btn-secondary",
3252
+ ghost: "btn-ghost",
3253
+ destructive: "btn-destructive",
3254
+ link: "btn-link"
3247
3255
  },
3248
3256
  size: {
3249
- default: "p-4",
3250
- compact: "py-3 px-4 gap-2.5"
3251
- }
3252
- },
3253
- defaultVariants: {
3254
- variant: "outline",
3255
- size: "default"
3256
- }
3257
- }
3258
- );
3259
- const Item = ({ className, variant, size, children, ...props }) => /* @__PURE__ */ jsxRuntime.jsx("article", { class: cn(itemVariants({ variant, size }), className), ...props, children });
3260
- const ItemLeading = ({ className, children, ...props }) => /* @__PURE__ */ jsxRuntime.jsx("div", { class: cn("flex shrink-0 items-center justify-center gap-2", className), ...props, children });
3261
- const ItemContent = ({ className, children, ...props }) => /* @__PURE__ */ jsxRuntime.jsx("div", { class: cn("flex flex-1 flex-col gap-1", className), ...props, children });
3262
- const ItemTitle = ({ className, children, ...props }) => /* @__PURE__ */ jsxRuntime.jsx("h3", { class: cn("flex w-fit items-center gap-2 text-sm leading-snug font-medium", className), ...props, children });
3263
- const ItemDescription = ({ className, children, ...props }) => /* @__PURE__ */ jsxRuntime.jsx("p", { class: cn("text-muted-foreground line-clamp-2 text-sm leading-normal font-normal text-balance [&>a:hover]:text-primary [&>a]:underline [&>a]:underline-offset-4", className), ...props, children });
3264
- const ItemTrailing = ({ className, children, ...props }) => /* @__PURE__ */ jsxRuntime.jsx("div", { class: cn("flex items-center gap-2", className), ...props, children });
3265
-
3266
- const Kbd = ({ className, children, ...props }) => {
3267
- return /* @__PURE__ */ jsxRuntime.jsx(
3268
- "kbd",
3269
- {
3270
- class: cn(
3271
- "kbd pointer-events-none inline-flex items-center justify-center select-none",
3272
- className
3273
- ),
3274
- ...props,
3275
- children
3276
- }
3277
- );
3278
- };
3279
- const KbdGroup = ({ className, children, ...props }) => {
3280
- return /* @__PURE__ */ jsxRuntime.jsx(
3281
- "div",
3282
- {
3283
- class: cn("kbd-group inline-flex items-center gap-1", className),
3284
- ...props,
3285
- children
3286
- }
3287
- );
3288
- };
3289
-
3290
- const Spinner = ({ className, ...props }) => {
3291
- return /* @__PURE__ */ jsxRuntime.jsx(
3292
- "svg",
3293
- {
3294
- role: "status",
3295
- "aria-label": "Loading",
3296
- class: cn("animate-spin", className),
3297
- xmlns: "http://www.w3.org/2000/svg",
3298
- fill: "none",
3299
- viewBox: "0 0 24 24",
3300
- stroke: "currentColor",
3301
- "stroke-width": "2",
3302
- "stroke-linecap": "round",
3303
- "stroke-linejoin": "round",
3304
- ...props,
3305
- children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M21 12a9 9 0 1 1-6.219-8.56" })
3306
- }
3307
- );
3308
- };
3309
-
3310
- const Separator = ({
3311
- className,
3312
- orientation = "horizontal",
3313
- ...props
3314
- }) => {
3315
- return /* @__PURE__ */ jsxRuntime.jsx(
3316
- "hr",
3317
- {
3318
- role: "separator",
3319
- "aria-orientation": orientation,
3320
- class: cn(className),
3321
- ...props
3322
- }
3323
- );
3324
- };
3325
-
3326
- const Tooltip = ({
3327
- className,
3328
- tooltip,
3329
- side,
3330
- align,
3331
- children,
3332
- ...props
3333
- }) => {
3334
- return /* @__PURE__ */ jsxRuntime.jsx(
3335
- "div",
3336
- {
3337
- "data-tooltip": tooltip,
3338
- "data-side": side,
3339
- "data-align": align,
3340
- class: cn(className),
3341
- ...props,
3342
- children
3343
- }
3344
- );
3345
- };
3346
-
3347
- const Form = ({ className, children, ...props }) => /* @__PURE__ */ jsxRuntime.jsx("form", { class: cn("form", className), ...props, children });
3348
- const FormField = ({ className, children, ...props }) => /* @__PURE__ */ jsxRuntime.jsx("div", { class: cn("field", className), ...props, children });
3349
-
3350
- const alertVariants = cva(
3351
- "alert w-full relative",
3352
- {
3353
- variants: {
3354
- variant: {
3355
3257
  default: "",
3356
- destructive: "alert-destructive"
3258
+ xs: "btn-xs",
3259
+ sm: "btn-sm",
3260
+ lg: "btn-lg",
3261
+ icon: "btn-icon",
3262
+ "icon-xs": "btn-icon-xs",
3263
+ "icon-sm": "btn-icon-sm",
3264
+ "icon-lg": "btn-icon-lg"
3357
3265
  }
3358
3266
  },
3359
3267
  defaultVariants: {
3360
- variant: "default"
3268
+ variant: "default",
3269
+ size: "default"
3361
3270
  }
3362
3271
  }
3363
3272
  );
3364
- const Alert = ({ className, variant, children, ...props }) => {
3365
- return /* @__PURE__ */ jsxRuntime.jsx(
3366
- "div",
3367
- {
3368
- role: "alert",
3369
- class: cn(alertVariants({ variant }), className),
3370
- ...props,
3371
- children
3372
- }
3373
- );
3374
- };
3375
- const AlertTitle = ({ className, children, ...props }) => {
3376
- return /* @__PURE__ */ jsxRuntime.jsx(
3377
- "h2",
3378
- {
3379
- class: cn(className),
3380
- ...props,
3381
- children
3382
- }
3383
- );
3384
- };
3385
- const AlertDescription = ({ className, children, ...props }) => {
3386
- return /* @__PURE__ */ jsxRuntime.jsx(
3387
- "section",
3388
- {
3389
- class: cn(className),
3390
- ...props,
3391
- children
3392
- }
3393
- );
3394
- };
3395
- const AlertAction = ({ className, children, ...props }) => {
3273
+ const Button = ({ variant, size, className, children, ...props }) => {
3396
3274
  return /* @__PURE__ */ jsxRuntime.jsx(
3397
- "footer",
3275
+ "button",
3398
3276
  {
3399
- class: cn("col-start-2 mt-1", className),
3277
+ class: cn(buttonVariants({ variant, size }), className),
3400
3278
  ...props,
3401
3279
  children
3402
3280
  }
3403
3281
  );
3404
3282
  };
3405
3283
 
3406
- const AlertDialog = ({ className, children, ...props }) => /* @__PURE__ */ jsxRuntime.jsx("dialog", { class: cn("dialog", className), ...props, children });
3407
- const AlertDialogTrigger = ({ dialogId, className, children, ...props }) => /* @__PURE__ */ jsxRuntime.jsx(
3408
- "button",
3409
- {
3410
- type: "button",
3411
- class: cn(className),
3412
- onClick: () => {
3413
- const dialog = document.getElementById(dialogId);
3414
- dialog?.showModal();
3415
- },
3416
- ...props,
3417
- children
3418
- }
3419
- );
3420
- const AlertDialogContent = ({ className, children, ...props }) => /* @__PURE__ */ jsxRuntime.jsx("div", { class: cn(className), ...props, children });
3421
- const AlertDialogHeader = ({ className, children, ...props }) => /* @__PURE__ */ jsxRuntime.jsx("header", { class: cn(className), ...props, children });
3422
- const AlertDialogTitle = ({ className, children, ...props }) => /* @__PURE__ */ jsxRuntime.jsx("h2", { class: cn(className), ...props, children });
3423
- const AlertDialogDescription = ({ className, children, ...props }) => /* @__PURE__ */ jsxRuntime.jsx("p", { class: cn(className), ...props, children });
3424
- const AlertDialogFooter = ({ className, children, ...props }) => /* @__PURE__ */ jsxRuntime.jsx("footer", { class: cn(className), ...props, children });
3284
+ const Card = ({ className, children, ...props }) => /* @__PURE__ */ jsxRuntime.jsx("div", { class: cn("card", className), ...props, children });
3285
+ const CardHeader = ({ className, children, ...props }) => /* @__PURE__ */ jsxRuntime.jsx("header", { class: cn(className), ...props, children });
3286
+ const CardTitle = ({ className, children, ...props }) => /* @__PURE__ */ jsxRuntime.jsx("h2", { class: cn(className), ...props, children });
3287
+ const CardDescription = ({ className, children, ...props }) => /* @__PURE__ */ jsxRuntime.jsx("p", { class: cn(className), ...props, children });
3288
+ const CardContent = ({ className, children, ...props }) => /* @__PURE__ */ jsxRuntime.jsx("section", { class: cn(className), ...props, children });
3289
+ const CardFooter = ({ className, children, ...props }) => /* @__PURE__ */ jsxRuntime.jsx("footer", { class: cn(className), ...props, children });
3425
3290
 
3426
3291
  const Checkbox = ({
3427
3292
  className,
@@ -3441,286 +3306,482 @@ const Checkbox = ({
3441
3306
  );
3442
3307
  };
3443
3308
 
3444
- const Switch = ({
3445
- className,
3446
- checked,
3447
- onCheckedChange,
3448
- ...props
3449
- }) => {
3450
- return /* @__PURE__ */ jsxRuntime.jsx(
3451
- "input",
3452
- {
3453
- type: "checkbox",
3454
- role: "switch",
3455
- class: cn("input", className),
3456
- checked,
3457
- onChange: (e) => onCheckedChange?.(e.target.checked),
3458
- ...props
3459
- }
3460
- );
3461
- };
3462
-
3463
- const RadioGroup = ({
3464
- className,
3465
- value,
3466
- onValueChange,
3467
- name,
3468
- children,
3469
- ...props
3470
- }) => {
3471
- return /* @__PURE__ */ jsxRuntime.jsx(
3472
- "div",
3473
- {
3474
- role: "radiogroup",
3475
- class: cn(className),
3476
- ...props,
3477
- children
3478
- }
3479
- );
3480
- };
3481
- const RadioGroupItem = ({
3482
- className,
3483
- value,
3484
- ...props
3485
- }) => {
3486
- return /* @__PURE__ */ jsxRuntime.jsx(
3487
- "input",
3488
- {
3489
- type: "radio",
3490
- value,
3491
- class: cn("input", className),
3492
- ...props
3309
+ const initCombobox = (component, onValueChange, disabled = false) => {
3310
+ const trigger = component.querySelector(":scope > button");
3311
+ const triggerLabel = trigger?.querySelector(":scope > span");
3312
+ const popover = component.querySelector(":scope > [data-popover]");
3313
+ const filter = popover?.querySelector('header input[role="combobox"]');
3314
+ const listbox = popover?.querySelector('[role="listbox"]');
3315
+ const allOptions = listbox ? Array.from(listbox.querySelectorAll('[role="option"]')) : [];
3316
+ const options = allOptions.filter((option) => option.getAttribute("aria-disabled") !== "true");
3317
+ const hiddenInput = component.querySelector(':scope > input[type="hidden"]');
3318
+ if (!trigger || !triggerLabel || !popover || !filter || !listbox || !hiddenInput) {
3319
+ return;
3320
+ }
3321
+ if (disabled) {
3322
+ trigger.disabled = true;
3323
+ trigger.setAttribute("aria-disabled", "true");
3324
+ trigger.setAttribute("aria-expanded", "false");
3325
+ filter.disabled = true;
3326
+ filter.setAttribute("aria-expanded", "false");
3327
+ popover.setAttribute("aria-hidden", "true");
3328
+ component.dataset.comboboxInitialized = "true";
3329
+ component.dispatchEvent(new CustomEvent("basecoat:initialized"));
3330
+ return;
3331
+ }
3332
+ if (!trigger.id && component.id) {
3333
+ trigger.id = `${component.id}-trigger`;
3334
+ }
3335
+ if (!popover.id && component.id) {
3336
+ popover.id = `${component.id}-popover`;
3337
+ }
3338
+ if (!listbox.id && component.id) {
3339
+ listbox.id = `${component.id}-listbox`;
3340
+ }
3341
+ trigger.setAttribute("aria-controls", listbox.id);
3342
+ listbox.setAttribute("aria-labelledby", trigger.id);
3343
+ const setVisible = (option, visible) => {
3344
+ option.setAttribute("aria-hidden", String(!visible));
3345
+ };
3346
+ const filteredOptions = () => options.filter((o) => o.getAttribute("aria-hidden") !== "true");
3347
+ const getOptionValue = (option) => option.dataset.value ?? option.textContent?.trim() ?? "";
3348
+ options.forEach((option, index) => {
3349
+ if (!option.id) {
3350
+ option.id = `${component.id}-option-${index}`;
3493
3351
  }
3494
- );
3495
- };
3496
-
3497
- const Slider = ({
3498
- className,
3499
- min = 0,
3500
- max = 100,
3501
- step = 1,
3502
- value,
3503
- onValueChange,
3504
- ...props
3505
- }) => {
3506
- const sliderRef = defuss.createRef();
3507
- const updateSliderValue = () => {
3508
- if (!sliderRef.current) return;
3509
- const val = Number(sliderRef.current.value);
3510
- const percentage = (val - min) / (max - min) * 100;
3511
- sliderRef.current.style.setProperty("--slider-value", `${percentage}%`);
3512
- onValueChange?.(val);
3352
+ });
3353
+ let activeIndex = -1;
3354
+ const setActive = (index) => {
3355
+ const visible = filteredOptions();
3356
+ options.forEach((option) => option.classList.remove("active"));
3357
+ activeIndex = index;
3358
+ if (activeIndex >= 0 && visible[activeIndex]) {
3359
+ visible[activeIndex].classList.add("active");
3360
+ trigger.setAttribute("aria-activedescendant", visible[activeIndex].id);
3361
+ visible[activeIndex].scrollIntoView({ block: "nearest" });
3362
+ return;
3363
+ }
3364
+ trigger.removeAttribute("aria-activedescendant");
3513
3365
  };
3514
- return /* @__PURE__ */ jsxRuntime.jsx(
3515
- "input",
3516
- {
3517
- ref: sliderRef,
3518
- type: "range",
3519
- min,
3520
- max,
3521
- step,
3522
- value,
3523
- class: cn("input", className),
3524
- onInput: updateSliderValue,
3525
- onMount: updateSliderValue,
3526
- ...props
3366
+ const close = (focusTrigger = true) => {
3367
+ popover.setAttribute("aria-hidden", "true");
3368
+ trigger.setAttribute("aria-expanded", "false");
3369
+ filter.setAttribute("aria-expanded", "false");
3370
+ if (focusTrigger) {
3371
+ trigger.focus();
3527
3372
  }
3528
- );
3529
- };
3530
-
3531
- const Table = ({ className, children, ...props }) => {
3532
- return /* @__PURE__ */ jsxRuntime.jsx("div", { class: "table-container", children: /* @__PURE__ */ jsxRuntime.jsx(
3533
- "table",
3534
- {
3535
- class: cn("table", className),
3536
- ...props,
3537
- children
3373
+ filter.value = "";
3374
+ allOptions.forEach((option) => setVisible(option, true));
3375
+ setActive(-1);
3376
+ };
3377
+ const open = () => {
3378
+ document.dispatchEvent(new CustomEvent("basecoat:popover", {
3379
+ detail: { source: component }
3380
+ }));
3381
+ popover.setAttribute("aria-hidden", "false");
3382
+ trigger.setAttribute("aria-expanded", "true");
3383
+ filter.setAttribute("aria-expanded", "true");
3384
+ filter.focus();
3385
+ const selected = options.find((option) => option.getAttribute("aria-selected") === "true");
3386
+ if (selected) {
3387
+ const visible = filteredOptions();
3388
+ setActive(Math.max(visible.indexOf(selected), 0));
3389
+ } else {
3390
+ setActive(0);
3538
3391
  }
3539
- ) });
3540
- };
3541
- const TableHeader = ({ className, children, ...props }) => {
3542
- return /* @__PURE__ */ jsxRuntime.jsx(
3543
- "thead",
3544
- {
3545
- class: cn("table-header", className),
3546
- ...props,
3547
- children
3392
+ };
3393
+ const dispatchChange = (value) => {
3394
+ component.dispatchEvent(new CustomEvent("change", {
3395
+ detail: { value },
3396
+ bubbles: true
3397
+ }));
3398
+ };
3399
+ const selectOption = (option, triggerEvent = true) => {
3400
+ const value = getOptionValue(option);
3401
+ hiddenInput.value = value;
3402
+ triggerLabel.textContent = option.textContent ?? "";
3403
+ allOptions.forEach((candidate) => {
3404
+ if (candidate === option) {
3405
+ candidate.setAttribute("aria-selected", "true");
3406
+ } else {
3407
+ candidate.removeAttribute("aria-selected");
3408
+ }
3409
+ });
3410
+ onValueChange?.(value);
3411
+ if (triggerEvent) {
3412
+ dispatchChange(value);
3548
3413
  }
3549
- );
3550
- };
3551
- const TableBody = ({ className, children, ...props }) => {
3552
- return /* @__PURE__ */ jsxRuntime.jsx(
3553
- "tbody",
3554
- {
3555
- class: cn("table-body", className),
3556
- ...props,
3557
- children
3414
+ close();
3415
+ };
3416
+ trigger.addEventListener("click", () => {
3417
+ const expanded = trigger.getAttribute("aria-expanded") === "true";
3418
+ if (expanded) {
3419
+ close();
3420
+ } else {
3421
+ open();
3558
3422
  }
3559
- );
3560
- };
3561
- const TableFooter = ({ className, children, ...props }) => {
3562
- return /* @__PURE__ */ jsxRuntime.jsx(
3563
- "tfoot",
3564
- {
3565
- class: cn("table-footer", className),
3566
- ...props,
3567
- children
3423
+ });
3424
+ filter.addEventListener("input", () => {
3425
+ const term = filter.value.trim().toLowerCase();
3426
+ allOptions.forEach((option) => {
3427
+ if (option.hasAttribute("data-force")) {
3428
+ setVisible(option, true);
3429
+ return;
3430
+ }
3431
+ const label = (option.dataset.filter || option.textContent || "").toLowerCase();
3432
+ const keywords = (option.dataset.keywords || "").toLowerCase();
3433
+ const visible = label.includes(term) || keywords.includes(term);
3434
+ setVisible(option, visible);
3435
+ });
3436
+ if (filteredOptions().length > 0) {
3437
+ setActive(0);
3438
+ } else {
3439
+ setActive(-1);
3568
3440
  }
3569
- );
3441
+ });
3442
+ const onKeyboard = (event) => {
3443
+ if (!["ArrowDown", "ArrowUp", "Enter", "Escape"].includes(event.key)) return;
3444
+ const isOpen = popover.getAttribute("aria-hidden") !== "true";
3445
+ if (!isOpen && (event.key === "ArrowDown" || event.key === "ArrowUp" || event.key === "Enter")) {
3446
+ event.preventDefault();
3447
+ open();
3448
+ return;
3449
+ }
3450
+ if (!isOpen) return;
3451
+ const visible = filteredOptions();
3452
+ if (event.key === "Escape") {
3453
+ event.preventDefault();
3454
+ close();
3455
+ return;
3456
+ }
3457
+ if (visible.length === 0) return;
3458
+ if (event.key === "ArrowDown") {
3459
+ event.preventDefault();
3460
+ setActive(Math.min(activeIndex + 1, visible.length - 1));
3461
+ }
3462
+ if (event.key === "ArrowUp") {
3463
+ event.preventDefault();
3464
+ setActive(Math.max(activeIndex - 1, 0));
3465
+ }
3466
+ if (event.key === "Enter") {
3467
+ event.preventDefault();
3468
+ const option = visible[Math.max(activeIndex, 0)];
3469
+ if (option) {
3470
+ selectOption(option);
3471
+ }
3472
+ }
3473
+ };
3474
+ trigger.addEventListener("keydown", onKeyboard);
3475
+ filter.addEventListener("keydown", onKeyboard);
3476
+ listbox.addEventListener("click", (event) => {
3477
+ const option = event.target.closest('[role="option"]');
3478
+ if (!option || option.getAttribute("aria-disabled") === "true") return;
3479
+ selectOption(option);
3480
+ });
3481
+ document.addEventListener("click", (event) => {
3482
+ if (!component.contains(event.target)) {
3483
+ close(false);
3484
+ }
3485
+ });
3486
+ document.addEventListener("basecoat:popover", ((event) => {
3487
+ if (event.detail.source !== component) {
3488
+ close(false);
3489
+ }
3490
+ }));
3491
+ const initialOption = options.find((option) => getOptionValue(option) === hiddenInput.value) || options[0];
3492
+ if (initialOption) {
3493
+ const value = getOptionValue(initialOption);
3494
+ hiddenInput.value = value;
3495
+ triggerLabel.textContent = initialOption.textContent ?? "";
3496
+ allOptions.forEach((candidate) => {
3497
+ if (candidate === initialOption) {
3498
+ candidate.setAttribute("aria-selected", "true");
3499
+ } else {
3500
+ candidate.removeAttribute("aria-selected");
3501
+ }
3502
+ });
3503
+ }
3504
+ component.selectByValue = (value) => {
3505
+ const option = options.find((candidate) => getOptionValue(candidate) === value);
3506
+ if (option) {
3507
+ selectOption(option);
3508
+ }
3509
+ };
3510
+ popover.setAttribute("aria-hidden", "true");
3511
+ component.dataset.comboboxInitialized = "true";
3512
+ component.dispatchEvent(new CustomEvent("basecoat:initialized"));
3570
3513
  };
3571
- const TableRow = ({ className, children, ...props }) => {
3572
- return /* @__PURE__ */ jsxRuntime.jsx(
3573
- "tr",
3514
+ const Combobox = ({
3515
+ id,
3516
+ className,
3517
+ placeholder = "Select option",
3518
+ searchPlaceholder = "Search entries...",
3519
+ onValueChange,
3520
+ disabled = false,
3521
+ children,
3522
+ ...props
3523
+ }) => {
3524
+ const rootRef = defuss.createRef();
3525
+ const comboboxId = id || `combobox-${Math.random().toString(36).slice(2, 9)}`;
3526
+ return /* @__PURE__ */ jsxRuntime.jsxs(
3527
+ "div",
3574
3528
  {
3575
- class: cn("table-row", className),
3529
+ ref: rootRef,
3530
+ id: comboboxId,
3531
+ class: cn("select", className),
3532
+ onMount: () => initCombobox(rootRef.current, onValueChange, disabled),
3576
3533
  ...props,
3577
- children
3534
+ children: [
3535
+ /* @__PURE__ */ jsxRuntime.jsxs(
3536
+ "button",
3537
+ {
3538
+ type: "button",
3539
+ id: `${comboboxId}-trigger`,
3540
+ "aria-haspopup": "listbox",
3541
+ "aria-expanded": "false",
3542
+ "aria-controls": `${comboboxId}-listbox`,
3543
+ "aria-disabled": disabled,
3544
+ disabled,
3545
+ class: "btn-outline w-full justify-between",
3546
+ children: [
3547
+ /* @__PURE__ */ jsxRuntime.jsx("span", { class: "truncate", children: placeholder }),
3548
+ /* @__PURE__ */ jsxRuntime.jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round", class: "text-muted-foreground opacity-50 shrink-0", children: [
3549
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "m7 15 5 5 5-5" }),
3550
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "m7 9 5-5 5 5" })
3551
+ ] })
3552
+ ]
3553
+ }
3554
+ ),
3555
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { id: `${comboboxId}-popover`, "data-popover": "", "aria-hidden": "true", children: [
3556
+ /* @__PURE__ */ jsxRuntime.jsxs("header", { children: [
3557
+ /* @__PURE__ */ jsxRuntime.jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round", class: "lucide lucide-search", children: [
3558
+ /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "11", cy: "11", r: "8" }),
3559
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "m21 21-4.3-4.3" })
3560
+ ] }),
3561
+ /* @__PURE__ */ jsxRuntime.jsx(
3562
+ "input",
3563
+ {
3564
+ type: "text",
3565
+ placeholder: searchPlaceholder,
3566
+ disabled,
3567
+ autocomplete: "off",
3568
+ autocorrect: "off",
3569
+ spellcheck: false,
3570
+ "aria-autocomplete": "list",
3571
+ role: "combobox",
3572
+ "aria-expanded": "false",
3573
+ "aria-controls": `${comboboxId}-listbox`,
3574
+ "aria-labelledby": `${comboboxId}-trigger`
3575
+ }
3576
+ )
3577
+ ] }),
3578
+ /* @__PURE__ */ jsxRuntime.jsx(
3579
+ "div",
3580
+ {
3581
+ role: "listbox",
3582
+ id: `${comboboxId}-listbox`,
3583
+ "aria-orientation": "vertical",
3584
+ "aria-labelledby": `${comboboxId}-trigger`,
3585
+ "data-empty": "No results found.",
3586
+ children
3587
+ }
3588
+ )
3589
+ ] }),
3590
+ /* @__PURE__ */ jsxRuntime.jsx("input", { type: "hidden", name: `${comboboxId}-value`, value: "" })
3591
+ ]
3578
3592
  }
3579
3593
  );
3580
3594
  };
3581
- const TableHead = ({ className, children, ...props }) => {
3582
- return /* @__PURE__ */ jsxRuntime.jsx(
3583
- "th",
3584
- {
3585
- class: cn("table-head", className),
3586
- ...props,
3587
- children
3588
- }
3595
+ const ComboboxOption = ({ className, value, children, ...props }) => /* @__PURE__ */ jsxRuntime.jsx("div", { role: "option", "data-value": value, class: cn(className), ...props, children: Array.isArray(children) ? children.length > 0 ? children : value : children ?? value });
3596
+
3597
+ const initCommand = (container) => {
3598
+ const input = container.querySelector("header input");
3599
+ const menu = container.querySelector('[role="menu"]');
3600
+ if (!input || !menu) {
3601
+ const missing = [];
3602
+ if (!input) missing.push("input");
3603
+ if (!menu) missing.push("menu");
3604
+ console.error(`Command component initialization failed. Missing element(s): ${missing.join(", ")}`, container);
3605
+ return;
3606
+ }
3607
+ const allMenuItems = Array.from(menu.querySelectorAll('[role="menuitem"]'));
3608
+ const menuItems = allMenuItems.filter(
3609
+ (item) => !item.hasAttribute("disabled") && item.getAttribute("aria-disabled") !== "true"
3589
3610
  );
3590
- };
3591
- const TableCell = ({ className, children, ...props }) => {
3592
- return /* @__PURE__ */ jsxRuntime.jsx(
3593
- "td",
3594
- {
3595
- class: cn("table-cell", className),
3596
- ...props,
3597
- children
3611
+ let visibleMenuItems = [...menuItems];
3612
+ let activeIndex = -1;
3613
+ const setActiveItem = (index) => {
3614
+ if (activeIndex > -1 && menuItems[activeIndex]) {
3615
+ menuItems[activeIndex].classList.remove("active");
3598
3616
  }
3599
- );
3600
- };
3601
- const TableCaption = ({ className, children, ...props }) => {
3602
- return /* @__PURE__ */ jsxRuntime.jsx(
3603
- "caption",
3604
- {
3605
- class: cn("table-caption", className),
3606
- ...props,
3607
- children
3617
+ activeIndex = index;
3618
+ if (activeIndex > -1) {
3619
+ const activeItem = menuItems[activeIndex];
3620
+ activeItem.classList.add("active");
3621
+ if (activeItem.id) {
3622
+ input.setAttribute("aria-activedescendant", activeItem.id);
3623
+ } else {
3624
+ input.removeAttribute("aria-activedescendant");
3625
+ }
3626
+ } else {
3627
+ input.removeAttribute("aria-activedescendant");
3608
3628
  }
3609
- );
3610
- };
3611
-
3612
- const initTabs = (tabsComponent) => {
3613
- const tablist = tabsComponent.querySelector('[role="tablist"]');
3614
- if (!tablist) return;
3615
- const tabs = Array.from(tablist.querySelectorAll('[role="tab"]'));
3616
- const panels = tabs.map(
3617
- (tab) => document.getElementById(tab.getAttribute("aria-controls") || "")
3618
- ).filter(Boolean);
3619
- const selectTab = (tabToSelect) => {
3620
- tabs.forEach((tab, index) => {
3621
- tab.setAttribute("aria-selected", "false");
3622
- tab.setAttribute("tabindex", "-1");
3623
- if (panels[index]) panels[index].hidden = true;
3629
+ };
3630
+ const filterMenuItems = () => {
3631
+ const searchTerm = input.value.trim().toLowerCase();
3632
+ setActiveItem(-1);
3633
+ visibleMenuItems = [];
3634
+ allMenuItems.forEach((item) => {
3635
+ if (item.hasAttribute("data-force")) {
3636
+ item.setAttribute("aria-hidden", "false");
3637
+ if (menuItems.includes(item)) {
3638
+ visibleMenuItems.push(item);
3639
+ }
3640
+ return;
3641
+ }
3642
+ const itemText = (item.dataset.filter || item.textContent || "").trim().toLowerCase();
3643
+ const keywordList = (item.dataset.keywords || "").toLowerCase().split(/[\s,]+/).filter(Boolean);
3644
+ const matchesKeyword = keywordList.some((keyword) => keyword.includes(searchTerm));
3645
+ const matches = itemText.includes(searchTerm) || matchesKeyword;
3646
+ item.setAttribute("aria-hidden", String(!matches));
3647
+ if (matches && menuItems.includes(item)) {
3648
+ visibleMenuItems.push(item);
3649
+ }
3624
3650
  });
3625
- tabToSelect.setAttribute("aria-selected", "true");
3626
- tabToSelect.setAttribute("tabindex", "0");
3627
- const activePanel = document.getElementById(tabToSelect.getAttribute("aria-controls") || "");
3628
- if (activePanel) activePanel.hidden = false;
3651
+ if (visibleMenuItems.length > 0) {
3652
+ setActiveItem(menuItems.indexOf(visibleMenuItems[0]));
3653
+ visibleMenuItems[0].scrollIntoView({ block: "nearest" });
3654
+ }
3629
3655
  };
3630
- tablist.addEventListener("click", (event) => {
3631
- const clickedTab = event.target.closest('[role="tab"]');
3632
- if (clickedTab) selectTab(clickedTab);
3633
- });
3634
- tablist.addEventListener("keydown", (event) => {
3635
- const currentTab = event.target;
3636
- if (!tabs.includes(currentTab)) return;
3637
- let nextTab;
3638
- const currentIndex = tabs.indexOf(currentTab);
3656
+ input.addEventListener("input", filterMenuItems);
3657
+ const handleKeyNavigation = (event) => {
3658
+ if (!["ArrowDown", "ArrowUp", "Enter", "Home", "End"].includes(event.key)) {
3659
+ return;
3660
+ }
3661
+ if (event.key === "Enter") {
3662
+ event.preventDefault();
3663
+ if (activeIndex > -1) {
3664
+ menuItems[activeIndex]?.click();
3665
+ }
3666
+ return;
3667
+ }
3668
+ if (visibleMenuItems.length === 0) return;
3669
+ event.preventDefault();
3670
+ const currentVisibleIndex = activeIndex > -1 ? visibleMenuItems.indexOf(menuItems[activeIndex]) : -1;
3671
+ let nextVisibleIndex = currentVisibleIndex;
3639
3672
  switch (event.key) {
3640
- case "ArrowRight":
3641
- nextTab = tabs[(currentIndex + 1) % tabs.length];
3673
+ case "ArrowDown":
3674
+ if (currentVisibleIndex < visibleMenuItems.length - 1) {
3675
+ nextVisibleIndex = currentVisibleIndex + 1;
3676
+ }
3642
3677
  break;
3643
- case "ArrowLeft":
3644
- nextTab = tabs[(currentIndex - 1 + tabs.length) % tabs.length];
3678
+ case "ArrowUp":
3679
+ if (currentVisibleIndex > 0) {
3680
+ nextVisibleIndex = currentVisibleIndex - 1;
3681
+ } else if (currentVisibleIndex === -1) {
3682
+ nextVisibleIndex = 0;
3683
+ }
3645
3684
  break;
3646
3685
  case "Home":
3647
- nextTab = tabs[0];
3686
+ nextVisibleIndex = 0;
3648
3687
  break;
3649
3688
  case "End":
3650
- nextTab = tabs[tabs.length - 1];
3689
+ nextVisibleIndex = visibleMenuItems.length - 1;
3651
3690
  break;
3652
- default:
3653
- return;
3654
3691
  }
3655
- event.preventDefault();
3656
- if (nextTab) {
3657
- selectTab(nextTab);
3658
- nextTab.focus();
3692
+ if (nextVisibleIndex !== currentVisibleIndex) {
3693
+ const newActiveItem = visibleMenuItems[nextVisibleIndex];
3694
+ setActiveItem(menuItems.indexOf(newActiveItem));
3695
+ newActiveItem.scrollIntoView({ block: "nearest", behavior: "smooth" });
3659
3696
  }
3660
- });
3661
- tabsComponent.dataset.tabsInitialized = true;
3662
- tabsComponent.dispatchEvent(new CustomEvent("basecoat:initialized"));
3663
- };
3664
- const Tabs = ({ className, defaultValue, children, ...props }) => {
3665
- const tabsRef = defuss.createRef();
3666
- const onMount = () => {
3667
- if (!tabsRef.current) return;
3668
- initTabs(tabsRef.current);
3669
- if (defaultValue) {
3670
- const defaultTab = tabsRef.current.querySelector(`[role="tab"][data-value="${defaultValue}"]`);
3671
- if (defaultTab) {
3672
- defaultTab.click();
3697
+ };
3698
+ menu.addEventListener("mousemove", (event) => {
3699
+ const menuItem = event.target.closest('[role="menuitem"]');
3700
+ if (menuItem && visibleMenuItems.includes(menuItem)) {
3701
+ const index = menuItems.indexOf(menuItem);
3702
+ if (index !== activeIndex) {
3703
+ setActiveItem(index);
3673
3704
  }
3674
- } else {
3675
- const firstTab = tabsRef.current.querySelector('[role="tab"]');
3676
- if (firstTab) {
3677
- firstTab.click();
3705
+ }
3706
+ });
3707
+ menu.addEventListener("click", (event) => {
3708
+ const clickedItem = event.target.closest('[role="menuitem"]');
3709
+ if (clickedItem && visibleMenuItems.includes(clickedItem)) {
3710
+ const dialog = container.closest("dialog.command-dialog");
3711
+ if (dialog && !clickedItem.hasAttribute("data-keep-command-open")) {
3712
+ dialog.close();
3678
3713
  }
3679
3714
  }
3680
- };
3681
- return /* @__PURE__ */ jsxRuntime.jsx("div", { ref: tabsRef, class: cn("tabs", className), onMount, ...props, children });
3715
+ });
3716
+ input.addEventListener("keydown", handleKeyNavigation);
3717
+ if (visibleMenuItems.length > 0) {
3718
+ setActiveItem(menuItems.indexOf(visibleMenuItems[0]));
3719
+ visibleMenuItems[0].scrollIntoView({ block: "nearest" });
3720
+ }
3721
+ container.dataset.commandInitialized = true;
3722
+ container.dispatchEvent(new CustomEvent("basecoat:initialized"));
3682
3723
  };
3683
- const TabsList = ({ className, children, ...props }) => {
3724
+ const Command = ({ className, children, ...props }) => {
3725
+ const commandRef = defuss.createRef();
3684
3726
  return /* @__PURE__ */ jsxRuntime.jsx(
3685
- "nav",
3727
+ "div",
3686
3728
  {
3687
- role: "tablist",
3688
- "aria-orientation": "horizontal",
3689
- class: cn(className),
3729
+ ref: commandRef,
3730
+ class: cn("command", className),
3731
+ onMount: () => initCommand(commandRef.current),
3690
3732
  ...props,
3691
3733
  children
3692
3734
  }
3693
3735
  );
3694
3736
  };
3695
- const TabsTrigger = ({ className, value, children, ...props }) => {
3696
- return /* @__PURE__ */ jsxRuntime.jsx(
3697
- "button",
3698
- {
3699
- role: "tab",
3700
- tabIndex: -1,
3701
- "aria-selected": "false",
3702
- "aria-controls": `panel-${value}`,
3703
- "data-value": value,
3704
- class: cn(className),
3705
- ...props,
3706
- children
3707
- }
3708
- );
3737
+ const CommandInput = ({ className, placeholder, ...props }) => {
3738
+ return /* @__PURE__ */ jsxRuntime.jsxs("header", { children: [
3739
+ /* @__PURE__ */ jsxRuntime.jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round", class: "lucide lucide-search", children: [
3740
+ /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "11", cy: "11", r: "8" }),
3741
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "m21 21-4.3-4.3" })
3742
+ ] }),
3743
+ /* @__PURE__ */ jsxRuntime.jsx(
3744
+ "input",
3745
+ {
3746
+ type: "text",
3747
+ placeholder,
3748
+ role: "combobox",
3749
+ "aria-autocomplete": "list",
3750
+ "aria-expanded": "true",
3751
+ class: cn(className),
3752
+ ...props
3753
+ }
3754
+ )
3755
+ ] });
3709
3756
  };
3710
- const TabsContent = ({ className, value, children, ...props }) => {
3757
+ const CommandList = ({ className, children, ...props }) => {
3758
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { role: "menu", class: cn("scrollbar", className), ...props, children });
3759
+ };
3760
+ const CommandGroup = ({ className, heading, children, ...props }) => {
3761
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { role: "group", class: cn(className), ...props, children: [
3762
+ heading && /* @__PURE__ */ jsxRuntime.jsx("div", { role: "presentation", children: heading }),
3763
+ children
3764
+ ] });
3765
+ };
3766
+ const CommandItem = ({ className, keywords, disabled, children, ...props }) => {
3711
3767
  return /* @__PURE__ */ jsxRuntime.jsx(
3712
3768
  "div",
3713
- {
3714
- role: "tabpanel",
3715
- id: `panel-${value}`,
3716
- tabIndex: -1,
3717
- hidden: true,
3769
+ {
3770
+ role: "menuitem",
3771
+ "data-keywords": keywords,
3772
+ "aria-disabled": disabled,
3718
3773
  class: cn(className),
3719
3774
  ...props,
3720
3775
  children
3721
3776
  }
3722
3777
  );
3723
3778
  };
3779
+ const CommandEmpty = ({ className, children, ...props }) => {
3780
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { class: cn(className), ...props, children });
3781
+ };
3782
+ const CommandSeparator = ({ className, ...props }) => {
3783
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { role: "separator", class: cn(className), ...props });
3784
+ };
3724
3785
 
3725
3786
  const Dialog = ({ className, id, open, children, ...props }) => {
3726
3787
  return /* @__PURE__ */ jsxRuntime.jsx(
@@ -3785,783 +3846,927 @@ const DialogClose = ({ className, children, ...props }) => {
3785
3846
  );
3786
3847
  };
3787
3848
 
3788
- const initPopover = (popoverComponent) => {
3789
- const trigger = popoverComponent.querySelector(":scope > button");
3790
- const content = popoverComponent.querySelector(":scope > [data-popover]");
3791
- if (!trigger || !content) {
3849
+ const initDropdownMenu = (dropdownMenuComponent) => {
3850
+ const trigger = dropdownMenuComponent.querySelector(":scope > button");
3851
+ const popover = dropdownMenuComponent.querySelector(":scope > [data-popover]");
3852
+ const menu = popover?.querySelector('[role="menu"]');
3853
+ if (!trigger || !menu || !popover) {
3792
3854
  const missing = [];
3793
3855
  if (!trigger) missing.push("trigger");
3794
- if (!content) missing.push("content");
3795
- console.error(`Popover initialisation failed. Missing element(s): ${missing.join(", ")}`, popoverComponent);
3856
+ if (!menu) missing.push("menu");
3857
+ if (!popover) missing.push("popover");
3858
+ console.error(`Dropdown menu initialisation failed. Missing element(s): ${missing.join(", ")}`, dropdownMenuComponent);
3796
3859
  return;
3797
3860
  }
3798
- const rootId = popoverComponent.id || `popover-${Math.random().toString(36).slice(2, 9)}`;
3799
- if (!popoverComponent.id) {
3800
- popoverComponent.id = rootId;
3861
+ const rootId = dropdownMenuComponent.id || `dropdown-menu-${Math.random().toString(36).slice(2, 9)}`;
3862
+ if (!dropdownMenuComponent.id) {
3863
+ dropdownMenuComponent.id = rootId;
3801
3864
  }
3802
3865
  if (!trigger.id) {
3803
3866
  trigger.id = `${rootId}-trigger`;
3804
3867
  }
3805
- if (!content.id) {
3806
- content.id = `${rootId}-popover`;
3868
+ if (!popover.id) {
3869
+ popover.id = `${rootId}-popover`;
3807
3870
  }
3808
- trigger.setAttribute("aria-controls", content.id);
3809
- content.setAttribute("aria-labelledby", trigger.id);
3871
+ if (!menu.id) {
3872
+ menu.id = `${rootId}-menu`;
3873
+ }
3874
+ trigger.setAttribute("aria-controls", menu.id);
3875
+ menu.setAttribute("aria-labelledby", trigger.id);
3876
+ let menuItems = [];
3877
+ let activeIndex = -1;
3810
3878
  const closePopover = (focusOnTrigger = true) => {
3811
3879
  if (trigger.getAttribute("aria-expanded") === "false") return;
3812
3880
  trigger.setAttribute("aria-expanded", "false");
3813
- content.setAttribute("aria-hidden", "true");
3881
+ trigger.removeAttribute("aria-activedescendant");
3882
+ popover.setAttribute("aria-hidden", "true");
3814
3883
  if (focusOnTrigger) {
3815
3884
  trigger.focus();
3816
3885
  }
3886
+ setActiveItem(-1);
3817
3887
  };
3818
- const openPopover = () => {
3888
+ const openPopover = (initialSelection = false) => {
3819
3889
  document.dispatchEvent(new CustomEvent("basecoat:popover", {
3820
- detail: { source: popoverComponent }
3890
+ detail: { source: dropdownMenuComponent }
3821
3891
  }));
3822
- const elementToFocus = content.querySelector("[autofocus]");
3823
- if (elementToFocus) {
3824
- content.addEventListener("transitionend", () => {
3825
- elementToFocus.focus();
3826
- }, { once: true });
3827
- }
3828
3892
  trigger.setAttribute("aria-expanded", "true");
3829
- content.setAttribute("aria-hidden", "false");
3830
- };
3831
- trigger.addEventListener("click", () => {
3832
- const isExpanded = trigger.getAttribute("aria-expanded") === "true";
3833
- if (isExpanded) {
3834
- closePopover();
3835
- } else {
3836
- openPopover();
3837
- }
3838
- });
3839
- popoverComponent.addEventListener("keydown", (event) => {
3840
- if (event.key === "Escape") {
3841
- closePopover();
3842
- }
3843
- });
3844
- document.addEventListener("click", (event) => {
3845
- if (!popoverComponent.contains(event.target)) {
3846
- closePopover();
3847
- }
3848
- });
3849
- document.addEventListener("basecoat:popover", ((event) => {
3850
- if (event.detail.source !== popoverComponent) {
3851
- closePopover(false);
3852
- }
3853
- }));
3854
- popoverComponent.dataset.popoverInitialized = true;
3855
- popoverComponent.dispatchEvent(new CustomEvent("basecoat:initialized"));
3856
- };
3857
- const Popover = ({ id, className, children, ...props }) => {
3858
- const popoverRef = defuss.createRef();
3859
- return /* @__PURE__ */ jsxRuntime.jsx(
3860
- "div",
3861
- {
3862
- ref: popoverRef,
3863
- id,
3864
- class: cn("popover", className),
3865
- onMount: () => initPopover(popoverRef.current),
3866
- ...props,
3867
- children
3868
- }
3869
- );
3870
- };
3871
- const PopoverTrigger = ({ id, className, children, ...props }) => {
3872
- return /* @__PURE__ */ jsxRuntime.jsx(
3873
- "button",
3874
- {
3875
- ...props,
3876
- id,
3877
- type: "button",
3878
- "aria-expanded": "false",
3879
- class: cn(className),
3880
- children
3881
- }
3882
- );
3883
- };
3884
- const PopoverContent = ({ id, className, children, ...props }) => {
3885
- return /* @__PURE__ */ jsxRuntime.jsx(
3886
- "div",
3887
- {
3888
- ...props,
3889
- id,
3890
- "data-popover": "",
3891
- "aria-hidden": "true",
3892
- "aria-labelledby": id ? id.replace("-popover", "-trigger") : void 0,
3893
- class: cn(className),
3894
- children
3893
+ popover.setAttribute("aria-hidden", "false");
3894
+ menuItems = Array.from(menu.querySelectorAll('[role^="menuitem"]')).filter(
3895
+ (item) => !item.hasAttribute("disabled") && item.getAttribute("aria-disabled") !== "true"
3896
+ );
3897
+ if (menuItems.length > 0 && initialSelection) {
3898
+ if (initialSelection === "first") {
3899
+ setActiveItem(0);
3900
+ } else if (initialSelection === "last") {
3901
+ setActiveItem(menuItems.length - 1);
3902
+ }
3895
3903
  }
3896
- );
3897
- };
3898
-
3899
- const initCombobox = (component, onValueChange) => {
3900
- const trigger = component.querySelector(":scope > button");
3901
- const triggerLabel = trigger?.querySelector(":scope > span");
3902
- const popover = component.querySelector(":scope > [data-popover]");
3903
- const filter = popover?.querySelector('header input[role="combobox"]');
3904
- const listbox = popover?.querySelector('[role="listbox"]');
3905
- const allOptions = listbox ? Array.from(listbox.querySelectorAll('[role="option"]')) : [];
3906
- const options = allOptions.filter((option) => option.getAttribute("aria-disabled") !== "true");
3907
- const hiddenInput = component.querySelector(':scope > input[type="hidden"]');
3908
- if (!trigger || !triggerLabel || !popover || !filter || !listbox || !hiddenInput) {
3909
- return;
3910
- }
3911
- if (!trigger.id && component.id) {
3912
- trigger.id = `${component.id}-trigger`;
3913
- }
3914
- if (!popover.id && component.id) {
3915
- popover.id = `${component.id}-popover`;
3916
- }
3917
- if (!listbox.id && component.id) {
3918
- listbox.id = `${component.id}-listbox`;
3919
- }
3920
- trigger.setAttribute("aria-controls", listbox.id);
3921
- listbox.setAttribute("aria-labelledby", trigger.id);
3922
- const setVisible = (option, visible) => {
3923
- option.setAttribute("aria-hidden", String(!visible));
3924
3904
  };
3925
- const filteredOptions = () => options.filter((o) => o.getAttribute("aria-hidden") !== "true");
3926
- const getOptionValue = (option) => option.dataset.value ?? option.textContent?.trim() ?? "";
3927
- options.forEach((option, index) => {
3928
- if (!option.id) {
3929
- option.id = `${component.id}-option-${index}`;
3905
+ const setActiveItem = (index) => {
3906
+ if (activeIndex > -1 && menuItems[activeIndex]) {
3907
+ menuItems[activeIndex].classList.remove("active");
3930
3908
  }
3931
- });
3932
- let activeIndex = -1;
3933
- const setActive = (index) => {
3934
- const visible = filteredOptions();
3935
- options.forEach((option) => option.classList.remove("active"));
3936
3909
  activeIndex = index;
3937
- if (activeIndex >= 0 && visible[activeIndex]) {
3938
- visible[activeIndex].classList.add("active");
3939
- trigger.setAttribute("aria-activedescendant", visible[activeIndex].id);
3940
- visible[activeIndex].scrollIntoView({ block: "nearest" });
3941
- return;
3942
- }
3943
- trigger.removeAttribute("aria-activedescendant");
3944
- };
3945
- const close = (focusTrigger = true) => {
3946
- popover.setAttribute("aria-hidden", "true");
3947
- trigger.setAttribute("aria-expanded", "false");
3948
- filter.setAttribute("aria-expanded", "false");
3949
- if (focusTrigger) {
3950
- trigger.focus();
3951
- }
3952
- filter.value = "";
3953
- allOptions.forEach((option) => setVisible(option, true));
3954
- setActive(-1);
3955
- };
3956
- const open = () => {
3957
- document.dispatchEvent(new CustomEvent("basecoat:popover", {
3958
- detail: { source: component }
3959
- }));
3960
- popover.setAttribute("aria-hidden", "false");
3961
- trigger.setAttribute("aria-expanded", "true");
3962
- filter.setAttribute("aria-expanded", "true");
3963
- filter.focus();
3964
- const selected = options.find((option) => option.getAttribute("aria-selected") === "true");
3965
- if (selected) {
3966
- const visible = filteredOptions();
3967
- setActive(Math.max(visible.indexOf(selected), 0));
3910
+ if (activeIndex > -1 && menuItems[activeIndex]) {
3911
+ const activeItem = menuItems[activeIndex];
3912
+ activeItem.classList.add("active");
3913
+ trigger.setAttribute("aria-activedescendant", activeItem.id);
3968
3914
  } else {
3969
- setActive(0);
3970
- }
3971
- };
3972
- const dispatchChange = (value) => {
3973
- component.dispatchEvent(new CustomEvent("change", {
3974
- detail: { value },
3975
- bubbles: true
3976
- }));
3977
- };
3978
- const selectOption = (option, triggerEvent = true) => {
3979
- const value = getOptionValue(option);
3980
- hiddenInput.value = value;
3981
- triggerLabel.textContent = option.textContent ?? "";
3982
- allOptions.forEach((candidate) => {
3983
- if (candidate === option) {
3984
- candidate.setAttribute("aria-selected", "true");
3985
- } else {
3986
- candidate.removeAttribute("aria-selected");
3987
- }
3988
- });
3989
- onValueChange?.(value);
3990
- if (triggerEvent) {
3991
- dispatchChange(value);
3915
+ trigger.removeAttribute("aria-activedescendant");
3992
3916
  }
3993
- close();
3994
3917
  };
3995
3918
  trigger.addEventListener("click", () => {
3996
- const expanded = trigger.getAttribute("aria-expanded") === "true";
3997
- if (expanded) {
3998
- close();
3999
- } else {
4000
- open();
4001
- }
4002
- });
4003
- filter.addEventListener("input", () => {
4004
- const term = filter.value.trim().toLowerCase();
4005
- allOptions.forEach((option) => {
4006
- if (option.hasAttribute("data-force")) {
4007
- setVisible(option, true);
4008
- return;
4009
- }
4010
- const label = (option.dataset.filter || option.textContent || "").toLowerCase();
4011
- const keywords = (option.dataset.keywords || "").toLowerCase();
4012
- const visible = label.includes(term) || keywords.includes(term);
4013
- setVisible(option, visible);
4014
- });
4015
- if (filteredOptions().length > 0) {
4016
- setActive(0);
3919
+ const isExpanded = trigger.getAttribute("aria-expanded") === "true";
3920
+ if (isExpanded) {
3921
+ closePopover();
4017
3922
  } else {
4018
- setActive(-1);
3923
+ openPopover(false);
4019
3924
  }
4020
3925
  });
4021
- const onKeyboard = (event) => {
4022
- if (!["ArrowDown", "ArrowUp", "Enter", "Escape"].includes(event.key)) return;
4023
- const isOpen = popover.getAttribute("aria-hidden") !== "true";
4024
- if (!isOpen && (event.key === "ArrowDown" || event.key === "ArrowUp" || event.key === "Enter")) {
4025
- event.preventDefault();
4026
- open();
3926
+ dropdownMenuComponent.addEventListener("keydown", (event) => {
3927
+ const isExpanded = trigger.getAttribute("aria-expanded") === "true";
3928
+ if (event.key === "Escape") {
3929
+ if (isExpanded) closePopover();
4027
3930
  return;
4028
3931
  }
4029
- if (!isOpen) return;
4030
- const visible = filteredOptions();
4031
- if (event.key === "Escape") {
4032
- event.preventDefault();
4033
- close();
3932
+ if (!isExpanded) {
3933
+ if (["Enter", " "].includes(event.key)) {
3934
+ event.preventDefault();
3935
+ openPopover(false);
3936
+ } else if (event.key === "ArrowDown") {
3937
+ event.preventDefault();
3938
+ openPopover("first");
3939
+ } else if (event.key === "ArrowUp") {
3940
+ event.preventDefault();
3941
+ openPopover("last");
3942
+ }
4034
3943
  return;
4035
3944
  }
4036
- if (visible.length === 0) return;
4037
- if (event.key === "ArrowDown") {
4038
- event.preventDefault();
4039
- setActive(Math.min(activeIndex + 1, visible.length - 1));
3945
+ if (menuItems.length === 0) return;
3946
+ let nextIndex = activeIndex;
3947
+ switch (event.key) {
3948
+ case "ArrowDown":
3949
+ event.preventDefault();
3950
+ nextIndex = activeIndex === -1 ? 0 : Math.min(activeIndex + 1, menuItems.length - 1);
3951
+ break;
3952
+ case "ArrowUp":
3953
+ event.preventDefault();
3954
+ nextIndex = activeIndex === -1 ? menuItems.length - 1 : Math.max(activeIndex - 1, 0);
3955
+ break;
3956
+ case "Home":
3957
+ event.preventDefault();
3958
+ nextIndex = 0;
3959
+ break;
3960
+ case "End":
3961
+ event.preventDefault();
3962
+ nextIndex = menuItems.length - 1;
3963
+ break;
3964
+ case "Enter":
3965
+ case " ":
3966
+ event.preventDefault();
3967
+ menuItems[activeIndex]?.click();
3968
+ closePopover();
3969
+ return;
4040
3970
  }
4041
- if (event.key === "ArrowUp") {
4042
- event.preventDefault();
4043
- setActive(Math.max(activeIndex - 1, 0));
3971
+ if (nextIndex !== activeIndex) {
3972
+ setActiveItem(nextIndex);
4044
3973
  }
4045
- if (event.key === "Enter") {
4046
- event.preventDefault();
4047
- const option = visible[Math.max(activeIndex, 0)];
4048
- if (option) {
4049
- selectOption(option);
3974
+ });
3975
+ menu.addEventListener("mousemove", (event) => {
3976
+ const menuItem = event.target.closest('[role^="menuitem"]');
3977
+ if (menuItem && menuItems.includes(menuItem)) {
3978
+ const index = menuItems.indexOf(menuItem);
3979
+ if (index !== activeIndex) {
3980
+ setActiveItem(index);
4050
3981
  }
4051
3982
  }
4052
- };
4053
- trigger.addEventListener("keydown", onKeyboard);
4054
- filter.addEventListener("keydown", onKeyboard);
4055
- listbox.addEventListener("click", (event) => {
4056
- const option = event.target.closest('[role="option"]');
4057
- if (!option || option.getAttribute("aria-disabled") === "true") return;
4058
- selectOption(option);
3983
+ });
3984
+ menu.addEventListener("mouseleave", () => {
3985
+ setActiveItem(-1);
3986
+ });
3987
+ menu.addEventListener("click", (event) => {
3988
+ const menuItem = event.target.closest('[role^="menuitem"]');
3989
+ if (!menuItem) return;
3990
+ const isDisabled = menuItem.hasAttribute("disabled") || menuItem.getAttribute("aria-disabled") === "true";
3991
+ if (isDisabled) {
3992
+ event.preventDefault();
3993
+ event.stopPropagation();
3994
+ return;
3995
+ }
3996
+ closePopover();
4059
3997
  });
4060
3998
  document.addEventListener("click", (event) => {
4061
- if (!component.contains(event.target)) {
4062
- close(false);
3999
+ if (!dropdownMenuComponent.contains(event.target)) {
4000
+ closePopover();
4063
4001
  }
4064
4002
  });
4065
4003
  document.addEventListener("basecoat:popover", ((event) => {
4066
- if (event.detail.source !== component) {
4067
- close(false);
4004
+ if (event.detail.source !== dropdownMenuComponent) {
4005
+ closePopover(false);
4068
4006
  }
4069
4007
  }));
4070
- const initialOption = options.find((option) => getOptionValue(option) === hiddenInput.value) || options[0];
4071
- if (initialOption) {
4072
- const value = getOptionValue(initialOption);
4073
- hiddenInput.value = value;
4074
- triggerLabel.textContent = initialOption.textContent ?? "";
4075
- allOptions.forEach((candidate) => {
4076
- if (candidate === initialOption) {
4077
- candidate.setAttribute("aria-selected", "true");
4078
- } else {
4079
- candidate.removeAttribute("aria-selected");
4080
- }
4081
- });
4082
- }
4083
- component.selectByValue = (value) => {
4084
- const option = options.find((candidate) => getOptionValue(candidate) === value);
4085
- if (option) {
4086
- selectOption(option);
4008
+ dropdownMenuComponent.dataset.dropdownMenuInitialized = true;
4009
+ dropdownMenuComponent.dispatchEvent(new CustomEvent("basecoat:initialized"));
4010
+ };
4011
+ const DropdownMenu = ({ className, children, ...props }) => {
4012
+ const dropdownRef = defuss.createRef();
4013
+ return /* @__PURE__ */ jsxRuntime.jsx(
4014
+ "div",
4015
+ {
4016
+ ref: dropdownRef,
4017
+ class: cn("dropdown-menu", className),
4018
+ onMount: () => initDropdownMenu(dropdownRef.current),
4019
+ ...props,
4020
+ children
4087
4021
  }
4088
- };
4089
- popover.setAttribute("aria-hidden", "true");
4090
- component.dataset.comboboxInitialized = "true";
4091
- component.dispatchEvent(new CustomEvent("basecoat:initialized"));
4022
+ );
4092
4023
  };
4093
- const Combobox = ({
4094
- id,
4095
- className,
4096
- placeholder = "Select option",
4097
- searchPlaceholder = "Search entries...",
4098
- onValueChange,
4099
- children,
4100
- ...props
4101
- }) => {
4102
- const rootRef = defuss.createRef();
4103
- const comboboxId = id || `combobox-${Math.random().toString(36).slice(2, 9)}`;
4104
- return /* @__PURE__ */ jsxRuntime.jsxs(
4024
+ const DropdownMenuTrigger = ({ className, children, ...props }) => {
4025
+ return /* @__PURE__ */ jsxRuntime.jsx(
4026
+ "button",
4027
+ {
4028
+ ...props,
4029
+ type: "button",
4030
+ "aria-expanded": "false",
4031
+ "aria-haspopup": "menu",
4032
+ class: cn(className),
4033
+ children
4034
+ }
4035
+ );
4036
+ };
4037
+ const DropdownMenuContent = ({ id, className, children, ...props }) => {
4038
+ const menuId = id ? id.replace("-popover", "-menu") : void 0;
4039
+ const triggerId = id ? id.replace("-popover", "-trigger") : void 0;
4040
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { ...props, id, "data-popover": "", "aria-hidden": "true", class: cn(className), children: /* @__PURE__ */ jsxRuntime.jsx("div", { role: "menu", id: menuId, "aria-labelledby": triggerId, "aria-orientation": "vertical", children }) });
4041
+ };
4042
+ const DropdownMenuItem = ({ className, disabled, children, role = "menuitem", ...props }) => {
4043
+ return /* @__PURE__ */ jsxRuntime.jsx(
4105
4044
  "div",
4106
4045
  {
4107
- ref: rootRef,
4108
- id: comboboxId,
4109
- class: cn("select", className),
4110
- onMount: () => initCombobox(rootRef.current, onValueChange),
4111
4046
  ...props,
4112
- children: [
4113
- /* @__PURE__ */ jsxRuntime.jsxs(
4114
- "button",
4115
- {
4116
- type: "button",
4117
- id: `${comboboxId}-trigger`,
4118
- "aria-haspopup": "listbox",
4119
- "aria-expanded": "false",
4120
- "aria-controls": `${comboboxId}-listbox`,
4121
- class: "btn-outline w-full justify-between",
4122
- children: [
4123
- /* @__PURE__ */ jsxRuntime.jsx("span", { class: "truncate", children: placeholder }),
4124
- /* @__PURE__ */ jsxRuntime.jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round", class: "text-muted-foreground opacity-50 shrink-0", children: [
4125
- /* @__PURE__ */ jsxRuntime.jsx("path", { d: "m7 15 5 5 5-5" }),
4126
- /* @__PURE__ */ jsxRuntime.jsx("path", { d: "m7 9 5-5 5 5" })
4127
- ] })
4128
- ]
4129
- }
4130
- ),
4131
- /* @__PURE__ */ jsxRuntime.jsxs("div", { id: `${comboboxId}-popover`, "data-popover": "", "aria-hidden": "true", children: [
4132
- /* @__PURE__ */ jsxRuntime.jsxs("header", { children: [
4133
- /* @__PURE__ */ jsxRuntime.jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round", class: "lucide lucide-search", children: [
4134
- /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "11", cy: "11", r: "8" }),
4135
- /* @__PURE__ */ jsxRuntime.jsx("path", { d: "m21 21-4.3-4.3" })
4136
- ] }),
4137
- /* @__PURE__ */ jsxRuntime.jsx(
4138
- "input",
4139
- {
4140
- type: "text",
4141
- placeholder: searchPlaceholder,
4142
- autocomplete: "off",
4143
- autocorrect: "off",
4144
- spellcheck: false,
4145
- "aria-autocomplete": "list",
4146
- role: "combobox",
4147
- "aria-expanded": "false",
4148
- "aria-controls": `${comboboxId}-listbox`,
4149
- "aria-labelledby": `${comboboxId}-trigger`
4150
- }
4151
- )
4152
- ] }),
4153
- /* @__PURE__ */ jsxRuntime.jsx(
4154
- "div",
4155
- {
4156
- role: "listbox",
4157
- id: `${comboboxId}-listbox`,
4158
- "aria-orientation": "vertical",
4159
- "aria-labelledby": `${comboboxId}-trigger`,
4160
- "data-empty": "No results found.",
4161
- children
4162
- }
4163
- )
4164
- ] }),
4165
- /* @__PURE__ */ jsxRuntime.jsx("input", { type: "hidden", name: `${comboboxId}-value`, value: "" })
4166
- ]
4047
+ role,
4048
+ "aria-disabled": disabled,
4049
+ "data-disabled": disabled ? "true" : void 0,
4050
+ tabIndex: disabled ? -1 : 0,
4051
+ class: cn(className),
4052
+ children
4053
+ }
4054
+ );
4055
+ };
4056
+ const DropdownMenuSeparator = ({ className, ...props }) => {
4057
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { role: "separator", class: cn(className), ...props });
4058
+ };
4059
+ const DropdownMenuLabel = ({ className, children, ...props }) => {
4060
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { class: cn(className), ...props, children });
4061
+ };
4062
+
4063
+ const Form = ({ className, children, ...props }) => /* @__PURE__ */ jsxRuntime.jsx("form", { class: cn("form", className), ...props, children });
4064
+ const FormField = ({ className, children, ...props }) => /* @__PURE__ */ jsxRuntime.jsx("div", { class: cn("field", className), ...props, children });
4065
+
4066
+ const Input = ({ className, type, ...props }) => {
4067
+ return /* @__PURE__ */ jsxRuntime.jsx(
4068
+ "input",
4069
+ {
4070
+ type,
4071
+ class: cn(
4072
+ "input",
4073
+ className
4074
+ ),
4075
+ ...props
4076
+ }
4077
+ );
4078
+ };
4079
+
4080
+ const itemVariants = cva(
4081
+ "group/item flex items-center border text-sm rounded-md transition-colors [a]:hover:bg-accent/50 [a]:transition-colors duration-100 flex-wrap outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] gap-4",
4082
+ {
4083
+ variants: {
4084
+ variant: {
4085
+ default: "border-transparent",
4086
+ outline: "border-border",
4087
+ muted: "border-transparent bg-muted/50"
4088
+ },
4089
+ size: {
4090
+ default: "p-4",
4091
+ compact: "py-3 px-4 gap-2.5"
4092
+ }
4093
+ },
4094
+ defaultVariants: {
4095
+ variant: "outline",
4096
+ size: "default"
4097
+ }
4098
+ }
4099
+ );
4100
+ const Item = ({ className, variant, size, children, ...props }) => /* @__PURE__ */ jsxRuntime.jsx("article", { class: cn(itemVariants({ variant, size }), className), ...props, children });
4101
+ const ItemLeading = ({ className, children, ...props }) => /* @__PURE__ */ jsxRuntime.jsx("div", { class: cn("flex shrink-0 items-center justify-center gap-2", className), ...props, children });
4102
+ const ItemContent = ({ className, children, ...props }) => /* @__PURE__ */ jsxRuntime.jsx("div", { class: cn("flex flex-1 flex-col gap-1", className), ...props, children });
4103
+ const ItemTitle = ({ className, children, ...props }) => /* @__PURE__ */ jsxRuntime.jsx("h3", { class: cn("flex w-fit items-center gap-2 text-sm leading-snug font-medium", className), ...props, children });
4104
+ const ItemDescription = ({ className, children, ...props }) => /* @__PURE__ */ jsxRuntime.jsx("p", { class: cn("text-muted-foreground line-clamp-2 text-sm leading-normal font-normal text-balance [&>a:hover]:text-primary [&>a]:underline [&>a]:underline-offset-4", className), ...props, children });
4105
+ const ItemTrailing = ({ className, children, ...props }) => /* @__PURE__ */ jsxRuntime.jsx("div", { class: cn("flex items-center gap-2", className), ...props, children });
4106
+
4107
+ const Kbd = ({ className, children, ...props }) => {
4108
+ return /* @__PURE__ */ jsxRuntime.jsx(
4109
+ "kbd",
4110
+ {
4111
+ class: cn(
4112
+ "kbd pointer-events-none inline-flex items-center justify-center select-none",
4113
+ className
4114
+ ),
4115
+ ...props,
4116
+ children
4117
+ }
4118
+ );
4119
+ };
4120
+ const KbdGroup = ({ className, children, ...props }) => {
4121
+ return /* @__PURE__ */ jsxRuntime.jsx(
4122
+ "div",
4123
+ {
4124
+ class: cn("kbd-group inline-flex items-center gap-1", className),
4125
+ ...props,
4126
+ children
4167
4127
  }
4168
4128
  );
4169
4129
  };
4170
- const ComboboxOption = ({ className, value, children, ...props }) => /* @__PURE__ */ jsxRuntime.jsx("div", { role: "option", "data-value": value, class: cn(className), ...props, children: Array.isArray(children) ? children.length > 0 ? children : value : children ?? value });
4171
4130
 
4172
- const initDropdownMenu = (dropdownMenuComponent) => {
4173
- const trigger = dropdownMenuComponent.querySelector(":scope > button");
4174
- const popover = dropdownMenuComponent.querySelector(":scope > [data-popover]");
4175
- const menu = popover?.querySelector('[role="menu"]');
4176
- if (!trigger || !menu || !popover) {
4131
+ const Label = ({ className, children, ...props }) => {
4132
+ return /* @__PURE__ */ jsxRuntime.jsx("label", { class: cn("label", className), ...props, children });
4133
+ };
4134
+
4135
+ const initPopover = (popoverComponent) => {
4136
+ const trigger = popoverComponent.querySelector(":scope > button");
4137
+ const content = popoverComponent.querySelector(":scope > [data-popover]");
4138
+ if (!trigger || !content) {
4177
4139
  const missing = [];
4178
4140
  if (!trigger) missing.push("trigger");
4179
- if (!menu) missing.push("menu");
4180
- if (!popover) missing.push("popover");
4181
- console.error(`Dropdown menu initialisation failed. Missing element(s): ${missing.join(", ")}`, dropdownMenuComponent);
4141
+ if (!content) missing.push("content");
4142
+ console.error(`Popover initialisation failed. Missing element(s): ${missing.join(", ")}`, popoverComponent);
4182
4143
  return;
4183
4144
  }
4184
- const rootId = dropdownMenuComponent.id || `dropdown-menu-${Math.random().toString(36).slice(2, 9)}`;
4185
- if (!dropdownMenuComponent.id) {
4186
- dropdownMenuComponent.id = rootId;
4145
+ const rootId = popoverComponent.id || `popover-${Math.random().toString(36).slice(2, 9)}`;
4146
+ if (!popoverComponent.id) {
4147
+ popoverComponent.id = rootId;
4187
4148
  }
4188
4149
  if (!trigger.id) {
4189
4150
  trigger.id = `${rootId}-trigger`;
4190
4151
  }
4191
- if (!popover.id) {
4192
- popover.id = `${rootId}-popover`;
4193
- }
4194
- if (!menu.id) {
4195
- menu.id = `${rootId}-menu`;
4152
+ if (!content.id) {
4153
+ content.id = `${rootId}-popover`;
4196
4154
  }
4197
- trigger.setAttribute("aria-controls", menu.id);
4198
- menu.setAttribute("aria-labelledby", trigger.id);
4199
- let menuItems = [];
4200
- let activeIndex = -1;
4155
+ trigger.setAttribute("aria-controls", content.id);
4156
+ content.setAttribute("aria-labelledby", trigger.id);
4201
4157
  const closePopover = (focusOnTrigger = true) => {
4202
4158
  if (trigger.getAttribute("aria-expanded") === "false") return;
4203
4159
  trigger.setAttribute("aria-expanded", "false");
4204
- trigger.removeAttribute("aria-activedescendant");
4205
- popover.setAttribute("aria-hidden", "true");
4160
+ content.setAttribute("aria-hidden", "true");
4206
4161
  if (focusOnTrigger) {
4207
4162
  trigger.focus();
4208
4163
  }
4209
- setActiveItem(-1);
4210
4164
  };
4211
- const openPopover = (initialSelection = false) => {
4165
+ const openPopover = () => {
4212
4166
  document.dispatchEvent(new CustomEvent("basecoat:popover", {
4213
- detail: { source: dropdownMenuComponent }
4167
+ detail: { source: popoverComponent }
4214
4168
  }));
4169
+ const elementToFocus = content.querySelector("[autofocus]");
4170
+ if (elementToFocus) {
4171
+ content.addEventListener("transitionend", () => {
4172
+ elementToFocus.focus();
4173
+ }, { once: true });
4174
+ }
4215
4175
  trigger.setAttribute("aria-expanded", "true");
4216
- popover.setAttribute("aria-hidden", "false");
4217
- menuItems = Array.from(menu.querySelectorAll('[role^="menuitem"]')).filter(
4218
- (item) => !item.hasAttribute("disabled") && item.getAttribute("aria-disabled") !== "true"
4219
- );
4220
- if (menuItems.length > 0 && initialSelection) {
4221
- if (initialSelection === "first") {
4222
- setActiveItem(0);
4223
- } else if (initialSelection === "last") {
4224
- setActiveItem(menuItems.length - 1);
4176
+ content.setAttribute("aria-hidden", "false");
4177
+ };
4178
+ trigger.addEventListener("click", () => {
4179
+ const isExpanded = trigger.getAttribute("aria-expanded") === "true";
4180
+ if (isExpanded) {
4181
+ closePopover();
4182
+ } else {
4183
+ openPopover();
4184
+ }
4185
+ });
4186
+ popoverComponent.addEventListener("keydown", (event) => {
4187
+ if (event.key === "Escape") {
4188
+ closePopover();
4189
+ }
4190
+ });
4191
+ document.addEventListener("click", (event) => {
4192
+ if (!popoverComponent.contains(event.target)) {
4193
+ closePopover();
4194
+ }
4195
+ });
4196
+ document.addEventListener("basecoat:popover", ((event) => {
4197
+ if (event.detail.source !== popoverComponent) {
4198
+ closePopover(false);
4199
+ }
4200
+ }));
4201
+ popoverComponent.dataset.popoverInitialized = true;
4202
+ popoverComponent.dispatchEvent(new CustomEvent("basecoat:initialized"));
4203
+ };
4204
+ const Popover = ({ id, className, children, ...props }) => {
4205
+ const popoverRef = defuss.createRef();
4206
+ return /* @__PURE__ */ jsxRuntime.jsx(
4207
+ "div",
4208
+ {
4209
+ ref: popoverRef,
4210
+ id,
4211
+ class: cn("popover", className),
4212
+ onMount: () => initPopover(popoverRef.current),
4213
+ ...props,
4214
+ children
4215
+ }
4216
+ );
4217
+ };
4218
+ const PopoverTrigger = ({ id, className, children, ...props }) => {
4219
+ return /* @__PURE__ */ jsxRuntime.jsx(
4220
+ "button",
4221
+ {
4222
+ ...props,
4223
+ id,
4224
+ type: "button",
4225
+ "aria-expanded": "false",
4226
+ class: cn(className),
4227
+ children
4228
+ }
4229
+ );
4230
+ };
4231
+ const PopoverContent = ({ id, className, children, ...props }) => {
4232
+ return /* @__PURE__ */ jsxRuntime.jsx(
4233
+ "div",
4234
+ {
4235
+ ...props,
4236
+ id,
4237
+ "data-popover": "",
4238
+ "aria-hidden": "true",
4239
+ "aria-labelledby": id ? id.replace("-popover", "-trigger") : void 0,
4240
+ class: cn(className),
4241
+ children
4242
+ }
4243
+ );
4244
+ };
4245
+
4246
+ const Progress = ({ className, value = 0, max = 100, ...props }) => {
4247
+ const percentage = Math.max(0, Math.min(100, value / max * 100));
4248
+ return /* @__PURE__ */ jsxRuntime.jsx(
4249
+ "div",
4250
+ {
4251
+ class: cn("bg-primary/20 relative h-2 w-full overflow-hidden rounded-full", className),
4252
+ role: "progressbar",
4253
+ "aria-valuemin": 0,
4254
+ "aria-valuemax": max,
4255
+ "aria-valuenow": value,
4256
+ ...props,
4257
+ children: /* @__PURE__ */ jsxRuntime.jsx("div", { class: "bg-primary h-full w-full flex-1 transition-all", style: `width: ${percentage}%` })
4258
+ }
4259
+ );
4260
+ };
4261
+
4262
+ const RadioGroup = ({
4263
+ className,
4264
+ value,
4265
+ onValueChange,
4266
+ name,
4267
+ children,
4268
+ ...props
4269
+ }) => {
4270
+ return /* @__PURE__ */ jsxRuntime.jsx(
4271
+ "div",
4272
+ {
4273
+ role: "radiogroup",
4274
+ class: cn(className),
4275
+ ...props,
4276
+ children
4277
+ }
4278
+ );
4279
+ };
4280
+ const RadioGroupItem = ({
4281
+ className,
4282
+ value,
4283
+ ...props
4284
+ }) => {
4285
+ return /* @__PURE__ */ jsxRuntime.jsx(
4286
+ "input",
4287
+ {
4288
+ type: "radio",
4289
+ value,
4290
+ class: cn("input", className),
4291
+ ...props
4292
+ }
4293
+ );
4294
+ };
4295
+
4296
+ const Select = ({ className, children, ...props }) => {
4297
+ return /* @__PURE__ */ jsxRuntime.jsx(
4298
+ "select",
4299
+ {
4300
+ class: cn("select", className),
4301
+ ...props,
4302
+ children
4303
+ }
4304
+ );
4305
+ };
4306
+
4307
+ const Separator = ({
4308
+ className,
4309
+ orientation = "horizontal",
4310
+ ...props
4311
+ }) => {
4312
+ return /* @__PURE__ */ jsxRuntime.jsx(
4313
+ "hr",
4314
+ {
4315
+ role: "separator",
4316
+ "aria-orientation": orientation,
4317
+ class: cn(className),
4318
+ ...props
4319
+ }
4320
+ );
4321
+ };
4322
+
4323
+ if (typeof window !== "undefined" && !window.history.__basecoatPatched) {
4324
+ const originalPushState = window.history.pushState;
4325
+ window.history.pushState = function(...args) {
4326
+ originalPushState.apply(this, args);
4327
+ window.dispatchEvent(new Event("basecoat:locationchange"));
4328
+ };
4329
+ const originalReplaceState = window.history.replaceState;
4330
+ window.history.replaceState = function(...args) {
4331
+ originalReplaceState.apply(this, args);
4332
+ window.dispatchEvent(new Event("basecoat:locationchange"));
4333
+ };
4334
+ window.history.__basecoatPatched = true;
4335
+ }
4336
+ const initSidebar = (sidebarComponent) => {
4337
+ const initialOpen = sidebarComponent.dataset.initialOpen !== "false";
4338
+ const initialMobileOpen = sidebarComponent.dataset.initialMobileOpen === "true";
4339
+ const breakpoint = parseInt(sidebarComponent.dataset.breakpoint) || 768;
4340
+ let open = breakpoint > 0 ? window.innerWidth >= breakpoint ? initialOpen : initialMobileOpen : initialOpen;
4341
+ const updateCurrentPageLinks = () => {
4342
+ const currentPath = window.location.pathname.replace(/\/$/, "");
4343
+ sidebarComponent.querySelectorAll("a").forEach((link) => {
4344
+ if (link.hasAttribute("data-ignore-current")) return;
4345
+ if (!link.href) return;
4346
+ const rawHref = link.getAttribute("href");
4347
+ if (!rawHref) return;
4348
+ if (rawHref.startsWith("#")) {
4349
+ link.removeAttribute("aria-current");
4350
+ return;
4225
4351
  }
4226
- }
4352
+ try {
4353
+ const linkPath = new URL(link.href).pathname.replace(/\/$/, "");
4354
+ if (linkPath === currentPath) {
4355
+ link.setAttribute("aria-current", "page");
4356
+ } else {
4357
+ link.removeAttribute("aria-current");
4358
+ }
4359
+ } catch (e) {
4360
+ }
4361
+ });
4227
4362
  };
4228
- const setActiveItem = (index) => {
4229
- if (activeIndex > -1 && menuItems[activeIndex]) {
4230
- menuItems[activeIndex].classList.remove("active");
4231
- }
4232
- activeIndex = index;
4233
- if (activeIndex > -1 && menuItems[activeIndex]) {
4234
- const activeItem = menuItems[activeIndex];
4235
- activeItem.classList.add("active");
4236
- trigger.setAttribute("aria-activedescendant", activeItem.id);
4363
+ const updateState = () => {
4364
+ sidebarComponent.setAttribute("aria-hidden", String(!open));
4365
+ if (open) {
4366
+ sidebarComponent.removeAttribute("inert");
4237
4367
  } else {
4238
- trigger.removeAttribute("aria-activedescendant");
4368
+ sidebarComponent.setAttribute("inert", "");
4239
4369
  }
4240
4370
  };
4241
- trigger.addEventListener("click", () => {
4242
- const isExpanded = trigger.getAttribute("aria-expanded") === "true";
4243
- if (isExpanded) {
4244
- closePopover();
4245
- } else {
4246
- openPopover(false);
4247
- }
4248
- });
4249
- dropdownMenuComponent.addEventListener("keydown", (event) => {
4250
- const isExpanded = trigger.getAttribute("aria-expanded") === "true";
4251
- if (event.key === "Escape") {
4252
- if (isExpanded) closePopover();
4253
- return;
4254
- }
4255
- if (!isExpanded) {
4256
- if (["Enter", " "].includes(event.key)) {
4257
- event.preventDefault();
4258
- openPopover(false);
4259
- } else if (event.key === "ArrowDown") {
4260
- event.preventDefault();
4261
- openPopover("first");
4262
- } else if (event.key === "ArrowUp") {
4263
- event.preventDefault();
4264
- openPopover("last");
4265
- }
4266
- return;
4267
- }
4268
- if (menuItems.length === 0) return;
4269
- let nextIndex = activeIndex;
4270
- switch (event.key) {
4271
- case "ArrowDown":
4272
- event.preventDefault();
4273
- nextIndex = activeIndex === -1 ? 0 : Math.min(activeIndex + 1, menuItems.length - 1);
4274
- break;
4275
- case "ArrowUp":
4276
- event.preventDefault();
4277
- nextIndex = activeIndex === -1 ? menuItems.length - 1 : Math.max(activeIndex - 1, 0);
4371
+ const setState = (state) => {
4372
+ open = state;
4373
+ updateState();
4374
+ };
4375
+ const sidebarId = sidebarComponent.id;
4376
+ document.addEventListener("basecoat:sidebar", ((event) => {
4377
+ if (event.detail?.id && event.detail.id !== sidebarId) return;
4378
+ switch (event.detail?.action) {
4379
+ case "open":
4380
+ setState(true);
4278
4381
  break;
4279
- case "Home":
4280
- event.preventDefault();
4281
- nextIndex = 0;
4382
+ case "close":
4383
+ setState(false);
4282
4384
  break;
4283
- case "End":
4284
- event.preventDefault();
4285
- nextIndex = menuItems.length - 1;
4385
+ default:
4386
+ setState(!open);
4286
4387
  break;
4287
- case "Enter":
4288
- case " ":
4289
- event.preventDefault();
4290
- menuItems[activeIndex]?.click();
4291
- closePopover();
4292
- return;
4293
4388
  }
4294
- if (nextIndex !== activeIndex) {
4295
- setActiveItem(nextIndex);
4389
+ }));
4390
+ sidebarComponent.addEventListener("click", (event) => {
4391
+ const target = event.target;
4392
+ const nav = sidebarComponent.querySelector("nav");
4393
+ const isMobile = window.innerWidth < breakpoint;
4394
+ if (isMobile && (target.closest("a, button") && !target.closest("[data-keep-mobile-sidebar-open]"))) {
4395
+ if (document.activeElement) document.activeElement.blur();
4396
+ setState(false);
4397
+ return;
4398
+ }
4399
+ if (target === sidebarComponent || nav && !nav.contains(target)) {
4400
+ if (document.activeElement) document.activeElement.blur();
4401
+ setState(false);
4296
4402
  }
4297
4403
  });
4298
- menu.addEventListener("mousemove", (event) => {
4299
- const menuItem = event.target.closest('[role^="menuitem"]');
4300
- if (menuItem && menuItems.includes(menuItem)) {
4301
- const index = menuItems.indexOf(menuItem);
4302
- if (index !== activeIndex) {
4303
- setActiveItem(index);
4304
- }
4404
+ window.addEventListener("popstate", updateCurrentPageLinks);
4405
+ window.addEventListener("basecoat:locationchange", updateCurrentPageLinks);
4406
+ updateState();
4407
+ updateCurrentPageLinks();
4408
+ sidebarComponent.dataset.sidebarInitialized = true;
4409
+ sidebarComponent.dispatchEvent(new CustomEvent("basecoat:initialized"));
4410
+ };
4411
+ const Sidebar = ({
4412
+ id,
4413
+ className,
4414
+ initialOpen = true,
4415
+ initialMobileOpen = false,
4416
+ breakpoint = 768,
4417
+ children,
4418
+ ...props
4419
+ }) => {
4420
+ const sidebarRef = defuss.createRef();
4421
+ return /* @__PURE__ */ jsxRuntime.jsx(
4422
+ "aside",
4423
+ {
4424
+ ref: sidebarRef,
4425
+ id,
4426
+ class: cn("sidebar", className),
4427
+ "data-initial-open": String(initialOpen),
4428
+ "data-initial-mobile-open": String(initialMobileOpen),
4429
+ "data-breakpoint": breakpoint,
4430
+ onMount: () => initSidebar(sidebarRef.current),
4431
+ ...props,
4432
+ children: /* @__PURE__ */ jsxRuntime.jsx("nav", { "aria-label": "Sidebar navigation", children })
4433
+ }
4434
+ );
4435
+ };
4436
+ const SidebarTrigger = ({
4437
+ className,
4438
+ sidebarId,
4439
+ action = "toggle",
4440
+ children,
4441
+ ...props
4442
+ }) => {
4443
+ const handleClick = () => {
4444
+ document.dispatchEvent(new CustomEvent("basecoat:sidebar", {
4445
+ detail: { id: sidebarId, action }
4446
+ }));
4447
+ };
4448
+ return /* @__PURE__ */ jsxRuntime.jsx(
4449
+ "button",
4450
+ {
4451
+ type: "button",
4452
+ class: cn(className),
4453
+ onClick: handleClick,
4454
+ ...props,
4455
+ children
4456
+ }
4457
+ );
4458
+ };
4459
+ const SidebarContent = ({ className, children, ...props }) => /* @__PURE__ */ jsxRuntime.jsx("section", { class: cn(className), ...props, children });
4460
+ const SidebarHeader = ({ className, children, ...props }) => /* @__PURE__ */ jsxRuntime.jsx("header", { class: cn(className), ...props, children });
4461
+ const SidebarFooter = ({ className, children, ...props }) => /* @__PURE__ */ jsxRuntime.jsx("footer", { class: cn(className), ...props, children });
4462
+ const SidebarGroup = ({ className, children, ...props }) => /* @__PURE__ */ jsxRuntime.jsx("div", { role: "group", class: cn(className), ...props, children });
4463
+ const SidebarGroupLabel = ({ className, children, ...props }) => /* @__PURE__ */ jsxRuntime.jsx("h3", { class: cn(className), ...props, children });
4464
+ const SidebarMenu = ({ className, children, ...props }) => /* @__PURE__ */ jsxRuntime.jsx("ul", { class: cn(className), ...props, children });
4465
+ const SidebarMenuItem = ({ className, children, ...props }) => /* @__PURE__ */ jsxRuntime.jsx("li", { class: cn(className), ...props, children });
4466
+ const SidebarMenuButton = ({ className, isActive, children, ...props }) => /* @__PURE__ */ jsxRuntime.jsx(
4467
+ "a",
4468
+ {
4469
+ role: "button",
4470
+ class: cn(className),
4471
+ "aria-current": isActive ? "page" : void 0,
4472
+ ...props,
4473
+ children
4474
+ }
4475
+ );
4476
+
4477
+ const Skeleton = ({ className, ...props }) => /* @__PURE__ */ jsxRuntime.jsx("div", { class: cn("bg-accent animate-pulse rounded-md", className), ...props });
4478
+
4479
+ const Slider = ({
4480
+ className,
4481
+ min = 0,
4482
+ max = 100,
4483
+ step = 1,
4484
+ value,
4485
+ onValueChange,
4486
+ ...props
4487
+ }) => {
4488
+ const sliderRef = defuss.createRef();
4489
+ const updateSliderValue = () => {
4490
+ if (!sliderRef.current) return;
4491
+ const val = Number(sliderRef.current.value);
4492
+ const percentage = (val - min) / (max - min) * 100;
4493
+ sliderRef.current.style.setProperty("--slider-value", `${percentage}%`);
4494
+ onValueChange?.(val);
4495
+ };
4496
+ return /* @__PURE__ */ jsxRuntime.jsx(
4497
+ "input",
4498
+ {
4499
+ ref: sliderRef,
4500
+ type: "range",
4501
+ min,
4502
+ max,
4503
+ step,
4504
+ value,
4505
+ class: cn("input", className),
4506
+ onInput: updateSliderValue,
4507
+ onMount: updateSliderValue,
4508
+ ...props
4509
+ }
4510
+ );
4511
+ };
4512
+
4513
+ const Spinner = ({ className, ...props }) => {
4514
+ return /* @__PURE__ */ jsxRuntime.jsx(
4515
+ "svg",
4516
+ {
4517
+ role: "status",
4518
+ "aria-label": "Loading",
4519
+ class: cn("animate-spin", className),
4520
+ xmlns: "http://www.w3.org/2000/svg",
4521
+ fill: "none",
4522
+ viewBox: "0 0 24 24",
4523
+ stroke: "currentColor",
4524
+ "stroke-width": "2",
4525
+ "stroke-linecap": "round",
4526
+ "stroke-linejoin": "round",
4527
+ ...props,
4528
+ children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M21 12a9 9 0 1 1-6.219-8.56" })
4305
4529
  }
4306
- });
4307
- menu.addEventListener("mouseleave", () => {
4308
- setActiveItem(-1);
4309
- });
4310
- menu.addEventListener("click", (event) => {
4311
- if (event.target.closest('[role^="menuitem"]')) {
4312
- closePopover();
4530
+ );
4531
+ };
4532
+
4533
+ const Switch = ({
4534
+ className,
4535
+ checked,
4536
+ onCheckedChange,
4537
+ ...props
4538
+ }) => {
4539
+ return /* @__PURE__ */ jsxRuntime.jsx(
4540
+ "input",
4541
+ {
4542
+ type: "checkbox",
4543
+ role: "switch",
4544
+ class: cn("input", className),
4545
+ checked,
4546
+ onChange: (e) => onCheckedChange?.(e.target.checked),
4547
+ ...props
4313
4548
  }
4314
- });
4315
- document.addEventListener("click", (event) => {
4316
- if (!dropdownMenuComponent.contains(event.target)) {
4317
- closePopover();
4549
+ );
4550
+ };
4551
+
4552
+ const Table = ({ className, children, ...props }) => {
4553
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { class: "table-container", children: /* @__PURE__ */ jsxRuntime.jsx(
4554
+ "table",
4555
+ {
4556
+ class: cn("table", className),
4557
+ ...props,
4558
+ children
4318
4559
  }
4319
- });
4320
- document.addEventListener("basecoat:popover", ((event) => {
4321
- if (event.detail.source !== dropdownMenuComponent) {
4322
- closePopover(false);
4560
+ ) });
4561
+ };
4562
+ const TableHeader = ({ className, children, ...props }) => {
4563
+ return /* @__PURE__ */ jsxRuntime.jsx(
4564
+ "thead",
4565
+ {
4566
+ class: cn("table-header", className),
4567
+ ...props,
4568
+ children
4323
4569
  }
4324
- }));
4325
- dropdownMenuComponent.dataset.dropdownMenuInitialized = true;
4326
- dropdownMenuComponent.dispatchEvent(new CustomEvent("basecoat:initialized"));
4570
+ );
4327
4571
  };
4328
- const DropdownMenu = ({ className, children, ...props }) => {
4329
- const dropdownRef = defuss.createRef();
4572
+ const TableBody = ({ className, children, ...props }) => {
4330
4573
  return /* @__PURE__ */ jsxRuntime.jsx(
4331
- "div",
4574
+ "tbody",
4332
4575
  {
4333
- ref: dropdownRef,
4334
- class: cn("dropdown-menu", className),
4335
- onMount: () => initDropdownMenu(dropdownRef.current),
4576
+ class: cn("table-body", className),
4336
4577
  ...props,
4337
4578
  children
4338
4579
  }
4339
4580
  );
4340
4581
  };
4341
- const DropdownMenuTrigger = ({ className, children, ...props }) => {
4582
+ const TableFooter = ({ className, children, ...props }) => {
4342
4583
  return /* @__PURE__ */ jsxRuntime.jsx(
4343
- "button",
4584
+ "tfoot",
4344
4585
  {
4586
+ class: cn("table-footer", className),
4345
4587
  ...props,
4346
- type: "button",
4347
- "aria-expanded": "false",
4348
- "aria-haspopup": "menu",
4349
- class: cn(className),
4350
4588
  children
4351
4589
  }
4352
4590
  );
4353
4591
  };
4354
- const DropdownMenuContent = ({ id, className, children, ...props }) => {
4355
- const menuId = id ? id.replace("-popover", "-menu") : void 0;
4356
- const triggerId = id ? id.replace("-popover", "-trigger") : void 0;
4357
- return /* @__PURE__ */ jsxRuntime.jsx("div", { ...props, id, "data-popover": "", "aria-hidden": "true", class: cn(className), children: /* @__PURE__ */ jsxRuntime.jsx("div", { role: "menu", id: menuId, "aria-labelledby": triggerId, "aria-orientation": "vertical", children }) });
4592
+ const TableRow = ({ className, children, ...props }) => {
4593
+ return /* @__PURE__ */ jsxRuntime.jsx(
4594
+ "tr",
4595
+ {
4596
+ class: cn("table-row", className),
4597
+ ...props,
4598
+ children
4599
+ }
4600
+ );
4358
4601
  };
4359
- const DropdownMenuItem = ({ className, disabled, children, ...props }) => {
4602
+ const TableHead = ({ className, children, ...props }) => {
4360
4603
  return /* @__PURE__ */ jsxRuntime.jsx(
4361
- "div",
4604
+ "th",
4362
4605
  {
4606
+ class: cn("table-head", className),
4363
4607
  ...props,
4364
- role: "menuitem",
4365
- "aria-disabled": disabled,
4366
- class: cn(className),
4367
4608
  children
4368
4609
  }
4369
4610
  );
4370
4611
  };
4371
- const DropdownMenuSeparator = ({ className, ...props }) => {
4372
- return /* @__PURE__ */ jsxRuntime.jsx("div", { role: "separator", class: cn(className), ...props });
4612
+ const TableCell = ({ className, children, ...props }) => {
4613
+ return /* @__PURE__ */ jsxRuntime.jsx(
4614
+ "td",
4615
+ {
4616
+ class: cn("table-cell", className),
4617
+ ...props,
4618
+ children
4619
+ }
4620
+ );
4373
4621
  };
4374
- const DropdownMenuLabel = ({ className, children, ...props }) => {
4375
- return /* @__PURE__ */ jsxRuntime.jsx("div", { class: cn(className), ...props, children });
4622
+ const TableCaption = ({ className, children, ...props }) => {
4623
+ return /* @__PURE__ */ jsxRuntime.jsx(
4624
+ "caption",
4625
+ {
4626
+ class: cn("table-caption", className),
4627
+ ...props,
4628
+ children
4629
+ }
4630
+ );
4376
4631
  };
4377
4632
 
4378
- const initCommand = (container) => {
4379
- const input = container.querySelector("header input");
4380
- const menu = container.querySelector('[role="menu"]');
4381
- if (!input || !menu) {
4382
- const missing = [];
4383
- if (!input) missing.push("input");
4384
- if (!menu) missing.push("menu");
4385
- console.error(`Command component initialization failed. Missing element(s): ${missing.join(", ")}`, container);
4386
- return;
4387
- }
4388
- const allMenuItems = Array.from(menu.querySelectorAll('[role="menuitem"]'));
4389
- const menuItems = allMenuItems.filter(
4390
- (item) => !item.hasAttribute("disabled") && item.getAttribute("aria-disabled") !== "true"
4391
- );
4392
- let visibleMenuItems = [...menuItems];
4393
- let activeIndex = -1;
4394
- const setActiveItem = (index) => {
4395
- if (activeIndex > -1 && menuItems[activeIndex]) {
4396
- menuItems[activeIndex].classList.remove("active");
4397
- }
4398
- activeIndex = index;
4399
- if (activeIndex > -1) {
4400
- const activeItem = menuItems[activeIndex];
4401
- activeItem.classList.add("active");
4402
- if (activeItem.id) {
4403
- input.setAttribute("aria-activedescendant", activeItem.id);
4404
- } else {
4405
- input.removeAttribute("aria-activedescendant");
4406
- }
4407
- } else {
4408
- input.removeAttribute("aria-activedescendant");
4409
- }
4633
+ const initTabs = (tabsComponent) => {
4634
+ const tablist = tabsComponent.querySelector('[role="tablist"]');
4635
+ if (!tablist) return;
4636
+ const tabs = Array.from(tablist.querySelectorAll('[role="tab"]'));
4637
+ const getPanelForTab = (tab) => {
4638
+ const value = tab.dataset.value;
4639
+ if (!value) return null;
4640
+ return tabsComponent.querySelector(`[role="tabpanel"][data-value="${value}"]`);
4410
4641
  };
4411
- const filterMenuItems = () => {
4412
- const searchTerm = input.value.trim().toLowerCase();
4413
- setActiveItem(-1);
4414
- visibleMenuItems = [];
4415
- allMenuItems.forEach((item) => {
4416
- if (item.hasAttribute("data-force")) {
4417
- item.setAttribute("aria-hidden", "false");
4418
- if (menuItems.includes(item)) {
4419
- visibleMenuItems.push(item);
4420
- }
4421
- return;
4422
- }
4423
- const itemText = (item.dataset.filter || item.textContent || "").trim().toLowerCase();
4424
- const keywordList = (item.dataset.keywords || "").toLowerCase().split(/[\s,]+/).filter(Boolean);
4425
- const matchesKeyword = keywordList.some((keyword) => keyword.includes(searchTerm));
4426
- const matches = itemText.includes(searchTerm) || matchesKeyword;
4427
- item.setAttribute("aria-hidden", String(!matches));
4428
- if (matches && menuItems.includes(item)) {
4429
- visibleMenuItems.push(item);
4430
- }
4642
+ const selectTab = (tabToSelect) => {
4643
+ tabs.forEach((tab) => {
4644
+ tab.setAttribute("aria-selected", "false");
4645
+ tab.setAttribute("tabindex", "-1");
4646
+ const panel = getPanelForTab(tab);
4647
+ if (panel) panel.hidden = true;
4431
4648
  });
4432
- if (visibleMenuItems.length > 0) {
4433
- setActiveItem(menuItems.indexOf(visibleMenuItems[0]));
4434
- visibleMenuItems[0].scrollIntoView({ block: "nearest" });
4435
- }
4649
+ tabToSelect.setAttribute("aria-selected", "true");
4650
+ tabToSelect.setAttribute("tabindex", "0");
4651
+ const activePanel = getPanelForTab(tabToSelect);
4652
+ if (activePanel) activePanel.hidden = false;
4436
4653
  };
4437
- input.addEventListener("input", filterMenuItems);
4438
- const handleKeyNavigation = (event) => {
4439
- if (!["ArrowDown", "ArrowUp", "Enter", "Home", "End"].includes(event.key)) {
4440
- return;
4441
- }
4442
- if (event.key === "Enter") {
4443
- event.preventDefault();
4444
- if (activeIndex > -1) {
4445
- menuItems[activeIndex]?.click();
4446
- }
4447
- return;
4448
- }
4449
- if (visibleMenuItems.length === 0) return;
4450
- event.preventDefault();
4451
- const currentVisibleIndex = activeIndex > -1 ? visibleMenuItems.indexOf(menuItems[activeIndex]) : -1;
4452
- let nextVisibleIndex = currentVisibleIndex;
4654
+ tablist.addEventListener("click", (event) => {
4655
+ const clickedTab = event.target.closest('[role="tab"]');
4656
+ if (clickedTab) selectTab(clickedTab);
4657
+ });
4658
+ tablist.addEventListener("keydown", (event) => {
4659
+ const currentTab = event.target;
4660
+ if (!tabs.includes(currentTab)) return;
4661
+ let nextTab;
4662
+ const currentIndex = tabs.indexOf(currentTab);
4453
4663
  switch (event.key) {
4454
- case "ArrowDown":
4455
- if (currentVisibleIndex < visibleMenuItems.length - 1) {
4456
- nextVisibleIndex = currentVisibleIndex + 1;
4457
- }
4664
+ case "ArrowRight":
4665
+ nextTab = tabs[(currentIndex + 1) % tabs.length];
4458
4666
  break;
4459
- case "ArrowUp":
4460
- if (currentVisibleIndex > 0) {
4461
- nextVisibleIndex = currentVisibleIndex - 1;
4462
- } else if (currentVisibleIndex === -1) {
4463
- nextVisibleIndex = 0;
4464
- }
4667
+ case "ArrowLeft":
4668
+ nextTab = tabs[(currentIndex - 1 + tabs.length) % tabs.length];
4465
4669
  break;
4466
4670
  case "Home":
4467
- nextVisibleIndex = 0;
4671
+ nextTab = tabs[0];
4468
4672
  break;
4469
4673
  case "End":
4470
- nextVisibleIndex = visibleMenuItems.length - 1;
4674
+ nextTab = tabs[tabs.length - 1];
4471
4675
  break;
4676
+ default:
4677
+ return;
4472
4678
  }
4473
- if (nextVisibleIndex !== currentVisibleIndex) {
4474
- const newActiveItem = visibleMenuItems[nextVisibleIndex];
4475
- setActiveItem(menuItems.indexOf(newActiveItem));
4476
- newActiveItem.scrollIntoView({ block: "nearest", behavior: "smooth" });
4679
+ event.preventDefault();
4680
+ if (nextTab) {
4681
+ selectTab(nextTab);
4682
+ nextTab.focus();
4477
4683
  }
4478
- };
4479
- menu.addEventListener("mousemove", (event) => {
4480
- const menuItem = event.target.closest('[role="menuitem"]');
4481
- if (menuItem && visibleMenuItems.includes(menuItem)) {
4482
- const index = menuItems.indexOf(menuItem);
4483
- if (index !== activeIndex) {
4484
- setActiveItem(index);
4684
+ });
4685
+ tabsComponent.dataset.tabsInitialized = true;
4686
+ tabsComponent.dispatchEvent(new CustomEvent("basecoat:initialized"));
4687
+ };
4688
+ const Tabs = ({ className, defaultValue, children, ...props }) => {
4689
+ const tabsRef = defuss.createRef();
4690
+ const onMount = () => {
4691
+ if (!tabsRef.current) return;
4692
+ const tabsRoot = tabsRef.current;
4693
+ const instanceId = tabsRoot.id || `tabs-${Math.random().toString(36).slice(2, 9)}`;
4694
+ if (!tabsRoot.id) {
4695
+ tabsRoot.id = instanceId;
4696
+ }
4697
+ const tablist = tabsRoot.querySelector('[role="tablist"]');
4698
+ const tabs = tablist ? Array.from(tablist.querySelectorAll('[role="tab"]')) : [];
4699
+ tabs.forEach((tab, index) => {
4700
+ const value = tab.dataset.value;
4701
+ if (!value) return;
4702
+ const panel = tabsRoot.querySelector(`[role="tabpanel"][data-value="${value}"]`);
4703
+ const tabId = `${instanceId}-tab-${index}`;
4704
+ const panelId = `${instanceId}-panel-${index}`;
4705
+ tab.id = tabId;
4706
+ if (panel) {
4707
+ panel.id = panelId;
4708
+ tab.setAttribute("aria-controls", panelId);
4709
+ panel.setAttribute("aria-labelledby", tabId);
4710
+ }
4711
+ });
4712
+ initTabs(tabsRef.current);
4713
+ if (defaultValue) {
4714
+ const defaultTab = tabsRef.current.querySelector(`[role="tab"][data-value="${defaultValue}"]`);
4715
+ if (defaultTab) {
4716
+ defaultTab.click();
4485
4717
  }
4486
- }
4487
- });
4488
- menu.addEventListener("click", (event) => {
4489
- const clickedItem = event.target.closest('[role="menuitem"]');
4490
- if (clickedItem && visibleMenuItems.includes(clickedItem)) {
4491
- const dialog = container.closest("dialog.command-dialog");
4492
- if (dialog && !clickedItem.hasAttribute("data-keep-command-open")) {
4493
- dialog.close();
4718
+ } else {
4719
+ const firstTab = tabsRef.current.querySelector('[role="tab"]');
4720
+ if (firstTab) {
4721
+ firstTab.click();
4494
4722
  }
4495
4723
  }
4496
- });
4497
- input.addEventListener("keydown", handleKeyNavigation);
4498
- if (visibleMenuItems.length > 0) {
4499
- setActiveItem(menuItems.indexOf(visibleMenuItems[0]));
4500
- visibleMenuItems[0].scrollIntoView({ block: "nearest" });
4501
- }
4502
- container.dataset.commandInitialized = true;
4503
- container.dispatchEvent(new CustomEvent("basecoat:initialized"));
4724
+ };
4725
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { ref: tabsRef, class: cn("tabs", className), onMount, ...props, children });
4504
4726
  };
4505
- const Command = ({ className, children, ...props }) => {
4506
- const commandRef = defuss.createRef();
4727
+ const TabsList = ({ className, children, ...props }) => {
4507
4728
  return /* @__PURE__ */ jsxRuntime.jsx(
4508
- "div",
4729
+ "nav",
4509
4730
  {
4510
- ref: commandRef,
4511
- class: cn("command", className),
4512
- onMount: () => initCommand(commandRef.current),
4731
+ role: "tablist",
4732
+ "aria-orientation": "horizontal",
4733
+ class: cn(className),
4513
4734
  ...props,
4514
4735
  children
4515
4736
  }
4516
4737
  );
4517
4738
  };
4518
- const CommandInput = ({ className, placeholder, ...props }) => {
4519
- return /* @__PURE__ */ jsxRuntime.jsxs("header", { children: [
4520
- /* @__PURE__ */ jsxRuntime.jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round", class: "lucide lucide-search", children: [
4521
- /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "11", cy: "11", r: "8" }),
4522
- /* @__PURE__ */ jsxRuntime.jsx("path", { d: "m21 21-4.3-4.3" })
4523
- ] }),
4524
- /* @__PURE__ */ jsxRuntime.jsx(
4525
- "input",
4526
- {
4527
- type: "text",
4528
- placeholder,
4529
- role: "combobox",
4530
- "aria-autocomplete": "list",
4531
- "aria-expanded": "true",
4532
- class: cn(className),
4533
- ...props
4534
- }
4535
- )
4536
- ] });
4537
- };
4538
- const CommandList = ({ className, children, ...props }) => {
4539
- return /* @__PURE__ */ jsxRuntime.jsx("div", { role: "menu", class: cn("scrollbar", className), ...props, children });
4540
- };
4541
- const CommandGroup = ({ className, heading, children, ...props }) => {
4542
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { role: "group", class: cn(className), ...props, children: [
4543
- heading && /* @__PURE__ */ jsxRuntime.jsx("div", { role: "presentation", children: heading }),
4544
- children
4545
- ] });
4739
+ const TabsTrigger = ({ className, value, children, ...props }) => {
4740
+ return /* @__PURE__ */ jsxRuntime.jsx(
4741
+ "button",
4742
+ {
4743
+ role: "tab",
4744
+ tabIndex: -1,
4745
+ "aria-selected": "false",
4746
+ "data-value": value,
4747
+ class: cn(className),
4748
+ ...props,
4749
+ children
4750
+ }
4751
+ );
4546
4752
  };
4547
- const CommandItem = ({ className, keywords, disabled, children, ...props }) => {
4753
+ const TabsContent = ({ className, value, children, ...props }) => {
4548
4754
  return /* @__PURE__ */ jsxRuntime.jsx(
4549
4755
  "div",
4550
4756
  {
4551
- role: "menuitem",
4552
- "data-keywords": keywords,
4553
- "aria-disabled": disabled,
4757
+ role: "tabpanel",
4758
+ "data-value": value,
4759
+ tabIndex: -1,
4760
+ hidden: true,
4554
4761
  class: cn(className),
4555
4762
  ...props,
4556
4763
  children
4557
4764
  }
4558
4765
  );
4559
4766
  };
4560
- const CommandEmpty = ({ className, children, ...props }) => {
4561
- return /* @__PURE__ */ jsxRuntime.jsx("div", { class: cn(className), ...props, children });
4562
- };
4563
- const CommandSeparator = ({ className, ...props }) => {
4564
- return /* @__PURE__ */ jsxRuntime.jsx("div", { role: "separator", class: cn(className), ...props });
4767
+
4768
+ const Textarea = ({ className, ...props }) => {
4769
+ return /* @__PURE__ */ jsxRuntime.jsx("textarea", { class: cn("textarea", className), ...props });
4565
4770
  };
4566
4771
 
4567
4772
  const ICONS = {
@@ -4737,165 +4942,62 @@ const ToastTitle = ({ className, children, ...props }) => /* @__PURE__ */ jsxRun
4737
4942
  const ToastDescription = ({ className, children, ...props }) => /* @__PURE__ */ jsxRuntime.jsx("p", { class: cn(className), ...props, children });
4738
4943
  const ToastAction = ({ className, children, ...props }) => /* @__PURE__ */ jsxRuntime.jsx("footer", { children: /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", class: cn("btn", className), "data-toast-action": true, ...props, children }) });
4739
4944
 
4740
- if (typeof window !== "undefined" && !window.history.__basecoatPatched) {
4741
- const originalPushState = window.history.pushState;
4742
- window.history.pushState = function(...args) {
4743
- originalPushState.apply(this, args);
4744
- window.dispatchEvent(new Event("basecoat:locationchange"));
4745
- };
4746
- const originalReplaceState = window.history.replaceState;
4747
- window.history.replaceState = function(...args) {
4748
- originalReplaceState.apply(this, args);
4749
- window.dispatchEvent(new Event("basecoat:locationchange"));
4750
- };
4751
- window.history.__basecoatPatched = true;
4752
- }
4753
- const initSidebar = (sidebarComponent) => {
4754
- const initialOpen = sidebarComponent.dataset.initialOpen !== "false";
4755
- const initialMobileOpen = sidebarComponent.dataset.initialMobileOpen === "true";
4756
- const breakpoint = parseInt(sidebarComponent.dataset.breakpoint) || 768;
4757
- let open = breakpoint > 0 ? window.innerWidth >= breakpoint ? initialOpen : initialMobileOpen : initialOpen;
4758
- const updateCurrentPageLinks = () => {
4759
- const currentPath = window.location.pathname.replace(/\/$/, "");
4760
- sidebarComponent.querySelectorAll("a").forEach((link) => {
4761
- if (link.hasAttribute("data-ignore-current")) return;
4762
- if (!link.href) return;
4763
- const rawHref = link.getAttribute("href");
4764
- if (!rawHref) return;
4765
- if (rawHref.startsWith("#")) {
4766
- link.removeAttribute("aria-current");
4767
- return;
4768
- }
4769
- try {
4770
- const linkPath = new URL(link.href).pathname.replace(/\/$/, "");
4771
- if (linkPath === currentPath) {
4772
- link.setAttribute("aria-current", "page");
4773
- } else {
4774
- link.removeAttribute("aria-current");
4775
- }
4776
- } catch (e) {
4777
- }
4778
- });
4779
- };
4780
- const updateState = () => {
4781
- sidebarComponent.setAttribute("aria-hidden", String(!open));
4782
- if (open) {
4783
- sidebarComponent.removeAttribute("inert");
4784
- } else {
4785
- sidebarComponent.setAttribute("inert", "");
4786
- }
4787
- };
4788
- const setState = (state) => {
4789
- open = state;
4790
- updateState();
4791
- };
4792
- const sidebarId = sidebarComponent.id;
4793
- document.addEventListener("basecoat:sidebar", ((event) => {
4794
- if (event.detail?.id && event.detail.id !== sidebarId) return;
4795
- switch (event.detail?.action) {
4796
- case "open":
4797
- setState(true);
4798
- break;
4799
- case "close":
4800
- setState(false);
4801
- break;
4802
- default:
4803
- setState(!open);
4804
- break;
4805
- }
4806
- }));
4807
- sidebarComponent.addEventListener("click", (event) => {
4808
- const target = event.target;
4809
- const nav = sidebarComponent.querySelector("nav");
4810
- const isMobile = window.innerWidth < breakpoint;
4811
- if (isMobile && (target.closest("a, button") && !target.closest("[data-keep-mobile-sidebar-open]"))) {
4812
- if (document.activeElement) document.activeElement.blur();
4813
- setState(false);
4814
- return;
4815
- }
4816
- if (target === sidebarComponent || nav && !nav.contains(target)) {
4817
- if (document.activeElement) document.activeElement.blur();
4818
- setState(false);
4819
- }
4820
- });
4821
- window.addEventListener("popstate", updateCurrentPageLinks);
4822
- window.addEventListener("basecoat:locationchange", updateCurrentPageLinks);
4823
- updateState();
4824
- updateCurrentPageLinks();
4825
- sidebarComponent.dataset.sidebarInitialized = true;
4826
- sidebarComponent.dispatchEvent(new CustomEvent("basecoat:initialized"));
4827
- };
4828
- const Sidebar = ({
4829
- id,
4945
+ const ToggleButton = ({
4946
+ class: classProp,
4830
4947
  className,
4831
- initialOpen = true,
4832
- initialMobileOpen = false,
4833
- breakpoint = 768,
4948
+ pressed,
4834
4949
  children,
4950
+ onClick,
4835
4951
  ...props
4836
4952
  }) => {
4837
- const sidebarRef = defuss.createRef();
4953
+ const isPressed = pressed === true;
4838
4954
  return /* @__PURE__ */ jsxRuntime.jsx(
4839
- "aside",
4955
+ "button",
4840
4956
  {
4841
- ref: sidebarRef,
4842
- id,
4843
- class: cn("sidebar", className),
4844
- "data-initial-open": String(initialOpen),
4845
- "data-initial-mobile-open": String(initialMobileOpen),
4846
- "data-breakpoint": breakpoint,
4847
- onMount: () => initSidebar(sidebarRef.current),
4957
+ class: cn(
4958
+ buttonVariants({ variant: isPressed ? "default" : "outline", size: "sm" }),
4959
+ classProp,
4960
+ className
4961
+ ),
4962
+ "aria-pressed": isPressed,
4963
+ "data-pressed": isPressed,
4964
+ onClick: (event) => {
4965
+ if (pressed === void 0) {
4966
+ const button = event.target.closest("button");
4967
+ if (!button) {
4968
+ onClick?.(event);
4969
+ return;
4970
+ }
4971
+ const nextPressed = button.getAttribute("aria-pressed") !== "true";
4972
+ button.setAttribute("aria-pressed", String(nextPressed));
4973
+ button.dataset.pressed = String(nextPressed);
4974
+ button.classList.toggle("btn-primary", nextPressed);
4975
+ button.classList.toggle("btn-outline", !nextPressed);
4976
+ }
4977
+ onClick?.(event);
4978
+ },
4848
4979
  ...props,
4849
- children: /* @__PURE__ */ jsxRuntime.jsx("nav", { "aria-label": "Sidebar navigation", children })
4980
+ children
4850
4981
  }
4851
4982
  );
4852
4983
  };
4853
- const SidebarTrigger = ({
4984
+ const Toggle = ToggleButton;
4985
+
4986
+ const Tooltip = ({
4854
4987
  className,
4855
- sidebarId,
4856
- action = "toggle",
4988
+ tooltip,
4989
+ side,
4990
+ align,
4857
4991
  children,
4858
4992
  ...props
4859
4993
  }) => {
4860
- const handleClick = () => {
4861
- document.dispatchEvent(new CustomEvent("basecoat:sidebar", {
4862
- detail: { id: sidebarId, action }
4863
- }));
4864
- };
4865
4994
  return /* @__PURE__ */ jsxRuntime.jsx(
4866
- "button",
4995
+ "div",
4867
4996
  {
4868
- type: "button",
4997
+ "data-tooltip": tooltip,
4998
+ "data-side": side,
4999
+ "data-align": align,
4869
5000
  class: cn(className),
4870
- onClick: handleClick,
4871
- ...props,
4872
- children
4873
- }
4874
- );
4875
- };
4876
- const SidebarContent = ({ className, children, ...props }) => /* @__PURE__ */ jsxRuntime.jsx("section", { class: cn(className), ...props, children });
4877
- const SidebarHeader = ({ className, children, ...props }) => /* @__PURE__ */ jsxRuntime.jsx("header", { class: cn(className), ...props, children });
4878
- const SidebarFooter = ({ className, children, ...props }) => /* @__PURE__ */ jsxRuntime.jsx("footer", { class: cn(className), ...props, children });
4879
- const SidebarGroup = ({ className, children, ...props }) => /* @__PURE__ */ jsxRuntime.jsx("div", { role: "group", class: cn(className), ...props, children });
4880
- const SidebarGroupLabel = ({ className, children, ...props }) => /* @__PURE__ */ jsxRuntime.jsx("h3", { class: cn(className), ...props, children });
4881
- const SidebarMenu = ({ className, children, ...props }) => /* @__PURE__ */ jsxRuntime.jsx("ul", { class: cn(className), ...props, children });
4882
- const SidebarMenuItem = ({ className, children, ...props }) => /* @__PURE__ */ jsxRuntime.jsx("li", { class: cn(className), ...props, children });
4883
- const SidebarMenuButton = ({ className, isActive, children, ...props }) => /* @__PURE__ */ jsxRuntime.jsx(
4884
- "a",
4885
- {
4886
- role: "button",
4887
- class: cn(className),
4888
- "aria-current": isActive ? "page" : void 0,
4889
- ...props,
4890
- children
4891
- }
4892
- );
4893
-
4894
- const Select = ({ className, children, ...props }) => {
4895
- return /* @__PURE__ */ jsxRuntime.jsx(
4896
- "select",
4897
- {
4898
- class: cn("select", className),
4899
5001
  ...props,
4900
5002
  children
4901
5003
  }
@@ -5010,6 +5112,8 @@ exports.ToastAction = ToastAction;
5010
5112
  exports.ToastDescription = ToastDescription;
5011
5113
  exports.ToastTitle = ToastTitle;
5012
5114
  exports.Toaster = Toaster;
5115
+ exports.Toggle = Toggle;
5116
+ exports.ToggleButton = ToggleButton;
5013
5117
  exports.Tooltip = Tooltip;
5014
5118
  exports.alertVariants = alertVariants;
5015
5119
  exports.badgeVariants = badgeVariants;