@inshorts/glint-ui 0.5.3 → 0.6.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/LICENSE +13 -0
- package/README.md +3 -9
- package/dist/alert.cjs +96 -0
- package/dist/alert.cjs.map +1 -0
- package/dist/alert.d.cts +29 -0
- package/dist/alert.d.ts +29 -0
- package/dist/alert.js +10 -0
- package/dist/alert.js.map +1 -0
- package/dist/autocomplete.cjs +578 -0
- package/dist/autocomplete.cjs.map +1 -0
- package/dist/autocomplete.d.cts +56 -0
- package/dist/autocomplete.d.ts +56 -0
- package/dist/autocomplete.js +11 -0
- package/dist/autocomplete.js.map +1 -0
- package/dist/badge.cjs +89 -0
- package/dist/badge.cjs.map +1 -0
- package/dist/badge.d.cts +25 -0
- package/dist/badge.d.ts +25 -0
- package/dist/badge.js +10 -0
- package/dist/badge.js.map +1 -0
- package/dist/breadcrumb.cjs +88 -0
- package/dist/breadcrumb.cjs.map +1 -0
- package/dist/breadcrumb.d.cts +14 -0
- package/dist/breadcrumb.d.ts +14 -0
- package/dist/breadcrumb.js +8 -0
- package/dist/breadcrumb.js.map +1 -0
- package/dist/button.cjs +143 -0
- package/dist/button.cjs.map +1 -0
- package/dist/button.d.cts +34 -0
- package/dist/button.d.ts +34 -0
- package/dist/button.js +9 -0
- package/dist/button.js.map +1 -0
- package/dist/card.cjs +111 -0
- package/dist/card.cjs.map +1 -0
- package/dist/card.d.cts +15 -0
- package/dist/card.d.ts +15 -0
- package/dist/card.js +18 -0
- package/dist/card.js.map +1 -0
- package/dist/checkbox.cjs +66 -0
- package/dist/checkbox.cjs.map +1 -0
- package/{src/checkbox.tsx → dist/checkbox.d.cts} +5 -22
- package/dist/checkbox.d.ts +25 -0
- package/dist/checkbox.js +10 -0
- package/dist/checkbox.js.map +1 -0
- package/dist/chunk-43ZUFY63.js +85 -0
- package/dist/chunk-43ZUFY63.js.map +1 -0
- package/dist/chunk-67ACNS42.js +29 -0
- package/dist/chunk-67ACNS42.js.map +1 -0
- package/dist/chunk-6URKHMCF.js +76 -0
- package/dist/chunk-6URKHMCF.js.map +1 -0
- package/dist/chunk-ASU6CUTZ.js +85 -0
- package/dist/chunk-ASU6CUTZ.js.map +1 -0
- package/dist/chunk-EWSBFU54.js +53 -0
- package/dist/chunk-EWSBFU54.js.map +1 -0
- package/dist/chunk-G4ZQUSAJ.js +38 -0
- package/dist/chunk-G4ZQUSAJ.js.map +1 -0
- package/dist/chunk-GMCUXEFI.js +67 -0
- package/dist/chunk-GMCUXEFI.js.map +1 -0
- package/dist/chunk-HBBOR43A.js +85 -0
- package/dist/chunk-HBBOR43A.js.map +1 -0
- package/dist/chunk-HCA5ODCD.js +11 -0
- package/dist/chunk-HCA5ODCD.js.map +1 -0
- package/dist/chunk-HYRJYUPI.js +26 -0
- package/dist/chunk-HYRJYUPI.js.map +1 -0
- package/dist/chunk-IRCTGJQB.js +26 -0
- package/dist/chunk-IRCTGJQB.js.map +1 -0
- package/dist/chunk-JDE5XYPT.js +30 -0
- package/dist/chunk-JDE5XYPT.js.map +1 -0
- package/dist/chunk-JSJEEKR3.js +59 -0
- package/dist/chunk-JSJEEKR3.js.map +1 -0
- package/dist/chunk-KR3BCFP7.js +85 -0
- package/dist/chunk-KR3BCFP7.js.map +1 -0
- package/dist/chunk-MQT5RAQV.js +202 -0
- package/dist/chunk-MQT5RAQV.js.map +1 -0
- package/dist/chunk-MUPW2TFJ.js +102 -0
- package/dist/chunk-MUPW2TFJ.js.map +1 -0
- package/dist/chunk-PM2VE7DJ.js +100 -0
- package/dist/chunk-PM2VE7DJ.js.map +1 -0
- package/dist/chunk-PPYR3O3L.js +44 -0
- package/dist/chunk-PPYR3O3L.js.map +1 -0
- package/dist/chunk-PUTC52SI.js +98 -0
- package/dist/chunk-PUTC52SI.js.map +1 -0
- package/dist/chunk-PVK4WQJZ.js +513 -0
- package/dist/chunk-PVK4WQJZ.js.map +1 -0
- package/dist/chunk-PWAKTEXF.js +18 -0
- package/dist/chunk-PWAKTEXF.js.map +1 -0
- package/dist/chunk-QTFWORA7.js +26 -0
- package/dist/chunk-QTFWORA7.js.map +1 -0
- package/dist/chunk-RUPL2H6V.js +49 -0
- package/dist/chunk-RUPL2H6V.js.map +1 -0
- package/dist/chunk-S7PVZAPL.js +28 -0
- package/dist/chunk-S7PVZAPL.js.map +1 -0
- package/dist/chunk-SQDV2AKJ.js +49 -0
- package/dist/chunk-SQDV2AKJ.js.map +1 -0
- package/dist/chunk-T2KXVH3O.js +297 -0
- package/dist/chunk-T2KXVH3O.js.map +1 -0
- package/dist/chunk-T4WCCHJQ.js +29 -0
- package/dist/chunk-T4WCCHJQ.js.map +1 -0
- package/dist/chunk-UC3ZIQPE.js +93 -0
- package/dist/chunk-UC3ZIQPE.js.map +1 -0
- package/dist/chunk-UE7JY7VR.js +28 -0
- package/dist/chunk-UE7JY7VR.js.map +1 -0
- package/dist/chunk-WVNRFO5F.js +70 -0
- package/dist/chunk-WVNRFO5F.js.map +1 -0
- package/dist/chunk-Y5WCT6XN.js +93 -0
- package/dist/chunk-Y5WCT6XN.js.map +1 -0
- package/dist/chunk-YDICVETX.js +151 -0
- package/dist/chunk-YDICVETX.js.map +1 -0
- package/dist/chunk-YSU4VWRD.js +90 -0
- package/dist/chunk-YSU4VWRD.js.map +1 -0
- package/dist/chunk-ZFSQVMLJ.js +126 -0
- package/dist/chunk-ZFSQVMLJ.js.map +1 -0
- package/dist/chunk-ZHATD73Y.js +46 -0
- package/dist/chunk-ZHATD73Y.js.map +1 -0
- package/dist/chunk-ZLAA4DGG.js +111 -0
- package/dist/chunk-ZLAA4DGG.js.map +1 -0
- package/dist/chunk-ZP252OJZ.js +56 -0
- package/dist/chunk-ZP252OJZ.js.map +1 -0
- package/dist/clipboard.cjs +125 -0
- package/dist/clipboard.cjs.map +1 -0
- package/dist/clipboard.d.cts +52 -0
- package/dist/clipboard.d.ts +52 -0
- package/dist/clipboard.js +23 -0
- package/dist/clipboard.js.map +1 -0
- package/dist/collapsible.cjs +94 -0
- package/dist/collapsible.cjs.map +1 -0
- package/dist/collapsible.d.cts +21 -0
- package/dist/collapsible.d.ts +21 -0
- package/dist/collapsible.js +9 -0
- package/dist/collapsible.js.map +1 -0
- package/dist/confirm-dialog.cjs +369 -0
- package/dist/confirm-dialog.cjs.map +1 -0
- package/dist/confirm-dialog.d.cts +16 -0
- package/dist/confirm-dialog.d.ts +16 -0
- package/dist/confirm-dialog.js +13 -0
- package/dist/confirm-dialog.js.map +1 -0
- package/dist/copy-button.cjs +102 -0
- package/dist/copy-button.cjs.map +1 -0
- package/dist/copy-button.d.cts +18 -0
- package/dist/copy-button.d.ts +18 -0
- package/dist/copy-button.js +10 -0
- package/dist/copy-button.js.map +1 -0
- package/dist/data-table.cjs +140 -0
- package/dist/data-table.cjs.map +1 -0
- package/dist/data-table.d.cts +81 -0
- package/dist/data-table.d.ts +81 -0
- package/dist/data-table.js +8 -0
- package/dist/data-table.js.map +1 -0
- package/dist/date-range-picker/dateRange.cjs +138 -0
- package/dist/date-range-picker/dateRange.cjs.map +1 -0
- package/dist/date-range-picker/dateRange.d.cts +57 -0
- package/dist/date-range-picker/dateRange.d.ts +57 -0
- package/dist/date-range-picker/dateRange.js +31 -0
- package/dist/date-range-picker/dateRange.js.map +1 -0
- package/dist/date-range-picker/index.cjs +375 -0
- package/dist/date-range-picker/index.cjs.map +1 -0
- package/dist/date-range-picker/index.d.cts +18 -0
- package/dist/date-range-picker/index.d.ts +18 -0
- package/dist/date-range-picker/index.js +10 -0
- package/dist/date-range-picker/index.js.map +1 -0
- package/dist/dialogLayer.cjs +121 -0
- package/dist/dialogLayer.cjs.map +1 -0
- package/dist/dialogLayer.d.cts +11 -0
- package/dist/dialogLayer.d.ts +11 -0
- package/dist/dialogLayer.js +8 -0
- package/dist/dialogLayer.js.map +1 -0
- package/dist/feature-access-gate.cjs +160 -0
- package/dist/feature-access-gate.cjs.map +1 -0
- package/dist/feature-access-gate.d.cts +14 -0
- package/dist/feature-access-gate.d.ts +14 -0
- package/dist/feature-access-gate.js +11 -0
- package/dist/feature-access-gate.js.map +1 -0
- package/dist/field.cjs +58 -0
- package/dist/field.cjs.map +1 -0
- package/dist/field.d.cts +23 -0
- package/dist/field.d.ts +23 -0
- package/dist/field.js +8 -0
- package/dist/field.js.map +1 -0
- package/dist/image-uploader.cjs +293 -0
- package/dist/image-uploader.cjs.map +1 -0
- package/dist/image-uploader.d.cts +34 -0
- package/dist/image-uploader.d.ts +34 -0
- package/dist/image-uploader.js +12 -0
- package/dist/image-uploader.js.map +1 -0
- package/dist/index.cjs +3012 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +41 -0
- package/dist/index.d.ts +41 -0
- package/dist/index.js +232 -0
- package/dist/index.js.map +1 -0
- package/dist/input.cjs +58 -0
- package/dist/input.cjs.map +1 -0
- package/dist/input.d.cts +7 -0
- package/dist/input.d.ts +7 -0
- package/dist/input.js +10 -0
- package/dist/input.js.map +1 -0
- package/dist/label.cjs +67 -0
- package/dist/label.cjs.map +1 -0
- package/dist/label.d.cts +7 -0
- package/dist/label.d.ts +7 -0
- package/dist/label.js +9 -0
- package/dist/label.js.map +1 -0
- package/dist/modal.cjs +212 -0
- package/dist/modal.cjs.map +1 -0
- package/dist/modal.d.cts +39 -0
- package/dist/modal.d.ts +39 -0
- package/dist/modal.js +12 -0
- package/dist/modal.js.map +1 -0
- package/dist/page-header.cjs +78 -0
- package/dist/page-header.cjs.map +1 -0
- package/dist/page-header.d.cts +35 -0
- package/dist/page-header.d.ts +35 -0
- package/dist/page-header.js +8 -0
- package/dist/page-header.js.map +1 -0
- package/dist/pagination.cjs +237 -0
- package/dist/pagination.cjs.map +1 -0
- package/dist/pagination.d.cts +15 -0
- package/dist/pagination.d.ts +15 -0
- package/dist/pagination.js +11 -0
- package/dist/pagination.js.map +1 -0
- package/dist/panel-brand-mark.cjs +59 -0
- package/dist/panel-brand-mark.cjs.map +1 -0
- package/dist/panel-brand-mark.d.cts +13 -0
- package/dist/panel-brand-mark.d.ts +13 -0
- package/dist/panel-brand-mark.js +8 -0
- package/dist/panel-brand-mark.js.map +1 -0
- package/dist/panel-sign-in.cjs +256 -0
- package/dist/panel-sign-in.cjs.map +1 -0
- package/dist/panel-sign-in.d.cts +31 -0
- package/dist/panel-sign-in.d.ts +31 -0
- package/dist/panel-sign-in.js +14 -0
- package/dist/panel-sign-in.js.map +1 -0
- package/dist/segmented-control.cjs +126 -0
- package/dist/segmented-control.cjs.map +1 -0
- package/dist/segmented-control.d.cts +60 -0
- package/dist/segmented-control.d.ts +60 -0
- package/dist/segmented-control.js +9 -0
- package/dist/segmented-control.js.map +1 -0
- package/dist/select.cjs +648 -0
- package/dist/select.cjs.map +1 -0
- package/dist/select.d.cts +30 -0
- package/dist/select.d.ts +30 -0
- package/dist/select.js +12 -0
- package/dist/select.js.map +1 -0
- package/dist/separator.cjs +70 -0
- package/dist/separator.cjs.map +1 -0
- package/dist/separator.d.cts +13 -0
- package/dist/separator.d.ts +13 -0
- package/dist/separator.js +9 -0
- package/dist/separator.js.map +1 -0
- package/dist/side-drawer.cjs +321 -0
- package/dist/side-drawer.cjs.map +1 -0
- package/dist/side-drawer.d.cts +21 -0
- package/dist/side-drawer.d.ts +21 -0
- package/dist/side-drawer.js +13 -0
- package/dist/side-drawer.js.map +1 -0
- package/dist/spinner.cjs +57 -0
- package/dist/spinner.cjs.map +1 -0
- package/{src/spinner.tsx → dist/spinner.d.cts} +11 -22
- package/dist/spinner.d.ts +27 -0
- package/dist/spinner.js +8 -0
- package/dist/spinner.js.map +1 -0
- package/dist/spreadsheet-grid.cjs +238 -0
- package/dist/spreadsheet-grid.cjs.map +1 -0
- package/dist/spreadsheet-grid.d.cts +48 -0
- package/dist/spreadsheet-grid.d.ts +48 -0
- package/dist/spreadsheet-grid.js +8 -0
- package/dist/spreadsheet-grid.js.map +1 -0
- package/dist/state-panel.cjs +86 -0
- package/dist/state-panel.cjs.map +1 -0
- package/dist/state-panel.d.cts +23 -0
- package/dist/state-panel.d.ts +23 -0
- package/dist/state-panel.js +9 -0
- package/dist/state-panel.js.map +1 -0
- package/dist/switch.cjs +87 -0
- package/dist/switch.cjs.map +1 -0
- package/dist/switch.d.cts +13 -0
- package/dist/switch.d.ts +13 -0
- package/dist/switch.js +9 -0
- package/dist/switch.js.map +1 -0
- package/dist/table.cjs +139 -0
- package/dist/table.cjs.map +1 -0
- package/dist/table.d.cts +16 -0
- package/dist/table.d.ts +16 -0
- package/dist/table.js +22 -0
- package/dist/table.js.map +1 -0
- package/dist/tabs.cjs +168 -0
- package/dist/tabs.cjs.map +1 -0
- package/dist/tabs.d.cts +59 -0
- package/dist/tabs.d.ts +59 -0
- package/dist/tabs.js +11 -0
- package/dist/tabs.js.map +1 -0
- package/dist/textarea.cjs +66 -0
- package/dist/textarea.cjs.map +1 -0
- package/dist/textarea.d.cts +18 -0
- package/dist/textarea.d.ts +18 -0
- package/dist/textarea.js +10 -0
- package/dist/textarea.js.map +1 -0
- package/dist/utils.cjs +35 -0
- package/dist/utils.cjs.map +1 -0
- package/dist/utils.d.cts +5 -0
- package/dist/utils.d.ts +5 -0
- package/dist/utils.js +7 -0
- package/dist/utils.js.map +1 -0
- package/package.json +395 -19
- package/src/alert.tsx +0 -74
- package/src/autocomplete.tsx +0 -666
- package/src/badge.tsx +0 -60
- package/src/breadcrumb.tsx +0 -71
- package/src/button.tsx +0 -113
- package/src/card.tsx +0 -79
- package/src/clipboard.ts +0 -144
- package/src/collapsible.tsx +0 -63
- package/src/confirm-dialog.tsx +0 -84
- package/src/copy-button.tsx +0 -58
- package/src/data-table.tsx +0 -190
- package/src/date-range-picker/dateRange.ts +0 -142
- package/src/date-range-picker/index.tsx +0 -366
- package/src/dialogLayer.ts +0 -142
- package/src/feature-access-gate.tsx +0 -38
- package/src/field.tsx +0 -56
- package/src/image-uploader.tsx +0 -181
- package/src/index.ts +0 -37
- package/src/input.tsx +0 -16
- package/src/label.tsx +0 -23
- package/src/modal.tsx +0 -117
- package/src/page-header.tsx +0 -75
- package/src/pagination.tsx +0 -116
- package/src/panel-brand-mark.tsx +0 -32
- package/src/panel-sign-in.tsx +0 -134
- package/src/segmented-control.tsx +0 -155
- package/src/select.tsx +0 -105
- package/src/separator.tsx +0 -32
- package/src/side-drawer.tsx +0 -87
- package/src/spreadsheet-grid.tsx +0 -264
- package/src/state-panel.tsx +0 -46
- package/src/switch.tsx +0 -46
- package/src/table.tsx +0 -110
- package/src/tabs.tsx +0 -201
- package/src/textarea.tsx +0 -32
- package/src/utils.ts +0 -6
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import {
|
|
2
|
+
cn
|
|
3
|
+
} from "./chunk-HCA5ODCD.js";
|
|
4
|
+
|
|
5
|
+
// src/separator.tsx
|
|
6
|
+
import * as React from "react";
|
|
7
|
+
import { jsx } from "react/jsx-runtime";
|
|
8
|
+
var Separator = React.forwardRef(
|
|
9
|
+
({ className, orientation = "horizontal", decorative = true, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
10
|
+
"div",
|
|
11
|
+
{
|
|
12
|
+
ref,
|
|
13
|
+
role: decorative ? "none" : "separator",
|
|
14
|
+
"aria-orientation": orientation,
|
|
15
|
+
className: cn(
|
|
16
|
+
"bg-border shrink-0",
|
|
17
|
+
orientation === "horizontal" ? "h-px w-full" : "h-full w-px",
|
|
18
|
+
className
|
|
19
|
+
),
|
|
20
|
+
...props
|
|
21
|
+
}
|
|
22
|
+
)
|
|
23
|
+
);
|
|
24
|
+
Separator.displayName = "Separator";
|
|
25
|
+
|
|
26
|
+
export {
|
|
27
|
+
Separator
|
|
28
|
+
};
|
|
29
|
+
//# sourceMappingURL=chunk-T4WCCHJQ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/separator.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\n\nimport { cn } from \"./utils\";\n\nexport type SeparatorProps = React.HTMLAttributes<HTMLDivElement> & {\n orientation?: \"horizontal\" | \"vertical\";\n decorative?: boolean;\n};\n\n/** Thin rule between sections. */\nexport const Separator = React.forwardRef<HTMLDivElement, SeparatorProps>(\n (\n { className, orientation = \"horizontal\", decorative = true, ...props },\n ref,\n ) => (\n <div\n ref={ref}\n role={decorative ? \"none\" : \"separator\"}\n aria-orientation={orientation}\n className={cn(\n \"bg-border shrink-0\",\n orientation === \"horizontal\" ? \"h-px w-full\" : \"h-full w-px\",\n className,\n )}\n {...props}\n />\n ),\n);\n\nSeparator.displayName = \"Separator\";\n"],"mappings":";;;;;AAEA,YAAY,WAAW;AAenB;AALG,IAAM,YAAkB;AAAA,EAC7B,CACE,EAAE,WAAW,cAAc,cAAc,aAAa,MAAM,GAAG,MAAM,GACrE,QAEA;AAAA,IAAC;AAAA;AAAA,MACC;AAAA,MACA,MAAM,aAAa,SAAS;AAAA,MAC5B,oBAAkB;AAAA,MAClB,WAAW;AAAA,QACT;AAAA,QACA,gBAAgB,eAAe,gBAAgB;AAAA,QAC/C;AAAA,MACF;AAAA,MACC,GAAG;AAAA;AAAA,EACN;AAEJ;AAEA,UAAU,cAAc;","names":[]}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import {
|
|
2
|
+
cn
|
|
3
|
+
} from "./chunk-HCA5ODCD.js";
|
|
4
|
+
|
|
5
|
+
// src/table.tsx
|
|
6
|
+
import * as React from "react";
|
|
7
|
+
import { jsx } from "react/jsx-runtime";
|
|
8
|
+
var Table = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx("div", { className: "relative w-full overflow-auto", children: /* @__PURE__ */ jsx(
|
|
9
|
+
"table",
|
|
10
|
+
{
|
|
11
|
+
ref,
|
|
12
|
+
className: cn("w-full caption-bottom text-sm", className),
|
|
13
|
+
...props
|
|
14
|
+
}
|
|
15
|
+
) }));
|
|
16
|
+
Table.displayName = "Table";
|
|
17
|
+
var TableHeader = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx("thead", { ref, className: cn("[&_tr]:border-b", className), ...props }));
|
|
18
|
+
TableHeader.displayName = "TableHeader";
|
|
19
|
+
var TableBody = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
20
|
+
"tbody",
|
|
21
|
+
{
|
|
22
|
+
ref,
|
|
23
|
+
className: cn("[&_tr:last-child]:border-0", className),
|
|
24
|
+
...props
|
|
25
|
+
}
|
|
26
|
+
));
|
|
27
|
+
TableBody.displayName = "TableBody";
|
|
28
|
+
var TableFooter = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
29
|
+
"tfoot",
|
|
30
|
+
{
|
|
31
|
+
ref,
|
|
32
|
+
className: cn(
|
|
33
|
+
"bg-muted/50 border-t font-medium [&>tr]:last:border-b-0",
|
|
34
|
+
className
|
|
35
|
+
),
|
|
36
|
+
...props
|
|
37
|
+
}
|
|
38
|
+
));
|
|
39
|
+
TableFooter.displayName = "TableFooter";
|
|
40
|
+
var TableRow = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
41
|
+
"tr",
|
|
42
|
+
{
|
|
43
|
+
ref,
|
|
44
|
+
className: cn(
|
|
45
|
+
"hover:bg-muted/50 data-[state=selected]:bg-muted border-b transition-colors",
|
|
46
|
+
className
|
|
47
|
+
),
|
|
48
|
+
...props
|
|
49
|
+
}
|
|
50
|
+
));
|
|
51
|
+
TableRow.displayName = "TableRow";
|
|
52
|
+
var TableHead = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
53
|
+
"th",
|
|
54
|
+
{
|
|
55
|
+
ref,
|
|
56
|
+
className: cn(
|
|
57
|
+
"text-muted-foreground h-12 px-4 text-left align-middle font-medium [&:has([role=checkbox])]:pr-0",
|
|
58
|
+
className
|
|
59
|
+
),
|
|
60
|
+
...props
|
|
61
|
+
}
|
|
62
|
+
));
|
|
63
|
+
TableHead.displayName = "TableHead";
|
|
64
|
+
var TableCell = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
65
|
+
"td",
|
|
66
|
+
{
|
|
67
|
+
ref,
|
|
68
|
+
className: cn("p-4 align-middle [&:has([role=checkbox])]:pr-0", className),
|
|
69
|
+
...props
|
|
70
|
+
}
|
|
71
|
+
));
|
|
72
|
+
TableCell.displayName = "TableCell";
|
|
73
|
+
var TableCaption = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
74
|
+
"caption",
|
|
75
|
+
{
|
|
76
|
+
ref,
|
|
77
|
+
className: cn("text-muted-foreground mt-4 text-sm", className),
|
|
78
|
+
...props
|
|
79
|
+
}
|
|
80
|
+
));
|
|
81
|
+
TableCaption.displayName = "TableCaption";
|
|
82
|
+
|
|
83
|
+
export {
|
|
84
|
+
Table,
|
|
85
|
+
TableHeader,
|
|
86
|
+
TableBody,
|
|
87
|
+
TableFooter,
|
|
88
|
+
TableRow,
|
|
89
|
+
TableHead,
|
|
90
|
+
TableCell,
|
|
91
|
+
TableCaption
|
|
92
|
+
};
|
|
93
|
+
//# sourceMappingURL=chunk-UC3ZIQPE.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/table.tsx"],"sourcesContent":["import * as React from \"react\";\n\nimport { cn } from \"./utils\";\n\n/**\n * Low-level table primitives. Prefer `DataTable` for list panels; use these\n * when the row markup is custom (nested actions, expandable rows, etc.).\n */\nexport const Table = React.forwardRef<\n HTMLTableElement,\n React.HTMLAttributes<HTMLTableElement>\n>(({ className, ...props }, ref) => (\n <div className=\"relative w-full overflow-auto\">\n <table\n ref={ref}\n className={cn(\"w-full caption-bottom text-sm\", className)}\n {...props}\n />\n </div>\n));\nTable.displayName = \"Table\";\n\nexport const TableHeader = React.forwardRef<\n HTMLTableSectionElement,\n React.HTMLAttributes<HTMLTableSectionElement>\n>(({ className, ...props }, ref) => (\n <thead ref={ref} className={cn(\"[&_tr]:border-b\", className)} {...props} />\n));\nTableHeader.displayName = \"TableHeader\";\n\nexport const TableBody = React.forwardRef<\n HTMLTableSectionElement,\n React.HTMLAttributes<HTMLTableSectionElement>\n>(({ className, ...props }, ref) => (\n <tbody\n ref={ref}\n className={cn(\"[&_tr:last-child]:border-0\", className)}\n {...props}\n />\n));\nTableBody.displayName = \"TableBody\";\n\nexport const TableFooter = React.forwardRef<\n HTMLTableSectionElement,\n React.HTMLAttributes<HTMLTableSectionElement>\n>(({ className, ...props }, ref) => (\n <tfoot\n ref={ref}\n className={cn(\n \"bg-muted/50 border-t font-medium [&>tr]:last:border-b-0\",\n className,\n )}\n {...props}\n />\n));\nTableFooter.displayName = \"TableFooter\";\n\nexport const TableRow = React.forwardRef<\n HTMLTableRowElement,\n React.HTMLAttributes<HTMLTableRowElement>\n>(({ className, ...props }, ref) => (\n <tr\n ref={ref}\n className={cn(\n \"hover:bg-muted/50 data-[state=selected]:bg-muted border-b transition-colors\",\n className,\n )}\n {...props}\n />\n));\nTableRow.displayName = \"TableRow\";\n\nexport const TableHead = React.forwardRef<\n HTMLTableCellElement,\n React.ThHTMLAttributes<HTMLTableCellElement>\n>(({ className, ...props }, ref) => (\n <th\n ref={ref}\n className={cn(\n \"text-muted-foreground h-12 px-4 text-left align-middle font-medium [&:has([role=checkbox])]:pr-0\",\n className,\n )}\n {...props}\n />\n));\nTableHead.displayName = \"TableHead\";\n\nexport const TableCell = React.forwardRef<\n HTMLTableCellElement,\n React.TdHTMLAttributes<HTMLTableCellElement>\n>(({ className, ...props }, ref) => (\n <td\n ref={ref}\n className={cn(\"p-4 align-middle [&:has([role=checkbox])]:pr-0\", className)}\n {...props}\n />\n));\nTableCell.displayName = \"TableCell\";\n\nexport const TableCaption = React.forwardRef<\n HTMLTableCaptionElement,\n React.HTMLAttributes<HTMLTableCaptionElement>\n>(({ className, ...props }, ref) => (\n <caption\n ref={ref}\n className={cn(\"text-muted-foreground mt-4 text-sm\", className)}\n {...props}\n />\n));\nTableCaption.displayName = \"TableCaption\";\n"],"mappings":";;;;;AAAA,YAAY,WAAW;AAanB;AALG,IAAM,QAAc,iBAGzB,CAAC,EAAE,WAAW,GAAG,MAAM,GAAG,QAC1B,oBAAC,SAAI,WAAU,iCACb;AAAA,EAAC;AAAA;AAAA,IACC;AAAA,IACA,WAAW,GAAG,iCAAiC,SAAS;AAAA,IACvD,GAAG;AAAA;AACN,GACF,CACD;AACD,MAAM,cAAc;AAEb,IAAM,cAAoB,iBAG/B,CAAC,EAAE,WAAW,GAAG,MAAM,GAAG,QAC1B,oBAAC,WAAM,KAAU,WAAW,GAAG,mBAAmB,SAAS,GAAI,GAAG,OAAO,CAC1E;AACD,YAAY,cAAc;AAEnB,IAAM,YAAkB,iBAG7B,CAAC,EAAE,WAAW,GAAG,MAAM,GAAG,QAC1B;AAAA,EAAC;AAAA;AAAA,IACC;AAAA,IACA,WAAW,GAAG,8BAA8B,SAAS;AAAA,IACpD,GAAG;AAAA;AACN,CACD;AACD,UAAU,cAAc;AAEjB,IAAM,cAAoB,iBAG/B,CAAC,EAAE,WAAW,GAAG,MAAM,GAAG,QAC1B;AAAA,EAAC;AAAA;AAAA,IACC;AAAA,IACA,WAAW;AAAA,MACT;AAAA,MACA;AAAA,IACF;AAAA,IACC,GAAG;AAAA;AACN,CACD;AACD,YAAY,cAAc;AAEnB,IAAM,WAAiB,iBAG5B,CAAC,EAAE,WAAW,GAAG,MAAM,GAAG,QAC1B;AAAA,EAAC;AAAA;AAAA,IACC;AAAA,IACA,WAAW;AAAA,MACT;AAAA,MACA;AAAA,IACF;AAAA,IACC,GAAG;AAAA;AACN,CACD;AACD,SAAS,cAAc;AAEhB,IAAM,YAAkB,iBAG7B,CAAC,EAAE,WAAW,GAAG,MAAM,GAAG,QAC1B;AAAA,EAAC;AAAA;AAAA,IACC;AAAA,IACA,WAAW;AAAA,MACT;AAAA,MACA;AAAA,IACF;AAAA,IACC,GAAG;AAAA;AACN,CACD;AACD,UAAU,cAAc;AAEjB,IAAM,YAAkB,iBAG7B,CAAC,EAAE,WAAW,GAAG,MAAM,GAAG,QAC1B;AAAA,EAAC;AAAA;AAAA,IACC;AAAA,IACA,WAAW,GAAG,kDAAkD,SAAS;AAAA,IACxE,GAAG;AAAA;AACN,CACD;AACD,UAAU,cAAc;AAEjB,IAAM,eAAqB,iBAGhC,CAAC,EAAE,WAAW,GAAG,MAAM,GAAG,QAC1B;AAAA,EAAC;AAAA;AAAA,IACC;AAAA,IACA,WAAW,GAAG,sCAAsC,SAAS;AAAA,IAC5D,GAAG;AAAA;AACN,CACD;AACD,aAAa,cAAc;","names":[]}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import {
|
|
2
|
+
cn
|
|
3
|
+
} from "./chunk-HCA5ODCD.js";
|
|
4
|
+
|
|
5
|
+
// src/spinner.tsx
|
|
6
|
+
import { Loader2 } from "lucide-react";
|
|
7
|
+
import { jsx } from "react/jsx-runtime";
|
|
8
|
+
var SIZES = {
|
|
9
|
+
sm: "size-3.5",
|
|
10
|
+
md: "size-4",
|
|
11
|
+
lg: "size-5",
|
|
12
|
+
xl: "size-10 border-2"
|
|
13
|
+
};
|
|
14
|
+
function Spinner({ size = "md", className, ...props }) {
|
|
15
|
+
return /* @__PURE__ */ jsx(
|
|
16
|
+
Loader2,
|
|
17
|
+
{
|
|
18
|
+
"aria-hidden": true,
|
|
19
|
+
className: cn("animate-spin", SIZES[size], className),
|
|
20
|
+
...props
|
|
21
|
+
}
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export {
|
|
26
|
+
Spinner
|
|
27
|
+
};
|
|
28
|
+
//# sourceMappingURL=chunk-UE7JY7VR.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/spinner.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { Loader2 } from \"lucide-react\";\n\nimport { cn } from \"./utils\";\n\n/**\n * The one spinner.\n *\n * There were five spellings of `animate-spin` across twelve files — `h-4 w-4`,\n * `h-5 w-5`, `size-4`, bare, and a hand-drawn ring in `ProtectedRoute` — so the\n * same \"working\" signal came out a different size depending on which panel you\n * were on.\n *\n * It is `aria-hidden` on purpose. A spinner is decoration; what a screen reader\n * needs is the surrounding control's `aria-busy` or the panel's status text,\n * and announcing \"loading\" twice is worse than once. `Button` and `StatePanel`\n * both supply that.\n */\nconst SIZES = {\n sm: \"size-3.5\",\n md: \"size-4\",\n lg: \"size-5\",\n xl: \"size-10 border-2\",\n} as const;\n\nexport interface SpinnerProps extends React.SVGProps<SVGSVGElement> {\n size?: keyof typeof SIZES;\n}\n\nexport function Spinner({ size = \"md\", className, ...props }: SpinnerProps) {\n return (\n <Loader2\n aria-hidden\n className={cn(\"animate-spin\", SIZES[size], className)}\n {...props}\n />\n );\n}\n"],"mappings":";;;;;AACA,SAAS,eAAe;AA8BpB;AAbJ,IAAM,QAAQ;AAAA,EACZ,IAAI;AAAA,EACJ,IAAI;AAAA,EACJ,IAAI;AAAA,EACJ,IAAI;AACN;AAMO,SAAS,QAAQ,EAAE,OAAO,MAAM,WAAW,GAAG,MAAM,GAAiB;AAC1E,SACE;AAAA,IAAC;AAAA;AAAA,MACC,eAAW;AAAA,MACX,WAAW,GAAG,gBAAgB,MAAM,IAAI,GAAG,SAAS;AAAA,MACnD,GAAG;AAAA;AAAA,EACN;AAEJ;","names":[]}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Modal
|
|
3
|
+
} from "./chunk-YSU4VWRD.js";
|
|
4
|
+
import {
|
|
5
|
+
Button
|
|
6
|
+
} from "./chunk-KR3BCFP7.js";
|
|
7
|
+
|
|
8
|
+
// src/confirm-dialog.tsx
|
|
9
|
+
import * as React from "react";
|
|
10
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
11
|
+
function ConfirmDialog({
|
|
12
|
+
isOpen,
|
|
13
|
+
title,
|
|
14
|
+
description,
|
|
15
|
+
confirmLabel = "Delete",
|
|
16
|
+
cancelLabel = "Cancel",
|
|
17
|
+
isConfirming,
|
|
18
|
+
error,
|
|
19
|
+
onConfirm,
|
|
20
|
+
onClose
|
|
21
|
+
}) {
|
|
22
|
+
const titleId = React.useId();
|
|
23
|
+
const descriptionId = React.useId();
|
|
24
|
+
const errorId = React.useId();
|
|
25
|
+
const describedBy = [
|
|
26
|
+
description ? descriptionId : null,
|
|
27
|
+
error ? errorId : null
|
|
28
|
+
].filter(Boolean).join(" ");
|
|
29
|
+
return /* @__PURE__ */ jsxs(
|
|
30
|
+
Modal,
|
|
31
|
+
{
|
|
32
|
+
isOpen,
|
|
33
|
+
role: "alertdialog",
|
|
34
|
+
labelledBy: titleId,
|
|
35
|
+
describedBy: describedBy || void 0,
|
|
36
|
+
closeLabel: "Close dialog",
|
|
37
|
+
onClose,
|
|
38
|
+
children: [
|
|
39
|
+
/* @__PURE__ */ jsx("h3", { id: titleId, className: "text-lg font-semibold", children: title }),
|
|
40
|
+
description ? /* @__PURE__ */ jsx("div", { id: descriptionId, className: "text-muted-foreground mt-2 text-sm", children: description }) : null,
|
|
41
|
+
error ? /* @__PURE__ */ jsx(
|
|
42
|
+
"div",
|
|
43
|
+
{
|
|
44
|
+
id: errorId,
|
|
45
|
+
role: "alert",
|
|
46
|
+
className: "border-destructive/30 bg-destructive/5 text-destructive mt-4 rounded-lg border px-3 py-2 text-sm",
|
|
47
|
+
children: error
|
|
48
|
+
}
|
|
49
|
+
) : null,
|
|
50
|
+
/* @__PURE__ */ jsxs("div", { className: "mt-6 flex justify-end gap-2", children: [
|
|
51
|
+
/* @__PURE__ */ jsx(Button, { variant: "outline", onClick: onClose, disabled: isConfirming, children: cancelLabel }),
|
|
52
|
+
/* @__PURE__ */ jsx(
|
|
53
|
+
Button,
|
|
54
|
+
{
|
|
55
|
+
variant: "destructive",
|
|
56
|
+
onClick: onConfirm,
|
|
57
|
+
loading: isConfirming,
|
|
58
|
+
children: confirmLabel
|
|
59
|
+
}
|
|
60
|
+
)
|
|
61
|
+
] })
|
|
62
|
+
]
|
|
63
|
+
}
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export {
|
|
68
|
+
ConfirmDialog
|
|
69
|
+
};
|
|
70
|
+
//# sourceMappingURL=chunk-WVNRFO5F.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/confirm-dialog.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\n\nimport { Button } from \"./button\";\nimport { Modal } from \"./modal\";\n\ninterface ConfirmDialogProps {\n isOpen: boolean;\n title: string;\n description?: React.ReactNode;\n confirmLabel?: string;\n cancelLabel?: string;\n isConfirming?: boolean;\n error?: string | null;\n onConfirm: () => void;\n onClose: () => void;\n}\n\nexport function ConfirmDialog({\n isOpen,\n title,\n description,\n confirmLabel = \"Delete\",\n cancelLabel = \"Cancel\",\n isConfirming,\n error,\n onConfirm,\n onClose,\n}: ConfirmDialogProps) {\n const titleId = React.useId();\n const descriptionId = React.useId();\n const errorId = React.useId();\n\n const describedBy = [\n description ? descriptionId : null,\n error ? errorId : null,\n ]\n .filter(Boolean)\n .join(\" \");\n\n return (\n <Modal\n isOpen={isOpen}\n role=\"alertdialog\"\n labelledBy={titleId}\n describedBy={describedBy || undefined}\n closeLabel=\"Close dialog\"\n onClose={onClose}\n >\n <h3 id={titleId} className=\"text-lg font-semibold\">\n {title}\n </h3>\n {description ? (\n <div id={descriptionId} className=\"text-muted-foreground mt-2 text-sm\">\n {description}\n </div>\n ) : null}\n\n {error ? (\n <div\n id={errorId}\n role=\"alert\"\n className=\"border-destructive/30 bg-destructive/5 text-destructive mt-4 rounded-lg border px-3 py-2 text-sm\"\n >\n {error}\n </div>\n ) : null}\n\n <div className=\"mt-6 flex justify-end gap-2\">\n <Button variant=\"outline\" onClick={onClose} disabled={isConfirming}>\n {cancelLabel}\n </Button>\n <Button\n variant=\"destructive\"\n onClick={onConfirm}\n loading={isConfirming}\n >\n {confirmLabel}\n </Button>\n </div>\n </Modal>\n );\n}\n"],"mappings":";;;;;;;;AAEA,YAAY,WAAW;AAgDjB,cAmBA,YAnBA;AA/BC,SAAS,cAAc;AAAA,EAC5B;AAAA,EACA;AAAA,EACA;AAAA,EACA,eAAe;AAAA,EACf,cAAc;AAAA,EACd;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAAuB;AACrB,QAAM,UAAgB,YAAM;AAC5B,QAAM,gBAAsB,YAAM;AAClC,QAAM,UAAgB,YAAM;AAE5B,QAAM,cAAc;AAAA,IAClB,cAAc,gBAAgB;AAAA,IAC9B,QAAQ,UAAU;AAAA,EACpB,EACG,OAAO,OAAO,EACd,KAAK,GAAG;AAEX,SACE;AAAA,IAAC;AAAA;AAAA,MACC;AAAA,MACA,MAAK;AAAA,MACL,YAAY;AAAA,MACZ,aAAa,eAAe;AAAA,MAC5B,YAAW;AAAA,MACX;AAAA,MAEA;AAAA,4BAAC,QAAG,IAAI,SAAS,WAAU,yBACxB,iBACH;AAAA,QACC,cACC,oBAAC,SAAI,IAAI,eAAe,WAAU,sCAC/B,uBACH,IACE;AAAA,QAEH,QACC;AAAA,UAAC;AAAA;AAAA,YACC,IAAI;AAAA,YACJ,MAAK;AAAA,YACL,WAAU;AAAA,YAET;AAAA;AAAA,QACH,IACE;AAAA,QAEJ,qBAAC,SAAI,WAAU,+BACb;AAAA,8BAAC,UAAO,SAAQ,WAAU,SAAS,SAAS,UAAU,cACnD,uBACH;AAAA,UACA;AAAA,YAAC;AAAA;AAAA,cACC,SAAQ;AAAA,cACR,SAAS;AAAA,cACT,SAAS;AAAA,cAER;AAAA;AAAA,UACH;AAAA,WACF;AAAA;AAAA;AAAA,EACF;AAEJ;","names":[]}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
// src/clipboard.ts
|
|
2
|
+
var TSV_DELIMITER = " ";
|
|
3
|
+
var CSV_DELIMITER = ",";
|
|
4
|
+
function normalizeRange(anchor, focus) {
|
|
5
|
+
if (!anchor || !focus) return null;
|
|
6
|
+
return {
|
|
7
|
+
r1: Math.min(anchor.r, focus.r),
|
|
8
|
+
r2: Math.max(anchor.r, focus.r),
|
|
9
|
+
c1: Math.min(anchor.c, focus.c),
|
|
10
|
+
c2: Math.max(anchor.c, focus.c)
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
function escapeCell(raw, delimiter) {
|
|
14
|
+
const value = raw == null ? "" : String(raw);
|
|
15
|
+
if (value.includes(delimiter) || value.includes("\n") || value.includes("\r") || value.includes('"')) {
|
|
16
|
+
return `"${value.replace(/"/g, '""')}"`;
|
|
17
|
+
}
|
|
18
|
+
return value;
|
|
19
|
+
}
|
|
20
|
+
function buildRangeText(range, columns, rows, delimiter, includeHeaders) {
|
|
21
|
+
if (!range) return "";
|
|
22
|
+
const lines = [];
|
|
23
|
+
if (includeHeaders) {
|
|
24
|
+
const header = [];
|
|
25
|
+
for (let c = range.c1; c <= range.c2; c++) {
|
|
26
|
+
header.push(escapeCell(columns[c]?.title, delimiter));
|
|
27
|
+
}
|
|
28
|
+
lines.push(header.join(delimiter));
|
|
29
|
+
}
|
|
30
|
+
for (let r = range.r1; r <= range.r2; r++) {
|
|
31
|
+
const line = [];
|
|
32
|
+
const row = rows[r];
|
|
33
|
+
for (let c = range.c1; c <= range.c2; c++) {
|
|
34
|
+
const column = columns[c];
|
|
35
|
+
line.push(
|
|
36
|
+
escapeCell(
|
|
37
|
+
column && row !== void 0 ? column.value(row) : "",
|
|
38
|
+
delimiter
|
|
39
|
+
)
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
lines.push(line.join(delimiter));
|
|
43
|
+
}
|
|
44
|
+
return lines.join("\n");
|
|
45
|
+
}
|
|
46
|
+
function fullRange(columns, rows) {
|
|
47
|
+
if (!rows.length || !columns.length) return null;
|
|
48
|
+
return { r1: 0, r2: rows.length - 1, c1: 0, c2: columns.length - 1 };
|
|
49
|
+
}
|
|
50
|
+
function describeRange(range) {
|
|
51
|
+
if (!range) return "No selection";
|
|
52
|
+
const rowCount = range.r2 - range.r1 + 1;
|
|
53
|
+
const colCount = range.c2 - range.c1 + 1;
|
|
54
|
+
if (rowCount === 1 && colCount === 1) return "1 cell selected";
|
|
55
|
+
return `${rowCount} \xD7 ${colCount} selected`;
|
|
56
|
+
}
|
|
57
|
+
function countCells(range) {
|
|
58
|
+
return {
|
|
59
|
+
rows: range.r2 - range.r1 + 1,
|
|
60
|
+
columns: range.c2 - range.c1 + 1
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
async function writeToClipboard(text) {
|
|
64
|
+
if (navigator.clipboard?.writeText) {
|
|
65
|
+
return navigator.clipboard.writeText(text);
|
|
66
|
+
}
|
|
67
|
+
const textarea = document.createElement("textarea");
|
|
68
|
+
textarea.value = text;
|
|
69
|
+
textarea.style.position = "fixed";
|
|
70
|
+
textarea.style.top = "-1000px";
|
|
71
|
+
document.body.appendChild(textarea);
|
|
72
|
+
textarea.select();
|
|
73
|
+
try {
|
|
74
|
+
if (!document.execCommand("copy")) {
|
|
75
|
+
throw new Error("Could not write to the clipboard");
|
|
76
|
+
}
|
|
77
|
+
} finally {
|
|
78
|
+
document.body.removeChild(textarea);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export {
|
|
83
|
+
TSV_DELIMITER,
|
|
84
|
+
CSV_DELIMITER,
|
|
85
|
+
normalizeRange,
|
|
86
|
+
escapeCell,
|
|
87
|
+
buildRangeText,
|
|
88
|
+
fullRange,
|
|
89
|
+
describeRange,
|
|
90
|
+
countCells,
|
|
91
|
+
writeToClipboard
|
|
92
|
+
};
|
|
93
|
+
//# sourceMappingURL=chunk-Y5WCT6XN.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/clipboard.ts"],"sourcesContent":["/**\n * Range maths and clipboard text for the card table's spreadsheet selection.\n *\n * Ported from video-shorts-panel `app/src/common/components/ExcelTable.jsx`.\n * Kept free of React and the DOM (except `writeToClipboard`) so the escaping\n * and range rules are unit-testable.\n */\n\nexport type CellRef = { r: number; c: number };\n\nexport type CellRange = { r1: number; r2: number; c1: number; c2: number };\n\n/** Tab for the clipboard (Excel/Sheets split on it), comma for a .csv file. */\nexport const TSV_DELIMITER = \"\\t\";\nexport const CSV_DELIMITER = \",\";\n\n/**\n * Anchor and focus can be dragged in any direction, so the range is whichever\n * corners they happen to be — normalize to top-left / bottom-right.\n */\nexport function normalizeRange(\n anchor: CellRef | null | undefined,\n focus: CellRef | null | undefined,\n): CellRange | null {\n if (!anchor || !focus) return null;\n return {\n r1: Math.min(anchor.r, focus.r),\n r2: Math.max(anchor.r, focus.r),\n c1: Math.min(anchor.c, focus.c),\n c2: Math.max(anchor.c, focus.c),\n };\n}\n\n/**\n * A cell needs quoting once it carries the delimiters themselves — the same\n * rule Excel uses when it parses the clipboard. Without it, a title containing\n * a tab or a newline silently becomes extra columns or rows on paste.\n */\nexport function escapeCell(raw: unknown, delimiter: string): string {\n const value = raw == null ? \"\" : String(raw);\n if (\n value.includes(delimiter) ||\n value.includes(\"\\n\") ||\n value.includes(\"\\r\") ||\n value.includes('\"')\n ) {\n return `\"${value.replace(/\"/g, '\"\"')}\"`;\n }\n return value;\n}\n\nexport type TextColumn<Row> = {\n title: string;\n value: (row: Row) => string;\n};\n\n/** Renders a range as delimited text, optionally with a header line. */\nexport function buildRangeText<Row>(\n range: CellRange | null,\n columns: TextColumn<Row>[],\n rows: Row[],\n delimiter: string,\n includeHeaders: boolean,\n): string {\n if (!range) return \"\";\n const lines: string[] = [];\n\n if (includeHeaders) {\n const header: string[] = [];\n for (let c = range.c1; c <= range.c2; c++) {\n header.push(escapeCell(columns[c]?.title, delimiter));\n }\n lines.push(header.join(delimiter));\n }\n\n for (let r = range.r1; r <= range.r2; r++) {\n const line: string[] = [];\n const row = rows[r];\n for (let c = range.c1; c <= range.c2; c++) {\n const column = columns[c];\n line.push(\n escapeCell(\n column && row !== undefined ? column.value(row) : \"\",\n delimiter,\n ),\n );\n }\n lines.push(line.join(delimiter));\n }\n\n return lines.join(\"\\n\");\n}\n\n/** The whole grid, or null when there is nothing to select. */\nexport function fullRange<Row>(\n columns: TextColumn<Row>[],\n rows: Row[],\n): CellRange | null {\n if (!rows.length || !columns.length) return null;\n return { r1: 0, r2: rows.length - 1, c1: 0, c2: columns.length - 1 };\n}\n\n/** \"3 × 2 selected\" — what the toolbar reports back to the user. */\nexport function describeRange(range: CellRange | null): string {\n if (!range) return \"No selection\";\n const rowCount = range.r2 - range.r1 + 1;\n const colCount = range.c2 - range.c1 + 1;\n if (rowCount === 1 && colCount === 1) return \"1 cell selected\";\n return `${rowCount} × ${colCount} selected`;\n}\n\nexport function countCells(range: CellRange): {\n rows: number;\n columns: number;\n} {\n return {\n rows: range.r2 - range.r1 + 1,\n columns: range.c2 - range.c1 + 1,\n };\n}\n\n/**\n * `navigator.clipboard` is unavailable on http origins and older browsers, so\n * fall back to a hidden textarea + execCommand rather than failing the copy.\n */\nexport async function writeToClipboard(text: string): Promise<void> {\n if (navigator.clipboard?.writeText) {\n return navigator.clipboard.writeText(text);\n }\n\n const textarea = document.createElement(\"textarea\");\n textarea.value = text;\n textarea.style.position = \"fixed\";\n textarea.style.top = \"-1000px\";\n document.body.appendChild(textarea);\n textarea.select();\n try {\n if (!document.execCommand(\"copy\")) {\n throw new Error(\"Could not write to the clipboard\");\n }\n } finally {\n document.body.removeChild(textarea);\n }\n}\n"],"mappings":";AAaO,IAAM,gBAAgB;AACtB,IAAM,gBAAgB;AAMtB,SAAS,eACd,QACA,OACkB;AAClB,MAAI,CAAC,UAAU,CAAC,MAAO,QAAO;AAC9B,SAAO;AAAA,IACL,IAAI,KAAK,IAAI,OAAO,GAAG,MAAM,CAAC;AAAA,IAC9B,IAAI,KAAK,IAAI,OAAO,GAAG,MAAM,CAAC;AAAA,IAC9B,IAAI,KAAK,IAAI,OAAO,GAAG,MAAM,CAAC;AAAA,IAC9B,IAAI,KAAK,IAAI,OAAO,GAAG,MAAM,CAAC;AAAA,EAChC;AACF;AAOO,SAAS,WAAW,KAAc,WAA2B;AAClE,QAAM,QAAQ,OAAO,OAAO,KAAK,OAAO,GAAG;AAC3C,MACE,MAAM,SAAS,SAAS,KACxB,MAAM,SAAS,IAAI,KACnB,MAAM,SAAS,IAAI,KACnB,MAAM,SAAS,GAAG,GAClB;AACA,WAAO,IAAI,MAAM,QAAQ,MAAM,IAAI,CAAC;AAAA,EACtC;AACA,SAAO;AACT;AAQO,SAAS,eACd,OACA,SACA,MACA,WACA,gBACQ;AACR,MAAI,CAAC,MAAO,QAAO;AACnB,QAAM,QAAkB,CAAC;AAEzB,MAAI,gBAAgB;AAClB,UAAM,SAAmB,CAAC;AAC1B,aAAS,IAAI,MAAM,IAAI,KAAK,MAAM,IAAI,KAAK;AACzC,aAAO,KAAK,WAAW,QAAQ,CAAC,GAAG,OAAO,SAAS,CAAC;AAAA,IACtD;AACA,UAAM,KAAK,OAAO,KAAK,SAAS,CAAC;AAAA,EACnC;AAEA,WAAS,IAAI,MAAM,IAAI,KAAK,MAAM,IAAI,KAAK;AACzC,UAAM,OAAiB,CAAC;AACxB,UAAM,MAAM,KAAK,CAAC;AAClB,aAAS,IAAI,MAAM,IAAI,KAAK,MAAM,IAAI,KAAK;AACzC,YAAM,SAAS,QAAQ,CAAC;AACxB,WAAK;AAAA,QACH;AAAA,UACE,UAAU,QAAQ,SAAY,OAAO,MAAM,GAAG,IAAI;AAAA,UAClD;AAAA,QACF;AAAA,MACF;AAAA,IACF;AACA,UAAM,KAAK,KAAK,KAAK,SAAS,CAAC;AAAA,EACjC;AAEA,SAAO,MAAM,KAAK,IAAI;AACxB;AAGO,SAAS,UACd,SACA,MACkB;AAClB,MAAI,CAAC,KAAK,UAAU,CAAC,QAAQ,OAAQ,QAAO;AAC5C,SAAO,EAAE,IAAI,GAAG,IAAI,KAAK,SAAS,GAAG,IAAI,GAAG,IAAI,QAAQ,SAAS,EAAE;AACrE;AAGO,SAAS,cAAc,OAAiC;AAC7D,MAAI,CAAC,MAAO,QAAO;AACnB,QAAM,WAAW,MAAM,KAAK,MAAM,KAAK;AACvC,QAAM,WAAW,MAAM,KAAK,MAAM,KAAK;AACvC,MAAI,aAAa,KAAK,aAAa,EAAG,QAAO;AAC7C,SAAO,GAAG,QAAQ,SAAM,QAAQ;AAClC;AAEO,SAAS,WAAW,OAGzB;AACA,SAAO;AAAA,IACL,MAAM,MAAM,KAAK,MAAM,KAAK;AAAA,IAC5B,SAAS,MAAM,KAAK,MAAM,KAAK;AAAA,EACjC;AACF;AAMA,eAAsB,iBAAiB,MAA6B;AAClE,MAAI,UAAU,WAAW,WAAW;AAClC,WAAO,UAAU,UAAU,UAAU,IAAI;AAAA,EAC3C;AAEA,QAAM,WAAW,SAAS,cAAc,UAAU;AAClD,WAAS,QAAQ;AACjB,WAAS,MAAM,WAAW;AAC1B,WAAS,MAAM,MAAM;AACrB,WAAS,KAAK,YAAY,QAAQ;AAClC,WAAS,OAAO;AAChB,MAAI;AACF,QAAI,CAAC,SAAS,YAAY,MAAM,GAAG;AACjC,YAAM,IAAI,MAAM,kCAAkC;AAAA,IACpD;AAAA,EACF,UAAE;AACA,aAAS,KAAK,YAAY,QAAQ;AAAA,EACpC;AACF;","names":[]}
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Input
|
|
3
|
+
} from "./chunk-PWAKTEXF.js";
|
|
4
|
+
import {
|
|
5
|
+
Button
|
|
6
|
+
} from "./chunk-KR3BCFP7.js";
|
|
7
|
+
import {
|
|
8
|
+
cn
|
|
9
|
+
} from "./chunk-HCA5ODCD.js";
|
|
10
|
+
|
|
11
|
+
// src/image-uploader.tsx
|
|
12
|
+
import { ImagePlus, Loader2, X } from "lucide-react";
|
|
13
|
+
import * as React from "react";
|
|
14
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
15
|
+
function ImageUploader({
|
|
16
|
+
value = "",
|
|
17
|
+
onChange,
|
|
18
|
+
disabled,
|
|
19
|
+
label,
|
|
20
|
+
className,
|
|
21
|
+
allowUrlPaste = true,
|
|
22
|
+
accept = "image/*,.gif",
|
|
23
|
+
uploadFile,
|
|
24
|
+
uploadFromUrl
|
|
25
|
+
}) {
|
|
26
|
+
const inputRef = React.useRef(null);
|
|
27
|
+
const [uploading, setUploading] = React.useState(false);
|
|
28
|
+
const [error, setError] = React.useState(null);
|
|
29
|
+
const busy = Boolean(disabled || uploading);
|
|
30
|
+
const handleFile = async (file) => {
|
|
31
|
+
if (!uploadFile) {
|
|
32
|
+
setError("File upload is not configured for this panel.");
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
setUploading(true);
|
|
36
|
+
setError(null);
|
|
37
|
+
try {
|
|
38
|
+
onChange(await uploadFile(file));
|
|
39
|
+
} catch (err) {
|
|
40
|
+
setError(err instanceof Error ? err.message : "Upload failed");
|
|
41
|
+
} finally {
|
|
42
|
+
setUploading(false);
|
|
43
|
+
if (inputRef.current) inputRef.current.value = "";
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
const handleUrl = async (imageUrl) => {
|
|
47
|
+
const trimmed = imageUrl.trim();
|
|
48
|
+
if (!trimmed) {
|
|
49
|
+
onChange("");
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
if (trimmed === value) return;
|
|
53
|
+
if (!uploadFromUrl || /^https?:\/\//i.test(trimmed)) {
|
|
54
|
+
setError(null);
|
|
55
|
+
onChange(trimmed);
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
setUploading(true);
|
|
59
|
+
setError(null);
|
|
60
|
+
try {
|
|
61
|
+
onChange(await uploadFromUrl(trimmed));
|
|
62
|
+
} catch (err) {
|
|
63
|
+
setError(err instanceof Error ? err.message : "Upload failed");
|
|
64
|
+
} finally {
|
|
65
|
+
setUploading(false);
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
return /* @__PURE__ */ jsxs("div", { className: cn("space-y-2", className), children: [
|
|
69
|
+
label ? /* @__PURE__ */ jsx("div", { className: "text-muted-foreground text-xs font-medium", children: label }) : null,
|
|
70
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [
|
|
71
|
+
/* @__PURE__ */ jsx(
|
|
72
|
+
"input",
|
|
73
|
+
{
|
|
74
|
+
ref: inputRef,
|
|
75
|
+
type: "file",
|
|
76
|
+
accept,
|
|
77
|
+
className: "hidden",
|
|
78
|
+
disabled: busy,
|
|
79
|
+
onChange: (e) => {
|
|
80
|
+
const file = e.target.files?.[0];
|
|
81
|
+
if (file) void handleFile(file);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
),
|
|
85
|
+
/* @__PURE__ */ jsxs(
|
|
86
|
+
Button,
|
|
87
|
+
{
|
|
88
|
+
type: "button",
|
|
89
|
+
variant: "outline",
|
|
90
|
+
size: "sm",
|
|
91
|
+
disabled: busy,
|
|
92
|
+
onClick: () => inputRef.current?.click(),
|
|
93
|
+
children: [
|
|
94
|
+
uploading ? /* @__PURE__ */ jsx(Loader2, { className: "h-4 w-4 animate-spin" }) : /* @__PURE__ */ jsx(ImagePlus, { className: "h-4 w-4" }),
|
|
95
|
+
uploading ? "Uploading\u2026" : "Upload"
|
|
96
|
+
]
|
|
97
|
+
}
|
|
98
|
+
),
|
|
99
|
+
value ? /* @__PURE__ */ jsxs(
|
|
100
|
+
Button,
|
|
101
|
+
{
|
|
102
|
+
type: "button",
|
|
103
|
+
variant: "ghost",
|
|
104
|
+
size: "sm",
|
|
105
|
+
disabled: busy,
|
|
106
|
+
onClick: () => onChange(""),
|
|
107
|
+
"aria-label": "Clear image",
|
|
108
|
+
children: [
|
|
109
|
+
/* @__PURE__ */ jsx(X, { className: "h-4 w-4" }),
|
|
110
|
+
"Clear"
|
|
111
|
+
]
|
|
112
|
+
}
|
|
113
|
+
) : null
|
|
114
|
+
] }),
|
|
115
|
+
allowUrlPaste ? /* @__PURE__ */ jsx(
|
|
116
|
+
Input,
|
|
117
|
+
{
|
|
118
|
+
defaultValue: value,
|
|
119
|
+
disabled: busy,
|
|
120
|
+
placeholder: "Or paste image URL\u2026",
|
|
121
|
+
onBlur: (e) => {
|
|
122
|
+
void handleUrl(e.target.value);
|
|
123
|
+
},
|
|
124
|
+
onKeyDown: (e) => {
|
|
125
|
+
if (e.key === "Enter") {
|
|
126
|
+
e.preventDefault();
|
|
127
|
+
void handleUrl(e.target.value);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
},
|
|
131
|
+
value || "empty"
|
|
132
|
+
) : null,
|
|
133
|
+
value ? (
|
|
134
|
+
// eslint-disable-next-line @next/next/no-img-element -- CDN URLs vary
|
|
135
|
+
/* @__PURE__ */ jsx(
|
|
136
|
+
"img",
|
|
137
|
+
{
|
|
138
|
+
src: value,
|
|
139
|
+
alt: "",
|
|
140
|
+
className: "border-border max-h-24 rounded border bg-white object-contain p-1"
|
|
141
|
+
}
|
|
142
|
+
)
|
|
143
|
+
) : null,
|
|
144
|
+
error ? /* @__PURE__ */ jsx("p", { className: "text-destructive text-xs", role: "alert", children: error }) : null
|
|
145
|
+
] });
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
export {
|
|
149
|
+
ImageUploader
|
|
150
|
+
};
|
|
151
|
+
//# sourceMappingURL=chunk-YDICVETX.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/image-uploader.tsx"],"sourcesContent":["\"use client\";\n\nimport { ImagePlus, Loader2, X } from \"lucide-react\";\nimport * as React from \"react\";\n\nimport { Button } from \"./button\";\nimport { Input } from \"./input\";\nimport { cn } from \"./utils\";\n\nexport type ImageUploaderProps = {\n value?: string;\n onChange: (url: string) => void;\n disabled?: boolean;\n label?: string;\n className?: string;\n /** Allow pasting a remote URL in addition to file upload. Default true. */\n allowUrlPaste?: boolean;\n accept?: string;\n /**\n * Upload a picked file and return its hosted URL.\n * Injected by the panel — Upload stays enabled either way; without a\n * handler, picking a file surfaces a configuration error.\n */\n uploadFile?: (file: File) => Promise<string>;\n /**\n * Optional transform for pasted values (e.g. host a relative path).\n * When omitted, any pasted non-empty string is applied as-is. Absolute\n * `http(s)` URLs never call this.\n */\n uploadFromUrl?: (imageUrl: string) => Promise<string>;\n};\n\n/**\n * Reusable image uploader.\n *\n * Upload and URL paste are both enabled by default. Hosting is injected:\n * each panel passes `uploadFile` / `uploadFromUrl` so the kit stays free of\n * panel-specific API routes.\n */\nexport function ImageUploader({\n value = \"\",\n onChange,\n disabled,\n label,\n className,\n allowUrlPaste = true,\n accept = \"image/*,.gif\",\n uploadFile,\n uploadFromUrl,\n}: ImageUploaderProps) {\n const inputRef = React.useRef<HTMLInputElement>(null);\n const [uploading, setUploading] = React.useState(false);\n const [error, setError] = React.useState<string | null>(null);\n const busy = Boolean(disabled || uploading);\n\n const handleFile = async (file: File) => {\n if (!uploadFile) {\n setError(\"File upload is not configured for this panel.\");\n return;\n }\n setUploading(true);\n setError(null);\n try {\n onChange(await uploadFile(file));\n } catch (err) {\n setError(err instanceof Error ? err.message : \"Upload failed\");\n } finally {\n setUploading(false);\n if (inputRef.current) inputRef.current.value = \"\";\n }\n };\n\n const handleUrl = async (imageUrl: string) => {\n const trimmed = imageUrl.trim();\n if (!trimmed) {\n onChange(\"\");\n return;\n }\n if (trimmed === value) return;\n\n // Absolute URLs — and any paste when no transform is configured — apply\n // as-is. Paste is never gated behind a disabled input.\n if (!uploadFromUrl || /^https?:\\/\\//i.test(trimmed)) {\n setError(null);\n onChange(trimmed);\n return;\n }\n\n setUploading(true);\n setError(null);\n try {\n onChange(await uploadFromUrl(trimmed));\n } catch (err) {\n setError(err instanceof Error ? err.message : \"Upload failed\");\n } finally {\n setUploading(false);\n }\n };\n\n return (\n <div className={cn(\"space-y-2\", className)}>\n {label ? (\n <div className=\"text-muted-foreground text-xs font-medium\">{label}</div>\n ) : null}\n\n <div className=\"flex flex-wrap items-center gap-2\">\n <input\n ref={inputRef}\n type=\"file\"\n accept={accept}\n className=\"hidden\"\n disabled={busy}\n onChange={(e) => {\n const file = e.target.files?.[0];\n if (file) void handleFile(file);\n }}\n />\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n disabled={busy}\n onClick={() => inputRef.current?.click()}\n >\n {uploading ? (\n <Loader2 className=\"h-4 w-4 animate-spin\" />\n ) : (\n <ImagePlus className=\"h-4 w-4\" />\n )}\n {uploading ? \"Uploading…\" : \"Upload\"}\n </Button>\n {value ? (\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n disabled={busy}\n onClick={() => onChange(\"\")}\n aria-label=\"Clear image\"\n >\n <X className=\"h-4 w-4\" />\n Clear\n </Button>\n ) : null}\n </div>\n\n {allowUrlPaste ? (\n <Input\n key={value || \"empty\"}\n defaultValue={value}\n disabled={busy}\n placeholder=\"Or paste image URL…\"\n onBlur={(e) => {\n void handleUrl(e.target.value);\n }}\n onKeyDown={(e) => {\n if (e.key === \"Enter\") {\n e.preventDefault();\n void handleUrl((e.target as HTMLInputElement).value);\n }\n }}\n />\n ) : null}\n\n {value ? (\n // eslint-disable-next-line @next/next/no-img-element -- CDN URLs vary\n <img\n src={value}\n alt=\"\"\n className=\"border-border max-h-24 rounded border bg-white object-contain p-1\"\n />\n ) : null}\n\n {error ? (\n <p className=\"text-destructive text-xs\" role=\"alert\">\n {error}\n </p>\n ) : null}\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;AAEA,SAAS,WAAW,SAAS,SAAS;AACtC,YAAY,WAAW;AAmGf,cAeA,YAfA;AA/DD,SAAS,cAAc;AAAA,EAC5B,QAAQ;AAAA,EACR;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,gBAAgB;AAAA,EAChB,SAAS;AAAA,EACT;AAAA,EACA;AACF,GAAuB;AACrB,QAAM,WAAiB,aAAyB,IAAI;AACpD,QAAM,CAAC,WAAW,YAAY,IAAU,eAAS,KAAK;AACtD,QAAM,CAAC,OAAO,QAAQ,IAAU,eAAwB,IAAI;AAC5D,QAAM,OAAO,QAAQ,YAAY,SAAS;AAE1C,QAAM,aAAa,OAAO,SAAe;AACvC,QAAI,CAAC,YAAY;AACf,eAAS,+CAA+C;AACxD;AAAA,IACF;AACA,iBAAa,IAAI;AACjB,aAAS,IAAI;AACb,QAAI;AACF,eAAS,MAAM,WAAW,IAAI,CAAC;AAAA,IACjC,SAAS,KAAK;AACZ,eAAS,eAAe,QAAQ,IAAI,UAAU,eAAe;AAAA,IAC/D,UAAE;AACA,mBAAa,KAAK;AAClB,UAAI,SAAS,QAAS,UAAS,QAAQ,QAAQ;AAAA,IACjD;AAAA,EACF;AAEA,QAAM,YAAY,OAAO,aAAqB;AAC5C,UAAM,UAAU,SAAS,KAAK;AAC9B,QAAI,CAAC,SAAS;AACZ,eAAS,EAAE;AACX;AAAA,IACF;AACA,QAAI,YAAY,MAAO;AAIvB,QAAI,CAAC,iBAAiB,gBAAgB,KAAK,OAAO,GAAG;AACnD,eAAS,IAAI;AACb,eAAS,OAAO;AAChB;AAAA,IACF;AAEA,iBAAa,IAAI;AACjB,aAAS,IAAI;AACb,QAAI;AACF,eAAS,MAAM,cAAc,OAAO,CAAC;AAAA,IACvC,SAAS,KAAK;AACZ,eAAS,eAAe,QAAQ,IAAI,UAAU,eAAe;AAAA,IAC/D,UAAE;AACA,mBAAa,KAAK;AAAA,IACpB;AAAA,EACF;AAEA,SACE,qBAAC,SAAI,WAAW,GAAG,aAAa,SAAS,GACtC;AAAA,YACC,oBAAC,SAAI,WAAU,6CAA6C,iBAAM,IAChE;AAAA,IAEJ,qBAAC,SAAI,WAAU,qCACb;AAAA;AAAA,QAAC;AAAA;AAAA,UACC,KAAK;AAAA,UACL,MAAK;AAAA,UACL;AAAA,UACA,WAAU;AAAA,UACV,UAAU;AAAA,UACV,UAAU,CAAC,MAAM;AACf,kBAAM,OAAO,EAAE,OAAO,QAAQ,CAAC;AAC/B,gBAAI,KAAM,MAAK,WAAW,IAAI;AAAA,UAChC;AAAA;AAAA,MACF;AAAA,MACA;AAAA,QAAC;AAAA;AAAA,UACC,MAAK;AAAA,UACL,SAAQ;AAAA,UACR,MAAK;AAAA,UACL,UAAU;AAAA,UACV,SAAS,MAAM,SAAS,SAAS,MAAM;AAAA,UAEtC;AAAA,wBACC,oBAAC,WAAQ,WAAU,wBAAuB,IAE1C,oBAAC,aAAU,WAAU,WAAU;AAAA,YAEhC,YAAY,oBAAe;AAAA;AAAA;AAAA,MAC9B;AAAA,MACC,QACC;AAAA,QAAC;AAAA;AAAA,UACC,MAAK;AAAA,UACL,SAAQ;AAAA,UACR,MAAK;AAAA,UACL,UAAU;AAAA,UACV,SAAS,MAAM,SAAS,EAAE;AAAA,UAC1B,cAAW;AAAA,UAEX;AAAA,gCAAC,KAAE,WAAU,WAAU;AAAA,YAAE;AAAA;AAAA;AAAA,MAE3B,IACE;AAAA,OACN;AAAA,IAEC,gBACC;AAAA,MAAC;AAAA;AAAA,QAEC,cAAc;AAAA,QACd,UAAU;AAAA,QACV,aAAY;AAAA,QACZ,QAAQ,CAAC,MAAM;AACb,eAAK,UAAU,EAAE,OAAO,KAAK;AAAA,QAC/B;AAAA,QACA,WAAW,CAAC,MAAM;AAChB,cAAI,EAAE,QAAQ,SAAS;AACrB,cAAE,eAAe;AACjB,iBAAK,UAAW,EAAE,OAA4B,KAAK;AAAA,UACrD;AAAA,QACF;AAAA;AAAA,MAZK,SAAS;AAAA,IAahB,IACE;AAAA,IAEH;AAAA;AAAA,MAEC;AAAA,QAAC;AAAA;AAAA,UACC,KAAK;AAAA,UACL,KAAI;AAAA,UACJ,WAAU;AAAA;AAAA,MACZ;AAAA,QACE;AAAA,IAEH,QACC,oBAAC,OAAE,WAAU,4BAA2B,MAAK,SAC1C,iBACH,IACE;AAAA,KACN;AAEJ;","names":[]}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import {
|
|
2
|
+
useDialogLayer
|
|
3
|
+
} from "./chunk-HBBOR43A.js";
|
|
4
|
+
import {
|
|
5
|
+
cn
|
|
6
|
+
} from "./chunk-HCA5ODCD.js";
|
|
7
|
+
|
|
8
|
+
// src/modal.tsx
|
|
9
|
+
import * as React from "react";
|
|
10
|
+
import { createPortal } from "react-dom";
|
|
11
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
12
|
+
var emptySubscribe = () => () => {
|
|
13
|
+
};
|
|
14
|
+
function useHasMounted() {
|
|
15
|
+
return React.useSyncExternalStore(
|
|
16
|
+
emptySubscribe,
|
|
17
|
+
() => true,
|
|
18
|
+
() => false
|
|
19
|
+
);
|
|
20
|
+
}
|
|
21
|
+
function Modal({
|
|
22
|
+
isOpen,
|
|
23
|
+
labelledBy,
|
|
24
|
+
describedBy,
|
|
25
|
+
closeLabel,
|
|
26
|
+
onClose,
|
|
27
|
+
children,
|
|
28
|
+
dismissible = true,
|
|
29
|
+
role = "dialog",
|
|
30
|
+
panelClassName
|
|
31
|
+
}) {
|
|
32
|
+
const mounted = useHasMounted();
|
|
33
|
+
const panelRef = React.useRef(null);
|
|
34
|
+
useDialogLayer({
|
|
35
|
+
// Gated on `mounted` too: before that the panel is not rendered, so there
|
|
36
|
+
// would be nothing to move focus into.
|
|
37
|
+
active: isOpen && mounted,
|
|
38
|
+
containerRef: panelRef,
|
|
39
|
+
onEscape: dismissible ? onClose : void 0
|
|
40
|
+
});
|
|
41
|
+
if (!mounted || !isOpen) return null;
|
|
42
|
+
return createPortal(
|
|
43
|
+
/*
|
|
44
|
+
`overflow-y-auto` on the overlay, `my-auto` on the panel — not
|
|
45
|
+
`items-center`. A flex item centred with `align-items` overflows equally
|
|
46
|
+
in both directions once it is taller than its container, and the half
|
|
47
|
+
above the top edge cannot be scrolled to: with the page behind it locked,
|
|
48
|
+
an over-tall dialog was simply unreachable at both ends. Auto margins
|
|
49
|
+
centre a short panel the same way and collapse to zero on a tall one,
|
|
50
|
+
which scrolls.
|
|
51
|
+
*/
|
|
52
|
+
/* @__PURE__ */ jsxs("div", { className: "fixed inset-0 z-[100] flex justify-center overflow-y-auto p-4", children: [
|
|
53
|
+
/* @__PURE__ */ jsx(
|
|
54
|
+
"button",
|
|
55
|
+
{
|
|
56
|
+
type: "button",
|
|
57
|
+
"aria-label": closeLabel,
|
|
58
|
+
tabIndex: -1,
|
|
59
|
+
className: "fixed inset-0 bg-slate-950/50",
|
|
60
|
+
onClick: () => {
|
|
61
|
+
if (dismissible) onClose();
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
),
|
|
65
|
+
/* @__PURE__ */ jsx(
|
|
66
|
+
"div",
|
|
67
|
+
{
|
|
68
|
+
ref: panelRef,
|
|
69
|
+
role,
|
|
70
|
+
"aria-modal": "true",
|
|
71
|
+
"aria-labelledby": labelledBy,
|
|
72
|
+
"aria-describedby": describedBy,
|
|
73
|
+
tabIndex: -1,
|
|
74
|
+
className: cn(
|
|
75
|
+
"border-border bg-background relative z-10 my-auto w-full rounded-lg border shadow-2xl outline-none",
|
|
76
|
+
panelClassName ?? "max-w-md p-6"
|
|
77
|
+
),
|
|
78
|
+
children
|
|
79
|
+
}
|
|
80
|
+
)
|
|
81
|
+
] }),
|
|
82
|
+
document.body
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export {
|
|
87
|
+
useHasMounted,
|
|
88
|
+
Modal
|
|
89
|
+
};
|
|
90
|
+
//# sourceMappingURL=chunk-YSU4VWRD.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/modal.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { createPortal } from \"react-dom\";\n\nimport { useDialogLayer } from \"./dialogLayer\";\nimport { cn } from \"./utils\";\n\nconst emptySubscribe = () => () => {};\n\n/**\n * Returns false during SSR and the first client render, then true once mounted.\n * `useSyncExternalStore` is the React-recommended way to read client-only state\n * without a setState-in-effect (which triggers cascading renders).\n */\nexport function useHasMounted(): boolean {\n return React.useSyncExternalStore(\n emptySubscribe,\n () => true,\n () => false,\n );\n}\n\ninterface ModalProps {\n isOpen: boolean;\n /** id of the element labelling the dialog; wired to aria-labelledby. */\n labelledBy: string;\n /**\n * Optional id(s) of elements describing the dialog (APG alertdialog body).\n * Space-separated when multiple.\n */\n describedBy?: string;\n /** Accessible label for the backdrop dismiss button. */\n closeLabel: string;\n onClose: () => void;\n children: React.ReactNode;\n /**\n * When false, Escape and backdrop clicks do not close the modal (e.g. while a\n * save is in flight). Defaults to true.\n */\n dismissible?: boolean;\n /** Use \"alertdialog\" for destructive confirmations. Defaults to \"dialog\". */\n role?: \"dialog\" | \"alertdialog\";\n /** Tailwind max-width class for the panel. Defaults to \"max-w-md\". */\n panelClassName?: string;\n}\n\n/**\n * Shared modal shell: renders a portal-backed, scroll-locked, Escape-dismissible\n * overlay with a centered panel. Callers provide the panel body (heading, form,\n * footer). Consolidates the boilerplate previously copied across every dialog.\n */\nexport function Modal({\n isOpen,\n labelledBy,\n describedBy,\n closeLabel,\n onClose,\n children,\n dismissible = true,\n role = \"dialog\",\n panelClassName,\n}: ModalProps) {\n const mounted = useHasMounted();\n const panelRef = React.useRef<HTMLDivElement>(null);\n\n useDialogLayer({\n // Gated on `mounted` too: before that the panel is not rendered, so there\n // would be nothing to move focus into.\n active: isOpen && mounted,\n containerRef: panelRef,\n onEscape: dismissible ? onClose : undefined,\n });\n\n if (!mounted || !isOpen) return null;\n\n return createPortal(\n /*\n `overflow-y-auto` on the overlay, `my-auto` on the panel — not\n `items-center`. A flex item centred with `align-items` overflows equally\n in both directions once it is taller than its container, and the half\n above the top edge cannot be scrolled to: with the page behind it locked,\n an over-tall dialog was simply unreachable at both ends. Auto margins\n centre a short panel the same way and collapse to zero on a tall one,\n which scrolls.\n */\n <div className=\"fixed inset-0 z-[100] flex justify-center overflow-y-auto p-4\">\n {/* `fixed`, not `absolute`: inside a scrolling overlay an absolute\n backdrop scrolls away with the content and leaves the rest of a tall\n dialog sitting on an undimmed page. */}\n <button\n type=\"button\"\n aria-label={closeLabel}\n tabIndex={-1}\n className=\"fixed inset-0 bg-slate-950/50\"\n onClick={() => {\n if (dismissible) onClose();\n }}\n />\n <div\n ref={panelRef}\n role={role}\n aria-modal=\"true\"\n aria-labelledby={labelledBy}\n aria-describedby={describedBy}\n tabIndex={-1}\n className={cn(\n \"border-border bg-background relative z-10 my-auto w-full rounded-lg border shadow-2xl outline-none\",\n panelClassName ?? \"max-w-md p-6\",\n )}\n >\n {children}\n </div>\n </div>,\n document.body,\n );\n}\n"],"mappings":";;;;;;;;AAEA,YAAY,WAAW;AACvB,SAAS,oBAAoB;AAmFzB,SAIE,KAJF;AA9EJ,IAAM,iBAAiB,MAAM,MAAM;AAAC;AAO7B,SAAS,gBAAyB;AACvC,SAAa;AAAA,IACX;AAAA,IACA,MAAM;AAAA,IACN,MAAM;AAAA,EACR;AACF;AA+BO,SAAS,MAAM;AAAA,EACpB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,cAAc;AAAA,EACd,OAAO;AAAA,EACP;AACF,GAAe;AACb,QAAM,UAAU,cAAc;AAC9B,QAAM,WAAiB,aAAuB,IAAI;AAElD,iBAAe;AAAA;AAAA;AAAA,IAGb,QAAQ,UAAU;AAAA,IAClB,cAAc;AAAA,IACd,UAAU,cAAc,UAAU;AAAA,EACpC,CAAC;AAED,MAAI,CAAC,WAAW,CAAC,OAAQ,QAAO;AAEhC,SAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAUL,qBAAC,SAAI,WAAU,iEAIb;AAAA;AAAA,QAAC;AAAA;AAAA,UACC,MAAK;AAAA,UACL,cAAY;AAAA,UACZ,UAAU;AAAA,UACV,WAAU;AAAA,UACV,SAAS,MAAM;AACb,gBAAI,YAAa,SAAQ;AAAA,UAC3B;AAAA;AAAA,MACF;AAAA,MACA;AAAA,QAAC;AAAA;AAAA,UACC,KAAK;AAAA,UACL;AAAA,UACA,cAAW;AAAA,UACX,mBAAiB;AAAA,UACjB,oBAAkB;AAAA,UAClB,UAAU;AAAA,UACV,WAAW;AAAA,YACT;AAAA,YACA,kBAAkB;AAAA,UACpB;AAAA,UAEC;AAAA;AAAA,MACH;AAAA,OACF;AAAA,IACA,SAAS;AAAA,EACX;AACF;","names":[]}
|