@burojs/ui-shadcn 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.
Files changed (126) hide show
  1. package/dist/{bulk-summary-2HLgz7Il.cjs → bulk-summary-Chc61Pyw.cjs} +9 -7
  2. package/dist/bulk-summary-Chc61Pyw.cjs.map +1 -0
  3. package/dist/{bulk-summary-D6A0ua21.js → bulk-summary-DpSQD2Fq.js} +9 -7
  4. package/dist/bulk-summary-DpSQD2Fq.js.map +1 -0
  5. package/dist/{candidates-relation-picker-CGXItMN0.js → candidates-relation-picker-DRsHKe6N.js} +3 -3
  6. package/dist/{candidates-relation-picker-CGXItMN0.js.map → candidates-relation-picker-DRsHKe6N.js.map} +1 -1
  7. package/dist/{candidates-relation-picker-C47mF260.cjs → candidates-relation-picker-GIxsp7D_.cjs} +3 -3
  8. package/dist/{candidates-relation-picker-C47mF260.cjs.map → candidates-relation-picker-GIxsp7D_.cjs.map} +1 -1
  9. package/dist/{check-confirm-dialog-BT04Mieo.cjs → check-confirm-dialog-BMSb7wFv.cjs} +76 -9
  10. package/dist/check-confirm-dialog-BMSb7wFv.cjs.map +1 -0
  11. package/dist/{check-confirm-dialog-CtiRzOAt.js → check-confirm-dialog-B_u3YVHS.js} +72 -11
  12. package/dist/{check-confirm-dialog-CtiRzOAt.js.map → check-confirm-dialog-B_u3YVHS.js.map} +1 -1
  13. package/dist/{filter-builder-z3Axzf9Y.js → filter-builder-DX-xC3Yd.js} +14 -6
  14. package/dist/filter-builder-DX-xC3Yd.js.map +1 -0
  15. package/dist/{filter-builder-ByGhxLf_.cjs → filter-builder-Dt5E8PSX.cjs} +14 -6
  16. package/dist/filter-builder-Dt5E8PSX.cjs.map +1 -0
  17. package/dist/{flow-runtime-BVFjMU5-.cjs → flow-runtime-CQ4wf8co.cjs} +3 -3
  18. package/dist/{flow-runtime-BVFjMU5-.cjs.map → flow-runtime-CQ4wf8co.cjs.map} +1 -1
  19. package/dist/{flow-runtime-DrUWyTS0.js → flow-runtime-Q2vD7R3c.js} +3 -3
  20. package/dist/{flow-runtime-DrUWyTS0.js.map → flow-runtime-Q2vD7R3c.js.map} +1 -1
  21. package/dist/{flow-screen-3fk6J33y.js → flow-screen-B1y2yizP.js} +68 -29
  22. package/dist/flow-screen-B1y2yizP.js.map +1 -0
  23. package/dist/{flow-screen-6y5E_HT6.cjs → flow-screen-sw2cP70t.cjs} +67 -28
  24. package/dist/flow-screen-sw2cP70t.cjs.map +1 -0
  25. package/dist/index.cjs +2304 -247
  26. package/dist/index.cjs.map +1 -1
  27. package/dist/index.d.cts +119 -12
  28. package/dist/index.d.ts +119 -12
  29. package/dist/index.js +2225 -172
  30. package/dist/index.js.map +1 -1
  31. package/dist/{inline-flow-host-C4KmZIYE.cjs → inline-flow-host-CkhoA69R.cjs} +3 -3
  32. package/dist/{inline-flow-host-C4KmZIYE.cjs.map → inline-flow-host-CkhoA69R.cjs.map} +1 -1
  33. package/dist/{inline-flow-host-YnnvWjdU.js → inline-flow-host-DqbXqcPS.js} +3 -3
  34. package/dist/{inline-flow-host-YnnvWjdU.js.map → inline-flow-host-DqbXqcPS.js.map} +1 -1
  35. package/dist/{lazy-relation-picker-dialog-DN4bvQfg.js → lazy-relation-picker-dialog-BaIEWLbF.js} +3 -52
  36. package/dist/lazy-relation-picker-dialog-BaIEWLbF.js.map +1 -0
  37. package/dist/{lazy-relation-picker-dialog-BAKBRn3V.cjs → lazy-relation-picker-dialog-CeRzOAi0.cjs} +3 -52
  38. package/dist/lazy-relation-picker-dialog-CeRzOAi0.cjs.map +1 -0
  39. package/dist/{page-flow-route-CtEfqAvU.js → page-flow-route-CP7cS7FY.js} +7 -7
  40. package/dist/page-flow-route-CP7cS7FY.js.map +1 -0
  41. package/dist/{page-flow-route-DEjAxHe3.cjs → page-flow-route-UDx8qvdM.cjs} +10 -10
  42. package/dist/page-flow-route-UDx8qvdM.cjs.map +1 -0
  43. package/dist/{place-node-Bhl8B3fF.cjs → place-node-C5ZmuHaN.cjs} +208 -55
  44. package/dist/place-node-C5ZmuHaN.cjs.map +1 -0
  45. package/dist/{place-node-BdxT-30h.js → place-node-DwWAigv0.js} +205 -58
  46. package/dist/place-node-DwWAigv0.js.map +1 -0
  47. package/dist/record-trail-store-Cjo9vtod.cjs +126 -0
  48. package/dist/record-trail-store-Cjo9vtod.cjs.map +1 -0
  49. package/dist/record-trail-store-Dn6bVry4.js +91 -0
  50. package/dist/record-trail-store-Dn6bVry4.js.map +1 -0
  51. package/dist/{related-list-cards-Bc_vfCE_.cjs → related-list-cards-BypKfm9t.cjs} +3 -3
  52. package/dist/{related-list-cards-Bc_vfCE_.cjs.map → related-list-cards-BypKfm9t.cjs.map} +1 -1
  53. package/dist/{related-list-cards-BvkD5OcC.js → related-list-cards-Dcmruy09.js} +3 -3
  54. package/dist/{related-list-cards-BvkD5OcC.js.map → related-list-cards-Dcmruy09.js.map} +1 -1
  55. package/dist/{related-list-timeline-k0DtnxTs.cjs → related-list-timeline-D-KClgKb.cjs} +4 -4
  56. package/dist/related-list-timeline-D-KClgKb.cjs.map +1 -0
  57. package/dist/{related-list-timeline-DCrwN9oO.js → related-list-timeline-DZON_qSc.js} +4 -4
  58. package/dist/related-list-timeline-DZON_qSc.js.map +1 -0
  59. package/dist/{related-rows-BW9Jr07T.cjs → related-rows-DgF05ssi.cjs} +4 -4
  60. package/dist/{related-rows-BW9Jr07T.cjs.map → related-rows-DgF05ssi.cjs.map} +1 -1
  61. package/dist/{related-rows-CPE-9FFr.js → related-rows-ETZl5CB9.js} +4 -4
  62. package/dist/{related-rows-CPE-9FFr.js.map → related-rows-ETZl5CB9.js.map} +1 -1
  63. package/dist/{relation-create-DDEYcKfw.cjs → relation-create-B8zngAcA.cjs} +82 -13
  64. package/dist/relation-create-B8zngAcA.cjs.map +1 -0
  65. package/dist/{relation-create-C43cdPQt.js → relation-create-CeCWyxIb.js} +79 -16
  66. package/dist/relation-create-CeCWyxIb.js.map +1 -0
  67. package/dist/{relation-many-picker-BlrGRwMV.js → relation-many-picker-CT7nKfE2.js} +12 -10
  68. package/dist/relation-many-picker-CT7nKfE2.js.map +1 -0
  69. package/dist/{relation-many-picker-Br-zBHxT.cjs → relation-many-picker-Cq0EHY5I.cjs} +12 -10
  70. package/dist/relation-many-picker-Cq0EHY5I.cjs.map +1 -0
  71. package/dist/{relation-picker-x7P5HKie.js → relation-picker-CCyuMeGq.js} +13 -10
  72. package/dist/relation-picker-CCyuMeGq.js.map +1 -0
  73. package/dist/{relation-picker-ADjxI_TI.cjs → relation-picker-DmwTzgsd.cjs} +13 -10
  74. package/dist/relation-picker-DmwTzgsd.cjs.map +1 -0
  75. package/dist/{relation-picker-dialog-CyKliBkN.js → relation-picker-dialog-CoWKjNv8.js} +5 -5
  76. package/dist/relation-picker-dialog-CoWKjNv8.js.map +1 -0
  77. package/dist/{relation-picker-dialog-DimVQF9Y.cjs → relation-picker-dialog-mbwyNK43.cjs} +5 -5
  78. package/dist/relation-picker-dialog-mbwyNK43.cjs.map +1 -0
  79. package/dist/{relation-select-Cr3CfNLy.cjs → relation-select-CguIo2M5.cjs} +2 -2
  80. package/dist/{relation-select-Cr3CfNLy.cjs.map → relation-select-CguIo2M5.cjs.map} +1 -1
  81. package/dist/{relation-select-B8Fq4o-t.js → relation-select-nAYxhopN.js} +2 -2
  82. package/dist/{relation-select-B8Fq4o-t.js.map → relation-select-nAYxhopN.js.map} +1 -1
  83. package/dist/{success-notices-Dl3M1fZP.cjs → success-notices-Cl4b6MLQ.cjs} +3 -2
  84. package/dist/success-notices-Cl4b6MLQ.cjs.map +1 -0
  85. package/dist/{success-notices-LDEgEkc4.js → success-notices-CrcWOGSo.js} +3 -2
  86. package/dist/success-notices-CrcWOGSo.js.map +1 -0
  87. package/dist/{use-dialog-filter-state-tNvqAAjh.cjs → use-dialog-filter-state-CpWblz3C.cjs} +2 -2
  88. package/dist/{use-dialog-filter-state-tNvqAAjh.cjs.map → use-dialog-filter-state-CpWblz3C.cjs.map} +1 -1
  89. package/dist/{use-dialog-filter-state-B_MqzHaT.js → use-dialog-filter-state-ZYxOp5WO.js} +2 -2
  90. package/dist/{use-dialog-filter-state-B_MqzHaT.js.map → use-dialog-filter-state-ZYxOp5WO.js.map} +1 -1
  91. package/dist/{use-record-subtitle-bfwIoUme.cjs → use-record-subtitle-B4lTR7pf.cjs} +5 -8
  92. package/dist/use-record-subtitle-B4lTR7pf.cjs.map +1 -0
  93. package/dist/{use-record-subtitle-gIaCGgbI.js → use-record-subtitle-pWMBqQx9.js} +5 -8
  94. package/dist/use-record-subtitle-pWMBqQx9.js.map +1 -0
  95. package/package.json +9 -9
  96. package/dist/bulk-summary-2HLgz7Il.cjs.map +0 -1
  97. package/dist/bulk-summary-D6A0ua21.js.map +0 -1
  98. package/dist/check-confirm-dialog-BT04Mieo.cjs.map +0 -1
  99. package/dist/filter-builder-ByGhxLf_.cjs.map +0 -1
  100. package/dist/filter-builder-z3Axzf9Y.js.map +0 -1
  101. package/dist/flow-screen-3fk6J33y.js.map +0 -1
  102. package/dist/flow-screen-6y5E_HT6.cjs.map +0 -1
  103. package/dist/lazy-relation-picker-dialog-BAKBRn3V.cjs.map +0 -1
  104. package/dist/lazy-relation-picker-dialog-DN4bvQfg.js.map +0 -1
  105. package/dist/page-flow-route-CtEfqAvU.js.map +0 -1
  106. package/dist/page-flow-route-DEjAxHe3.cjs.map +0 -1
  107. package/dist/place-node-BdxT-30h.js.map +0 -1
  108. package/dist/place-node-Bhl8B3fF.cjs.map +0 -1
  109. package/dist/related-list-timeline-DCrwN9oO.js.map +0 -1
  110. package/dist/related-list-timeline-k0DtnxTs.cjs.map +0 -1
  111. package/dist/relation-create-C43cdPQt.js.map +0 -1
  112. package/dist/relation-create-DDEYcKfw.cjs.map +0 -1
  113. package/dist/relation-many-picker-BlrGRwMV.js.map +0 -1
  114. package/dist/relation-many-picker-Br-zBHxT.cjs.map +0 -1
  115. package/dist/relation-picker-ADjxI_TI.cjs.map +0 -1
  116. package/dist/relation-picker-dialog-CyKliBkN.js.map +0 -1
  117. package/dist/relation-picker-dialog-DimVQF9Y.cjs.map +0 -1
  118. package/dist/relation-picker-x7P5HKie.js.map +0 -1
  119. package/dist/resource-page-router-BRpUnhay.js +0 -1958
  120. package/dist/resource-page-router-BRpUnhay.js.map +0 -1
  121. package/dist/resource-page-router-qEd2xb55.cjs +0 -2167
  122. package/dist/resource-page-router-qEd2xb55.cjs.map +0 -1
  123. package/dist/success-notices-Dl3M1fZP.cjs.map +0 -1
  124. package/dist/success-notices-LDEgEkc4.js.map +0 -1
  125. package/dist/use-record-subtitle-bfwIoUme.cjs.map +0 -1
  126. package/dist/use-record-subtitle-gIaCGgbI.js.map +0 -1
@@ -1,6 +1,6 @@
1
1
  require("./toaster-BbByOytP.cjs");
2
- const require_check_confirm_dialog = require("./check-confirm-dialog-BT04Mieo.cjs");
3
- const require_success_notices = require("./success-notices-Dl3M1fZP.cjs");
2
+ const require_check_confirm_dialog = require("./check-confirm-dialog-BMSb7wFv.cjs");
3
+ const require_success_notices = require("./success-notices-Cl4b6MLQ.cjs");
4
4
  let _burojs_core = require("@burojs/core");
5
5
  let _burojs_react = require("@burojs/react");
6
6
  let react = require("react");
@@ -287,4 +287,4 @@ exports.AppFlowRunner = AppFlowRunner;
287
287
  exports.BulkFlowRunner = BulkFlowRunner;
288
288
  exports.FlowRunner = FlowRunner;
289
289
 
290
- //# sourceMappingURL=flow-runtime-BVFjMU5-.cjs.map
290
+ //# sourceMappingURL=flow-runtime-CQ4wf8co.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"flow-runtime-BVFjMU5-.cjs","names":["useOptionalT","APP_ACTION_RESOURCE","useI18nCaps","useNoticeSink","useState","useRef","useCallback","reasonText","toMessageRef","CheckConfirmDialog","useMemo","useFlowRun","useFlowChrome","recordKeyOf","useApp","useProjectedTitle","useFlowHost","useTestIds","makeTestId","useFlowDialogTexts"],"sources":["../src/flows/flow-runtime.tsx"],"sourcesContent":["'use client';\n/**\n * The flow runtime's side of the UI kit (P4, the coordinator's ruling on the bundle): every\n * component that holds a run. It loads on the first sign that a user means to run a flow (a\n * hover, a focus, a menu opening, at the latest the click) through `./load-runtime.js`, and with\n * it `@burojs/react/flows` and `@burojs/core/flows` — the flow machine stays out of an\n * application's first screen.\n */\nimport { toMessageRef } from '@burojs/core/internal';\nimport {\n APP_ACTION_RESOURCE,\n type BulkProgress,\n type FlowControlState,\n type FlowResult,\n type Row,\n type ValidationIssue,\n} from '@burojs/core';\nimport {\n type CheckGateState,\n recordKeyOf,\n useApp,\n useI18nCaps,\n useOptionalT,\n useProjectedTitle,\n useTestIds,\n} from '@burojs/react';\nimport {\n type FlowHostControl,\n type UseFlowRunResult,\n useFlowHost,\n useFlowRun,\n} from '@burojs/react/flows';\nimport { type ReactElement, useCallback, useEffect, useMemo, useRef, useState } from 'react';\nimport { makeTestId } from '../actions/action-button.js';\nimport { CheckConfirmDialog } from '../checks/check-confirm-dialog.js';\nimport { useNoticeSink } from '../forms/success-notices.js';\nimport { reasonText } from '../lib/reason-text.js';\nimport { toast } from '../notification/toaster.js';\nimport { type FlowChrome, useFlowChrome, useFlowDialogTexts } from './chrome.js';\n\nconst NO_ISSUES: readonly ValidationIssue[] = Object.freeze([]);\n\n/**\n * Runs a flow from its control (4.15): a flow with a host presents it and the interaction ends\n * when it closes; a `{ label, submit }` flow sends, asks through the confirm dialog when\n * `confirm` or a fired warning asks, and shows `rejected`, `failed` and `unknown` in the flow's\n * screen in a dialog (`success` notifies through the flow's `then` and shows the write's notices; a lock another tab holds\n * says so; an `unavailable` send toasts the run's reason). `launch()` settles when the interaction is over.\n */\nfunction useFlowLauncher(\n flow: UseFlowRunResult,\n host: FlowHostControl,\n chrome: FlowChrome,\n resource: string,\n testid: string,\n): { readonly launch: () => Promise<void>; readonly dialog: ReactElement | null } {\n const t = useOptionalT();\n // An app flow's reason resolves app-wide: it has no resource bundle of its own.\n const own = resource === APP_ACTION_RESOURCE ? undefined : resource;\n const caps = useI18nCaps(own);\n // The write's own notices (spec 4.7): a host-less flow mounts no flow screen to show them, so\n // the launcher shows them through the same sink the flow screen uses.\n const showNotices = useNoticeSink(own);\n const [confirming, setConfirming] = useState(false);\n const decide = useRef<((proceed: boolean) => void) | null>(null);\n const latest = useRef(flow);\n latest.current = flow;\n useEffect(\n () => () => {\n decide.current?.(false);\n decide.current = null;\n },\n [],\n );\n\n const launch = useCallback(async (): Promise<void> => {\n const { run, compiled } = latest.current;\n if (run === null || compiled === null) return;\n if (compiled.hasHost) {\n await (host.open() ?? Promise.resolve());\n return;\n }\n let result: FlowResult = await run.submit();\n while (result.outcome === 'halted' && (result.step === 'accept' || result.step === 'review')) {\n setConfirming(true);\n const proceed = await new Promise<boolean>((resolve) => {\n decide.current = resolve;\n });\n decide.current = null;\n setConfirming(false);\n if (!proceed) {\n run.startOver();\n return;\n }\n result = await run.accept();\n }\n if (result.outcome === 'success') {\n const { notices } = run.getSnapshot();\n if (notices.length > 0) showNotices(notices);\n return;\n }\n // Another tab holds the attempt's lock: nothing was sent (4.10).\n if (result.outcome === 'failed' && 'reason' in result && result.reason === 'busy') {\n toast(t('flow.busyElsewhere', { defaultValue: 'This action is running in another tab.' }));\n return;\n }\n // The send found the flow unavailable (a check that runs at submit, access revoked since the\n // click): the run's reason, as a refusal before the click shows it.\n if (result.outcome === 'unavailable') {\n const reason = run.getSnapshot().reason;\n toast(\n reason === undefined\n ? t('availability.denied', { defaultValue: \"This action isn't available\" })\n : reasonText(caps, reason, own),\n );\n return;\n }\n if (\n result.outcome === 'rejected' ||\n result.outcome === 'failed' ||\n result.outcome === 'unknown' ||\n (result.outcome === 'halted' && result.step === 'outcome')\n )\n await (host.open('dialog') ?? Promise.resolve());\n }, [host, t, caps, own, showNotices]);\n\n const { snapshot, compiled } = flow;\n const gate: CheckGateState = {\n resource,\n open: confirming,\n busy: false,\n header: null,\n blockers: NO_ISSUES,\n confirmable: confirming\n ? snapshot.warnings.map(\n (warning): ValidationIssue => ({\n code: 'check.warning',\n // The machine's text (bundle and params resolved); the key is the check's.\n message: {\n ...toMessageRef(warning.message, `checks.${warning.id}`),\n // The run's text is final (bundle and params resolved): the dialog shows it as is.\n params: { resolved: true },\n },\n path: [],\n check: { id: warning.id },\n source: 'check',\n severity: 'warning',\n }),\n )\n : NO_ISSUES,\n proceed: () => decide.current?.(true),\n cancel: () => decide.current?.(false),\n };\n const dialog =\n compiled === null || compiled.hasHost ? null : (\n <CheckConfirmDialog\n state={gate}\n testid={`${testid}:confirm`}\n title={chrome.confirmTitle}\n proceedLabel={chrome.confirmProceed}\n {...(chrome.dismiss === undefined ? {} : { cancelLabel: chrome.dismiss })}\n tone={chrome.destructive ? 'destructive' : 'default'}\n />\n );\n return { launch, dialog };\n}\n\nexport interface FlowRunnerProps {\n readonly resource: string;\n readonly actionName: string;\n readonly record?: Row;\n readonly scope: string;\n /** The interaction is over (the host closed, the write answered, the confirm was declined). */\n readonly onClose: () => void;\n}\n\n/**\n * @internal A flow started from a control that holds no run — a row's button, a menu item\n * (4.15: a menu item \"presents its host through the runtime from `onSelect`, so the menu closes\n * normally\"). It renders no control: it starts the run (its `with` sources load now) and, once\n * the run has decided, presents the host or sends; `onClose` reports the end of the\n * interaction. A run that decides against the click (a check over a `with` source fired) says\n * why.\n */\nexport function FlowRunner({\n resource,\n actionName,\n record,\n scope,\n onClose,\n}: FlowRunnerProps): ReactElement | null {\n const target = useMemo(() => ({ resource, action: actionName }), [resource, actionName]);\n const flow = useFlowRun(target, {\n ...(record === undefined ? {} : { record }),\n surface: scope === 'row' ? 'row' : 'menu',\n });\n const chrome = useFlowChrome(resource, actionName);\n const recordKey = recordKeyOf(useApp().registry.get(resource), record);\n const title = useProjectedTitle(resource, recordKey, record);\n const host = useFlowHost(flow, {\n label: chrome.labelText,\n ...(record === undefined || title === '' ? {} : { title }),\n ...(record === undefined ? {} : { record }),\n });\n const tid = useTestIds();\n const t = useOptionalT();\n const caps = useI18nCaps(resource);\n const testid = makeTestId(scope, resource, actionName, String(recordKey ?? ''), tid);\n const { launch, dialog } = useFlowLauncher(flow, host, chrome, resource, testid);\n const started = useRef(false);\n const onCloseRef = useRef(onClose);\n onCloseRef.current = onClose;\n const { snapshot, compiled } = flow;\n const decided =\n compiled !== null &&\n (snapshot.control === 'disabled' ||\n snapshot.control === 'absent' ||\n (snapshot.control === 'ready' &&\n (!compiled.hasHost || snapshot.history.includes('open') || snapshot.step === 'outcome')));\n useEffect(() => {\n if (!decided || started.current) return;\n started.current = true;\n if (snapshot.control !== 'ready') {\n toast(\n snapshot.reason === undefined\n ? t('availability.denied', { defaultValue: \"This action isn't available\" })\n : reasonText(caps, snapshot.reason, resource),\n );\n onCloseRef.current();\n return;\n }\n void launch().finally(() => onCloseRef.current());\n }, [decided, snapshot.control, snapshot.reason, launch, t, caps]);\n return dialog;\n}\n\nexport interface AppFlowRunnerProps {\n /** The app action's name (`defineAppAction({ name, kind: 'flow' })`). */\n readonly name: string;\n /** Its label, as the opener resolved it (`appActions.<name>` over the declared default). */\n readonly label: string;\n /** The opener's test id: the confirm dialog's ids suffix it (`<testid>:confirm:…`). */\n readonly testid: string;\n readonly onClose: () => void;\n}\n\n/**\n * @internal An app flow started from its control (`AppFlowButton`, P4-D): the same launcher as a\n * resource flow's — the host when the flow has a form or a review, else the send with the\n * confirm dialog — over the target `{ appAction: name }`, with no record.\n */\nexport function AppFlowRunner({\n name,\n label,\n testid,\n onClose,\n}: AppFlowRunnerProps): ReactElement | null {\n const target = useMemo(() => ({ appAction: name }), [name]);\n const flow = useFlowRun(target, { surface: 'app' });\n const dialogTexts = useFlowDialogTexts('', name, label);\n const chrome: FlowChrome = {\n label,\n labelText: label,\n icon: undefined,\n destructive: false,\n ...dialogTexts,\n };\n const host = useFlowHost(flow, { label });\n const t = useOptionalT();\n const caps = useI18nCaps();\n const { launch, dialog } = useFlowLauncher(flow, host, chrome, APP_ACTION_RESOURCE, testid);\n const started = useRef(false);\n const onCloseRef = useRef(onClose);\n onCloseRef.current = onClose;\n const { snapshot, compiled } = flow;\n const decided =\n compiled !== null &&\n (snapshot.control === 'disabled' ||\n snapshot.control === 'absent' ||\n (snapshot.control === 'ready' &&\n (!compiled.hasHost || snapshot.history.includes('open') || snapshot.step === 'outcome')));\n useEffect(() => {\n if (!decided || started.current) return;\n started.current = true;\n if (snapshot.control !== 'ready') {\n toast(\n snapshot.reason === undefined\n ? t('availability.denied', { defaultValue: \"This action isn't available\" })\n : reasonText(caps, snapshot.reason),\n );\n onCloseRef.current();\n return;\n }\n void launch().finally(() => onCloseRef.current());\n }, [decided, snapshot.control, snapshot.reason, launch, t, caps]);\n return dialog;\n}\n\nexport interface BulkFlowRunnerProps {\n readonly resource: string;\n readonly actionName: string;\n readonly ids: readonly string[];\n /** The run is over: its result and its per-id report (absent when nothing was sent). */\n readonly onFinish: (result: FlowResult, progress: BulkProgress | undefined) => void;\n}\n\n/**\n * @internal A bulk flow's run over the selection (4.16), started by the bar's click: the confirm\n * (`confirm` and the fired warnings) asks through the confirm dialog, then the run (re-reading the\n * rows first where the flow declares `submit.reread`) writes each under its own key; `onFinish` reports the result and the per-id report.\n */\nexport function BulkFlowRunner({\n resource,\n actionName,\n ids,\n onFinish,\n}: BulkFlowRunnerProps): ReactElement | null {\n const tid = useTestIds();\n const t = useOptionalT();\n const caps = useI18nCaps(resource);\n const idsKey = ids.join('\\u0000');\n // biome-ignore lint/correctness/useExhaustiveDependencies: keyed by the ids' values\n const selection = useMemo(() => ({ ids: [...ids], count: ids.length }), [idsKey]);\n const target = useMemo(() => ({ resource, action: actionName }), [resource, actionName]);\n const flow = useFlowRun(target, { selection, surface: 'bulk' });\n const chrome = useFlowChrome(resource, actionName);\n const testid = tid.bulkAction(resource, actionName);\n const [confirming, setConfirming] = useState(false);\n const decide = useRef<((proceed: boolean) => void) | null>(null);\n const started = useRef(false);\n const onFinishRef = useRef(onFinish);\n onFinishRef.current = onFinish;\n const { run, snapshot } = flow;\n useEffect(\n () => () => {\n decide.current?.(false);\n decide.current = null;\n },\n [],\n );\n const decided =\n run !== null && snapshot.control !== 'undecided' && snapshot.control !== 'pending';\n useEffect(() => {\n if (!decided || started.current || run === null) return;\n started.current = true;\n void (async () => {\n if (run.getSnapshot().control !== 'ready') {\n const refusal = run.getSnapshot().reason;\n toast(\n refusal === undefined\n ? t('availability.denied', { defaultValue: \"This action isn't available\" })\n : reasonText(caps, refusal, resource),\n );\n onFinishRef.current(\n Object.freeze({ outcome: 'unavailable', state: 'disabled' }),\n undefined,\n );\n return;\n }\n let result = await run.submit();\n while (\n result.outcome === 'halted' &&\n (result.step === 'accept' || result.step === 'review')\n ) {\n setConfirming(true);\n const proceed = await new Promise<boolean>((resolve) => {\n decide.current = resolve;\n });\n decide.current = null;\n setConfirming(false);\n if (!proceed) {\n run.startOver();\n onFinishRef.current(result, undefined);\n return;\n }\n result = await run.accept();\n }\n if (result.outcome === 'failed' && 'reason' in result && result.reason === 'busy')\n toast(t('flow.busyElsewhere', { defaultValue: 'This action is running in another tab.' }));\n onFinishRef.current(result, run.getSnapshot().bulk);\n })();\n }, [decided, run, t, caps, resource]);\n const gate: CheckGateState = {\n resource,\n open: confirming,\n busy: false,\n header: null,\n blockers: NO_ISSUES,\n confirmable: confirming\n ? snapshot.warnings.map(\n (warning): ValidationIssue => ({\n code: 'check.warning',\n // The machine's text (bundle and params resolved); the key is the check's.\n message: {\n ...toMessageRef(\n warning.rows === undefined\n ? warning.message\n : `${warning.message} — ${t('flow.bulkWarningRows', {\n defaultValue: warning.rows === 1 ? '1 row' : `${warning.rows} rows`,\n count: warning.rows,\n })}`,\n `checks.${warning.id}`,\n ),\n // The run's text is final (bundle and params resolved): the dialog shows it as is.\n params: { resolved: true },\n },\n path: [],\n check: { id: warning.id },\n source: 'check',\n severity: 'warning',\n }),\n )\n : NO_ISSUES,\n proceed: () => decide.current?.(true),\n cancel: () => decide.current?.(false),\n };\n return (\n <CheckConfirmDialog\n state={gate}\n testid={`${testid}:confirm`}\n title={chrome.confirmTitle}\n proceedLabel={chrome.confirmProceed}\n {...(chrome.dismiss === undefined ? {} : { cancelLabel: chrome.dismiss })}\n tone={chrome.destructive ? 'destructive' : 'default'}\n />\n );\n}\n"],"mappings":";;;;;;;;;;;AAwCA,MAAM,YAAwC,OAAO,OAAO,CAAC,CAAC;AAS9D,SAAS,gBACP,MACA,MACA,QACA,UACA,QACgF;CAChF,MAAM,KAAA,GAAIA,cAAAA,aAAAA,CAAa;CAEvB,MAAM,MAAM,aAAaC,aAAAA,sBAAsB,KAAA,IAAY;CAC3D,MAAM,QAAA,GAAOC,cAAAA,YAAAA,CAAY,GAAG;CAG5B,MAAM,cAAcC,wBAAAA,cAAc,GAAG;CACrC,MAAM,CAAC,YAAY,kBAAA,GAAiBC,MAAAA,SAAAA,CAAS,KAAK;CAClD,MAAM,UAAA,GAASC,MAAAA,OAAAA,CAA4C,IAAI;CAC/D,MAAM,UAAA,GAASA,MAAAA,OAAAA,CAAO,IAAI;CAC1B,OAAO,UAAU;CACjB,CAAA,GAAA,MAAA,UAAA,aACc;EACV,OAAO,UAAU,KAAK;EACtB,OAAO,UAAU;CACnB,GACA,CAAC,CACH;CAEA,MAAM,UAAA,GAASC,MAAAA,YAAAA,CAAY,YAA2B;EACpD,MAAM,EAAE,KAAK,aAAa,OAAO;EACjC,IAAI,QAAQ,QAAQ,aAAa,MAAM;EACvC,IAAI,SAAS,SAAS;GACpB,OAAO,KAAK,KAAK,KAAK,QAAQ,QAAQ;GACtC;EACF;EACA,IAAI,SAAqB,MAAM,IAAI,OAAO;EAC1C,OAAO,OAAO,YAAY,aAAa,OAAO,SAAS,YAAY,OAAO,SAAS,WAAW;GAC5F,cAAc,IAAI;GAClB,MAAM,UAAU,MAAM,IAAI,SAAkB,YAAY;IACtD,OAAO,UAAU;GACnB,CAAC;GACD,OAAO,UAAU;GACjB,cAAc,KAAK;GACnB,IAAI,CAAC,SAAS;IACZ,IAAI,UAAU;IACd;GACF;GACA,SAAS,MAAM,IAAI,OAAO;EAC5B;EACA,IAAI,OAAO,YAAY,WAAW;GAChC,MAAM,EAAE,YAAY,IAAI,YAAY;GACpC,IAAI,QAAQ,SAAS,GAAG,YAAY,OAAO;GAC3C;EACF;EAEA,IAAI,OAAO,YAAY,YAAY,YAAY,UAAU,OAAO,WAAW,QAAQ;GACjF,CAAA,GAAA,OAAA,MAAA,CAAM,EAAE,sBAAsB,EAAE,cAAc,yCAAyC,CAAC,CAAC;GACzF;EACF;EAGA,IAAI,OAAO,YAAY,eAAe;GACpC,MAAM,SAAS,IAAI,YAAY,CAAC,CAAC;GACjC,CAAA,GAAA,OAAA,MAAA,CACE,WAAW,KAAA,IACP,EAAE,uBAAuB,EAAE,cAAc,8BAA8B,CAAC,IACxEC,6BAAAA,WAAW,MAAM,QAAQ,GAAG,CAClC;GACA;EACF;EACA,IACE,OAAO,YAAY,cACnB,OAAO,YAAY,YACnB,OAAO,YAAY,aAClB,OAAO,YAAY,YAAY,OAAO,SAAS,WAEhD,OAAO,KAAK,KAAK,QAAQ,KAAK,QAAQ,QAAQ;CAClD,GAAG;EAAC;EAAM;EAAG;EAAM;EAAK;CAAW,CAAC;CAEpC,MAAM,EAAE,UAAU,aAAa;CAC/B,MAAM,OAAuB;EAC3B;EACA,MAAM;EACN,MAAM;EACN,QAAQ;EACR,UAAU;EACV,aAAa,aACT,SAAS,SAAS,KACf,aAA8B;GAC7B,MAAM;GAEN,SAAS;IACP,IAAA,GAAGC,sBAAAA,aAAAA,CAAa,QAAQ,SAAS,UAAU,QAAQ,IAAI;IAEvD,QAAQ,EAAE,UAAU,KAAK;GAC3B;GACA,MAAM,CAAC;GACP,OAAO,EAAE,IAAI,QAAQ,GAAG;GACxB,QAAQ;GACR,UAAU;EACZ,EACF,IACA;EACJ,eAAe,OAAO,UAAU,IAAI;EACpC,cAAc,OAAO,UAAU,KAAK;CACtC;CAYA,OAAO;EAAE;EAAQ,QAVf,aAAa,QAAQ,SAAS,UAAU,QACtC,GAAA,kBAAA,IAAA,CAACC,6BAAAA,oBAAD;GACE,OAAO;GACP,QAAQ,GAAG,OAAO;GAClB,OAAO,OAAO;GACd,cAAc,OAAO;GACrB,GAAK,OAAO,YAAY,KAAA,IAAY,CAAC,IAAI,EAAE,aAAa,OAAO,QAAQ;GACvE,MAAM,OAAO,cAAc,gBAAgB;EAC5C,CAAA;CAEmB;AAC1B;AAmBA,SAAgB,WAAW,EACzB,UACA,YACA,QACA,OACA,WACuC;CACvC,MAAM,UAAA,GAASC,MAAAA,QAAAA,QAAe;EAAE;EAAU,QAAQ;CAAW,IAAI,CAAC,UAAU,UAAU,CAAC;CACvF,MAAM,QAAA,GAAOC,oBAAAA,WAAAA,CAAW,QAAQ;EAC9B,GAAI,WAAW,KAAA,IAAY,CAAC,IAAI,EAAE,OAAO;EACzC,SAAS,UAAU,QAAQ,QAAQ;CACrC,CAAC;CACD,MAAM,SAASC,6BAAAA,cAAc,UAAU,UAAU;CACjD,MAAM,aAAA,GAAYC,cAAAA,YAAAA,EAAAA,GAAYC,cAAAA,OAAAA,CAAO,CAAC,CAAC,SAAS,IAAI,QAAQ,GAAG,MAAM;CACrE,MAAM,SAAA,GAAQC,cAAAA,kBAAAA,CAAkB,UAAU,WAAW,MAAM;CAC3D,MAAM,QAAA,GAAOC,oBAAAA,YAAAA,CAAY,MAAM;EAC7B,OAAO,OAAO;EACd,GAAI,WAAW,KAAA,KAAa,UAAU,KAAK,CAAC,IAAI,EAAE,MAAM;EACxD,GAAI,WAAW,KAAA,IAAY,CAAC,IAAI,EAAE,OAAO;CAC3C,CAAC;CACD,MAAM,OAAA,GAAMC,cAAAA,WAAAA,CAAW;CACvB,MAAM,KAAA,GAAIjB,cAAAA,aAAAA,CAAa;CACvB,MAAM,QAAA,GAAOE,cAAAA,YAAAA,CAAY,QAAQ;CAEjC,MAAM,EAAE,QAAQ,WAAW,gBAAgB,MAAM,MAAM,QAAQ,UADhDgB,6BAAAA,WAAW,OAAO,UAAU,YAAY,OAAO,aAAa,EAAE,GAAG,GACF,CAAC;CAC/E,MAAM,WAAA,GAAUb,MAAAA,OAAAA,CAAO,KAAK;CAC5B,MAAM,cAAA,GAAaA,MAAAA,OAAAA,CAAO,OAAO;CACjC,WAAW,UAAU;CACrB,MAAM,EAAE,UAAU,aAAa;CAC/B,MAAM,UACJ,aAAa,SACZ,SAAS,YAAY,cACpB,SAAS,YAAY,YACpB,SAAS,YAAY,YACnB,CAAC,SAAS,WAAW,SAAS,QAAQ,SAAS,MAAM,KAAK,SAAS,SAAS;CACnF,CAAA,GAAA,MAAA,UAAA,OAAgB;EACd,IAAI,CAAC,WAAW,QAAQ,SAAS;EACjC,QAAQ,UAAU;EAClB,IAAI,SAAS,YAAY,SAAS;GAChC,CAAA,GAAA,OAAA,MAAA,CACE,SAAS,WAAW,KAAA,IAChB,EAAE,uBAAuB,EAAE,cAAc,8BAA8B,CAAC,IACxEE,6BAAAA,WAAW,MAAM,SAAS,QAAQ,QAAQ,CAChD;GACA,WAAW,QAAQ;GACnB;EACF;EACA,OAAY,CAAC,CAAC,cAAc,WAAW,QAAQ,CAAC;CAClD,GAAG;EAAC;EAAS,SAAS;EAAS,SAAS;EAAQ;EAAQ;EAAG;CAAI,CAAC;CAChE,OAAO;AACT;AAiBA,SAAgB,cAAc,EAC5B,MACA,OACA,QACA,WAC0C;CAC1C,MAAM,UAAA,GAASG,MAAAA,QAAAA,QAAe,EAAE,WAAW,KAAK,IAAI,CAAC,IAAI,CAAC;CAC1D,MAAM,QAAA,GAAOC,oBAAAA,WAAAA,CAAW,QAAQ,EAAE,SAAS,MAAM,CAAC;CAElD,MAAM,SAAqB;EACzB;EACA,WAAW;EACX,MAAM,KAAA;EACN,aAAa;EACb,GANkBQ,6BAAAA,mBAAmB,IAAI,MAAM,KAMlC;CACf;CACA,MAAM,QAAA,GAAOH,oBAAAA,YAAAA,CAAY,MAAM,EAAE,MAAM,CAAC;CACxC,MAAM,KAAA,GAAIhB,cAAAA,aAAAA,CAAa;CACvB,MAAM,QAAA,GAAOE,cAAAA,YAAAA,CAAY;CACzB,MAAM,EAAE,QAAQ,WAAW,gBAAgB,MAAM,MAAM,QAAQD,aAAAA,qBAAqB,MAAM;CAC1F,MAAM,WAAA,GAAUI,MAAAA,OAAAA,CAAO,KAAK;CAC5B,MAAM,cAAA,GAAaA,MAAAA,OAAAA,CAAO,OAAO;CACjC,WAAW,UAAU;CACrB,MAAM,EAAE,UAAU,aAAa;CAC/B,MAAM,UACJ,aAAa,SACZ,SAAS,YAAY,cACpB,SAAS,YAAY,YACpB,SAAS,YAAY,YACnB,CAAC,SAAS,WAAW,SAAS,QAAQ,SAAS,MAAM,KAAK,SAAS,SAAS;CACnF,CAAA,GAAA,MAAA,UAAA,OAAgB;EACd,IAAI,CAAC,WAAW,QAAQ,SAAS;EACjC,QAAQ,UAAU;EAClB,IAAI,SAAS,YAAY,SAAS;GAChC,CAAA,GAAA,OAAA,MAAA,CACE,SAAS,WAAW,KAAA,IAChB,EAAE,uBAAuB,EAAE,cAAc,8BAA8B,CAAC,IACxEE,6BAAAA,WAAW,MAAM,SAAS,MAAM,CACtC;GACA,WAAW,QAAQ;GACnB;EACF;EACA,OAAY,CAAC,CAAC,cAAc,WAAW,QAAQ,CAAC;CAClD,GAAG;EAAC;EAAS,SAAS;EAAS,SAAS;EAAQ;EAAQ;EAAG;CAAI,CAAC;CAChE,OAAO;AACT;AAeA,SAAgB,eAAe,EAC7B,UACA,YACA,KACA,YAC2C;CAC3C,MAAM,OAAA,GAAMU,cAAAA,WAAAA,CAAW;CACvB,MAAM,KAAA,GAAIjB,cAAAA,aAAAA,CAAa;CACvB,MAAM,QAAA,GAAOE,cAAAA,YAAAA,CAAY,QAAQ;CACjC,MAAM,SAAS,IAAI,KAAK,IAAQ;CAEhC,MAAM,aAAA,GAAYQ,MAAAA,QAAAA,QAAe;EAAE,KAAK,CAAC,GAAG,GAAG;EAAG,OAAO,IAAI;CAAO,IAAI,CAAC,MAAM,CAAC;CAChF,MAAM,UAAA,GAASA,MAAAA,QAAAA,QAAe;EAAE;EAAU,QAAQ;CAAW,IAAI,CAAC,UAAU,UAAU,CAAC;CACvF,MAAM,QAAA,GAAOC,oBAAAA,WAAAA,CAAW,QAAQ;EAAE;EAAW,SAAS;CAAO,CAAC;CAC9D,MAAM,SAASC,6BAAAA,cAAc,UAAU,UAAU;CACjD,MAAM,SAAS,IAAI,WAAW,UAAU,UAAU;CAClD,MAAM,CAAC,YAAY,kBAAA,GAAiBR,MAAAA,SAAAA,CAAS,KAAK;CAClD,MAAM,UAAA,GAASC,MAAAA,OAAAA,CAA4C,IAAI;CAC/D,MAAM,WAAA,GAAUA,MAAAA,OAAAA,CAAO,KAAK;CAC5B,MAAM,eAAA,GAAcA,MAAAA,OAAAA,CAAO,QAAQ;CACnC,YAAY,UAAU;CACtB,MAAM,EAAE,KAAK,aAAa;CAC1B,CAAA,GAAA,MAAA,UAAA,aACc;EACV,OAAO,UAAU,KAAK;EACtB,OAAO,UAAU;CACnB,GACA,CAAC,CACH;CACA,MAAM,UACJ,QAAQ,QAAQ,SAAS,YAAY,eAAe,SAAS,YAAY;CAC3E,CAAA,GAAA,MAAA,UAAA,OAAgB;EACd,IAAI,CAAC,WAAW,QAAQ,WAAW,QAAQ,MAAM;EACjD,QAAQ,UAAU;EAClB,CAAM,YAAY;GAChB,IAAI,IAAI,YAAY,CAAC,CAAC,YAAY,SAAS;IACzC,MAAM,UAAU,IAAI,YAAY,CAAC,CAAC;IAClC,CAAA,GAAA,OAAA,MAAA,CACE,YAAY,KAAA,IACR,EAAE,uBAAuB,EAAE,cAAc,8BAA8B,CAAC,IACxEE,6BAAAA,WAAW,MAAM,SAAS,QAAQ,CACxC;IACA,YAAY,QACV,OAAO,OAAO;KAAE,SAAS;KAAe,OAAO;IAAW,CAAC,GAC3D,KAAA,CACF;IACA;GACF;GACA,IAAI,SAAS,MAAM,IAAI,OAAO;GAC9B,OACE,OAAO,YAAY,aAClB,OAAO,SAAS,YAAY,OAAO,SAAS,WAC7C;IACA,cAAc,IAAI;IAClB,MAAM,UAAU,MAAM,IAAI,SAAkB,YAAY;KACtD,OAAO,UAAU;IACnB,CAAC;IACD,OAAO,UAAU;IACjB,cAAc,KAAK;IACnB,IAAI,CAAC,SAAS;KACZ,IAAI,UAAU;KACd,YAAY,QAAQ,QAAQ,KAAA,CAAS;KACrC;IACF;IACA,SAAS,MAAM,IAAI,OAAO;GAC5B;GACA,IAAI,OAAO,YAAY,YAAY,YAAY,UAAU,OAAO,WAAW,QACzE,CAAA,GAAA,OAAA,MAAA,CAAM,EAAE,sBAAsB,EAAE,cAAc,yCAAyC,CAAC,CAAC;GAC3F,YAAY,QAAQ,QAAQ,IAAI,YAAY,CAAC,CAAC,IAAI;EACpD,EAAA,CAAG;CACL,GAAG;EAAC;EAAS;EAAK;EAAG;EAAM;CAAQ,CAAC;CACpC,MAAM,OAAuB;EAC3B;EACA,MAAM;EACN,MAAM;EACN,QAAQ;EACR,UAAU;EACV,aAAa,aACT,SAAS,SAAS,KACf,aAA8B;GAC7B,MAAM;GAEN,SAAS;IACP,IAAA,GAAGC,sBAAAA,aAAAA,CACD,QAAQ,SAAS,KAAA,IACb,QAAQ,UACR,GAAG,QAAQ,QAAQ,KAAK,EAAE,wBAAwB;KAChD,cAAc,QAAQ,SAAS,IAAI,UAAU,GAAG,QAAQ,KAAK;KAC7D,OAAO,QAAQ;IACjB,CAAC,KACL,UAAU,QAAQ,IACpB;IAEA,QAAQ,EAAE,UAAU,KAAK;GAC3B;GACA,MAAM,CAAC;GACP,OAAO,EAAE,IAAI,QAAQ,GAAG;GACxB,QAAQ;GACR,UAAU;EACZ,EACF,IACA;EACJ,eAAe,OAAO,UAAU,IAAI;EACpC,cAAc,OAAO,UAAU,KAAK;CACtC;CACA,QACE,GAAA,kBAAA,IAAA,CAACC,6BAAAA,oBAAD;EACE,OAAO;EACP,QAAQ,GAAG,OAAO;EAClB,OAAO,OAAO;EACd,cAAc,OAAO;EACrB,GAAK,OAAO,YAAY,KAAA,IAAY,CAAC,IAAI,EAAE,aAAa,OAAO,QAAQ;EACvE,MAAM,OAAO,cAAc,gBAAgB;CAC5C,CAAA;AAEL"}
1
+ {"version":3,"file":"flow-runtime-CQ4wf8co.cjs","names":["useOptionalT","APP_ACTION_RESOURCE","useI18nCaps","useNoticeSink","useState","useRef","useCallback","reasonText","toMessageRef","CheckConfirmDialog","useMemo","useFlowRun","useFlowChrome","recordKeyOf","useApp","useProjectedTitle","useFlowHost","useTestIds","makeTestId","useFlowDialogTexts"],"sources":["../src/flows/flow-runtime.tsx"],"sourcesContent":["'use client';\n/**\n * The flow runtime's side of the UI kit (P4, the coordinator's ruling on the bundle): every\n * component that holds a run. It loads on the first sign that a user means to run a flow (a\n * hover, a focus, a menu opening, at the latest the click) through `./load-runtime.js`, and with\n * it `@burojs/react/flows` and `@burojs/core/flows` — the flow machine stays out of an\n * application's first screen.\n */\nimport { toMessageRef } from '@burojs/core/internal';\nimport {\n APP_ACTION_RESOURCE,\n type BulkProgress,\n type FlowControlState,\n type FlowResult,\n type Row,\n type ValidationIssue,\n} from '@burojs/core';\nimport {\n type CheckGateState,\n recordKeyOf,\n useApp,\n useI18nCaps,\n useOptionalT,\n useProjectedTitle,\n useTestIds,\n} from '@burojs/react';\nimport {\n type FlowHostControl,\n type UseFlowRunResult,\n useFlowHost,\n useFlowRun,\n} from '@burojs/react/flows';\nimport { type ReactElement, useCallback, useEffect, useMemo, useRef, useState } from 'react';\nimport { makeTestId } from '../actions/action-button.js';\nimport { CheckConfirmDialog } from '../checks/check-confirm-dialog.js';\nimport { useNoticeSink } from '../forms/success-notices.js';\nimport { reasonText } from '../lib/reason-text.js';\nimport { toast } from '../notification/toaster.js';\nimport { type FlowChrome, useFlowChrome, useFlowDialogTexts } from './chrome.js';\n\nconst NO_ISSUES: readonly ValidationIssue[] = Object.freeze([]);\n\n/**\n * Runs a flow from its control (4.15): a flow with a host presents it and the interaction ends\n * when it closes; a `{ label, submit }` flow sends, asks through the confirm dialog when\n * `confirm` or a fired warning asks, and shows `rejected`, `failed` and `unknown` in the flow's\n * screen in a dialog (`success` notifies through the flow's `then` and shows the write's notices; a lock another tab holds\n * says so; an `unavailable` send toasts the run's reason). `launch()` settles when the interaction is over.\n */\nfunction useFlowLauncher(\n flow: UseFlowRunResult,\n host: FlowHostControl,\n chrome: FlowChrome,\n resource: string,\n testid: string,\n): { readonly launch: () => Promise<void>; readonly dialog: ReactElement | null } {\n const t = useOptionalT();\n // An app flow's reason resolves app-wide: it has no resource bundle of its own.\n const own = resource === APP_ACTION_RESOURCE ? undefined : resource;\n const caps = useI18nCaps(own);\n // The write's own notices (spec 4.7): a host-less flow mounts no flow screen to show them, so\n // the launcher shows them through the same sink the flow screen uses.\n const showNotices = useNoticeSink(own);\n const [confirming, setConfirming] = useState(false);\n const decide = useRef<((proceed: boolean) => void) | null>(null);\n const latest = useRef(flow);\n latest.current = flow;\n useEffect(\n () => () => {\n decide.current?.(false);\n decide.current = null;\n },\n [],\n );\n\n const launch = useCallback(async (): Promise<void> => {\n const { run, compiled } = latest.current;\n if (run === null || compiled === null) return;\n if (compiled.hasHost) {\n await (host.open() ?? Promise.resolve());\n return;\n }\n let result: FlowResult = await run.submit();\n while (result.outcome === 'halted' && (result.step === 'accept' || result.step === 'review')) {\n setConfirming(true);\n const proceed = await new Promise<boolean>((resolve) => {\n decide.current = resolve;\n });\n decide.current = null;\n setConfirming(false);\n if (!proceed) {\n run.startOver();\n return;\n }\n result = await run.accept();\n }\n if (result.outcome === 'success') {\n const { notices } = run.getSnapshot();\n if (notices.length > 0) showNotices(notices);\n return;\n }\n // Another tab holds the attempt's lock: nothing was sent (4.10).\n if (result.outcome === 'failed' && 'reason' in result && result.reason === 'busy') {\n toast(t('flow.busyElsewhere', { defaultValue: 'This action is running in another tab.' }));\n return;\n }\n // The send found the flow unavailable (a check that runs at submit, access revoked since the\n // click): the run's reason, as a refusal before the click shows it.\n if (result.outcome === 'unavailable') {\n const reason = run.getSnapshot().reason;\n toast(\n reason === undefined\n ? t('availability.denied', { defaultValue: \"This action isn't available\" })\n : reasonText(caps, reason, own),\n );\n return;\n }\n if (\n result.outcome === 'rejected' ||\n result.outcome === 'failed' ||\n result.outcome === 'unknown' ||\n (result.outcome === 'halted' && result.step === 'outcome')\n )\n await (host.open('dialog') ?? Promise.resolve());\n }, [host, t, caps, own, showNotices]);\n\n const { snapshot, compiled } = flow;\n const gate: CheckGateState = {\n resource,\n open: confirming,\n busy: false,\n header: null,\n blockers: NO_ISSUES,\n confirmable: confirming\n ? snapshot.warnings.map(\n (warning): ValidationIssue => ({\n code: 'check.warning',\n // The machine's text (bundle and params resolved); the key is the check's.\n message: {\n ...toMessageRef(warning.message, `checks.${warning.id}`),\n // The run's text is final (bundle and params resolved): the dialog shows it as is.\n params: { resolved: true },\n },\n path: [],\n check: { id: warning.id },\n source: 'check',\n severity: 'warning',\n }),\n )\n : NO_ISSUES,\n proceed: () => decide.current?.(true),\n cancel: () => decide.current?.(false),\n };\n const dialog =\n compiled === null || compiled.hasHost ? null : (\n <CheckConfirmDialog\n state={gate}\n testid={`${testid}:confirm`}\n title={chrome.confirmTitle}\n proceedLabel={chrome.confirmProceed}\n {...(chrome.dismiss === undefined ? {} : { cancelLabel: chrome.dismiss })}\n tone={chrome.destructive ? 'destructive' : 'default'}\n />\n );\n return { launch, dialog };\n}\n\nexport interface FlowRunnerProps {\n readonly resource: string;\n readonly actionName: string;\n readonly record?: Row;\n readonly scope: string;\n /** The interaction is over (the host closed, the write answered, the confirm was declined). */\n readonly onClose: () => void;\n}\n\n/**\n * @internal A flow started from a control that holds no run — a row's button, a menu item\n * (4.15: a menu item \"presents its host through the runtime from `onSelect`, so the menu closes\n * normally\"). It renders no control: it starts the run (its `with` sources load now) and, once\n * the run has decided, presents the host or sends; `onClose` reports the end of the\n * interaction. A run that decides against the click (a check over a `with` source fired) says\n * why.\n */\nexport function FlowRunner({\n resource,\n actionName,\n record,\n scope,\n onClose,\n}: FlowRunnerProps): ReactElement | null {\n const target = useMemo(() => ({ resource, action: actionName }), [resource, actionName]);\n const flow = useFlowRun(target, {\n ...(record === undefined ? {} : { record }),\n surface: scope === 'row' ? 'row' : 'menu',\n });\n const chrome = useFlowChrome(resource, actionName);\n const recordKey = recordKeyOf(useApp().registry.get(resource), record);\n const title = useProjectedTitle(resource, recordKey, record);\n const host = useFlowHost(flow, {\n label: chrome.labelText,\n ...(record === undefined || title === '' ? {} : { title }),\n ...(record === undefined ? {} : { record }),\n });\n const tid = useTestIds();\n const t = useOptionalT();\n const caps = useI18nCaps(resource);\n const testid = makeTestId(scope, resource, actionName, String(recordKey ?? ''), tid);\n const { launch, dialog } = useFlowLauncher(flow, host, chrome, resource, testid);\n const started = useRef(false);\n const onCloseRef = useRef(onClose);\n onCloseRef.current = onClose;\n const { snapshot, compiled } = flow;\n const decided =\n compiled !== null &&\n (snapshot.control === 'disabled' ||\n snapshot.control === 'absent' ||\n (snapshot.control === 'ready' &&\n (!compiled.hasHost || snapshot.history.includes('open') || snapshot.step === 'outcome')));\n useEffect(() => {\n if (!decided || started.current) return;\n started.current = true;\n if (snapshot.control !== 'ready') {\n toast(\n snapshot.reason === undefined\n ? t('availability.denied', { defaultValue: \"This action isn't available\" })\n : reasonText(caps, snapshot.reason, resource),\n );\n onCloseRef.current();\n return;\n }\n void launch().finally(() => onCloseRef.current());\n }, [decided, snapshot.control, snapshot.reason, launch, t, caps]);\n return dialog;\n}\n\nexport interface AppFlowRunnerProps {\n /** The app action's name (`defineAppAction({ name, kind: 'flow' })`). */\n readonly name: string;\n /** Its label, as the opener resolved it (`appActions.<name>` over the declared default). */\n readonly label: string;\n /** The opener's test id: the confirm dialog's ids suffix it (`<testid>:confirm:…`). */\n readonly testid: string;\n readonly onClose: () => void;\n}\n\n/**\n * @internal An app flow started from its control (`AppFlowButton`, P4-D): the same launcher as a\n * resource flow's — the host when the flow has a form or a review, else the send with the\n * confirm dialog — over the target `{ appAction: name }`, with no record.\n */\nexport function AppFlowRunner({\n name,\n label,\n testid,\n onClose,\n}: AppFlowRunnerProps): ReactElement | null {\n const target = useMemo(() => ({ appAction: name }), [name]);\n const flow = useFlowRun(target, { surface: 'app' });\n const dialogTexts = useFlowDialogTexts('', name, label);\n const chrome: FlowChrome = {\n label,\n labelText: label,\n icon: undefined,\n destructive: false,\n ...dialogTexts,\n };\n const host = useFlowHost(flow, { label });\n const t = useOptionalT();\n const caps = useI18nCaps();\n const { launch, dialog } = useFlowLauncher(flow, host, chrome, APP_ACTION_RESOURCE, testid);\n const started = useRef(false);\n const onCloseRef = useRef(onClose);\n onCloseRef.current = onClose;\n const { snapshot, compiled } = flow;\n const decided =\n compiled !== null &&\n (snapshot.control === 'disabled' ||\n snapshot.control === 'absent' ||\n (snapshot.control === 'ready' &&\n (!compiled.hasHost || snapshot.history.includes('open') || snapshot.step === 'outcome')));\n useEffect(() => {\n if (!decided || started.current) return;\n started.current = true;\n if (snapshot.control !== 'ready') {\n toast(\n snapshot.reason === undefined\n ? t('availability.denied', { defaultValue: \"This action isn't available\" })\n : reasonText(caps, snapshot.reason),\n );\n onCloseRef.current();\n return;\n }\n void launch().finally(() => onCloseRef.current());\n }, [decided, snapshot.control, snapshot.reason, launch, t, caps]);\n return dialog;\n}\n\nexport interface BulkFlowRunnerProps {\n readonly resource: string;\n readonly actionName: string;\n readonly ids: readonly string[];\n /** The run is over: its result and its per-id report (absent when nothing was sent). */\n readonly onFinish: (result: FlowResult, progress: BulkProgress | undefined) => void;\n}\n\n/**\n * @internal A bulk flow's run over the selection (4.16), started by the bar's click: the confirm\n * (`confirm` and the fired warnings) asks through the confirm dialog, then the run (re-reading the\n * rows first where the flow declares `submit.reread`) writes each under its own key; `onFinish` reports the result and the per-id report.\n */\nexport function BulkFlowRunner({\n resource,\n actionName,\n ids,\n onFinish,\n}: BulkFlowRunnerProps): ReactElement | null {\n const tid = useTestIds();\n const t = useOptionalT();\n const caps = useI18nCaps(resource);\n const idsKey = ids.join('\\u0000');\n // biome-ignore lint/correctness/useExhaustiveDependencies: keyed by the ids' values\n const selection = useMemo(() => ({ ids: [...ids], count: ids.length }), [idsKey]);\n const target = useMemo(() => ({ resource, action: actionName }), [resource, actionName]);\n const flow = useFlowRun(target, { selection, surface: 'bulk' });\n const chrome = useFlowChrome(resource, actionName);\n const testid = tid.bulkAction(resource, actionName);\n const [confirming, setConfirming] = useState(false);\n const decide = useRef<((proceed: boolean) => void) | null>(null);\n const started = useRef(false);\n const onFinishRef = useRef(onFinish);\n onFinishRef.current = onFinish;\n const { run, snapshot } = flow;\n useEffect(\n () => () => {\n decide.current?.(false);\n decide.current = null;\n },\n [],\n );\n const decided =\n run !== null && snapshot.control !== 'undecided' && snapshot.control !== 'pending';\n useEffect(() => {\n if (!decided || started.current || run === null) return;\n started.current = true;\n void (async () => {\n if (run.getSnapshot().control !== 'ready') {\n const refusal = run.getSnapshot().reason;\n toast(\n refusal === undefined\n ? t('availability.denied', { defaultValue: \"This action isn't available\" })\n : reasonText(caps, refusal, resource),\n );\n onFinishRef.current(\n Object.freeze({ outcome: 'unavailable', state: 'disabled' }),\n undefined,\n );\n return;\n }\n let result = await run.submit();\n while (\n result.outcome === 'halted' &&\n (result.step === 'accept' || result.step === 'review')\n ) {\n setConfirming(true);\n const proceed = await new Promise<boolean>((resolve) => {\n decide.current = resolve;\n });\n decide.current = null;\n setConfirming(false);\n if (!proceed) {\n run.startOver();\n onFinishRef.current(result, undefined);\n return;\n }\n result = await run.accept();\n }\n if (result.outcome === 'failed' && 'reason' in result && result.reason === 'busy')\n toast(t('flow.busyElsewhere', { defaultValue: 'This action is running in another tab.' }));\n onFinishRef.current(result, run.getSnapshot().bulk);\n })();\n }, [decided, run, t, caps, resource]);\n const gate: CheckGateState = {\n resource,\n open: confirming,\n busy: false,\n header: null,\n blockers: NO_ISSUES,\n confirmable: confirming\n ? snapshot.warnings.map(\n (warning): ValidationIssue => ({\n code: 'check.warning',\n // The machine's text (bundle and params resolved); the key is the check's.\n message: {\n ...toMessageRef(\n warning.rows === undefined\n ? warning.message\n : `${warning.message} — ${t('flow.bulkWarningRows', {\n defaultValue: warning.rows === 1 ? '1 row' : `${warning.rows} rows`,\n count: warning.rows,\n })}`,\n `checks.${warning.id}`,\n ),\n // The run's text is final (bundle and params resolved): the dialog shows it as is.\n params: { resolved: true },\n },\n path: [],\n check: { id: warning.id },\n source: 'check',\n severity: 'warning',\n }),\n )\n : NO_ISSUES,\n proceed: () => decide.current?.(true),\n cancel: () => decide.current?.(false),\n };\n return (\n <CheckConfirmDialog\n state={gate}\n testid={`${testid}:confirm`}\n title={chrome.confirmTitle}\n proceedLabel={chrome.confirmProceed}\n {...(chrome.dismiss === undefined ? {} : { cancelLabel: chrome.dismiss })}\n tone={chrome.destructive ? 'destructive' : 'default'}\n />\n );\n}\n"],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  import { n as toast } from "./toaster-Ct_YHGs6.js";
2
- import { _ as reasonText, h as useFlowDialogTexts, i as makeTestId, m as useFlowChrome, t as CheckConfirmDialog } from "./check-confirm-dialog-CtiRzOAt.js";
3
- import { t as useNoticeSink } from "./success-notices-LDEgEkc4.js";
2
+ import { a as makeTestId, g as useFlowDialogTexts, h as useFlowChrome, t as CheckConfirmDialog, v as reasonText } from "./check-confirm-dialog-B_u3YVHS.js";
3
+ import { t as useNoticeSink } from "./success-notices-CrcWOGSo.js";
4
4
  import { APP_ACTION_RESOURCE } from "@burojs/core";
5
5
  import { recordKeyOf, useApp, useI18nCaps, useOptionalT, useProjectedTitle, useTestIds } from "@burojs/react";
6
6
  import { useCallback, useEffect, useMemo, useRef, useState } from "react";
@@ -284,4 +284,4 @@ function BulkFlowRunner({ resource, actionName, ids, onFinish }) {
284
284
  //#endregion
285
285
  export { AppFlowRunner, BulkFlowRunner, FlowRunner };
286
286
 
287
- //# sourceMappingURL=flow-runtime-DrUWyTS0.js.map
287
+ //# sourceMappingURL=flow-runtime-Q2vD7R3c.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"flow-runtime-DrUWyTS0.js","names":[],"sources":["../src/flows/flow-runtime.tsx"],"sourcesContent":["'use client';\n/**\n * The flow runtime's side of the UI kit (P4, the coordinator's ruling on the bundle): every\n * component that holds a run. It loads on the first sign that a user means to run a flow (a\n * hover, a focus, a menu opening, at the latest the click) through `./load-runtime.js`, and with\n * it `@burojs/react/flows` and `@burojs/core/flows` — the flow machine stays out of an\n * application's first screen.\n */\nimport { toMessageRef } from '@burojs/core/internal';\nimport {\n APP_ACTION_RESOURCE,\n type BulkProgress,\n type FlowControlState,\n type FlowResult,\n type Row,\n type ValidationIssue,\n} from '@burojs/core';\nimport {\n type CheckGateState,\n recordKeyOf,\n useApp,\n useI18nCaps,\n useOptionalT,\n useProjectedTitle,\n useTestIds,\n} from '@burojs/react';\nimport {\n type FlowHostControl,\n type UseFlowRunResult,\n useFlowHost,\n useFlowRun,\n} from '@burojs/react/flows';\nimport { type ReactElement, useCallback, useEffect, useMemo, useRef, useState } from 'react';\nimport { makeTestId } from '../actions/action-button.js';\nimport { CheckConfirmDialog } from '../checks/check-confirm-dialog.js';\nimport { useNoticeSink } from '../forms/success-notices.js';\nimport { reasonText } from '../lib/reason-text.js';\nimport { toast } from '../notification/toaster.js';\nimport { type FlowChrome, useFlowChrome, useFlowDialogTexts } from './chrome.js';\n\nconst NO_ISSUES: readonly ValidationIssue[] = Object.freeze([]);\n\n/**\n * Runs a flow from its control (4.15): a flow with a host presents it and the interaction ends\n * when it closes; a `{ label, submit }` flow sends, asks through the confirm dialog when\n * `confirm` or a fired warning asks, and shows `rejected`, `failed` and `unknown` in the flow's\n * screen in a dialog (`success` notifies through the flow's `then` and shows the write's notices; a lock another tab holds\n * says so; an `unavailable` send toasts the run's reason). `launch()` settles when the interaction is over.\n */\nfunction useFlowLauncher(\n flow: UseFlowRunResult,\n host: FlowHostControl,\n chrome: FlowChrome,\n resource: string,\n testid: string,\n): { readonly launch: () => Promise<void>; readonly dialog: ReactElement | null } {\n const t = useOptionalT();\n // An app flow's reason resolves app-wide: it has no resource bundle of its own.\n const own = resource === APP_ACTION_RESOURCE ? undefined : resource;\n const caps = useI18nCaps(own);\n // The write's own notices (spec 4.7): a host-less flow mounts no flow screen to show them, so\n // the launcher shows them through the same sink the flow screen uses.\n const showNotices = useNoticeSink(own);\n const [confirming, setConfirming] = useState(false);\n const decide = useRef<((proceed: boolean) => void) | null>(null);\n const latest = useRef(flow);\n latest.current = flow;\n useEffect(\n () => () => {\n decide.current?.(false);\n decide.current = null;\n },\n [],\n );\n\n const launch = useCallback(async (): Promise<void> => {\n const { run, compiled } = latest.current;\n if (run === null || compiled === null) return;\n if (compiled.hasHost) {\n await (host.open() ?? Promise.resolve());\n return;\n }\n let result: FlowResult = await run.submit();\n while (result.outcome === 'halted' && (result.step === 'accept' || result.step === 'review')) {\n setConfirming(true);\n const proceed = await new Promise<boolean>((resolve) => {\n decide.current = resolve;\n });\n decide.current = null;\n setConfirming(false);\n if (!proceed) {\n run.startOver();\n return;\n }\n result = await run.accept();\n }\n if (result.outcome === 'success') {\n const { notices } = run.getSnapshot();\n if (notices.length > 0) showNotices(notices);\n return;\n }\n // Another tab holds the attempt's lock: nothing was sent (4.10).\n if (result.outcome === 'failed' && 'reason' in result && result.reason === 'busy') {\n toast(t('flow.busyElsewhere', { defaultValue: 'This action is running in another tab.' }));\n return;\n }\n // The send found the flow unavailable (a check that runs at submit, access revoked since the\n // click): the run's reason, as a refusal before the click shows it.\n if (result.outcome === 'unavailable') {\n const reason = run.getSnapshot().reason;\n toast(\n reason === undefined\n ? t('availability.denied', { defaultValue: \"This action isn't available\" })\n : reasonText(caps, reason, own),\n );\n return;\n }\n if (\n result.outcome === 'rejected' ||\n result.outcome === 'failed' ||\n result.outcome === 'unknown' ||\n (result.outcome === 'halted' && result.step === 'outcome')\n )\n await (host.open('dialog') ?? Promise.resolve());\n }, [host, t, caps, own, showNotices]);\n\n const { snapshot, compiled } = flow;\n const gate: CheckGateState = {\n resource,\n open: confirming,\n busy: false,\n header: null,\n blockers: NO_ISSUES,\n confirmable: confirming\n ? snapshot.warnings.map(\n (warning): ValidationIssue => ({\n code: 'check.warning',\n // The machine's text (bundle and params resolved); the key is the check's.\n message: {\n ...toMessageRef(warning.message, `checks.${warning.id}`),\n // The run's text is final (bundle and params resolved): the dialog shows it as is.\n params: { resolved: true },\n },\n path: [],\n check: { id: warning.id },\n source: 'check',\n severity: 'warning',\n }),\n )\n : NO_ISSUES,\n proceed: () => decide.current?.(true),\n cancel: () => decide.current?.(false),\n };\n const dialog =\n compiled === null || compiled.hasHost ? null : (\n <CheckConfirmDialog\n state={gate}\n testid={`${testid}:confirm`}\n title={chrome.confirmTitle}\n proceedLabel={chrome.confirmProceed}\n {...(chrome.dismiss === undefined ? {} : { cancelLabel: chrome.dismiss })}\n tone={chrome.destructive ? 'destructive' : 'default'}\n />\n );\n return { launch, dialog };\n}\n\nexport interface FlowRunnerProps {\n readonly resource: string;\n readonly actionName: string;\n readonly record?: Row;\n readonly scope: string;\n /** The interaction is over (the host closed, the write answered, the confirm was declined). */\n readonly onClose: () => void;\n}\n\n/**\n * @internal A flow started from a control that holds no run — a row's button, a menu item\n * (4.15: a menu item \"presents its host through the runtime from `onSelect`, so the menu closes\n * normally\"). It renders no control: it starts the run (its `with` sources load now) and, once\n * the run has decided, presents the host or sends; `onClose` reports the end of the\n * interaction. A run that decides against the click (a check over a `with` source fired) says\n * why.\n */\nexport function FlowRunner({\n resource,\n actionName,\n record,\n scope,\n onClose,\n}: FlowRunnerProps): ReactElement | null {\n const target = useMemo(() => ({ resource, action: actionName }), [resource, actionName]);\n const flow = useFlowRun(target, {\n ...(record === undefined ? {} : { record }),\n surface: scope === 'row' ? 'row' : 'menu',\n });\n const chrome = useFlowChrome(resource, actionName);\n const recordKey = recordKeyOf(useApp().registry.get(resource), record);\n const title = useProjectedTitle(resource, recordKey, record);\n const host = useFlowHost(flow, {\n label: chrome.labelText,\n ...(record === undefined || title === '' ? {} : { title }),\n ...(record === undefined ? {} : { record }),\n });\n const tid = useTestIds();\n const t = useOptionalT();\n const caps = useI18nCaps(resource);\n const testid = makeTestId(scope, resource, actionName, String(recordKey ?? ''), tid);\n const { launch, dialog } = useFlowLauncher(flow, host, chrome, resource, testid);\n const started = useRef(false);\n const onCloseRef = useRef(onClose);\n onCloseRef.current = onClose;\n const { snapshot, compiled } = flow;\n const decided =\n compiled !== null &&\n (snapshot.control === 'disabled' ||\n snapshot.control === 'absent' ||\n (snapshot.control === 'ready' &&\n (!compiled.hasHost || snapshot.history.includes('open') || snapshot.step === 'outcome')));\n useEffect(() => {\n if (!decided || started.current) return;\n started.current = true;\n if (snapshot.control !== 'ready') {\n toast(\n snapshot.reason === undefined\n ? t('availability.denied', { defaultValue: \"This action isn't available\" })\n : reasonText(caps, snapshot.reason, resource),\n );\n onCloseRef.current();\n return;\n }\n void launch().finally(() => onCloseRef.current());\n }, [decided, snapshot.control, snapshot.reason, launch, t, caps]);\n return dialog;\n}\n\nexport interface AppFlowRunnerProps {\n /** The app action's name (`defineAppAction({ name, kind: 'flow' })`). */\n readonly name: string;\n /** Its label, as the opener resolved it (`appActions.<name>` over the declared default). */\n readonly label: string;\n /** The opener's test id: the confirm dialog's ids suffix it (`<testid>:confirm:…`). */\n readonly testid: string;\n readonly onClose: () => void;\n}\n\n/**\n * @internal An app flow started from its control (`AppFlowButton`, P4-D): the same launcher as a\n * resource flow's — the host when the flow has a form or a review, else the send with the\n * confirm dialog — over the target `{ appAction: name }`, with no record.\n */\nexport function AppFlowRunner({\n name,\n label,\n testid,\n onClose,\n}: AppFlowRunnerProps): ReactElement | null {\n const target = useMemo(() => ({ appAction: name }), [name]);\n const flow = useFlowRun(target, { surface: 'app' });\n const dialogTexts = useFlowDialogTexts('', name, label);\n const chrome: FlowChrome = {\n label,\n labelText: label,\n icon: undefined,\n destructive: false,\n ...dialogTexts,\n };\n const host = useFlowHost(flow, { label });\n const t = useOptionalT();\n const caps = useI18nCaps();\n const { launch, dialog } = useFlowLauncher(flow, host, chrome, APP_ACTION_RESOURCE, testid);\n const started = useRef(false);\n const onCloseRef = useRef(onClose);\n onCloseRef.current = onClose;\n const { snapshot, compiled } = flow;\n const decided =\n compiled !== null &&\n (snapshot.control === 'disabled' ||\n snapshot.control === 'absent' ||\n (snapshot.control === 'ready' &&\n (!compiled.hasHost || snapshot.history.includes('open') || snapshot.step === 'outcome')));\n useEffect(() => {\n if (!decided || started.current) return;\n started.current = true;\n if (snapshot.control !== 'ready') {\n toast(\n snapshot.reason === undefined\n ? t('availability.denied', { defaultValue: \"This action isn't available\" })\n : reasonText(caps, snapshot.reason),\n );\n onCloseRef.current();\n return;\n }\n void launch().finally(() => onCloseRef.current());\n }, [decided, snapshot.control, snapshot.reason, launch, t, caps]);\n return dialog;\n}\n\nexport interface BulkFlowRunnerProps {\n readonly resource: string;\n readonly actionName: string;\n readonly ids: readonly string[];\n /** The run is over: its result and its per-id report (absent when nothing was sent). */\n readonly onFinish: (result: FlowResult, progress: BulkProgress | undefined) => void;\n}\n\n/**\n * @internal A bulk flow's run over the selection (4.16), started by the bar's click: the confirm\n * (`confirm` and the fired warnings) asks through the confirm dialog, then the run (re-reading the\n * rows first where the flow declares `submit.reread`) writes each under its own key; `onFinish` reports the result and the per-id report.\n */\nexport function BulkFlowRunner({\n resource,\n actionName,\n ids,\n onFinish,\n}: BulkFlowRunnerProps): ReactElement | null {\n const tid = useTestIds();\n const t = useOptionalT();\n const caps = useI18nCaps(resource);\n const idsKey = ids.join('\\u0000');\n // biome-ignore lint/correctness/useExhaustiveDependencies: keyed by the ids' values\n const selection = useMemo(() => ({ ids: [...ids], count: ids.length }), [idsKey]);\n const target = useMemo(() => ({ resource, action: actionName }), [resource, actionName]);\n const flow = useFlowRun(target, { selection, surface: 'bulk' });\n const chrome = useFlowChrome(resource, actionName);\n const testid = tid.bulkAction(resource, actionName);\n const [confirming, setConfirming] = useState(false);\n const decide = useRef<((proceed: boolean) => void) | null>(null);\n const started = useRef(false);\n const onFinishRef = useRef(onFinish);\n onFinishRef.current = onFinish;\n const { run, snapshot } = flow;\n useEffect(\n () => () => {\n decide.current?.(false);\n decide.current = null;\n },\n [],\n );\n const decided =\n run !== null && snapshot.control !== 'undecided' && snapshot.control !== 'pending';\n useEffect(() => {\n if (!decided || started.current || run === null) return;\n started.current = true;\n void (async () => {\n if (run.getSnapshot().control !== 'ready') {\n const refusal = run.getSnapshot().reason;\n toast(\n refusal === undefined\n ? t('availability.denied', { defaultValue: \"This action isn't available\" })\n : reasonText(caps, refusal, resource),\n );\n onFinishRef.current(\n Object.freeze({ outcome: 'unavailable', state: 'disabled' }),\n undefined,\n );\n return;\n }\n let result = await run.submit();\n while (\n result.outcome === 'halted' &&\n (result.step === 'accept' || result.step === 'review')\n ) {\n setConfirming(true);\n const proceed = await new Promise<boolean>((resolve) => {\n decide.current = resolve;\n });\n decide.current = null;\n setConfirming(false);\n if (!proceed) {\n run.startOver();\n onFinishRef.current(result, undefined);\n return;\n }\n result = await run.accept();\n }\n if (result.outcome === 'failed' && 'reason' in result && result.reason === 'busy')\n toast(t('flow.busyElsewhere', { defaultValue: 'This action is running in another tab.' }));\n onFinishRef.current(result, run.getSnapshot().bulk);\n })();\n }, [decided, run, t, caps, resource]);\n const gate: CheckGateState = {\n resource,\n open: confirming,\n busy: false,\n header: null,\n blockers: NO_ISSUES,\n confirmable: confirming\n ? snapshot.warnings.map(\n (warning): ValidationIssue => ({\n code: 'check.warning',\n // The machine's text (bundle and params resolved); the key is the check's.\n message: {\n ...toMessageRef(\n warning.rows === undefined\n ? warning.message\n : `${warning.message} — ${t('flow.bulkWarningRows', {\n defaultValue: warning.rows === 1 ? '1 row' : `${warning.rows} rows`,\n count: warning.rows,\n })}`,\n `checks.${warning.id}`,\n ),\n // The run's text is final (bundle and params resolved): the dialog shows it as is.\n params: { resolved: true },\n },\n path: [],\n check: { id: warning.id },\n source: 'check',\n severity: 'warning',\n }),\n )\n : NO_ISSUES,\n proceed: () => decide.current?.(true),\n cancel: () => decide.current?.(false),\n };\n return (\n <CheckConfirmDialog\n state={gate}\n testid={`${testid}:confirm`}\n title={chrome.confirmTitle}\n proceedLabel={chrome.confirmProceed}\n {...(chrome.dismiss === undefined ? {} : { cancelLabel: chrome.dismiss })}\n tone={chrome.destructive ? 'destructive' : 'default'}\n />\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"flow-runtime-Q2vD7R3c.js","names":[],"sources":["../src/flows/flow-runtime.tsx"],"sourcesContent":["'use client';\n/**\n * The flow runtime's side of the UI kit (P4, the coordinator's ruling on the bundle): every\n * component that holds a run. It loads on the first sign that a user means to run a flow (a\n * hover, a focus, a menu opening, at the latest the click) through `./load-runtime.js`, and with\n * it `@burojs/react/flows` and `@burojs/core/flows` — the flow machine stays out of an\n * application's first screen.\n */\nimport { toMessageRef } from '@burojs/core/internal';\nimport {\n APP_ACTION_RESOURCE,\n type BulkProgress,\n type FlowControlState,\n type FlowResult,\n type Row,\n type ValidationIssue,\n} from '@burojs/core';\nimport {\n type CheckGateState,\n recordKeyOf,\n useApp,\n useI18nCaps,\n useOptionalT,\n useProjectedTitle,\n useTestIds,\n} from '@burojs/react';\nimport {\n type FlowHostControl,\n type UseFlowRunResult,\n useFlowHost,\n useFlowRun,\n} from '@burojs/react/flows';\nimport { type ReactElement, useCallback, useEffect, useMemo, useRef, useState } from 'react';\nimport { makeTestId } from '../actions/action-button.js';\nimport { CheckConfirmDialog } from '../checks/check-confirm-dialog.js';\nimport { useNoticeSink } from '../forms/success-notices.js';\nimport { reasonText } from '../lib/reason-text.js';\nimport { toast } from '../notification/toaster.js';\nimport { type FlowChrome, useFlowChrome, useFlowDialogTexts } from './chrome.js';\n\nconst NO_ISSUES: readonly ValidationIssue[] = Object.freeze([]);\n\n/**\n * Runs a flow from its control (4.15): a flow with a host presents it and the interaction ends\n * when it closes; a `{ label, submit }` flow sends, asks through the confirm dialog when\n * `confirm` or a fired warning asks, and shows `rejected`, `failed` and `unknown` in the flow's\n * screen in a dialog (`success` notifies through the flow's `then` and shows the write's notices; a lock another tab holds\n * says so; an `unavailable` send toasts the run's reason). `launch()` settles when the interaction is over.\n */\nfunction useFlowLauncher(\n flow: UseFlowRunResult,\n host: FlowHostControl,\n chrome: FlowChrome,\n resource: string,\n testid: string,\n): { readonly launch: () => Promise<void>; readonly dialog: ReactElement | null } {\n const t = useOptionalT();\n // An app flow's reason resolves app-wide: it has no resource bundle of its own.\n const own = resource === APP_ACTION_RESOURCE ? undefined : resource;\n const caps = useI18nCaps(own);\n // The write's own notices (spec 4.7): a host-less flow mounts no flow screen to show them, so\n // the launcher shows them through the same sink the flow screen uses.\n const showNotices = useNoticeSink(own);\n const [confirming, setConfirming] = useState(false);\n const decide = useRef<((proceed: boolean) => void) | null>(null);\n const latest = useRef(flow);\n latest.current = flow;\n useEffect(\n () => () => {\n decide.current?.(false);\n decide.current = null;\n },\n [],\n );\n\n const launch = useCallback(async (): Promise<void> => {\n const { run, compiled } = latest.current;\n if (run === null || compiled === null) return;\n if (compiled.hasHost) {\n await (host.open() ?? Promise.resolve());\n return;\n }\n let result: FlowResult = await run.submit();\n while (result.outcome === 'halted' && (result.step === 'accept' || result.step === 'review')) {\n setConfirming(true);\n const proceed = await new Promise<boolean>((resolve) => {\n decide.current = resolve;\n });\n decide.current = null;\n setConfirming(false);\n if (!proceed) {\n run.startOver();\n return;\n }\n result = await run.accept();\n }\n if (result.outcome === 'success') {\n const { notices } = run.getSnapshot();\n if (notices.length > 0) showNotices(notices);\n return;\n }\n // Another tab holds the attempt's lock: nothing was sent (4.10).\n if (result.outcome === 'failed' && 'reason' in result && result.reason === 'busy') {\n toast(t('flow.busyElsewhere', { defaultValue: 'This action is running in another tab.' }));\n return;\n }\n // The send found the flow unavailable (a check that runs at submit, access revoked since the\n // click): the run's reason, as a refusal before the click shows it.\n if (result.outcome === 'unavailable') {\n const reason = run.getSnapshot().reason;\n toast(\n reason === undefined\n ? t('availability.denied', { defaultValue: \"This action isn't available\" })\n : reasonText(caps, reason, own),\n );\n return;\n }\n if (\n result.outcome === 'rejected' ||\n result.outcome === 'failed' ||\n result.outcome === 'unknown' ||\n (result.outcome === 'halted' && result.step === 'outcome')\n )\n await (host.open('dialog') ?? Promise.resolve());\n }, [host, t, caps, own, showNotices]);\n\n const { snapshot, compiled } = flow;\n const gate: CheckGateState = {\n resource,\n open: confirming,\n busy: false,\n header: null,\n blockers: NO_ISSUES,\n confirmable: confirming\n ? snapshot.warnings.map(\n (warning): ValidationIssue => ({\n code: 'check.warning',\n // The machine's text (bundle and params resolved); the key is the check's.\n message: {\n ...toMessageRef(warning.message, `checks.${warning.id}`),\n // The run's text is final (bundle and params resolved): the dialog shows it as is.\n params: { resolved: true },\n },\n path: [],\n check: { id: warning.id },\n source: 'check',\n severity: 'warning',\n }),\n )\n : NO_ISSUES,\n proceed: () => decide.current?.(true),\n cancel: () => decide.current?.(false),\n };\n const dialog =\n compiled === null || compiled.hasHost ? null : (\n <CheckConfirmDialog\n state={gate}\n testid={`${testid}:confirm`}\n title={chrome.confirmTitle}\n proceedLabel={chrome.confirmProceed}\n {...(chrome.dismiss === undefined ? {} : { cancelLabel: chrome.dismiss })}\n tone={chrome.destructive ? 'destructive' : 'default'}\n />\n );\n return { launch, dialog };\n}\n\nexport interface FlowRunnerProps {\n readonly resource: string;\n readonly actionName: string;\n readonly record?: Row;\n readonly scope: string;\n /** The interaction is over (the host closed, the write answered, the confirm was declined). */\n readonly onClose: () => void;\n}\n\n/**\n * @internal A flow started from a control that holds no run — a row's button, a menu item\n * (4.15: a menu item \"presents its host through the runtime from `onSelect`, so the menu closes\n * normally\"). It renders no control: it starts the run (its `with` sources load now) and, once\n * the run has decided, presents the host or sends; `onClose` reports the end of the\n * interaction. A run that decides against the click (a check over a `with` source fired) says\n * why.\n */\nexport function FlowRunner({\n resource,\n actionName,\n record,\n scope,\n onClose,\n}: FlowRunnerProps): ReactElement | null {\n const target = useMemo(() => ({ resource, action: actionName }), [resource, actionName]);\n const flow = useFlowRun(target, {\n ...(record === undefined ? {} : { record }),\n surface: scope === 'row' ? 'row' : 'menu',\n });\n const chrome = useFlowChrome(resource, actionName);\n const recordKey = recordKeyOf(useApp().registry.get(resource), record);\n const title = useProjectedTitle(resource, recordKey, record);\n const host = useFlowHost(flow, {\n label: chrome.labelText,\n ...(record === undefined || title === '' ? {} : { title }),\n ...(record === undefined ? {} : { record }),\n });\n const tid = useTestIds();\n const t = useOptionalT();\n const caps = useI18nCaps(resource);\n const testid = makeTestId(scope, resource, actionName, String(recordKey ?? ''), tid);\n const { launch, dialog } = useFlowLauncher(flow, host, chrome, resource, testid);\n const started = useRef(false);\n const onCloseRef = useRef(onClose);\n onCloseRef.current = onClose;\n const { snapshot, compiled } = flow;\n const decided =\n compiled !== null &&\n (snapshot.control === 'disabled' ||\n snapshot.control === 'absent' ||\n (snapshot.control === 'ready' &&\n (!compiled.hasHost || snapshot.history.includes('open') || snapshot.step === 'outcome')));\n useEffect(() => {\n if (!decided || started.current) return;\n started.current = true;\n if (snapshot.control !== 'ready') {\n toast(\n snapshot.reason === undefined\n ? t('availability.denied', { defaultValue: \"This action isn't available\" })\n : reasonText(caps, snapshot.reason, resource),\n );\n onCloseRef.current();\n return;\n }\n void launch().finally(() => onCloseRef.current());\n }, [decided, snapshot.control, snapshot.reason, launch, t, caps]);\n return dialog;\n}\n\nexport interface AppFlowRunnerProps {\n /** The app action's name (`defineAppAction({ name, kind: 'flow' })`). */\n readonly name: string;\n /** Its label, as the opener resolved it (`appActions.<name>` over the declared default). */\n readonly label: string;\n /** The opener's test id: the confirm dialog's ids suffix it (`<testid>:confirm:…`). */\n readonly testid: string;\n readonly onClose: () => void;\n}\n\n/**\n * @internal An app flow started from its control (`AppFlowButton`, P4-D): the same launcher as a\n * resource flow's — the host when the flow has a form or a review, else the send with the\n * confirm dialog — over the target `{ appAction: name }`, with no record.\n */\nexport function AppFlowRunner({\n name,\n label,\n testid,\n onClose,\n}: AppFlowRunnerProps): ReactElement | null {\n const target = useMemo(() => ({ appAction: name }), [name]);\n const flow = useFlowRun(target, { surface: 'app' });\n const dialogTexts = useFlowDialogTexts('', name, label);\n const chrome: FlowChrome = {\n label,\n labelText: label,\n icon: undefined,\n destructive: false,\n ...dialogTexts,\n };\n const host = useFlowHost(flow, { label });\n const t = useOptionalT();\n const caps = useI18nCaps();\n const { launch, dialog } = useFlowLauncher(flow, host, chrome, APP_ACTION_RESOURCE, testid);\n const started = useRef(false);\n const onCloseRef = useRef(onClose);\n onCloseRef.current = onClose;\n const { snapshot, compiled } = flow;\n const decided =\n compiled !== null &&\n (snapshot.control === 'disabled' ||\n snapshot.control === 'absent' ||\n (snapshot.control === 'ready' &&\n (!compiled.hasHost || snapshot.history.includes('open') || snapshot.step === 'outcome')));\n useEffect(() => {\n if (!decided || started.current) return;\n started.current = true;\n if (snapshot.control !== 'ready') {\n toast(\n snapshot.reason === undefined\n ? t('availability.denied', { defaultValue: \"This action isn't available\" })\n : reasonText(caps, snapshot.reason),\n );\n onCloseRef.current();\n return;\n }\n void launch().finally(() => onCloseRef.current());\n }, [decided, snapshot.control, snapshot.reason, launch, t, caps]);\n return dialog;\n}\n\nexport interface BulkFlowRunnerProps {\n readonly resource: string;\n readonly actionName: string;\n readonly ids: readonly string[];\n /** The run is over: its result and its per-id report (absent when nothing was sent). */\n readonly onFinish: (result: FlowResult, progress: BulkProgress | undefined) => void;\n}\n\n/**\n * @internal A bulk flow's run over the selection (4.16), started by the bar's click: the confirm\n * (`confirm` and the fired warnings) asks through the confirm dialog, then the run (re-reading the\n * rows first where the flow declares `submit.reread`) writes each under its own key; `onFinish` reports the result and the per-id report.\n */\nexport function BulkFlowRunner({\n resource,\n actionName,\n ids,\n onFinish,\n}: BulkFlowRunnerProps): ReactElement | null {\n const tid = useTestIds();\n const t = useOptionalT();\n const caps = useI18nCaps(resource);\n const idsKey = ids.join('\\u0000');\n // biome-ignore lint/correctness/useExhaustiveDependencies: keyed by the ids' values\n const selection = useMemo(() => ({ ids: [...ids], count: ids.length }), [idsKey]);\n const target = useMemo(() => ({ resource, action: actionName }), [resource, actionName]);\n const flow = useFlowRun(target, { selection, surface: 'bulk' });\n const chrome = useFlowChrome(resource, actionName);\n const testid = tid.bulkAction(resource, actionName);\n const [confirming, setConfirming] = useState(false);\n const decide = useRef<((proceed: boolean) => void) | null>(null);\n const started = useRef(false);\n const onFinishRef = useRef(onFinish);\n onFinishRef.current = onFinish;\n const { run, snapshot } = flow;\n useEffect(\n () => () => {\n decide.current?.(false);\n decide.current = null;\n },\n [],\n );\n const decided =\n run !== null && snapshot.control !== 'undecided' && snapshot.control !== 'pending';\n useEffect(() => {\n if (!decided || started.current || run === null) return;\n started.current = true;\n void (async () => {\n if (run.getSnapshot().control !== 'ready') {\n const refusal = run.getSnapshot().reason;\n toast(\n refusal === undefined\n ? t('availability.denied', { defaultValue: \"This action isn't available\" })\n : reasonText(caps, refusal, resource),\n );\n onFinishRef.current(\n Object.freeze({ outcome: 'unavailable', state: 'disabled' }),\n undefined,\n );\n return;\n }\n let result = await run.submit();\n while (\n result.outcome === 'halted' &&\n (result.step === 'accept' || result.step === 'review')\n ) {\n setConfirming(true);\n const proceed = await new Promise<boolean>((resolve) => {\n decide.current = resolve;\n });\n decide.current = null;\n setConfirming(false);\n if (!proceed) {\n run.startOver();\n onFinishRef.current(result, undefined);\n return;\n }\n result = await run.accept();\n }\n if (result.outcome === 'failed' && 'reason' in result && result.reason === 'busy')\n toast(t('flow.busyElsewhere', { defaultValue: 'This action is running in another tab.' }));\n onFinishRef.current(result, run.getSnapshot().bulk);\n })();\n }, [decided, run, t, caps, resource]);\n const gate: CheckGateState = {\n resource,\n open: confirming,\n busy: false,\n header: null,\n blockers: NO_ISSUES,\n confirmable: confirming\n ? snapshot.warnings.map(\n (warning): ValidationIssue => ({\n code: 'check.warning',\n // The machine's text (bundle and params resolved); the key is the check's.\n message: {\n ...toMessageRef(\n warning.rows === undefined\n ? warning.message\n : `${warning.message} — ${t('flow.bulkWarningRows', {\n defaultValue: warning.rows === 1 ? '1 row' : `${warning.rows} rows`,\n count: warning.rows,\n })}`,\n `checks.${warning.id}`,\n ),\n // The run's text is final (bundle and params resolved): the dialog shows it as is.\n params: { resolved: true },\n },\n path: [],\n check: { id: warning.id },\n source: 'check',\n severity: 'warning',\n }),\n )\n : NO_ISSUES,\n proceed: () => decide.current?.(true),\n cancel: () => decide.current?.(false),\n };\n return (\n <CheckConfirmDialog\n state={gate}\n testid={`${testid}:confirm`}\n title={chrome.confirmTitle}\n proceedLabel={chrome.confirmProceed}\n {...(chrome.dismiss === undefined ? {} : { cancelLabel: chrome.dismiss })}\n tone={chrome.destructive ? 'destructive' : 'default'}\n />\n );\n}\n"],"mappings":"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"}
@@ -1,9 +1,9 @@
1
- import { A as OwnTitleHeading, N as OwnTitleContext, _ as reasonText, dt as DocInfo, j as useFocusOnMount } from "./check-confirm-dialog-CtiRzOAt.js";
1
+ import { M as useFocusOnMount, P as OwnTitleContext, ft as DocInfo, j as OwnTitleHeading, v as reasonText } from "./check-confirm-dialog-B_u3YVHS.js";
2
2
  import { t as Button } from "./button-MWVeZaqj.js";
3
- import { H as ScreenTree, J as FormControlProvider, Mt as FieldDisplay, X as useFormControlOf, n as viewOf, t as selectImplementation } from "./place-node-BdxT-30h.js";
4
- import { t as useNoticeSink } from "./success-notices-LDEgEkc4.js";
3
+ import { Nt as FieldDisplay, U as ScreenTree, Y as FormControlProvider, Z as useFormControlOf, n as viewOf, t as selectImplementation } from "./place-node-DwWAigv0.js";
4
+ import { t as useNoticeSink } from "./success-notices-CrcWOGSo.js";
5
5
  import { declaredText, fieldSensitivity, flowRecordOf, humanizeKey, textsValueAt, validatePlacementProps } from "@burojs/core";
6
- import { BulkSelectionScope, REJECTED_KEYS, ScreenStateProvider, resolveValidationMessage, specificErrorText, useChromeT, useDismissPending, useFlowFormCatalog, useFlowOpenImplementation, useFormController, useI18nCaps, useOptionalT, useProjectedTitle, useTestIds, useUIInstance, useUIPresentationRuntime } from "@burojs/react";
6
+ import { BulkSelectionScope, REJECTED_KEYS, ScreenStateProvider, resolveValidationMessage, specificErrorText, useChromeT, useCurrentUserId, useDismissHold, useDismissPending, useFlowFormCatalog, useFlowOpenImplementation, useFormController, useI18nCaps, useNavigationBlocker, useOptionalRouterAdapter, useOptionalT, useProjectedTitle, useTestIds, useUIInstance, useUIPresentationRuntime } from "@burojs/react";
7
7
  import { IconLoader2, IconX } from "@tabler/icons-react";
8
8
  import { Suspense, createContext, useContext, useEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
9
9
  import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
@@ -114,7 +114,12 @@ function OverlayBinding({ children }) {
114
114
  function WithBinding({ children }) {
115
115
  return useContext(FlowHostBindingContext) !== null ? jsx(Fragment$1, { children }) : jsx(OverlayBinding, { children });
116
116
  }
117
- function outcomeMessage(snapshot, busy, t, caps, textPrefix, flow, reasonOf) {
117
+ function outcomeMessage(snapshot, busy, t, caps, textPrefixes, flow, reasonOf) {
118
+ const place = { action: {
119
+ prefix: textPrefixes[0] ?? "",
120
+ prefixes: textPrefixes,
121
+ texts: flow?.texts
122
+ } };
118
123
  if (busy) return t("flow.busyElsewhere", { defaultValue: "This action is running in another tab." });
119
124
  const outcome = snapshot.outcome;
120
125
  if (outcome === void 0 || outcome.outcome === "success") {
@@ -130,14 +135,11 @@ function outcomeMessage(snapshot, busy, t, caps, textPrefix, flow, reasonOf) {
130
135
  const specific = specificErrorText({
131
136
  code,
132
137
  ..."message" in outcome && outcome.message !== void 0 ? { message: outcome.message } : {}
133
- }, caps, { action: {
134
- prefix: textPrefix,
135
- texts: flow?.texts
136
- } });
138
+ }, caps, place);
137
139
  if (specific !== void 0) return specific;
138
140
  }
139
141
  const key = kind === void 0 ? void 0 : REJECTED_KEYS[kind];
140
- if (key !== void 0) return t(`${textPrefix}.rejected.${key}`, { defaultValue: textsValueAt(flow?.texts, ["rejected", key]) ?? t(`flow.rejected.${key}`, { defaultValue: "Nothing was saved." }) });
142
+ if (key !== void 0) return textPrefixes.reduceRight((rest, prefix) => t(`${prefix}.rejected.${key}`, { defaultValue: rest }), textsValueAt(flow?.texts, ["rejected", key]) ?? t(`flow.rejected.${key}`, { defaultValue: "Nothing was saved." }));
141
143
  return t("flow.rejected.precondition", { defaultValue: "The record changed. Nothing was sent." });
142
144
  }
143
145
  if (outcome.outcome === "failed") {
@@ -146,10 +148,7 @@ function outcomeMessage(snapshot, busy, t, caps, textPrefix, flow, reasonOf) {
146
148
  return (code === void 0 ? void 0 : specificErrorText({
147
149
  code,
148
150
  ..."message" in outcome && outcome.message !== void 0 ? { message: outcome.message } : {}
149
- }, caps, { action: {
150
- prefix: textPrefix,
151
- texts: flow?.texts
152
- } })) ?? t("flow.failed", { defaultValue: "The request could not be sent. Nothing was saved." });
151
+ }, caps, place)) ?? t("flow.failed", { defaultValue: "The request could not be sent. Nothing was saved." });
153
152
  }
154
153
  return null;
155
154
  }
@@ -273,12 +272,38 @@ function FlowScreenBody({ run, host, label, title: givenTitle, compiled, record:
273
272
  const [initial] = useState(() => run.getSnapshot().values);
274
273
  const formErrors = form.errors.filter((issue) => issue.field === "_form");
275
274
  const locked = snapshot.control === "running";
276
- useDismissPending(locked);
277
275
  const outcome = snapshot.outcome?.outcome;
278
276
  const unknown = outcome === "unknown";
279
277
  const succeeded = outcome === "success";
280
278
  const finalStep = snapshot.step === "review" || snapshot.step === "accept";
281
279
  const openBlock = succeeded ? blockOpen(snapshot.effects?.open) : null;
280
+ useDismissPending(locked);
281
+ const held = openBlock?.dismiss === "explicit";
282
+ const userId = useCurrentUserId();
283
+ const heldBy = useRef(null);
284
+ if (!held) heldBy.current = null;
285
+ else if (heldBy.current === null) heldBy.current = { id: userId };
286
+ const guarding = held && userId === heldBy.current?.id;
287
+ const leaveGuard = useNavigationBlocker(guarding);
288
+ const hold = useDismissHold(guarding && binding.kind === "overlay");
289
+ const pathname = useOptionalRouterAdapter()?.pathname;
290
+ const heldAt = useRef(void 0);
291
+ useEffect(() => {
292
+ if (!held || binding.kind !== "overlay") {
293
+ heldAt.current = void 0;
294
+ return;
295
+ }
296
+ if (heldAt.current === void 0) heldAt.current = pathname;
297
+ else if (heldAt.current !== pathname) {
298
+ hold.close();
299
+ binding.finish();
300
+ }
301
+ }, [
302
+ held,
303
+ pathname,
304
+ binding,
305
+ hold
306
+ ]);
282
307
  const showNotices = useNoticeSink(resource);
283
308
  const shownNotices = useRef(null);
284
309
  useEffect(() => {
@@ -329,9 +354,10 @@ function FlowScreenBody({ run, host, label, title: givenTitle, compiled, record:
329
354
  }
330
355
  settle(await run.submit());
331
356
  };
332
- const pendingKey = compiled.resource === null ? `appActions.${compiled.name}.pending` : `resources.${compiled.resource}.actions.${compiled.name}.pending`;
333
- const pendingLabel = caps.has(pendingKey) ? caps.t(pendingKey) : `${label}…`;
334
- const message = outcomeMessage(snapshot, busy, t, caps, compiled.textKeys.prefix, compiled.flow, (reason) => reasonText(caps, reason, resource));
357
+ const ownKey = (slot) => compiled.textKeys.prefixes.map((prefix) => `${prefix}.${slot}`).find((key) => caps.has(key));
358
+ const pendingKey = ownKey("pending");
359
+ const pendingLabel = pendingKey !== void 0 ? caps.t(pendingKey) : `${label}…`;
360
+ const message = outcomeMessage(snapshot, busy, t, caps, compiled.textKeys.prefixes, compiled.flow, (reason) => reasonText(caps, reason, resource));
335
361
  const formErrorTexts = formErrors.map((issue) => resolveValidationMessage({
336
362
  code: issue.code ?? "",
337
363
  message: issue.message,
@@ -340,8 +366,8 @@ function FlowScreenBody({ run, host, label, title: givenTitle, compiled, record:
340
366
  has: caps.has,
341
367
  t: caps.t
342
368
  })).filter((text) => text !== message);
343
- const dismissKey = `${compiled.textKeys.prefix}.dismiss`;
344
- const cancelWord = (caps.has(dismissKey) ? caps.t(dismissKey) : void 0) ?? declaredText("flow", compiled.flow, "dismiss") ?? chromeT("actions.cancel", { defaultValue: "Cancel" });
369
+ const dismissKey = ownKey("dismiss");
370
+ const cancelWord = (dismissKey !== void 0 ? caps.t(dismissKey) : void 0) ?? declaredText("flow", compiled.flow, "dismiss") ?? chromeT("actions.cancel", { defaultValue: "Cancel" });
345
371
  const couldNotCheck = snapshot.control === "disabled" && snapshot.unverified === true;
346
372
  const steps = form.steps;
347
373
  const lastStep = steps === null || steps.ids.length === 0 || steps.current === steps.ids[steps.ids.length - 1];
@@ -359,14 +385,19 @@ function FlowScreenBody({ run, host, label, title: givenTitle, compiled, record:
359
385
  };
360
386
  const headingRef = useRef(null);
361
387
  useFocusOnMount(headingRef);
388
+ const doneRef = useRef(null);
389
+ const blockShown = openBlock !== null;
390
+ useEffect(() => {
391
+ if (blockShown) (doneRef.current ?? headingRef.current)?.focus();
392
+ }, [blockShown]);
362
393
  const heading = jsxs("h2", {
363
394
  ref: headingRef,
364
395
  tabIndex: -1,
365
396
  "data-buro-overlay-heading": "",
366
397
  className: "min-w-0 flex-1 break-words text-lg font-semibold tracking-tight focus-visible:outline-hidden",
367
- children: [label, title === void 0 ? null : jsx("span", {
368
- className: "ml-2 font-normal text-muted-foreground",
369
- children: title
398
+ children: [label, title === void 0 ? null : jsxs("span", {
399
+ className: "font-normal text-muted-foreground",
400
+ children: [" — ", title]
370
401
  })]
371
402
  });
372
403
  return jsxs("div", {
@@ -389,7 +420,7 @@ function FlowScreenBody({ run, host, label, title: givenTitle, compiled, record:
389
420
  name: label,
390
421
  testId: `${tid.flowHost(flowId)}:info`
391
422
  }),
392
- binding.kind === "overlay" ? jsx(Button, {
423
+ binding.kind === "overlay" && !held ? jsx(Button, {
393
424
  type: "button",
394
425
  variant: "ghost",
395
426
  size: "icon",
@@ -409,13 +440,20 @@ function FlowScreenBody({ run, host, label, title: givenTitle, compiled, record:
409
440
  prefix,
410
441
  testid: tid.flowHost(flowId)
411
442
  }) : null,
412
- openBlock !== null && binding.kind !== "overlay" ? jsx("div", {
443
+ openBlock !== null && (binding.kind !== "overlay" || held) ? jsx("div", {
413
444
  className: "flex justify-end",
414
445
  children: jsx(Button, {
415
446
  type: "button",
416
447
  variant: "outline",
448
+ ref: doneRef,
417
449
  "data-testid": tid.flowClose(flowId),
418
- onClick: close,
450
+ onClick: () => {
451
+ leaveGuard.release();
452
+ if (held && binding.kind === "overlay") {
453
+ hold.close();
454
+ binding.finish();
455
+ } else close();
456
+ },
419
457
  children: t("flow.done", { defaultValue: "Done" })
420
458
  })
421
459
  }) : null,
@@ -695,12 +733,13 @@ function placedLeaves(tree) {
695
733
  }
696
734
  function blockOpen(open) {
697
735
  if (open === null || typeof open !== "object" || !Object.hasOwn(open, "block")) return null;
698
- const { block, props } = open;
736
+ const { block, props, dismiss } = open;
699
737
  const ref = block !== null && typeof block === "object" && "ref" in block ? block.ref : block;
700
738
  if (ref === null || typeof ref !== "object" || typeof ref.namespace !== "string" || typeof ref.id !== "string") return null;
701
739
  return {
702
740
  block: ref,
703
- props: props !== null && typeof props === "object" ? props : {}
741
+ props: props !== null && typeof props === "object" ? props : {},
742
+ ...dismiss === "explicit" ? { dismiss } : {}
704
743
  };
705
744
  }
706
745
  function FlowOpenBlock({ open, testid }) {
@@ -744,4 +783,4 @@ function FlowOpenBlock({ open, testid }) {
744
783
  //#endregion
745
784
  export { FlowHostBindingContext, FlowScreen, HostedFlowScreen };
746
785
 
747
- //# sourceMappingURL=flow-screen-3fk6J33y.js.map
786
+ //# sourceMappingURL=flow-screen-B1y2yizP.js.map