@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,11 +1,12 @@
1
1
  import { l as cn } from "./use-theme-dnE3Od3I.js";
2
- import { o as useLinkComponent } from "./toaster-C02XbB9r.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: {
@@ -484,7 +723,7 @@ function LazyByOpener({ to, by, idStr, target }) {
484
723
  if (!asked || rows === void 0 || !requested.current) return;
485
724
  if (rows.length > 1) {
486
725
  requested.current = false;
487
- 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))}`);
488
727
  return;
489
728
  }
490
729
  requested.current = false;
@@ -572,10 +811,16 @@ function RelationDisplayBody({ to, by, id, idStr, app, open: openMode, quickTarg
572
811
  title: `${to}: more than one row has ${by ?? "id"} ${idStr}`,
573
812
  children: idStr
574
813
  });
575
- if (error) return jsx("span", {
576
- "data-testid": `${baseId}:error`,
814
+ if (error) return jsx(LoadError, {
815
+ surface: "relation",
816
+ error,
817
+ resource: to,
818
+ testId: `${baseId}:error`,
577
819
  className: "text-muted-foreground",
578
- title: error.message,
820
+ fallback: {
821
+ key: "relation.failed",
822
+ default: "Failed to load options."
823
+ },
579
824
  children: idStr
580
825
  });
581
826
  if (!record) return jsx("span", {
@@ -649,6 +894,343 @@ function namesHiddenField(filter, visible) {
649
894
  return filter !== void 0 && filterFields(filter).some((field) => !visible(field));
650
895
  }
651
896
  //#endregion
652
- export { CommandSeparator as C, CommandLoading as S, CommandEmpty as _, linksToPage as a, CommandItem as b, peekAnchorOpener as c, useRowTitle as d, recordDisplayName as f, CommandDialog as g, Command$1 as h, RelationField as i, PeekContext as l, isPlainLeftClick as m, narrowSearchDecl as n, relationLinkClassName as o, ClaimedQuickLink as p, RelationDisplay as r, focusWithoutPeek as s, namesHiddenField as t, PeekHostContext as u, CommandGroup as v, CommandList as x, CommandInput 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 };
653
1235
 
654
- //# sourceMappingURL=search-access-CPPmfuvy.js.map
1236
+ //# sourceMappingURL=relation-create-C43cdPQt.js.map