@bizapps/design-system-testing 1.9.0 → 1.10.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/main.js CHANGED
@@ -1,10 +1,10 @@
1
1
  "use client";
2
- import { SearchIcon, BellIcon, CircleHelpIcon, ChevronDownIcon, ChevronLeftIcon, MoreHorizontalIcon, ChevronRightIcon, MinusIcon, CheckIcon, LoaderCircle, ChevronUpIcon, X, ChevronLeft, ChevronRight, ExternalLinkIcon, ChevronsUpDownIcon, XIcon, UploadIcon, Loader2Icon, LoaderIcon, PipetteIcon, CalendarIcon, MenuIcon, Pencil, Clock, Funnel, ArrowUp, ArrowDown, ArrowUpDown, Ellipsis, Rows2, Eye, Search, House, Plane, Wallet, LayoutDashboardIcon, InfoIcon, TriangleAlertIcon, OctagonXIcon, CircleCheckIcon } from 'lucide-react';
2
+ import { SearchIcon, BellIcon, CircleHelpIcon, ChevronDownIcon, ChevronLeftIcon, MoreHorizontalIcon, ChevronRightIcon, MinusIcon, CheckIcon, LoaderCircle, ChevronUpIcon, X, ChevronLeft, ChevronRight, ExternalLinkIcon, ChevronsUpDownIcon, XIcon, UploadIcon, Loader2Icon, LoaderIcon, PipetteIcon, CalendarIcon, MenuIcon, Pencil, Clock, Funnel, ArrowUp, ArrowDown, ArrowUpDown, Ellipsis, Rows2, Eye, Search, BrushCleaning, House, Plane, Wallet, LayoutDashboardIcon, InfoIcon, TriangleAlertIcon, OctagonXIcon, CircleCheckIcon } from 'lucide-react';
3
3
  import { cva } from 'class-variance-authority';
4
4
  import { clsx } from 'clsx';
5
5
  import { twMerge } from 'tailwind-merge';
6
6
  import { jsx, jsxs, Fragment as Fragment$1 } from 'react/jsx-runtime';
7
- import { createContext, memo, useRef, useState, useMemo, useCallback, useEffect, forwardRef, Fragment, useId, useContext, useLayoutEffect } from 'react';
7
+ import { createContext, memo, useRef, useState, useMemo, useCallback, useEffect, forwardRef, Fragment, useId, useContext, useLayoutEffect, isValidElement, useImperativeHandle, cloneElement } from 'react';
8
8
  import { Avatar as Avatar$1, Checkbox as Checkbox$1, Slot, Select as Select$1, Label as Label$1, Popover as Popover$1, Toggle as Toggle$1, ToggleGroup, Separator as Separator$1, Progress as Progress$1, Slider as Slider$1, Switch as Switch$1, Tabs as Tabs$1, Tooltip as Tooltip$1, Dialog as Dialog$1, AlertDialog as AlertDialog$1, Accordion as Accordion$1, DropdownMenu as DropdownMenu$1, RadioGroup as RadioGroup$1, NavigationMenu as NavigationMenu$1 } from 'radix-ui';
9
9
  import { isSameDay, isToday, format, addDays } from 'date-fns';
10
10
  import useEmblaCarousel from 'embla-carousel-react';
@@ -25,8 +25,10 @@ import { Temporal } from 'temporal-polyfill';
25
25
  import { useForm } from 'react-hook-form';
26
26
  export { Controller, useFieldArray } from 'react-hook-form';
27
27
  import { zodResolver } from '@hookform/resolvers/zod';
28
+ import confetti from 'canvas-confetti';
28
29
  import { flexRender, useReactTable, getFilteredRowModel, getSortedRowModel, getCoreRowModel } from '@tanstack/react-table';
29
30
  export { createColumnHelper } from '@tanstack/react-table';
31
+ import SignaturePadLib from 'signature_pad';
30
32
 
31
33
  // src/components/ui/Alert/alert.tsx
32
34
  function cn(...inputs) {
@@ -927,10 +929,11 @@ function Input({ className, type, ...props }) {
927
929
  type,
928
930
  "data-slot": "input",
929
931
  className: cn(
930
- "border-input flex h-9 w-full min-w-0 rounded-md border bg-transparent px-3 py-1 text-sm shadow-xs transition-[color,box-shadow] outline-none",
931
- "placeholder:text-muted-foreground",
932
+ "border-input h-9 w-full min-w-0 rounded-md border bg-transparent px-3 py-1 text-base shadow-xs transition-[border-color,box-shadow] outline-hidden md:text-sm",
933
+ "placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground",
934
+ "file:text-foreground file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium",
932
935
  "focus-visible:border-ring focus-visible:ring-ring/30 focus-visible:ring-3",
933
- "disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50",
936
+ "disabled:cursor-not-allowed disabled:opacity-50",
934
937
  "aria-invalid:border-destructive aria-invalid:ring-destructive/20 aria-invalid:ring-3",
935
938
  "dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40",
936
939
  className
@@ -1676,31 +1679,37 @@ function SelectContent({
1676
1679
  className,
1677
1680
  children,
1678
1681
  position = "popper",
1682
+ maxHeight,
1683
+ style,
1679
1684
  ...props
1680
1685
  }) {
1686
+ const resolvedMaxHeight = typeof maxHeight === "number" ? `${maxHeight}px` : maxHeight;
1681
1687
  return /* @__PURE__ */ jsx(Select$1.Portal, { children: /* @__PURE__ */ jsxs(
1682
1688
  Select$1.Content,
1683
1689
  {
1684
1690
  "data-slot": "select-content",
1685
1691
  className: cn(
1686
- "border-border bg-popover text-popover-foreground relative z-50 max-h-[--radix-select-content-available-height] min-w-[8rem] overflow-x-hidden overflow-y-auto rounded-md border shadow-md",
1692
+ "border-border bg-popover text-popover-foreground relative z-50 max-h-(--radix-select-content-available-height) min-w-[8rem] origin-(--radix-select-content-transform-origin) overflow-hidden rounded-md border shadow-md",
1687
1693
  "data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95",
1688
1694
  "data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95",
1689
1695
  "data-[side=bottom]:translate-y-1 data-[side=top]:-translate-y-1",
1690
- position === "popper" && "w-[--radix-select-trigger-width]",
1696
+ position === "popper" && "w-(--radix-select-trigger-width)",
1691
1697
  className
1692
1698
  ),
1693
1699
  position,
1700
+ style: {
1701
+ ...resolvedMaxHeight ? {
1702
+ maxHeight: `min(${resolvedMaxHeight}, var(--radix-select-content-available-height))`
1703
+ } : null,
1704
+ ...style
1705
+ },
1694
1706
  ...props,
1695
1707
  children: [
1696
1708
  /* @__PURE__ */ jsx(SelectScrollUpButton, {}),
1697
1709
  /* @__PURE__ */ jsx(
1698
1710
  Select$1.Viewport,
1699
1711
  {
1700
- className: cn(
1701
- "p-1",
1702
- position === "popper" && "h-[--radix-select-trigger-height]"
1703
- ),
1712
+ className: cn("scroll-my-1 p-1", position === "popper" && "w-full"),
1704
1713
  children
1705
1714
  }
1706
1715
  ),
@@ -4362,6 +4371,7 @@ ColorPickerPresets.displayName = "ColorPickerPresets";
4362
4371
  function Calendar({
4363
4372
  className,
4364
4373
  classNames,
4374
+ modifiersClassNames,
4365
4375
  showOutsideDays = true,
4366
4376
  captionLayout = "dropdown",
4367
4377
  buttonVariant = "ghost",
@@ -4373,6 +4383,36 @@ function Calendar({
4373
4383
  ...props
4374
4384
  }) {
4375
4385
  const defaultClassNames = getDefaultClassNames();
4386
+ const resolvedComponents = useMemo(
4387
+ () => ({
4388
+ Root: ({ className: className2, rootRef, ...rootProps }) => {
4389
+ return /* @__PURE__ */ jsx(
4390
+ "div",
4391
+ {
4392
+ "data-slot": "calendar",
4393
+ ref: rootRef,
4394
+ className: cn(className2),
4395
+ ...rootProps
4396
+ }
4397
+ );
4398
+ },
4399
+ Chevron: ({ className: className2, orientation, ...chevronProps }) => {
4400
+ if (orientation === "left") {
4401
+ return /* @__PURE__ */ jsx(ChevronLeftIcon, { className: cn("size-4", className2), ...chevronProps });
4402
+ }
4403
+ if (orientation === "right") {
4404
+ return /* @__PURE__ */ jsx(ChevronRightIcon, { className: cn("size-4", className2), ...chevronProps });
4405
+ }
4406
+ return /* @__PURE__ */ jsx(ChevronDownIcon, { className: cn("size-4", className2), ...chevronProps });
4407
+ },
4408
+ DayButton: (dayButtonProps) => /* @__PURE__ */ jsx(CalendarDayButton, { locale, ...dayButtonProps }),
4409
+ WeekNumber: ({ children, ...weekNumberProps }) => {
4410
+ return /* @__PURE__ */ jsx("td", { ...weekNumberProps, children: /* @__PURE__ */ jsx("div", { className: "flex size-(--cell-size) items-center justify-center text-center", children }) });
4411
+ },
4412
+ ...components
4413
+ }),
4414
+ [locale, components]
4415
+ );
4376
4416
  return /* @__PURE__ */ jsx(
4377
4417
  DayPicker,
4378
4418
  {
@@ -4464,33 +4504,13 @@ function Calendar({
4464
4504
  hidden: cn("invisible", defaultClassNames.hidden),
4465
4505
  ...classNames
4466
4506
  },
4467
- components: {
4468
- Root: ({ className: className2, rootRef, ...props2 }) => {
4469
- return /* @__PURE__ */ jsx(
4470
- "div",
4471
- {
4472
- "data-slot": "calendar",
4473
- ref: rootRef,
4474
- className: cn(className2),
4475
- ...props2
4476
- }
4477
- );
4478
- },
4479
- Chevron: ({ className: className2, orientation, ...props2 }) => {
4480
- if (orientation === "left") {
4481
- return /* @__PURE__ */ jsx(ChevronLeftIcon, { className: cn("size-4", className2), ...props2 });
4482
- }
4483
- if (orientation === "right") {
4484
- return /* @__PURE__ */ jsx(ChevronRightIcon, { className: cn("size-4", className2), ...props2 });
4485
- }
4486
- return /* @__PURE__ */ jsx(ChevronDownIcon, { className: cn("size-4", className2), ...props2 });
4487
- },
4488
- DayButton: ({ ...props2 }) => /* @__PURE__ */ jsx(CalendarDayButton, { locale, ...props2 }),
4489
- WeekNumber: ({ children, ...props2 }) => {
4490
- return /* @__PURE__ */ jsx("td", { ...props2, children: /* @__PURE__ */ jsx("div", { className: "flex size-(--cell-size) items-center justify-center text-center", children }) });
4491
- },
4492
- ...components
4507
+ modifiersClassNames: {
4508
+ preview_start: "relative isolate z-0 rounded-l-md bg-primary/8 after:absolute after:inset-y-0 after:right-0 after:w-3 after:bg-primary/8",
4509
+ preview_middle: "rounded-none bg-primary/8",
4510
+ preview_end: "relative isolate z-0 rounded-r-md bg-primary/8 after:absolute after:inset-y-0 after:left-0 after:w-3 after:bg-primary/8",
4511
+ ...modifiersClassNames
4493
4512
  },
4513
+ components: resolvedComponents,
4494
4514
  ...props
4495
4515
  }
4496
4516
  );
@@ -4507,6 +4527,9 @@ function CalendarDayButton({
4507
4527
  useEffect(() => {
4508
4528
  if (modifiers.focused) ref.current?.focus();
4509
4529
  }, [modifiers.focused]);
4530
+ const isPreviewEdge = Boolean(
4531
+ (modifiers.preview_start || modifiers.preview_end) && !modifiers.selected
4532
+ );
4510
4533
  return /* @__PURE__ */ jsx(
4511
4534
  Button,
4512
4535
  {
@@ -4518,8 +4541,10 @@ function CalendarDayButton({
4518
4541
  "data-range-start": modifiers.range_start,
4519
4542
  "data-range-end": modifiers.range_end,
4520
4543
  "data-range-middle": modifiers.range_middle,
4544
+ "data-preview-middle": Boolean(modifiers.preview_middle),
4545
+ "data-preview-edge": isPreviewEdge,
4521
4546
  className: cn(
4522
- "group-data-[focused=true]/day:ring-primary/40 data-[range-end=true]:!bg-primary data-[range-end=true]:text-primary-foreground data-[range-middle=true]:!bg-primary/8 data-[range-middle=true]:text-foreground data-[range-start=true]:!bg-primary data-[range-start=true]:text-primary-foreground data-[selected-single=true]:!bg-primary data-[selected-single=true]:text-primary-foreground relative isolate z-10 flex aspect-square size-auto w-full min-w-(--cell-size) flex-col gap-1 !rounded-md border-0 !bg-transparent leading-none font-normal transition-colors duration-150 group-data-[focused=true]/day:relative group-data-[focused=true]/day:z-10 group-data-[focused=true]/day:rounded-md group-data-[focused=true]/day:ring-[2px] hover:!bg-transparent data-[range-end=true]:rounded-md data-[range-end=true]:rounded-r-md data-[range-middle=true]:rounded-none data-[range-start=true]:rounded-md data-[range-start=true]:rounded-l-md data-[selected-single=true]:rounded-md [&>span]:text-xs [&>span]:font-medium [&>span]:opacity-75",
4547
+ "group-data-[focused=true]/day:ring-primary/40 data-[preview-edge=true]:ring-primary/50 data-[preview-edge=true]:text-primary data-[preview-middle=true]:text-primary data-[range-end=true]:!bg-primary data-[range-end=true]:text-primary-foreground data-[range-middle=true]:!bg-primary/8 data-[range-middle=true]:text-foreground data-[range-start=true]:!bg-primary data-[range-start=true]:text-primary-foreground data-[selected-single=true]:!bg-primary data-[selected-single=true]:text-primary-foreground relative isolate z-10 flex aspect-square size-auto w-full min-w-(--cell-size) flex-col gap-1 !rounded-md border-0 !bg-transparent leading-none font-normal transition-colors duration-150 group-data-[focused=true]/day:relative group-data-[focused=true]/day:z-10 group-data-[focused=true]/day:rounded-md group-data-[focused=true]/day:ring-[2px] hover:!bg-transparent data-[preview-edge=true]:rounded-md data-[preview-edge=true]:font-medium data-[preview-edge=true]:ring-[2px] data-[preview-middle=true]:font-medium data-[range-end=true]:rounded-md data-[range-end=true]:rounded-r-md data-[range-middle=true]:rounded-none data-[range-start=true]:rounded-md data-[range-start=true]:rounded-l-md data-[selected-single=true]:rounded-md [&>span]:text-xs [&>span]:font-medium [&>span]:opacity-75",
4523
4548
  defaultClassNames.day,
4524
4549
  className
4525
4550
  ),
@@ -4527,30 +4552,79 @@ function CalendarDayButton({
4527
4552
  }
4528
4553
  );
4529
4554
  }
4530
- function DatePicker({
4531
- mode = "single",
4532
- selected,
4533
- onSelect,
4534
- placeholder = "Pick a date",
4535
- disabled = false,
4536
- className,
4537
- fromDate,
4538
- toDate
4539
- }) {
4555
+ function DatePicker(props) {
4556
+ const {
4557
+ placeholder = "Pick a date",
4558
+ disabled = false,
4559
+ className,
4560
+ fromDate,
4561
+ toDate,
4562
+ numberOfMonths = 1
4563
+ } = props;
4540
4564
  const [open, setOpen] = useState(false);
4565
+ const [awaitingRangeEnd, setAwaitingRangeEnd] = useState(false);
4566
+ const [previewDate, setPreviewDate] = useState(void 0);
4541
4567
  const label = useMemo(() => {
4542
- if (mode === "range") {
4543
- const range = selected;
4544
- if (range?.from && range?.to)
4545
- return `${format(range.from, "MMM d, yyyy")} \u2013 ${format(range.to, "MMM d, yyyy")}`;
4546
- if (range?.from) return `${format(range.from, "MMM d, yyyy")} \u2013 \u2026`;
4547
- return placeholder;
4548
- }
4549
- const date = selected;
4550
- return date ? format(date, "MMM d, yyyy") : placeholder;
4551
- }, [mode, selected, placeholder]);
4552
- const hasValue = mode === "range" ? !!selected?.from : !!selected;
4553
- return /* @__PURE__ */ jsxs(Popover, { open, onOpenChange: setOpen, children: [
4568
+ if (props.mode === "range") {
4569
+ const range = props.selected;
4570
+ if (!range?.from) return placeholder;
4571
+ if (!range.to) return `${format(range.from, "MMM d, yyyy")} \u2013 \u2026`;
4572
+ return `${format(range.from, "MMM d, yyyy")} \u2013 ${format(range.to, "MMM d, yyyy")}`;
4573
+ }
4574
+ return props.selected ? format(props.selected, "MMM d, yyyy") : placeholder;
4575
+ }, [props.mode, props.selected, placeholder]);
4576
+ const handleOpenChange = (nextOpen) => {
4577
+ setOpen(nextOpen);
4578
+ if (!nextOpen) {
4579
+ setAwaitingRangeEnd(false);
4580
+ setPreviewDate(void 0);
4581
+ }
4582
+ };
4583
+ const handleRangeSelect = (range, triggerDate) => {
4584
+ if (props.mode !== "range") return;
4585
+ const emit = props.onSelect;
4586
+ if (!awaitingRangeEnd) {
4587
+ setAwaitingRangeEnd(true);
4588
+ emit?.({ from: triggerDate, to: void 0 });
4589
+ return;
4590
+ }
4591
+ emit?.(range);
4592
+ if (!range?.from || !range.to) {
4593
+ setAwaitingRangeEnd(!!range?.from);
4594
+ return;
4595
+ }
4596
+ setAwaitingRangeEnd(false);
4597
+ setPreviewDate(void 0);
4598
+ setOpen(false);
4599
+ };
4600
+ const rangeFrom = props.mode === "range" ? props.selected?.from : void 0;
4601
+ const previewModifiers = useMemo(() => {
4602
+ if (!awaitingRangeEnd || !rangeFrom || !previewDate) return void 0;
4603
+ if (isSameDay(rangeFrom, previewDate)) return void 0;
4604
+ const [start, end] = previewDate < rangeFrom ? [previewDate, rangeFrom] : [rangeFrom, previewDate];
4605
+ return {
4606
+ preview_start: start,
4607
+ preview_middle: { after: start, before: end },
4608
+ preview_end: end
4609
+ };
4610
+ }, [awaitingRangeEnd, rangeFrom, previewDate]);
4611
+ const handleDayPreview = (day, dayModifiers) => {
4612
+ if (!awaitingRangeEnd) return;
4613
+ if (dayModifiers.disabled || dayModifiers.hidden) return;
4614
+ setPreviewDate((current) => current && isSameDay(current, day) ? current : day);
4615
+ };
4616
+ const dateConstraints = useMemo(() => {
4617
+ const disabledMatchers = [];
4618
+ if (fromDate) disabledMatchers.push({ before: fromDate });
4619
+ if (toDate) disabledMatchers.push({ after: toDate });
4620
+ return {
4621
+ ...disabledMatchers.length > 0 && { disabled: disabledMatchers },
4622
+ ...fromDate && { startMonth: fromDate },
4623
+ ...toDate && { endMonth: toDate }
4624
+ };
4625
+ }, [fromDate, toDate]);
4626
+ const hasValue = props.mode === "range" ? !!props.selected?.from : !!props.selected;
4627
+ return /* @__PURE__ */ jsxs(Popover, { open, onOpenChange: handleOpenChange, children: [
4554
4628
  /* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(
4555
4629
  Button,
4556
4630
  {
@@ -4568,36 +4642,36 @@ function DatePicker({
4568
4642
  ]
4569
4643
  }
4570
4644
  ) }),
4571
- /* @__PURE__ */ jsx(PopoverContent, { "data-slot": "date-picker-content", className: "w-auto p-0", align: "start", children: mode === "range" ? /* @__PURE__ */ jsx(
4572
- Calendar,
4573
- {
4574
- ...{
4645
+ /* @__PURE__ */ jsx(PopoverContent, { "data-slot": "date-picker-content", className: "w-auto p-0", align: "start", children: props.mode === "range" ? (
4646
+ // Clearing the preview on the wrapper instead of per day
4647
+ // avoids the blank frame `onDayMouseLeave` would cause when
4648
+ // the pointer travels from one day to the next.
4649
+ /* @__PURE__ */ jsx("div", { onMouseLeave: () => setPreviewDate(void 0), children: /* @__PURE__ */ jsx(
4650
+ Calendar,
4651
+ {
4575
4652
  mode: "range",
4576
- selected,
4577
- onSelect: (range) => {
4578
- onSelect?.(range);
4579
- if (range?.to) setOpen(false);
4580
- },
4581
- fromDate,
4582
- toDate,
4583
- numberOfMonths: 2,
4584
- initialFocus: true
4653
+ selected: props.selected,
4654
+ onSelect: handleRangeSelect,
4655
+ modifiers: previewModifiers,
4656
+ onDayMouseEnter: handleDayPreview,
4657
+ onDayFocus: handleDayPreview,
4658
+ numberOfMonths,
4659
+ autoFocus: true,
4660
+ ...dateConstraints
4585
4661
  }
4586
- }
4662
+ ) })
4587
4663
  ) : /* @__PURE__ */ jsx(
4588
4664
  Calendar,
4589
4665
  {
4590
- ...{
4591
- mode: "single",
4592
- selected,
4593
- onSelect: (date) => {
4594
- onSelect?.(date);
4595
- setOpen(false);
4596
- },
4597
- fromDate,
4598
- toDate,
4599
- initialFocus: true
4600
- }
4666
+ mode: "single",
4667
+ selected: props.selected,
4668
+ onSelect: (date) => {
4669
+ props.onSelect?.(date);
4670
+ setOpen(false);
4671
+ },
4672
+ numberOfMonths,
4673
+ autoFocus: true,
4674
+ ...dateConstraints
4601
4675
  }
4602
4676
  ) })
4603
4677
  ] });
@@ -5039,6 +5113,8 @@ function NavigationMenuIndicator({
5039
5113
  }
5040
5114
  );
5041
5115
  }
5116
+
5117
+ // src/components/authorization/policy.ts
5042
5118
  function matchPermission(pattern, permission) {
5043
5119
  if (pattern === "*") return true;
5044
5120
  if (pattern === permission) return true;
@@ -5054,41 +5130,46 @@ function matchesAny(permissionSet, permission) {
5054
5130
  }
5055
5131
  return false;
5056
5132
  }
5133
+ function can(permissionSet, perms, mode = "any") {
5134
+ const list = Array.isArray(perms) ? perms : [perms];
5135
+ if (list.length === 0) return false;
5136
+ if (permissionSet.has("*")) return true;
5137
+ if (mode === "all") {
5138
+ return list.every((p) => matchesAny(permissionSet, p));
5139
+ }
5140
+ return list.some((p) => matchesAny(permissionSet, p));
5141
+ }
5057
5142
  var AbilityContext = createContext(void 0);
5058
5143
  AbilityContext.displayName = "AbilityContext";
5059
5144
  function AbilityProvider({ children, permissions }) {
5060
5145
  const [permissionSet, setPermissionSet] = useState(
5061
5146
  () => new Set(permissions ?? [])
5062
5147
  );
5063
- const can = useCallback(
5064
- (perms, mode = "any") => {
5065
- const list = Array.isArray(perms) ? perms : [perms];
5066
- if (list.length === 0) return false;
5067
- if (permissionSet.has("*")) return true;
5068
- if (mode === "all") {
5069
- return list.every((p) => matchesAny(permissionSet, p));
5070
- }
5071
- return list.some((p) => matchesAny(permissionSet, p));
5072
- },
5148
+ const [imperativelyResolved, setImperativelyResolved] = useState(false);
5149
+ const status = permissions !== void 0 || imperativelyResolved ? "ready" : "loading";
5150
+ const can2 = useCallback(
5151
+ (perms, mode = "any") => can(permissionSet, perms, mode),
5073
5152
  [permissionSet]
5074
5153
  );
5075
5154
  const setAbilities = useCallback((perms) => {
5076
5155
  setPermissionSet(new Set(perms));
5156
+ setImperativelyResolved(true);
5077
5157
  }, []);
5078
5158
  const clearAbilities = useCallback(() => {
5079
5159
  setPermissionSet(/* @__PURE__ */ new Set());
5160
+ setImperativelyResolved(true);
5080
5161
  }, []);
5081
- const syncedKey = useRef(permissions ? permissions.join("|") : null);
5162
+ const permissionKey = permissions === void 0 ? null : [...permissions].sort().join("|");
5163
+ const syncedKey = useRef(permissionKey);
5082
5164
  useEffect(() => {
5083
- if (permissions === void 0) return;
5084
- const key = permissions.join("|");
5085
- if (key === syncedKey.current) return;
5086
- syncedKey.current = key;
5165
+ if (permissionKey === null) return;
5166
+ if (permissionKey === syncedKey.current) return;
5167
+ syncedKey.current = permissionKey;
5087
5168
  setPermissionSet(new Set(permissions));
5088
- }, [permissions]);
5169
+ }, [permissionKey, permissions]);
5089
5170
  const value = useMemo(
5090
- () => ({ can, setAbilities, clearAbilities }),
5091
- [can, setAbilities, clearAbilities]
5171
+ () => ({ can: can2, setAbilities, clearAbilities, status }),
5172
+ [can2, setAbilities, clearAbilities, status]
5092
5173
  );
5093
5174
  return /* @__PURE__ */ jsx(AbilityContext.Provider, { value, children });
5094
5175
  }
@@ -5100,21 +5181,176 @@ function useAbility() {
5100
5181
  return context;
5101
5182
  }
5102
5183
  function useCan(permissions, mode = "any") {
5103
- const { can } = useAbility();
5104
- return can(permissions, mode);
5184
+ const { can: can2 } = useAbility();
5185
+ return can2(permissions, mode);
5186
+ }
5187
+ function useAbilityStatus() {
5188
+ return useAbility().status;
5189
+ }
5190
+
5191
+ // src/components/authorization/permissions.generated.ts
5192
+ var KNOWN_PERMISSIONS = [];
5193
+
5194
+ // src/components/authorization/permission-catalog.ts
5195
+ var CATALOG = new Set(KNOWN_PERMISSIONS);
5196
+ var warned = /* @__PURE__ */ new Set();
5197
+ function warnUnknownPermission(permission) {
5198
+ if (process.env.NODE_ENV === "production") return;
5199
+ if (CATALOG.size === 0) return;
5200
+ const requested = Array.isArray(permission) ? permission : [permission];
5201
+ for (const entry of requested) {
5202
+ if (entry.includes("*")) continue;
5203
+ if (CATALOG.has(entry) || warned.has(entry)) continue;
5204
+ warned.add(entry);
5205
+ console.warn(
5206
+ `[Can] Unknown permission "${entry}". It is not in the generated permission catalog. Check for a typo, or regenerate the catalog with \`pnpm generate-permissions\`.`
5207
+ );
5208
+ }
5209
+ }
5210
+ var FOCUS_TRAVERSAL_KEYS = /* @__PURE__ */ new Set(["Tab", "Escape"]);
5211
+ function blockInteraction(event) {
5212
+ event.preventDefault();
5213
+ event.stopPropagation();
5214
+ }
5215
+ function blockNonTraversalKey(event) {
5216
+ if (!FOCUS_TRAVERSAL_KEYS.has(event.key)) blockInteraction(event);
5217
+ }
5218
+ var warnedRenderPropMisuse = /* @__PURE__ */ new Set();
5219
+ function warnRenderPropVoidsProps(voided) {
5220
+ if (process.env.NODE_ENV === "production") return;
5221
+ if (voided.length === 0) return;
5222
+ const key = voided.join(",");
5223
+ if (warnedRenderPropMisuse.has(key)) return;
5224
+ warnedRenderPropMisuse.add(key);
5225
+ console.warn(
5226
+ `[Can] \`children\` is a function, so ${voided.map((prop) => `\`${prop}\``).join(", ")} ${voided.length === 1 ? "is" : "are"} ignored. A render prop owns every state: read \`allowed\` and \`status\` and render the disabled, loading and denied cases yourself, or pass an element instead of a function.`
5227
+ );
5228
+ }
5229
+ function disableElement(child) {
5230
+ return cloneElement(child, {
5231
+ "aria-disabled": "true",
5232
+ className: cn(child.props.className, "cursor-not-allowed opacity-50"),
5233
+ onClickCapture: blockInteraction,
5234
+ onKeyDownCapture: blockNonTraversalKey,
5235
+ onKeyUpCapture: blockNonTraversalKey,
5236
+ onPointerDownCapture: blockInteraction,
5237
+ onMouseDownCapture: blockInteraction
5238
+ });
5239
+ }
5240
+ function InteractionFirewall({ children }) {
5241
+ return /* @__PURE__ */ jsx(
5242
+ "span",
5243
+ {
5244
+ "data-slot": "can-blocked",
5245
+ className: "contents",
5246
+ onClickCapture: blockInteraction,
5247
+ onPointerDownCapture: blockInteraction,
5248
+ onMouseDownCapture: blockInteraction,
5249
+ onKeyDownCapture: blockNonTraversalKey,
5250
+ onKeyUpCapture: blockNonTraversalKey,
5251
+ children
5252
+ }
5253
+ );
5254
+ }
5255
+ function DisabledSubtree({
5256
+ children,
5257
+ className,
5258
+ ...props
5259
+ }) {
5260
+ const { "data-slot": _incomingDataSlot, ...restProps } = props;
5261
+ return /* @__PURE__ */ jsx(
5262
+ "span",
5263
+ {
5264
+ "data-slot": "can-disabled",
5265
+ "aria-disabled": "true",
5266
+ className: cn(
5267
+ "inline-flex cursor-not-allowed opacity-50",
5268
+ "*:pointer-events-none",
5269
+ className
5270
+ ),
5271
+ onClickCapture: blockInteraction,
5272
+ onPointerDownCapture: blockInteraction,
5273
+ onMouseDownCapture: blockInteraction,
5274
+ onKeyDownCapture: blockNonTraversalKey,
5275
+ onKeyUpCapture: blockNonTraversalKey,
5276
+ ...restProps,
5277
+ children
5278
+ }
5279
+ );
5105
5280
  }
5106
- function Can({ permission, mode = "any", negate = false, fallback = null, children }) {
5107
- const { can } = useAbility();
5108
- const hasPermission = can(permission, mode);
5281
+ function Can({
5282
+ permission,
5283
+ mode = "any",
5284
+ negate = false,
5285
+ behavior = "hide",
5286
+ reason = null,
5287
+ fallback = null,
5288
+ loading,
5289
+ children
5290
+ }) {
5291
+ warnUnknownPermission(permission);
5292
+ const { can: can2, status } = useAbility();
5293
+ const hasPermission = status === "ready" && can2(permission, mode);
5109
5294
  const show = negate ? !hasPermission : hasPermission;
5110
5295
  if (typeof children === "function") {
5111
- return /* @__PURE__ */ jsx(Fragment$1, { children: children(show) });
5296
+ warnRenderPropVoidsProps([
5297
+ ...behavior === "disable" ? ["behavior='disable'"] : [],
5298
+ ...reason !== null ? ["reason"] : [],
5299
+ ...fallback !== null ? ["fallback"] : [],
5300
+ ...loading !== void 0 ? ["loading"] : []
5301
+ ]);
5302
+ return /* @__PURE__ */ jsx(Fragment$1, { children: children({ allowed: show, status }) });
5303
+ }
5304
+ if (status === "loading" && loading !== void 0) {
5305
+ return /* @__PURE__ */ jsx(Fragment$1, { children: loading });
5306
+ }
5307
+ if (show) {
5308
+ return /* @__PURE__ */ jsx(Fragment$1, { children });
5309
+ }
5310
+ if (behavior === "disable") {
5311
+ if (isValidElement(children) && children.type !== Fragment) {
5312
+ const disabledChild = disableElement(children);
5313
+ if (reason === null) {
5314
+ return /* @__PURE__ */ jsx(InteractionFirewall, { children: disabledChild });
5315
+ }
5316
+ return /* @__PURE__ */ jsxs(Tooltip, { children: [
5317
+ /* @__PURE__ */ jsx(InteractionFirewall, { children: /* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: disabledChild }) }),
5318
+ /* @__PURE__ */ jsx(TooltipContent, { children: reason })
5319
+ ] });
5320
+ }
5321
+ if (reason === null) {
5322
+ return /* @__PURE__ */ jsx(DisabledSubtree, { children });
5323
+ }
5324
+ return /* @__PURE__ */ jsxs(Tooltip, { children: [
5325
+ /* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx(DisabledSubtree, { children }) }),
5326
+ /* @__PURE__ */ jsx(TooltipContent, { children: reason })
5327
+ ] });
5112
5328
  }
5113
- return show ? /* @__PURE__ */ jsx(Fragment$1, { children }) : /* @__PURE__ */ jsx(Fragment$1, { children: fallback });
5329
+ return /* @__PURE__ */ jsx(Fragment$1, { children: fallback });
5114
5330
  }
5115
5331
  Can.displayName = "Can";
5116
- function CanNot({ permission, mode = "any", fallback = null, children }) {
5117
- return /* @__PURE__ */ jsx(Can, { permission, mode, negate: true, fallback, children });
5332
+ function CanNot({
5333
+ permission,
5334
+ mode = "any",
5335
+ behavior = "hide",
5336
+ reason = null,
5337
+ fallback = null,
5338
+ loading,
5339
+ children
5340
+ }) {
5341
+ return /* @__PURE__ */ jsx(
5342
+ Can,
5343
+ {
5344
+ permission,
5345
+ mode,
5346
+ negate: true,
5347
+ behavior,
5348
+ reason,
5349
+ fallback,
5350
+ loading,
5351
+ children
5352
+ }
5353
+ );
5118
5354
  }
5119
5355
  CanNot.displayName = "CanNot";
5120
5356
  function useMediaQuery(query) {
@@ -5929,10 +6165,10 @@ function Textarea({ className, ...props }) {
5929
6165
  {
5930
6166
  "data-slot": "textarea",
5931
6167
  className: cn(
5932
- "border-input flex min-h-[80px] w-full resize-y rounded-md border bg-transparent px-3 py-2 text-sm shadow-xs transition-[color,box-shadow] outline-none",
6168
+ "border-input flex min-h-[80px] w-full resize-y rounded-md border bg-transparent px-3 py-2 text-base shadow-xs transition-[border-color,box-shadow] outline-hidden md:text-sm",
5933
6169
  "placeholder:text-muted-foreground",
5934
6170
  "focus-visible:border-ring focus-visible:ring-ring/30 focus-visible:ring-3",
5935
- "disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50",
6171
+ "disabled:cursor-not-allowed disabled:opacity-50",
5936
6172
  "aria-invalid:border-destructive aria-invalid:ring-destructive/20 aria-invalid:ring-3",
5937
6173
  "dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40",
5938
6174
  className
@@ -6860,6 +7096,38 @@ var logos = {
6860
7096
  millworkColor: `${CDN_BASE}/logos/Logo_Millwork_Color.webp`,
6861
7097
  millworkNegative: `${CDN_BASE}/logos/Logo_Millwork_Negative.webp`
6862
7098
  };
7099
+ var CONFETTI_COLORS = [
7100
+ "#22205f",
7101
+ // --color-main-blue-primary
7102
+ "#f66a24",
7103
+ // --color-main-orange-primary
7104
+ "#faca4a",
7105
+ // --color-main-yellow-primary
7106
+ "#01d42f",
7107
+ // --color-main-positive-state
7108
+ "#d93235"
7109
+ // --color-main-warning-state
7110
+ ];
7111
+ function fireConfetti({
7112
+ particleCount = 60,
7113
+ originY = 0.7,
7114
+ colors = [...CONFETTI_COLORS]
7115
+ } = {}) {
7116
+ const shared = {
7117
+ particleCount,
7118
+ colors,
7119
+ spread: 70,
7120
+ startVelocity: 55,
7121
+ disableForReducedMotion: true
7122
+ };
7123
+ return Promise.all([
7124
+ confetti({ ...shared, angle: 60, origin: { x: 0, y: originY } }),
7125
+ confetti({ ...shared, angle: 120, origin: { x: 1, y: originY } })
7126
+ ]).then(() => void 0);
7127
+ }
7128
+ function resetConfetti() {
7129
+ confetti.reset();
7130
+ }
6863
7131
  function Table({
6864
7132
  className,
6865
7133
  containerClassName = "overflow-x-auto",
@@ -6916,7 +7184,7 @@ function TableHead({ className, ...props }) {
6916
7184
  {
6917
7185
  "data-slot": "table-head",
6918
7186
  className: cn(
6919
- "text-foreground has([role=checkbox])]:pr-0 h-12 px-3 text-left align-middle font-medium whitespace-nowrap",
7187
+ "text-foreground has-[[role=checkbox]]:pr-0 h-12 px-3 text-left align-middle font-medium whitespace-nowrap",
6920
7188
  className
6921
7189
  ),
6922
7190
  ...props
@@ -6929,7 +7197,7 @@ function TableCell({ className, ...props }) {
6929
7197
  {
6930
7198
  "data-slot": "table-cell",
6931
7199
  className: cn(
6932
- "has([role=checkbox])]:pr-0 p-3 align-middle whitespace-nowrap",
7200
+ "has-[[role=checkbox]]:pr-0 p-3 align-middle whitespace-nowrap",
6933
7201
  className
6934
7202
  ),
6935
7203
  ...props
@@ -7190,6 +7458,13 @@ function useTableContext() {
7190
7458
  function useTableContextSafe() {
7191
7459
  return useContext(TableContext);
7192
7460
  }
7461
+
7462
+ // src/components/compounds/Table/cell-size-class.ts
7463
+ var cellSizeClass = {
7464
+ sm: "px-3 py-1.5 text-xs",
7465
+ md: "px-5 py-2.5 text-sm",
7466
+ lg: "px-6 py-3 text-sm"
7467
+ };
7193
7468
  var cardPadding = {
7194
7469
  sm: "p-3",
7195
7470
  md: "p-4",
@@ -7527,6 +7802,9 @@ function DataTableHeaderCell({ header }) {
7527
7802
  },
7528
7803
  className: cn(
7529
7804
  "bg-primary text-primary-foreground",
7805
+ // Same density padding as the body cells: without it the header
7806
+ // label and the cell value below it sit at different offsets.
7807
+ cellSizeClass[meta.size ?? tableSize],
7530
7808
  canResize && "relative",
7531
7809
  meta.align === "center" && "text-center",
7532
7810
  meta.align === "right" && "text-right",
@@ -7916,11 +8194,6 @@ function useTablePersistence({
7916
8194
  persistDensity
7917
8195
  };
7918
8196
  }
7919
- var cellSizeClass = {
7920
- sm: "px-1 py-0 text-xs",
7921
- md: "px-5 py-2.5 text-sm",
7922
- lg: "px-6 py-3 text-sm"
7923
- };
7924
8197
  function resolveClassName(value, row) {
7925
8198
  return typeof value === "function" ? value(row) : value;
7926
8199
  }
@@ -7961,6 +8234,13 @@ function DataTableCell({
7961
8234
  }
7962
8235
  );
7963
8236
  }
8237
+ var INTERACTIVE_CELL_CONTENT = 'button, a, input, select, textarea, [role="button"], [role="checkbox"], [role="menuitem"], [role="link"]';
8238
+ function isInteractiveCellEvent(event) {
8239
+ const target = event.target;
8240
+ if (!(target instanceof Element)) return false;
8241
+ const match = target.closest(INTERACTIVE_CELL_CONTENT);
8242
+ return match !== null && match !== event.currentTarget;
8243
+ }
7964
8244
  function DataTableRow({ row, classNames }) {
7965
8245
  const { onRowClick, variant } = useTableContext();
7966
8246
  const clickable = Boolean(onRowClick);
@@ -7973,12 +8253,15 @@ function DataTableRow({ row, classNames }) {
7973
8253
  "data-clickable": clickable || void 0,
7974
8254
  role: clickable ? "button" : void 0,
7975
8255
  tabIndex: clickable ? 0 : void 0,
7976
- onClick: clickable ? () => onRowClick?.(row.original) : void 0,
8256
+ onClick: clickable ? (e) => {
8257
+ if (isInteractiveCellEvent(e)) return;
8258
+ onRowClick?.(row.original);
8259
+ } : void 0,
7977
8260
  onKeyDown: clickable ? (e) => {
7978
- if (e.key === "Enter" || e.key === " ") {
7979
- e.preventDefault();
7980
- onRowClick?.(row.original);
7981
- }
8261
+ if (e.key !== "Enter" && e.key !== " ") return;
8262
+ if (isInteractiveCellEvent(e)) return;
8263
+ e.preventDefault();
8264
+ onRowClick?.(row.original);
7982
8265
  } : void 0,
7983
8266
  className: cn("hover:bg-muted", clickable && "cursor-pointer", rowClassName),
7984
8267
  children: row.getVisibleCells().map((cell) => /* @__PURE__ */ jsx(DataTableCell, { cell, cellClassName }, cell.id))
@@ -8579,7 +8862,7 @@ function DataTableFooter({ pagination, selectedCount, className }) {
8579
8862
  className: cn("flex items-center justify-between border-t px-4 py-2", className),
8580
8863
  children: [
8581
8864
  selectedCount != null && selectedCount > 0 && /* @__PURE__ */ jsx("span", { "aria-live": "polite", className: "text-muted-foreground text-xs", children: `${selectedCount} row${selectedCount > 1 ? "s" : ""} selected` }),
8582
- /* @__PURE__ */ jsx(TablePagination, { ...pagination })
8865
+ /* @__PURE__ */ jsx("div", { className: "ml-auto", children: /* @__PURE__ */ jsx(TablePagination, { ...pagination }) })
8583
8866
  ]
8584
8867
  }
8585
8868
  );
@@ -8688,18 +8971,21 @@ function DataTableSearch({
8688
8971
  debounce = 300
8689
8972
  }) {
8690
8973
  const ctx = useTableContextSafe();
8691
- const [inputValue, setInputValue] = useState("");
8692
8974
  const globalFilter = ctx?.globalFilter;
8693
8975
  const setGlobalFilter = ctx?.setGlobalFilter;
8976
+ const committedValue = globalFilter ?? "";
8977
+ const [inputValue, setInputValue] = useState(committedValue);
8694
8978
  useEffect(() => {
8695
- if (globalFilter === "") setInputValue("");
8979
+ if (globalFilter === void 0) return;
8980
+ setInputValue(globalFilter);
8696
8981
  }, [globalFilter]);
8697
8982
  useEffect(() => {
8983
+ if (inputValue === committedValue) return;
8698
8984
  const timer = setTimeout(() => {
8699
8985
  setGlobalFilter?.(inputValue);
8700
8986
  }, debounce);
8701
8987
  return () => clearTimeout(timer);
8702
- }, [inputValue, debounce, setGlobalFilter]);
8988
+ }, [inputValue, committedValue, debounce, setGlobalFilter]);
8703
8989
  const handleClear = () => {
8704
8990
  setInputValue("");
8705
8991
  setGlobalFilter?.("");
@@ -8720,7 +9006,13 @@ function DataTableSearch({
8720
9006
  onChange: (e) => setInputValue(e.target.value),
8721
9007
  placeholder,
8722
9008
  "aria-label": placeholder,
8723
- className: "h-8 w-48 pr-8 pl-8 text-sm"
9009
+ className: cn(
9010
+ "h-8 w-48 pr-9 pl-9 text-sm",
9011
+ "[&::-webkit-search-cancel-button]:appearance-none",
9012
+ "[&::-webkit-search-decoration]:appearance-none",
9013
+ "[&::-webkit-search-results-button]:appearance-none",
9014
+ "[&::-webkit-search-results-decoration]:appearance-none"
9015
+ )
8724
9016
  }
8725
9017
  ),
8726
9018
  inputValue && /* @__PURE__ */ jsx(
@@ -8730,7 +9022,7 @@ function DataTableSearch({
8730
9022
  size: "icon-sm",
8731
9023
  onClick: handleClear,
8732
9024
  "aria-label": "Clear search",
8733
- className: "text-muted-foreground hover:text-foreground absolute right-0.5 size-6",
9025
+ className: "text-muted-foreground hover:text-foreground absolute top-1/2 right-1.5 size-6 -translate-y-1/2",
8734
9026
  children: /* @__PURE__ */ jsx(X, { className: "size-3.5", "aria-hidden": "true" })
8735
9027
  }
8736
9028
  )
@@ -9106,6 +9398,201 @@ function DrawerDescription({
9106
9398
  }
9107
9399
  );
9108
9400
  }
9401
+ function SignaturePad({
9402
+ "aria-label": ariaLabel,
9403
+ "aria-describedby": ariaDescribedBy,
9404
+ onEmptyChange,
9405
+ disabled = false,
9406
+ height = 260,
9407
+ penColor = "black",
9408
+ // Smoother than the library defaults (0.5–2.5 px, 5 px, 16 ms), which drop points and turn
9409
+ // fast or small curves into visible straight segments.
9410
+ minWidth = 1,
9411
+ maxWidth = 3,
9412
+ minDistance = 1,
9413
+ throttle = 0,
9414
+ velocityFilterWeight,
9415
+ clearLabel,
9416
+ className,
9417
+ ref
9418
+ }) {
9419
+ const canvasRef = useRef(null);
9420
+ const padRef = useRef(null);
9421
+ const emptyRef = useRef(true);
9422
+ const [empty, setEmptyState] = useState(true);
9423
+ const onEmptyChangeRef = useRef(onEmptyChange);
9424
+ const drawnRef = useRef({
9425
+ data: [],
9426
+ width: 0,
9427
+ height: 0,
9428
+ scale: 1
9429
+ });
9430
+ const optionsRef = useRef({
9431
+ penColor,
9432
+ minWidth,
9433
+ maxWidth,
9434
+ minDistance,
9435
+ throttle,
9436
+ velocityFilterWeight
9437
+ });
9438
+ useEffect(() => {
9439
+ onEmptyChangeRef.current = onEmptyChange;
9440
+ }, [onEmptyChange]);
9441
+ const setEmpty = (next) => {
9442
+ if (emptyRef.current === next) return;
9443
+ emptyRef.current = next;
9444
+ setEmptyState(next);
9445
+ onEmptyChangeRef.current?.(next);
9446
+ };
9447
+ const clear = () => {
9448
+ padRef.current?.clear();
9449
+ drawnRef.current = { data: [], width: 0, height: 0, scale: 1 };
9450
+ setEmpty(true);
9451
+ };
9452
+ useEffect(() => {
9453
+ const canvas = canvasRef.current;
9454
+ if (!canvas) return;
9455
+ const pad = new SignaturePadLib(canvas, optionsRef.current);
9456
+ padRef.current = pad;
9457
+ let width = 0;
9458
+ let fittedHeight = 0;
9459
+ const redraw = () => {
9460
+ const drawn = drawnRef.current;
9461
+ drawn.scale = Math.min(width / drawn.width, fittedHeight / drawn.height, 1);
9462
+ pad.clear();
9463
+ pad.fromData(
9464
+ drawn.data.map((group) => ({
9465
+ ...group,
9466
+ points: group.points.map((point) => ({
9467
+ ...point,
9468
+ x: point.x * drawn.scale,
9469
+ y: point.y * drawn.scale
9470
+ }))
9471
+ }))
9472
+ );
9473
+ };
9474
+ const handleEnd = () => {
9475
+ const drawn = drawnRef.current;
9476
+ if (drawn.data.length === 0) {
9477
+ drawn.width = width;
9478
+ drawn.height = fittedHeight;
9479
+ drawn.scale = 1;
9480
+ }
9481
+ const newest = pad.toData().at(-1);
9482
+ if (newest) {
9483
+ const points = newest.points.map((point) => ({
9484
+ ...point,
9485
+ x: point.x / drawn.scale,
9486
+ y: point.y / drawn.scale
9487
+ }));
9488
+ drawn.data = [...drawn.data, { ...newest, points }];
9489
+ const margin = newest.maxWidth ?? 0;
9490
+ const reach = (axis) => Math.max(...points.map((point) => point[axis])) + margin;
9491
+ drawn.width = Math.max(drawn.width, reach("x"));
9492
+ drawn.height = Math.max(drawn.height, reach("y"));
9493
+ const fitting = Math.min(width / drawn.width, fittedHeight / drawn.height, 1);
9494
+ if (fitting !== drawn.scale) redraw();
9495
+ }
9496
+ setEmpty(pad.isEmpty());
9497
+ };
9498
+ pad.addEventListener("endStroke", handleEnd);
9499
+ const fit = () => {
9500
+ const nextWidth = canvas.offsetWidth;
9501
+ const nextHeight = canvas.offsetHeight;
9502
+ if (!nextWidth || !nextHeight) return;
9503
+ if (nextWidth === width && nextHeight === fittedHeight) return;
9504
+ width = nextWidth;
9505
+ fittedHeight = nextHeight;
9506
+ const ratio = window.devicePixelRatio || 1;
9507
+ canvas.width = nextWidth * ratio;
9508
+ canvas.height = nextHeight * ratio;
9509
+ canvas.getContext("2d")?.scale(ratio, ratio);
9510
+ pad.clear();
9511
+ if (drawnRef.current.data.length > 0) redraw();
9512
+ };
9513
+ fit();
9514
+ const observer = new ResizeObserver(fit);
9515
+ observer.observe(canvas);
9516
+ return () => {
9517
+ observer.disconnect();
9518
+ pad.removeEventListener("endStroke", handleEnd);
9519
+ pad.off();
9520
+ padRef.current = null;
9521
+ };
9522
+ }, []);
9523
+ useEffect(() => {
9524
+ if (disabled) padRef.current?.off();
9525
+ else padRef.current?.on();
9526
+ }, [disabled]);
9527
+ useImperativeHandle(
9528
+ ref,
9529
+ () => ({
9530
+ clear,
9531
+ isEmpty: () => padRef.current?.isEmpty() ?? true,
9532
+ toBlob: () => new Promise((resolve, reject) => {
9533
+ const canvas = canvasRef.current;
9534
+ if (!canvas) return reject(new Error("The signature pad is not mounted"));
9535
+ canvas.toBlob((blob) => {
9536
+ if (blob) resolve(blob);
9537
+ else reject(new Error("The signature could not be encoded as PNG"));
9538
+ }, "image/png");
9539
+ })
9540
+ }),
9541
+ // `clear` only touches refs and state setters, so the handle is created once.
9542
+ // eslint-disable-next-line react-hooks/exhaustive-deps
9543
+ []
9544
+ );
9545
+ return /* @__PURE__ */ jsxs(
9546
+ "div",
9547
+ {
9548
+ "data-slot": "signature-pad",
9549
+ "data-disabled": disabled ? "" : void 0,
9550
+ className: cn(
9551
+ // Fixed light "paper": the pen is black and the PNG is read on white by others. The
9552
+ // primitive keeps the pad light for consumers that define a dark theme, where a themed
9553
+ // surface (`background`, `border`) would turn dark and hide the black ink. The edge
9554
+ // uses `dark-400` (4.66:1 on white) so the pad reads as a control (WCAG 1.4.11).
9555
+ "bg-main-alternative-secondary border-dark-400 relative overflow-hidden rounded-xl border",
9556
+ "data-[disabled]:cursor-not-allowed data-[disabled]:opacity-50",
9557
+ className
9558
+ ),
9559
+ children: [
9560
+ /* @__PURE__ */ jsx(
9561
+ "canvas",
9562
+ {
9563
+ ref: canvasRef,
9564
+ "data-slot": "signature-pad-canvas",
9565
+ role: "img",
9566
+ "aria-label": ariaLabel,
9567
+ "aria-describedby": ariaDescribedBy,
9568
+ "aria-disabled": disabled || void 0,
9569
+ style: { height },
9570
+ className: "block w-full touch-none"
9571
+ }
9572
+ ),
9573
+ /* @__PURE__ */ jsxs(
9574
+ Button,
9575
+ {
9576
+ type: "button",
9577
+ variant: "ghost",
9578
+ size: "sm",
9579
+ "data-slot": "signature-pad-clear",
9580
+ disabled,
9581
+ "aria-disabled": empty && !disabled ? true : void 0,
9582
+ onClick: () => {
9583
+ if (!emptyRef.current) clear();
9584
+ },
9585
+ className: "bg-main-white text-dark-600 border-dark-400 hover:bg-dark-100 hover:text-main-black aria-disabled:hover:bg-main-white absolute right-2 bottom-2 h-11 max-w-[calc(100%-1rem)] gap-1.5 rounded-full border border-solid px-4 text-sm aria-disabled:cursor-not-allowed aria-disabled:opacity-70",
9586
+ children: [
9587
+ /* @__PURE__ */ jsx(BrushCleaning, { "aria-hidden": true, className: "size-4 shrink-0" }),
9588
+ /* @__PURE__ */ jsx("span", { className: "truncate", children: clearLabel })
9589
+ ]
9590
+ }
9591
+ )
9592
+ ]
9593
+ }
9594
+ );
9595
+ }
9109
9596
  function TCCEProvider({
9110
9597
  children,
9111
9598
  permissions,
@@ -9115,6 +9602,6 @@ function TCCEProvider({
9115
9602
  return /* @__PURE__ */ jsx(TooltipProvider, { delayDuration: tooltipDelayDuration, children: /* @__PURE__ */ jsx(AbilityProvider, { permissions, children: /* @__PURE__ */ jsx(Toaster, { position: toasterPosition, children }) }) });
9116
9603
  }
9117
9604
 
9118
- export { AbilityProvider, Accordion, AccordionContent, AccordionItem, AccordionTrigger, Alert, AlertDescription, AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogMedia, AlertDialogOverlay, AlertDialogPortal, AlertDialogTitle, AlertDialogTrigger, AlertTitle, AppShell, Avatar, AvatarBadge, AvatarFallback, AvatarGroup, AvatarGroupCount, AvatarImage, Badge2 as Badge, BottomNav, BottomNavTabs, BottomNavTabsContent, BottomNavTabsList, Breadcrumb, BreadcrumbEllipsis, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbPage, BreadcrumbSeparator, Button, Can, CanNot, Card, CardAction, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, Carousel, CarouselContent, CarouselItem, CarouselNext, CarouselPrevious, Checkbox, ColorPicker, ColorPickerAlpha, ColorPickerEyeDropper, ColorPickerFormat, ColorPickerHue, ColorPickerOutput, ColorPickerPresets, ColorPickerSelection, Combobox, DataTable, DataTableCardView, DataTableColumnFilter, DataTableFooter, DataTableHeaderCell, DataTableRowActions, DataTableSearch, DataTableSkeleton, DataTableToolbar, DatePicker, Dialog, DialogBody, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, Drawer, DrawerBody, DrawerClose, DrawerContent, DrawerDescription, DrawerFooter, DrawerHeader, DrawerOverlay, DrawerPortal, DrawerTitle, DrawerTrigger, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, Dropzone, DropzoneContent, DropzoneEmptyState, Empty, EmptyContent, EmptyDescription, EmptyHeader, EmptyMedia, EmptyTitle, EventCalendar, Field, FieldDescription, FieldError, FieldGroup, FieldLabel, FilterOption, Input, Item, ItemActions, ItemContent, ItemDescription, ItemFooter, ItemGroup, ItemHeader, ItemMedia, ItemSeparator, ItemTitle, Label, Link, Logo, MiniCalendar, MiniCalendarDay, MiniCalendarDays, MiniCalendarNavigation, Navbar, NavigationMenu, NavigationMenuContent, NavigationMenuIndicator, NavigationMenuItem, NavigationMenuLink, NavigationMenuList, NavigationMenuTrigger, NavigationMenuViewport, Pagination, PaginationContent, PaginationEllipsis, PaginationItem, PaginationLink, PaginationNext, PaginationPrevious, PhoneField, PinInput, Popover, PopoverAnchor, PopoverContent, PopoverDescription, PopoverHeader, PopoverTitle, PopoverTrigger, Progress, RadioGroup, RadioGroupItem, SegmentedControl, SegmentedControlItem, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, Separator, Sheet, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetTitle, SheetTrigger, Sidebar, SidebarContent, SidebarFooter, SidebarGroup, SidebarGroupAction, SidebarGroupContent, SidebarGroupLabel, SidebarHeader, SidebarInput, SidebarInset, SidebarMenu, SidebarMenuAction, SidebarMenuBadge, SidebarMenuButton, SidebarMenuItem, SidebarMenuSkeleton, SidebarMenuSub, SidebarMenuSubButton, SidebarMenuSubItem, SidebarProvider, SidebarRail, SidebarSeparator, SidebarTrigger, Skeleton, Slider, Spinner, SpinnerDots, SpinnerLoader, Status, StatusIndicator, StatusLabel, Switch, TCCEProvider, TEXT_SIZES, TEXT_WEIGHTS, Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TablePagination, TableRow, Tabs, TabsContent, TabsList, TabsTrigger, Text, Textarea, Toaster, Toggle, ToggleGroup2 as ToggleGroup, ToggleGroupItem, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, alertVariants, animation, avatarVariants, badgeVariants2 as badgeVariants, buttonVariants, columnFiltersToArrayQueryParams, columnFiltersToJsonString, columnFiltersToQueryParams, columnFiltersToRecord, createBackendParams, createBackendParamsWithFilters, duration, ease, fadeIn, getActiveFilterCount, getActiveFilterValues, getColumnValueCounts, getUniqueColumnValues, hasActiveFilters, linkVariants, logos, matchPermission, mergeFilters, multiSelectFilter, navigationMenuTriggerStyle, parseJsonToFilters, parseQueryParamsToFilters, progressIndicatorVariants, progressVariants, scaleIn, searchInArray, skeletonVariants, slideRight, slideUp, sortRecordToSortingState, sortingStateToJsonString, sortingStateToRecord, spinnerDotsVariants, spinnerLoaderVariants, spinnerVariants, spring, stagger, toggleVariants, tooltipContentVariants, transition, useAbility, useAppForm, useCan, useCarousel, useColorPicker, useColumnFilter, useSidebar, useTableContext, useTableData, useTableFiltering, useTableGlobalFilter, useTableSizing, useTableSorting, useTableVisibility, useToast, validatePhone };
9605
+ export { AbilityProvider, Accordion, AccordionContent, AccordionItem, AccordionTrigger, Alert, AlertDescription, AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogMedia, AlertDialogOverlay, AlertDialogPortal, AlertDialogTitle, AlertDialogTrigger, AlertTitle, AppShell, Avatar, AvatarBadge, AvatarFallback, AvatarGroup, AvatarGroupCount, AvatarImage, Badge2 as Badge, BottomNav, BottomNavTabs, BottomNavTabsContent, BottomNavTabsList, Breadcrumb, BreadcrumbEllipsis, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbPage, BreadcrumbSeparator, Button, CONFETTI_COLORS, Can, CanNot, Card, CardAction, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, Carousel, CarouselContent, CarouselItem, CarouselNext, CarouselPrevious, Checkbox, ColorPicker, ColorPickerAlpha, ColorPickerEyeDropper, ColorPickerFormat, ColorPickerHue, ColorPickerOutput, ColorPickerPresets, ColorPickerSelection, Combobox, DataTable, DataTableCardView, DataTableColumnFilter, DataTableFooter, DataTableHeaderCell, DataTableRowActions, DataTableSearch, DataTableSkeleton, DataTableToolbar, DatePicker, Dialog, DialogBody, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, Drawer, DrawerBody, DrawerClose, DrawerContent, DrawerDescription, DrawerFooter, DrawerHeader, DrawerOverlay, DrawerPortal, DrawerTitle, DrawerTrigger, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, Dropzone, DropzoneContent, DropzoneEmptyState, Empty, EmptyContent, EmptyDescription, EmptyHeader, EmptyMedia, EmptyTitle, EventCalendar, Field, FieldDescription, FieldError, FieldGroup, FieldLabel, FilterOption, Input, Item, ItemActions, ItemContent, ItemDescription, ItemFooter, ItemGroup, ItemHeader, ItemMedia, ItemSeparator, ItemTitle, KNOWN_PERMISSIONS, Label, Link, Logo, MiniCalendar, MiniCalendarDay, MiniCalendarDays, MiniCalendarNavigation, Navbar, NavigationMenu, NavigationMenuContent, NavigationMenuIndicator, NavigationMenuItem, NavigationMenuLink, NavigationMenuList, NavigationMenuTrigger, NavigationMenuViewport, Pagination, PaginationContent, PaginationEllipsis, PaginationItem, PaginationLink, PaginationNext, PaginationPrevious, PhoneField, PinInput, Popover, PopoverAnchor, PopoverContent, PopoverDescription, PopoverHeader, PopoverTitle, PopoverTrigger, Progress, RadioGroup, RadioGroupItem, SegmentedControl, SegmentedControlItem, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, Separator, Sheet, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetTitle, SheetTrigger, Sidebar, SidebarContent, SidebarFooter, SidebarGroup, SidebarGroupAction, SidebarGroupContent, SidebarGroupLabel, SidebarHeader, SidebarInput, SidebarInset, SidebarMenu, SidebarMenuAction, SidebarMenuBadge, SidebarMenuButton, SidebarMenuItem, SidebarMenuSkeleton, SidebarMenuSub, SidebarMenuSubButton, SidebarMenuSubItem, SidebarProvider, SidebarRail, SidebarSeparator, SidebarTrigger, SignaturePad, Skeleton, Slider, Spinner, SpinnerDots, SpinnerLoader, Status, StatusIndicator, StatusLabel, Switch, TCCEProvider, TEXT_SIZES, TEXT_WEIGHTS, Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TablePagination, TableRow, Tabs, TabsContent, TabsList, TabsTrigger, Text, Textarea, Toaster, Toggle, ToggleGroup2 as ToggleGroup, ToggleGroupItem, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, alertVariants, animation, avatarVariants, badgeVariants2 as badgeVariants, buttonVariants, columnFiltersToArrayQueryParams, columnFiltersToJsonString, columnFiltersToQueryParams, columnFiltersToRecord, createBackendParams, createBackendParamsWithFilters, duration, ease, fadeIn, fireConfetti, getActiveFilterCount, getActiveFilterValues, getColumnValueCounts, getUniqueColumnValues, hasActiveFilters, linkVariants, logos, matchPermission, mergeFilters, multiSelectFilter, navigationMenuTriggerStyle, parseJsonToFilters, parseQueryParamsToFilters, progressIndicatorVariants, progressVariants, resetConfetti, scaleIn, searchInArray, skeletonVariants, slideRight, slideUp, sortRecordToSortingState, sortingStateToJsonString, sortingStateToRecord, spinnerDotsVariants, spinnerLoaderVariants, spinnerVariants, spring, stagger, toggleVariants, tooltipContentVariants, transition, useAbility, useAbilityStatus, useAppForm, useCan, useCarousel, useColorPicker, useColumnFilter, useSidebar, useTableContext, useTableData, useTableFiltering, useTableGlobalFilter, useTableSizing, useTableSorting, useTableVisibility, useToast, validatePhone };
9119
9606
  //# sourceMappingURL=main.js.map
9120
9607
  //# sourceMappingURL=main.js.map