aporia 0.2.6 → 0.2.8
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/dist/components/Category.d.ts.map +1 -1
- package/dist/components/ColorPicker.d.ts.map +1 -1
- package/dist/components/ColorRow.d.ts.map +1 -1
- package/dist/components/GradientPicker.d.ts.map +1 -1
- package/dist/components/Panel.d.ts +16 -3
- package/dist/components/Panel.d.ts.map +1 -1
- package/dist/components/SliderRow.d.ts.map +1 -1
- package/dist/index.css +201 -25
- package/dist/index.js +423 -61
- package/dist/index.js.map +1 -1
- package/dist/utils/colorSpace.d.ts +12 -0
- package/dist/utils/colorSpace.d.ts.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -1,19 +1,391 @@
|
|
|
1
1
|
var __defProp = Object.defineProperty;
|
|
2
2
|
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
3
3
|
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
4
|
-
import {
|
|
5
|
-
import t, {
|
|
6
|
-
import { motion, useMotionValue, useSpring, useTransform } from "motion/react";
|
|
4
|
+
import { jsxs, jsx } from "react/jsx-runtime";
|
|
5
|
+
import t, { useState, useCallback, useRef, useEffect, useLayoutEffect, Fragment, useContext, createContext, useId, useMemo } from "react";
|
|
6
|
+
import { useReducedMotion, AnimatePresence, motion, useMotionValue, useSpring, useTransform } from "motion/react";
|
|
7
7
|
import { Popover, Select } from "@base-ui/react";
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
8
|
+
const EASE_IN_OUT_STRONG = [0.77, 0, 0.175, 1];
|
|
9
|
+
const MORPH_DURATION = 0.28;
|
|
10
|
+
const FAST_EASE_OUT = [0.23, 1, 0.32, 1];
|
|
11
|
+
const COLLAPSED_SIZE_PX = 32;
|
|
12
|
+
const DRAG_THRESHOLD_PX = 4;
|
|
13
|
+
const EDGE_PADDING_PX = 8;
|
|
14
|
+
const PANEL_EXPANDED_PADDING_PX = 16;
|
|
15
|
+
const PANEL_HEADER_HEIGHT_PX = 22;
|
|
16
|
+
const PANEL_BODY_MARGIN_TOP_PX = 16;
|
|
17
|
+
const SNAP_HINT_IDLE_OPACITY = 0.5;
|
|
18
|
+
const SNAP_HINT_ACTIVE_OPACITY = 0.8;
|
|
19
|
+
function clamp(n2, min, max) {
|
|
20
|
+
return Math.min(Math.max(n2, min), max);
|
|
21
|
+
}
|
|
22
|
+
function readRootPxVar(name, fallback) {
|
|
23
|
+
if (typeof window === "undefined" || typeof document === "undefined") {
|
|
24
|
+
return fallback;
|
|
25
|
+
}
|
|
26
|
+
const raw = window.getComputedStyle(document.documentElement).getPropertyValue(name).trim();
|
|
27
|
+
const parsed = Number.parseFloat(raw);
|
|
28
|
+
return Number.isFinite(parsed) ? parsed : fallback;
|
|
29
|
+
}
|
|
30
|
+
function anchorFromPoint(centerX, centerY, viewportWidth, viewportHeight) {
|
|
31
|
+
const vertical = centerY > viewportHeight / 2 ? "bottom" : "top";
|
|
32
|
+
const horizontal = centerX > viewportWidth / 2 ? "right" : "left";
|
|
33
|
+
return `${vertical}-${horizontal}`;
|
|
34
|
+
}
|
|
35
|
+
function anchorPosition(anchor, offsets, viewportWidth, viewportHeight) {
|
|
36
|
+
const left = anchor.endsWith("left") ? offsets.left : viewportWidth - offsets.right - COLLAPSED_SIZE_PX;
|
|
37
|
+
const top = anchor.startsWith("top") ? offsets.top : viewportHeight - offsets.bottom - COLLAPSED_SIZE_PX;
|
|
38
|
+
return { left, top };
|
|
39
|
+
}
|
|
40
|
+
function Panel({
|
|
41
|
+
children,
|
|
42
|
+
className,
|
|
43
|
+
floating = true,
|
|
44
|
+
collapsed: collapsedProp,
|
|
45
|
+
defaultCollapsed = false,
|
|
46
|
+
onCollapsedChange,
|
|
47
|
+
collapseAriaLabel = "Toggle panel",
|
|
48
|
+
style: externalStyle,
|
|
49
|
+
...domRest
|
|
50
|
+
}) {
|
|
51
|
+
const [uncontrolledCollapsed, setUncontrolledCollapsed] = useState(defaultCollapsed);
|
|
52
|
+
const shouldReduceMotion = useReducedMotion();
|
|
53
|
+
const collapsed = collapsedProp !== void 0 ? collapsedProp : uncontrolledCollapsed;
|
|
54
|
+
const setCollapsed = useCallback(
|
|
55
|
+
(next) => {
|
|
56
|
+
onCollapsedChange == null ? void 0 : onCollapsedChange(next);
|
|
57
|
+
if (collapsedProp === void 0) {
|
|
58
|
+
setUncontrolledCollapsed(next);
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
[collapsedProp, onCollapsedChange]
|
|
62
|
+
);
|
|
63
|
+
const prevCollapsedRef = useRef(collapsed);
|
|
64
|
+
const collapsedChanged = prevCollapsedRef.current !== collapsed;
|
|
65
|
+
useEffect(() => {
|
|
66
|
+
prevCollapsedRef.current = collapsed;
|
|
67
|
+
}, [collapsed]);
|
|
68
|
+
const shellHeightTransition = shouldReduceMotion ? { duration: 0 } : collapsedChanged ? { duration: MORPH_DURATION, ease: EASE_IN_OUT_STRONG } : { duration: 0 };
|
|
69
|
+
const shellShapeTransition = shouldReduceMotion ? { duration: 0 } : collapsedChanged ? { duration: MORPH_DURATION, ease: EASE_IN_OUT_STRONG } : { duration: 0 };
|
|
70
|
+
const bodyTransition = shouldReduceMotion ? { duration: 0 } : { duration: collapsed ? 0.12 : 0.18, ease: FAST_EASE_OUT };
|
|
71
|
+
const [floatingAnchor, setFloatingAnchor] = useState("top-left");
|
|
72
|
+
const bodyRef = useRef(null);
|
|
73
|
+
const bodyContentRef = useRef(null);
|
|
74
|
+
const [measuredBodyHeight, setMeasuredBodyHeight] = useState(0);
|
|
75
|
+
const [viewportHeight, setViewportHeight] = useState(
|
|
76
|
+
() => typeof window !== "undefined" ? window.innerHeight : 0
|
|
77
|
+
);
|
|
78
|
+
const [dragRect, setDragRect] = useState(null);
|
|
79
|
+
const [isDragging, setIsDragging] = useState(false);
|
|
80
|
+
const justDraggedRef = useRef(false);
|
|
81
|
+
const dragSessionRef = useRef(null);
|
|
82
|
+
useLayoutEffect(() => {
|
|
83
|
+
const bodyContent = bodyContentRef.current;
|
|
84
|
+
if (!bodyContent) return;
|
|
85
|
+
const measure = () => {
|
|
86
|
+
setMeasuredBodyHeight(bodyContent.scrollHeight);
|
|
87
|
+
};
|
|
88
|
+
measure();
|
|
89
|
+
const ro = new ResizeObserver(measure);
|
|
90
|
+
ro.observe(bodyContent);
|
|
91
|
+
return () => ro.disconnect();
|
|
92
|
+
}, []);
|
|
93
|
+
useEffect(() => {
|
|
94
|
+
if (typeof window === "undefined") return;
|
|
95
|
+
const onResize = () => setViewportHeight(window.innerHeight);
|
|
96
|
+
window.addEventListener("resize", onResize);
|
|
97
|
+
return () => window.removeEventListener("resize", onResize);
|
|
98
|
+
}, []);
|
|
99
|
+
const handleShellClick = () => {
|
|
100
|
+
if (!collapsed) return;
|
|
101
|
+
if (justDraggedRef.current) {
|
|
102
|
+
justDraggedRef.current = false;
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
setCollapsed(false);
|
|
106
|
+
};
|
|
107
|
+
const handleShellKeyDown = (e) => {
|
|
108
|
+
if (!collapsed) return;
|
|
109
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
110
|
+
e.preventDefault();
|
|
111
|
+
setCollapsed(false);
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
const handlePointerDown = (e) => {
|
|
115
|
+
if (!collapsed || !floating) return;
|
|
116
|
+
if (e.button !== 0) return;
|
|
117
|
+
justDraggedRef.current = false;
|
|
118
|
+
const rect = e.currentTarget.getBoundingClientRect();
|
|
119
|
+
dragSessionRef.current = {
|
|
120
|
+
pointerId: e.pointerId,
|
|
121
|
+
startClientX: e.clientX,
|
|
122
|
+
startClientY: e.clientY,
|
|
123
|
+
startTop: rect.top,
|
|
124
|
+
startLeft: rect.left,
|
|
125
|
+
moved: false
|
|
126
|
+
};
|
|
127
|
+
setDragRect({ top: rect.top, left: rect.left });
|
|
128
|
+
e.currentTarget.setPointerCapture(e.pointerId);
|
|
129
|
+
};
|
|
130
|
+
const handlePointerMove = (e) => {
|
|
131
|
+
const session = dragSessionRef.current;
|
|
132
|
+
if (!session || session.pointerId !== e.pointerId) return;
|
|
133
|
+
const deltaY = e.clientY - session.startClientY;
|
|
134
|
+
const deltaX = e.clientX - session.startClientX;
|
|
135
|
+
if (!session.moved && (Math.abs(deltaY) >= DRAG_THRESHOLD_PX || Math.abs(deltaX) >= DRAG_THRESHOLD_PX)) {
|
|
136
|
+
session.moved = true;
|
|
137
|
+
setIsDragging(true);
|
|
138
|
+
}
|
|
139
|
+
if (!session.moved) return;
|
|
140
|
+
const viewportHeight2 = typeof window !== "undefined" ? window.innerHeight : COLLAPSED_SIZE_PX + EDGE_PADDING_PX * 2;
|
|
141
|
+
const viewportWidth = typeof window !== "undefined" ? window.innerWidth : COLLAPSED_SIZE_PX + EDGE_PADDING_PX * 2;
|
|
142
|
+
const maxTop = Math.max(
|
|
143
|
+
EDGE_PADDING_PX,
|
|
144
|
+
viewportHeight2 - COLLAPSED_SIZE_PX - EDGE_PADDING_PX
|
|
145
|
+
);
|
|
146
|
+
const maxLeft = Math.max(
|
|
147
|
+
EDGE_PADDING_PX,
|
|
148
|
+
viewportWidth - COLLAPSED_SIZE_PX - EDGE_PADDING_PX
|
|
149
|
+
);
|
|
150
|
+
setDragRect({
|
|
151
|
+
top: clamp(session.startTop + deltaY, EDGE_PADDING_PX, maxTop),
|
|
152
|
+
left: clamp(session.startLeft + deltaX, EDGE_PADDING_PX, maxLeft)
|
|
153
|
+
});
|
|
154
|
+
};
|
|
155
|
+
const handlePointerUp = (e) => {
|
|
156
|
+
const session = dragSessionRef.current;
|
|
157
|
+
if (!session || session.pointerId !== e.pointerId) return;
|
|
158
|
+
if (e.currentTarget.hasPointerCapture(e.pointerId)) {
|
|
159
|
+
e.currentTarget.releasePointerCapture(e.pointerId);
|
|
160
|
+
}
|
|
161
|
+
dragSessionRef.current = null;
|
|
162
|
+
setIsDragging(false);
|
|
163
|
+
if (!session.moved) {
|
|
164
|
+
setDragRect(null);
|
|
165
|
+
return;
|
|
15
166
|
}
|
|
167
|
+
justDraggedRef.current = true;
|
|
168
|
+
const viewportHeight2 = typeof window !== "undefined" ? window.innerHeight : COLLAPSED_SIZE_PX + EDGE_PADDING_PX * 2;
|
|
169
|
+
const viewportWidth = typeof window !== "undefined" ? window.innerWidth : COLLAPSED_SIZE_PX + EDGE_PADDING_PX * 2;
|
|
170
|
+
const maxTop = Math.max(
|
|
171
|
+
EDGE_PADDING_PX,
|
|
172
|
+
viewportHeight2 - COLLAPSED_SIZE_PX - EDGE_PADDING_PX
|
|
173
|
+
);
|
|
174
|
+
const maxLeft = Math.max(
|
|
175
|
+
EDGE_PADDING_PX,
|
|
176
|
+
viewportWidth - COLLAPSED_SIZE_PX - EDGE_PADDING_PX
|
|
177
|
+
);
|
|
178
|
+
const finalTop = clamp(
|
|
179
|
+
(dragRect == null ? void 0 : dragRect.top) ?? session.startTop,
|
|
180
|
+
EDGE_PADDING_PX,
|
|
181
|
+
maxTop
|
|
182
|
+
);
|
|
183
|
+
const finalLeft = clamp(
|
|
184
|
+
(dragRect == null ? void 0 : dragRect.left) ?? session.startLeft,
|
|
185
|
+
EDGE_PADDING_PX,
|
|
186
|
+
maxLeft
|
|
187
|
+
);
|
|
188
|
+
const centerY = finalTop + COLLAPSED_SIZE_PX / 2;
|
|
189
|
+
const centerX = finalLeft + COLLAPSED_SIZE_PX / 2;
|
|
190
|
+
setFloatingAnchor(anchorFromPoint(centerX, centerY, viewportWidth, viewportHeight2));
|
|
191
|
+
setDragRect(null);
|
|
192
|
+
};
|
|
193
|
+
const offsets = {
|
|
194
|
+
top: readRootPxVar("--aporia-panel-offset-top", 40),
|
|
195
|
+
left: readRootPxVar("--aporia-panel-offset-left", 40),
|
|
196
|
+
right: readRootPxVar("--aporia-panel-offset-right", 40),
|
|
197
|
+
bottom: readRootPxVar("--aporia-panel-offset-bottom", 40),
|
|
198
|
+
zIndex: readRootPxVar("--aporia-panel-z-index", 1e3)
|
|
199
|
+
};
|
|
200
|
+
const expandedChromeHeight = PANEL_EXPANDED_PADDING_PX * 2 + PANEL_HEADER_HEIGHT_PX + PANEL_BODY_MARGIN_TOP_PX;
|
|
201
|
+
const expandedContentHeight = expandedChromeHeight + measuredBodyHeight;
|
|
202
|
+
const maxExpandedHeight = floating ? Math.max(
|
|
203
|
+
COLLAPSED_SIZE_PX,
|
|
204
|
+
viewportHeight - offsets.top - offsets.bottom
|
|
205
|
+
) : Number.POSITIVE_INFINITY;
|
|
206
|
+
const targetExpandedHeight = Math.max(
|
|
207
|
+
COLLAPSED_SIZE_PX,
|
|
208
|
+
Math.min(expandedContentHeight, maxExpandedHeight)
|
|
16
209
|
);
|
|
210
|
+
const viewport = {
|
|
211
|
+
width: typeof window !== "undefined" ? window.innerWidth : COLLAPSED_SIZE_PX + EDGE_PADDING_PX * 2,
|
|
212
|
+
height: typeof window !== "undefined" ? window.innerHeight : COLLAPSED_SIZE_PX + EDGE_PADDING_PX * 2
|
|
213
|
+
};
|
|
214
|
+
const floatingStyle = (() => {
|
|
215
|
+
if (!floating) return void 0;
|
|
216
|
+
if (dragRect !== null) {
|
|
217
|
+
return {
|
|
218
|
+
left: `${dragRect.left}px`,
|
|
219
|
+
top: `${dragRect.top}px`,
|
|
220
|
+
bottom: "auto",
|
|
221
|
+
right: "auto"
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
if (floatingAnchor === "bottom-left") {
|
|
225
|
+
return {
|
|
226
|
+
left: `${offsets.left}px`,
|
|
227
|
+
bottom: `${offsets.bottom}px`,
|
|
228
|
+
top: "auto",
|
|
229
|
+
right: "auto"
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
if (floatingAnchor === "top-right") {
|
|
233
|
+
return {
|
|
234
|
+
right: `${offsets.right}px`,
|
|
235
|
+
top: `${offsets.top}px`,
|
|
236
|
+
left: "auto",
|
|
237
|
+
bottom: "auto"
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
if (floatingAnchor === "bottom-right") {
|
|
241
|
+
return {
|
|
242
|
+
right: `${offsets.right}px`,
|
|
243
|
+
bottom: `${offsets.bottom}px`,
|
|
244
|
+
left: "auto",
|
|
245
|
+
top: "auto"
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
return {
|
|
249
|
+
left: `${offsets.left}px`,
|
|
250
|
+
top: `${offsets.top}px`,
|
|
251
|
+
bottom: "auto",
|
|
252
|
+
right: "auto"
|
|
253
|
+
};
|
|
254
|
+
})();
|
|
255
|
+
const snapHints = (() => {
|
|
256
|
+
if (!floating || !collapsed || !isDragging || dragRect === null) return [];
|
|
257
|
+
const corners = [
|
|
258
|
+
"top-left",
|
|
259
|
+
"top-right",
|
|
260
|
+
"bottom-left",
|
|
261
|
+
"bottom-right"
|
|
262
|
+
];
|
|
263
|
+
const dragCenter = {
|
|
264
|
+
x: dragRect.left + COLLAPSED_SIZE_PX / 2,
|
|
265
|
+
y: dragRect.top + COLLAPSED_SIZE_PX / 2
|
|
266
|
+
};
|
|
267
|
+
const activeAnchor = anchorFromPoint(
|
|
268
|
+
dragCenter.x,
|
|
269
|
+
dragCenter.y,
|
|
270
|
+
viewport.width,
|
|
271
|
+
viewport.height
|
|
272
|
+
);
|
|
273
|
+
return corners.map((anchor) => {
|
|
274
|
+
const pos = anchorPosition(anchor, offsets, viewport.width, viewport.height);
|
|
275
|
+
const isActive = anchor === activeAnchor;
|
|
276
|
+
return {
|
|
277
|
+
anchor,
|
|
278
|
+
left: pos.left,
|
|
279
|
+
top: pos.top,
|
|
280
|
+
opacity: isActive ? SNAP_HINT_ACTIVE_OPACITY : SNAP_HINT_IDLE_OPACITY
|
|
281
|
+
};
|
|
282
|
+
});
|
|
283
|
+
})();
|
|
284
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
285
|
+
/* @__PURE__ */ jsx(AnimatePresence, { children: snapHints.map((hint) => /* @__PURE__ */ jsx(
|
|
286
|
+
motion.div,
|
|
287
|
+
{
|
|
288
|
+
className: "aporiaPanelSnapHint",
|
|
289
|
+
initial: { opacity: 0 },
|
|
290
|
+
animate: { opacity: hint.opacity },
|
|
291
|
+
exit: { opacity: 0 },
|
|
292
|
+
transition: shouldReduceMotion ? { duration: 0 } : { duration: 0.18, ease: FAST_EASE_OUT },
|
|
293
|
+
style: {
|
|
294
|
+
top: hint.top,
|
|
295
|
+
left: hint.left,
|
|
296
|
+
zIndex: Math.max(0, Math.round(offsets.zIndex - 1))
|
|
297
|
+
}
|
|
298
|
+
},
|
|
299
|
+
hint.anchor
|
|
300
|
+
)) }),
|
|
301
|
+
/* @__PURE__ */ jsxs(
|
|
302
|
+
motion.section,
|
|
303
|
+
{
|
|
304
|
+
...domRest,
|
|
305
|
+
initial: false,
|
|
306
|
+
onClick: handleShellClick,
|
|
307
|
+
onKeyDown: handleShellKeyDown,
|
|
308
|
+
onPointerDown: handlePointerDown,
|
|
309
|
+
onPointerMove: handlePointerMove,
|
|
310
|
+
onPointerUp: handlePointerUp,
|
|
311
|
+
onPointerCancel: handlePointerUp,
|
|
312
|
+
role: collapsed ? "button" : void 0,
|
|
313
|
+
tabIndex: collapsed ? 0 : void 0,
|
|
314
|
+
"aria-label": collapsed ? "Expand panel" : void 0,
|
|
315
|
+
animate: {
|
|
316
|
+
height: collapsed ? COLLAPSED_SIZE_PX : targetExpandedHeight,
|
|
317
|
+
borderRadius: collapsed ? 12 : 24,
|
|
318
|
+
padding: collapsed ? 5 : 16,
|
|
319
|
+
scale: collapsed && isDragging ? 1.06 : 1,
|
|
320
|
+
rotate: collapsed && isDragging ? -2.5 : 0,
|
|
321
|
+
y: collapsed && isDragging ? -4 : 0
|
|
322
|
+
},
|
|
323
|
+
transition: {
|
|
324
|
+
height: shellHeightTransition,
|
|
325
|
+
borderRadius: shellShapeTransition,
|
|
326
|
+
padding: shellShapeTransition,
|
|
327
|
+
scale: shouldReduceMotion ? { duration: 0 } : { duration: 0.18, ease: FAST_EASE_OUT },
|
|
328
|
+
rotate: shouldReduceMotion ? { duration: 0 } : { duration: 0.18, ease: FAST_EASE_OUT },
|
|
329
|
+
y: shouldReduceMotion ? { duration: 0 } : { duration: 0.18, ease: FAST_EASE_OUT }
|
|
330
|
+
},
|
|
331
|
+
style: {
|
|
332
|
+
...externalStyle,
|
|
333
|
+
...floatingStyle
|
|
334
|
+
},
|
|
335
|
+
className: ["aporiaPanel", className].filter(Boolean).join(" "),
|
|
336
|
+
"data-collapsed": collapsed ? "true" : "false",
|
|
337
|
+
"data-floating": floating ? "true" : "false",
|
|
338
|
+
"data-anchor": floatingAnchor,
|
|
339
|
+
"data-dragging": isDragging ? "true" : "false",
|
|
340
|
+
children: [
|
|
341
|
+
/* @__PURE__ */ jsx("div", { className: "aporiaPanelHeader", children: /* @__PURE__ */ jsx(
|
|
342
|
+
"button",
|
|
343
|
+
{
|
|
344
|
+
type: "button",
|
|
345
|
+
className: "aporiaPanelToggle",
|
|
346
|
+
"aria-label": collapseAriaLabel,
|
|
347
|
+
"aria-pressed": collapsed,
|
|
348
|
+
onClick: () => setCollapsed(!collapsed),
|
|
349
|
+
children: /* @__PURE__ */ jsx(
|
|
350
|
+
"svg",
|
|
351
|
+
{
|
|
352
|
+
className: "aporiaPanelLogoGlyph",
|
|
353
|
+
viewBox: "0 0 22 22",
|
|
354
|
+
"aria-hidden": "true",
|
|
355
|
+
focusable: "false",
|
|
356
|
+
children: /* @__PURE__ */ jsx(
|
|
357
|
+
"path",
|
|
358
|
+
{
|
|
359
|
+
fillRule: "evenodd",
|
|
360
|
+
clipRule: "evenodd",
|
|
361
|
+
d: "M17.6185 16.089C18 15.3403 18 14.3602 18 12.4V9.6C18 7.63982 18 6.65972 17.6185 5.91103C17.283 5.25247 16.7475 4.71703 16.089 4.38148C15.3403 4 14.3602 4 12.4 4H9.6C7.63982 4 6.65972 4 5.91103 4.38148C5.25247 4.71703 4.71703 5.25247 4.38148 5.91103C4 6.65972 4 7.63982 4 9.6V12.4C4 14.3602 4 15.3403 4.38148 16.089C4.71703 16.7475 5.25247 17.283 5.91103 17.6185C6.65972 18 7.63982 18 9.6 18H12.4C14.3602 18 15.3403 18 16.089 17.6185C16.7475 17.283 17.283 16.7475 17.6185 16.089ZM10.125 13.4062V8.59375C10.125 7.5745 10.125 7.06488 10.2915 6.66288C10.5135 6.12688 10.9394 5.70103 11.4754 5.47901C11.8774 5.3125 12.387 5.3125 13.4062 5.3125C14.4255 5.3125 14.9351 5.3125 15.3371 5.47901C15.8731 5.70103 16.299 6.12688 16.521 6.66288C16.6875 7.06488 16.6875 7.5745 16.6875 8.59375V13.4062C16.6875 14.4255 16.6875 14.9351 16.521 15.3371C16.299 15.8731 15.8731 16.299 15.3371 16.521C14.9351 16.6875 14.4255 16.6875 13.4062 16.6875C12.387 16.6875 11.8774 16.6875 11.4754 16.521C10.9394 16.299 10.5135 15.8731 10.2915 15.3371C10.125 14.9351 10.125 14.4255 10.125 13.4062Z",
|
|
362
|
+
fill: "currentColor"
|
|
363
|
+
}
|
|
364
|
+
)
|
|
365
|
+
}
|
|
366
|
+
)
|
|
367
|
+
}
|
|
368
|
+
) }),
|
|
369
|
+
/* @__PURE__ */ jsx(
|
|
370
|
+
motion.div,
|
|
371
|
+
{
|
|
372
|
+
ref: bodyRef,
|
|
373
|
+
className: "aporiaPanelBody",
|
|
374
|
+
"aria-hidden": collapsed,
|
|
375
|
+
inert: collapsed,
|
|
376
|
+
initial: false,
|
|
377
|
+
animate: {
|
|
378
|
+
opacity: collapsed ? 0 : 1,
|
|
379
|
+
marginTop: collapsed ? 0 : 16
|
|
380
|
+
},
|
|
381
|
+
transition: bodyTransition,
|
|
382
|
+
children: /* @__PURE__ */ jsx("div", { ref: bodyContentRef, className: "aporiaPanelBodyContent", children })
|
|
383
|
+
}
|
|
384
|
+
)
|
|
385
|
+
]
|
|
386
|
+
}
|
|
387
|
+
)
|
|
388
|
+
] });
|
|
17
389
|
}
|
|
18
390
|
const CategoryDisabledContext = createContext(false);
|
|
19
391
|
function CategoryDisabledProvider({
|
|
@@ -90,8 +462,8 @@ function Category({
|
|
|
90
462
|
{
|
|
91
463
|
className: "categoryChevronIcon",
|
|
92
464
|
"data-collapsed": collapsed ? "true" : "false",
|
|
93
|
-
width: "
|
|
94
|
-
height: "
|
|
465
|
+
width: "14",
|
|
466
|
+
height: "14",
|
|
95
467
|
viewBox: "0 0 12 12",
|
|
96
468
|
"aria-hidden": true,
|
|
97
469
|
children: /* @__PURE__ */ jsx(
|
|
@@ -153,8 +525,9 @@ function Category({
|
|
|
153
525
|
opacity: collapsed ? { duration: 0.09, ease: "easeIn" } : { duration: 0.15, ease: "easeOut" }
|
|
154
526
|
},
|
|
155
527
|
style: {
|
|
156
|
-
|
|
157
|
-
|
|
528
|
+
// Keep overdrag visible while open, but fully clip collapsed content so
|
|
529
|
+
// the last category cannot leak height into panel measurements.
|
|
530
|
+
overflow: collapsed ? "hidden" : "visible",
|
|
158
531
|
minHeight: 0,
|
|
159
532
|
boxSizing: "border-box"
|
|
160
533
|
},
|
|
@@ -835,21 +1208,19 @@ function SliderRow({
|
|
|
835
1208
|
if (Math.abs(leftExtra) < STRETCH_SNAP_EPS_PX) leftExtra = 0;
|
|
836
1209
|
return Math.round(-leftExtra);
|
|
837
1210
|
});
|
|
838
|
-
const
|
|
1211
|
+
const syncBaseWidthFromWrap = () => {
|
|
839
1212
|
const wrap = wrapRef.current;
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
baseW.set(parent.offsetWidth);
|
|
1213
|
+
if (!wrap) return;
|
|
1214
|
+
baseW.set(wrap.offsetWidth);
|
|
843
1215
|
};
|
|
844
1216
|
useLayoutEffect(() => {
|
|
845
1217
|
const wrap = wrapRef.current;
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
syncBaseWidthFromParent();
|
|
1218
|
+
if (!wrap) return;
|
|
1219
|
+
syncBaseWidthFromWrap();
|
|
849
1220
|
const ro = new ResizeObserver(() => {
|
|
850
|
-
|
|
1221
|
+
syncBaseWidthFromWrap();
|
|
851
1222
|
});
|
|
852
|
-
ro.observe(
|
|
1223
|
+
ro.observe(wrap);
|
|
853
1224
|
return () => ro.disconnect();
|
|
854
1225
|
}, [baseW]);
|
|
855
1226
|
useEffect(() => {
|
|
@@ -932,7 +1303,7 @@ function SliderRow({
|
|
|
932
1303
|
const onEnd = () => {
|
|
933
1304
|
resetPull();
|
|
934
1305
|
requestAnimationFrame(() => {
|
|
935
|
-
requestAnimationFrame(
|
|
1306
|
+
requestAnimationFrame(syncBaseWidthFromWrap);
|
|
936
1307
|
});
|
|
937
1308
|
};
|
|
938
1309
|
window.addEventListener("pointermove", onMove);
|
|
@@ -999,7 +1370,7 @@ function SliderRow({
|
|
|
999
1370
|
if (disabled) return;
|
|
1000
1371
|
clearDocumentSelection();
|
|
1001
1372
|
lastPointerXRef.current = e.clientX;
|
|
1002
|
-
|
|
1373
|
+
syncBaseWidthFromWrap();
|
|
1003
1374
|
try {
|
|
1004
1375
|
e.currentTarget.setPointerCapture(e.pointerId);
|
|
1005
1376
|
} catch {
|
|
@@ -1019,7 +1390,7 @@ function SliderRow({
|
|
|
1019
1390
|
resetPull();
|
|
1020
1391
|
setDragging(false);
|
|
1021
1392
|
requestAnimationFrame(() => {
|
|
1022
|
-
requestAnimationFrame(
|
|
1393
|
+
requestAnimationFrame(syncBaseWidthFromWrap);
|
|
1023
1394
|
});
|
|
1024
1395
|
};
|
|
1025
1396
|
return /* @__PURE__ */ jsx(
|
|
@@ -1137,7 +1508,7 @@ function SliderRow({
|
|
|
1137
1508
|
resetPull();
|
|
1138
1509
|
setDragging(false);
|
|
1139
1510
|
requestAnimationFrame(() => {
|
|
1140
|
-
requestAnimationFrame(
|
|
1511
|
+
requestAnimationFrame(syncBaseWidthFromWrap);
|
|
1141
1512
|
});
|
|
1142
1513
|
},
|
|
1143
1514
|
onLostPointerCapture: () => {
|
|
@@ -1146,7 +1517,7 @@ function SliderRow({
|
|
|
1146
1517
|
resetPull();
|
|
1147
1518
|
setDragging(false);
|
|
1148
1519
|
requestAnimationFrame(() => {
|
|
1149
|
-
requestAnimationFrame(
|
|
1520
|
+
requestAnimationFrame(syncBaseWidthFromWrap);
|
|
1150
1521
|
});
|
|
1151
1522
|
}
|
|
1152
1523
|
}
|
|
@@ -1212,6 +1583,14 @@ function normalizeHex(raw) {
|
|
|
1212
1583
|
}
|
|
1213
1584
|
return `#${h2.toUpperCase()}`;
|
|
1214
1585
|
}
|
|
1586
|
+
function sanitizeHexDigits(raw) {
|
|
1587
|
+
return raw.replace(/[^0-9a-fA-F]/g, "").slice(0, 6);
|
|
1588
|
+
}
|
|
1589
|
+
function expandHexDigitsToSix(raw) {
|
|
1590
|
+
const digits = sanitizeHexDigits(raw);
|
|
1591
|
+
if (!digits) return null;
|
|
1592
|
+
return digits.repeat(Math.ceil(6 / digits.length)).slice(0, 6).toUpperCase();
|
|
1593
|
+
}
|
|
1215
1594
|
function hslToHex(h2, s, l2) {
|
|
1216
1595
|
const [r, g, b2] = hslToRgbBytes(h2, s, l2);
|
|
1217
1596
|
const toHex = (v2) => v2.toString(16).padStart(2, "0");
|
|
@@ -1449,12 +1828,6 @@ function clampHueDeg(h2) {
|
|
|
1449
1828
|
function clampInt(n2, min, max) {
|
|
1450
1829
|
return Math.max(min, Math.min(max, Math.round(n2)));
|
|
1451
1830
|
}
|
|
1452
|
-
function normalizeHexDigits(raw) {
|
|
1453
|
-
return raw.replace(/[^0-9a-fA-F]/g, "").slice(0, 6);
|
|
1454
|
-
}
|
|
1455
|
-
function isValidSixHex$2(d2) {
|
|
1456
|
-
return /^[0-9a-fA-F]{6}$/.test(d2);
|
|
1457
|
-
}
|
|
1458
1831
|
function clampHsv01(base) {
|
|
1459
1832
|
return { h: clampHueDeg(base.h), s: clamp01(base.s), v: clamp01(base.v) };
|
|
1460
1833
|
}
|
|
@@ -1798,11 +2171,11 @@ function ColorPicker({ value, onChange }) {
|
|
|
1798
2171
|
value: hex.slice(1),
|
|
1799
2172
|
prefix: "#",
|
|
1800
2173
|
onCommit: (val) => {
|
|
1801
|
-
const
|
|
1802
|
-
if (
|
|
2174
|
+
const expanded = expandHexDigitsToSix(val);
|
|
2175
|
+
if (expanded) onChange(`#${expanded}`);
|
|
1803
2176
|
},
|
|
1804
|
-
sanitize:
|
|
1805
|
-
validate:
|
|
2177
|
+
sanitize: sanitizeHexDigits,
|
|
2178
|
+
validate: (v2) => sanitizeHexDigits(v2).length > 0,
|
|
1806
2179
|
maxLength: 6,
|
|
1807
2180
|
inputMode: "text",
|
|
1808
2181
|
ariaLabel: `Edit hex color, ${hex}`,
|
|
@@ -1813,7 +2186,7 @@ function ColorPicker({ value, onChange }) {
|
|
|
1813
2186
|
onChange(parsed);
|
|
1814
2187
|
return "";
|
|
1815
2188
|
}
|
|
1816
|
-
return
|
|
2189
|
+
return sanitizeHexDigits(pasted);
|
|
1817
2190
|
}
|
|
1818
2191
|
}
|
|
1819
2192
|
) : mode === "hsl" ? /* @__PURE__ */ jsxs("div", { className: "colorPickerTriplet", "aria-label": "HSL values", children: [
|
|
@@ -2050,20 +2423,15 @@ function hexDigits$1(hex) {
|
|
|
2050
2423
|
const n2 = normalizeHex(hex);
|
|
2051
2424
|
return n2.slice(1);
|
|
2052
2425
|
}
|
|
2053
|
-
function isValidSixHex$1(d2) {
|
|
2054
|
-
return /^[0-9a-fA-F]{6}$/.test(d2);
|
|
2055
|
-
}
|
|
2056
|
-
function sanitizeHex$1(input) {
|
|
2057
|
-
return input.replace(/[^0-9a-fA-F]/g, "").slice(0, 6);
|
|
2058
|
-
}
|
|
2059
2426
|
function ColorRow({ label = "Color", value, onChange, disabled: disabledProp }) {
|
|
2060
2427
|
const categoryDisabled = useCategoryDisabled();
|
|
2061
2428
|
const disabled = Boolean(disabledProp || categoryDisabled);
|
|
2062
2429
|
const [hovered, setHovered] = useState(false);
|
|
2063
2430
|
const hex = normalizeHex(value);
|
|
2064
2431
|
const handleCommit = (sanitized) => {
|
|
2065
|
-
|
|
2066
|
-
|
|
2432
|
+
const expanded = expandHexDigitsToSix(sanitized);
|
|
2433
|
+
if (expanded) {
|
|
2434
|
+
onChange(`#${expanded}`);
|
|
2067
2435
|
}
|
|
2068
2436
|
};
|
|
2069
2437
|
const handlePaste = (pasted) => {
|
|
@@ -2085,8 +2453,8 @@ function ColorRow({ label = "Color", value, onChange, disabled: disabledProp })
|
|
|
2085
2453
|
value: hexDigits$1(hex),
|
|
2086
2454
|
prefix: "#",
|
|
2087
2455
|
onCommit: handleCommit,
|
|
2088
|
-
sanitize:
|
|
2089
|
-
validate:
|
|
2456
|
+
sanitize: sanitizeHexDigits,
|
|
2457
|
+
validate: (v2) => sanitizeHexDigits(v2).length > 0,
|
|
2090
2458
|
maxLength: 6,
|
|
2091
2459
|
inputMode: "text",
|
|
2092
2460
|
ariaLabel: `Edit hex color, ${hex}`,
|
|
@@ -2258,9 +2626,6 @@ function generateAestheticGradient(stopCount) {
|
|
|
2258
2626
|
function hexDigits(hex) {
|
|
2259
2627
|
return normalizeHex(hex).slice(1);
|
|
2260
2628
|
}
|
|
2261
|
-
function isValidSixHex(d2) {
|
|
2262
|
-
return /^[0-9a-fA-F]{6}$/.test(d2);
|
|
2263
|
-
}
|
|
2264
2629
|
function stopsToGradient(stops, angle = 90) {
|
|
2265
2630
|
if (stops.length === 0) return "linear-gradient(90deg, #808080, #808080)";
|
|
2266
2631
|
if (stops.length === 1) {
|
|
@@ -2295,19 +2660,16 @@ const DEFAULT_GRADIENT_STOPS = [
|
|
|
2295
2660
|
{ color: "#42C0B0" },
|
|
2296
2661
|
{ color: "#BAC9C7" }
|
|
2297
2662
|
];
|
|
2298
|
-
function sanitizeHex(input) {
|
|
2299
|
-
return input.replace(/[^0-9a-fA-F]/g, "").slice(0, 6);
|
|
2300
|
-
}
|
|
2301
2663
|
function StopRow({ index, stop, canDelete, onColorChange, onDelete }) {
|
|
2302
2664
|
const hex = normalizeHex(stop.color);
|
|
2303
2665
|
const handleCommit = (val) => {
|
|
2304
|
-
const
|
|
2305
|
-
if (
|
|
2306
|
-
onColorChange(`#${
|
|
2666
|
+
const expanded = expandHexDigitsToSix(val);
|
|
2667
|
+
if (expanded) {
|
|
2668
|
+
onColorChange(`#${expanded}`);
|
|
2307
2669
|
}
|
|
2308
2670
|
};
|
|
2309
2671
|
const handlePaste = (pasted) => {
|
|
2310
|
-
return
|
|
2672
|
+
return sanitizeHexDigits(pasted);
|
|
2311
2673
|
};
|
|
2312
2674
|
return /* @__PURE__ */ jsxs("div", { className: "gradientPickerStop", children: [
|
|
2313
2675
|
/* @__PURE__ */ jsxs("div", { className: "gradientPickerStopColorHex", children: [
|
|
@@ -2338,8 +2700,8 @@ function StopRow({ index, stop, canDelete, onColorChange, onDelete }) {
|
|
|
2338
2700
|
value: hexDigits(hex),
|
|
2339
2701
|
prefix: "#",
|
|
2340
2702
|
onCommit: handleCommit,
|
|
2341
|
-
sanitize:
|
|
2342
|
-
validate:
|
|
2703
|
+
sanitize: sanitizeHexDigits,
|
|
2704
|
+
validate: (v2) => sanitizeHexDigits(v2).length > 0,
|
|
2343
2705
|
maxLength: 6,
|
|
2344
2706
|
inputMode: "text",
|
|
2345
2707
|
ariaLabel: "Hex color without hash",
|