@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.
- package/dist/{bulk-summary-2HLgz7Il.cjs → bulk-summary-Chc61Pyw.cjs} +9 -7
- package/dist/bulk-summary-Chc61Pyw.cjs.map +1 -0
- package/dist/{bulk-summary-D6A0ua21.js → bulk-summary-DpSQD2Fq.js} +9 -7
- package/dist/bulk-summary-DpSQD2Fq.js.map +1 -0
- package/dist/{candidates-relation-picker-CGXItMN0.js → candidates-relation-picker-DRsHKe6N.js} +3 -3
- package/dist/{candidates-relation-picker-CGXItMN0.js.map → candidates-relation-picker-DRsHKe6N.js.map} +1 -1
- package/dist/{candidates-relation-picker-C47mF260.cjs → candidates-relation-picker-GIxsp7D_.cjs} +3 -3
- package/dist/{candidates-relation-picker-C47mF260.cjs.map → candidates-relation-picker-GIxsp7D_.cjs.map} +1 -1
- package/dist/{check-confirm-dialog-BT04Mieo.cjs → check-confirm-dialog-BMSb7wFv.cjs} +76 -9
- package/dist/check-confirm-dialog-BMSb7wFv.cjs.map +1 -0
- package/dist/{check-confirm-dialog-CtiRzOAt.js → check-confirm-dialog-B_u3YVHS.js} +72 -11
- package/dist/{check-confirm-dialog-CtiRzOAt.js.map → check-confirm-dialog-B_u3YVHS.js.map} +1 -1
- package/dist/{filter-builder-z3Axzf9Y.js → filter-builder-DX-xC3Yd.js} +14 -6
- package/dist/filter-builder-DX-xC3Yd.js.map +1 -0
- package/dist/{filter-builder-ByGhxLf_.cjs → filter-builder-Dt5E8PSX.cjs} +14 -6
- package/dist/filter-builder-Dt5E8PSX.cjs.map +1 -0
- package/dist/{flow-runtime-BVFjMU5-.cjs → flow-runtime-CQ4wf8co.cjs} +3 -3
- package/dist/{flow-runtime-BVFjMU5-.cjs.map → flow-runtime-CQ4wf8co.cjs.map} +1 -1
- package/dist/{flow-runtime-DrUWyTS0.js → flow-runtime-Q2vD7R3c.js} +3 -3
- package/dist/{flow-runtime-DrUWyTS0.js.map → flow-runtime-Q2vD7R3c.js.map} +1 -1
- package/dist/{flow-screen-3fk6J33y.js → flow-screen-B1y2yizP.js} +68 -29
- package/dist/flow-screen-B1y2yizP.js.map +1 -0
- package/dist/{flow-screen-6y5E_HT6.cjs → flow-screen-sw2cP70t.cjs} +67 -28
- package/dist/flow-screen-sw2cP70t.cjs.map +1 -0
- package/dist/index.cjs +2304 -247
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +119 -12
- package/dist/index.d.ts +119 -12
- package/dist/index.js +2225 -172
- package/dist/index.js.map +1 -1
- package/dist/{inline-flow-host-C4KmZIYE.cjs → inline-flow-host-CkhoA69R.cjs} +3 -3
- package/dist/{inline-flow-host-C4KmZIYE.cjs.map → inline-flow-host-CkhoA69R.cjs.map} +1 -1
- package/dist/{inline-flow-host-YnnvWjdU.js → inline-flow-host-DqbXqcPS.js} +3 -3
- package/dist/{inline-flow-host-YnnvWjdU.js.map → inline-flow-host-DqbXqcPS.js.map} +1 -1
- package/dist/{lazy-relation-picker-dialog-DN4bvQfg.js → lazy-relation-picker-dialog-BaIEWLbF.js} +3 -52
- package/dist/lazy-relation-picker-dialog-BaIEWLbF.js.map +1 -0
- package/dist/{lazy-relation-picker-dialog-BAKBRn3V.cjs → lazy-relation-picker-dialog-CeRzOAi0.cjs} +3 -52
- package/dist/lazy-relation-picker-dialog-CeRzOAi0.cjs.map +1 -0
- package/dist/{page-flow-route-CtEfqAvU.js → page-flow-route-CP7cS7FY.js} +7 -7
- package/dist/page-flow-route-CP7cS7FY.js.map +1 -0
- package/dist/{page-flow-route-DEjAxHe3.cjs → page-flow-route-UDx8qvdM.cjs} +10 -10
- package/dist/page-flow-route-UDx8qvdM.cjs.map +1 -0
- package/dist/{place-node-Bhl8B3fF.cjs → place-node-C5ZmuHaN.cjs} +208 -55
- package/dist/place-node-C5ZmuHaN.cjs.map +1 -0
- package/dist/{place-node-BdxT-30h.js → place-node-DwWAigv0.js} +205 -58
- package/dist/place-node-DwWAigv0.js.map +1 -0
- package/dist/record-trail-store-Cjo9vtod.cjs +126 -0
- package/dist/record-trail-store-Cjo9vtod.cjs.map +1 -0
- package/dist/record-trail-store-Dn6bVry4.js +91 -0
- package/dist/record-trail-store-Dn6bVry4.js.map +1 -0
- package/dist/{related-list-cards-Bc_vfCE_.cjs → related-list-cards-BypKfm9t.cjs} +3 -3
- package/dist/{related-list-cards-Bc_vfCE_.cjs.map → related-list-cards-BypKfm9t.cjs.map} +1 -1
- package/dist/{related-list-cards-BvkD5OcC.js → related-list-cards-Dcmruy09.js} +3 -3
- package/dist/{related-list-cards-BvkD5OcC.js.map → related-list-cards-Dcmruy09.js.map} +1 -1
- package/dist/{related-list-timeline-k0DtnxTs.cjs → related-list-timeline-D-KClgKb.cjs} +4 -4
- package/dist/related-list-timeline-D-KClgKb.cjs.map +1 -0
- package/dist/{related-list-timeline-DCrwN9oO.js → related-list-timeline-DZON_qSc.js} +4 -4
- package/dist/related-list-timeline-DZON_qSc.js.map +1 -0
- package/dist/{related-rows-BW9Jr07T.cjs → related-rows-DgF05ssi.cjs} +4 -4
- package/dist/{related-rows-BW9Jr07T.cjs.map → related-rows-DgF05ssi.cjs.map} +1 -1
- package/dist/{related-rows-CPE-9FFr.js → related-rows-ETZl5CB9.js} +4 -4
- package/dist/{related-rows-CPE-9FFr.js.map → related-rows-ETZl5CB9.js.map} +1 -1
- package/dist/{relation-create-DDEYcKfw.cjs → relation-create-B8zngAcA.cjs} +82 -13
- package/dist/relation-create-B8zngAcA.cjs.map +1 -0
- package/dist/{relation-create-C43cdPQt.js → relation-create-CeCWyxIb.js} +79 -16
- package/dist/relation-create-CeCWyxIb.js.map +1 -0
- package/dist/{relation-many-picker-BlrGRwMV.js → relation-many-picker-CT7nKfE2.js} +12 -10
- package/dist/relation-many-picker-CT7nKfE2.js.map +1 -0
- package/dist/{relation-many-picker-Br-zBHxT.cjs → relation-many-picker-Cq0EHY5I.cjs} +12 -10
- package/dist/relation-many-picker-Cq0EHY5I.cjs.map +1 -0
- package/dist/{relation-picker-x7P5HKie.js → relation-picker-CCyuMeGq.js} +13 -10
- package/dist/relation-picker-CCyuMeGq.js.map +1 -0
- package/dist/{relation-picker-ADjxI_TI.cjs → relation-picker-DmwTzgsd.cjs} +13 -10
- package/dist/relation-picker-DmwTzgsd.cjs.map +1 -0
- package/dist/{relation-picker-dialog-CyKliBkN.js → relation-picker-dialog-CoWKjNv8.js} +5 -5
- package/dist/relation-picker-dialog-CoWKjNv8.js.map +1 -0
- package/dist/{relation-picker-dialog-DimVQF9Y.cjs → relation-picker-dialog-mbwyNK43.cjs} +5 -5
- package/dist/relation-picker-dialog-mbwyNK43.cjs.map +1 -0
- package/dist/{relation-select-Cr3CfNLy.cjs → relation-select-CguIo2M5.cjs} +2 -2
- package/dist/{relation-select-Cr3CfNLy.cjs.map → relation-select-CguIo2M5.cjs.map} +1 -1
- package/dist/{relation-select-B8Fq4o-t.js → relation-select-nAYxhopN.js} +2 -2
- package/dist/{relation-select-B8Fq4o-t.js.map → relation-select-nAYxhopN.js.map} +1 -1
- package/dist/{success-notices-Dl3M1fZP.cjs → success-notices-Cl4b6MLQ.cjs} +3 -2
- package/dist/success-notices-Cl4b6MLQ.cjs.map +1 -0
- package/dist/{success-notices-LDEgEkc4.js → success-notices-CrcWOGSo.js} +3 -2
- package/dist/success-notices-CrcWOGSo.js.map +1 -0
- package/dist/{use-dialog-filter-state-tNvqAAjh.cjs → use-dialog-filter-state-CpWblz3C.cjs} +2 -2
- package/dist/{use-dialog-filter-state-tNvqAAjh.cjs.map → use-dialog-filter-state-CpWblz3C.cjs.map} +1 -1
- package/dist/{use-dialog-filter-state-B_MqzHaT.js → use-dialog-filter-state-ZYxOp5WO.js} +2 -2
- package/dist/{use-dialog-filter-state-B_MqzHaT.js.map → use-dialog-filter-state-ZYxOp5WO.js.map} +1 -1
- package/dist/{use-record-subtitle-bfwIoUme.cjs → use-record-subtitle-B4lTR7pf.cjs} +5 -8
- package/dist/use-record-subtitle-B4lTR7pf.cjs.map +1 -0
- package/dist/{use-record-subtitle-gIaCGgbI.js → use-record-subtitle-pWMBqQx9.js} +5 -8
- package/dist/use-record-subtitle-pWMBqQx9.js.map +1 -0
- package/package.json +9 -9
- package/dist/bulk-summary-2HLgz7Il.cjs.map +0 -1
- package/dist/bulk-summary-D6A0ua21.js.map +0 -1
- package/dist/check-confirm-dialog-BT04Mieo.cjs.map +0 -1
- package/dist/filter-builder-ByGhxLf_.cjs.map +0 -1
- package/dist/filter-builder-z3Axzf9Y.js.map +0 -1
- package/dist/flow-screen-3fk6J33y.js.map +0 -1
- package/dist/flow-screen-6y5E_HT6.cjs.map +0 -1
- package/dist/lazy-relation-picker-dialog-BAKBRn3V.cjs.map +0 -1
- package/dist/lazy-relation-picker-dialog-DN4bvQfg.js.map +0 -1
- package/dist/page-flow-route-CtEfqAvU.js.map +0 -1
- package/dist/page-flow-route-DEjAxHe3.cjs.map +0 -1
- package/dist/place-node-BdxT-30h.js.map +0 -1
- package/dist/place-node-Bhl8B3fF.cjs.map +0 -1
- package/dist/related-list-timeline-DCrwN9oO.js.map +0 -1
- package/dist/related-list-timeline-k0DtnxTs.cjs.map +0 -1
- package/dist/relation-create-C43cdPQt.js.map +0 -1
- package/dist/relation-create-DDEYcKfw.cjs.map +0 -1
- package/dist/relation-many-picker-BlrGRwMV.js.map +0 -1
- package/dist/relation-many-picker-Br-zBHxT.cjs.map +0 -1
- package/dist/relation-picker-ADjxI_TI.cjs.map +0 -1
- package/dist/relation-picker-dialog-CyKliBkN.js.map +0 -1
- package/dist/relation-picker-dialog-DimVQF9Y.cjs.map +0 -1
- package/dist/relation-picker-x7P5HKie.js.map +0 -1
- package/dist/resource-page-router-BRpUnhay.js +0 -1958
- package/dist/resource-page-router-BRpUnhay.js.map +0 -1
- package/dist/resource-page-router-qEd2xb55.cjs +0 -2167
- package/dist/resource-page-router-qEd2xb55.cjs.map +0 -1
- package/dist/success-notices-Dl3M1fZP.cjs.map +0 -1
- package/dist/success-notices-LDEgEkc4.js.map +0 -1
- package/dist/use-record-subtitle-bfwIoUme.cjs.map +0 -1
- 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-
|
|
3
|
-
const require_success_notices = require("./success-notices-
|
|
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-
|
|
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":"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"}
|
|
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 {
|
|
3
|
-
import { t as useNoticeSink } from "./success-notices-
|
|
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-
|
|
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":";;;;;;;;;;AAwCA,MAAM,YAAwC,OAAO,OAAO,CAAC,CAAC;AAS9D,SAAS,gBACP,MACA,MACA,QACA,UACA,QACgF;CAChF,MAAM,IAAI,aAAa;CAEvB,MAAM,MAAM,aAAa,sBAAsB,KAAA,IAAY;CAC3D,MAAM,OAAO,YAAY,GAAG;CAG5B,MAAM,cAAc,cAAc,GAAG;CACrC,MAAM,CAAC,YAAY,iBAAiB,SAAS,KAAK;CAClD,MAAM,SAAS,OAA4C,IAAI;CAC/D,MAAM,SAAS,OAAO,IAAI;CAC1B,OAAO,UAAU;CACjB,sBACc;EACV,OAAO,UAAU,KAAK;EACtB,OAAO,UAAU;CACnB,GACA,CAAC,CACH;CAEA,MAAM,SAAS,YAAY,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,MAAM,EAAE,sBAAsB,EAAE,cAAc,yCAAyC,CAAC,CAAC;GACzF;EACF;EAGA,IAAI,OAAO,YAAY,eAAe;GACpC,MAAM,SAAS,IAAI,YAAY,CAAC,CAAC;GACjC,MACE,WAAW,KAAA,IACP,EAAE,uBAAuB,EAAE,cAAc,8BAA8B,CAAC,IACxE,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,GAAG,aAAa,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,OACtC,IAAC,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,SAAS,eAAe;EAAE;EAAU,QAAQ;CAAW,IAAI,CAAC,UAAU,UAAU,CAAC;CACvF,MAAM,OAAO,WAAW,QAAQ;EAC9B,GAAI,WAAW,KAAA,IAAY,CAAC,IAAI,EAAE,OAAO;EACzC,SAAS,UAAU,QAAQ,QAAQ;CACrC,CAAC;CACD,MAAM,SAAS,cAAc,UAAU,UAAU;CACjD,MAAM,YAAY,YAAY,OAAO,CAAC,CAAC,SAAS,IAAI,QAAQ,GAAG,MAAM;CACrE,MAAM,QAAQ,kBAAkB,UAAU,WAAW,MAAM;CAC3D,MAAM,OAAO,YAAY,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,MAAM,WAAW;CACvB,MAAM,IAAI,aAAa;CACvB,MAAM,OAAO,YAAY,QAAQ;CAEjC,MAAM,EAAE,QAAQ,WAAW,gBAAgB,MAAM,MAAM,QAAQ,UADhD,WAAW,OAAO,UAAU,YAAY,OAAO,aAAa,EAAE,GAAG,GACF,CAAC;CAC/E,MAAM,UAAU,OAAO,KAAK;CAC5B,MAAM,aAAa,OAAO,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,gBAAgB;EACd,IAAI,CAAC,WAAW,QAAQ,SAAS;EACjC,QAAQ,UAAU;EAClB,IAAI,SAAS,YAAY,SAAS;GAChC,MACE,SAAS,WAAW,KAAA,IAChB,EAAE,uBAAuB,EAAE,cAAc,8BAA8B,CAAC,IACxE,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,SAAS,eAAe,EAAE,WAAW,KAAK,IAAI,CAAC,IAAI,CAAC;CAC1D,MAAM,OAAO,WAAW,QAAQ,EAAE,SAAS,MAAM,CAAC;CAElD,MAAM,SAAqB;EACzB;EACA,WAAW;EACX,MAAM,KAAA;EACN,aAAa;EACb,GANkB,mBAAmB,IAAI,MAAM,KAMlC;CACf;CACA,MAAM,OAAO,YAAY,MAAM,EAAE,MAAM,CAAC;CACxC,MAAM,IAAI,aAAa;CACvB,MAAM,OAAO,YAAY;CACzB,MAAM,EAAE,QAAQ,WAAW,gBAAgB,MAAM,MAAM,QAAQ,qBAAqB,MAAM;CAC1F,MAAM,UAAU,OAAO,KAAK;CAC5B,MAAM,aAAa,OAAO,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,gBAAgB;EACd,IAAI,CAAC,WAAW,QAAQ,SAAS;EACjC,QAAQ,UAAU;EAClB,IAAI,SAAS,YAAY,SAAS;GAChC,MACE,SAAS,WAAW,KAAA,IAChB,EAAE,uBAAuB,EAAE,cAAc,8BAA8B,CAAC,IACxE,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,MAAM,WAAW;CACvB,MAAM,IAAI,aAAa;CACvB,MAAM,OAAO,YAAY,QAAQ;CACjC,MAAM,SAAS,IAAI,KAAK,IAAQ;CAEhC,MAAM,YAAY,eAAe;EAAE,KAAK,CAAC,GAAG,GAAG;EAAG,OAAO,IAAI;CAAO,IAAI,CAAC,MAAM,CAAC;CAChF,MAAM,SAAS,eAAe;EAAE;EAAU,QAAQ;CAAW,IAAI,CAAC,UAAU,UAAU,CAAC;CACvF,MAAM,OAAO,WAAW,QAAQ;EAAE;EAAW,SAAS;CAAO,CAAC;CAC9D,MAAM,SAAS,cAAc,UAAU,UAAU;CACjD,MAAM,SAAS,IAAI,WAAW,UAAU,UAAU;CAClD,MAAM,CAAC,YAAY,iBAAiB,SAAS,KAAK;CAClD,MAAM,SAAS,OAA4C,IAAI;CAC/D,MAAM,UAAU,OAAO,KAAK;CAC5B,MAAM,cAAc,OAAO,QAAQ;CACnC,YAAY,UAAU;CACtB,MAAM,EAAE,KAAK,aAAa;CAC1B,sBACc;EACV,OAAO,UAAU,KAAK;EACtB,OAAO,UAAU;CACnB,GACA,CAAC,CACH;CACA,MAAM,UACJ,QAAQ,QAAQ,SAAS,YAAY,eAAe,SAAS,YAAY;CAC3E,gBAAgB;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,MACE,YAAY,KAAA,IACR,EAAE,uBAAuB,EAAE,cAAc,8BAA8B,CAAC,IACxE,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,MAAM,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,GAAG,aACD,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,OACE,IAAC,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-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 {
|
|
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 {
|
|
4
|
-
import { t as useNoticeSink } from "./success-notices-
|
|
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,
|
|
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,
|
|
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(`${
|
|
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, {
|
|
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
|
|
333
|
-
const
|
|
334
|
-
const
|
|
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 =
|
|
344
|
-
const cancelWord = (
|
|
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 :
|
|
368
|
-
className: "
|
|
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:
|
|
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-
|
|
786
|
+
//# sourceMappingURL=flow-screen-B1y2yizP.js.map
|