@apotech-os/ui-sdk 0.8.0 → 0.10.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/dist/index.cjs +344 -140
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +94 -8
- package/dist/index.d.ts +94 -8
- package/dist/index.js +336 -141
- package/dist/index.js.map +1 -1
- package/package.json +3 -2
package/dist/index.js
CHANGED
|
@@ -2,6 +2,11 @@ import { clsx } from 'clsx';
|
|
|
2
2
|
import { twMerge } from 'tailwind-merge';
|
|
3
3
|
import * as React from 'react';
|
|
4
4
|
import { createContext, useContext, useState, useRef, useCallback, useEffect } from 'react';
|
|
5
|
+
import { Dialog } from '@base-ui/react/dialog';
|
|
6
|
+
import { Menu } from '@base-ui/react/menu';
|
|
7
|
+
import { Popover as Popover$1 } from '@base-ui/react/popover';
|
|
8
|
+
import { Switch as Switch$1 } from '@base-ui/react/switch';
|
|
9
|
+
import { Tabs as Tabs$1 } from '@base-ui/react/tabs';
|
|
5
10
|
import { cva } from 'class-variance-authority';
|
|
6
11
|
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
7
12
|
|
|
@@ -88,6 +93,175 @@ function useAppSettings() {
|
|
|
88
93
|
);
|
|
89
94
|
return { settings, isLoading, error, save, isSaving, saveError };
|
|
90
95
|
}
|
|
96
|
+
var DEFAULT_RUN_POLL_INTERVAL_MS = 1e3;
|
|
97
|
+
var MAX_RUN_POLL_INTERVAL_MS = 5e3;
|
|
98
|
+
var RUN_POLL_BACKOFF = 1.5;
|
|
99
|
+
var RUNS_FOLLOW_POLL_INTERVAL_MS = 3e3;
|
|
100
|
+
function runProgressSignature(run) {
|
|
101
|
+
return `${run.progress ?? ""}|${run.message ?? ""}`;
|
|
102
|
+
}
|
|
103
|
+
function isTerminalRunError(err) {
|
|
104
|
+
const status = extractHttpStatus(err);
|
|
105
|
+
return status === 403 || status === 404;
|
|
106
|
+
}
|
|
107
|
+
function extractHttpStatus(err) {
|
|
108
|
+
if (typeof err !== "object" || err === null) return void 0;
|
|
109
|
+
const direct = err.status;
|
|
110
|
+
if (typeof direct === "number") return direct;
|
|
111
|
+
const response = err.response;
|
|
112
|
+
if (typeof response !== "object" || response === null) return void 0;
|
|
113
|
+
const nested = response.status;
|
|
114
|
+
return typeof nested === "number" ? nested : void 0;
|
|
115
|
+
}
|
|
116
|
+
function useAppRun(runId, opts = {}) {
|
|
117
|
+
const { appSlug } = useAppContext();
|
|
118
|
+
const api = useAppApi();
|
|
119
|
+
const intervalMs = opts.intervalMs ?? DEFAULT_RUN_POLL_INTERVAL_MS;
|
|
120
|
+
const maxIntervalMs = opts.maxIntervalMs ?? MAX_RUN_POLL_INTERVAL_MS;
|
|
121
|
+
const [run, setRun] = useState(void 0);
|
|
122
|
+
const [isPolling, setIsPolling] = useState(false);
|
|
123
|
+
const [error, setError] = useState(void 0);
|
|
124
|
+
const clientRef = useRef(api);
|
|
125
|
+
clientRef.current = api;
|
|
126
|
+
const requestIdRef = useRef(0);
|
|
127
|
+
const isMountedRef = useRef(true);
|
|
128
|
+
const path = runId !== null ? `/data/apps/${appSlug}/runs/${encodeURIComponent(runId)}` : null;
|
|
129
|
+
const fetchOnce = useCallback(async () => {
|
|
130
|
+
if (path === null) return { kind: "no-path" };
|
|
131
|
+
const requestId = ++requestIdRef.current;
|
|
132
|
+
try {
|
|
133
|
+
const loaded = await clientRef.current.get(path);
|
|
134
|
+
if (requestId !== requestIdRef.current || !isMountedRef.current) return { kind: "superseded" };
|
|
135
|
+
setRun(loaded);
|
|
136
|
+
setError(void 0);
|
|
137
|
+
return { kind: "success", run: loaded };
|
|
138
|
+
} catch (err) {
|
|
139
|
+
if (requestId !== requestIdRef.current || !isMountedRef.current) return { kind: "superseded" };
|
|
140
|
+
setError(err);
|
|
141
|
+
return { kind: "error", err };
|
|
142
|
+
}
|
|
143
|
+
}, [path]);
|
|
144
|
+
useEffect(() => {
|
|
145
|
+
isMountedRef.current = true;
|
|
146
|
+
requestIdRef.current += 1;
|
|
147
|
+
setRun(void 0);
|
|
148
|
+
setError(void 0);
|
|
149
|
+
if (path === null) {
|
|
150
|
+
setIsPolling(false);
|
|
151
|
+
return () => {
|
|
152
|
+
isMountedRef.current = false;
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
let timeoutHandle;
|
|
156
|
+
let interval = intervalMs;
|
|
157
|
+
let lastSignature;
|
|
158
|
+
let cancelled = false;
|
|
159
|
+
const tick = async () => {
|
|
160
|
+
const outcome = await fetchOnce();
|
|
161
|
+
if (cancelled || !isMountedRef.current) return;
|
|
162
|
+
if (outcome.kind === "no-path") {
|
|
163
|
+
setIsPolling(false);
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
if (outcome.kind === "superseded") {
|
|
167
|
+
timeoutHandle = setTimeout(() => void tick(), interval);
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
if (outcome.kind === "error") {
|
|
171
|
+
if (isTerminalRunError(outcome.err)) {
|
|
172
|
+
setIsPolling(false);
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
timeoutHandle = setTimeout(() => void tick(), interval);
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
const loaded = outcome.run;
|
|
179
|
+
const signature = runProgressSignature(loaded);
|
|
180
|
+
interval = signature === lastSignature ? Math.min(interval * RUN_POLL_BACKOFF, maxIntervalMs) : intervalMs;
|
|
181
|
+
lastSignature = signature;
|
|
182
|
+
if (loaded.status !== "running") {
|
|
183
|
+
setIsPolling(false);
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
timeoutHandle = setTimeout(() => void tick(), interval);
|
|
187
|
+
};
|
|
188
|
+
setIsPolling(true);
|
|
189
|
+
void tick();
|
|
190
|
+
return () => {
|
|
191
|
+
cancelled = true;
|
|
192
|
+
isMountedRef.current = false;
|
|
193
|
+
if (timeoutHandle) clearTimeout(timeoutHandle);
|
|
194
|
+
};
|
|
195
|
+
}, [path, fetchOnce, intervalMs, maxIntervalMs]);
|
|
196
|
+
const refresh = useCallback(async () => {
|
|
197
|
+
await fetchOnce();
|
|
198
|
+
}, [fetchOnce]);
|
|
199
|
+
return { run, isPolling, error, refresh };
|
|
200
|
+
}
|
|
201
|
+
function useAppRuns(opts) {
|
|
202
|
+
const { appSlug } = useAppContext();
|
|
203
|
+
const api = useAppApi();
|
|
204
|
+
const follow = opts.follow ?? true;
|
|
205
|
+
const [runs, setRuns] = useState([]);
|
|
206
|
+
const [isLoading, setIsLoading] = useState(true);
|
|
207
|
+
const [error, setError] = useState(void 0);
|
|
208
|
+
const clientRef = useRef(api);
|
|
209
|
+
clientRef.current = api;
|
|
210
|
+
const requestIdRef = useRef(0);
|
|
211
|
+
const isMountedRef = useRef(true);
|
|
212
|
+
const params = {};
|
|
213
|
+
if (opts.kind) params["kind"] = opts.kind;
|
|
214
|
+
if (opts.key) params["key"] = opts.key;
|
|
215
|
+
if (opts.status?.length) params["status"] = opts.status.join(",");
|
|
216
|
+
if (opts.limit) params["limit"] = String(opts.limit);
|
|
217
|
+
const paramsKey = JSON.stringify(params);
|
|
218
|
+
const path = `/data/apps/${appSlug}/runs`;
|
|
219
|
+
const fetchOnce = useCallback(
|
|
220
|
+
async ({ silent } = {}) => {
|
|
221
|
+
const requestId = ++requestIdRef.current;
|
|
222
|
+
if (!silent) setIsLoading(true);
|
|
223
|
+
try {
|
|
224
|
+
const page = await clientRef.current.get(
|
|
225
|
+
path,
|
|
226
|
+
JSON.parse(paramsKey)
|
|
227
|
+
);
|
|
228
|
+
if (requestId !== requestIdRef.current || !isMountedRef.current) return { kind: "superseded" };
|
|
229
|
+
setRuns(page.items);
|
|
230
|
+
setError(void 0);
|
|
231
|
+
return { kind: "success", items: page.items };
|
|
232
|
+
} catch (err) {
|
|
233
|
+
if (requestId !== requestIdRef.current || !isMountedRef.current) return { kind: "superseded" };
|
|
234
|
+
setError(err);
|
|
235
|
+
return { kind: "error", err };
|
|
236
|
+
} finally {
|
|
237
|
+
if (requestId === requestIdRef.current) setIsLoading(false);
|
|
238
|
+
}
|
|
239
|
+
},
|
|
240
|
+
[path, paramsKey]
|
|
241
|
+
);
|
|
242
|
+
useEffect(() => {
|
|
243
|
+
isMountedRef.current = true;
|
|
244
|
+
let timeoutHandle;
|
|
245
|
+
let cancelled = false;
|
|
246
|
+
const tick = async (silent) => {
|
|
247
|
+
const outcome = await fetchOnce({ silent });
|
|
248
|
+
if (cancelled || !isMountedRef.current || !follow) return;
|
|
249
|
+
if (outcome.kind !== "success" || outcome.items.some((r) => r.status === "running")) {
|
|
250
|
+
timeoutHandle = setTimeout(() => void tick(true), RUNS_FOLLOW_POLL_INTERVAL_MS);
|
|
251
|
+
}
|
|
252
|
+
};
|
|
253
|
+
void tick(false);
|
|
254
|
+
return () => {
|
|
255
|
+
cancelled = true;
|
|
256
|
+
isMountedRef.current = false;
|
|
257
|
+
if (timeoutHandle) clearTimeout(timeoutHandle);
|
|
258
|
+
};
|
|
259
|
+
}, [fetchOnce, follow]);
|
|
260
|
+
const refresh = useCallback(async () => {
|
|
261
|
+
await fetchOnce({ silent: true });
|
|
262
|
+
}, [fetchOnce]);
|
|
263
|
+
return { runs, isLoading, error, refresh };
|
|
264
|
+
}
|
|
91
265
|
|
|
92
266
|
// src/utils.ts
|
|
93
267
|
async function copyToClipboard(text) {
|
|
@@ -120,6 +294,12 @@ function downloadCsv(rows, filename) {
|
|
|
120
294
|
const blob = new Blob([`${BOM}${toCsv(rows)}`], { type: "text/csv;charset=utf-8" });
|
|
121
295
|
downloadBlob(blob, filename);
|
|
122
296
|
}
|
|
297
|
+
function triggerProps(trigger) {
|
|
298
|
+
if (React.isValidElement(trigger)) {
|
|
299
|
+
return { render: trigger, nativeButton: trigger.type === "button" || trigger.type === Button };
|
|
300
|
+
}
|
|
301
|
+
return { render: /* @__PURE__ */ jsx("span", { children: trigger }), nativeButton: false };
|
|
302
|
+
}
|
|
123
303
|
var buttonVariants = cva(
|
|
124
304
|
"inline-flex cursor-pointer items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50",
|
|
125
305
|
{
|
|
@@ -183,9 +363,18 @@ function CardHeader({ className, ...props }) {
|
|
|
183
363
|
function CardTitle({ className, ...props }) {
|
|
184
364
|
return /* @__PURE__ */ jsx("h3", { className: cn("text-lg font-semibold leading-none tracking-tight", className), ...props });
|
|
185
365
|
}
|
|
366
|
+
function CardDescription({
|
|
367
|
+
className,
|
|
368
|
+
...props
|
|
369
|
+
}) {
|
|
370
|
+
return /* @__PURE__ */ jsx("p", { className: cn("text-sm text-muted-foreground", className), ...props });
|
|
371
|
+
}
|
|
186
372
|
function CardContent({ className, ...props }) {
|
|
187
373
|
return /* @__PURE__ */ jsx("div", { className: cn("p-6 pt-0", className), ...props });
|
|
188
374
|
}
|
|
375
|
+
function CardFooter({ className, ...props }) {
|
|
376
|
+
return /* @__PURE__ */ jsx("div", { className: cn("flex items-center p-6 pt-0", className), ...props });
|
|
377
|
+
}
|
|
189
378
|
var Input = React.forwardRef(({ className, type, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
190
379
|
"input",
|
|
191
380
|
{
|
|
@@ -206,7 +395,13 @@ function TableHeader({
|
|
|
206
395
|
className,
|
|
207
396
|
...props
|
|
208
397
|
}) {
|
|
209
|
-
return /* @__PURE__ */ jsx(
|
|
398
|
+
return /* @__PURE__ */ jsx(
|
|
399
|
+
"thead",
|
|
400
|
+
{
|
|
401
|
+
className: cn("[&_tr]:border-b [&_tr]:hover:bg-transparent", className),
|
|
402
|
+
...props
|
|
403
|
+
}
|
|
404
|
+
);
|
|
210
405
|
}
|
|
211
406
|
function TableBody({ className, ...props }) {
|
|
212
407
|
return /* @__PURE__ */ jsx("tbody", { className: cn("[&_tr:last-child]:border-0", className), ...props });
|
|
@@ -228,7 +423,7 @@ function TableHead({ className, ...props }) {
|
|
|
228
423
|
"th",
|
|
229
424
|
{
|
|
230
425
|
className: cn(
|
|
231
|
-
"h-10 px-4 text-left align-middle text-xs font-medium text-muted-foreground",
|
|
426
|
+
"h-10 px-4 text-left align-middle text-xs font-medium uppercase tracking-wide text-muted-foreground",
|
|
232
427
|
className
|
|
233
428
|
),
|
|
234
429
|
...props
|
|
@@ -253,42 +448,43 @@ var Select = React.forwardRef(
|
|
|
253
448
|
)
|
|
254
449
|
);
|
|
255
450
|
Select.displayName = "Select";
|
|
256
|
-
var TabsContext = React.createContext(null);
|
|
257
|
-
function useTabsContext() {
|
|
258
|
-
const ctx = React.useContext(TabsContext);
|
|
259
|
-
if (!ctx) throw new Error("Tabs sub-component must be used within <Tabs>");
|
|
260
|
-
return ctx;
|
|
261
|
-
}
|
|
262
451
|
function Tabs({ defaultValue, value, onValueChange, className, children }) {
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
452
|
+
return /* @__PURE__ */ jsx(
|
|
453
|
+
Tabs$1.Root,
|
|
454
|
+
{
|
|
455
|
+
className,
|
|
456
|
+
defaultValue,
|
|
457
|
+
value,
|
|
458
|
+
onValueChange: (next) => onValueChange?.(String(next)),
|
|
459
|
+
children
|
|
460
|
+
}
|
|
461
|
+
);
|
|
270
462
|
}
|
|
271
463
|
function TabsList({
|
|
272
464
|
className,
|
|
273
465
|
children
|
|
274
466
|
}) {
|
|
275
|
-
return /* @__PURE__ */ jsx(
|
|
467
|
+
return /* @__PURE__ */ jsx(
|
|
468
|
+
Tabs$1.List,
|
|
469
|
+
{
|
|
470
|
+
className: cn("inline-flex items-center gap-1 rounded-lg bg-muted p-1", className),
|
|
471
|
+
children
|
|
472
|
+
}
|
|
473
|
+
);
|
|
276
474
|
}
|
|
277
475
|
function TabsTrigger({
|
|
278
476
|
value,
|
|
279
477
|
className,
|
|
280
478
|
children
|
|
281
479
|
}) {
|
|
282
|
-
const { value: current, setValue } = useTabsContext();
|
|
283
|
-
const active = current === value;
|
|
284
480
|
return /* @__PURE__ */ jsx(
|
|
285
|
-
|
|
481
|
+
Tabs$1.Tab,
|
|
286
482
|
{
|
|
287
|
-
|
|
288
|
-
onClick: () => setValue(value),
|
|
483
|
+
value,
|
|
289
484
|
className: cn(
|
|
290
485
|
"rounded-md px-3 py-1.5 text-sm font-medium transition-colors",
|
|
291
|
-
|
|
486
|
+
"text-muted-foreground hover:text-foreground",
|
|
487
|
+
"data-[active]:bg-card data-[active]:text-foreground data-[active]:shadow-sm",
|
|
292
488
|
className
|
|
293
489
|
),
|
|
294
490
|
children
|
|
@@ -300,38 +496,25 @@ function TabsContent({
|
|
|
300
496
|
className,
|
|
301
497
|
children
|
|
302
498
|
}) {
|
|
303
|
-
|
|
304
|
-
if (current !== value) return null;
|
|
305
|
-
return /* @__PURE__ */ jsx("div", { className, children });
|
|
499
|
+
return /* @__PURE__ */ jsx(Tabs$1.Panel, { value, className, children });
|
|
306
500
|
}
|
|
307
501
|
function Modal({ open, onClose, title, children, className }) {
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
/* @__PURE__ */ jsx(
|
|
311
|
-
"button",
|
|
312
|
-
{
|
|
313
|
-
type: "button",
|
|
314
|
-
"aria-label": "Close",
|
|
315
|
-
className: "absolute inset-0 cursor-default bg-foreground/40",
|
|
316
|
-
onClick: onClose
|
|
317
|
-
}
|
|
318
|
-
),
|
|
502
|
+
return /* @__PURE__ */ jsx(Dialog.Root, { open, onOpenChange: (next) => !next && onClose(), children: /* @__PURE__ */ jsxs(Dialog.Portal, { children: [
|
|
503
|
+
/* @__PURE__ */ jsx(Dialog.Backdrop, { "aria-label": "Close", className: "fixed inset-0 z-50 bg-foreground/40" }),
|
|
319
504
|
/* @__PURE__ */ jsxs(
|
|
320
|
-
|
|
505
|
+
Dialog.Popup,
|
|
321
506
|
{
|
|
322
|
-
role: "dialog",
|
|
323
|
-
"aria-modal": true,
|
|
324
507
|
className: cn(
|
|
325
|
-
"
|
|
508
|
+
"fixed left-1/2 top-1/2 z-50 w-[calc(100%-2rem)] max-w-md -translate-x-1/2 -translate-y-1/2 rounded-xl border bg-card p-5 shadow-lg",
|
|
326
509
|
className
|
|
327
510
|
),
|
|
328
511
|
children: [
|
|
329
|
-
title && /* @__PURE__ */ jsx(
|
|
512
|
+
title && /* @__PURE__ */ jsx(Dialog.Title, { className: "mb-3 text-base font-semibold", children: title }),
|
|
330
513
|
children
|
|
331
514
|
]
|
|
332
515
|
}
|
|
333
516
|
)
|
|
334
|
-
] });
|
|
517
|
+
] }) });
|
|
335
518
|
}
|
|
336
519
|
var spinnerSizes = { sm: "size-4", md: "size-6", lg: "size-8" };
|
|
337
520
|
function Spinner({ className, size = "md" }) {
|
|
@@ -381,28 +564,19 @@ function Switch({
|
|
|
381
564
|
...props
|
|
382
565
|
}) {
|
|
383
566
|
return /* @__PURE__ */ jsx(
|
|
384
|
-
|
|
567
|
+
Switch$1.Root,
|
|
385
568
|
{
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
"aria-checked": checked,
|
|
569
|
+
checked,
|
|
570
|
+
onCheckedChange: (next) => onCheckedChange?.(next),
|
|
389
571
|
disabled,
|
|
390
|
-
onClick: () => onCheckedChange?.(!checked),
|
|
391
572
|
className: cn(
|
|
392
|
-
|
|
393
|
-
|
|
573
|
+
// `data-[disabled]`, not `disabled:` — the root is a span, so `:disabled`
|
|
574
|
+
// never matches it and the dimming would silently stop happening.
|
|
575
|
+
"relative inline-flex h-5 w-9 shrink-0 cursor-pointer items-center rounded-full bg-input transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring data-[checked]:bg-primary data-[disabled]:cursor-not-allowed data-[disabled]:opacity-50",
|
|
394
576
|
className
|
|
395
577
|
),
|
|
396
578
|
...props,
|
|
397
|
-
children: /* @__PURE__ */ jsx(
|
|
398
|
-
"span",
|
|
399
|
-
{
|
|
400
|
-
className: cn(
|
|
401
|
-
"inline-block size-4 transform rounded-full bg-white shadow transition-transform",
|
|
402
|
-
checked ? "translate-x-4" : "translate-x-0.5"
|
|
403
|
-
)
|
|
404
|
-
}
|
|
405
|
-
)
|
|
579
|
+
children: /* @__PURE__ */ jsx(Switch$1.Thumb, { className: "inline-block size-4 translate-x-0.5 rounded-full bg-white shadow transition-transform data-[checked]:translate-x-4" })
|
|
406
580
|
}
|
|
407
581
|
);
|
|
408
582
|
}
|
|
@@ -531,47 +705,26 @@ function SidePanel({
|
|
|
531
705
|
children,
|
|
532
706
|
className
|
|
533
707
|
}) {
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
const onKeyDown = (e) => {
|
|
537
|
-
if (e.key === "Escape") onClose();
|
|
538
|
-
};
|
|
539
|
-
window.addEventListener("keydown", onKeyDown);
|
|
540
|
-
return () => window.removeEventListener("keydown", onKeyDown);
|
|
541
|
-
}, [open, onClose]);
|
|
542
|
-
if (!open) return null;
|
|
543
|
-
return /* @__PURE__ */ jsxs("div", { className: "fixed inset-0 z-50", children: [
|
|
544
|
-
/* @__PURE__ */ jsx(
|
|
545
|
-
"button",
|
|
546
|
-
{
|
|
547
|
-
type: "button",
|
|
548
|
-
"aria-label": "Fermer",
|
|
549
|
-
className: "absolute inset-0 cursor-default bg-foreground/40",
|
|
550
|
-
onClick: onClose
|
|
551
|
-
}
|
|
552
|
-
),
|
|
708
|
+
return /* @__PURE__ */ jsx(Dialog.Root, { open, onOpenChange: (next) => !next && onClose(), children: /* @__PURE__ */ jsxs(Dialog.Portal, { children: [
|
|
709
|
+
/* @__PURE__ */ jsx(Dialog.Backdrop, { "aria-label": "Fermer", className: "fixed inset-0 z-50 bg-foreground/40" }),
|
|
553
710
|
/* @__PURE__ */ jsxs(
|
|
554
|
-
|
|
711
|
+
Dialog.Popup,
|
|
555
712
|
{
|
|
556
|
-
role: "dialog",
|
|
557
|
-
"aria-modal": true,
|
|
558
713
|
className: cn(
|
|
559
|
-
"
|
|
714
|
+
"fixed inset-y-0 right-0 z-50 flex w-full max-w-lg flex-col border-l bg-card shadow-xl",
|
|
560
715
|
className
|
|
561
716
|
),
|
|
562
717
|
children: [
|
|
563
718
|
/* @__PURE__ */ jsxs("header", { className: "flex shrink-0 items-start gap-3 border-b px-5 py-4", children: [
|
|
564
719
|
/* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
|
|
565
|
-
title && /* @__PURE__ */ jsx(
|
|
566
|
-
description && /* @__PURE__ */ jsx(
|
|
720
|
+
title && /* @__PURE__ */ jsx(Dialog.Title, { className: "text-base font-semibold", children: title }),
|
|
721
|
+
description && /* @__PURE__ */ jsx(Dialog.Description, { className: "mt-0.5 text-xs text-muted-foreground", children: description })
|
|
567
722
|
] }),
|
|
568
723
|
/* @__PURE__ */ jsx(
|
|
569
|
-
|
|
724
|
+
Dialog.Close,
|
|
570
725
|
{
|
|
571
|
-
type: "button",
|
|
572
726
|
"aria-label": "Fermer",
|
|
573
727
|
className: "-mr-1 shrink-0 rounded-md p-1 text-muted-foreground hover:bg-accent hover:text-foreground",
|
|
574
|
-
onClick: onClose,
|
|
575
728
|
children: /* @__PURE__ */ jsx(IconX, { className: "size-4" })
|
|
576
729
|
}
|
|
577
730
|
)
|
|
@@ -580,7 +733,7 @@ function SidePanel({
|
|
|
580
733
|
]
|
|
581
734
|
}
|
|
582
735
|
)
|
|
583
|
-
] });
|
|
736
|
+
] }) });
|
|
584
737
|
}
|
|
585
738
|
var COPY_FEEDBACK_MS = 2e3;
|
|
586
739
|
function CopyButton({
|
|
@@ -623,69 +776,111 @@ function Popover({
|
|
|
623
776
|
align = "start",
|
|
624
777
|
className
|
|
625
778
|
}) {
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
return /* @__PURE__ */ jsxs(
|
|
643
|
-
/* @__PURE__ */ jsx("
|
|
644
|
-
|
|
779
|
+
return (
|
|
780
|
+
// The callback is re-wrapped so Base UI's event-details argument stops at the
|
|
781
|
+
// SDK boundary: the engine is an implementation detail, not part of the API.
|
|
782
|
+
/* @__PURE__ */ jsxs(Popover$1.Root, { open, onOpenChange: (next) => onOpenChange(next), children: [
|
|
783
|
+
/* @__PURE__ */ jsx("span", { className: "inline-block", children: /* @__PURE__ */ jsx(Popover$1.Trigger, { ...triggerProps(trigger) }) }),
|
|
784
|
+
/* @__PURE__ */ jsx(Popover$1.Portal, { children: /* @__PURE__ */ jsx(Popover$1.Positioner, { side: "bottom", align, sideOffset: 4, className: "z-50", children: /* @__PURE__ */ jsx(
|
|
785
|
+
Popover$1.Popup,
|
|
786
|
+
{
|
|
787
|
+
className: cn("min-w-48 rounded-md border bg-card p-2 shadow-lg", className),
|
|
788
|
+
children
|
|
789
|
+
}
|
|
790
|
+
) }) })
|
|
791
|
+
] })
|
|
792
|
+
);
|
|
793
|
+
}
|
|
794
|
+
function DropdownMenu({ trigger, items, align = "end", className }) {
|
|
795
|
+
return /* @__PURE__ */ jsxs(Menu.Root, { children: [
|
|
796
|
+
/* @__PURE__ */ jsx("span", { className: "inline-block", children: /* @__PURE__ */ jsx(Menu.Trigger, { ...triggerProps(trigger) }) }),
|
|
797
|
+
/* @__PURE__ */ jsx(Menu.Portal, { children: /* @__PURE__ */ jsx(Menu.Positioner, { side: "bottom", align, sideOffset: 4, className: "z-50", children: /* @__PURE__ */ jsx(
|
|
798
|
+
Menu.Popup,
|
|
799
|
+
{
|
|
800
|
+
className: cn(
|
|
801
|
+
"flex min-w-48 flex-col rounded-md border bg-card p-1 shadow-lg",
|
|
802
|
+
className
|
|
803
|
+
),
|
|
804
|
+
children: items.map((item, i) => /* @__PURE__ */ jsx(
|
|
805
|
+
Menu.Item,
|
|
806
|
+
{
|
|
807
|
+
disabled: item.disabled,
|
|
808
|
+
onClick: item.onSelect,
|
|
809
|
+
className: cn(
|
|
810
|
+
"cursor-pointer rounded px-2 py-1.5 text-left text-sm outline-none data-[highlighted]:bg-accent data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
|
811
|
+
item.destructive && "text-destructive"
|
|
812
|
+
),
|
|
813
|
+
children: item.label
|
|
814
|
+
},
|
|
815
|
+
i
|
|
816
|
+
))
|
|
817
|
+
}
|
|
818
|
+
) }) })
|
|
819
|
+
] });
|
|
820
|
+
}
|
|
821
|
+
var ToastContext = createContext(null);
|
|
822
|
+
function useToast() {
|
|
823
|
+
const toast = useContext(ToastContext);
|
|
824
|
+
if (!toast) throw new Error("useToast must be used within ToastProvider");
|
|
825
|
+
return toast;
|
|
826
|
+
}
|
|
827
|
+
var TOAST_TTL_MS = 4e3;
|
|
828
|
+
var TONE = {
|
|
829
|
+
success: "border-l-status-success",
|
|
830
|
+
error: "border-l-status-blocked",
|
|
831
|
+
info: "border-l-status-info"
|
|
832
|
+
};
|
|
833
|
+
function ToastProvider({ children }) {
|
|
834
|
+
const [toasts, setToasts] = useState([]);
|
|
835
|
+
const nextId = useRef(0);
|
|
836
|
+
const toast = useCallback((input) => {
|
|
837
|
+
const id = nextId.current++;
|
|
838
|
+
setToasts((prev) => [...prev, { id, ...input }]);
|
|
839
|
+
setTimeout(() => setToasts((prev) => prev.filter((t) => t.id !== id)), TOAST_TTL_MS);
|
|
840
|
+
}, []);
|
|
841
|
+
return /* @__PURE__ */ jsxs(ToastContext.Provider, { value: toast, children: [
|
|
842
|
+
children,
|
|
843
|
+
/* @__PURE__ */ jsx("div", { className: "pointer-events-none fixed bottom-4 right-4 z-[60] flex w-80 flex-col gap-2", children: toasts.map((t) => /* @__PURE__ */ jsxs(
|
|
645
844
|
"div",
|
|
646
845
|
{
|
|
846
|
+
role: "status",
|
|
847
|
+
className: cn(
|
|
848
|
+
"pointer-events-auto rounded-lg border border-l-4 bg-card px-4 py-3 text-foreground shadow-md",
|
|
849
|
+
TONE[t.variant ?? "success"]
|
|
850
|
+
),
|
|
851
|
+
children: [
|
|
852
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm font-medium", children: t.title }),
|
|
853
|
+
t.description && /* @__PURE__ */ jsx("p", { className: "mt-0.5 break-all text-xs text-muted-foreground", children: t.description })
|
|
854
|
+
]
|
|
855
|
+
},
|
|
856
|
+
t.id
|
|
857
|
+
)) })
|
|
858
|
+
] });
|
|
859
|
+
}
|
|
860
|
+
var SIDES = {
|
|
861
|
+
top: "bottom-full left-1/2 -translate-x-1/2 mb-1.5",
|
|
862
|
+
right: "left-full top-1/2 -translate-y-1/2 ml-1.5",
|
|
863
|
+
bottom: "top-full left-1/2 -translate-x-1/2 mt-1.5",
|
|
864
|
+
left: "right-full top-1/2 -translate-y-1/2 mr-1.5"
|
|
865
|
+
};
|
|
866
|
+
function Tooltip({ label, children, side = "top", className }) {
|
|
867
|
+
return /* @__PURE__ */ jsxs("span", { className: "group/tt relative inline-flex", children: [
|
|
868
|
+
children,
|
|
869
|
+
/* @__PURE__ */ jsx(
|
|
870
|
+
"span",
|
|
871
|
+
{
|
|
872
|
+
role: "tooltip",
|
|
647
873
|
className: cn(
|
|
648
|
-
"absolute z-
|
|
649
|
-
|
|
874
|
+
"pointer-events-none absolute z-50 whitespace-nowrap rounded-md bg-foreground px-2 py-1 text-xs font-medium text-background opacity-0 shadow-md transition-opacity group-hover/tt:opacity-100",
|
|
875
|
+
SIDES[side],
|
|
650
876
|
className
|
|
651
877
|
),
|
|
652
|
-
children
|
|
878
|
+
children: label
|
|
653
879
|
}
|
|
654
880
|
)
|
|
655
881
|
] });
|
|
656
882
|
}
|
|
657
|
-
function DropdownMenu({ trigger, items, align = "end", className }) {
|
|
658
|
-
const [open, setOpen] = React.useState(false);
|
|
659
|
-
return /* @__PURE__ */ jsx(
|
|
660
|
-
Popover,
|
|
661
|
-
{
|
|
662
|
-
open,
|
|
663
|
-
onOpenChange: setOpen,
|
|
664
|
-
trigger,
|
|
665
|
-
align,
|
|
666
|
-
className: cn("p-1", className),
|
|
667
|
-
children: /* @__PURE__ */ jsx("div", { role: "menu", className: "flex flex-col", children: items.map((item, i) => /* @__PURE__ */ jsx(
|
|
668
|
-
"button",
|
|
669
|
-
{
|
|
670
|
-
type: "button",
|
|
671
|
-
role: "menuitem",
|
|
672
|
-
disabled: item.disabled,
|
|
673
|
-
className: cn(
|
|
674
|
-
"cursor-pointer rounded px-2 py-1.5 text-left text-sm hover:bg-accent disabled:pointer-events-none disabled:opacity-50",
|
|
675
|
-
item.destructive && "text-destructive"
|
|
676
|
-
),
|
|
677
|
-
onClick: () => {
|
|
678
|
-
setOpen(false);
|
|
679
|
-
item.onSelect();
|
|
680
|
-
},
|
|
681
|
-
children: item.label
|
|
682
|
-
},
|
|
683
|
-
i
|
|
684
|
-
)) })
|
|
685
|
-
}
|
|
686
|
-
);
|
|
687
|
-
}
|
|
688
883
|
|
|
689
|
-
export { AppApiProvider, AppContextProvider, AppInvokeError, Badge, Button, Card, CardContent, CardHeader, CardTitle, Checkbox, CopyButton, DateRangePicker, DropdownMenu, Input, Link, Modal, Popover, Select, SidePanel, Spinner, Switch, Table, TableBody, TableCell, TableHead, TableHeader, TableRow, Tabs, TabsContent, TabsList, TabsTrigger, Textarea, cn, copyToClipboard, downloadBlob, downloadCsv, toCsv, useAppApi, useAppContext, useAppSettings };
|
|
884
|
+
export { AppApiProvider, AppContextProvider, AppInvokeError, Badge, Button, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, Checkbox, CopyButton, DateRangePicker, DropdownMenu, Input, Link, Modal, Popover, Select, SidePanel, Spinner, Switch, Table, TableBody, TableCell, TableHead, TableHeader, TableRow, Tabs, TabsContent, TabsList, TabsTrigger, Textarea, ToastProvider, Tooltip, badgeVariants, buttonVariants, cn, copyToClipboard, downloadBlob, downloadCsv, toCsv, useAppApi, useAppContext, useAppRun, useAppRuns, useAppSettings, useToast };
|
|
690
885
|
//# sourceMappingURL=index.js.map
|
|
691
886
|
//# sourceMappingURL=index.js.map
|