@burojs/ui-shadcn 0.6.0 → 0.8.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 (158) hide show
  1. package/README.md +9 -0
  2. package/dist/bulk-summary-2HLgz7Il.cjs +169 -0
  3. package/dist/bulk-summary-2HLgz7Il.cjs.map +1 -0
  4. package/dist/bulk-summary-D6A0ua21.js +169 -0
  5. package/dist/bulk-summary-D6A0ua21.js.map +1 -0
  6. package/dist/{candidates-relation-picker-Cy0rTxvI.cjs → candidates-relation-picker-C47mF260.cjs} +4 -4
  7. package/dist/{candidates-relation-picker-Cy0rTxvI.cjs.map → candidates-relation-picker-C47mF260.cjs.map} +1 -1
  8. package/dist/{candidates-relation-picker-B116Bmqi.js → candidates-relation-picker-CGXItMN0.js} +3 -3
  9. package/dist/{candidates-relation-picker-B116Bmqi.js.map → candidates-relation-picker-CGXItMN0.js.map} +1 -1
  10. package/dist/{check-confirm-dialog--gPFnEeg.cjs → check-confirm-dialog-BT04Mieo.cjs} +475 -107
  11. package/dist/check-confirm-dialog-BT04Mieo.cjs.map +1 -0
  12. package/dist/{check-confirm-dialog-bRT8lt0d.js → check-confirm-dialog-CtiRzOAt.js} +391 -108
  13. package/dist/check-confirm-dialog-CtiRzOAt.js.map +1 -0
  14. package/dist/{dialog-B_rt9S1c.js → dialog-C123sVQ8.js} +4 -4
  15. package/dist/dialog-C123sVQ8.js.map +1 -0
  16. package/dist/{dialog-DRNUq9EA.cjs → dialog-vmMdfUWs.cjs} +3 -3
  17. package/dist/dialog-vmMdfUWs.cjs.map +1 -0
  18. package/dist/{filter-builder-nXqtM_h4.cjs → filter-builder-ByGhxLf_.cjs} +1021 -154
  19. package/dist/filter-builder-ByGhxLf_.cjs.map +1 -0
  20. package/dist/{filter-builder-Bhn-dhZV.js → filter-builder-z3Axzf9Y.js} +929 -140
  21. package/dist/filter-builder-z3Axzf9Y.js.map +1 -0
  22. package/dist/{flow-runtime-D3avVH0I.cjs → flow-runtime-BVFjMU5-.cjs} +64 -29
  23. package/dist/flow-runtime-BVFjMU5-.cjs.map +1 -0
  24. package/dist/{flow-runtime-s6lkRPKF.js → flow-runtime-DrUWyTS0.js} +65 -30
  25. package/dist/flow-runtime-DrUWyTS0.js.map +1 -0
  26. package/dist/{flow-screen-CCvgDI9G.js → flow-screen-3fk6J33y.js} +130 -87
  27. package/dist/flow-screen-3fk6J33y.js.map +1 -0
  28. package/dist/{flow-screen-Ci_Sik4Y.cjs → flow-screen-6y5E_HT6.cjs} +130 -87
  29. package/dist/flow-screen-6y5E_HT6.cjs.map +1 -0
  30. package/dist/index.cjs +1186 -950
  31. package/dist/index.cjs.map +1 -1
  32. package/dist/index.d.cts +631 -322
  33. package/dist/index.d.ts +631 -322
  34. package/dist/index.js +1100 -870
  35. package/dist/index.js.map +1 -1
  36. package/dist/{inline-flow-host-DV1et3c6.cjs → inline-flow-host-C4KmZIYE.cjs} +5 -5
  37. package/dist/inline-flow-host-C4KmZIYE.cjs.map +1 -0
  38. package/dist/{inline-flow-host-B6FI0ctQ.js → inline-flow-host-YnnvWjdU.js} +5 -5
  39. package/dist/inline-flow-host-YnnvWjdU.js.map +1 -0
  40. package/dist/{candidates-gate-D33jXLHO.cjs → lazy-relation-picker-dialog-BAKBRn3V.cjs} +10 -10
  41. package/dist/lazy-relation-picker-dialog-BAKBRn3V.cjs.map +1 -0
  42. package/dist/{candidates-gate-Bb70LPAy.js → lazy-relation-picker-dialog-DN4bvQfg.js} +11 -11
  43. package/dist/lazy-relation-picker-dialog-DN4bvQfg.js.map +1 -0
  44. package/dist/{page-flow-route-i3ZfJ1sd.js → page-flow-route-CtEfqAvU.js} +35 -23
  45. package/dist/page-flow-route-CtEfqAvU.js.map +1 -0
  46. package/dist/{page-flow-route-hdnULWqd.cjs → page-flow-route-DEjAxHe3.cjs} +42 -30
  47. package/dist/page-flow-route-DEjAxHe3.cjs.map +1 -0
  48. package/dist/{place-node-NraNYKPx.js → place-node-BdxT-30h.js} +1980 -1041
  49. package/dist/place-node-BdxT-30h.js.map +1 -0
  50. package/dist/{place-node-C5SmIAYU.cjs → place-node-Bhl8B3fF.cjs} +2038 -1087
  51. package/dist/place-node-Bhl8B3fF.cjs.map +1 -0
  52. package/dist/{related-list-cards-CFq3meUn.cjs → related-list-cards-Bc_vfCE_.cjs} +9 -4
  53. package/dist/{related-list-cards-CFq3meUn.cjs.map → related-list-cards-Bc_vfCE_.cjs.map} +1 -1
  54. package/dist/{related-list-cards-BWZ9GfOG.js → related-list-cards-BvkD5OcC.js} +9 -4
  55. package/dist/{related-list-cards-BWZ9GfOG.js.map → related-list-cards-BvkD5OcC.js.map} +1 -1
  56. package/dist/related-list-timeline-DCrwN9oO.js +167 -0
  57. package/dist/related-list-timeline-DCrwN9oO.js.map +1 -0
  58. package/dist/related-list-timeline-k0DtnxTs.cjs +167 -0
  59. package/dist/related-list-timeline-k0DtnxTs.cjs.map +1 -0
  60. package/dist/{related-rows-ea43qhhy.cjs → related-rows-BW9Jr07T.cjs} +27 -16
  61. package/dist/related-rows-BW9Jr07T.cjs.map +1 -0
  62. package/dist/{related-rows-o0_t-Yce.js → related-rows-CPE-9FFr.js} +25 -14
  63. package/dist/related-rows-CPE-9FFr.js.map +1 -0
  64. package/dist/{search-access-CPPmfuvy.js → relation-create-C43cdPQt.js} +595 -13
  65. package/dist/relation-create-C43cdPQt.js.map +1 -0
  66. package/dist/{search-access-Dugvttlg.cjs → relation-create-DDEYcKfw.cjs} +633 -14
  67. package/dist/relation-create-DDEYcKfw.cjs.map +1 -0
  68. package/dist/{relation-many-picker-bvkjkv-k.js → relation-many-picker-BlrGRwMV.js} +210 -104
  69. package/dist/relation-many-picker-BlrGRwMV.js.map +1 -0
  70. package/dist/{relation-many-picker-C8zFEjwd.cjs → relation-many-picker-Br-zBHxT.cjs} +218 -112
  71. package/dist/relation-many-picker-Br-zBHxT.cjs.map +1 -0
  72. package/dist/{relation-picker-gag0qSR5.cjs → relation-picker-ADjxI_TI.cjs} +36 -73
  73. package/dist/relation-picker-ADjxI_TI.cjs.map +1 -0
  74. package/dist/{relation-picker-dialog-B6nIiJBf.js → relation-picker-dialog-CyKliBkN.js} +134 -36
  75. package/dist/relation-picker-dialog-CyKliBkN.js.map +1 -0
  76. package/dist/{relation-picker-dialog-0IzkjmiA.cjs → relation-picker-dialog-DimVQF9Y.cjs} +133 -35
  77. package/dist/relation-picker-dialog-DimVQF9Y.cjs.map +1 -0
  78. package/dist/{relation-picker-Z0ZJJ5uS.js → relation-picker-x7P5HKie.js} +30 -61
  79. package/dist/relation-picker-x7P5HKie.js.map +1 -0
  80. package/dist/relation-select-B8Fq4o-t.js +48 -0
  81. package/dist/relation-select-B8Fq4o-t.js.map +1 -0
  82. package/dist/relation-select-Cr3CfNLy.cjs +55 -0
  83. package/dist/relation-select-Cr3CfNLy.cjs.map +1 -0
  84. package/dist/{resource-page-router-3RUf9d1j.js → resource-page-router-BRpUnhay.js} +634 -264
  85. package/dist/resource-page-router-BRpUnhay.js.map +1 -0
  86. package/dist/{resource-page-router-PXfMW9nh.cjs → resource-page-router-qEd2xb55.cjs} +694 -312
  87. package/dist/resource-page-router-qEd2xb55.cjs.map +1 -0
  88. package/dist/{rich-text-vTyVjDoa.cjs → rich-text-C_7JCvRo.cjs} +2 -2
  89. package/dist/{rich-text-vTyVjDoa.cjs.map → rich-text-C_7JCvRo.cjs.map} +1 -1
  90. package/dist/{rich-text-DFerywgz.js → rich-text-RcPBf6hr.js} +2 -2
  91. package/dist/{rich-text-DFerywgz.js.map → rich-text-RcPBf6hr.js.map} +1 -1
  92. package/dist/success-notices-Dl3M1fZP.cjs +46 -0
  93. package/dist/success-notices-Dl3M1fZP.cjs.map +1 -0
  94. package/dist/success-notices-LDEgEkc4.js +41 -0
  95. package/dist/success-notices-LDEgEkc4.js.map +1 -0
  96. package/dist/{toaster-Cr51X7bz.cjs → toaster-BbByOytP.cjs} +60 -15
  97. package/dist/toaster-BbByOytP.cjs.map +1 -0
  98. package/dist/{toaster-C02XbB9r.js → toaster-Ct_YHGs6.js} +43 -16
  99. package/dist/toaster-Ct_YHGs6.js.map +1 -0
  100. package/dist/{use-dialog-filter-state-C--oMAZG.js → use-dialog-filter-state-B_MqzHaT.js} +5 -4
  101. package/dist/use-dialog-filter-state-B_MqzHaT.js.map +1 -0
  102. package/dist/{use-dialog-filter-state-Dhe8PcB5.cjs → use-dialog-filter-state-tNvqAAjh.cjs} +6 -5
  103. package/dist/use-dialog-filter-state-tNvqAAjh.cjs.map +1 -0
  104. package/dist/use-record-subtitle-bfwIoUme.cjs +70 -0
  105. package/dist/use-record-subtitle-bfwIoUme.cjs.map +1 -0
  106. package/dist/use-record-subtitle-gIaCGgbI.js +53 -0
  107. package/dist/use-record-subtitle-gIaCGgbI.js.map +1 -0
  108. package/package.json +11 -11
  109. package/dist/bulk-summary-CwSd6Yjb.cjs +0 -108
  110. package/dist/bulk-summary-CwSd6Yjb.cjs.map +0 -1
  111. package/dist/bulk-summary-GxMDP4bw.js +0 -108
  112. package/dist/bulk-summary-GxMDP4bw.js.map +0 -1
  113. package/dist/candidates-gate-Bb70LPAy.js.map +0 -1
  114. package/dist/candidates-gate-D33jXLHO.cjs.map +0 -1
  115. package/dist/check-confirm-dialog--gPFnEeg.cjs.map +0 -1
  116. package/dist/check-confirm-dialog-bRT8lt0d.js.map +0 -1
  117. package/dist/dialog-B_rt9S1c.js.map +0 -1
  118. package/dist/dialog-DRNUq9EA.cjs.map +0 -1
  119. package/dist/filter-builder-Bhn-dhZV.js.map +0 -1
  120. package/dist/filter-builder-nXqtM_h4.cjs.map +0 -1
  121. package/dist/flow-runtime-D3avVH0I.cjs.map +0 -1
  122. package/dist/flow-runtime-s6lkRPKF.js.map +0 -1
  123. package/dist/flow-screen-CCvgDI9G.js.map +0 -1
  124. package/dist/flow-screen-Ci_Sik4Y.cjs.map +0 -1
  125. package/dist/inline-flow-host-B6FI0ctQ.js.map +0 -1
  126. package/dist/inline-flow-host-DV1et3c6.cjs.map +0 -1
  127. package/dist/page-flow-route-hdnULWqd.cjs.map +0 -1
  128. package/dist/page-flow-route-i3ZfJ1sd.js.map +0 -1
  129. package/dist/place-node-C5SmIAYU.cjs.map +0 -1
  130. package/dist/place-node-NraNYKPx.js.map +0 -1
  131. package/dist/related-list-timeline-CCzl71aA.cjs +0 -142
  132. package/dist/related-list-timeline-CCzl71aA.cjs.map +0 -1
  133. package/dist/related-list-timeline-yrPsjA3F.js +0 -142
  134. package/dist/related-list-timeline-yrPsjA3F.js.map +0 -1
  135. package/dist/related-rows-ea43qhhy.cjs.map +0 -1
  136. package/dist/related-rows-o0_t-Yce.js.map +0 -1
  137. package/dist/relation-create-IA40t1hi.cjs +0 -364
  138. package/dist/relation-create-IA40t1hi.cjs.map +0 -1
  139. package/dist/relation-create-h6F9B4C4.js +0 -339
  140. package/dist/relation-create-h6F9B4C4.js.map +0 -1
  141. package/dist/relation-many-picker-C8zFEjwd.cjs.map +0 -1
  142. package/dist/relation-many-picker-bvkjkv-k.js.map +0 -1
  143. package/dist/relation-picker-Z0ZJJ5uS.js.map +0 -1
  144. package/dist/relation-picker-dialog-0IzkjmiA.cjs.map +0 -1
  145. package/dist/relation-picker-dialog-B6nIiJBf.js.map +0 -1
  146. package/dist/relation-picker-gag0qSR5.cjs.map +0 -1
  147. package/dist/resource-page-router-3RUf9d1j.js.map +0 -1
  148. package/dist/resource-page-router-PXfMW9nh.cjs.map +0 -1
  149. package/dist/search-access-CPPmfuvy.js.map +0 -1
  150. package/dist/search-access-Dugvttlg.cjs.map +0 -1
  151. package/dist/shell-BPlk8hEa.js +0 -205
  152. package/dist/shell-BPlk8hEa.js.map +0 -1
  153. package/dist/shell-DxhlsPwm.cjs +0 -271
  154. package/dist/shell-DxhlsPwm.cjs.map +0 -1
  155. package/dist/toaster-C02XbB9r.js.map +0 -1
  156. package/dist/toaster-Cr51X7bz.cjs.map +0 -1
  157. package/dist/use-dialog-filter-state-C--oMAZG.js.map +0 -1
  158. package/dist/use-dialog-filter-state-Dhe8PcB5.cjs.map +0 -1
@@ -1,6 +1,6 @@
1
- const require_check_confirm_dialog = require("./check-confirm-dialog--gPFnEeg.cjs");
1
+ const require_check_confirm_dialog = require("./check-confirm-dialog-BT04Mieo.cjs");
2
2
  const require_button = require("./button-BxnnE28H.cjs");
3
- const require_flow_screen = require("./flow-screen-Ci_Sik4Y.cjs");
3
+ const require_flow_screen = require("./flow-screen-6y5E_HT6.cjs");
4
4
  let _burojs_react = require("@burojs/react");
5
5
  let _tabler_icons_react = require("@tabler/icons-react");
6
6
  let react = require("react");
@@ -82,12 +82,12 @@ function InlineFlowHost({ resource = "", name = "", target: givenTarget, label,
82
82
  role: "status",
83
83
  "data-buro-flow-state": "stale",
84
84
  className: "flex flex-wrap items-center gap-2 text-sm text-muted-foreground",
85
- children: [(0, react_jsx_runtime.jsx)("span", { children: require_check_confirm_dialog.text(t, "flow.noLongerApplies", "This action no longer applies") }), (0, react_jsx_runtime.jsx)(require_button.Button, {
85
+ children: [(0, react_jsx_runtime.jsx)("span", { children: t("flow.noLongerApplies", { defaultValue: "This action no longer applies" }) }), (0, react_jsx_runtime.jsx)(require_button.Button, {
86
86
  type: "button",
87
87
  size: "sm",
88
88
  variant: "outline",
89
89
  onClick: onDiscard,
90
- children: require_check_confirm_dialog.text(t, "flow.discard", "Discard")
90
+ children: t("flow.discard", { defaultValue: "Discard" })
91
91
  })]
92
92
  }) : null, opened ? (0, react_jsx_runtime.jsx)("fieldset", {
93
93
  disabled: stale,
@@ -109,4 +109,4 @@ function InlineFlowHost({ resource = "", name = "", target: givenTarget, label,
109
109
  //#endregion
110
110
  exports.InlineFlowHost = InlineFlowHost;
111
111
 
112
- //# sourceMappingURL=inline-flow-host-DV1et3c6.cjs.map
112
+ //# sourceMappingURL=inline-flow-host-C4KmZIYE.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"inline-flow-host-C4KmZIYE.cjs","names":["useTestIds","useFlowChrome","useMemo","useFlowRun","useScreenState","useOptionalT","Button","HostedFlowScreen","IconLoader2"],"sources":["../src/flows/inline-flow-host.tsx"],"sourcesContent":["'use client';\n/**\n * The inline host (P6 3.12): an item flow drawn in place in a show tree. The run lives in the\n * placement's lease, held by the screen, so a tab switch away and back finds the typed input and\n * a write in flight, and the flow's attempt slot stays the one P4 keys on (user, flow, record).\n */\nimport type { FlowTarget, Row } from '@burojs/core';\nimport type { InlineLease } from '@burojs/react';\nimport { useOptionalT, useScreenState, useTestIds } from '@burojs/react';\nimport { useFlowRun } from '@burojs/react/flows';\nimport { IconLoader2 } from '@tabler/icons-react';\nimport { type ReactElement, useEffect, useMemo } from 'react';\nimport { Button } from '../primitives/button.js';\nimport { useFlowChrome } from './chrome.js';\nimport { type FlowHostBinding, HostedFlowScreen } from './flow-screen.js';\n\nexport interface InlineFlowHostProps {\n /** The resource of an item flow on a record screen; a page names `target` instead. */\n readonly resource?: string;\n readonly name?: string;\n /** The flow, when it is not an item flow of the screen's resource (a page's, P7 3.15). */\n readonly target?: FlowTarget;\n /** The label of a flow with no resource to ask (an app flow's). */\n readonly label?: string;\n /** The record an item flow runs on; none on a page. */\n readonly record?: Row;\n readonly lease?: InlineLease;\n readonly placementKey: string;\n /**\n * The flow no longer applies to the record (its `when` turned false) while the input is dirty\n * or the attempt unresolved: the node stays, read-only, with \"no longer applies\" and Discard.\n */\n readonly stale?: boolean;\n readonly onDiscard?: () => void;\n}\n\nexport function InlineFlowHost({\n resource = '',\n name = '',\n target: givenTarget,\n label,\n record,\n lease,\n placementKey,\n stale = false,\n onDiscard,\n}: InlineFlowHostProps): ReactElement {\n const tid = useTestIds();\n const own = useFlowChrome(resource, name);\n const chrome = label === undefined ? own : { ...own, label, labelText: label };\n const target = useMemo<FlowTarget>(\n () => givenTarget ?? { resource, action: name },\n [givenTarget, resource, name],\n );\n const flow = useFlowRun(target, {\n ...(record === undefined ? {} : { record }),\n surface: 'inline',\n ...(lease === undefined ? {} : { lease }),\n });\n // `then` is this host's to run, once the form it succeeded in has started over.\n flow.holdThen(true);\n const { run, snapshot, compiled } = flow;\n const binding = useMemo<FlowHostBinding>(\n () => ({\n kind: 'inline',\n close: () => flow.reset(),\n finish: () => {\n flow.runThen();\n flow.reset();\n },\n }),\n // biome-ignore lint/correctness/useExhaustiveDependencies: reset and runThen are stable callbacks\n [flow.reset, flow.runThen],\n );\n const holder = useScreenState()?.inline;\n const t = useOptionalT();\n // The lease's guard state, from the run: unsaved input or an unresolved attempt (dirty), a\n // write in flight (locked), and whether a node shows it. The screen's blockers read these\n // when the node is not on screen (tab switched, placement hidden).\n useEffect(() => {\n if (lease === undefined || holder === undefined) return;\n lease.mounted += 1;\n lease.label = chrome.labelText;\n holder.touch();\n return () => {\n lease.mounted -= 1;\n holder.touch();\n };\n }, [lease, holder, chrome.labelText]);\n useEffect(() => {\n if (lease === undefined || holder === undefined || run === null) return;\n const sync = () => {\n const now = run.getSnapshot();\n if (lease.initial === undefined && now.history.includes('open'))\n lease.initial = JSON.stringify(now.values);\n const dirty =\n now.outcome?.outcome !== 'success' &&\n (now.outcome?.outcome === 'unknown' ||\n (lease.initial !== undefined && JSON.stringify(now.values) !== lease.initial));\n const locked = now.control === 'running';\n if (dirty === lease.dirty && locked === lease.locked) return;\n lease.dirty = dirty;\n lease.locked = locked;\n holder.touch();\n };\n sync();\n return run.subscribe(sync);\n }, [lease, holder, run]);\n const opened =\n run !== null &&\n compiled !== null &&\n (snapshot.history.includes('open') || snapshot.step === 'outcome');\n return (\n <div\n data-testid={tid.inlineFlow(compiled?.flowId ?? `${resource}/actions/${name}`, placementKey)}\n data-buro-host=\"inline\"\n className=\"flex min-h-0 flex-col gap-4\"\n >\n {stale ? (\n <div\n role=\"status\"\n data-buro-flow-state=\"stale\"\n className=\"flex flex-wrap items-center gap-2 text-sm text-muted-foreground\"\n >\n <span>\n {t('flow.noLongerApplies', { defaultValue: 'This action no longer applies' })}\n </span>\n <Button type=\"button\" size=\"sm\" variant=\"outline\" onClick={onDiscard}>\n {t('flow.discard', { defaultValue: 'Discard' })}\n </Button>\n </div>\n ) : null}\n {opened ? (\n <fieldset disabled={stale} className=\"m-0 min-w-0 border-0 p-0\">\n <HostedFlowScreen\n binding={binding}\n host=\"inline\"\n run={run}\n compiled={compiled}\n label={chrome.labelText}\n record={record}\n />\n </fieldset>\n ) : (\n <IconLoader2 className=\"h-5 w-5 animate-spin self-center\" aria-hidden=\"true\" />\n )}\n </div>\n );\n}\n"],"mappings":"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"}
@@ -1,6 +1,6 @@
1
- import { h as useFlowChrome, m as text } from "./check-confirm-dialog-bRT8lt0d.js";
1
+ import { m as useFlowChrome } from "./check-confirm-dialog-CtiRzOAt.js";
2
2
  import { t as Button } from "./button-MWVeZaqj.js";
3
- import { HostedFlowScreen } from "./flow-screen-CCvgDI9G.js";
3
+ import { HostedFlowScreen } from "./flow-screen-3fk6J33y.js";
4
4
  import { useOptionalT, useScreenState, useTestIds } from "@burojs/react";
5
5
  import { IconLoader2 } from "@tabler/icons-react";
6
6
  import { useEffect, useMemo } from "react";
@@ -82,12 +82,12 @@ function InlineFlowHost({ resource = "", name = "", target: givenTarget, label,
82
82
  role: "status",
83
83
  "data-buro-flow-state": "stale",
84
84
  className: "flex flex-wrap items-center gap-2 text-sm text-muted-foreground",
85
- children: [jsx("span", { children: text(t, "flow.noLongerApplies", "This action no longer applies") }), jsx(Button, {
85
+ children: [jsx("span", { children: t("flow.noLongerApplies", { defaultValue: "This action no longer applies" }) }), jsx(Button, {
86
86
  type: "button",
87
87
  size: "sm",
88
88
  variant: "outline",
89
89
  onClick: onDiscard,
90
- children: text(t, "flow.discard", "Discard")
90
+ children: t("flow.discard", { defaultValue: "Discard" })
91
91
  })]
92
92
  }) : null, opened ? jsx("fieldset", {
93
93
  disabled: stale,
@@ -109,4 +109,4 @@ function InlineFlowHost({ resource = "", name = "", target: givenTarget, label,
109
109
  //#endregion
110
110
  export { InlineFlowHost };
111
111
 
112
- //# sourceMappingURL=inline-flow-host-B6FI0ctQ.js.map
112
+ //# sourceMappingURL=inline-flow-host-YnnvWjdU.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"inline-flow-host-YnnvWjdU.js","names":[],"sources":["../src/flows/inline-flow-host.tsx"],"sourcesContent":["'use client';\n/**\n * The inline host (P6 3.12): an item flow drawn in place in a show tree. The run lives in the\n * placement's lease, held by the screen, so a tab switch away and back finds the typed input and\n * a write in flight, and the flow's attempt slot stays the one P4 keys on (user, flow, record).\n */\nimport type { FlowTarget, Row } from '@burojs/core';\nimport type { InlineLease } from '@burojs/react';\nimport { useOptionalT, useScreenState, useTestIds } from '@burojs/react';\nimport { useFlowRun } from '@burojs/react/flows';\nimport { IconLoader2 } from '@tabler/icons-react';\nimport { type ReactElement, useEffect, useMemo } from 'react';\nimport { Button } from '../primitives/button.js';\nimport { useFlowChrome } from './chrome.js';\nimport { type FlowHostBinding, HostedFlowScreen } from './flow-screen.js';\n\nexport interface InlineFlowHostProps {\n /** The resource of an item flow on a record screen; a page names `target` instead. */\n readonly resource?: string;\n readonly name?: string;\n /** The flow, when it is not an item flow of the screen's resource (a page's, P7 3.15). */\n readonly target?: FlowTarget;\n /** The label of a flow with no resource to ask (an app flow's). */\n readonly label?: string;\n /** The record an item flow runs on; none on a page. */\n readonly record?: Row;\n readonly lease?: InlineLease;\n readonly placementKey: string;\n /**\n * The flow no longer applies to the record (its `when` turned false) while the input is dirty\n * or the attempt unresolved: the node stays, read-only, with \"no longer applies\" and Discard.\n */\n readonly stale?: boolean;\n readonly onDiscard?: () => void;\n}\n\nexport function InlineFlowHost({\n resource = '',\n name = '',\n target: givenTarget,\n label,\n record,\n lease,\n placementKey,\n stale = false,\n onDiscard,\n}: InlineFlowHostProps): ReactElement {\n const tid = useTestIds();\n const own = useFlowChrome(resource, name);\n const chrome = label === undefined ? own : { ...own, label, labelText: label };\n const target = useMemo<FlowTarget>(\n () => givenTarget ?? { resource, action: name },\n [givenTarget, resource, name],\n );\n const flow = useFlowRun(target, {\n ...(record === undefined ? {} : { record }),\n surface: 'inline',\n ...(lease === undefined ? {} : { lease }),\n });\n // `then` is this host's to run, once the form it succeeded in has started over.\n flow.holdThen(true);\n const { run, snapshot, compiled } = flow;\n const binding = useMemo<FlowHostBinding>(\n () => ({\n kind: 'inline',\n close: () => flow.reset(),\n finish: () => {\n flow.runThen();\n flow.reset();\n },\n }),\n // biome-ignore lint/correctness/useExhaustiveDependencies: reset and runThen are stable callbacks\n [flow.reset, flow.runThen],\n );\n const holder = useScreenState()?.inline;\n const t = useOptionalT();\n // The lease's guard state, from the run: unsaved input or an unresolved attempt (dirty), a\n // write in flight (locked), and whether a node shows it. The screen's blockers read these\n // when the node is not on screen (tab switched, placement hidden).\n useEffect(() => {\n if (lease === undefined || holder === undefined) return;\n lease.mounted += 1;\n lease.label = chrome.labelText;\n holder.touch();\n return () => {\n lease.mounted -= 1;\n holder.touch();\n };\n }, [lease, holder, chrome.labelText]);\n useEffect(() => {\n if (lease === undefined || holder === undefined || run === null) return;\n const sync = () => {\n const now = run.getSnapshot();\n if (lease.initial === undefined && now.history.includes('open'))\n lease.initial = JSON.stringify(now.values);\n const dirty =\n now.outcome?.outcome !== 'success' &&\n (now.outcome?.outcome === 'unknown' ||\n (lease.initial !== undefined && JSON.stringify(now.values) !== lease.initial));\n const locked = now.control === 'running';\n if (dirty === lease.dirty && locked === lease.locked) return;\n lease.dirty = dirty;\n lease.locked = locked;\n holder.touch();\n };\n sync();\n return run.subscribe(sync);\n }, [lease, holder, run]);\n const opened =\n run !== null &&\n compiled !== null &&\n (snapshot.history.includes('open') || snapshot.step === 'outcome');\n return (\n <div\n data-testid={tid.inlineFlow(compiled?.flowId ?? `${resource}/actions/${name}`, placementKey)}\n data-buro-host=\"inline\"\n className=\"flex min-h-0 flex-col gap-4\"\n >\n {stale ? (\n <div\n role=\"status\"\n data-buro-flow-state=\"stale\"\n className=\"flex flex-wrap items-center gap-2 text-sm text-muted-foreground\"\n >\n <span>\n {t('flow.noLongerApplies', { defaultValue: 'This action no longer applies' })}\n </span>\n <Button type=\"button\" size=\"sm\" variant=\"outline\" onClick={onDiscard}>\n {t('flow.discard', { defaultValue: 'Discard' })}\n </Button>\n </div>\n ) : null}\n {opened ? (\n <fieldset disabled={stale} className=\"m-0 min-w-0 border-0 p-0\">\n <HostedFlowScreen\n binding={binding}\n host=\"inline\"\n run={run}\n compiled={compiled}\n label={chrome.labelText}\n record={record}\n />\n </fieldset>\n ) : (\n <IconLoader2 className=\"h-5 w-5 animate-spin self-center\" aria-hidden=\"true\" />\n )}\n </div>\n );\n}\n"],"mappings":";;;;;;;;;AAoCA,SAAgB,eAAe,EAC7B,WAAW,IACX,OAAO,IACP,QAAQ,aACR,OACA,QACA,OACA,cACA,QAAQ,OACR,aACoC;CACpC,MAAM,MAAM,WAAW;CACvB,MAAM,MAAM,cAAc,UAAU,IAAI;CACxC,MAAM,SAAS,UAAU,KAAA,IAAY,MAAM;EAAE,GAAG;EAAK;EAAO,WAAW;CAAM;CAC7E,MAAM,SAAS,cACP,eAAe;EAAE;EAAU,QAAQ;CAAK,GAC9C;EAAC;EAAa;EAAU;CAAI,CAC9B;CACA,MAAM,OAAO,WAAW,QAAQ;EAC9B,GAAI,WAAW,KAAA,IAAY,CAAC,IAAI,EAAE,OAAO;EACzC,SAAS;EACT,GAAI,UAAU,KAAA,IAAY,CAAC,IAAI,EAAE,MAAM;CACzC,CAAC;CAED,KAAK,SAAS,IAAI;CAClB,MAAM,EAAE,KAAK,UAAU,aAAa;CACpC,MAAM,UAAU,eACP;EACL,MAAM;EACN,aAAa,KAAK,MAAM;EACxB,cAAc;GACZ,KAAK,QAAQ;GACb,KAAK,MAAM;EACb;CACF,IAEA,CAAC,KAAK,OAAO,KAAK,OAAO,CAC3B;CACA,MAAM,SAAS,eAAe,CAAC,EAAE;CACjC,MAAM,IAAI,aAAa;CAIvB,gBAAgB;EACd,IAAI,UAAU,KAAA,KAAa,WAAW,KAAA,GAAW;EACjD,MAAM,WAAW;EACjB,MAAM,QAAQ,OAAO;EACrB,OAAO,MAAM;EACb,aAAa;GACX,MAAM,WAAW;GACjB,OAAO,MAAM;EACf;CACF,GAAG;EAAC;EAAO;EAAQ,OAAO;CAAS,CAAC;CACpC,gBAAgB;EACd,IAAI,UAAU,KAAA,KAAa,WAAW,KAAA,KAAa,QAAQ,MAAM;EACjE,MAAM,aAAa;GACjB,MAAM,MAAM,IAAI,YAAY;GAC5B,IAAI,MAAM,YAAY,KAAA,KAAa,IAAI,QAAQ,SAAS,MAAM,GAC5D,MAAM,UAAU,KAAK,UAAU,IAAI,MAAM;GAC3C,MAAM,QACJ,IAAI,SAAS,YAAY,cACxB,IAAI,SAAS,YAAY,aACvB,MAAM,YAAY,KAAA,KAAa,KAAK,UAAU,IAAI,MAAM,MAAM,MAAM;GACzE,MAAM,SAAS,IAAI,YAAY;GAC/B,IAAI,UAAU,MAAM,SAAS,WAAW,MAAM,QAAQ;GACtD,MAAM,QAAQ;GACd,MAAM,SAAS;GACf,OAAO,MAAM;EACf;EACA,KAAK;EACL,OAAO,IAAI,UAAU,IAAI;CAC3B,GAAG;EAAC;EAAO;EAAQ;CAAG,CAAC;CACvB,MAAM,SACJ,QAAQ,QACR,aAAa,SACZ,SAAS,QAAQ,SAAS,MAAM,KAAK,SAAS,SAAS;CAC1D,OACE,KAAC,OAAD;EACE,eAAa,IAAI,WAAW,UAAU,UAAU,GAAG,SAAS,WAAW,QAAQ,YAAY;EAC3F,kBAAe;EACf,WAAU;EAHZ,UAAA,CAKG,QACC,KAAC,OAAD;GACE,MAAK;GACL,wBAAqB;GACrB,WAAU;GAHZ,UAAA,CAKE,IAAC,QAAD,EAAA,UACG,EAAE,wBAAwB,EAAE,cAAc,gCAAgC,CAAC,EACxE,CAAA,GACN,IAAC,QAAD;IAAQ,MAAK;IAAS,MAAK;IAAK,SAAQ;IAAU,SAAS;IACxD,UAAA,EAAE,gBAAgB,EAAE,cAAc,UAAU,CAAC;GACxC,CAAA,CACL;EACH,CAAA,IAAA,MACH,SACC,IAAC,YAAD;GAAU,UAAU;GAAO,WAAU;GACnC,UAAA,IAAC,kBAAD;IACW;IACT,MAAK;IACA;IACK;IACV,OAAO,OAAO;IACN;GACT,CAAA;EACO,CAAA,IAEV,IAAC,aAAD;GAAa,WAAU;GAAmC,eAAY;EAAQ,CAAA,CAE7E;;AAET"}
@@ -4,15 +4,6 @@ let _burojs_react = require("@burojs/react");
4
4
  let react = require("react");
5
5
  react = require_use_theme.__toESM(react, 1);
6
6
  let react_jsx_runtime = require("react/jsx-runtime");
7
- //#region src/fields/lazy-relation-picker-dialog.tsx
8
- const Dialog = (0, react.lazy)(() => Promise.resolve().then(() => require("./relation-picker-dialog-0IzkjmiA.cjs")).then((module) => ({ default: module.RelationPickerDialog })));
9
- function LazyRelationPickerDialog(props) {
10
- return (0, react_jsx_runtime.jsx)(react.Suspense, {
11
- fallback: null,
12
- children: (0, react_jsx_runtime.jsx)(Dialog, { ...props })
13
- });
14
- }
15
- //#endregion
16
7
  //#region src/fields/candidates-match.ts
17
8
  function rowMatches(row, filters) {
18
9
  let unknown = false;
@@ -131,6 +122,15 @@ function useCandidateSelection(input) {
131
122
  };
132
123
  }
133
124
  //#endregion
125
+ //#region src/fields/lazy-relation-picker-dialog.tsx
126
+ const Dialog = (0, react.lazy)(() => Promise.resolve().then(() => require("./relation-picker-dialog-DimVQF9Y.cjs")).then((module) => ({ default: module.RelationPickerDialog })));
127
+ function LazyRelationPickerDialog(props) {
128
+ return (0, react_jsx_runtime.jsx)(react.Suspense, {
129
+ fallback: null,
130
+ children: (0, react_jsx_runtime.jsx)(Dialog, { ...props })
131
+ });
132
+ }
133
+ //#endregion
134
134
  Object.defineProperty(exports, "LazyRelationPickerDialog", {
135
135
  enumerable: true,
136
136
  get: function() {
@@ -150,4 +150,4 @@ Object.defineProperty(exports, "useCandidatesGate", {
150
150
  }
151
151
  });
152
152
 
153
- //# sourceMappingURL=candidates-gate-D33jXLHO.cjs.map
153
+ //# sourceMappingURL=lazy-relation-picker-dialog-BAKBRn3V.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lazy-relation-picker-dialog-BAKBRn3V.cjs","names":["useOptionalT","useApp","useTestIds","useI18nCaps","useCandidatesAccess","React","resolveCandidates","declaredText","resolveFieldLabel","lazy","Suspense"],"sources":["../src/fields/candidates-match.ts","../src/fields/candidates-gate.ts","../src/fields/lazy-relation-picker-dialog.tsx"],"sourcesContent":["import type { FixedFilterInput } from '@burojs/core';\n\ntype Entry = { readonly field?: unknown; readonly op?: unknown; readonly value?: unknown };\n\n/**\n * Whether `row` meets every candidate filter (P7 3.4): `true`, `false`, or `undefined` when the\n * row does not carry a field a filter names (a narrower projection), so nothing can be said.\n * Only the flat `{ field, op, value }` entries candidates resolve to are evaluated; this is what\n * \"does not match the current filter\" is decided on, never what is sent.\n */\nexport function rowMatches(\n row: Readonly<Record<string, unknown>>,\n filters: readonly FixedFilterInput[],\n): boolean | undefined {\n let unknown = false;\n for (const filter of filters) {\n const group = filter as { readonly and?: unknown; readonly or?: unknown };\n if (Array.isArray(group.and) || Array.isArray(group.or)) {\n const parts = (\n Array.isArray(group.and) ? group.and : group.or\n ) as readonly FixedFilterInput[];\n const answers = parts.map((part) => rowMatches(row, [part]));\n const all = Array.isArray(group.and);\n const decided = answers.filter((answer) => answer !== undefined);\n if (all ? decided.includes(false) : decided.length > 0 && !decided.includes(true))\n return false;\n if (decided.length < answers.length && !(all ? false : decided.includes(true)))\n unknown = true;\n continue;\n }\n const { field, op, value } = filter as Entry;\n if (typeof field !== 'string' || typeof op !== 'string') continue;\n if (!Object.hasOwn(row, field)) {\n unknown = true;\n continue;\n }\n const held = holds(row[field], op, value);\n if (held === false) return false;\n if (held === undefined) unknown = true;\n }\n return unknown ? undefined : true;\n}\n\nfunction holds(actual: unknown, op: string, expected: unknown): boolean | undefined {\n const list = Array.isArray(expected) ? (expected as readonly unknown[]) : undefined;\n const text = (value: unknown) => String(value ?? '').toLowerCase();\n switch (op) {\n case 'eq':\n return Object.is(actual, expected) || String(actual) === String(expected);\n case 'neq':\n return !(Object.is(actual, expected) || String(actual) === String(expected));\n case 'in':\n return list === undefined\n ? undefined\n : list.some((entry) => String(entry) === String(actual));\n case 'notIn':\n return list === undefined\n ? undefined\n : !list.some((entry) => String(entry) === String(actual));\n case 'gt':\n return compare(actual, expected, (a, b) => a > b);\n case 'gte':\n return compare(actual, expected, (a, b) => a >= b);\n case 'lt':\n return compare(actual, expected, (a, b) => a < b);\n case 'lte':\n return compare(actual, expected, (a, b) => a <= b);\n case 'contains':\n return text(actual).includes(text(expected));\n case 'startsWith':\n return text(actual).startsWith(text(expected));\n case 'endsWith':\n return text(actual).endsWith(text(expected));\n case 'isNull':\n return actual === null || actual === undefined;\n case 'isNotNull':\n return actual !== null && actual !== undefined;\n default:\n return undefined;\n }\n}\n\nfunction compare(\n actual: unknown,\n expected: unknown,\n test: (a: number | string, b: number | string) => boolean,\n): boolean | undefined {\n const both =\n (typeof actual === 'number' && typeof expected === 'number') ||\n (typeof actual === 'string' && typeof expected === 'string');\n return both ? test(actual as number | string, expected as number | string) : undefined;\n}\n","import {\n type FixedFilterInput,\n type RelationCandidate,\n type ScreenSources,\n resolveCandidates,\n resolveFieldLabel,\n declaredText,\n} from '@burojs/core';\nimport { useApp, useCandidatesAccess, useI18nCaps, useOptionalT, useTestIds } from '@burojs/react';\nimport * as React from 'react';\nimport { rowMatches } from './candidates-match.js';\n\n/** A select that must not read its target now (P7 3.4): why, and what it tells the user. */\nexport type CandidatesGate =\n | { readonly kind: 'pending' }\n | { readonly kind: 'empty'; readonly message: string; readonly testId: string }\n | { readonly kind: 'forbidden'; readonly message: string; readonly testId: string };\n\nconst NO_SOURCES: ScreenSources = Object.freeze({});\nconst NO_FILTERS: readonly FixedFilterInput[] = Object.freeze([]);\n\n/**\n * A relation field's `candidates` as the select applies them (P7 3.4): the filters to send and,\n * when they say the target must not be read yet, the gate that tells the user why. The refs are\n * read from the owner's projected sources, so a field P5 hides reads `null` (the select asks for\n * it first, and sends nothing); a candidate on a target field the user may not view forbids the\n * read; an undecided source or permission map keeps the select disabled.\n */\nexport function useCandidatesGate(input: {\n readonly to: string | undefined;\n readonly candidates: readonly RelationCandidate[] | undefined;\n readonly sources: ScreenSources | undefined;\n readonly resource: string;\n readonly fieldName: string;\n}): { readonly filters: readonly FixedFilterInput[]; readonly gate?: CandidatesGate } {\n const { to, candidates, sources, resource, fieldName } = input;\n const t = useOptionalT();\n const app = useApp();\n const tid = useTestIds();\n const ownerCaps = useI18nCaps(resource);\n const targetCaps = useI18nCaps(to ?? resource);\n const access = useCandidatesAccess(to ?? '', candidates);\n const resolution = React.useMemo(\n () => resolveCandidates(candidates, sources ?? NO_SOURCES),\n [candidates, sources],\n );\n if (candidates === undefined || candidates.length === 0) return { filters: NO_FILTERS };\n const testId = tid.relationCandidatesHint(resource, fieldName);\n if (access.status === 'pending') return { filters: NO_FILTERS, gate: { kind: 'pending' } };\n if (access.status === 'forbidden') {\n const declared = declaredText(\n 'field',\n (app.registry.get(to ?? '')?.fields as Record<string, unknown> | undefined)?.[access.field],\n 'label',\n );\n const label = resolveFieldLabel(targetCaps, access.field, declared);\n return {\n filters: NO_FILTERS,\n gate: {\n kind: 'forbidden',\n testId,\n message: t('relationPicker.cannotSearchBy', {\n params: { label },\n defaultValue: `You cannot search by ${label}`,\n }),\n },\n };\n }\n if (resolution.status === 'pending') return { filters: NO_FILTERS, gate: { kind: 'pending' } };\n if (resolution.status === 'empty') {\n const { source, path } = resolution.hint;\n const head = path[0];\n // The field a `values` / `record` ref reads is labelled as the owner labels it; any other\n // source (`scope`, `user`) names the key it reads, humanised.\n const own = source === 'values' || source === 'record';\n const declared =\n own && head !== undefined\n ? declaredText(\n 'field',\n (app.registry.get(resource)?.fields as Record<string, unknown> | undefined)?.[head],\n 'label',\n )\n : undefined;\n const label = resolveFieldLabel(ownerCaps, head ?? source, declared);\n return {\n filters: NO_FILTERS,\n gate: {\n kind: 'empty',\n testId,\n message: t('relationPicker.chooseFirst', {\n params: { label },\n defaultValue: `Choose ${label} first`,\n }),\n },\n };\n }\n return { filters: resolution.filters };\n}\n\n/**\n * A select's declared candidates as the shared select wiring applies them (`CandidatesRelationPicker`\n * and `useRelationPicker`): the gate, the resolved candidate filters prepended to the caller's\n * `fixedFilters`, and whether a loaded row fits them (`undefined` when no candidate filter applies).\n */\nexport function useCandidateSelection(input: {\n readonly to: string | undefined;\n readonly candidates: readonly RelationCandidate[] | undefined;\n readonly sources: ScreenSources | undefined;\n readonly resource: string;\n readonly fieldName: string;\n readonly fixedFilters: readonly FixedFilterInput[] | undefined;\n}): {\n readonly fixedFilters: readonly FixedFilterInput[] | undefined;\n readonly gate?: CandidatesGate;\n readonly rowFits?: (row: Record<string, unknown>) => boolean | undefined;\n} {\n const { fixedFilters } = input;\n const gate = useCandidatesGate(input);\n const filters = gate.filters;\n const combined = React.useMemo(\n () => (filters.length === 0 ? fixedFilters : [...filters, ...(fixedFilters ?? [])]),\n [filters, fixedFilters],\n );\n const rowFits = React.useMemo(\n () =>\n filters.length === 0 ? undefined : (row: Record<string, unknown>) => rowMatches(row, filters),\n [filters],\n );\n return {\n fixedFilters: combined,\n ...(gate.gate === undefined ? {} : { gate: gate.gate }),\n ...(rowFits === undefined ? {} : { rowFits }),\n };\n}\n","import { type ReactElement, Suspense, lazy } from 'react';\nimport type { RelationPickerDialogProps } from './relation-picker-dialog.js';\n\nconst Dialog = lazy(() =>\n import('./relation-picker-dialog.js').then((module) => ({\n default: module.RelationPickerDialog,\n })),\n);\n\n/** The \"Advanced search\" dialog, loaded on the first open: nothing is drawn until it arrives. */\nexport function LazyRelationPickerDialog<const N extends string = string>(\n props: RelationPickerDialogProps<N>,\n): ReactElement {\n return (\n <Suspense fallback={null}>\n <Dialog {...props} />\n </Suspense>\n );\n}\n"],"mappings":"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"}
@@ -3,15 +3,6 @@ import { useApp, useCandidatesAccess, useI18nCaps, useOptionalT, useTestIds } fr
3
3
  import * as React from "react";
4
4
  import { Suspense, lazy } from "react";
5
5
  import { jsx } from "react/jsx-runtime";
6
- //#region src/fields/lazy-relation-picker-dialog.tsx
7
- const Dialog = lazy(() => import("./relation-picker-dialog-B6nIiJBf.js").then((module) => ({ default: module.RelationPickerDialog })));
8
- function LazyRelationPickerDialog(props) {
9
- return jsx(Suspense, {
10
- fallback: null,
11
- children: jsx(Dialog, { ...props })
12
- });
13
- }
14
- //#endregion
15
6
  //#region src/fields/candidates-match.ts
16
7
  function rowMatches(row, filters) {
17
8
  let unknown = false;
@@ -130,6 +121,15 @@ function useCandidateSelection(input) {
130
121
  };
131
122
  }
132
123
  //#endregion
133
- export { useCandidatesGate as n, LazyRelationPickerDialog as r, useCandidateSelection as t };
124
+ //#region src/fields/lazy-relation-picker-dialog.tsx
125
+ const Dialog = lazy(() => import("./relation-picker-dialog-CyKliBkN.js").then((module) => ({ default: module.RelationPickerDialog })));
126
+ function LazyRelationPickerDialog(props) {
127
+ return jsx(Suspense, {
128
+ fallback: null,
129
+ children: jsx(Dialog, { ...props })
130
+ });
131
+ }
132
+ //#endregion
133
+ export { useCandidateSelection as n, useCandidatesGate as r, LazyRelationPickerDialog as t };
134
134
 
135
- //# sourceMappingURL=candidates-gate-Bb70LPAy.js.map
135
+ //# sourceMappingURL=lazy-relation-picker-dialog-DN4bvQfg.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lazy-relation-picker-dialog-DN4bvQfg.js","names":[],"sources":["../src/fields/candidates-match.ts","../src/fields/candidates-gate.ts","../src/fields/lazy-relation-picker-dialog.tsx"],"sourcesContent":["import type { FixedFilterInput } from '@burojs/core';\n\ntype Entry = { readonly field?: unknown; readonly op?: unknown; readonly value?: unknown };\n\n/**\n * Whether `row` meets every candidate filter (P7 3.4): `true`, `false`, or `undefined` when the\n * row does not carry a field a filter names (a narrower projection), so nothing can be said.\n * Only the flat `{ field, op, value }` entries candidates resolve to are evaluated; this is what\n * \"does not match the current filter\" is decided on, never what is sent.\n */\nexport function rowMatches(\n row: Readonly<Record<string, unknown>>,\n filters: readonly FixedFilterInput[],\n): boolean | undefined {\n let unknown = false;\n for (const filter of filters) {\n const group = filter as { readonly and?: unknown; readonly or?: unknown };\n if (Array.isArray(group.and) || Array.isArray(group.or)) {\n const parts = (\n Array.isArray(group.and) ? group.and : group.or\n ) as readonly FixedFilterInput[];\n const answers = parts.map((part) => rowMatches(row, [part]));\n const all = Array.isArray(group.and);\n const decided = answers.filter((answer) => answer !== undefined);\n if (all ? decided.includes(false) : decided.length > 0 && !decided.includes(true))\n return false;\n if (decided.length < answers.length && !(all ? false : decided.includes(true)))\n unknown = true;\n continue;\n }\n const { field, op, value } = filter as Entry;\n if (typeof field !== 'string' || typeof op !== 'string') continue;\n if (!Object.hasOwn(row, field)) {\n unknown = true;\n continue;\n }\n const held = holds(row[field], op, value);\n if (held === false) return false;\n if (held === undefined) unknown = true;\n }\n return unknown ? undefined : true;\n}\n\nfunction holds(actual: unknown, op: string, expected: unknown): boolean | undefined {\n const list = Array.isArray(expected) ? (expected as readonly unknown[]) : undefined;\n const text = (value: unknown) => String(value ?? '').toLowerCase();\n switch (op) {\n case 'eq':\n return Object.is(actual, expected) || String(actual) === String(expected);\n case 'neq':\n return !(Object.is(actual, expected) || String(actual) === String(expected));\n case 'in':\n return list === undefined\n ? undefined\n : list.some((entry) => String(entry) === String(actual));\n case 'notIn':\n return list === undefined\n ? undefined\n : !list.some((entry) => String(entry) === String(actual));\n case 'gt':\n return compare(actual, expected, (a, b) => a > b);\n case 'gte':\n return compare(actual, expected, (a, b) => a >= b);\n case 'lt':\n return compare(actual, expected, (a, b) => a < b);\n case 'lte':\n return compare(actual, expected, (a, b) => a <= b);\n case 'contains':\n return text(actual).includes(text(expected));\n case 'startsWith':\n return text(actual).startsWith(text(expected));\n case 'endsWith':\n return text(actual).endsWith(text(expected));\n case 'isNull':\n return actual === null || actual === undefined;\n case 'isNotNull':\n return actual !== null && actual !== undefined;\n default:\n return undefined;\n }\n}\n\nfunction compare(\n actual: unknown,\n expected: unknown,\n test: (a: number | string, b: number | string) => boolean,\n): boolean | undefined {\n const both =\n (typeof actual === 'number' && typeof expected === 'number') ||\n (typeof actual === 'string' && typeof expected === 'string');\n return both ? test(actual as number | string, expected as number | string) : undefined;\n}\n","import {\n type FixedFilterInput,\n type RelationCandidate,\n type ScreenSources,\n resolveCandidates,\n resolveFieldLabel,\n declaredText,\n} from '@burojs/core';\nimport { useApp, useCandidatesAccess, useI18nCaps, useOptionalT, useTestIds } from '@burojs/react';\nimport * as React from 'react';\nimport { rowMatches } from './candidates-match.js';\n\n/** A select that must not read its target now (P7 3.4): why, and what it tells the user. */\nexport type CandidatesGate =\n | { readonly kind: 'pending' }\n | { readonly kind: 'empty'; readonly message: string; readonly testId: string }\n | { readonly kind: 'forbidden'; readonly message: string; readonly testId: string };\n\nconst NO_SOURCES: ScreenSources = Object.freeze({});\nconst NO_FILTERS: readonly FixedFilterInput[] = Object.freeze([]);\n\n/**\n * A relation field's `candidates` as the select applies them (P7 3.4): the filters to send and,\n * when they say the target must not be read yet, the gate that tells the user why. The refs are\n * read from the owner's projected sources, so a field P5 hides reads `null` (the select asks for\n * it first, and sends nothing); a candidate on a target field the user may not view forbids the\n * read; an undecided source or permission map keeps the select disabled.\n */\nexport function useCandidatesGate(input: {\n readonly to: string | undefined;\n readonly candidates: readonly RelationCandidate[] | undefined;\n readonly sources: ScreenSources | undefined;\n readonly resource: string;\n readonly fieldName: string;\n}): { readonly filters: readonly FixedFilterInput[]; readonly gate?: CandidatesGate } {\n const { to, candidates, sources, resource, fieldName } = input;\n const t = useOptionalT();\n const app = useApp();\n const tid = useTestIds();\n const ownerCaps = useI18nCaps(resource);\n const targetCaps = useI18nCaps(to ?? resource);\n const access = useCandidatesAccess(to ?? '', candidates);\n const resolution = React.useMemo(\n () => resolveCandidates(candidates, sources ?? NO_SOURCES),\n [candidates, sources],\n );\n if (candidates === undefined || candidates.length === 0) return { filters: NO_FILTERS };\n const testId = tid.relationCandidatesHint(resource, fieldName);\n if (access.status === 'pending') return { filters: NO_FILTERS, gate: { kind: 'pending' } };\n if (access.status === 'forbidden') {\n const declared = declaredText(\n 'field',\n (app.registry.get(to ?? '')?.fields as Record<string, unknown> | undefined)?.[access.field],\n 'label',\n );\n const label = resolveFieldLabel(targetCaps, access.field, declared);\n return {\n filters: NO_FILTERS,\n gate: {\n kind: 'forbidden',\n testId,\n message: t('relationPicker.cannotSearchBy', {\n params: { label },\n defaultValue: `You cannot search by ${label}`,\n }),\n },\n };\n }\n if (resolution.status === 'pending') return { filters: NO_FILTERS, gate: { kind: 'pending' } };\n if (resolution.status === 'empty') {\n const { source, path } = resolution.hint;\n const head = path[0];\n // The field a `values` / `record` ref reads is labelled as the owner labels it; any other\n // source (`scope`, `user`) names the key it reads, humanised.\n const own = source === 'values' || source === 'record';\n const declared =\n own && head !== undefined\n ? declaredText(\n 'field',\n (app.registry.get(resource)?.fields as Record<string, unknown> | undefined)?.[head],\n 'label',\n )\n : undefined;\n const label = resolveFieldLabel(ownerCaps, head ?? source, declared);\n return {\n filters: NO_FILTERS,\n gate: {\n kind: 'empty',\n testId,\n message: t('relationPicker.chooseFirst', {\n params: { label },\n defaultValue: `Choose ${label} first`,\n }),\n },\n };\n }\n return { filters: resolution.filters };\n}\n\n/**\n * A select's declared candidates as the shared select wiring applies them (`CandidatesRelationPicker`\n * and `useRelationPicker`): the gate, the resolved candidate filters prepended to the caller's\n * `fixedFilters`, and whether a loaded row fits them (`undefined` when no candidate filter applies).\n */\nexport function useCandidateSelection(input: {\n readonly to: string | undefined;\n readonly candidates: readonly RelationCandidate[] | undefined;\n readonly sources: ScreenSources | undefined;\n readonly resource: string;\n readonly fieldName: string;\n readonly fixedFilters: readonly FixedFilterInput[] | undefined;\n}): {\n readonly fixedFilters: readonly FixedFilterInput[] | undefined;\n readonly gate?: CandidatesGate;\n readonly rowFits?: (row: Record<string, unknown>) => boolean | undefined;\n} {\n const { fixedFilters } = input;\n const gate = useCandidatesGate(input);\n const filters = gate.filters;\n const combined = React.useMemo(\n () => (filters.length === 0 ? fixedFilters : [...filters, ...(fixedFilters ?? [])]),\n [filters, fixedFilters],\n );\n const rowFits = React.useMemo(\n () =>\n filters.length === 0 ? undefined : (row: Record<string, unknown>) => rowMatches(row, filters),\n [filters],\n );\n return {\n fixedFilters: combined,\n ...(gate.gate === undefined ? {} : { gate: gate.gate }),\n ...(rowFits === undefined ? {} : { rowFits }),\n };\n}\n","import { type ReactElement, Suspense, lazy } from 'react';\nimport type { RelationPickerDialogProps } from './relation-picker-dialog.js';\n\nconst Dialog = lazy(() =>\n import('./relation-picker-dialog.js').then((module) => ({\n default: module.RelationPickerDialog,\n })),\n);\n\n/** The \"Advanced search\" dialog, loaded on the first open: nothing is drawn until it arrives. */\nexport function LazyRelationPickerDialog<const N extends string = string>(\n props: RelationPickerDialogProps<N>,\n): ReactElement {\n return (\n <Suspense fallback={null}>\n <Dialog {...props} />\n </Suspense>\n );\n}\n"],"mappings":"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"}
@@ -1,11 +1,11 @@
1
- import { o as useLinkComponent } from "./toaster-C02XbB9r.js";
2
- import { h as useFlowChrome, m as text } from "./check-confirm-dialog-bRT8lt0d.js";
1
+ import { l as useLinkComponent } from "./toaster-Ct_YHGs6.js";
2
+ import { _ as reasonText, m as useFlowChrome } from "./check-confirm-dialog-CtiRzOAt.js";
3
3
  import { t as Button } from "./button-MWVeZaqj.js";
4
- import { yt as recordRouteAvailable } from "./place-node-NraNYKPx.js";
5
- import { M as canonicalNavKey, a as recordErrorState, b as RecordState, m as usePublishRecordTrail, p as recordTrailKey, t as NavAreaNotFound } from "./resource-page-router-3RUf9d1j.js";
6
- import { FlowHostBindingContext, FlowScreen } from "./flow-screen-CCvgDI9G.js";
4
+ import { b as RecordState, v as InvalidRouteId, y as LoadError } from "./relation-create-C43cdPQt.js";
5
+ import { P as canonicalNavKey, h as usePublishRecordTrail, m as recordTrailKey, t as NavAreaNotFound } from "./resource-page-router-BRpUnhay.js";
6
+ import { FlowHostBindingContext, FlowScreen } from "./flow-screen-3fk6J33y.js";
7
7
  import { compileFlow, dtoFields, effectiveFlow, hrefFor } from "@burojs/core";
8
- import { retryRecordRead, useApp, useApplicationSnapshot, useDispatcherEffects, useFlowRowResolver, useOne, useOptionalT, usePermissions, useProjectedTitle, useRouteNavigate, useTestIds } from "@burojs/react";
8
+ import { recordRouteAvailable, retryRecordRead, useApp, useApplicationSnapshot, useDispatcherEffects, useFlowRowResolver, useI18nCaps, useOne, useOptionalT, usePermissions, useProjectedTitle, useRouteNavigate, useRouteRecordId, useTestIds } from "@burojs/react";
9
9
  import { IconLoader2 } from "@tabler/icons-react";
10
10
  import { useCallback, useEffect, useMemo, useRef, useState } from "react";
11
11
  import { jsx, jsxs } from "react/jsx-runtime";
@@ -31,10 +31,19 @@ function PageFlowRoute({ match }) {
31
31
  }
32
32
  function PageFlowRecord(props) {
33
33
  const { resource, id } = props;
34
+ const recordId = useRouteRecordId(resource, id);
35
+ if (recordId === void 0) return jsx(InvalidRouteId, { resource });
36
+ return jsx(PageFlowRead, {
37
+ ...props,
38
+ recordId
39
+ });
40
+ }
41
+ function PageFlowRead(props) {
42
+ const { resource, recordId } = props;
34
43
  const def = useApp().registry.get(resource);
35
44
  const projection = useMemo(() => def === void 0 ? void 0 : dtoFields(def, "record"), [def]);
36
45
  const query = useOne(resource, {
37
- id,
46
+ id: recordId,
38
47
  retry: retryRecordRead,
39
48
  ...projection === void 0 ? {} : { projection }
40
49
  });
@@ -43,17 +52,18 @@ function PageFlowRecord(props) {
43
52
  const record = result.data;
44
53
  if (record === null || typeof record !== "object") return jsx(RecordState, { state: "not-found" });
45
54
  return jsx(PageFlowGate, {
46
- ...props,
55
+ resource,
56
+ id: props.id,
57
+ name: props.name,
47
58
  record
48
59
  });
49
60
  }
50
- if (query.error != null) {
51
- const state = recordErrorState(query.error);
52
- return jsx(RecordState, {
53
- state,
54
- onRetry: state === "error" ? () => void query.refetch() : void 0
55
- });
56
- }
61
+ if (query.error != null) return jsx(LoadError, {
62
+ surface: "record",
63
+ resource,
64
+ error: query.error,
65
+ retry: () => void query.refetch()
66
+ });
57
67
  return jsx(RecordState, { state: "loading" });
58
68
  }
59
69
  function PageFlowGate(props) {
@@ -110,6 +120,7 @@ function usePageFlowFrame(props) {
110
120
  }
111
121
  function PageFlowUnavailable(props) {
112
122
  const t = useOptionalT();
123
+ const caps = useI18nCaps(props.resource);
113
124
  const tid = useTestIds();
114
125
  const { recordHref } = usePageFlowFrame(props);
115
126
  const Link = useLinkComponent();
@@ -121,16 +132,16 @@ function PageFlowUnavailable(props) {
121
132
  children: [
122
133
  jsx("p", {
123
134
  className: "font-medium",
124
- children: text(t, "flow.notAvailable", "This action is not available")
135
+ children: t("flow.notAvailable", { defaultValue: "This action is not available" })
125
136
  }),
126
137
  props.reason === void 0 ? null : jsx("p", {
127
138
  className: "text-muted-foreground",
128
- children: props.reason
139
+ children: reasonText(caps, props.reason, props.resource)
129
140
  }),
130
141
  jsx(Link, {
131
142
  href: recordHref,
132
143
  className: "underline",
133
- children: text(t, "flow.back", "Back")
144
+ children: t("flow.back", { defaultValue: "Back" })
134
145
  })
135
146
  ]
136
147
  }) });
@@ -153,6 +164,7 @@ function PageFlowRun(props) {
153
164
  const { resource, name, stale = false, onDirty, onMounted } = props;
154
165
  const [record] = useState(props.record);
155
166
  const t = useOptionalT();
167
+ const caps = useI18nCaps(resource);
156
168
  const tid = useTestIds();
157
169
  const navigate = useRouteNavigate();
158
170
  const Link = useLinkComponent();
@@ -228,14 +240,14 @@ function PageFlowRun(props) {
228
240
  href: recordHref,
229
241
  className: "ml-auto text-sm underline",
230
242
  "data-testid": `${tid.pageFlow(compiled?.flowId ?? name)}:back`,
231
- children: text(t, "flow.back", "Back")
243
+ children: t("flow.back", { defaultValue: "Back" })
232
244
  })
233
245
  }),
234
246
  stale ? jsxs("div", {
235
247
  role: "status",
236
248
  "data-buro-flow-state": "stale",
237
249
  className: "flex flex-wrap items-center gap-2 text-sm text-muted-foreground",
238
- children: [jsx("span", { children: text(t, "flow.noLongerApplies", "This action no longer applies") }), jsx(Button, {
250
+ children: [jsx("span", { children: t("flow.noLongerApplies", { defaultValue: "This action no longer applies" }) }), jsx(Button, {
239
251
  type: "button",
240
252
  size: "sm",
241
253
  variant: "outline",
@@ -243,7 +255,7 @@ function PageFlowRun(props) {
243
255
  flow.reset();
244
256
  setDiscarded(true);
245
257
  },
246
- children: text(t, "flow.discard", "Discard")
258
+ children: t("flow.discard", { defaultValue: "Discard" })
247
259
  })]
248
260
  }) : null,
249
261
  opened ? jsx(FlowHostBindingContext.Provider, {
@@ -263,7 +275,7 @@ function PageFlowRun(props) {
263
275
  }) : refused ? jsx("p", {
264
276
  role: "alert",
265
277
  className: "text-sm",
266
- children: snapshot.reason ?? text(t, "flow.unavailable", "Not allowed.")
278
+ children: snapshot.reason === void 0 ? t("flow.unavailable", { defaultValue: "Not allowed." }) : reasonText(caps, snapshot.reason, resource)
267
279
  }) : jsx(IconLoader2, {
268
280
  className: "h-5 w-5 animate-spin self-center",
269
281
  "aria-hidden": "true"
@@ -274,4 +286,4 @@ function PageFlowRun(props) {
274
286
  //#endregion
275
287
  export { PageFlowRoute };
276
288
 
277
- //# sourceMappingURL=page-flow-route-i3ZfJ1sd.js.map
289
+ //# sourceMappingURL=page-flow-route-CtEfqAvU.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"page-flow-route-CtEfqAvU.js","names":[],"sources":["../src/flows/page-flow-route.tsx"],"sourcesContent":["'use client';\n/**\n * A page-hosted flow's route (P6 3.11): `<record route>/<flow name>`. It owns the whole gate in\n * order, so a user who may not run the flow never sees a trace of it:\n *\n * 1. the permission map is decided, else the record route's loading state and nothing is read;\n * 2. static: the resource has a record page, the flow is an item flow and, in the published\n * snapshot, its `host` is `page`; else the record route's not-found;\n * 3. the record is read by id with the record DTO;\n * 4. the flow's gate over that row (`requires`, the mutation's `requires`, `action/run` rules):\n * hidden, absent, or a missing or forbidden row is the same not-found as `/articles/missing`;\n * a false `when` or a blocking pre-click check is \"not available\" and the way back;\n * 5. only then the run (`useFlowRun`) and its sources.\n */\nimport {\n type RecordId,\n type RouteMatch,\n compileFlow,\n dtoFields,\n effectiveFlow,\n hrefFor,\n} from '@burojs/core';\nimport {\n recordRouteAvailable,\n retryRecordRead,\n useApp,\n useApplicationSnapshot,\n useDispatcherEffects,\n useFlowRowResolver,\n useI18nCaps,\n useOne,\n useOptionalT,\n usePermissions,\n useProjectedTitle,\n useRouteNavigate,\n useRouteRecordId,\n useTestIds,\n} from '@burojs/react';\nimport { useFlowRun } from '@burojs/react/flows';\nimport { IconLoader2 } from '@tabler/icons-react';\nimport { type ReactElement, useCallback, useEffect, useMemo, useRef, useState } from 'react';\nimport { useLinkComponent } from '../layout/link-component.js';\nimport { reasonText } from '../lib/reason-text.js';\nimport { InvalidRouteId, LoadError } from '../load-error/load-error-view.js';\nimport { recordTrailKey, usePublishRecordTrail } from '../layout/record-trail-store.js';\nimport { RecordState } from '../record/record-state.js';\nimport { NavAreaNotFound } from '../layout/resource-page-router.js';\nimport { canonicalNavKey } from '../resource-key.js';\nimport { Button } from '../primitives/button.js';\nimport { useFlowChrome } from './chrome.js';\nimport { type FlowHostBinding, FlowHostBindingContext, FlowScreen } from './flow-screen.js';\n\ntype Row = { id: string } & Record<string, unknown>;\n\ninterface PageFlowProps {\n readonly resource: string;\n readonly id: string;\n readonly name: string;\n}\n\nexport interface PageFlowRouteProps {\n readonly match: RouteMatch;\n}\n\n/** The route: the static gate, then the record, then the flow's own gate. */\nexport function PageFlowRoute({ match }: PageFlowRouteProps): ReactElement {\n const app = useApp();\n const snapshot = useApplicationSnapshot();\n const permissions = usePermissions();\n const resource = canonicalNavKey(match as never);\n const id = match.id ?? '';\n const name = match.flow ?? '';\n // (1) Decided before anything is read.\n if (permissions?.status === 'loading') return <RecordState state=\"loading\" />;\n // (2) Static: a record page to hang the route on, and an item flow whose effective host is page.\n const flow = effectiveFlow(snapshot, match.ref, name);\n const hasRecordPage =\n recordRouteAvailable(app.registry, resource, 'show') ||\n recordRouteAvailable(app.registry, resource, 'edit');\n // A name no item flow answers to is no route at all (`/orders/x/segments`): the framework 404,\n // as for any unmatched URL. A flow that exists but is not page-hosted, or a record page that is\n // missing, keeps the record route's not-found, so a denied flow reads like a missing record.\n if (flow === undefined) return <NavAreaNotFound />;\n if (!hasRecordPage || flow.host !== 'page' || id === '') return <RecordState state=\"not-found\" />;\n return <PageFlowRecord key={`${resource}:${id}:${name}`} {...{ resource, id, name }} />;\n}\n\n/** (3) The record by id, with the record DTO: the same read the record route makes. */\nfunction PageFlowRecord(props: PageFlowProps): ReactElement {\n const { resource, id } = props;\n // The URL's id as the id field types it (spec 4.19); an invalid one names no record.\n const recordId = useRouteRecordId(resource, id);\n if (recordId === undefined) return <InvalidRouteId resource={resource} />;\n return <PageFlowRead {...props} recordId={recordId} />;\n}\n\n/** The record by its typed id. */\nfunction PageFlowRead(props: PageFlowProps & { readonly recordId: RecordId }): ReactElement {\n const { resource, recordId } = props;\n const app = useApp();\n const def = app.registry.get(resource);\n const projection = useMemo(\n () => (def === undefined ? undefined : dtoFields(def, 'record')),\n [def],\n );\n const query = useOne(resource, {\n id: recordId,\n retry: retryRecordRead,\n ...(projection === undefined ? {} : { projection }),\n });\n const result = query.data as { readonly data?: unknown } | undefined;\n if (result !== undefined) {\n const record = result.data;\n if (record === null || typeof record !== 'object') return <RecordState state=\"not-found\" />;\n return (\n <PageFlowGate resource={resource} id={props.id} name={props.name} record={record as Row} />\n );\n }\n if (query.error != null) {\n return (\n <LoadError\n surface=\"record\"\n resource={resource}\n error={query.error}\n retry={() => void query.refetch()}\n />\n );\n }\n return <RecordState state=\"loading\" />;\n}\n\n/** (4) The flow's gate over the row, as its button decides it: no second rule here. */\nfunction PageFlowGate(props: PageFlowProps & { readonly record: Row }): ReactElement {\n const { resource, name, record } = props;\n const resolve = useFlowRowResolver(resource);\n const control = resolve(name, record);\n // Someone else's change can take the flow away under a form that holds input: the page keeps\n // the form, read-only, with the \"no longer applies\" notice and Discard (3.12), instead of\n // unmounting it silently. `useRef` here, `PageFlowRun` mounted from the first available render.\n const dirty = useRef(false);\n const running = useRef(false);\n const goneNow =\n (control.state === 'absent' && control.cause === 'when') || control.state === 'disabled';\n const keep = goneNow && running.current && dirty.current;\n const track = (value: boolean) => {\n dirty.current = value;\n };\n if (keep)\n return (\n <PageFlowRun {...props} stale onDirty={track} onMounted={(on) => (running.current = on)} />\n );\n switch (control.state) {\n case 'undecided':\n case 'pending':\n return <RecordState state=\"loading\" />;\n case 'absent':\n return control.cause === 'when' ? (\n <PageFlowUnavailable {...props} />\n ) : (\n <RecordState state=\"not-found\" />\n );\n case 'disabled':\n return (\n <PageFlowUnavailable\n {...props}\n {...(control.reason === undefined ? {} : { reason: control.reason })}\n />\n );\n default:\n return (\n <PageFlowRun\n {...props}\n record={(control.record ?? record) as Row}\n onDirty={track}\n onMounted={(on) => (running.current = on)}\n />\n );\n }\n}\n\n/**\n * The flow's name, the record's title and the record's way back, shared by every state of the\n * page that shows the record. It also tells the header's trail (`<HeaderBreadcrumbs>`) the\n * record's title and the flow's label, which the trail ends with: only these states do, so a\n * loading or not-found flow route never names its flow.\n */\nfunction usePageFlowFrame(props: PageFlowProps & { readonly record: Row }) {\n const { resource, id, name, record } = props;\n const app = useApp();\n const chrome = useFlowChrome(resource, name);\n const title = useProjectedTitle(resource, id, record);\n const showable = recordRouteAvailable(app.registry, resource, 'show');\n const recordHref = hrefFor(app.registry, resource, {\n action: showable ? 'show' : 'edit',\n id,\n });\n usePublishRecordTrail({\n key: recordTrailKey(resource, id, `flow:${name}`),\n title,\n flowLabel: chrome.labelText,\n });\n return { chrome, title, recordHref };\n}\n\n/** `when` false, or a blocking check: the reason and the way back, no form. */\nfunction PageFlowUnavailable(\n props: PageFlowProps & { readonly record: Row; readonly reason?: string },\n): ReactElement {\n const t = useOptionalT();\n const caps = useI18nCaps(props.resource);\n const tid = useTestIds();\n const { recordHref } = usePageFlowFrame(props);\n const Link = useLinkComponent();\n const flowId = usePageFlowId(props.resource, props.name);\n return (\n <div>\n <div\n role=\"status\"\n data-testid={`${tid.pageFlow(flowId)}:unavailable`}\n className=\"flex flex-col gap-2 px-1 py-6 text-sm\"\n >\n <p className=\"font-medium\">\n {t('flow.notAvailable', { defaultValue: 'This action is not available' })}\n </p>\n {props.reason === undefined ? null : (\n <p className=\"text-muted-foreground\">{reasonText(caps, props.reason, props.resource)}</p>\n )}\n <Link href={recordHref} className=\"underline\">\n {t('flow.back', { defaultValue: 'Back' })}\n </Link>\n </div>\n </div>\n );\n}\n\n/** The flow's identity (`default:articles/actions/rename`), for test ids; stable when compiled. */\nfunction usePageFlowId(resource: string, name: string): string {\n const app = useApp();\n const def = app.registry.get(resource);\n return useMemo(() => {\n try {\n return def === undefined\n ? `${resource}/actions/${name}`\n : compileFlow(def as never, name).flowId;\n } catch {\n return `${resource}/actions/${name}`;\n }\n }, [def, resource, name]);\n}\n\n/** (5) The run and its host. */\nfunction PageFlowRun(\n props: PageFlowProps & {\n readonly record: Row;\n /** The flow no longer applies: the form stays, read-only, with a notice and Discard. */\n readonly stale?: boolean;\n readonly onDirty?: (dirty: boolean) => void;\n readonly onMounted?: (mounted: boolean) => void;\n },\n): ReactElement {\n const { resource, name, stale = false, onDirty, onMounted } = props;\n // The run belongs to the record as it was opened: the refetch after the write must not build\n // a new run (and lose the outcome `then` is about to run on). Display reads the fresh one.\n const [record] = useState(props.record);\n const t = useOptionalT();\n const caps = useI18nCaps(resource);\n const tid = useTestIds();\n const navigate = useRouteNavigate();\n const Link = useLinkComponent();\n const scopeEffects = useDispatcherEffects();\n const { chrome, title, recordHref } = usePageFlowFrame(props);\n const navigateRef = useRef(navigate);\n navigateRef.current = navigate;\n // `redirect` replaces the page: Back cannot reach a form that has been submitted.\n const thenEffects = useCallback(\n () => ({\n ...scopeEffects(),\n redirect: (to: Parameters<typeof navigate>[0]) => navigateRef.current(to, { replace: true }),\n }),\n [scopeEffects],\n );\n const target = useMemo(() => ({ resource, action: name }), [resource, name]);\n const flow = useFlowRun(target, { record, surface: 'page', thenEffects });\n // `then` is the page's to run, once it left the form (4.13).\n flow.holdThen(true);\n const { run, snapshot, compiled } = flow;\n const flowRef = useRef(flow);\n flowRef.current = flow;\n\n // The page leaves the form behind: after the form below it reset (a clean form asks the guard\n // nothing), replace the page by the destination, then run `then` over it.\n const [finished, setFinished] = useState(false);\n useEffect(() => {\n if (!finished) return;\n const current = flowRef.current;\n if (current.compiled?.flow.then?.redirect === undefined)\n navigateRef.current(recordHref, { replace: true });\n // After the route committed: an overlay opened by `then` belongs to the destination, and a\n // route change in flight would close it again.\n setTimeout(() => current.runThen(), 0);\n }, [finished, recordHref]);\n const binding = useMemo<FlowHostBinding>(\n () => ({\n kind: 'page',\n close: () => navigateRef.current(recordHref),\n finish: () => setFinished(true),\n }),\n [recordHref],\n );\n\n useEffect(() => {\n onMounted?.(true);\n return () => onMounted?.(false);\n }, [onMounted]);\n // Unsaved input or an unresolved attempt: what the gate must not unmount silently.\n useEffect(() => {\n if (run === null) return;\n let initial: string | undefined;\n const sync = () => {\n const now = run.getSnapshot();\n if (initial === undefined && now.history.includes('open'))\n initial = JSON.stringify(now.values);\n onDirty?.(\n now.outcome?.outcome !== 'success' &&\n (now.outcome?.outcome === 'unknown' ||\n (initial !== undefined && JSON.stringify(now.values) !== initial)),\n );\n };\n sync();\n return run.subscribe(sync);\n }, [run, onDirty]);\n // Discard: the input is dropped first, so the guard has nothing to ask, then the page leaves.\n const [discarded, setDiscarded] = useState(false);\n useEffect(() => {\n if (discarded) navigateRef.current(recordHref, { replace: true });\n }, [discarded, recordHref]);\n\n const documentTitle = `${chrome.labelText}${title === '' ? '' : ` · ${title}`}`;\n useEffect(() => {\n const previous = document.title;\n document.title = documentTitle;\n return () => {\n document.title = previous;\n };\n }, [documentTitle]);\n\n const opened =\n run !== null &&\n compiled !== null &&\n (snapshot.history.includes('open') || snapshot.step === 'outcome');\n const refused = snapshot.control === 'disabled' || snapshot.control === 'absent';\n return (\n <div\n data-testid={compiled === null ? undefined : tid.pageFlow(compiled.flowId)}\n className=\"flex min-h-0 flex-col gap-4\"\n >\n <div className=\"flex flex-wrap items-center gap-2\">\n <Link\n href={recordHref}\n className=\"ml-auto text-sm underline\"\n data-testid={`${tid.pageFlow(compiled?.flowId ?? name)}:back`}\n >\n {t('flow.back', { defaultValue: 'Back' })}\n </Link>\n </div>\n {stale ? (\n <div\n role=\"status\"\n data-buro-flow-state=\"stale\"\n className=\"flex flex-wrap items-center gap-2 text-sm text-muted-foreground\"\n >\n <span>\n {t('flow.noLongerApplies', { defaultValue: 'This action no longer applies' })}\n </span>\n <Button\n type=\"button\"\n size=\"sm\"\n variant=\"outline\"\n onClick={() => {\n flow.reset();\n setDiscarded(true);\n }}\n >\n {t('flow.discard', { defaultValue: 'Discard' })}\n </Button>\n </div>\n ) : null}\n {opened ? (\n <FlowHostBindingContext.Provider value={binding}>\n <fieldset disabled={stale} className=\"m-0 min-w-0 border-0 p-0\">\n <FlowScreen\n host=\"page\"\n run={run}\n compiled={compiled}\n label={chrome.labelText}\n {...(title === '' ? {} : { title })}\n record={record}\n />\n </fieldset>\n </FlowHostBindingContext.Provider>\n ) : refused ? (\n <p role=\"alert\" className=\"text-sm\">\n {snapshot.reason === undefined\n ? t('flow.unavailable', { defaultValue: 'Not allowed.' })\n : reasonText(caps, snapshot.reason, resource)}\n </p>\n ) : (\n <IconLoader2 className=\"h-5 w-5 animate-spin self-center\" aria-hidden=\"true\" />\n )}\n </div>\n );\n}\n"],"mappings":"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"}
@@ -1,9 +1,9 @@
1
- const require_toaster = require("./toaster-Cr51X7bz.cjs");
2
- const require_check_confirm_dialog = require("./check-confirm-dialog--gPFnEeg.cjs");
1
+ const require_toaster = require("./toaster-BbByOytP.cjs");
2
+ const require_check_confirm_dialog = require("./check-confirm-dialog-BT04Mieo.cjs");
3
3
  const require_button = require("./button-BxnnE28H.cjs");
4
- const require_place_node = require("./place-node-C5SmIAYU.cjs");
5
- const require_resource_page_router = require("./resource-page-router-PXfMW9nh.cjs");
6
- const require_flow_screen = require("./flow-screen-Ci_Sik4Y.cjs");
4
+ const require_relation_create = require("./relation-create-DDEYcKfw.cjs");
5
+ const require_resource_page_router = require("./resource-page-router-qEd2xb55.cjs");
6
+ const require_flow_screen = require("./flow-screen-6y5E_HT6.cjs");
7
7
  let _burojs_core = require("@burojs/core");
8
8
  let _burojs_react = require("@burojs/react");
9
9
  let _tabler_icons_react = require("@tabler/icons-react");
@@ -18,11 +18,11 @@ function PageFlowRoute({ match }) {
18
18
  const resource = require_resource_page_router.canonicalNavKey(match);
19
19
  const id = match.id ?? "";
20
20
  const name = match.flow ?? "";
21
- if (permissions?.status === "loading") return (0, react_jsx_runtime.jsx)(require_resource_page_router.RecordState, { state: "loading" });
21
+ if (permissions?.status === "loading") return (0, react_jsx_runtime.jsx)(require_relation_create.RecordState, { state: "loading" });
22
22
  const flow = (0, _burojs_core.effectiveFlow)(snapshot, match.ref, name);
23
- const hasRecordPage = require_place_node.recordRouteAvailable(app.registry, resource, "show") || require_place_node.recordRouteAvailable(app.registry, resource, "edit");
23
+ const hasRecordPage = (0, _burojs_react.recordRouteAvailable)(app.registry, resource, "show") || (0, _burojs_react.recordRouteAvailable)(app.registry, resource, "edit");
24
24
  if (flow === void 0) return (0, react_jsx_runtime.jsx)(require_resource_page_router.NavAreaNotFound, {});
25
- if (!hasRecordPage || flow.host !== "page" || id === "") return (0, react_jsx_runtime.jsx)(require_resource_page_router.RecordState, { state: "not-found" });
25
+ if (!hasRecordPage || flow.host !== "page" || id === "") return (0, react_jsx_runtime.jsx)(require_relation_create.RecordState, { state: "not-found" });
26
26
  return (0, react_jsx_runtime.jsx)(PageFlowRecord, {
27
27
  resource,
28
28
  id,
@@ -31,30 +31,40 @@ function PageFlowRoute({ match }) {
31
31
  }
32
32
  function PageFlowRecord(props) {
33
33
  const { resource, id } = props;
34
+ const recordId = (0, _burojs_react.useRouteRecordId)(resource, id);
35
+ if (recordId === void 0) return (0, react_jsx_runtime.jsx)(require_relation_create.InvalidRouteId, { resource });
36
+ return (0, react_jsx_runtime.jsx)(PageFlowRead, {
37
+ ...props,
38
+ recordId
39
+ });
40
+ }
41
+ function PageFlowRead(props) {
42
+ const { resource, recordId } = props;
34
43
  const def = (0, _burojs_react.useApp)().registry.get(resource);
35
44
  const projection = (0, react.useMemo)(() => def === void 0 ? void 0 : (0, _burojs_core.dtoFields)(def, "record"), [def]);
36
45
  const query = (0, _burojs_react.useOne)(resource, {
37
- id,
46
+ id: recordId,
38
47
  retry: _burojs_react.retryRecordRead,
39
48
  ...projection === void 0 ? {} : { projection }
40
49
  });
41
50
  const result = query.data;
42
51
  if (result !== void 0) {
43
52
  const record = result.data;
44
- if (record === null || typeof record !== "object") return (0, react_jsx_runtime.jsx)(require_resource_page_router.RecordState, { state: "not-found" });
53
+ if (record === null || typeof record !== "object") return (0, react_jsx_runtime.jsx)(require_relation_create.RecordState, { state: "not-found" });
45
54
  return (0, react_jsx_runtime.jsx)(PageFlowGate, {
46
- ...props,
55
+ resource,
56
+ id: props.id,
57
+ name: props.name,
47
58
  record
48
59
  });
49
60
  }
50
- if (query.error != null) {
51
- const state = require_resource_page_router.recordErrorState(query.error);
52
- return (0, react_jsx_runtime.jsx)(require_resource_page_router.RecordState, {
53
- state,
54
- onRetry: state === "error" ? () => void query.refetch() : void 0
55
- });
56
- }
57
- return (0, react_jsx_runtime.jsx)(require_resource_page_router.RecordState, { state: "loading" });
61
+ if (query.error != null) return (0, react_jsx_runtime.jsx)(require_relation_create.LoadError, {
62
+ surface: "record",
63
+ resource,
64
+ error: query.error,
65
+ retry: () => void query.refetch()
66
+ });
67
+ return (0, react_jsx_runtime.jsx)(require_relation_create.RecordState, { state: "loading" });
58
68
  }
59
69
  function PageFlowGate(props) {
60
70
  const { resource, name, record } = props;
@@ -73,8 +83,8 @@ function PageFlowGate(props) {
73
83
  });
74
84
  switch (control.state) {
75
85
  case "undecided":
76
- case "pending": return (0, react_jsx_runtime.jsx)(require_resource_page_router.RecordState, { state: "loading" });
77
- case "absent": return control.cause === "when" ? (0, react_jsx_runtime.jsx)(PageFlowUnavailable, { ...props }) : (0, react_jsx_runtime.jsx)(require_resource_page_router.RecordState, { state: "not-found" });
86
+ case "pending": return (0, react_jsx_runtime.jsx)(require_relation_create.RecordState, { state: "loading" });
87
+ case "absent": return control.cause === "when" ? (0, react_jsx_runtime.jsx)(PageFlowUnavailable, { ...props }) : (0, react_jsx_runtime.jsx)(require_relation_create.RecordState, { state: "not-found" });
78
88
  case "disabled": return (0, react_jsx_runtime.jsx)(PageFlowUnavailable, {
79
89
  ...props,
80
90
  ...control.reason === void 0 ? {} : { reason: control.reason }
@@ -92,7 +102,7 @@ function usePageFlowFrame(props) {
92
102
  const app = (0, _burojs_react.useApp)();
93
103
  const chrome = require_check_confirm_dialog.useFlowChrome(resource, name);
94
104
  const title = (0, _burojs_react.useProjectedTitle)(resource, id, record);
95
- const showable = require_place_node.recordRouteAvailable(app.registry, resource, "show");
105
+ const showable = (0, _burojs_react.recordRouteAvailable)(app.registry, resource, "show");
96
106
  const recordHref = (0, _burojs_core.hrefFor)(app.registry, resource, {
97
107
  action: showable ? "show" : "edit",
98
108
  id
@@ -110,6 +120,7 @@ function usePageFlowFrame(props) {
110
120
  }
111
121
  function PageFlowUnavailable(props) {
112
122
  const t = (0, _burojs_react.useOptionalT)();
123
+ const caps = (0, _burojs_react.useI18nCaps)(props.resource);
113
124
  const tid = (0, _burojs_react.useTestIds)();
114
125
  const { recordHref } = usePageFlowFrame(props);
115
126
  const Link = require_toaster.useLinkComponent();
@@ -121,16 +132,16 @@ function PageFlowUnavailable(props) {
121
132
  children: [
122
133
  (0, react_jsx_runtime.jsx)("p", {
123
134
  className: "font-medium",
124
- children: require_check_confirm_dialog.text(t, "flow.notAvailable", "This action is not available")
135
+ children: t("flow.notAvailable", { defaultValue: "This action is not available" })
125
136
  }),
126
137
  props.reason === void 0 ? null : (0, react_jsx_runtime.jsx)("p", {
127
138
  className: "text-muted-foreground",
128
- children: props.reason
139
+ children: require_check_confirm_dialog.reasonText(caps, props.reason, props.resource)
129
140
  }),
130
141
  (0, react_jsx_runtime.jsx)(Link, {
131
142
  href: recordHref,
132
143
  className: "underline",
133
- children: require_check_confirm_dialog.text(t, "flow.back", "Back")
144
+ children: t("flow.back", { defaultValue: "Back" })
134
145
  })
135
146
  ]
136
147
  }) });
@@ -153,6 +164,7 @@ function PageFlowRun(props) {
153
164
  const { resource, name, stale = false, onDirty, onMounted } = props;
154
165
  const [record] = (0, react.useState)(props.record);
155
166
  const t = (0, _burojs_react.useOptionalT)();
167
+ const caps = (0, _burojs_react.useI18nCaps)(resource);
156
168
  const tid = (0, _burojs_react.useTestIds)();
157
169
  const navigate = (0, _burojs_react.useRouteNavigate)();
158
170
  const Link = require_toaster.useLinkComponent();
@@ -228,14 +240,14 @@ function PageFlowRun(props) {
228
240
  href: recordHref,
229
241
  className: "ml-auto text-sm underline",
230
242
  "data-testid": `${tid.pageFlow(compiled?.flowId ?? name)}:back`,
231
- children: require_check_confirm_dialog.text(t, "flow.back", "Back")
243
+ children: t("flow.back", { defaultValue: "Back" })
232
244
  })
233
245
  }),
234
246
  stale ? (0, react_jsx_runtime.jsxs)("div", {
235
247
  role: "status",
236
248
  "data-buro-flow-state": "stale",
237
249
  className: "flex flex-wrap items-center gap-2 text-sm text-muted-foreground",
238
- children: [(0, react_jsx_runtime.jsx)("span", { children: require_check_confirm_dialog.text(t, "flow.noLongerApplies", "This action no longer applies") }), (0, react_jsx_runtime.jsx)(require_button.Button, {
250
+ children: [(0, react_jsx_runtime.jsx)("span", { children: t("flow.noLongerApplies", { defaultValue: "This action no longer applies" }) }), (0, react_jsx_runtime.jsx)(require_button.Button, {
239
251
  type: "button",
240
252
  size: "sm",
241
253
  variant: "outline",
@@ -243,7 +255,7 @@ function PageFlowRun(props) {
243
255
  flow.reset();
244
256
  setDiscarded(true);
245
257
  },
246
- children: require_check_confirm_dialog.text(t, "flow.discard", "Discard")
258
+ children: t("flow.discard", { defaultValue: "Discard" })
247
259
  })]
248
260
  }) : null,
249
261
  opened ? (0, react_jsx_runtime.jsx)(require_flow_screen.FlowHostBindingContext.Provider, {
@@ -263,7 +275,7 @@ function PageFlowRun(props) {
263
275
  }) : refused ? (0, react_jsx_runtime.jsx)("p", {
264
276
  role: "alert",
265
277
  className: "text-sm",
266
- children: snapshot.reason ?? require_check_confirm_dialog.text(t, "flow.unavailable", "Not allowed.")
278
+ children: snapshot.reason === void 0 ? t("flow.unavailable", { defaultValue: "Not allowed." }) : require_check_confirm_dialog.reasonText(caps, snapshot.reason, resource)
267
279
  }) : (0, react_jsx_runtime.jsx)(_tabler_icons_react.IconLoader2, {
268
280
  className: "h-5 w-5 animate-spin self-center",
269
281
  "aria-hidden": "true"
@@ -274,4 +286,4 @@ function PageFlowRun(props) {
274
286
  //#endregion
275
287
  exports.PageFlowRoute = PageFlowRoute;
276
288
 
277
- //# sourceMappingURL=page-flow-route-hdnULWqd.cjs.map
289
+ //# sourceMappingURL=page-flow-route-DEjAxHe3.cjs.map