@burojs/ui-shadcn 0.5.0 → 0.7.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 (172) hide show
  1. package/README.md +9 -0
  2. package/dist/bulk-summary-BWSAS7k2.js +143 -0
  3. package/dist/bulk-summary-BWSAS7k2.js.map +1 -0
  4. package/dist/bulk-summary-CrEij5vq.cjs +143 -0
  5. package/dist/bulk-summary-CrEij5vq.cjs.map +1 -0
  6. package/dist/candidates-relation-picker-CS_JZ4Fd.js +31 -0
  7. package/dist/candidates-relation-picker-CS_JZ4Fd.js.map +1 -0
  8. package/dist/candidates-relation-picker-y5M6_oe4.cjs +31 -0
  9. package/dist/candidates-relation-picker-y5M6_oe4.cjs.map +1 -0
  10. package/dist/{check-confirm-dialog-CkaVTPT9.js → check-confirm-dialog-2j0gOd7x.js} +392 -103
  11. package/dist/check-confirm-dialog-2j0gOd7x.js.map +1 -0
  12. package/dist/{check-confirm-dialog-BRByTFpg.cjs → check-confirm-dialog-C-NHgMST.cjs} +476 -102
  13. package/dist/check-confirm-dialog-C-NHgMST.cjs.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-B3mZFL-K.cjs → filter-builder-DFu0JP-1.cjs} +959 -128
  19. package/dist/filter-builder-DFu0JP-1.cjs.map +1 -0
  20. package/dist/{filter-builder-DOnaXNfP.js → filter-builder-DaWHUZuV.js} +863 -116
  21. package/dist/filter-builder-DaWHUZuV.js.map +1 -0
  22. package/dist/{flow-runtime-B6zOHYqI.cjs → flow-runtime-BpmrIUb6.cjs} +61 -29
  23. package/dist/flow-runtime-BpmrIUb6.cjs.map +1 -0
  24. package/dist/{flow-runtime-D8h99FSg.js → flow-runtime-DyqTmK3H.js} +62 -30
  25. package/dist/flow-runtime-DyqTmK3H.js.map +1 -0
  26. package/dist/{flow-screen-CX05b3G9.cjs → flow-screen-Bcz9NF2V.cjs} +132 -88
  27. package/dist/flow-screen-Bcz9NF2V.cjs.map +1 -0
  28. package/dist/{flow-screen-CmbARSqg.js → flow-screen-DCdli6Ha.js} +132 -88
  29. package/dist/flow-screen-DCdli6Ha.js.map +1 -0
  30. package/dist/index.cjs +951 -605
  31. package/dist/index.cjs.map +1 -1
  32. package/dist/index.d.cts +618 -332
  33. package/dist/index.d.ts +618 -332
  34. package/dist/index.js +863 -522
  35. package/dist/index.js.map +1 -1
  36. package/dist/{inline-flow-host-B7U1OJUe.cjs → inline-flow-host-3vw7LzzD.cjs} +5 -5
  37. package/dist/inline-flow-host-3vw7LzzD.cjs.map +1 -0
  38. package/dist/{inline-flow-host-C3H2UpSA.js → inline-flow-host-C0rI9B7f.js} +5 -5
  39. package/dist/inline-flow-host-C0rI9B7f.js.map +1 -0
  40. package/dist/label-66W1qbke.js.map +1 -1
  41. package/dist/label-w6JYf2cF.cjs.map +1 -1
  42. package/dist/lazy-relation-picker-dialog-Bt8lrEIu.cjs +153 -0
  43. package/dist/lazy-relation-picker-dialog-Bt8lrEIu.cjs.map +1 -0
  44. package/dist/lazy-relation-picker-dialog-CAScLPb2.js +135 -0
  45. package/dist/lazy-relation-picker-dialog-CAScLPb2.js.map +1 -0
  46. package/dist/markdown/index.d.cts +2 -3
  47. package/dist/markdown/index.d.ts +2 -3
  48. package/dist/{page-flow-route-CyXSvXSE.js → page-flow-route-BHUEpn0I.js} +36 -23
  49. package/dist/page-flow-route-BHUEpn0I.js.map +1 -0
  50. package/dist/{page-flow-route-CQ7HsbBm.cjs → page-flow-route-DS8IVTVH.cjs} +41 -28
  51. package/dist/page-flow-route-DS8IVTVH.cjs.map +1 -0
  52. package/dist/{place-node-SIp4XYQH.cjs → place-node-CNb3w-c7.cjs} +2227 -999
  53. package/dist/place-node-CNb3w-c7.cjs.map +1 -0
  54. package/dist/{place-node--GdsP688.js → place-node-CfzQvh4Y.js} +2164 -984
  55. package/dist/place-node-CfzQvh4Y.js.map +1 -0
  56. package/dist/{related-list-cards-B6x9HQQp.js → related-list-cards-BIqqY69Y.js} +9 -4
  57. package/dist/{related-list-cards-B6x9HQQp.js.map → related-list-cards-BIqqY69Y.js.map} +1 -1
  58. package/dist/{related-list-cards-nxuODlIA.cjs → related-list-cards-CelMxl4q.cjs} +9 -4
  59. package/dist/{related-list-cards-nxuODlIA.cjs.map → related-list-cards-CelMxl4q.cjs.map} +1 -1
  60. package/dist/{related-list-timeline-DEdfd-rr.js → related-list-timeline-CRh7xwLD.js} +6 -7
  61. package/dist/related-list-timeline-CRh7xwLD.js.map +1 -0
  62. package/dist/{related-list-timeline-BYJKEbmA.cjs → related-list-timeline-DsLdi8sv.cjs} +5 -6
  63. package/dist/related-list-timeline-DsLdi8sv.cjs.map +1 -0
  64. package/dist/{related-rows-B4levHcR.js → related-rows--uP2iKQC.js} +27 -17
  65. package/dist/related-rows--uP2iKQC.js.map +1 -0
  66. package/dist/{related-rows-zh5f_4QR.cjs → related-rows-MPnLCmPF.cjs} +32 -22
  67. package/dist/related-rows-MPnLCmPF.cjs.map +1 -0
  68. package/dist/{search-access-CURsWn-U.js → relation-create-C43cdPQt.js} +596 -29
  69. package/dist/relation-create-C43cdPQt.js.map +1 -0
  70. package/dist/{search-access-B7G5oR8n.cjs → relation-create-DDEYcKfw.cjs} +630 -32
  71. package/dist/relation-create-DDEYcKfw.cjs.map +1 -0
  72. package/dist/{relation-many-picker-gdAOD-qK.js → relation-many-picker-BJhaEqxz.js} +210 -107
  73. package/dist/relation-many-picker-BJhaEqxz.js.map +1 -0
  74. package/dist/{relation-many-picker-B-NCEUgX.cjs → relation-many-picker-DzuGJpEl.cjs} +218 -115
  75. package/dist/relation-many-picker-DzuGJpEl.cjs.map +1 -0
  76. package/dist/{relation-picker-BAY77ivO.js → relation-picker-D-tBBZW_.js} +40 -45
  77. package/dist/relation-picker-D-tBBZW_.js.map +1 -0
  78. package/dist/{relation-picker-Da_uhFvA.cjs → relation-picker-DaIMzf2H.cjs} +45 -50
  79. package/dist/relation-picker-DaIMzf2H.cjs.map +1 -0
  80. package/dist/{relation-picker-dialog-M0pQaWlp.cjs → relation-picker-dialog-9Vg5vvR1.cjs} +142 -40
  81. package/dist/relation-picker-dialog-9Vg5vvR1.cjs.map +1 -0
  82. package/dist/{relation-picker-dialog-hA9rYrZB.js → relation-picker-dialog-DoEY8EkH.js} +143 -41
  83. package/dist/relation-picker-dialog-DoEY8EkH.js.map +1 -0
  84. package/dist/relation-select-B8Fq4o-t.js +48 -0
  85. package/dist/relation-select-B8Fq4o-t.js.map +1 -0
  86. package/dist/relation-select-Cr3CfNLy.cjs +55 -0
  87. package/dist/relation-select-Cr3CfNLy.cjs.map +1 -0
  88. package/dist/{resource-page-router-OSQyRA5b.cjs → resource-page-router-C_kGW2o_.cjs} +552 -303
  89. package/dist/resource-page-router-C_kGW2o_.cjs.map +1 -0
  90. package/dist/{resource-page-router-T4vS5gbJ.js → resource-page-router-Diw6S6TK.js} +507 -258
  91. package/dist/resource-page-router-Diw6S6TK.js.map +1 -0
  92. package/dist/{rich-text-vTyVjDoa.cjs → rich-text-C_7JCvRo.cjs} +2 -2
  93. package/dist/{rich-text-vTyVjDoa.cjs.map → rich-text-C_7JCvRo.cjs.map} +1 -1
  94. package/dist/{rich-text-DFerywgz.js → rich-text-RcPBf6hr.js} +2 -2
  95. package/dist/{rich-text-DFerywgz.js.map → rich-text-RcPBf6hr.js.map} +1 -1
  96. package/dist/success-notices-Dl3M1fZP.cjs +46 -0
  97. package/dist/success-notices-Dl3M1fZP.cjs.map +1 -0
  98. package/dist/success-notices-LDEgEkc4.js +41 -0
  99. package/dist/success-notices-LDEgEkc4.js.map +1 -0
  100. package/dist/{toaster-C7CiYA4Z.cjs → toaster-BbByOytP.cjs} +72 -80
  101. package/dist/toaster-BbByOytP.cjs.map +1 -0
  102. package/dist/{toaster-CfCRLgR-.js → toaster-Ct_YHGs6.js} +66 -68
  103. package/dist/toaster-Ct_YHGs6.js.map +1 -0
  104. package/dist/{use-dialog-filter-state-BDcgS5Oz.js → use-dialog-filter-state-B_MqzHaT.js} +5 -4
  105. package/dist/use-dialog-filter-state-B_MqzHaT.js.map +1 -0
  106. package/dist/{use-dialog-filter-state-BBCC0OEK.cjs → use-dialog-filter-state-tNvqAAjh.cjs} +6 -5
  107. package/dist/use-dialog-filter-state-tNvqAAjh.cjs.map +1 -0
  108. package/dist/use-theme-DFlpXils.cjs.map +1 -1
  109. package/dist/use-theme-dnE3Od3I.js.map +1 -1
  110. package/package.json +11 -11
  111. package/dist/bulk-summary-CwSd6Yjb.cjs +0 -108
  112. package/dist/bulk-summary-CwSd6Yjb.cjs.map +0 -1
  113. package/dist/bulk-summary-GxMDP4bw.js +0 -108
  114. package/dist/bulk-summary-GxMDP4bw.js.map +0 -1
  115. package/dist/candidates-gate-CX2PBu6l.js +0 -63
  116. package/dist/candidates-gate-CX2PBu6l.js.map +0 -1
  117. package/dist/candidates-gate-DiFI52r7.cjs +0 -70
  118. package/dist/candidates-gate-DiFI52r7.cjs.map +0 -1
  119. package/dist/candidates-relation-picker-CBsVcnPR.js +0 -82
  120. package/dist/candidates-relation-picker-CBsVcnPR.js.map +0 -1
  121. package/dist/candidates-relation-picker-Dd_GVLlu.cjs +0 -84
  122. package/dist/candidates-relation-picker-Dd_GVLlu.cjs.map +0 -1
  123. package/dist/check-confirm-dialog-BRByTFpg.cjs.map +0 -1
  124. package/dist/check-confirm-dialog-CkaVTPT9.js.map +0 -1
  125. package/dist/dialog-B_rt9S1c.js.map +0 -1
  126. package/dist/dialog-DRNUq9EA.cjs.map +0 -1
  127. package/dist/filter-builder-B3mZFL-K.cjs.map +0 -1
  128. package/dist/filter-builder-DOnaXNfP.js.map +0 -1
  129. package/dist/flow-runtime-B6zOHYqI.cjs.map +0 -1
  130. package/dist/flow-runtime-D8h99FSg.js.map +0 -1
  131. package/dist/flow-screen-CX05b3G9.cjs.map +0 -1
  132. package/dist/flow-screen-CmbARSqg.js.map +0 -1
  133. package/dist/inline-flow-host-B7U1OJUe.cjs.map +0 -1
  134. package/dist/inline-flow-host-C3H2UpSA.js.map +0 -1
  135. package/dist/lazy-relation-picker-dialog-2oxnA6IR.js +0 -14
  136. package/dist/lazy-relation-picker-dialog-2oxnA6IR.js.map +0 -1
  137. package/dist/lazy-relation-picker-dialog-BfAGKzUG.cjs +0 -19
  138. package/dist/lazy-relation-picker-dialog-BfAGKzUG.cjs.map +0 -1
  139. package/dist/page-flow-route-CQ7HsbBm.cjs.map +0 -1
  140. package/dist/page-flow-route-CyXSvXSE.js.map +0 -1
  141. package/dist/peek-filter-CnqHwTMU.cjs +0 -51
  142. package/dist/peek-filter-CnqHwTMU.cjs.map +0 -1
  143. package/dist/peek-filter-ErLfO153.js +0 -28
  144. package/dist/peek-filter-ErLfO153.js.map +0 -1
  145. package/dist/place-node--GdsP688.js.map +0 -1
  146. package/dist/place-node-SIp4XYQH.cjs.map +0 -1
  147. package/dist/related-list-timeline-BYJKEbmA.cjs.map +0 -1
  148. package/dist/related-list-timeline-DEdfd-rr.js.map +0 -1
  149. package/dist/related-rows-B4levHcR.js.map +0 -1
  150. package/dist/related-rows-zh5f_4QR.cjs.map +0 -1
  151. package/dist/relation-create-BOaUe12G.cjs +0 -362
  152. package/dist/relation-create-BOaUe12G.cjs.map +0 -1
  153. package/dist/relation-create-Bg-eEJLs.js +0 -337
  154. package/dist/relation-create-Bg-eEJLs.js.map +0 -1
  155. package/dist/relation-many-picker-B-NCEUgX.cjs.map +0 -1
  156. package/dist/relation-many-picker-gdAOD-qK.js.map +0 -1
  157. package/dist/relation-picker-BAY77ivO.js.map +0 -1
  158. package/dist/relation-picker-Da_uhFvA.cjs.map +0 -1
  159. package/dist/relation-picker-dialog-M0pQaWlp.cjs.map +0 -1
  160. package/dist/relation-picker-dialog-hA9rYrZB.js.map +0 -1
  161. package/dist/resource-page-router-OSQyRA5b.cjs.map +0 -1
  162. package/dist/resource-page-router-T4vS5gbJ.js.map +0 -1
  163. package/dist/search-access-B7G5oR8n.cjs.map +0 -1
  164. package/dist/search-access-CURsWn-U.js.map +0 -1
  165. package/dist/shell-BPlk8hEa.js +0 -205
  166. package/dist/shell-BPlk8hEa.js.map +0 -1
  167. package/dist/shell-DxhlsPwm.cjs +0 -271
  168. package/dist/shell-DxhlsPwm.cjs.map +0 -1
  169. package/dist/toaster-C7CiYA4Z.cjs.map +0 -1
  170. package/dist/toaster-CfCRLgR-.js.map +0 -1
  171. package/dist/use-dialog-filter-state-BBCC0OEK.cjs.map +0 -1
  172. package/dist/use-dialog-filter-state-BDcgS5Oz.js.map +0 -1
@@ -1,11 +1,12 @@
1
1
  import { l as cn } from "./use-theme-dnE3Od3I.js";
2
- import { d as useLinkComponent } from "./toaster-CfCRLgR-.js";
3
- import { i as DialogDescription, r as DialogContent, s as DialogTitle, t as Dialog } from "./dialog-B_rt9S1c.js";
4
- import { hasRecordPage, relationAmbiguousError, relationOpenOf, resourceKey, resourceShowHref } from "@burojs/core";
5
- import { PEEK_COMPONENT, QUICK_ANCHOR_ATTRIBUTE, filterFields, quickAnchorRef, useApp, useCurrentUIActivation, useEntryGate, useList, useListAccess, useOptionalT, useQuickOpener, useRelatedRecord, useUIPresentationRuntime } from "@burojs/react";
6
- import { IconSearch } from "@tabler/icons-react";
2
+ import { a as technicalText, l as useLinkComponent, n as toast, o as technicalTexts, r as friendlyStringsFrom, s as toFriendlyError } from "./toaster-Ct_YHGs6.js";
3
+ import { n as buttonVariants, t as Button } from "./button-MWVeZaqj.js";
4
+ import { i as DialogDescription, r as DialogContent, s as DialogTitle, t as Dialog } from "./dialog-C123sVQ8.js";
5
+ import { composeListFilter, hasRecordPage, normalizeDataError, normalizeFixedFilters, relationAmbiguousError, relationOpenOf, resourceKey, resourceShowHref, routeResourceKey } from "@burojs/core";
6
+ import { PEEK_COMPONENT, QUICK_ANCHOR_ATTRIBUTE, filterFields, quickAnchorRef, specificErrorText, useApp, useChromeT, useCurrentUIActivation, useEntryGate, useI18nCaps, useList, useListAccess, useLoadErrorView, useOptionalT, useQuick, useQuickOpener, useRelatedRecord, useRouteNavigate, useT, useTestIds, useUIPresentationRuntime } from "@burojs/react";
7
+ import { IconAlertCircle, IconEye, IconRefresh, IconSearch } from "@tabler/icons-react";
7
8
  import * as React from "react";
8
- import { createContext, useCallback, useContext, useEffect, useRef, useState, useSyncExternalStore } from "react";
9
+ import { Component, createContext, useCallback, useContext, useEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
9
10
  import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
10
11
  import { Command } from "cmdk";
11
12
  //#region src/primitives/command.tsx
@@ -92,6 +93,243 @@ function CommandDialog({ open, onOpenChange, defaultOpen, modal, title = "Comman
92
93
  }
93
94
  CommandDialog.displayName = "CommandDialog";
94
95
  //#endregion
96
+ //#region src/record/record-state.tsx
97
+ function RecordState({ state, onRetry, message: given }) {
98
+ const t = useT();
99
+ const chromeT = useChromeT();
100
+ if (state === "loading") return jsx("div", {
101
+ role: "status",
102
+ "aria-busy": "true",
103
+ "data-testid": "record-page:loading",
104
+ className: "p-6 text-sm text-muted-foreground",
105
+ children: t("router.recordLoading")
106
+ });
107
+ const message = given ?? (state === "not-found" ? t("record.notFoundOrNoAccess") : state === "session-expired" ? t("router.recordSessionExpired") : t("router.recordError"));
108
+ return jsxs("div", {
109
+ role: "alert",
110
+ "data-testid": `record-page:${state}`,
111
+ className: "flex flex-col items-center justify-center gap-3 px-6 py-24 text-center",
112
+ children: [jsx("p", {
113
+ className: "max-w-sm text-sm text-muted-foreground",
114
+ children: message
115
+ }), onRetry ? jsx(Button, {
116
+ type: "button",
117
+ variant: "outline",
118
+ size: "sm",
119
+ "data-testid": "record-page:retry",
120
+ onClick: onRetry,
121
+ children: chromeT("actions.retry")
122
+ }) : null]
123
+ });
124
+ }
125
+ //#endregion
126
+ //#region src/load-error/list-error-panel.tsx
127
+ function ListErrorPanel({ text, technical, onRetry, resource, title, className }) {
128
+ useT();
129
+ const t = useOptionalT();
130
+ const chromeT = useChromeT();
131
+ const raw = process.env.NODE_ENV === "production" ? void 0 : technical;
132
+ return jsxs("div", {
133
+ role: "alert",
134
+ className: cn("flex flex-col items-center justify-center gap-3 px-6 py-16 rounded-lg border border-destructive/40 bg-destructive/5 text-center", className),
135
+ "data-testid": "error",
136
+ children: [
137
+ jsx("div", {
138
+ className: "rounded-full bg-destructive/10 p-3 text-destructive",
139
+ children: jsx(IconAlertCircle, {
140
+ className: "size-6",
141
+ "aria-hidden": "true"
142
+ })
143
+ }),
144
+ jsxs("div", {
145
+ className: "space-y-1",
146
+ children: [jsx("h3", {
147
+ className: "text-base font-semibold tracking-tight text-destructive",
148
+ children: title ?? t("list.errorTitle")
149
+ }), jsx("p", {
150
+ className: "max-w-md text-sm text-muted-foreground",
151
+ children: text
152
+ })]
153
+ }),
154
+ raw && raw !== text ? jsxs("details", {
155
+ className: "max-w-md text-left",
156
+ children: [jsx("summary", {
157
+ className: "cursor-pointer text-xs text-muted-foreground hover:text-foreground",
158
+ children: t("list.errorDetails")
159
+ }), jsx("p", {
160
+ className: "mt-1.5 break-all font-mono text-xs text-muted-foreground",
161
+ "data-testid": "list-error-detail",
162
+ children: raw
163
+ })]
164
+ }) : null,
165
+ onRetry && jsx("div", {
166
+ className: "flex justify-center pt-2",
167
+ children: jsxs(Button, {
168
+ type: "button",
169
+ variant: "outline",
170
+ size: "sm",
171
+ "data-testid": resource ? `list-error-retry:${resource}` : "list-error-retry",
172
+ onClick: onRetry,
173
+ className: "gap-1.5",
174
+ children: [jsx(IconRefresh, {
175
+ className: "size-3.5",
176
+ "aria-hidden": "true"
177
+ }), chromeT("actions.retry")]
178
+ })
179
+ })
180
+ ]
181
+ });
182
+ }
183
+ //#endregion
184
+ //#region src/load-error/state.ts
185
+ function recordErrorState(error) {
186
+ const { kind } = normalizeDataError(error);
187
+ if (kind === "not-found" || kind === "forbidden") return "not-found";
188
+ if (kind === "auth") return "session-expired";
189
+ return "error";
190
+ }
191
+ //#endregion
192
+ //#region src/load-error/load-error-view.tsx
193
+ const RECORD_KEYS = {
194
+ "not-found": "record.notFoundOrNoAccess",
195
+ "session-expired": "router.recordSessionExpired",
196
+ error: "router.recordError"
197
+ };
198
+ const LIST_FALLBACK = {
199
+ key: "list.errorBody",
200
+ default: "Failed to load data. Please try again."
201
+ };
202
+ function loadErrorTechnicalTexts(error, original) {
203
+ const out = [...technicalTexts(error), ...technicalTexts(original)];
204
+ if (original !== null && typeof original === "object" && !(original instanceof Error)) {
205
+ const message = original.message;
206
+ if (typeof message === "string") out.push(message);
207
+ }
208
+ return out.filter((text, index) => text.length > 0 && out.indexOf(text) === index);
209
+ }
210
+ function useLoadErrorText(error, original, state, surface, fallback) {
211
+ const t = useOptionalT();
212
+ const caps = useI18nCaps();
213
+ if (state !== "error") return t(RECORD_KEYS[state]);
214
+ const own = specificErrorText(error, caps);
215
+ if (own !== void 0) return own;
216
+ const strings = friendlyStringsFrom(t);
217
+ for (const text of loadErrorTechnicalTexts(error, original)) {
218
+ const friendly = toFriendlyError(text, "", strings);
219
+ if (friendly.replaced && friendly.detail !== strings.generic) return friendly.detail;
220
+ }
221
+ const generic = surface === "list" ? fallback ?? LIST_FALLBACK : fallback;
222
+ if (surface === "record" || generic === void 0) return t(RECORD_KEYS.error);
223
+ return t(generic.key, { defaultValue: generic.default });
224
+ }
225
+ function DefaultLoadErrorView(props) {
226
+ const { error, surface, state, retry, original, fallback, testId, className, children, attributes } = props;
227
+ const text = useLoadErrorText(error, original, state, surface, fallback);
228
+ const chromeT = useChromeT();
229
+ if (surface === "record") return jsx(RecordState, {
230
+ state,
231
+ message: text,
232
+ ...retry === void 0 ? {} : { onRetry: retry }
233
+ });
234
+ if (surface === "list") return jsx(ListErrorPanel, {
235
+ text,
236
+ technical: loadErrorTechnicalTexts(error, original)[0],
237
+ ...retry === void 0 ? {} : { onRetry: retry },
238
+ ...props.resource === void 0 ? {} : { resource: props.resource },
239
+ ...props.title === void 0 ? {} : { title: props.title },
240
+ ...className === void 0 ? {} : { className }
241
+ });
242
+ if (surface === "relation" || surface === "picker") {
243
+ const technical = process.env.NODE_ENV === "production" ? void 0 : loadErrorTechnicalTexts(error, original)[0];
244
+ return jsx("span", {
245
+ ...attributes,
246
+ ...testId === void 0 ? {} : { "data-testid": testId },
247
+ className,
248
+ title: technical === void 0 ? text : `${text} (${technical})`,
249
+ children: children ?? text
250
+ });
251
+ }
252
+ return jsxs("div", {
253
+ ...attributes,
254
+ role: "alert",
255
+ ...testId === void 0 ? {} : { "data-testid": testId },
256
+ className: cn("flex flex-wrap items-center gap-2 rounded-md border border-dashed px-3 py-2 text-sm text-muted-foreground", className),
257
+ children: [jsx("span", { children: text }), retry === void 0 ? null : jsx(Button, {
258
+ type: "button",
259
+ variant: "outline",
260
+ size: "sm",
261
+ onClick: retry,
262
+ children: chromeT("actions.retry", { defaultValue: "Retry" })
263
+ })]
264
+ });
265
+ }
266
+ const UNSET = Symbol("unset");
267
+ var AppViewBoundary = class extends Component {
268
+ state = {
269
+ failedFor: UNSET,
270
+ failed: false
271
+ };
272
+ static getDerivedStateFromError() {
273
+ return { failed: true };
274
+ }
275
+ static getDerivedStateFromProps(props, state) {
276
+ if (state.failed && state.failedFor !== UNSET && state.failedFor !== props.error) return {
277
+ failedFor: UNSET,
278
+ failed: false
279
+ };
280
+ return null;
281
+ }
282
+ componentDidCatch(error, _info) {
283
+ this.setState({ failedFor: this.props.error });
284
+ if (process.env.NODE_ENV !== "production") console.error("[burojs] load-error.view-threw: the app's views.loadError threw while rendering; the kit's view is shown instead.", error);
285
+ }
286
+ render() {
287
+ return this.state.failed ? this.props.fallback : this.props.children;
288
+ }
289
+ };
290
+ function LoadError(props) {
291
+ const App = useLoadErrorView();
292
+ const { error: original, surface, resource } = props;
293
+ const error = useMemo(() => normalizeDataError(original), [original]);
294
+ const state = props.state ?? recordErrorState(error);
295
+ const retry = state === "error" ? props.retry : void 0;
296
+ const viewProps = {
297
+ error,
298
+ surface,
299
+ state,
300
+ ...resource === void 0 ? {} : { resource },
301
+ ...retry === void 0 ? {} : { retry }
302
+ };
303
+ const standard = jsx(DefaultLoadErrorView, {
304
+ ...viewProps,
305
+ original,
306
+ fallback: props.fallback,
307
+ testId: props.testId,
308
+ className: props.className,
309
+ title: props.title,
310
+ attributes: props.attributes,
311
+ children: props.children
312
+ });
313
+ if (App === void 0) return standard;
314
+ return jsx(AppViewBoundary, {
315
+ error: original,
316
+ fallback: standard,
317
+ children: jsx(App, { ...viewProps })
318
+ });
319
+ }
320
+ const INVALID_ROUTE_ID = Object.freeze({
321
+ kind: "not-found",
322
+ code: "invalid-record-id"
323
+ });
324
+ function InvalidRouteId({ resource }) {
325
+ return jsx(LoadError, {
326
+ surface: "record",
327
+ resource,
328
+ state: "not-found",
329
+ error: INVALID_ROUTE_ID
330
+ });
331
+ }
332
+ //#endregion
95
333
  //#region src/fields/claimed-quick-link.tsx
96
334
  function isPlainLeftClick(event) {
97
335
  return event.button === 0 && !event.metaKey && !event.ctrlKey && !event.shiftKey && !event.altKey;
@@ -330,7 +568,8 @@ function RelationDisplay({ value, field }) {
330
568
  open
331
569
  });
332
570
  }
333
- function RelationField({ to, value }) {
571
+ function RelationField({ to: toInput, value }) {
572
+ const to = routeResourceKey(toInput);
334
573
  return jsx(RelationDisplay, {
335
574
  value,
336
575
  field: {
@@ -472,6 +711,7 @@ function LazyByOpener({ to, by, idStr, target }) {
472
711
  pageSize: 2
473
712
  }
474
713
  } : {},
714
+ scope: "none",
475
715
  enabled: asked,
476
716
  retry: false
477
717
  }).data?.data;
@@ -483,7 +723,7 @@ function LazyByOpener({ to, by, idStr, target }) {
483
723
  if (!asked || rows === void 0 || !requested.current) return;
484
724
  if (rows.length > 1) {
485
725
  requested.current = false;
486
- if (process.env.NODE_ENV !== "production") console.error(`[burojs] ${relationAmbiguousError(to, by, idStr).message}`);
726
+ if (process.env.NODE_ENV !== "production") console.error(`[burojs] ${technicalText(relationAmbiguousError(to, by, idStr))}`);
487
727
  return;
488
728
  }
489
729
  requested.current = false;
@@ -571,10 +811,16 @@ function RelationDisplayBody({ to, by, id, idStr, app, open: openMode, quickTarg
571
811
  title: `${to}: more than one row has ${by ?? "id"} ${idStr}`,
572
812
  children: idStr
573
813
  });
574
- if (error) return jsx("span", {
575
- "data-testid": `${baseId}:error`,
814
+ if (error) return jsx(LoadError, {
815
+ surface: "relation",
816
+ error,
817
+ resource: to,
818
+ testId: `${baseId}:error`,
576
819
  className: "text-muted-foreground",
577
- title: error.message,
820
+ fallback: {
821
+ key: "relation.failed",
822
+ default: "Failed to load options."
823
+ },
578
824
  children: idStr
579
825
  });
580
826
  if (!record) return jsx("span", {
@@ -632,22 +878,6 @@ function RowOpener({ to, rowId, href, testid, open, children }) {
632
878
  })
633
879
  });
634
880
  }
635
- function RelationManyDisplay({ value, field }) {
636
- const keys = Array.isArray(value) ? value : [];
637
- if (keys.length === 0) return jsx("span", {
638
- "data-testid": "relation-many-display:empty",
639
- className: "text-muted-foreground",
640
- children: "—"
641
- });
642
- return jsx("span", {
643
- className: "inline-flex flex-wrap gap-x-2",
644
- "data-testid": "relation-many-display",
645
- children: keys.map((key, index) => jsx(RelationDisplay, {
646
- value: key,
647
- field
648
- }, `${String(key)}:${index}`))
649
- });
650
- }
651
881
  //#endregion
652
882
  //#region src/fields/search-access.ts
653
883
  function narrowSearchDecl(declared, visible) {
@@ -664,6 +894,343 @@ function namesHiddenField(filter, visible) {
664
894
  return filter !== void 0 && filterFields(filter).some((field) => !visible(field));
665
895
  }
666
896
  //#endregion
667
- export { CommandLoading as C, CommandList as S, CommandDialog as _, RelationManyDisplay as a, CommandInput as b, focusWithoutPeek as c, PeekHostContext as d, useRowTitle as f, Command$1 as g, isPlainLeftClick as h, RelationField as i, peekAnchorOpener as l, ClaimedQuickLink as m, narrowSearchDecl as n, linksToPage as o, recordDisplayName as p, RelationDisplay as r, relationLinkClassName as s, namesHiddenField as t, PeekContext as u, CommandEmpty as v, CommandSeparator as w, CommandItem as x, CommandGroup as y };
897
+ //#region src/fields/relation-hand-back.tsx
898
+ function createdOptionFilter(input) {
899
+ const own = createdOptionScope(input);
900
+ const byId = {
901
+ field: input.idField,
902
+ op: "eq",
903
+ value: input.id
904
+ };
905
+ return own === void 0 ? byId : { and: [own, byId] };
906
+ }
907
+ function createdOptionScope(input) {
908
+ return composeListFilter({
909
+ fixed: normalizeFixedFilters(input.fixed),
910
+ search: "",
911
+ ...input.searchDecl === void 0 ? {} : { searchDecl: input.searchDecl },
912
+ ...input.searchContext === void 0 ? {} : { searchContext: input.searchContext }
913
+ });
914
+ }
915
+ function CreatedOptionRead({ to, filter, scope, scopeField, by, idField, onSettled }) {
916
+ const query = useList(to, {
917
+ filter,
918
+ pagination: {
919
+ kind: "offset",
920
+ page: 1,
921
+ pageSize: 1
922
+ },
923
+ scope,
924
+ ...scopeField === void 0 ? {} : { scopeField },
925
+ retry: false
926
+ });
927
+ const settled = React.useRef(false);
928
+ const latest = React.useRef(onSettled);
929
+ latest.current = onSettled;
930
+ const rows = query.data?.data;
931
+ const answered = rows !== void 0 && query.isCurrentData && !query.isFetching;
932
+ const failed = query.isError && !query.isFetching;
933
+ React.useEffect(() => {
934
+ if (settled.current) return;
935
+ if (failed) {
936
+ settled.current = true;
937
+ latest.current({ kind: "failed" });
938
+ return;
939
+ }
940
+ if (!answered) return;
941
+ settled.current = true;
942
+ const value = rows[0]?.[by ?? idField];
943
+ latest.current(typeof value === "string" || typeof value === "number" ? {
944
+ kind: "fits",
945
+ value
946
+ } : { kind: "does-not-fit" });
947
+ }, [
948
+ answered,
949
+ failed,
950
+ rows,
951
+ by,
952
+ idField
953
+ ]);
954
+ return null;
955
+ }
956
+ //#endregion
957
+ //#region src/fields/relation-view-button.tsx
958
+ function selectKey(value) {
959
+ if (typeof value === "string") return value.length === 0 ? null : value;
960
+ if (typeof value === "number") return Number.isFinite(value) ? String(value) : null;
961
+ return null;
962
+ }
963
+ const VIEW_CLASS = cn(buttonVariants({
964
+ variant: "outline",
965
+ size: "icon"
966
+ }), "shrink-0");
967
+ const TITLE_CLASS = "min-w-0 max-w-[16rem] truncate";
968
+ const TITLE_VIEW_CLASS = cn(TITLE_CLASS, "cursor-pointer rounded-sm text-left hover:underline focus:outline-hidden focus-visible:ring-2 focus-visible:ring-ring");
969
+ function RelationViewButton(props) {
970
+ const gate = useEntryGate();
971
+ const { to, value, by } = props;
972
+ const access = useListAccess(to);
973
+ const key = selectKey(value);
974
+ const plain = plainTitle(props.title);
975
+ if (key === null || props.open === "none") return plain;
976
+ if (by !== void 0 && !(access.status === "ready" && access.fieldVisible(by))) return plain;
977
+ if (gate(to, "show").state !== "open") return plain;
978
+ return jsx(ViewButton, {
979
+ ...props,
980
+ keyString: key
981
+ }, key);
982
+ }
983
+ function plainTitle(title) {
984
+ return title === void 0 ? null : jsx("span", {
985
+ className: TITLE_CLASS,
986
+ children: title
987
+ });
988
+ }
989
+ function ViewButton({ to, by, fieldName, keyString, beforeOpen, open: openMode = "quick", title }) {
990
+ const app = useApp();
991
+ const t = useOptionalT();
992
+ const tid = useTestIds();
993
+ const quick = useQuick();
994
+ const navigate = useRouteNavigate();
995
+ const RouterLink = useLinkComponent();
996
+ const [asked, setAsked] = React.useState(false);
997
+ const requested = React.useRef(false);
998
+ const targetDef = app.registry.get(to);
999
+ const rawIdField = (targetDef?.data)?.idField;
1000
+ const idField = typeof rawIdField === "string" && rawIdField.length > 0 ? rawIdField : "id";
1001
+ const read = useList(to, {
1002
+ ...asked && by !== void 0 ? {
1003
+ filter: {
1004
+ field: by,
1005
+ op: "eq",
1006
+ value: keyString
1007
+ },
1008
+ pagination: {
1009
+ kind: "offset",
1010
+ page: 1,
1011
+ pageSize: 2
1012
+ }
1013
+ } : {},
1014
+ scope: "none",
1015
+ enabled: asked && by !== void 0,
1016
+ retry: false
1017
+ });
1018
+ const rows = read.isCurrentData && !read.isPlaceholderData && !read.isFetching ? read.data?.data : void 0;
1019
+ const open = React.useCallback((id) => {
1020
+ beforeOpen();
1021
+ if (openMode === "page") {
1022
+ if (targetDef !== void 0 && linksToPage(app, targetDef)) navigate(resourceShowHref(app.registry, targetDef, to, id));
1023
+ return;
1024
+ }
1025
+ quick.show(to, id);
1026
+ }, [
1027
+ beforeOpen,
1028
+ quick,
1029
+ to,
1030
+ openMode,
1031
+ navigate,
1032
+ app,
1033
+ targetDef
1034
+ ]);
1035
+ React.useEffect(() => {
1036
+ if (by === void 0 || !asked || rows === void 0 || !requested.current) return;
1037
+ requested.current = false;
1038
+ if (rows.length !== 1) {
1039
+ if (rows.length > 1 && process.env.NODE_ENV !== "production") console.error(`[burojs] ${to}: more than one row has ${by} ${keyString}; "view" opened nothing.`);
1040
+ return;
1041
+ }
1042
+ const id = rows[0]?.[idField];
1043
+ const next = selectKey(id);
1044
+ if (next !== null) open(next);
1045
+ }, [
1046
+ by,
1047
+ asked,
1048
+ rows,
1049
+ idField,
1050
+ keyString,
1051
+ to,
1052
+ open
1053
+ ]);
1054
+ const label = t("relationPicker.view", { defaultValue: "View" });
1055
+ const content = title === void 0 ? jsx(IconEye, {
1056
+ className: "h-4 w-4",
1057
+ "aria-hidden": true
1058
+ }) : title;
1059
+ const className = title === void 0 ? VIEW_CLASS : TITLE_VIEW_CLASS;
1060
+ const testid = tid.relationView(to, fieldName);
1061
+ if (openMode === "page" && (targetDef === void 0 || !linksToPage(app, targetDef))) return plainTitle(title);
1062
+ if (openMode === "page" && by === void 0 && targetDef !== void 0) return jsxs(RouterLink, {
1063
+ href: resourceShowHref(app.registry, targetDef, to, keyString),
1064
+ "data-testid": testid,
1065
+ className,
1066
+ onClick: () => beforeOpen(),
1067
+ children: [jsxs("span", {
1068
+ className: "sr-only",
1069
+ children: [label, " "]
1070
+ }), content]
1071
+ });
1072
+ if (by === void 0 && targetDef !== void 0 && linksToPage(app, targetDef)) return jsxs(ClaimedQuickLink, {
1073
+ href: resourceShowHref(app.registry, targetDef, to, keyString),
1074
+ testid,
1075
+ className,
1076
+ onOpen: () => open(keyString),
1077
+ children: [jsxs("span", {
1078
+ className: "sr-only",
1079
+ children: [label, " "]
1080
+ }), content]
1081
+ });
1082
+ return jsx("button", {
1083
+ type: "button",
1084
+ className,
1085
+ "data-testid": testid,
1086
+ "aria-label": title === void 0 ? label : `${label} ${title}`,
1087
+ [QUICK_ANCHOR_ATTRIBUTE]: "",
1088
+ onClick: (event) => {
1089
+ event.preventDefault();
1090
+ event.stopPropagation();
1091
+ if (!isPlainLeftClick(event)) return;
1092
+ if (by === void 0) {
1093
+ open(keyString);
1094
+ return;
1095
+ }
1096
+ requested.current = true;
1097
+ setAsked(true);
1098
+ if (rows !== void 0 && rows.length === 1) {
1099
+ const next = selectKey(rows[0]?.[idField]);
1100
+ requested.current = false;
1101
+ if (next !== null) open(next);
1102
+ }
1103
+ },
1104
+ children: content
1105
+ });
1106
+ }
1107
+ //#endregion
1108
+ //#region src/fields/relation-create.tsx
1109
+ function useSelectCreate(input) {
1110
+ const t = useOptionalT();
1111
+ const { to: target } = input;
1112
+ const [pending, setPending] = React.useState(false);
1113
+ const [created, setCreated] = React.useState(null);
1114
+ const latch = React.useRef(false);
1115
+ const mounted = React.useRef(true);
1116
+ React.useEffect(() => {
1117
+ mounted.current = true;
1118
+ return () => {
1119
+ mounted.current = false;
1120
+ };
1121
+ }, []);
1122
+ const latest = React.useRef(input);
1123
+ latest.current = input;
1124
+ const access = useListAccess(target);
1125
+ const blocked = input.held === true || access.status !== "ready" || namesHiddenField(createdOptionScope({
1126
+ fixed: input.fixedFilters,
1127
+ searchDecl: input.searchDecl,
1128
+ searchContext: input.searchContext
1129
+ }), (name) => access.fieldVisible(name));
1130
+ const blockedRef = React.useRef(blocked);
1131
+ blockedRef.current = blocked;
1132
+ const release = React.useCallback(() => {
1133
+ latch.current = false;
1134
+ if (mounted.current) {
1135
+ setPending(false);
1136
+ setCreated(null);
1137
+ }
1138
+ }, []);
1139
+ const start = React.useCallback((open) => {
1140
+ if (latch.current || !mounted.current || blockedRef.current) return;
1141
+ latch.current = true;
1142
+ setPending(true);
1143
+ let opened;
1144
+ try {
1145
+ opened = open();
1146
+ } catch (error) {
1147
+ release();
1148
+ throw error;
1149
+ }
1150
+ opened.then((row) => {
1151
+ if (!mounted.current) {
1152
+ latch.current = false;
1153
+ return;
1154
+ }
1155
+ const id = row === null ? null : selectKey(row[latest.current.idField]) ?? selectKey(row.id);
1156
+ if (id === null) release();
1157
+ else setCreated({ id });
1158
+ }, () => release());
1159
+ }, [release]);
1160
+ const settle = React.useCallback((result) => {
1161
+ if (mounted.current) {
1162
+ if (result.kind === "fits") latest.current.onValue(result.value);
1163
+ else if (result.kind === "does-not-fit") toast(t("relationPicker.createdNotFitting", { defaultValue: "The record was created but does not fit this field" }));
1164
+ else toast.error(t("relationPicker.createdNotSelected", { defaultValue: "The record was created but could not be selected" }));
1165
+ }
1166
+ release();
1167
+ }, [release, t]);
1168
+ return {
1169
+ create: React.useMemo(() => ({
1170
+ pending,
1171
+ blocked,
1172
+ start
1173
+ }), [
1174
+ pending,
1175
+ blocked,
1176
+ start
1177
+ ]),
1178
+ reader: created === null || blocked ? null : jsx(CreatedOptionRead, {
1179
+ to: target,
1180
+ filter: createdOptionFilter({
1181
+ fixed: input.fixedFilters,
1182
+ idField: input.idField,
1183
+ id: created.id,
1184
+ searchDecl: input.searchDecl,
1185
+ searchContext: input.searchContext
1186
+ }),
1187
+ scope: input.scope,
1188
+ ...input.scopeField === void 0 ? {} : { scopeField: input.scopeField },
1189
+ by: input.by,
1190
+ idField: input.idField,
1191
+ onSettled: settle
1192
+ }, created.id)
1193
+ };
1194
+ }
1195
+ function plainStringField(def) {
1196
+ if (def === null || typeof def !== "object") return false;
1197
+ const field = def;
1198
+ return field.kind === "string" && field.system !== true && field.opts?.system !== true && field.opts?.enum === void 0 && field.opts?.localized !== true;
1199
+ }
1200
+ function RelationCreateButton(props) {
1201
+ const gate = useEntryGate();
1202
+ const { to } = props;
1203
+ if (props.create.blocked || gate(to, "create").state !== "open") return null;
1204
+ return jsx(CreateButton, { ...props });
1205
+ }
1206
+ function CreateButton({ to, fieldName, create, seeds, search, beforeOpen, className, testId }) {
1207
+ const app = useApp();
1208
+ const t = useOptionalT();
1209
+ const tid = useTestIds();
1210
+ const quick = useQuick();
1211
+ const label = t("relationPicker.create", { defaultValue: "+ Create" });
1212
+ return jsx("button", {
1213
+ type: "button",
1214
+ "data-testid": testId ?? tid.relationCreate(to, fieldName),
1215
+ disabled: create.pending,
1216
+ className,
1217
+ onClick: () => {
1218
+ const text = search?.trim() ?? "";
1219
+ const displayField = app.registry.get(to)?.displayField;
1220
+ const fields = app.registry.get(to)?.fields;
1221
+ const prefill = text.length > 0 && typeof displayField === "string" && fields !== void 0 && Object.hasOwn(fields, displayField) && plainStringField(fields[displayField]) ? { [displayField]: text } : void 0;
1222
+ create.start(() => {
1223
+ beforeOpen();
1224
+ return quick.create(to, {
1225
+ ...seeds === void 0 || Object.keys(seeds).length === 0 ? {} : { defaults: seeds },
1226
+ ...prefill === void 0 ? {} : { input: prefill }
1227
+ });
1228
+ });
1229
+ },
1230
+ children: label
1231
+ });
1232
+ }
1233
+ //#endregion
1234
+ export { CommandEmpty as C, CommandList as D, CommandItem as E, CommandLoading as O, CommandDialog as S, CommandInput as T, isPlainLeftClick as _, namesHiddenField as a, RecordState as b, RelationField as c, peekAnchorOpener as d, PeekContext as f, ClaimedQuickLink as g, recordDisplayName as h, selectKey as i, CommandSeparator as k, relationLinkClassName as l, useRowTitle as m, useSelectCreate as n, narrowSearchDecl as o, PeekHostContext as p, RelationViewButton as r, RelationDisplay as s, RelationCreateButton as t, focusWithoutPeek as u, InvalidRouteId as v, CommandGroup as w, Command$1 as x, LoadError as y };
668
1235
 
669
- //# sourceMappingURL=search-access-CURsWn-U.js.map
1236
+ //# sourceMappingURL=relation-create-C43cdPQt.js.map