@blurrow/ui 0.1.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +60 -20
- package/dist/Accordion.d.ts +17 -0
- package/dist/Accordion.js +138 -0
- package/dist/Accordion.js.map +1 -0
- package/dist/Alert.d.ts +13 -0
- package/dist/Alert.js +69 -0
- package/dist/Alert.js.map +1 -0
- package/dist/AuthorCard.d.ts +12 -0
- package/dist/AuthorCard.js +57 -0
- package/dist/AuthorCard.js.map +1 -0
- package/dist/Avatar.d.ts +21 -0
- package/dist/Avatar.js +96 -0
- package/dist/Avatar.js.map +1 -0
- package/dist/Badge.d.ts +12 -0
- package/dist/Badge.js +46 -0
- package/dist/Badge.js.map +1 -0
- package/dist/Banner.d.ts +15 -0
- package/dist/Banner.js +68 -0
- package/dist/Banner.js.map +1 -0
- package/dist/Breadcrumb.d.ts +13 -0
- package/dist/Breadcrumb.js +45 -0
- package/dist/Breadcrumb.js.map +1 -0
- package/dist/Button.d.ts +19 -0
- package/dist/Button.js +282 -0
- package/dist/Button.js.map +1 -0
- package/dist/ButtonGroup.d.ts +19 -0
- package/dist/ButtonGroup.js +62 -0
- package/dist/ButtonGroup.js.map +1 -0
- package/dist/Card.d.ts +40 -0
- package/dist/Card.js +175 -0
- package/dist/Card.js.map +1 -0
- package/dist/CardShell.d.ts +18 -0
- package/dist/CardShell.js +58 -0
- package/dist/CardShell.js.map +1 -0
- package/dist/Carousel.d.ts +22 -0
- package/dist/Carousel.js +247 -0
- package/dist/Carousel.js.map +1 -0
- package/dist/Checkbox.d.ts +15 -0
- package/dist/Checkbox.js +84 -0
- package/dist/Checkbox.js.map +1 -0
- package/dist/CloseButton.d.ts +12 -0
- package/dist/CloseButton.js +63 -0
- package/dist/CloseButton.js.map +1 -0
- package/dist/CodeBlock.d.ts +12 -0
- package/dist/CodeBlock.js +221 -0
- package/dist/CodeBlock.js.map +1 -0
- package/dist/ColorPicker.d.ts +24 -0
- package/dist/ColorPicker.js +366 -0
- package/dist/ColorPicker.js.map +1 -0
- package/dist/Combobox.d.ts +34 -0
- package/dist/Combobox.js +255 -0
- package/dist/Combobox.js.map +1 -0
- package/dist/CommandPalette.d.ts +25 -0
- package/dist/CommandPalette.js +304 -0
- package/dist/CommandPalette.js.map +1 -0
- package/dist/ContextMenu.d.ts +16 -0
- package/dist/ContextMenu.js +209 -0
- package/dist/ContextMenu.js.map +1 -0
- package/dist/DataList.d.ts +15 -0
- package/dist/DataList.js +42 -0
- package/dist/DataList.js.map +1 -0
- package/dist/DatePicker.d.ts +65 -0
- package/dist/DatePicker.js +391 -0
- package/dist/DatePicker.js.map +1 -0
- package/dist/DateRangePicker.d.ts +34 -0
- package/dist/DateRangePicker.js +180 -0
- package/dist/DateRangePicker.js.map +1 -0
- package/dist/Drawer.d.ts +17 -0
- package/dist/Drawer.js +132 -0
- package/dist/Drawer.js.map +1 -0
- package/dist/DropdownMenu.d.ts +23 -0
- package/dist/DropdownMenu.js +185 -0
- package/dist/DropdownMenu.js.map +1 -0
- package/dist/EmptyState.d.ts +13 -0
- package/dist/EmptyState.js +55 -0
- package/dist/EmptyState.js.map +1 -0
- package/dist/FieldMessage.d.ts +26 -0
- package/dist/FieldMessage.js +95 -0
- package/dist/FieldMessage.js.map +1 -0
- package/dist/FileUpload.d.ts +32 -0
- package/dist/FileUpload.js +253 -0
- package/dist/FileUpload.js.map +1 -0
- package/dist/Floating.d.ts +32 -0
- package/dist/Floating.js +95 -0
- package/dist/Floating.js.map +1 -0
- package/dist/HoverCard.d.ts +18 -0
- package/dist/HoverCard.js +90 -0
- package/dist/HoverCard.js.map +1 -0
- package/dist/Input.d.ts +17 -0
- package/dist/Input.js +96 -0
- package/dist/Input.js.map +1 -0
- package/dist/Kbd.d.ts +9 -0
- package/dist/Kbd.js +24 -0
- package/dist/Kbd.js.map +1 -0
- package/dist/Modal.d.ts +15 -0
- package/dist/Modal.js +109 -0
- package/dist/Modal.js.map +1 -0
- package/dist/NumberInput.d.ts +23 -0
- package/dist/NumberInput.js +162 -0
- package/dist/NumberInput.js.map +1 -0
- package/dist/OtpInput.d.ts +26 -0
- package/dist/OtpInput.js +173 -0
- package/dist/OtpInput.js.map +1 -0
- package/dist/Pagination.d.ts +11 -0
- package/dist/Pagination.js +148 -0
- package/dist/Pagination.js.map +1 -0
- package/dist/Popover.d.ts +13 -0
- package/dist/Popover.js +98 -0
- package/dist/Popover.js.map +1 -0
- package/dist/Progress.d.ts +12 -0
- package/dist/Progress.js +61 -0
- package/dist/Progress.js.map +1 -0
- package/dist/QuoteCard.d.ts +13 -0
- package/dist/QuoteCard.js +92 -0
- package/dist/QuoteCard.js.map +1 -0
- package/dist/Radio.d.ts +19 -0
- package/dist/Radio.js +161 -0
- package/dist/Radio.js.map +1 -0
- package/dist/RadioCards.d.ts +27 -0
- package/dist/RadioCards.js +205 -0
- package/dist/RadioCards.js.map +1 -0
- package/dist/ReactionBar.d.ts +15 -0
- package/dist/ReactionBar.js +92 -0
- package/dist/ReactionBar.js.map +1 -0
- package/dist/ReadingProgress.d.ts +8 -0
- package/dist/ReadingProgress.js +51 -0
- package/dist/ReadingProgress.js.map +1 -0
- package/dist/ReadingTime.d.ts +9 -0
- package/dist/ReadingTime.js +39 -0
- package/dist/ReadingTime.js.map +1 -0
- package/dist/Select.d.ts +20 -0
- package/dist/Select.js +244 -0
- package/dist/Select.js.map +1 -0
- package/dist/Separator.d.ts +14 -0
- package/dist/Separator.js +33 -0
- package/dist/Separator.js.map +1 -0
- package/dist/ShareButtons.d.ts +10 -0
- package/dist/ShareButtons.js +107 -0
- package/dist/ShareButtons.js.map +1 -0
- package/dist/Sidebar.d.ts +30 -0
- package/dist/Sidebar.js +140 -0
- package/dist/Sidebar.js.map +1 -0
- package/dist/Skeleton.d.ts +15 -0
- package/dist/Skeleton.js +67 -0
- package/dist/Skeleton.js.map +1 -0
- package/dist/Slider.d.ts +27 -0
- package/dist/Slider.js +182 -0
- package/dist/Slider.js.map +1 -0
- package/dist/Spinner.d.ts +12 -0
- package/dist/Spinner.js +24 -0
- package/dist/Spinner.js.map +1 -0
- package/dist/SpotlightCard.d.ts +17 -0
- package/dist/SpotlightCard.js +87 -0
- package/dist/SpotlightCard.js.map +1 -0
- package/dist/Stat.d.ts +19 -0
- package/dist/Stat.js +71 -0
- package/dist/Stat.js.map +1 -0
- package/dist/StatCard.d.ts +24 -0
- package/dist/StatCard.js +212 -0
- package/dist/StatCard.js.map +1 -0
- package/dist/Steps.d.ts +14 -0
- package/dist/Steps.js +94 -0
- package/dist/Steps.js.map +1 -0
- package/dist/Table.d.ts +21 -0
- package/dist/Table.js +93 -0
- package/dist/Table.js.map +1 -0
- package/dist/TableOfContents.d.ts +17 -0
- package/dist/TableOfContents.js +92 -0
- package/dist/TableOfContents.js.map +1 -0
- package/dist/Tabs.d.ts +17 -0
- package/dist/Tabs.js +123 -0
- package/dist/Tabs.js.map +1 -0
- package/dist/TagInput.d.ts +16 -0
- package/dist/TagInput.js +169 -0
- package/dist/TagInput.js.map +1 -0
- package/dist/Textarea.d.ts +13 -0
- package/dist/Textarea.js +74 -0
- package/dist/Textarea.js.map +1 -0
- package/dist/TimePicker.d.ts +28 -0
- package/dist/TimePicker.js +264 -0
- package/dist/TimePicker.js.map +1 -0
- package/dist/Timeline.d.ts +19 -0
- package/dist/Timeline.js +76 -0
- package/dist/Timeline.js.map +1 -0
- package/dist/Toast.d.ts +18 -0
- package/dist/Toast.js +318 -0
- package/dist/Toast.js.map +1 -0
- package/dist/Toggle.d.ts +13 -0
- package/dist/Toggle.js +84 -0
- package/dist/Toggle.js.map +1 -0
- package/dist/Tooltip.d.ts +13 -0
- package/dist/Tooltip.js +138 -0
- package/dist/Tooltip.js.map +1 -0
- package/dist/cli.js +1644 -0
- package/dist/index.d.ts +68 -631
- package/dist/index.js +67 -5558
- package/dist/index.js.map +1 -1
- package/dist/registry/Accordion.tsx +140 -0
- package/dist/registry/Alert.tsx +95 -0
- package/dist/registry/AuthorCard.tsx +67 -0
- package/dist/registry/Avatar.tsx +116 -0
- package/dist/registry/Badge.tsx +55 -0
- package/dist/registry/Banner.tsx +86 -0
- package/dist/registry/Breadcrumb.tsx +60 -0
- package/dist/registry/Button.tsx +361 -0
- package/dist/registry/ButtonGroup.tsx +82 -0
- package/dist/registry/Card.tsx +241 -0
- package/dist/registry/CardShell.tsx +78 -0
- package/dist/registry/Carousel.tsx +296 -0
- package/dist/registry/Checkbox.tsx +104 -0
- package/dist/registry/CloseButton.tsx +68 -0
- package/dist/registry/CodeBlock.tsx +202 -0
- package/dist/registry/ColorPicker.tsx +404 -0
- package/dist/registry/Combobox.tsx +318 -0
- package/dist/registry/CommandPalette.tsx +356 -0
- package/dist/registry/ContextMenu.tsx +241 -0
- package/dist/registry/DataList.tsx +49 -0
- package/dist/registry/DatePicker.tsx +494 -0
- package/dist/registry/DateRangePicker.tsx +232 -0
- package/dist/registry/Drawer.tsx +166 -0
- package/dist/registry/DropdownMenu.tsx +219 -0
- package/dist/registry/EmptyState.tsx +55 -0
- package/dist/registry/FieldMessage.tsx +123 -0
- package/dist/registry/FileUpload.tsx +318 -0
- package/dist/registry/Floating.tsx +134 -0
- package/dist/registry/HoverCard.tsx +113 -0
- package/dist/registry/Input.tsx +108 -0
- package/dist/registry/Kbd.tsx +23 -0
- package/dist/registry/Modal.tsx +130 -0
- package/dist/registry/NumberInput.tsx +183 -0
- package/dist/registry/OtpInput.tsx +219 -0
- package/dist/registry/Pagination.tsx +160 -0
- package/dist/registry/Popover.tsx +116 -0
- package/dist/registry/Progress.tsx +66 -0
- package/dist/registry/QuoteCard.tsx +93 -0
- package/dist/registry/Radio.tsx +205 -0
- package/dist/registry/RadioCards.tsx +247 -0
- package/dist/registry/ReactionBar.tsx +101 -0
- package/dist/registry/ReadingProgress.tsx +51 -0
- package/dist/registry/ReadingTime.tsx +35 -0
- package/dist/registry/Select.tsx +265 -0
- package/dist/registry/Separator.tsx +52 -0
- package/dist/registry/ShareButtons.tsx +134 -0
- package/dist/registry/Sidebar.tsx +182 -0
- package/dist/registry/Skeleton.tsx +83 -0
- package/dist/registry/Slider.tsx +229 -0
- package/dist/registry/Spinner.tsx +32 -0
- package/dist/registry/SpotlightCard.tsx +106 -0
- package/dist/registry/Stat.tsx +82 -0
- package/dist/registry/StatCard.tsx +256 -0
- package/dist/registry/Steps.tsx +109 -0
- package/dist/registry/Table.tsx +123 -0
- package/dist/registry/TableOfContents.tsx +116 -0
- package/dist/registry/Tabs.tsx +136 -0
- package/dist/registry/TagInput.tsx +191 -0
- package/dist/registry/Textarea.tsx +84 -0
- package/dist/registry/TimePicker.tsx +315 -0
- package/dist/registry/Timeline.tsx +102 -0
- package/dist/registry/Toast.tsx +388 -0
- package/dist/registry/Toggle.tsx +96 -0
- package/dist/registry/Tooltip.tsx +160 -0
- package/dist/registry/styles.css +1207 -0
- package/dist/registry/useFloatingPanel.ts +112 -0
- package/dist/registry/useModalBehavior.ts +77 -0
- package/dist/styles.css +868 -62
- package/dist/useFloatingPanel.d.ts +24 -0
- package/dist/useFloatingPanel.js +89 -0
- package/dist/useFloatingPanel.js.map +1 -0
- package/dist/useModalBehavior.d.ts +15 -0
- package/dist/useModalBehavior.js +57 -0
- package/dist/useModalBehavior.js.map +1 -0
- package/package.json +23 -6
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
// ../../components/ui/ShareButtons.tsx
|
|
4
|
+
import { useState, useCallback } from "react";
|
|
5
|
+
import { ButtonGroup } from "./ButtonGroup.js";
|
|
6
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
7
|
+
function LinkIcon() {
|
|
8
|
+
return /* @__PURE__ */ jsxs(
|
|
9
|
+
"svg",
|
|
10
|
+
{
|
|
11
|
+
width: "14",
|
|
12
|
+
height: "14",
|
|
13
|
+
viewBox: "0 0 24 24",
|
|
14
|
+
fill: "none",
|
|
15
|
+
stroke: "currentColor",
|
|
16
|
+
strokeWidth: "1.75",
|
|
17
|
+
strokeLinecap: "round",
|
|
18
|
+
strokeLinejoin: "round",
|
|
19
|
+
children: [
|
|
20
|
+
/* @__PURE__ */ jsx("path", { d: "M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" }),
|
|
21
|
+
/* @__PURE__ */ jsx("path", { d: "M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" })
|
|
22
|
+
]
|
|
23
|
+
}
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
function CheckIcon() {
|
|
27
|
+
return /* @__PURE__ */ jsx(
|
|
28
|
+
"svg",
|
|
29
|
+
{
|
|
30
|
+
width: "14",
|
|
31
|
+
height: "14",
|
|
32
|
+
viewBox: "0 0 24 24",
|
|
33
|
+
fill: "none",
|
|
34
|
+
stroke: "currentColor",
|
|
35
|
+
strokeWidth: "2",
|
|
36
|
+
strokeLinecap: "round",
|
|
37
|
+
strokeLinejoin: "round",
|
|
38
|
+
children: /* @__PURE__ */ jsx("polyline", { points: "20 6 9 17 4 12" })
|
|
39
|
+
}
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
function XIcon() {
|
|
43
|
+
return /* @__PURE__ */ jsx("svg", { width: "13", height: "13", fill: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx("path", { d: "M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z" }) });
|
|
44
|
+
}
|
|
45
|
+
function LinkedInIcon() {
|
|
46
|
+
return /* @__PURE__ */ jsx("svg", { width: "13", height: "13", fill: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx("path", { d: "M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433a2.062 2.062 0 01-2.063-2.065 2.063 2.063 0 112.063 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003z" }) });
|
|
47
|
+
}
|
|
48
|
+
function ShareButtons({
|
|
49
|
+
url,
|
|
50
|
+
title,
|
|
51
|
+
className = ""
|
|
52
|
+
}) {
|
|
53
|
+
const [copied, setCopied] = useState(false);
|
|
54
|
+
const shareUrl = url ?? (typeof window !== "undefined" ? window.location.href : "");
|
|
55
|
+
const shareTitle = title ?? "";
|
|
56
|
+
const handleCopy = useCallback(async () => {
|
|
57
|
+
try {
|
|
58
|
+
await navigator.clipboard.writeText(shareUrl);
|
|
59
|
+
setCopied(true);
|
|
60
|
+
setTimeout(() => setCopied(false), 2e3);
|
|
61
|
+
} catch {
|
|
62
|
+
}
|
|
63
|
+
}, [shareUrl]);
|
|
64
|
+
const handleTwitter = useCallback(() => {
|
|
65
|
+
const params = new URLSearchParams({
|
|
66
|
+
text: shareTitle,
|
|
67
|
+
url: shareUrl
|
|
68
|
+
});
|
|
69
|
+
window.open(
|
|
70
|
+
`https://x.com/intent/tweet?${params.toString()}`,
|
|
71
|
+
"_blank",
|
|
72
|
+
"noopener,noreferrer"
|
|
73
|
+
);
|
|
74
|
+
}, [shareUrl, shareTitle]);
|
|
75
|
+
const handleLinkedIn = useCallback(() => {
|
|
76
|
+
const params = new URLSearchParams({
|
|
77
|
+
url: shareUrl
|
|
78
|
+
});
|
|
79
|
+
window.open(
|
|
80
|
+
`https://www.linkedin.com/sharing/share-offsite/?${params.toString()}`,
|
|
81
|
+
"_blank",
|
|
82
|
+
"noopener,noreferrer"
|
|
83
|
+
);
|
|
84
|
+
}, [shareUrl]);
|
|
85
|
+
const keys = [
|
|
86
|
+
{
|
|
87
|
+
label: copied ? "Link copied" : "Copy link",
|
|
88
|
+
onClick: handleCopy,
|
|
89
|
+
content: /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
90
|
+
/* @__PURE__ */ jsx("span", { className: ["animate-checkbox-in", copied ? "text-success-soft" : ""].join(" "), children: copied ? /* @__PURE__ */ jsx(CheckIcon, {}) : /* @__PURE__ */ jsx(LinkIcon, {}) }, String(copied)),
|
|
91
|
+
/* @__PURE__ */ jsx("span", { className: "w-[62px] text-left", children: copied ? "copied" : "copy link" })
|
|
92
|
+
] }),
|
|
93
|
+
wide: true
|
|
94
|
+
},
|
|
95
|
+
{ label: "Share on X", onClick: handleTwitter, content: /* @__PURE__ */ jsx(XIcon, {}), wide: false },
|
|
96
|
+
{ label: "Share on LinkedIn", onClick: handleLinkedIn, content: /* @__PURE__ */ jsx(LinkedInIcon, {}), wide: false }
|
|
97
|
+
];
|
|
98
|
+
return /* @__PURE__ */ jsxs("div", { className, children: [
|
|
99
|
+
/* @__PURE__ */ jsx("p", { className: "text-[12px] font-medium text-ink-muted lowercase mb-3", children: "Share" }),
|
|
100
|
+
/* @__PURE__ */ jsx(ButtonGroup, { label: "Share", items: keys }),
|
|
101
|
+
/* @__PURE__ */ jsx("span", { "aria-live": "polite", className: "sr-only", children: copied ? "Link copied to clipboard" : "" })
|
|
102
|
+
] });
|
|
103
|
+
}
|
|
104
|
+
export {
|
|
105
|
+
ShareButtons
|
|
106
|
+
};
|
|
107
|
+
//# sourceMappingURL=ShareButtons.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../components/ui/ShareButtons.tsx"],"sourcesContent":["\"use client\";\n\nimport React, { useState, useCallback } from \"react\";\nimport { ButtonGroup } from \"./ButtonGroup\";\n\ninterface ShareButtonsProps {\n url?: string;\n title?: string;\n className?: string;\n}\n\n\nfunction LinkIcon() {\n return (\n <svg\n width=\"14\"\n height=\"14\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"1.75\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n >\n <path d=\"M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71\" />\n <path d=\"M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71\" />\n </svg>\n );\n}\n\nfunction CheckIcon() {\n return (\n <svg\n width=\"14\"\n height=\"14\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n >\n <polyline points=\"20 6 9 17 4 12\" />\n </svg>\n );\n}\n\nfunction XIcon() {\n return (\n <svg width=\"13\" height=\"13\" fill=\"currentColor\" viewBox=\"0 0 24 24\">\n <path d=\"M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z\" />\n </svg>\n );\n}\n\nfunction LinkedInIcon() {\n return (\n <svg width=\"13\" height=\"13\" fill=\"currentColor\" viewBox=\"0 0 24 24\">\n <path d=\"M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433a2.062 2.062 0 01-2.063-2.065 2.063 2.063 0 112.063 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003z\" />\n </svg>\n );\n}\n\nexport function ShareButtons({\n url,\n title,\n className = \"\",\n}: ShareButtonsProps) {\n const [copied, setCopied] = useState(false);\n\n const shareUrl = url ?? (typeof window !== \"undefined\" ? window.location.href : \"\");\n const shareTitle = title ?? \"\";\n\n const handleCopy = useCallback(async () => {\n try {\n await navigator.clipboard.writeText(shareUrl);\n setCopied(true);\n setTimeout(() => setCopied(false), 2000);\n } catch {\n // Fallback silently\n }\n }, [shareUrl]);\n\n const handleTwitter = useCallback(() => {\n const params = new URLSearchParams({\n text: shareTitle,\n url: shareUrl,\n });\n window.open(\n `https://x.com/intent/tweet?${params.toString()}`,\n \"_blank\",\n \"noopener,noreferrer\"\n );\n }, [shareUrl, shareTitle]);\n\n const handleLinkedIn = useCallback(() => {\n const params = new URLSearchParams({\n url: shareUrl,\n });\n window.open(\n `https://www.linkedin.com/sharing/share-offsite/?${params.toString()}`,\n \"_blank\",\n \"noopener,noreferrer\"\n );\n }, [shareUrl]);\n\n const keys = [\n {\n label: copied ? \"Link copied\" : \"Copy link\",\n onClick: handleCopy as () => void,\n content: (\n <>\n <span key={String(copied)} className={[\"animate-checkbox-in\", copied ? \"text-success-soft\" : \"\"].join(\" \")}>\n {copied ? <CheckIcon /> : <LinkIcon />}\n </span>\n <span className=\"w-[62px] text-left\">{copied ? \"copied\" : \"copy link\"}</span>\n </>\n ),\n wide: true,\n },\n { label: \"Share on X\", onClick: handleTwitter, content: <XIcon />, wide: false },\n { label: \"Share on LinkedIn\", onClick: handleLinkedIn, content: <LinkedInIcon />, wide: false },\n ];\n\n return (\n <div className={className}>\n <p className=\"text-[12px] font-medium text-ink-muted lowercase mb-3\">\n Share\n </p>\n <ButtonGroup label=\"Share\" items={keys} />\n <span aria-live=\"polite\" className=\"sr-only\">{copied ? \"Link copied to clipboard\" : \"\"}</span>\n </div>\n );\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
|
|
4
|
+
interface SidebarItem {
|
|
5
|
+
key: string;
|
|
6
|
+
label: string;
|
|
7
|
+
icon?: React.ReactNode;
|
|
8
|
+
badge?: string | number;
|
|
9
|
+
disabled?: boolean;
|
|
10
|
+
}
|
|
11
|
+
interface SidebarSection {
|
|
12
|
+
title?: string;
|
|
13
|
+
items: SidebarItem[];
|
|
14
|
+
}
|
|
15
|
+
interface SidebarHeader {
|
|
16
|
+
logo?: React.ReactNode;
|
|
17
|
+
title: string;
|
|
18
|
+
subtitle?: string;
|
|
19
|
+
}
|
|
20
|
+
interface SidebarProps {
|
|
21
|
+
sections: SidebarSection[];
|
|
22
|
+
header?: SidebarHeader;
|
|
23
|
+
defaultActive?: string;
|
|
24
|
+
active?: string;
|
|
25
|
+
onSelect?: (key: string) => void;
|
|
26
|
+
className?: string;
|
|
27
|
+
}
|
|
28
|
+
declare function Sidebar({ sections, header, defaultActive, active, onSelect, className, }: SidebarProps): react_jsx_runtime.JSX.Element;
|
|
29
|
+
|
|
30
|
+
export { Sidebar };
|
package/dist/Sidebar.js
ADDED
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
// ../../components/ui/Sidebar.tsx
|
|
4
|
+
import { useState, useRef, useEffect, useLayoutEffect } from "react";
|
|
5
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
6
|
+
function Sidebar({
|
|
7
|
+
sections,
|
|
8
|
+
header,
|
|
9
|
+
defaultActive = "",
|
|
10
|
+
active,
|
|
11
|
+
onSelect,
|
|
12
|
+
className = ""
|
|
13
|
+
}) {
|
|
14
|
+
const [internal, setInternal] = useState(defaultActive);
|
|
15
|
+
const current = active !== void 0 ? active : internal;
|
|
16
|
+
const [pill, setPill] = useState({ top: 0, height: 0, ready: false });
|
|
17
|
+
const navRef = useRef(null);
|
|
18
|
+
const btnRefs = useRef({});
|
|
19
|
+
const measurePill = (key) => {
|
|
20
|
+
const btn = btnRefs.current[key];
|
|
21
|
+
const nav = navRef.current;
|
|
22
|
+
if (!btn || !nav) return;
|
|
23
|
+
const nr = nav.getBoundingClientRect();
|
|
24
|
+
const br = btn.getBoundingClientRect();
|
|
25
|
+
setPill({ top: br.top - nr.top, height: br.height, ready: true });
|
|
26
|
+
};
|
|
27
|
+
useLayoutEffect(() => {
|
|
28
|
+
measurePill(current);
|
|
29
|
+
}, [current]);
|
|
30
|
+
useEffect(() => {
|
|
31
|
+
const ro = new ResizeObserver(() => measurePill(current));
|
|
32
|
+
if (navRef.current) ro.observe(navRef.current);
|
|
33
|
+
return () => ro.disconnect();
|
|
34
|
+
}, [current]);
|
|
35
|
+
const handleSelect = (key, disabled) => {
|
|
36
|
+
if (disabled) return;
|
|
37
|
+
setInternal(key);
|
|
38
|
+
onSelect?.(key);
|
|
39
|
+
};
|
|
40
|
+
return /* @__PURE__ */ jsx(
|
|
41
|
+
"div",
|
|
42
|
+
{
|
|
43
|
+
className: ["rounded-[20px] p-1.5", className].join(" "),
|
|
44
|
+
style: { background: "var(--panel-bezel)", boxShadow: "var(--panel-shadow)" },
|
|
45
|
+
children: /* @__PURE__ */ jsxs("div", { className: "bg-panel-face rounded-[15px] overflow-hidden", style: { boxShadow: "var(--panel-face-shadow)" }, children: [
|
|
46
|
+
header && /* @__PURE__ */ jsxs("div", { className: "px-3 py-3 border-b border-line flex items-center gap-2.5", children: [
|
|
47
|
+
header.logo && /* @__PURE__ */ jsx(
|
|
48
|
+
"div",
|
|
49
|
+
{
|
|
50
|
+
className: "w-7 h-7 rounded-[8px] flex items-center justify-center shrink-0 text-ink-inverse",
|
|
51
|
+
style: { background: "var(--face-inverse)", boxShadow: "var(--face-inverse-shadow)" },
|
|
52
|
+
children: header.logo
|
|
53
|
+
}
|
|
54
|
+
),
|
|
55
|
+
/* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
|
|
56
|
+
/* @__PURE__ */ jsx("p", { className: "text-[13px] font-semibold text-ink leading-none truncate", children: header.title }),
|
|
57
|
+
header.subtitle && /* @__PURE__ */ jsx("p", { className: "text-[11px] text-ink-faint mt-0.5 leading-none truncate", children: header.subtitle })
|
|
58
|
+
] }),
|
|
59
|
+
/* @__PURE__ */ jsx(
|
|
60
|
+
"button",
|
|
61
|
+
{
|
|
62
|
+
type: "button",
|
|
63
|
+
"aria-label": "Switch workspace",
|
|
64
|
+
className: "shrink-0 w-6 h-6 flex items-center justify-center text-ink-faint hover:text-ink transition-colors cursor-pointer rounded-[7px] hover:bg-surface-active",
|
|
65
|
+
children: /* @__PURE__ */ jsx("svg", { width: "13", height: "13", fill: "none", stroke: "currentColor", strokeWidth: "2", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M8 9l4-4 4 4M8 15l4 4 4-4" }) })
|
|
66
|
+
}
|
|
67
|
+
)
|
|
68
|
+
] }),
|
|
69
|
+
/* @__PURE__ */ jsxs("nav", { ref: navRef, "aria-label": header?.title ?? "Sidebar", className: "relative p-2 flex flex-col gap-4", children: [
|
|
70
|
+
pill.ready && /* @__PURE__ */ jsx(
|
|
71
|
+
"span",
|
|
72
|
+
{
|
|
73
|
+
"aria-hidden": true,
|
|
74
|
+
className: "absolute left-2 right-2 rounded-[11px] pointer-events-none z-0",
|
|
75
|
+
style: {
|
|
76
|
+
top: pill.top,
|
|
77
|
+
height: pill.height,
|
|
78
|
+
background: "var(--face-inverse)",
|
|
79
|
+
boxShadow: "var(--face-inverse-shadow)",
|
|
80
|
+
transition: "top 300ms var(--ease-spring), height 300ms var(--ease-spring)"
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
),
|
|
84
|
+
sections.map((section, si) => /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-0.5", children: [
|
|
85
|
+
section.title && /* @__PURE__ */ jsx("p", { className: "relative z-10 px-2.5 pt-1 pb-1.5 text-[11.5px] font-medium text-ink-faint lowercase", children: section.title }),
|
|
86
|
+
section.items.map((item) => {
|
|
87
|
+
const isActive = current === item.key;
|
|
88
|
+
return /* @__PURE__ */ jsxs(
|
|
89
|
+
"button",
|
|
90
|
+
{
|
|
91
|
+
ref: (el) => {
|
|
92
|
+
btnRefs.current[item.key] = el;
|
|
93
|
+
},
|
|
94
|
+
type: "button",
|
|
95
|
+
onClick: () => handleSelect(item.key, item.disabled),
|
|
96
|
+
disabled: item.disabled,
|
|
97
|
+
"aria-current": isActive ? "page" : void 0,
|
|
98
|
+
className: [
|
|
99
|
+
"relative z-10 w-full flex items-center gap-2.5 px-2.5 py-2 rounded-[11px] text-[13px] font-medium text-left",
|
|
100
|
+
"transition-colors duration-200 cursor-pointer",
|
|
101
|
+
"disabled:opacity-35 disabled:pointer-events-none",
|
|
102
|
+
isActive ? "text-ink-inverse" : "text-ink-body hover:text-ink hover:bg-surface-active/80"
|
|
103
|
+
].join(" "),
|
|
104
|
+
children: [
|
|
105
|
+
item.icon && /* @__PURE__ */ jsx(
|
|
106
|
+
"span",
|
|
107
|
+
{
|
|
108
|
+
className: [
|
|
109
|
+
"shrink-0 w-[22px] h-[22px] rounded-md flex items-center justify-center transition-colors duration-200",
|
|
110
|
+
isActive ? "bg-ink-inverse/12 shadow-[inset_0_1px_0_var(--inverse-lip)]" : "text-ink-muted [background:var(--face)] [box-shadow:var(--face-shadow)]"
|
|
111
|
+
].join(" "),
|
|
112
|
+
children: item.icon
|
|
113
|
+
}
|
|
114
|
+
),
|
|
115
|
+
/* @__PURE__ */ jsx("span", { className: "flex-1 truncate", children: item.label }),
|
|
116
|
+
item.badge !== void 0 && /* @__PURE__ */ jsx(
|
|
117
|
+
"span",
|
|
118
|
+
{
|
|
119
|
+
className: [
|
|
120
|
+
"shrink-0 text-[11px] font-medium px-1.5 py-0.5 rounded-md tabular-nums leading-none transition-colors duration-200",
|
|
121
|
+
isActive ? "bg-ink-inverse/20 text-ink-inverse/80" : "bg-surface-active text-ink-muted"
|
|
122
|
+
].join(" "),
|
|
123
|
+
children: item.badge
|
|
124
|
+
}
|
|
125
|
+
)
|
|
126
|
+
]
|
|
127
|
+
},
|
|
128
|
+
item.key
|
|
129
|
+
);
|
|
130
|
+
})
|
|
131
|
+
] }, si))
|
|
132
|
+
] })
|
|
133
|
+
] })
|
|
134
|
+
}
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
export {
|
|
138
|
+
Sidebar
|
|
139
|
+
};
|
|
140
|
+
//# sourceMappingURL=Sidebar.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../components/ui/Sidebar.tsx"],"sourcesContent":["\"use client\";\n\nimport React, { useState, useRef, useEffect, useLayoutEffect } from \"react\";\n\ninterface SidebarItem {\n key: string;\n label: string;\n icon?: React.ReactNode;\n badge?: string | number;\n disabled?: boolean;\n}\n\ninterface SidebarSection {\n title?: string;\n items: SidebarItem[];\n}\n\ninterface SidebarHeader {\n logo?: React.ReactNode;\n title: string;\n subtitle?: string;\n}\n\ninterface SidebarProps {\n sections: SidebarSection[];\n header?: SidebarHeader;\n defaultActive?: string;\n active?: string;\n onSelect?: (key: string) => void;\n className?: string;\n}\n\nexport function Sidebar({\n sections,\n header,\n defaultActive = \"\",\n active,\n onSelect,\n className = \"\",\n}: SidebarProps) {\n const [internal, setInternal] = useState(defaultActive);\n const current = active !== undefined ? active : internal;\n const [pill, setPill] = useState({ top: 0, height: 0, ready: false });\n const navRef = useRef<HTMLElement>(null);\n const btnRefs = useRef<Record<string, HTMLButtonElement | null>>({});\n\n const measurePill = (key: string) => {\n const btn = btnRefs.current[key];\n const nav = navRef.current;\n if (!btn || !nav) return;\n const nr = nav.getBoundingClientRect();\n const br = btn.getBoundingClientRect();\n setPill({ top: br.top - nr.top, height: br.height, ready: true });\n };\n\n // Before paint, so the pill is never seen out of place\n useLayoutEffect(() => { measurePill(current); }, [current]);\n\n useEffect(() => {\n const ro = new ResizeObserver(() => measurePill(current));\n if (navRef.current) ro.observe(navRef.current);\n return () => ro.disconnect();\n }, [current]);\n\n const handleSelect = (key: string, disabled?: boolean) => {\n if (disabled) return;\n setInternal(key);\n onSelect?.(key);\n };\n\n return (\n <div\n className={[\"rounded-[20px] p-1.5\", className].join(\" \")}\n style={{ background: \"var(--panel-bezel)\", boxShadow: \"var(--panel-shadow)\" }}\n >\n <div className=\"bg-panel-face rounded-[15px] overflow-hidden\" style={{ boxShadow: \"var(--panel-face-shadow)\" }}>\n {header && (\n <div className=\"px-3 py-3 border-b border-line flex items-center gap-2.5\">\n {header.logo && (\n <div\n className=\"w-7 h-7 rounded-[8px] flex items-center justify-center shrink-0 text-ink-inverse\"\n style={{ background: \"var(--face-inverse)\", boxShadow: \"var(--face-inverse-shadow)\" }}\n >\n {header.logo}\n </div>\n )}\n <div className=\"flex-1 min-w-0\">\n <p className=\"text-[13px] font-semibold text-ink leading-none truncate\">{header.title}</p>\n {header.subtitle && (\n <p className=\"text-[11px] text-ink-faint mt-0.5 leading-none truncate\">{header.subtitle}</p>\n )}\n </div>\n <button\n type=\"button\"\n aria-label=\"Switch workspace\"\n className=\"shrink-0 w-6 h-6 flex items-center justify-center text-ink-faint hover:text-ink transition-colors cursor-pointer rounded-[7px] hover:bg-surface-active\"\n >\n <svg width=\"13\" height=\"13\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" viewBox=\"0 0 24 24\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M8 9l4-4 4 4M8 15l4 4 4-4\" />\n </svg>\n </button>\n </div>\n )}\n\n <nav ref={navRef} aria-label={header?.title ?? \"Sidebar\"} className=\"relative p-2 flex flex-col gap-4\">\n {pill.ready && (\n <span\n aria-hidden\n className=\"absolute left-2 right-2 rounded-[11px] pointer-events-none z-0\"\n style={{\n top: pill.top,\n height: pill.height,\n background: \"var(--face-inverse)\",\n boxShadow: \"var(--face-inverse-shadow)\",\n transition: \"top 300ms var(--ease-spring), height 300ms var(--ease-spring)\",\n }}\n />\n )}\n\n {sections.map((section, si) => (\n <div key={si} className=\"flex flex-col gap-0.5\">\n {section.title && (\n <p className=\"relative z-10 px-2.5 pt-1 pb-1.5 text-[11.5px] font-medium text-ink-faint lowercase\">\n {section.title}\n </p>\n )}\n {section.items.map((item) => {\n const isActive = current === item.key;\n return (\n <button\n key={item.key}\n ref={(el) => { btnRefs.current[item.key] = el; }}\n type=\"button\"\n onClick={() => handleSelect(item.key, item.disabled)}\n disabled={item.disabled}\n aria-current={isActive ? \"page\" : undefined}\n className={[\n \"relative z-10 w-full flex items-center gap-2.5 px-2.5 py-2 rounded-[11px] text-[13px] font-medium text-left\",\n \"transition-colors duration-200 cursor-pointer\",\n \"disabled:opacity-35 disabled:pointer-events-none\",\n isActive\n ? \"text-ink-inverse\"\n : \"text-ink-body hover:text-ink hover:bg-surface-active/80\",\n ].join(\" \")}\n >\n {item.icon && (\n <span\n className={[\n \"shrink-0 w-[22px] h-[22px] rounded-md flex items-center justify-center transition-colors duration-200\",\n isActive\n ? \"bg-ink-inverse/12 shadow-[inset_0_1px_0_var(--inverse-lip)]\"\n : \"text-ink-muted [background:var(--face)] [box-shadow:var(--face-shadow)]\",\n ].join(\" \")}\n >\n {item.icon}\n </span>\n )}\n\n <span className=\"flex-1 truncate\">{item.label}</span>\n\n {item.badge !== undefined && (\n <span\n className={[\n \"shrink-0 text-[11px] font-medium px-1.5 py-0.5 rounded-md tabular-nums leading-none transition-colors duration-200\",\n isActive\n ? \"bg-ink-inverse/20 text-ink-inverse/80\"\n : \"bg-surface-active text-ink-muted\",\n ].join(\" \")}\n >\n {item.badge}\n </span>\n )}\n </button>\n );\n })}\n </div>\n ))}\n </nav>\n </div>\n </div>\n );\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
|
|
3
|
+
type SkeletonVariant = "text" | "circle" | "rect" | "card";
|
|
4
|
+
interface SkeletonProps {
|
|
5
|
+
variant?: SkeletonVariant;
|
|
6
|
+
width?: string | number;
|
|
7
|
+
height?: string | number;
|
|
8
|
+
lines?: number;
|
|
9
|
+
className?: string;
|
|
10
|
+
/** What is loading, for screen readers */
|
|
11
|
+
label?: string;
|
|
12
|
+
}
|
|
13
|
+
declare function Skeleton({ variant, width, height, lines, className, label, }: SkeletonProps): react_jsx_runtime.JSX.Element;
|
|
14
|
+
|
|
15
|
+
export { Skeleton };
|
package/dist/Skeleton.js
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
// ../../components/ui/Skeleton.tsx
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
var base = "skeleton-sheen relative overflow-hidden rounded-[8px]";
|
|
4
|
+
var well = { background: "var(--well)", boxShadow: "var(--well-shadow)" };
|
|
5
|
+
function Skeleton({
|
|
6
|
+
variant = "text",
|
|
7
|
+
width,
|
|
8
|
+
height,
|
|
9
|
+
lines = 1,
|
|
10
|
+
className = "",
|
|
11
|
+
label = "Loading"
|
|
12
|
+
}) {
|
|
13
|
+
const a11y = { role: "status", "aria-busy": true, "aria-label": label };
|
|
14
|
+
if (variant === "circle") {
|
|
15
|
+
return /* @__PURE__ */ jsx(
|
|
16
|
+
"div",
|
|
17
|
+
{
|
|
18
|
+
...a11y,
|
|
19
|
+
className: [base, "rounded-full", className].join(" "),
|
|
20
|
+
style: { ...well, width: width ?? 40, height: height ?? 40 }
|
|
21
|
+
}
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
if (variant === "card") {
|
|
25
|
+
return /* @__PURE__ */ jsx(
|
|
26
|
+
"div",
|
|
27
|
+
{
|
|
28
|
+
...a11y,
|
|
29
|
+
className: ["rounded-[20px] p-1.5", className].join(" "),
|
|
30
|
+
style: { background: "var(--panel-bezel)", boxShadow: "var(--panel-shadow)" },
|
|
31
|
+
children: /* @__PURE__ */ jsxs("div", { className: "bg-panel-face rounded-[15px] p-1.5", style: { boxShadow: "var(--panel-face-shadow)" }, children: [
|
|
32
|
+
/* @__PURE__ */ jsx("div", { className: [base, "w-full rounded-[11px]"].join(" "), style: { ...well, height: height ?? 160 } }),
|
|
33
|
+
/* @__PURE__ */ jsxs("div", { className: "px-3.5 pt-4 pb-3.5 flex flex-col gap-2.5", children: [
|
|
34
|
+
/* @__PURE__ */ jsx("div", { className: [base, "h-3.5 w-3/5"].join(" "), style: well }),
|
|
35
|
+
/* @__PURE__ */ jsx("div", { className: [base, "h-2.5 w-full"].join(" "), style: well }),
|
|
36
|
+
/* @__PURE__ */ jsx("div", { className: [base, "h-2.5 w-4/5"].join(" "), style: well })
|
|
37
|
+
] })
|
|
38
|
+
] })
|
|
39
|
+
}
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
if (variant === "rect") {
|
|
43
|
+
return /* @__PURE__ */ jsx(
|
|
44
|
+
"div",
|
|
45
|
+
{
|
|
46
|
+
...a11y,
|
|
47
|
+
className: [base, "rounded-[12px]", className].join(" "),
|
|
48
|
+
style: { ...well, width: width ?? "100%", height: height ?? 80 }
|
|
49
|
+
}
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
return /* @__PURE__ */ jsx("div", { ...a11y, className: ["flex flex-col gap-2", className].join(" "), children: Array.from({ length: lines }).map((_, i) => /* @__PURE__ */ jsx(
|
|
53
|
+
"div",
|
|
54
|
+
{
|
|
55
|
+
className: [base, "h-2.5 rounded-full"].join(" "),
|
|
56
|
+
style: {
|
|
57
|
+
...well,
|
|
58
|
+
width: width ?? (i === lines - 1 && lines > 1 ? "60%" : "100%")
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
i
|
|
62
|
+
)) });
|
|
63
|
+
}
|
|
64
|
+
export {
|
|
65
|
+
Skeleton
|
|
66
|
+
};
|
|
67
|
+
//# sourceMappingURL=Skeleton.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../components/ui/Skeleton.tsx"],"sourcesContent":["import React from \"react\";\n\ntype SkeletonVariant = \"text\" | \"circle\" | \"rect\" | \"card\";\n\ninterface SkeletonProps {\n variant?: SkeletonVariant;\n width?: string | number;\n height?: string | number;\n lines?: number;\n className?: string;\n /** What is loading, for screen readers */\n label?: string;\n}\n\n/* Placeholders are empty sunken wells — the shape of what will fill them —\n with a light sweep travelling across, rather than a blink. */\nconst base = \"skeleton-sheen relative overflow-hidden rounded-[8px]\";\nconst well: React.CSSProperties = { background: \"var(--well)\", boxShadow: \"var(--well-shadow)\" };\n\nexport function Skeleton({\n variant = \"text\",\n width,\n height,\n lines = 1,\n className = \"\",\n label = \"Loading\",\n}: SkeletonProps) {\n const a11y = { role: \"status\", \"aria-busy\": true, \"aria-label\": label } as const;\n\n if (variant === \"circle\") {\n return (\n <div\n {...a11y}\n className={[base, \"rounded-full\", className].join(\" \")}\n style={{ ...well, width: width ?? 40, height: height ?? 40 }}\n />\n );\n }\n\n if (variant === \"card\") {\n return (\n <div\n {...a11y}\n className={[\"rounded-[20px] p-1.5\", className].join(\" \")}\n style={{ background: \"var(--panel-bezel)\", boxShadow: \"var(--panel-shadow)\" }}\n >\n <div className=\"bg-panel-face rounded-[15px] p-1.5\" style={{ boxShadow: \"var(--panel-face-shadow)\" }}>\n <div className={[base, \"w-full rounded-[11px]\"].join(\" \")} style={{ ...well, height: height ?? 160 }} />\n <div className=\"px-3.5 pt-4 pb-3.5 flex flex-col gap-2.5\">\n <div className={[base, \"h-3.5 w-3/5\"].join(\" \")} style={well} />\n <div className={[base, \"h-2.5 w-full\"].join(\" \")} style={well} />\n <div className={[base, \"h-2.5 w-4/5\"].join(\" \")} style={well} />\n </div>\n </div>\n </div>\n );\n }\n\n if (variant === \"rect\") {\n return (\n <div\n {...a11y}\n className={[base, \"rounded-[12px]\", className].join(\" \")}\n style={{ ...well, width: width ?? \"100%\", height: height ?? 80 }}\n />\n );\n }\n\n return (\n <div {...a11y} className={[\"flex flex-col gap-2\", className].join(\" \")}>\n {Array.from({ length: lines }).map((_, i) => (\n <div\n key={i}\n className={[base, \"h-2.5 rounded-full\"].join(\" \")}\n style={{\n ...well,\n width: width ?? (i === lines - 1 && lines > 1 ? \"60%\" : \"100%\"),\n }}\n />\n ))}\n </div>\n );\n}\n"],"mappings":";AA+BM,cAiBI,YAjBJ;AAfN,IAAM,OAAO;AACb,IAAM,OAA4B,EAAE,YAAY,eAAe,WAAW,qBAAqB;AAExF,SAAS,SAAS;AAAA,EACvB,UAAU;AAAA,EACV;AAAA,EACA;AAAA,EACA,QAAQ;AAAA,EACR,YAAY;AAAA,EACZ,QAAQ;AACV,GAAkB;AAChB,QAAM,OAAO,EAAE,MAAM,UAAU,aAAa,MAAM,cAAc,MAAM;AAEtE,MAAI,YAAY,UAAU;AACxB,WACE;AAAA,MAAC;AAAA;AAAA,QACE,GAAG;AAAA,QACJ,WAAW,CAAC,MAAM,gBAAgB,SAAS,EAAE,KAAK,GAAG;AAAA,QACrD,OAAO,EAAE,GAAG,MAAM,OAAO,SAAS,IAAI,QAAQ,UAAU,GAAG;AAAA;AAAA,IAC7D;AAAA,EAEJ;AAEA,MAAI,YAAY,QAAQ;AACtB,WACE;AAAA,MAAC;AAAA;AAAA,QACE,GAAG;AAAA,QACJ,WAAW,CAAC,wBAAwB,SAAS,EAAE,KAAK,GAAG;AAAA,QACvD,OAAO,EAAE,YAAY,sBAAsB,WAAW,sBAAsB;AAAA,QAE5E,+BAAC,SAAI,WAAU,sCAAqC,OAAO,EAAE,WAAW,2BAA2B,GACjG;AAAA,8BAAC,SAAI,WAAW,CAAC,MAAM,uBAAuB,EAAE,KAAK,GAAG,GAAG,OAAO,EAAE,GAAG,MAAM,QAAQ,UAAU,IAAI,GAAG;AAAA,UACtG,qBAAC,SAAI,WAAU,4CACb;AAAA,gCAAC,SAAI,WAAW,CAAC,MAAM,aAAa,EAAE,KAAK,GAAG,GAAG,OAAO,MAAM;AAAA,YAC9D,oBAAC,SAAI,WAAW,CAAC,MAAM,cAAc,EAAE,KAAK,GAAG,GAAG,OAAO,MAAM;AAAA,YAC/D,oBAAC,SAAI,WAAW,CAAC,MAAM,aAAa,EAAE,KAAK,GAAG,GAAG,OAAO,MAAM;AAAA,aAChE;AAAA,WACF;AAAA;AAAA,IACF;AAAA,EAEJ;AAEA,MAAI,YAAY,QAAQ;AACtB,WACE;AAAA,MAAC;AAAA;AAAA,QACE,GAAG;AAAA,QACJ,WAAW,CAAC,MAAM,kBAAkB,SAAS,EAAE,KAAK,GAAG;AAAA,QACvD,OAAO,EAAE,GAAG,MAAM,OAAO,SAAS,QAAQ,QAAQ,UAAU,GAAG;AAAA;AAAA,IACjE;AAAA,EAEJ;AAEA,SACE,oBAAC,SAAK,GAAG,MAAM,WAAW,CAAC,uBAAuB,SAAS,EAAE,KAAK,GAAG,GAClE,gBAAM,KAAK,EAAE,QAAQ,MAAM,CAAC,EAAE,IAAI,CAAC,GAAG,MACrC;AAAA,IAAC;AAAA;AAAA,MAEC,WAAW,CAAC,MAAM,oBAAoB,EAAE,KAAK,GAAG;AAAA,MAChD,OAAO;AAAA,QACL,GAAG;AAAA,QACH,OAAO,UAAU,MAAM,QAAQ,KAAK,QAAQ,IAAI,QAAQ;AAAA,MAC1D;AAAA;AAAA,IALK;AAAA,EAMP,CACD,GACH;AAEJ;","names":[]}
|
package/dist/Slider.d.ts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
|
|
4
|
+
type SliderValue = number | [number, number];
|
|
5
|
+
interface SliderProps<V extends SliderValue> {
|
|
6
|
+
/** One number for a slider, two for a range */
|
|
7
|
+
value?: V;
|
|
8
|
+
defaultValue?: V;
|
|
9
|
+
onChange?: (value: V) => void;
|
|
10
|
+
/** Fires once the knob is let go, for work too heavy to do while dragging */
|
|
11
|
+
onCommit?: (value: V) => void;
|
|
12
|
+
min?: number;
|
|
13
|
+
max?: number;
|
|
14
|
+
step?: number;
|
|
15
|
+
label?: string;
|
|
16
|
+
/** Show the value beside the label; pass a function to format it */
|
|
17
|
+
showValue?: boolean | ((value: V) => React.ReactNode);
|
|
18
|
+
/** Small labels under the track, at these values */
|
|
19
|
+
marks?: number[];
|
|
20
|
+
disabled?: boolean;
|
|
21
|
+
/** Name of a hidden input (two for a range: name[0], name[1]) */
|
|
22
|
+
name?: string;
|
|
23
|
+
className?: string;
|
|
24
|
+
}
|
|
25
|
+
declare function Slider<V extends SliderValue = number>({ value, defaultValue, onChange, onCommit, min, max, step, label, showValue, marks, disabled, name, className, }: SliderProps<V>): react_jsx_runtime.JSX.Element;
|
|
26
|
+
|
|
27
|
+
export { Slider };
|
package/dist/Slider.js
ADDED
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
// ../../components/ui/Slider.tsx
|
|
4
|
+
import { useId, useRef, useState } from "react";
|
|
5
|
+
import { FieldLabel } from "./FieldMessage.js";
|
|
6
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
7
|
+
var clamp = (n, lo, hi) => Math.min(hi, Math.max(lo, n));
|
|
8
|
+
var decimals = (n) => (String(n).split(".")[1] ?? "").length;
|
|
9
|
+
function Slider({
|
|
10
|
+
value,
|
|
11
|
+
defaultValue,
|
|
12
|
+
onChange,
|
|
13
|
+
onCommit,
|
|
14
|
+
min = 0,
|
|
15
|
+
max = 100,
|
|
16
|
+
step = 1,
|
|
17
|
+
label,
|
|
18
|
+
showValue = false,
|
|
19
|
+
marks,
|
|
20
|
+
disabled = false,
|
|
21
|
+
name,
|
|
22
|
+
className = ""
|
|
23
|
+
}) {
|
|
24
|
+
const uid = useId();
|
|
25
|
+
const trackRef = useRef(null);
|
|
26
|
+
const knobs = useRef([]);
|
|
27
|
+
const isRange = Array.isArray(value ?? defaultValue);
|
|
28
|
+
const [internal, setInternal] = useState(() => defaultValue ?? value ?? min);
|
|
29
|
+
const current = value !== void 0 ? value : internal;
|
|
30
|
+
const values = Array.isArray(current) ? [...current] : [current];
|
|
31
|
+
const [dragging, setDragging] = useState(null);
|
|
32
|
+
const digits = decimals(step);
|
|
33
|
+
const snap = (n) => Number(clamp(Math.round((n - min) / step) * step + min, min, max).toFixed(digits));
|
|
34
|
+
const pct = (n) => (n - min) / (max - min) * 100;
|
|
35
|
+
const emit = (next, commit = false) => {
|
|
36
|
+
const v = isRange ? [next[0], next[1]] : next[0];
|
|
37
|
+
setInternal(v);
|
|
38
|
+
if (next.some((n, i) => n !== values[i])) onChange?.(v);
|
|
39
|
+
if (commit) onCommit?.(v);
|
|
40
|
+
};
|
|
41
|
+
const setAt = (i, n, commit = false) => {
|
|
42
|
+
const next = [...values];
|
|
43
|
+
next[i] = snap(n);
|
|
44
|
+
if (isRange) next[i] = i === 0 ? Math.min(next[0], values[1]) : Math.max(next[1], values[0]);
|
|
45
|
+
emit(next, commit);
|
|
46
|
+
};
|
|
47
|
+
const fromPointer = (clientX) => {
|
|
48
|
+
const r = trackRef.current.getBoundingClientRect();
|
|
49
|
+
return min + clamp((clientX - r.left) / r.width, 0, 1) * (max - min);
|
|
50
|
+
};
|
|
51
|
+
const onPointerDown = (e) => {
|
|
52
|
+
if (disabled || e.button !== 0) return;
|
|
53
|
+
e.preventDefault();
|
|
54
|
+
const n = fromPointer(e.clientX);
|
|
55
|
+
let i = 0;
|
|
56
|
+
if (isRange) {
|
|
57
|
+
const d0 = Math.abs(n - values[0]);
|
|
58
|
+
const d1 = Math.abs(n - values[1]);
|
|
59
|
+
i = d1 < d0 || d0 === d1 && n > values[1] ? 1 : 0;
|
|
60
|
+
}
|
|
61
|
+
e.currentTarget.setPointerCapture(e.pointerId);
|
|
62
|
+
setDragging(i);
|
|
63
|
+
knobs.current[i]?.focus();
|
|
64
|
+
setAt(i, n);
|
|
65
|
+
};
|
|
66
|
+
const onKeyDown = (i) => (e) => {
|
|
67
|
+
const big = Math.max(step, (max - min) / 10);
|
|
68
|
+
const moves = {
|
|
69
|
+
ArrowRight: step * (e.shiftKey ? 10 : 1),
|
|
70
|
+
ArrowUp: step * (e.shiftKey ? 10 : 1),
|
|
71
|
+
ArrowLeft: -step * (e.shiftKey ? 10 : 1),
|
|
72
|
+
ArrowDown: -step * (e.shiftKey ? 10 : 1),
|
|
73
|
+
PageUp: big,
|
|
74
|
+
PageDown: -big
|
|
75
|
+
};
|
|
76
|
+
if (e.key in moves) {
|
|
77
|
+
e.preventDefault();
|
|
78
|
+
setAt(i, values[i] + moves[e.key], true);
|
|
79
|
+
} else if (e.key === "Home") {
|
|
80
|
+
e.preventDefault();
|
|
81
|
+
setAt(i, min, true);
|
|
82
|
+
} else if (e.key === "End") {
|
|
83
|
+
e.preventDefault();
|
|
84
|
+
setAt(i, max, true);
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
const shown = typeof showValue === "function" ? showValue(current) : showValue ? values.join(" \u2013 ") : null;
|
|
88
|
+
const lo = isRange ? pct(values[0]) : 0;
|
|
89
|
+
const hi = pct(values[isRange ? 1 : 0]);
|
|
90
|
+
return /* @__PURE__ */ jsxs("div", { className: ["flex w-full flex-col gap-2.5", disabled ? "opacity-40" : "", className].join(" "), children: [
|
|
91
|
+
(label || shown !== null) && /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-4", children: [
|
|
92
|
+
label && /* @__PURE__ */ jsx(FieldLabel, { id: `${uid}-label`, children: label }),
|
|
93
|
+
shown !== null && /* @__PURE__ */ jsx("span", { className: "text-[12.5px] tabular-nums text-ink-muted", children: shown })
|
|
94
|
+
] }),
|
|
95
|
+
/* @__PURE__ */ jsxs(
|
|
96
|
+
"div",
|
|
97
|
+
{
|
|
98
|
+
className: ["relative flex h-6 touch-none items-center", disabled ? "pointer-events-none" : "cursor-pointer"].join(" "),
|
|
99
|
+
onPointerDown,
|
|
100
|
+
onPointerMove: (e) => dragging !== null && setAt(dragging, fromPointer(e.clientX)),
|
|
101
|
+
onPointerUp: () => {
|
|
102
|
+
if (dragging !== null) onCommit?.(current);
|
|
103
|
+
setDragging(null);
|
|
104
|
+
},
|
|
105
|
+
onPointerCancel: () => setDragging(null),
|
|
106
|
+
children: [
|
|
107
|
+
/* @__PURE__ */ jsx(
|
|
108
|
+
"div",
|
|
109
|
+
{
|
|
110
|
+
ref: trackRef,
|
|
111
|
+
className: "relative h-2 w-full rounded-full",
|
|
112
|
+
style: { background: "var(--track)", boxShadow: "var(--track-shadow)" },
|
|
113
|
+
children: /* @__PURE__ */ jsx(
|
|
114
|
+
"div",
|
|
115
|
+
{
|
|
116
|
+
className: "absolute inset-y-px rounded-full",
|
|
117
|
+
style: {
|
|
118
|
+
left: `calc(${lo}% + ${isRange ? 0 : 1}px)`,
|
|
119
|
+
width: `calc(${hi - lo}% - ${isRange ? 0 : 1}px)`,
|
|
120
|
+
background: "var(--face-inverse)",
|
|
121
|
+
boxShadow: "var(--fill-shadow)",
|
|
122
|
+
transition: dragging === null ? "left 200ms var(--ease-out-soft), width 200ms var(--ease-out-soft)" : "none"
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
)
|
|
126
|
+
}
|
|
127
|
+
),
|
|
128
|
+
values.map((v, i) => /* @__PURE__ */ jsx(
|
|
129
|
+
"span",
|
|
130
|
+
{
|
|
131
|
+
ref: (el) => {
|
|
132
|
+
knobs.current[i] = el;
|
|
133
|
+
},
|
|
134
|
+
role: "slider",
|
|
135
|
+
tabIndex: disabled ? -1 : 0,
|
|
136
|
+
"aria-labelledby": label ? `${uid}-label` : void 0,
|
|
137
|
+
"aria-label": isRange ? i === 0 ? "minimum" : "maximum" : label ? void 0 : "value",
|
|
138
|
+
"aria-valuemin": isRange && i === 1 ? values[0] : min,
|
|
139
|
+
"aria-valuemax": isRange && i === 0 ? values[1] : max,
|
|
140
|
+
"aria-valuenow": v,
|
|
141
|
+
"aria-disabled": disabled || void 0,
|
|
142
|
+
onKeyDown: onKeyDown(i),
|
|
143
|
+
className: "absolute top-1/2 h-5 w-5 rounded-full outline-none focus-visible:ring-2 focus-visible:ring-ink focus-visible:ring-offset-2 focus-visible:ring-offset-page",
|
|
144
|
+
style: {
|
|
145
|
+
left: `${pct(v)}%`,
|
|
146
|
+
// the last-dragged knob of a range sits on top, so two at one spot can be pulled apart
|
|
147
|
+
zIndex: dragging === i ? 2 : 1,
|
|
148
|
+
background: "var(--face)",
|
|
149
|
+
boxShadow: "var(--knob-shadow)",
|
|
150
|
+
transform: `translate(-50%, -50%) scale(${dragging === i ? 1.15 : 1})`,
|
|
151
|
+
transition: dragging === null ? "left 200ms var(--ease-out-soft), transform 220ms var(--ease-spring)" : "transform 220ms var(--ease-spring)"
|
|
152
|
+
},
|
|
153
|
+
children: /* @__PURE__ */ jsx(
|
|
154
|
+
"span",
|
|
155
|
+
{
|
|
156
|
+
"aria-hidden": true,
|
|
157
|
+
className: "absolute left-1/2 top-1/2 h-1.5 w-1.5 -translate-x-1/2 -translate-y-1/2 rounded-full",
|
|
158
|
+
style: { background: "var(--track)", boxShadow: "var(--track-shadow)" }
|
|
159
|
+
}
|
|
160
|
+
)
|
|
161
|
+
},
|
|
162
|
+
i
|
|
163
|
+
))
|
|
164
|
+
]
|
|
165
|
+
}
|
|
166
|
+
),
|
|
167
|
+
marks && marks.length > 0 && /* @__PURE__ */ jsx("div", { "aria-hidden": true, className: "relative h-4", children: marks.map((m) => /* @__PURE__ */ jsx(
|
|
168
|
+
"span",
|
|
169
|
+
{
|
|
170
|
+
className: "absolute -translate-x-1/2 text-[11px] tabular-nums text-ink-faint",
|
|
171
|
+
style: { left: `${pct(m)}%` },
|
|
172
|
+
children: m
|
|
173
|
+
},
|
|
174
|
+
m
|
|
175
|
+
)) }),
|
|
176
|
+
name && values.map((v, i) => /* @__PURE__ */ jsx("input", { type: "hidden", name: isRange ? `${name}[${i}]` : name, value: v }, i))
|
|
177
|
+
] });
|
|
178
|
+
}
|
|
179
|
+
export {
|
|
180
|
+
Slider
|
|
181
|
+
};
|
|
182
|
+
//# sourceMappingURL=Slider.js.map
|