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