@burojs/ui-shadcn 0.4.0 → 0.6.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.
Files changed (131) hide show
  1. package/README.md +22 -5
  2. package/dist/bulk-summary-CwSd6Yjb.cjs +108 -0
  3. package/dist/bulk-summary-CwSd6Yjb.cjs.map +1 -0
  4. package/dist/bulk-summary-GxMDP4bw.js +108 -0
  5. package/dist/bulk-summary-GxMDP4bw.js.map +1 -0
  6. package/dist/button-BxnnE28H.cjs +55 -0
  7. package/dist/button-BxnnE28H.cjs.map +1 -0
  8. package/dist/button-MWVeZaqj.js +44 -0
  9. package/dist/button-MWVeZaqj.js.map +1 -0
  10. package/dist/candidates-gate-Bb70LPAy.js +135 -0
  11. package/dist/candidates-gate-Bb70LPAy.js.map +1 -0
  12. package/dist/candidates-gate-D33jXLHO.cjs +153 -0
  13. package/dist/candidates-gate-D33jXLHO.cjs.map +1 -0
  14. package/dist/candidates-relation-picker-B116Bmqi.js +31 -0
  15. package/dist/candidates-relation-picker-B116Bmqi.js.map +1 -0
  16. package/dist/candidates-relation-picker-Cy0rTxvI.cjs +31 -0
  17. package/dist/candidates-relation-picker-Cy0rTxvI.cjs.map +1 -0
  18. package/dist/check-confirm-dialog--gPFnEeg.cjs +1547 -0
  19. package/dist/check-confirm-dialog--gPFnEeg.cjs.map +1 -0
  20. package/dist/check-confirm-dialog-bRT8lt0d.js +1245 -0
  21. package/dist/check-confirm-dialog-bRT8lt0d.js.map +1 -0
  22. package/dist/dialog-B_rt9S1c.js +63 -0
  23. package/dist/dialog-B_rt9S1c.js.map +1 -0
  24. package/dist/dialog-DRNUq9EA.cjs +112 -0
  25. package/dist/dialog-DRNUq9EA.cjs.map +1 -0
  26. package/dist/filter-builder-Bhn-dhZV.js +1621 -0
  27. package/dist/filter-builder-Bhn-dhZV.js.map +1 -0
  28. package/dist/filter-builder-nXqtM_h4.cjs +1789 -0
  29. package/dist/filter-builder-nXqtM_h4.cjs.map +1 -0
  30. package/dist/flow-runtime-D3avVH0I.cjs +255 -0
  31. package/dist/flow-runtime-D3avVH0I.cjs.map +1 -0
  32. package/dist/flow-runtime-s6lkRPKF.js +252 -0
  33. package/dist/flow-runtime-s6lkRPKF.js.map +1 -0
  34. package/dist/flow-screen-CCvgDI9G.js +704 -0
  35. package/dist/flow-screen-CCvgDI9G.js.map +1 -0
  36. package/dist/flow-screen-Ci_Sik4Y.cjs +708 -0
  37. package/dist/flow-screen-Ci_Sik4Y.cjs.map +1 -0
  38. package/dist/index.cjs +3420 -11986
  39. package/dist/index.cjs.map +1 -1
  40. package/dist/index.d.cts +1843 -2015
  41. package/dist/index.d.ts +1849 -2021
  42. package/dist/index.js +3090 -11675
  43. package/dist/index.js.map +1 -1
  44. package/dist/inline-flow-host-B6FI0ctQ.js +112 -0
  45. package/dist/inline-flow-host-B6FI0ctQ.js.map +1 -0
  46. package/dist/inline-flow-host-DV1et3c6.cjs +112 -0
  47. package/dist/inline-flow-host-DV1et3c6.cjs.map +1 -0
  48. package/dist/input-BI0czGyI.cjs +23 -0
  49. package/dist/input-BI0czGyI.cjs.map +1 -0
  50. package/dist/input-CUPStzZO.js +17 -0
  51. package/dist/input-CUPStzZO.js.map +1 -0
  52. package/dist/label-66W1qbke.js +29 -0
  53. package/dist/label-66W1qbke.js.map +1 -0
  54. package/dist/label-w6JYf2cF.cjs +36 -0
  55. package/dist/label-w6JYf2cF.cjs.map +1 -0
  56. package/dist/markdown/index.cjs +1 -1
  57. package/dist/markdown/index.d.cts +2 -3
  58. package/dist/markdown/index.d.ts +2 -3
  59. package/dist/markdown/index.js +1 -1
  60. package/dist/page-flow-route-hdnULWqd.cjs +277 -0
  61. package/dist/page-flow-route-hdnULWqd.cjs.map +1 -0
  62. package/dist/page-flow-route-i3ZfJ1sd.js +277 -0
  63. package/dist/page-flow-route-i3ZfJ1sd.js.map +1 -0
  64. package/dist/place-node-C5SmIAYU.cjs +8139 -0
  65. package/dist/place-node-C5SmIAYU.cjs.map +1 -0
  66. package/dist/place-node-NraNYKPx.js +7597 -0
  67. package/dist/place-node-NraNYKPx.js.map +1 -0
  68. package/dist/related-list-cards-BWZ9GfOG.js +42 -0
  69. package/dist/related-list-cards-BWZ9GfOG.js.map +1 -0
  70. package/dist/related-list-cards-CFq3meUn.cjs +42 -0
  71. package/dist/related-list-cards-CFq3meUn.cjs.map +1 -0
  72. package/dist/related-list-timeline-CCzl71aA.cjs +142 -0
  73. package/dist/related-list-timeline-CCzl71aA.cjs.map +1 -0
  74. package/dist/related-list-timeline-yrPsjA3F.js +142 -0
  75. package/dist/related-list-timeline-yrPsjA3F.js.map +1 -0
  76. package/dist/related-rows-ea43qhhy.cjs +208 -0
  77. package/dist/related-rows-ea43qhhy.cjs.map +1 -0
  78. package/dist/related-rows-o0_t-Yce.js +185 -0
  79. package/dist/related-rows-o0_t-Yce.js.map +1 -0
  80. package/dist/relation-create-IA40t1hi.cjs +364 -0
  81. package/dist/relation-create-IA40t1hi.cjs.map +1 -0
  82. package/dist/relation-create-h6F9B4C4.js +339 -0
  83. package/dist/relation-create-h6F9B4C4.js.map +1 -0
  84. package/dist/relation-many-picker-C8zFEjwd.cjs +467 -0
  85. package/dist/relation-many-picker-C8zFEjwd.cjs.map +1 -0
  86. package/dist/relation-many-picker-bvkjkv-k.js +466 -0
  87. package/dist/relation-many-picker-bvkjkv-k.js.map +1 -0
  88. package/dist/relation-picker-Z0ZJJ5uS.js +493 -0
  89. package/dist/relation-picker-Z0ZJJ5uS.js.map +1 -0
  90. package/dist/relation-picker-dialog-0IzkjmiA.cjs +304 -0
  91. package/dist/relation-picker-dialog-0IzkjmiA.cjs.map +1 -0
  92. package/dist/relation-picker-dialog-B6nIiJBf.js +303 -0
  93. package/dist/relation-picker-dialog-B6nIiJBf.js.map +1 -0
  94. package/dist/relation-picker-gag0qSR5.cjs +516 -0
  95. package/dist/relation-picker-gag0qSR5.cjs.map +1 -0
  96. package/dist/resource-page-router-3RUf9d1j.js +1588 -0
  97. package/dist/resource-page-router-3RUf9d1j.js.map +1 -0
  98. package/dist/resource-page-router-PXfMW9nh.cjs +1785 -0
  99. package/dist/resource-page-router-PXfMW9nh.cjs.map +1 -0
  100. package/dist/{rich-text-IDFvyvRE.js → rich-text-DFerywgz.js} +7 -4
  101. package/dist/{rich-text-IDFvyvRE.js.map → rich-text-DFerywgz.js.map} +1 -1
  102. package/dist/{rich-text-lndbYqzR.cjs → rich-text-vTyVjDoa.cjs} +25 -22
  103. package/dist/{rich-text-lndbYqzR.cjs.map → rich-text-vTyVjDoa.cjs.map} +1 -1
  104. package/dist/search-access-CPPmfuvy.js +654 -0
  105. package/dist/search-access-CPPmfuvy.js.map +1 -0
  106. package/dist/search-access-Dugvttlg.cjs +791 -0
  107. package/dist/search-access-Dugvttlg.cjs.map +1 -0
  108. package/dist/shell-BPlk8hEa.js +205 -0
  109. package/dist/shell-BPlk8hEa.js.map +1 -0
  110. package/dist/shell-DxhlsPwm.cjs +271 -0
  111. package/dist/shell-DxhlsPwm.cjs.map +1 -0
  112. package/dist/skeleton-U2noefj5.js +13 -0
  113. package/dist/skeleton-U2noefj5.js.map +1 -0
  114. package/dist/skeleton-_1gP7wan.cjs +18 -0
  115. package/dist/skeleton-_1gP7wan.cjs.map +1 -0
  116. package/dist/styles.css +241 -9
  117. package/dist/toaster-C02XbB9r.js +135 -0
  118. package/dist/toaster-C02XbB9r.js.map +1 -0
  119. package/dist/toaster-Cr51X7bz.cjs +195 -0
  120. package/dist/toaster-Cr51X7bz.cjs.map +1 -0
  121. package/dist/use-dialog-filter-state-C--oMAZG.js +87 -0
  122. package/dist/use-dialog-filter-state-C--oMAZG.js.map +1 -0
  123. package/dist/use-dialog-filter-state-Dhe8PcB5.cjs +92 -0
  124. package/dist/use-dialog-filter-state-Dhe8PcB5.cjs.map +1 -0
  125. package/dist/{use-theme-BwPWinB2.cjs → use-theme-DFlpXils.cjs} +12 -1
  126. package/dist/use-theme-DFlpXils.cjs.map +1 -0
  127. package/dist/{use-theme-IHmtrhEt.js → use-theme-dnE3Od3I.js} +7 -2
  128. package/dist/use-theme-dnE3Od3I.js.map +1 -0
  129. package/package.json +16 -14
  130. package/dist/use-theme-BwPWinB2.cjs.map +0 -1
  131. package/dist/use-theme-IHmtrhEt.js.map +0 -1
@@ -0,0 +1,704 @@
1
+ import { Z as DocInfo } from "./check-confirm-dialog-bRT8lt0d.js";
2
+ import { t as Button } from "./button-MWVeZaqj.js";
3
+ import { n as OwnTitleContext } from "./shell-BPlk8hEa.js";
4
+ import { V as ScreenTree, kt as FieldDisplay, n as viewOf, t as selectImplementation } from "./place-node-NraNYKPx.js";
5
+ import { fieldSensitivity, humanizeKey, validatePlacementProps } from "@burojs/core";
6
+ import { ScreenStateProvider, resolveValidationMessage, useDismissPending, useFlowFormCatalog, useFlowOpenImplementation, useFormController, useI18nCaps, useOptionalT, useProjectedTitle, useTestIds, useUIInstance, useUIPresentationRuntime } from "@burojs/react";
7
+ import { IconLoader2, IconX } from "@tabler/icons-react";
8
+ import { Suspense, createContext, useContext, useEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
9
+ import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
10
+ import * as DialogPrimitive from "@radix-ui/react-dialog";
11
+ import { fieldEntriesToMap } from "@burojs/core/internal";
12
+ import { FlowTextsScope, useFlowForm, useFlowRun } from "@burojs/react/flows";
13
+ //#region src/flows/review-diff.tsx
14
+ const REVIEW_DIFF_BLOCK = "buro:review-diff";
15
+ function sensitive(definition) {
16
+ if (fieldSensitivity(definition) !== void 0) return true;
17
+ if (definition === null || typeof definition !== "object") return false;
18
+ const opts = definition.opts;
19
+ const marked = opts !== null && typeof opts === "object" ? opts.sensitive : void 0;
20
+ return marked !== void 0 && marked !== false && marked !== null;
21
+ }
22
+ const same = (a, b) => {
23
+ if (Object.is(a, b)) return true;
24
+ const empty = (value) => value === void 0 || value === null || value === "";
25
+ if (empty(a) && empty(b)) return true;
26
+ try {
27
+ return JSON.stringify(a) === JSON.stringify(b);
28
+ } catch {
29
+ return false;
30
+ }
31
+ };
32
+ function ReviewDiff({ form, initial, values, i18nPrefix, testid }) {
33
+ const t = useOptionalT();
34
+ const changedText = (() => {
35
+ const value = t("review.changed");
36
+ return value === "review.changed" ? "changed" : value;
37
+ })();
38
+ const rows = form.fields.filter((binding) => !same(initial[binding.name], values[binding.name]));
39
+ return jsx("dl", {
40
+ "data-testid": `${testid}:review`,
41
+ "data-buro-block": REVIEW_DIFF_BLOCK,
42
+ className: "grid gap-2",
43
+ children: rows.map((binding) => {
44
+ const definition = binding.field;
45
+ const label = typeof definition.label === "string" ? definition.label : humanizeKey(binding.name);
46
+ const hidden = sensitive(binding.field);
47
+ return jsxs("div", {
48
+ "data-testid": `${testid}:review:${binding.name}`,
49
+ className: "grid grid-cols-[minmax(0,1fr)_minmax(0,2fr)] gap-2 text-sm",
50
+ children: [jsx("dt", {
51
+ className: "font-medium",
52
+ children: label
53
+ }), jsx("dd", {
54
+ className: "flex flex-wrap items-center gap-2",
55
+ children: hidden ? jsx("span", {
56
+ className: "text-muted-foreground",
57
+ children: changedText
58
+ }) : jsxs(Fragment$1, { children: [
59
+ jsx("span", {
60
+ "data-buro-review": "old",
61
+ className: "text-muted-foreground line-through",
62
+ children: jsx(FieldDisplay, {
63
+ i18nPrefix,
64
+ name: binding.name,
65
+ field: binding.field,
66
+ value: initial[binding.name],
67
+ fallback: jsx("span", { children: display(initial[binding.name]) })
68
+ })
69
+ }),
70
+ jsx("span", {
71
+ "aria-hidden": "true",
72
+ children: "→"
73
+ }),
74
+ jsx("span", {
75
+ "data-buro-review": "new",
76
+ children: jsx(FieldDisplay, {
77
+ i18nPrefix,
78
+ name: binding.name,
79
+ field: binding.field,
80
+ value: values[binding.name],
81
+ fallback: jsx("span", { children: display(values[binding.name]) })
82
+ })
83
+ })
84
+ ] })
85
+ })]
86
+ }, binding.name);
87
+ })
88
+ });
89
+ }
90
+ const display = (value) => value === void 0 || value === null || value === "" ? "—" : String(value);
91
+ //#endregion
92
+ //#region src/flows/flow-screen.tsx
93
+ const HELD = Object.freeze({
94
+ name: "AbortError",
95
+ message: "The flow run sends the values."
96
+ });
97
+ function HostedFlowScreen(props) {
98
+ const { binding, ...rest } = props;
99
+ return jsx(FlowHostBindingContext.Provider, {
100
+ value: binding,
101
+ children: jsx(FlowScreen, { ...rest })
102
+ });
103
+ }
104
+ const FlowHostBindingContext = createContext(null);
105
+ function OverlayBinding({ children }) {
106
+ const instance = useUIInstance();
107
+ const runtime = useUIPresentationRuntime();
108
+ const binding = useMemo(() => ({
109
+ kind: "overlay",
110
+ close: () => instance.dismiss(),
111
+ finish: () => runtime.dismiss(instance.id)
112
+ }), [instance, runtime]);
113
+ return jsx(FlowHostBindingContext.Provider, {
114
+ value: binding,
115
+ children
116
+ });
117
+ }
118
+ function WithBinding({ children }) {
119
+ return useContext(FlowHostBindingContext) !== null ? jsx(Fragment$1, { children }) : jsx(OverlayBinding, { children });
120
+ }
121
+ function text(t, key, fallback, params) {
122
+ const value = t(key, params === void 0 ? void 0 : { params });
123
+ return value === key ? fallback : value;
124
+ }
125
+ const REJECTED_KEYS = {
126
+ validation: "validation",
127
+ conflict: "conflict",
128
+ forbidden: "forbidden",
129
+ "not-found": "notFound"
130
+ };
131
+ function outcomeMessage(snapshot, busy, t) {
132
+ if (busy) return text(t, "flow.busyElsewhere", "This action is running in another tab.");
133
+ const outcome = snapshot.outcome;
134
+ if (outcome === void 0 || outcome.outcome === "success") {
135
+ if (snapshot.control === "disabled" && snapshot.reason !== void 0) return snapshot.reason;
136
+ return null;
137
+ }
138
+ if (outcome.outcome === "unknown") return [text(t, "flow.unknown", "The request timed out. It may have been applied. Nothing was resent."), ...snapshot.reason === void 0 ? [] : [snapshot.reason]].join(" ");
139
+ if (outcome.outcome === "rejected") {
140
+ if (snapshot.reason !== void 0) return snapshot.reason;
141
+ const kind = "errorKind" in outcome ? outcome.errorKind : void 0;
142
+ const key = kind === void 0 ? void 0 : REJECTED_KEYS[kind];
143
+ if (key !== void 0) return text(t, `flow.rejected.${key}`, "Nothing was saved.");
144
+ return text(t, "flow.rejected.precondition", "The record changed. Nothing was sent.");
145
+ }
146
+ if (outcome.outcome === "failed") return snapshot.reason ?? text(t, "flow.failed", "The request could not be sent. Nothing was saved.");
147
+ return null;
148
+ }
149
+ function FlowScreen(props) {
150
+ if (props.run !== void 0 && props.compiled !== void 0) return jsx(HeldFlowScreen, {
151
+ ...props,
152
+ run: props.run,
153
+ compiled: props.compiled
154
+ });
155
+ if (props.start !== void 0) return jsx(OwnedFlowScreen, {
156
+ ...props,
157
+ start: props.start
158
+ });
159
+ return null;
160
+ }
161
+ function HeldFlowScreen(props) {
162
+ const [generation, setGeneration] = useState(0);
163
+ return jsx(FlowTextsScope, {
164
+ compiled: props.compiled,
165
+ children: jsx(WithBinding, { children: jsx(ScreenStateProvider, { children: jsx(FlowScreenBody, {
166
+ ...props,
167
+ onFreshForm: () => setGeneration((value) => value + 1)
168
+ }) }, generation) })
169
+ });
170
+ }
171
+ function OwnedFlowScreen(props) {
172
+ const { start, effects, onSettle } = props;
173
+ const flow = useFlowRun(start.target, {
174
+ ...start.record === void 0 ? {} : { record: start.record },
175
+ ...start.args === void 0 ? {} : { args: start.args },
176
+ ...start.selection === void 0 ? {} : { selection: start.selection },
177
+ surface: "host",
178
+ ...effects === void 0 ? {} : { thenEffects: effects }
179
+ });
180
+ flow.holdThen(true);
181
+ const latest = useRef(flow);
182
+ latest.current = flow;
183
+ useEffect(() => () => {
184
+ setTimeout(() => {
185
+ latest.current.holdThen(false);
186
+ latest.current.runThen();
187
+ }, 0);
188
+ }, []);
189
+ const { run, snapshot, compiled } = flow;
190
+ const outcome = snapshot.outcome;
191
+ useEffect(() => {
192
+ if (outcome !== void 0) onSettle?.(outcome);
193
+ }, [outcome, onSettle]);
194
+ const instance = useUIInstance();
195
+ const t = useOptionalT();
196
+ const tid = useTestIds();
197
+ if (run !== null && compiled !== null && (snapshot.history.includes("open") || snapshot.step === "outcome")) return jsx(HeldFlowScreen, {
198
+ ...props,
199
+ run,
200
+ compiled
201
+ });
202
+ const refused = snapshot.control === "disabled" || snapshot.control === "absent";
203
+ return jsxs("div", {
204
+ "data-testid": compiled === null ? void 0 : `${tid.flowHost(compiled.flowId)}:opening`,
205
+ "data-buro-host": props.host,
206
+ "data-buro-flow-step": refused ? "refused" : "opening",
207
+ "aria-busy": refused ? void 0 : "true",
208
+ className: "flex min-h-0 flex-1 flex-col gap-4",
209
+ children: [
210
+ jsx(OwnTitleHeading, { label: props.label }),
211
+ refused ? jsx("p", {
212
+ role: "alert",
213
+ className: "text-sm",
214
+ children: snapshot.reason ?? text(t, "flow.unavailable", "Not allowed.")
215
+ }) : jsx(IconLoader2, {
216
+ className: "h-5 w-5 animate-spin self-center",
217
+ "aria-hidden": "true"
218
+ }),
219
+ jsx("div", {
220
+ className: "flex justify-end",
221
+ children: jsx(Button, {
222
+ type: "button",
223
+ variant: "ghost",
224
+ onClick: () => instance.dismiss(),
225
+ children: text(t, "flow.close", "Close")
226
+ })
227
+ })
228
+ ]
229
+ });
230
+ }
231
+ function OwnTitleHeading({ label }) {
232
+ const ownTitle = useContext(OwnTitleContext);
233
+ const heading = jsx("h2", {
234
+ tabIndex: -1,
235
+ "data-buro-overlay-heading": "",
236
+ className: "text-lg font-semibold",
237
+ children: label
238
+ });
239
+ return ownTitle ? jsx(DialogPrimitive.Title, {
240
+ asChild: true,
241
+ children: heading
242
+ }) : heading;
243
+ }
244
+ function FlowScreenBody({ run, host, label, title: givenTitle, compiled, record, args, selection, onFreshForm }) {
245
+ const snapshot = useSyncSnapshot(run);
246
+ const binding = useContext(FlowHostBindingContext);
247
+ const tid = useTestIds();
248
+ const t = useOptionalT();
249
+ const resource = compiled.resource ?? void 0;
250
+ const caps = useI18nCaps(resource);
251
+ const ownTitle = useContext(OwnTitleContext);
252
+ const flowId = compiled.flowId;
253
+ const recordId = record?.[compiled.idField] ?? record?.id;
254
+ const recordTitle = useProjectedTitle(resource, recordId, record);
255
+ const title = givenTitle ?? (record === void 0 || recordTitle === "" ? void 0 : recordTitle);
256
+ const form = useFlowForm(run, compiled, snapshot, {
257
+ ...record === void 0 ? {} : { record },
258
+ ...args === void 0 ? {} : { args },
259
+ ...selection === void 0 ? {} : { selection: selection.ids }
260
+ });
261
+ const hasForm = compiled.flow.form !== void 0;
262
+ const [busy, setBusy] = useState(false);
263
+ const [initial] = useState(() => run.getSnapshot().values);
264
+ const formErrors = form.errors.filter((issue) => issue.field === "_form");
265
+ const locked = snapshot.control === "running";
266
+ useDismissPending(locked);
267
+ const outcome = snapshot.outcome?.outcome;
268
+ const unknown = outcome === "unknown";
269
+ const succeeded = outcome === "success";
270
+ const finalStep = snapshot.step === "review" || snapshot.step === "accept";
271
+ const openBlock = succeeded ? blockOpen(snapshot.effects?.open) : null;
272
+ useEffect(() => {
273
+ if (succeeded && !locked && openBlock === null) binding.finish();
274
+ }, [
275
+ succeeded,
276
+ locked,
277
+ openBlock,
278
+ binding
279
+ ]);
280
+ const prefix = compiled.resource === null ? `appActions.${compiled.name}` : `${compiled.resource}.actions.${compiled.name}`;
281
+ const settle = (result) => {
282
+ setBusy(result.outcome === "failed" && "reason" in result && result.reason === "busy");
283
+ };
284
+ const submit = async () => {
285
+ if (locked) return;
286
+ setBusy(false);
287
+ if (finalStep) {
288
+ settle(await run.accept());
289
+ return;
290
+ }
291
+ if (unknown && editingNew) {
292
+ let valid = false;
293
+ await form.handleSubmit(() => {
294
+ valid = true;
295
+ throw HELD;
296
+ })();
297
+ if (valid) setConfirmingNew(true);
298
+ return;
299
+ }
300
+ if (unknown) return;
301
+ if (hasForm) {
302
+ let validated = false;
303
+ await form.handleSubmit(() => {
304
+ validated = true;
305
+ throw HELD;
306
+ })();
307
+ if (!validated) return;
308
+ }
309
+ settle(await run.submit());
310
+ };
311
+ const pendingKey = compiled.resource === null ? `appActions.${compiled.name}.pending` : `resources.${compiled.resource}.actions.${compiled.name}.pending`;
312
+ const pendingLabel = caps.has(pendingKey) ? caps.t(pendingKey) : `${label}…`;
313
+ const message = outcomeMessage(snapshot, busy, t);
314
+ const couldNotCheck = snapshot.control === "disabled" && snapshot.reason !== void 0 && snapshot.reason === text(t, "flow.couldNotCheck", "Couldn't check");
315
+ const steps = form.steps;
316
+ const lastStep = steps === null || steps.ids.length === 0 || steps.current === steps.ids[steps.ids.length - 1];
317
+ const showSubmit = !unknown && !succeeded && (!hasForm || finalStep || lastStep);
318
+ const [confirmingNew, setConfirmingNew] = useState(false);
319
+ const [editingNew, setEditingNew] = useState(false);
320
+ const checkable = compiled.flow.submit.onUnknown !== void 0;
321
+ const unhandled = unknown && !checkable && !snapshot.canResend;
322
+ const exit = (action) => () => {
323
+ if (!locked) action().then(settle);
324
+ };
325
+ const close = () => {
326
+ if (binding.kind === "inline" && hasForm && !unknown) form.draft.discard();
327
+ binding.close();
328
+ };
329
+ const heading = jsxs("h2", {
330
+ tabIndex: -1,
331
+ "data-buro-overlay-heading": "",
332
+ className: "min-w-0 flex-1 break-words text-lg font-semibold tracking-tight focus-visible:outline-hidden",
333
+ children: [label, title === void 0 ? null : jsx("span", {
334
+ className: "ml-2 font-normal text-muted-foreground",
335
+ children: title
336
+ })]
337
+ });
338
+ return jsxs("div", {
339
+ "data-testid": tid.flowHost(flowId),
340
+ "data-buro-host": host,
341
+ "data-buro-flow-step": snapshot.step,
342
+ "aria-busy": locked ? "true" : void 0,
343
+ className: "flex min-h-0 flex-1 flex-col gap-4",
344
+ children: [
345
+ binding.kind === "inline" ? null : jsxs("div", {
346
+ "data-buro-region": "flow-header",
347
+ className: "flex items-center gap-2 pr-1",
348
+ children: [
349
+ ownTitle ? jsx(DialogPrimitive.Title, {
350
+ asChild: true,
351
+ children: heading
352
+ }) : heading,
353
+ compiled.resource === null ? null : jsx(DocInfo, {
354
+ address: `${compiled.resource}/actions/${compiled.name}`,
355
+ name: label,
356
+ testId: `${tid.flowHost(flowId)}:info`
357
+ }),
358
+ binding.kind === "overlay" ? jsx(Button, {
359
+ type: "button",
360
+ variant: "ghost",
361
+ size: "icon",
362
+ className: "h-8 w-8 shrink-0",
363
+ "data-testid": tid.flowClose(flowId),
364
+ "aria-label": text(t, "flow.close", "Close"),
365
+ onClick: close,
366
+ children: jsx(IconX, {
367
+ className: "h-4 w-4",
368
+ "aria-hidden": "true"
369
+ })
370
+ }) : null
371
+ ]
372
+ }),
373
+ openBlock !== null ? jsx(FlowOpenBlock, {
374
+ open: openBlock,
375
+ prefix,
376
+ testid: tid.flowHost(flowId)
377
+ }) : null,
378
+ openBlock !== null && binding.kind !== "overlay" ? jsx("div", {
379
+ className: "flex justify-end",
380
+ children: jsx(Button, {
381
+ type: "button",
382
+ variant: "outline",
383
+ "data-testid": tid.flowClose(flowId),
384
+ onClick: close,
385
+ children: text(t, "flow.done", "Done")
386
+ })
387
+ }) : null,
388
+ jsxs("form", {
389
+ hidden: openBlock !== null,
390
+ noValidate: true,
391
+ className: openBlock === null ? "flex min-h-0 flex-1 flex-col gap-4" : "hidden",
392
+ onSubmit: (event) => {
393
+ event.preventDefault();
394
+ submit();
395
+ },
396
+ children: [
397
+ jsxs("div", {
398
+ className: "-mx-1 flex min-h-0 flex-1 flex-col gap-4 overflow-y-auto px-1",
399
+ children: [
400
+ message === null ? null : jsxs("div", {
401
+ role: unknown || outcome === "rejected" || outcome === "failed" ? "alert" : "status",
402
+ "data-testid": tid.flowOutcome(flowId),
403
+ "data-buro-flow-outcome": busy ? "busy" : outcome ?? snapshot.control,
404
+ className: "flex flex-wrap items-center gap-2 rounded-md border border-destructive/30 bg-destructive/10 px-3 py-2 text-sm text-destructive",
405
+ children: [jsx("span", { children: message }), couldNotCheck ? jsx(Button, {
406
+ type: "button",
407
+ size: "sm",
408
+ variant: "outline",
409
+ className: "ml-auto",
410
+ onClick: () => run.retryChecks(),
411
+ children: text(t, "actions.retry", "Retry")
412
+ }) : null]
413
+ }),
414
+ hasForm && form.draft.pending !== null && (!unknown || editingNew) ? jsxs("div", {
415
+ "data-testid": tid.flowDraft(flowId),
416
+ role: "status",
417
+ "aria-live": "polite",
418
+ className: "flex flex-wrap items-center gap-2 rounded-md border border-warning/50 bg-warning/10 px-3 py-2 text-sm",
419
+ children: [jsx("span", {
420
+ className: "font-medium",
421
+ children: text(t, "draft.restorePrompt", "Restore draft?")
422
+ }), jsxs("div", {
423
+ className: "ml-auto flex gap-2",
424
+ children: [jsx(Button, {
425
+ type: "button",
426
+ size: "sm",
427
+ variant: "outline",
428
+ "data-testid": `${tid.flowDraft(flowId)}:restore`,
429
+ onClick: form.draft.restore,
430
+ children: text(t, "draft.restore", "Restore")
431
+ }), jsx(Button, {
432
+ type: "button",
433
+ size: "sm",
434
+ variant: "ghost",
435
+ "data-testid": `${tid.flowDraft(flowId)}:discard`,
436
+ onClick: form.draft.discard,
437
+ children: text(t, "draft.discard", "Discard")
438
+ })]
439
+ })]
440
+ }) : null,
441
+ formErrors.length > 0 ? jsx("div", {
442
+ role: "alert",
443
+ "data-testid": `${tid.flowHost(flowId)}:form-errors`,
444
+ className: "flex flex-col gap-1 rounded-md border border-destructive/30 bg-destructive/10 px-3 py-2 text-sm text-destructive",
445
+ children: formErrors.map((issue, index) => jsx("p", { children: resolveValidationMessage({
446
+ code: issue.code ?? "",
447
+ message: issue.message,
448
+ ...issue.params === void 0 ? {} : { params: issue.params }
449
+ }, { t }) }, index))
450
+ }) : null,
451
+ finalStep && compiled.flow.review === true ? jsx(ReviewDiff, {
452
+ form,
453
+ initial,
454
+ values: snapshot.values,
455
+ i18nPrefix: prefix,
456
+ testid: tid.flowHost(flowId)
457
+ }) : finalStep && Array.isArray(compiled.flow.review) ? jsx(ReviewTree, {
458
+ compiled,
459
+ tree: compiled.flow.review,
460
+ values: snapshot.values,
461
+ prefix
462
+ }) : hasForm ? jsx(ScreenTree, {
463
+ region: "form-body",
464
+ resource: prefix,
465
+ mode: "create",
466
+ compiled: form.screen,
467
+ controller: form,
468
+ leafIds: "record",
469
+ disabled: locked || unknown && !editingNew || finalStep || succeeded
470
+ }) : null,
471
+ finalStep && snapshot.warnings.length > 0 ? jsx("ul", {
472
+ role: "status",
473
+ "data-buro-region": "flow-warnings",
474
+ className: "flex flex-col gap-1 rounded-md border border-warning/50 bg-warning/10 px-3 py-2 text-sm text-warning-emphasis",
475
+ children: snapshot.warnings.map((warning) => jsx("li", {
476
+ "data-testid": `${tid.flowHost(flowId)}:warning:${warning.id}`,
477
+ children: warning.message
478
+ }, warning.id))
479
+ }) : null
480
+ ]
481
+ }),
482
+ unhandled ? jsx("p", {
483
+ "data-testid": `${tid.flowOutcome(flowId)}:unhandled`,
484
+ className: "text-sm",
485
+ children: text(t, "flow.unknownUnhandled", "Check the record before trying again.")
486
+ }) : null,
487
+ unknown && confirmingNew ? jsxs("div", {
488
+ role: "alertdialog",
489
+ "aria-label": text(t, "flow.sendAsNew", "Send as new"),
490
+ "data-testid": `${tid.flowSendAsNew(flowId)}:confirm`,
491
+ className: "flex flex-wrap items-center gap-2 rounded-md border border-warning/50 bg-warning/10 px-3 py-2 text-sm",
492
+ children: [jsx("span", { children: text(t, "flow.sendAsNewConfirm", "This may apply the change twice. Send it as a new request?") }), jsxs("div", {
493
+ className: "ml-auto flex gap-2",
494
+ children: [jsx(Button, {
495
+ type: "button",
496
+ size: "sm",
497
+ variant: "ghost",
498
+ "data-testid": `${tid.flowSendAsNew(flowId)}:cancel`,
499
+ onClick: () => setConfirmingNew(false),
500
+ children: text(t, "actions.cancel", "Cancel")
501
+ }), jsx(Button, {
502
+ type: "button",
503
+ size: "sm",
504
+ variant: "destructive",
505
+ "data-testid": `${tid.flowSendAsNew(flowId)}:ok`,
506
+ onClick: () => {
507
+ setConfirmingNew(false);
508
+ exit(() => run.sendAsNew({ confirmed: true }))();
509
+ },
510
+ children: text(t, "flow.sendAsNew", "Send as new")
511
+ })]
512
+ })]
513
+ }) : null,
514
+ unknown ? jsxs("div", {
515
+ "data-buro-region": "flow-unknown-exits",
516
+ className: "flex flex-wrap gap-2",
517
+ children: [
518
+ checkable ? jsx(Button, {
519
+ type: "button",
520
+ variant: "default",
521
+ "data-testid": tid.flowCheckAgain(flowId),
522
+ disabled: locked,
523
+ onClick: exit(() => run.checkAgain()),
524
+ children: text(t, "flow.checkAgain", "Check again")
525
+ }) : null,
526
+ snapshot.canResend ? jsx(Button, {
527
+ type: "button",
528
+ variant: "outline",
529
+ "data-testid": tid.flowResend(flowId),
530
+ disabled: locked,
531
+ onClick: exit(() => run.resend()),
532
+ children: text(t, "flow.resend", "Resend")
533
+ }) : null,
534
+ jsx(Button, {
535
+ type: "button",
536
+ variant: "outline",
537
+ "data-testid": tid.flowSendAsNew(flowId),
538
+ disabled: locked || confirmingNew || editingNew,
539
+ onClick: () => {
540
+ if (hasForm) setEditingNew(true);
541
+ else setConfirmingNew(true);
542
+ },
543
+ children: text(t, "flow.sendAsNew", "Send as new")
544
+ }),
545
+ jsx(Button, {
546
+ type: "button",
547
+ variant: "outline",
548
+ "data-testid": tid.flowStartOver(flowId),
549
+ disabled: locked,
550
+ onClick: () => {
551
+ run.startOver();
552
+ onFreshForm();
553
+ },
554
+ children: text(t, "flow.startOver", "I've checked — start over")
555
+ })
556
+ ]
557
+ }) : null,
558
+ jsxs("div", {
559
+ "data-buro-region": "flow-footer",
560
+ className: "flex flex-wrap items-center gap-2",
561
+ children: [finalStep && hasForm && !locked ? jsx(Button, {
562
+ type: "button",
563
+ variant: "outline",
564
+ "data-testid": tid.flowBack(flowId),
565
+ onClick: () => run.back(),
566
+ children: text(t, "flow.back", "Back")
567
+ }) : null, jsxs("div", {
568
+ className: "ml-auto flex gap-2",
569
+ children: [
570
+ jsx(Button, {
571
+ type: "button",
572
+ variant: "ghost",
573
+ "data-testid": tid.flowCancel(flowId),
574
+ disabled: locked,
575
+ onClick: close,
576
+ children: unknown ? text(t, "flow.close", "Close") : binding.kind === "inline" ? text(t, "flow.reset", "Reset") : text(t, "actions.cancel", "Cancel")
577
+ }),
578
+ unknown && editingNew ? jsx(Button, {
579
+ type: "submit",
580
+ variant: "destructive",
581
+ "data-testid": `${tid.flowSendAsNew(flowId)}:submit`,
582
+ disabled: locked || confirmingNew,
583
+ onMouseDown: (event) => event.preventDefault(),
584
+ children: text(t, "flow.sendAsNew", "Send as new")
585
+ }) : null,
586
+ showSubmit ? jsxs(Button, {
587
+ type: "submit",
588
+ variant: compiled.flow.variant === "destructive" ? "destructive" : "default",
589
+ "data-testid": tid.flowSubmit(flowId),
590
+ disabled: locked || couldNotCheck,
591
+ "aria-busy": locked ? "true" : void 0,
592
+ onMouseDown: (event) => event.preventDefault(),
593
+ children: [locked ? jsx(IconLoader2, {
594
+ className: "h-4 w-4 animate-spin",
595
+ "aria-hidden": "true"
596
+ }) : null, locked ? pendingLabel : label]
597
+ }) : null
598
+ ]
599
+ })]
600
+ })
601
+ ]
602
+ })
603
+ ]
604
+ });
605
+ }
606
+ function useSyncSnapshot(run) {
607
+ return useSyncExternalStore(run.subscribe, run.getSnapshot, run.getSnapshot);
608
+ }
609
+ const NO_FIELDS = Object.freeze({});
610
+ function ReviewTree({ compiled, tree, values, prefix }) {
611
+ const fields = compiled.flow.form?.fields;
612
+ const map = useState(() => {
613
+ if (fields === void 0) return NO_FIELDS;
614
+ const placed = placedLeaves(tree);
615
+ return fieldEntriesToMap(fields.map((field) => placed.has(String(field.name)) ? field : {
616
+ ...field,
617
+ in: ["create"]
618
+ }));
619
+ })[0];
620
+ const body = useState(() => () => tree)[0];
621
+ const blocks = useFlowFormCatalog();
622
+ const review = useFormController({
623
+ blocks,
624
+ fields: map,
625
+ body,
626
+ mode: "show",
627
+ i18nPrefix: prefix,
628
+ value: values
629
+ });
630
+ return jsx(ScreenTree, {
631
+ region: "form-body",
632
+ resource: prefix,
633
+ mode: "show",
634
+ compiled: review.screen,
635
+ controller: review,
636
+ leafIds: "record"
637
+ });
638
+ }
639
+ function placedLeaves(tree) {
640
+ const out = new Set();
641
+ const walk = (node) => {
642
+ if (typeof node === "string") {
643
+ out.add(node);
644
+ return;
645
+ }
646
+ if (node === null || typeof node !== "object") return;
647
+ const children = node.children;
648
+ if (Array.isArray(children)) for (const child of children) walk(child);
649
+ };
650
+ for (const node of tree) walk(node);
651
+ return out;
652
+ }
653
+ function blockOpen(open) {
654
+ if (open === null || typeof open !== "object" || !Object.hasOwn(open, "block")) return null;
655
+ const { block, props } = open;
656
+ const ref = block !== null && typeof block === "object" && "ref" in block ? block.ref : block;
657
+ if (ref === null || typeof ref !== "object" || typeof ref.namespace !== "string" || typeof ref.id !== "string") return null;
658
+ return {
659
+ block: ref,
660
+ props: props !== null && typeof props === "object" ? props : {}
661
+ };
662
+ }
663
+ function FlowOpenBlock({ open, testid }) {
664
+ const t = useOptionalT();
665
+ const id = `${open.block.namespace}:${open.block.id}`;
666
+ const implementation = selectImplementation(id, useFlowOpenImplementation(id));
667
+ const [validation, setValidation] = useState(() => implementation === void 0 ? null : validatePlacementProps(implementation.definition, open.props));
668
+ useEffect(() => {
669
+ if (validation?.status !== "pending") return;
670
+ let live = true;
671
+ validation.settled.then((settled) => {
672
+ if (live) setValidation(settled);
673
+ });
674
+ return () => {
675
+ live = false;
676
+ };
677
+ }, [validation]);
678
+ const failed = implementation === void 0 || validation?.status === "failed";
679
+ const View = implementation === void 0 ? null : viewOf(implementation);
680
+ return jsx("div", {
681
+ "data-testid": `${testid}:open`,
682
+ "data-buro-block": id,
683
+ className: "min-h-0 flex-1 overflow-y-auto",
684
+ children: failed ? jsx("p", {
685
+ role: "alert",
686
+ className: "text-sm text-muted-foreground",
687
+ children: text(t, "block.notImplemented", "This section is not available.")
688
+ }) : View === null || validation?.status !== "ready" ? null : jsx(Suspense, {
689
+ fallback: null,
690
+ children: jsx(View, {
691
+ props: validation.props,
692
+ data: NO_FIELDS,
693
+ t: (key, options) => t(`blocks.${id}.${key}`, {
694
+ ...options?.params === void 0 ? {} : { params: options.params },
695
+ ...options?.count === void 0 ? {} : { count: options.count }
696
+ })
697
+ })
698
+ })
699
+ });
700
+ }
701
+ //#endregion
702
+ export { FlowHostBindingContext, FlowScreen, HostedFlowScreen };
703
+
704
+ //# sourceMappingURL=flow-screen-CCvgDI9G.js.map