krts.ui 0.0.0-stage → 0.1.0
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 +151 -2
- package/dist/index.cjs +2334 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +715 -0
- package/dist/index.d.ts +715 -0
- package/dist/index.js +2293 -0
- package/dist/index.js.map +1 -0
- package/dist/krts.css +2 -0
- package/package.json +82 -4
- package/src/styles/build.css +4 -0
- package/src/styles/tailwind.css +3 -0
- package/src/styles/theme.css +152 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,2293 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
// src/lib/cn.ts
|
|
4
|
+
import { clsx } from "clsx";
|
|
5
|
+
import { composeRenderProps } from "react-aria-components";
|
|
6
|
+
import { twMerge } from "tailwind-merge";
|
|
7
|
+
function cn(...inputs) {
|
|
8
|
+
return twMerge(clsx(inputs));
|
|
9
|
+
}
|
|
10
|
+
function composeTw(className, tw) {
|
|
11
|
+
return composeRenderProps(
|
|
12
|
+
className,
|
|
13
|
+
(cls, state) => cn(typeof tw === "function" ? tw(state) : tw, cls)
|
|
14
|
+
);
|
|
15
|
+
}
|
|
16
|
+
var focusRing = "outline-none focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-focus";
|
|
17
|
+
|
|
18
|
+
// src/lib/dom-events.ts
|
|
19
|
+
var KRTS_EVENTS = {
|
|
20
|
+
singleTap: "krts:singletap",
|
|
21
|
+
doubleTap: "krts:doubletap",
|
|
22
|
+
multiTap: "krts:multitap",
|
|
23
|
+
holdStart: "krts:holdstart",
|
|
24
|
+
hold: "krts:hold",
|
|
25
|
+
holdTick: "krts:holdtick",
|
|
26
|
+
holdEnd: "krts:holdend",
|
|
27
|
+
holdCancel: "krts:holdcancel",
|
|
28
|
+
swipe: "krts:swipe",
|
|
29
|
+
confirm: "krts:confirm"
|
|
30
|
+
};
|
|
31
|
+
function dispatchKrtsEvent(target, name, detail) {
|
|
32
|
+
if (!target || typeof CustomEvent === "undefined") return;
|
|
33
|
+
target.dispatchEvent(new CustomEvent(name, { detail, bubbles: true, composed: true }));
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// src/lib/config.tsx
|
|
37
|
+
import { createContext, useContext } from "react";
|
|
38
|
+
import { jsx } from "react/jsx-runtime";
|
|
39
|
+
var defaults = { menuAnimation: "fade" };
|
|
40
|
+
var KrtsContext = createContext(defaults);
|
|
41
|
+
function KrtsProvider({ children, ...config }) {
|
|
42
|
+
const parent = useContext(KrtsContext);
|
|
43
|
+
return /* @__PURE__ */ jsx(KrtsContext.Provider, { value: { ...parent, ...config }, children });
|
|
44
|
+
}
|
|
45
|
+
function useKrtsConfig() {
|
|
46
|
+
return useContext(KrtsContext);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// src/hooks/useGesture.ts
|
|
50
|
+
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
51
|
+
import { mergeProps, usePress } from "react-aria";
|
|
52
|
+
var now = () => typeof performance !== "undefined" ? performance.now() : Date.now();
|
|
53
|
+
function useGestureHandlers(options) {
|
|
54
|
+
const opts = useRef(options);
|
|
55
|
+
opts.current = options;
|
|
56
|
+
const s = useRef({
|
|
57
|
+
taps: 0,
|
|
58
|
+
tapTimer: void 0,
|
|
59
|
+
lastTarget: null,
|
|
60
|
+
lastPointer: "mouse",
|
|
61
|
+
pressStart: 0,
|
|
62
|
+
holdTimer: void 0,
|
|
63
|
+
tickTimer: void 0,
|
|
64
|
+
raf: void 0,
|
|
65
|
+
holdFired: false,
|
|
66
|
+
holdSettled: false,
|
|
67
|
+
pressActive: false,
|
|
68
|
+
moved: false,
|
|
69
|
+
pointerId: null,
|
|
70
|
+
origin: null
|
|
71
|
+
}).current;
|
|
72
|
+
const [state, setState] = useState({ isPressed: false, isHolding: false, didHold: false });
|
|
73
|
+
const holdEvent = useCallback(
|
|
74
|
+
(type) => {
|
|
75
|
+
const duration = s.pressStart ? now() - s.pressStart : 0;
|
|
76
|
+
const holdDuration = opts.current.holdDuration ?? 500;
|
|
77
|
+
return {
|
|
78
|
+
type,
|
|
79
|
+
duration,
|
|
80
|
+
seconds: Math.floor(duration / 1e3),
|
|
81
|
+
progress: Math.min(1, duration / holdDuration),
|
|
82
|
+
pointerType: s.lastPointer,
|
|
83
|
+
target: s.lastTarget
|
|
84
|
+
};
|
|
85
|
+
},
|
|
86
|
+
[s]
|
|
87
|
+
);
|
|
88
|
+
const emit2 = useCallback(
|
|
89
|
+
(name, detail) => {
|
|
90
|
+
if (opts.current.dispatchDomEvents === false) return;
|
|
91
|
+
dispatchKrtsEvent(s.lastTarget, name, detail);
|
|
92
|
+
},
|
|
93
|
+
[s]
|
|
94
|
+
);
|
|
95
|
+
const stopHoldTimers = useCallback(() => {
|
|
96
|
+
clearTimeout(s.holdTimer);
|
|
97
|
+
clearInterval(s.tickTimer);
|
|
98
|
+
if (s.raf !== void 0) cancelAnimationFrame(s.raf);
|
|
99
|
+
s.holdTimer = void 0;
|
|
100
|
+
s.tickTimer = void 0;
|
|
101
|
+
s.raf = void 0;
|
|
102
|
+
}, [s]);
|
|
103
|
+
const resolveTaps = useCallback(() => {
|
|
104
|
+
clearTimeout(s.tapTimer);
|
|
105
|
+
s.tapTimer = void 0;
|
|
106
|
+
const count = s.taps;
|
|
107
|
+
s.taps = 0;
|
|
108
|
+
if (count === 0 || !s.lastTarget) return;
|
|
109
|
+
const o = opts.current;
|
|
110
|
+
const base = { count, pointerType: s.lastPointer, target: s.lastTarget };
|
|
111
|
+
if (count === 1 && o.onSingleTap) {
|
|
112
|
+
const e = { ...base, type: "singletap" };
|
|
113
|
+
o.onSingleTap(e);
|
|
114
|
+
emit2(KRTS_EVENTS.singleTap, e);
|
|
115
|
+
} else if (count === 1) {
|
|
116
|
+
emit2(KRTS_EVENTS.singleTap, { ...base, type: "singletap" });
|
|
117
|
+
}
|
|
118
|
+
if (count === 2) {
|
|
119
|
+
const e = { ...base, type: "doubletap" };
|
|
120
|
+
o.onDoubleTap?.(e);
|
|
121
|
+
emit2(KRTS_EVENTS.doubleTap, e);
|
|
122
|
+
}
|
|
123
|
+
const multi = { ...base, type: "multitap" };
|
|
124
|
+
o.onMultiTap?.(multi);
|
|
125
|
+
emit2(KRTS_EVENTS.multiTap, multi);
|
|
126
|
+
}, [s, emit2]);
|
|
127
|
+
const maxTaps = () => {
|
|
128
|
+
const o = opts.current;
|
|
129
|
+
if (o.onMultiTap) return Math.max(2, o.maxTaps ?? 3);
|
|
130
|
+
if (o.onDoubleTap) return 2;
|
|
131
|
+
return 1;
|
|
132
|
+
};
|
|
133
|
+
const cancelHold = useCallback(
|
|
134
|
+
(fireCancel) => {
|
|
135
|
+
const wasActive = s.pressActive;
|
|
136
|
+
stopHoldTimers();
|
|
137
|
+
if (wasActive && fireCancel && !s.holdFired && !s.holdSettled) {
|
|
138
|
+
s.holdSettled = true;
|
|
139
|
+
const e = holdEvent("holdcancel");
|
|
140
|
+
opts.current.onHoldCancel?.(e);
|
|
141
|
+
emit2(KRTS_EVENTS.holdCancel, e);
|
|
142
|
+
}
|
|
143
|
+
opts.current.onHoldProgress?.(0);
|
|
144
|
+
setState((p) => p.isHolding ? { ...p, isHolding: false } : p);
|
|
145
|
+
},
|
|
146
|
+
[s, stopHoldTimers, holdEvent, emit2]
|
|
147
|
+
);
|
|
148
|
+
const onPressStart = useCallback(
|
|
149
|
+
(e) => {
|
|
150
|
+
if (opts.current.isDisabled) return;
|
|
151
|
+
const o = opts.current;
|
|
152
|
+
clearTimeout(s.tapTimer);
|
|
153
|
+
s.tapTimer = void 0;
|
|
154
|
+
s.lastTarget = e.target;
|
|
155
|
+
s.lastPointer = e.pointerType;
|
|
156
|
+
s.pressStart = now();
|
|
157
|
+
s.holdFired = false;
|
|
158
|
+
s.holdSettled = false;
|
|
159
|
+
s.pressActive = true;
|
|
160
|
+
s.moved = false;
|
|
161
|
+
setState({ isPressed: true, isHolding: true, didHold: false });
|
|
162
|
+
const startEvt = holdEvent("holdstart");
|
|
163
|
+
o.onHoldStart?.(startEvt);
|
|
164
|
+
emit2(KRTS_EVENTS.holdStart, startEvt);
|
|
165
|
+
const holdDuration = o.holdDuration ?? 500;
|
|
166
|
+
s.holdTimer = setTimeout(() => {
|
|
167
|
+
s.holdFired = true;
|
|
168
|
+
s.taps = 0;
|
|
169
|
+
const he = holdEvent("hold");
|
|
170
|
+
opts.current.onHold?.(he);
|
|
171
|
+
emit2(KRTS_EVENTS.hold, he);
|
|
172
|
+
setState((p) => ({ ...p, didHold: true }));
|
|
173
|
+
}, holdDuration);
|
|
174
|
+
const tickInterval = o.holdTickInterval ?? 1e3;
|
|
175
|
+
let ticks = 0;
|
|
176
|
+
s.tickTimer = setInterval(() => {
|
|
177
|
+
ticks += 1;
|
|
178
|
+
const te = { ...holdEvent("holdtick"), seconds: Math.floor(ticks * tickInterval / 1e3) };
|
|
179
|
+
opts.current.onHoldTick?.(te);
|
|
180
|
+
emit2(KRTS_EVENTS.holdTick, te);
|
|
181
|
+
}, tickInterval);
|
|
182
|
+
if (o.onHoldProgress && typeof requestAnimationFrame !== "undefined") {
|
|
183
|
+
const loop = () => {
|
|
184
|
+
const p = Math.min(1, (now() - s.pressStart) / (opts.current.holdDuration ?? 500));
|
|
185
|
+
opts.current.onHoldProgress?.(p);
|
|
186
|
+
s.raf = p < 1 ? requestAnimationFrame(loop) : void 0;
|
|
187
|
+
};
|
|
188
|
+
s.raf = requestAnimationFrame(loop);
|
|
189
|
+
}
|
|
190
|
+
},
|
|
191
|
+
[s, holdEvent, emit2]
|
|
192
|
+
);
|
|
193
|
+
const onPressEnd = useCallback(
|
|
194
|
+
(_e) => {
|
|
195
|
+
if (!s.pressActive) return;
|
|
196
|
+
if (s.holdFired) {
|
|
197
|
+
const e = holdEvent("holdend");
|
|
198
|
+
opts.current.onHoldEnd?.(e);
|
|
199
|
+
emit2(KRTS_EVENTS.holdEnd, e);
|
|
200
|
+
}
|
|
201
|
+
cancelHold(true);
|
|
202
|
+
s.pressActive = false;
|
|
203
|
+
setState((p) => ({ ...p, isPressed: false, isHolding: false }));
|
|
204
|
+
if (s.taps > 0 && !s.tapTimer) {
|
|
205
|
+
s.tapTimer = setTimeout(resolveTaps, opts.current.multiTapDelay ?? 250);
|
|
206
|
+
}
|
|
207
|
+
},
|
|
208
|
+
[s, holdEvent, emit2, cancelHold, resolveTaps]
|
|
209
|
+
);
|
|
210
|
+
const onPress = useCallback(
|
|
211
|
+
(e) => {
|
|
212
|
+
if (opts.current.isDisabled || s.holdFired || s.moved) return;
|
|
213
|
+
s.lastTarget = e.target;
|
|
214
|
+
s.lastPointer = e.pointerType;
|
|
215
|
+
s.taps += 1;
|
|
216
|
+
clearTimeout(s.tapTimer);
|
|
217
|
+
if (s.taps >= maxTaps()) resolveTaps();
|
|
218
|
+
else s.tapTimer = setTimeout(resolveTaps, opts.current.multiTapDelay ?? 250);
|
|
219
|
+
},
|
|
220
|
+
[s, resolveTaps]
|
|
221
|
+
);
|
|
222
|
+
const onPointerDown = useCallback(
|
|
223
|
+
(e) => {
|
|
224
|
+
if (opts.current.isDisabled || !e.isPrimary) return;
|
|
225
|
+
if (s.pointerId !== null) return;
|
|
226
|
+
s.pointerId = e.pointerId;
|
|
227
|
+
s.origin = { x: e.clientX, y: e.clientY, t: now() };
|
|
228
|
+
},
|
|
229
|
+
[s]
|
|
230
|
+
);
|
|
231
|
+
const onPointerMove = useCallback(
|
|
232
|
+
(e) => {
|
|
233
|
+
if (e.pointerId !== s.pointerId || !s.origin) return;
|
|
234
|
+
const dx = e.clientX - s.origin.x;
|
|
235
|
+
const dy = e.clientY - s.origin.y;
|
|
236
|
+
if (!s.moved && Math.hypot(dx, dy) > (opts.current.moveTolerance ?? 10)) {
|
|
237
|
+
s.moved = true;
|
|
238
|
+
if (!s.holdFired) cancelHold(true);
|
|
239
|
+
}
|
|
240
|
+
},
|
|
241
|
+
[s, cancelHold]
|
|
242
|
+
);
|
|
243
|
+
const endPointer = useCallback(
|
|
244
|
+
(e, cancelled) => {
|
|
245
|
+
if (e.pointerId !== s.pointerId || !s.origin) return;
|
|
246
|
+
const origin = s.origin;
|
|
247
|
+
s.pointerId = null;
|
|
248
|
+
s.origin = null;
|
|
249
|
+
const o = opts.current;
|
|
250
|
+
if (cancelled || !o.onSwipe) return;
|
|
251
|
+
const dx = e.clientX - origin.x;
|
|
252
|
+
const dy = e.clientY - origin.y;
|
|
253
|
+
const horizontal = Math.abs(dx) >= Math.abs(dy);
|
|
254
|
+
const distance = Math.abs(horizontal ? dx : dy);
|
|
255
|
+
const elapsed = Math.max(1, now() - origin.t);
|
|
256
|
+
const velocity = distance / elapsed;
|
|
257
|
+
if (distance >= (o.swipeThreshold ?? 40) || distance > 10 && velocity > (o.swipeVelocity ?? 0.11)) {
|
|
258
|
+
const direction = horizontal ? dx > 0 ? "right" : "left" : dy > 0 ? "down" : "up";
|
|
259
|
+
const se = { type: "swipe", direction, distance, velocity, target: e.currentTarget };
|
|
260
|
+
o.onSwipe(se);
|
|
261
|
+
if (o.dispatchDomEvents !== false) dispatchKrtsEvent(e.currentTarget, KRTS_EVENTS.swipe, se);
|
|
262
|
+
}
|
|
263
|
+
},
|
|
264
|
+
[s]
|
|
265
|
+
);
|
|
266
|
+
const pointerProps = useMemo(
|
|
267
|
+
() => ({
|
|
268
|
+
onPointerDown,
|
|
269
|
+
onPointerMove,
|
|
270
|
+
onPointerUp: (e) => endPointer(e, false),
|
|
271
|
+
onPointerCancel: (e) => endPointer(e, true),
|
|
272
|
+
onContextMenu: (e) => {
|
|
273
|
+
const o = opts.current;
|
|
274
|
+
if ((o.onHold || o.onHoldTick) && s.lastPointer === "touch") e.preventDefault();
|
|
275
|
+
},
|
|
276
|
+
"data-krts-gesture": ""
|
|
277
|
+
}),
|
|
278
|
+
[onPointerDown, onPointerMove, endPointer, s]
|
|
279
|
+
);
|
|
280
|
+
const pressHandlers = useMemo(
|
|
281
|
+
() => ({ onPressStart, onPressEnd, onPress }),
|
|
282
|
+
[onPressStart, onPressEnd, onPress]
|
|
283
|
+
);
|
|
284
|
+
useEffect(
|
|
285
|
+
() => () => {
|
|
286
|
+
clearTimeout(s.tapTimer);
|
|
287
|
+
stopHoldTimers();
|
|
288
|
+
},
|
|
289
|
+
[s, stopHoldTimers]
|
|
290
|
+
);
|
|
291
|
+
return { pressHandlers, pointerProps, state };
|
|
292
|
+
}
|
|
293
|
+
function useGesture(options) {
|
|
294
|
+
const { pressHandlers, pointerProps, state } = useGestureHandlers(options);
|
|
295
|
+
const { pressProps } = usePress({ ...pressHandlers, isDisabled: options.isDisabled });
|
|
296
|
+
return {
|
|
297
|
+
gestureProps: mergeProps(pressProps, pointerProps),
|
|
298
|
+
isPressed: state.isPressed,
|
|
299
|
+
isHolding: state.isHolding,
|
|
300
|
+
didHold: state.didHold
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// src/hooks/usePerceived.ts
|
|
305
|
+
import { useEffect as useEffect2, useRef as useRef2, useState as useState2 } from "react";
|
|
306
|
+
function useSmoothPending(isPending, { delay = 150, minDuration = 500 } = {}) {
|
|
307
|
+
const [shown, setShown] = useState2(false);
|
|
308
|
+
const shownAt = useRef2(0);
|
|
309
|
+
useEffect2(() => {
|
|
310
|
+
if (isPending && !shown) {
|
|
311
|
+
const t = setTimeout(() => {
|
|
312
|
+
shownAt.current = Date.now();
|
|
313
|
+
setShown(true);
|
|
314
|
+
}, delay);
|
|
315
|
+
return () => clearTimeout(t);
|
|
316
|
+
}
|
|
317
|
+
if (!isPending && shown) {
|
|
318
|
+
const remaining = Math.max(0, minDuration - (Date.now() - shownAt.current));
|
|
319
|
+
const t = setTimeout(() => setShown(false), remaining);
|
|
320
|
+
return () => clearTimeout(t);
|
|
321
|
+
}
|
|
322
|
+
return void 0;
|
|
323
|
+
}, [isPending, shown, delay, minDuration]);
|
|
324
|
+
return shown;
|
|
325
|
+
}
|
|
326
|
+
function usePerceivedProgress(isRunning, isDone, { expectedDuration = 3e3, ceiling = 92 } = {}) {
|
|
327
|
+
const [value, setValue] = useState2(0);
|
|
328
|
+
const start = useRef2(null);
|
|
329
|
+
useEffect2(() => {
|
|
330
|
+
if (isDone) {
|
|
331
|
+
setValue(100);
|
|
332
|
+
start.current = null;
|
|
333
|
+
return void 0;
|
|
334
|
+
}
|
|
335
|
+
if (!isRunning) {
|
|
336
|
+
setValue(0);
|
|
337
|
+
start.current = null;
|
|
338
|
+
return void 0;
|
|
339
|
+
}
|
|
340
|
+
start.current = typeof performance !== "undefined" ? performance.now() : Date.now();
|
|
341
|
+
let raf = 0;
|
|
342
|
+
const tick = () => {
|
|
343
|
+
const t = (performance.now() - (start.current ?? 0)) / expectedDuration;
|
|
344
|
+
setValue(ceiling * (1 - Math.exp(-2.2 * t)));
|
|
345
|
+
raf = requestAnimationFrame(tick);
|
|
346
|
+
};
|
|
347
|
+
raf = requestAnimationFrame(tick);
|
|
348
|
+
return () => cancelAnimationFrame(raf);
|
|
349
|
+
}, [isRunning, isDone, expectedDuration, ceiling]);
|
|
350
|
+
return value;
|
|
351
|
+
}
|
|
352
|
+
function usePrefersReducedMotion() {
|
|
353
|
+
const [reduced, setReduced] = useState2(false);
|
|
354
|
+
useEffect2(() => {
|
|
355
|
+
if (typeof matchMedia === "undefined") return void 0;
|
|
356
|
+
const mq = matchMedia("(prefers-reduced-motion: reduce)");
|
|
357
|
+
setReduced(mq.matches);
|
|
358
|
+
const on = () => setReduced(mq.matches);
|
|
359
|
+
mq.addEventListener("change", on);
|
|
360
|
+
return () => mq.removeEventListener("change", on);
|
|
361
|
+
}, []);
|
|
362
|
+
return reduced;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
// src/hooks/useAnimatedWidth.ts
|
|
366
|
+
import { useCallback as useCallback2, useEffect as useEffect3, useLayoutEffect, useRef as useRef3, useState as useState3 } from "react";
|
|
367
|
+
var useIsoLayoutEffect = typeof window !== "undefined" ? useLayoutEffect : useEffect3;
|
|
368
|
+
function useAnimatedWidth({ enabled = true, duration = 250 } = {}) {
|
|
369
|
+
const contentRef = useRef3(null);
|
|
370
|
+
const [width, setWidth] = useState3(void 0);
|
|
371
|
+
const [ready, setReady] = useState3(false);
|
|
372
|
+
const reduced = usePrefersReducedMotion();
|
|
373
|
+
const measure = useCallback2(() => {
|
|
374
|
+
const content = contentRef.current;
|
|
375
|
+
const host = content?.parentElement;
|
|
376
|
+
if (!content || !host) return;
|
|
377
|
+
const cs = getComputedStyle(host);
|
|
378
|
+
const chrome = parseFloat(cs.paddingLeft) + parseFloat(cs.paddingRight) + parseFloat(cs.borderLeftWidth) + parseFloat(cs.borderRightWidth);
|
|
379
|
+
const next = Math.ceil(content.getBoundingClientRect().width + chrome);
|
|
380
|
+
setWidth((prev) => prev === next ? prev : next);
|
|
381
|
+
}, []);
|
|
382
|
+
useIsoLayoutEffect(() => {
|
|
383
|
+
if (!enabled) {
|
|
384
|
+
setWidth(void 0);
|
|
385
|
+
return void 0;
|
|
386
|
+
}
|
|
387
|
+
const content = contentRef.current;
|
|
388
|
+
if (!content || typeof ResizeObserver === "undefined") return void 0;
|
|
389
|
+
measure();
|
|
390
|
+
const ro = new ResizeObserver(() => measure());
|
|
391
|
+
ro.observe(content);
|
|
392
|
+
return () => ro.disconnect();
|
|
393
|
+
}, [enabled, measure]);
|
|
394
|
+
useEffect3(() => {
|
|
395
|
+
if (!enabled || width === void 0 || ready) return void 0;
|
|
396
|
+
let cancelled = false;
|
|
397
|
+
const fonts = typeof document !== "undefined" && document.fonts ? document.fonts.ready : Promise.resolve();
|
|
398
|
+
fonts.then(() => {
|
|
399
|
+
requestAnimationFrame(() => {
|
|
400
|
+
if (!cancelled) setReady(true);
|
|
401
|
+
});
|
|
402
|
+
});
|
|
403
|
+
return () => {
|
|
404
|
+
cancelled = true;
|
|
405
|
+
};
|
|
406
|
+
}, [enabled, width, ready]);
|
|
407
|
+
const style = enabled && width !== void 0 ? { width } : void 0;
|
|
408
|
+
return {
|
|
409
|
+
contentRef,
|
|
410
|
+
/** Spread into the host's `style`. */
|
|
411
|
+
widthStyle: style,
|
|
412
|
+
/** Append to the host's `transition` list. */
|
|
413
|
+
widthTransition: enabled && ready && !reduced ? `width ${duration}ms var(--ease-out)` : ""
|
|
414
|
+
};
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
// src/components/Button.tsx
|
|
418
|
+
import { forwardRef } from "react";
|
|
419
|
+
import { Button as RACButton, composeRenderProps as composeRenderProps2 } from "react-aria-components";
|
|
420
|
+
import { mergeProps as mergeProps2 } from "react-aria";
|
|
421
|
+
|
|
422
|
+
// src/components/Spinner.tsx
|
|
423
|
+
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
424
|
+
function Spinner({ size = 16, className, label }) {
|
|
425
|
+
return /* @__PURE__ */ jsx2(
|
|
426
|
+
"span",
|
|
427
|
+
{
|
|
428
|
+
role: label ? "progressbar" : void 0,
|
|
429
|
+
"aria-label": label,
|
|
430
|
+
"aria-hidden": label ? void 0 : true,
|
|
431
|
+
className: cn(
|
|
432
|
+
"inline-block shrink-0 animate-krts-spin rounded-full border-2 border-current border-r-transparent opacity-80",
|
|
433
|
+
className
|
|
434
|
+
),
|
|
435
|
+
style: { width: size, height: size }
|
|
436
|
+
}
|
|
437
|
+
);
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
// src/components/Button.tsx
|
|
441
|
+
import { Fragment, jsx as jsx3, jsxs } from "react/jsx-runtime";
|
|
442
|
+
var variants = {
|
|
443
|
+
primary: "bg-primary border-primary-line text-primary-fg hover:bg-primary-hover pressed:bg-primary-pressed shadow-control",
|
|
444
|
+
secondary: "bg-surface border-line text-fg hover:bg-surface-hover pressed:bg-surface-pressed pressed:border-line-strong shadow-control",
|
|
445
|
+
danger: "bg-danger border-danger-line text-danger-fg hover:bg-danger-hover pressed:bg-danger-pressed shadow-control",
|
|
446
|
+
ghost: "bg-transparent border-transparent text-fg/80 hover:bg-surface/70 hover:text-fg pressed:bg-surface-pressed/60 pressed:text-fg"
|
|
447
|
+
};
|
|
448
|
+
var sizes = {
|
|
449
|
+
xs: "h-7 px-2.5 text-[13px] gap-1.5 rounded-[6px]",
|
|
450
|
+
sm: "h-9 px-3.5 text-sm gap-2",
|
|
451
|
+
md: "h-12 px-[18px] text-base gap-2.5"
|
|
452
|
+
};
|
|
453
|
+
var iconSizes = { xs: "size-7 px-0", sm: "size-9 px-0", md: "size-12 px-0" };
|
|
454
|
+
function buttonStyles({
|
|
455
|
+
variant = "secondary",
|
|
456
|
+
size = "md",
|
|
457
|
+
isIconOnly = false,
|
|
458
|
+
className
|
|
459
|
+
} = {}) {
|
|
460
|
+
return cn(
|
|
461
|
+
"group/button relative isolate inline-flex select-none items-center justify-center whitespace-nowrap rounded-control border font-sans font-normal leading-none",
|
|
462
|
+
"cursor-default [-webkit-tap-highlight-color:transparent] touch-manipulation",
|
|
463
|
+
// Press feedback comes from the pressed colours (Figma "Pressed" row) — no scaling.
|
|
464
|
+
"transition-[background-color,border-color,color,box-shadow,opacity] duration-150 ease-out",
|
|
465
|
+
"disabled:cursor-not-allowed disabled:opacity-45 disabled:shadow-none",
|
|
466
|
+
focusRing,
|
|
467
|
+
variants[variant],
|
|
468
|
+
sizes[size],
|
|
469
|
+
isIconOnly && iconSizes[size],
|
|
470
|
+
className
|
|
471
|
+
);
|
|
472
|
+
}
|
|
473
|
+
var gestureKeys = [
|
|
474
|
+
"onSingleTap",
|
|
475
|
+
"onDoubleTap",
|
|
476
|
+
"onMultiTap",
|
|
477
|
+
"onHoldStart",
|
|
478
|
+
"onHold",
|
|
479
|
+
"onHoldTick",
|
|
480
|
+
"onHoldProgress",
|
|
481
|
+
"onHoldEnd",
|
|
482
|
+
"onHoldCancel",
|
|
483
|
+
"onSwipe"
|
|
484
|
+
];
|
|
485
|
+
var BASE_TRANSITION = "background-color 150ms var(--ease-out), border-color 150ms var(--ease-out), color 150ms var(--ease-out), box-shadow 150ms var(--ease-out), opacity 150ms var(--ease-out)";
|
|
486
|
+
var Button = forwardRef(function Button2(props, ref) {
|
|
487
|
+
const {
|
|
488
|
+
variant = "secondary",
|
|
489
|
+
size = "md",
|
|
490
|
+
isIconOnly,
|
|
491
|
+
className,
|
|
492
|
+
children,
|
|
493
|
+
startContent,
|
|
494
|
+
endContent,
|
|
495
|
+
isPending,
|
|
496
|
+
maxTaps,
|
|
497
|
+
multiTapDelay,
|
|
498
|
+
holdDuration,
|
|
499
|
+
holdTickInterval,
|
|
500
|
+
animateWidth = !isIconOnly,
|
|
501
|
+
style,
|
|
502
|
+
...rest
|
|
503
|
+
} = props;
|
|
504
|
+
const gestureOpts = { maxTaps, multiTapDelay, holdDuration, holdTickInterval };
|
|
505
|
+
const racProps = {};
|
|
506
|
+
let hasGestures = false;
|
|
507
|
+
for (const [k, v] of Object.entries(rest)) {
|
|
508
|
+
if (gestureKeys.includes(k)) {
|
|
509
|
+
gestureOpts[k] = v;
|
|
510
|
+
if (v) hasGestures = true;
|
|
511
|
+
} else racProps[k] = v;
|
|
512
|
+
}
|
|
513
|
+
gestureOpts.isDisabled = !!props.isDisabled || !!isPending;
|
|
514
|
+
const { pressHandlers, pointerProps } = useGestureHandlers(gestureOpts);
|
|
515
|
+
const showSpinner = useSmoothPending(!!isPending);
|
|
516
|
+
const { contentRef, widthStyle, widthTransition } = useAnimatedWidth({ enabled: animateWidth });
|
|
517
|
+
const merged = hasGestures ? mergeProps2(racProps, pressHandlers, pointerProps) : racProps;
|
|
518
|
+
return /* @__PURE__ */ jsx3(
|
|
519
|
+
RACButton,
|
|
520
|
+
{
|
|
521
|
+
...merged,
|
|
522
|
+
ref,
|
|
523
|
+
isPending,
|
|
524
|
+
"data-spinner": showSpinner || void 0,
|
|
525
|
+
className: composeRenderProps2(
|
|
526
|
+
className,
|
|
527
|
+
(cls) => buttonStyles({ variant, size, isIconOnly, className: cn(animateWidth && "shrink-0 overflow-hidden", cls) })
|
|
528
|
+
),
|
|
529
|
+
style: composeRenderProps2(style, (s) => ({
|
|
530
|
+
...widthStyle,
|
|
531
|
+
...widthTransition ? { transition: `${BASE_TRANSITION}, ${widthTransition}` } : null,
|
|
532
|
+
...s
|
|
533
|
+
})),
|
|
534
|
+
children: composeRenderProps2(children, (child) => /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
535
|
+
/* @__PURE__ */ jsxs(
|
|
536
|
+
"span",
|
|
537
|
+
{
|
|
538
|
+
ref: contentRef,
|
|
539
|
+
className: cn(
|
|
540
|
+
"inline-flex shrink-0 items-center justify-center gap-[inherit] self-stretch transition-[opacity,filter] duration-200 ease-out",
|
|
541
|
+
showSpinner && "opacity-0 blur-[2px]"
|
|
542
|
+
),
|
|
543
|
+
children: [
|
|
544
|
+
startContent,
|
|
545
|
+
child,
|
|
546
|
+
endContent
|
|
547
|
+
]
|
|
548
|
+
}
|
|
549
|
+
),
|
|
550
|
+
/* @__PURE__ */ jsx3(
|
|
551
|
+
"span",
|
|
552
|
+
{
|
|
553
|
+
"aria-hidden": true,
|
|
554
|
+
className: cn(
|
|
555
|
+
"pointer-events-none absolute inset-0 flex items-center justify-center transition-[opacity,filter] duration-200 ease-out",
|
|
556
|
+
showSpinner ? "opacity-100 blur-0" : "opacity-0 blur-[2px]"
|
|
557
|
+
),
|
|
558
|
+
children: showSpinner && /* @__PURE__ */ jsx3(Spinner, { size: size === "md" ? 18 : 14 })
|
|
559
|
+
}
|
|
560
|
+
)
|
|
561
|
+
] }))
|
|
562
|
+
}
|
|
563
|
+
);
|
|
564
|
+
});
|
|
565
|
+
|
|
566
|
+
// src/components/HoldButton.tsx
|
|
567
|
+
import { forwardRef as forwardRef2, useEffect as useEffect4, useId, useRef as useRef4, useState as useState4 } from "react";
|
|
568
|
+
import { Button as RACButton2 } from "react-aria-components";
|
|
569
|
+
import { mergeProps as mergeProps3 } from "react-aria";
|
|
570
|
+
|
|
571
|
+
// src/components/icons.tsx
|
|
572
|
+
import { jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
573
|
+
function Svg({ size = 16, children, ...props }) {
|
|
574
|
+
return /* @__PURE__ */ jsx4(
|
|
575
|
+
"svg",
|
|
576
|
+
{
|
|
577
|
+
width: size,
|
|
578
|
+
height: size,
|
|
579
|
+
viewBox: "0 0 24 24",
|
|
580
|
+
fill: "none",
|
|
581
|
+
stroke: "currentColor",
|
|
582
|
+
strokeWidth: 2,
|
|
583
|
+
strokeLinecap: "round",
|
|
584
|
+
strokeLinejoin: "round",
|
|
585
|
+
"aria-hidden": "true",
|
|
586
|
+
focusable: "false",
|
|
587
|
+
...props,
|
|
588
|
+
children
|
|
589
|
+
}
|
|
590
|
+
);
|
|
591
|
+
}
|
|
592
|
+
var ChevronDownIcon = (p) => /* @__PURE__ */ jsx4(Svg, { ...p, children: /* @__PURE__ */ jsx4("path", { d: "m6 9 6 6 6-6" }) });
|
|
593
|
+
var ChevronRightIcon = (p) => /* @__PURE__ */ jsx4(Svg, { ...p, children: /* @__PURE__ */ jsx4("path", { d: "m9 6 6 6-6 6" }) });
|
|
594
|
+
var CheckIcon = (p) => /* @__PURE__ */ jsx4(Svg, { strokeWidth: 2.5, ...p, children: /* @__PURE__ */ jsx4("path", { d: "M20 6 9 17l-5-5" }) });
|
|
595
|
+
var MinusIcon = (p) => /* @__PURE__ */ jsx4(Svg, { strokeWidth: 2.5, ...p, children: /* @__PURE__ */ jsx4("path", { d: "M5 12h14" }) });
|
|
596
|
+
var XIcon = (p) => /* @__PURE__ */ jsx4(Svg, { ...p, children: /* @__PURE__ */ jsx4("path", { d: "M18 6 6 18M6 6l12 12" }) });
|
|
597
|
+
var SearchIcon = (p) => /* @__PURE__ */ jsxs2(Svg, { ...p, children: [
|
|
598
|
+
/* @__PURE__ */ jsx4("circle", { cx: "11", cy: "11", r: "7" }),
|
|
599
|
+
/* @__PURE__ */ jsx4("path", { d: "m20 20-3.5-3.5" })
|
|
600
|
+
] });
|
|
601
|
+
var CopyIcon = (p) => /* @__PURE__ */ jsxs2(Svg, { ...p, children: [
|
|
602
|
+
/* @__PURE__ */ jsx4("rect", { x: "9", y: "9", width: "12", height: "12", rx: "2" }),
|
|
603
|
+
/* @__PURE__ */ jsx4("path", { d: "M5 15V5a2 2 0 0 1 2-2h10" })
|
|
604
|
+
] });
|
|
605
|
+
var PlusIcon = (p) => /* @__PURE__ */ jsx4(Svg, { ...p, children: /* @__PURE__ */ jsx4("path", { d: "M12 5v14M5 12h14" }) });
|
|
606
|
+
|
|
607
|
+
// src/components/HoldButton.tsx
|
|
608
|
+
import { Fragment as Fragment2, jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
609
|
+
var fills = {
|
|
610
|
+
danger: "bg-danger text-danger-fg",
|
|
611
|
+
primary: "bg-primary text-primary-fg",
|
|
612
|
+
secondary: "bg-surface-pressed text-fg"
|
|
613
|
+
};
|
|
614
|
+
var bases = {
|
|
615
|
+
danger: "bg-surface border-line text-danger hover:bg-danger-soft",
|
|
616
|
+
primary: "bg-surface border-line text-fg hover:bg-surface-hover",
|
|
617
|
+
secondary: "bg-surface border-line text-fg hover:bg-surface-hover"
|
|
618
|
+
};
|
|
619
|
+
var HoldButton = forwardRef2(function HoldButton2({
|
|
620
|
+
children,
|
|
621
|
+
onConfirm,
|
|
622
|
+
holdDuration = 2e3,
|
|
623
|
+
onHoldProgress,
|
|
624
|
+
onHoldCancel,
|
|
625
|
+
hint = "Hold to confirm",
|
|
626
|
+
confirmedContent,
|
|
627
|
+
resetAfter = 1600,
|
|
628
|
+
variant = "danger",
|
|
629
|
+
size = "md",
|
|
630
|
+
className,
|
|
631
|
+
isDisabled,
|
|
632
|
+
animateWidth = true,
|
|
633
|
+
...rest
|
|
634
|
+
}, ref) {
|
|
635
|
+
const [phase, setPhase] = useState4("idle");
|
|
636
|
+
const timer = useRef4(void 0);
|
|
637
|
+
const hintId = useId();
|
|
638
|
+
useEffect4(() => () => clearTimeout(timer.current), []);
|
|
639
|
+
const settle = (next, ms) => {
|
|
640
|
+
setPhase(next);
|
|
641
|
+
clearTimeout(timer.current);
|
|
642
|
+
timer.current = setTimeout(() => setPhase("idle"), ms);
|
|
643
|
+
};
|
|
644
|
+
const { pressHandlers, pointerProps } = useGestureHandlers({
|
|
645
|
+
holdDuration,
|
|
646
|
+
isDisabled: isDisabled || phase === "confirmed",
|
|
647
|
+
onHoldStart: (e) => {
|
|
648
|
+
if (e.pointerType === "virtual") return;
|
|
649
|
+
clearTimeout(timer.current);
|
|
650
|
+
setPhase("holding");
|
|
651
|
+
},
|
|
652
|
+
onHold: (e) => {
|
|
653
|
+
settle("confirmed", resetAfter);
|
|
654
|
+
dispatchKrtsEvent(e.target, KRTS_EVENTS.confirm, e);
|
|
655
|
+
onConfirm(e);
|
|
656
|
+
},
|
|
657
|
+
onHoldCancel: (e) => {
|
|
658
|
+
onHoldCancel?.(e);
|
|
659
|
+
if (e.duration < 350 && hint) settle("hint", 1400);
|
|
660
|
+
else setPhase("idle");
|
|
661
|
+
},
|
|
662
|
+
onHoldProgress
|
|
663
|
+
});
|
|
664
|
+
const onPress = (e) => {
|
|
665
|
+
if (e.pointerType === "virtual" && phase !== "confirmed") {
|
|
666
|
+
settle("confirmed", resetAfter);
|
|
667
|
+
onConfirm({ type: "virtual" });
|
|
668
|
+
}
|
|
669
|
+
};
|
|
670
|
+
const { contentRef, widthStyle, widthTransition } = useAnimatedWidth({ enabled: animateWidth });
|
|
671
|
+
const holding = phase === "holding";
|
|
672
|
+
const confirmed = phase === "confirmed";
|
|
673
|
+
const label = phase === "hint" ? hint : confirmed && confirmedContent !== null ? confirmedContent ?? /* @__PURE__ */ jsxs3(Fragment2, { children: [
|
|
674
|
+
/* @__PURE__ */ jsx5(CheckIcon, { size: 16 }),
|
|
675
|
+
" Done"
|
|
676
|
+
] }) : children;
|
|
677
|
+
const content = /* @__PURE__ */ jsx5("span", { className: "inline-flex items-center gap-2 transition-[opacity,filter] duration-200 ease-out starting:opacity-0 starting:blur-[2px]", children: label }, phase === "hint" ? "hint" : confirmed ? "done" : "label");
|
|
678
|
+
return /* @__PURE__ */ jsxs3(
|
|
679
|
+
RACButton2,
|
|
680
|
+
{
|
|
681
|
+
...mergeProps3(rest, pressHandlers, pointerProps, { onPress }),
|
|
682
|
+
ref,
|
|
683
|
+
isDisabled,
|
|
684
|
+
"aria-describedby": hintId,
|
|
685
|
+
"data-holding": holding || void 0,
|
|
686
|
+
"data-confirmed": confirmed || void 0,
|
|
687
|
+
className: cn(
|
|
688
|
+
"group/hold relative isolate inline-flex select-none items-center justify-center overflow-hidden whitespace-nowrap rounded-control border font-sans leading-none shadow-control",
|
|
689
|
+
"cursor-default touch-manipulation [-webkit-tap-highlight-color:transparent] [-webkit-touch-callout:none]",
|
|
690
|
+
"transition-[background-color,border-color,color] duration-150 ease-out",
|
|
691
|
+
"disabled:cursor-not-allowed disabled:opacity-45",
|
|
692
|
+
focusRing,
|
|
693
|
+
size === "md" ? "h-12 px-[18px] text-base" : "h-9 px-3.5 text-sm",
|
|
694
|
+
animateWidth && "shrink-0",
|
|
695
|
+
bases[variant],
|
|
696
|
+
className
|
|
697
|
+
),
|
|
698
|
+
style: {
|
|
699
|
+
...widthStyle,
|
|
700
|
+
...widthTransition ? { transition: `background-color 150ms var(--ease-out), border-color 150ms var(--ease-out), color 150ms var(--ease-out), ${widthTransition}` } : null
|
|
701
|
+
},
|
|
702
|
+
children: [
|
|
703
|
+
/* @__PURE__ */ jsx5("span", { ref: contentRef, className: "relative inline-flex shrink-0 items-center gap-2", children: content }),
|
|
704
|
+
/* @__PURE__ */ jsx5(
|
|
705
|
+
"span",
|
|
706
|
+
{
|
|
707
|
+
"aria-hidden": true,
|
|
708
|
+
className: cn(
|
|
709
|
+
"pointer-events-none absolute inset-0 flex items-center justify-center",
|
|
710
|
+
fills[variant],
|
|
711
|
+
// Release: snap back fast. Press: slow, linear, deliberate.
|
|
712
|
+
"[clip-path:inset(0_100%_0_0)] [transition:clip-path_200ms_var(--ease-out)]",
|
|
713
|
+
"group-data-[holding]/hold:[clip-path:inset(0_0_0_0)] group-data-[holding]/hold:[transition:clip-path_var(--hold-ms)_linear]",
|
|
714
|
+
"group-data-[confirmed]/hold:[clip-path:inset(0_0_0_0)]"
|
|
715
|
+
),
|
|
716
|
+
style: { "--hold-ms": `${holdDuration}ms` },
|
|
717
|
+
children: /* @__PURE__ */ jsx5("span", { className: "inline-flex items-center gap-2", children: content })
|
|
718
|
+
}
|
|
719
|
+
),
|
|
720
|
+
/* @__PURE__ */ jsxs3("span", { id: hintId, className: "sr-only", children: [
|
|
721
|
+
"Press and hold for ",
|
|
722
|
+
Math.round(holdDuration / 100) / 10,
|
|
723
|
+
" seconds to confirm."
|
|
724
|
+
] })
|
|
725
|
+
]
|
|
726
|
+
}
|
|
727
|
+
);
|
|
728
|
+
});
|
|
729
|
+
|
|
730
|
+
// src/components/CopyButton.tsx
|
|
731
|
+
import { useEffect as useEffect5, useRef as useRef5, useState as useState5 } from "react";
|
|
732
|
+
import { jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
733
|
+
function CopyButton({ value, label = "Copy", copiedLabel = "Copied", onCopy, isIconOnly = true, size = "sm", variant = "secondary", ...props }) {
|
|
734
|
+
const [copied, setCopied] = useState5(false);
|
|
735
|
+
const t = useRef5(void 0);
|
|
736
|
+
useEffect5(() => () => clearTimeout(t.current), []);
|
|
737
|
+
const icon = /* @__PURE__ */ jsxs4("span", { className: "relative grid size-4 place-items-center", children: [
|
|
738
|
+
/* @__PURE__ */ jsx6(
|
|
739
|
+
CopyIcon,
|
|
740
|
+
{
|
|
741
|
+
size: 16,
|
|
742
|
+
className: cn("absolute transition-[opacity,filter] duration-200 ease-out", copied ? "opacity-0 blur-[2px]" : "opacity-100")
|
|
743
|
+
}
|
|
744
|
+
),
|
|
745
|
+
/* @__PURE__ */ jsx6(
|
|
746
|
+
CheckIcon,
|
|
747
|
+
{
|
|
748
|
+
size: 16,
|
|
749
|
+
className: cn("absolute transition-[opacity,filter] duration-200 ease-out", copied ? "opacity-100" : "opacity-0 blur-[2px]")
|
|
750
|
+
}
|
|
751
|
+
)
|
|
752
|
+
] });
|
|
753
|
+
return /* @__PURE__ */ jsxs4(
|
|
754
|
+
Button,
|
|
755
|
+
{
|
|
756
|
+
...props,
|
|
757
|
+
size,
|
|
758
|
+
variant,
|
|
759
|
+
isIconOnly,
|
|
760
|
+
"aria-label": isIconOnly ? copied ? copiedLabel : label : void 0,
|
|
761
|
+
onPress: () => {
|
|
762
|
+
const text = typeof value === "function" ? value() : value;
|
|
763
|
+
void navigator.clipboard?.writeText(text).catch(() => setCopied(false));
|
|
764
|
+
setCopied(true);
|
|
765
|
+
onCopy?.(text);
|
|
766
|
+
clearTimeout(t.current);
|
|
767
|
+
t.current = setTimeout(() => setCopied(false), 1600);
|
|
768
|
+
},
|
|
769
|
+
children: [
|
|
770
|
+
icon,
|
|
771
|
+
!isIconOnly && /* @__PURE__ */ jsx6("span", { children: copied ? copiedLabel : label }),
|
|
772
|
+
/* @__PURE__ */ jsx6("span", { role: "status", className: "sr-only", children: copied ? copiedLabel : "" })
|
|
773
|
+
]
|
|
774
|
+
}
|
|
775
|
+
);
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
// src/components/GestureArea.tsx
|
|
779
|
+
import { forwardRef as forwardRef3 } from "react";
|
|
780
|
+
import { mergeProps as mergeProps4 } from "react-aria";
|
|
781
|
+
import { jsx as jsx7 } from "react/jsx-runtime";
|
|
782
|
+
var GestureArea = forwardRef3(function GestureArea2(props, ref) {
|
|
783
|
+
const {
|
|
784
|
+
onSingleTap,
|
|
785
|
+
onDoubleTap,
|
|
786
|
+
onMultiTap,
|
|
787
|
+
maxTaps,
|
|
788
|
+
multiTapDelay,
|
|
789
|
+
onHoldStart,
|
|
790
|
+
onHold,
|
|
791
|
+
onHoldTick,
|
|
792
|
+
onHoldProgress,
|
|
793
|
+
onHoldEnd,
|
|
794
|
+
onHoldCancel,
|
|
795
|
+
holdDuration,
|
|
796
|
+
holdTickInterval,
|
|
797
|
+
onSwipe,
|
|
798
|
+
swipeThreshold,
|
|
799
|
+
swipeVelocity,
|
|
800
|
+
moveTolerance,
|
|
801
|
+
dispatchDomEvents,
|
|
802
|
+
isDisabled,
|
|
803
|
+
className,
|
|
804
|
+
...rest
|
|
805
|
+
} = props;
|
|
806
|
+
const { gestureProps, isPressed, isHolding, didHold } = useGesture({
|
|
807
|
+
onSingleTap,
|
|
808
|
+
onDoubleTap,
|
|
809
|
+
onMultiTap,
|
|
810
|
+
maxTaps,
|
|
811
|
+
multiTapDelay,
|
|
812
|
+
onHoldStart,
|
|
813
|
+
onHold,
|
|
814
|
+
onHoldTick,
|
|
815
|
+
onHoldProgress,
|
|
816
|
+
onHoldEnd,
|
|
817
|
+
onHoldCancel,
|
|
818
|
+
holdDuration,
|
|
819
|
+
holdTickInterval,
|
|
820
|
+
onSwipe,
|
|
821
|
+
swipeThreshold,
|
|
822
|
+
swipeVelocity,
|
|
823
|
+
moveTolerance,
|
|
824
|
+
dispatchDomEvents,
|
|
825
|
+
isDisabled
|
|
826
|
+
});
|
|
827
|
+
return /* @__PURE__ */ jsx7(
|
|
828
|
+
"div",
|
|
829
|
+
{
|
|
830
|
+
ref,
|
|
831
|
+
role: "button",
|
|
832
|
+
tabIndex: isDisabled ? -1 : 0,
|
|
833
|
+
"aria-disabled": isDisabled || void 0,
|
|
834
|
+
...mergeProps4(rest, gestureProps),
|
|
835
|
+
"data-pressed": isPressed || void 0,
|
|
836
|
+
"data-holding": isPressed && isHolding && !didHold || void 0,
|
|
837
|
+
"data-held": didHold || void 0,
|
|
838
|
+
className: cn("touch-manipulation select-none [-webkit-touch-callout:none] [-webkit-tap-highlight-color:transparent]", focusRing, className)
|
|
839
|
+
}
|
|
840
|
+
);
|
|
841
|
+
});
|
|
842
|
+
|
|
843
|
+
// src/components/ToggleButton.tsx
|
|
844
|
+
import {
|
|
845
|
+
SelectionIndicator,
|
|
846
|
+
ToggleButton as RACToggleButton,
|
|
847
|
+
ToggleButtonGroup as RACToggleButtonGroup
|
|
848
|
+
} from "react-aria-components";
|
|
849
|
+
import { jsx as jsx8, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
850
|
+
function ToggleButton({ size = "sm", isIconOnly, className, ...props }) {
|
|
851
|
+
return /* @__PURE__ */ jsx8(
|
|
852
|
+
RACToggleButton,
|
|
853
|
+
{
|
|
854
|
+
...props,
|
|
855
|
+
className: composeTw(
|
|
856
|
+
className,
|
|
857
|
+
(rp) => buttonStyles({
|
|
858
|
+
variant: rp.isSelected ? "primary" : "secondary",
|
|
859
|
+
size,
|
|
860
|
+
isIconOnly
|
|
861
|
+
})
|
|
862
|
+
)
|
|
863
|
+
}
|
|
864
|
+
);
|
|
865
|
+
}
|
|
866
|
+
function SegmentedControl({ size = "sm", className, selectionMode = "single", disallowEmptySelection = true, ...props }) {
|
|
867
|
+
return /* @__PURE__ */ jsx8(
|
|
868
|
+
RACToggleButtonGroup,
|
|
869
|
+
{
|
|
870
|
+
...props,
|
|
871
|
+
selectionMode,
|
|
872
|
+
disallowEmptySelection,
|
|
873
|
+
"data-size": size,
|
|
874
|
+
className: composeTw(
|
|
875
|
+
className,
|
|
876
|
+
"group/seg inline-flex w-fit gap-0.5 rounded-[10px] border border-line bg-surface p-[3px] shadow-control"
|
|
877
|
+
)
|
|
878
|
+
}
|
|
879
|
+
);
|
|
880
|
+
}
|
|
881
|
+
function Segment({ children, className, ...props }) {
|
|
882
|
+
return /* @__PURE__ */ jsxs5(
|
|
883
|
+
RACToggleButton,
|
|
884
|
+
{
|
|
885
|
+
...props,
|
|
886
|
+
className: composeTw(
|
|
887
|
+
className,
|
|
888
|
+
cn(
|
|
889
|
+
"relative inline-flex cursor-default items-center justify-center gap-2 rounded-[7px] px-3.5 whitespace-nowrap outline-none select-none",
|
|
890
|
+
"text-fg-muted transition-colors duration-200 ease-out hover:text-fg selected:text-fg disabled:opacity-40",
|
|
891
|
+
"focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-focus",
|
|
892
|
+
"h-8 text-[14px] group-data-[size=md]/seg:h-10 group-data-[size=md]/seg:text-[15px]"
|
|
893
|
+
)
|
|
894
|
+
),
|
|
895
|
+
children: [
|
|
896
|
+
/* @__PURE__ */ jsx8(SelectionIndicator, { className: "absolute inset-0 rounded-[7px] bg-bg shadow-control transition-[translate,width] duration-250 ease-out motion-reduce:transition-none" }),
|
|
897
|
+
/* @__PURE__ */ jsx8("span", { className: "relative z-10 inline-flex items-center gap-2", children })
|
|
898
|
+
]
|
|
899
|
+
}
|
|
900
|
+
);
|
|
901
|
+
}
|
|
902
|
+
|
|
903
|
+
// src/components/Menu.tsx
|
|
904
|
+
import {
|
|
905
|
+
Collection,
|
|
906
|
+
Header,
|
|
907
|
+
Keyboard,
|
|
908
|
+
Menu as RACMenu,
|
|
909
|
+
MenuItem as RACMenuItem,
|
|
910
|
+
MenuSection as RACMenuSection,
|
|
911
|
+
MenuTrigger as RACMenuTrigger,
|
|
912
|
+
Separator,
|
|
913
|
+
SubmenuTrigger as RACSubmenuTrigger,
|
|
914
|
+
Text
|
|
915
|
+
} from "react-aria-components";
|
|
916
|
+
|
|
917
|
+
// src/components/Popover.tsx
|
|
918
|
+
import { Popover as RACPopover } from "react-aria-components";
|
|
919
|
+
import { jsx as jsx9 } from "react/jsx-runtime";
|
|
920
|
+
var floatingSurface = cn(
|
|
921
|
+
"rounded-menu border border-line-subtle bg-overlay text-fg shadow-float outline-none"
|
|
922
|
+
);
|
|
923
|
+
var floatingMotion = {
|
|
924
|
+
fade: cn("transition-opacity duration-120 ease-out", "entering:opacity-0 exiting:opacity-0 exiting:duration-100"),
|
|
925
|
+
scale: cn(
|
|
926
|
+
"origin-(--trigger-anchor-point) will-change-transform",
|
|
927
|
+
"transition-[opacity,transform] duration-200 ease-out",
|
|
928
|
+
"entering:opacity-0 entering:scale-[0.96]",
|
|
929
|
+
"exiting:opacity-0 exiting:scale-[0.98] exiting:duration-120",
|
|
930
|
+
"motion-reduce:entering:scale-100 motion-reduce:exiting:scale-100"
|
|
931
|
+
),
|
|
932
|
+
none: ""
|
|
933
|
+
};
|
|
934
|
+
function Popover({ className, offset = 6, animation, ...props }) {
|
|
935
|
+
const { menuAnimation } = useKrtsConfig();
|
|
936
|
+
return /* @__PURE__ */ jsx9(
|
|
937
|
+
RACPopover,
|
|
938
|
+
{
|
|
939
|
+
offset,
|
|
940
|
+
...props,
|
|
941
|
+
"data-animation": animation ?? menuAnimation,
|
|
942
|
+
className: composeTw(className, cn(floatingSurface, floatingMotion[animation ?? menuAnimation], "min-w-(--trigger-width)"))
|
|
943
|
+
}
|
|
944
|
+
);
|
|
945
|
+
}
|
|
946
|
+
|
|
947
|
+
// src/components/Menu.tsx
|
|
948
|
+
import { Fragment as Fragment3, jsx as jsx10, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
949
|
+
function MenuTrigger(props) {
|
|
950
|
+
return /* @__PURE__ */ jsx10(RACMenuTrigger, { ...props });
|
|
951
|
+
}
|
|
952
|
+
function SubmenuTrigger(props) {
|
|
953
|
+
return /* @__PURE__ */ jsx10(RACSubmenuTrigger, { ...props });
|
|
954
|
+
}
|
|
955
|
+
function Menu({ placement = "bottom start", withPopover = true, popoverClassName, animation, className, ...props }) {
|
|
956
|
+
const menu = /* @__PURE__ */ jsx10(
|
|
957
|
+
RACMenu,
|
|
958
|
+
{
|
|
959
|
+
...props,
|
|
960
|
+
className: composeTw(className, "max-h-[inherit] min-w-[13rem] overflow-auto p-1.5 outline-none")
|
|
961
|
+
}
|
|
962
|
+
);
|
|
963
|
+
if (!withPopover) return menu;
|
|
964
|
+
return /* @__PURE__ */ jsx10(Popover, { placement, animation, className: popoverClassName, children: menu });
|
|
965
|
+
}
|
|
966
|
+
function MenuItem({ children, description, icon, shortcut, variant = "default", className, ...props }) {
|
|
967
|
+
const textValue = props.textValue ?? (typeof children === "string" ? children : void 0);
|
|
968
|
+
return /* @__PURE__ */ jsx10(
|
|
969
|
+
RACMenuItem,
|
|
970
|
+
{
|
|
971
|
+
textValue,
|
|
972
|
+
...props,
|
|
973
|
+
className: composeTw(
|
|
974
|
+
className,
|
|
975
|
+
cn(
|
|
976
|
+
// Hovered/focused items change instantly: menus are used too often to animate.
|
|
977
|
+
"group relative flex min-h-9 cursor-default items-center gap-2.5 rounded-control px-2.5 py-1.5 text-[15px] outline-none select-none",
|
|
978
|
+
"disabled:opacity-40",
|
|
979
|
+
variant === "danger" ? "text-danger focus:bg-danger-soft" : "text-fg focus:bg-surface-hover pressed:bg-surface-pressed"
|
|
980
|
+
)
|
|
981
|
+
),
|
|
982
|
+
children: ({ hasSubmenu, isSelected, selectionMode }) => /* @__PURE__ */ jsxs6(Fragment3, { children: [
|
|
983
|
+
selectionMode !== "none" && /* @__PURE__ */ jsx10("span", { className: "grid size-4 shrink-0 place-items-center", children: isSelected && /* @__PURE__ */ jsx10(CheckIcon, { size: 14 }) }),
|
|
984
|
+
icon && /* @__PURE__ */ jsx10("span", { className: "grid size-4 shrink-0 place-items-center text-fg-muted group-focus:text-current", children: icon }),
|
|
985
|
+
/* @__PURE__ */ jsxs6("span", { className: "flex min-w-0 flex-1 flex-col", children: [
|
|
986
|
+
/* @__PURE__ */ jsx10(Text, { slot: "label", className: "truncate", children }),
|
|
987
|
+
description && /* @__PURE__ */ jsx10(Text, { slot: "description", className: "truncate text-[13px] text-fg-muted", children: description })
|
|
988
|
+
] }),
|
|
989
|
+
shortcut && /* @__PURE__ */ jsx10(Keyboard, { className: "font-sans text-[12px] tracking-wide text-fg-subtle", children: shortcut }),
|
|
990
|
+
hasSubmenu && /* @__PURE__ */ jsx10(ChevronRightIcon, { size: 14, className: "text-fg-subtle" })
|
|
991
|
+
] })
|
|
992
|
+
}
|
|
993
|
+
);
|
|
994
|
+
}
|
|
995
|
+
function MenuSection({ title, children, className, ...props }) {
|
|
996
|
+
return /* @__PURE__ */ jsxs6(RACMenuSection, { ...props, className: cn("flex flex-col", className), children: [
|
|
997
|
+
title && /* @__PURE__ */ jsx10(Header, { className: "px-2.5 pt-2 pb-1 text-[12px] font-medium text-fg-subtle", children: title }),
|
|
998
|
+
/* @__PURE__ */ jsx10(Collection, { items: props.items, children })
|
|
999
|
+
] });
|
|
1000
|
+
}
|
|
1001
|
+
function MenuSeparator() {
|
|
1002
|
+
return /* @__PURE__ */ jsx10(Separator, { className: "mx-1 my-1.5 h-px bg-line-subtle" });
|
|
1003
|
+
}
|
|
1004
|
+
|
|
1005
|
+
// src/components/DropdownButton.tsx
|
|
1006
|
+
import { jsx as jsx11, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
1007
|
+
function DropdownButton({
|
|
1008
|
+
label,
|
|
1009
|
+
children,
|
|
1010
|
+
variant = "primary",
|
|
1011
|
+
size = "md",
|
|
1012
|
+
isDisabled,
|
|
1013
|
+
placement = "bottom start",
|
|
1014
|
+
className,
|
|
1015
|
+
...menuProps
|
|
1016
|
+
}) {
|
|
1017
|
+
const divider = variant === "primary" ? "bg-primary-line" : variant === "danger" ? "bg-danger-line" : "bg-line";
|
|
1018
|
+
return /* @__PURE__ */ jsxs7(MenuTrigger, { children: [
|
|
1019
|
+
/* @__PURE__ */ jsxs7(Button, { variant, size, isDisabled, className: cn("group gap-0 pr-0", size === "md" ? "pl-6" : "pl-3.5", className), children: [
|
|
1020
|
+
/* @__PURE__ */ jsx11("span", { children: label }),
|
|
1021
|
+
/* @__PURE__ */ jsx11("span", { "aria-hidden": true, className: cn("w-px self-stretch", size === "md" ? "ml-3" : "ml-2.5", divider) }),
|
|
1022
|
+
/* @__PURE__ */ jsx11("span", { className: cn("grid place-items-center", size === "md" ? "w-11" : size === "sm" ? "w-9" : "w-7"), children: /* @__PURE__ */ jsx11(
|
|
1023
|
+
ChevronDownIcon,
|
|
1024
|
+
{
|
|
1025
|
+
size: size === "md" ? 20 : 16,
|
|
1026
|
+
className: "transition-transform duration-200 ease-out group-aria-expanded:rotate-180"
|
|
1027
|
+
}
|
|
1028
|
+
) })
|
|
1029
|
+
] }),
|
|
1030
|
+
/* @__PURE__ */ jsx11(Menu, { placement, ...menuProps, children })
|
|
1031
|
+
] });
|
|
1032
|
+
}
|
|
1033
|
+
function SplitButton({
|
|
1034
|
+
label,
|
|
1035
|
+
children,
|
|
1036
|
+
variant = "primary",
|
|
1037
|
+
size = "md",
|
|
1038
|
+
isDisabled,
|
|
1039
|
+
placement = "bottom end",
|
|
1040
|
+
onAction,
|
|
1041
|
+
onMenuAction,
|
|
1042
|
+
menuLabel = "More options",
|
|
1043
|
+
className,
|
|
1044
|
+
...menuProps
|
|
1045
|
+
}) {
|
|
1046
|
+
return /* @__PURE__ */ jsxs7("div", { className: cn("inline-flex", className), role: "group", children: [
|
|
1047
|
+
/* @__PURE__ */ jsx11(Button, { variant, size, isDisabled, onPress: onAction, className: "rounded-r-none border-r-0", children: label }),
|
|
1048
|
+
/* @__PURE__ */ jsxs7(MenuTrigger, { children: [
|
|
1049
|
+
/* @__PURE__ */ jsx11(
|
|
1050
|
+
Button,
|
|
1051
|
+
{
|
|
1052
|
+
variant,
|
|
1053
|
+
size,
|
|
1054
|
+
isDisabled,
|
|
1055
|
+
isIconOnly: true,
|
|
1056
|
+
"aria-label": menuLabel,
|
|
1057
|
+
className: cn(
|
|
1058
|
+
"group rounded-l-none",
|
|
1059
|
+
variant === "primary" ? "border-l-primary-line" : variant === "danger" ? "border-l-danger-line" : "border-l-line"
|
|
1060
|
+
),
|
|
1061
|
+
children: /* @__PURE__ */ jsx11(ChevronDownIcon, { size: size === "md" ? 20 : 16, className: "transition-transform duration-200 ease-out group-aria-expanded:rotate-180" })
|
|
1062
|
+
}
|
|
1063
|
+
),
|
|
1064
|
+
/* @__PURE__ */ jsx11(Menu, { placement, onAction: onMenuAction, ...menuProps, children })
|
|
1065
|
+
] })
|
|
1066
|
+
] });
|
|
1067
|
+
}
|
|
1068
|
+
|
|
1069
|
+
// src/components/Tooltip.tsx
|
|
1070
|
+
import {
|
|
1071
|
+
OverlayArrow,
|
|
1072
|
+
Tooltip as RACTooltip,
|
|
1073
|
+
TooltipTrigger as RACTooltipTrigger
|
|
1074
|
+
} from "react-aria-components";
|
|
1075
|
+
import { jsx as jsx12, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
1076
|
+
function Tooltip({ children, shortcut, showArrow = false, offset = 8, className, ...props }) {
|
|
1077
|
+
return /* @__PURE__ */ jsxs8(
|
|
1078
|
+
RACTooltip,
|
|
1079
|
+
{
|
|
1080
|
+
offset,
|
|
1081
|
+
...props,
|
|
1082
|
+
className: composeTw(
|
|
1083
|
+
className,
|
|
1084
|
+
cn(
|
|
1085
|
+
"group z-50 flex max-w-64 items-center gap-2 rounded-lg bg-primary px-2.5 py-1.5 text-[13px] leading-snug text-primary-fg shadow-float",
|
|
1086
|
+
"origin-(--trigger-anchor-point) will-change-transform",
|
|
1087
|
+
"transition-[opacity,transform] duration-125 ease-out",
|
|
1088
|
+
"entering:scale-[0.97] entering:opacity-0 exiting:scale-[0.97] exiting:opacity-0",
|
|
1089
|
+
"motion-reduce:entering:scale-100 motion-reduce:exiting:scale-100"
|
|
1090
|
+
)
|
|
1091
|
+
),
|
|
1092
|
+
children: [
|
|
1093
|
+
showArrow && /* @__PURE__ */ jsx12(OverlayArrow, { children: /* @__PURE__ */ jsx12("svg", { width: 8, height: 8, viewBox: "0 0 8 8", className: "fill-primary group-placement-bottom:rotate-180 group-placement-left:-rotate-90 group-placement-right:rotate-90", children: /* @__PURE__ */ jsx12("path", { d: "M0 0 L4 4 L8 0" }) }) }),
|
|
1094
|
+
/* @__PURE__ */ jsx12("span", { children }),
|
|
1095
|
+
shortcut && /* @__PURE__ */ jsx12("kbd", { className: "rounded border border-primary-fg/20 px-1 font-sans text-[11px] text-primary-fg/70", children: shortcut })
|
|
1096
|
+
]
|
|
1097
|
+
}
|
|
1098
|
+
);
|
|
1099
|
+
}
|
|
1100
|
+
function TooltipTrigger({ delay = 600, closeDelay = 300, ...props }) {
|
|
1101
|
+
return /* @__PURE__ */ jsx12(RACTooltipTrigger, { delay, closeDelay, ...props });
|
|
1102
|
+
}
|
|
1103
|
+
function WithTooltip({
|
|
1104
|
+
content,
|
|
1105
|
+
children,
|
|
1106
|
+
placement,
|
|
1107
|
+
shortcut,
|
|
1108
|
+
delay
|
|
1109
|
+
}) {
|
|
1110
|
+
return /* @__PURE__ */ jsxs8(TooltipTrigger, { delay, children: [
|
|
1111
|
+
children,
|
|
1112
|
+
/* @__PURE__ */ jsx12(Tooltip, { placement, shortcut, children: content })
|
|
1113
|
+
] });
|
|
1114
|
+
}
|
|
1115
|
+
|
|
1116
|
+
// src/components/Field.tsx
|
|
1117
|
+
import { forwardRef as forwardRef4 } from "react";
|
|
1118
|
+
import {
|
|
1119
|
+
FieldError as RACFieldError,
|
|
1120
|
+
Input as RACInput,
|
|
1121
|
+
Label as RACLabel,
|
|
1122
|
+
NumberField as RACNumberField,
|
|
1123
|
+
SearchField as RACSearchField,
|
|
1124
|
+
Text as Text2,
|
|
1125
|
+
TextArea as RACTextArea,
|
|
1126
|
+
TextField as RACTextField,
|
|
1127
|
+
Group,
|
|
1128
|
+
Button as RACButton3
|
|
1129
|
+
} from "react-aria-components";
|
|
1130
|
+
import { jsx as jsx13, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
1131
|
+
function Label(props) {
|
|
1132
|
+
return /* @__PURE__ */ jsx13(RACLabel, { ...props, className: cn("text-sm font-medium text-fg select-none", props.className) });
|
|
1133
|
+
}
|
|
1134
|
+
function Description(props) {
|
|
1135
|
+
return /* @__PURE__ */ jsx13(Text2, { ...props, slot: "description", className: cn("text-[13px] text-fg-muted", props.className) });
|
|
1136
|
+
}
|
|
1137
|
+
function FieldError(props) {
|
|
1138
|
+
return /* @__PURE__ */ jsx13(
|
|
1139
|
+
RACFieldError,
|
|
1140
|
+
{
|
|
1141
|
+
...props,
|
|
1142
|
+
className: composeTw(
|
|
1143
|
+
props.className,
|
|
1144
|
+
// Errors appear mid-form; ease them in so the layout change isn't jarring.
|
|
1145
|
+
"text-[13px] text-danger transition-[opacity,translate] duration-200 ease-out starting:-translate-y-1 starting:opacity-0 motion-reduce:starting:translate-y-0"
|
|
1146
|
+
)
|
|
1147
|
+
}
|
|
1148
|
+
);
|
|
1149
|
+
}
|
|
1150
|
+
var inputFrame = cn(
|
|
1151
|
+
"flex w-full items-center rounded-control border bg-surface text-fg shadow-control",
|
|
1152
|
+
"border-line transition-[border-color,box-shadow,background-color] duration-150 ease-out",
|
|
1153
|
+
"hover:border-line-strong",
|
|
1154
|
+
"focus-within:border-line-strong focus-within:ring-2 focus-within:ring-focus/15",
|
|
1155
|
+
"has-[[data-invalid]]:border-danger has-[[aria-invalid=true]]:border-danger",
|
|
1156
|
+
"has-[[data-disabled]]:opacity-50 has-[[data-disabled]]:cursor-not-allowed"
|
|
1157
|
+
);
|
|
1158
|
+
var inputSizes = { sm: "h-9 text-sm", md: "h-12 text-base" };
|
|
1159
|
+
var Input = forwardRef4(function Input2(props, ref) {
|
|
1160
|
+
return /* @__PURE__ */ jsx13(
|
|
1161
|
+
RACInput,
|
|
1162
|
+
{
|
|
1163
|
+
ref,
|
|
1164
|
+
...props,
|
|
1165
|
+
className: composeTw(
|
|
1166
|
+
props.className,
|
|
1167
|
+
"min-w-0 flex-1 bg-transparent px-3.5 font-sans text-fg outline-none placeholder:text-fg-subtle disabled:cursor-not-allowed [&::-webkit-search-cancel-button]:hidden"
|
|
1168
|
+
)
|
|
1169
|
+
}
|
|
1170
|
+
);
|
|
1171
|
+
});
|
|
1172
|
+
function TextField({
|
|
1173
|
+
label,
|
|
1174
|
+
description,
|
|
1175
|
+
errorMessage,
|
|
1176
|
+
placeholder,
|
|
1177
|
+
size = "md",
|
|
1178
|
+
startContent,
|
|
1179
|
+
endContent,
|
|
1180
|
+
className,
|
|
1181
|
+
...props
|
|
1182
|
+
}) {
|
|
1183
|
+
return /* @__PURE__ */ jsxs9(RACTextField, { ...props, className: composeTw(className, "group flex flex-col gap-1.5"), children: [
|
|
1184
|
+
label && /* @__PURE__ */ jsx13(Label, { children: label }),
|
|
1185
|
+
/* @__PURE__ */ jsxs9("div", { className: cn(inputFrame, inputSizes[size]), children: [
|
|
1186
|
+
startContent && /* @__PURE__ */ jsx13("span", { className: "pl-3.5 text-fg-subtle [&+input]:pl-2", children: startContent }),
|
|
1187
|
+
/* @__PURE__ */ jsx13(Input, { placeholder }),
|
|
1188
|
+
endContent && /* @__PURE__ */ jsx13("span", { className: "pr-3.5 text-fg-subtle", children: endContent })
|
|
1189
|
+
] }),
|
|
1190
|
+
description && /* @__PURE__ */ jsx13(Description, { children: description }),
|
|
1191
|
+
/* @__PURE__ */ jsx13(FieldError, { children: errorMessage })
|
|
1192
|
+
] });
|
|
1193
|
+
}
|
|
1194
|
+
function TextAreaField({ label, description, errorMessage, placeholder, rows = 4, className, ...props }) {
|
|
1195
|
+
return /* @__PURE__ */ jsxs9(RACTextField, { ...props, className: composeTw(className, "group flex flex-col gap-1.5"), children: [
|
|
1196
|
+
label && /* @__PURE__ */ jsx13(Label, { children: label }),
|
|
1197
|
+
/* @__PURE__ */ jsx13("div", { className: cn(inputFrame, "items-stretch"), children: /* @__PURE__ */ jsx13(
|
|
1198
|
+
RACTextArea,
|
|
1199
|
+
{
|
|
1200
|
+
rows,
|
|
1201
|
+
placeholder,
|
|
1202
|
+
className: "min-h-0 w-full resize-y bg-transparent px-3.5 py-3 font-sans text-base text-fg outline-none placeholder:text-fg-subtle"
|
|
1203
|
+
}
|
|
1204
|
+
) }),
|
|
1205
|
+
description && /* @__PURE__ */ jsx13(Description, { children: description }),
|
|
1206
|
+
/* @__PURE__ */ jsx13(FieldError, { children: errorMessage })
|
|
1207
|
+
] });
|
|
1208
|
+
}
|
|
1209
|
+
function SearchField({ label, description, errorMessage, placeholder = "Search\u2026", size = "md", className, ...props }) {
|
|
1210
|
+
return /* @__PURE__ */ jsxs9(RACSearchField, { ...props, className: composeTw(className, "group flex flex-col gap-1.5"), children: [
|
|
1211
|
+
label && /* @__PURE__ */ jsx13(Label, { children: label }),
|
|
1212
|
+
/* @__PURE__ */ jsxs9("div", { className: cn(inputFrame, inputSizes[size]), children: [
|
|
1213
|
+
/* @__PURE__ */ jsx13(SearchIcon, { className: "ml-3.5 shrink-0 text-fg-subtle" }),
|
|
1214
|
+
/* @__PURE__ */ jsx13(Input, { placeholder, className: "pl-2" }),
|
|
1215
|
+
/* @__PURE__ */ jsx13(
|
|
1216
|
+
RACButton3,
|
|
1217
|
+
{
|
|
1218
|
+
className: cn(
|
|
1219
|
+
"mr-2 grid size-7 place-items-center rounded-md text-fg-subtle outline-none",
|
|
1220
|
+
"transition-[opacity,background-color] duration-150 ease-out hover:bg-surface-hover pressed:bg-surface-pressed",
|
|
1221
|
+
"focus-visible:ring-2 focus-visible:ring-focus/40 group-data-[empty]:pointer-events-none group-data-[empty]:opacity-0"
|
|
1222
|
+
),
|
|
1223
|
+
children: /* @__PURE__ */ jsx13(XIcon, { size: 14 })
|
|
1224
|
+
}
|
|
1225
|
+
)
|
|
1226
|
+
] }),
|
|
1227
|
+
description && /* @__PURE__ */ jsx13(Description, { children: description }),
|
|
1228
|
+
/* @__PURE__ */ jsx13(FieldError, { children: errorMessage })
|
|
1229
|
+
] });
|
|
1230
|
+
}
|
|
1231
|
+
var stepper = cn(
|
|
1232
|
+
"grid h-full w-10 shrink-0 place-items-center text-fg-muted outline-none",
|
|
1233
|
+
"transition-[background-color,color] duration-150 ease-out hover:bg-surface-hover hover:text-fg pressed:bg-surface-pressed",
|
|
1234
|
+
"disabled:opacity-40 focus-visible:bg-surface-hover"
|
|
1235
|
+
);
|
|
1236
|
+
function NumberField({ label, description, errorMessage, placeholder, size = "md", className, ...props }) {
|
|
1237
|
+
return /* @__PURE__ */ jsxs9(RACNumberField, { ...props, className: composeTw(className, "group flex flex-col gap-1.5"), children: [
|
|
1238
|
+
label && /* @__PURE__ */ jsx13(Label, { children: label }),
|
|
1239
|
+
/* @__PURE__ */ jsxs9(Group, { className: cn(inputFrame, inputSizes[size], "overflow-hidden"), children: [
|
|
1240
|
+
/* @__PURE__ */ jsx13(RACButton3, { slot: "decrement", className: cn(stepper, "border-r border-line"), children: /* @__PURE__ */ jsx13(MinusIcon, { size: 14 }) }),
|
|
1241
|
+
/* @__PURE__ */ jsx13(Input, { placeholder, className: "text-center tabular-nums" }),
|
|
1242
|
+
/* @__PURE__ */ jsx13(RACButton3, { slot: "increment", className: cn(stepper, "border-l border-line"), children: /* @__PURE__ */ jsx13(PlusIcon, { size: 14 }) })
|
|
1243
|
+
] }),
|
|
1244
|
+
description && /* @__PURE__ */ jsx13(Description, { children: description }),
|
|
1245
|
+
/* @__PURE__ */ jsx13(FieldError, { children: errorMessage })
|
|
1246
|
+
] });
|
|
1247
|
+
}
|
|
1248
|
+
|
|
1249
|
+
// src/components/Checkbox.tsx
|
|
1250
|
+
import {
|
|
1251
|
+
Checkbox as RACCheckbox,
|
|
1252
|
+
CheckboxGroup as RACCheckboxGroup
|
|
1253
|
+
} from "react-aria-components";
|
|
1254
|
+
import { Fragment as Fragment4, jsx as jsx14, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
1255
|
+
function Checkbox({ children, className, ...props }) {
|
|
1256
|
+
return /* @__PURE__ */ jsx14(
|
|
1257
|
+
RACCheckbox,
|
|
1258
|
+
{
|
|
1259
|
+
...props,
|
|
1260
|
+
className: composeTw(
|
|
1261
|
+
className,
|
|
1262
|
+
"group flex items-center gap-2.5 text-base text-fg select-none disabled:opacity-50 [-webkit-tap-highlight-color:transparent]"
|
|
1263
|
+
),
|
|
1264
|
+
children: ({ isSelected, isIndeterminate }) => /* @__PURE__ */ jsxs10(Fragment4, { children: [
|
|
1265
|
+
/* @__PURE__ */ jsx14(
|
|
1266
|
+
"span",
|
|
1267
|
+
{
|
|
1268
|
+
className: cn(
|
|
1269
|
+
"grid size-6 shrink-0 place-items-center rounded-[7px] border shadow-control",
|
|
1270
|
+
"transition-[background-color,border-color] duration-150 ease-out",
|
|
1271
|
+
"group-focus-visible:outline-2 group-focus-visible:outline-offset-2 group-focus-visible:outline-focus",
|
|
1272
|
+
isSelected || isIndeterminate ? "border-primary-line bg-primary text-primary-fg group-hover:bg-primary-hover" : "border-line bg-surface group-hover:border-line-strong",
|
|
1273
|
+
"group-invalid:border-danger"
|
|
1274
|
+
),
|
|
1275
|
+
children: /* @__PURE__ */ jsx14("svg", { viewBox: "0 0 16 16", className: "size-4", fill: "none", stroke: "currentColor", strokeWidth: 2.25, strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": true, children: isIndeterminate ? /* @__PURE__ */ jsx14("path", { d: "M4 8h8" }) : /* @__PURE__ */ jsx14(
|
|
1276
|
+
"path",
|
|
1277
|
+
{
|
|
1278
|
+
d: "M3.5 8.5 6.5 11.5 12.5 4.5",
|
|
1279
|
+
pathLength: 1,
|
|
1280
|
+
className: "transition-[stroke-dashoffset] duration-200 ease-out [stroke-dasharray:1]",
|
|
1281
|
+
style: { strokeDashoffset: isSelected ? 0 : 1 }
|
|
1282
|
+
}
|
|
1283
|
+
) })
|
|
1284
|
+
}
|
|
1285
|
+
),
|
|
1286
|
+
children
|
|
1287
|
+
] })
|
|
1288
|
+
}
|
|
1289
|
+
);
|
|
1290
|
+
}
|
|
1291
|
+
function CheckboxGroup({ label, description, errorMessage, children, className, ...props }) {
|
|
1292
|
+
return /* @__PURE__ */ jsxs10(RACCheckboxGroup, { ...props, className: composeTw(className, "flex flex-col gap-2.5"), children: [
|
|
1293
|
+
label && /* @__PURE__ */ jsx14(Label, { children: label }),
|
|
1294
|
+
children,
|
|
1295
|
+
description && /* @__PURE__ */ jsx14(Description, { children: description }),
|
|
1296
|
+
/* @__PURE__ */ jsx14(FieldError, { children: errorMessage })
|
|
1297
|
+
] });
|
|
1298
|
+
}
|
|
1299
|
+
|
|
1300
|
+
// src/components/Radio.tsx
|
|
1301
|
+
import {
|
|
1302
|
+
Radio as RACRadio,
|
|
1303
|
+
RadioGroup as RACRadioGroup
|
|
1304
|
+
} from "react-aria-components";
|
|
1305
|
+
import { Fragment as Fragment5, jsx as jsx15, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
1306
|
+
function RadioGroup({ label, description, errorMessage, children, className, ...props }) {
|
|
1307
|
+
return /* @__PURE__ */ jsxs11(
|
|
1308
|
+
RACRadioGroup,
|
|
1309
|
+
{
|
|
1310
|
+
...props,
|
|
1311
|
+
className: composeTw(className, "group flex flex-col gap-2.5"),
|
|
1312
|
+
children: [
|
|
1313
|
+
label && /* @__PURE__ */ jsx15(Label, { children: label }),
|
|
1314
|
+
/* @__PURE__ */ jsx15("div", { className: "flex gap-x-5 gap-y-2.5 group-orientation-vertical:flex-col group-orientation-horizontal:flex-wrap", children }),
|
|
1315
|
+
description && /* @__PURE__ */ jsx15(Description, { children: description }),
|
|
1316
|
+
/* @__PURE__ */ jsx15(FieldError, { children: errorMessage })
|
|
1317
|
+
]
|
|
1318
|
+
}
|
|
1319
|
+
);
|
|
1320
|
+
}
|
|
1321
|
+
function Radio({ children, className, ...props }) {
|
|
1322
|
+
return /* @__PURE__ */ jsx15(
|
|
1323
|
+
RACRadio,
|
|
1324
|
+
{
|
|
1325
|
+
...props,
|
|
1326
|
+
className: composeTw(
|
|
1327
|
+
className,
|
|
1328
|
+
"group flex items-center gap-2.5 text-base text-fg select-none disabled:opacity-50 [-webkit-tap-highlight-color:transparent]"
|
|
1329
|
+
),
|
|
1330
|
+
children: (rp) => /* @__PURE__ */ jsxs11(Fragment5, { children: [
|
|
1331
|
+
/* @__PURE__ */ jsx15(
|
|
1332
|
+
"span",
|
|
1333
|
+
{
|
|
1334
|
+
className: cn(
|
|
1335
|
+
"relative grid size-6 shrink-0 place-items-center rounded-full border shadow-control",
|
|
1336
|
+
"transition-[background-color,border-color] duration-150 ease-out",
|
|
1337
|
+
"group-focus-visible:outline-2 group-focus-visible:outline-offset-2 group-focus-visible:outline-focus",
|
|
1338
|
+
rp.isSelected ? "border-primary-line bg-primary group-hover:bg-primary-hover" : "border-line bg-surface group-hover:border-line-strong"
|
|
1339
|
+
),
|
|
1340
|
+
children: /* @__PURE__ */ jsx15(
|
|
1341
|
+
"span",
|
|
1342
|
+
{
|
|
1343
|
+
className: cn(
|
|
1344
|
+
"size-3 rounded-full bg-primary-fg shadow-hairline transition-[transform,opacity] duration-200 ease-out",
|
|
1345
|
+
rp.isSelected ? "scale-100 opacity-100" : "scale-50 opacity-0"
|
|
1346
|
+
)
|
|
1347
|
+
}
|
|
1348
|
+
)
|
|
1349
|
+
}
|
|
1350
|
+
),
|
|
1351
|
+
typeof children === "function" ? children(rp) : children
|
|
1352
|
+
] })
|
|
1353
|
+
}
|
|
1354
|
+
);
|
|
1355
|
+
}
|
|
1356
|
+
|
|
1357
|
+
// src/components/Switch.tsx
|
|
1358
|
+
import { Switch as RACSwitch } from "react-aria-components";
|
|
1359
|
+
import { jsx as jsx16, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
1360
|
+
function Switch({ children, size = "md", className, ...props }) {
|
|
1361
|
+
const md = size === "md";
|
|
1362
|
+
return /* @__PURE__ */ jsxs12(
|
|
1363
|
+
RACSwitch,
|
|
1364
|
+
{
|
|
1365
|
+
...props,
|
|
1366
|
+
className: composeTw(
|
|
1367
|
+
className,
|
|
1368
|
+
"group flex items-center gap-2.5 text-base text-fg select-none disabled:opacity-50 [-webkit-tap-highlight-color:transparent]"
|
|
1369
|
+
),
|
|
1370
|
+
children: [
|
|
1371
|
+
/* @__PURE__ */ jsx16(
|
|
1372
|
+
"span",
|
|
1373
|
+
{
|
|
1374
|
+
className: cn(
|
|
1375
|
+
"relative shrink-0 rounded-full border shadow-control",
|
|
1376
|
+
"transition-[background-color,border-color] duration-200 ease-out",
|
|
1377
|
+
"border-line bg-surface group-hover:border-line-strong",
|
|
1378
|
+
"group-selected:border-primary-line group-selected:bg-primary",
|
|
1379
|
+
"group-focus-visible:outline-2 group-focus-visible:outline-offset-2 group-focus-visible:outline-focus",
|
|
1380
|
+
md ? "h-8 w-16" : "h-6 w-11"
|
|
1381
|
+
),
|
|
1382
|
+
children: /* @__PURE__ */ jsx16(
|
|
1383
|
+
"span",
|
|
1384
|
+
{
|
|
1385
|
+
className: cn(
|
|
1386
|
+
"absolute top-[1px] rounded-full bg-white group-selected:bg-primary-fg shadow-[0_0_1px_rgb(0_0_0/0.35),0_1px_3px_rgb(0_0_0/0.12)]",
|
|
1387
|
+
"transition-[left,width,background-color] duration-250 ease-out motion-reduce:transition-none",
|
|
1388
|
+
md ? "left-[1px] size-7 group-pressed:w-9 group-selected:left-[calc(100%-29px)] group-selected:group-pressed:left-[calc(100%-37px)]" : "left-[1px] size-5 group-pressed:w-6 group-selected:left-[calc(100%-21px)] group-selected:group-pressed:left-[calc(100%-25px)]"
|
|
1389
|
+
)
|
|
1390
|
+
}
|
|
1391
|
+
)
|
|
1392
|
+
}
|
|
1393
|
+
),
|
|
1394
|
+
children
|
|
1395
|
+
]
|
|
1396
|
+
}
|
|
1397
|
+
);
|
|
1398
|
+
}
|
|
1399
|
+
|
|
1400
|
+
// src/components/Slider.tsx
|
|
1401
|
+
import { useLayoutEffect as useLayoutEffect2, useRef as useRef6 } from "react";
|
|
1402
|
+
import {
|
|
1403
|
+
Slider as RACSlider,
|
|
1404
|
+
SliderOutput,
|
|
1405
|
+
SliderThumb,
|
|
1406
|
+
SliderTrack
|
|
1407
|
+
} from "react-aria-components";
|
|
1408
|
+
import { Fragment as Fragment6, jsx as jsx17, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
1409
|
+
function Slider({
|
|
1410
|
+
label,
|
|
1411
|
+
showValue = !!label,
|
|
1412
|
+
showFill = true,
|
|
1413
|
+
thumbLabels,
|
|
1414
|
+
className,
|
|
1415
|
+
...props
|
|
1416
|
+
}) {
|
|
1417
|
+
return /* @__PURE__ */ jsxs13(
|
|
1418
|
+
RACSlider,
|
|
1419
|
+
{
|
|
1420
|
+
...props,
|
|
1421
|
+
className: composeTw(className, "group flex w-full flex-col gap-2 select-none disabled:opacity-50"),
|
|
1422
|
+
children: [
|
|
1423
|
+
(label || showValue) && /* @__PURE__ */ jsxs13("div", { className: "flex items-center justify-between gap-4", children: [
|
|
1424
|
+
label && /* @__PURE__ */ jsx17(Label, { children: label }),
|
|
1425
|
+
showValue && /* @__PURE__ */ jsx17(SliderOutput, { className: "text-sm tabular-nums text-fg-muted", children: ({ state }) => state.values.map((_, i) => state.getThumbValueLabel(i)).join(" \u2013 ") })
|
|
1426
|
+
] }),
|
|
1427
|
+
/* @__PURE__ */ jsx17(SliderTrack, { className: "group/track relative h-7 w-full touch-none", children: ({ state }) => /* @__PURE__ */ jsx17(TrackContents, { state, showFill, thumbLabels }) })
|
|
1428
|
+
]
|
|
1429
|
+
}
|
|
1430
|
+
);
|
|
1431
|
+
}
|
|
1432
|
+
function TrackContents({ state, showFill, thumbLabels }) {
|
|
1433
|
+
const pcts = state.values.map((_, i) => state.getThumbPercent(i) * 100);
|
|
1434
|
+
const dragging = state.values.map((_, i) => state.isThumbDragging(i));
|
|
1435
|
+
const wasDragging = useRef6(dragging);
|
|
1436
|
+
useLayoutEffect2(() => {
|
|
1437
|
+
wasDragging.current = dragging;
|
|
1438
|
+
});
|
|
1439
|
+
const glide = dragging.map((d, i) => !d || !wasDragging.current[i]);
|
|
1440
|
+
const anyTracking = dragging.some((d, i) => d && !glide[i]);
|
|
1441
|
+
const start = pcts.length > 1 ? pcts[0] : 0;
|
|
1442
|
+
const end = pcts.length > 1 ? pcts[pcts.length - 1] : pcts[0];
|
|
1443
|
+
return /* @__PURE__ */ jsxs13(Fragment6, { children: [
|
|
1444
|
+
/* @__PURE__ */ jsx17("div", { className: "absolute inset-x-0 top-1/2 h-2 -translate-y-1/2 rounded-full bg-surface shadow-hairline" }),
|
|
1445
|
+
showFill && /* @__PURE__ */ jsx17(
|
|
1446
|
+
"div",
|
|
1447
|
+
{
|
|
1448
|
+
className: cn(
|
|
1449
|
+
"absolute top-1/2 h-2 -translate-y-1/2 rounded-full bg-primary",
|
|
1450
|
+
!anyTracking && "transition-[left,width] duration-200 ease-out motion-reduce:transition-none"
|
|
1451
|
+
),
|
|
1452
|
+
style: { left: `${start}%`, width: `${end - start}%` }
|
|
1453
|
+
}
|
|
1454
|
+
),
|
|
1455
|
+
state.values.map((_, i) => /* @__PURE__ */ jsx17(
|
|
1456
|
+
SliderThumb,
|
|
1457
|
+
{
|
|
1458
|
+
index: i,
|
|
1459
|
+
"aria-label": thumbLabels?.[i],
|
|
1460
|
+
className: cn(
|
|
1461
|
+
"top-1/2 size-7 rounded-full border border-surface bg-white",
|
|
1462
|
+
"shadow-[0_0_1px_rgb(0_0_0/0.35),0_1px_3px_rgb(0_0_0/0.15),0_0_0_0_rgb(0_0_0/0)]",
|
|
1463
|
+
// Halo instead of scaling: grows on hover, stronger while dragging.
|
|
1464
|
+
"hover:shadow-[0_0_1px_rgb(0_0_0/0.35),0_1px_3px_rgb(0_0_0/0.15),0_0_0_5px_rgb(0_0_0/0.06)]",
|
|
1465
|
+
"dragging:shadow-[0_0_1px_rgb(0_0_0/0.35),0_1px_3px_rgb(0_0_0/0.15),0_0_0_7px_rgb(0_0_0/0.08)]",
|
|
1466
|
+
"dark:hover:shadow-[0_0_1px_rgb(0_0_0/0.35),0_1px_3px_rgb(0_0_0/0.15),0_0_0_5px_rgb(255_255_255/0.1)]",
|
|
1467
|
+
"dark:dragging:shadow-[0_0_1px_rgb(0_0_0/0.35),0_1px_3px_rgb(0_0_0/0.15),0_0_0_7px_rgb(255_255_255/0.14)]",
|
|
1468
|
+
"focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-focus",
|
|
1469
|
+
glide[i] ? "transition-[left,box-shadow] duration-200 ease-out motion-reduce:transition-[box-shadow]" : "transition-[box-shadow] duration-200 ease-out"
|
|
1470
|
+
)
|
|
1471
|
+
},
|
|
1472
|
+
i
|
|
1473
|
+
))
|
|
1474
|
+
] });
|
|
1475
|
+
}
|
|
1476
|
+
|
|
1477
|
+
// src/components/Select.tsx
|
|
1478
|
+
import {
|
|
1479
|
+
Button as RACButton4,
|
|
1480
|
+
ListBox,
|
|
1481
|
+
ListBoxItem as RACListBoxItem,
|
|
1482
|
+
Select as RACSelect,
|
|
1483
|
+
SelectValue
|
|
1484
|
+
} from "react-aria-components";
|
|
1485
|
+
import { Fragment as Fragment7, jsx as jsx18, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
1486
|
+
function Select({
|
|
1487
|
+
label,
|
|
1488
|
+
description,
|
|
1489
|
+
errorMessage,
|
|
1490
|
+
children,
|
|
1491
|
+
items,
|
|
1492
|
+
size = "md",
|
|
1493
|
+
animation,
|
|
1494
|
+
className,
|
|
1495
|
+
...props
|
|
1496
|
+
}) {
|
|
1497
|
+
return /* @__PURE__ */ jsxs14(RACSelect, { ...props, className: composeTw(className, "group flex flex-col gap-1.5"), children: [
|
|
1498
|
+
label && /* @__PURE__ */ jsx18(Label, { children: label }),
|
|
1499
|
+
/* @__PURE__ */ jsxs14(
|
|
1500
|
+
RACButton4,
|
|
1501
|
+
{
|
|
1502
|
+
className: cn(
|
|
1503
|
+
"flex w-full items-center justify-between gap-2 rounded-control border border-line bg-surface px-3.5 text-left text-fg shadow-control",
|
|
1504
|
+
"transition-[border-color,background-color] duration-150 ease-out hover:border-line-strong pressed:bg-surface-hover",
|
|
1505
|
+
"group-invalid:border-danger disabled:opacity-50",
|
|
1506
|
+
focusRing,
|
|
1507
|
+
inputSizes[size]
|
|
1508
|
+
),
|
|
1509
|
+
children: [
|
|
1510
|
+
/* @__PURE__ */ jsx18(SelectValue, { className: "truncate data-[placeholder]:text-fg-subtle" }),
|
|
1511
|
+
/* @__PURE__ */ jsx18(ChevronDownIcon, { size: 18, className: "shrink-0 text-fg-muted transition-transform duration-200 ease-out group-open:rotate-180" })
|
|
1512
|
+
]
|
|
1513
|
+
}
|
|
1514
|
+
),
|
|
1515
|
+
description && /* @__PURE__ */ jsx18(Description, { children: description }),
|
|
1516
|
+
/* @__PURE__ */ jsx18(FieldError, { children: errorMessage }),
|
|
1517
|
+
/* @__PURE__ */ jsx18(Popover, { placement: "bottom start", animation, children: /* @__PURE__ */ jsx18(ListBox, { items, className: "max-h-72 overflow-auto p-1.5 outline-none", children }) })
|
|
1518
|
+
] });
|
|
1519
|
+
}
|
|
1520
|
+
function SelectItem(props) {
|
|
1521
|
+
const textValue = props.textValue ?? (typeof props.children === "string" ? props.children : void 0);
|
|
1522
|
+
return /* @__PURE__ */ jsx18(
|
|
1523
|
+
RACListBoxItem,
|
|
1524
|
+
{
|
|
1525
|
+
...props,
|
|
1526
|
+
textValue,
|
|
1527
|
+
className: composeTw(
|
|
1528
|
+
props.className,
|
|
1529
|
+
"group flex h-9 cursor-default items-center gap-2 rounded-control px-2.5 text-[15px] text-fg outline-none select-none focus:bg-surface-hover pressed:bg-surface-pressed disabled:opacity-40"
|
|
1530
|
+
),
|
|
1531
|
+
children: ({ isSelected }) => /* @__PURE__ */ jsxs14(Fragment7, { children: [
|
|
1532
|
+
/* @__PURE__ */ jsx18("span", { className: "flex-1 truncate", children: props.children }),
|
|
1533
|
+
isSelected && /* @__PURE__ */ jsx18(CheckIcon, { size: 14 })
|
|
1534
|
+
] })
|
|
1535
|
+
}
|
|
1536
|
+
);
|
|
1537
|
+
}
|
|
1538
|
+
|
|
1539
|
+
// src/components/Dialog.tsx
|
|
1540
|
+
import {
|
|
1541
|
+
Dialog as RACDialog,
|
|
1542
|
+
DialogTrigger as RACDialogTrigger,
|
|
1543
|
+
Heading,
|
|
1544
|
+
Modal as RACModal,
|
|
1545
|
+
ModalOverlay
|
|
1546
|
+
} from "react-aria-components";
|
|
1547
|
+
import { Fragment as Fragment8, jsx as jsx19, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
1548
|
+
function DialogTrigger(props) {
|
|
1549
|
+
return /* @__PURE__ */ jsx19(RACDialogTrigger, { ...props });
|
|
1550
|
+
}
|
|
1551
|
+
var overlayMotion = cn(
|
|
1552
|
+
"fixed inset-0 z-50 bg-black/35 backdrop-blur-[2px]",
|
|
1553
|
+
"transition-opacity duration-200 ease-out entering:opacity-0 exiting:opacity-0 exiting:duration-150"
|
|
1554
|
+
);
|
|
1555
|
+
function Modal({ size = "md", className, children, ...props }) {
|
|
1556
|
+
return /* @__PURE__ */ jsx19(ModalOverlay, { ...props, className: overlayMotion, children: /* @__PURE__ */ jsx19("div", { className: "flex min-h-full items-center justify-center p-4", children: /* @__PURE__ */ jsx19(
|
|
1557
|
+
RACModal,
|
|
1558
|
+
{
|
|
1559
|
+
className: composeTw(
|
|
1560
|
+
className,
|
|
1561
|
+
cn(
|
|
1562
|
+
"w-full rounded-[16px] border border-line-subtle bg-bg text-fg shadow-float outline-none",
|
|
1563
|
+
"transition-[opacity,transform] duration-250 ease-out",
|
|
1564
|
+
"entering:translate-y-2 entering:scale-[0.97] entering:opacity-0",
|
|
1565
|
+
"exiting:scale-[0.98] exiting:opacity-0 exiting:duration-150",
|
|
1566
|
+
"motion-reduce:entering:translate-y-0 motion-reduce:entering:scale-100 motion-reduce:exiting:scale-100",
|
|
1567
|
+
size === "sm" ? "max-w-sm" : size === "lg" ? "max-w-2xl" : "max-w-md"
|
|
1568
|
+
)
|
|
1569
|
+
),
|
|
1570
|
+
children
|
|
1571
|
+
}
|
|
1572
|
+
) }) });
|
|
1573
|
+
}
|
|
1574
|
+
function Dialog({ title, description, children, footer, showClose = true, className, ...props }) {
|
|
1575
|
+
return /* @__PURE__ */ jsx19(RACDialog, { ...props, className: cn("relative flex flex-col gap-4 p-6 outline-none", className), children: (rp) => /* @__PURE__ */ jsxs15(Fragment8, { children: [
|
|
1576
|
+
(title || description) && /* @__PURE__ */ jsxs15("div", { className: "flex flex-col gap-1.5 pr-8", children: [
|
|
1577
|
+
title && /* @__PURE__ */ jsx19(Heading, { slot: "title", className: "text-lg font-semibold leading-tight text-fg", children: title }),
|
|
1578
|
+
description && /* @__PURE__ */ jsx19("p", { className: "text-[15px] leading-relaxed text-fg-muted", children: description })
|
|
1579
|
+
] }),
|
|
1580
|
+
typeof children === "function" ? children(rp) : children,
|
|
1581
|
+
footer && /* @__PURE__ */ jsx19("div", { className: "mt-2 flex flex-wrap justify-end gap-2", children: footer(rp.close) }),
|
|
1582
|
+
showClose && /* @__PURE__ */ jsx19(
|
|
1583
|
+
Button,
|
|
1584
|
+
{
|
|
1585
|
+
variant: "ghost",
|
|
1586
|
+
size: "xs",
|
|
1587
|
+
isIconOnly: true,
|
|
1588
|
+
"aria-label": "Close",
|
|
1589
|
+
onPress: rp.close,
|
|
1590
|
+
className: "absolute top-4 right-4",
|
|
1591
|
+
children: /* @__PURE__ */ jsx19(XIcon, { size: 16 })
|
|
1592
|
+
}
|
|
1593
|
+
)
|
|
1594
|
+
] }) });
|
|
1595
|
+
}
|
|
1596
|
+
function AlertDialog({
|
|
1597
|
+
confirmLabel = "Continue",
|
|
1598
|
+
cancelLabel = "Cancel",
|
|
1599
|
+
variant = "primary",
|
|
1600
|
+
onConfirm,
|
|
1601
|
+
...props
|
|
1602
|
+
}) {
|
|
1603
|
+
return /* @__PURE__ */ jsx19(
|
|
1604
|
+
Dialog,
|
|
1605
|
+
{
|
|
1606
|
+
role: "alertdialog",
|
|
1607
|
+
showClose: false,
|
|
1608
|
+
...props,
|
|
1609
|
+
footer: (close) => /* @__PURE__ */ jsxs15(Fragment8, { children: [
|
|
1610
|
+
/* @__PURE__ */ jsx19(Button, { size: "sm", variant: "secondary", onPress: close, children: cancelLabel }),
|
|
1611
|
+
/* @__PURE__ */ jsx19(
|
|
1612
|
+
Button,
|
|
1613
|
+
{
|
|
1614
|
+
size: "sm",
|
|
1615
|
+
variant,
|
|
1616
|
+
autoFocus: true,
|
|
1617
|
+
onPress: async () => {
|
|
1618
|
+
await onConfirm?.();
|
|
1619
|
+
close();
|
|
1620
|
+
},
|
|
1621
|
+
children: confirmLabel
|
|
1622
|
+
}
|
|
1623
|
+
)
|
|
1624
|
+
] })
|
|
1625
|
+
}
|
|
1626
|
+
);
|
|
1627
|
+
}
|
|
1628
|
+
function Drawer({ side = "right", className, children, ...props }) {
|
|
1629
|
+
const pos = side === "bottom" ? "inset-x-0 bottom-0 max-h-[85vh] rounded-t-[20px] entering:translate-y-full exiting:translate-y-full" : side === "left" ? "inset-y-0 left-0 w-[min(24rem,90vw)] entering:-translate-x-full exiting:-translate-x-full" : "inset-y-0 right-0 w-[min(24rem,90vw)] entering:translate-x-full exiting:translate-x-full";
|
|
1630
|
+
return /* @__PURE__ */ jsx19(ModalOverlay, { ...props, className: overlayMotion, children: /* @__PURE__ */ jsxs15(
|
|
1631
|
+
RACModal,
|
|
1632
|
+
{
|
|
1633
|
+
className: composeTw(
|
|
1634
|
+
className,
|
|
1635
|
+
cn(
|
|
1636
|
+
"fixed flex flex-col overflow-auto border-line-subtle bg-bg text-fg shadow-float outline-none",
|
|
1637
|
+
"transition-transform duration-400 ease-drawer exiting:duration-250",
|
|
1638
|
+
"motion-reduce:transition-opacity motion-reduce:entering:translate-0 motion-reduce:exiting:translate-0 motion-reduce:entering:opacity-0",
|
|
1639
|
+
side === "bottom" ? "border-t" : side === "left" ? "border-r" : "border-l",
|
|
1640
|
+
pos
|
|
1641
|
+
)
|
|
1642
|
+
),
|
|
1643
|
+
children: [
|
|
1644
|
+
side === "bottom" && /* @__PURE__ */ jsx19("div", { "aria-hidden": true, className: "mx-auto mt-2.5 h-1 w-10 shrink-0 rounded-full bg-line" }),
|
|
1645
|
+
children
|
|
1646
|
+
]
|
|
1647
|
+
}
|
|
1648
|
+
) });
|
|
1649
|
+
}
|
|
1650
|
+
|
|
1651
|
+
// src/components/Tabs.tsx
|
|
1652
|
+
import {
|
|
1653
|
+
SelectionIndicator as SelectionIndicator2,
|
|
1654
|
+
Tab as RACTab,
|
|
1655
|
+
TabList as RACTabList,
|
|
1656
|
+
TabPanel as RACTabPanel,
|
|
1657
|
+
Tabs as RACTabs
|
|
1658
|
+
} from "react-aria-components";
|
|
1659
|
+
import { jsx as jsx20, jsxs as jsxs16 } from "react/jsx-runtime";
|
|
1660
|
+
function Tabs({ className, ...props }) {
|
|
1661
|
+
return /* @__PURE__ */ jsx20(RACTabs, { ...props, className: composeTw(className, "flex gap-4 orientation-horizontal:flex-col") });
|
|
1662
|
+
}
|
|
1663
|
+
function TabList({ variant = "pill", className, ...props }) {
|
|
1664
|
+
return /* @__PURE__ */ jsx20(
|
|
1665
|
+
RACTabList,
|
|
1666
|
+
{
|
|
1667
|
+
...props,
|
|
1668
|
+
"data-variant": variant,
|
|
1669
|
+
className: composeTw(
|
|
1670
|
+
className,
|
|
1671
|
+
cn(
|
|
1672
|
+
"group/tablist relative flex",
|
|
1673
|
+
variant === "pill" ? "w-fit gap-0.5 rounded-[10px] border border-line bg-surface p-[3px] shadow-control" : "gap-5 border-b border-line-subtle"
|
|
1674
|
+
)
|
|
1675
|
+
)
|
|
1676
|
+
}
|
|
1677
|
+
);
|
|
1678
|
+
}
|
|
1679
|
+
function Tab({ children, className, ...props }) {
|
|
1680
|
+
return /* @__PURE__ */ jsxs16(
|
|
1681
|
+
RACTab,
|
|
1682
|
+
{
|
|
1683
|
+
...props,
|
|
1684
|
+
className: composeTw(
|
|
1685
|
+
className,
|
|
1686
|
+
cn(
|
|
1687
|
+
"group relative flex cursor-default items-center justify-center gap-2 text-[15px] whitespace-nowrap outline-none select-none",
|
|
1688
|
+
"text-fg-muted transition-colors duration-200 ease-out hover:text-fg selected:text-fg disabled:opacity-40",
|
|
1689
|
+
"focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-focus",
|
|
1690
|
+
"group-data-[variant=pill]/tablist:h-9 group-data-[variant=pill]/tablist:rounded-[7px] group-data-[variant=pill]/tablist:px-3.5",
|
|
1691
|
+
"group-data-[variant=underline]/tablist:h-10 group-data-[variant=underline]/tablist:px-0.5"
|
|
1692
|
+
)
|
|
1693
|
+
),
|
|
1694
|
+
children: [
|
|
1695
|
+
/* @__PURE__ */ jsx20(
|
|
1696
|
+
SelectionIndicator2,
|
|
1697
|
+
{
|
|
1698
|
+
className: cn(
|
|
1699
|
+
"absolute -z-0 transition-[translate,width,height] duration-250 ease-out motion-reduce:transition-none",
|
|
1700
|
+
"group-data-[variant=pill]/tablist:inset-0 group-data-[variant=pill]/tablist:rounded-[7px] group-data-[variant=pill]/tablist:bg-bg group-data-[variant=pill]/tablist:shadow-control",
|
|
1701
|
+
"group-data-[variant=underline]/tablist:inset-x-0 group-data-[variant=underline]/tablist:-bottom-px group-data-[variant=underline]/tablist:h-0.5 group-data-[variant=underline]/tablist:bg-primary"
|
|
1702
|
+
)
|
|
1703
|
+
}
|
|
1704
|
+
),
|
|
1705
|
+
/* @__PURE__ */ jsx20("span", { className: "relative z-10 inline-flex items-center gap-2", children })
|
|
1706
|
+
]
|
|
1707
|
+
}
|
|
1708
|
+
);
|
|
1709
|
+
}
|
|
1710
|
+
function TabPanel({ className, ...props }) {
|
|
1711
|
+
return /* @__PURE__ */ jsx20(
|
|
1712
|
+
RACTabPanel,
|
|
1713
|
+
{
|
|
1714
|
+
...props,
|
|
1715
|
+
className: composeTw(
|
|
1716
|
+
className,
|
|
1717
|
+
"rounded-control outline-none focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-focus"
|
|
1718
|
+
)
|
|
1719
|
+
}
|
|
1720
|
+
);
|
|
1721
|
+
}
|
|
1722
|
+
|
|
1723
|
+
// src/components/Progress.tsx
|
|
1724
|
+
import {
|
|
1725
|
+
Meter as RACMeter,
|
|
1726
|
+
ProgressBar as RACProgressBar
|
|
1727
|
+
} from "react-aria-components";
|
|
1728
|
+
import { Fragment as Fragment9, jsx as jsx21, jsxs as jsxs17 } from "react/jsx-runtime";
|
|
1729
|
+
function ProgressBar({ label, showValue = !!label, className, ...props }) {
|
|
1730
|
+
return /* @__PURE__ */ jsx21(RACProgressBar, { ...props, className: composeTw(className, "flex w-full flex-col gap-2"), children: ({ percentage, valueText, isIndeterminate }) => /* @__PURE__ */ jsxs17(Fragment9, { children: [
|
|
1731
|
+
(label || showValue) && /* @__PURE__ */ jsxs17("div", { className: "flex justify-between gap-4", children: [
|
|
1732
|
+
label && /* @__PURE__ */ jsx21(Label, { children: label }),
|
|
1733
|
+
showValue && !isIndeterminate && /* @__PURE__ */ jsx21("span", { className: "text-sm tabular-nums text-fg-muted", children: valueText })
|
|
1734
|
+
] }),
|
|
1735
|
+
/* @__PURE__ */ jsx21("div", { className: "relative h-2 w-full overflow-hidden rounded-full bg-surface shadow-hairline", children: isIndeterminate ? /* @__PURE__ */ jsx21("div", { className: "absolute inset-y-0 left-0 w-full origin-left animate-krts-indeterminate rounded-full bg-primary" }) : /* @__PURE__ */ jsx21(
|
|
1736
|
+
"div",
|
|
1737
|
+
{
|
|
1738
|
+
className: "absolute inset-y-0 left-0 w-full origin-left rounded-full bg-primary transition-transform duration-300 ease-linear",
|
|
1739
|
+
style: { transform: `scaleX(${(percentage ?? 0) / 100})` }
|
|
1740
|
+
}
|
|
1741
|
+
) })
|
|
1742
|
+
] }) });
|
|
1743
|
+
}
|
|
1744
|
+
function Meter({ label, thresholds = { warning: 0.7, danger: 0.9 }, className, ...props }) {
|
|
1745
|
+
return /* @__PURE__ */ jsx21(RACMeter, { ...props, className: composeTw(className, "flex w-full flex-col gap-2"), children: ({ percentage, valueText }) => {
|
|
1746
|
+
const f = percentage / 100;
|
|
1747
|
+
const tone = f >= thresholds.danger ? "bg-danger" : f >= thresholds.warning ? "bg-warning" : "bg-success";
|
|
1748
|
+
return /* @__PURE__ */ jsxs17(Fragment9, { children: [
|
|
1749
|
+
/* @__PURE__ */ jsxs17("div", { className: "flex justify-between gap-4", children: [
|
|
1750
|
+
label && /* @__PURE__ */ jsx21(Label, { children: label }),
|
|
1751
|
+
/* @__PURE__ */ jsx21("span", { className: "text-sm tabular-nums text-fg-muted", children: valueText })
|
|
1752
|
+
] }),
|
|
1753
|
+
/* @__PURE__ */ jsx21("div", { className: "relative h-2 w-full overflow-hidden rounded-full bg-surface shadow-hairline", children: /* @__PURE__ */ jsx21(
|
|
1754
|
+
"div",
|
|
1755
|
+
{
|
|
1756
|
+
className: cn("absolute inset-y-0 left-0 w-full origin-left rounded-full transition-[transform,background-color] duration-300 ease-out", tone),
|
|
1757
|
+
style: { transform: `scaleX(${f})` }
|
|
1758
|
+
}
|
|
1759
|
+
) })
|
|
1760
|
+
] });
|
|
1761
|
+
} });
|
|
1762
|
+
}
|
|
1763
|
+
|
|
1764
|
+
// src/components/Disclosure.tsx
|
|
1765
|
+
import {
|
|
1766
|
+
Button as RACButton5,
|
|
1767
|
+
Disclosure as RACDisclosure,
|
|
1768
|
+
DisclosureGroup,
|
|
1769
|
+
DisclosurePanel,
|
|
1770
|
+
Heading as Heading2
|
|
1771
|
+
} from "react-aria-components";
|
|
1772
|
+
import { jsx as jsx22, jsxs as jsxs18 } from "react/jsx-runtime";
|
|
1773
|
+
function Disclosure({ title, children, className, ...props }) {
|
|
1774
|
+
return /* @__PURE__ */ jsxs18(RACDisclosure, { ...props, className: composeTw(className, "group border-b border-line-subtle"), children: [
|
|
1775
|
+
/* @__PURE__ */ jsx22(Heading2, { className: "m-0", children: /* @__PURE__ */ jsxs18(
|
|
1776
|
+
RACButton5,
|
|
1777
|
+
{
|
|
1778
|
+
slot: "trigger",
|
|
1779
|
+
className: cn(
|
|
1780
|
+
"flex w-full items-center justify-between gap-4 py-3.5 text-left text-[15px] font-medium text-fg outline-none",
|
|
1781
|
+
"rounded-sm focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-focus"
|
|
1782
|
+
),
|
|
1783
|
+
children: [
|
|
1784
|
+
title,
|
|
1785
|
+
/* @__PURE__ */ jsx22(ChevronDownIcon, { size: 18, className: "shrink-0 text-fg-muted transition-transform duration-250 ease-out group-expanded:rotate-180" })
|
|
1786
|
+
]
|
|
1787
|
+
}
|
|
1788
|
+
) }),
|
|
1789
|
+
/* @__PURE__ */ jsx22(
|
|
1790
|
+
DisclosurePanel,
|
|
1791
|
+
{
|
|
1792
|
+
className: cn(
|
|
1793
|
+
"h-(--disclosure-panel-height) overflow-clip motion-safe:transition-[height] duration-250 ease-out"
|
|
1794
|
+
),
|
|
1795
|
+
children: /* @__PURE__ */ jsx22("div", { className: "pb-4 text-[15px] leading-relaxed text-fg-muted opacity-0 transition-opacity duration-200 ease-out group-expanded:opacity-100", children })
|
|
1796
|
+
}
|
|
1797
|
+
)
|
|
1798
|
+
] });
|
|
1799
|
+
}
|
|
1800
|
+
function Accordion({ className, ...props }) {
|
|
1801
|
+
return /* @__PURE__ */ jsx22(DisclosureGroup, { ...props, className: composeTw(className, "flex flex-col border-t border-line-subtle") });
|
|
1802
|
+
}
|
|
1803
|
+
|
|
1804
|
+
// src/components/Toast.tsx
|
|
1805
|
+
import { useEffect as useEffect6, useLayoutEffect as useLayoutEffect3, useRef as useRef7, useState as useState6, useSyncExternalStore } from "react";
|
|
1806
|
+
import { jsx as jsx23, jsxs as jsxs19 } from "react/jsx-runtime";
|
|
1807
|
+
var toasts = [];
|
|
1808
|
+
var counter = 0;
|
|
1809
|
+
var listeners = /* @__PURE__ */ new Set();
|
|
1810
|
+
var emit = () => listeners.forEach((l) => l());
|
|
1811
|
+
var subscribe = (l) => {
|
|
1812
|
+
listeners.add(l);
|
|
1813
|
+
return () => listeners.delete(l);
|
|
1814
|
+
};
|
|
1815
|
+
function upsert(title, type, opts = {}) {
|
|
1816
|
+
const id = opts.id ?? ++counter;
|
|
1817
|
+
const existing = toasts.find((t) => t.id === id);
|
|
1818
|
+
if (existing) {
|
|
1819
|
+
toasts = toasts.map((t) => t.id === id ? { ...t, ...opts, title, type, createdAt: Date.now(), removing: false } : t);
|
|
1820
|
+
} else {
|
|
1821
|
+
toasts = [...toasts, { ...opts, id, title, type, createdAt: Date.now() }];
|
|
1822
|
+
}
|
|
1823
|
+
emit();
|
|
1824
|
+
return id;
|
|
1825
|
+
}
|
|
1826
|
+
function dismiss(id) {
|
|
1827
|
+
toasts = toasts.map((t) => id === void 0 || t.id === id ? { ...t, removing: true } : t);
|
|
1828
|
+
emit();
|
|
1829
|
+
}
|
|
1830
|
+
function remove(id) {
|
|
1831
|
+
const t = toasts.find((x) => x.id === id);
|
|
1832
|
+
toasts = toasts.filter((x) => x.id !== id);
|
|
1833
|
+
t?.onDismiss?.();
|
|
1834
|
+
emit();
|
|
1835
|
+
}
|
|
1836
|
+
var toast = Object.assign((title, opts) => upsert(title, "default", opts), {
|
|
1837
|
+
success: (title, opts) => upsert(title, "success", opts),
|
|
1838
|
+
error: (title, opts) => upsert(title, "error", opts),
|
|
1839
|
+
loading: (title, opts) => upsert(title, "loading", opts),
|
|
1840
|
+
dismiss,
|
|
1841
|
+
promise(promise, msgs, opts) {
|
|
1842
|
+
const id = upsert(msgs.loading, "loading", opts);
|
|
1843
|
+
promise.then(
|
|
1844
|
+
(v) => upsert(typeof msgs.success === "function" ? msgs.success(v) : msgs.success, "success", { ...opts, id }),
|
|
1845
|
+
(e) => upsert(typeof msgs.error === "function" ? msgs.error(e) : msgs.error, "error", { ...opts, id })
|
|
1846
|
+
);
|
|
1847
|
+
return promise;
|
|
1848
|
+
}
|
|
1849
|
+
});
|
|
1850
|
+
var GAP = 10;
|
|
1851
|
+
function Toaster({ position = "bottom-right", visibleToasts = 3, expand = false }) {
|
|
1852
|
+
const list = useSyncExternalStore(subscribe, () => toasts, () => toasts);
|
|
1853
|
+
const [hovered, setHovered] = useState6(false);
|
|
1854
|
+
const [heights, setHeights] = useState6({});
|
|
1855
|
+
const isTop = position.startsWith("top");
|
|
1856
|
+
const expanded = expand || hovered;
|
|
1857
|
+
const [docHidden, setDocHidden] = useState6(false);
|
|
1858
|
+
useEffect6(() => {
|
|
1859
|
+
const on = () => setDocHidden(document.hidden);
|
|
1860
|
+
document.addEventListener("visibilitychange", on);
|
|
1861
|
+
return () => document.removeEventListener("visibilitychange", on);
|
|
1862
|
+
}, []);
|
|
1863
|
+
useEffect6(() => {
|
|
1864
|
+
if (list.length === 0) setHovered(false);
|
|
1865
|
+
}, [list.length]);
|
|
1866
|
+
const ordered = [...list].reverse();
|
|
1867
|
+
const visible = ordered.filter((t) => !t.removing);
|
|
1868
|
+
const frontHeight = heights[String(visible[0]?.id)] ?? 56;
|
|
1869
|
+
return /* @__PURE__ */ jsx23(
|
|
1870
|
+
"section",
|
|
1871
|
+
{
|
|
1872
|
+
"aria-label": "Notifications",
|
|
1873
|
+
tabIndex: -1,
|
|
1874
|
+
className: cn(
|
|
1875
|
+
"pointer-events-none fixed z-[100] w-[min(360px,calc(100vw-32px))]",
|
|
1876
|
+
isTop ? "top-4" : "bottom-4",
|
|
1877
|
+
position.endsWith("right") ? "right-4" : position.endsWith("left") ? "left-4" : "left-1/2 -translate-x-1/2"
|
|
1878
|
+
),
|
|
1879
|
+
children: /* @__PURE__ */ jsx23(
|
|
1880
|
+
"ol",
|
|
1881
|
+
{
|
|
1882
|
+
onMouseEnter: () => setHovered(true),
|
|
1883
|
+
onMouseLeave: () => setHovered(false),
|
|
1884
|
+
className: "pointer-events-auto relative",
|
|
1885
|
+
style: {
|
|
1886
|
+
height: expanded ? visible.reduce((sum, t) => sum + (heights[String(t.id)] ?? 56) + GAP, 0) : list.length ? frontHeight : 0
|
|
1887
|
+
},
|
|
1888
|
+
children: ordered.map((t) => {
|
|
1889
|
+
const index = visible.indexOf(t);
|
|
1890
|
+
const offset = expanded ? visible.slice(0, Math.max(0, index)).reduce((sum, x) => sum + (heights[String(x.id)] ?? 56) + GAP, 0) : Math.max(0, index) * 12;
|
|
1891
|
+
return /* @__PURE__ */ jsx23(
|
|
1892
|
+
ToastItem,
|
|
1893
|
+
{
|
|
1894
|
+
toast: t,
|
|
1895
|
+
index,
|
|
1896
|
+
isTop,
|
|
1897
|
+
expanded,
|
|
1898
|
+
offset,
|
|
1899
|
+
hiddenInStack: index >= visibleToasts,
|
|
1900
|
+
collapsedHeight: frontHeight,
|
|
1901
|
+
paused: hovered || docHidden,
|
|
1902
|
+
onHeight: (h) => setHeights((p) => p[String(t.id)] === h ? p : { ...p, [String(t.id)]: h })
|
|
1903
|
+
},
|
|
1904
|
+
t.id
|
|
1905
|
+
);
|
|
1906
|
+
})
|
|
1907
|
+
}
|
|
1908
|
+
)
|
|
1909
|
+
}
|
|
1910
|
+
);
|
|
1911
|
+
}
|
|
1912
|
+
var typeIcon = {
|
|
1913
|
+
default: null,
|
|
1914
|
+
success: /* @__PURE__ */ jsx23("span", { className: "grid size-5 place-items-center rounded-full bg-success text-white", children: /* @__PURE__ */ jsx23(CheckIcon, { size: 12 }) }),
|
|
1915
|
+
error: /* @__PURE__ */ jsx23("span", { className: "grid size-5 place-items-center rounded-full bg-danger text-white", children: /* @__PURE__ */ jsx23(XIcon, { size: 12 }) }),
|
|
1916
|
+
loading: /* @__PURE__ */ jsx23(Spinner, { size: 16, className: "text-fg-muted" })
|
|
1917
|
+
};
|
|
1918
|
+
function ToastItem({
|
|
1919
|
+
toast: t,
|
|
1920
|
+
index,
|
|
1921
|
+
isTop,
|
|
1922
|
+
expanded,
|
|
1923
|
+
offset,
|
|
1924
|
+
hiddenInStack,
|
|
1925
|
+
collapsedHeight,
|
|
1926
|
+
paused,
|
|
1927
|
+
onHeight
|
|
1928
|
+
}) {
|
|
1929
|
+
const ref = useRef7(null);
|
|
1930
|
+
const [mounted, setMounted] = useState6(false);
|
|
1931
|
+
const drag = useRef7(null);
|
|
1932
|
+
const removing = !!t.removing;
|
|
1933
|
+
useLayoutEffect3(() => {
|
|
1934
|
+
const el = ref.current;
|
|
1935
|
+
if (!el) return;
|
|
1936
|
+
const prev = el.style.height;
|
|
1937
|
+
el.style.height = "auto";
|
|
1938
|
+
onHeight(el.getBoundingClientRect().height);
|
|
1939
|
+
el.style.height = prev;
|
|
1940
|
+
}, [t.title, t.description, t.action, t.type]);
|
|
1941
|
+
useEffect6(() => {
|
|
1942
|
+
const r = requestAnimationFrame(() => setMounted(true));
|
|
1943
|
+
return () => cancelAnimationFrame(r);
|
|
1944
|
+
}, []);
|
|
1945
|
+
const remaining = useRef7(t.duration ?? (t.type === "loading" ? Infinity : 4e3));
|
|
1946
|
+
useEffect6(() => {
|
|
1947
|
+
remaining.current = t.duration ?? (t.type === "loading" ? Infinity : 4e3);
|
|
1948
|
+
}, [t.createdAt, t.duration, t.type]);
|
|
1949
|
+
useEffect6(() => {
|
|
1950
|
+
if (removing || paused || !Number.isFinite(remaining.current)) return;
|
|
1951
|
+
const start = Date.now();
|
|
1952
|
+
const timer = setTimeout(() => dismiss(t.id), remaining.current);
|
|
1953
|
+
return () => {
|
|
1954
|
+
clearTimeout(timer);
|
|
1955
|
+
remaining.current -= Date.now() - start;
|
|
1956
|
+
};
|
|
1957
|
+
}, [paused, removing, t.id, t.createdAt]);
|
|
1958
|
+
useEffect6(() => {
|
|
1959
|
+
if (!removing) return;
|
|
1960
|
+
const timer = setTimeout(() => remove(t.id), 200);
|
|
1961
|
+
return () => clearTimeout(timer);
|
|
1962
|
+
}, [removing, t.id]);
|
|
1963
|
+
const dir = isTop ? 1 : -1;
|
|
1964
|
+
const visible = mounted && !removing && !hiddenInStack;
|
|
1965
|
+
const scale = expanded || index <= 0 ? 1 : Math.max(0.8, 1 - index * 0.05);
|
|
1966
|
+
const transform = `translateY(${!mounted || removing ? `${isTop ? -100 : 100}%` : `${dir * offset}px`}) scale(${scale})`;
|
|
1967
|
+
return /* @__PURE__ */ jsxs19(
|
|
1968
|
+
"li",
|
|
1969
|
+
{
|
|
1970
|
+
ref,
|
|
1971
|
+
role: t.type === "error" ? "alert" : "status",
|
|
1972
|
+
"aria-live": t.type === "error" ? "assertive" : "polite",
|
|
1973
|
+
"aria-atomic": true,
|
|
1974
|
+
tabIndex: 0,
|
|
1975
|
+
"data-front": index === 0 || void 0,
|
|
1976
|
+
onKeyDown: (e) => {
|
|
1977
|
+
if (e.key === "Escape") dismiss(t.id);
|
|
1978
|
+
},
|
|
1979
|
+
onPointerDown: (e) => {
|
|
1980
|
+
if (e.target.closest("button")) return;
|
|
1981
|
+
if (drag.current) return;
|
|
1982
|
+
drag.current = { x: e.clientX, y: e.clientY, t: Date.now(), id: e.pointerId };
|
|
1983
|
+
e.currentTarget.setPointerCapture(e.pointerId);
|
|
1984
|
+
},
|
|
1985
|
+
onPointerMove: (e) => {
|
|
1986
|
+
const d = drag.current;
|
|
1987
|
+
if (!d || d.id !== e.pointerId || !ref.current) return;
|
|
1988
|
+
const dx = e.clientX - d.x;
|
|
1989
|
+
ref.current.style.transition = "none";
|
|
1990
|
+
ref.current.style.transform = `translateY(${dir * offset}px) translateX(${dx}px)`;
|
|
1991
|
+
ref.current.style.opacity = String(Math.max(0, 1 - Math.abs(dx) / 260));
|
|
1992
|
+
},
|
|
1993
|
+
onPointerUp: (e) => {
|
|
1994
|
+
const d = drag.current;
|
|
1995
|
+
drag.current = null;
|
|
1996
|
+
if (!d || !ref.current) return;
|
|
1997
|
+
const dx = e.clientX - d.x;
|
|
1998
|
+
const velocity = Math.abs(dx) / Math.max(1, Date.now() - d.t);
|
|
1999
|
+
ref.current.style.transition = "";
|
|
2000
|
+
ref.current.style.opacity = "";
|
|
2001
|
+
if (Math.abs(dx) > 80 || velocity > 0.11) {
|
|
2002
|
+
ref.current.style.transform = `translateX(${dx > 0 ? 120 : -120}%)`;
|
|
2003
|
+
ref.current.style.opacity = "0";
|
|
2004
|
+
dismiss(t.id);
|
|
2005
|
+
} else {
|
|
2006
|
+
ref.current.style.transform = "";
|
|
2007
|
+
}
|
|
2008
|
+
},
|
|
2009
|
+
onPointerCancel: () => {
|
|
2010
|
+
drag.current = null;
|
|
2011
|
+
if (ref.current) {
|
|
2012
|
+
ref.current.style.transition = "";
|
|
2013
|
+
ref.current.style.transform = "";
|
|
2014
|
+
ref.current.style.opacity = "";
|
|
2015
|
+
}
|
|
2016
|
+
},
|
|
2017
|
+
className: cn(
|
|
2018
|
+
"absolute inset-x-0 flex touch-none items-start gap-3 overflow-hidden rounded-[12px] border border-line-subtle bg-overlay p-3.5 text-fg shadow-float outline-none select-none",
|
|
2019
|
+
"focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-focus",
|
|
2020
|
+
// Transitions, not keyframes: toasts arrive in bursts and must retarget smoothly.
|
|
2021
|
+
"transition-[transform,opacity,height] duration-400 ease-[ease] motion-reduce:transition-opacity",
|
|
2022
|
+
isTop ? "top-0 origin-bottom" : "bottom-0 origin-top"
|
|
2023
|
+
),
|
|
2024
|
+
style: {
|
|
2025
|
+
transform,
|
|
2026
|
+
opacity: visible ? 1 : 0,
|
|
2027
|
+
zIndex: 100 - Math.max(0, index),
|
|
2028
|
+
height: !expanded && index > 0 ? collapsedHeight : void 0,
|
|
2029
|
+
pointerEvents: hiddenInStack ? "none" : void 0
|
|
2030
|
+
},
|
|
2031
|
+
children: [
|
|
2032
|
+
typeIcon[t.type] && /* @__PURE__ */ jsx23("span", { className: "mt-px shrink-0", children: typeIcon[t.type] }),
|
|
2033
|
+
/* @__PURE__ */ jsxs19(
|
|
2034
|
+
"div",
|
|
2035
|
+
{
|
|
2036
|
+
className: cn(
|
|
2037
|
+
"flex min-w-0 flex-1 flex-col gap-0.5 transition-opacity duration-200",
|
|
2038
|
+
!expanded && index > 0 && "opacity-0"
|
|
2039
|
+
),
|
|
2040
|
+
children: [
|
|
2041
|
+
/* @__PURE__ */ jsx23("div", { className: "text-[14.5px] font-medium leading-snug", children: t.title }),
|
|
2042
|
+
t.description && /* @__PURE__ */ jsx23("div", { className: "text-[13.5px] leading-snug text-fg-muted", children: t.description })
|
|
2043
|
+
]
|
|
2044
|
+
}
|
|
2045
|
+
),
|
|
2046
|
+
t.action && /* @__PURE__ */ jsx23(
|
|
2047
|
+
"button",
|
|
2048
|
+
{
|
|
2049
|
+
type: "button",
|
|
2050
|
+
onClick: () => {
|
|
2051
|
+
t.action.onPress();
|
|
2052
|
+
dismiss(t.id);
|
|
2053
|
+
},
|
|
2054
|
+
className: "h-7 shrink-0 rounded-md bg-primary px-2.5 text-[13px] font-medium text-primary-fg transition-opacity duration-150 ease-out outline-none hover:opacity-90 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-focus",
|
|
2055
|
+
children: t.action.label
|
|
2056
|
+
}
|
|
2057
|
+
),
|
|
2058
|
+
/* @__PURE__ */ jsx23(
|
|
2059
|
+
"button",
|
|
2060
|
+
{
|
|
2061
|
+
type: "button",
|
|
2062
|
+
"aria-label": "Dismiss notification",
|
|
2063
|
+
onClick: () => dismiss(t.id),
|
|
2064
|
+
className: "-mt-0.5 -mr-1 grid size-6 shrink-0 place-items-center rounded-md text-fg-subtle outline-none transition-colors duration-150 hover:bg-surface-hover hover:text-fg focus-visible:outline-2 focus-visible:outline-focus",
|
|
2065
|
+
children: /* @__PURE__ */ jsx23(XIcon, { size: 13 })
|
|
2066
|
+
}
|
|
2067
|
+
)
|
|
2068
|
+
]
|
|
2069
|
+
}
|
|
2070
|
+
);
|
|
2071
|
+
}
|
|
2072
|
+
|
|
2073
|
+
// src/components/Display.tsx
|
|
2074
|
+
import { forwardRef as forwardRef5, useState as useState7 } from "react";
|
|
2075
|
+
import { Link as RACLink, Separator as RACSeparator } from "react-aria-components";
|
|
2076
|
+
import { jsx as jsx24, jsxs as jsxs20 } from "react/jsx-runtime";
|
|
2077
|
+
function Skeleton({ circle, className, ...props }) {
|
|
2078
|
+
return /* @__PURE__ */ jsx24(
|
|
2079
|
+
"div",
|
|
2080
|
+
{
|
|
2081
|
+
"aria-hidden": true,
|
|
2082
|
+
...props,
|
|
2083
|
+
className: cn(
|
|
2084
|
+
"relative isolate overflow-hidden bg-surface-hover",
|
|
2085
|
+
circle ? "rounded-full" : "rounded-md",
|
|
2086
|
+
"after:absolute after:inset-0 after:-translate-x-full after:animate-krts-shimmer after:bg-gradient-to-r after:from-transparent after:via-bg/60 after:to-transparent",
|
|
2087
|
+
"motion-reduce:after:hidden",
|
|
2088
|
+
className
|
|
2089
|
+
)
|
|
2090
|
+
}
|
|
2091
|
+
);
|
|
2092
|
+
}
|
|
2093
|
+
var badgeTones = {
|
|
2094
|
+
neutral: "bg-surface text-fg border-line",
|
|
2095
|
+
solid: "bg-primary text-primary-fg border-primary-line",
|
|
2096
|
+
danger: "bg-danger-soft text-danger border-danger/25",
|
|
2097
|
+
success: "bg-success/10 text-success border-success/25",
|
|
2098
|
+
warning: "bg-warning/10 text-warning border-warning/25"
|
|
2099
|
+
};
|
|
2100
|
+
function Badge({ tone = "neutral", dot, className, children, ...props }) {
|
|
2101
|
+
return /* @__PURE__ */ jsxs20(
|
|
2102
|
+
"span",
|
|
2103
|
+
{
|
|
2104
|
+
...props,
|
|
2105
|
+
className: cn(
|
|
2106
|
+
"inline-flex h-6 items-center gap-1.5 rounded-full border px-2.5 text-[12.5px] font-medium leading-none whitespace-nowrap",
|
|
2107
|
+
badgeTones[tone],
|
|
2108
|
+
className
|
|
2109
|
+
),
|
|
2110
|
+
children: [
|
|
2111
|
+
dot && /* @__PURE__ */ jsx24("span", { "aria-hidden": true, className: "size-1.5 rounded-full bg-current" }),
|
|
2112
|
+
children
|
|
2113
|
+
]
|
|
2114
|
+
}
|
|
2115
|
+
);
|
|
2116
|
+
}
|
|
2117
|
+
function Kbd({ className, ...props }) {
|
|
2118
|
+
return /* @__PURE__ */ jsx24(
|
|
2119
|
+
"kbd",
|
|
2120
|
+
{
|
|
2121
|
+
...props,
|
|
2122
|
+
className: cn(
|
|
2123
|
+
"inline-flex h-6 min-w-6 items-center justify-center rounded-md border border-line border-b-2 bg-surface px-1.5 font-sans text-[12px] text-fg-muted",
|
|
2124
|
+
className
|
|
2125
|
+
)
|
|
2126
|
+
}
|
|
2127
|
+
);
|
|
2128
|
+
}
|
|
2129
|
+
function Avatar({ src, alt, name = "", size = 36, className, ...props }) {
|
|
2130
|
+
const [loadedSrc, setLoadedSrc] = useState7(null);
|
|
2131
|
+
const loaded = !!src && loadedSrc === src;
|
|
2132
|
+
const initials = name.split(/\s+/).filter(Boolean).slice(0, 2).map((w) => w[0].toUpperCase()).join("");
|
|
2133
|
+
return /* @__PURE__ */ jsxs20(
|
|
2134
|
+
"span",
|
|
2135
|
+
{
|
|
2136
|
+
...props,
|
|
2137
|
+
role: "img",
|
|
2138
|
+
"aria-label": alt ?? name,
|
|
2139
|
+
className: cn(
|
|
2140
|
+
"relative inline-grid shrink-0 place-items-center overflow-hidden rounded-full border border-line bg-surface font-medium text-fg-muted select-none",
|
|
2141
|
+
className
|
|
2142
|
+
),
|
|
2143
|
+
style: { width: size, height: size, fontSize: size * 0.38, ...props.style },
|
|
2144
|
+
children: [
|
|
2145
|
+
(!src || !loaded) && initials,
|
|
2146
|
+
src && /* @__PURE__ */ jsx24(
|
|
2147
|
+
"img",
|
|
2148
|
+
{
|
|
2149
|
+
src,
|
|
2150
|
+
alt: "",
|
|
2151
|
+
draggable: false,
|
|
2152
|
+
onLoad: () => setLoadedSrc(src),
|
|
2153
|
+
className: cn("absolute inset-0 size-full object-cover transition-opacity duration-300 ease-out", loaded ? "opacity-100" : "opacity-0")
|
|
2154
|
+
}
|
|
2155
|
+
)
|
|
2156
|
+
]
|
|
2157
|
+
}
|
|
2158
|
+
);
|
|
2159
|
+
}
|
|
2160
|
+
var Card = forwardRef5(function Card2({ className, ...props }, ref) {
|
|
2161
|
+
return /* @__PURE__ */ jsx24(
|
|
2162
|
+
"div",
|
|
2163
|
+
{
|
|
2164
|
+
ref,
|
|
2165
|
+
...props,
|
|
2166
|
+
className: cn("rounded-[14px] border border-line-subtle bg-bg p-5 text-fg shadow-control", className)
|
|
2167
|
+
}
|
|
2168
|
+
);
|
|
2169
|
+
});
|
|
2170
|
+
function CardHeader({ title, description, action }) {
|
|
2171
|
+
return /* @__PURE__ */ jsxs20("div", { className: "mb-4 flex items-start justify-between gap-4", children: [
|
|
2172
|
+
/* @__PURE__ */ jsxs20("div", { className: "flex flex-col gap-1", children: [
|
|
2173
|
+
/* @__PURE__ */ jsx24("h3", { className: "text-base font-semibold leading-tight text-fg", children: title }),
|
|
2174
|
+
description && /* @__PURE__ */ jsx24("p", { className: "text-sm text-fg-muted", children: description })
|
|
2175
|
+
] }),
|
|
2176
|
+
action
|
|
2177
|
+
] });
|
|
2178
|
+
}
|
|
2179
|
+
function Separator2({ className, ...props }) {
|
|
2180
|
+
return /* @__PURE__ */ jsx24(
|
|
2181
|
+
RACSeparator,
|
|
2182
|
+
{
|
|
2183
|
+
...props,
|
|
2184
|
+
className: cn(
|
|
2185
|
+
"shrink-0 bg-line-subtle",
|
|
2186
|
+
props.orientation === "vertical" ? "w-px self-stretch" : "h-px w-full",
|
|
2187
|
+
className
|
|
2188
|
+
)
|
|
2189
|
+
}
|
|
2190
|
+
);
|
|
2191
|
+
}
|
|
2192
|
+
function Link({ className, ...props }) {
|
|
2193
|
+
return /* @__PURE__ */ jsx24(
|
|
2194
|
+
RACLink,
|
|
2195
|
+
{
|
|
2196
|
+
...props,
|
|
2197
|
+
className: composeTw(
|
|
2198
|
+
className,
|
|
2199
|
+
cn(
|
|
2200
|
+
"rounded-sm text-fg underline decoration-line-strong underline-offset-[3px] transition-[text-decoration-color] duration-150 ease-out hover:decoration-fg",
|
|
2201
|
+
focusRing
|
|
2202
|
+
)
|
|
2203
|
+
)
|
|
2204
|
+
}
|
|
2205
|
+
);
|
|
2206
|
+
}
|
|
2207
|
+
export {
|
|
2208
|
+
Accordion,
|
|
2209
|
+
AlertDialog,
|
|
2210
|
+
Avatar,
|
|
2211
|
+
Badge,
|
|
2212
|
+
Button,
|
|
2213
|
+
Card,
|
|
2214
|
+
CardHeader,
|
|
2215
|
+
CheckIcon,
|
|
2216
|
+
Checkbox,
|
|
2217
|
+
CheckboxGroup,
|
|
2218
|
+
ChevronDownIcon,
|
|
2219
|
+
ChevronRightIcon,
|
|
2220
|
+
CopyButton,
|
|
2221
|
+
CopyIcon,
|
|
2222
|
+
Description,
|
|
2223
|
+
Dialog,
|
|
2224
|
+
DialogTrigger,
|
|
2225
|
+
Disclosure,
|
|
2226
|
+
Drawer,
|
|
2227
|
+
DropdownButton,
|
|
2228
|
+
FieldError,
|
|
2229
|
+
GestureArea,
|
|
2230
|
+
HoldButton,
|
|
2231
|
+
Input,
|
|
2232
|
+
KRTS_EVENTS,
|
|
2233
|
+
Kbd,
|
|
2234
|
+
KrtsProvider,
|
|
2235
|
+
Label,
|
|
2236
|
+
Link,
|
|
2237
|
+
Menu,
|
|
2238
|
+
MenuItem,
|
|
2239
|
+
MenuSection,
|
|
2240
|
+
MenuSeparator,
|
|
2241
|
+
MenuTrigger,
|
|
2242
|
+
Meter,
|
|
2243
|
+
MinusIcon,
|
|
2244
|
+
Modal,
|
|
2245
|
+
NumberField,
|
|
2246
|
+
PlusIcon,
|
|
2247
|
+
Popover,
|
|
2248
|
+
ProgressBar,
|
|
2249
|
+
Radio,
|
|
2250
|
+
RadioGroup,
|
|
2251
|
+
SearchField,
|
|
2252
|
+
SearchIcon,
|
|
2253
|
+
Segment,
|
|
2254
|
+
SegmentedControl,
|
|
2255
|
+
Select,
|
|
2256
|
+
SelectItem,
|
|
2257
|
+
Separator2 as Separator,
|
|
2258
|
+
Skeleton,
|
|
2259
|
+
Slider,
|
|
2260
|
+
Spinner,
|
|
2261
|
+
SplitButton,
|
|
2262
|
+
SubmenuTrigger,
|
|
2263
|
+
Switch,
|
|
2264
|
+
Tab,
|
|
2265
|
+
TabList,
|
|
2266
|
+
TabPanel,
|
|
2267
|
+
Tabs,
|
|
2268
|
+
TextAreaField,
|
|
2269
|
+
TextField,
|
|
2270
|
+
Toaster,
|
|
2271
|
+
ToggleButton,
|
|
2272
|
+
Tooltip,
|
|
2273
|
+
TooltipTrigger,
|
|
2274
|
+
WithTooltip,
|
|
2275
|
+
XIcon,
|
|
2276
|
+
buttonStyles,
|
|
2277
|
+
cn,
|
|
2278
|
+
composeTw,
|
|
2279
|
+
dispatchKrtsEvent,
|
|
2280
|
+
floatingMotion,
|
|
2281
|
+
floatingSurface,
|
|
2282
|
+
focusRing,
|
|
2283
|
+
inputFrame,
|
|
2284
|
+
toast,
|
|
2285
|
+
useAnimatedWidth,
|
|
2286
|
+
useGesture,
|
|
2287
|
+
useGestureHandlers,
|
|
2288
|
+
useKrtsConfig,
|
|
2289
|
+
usePerceivedProgress,
|
|
2290
|
+
usePrefersReducedMotion,
|
|
2291
|
+
useSmoothPending
|
|
2292
|
+
};
|
|
2293
|
+
//# sourceMappingURL=index.js.map
|