@facter/ds-core 1.33.11 → 1.34.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.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';
@@ -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,550 @@ 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
+ width: "var(--radix-popover-trigger-width)",
3857
+ maxHeight: "min(340px, var(--radix-popover-content-available-height, 340px))"
3858
+ },
3859
+ onOpenAutoFocus: (e) => {
3860
+ e.preventDefault();
3861
+ if (searchable) {
3862
+ setTimeout(() => searchRef.current?.focus(), 0);
3863
+ }
3864
+ },
3865
+ children: [
3866
+ searchable && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 px-3 py-2 border-b border-border", children: [
3867
+ /* @__PURE__ */ jsx(Search, { className: "h-4 w-4 shrink-0 text-muted-foreground" }),
3868
+ /* @__PURE__ */ jsx(
3869
+ "input",
3870
+ {
3871
+ ref: searchRef,
3872
+ type: "text",
3873
+ value: search,
3874
+ onChange: (e) => handleSearch(e.target.value),
3875
+ placeholder: searchPlaceholder,
3876
+ className: "flex-1 bg-transparent text-sm outline-none placeholder:text-muted-foreground"
3877
+ }
3878
+ )
3879
+ ] }),
3880
+ /* @__PURE__ */ jsx(
3881
+ "div",
3882
+ {
3883
+ ref: listRef,
3884
+ className: "overflow-y-auto overscroll-contain min-h-0 flex-1",
3885
+ onScroll: handleScroll,
3886
+ children: /* @__PURE__ */ jsxs("div", { className: "p-1", children: [
3887
+ filteredOptions.length === 0 && !loading ? /* @__PURE__ */ jsx("p", { className: "py-4 text-center text-sm text-muted-foreground", children: emptyText }) : filteredOptions.map((option) => {
3888
+ const isSelected = value === option.value;
3889
+ const isDisabled = option.disabled || disabled;
3890
+ return /* @__PURE__ */ jsxs(
3891
+ "button",
3892
+ {
3893
+ type: "button",
3894
+ disabled: isDisabled,
3895
+ onClick: () => {
3896
+ onChange(option.value);
3897
+ setOpen(false);
3898
+ },
3899
+ className: cn(
3900
+ "flex w-full items-center gap-2 rounded-sm px-2 text-left text-sm outline-none",
3901
+ "cursor-pointer hover:bg-accent hover:text-accent-foreground",
3902
+ isSelected && "bg-accent/50",
3903
+ isDisabled && "pointer-events-none opacity-50",
3904
+ option.description ? "py-2" : "py-1.5"
3905
+ ),
3906
+ children: [
3907
+ 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" }) }),
3908
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
3909
+ /* @__PURE__ */ jsx("span", { className: "block leading-tight", children: option.label }),
3910
+ option.description && /* @__PURE__ */ jsx("span", { className: "block text-xs leading-tight text-muted-foreground", children: option.description })
3911
+ ] }),
3912
+ isSelected && /* @__PURE__ */ jsx(Check, { className: "h-4 w-4 shrink-0 text-primary" })
3913
+ ]
3914
+ },
3915
+ option.value
3916
+ );
3917
+ }),
3918
+ 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" }) })
3919
+ ] })
3920
+ }
3921
+ )
3922
+ ]
3923
+ }
3924
+ )
3925
+ ] });
3926
+ }
3927
+ function FormMultiSelect({
3928
+ name,
3929
+ label,
3930
+ description,
3931
+ tooltip,
3932
+ required,
3933
+ disabled,
3934
+ className,
3935
+ options,
3936
+ placeholder = "Selecione...",
3937
+ hideError = false,
3938
+ emptyText = "Nenhuma op\xE7\xE3o dispon\xEDvel",
3939
+ loading = false,
3940
+ searchable = false,
3941
+ searchPlaceholder = "Buscar...",
3942
+ clearable = true,
3943
+ maxVisibleChips = 3,
3944
+ dropdownPosition = "bottom"
3945
+ }) {
3946
+ const form = useFormContext();
3947
+ const fieldState = form.getFieldState(name, form.formState);
3948
+ const error = fieldState.error?.message;
3949
+ return /* @__PURE__ */ jsx(FormFieldProvider, { name, children: /* @__PURE__ */ jsx(
3950
+ Controller,
3951
+ {
3952
+ control: form.control,
3953
+ name,
3954
+ render: ({ field }) => {
3955
+ const values = Array.isArray(field.value) ? field.value : [];
3956
+ return /* @__PURE__ */ jsxs("div", { className: cn("space-y-1", className), children: [
3957
+ /* @__PURE__ */ jsx(
3958
+ MultiSelectDropdown,
3959
+ {
3960
+ options,
3961
+ value: values,
3962
+ onChange: (v) => field.onChange(v),
3963
+ disabled,
3964
+ label,
3965
+ tooltip,
3966
+ required,
3967
+ error: !!error,
3968
+ placeholder,
3969
+ searchable,
3970
+ loading,
3971
+ emptyText,
3972
+ searchPlaceholder,
3973
+ clearable,
3974
+ maxVisibleChips,
3975
+ dropdownPosition
3976
+ }
3977
+ ),
3978
+ description && !error && /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground px-1", children: description }),
3979
+ !hideError && error && /* @__PURE__ */ jsx("p", { className: "text-xs text-red-500 px-1", children: error })
3980
+ ] });
3981
+ }
3982
+ }
3983
+ ) });
3984
+ }
3985
+ FormMultiSelect.displayName = "Form.MultiSelect";
3986
+ function MultiSelectDropdown({
3471
3987
  options,
3472
3988
  value,
3473
3989
  onChange,
3474
3990
  disabled,
3475
3991
  label,
3992
+ tooltip,
3476
3993
  required,
3477
3994
  error,
3478
3995
  placeholder = "Selecione...",
3479
3996
  searchable,
3480
- onSearch,
3481
- onLoadMore,
3482
- hasMore,
3483
3997
  loading,
3484
3998
  emptyText = "Nenhuma op\xE7\xE3o dispon\xEDvel",
3485
- searchPlaceholder = "Buscar..."
3999
+ searchPlaceholder = "Buscar...",
4000
+ clearable = true,
4001
+ maxVisibleChips = 3,
4002
+ dropdownPosition = "bottom"
3486
4003
  }) {
3487
4004
  const [open, setOpen] = React10.useState(false);
3488
4005
  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);
4006
+ const [computedMax, setComputedMax] = React10.useState(maxVisibleChips);
4007
+ const [prevValueKey, setPrevValueKey] = React10.useState(() => value.join(","));
4008
+ const contentRef = React10.useRef(null);
3492
4009
  const listRef = React10.useRef(null);
3493
4010
  const searchRef = React10.useRef(null);
3494
- const [dropdownPos, setDropdownPos] = React10.useState({});
4011
+ const chipsRef = React10.useRef(null);
4012
+ const selectedOptions = React10.useMemo(
4013
+ () => options.filter((o) => value.includes(o.value)),
4014
+ [options, value]
4015
+ );
4016
+ const valueKey = value.join(",");
4017
+ if (valueKey !== prevValueKey) {
4018
+ setPrevValueKey(valueKey);
4019
+ setComputedMax(maxVisibleChips);
4020
+ }
4021
+ React10.useLayoutEffect(() => {
4022
+ const container = chipsRef.current;
4023
+ if (!container || selectedOptions.length === 0) return;
4024
+ const containerRect = container.getBoundingClientRect();
4025
+ const containerWidth = containerRect.width;
4026
+ const chips = container.querySelectorAll("[data-chip]");
4027
+ if (chips.length === 0 || containerWidth === 0) return;
4028
+ const gap = 4;
4029
+ const buffer = 4;
4030
+ const badge = container.querySelector("[data-badge]");
4031
+ const badgeWidth = badge ? badge.getBoundingClientRect().width : 28;
4032
+ const availableWidth = containerWidth - buffer;
4033
+ let usedWidth = 0;
4034
+ let fits = 0;
4035
+ for (let i = 0; i < chips.length; i++) {
4036
+ const chipWidth = chips[i].getBoundingClientRect().width;
4037
+ const widthWithGap = fits > 0 ? chipWidth + gap : chipWidth;
4038
+ const remaining = selectedOptions.length - (fits + 1);
4039
+ const needsBadge = remaining > 0;
4040
+ const badgeSpace = needsBadge ? badgeWidth + gap : 0;
4041
+ if (usedWidth + widthWithGap + badgeSpace > availableWidth && fits > 0) {
4042
+ break;
4043
+ }
4044
+ usedWidth += widthWithGap;
4045
+ fits++;
4046
+ }
4047
+ const newMax = Math.max(1, fits);
4048
+ if (newMax !== computedMax) {
4049
+ setComputedMax(newMax);
4050
+ }
4051
+ });
3495
4052
  const filteredOptions = React10.useMemo(() => {
3496
- if (!searchable || onSearch || !search) return options;
4053
+ if (!searchable || !search) return options;
3497
4054
  const q = search.toLowerCase();
3498
4055
  return options.filter(
3499
4056
  (o) => o.label.toLowerCase().includes(q) || o.description?.toLowerCase().includes(q)
3500
4057
  );
3501
- }, [options, search, searchable, onSearch]);
3502
- const handleSearch = React10.useCallback(
3503
- (value2) => {
3504
- setSearch(value2);
3505
- if (onSearch) onSearch(value2);
4058
+ }, [options, search, searchable]);
4059
+ const toggleOption = React10.useCallback(
4060
+ (optionValue) => {
4061
+ if (value.includes(optionValue)) {
4062
+ onChange(value.filter((v) => v !== optionValue));
4063
+ } else {
4064
+ onChange([...value, optionValue]);
4065
+ }
3506
4066
  },
3507
- [onSearch]
4067
+ [value, onChange]
3508
4068
  );
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) {
4069
+ const handleClearAll = React10.useCallback(
4070
+ (e) => {
4071
+ e.stopPropagation();
4072
+ onChange([]);
4073
+ },
4074
+ [onChange]
4075
+ );
4076
+ const handleRemoveChip = React10.useCallback(
4077
+ (e, optionValue) => {
4078
+ e.stopPropagation();
4079
+ onChange(value.filter((v) => v !== optionValue));
4080
+ },
4081
+ [value, onChange]
4082
+ );
4083
+ const handleOpenChange = React10.useCallback((nextOpen) => {
4084
+ setOpen(nextOpen);
4085
+ if (!nextOpen) {
3553
4086
  setSearch("");
3554
- if (onSearch) onSearch("");
3555
- }
3556
- }, [open, onSearch]);
3557
- React10.useEffect(() => {
3558
- if (open && searchable) {
3559
- setTimeout(() => searchRef.current?.focus(), 0);
3560
4087
  }
3561
- }, [open, searchable]);
4088
+ }, []);
3562
4089
  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
- );
4090
+ if (!open) return;
4091
+ let cancelled = false;
4092
+ let removeHandler;
4093
+ requestAnimationFrame(() => {
4094
+ if (cancelled) return;
4095
+ const content = contentRef.current;
4096
+ const list = listRef.current;
4097
+ if (!content || !list) return;
4098
+ const handleWheel = (e) => {
4099
+ if (!content.contains(e.target)) return;
4100
+ const { scrollTop, scrollHeight, clientHeight } = list;
4101
+ if (scrollHeight <= clientHeight) return;
4102
+ e.preventDefault();
4103
+ e.stopPropagation();
4104
+ list.scrollTop = Math.max(0, Math.min(scrollTop + e.deltaY, scrollHeight - clientHeight));
4105
+ };
4106
+ document.addEventListener("wheel", handleWheel, { passive: false, capture: true });
4107
+ removeHandler = () => document.removeEventListener("wheel", handleWheel, { capture: true });
4108
+ });
4109
+ return () => {
4110
+ cancelled = true;
4111
+ removeHandler?.();
3575
4112
  };
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",
4113
+ }, [open]);
4114
+ const side = dropdownPosition === "top" ? "top" : "bottom";
4115
+ const avoidCollisions = dropdownPosition === "auto";
4116
+ const visibleChips = selectedOptions.slice(0, computedMax);
4117
+ const overflowCount = selectedOptions.length - computedMax;
4118
+ return /* @__PURE__ */ jsxs(Popover, { open, onOpenChange: handleOpenChange, children: [
4119
+ /* @__PURE__ */ jsxs("div", { className: "relative", children: [
4120
+ /* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(
4121
+ "button",
3628
4122
  {
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",
4123
+ type: "button",
4124
+ disabled,
4125
+ className: cn(
4126
+ "flex w-full items-center justify-between rounded-md border-2 bg-background px-3 text-sm transition-colors",
4127
+ "focus:outline-none focus:border-primary",
4128
+ "disabled:cursor-not-allowed disabled:opacity-50",
4129
+ label ? "min-h-[3rem] pt-4 pb-1.5" : "min-h-[2.25rem] py-1.5",
4130
+ error ? "border-red-500" : "border-border",
4131
+ open && !error && "border-primary"
4132
+ ),
3632
4133
  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",
4134
+ /* @__PURE__ */ jsx("div", { ref: chipsRef, className: "flex gap-1 items-center flex-1 overflow-hidden", children: selectedOptions.length > 0 ? /* @__PURE__ */ jsxs(Fragment, { children: [
4135
+ visibleChips.map((option) => /* @__PURE__ */ jsxs(
4136
+ "span",
4137
+ {
4138
+ "data-chip": true,
4139
+ 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",
4140
+ children: [
4141
+ option.icon && /* @__PURE__ */ jsx(option.icon, { className: "h-3 w-3 shrink-0" }),
4142
+ /* @__PURE__ */ jsx("span", { className: "truncate", children: option.label }),
4143
+ /* @__PURE__ */ jsx(
4144
+ X,
4145
+ {
4146
+ className: "h-3 w-3 shrink-0 cursor-pointer hover:text-destructive",
4147
+ onClick: (e) => handleRemoveChip(e, option.value)
4148
+ }
4149
+ )
4150
+ ]
4151
+ },
4152
+ option.value
4153
+ )),
4154
+ 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: [
4155
+ "+",
4156
+ overflowCount
4157
+ ] })
4158
+ ] }) : /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: placeholder }) }),
4159
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1 ml-1 shrink-0", children: [
4160
+ clearable && selectedOptions.length > 0 && /* @__PURE__ */ jsx(
4161
+ X,
3637
4162
  {
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"
4163
+ className: "h-4 w-4 text-muted-foreground hover:text-foreground cursor-pointer",
4164
+ onClick: handleClearAll
3644
4165
  }
3645
- )
3646
- ] }),
4166
+ ),
4167
+ /* @__PURE__ */ jsx(ChevronDown, { className: cn("h-4 w-4 opacity-50 transition-transform", open && "rotate-180") })
4168
+ ] })
4169
+ ]
4170
+ }
4171
+ ) }),
4172
+ label && /* @__PURE__ */ jsxs(
4173
+ "label",
4174
+ {
4175
+ className: cn(
4176
+ "absolute left-3 top-[-6px] text-xs font-medium bg-background px-1 inline-flex items-center gap-1",
4177
+ tooltip ? "pointer-events-auto" : "pointer-events-none",
4178
+ error ? "text-red-500" : "text-foreground"
4179
+ ),
4180
+ children: [
4181
+ label,
4182
+ required && /* @__PURE__ */ jsx("span", { className: "text-red-500 ml-0.5", children: "*" }),
4183
+ tooltip && /* @__PURE__ */ jsx(FieldTooltipIcon, { tooltip })
4184
+ ]
4185
+ }
4186
+ )
4187
+ ] }),
4188
+ /* @__PURE__ */ jsxs(
4189
+ PopoverContent,
4190
+ {
4191
+ ref: contentRef,
4192
+ side,
4193
+ align: "start",
4194
+ sideOffset: 4,
4195
+ avoidCollisions,
4196
+ collisionPadding: 16,
4197
+ className: "p-0 overflow-hidden flex flex-col",
4198
+ style: {
4199
+ width: "var(--radix-popover-trigger-width)",
4200
+ maxHeight: "min(340px, var(--radix-popover-content-available-height, 340px))"
4201
+ },
4202
+ onOpenAutoFocus: (e) => {
4203
+ e.preventDefault();
4204
+ if (searchable) {
4205
+ setTimeout(() => searchRef.current?.focus(), 0);
4206
+ }
4207
+ },
4208
+ children: [
4209
+ searchable && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 px-3 py-2 border-b border-border", children: [
4210
+ /* @__PURE__ */ jsx(Search, { className: "h-4 w-4 shrink-0 text-muted-foreground" }),
3647
4211
  /* @__PURE__ */ jsx(
3648
- "div",
4212
+ "input",
3649
4213
  {
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
- ] })
4214
+ ref: searchRef,
4215
+ type: "text",
4216
+ value: search,
4217
+ onChange: (e) => setSearch(e.target.value),
4218
+ placeholder: searchPlaceholder,
4219
+ className: "flex-1 bg-transparent text-sm outline-none placeholder:text-muted-foreground"
3686
4220
  }
3687
4221
  )
3688
- ]
3689
- }
3690
- ),
3691
- document.body
4222
+ ] }),
4223
+ /* @__PURE__ */ jsx(
4224
+ "div",
4225
+ {
4226
+ ref: listRef,
4227
+ className: "overflow-y-auto overscroll-contain min-h-0 flex-1",
4228
+ children: /* @__PURE__ */ jsxs("div", { className: "p-1", children: [
4229
+ filteredOptions.length === 0 && !loading ? /* @__PURE__ */ jsx("p", { className: "py-4 text-center text-sm text-muted-foreground", children: emptyText }) : filteredOptions.map((option) => {
4230
+ const isSelected = value.includes(option.value);
4231
+ const isDisabled = option.disabled || disabled;
4232
+ return /* @__PURE__ */ jsxs(
4233
+ "button",
4234
+ {
4235
+ type: "button",
4236
+ disabled: isDisabled,
4237
+ onClick: () => toggleOption(option.value),
4238
+ className: cn(
4239
+ "flex w-full items-center gap-2 rounded-sm px-2 text-left text-sm outline-none",
4240
+ "cursor-pointer hover:bg-accent hover:text-accent-foreground",
4241
+ isSelected && "bg-accent/50",
4242
+ isDisabled && "pointer-events-none opacity-50",
4243
+ option.description ? "py-2" : "py-1.5"
4244
+ ),
4245
+ children: [
4246
+ 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" }) }),
4247
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
4248
+ /* @__PURE__ */ jsx("span", { className: "block leading-tight", children: option.label }),
4249
+ option.description && /* @__PURE__ */ jsx("span", { className: "block text-xs leading-tight text-muted-foreground", children: option.description })
4250
+ ] }),
4251
+ isSelected && /* @__PURE__ */ jsx(Check, { className: "h-4 w-4 shrink-0 text-primary" })
4252
+ ]
4253
+ },
4254
+ option.value
4255
+ );
4256
+ }),
4257
+ 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" }) })
4258
+ ] })
4259
+ }
4260
+ )
4261
+ ]
4262
+ }
3692
4263
  )
3693
4264
  ] });
3694
4265
  }
@@ -3956,107 +4527,41 @@ function FormRadioGroup({
3956
4527
  id: optionId,
3957
4528
  value: option.value,
3958
4529
  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;
4530
+ className: cn(
4531
+ "aspect-square h-4 w-4 rounded-full border",
4532
+ isSelected ? styles.radio : "border-border text-muted-foreground",
4533
+ "ring-offset-background focus:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
4534
+ "disabled:cursor-not-allowed disabled:opacity-50"
4535
+ ),
4536
+ 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" }) })
4537
+ }
4538
+ ),
4539
+ /* @__PURE__ */ jsxs("div", { className: "grid gap-0.5 leading-none", children: [
4540
+ /* @__PURE__ */ jsx(
4541
+ "span",
4542
+ {
4543
+ className: cn(
4544
+ "text-sm font-medium leading-none",
4545
+ isSelected && styles.text
4546
+ ),
4547
+ children: option.label
4548
+ }
4549
+ ),
4550
+ option.description && /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: option.description })
4551
+ ] })
4552
+ ]
4553
+ },
4554
+ option.value
4555
+ );
4556
+ })
4557
+ }
4558
+ ),
4559
+ !hideError && error && /* @__PURE__ */ jsx("p", { className: "text-xs text-red-500", children: error })
4560
+ ] })
4038
4561
  }
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";
4562
+ ) });
4563
+ }
4564
+ FormRadioGroup.displayName = "Form.RadioGroup";
4060
4565
  function FormRoot({
4061
4566
  form,
4062
4567
  onSubmit,
@@ -4080,6 +4585,7 @@ var Form = Object.assign(FormRoot, {
4080
4585
  // Campos com auto-bind
4081
4586
  Input: FormInput,
4082
4587
  Select: FormSelect,
4588
+ MultiSelect: FormMultiSelect,
4083
4589
  Textarea: FormTextarea,
4084
4590
  Checkbox: FormCheckbox,
4085
4591
  Switch: FormSwitch,
@@ -4253,207 +4759,6 @@ var DropdownMenuShortcut = ({
4253
4759
  );
4254
4760
  };
4255
4761
  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
4762
  var AuthLayoutContext = React10.createContext({
4458
4763
  imagePosition: "left"
4459
4764
  });