@bizapps/design-system-testing 1.9.0 → 1.10.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/main.cjs CHANGED
@@ -26,7 +26,9 @@ require('temporal-polyfill/global');
26
26
  var temporalPolyfill = require('temporal-polyfill');
27
27
  var reactHookForm = require('react-hook-form');
28
28
  var zod = require('@hookform/resolvers/zod');
29
+ var confetti = require('canvas-confetti');
29
30
  var reactTable = require('@tanstack/react-table');
31
+ var SignaturePadLib = require('signature_pad');
30
32
 
31
33
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
32
34
 
@@ -51,6 +53,8 @@ function _interopNamespace(e) {
51
53
  var useEmblaCarousel__default = /*#__PURE__*/_interopDefault(useEmblaCarousel);
52
54
  var countryFlags__namespace = /*#__PURE__*/_interopNamespace(countryFlags);
53
55
  var Color__default = /*#__PURE__*/_interopDefault(Color);
56
+ var confetti__default = /*#__PURE__*/_interopDefault(confetti);
57
+ var SignaturePadLib__default = /*#__PURE__*/_interopDefault(SignaturePadLib);
54
58
 
55
59
  // src/components/ui/Alert/alert.tsx
56
60
  function cn(...inputs) {
@@ -951,10 +955,11 @@ function Input({ className, type, ...props }) {
951
955
  type,
952
956
  "data-slot": "input",
953
957
  className: cn(
954
- "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",
955
- "placeholder:text-muted-foreground",
958
+ "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",
959
+ "placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground",
960
+ "file:text-foreground file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium",
956
961
  "focus-visible:border-ring focus-visible:ring-ring/30 focus-visible:ring-3",
957
- "disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50",
962
+ "disabled:cursor-not-allowed disabled:opacity-50",
958
963
  "aria-invalid:border-destructive aria-invalid:ring-destructive/20 aria-invalid:ring-3",
959
964
  "dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40",
960
965
  className
@@ -1700,31 +1705,37 @@ function SelectContent({
1700
1705
  className,
1701
1706
  children,
1702
1707
  position = "popper",
1708
+ maxHeight,
1709
+ style,
1703
1710
  ...props
1704
1711
  }) {
1712
+ const resolvedMaxHeight = typeof maxHeight === "number" ? `${maxHeight}px` : maxHeight;
1705
1713
  return /* @__PURE__ */ jsxRuntime.jsx(radixUi.Select.Portal, { children: /* @__PURE__ */ jsxRuntime.jsxs(
1706
1714
  radixUi.Select.Content,
1707
1715
  {
1708
1716
  "data-slot": "select-content",
1709
1717
  className: cn(
1710
- "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",
1718
+ "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",
1711
1719
  "data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95",
1712
1720
  "data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95",
1713
1721
  "data-[side=bottom]:translate-y-1 data-[side=top]:-translate-y-1",
1714
- position === "popper" && "w-[--radix-select-trigger-width]",
1722
+ position === "popper" && "w-(--radix-select-trigger-width)",
1715
1723
  className
1716
1724
  ),
1717
1725
  position,
1726
+ style: {
1727
+ ...resolvedMaxHeight ? {
1728
+ maxHeight: `min(${resolvedMaxHeight}, var(--radix-select-content-available-height))`
1729
+ } : null,
1730
+ ...style
1731
+ },
1718
1732
  ...props,
1719
1733
  children: [
1720
1734
  /* @__PURE__ */ jsxRuntime.jsx(SelectScrollUpButton, {}),
1721
1735
  /* @__PURE__ */ jsxRuntime.jsx(
1722
1736
  radixUi.Select.Viewport,
1723
1737
  {
1724
- className: cn(
1725
- "p-1",
1726
- position === "popper" && "h-[--radix-select-trigger-height]"
1727
- ),
1738
+ className: cn("scroll-my-1 p-1", position === "popper" && "w-full"),
1728
1739
  children
1729
1740
  }
1730
1741
  ),
@@ -4386,6 +4397,7 @@ ColorPickerPresets.displayName = "ColorPickerPresets";
4386
4397
  function Calendar({
4387
4398
  className,
4388
4399
  classNames,
4400
+ modifiersClassNames,
4389
4401
  showOutsideDays = true,
4390
4402
  captionLayout = "dropdown",
4391
4403
  buttonVariant = "ghost",
@@ -4397,6 +4409,36 @@ function Calendar({
4397
4409
  ...props
4398
4410
  }) {
4399
4411
  const defaultClassNames = reactDayPicker.getDefaultClassNames();
4412
+ const resolvedComponents = react.useMemo(
4413
+ () => ({
4414
+ Root: ({ className: className2, rootRef, ...rootProps }) => {
4415
+ return /* @__PURE__ */ jsxRuntime.jsx(
4416
+ "div",
4417
+ {
4418
+ "data-slot": "calendar",
4419
+ ref: rootRef,
4420
+ className: cn(className2),
4421
+ ...rootProps
4422
+ }
4423
+ );
4424
+ },
4425
+ Chevron: ({ className: className2, orientation, ...chevronProps }) => {
4426
+ if (orientation === "left") {
4427
+ return /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronLeftIcon, { className: cn("size-4", className2), ...chevronProps });
4428
+ }
4429
+ if (orientation === "right") {
4430
+ return /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronRightIcon, { className: cn("size-4", className2), ...chevronProps });
4431
+ }
4432
+ return /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronDownIcon, { className: cn("size-4", className2), ...chevronProps });
4433
+ },
4434
+ DayButton: (dayButtonProps) => /* @__PURE__ */ jsxRuntime.jsx(CalendarDayButton, { locale, ...dayButtonProps }),
4435
+ WeekNumber: ({ children, ...weekNumberProps }) => {
4436
+ return /* @__PURE__ */ jsxRuntime.jsx("td", { ...weekNumberProps, children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex size-(--cell-size) items-center justify-center text-center", children }) });
4437
+ },
4438
+ ...components
4439
+ }),
4440
+ [locale, components]
4441
+ );
4400
4442
  return /* @__PURE__ */ jsxRuntime.jsx(
4401
4443
  reactDayPicker.DayPicker,
4402
4444
  {
@@ -4488,33 +4530,13 @@ function Calendar({
4488
4530
  hidden: cn("invisible", defaultClassNames.hidden),
4489
4531
  ...classNames
4490
4532
  },
4491
- components: {
4492
- Root: ({ className: className2, rootRef, ...props2 }) => {
4493
- return /* @__PURE__ */ jsxRuntime.jsx(
4494
- "div",
4495
- {
4496
- "data-slot": "calendar",
4497
- ref: rootRef,
4498
- className: cn(className2),
4499
- ...props2
4500
- }
4501
- );
4502
- },
4503
- Chevron: ({ className: className2, orientation, ...props2 }) => {
4504
- if (orientation === "left") {
4505
- return /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronLeftIcon, { className: cn("size-4", className2), ...props2 });
4506
- }
4507
- if (orientation === "right") {
4508
- return /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronRightIcon, { className: cn("size-4", className2), ...props2 });
4509
- }
4510
- return /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronDownIcon, { className: cn("size-4", className2), ...props2 });
4511
- },
4512
- DayButton: ({ ...props2 }) => /* @__PURE__ */ jsxRuntime.jsx(CalendarDayButton, { locale, ...props2 }),
4513
- WeekNumber: ({ children, ...props2 }) => {
4514
- return /* @__PURE__ */ jsxRuntime.jsx("td", { ...props2, children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex size-(--cell-size) items-center justify-center text-center", children }) });
4515
- },
4516
- ...components
4533
+ modifiersClassNames: {
4534
+ 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",
4535
+ preview_middle: "rounded-none bg-primary/8",
4536
+ 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",
4537
+ ...modifiersClassNames
4517
4538
  },
4539
+ components: resolvedComponents,
4518
4540
  ...props
4519
4541
  }
4520
4542
  );
@@ -4531,6 +4553,9 @@ function CalendarDayButton({
4531
4553
  react.useEffect(() => {
4532
4554
  if (modifiers.focused) ref.current?.focus();
4533
4555
  }, [modifiers.focused]);
4556
+ const isPreviewEdge = Boolean(
4557
+ (modifiers.preview_start || modifiers.preview_end) && !modifiers.selected
4558
+ );
4534
4559
  return /* @__PURE__ */ jsxRuntime.jsx(
4535
4560
  Button,
4536
4561
  {
@@ -4542,8 +4567,10 @@ function CalendarDayButton({
4542
4567
  "data-range-start": modifiers.range_start,
4543
4568
  "data-range-end": modifiers.range_end,
4544
4569
  "data-range-middle": modifiers.range_middle,
4570
+ "data-preview-middle": Boolean(modifiers.preview_middle),
4571
+ "data-preview-edge": isPreviewEdge,
4545
4572
  className: cn(
4546
- "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",
4573
+ "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",
4547
4574
  defaultClassNames.day,
4548
4575
  className
4549
4576
  ),
@@ -4551,30 +4578,79 @@ function CalendarDayButton({
4551
4578
  }
4552
4579
  );
4553
4580
  }
4554
- function DatePicker({
4555
- mode = "single",
4556
- selected,
4557
- onSelect,
4558
- placeholder = "Pick a date",
4559
- disabled = false,
4560
- className,
4561
- fromDate,
4562
- toDate
4563
- }) {
4581
+ function DatePicker(props) {
4582
+ const {
4583
+ placeholder = "Pick a date",
4584
+ disabled = false,
4585
+ className,
4586
+ fromDate,
4587
+ toDate,
4588
+ numberOfMonths = 1
4589
+ } = props;
4564
4590
  const [open, setOpen] = react.useState(false);
4591
+ const [awaitingRangeEnd, setAwaitingRangeEnd] = react.useState(false);
4592
+ const [previewDate, setPreviewDate] = react.useState(void 0);
4565
4593
  const label = react.useMemo(() => {
4566
- if (mode === "range") {
4567
- const range = selected;
4568
- if (range?.from && range?.to)
4569
- return `${dateFns.format(range.from, "MMM d, yyyy")} \u2013 ${dateFns.format(range.to, "MMM d, yyyy")}`;
4570
- if (range?.from) return `${dateFns.format(range.from, "MMM d, yyyy")} \u2013 \u2026`;
4571
- return placeholder;
4572
- }
4573
- const date = selected;
4574
- return date ? dateFns.format(date, "MMM d, yyyy") : placeholder;
4575
- }, [mode, selected, placeholder]);
4576
- const hasValue = mode === "range" ? !!selected?.from : !!selected;
4577
- return /* @__PURE__ */ jsxRuntime.jsxs(Popover, { open, onOpenChange: setOpen, children: [
4594
+ if (props.mode === "range") {
4595
+ const range = props.selected;
4596
+ if (!range?.from) return placeholder;
4597
+ if (!range.to) return `${dateFns.format(range.from, "MMM d, yyyy")} \u2013 \u2026`;
4598
+ return `${dateFns.format(range.from, "MMM d, yyyy")} \u2013 ${dateFns.format(range.to, "MMM d, yyyy")}`;
4599
+ }
4600
+ return props.selected ? dateFns.format(props.selected, "MMM d, yyyy") : placeholder;
4601
+ }, [props.mode, props.selected, placeholder]);
4602
+ const handleOpenChange = (nextOpen) => {
4603
+ setOpen(nextOpen);
4604
+ if (!nextOpen) {
4605
+ setAwaitingRangeEnd(false);
4606
+ setPreviewDate(void 0);
4607
+ }
4608
+ };
4609
+ const handleRangeSelect = (range, triggerDate) => {
4610
+ if (props.mode !== "range") return;
4611
+ const emit = props.onSelect;
4612
+ if (!awaitingRangeEnd) {
4613
+ setAwaitingRangeEnd(true);
4614
+ emit?.({ from: triggerDate, to: void 0 });
4615
+ return;
4616
+ }
4617
+ emit?.(range);
4618
+ if (!range?.from || !range.to) {
4619
+ setAwaitingRangeEnd(!!range?.from);
4620
+ return;
4621
+ }
4622
+ setAwaitingRangeEnd(false);
4623
+ setPreviewDate(void 0);
4624
+ setOpen(false);
4625
+ };
4626
+ const rangeFrom = props.mode === "range" ? props.selected?.from : void 0;
4627
+ const previewModifiers = react.useMemo(() => {
4628
+ if (!awaitingRangeEnd || !rangeFrom || !previewDate) return void 0;
4629
+ if (dateFns.isSameDay(rangeFrom, previewDate)) return void 0;
4630
+ const [start, end] = previewDate < rangeFrom ? [previewDate, rangeFrom] : [rangeFrom, previewDate];
4631
+ return {
4632
+ preview_start: start,
4633
+ preview_middle: { after: start, before: end },
4634
+ preview_end: end
4635
+ };
4636
+ }, [awaitingRangeEnd, rangeFrom, previewDate]);
4637
+ const handleDayPreview = (day, dayModifiers) => {
4638
+ if (!awaitingRangeEnd) return;
4639
+ if (dayModifiers.disabled || dayModifiers.hidden) return;
4640
+ setPreviewDate((current) => current && dateFns.isSameDay(current, day) ? current : day);
4641
+ };
4642
+ const dateConstraints = react.useMemo(() => {
4643
+ const disabledMatchers = [];
4644
+ if (fromDate) disabledMatchers.push({ before: fromDate });
4645
+ if (toDate) disabledMatchers.push({ after: toDate });
4646
+ return {
4647
+ ...disabledMatchers.length > 0 && { disabled: disabledMatchers },
4648
+ ...fromDate && { startMonth: fromDate },
4649
+ ...toDate && { endMonth: toDate }
4650
+ };
4651
+ }, [fromDate, toDate]);
4652
+ const hasValue = props.mode === "range" ? !!props.selected?.from : !!props.selected;
4653
+ return /* @__PURE__ */ jsxRuntime.jsxs(Popover, { open, onOpenChange: handleOpenChange, children: [
4578
4654
  /* @__PURE__ */ jsxRuntime.jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsxs(
4579
4655
  Button,
4580
4656
  {
@@ -4592,36 +4668,36 @@ function DatePicker({
4592
4668
  ]
4593
4669
  }
4594
4670
  ) }),
4595
- /* @__PURE__ */ jsxRuntime.jsx(PopoverContent, { "data-slot": "date-picker-content", className: "w-auto p-0", align: "start", children: mode === "range" ? /* @__PURE__ */ jsxRuntime.jsx(
4596
- Calendar,
4597
- {
4598
- ...{
4671
+ /* @__PURE__ */ jsxRuntime.jsx(PopoverContent, { "data-slot": "date-picker-content", className: "w-auto p-0", align: "start", children: props.mode === "range" ? (
4672
+ // Clearing the preview on the wrapper instead of per day
4673
+ // avoids the blank frame `onDayMouseLeave` would cause when
4674
+ // the pointer travels from one day to the next.
4675
+ /* @__PURE__ */ jsxRuntime.jsx("div", { onMouseLeave: () => setPreviewDate(void 0), children: /* @__PURE__ */ jsxRuntime.jsx(
4676
+ Calendar,
4677
+ {
4599
4678
  mode: "range",
4600
- selected,
4601
- onSelect: (range) => {
4602
- onSelect?.(range);
4603
- if (range?.to) setOpen(false);
4604
- },
4605
- fromDate,
4606
- toDate,
4607
- numberOfMonths: 2,
4608
- initialFocus: true
4679
+ selected: props.selected,
4680
+ onSelect: handleRangeSelect,
4681
+ modifiers: previewModifiers,
4682
+ onDayMouseEnter: handleDayPreview,
4683
+ onDayFocus: handleDayPreview,
4684
+ numberOfMonths,
4685
+ autoFocus: true,
4686
+ ...dateConstraints
4609
4687
  }
4610
- }
4688
+ ) })
4611
4689
  ) : /* @__PURE__ */ jsxRuntime.jsx(
4612
4690
  Calendar,
4613
4691
  {
4614
- ...{
4615
- mode: "single",
4616
- selected,
4617
- onSelect: (date) => {
4618
- onSelect?.(date);
4619
- setOpen(false);
4620
- },
4621
- fromDate,
4622
- toDate,
4623
- initialFocus: true
4624
- }
4692
+ mode: "single",
4693
+ selected: props.selected,
4694
+ onSelect: (date) => {
4695
+ props.onSelect?.(date);
4696
+ setOpen(false);
4697
+ },
4698
+ numberOfMonths,
4699
+ autoFocus: true,
4700
+ ...dateConstraints
4625
4701
  }
4626
4702
  ) })
4627
4703
  ] });
@@ -5063,6 +5139,8 @@ function NavigationMenuIndicator({
5063
5139
  }
5064
5140
  );
5065
5141
  }
5142
+
5143
+ // src/components/authorization/policy.ts
5066
5144
  function matchPermission(pattern, permission) {
5067
5145
  if (pattern === "*") return true;
5068
5146
  if (pattern === permission) return true;
@@ -5078,41 +5156,46 @@ function matchesAny(permissionSet, permission) {
5078
5156
  }
5079
5157
  return false;
5080
5158
  }
5159
+ function can(permissionSet, perms, mode = "any") {
5160
+ const list = Array.isArray(perms) ? perms : [perms];
5161
+ if (list.length === 0) return false;
5162
+ if (permissionSet.has("*")) return true;
5163
+ if (mode === "all") {
5164
+ return list.every((p) => matchesAny(permissionSet, p));
5165
+ }
5166
+ return list.some((p) => matchesAny(permissionSet, p));
5167
+ }
5081
5168
  var AbilityContext = react.createContext(void 0);
5082
5169
  AbilityContext.displayName = "AbilityContext";
5083
5170
  function AbilityProvider({ children, permissions }) {
5084
5171
  const [permissionSet, setPermissionSet] = react.useState(
5085
5172
  () => new Set(permissions ?? [])
5086
5173
  );
5087
- const can = react.useCallback(
5088
- (perms, mode = "any") => {
5089
- const list = Array.isArray(perms) ? perms : [perms];
5090
- if (list.length === 0) return false;
5091
- if (permissionSet.has("*")) return true;
5092
- if (mode === "all") {
5093
- return list.every((p) => matchesAny(permissionSet, p));
5094
- }
5095
- return list.some((p) => matchesAny(permissionSet, p));
5096
- },
5174
+ const [imperativelyResolved, setImperativelyResolved] = react.useState(false);
5175
+ const status = permissions !== void 0 || imperativelyResolved ? "ready" : "loading";
5176
+ const can2 = react.useCallback(
5177
+ (perms, mode = "any") => can(permissionSet, perms, mode),
5097
5178
  [permissionSet]
5098
5179
  );
5099
5180
  const setAbilities = react.useCallback((perms) => {
5100
5181
  setPermissionSet(new Set(perms));
5182
+ setImperativelyResolved(true);
5101
5183
  }, []);
5102
5184
  const clearAbilities = react.useCallback(() => {
5103
5185
  setPermissionSet(/* @__PURE__ */ new Set());
5186
+ setImperativelyResolved(true);
5104
5187
  }, []);
5105
- const syncedKey = react.useRef(permissions ? permissions.join("|") : null);
5188
+ const permissionKey = permissions === void 0 ? null : [...permissions].sort().join("|");
5189
+ const syncedKey = react.useRef(permissionKey);
5106
5190
  react.useEffect(() => {
5107
- if (permissions === void 0) return;
5108
- const key = permissions.join("|");
5109
- if (key === syncedKey.current) return;
5110
- syncedKey.current = key;
5191
+ if (permissionKey === null) return;
5192
+ if (permissionKey === syncedKey.current) return;
5193
+ syncedKey.current = permissionKey;
5111
5194
  setPermissionSet(new Set(permissions));
5112
- }, [permissions]);
5195
+ }, [permissionKey, permissions]);
5113
5196
  const value = react.useMemo(
5114
- () => ({ can, setAbilities, clearAbilities }),
5115
- [can, setAbilities, clearAbilities]
5197
+ () => ({ can: can2, setAbilities, clearAbilities, status }),
5198
+ [can2, setAbilities, clearAbilities, status]
5116
5199
  );
5117
5200
  return /* @__PURE__ */ jsxRuntime.jsx(AbilityContext.Provider, { value, children });
5118
5201
  }
@@ -5124,21 +5207,176 @@ function useAbility() {
5124
5207
  return context;
5125
5208
  }
5126
5209
  function useCan(permissions, mode = "any") {
5127
- const { can } = useAbility();
5128
- return can(permissions, mode);
5210
+ const { can: can2 } = useAbility();
5211
+ return can2(permissions, mode);
5212
+ }
5213
+ function useAbilityStatus() {
5214
+ return useAbility().status;
5215
+ }
5216
+
5217
+ // src/components/authorization/permissions.generated.ts
5218
+ var KNOWN_PERMISSIONS = [];
5219
+
5220
+ // src/components/authorization/permission-catalog.ts
5221
+ var CATALOG = new Set(KNOWN_PERMISSIONS);
5222
+ var warned = /* @__PURE__ */ new Set();
5223
+ function warnUnknownPermission(permission) {
5224
+ if (process.env.NODE_ENV === "production") return;
5225
+ if (CATALOG.size === 0) return;
5226
+ const requested = Array.isArray(permission) ? permission : [permission];
5227
+ for (const entry of requested) {
5228
+ if (entry.includes("*")) continue;
5229
+ if (CATALOG.has(entry) || warned.has(entry)) continue;
5230
+ warned.add(entry);
5231
+ console.warn(
5232
+ `[Can] Unknown permission "${entry}". It is not in the generated permission catalog. Check for a typo, or regenerate the catalog with \`pnpm generate-permissions\`.`
5233
+ );
5234
+ }
5235
+ }
5236
+ var FOCUS_TRAVERSAL_KEYS = /* @__PURE__ */ new Set(["Tab", "Escape"]);
5237
+ function blockInteraction(event) {
5238
+ event.preventDefault();
5239
+ event.stopPropagation();
5240
+ }
5241
+ function blockNonTraversalKey(event) {
5242
+ if (!FOCUS_TRAVERSAL_KEYS.has(event.key)) blockInteraction(event);
5243
+ }
5244
+ var warnedRenderPropMisuse = /* @__PURE__ */ new Set();
5245
+ function warnRenderPropVoidsProps(voided) {
5246
+ if (process.env.NODE_ENV === "production") return;
5247
+ if (voided.length === 0) return;
5248
+ const key = voided.join(",");
5249
+ if (warnedRenderPropMisuse.has(key)) return;
5250
+ warnedRenderPropMisuse.add(key);
5251
+ console.warn(
5252
+ `[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.`
5253
+ );
5254
+ }
5255
+ function disableElement(child) {
5256
+ return react.cloneElement(child, {
5257
+ "aria-disabled": "true",
5258
+ className: cn(child.props.className, "cursor-not-allowed opacity-50"),
5259
+ onClickCapture: blockInteraction,
5260
+ onKeyDownCapture: blockNonTraversalKey,
5261
+ onKeyUpCapture: blockNonTraversalKey,
5262
+ onPointerDownCapture: blockInteraction,
5263
+ onMouseDownCapture: blockInteraction
5264
+ });
5265
+ }
5266
+ function InteractionFirewall({ children }) {
5267
+ return /* @__PURE__ */ jsxRuntime.jsx(
5268
+ "span",
5269
+ {
5270
+ "data-slot": "can-blocked",
5271
+ className: "contents",
5272
+ onClickCapture: blockInteraction,
5273
+ onPointerDownCapture: blockInteraction,
5274
+ onMouseDownCapture: blockInteraction,
5275
+ onKeyDownCapture: blockNonTraversalKey,
5276
+ onKeyUpCapture: blockNonTraversalKey,
5277
+ children
5278
+ }
5279
+ );
5280
+ }
5281
+ function DisabledSubtree({
5282
+ children,
5283
+ className,
5284
+ ...props
5285
+ }) {
5286
+ const { "data-slot": _incomingDataSlot, ...restProps } = props;
5287
+ return /* @__PURE__ */ jsxRuntime.jsx(
5288
+ "span",
5289
+ {
5290
+ "data-slot": "can-disabled",
5291
+ "aria-disabled": "true",
5292
+ className: cn(
5293
+ "inline-flex cursor-not-allowed opacity-50",
5294
+ "*:pointer-events-none",
5295
+ className
5296
+ ),
5297
+ onClickCapture: blockInteraction,
5298
+ onPointerDownCapture: blockInteraction,
5299
+ onMouseDownCapture: blockInteraction,
5300
+ onKeyDownCapture: blockNonTraversalKey,
5301
+ onKeyUpCapture: blockNonTraversalKey,
5302
+ ...restProps,
5303
+ children
5304
+ }
5305
+ );
5129
5306
  }
5130
- function Can({ permission, mode = "any", negate = false, fallback = null, children }) {
5131
- const { can } = useAbility();
5132
- const hasPermission = can(permission, mode);
5307
+ function Can({
5308
+ permission,
5309
+ mode = "any",
5310
+ negate = false,
5311
+ behavior = "hide",
5312
+ reason = null,
5313
+ fallback = null,
5314
+ loading,
5315
+ children
5316
+ }) {
5317
+ warnUnknownPermission(permission);
5318
+ const { can: can2, status } = useAbility();
5319
+ const hasPermission = status === "ready" && can2(permission, mode);
5133
5320
  const show = negate ? !hasPermission : hasPermission;
5134
5321
  if (typeof children === "function") {
5135
- return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: children(show) });
5322
+ warnRenderPropVoidsProps([
5323
+ ...behavior === "disable" ? ["behavior='disable'"] : [],
5324
+ ...reason !== null ? ["reason"] : [],
5325
+ ...fallback !== null ? ["fallback"] : [],
5326
+ ...loading !== void 0 ? ["loading"] : []
5327
+ ]);
5328
+ return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: children({ allowed: show, status }) });
5329
+ }
5330
+ if (status === "loading" && loading !== void 0) {
5331
+ return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: loading });
5332
+ }
5333
+ if (show) {
5334
+ return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children });
5136
5335
  }
5137
- return show ? /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children }) : /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: fallback });
5336
+ if (behavior === "disable") {
5337
+ if (react.isValidElement(children) && children.type !== react.Fragment) {
5338
+ const disabledChild = disableElement(children);
5339
+ if (reason === null) {
5340
+ return /* @__PURE__ */ jsxRuntime.jsx(InteractionFirewall, { children: disabledChild });
5341
+ }
5342
+ return /* @__PURE__ */ jsxRuntime.jsxs(Tooltip, { children: [
5343
+ /* @__PURE__ */ jsxRuntime.jsx(InteractionFirewall, { children: /* @__PURE__ */ jsxRuntime.jsx(TooltipTrigger, { asChild: true, children: disabledChild }) }),
5344
+ /* @__PURE__ */ jsxRuntime.jsx(TooltipContent, { children: reason })
5345
+ ] });
5346
+ }
5347
+ if (reason === null) {
5348
+ return /* @__PURE__ */ jsxRuntime.jsx(DisabledSubtree, { children });
5349
+ }
5350
+ return /* @__PURE__ */ jsxRuntime.jsxs(Tooltip, { children: [
5351
+ /* @__PURE__ */ jsxRuntime.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(DisabledSubtree, { children }) }),
5352
+ /* @__PURE__ */ jsxRuntime.jsx(TooltipContent, { children: reason })
5353
+ ] });
5354
+ }
5355
+ return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: fallback });
5138
5356
  }
5139
5357
  Can.displayName = "Can";
5140
- function CanNot({ permission, mode = "any", fallback = null, children }) {
5141
- return /* @__PURE__ */ jsxRuntime.jsx(Can, { permission, mode, negate: true, fallback, children });
5358
+ function CanNot({
5359
+ permission,
5360
+ mode = "any",
5361
+ behavior = "hide",
5362
+ reason = null,
5363
+ fallback = null,
5364
+ loading,
5365
+ children
5366
+ }) {
5367
+ return /* @__PURE__ */ jsxRuntime.jsx(
5368
+ Can,
5369
+ {
5370
+ permission,
5371
+ mode,
5372
+ negate: true,
5373
+ behavior,
5374
+ reason,
5375
+ fallback,
5376
+ loading,
5377
+ children
5378
+ }
5379
+ );
5142
5380
  }
5143
5381
  CanNot.displayName = "CanNot";
5144
5382
  function useMediaQuery(query) {
@@ -5953,10 +6191,10 @@ function Textarea({ className, ...props }) {
5953
6191
  {
5954
6192
  "data-slot": "textarea",
5955
6193
  className: cn(
5956
- "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",
6194
+ "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",
5957
6195
  "placeholder:text-muted-foreground",
5958
6196
  "focus-visible:border-ring focus-visible:ring-ring/30 focus-visible:ring-3",
5959
- "disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50",
6197
+ "disabled:cursor-not-allowed disabled:opacity-50",
5960
6198
  "aria-invalid:border-destructive aria-invalid:ring-destructive/20 aria-invalid:ring-3",
5961
6199
  "dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40",
5962
6200
  className
@@ -6884,6 +7122,38 @@ var logos = {
6884
7122
  millworkColor: `${CDN_BASE}/logos/Logo_Millwork_Color.webp`,
6885
7123
  millworkNegative: `${CDN_BASE}/logos/Logo_Millwork_Negative.webp`
6886
7124
  };
7125
+ var CONFETTI_COLORS = [
7126
+ "#22205f",
7127
+ // --color-main-blue-primary
7128
+ "#f66a24",
7129
+ // --color-main-orange-primary
7130
+ "#faca4a",
7131
+ // --color-main-yellow-primary
7132
+ "#01d42f",
7133
+ // --color-main-positive-state
7134
+ "#d93235"
7135
+ // --color-main-warning-state
7136
+ ];
7137
+ function fireConfetti({
7138
+ particleCount = 60,
7139
+ originY = 0.7,
7140
+ colors = [...CONFETTI_COLORS]
7141
+ } = {}) {
7142
+ const shared = {
7143
+ particleCount,
7144
+ colors,
7145
+ spread: 70,
7146
+ startVelocity: 55,
7147
+ disableForReducedMotion: true
7148
+ };
7149
+ return Promise.all([
7150
+ confetti__default.default({ ...shared, angle: 60, origin: { x: 0, y: originY } }),
7151
+ confetti__default.default({ ...shared, angle: 120, origin: { x: 1, y: originY } })
7152
+ ]).then(() => void 0);
7153
+ }
7154
+ function resetConfetti() {
7155
+ confetti__default.default.reset();
7156
+ }
6887
7157
  function Table({
6888
7158
  className,
6889
7159
  containerClassName = "overflow-x-auto",
@@ -6940,7 +7210,7 @@ function TableHead({ className, ...props }) {
6940
7210
  {
6941
7211
  "data-slot": "table-head",
6942
7212
  className: cn(
6943
- "text-foreground has([role=checkbox])]:pr-0 h-12 px-3 text-left align-middle font-medium whitespace-nowrap",
7213
+ "text-foreground has-[[role=checkbox]]:pr-0 h-12 px-3 text-left align-middle font-medium whitespace-nowrap",
6944
7214
  className
6945
7215
  ),
6946
7216
  ...props
@@ -6953,7 +7223,7 @@ function TableCell({ className, ...props }) {
6953
7223
  {
6954
7224
  "data-slot": "table-cell",
6955
7225
  className: cn(
6956
- "has([role=checkbox])]:pr-0 p-3 align-middle whitespace-nowrap",
7226
+ "has-[[role=checkbox]]:pr-0 p-3 align-middle whitespace-nowrap",
6957
7227
  className
6958
7228
  ),
6959
7229
  ...props
@@ -7214,6 +7484,13 @@ function useTableContext() {
7214
7484
  function useTableContextSafe() {
7215
7485
  return react.useContext(TableContext);
7216
7486
  }
7487
+
7488
+ // src/components/compounds/Table/cell-size-class.ts
7489
+ var cellSizeClass = {
7490
+ sm: "px-3 py-1.5 text-xs",
7491
+ md: "px-5 py-2.5 text-sm",
7492
+ lg: "px-6 py-3 text-sm"
7493
+ };
7217
7494
  var cardPadding = {
7218
7495
  sm: "p-3",
7219
7496
  md: "p-4",
@@ -7551,6 +7828,9 @@ function DataTableHeaderCell({ header }) {
7551
7828
  },
7552
7829
  className: cn(
7553
7830
  "bg-primary text-primary-foreground",
7831
+ // Same density padding as the body cells: without it the header
7832
+ // label and the cell value below it sit at different offsets.
7833
+ cellSizeClass[meta.size ?? tableSize],
7554
7834
  canResize && "relative",
7555
7835
  meta.align === "center" && "text-center",
7556
7836
  meta.align === "right" && "text-right",
@@ -7940,11 +8220,6 @@ function useTablePersistence({
7940
8220
  persistDensity
7941
8221
  };
7942
8222
  }
7943
- var cellSizeClass = {
7944
- sm: "px-1 py-0 text-xs",
7945
- md: "px-5 py-2.5 text-sm",
7946
- lg: "px-6 py-3 text-sm"
7947
- };
7948
8223
  function resolveClassName(value, row) {
7949
8224
  return typeof value === "function" ? value(row) : value;
7950
8225
  }
@@ -7985,6 +8260,13 @@ function DataTableCell({
7985
8260
  }
7986
8261
  );
7987
8262
  }
8263
+ var INTERACTIVE_CELL_CONTENT = 'button, a, input, select, textarea, [role="button"], [role="checkbox"], [role="menuitem"], [role="link"]';
8264
+ function isInteractiveCellEvent(event) {
8265
+ const target = event.target;
8266
+ if (!(target instanceof Element)) return false;
8267
+ const match = target.closest(INTERACTIVE_CELL_CONTENT);
8268
+ return match !== null && match !== event.currentTarget;
8269
+ }
7988
8270
  function DataTableRow({ row, classNames }) {
7989
8271
  const { onRowClick, variant } = useTableContext();
7990
8272
  const clickable = Boolean(onRowClick);
@@ -7997,12 +8279,15 @@ function DataTableRow({ row, classNames }) {
7997
8279
  "data-clickable": clickable || void 0,
7998
8280
  role: clickable ? "button" : void 0,
7999
8281
  tabIndex: clickable ? 0 : void 0,
8000
- onClick: clickable ? () => onRowClick?.(row.original) : void 0,
8282
+ onClick: clickable ? (e) => {
8283
+ if (isInteractiveCellEvent(e)) return;
8284
+ onRowClick?.(row.original);
8285
+ } : void 0,
8001
8286
  onKeyDown: clickable ? (e) => {
8002
- if (e.key === "Enter" || e.key === " ") {
8003
- e.preventDefault();
8004
- onRowClick?.(row.original);
8005
- }
8287
+ if (e.key !== "Enter" && e.key !== " ") return;
8288
+ if (isInteractiveCellEvent(e)) return;
8289
+ e.preventDefault();
8290
+ onRowClick?.(row.original);
8006
8291
  } : void 0,
8007
8292
  className: cn("hover:bg-muted", clickable && "cursor-pointer", rowClassName),
8008
8293
  children: row.getVisibleCells().map((cell) => /* @__PURE__ */ jsxRuntime.jsx(DataTableCell, { cell, cellClassName }, cell.id))
@@ -8603,7 +8888,7 @@ function DataTableFooter({ pagination, selectedCount, className }) {
8603
8888
  className: cn("flex items-center justify-between border-t px-4 py-2", className),
8604
8889
  children: [
8605
8890
  selectedCount != null && selectedCount > 0 && /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-live": "polite", className: "text-muted-foreground text-xs", children: `${selectedCount} row${selectedCount > 1 ? "s" : ""} selected` }),
8606
- /* @__PURE__ */ jsxRuntime.jsx(TablePagination, { ...pagination })
8891
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "ml-auto", children: /* @__PURE__ */ jsxRuntime.jsx(TablePagination, { ...pagination }) })
8607
8892
  ]
8608
8893
  }
8609
8894
  );
@@ -8712,18 +8997,21 @@ function DataTableSearch({
8712
8997
  debounce = 300
8713
8998
  }) {
8714
8999
  const ctx = useTableContextSafe();
8715
- const [inputValue, setInputValue] = react.useState("");
8716
9000
  const globalFilter = ctx?.globalFilter;
8717
9001
  const setGlobalFilter = ctx?.setGlobalFilter;
9002
+ const committedValue = globalFilter ?? "";
9003
+ const [inputValue, setInputValue] = react.useState(committedValue);
8718
9004
  react.useEffect(() => {
8719
- if (globalFilter === "") setInputValue("");
9005
+ if (globalFilter === void 0) return;
9006
+ setInputValue(globalFilter);
8720
9007
  }, [globalFilter]);
8721
9008
  react.useEffect(() => {
9009
+ if (inputValue === committedValue) return;
8722
9010
  const timer = setTimeout(() => {
8723
9011
  setGlobalFilter?.(inputValue);
8724
9012
  }, debounce);
8725
9013
  return () => clearTimeout(timer);
8726
- }, [inputValue, debounce, setGlobalFilter]);
9014
+ }, [inputValue, committedValue, debounce, setGlobalFilter]);
8727
9015
  const handleClear = () => {
8728
9016
  setInputValue("");
8729
9017
  setGlobalFilter?.("");
@@ -8744,7 +9032,13 @@ function DataTableSearch({
8744
9032
  onChange: (e) => setInputValue(e.target.value),
8745
9033
  placeholder,
8746
9034
  "aria-label": placeholder,
8747
- className: "h-8 w-48 pr-8 pl-8 text-sm"
9035
+ className: cn(
9036
+ "h-8 w-48 pr-9 pl-9 text-sm",
9037
+ "[&::-webkit-search-cancel-button]:appearance-none",
9038
+ "[&::-webkit-search-decoration]:appearance-none",
9039
+ "[&::-webkit-search-results-button]:appearance-none",
9040
+ "[&::-webkit-search-results-decoration]:appearance-none"
9041
+ )
8748
9042
  }
8749
9043
  ),
8750
9044
  inputValue && /* @__PURE__ */ jsxRuntime.jsx(
@@ -8754,7 +9048,7 @@ function DataTableSearch({
8754
9048
  size: "icon-sm",
8755
9049
  onClick: handleClear,
8756
9050
  "aria-label": "Clear search",
8757
- className: "text-muted-foreground hover:text-foreground absolute right-0.5 size-6",
9051
+ className: "text-muted-foreground hover:text-foreground absolute top-1/2 right-1.5 size-6 -translate-y-1/2",
8758
9052
  children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { className: "size-3.5", "aria-hidden": "true" })
8759
9053
  }
8760
9054
  )
@@ -9130,6 +9424,198 @@ function DrawerDescription({
9130
9424
  }
9131
9425
  );
9132
9426
  }
9427
+ function SignaturePad({
9428
+ "aria-label": ariaLabel,
9429
+ "aria-describedby": ariaDescribedBy,
9430
+ onEmptyChange,
9431
+ disabled = false,
9432
+ height = 260,
9433
+ penColor = "black",
9434
+ // Smoother than the library defaults (0.5–2.5 px, 5 px, 16 ms), which drop points and turn
9435
+ // fast or small curves into visible straight segments.
9436
+ minWidth = 1,
9437
+ maxWidth = 3,
9438
+ minDistance = 1,
9439
+ throttle = 0,
9440
+ velocityFilterWeight,
9441
+ clearLabel,
9442
+ className,
9443
+ ref
9444
+ }) {
9445
+ const canvasRef = react.useRef(null);
9446
+ const padRef = react.useRef(null);
9447
+ const emptyRef = react.useRef(true);
9448
+ const [empty, setEmptyState] = react.useState(true);
9449
+ const onEmptyChangeRef = react.useRef(onEmptyChange);
9450
+ const drawnRef = react.useRef({
9451
+ data: [],
9452
+ width: 0,
9453
+ height: 0,
9454
+ scale: 1
9455
+ });
9456
+ const optionsRef = react.useRef({
9457
+ penColor,
9458
+ minWidth,
9459
+ maxWidth,
9460
+ minDistance,
9461
+ throttle,
9462
+ velocityFilterWeight
9463
+ });
9464
+ react.useEffect(() => {
9465
+ onEmptyChangeRef.current = onEmptyChange;
9466
+ }, [onEmptyChange]);
9467
+ const setEmpty = (next) => {
9468
+ if (emptyRef.current === next) return;
9469
+ emptyRef.current = next;
9470
+ setEmptyState(next);
9471
+ onEmptyChangeRef.current?.(next);
9472
+ };
9473
+ const clear = () => {
9474
+ padRef.current?.clear();
9475
+ drawnRef.current = { data: [], width: 0, height: 0, scale: 1 };
9476
+ setEmpty(true);
9477
+ };
9478
+ react.useEffect(() => {
9479
+ const canvas = canvasRef.current;
9480
+ if (!canvas) return;
9481
+ const pad = new SignaturePadLib__default.default(canvas, optionsRef.current);
9482
+ padRef.current = pad;
9483
+ let width = 0;
9484
+ let fittedHeight = 0;
9485
+ const redraw = () => {
9486
+ const drawn = drawnRef.current;
9487
+ drawn.scale = Math.min(width / drawn.width, fittedHeight / drawn.height, 1);
9488
+ pad.clear();
9489
+ pad.fromData(
9490
+ drawn.data.map((group) => ({
9491
+ ...group,
9492
+ points: group.points.map((point) => ({
9493
+ ...point,
9494
+ x: point.x * drawn.scale,
9495
+ y: point.y * drawn.scale
9496
+ }))
9497
+ }))
9498
+ );
9499
+ };
9500
+ const handleEnd = () => {
9501
+ const drawn = drawnRef.current;
9502
+ if (drawn.data.length === 0) {
9503
+ drawn.width = width;
9504
+ drawn.height = fittedHeight;
9505
+ drawn.scale = 1;
9506
+ }
9507
+ const newest = pad.toData().at(-1);
9508
+ if (newest) {
9509
+ const points = newest.points.map((point) => ({
9510
+ ...point,
9511
+ x: point.x / drawn.scale,
9512
+ y: point.y / drawn.scale
9513
+ }));
9514
+ drawn.data = [...drawn.data, { ...newest, points }];
9515
+ const margin = newest.maxWidth ?? 0;
9516
+ const reach = (axis) => Math.max(...points.map((point) => point[axis])) + margin;
9517
+ drawn.width = Math.max(drawn.width, reach("x"));
9518
+ drawn.height = Math.max(drawn.height, reach("y"));
9519
+ const fitting = Math.min(width / drawn.width, fittedHeight / drawn.height, 1);
9520
+ if (fitting !== drawn.scale) redraw();
9521
+ }
9522
+ setEmpty(pad.isEmpty());
9523
+ };
9524
+ pad.addEventListener("endStroke", handleEnd);
9525
+ const fit = () => {
9526
+ const nextWidth = canvas.offsetWidth;
9527
+ const nextHeight = canvas.offsetHeight;
9528
+ if (!nextWidth || !nextHeight) return;
9529
+ if (nextWidth === width && nextHeight === fittedHeight) return;
9530
+ width = nextWidth;
9531
+ fittedHeight = nextHeight;
9532
+ const ratio = window.devicePixelRatio || 1;
9533
+ canvas.width = nextWidth * ratio;
9534
+ canvas.height = nextHeight * ratio;
9535
+ canvas.getContext("2d")?.scale(ratio, ratio);
9536
+ pad.clear();
9537
+ if (drawnRef.current.data.length > 0) redraw();
9538
+ };
9539
+ fit();
9540
+ const observer = new ResizeObserver(fit);
9541
+ observer.observe(canvas);
9542
+ return () => {
9543
+ observer.disconnect();
9544
+ pad.removeEventListener("endStroke", handleEnd);
9545
+ pad.off();
9546
+ padRef.current = null;
9547
+ };
9548
+ }, []);
9549
+ react.useEffect(() => {
9550
+ if (disabled) padRef.current?.off();
9551
+ else padRef.current?.on();
9552
+ }, [disabled]);
9553
+ react.useImperativeHandle(
9554
+ ref,
9555
+ () => ({
9556
+ clear,
9557
+ isEmpty: () => padRef.current?.isEmpty() ?? true,
9558
+ toBlob: () => new Promise((resolve, reject) => {
9559
+ const canvas = canvasRef.current;
9560
+ if (!canvas) return reject(new Error("The signature pad is not mounted"));
9561
+ canvas.toBlob((blob) => {
9562
+ if (blob) resolve(blob);
9563
+ else reject(new Error("The signature could not be encoded as PNG"));
9564
+ }, "image/png");
9565
+ })
9566
+ }),
9567
+ // `clear` only touches refs and state setters, so the handle is created once.
9568
+ // eslint-disable-next-line react-hooks/exhaustive-deps
9569
+ []
9570
+ );
9571
+ return /* @__PURE__ */ jsxRuntime.jsxs(
9572
+ "div",
9573
+ {
9574
+ "data-slot": "signature-pad",
9575
+ "data-disabled": disabled ? "" : void 0,
9576
+ className: cn(
9577
+ // Fixed light "paper": the pen is black and the PNG is read on white by others. The
9578
+ // primitive keeps the pad light for consumers that define a dark theme, where a themed
9579
+ // surface (`background`, `border`) would turn dark and hide the black ink.
9580
+ "bg-main-alternative-secondary border-dark-100 relative overflow-hidden rounded-xl border",
9581
+ "data-[disabled]:cursor-not-allowed data-[disabled]:opacity-50",
9582
+ className
9583
+ ),
9584
+ children: [
9585
+ /* @__PURE__ */ jsxRuntime.jsx(
9586
+ "canvas",
9587
+ {
9588
+ ref: canvasRef,
9589
+ "data-slot": "signature-pad-canvas",
9590
+ role: "img",
9591
+ "aria-label": ariaLabel,
9592
+ "aria-describedby": ariaDescribedBy,
9593
+ "aria-disabled": disabled || void 0,
9594
+ style: { height },
9595
+ className: "block w-full touch-none"
9596
+ }
9597
+ ),
9598
+ /* @__PURE__ */ jsxRuntime.jsx(
9599
+ Button,
9600
+ {
9601
+ type: "button",
9602
+ variant: "ghost",
9603
+ size: "icon-sm",
9604
+ "data-slot": "signature-pad-clear",
9605
+ "aria-label": clearLabel,
9606
+ disabled,
9607
+ "aria-disabled": empty && !disabled ? true : void 0,
9608
+ onClick: () => {
9609
+ if (!emptyRef.current) clear();
9610
+ },
9611
+ className: "bg-main-white text-dark-600 border-dark-100 hover:bg-dark-100 hover:text-main-black aria-disabled:hover:bg-main-white absolute right-2 bottom-2 border border-solid aria-disabled:cursor-not-allowed aria-disabled:opacity-50",
9612
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.BrushCleaning, { "aria-hidden": true, className: "size-4" })
9613
+ }
9614
+ )
9615
+ ]
9616
+ }
9617
+ );
9618
+ }
9133
9619
  function TCCEProvider({
9134
9620
  children,
9135
9621
  permissions,
@@ -9191,6 +9677,7 @@ exports.BreadcrumbList = BreadcrumbList;
9191
9677
  exports.BreadcrumbPage = BreadcrumbPage;
9192
9678
  exports.BreadcrumbSeparator = BreadcrumbSeparator;
9193
9679
  exports.Button = Button;
9680
+ exports.CONFETTI_COLORS = CONFETTI_COLORS;
9194
9681
  exports.Can = Can;
9195
9682
  exports.CanNot = CanNot;
9196
9683
  exports.Card = Card;
@@ -9289,6 +9776,7 @@ exports.ItemHeader = ItemHeader;
9289
9776
  exports.ItemMedia = ItemMedia;
9290
9777
  exports.ItemSeparator = ItemSeparator;
9291
9778
  exports.ItemTitle = ItemTitle;
9779
+ exports.KNOWN_PERMISSIONS = KNOWN_PERMISSIONS;
9292
9780
  exports.Label = Label;
9293
9781
  exports.Link = Link;
9294
9782
  exports.Logo = Logo;
@@ -9368,6 +9856,7 @@ exports.SidebarProvider = SidebarProvider;
9368
9856
  exports.SidebarRail = SidebarRail;
9369
9857
  exports.SidebarSeparator = SidebarSeparator;
9370
9858
  exports.SidebarTrigger = SidebarTrigger;
9859
+ exports.SignaturePad = SignaturePad;
9371
9860
  exports.Skeleton = Skeleton;
9372
9861
  exports.Slider = Slider;
9373
9862
  exports.Spinner = Spinner;
@@ -9417,6 +9906,7 @@ exports.createBackendParamsWithFilters = createBackendParamsWithFilters;
9417
9906
  exports.duration = duration;
9418
9907
  exports.ease = ease;
9419
9908
  exports.fadeIn = fadeIn;
9909
+ exports.fireConfetti = fireConfetti;
9420
9910
  exports.getActiveFilterCount = getActiveFilterCount;
9421
9911
  exports.getActiveFilterValues = getActiveFilterValues;
9422
9912
  exports.getColumnValueCounts = getColumnValueCounts;
@@ -9432,6 +9922,7 @@ exports.parseJsonToFilters = parseJsonToFilters;
9432
9922
  exports.parseQueryParamsToFilters = parseQueryParamsToFilters;
9433
9923
  exports.progressIndicatorVariants = progressIndicatorVariants;
9434
9924
  exports.progressVariants = progressVariants;
9925
+ exports.resetConfetti = resetConfetti;
9435
9926
  exports.scaleIn = scaleIn;
9436
9927
  exports.searchInArray = searchInArray;
9437
9928
  exports.skeletonVariants = skeletonVariants;
@@ -9449,6 +9940,7 @@ exports.toggleVariants = toggleVariants;
9449
9940
  exports.tooltipContentVariants = tooltipContentVariants;
9450
9941
  exports.transition = transition;
9451
9942
  exports.useAbility = useAbility;
9943
+ exports.useAbilityStatus = useAbilityStatus;
9452
9944
  exports.useAppForm = useAppForm;
9453
9945
  exports.useCan = useCan;
9454
9946
  exports.useCarousel = useCarousel;