@blurrow/ui 0.2.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 +48 -36
- 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 +172 -5
- 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 +6 -6
- package/dist/registry/Alert.tsx +13 -10
- package/dist/registry/AuthorCard.tsx +7 -7
- package/dist/registry/Avatar.tsx +5 -5
- package/dist/registry/Badge.tsx +12 -15
- package/dist/registry/Banner.tsx +15 -18
- package/dist/registry/Breadcrumb.tsx +3 -3
- package/dist/registry/Button.tsx +58 -69
- package/dist/registry/ButtonGroup.tsx +9 -9
- package/dist/registry/Card.tsx +15 -15
- package/dist/registry/CardShell.tsx +6 -6
- package/dist/registry/Carousel.tsx +19 -15
- package/dist/registry/Checkbox.tsx +14 -9
- package/dist/registry/CloseButton.tsx +2 -2
- package/dist/registry/CodeBlock.tsx +15 -15
- package/dist/registry/ColorPicker.tsx +404 -0
- package/dist/registry/Combobox.tsx +318 -0
- package/dist/registry/CommandPalette.tsx +31 -24
- package/dist/registry/ContextMenu.tsx +241 -0
- package/dist/registry/DataList.tsx +4 -4
- package/dist/registry/DatePicker.tsx +494 -0
- package/dist/registry/DateRangePicker.tsx +232 -0
- package/dist/registry/Drawer.tsx +17 -15
- package/dist/registry/DropdownMenu.tsx +39 -77
- package/dist/registry/EmptyState.tsx +8 -8
- 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 +54 -43
- package/dist/registry/Kbd.tsx +4 -5
- package/dist/registry/Modal.tsx +17 -15
- package/dist/registry/NumberInput.tsx +183 -0
- package/dist/registry/OtpInput.tsx +219 -0
- package/dist/registry/Pagination.tsx +15 -18
- package/dist/registry/Popover.tsx +40 -45
- package/dist/registry/Progress.tsx +4 -4
- package/dist/registry/QuoteCard.tsx +7 -7
- package/dist/registry/Radio.tsx +24 -21
- package/dist/registry/RadioCards.tsx +247 -0
- package/dist/registry/ReactionBar.tsx +5 -5
- package/dist/registry/ReadingProgress.tsx +2 -2
- package/dist/registry/ReadingTime.tsx +2 -2
- package/dist/registry/Select.tsx +28 -53
- package/dist/registry/Separator.tsx +52 -0
- package/dist/registry/ShareButtons.tsx +2 -2
- package/dist/registry/Sidebar.tsx +18 -18
- package/dist/registry/Skeleton.tsx +1 -1
- package/dist/registry/Slider.tsx +229 -0
- package/dist/registry/Spinner.tsx +32 -0
- package/dist/registry/SpotlightCard.tsx +6 -6
- package/dist/registry/Stat.tsx +4 -4
- package/dist/registry/StatCard.tsx +19 -19
- package/dist/registry/Steps.tsx +7 -7
- package/dist/registry/Table.tsx +12 -10
- package/dist/registry/TableOfContents.tsx +8 -8
- package/dist/registry/Tabs.tsx +6 -6
- package/dist/registry/TagInput.tsx +9 -9
- package/dist/registry/Textarea.tsx +5 -5
- package/dist/registry/TimePicker.tsx +315 -0
- package/dist/registry/Timeline.tsx +14 -14
- package/dist/registry/Toast.tsx +15 -12
- package/dist/registry/Toggle.tsx +7 -10
- package/dist/registry/Tooltip.tsx +21 -73
- package/dist/registry/styles.css +868 -62
- package/dist/registry/useFloatingPanel.ts +112 -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 +1 -1
package/dist/Card.d.ts
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
|
|
4
|
+
type CardTheme = "light" | "dark";
|
|
5
|
+
interface SplitCardProps {
|
|
6
|
+
title?: string;
|
|
7
|
+
subtitle?: string;
|
|
8
|
+
icon?: React.ReactNode;
|
|
9
|
+
image?: React.ReactNode;
|
|
10
|
+
imageSrc?: string;
|
|
11
|
+
imageAlt?: string;
|
|
12
|
+
actions?: React.ReactNode;
|
|
13
|
+
theme?: CardTheme;
|
|
14
|
+
className?: string;
|
|
15
|
+
children?: React.ReactNode;
|
|
16
|
+
layout?: "split";
|
|
17
|
+
}
|
|
18
|
+
interface StackCardProps {
|
|
19
|
+
title?: string;
|
|
20
|
+
badge?: React.ReactNode;
|
|
21
|
+
subtitle?: string;
|
|
22
|
+
note?: string;
|
|
23
|
+
image?: React.ReactNode;
|
|
24
|
+
imageSrc?: string;
|
|
25
|
+
imageAlt?: string;
|
|
26
|
+
imageBg?: string;
|
|
27
|
+
actions?: React.ReactNode;
|
|
28
|
+
className?: string;
|
|
29
|
+
children?: React.ReactNode;
|
|
30
|
+
layout: "stack";
|
|
31
|
+
}
|
|
32
|
+
type CardProps = SplitCardProps | StackCardProps;
|
|
33
|
+
declare function Card(props: CardProps): react_jsx_runtime.JSX.Element;
|
|
34
|
+
interface CardGridProps {
|
|
35
|
+
children: React.ReactNode;
|
|
36
|
+
className?: string;
|
|
37
|
+
}
|
|
38
|
+
declare function CardGrid({ children, className }: CardGridProps): react_jsx_runtime.JSX.Element;
|
|
39
|
+
|
|
40
|
+
export { Card, CardGrid };
|
package/dist/Card.js
ADDED
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
// ../../components/ui/Card.tsx
|
|
2
|
+
import { CardShell } from "./CardShell.js";
|
|
3
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
4
|
+
function MediaPlaceholder({ dark, bg, monogram }) {
|
|
5
|
+
return /* @__PURE__ */ jsxs(
|
|
6
|
+
"div",
|
|
7
|
+
{
|
|
8
|
+
className: ["relative w-full h-full flex items-center justify-center", bg ?? ""].join(" "),
|
|
9
|
+
style: bg ? void 0 : { background: dark ? "var(--media-well-dark)" : "var(--media-well)" },
|
|
10
|
+
children: [
|
|
11
|
+
/* @__PURE__ */ jsx(
|
|
12
|
+
"div",
|
|
13
|
+
{
|
|
14
|
+
"aria-hidden": true,
|
|
15
|
+
className: "absolute inset-0",
|
|
16
|
+
style: {
|
|
17
|
+
backgroundImage: `radial-gradient(${bg ? "var(--media-dots-on-colour)" : dark ? "var(--media-dots-dark)" : "var(--media-dots)"} 1px, transparent 1px)`,
|
|
18
|
+
backgroundSize: "12px 12px",
|
|
19
|
+
maskImage: "radial-gradient(70% 70% at 50% 50%, black 0%, transparent 100%)",
|
|
20
|
+
WebkitMaskImage: "radial-gradient(70% 70% at 50% 50%, black 0%, transparent 100%)"
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
),
|
|
24
|
+
monogram && /* @__PURE__ */ jsx(
|
|
25
|
+
"span",
|
|
26
|
+
{
|
|
27
|
+
"aria-hidden": true,
|
|
28
|
+
className: [
|
|
29
|
+
"absolute -right-3 -bottom-[0.32em] text-[220px] font-semibold leading-none tracking-[-0.06em] select-none",
|
|
30
|
+
dark ? "text-white/[0.045]" : "text-ink/[0.045]"
|
|
31
|
+
].join(" "),
|
|
32
|
+
children: monogram
|
|
33
|
+
}
|
|
34
|
+
)
|
|
35
|
+
]
|
|
36
|
+
}
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
function Media({
|
|
40
|
+
image,
|
|
41
|
+
imageSrc,
|
|
42
|
+
imageAlt,
|
|
43
|
+
dark,
|
|
44
|
+
bg,
|
|
45
|
+
monogram
|
|
46
|
+
}) {
|
|
47
|
+
if (image) return /* @__PURE__ */ jsx(Fragment, { children: image });
|
|
48
|
+
if (imageSrc) {
|
|
49
|
+
return /* @__PURE__ */ jsx("img", { src: imageSrc, alt: imageAlt, loading: "lazy", className: "w-full h-full object-cover" });
|
|
50
|
+
}
|
|
51
|
+
return /* @__PURE__ */ jsx(MediaPlaceholder, { dark, bg, monogram });
|
|
52
|
+
}
|
|
53
|
+
function IconTile({ dark, children }) {
|
|
54
|
+
return /* @__PURE__ */ jsx(
|
|
55
|
+
"div",
|
|
56
|
+
{
|
|
57
|
+
"aria-hidden": true,
|
|
58
|
+
className: [
|
|
59
|
+
"w-9 h-9 rounded-[11px] flex items-center justify-center shrink-0",
|
|
60
|
+
dark ? "text-neutral-100" : "text-ink-label"
|
|
61
|
+
].join(" "),
|
|
62
|
+
style: {
|
|
63
|
+
background: dark ? "var(--tile-dark)" : "var(--tile)",
|
|
64
|
+
boxShadow: dark ? "var(--tile-dark-shadow)" : "var(--tile-shadow)"
|
|
65
|
+
},
|
|
66
|
+
children
|
|
67
|
+
}
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
function Card(props) {
|
|
71
|
+
if (props.layout === "stack") {
|
|
72
|
+
const {
|
|
73
|
+
title: title2,
|
|
74
|
+
badge,
|
|
75
|
+
subtitle: subtitle2,
|
|
76
|
+
note,
|
|
77
|
+
image: image2,
|
|
78
|
+
imageSrc: imageSrc2,
|
|
79
|
+
imageAlt: imageAlt2 = "",
|
|
80
|
+
imageBg,
|
|
81
|
+
actions: actions2,
|
|
82
|
+
className: className2 = "",
|
|
83
|
+
children: children2
|
|
84
|
+
} = props;
|
|
85
|
+
return /* @__PURE__ */ jsxs(
|
|
86
|
+
CardShell,
|
|
87
|
+
{
|
|
88
|
+
as: "article",
|
|
89
|
+
className: className2,
|
|
90
|
+
rail: actions2,
|
|
91
|
+
children: [
|
|
92
|
+
/* @__PURE__ */ jsx("div", { className: "relative h-52 shrink-0 overflow-hidden", style: { boxShadow: "0 1px 0 var(--divider)" }, children: /* @__PURE__ */ jsx(Media, { image: image2, imageSrc: imageSrc2, imageAlt: imageAlt2, bg: imageBg }) }),
|
|
93
|
+
/* @__PURE__ */ jsxs("div", { className: "flex-1 flex flex-col p-6", children: [
|
|
94
|
+
(title2 || badge) && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
95
|
+
title2 && /* @__PURE__ */ jsx("h3", { className: "text-[17px] font-semibold text-ink tracking-[-0.015em] leading-snug", children: title2 }),
|
|
96
|
+
badge
|
|
97
|
+
] }),
|
|
98
|
+
subtitle2 && // Line breaks in the copy are for centred layouts; let it flow here
|
|
99
|
+
/* @__PURE__ */ jsx("p", { className: "mt-1.5 text-[14px] text-ink-body leading-[1.55] text-pretty", children: subtitle2.replace(/\n/g, " ") }),
|
|
100
|
+
children2,
|
|
101
|
+
note && /* @__PURE__ */ jsx("p", { className: "mt-auto pt-5 text-[12px] text-ink-muted leading-relaxed text-pretty", children: note })
|
|
102
|
+
] })
|
|
103
|
+
]
|
|
104
|
+
}
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
const {
|
|
108
|
+
title,
|
|
109
|
+
subtitle,
|
|
110
|
+
icon,
|
|
111
|
+
image,
|
|
112
|
+
imageSrc,
|
|
113
|
+
imageAlt = "",
|
|
114
|
+
actions,
|
|
115
|
+
theme = "light",
|
|
116
|
+
className = "",
|
|
117
|
+
children
|
|
118
|
+
} = props;
|
|
119
|
+
const dark = theme === "dark";
|
|
120
|
+
return /* @__PURE__ */ jsxs(
|
|
121
|
+
CardShell,
|
|
122
|
+
{
|
|
123
|
+
as: "article",
|
|
124
|
+
theme: dark ? "dark" : "light",
|
|
125
|
+
className,
|
|
126
|
+
faceClassName: "flex-row! min-h-[220px]",
|
|
127
|
+
rail: actions,
|
|
128
|
+
children: [
|
|
129
|
+
/* @__PURE__ */ jsxs("div", { className: "relative flex flex-col p-7 flex-1 min-w-0", children: [
|
|
130
|
+
icon && /* @__PURE__ */ jsx(IconTile, { dark, children: icon }),
|
|
131
|
+
title && /* @__PURE__ */ jsx(
|
|
132
|
+
"h3",
|
|
133
|
+
{
|
|
134
|
+
className: [
|
|
135
|
+
"text-[34px] font-medium leading-[1.02] tracking-[-0.04em] text-balance max-w-[14ch]",
|
|
136
|
+
icon ? "mt-5" : "",
|
|
137
|
+
dark ? "text-white" : "text-ink"
|
|
138
|
+
].join(" "),
|
|
139
|
+
children: title
|
|
140
|
+
}
|
|
141
|
+
),
|
|
142
|
+
/* @__PURE__ */ jsxs("div", { className: "mt-auto pt-8 flex flex-col gap-2", children: [
|
|
143
|
+
subtitle && /* @__PURE__ */ jsx("p", { className: ["text-[14px] leading-[1.45] tracking-[-0.005em]", dark ? "text-neutral-500" : "text-ink-muted"].join(" "), children: subtitle }),
|
|
144
|
+
children
|
|
145
|
+
] })
|
|
146
|
+
] }),
|
|
147
|
+
/* @__PURE__ */ jsx(
|
|
148
|
+
"div",
|
|
149
|
+
{
|
|
150
|
+
className: "relative w-[40%] shrink-0 overflow-hidden",
|
|
151
|
+
style: { boxShadow: dark ? "-1px 0 0 var(--divider-dark)" : "-1px 0 0 var(--divider)" },
|
|
152
|
+
children: /* @__PURE__ */ jsx(
|
|
153
|
+
Media,
|
|
154
|
+
{
|
|
155
|
+
image,
|
|
156
|
+
imageSrc,
|
|
157
|
+
imageAlt,
|
|
158
|
+
dark,
|
|
159
|
+
monogram: typeof title === "string" ? title.trim().charAt(0).toLowerCase() : void 0
|
|
160
|
+
}
|
|
161
|
+
)
|
|
162
|
+
}
|
|
163
|
+
)
|
|
164
|
+
]
|
|
165
|
+
}
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
function CardGrid({ children, className = "" }) {
|
|
169
|
+
return /* @__PURE__ */ jsx("div", { className: ["grid grid-cols-1 sm:grid-cols-2 gap-4", className].join(" "), children });
|
|
170
|
+
}
|
|
171
|
+
export {
|
|
172
|
+
Card,
|
|
173
|
+
CardGrid
|
|
174
|
+
};
|
|
175
|
+
//# sourceMappingURL=Card.js.map
|
package/dist/Card.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../components/ui/Card.tsx"],"sourcesContent":["import React from \"react\";\nimport { CardShell } from \"./CardShell\";\n\ntype CardTheme = \"light\" | \"dark\";\n\ninterface SplitCardProps {\n title?: string;\n subtitle?: string;\n icon?: React.ReactNode;\n image?: React.ReactNode;\n imageSrc?: string;\n imageAlt?: string;\n actions?: React.ReactNode;\n theme?: CardTheme;\n className?: string;\n children?: React.ReactNode;\n layout?: \"split\";\n}\n\ninterface StackCardProps {\n title?: string;\n badge?: React.ReactNode;\n subtitle?: string;\n note?: string;\n image?: React.ReactNode;\n imageSrc?: string;\n imageAlt?: string;\n imageBg?: string;\n actions?: React.ReactNode;\n className?: string;\n children?: React.ReactNode;\n layout: \"stack\";\n}\n\ntype CardProps = SplitCardProps | StackCardProps;\n\n/* Both layouts use CardShell: content on the face, actions on the rail.\n Media sits flush in the face — the face's own corners frame it, so there\n is no box-inside-a-box. */\n\nfunction MediaPlaceholder({ dark, bg, monogram }: { dark?: boolean; bg?: string; monogram?: string }) {\n return (\n <div\n className={[\"relative w-full h-full flex items-center justify-center\", bg ?? \"\"].join(\" \")}\n style={bg ? undefined : { background: dark ? \"var(--media-well-dark)\" : \"var(--media-well)\" }}\n >\n {/* A faint dot grid fading out from the centre: a slot, not a hole */}\n <div\n aria-hidden\n className=\"absolute inset-0\"\n style={{\n backgroundImage: `radial-gradient(${\n bg ? \"var(--media-dots-on-colour)\" : dark ? \"var(--media-dots-dark)\" : \"var(--media-dots)\"\n } 1px, transparent 1px)`,\n backgroundSize: \"12px 12px\",\n maskImage: \"radial-gradient(70% 70% at 50% 50%, black 0%, transparent 100%)\",\n WebkitMaskImage: \"radial-gradient(70% 70% at 50% 50%, black 0%, transparent 100%)\",\n }}\n />\n {/* The title's initial, set huge and barely there and cropped by the\n slot's edges: the empty half carries the card's type, not a glyph */}\n {monogram && (\n <span\n aria-hidden\n className={[\n \"absolute -right-3 -bottom-[0.32em] text-[220px] font-semibold leading-none tracking-[-0.06em] select-none\",\n dark ? \"text-white/[0.045]\" : \"text-ink/[0.045]\",\n ].join(\" \")}\n >\n {monogram}\n </span>\n )}\n </div>\n );\n}\n\nfunction Media({\n image,\n imageSrc,\n imageAlt,\n dark,\n bg,\n monogram,\n}: {\n image?: React.ReactNode;\n imageSrc?: string;\n imageAlt: string;\n dark?: boolean;\n bg?: string;\n monogram?: string;\n}) {\n if (image) return <>{image}</>;\n if (imageSrc) {\n // eslint-disable-next-line @next/next/no-img-element\n return <img src={imageSrc} alt={imageAlt} loading=\"lazy\" className=\"w-full h-full object-cover\" />;\n }\n return <MediaPlaceholder dark={dark} bg={bg} monogram={monogram} />;\n}\n\nfunction IconTile({ dark, children }: { dark: boolean; children: React.ReactNode }) {\n return (\n <div\n aria-hidden\n className={[\n \"w-9 h-9 rounded-[11px] flex items-center justify-center shrink-0\",\n dark ? \"text-neutral-100\" : \"text-ink-label\",\n ].join(\" \")}\n style={{\n background: dark ? \"var(--tile-dark)\" : \"var(--tile)\",\n boxShadow: dark ? \"var(--tile-dark-shadow)\" : \"var(--tile-shadow)\",\n }}\n >\n {children}\n </div>\n );\n}\n\nexport function Card(props: CardProps) {\n if (props.layout === \"stack\") {\n const {\n title,\n badge,\n subtitle,\n note,\n image,\n imageSrc,\n imageAlt = \"\",\n imageBg,\n actions,\n className = \"\",\n children,\n } = props;\n\n return (\n <CardShell\n as=\"article\"\n className={className}\n rail={actions}\n >\n {/* Hairline under the media so it reads as set into the face */}\n <div className=\"relative h-52 shrink-0 overflow-hidden\" style={{ boxShadow: \"0 1px 0 var(--divider)\" }}>\n <Media image={image} imageSrc={imageSrc} imageAlt={imageAlt} bg={imageBg} />\n </div>\n\n <div className=\"flex-1 flex flex-col p-6\">\n {(title || badge) && (\n <div className=\"flex items-center gap-2\">\n {title && (\n <h3 className=\"text-[17px] font-semibold text-ink tracking-[-0.015em] leading-snug\">{title}</h3>\n )}\n {badge}\n </div>\n )}\n {subtitle && (\n // Line breaks in the copy are for centred layouts; let it flow here\n <p className=\"mt-1.5 text-[14px] text-ink-body leading-[1.55] text-pretty\">\n {subtitle.replace(/\\n/g, \" \")}\n </p>\n )}\n {children}\n {note && (\n <p className=\"mt-auto pt-5 text-[12px] text-ink-muted leading-relaxed text-pretty\">{note}</p>\n )}\n </div>\n </CardShell>\n );\n }\n\n const {\n title,\n subtitle,\n icon,\n image,\n imageSrc,\n imageAlt = \"\",\n actions,\n theme = \"light\",\n className = \"\",\n children,\n } = props as SplitCardProps;\n const dark = theme === \"dark\";\n\n return (\n <CardShell\n as=\"article\"\n theme={dark ? \"dark\" : \"light\"}\n className={className}\n faceClassName=\"flex-row! min-h-[220px]\"\n rail={actions}\n >\n {/* The title is the hero: display-sized and pinned to the top. The\n subtitle answers it from the bottom edge, so the two frame the\n face between them instead of huddling in one corner. */}\n <div className=\"relative flex flex-col p-7 flex-1 min-w-0\">\n {icon && <IconTile dark={dark}>{icon}</IconTile>}\n {title && (\n <h3\n className={[\n \"text-[34px] font-medium leading-[1.02] tracking-[-0.04em] text-balance max-w-[14ch]\",\n icon ? \"mt-5\" : \"\",\n dark ? \"text-white\" : \"text-ink\",\n ].join(\" \")}\n >\n {title}\n </h3>\n )}\n <div className=\"mt-auto pt-8 flex flex-col gap-2\">\n {subtitle && (\n <p className={[\"text-[14px] leading-[1.45] tracking-[-0.005em]\", dark ? \"text-neutral-500\" : \"text-ink-muted\"].join(\" \")}>{subtitle}</p>\n )}\n {children}\n </div>\n </div>\n <div\n className=\"relative w-[40%] shrink-0 overflow-hidden\"\n style={{ boxShadow: dark ? \"-1px 0 0 var(--divider-dark)\" : \"-1px 0 0 var(--divider)\" }}\n >\n <Media\n image={image}\n imageSrc={imageSrc}\n imageAlt={imageAlt}\n dark={dark}\n monogram={typeof title === \"string\" ? title.trim().charAt(0).toLowerCase() : undefined}\n />\n </div>\n </CardShell>\n );\n}\n\ninterface CardGridProps {\n children: React.ReactNode;\n className?: string;\n}\n\nexport function CardGrid({ children, className = \"\" }: CardGridProps) {\n return (\n <div className={[\"grid grid-cols-1 sm:grid-cols-2 gap-4\", className].join(\" \")}>\n {children}\n </div>\n );\n}\n"],"mappings":"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","names":["title","subtitle","image","imageSrc","imageAlt","actions","className","children"]}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
|
|
4
|
+
type Theme = "light" | "dark";
|
|
5
|
+
interface CardShellProps {
|
|
6
|
+
theme?: Theme;
|
|
7
|
+
/** Controls and context: actions, attribution, deltas. Lives on the chassis. */
|
|
8
|
+
rail?: React.ReactNode;
|
|
9
|
+
/** "actions" for buttons; "text" for plain context, indented to the face's text edge */
|
|
10
|
+
railKind?: "actions" | "text";
|
|
11
|
+
as?: "div" | "figure" | "article";
|
|
12
|
+
className?: string;
|
|
13
|
+
faceClassName?: string;
|
|
14
|
+
children: React.ReactNode;
|
|
15
|
+
}
|
|
16
|
+
declare function CardShell({ theme, rail, railKind, as: Tag, className, faceClassName, children, }: CardShellProps): react_jsx_runtime.JSX.Element;
|
|
17
|
+
|
|
18
|
+
export { CardShell };
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
// ../../components/ui/CardShell.tsx
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
function CardShell({
|
|
4
|
+
theme = "light",
|
|
5
|
+
rail,
|
|
6
|
+
railKind = "actions",
|
|
7
|
+
as: Tag = "div",
|
|
8
|
+
className = "",
|
|
9
|
+
faceClassName = "",
|
|
10
|
+
children
|
|
11
|
+
}) {
|
|
12
|
+
const dark = theme === "dark";
|
|
13
|
+
return /* @__PURE__ */ jsxs(
|
|
14
|
+
Tag,
|
|
15
|
+
{
|
|
16
|
+
className: [
|
|
17
|
+
"rim relative m-0 rounded-[24px] p-1.5 flex flex-col h-full",
|
|
18
|
+
rail ? "pb-0" : "",
|
|
19
|
+
className
|
|
20
|
+
].join(" "),
|
|
21
|
+
style: {
|
|
22
|
+
background: dark ? "var(--card-bezel-dark)" : "var(--card-bezel)",
|
|
23
|
+
boxShadow: dark ? "var(--card-dark-shadow)" : "var(--card-shadow)",
|
|
24
|
+
["--rim"]: dark ? "var(--card-dark-rim)" : "var(--card-rim)"
|
|
25
|
+
},
|
|
26
|
+
children: [
|
|
27
|
+
/* @__PURE__ */ jsx(
|
|
28
|
+
"div",
|
|
29
|
+
{
|
|
30
|
+
className: ["relative flex-1 min-h-0 rounded-[18px] overflow-hidden flex flex-col", faceClassName].join(" "),
|
|
31
|
+
style: {
|
|
32
|
+
background: dark ? "var(--card-dark)" : "var(--card)",
|
|
33
|
+
boxShadow: dark ? "var(--card-face-dark-shadow)" : "var(--card-face-shadow)"
|
|
34
|
+
},
|
|
35
|
+
children
|
|
36
|
+
}
|
|
37
|
+
),
|
|
38
|
+
rail && /* @__PURE__ */ jsx("div", { className: "relative shrink-0 px-1.5 pt-2.5 pb-3", children: /* @__PURE__ */ jsx(
|
|
39
|
+
"div",
|
|
40
|
+
{
|
|
41
|
+
className: [
|
|
42
|
+
// 40px = a small button's full height, so text rails and button
|
|
43
|
+
// rails come out the same height and line up across a row
|
|
44
|
+
"min-h-10 flex items-center gap-2 text-[12.5px]",
|
|
45
|
+
railKind === "text" ? "pl-[18px] pr-3 gap-2.5" : "",
|
|
46
|
+
dark ? "text-neutral-400" : "text-ink-muted"
|
|
47
|
+
].join(" "),
|
|
48
|
+
children: rail
|
|
49
|
+
}
|
|
50
|
+
) })
|
|
51
|
+
]
|
|
52
|
+
}
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
export {
|
|
56
|
+
CardShell
|
|
57
|
+
};
|
|
58
|
+
//# sourceMappingURL=CardShell.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../components/ui/CardShell.tsx"],"sourcesContent":["import React from \"react\";\n\ntype Theme = \"light\" | \"dark\";\n\ninterface CardShellProps {\n theme?: Theme;\n /** Controls and context: actions, attribution, deltas. Lives on the chassis. */\n rail?: React.ReactNode;\n /** \"actions\" for buttons; \"text\" for plain context, indented to the face's text edge */\n railKind?: \"actions\" | \"text\";\n as?: \"div\" | \"figure\" | \"article\";\n className?: string;\n faceClassName?: string;\n children: React.ReactNode;\n}\n\n/* Every card is built like every other object in the kit: a chassis (bezel)\n holding a face. Content goes on the face; controls and context go on the\n rail, the strip of chassis below it. The two are nearly the same tone —\n the hairline around the face is what separates them.\n\n Geometry, measured from the card's outer edge:\n - chassis 24px radius, 6px bezel → face 18px radius (concentric)\n - rail content sits 12px in from the left and bottom, 10px below the face\n - a button's label then starts 30px in — the same edge as the face's text,\n and plain-text rails are indented to that edge too */\nexport function CardShell({\n theme = \"light\",\n rail,\n railKind = \"actions\",\n as: Tag = \"div\",\n className = \"\",\n faceClassName = \"\",\n children,\n}: CardShellProps) {\n const dark = theme === \"dark\";\n\n return (\n <Tag\n className={[\n \"rim relative m-0 rounded-[24px] p-1.5 flex flex-col h-full\",\n rail ? \"pb-0\" : \"\",\n className,\n ].join(\" \")}\n style={{\n background: dark ? \"var(--card-bezel-dark)\" : \"var(--card-bezel)\",\n boxShadow: dark ? \"var(--card-dark-shadow)\" : \"var(--card-shadow)\",\n [\"--rim\" as string]: dark ? \"var(--card-dark-rim)\" : \"var(--card-rim)\",\n } as React.CSSProperties}\n >\n <div\n className={[\"relative flex-1 min-h-0 rounded-[18px] overflow-hidden flex flex-col\", faceClassName].join(\" \")}\n style={{\n background: dark ? \"var(--card-dark)\" : \"var(--card)\",\n boxShadow: dark ? \"var(--card-face-dark-shadow)\" : \"var(--card-face-shadow)\",\n }}\n >\n {children}\n </div>\n\n {rail && (\n <div className=\"relative shrink-0 px-1.5 pt-2.5 pb-3\">\n <div\n className={[\n // 40px = a small button's full height, so text rails and button\n // rails come out the same height and line up across a row\n \"min-h-10 flex items-center gap-2 text-[12.5px]\",\n railKind === \"text\" ? \"pl-[18px] pr-3 gap-2.5\" : \"\",\n dark ? \"text-neutral-400\" : \"text-ink-muted\",\n ].join(\" \")}\n >\n {rail}\n </div>\n </div>\n )}\n </Tag>\n );\n}\n"],"mappings":";AAsCI,SAYE,KAZF;AAZG,SAAS,UAAU;AAAA,EACxB,QAAQ;AAAA,EACR;AAAA,EACA,WAAW;AAAA,EACX,IAAI,MAAM;AAAA,EACV,YAAY;AAAA,EACZ,gBAAgB;AAAA,EAChB;AACF,GAAmB;AACjB,QAAM,OAAO,UAAU;AAEvB,SACE;AAAA,IAAC;AAAA;AAAA,MACC,WAAW;AAAA,QACT;AAAA,QACA,OAAO,SAAS;AAAA,QAChB;AAAA,MACF,EAAE,KAAK,GAAG;AAAA,MACV,OAAO;AAAA,QACL,YAAY,OAAO,2BAA2B;AAAA,QAC9C,WAAW,OAAO,4BAA4B;AAAA,QAC9C,CAAC,OAAiB,GAAG,OAAO,yBAAyB;AAAA,MACvD;AAAA,MAEA;AAAA;AAAA,UAAC;AAAA;AAAA,YACC,WAAW,CAAC,wEAAwE,aAAa,EAAE,KAAK,GAAG;AAAA,YAC3G,OAAO;AAAA,cACL,YAAY,OAAO,qBAAqB;AAAA,cACxC,WAAW,OAAO,iCAAiC;AAAA,YACrD;AAAA,YAEC;AAAA;AAAA,QACH;AAAA,QAEC,QACC,oBAAC,SAAI,WAAU,wCACb;AAAA,UAAC;AAAA;AAAA,YACC,WAAW;AAAA;AAAA;AAAA,cAGT;AAAA,cACA,aAAa,SAAS,2BAA2B;AAAA,cACjD,OAAO,qBAAqB;AAAA,YAC9B,EAAE,KAAK,GAAG;AAAA,YAET;AAAA;AAAA,QACH,GACF;AAAA;AAAA;AAAA,EAEJ;AAEJ;","names":[]}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
|
|
4
|
+
interface CarouselProps {
|
|
5
|
+
items: React.ReactNode[];
|
|
6
|
+
autoPlay?: boolean;
|
|
7
|
+
interval?: number;
|
|
8
|
+
className?: string;
|
|
9
|
+
}
|
|
10
|
+
declare function Carousel({ items, autoPlay, interval, className, }: CarouselProps): react_jsx_runtime.JSX.Element;
|
|
11
|
+
type SlideTheme = "dark" | "light" | "tint";
|
|
12
|
+
interface CarouselSlideProps {
|
|
13
|
+
theme?: SlideTheme;
|
|
14
|
+
/** Background for the "tint" theme */
|
|
15
|
+
tint?: string;
|
|
16
|
+
icon?: React.ReactNode;
|
|
17
|
+
title: React.ReactNode;
|
|
18
|
+
meta?: React.ReactNode;
|
|
19
|
+
}
|
|
20
|
+
declare function CarouselSlide({ theme, tint, icon, title, meta }: CarouselSlideProps): react_jsx_runtime.JSX.Element;
|
|
21
|
+
|
|
22
|
+
export { Carousel, CarouselSlide };
|
package/dist/Carousel.js
ADDED
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
// ../../components/ui/Carousel.tsx
|
|
4
|
+
import { useState, useEffect, useRef } from "react";
|
|
5
|
+
import { Button } from "./Button.js";
|
|
6
|
+
import { CardShell } from "./CardShell.js";
|
|
7
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
8
|
+
var SLIDE_MS = 680;
|
|
9
|
+
function Carousel({
|
|
10
|
+
items,
|
|
11
|
+
autoPlay = false,
|
|
12
|
+
interval = 4e3,
|
|
13
|
+
className = ""
|
|
14
|
+
}) {
|
|
15
|
+
const [slots, setSlots] = useState([
|
|
16
|
+
{ itemIndex: 0, key: "s-0-0", role: "current" }
|
|
17
|
+
]);
|
|
18
|
+
const [direction, setDirection] = useState("right");
|
|
19
|
+
const [currentIdx, setCurrentIdx] = useState(0);
|
|
20
|
+
const [paused, setPaused] = useState(false);
|
|
21
|
+
const currentIdxRef = useRef(0);
|
|
22
|
+
const lockedRef = useRef(false);
|
|
23
|
+
const tcRef = useRef(0);
|
|
24
|
+
const cleanupRef = useRef(null);
|
|
25
|
+
const go = (nextIdx, dir) => {
|
|
26
|
+
if (lockedRef.current || nextIdx === currentIdxRef.current) return;
|
|
27
|
+
const prevIdx = currentIdxRef.current;
|
|
28
|
+
const tc = ++tcRef.current;
|
|
29
|
+
lockedRef.current = true;
|
|
30
|
+
currentIdxRef.current = nextIdx;
|
|
31
|
+
setDirection(dir);
|
|
32
|
+
setCurrentIdx(nextIdx);
|
|
33
|
+
setSlots([
|
|
34
|
+
{ itemIndex: nextIdx, key: `c-${nextIdx}-${tc}`, role: "current" },
|
|
35
|
+
{ itemIndex: prevIdx, key: `p-${prevIdx}-${tc}`, role: "prev" }
|
|
36
|
+
]);
|
|
37
|
+
if (cleanupRef.current) clearTimeout(cleanupRef.current);
|
|
38
|
+
cleanupRef.current = setTimeout(() => {
|
|
39
|
+
setSlots([{ itemIndex: nextIdx, key: `c-${nextIdx}-${tc}`, role: "current" }]);
|
|
40
|
+
lockedRef.current = false;
|
|
41
|
+
}, SLIDE_MS + 20);
|
|
42
|
+
};
|
|
43
|
+
const goNext = () => go((currentIdxRef.current + 1) % items.length, "right");
|
|
44
|
+
const goPrev = () => go((currentIdxRef.current - 1 + items.length) % items.length, "left");
|
|
45
|
+
const goNextRef = useRef(goNext);
|
|
46
|
+
useEffect(() => {
|
|
47
|
+
goNextRef.current = goNext;
|
|
48
|
+
});
|
|
49
|
+
useEffect(() => {
|
|
50
|
+
if (!autoPlay || paused) return;
|
|
51
|
+
if (window.matchMedia("(prefers-reduced-motion: reduce)").matches) return;
|
|
52
|
+
const id = setInterval(() => goNextRef.current(), interval);
|
|
53
|
+
return () => clearInterval(id);
|
|
54
|
+
}, [autoPlay, interval, paused]);
|
|
55
|
+
const onKeyDown = (e) => {
|
|
56
|
+
if (e.key === "ArrowRight") {
|
|
57
|
+
e.preventDefault();
|
|
58
|
+
goNextRef.current();
|
|
59
|
+
}
|
|
60
|
+
if (e.key === "ArrowLeft") {
|
|
61
|
+
e.preventDefault();
|
|
62
|
+
goPrev();
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
useEffect(() => () => {
|
|
66
|
+
if (cleanupRef.current) clearTimeout(cleanupRef.current);
|
|
67
|
+
}, []);
|
|
68
|
+
const dragRef = useRef({ x: 0, y: 0, active: false });
|
|
69
|
+
const THRESHOLD = 48;
|
|
70
|
+
const onPointerDown = (e) => {
|
|
71
|
+
dragRef.current = { x: e.clientX, y: e.clientY, active: true };
|
|
72
|
+
e.currentTarget.setPointerCapture(e.pointerId);
|
|
73
|
+
};
|
|
74
|
+
const onPointerUp = (e) => {
|
|
75
|
+
if (!dragRef.current.active) return;
|
|
76
|
+
const dx = e.clientX - dragRef.current.x;
|
|
77
|
+
const dy = e.clientY - dragRef.current.y;
|
|
78
|
+
dragRef.current.active = false;
|
|
79
|
+
if (Math.abs(dx) > THRESHOLD && Math.abs(dx) > Math.abs(dy) * 1.5) {
|
|
80
|
+
if (dx < 0) goNextRef.current();
|
|
81
|
+
else goPrev();
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
const onPointerCancel = () => {
|
|
85
|
+
dragRef.current.active = false;
|
|
86
|
+
};
|
|
87
|
+
const slide = (role) => (el) => {
|
|
88
|
+
if (!el || el.dataset.animated || slots.length < 2) return;
|
|
89
|
+
el.dataset.animated = "1";
|
|
90
|
+
if (window.matchMedia("(prefers-reduced-motion: reduce)").matches) return;
|
|
91
|
+
const sign = direction === "right" ? 1 : -1;
|
|
92
|
+
const frames = role === "current" ? [{ transform: `translateX(${sign * 100}%)` }, { transform: "translateX(0)" }] : [{ transform: "translateX(0)" }, { transform: `translateX(${-sign * 100}%)` }];
|
|
93
|
+
el.animate(frames, { duration: SLIDE_MS, easing: "cubic-bezier(0.22, 1, 0.36, 1)", fill: "both" });
|
|
94
|
+
};
|
|
95
|
+
const controls = /* @__PURE__ */ jsxs("div", { className: "w-full flex items-center justify-between", children: [
|
|
96
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 pl-1.5", children: [
|
|
97
|
+
/* @__PURE__ */ jsx(
|
|
98
|
+
"div",
|
|
99
|
+
{
|
|
100
|
+
className: "flex items-center gap-1 rounded-full p-[5px]",
|
|
101
|
+
style: { background: "var(--well)", boxShadow: "var(--well-shadow)" },
|
|
102
|
+
children: items.map((_, i) => {
|
|
103
|
+
const active = i === currentIdx;
|
|
104
|
+
return /* @__PURE__ */ jsx(
|
|
105
|
+
"button",
|
|
106
|
+
{
|
|
107
|
+
onClick: () => go(i, i > currentIdxRef.current ? "right" : "left"),
|
|
108
|
+
className: [
|
|
109
|
+
"relative h-1.5 rounded-full overflow-hidden cursor-pointer",
|
|
110
|
+
"transition-[width,background] duration-300 ease-[var(--ease-out-soft)]",
|
|
111
|
+
"after:absolute after:-inset-2 after:content-['']",
|
|
112
|
+
active ? "w-6 bg-ink/15" : "w-1.5 bg-ink/20 hover:bg-ink/40"
|
|
113
|
+
].join(" "),
|
|
114
|
+
"aria-label": `Go to slide ${i + 1}`,
|
|
115
|
+
"aria-current": active ? "true" : void 0,
|
|
116
|
+
children: active && /* @__PURE__ */ jsx(
|
|
117
|
+
"span",
|
|
118
|
+
{
|
|
119
|
+
"aria-hidden": true,
|
|
120
|
+
className: ["absolute inset-0 rounded-full origin-left", autoPlay ? "carousel-progress" : ""].join(" "),
|
|
121
|
+
style: {
|
|
122
|
+
background: "var(--face-inverse)",
|
|
123
|
+
animationDuration: `${interval}ms`,
|
|
124
|
+
animationPlayState: paused ? "paused" : "running"
|
|
125
|
+
}
|
|
126
|
+
},
|
|
127
|
+
`${currentIdx}-${autoPlay}`
|
|
128
|
+
)
|
|
129
|
+
},
|
|
130
|
+
i
|
|
131
|
+
);
|
|
132
|
+
})
|
|
133
|
+
}
|
|
134
|
+
),
|
|
135
|
+
/* @__PURE__ */ jsxs("span", { className: "text-[12px] font-medium text-ink-body tabular-nums", children: [
|
|
136
|
+
currentIdx + 1,
|
|
137
|
+
/* @__PURE__ */ jsxs("span", { className: "text-ink-faint", children: [
|
|
138
|
+
" / ",
|
|
139
|
+
items.length
|
|
140
|
+
] })
|
|
141
|
+
] })
|
|
142
|
+
] }),
|
|
143
|
+
/* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
|
|
144
|
+
/* @__PURE__ */ jsx(Button, { variant: "secondary", size: "sm", square: true, onClick: goPrev, "aria-label": "Previous slide", 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: "M15 19l-7-7 7-7" }) }) }),
|
|
145
|
+
/* @__PURE__ */ jsx(Button, { variant: "secondary", size: "sm", square: true, onClick: goNext, "aria-label": "Next slide", 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: "M9 5l7 7-7 7" }) }) })
|
|
146
|
+
] })
|
|
147
|
+
] });
|
|
148
|
+
return /* @__PURE__ */ jsx(
|
|
149
|
+
"section",
|
|
150
|
+
{
|
|
151
|
+
"aria-roledescription": "carousel",
|
|
152
|
+
"aria-label": "Carousel",
|
|
153
|
+
onKeyDown,
|
|
154
|
+
onMouseEnter: () => setPaused(true),
|
|
155
|
+
onMouseLeave: () => setPaused(false),
|
|
156
|
+
onFocus: () => setPaused(true),
|
|
157
|
+
onBlur: (e) => {
|
|
158
|
+
if (!e.currentTarget.contains(e.relatedTarget)) setPaused(false);
|
|
159
|
+
},
|
|
160
|
+
className,
|
|
161
|
+
children: /* @__PURE__ */ jsx(CardShell, { rail: controls, children: /* @__PURE__ */ jsx(
|
|
162
|
+
"div",
|
|
163
|
+
{
|
|
164
|
+
className: "relative w-full overflow-hidden cursor-grab active:cursor-grabbing touch-pan-y",
|
|
165
|
+
style: { minHeight: 300 },
|
|
166
|
+
"aria-live": autoPlay && !paused ? "off" : "polite",
|
|
167
|
+
onPointerDown,
|
|
168
|
+
onPointerUp,
|
|
169
|
+
onPointerCancel,
|
|
170
|
+
children: slots.map((slot) => /* @__PURE__ */ jsx(
|
|
171
|
+
"div",
|
|
172
|
+
{
|
|
173
|
+
role: "group",
|
|
174
|
+
"aria-roledescription": "slide",
|
|
175
|
+
"aria-label": `${slot.itemIndex + 1} of ${items.length}`,
|
|
176
|
+
"aria-hidden": slot.role === "prev",
|
|
177
|
+
ref: slide(slot.role),
|
|
178
|
+
className: ["absolute inset-0 will-change-transform [backface-visibility:hidden]", slot.role === "prev" ? "pointer-events-none" : ""].join(" "),
|
|
179
|
+
children: items[slot.itemIndex]
|
|
180
|
+
},
|
|
181
|
+
slot.key
|
|
182
|
+
))
|
|
183
|
+
}
|
|
184
|
+
) })
|
|
185
|
+
}
|
|
186
|
+
);
|
|
187
|
+
}
|
|
188
|
+
function CarouselSlide({ theme = "dark", tint = "#8A8E7A", icon, title, meta }) {
|
|
189
|
+
const dark = theme !== "light";
|
|
190
|
+
return /* @__PURE__ */ jsxs(
|
|
191
|
+
"div",
|
|
192
|
+
{
|
|
193
|
+
className: "relative w-full h-full flex flex-col justify-end gap-6 p-8 overflow-hidden select-none",
|
|
194
|
+
style: {
|
|
195
|
+
background: theme === "dark" ? "var(--card-dark)" : theme === "light" ? "var(--card)" : tint
|
|
196
|
+
},
|
|
197
|
+
children: [
|
|
198
|
+
/* @__PURE__ */ jsx(
|
|
199
|
+
"div",
|
|
200
|
+
{
|
|
201
|
+
"aria-hidden": true,
|
|
202
|
+
className: "absolute inset-y-0 right-0 w-3/5",
|
|
203
|
+
style: {
|
|
204
|
+
backgroundImage: `radial-gradient(${theme === "tint" ? "var(--media-dots-on-colour)" : dark ? "var(--media-dots-dark)" : "var(--slide-dots)"} 1px, transparent 1px)`,
|
|
205
|
+
backgroundSize: "12px 12px",
|
|
206
|
+
maskImage: "radial-gradient(60% 70% at 70% 50%, black 0%, transparent 100%)",
|
|
207
|
+
WebkitMaskImage: "radial-gradient(60% 70% at 70% 50%, black 0%, transparent 100%)"
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
),
|
|
211
|
+
icon && /* @__PURE__ */ jsx(
|
|
212
|
+
"div",
|
|
213
|
+
{
|
|
214
|
+
"aria-hidden": true,
|
|
215
|
+
className: [
|
|
216
|
+
"relative w-9 h-9 rounded-[11px] flex items-center justify-center mb-auto",
|
|
217
|
+
dark ? "text-neutral-100" : "text-ink-label"
|
|
218
|
+
].join(" "),
|
|
219
|
+
style: {
|
|
220
|
+
background: dark ? "var(--tile-dark)" : "var(--tile)",
|
|
221
|
+
boxShadow: dark ? "var(--tile-dark-shadow)" : "var(--tile-shadow)"
|
|
222
|
+
},
|
|
223
|
+
children: icon
|
|
224
|
+
}
|
|
225
|
+
),
|
|
226
|
+
/* @__PURE__ */ jsxs("div", { className: "relative", children: [
|
|
227
|
+
/* @__PURE__ */ jsx(
|
|
228
|
+
"p",
|
|
229
|
+
{
|
|
230
|
+
className: [
|
|
231
|
+
"text-[22px] font-medium leading-[1.2] tracking-[-0.015em] mb-2",
|
|
232
|
+
dark ? "text-white" : "text-ink"
|
|
233
|
+
].join(" "),
|
|
234
|
+
children: title
|
|
235
|
+
}
|
|
236
|
+
),
|
|
237
|
+
meta && /* @__PURE__ */ jsx("p", { className: ["text-[13px]", theme === "tint" ? "text-white/65" : dark ? "text-neutral-400" : "text-ink-muted"].join(" "), children: meta })
|
|
238
|
+
] })
|
|
239
|
+
]
|
|
240
|
+
}
|
|
241
|
+
);
|
|
242
|
+
}
|
|
243
|
+
export {
|
|
244
|
+
Carousel,
|
|
245
|
+
CarouselSlide
|
|
246
|
+
};
|
|
247
|
+
//# sourceMappingURL=Carousel.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../components/ui/Carousel.tsx"],"sourcesContent":["\"use client\";\n\nimport React, { useState, useEffect, useRef } from \"react\";\nimport { Button } from \"./Button\";\nimport { CardShell } from \"./CardShell\";\n\ninterface CarouselProps {\n items: React.ReactNode[];\n autoPlay?: boolean;\n interval?: number;\n className?: string;\n}\n\nconst SLIDE_MS = 680;\n\ninterface Slot {\n itemIndex: number;\n key: string;\n role: \"current\" | \"prev\";\n}\n\nexport function Carousel({\n items,\n autoPlay = false,\n interval = 4000,\n className = \"\",\n}: CarouselProps) {\n const [slots, setSlots] = useState<Slot[]>([\n { itemIndex: 0, key: \"s-0-0\", role: \"current\" },\n ]);\n const [direction, setDirection] = useState<\"left\" | \"right\">(\"right\");\n const [currentIdx, setCurrentIdx] = useState(0);\n // Autoplay never moves content out from under someone looking at it\n const [paused, setPaused] = useState(false);\n\n const currentIdxRef = useRef(0);\n const lockedRef = useRef(false);\n const tcRef = useRef(0);\n const cleanupRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n const go = (nextIdx: number, dir: \"left\" | \"right\") => {\n if (lockedRef.current || nextIdx === currentIdxRef.current) return;\n\n const prevIdx = currentIdxRef.current;\n const tc = ++tcRef.current;\n\n lockedRef.current = true;\n currentIdxRef.current = nextIdx;\n\n setDirection(dir);\n setCurrentIdx(nextIdx);\n setSlots([\n { itemIndex: nextIdx, key: `c-${nextIdx}-${tc}`, role: \"current\" },\n { itemIndex: prevIdx, key: `p-${prevIdx}-${tc}`, role: \"prev\" },\n ]);\n\n if (cleanupRef.current) clearTimeout(cleanupRef.current);\n cleanupRef.current = setTimeout(() => {\n setSlots([{ itemIndex: nextIdx, key: `c-${nextIdx}-${tc}`, role: \"current\" }]);\n lockedRef.current = false;\n }, SLIDE_MS + 20);\n };\n\n const goNext = () => go((currentIdxRef.current + 1) % items.length, \"right\");\n const goPrev = () => go((currentIdxRef.current - 1 + items.length) % items.length, \"left\");\n\n // Kept current after each render so the autoplay timer never goes stale\n const goNextRef = useRef(goNext);\n useEffect(() => {\n goNextRef.current = goNext;\n });\n\n useEffect(() => {\n if (!autoPlay || paused) return;\n if (window.matchMedia(\"(prefers-reduced-motion: reduce)\").matches) return;\n const id = setInterval(() => goNextRef.current(), interval);\n return () => clearInterval(id);\n }, [autoPlay, interval, paused]);\n\n const onKeyDown = (e: React.KeyboardEvent) => {\n if (e.key === \"ArrowRight\") { e.preventDefault(); goNextRef.current(); }\n if (e.key === \"ArrowLeft\") { e.preventDefault(); goPrev(); }\n };\n\n useEffect(() => () => { if (cleanupRef.current) clearTimeout(cleanupRef.current); }, []);\n\n // ── Swipe / pointer drag ──────────────────────────────────────────\n const dragRef = useRef<{ x: number; y: number; active: boolean }>({ x: 0, y: 0, active: false });\n const THRESHOLD = 48;\n\n const onPointerDown = (e: React.PointerEvent) => {\n dragRef.current = { x: e.clientX, y: e.clientY, active: true };\n (e.currentTarget as HTMLElement).setPointerCapture(e.pointerId);\n };\n\n const onPointerUp = (e: React.PointerEvent) => {\n if (!dragRef.current.active) return;\n const dx = e.clientX - dragRef.current.x;\n const dy = e.clientY - dragRef.current.y;\n dragRef.current.active = false;\n if (Math.abs(dx) > THRESHOLD && Math.abs(dx) > Math.abs(dy) * 1.5) {\n if (dx < 0) goNextRef.current();\n else goPrev();\n }\n };\n\n const onPointerCancel = () => { dragRef.current.active = false; };\n\n /* A plain slide: the incoming and outgoing slides travel together as\n one strip, a full width each. The curve is a strong ease-out, so the\n strip moves the instant you click and then settles — an ease-in-out\n hesitates first, which reads as lag. Driven by the Web Animations API\n on mount, so it never depends on a stylesheet. */\n const slide = (role: Slot[\"role\"]) => (el: HTMLDivElement | null) => {\n if (!el || el.dataset.animated || slots.length < 2) return;\n el.dataset.animated = \"1\";\n if (window.matchMedia(\"(prefers-reduced-motion: reduce)\").matches) return;\n const sign = direction === \"right\" ? 1 : -1;\n const frames =\n role === \"current\"\n ? [{ transform: `translateX(${sign * 100}%)` }, { transform: \"translateX(0)\" }]\n : [{ transform: \"translateX(0)\" }, { transform: `translateX(${-sign * 100}%)` }];\n el.animate(frames, { duration: SLIDE_MS, easing: \"cubic-bezier(0.22, 1, 0.36, 1)\", fill: \"both\" });\n };\n\n const controls = (\n <div className=\"w-full flex items-center justify-between\">\n <div className=\"flex items-center gap-3 pl-1.5\">\n {/* Dots sit in a well; the active one fills over the autoplay\n interval, and the fill holds while autoplay is paused */}\n <div\n className=\"flex items-center gap-1 rounded-full p-[5px]\"\n style={{ background: \"var(--well)\", boxShadow: \"var(--well-shadow)\" }}\n >\n {items.map((_, i) => {\n const active = i === currentIdx;\n return (\n <button\n key={i}\n onClick={() => go(i, i > currentIdxRef.current ? \"right\" : \"left\")}\n className={[\n \"relative h-1.5 rounded-full overflow-hidden cursor-pointer\",\n \"transition-[width,background] duration-300 ease-[var(--ease-out-soft)]\",\n \"after:absolute after:-inset-2 after:content-['']\",\n active ? \"w-6 bg-ink/15\" : \"w-1.5 bg-ink/20 hover:bg-ink/40\",\n ].join(\" \")}\n aria-label={`Go to slide ${i + 1}`}\n aria-current={active ? \"true\" : undefined}\n >\n {active && (\n <span\n key={`${currentIdx}-${autoPlay}`}\n aria-hidden\n className={[\"absolute inset-0 rounded-full origin-left\", autoPlay ? \"carousel-progress\" : \"\"].join(\" \")}\n style={{\n background: \"var(--face-inverse)\",\n animationDuration: `${interval}ms`,\n animationPlayState: paused ? \"paused\" : \"running\",\n }}\n />\n )}\n </button>\n );\n })}\n </div>\n <span className=\"text-[12px] font-medium text-ink-body tabular-nums\">\n {currentIdx + 1}\n <span className=\"text-ink-faint\"> / {items.length}</span>\n </span>\n </div>\n\n <div className=\"flex gap-2\">\n <Button variant=\"secondary\" size=\"sm\" square onClick={goPrev} aria-label=\"Previous slide\">\n <svg width=\"13\" height=\"13\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" viewBox=\"0 0 24 24\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M15 19l-7-7 7-7\" />\n </svg>\n </Button>\n <Button variant=\"secondary\" size=\"sm\" square onClick={goNext} aria-label=\"Next slide\">\n <svg width=\"13\" height=\"13\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" viewBox=\"0 0 24 24\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M9 5l7 7-7 7\" />\n </svg>\n </Button>\n </div>\n </div>\n );\n\n /* Built on the card chassis like every other surface: slides are the\n face, controls live on the rail below it. */\n return (\n <section\n aria-roledescription=\"carousel\"\n aria-label=\"Carousel\"\n onKeyDown={onKeyDown}\n onMouseEnter={() => setPaused(true)}\n onMouseLeave={() => setPaused(false)}\n onFocus={() => setPaused(true)}\n onBlur={(e) => {\n if (!e.currentTarget.contains(e.relatedTarget as Node)) setPaused(false);\n }}\n className={className}\n >\n <CardShell rail={controls}>\n <div\n className=\"relative w-full overflow-hidden cursor-grab active:cursor-grabbing touch-pan-y\"\n style={{ minHeight: 300 }}\n aria-live={autoPlay && !paused ? \"off\" : \"polite\"}\n onPointerDown={onPointerDown}\n onPointerUp={onPointerUp}\n onPointerCancel={onPointerCancel}\n >\n {slots.map((slot) => (\n <div\n key={slot.key}\n role=\"group\"\n aria-roledescription=\"slide\"\n aria-label={`${slot.itemIndex + 1} of ${items.length}`}\n aria-hidden={slot.role === \"prev\"}\n ref={slide(slot.role)}\n className={[\"absolute inset-0 will-change-transform [backface-visibility:hidden]\", slot.role === \"prev\" ? \"pointer-events-none\" : \"\"].join(\" \")}\n >\n {items[slot.itemIndex]}\n </div>\n ))}\n </div>\n </CardShell>\n </section>\n );\n}\n\ntype SlideTheme = \"dark\" | \"light\" | \"tint\";\n\ninterface CarouselSlideProps {\n theme?: SlideTheme;\n /** Background for the \"tint\" theme */\n tint?: string;\n icon?: React.ReactNode;\n title: React.ReactNode;\n meta?: React.ReactNode;\n}\n\n/* A ready-made slide in the cards' vocabulary: the card's own material,\n a raised icon tile, and the same fading dot grid as empty media. */\nexport function CarouselSlide({ theme = \"dark\", tint = \"#8A8E7A\", icon, title, meta }: CarouselSlideProps) {\n const dark = theme !== \"light\";\n return (\n <div\n className=\"relative w-full h-full flex flex-col justify-end gap-6 p-8 overflow-hidden select-none\"\n style={{\n background: theme === \"dark\" ? \"var(--card-dark)\" : theme === \"light\" ? \"var(--card)\" : tint,\n }}\n >\n <div\n aria-hidden\n className=\"absolute inset-y-0 right-0 w-3/5\"\n style={{\n backgroundImage: `radial-gradient(${\n theme === \"tint\" ? \"var(--media-dots-on-colour)\" : dark ? \"var(--media-dots-dark)\" : \"var(--slide-dots)\"\n } 1px, transparent 1px)`,\n backgroundSize: \"12px 12px\",\n maskImage: \"radial-gradient(60% 70% at 70% 50%, black 0%, transparent 100%)\",\n WebkitMaskImage: \"radial-gradient(60% 70% at 70% 50%, black 0%, transparent 100%)\",\n }}\n />\n {icon && (\n <div\n aria-hidden\n className={[\n \"relative w-9 h-9 rounded-[11px] flex items-center justify-center mb-auto\",\n dark ? \"text-neutral-100\" : \"text-ink-label\",\n ].join(\" \")}\n style={{\n background: dark ? \"var(--tile-dark)\" : \"var(--tile)\",\n boxShadow: dark ? \"var(--tile-dark-shadow)\" : \"var(--tile-shadow)\",\n }}\n >\n {icon}\n </div>\n )}\n <div className=\"relative\">\n <p\n className={[\n \"text-[22px] font-medium leading-[1.2] tracking-[-0.015em] mb-2\",\n dark ? \"text-white\" : \"text-ink\",\n ].join(\" \")}\n >\n {title}\n </p>\n {meta && (\n <p className={[\"text-[13px]\", theme === \"tint\" ? \"text-white/65\" : dark ? \"text-neutral-400\" : \"text-ink-muted\"].join(\" \")}>\n {meta}\n </p>\n )}\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
|
+
interface CheckboxProps {
|
|
4
|
+
checked?: boolean;
|
|
5
|
+
defaultChecked?: boolean;
|
|
6
|
+
onChange?: (checked: boolean) => void;
|
|
7
|
+
label?: string;
|
|
8
|
+
description?: string;
|
|
9
|
+
disabled?: boolean;
|
|
10
|
+
indeterminate?: boolean;
|
|
11
|
+
className?: string;
|
|
12
|
+
}
|
|
13
|
+
declare function Checkbox({ checked, defaultChecked, onChange, label, description, disabled, indeterminate, className, }: CheckboxProps): react_jsx_runtime.JSX.Element;
|
|
14
|
+
|
|
15
|
+
export { Checkbox };
|