@pactor-app/ui 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.
@@ -0,0 +1,752 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/interaction/index.ts
31
+ var interaction_exports = {};
32
+ __export(interaction_exports, {
33
+ OVERLAY_CANCEL_PAYLOAD: () => OVERLAY_CANCEL_PAYLOAD,
34
+ OverlayManager: () => OverlayManager,
35
+ ShadcnInteractionProvider: () => ShadcnInteractionProvider,
36
+ ShadcnOverlayHost: () => ShadcnOverlayHost,
37
+ createUiBridge: () => createUiBridge,
38
+ registerInteractionActions: () => registerInteractionActions
39
+ });
40
+ module.exports = __toCommonJS(interaction_exports);
41
+
42
+ // src/interaction/manager.ts
43
+ var OVERLAY_CANCEL_PAYLOAD = { success: false };
44
+ var OverlayManager = class {
45
+ constructor() {
46
+ this.pending = [];
47
+ this.listeners = /* @__PURE__ */ new Set();
48
+ this.counter = 0;
49
+ /** 缓存的堆栈快照:变化时才更换引用,供 useSyncExternalStore 使用 */
50
+ this.snapshot = Object.freeze([]);
51
+ }
52
+ /**
53
+ * 打开 overlay:入栈、通知,返回 Promise——close 时以 payload
54
+ * resolve;取消关闭(无 payload)resolve `{ success: false }`。
55
+ * `options.parentScope` 记录打开时刻的父作用域浅快照。
56
+ */
57
+ open(config, options = {}) {
58
+ if (!config || config.type !== "modal" && config.type !== "drawer") {
59
+ return Promise.reject(
60
+ new Error(
61
+ '[pactor-interaction] overlay.open \u9700\u8981 type \u4E3A "modal" \u6216 "drawer" \u7684\u914D\u7F6E'
62
+ )
63
+ );
64
+ }
65
+ this.counter += 1;
66
+ const entry = { ...config, id: `overlay-${this.counter}` };
67
+ return new Promise((resolve) => {
68
+ this.pending.push({
69
+ entry,
70
+ parentScope: options.parentScope ? { ...options.parentScope } : void 0,
71
+ resolve
72
+ });
73
+ this.commit();
74
+ });
75
+ }
76
+ /**
77
+ * 关闭 overlay 并以 payload resolve 对应的 open Promise:
78
+ * - `close(id, payload)`:关闭指定条目,原样 resolve payload
79
+ * (payload 缺省为取消语义 `{ success: false }`)
80
+ * - `close(payload)`:关闭栈顶,原样 resolve payload
81
+ * - `close()`:关闭栈顶,取消语义
82
+ * 未命中任何条目时为受控 no-op,不抛错。
83
+ */
84
+ close(idOrPayload, payload) {
85
+ let index;
86
+ let resolvedPayload;
87
+ if (typeof idOrPayload === "string") {
88
+ index = this.pending.findIndex((p) => p.entry.id === idOrPayload);
89
+ if (index === -1) {
90
+ return;
91
+ }
92
+ resolvedPayload = payload;
93
+ } else {
94
+ index = this.pending.length - 1;
95
+ if (index === -1) {
96
+ return;
97
+ }
98
+ resolvedPayload = idOrPayload === void 0 ? payload : idOrPayload;
99
+ }
100
+ const [target] = this.pending.splice(index, 1);
101
+ this.commit();
102
+ target?.resolve(
103
+ resolvedPayload === void 0 ? OVERLAY_CANCEL_PAYLOAD : resolvedPayload
104
+ );
105
+ }
106
+ /** 当前堆栈快照(打开顺序,栈顶在末尾);引用在堆栈不变时保持稳定 */
107
+ stack() {
108
+ return this.snapshot;
109
+ }
110
+ /** 打开时记录的父作用域浅快照(只读约定);未知 id 返回 undefined */
111
+ parentScopeOf(id) {
112
+ return this.pending.find((p) => p.entry.id === id)?.parentScope;
113
+ }
114
+ /** 订阅堆栈变化(open / close 时通知),返回退订函数 */
115
+ subscribe(listener) {
116
+ this.listeners.add(listener);
117
+ return () => {
118
+ this.listeners.delete(listener);
119
+ };
120
+ }
121
+ /** 重建快照并通知订阅者 */
122
+ commit() {
123
+ this.snapshot = Object.freeze(this.pending.map((p) => p.entry));
124
+ for (const listener of [...this.listeners]) {
125
+ listener(this.snapshot);
126
+ }
127
+ }
128
+ };
129
+
130
+ // src/interaction/actions.ts
131
+ var import_runtime = require("@pactor-app/runtime");
132
+ function createUiBridge() {
133
+ return {};
134
+ }
135
+ function registerInteractionActions(actions, manager, ui) {
136
+ const register = (name, handler) => {
137
+ if (!actions.has(name)) {
138
+ actions.register(name, handler);
139
+ }
140
+ };
141
+ register("overlay.open", async (action, ctx) => {
142
+ const overlay = action.params?.["overlay"];
143
+ if (!isRecord(overlay)) {
144
+ return failure("[pactor-interaction] overlay.open \u7F3A\u5C11 overlay \u914D\u7F6E\u53C2\u6570");
145
+ }
146
+ const { content, ...rest } = overlay;
147
+ const resolved = (0, import_runtime.resolveDeep)(rest, ctx.scope);
148
+ const config = { ...resolved, content };
149
+ const data = await manager.open(config, {
150
+ parentScope: { ...ctx.scope }
151
+ });
152
+ return { ok: true, data };
153
+ });
154
+ register("overlay.close", (action) => {
155
+ const id = action.params?.["id"];
156
+ const payload = action.params?.["payload"];
157
+ if (typeof id === "string" && id) {
158
+ manager.close(id, payload);
159
+ } else if (payload !== void 0) {
160
+ manager.close(payload);
161
+ } else {
162
+ manager.close();
163
+ }
164
+ return { ok: true };
165
+ });
166
+ register("confirm", async (action) => {
167
+ const modal = ui.modal;
168
+ if (!modal) {
169
+ return failure(
170
+ "[pactor-interaction] confirm \u52A8\u4F5C\u9700\u8981 <InteractionProvider> \u6CE8\u5165 modal \u5B9E\u4F8B"
171
+ );
172
+ }
173
+ const params = action.params ?? {};
174
+ const data = await new Promise((resolve) => {
175
+ modal.confirm({
176
+ title: params["title"],
177
+ content: params["content"],
178
+ onOk: () => resolve(true),
179
+ onCancel: () => resolve(false)
180
+ });
181
+ });
182
+ return { ok: true, data };
183
+ });
184
+ register("message", (action) => {
185
+ const message = ui.message;
186
+ if (!message) {
187
+ return failure(
188
+ "[pactor-interaction] message \u52A8\u4F5C\u9700\u8981 <InteractionProvider> \u6CE8\u5165 message \u5B9E\u4F8B"
189
+ );
190
+ }
191
+ const params = action.params ?? {};
192
+ const type = params["type"];
193
+ const text = params["text"];
194
+ if (type === "success" || type === "error" || type === "warning") {
195
+ message[type](text);
196
+ } else {
197
+ message.info(text);
198
+ }
199
+ return { ok: true };
200
+ });
201
+ }
202
+ function failure(message) {
203
+ return { ok: false, error: new Error(message) };
204
+ }
205
+ function isRecord(value) {
206
+ return typeof value === "object" && value !== null && !Array.isArray(value);
207
+ }
208
+
209
+ // src/interaction/provider.tsx
210
+ var import_react = require("react");
211
+ var import_react_dom = require("react-dom");
212
+
213
+ // src/lib/utils.ts
214
+ var import_clsx = require("clsx");
215
+ var import_tailwind_merge = require("tailwind-merge");
216
+ function cn(...inputs) {
217
+ return (0, import_tailwind_merge.twMerge)((0, import_clsx.clsx)(inputs));
218
+ }
219
+
220
+ // src/ui/button.tsx
221
+ var import_button = require("@base-ui/react/button");
222
+ var import_class_variance_authority = require("class-variance-authority");
223
+ var React = __toESM(require("react"), 1);
224
+ var import_jsx_runtime = require("react/jsx-runtime");
225
+ var buttonVariants = (0, import_class_variance_authority.cva)(
226
+ "inline-flex shrink-0 cursor-pointer items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium outline-none transition-colors focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
227
+ {
228
+ variants: {
229
+ variant: {
230
+ default: "bg-primary text-primary-foreground hover:bg-primary/90",
231
+ destructive: "bg-destructive text-destructive-foreground hover:bg-destructive/90 focus-visible:ring-destructive/20",
232
+ outline: "border border-border bg-background hover:bg-accent hover:text-accent-foreground",
233
+ secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
234
+ ghost: "hover:bg-accent hover:text-accent-foreground",
235
+ link: "text-primary underline-offset-4 hover:underline"
236
+ },
237
+ size: {
238
+ default: "h-9 px-4 py-2",
239
+ sm: "h-8 gap-1.5 rounded-md px-3 text-xs",
240
+ lg: "h-10 rounded-md px-6",
241
+ icon: "size-9"
242
+ }
243
+ },
244
+ defaultVariants: {
245
+ variant: "default",
246
+ size: "default"
247
+ }
248
+ }
249
+ );
250
+ function Button({
251
+ className,
252
+ variant,
253
+ size,
254
+ asChild = false,
255
+ children,
256
+ ...props
257
+ }) {
258
+ if (asChild && React.isValidElement(children)) {
259
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
260
+ import_button.Button,
261
+ {
262
+ "data-slot": "button",
263
+ className: cn(buttonVariants({ variant, size, className })),
264
+ render: children,
265
+ ...props
266
+ }
267
+ );
268
+ }
269
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
270
+ import_button.Button,
271
+ {
272
+ "data-slot": "button",
273
+ className: cn(buttonVariants({ variant, size, className })),
274
+ ...props,
275
+ children
276
+ }
277
+ );
278
+ }
279
+
280
+ // src/ui/dialog.tsx
281
+ var import_dialog = require("@base-ui/react/dialog");
282
+ var import_lucide_react = require("lucide-react");
283
+ var import_jsx_runtime2 = require("react/jsx-runtime");
284
+ function Dialog({
285
+ ...props
286
+ }) {
287
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_dialog.Dialog.Root, { "data-slot": "dialog", ...props });
288
+ }
289
+ function DialogPortal({
290
+ ...props
291
+ }) {
292
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_dialog.Dialog.Portal, { "data-slot": "dialog-portal", ...props });
293
+ }
294
+ function DialogClose({
295
+ ...props
296
+ }) {
297
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_dialog.Dialog.Close, { "data-slot": "dialog-close", ...props });
298
+ }
299
+ function DialogOverlay({
300
+ className,
301
+ ...props
302
+ }) {
303
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
304
+ import_dialog.Dialog.Backdrop,
305
+ {
306
+ "data-slot": "dialog-overlay",
307
+ className: cn(
308
+ "fixed inset-0 z-50 bg-black/50 data-open:animate-in data-open:fade-in-0 data-closed:animate-out data-closed:fade-out-0",
309
+ className
310
+ ),
311
+ ...props
312
+ }
313
+ );
314
+ }
315
+ function DialogContent({
316
+ className,
317
+ children,
318
+ showCloseButton = true,
319
+ ...props
320
+ }) {
321
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(DialogPortal, { "data-slot": "dialog-portal", children: [
322
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(DialogOverlay, {}),
323
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
324
+ import_dialog.Dialog.Popup,
325
+ {
326
+ "data-slot": "dialog-content",
327
+ className: cn(
328
+ "fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border border-border bg-background p-6 shadow-lg duration-200 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95 sm:max-w-lg",
329
+ className
330
+ ),
331
+ ...props,
332
+ children: [
333
+ children,
334
+ showCloseButton && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
335
+ import_dialog.Dialog.Close,
336
+ {
337
+ "data-slot": "dialog-close",
338
+ render: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
339
+ "button",
340
+ {
341
+ type: "button",
342
+ className: "absolute top-4 right-4 cursor-pointer rounded-xs opacity-70 transition-opacity hover:opacity-100 focus-visible:ring-[3px] focus-visible:ring-ring/50 focus-visible:outline-none disabled:pointer-events-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4"
343
+ }
344
+ ),
345
+ children: [
346
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_lucide_react.XIcon, {}),
347
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "sr-only", children: "Close" })
348
+ ]
349
+ }
350
+ )
351
+ ]
352
+ }
353
+ )
354
+ ] });
355
+ }
356
+ function DialogHeader({ className, ...props }) {
357
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
358
+ "div",
359
+ {
360
+ "data-slot": "dialog-header",
361
+ className: cn("flex flex-col gap-2 text-center sm:text-left", className),
362
+ ...props
363
+ }
364
+ );
365
+ }
366
+ function DialogFooter({ className, ...props }) {
367
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
368
+ "div",
369
+ {
370
+ "data-slot": "dialog-footer",
371
+ className: cn(
372
+ "flex flex-col-reverse gap-2 sm:flex-row sm:justify-end",
373
+ className
374
+ ),
375
+ ...props
376
+ }
377
+ );
378
+ }
379
+ function DialogTitle({
380
+ className,
381
+ ...props
382
+ }) {
383
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
384
+ import_dialog.Dialog.Title,
385
+ {
386
+ "data-slot": "dialog-title",
387
+ className: cn("text-lg leading-none font-semibold", className),
388
+ ...props
389
+ }
390
+ );
391
+ }
392
+ function DialogDescription({
393
+ className,
394
+ ...props
395
+ }) {
396
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
397
+ import_dialog.Dialog.Description,
398
+ {
399
+ "data-slot": "dialog-description",
400
+ className: cn("text-sm text-muted-foreground", className),
401
+ ...props
402
+ }
403
+ );
404
+ }
405
+
406
+ // src/interaction/provider.tsx
407
+ var import_jsx_runtime3 = require("react/jsx-runtime");
408
+ var TOAST_DURATION = 3e3;
409
+ var toastTypeClass = {
410
+ success: "border-l-4 border-l-success",
411
+ error: "border-l-4 border-l-destructive",
412
+ info: "border-l-4 border-l-info",
413
+ warning: "border-l-4 border-l-warning"
414
+ };
415
+ function ShadcnInteractionProvider({
416
+ bridge,
417
+ children
418
+ }) {
419
+ const [confirmState, setConfirmState] = (0, import_react.useState)(null);
420
+ const [toasts, setToasts] = (0, import_react.useState)([]);
421
+ const { modal, message } = (0, import_react.useMemo)(() => {
422
+ const pushToast = (type, content) => {
423
+ const id = Date.now() + Math.random();
424
+ setToasts((prev) => [...prev, { id, type, content }]);
425
+ setTimeout(() => {
426
+ setToasts((prev) => prev.filter((toast) => toast.id !== id));
427
+ }, TOAST_DURATION);
428
+ };
429
+ const modalImpl = {
430
+ confirm(config) {
431
+ setConfirmState({ ...config });
432
+ }
433
+ };
434
+ const messageImpl = {
435
+ success: (content) => pushToast("success", content),
436
+ error: (content) => pushToast("error", content),
437
+ info: (content) => pushToast("info", content),
438
+ warning: (content) => pushToast("warning", content)
439
+ };
440
+ return { modal: modalImpl, message: messageImpl };
441
+ }, []);
442
+ (0, import_react.useEffect)(() => {
443
+ bridge.modal = modal;
444
+ bridge.message = message;
445
+ return () => {
446
+ bridge.modal = void 0;
447
+ bridge.message = void 0;
448
+ };
449
+ }, [bridge, modal, message]);
450
+ const handleOk = () => {
451
+ const state = confirmState;
452
+ setConfirmState(null);
453
+ state?.onOk?.();
454
+ };
455
+ const handleCancel = () => {
456
+ const state = confirmState;
457
+ setConfirmState(null);
458
+ state?.onCancel?.();
459
+ };
460
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
461
+ children,
462
+ typeof document !== "undefined" && (0, import_react_dom.createPortal)(
463
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
464
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
465
+ Dialog,
466
+ {
467
+ open: confirmState !== null,
468
+ onOpenChange: (open) => {
469
+ if (!open) {
470
+ handleCancel();
471
+ }
472
+ },
473
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(DialogContent, { "data-slot": "confirm-dialog", className: "sm:max-w-sm", children: [
474
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(DialogHeader, { children: [
475
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(DialogTitle, { children: confirmState?.title ?? "" }),
476
+ confirmState?.content !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
477
+ DialogDescription,
478
+ {
479
+ render: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { children: confirmState.content })
480
+ }
481
+ )
482
+ ] }),
483
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(DialogFooter, { children: [
484
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Button, { type: "button", variant: "outline", onClick: handleCancel, children: "\u53D6\u6D88" }),
485
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Button, { type: "button", onClick: handleOk, children: "\u786E\u5B9A" })
486
+ ] })
487
+ ] })
488
+ }
489
+ ),
490
+ toasts.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
491
+ "div",
492
+ {
493
+ "data-slot": "toast-container",
494
+ className: "fixed top-4 right-4 z-[100] flex w-80 flex-col gap-2",
495
+ children: toasts.map((toast) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
496
+ "div",
497
+ {
498
+ "data-slot": "toast",
499
+ "data-type": toast.type,
500
+ role: "status",
501
+ className: cn(
502
+ "rounded-md border border-border bg-popover px-4 py-3 text-sm text-popover-foreground shadow-md",
503
+ toastTypeClass[toast.type]
504
+ ),
505
+ children: toast.content
506
+ },
507
+ toast.id
508
+ ))
509
+ }
510
+ )
511
+ ] }),
512
+ document.body
513
+ )
514
+ ] });
515
+ }
516
+
517
+ // src/interaction/host.tsx
518
+ var import_lucide_react2 = require("lucide-react");
519
+ var import_runtime2 = require("@pactor-app/runtime");
520
+ var import_react2 = require("react");
521
+ var import_jsx_runtime4 = require("react/jsx-runtime");
522
+ function toCssWidth(width) {
523
+ if (width === void 0) {
524
+ return void 0;
525
+ }
526
+ return typeof width === "number" ? `${width}px` : width;
527
+ }
528
+ function ShadcnOverlayHost({ manager, resolvePage }) {
529
+ const renderer = (0, import_runtime2.useRenderer)();
530
+ const stack = (0, import_react2.useSyncExternalStore)(
531
+ (onChange) => manager.subscribe(onChange),
532
+ () => manager.stack()
533
+ );
534
+ (0, import_react2.useSyncExternalStore)(
535
+ (onChange) => {
536
+ const offState = renderer.context.state.subscribe(onChange);
537
+ const offData = renderer.context.data.subscribe(onChange);
538
+ return () => {
539
+ offState();
540
+ offData();
541
+ };
542
+ },
543
+ () => `${renderer.context.state.getVersion()}:${renderer.context.data.getVersion()}`
544
+ );
545
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_jsx_runtime4.Fragment, { children: stack.map((entry) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
546
+ OverlayEntryView,
547
+ {
548
+ entry,
549
+ manager,
550
+ renderer,
551
+ resolvePage
552
+ },
553
+ entry.id
554
+ )) });
555
+ }
556
+ function OverlayEntryView({
557
+ entry,
558
+ manager,
559
+ renderer,
560
+ resolvePage
561
+ }) {
562
+ const handleCancel = () => manager.close(entry.id);
563
+ let body = null;
564
+ if (entry.content) {
565
+ body = renderer.renderChildren([entry.content], {
566
+ params: entry.params ?? {},
567
+ parent: manager.parentScopeOf(entry.id)
568
+ });
569
+ } else if (entry.page) {
570
+ body = /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
571
+ PageOverlayBody,
572
+ {
573
+ entry,
574
+ renderer,
575
+ resolvePage
576
+ }
577
+ );
578
+ }
579
+ if (entry.type === "drawer") {
580
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(ShadcnDrawerView, { entry, onCancel: handleCancel, children: body });
581
+ }
582
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
583
+ Dialog,
584
+ {
585
+ open: true,
586
+ onOpenChange: (open) => {
587
+ if (!open) {
588
+ handleCancel();
589
+ }
590
+ },
591
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
592
+ DialogContent,
593
+ {
594
+ "data-slot": "overlay-modal",
595
+ className: cn(entry.width !== void 0 && "sm:max-w-none"),
596
+ style: entry.width === void 0 ? void 0 : { width: toCssWidth(entry.width) },
597
+ showCloseButton: false,
598
+ children: [
599
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(DialogHeader, { children: [
600
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(DialogTitle, { children: entry.title ?? "" }),
601
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
602
+ DialogClose,
603
+ {
604
+ render: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
605
+ "button",
606
+ {
607
+ type: "button",
608
+ "data-slot": "overlay-modal-close",
609
+ "aria-label": "\u5173\u95ED",
610
+ className: "absolute top-4 right-4 cursor-pointer rounded-xs opacity-70 transition-opacity hover:opacity-100 focus-visible:ring-[3px] focus-visible:ring-ring/50 focus-visible:outline-none",
611
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_lucide_react2.XIcon, { className: "size-4" })
612
+ }
613
+ )
614
+ }
615
+ )
616
+ ] }),
617
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { "data-slot": "overlay-modal-body", children: body })
618
+ ]
619
+ }
620
+ )
621
+ }
622
+ );
623
+ }
624
+ function ShadcnDrawerView({
625
+ entry,
626
+ onCancel,
627
+ children
628
+ }) {
629
+ (0, import_react2.useEffect)(() => {
630
+ const handleKeyDown = (event) => {
631
+ if (event.key === "Escape") {
632
+ onCancel();
633
+ }
634
+ };
635
+ document.addEventListener("keydown", handleKeyDown);
636
+ return () => {
637
+ document.removeEventListener("keydown", handleKeyDown);
638
+ };
639
+ }, [onCancel]);
640
+ const panelStyle = entry.width === void 0 ? void 0 : { width: toCssWidth(entry.width) };
641
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { "data-slot": "drawer-root", children: [
642
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
643
+ "div",
644
+ {
645
+ "data-slot": "drawer-mask",
646
+ "data-testid": "drawer-mask",
647
+ className: "fixed inset-0 z-50 bg-black/50",
648
+ onClick: onCancel
649
+ }
650
+ ),
651
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
652
+ "div",
653
+ {
654
+ "data-slot": "drawer-panel",
655
+ role: "dialog",
656
+ "aria-modal": "true",
657
+ "aria-label": entry.title,
658
+ style: panelStyle,
659
+ className: "fixed inset-y-0 right-0 z-50 flex w-96 max-w-full flex-col border-l border-border bg-background shadow-lg",
660
+ children: [
661
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
662
+ "div",
663
+ {
664
+ "data-slot": "drawer-header",
665
+ className: "flex items-center justify-between border-b border-border px-6 py-4",
666
+ children: [
667
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "text-lg font-semibold", children: entry.title ?? "" }),
668
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
669
+ "button",
670
+ {
671
+ type: "button",
672
+ "data-slot": "drawer-close",
673
+ "aria-label": "\u5173\u95ED",
674
+ className: "cursor-pointer rounded-xs opacity-70 transition-opacity hover:opacity-100 focus-visible:ring-[3px] focus-visible:ring-ring/50 focus-visible:outline-none",
675
+ onClick: onCancel,
676
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_lucide_react2.XIcon, { className: "size-4" })
677
+ }
678
+ )
679
+ ]
680
+ }
681
+ ),
682
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { "data-slot": "drawer-body", className: "flex-1 overflow-auto p-6", children })
683
+ ]
684
+ }
685
+ )
686
+ ] });
687
+ }
688
+ function PageOverlayBody({
689
+ entry,
690
+ renderer,
691
+ resolvePage
692
+ }) {
693
+ const [child, setChild] = (0, import_react2.useState)(null);
694
+ const [error, setError] = (0, import_react2.useState)(null);
695
+ (0, import_react2.useEffect)(() => {
696
+ if (!resolvePage) {
697
+ setError(
698
+ new Error(
699
+ `[@pactor-app/ui] page overlay\uFF08pageId: "${entry.page?.pageId}"\uFF09\u9700\u8981 ShadcnOverlayHost \u6CE8\u5165 resolvePage`
700
+ )
701
+ );
702
+ return;
703
+ }
704
+ let cancelled = false;
705
+ let created = null;
706
+ void (async () => {
707
+ try {
708
+ const pageDsl = await resolvePage(entry.page?.pageId ?? "");
709
+ if (cancelled) {
710
+ return;
711
+ }
712
+ const host = renderer.context;
713
+ created = (0, import_runtime2.createPageRuntime)(pageDsl, {
714
+ app: host.app,
715
+ user: host.user,
716
+ components: host.components,
717
+ services: host.services,
718
+ actions: host.actions,
719
+ permission: host.permission,
720
+ capabilities: host.capabilities,
721
+ http: host.http,
722
+ route: { params: entry.params ?? {} }
723
+ });
724
+ setChild(created);
725
+ } catch (cause) {
726
+ if (!cancelled) {
727
+ setError(cause instanceof Error ? cause : new Error(String(cause)));
728
+ }
729
+ }
730
+ })();
731
+ return () => {
732
+ cancelled = true;
733
+ void created?.destroy();
734
+ };
735
+ }, [entry.id]);
736
+ if (error) {
737
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { role: "alert", children: error.message });
738
+ }
739
+ if (!child) {
740
+ return null;
741
+ }
742
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_runtime2.PageRuntimeView, { runtime: child });
743
+ }
744
+ // Annotate the CommonJS export names for ESM import in node:
745
+ 0 && (module.exports = {
746
+ OVERLAY_CANCEL_PAYLOAD,
747
+ OverlayManager,
748
+ ShadcnInteractionProvider,
749
+ ShadcnOverlayHost,
750
+ createUiBridge,
751
+ registerInteractionActions
752
+ });