@facter/ds-core 1.13.0 → 1.15.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,10 +17,10 @@ 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 * as PopoverPrimitive from '@radix-ui/react-popover';
21
20
  import * as RadioGroupPrimitive from '@radix-ui/react-radio-group';
22
21
  import * as AvatarPrimitive from '@radix-ui/react-avatar';
23
22
  import * as DropdownMenuPrimitive from '@radix-ui/react-dropdown-menu';
23
+ import * as PopoverPrimitive from '@radix-ui/react-popover';
24
24
  import * as TooltipPrimitive from '@radix-ui/react-tooltip';
25
25
  import * as ScrollAreaPrimitive from '@radix-ui/react-scroll-area';
26
26
  import * as SeparatorPrimitive from '@radix-ui/react-separator';
@@ -3208,22 +3208,6 @@ function FormInput({
3208
3208
  ) });
3209
3209
  }
3210
3210
  FormInput.displayName = "Form.Input";
3211
- var Popover = PopoverPrimitive.Root;
3212
- var PopoverTrigger = PopoverPrimitive.Trigger;
3213
- var PopoverContent = React10.forwardRef(({ className, align = "center", sideOffset = 4, ...props }, ref) => /* @__PURE__ */ jsx(PopoverPrimitive.Portal, { children: /* @__PURE__ */ jsx(
3214
- PopoverPrimitive.Content,
3215
- {
3216
- ref,
3217
- align,
3218
- sideOffset,
3219
- className: cn(
3220
- "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",
3221
- className
3222
- ),
3223
- ...props
3224
- }
3225
- ) }));
3226
- PopoverContent.displayName = PopoverPrimitive.Content.displayName;
3227
3211
  function FormSelect({
3228
3212
  name,
3229
3213
  label,
@@ -3331,6 +3315,7 @@ function CardSelect({
3331
3315
  const [open, setOpen] = React10.useState(false);
3332
3316
  const [search, setSearch] = React10.useState("");
3333
3317
  const selected = options.find((o) => o.value === value);
3318
+ const containerRef = React10.useRef(null);
3334
3319
  const listRef = React10.useRef(null);
3335
3320
  const searchRef = React10.useRef(null);
3336
3321
  const filteredOptions = React10.useMemo(() => {
@@ -3347,6 +3332,24 @@ function CardSelect({
3347
3332
  },
3348
3333
  [onSearch]
3349
3334
  );
3335
+ React10.useEffect(() => {
3336
+ if (!open) return;
3337
+ const handleClickOutside = (e) => {
3338
+ if (containerRef.current && !containerRef.current.contains(e.target)) {
3339
+ setOpen(false);
3340
+ }
3341
+ };
3342
+ document.addEventListener("mousedown", handleClickOutside);
3343
+ return () => document.removeEventListener("mousedown", handleClickOutside);
3344
+ }, [open]);
3345
+ React10.useEffect(() => {
3346
+ if (!open) return;
3347
+ const handleEscape = (e) => {
3348
+ if (e.key === "Escape") setOpen(false);
3349
+ };
3350
+ document.addEventListener("keydown", handleEscape);
3351
+ return () => document.removeEventListener("keydown", handleEscape);
3352
+ }, [open]);
3350
3353
  React10.useEffect(() => {
3351
3354
  if (!open) {
3352
3355
  setSearch("");
@@ -3358,12 +3361,23 @@ function CardSelect({
3358
3361
  setTimeout(() => searchRef.current?.focus(), 0);
3359
3362
  }
3360
3363
  }, [open, searchable]);
3361
- const handleWheel = React10.useCallback((e) => {
3362
- e.stopPropagation();
3364
+ React10.useEffect(() => {
3363
3365
  const el = listRef.current;
3364
- if (!el) return;
3365
- el.scrollTop += e.deltaY;
3366
- }, []);
3366
+ if (!el || !open) return;
3367
+ const handleWheel = (e) => {
3368
+ const { scrollTop, scrollHeight, clientHeight } = el;
3369
+ const isScrollable = scrollHeight > clientHeight;
3370
+ if (!isScrollable) return;
3371
+ e.preventDefault();
3372
+ e.stopPropagation();
3373
+ el.scrollTop = Math.max(
3374
+ 0,
3375
+ Math.min(scrollTop + e.deltaY, scrollHeight - clientHeight)
3376
+ );
3377
+ };
3378
+ el.addEventListener("wheel", handleWheel, { passive: false, capture: true });
3379
+ return () => el.removeEventListener("wheel", handleWheel, { capture: true });
3380
+ }, [open, filteredOptions]);
3367
3381
  const handleScroll = React10.useCallback(() => {
3368
3382
  if (!onLoadMore || !hasMore || loading) return;
3369
3383
  const el = listRef.current;
@@ -3373,111 +3387,100 @@ function CardSelect({
3373
3387
  onLoadMore();
3374
3388
  }
3375
3389
  }, [onLoadMore, hasMore, loading]);
3376
- return /* @__PURE__ */ jsxs(Popover, { open, onOpenChange: setOpen, modal: false, children: [
3377
- /* @__PURE__ */ jsxs("div", { className: "relative", children: [
3378
- /* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, disabled, children: /* @__PURE__ */ jsxs(
3379
- "button",
3380
- {
3381
- type: "button",
3382
- className: cn(
3383
- "flex w-full items-center justify-between rounded-md border-2 bg-background px-3 text-sm transition-colors",
3384
- "focus:outline-none focus:border-primary",
3385
- "disabled:cursor-not-allowed disabled:opacity-50",
3386
- label ? "h-12 pt-4 pb-2" : "h-9 py-2",
3387
- error ? "border-red-500" : "border-border",
3388
- open && !error && "border-primary"
3389
- ),
3390
- children: [
3391
- selected ? /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 truncate", children: [
3392
- selected.icon && /* @__PURE__ */ jsx(selected.icon, { className: "h-4 w-4 shrink-0 text-muted-foreground" }),
3393
- /* @__PURE__ */ jsx("span", { className: "truncate", children: selected.label })
3394
- ] }) : /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: placeholder }),
3395
- /* @__PURE__ */ jsx(ChevronDown, { className: "h-4 w-4 shrink-0 opacity-50" })
3396
- ]
3397
- }
3398
- ) }),
3399
- label && /* @__PURE__ */ jsxs(
3400
- "label",
3401
- {
3402
- className: cn(
3403
- "absolute left-3 top-[-6px] text-xs font-medium bg-background px-1 pointer-events-none",
3404
- error ? "text-red-500" : "text-foreground"
3405
- ),
3406
- children: [
3407
- label,
3408
- required && /* @__PURE__ */ jsx("span", { className: "text-red-500 ml-0.5", children: "*" })
3409
- ]
3410
- }
3411
- )
3412
- ] }),
3390
+ return /* @__PURE__ */ jsxs("div", { ref: containerRef, className: "relative", children: [
3413
3391
  /* @__PURE__ */ jsxs(
3414
- PopoverContent,
3392
+ "button",
3415
3393
  {
3416
- align: "start",
3417
- className: "p-0 overflow-hidden",
3418
- style: {
3419
- width: "var(--radix-popover-trigger-width)"
3420
- },
3394
+ type: "button",
3395
+ disabled,
3396
+ onClick: () => setOpen((prev) => !prev),
3397
+ className: cn(
3398
+ "flex w-full items-center justify-between rounded-md border-2 bg-background px-3 text-sm transition-colors",
3399
+ "focus:outline-none focus:border-primary",
3400
+ "disabled:cursor-not-allowed disabled:opacity-50",
3401
+ label ? "h-12 pt-4 pb-2" : "h-9 py-2",
3402
+ error ? "border-red-500" : "border-border",
3403
+ open && !error && "border-primary"
3404
+ ),
3421
3405
  children: [
3422
- searchable && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 px-3 py-2 border-b border-border", children: [
3423
- /* @__PURE__ */ jsx(Search, { className: "h-4 w-4 shrink-0 text-muted-foreground" }),
3424
- /* @__PURE__ */ jsx(
3425
- "input",
3426
- {
3427
- ref: searchRef,
3428
- type: "text",
3429
- value: search,
3430
- onChange: (e) => handleSearch(e.target.value),
3431
- placeholder: searchPlaceholder,
3432
- className: "flex-1 bg-transparent text-sm outline-none placeholder:text-muted-foreground"
3433
- }
3434
- )
3435
- ] }),
3436
- /* @__PURE__ */ jsx(
3437
- "div",
3438
- {
3439
- ref: listRef,
3440
- className: "overflow-y-auto overscroll-contain max-h-[300px]",
3441
- onScroll: handleScroll,
3442
- onWheel: handleWheel,
3443
- children: /* @__PURE__ */ jsxs("div", { className: "divide-y divide-border/50 p-2", children: [
3444
- filteredOptions.length === 0 && !loading ? /* @__PURE__ */ jsx("p", { className: "py-4 text-center text-sm text-muted-foreground", children: emptyText }) : filteredOptions.map((option) => {
3445
- const isSelected = value === option.value;
3446
- const isDisabled = option.disabled || disabled;
3447
- return /* @__PURE__ */ jsxs(
3448
- "button",
3449
- {
3450
- type: "button",
3451
- disabled: isDisabled,
3452
- onClick: () => {
3453
- onChange(option.value);
3454
- setOpen(false);
3455
- },
3456
- className: cn(
3457
- "flex w-full items-center gap-3 p-3 text-left transition-all",
3458
- "cursor-pointer hover:bg-accent",
3459
- isSelected && "bg-primary/5",
3460
- isDisabled && "cursor-not-allowed opacity-50 hover:bg-transparent"
3461
- ),
3462
- children: [
3463
- option.icon && /* @__PURE__ */ jsx("div", { className: "flex h-10 w-10 shrink-0 items-center justify-center rounded-full bg-primary/10", children: /* @__PURE__ */ jsx(option.icon, { className: "h-5 w-5 text-primary" }) }),
3464
- /* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
3465
- /* @__PURE__ */ jsx("p", { className: "text-sm font-medium leading-tight", children: option.label }),
3466
- option.description && /* @__PURE__ */ jsx("p", { className: "mt-0.5 text-xs leading-tight text-muted-foreground", children: option.description })
3467
- ] }),
3468
- 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" }) })
3469
- ]
3470
- },
3471
- option.value
3472
- );
3473
- }),
3474
- 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" }) })
3475
- ] })
3476
- }
3477
- )
3406
+ selected ? /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 truncate", children: [
3407
+ selected.icon && /* @__PURE__ */ jsx(selected.icon, { className: "h-4 w-4 shrink-0 text-muted-foreground" }),
3408
+ /* @__PURE__ */ jsx("span", { className: "truncate", children: selected.label })
3409
+ ] }) : /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: placeholder }),
3410
+ /* @__PURE__ */ jsx(ChevronDown, { className: cn("h-4 w-4 shrink-0 opacity-50 transition-transform", open && "rotate-180") })
3478
3411
  ]
3479
3412
  }
3480
- )
3413
+ ),
3414
+ label && /* @__PURE__ */ jsxs(
3415
+ "label",
3416
+ {
3417
+ className: cn(
3418
+ "absolute left-3 top-[-6px] text-xs font-medium bg-background px-1 pointer-events-none",
3419
+ error ? "text-red-500" : "text-foreground"
3420
+ ),
3421
+ children: [
3422
+ label,
3423
+ required && /* @__PURE__ */ jsx("span", { className: "text-red-500 ml-0.5", children: "*" })
3424
+ ]
3425
+ }
3426
+ ),
3427
+ open && /* @__PURE__ */ jsxs("div", { className: "absolute left-0 top-full z-50 mt-1 w-full 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", children: [
3428
+ searchable && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 px-3 py-2 border-b border-border", children: [
3429
+ /* @__PURE__ */ jsx(Search, { className: "h-4 w-4 shrink-0 text-muted-foreground" }),
3430
+ /* @__PURE__ */ jsx(
3431
+ "input",
3432
+ {
3433
+ ref: searchRef,
3434
+ type: "text",
3435
+ value: search,
3436
+ onChange: (e) => handleSearch(e.target.value),
3437
+ placeholder: searchPlaceholder,
3438
+ className: "flex-1 bg-transparent text-sm outline-none placeholder:text-muted-foreground"
3439
+ }
3440
+ )
3441
+ ] }),
3442
+ /* @__PURE__ */ jsx(
3443
+ "div",
3444
+ {
3445
+ ref: listRef,
3446
+ className: "overflow-y-auto overscroll-contain max-h-[300px]",
3447
+ onScroll: handleScroll,
3448
+ children: /* @__PURE__ */ jsxs("div", { className: "divide-y divide-border/50 p-2", children: [
3449
+ filteredOptions.length === 0 && !loading ? /* @__PURE__ */ jsx("p", { className: "py-4 text-center text-sm text-muted-foreground", children: emptyText }) : filteredOptions.map((option) => {
3450
+ const isSelected = value === option.value;
3451
+ const isDisabled = option.disabled || disabled;
3452
+ return /* @__PURE__ */ jsxs(
3453
+ "button",
3454
+ {
3455
+ type: "button",
3456
+ disabled: isDisabled,
3457
+ onClick: () => {
3458
+ onChange(option.value);
3459
+ setOpen(false);
3460
+ },
3461
+ className: cn(
3462
+ "flex w-full items-center gap-3 p-3 text-left transition-all",
3463
+ "cursor-pointer hover:bg-accent",
3464
+ isSelected && "bg-primary/5",
3465
+ isDisabled && "cursor-not-allowed opacity-50 hover:bg-transparent"
3466
+ ),
3467
+ children: [
3468
+ option.icon && /* @__PURE__ */ jsx("div", { className: "flex h-10 w-10 shrink-0 items-center justify-center rounded-full bg-primary/10", children: /* @__PURE__ */ jsx(option.icon, { className: "h-5 w-5 text-primary" }) }),
3469
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
3470
+ /* @__PURE__ */ jsx("p", { className: "text-sm font-medium leading-tight", children: option.label }),
3471
+ option.description && /* @__PURE__ */ jsx("p", { className: "mt-0.5 text-xs leading-tight text-muted-foreground", children: option.description })
3472
+ ] }),
3473
+ 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" }) })
3474
+ ]
3475
+ },
3476
+ option.value
3477
+ );
3478
+ }),
3479
+ 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" }) })
3480
+ ] })
3481
+ }
3482
+ )
3483
+ ] })
3481
3484
  ] });
3482
3485
  }
3483
3486
  function FormTextarea({
@@ -4041,6 +4044,22 @@ var DropdownMenuShortcut = ({
4041
4044
  );
4042
4045
  };
4043
4046
  DropdownMenuShortcut.displayName = "DropdownMenuShortcut";
4047
+ var Popover = PopoverPrimitive.Root;
4048
+ var PopoverTrigger = PopoverPrimitive.Trigger;
4049
+ var PopoverContent = React10.forwardRef(({ className, align = "center", sideOffset = 4, ...props }, ref) => /* @__PURE__ */ jsx(PopoverPrimitive.Portal, { children: /* @__PURE__ */ jsx(
4050
+ PopoverPrimitive.Content,
4051
+ {
4052
+ ref,
4053
+ align,
4054
+ sideOffset,
4055
+ className: cn(
4056
+ "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",
4057
+ className
4058
+ ),
4059
+ ...props
4060
+ }
4061
+ ) }));
4062
+ PopoverContent.displayName = PopoverPrimitive.Content.displayName;
4044
4063
  var TooltipProvider = TooltipPrimitive.Provider;
4045
4064
  var TooltipRoot = TooltipPrimitive.Root;
4046
4065
  var TooltipTrigger = TooltipPrimitive.Trigger;