@facter/ds-core 1.33.11 → 1.34.1

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.mjs CHANGED
@@ -17,12 +17,11 @@ import { toast as toast$1, Toaster as Toaster$1 } from 'sonner';
17
17
  import * as SwitchPrimitives from '@radix-ui/react-switch';
18
18
  import { FormProvider, useFormContext, Controller } from 'react-hook-form';
19
19
  export { FormProvider, useFormContext } from 'react-hook-form';
20
- import { createPortal } from 'react-dom';
20
+ import * as TooltipPrimitive from '@radix-ui/react-tooltip';
21
+ import * as PopoverPrimitive from '@radix-ui/react-popover';
21
22
  import * as RadioGroupPrimitive from '@radix-ui/react-radio-group';
22
23
  import * as AvatarPrimitive from '@radix-ui/react-avatar';
23
24
  import * as DropdownMenuPrimitive from '@radix-ui/react-dropdown-menu';
24
- import * as PopoverPrimitive from '@radix-ui/react-popover';
25
- import * as TooltipPrimitive from '@radix-ui/react-tooltip';
26
25
  import * as ScrollAreaPrimitive from '@radix-ui/react-scroll-area';
27
26
  import * as SeparatorPrimitive from '@radix-ui/react-separator';
28
27
  import { Slot } from '@radix-ui/react-slot';
@@ -33,7 +32,7 @@ function cn(...inputs) {
33
32
  return twMerge(clsx(inputs));
34
33
  }
35
34
  var buttonVariants = cva(
36
- "inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:pointer-events-none disabled:opacity-50",
35
+ "inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:pointer-events-none disabled:opacity-70",
37
36
  {
38
37
  variants: {
39
38
  variant: {
@@ -166,6 +165,7 @@ var Input = React10.forwardRef(
166
165
  label,
167
166
  icon: Icon2,
168
167
  required,
168
+ labelSuffix,
169
169
  containerClassName,
170
170
  labelClassName,
171
171
  ...props
@@ -210,7 +210,7 @@ var Input = React10.forwardRef(
210
210
  "label",
211
211
  {
212
212
  className: cn(
213
- "absolute left-3 top-[-6px] text-xs font-medium bg-background px-1 cursor-pointer",
213
+ "absolute left-3 top-[-6px] text-xs font-medium bg-background px-1 cursor-pointer inline-flex items-center gap-1",
214
214
  error ? "text-red-500" : "text-foreground",
215
215
  Icon2 && "left-10",
216
216
  labelClassName
@@ -218,7 +218,8 @@ var Input = React10.forwardRef(
218
218
  onClick: focusInput,
219
219
  children: [
220
220
  label,
221
- required && /* @__PURE__ */ jsx("span", { className: "text-red-500 ml-0.5", children: "*" })
221
+ required && /* @__PURE__ */ jsx("span", { className: "text-red-500 ml-0.5", children: "*" }),
222
+ labelSuffix
222
223
  ]
223
224
  }
224
225
  ),
@@ -3226,238 +3227,487 @@ function FormFieldProvider({ name, children }) {
3226
3227
  );
3227
3228
  return /* @__PURE__ */ jsx(FormFieldContext.Provider, { value, children });
3228
3229
  }
3229
- function applyMask(value, mask) {
3230
- const digits = value.replace(/\D/g, "");
3231
- switch (mask) {
3232
- case "phone":
3233
- if (digits.length <= 10) {
3234
- return digits.replace(/(\d{2})(\d{4})(\d{0,4})/, "($1) $2-$3").trim();
3235
- }
3236
- return digits.replace(/(\d{2})(\d{5})(\d{0,4})/, "($1) $2-$3").trim();
3237
- case "cpf":
3238
- return digits.replace(/(\d{3})(\d)/, "$1.$2").replace(/(\d{3})(\d)/, "$1.$2").replace(/(\d{3})(\d{1,2})$/, "$1-$2");
3239
- case "cnpj":
3240
- return digits.replace(/(\d{2})(\d)/, "$1.$2").replace(/(\d{3})(\d)/, "$1.$2").replace(/(\d{3})(\d)/, "$1/$2").replace(/(\d{4})(\d{1,2})$/, "$1-$2");
3241
- case "cep":
3242
- return digits.replace(/(\d{5})(\d{0,3})/, "$1-$2");
3243
- case "money":
3244
- if (!digits) return "";
3245
- const cents = parseInt(digits, 10) / 100;
3246
- return new Intl.NumberFormat("pt-BR", {
3247
- style: "currency",
3248
- currency: "BRL"
3249
- }).format(cents);
3250
- case "percent":
3251
- if (!digits) return "";
3252
- const percent = parseInt(digits, 10) / 100;
3253
- return `${percent.toFixed(2)}%`;
3254
- case "plate":
3255
- const upper = value.toUpperCase().replace(/[^A-Z0-9]/g, "");
3256
- if (upper.length <= 3) return upper;
3257
- if (upper.length <= 7) {
3258
- return upper.replace(/([A-Z]{3})(\d{0,1})([A-Z0-9]{0,1})(\d{0,2})/, "$1-$2$3$4");
3259
- }
3260
- return upper.slice(0, 7).replace(/([A-Z]{3})(\d{0,1})([A-Z0-9]{0,1})(\d{0,2})/, "$1-$2$3$4");
3261
- case "date":
3262
- return digits.replace(/(\d{2})(\d)/, "$1/$2").replace(/(\d{2})(\d)/, "$1/$2").slice(0, 10);
3263
- case "time":
3264
- return digits.replace(/(\d{2})(\d{0,2})/, "$1:$2").slice(0, 5);
3265
- case "datetime":
3266
- const dateTime = digits.replace(/(\d{2})(\d)/, "$1/$2").replace(/(\d{2})(\d)/, "$1/$2").replace(/(\d{4})(\d)/, "$1 $2").replace(/(\d{2})(\d{0,2})$/, "$1:$2");
3267
- return dateTime.slice(0, 16);
3268
- default:
3269
- return value;
3270
- }
3271
- }
3272
- function getMaxLength(mask) {
3273
- switch (mask) {
3274
- case "phone":
3275
- return 15;
3276
- case "cpf":
3277
- return 14;
3278
- case "cnpj":
3279
- return 18;
3280
- case "cep":
3281
- return 9;
3282
- case "plate":
3283
- return 8;
3284
- case "date":
3285
- return 10;
3286
- case "time":
3287
- return 5;
3288
- case "datetime":
3289
- return 16;
3290
- default:
3291
- return void 0;
3292
- }
3293
- }
3294
- function parseValue(displayValue, mask) {
3295
- if (!displayValue) return void 0;
3296
- switch (mask) {
3297
- case "money":
3298
- const moneyDigits = displayValue.replace(/\D/g, "");
3299
- return moneyDigits ? parseInt(moneyDigits, 10) / 100 : void 0;
3300
- case "percent":
3301
- const percentDigits = displayValue.replace(/\D/g, "");
3302
- return percentDigits ? parseInt(percentDigits, 10) / 100 : void 0;
3303
- default:
3304
- return displayValue || void 0;
3230
+ var TooltipProvider = TooltipPrimitive.Provider;
3231
+ var TooltipRoot = TooltipPrimitive.Root;
3232
+ var TooltipTrigger = TooltipPrimitive.Trigger;
3233
+ var TooltipPortal = TooltipPrimitive.Portal;
3234
+ var TooltipArrow = React10.forwardRef(({ className, variant = "light", ...props }, ref) => /* @__PURE__ */ jsx(
3235
+ TooltipPrimitive.Arrow,
3236
+ {
3237
+ ref,
3238
+ className: cn(
3239
+ "z-50",
3240
+ variant === "dark" ? "fill-popover" : "fill-background",
3241
+ className
3242
+ ),
3243
+ ...props
3305
3244
  }
3306
- }
3307
- function FormInput({
3308
- name,
3309
- label,
3310
- description,
3311
- required,
3312
- disabled,
3313
- className,
3314
- mask,
3315
- icon,
3316
- showPasswordToggle = true,
3317
- inputSize = "default",
3318
- hideError = false,
3319
- type = "text",
3320
- maxLength,
3321
- ...inputProps
3322
- }) {
3323
- const form = useFormContext();
3324
- const fieldState = form.getFieldState(name, form.formState);
3325
- const error = fieldState.error?.message;
3326
- const getInputType = React10.useCallback(() => {
3327
- if (["money", "percent", "phone", "cpf", "cnpj", "cep"].includes(mask || "")) {
3328
- return "tel";
3329
- }
3330
- return type;
3331
- }, [mask, type]);
3332
- return /* @__PURE__ */ jsx(FormFieldProvider, { name, children: /* @__PURE__ */ jsx(
3333
- Controller,
3334
- {
3335
- control: form.control,
3336
- name,
3337
- render: ({ field }) => {
3338
- const getDisplayValue = () => {
3339
- if (field.value === void 0 || field.value === null) return "";
3340
- if (mask) {
3341
- return applyMask(String(field.value), mask);
3342
- }
3343
- return String(field.value);
3344
- };
3345
- const handleChange = (e) => {
3346
- let newValue = e.target.value;
3347
- if (mask) {
3348
- newValue = applyMask(newValue, mask);
3349
- const parsed = parseValue(newValue, mask);
3350
- field.onChange(parsed);
3351
- } else {
3352
- field.onChange(newValue || void 0);
3353
- }
3354
- };
3355
- return /* @__PURE__ */ jsxs("div", { className: cn("space-y-1", className), children: [
3356
- /* @__PURE__ */ jsx(
3357
- Input,
3358
- {
3359
- ...inputProps,
3360
- ref: field.ref,
3361
- name: field.name,
3362
- value: getDisplayValue(),
3363
- onChange: handleChange,
3364
- onBlur: field.onBlur,
3365
- disabled,
3366
- type: getInputType(),
3367
- label,
3368
- required,
3369
- error: !!error,
3370
- icon,
3371
- inputSize,
3372
- maxLength: maxLength ?? getMaxLength(mask)
3373
- }
3374
- ),
3375
- description && !error && /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground px-1", children: description }),
3376
- !hideError && error && /* @__PURE__ */ jsx("p", { className: "text-xs text-red-500 px-1", children: error })
3377
- ] });
3245
+ ));
3246
+ TooltipArrow.displayName = "TooltipArrow";
3247
+ var tooltipContentVariants = cva(
3248
+ "z-50 overflow-hidden rounded-lg shadow-lg outline-none animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
3249
+ {
3250
+ variants: {
3251
+ variant: {
3252
+ light: "bg-background text-foreground border border-border",
3253
+ dark: "bg-popover text-popover-foreground border border-border"
3254
+ },
3255
+ size: {
3256
+ sm: "max-w-[200px] p-2",
3257
+ md: "max-w-[280px] p-3",
3258
+ lg: "max-w-[360px] p-4"
3378
3259
  }
3260
+ },
3261
+ defaultVariants: {
3262
+ variant: "light",
3263
+ size: "md"
3379
3264
  }
3380
- ) });
3381
- }
3382
- FormInput.displayName = "Form.Input";
3383
- function FormSelect({
3384
- name,
3385
- label,
3386
- description,
3387
- required,
3388
- disabled,
3389
- className,
3390
- options,
3391
- placeholder = "Selecione...",
3392
- icon,
3393
- hideError = false,
3394
- selectSize = "default",
3395
- emptyText = "Nenhuma op\xE7\xE3o dispon\xEDvel",
3396
- loading = false,
3397
- variant = "default",
3398
- searchable = false,
3399
- onSearch,
3400
- onLoadMore,
3401
- hasMore,
3402
- searchPlaceholder = "Buscar..."
3403
- }) {
3404
- const form = useFormContext();
3405
- const fieldState = form.getFieldState(name, form.formState);
3406
- const error = fieldState.error?.message;
3407
- return /* @__PURE__ */ jsx(FormFieldProvider, { name, children: /* @__PURE__ */ jsx(
3408
- Controller,
3265
+ }
3266
+ );
3267
+ var TooltipContent = React10.forwardRef(
3268
+ ({
3269
+ className,
3270
+ variant = "light",
3271
+ size = "md",
3272
+ sideOffset = 8,
3273
+ showArrow = true,
3274
+ onDismiss,
3275
+ children,
3276
+ ...props
3277
+ }, ref) => /* @__PURE__ */ jsx(TooltipPrimitive.Portal, { children: /* @__PURE__ */ jsxs(
3278
+ TooltipPrimitive.Content,
3409
3279
  {
3410
- control: form.control,
3411
- name,
3412
- render: ({ field }) => /* @__PURE__ */ jsxs("div", { className: cn("space-y-1", className), children: [
3413
- variant === "card" ? /* @__PURE__ */ jsx(
3414
- CardSelect,
3280
+ ref,
3281
+ sideOffset,
3282
+ className: cn(tooltipContentVariants({ variant, size }), className),
3283
+ ...props,
3284
+ children: [
3285
+ onDismiss && /* @__PURE__ */ jsx(
3286
+ "button",
3415
3287
  {
3416
- options,
3417
- value: field.value,
3418
- onChange: (v) => field.onChange(v || void 0),
3419
- disabled,
3420
- label,
3421
- required,
3422
- error: !!error,
3423
- placeholder,
3424
- searchable,
3425
- onSearch,
3426
- onLoadMore,
3427
- hasMore,
3428
- loading,
3429
- emptyText,
3430
- searchPlaceholder
3288
+ onClick: onDismiss,
3289
+ className: "absolute top-2 right-2 p-1 rounded-full transition-colors hover:bg-muted text-muted-foreground hover:text-foreground",
3290
+ "aria-label": "Fechar",
3291
+ children: /* @__PURE__ */ jsx(X, { className: "h-3.5 w-3.5" })
3431
3292
  }
3432
- ) : /* @__PURE__ */ jsx(
3433
- Select,
3434
- {
3435
- value: field.value ?? "",
3436
- onValueChange: (value) => {
3437
- field.onChange(value || void 0);
3438
- },
3439
- disabled: disabled || loading,
3440
- label,
3293
+ ),
3294
+ children,
3295
+ showArrow && /* @__PURE__ */ jsx(TooltipArrow, { variant: variant ?? "light", width: 12, height: 6 })
3296
+ ]
3297
+ }
3298
+ ) })
3299
+ );
3300
+ TooltipContent.displayName = TooltipPrimitive.Content.displayName;
3301
+ var TooltipHeader = React10.forwardRef(
3302
+ ({ className, ...props }, ref) => /* @__PURE__ */ jsx("div", { ref, className: cn("flex flex-col gap-1", className), ...props })
3303
+ );
3304
+ TooltipHeader.displayName = "TooltipHeader";
3305
+ var TooltipTitle = React10.forwardRef(
3306
+ ({ className, ...props }, ref) => /* @__PURE__ */ jsx(
3307
+ "h4",
3308
+ {
3309
+ ref,
3310
+ className: cn("text-sm font-semibold leading-tight", className),
3311
+ ...props
3312
+ }
3313
+ )
3314
+ );
3315
+ TooltipTitle.displayName = "TooltipTitle";
3316
+ var TooltipDescription = React10.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
3317
+ "p",
3318
+ {
3319
+ ref,
3320
+ className: cn("text-xs leading-relaxed text-muted-foreground", className),
3321
+ ...props
3322
+ }
3323
+ ));
3324
+ TooltipDescription.displayName = "TooltipDescription";
3325
+ var TooltipActions = React10.forwardRef(
3326
+ ({ className, ...props }, ref) => /* @__PURE__ */ jsx(
3327
+ "div",
3328
+ {
3329
+ ref,
3330
+ className: cn("flex items-center gap-2 mt-3", className),
3331
+ ...props
3332
+ }
3333
+ )
3334
+ );
3335
+ TooltipActions.displayName = "TooltipActions";
3336
+ var tooltipActionVariants = cva(
3337
+ "inline-flex items-center justify-center text-xs font-medium rounded-md transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",
3338
+ {
3339
+ variants: {
3340
+ variant: {
3341
+ primary: "bg-primary text-primary-foreground hover:bg-primary/90 px-3 py-1.5",
3342
+ secondary: "bg-transparent text-muted-foreground hover:text-foreground hover:underline px-2 py-1.5",
3343
+ outline: "border border-border bg-background text-foreground hover:bg-muted px-3 py-1.5"
3344
+ }
3345
+ },
3346
+ defaultVariants: {
3347
+ variant: "primary"
3348
+ }
3349
+ }
3350
+ );
3351
+ var TooltipAction = React10.forwardRef(
3352
+ ({ className, variant = "primary", ...props }, ref) => /* @__PURE__ */ jsx(
3353
+ "button",
3354
+ {
3355
+ ref,
3356
+ className: cn(tooltipActionVariants({ variant }), className),
3357
+ ...props
3358
+ }
3359
+ )
3360
+ );
3361
+ TooltipAction.displayName = "TooltipAction";
3362
+ var TooltipIcon = React10.forwardRef(
3363
+ ({ className, children, ...props }, ref) => /* @__PURE__ */ jsx(
3364
+ "div",
3365
+ {
3366
+ ref,
3367
+ className: cn(
3368
+ "flex items-center justify-center w-10 h-10 rounded-full mb-3 bg-muted",
3369
+ className
3370
+ ),
3371
+ ...props,
3372
+ children
3373
+ }
3374
+ )
3375
+ );
3376
+ TooltipIcon.displayName = "TooltipIcon";
3377
+ var SimpleTooltip = ({
3378
+ children,
3379
+ content,
3380
+ variant = "light",
3381
+ side = "top",
3382
+ align = "center",
3383
+ delayDuration = 200,
3384
+ open,
3385
+ defaultOpen,
3386
+ onOpenChange
3387
+ }) => /* @__PURE__ */ jsxs(
3388
+ TooltipRoot,
3389
+ {
3390
+ open,
3391
+ defaultOpen,
3392
+ onOpenChange,
3393
+ delayDuration,
3394
+ children: [
3395
+ /* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children }),
3396
+ /* @__PURE__ */ jsx(TooltipContent, { variant, side, align, size: "sm", children: /* @__PURE__ */ jsx("span", { className: "text-xs", children: content }) })
3397
+ ]
3398
+ }
3399
+ );
3400
+ SimpleTooltip.displayName = "SimpleTooltip";
3401
+ var Tooltip = Object.assign(TooltipRoot, {
3402
+ Provider: TooltipProvider,
3403
+ Trigger: TooltipTrigger,
3404
+ Portal: TooltipPortal,
3405
+ Content: TooltipContent,
3406
+ Arrow: TooltipArrow,
3407
+ Header: TooltipHeader,
3408
+ Title: TooltipTitle,
3409
+ Description: TooltipDescription,
3410
+ Actions: TooltipActions,
3411
+ Action: TooltipAction,
3412
+ Icon: TooltipIcon,
3413
+ Simple: SimpleTooltip
3414
+ });
3415
+ var FormLabel = React10.forwardRef(
3416
+ ({ className, required, children, ...props }, ref) => {
3417
+ const fieldContext = useFormFieldContextOptional();
3418
+ return /* @__PURE__ */ jsxs(
3419
+ "label",
3420
+ {
3421
+ ref,
3422
+ htmlFor: fieldContext?.id,
3423
+ className: cn(
3424
+ "text-sm font-medium leading-none",
3425
+ fieldContext?.error && "text-red-500",
3426
+ className
3427
+ ),
3428
+ ...props,
3429
+ children: [
3430
+ children,
3431
+ (required || fieldContext?.isRequired) && /* @__PURE__ */ jsx("span", { className: "text-red-500 ml-0.5", children: "*" })
3432
+ ]
3433
+ }
3434
+ );
3435
+ }
3436
+ );
3437
+ FormLabel.displayName = "Form.Label";
3438
+ var FormDescription = React10.forwardRef(({ className, ...props }, ref) => {
3439
+ const fieldContext = useFormFieldContextOptional();
3440
+ if (fieldContext?.error) {
3441
+ return null;
3442
+ }
3443
+ return /* @__PURE__ */ jsx(
3444
+ "p",
3445
+ {
3446
+ ref,
3447
+ className: cn("text-xs text-muted-foreground", className),
3448
+ ...props
3449
+ }
3450
+ );
3451
+ });
3452
+ FormDescription.displayName = "Form.Description";
3453
+ var FormError = React10.forwardRef(
3454
+ ({ className, message, children, ...props }, ref) => {
3455
+ const fieldContext = useFormFieldContextOptional();
3456
+ const errorMessage = message ?? fieldContext?.error;
3457
+ if (!errorMessage && !children) {
3458
+ return null;
3459
+ }
3460
+ return /* @__PURE__ */ jsx(
3461
+ "p",
3462
+ {
3463
+ ref,
3464
+ className: cn("text-xs text-red-500", className),
3465
+ ...props,
3466
+ children: children || errorMessage
3467
+ }
3468
+ );
3469
+ }
3470
+ );
3471
+ FormError.displayName = "Form.Error";
3472
+ var FormFieldWrapper = React10.forwardRef(({ className, label, description, required, error, children, ...props }, ref) => {
3473
+ return /* @__PURE__ */ jsxs("div", { ref, className: cn("space-y-1", className), ...props, children: [
3474
+ label && /* @__PURE__ */ jsx(FormLabel, { required, children: label }),
3475
+ children,
3476
+ description && /* @__PURE__ */ jsx(FormDescription, { children: description }),
3477
+ error && /* @__PURE__ */ jsx(FormError, { message: error })
3478
+ ] });
3479
+ });
3480
+ FormFieldWrapper.displayName = "Form.FieldWrapper";
3481
+ function FieldTooltipIcon({ tooltip }) {
3482
+ return /* @__PURE__ */ jsxs(TooltipRoot, { delayDuration: 200, children: [
3483
+ /* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx(Info, { className: "h-3 w-3 text-primary cursor-help shrink-0" }) }),
3484
+ /* @__PURE__ */ jsx(TooltipContent, { side: "top", size: "md", children: typeof tooltip === "string" ? /* @__PURE__ */ jsx("span", { className: "text-xs", children: tooltip }) : /* @__PURE__ */ jsxs("div", { className: "space-y-0.5", children: [
3485
+ /* @__PURE__ */ jsx("p", { className: "text-xs font-semibold leading-snug", children: tooltip.title }),
3486
+ /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground leading-snug", children: tooltip.description })
3487
+ ] }) })
3488
+ ] });
3489
+ }
3490
+ function applyMask(value, mask) {
3491
+ const digits = value.replace(/\D/g, "");
3492
+ switch (mask) {
3493
+ case "phone":
3494
+ if (digits.length <= 10) {
3495
+ return digits.replace(/(\d{2})(\d{4})(\d{0,4})/, "($1) $2-$3").trim();
3496
+ }
3497
+ return digits.replace(/(\d{2})(\d{5})(\d{0,4})/, "($1) $2-$3").trim();
3498
+ case "cpf":
3499
+ return digits.replace(/(\d{3})(\d)/, "$1.$2").replace(/(\d{3})(\d)/, "$1.$2").replace(/(\d{3})(\d{1,2})$/, "$1-$2");
3500
+ case "cnpj":
3501
+ return digits.replace(/(\d{2})(\d)/, "$1.$2").replace(/(\d{3})(\d)/, "$1.$2").replace(/(\d{3})(\d)/, "$1/$2").replace(/(\d{4})(\d{1,2})$/, "$1-$2");
3502
+ case "cep":
3503
+ return digits.replace(/(\d{5})(\d{0,3})/, "$1-$2");
3504
+ case "money":
3505
+ if (!digits) return "";
3506
+ const cents = parseInt(digits, 10) / 100;
3507
+ return new Intl.NumberFormat("pt-BR", {
3508
+ style: "currency",
3509
+ currency: "BRL"
3510
+ }).format(cents);
3511
+ case "percent":
3512
+ if (!digits) return "";
3513
+ const percent = parseInt(digits, 10) / 100;
3514
+ return `${percent.toFixed(2)}%`;
3515
+ case "plate":
3516
+ const upper = value.toUpperCase().replace(/[^A-Z0-9]/g, "");
3517
+ if (upper.length <= 3) return upper;
3518
+ if (upper.length <= 7) {
3519
+ return upper.replace(/([A-Z]{3})(\d{0,1})([A-Z0-9]{0,1})(\d{0,2})/, "$1-$2$3$4");
3520
+ }
3521
+ return upper.slice(0, 7).replace(/([A-Z]{3})(\d{0,1})([A-Z0-9]{0,1})(\d{0,2})/, "$1-$2$3$4");
3522
+ case "date":
3523
+ return digits.replace(/(\d{2})(\d)/, "$1/$2").replace(/(\d{2})(\d)/, "$1/$2").slice(0, 10);
3524
+ case "time":
3525
+ return digits.replace(/(\d{2})(\d{0,2})/, "$1:$2").slice(0, 5);
3526
+ case "datetime":
3527
+ const dateTime = digits.replace(/(\d{2})(\d)/, "$1/$2").replace(/(\d{2})(\d)/, "$1/$2").replace(/(\d{4})(\d)/, "$1 $2").replace(/(\d{2})(\d{0,2})$/, "$1:$2");
3528
+ return dateTime.slice(0, 16);
3529
+ default:
3530
+ return value;
3531
+ }
3532
+ }
3533
+ function getMaxLength(mask) {
3534
+ switch (mask) {
3535
+ case "phone":
3536
+ return 15;
3537
+ case "cpf":
3538
+ return 14;
3539
+ case "cnpj":
3540
+ return 18;
3541
+ case "cep":
3542
+ return 9;
3543
+ case "plate":
3544
+ return 8;
3545
+ case "date":
3546
+ return 10;
3547
+ case "time":
3548
+ return 5;
3549
+ case "datetime":
3550
+ return 16;
3551
+ default:
3552
+ return void 0;
3553
+ }
3554
+ }
3555
+ function parseValue(displayValue, mask) {
3556
+ if (!displayValue) return void 0;
3557
+ switch (mask) {
3558
+ case "money":
3559
+ const moneyDigits = displayValue.replace(/\D/g, "");
3560
+ return moneyDigits ? parseInt(moneyDigits, 10) / 100 : void 0;
3561
+ case "percent":
3562
+ const percentDigits = displayValue.replace(/\D/g, "");
3563
+ return percentDigits ? parseInt(percentDigits, 10) / 100 : void 0;
3564
+ default:
3565
+ return displayValue || void 0;
3566
+ }
3567
+ }
3568
+ function FormInput({
3569
+ name,
3570
+ label,
3571
+ description,
3572
+ tooltip,
3573
+ required,
3574
+ disabled,
3575
+ className,
3576
+ mask,
3577
+ icon,
3578
+ showPasswordToggle = true,
3579
+ inputSize = "default",
3580
+ hideError = false,
3581
+ type = "text",
3582
+ maxLength,
3583
+ ...inputProps
3584
+ }) {
3585
+ const form = useFormContext();
3586
+ const fieldState = form.getFieldState(name, form.formState);
3587
+ const error = fieldState.error?.message;
3588
+ const getInputType = React10.useCallback(() => {
3589
+ if (["money", "percent", "phone", "cpf", "cnpj", "cep"].includes(mask || "")) {
3590
+ return "tel";
3591
+ }
3592
+ return type;
3593
+ }, [mask, type]);
3594
+ return /* @__PURE__ */ jsx(FormFieldProvider, { name, children: /* @__PURE__ */ jsx(
3595
+ Controller,
3596
+ {
3597
+ control: form.control,
3598
+ name,
3599
+ render: ({ field }) => {
3600
+ const getDisplayValue = () => {
3601
+ if (field.value === void 0 || field.value === null) return "";
3602
+ if (mask) {
3603
+ return applyMask(String(field.value), mask);
3604
+ }
3605
+ return String(field.value);
3606
+ };
3607
+ const handleChange = (e) => {
3608
+ let newValue = e.target.value;
3609
+ if (mask) {
3610
+ newValue = applyMask(newValue, mask);
3611
+ const parsed = parseValue(newValue, mask);
3612
+ field.onChange(parsed);
3613
+ } else {
3614
+ field.onChange(newValue || void 0);
3615
+ }
3616
+ };
3617
+ return /* @__PURE__ */ jsxs("div", { className: cn("space-y-1", className), children: [
3618
+ /* @__PURE__ */ jsx(
3619
+ Input,
3620
+ {
3621
+ ...inputProps,
3622
+ ref: field.ref,
3623
+ name: field.name,
3624
+ value: getDisplayValue(),
3625
+ onChange: handleChange,
3626
+ onBlur: field.onBlur,
3627
+ disabled,
3628
+ type: getInputType(),
3629
+ label,
3630
+ required,
3631
+ error: !!error,
3632
+ icon,
3633
+ inputSize,
3634
+ maxLength: maxLength ?? getMaxLength(mask),
3635
+ labelSuffix: tooltip ? /* @__PURE__ */ jsx(FieldTooltipIcon, { tooltip }) : void 0
3636
+ }
3637
+ ),
3638
+ description && !error && /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground px-1", children: description }),
3639
+ !hideError && error && /* @__PURE__ */ jsx("p", { className: "text-xs text-red-500 px-1", children: error })
3640
+ ] });
3641
+ }
3642
+ }
3643
+ ) });
3644
+ }
3645
+ FormInput.displayName = "Form.Input";
3646
+ var Popover = PopoverPrimitive.Root;
3647
+ var PopoverTrigger = PopoverPrimitive.Trigger;
3648
+ var PopoverContent = React10.forwardRef(({ className, align = "center", sideOffset = 4, ...props }, ref) => /* @__PURE__ */ jsx(PopoverPrimitive.Portal, { children: /* @__PURE__ */ jsx(
3649
+ PopoverPrimitive.Content,
3650
+ {
3651
+ ref,
3652
+ align,
3653
+ sideOffset,
3654
+ className: cn(
3655
+ "z-50 w-auto rounded-md border bg-popover p-4 text-popover-foreground shadow-lg outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
3656
+ className
3657
+ ),
3658
+ ...props
3659
+ }
3660
+ ) }));
3661
+ PopoverContent.displayName = PopoverPrimitive.Content.displayName;
3662
+ function FormSelect({
3663
+ name,
3664
+ label,
3665
+ description,
3666
+ tooltip,
3667
+ required,
3668
+ disabled,
3669
+ className,
3670
+ options,
3671
+ placeholder = "Selecione...",
3672
+ hideError = false,
3673
+ emptyText = "Nenhuma op\xE7\xE3o dispon\xEDvel",
3674
+ loading = false,
3675
+ searchable = false,
3676
+ onSearch,
3677
+ onLoadMore,
3678
+ hasMore,
3679
+ searchPlaceholder = "Buscar...",
3680
+ dropdownPosition = "bottom"
3681
+ }) {
3682
+ const form = useFormContext();
3683
+ const fieldState = form.getFieldState(name, form.formState);
3684
+ const error = fieldState.error?.message;
3685
+ return /* @__PURE__ */ jsx(FormFieldProvider, { name, children: /* @__PURE__ */ jsx(
3686
+ Controller,
3687
+ {
3688
+ control: form.control,
3689
+ name,
3690
+ render: ({ field }) => /* @__PURE__ */ jsxs("div", { className: cn("space-y-1", className), children: [
3691
+ /* @__PURE__ */ jsx(
3692
+ SelectDropdown,
3693
+ {
3694
+ options,
3695
+ value: field.value,
3696
+ onChange: (v) => field.onChange(v || void 0),
3697
+ disabled,
3698
+ label,
3699
+ tooltip,
3441
3700
  required,
3442
3701
  error: !!error,
3443
- icon,
3444
- selectSize,
3445
3702
  placeholder,
3446
- children: loading ? /* @__PURE__ */ jsx(SelectItem, { value: "__loading__", disabled: true, children: "Carregando..." }) : options.length === 0 ? /* @__PURE__ */ jsx(SelectItem, { value: "__empty__", disabled: true, children: emptyText }) : options.map((option) => /* @__PURE__ */ jsx(
3447
- SelectItem,
3448
- {
3449
- value: option.value,
3450
- disabled: option.disabled,
3451
- children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
3452
- option.icon && /* @__PURE__ */ jsx(option.icon, { className: "h-4 w-4 text-muted-foreground" }),
3453
- /* @__PURE__ */ jsxs("div", { children: [
3454
- /* @__PURE__ */ jsx("span", { children: option.label }),
3455
- option.description && /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground ml-2", children: option.description })
3456
- ] })
3457
- ] })
3458
- },
3459
- option.value
3460
- ))
3703
+ searchable,
3704
+ onSearch,
3705
+ onLoadMore,
3706
+ hasMore,
3707
+ loading,
3708
+ emptyText,
3709
+ searchPlaceholder,
3710
+ dropdownPosition
3461
3711
  }
3462
3712
  ),
3463
3713
  description && !error && /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground px-1", children: description }),
@@ -3466,229 +3716,554 @@ function FormSelect({
3466
3716
  }
3467
3717
  ) });
3468
3718
  }
3469
- FormSelect.displayName = "Form.Select";
3470
- function CardSelect({
3719
+ FormSelect.displayName = "Form.Select";
3720
+ function SelectDropdown({
3721
+ options,
3722
+ value,
3723
+ onChange,
3724
+ disabled,
3725
+ label,
3726
+ tooltip,
3727
+ required,
3728
+ error,
3729
+ placeholder = "Selecione...",
3730
+ searchable,
3731
+ onSearch,
3732
+ onLoadMore,
3733
+ hasMore,
3734
+ loading,
3735
+ emptyText = "Nenhuma op\xE7\xE3o dispon\xEDvel",
3736
+ searchPlaceholder = "Buscar...",
3737
+ dropdownPosition = "bottom"
3738
+ }) {
3739
+ const [open, setOpen] = React10.useState(false);
3740
+ const [search, setSearch] = React10.useState("");
3741
+ const selected = options.find((o) => o.value === value);
3742
+ const contentRef = React10.useRef(null);
3743
+ const listRef = React10.useRef(null);
3744
+ const searchRef = React10.useRef(null);
3745
+ const filteredOptions = React10.useMemo(() => {
3746
+ if (!searchable || onSearch || !search) return options;
3747
+ const q = search.toLowerCase();
3748
+ return options.filter(
3749
+ (o) => o.label.toLowerCase().includes(q) || o.description?.toLowerCase().includes(q)
3750
+ );
3751
+ }, [options, search, searchable, onSearch]);
3752
+ const handleSearch = React10.useCallback(
3753
+ (val) => {
3754
+ setSearch(val);
3755
+ if (onSearch) onSearch(val);
3756
+ },
3757
+ [onSearch]
3758
+ );
3759
+ const handleOpenChange = React10.useCallback(
3760
+ (nextOpen) => {
3761
+ setOpen(nextOpen);
3762
+ if (!nextOpen) {
3763
+ setSearch("");
3764
+ if (onSearch) onSearch("");
3765
+ }
3766
+ },
3767
+ [onSearch]
3768
+ );
3769
+ const handleScroll = React10.useCallback(() => {
3770
+ if (!onLoadMore || !hasMore || loading) return;
3771
+ const el = listRef.current;
3772
+ if (!el) return;
3773
+ const { scrollTop, scrollHeight, clientHeight } = el;
3774
+ if (scrollHeight - scrollTop - clientHeight < 80) {
3775
+ onLoadMore();
3776
+ }
3777
+ }, [onLoadMore, hasMore, loading]);
3778
+ React10.useEffect(() => {
3779
+ if (!open) return;
3780
+ let cancelled = false;
3781
+ let removeHandler;
3782
+ requestAnimationFrame(() => {
3783
+ if (cancelled) return;
3784
+ const content = contentRef.current;
3785
+ const list = listRef.current;
3786
+ if (!content || !list) return;
3787
+ const handleWheel = (e) => {
3788
+ if (!content.contains(e.target)) return;
3789
+ const { scrollTop, scrollHeight, clientHeight } = list;
3790
+ if (scrollHeight <= clientHeight) return;
3791
+ e.preventDefault();
3792
+ e.stopPropagation();
3793
+ list.scrollTop = Math.max(0, Math.min(scrollTop + e.deltaY, scrollHeight - clientHeight));
3794
+ };
3795
+ document.addEventListener("wheel", handleWheel, { passive: false, capture: true });
3796
+ removeHandler = () => document.removeEventListener("wheel", handleWheel, { capture: true });
3797
+ });
3798
+ return () => {
3799
+ cancelled = true;
3800
+ removeHandler?.();
3801
+ };
3802
+ }, [open]);
3803
+ const side = dropdownPosition === "top" ? "top" : "bottom";
3804
+ const avoidCollisions = dropdownPosition === "auto";
3805
+ return /* @__PURE__ */ jsxs(Popover, { open, onOpenChange: handleOpenChange, children: [
3806
+ /* @__PURE__ */ jsxs("div", { className: "relative", children: [
3807
+ /* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(
3808
+ "button",
3809
+ {
3810
+ type: "button",
3811
+ disabled,
3812
+ className: cn(
3813
+ "flex w-full items-center justify-between rounded-md border-2 bg-background px-3 text-sm transition-colors",
3814
+ "focus:outline-none focus:border-primary",
3815
+ "disabled:cursor-not-allowed disabled:opacity-50",
3816
+ label ? "h-12 pt-4 pb-2" : "h-9 py-2",
3817
+ error ? "border-red-500" : "border-border",
3818
+ open && !error && "border-primary"
3819
+ ),
3820
+ children: [
3821
+ selected ? /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 truncate", children: [
3822
+ selected.icon && /* @__PURE__ */ jsx(selected.icon, { className: "h-4 w-4 shrink-0 text-muted-foreground" }),
3823
+ /* @__PURE__ */ jsx("span", { className: "truncate", children: selected.label })
3824
+ ] }) : /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: placeholder }),
3825
+ /* @__PURE__ */ jsx(ChevronDown, { className: cn("h-4 w-4 shrink-0 opacity-50 transition-transform", open && "rotate-180") })
3826
+ ]
3827
+ }
3828
+ ) }),
3829
+ label && /* @__PURE__ */ jsxs(
3830
+ "label",
3831
+ {
3832
+ className: cn(
3833
+ "absolute left-3 top-[-6px] text-xs font-medium bg-background px-1 inline-flex items-center gap-1",
3834
+ tooltip ? "pointer-events-auto" : "pointer-events-none",
3835
+ error ? "text-red-500" : "text-foreground"
3836
+ ),
3837
+ children: [
3838
+ label,
3839
+ required && /* @__PURE__ */ jsx("span", { className: "text-red-500 ml-0.5", children: "*" }),
3840
+ tooltip && /* @__PURE__ */ jsx(FieldTooltipIcon, { tooltip })
3841
+ ]
3842
+ }
3843
+ )
3844
+ ] }),
3845
+ /* @__PURE__ */ jsxs(
3846
+ PopoverContent,
3847
+ {
3848
+ ref: contentRef,
3849
+ side,
3850
+ align: "start",
3851
+ sideOffset: 4,
3852
+ avoidCollisions,
3853
+ collisionPadding: 16,
3854
+ className: "p-0 overflow-hidden flex flex-col",
3855
+ style: {
3856
+ minWidth: "var(--radix-popover-trigger-width)",
3857
+ width: "fit-content",
3858
+ maxWidth: "var(--radix-popover-content-available-width, 100%)",
3859
+ maxHeight: "min(340px, var(--radix-popover-content-available-height, 340px))"
3860
+ },
3861
+ onOpenAutoFocus: (e) => {
3862
+ e.preventDefault();
3863
+ if (searchable) {
3864
+ setTimeout(() => searchRef.current?.focus(), 0);
3865
+ }
3866
+ },
3867
+ children: [
3868
+ searchable && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 px-3 py-2 border-b border-border", children: [
3869
+ /* @__PURE__ */ jsx(Search, { className: "h-4 w-4 shrink-0 text-muted-foreground" }),
3870
+ /* @__PURE__ */ jsx(
3871
+ "input",
3872
+ {
3873
+ ref: searchRef,
3874
+ type: "text",
3875
+ value: search,
3876
+ onChange: (e) => handleSearch(e.target.value),
3877
+ placeholder: searchPlaceholder,
3878
+ className: "flex-1 bg-transparent text-sm outline-none placeholder:text-muted-foreground"
3879
+ }
3880
+ )
3881
+ ] }),
3882
+ /* @__PURE__ */ jsx(
3883
+ "div",
3884
+ {
3885
+ ref: listRef,
3886
+ className: "overflow-y-auto overscroll-contain min-h-0 flex-1",
3887
+ onScroll: handleScroll,
3888
+ children: /* @__PURE__ */ jsxs("div", { className: "p-1", children: [
3889
+ filteredOptions.length === 0 && !loading ? /* @__PURE__ */ jsx("p", { className: "py-4 text-center text-sm text-muted-foreground", children: emptyText }) : filteredOptions.map((option) => {
3890
+ const isSelected = value === option.value;
3891
+ const isDisabled = option.disabled || disabled;
3892
+ return /* @__PURE__ */ jsxs(
3893
+ "button",
3894
+ {
3895
+ type: "button",
3896
+ disabled: isDisabled,
3897
+ onClick: () => {
3898
+ onChange(option.value);
3899
+ setOpen(false);
3900
+ },
3901
+ className: cn(
3902
+ "flex w-full items-center gap-2 rounded-sm px-2 text-left text-sm outline-none",
3903
+ "cursor-pointer hover:bg-accent hover:text-accent-foreground",
3904
+ isSelected && "bg-accent/50",
3905
+ isDisabled && "pointer-events-none opacity-50",
3906
+ option.description ? "py-2" : "py-1.5"
3907
+ ),
3908
+ children: [
3909
+ option.icon && /* @__PURE__ */ jsx("div", { className: "flex h-6 w-6 shrink-0 items-center justify-center rounded-full bg-primary/10", children: /* @__PURE__ */ jsx(option.icon, { className: "h-3 w-3 text-primary" }) }),
3910
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
3911
+ /* @__PURE__ */ jsx("span", { className: "block leading-tight", children: option.label }),
3912
+ option.description && /* @__PURE__ */ jsx("span", { className: "block text-xs leading-tight text-muted-foreground", children: option.description })
3913
+ ] }),
3914
+ isSelected && /* @__PURE__ */ jsx(Check, { className: "h-4 w-4 shrink-0 text-primary" })
3915
+ ]
3916
+ },
3917
+ option.value
3918
+ );
3919
+ }),
3920
+ loading && /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center py-3", children: /* @__PURE__ */ jsx(Loader2, { className: "h-4 w-4 animate-spin text-muted-foreground" }) })
3921
+ ] })
3922
+ }
3923
+ )
3924
+ ]
3925
+ }
3926
+ )
3927
+ ] });
3928
+ }
3929
+ function FormMultiSelect({
3930
+ name,
3931
+ label,
3932
+ description,
3933
+ tooltip,
3934
+ required,
3935
+ disabled,
3936
+ className,
3937
+ options,
3938
+ placeholder = "Selecione...",
3939
+ hideError = false,
3940
+ emptyText = "Nenhuma op\xE7\xE3o dispon\xEDvel",
3941
+ loading = false,
3942
+ searchable = false,
3943
+ searchPlaceholder = "Buscar...",
3944
+ clearable = true,
3945
+ maxVisibleChips = 3,
3946
+ dropdownPosition = "bottom"
3947
+ }) {
3948
+ const form = useFormContext();
3949
+ const fieldState = form.getFieldState(name, form.formState);
3950
+ const error = fieldState.error?.message;
3951
+ return /* @__PURE__ */ jsx(FormFieldProvider, { name, children: /* @__PURE__ */ jsx(
3952
+ Controller,
3953
+ {
3954
+ control: form.control,
3955
+ name,
3956
+ render: ({ field }) => {
3957
+ const values = Array.isArray(field.value) ? field.value : [];
3958
+ return /* @__PURE__ */ jsxs("div", { className: cn("space-y-1", className), children: [
3959
+ /* @__PURE__ */ jsx(
3960
+ MultiSelectDropdown,
3961
+ {
3962
+ options,
3963
+ value: values,
3964
+ onChange: (v) => field.onChange(v),
3965
+ disabled,
3966
+ label,
3967
+ tooltip,
3968
+ required,
3969
+ error: !!error,
3970
+ placeholder,
3971
+ searchable,
3972
+ loading,
3973
+ emptyText,
3974
+ searchPlaceholder,
3975
+ clearable,
3976
+ maxVisibleChips,
3977
+ dropdownPosition
3978
+ }
3979
+ ),
3980
+ description && !error && /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground px-1", children: description }),
3981
+ !hideError && error && /* @__PURE__ */ jsx("p", { className: "text-xs text-red-500 px-1", children: error })
3982
+ ] });
3983
+ }
3984
+ }
3985
+ ) });
3986
+ }
3987
+ FormMultiSelect.displayName = "Form.MultiSelect";
3988
+ function MultiSelectDropdown({
3471
3989
  options,
3472
3990
  value,
3473
3991
  onChange,
3474
3992
  disabled,
3475
3993
  label,
3994
+ tooltip,
3476
3995
  required,
3477
3996
  error,
3478
3997
  placeholder = "Selecione...",
3479
3998
  searchable,
3480
- onSearch,
3481
- onLoadMore,
3482
- hasMore,
3483
3999
  loading,
3484
4000
  emptyText = "Nenhuma op\xE7\xE3o dispon\xEDvel",
3485
- searchPlaceholder = "Buscar..."
4001
+ searchPlaceholder = "Buscar...",
4002
+ clearable = true,
4003
+ maxVisibleChips = 3,
4004
+ dropdownPosition = "bottom"
3486
4005
  }) {
3487
4006
  const [open, setOpen] = React10.useState(false);
3488
4007
  const [search, setSearch] = React10.useState("");
3489
- const selected = options.find((o) => o.value === value);
3490
- const containerRef = React10.useRef(null);
3491
- const dropdownRef = React10.useRef(null);
4008
+ const [computedMax, setComputedMax] = React10.useState(maxVisibleChips);
4009
+ const [prevValueKey, setPrevValueKey] = React10.useState(() => value.join(","));
4010
+ const contentRef = React10.useRef(null);
3492
4011
  const listRef = React10.useRef(null);
3493
4012
  const searchRef = React10.useRef(null);
3494
- const [dropdownPos, setDropdownPos] = React10.useState({});
4013
+ const chipsRef = React10.useRef(null);
4014
+ const selectedOptions = React10.useMemo(
4015
+ () => options.filter((o) => value.includes(o.value)),
4016
+ [options, value]
4017
+ );
4018
+ const valueKey = value.join(",");
4019
+ if (valueKey !== prevValueKey) {
4020
+ setPrevValueKey(valueKey);
4021
+ setComputedMax(maxVisibleChips);
4022
+ }
4023
+ React10.useLayoutEffect(() => {
4024
+ const container = chipsRef.current;
4025
+ if (!container || selectedOptions.length === 0) return;
4026
+ const containerRect = container.getBoundingClientRect();
4027
+ const containerWidth = containerRect.width;
4028
+ const chips = container.querySelectorAll("[data-chip]");
4029
+ if (chips.length === 0 || containerWidth === 0) return;
4030
+ const gap = 4;
4031
+ const buffer = 4;
4032
+ const badge = container.querySelector("[data-badge]");
4033
+ const badgeWidth = badge ? badge.getBoundingClientRect().width : 28;
4034
+ const availableWidth = containerWidth - buffer;
4035
+ let usedWidth = 0;
4036
+ let fits = 0;
4037
+ for (let i = 0; i < chips.length; i++) {
4038
+ const chipWidth = chips[i].getBoundingClientRect().width;
4039
+ const widthWithGap = fits > 0 ? chipWidth + gap : chipWidth;
4040
+ const remaining = selectedOptions.length - (fits + 1);
4041
+ const needsBadge = remaining > 0;
4042
+ const badgeSpace = needsBadge ? badgeWidth + gap : 0;
4043
+ if (usedWidth + widthWithGap + badgeSpace > availableWidth && fits > 0) {
4044
+ break;
4045
+ }
4046
+ usedWidth += widthWithGap;
4047
+ fits++;
4048
+ }
4049
+ const newMax = Math.max(1, fits);
4050
+ if (newMax !== computedMax) {
4051
+ setComputedMax(newMax);
4052
+ }
4053
+ });
3495
4054
  const filteredOptions = React10.useMemo(() => {
3496
- if (!searchable || onSearch || !search) return options;
4055
+ if (!searchable || !search) return options;
3497
4056
  const q = search.toLowerCase();
3498
4057
  return options.filter(
3499
4058
  (o) => o.label.toLowerCase().includes(q) || o.description?.toLowerCase().includes(q)
3500
4059
  );
3501
- }, [options, search, searchable, onSearch]);
3502
- const handleSearch = React10.useCallback(
3503
- (value2) => {
3504
- setSearch(value2);
3505
- if (onSearch) onSearch(value2);
4060
+ }, [options, search, searchable]);
4061
+ const toggleOption = React10.useCallback(
4062
+ (optionValue) => {
4063
+ if (value.includes(optionValue)) {
4064
+ onChange(value.filter((v) => v !== optionValue));
4065
+ } else {
4066
+ onChange([...value, optionValue]);
4067
+ }
3506
4068
  },
3507
- [onSearch]
4069
+ [value, onChange]
3508
4070
  );
3509
- React10.useEffect(() => {
3510
- if (!open || !containerRef.current) return;
3511
- const updatePosition = () => {
3512
- const rect = containerRef.current.getBoundingClientRect();
3513
- const spaceBelow = window.innerHeight - rect.bottom;
3514
- const estimatedHeight = 340;
3515
- const showAbove = spaceBelow < estimatedHeight && rect.top > spaceBelow;
3516
- setDropdownPos({
3517
- position: "fixed",
3518
- left: rect.left,
3519
- width: rect.width,
3520
- zIndex: 9999,
3521
- ...showAbove ? { bottom: window.innerHeight - rect.top + 4 } : { top: rect.bottom + 4 }
3522
- });
3523
- };
3524
- updatePosition();
3525
- window.addEventListener("scroll", updatePosition, true);
3526
- window.addEventListener("resize", updatePosition);
3527
- return () => {
3528
- window.removeEventListener("scroll", updatePosition, true);
3529
- window.removeEventListener("resize", updatePosition);
3530
- };
3531
- }, [open]);
3532
- React10.useEffect(() => {
3533
- if (!open) return;
3534
- const handleClickOutside = (e) => {
3535
- const target = e.target;
3536
- if (containerRef.current && !containerRef.current.contains(target) && (!dropdownRef.current || !dropdownRef.current.contains(target))) {
3537
- setOpen(false);
3538
- }
3539
- };
3540
- document.addEventListener("mousedown", handleClickOutside);
3541
- return () => document.removeEventListener("mousedown", handleClickOutside);
3542
- }, [open]);
3543
- React10.useEffect(() => {
3544
- if (!open) return;
3545
- const handleEscape = (e) => {
3546
- if (e.key === "Escape") setOpen(false);
3547
- };
3548
- document.addEventListener("keydown", handleEscape);
3549
- return () => document.removeEventListener("keydown", handleEscape);
3550
- }, [open]);
3551
- React10.useEffect(() => {
3552
- if (!open) {
4071
+ const handleClearAll = React10.useCallback(
4072
+ (e) => {
4073
+ e.stopPropagation();
4074
+ onChange([]);
4075
+ },
4076
+ [onChange]
4077
+ );
4078
+ const handleRemoveChip = React10.useCallback(
4079
+ (e, optionValue) => {
4080
+ e.stopPropagation();
4081
+ onChange(value.filter((v) => v !== optionValue));
4082
+ },
4083
+ [value, onChange]
4084
+ );
4085
+ const handleOpenChange = React10.useCallback((nextOpen) => {
4086
+ setOpen(nextOpen);
4087
+ if (!nextOpen) {
3553
4088
  setSearch("");
3554
- if (onSearch) onSearch("");
3555
- }
3556
- }, [open, onSearch]);
3557
- React10.useEffect(() => {
3558
- if (open && searchable) {
3559
- setTimeout(() => searchRef.current?.focus(), 0);
3560
4089
  }
3561
- }, [open, searchable]);
4090
+ }, []);
3562
4091
  React10.useEffect(() => {
3563
- const el = listRef.current;
3564
- if (!el || !open) return;
3565
- const handleWheel = (e) => {
3566
- const { scrollTop, scrollHeight, clientHeight } = el;
3567
- const isScrollable = scrollHeight > clientHeight;
3568
- if (!isScrollable) return;
3569
- e.preventDefault();
3570
- e.stopPropagation();
3571
- el.scrollTop = Math.max(
3572
- 0,
3573
- Math.min(scrollTop + e.deltaY, scrollHeight - clientHeight)
3574
- );
4092
+ if (!open) return;
4093
+ let cancelled = false;
4094
+ let removeHandler;
4095
+ requestAnimationFrame(() => {
4096
+ if (cancelled) return;
4097
+ const content = contentRef.current;
4098
+ const list = listRef.current;
4099
+ if (!content || !list) return;
4100
+ const handleWheel = (e) => {
4101
+ if (!content.contains(e.target)) return;
4102
+ const { scrollTop, scrollHeight, clientHeight } = list;
4103
+ if (scrollHeight <= clientHeight) return;
4104
+ e.preventDefault();
4105
+ e.stopPropagation();
4106
+ list.scrollTop = Math.max(0, Math.min(scrollTop + e.deltaY, scrollHeight - clientHeight));
4107
+ };
4108
+ document.addEventListener("wheel", handleWheel, { passive: false, capture: true });
4109
+ removeHandler = () => document.removeEventListener("wheel", handleWheel, { capture: true });
4110
+ });
4111
+ return () => {
4112
+ cancelled = true;
4113
+ removeHandler?.();
3575
4114
  };
3576
- el.addEventListener("wheel", handleWheel, { passive: false, capture: true });
3577
- return () => el.removeEventListener("wheel", handleWheel, { capture: true });
3578
- }, [open, filteredOptions]);
3579
- const handleScroll = React10.useCallback(() => {
3580
- if (!onLoadMore || !hasMore || loading) return;
3581
- const el = listRef.current;
3582
- if (!el) return;
3583
- const { scrollTop, scrollHeight, clientHeight } = el;
3584
- if (scrollHeight - scrollTop - clientHeight < 80) {
3585
- onLoadMore();
3586
- }
3587
- }, [onLoadMore, hasMore, loading]);
3588
- return /* @__PURE__ */ jsxs("div", { ref: containerRef, className: "relative", children: [
3589
- /* @__PURE__ */ jsxs(
3590
- "button",
3591
- {
3592
- type: "button",
3593
- disabled,
3594
- onClick: () => setOpen((prev) => !prev),
3595
- className: cn(
3596
- "flex w-full items-center justify-between rounded-md border-2 bg-background px-3 text-sm transition-colors",
3597
- "focus:outline-none focus:border-primary",
3598
- "disabled:cursor-not-allowed disabled:opacity-50",
3599
- label ? "h-12 pt-4 pb-2" : "h-9 py-2",
3600
- error ? "border-red-500" : "border-border",
3601
- open && !error && "border-primary"
3602
- ),
3603
- children: [
3604
- selected ? /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 truncate", children: [
3605
- selected.icon && /* @__PURE__ */ jsx(selected.icon, { className: "h-4 w-4 shrink-0 text-muted-foreground" }),
3606
- /* @__PURE__ */ jsx("span", { className: "truncate", children: selected.label })
3607
- ] }) : /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: placeholder }),
3608
- /* @__PURE__ */ jsx(ChevronDown, { className: cn("h-4 w-4 shrink-0 opacity-50 transition-transform", open && "rotate-180") })
3609
- ]
3610
- }
3611
- ),
3612
- label && /* @__PURE__ */ jsxs(
3613
- "label",
3614
- {
3615
- className: cn(
3616
- "absolute left-3 top-[-6px] text-xs font-medium bg-background px-1 pointer-events-none",
3617
- error ? "text-red-500" : "text-foreground"
3618
- ),
3619
- children: [
3620
- label,
3621
- required && /* @__PURE__ */ jsx("span", { className: "text-red-500 ml-0.5", children: "*" })
3622
- ]
3623
- }
3624
- ),
3625
- open && createPortal(
3626
- /* @__PURE__ */ jsxs(
3627
- "div",
4115
+ }, [open]);
4116
+ const side = dropdownPosition === "top" ? "top" : "bottom";
4117
+ const avoidCollisions = dropdownPosition === "auto";
4118
+ const visibleChips = selectedOptions.slice(0, computedMax);
4119
+ const overflowCount = selectedOptions.length - computedMax;
4120
+ return /* @__PURE__ */ jsxs(Popover, { open, onOpenChange: handleOpenChange, children: [
4121
+ /* @__PURE__ */ jsxs("div", { className: "relative", children: [
4122
+ /* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(
4123
+ "button",
3628
4124
  {
3629
- ref: dropdownRef,
3630
- style: dropdownPos,
3631
- className: "rounded-md border border-border bg-popover shadow-md overflow-hidden animate-in fade-in-0 zoom-in-95 slide-in-from-top-2 duration-150",
4125
+ type: "button",
4126
+ disabled,
4127
+ className: cn(
4128
+ "flex w-full items-center justify-between rounded-md border-2 bg-background px-3 text-sm transition-colors",
4129
+ "focus:outline-none focus:border-primary",
4130
+ "disabled:cursor-not-allowed disabled:opacity-50",
4131
+ label ? "min-h-[3rem] pt-4 pb-1.5" : "min-h-[2.25rem] py-1.5",
4132
+ error ? "border-red-500" : "border-border",
4133
+ open && !error && "border-primary"
4134
+ ),
3632
4135
  children: [
3633
- searchable && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 px-3 py-2 border-b border-border", children: [
3634
- /* @__PURE__ */ jsx(Search, { className: "h-4 w-4 shrink-0 text-muted-foreground" }),
3635
- /* @__PURE__ */ jsx(
3636
- "input",
4136
+ /* @__PURE__ */ jsx("div", { ref: chipsRef, className: "flex gap-1 items-center flex-1 overflow-hidden", children: selectedOptions.length > 0 ? /* @__PURE__ */ jsxs(Fragment, { children: [
4137
+ visibleChips.map((option) => /* @__PURE__ */ jsxs(
4138
+ "span",
4139
+ {
4140
+ "data-chip": true,
4141
+ className: "inline-flex items-center gap-1 max-w-[150px] shrink-0 rounded-sm bg-primary/10 px-1.5 py-0.5 text-xs font-medium text-primary",
4142
+ children: [
4143
+ option.icon && /* @__PURE__ */ jsx(option.icon, { className: "h-3 w-3 shrink-0" }),
4144
+ /* @__PURE__ */ jsx("span", { className: "truncate", children: option.label }),
4145
+ /* @__PURE__ */ jsx(
4146
+ X,
4147
+ {
4148
+ className: "h-3 w-3 shrink-0 cursor-pointer hover:text-destructive",
4149
+ onClick: (e) => handleRemoveChip(e, option.value)
4150
+ }
4151
+ )
4152
+ ]
4153
+ },
4154
+ option.value
4155
+ )),
4156
+ overflowCount > 0 && /* @__PURE__ */ jsxs("span", { "data-badge": true, className: "inline-flex items-center justify-center h-5 min-w-5 px-1.5 shrink-0 rounded-full bg-gray-100 text-gray-500 text-[10px] font-medium", children: [
4157
+ "+",
4158
+ overflowCount
4159
+ ] })
4160
+ ] }) : /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: placeholder }) }),
4161
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1 ml-1 shrink-0", children: [
4162
+ clearable && selectedOptions.length > 0 && /* @__PURE__ */ jsx(
4163
+ X,
3637
4164
  {
3638
- ref: searchRef,
3639
- type: "text",
3640
- value: search,
3641
- onChange: (e) => handleSearch(e.target.value),
3642
- placeholder: searchPlaceholder,
3643
- className: "flex-1 bg-transparent text-sm outline-none placeholder:text-muted-foreground"
4165
+ className: "h-4 w-4 text-muted-foreground hover:text-foreground cursor-pointer",
4166
+ onClick: handleClearAll
3644
4167
  }
3645
- )
3646
- ] }),
4168
+ ),
4169
+ /* @__PURE__ */ jsx(ChevronDown, { className: cn("h-4 w-4 opacity-50 transition-transform", open && "rotate-180") })
4170
+ ] })
4171
+ ]
4172
+ }
4173
+ ) }),
4174
+ label && /* @__PURE__ */ jsxs(
4175
+ "label",
4176
+ {
4177
+ className: cn(
4178
+ "absolute left-3 top-[-6px] text-xs font-medium bg-background px-1 inline-flex items-center gap-1",
4179
+ tooltip ? "pointer-events-auto" : "pointer-events-none",
4180
+ error ? "text-red-500" : "text-foreground"
4181
+ ),
4182
+ children: [
4183
+ label,
4184
+ required && /* @__PURE__ */ jsx("span", { className: "text-red-500 ml-0.5", children: "*" }),
4185
+ tooltip && /* @__PURE__ */ jsx(FieldTooltipIcon, { tooltip })
4186
+ ]
4187
+ }
4188
+ )
4189
+ ] }),
4190
+ /* @__PURE__ */ jsxs(
4191
+ PopoverContent,
4192
+ {
4193
+ ref: contentRef,
4194
+ side,
4195
+ align: "start",
4196
+ sideOffset: 4,
4197
+ avoidCollisions,
4198
+ collisionPadding: 16,
4199
+ className: "p-0 overflow-hidden flex flex-col",
4200
+ style: {
4201
+ minWidth: "var(--radix-popover-trigger-width)",
4202
+ width: "fit-content",
4203
+ maxWidth: "var(--radix-popover-content-available-width, 100%)",
4204
+ maxHeight: "min(340px, var(--radix-popover-content-available-height, 340px))"
4205
+ },
4206
+ onOpenAutoFocus: (e) => {
4207
+ e.preventDefault();
4208
+ if (searchable) {
4209
+ setTimeout(() => searchRef.current?.focus(), 0);
4210
+ }
4211
+ },
4212
+ children: [
4213
+ searchable && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 px-3 py-2 border-b border-border", children: [
4214
+ /* @__PURE__ */ jsx(Search, { className: "h-4 w-4 shrink-0 text-muted-foreground" }),
3647
4215
  /* @__PURE__ */ jsx(
3648
- "div",
4216
+ "input",
3649
4217
  {
3650
- ref: listRef,
3651
- className: "overflow-y-auto overscroll-contain max-h-[300px]",
3652
- onScroll: handleScroll,
3653
- children: /* @__PURE__ */ jsxs("div", { className: "divide-y divide-border/50 p-2", children: [
3654
- filteredOptions.length === 0 && !loading ? /* @__PURE__ */ jsx("p", { className: "py-4 text-center text-sm text-muted-foreground", children: emptyText }) : filteredOptions.map((option) => {
3655
- const isSelected = value === option.value;
3656
- const isDisabled = option.disabled || disabled;
3657
- return /* @__PURE__ */ jsxs(
3658
- "button",
3659
- {
3660
- type: "button",
3661
- disabled: isDisabled,
3662
- onClick: () => {
3663
- onChange(option.value);
3664
- setOpen(false);
3665
- },
3666
- className: cn(
3667
- "flex w-full items-center gap-3 p-3 text-left transition-all",
3668
- "cursor-pointer hover:bg-accent",
3669
- isSelected && "bg-primary/5",
3670
- isDisabled && "cursor-not-allowed opacity-50 hover:bg-transparent"
3671
- ),
3672
- children: [
3673
- option.icon && /* @__PURE__ */ jsx("div", { className: "flex h-7 w-7 shrink-0 items-center justify-center rounded-full bg-primary/10", children: /* @__PURE__ */ jsx(option.icon, { className: "h-3.5 w-3.5 text-primary" }) }),
3674
- /* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
3675
- /* @__PURE__ */ jsx("p", { className: "text-sm font-medium leading-tight", children: option.label }),
3676
- option.description && /* @__PURE__ */ jsx("p", { className: "mt-0.5 text-xs leading-tight text-muted-foreground", children: option.description })
3677
- ] }),
3678
- isSelected && /* @__PURE__ */ jsx("span", { className: "flex h-5 w-5 shrink-0 items-center justify-center rounded-full bg-primary text-primary-foreground", children: /* @__PURE__ */ jsx(Check, { className: "h-3 w-3" }) })
3679
- ]
3680
- },
3681
- option.value
3682
- );
3683
- }),
3684
- loading && /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center py-3", children: /* @__PURE__ */ jsx(Loader2, { className: "h-4 w-4 animate-spin text-muted-foreground" }) })
3685
- ] })
4218
+ ref: searchRef,
4219
+ type: "text",
4220
+ value: search,
4221
+ onChange: (e) => setSearch(e.target.value),
4222
+ placeholder: searchPlaceholder,
4223
+ className: "flex-1 bg-transparent text-sm outline-none placeholder:text-muted-foreground"
3686
4224
  }
3687
4225
  )
3688
- ]
3689
- }
3690
- ),
3691
- document.body
4226
+ ] }),
4227
+ /* @__PURE__ */ jsx(
4228
+ "div",
4229
+ {
4230
+ ref: listRef,
4231
+ className: "overflow-y-auto overscroll-contain min-h-0 flex-1",
4232
+ children: /* @__PURE__ */ jsxs("div", { className: "p-1", children: [
4233
+ filteredOptions.length === 0 && !loading ? /* @__PURE__ */ jsx("p", { className: "py-4 text-center text-sm text-muted-foreground", children: emptyText }) : filteredOptions.map((option) => {
4234
+ const isSelected = value.includes(option.value);
4235
+ const isDisabled = option.disabled || disabled;
4236
+ return /* @__PURE__ */ jsxs(
4237
+ "button",
4238
+ {
4239
+ type: "button",
4240
+ disabled: isDisabled,
4241
+ onClick: () => toggleOption(option.value),
4242
+ className: cn(
4243
+ "flex w-full items-center gap-2 rounded-sm px-2 text-left text-sm outline-none",
4244
+ "cursor-pointer hover:bg-accent hover:text-accent-foreground",
4245
+ isSelected && "bg-accent/50",
4246
+ isDisabled && "pointer-events-none opacity-50",
4247
+ option.description ? "py-2" : "py-1.5"
4248
+ ),
4249
+ children: [
4250
+ option.icon && /* @__PURE__ */ jsx("div", { className: "flex h-6 w-6 shrink-0 items-center justify-center rounded-full bg-primary/10", children: /* @__PURE__ */ jsx(option.icon, { className: "h-3 w-3 text-primary" }) }),
4251
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
4252
+ /* @__PURE__ */ jsx("span", { className: "block leading-tight", children: option.label }),
4253
+ option.description && /* @__PURE__ */ jsx("span", { className: "block text-xs leading-tight text-muted-foreground", children: option.description })
4254
+ ] }),
4255
+ isSelected && /* @__PURE__ */ jsx(Check, { className: "h-4 w-4 shrink-0 text-primary" })
4256
+ ]
4257
+ },
4258
+ option.value
4259
+ );
4260
+ }),
4261
+ loading && /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center py-3", children: /* @__PURE__ */ jsx(Loader2, { className: "h-4 w-4 animate-spin text-muted-foreground" }) })
4262
+ ] })
4263
+ }
4264
+ )
4265
+ ]
4266
+ }
3692
4267
  )
3693
4268
  ] });
3694
4269
  }
@@ -3956,107 +4531,41 @@ function FormRadioGroup({
3956
4531
  id: optionId,
3957
4532
  value: option.value,
3958
4533
  disabled: option.disabled || disabled,
3959
- className: cn(
3960
- "aspect-square h-4 w-4 rounded-full border",
3961
- isSelected ? styles.radio : "border-border text-muted-foreground",
3962
- "ring-offset-background focus:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
3963
- "disabled:cursor-not-allowed disabled:opacity-50"
3964
- ),
3965
- children: /* @__PURE__ */ jsx(RadioGroupPrimitive.Indicator, { className: "flex items-center justify-center", children: /* @__PURE__ */ jsx(Circle, { className: "h-2.5 w-2.5 fill-current text-current" }) })
3966
- }
3967
- ),
3968
- /* @__PURE__ */ jsxs("div", { className: "grid gap-0.5 leading-none", children: [
3969
- /* @__PURE__ */ jsx(
3970
- "span",
3971
- {
3972
- className: cn(
3973
- "text-sm font-medium leading-none",
3974
- isSelected && styles.text
3975
- ),
3976
- children: option.label
3977
- }
3978
- ),
3979
- option.description && /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: option.description })
3980
- ] })
3981
- ]
3982
- },
3983
- option.value
3984
- );
3985
- })
3986
- }
3987
- ),
3988
- !hideError && error && /* @__PURE__ */ jsx("p", { className: "text-xs text-red-500", children: error })
3989
- ] })
3990
- }
3991
- ) });
3992
- }
3993
- FormRadioGroup.displayName = "Form.RadioGroup";
3994
- var FormLabel = React10.forwardRef(
3995
- ({ className, required, children, ...props }, ref) => {
3996
- const fieldContext = useFormFieldContextOptional();
3997
- return /* @__PURE__ */ jsxs(
3998
- "label",
3999
- {
4000
- ref,
4001
- htmlFor: fieldContext?.id,
4002
- className: cn(
4003
- "text-sm font-medium leading-none",
4004
- fieldContext?.error && "text-red-500",
4005
- className
4006
- ),
4007
- ...props,
4008
- children: [
4009
- children,
4010
- (required || fieldContext?.isRequired) && /* @__PURE__ */ jsx("span", { className: "text-red-500 ml-0.5", children: "*" })
4011
- ]
4012
- }
4013
- );
4014
- }
4015
- );
4016
- FormLabel.displayName = "Form.Label";
4017
- var FormDescription = React10.forwardRef(({ className, ...props }, ref) => {
4018
- const fieldContext = useFormFieldContextOptional();
4019
- if (fieldContext?.error) {
4020
- return null;
4021
- }
4022
- return /* @__PURE__ */ jsx(
4023
- "p",
4024
- {
4025
- ref,
4026
- className: cn("text-xs text-muted-foreground", className),
4027
- ...props
4028
- }
4029
- );
4030
- });
4031
- FormDescription.displayName = "Form.Description";
4032
- var FormError = React10.forwardRef(
4033
- ({ className, message, children, ...props }, ref) => {
4034
- const fieldContext = useFormFieldContextOptional();
4035
- const errorMessage = message ?? fieldContext?.error;
4036
- if (!errorMessage && !children) {
4037
- return null;
4534
+ className: cn(
4535
+ "aspect-square h-4 w-4 rounded-full border",
4536
+ isSelected ? styles.radio : "border-border text-muted-foreground",
4537
+ "ring-offset-background focus:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
4538
+ "disabled:cursor-not-allowed disabled:opacity-50"
4539
+ ),
4540
+ children: /* @__PURE__ */ jsx(RadioGroupPrimitive.Indicator, { className: "flex items-center justify-center", children: /* @__PURE__ */ jsx(Circle, { className: "h-2.5 w-2.5 fill-current text-current" }) })
4541
+ }
4542
+ ),
4543
+ /* @__PURE__ */ jsxs("div", { className: "grid gap-0.5 leading-none", children: [
4544
+ /* @__PURE__ */ jsx(
4545
+ "span",
4546
+ {
4547
+ className: cn(
4548
+ "text-sm font-medium leading-none",
4549
+ isSelected && styles.text
4550
+ ),
4551
+ children: option.label
4552
+ }
4553
+ ),
4554
+ option.description && /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: option.description })
4555
+ ] })
4556
+ ]
4557
+ },
4558
+ option.value
4559
+ );
4560
+ })
4561
+ }
4562
+ ),
4563
+ !hideError && error && /* @__PURE__ */ jsx("p", { className: "text-xs text-red-500", children: error })
4564
+ ] })
4038
4565
  }
4039
- return /* @__PURE__ */ jsx(
4040
- "p",
4041
- {
4042
- ref,
4043
- className: cn("text-xs text-red-500", className),
4044
- ...props,
4045
- children: children || errorMessage
4046
- }
4047
- );
4048
- }
4049
- );
4050
- FormError.displayName = "Form.Error";
4051
- var FormFieldWrapper = React10.forwardRef(({ className, label, description, required, error, children, ...props }, ref) => {
4052
- return /* @__PURE__ */ jsxs("div", { ref, className: cn("space-y-1", className), ...props, children: [
4053
- label && /* @__PURE__ */ jsx(FormLabel, { required, children: label }),
4054
- children,
4055
- description && /* @__PURE__ */ jsx(FormDescription, { children: description }),
4056
- error && /* @__PURE__ */ jsx(FormError, { message: error })
4057
- ] });
4058
- });
4059
- FormFieldWrapper.displayName = "Form.FieldWrapper";
4566
+ ) });
4567
+ }
4568
+ FormRadioGroup.displayName = "Form.RadioGroup";
4060
4569
  function FormRoot({
4061
4570
  form,
4062
4571
  onSubmit,
@@ -4080,6 +4589,7 @@ var Form = Object.assign(FormRoot, {
4080
4589
  // Campos com auto-bind
4081
4590
  Input: FormInput,
4082
4591
  Select: FormSelect,
4592
+ MultiSelect: FormMultiSelect,
4083
4593
  Textarea: FormTextarea,
4084
4594
  Checkbox: FormCheckbox,
4085
4595
  Switch: FormSwitch,
@@ -4253,207 +4763,6 @@ var DropdownMenuShortcut = ({
4253
4763
  );
4254
4764
  };
4255
4765
  DropdownMenuShortcut.displayName = "DropdownMenuShortcut";
4256
- var Popover = PopoverPrimitive.Root;
4257
- var PopoverTrigger = PopoverPrimitive.Trigger;
4258
- var PopoverContent = React10.forwardRef(({ className, align = "center", sideOffset = 4, ...props }, ref) => /* @__PURE__ */ jsx(PopoverPrimitive.Portal, { children: /* @__PURE__ */ jsx(
4259
- PopoverPrimitive.Content,
4260
- {
4261
- ref,
4262
- align,
4263
- sideOffset,
4264
- className: cn(
4265
- "z-50 w-auto rounded-md border bg-popover p-4 text-popover-foreground shadow-lg outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
4266
- className
4267
- ),
4268
- ...props
4269
- }
4270
- ) }));
4271
- PopoverContent.displayName = PopoverPrimitive.Content.displayName;
4272
- var TooltipProvider = TooltipPrimitive.Provider;
4273
- var TooltipRoot = TooltipPrimitive.Root;
4274
- var TooltipTrigger = TooltipPrimitive.Trigger;
4275
- var TooltipPortal = TooltipPrimitive.Portal;
4276
- var TooltipArrow = React10.forwardRef(({ className, variant = "light", ...props }, ref) => /* @__PURE__ */ jsx(
4277
- TooltipPrimitive.Arrow,
4278
- {
4279
- ref,
4280
- className: cn(
4281
- "z-50",
4282
- variant === "dark" ? "fill-popover" : "fill-background",
4283
- className
4284
- ),
4285
- ...props
4286
- }
4287
- ));
4288
- TooltipArrow.displayName = "TooltipArrow";
4289
- var tooltipContentVariants = cva(
4290
- "z-50 overflow-hidden rounded-lg shadow-lg outline-none animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
4291
- {
4292
- variants: {
4293
- variant: {
4294
- light: "bg-background text-foreground border border-border",
4295
- dark: "bg-popover text-popover-foreground border border-border"
4296
- },
4297
- size: {
4298
- sm: "max-w-[200px] p-2",
4299
- md: "max-w-[280px] p-3",
4300
- lg: "max-w-[360px] p-4"
4301
- }
4302
- },
4303
- defaultVariants: {
4304
- variant: "light",
4305
- size: "md"
4306
- }
4307
- }
4308
- );
4309
- var TooltipContent = React10.forwardRef(
4310
- ({
4311
- className,
4312
- variant = "light",
4313
- size = "md",
4314
- sideOffset = 8,
4315
- showArrow = true,
4316
- onDismiss,
4317
- children,
4318
- ...props
4319
- }, ref) => /* @__PURE__ */ jsx(TooltipPrimitive.Portal, { children: /* @__PURE__ */ jsxs(
4320
- TooltipPrimitive.Content,
4321
- {
4322
- ref,
4323
- sideOffset,
4324
- className: cn(tooltipContentVariants({ variant, size }), className),
4325
- ...props,
4326
- children: [
4327
- onDismiss && /* @__PURE__ */ jsx(
4328
- "button",
4329
- {
4330
- onClick: onDismiss,
4331
- className: "absolute top-2 right-2 p-1 rounded-full transition-colors hover:bg-muted text-muted-foreground hover:text-foreground",
4332
- "aria-label": "Fechar",
4333
- children: /* @__PURE__ */ jsx(X, { className: "h-3.5 w-3.5" })
4334
- }
4335
- ),
4336
- children,
4337
- showArrow && /* @__PURE__ */ jsx(TooltipArrow, { variant: variant ?? "light", width: 12, height: 6 })
4338
- ]
4339
- }
4340
- ) })
4341
- );
4342
- TooltipContent.displayName = TooltipPrimitive.Content.displayName;
4343
- var TooltipHeader = React10.forwardRef(
4344
- ({ className, ...props }, ref) => /* @__PURE__ */ jsx("div", { ref, className: cn("flex flex-col gap-1", className), ...props })
4345
- );
4346
- TooltipHeader.displayName = "TooltipHeader";
4347
- var TooltipTitle = React10.forwardRef(
4348
- ({ className, ...props }, ref) => /* @__PURE__ */ jsx(
4349
- "h4",
4350
- {
4351
- ref,
4352
- className: cn("text-sm font-semibold leading-tight", className),
4353
- ...props
4354
- }
4355
- )
4356
- );
4357
- TooltipTitle.displayName = "TooltipTitle";
4358
- var TooltipDescription = React10.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
4359
- "p",
4360
- {
4361
- ref,
4362
- className: cn("text-xs leading-relaxed text-muted-foreground", className),
4363
- ...props
4364
- }
4365
- ));
4366
- TooltipDescription.displayName = "TooltipDescription";
4367
- var TooltipActions = React10.forwardRef(
4368
- ({ className, ...props }, ref) => /* @__PURE__ */ jsx(
4369
- "div",
4370
- {
4371
- ref,
4372
- className: cn("flex items-center gap-2 mt-3", className),
4373
- ...props
4374
- }
4375
- )
4376
- );
4377
- TooltipActions.displayName = "TooltipActions";
4378
- var tooltipActionVariants = cva(
4379
- "inline-flex items-center justify-center text-xs font-medium rounded-md transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",
4380
- {
4381
- variants: {
4382
- variant: {
4383
- primary: "bg-primary text-primary-foreground hover:bg-primary/90 px-3 py-1.5",
4384
- secondary: "bg-transparent text-muted-foreground hover:text-foreground hover:underline px-2 py-1.5",
4385
- outline: "border border-border bg-background text-foreground hover:bg-muted px-3 py-1.5"
4386
- }
4387
- },
4388
- defaultVariants: {
4389
- variant: "primary"
4390
- }
4391
- }
4392
- );
4393
- var TooltipAction = React10.forwardRef(
4394
- ({ className, variant = "primary", ...props }, ref) => /* @__PURE__ */ jsx(
4395
- "button",
4396
- {
4397
- ref,
4398
- className: cn(tooltipActionVariants({ variant }), className),
4399
- ...props
4400
- }
4401
- )
4402
- );
4403
- TooltipAction.displayName = "TooltipAction";
4404
- var TooltipIcon = React10.forwardRef(
4405
- ({ className, children, ...props }, ref) => /* @__PURE__ */ jsx(
4406
- "div",
4407
- {
4408
- ref,
4409
- className: cn(
4410
- "flex items-center justify-center w-10 h-10 rounded-full mb-3 bg-muted",
4411
- className
4412
- ),
4413
- ...props,
4414
- children
4415
- }
4416
- )
4417
- );
4418
- TooltipIcon.displayName = "TooltipIcon";
4419
- var SimpleTooltip = ({
4420
- children,
4421
- content,
4422
- variant = "light",
4423
- side = "top",
4424
- align = "center",
4425
- delayDuration = 200,
4426
- open,
4427
- defaultOpen,
4428
- onOpenChange
4429
- }) => /* @__PURE__ */ jsxs(
4430
- TooltipRoot,
4431
- {
4432
- open,
4433
- defaultOpen,
4434
- onOpenChange,
4435
- delayDuration,
4436
- children: [
4437
- /* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children }),
4438
- /* @__PURE__ */ jsx(TooltipContent, { variant, side, align, size: "sm", children: /* @__PURE__ */ jsx("span", { className: "text-xs", children: content }) })
4439
- ]
4440
- }
4441
- );
4442
- SimpleTooltip.displayName = "SimpleTooltip";
4443
- var Tooltip = Object.assign(TooltipRoot, {
4444
- Provider: TooltipProvider,
4445
- Trigger: TooltipTrigger,
4446
- Portal: TooltipPortal,
4447
- Content: TooltipContent,
4448
- Arrow: TooltipArrow,
4449
- Header: TooltipHeader,
4450
- Title: TooltipTitle,
4451
- Description: TooltipDescription,
4452
- Actions: TooltipActions,
4453
- Action: TooltipAction,
4454
- Icon: TooltipIcon,
4455
- Simple: SimpleTooltip
4456
- });
4457
4766
  var AuthLayoutContext = React10.createContext({
4458
4767
  imagePosition: "left"
4459
4768
  });