@inshorts/glint-ui 0.5.4 → 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/README.md +3 -1
- 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 +390 -13
- 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 -47
- 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,85 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Spinner
|
|
3
|
+
} from "./chunk-UE7JY7VR.js";
|
|
4
|
+
import {
|
|
5
|
+
cn
|
|
6
|
+
} from "./chunk-HCA5ODCD.js";
|
|
7
|
+
|
|
8
|
+
// src/button.tsx
|
|
9
|
+
import * as React from "react";
|
|
10
|
+
import { cva } from "class-variance-authority";
|
|
11
|
+
import { Slot } from "@radix-ui/react-slot";
|
|
12
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
13
|
+
var buttonVariants = cva(
|
|
14
|
+
"inline-flex h-9 cursor-pointer items-center justify-center gap-2 whitespace-nowrap rounded-md px-3 text-sm font-medium transition duration-200 active:scale-[0.98] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50",
|
|
15
|
+
{
|
|
16
|
+
variants: {
|
|
17
|
+
variant: {
|
|
18
|
+
default: "bg-primary text-primary-foreground hover:bg-primary/90",
|
|
19
|
+
secondary: "bg-muted text-foreground hover:bg-muted/80",
|
|
20
|
+
ghost: "hover:bg-muted",
|
|
21
|
+
destructive: "bg-destructive text-destructive-foreground hover:bg-destructive/90",
|
|
22
|
+
outline: "border border-border bg-background hover:bg-muted"
|
|
23
|
+
},
|
|
24
|
+
size: {
|
|
25
|
+
sm: "h-8 px-2.5",
|
|
26
|
+
md: "h-9 px-3",
|
|
27
|
+
lg: "h-10 px-4",
|
|
28
|
+
icon: "h-9 w-9 px-0",
|
|
29
|
+
// Square, matching `sm`'s height — for an icon-only control sitting in
|
|
30
|
+
// a table row or beside `sm` buttons, where `icon` is a row taller.
|
|
31
|
+
"icon-sm": "h-8 w-8 px-0"
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
defaultVariants: {
|
|
35
|
+
variant: "default",
|
|
36
|
+
size: "md"
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
);
|
|
40
|
+
var ICON_SIZES = /* @__PURE__ */ new Set(["icon", "icon-sm"]);
|
|
41
|
+
var Button = React.forwardRef(
|
|
42
|
+
({
|
|
43
|
+
className,
|
|
44
|
+
variant,
|
|
45
|
+
size,
|
|
46
|
+
asChild = false,
|
|
47
|
+
loading,
|
|
48
|
+
disabled,
|
|
49
|
+
children,
|
|
50
|
+
...props
|
|
51
|
+
}, ref) => {
|
|
52
|
+
const Comp = asChild ? Slot : "button";
|
|
53
|
+
if (asChild) {
|
|
54
|
+
return /* @__PURE__ */ jsx(
|
|
55
|
+
Comp,
|
|
56
|
+
{
|
|
57
|
+
ref,
|
|
58
|
+
className: cn(buttonVariants({ variant, size }), className),
|
|
59
|
+
...props,
|
|
60
|
+
children
|
|
61
|
+
}
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
return /* @__PURE__ */ jsxs(
|
|
65
|
+
"button",
|
|
66
|
+
{
|
|
67
|
+
ref,
|
|
68
|
+
disabled: disabled || loading,
|
|
69
|
+
"aria-busy": loading || void 0,
|
|
70
|
+
className: cn(buttonVariants({ variant, size }), className),
|
|
71
|
+
...props,
|
|
72
|
+
children: [
|
|
73
|
+
loading ? /* @__PURE__ */ jsx(Spinner, { size: "sm" }) : null,
|
|
74
|
+
loading && size != null && ICON_SIZES.has(size) ? null : children
|
|
75
|
+
]
|
|
76
|
+
}
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
);
|
|
80
|
+
Button.displayName = "Button";
|
|
81
|
+
|
|
82
|
+
export {
|
|
83
|
+
Button
|
|
84
|
+
};
|
|
85
|
+
//# sourceMappingURL=chunk-KR3BCFP7.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/button.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport { Slot } from \"@radix-ui/react-slot\";\n\nimport { Spinner } from \"./spinner\";\nimport { cn } from \"./utils\";\n\nconst buttonVariants = cva(\n \"inline-flex h-9 cursor-pointer items-center justify-center gap-2 whitespace-nowrap rounded-md px-3 text-sm font-medium transition duration-200 active:scale-[0.98] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50\",\n {\n variants: {\n variant: {\n default: \"bg-primary text-primary-foreground hover:bg-primary/90\",\n secondary: \"bg-muted text-foreground hover:bg-muted/80\",\n ghost: \"hover:bg-muted\",\n destructive:\n \"bg-destructive text-destructive-foreground hover:bg-destructive/90\",\n outline: \"border border-border bg-background hover:bg-muted\",\n },\n size: {\n sm: \"h-8 px-2.5\",\n md: \"h-9 px-3\",\n lg: \"h-10 px-4\",\n icon: \"h-9 w-9 px-0\",\n // Square, matching `sm`'s height — for an icon-only control sitting in\n // a table row or beside `sm` buttons, where `icon` is a row taller.\n \"icon-sm\": \"h-8 w-8 px-0\",\n },\n },\n defaultVariants: {\n variant: \"default\",\n size: \"md\",\n },\n },\n);\n\nexport interface ButtonProps\n extends\n React.ButtonHTMLAttributes<HTMLButtonElement>,\n VariantProps<typeof buttonVariants> {\n asChild?: boolean;\n /**\n * Work is in flight: shows a spinner, disables the button, and sets\n * `aria-busy`.\n *\n * This exists because the panel had 272 disabled buttons and only 22 that\n * said anything about why — the rest went dead on click with no spinner and\n * no label change, which reads as a broken button rather than a busy one.\n * Making the feedback part of `disabled` rather than something each caller\n * remembers separately is the only way that ratio stays fixed.\n *\n * The label is kept rather than swapped. \"Saving…\" loses which button you\n * pressed, and the spinner already says it is working. An icon-only button\n * is the exception — there is no room beside the glyph, so the spinner takes\n * its place; the `aria-label` is what names the button either way.\n *\n * \"Icon-only\" means an icon `size`. A square button that grew a spinner\n * beside its glyph would not just look wrong, it would resize mid-request\n * and shove whatever sits next to it sideways.\n */\n loading?: boolean;\n}\n\n/** Sizes with no room for a spinner beside the glyph. */\nconst ICON_SIZES = new Set<string>([\"icon\", \"icon-sm\"]);\n\nexport const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(\n (\n {\n className,\n variant,\n size,\n asChild = false,\n loading,\n disabled,\n children,\n ...props\n },\n ref,\n ) => {\n const Comp = asChild ? Slot : \"button\";\n\n // `asChild` renders someone else's element, which may not take a spinner or\n // a disabled attribute — so the busy treatment is only applied to a real\n // button, and passing both is a mistake worth not papering over.\n if (asChild) {\n return (\n <Comp\n ref={ref}\n className={cn(buttonVariants({ variant, size }), className)}\n {...props}\n >\n {children}\n </Comp>\n );\n }\n\n return (\n <button\n ref={ref}\n disabled={disabled || loading}\n aria-busy={loading || undefined}\n className={cn(buttonVariants({ variant, size }), className)}\n {...props}\n >\n {loading ? <Spinner size=\"sm\" /> : null}\n {loading && size != null && ICON_SIZES.has(size) ? null : children}\n </button>\n );\n },\n);\n\nButton.displayName = \"Button\";\n"],"mappings":";;;;;;;;AAAA,YAAY,WAAW;AACvB,SAAS,WAA8B;AACvC,SAAS,YAAY;AAqFb,cAWF,YAXE;AAhFR,IAAM,iBAAiB;AAAA,EACrB;AAAA,EACA;AAAA,IACE,UAAU;AAAA,MACR,SAAS;AAAA,QACP,SAAS;AAAA,QACT,WAAW;AAAA,QACX,OAAO;AAAA,QACP,aACE;AAAA,QACF,SAAS;AAAA,MACX;AAAA,MACA,MAAM;AAAA,QACJ,IAAI;AAAA,QACJ,IAAI;AAAA,QACJ,IAAI;AAAA,QACJ,MAAM;AAAA;AAAA;AAAA,QAGN,WAAW;AAAA,MACb;AAAA,IACF;AAAA,IACA,iBAAiB;AAAA,MACf,SAAS;AAAA,MACT,MAAM;AAAA,IACR;AAAA,EACF;AACF;AA8BA,IAAM,aAAa,oBAAI,IAAY,CAAC,QAAQ,SAAS,CAAC;AAE/C,IAAM,SAAe;AAAA,EAC1B,CACE;AAAA,IACE;AAAA,IACA;AAAA,IACA;AAAA,IACA,UAAU;AAAA,IACV;AAAA,IACA;AAAA,IACA;AAAA,IACA,GAAG;AAAA,EACL,GACA,QACG;AACH,UAAM,OAAO,UAAU,OAAO;AAK9B,QAAI,SAAS;AACX,aACE;AAAA,QAAC;AAAA;AAAA,UACC;AAAA,UACA,WAAW,GAAG,eAAe,EAAE,SAAS,KAAK,CAAC,GAAG,SAAS;AAAA,UACzD,GAAG;AAAA,UAEH;AAAA;AAAA,MACH;AAAA,IAEJ;AAEA,WACE;AAAA,MAAC;AAAA;AAAA,QACC;AAAA,QACA,UAAU,YAAY;AAAA,QACtB,aAAW,WAAW;AAAA,QACtB,WAAW,GAAG,eAAe,EAAE,SAAS,KAAK,CAAC,GAAG,SAAS;AAAA,QACzD,GAAG;AAAA,QAEH;AAAA,oBAAU,oBAAC,WAAQ,MAAK,MAAK,IAAK;AAAA,UAClC,WAAW,QAAQ,QAAQ,WAAW,IAAI,IAAI,IAAI,OAAO;AAAA;AAAA;AAAA,IAC5D;AAAA,EAEJ;AACF;AAEA,OAAO,cAAc;","names":[]}
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
// src/spreadsheet-grid.tsx
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
4
|
+
function isInRange(range, r, c) {
|
|
5
|
+
if (!range) return false;
|
|
6
|
+
return r >= range.r1 && r <= range.r2 && c >= range.c1 && c <= range.c2;
|
|
7
|
+
}
|
|
8
|
+
function SpreadsheetGrid({
|
|
9
|
+
columns,
|
|
10
|
+
rows,
|
|
11
|
+
rowKey,
|
|
12
|
+
loading,
|
|
13
|
+
emptyMessage = "Nothing to show.",
|
|
14
|
+
loadingMessage = "Loading\u2026",
|
|
15
|
+
ariaLabel = "Data grid",
|
|
16
|
+
range,
|
|
17
|
+
onRangeChange,
|
|
18
|
+
onCopy
|
|
19
|
+
}) {
|
|
20
|
+
const gridRef = React.useRef(null);
|
|
21
|
+
const anchorRef = React.useRef(null);
|
|
22
|
+
const focusRef = React.useRef(null);
|
|
23
|
+
const [dragging, setDragging] = React.useState(false);
|
|
24
|
+
const setSelection = React.useCallback(
|
|
25
|
+
(anchor, focus) => {
|
|
26
|
+
anchorRef.current = anchor;
|
|
27
|
+
focusRef.current = focus;
|
|
28
|
+
onRangeChange({ anchor, focus });
|
|
29
|
+
},
|
|
30
|
+
[onRangeChange]
|
|
31
|
+
);
|
|
32
|
+
const selectCell = React.useCallback(
|
|
33
|
+
(r, c, extend) => {
|
|
34
|
+
if (extend && anchorRef.current)
|
|
35
|
+
setSelection(anchorRef.current, { r, c });
|
|
36
|
+
else setSelection({ r, c }, { r, c });
|
|
37
|
+
},
|
|
38
|
+
[setSelection]
|
|
39
|
+
);
|
|
40
|
+
const selectColumn = React.useCallback(
|
|
41
|
+
(c, extend) => {
|
|
42
|
+
const lastRow = Math.max(rows.length - 1, 0);
|
|
43
|
+
const anchorCol = extend && anchorRef.current ? anchorRef.current.c : c;
|
|
44
|
+
setSelection({ r: 0, c: anchorCol }, { r: lastRow, c });
|
|
45
|
+
},
|
|
46
|
+
[rows.length, setSelection]
|
|
47
|
+
);
|
|
48
|
+
const selectRow = React.useCallback(
|
|
49
|
+
(r, extend) => {
|
|
50
|
+
const lastCol = columns.length - 1;
|
|
51
|
+
const anchorRow = extend && anchorRef.current ? anchorRef.current.r : r;
|
|
52
|
+
setSelection({ r: anchorRow, c: 0 }, { r, c: lastCol });
|
|
53
|
+
},
|
|
54
|
+
[columns.length, setSelection]
|
|
55
|
+
);
|
|
56
|
+
const selectAll = React.useCallback(() => {
|
|
57
|
+
if (!rows.length || !columns.length) return;
|
|
58
|
+
setSelection({ r: 0, c: 0 }, { r: rows.length - 1, c: columns.length - 1 });
|
|
59
|
+
}, [rows.length, columns.length, setSelection]);
|
|
60
|
+
const scrollCellIntoView = React.useCallback((r, c) => {
|
|
61
|
+
const cell = gridRef.current?.querySelector(`[data-cell="${r}-${c}"]`);
|
|
62
|
+
cell?.scrollIntoView({ block: "nearest", inline: "nearest" });
|
|
63
|
+
}, []);
|
|
64
|
+
const moveFocus = React.useCallback(
|
|
65
|
+
(dr, dc, extend) => {
|
|
66
|
+
if (!rows.length || !columns.length) return;
|
|
67
|
+
const base = focusRef.current || { r: 0, c: 0 };
|
|
68
|
+
const r = Math.min(Math.max(base.r + dr, 0), rows.length - 1);
|
|
69
|
+
const c = Math.min(Math.max(base.c + dc, 0), columns.length - 1);
|
|
70
|
+
selectCell(r, c, extend);
|
|
71
|
+
scrollCellIntoView(r, c);
|
|
72
|
+
},
|
|
73
|
+
[rows.length, columns.length, selectCell, scrollCellIntoView]
|
|
74
|
+
);
|
|
75
|
+
React.useEffect(() => {
|
|
76
|
+
if (!dragging) return;
|
|
77
|
+
const stop = () => setDragging(false);
|
|
78
|
+
window.addEventListener("mouseup", stop);
|
|
79
|
+
return () => window.removeEventListener("mouseup", stop);
|
|
80
|
+
}, [dragging]);
|
|
81
|
+
const handleKeyDown = (event) => {
|
|
82
|
+
const meta = event.metaKey || event.ctrlKey;
|
|
83
|
+
if (meta && event.key.toLowerCase() === "c") {
|
|
84
|
+
event.preventDefault();
|
|
85
|
+
onCopy();
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
if (meta && event.key.toLowerCase() === "a") {
|
|
89
|
+
event.preventDefault();
|
|
90
|
+
selectAll();
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
switch (event.key) {
|
|
94
|
+
case "ArrowUp":
|
|
95
|
+
event.preventDefault();
|
|
96
|
+
moveFocus(-1, 0, event.shiftKey);
|
|
97
|
+
break;
|
|
98
|
+
case "ArrowDown":
|
|
99
|
+
event.preventDefault();
|
|
100
|
+
moveFocus(1, 0, event.shiftKey);
|
|
101
|
+
break;
|
|
102
|
+
case "ArrowLeft":
|
|
103
|
+
event.preventDefault();
|
|
104
|
+
moveFocus(0, -1, event.shiftKey);
|
|
105
|
+
break;
|
|
106
|
+
case "ArrowRight":
|
|
107
|
+
case "Tab":
|
|
108
|
+
event.preventDefault();
|
|
109
|
+
moveFocus(0, 1, event.shiftKey && event.key !== "Tab");
|
|
110
|
+
break;
|
|
111
|
+
case "Escape":
|
|
112
|
+
setSelection(null, null);
|
|
113
|
+
break;
|
|
114
|
+
default:
|
|
115
|
+
break;
|
|
116
|
+
}
|
|
117
|
+
};
|
|
118
|
+
return /* @__PURE__ */ jsx(
|
|
119
|
+
"div",
|
|
120
|
+
{
|
|
121
|
+
ref: gridRef,
|
|
122
|
+
tabIndex: 0,
|
|
123
|
+
role: "grid",
|
|
124
|
+
"aria-label": ariaLabel,
|
|
125
|
+
onKeyDown: handleKeyDown,
|
|
126
|
+
className: "border-border bg-background/80 overflow-x-auto rounded-lg border outline-none",
|
|
127
|
+
children: /* @__PURE__ */ jsxs("table", { className: "w-full text-sm whitespace-nowrap select-none", children: [
|
|
128
|
+
/* @__PURE__ */ jsx("thead", { className: "text-muted-foreground border-border border-b text-left", children: /* @__PURE__ */ jsxs("tr", { children: [
|
|
129
|
+
/* @__PURE__ */ jsx(
|
|
130
|
+
"th",
|
|
131
|
+
{
|
|
132
|
+
scope: "col",
|
|
133
|
+
className: "w-10 cursor-pointer px-2 py-2 text-center font-medium",
|
|
134
|
+
title: "Select everything",
|
|
135
|
+
onMouseDown: () => selectAll(),
|
|
136
|
+
children: "#"
|
|
137
|
+
}
|
|
138
|
+
),
|
|
139
|
+
columns.map((column, c) => /* @__PURE__ */ jsx(
|
|
140
|
+
"th",
|
|
141
|
+
{
|
|
142
|
+
scope: "col",
|
|
143
|
+
className: "cursor-pointer px-3 py-2 font-medium",
|
|
144
|
+
title: `Select the ${column.title} column`,
|
|
145
|
+
onMouseDown: (event) => selectColumn(c, event.shiftKey),
|
|
146
|
+
children: column.title
|
|
147
|
+
},
|
|
148
|
+
column.key
|
|
149
|
+
))
|
|
150
|
+
] }) }),
|
|
151
|
+
/* @__PURE__ */ jsx("tbody", { children: rows.length === 0 ? /* @__PURE__ */ jsx("tr", { children: /* @__PURE__ */ jsx(
|
|
152
|
+
"td",
|
|
153
|
+
{
|
|
154
|
+
colSpan: columns.length + 1,
|
|
155
|
+
className: "text-muted-foreground px-3 py-6 text-center",
|
|
156
|
+
children: loading ? loadingMessage : emptyMessage
|
|
157
|
+
}
|
|
158
|
+
) }) : rows.map((row, r) => /* @__PURE__ */ jsxs(
|
|
159
|
+
"tr",
|
|
160
|
+
{
|
|
161
|
+
className: "border-border border-b",
|
|
162
|
+
children: [
|
|
163
|
+
/* @__PURE__ */ jsx(
|
|
164
|
+
"td",
|
|
165
|
+
{
|
|
166
|
+
className: "text-muted-foreground w-10 cursor-pointer px-2 py-2 text-center text-xs",
|
|
167
|
+
title: "Select this row",
|
|
168
|
+
onMouseDown: (event) => selectRow(r, event.shiftKey),
|
|
169
|
+
children: r + 1
|
|
170
|
+
}
|
|
171
|
+
),
|
|
172
|
+
columns.map((column, c) => /* @__PURE__ */ jsx(
|
|
173
|
+
"td",
|
|
174
|
+
{
|
|
175
|
+
"data-cell": `${r}-${c}`,
|
|
176
|
+
onMouseDown: (event) => {
|
|
177
|
+
selectCell(r, c, event.shiftKey);
|
|
178
|
+
setDragging(true);
|
|
179
|
+
gridRef.current?.focus();
|
|
180
|
+
},
|
|
181
|
+
onMouseEnter: () => {
|
|
182
|
+
if (dragging) setSelection(anchorRef.current, { r, c });
|
|
183
|
+
},
|
|
184
|
+
className: `px-3 py-2 ${column.className || ""} ${isInRange(range, r, c) ? "bg-primary/15" : ""}`,
|
|
185
|
+
title: column.className?.includes("truncate") ? column.value(row) : void 0,
|
|
186
|
+
children: column.render ? column.render(row) : column.value(row)
|
|
187
|
+
},
|
|
188
|
+
column.key
|
|
189
|
+
))
|
|
190
|
+
]
|
|
191
|
+
},
|
|
192
|
+
rowKey ? rowKey(row, r) : String(r)
|
|
193
|
+
)) })
|
|
194
|
+
] })
|
|
195
|
+
}
|
|
196
|
+
);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
export {
|
|
200
|
+
SpreadsheetGrid
|
|
201
|
+
};
|
|
202
|
+
//# sourceMappingURL=chunk-MQT5RAQV.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/spreadsheet-grid.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\n\nimport { type CellRange, type CellRef } from \"./clipboard\";\n\n/**\n * A column knows three things: its heading, the text it contributes to a copy\n * or CSV export, and optionally how to draw itself.\n *\n * Keeping `value` and `render` together is what stops an exported sheet from\n * drifting from the visible grid.\n */\nexport type GridColumn<Row> = {\n key: string;\n title: string;\n /** Text used for clipboard and CSV. Never JSX. */\n value: (row: Row) => string;\n /** Defaults to `value`. Use for links, tiles, truncation. */\n render?: (row: Row) => React.ReactNode;\n /** Extra classes for the <td>. */\n className?: string;\n};\n\nexport interface SpreadsheetGridProps<Row> {\n columns: GridColumn<Row>[];\n rows: Row[];\n /** Stable React key per row. Defaults to the row index. */\n rowKey?: (row: Row, index: number) => string;\n loading?: boolean;\n emptyMessage?: string;\n loadingMessage?: string;\n ariaLabel?: string;\n /** Lifted so a toolbar can copy and describe the current selection. */\n range: CellRange | null;\n onRangeChange: (selection: {\n anchor: CellRef | null;\n focus: CellRef | null;\n }) => void;\n onCopy: () => void;\n}\n\nfunction isInRange(range: CellRange | null, r: number, c: number): boolean {\n if (!range) return false;\n return r >= range.r1 && r <= range.r2 && c >= range.c1 && c <= range.c2;\n}\n\n/**\n * Spreadsheet-like grid: click-drag a range, shift+click to extend, arrows to\n * move, click a heading to take the column, click the row number to take the\n * row, Cmd/Ctrl+A for everything, Escape to clear, Cmd/Ctrl+C to copy.\n *\n * Ported from video-shorts-panel's ExcelTable, which several panels share for\n * exactly this reason — a table people paste into a sheet.\n */\nexport function SpreadsheetGrid<Row>({\n columns,\n rows,\n rowKey,\n loading,\n emptyMessage = \"Nothing to show.\",\n loadingMessage = \"Loading…\",\n ariaLabel = \"Data grid\",\n range,\n onRangeChange,\n onCopy,\n}: SpreadsheetGridProps<Row>) {\n const gridRef = React.useRef<HTMLDivElement>(null);\n const anchorRef = React.useRef<CellRef | null>(null);\n const focusRef = React.useRef<CellRef | null>(null);\n const [dragging, setDragging] = React.useState(false);\n\n const setSelection = React.useCallback(\n (anchor: CellRef | null, focus: CellRef | null) => {\n anchorRef.current = anchor;\n focusRef.current = focus;\n onRangeChange({ anchor, focus });\n },\n [onRangeChange],\n );\n\n const selectCell = React.useCallback(\n (r: number, c: number, extend?: boolean) => {\n if (extend && anchorRef.current)\n setSelection(anchorRef.current, { r, c });\n else setSelection({ r, c }, { r, c });\n },\n [setSelection],\n );\n\n const selectColumn = React.useCallback(\n (c: number, extend?: boolean) => {\n const lastRow = Math.max(rows.length - 1, 0);\n const anchorCol = extend && anchorRef.current ? anchorRef.current.c : c;\n setSelection({ r: 0, c: anchorCol }, { r: lastRow, c });\n },\n [rows.length, setSelection],\n );\n\n const selectRow = React.useCallback(\n (r: number, extend?: boolean) => {\n const lastCol = columns.length - 1;\n const anchorRow = extend && anchorRef.current ? anchorRef.current.r : r;\n setSelection({ r: anchorRow, c: 0 }, { r, c: lastCol });\n },\n [columns.length, setSelection],\n );\n\n const selectAll = React.useCallback(() => {\n if (!rows.length || !columns.length) return;\n setSelection({ r: 0, c: 0 }, { r: rows.length - 1, c: columns.length - 1 });\n }, [rows.length, columns.length, setSelection]);\n\n const scrollCellIntoView = React.useCallback((r: number, c: number) => {\n const cell = gridRef.current?.querySelector(`[data-cell=\"${r}-${c}\"]`);\n cell?.scrollIntoView({ block: \"nearest\", inline: \"nearest\" });\n }, []);\n\n const moveFocus = React.useCallback(\n (dr: number, dc: number, extend?: boolean) => {\n if (!rows.length || !columns.length) return;\n const base = focusRef.current || { r: 0, c: 0 };\n const r = Math.min(Math.max(base.r + dr, 0), rows.length - 1);\n const c = Math.min(Math.max(base.c + dc, 0), columns.length - 1);\n selectCell(r, c, extend);\n scrollCellIntoView(r, c);\n },\n [rows.length, columns.length, selectCell, scrollCellIntoView],\n );\n\n // Dragging must end even if the pointer is released outside the grid.\n React.useEffect(() => {\n if (!dragging) return;\n const stop = () => setDragging(false);\n window.addEventListener(\"mouseup\", stop);\n return () => window.removeEventListener(\"mouseup\", stop);\n }, [dragging]);\n\n const handleKeyDown = (event: React.KeyboardEvent<HTMLDivElement>) => {\n const meta = event.metaKey || event.ctrlKey;\n if (meta && event.key.toLowerCase() === \"c\") {\n event.preventDefault();\n onCopy();\n return;\n }\n if (meta && event.key.toLowerCase() === \"a\") {\n event.preventDefault();\n selectAll();\n return;\n }\n switch (event.key) {\n case \"ArrowUp\":\n event.preventDefault();\n moveFocus(-1, 0, event.shiftKey);\n break;\n case \"ArrowDown\":\n event.preventDefault();\n moveFocus(1, 0, event.shiftKey);\n break;\n case \"ArrowLeft\":\n event.preventDefault();\n moveFocus(0, -1, event.shiftKey);\n break;\n case \"ArrowRight\":\n case \"Tab\":\n event.preventDefault();\n moveFocus(0, 1, event.shiftKey && event.key !== \"Tab\");\n break;\n case \"Escape\":\n setSelection(null, null);\n break;\n default:\n break;\n }\n };\n\n return (\n <div\n ref={gridRef}\n tabIndex={0}\n role=\"grid\"\n aria-label={ariaLabel}\n onKeyDown={handleKeyDown}\n className=\"border-border bg-background/80 overflow-x-auto rounded-lg border outline-none\"\n >\n <table className=\"w-full text-sm whitespace-nowrap select-none\">\n <thead className=\"text-muted-foreground border-border border-b text-left\">\n <tr>\n <th\n scope=\"col\"\n className=\"w-10 cursor-pointer px-2 py-2 text-center font-medium\"\n title=\"Select everything\"\n onMouseDown={() => selectAll()}\n >\n #\n </th>\n {columns.map((column, c) => (\n <th\n key={column.key}\n scope=\"col\"\n className=\"cursor-pointer px-3 py-2 font-medium\"\n title={`Select the ${column.title} column`}\n onMouseDown={(event) => selectColumn(c, event.shiftKey)}\n >\n {column.title}\n </th>\n ))}\n </tr>\n </thead>\n <tbody>\n {rows.length === 0 ? (\n <tr>\n <td\n colSpan={columns.length + 1}\n className=\"text-muted-foreground px-3 py-6 text-center\"\n >\n {loading ? loadingMessage : emptyMessage}\n </td>\n </tr>\n ) : (\n rows.map((row, r) => (\n <tr\n key={rowKey ? rowKey(row, r) : String(r)}\n className=\"border-border border-b\"\n >\n <td\n className=\"text-muted-foreground w-10 cursor-pointer px-2 py-2 text-center text-xs\"\n title=\"Select this row\"\n onMouseDown={(event) => selectRow(r, event.shiftKey)}\n >\n {r + 1}\n </td>\n {columns.map((column, c) => (\n <td\n key={column.key}\n data-cell={`${r}-${c}`}\n onMouseDown={(event) => {\n selectCell(r, c, event.shiftKey);\n setDragging(true);\n gridRef.current?.focus();\n }}\n onMouseEnter={() => {\n if (dragging) setSelection(anchorRef.current, { r, c });\n }}\n className={`px-3 py-2 ${column.className || \"\"} ${\n isInRange(range, r, c) ? \"bg-primary/15\" : \"\"\n }`}\n title={\n column.className?.includes(\"truncate\")\n ? column.value(row)\n : undefined\n }\n >\n {column.render ? column.render(row) : column.value(row)}\n </td>\n ))}\n </tr>\n ))\n )}\n </tbody>\n </table>\n </div>\n );\n}\n"],"mappings":";AAEA,YAAY,WAAW;AAyLb,SACE,KADF;AAjJV,SAAS,UAAU,OAAyB,GAAW,GAAoB;AACzE,MAAI,CAAC,MAAO,QAAO;AACnB,SAAO,KAAK,MAAM,MAAM,KAAK,MAAM,MAAM,KAAK,MAAM,MAAM,KAAK,MAAM;AACvE;AAUO,SAAS,gBAAqB;AAAA,EACnC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,eAAe;AAAA,EACf,iBAAiB;AAAA,EACjB,YAAY;AAAA,EACZ;AAAA,EACA;AAAA,EACA;AACF,GAA8B;AAC5B,QAAM,UAAgB,aAAuB,IAAI;AACjD,QAAM,YAAkB,aAAuB,IAAI;AACnD,QAAM,WAAiB,aAAuB,IAAI;AAClD,QAAM,CAAC,UAAU,WAAW,IAAU,eAAS,KAAK;AAEpD,QAAM,eAAqB;AAAA,IACzB,CAAC,QAAwB,UAA0B;AACjD,gBAAU,UAAU;AACpB,eAAS,UAAU;AACnB,oBAAc,EAAE,QAAQ,MAAM,CAAC;AAAA,IACjC;AAAA,IACA,CAAC,aAAa;AAAA,EAChB;AAEA,QAAM,aAAmB;AAAA,IACvB,CAAC,GAAW,GAAW,WAAqB;AAC1C,UAAI,UAAU,UAAU;AACtB,qBAAa,UAAU,SAAS,EAAE,GAAG,EAAE,CAAC;AAAA,UACrC,cAAa,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC;AAAA,IACtC;AAAA,IACA,CAAC,YAAY;AAAA,EACf;AAEA,QAAM,eAAqB;AAAA,IACzB,CAAC,GAAW,WAAqB;AAC/B,YAAM,UAAU,KAAK,IAAI,KAAK,SAAS,GAAG,CAAC;AAC3C,YAAM,YAAY,UAAU,UAAU,UAAU,UAAU,QAAQ,IAAI;AACtE,mBAAa,EAAE,GAAG,GAAG,GAAG,UAAU,GAAG,EAAE,GAAG,SAAS,EAAE,CAAC;AAAA,IACxD;AAAA,IACA,CAAC,KAAK,QAAQ,YAAY;AAAA,EAC5B;AAEA,QAAM,YAAkB;AAAA,IACtB,CAAC,GAAW,WAAqB;AAC/B,YAAM,UAAU,QAAQ,SAAS;AACjC,YAAM,YAAY,UAAU,UAAU,UAAU,UAAU,QAAQ,IAAI;AACtE,mBAAa,EAAE,GAAG,WAAW,GAAG,EAAE,GAAG,EAAE,GAAG,GAAG,QAAQ,CAAC;AAAA,IACxD;AAAA,IACA,CAAC,QAAQ,QAAQ,YAAY;AAAA,EAC/B;AAEA,QAAM,YAAkB,kBAAY,MAAM;AACxC,QAAI,CAAC,KAAK,UAAU,CAAC,QAAQ,OAAQ;AACrC,iBAAa,EAAE,GAAG,GAAG,GAAG,EAAE,GAAG,EAAE,GAAG,KAAK,SAAS,GAAG,GAAG,QAAQ,SAAS,EAAE,CAAC;AAAA,EAC5E,GAAG,CAAC,KAAK,QAAQ,QAAQ,QAAQ,YAAY,CAAC;AAE9C,QAAM,qBAA2B,kBAAY,CAAC,GAAW,MAAc;AACrE,UAAM,OAAO,QAAQ,SAAS,cAAc,eAAe,CAAC,IAAI,CAAC,IAAI;AACrE,UAAM,eAAe,EAAE,OAAO,WAAW,QAAQ,UAAU,CAAC;AAAA,EAC9D,GAAG,CAAC,CAAC;AAEL,QAAM,YAAkB;AAAA,IACtB,CAAC,IAAY,IAAY,WAAqB;AAC5C,UAAI,CAAC,KAAK,UAAU,CAAC,QAAQ,OAAQ;AACrC,YAAM,OAAO,SAAS,WAAW,EAAE,GAAG,GAAG,GAAG,EAAE;AAC9C,YAAM,IAAI,KAAK,IAAI,KAAK,IAAI,KAAK,IAAI,IAAI,CAAC,GAAG,KAAK,SAAS,CAAC;AAC5D,YAAM,IAAI,KAAK,IAAI,KAAK,IAAI,KAAK,IAAI,IAAI,CAAC,GAAG,QAAQ,SAAS,CAAC;AAC/D,iBAAW,GAAG,GAAG,MAAM;AACvB,yBAAmB,GAAG,CAAC;AAAA,IACzB;AAAA,IACA,CAAC,KAAK,QAAQ,QAAQ,QAAQ,YAAY,kBAAkB;AAAA,EAC9D;AAGA,EAAM,gBAAU,MAAM;AACpB,QAAI,CAAC,SAAU;AACf,UAAM,OAAO,MAAM,YAAY,KAAK;AACpC,WAAO,iBAAiB,WAAW,IAAI;AACvC,WAAO,MAAM,OAAO,oBAAoB,WAAW,IAAI;AAAA,EACzD,GAAG,CAAC,QAAQ,CAAC;AAEb,QAAM,gBAAgB,CAAC,UAA+C;AACpE,UAAM,OAAO,MAAM,WAAW,MAAM;AACpC,QAAI,QAAQ,MAAM,IAAI,YAAY,MAAM,KAAK;AAC3C,YAAM,eAAe;AACrB,aAAO;AACP;AAAA,IACF;AACA,QAAI,QAAQ,MAAM,IAAI,YAAY,MAAM,KAAK;AAC3C,YAAM,eAAe;AACrB,gBAAU;AACV;AAAA,IACF;AACA,YAAQ,MAAM,KAAK;AAAA,MACjB,KAAK;AACH,cAAM,eAAe;AACrB,kBAAU,IAAI,GAAG,MAAM,QAAQ;AAC/B;AAAA,MACF,KAAK;AACH,cAAM,eAAe;AACrB,kBAAU,GAAG,GAAG,MAAM,QAAQ;AAC9B;AAAA,MACF,KAAK;AACH,cAAM,eAAe;AACrB,kBAAU,GAAG,IAAI,MAAM,QAAQ;AAC/B;AAAA,MACF,KAAK;AAAA,MACL,KAAK;AACH,cAAM,eAAe;AACrB,kBAAU,GAAG,GAAG,MAAM,YAAY,MAAM,QAAQ,KAAK;AACrD;AAAA,MACF,KAAK;AACH,qBAAa,MAAM,IAAI;AACvB;AAAA,MACF;AACE;AAAA,IACJ;AAAA,EACF;AAEA,SACE;AAAA,IAAC;AAAA;AAAA,MACC,KAAK;AAAA,MACL,UAAU;AAAA,MACV,MAAK;AAAA,MACL,cAAY;AAAA,MACZ,WAAW;AAAA,MACX,WAAU;AAAA,MAEV,+BAAC,WAAM,WAAU,gDACf;AAAA,4BAAC,WAAM,WAAU,0DACf,+BAAC,QACC;AAAA;AAAA,YAAC;AAAA;AAAA,cACC,OAAM;AAAA,cACN,WAAU;AAAA,cACV,OAAM;AAAA,cACN,aAAa,MAAM,UAAU;AAAA,cAC9B;AAAA;AAAA,UAED;AAAA,UACC,QAAQ,IAAI,CAAC,QAAQ,MACpB;AAAA,YAAC;AAAA;AAAA,cAEC,OAAM;AAAA,cACN,WAAU;AAAA,cACV,OAAO,cAAc,OAAO,KAAK;AAAA,cACjC,aAAa,CAAC,UAAU,aAAa,GAAG,MAAM,QAAQ;AAAA,cAErD,iBAAO;AAAA;AAAA,YANH,OAAO;AAAA,UAOd,CACD;AAAA,WACH,GACF;AAAA,QACA,oBAAC,WACE,eAAK,WAAW,IACf,oBAAC,QACC;AAAA,UAAC;AAAA;AAAA,YACC,SAAS,QAAQ,SAAS;AAAA,YAC1B,WAAU;AAAA,YAET,oBAAU,iBAAiB;AAAA;AAAA,QAC9B,GACF,IAEA,KAAK,IAAI,CAAC,KAAK,MACb;AAAA,UAAC;AAAA;AAAA,YAEC,WAAU;AAAA,YAEV;AAAA;AAAA,gBAAC;AAAA;AAAA,kBACC,WAAU;AAAA,kBACV,OAAM;AAAA,kBACN,aAAa,CAAC,UAAU,UAAU,GAAG,MAAM,QAAQ;AAAA,kBAElD,cAAI;AAAA;AAAA,cACP;AAAA,cACC,QAAQ,IAAI,CAAC,QAAQ,MACpB;AAAA,gBAAC;AAAA;AAAA,kBAEC,aAAW,GAAG,CAAC,IAAI,CAAC;AAAA,kBACpB,aAAa,CAAC,UAAU;AACtB,+BAAW,GAAG,GAAG,MAAM,QAAQ;AAC/B,gCAAY,IAAI;AAChB,4BAAQ,SAAS,MAAM;AAAA,kBACzB;AAAA,kBACA,cAAc,MAAM;AAClB,wBAAI,SAAU,cAAa,UAAU,SAAS,EAAE,GAAG,EAAE,CAAC;AAAA,kBACxD;AAAA,kBACA,WAAW,aAAa,OAAO,aAAa,EAAE,IAC5C,UAAU,OAAO,GAAG,CAAC,IAAI,kBAAkB,EAC7C;AAAA,kBACA,OACE,OAAO,WAAW,SAAS,UAAU,IACjC,OAAO,MAAM,GAAG,IAChB;AAAA,kBAGL,iBAAO,SAAS,OAAO,OAAO,GAAG,IAAI,OAAO,MAAM,GAAG;AAAA;AAAA,gBAnBjD,OAAO;AAAA,cAoBd,CACD;AAAA;AAAA;AAAA,UAjCI,SAAS,OAAO,KAAK,CAAC,IAAI,OAAO,CAAC;AAAA,QAkCzC,CACD,GAEL;AAAA,SACF;AAAA;AAAA,EACF;AAEJ;","names":[]}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
// src/date-range-picker/dateRange.ts
|
|
2
|
+
var pad = (n) => String(n).padStart(2, "0");
|
|
3
|
+
function toIsoDate(date) {
|
|
4
|
+
return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}`;
|
|
5
|
+
}
|
|
6
|
+
function fromIsoDate(value) {
|
|
7
|
+
if (!value) return null;
|
|
8
|
+
const trimmed = value.trim();
|
|
9
|
+
const match = /^(\d{4})-(\d{2})-(\d{2})$/.exec(trimmed);
|
|
10
|
+
if (!match) return null;
|
|
11
|
+
const date = new Date(
|
|
12
|
+
Number(match[1]),
|
|
13
|
+
Number(match[2]) - 1,
|
|
14
|
+
Number(match[3])
|
|
15
|
+
);
|
|
16
|
+
return toIsoDate(date) === trimmed ? date : null;
|
|
17
|
+
}
|
|
18
|
+
function startOfMonth(date) {
|
|
19
|
+
return new Date(date.getFullYear(), date.getMonth(), 1);
|
|
20
|
+
}
|
|
21
|
+
function addMonths(date, count) {
|
|
22
|
+
return new Date(date.getFullYear(), date.getMonth() + count, 1);
|
|
23
|
+
}
|
|
24
|
+
function addDays(date, count) {
|
|
25
|
+
return new Date(date.getFullYear(), date.getMonth(), date.getDate() + count);
|
|
26
|
+
}
|
|
27
|
+
function isSameDay(a, b) {
|
|
28
|
+
if (!a || !b) return false;
|
|
29
|
+
return toIsoDate(a) === toIsoDate(b);
|
|
30
|
+
}
|
|
31
|
+
function monthGrid(month) {
|
|
32
|
+
const first = startOfMonth(month);
|
|
33
|
+
const start = addDays(first, -first.getDay());
|
|
34
|
+
return Array.from({ length: 42 }, (_, index) => addDays(start, index));
|
|
35
|
+
}
|
|
36
|
+
function monthLabel(month, locale = "en-IN") {
|
|
37
|
+
return month.toLocaleDateString(locale, { month: "long", year: "numeric" });
|
|
38
|
+
}
|
|
39
|
+
function formatDisplay(value, locale = "en-IN") {
|
|
40
|
+
const date = fromIsoDate(value);
|
|
41
|
+
if (!date) return "";
|
|
42
|
+
return date.toLocaleDateString(locale, {
|
|
43
|
+
day: "numeric",
|
|
44
|
+
month: "short",
|
|
45
|
+
year: "numeric"
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
function startOfDayMs(value) {
|
|
49
|
+
const date = fromIsoDate(value);
|
|
50
|
+
return date ? date.getTime() : 0;
|
|
51
|
+
}
|
|
52
|
+
function endOfDayMs(value) {
|
|
53
|
+
const date = fromIsoDate(value);
|
|
54
|
+
if (!date) return 0;
|
|
55
|
+
return new Date(
|
|
56
|
+
date.getFullYear(),
|
|
57
|
+
date.getMonth(),
|
|
58
|
+
date.getDate(),
|
|
59
|
+
23,
|
|
60
|
+
59,
|
|
61
|
+
59,
|
|
62
|
+
999
|
|
63
|
+
).getTime();
|
|
64
|
+
}
|
|
65
|
+
function endAt2359Ms(value) {
|
|
66
|
+
const date = fromIsoDate(value);
|
|
67
|
+
if (!date) return 0;
|
|
68
|
+
return new Date(
|
|
69
|
+
date.getFullYear(),
|
|
70
|
+
date.getMonth(),
|
|
71
|
+
date.getDate(),
|
|
72
|
+
23,
|
|
73
|
+
59,
|
|
74
|
+
59,
|
|
75
|
+
0
|
|
76
|
+
).getTime();
|
|
77
|
+
}
|
|
78
|
+
function createdRangeMs(from, to) {
|
|
79
|
+
const startDate = from.trim() || to.trim();
|
|
80
|
+
const endDate = to.trim() || from.trim();
|
|
81
|
+
return {
|
|
82
|
+
start_time: startOfDayMs(startDate),
|
|
83
|
+
end_time: endAt2359Ms(endDate)
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export {
|
|
88
|
+
toIsoDate,
|
|
89
|
+
fromIsoDate,
|
|
90
|
+
startOfMonth,
|
|
91
|
+
addMonths,
|
|
92
|
+
addDays,
|
|
93
|
+
isSameDay,
|
|
94
|
+
monthGrid,
|
|
95
|
+
monthLabel,
|
|
96
|
+
formatDisplay,
|
|
97
|
+
startOfDayMs,
|
|
98
|
+
endOfDayMs,
|
|
99
|
+
endAt2359Ms,
|
|
100
|
+
createdRangeMs
|
|
101
|
+
};
|
|
102
|
+
//# sourceMappingURL=chunk-MUPW2TFJ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/date-range-picker/dateRange.ts"],"sourcesContent":["/**\n * Date helpers for the range picker, all in local time.\n *\n * Deliberately not `toISOString()`: that converts to UTC first, so on any\n * negative-offset day it returns yesterday's date and the picker highlights the\n * wrong cell. Every value here is built and read as the local calendar day the\n * user is actually looking at.\n */\n\n/** `YYYY-MM-DD`, the value shape the filters store. */\nexport type IsoDate = string;\n\nconst pad = (n: number) => String(n).padStart(2, \"0\");\n\nexport function toIsoDate(date: Date): IsoDate {\n return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}`;\n}\n\n/** Parses `YYYY-MM-DD` into a local midnight Date; null for anything else. */\nexport function fromIsoDate(value: string | null | undefined): Date | null {\n if (!value) return null;\n const trimmed = value.trim();\n const match = /^(\\d{4})-(\\d{2})-(\\d{2})$/.exec(trimmed);\n if (!match) return null;\n const date = new Date(\n Number(match[1]),\n Number(match[2]) - 1,\n Number(match[3]),\n );\n // Rejects 2026-02-31, which would otherwise roll silently into March.\n return toIsoDate(date) === trimmed ? date : null;\n}\n\nexport function startOfMonth(date: Date): Date {\n return new Date(date.getFullYear(), date.getMonth(), 1);\n}\n\nexport function addMonths(date: Date, count: number): Date {\n return new Date(date.getFullYear(), date.getMonth() + count, 1);\n}\n\nexport function addDays(date: Date, count: number): Date {\n return new Date(date.getFullYear(), date.getMonth(), date.getDate() + count);\n}\n\nexport function isSameDay(a: Date | null, b: Date | null): boolean {\n if (!a || !b) return false;\n return toIsoDate(a) === toIsoDate(b);\n}\n\n/**\n * The six-week grid for a month, Sunday-first, padded with the neighbouring\n * months so every row has seven cells.\n *\n * Always six rows, never five: a grid that changes height as you page through\n * months makes everything below it jump.\n */\nexport function monthGrid(month: Date): Date[] {\n const first = startOfMonth(month);\n const start = addDays(first, -first.getDay());\n return Array.from({ length: 42 }, (_, index) => addDays(start, index));\n}\n\nexport function monthLabel(month: Date, locale = \"en-IN\"): string {\n return month.toLocaleDateString(locale, { month: \"long\", year: \"numeric\" });\n}\n\n/** Human form for the trigger — \"22 Aug 2026\". */\nexport function formatDisplay(value: string, locale = \"en-IN\"): string {\n const date = fromIsoDate(value);\n if (!date) return \"\";\n return date.toLocaleDateString(locale, {\n day: \"numeric\",\n month: \"short\",\n year: \"numeric\",\n });\n}\n\nexport function startOfDayMs(value: string): number {\n const date = fromIsoDate(value);\n return date ? date.getTime() : 0;\n}\n\n/**\n * The end of a day as milliseconds.\n *\n * A range picked as whole days has to include everything created on the last\n * day, so \"to\" means 23:59:59.999 rather than that morning's midnight — which\n * would drop a whole day of cards without saying so.\n */\nexport function endOfDayMs(value: string): number {\n const date = fromIsoDate(value);\n if (!date) return 0;\n return new Date(\n date.getFullYear(),\n date.getMonth(),\n date.getDate(),\n 23,\n 59,\n 59,\n 999,\n ).getTime();\n}\n\n/**\n * 11:59 PM on that local day, as 23:59:59.\n *\n * The next midnight is already the following date, and the feed includes that\n * whole day — so the 28th through the 28th came back with the 29th. Stopping\n * at 11:59 PM keeps the end on the day that was picked.\n */\nexport function endAt2359Ms(value: string): number {\n const date = fromIsoDate(value);\n if (!date) return 0;\n return new Date(\n date.getFullYear(),\n date.getMonth(),\n date.getDate(),\n 23,\n 59,\n 59,\n 0,\n ).getTime();\n}\n\n/**\n * Created-date window for the feed.\n *\n * A blank end means the start day, so one selected date is that whole day.\n * `end_time` is 11:59 PM on the end date.\n */\nexport function createdRangeMs(\n from: string,\n to: string,\n): { start_time: number; end_time: number } {\n const startDate = from.trim() || to.trim();\n const endDate = to.trim() || from.trim();\n return {\n start_time: startOfDayMs(startDate),\n end_time: endAt2359Ms(endDate),\n };\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import {
|
|
2
|
+
PanelBrandMark
|
|
3
|
+
} from "./chunk-JDE5XYPT.js";
|
|
4
|
+
import {
|
|
5
|
+
Button
|
|
6
|
+
} from "./chunk-KR3BCFP7.js";
|
|
7
|
+
import {
|
|
8
|
+
cn
|
|
9
|
+
} from "./chunk-HCA5ODCD.js";
|
|
10
|
+
|
|
11
|
+
// src/panel-sign-in.tsx
|
|
12
|
+
import { Loader2 } from "lucide-react";
|
|
13
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
14
|
+
function PanelSignIn({
|
|
15
|
+
appName,
|
|
16
|
+
eyebrow = "Secure Google sign-in",
|
|
17
|
+
description = "Use your work Google account to continue.",
|
|
18
|
+
features,
|
|
19
|
+
brandIcon,
|
|
20
|
+
error,
|
|
21
|
+
refused = false,
|
|
22
|
+
refusedTitle = "Access denied",
|
|
23
|
+
refusedMessage = "This Google account is signed in, but it is not provisioned for this panel.",
|
|
24
|
+
isSessionLoading = false,
|
|
25
|
+
isSigningIn = false,
|
|
26
|
+
onSignIn,
|
|
27
|
+
signInLabel = "Continue with Google",
|
|
28
|
+
className
|
|
29
|
+
}) {
|
|
30
|
+
const busy = isSessionLoading || isSigningIn;
|
|
31
|
+
return /* @__PURE__ */ jsx(
|
|
32
|
+
"main",
|
|
33
|
+
{
|
|
34
|
+
className: cn(
|
|
35
|
+
"bg-background text-foreground grid min-h-screen place-items-center p-4 sm:p-8",
|
|
36
|
+
className
|
|
37
|
+
),
|
|
38
|
+
children: /* @__PURE__ */ jsxs("section", { className: "border-border bg-card w-full max-w-md space-y-6 rounded-lg border p-8 shadow-sm", children: [
|
|
39
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
|
|
40
|
+
/* @__PURE__ */ jsx(PanelBrandMark, { children: brandIcon ?? /* @__PURE__ */ jsx("span", { className: "text-sm font-semibold", "aria-hidden": true, children: appName.slice(0, 1).toUpperCase() }) }),
|
|
41
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
|
|
42
|
+
/* @__PURE__ */ jsx("p", { className: "text-title truncate font-semibold", children: appName }),
|
|
43
|
+
/* @__PURE__ */ jsx("p", { className: "text-muted-foreground text-meta", children: eyebrow })
|
|
44
|
+
] })
|
|
45
|
+
] }),
|
|
46
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
47
|
+
/* @__PURE__ */ jsx("h1", { className: "text-display font-semibold tracking-tight", children: refused ? refusedTitle : "Sign in" }),
|
|
48
|
+
/* @__PURE__ */ jsx("p", { className: "text-muted-foreground text-body", children: refused ? refusedMessage : description })
|
|
49
|
+
] }),
|
|
50
|
+
features && features.length > 0 && !refused ? /* @__PURE__ */ jsx("ul", { className: "text-muted-foreground text-body space-y-1.5", children: features.slice(0, 3).map((feature) => /* @__PURE__ */ jsxs("li", { className: "flex gap-2", children: [
|
|
51
|
+
/* @__PURE__ */ jsx("span", { className: "text-foreground mt-1.5 h-1 w-1 shrink-0 rounded-full bg-current" }),
|
|
52
|
+
/* @__PURE__ */ jsx("span", { children: feature })
|
|
53
|
+
] }, feature)) }) : null,
|
|
54
|
+
error ? /* @__PURE__ */ jsx(
|
|
55
|
+
"p",
|
|
56
|
+
{
|
|
57
|
+
role: "alert",
|
|
58
|
+
className: "border-destructive/30 bg-destructive/5 text-destructive rounded-md border px-3 py-2 text-sm",
|
|
59
|
+
children: error
|
|
60
|
+
}
|
|
61
|
+
) : null,
|
|
62
|
+
/* @__PURE__ */ jsxs(
|
|
63
|
+
Button,
|
|
64
|
+
{
|
|
65
|
+
className: "w-full",
|
|
66
|
+
disabled: busy,
|
|
67
|
+
onClick: onSignIn,
|
|
68
|
+
"aria-busy": busy,
|
|
69
|
+
children: [
|
|
70
|
+
busy ? /* @__PURE__ */ jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin", "aria-hidden": true }) : null,
|
|
71
|
+
isSigningIn ? "Redirecting\u2026" : signInLabel
|
|
72
|
+
]
|
|
73
|
+
}
|
|
74
|
+
)
|
|
75
|
+
] })
|
|
76
|
+
}
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
function PanelSignInSkeleton({ className }) {
|
|
80
|
+
return /* @__PURE__ */ jsx(
|
|
81
|
+
"main",
|
|
82
|
+
{
|
|
83
|
+
className: cn(
|
|
84
|
+
"bg-background grid min-h-screen place-items-center p-4 sm:p-8",
|
|
85
|
+
className
|
|
86
|
+
),
|
|
87
|
+
children: /* @__PURE__ */ jsxs("div", { className: "border-border bg-card w-full max-w-md space-y-4 rounded-lg border p-8 shadow-sm", children: [
|
|
88
|
+
/* @__PURE__ */ jsx("div", { className: "bg-muted h-3 w-24 animate-pulse rounded" }),
|
|
89
|
+
/* @__PURE__ */ jsx("div", { className: "bg-muted h-8 w-48 animate-pulse rounded" }),
|
|
90
|
+
/* @__PURE__ */ jsx("div", { className: "bg-muted mt-7 h-10 w-full animate-pulse rounded-md" })
|
|
91
|
+
] })
|
|
92
|
+
}
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export {
|
|
97
|
+
PanelSignIn,
|
|
98
|
+
PanelSignInSkeleton
|
|
99
|
+
};
|
|
100
|
+
//# sourceMappingURL=chunk-PM2VE7DJ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/panel-sign-in.tsx"],"sourcesContent":["\"use client\";\n\nimport { Loader2 } from \"lucide-react\";\nimport * as React from \"react\";\n\nimport { Button } from \"./button\";\nimport { PanelBrandMark } from \"./panel-brand-mark\";\nimport { cn } from \"./utils\";\n\nexport type PanelSignInProps = {\n appName: string;\n /** Short line under the brand (e.g. \"Secure Google sign-in\"). */\n eyebrow?: string;\n /** One supporting sentence under the headline. */\n description?: string;\n /** Optional feature bullets — keep to ≤3; DESIGN.md: one job per surface. */\n features?: string[];\n brandIcon?: React.ReactNode;\n error?: string | null;\n /** Google signed in but panel access refused. */\n refused?: boolean;\n refusedTitle?: string;\n refusedMessage?: string;\n isSessionLoading?: boolean;\n isSigningIn?: boolean;\n onSignIn: () => void;\n signInLabel?: string;\n className?: string;\n};\n\n/**\n * DESIGN.md-compliant panel login: brand-first, ink neutrals, no purple glow.\n */\nexport function PanelSignIn({\n appName,\n eyebrow = \"Secure Google sign-in\",\n description = \"Use your work Google account to continue.\",\n features,\n brandIcon,\n error,\n refused = false,\n refusedTitle = \"Access denied\",\n refusedMessage = \"This Google account is signed in, but it is not provisioned for this panel.\",\n isSessionLoading = false,\n isSigningIn = false,\n onSignIn,\n signInLabel = \"Continue with Google\",\n className,\n}: PanelSignInProps) {\n const busy = isSessionLoading || isSigningIn;\n\n return (\n <main\n className={cn(\n \"bg-background text-foreground grid min-h-screen place-items-center p-4 sm:p-8\",\n className,\n )}\n >\n <section className=\"border-border bg-card w-full max-w-md space-y-6 rounded-lg border p-8 shadow-sm\">\n <div className=\"flex items-center gap-3\">\n <PanelBrandMark>\n {brandIcon ?? (\n <span className=\"text-sm font-semibold\" aria-hidden>\n {appName.slice(0, 1).toUpperCase()}\n </span>\n )}\n </PanelBrandMark>\n <div className=\"min-w-0\">\n <p className=\"text-title truncate font-semibold\">{appName}</p>\n <p className=\"text-muted-foreground text-meta\">{eyebrow}</p>\n </div>\n </div>\n\n <div className=\"space-y-2\">\n <h1 className=\"text-display font-semibold tracking-tight\">\n {refused ? refusedTitle : \"Sign in\"}\n </h1>\n <p className=\"text-muted-foreground text-body\">\n {refused ? refusedMessage : description}\n </p>\n </div>\n\n {features && features.length > 0 && !refused ? (\n <ul className=\"text-muted-foreground text-body space-y-1.5\">\n {features.slice(0, 3).map((feature) => (\n <li key={feature} className=\"flex gap-2\">\n <span className=\"text-foreground mt-1.5 h-1 w-1 shrink-0 rounded-full bg-current\" />\n <span>{feature}</span>\n </li>\n ))}\n </ul>\n ) : null}\n\n {error ? (\n <p\n role=\"alert\"\n className=\"border-destructive/30 bg-destructive/5 text-destructive rounded-md border px-3 py-2 text-sm\"\n >\n {error}\n </p>\n ) : null}\n\n <Button\n className=\"w-full\"\n disabled={busy}\n onClick={onSignIn}\n aria-busy={busy}\n >\n {busy ? (\n <Loader2 className=\"mr-2 h-4 w-4 animate-spin\" aria-hidden />\n ) : null}\n {isSigningIn ? \"Redirecting…\" : signInLabel}\n </Button>\n </section>\n </main>\n );\n}\n\nexport function PanelSignInSkeleton({ className }: { className?: string }) {\n return (\n <main\n className={cn(\n \"bg-background grid min-h-screen place-items-center p-4 sm:p-8\",\n className,\n )}\n >\n <div className=\"border-border bg-card w-full max-w-md space-y-4 rounded-lg border p-8 shadow-sm\">\n <div className=\"bg-muted h-3 w-24 animate-pulse rounded\" />\n <div className=\"bg-muted h-8 w-48 animate-pulse rounded\" />\n <div className=\"bg-muted mt-7 h-10 w-full animate-pulse rounded-md\" />\n </div>\n </main>\n );\n}\n"],"mappings":";;;;;;;;;;;AAEA,SAAS,eAAe;AA4DV,cAKJ,YALI;AA7BP,SAAS,YAAY;AAAA,EAC1B;AAAA,EACA,UAAU;AAAA,EACV,cAAc;AAAA,EACd;AAAA,EACA;AAAA,EACA;AAAA,EACA,UAAU;AAAA,EACV,eAAe;AAAA,EACf,iBAAiB;AAAA,EACjB,mBAAmB;AAAA,EACnB,cAAc;AAAA,EACd;AAAA,EACA,cAAc;AAAA,EACd;AACF,GAAqB;AACnB,QAAM,OAAO,oBAAoB;AAEjC,SACE;AAAA,IAAC;AAAA;AAAA,MACC,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,MAEA,+BAAC,aAAQ,WAAU,mFACjB;AAAA,6BAAC,SAAI,WAAU,2BACb;AAAA,8BAAC,kBACE,uBACC,oBAAC,UAAK,WAAU,yBAAwB,eAAW,MAChD,kBAAQ,MAAM,GAAG,CAAC,EAAE,YAAY,GACnC,GAEJ;AAAA,UACA,qBAAC,SAAI,WAAU,WACb;AAAA,gCAAC,OAAE,WAAU,qCAAqC,mBAAQ;AAAA,YAC1D,oBAAC,OAAE,WAAU,mCAAmC,mBAAQ;AAAA,aAC1D;AAAA,WACF;AAAA,QAEA,qBAAC,SAAI,WAAU,aACb;AAAA,8BAAC,QAAG,WAAU,6CACX,oBAAU,eAAe,WAC5B;AAAA,UACA,oBAAC,OAAE,WAAU,mCACV,oBAAU,iBAAiB,aAC9B;AAAA,WACF;AAAA,QAEC,YAAY,SAAS,SAAS,KAAK,CAAC,UACnC,oBAAC,QAAG,WAAU,+CACX,mBAAS,MAAM,GAAG,CAAC,EAAE,IAAI,CAAC,YACzB,qBAAC,QAAiB,WAAU,cAC1B;AAAA,8BAAC,UAAK,WAAU,mEAAkE;AAAA,UAClF,oBAAC,UAAM,mBAAQ;AAAA,aAFR,OAGT,CACD,GACH,IACE;AAAA,QAEH,QACC;AAAA,UAAC;AAAA;AAAA,YACC,MAAK;AAAA,YACL,WAAU;AAAA,YAET;AAAA;AAAA,QACH,IACE;AAAA,QAEJ;AAAA,UAAC;AAAA;AAAA,YACC,WAAU;AAAA,YACV,UAAU;AAAA,YACV,SAAS;AAAA,YACT,aAAW;AAAA,YAEV;AAAA,qBACC,oBAAC,WAAQ,WAAU,6BAA4B,eAAW,MAAC,IACzD;AAAA,cACH,cAAc,sBAAiB;AAAA;AAAA;AAAA,QAClC;AAAA,SACF;AAAA;AAAA,EACF;AAEJ;AAEO,SAAS,oBAAoB,EAAE,UAAU,GAA2B;AACzE,SACE;AAAA,IAAC;AAAA;AAAA,MACC,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,MAEA,+BAAC,SAAI,WAAU,mFACb;AAAA,4BAAC,SAAI,WAAU,2CAA0C;AAAA,QACzD,oBAAC,SAAI,WAAU,2CAA0C;AAAA,QACzD,oBAAC,SAAI,WAAU,sDAAqD;AAAA,SACtE;AAAA;AAAA,EACF;AAEJ;","names":[]}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import {
|
|
2
|
+
cn
|
|
3
|
+
} from "./chunk-HCA5ODCD.js";
|
|
4
|
+
import {
|
|
5
|
+
writeToClipboard
|
|
6
|
+
} from "./chunk-Y5WCT6XN.js";
|
|
7
|
+
|
|
8
|
+
// src/copy-button.tsx
|
|
9
|
+
import { Check, Copy } from "lucide-react";
|
|
10
|
+
import * as React from "react";
|
|
11
|
+
import { jsx } from "react/jsx-runtime";
|
|
12
|
+
function CopyButton({
|
|
13
|
+
value,
|
|
14
|
+
label,
|
|
15
|
+
className
|
|
16
|
+
}) {
|
|
17
|
+
const [copied, setCopied] = React.useState(false);
|
|
18
|
+
React.useEffect(() => {
|
|
19
|
+
if (!copied) return;
|
|
20
|
+
const timer = window.setTimeout(() => setCopied(false), 1500);
|
|
21
|
+
return () => window.clearTimeout(timer);
|
|
22
|
+
}, [copied]);
|
|
23
|
+
return /* @__PURE__ */ jsx(
|
|
24
|
+
"button",
|
|
25
|
+
{
|
|
26
|
+
type: "button",
|
|
27
|
+
"aria-label": copied ? `${label} copied` : `Copy ${label}`,
|
|
28
|
+
title: copied ? "Copied" : `Copy ${label}`,
|
|
29
|
+
className: cn(
|
|
30
|
+
"text-muted-foreground hover:text-foreground hover:bg-muted focus-visible:ring-ring shrink-0 cursor-pointer rounded p-1 transition focus-visible:ring-2 focus-visible:outline-none",
|
|
31
|
+
className
|
|
32
|
+
),
|
|
33
|
+
onClick: () => {
|
|
34
|
+
void writeToClipboard(value).then(() => setCopied(true)).catch(() => void 0);
|
|
35
|
+
},
|
|
36
|
+
children: copied ? /* @__PURE__ */ jsx(Check, { className: "size-3.5 text-success", "aria-hidden": true }) : /* @__PURE__ */ jsx(Copy, { className: "size-3.5", "aria-hidden": true })
|
|
37
|
+
}
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export {
|
|
42
|
+
CopyButton
|
|
43
|
+
};
|
|
44
|
+
//# sourceMappingURL=chunk-PPYR3O3L.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/copy-button.tsx"],"sourcesContent":["\"use client\";\n\nimport { Check, Copy } from \"lucide-react\";\nimport * as React from \"react\";\n\nimport { writeToClipboard } from \"./clipboard\";\nimport { cn } from \"./utils\";\n\n/**\n * Copies one short string — an id, a title, a URL — and says so for a moment.\n *\n * The panel had this three times over, each with its own timeout and its own\n * silent-failure story. `writeToClipboard` is used rather than\n * `navigator.clipboard` directly because the latter is undefined on http\n * origins, which is exactly where this panel runs in local dev.\n */\nexport function CopyButton({\n value,\n label,\n className,\n}: {\n value: string;\n /** What is being copied, for the accessible name: \"card id\", \"title\". */\n label: string;\n className?: string;\n}) {\n const [copied, setCopied] = React.useState(false);\n\n React.useEffect(() => {\n if (!copied) return;\n const timer = window.setTimeout(() => setCopied(false), 1500);\n return () => window.clearTimeout(timer);\n }, [copied]);\n\n return (\n <button\n type=\"button\"\n aria-label={copied ? `${label} copied` : `Copy ${label}`}\n title={copied ? \"Copied\" : `Copy ${label}`}\n className={cn(\n \"text-muted-foreground hover:text-foreground hover:bg-muted focus-visible:ring-ring shrink-0 cursor-pointer rounded p-1 transition focus-visible:ring-2 focus-visible:outline-none\",\n className,\n )}\n onClick={() => {\n // A denied clipboard permission should not throw into the row.\n void writeToClipboard(value)\n .then(() => setCopied(true))\n .catch(() => undefined);\n }}\n >\n {copied ? (\n <Check className=\"size-3.5 text-success\" aria-hidden />\n ) : (\n <Copy className=\"size-3.5\" aria-hidden />\n )}\n </button>\n );\n}\n"],"mappings":";;;;;;;;AAEA,SAAS,OAAO,YAAY;AAC5B,YAAY,WAAW;AAgDf;AAnCD,SAAS,WAAW;AAAA,EACzB;AAAA,EACA;AAAA,EACA;AACF,GAKG;AACD,QAAM,CAAC,QAAQ,SAAS,IAAU,eAAS,KAAK;AAEhD,EAAM,gBAAU,MAAM;AACpB,QAAI,CAAC,OAAQ;AACb,UAAM,QAAQ,OAAO,WAAW,MAAM,UAAU,KAAK,GAAG,IAAI;AAC5D,WAAO,MAAM,OAAO,aAAa,KAAK;AAAA,EACxC,GAAG,CAAC,MAAM,CAAC;AAEX,SACE;AAAA,IAAC;AAAA;AAAA,MACC,MAAK;AAAA,MACL,cAAY,SAAS,GAAG,KAAK,YAAY,QAAQ,KAAK;AAAA,MACtD,OAAO,SAAS,WAAW,QAAQ,KAAK;AAAA,MACxC,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,MACA,SAAS,MAAM;AAEb,aAAK,iBAAiB,KAAK,EACxB,KAAK,MAAM,UAAU,IAAI,CAAC,EAC1B,MAAM,MAAM,MAAS;AAAA,MAC1B;AAAA,MAEC,mBACC,oBAAC,SAAM,WAAU,yBAAwB,eAAW,MAAC,IAErD,oBAAC,QAAK,WAAU,YAAW,eAAW,MAAC;AAAA;AAAA,EAE3C;AAEJ;","names":[]}
|