@facter/ds-core 1.10.0 → 1.12.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.d.mts CHANGED
@@ -896,6 +896,16 @@ interface FormSelectProps<T extends FieldValues = FieldValues> extends BaseField
896
896
  }>;
897
897
  hideError?: boolean;
898
898
  selectSize?: 'sm' | 'default' | 'lg';
899
+ /** Render as dropdown (default) or visual card grid */
900
+ variant?: 'default' | 'card';
901
+ /** Server-side search callback. If not provided, filters locally */
902
+ onSearch?: (query: string) => void;
903
+ /** Infinite scroll: callback to load more items */
904
+ onLoadMore?: () => void;
905
+ /** Infinite scroll: whether there are more items to load */
906
+ hasMore?: boolean;
907
+ /** Placeholder for the search input */
908
+ searchPlaceholder?: string;
899
909
  }
900
910
  interface FormTextareaProps<T extends FieldValues = FieldValues> extends BaseFieldProps<T>, Omit<React$1.TextareaHTMLAttributes<HTMLTextAreaElement>, 'name'> {
901
911
  hideError?: boolean;
@@ -927,7 +937,7 @@ declare namespace FormInput {
927
937
  var displayName: string;
928
938
  }
929
939
 
930
- declare function FormSelect<T extends FieldValues = FieldValues>({ name, label, description, required, disabled, className, options, placeholder, icon, hideError, selectSize, emptyText, loading, }: FormSelectProps<T>): react_jsx_runtime.JSX.Element;
940
+ declare function FormSelect<T extends FieldValues = FieldValues>({ name, label, description, required, disabled, className, options, placeholder, icon, hideError, selectSize, emptyText, loading, variant, searchable, onSearch, onLoadMore, hasMore, searchPlaceholder, }: FormSelectProps<T>): react_jsx_runtime.JSX.Element;
931
941
  declare namespace FormSelect {
932
942
  var displayName: string;
933
943
  }
package/dist/index.d.ts CHANGED
@@ -896,6 +896,16 @@ interface FormSelectProps<T extends FieldValues = FieldValues> extends BaseField
896
896
  }>;
897
897
  hideError?: boolean;
898
898
  selectSize?: 'sm' | 'default' | 'lg';
899
+ /** Render as dropdown (default) or visual card grid */
900
+ variant?: 'default' | 'card';
901
+ /** Server-side search callback. If not provided, filters locally */
902
+ onSearch?: (query: string) => void;
903
+ /** Infinite scroll: callback to load more items */
904
+ onLoadMore?: () => void;
905
+ /** Infinite scroll: whether there are more items to load */
906
+ hasMore?: boolean;
907
+ /** Placeholder for the search input */
908
+ searchPlaceholder?: string;
899
909
  }
900
910
  interface FormTextareaProps<T extends FieldValues = FieldValues> extends BaseFieldProps<T>, Omit<React$1.TextareaHTMLAttributes<HTMLTextAreaElement>, 'name'> {
901
911
  hideError?: boolean;
@@ -927,7 +937,7 @@ declare namespace FormInput {
927
937
  var displayName: string;
928
938
  }
929
939
 
930
- declare function FormSelect<T extends FieldValues = FieldValues>({ name, label, description, required, disabled, className, options, placeholder, icon, hideError, selectSize, emptyText, loading, }: FormSelectProps<T>): react_jsx_runtime.JSX.Element;
940
+ declare function FormSelect<T extends FieldValues = FieldValues>({ name, label, description, required, disabled, className, options, placeholder, icon, hideError, selectSize, emptyText, loading, variant, searchable, onSearch, onLoadMore, hasMore, searchPlaceholder, }: FormSelectProps<T>): react_jsx_runtime.JSX.Element;
931
941
  declare namespace FormSelect {
932
942
  var displayName: string;
933
943
  }
package/dist/index.js CHANGED
@@ -16,10 +16,10 @@ var DialogPrimitive = require('@radix-ui/react-dialog');
16
16
  var sonner = require('sonner');
17
17
  var SwitchPrimitives = require('@radix-ui/react-switch');
18
18
  var reactHookForm = require('react-hook-form');
19
+ var PopoverPrimitive = require('@radix-ui/react-popover');
19
20
  var RadioGroupPrimitive = require('@radix-ui/react-radio-group');
20
21
  var AvatarPrimitive = require('@radix-ui/react-avatar');
21
22
  var DropdownMenuPrimitive = require('@radix-ui/react-dropdown-menu');
22
- var PopoverPrimitive = require('@radix-ui/react-popover');
23
23
  var TooltipPrimitive = require('@radix-ui/react-tooltip');
24
24
  var ScrollAreaPrimitive = require('@radix-ui/react-scroll-area');
25
25
  var SeparatorPrimitive = require('@radix-ui/react-separator');
@@ -50,10 +50,10 @@ var TabsPrimitive__namespace = /*#__PURE__*/_interopNamespace(TabsPrimitive);
50
50
  var CheckboxPrimitive__namespace = /*#__PURE__*/_interopNamespace(CheckboxPrimitive);
51
51
  var DialogPrimitive__namespace = /*#__PURE__*/_interopNamespace(DialogPrimitive);
52
52
  var SwitchPrimitives__namespace = /*#__PURE__*/_interopNamespace(SwitchPrimitives);
53
+ var PopoverPrimitive__namespace = /*#__PURE__*/_interopNamespace(PopoverPrimitive);
53
54
  var RadioGroupPrimitive__namespace = /*#__PURE__*/_interopNamespace(RadioGroupPrimitive);
54
55
  var AvatarPrimitive__namespace = /*#__PURE__*/_interopNamespace(AvatarPrimitive);
55
56
  var DropdownMenuPrimitive__namespace = /*#__PURE__*/_interopNamespace(DropdownMenuPrimitive);
56
- var PopoverPrimitive__namespace = /*#__PURE__*/_interopNamespace(PopoverPrimitive);
57
57
  var TooltipPrimitive__namespace = /*#__PURE__*/_interopNamespace(TooltipPrimitive);
58
58
  var ScrollAreaPrimitive__namespace = /*#__PURE__*/_interopNamespace(ScrollAreaPrimitive);
59
59
  var SeparatorPrimitive__namespace = /*#__PURE__*/_interopNamespace(SeparatorPrimitive);
@@ -3239,6 +3239,22 @@ function FormInput({
3239
3239
  ) });
3240
3240
  }
3241
3241
  FormInput.displayName = "Form.Input";
3242
+ var Popover = PopoverPrimitive__namespace.Root;
3243
+ var PopoverTrigger = PopoverPrimitive__namespace.Trigger;
3244
+ var PopoverContent = React10__namespace.forwardRef(({ className, align = "center", sideOffset = 4, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(PopoverPrimitive__namespace.Portal, { children: /* @__PURE__ */ jsxRuntime.jsx(
3245
+ PopoverPrimitive__namespace.Content,
3246
+ {
3247
+ ref,
3248
+ align,
3249
+ sideOffset,
3250
+ className: cn(
3251
+ "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",
3252
+ className
3253
+ ),
3254
+ ...props
3255
+ }
3256
+ ) }));
3257
+ PopoverContent.displayName = PopoverPrimitive__namespace.Content.displayName;
3242
3258
  function FormSelect({
3243
3259
  name,
3244
3260
  label,
@@ -3252,7 +3268,13 @@ function FormSelect({
3252
3268
  hideError = false,
3253
3269
  selectSize = "default",
3254
3270
  emptyText = "Nenhuma op\xE7\xE3o dispon\xEDvel",
3255
- loading = false
3271
+ loading = false,
3272
+ variant = "default",
3273
+ searchable = false,
3274
+ onSearch,
3275
+ onLoadMore,
3276
+ hasMore,
3277
+ searchPlaceholder = "Buscar..."
3256
3278
  }) {
3257
3279
  const form = reactHookForm.useFormContext();
3258
3280
  const fieldState = form.getFieldState(name, form.formState);
@@ -3263,7 +3285,26 @@ function FormSelect({
3263
3285
  control: form.control,
3264
3286
  name,
3265
3287
  render: ({ field }) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("space-y-1", className), children: [
3266
- /* @__PURE__ */ jsxRuntime.jsx(
3288
+ variant === "card" ? /* @__PURE__ */ jsxRuntime.jsx(
3289
+ CardSelect,
3290
+ {
3291
+ options,
3292
+ value: field.value,
3293
+ onChange: (v) => field.onChange(v || void 0),
3294
+ disabled,
3295
+ label,
3296
+ required,
3297
+ error: !!error,
3298
+ placeholder,
3299
+ searchable,
3300
+ onSearch,
3301
+ onLoadMore,
3302
+ hasMore,
3303
+ loading,
3304
+ emptyText,
3305
+ searchPlaceholder
3306
+ }
3307
+ ) : /* @__PURE__ */ jsxRuntime.jsx(
3267
3308
  Select,
3268
3309
  {
3269
3310
  value: field.value ?? "",
@@ -3301,6 +3342,170 @@ function FormSelect({
3301
3342
  ) });
3302
3343
  }
3303
3344
  FormSelect.displayName = "Form.Select";
3345
+ function CardSelect({
3346
+ options,
3347
+ value,
3348
+ onChange,
3349
+ disabled,
3350
+ label,
3351
+ required,
3352
+ error,
3353
+ placeholder = "Selecione...",
3354
+ searchable,
3355
+ onSearch,
3356
+ onLoadMore,
3357
+ hasMore,
3358
+ loading,
3359
+ emptyText = "Nenhuma op\xE7\xE3o dispon\xEDvel",
3360
+ searchPlaceholder = "Buscar..."
3361
+ }) {
3362
+ const [open, setOpen] = React10__namespace.useState(false);
3363
+ const [search, setSearch] = React10__namespace.useState("");
3364
+ const selected = options.find((o) => o.value === value);
3365
+ const listRef = React10__namespace.useRef(null);
3366
+ const searchRef = React10__namespace.useRef(null);
3367
+ const filteredOptions = React10__namespace.useMemo(() => {
3368
+ if (!searchable || onSearch || !search) return options;
3369
+ const q = search.toLowerCase();
3370
+ return options.filter(
3371
+ (o) => o.label.toLowerCase().includes(q) || o.description?.toLowerCase().includes(q)
3372
+ );
3373
+ }, [options, search, searchable, onSearch]);
3374
+ const handleSearch = React10__namespace.useCallback(
3375
+ (value2) => {
3376
+ setSearch(value2);
3377
+ if (onSearch) onSearch(value2);
3378
+ },
3379
+ [onSearch]
3380
+ );
3381
+ React10__namespace.useEffect(() => {
3382
+ if (!open) {
3383
+ setSearch("");
3384
+ if (onSearch) onSearch("");
3385
+ }
3386
+ }, [open, onSearch]);
3387
+ React10__namespace.useEffect(() => {
3388
+ if (open && searchable) {
3389
+ setTimeout(() => searchRef.current?.focus(), 0);
3390
+ }
3391
+ }, [open, searchable]);
3392
+ const handleScroll = React10__namespace.useCallback(() => {
3393
+ if (!onLoadMore || !hasMore || loading) return;
3394
+ const el = listRef.current;
3395
+ if (!el) return;
3396
+ const { scrollTop, scrollHeight, clientHeight } = el;
3397
+ if (scrollHeight - scrollTop - clientHeight < 80) {
3398
+ onLoadMore();
3399
+ }
3400
+ }, [onLoadMore, hasMore, loading]);
3401
+ return /* @__PURE__ */ jsxRuntime.jsxs(Popover, { open, onOpenChange: setOpen, modal: false, children: [
3402
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", children: [
3403
+ /* @__PURE__ */ jsxRuntime.jsx(PopoverTrigger, { asChild: true, disabled, children: /* @__PURE__ */ jsxRuntime.jsxs(
3404
+ "button",
3405
+ {
3406
+ type: "button",
3407
+ className: cn(
3408
+ "flex w-full items-center justify-between rounded-md border-2 bg-background px-3 text-sm transition-colors",
3409
+ "focus:outline-none focus:border-primary",
3410
+ "disabled:cursor-not-allowed disabled:opacity-50",
3411
+ label ? "h-12 pt-4 pb-2" : "h-9 py-2",
3412
+ error ? "border-red-500" : "border-border",
3413
+ open && !error && "border-primary"
3414
+ ),
3415
+ children: [
3416
+ selected ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 truncate", children: [
3417
+ selected.icon && /* @__PURE__ */ jsxRuntime.jsx(selected.icon, { className: "h-4 w-4 shrink-0 text-muted-foreground" }),
3418
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate", children: selected.label })
3419
+ ] }) : /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground", children: placeholder }),
3420
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronDown, { className: "h-4 w-4 shrink-0 opacity-50" })
3421
+ ]
3422
+ }
3423
+ ) }),
3424
+ label && /* @__PURE__ */ jsxRuntime.jsxs(
3425
+ "label",
3426
+ {
3427
+ className: cn(
3428
+ "absolute left-3 top-[-6px] text-xs font-medium bg-background px-1 pointer-events-none",
3429
+ error ? "text-red-500" : "text-foreground"
3430
+ ),
3431
+ children: [
3432
+ label,
3433
+ required && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-red-500 ml-0.5", children: "*" })
3434
+ ]
3435
+ }
3436
+ )
3437
+ ] }),
3438
+ /* @__PURE__ */ jsxRuntime.jsxs(
3439
+ PopoverContent,
3440
+ {
3441
+ align: "start",
3442
+ className: "p-0 overflow-hidden",
3443
+ style: {
3444
+ width: "var(--radix-popover-trigger-width)",
3445
+ maxHeight: "var(--radix-popover-content-available-height)"
3446
+ },
3447
+ children: [
3448
+ searchable && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 px-3 py-2 border-b border-border", children: [
3449
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Search, { className: "h-4 w-4 shrink-0 text-muted-foreground" }),
3450
+ /* @__PURE__ */ jsxRuntime.jsx(
3451
+ "input",
3452
+ {
3453
+ ref: searchRef,
3454
+ type: "text",
3455
+ value: search,
3456
+ onChange: (e) => handleSearch(e.target.value),
3457
+ placeholder: searchPlaceholder,
3458
+ className: "flex-1 bg-transparent text-sm outline-none placeholder:text-muted-foreground"
3459
+ }
3460
+ )
3461
+ ] }),
3462
+ /* @__PURE__ */ jsxRuntime.jsx(
3463
+ "div",
3464
+ {
3465
+ ref: listRef,
3466
+ className: "overflow-y-auto overscroll-contain",
3467
+ style: { maxHeight: searchable ? "calc(var(--radix-popover-content-available-height) - 45px)" : "var(--radix-popover-content-available-height)" },
3468
+ onScroll: handleScroll,
3469
+ children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "divide-y divide-border/50 p-2", children: [
3470
+ filteredOptions.length === 0 && !loading ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "py-4 text-center text-sm text-muted-foreground", children: emptyText }) : filteredOptions.map((option) => {
3471
+ const isSelected = value === option.value;
3472
+ const isDisabled = option.disabled || disabled;
3473
+ return /* @__PURE__ */ jsxRuntime.jsxs(
3474
+ "button",
3475
+ {
3476
+ type: "button",
3477
+ disabled: isDisabled,
3478
+ onClick: () => {
3479
+ onChange(option.value);
3480
+ setOpen(false);
3481
+ },
3482
+ className: cn(
3483
+ "flex w-full items-center gap-3 p-3 text-left transition-all",
3484
+ "cursor-pointer hover:bg-accent",
3485
+ isSelected && "bg-primary/5",
3486
+ isDisabled && "cursor-not-allowed opacity-50 hover:bg-transparent"
3487
+ ),
3488
+ children: [
3489
+ option.icon && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-10 w-10 shrink-0 items-center justify-center rounded-full bg-primary/10", children: /* @__PURE__ */ jsxRuntime.jsx(option.icon, { className: "h-5 w-5 text-primary" }) }),
3490
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0 flex-1", children: [
3491
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-medium leading-tight", children: option.label }),
3492
+ option.description && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-0.5 text-xs leading-tight text-muted-foreground", children: option.description })
3493
+ ] }),
3494
+ isSelected && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex h-5 w-5 shrink-0 items-center justify-center rounded-full bg-primary text-primary-foreground", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Check, { className: "h-3 w-3" }) })
3495
+ ]
3496
+ },
3497
+ option.value
3498
+ );
3499
+ }),
3500
+ loading && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-center py-3", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "h-4 w-4 animate-spin text-muted-foreground" }) })
3501
+ ] })
3502
+ }
3503
+ )
3504
+ ]
3505
+ }
3506
+ )
3507
+ ] });
3508
+ }
3304
3509
  function FormTextarea({
3305
3510
  name,
3306
3511
  label,
@@ -3862,22 +4067,6 @@ var DropdownMenuShortcut = ({
3862
4067
  );
3863
4068
  };
3864
4069
  DropdownMenuShortcut.displayName = "DropdownMenuShortcut";
3865
- var Popover = PopoverPrimitive__namespace.Root;
3866
- var PopoverTrigger = PopoverPrimitive__namespace.Trigger;
3867
- var PopoverContent = React10__namespace.forwardRef(({ className, align = "center", sideOffset = 4, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(PopoverPrimitive__namespace.Portal, { children: /* @__PURE__ */ jsxRuntime.jsx(
3868
- PopoverPrimitive__namespace.Content,
3869
- {
3870
- ref,
3871
- align,
3872
- sideOffset,
3873
- className: cn(
3874
- "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",
3875
- className
3876
- ),
3877
- ...props
3878
- }
3879
- ) }));
3880
- PopoverContent.displayName = PopoverPrimitive__namespace.Content.displayName;
3881
4070
  var TooltipProvider = TooltipPrimitive__namespace.Provider;
3882
4071
  var TooltipRoot = TooltipPrimitive__namespace.Root;
3883
4072
  var TooltipTrigger = TooltipPrimitive__namespace.Trigger;