@hoardodile/ui 0.1.7 → 0.1.9
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/chunk-2BVZEYNW.js +97 -0
- package/dist/chunk-2BVZEYNW.js.map +1 -0
- package/dist/chunk-2DTR6CD5.js +47 -0
- package/dist/chunk-2DTR6CD5.js.map +1 -0
- package/dist/chunk-2HWSCY4T.js +20 -0
- package/dist/chunk-2HWSCY4T.js.map +1 -0
- package/dist/chunk-363CCKQ7.js +75 -0
- package/dist/chunk-363CCKQ7.js.map +1 -0
- package/dist/chunk-3ZQJWBVB.js +30 -0
- package/dist/chunk-3ZQJWBVB.js.map +1 -0
- package/dist/chunk-4D36OO5W.js +277 -0
- package/dist/chunk-4D36OO5W.js.map +1 -0
- package/dist/chunk-4UITFLIN.js +31 -0
- package/dist/chunk-4UITFLIN.js.map +1 -0
- package/dist/chunk-6HAIUXNZ.js +11 -0
- package/dist/chunk-6HAIUXNZ.js.map +1 -0
- package/dist/chunk-7JMMFY67.js +62 -0
- package/dist/chunk-7JMMFY67.js.map +1 -0
- package/dist/chunk-7MWWHI6T.js +1149 -0
- package/dist/chunk-7MWWHI6T.js.map +1 -0
- package/dist/chunk-AEHTAUAQ.js +65 -0
- package/dist/chunk-AEHTAUAQ.js.map +1 -0
- package/dist/chunk-ASRGQ5BD.js +130 -0
- package/dist/chunk-ASRGQ5BD.js.map +1 -0
- package/dist/chunk-BFGOE5AI.js +187 -0
- package/dist/chunk-BFGOE5AI.js.map +1 -0
- package/dist/chunk-BRT25IDQ.js +46 -0
- package/dist/chunk-BRT25IDQ.js.map +1 -0
- package/dist/chunk-BU5DQOPM.js +29 -0
- package/dist/chunk-BU5DQOPM.js.map +1 -0
- package/dist/chunk-BWASOT5X.js +266 -0
- package/dist/chunk-BWASOT5X.js.map +1 -0
- package/dist/chunk-F32V4Z7Q.js +26 -0
- package/dist/chunk-F32V4Z7Q.js.map +1 -0
- package/dist/chunk-F7RHDRRJ.js +24 -0
- package/dist/chunk-F7RHDRRJ.js.map +1 -0
- package/dist/chunk-FH5RNCFR.js +21 -0
- package/dist/chunk-FH5RNCFR.js.map +1 -0
- package/dist/chunk-GKSX5ULZ.js +513 -0
- package/dist/chunk-GKSX5ULZ.js.map +1 -0
- package/dist/chunk-JPFDNRCA.js +26 -0
- package/dist/chunk-JPFDNRCA.js.map +1 -0
- package/dist/chunk-KNPRUZ4H.js +13 -0
- package/dist/chunk-KNPRUZ4H.js.map +1 -0
- package/dist/chunk-KOFWEPPB.js +254 -0
- package/dist/chunk-KOFWEPPB.js.map +1 -0
- package/dist/chunk-M5OJN54O.js +10 -0
- package/dist/chunk-M5OJN54O.js.map +1 -0
- package/dist/chunk-OEVALKQ5.js +51 -0
- package/dist/chunk-OEVALKQ5.js.map +1 -0
- package/dist/chunk-OW5FWLPH.js +33 -0
- package/dist/chunk-OW5FWLPH.js.map +1 -0
- package/dist/chunk-POX7TXBH.js +41 -0
- package/dist/chunk-POX7TXBH.js.map +1 -0
- package/dist/chunk-PWJBHUM2.js +58 -0
- package/dist/chunk-PWJBHUM2.js.map +1 -0
- package/dist/chunk-QGMDWGJT.js +23 -0
- package/dist/chunk-QGMDWGJT.js.map +1 -0
- package/dist/chunk-QGTQQNUL.js +36 -0
- package/dist/chunk-QGTQQNUL.js.map +1 -0
- package/dist/chunk-SAY52ZKI.js +152 -0
- package/dist/chunk-SAY52ZKI.js.map +1 -0
- package/dist/chunk-SKDPGSRL.js +166 -0
- package/dist/chunk-SKDPGSRL.js.map +1 -0
- package/dist/chunk-T56ARCUK.js +38 -0
- package/dist/chunk-T56ARCUK.js.map +1 -0
- package/dist/chunk-TDDHQZ62.js +227 -0
- package/dist/chunk-TDDHQZ62.js.map +1 -0
- package/dist/chunk-TMQ5OMBK.js +29 -0
- package/dist/chunk-TMQ5OMBK.js.map +1 -0
- package/dist/chunk-UEVND7AS.js +23 -0
- package/dist/chunk-UEVND7AS.js.map +1 -0
- package/dist/chunk-UPWHCG4S.js +40 -0
- package/dist/chunk-UPWHCG4S.js.map +1 -0
- package/dist/chunk-VWUNF7VX.js +3 -0
- package/dist/chunk-VWUNF7VX.js.map +1 -0
- package/dist/chunk-WI6O7GZK.js +45 -0
- package/dist/chunk-WI6O7GZK.js.map +1 -0
- package/dist/chunk-WVEK3WCX.js +21 -0
- package/dist/chunk-WVEK3WCX.js.map +1 -0
- package/dist/chunk-XBPQEZES.js +48 -0
- package/dist/chunk-XBPQEZES.js.map +1 -0
- package/dist/chunk-ZYAGKKR7.js +142 -0
- package/dist/chunk-ZYAGKKR7.js.map +1 -0
- package/dist/components/alert.js +2 -21
- package/dist/components/alert.js.map +1 -1
- package/dist/components/app-dialog.js +10 -654
- package/dist/components/app-dialog.js.map +1 -1
- package/dist/components/badge.js +2 -23
- package/dist/components/badge.js.map +1 -1
- package/dist/components/breadcrumb.js +5 -504
- package/dist/components/breadcrumb.js.map +1 -1
- package/dist/components/button.js +3 -76
- package/dist/components/button.js.map +1 -1
- package/dist/components/caption-bar.js +7 -521
- package/dist/components/caption-bar.js.map +1 -1
- package/dist/components/checkbox.js +4 -74
- package/dist/components/checkbox.js.map +1 -1
- package/dist/components/chip-button.js +4 -85
- package/dist/components/chip-button.js.map +1 -1
- package/dist/components/close-confirm-dialog.js +12 -662
- package/dist/components/close-confirm-dialog.js.map +1 -1
- package/dist/components/color-picker.js +7 -533
- package/dist/components/color-picker.js.map +1 -1
- package/dist/components/combobox.js +15 -937
- package/dist/components/combobox.js.map +1 -1
- package/dist/components/confirm-by-typing-dialog.js +12 -669
- package/dist/components/confirm-by-typing-dialog.js.map +1 -1
- package/dist/components/confirm-dialog.js +11 -637
- package/dist/components/confirm-dialog.js.map +1 -1
- package/dist/components/count-badge.js +2 -21
- package/dist/components/count-badge.js.map +1 -1
- package/dist/components/dialog.js +8 -569
- package/dist/components/dialog.js.map +1 -1
- package/dist/components/dropdown-menu.js +9 -1007
- package/dist/components/dropdown-menu.js.map +1 -1
- package/dist/components/dropdown-select.js +9 -883
- package/dist/components/dropdown-select.js.map +1 -1
- package/dist/components/empty.js +2 -21
- package/dist/components/empty.js.map +1 -1
- package/dist/components/filter-rail.js +7 -145
- package/dist/components/filter-rail.js.map +1 -1
- package/dist/components/fixed-action-bar.js +2 -21
- package/dist/components/fixed-action-bar.js.map +1 -1
- package/dist/components/font-picker.js +11 -589
- package/dist/components/font-picker.js.map +1 -1
- package/dist/components/form.js +9 -66
- package/dist/components/form.js.map +1 -1
- package/dist/components/icon-tile.js +3 -43
- package/dist/components/icon-tile.js.map +1 -1
- package/dist/components/icon-toggle.js +3 -43
- package/dist/components/icon-toggle.js.map +1 -1
- package/dist/components/icon.js +3 -47
- package/dist/components/icon.js.map +1 -1
- package/dist/components/image-crop-panel.js +19 -775
- package/dist/components/image-crop-panel.js.map +1 -1
- package/dist/components/image-cropper.js +1 -164
- package/dist/components/image-cropper.js.map +1 -1
- package/dist/components/input-group.js +6 -268
- package/dist/components/input-group.js.map +1 -1
- package/dist/components/input.js +3 -58
- package/dist/components/input.js.map +1 -1
- package/dist/components/label.js +3 -38
- package/dist/components/label.js.map +1 -1
- package/dist/components/list-empty-row.js +2 -21
- package/dist/components/list-empty-row.js.map +1 -1
- package/dist/components/management-empty.js +2 -21
- package/dist/components/management-empty.js.map +1 -1
- package/dist/components/management-skeleton.js +4 -59
- package/dist/components/management-skeleton.js.map +1 -1
- package/dist/components/meta-chip.js +2 -21
- package/dist/components/meta-chip.js.map +1 -1
- package/dist/components/mobile-drawer.js +7 -268
- package/dist/components/mobile-drawer.js.map +1 -1
- package/dist/components/page-header.js +3 -42
- package/dist/components/page-header.js.map +1 -1
- package/dist/components/page-scaffold.js +5 -76
- package/dist/components/page-scaffold.js.map +1 -1
- package/dist/components/pagination-bar.js +13 -617
- package/dist/components/pagination-bar.js.map +1 -1
- package/dist/components/pagination.js +6 -684
- package/dist/components/pagination.js.map +1 -1
- package/dist/components/panel-toolbar.js +11 -716
- package/dist/components/panel-toolbar.js.map +1 -1
- package/dist/components/pill-tabs.js +2 -21
- package/dist/components/pill-tabs.js.map +1 -1
- package/dist/components/plugin-download-consent.js +13 -663
- package/dist/components/plugin-download-consent.js.map +1 -1
- package/dist/components/popover.js +5 -242
- package/dist/components/popover.js.map +1 -1
- package/dist/components/preview-card.js +2 -21
- package/dist/components/preview-card.js.map +1 -1
- package/dist/components/progress.js +2 -20
- package/dist/components/progress.js.map +1 -1
- package/dist/components/radio-group.js +2 -21
- package/dist/components/radio-group.js.map +1 -1
- package/dist/components/scroll-area.js +2 -21
- package/dist/components/scroll-area.js.map +1 -1
- package/dist/components/search-field.js +6 -590
- package/dist/components/search-field.js.map +1 -1
- package/dist/components/secondary-stat.js +3 -40
- package/dist/components/secondary-stat.js.map +1 -1
- package/dist/components/section-header.js +3 -43
- package/dist/components/section-header.js.map +1 -1
- package/dist/components/section-label.js +3 -44
- package/dist/components/section-label.js.map +1 -1
- package/dist/components/section-tabs.js +2 -21
- package/dist/components/section-tabs.js.map +1 -1
- package/dist/components/separator.js +3 -47
- package/dist/components/separator.js.map +1 -1
- package/dist/components/sheet.js +8 -467
- package/dist/components/sheet.js.map +1 -1
- package/dist/components/sidebar.js +24 -1047
- package/dist/components/sidebar.js.map +1 -1
- package/dist/components/skeleton.js +3 -39
- package/dist/components/skeleton.js.map +1 -1
- package/dist/components/slider.js +2 -21
- package/dist/components/slider.js.map +1 -1
- package/dist/components/sortable-chip-list.js +2 -21
- package/dist/components/sortable-chip-list.js.map +1 -1
- package/dist/components/special-tag-surface.js +3 -531
- package/dist/components/special-tag-surface.js.map +1 -1
- package/dist/components/spinner.js +3 -63
- package/dist/components/spinner.js.map +1 -1
- package/dist/components/surface.js +3 -49
- package/dist/components/surface.js.map +1 -1
- package/dist/components/switch.js +3 -51
- package/dist/components/switch.js.map +1 -1
- package/dist/components/table.js +2 -20
- package/dist/components/table.js.map +1 -1
- package/dist/components/tag-chip.js +6 -617
- package/dist/components/tag-chip.js.map +1 -1
- package/dist/components/textarea.js +3 -56
- package/dist/components/textarea.js.map +1 -1
- package/dist/components/toast.js +8 -809
- package/dist/components/toast.js.map +1 -1
- package/dist/components/toggle.js +2 -20
- package/dist/components/toggle.js.map +1 -1
- package/dist/components/tooltip.js +3 -80
- package/dist/components/tooltip.js.map +1 -1
- package/dist/hooks/use-dialog-footer-actions.js +1 -9
- package/dist/hooks/use-dialog-footer-actions.js.map +1 -1
- package/dist/hooks/use-form-field.js +1 -34
- package/dist/hooks/use-form-field.js.map +1 -1
- package/dist/hooks/use-mobile.js +2 -27
- package/dist/hooks/use-mobile.js.map +1 -1
- package/dist/hooks/useMobileBackToClose.js +3 -232
- package/dist/hooks/useMobileBackToClose.js.map +1 -1
- package/dist/icons/actions.js +4 -524
- package/dist/icons/actions.js.map +1 -1
- package/dist/icons/icon-style.js +1 -49
- package/dist/icons/icon-style.js.map +1 -1
- package/dist/icons/marks.js +1 -46
- package/dist/icons/marks.js.map +1 -1
- package/dist/icons/registry.js +2 -1194
- package/dist/icons/registry.js.map +1 -1
- package/dist/index.js +10 -651
- package/dist/index.js.map +1 -1
- package/dist/lib/colors.js +1 -63
- package/dist/lib/colors.js.map +1 -1
- package/dist/lib/merge-config.js +1 -18
- package/dist/lib/merge-config.js.map +1 -1
- package/dist/lib/pagination.js +1 -24
- package/dist/lib/pagination.js.map +1 -1
- package/dist/lib/tag-surface.js +5 -613
- package/dist/lib/tag-surface.js.map +1 -1
- package/dist/lib/utils.js +2 -24
- package/dist/lib/utils.js.map +1 -1
- package/dist/res-card-template/format.js +1 -21
- package/dist/res-card-template/format.js.map +1 -1
- package/dist/res-card-template/icon.js +1 -39
- package/dist/res-card-template/icon.js.map +1 -1
- package/dist/res-card-template/index.js +3 -329
- package/dist/res-card-template/index.js.map +1 -1
- package/dist/res-card-template/template.js +3 -331
- package/dist/res-card-template/template.js.map +1 -1
- package/dist/viewport.js +1 -11
- package/dist/viewport.js.map +1 -1
- package/package.json +3 -3
- package/src/components/image-crop-panel.dist.test.tsx +54 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/hooks/use-form-field.ts"],"names":[],"mappings":";;;;AAuBA,IAAM,gBAAA,GAAyB,KAAA,CAAA,aAAA;AAAA,EAC9B;AACD;AAMA,IAAM,eAAA,GAAwB,KAAA,CAAA,aAAA;AAAA,EAC7B;AACD;AAEA,SAAS,YAAA,GAAe;AACvB,EAAA,MAAM,YAAA,GAAqB,iBAAW,gBAAgB,CAAA;AACtD,EAAA,MAAM,WAAA,GAAoB,iBAAW,eAAe,CAAA;AACpD,EAAA,MAAM,EAAE,aAAA,EAAc,GAAI,cAAA,EAAe;AACzC,EAAA,MAAM,YAAY,YAAA,CAAa,EAAE,IAAA,EAAM,YAAA,EAAc,MAAM,CAAA;AAE3D,EAAA,IAAI,CAAC,YAAA,EAAc;AAClB,IAAA,MAAM,IAAI,MAAM,gDAAgD,CAAA;AAAA,EACjE;AACA,EAAA,IAAI,CAAC,WAAA,EAAa;AACjB,IAAA,MAAM,IAAI,MAAM,+CAA+C,CAAA;AAAA,EAChE;AAEA,EAAA,MAAM,UAAA,GAAa,aAAA,CAAc,YAAA,CAAa,IAAA,EAAM,SAAS,CAAA;AAC7D,EAAA,MAAM,EAAE,IAAG,GAAI,WAAA;AAEf,EAAA,OAAO;AAAA,IACN,EAAA;AAAA,IACA,MAAM,YAAA,CAAa,IAAA;AAAA,IACnB,UAAA,EAAY,GAAG,EAAE,CAAA,UAAA,CAAA;AAAA,IACjB,iBAAA,EAAmB,GAAG,EAAE,CAAA,sBAAA,CAAA;AAAA,IACxB,aAAA,EAAe,GAAG,EAAE,CAAA,kBAAA,CAAA;AAAA,IACpB,GAAG;AAAA,GACJ;AACD","file":"chunk-QGTQQNUL.js","sourcesContent":["import * as React from \"react\"\nimport {\n\ttype FieldPath,\n\ttype FieldValues,\n\tuseFormContext,\n\tuseFormState,\n} from \"react-hook-form\"\n\n/**\n * Field-state plumbing shared by the `Form*` components in\n * `@hoardodile/ui/components/form`: connects a `<FormField>` (the\n * react-hook-form `Controller` wrapper) to its `<FormItem>` via two\n * private contexts and exposes the resolved field state — id parts and\n * error — to the label/control/description/message render pieces.\n */\n\ntype FormFieldContextValue<\n\tTFieldValues extends FieldValues = FieldValues,\n\tTName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,\n> = {\n\tname: TName\n}\n\nconst FormFieldContext = React.createContext<FormFieldContextValue | undefined>(\n\tundefined,\n)\n\ntype FormItemContextValue = {\n\tid: string\n}\n\nconst FormItemContext = React.createContext<FormItemContextValue | undefined>(\n\tundefined,\n)\n\nfunction useFormField() {\n\tconst fieldContext = React.useContext(FormFieldContext)\n\tconst itemContext = React.useContext(FormItemContext)\n\tconst { getFieldState } = useFormContext()\n\tconst formState = useFormState({ name: fieldContext?.name })\n\n\tif (!fieldContext) {\n\t\tthrow new Error(\"useFormField should be used within <FormField>\")\n\t}\n\tif (!itemContext) {\n\t\tthrow new Error(\"useFormField should be used within <FormItem>\")\n\t}\n\n\tconst fieldState = getFieldState(fieldContext.name, formState)\n\tconst { id } = itemContext\n\n\treturn {\n\t\tid,\n\t\tname: fieldContext.name,\n\t\tformItemId: `${id}-form-item`,\n\t\tformDescriptionId: `${id}-form-item-description`,\n\t\tformMessageId: `${id}-form-item-message`,\n\t\t...fieldState,\n\t}\n}\n\nexport { FormFieldContext, FormItemContext, useFormField }\n"]}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import { useMobileBackToClose } from './chunk-TDDHQZ62.js';
|
|
2
|
+
import { Cross } from './chunk-XBPQEZES.js';
|
|
3
|
+
import { Button } from './chunk-PWJBHUM2.js';
|
|
4
|
+
import { cn } from './chunk-M5OJN54O.js';
|
|
5
|
+
import * as React from 'react';
|
|
6
|
+
import { Dialog } from '@base-ui/react/dialog';
|
|
7
|
+
import { useTranslation } from 'react-i18next';
|
|
8
|
+
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
9
|
+
|
|
10
|
+
function Sheet({
|
|
11
|
+
open,
|
|
12
|
+
defaultOpen,
|
|
13
|
+
onOpenChange,
|
|
14
|
+
...props
|
|
15
|
+
}) {
|
|
16
|
+
const [uncontrolledOpen, setUncontrolledOpen] = React.useState(
|
|
17
|
+
defaultOpen ?? false
|
|
18
|
+
);
|
|
19
|
+
const isControlled = open !== void 0;
|
|
20
|
+
const currentOpen = isControlled ? open : uncontrolledOpen;
|
|
21
|
+
function handleOpenChange(next, eventDetails) {
|
|
22
|
+
if (!isControlled) setUncontrolledOpen(next);
|
|
23
|
+
onOpenChange?.(next, eventDetails);
|
|
24
|
+
}
|
|
25
|
+
useMobileBackToClose(currentOpen, handleOpenChange);
|
|
26
|
+
return /* @__PURE__ */ jsx(
|
|
27
|
+
Dialog.Root,
|
|
28
|
+
{
|
|
29
|
+
"data-slot": "sheet",
|
|
30
|
+
open: currentOpen,
|
|
31
|
+
onOpenChange: handleOpenChange,
|
|
32
|
+
...props
|
|
33
|
+
}
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
function SheetTrigger({ ...props }) {
|
|
37
|
+
return /* @__PURE__ */ jsx(Dialog.Trigger, { "data-slot": "sheet-trigger", ...props });
|
|
38
|
+
}
|
|
39
|
+
function SheetClose({ ...props }) {
|
|
40
|
+
return /* @__PURE__ */ jsx(Dialog.Close, { "data-slot": "sheet-close", ...props });
|
|
41
|
+
}
|
|
42
|
+
function SheetPortal({ ...props }) {
|
|
43
|
+
return /* @__PURE__ */ jsx(Dialog.Portal, { "data-slot": "sheet-portal", ...props });
|
|
44
|
+
}
|
|
45
|
+
function SheetOverlay({ className, ...props }) {
|
|
46
|
+
return /* @__PURE__ */ jsx(
|
|
47
|
+
Dialog.Backdrop,
|
|
48
|
+
{
|
|
49
|
+
"data-slot": "sheet-overlay",
|
|
50
|
+
className: cn(
|
|
51
|
+
"fixed inset-0 z-50 bg-black/10 transition-opacity duration-150 data-ending-style:opacity-0 data-starting-style:opacity-0 supports-backdrop-filter:backdrop-blur-xs",
|
|
52
|
+
className
|
|
53
|
+
),
|
|
54
|
+
...props
|
|
55
|
+
}
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
function SheetContent({
|
|
59
|
+
className,
|
|
60
|
+
children,
|
|
61
|
+
side = "right",
|
|
62
|
+
showCloseButton = true,
|
|
63
|
+
...props
|
|
64
|
+
}) {
|
|
65
|
+
const { t } = useTranslation("ui", { useSuspense: false });
|
|
66
|
+
return /* @__PURE__ */ jsxs(SheetPortal, { children: [
|
|
67
|
+
/* @__PURE__ */ jsx(SheetOverlay, {}),
|
|
68
|
+
/* @__PURE__ */ jsxs(
|
|
69
|
+
Dialog.Popup,
|
|
70
|
+
{
|
|
71
|
+
"data-slot": "sheet-content",
|
|
72
|
+
"data-side": side,
|
|
73
|
+
className: cn(
|
|
74
|
+
"fixed z-50 flex flex-col gap-4 bg-popover bg-clip-padding text-sm text-popover-foreground shadow-lg transition duration-200 ease-in-out data-ending-style:opacity-0 data-starting-style:opacity-0 data-[side=bottom]:inset-x-0 data-[side=bottom]:bottom-0 data-[side=bottom]:h-auto data-[side=bottom]:border-t data-[side=bottom]:data-ending-style:translate-y-[2.5rem] data-[side=bottom]:data-starting-style:translate-y-[2.5rem] data-[side=left]:inset-y-0 data-[side=left]:left-0 data-[side=left]:h-full data-[side=left]:w-3/4 data-[side=left]:border-r data-[side=left]:data-ending-style:translate-x-[-2.5rem] data-[side=left]:data-starting-style:translate-x-[-2.5rem] data-[side=right]:inset-y-0 data-[side=right]:right-0 data-[side=right]:h-full data-[side=right]:w-3/4 data-[side=right]:border-l data-[side=right]:data-ending-style:translate-x-[2.5rem] data-[side=right]:data-starting-style:translate-x-[2.5rem] data-[side=top]:inset-x-0 data-[side=top]:top-0 data-[side=top]:h-auto data-[side=top]:border-b data-[side=top]:data-ending-style:translate-y-[-2.5rem] data-[side=top]:data-starting-style:translate-y-[-2.5rem] data-[side=left]:sm:max-w-sm data-[side=right]:sm:max-w-sm",
|
|
75
|
+
className
|
|
76
|
+
),
|
|
77
|
+
...props,
|
|
78
|
+
children: [
|
|
79
|
+
children,
|
|
80
|
+
showCloseButton && /* @__PURE__ */ jsxs(
|
|
81
|
+
Dialog.Close,
|
|
82
|
+
{
|
|
83
|
+
"data-slot": "sheet-close",
|
|
84
|
+
render: /* @__PURE__ */ jsx(
|
|
85
|
+
Button,
|
|
86
|
+
{
|
|
87
|
+
variant: "ghost",
|
|
88
|
+
className: "absolute top-4 right-4",
|
|
89
|
+
size: "icon-sm"
|
|
90
|
+
}
|
|
91
|
+
),
|
|
92
|
+
children: [
|
|
93
|
+
/* @__PURE__ */ jsx(
|
|
94
|
+
Cross,
|
|
95
|
+
{}
|
|
96
|
+
),
|
|
97
|
+
/* @__PURE__ */ jsx("span", { className: "sr-only", children: t("aria.close") })
|
|
98
|
+
]
|
|
99
|
+
}
|
|
100
|
+
)
|
|
101
|
+
]
|
|
102
|
+
}
|
|
103
|
+
)
|
|
104
|
+
] });
|
|
105
|
+
}
|
|
106
|
+
function SheetHeader({ className, ...props }) {
|
|
107
|
+
return /* @__PURE__ */ jsx(
|
|
108
|
+
"div",
|
|
109
|
+
{
|
|
110
|
+
"data-slot": "sheet-header",
|
|
111
|
+
className: cn("flex flex-col gap-1.5 p-4", className),
|
|
112
|
+
...props
|
|
113
|
+
}
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
function SheetFooter({ className, ...props }) {
|
|
117
|
+
return /* @__PURE__ */ jsx(
|
|
118
|
+
"div",
|
|
119
|
+
{
|
|
120
|
+
"data-slot": "sheet-footer",
|
|
121
|
+
className: cn("mt-auto flex flex-col gap-2 p-4", className),
|
|
122
|
+
...props
|
|
123
|
+
}
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
function SheetTitle({ className, ...props }) {
|
|
127
|
+
return /* @__PURE__ */ jsx(
|
|
128
|
+
Dialog.Title,
|
|
129
|
+
{
|
|
130
|
+
"data-slot": "sheet-title",
|
|
131
|
+
className: cn("font-heading font-medium text-foreground", className),
|
|
132
|
+
...props
|
|
133
|
+
}
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
function SheetDescription({
|
|
137
|
+
className,
|
|
138
|
+
...props
|
|
139
|
+
}) {
|
|
140
|
+
return /* @__PURE__ */ jsx(
|
|
141
|
+
Dialog.Description,
|
|
142
|
+
{
|
|
143
|
+
"data-slot": "sheet-description",
|
|
144
|
+
className: cn("text-sm text-muted-foreground", className),
|
|
145
|
+
...props
|
|
146
|
+
}
|
|
147
|
+
);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export { Sheet, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetTitle, SheetTrigger };
|
|
151
|
+
//# sourceMappingURL=chunk-SAY52ZKI.js.map
|
|
152
|
+
//# sourceMappingURL=chunk-SAY52ZKI.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/sheet.tsx"],"names":["SheetPrimitive"],"mappings":"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","file":"chunk-SAY52ZKI.js","sourcesContent":["import * as React from \"react\"\nimport { Dialog as SheetPrimitive } from \"@base-ui/react/dialog\"\nimport { useTranslation } from \"react-i18next\"\n\nimport { cn } from \"@hoardodile/ui/lib/utils\"\nimport { Button } from \"@hoardodile/ui/components/button\"\nimport { useMobileBackToClose } from \"@hoardodile/ui/hooks/useMobileBackToClose\"\nimport { Cross } from \"@hoardodile/ui/icons/marks\"\n\nfunction Sheet({\n open,\n defaultOpen,\n onOpenChange,\n ...props\n}: Omit<SheetPrimitive.Root.Props, \"onOpenChange\"> & {\n /**\n * `eventDetails` is absent for closes triggered by the mobile back\n * gesture (a synthetic close with no underlying Base UI event).\n */\n onOpenChange?: (\n open: boolean,\n eventDetails?: SheetPrimitive.Root.ChangeEventDetails,\n ) => void\n}) {\n const [uncontrolledOpen, setUncontrolledOpen] = React.useState(\n defaultOpen ?? false,\n )\n const isControlled = open !== undefined\n const currentOpen = isControlled ? open : uncontrolledOpen\n function handleOpenChange(\n next: boolean,\n eventDetails?: SheetPrimitive.Root.ChangeEventDetails,\n ) {\n if (!isControlled) setUncontrolledOpen(next)\n onOpenChange?.(next, eventDetails)\n }\n useMobileBackToClose(currentOpen, handleOpenChange)\n return (\n <SheetPrimitive.Root\n data-slot=\"sheet\"\n open={currentOpen}\n onOpenChange={handleOpenChange}\n {...props}\n />\n )\n}\n\nfunction SheetTrigger({ ...props }: SheetPrimitive.Trigger.Props) {\n return <SheetPrimitive.Trigger data-slot=\"sheet-trigger\" {...props} />\n}\n\nfunction SheetClose({ ...props }: SheetPrimitive.Close.Props) {\n return <SheetPrimitive.Close data-slot=\"sheet-close\" {...props} />\n}\n\nfunction SheetPortal({ ...props }: SheetPrimitive.Portal.Props) {\n return <SheetPrimitive.Portal data-slot=\"sheet-portal\" {...props} />\n}\n\nfunction SheetOverlay({ className, ...props }: SheetPrimitive.Backdrop.Props) {\n return (\n <SheetPrimitive.Backdrop\n data-slot=\"sheet-overlay\"\n className={cn(\n \"fixed inset-0 z-50 bg-black/10 transition-opacity duration-150 data-ending-style:opacity-0 data-starting-style:opacity-0 supports-backdrop-filter:backdrop-blur-xs\",\n className\n )}\n {...props}\n />\n )\n}\n\nfunction SheetContent({\n className,\n children,\n side = \"right\",\n showCloseButton = true,\n ...props\n}: SheetPrimitive.Popup.Props & {\n side?: \"top\" | \"right\" | \"bottom\" | \"left\"\n showCloseButton?: boolean\n}) {\n const { t } = useTranslation(\"ui\", { useSuspense: false })\n return (\n <SheetPortal>\n <SheetOverlay />\n <SheetPrimitive.Popup\n data-slot=\"sheet-content\"\n data-side={side}\n className={cn(\n \"fixed z-50 flex flex-col gap-4 bg-popover bg-clip-padding text-sm text-popover-foreground shadow-lg transition duration-200 ease-in-out data-ending-style:opacity-0 data-starting-style:opacity-0 data-[side=bottom]:inset-x-0 data-[side=bottom]:bottom-0 data-[side=bottom]:h-auto data-[side=bottom]:border-t data-[side=bottom]:data-ending-style:translate-y-[2.5rem] data-[side=bottom]:data-starting-style:translate-y-[2.5rem] data-[side=left]:inset-y-0 data-[side=left]:left-0 data-[side=left]:h-full data-[side=left]:w-3/4 data-[side=left]:border-r data-[side=left]:data-ending-style:translate-x-[-2.5rem] data-[side=left]:data-starting-style:translate-x-[-2.5rem] data-[side=right]:inset-y-0 data-[side=right]:right-0 data-[side=right]:h-full data-[side=right]:w-3/4 data-[side=right]:border-l data-[side=right]:data-ending-style:translate-x-[2.5rem] data-[side=right]:data-starting-style:translate-x-[2.5rem] data-[side=top]:inset-x-0 data-[side=top]:top-0 data-[side=top]:h-auto data-[side=top]:border-b data-[side=top]:data-ending-style:translate-y-[-2.5rem] data-[side=top]:data-starting-style:translate-y-[-2.5rem] data-[side=left]:sm:max-w-sm data-[side=right]:sm:max-w-sm\",\n className\n )}\n {...props}\n >\n {children}\n {showCloseButton && (\n <SheetPrimitive.Close\n data-slot=\"sheet-close\"\n render={\n <Button\n variant=\"ghost\"\n className=\"absolute top-4 right-4\"\n size=\"icon-sm\"\n />\n }\n >\n <Cross\n />\n <span className=\"sr-only\">{t(\"aria.close\")}</span>\n </SheetPrimitive.Close>\n )}\n </SheetPrimitive.Popup>\n </SheetPortal>\n )\n}\n\nfunction SheetHeader({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"sheet-header\"\n className={cn(\"flex flex-col gap-1.5 p-4\", className)}\n {...props}\n />\n )\n}\n\nfunction SheetFooter({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"sheet-footer\"\n className={cn(\"mt-auto flex flex-col gap-2 p-4\", className)}\n {...props}\n />\n )\n}\n\nfunction SheetTitle({ className, ...props }: SheetPrimitive.Title.Props) {\n return (\n <SheetPrimitive.Title\n data-slot=\"sheet-title\"\n className={cn(\"font-heading font-medium text-foreground\", className)}\n {...props}\n />\n )\n}\n\nfunction SheetDescription({\n className,\n ...props\n}: SheetPrimitive.Description.Props) {\n return (\n <SheetPrimitive.Description\n data-slot=\"sheet-description\"\n className={cn(\"text-sm text-muted-foreground\", className)}\n {...props}\n />\n )\n}\n\nexport {\n Sheet,\n SheetTrigger,\n SheetClose,\n SheetContent,\n SheetHeader,\n SheetFooter,\n SheetTitle,\n SheetDescription,\n}\n"]}
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import { useRef, useState, useCallback, useEffect } from 'react';
|
|
2
|
+
import ReactCrop from 'react-image-crop';
|
|
3
|
+
import 'react-image-crop/dist/ReactCrop.css';
|
|
4
|
+
import { jsx } from 'react/jsx-runtime';
|
|
5
|
+
|
|
6
|
+
// src/components/image-cropper.tsx
|
|
7
|
+
function ImageCropper(props) {
|
|
8
|
+
const {
|
|
9
|
+
src,
|
|
10
|
+
aspect,
|
|
11
|
+
mimeType = "image/png",
|
|
12
|
+
quality = 0.92,
|
|
13
|
+
displayMaxWidth,
|
|
14
|
+
displayMaxHeight = "70vh"
|
|
15
|
+
} = props;
|
|
16
|
+
const imgRef = useRef(null);
|
|
17
|
+
const [crop, setCrop] = useState(void 0);
|
|
18
|
+
const [completed, setCompleted] = useState(void 0);
|
|
19
|
+
const handleImageLoad = useCallback(
|
|
20
|
+
(e) => {
|
|
21
|
+
const { naturalWidth, naturalHeight } = e.currentTarget;
|
|
22
|
+
const initial = buildInitialCrop(naturalWidth, naturalHeight, aspect);
|
|
23
|
+
setCrop(initial);
|
|
24
|
+
setCompleted(initial);
|
|
25
|
+
},
|
|
26
|
+
[aspect]
|
|
27
|
+
);
|
|
28
|
+
const onCropReady = props.onCropReady;
|
|
29
|
+
const onPreviewChange = props.onPreviewChange;
|
|
30
|
+
useEffect(() => {
|
|
31
|
+
const img = imgRef.current;
|
|
32
|
+
if (img === null || completed === void 0 || completed.width === 0) {
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
onCropReady(() => renderCroppedImage(img, completed, mimeType, quality));
|
|
36
|
+
if (onPreviewChange !== void 0) {
|
|
37
|
+
onPreviewChange(renderPreviewDataUrl(img, completed));
|
|
38
|
+
}
|
|
39
|
+
}, [completed, mimeType, quality, onCropReady, onPreviewChange]);
|
|
40
|
+
return /* @__PURE__ */ jsx("div", { className: "react-crop-theme flex min-h-0 w-full max-w-full flex-col items-center gap-2 overflow-hidden", children: /* @__PURE__ */ jsx(
|
|
41
|
+
ReactCrop,
|
|
42
|
+
{
|
|
43
|
+
className: "max-w-full",
|
|
44
|
+
crop,
|
|
45
|
+
onChange: (_, percentCrop) => setCrop(percentCrop),
|
|
46
|
+
onComplete: (_, percentCrop) => setCompleted(percentCrop),
|
|
47
|
+
aspect,
|
|
48
|
+
keepSelection: true,
|
|
49
|
+
ruleOfThirds: true,
|
|
50
|
+
children: /* @__PURE__ */ jsx(
|
|
51
|
+
"img",
|
|
52
|
+
{
|
|
53
|
+
ref: imgRef,
|
|
54
|
+
src,
|
|
55
|
+
onLoad: handleImageLoad,
|
|
56
|
+
className: "h-auto max-w-full",
|
|
57
|
+
style: {
|
|
58
|
+
maxWidth: displayMaxWidth,
|
|
59
|
+
maxHeight: displayMaxHeight
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
)
|
|
63
|
+
}
|
|
64
|
+
) });
|
|
65
|
+
}
|
|
66
|
+
function buildInitialCrop(imgWidth, imgHeight, aspect) {
|
|
67
|
+
if (aspect === void 0) {
|
|
68
|
+
return { unit: "%", x: 0, y: 0, width: 100, height: 100 };
|
|
69
|
+
}
|
|
70
|
+
const imgAspect = imgWidth / imgHeight;
|
|
71
|
+
let widthPct;
|
|
72
|
+
let heightPct;
|
|
73
|
+
if (imgAspect >= aspect) {
|
|
74
|
+
heightPct = 100;
|
|
75
|
+
widthPct = 100 * aspect / imgAspect;
|
|
76
|
+
} else {
|
|
77
|
+
widthPct = 100;
|
|
78
|
+
heightPct = 100 * imgAspect / aspect;
|
|
79
|
+
}
|
|
80
|
+
return {
|
|
81
|
+
unit: "%",
|
|
82
|
+
x: (100 - widthPct) / 2,
|
|
83
|
+
y: (100 - heightPct) / 2,
|
|
84
|
+
width: widthPct,
|
|
85
|
+
height: heightPct
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
var CroppedImageRenderError = class extends Error {
|
|
89
|
+
constructor(message) {
|
|
90
|
+
super(message);
|
|
91
|
+
this.name = "CroppedImageRenderError";
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
async function renderCroppedImage(image, crop, mimeType, quality) {
|
|
95
|
+
const sourceRect = cropToNaturalRect(image, crop);
|
|
96
|
+
const canvas = document.createElement("canvas");
|
|
97
|
+
canvas.width = sourceRect.sw;
|
|
98
|
+
canvas.height = sourceRect.sh;
|
|
99
|
+
const ctx = canvas.getContext("2d");
|
|
100
|
+
if (ctx === null) {
|
|
101
|
+
throw new CroppedImageRenderError("2d context is unavailable");
|
|
102
|
+
}
|
|
103
|
+
ctx.imageSmoothingQuality = "high";
|
|
104
|
+
ctx.drawImage(
|
|
105
|
+
image,
|
|
106
|
+
sourceRect.sx,
|
|
107
|
+
sourceRect.sy,
|
|
108
|
+
sourceRect.sw,
|
|
109
|
+
sourceRect.sh,
|
|
110
|
+
0,
|
|
111
|
+
0,
|
|
112
|
+
sourceRect.sw,
|
|
113
|
+
sourceRect.sh
|
|
114
|
+
);
|
|
115
|
+
const blob = await new Promise((resolve) => {
|
|
116
|
+
canvas.toBlob(resolve, mimeType, quality);
|
|
117
|
+
});
|
|
118
|
+
if (blob === null) {
|
|
119
|
+
throw new CroppedImageRenderError("toBlob returned null");
|
|
120
|
+
}
|
|
121
|
+
return { blob, width: sourceRect.sw, height: sourceRect.sh, mimeType };
|
|
122
|
+
}
|
|
123
|
+
function renderPreviewDataUrl(image, crop) {
|
|
124
|
+
const sourceRect = cropToNaturalRect(image, crop);
|
|
125
|
+
const canvas = document.createElement("canvas");
|
|
126
|
+
canvas.width = sourceRect.sw;
|
|
127
|
+
canvas.height = sourceRect.sh;
|
|
128
|
+
const ctx = canvas.getContext("2d", { alpha: true });
|
|
129
|
+
if (ctx === null) return "";
|
|
130
|
+
ctx.imageSmoothingQuality = "high";
|
|
131
|
+
ctx.drawImage(
|
|
132
|
+
image,
|
|
133
|
+
sourceRect.sx,
|
|
134
|
+
sourceRect.sy,
|
|
135
|
+
sourceRect.sw,
|
|
136
|
+
sourceRect.sh,
|
|
137
|
+
0,
|
|
138
|
+
0,
|
|
139
|
+
sourceRect.sw,
|
|
140
|
+
sourceRect.sh
|
|
141
|
+
);
|
|
142
|
+
return canvas.toDataURL("image/png");
|
|
143
|
+
}
|
|
144
|
+
function cropToNaturalRect(image, crop) {
|
|
145
|
+
const naturalWidth = image.naturalWidth;
|
|
146
|
+
const naturalHeight = image.naturalHeight;
|
|
147
|
+
if (crop.unit === "px") {
|
|
148
|
+
return {
|
|
149
|
+
sx: crop.x,
|
|
150
|
+
sy: crop.y,
|
|
151
|
+
sw: Math.max(1, Math.round(crop.width)),
|
|
152
|
+
sh: Math.max(1, Math.round(crop.height))
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
const sx = crop.x / 100 * naturalWidth;
|
|
156
|
+
const sy = crop.y / 100 * naturalHeight;
|
|
157
|
+
const swRaw = crop.width / 100 * naturalWidth;
|
|
158
|
+
const shRaw = crop.height / 100 * naturalHeight;
|
|
159
|
+
const sw = Math.max(1, Math.round(swRaw));
|
|
160
|
+
const sh = Math.max(1, Math.round(shRaw));
|
|
161
|
+
return { sx, sy, sw, sh };
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
export { ImageCropper };
|
|
165
|
+
//# sourceMappingURL=chunk-SKDPGSRL.js.map
|
|
166
|
+
//# sourceMappingURL=chunk-SKDPGSRL.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/image-cropper.tsx"],"names":[],"mappings":"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","file":"chunk-SKDPGSRL.js","sourcesContent":["import { useCallback, useEffect, useRef, useState } from \"react\"\nimport ReactCrop, { type Crop } from \"react-image-crop\"\nimport \"react-image-crop/dist/ReactCrop.css\"\n\n/**\n * Output of {@link ImageCropper} once the user releases the selection\n * box. Contains the cropped pixel data ready to upload (PNG by default)\n * plus its dimensions.\n */\nexport type CroppedImage = {\n\treadonly blob: Blob\n\treadonly width: number\n\treadonly height: number\n\treadonly mimeType: string\n}\n\nexport type ImageCropperProps = {\n\t/** The source image as a data URL or object URL. */\n\treadonly src: string\n\t/**\n\t * Optional fixed aspect ratio (width / height). When omitted, the\n\t * user can crop freely.\n\t */\n\treadonly aspect?: number\n\t/**\n\t * Output mime type. Defaults to `image/png`. PNG preserves quality;\n\t * JPEG produces smaller files for photographic content.\n\t */\n\treadonly mimeType?: \"image/png\" | \"image/jpeg\" | \"image/webp\"\n\t/** JPEG/WebP quality (0..1). Ignored for PNG. */\n\treadonly quality?: number\n\t/**\n\t * Called whenever the cropped pixel rectangle changes and is non-empty.\n\t * The callback receives a function that, when invoked, produces the\n\t * final {@link CroppedImage}. The render is deferred to avoid running\n\t * the canvas pipeline on every drag tick.\n\t */\n\treadonly onCropReady: (render: () => Promise<CroppedImage>) => void\n\t/**\n\t * Called synchronously with a PNG `data:` URL whenever the crop\n\t * selection changes. PNG preserves alpha for the live preview (JPEG\n\t * would flatten transparency to black). Unlike blob URLs, `data:`\n\t * URLs never appear in the browser network tab and setting them as\n\t * image `src` cannot cause an infinite re-render loop.\n\t */\n\treadonly onPreviewChange?: (dataUrl: string) => void\n\t/**\n\t * Max CSS width for the source image in the crop stage. When omitted the\n\t * image relies on `max-w-full` to fill the parent width.\n\t */\n\treadonly displayMaxWidth?: number | string\n\t/**\n\t * Max CSS height for the source image in the crop stage (e.g. `80vh` for\n\t * large canvas captures). Defaults to `70vh`.\n\t */\n\treadonly displayMaxHeight?: number | string\n}\n\n/**\n * Free-aspect (or fixed-aspect when {@link ImageCropperProps.aspect} is\n * provided) image cropper. Wraps `react-image-crop` and exposes a\n * deferred `render()` callback so callers can rasterise the selection on\n * demand (typically when the user clicks \"save\").\n */\nexport function ImageCropper(props: ImageCropperProps) {\n\tconst {\n\t\tsrc,\n\t\taspect,\n\t\tmimeType = \"image/png\",\n\t\tquality = 0.92,\n\t\tdisplayMaxWidth,\n\t\tdisplayMaxHeight = \"70vh\",\n\t} = props\n\n\tconst imgRef = useRef<HTMLImageElement | null>(null)\n\tconst [crop, setCrop] = useState<Crop | undefined>(undefined)\n\tconst [completed, setCompleted] = useState<Crop | undefined>(undefined)\n\n\tconst handleImageLoad = useCallback(\n\t\t(e: React.SyntheticEvent<HTMLImageElement>) => {\n\t\t\tconst { naturalWidth, naturalHeight } = e.currentTarget\n\t\t\tconst initial = buildInitialCrop(naturalWidth, naturalHeight, aspect)\n\t\t\tsetCrop(initial)\n\t\t\tsetCompleted(initial)\n\t\t},\n\t\t[aspect],\n\t)\n\n\tconst onCropReady = props.onCropReady\n\tconst onPreviewChange = props.onPreviewChange\n\n\tuseEffect(() => {\n\t\tconst img = imgRef.current\n\t\tif (img === null || completed === undefined || completed.width === 0) {\n\t\t\treturn\n\t\t}\n\t\tonCropReady(() => renderCroppedImage(img, completed, mimeType, quality))\n\t\tif (onPreviewChange !== undefined) {\n\t\t\tonPreviewChange(renderPreviewDataUrl(img, completed))\n\t\t}\n\t}, [completed, mimeType, quality, onCropReady, onPreviewChange])\n\n\treturn (\n\t\t<div className=\"react-crop-theme flex min-h-0 w-full max-w-full flex-col items-center gap-2 overflow-hidden\">\n\t\t\t<ReactCrop\n\t\t\t\tclassName=\"max-w-full\"\n\t\t\t\tcrop={crop}\n\t\t\t\tonChange={(_, percentCrop) => setCrop(percentCrop)}\n\t\t\t\tonComplete={(_, percentCrop) => setCompleted(percentCrop)}\n\t\t\t\taspect={aspect}\n\t\t\t\tkeepSelection\n\t\t\t\truleOfThirds\n\t\t\t>\n\t\t\t\t{/* biome-ignore lint/a11y/useAltText: cropper preview lacks user-supplied alt */}\n\t\t\t\t<img\n\t\t\t\t\tref={imgRef}\n\t\t\t\t\tsrc={src}\n\t\t\t\t\tonLoad={handleImageLoad}\n\t\t\t\t\tclassName=\"h-auto max-w-full\"\n\t\t\t\t\tstyle={{\n\t\t\t\t\t\tmaxWidth: displayMaxWidth,\n\t\t\t\t\t\tmaxHeight: displayMaxHeight,\n\t\t\t\t\t}}\n\t\t\t\t/>\n\t\t\t</ReactCrop>\n\t\t</div>\n\t)\n}\n\nfunction buildInitialCrop(\n\timgWidth: number,\n\timgHeight: number,\n\taspect: number | undefined,\n): Crop {\n\tif (aspect === undefined) {\n\t\treturn { unit: \"%\" as const, x: 0, y: 0, width: 100, height: 100 }\n\t}\n\n\tconst imgAspect = imgWidth / imgHeight\n\tlet widthPct: number\n\tlet heightPct: number\n\n\tif (imgAspect >= aspect) {\n\t\t// Image is wider (or equal) than the target aspect: fit to height,\n\t\t// crop the sides.\n\t\theightPct = 100\n\t\twidthPct = (100 * aspect) / imgAspect\n\t} else {\n\t\t// Image is taller than the target aspect: fit to width, crop top/bottom.\n\t\twidthPct = 100\n\t\theightPct = (100 * imgAspect) / aspect\n\t}\n\n\treturn {\n\t\tunit: \"%\" as const,\n\t\tx: (100 - widthPct) / 2,\n\t\ty: (100 - heightPct) / 2,\n\t\twidth: widthPct,\n\t\theight: heightPct,\n\t}\n}\n\nclass CroppedImageRenderError extends Error {\n\tconstructor(message: string) {\n\t\tsuper(message)\n\t\tthis.name = \"CroppedImageRenderError\"\n\t}\n}\n\n/**\n * @throws CroppedImageRenderError when the canvas context is unavailable\n * or the browser refuses to encode the selected blob.\n */\nasync function renderCroppedImage(\n\timage: HTMLImageElement,\n\tcrop: Crop,\n\tmimeType: string,\n\tquality: number,\n): Promise<CroppedImage> {\n\tconst sourceRect = cropToNaturalRect(image, crop)\n\n\tconst canvas = document.createElement(\"canvas\")\n\tcanvas.width = sourceRect.sw\n\tcanvas.height = sourceRect.sh\n\tconst ctx = canvas.getContext(\"2d\")\n\tif (ctx === null) {\n\t\tthrow new CroppedImageRenderError(\"2d context is unavailable\")\n\t}\n\tctx.imageSmoothingQuality = \"high\"\n\tctx.drawImage(\n\t\timage,\n\t\tsourceRect.sx,\n\t\tsourceRect.sy,\n\t\tsourceRect.sw,\n\t\tsourceRect.sh,\n\t\t0,\n\t\t0,\n\t\tsourceRect.sw,\n\t\tsourceRect.sh,\n\t)\n\n\tconst blob = await new Promise<Blob | null>((resolve) => {\n\t\tcanvas.toBlob(resolve, mimeType, quality)\n\t})\n\tif (blob === null) {\n\t\tthrow new CroppedImageRenderError(\"toBlob returned null\")\n\t}\n\treturn { blob, width: sourceRect.sw, height: sourceRect.sh, mimeType }\n}\n\n/**\n * Render the current crop to a PNG `data:` URL synchronously.\n *\n * Unlike {@link renderCroppedImage}, this function does NOT create a\n * `blob:` URL and therefore does not appear in the browser network tab.\n * Preview uses PNG (not JPEG) so transparent regions stay transparent.\n */\nfunction renderPreviewDataUrl(image: HTMLImageElement, crop: Crop): string {\n\tconst sourceRect = cropToNaturalRect(image, crop)\n\n\tconst canvas = document.createElement(\"canvas\")\n\tcanvas.width = sourceRect.sw\n\tcanvas.height = sourceRect.sh\n\tconst ctx = canvas.getContext(\"2d\", { alpha: true })\n\tif (ctx === null) return \"\"\n\tctx.imageSmoothingQuality = \"high\"\n\tctx.drawImage(\n\t\timage,\n\t\tsourceRect.sx,\n\t\tsourceRect.sy,\n\t\tsourceRect.sw,\n\t\tsourceRect.sh,\n\t\t0,\n\t\t0,\n\t\tsourceRect.sw,\n\t\tsourceRect.sh,\n\t)\n\treturn canvas.toDataURL(\"image/png\")\n}\n\nfunction cropToNaturalRect(\n\timage: HTMLImageElement,\n\tcrop: Crop,\n): { sx: number; sy: number; sw: number; sh: number } {\n\tconst naturalWidth = image.naturalWidth\n\tconst naturalHeight = image.naturalHeight\n\n\tif (crop.unit === \"px\") {\n\t\treturn {\n\t\t\tsx: crop.x,\n\t\t\tsy: crop.y,\n\t\t\tsw: Math.max(1, Math.round(crop.width)),\n\t\t\tsh: Math.max(1, Math.round(crop.height)),\n\t\t}\n\t}\n\n\tconst sx = (crop.x / 100) * naturalWidth\n\tconst sy = (crop.y / 100) * naturalHeight\n\tconst swRaw = (crop.width / 100) * naturalWidth\n\tconst shRaw = (crop.height / 100) * naturalHeight\n\tconst sw = Math.max(1, Math.round(swRaw))\n\tconst sh = Math.max(1, Math.round(shRaw))\n\n\treturn { sx, sy, sw, sh }\n}\n"]}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { cn } from './chunk-M5OJN54O.js';
|
|
2
|
+
import { cva } from 'class-variance-authority';
|
|
3
|
+
import 'react';
|
|
4
|
+
import { jsx } from 'react/jsx-runtime';
|
|
5
|
+
|
|
6
|
+
var textareaVariants = cva(
|
|
7
|
+
"flex field-sizing-content min-h-16 w-full resize-none rounded-lg bg-muted px-3 py-2.5 text-ui text-foreground outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:ring-[3px] aria-invalid:ring-destructive/20",
|
|
8
|
+
{
|
|
9
|
+
variants: {
|
|
10
|
+
size: {
|
|
11
|
+
sm: "px-2.5 py-2 text-xs",
|
|
12
|
+
md: "px-3 py-2.5",
|
|
13
|
+
lg: "px-3.5 py-3"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
defaultVariants: {
|
|
17
|
+
size: "md"
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
);
|
|
21
|
+
function Textarea({
|
|
22
|
+
className,
|
|
23
|
+
size,
|
|
24
|
+
...props
|
|
25
|
+
}) {
|
|
26
|
+
return /* @__PURE__ */ jsx(
|
|
27
|
+
"textarea",
|
|
28
|
+
{
|
|
29
|
+
"data-slot": "textarea",
|
|
30
|
+
className: cn(textareaVariants({ size }), className),
|
|
31
|
+
...props
|
|
32
|
+
}
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export { Textarea, textareaVariants };
|
|
37
|
+
//# sourceMappingURL=chunk-T56ARCUK.js.map
|
|
38
|
+
//# sourceMappingURL=chunk-T56ARCUK.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/textarea.tsx"],"names":[],"mappings":";;;;;AAUA,IAAM,gBAAA,GAAmB,GAAA;AAAA,EACvB,uQAAA;AAAA,EACA;AAAA,IACE,QAAA,EAAU;AAAA,MACR,IAAA,EAAM;AAAA,QACJ,EAAA,EAAI,qBAAA;AAAA,QACJ,EAAA,EAAI,aAAA;AAAA,QACJ,EAAA,EAAI;AAAA;AACN,KACF;AAAA,IACA,eAAA,EAAiB;AAAA,MACf,IAAA,EAAM;AAAA;AACR;AAEJ;AAEA,SAAS,QAAA,CAAS;AAAA,EAChB,SAAA;AAAA,EACA,IAAA;AAAA,EACA,GAAG;AACL,CAAA,EAA6E;AAC3E,EAAA,uBACE,GAAA;AAAA,IAAC,UAAA;AAAA,IAAA;AAAA,MACC,WAAA,EAAU,UAAA;AAAA,MACV,WAAW,EAAA,CAAG,gBAAA,CAAiB,EAAE,IAAA,EAAM,GAAG,SAAS,CAAA;AAAA,MAClD,GAAG;AAAA;AAAA,GACN;AAEJ","file":"chunk-T56ARCUK.js","sourcesContent":["import { cva, type VariantProps } from \"class-variance-authority\"\nimport * as React from \"react\"\n\nimport { cn } from \"@hoardodile/ui/lib/utils\"\n\n/**\n * Muted-fill textarea (the forms Textarea anatomy): fill background,\n * `rounded-lg`, no border, same anatomy as the inputs. The `size` tiers\n * only shift padding and text; the height stays row-driven.\n */\nconst textareaVariants = cva(\n \"flex field-sizing-content min-h-16 w-full resize-none rounded-lg bg-muted px-3 py-2.5 text-ui text-foreground outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:ring-[3px] aria-invalid:ring-destructive/20\",\n {\n variants: {\n size: {\n sm: \"px-2.5 py-2 text-xs\",\n md: \"px-3 py-2.5\",\n lg: \"px-3.5 py-3\",\n },\n },\n defaultVariants: {\n size: \"md\",\n },\n }\n)\n\nfunction Textarea({\n className,\n size,\n ...props\n}: React.ComponentProps<\"textarea\"> & VariantProps<typeof textareaVariants>) {\n return (\n <textarea\n data-slot=\"textarea\"\n className={cn(textareaVariants({ size }), className)}\n {...props}\n />\n )\n}\n\nexport { Textarea, textareaVariants }\n"]}
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
import { isBelowMd } from './chunk-QGMDWGJT.js';
|
|
2
|
+
import { useRef, useEffect } from 'react';
|
|
3
|
+
|
|
4
|
+
var HISTORY_KEY = "__appMobileOverlay";
|
|
5
|
+
var overlayStack = [];
|
|
6
|
+
var popListenerInstalled = false;
|
|
7
|
+
var historyPatched = false;
|
|
8
|
+
var nextOverlayId = 0;
|
|
9
|
+
var suppressPopCount = 0;
|
|
10
|
+
var seenSyntheticIds = /* @__PURE__ */ new Set();
|
|
11
|
+
var navigationResolver;
|
|
12
|
+
function setNavigationResolver(resolver) {
|
|
13
|
+
navigationResolver = resolver;
|
|
14
|
+
}
|
|
15
|
+
function patchHistory() {
|
|
16
|
+
if (historyPatched) return;
|
|
17
|
+
if (typeof window === "undefined") return;
|
|
18
|
+
historyPatched = true;
|
|
19
|
+
const origPush = window.history.pushState.bind(window.history);
|
|
20
|
+
window.history.pushState = (data, unused, url) => {
|
|
21
|
+
const state = data;
|
|
22
|
+
if (state !== null && typeof state === "object" && HISTORY_KEY in state) {
|
|
23
|
+
origPush(data, unused, url);
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
if (overlayStack.length > 0) {
|
|
27
|
+
closeAllOverlays();
|
|
28
|
+
}
|
|
29
|
+
origPush(data, unused, url);
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
function closeAllOverlays() {
|
|
33
|
+
while (overlayStack.length > 0) {
|
|
34
|
+
const entry = overlayStack.pop();
|
|
35
|
+
if (entry?.close !== void 0) {
|
|
36
|
+
const fn = entry.close;
|
|
37
|
+
entry.close = void 0;
|
|
38
|
+
fn();
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
function ensurePopListener() {
|
|
43
|
+
if (popListenerInstalled) return;
|
|
44
|
+
if (typeof window === "undefined") return;
|
|
45
|
+
popListenerInstalled = true;
|
|
46
|
+
window.addEventListener(
|
|
47
|
+
"popstate",
|
|
48
|
+
(e) => {
|
|
49
|
+
if (suppressPopCount > 0) return;
|
|
50
|
+
const state = e.state;
|
|
51
|
+
if (state !== null && typeof state === "object" && HISTORY_KEY in state) {
|
|
52
|
+
const id = state[HISTORY_KEY];
|
|
53
|
+
if (typeof id === "number" && overlayStack.some((entry) => entry.id === id)) {
|
|
54
|
+
closeTopOverlay();
|
|
55
|
+
e.stopImmediatePropagation();
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
e.stopImmediatePropagation();
|
|
59
|
+
if (typeof id === "number" && seenSyntheticIds.has(id)) {
|
|
60
|
+
window.history.back();
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
true
|
|
65
|
+
// capture phase
|
|
66
|
+
);
|
|
67
|
+
window.addEventListener("popstate", handlePop);
|
|
68
|
+
patchHistory();
|
|
69
|
+
}
|
|
70
|
+
function closeTopOverlay() {
|
|
71
|
+
const top = overlayStack.pop();
|
|
72
|
+
if (top === void 0) return;
|
|
73
|
+
if (top.close === void 0) return;
|
|
74
|
+
const closeFn = top.close;
|
|
75
|
+
top.close = void 0;
|
|
76
|
+
closeFn();
|
|
77
|
+
}
|
|
78
|
+
function handlePop() {
|
|
79
|
+
if (suppressPopCount > 0) {
|
|
80
|
+
suppressPopCount--;
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
closeTopOverlay();
|
|
84
|
+
}
|
|
85
|
+
function removeFromStack(id) {
|
|
86
|
+
const idx = overlayStack.findIndex((e) => e.id === id);
|
|
87
|
+
if (idx === -1) return { removed: false, parkedBelow: 0 };
|
|
88
|
+
const wasTop = idx === overlayStack.length - 1;
|
|
89
|
+
if (!wasTop) {
|
|
90
|
+
const entry = overlayStack[idx];
|
|
91
|
+
if (entry !== void 0) entry.close = void 0;
|
|
92
|
+
return { removed: true, parkedBelow: 0 };
|
|
93
|
+
}
|
|
94
|
+
overlayStack.pop();
|
|
95
|
+
const remaining = overlayStack.splice(0, overlayStack.length);
|
|
96
|
+
const active = remaining.filter((e) => e.close !== void 0);
|
|
97
|
+
const parkedBelow = remaining.length - active.length;
|
|
98
|
+
overlayStack.push(...active);
|
|
99
|
+
return { removed: true, parkedBelow };
|
|
100
|
+
}
|
|
101
|
+
function scheduleStateRestore(id, baseState, parkedBelow) {
|
|
102
|
+
function check() {
|
|
103
|
+
const currentState = window.history.state;
|
|
104
|
+
if (currentState === null || typeof currentState !== "object" || !(HISTORY_KEY in currentState) || currentState[HISTORY_KEY] !== id) {
|
|
105
|
+
sweepParkedEntries();
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
const totalBacks = 1 + parkedBelow;
|
|
109
|
+
let done = false;
|
|
110
|
+
function restore() {
|
|
111
|
+
if (done) return;
|
|
112
|
+
done = true;
|
|
113
|
+
window.removeEventListener("popstate", onPop);
|
|
114
|
+
window.history.replaceState(baseState, "");
|
|
115
|
+
const sweptIds = sweepParkedEntries();
|
|
116
|
+
cleanupSeenIds(sweptIds);
|
|
117
|
+
}
|
|
118
|
+
function onPop() {
|
|
119
|
+
if (suppressPopCount > 0) {
|
|
120
|
+
suppressPopCount--;
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
restore();
|
|
124
|
+
}
|
|
125
|
+
window.addEventListener("popstate", onPop);
|
|
126
|
+
suppressPopCount += totalBacks;
|
|
127
|
+
for (let i = 0; i < totalBacks; i++) {
|
|
128
|
+
window.history.back();
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
let disposed = false;
|
|
132
|
+
let rafId;
|
|
133
|
+
let unsub;
|
|
134
|
+
function run() {
|
|
135
|
+
if (disposed) return;
|
|
136
|
+
disposed = true;
|
|
137
|
+
unsub?.();
|
|
138
|
+
if (rafId !== void 0) {
|
|
139
|
+
cancelAnimationFrame(rafId);
|
|
140
|
+
}
|
|
141
|
+
check();
|
|
142
|
+
}
|
|
143
|
+
if (navigationResolver !== void 0) {
|
|
144
|
+
unsub = navigationResolver(run);
|
|
145
|
+
}
|
|
146
|
+
rafId = requestAnimationFrame(run);
|
|
147
|
+
}
|
|
148
|
+
function sweepParkedEntries() {
|
|
149
|
+
const sweptIds = [];
|
|
150
|
+
for (let i = overlayStack.length - 1; i >= 0; i--) {
|
|
151
|
+
const entry = overlayStack[i];
|
|
152
|
+
if (entry !== void 0 && entry.close === void 0) {
|
|
153
|
+
sweptIds.push(entry.id);
|
|
154
|
+
overlayStack.splice(i, 1);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
return sweptIds;
|
|
158
|
+
}
|
|
159
|
+
function cleanupSeenIds(sweptIds) {
|
|
160
|
+
const activeIds = new Set(overlayStack.map((e) => e.id));
|
|
161
|
+
for (const id of sweptIds) {
|
|
162
|
+
if (!activeIds.has(id)) {
|
|
163
|
+
seenSyntheticIds.delete(id);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
function useMobileBackToClose(open, onOpenChange) {
|
|
168
|
+
const idRef = useRef(void 0);
|
|
169
|
+
const onChangeRef = useRef(onOpenChange);
|
|
170
|
+
onChangeRef.current = onOpenChange;
|
|
171
|
+
useEffect(() => {
|
|
172
|
+
if (typeof window === "undefined") return;
|
|
173
|
+
if (onChangeRef.current === void 0) return;
|
|
174
|
+
if (!isBelowMd()) return;
|
|
175
|
+
ensurePopListener();
|
|
176
|
+
if (open === true && idRef.current === void 0) {
|
|
177
|
+
const id = ++nextOverlayId;
|
|
178
|
+
idRef.current = id;
|
|
179
|
+
seenSyntheticIds.add(id);
|
|
180
|
+
const baseState = window.history.state ?? null;
|
|
181
|
+
window.history.pushState(
|
|
182
|
+
{
|
|
183
|
+
...baseState ?? {},
|
|
184
|
+
[HISTORY_KEY]: id
|
|
185
|
+
},
|
|
186
|
+
""
|
|
187
|
+
);
|
|
188
|
+
overlayStack.push({
|
|
189
|
+
id,
|
|
190
|
+
baseState,
|
|
191
|
+
close() {
|
|
192
|
+
idRef.current = void 0;
|
|
193
|
+
onChangeRef.current?.(false);
|
|
194
|
+
}
|
|
195
|
+
});
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
if (open !== true && idRef.current !== void 0) {
|
|
199
|
+
const id = idRef.current;
|
|
200
|
+
idRef.current = void 0;
|
|
201
|
+
const entryIdx = overlayStack.findIndex((e) => e.id === id);
|
|
202
|
+
const entry = entryIdx !== -1 ? overlayStack[entryIdx] : void 0;
|
|
203
|
+
const { removed, parkedBelow } = removeFromStack(id);
|
|
204
|
+
if (removed && entry !== void 0) {
|
|
205
|
+
scheduleStateRestore(id, entry.baseState, parkedBelow);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}, [open]);
|
|
209
|
+
useEffect(() => {
|
|
210
|
+
return () => {
|
|
211
|
+
if (idRef.current !== void 0) {
|
|
212
|
+
const id = idRef.current;
|
|
213
|
+
idRef.current = void 0;
|
|
214
|
+
const entryIdx = overlayStack.findIndex((e) => e.id === id);
|
|
215
|
+
const entry = entryIdx !== -1 ? overlayStack[entryIdx] : void 0;
|
|
216
|
+
const { removed, parkedBelow } = removeFromStack(id);
|
|
217
|
+
if (removed && entry !== void 0) {
|
|
218
|
+
scheduleStateRestore(id, entry.baseState, parkedBelow);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
};
|
|
222
|
+
}, []);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
export { setNavigationResolver, useMobileBackToClose };
|
|
226
|
+
//# sourceMappingURL=chunk-TDDHQZ62.js.map
|
|
227
|
+
//# sourceMappingURL=chunk-TDDHQZ62.js.map
|