@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,366 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
// ../../components/ui/ColorPicker.tsx
|
|
4
|
+
import { useId, useRef, useState } from "react";
|
|
5
|
+
import { Portal } from "./Floating.js";
|
|
6
|
+
import { FieldLabel, FieldMessage } from "./FieldMessage.js";
|
|
7
|
+
import { useFloatingPanel } from "./useFloatingPanel.js";
|
|
8
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
9
|
+
var clamp = (n, lo = 0, hi = 1) => Math.min(hi, Math.max(lo, n));
|
|
10
|
+
function normalizeHex(input) {
|
|
11
|
+
const s = input.trim().replace(/^#/, "").toLowerCase();
|
|
12
|
+
if (/^[0-9a-f]{3}$/.test(s)) return "#" + s.split("").map((c) => c + c).join("");
|
|
13
|
+
if (/^[0-9a-f]{6}$/.test(s)) return "#" + s;
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
function hexToHsv(hex) {
|
|
17
|
+
const n = parseInt(hex.slice(1), 16);
|
|
18
|
+
const r = (n >> 16 & 255) / 255;
|
|
19
|
+
const g = (n >> 8 & 255) / 255;
|
|
20
|
+
const b = (n & 255) / 255;
|
|
21
|
+
const max = Math.max(r, g, b);
|
|
22
|
+
const d = max - Math.min(r, g, b);
|
|
23
|
+
let h = 0;
|
|
24
|
+
if (d) {
|
|
25
|
+
if (max === r) h = (g - b) / d % 6;
|
|
26
|
+
else if (max === g) h = (b - r) / d + 2;
|
|
27
|
+
else h = (r - g) / d + 4;
|
|
28
|
+
h = (h * 60 + 360) % 360;
|
|
29
|
+
}
|
|
30
|
+
return { h, s: max ? d / max : 0, v: max };
|
|
31
|
+
}
|
|
32
|
+
function hsvToHex({ h, s, v }) {
|
|
33
|
+
const f = (n) => {
|
|
34
|
+
const k = (n + h / 60) % 6;
|
|
35
|
+
return Math.round((v - v * s * Math.max(0, Math.min(k, 4 - k, 1))) * 255);
|
|
36
|
+
};
|
|
37
|
+
return "#" + [f(5), f(3), f(1)].map((x) => x.toString(16).padStart(2, "0")).join("");
|
|
38
|
+
}
|
|
39
|
+
var DEFAULT_SWATCHES = [
|
|
40
|
+
"#171717",
|
|
41
|
+
"#737373",
|
|
42
|
+
"#dc2626",
|
|
43
|
+
"#ea580c",
|
|
44
|
+
"#ca8a04",
|
|
45
|
+
"#16a34a",
|
|
46
|
+
"#0891b2",
|
|
47
|
+
"#2563eb",
|
|
48
|
+
"#7c3aed",
|
|
49
|
+
"#db2777"
|
|
50
|
+
];
|
|
51
|
+
function ColorPanel({ value, onChange, swatches = DEFAULT_SWATCHES, autoFocus = false }) {
|
|
52
|
+
const [hsv, setHsv] = useState(() => hexToHsv(value));
|
|
53
|
+
const [draft, setDraft] = useState(value);
|
|
54
|
+
const [seen, setSeen] = useState(value);
|
|
55
|
+
if (value !== seen) {
|
|
56
|
+
setSeen(value);
|
|
57
|
+
if (value !== hsvToHex(hsv)) {
|
|
58
|
+
setHsv(hexToHsv(value));
|
|
59
|
+
setDraft(value);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
const update = (next) => {
|
|
63
|
+
setHsv(next);
|
|
64
|
+
const hex = hsvToHex(next);
|
|
65
|
+
setDraft(hex);
|
|
66
|
+
if (hex !== value) onChange(hex);
|
|
67
|
+
};
|
|
68
|
+
const pickHex = (hex) => {
|
|
69
|
+
setHsv(hexToHsv(hex));
|
|
70
|
+
setDraft(hex);
|
|
71
|
+
if (hex !== value) onChange(hex);
|
|
72
|
+
};
|
|
73
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex w-[236px] flex-col gap-3", children: [
|
|
74
|
+
/* @__PURE__ */ jsx(SaturationArea, { hsv, onChange: update, autoFocus }),
|
|
75
|
+
/* @__PURE__ */ jsx(HueStrip, { hue: hsv.h, onChange: (h) => update({ ...hsv, h }) }),
|
|
76
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
77
|
+
/* @__PURE__ */ jsx(
|
|
78
|
+
"span",
|
|
79
|
+
{
|
|
80
|
+
"aria-hidden": true,
|
|
81
|
+
className: "h-9 w-9 shrink-0 rounded-[10px]",
|
|
82
|
+
style: { background: hsvToHex(hsv), boxShadow: "var(--tile-shadow), inset 0 0 0 1px var(--hairline)" }
|
|
83
|
+
}
|
|
84
|
+
),
|
|
85
|
+
/* @__PURE__ */ jsxs("div", { className: "relative flex-1", children: [
|
|
86
|
+
/* @__PURE__ */ jsx("span", { className: "pointer-events-none absolute left-3 top-1/2 z-20 -translate-y-1/2 text-[12.5px] text-ink-faint", children: "#" }),
|
|
87
|
+
/* @__PURE__ */ jsx(
|
|
88
|
+
"input",
|
|
89
|
+
{
|
|
90
|
+
"aria-label": "hex colour",
|
|
91
|
+
value: draft.replace(/^#/, ""),
|
|
92
|
+
maxLength: 7,
|
|
93
|
+
spellCheck: false,
|
|
94
|
+
autoComplete: "off",
|
|
95
|
+
onChange: (e) => {
|
|
96
|
+
setDraft("#" + e.target.value.replace(/^#/, ""));
|
|
97
|
+
const hex = normalizeHex(e.target.value);
|
|
98
|
+
if (hex && e.target.value.replace(/^#/, "").length === 6) pickHex(hex);
|
|
99
|
+
},
|
|
100
|
+
onBlur: () => {
|
|
101
|
+
const hex = normalizeHex(draft);
|
|
102
|
+
if (hex) pickHex(hex);
|
|
103
|
+
else setDraft(hsvToHex(hsv));
|
|
104
|
+
},
|
|
105
|
+
onKeyDown: (e) => {
|
|
106
|
+
if (e.key === "Enter") {
|
|
107
|
+
e.preventDefault();
|
|
108
|
+
e.target.blur();
|
|
109
|
+
}
|
|
110
|
+
},
|
|
111
|
+
className: "field relative z-10 w-full rounded-[10px] py-2 pl-6 pr-3 font-mono text-[12.5px] uppercase text-ink caret-ink"
|
|
112
|
+
}
|
|
113
|
+
)
|
|
114
|
+
] })
|
|
115
|
+
] }),
|
|
116
|
+
swatches.length > 0 && /* @__PURE__ */ jsx("div", { role: "group", "aria-label": "swatches", className: "grid grid-cols-10 gap-1.5 border-t border-line-strong pt-3", children: swatches.map((s) => {
|
|
117
|
+
const hex = normalizeHex(s) ?? s;
|
|
118
|
+
const on = hex === hsvToHex(hsv);
|
|
119
|
+
return /* @__PURE__ */ jsx(
|
|
120
|
+
"button",
|
|
121
|
+
{
|
|
122
|
+
type: "button",
|
|
123
|
+
"aria-label": hex,
|
|
124
|
+
"aria-pressed": on,
|
|
125
|
+
onClick: () => pickHex(hex),
|
|
126
|
+
className: "relative aspect-square cursor-pointer rounded-full transition-transform duration-200 ease-[var(--ease-spring)] hover:scale-115 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ink",
|
|
127
|
+
style: {
|
|
128
|
+
background: hex,
|
|
129
|
+
boxShadow: on ? "0 0 0 2px var(--panel-face), 0 0 0 3.5px var(--ink)" : "inset 0 0 0 1px var(--hairline), inset 0 1px 1px rgba(255,255,255,0.3), var(--dot-shadow)"
|
|
130
|
+
}
|
|
131
|
+
},
|
|
132
|
+
s
|
|
133
|
+
);
|
|
134
|
+
}) })
|
|
135
|
+
] });
|
|
136
|
+
}
|
|
137
|
+
function useDrag(onMove) {
|
|
138
|
+
const ref = useRef(null);
|
|
139
|
+
const [dragging, setDragging] = useState(false);
|
|
140
|
+
const at = (e) => {
|
|
141
|
+
const r = ref.current.getBoundingClientRect();
|
|
142
|
+
onMove(clamp((e.clientX - r.left) / r.width), clamp((e.clientY - r.top) / r.height));
|
|
143
|
+
};
|
|
144
|
+
const handlers = {
|
|
145
|
+
onPointerDown: (e) => {
|
|
146
|
+
if (e.button !== 0) return;
|
|
147
|
+
e.preventDefault();
|
|
148
|
+
e.currentTarget.setPointerCapture(e.pointerId);
|
|
149
|
+
e.currentTarget.querySelector("[data-thumb]")?.focus();
|
|
150
|
+
setDragging(true);
|
|
151
|
+
at(e);
|
|
152
|
+
},
|
|
153
|
+
onPointerMove: (e) => dragging && at(e),
|
|
154
|
+
onPointerUp: () => setDragging(false),
|
|
155
|
+
onPointerCancel: () => setDragging(false)
|
|
156
|
+
};
|
|
157
|
+
return { ref, dragging, handlers };
|
|
158
|
+
}
|
|
159
|
+
var thumbStyle = (color, dragging) => ({
|
|
160
|
+
background: color,
|
|
161
|
+
boxShadow: "0 0 0 2.5px #fff, 0 0 0 3px rgba(17,17,17,0.1), 0 2px 6px rgba(17,17,17,0.3), inset 0 0 0 1px rgba(17,17,17,0.1)",
|
|
162
|
+
transform: `translate(-50%, -50%) scale(${dragging ? 1.15 : 1})`,
|
|
163
|
+
transition: "transform 200ms var(--ease-spring)"
|
|
164
|
+
});
|
|
165
|
+
function SaturationArea({ hsv, onChange, autoFocus }) {
|
|
166
|
+
const { ref, dragging, handlers } = useDrag((x, y) => onChange({ ...hsv, s: x, v: 1 - y }));
|
|
167
|
+
const onKeyDown = (e) => {
|
|
168
|
+
const d = e.shiftKey ? 0.1 : 0.01;
|
|
169
|
+
const moves = {
|
|
170
|
+
ArrowLeft: { s: clamp(hsv.s - d) },
|
|
171
|
+
ArrowRight: { s: clamp(hsv.s + d) },
|
|
172
|
+
ArrowUp: { v: clamp(hsv.v + d) },
|
|
173
|
+
ArrowDown: { v: clamp(hsv.v - d) }
|
|
174
|
+
};
|
|
175
|
+
if (moves[e.key]) {
|
|
176
|
+
e.preventDefault();
|
|
177
|
+
onChange({ ...hsv, ...moves[e.key] });
|
|
178
|
+
}
|
|
179
|
+
};
|
|
180
|
+
return /* @__PURE__ */ jsx(
|
|
181
|
+
"div",
|
|
182
|
+
{
|
|
183
|
+
ref,
|
|
184
|
+
...handlers,
|
|
185
|
+
className: "relative h-[148px] cursor-crosshair touch-none rounded-[10px]",
|
|
186
|
+
style: {
|
|
187
|
+
background: `linear-gradient(to top, #000, transparent), linear-gradient(to right, #fff, hsl(${hsv.h} 100% 50%))`,
|
|
188
|
+
/* Cut into the face like a field: shade inside, lit lip below */
|
|
189
|
+
boxShadow: "inset 0 0 0 1px var(--hairline), inset 0 1px 3px rgba(0,0,0,0.25)"
|
|
190
|
+
},
|
|
191
|
+
children: /* @__PURE__ */ jsx(
|
|
192
|
+
"span",
|
|
193
|
+
{
|
|
194
|
+
"data-thumb": true,
|
|
195
|
+
ref: autoFocus ? (el) => el?.focus({ preventScroll: true }) : void 0,
|
|
196
|
+
role: "slider",
|
|
197
|
+
tabIndex: 0,
|
|
198
|
+
"aria-label": "saturation and brightness",
|
|
199
|
+
"aria-valuetext": `saturation ${Math.round(hsv.s * 100)}%, brightness ${Math.round(hsv.v * 100)}%`,
|
|
200
|
+
"aria-valuenow": Math.round(hsv.s * 100),
|
|
201
|
+
"aria-valuemin": 0,
|
|
202
|
+
"aria-valuemax": 100,
|
|
203
|
+
onKeyDown,
|
|
204
|
+
className: "absolute h-3.5 w-3.5 rounded-full focus-visible:outline-2 focus-visible:outline-offset-3 focus-visible:outline-ink",
|
|
205
|
+
style: { left: `${hsv.s * 100}%`, top: `${(1 - hsv.v) * 100}%`, ...thumbStyle(hsvToHex(hsv), dragging) }
|
|
206
|
+
}
|
|
207
|
+
)
|
|
208
|
+
}
|
|
209
|
+
);
|
|
210
|
+
}
|
|
211
|
+
function HueStrip({ hue, onChange }) {
|
|
212
|
+
const { ref, dragging, handlers } = useDrag((x) => onChange(Math.min(359.9, x * 360)));
|
|
213
|
+
const onKeyDown = (e) => {
|
|
214
|
+
const d = e.shiftKey ? 10 : 1;
|
|
215
|
+
if (e.key === "ArrowLeft" || e.key === "ArrowDown") {
|
|
216
|
+
e.preventDefault();
|
|
217
|
+
onChange((hue - d + 360) % 360);
|
|
218
|
+
}
|
|
219
|
+
if (e.key === "ArrowRight" || e.key === "ArrowUp") {
|
|
220
|
+
e.preventDefault();
|
|
221
|
+
onChange((hue + d) % 360);
|
|
222
|
+
}
|
|
223
|
+
};
|
|
224
|
+
return /* @__PURE__ */ jsxs(
|
|
225
|
+
"div",
|
|
226
|
+
{
|
|
227
|
+
ref,
|
|
228
|
+
...handlers,
|
|
229
|
+
className: "relative mx-[7px] h-3 cursor-pointer touch-none",
|
|
230
|
+
children: [
|
|
231
|
+
/* @__PURE__ */ jsx(
|
|
232
|
+
"span",
|
|
233
|
+
{
|
|
234
|
+
"aria-hidden": true,
|
|
235
|
+
className: "absolute -inset-x-[7px] inset-y-0 rounded-full",
|
|
236
|
+
style: {
|
|
237
|
+
background: "linear-gradient(to right, #f00 0%, #ff0 17%, #0f0 33%, #0ff 50%, #00f 67%, #f0f 83%, #f00 100%)",
|
|
238
|
+
boxShadow: "inset 0 0 0 1px var(--hairline), inset 0 1px 2px rgba(0,0,0,0.3)"
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
),
|
|
242
|
+
/* @__PURE__ */ jsx(
|
|
243
|
+
"span",
|
|
244
|
+
{
|
|
245
|
+
"data-thumb": true,
|
|
246
|
+
role: "slider",
|
|
247
|
+
tabIndex: 0,
|
|
248
|
+
"aria-label": "hue",
|
|
249
|
+
"aria-valuenow": Math.round(hue),
|
|
250
|
+
"aria-valuemin": 0,
|
|
251
|
+
"aria-valuemax": 360,
|
|
252
|
+
onKeyDown,
|
|
253
|
+
className: "absolute top-1/2 h-3.5 w-3.5 rounded-full focus-visible:outline-2 focus-visible:outline-offset-3 focus-visible:outline-ink",
|
|
254
|
+
style: { left: `${hue / 360 * 100}%`, ...thumbStyle(`hsl(${hue} 100% 50%)`, dragging) }
|
|
255
|
+
}
|
|
256
|
+
)
|
|
257
|
+
]
|
|
258
|
+
}
|
|
259
|
+
);
|
|
260
|
+
}
|
|
261
|
+
function ColorPicker({
|
|
262
|
+
value,
|
|
263
|
+
defaultValue = "#2563eb",
|
|
264
|
+
onChange,
|
|
265
|
+
label,
|
|
266
|
+
hint,
|
|
267
|
+
swatches,
|
|
268
|
+
inline = false,
|
|
269
|
+
name,
|
|
270
|
+
disabled = false,
|
|
271
|
+
fullWidth = false,
|
|
272
|
+
className = ""
|
|
273
|
+
}) {
|
|
274
|
+
const uid = useId();
|
|
275
|
+
const ref = useRef(null);
|
|
276
|
+
const buttonRef = useRef(null);
|
|
277
|
+
const [internal, setInternal] = useState(normalizeHex(defaultValue) ?? "#000000");
|
|
278
|
+
const color = (value !== void 0 ? normalizeHex(value) : null) ?? internal;
|
|
279
|
+
const panel = useFloatingPanel(ref, { onEscape: () => buttonRef.current?.focus() });
|
|
280
|
+
const { setFloating, panelStyle } = panel;
|
|
281
|
+
const set = (hex) => {
|
|
282
|
+
setInternal(hex);
|
|
283
|
+
onChange?.(hex);
|
|
284
|
+
};
|
|
285
|
+
const labelEl = label && /* @__PURE__ */ jsx(FieldLabel, { id: `${uid}-label`, children: label });
|
|
286
|
+
const hintEl = /* @__PURE__ */ jsx(FieldMessage, { hint });
|
|
287
|
+
const hidden = name && /* @__PURE__ */ jsx("input", { type: "hidden", name, value: color });
|
|
288
|
+
if (inline) {
|
|
289
|
+
return /* @__PURE__ */ jsxs("div", { role: "group", "aria-labelledby": label ? `${uid}-label` : void 0, className: ["flex flex-col gap-1.5", className].join(" "), children: [
|
|
290
|
+
labelEl,
|
|
291
|
+
/* @__PURE__ */ jsx("div", { className: "w-fit rounded-[16px] p-1.5", style: { background: "var(--panel-bezel)", boxShadow: "var(--panel-shadow)" }, children: /* @__PURE__ */ jsx("div", { className: "rounded-[11px] bg-panel-face p-3", style: { boxShadow: "var(--panel-face-shadow)" }, children: /* @__PURE__ */ jsx(ColorPanel, { value: color, onChange: set, swatches }) }) }),
|
|
292
|
+
hintEl,
|
|
293
|
+
hidden
|
|
294
|
+
] });
|
|
295
|
+
}
|
|
296
|
+
return /* @__PURE__ */ jsxs("div", { className: ["group/field flex flex-col gap-1.5", fullWidth ? "w-full" : ""].join(" "), children: [
|
|
297
|
+
labelEl,
|
|
298
|
+
/* @__PURE__ */ jsxs("div", { ref, className: ["relative", fullWidth ? "w-full" : "inline-block w-48", className].join(" "), children: [
|
|
299
|
+
/* @__PURE__ */ jsxs(
|
|
300
|
+
"button",
|
|
301
|
+
{
|
|
302
|
+
ref: buttonRef,
|
|
303
|
+
type: "button",
|
|
304
|
+
disabled,
|
|
305
|
+
onClick: panel.toggle,
|
|
306
|
+
"aria-haspopup": "dialog",
|
|
307
|
+
"aria-expanded": panel.open,
|
|
308
|
+
"aria-controls": panel.open ? `${uid}-panel` : void 0,
|
|
309
|
+
"aria-labelledby": label ? `${uid}-label ${uid}-value` : void 0,
|
|
310
|
+
"data-open": panel.open || void 0,
|
|
311
|
+
className: "field group/color relative isolate flex w-full cursor-pointer items-center gap-2.5 rounded-xl py-2.5 pl-2.5 pr-4 text-left focus:outline-none disabled:opacity-40 disabled:pointer-events-none",
|
|
312
|
+
children: [
|
|
313
|
+
/* @__PURE__ */ jsx(
|
|
314
|
+
"span",
|
|
315
|
+
{
|
|
316
|
+
"aria-hidden": true,
|
|
317
|
+
className: "h-6 w-6 shrink-0 rounded-[7px] transition-transform duration-300 ease-[var(--ease-spring)] group-hover/color:scale-105",
|
|
318
|
+
style: {
|
|
319
|
+
background: color,
|
|
320
|
+
boxShadow: "var(--key-shadow), inset 0 1px 1px rgba(255,255,255,0.3), inset 0 0 0 1px var(--hairline)"
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
),
|
|
324
|
+
/* @__PURE__ */ jsx("span", { id: `${uid}-value`, className: "flex-1 font-mono text-[13px] uppercase tracking-[0.02em] text-ink", children: color }),
|
|
325
|
+
/* @__PURE__ */ jsx(
|
|
326
|
+
"svg",
|
|
327
|
+
{
|
|
328
|
+
"aria-hidden": true,
|
|
329
|
+
width: "14",
|
|
330
|
+
height: "14",
|
|
331
|
+
fill: "none",
|
|
332
|
+
stroke: "currentColor",
|
|
333
|
+
strokeWidth: "2",
|
|
334
|
+
viewBox: "0 0 24 24",
|
|
335
|
+
className: [
|
|
336
|
+
"shrink-0 transition-[transform,color] duration-300 ease-[var(--ease-spring)]",
|
|
337
|
+
panel.open ? "rotate-180 text-ink" : "text-ink-faint group-hover/color:text-ink-label"
|
|
338
|
+
].join(" "),
|
|
339
|
+
children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M19 9l-7 7-7-7" })
|
|
340
|
+
}
|
|
341
|
+
)
|
|
342
|
+
]
|
|
343
|
+
}
|
|
344
|
+
),
|
|
345
|
+
hidden
|
|
346
|
+
] }),
|
|
347
|
+
panel.mounted && /* @__PURE__ */ jsx(Portal, { children: /* @__PURE__ */ jsx(
|
|
348
|
+
"div",
|
|
349
|
+
{
|
|
350
|
+
ref: setFloating,
|
|
351
|
+
id: `${uid}-panel`,
|
|
352
|
+
role: "dialog",
|
|
353
|
+
"aria-label": label ? `choose ${label}` : "choose a colour",
|
|
354
|
+
className: "rounded-[16px] p-1.5",
|
|
355
|
+
style: panelStyle,
|
|
356
|
+
children: /* @__PURE__ */ jsx("div", { className: "rounded-[11px] bg-panel-face p-3", style: { boxShadow: "var(--panel-face-shadow)" }, children: /* @__PURE__ */ jsx(ColorPanel, { value: color, onChange: set, swatches, autoFocus: true }) })
|
|
357
|
+
}
|
|
358
|
+
) }),
|
|
359
|
+
hintEl
|
|
360
|
+
] });
|
|
361
|
+
}
|
|
362
|
+
export {
|
|
363
|
+
ColorPicker,
|
|
364
|
+
normalizeHex
|
|
365
|
+
};
|
|
366
|
+
//# sourceMappingURL=ColorPicker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../components/ui/ColorPicker.tsx"],"sourcesContent":["\"use client\";\n\nimport React, { useId, useRef, useState } from \"react\";\nimport { Portal } from \"./Floating\";\nimport { FieldLabel, FieldMessage } from \"./FieldMessage\";\nimport { useFloatingPanel } from \"./useFloatingPanel\";\n\n/* ── Colour maths ─────────────────────────────────────────────────── */\ntype HSV = { h: number; s: number; v: number };\n\nconst clamp = (n: number, lo = 0, hi = 1) => Math.min(hi, Math.max(lo, n));\n\n/** Accepts #rgb, #rrggbb, with or without the hash. Returns #rrggbb or null */\nexport function normalizeHex(input: string): string | null {\n const s = input.trim().replace(/^#/, \"\").toLowerCase();\n if (/^[0-9a-f]{3}$/.test(s)) return \"#\" + s.split(\"\").map((c) => c + c).join(\"\");\n if (/^[0-9a-f]{6}$/.test(s)) return \"#\" + s;\n return null;\n}\n\nfunction hexToHsv(hex: string): HSV {\n const n = parseInt(hex.slice(1), 16);\n const r = ((n >> 16) & 255) / 255;\n const g = ((n >> 8) & 255) / 255;\n const b = (n & 255) / 255;\n const max = Math.max(r, g, b);\n const d = max - Math.min(r, g, b);\n let h = 0;\n if (d) {\n if (max === r) h = ((g - b) / d) % 6;\n else if (max === g) h = (b - r) / d + 2;\n else h = (r - g) / d + 4;\n h = (h * 60 + 360) % 360;\n }\n return { h, s: max ? d / max : 0, v: max };\n}\n\nfunction hsvToHex({ h, s, v }: HSV): string {\n const f = (n: number) => {\n const k = (n + h / 60) % 6;\n return Math.round((v - v * s * Math.max(0, Math.min(k, 4 - k, 1))) * 255);\n };\n return \"#\" + [f(5), f(3), f(1)].map((x) => x.toString(16).padStart(2, \"0\")).join(\"\");\n}\n\nconst DEFAULT_SWATCHES = [\n \"#171717\", \"#737373\", \"#dc2626\", \"#ea580c\", \"#ca8a04\",\n \"#16a34a\", \"#0891b2\", \"#2563eb\", \"#7c3aed\", \"#db2777\",\n];\n\n/* ── The panel: area, hue strip, hex field, swatches ──────────────── */\ninterface ColorPanelProps {\n value: string;\n onChange: (hex: string) => void;\n swatches?: string[];\n /** Focus the saturation knob as soon as the panel mounts */\n autoFocus?: boolean;\n}\n\nfunction ColorPanel({ value, onChange, swatches = DEFAULT_SWATCHES, autoFocus = false }: ColorPanelProps) {\n // Hue and saturation are kept apart from the hex, so dragging through\n // grey or black doesn't forget which colour you were on\n const [hsv, setHsv] = useState<HSV>(() => hexToHsv(value));\n const [draft, setDraft] = useState(value);\n\n // A colour set from outside (not one we just sent up) resets the panel\n const [seen, setSeen] = useState(value);\n if (value !== seen) {\n setSeen(value);\n if (value !== hsvToHex(hsv)) {\n setHsv(hexToHsv(value));\n setDraft(value);\n }\n }\n\n const update = (next: HSV) => {\n setHsv(next);\n const hex = hsvToHex(next);\n setDraft(hex);\n if (hex !== value) onChange(hex);\n };\n\n const pickHex = (hex: string) => {\n setHsv(hexToHsv(hex));\n setDraft(hex);\n if (hex !== value) onChange(hex);\n };\n\n return (\n <div className=\"flex w-[236px] flex-col gap-3\">\n <SaturationArea hsv={hsv} onChange={update} autoFocus={autoFocus} />\n <HueStrip hue={hsv.h} onChange={(h) => update({ ...hsv, h })} />\n\n <div className=\"flex items-center gap-2\">\n <span\n aria-hidden\n className=\"h-9 w-9 shrink-0 rounded-[10px]\"\n style={{ background: hsvToHex(hsv), boxShadow: \"var(--tile-shadow), inset 0 0 0 1px var(--hairline)\" }}\n />\n <div className=\"relative flex-1\">\n <span className=\"pointer-events-none absolute left-3 top-1/2 z-20 -translate-y-1/2 text-[12.5px] text-ink-faint\">#</span>\n <input\n aria-label=\"hex colour\"\n value={draft.replace(/^#/, \"\")}\n maxLength={7}\n spellCheck={false}\n autoComplete=\"off\"\n onChange={(e) => {\n setDraft(\"#\" + e.target.value.replace(/^#/, \"\"));\n const hex = normalizeHex(e.target.value);\n if (hex && e.target.value.replace(/^#/, \"\").length === 6) pickHex(hex);\n }}\n onBlur={() => {\n const hex = normalizeHex(draft);\n if (hex) pickHex(hex);\n else setDraft(hsvToHex(hsv));\n }}\n onKeyDown={(e) => {\n if (e.key === \"Enter\") {\n e.preventDefault();\n (e.target as HTMLInputElement).blur();\n }\n }}\n className=\"field relative z-10 w-full rounded-[10px] py-2 pl-6 pr-3 font-mono text-[12.5px] uppercase text-ink caret-ink\"\n />\n </div>\n </div>\n\n {swatches.length > 0 && (\n <div role=\"group\" aria-label=\"swatches\" className=\"grid grid-cols-10 gap-1.5 border-t border-line-strong pt-3\">\n {swatches.map((s) => {\n const hex = normalizeHex(s) ?? s;\n const on = hex === hsvToHex(hsv);\n return (\n <button\n key={s}\n type=\"button\"\n aria-label={hex}\n aria-pressed={on}\n onClick={() => pickHex(hex)}\n className=\"relative aspect-square cursor-pointer rounded-full transition-transform duration-200 ease-[var(--ease-spring)] hover:scale-115 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ink\"\n style={{\n background: hex,\n boxShadow: on\n ? \"0 0 0 2px var(--panel-face), 0 0 0 3.5px var(--ink)\"\n : \"inset 0 0 0 1px var(--hairline), inset 0 1px 1px rgba(255,255,255,0.3), var(--dot-shadow)\",\n }}\n />\n );\n })}\n </div>\n )}\n </div>\n );\n}\n\n/* Drag anywhere on an element and get back where you are, 0…1 on each axis */\nfunction useDrag(onMove: (x: number, y: number) => void) {\n const ref = useRef<HTMLDivElement>(null);\n const [dragging, setDragging] = useState(false);\n const at = (e: React.PointerEvent) => {\n const r = ref.current!.getBoundingClientRect();\n onMove(clamp((e.clientX - r.left) / r.width), clamp((e.clientY - r.top) / r.height));\n };\n const handlers = {\n onPointerDown: (e: React.PointerEvent) => {\n if (e.button !== 0) return;\n e.preventDefault();\n (e.currentTarget as HTMLElement).setPointerCapture(e.pointerId);\n (e.currentTarget.querySelector(\"[data-thumb]\") as HTMLElement | null)?.focus();\n setDragging(true);\n at(e);\n },\n onPointerMove: (e: React.PointerEvent) => dragging && at(e),\n onPointerUp: () => setDragging(false),\n onPointerCancel: () => setDragging(false),\n };\n return { ref, dragging, handlers };\n}\n\n/* A raised white ring that rides on the area and the strip */\nconst thumbStyle = (color: string, dragging: boolean): React.CSSProperties => ({\n background: color,\n boxShadow:\n \"0 0 0 2.5px #fff, 0 0 0 3px rgba(17,17,17,0.1), 0 2px 6px rgba(17,17,17,0.3), inset 0 0 0 1px rgba(17,17,17,0.1)\",\n transform: `translate(-50%, -50%) scale(${dragging ? 1.15 : 1})`,\n transition: \"transform 200ms var(--ease-spring)\",\n});\n\nfunction SaturationArea({ hsv, onChange, autoFocus }: { hsv: HSV; onChange: (hsv: HSV) => void; autoFocus?: boolean }) {\n const { ref, dragging, handlers } = useDrag((x, y) => onChange({ ...hsv, s: x, v: 1 - y }));\n const onKeyDown = (e: React.KeyboardEvent) => {\n const d = e.shiftKey ? 0.1 : 0.01;\n const moves: Record<string, Partial<HSV>> = {\n ArrowLeft: { s: clamp(hsv.s - d) },\n ArrowRight: { s: clamp(hsv.s + d) },\n ArrowUp: { v: clamp(hsv.v + d) },\n ArrowDown: { v: clamp(hsv.v - d) },\n };\n if (moves[e.key]) {\n e.preventDefault();\n onChange({ ...hsv, ...moves[e.key] });\n }\n };\n return (\n <div\n ref={ref}\n {...handlers}\n className=\"relative h-[148px] cursor-crosshair touch-none rounded-[10px]\"\n style={{\n background: `linear-gradient(to top, #000, transparent), linear-gradient(to right, #fff, hsl(${hsv.h} 100% 50%))`,\n /* Cut into the face like a field: shade inside, lit lip below */\n boxShadow: \"inset 0 0 0 1px var(--hairline), inset 0 1px 3px rgba(0,0,0,0.25)\",\n }}\n >\n <span\n data-thumb\n ref={autoFocus ? (el) => el?.focus({ preventScroll: true }) : undefined}\n role=\"slider\"\n tabIndex={0}\n aria-label=\"saturation and brightness\"\n aria-valuetext={`saturation ${Math.round(hsv.s * 100)}%, brightness ${Math.round(hsv.v * 100)}%`}\n aria-valuenow={Math.round(hsv.s * 100)}\n aria-valuemin={0}\n aria-valuemax={100}\n onKeyDown={onKeyDown}\n className=\"absolute h-3.5 w-3.5 rounded-full focus-visible:outline-2 focus-visible:outline-offset-3 focus-visible:outline-ink\"\n style={{ left: `${hsv.s * 100}%`, top: `${(1 - hsv.v) * 100}%`, ...thumbStyle(hsvToHex(hsv), dragging) }}\n />\n </div>\n );\n}\n\nfunction HueStrip({ hue, onChange }: { hue: number; onChange: (h: number) => void }) {\n const { ref, dragging, handlers } = useDrag((x) => onChange(Math.min(359.9, x * 360)));\n const onKeyDown = (e: React.KeyboardEvent) => {\n const d = e.shiftKey ? 10 : 1;\n if (e.key === \"ArrowLeft\" || e.key === \"ArrowDown\") { e.preventDefault(); onChange((hue - d + 360) % 360); }\n if (e.key === \"ArrowRight\" || e.key === \"ArrowUp\") { e.preventDefault(); onChange((hue + d) % 360); }\n };\n return (\n <div\n ref={ref}\n {...handlers}\n className=\"relative mx-[7px] h-3 cursor-pointer touch-none\"\n >\n <span\n aria-hidden\n className=\"absolute -inset-x-[7px] inset-y-0 rounded-full\"\n style={{\n background: \"linear-gradient(to right, #f00 0%, #ff0 17%, #0f0 33%, #0ff 50%, #00f 67%, #f0f 83%, #f00 100%)\",\n boxShadow: \"inset 0 0 0 1px var(--hairline), inset 0 1px 2px rgba(0,0,0,0.3)\",\n }}\n />\n <span\n data-thumb\n role=\"slider\"\n tabIndex={0}\n aria-label=\"hue\"\n aria-valuenow={Math.round(hue)}\n aria-valuemin={0}\n aria-valuemax={360}\n onKeyDown={onKeyDown}\n className=\"absolute top-1/2 h-3.5 w-3.5 rounded-full focus-visible:outline-2 focus-visible:outline-offset-3 focus-visible:outline-ink\"\n style={{ left: `${(hue / 360) * 100}%`, ...thumbStyle(`hsl(${hue} 100% 50%)`, dragging) }}\n />\n </div>\n );\n}\n\n/* ── ColorPicker ──────────────────────────────────────────────────── */\ninterface ColorPickerProps {\n /** A hex colour, like \"#2563eb\" */\n value?: string;\n defaultValue?: string;\n onChange?: (hex: string) => void;\n label?: string;\n hint?: string;\n /** Preset colours under the picker; pass [] to hide them */\n swatches?: string[];\n /** Render the panel straight onto the page instead of behind a field */\n inline?: boolean;\n /** Submit the colour with a form */\n name?: string;\n disabled?: boolean;\n fullWidth?: boolean;\n className?: string;\n}\n\n/* Closed, it's a sunken field holding a small raised chip of the colour.\n Open, a floating panel with a saturation area cut into its face, a hue\n strip, a hex field and a row of swatches. */\nexport function ColorPicker({\n value,\n defaultValue = \"#2563eb\",\n onChange,\n label,\n hint,\n swatches,\n inline = false,\n name,\n disabled = false,\n fullWidth = false,\n className = \"\",\n}: ColorPickerProps) {\n const uid = useId();\n const ref = useRef<HTMLDivElement>(null);\n const buttonRef = useRef<HTMLButtonElement>(null);\n const [internal, setInternal] = useState(normalizeHex(defaultValue) ?? \"#000000\");\n const color = (value !== undefined ? normalizeHex(value) : null) ?? internal;\n const panel = useFloatingPanel(ref, { onEscape: () => buttonRef.current?.focus() });\n const { setFloating, panelStyle } = panel;\n\n const set = (hex: string) => {\n setInternal(hex);\n onChange?.(hex);\n };\n\n const labelEl = label && <FieldLabel id={`${uid}-label`}>{label}</FieldLabel>;\n const hintEl = <FieldMessage hint={hint} />;\n const hidden = name && <input type=\"hidden\" name={name} value={color} />;\n\n if (inline) {\n return (\n <div role=\"group\" aria-labelledby={label ? `${uid}-label` : undefined} className={[\"flex flex-col gap-1.5\", className].join(\" \")}>\n {labelEl}\n <div className=\"w-fit rounded-[16px] p-1.5\" style={{ background: \"var(--panel-bezel)\", boxShadow: \"var(--panel-shadow)\" }}>\n <div className=\"rounded-[11px] bg-panel-face p-3\" style={{ boxShadow: \"var(--panel-face-shadow)\" }}>\n <ColorPanel value={color} onChange={set} swatches={swatches} />\n </div>\n </div>\n {hintEl}\n {hidden}\n </div>\n );\n }\n\n return (\n <div className={[\"group/field flex flex-col gap-1.5\", fullWidth ? \"w-full\" : \"\"].join(\" \")}>\n {labelEl}\n <div ref={ref} className={[\"relative\", fullWidth ? \"w-full\" : \"inline-block w-48\", className].join(\" \")}>\n <button\n ref={buttonRef}\n type=\"button\"\n disabled={disabled}\n onClick={panel.toggle}\n aria-haspopup=\"dialog\"\n aria-expanded={panel.open}\n aria-controls={panel.open ? `${uid}-panel` : undefined}\n aria-labelledby={label ? `${uid}-label ${uid}-value` : undefined}\n data-open={panel.open || undefined}\n className=\"field group/color relative isolate flex w-full cursor-pointer items-center gap-2.5 rounded-xl py-2.5 pl-2.5 pr-4 text-left focus:outline-none disabled:opacity-40 disabled:pointer-events-none\"\n >\n <span\n aria-hidden\n className=\"h-6 w-6 shrink-0 rounded-[7px] transition-transform duration-300 ease-[var(--ease-spring)] group-hover/color:scale-105\"\n style={{\n background: color,\n boxShadow:\n \"var(--key-shadow), inset 0 1px 1px rgba(255,255,255,0.3), inset 0 0 0 1px var(--hairline)\",\n }}\n />\n <span id={`${uid}-value`} className=\"flex-1 font-mono text-[13px] uppercase tracking-[0.02em] text-ink\">\n {color}\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 \"shrink-0 transition-[transform,color] duration-300 ease-[var(--ease-spring)]\",\n panel.open ? \"rotate-180 text-ink\" : \"text-ink-faint group-hover/color:text-ink-label\",\n ].join(\" \")}\n >\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M19 9l-7 7-7-7\" />\n </svg>\n </button>\n\n {hidden}\n </div>\n {panel.mounted && (\n <Portal>\n <div\n ref={setFloating}\n id={`${uid}-panel`}\n role=\"dialog\"\n aria-label={label ? `choose ${label}` : \"choose a colour\"}\n className=\"rounded-[16px] p-1.5\"\n style={panelStyle}\n >\n <div className=\"rounded-[11px] bg-panel-face p-3\" style={{ boxShadow: \"var(--panel-face-shadow)\" }}>\n <ColorPanel value={color} onChange={set} swatches={swatches} autoFocus />\n </div>\n </div>\n </Portal>\n )}\n {hintEl}\n </div>\n );\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
|
|
3
|
+
interface ComboboxOption {
|
|
4
|
+
value: string;
|
|
5
|
+
label: string;
|
|
6
|
+
/** A second, quieter line under the label */
|
|
7
|
+
description?: string;
|
|
8
|
+
/** Extra words to match on that aren't shown */
|
|
9
|
+
keywords?: string[];
|
|
10
|
+
disabled?: boolean;
|
|
11
|
+
}
|
|
12
|
+
interface ComboboxProps {
|
|
13
|
+
options: ComboboxOption[];
|
|
14
|
+
value?: string | null;
|
|
15
|
+
defaultValue?: string | null;
|
|
16
|
+
onChange?: (value: string | null) => void;
|
|
17
|
+
placeholder?: string;
|
|
18
|
+
label?: string;
|
|
19
|
+
hint?: string;
|
|
20
|
+
error?: string;
|
|
21
|
+
/** Shown when nothing matches */
|
|
22
|
+
emptyText?: string;
|
|
23
|
+
/** Replace the default "contains" match */
|
|
24
|
+
filter?: (option: ComboboxOption, query: string) => boolean;
|
|
25
|
+
/** Show a key that empties the field */
|
|
26
|
+
clearable?: boolean;
|
|
27
|
+
disabled?: boolean;
|
|
28
|
+
fullWidth?: boolean;
|
|
29
|
+
name?: string;
|
|
30
|
+
className?: string;
|
|
31
|
+
}
|
|
32
|
+
declare function Combobox({ options, value, defaultValue, onChange, placeholder, label, hint, error, emptyText, filter, clearable, disabled, fullWidth, name, className, }: ComboboxProps): react_jsx_runtime.JSX.Element;
|
|
33
|
+
|
|
34
|
+
export { Combobox, type ComboboxOption };
|