@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,92 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
// ../../components/ui/ReactionBar.tsx
|
|
4
|
+
import { useState, useCallback, useRef } from "react";
|
|
5
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
6
|
+
var defaultReactions = [
|
|
7
|
+
{ emoji: "\u{1F44F}", label: "clap", count: 0 },
|
|
8
|
+
{ emoji: "\u2764\uFE0F", label: "love", count: 0 },
|
|
9
|
+
{ emoji: "\u{1F525}", label: "fire", count: 0 },
|
|
10
|
+
{ emoji: "\u{1F4A1}", label: "insightful", count: 0 },
|
|
11
|
+
{ emoji: "\u{1F389}", label: "celebrate", count: 0 }
|
|
12
|
+
];
|
|
13
|
+
function ReactionBar({
|
|
14
|
+
reactions: initialReactions,
|
|
15
|
+
className = ""
|
|
16
|
+
}) {
|
|
17
|
+
const [reactions, setReactions] = useState(
|
|
18
|
+
initialReactions ?? defaultReactions
|
|
19
|
+
);
|
|
20
|
+
const [animatingKey, setAnimatingKey] = useState(null);
|
|
21
|
+
const timeoutRef = useRef(null);
|
|
22
|
+
const handleClick = useCallback(
|
|
23
|
+
(index) => {
|
|
24
|
+
setReactions(
|
|
25
|
+
(prev) => prev.map((r, i) => {
|
|
26
|
+
if (i !== index) return r;
|
|
27
|
+
return {
|
|
28
|
+
...r,
|
|
29
|
+
active: !r.active,
|
|
30
|
+
count: r.active ? r.count - 1 : r.count + 1
|
|
31
|
+
};
|
|
32
|
+
})
|
|
33
|
+
);
|
|
34
|
+
const key = reactions[index].label;
|
|
35
|
+
setAnimatingKey(key);
|
|
36
|
+
if (timeoutRef.current) clearTimeout(timeoutRef.current);
|
|
37
|
+
timeoutRef.current = setTimeout(() => setAnimatingKey(null), 300);
|
|
38
|
+
},
|
|
39
|
+
[reactions]
|
|
40
|
+
);
|
|
41
|
+
return /* @__PURE__ */ jsx(
|
|
42
|
+
"div",
|
|
43
|
+
{
|
|
44
|
+
className: ["flex flex-wrap gap-2", className].filter(Boolean).join(" "),
|
|
45
|
+
children: reactions.map((reaction, index) => {
|
|
46
|
+
const isAnimating = animatingKey === reaction.label;
|
|
47
|
+
return /* @__PURE__ */ jsxs(
|
|
48
|
+
"button",
|
|
49
|
+
{
|
|
50
|
+
type: "button",
|
|
51
|
+
onClick: () => handleClick(index),
|
|
52
|
+
"aria-pressed": !!reaction.active,
|
|
53
|
+
"aria-label": `${reaction.label}, ${reaction.count}`,
|
|
54
|
+
className: [
|
|
55
|
+
"inline-flex items-center gap-1.5 rounded-[11px] pl-2.5 pr-3 py-[7px] text-[13px] font-medium cursor-pointer",
|
|
56
|
+
"focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ink",
|
|
57
|
+
reaction.active ? "text-ink-inverse" : "text-ink-body hover:text-ink"
|
|
58
|
+
].join(" "),
|
|
59
|
+
style: {
|
|
60
|
+
/* Off is a raised face; on is the dark face — pressed in and lit */
|
|
61
|
+
background: reaction.active ? "var(--face-inverse)" : "var(--face)",
|
|
62
|
+
boxShadow: reaction.active ? "var(--face-inverse-shadow)" : "var(--face-shadow)",
|
|
63
|
+
textShadow: reaction.active ? "var(--emboss-inverse)" : "var(--emboss)",
|
|
64
|
+
transform: isAnimating ? "scale(1.08)" : "scale(1)",
|
|
65
|
+
transition: "transform 250ms var(--ease-spring), color 200ms ease, box-shadow 200ms ease"
|
|
66
|
+
},
|
|
67
|
+
children: [
|
|
68
|
+
/* @__PURE__ */ jsx("span", { "aria-hidden": true, className: "text-[15px] leading-none", children: reaction.emoji }),
|
|
69
|
+
/* @__PURE__ */ jsx(
|
|
70
|
+
"span",
|
|
71
|
+
{
|
|
72
|
+
className: "tabular-nums leading-none",
|
|
73
|
+
style: {
|
|
74
|
+
transform: isAnimating ? "scale(1.15)" : "scale(1)",
|
|
75
|
+
display: "inline-block",
|
|
76
|
+
transition: "transform 250ms cubic-bezier(0.34, 1.4, 0.64, 1)"
|
|
77
|
+
},
|
|
78
|
+
children: reaction.count
|
|
79
|
+
}
|
|
80
|
+
)
|
|
81
|
+
]
|
|
82
|
+
},
|
|
83
|
+
reaction.label
|
|
84
|
+
);
|
|
85
|
+
})
|
|
86
|
+
}
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
export {
|
|
90
|
+
ReactionBar
|
|
91
|
+
};
|
|
92
|
+
//# sourceMappingURL=ReactionBar.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../components/ui/ReactionBar.tsx"],"sourcesContent":["\"use client\";\n\nimport React, { useState, useCallback, useRef } from \"react\";\n\nexport interface ReactionItem {\n emoji: string;\n label: string;\n count: number;\n active?: boolean;\n}\n\ninterface ReactionBarProps {\n reactions?: ReactionItem[];\n className?: string;\n}\n\nconst defaultReactions: ReactionItem[] = [\n { emoji: \"👏\", label: \"clap\", count: 0 },\n { emoji: \"❤️\", label: \"love\", count: 0 },\n { emoji: \"🔥\", label: \"fire\", count: 0 },\n { emoji: \"💡\", label: \"insightful\", count: 0 },\n { emoji: \"🎉\", label: \"celebrate\", count: 0 },\n];\n\nexport function ReactionBar({\n reactions: initialReactions,\n className = \"\",\n}: ReactionBarProps) {\n const [reactions, setReactions] = useState<ReactionItem[]>(\n initialReactions ?? defaultReactions\n );\n const [animatingKey, setAnimatingKey] = useState<string | null>(null);\n const timeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n const handleClick = useCallback(\n (index: number) => {\n setReactions((prev) =>\n prev.map((r, i) => {\n if (i !== index) return r;\n return {\n ...r,\n active: !r.active,\n count: r.active ? r.count - 1 : r.count + 1,\n };\n })\n );\n\n const key = reactions[index].label;\n setAnimatingKey(key);\n if (timeoutRef.current) clearTimeout(timeoutRef.current);\n timeoutRef.current = setTimeout(() => setAnimatingKey(null), 300);\n },\n [reactions]\n );\n\n return (\n <div\n className={[\"flex flex-wrap gap-2\", className].filter(Boolean).join(\" \")}\n >\n {reactions.map((reaction, index) => {\n const isAnimating = animatingKey === reaction.label;\n return (\n <button\n key={reaction.label}\n type=\"button\"\n onClick={() => handleClick(index)}\n aria-pressed={!!reaction.active}\n aria-label={`${reaction.label}, ${reaction.count}`}\n className={[\n \"inline-flex items-center gap-1.5 rounded-[11px] pl-2.5 pr-3 py-[7px] text-[13px] font-medium cursor-pointer\",\n \"focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ink\",\n reaction.active ? \"text-ink-inverse\" : \"text-ink-body hover:text-ink\",\n ].join(\" \")}\n style={{\n /* Off is a raised face; on is the dark face — pressed in and lit */\n background: reaction.active ? \"var(--face-inverse)\" : \"var(--face)\",\n boxShadow: reaction.active ? \"var(--face-inverse-shadow)\" : \"var(--face-shadow)\",\n textShadow: reaction.active ? \"var(--emboss-inverse)\" : \"var(--emboss)\",\n transform: isAnimating ? \"scale(1.08)\" : \"scale(1)\",\n transition:\n \"transform 250ms var(--ease-spring), color 200ms ease, box-shadow 200ms ease\",\n }}\n >\n <span aria-hidden className=\"text-[15px] leading-none\">{reaction.emoji}</span>\n <span\n className=\"tabular-nums leading-none\"\n style={{\n transform: isAnimating ? \"scale(1.15)\" : \"scale(1)\",\n display: \"inline-block\",\n transition:\n \"transform 250ms cubic-bezier(0.34, 1.4, 0.64, 1)\",\n }}\n >\n {reaction.count}\n </span>\n </button>\n );\n })}\n </div>\n );\n}\n"],"mappings":";;;AAEA,SAAgB,UAAU,aAAa,cAAc;AA4D3C,SAqBE,KArBF;AA9CV,IAAM,mBAAmC;AAAA,EACvC,EAAE,OAAO,aAAM,OAAO,QAAQ,OAAO,EAAE;AAAA,EACvC,EAAE,OAAO,gBAAM,OAAO,QAAQ,OAAO,EAAE;AAAA,EACvC,EAAE,OAAO,aAAM,OAAO,QAAQ,OAAO,EAAE;AAAA,EACvC,EAAE,OAAO,aAAM,OAAO,cAAc,OAAO,EAAE;AAAA,EAC7C,EAAE,OAAO,aAAM,OAAO,aAAa,OAAO,EAAE;AAC9C;AAEO,SAAS,YAAY;AAAA,EAC1B,WAAW;AAAA,EACX,YAAY;AACd,GAAqB;AACnB,QAAM,CAAC,WAAW,YAAY,IAAI;AAAA,IAChC,oBAAoB;AAAA,EACtB;AACA,QAAM,CAAC,cAAc,eAAe,IAAI,SAAwB,IAAI;AACpE,QAAM,aAAa,OAA6C,IAAI;AAEpE,QAAM,cAAc;AAAA,IAClB,CAAC,UAAkB;AACjB;AAAA,QAAa,CAAC,SACZ,KAAK,IAAI,CAAC,GAAG,MAAM;AACjB,cAAI,MAAM,MAAO,QAAO;AACxB,iBAAO;AAAA,YACL,GAAG;AAAA,YACH,QAAQ,CAAC,EAAE;AAAA,YACX,OAAO,EAAE,SAAS,EAAE,QAAQ,IAAI,EAAE,QAAQ;AAAA,UAC5C;AAAA,QACF,CAAC;AAAA,MACH;AAEA,YAAM,MAAM,UAAU,KAAK,EAAE;AAC7B,sBAAgB,GAAG;AACnB,UAAI,WAAW,QAAS,cAAa,WAAW,OAAO;AACvD,iBAAW,UAAU,WAAW,MAAM,gBAAgB,IAAI,GAAG,GAAG;AAAA,IAClE;AAAA,IACA,CAAC,SAAS;AAAA,EACZ;AAEA,SACE;AAAA,IAAC;AAAA;AAAA,MACC,WAAW,CAAC,wBAAwB,SAAS,EAAE,OAAO,OAAO,EAAE,KAAK,GAAG;AAAA,MAEtE,oBAAU,IAAI,CAAC,UAAU,UAAU;AAClC,cAAM,cAAc,iBAAiB,SAAS;AAC9C,eACE;AAAA,UAAC;AAAA;AAAA,YAEC,MAAK;AAAA,YACL,SAAS,MAAM,YAAY,KAAK;AAAA,YAChC,gBAAc,CAAC,CAAC,SAAS;AAAA,YACzB,cAAY,GAAG,SAAS,KAAK,KAAK,SAAS,KAAK;AAAA,YAChD,WAAW;AAAA,cACT;AAAA,cACA;AAAA,cACA,SAAS,SAAS,qBAAqB;AAAA,YACzC,EAAE,KAAK,GAAG;AAAA,YACV,OAAO;AAAA;AAAA,cAEL,YAAY,SAAS,SAAS,wBAAwB;AAAA,cACtD,WAAW,SAAS,SAAS,+BAA+B;AAAA,cAC5D,YAAY,SAAS,SAAS,0BAA0B;AAAA,cACxD,WAAW,cAAc,gBAAgB;AAAA,cACzC,YACE;AAAA,YACJ;AAAA,YAEA;AAAA,kCAAC,UAAK,eAAW,MAAC,WAAU,4BAA4B,mBAAS,OAAM;AAAA,cACvE;AAAA,gBAAC;AAAA;AAAA,kBACC,WAAU;AAAA,kBACV,OAAO;AAAA,oBACL,WAAW,cAAc,gBAAgB;AAAA,oBACzC,SAAS;AAAA,oBACT,YACE;AAAA,kBACJ;AAAA,kBAEC,mBAAS;AAAA;AAAA,cACZ;AAAA;AAAA;AAAA,UA/BK,SAAS;AAAA,QAgChB;AAAA,MAEJ,CAAC;AAAA;AAAA,EACH;AAEJ;","names":[]}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
// ../../components/ui/ReadingProgress.tsx
|
|
4
|
+
import { useState, useEffect } from "react";
|
|
5
|
+
import { jsx } from "react/jsx-runtime";
|
|
6
|
+
function ReadingProgress({ className = "" }) {
|
|
7
|
+
const [progress, setProgress] = useState(0);
|
|
8
|
+
useEffect(() => {
|
|
9
|
+
let frame = 0;
|
|
10
|
+
const measure = () => {
|
|
11
|
+
frame = 0;
|
|
12
|
+
const scrollTop = window.scrollY;
|
|
13
|
+
const docHeight = document.documentElement.scrollHeight - window.innerHeight;
|
|
14
|
+
setProgress(docHeight > 0 ? scrollTop / docHeight * 100 : 0);
|
|
15
|
+
};
|
|
16
|
+
const update = () => {
|
|
17
|
+
if (!frame) frame = requestAnimationFrame(measure);
|
|
18
|
+
};
|
|
19
|
+
window.addEventListener("scroll", update, { passive: true });
|
|
20
|
+
window.addEventListener("resize", update);
|
|
21
|
+
measure();
|
|
22
|
+
return () => {
|
|
23
|
+
window.removeEventListener("scroll", update);
|
|
24
|
+
window.removeEventListener("resize", update);
|
|
25
|
+
if (frame) cancelAnimationFrame(frame);
|
|
26
|
+
};
|
|
27
|
+
}, []);
|
|
28
|
+
return /* @__PURE__ */ jsx(
|
|
29
|
+
"div",
|
|
30
|
+
{
|
|
31
|
+
"aria-hidden": true,
|
|
32
|
+
className: ["fixed top-0 left-0 right-0 z-[80] h-[2px] bg-transparent pointer-events-none", className].join(" "),
|
|
33
|
+
children: /* @__PURE__ */ jsx(
|
|
34
|
+
"div",
|
|
35
|
+
{
|
|
36
|
+
className: "h-full w-full origin-left",
|
|
37
|
+
style: {
|
|
38
|
+
// transform instead of width: no layout on every scroll frame
|
|
39
|
+
transform: `scaleX(${progress / 100})`,
|
|
40
|
+
background: "var(--progress-bar)",
|
|
41
|
+
boxShadow: "var(--progress-bar-glow)"
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
)
|
|
45
|
+
}
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
export {
|
|
49
|
+
ReadingProgress
|
|
50
|
+
};
|
|
51
|
+
//# sourceMappingURL=ReadingProgress.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../components/ui/ReadingProgress.tsx"],"sourcesContent":["\"use client\";\n\nimport React, { useState, useEffect } from \"react\";\n\ninterface ReadingProgressProps {\n className?: string;\n}\n\nexport function ReadingProgress({ className = \"\" }: ReadingProgressProps) {\n const [progress, setProgress] = useState(0);\n\n useEffect(() => {\n let frame = 0;\n const measure = () => {\n frame = 0;\n const scrollTop = window.scrollY;\n const docHeight = document.documentElement.scrollHeight - window.innerHeight;\n setProgress(docHeight > 0 ? (scrollTop / docHeight) * 100 : 0);\n };\n // One measurement per frame, however fast the scroll events arrive\n const update = () => {\n if (!frame) frame = requestAnimationFrame(measure);\n };\n\n window.addEventListener(\"scroll\", update, { passive: true });\n window.addEventListener(\"resize\", update);\n measure();\n return () => {\n window.removeEventListener(\"scroll\", update);\n window.removeEventListener(\"resize\", update);\n if (frame) cancelAnimationFrame(frame);\n };\n }, []);\n\n return (\n <div\n aria-hidden\n className={[\"fixed top-0 left-0 right-0 z-[80] h-[2px] bg-transparent pointer-events-none\", className].join(\" \")}\n >\n <div\n className=\"h-full w-full origin-left\"\n style={{\n // transform instead of width: no layout on every scroll frame\n transform: `scaleX(${progress / 100})`,\n background: \"var(--progress-bar)\",\n boxShadow: \"var(--progress-bar-glow)\",\n }}\n />\n </div>\n );\n}\n"],"mappings":";;;AAEA,SAAgB,UAAU,iBAAiB;AAqCrC;AA/BC,SAAS,gBAAgB,EAAE,YAAY,GAAG,GAAyB;AACxE,QAAM,CAAC,UAAU,WAAW,IAAI,SAAS,CAAC;AAE1C,YAAU,MAAM;AACd,QAAI,QAAQ;AACZ,UAAM,UAAU,MAAM;AACpB,cAAQ;AACR,YAAM,YAAY,OAAO;AACzB,YAAM,YAAY,SAAS,gBAAgB,eAAe,OAAO;AACjE,kBAAY,YAAY,IAAK,YAAY,YAAa,MAAM,CAAC;AAAA,IAC/D;AAEA,UAAM,SAAS,MAAM;AACnB,UAAI,CAAC,MAAO,SAAQ,sBAAsB,OAAO;AAAA,IACnD;AAEA,WAAO,iBAAiB,UAAU,QAAQ,EAAE,SAAS,KAAK,CAAC;AAC3D,WAAO,iBAAiB,UAAU,MAAM;AACxC,YAAQ;AACR,WAAO,MAAM;AACX,aAAO,oBAAoB,UAAU,MAAM;AAC3C,aAAO,oBAAoB,UAAU,MAAM;AAC3C,UAAI,MAAO,sBAAqB,KAAK;AAAA,IACvC;AAAA,EACF,GAAG,CAAC,CAAC;AAEL,SACE;AAAA,IAAC;AAAA;AAAA,MACC,eAAW;AAAA,MACX,WAAW,CAAC,gFAAgF,SAAS,EAAE,KAAK,GAAG;AAAA,MAE/G;AAAA,QAAC;AAAA;AAAA,UACC,WAAU;AAAA,UACV,OAAO;AAAA;AAAA,YAEL,WAAW,UAAU,WAAW,GAAG;AAAA,YACnC,YAAY;AAAA,YACZ,WAAW;AAAA,UACb;AAAA;AAAA,MACF;AAAA;AAAA,EACF;AAEJ;","names":[]}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
|
|
3
|
+
interface ReadingTimeProps {
|
|
4
|
+
minutes: number;
|
|
5
|
+
className?: string;
|
|
6
|
+
}
|
|
7
|
+
declare function ReadingTime({ minutes, className }: ReadingTimeProps): react_jsx_runtime.JSX.Element;
|
|
8
|
+
|
|
9
|
+
export { ReadingTime };
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
// ../../components/ui/ReadingTime.tsx
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
function ReadingTime({ minutes, className = "" }) {
|
|
4
|
+
return /* @__PURE__ */ jsxs(
|
|
5
|
+
"span",
|
|
6
|
+
{
|
|
7
|
+
className: [
|
|
8
|
+
"inline-flex items-center gap-1.5 text-[12px] font-medium text-ink-muted",
|
|
9
|
+
className
|
|
10
|
+
].filter(Boolean).join(" "),
|
|
11
|
+
children: [
|
|
12
|
+
/* @__PURE__ */ jsxs(
|
|
13
|
+
"svg",
|
|
14
|
+
{
|
|
15
|
+
width: "13",
|
|
16
|
+
height: "13",
|
|
17
|
+
viewBox: "0 0 24 24",
|
|
18
|
+
fill: "none",
|
|
19
|
+
stroke: "currentColor",
|
|
20
|
+
strokeWidth: "2",
|
|
21
|
+
strokeLinecap: "round",
|
|
22
|
+
strokeLinejoin: "round",
|
|
23
|
+
className: "text-ink-faint",
|
|
24
|
+
children: [
|
|
25
|
+
/* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "10" }),
|
|
26
|
+
/* @__PURE__ */ jsx("polyline", { points: "12 6 12 12 16 14" })
|
|
27
|
+
]
|
|
28
|
+
}
|
|
29
|
+
),
|
|
30
|
+
minutes,
|
|
31
|
+
" min read"
|
|
32
|
+
]
|
|
33
|
+
}
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
export {
|
|
37
|
+
ReadingTime
|
|
38
|
+
};
|
|
39
|
+
//# sourceMappingURL=ReadingTime.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../components/ui/ReadingTime.tsx"],"sourcesContent":["import React from \"react\";\n\ninterface ReadingTimeProps {\n minutes: number;\n className?: string;\n}\n\nexport function ReadingTime({ minutes, className = \"\" }: ReadingTimeProps) {\n return (\n <span\n className={[\n \"inline-flex items-center gap-1.5 text-[12px] font-medium text-ink-muted\",\n className,\n ]\n .filter(Boolean)\n .join(\" \")}\n >\n <svg\n width=\"13\"\n height=\"13\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n className=\"text-ink-faint\"\n >\n <circle cx=\"12\" cy=\"12\" r=\"10\" />\n <polyline points=\"12 6 12 12 16 14\" />\n </svg>\n {minutes} min read\n </span>\n );\n}\n"],"mappings":";AAiBM,SAWE,KAXF;AAVC,SAAS,YAAY,EAAE,SAAS,YAAY,GAAG,GAAqB;AACzE,SACE;AAAA,IAAC;AAAA;AAAA,MACC,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MACF,EACG,OAAO,OAAO,EACd,KAAK,GAAG;AAAA,MAEX;AAAA;AAAA,UAAC;AAAA;AAAA,YACC,OAAM;AAAA,YACN,QAAO;AAAA,YACP,SAAQ;AAAA,YACR,MAAK;AAAA,YACL,QAAO;AAAA,YACP,aAAY;AAAA,YACZ,eAAc;AAAA,YACd,gBAAe;AAAA,YACf,WAAU;AAAA,YAEV;AAAA,kCAAC,YAAO,IAAG,MAAK,IAAG,MAAK,GAAE,MAAK;AAAA,cAC/B,oBAAC,cAAS,QAAO,oBAAmB;AAAA;AAAA;AAAA,QACtC;AAAA,QACC;AAAA,QAAQ;AAAA;AAAA;AAAA,EACX;AAEJ;","names":[]}
|
package/dist/Select.d.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
|
|
3
|
+
interface SelectOption {
|
|
4
|
+
value: string;
|
|
5
|
+
label: string;
|
|
6
|
+
}
|
|
7
|
+
interface SelectProps {
|
|
8
|
+
options: SelectOption[];
|
|
9
|
+
value?: string;
|
|
10
|
+
onChange?: (value: string) => void;
|
|
11
|
+
placeholder?: string;
|
|
12
|
+
label?: string;
|
|
13
|
+
hint?: string;
|
|
14
|
+
disabled?: boolean;
|
|
15
|
+
fullWidth?: boolean;
|
|
16
|
+
className?: string;
|
|
17
|
+
}
|
|
18
|
+
declare function Select({ options, value, onChange, placeholder, label, hint, disabled, fullWidth, className, }: SelectProps): react_jsx_runtime.JSX.Element;
|
|
19
|
+
|
|
20
|
+
export { Select };
|
package/dist/Select.js
ADDED
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
// ../../components/ui/Select.tsx
|
|
4
|
+
import { useState, useRef, useEffect, useId } from "react";
|
|
5
|
+
import { Portal } from "./Floating.js";
|
|
6
|
+
import { useFloatingPanel } from "./useFloatingPanel.js";
|
|
7
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
8
|
+
function Select({
|
|
9
|
+
options,
|
|
10
|
+
value,
|
|
11
|
+
onChange,
|
|
12
|
+
placeholder = "select\u2026",
|
|
13
|
+
label,
|
|
14
|
+
hint,
|
|
15
|
+
disabled = false,
|
|
16
|
+
fullWidth = false,
|
|
17
|
+
className = ""
|
|
18
|
+
}) {
|
|
19
|
+
const [active, setActive] = useState(0);
|
|
20
|
+
const ref = useRef(null);
|
|
21
|
+
const buttonRef = useRef(null);
|
|
22
|
+
const listRef = useRef(null);
|
|
23
|
+
const uid = useId();
|
|
24
|
+
const { open, mounted, close, show, setFloating, panelStyle } = useFloatingPanel(ref, {
|
|
25
|
+
matchWidth: true,
|
|
26
|
+
onEscape: () => buttonRef.current?.focus()
|
|
27
|
+
});
|
|
28
|
+
const selectedIndex = options.findIndex((o) => o.value === value);
|
|
29
|
+
const selected = options[selectedIndex];
|
|
30
|
+
useEffect(() => {
|
|
31
|
+
if (!open) return;
|
|
32
|
+
listRef.current?.querySelector(`[data-index="${active}"]`)?.scrollIntoView({ block: "nearest" });
|
|
33
|
+
}, [open, active]);
|
|
34
|
+
const openList = () => {
|
|
35
|
+
setActive(selectedIndex >= 0 ? selectedIndex : 0);
|
|
36
|
+
show();
|
|
37
|
+
};
|
|
38
|
+
const pick = (index) => {
|
|
39
|
+
const opt = options[index];
|
|
40
|
+
if (!opt) return;
|
|
41
|
+
onChange?.(opt.value);
|
|
42
|
+
close();
|
|
43
|
+
buttonRef.current?.focus();
|
|
44
|
+
};
|
|
45
|
+
const onKeyDown = (e) => {
|
|
46
|
+
if (!open) {
|
|
47
|
+
if (["ArrowDown", "ArrowUp", "Enter", " "].includes(e.key)) {
|
|
48
|
+
e.preventDefault();
|
|
49
|
+
openList();
|
|
50
|
+
}
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
switch (e.key) {
|
|
54
|
+
case "ArrowDown":
|
|
55
|
+
e.preventDefault();
|
|
56
|
+
setActive((i) => Math.min(i + 1, options.length - 1));
|
|
57
|
+
break;
|
|
58
|
+
case "ArrowUp":
|
|
59
|
+
e.preventDefault();
|
|
60
|
+
setActive((i) => Math.max(i - 1, 0));
|
|
61
|
+
break;
|
|
62
|
+
case "Home":
|
|
63
|
+
e.preventDefault();
|
|
64
|
+
setActive(0);
|
|
65
|
+
break;
|
|
66
|
+
case "End":
|
|
67
|
+
e.preventDefault();
|
|
68
|
+
setActive(options.length - 1);
|
|
69
|
+
break;
|
|
70
|
+
case "Enter":
|
|
71
|
+
case " ":
|
|
72
|
+
e.preventDefault();
|
|
73
|
+
pick(active);
|
|
74
|
+
break;
|
|
75
|
+
case "Escape":
|
|
76
|
+
e.preventDefault();
|
|
77
|
+
close();
|
|
78
|
+
break;
|
|
79
|
+
case "Tab":
|
|
80
|
+
close();
|
|
81
|
+
break;
|
|
82
|
+
default:
|
|
83
|
+
if (e.key.length === 1) {
|
|
84
|
+
const k = e.key.toLowerCase();
|
|
85
|
+
const hit = options.findIndex((o) => o.label.toLowerCase().startsWith(k));
|
|
86
|
+
if (hit >= 0) setActive(hit);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
return /* @__PURE__ */ jsxs("div", { className: ["flex flex-col gap-1.5", fullWidth ? "w-full" : ""].join(" "), children: [
|
|
91
|
+
label && /* @__PURE__ */ jsx(
|
|
92
|
+
"span",
|
|
93
|
+
{
|
|
94
|
+
id: `${uid}-label`,
|
|
95
|
+
className: "text-[12.5px] font-medium text-ink-label tracking-[-0.005em]",
|
|
96
|
+
children: label
|
|
97
|
+
}
|
|
98
|
+
),
|
|
99
|
+
/* @__PURE__ */ jsxs(
|
|
100
|
+
"div",
|
|
101
|
+
{
|
|
102
|
+
ref,
|
|
103
|
+
className: ["relative", fullWidth ? "w-full" : "inline-block", className].join(" "),
|
|
104
|
+
children: [
|
|
105
|
+
/* @__PURE__ */ jsxs(
|
|
106
|
+
"button",
|
|
107
|
+
{
|
|
108
|
+
ref: buttonRef,
|
|
109
|
+
type: "button",
|
|
110
|
+
role: "combobox",
|
|
111
|
+
disabled,
|
|
112
|
+
onClick: () => open ? close() : openList(),
|
|
113
|
+
onKeyDown,
|
|
114
|
+
"aria-haspopup": "listbox",
|
|
115
|
+
"aria-expanded": open,
|
|
116
|
+
"aria-controls": open ? `${uid}-list` : void 0,
|
|
117
|
+
"aria-labelledby": label ? `${uid}-label ${uid}-value` : void 0,
|
|
118
|
+
"aria-activedescendant": open ? `${uid}-opt-${active}` : void 0,
|
|
119
|
+
className: [
|
|
120
|
+
"group/select relative isolate inline-flex items-center justify-between gap-3 w-full",
|
|
121
|
+
"rounded-xl px-4 py-3 text-[13.5px] font-medium text-ink",
|
|
122
|
+
"field cursor-pointer select-none",
|
|
123
|
+
"focus:outline-none disabled:opacity-40 disabled:pointer-events-none"
|
|
124
|
+
].join(" "),
|
|
125
|
+
"data-open": open || void 0,
|
|
126
|
+
children: [
|
|
127
|
+
/* @__PURE__ */ jsx(
|
|
128
|
+
"span",
|
|
129
|
+
{
|
|
130
|
+
id: `${uid}-value`,
|
|
131
|
+
className: [
|
|
132
|
+
"relative z-10 truncate",
|
|
133
|
+
selected ? "text-ink" : "text-ink-faint font-normal"
|
|
134
|
+
].join(" "),
|
|
135
|
+
children: selected ? selected.label : placeholder
|
|
136
|
+
}
|
|
137
|
+
),
|
|
138
|
+
/* @__PURE__ */ jsx(
|
|
139
|
+
"svg",
|
|
140
|
+
{
|
|
141
|
+
"aria-hidden": true,
|
|
142
|
+
width: "14",
|
|
143
|
+
height: "14",
|
|
144
|
+
fill: "none",
|
|
145
|
+
stroke: "currentColor",
|
|
146
|
+
strokeWidth: "2",
|
|
147
|
+
viewBox: "0 0 24 24",
|
|
148
|
+
className: [
|
|
149
|
+
"relative z-10 shrink-0 transition-[transform,color] duration-300 ease-[var(--ease-spring)]",
|
|
150
|
+
open ? "rotate-180 text-ink" : "text-ink-faint group-hover/select:text-ink-label"
|
|
151
|
+
].join(" "),
|
|
152
|
+
children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M19 9l-7 7-7-7" })
|
|
153
|
+
}
|
|
154
|
+
),
|
|
155
|
+
open && /* @__PURE__ */ jsx("span", { "aria-hidden": true, className: "pointer-events-none absolute inset-0 z-5 rounded-xl overflow-hidden", children: /* @__PURE__ */ jsx(
|
|
156
|
+
"span",
|
|
157
|
+
{
|
|
158
|
+
className: "block absolute inset-y-0 w-[30%] -left-[30%] animate-input-shimmer",
|
|
159
|
+
style: {
|
|
160
|
+
background: "linear-gradient(90deg, transparent 0%, var(--shimmer) 50%, transparent 100%)"
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
) })
|
|
164
|
+
]
|
|
165
|
+
}
|
|
166
|
+
),
|
|
167
|
+
mounted && /* @__PURE__ */ jsx(Portal, { children: /* @__PURE__ */ jsx(
|
|
168
|
+
"div",
|
|
169
|
+
{
|
|
170
|
+
ref: setFloating,
|
|
171
|
+
className: "p-1.5 rounded-[16px]",
|
|
172
|
+
style: {
|
|
173
|
+
...panelStyle,
|
|
174
|
+
maxWidth: "min(420px, calc(100vw - 2rem))",
|
|
175
|
+
width: "max-content"
|
|
176
|
+
},
|
|
177
|
+
children: /* @__PURE__ */ jsx(
|
|
178
|
+
"div",
|
|
179
|
+
{
|
|
180
|
+
ref: listRef,
|
|
181
|
+
id: `${uid}-list`,
|
|
182
|
+
role: "listbox",
|
|
183
|
+
"aria-labelledby": label ? `${uid}-label` : void 0,
|
|
184
|
+
className: "bg-panel-face rounded-[11px] p-1 flex flex-col gap-0.5 max-h-72 overflow-y-auto scroll-thin",
|
|
185
|
+
style: { boxShadow: "var(--panel-face-shadow)" },
|
|
186
|
+
children: options.map((opt, i) => {
|
|
187
|
+
const isSelected = opt.value === value;
|
|
188
|
+
const isActive = i === active;
|
|
189
|
+
return /* @__PURE__ */ jsxs(
|
|
190
|
+
"div",
|
|
191
|
+
{
|
|
192
|
+
id: `${uid}-opt-${i}`,
|
|
193
|
+
"data-index": i,
|
|
194
|
+
role: "option",
|
|
195
|
+
"aria-selected": isSelected,
|
|
196
|
+
onMouseDown: (e) => e.preventDefault(),
|
|
197
|
+
onClick: () => pick(i),
|
|
198
|
+
onMouseMove: () => setActive(i),
|
|
199
|
+
className: [
|
|
200
|
+
"flex items-center justify-between gap-3 w-full px-2.5 py-2 text-[13px] rounded-[8px] cursor-pointer",
|
|
201
|
+
"transition-colors duration-100",
|
|
202
|
+
isSelected ? "font-medium" : "",
|
|
203
|
+
// one ink per state: utilities sort by name, so text-ink-body would beat text-ink
|
|
204
|
+
isSelected || isActive ? "text-ink" : "text-ink-body",
|
|
205
|
+
isActive ? "bg-surface-active/80" : ""
|
|
206
|
+
].join(" "),
|
|
207
|
+
style: {
|
|
208
|
+
animation: `select-option-in 300ms var(--ease-out-soft) ${40 + i * 22}ms both`
|
|
209
|
+
},
|
|
210
|
+
children: [
|
|
211
|
+
/* @__PURE__ */ jsx("span", { className: "whitespace-nowrap", children: opt.label }),
|
|
212
|
+
isSelected && /* @__PURE__ */ jsx(
|
|
213
|
+
"svg",
|
|
214
|
+
{
|
|
215
|
+
"aria-hidden": true,
|
|
216
|
+
className: "shrink-0 text-ink",
|
|
217
|
+
width: "13",
|
|
218
|
+
height: "13",
|
|
219
|
+
fill: "none",
|
|
220
|
+
stroke: "currentColor",
|
|
221
|
+
strokeWidth: "2.5",
|
|
222
|
+
viewBox: "0 0 24 24",
|
|
223
|
+
children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M5 13l4 4L19 7" })
|
|
224
|
+
}
|
|
225
|
+
)
|
|
226
|
+
]
|
|
227
|
+
},
|
|
228
|
+
opt.value
|
|
229
|
+
);
|
|
230
|
+
})
|
|
231
|
+
}
|
|
232
|
+
)
|
|
233
|
+
}
|
|
234
|
+
) })
|
|
235
|
+
]
|
|
236
|
+
}
|
|
237
|
+
),
|
|
238
|
+
hint && /* @__PURE__ */ jsx("p", { className: "text-[11.5px] text-ink-muted mt-0.5", children: hint })
|
|
239
|
+
] });
|
|
240
|
+
}
|
|
241
|
+
export {
|
|
242
|
+
Select
|
|
243
|
+
};
|
|
244
|
+
//# sourceMappingURL=Select.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../components/ui/Select.tsx"],"sourcesContent":["\"use client\";\n\nimport React, { useState, useRef, useEffect, useId } from \"react\";\nimport { Portal } from \"./Floating\";\nimport { useFloatingPanel } from \"./useFloatingPanel\";\n\ninterface SelectOption {\n value: string;\n label: string;\n}\n\ninterface SelectProps {\n options: SelectOption[];\n value?: string;\n onChange?: (value: string) => void;\n placeholder?: string;\n label?: string;\n hint?: string;\n disabled?: boolean;\n fullWidth?: boolean;\n className?: string;\n}\n\n/* The closed select is a sunken field like Input; the open list is a\n floating panel like DropdownMenu. Keyboard follows the ARIA listbox\n pattern: arrows move, Enter picks, Escape backs out. */\nexport function Select({\n options,\n value,\n onChange,\n placeholder = \"select…\",\n label,\n hint,\n disabled = false,\n fullWidth = false,\n className = \"\",\n}: SelectProps) {\n const [active, setActive] = useState(0);\n const ref = useRef<HTMLDivElement>(null);\n const buttonRef = useRef<HTMLButtonElement>(null);\n const listRef = useRef<HTMLDivElement>(null);\n const uid = useId();\n // The list is portalled to the body and pinned under the field\n const { open, mounted, close, show, setFloating, panelStyle } = useFloatingPanel(ref, {\n matchWidth: true,\n onEscape: () => buttonRef.current?.focus(),\n });\n\n const selectedIndex = options.findIndex((o) => o.value === value);\n const selected = options[selectedIndex];\n\n useEffect(() => {\n if (!open) return;\n listRef.current\n ?.querySelector(`[data-index=\"${active}\"]`)\n ?.scrollIntoView({ block: \"nearest\" });\n }, [open, active]);\n\n const openList = () => {\n setActive(selectedIndex >= 0 ? selectedIndex : 0);\n show();\n };\n\n const pick = (index: number) => {\n const opt = options[index];\n if (!opt) return;\n onChange?.(opt.value);\n close();\n buttonRef.current?.focus();\n };\n\n const onKeyDown = (e: React.KeyboardEvent) => {\n if (!open) {\n if ([\"ArrowDown\", \"ArrowUp\", \"Enter\", \" \"].includes(e.key)) {\n e.preventDefault();\n openList();\n }\n return;\n }\n switch (e.key) {\n case \"ArrowDown\":\n e.preventDefault();\n setActive((i) => Math.min(i + 1, options.length - 1));\n break;\n case \"ArrowUp\":\n e.preventDefault();\n setActive((i) => Math.max(i - 1, 0));\n break;\n case \"Home\":\n e.preventDefault();\n setActive(0);\n break;\n case \"End\":\n e.preventDefault();\n setActive(options.length - 1);\n break;\n case \"Enter\":\n case \" \":\n e.preventDefault();\n pick(active);\n break;\n case \"Escape\":\n e.preventDefault();\n close();\n break;\n case \"Tab\":\n close();\n break;\n default:\n // Type-ahead: jump to the first option starting with the key\n if (e.key.length === 1) {\n const k = e.key.toLowerCase();\n const hit = options.findIndex((o) => o.label.toLowerCase().startsWith(k));\n if (hit >= 0) setActive(hit);\n }\n }\n };\n\n return (\n <div className={[\"flex flex-col gap-1.5\", fullWidth ? \"w-full\" : \"\"].join(\" \")}>\n {label && (\n <span\n id={`${uid}-label`}\n className=\"text-[12.5px] font-medium text-ink-label tracking-[-0.005em]\"\n >\n {label}\n </span>\n )}\n\n <div\n ref={ref}\n className={[\"relative\", fullWidth ? \"w-full\" : \"inline-block\", className].join(\" \")}\n >\n <button\n ref={buttonRef}\n type=\"button\"\n /* A select-only combobox: the role is what lets the button point\n at the active option while focus stays on it */\n role=\"combobox\"\n disabled={disabled}\n onClick={() => (open ? close() : openList())}\n onKeyDown={onKeyDown}\n aria-haspopup=\"listbox\"\n aria-expanded={open}\n aria-controls={open ? `${uid}-list` : undefined}\n aria-labelledby={label ? `${uid}-label ${uid}-value` : undefined}\n aria-activedescendant={open ? `${uid}-opt-${active}` : undefined}\n className={[\n \"group/select relative isolate inline-flex items-center justify-between gap-3 w-full\",\n \"rounded-xl px-4 py-3 text-[13.5px] font-medium text-ink\",\n \"field cursor-pointer select-none\",\n \"focus:outline-none disabled:opacity-40 disabled:pointer-events-none\",\n ].join(\" \")}\n data-open={open || undefined}\n >\n <span\n id={`${uid}-value`}\n className={[\n \"relative z-10 truncate\",\n selected ? \"text-ink\" : \"text-ink-faint font-normal\",\n ].join(\" \")}\n >\n {selected ? selected.label : placeholder}\n </span>\n <svg\n aria-hidden\n width=\"14\"\n height=\"14\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n viewBox=\"0 0 24 24\"\n className={[\n \"relative z-10 shrink-0 transition-[transform,color] duration-300 ease-[var(--ease-spring)]\",\n open ? \"rotate-180 text-ink\" : \"text-ink-faint group-hover/select:text-ink-label\",\n ].join(\" \")}\n >\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M19 9l-7 7-7-7\" />\n </svg>\n\n {open && (\n <span aria-hidden className=\"pointer-events-none absolute inset-0 z-5 rounded-xl overflow-hidden\">\n <span\n className=\"block absolute inset-y-0 w-[30%] -left-[30%] animate-input-shimmer\"\n style={{\n background:\n \"linear-gradient(90deg, transparent 0%, var(--shimmer) 50%, transparent 100%)\",\n }}\n />\n </span>\n )}\n </button>\n\n {mounted && (\n <Portal>\n <div\n ref={setFloating}\n className=\"p-1.5 rounded-[16px]\"\n style={{\n ...panelStyle,\n maxWidth: \"min(420px, calc(100vw - 2rem))\",\n width: \"max-content\",\n }}\n >\n <div\n ref={listRef}\n id={`${uid}-list`}\n role=\"listbox\"\n aria-labelledby={label ? `${uid}-label` : undefined}\n className=\"bg-panel-face rounded-[11px] p-1 flex flex-col gap-0.5 max-h-72 overflow-y-auto scroll-thin\"\n style={{ boxShadow: \"var(--panel-face-shadow)\" }}\n >\n {options.map((opt, i) => {\n const isSelected = opt.value === value;\n const isActive = i === active;\n return (\n <div\n key={opt.value}\n id={`${uid}-opt-${i}`}\n data-index={i}\n role=\"option\"\n aria-selected={isSelected}\n onMouseDown={(e) => e.preventDefault()}\n onClick={() => pick(i)}\n onMouseMove={() => setActive(i)}\n className={[\n \"flex items-center justify-between gap-3 w-full px-2.5 py-2 text-[13px] rounded-[8px] cursor-pointer\",\n \"transition-colors duration-100\",\n isSelected ? \"font-medium\" : \"\",\n // one ink per state: utilities sort by name, so text-ink-body would beat text-ink\n isSelected || isActive ? \"text-ink\" : \"text-ink-body\",\n isActive ? \"bg-surface-active/80\" : \"\",\n ].join(\" \")}\n style={{\n animation: `select-option-in 300ms var(--ease-out-soft) ${40 + i * 22}ms both`,\n }}\n >\n <span className=\"whitespace-nowrap\">{opt.label}</span>\n {isSelected && (\n <svg\n aria-hidden\n className=\"shrink-0 text-ink\"\n width=\"13\"\n height=\"13\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2.5\"\n viewBox=\"0 0 24 24\"\n >\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M5 13l4 4L19 7\" />\n </svg>\n )}\n </div>\n );\n })}\n </div>\n </div>\n </Portal>\n )}\n </div>\n\n {hint && <p className=\"text-[11.5px] text-ink-muted mt-0.5\">{hint}</p>}\n </div>\n );\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
|
|
4
|
+
interface SeparatorProps {
|
|
5
|
+
orientation?: "horizontal" | "vertical";
|
|
6
|
+
/** A few words set into the middle of the line, like "or" */
|
|
7
|
+
label?: React.ReactNode;
|
|
8
|
+
/** Only draws a line; screen readers skip it */
|
|
9
|
+
decorative?: boolean;
|
|
10
|
+
className?: string;
|
|
11
|
+
}
|
|
12
|
+
declare function Separator({ orientation, label, decorative, className }: SeparatorProps): react_jsx_runtime.JSX.Element;
|
|
13
|
+
|
|
14
|
+
export { Separator };
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
// ../../components/ui/Separator.tsx
|
|
2
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
function Groove({ vertical }) {
|
|
4
|
+
return /* @__PURE__ */ jsx(
|
|
5
|
+
"span",
|
|
6
|
+
{
|
|
7
|
+
"aria-hidden": true,
|
|
8
|
+
className: vertical ? "block w-px self-stretch" : "block h-px flex-1",
|
|
9
|
+
style: {
|
|
10
|
+
background: "var(--groove)",
|
|
11
|
+
boxShadow: vertical ? "var(--groove-lip)" : "var(--groove-lip-below)"
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
);
|
|
15
|
+
}
|
|
16
|
+
function Separator({ orientation = "horizontal", label, decorative = false, className = "" }) {
|
|
17
|
+
const vertical = orientation === "vertical";
|
|
18
|
+
const a11y = decorative ? { "aria-hidden": true } : { role: "separator", "aria-orientation": orientation };
|
|
19
|
+
if (vertical) {
|
|
20
|
+
return /* @__PURE__ */ jsx("span", { ...a11y, className: ["inline-flex self-stretch", className].join(" "), children: /* @__PURE__ */ jsx(Groove, { vertical: true }) });
|
|
21
|
+
}
|
|
22
|
+
return /* @__PURE__ */ jsxs("div", { ...a11y, className: ["flex w-full items-center gap-3", className].join(" "), children: [
|
|
23
|
+
/* @__PURE__ */ jsx(Groove, { vertical: false }),
|
|
24
|
+
label && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
25
|
+
/* @__PURE__ */ jsx("span", { className: "shrink-0 text-[11.5px] font-medium text-ink-faint", children: label }),
|
|
26
|
+
/* @__PURE__ */ jsx(Groove, { vertical: false })
|
|
27
|
+
] })
|
|
28
|
+
] });
|
|
29
|
+
}
|
|
30
|
+
export {
|
|
31
|
+
Separator
|
|
32
|
+
};
|
|
33
|
+
//# sourceMappingURL=Separator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../components/ui/Separator.tsx"],"sourcesContent":["import React from \"react\";\n\ninterface SeparatorProps {\n orientation?: \"horizontal\" | \"vertical\";\n /** A few words set into the middle of the line, like \"or\" */\n label?: React.ReactNode;\n /** Only draws a line; screen readers skip it */\n decorative?: boolean;\n className?: string;\n}\n\n/* A groove scored into the surface: a dark line with a lit lip just\n under it, the way a cut catches light from above. */\nfunction Groove({ vertical }: { vertical: boolean }) {\n return (\n <span\n aria-hidden\n className={vertical ? \"block w-px self-stretch\" : \"block h-px flex-1\"}\n style={{\n background: \"var(--groove)\",\n boxShadow: vertical ? \"var(--groove-lip)\" : \"var(--groove-lip-below)\",\n }}\n />\n );\n}\n\nexport function Separator({ orientation = \"horizontal\", label, decorative = false, className = \"\" }: SeparatorProps) {\n const vertical = orientation === \"vertical\";\n const a11y = decorative\n ? { \"aria-hidden\": true as const }\n : { role: \"separator\", \"aria-orientation\": orientation };\n\n if (vertical) {\n return (\n <span {...a11y} className={[\"inline-flex self-stretch\", className].join(\" \")}>\n <Groove vertical />\n </span>\n );\n }\n\n return (\n <div {...a11y} className={[\"flex w-full items-center gap-3\", className].join(\" \")}>\n <Groove vertical={false} />\n {label && (\n <>\n <span className=\"shrink-0 text-[11.5px] font-medium text-ink-faint\">{label}</span>\n <Groove vertical={false} />\n </>\n )}\n </div>\n );\n}\n"],"mappings":";AAeI,SA6BI,UA7BJ,KA6BI,YA7BJ;AAFJ,SAAS,OAAO,EAAE,SAAS,GAA0B;AACnD,SACE;AAAA,IAAC;AAAA;AAAA,MACC,eAAW;AAAA,MACX,WAAW,WAAW,4BAA4B;AAAA,MAClD,OAAO;AAAA,QACL,YAAY;AAAA,QACZ,WAAW,WAAW,sBAAsB;AAAA,MAC9C;AAAA;AAAA,EACF;AAEJ;AAEO,SAAS,UAAU,EAAE,cAAc,cAAc,OAAO,aAAa,OAAO,YAAY,GAAG,GAAmB;AACnH,QAAM,WAAW,gBAAgB;AACjC,QAAM,OAAO,aACT,EAAE,eAAe,KAAc,IAC/B,EAAE,MAAM,aAAa,oBAAoB,YAAY;AAEzD,MAAI,UAAU;AACZ,WACE,oBAAC,UAAM,GAAG,MAAM,WAAW,CAAC,4BAA4B,SAAS,EAAE,KAAK,GAAG,GACzE,8BAAC,UAAO,UAAQ,MAAC,GACnB;AAAA,EAEJ;AAEA,SACE,qBAAC,SAAK,GAAG,MAAM,WAAW,CAAC,kCAAkC,SAAS,EAAE,KAAK,GAAG,GAC9E;AAAA,wBAAC,UAAO,UAAU,OAAO;AAAA,IACxB,SACC,iCACE;AAAA,0BAAC,UAAK,WAAU,qDAAqD,iBAAM;AAAA,MAC3E,oBAAC,UAAO,UAAU,OAAO;AAAA,OAC3B;AAAA,KAEJ;AAEJ;","names":[]}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
|
|
3
|
+
interface ShareButtonsProps {
|
|
4
|
+
url?: string;
|
|
5
|
+
title?: string;
|
|
6
|
+
className?: string;
|
|
7
|
+
}
|
|
8
|
+
declare function ShareButtons({ url, title, className, }: ShareButtonsProps): react_jsx_runtime.JSX.Element;
|
|
9
|
+
|
|
10
|
+
export { ShareButtons };
|