@burojs/ui-shadcn 0.6.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.
- package/README.md +9 -0
- package/dist/bulk-summary-BWSAS7k2.js +143 -0
- package/dist/bulk-summary-BWSAS7k2.js.map +1 -0
- package/dist/bulk-summary-CrEij5vq.cjs +143 -0
- package/dist/bulk-summary-CrEij5vq.cjs.map +1 -0
- package/dist/{candidates-relation-picker-B116Bmqi.js → candidates-relation-picker-CS_JZ4Fd.js} +3 -3
- package/dist/{candidates-relation-picker-B116Bmqi.js.map → candidates-relation-picker-CS_JZ4Fd.js.map} +1 -1
- package/dist/{candidates-relation-picker-Cy0rTxvI.cjs → candidates-relation-picker-y5M6_oe4.cjs} +4 -4
- package/dist/{candidates-relation-picker-Cy0rTxvI.cjs.map → candidates-relation-picker-y5M6_oe4.cjs.map} +1 -1
- package/dist/{check-confirm-dialog-bRT8lt0d.js → check-confirm-dialog-2j0gOd7x.js} +384 -108
- package/dist/check-confirm-dialog-2j0gOd7x.js.map +1 -0
- package/dist/{check-confirm-dialog--gPFnEeg.cjs → check-confirm-dialog-C-NHgMST.cjs} +468 -107
- package/dist/check-confirm-dialog-C-NHgMST.cjs.map +1 -0
- package/dist/{dialog-B_rt9S1c.js → dialog-C123sVQ8.js} +4 -4
- package/dist/dialog-C123sVQ8.js.map +1 -0
- package/dist/{dialog-DRNUq9EA.cjs → dialog-vmMdfUWs.cjs} +3 -3
- package/dist/dialog-vmMdfUWs.cjs.map +1 -0
- package/dist/{filter-builder-nXqtM_h4.cjs → filter-builder-DFu0JP-1.cjs} +923 -120
- package/dist/filter-builder-DFu0JP-1.cjs.map +1 -0
- package/dist/{filter-builder-Bhn-dhZV.js → filter-builder-DaWHUZuV.js} +839 -108
- package/dist/filter-builder-DaWHUZuV.js.map +1 -0
- package/dist/{flow-runtime-D3avVH0I.cjs → flow-runtime-BpmrIUb6.cjs} +61 -29
- package/dist/flow-runtime-BpmrIUb6.cjs.map +1 -0
- package/dist/{flow-runtime-s6lkRPKF.js → flow-runtime-DyqTmK3H.js} +62 -30
- package/dist/flow-runtime-DyqTmK3H.js.map +1 -0
- package/dist/{flow-screen-Ci_Sik4Y.cjs → flow-screen-Bcz9NF2V.cjs} +128 -87
- package/dist/flow-screen-Bcz9NF2V.cjs.map +1 -0
- package/dist/{flow-screen-CCvgDI9G.js → flow-screen-DCdli6Ha.js} +128 -87
- package/dist/flow-screen-DCdli6Ha.js.map +1 -0
- package/dist/index.cjs +731 -906
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +455 -247
- package/dist/index.d.ts +455 -247
- package/dist/index.js +647 -828
- package/dist/index.js.map +1 -1
- package/dist/{inline-flow-host-DV1et3c6.cjs → inline-flow-host-3vw7LzzD.cjs} +5 -5
- package/dist/inline-flow-host-3vw7LzzD.cjs.map +1 -0
- package/dist/{inline-flow-host-B6FI0ctQ.js → inline-flow-host-C0rI9B7f.js} +5 -5
- package/dist/inline-flow-host-C0rI9B7f.js.map +1 -0
- package/dist/{candidates-gate-D33jXLHO.cjs → lazy-relation-picker-dialog-Bt8lrEIu.cjs} +10 -10
- package/dist/lazy-relation-picker-dialog-Bt8lrEIu.cjs.map +1 -0
- package/dist/{candidates-gate-Bb70LPAy.js → lazy-relation-picker-dialog-CAScLPb2.js} +11 -11
- package/dist/lazy-relation-picker-dialog-CAScLPb2.js.map +1 -0
- package/dist/{page-flow-route-i3ZfJ1sd.js → page-flow-route-BHUEpn0I.js} +36 -23
- package/dist/page-flow-route-BHUEpn0I.js.map +1 -0
- package/dist/{page-flow-route-hdnULWqd.cjs → page-flow-route-DS8IVTVH.cjs} +41 -28
- package/dist/page-flow-route-DS8IVTVH.cjs.map +1 -0
- package/dist/{place-node-C5SmIAYU.cjs → place-node-CNb3w-c7.cjs} +1869 -947
- package/dist/place-node-CNb3w-c7.cjs.map +1 -0
- package/dist/{place-node-NraNYKPx.js → place-node-CfzQvh4Y.js} +1824 -926
- package/dist/place-node-CfzQvh4Y.js.map +1 -0
- package/dist/{related-list-cards-BWZ9GfOG.js → related-list-cards-BIqqY69Y.js} +9 -4
- package/dist/{related-list-cards-BWZ9GfOG.js.map → related-list-cards-BIqqY69Y.js.map} +1 -1
- package/dist/{related-list-cards-CFq3meUn.cjs → related-list-cards-CelMxl4q.cjs} +9 -4
- package/dist/{related-list-cards-CFq3meUn.cjs.map → related-list-cards-CelMxl4q.cjs.map} +1 -1
- package/dist/{related-list-timeline-yrPsjA3F.js → related-list-timeline-CRh7xwLD.js} +6 -7
- package/dist/related-list-timeline-CRh7xwLD.js.map +1 -0
- package/dist/{related-list-timeline-CCzl71aA.cjs → related-list-timeline-DsLdi8sv.cjs} +5 -6
- package/dist/related-list-timeline-DsLdi8sv.cjs.map +1 -0
- package/dist/{related-rows-o0_t-Yce.js → related-rows--uP2iKQC.js} +25 -14
- package/dist/related-rows--uP2iKQC.js.map +1 -0
- package/dist/{related-rows-ea43qhhy.cjs → related-rows-MPnLCmPF.cjs} +27 -16
- package/dist/related-rows-MPnLCmPF.cjs.map +1 -0
- package/dist/{search-access-CPPmfuvy.js → relation-create-C43cdPQt.js} +595 -13
- package/dist/relation-create-C43cdPQt.js.map +1 -0
- package/dist/{search-access-Dugvttlg.cjs → relation-create-DDEYcKfw.cjs} +633 -14
- package/dist/relation-create-DDEYcKfw.cjs.map +1 -0
- package/dist/{relation-many-picker-bvkjkv-k.js → relation-many-picker-BJhaEqxz.js} +203 -102
- package/dist/relation-many-picker-BJhaEqxz.js.map +1 -0
- package/dist/{relation-many-picker-C8zFEjwd.cjs → relation-many-picker-DzuGJpEl.cjs} +211 -110
- package/dist/relation-many-picker-DzuGJpEl.cjs.map +1 -0
- package/dist/{relation-picker-Z0ZJJ5uS.js → relation-picker-D-tBBZW_.js} +23 -59
- package/dist/relation-picker-D-tBBZW_.js.map +1 -0
- package/dist/{relation-picker-gag0qSR5.cjs → relation-picker-DaIMzf2H.cjs} +29 -71
- package/dist/relation-picker-DaIMzf2H.cjs.map +1 -0
- package/dist/{relation-picker-dialog-0IzkjmiA.cjs → relation-picker-dialog-9Vg5vvR1.cjs} +133 -35
- package/dist/relation-picker-dialog-9Vg5vvR1.cjs.map +1 -0
- package/dist/{relation-picker-dialog-B6nIiJBf.js → relation-picker-dialog-DoEY8EkH.js} +134 -36
- package/dist/relation-picker-dialog-DoEY8EkH.js.map +1 -0
- package/dist/relation-select-B8Fq4o-t.js +48 -0
- package/dist/relation-select-B8Fq4o-t.js.map +1 -0
- package/dist/relation-select-Cr3CfNLy.cjs +55 -0
- package/dist/relation-select-Cr3CfNLy.cjs.map +1 -0
- package/dist/{resource-page-router-PXfMW9nh.cjs → resource-page-router-C_kGW2o_.cjs} +543 -300
- package/dist/resource-page-router-C_kGW2o_.cjs.map +1 -0
- package/dist/{resource-page-router-3RUf9d1j.js → resource-page-router-Diw6S6TK.js} +498 -255
- package/dist/resource-page-router-Diw6S6TK.js.map +1 -0
- package/dist/{rich-text-vTyVjDoa.cjs → rich-text-C_7JCvRo.cjs} +2 -2
- package/dist/{rich-text-vTyVjDoa.cjs.map → rich-text-C_7JCvRo.cjs.map} +1 -1
- package/dist/{rich-text-DFerywgz.js → rich-text-RcPBf6hr.js} +2 -2
- package/dist/{rich-text-DFerywgz.js.map → rich-text-RcPBf6hr.js.map} +1 -1
- package/dist/success-notices-Dl3M1fZP.cjs +46 -0
- package/dist/success-notices-Dl3M1fZP.cjs.map +1 -0
- package/dist/success-notices-LDEgEkc4.js +41 -0
- package/dist/success-notices-LDEgEkc4.js.map +1 -0
- package/dist/{toaster-Cr51X7bz.cjs → toaster-BbByOytP.cjs} +60 -15
- package/dist/toaster-BbByOytP.cjs.map +1 -0
- package/dist/{toaster-C02XbB9r.js → toaster-Ct_YHGs6.js} +43 -16
- package/dist/toaster-Ct_YHGs6.js.map +1 -0
- package/dist/{use-dialog-filter-state-C--oMAZG.js → use-dialog-filter-state-B_MqzHaT.js} +5 -4
- package/dist/use-dialog-filter-state-B_MqzHaT.js.map +1 -0
- package/dist/{use-dialog-filter-state-Dhe8PcB5.cjs → use-dialog-filter-state-tNvqAAjh.cjs} +6 -5
- package/dist/use-dialog-filter-state-tNvqAAjh.cjs.map +1 -0
- package/package.json +11 -11
- package/dist/bulk-summary-CwSd6Yjb.cjs +0 -108
- package/dist/bulk-summary-CwSd6Yjb.cjs.map +0 -1
- package/dist/bulk-summary-GxMDP4bw.js +0 -108
- package/dist/bulk-summary-GxMDP4bw.js.map +0 -1
- package/dist/candidates-gate-Bb70LPAy.js.map +0 -1
- package/dist/candidates-gate-D33jXLHO.cjs.map +0 -1
- package/dist/check-confirm-dialog--gPFnEeg.cjs.map +0 -1
- package/dist/check-confirm-dialog-bRT8lt0d.js.map +0 -1
- package/dist/dialog-B_rt9S1c.js.map +0 -1
- package/dist/dialog-DRNUq9EA.cjs.map +0 -1
- package/dist/filter-builder-Bhn-dhZV.js.map +0 -1
- package/dist/filter-builder-nXqtM_h4.cjs.map +0 -1
- package/dist/flow-runtime-D3avVH0I.cjs.map +0 -1
- package/dist/flow-runtime-s6lkRPKF.js.map +0 -1
- package/dist/flow-screen-CCvgDI9G.js.map +0 -1
- package/dist/flow-screen-Ci_Sik4Y.cjs.map +0 -1
- package/dist/inline-flow-host-B6FI0ctQ.js.map +0 -1
- package/dist/inline-flow-host-DV1et3c6.cjs.map +0 -1
- package/dist/page-flow-route-hdnULWqd.cjs.map +0 -1
- package/dist/page-flow-route-i3ZfJ1sd.js.map +0 -1
- package/dist/place-node-C5SmIAYU.cjs.map +0 -1
- package/dist/place-node-NraNYKPx.js.map +0 -1
- package/dist/related-list-timeline-CCzl71aA.cjs.map +0 -1
- package/dist/related-list-timeline-yrPsjA3F.js.map +0 -1
- package/dist/related-rows-ea43qhhy.cjs.map +0 -1
- package/dist/related-rows-o0_t-Yce.js.map +0 -1
- package/dist/relation-create-IA40t1hi.cjs +0 -364
- package/dist/relation-create-IA40t1hi.cjs.map +0 -1
- package/dist/relation-create-h6F9B4C4.js +0 -339
- package/dist/relation-create-h6F9B4C4.js.map +0 -1
- package/dist/relation-many-picker-C8zFEjwd.cjs.map +0 -1
- package/dist/relation-many-picker-bvkjkv-k.js.map +0 -1
- package/dist/relation-picker-Z0ZJJ5uS.js.map +0 -1
- package/dist/relation-picker-dialog-0IzkjmiA.cjs.map +0 -1
- package/dist/relation-picker-dialog-B6nIiJBf.js.map +0 -1
- package/dist/relation-picker-gag0qSR5.cjs.map +0 -1
- package/dist/resource-page-router-3RUf9d1j.js.map +0 -1
- package/dist/resource-page-router-PXfMW9nh.cjs.map +0 -1
- package/dist/search-access-CPPmfuvy.js.map +0 -1
- package/dist/search-access-Dugvttlg.cjs.map +0 -1
- package/dist/shell-BPlk8hEa.js +0 -205
- package/dist/shell-BPlk8hEa.js.map +0 -1
- package/dist/shell-DxhlsPwm.cjs +0 -271
- package/dist/shell-DxhlsPwm.cjs.map +0 -1
- package/dist/toaster-C02XbB9r.js.map +0 -1
- package/dist/toaster-Cr51X7bz.cjs.map +0 -1
- package/dist/use-dialog-filter-state-C--oMAZG.js.map +0 -1
- package/dist/use-dialog-filter-state-Dhe8PcB5.cjs.map +0 -1
|
@@ -1,20 +1,22 @@
|
|
|
1
1
|
import { l as cn } from "./use-theme-dnE3Od3I.js";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { i as isNetworkFailure, l as useLinkComponent, m as PopoverTrigger, n as toast, o as technicalTexts, p as PopoverContent, r as friendlyStringsFrom, s as toFriendlyError, u as Popover } from "./toaster-Ct_YHGs6.js";
|
|
3
|
+
import { B as DropdownMenuContent, C as loadBulkSummary, E as loadInlineFlowHost, F as Icon, H as DropdownMenuItem, O as preloadFlowRuntime, Q as DropdownMenuTrigger, R as DropdownMenu, S as RuntimeFlowRunner, U as DropdownMenuLabel, V as DropdownMenuGroup, _ as reasonText, b as RuntimeAppFlowRunner, c as useListSlot, d as useToBrowserHref, dt as DocInfo, f as FlowActionButton, g as ActionInner, k as useLoaded, m as useFlowChrome, n as ActionButton, o as canonicalResourceName, p as FlowMenuItem, q as DropdownMenuSeparator, r as DisabledAction, t as CheckConfirmDialog, v as FlowBoundary, w as loadFailedText, x as RuntimeBulkFlowRunner } from "./check-confirm-dialog-2j0gOd7x.js";
|
|
4
4
|
import { t as Button } from "./button-MWVeZaqj.js";
|
|
5
5
|
import { t as Input } from "./input-CUPStzZO.js";
|
|
6
|
-
import { a as DialogFooter, i as DialogDescription, o as DialogHeader, r as DialogContent, s as DialogTitle, t as Dialog } from "./dialog-
|
|
7
|
-
import {
|
|
6
|
+
import { a as DialogFooter, i as DialogDescription, o as DialogHeader, r as DialogContent, s as DialogTitle, t as Dialog } from "./dialog-C123sVQ8.js";
|
|
7
|
+
import { g as ClaimedQuickLink, l as relationLinkClassName, y as LoadError } from "./relation-create-C43cdPQt.js";
|
|
8
8
|
import { t as Skeleton } from "./skeleton-U2noefj5.js";
|
|
9
|
-
import { C as
|
|
9
|
+
import { A as TableRow, C as SelectValue, D as TableCell, F as ResolvedFieldInput, I as useFieldMetaDiagnostic, L as fieldDescription, N as MetaPlaceholder, O as TableHead, P as ResolvedFieldDisplay, R as lowerLegacyField, S as SelectTrigger, T as TableBody, _ as SelectItem, h as SelectContent, j as Badge, k as TableHeader, m as Select, t as FilterBuilder, w as Table$1 } from "./filter-builder-DaWHUZuV.js";
|
|
10
10
|
import { t as Label } from "./label-66W1qbke.js";
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
11
|
+
import { n as useCandidateSelection, t as LazyRelationPickerDialog } from "./lazy-relation-picker-dialog-CAScLPb2.js";
|
|
12
|
+
import { t as useRelationSelect } from "./relation-select-B8Fq4o-t.js";
|
|
13
|
+
import { BULK_EACH_LIMIT, FORM_SLOT, LAYOUT_SIZES, builtinFrames, canonicalEntityNodeKey, columnName, compileFlow, compiledScreenFor, declaredField, declaredText, displayClassOf, dtoFields, enumFallbackKey, enumFallbackText, enumNamespaceOf, enumValueText, exportFormatFieldKey, exportFormatLabelKey, exportFormatOptionKey, facts, fieldOptionText, fieldSensitivity, flowRecordOf, formatEntityRef, grantedPlacementKeys, hasRecordPage, humanizeKey, i18nPrefixToRef, i18nPrefixToRef as i18nPrefixToRef$1, inlineFlow, isBuiltinItemAction, isColumnSortable, isRelationField, isScreenFailure, linkOf, normalizeDataError, normalizeEntityRef, planItemActions, projectDto, recordEntries, relatedList, relatedRecord, resolveActionDensity, resolveActionSurfaceMode, resolveColumnUi, resolveContentSlot, resolveEnumLabel, resolveFieldLabel, resolveFieldOptionLabel, resolveFieldSlot, resolveNavLabel, resolveResourceLabel, resolveStaticAvailability, resolveSurfaceUi, resolveTextSlot, resourceKey, resourceShowHref, reverseLinkOf, routeResourceKey, routeServerIssues, savedViewKey, validatePlacementProps, whereEntryToFilter } from "@burojs/core";
|
|
14
|
+
import { ActionRouteContext, DismissGuardScope, FlowRowBatchContext, LOCAL_USER_SCOPE_ID, QUICK_ANCHOR_ATTRIBUTE, RECORD_KIND_KEYS, RecordScopeChain, ResourceListProvider, RouteSourceContext, ScreenStateProvider, columnTypeWidth, implementFrame, kindErrorText, linkCapabilitiesOf, policyRecordOf, policyRowsOf, recordKeyOf, resolveColumnField, resolveErrorText, resolveRoute, resolveValidationMessage, resourceFlowsOn, specificErrorText, tableLayoutSetting, useApp, useApplicationSnapshot, useAvailability, useAvailabilityResolver, useCapability, useCapabilityResolver, useChromeT, useClipboard, useComponent, useCtx, useCurrentUserId, useCustomQuery, useDataRuntime, useDismissGuard, useEntryGate, useEnumValues, useExportRuns, useFieldAccess, useFieldImplementation, useFieldMeta, useFieldMetaSources, useFlowRowBatch, useFlowRowResolver, useFormController, useFrameImplementations, useFramePiece, useFrameT, useI18nCaps, useItemActions, useLinkRights, useLinkWriter, useLinkedKeys, useListAccess, useLocale, useNavigationBlocker, useOne, useOptionalDataRuntime, useOptionalRecordFrame, useOptionalRouterAdapter, useOptionalT, useOptionalUiSettings, usePageFlowGate, usePageScreen, usePathAccess, usePlacement, useProjectedRecord, useProjectedTitle, usePublishDocVariant, useQuick, useQuickEntry, useQuickOpener, useRecordFrame, useRecordScopeFrames, useRecordTitle, useResource, useResourceExport, useResourceList, useResourceListContext, useRouteNavigate, useScreenState, useSearchParamsWriter, useT, useTestIds, useViewRow } from "@burojs/react";
|
|
15
|
+
import { IconAlertCircle, IconAlertTriangle, IconArrowDown, IconArrowLeft, IconArrowRight, IconArrowUp, IconBookmark, IconCheck, IconChevronLeft, IconChevronRight, IconChevronsLeft, IconChevronsRight, IconColumns3, IconCopy, IconDots, IconDownload, IconEyeOff, IconFilterX, IconInbox, IconLink, IconLoader2, IconLock, IconMinus, IconPencil, IconPinned, IconPinnedOff, IconPlus, IconRefresh, IconSelector, IconSettings2, IconTrash, IconUnlink, IconX } from "@tabler/icons-react";
|
|
14
16
|
import * as React from "react";
|
|
15
17
|
import { Component, Fragment, Suspense, createContext, createElement, lazy, memo, useCallback, useContext, useEffect, useId, useMemo, useRef, useState, useSyncExternalStore } from "react";
|
|
16
18
|
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
17
|
-
import { isFlowAction } from "@burojs/core/internal";
|
|
19
|
+
import { actionWhenHolds, enumBindingKey, hasNullRef, isFlowAction, whereToFilter } from "@burojs/core/internal";
|
|
18
20
|
import * as TabsPrimitive from "@radix-ui/react-tabs";
|
|
19
21
|
import * as CheckboxPrimitive from "@radix-ui/react-checkbox";
|
|
20
22
|
import { flexRender, getCoreRowModel, getFacetedRowModel, getFacetedUniqueValues, useReactTable } from "@tanstack/react-table";
|
|
@@ -117,428 +119,91 @@ function toDisplayText(value) {
|
|
|
117
119
|
}
|
|
118
120
|
}
|
|
119
121
|
//#endregion
|
|
120
|
-
//#region src/
|
|
121
|
-
|
|
122
|
-
const
|
|
123
|
-
|
|
124
|
-
text
|
|
125
|
-
|
|
126
|
-
format: "text"
|
|
127
|
-
},
|
|
128
|
-
textarea: {
|
|
129
|
-
type: "string",
|
|
130
|
-
format: "textarea"
|
|
131
|
-
},
|
|
132
|
-
code: {
|
|
133
|
-
type: "string",
|
|
134
|
-
format: "code"
|
|
135
|
-
},
|
|
136
|
-
email: {
|
|
137
|
-
type: "string",
|
|
138
|
-
format: "email"
|
|
139
|
-
},
|
|
140
|
-
url: {
|
|
141
|
-
type: "string",
|
|
142
|
-
format: "url"
|
|
143
|
-
},
|
|
144
|
-
uuid: {
|
|
145
|
-
type: "string",
|
|
146
|
-
format: "uuid"
|
|
147
|
-
},
|
|
148
|
-
number: { type: "number" },
|
|
149
|
-
boolean: { type: "boolean" },
|
|
150
|
-
date: {
|
|
151
|
-
type: "string",
|
|
152
|
-
format: "date"
|
|
153
|
-
},
|
|
154
|
-
dateTime: {
|
|
155
|
-
type: "string",
|
|
156
|
-
format: "date-time"
|
|
157
|
-
},
|
|
158
|
-
enum: {
|
|
159
|
-
type: "string",
|
|
160
|
-
format: "enum"
|
|
161
|
-
},
|
|
162
|
-
json: {
|
|
163
|
-
type: "object",
|
|
164
|
-
format: "json"
|
|
165
|
-
},
|
|
166
|
-
richText: {
|
|
167
|
-
type: "string",
|
|
168
|
-
format: "rich-text"
|
|
169
|
-
},
|
|
170
|
-
file: {
|
|
171
|
-
type: "array",
|
|
172
|
-
format: "file",
|
|
173
|
-
items: STRING_ITEM
|
|
174
|
-
},
|
|
175
|
-
relation: { type: "relation" },
|
|
176
|
-
relationMany: {
|
|
177
|
-
type: "array",
|
|
178
|
-
format: "relation",
|
|
179
|
-
items: STRING_ITEM
|
|
180
|
-
}
|
|
181
|
-
});
|
|
182
|
-
const SEMANTIC_KEYS = Object.freeze([
|
|
183
|
-
"type",
|
|
184
|
-
"format",
|
|
185
|
-
"meta",
|
|
186
|
-
"required",
|
|
187
|
-
"nullable",
|
|
188
|
-
"empty",
|
|
189
|
-
"default",
|
|
190
|
-
"constraints",
|
|
191
|
-
"texts",
|
|
192
|
-
"options",
|
|
193
|
-
"items",
|
|
194
|
-
"properties",
|
|
195
|
-
"additionalProperties",
|
|
196
|
-
"variants"
|
|
197
|
-
]);
|
|
198
|
-
function ownData(value, key) {
|
|
199
|
-
if (value === null || typeof value !== "object" && typeof value !== "function") return { present: false };
|
|
200
|
-
const descriptor = Object.getOwnPropertyDescriptor(value, key);
|
|
201
|
-
if (descriptor === void 0 || !Object.prototype.propertyIsEnumerable.call(value, key) || !("value" in descriptor)) return { present: false };
|
|
202
|
-
return {
|
|
203
|
-
present: true,
|
|
204
|
-
value: descriptor.value
|
|
205
|
-
};
|
|
206
|
-
}
|
|
207
|
-
function ownString(value, key) {
|
|
208
|
-
const candidate = ownData(value, key).value;
|
|
209
|
-
return typeof candidate === "string" && candidate.length > 0 ? candidate : void 0;
|
|
210
|
-
}
|
|
211
|
-
function projectUiSelection(input) {
|
|
212
|
-
const selection = {};
|
|
213
|
-
const inputId = ownString(input, "input");
|
|
214
|
-
const displayId = ownString(input, "display");
|
|
215
|
-
if (inputId !== void 0) selection.input = inputId;
|
|
216
|
-
if (displayId !== void 0) selection.display = displayId;
|
|
217
|
-
return inputId === void 0 && displayId === void 0 ? void 0 : Object.freeze(selection);
|
|
218
|
-
}
|
|
219
|
-
function projectFieldDeclaration(input) {
|
|
220
|
-
const type = ownString(input, "type");
|
|
221
|
-
if (type === void 0) return void 0;
|
|
222
|
-
const projected = { type };
|
|
223
|
-
for (const key of SEMANTIC_KEYS) {
|
|
224
|
-
if (key === "type" || key === "items" || key === "properties" || key === "variants") continue;
|
|
225
|
-
const candidate = ownData(input, key);
|
|
226
|
-
if (candidate.present && candidate.value !== void 0) projected[key] = candidate.value;
|
|
227
|
-
}
|
|
228
|
-
const ui = projectUiSelection(ownData(input, "ui").value);
|
|
229
|
-
if (ui !== void 0) projected.ui = ui;
|
|
230
|
-
if (type === "array") {
|
|
231
|
-
const item = projectFieldDeclaration(ownData(input, "items").value);
|
|
232
|
-
if (item !== void 0) projected.items = item;
|
|
233
|
-
}
|
|
234
|
-
if (type === "object") {
|
|
235
|
-
const properties = ownData(input, "properties").value;
|
|
236
|
-
if (properties !== null && typeof properties === "object" && !Array.isArray(properties)) {
|
|
237
|
-
const output = {};
|
|
238
|
-
for (const key of Object.keys(properties)) {
|
|
239
|
-
const child = projectFieldDeclaration(ownData(properties, key).value);
|
|
240
|
-
if (child !== void 0) output[key] = child;
|
|
241
|
-
}
|
|
242
|
-
projected.properties = Object.freeze(output);
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
if (type === "union") {
|
|
246
|
-
const variants = ownData(input, "variants").value;
|
|
247
|
-
if (Array.isArray(variants)) {
|
|
248
|
-
const output = [];
|
|
249
|
-
for (let index = 0; index < variants.length; index += 1) {
|
|
250
|
-
const child = projectFieldDeclaration(ownData(variants, index).value);
|
|
251
|
-
if (child !== void 0) output.push(child);
|
|
252
|
-
}
|
|
253
|
-
projected.variants = Object.freeze(output);
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
return Object.freeze(projected);
|
|
257
|
-
}
|
|
258
|
-
function projectUiHints(input) {
|
|
259
|
-
if (input === null || typeof input !== "object" || Array.isArray(input)) return void 0;
|
|
260
|
-
const projected = {};
|
|
261
|
-
for (const key of Object.keys(input)) {
|
|
262
|
-
const candidate = ownData(input, key);
|
|
263
|
-
if (candidate.present) projected[key] = candidate.value;
|
|
264
|
-
}
|
|
265
|
-
return Object.freeze(projected);
|
|
266
|
-
}
|
|
267
|
-
function lowerLegacyField(field) {
|
|
268
|
-
const declaration = projectFieldDeclaration(ownData(field, "declaration").value);
|
|
269
|
-
if (declaration !== void 0) return declaration;
|
|
270
|
-
const direct = projectFieldDeclaration(field);
|
|
271
|
-
const kind = ownString(field, "kind");
|
|
272
|
-
const mapping = kind === void 0 ? void 0 : LEGACY_KIND_DECLARATIONS[kind];
|
|
273
|
-
if (direct !== void 0) {
|
|
274
|
-
const format = direct.format ?? mapping?.format ?? kind;
|
|
275
|
-
return format === void 0 ? direct : Object.freeze({
|
|
276
|
-
...direct,
|
|
277
|
-
format
|
|
278
|
-
});
|
|
279
|
-
}
|
|
280
|
-
const ui = projectUiHints(ownData(field, "ui").value);
|
|
281
|
-
const type = kind === "file" && ui?.multiple !== true ? "unknown" : mapping?.type ?? "unknown";
|
|
282
|
-
const format = ownString(field, "format") ?? mapping?.format ?? kind;
|
|
283
|
-
const items = type === "array" ? projectFieldDeclaration(ownData(field, "items").value) ?? mapping?.items ?? STRING_ITEM : void 0;
|
|
284
|
-
const meta = ownData(field, "meta");
|
|
285
|
-
const texts = ownData(field, "texts");
|
|
286
|
-
const required = ownData(field, "required");
|
|
287
|
-
const nullable = ownData(field, "nullable");
|
|
288
|
-
const opts = ownData(field, "opts").value;
|
|
289
|
-
const optionRequired = ownData(opts, "required");
|
|
290
|
-
const optionNullable = ownData(opts, "nullable");
|
|
291
|
-
const requiredValue = required.present ? required.value : optionRequired.value;
|
|
292
|
-
const nullableValue = nullable.present ? nullable.value : optionNullable.value;
|
|
293
|
-
return Object.freeze({
|
|
294
|
-
type,
|
|
295
|
-
...format === void 0 ? {} : { format },
|
|
296
|
-
...items === void 0 ? {} : { items },
|
|
297
|
-
...meta.present && meta.value !== void 0 ? { meta: meta.value } : {},
|
|
298
|
-
...texts.present && texts.value !== void 0 ? { texts: texts.value } : {},
|
|
299
|
-
...requiredValue === true ? { required: true } : {},
|
|
300
|
-
...nullableValue === true ? { nullable: true } : {}
|
|
301
|
-
});
|
|
302
|
-
}
|
|
303
|
-
function lowerLegacyFieldSource(field) {
|
|
304
|
-
const declaration = lowerLegacyField(field);
|
|
305
|
-
const ui = projectUiHints(ownData(field, "ui").value);
|
|
306
|
-
if (ui === void 0) return declaration;
|
|
307
|
-
return Object.freeze({
|
|
308
|
-
...declaration,
|
|
309
|
-
ui
|
|
310
|
-
});
|
|
311
|
-
}
|
|
312
|
-
function fieldDescription(field) {
|
|
313
|
-
const declaration = field?.declaration;
|
|
314
|
-
return declaredText("field", declaration, "description") ?? declaredText("field", field, "description");
|
|
122
|
+
//#region src/data-table/annotate-if-clipped.ts
|
|
123
|
+
function annotateIfClipped(e) {
|
|
124
|
+
const el = e.currentTarget;
|
|
125
|
+
const text = el.scrollWidth > el.clientWidth || Array.from(el.querySelectorAll("*")).some((c) => c.scrollWidth > c.clientWidth) ? (el.textContent ?? "").trim() : "";
|
|
126
|
+
if (text) el.setAttribute("title", text);
|
|
127
|
+
else el.removeAttribute("title");
|
|
315
128
|
}
|
|
316
129
|
//#endregion
|
|
317
|
-
//#region src/fields/
|
|
318
|
-
const
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
const
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
if (item === null || typeof item !== "object" || Array.isArray(item)) continue;
|
|
332
|
-
const raw = item;
|
|
333
|
-
if (typeof raw.value !== "string" && typeof raw.value !== "number" && typeof raw.value !== "boolean" && raw.value !== null) continue;
|
|
334
|
-
const { value, label, description, ...legacyMeta } = raw;
|
|
335
|
-
if (typeof label !== "string" && process.env.NODE_ENV !== "production" && !warnedObjectLabels.has(`${name}:${value}`)) {
|
|
336
|
-
warnedObjectLabels.add(`${name}:${value}`);
|
|
337
|
-
console.warn(`[buro/ui-shadcn] field '${name}': option '${value}' has a non-string label, so it renders its raw value. Declare a plain string label and put the translations under fields.${name}.options.${value} (see docs/i18n-guide.md).`);
|
|
338
|
-
}
|
|
339
|
-
output.push(Object.freeze({
|
|
340
|
-
value,
|
|
341
|
-
label: typeof label === "string" ? label : String(value),
|
|
342
|
-
...typeof description === "string" ? { description } : {},
|
|
343
|
-
...Object.keys(legacyMeta).length === 0 ? {} : { meta: Object.freeze(legacyMeta) }
|
|
344
|
-
}));
|
|
345
|
-
}
|
|
346
|
-
return Object.freeze(output);
|
|
347
|
-
}
|
|
348
|
-
const readScope = (ctx) => ctx.scope;
|
|
349
|
-
function useFieldRuntimeContext(resource, record, values, metaSources) {
|
|
350
|
-
const scope = useCtx(readScope);
|
|
351
|
-
const locale = useLocale();
|
|
352
|
-
const caps = useI18nCaps(formatEntityRef(resource));
|
|
353
|
-
const metaSourcesOut = useFieldMetaSources({
|
|
354
|
-
record,
|
|
355
|
-
values,
|
|
356
|
-
extra: metaSources
|
|
357
|
-
});
|
|
358
|
-
return useMemo(() => Object.freeze({
|
|
359
|
-
i18n: Object.freeze({
|
|
360
|
-
locale,
|
|
361
|
-
has: caps.has,
|
|
362
|
-
t: caps.t
|
|
363
|
-
}),
|
|
364
|
-
sources: Object.freeze({
|
|
365
|
-
record: record ?? EMPTY,
|
|
366
|
-
values: values ?? EMPTY,
|
|
367
|
-
scope: scope ?? EMPTY
|
|
368
|
-
}),
|
|
369
|
-
metaSources: metaSourcesOut
|
|
370
|
-
}), [
|
|
371
|
-
caps,
|
|
372
|
-
locale,
|
|
373
|
-
record,
|
|
374
|
-
scope,
|
|
375
|
-
values,
|
|
376
|
-
metaSourcesOut
|
|
377
|
-
]);
|
|
378
|
-
}
|
|
379
|
-
function useFieldMetaDiagnostic(state, resource, name, field, surface) {
|
|
380
|
-
const reported = useRef(void 0);
|
|
381
|
-
const reason = state.status === "failed" ? state.reason : void 0;
|
|
382
|
-
useEffect(() => {
|
|
383
|
-
if (reason === void 0 || reported.current === reason || process.env.NODE_ENV === "production") return;
|
|
384
|
-
reported.current = reason;
|
|
385
|
-
const named = field.ui?.[surface];
|
|
386
|
-
const implementation = typeof named === "string" ? named : field.format ?? field.type;
|
|
387
|
-
console.error(`[burojs] field.meta-invalid: ${formatEntityRef(resource)}.${name} (${implementation}): ${reason}`);
|
|
388
|
-
}, [
|
|
389
|
-
reason,
|
|
390
|
-
resource,
|
|
391
|
-
name,
|
|
392
|
-
field,
|
|
393
|
-
surface
|
|
394
|
-
]);
|
|
395
|
-
}
|
|
396
|
-
function MetaPlaceholder({ state }) {
|
|
130
|
+
//#region src/fields/field-value-frame.tsx
|
|
131
|
+
const COPY_CLASSES = [
|
|
132
|
+
"string",
|
|
133
|
+
"number",
|
|
134
|
+
"date"
|
|
135
|
+
];
|
|
136
|
+
const COPIED_MS = 2e3;
|
|
137
|
+
const isRecord$1 = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
|
|
138
|
+
const isEmpty = (value) => value === null || value === void 0 || value === "";
|
|
139
|
+
function copyText(value) {
|
|
140
|
+
if (value instanceof Date) return value.toISOString();
|
|
141
|
+
return typeof value === "string" ? value : String(value);
|
|
142
|
+
}
|
|
143
|
+
function FieldValueFrame({ field, value, surface, entry, children }) {
|
|
397
144
|
const t = useOptionalT();
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
"data-buro-field-meta": "pending",
|
|
401
|
-
className: "h-9 w-full"
|
|
402
|
-
});
|
|
403
|
-
return jsx("div", {
|
|
404
|
-
role: "alert",
|
|
405
|
-
"data-buro-field-meta": "failed",
|
|
406
|
-
className: "rounded-md border border-dashed px-3 py-2 text-sm text-muted-foreground",
|
|
407
|
-
children: t("field.misconfigured", { defaultValue: "This field is misconfigured" })
|
|
408
|
-
});
|
|
409
|
-
}
|
|
410
|
-
function useFieldImplementationDiagnostic(diagnostic) {
|
|
411
|
-
const reported = useRef(void 0);
|
|
145
|
+
const clipboard = useClipboard();
|
|
146
|
+
const [copied, setCopied] = useState(false);
|
|
412
147
|
useEffect(() => {
|
|
413
|
-
if (
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
}, [
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
const
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
});
|
|
425
|
-
const runtimeContext = useFieldRuntimeContext(resource, record, record, metaSources);
|
|
426
|
-
const metaState = useFieldMeta(field, resolved.implementation, runtimeContext.metaSources);
|
|
427
|
-
useFieldMetaDiagnostic(metaState, resource, name, field, "display");
|
|
428
|
-
useFieldImplementationDiagnostic(resolved.diagnostic);
|
|
429
|
-
if (resolved.Component === void 0) return jsx(Fragment$1, { children: fallback });
|
|
430
|
-
if (metaState.status !== "ready") return jsx(MetaPlaceholder, { state: metaState });
|
|
431
|
-
const Component = resolved.Component;
|
|
432
|
-
const context = {
|
|
433
|
-
owner: resource,
|
|
434
|
-
resource,
|
|
148
|
+
if (!copied) return;
|
|
149
|
+
const timer = setTimeout(() => setCopied(false), COPIED_MS);
|
|
150
|
+
return () => clearTimeout(timer);
|
|
151
|
+
}, [copied]);
|
|
152
|
+
const ui = isRecord$1(field.ui) ? field.ui : void 0;
|
|
153
|
+
const asksCopy = ui?.copy === true;
|
|
154
|
+
const valueClass = useMemo(() => asksCopy ? displayClassOf({
|
|
155
|
+
...lowerLegacyField(field),
|
|
156
|
+
...ui === void 0 ? {} : { ui }
|
|
157
|
+
}) : void 0, [
|
|
158
|
+
asksCopy,
|
|
435
159
|
field,
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
};
|
|
451
|
-
if (resolved.multiple) return createElement(Component, {
|
|
452
|
-
...context,
|
|
453
|
-
multiple: true,
|
|
454
|
-
value: Array.isArray(value) ? value : []
|
|
455
|
-
});
|
|
456
|
-
return createElement(Component, {
|
|
457
|
-
...context,
|
|
458
|
-
multiple: false,
|
|
459
|
-
value
|
|
460
|
-
});
|
|
461
|
-
}
|
|
462
|
-
function ResolvedFieldInput({ resource, name, field: source, value, record, form, testId, inputId, onChange, onBlur, disabled, required, placeholder, descriptionId, errorId, errorMessage, pending = false, touched = errorMessage != null, issues: suppliedIssues, relationSeeds, fixedFilters, metaSources, fallback }) {
|
|
463
|
-
const sourceAtEdge = lowerLegacyFieldSource(source);
|
|
464
|
-
const base = canonical(sourceAtEdge);
|
|
465
|
-
const field = required === void 0 || required === base.required ? base : Object.freeze({
|
|
466
|
-
...base,
|
|
467
|
-
required
|
|
468
|
-
});
|
|
469
|
-
const ui = Object.freeze({
|
|
470
|
-
...sourceAtEdge.ui ?? EMPTY,
|
|
471
|
-
...placeholder === void 0 ? {} : { placeholder }
|
|
472
|
-
});
|
|
473
|
-
const resolved = useFieldImplementation({
|
|
474
|
-
field: {
|
|
475
|
-
...field,
|
|
476
|
-
ui
|
|
477
|
-
},
|
|
478
|
-
surface: "input"
|
|
160
|
+
ui
|
|
161
|
+
]);
|
|
162
|
+
const { overflow: declared } = resolveSurfaceUi(field, surface, entry);
|
|
163
|
+
const copyable = asksCopy && clipboard !== void 0 && !isEmpty(value) && valueClass !== void 0 && COPY_CLASSES.includes(valueClass);
|
|
164
|
+
if (!copyable && declared === void 0) return jsx(Fragment$1, { children });
|
|
165
|
+
const body = (declared ?? (surface === "column" ? "truncate" : "wrap")) === "wrap" ? jsx("span", {
|
|
166
|
+
className: "min-w-0 whitespace-normal [overflow-wrap:anywhere]",
|
|
167
|
+
"data-buro-overflow": "wrap",
|
|
168
|
+
children
|
|
169
|
+
}) : jsx("span", {
|
|
170
|
+
className: "inline-block min-w-0 max-w-full truncate align-bottom",
|
|
171
|
+
"data-buro-overflow": "truncate",
|
|
172
|
+
onPointerEnter: annotateIfClipped,
|
|
173
|
+
children
|
|
479
174
|
});
|
|
480
|
-
|
|
481
|
-
const
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
const Component = resolved.Component;
|
|
487
|
-
const issues = suppliedIssues ?? (errorMessage == null ? EMPTY_ISSUES$1 : Object.freeze([Object.freeze({
|
|
488
|
-
code: "ui.field-error",
|
|
489
|
-
message: {
|
|
490
|
-
key: "ui.field-error",
|
|
491
|
-
default: errorMessage
|
|
492
|
-
},
|
|
493
|
-
path: Object.freeze([]),
|
|
494
|
-
source: "field",
|
|
495
|
-
severity: "error"
|
|
496
|
-
})]));
|
|
497
|
-
const context = {
|
|
498
|
-
owner: resource,
|
|
499
|
-
resource,
|
|
500
|
-
field,
|
|
501
|
-
path: Object.freeze([name]),
|
|
502
|
-
surface: "input",
|
|
503
|
-
presentation: "default",
|
|
504
|
-
record,
|
|
505
|
-
form,
|
|
506
|
-
meta: metaState.meta,
|
|
507
|
-
ui,
|
|
508
|
-
options: optionsFor(field, name),
|
|
509
|
-
i18n: runtimeContext.i18n,
|
|
510
|
-
sources: runtimeContext.sources,
|
|
511
|
-
pending,
|
|
512
|
-
touched,
|
|
513
|
-
issues,
|
|
514
|
-
accessibility: Object.freeze({
|
|
515
|
-
input: inputId ?? name,
|
|
516
|
-
description: descriptionId,
|
|
517
|
-
error: errorId
|
|
518
|
-
}),
|
|
519
|
-
testIds: testId === void 0 ? EMPTY : Object.freeze({ input: testId })
|
|
175
|
+
if (!copyable) return jsx(Fragment$1, { children: body });
|
|
176
|
+
const label = t("field.copy", { defaultValue: "Copy" });
|
|
177
|
+
const copy = (event) => {
|
|
178
|
+
event.preventDefault();
|
|
179
|
+
event.stopPropagation();
|
|
180
|
+
clipboard(copyText(value)).then(() => setCopied(true), () => void 0);
|
|
520
181
|
};
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
182
|
+
return jsxs("span", {
|
|
183
|
+
className: "inline-flex min-w-0 max-w-full items-center gap-1 align-bottom",
|
|
184
|
+
children: [
|
|
185
|
+
body,
|
|
186
|
+
jsx("button", {
|
|
187
|
+
type: "button",
|
|
188
|
+
"data-testid": `field-copy:${field.name}`,
|
|
189
|
+
"aria-label": label,
|
|
190
|
+
title: label,
|
|
191
|
+
onClick: copy,
|
|
192
|
+
className: "inline-flex size-6 shrink-0 items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-accent hover:text-accent-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
|
|
193
|
+
children: copied ? jsx(IconCheck, {
|
|
194
|
+
"aria-hidden": "true",
|
|
195
|
+
className: "size-3.5"
|
|
196
|
+
}) : jsx(IconCopy, {
|
|
197
|
+
"aria-hidden": "true",
|
|
198
|
+
className: "size-3.5"
|
|
199
|
+
})
|
|
200
|
+
}),
|
|
201
|
+
jsx("span", {
|
|
202
|
+
"aria-live": "polite",
|
|
203
|
+
className: "sr-only",
|
|
204
|
+
children: copied ? t("field.copied", { defaultValue: "Copied" }) : ""
|
|
205
|
+
})
|
|
206
|
+
]
|
|
542
207
|
});
|
|
543
208
|
}
|
|
544
209
|
//#endregion
|
|
@@ -578,16 +243,26 @@ function Field({ resource, i18nPrefix, fallback, fixedFilters, field, doc, ...re
|
|
|
578
243
|
});
|
|
579
244
|
}
|
|
580
245
|
function FieldDisplay({ resource, i18nPrefix, fallback, value, ...rest }) {
|
|
581
|
-
|
|
582
|
-
...rest,
|
|
246
|
+
const frameField = useMemo(() => ({
|
|
247
|
+
...rest.field,
|
|
248
|
+
name: rest.name
|
|
249
|
+
}), [rest.field, rest.name]);
|
|
250
|
+
return jsx(FieldValueFrame, {
|
|
251
|
+
field: frameField,
|
|
583
252
|
value,
|
|
584
|
-
|
|
585
|
-
|
|
253
|
+
surface: "display",
|
|
254
|
+
children: jsx(ResolvedFieldDisplay, {
|
|
255
|
+
...rest,
|
|
256
|
+
value,
|
|
257
|
+
resource: resolveRef(resource, i18nPrefix),
|
|
258
|
+
fallback: fallback ?? jsx("span", { children: value == null || value === "" ? "—" : String(value) })
|
|
259
|
+
})
|
|
586
260
|
});
|
|
587
261
|
}
|
|
588
262
|
//#endregion
|
|
589
263
|
//#region src/screen/field-leaf.tsx
|
|
590
264
|
const SIZES = new Set(LAYOUT_SIZES);
|
|
265
|
+
const SHOWN_ISSUES = 3;
|
|
591
266
|
const FOCUSABLE = "input:not([type=\"hidden\"]):not([disabled]), textarea:not([disabled]), select:not([disabled]), button:not([disabled]), [contenteditable=\"true\"], [tabindex]:not([tabindex=\"-1\"])";
|
|
592
267
|
function useLeafSize(field) {
|
|
593
268
|
const implementation = useFieldImplementation({
|
|
@@ -633,7 +308,7 @@ function MisconfiguredLeaf({ node }) {
|
|
|
633
308
|
values: controller.values,
|
|
634
309
|
extra: controller.sources
|
|
635
310
|
});
|
|
636
|
-
const state = useFieldMeta(field, resolved.implementation, sources);
|
|
311
|
+
const state = useFieldMeta(field, resolved.implementation, sources, resolved.params);
|
|
637
312
|
useFieldMetaDiagnostic(state, binding.resource, name, field, surface);
|
|
638
313
|
const label = resolveFieldLabel(caps, name, declaredText("field", binding.field, "label"));
|
|
639
314
|
const testId = leafIds === "record" ? tid.formField(resource, mode, name) : `${leafIds.prefix}:field:${name}`;
|
|
@@ -665,10 +340,7 @@ function RecordLeaf({ node }) {
|
|
|
665
340
|
const t = useOptionalT();
|
|
666
341
|
if (node.restricted === true) {
|
|
667
342
|
const rawReason = node.restrictedReason;
|
|
668
|
-
const reason = rawReason === void 0 || rawReason === "" ? void 0 :
|
|
669
|
-
resource,
|
|
670
|
-
default: rawReason
|
|
671
|
-
});
|
|
343
|
+
const reason = rawReason === void 0 || rawReason === "" ? void 0 : reasonText(caps, rawReason, resource);
|
|
672
344
|
const restrictedId = tid.formFieldRestricted(resource, mode, name);
|
|
673
345
|
return jsxs("div", {
|
|
674
346
|
...attributes,
|
|
@@ -731,10 +403,7 @@ function RecordLeaf({ node }) {
|
|
|
731
403
|
});
|
|
732
404
|
if (node.editable !== true) {
|
|
733
405
|
const rawReason = node.lockReason;
|
|
734
|
-
const reason = rawReason === void 0 ? void 0 :
|
|
735
|
-
resource,
|
|
736
|
-
default: rawReason
|
|
737
|
-
});
|
|
406
|
+
const reason = rawReason === void 0 ? void 0 : reasonText(caps, rawReason, resource);
|
|
738
407
|
const locked = tid.formFieldLocked(resource, mode, name);
|
|
739
408
|
return jsxs("div", {
|
|
740
409
|
...attributes,
|
|
@@ -786,7 +455,6 @@ function RecordLeaf({ node }) {
|
|
|
786
455
|
function ActiveLeaf({ node, binding, label, attributes, inputId }) {
|
|
787
456
|
const { resource, mode, controller, disabled: locked } = useScreenTree();
|
|
788
457
|
const caps = useI18nCaps(resource);
|
|
789
|
-
const t = useOptionalT();
|
|
790
458
|
const tid = useTestIds();
|
|
791
459
|
const name = binding.name;
|
|
792
460
|
const record = controller.record;
|
|
@@ -802,24 +470,21 @@ function ActiveLeaf({ node, binding, label, attributes, inputId }) {
|
|
|
802
470
|
const messageId = `${mode}-${name}-msg`;
|
|
803
471
|
const placeholder = resolveFieldSlot(caps, name, "placeholder");
|
|
804
472
|
const hint = resolveFieldSlot(caps, name, "description", fieldDescription(binding.field));
|
|
473
|
+
const chromeT = useChromeT();
|
|
805
474
|
const fieldKey = `resources.${resource}.fields.${name}`;
|
|
806
|
-
const
|
|
807
|
-
code:
|
|
808
|
-
message:
|
|
809
|
-
...
|
|
810
|
-
}, {
|
|
811
|
-
t,
|
|
812
|
-
fieldKey
|
|
813
|
-
});
|
|
814
|
-
const warning = errorText === null ? binding.warnings[0] : void 0;
|
|
815
|
-
const warningText = warning === void 0 ? null : resolveValidationMessage({
|
|
816
|
-
code: warning.code ?? "",
|
|
817
|
-
message: warning.message,
|
|
818
|
-
...warning.params === void 0 ? {} : { params: warning.params }
|
|
475
|
+
const text = (issue) => resolveValidationMessage({
|
|
476
|
+
code: issue.code ?? "",
|
|
477
|
+
message: issue.message,
|
|
478
|
+
...issue.check === void 0 ? {} : { check: issue.check }
|
|
819
479
|
}, {
|
|
820
|
-
|
|
480
|
+
has: caps.has,
|
|
481
|
+
t: caps.t,
|
|
821
482
|
fieldKey
|
|
822
483
|
});
|
|
484
|
+
const shown = binding.issues.slice(0, SHOWN_ISSUES);
|
|
485
|
+
const more = binding.issues.length - shown.length;
|
|
486
|
+
const firstError = shown.find((issue) => issue.severity === "error");
|
|
487
|
+
const errorText = firstError === void 0 ? null : text(firstError);
|
|
823
488
|
const disabled = binding.disabled || controller.isSubmitting || locked === true;
|
|
824
489
|
const describedBy = errorText === null && hint ? messageId : void 0;
|
|
825
490
|
const errorMessageId = errorText === null ? void 0 : messageId;
|
|
@@ -833,6 +498,7 @@ function ActiveLeaf({ node, binding, label, attributes, inputId }) {
|
|
|
833
498
|
"data-buro-region": "field-label",
|
|
834
499
|
className: "flex items-center gap-2",
|
|
835
500
|
children: [jsx(Label, {
|
|
501
|
+
id: `${inputId}-label`,
|
|
836
502
|
htmlFor: inputId,
|
|
837
503
|
required: binding.required,
|
|
838
504
|
children: label
|
|
@@ -848,6 +514,7 @@ function ActiveLeaf({ node, binding, label, attributes, inputId }) {
|
|
|
848
514
|
resource: binding.resource,
|
|
849
515
|
name,
|
|
850
516
|
inputId,
|
|
517
|
+
labelId: `${inputId}-label`,
|
|
851
518
|
field: binding.field,
|
|
852
519
|
value: binding.value,
|
|
853
520
|
onChange: binding.onChange,
|
|
@@ -861,8 +528,10 @@ function ActiveLeaf({ node, binding, label, attributes, inputId }) {
|
|
|
861
528
|
...describedBy === void 0 ? {} : { descriptionId: describedBy },
|
|
862
529
|
...errorMessageId === void 0 ? {} : { errorId: errorMessageId },
|
|
863
530
|
errorMessage: errorText,
|
|
531
|
+
issues: binding.issues,
|
|
864
532
|
...binding.fixedFilters === void 0 ? {} : { fixedFilters: binding.fixedFilters },
|
|
865
533
|
...binding.relationSeeds === void 0 ? {} : { relationSeeds: binding.relationSeeds },
|
|
534
|
+
...binding.link === void 0 ? {} : { link: binding.link },
|
|
866
535
|
fallback: jsx(Input, {
|
|
867
536
|
id: inputId,
|
|
868
537
|
name: inputId,
|
|
@@ -877,18 +546,23 @@ function ActiveLeaf({ node, binding, label, attributes, inputId }) {
|
|
|
877
546
|
...errorMessageId === void 0 ? {} : { "aria-errormessage": errorMessageId },
|
|
878
547
|
onChange: (event) => binding.onChange(event.target.value)
|
|
879
548
|
})
|
|
880
|
-
}),
|
|
881
|
-
id: messageId,
|
|
882
|
-
"aria-live": "polite",
|
|
883
|
-
className: "text-sm text-destructive",
|
|
884
|
-
"data-testid": tid.formFieldError(resource, mode, name),
|
|
885
|
-
children: errorText
|
|
886
|
-
}) : warningText !== null ? jsx("p", {
|
|
549
|
+
}), shown.length > 0 ? jsxs("div", {
|
|
887
550
|
id: messageId,
|
|
888
551
|
"aria-live": "polite",
|
|
889
|
-
className: "text-sm text-warning-emphasis",
|
|
890
|
-
"data-testid": `form-field-warning:${resource}:${mode}:${name}
|
|
891
|
-
children:
|
|
552
|
+
className: cn("space-y-0.5 text-sm", errorText === null ? "text-warning-emphasis" : "text-destructive"),
|
|
553
|
+
"data-testid": errorText === null ? `form-field-warning:${resource}:${mode}:${name}` : tid.formFieldError(resource, mode, name),
|
|
554
|
+
children: [shown.map((issue, index) => jsx("p", {
|
|
555
|
+
"data-buro-severity": issue.severity,
|
|
556
|
+
className: issue.severity === "error" ? "text-destructive" : "text-warning-emphasis",
|
|
557
|
+
children: text(issue)
|
|
558
|
+
}, index)), more > 0 ? jsx("p", {
|
|
559
|
+
className: "text-muted-foreground",
|
|
560
|
+
"data-testid": `form-field-more:${resource}:${mode}:${name}`,
|
|
561
|
+
children: chromeT("validation.moreIssues", {
|
|
562
|
+
count: more,
|
|
563
|
+
defaultValue: "+{{count}} more"
|
|
564
|
+
})
|
|
565
|
+
}) : null]
|
|
892
566
|
}) : hint ? jsx("p", {
|
|
893
567
|
id: messageId,
|
|
894
568
|
"data-testid": tid.formFieldHelp(resource, mode, name),
|
|
@@ -908,7 +582,7 @@ function DeclarativeLeaf({ node, prefix, inputIds }) {
|
|
|
908
582
|
const docRef = i18nPrefixToRef(prefix);
|
|
909
583
|
const docScope = docRef.namespace === "default" ? docRef.id : formatEntityRef(docRef);
|
|
910
584
|
const caps = useI18nCaps(docScope);
|
|
911
|
-
const
|
|
585
|
+
const issueCaps = useI18nCaps();
|
|
912
586
|
const uid = useId();
|
|
913
587
|
const root = useRef(null);
|
|
914
588
|
const inputId = `${inputIds ?? uid}-${name}`;
|
|
@@ -962,9 +636,10 @@ function DeclarativeLeaf({ node, prefix, inputIds }) {
|
|
|
962
636
|
const text = (issue) => resolveValidationMessage({
|
|
963
637
|
code: issue.code ?? "",
|
|
964
638
|
message: issue.message,
|
|
965
|
-
...issue.
|
|
639
|
+
...issue.check === void 0 ? {} : { check: issue.check }
|
|
966
640
|
}, {
|
|
967
|
-
|
|
641
|
+
has: issueCaps.has,
|
|
642
|
+
t: issueCaps.t,
|
|
968
643
|
fieldKey
|
|
969
644
|
});
|
|
970
645
|
const defaultNode = jsx(Field, {
|
|
@@ -972,6 +647,7 @@ function DeclarativeLeaf({ node, prefix, inputIds }) {
|
|
|
972
647
|
field: binding.field,
|
|
973
648
|
metaSources: controller.sources,
|
|
974
649
|
inputId,
|
|
650
|
+
labelId: `${inputId}-label`,
|
|
975
651
|
i18nPrefix: prefix,
|
|
976
652
|
disabled: disabled === true || binding.disabled
|
|
977
653
|
});
|
|
@@ -987,6 +663,7 @@ function DeclarativeLeaf({ node, prefix, inputIds }) {
|
|
|
987
663
|
"data-buro-region": "field-label",
|
|
988
664
|
className: "flex items-center gap-2",
|
|
989
665
|
children: [jsx(Label, {
|
|
666
|
+
id: `${inputId}-label`,
|
|
990
667
|
htmlFor: inputId,
|
|
991
668
|
required: binding.required,
|
|
992
669
|
children: label
|
|
@@ -1027,9 +704,10 @@ function Notice({ children }) {
|
|
|
1027
704
|
children
|
|
1028
705
|
});
|
|
1029
706
|
}
|
|
1030
|
-
function FactsView({ props }) {
|
|
707
|
+
function FactsView({ props, empty }) {
|
|
1031
708
|
const own = props;
|
|
1032
709
|
const t = useOptionalT();
|
|
710
|
+
const chromeT = useChromeT();
|
|
1033
711
|
const snapshot = useApplicationSnapshot();
|
|
1034
712
|
const queryId = formatEntityRef(own.query);
|
|
1035
713
|
const caps = useI18nCaps();
|
|
@@ -1046,7 +724,10 @@ function FactsView({ props }) {
|
|
|
1046
724
|
kind: "custom-query",
|
|
1047
725
|
ref: own.query
|
|
1048
726
|
}), [own.query]);
|
|
1049
|
-
const result = useCustomQuery(handle, blocked ? {} : input, {
|
|
727
|
+
const result = useCustomQuery(handle, blocked ? {} : input, {
|
|
728
|
+
enabled: !blocked,
|
|
729
|
+
...own.refetchInterval === void 0 ? {} : { refetchInterval: own.refetchInterval }
|
|
730
|
+
});
|
|
1050
731
|
const shown = useMemo(() => {
|
|
1051
732
|
const byName = new Map(fields.map((field) => [field.name, field]));
|
|
1052
733
|
return (own.rows ?? fields.map((field) => field.name)).flatMap((name) => {
|
|
@@ -1054,7 +735,7 @@ function FactsView({ props }) {
|
|
|
1054
735
|
return field === void 0 ? [] : [field];
|
|
1055
736
|
});
|
|
1056
737
|
}, [fields, own.rows]);
|
|
1057
|
-
if (blocked) return jsx(Notice, { children: own.missing ?? t("facts.empty", { defaultValue: "No data" }) });
|
|
738
|
+
if (blocked) return jsx(Notice, { children: own.missing ?? empty ?? t("facts.empty", { defaultValue: "No data" }) });
|
|
1058
739
|
if (result.isError) return jsxs("div", {
|
|
1059
740
|
role: "alert",
|
|
1060
741
|
className: "flex flex-wrap items-center gap-2 rounded-md border border-dashed px-3 py-2 text-sm text-muted-foreground",
|
|
@@ -1063,7 +744,7 @@ function FactsView({ props }) {
|
|
|
1063
744
|
variant: "outline",
|
|
1064
745
|
size: "sm",
|
|
1065
746
|
onClick: () => void result.refetch(),
|
|
1066
|
-
children:
|
|
747
|
+
children: chromeT("actions.retry", { defaultValue: "Retry" })
|
|
1067
748
|
})]
|
|
1068
749
|
});
|
|
1069
750
|
if (result.data === void 0) return own.view === "list" ? jsx(Skeleton, {
|
|
@@ -1077,7 +758,7 @@ function FactsView({ props }) {
|
|
|
1077
758
|
}, field.name))
|
|
1078
759
|
});
|
|
1079
760
|
const data = result.data;
|
|
1080
|
-
if (data === null || typeof data !== "object" || shown.length === 0) return jsx(Notice, { children: own.missing ?? t("facts.empty", { defaultValue: "No data" }) });
|
|
761
|
+
if (data === null || typeof data !== "object" || shown.length === 0) return jsx(Notice, { children: own.missing ?? empty ?? t("facts.empty", { defaultValue: "No data" }) });
|
|
1081
762
|
const pairs = shown.map((field) => {
|
|
1082
763
|
const value = data[field.name];
|
|
1083
764
|
const label = resolveContentSlot(caps, [`queries.${queryId}.fields.${field.name}.label`, `queries.${queryId}.fields.${field.name}`], {
|
|
@@ -1087,13 +768,18 @@ function FactsView({ props }) {
|
|
|
1087
768
|
const body = value === null || value === void 0 ? jsx("span", {
|
|
1088
769
|
className: "text-muted-foreground",
|
|
1089
770
|
children: "—"
|
|
1090
|
-
}) : jsx(
|
|
1091
|
-
resource: own.query,
|
|
1092
|
-
name: field.name,
|
|
771
|
+
}) : jsx(FieldValueFrame, {
|
|
1093
772
|
field,
|
|
1094
773
|
value,
|
|
1095
|
-
|
|
1096
|
-
|
|
774
|
+
surface: "display",
|
|
775
|
+
children: jsx(ResolvedFieldDisplay, {
|
|
776
|
+
resource: own.query,
|
|
777
|
+
name: field.name,
|
|
778
|
+
field,
|
|
779
|
+
value,
|
|
780
|
+
record: data,
|
|
781
|
+
fallback: toDisplayText(value).text
|
|
782
|
+
})
|
|
1097
783
|
});
|
|
1098
784
|
return {
|
|
1099
785
|
name: field.name,
|
|
@@ -1147,6 +833,9 @@ const PlacementFrameContext = createContext(null);
|
|
|
1147
833
|
function usePlacementFrame() {
|
|
1148
834
|
return useContext(PlacementFrameContext);
|
|
1149
835
|
}
|
|
836
|
+
function placementLayoutId(frame) {
|
|
837
|
+
return frame?.owner === void 0 ? void 0 : `${frame.owner}/${frame.key.split(".").join("/")}`;
|
|
838
|
+
}
|
|
1150
839
|
//#endregion
|
|
1151
840
|
//#region src/flows/app-flow-button.tsx
|
|
1152
841
|
function AppFlowButton({ action, variant = "ghost", size = "sm", className }) {
|
|
@@ -1266,7 +955,7 @@ function RecordInlineFlow({ name }) {
|
|
|
1266
955
|
return jsx(InlineHostLoader, {
|
|
1267
956
|
resource,
|
|
1268
957
|
name: flowName,
|
|
1269
|
-
|
|
958
|
+
...hostRecord(flowRecordOf(compiled, row)),
|
|
1270
959
|
placementKey: key,
|
|
1271
960
|
lease: kept,
|
|
1272
961
|
stale: true,
|
|
@@ -1282,7 +971,7 @@ function RecordInlineFlow({ name }) {
|
|
|
1282
971
|
if (control.state === "disabled" && kept !== void 0 && kept.dirty) return jsx(InlineHostLoader, {
|
|
1283
972
|
resource,
|
|
1284
973
|
name: flowName,
|
|
1285
|
-
|
|
974
|
+
...hostRecord(flowRecordOf(compiled, row)),
|
|
1286
975
|
placementKey: key,
|
|
1287
976
|
lease: kept,
|
|
1288
977
|
stale: true,
|
|
@@ -1294,11 +983,12 @@ function RecordInlineFlow({ name }) {
|
|
|
1294
983
|
return jsx(InlineHostLoader, {
|
|
1295
984
|
resource,
|
|
1296
985
|
name: flowName,
|
|
1297
|
-
|
|
986
|
+
...hostRecord(flowRecordOf(compiled, control.record ?? row)),
|
|
1298
987
|
placementKey: key,
|
|
1299
988
|
...lease === void 0 ? {} : { lease }
|
|
1300
989
|
});
|
|
1301
990
|
}
|
|
991
|
+
const hostRecord = (record) => record === void 0 ? {} : { record };
|
|
1302
992
|
function InlineHostLoader(props) {
|
|
1303
993
|
const loaded = useLoaded(loadInlineFlowHost);
|
|
1304
994
|
if (loaded.error !== void 0) return jsx("p", {
|
|
@@ -1514,6 +1204,7 @@ function placementSeeds(where, resolved, closure) {
|
|
|
1514
1204
|
where.forEach((entry, index) => {
|
|
1515
1205
|
const value = resolved[index]?.value;
|
|
1516
1206
|
if (entry.op !== "eq" || value === null || value === void 0) return;
|
|
1207
|
+
if (typeof entry.field !== "string" || entry.field.includes(".")) return;
|
|
1517
1208
|
if (resolved[index]?.field !== entry.field) return;
|
|
1518
1209
|
const source = refSource(entry.value);
|
|
1519
1210
|
if (source === null ? isLiteral(entry.value) : source === "record" || source === "user" || !UNTRUSTED.includes(source) && Object.hasOwn(closure, source) && !(closure[source] ?? []).some((input) => UNTRUSTED.includes(input))) seeds[entry.field] = value;
|
|
@@ -1531,7 +1222,8 @@ function BadgeBody({ icon, label }) {
|
|
|
1531
1222
|
}) : null, label]
|
|
1532
1223
|
});
|
|
1533
1224
|
}
|
|
1534
|
-
function EnumBadge({ enumName, value, testid, className, presentation }) {
|
|
1225
|
+
function EnumBadge({ enumName: enumInput, value, testid, className, presentation }) {
|
|
1226
|
+
const enumName = enumBindingKey(enumInput) ?? "";
|
|
1535
1227
|
const app = useApp();
|
|
1536
1228
|
const caps = useI18nCaps();
|
|
1537
1229
|
const def = app.registry.enums.get(enumName);
|
|
@@ -1677,6 +1369,7 @@ function useExportRunWatcher() {
|
|
|
1677
1369
|
const runtime = useDataRuntime();
|
|
1678
1370
|
const runs = useExportRuns();
|
|
1679
1371
|
const t = useOptionalT();
|
|
1372
|
+
const caps = useI18nCaps();
|
|
1680
1373
|
React.useEffect(() => {
|
|
1681
1374
|
const announced = announcedRunsFor(runtime);
|
|
1682
1375
|
const unsubscribes = runs.map((run) => {
|
|
@@ -1695,7 +1388,7 @@ function useExportRunWatcher() {
|
|
|
1695
1388
|
label: t("export.toast.download"),
|
|
1696
1389
|
onClick: () => downloadArtifact(artifact)
|
|
1697
1390
|
} });
|
|
1698
|
-
} else if (state.status === "failed") toast.error(t("export.toast.failed", { params: { message: state.error
|
|
1391
|
+
} else if (state.status === "failed") toast.error(t("export.toast.failed", { params: { message: resolveErrorText(state.error, caps) } }));
|
|
1699
1392
|
};
|
|
1700
1393
|
check();
|
|
1701
1394
|
return run.handle.subscribe(check);
|
|
@@ -1706,24 +1399,27 @@ function useExportRunWatcher() {
|
|
|
1706
1399
|
}, [
|
|
1707
1400
|
runs,
|
|
1708
1401
|
runtime,
|
|
1709
|
-
t
|
|
1402
|
+
t,
|
|
1403
|
+
caps
|
|
1710
1404
|
]);
|
|
1711
1405
|
}
|
|
1712
1406
|
//#endregion
|
|
1713
1407
|
//#region src/actions/action-overflow.tsx
|
|
1714
1408
|
const RowActionsCollapseContext = createContext(false);
|
|
1715
|
-
function ActionOverflow({ resource, record, surface, className, hideFlows, collapse }) {
|
|
1716
|
-
const
|
|
1409
|
+
function ActionOverflow({ resource: resourceInput, record, surface, className, hideFlows, collapse, sources }) {
|
|
1410
|
+
const resource = routeResourceKey(resourceInput);
|
|
1411
|
+
const chromeT = useChromeT();
|
|
1717
1412
|
const tid = useTestIds();
|
|
1413
|
+
const recordKey = String(recordKeyOf(useApp().registry.get(resource), record) ?? "");
|
|
1718
1414
|
const collapsedRows = useContext(RowActionsCollapseContext);
|
|
1719
1415
|
const [flowFor, setFlowFor] = useState(null);
|
|
1720
|
-
const actions = useShownItemActions(resource, record, surface, hideFlows);
|
|
1416
|
+
const actions = useShownItemActions(resource, record, surface, hideFlows, sources);
|
|
1721
1417
|
if (actions.length === 0) return null;
|
|
1722
1418
|
const isRow = surface === "row";
|
|
1723
1419
|
const folded = isRow && (collapse ?? collapsedRows);
|
|
1724
1420
|
const standalone = folded ? [] : actions.filter((a) => a.placement === "standalone");
|
|
1725
1421
|
const menu = folded ? actions : actions.filter((a) => a.placement === "menu");
|
|
1726
|
-
const menuTestId = isRow ? tid.listRowActionsMenu(resource,
|
|
1422
|
+
const menuTestId = isRow ? tid.listRowActionsMenu(resource, recordKey) : tid.formActionsMenu(resource, surface);
|
|
1727
1423
|
const firstDestructive = menu.findIndex((a) => a.variant === "destructive");
|
|
1728
1424
|
const overlays = jsx(Fragment$1, { children: flowFor != null ? jsx(RuntimeFlowRunner, {
|
|
1729
1425
|
resource,
|
|
@@ -1737,6 +1433,7 @@ function ActionOverflow({ resource, record, surface, className, hideFlows, colla
|
|
|
1737
1433
|
actionName: a.name,
|
|
1738
1434
|
record,
|
|
1739
1435
|
scope: surface,
|
|
1436
|
+
...sources === void 0 ? {} : { sources },
|
|
1740
1437
|
density: a.density
|
|
1741
1438
|
}, a.name));
|
|
1742
1439
|
return jsxs("div", {
|
|
@@ -1760,7 +1457,7 @@ function ActionOverflow({ resource, record, surface, className, hideFlows, colla
|
|
|
1760
1457
|
size: "icon",
|
|
1761
1458
|
className: "size-7",
|
|
1762
1459
|
"data-testid": menuTestId,
|
|
1763
|
-
"aria-label":
|
|
1460
|
+
"aria-label": chromeT("actions.more", { defaultValue: "More" }),
|
|
1764
1461
|
children: jsx(IconDots, {
|
|
1765
1462
|
className: "h-4 w-4",
|
|
1766
1463
|
"aria-hidden": "true"
|
|
@@ -1774,15 +1471,16 @@ function ActionOverflow({ resource, record, surface, className, hideFlows, colla
|
|
|
1774
1471
|
children: [jsx(IconDots, {
|
|
1775
1472
|
className: "h-4 w-4",
|
|
1776
1473
|
"aria-hidden": "true"
|
|
1777
|
-
}),
|
|
1474
|
+
}), chromeT("actions.more", { defaultValue: "More" })]
|
|
1778
1475
|
})
|
|
1779
1476
|
}), jsx(DropdownMenuContent, {
|
|
1780
1477
|
align: "end",
|
|
1781
|
-
children: menu.map((a, index) => jsxs(Fragment, { children: [index === firstDestructive && index > 0 && jsx(DropdownMenuSeparator, { "data-testid": isRow ? `list-row-actions-separator:${resource}:${
|
|
1478
|
+
children: menu.map((a, index) => jsxs(Fragment, { children: [index === firstDestructive && index > 0 && jsx(DropdownMenuSeparator, { "data-testid": isRow ? `list-row-actions-separator:${resource}:${recordKey}` : `form-actions-separator:${resource}:${surface}` }), a.flow === void 0 ? jsx(ActionButton, {
|
|
1782
1479
|
resource,
|
|
1783
1480
|
actionName: a.name,
|
|
1784
1481
|
record,
|
|
1785
1482
|
scope: surface,
|
|
1483
|
+
...sources === void 0 ? {} : { sources },
|
|
1786
1484
|
asMenuItem: true
|
|
1787
1485
|
}) : jsx(FlowMenuItem, {
|
|
1788
1486
|
resource,
|
|
@@ -1802,11 +1500,12 @@ function ActionOverflow({ resource, record, surface, className, hideFlows, colla
|
|
|
1802
1500
|
]
|
|
1803
1501
|
});
|
|
1804
1502
|
}
|
|
1805
|
-
function useShownItemActions(resource, record, surface, hideFlows) {
|
|
1503
|
+
function useShownItemActions(resource, record, surface, hideFlows, sources) {
|
|
1806
1504
|
const resolved = useItemActions({
|
|
1807
1505
|
resource,
|
|
1808
1506
|
record,
|
|
1809
|
-
surface
|
|
1507
|
+
surface,
|
|
1508
|
+
...sources === void 0 ? {} : { sources }
|
|
1810
1509
|
});
|
|
1811
1510
|
const def = useApp().registry.get(resource);
|
|
1812
1511
|
const shown = def !== void 0 && isBuiltinItemAction((def.actions?.item)?.find((action) => action.name === "edit"), "edit") && !recordEntries(def).page.edit && recordEntries(def).quick.edit === null ? resolved.filter((a) => a.name !== "edit") : resolved;
|
|
@@ -1899,8 +1598,14 @@ function DocumentTitleProvider({ routeKey, children }) {
|
|
|
1899
1598
|
});
|
|
1900
1599
|
}
|
|
1901
1600
|
//#endregion
|
|
1601
|
+
//#region src/resource/resource-title.ts
|
|
1602
|
+
function resolveResourceTitle(caps, t, resource, slot, params) {
|
|
1603
|
+
return resolveContentSlot(caps, [`resources.${resource}.${slot}`], { params: { ...params } }) ?? t(`pageHeader.${slot}`, { params: { ...params } });
|
|
1604
|
+
}
|
|
1605
|
+
//#endregion
|
|
1902
1606
|
//#region src/resource/page-header.tsx
|
|
1903
|
-
function PageHeader({ resource, mode, recordName, backHref, backLabel, actions, title, breadcrumbs, backLink = false, visibleTitle = false, labelOverride, className }) {
|
|
1607
|
+
function PageHeader({ resource: resourceInput, mode, recordName, backHref, backLabel, actions, title, breadcrumbs, backLink = false, visibleTitle = false, labelOverride, className }) {
|
|
1608
|
+
const resource = routeResourceKey(resourceInput);
|
|
1904
1609
|
const app = useApp();
|
|
1905
1610
|
const t = useT();
|
|
1906
1611
|
const def = app.registry.get(resource);
|
|
@@ -1909,10 +1614,10 @@ function PageHeader({ resource, mode, recordName, backHref, backLabel, actions,
|
|
|
1909
1614
|
const resourceSingle = resolveResourceLabel(caps, resource, "single", def);
|
|
1910
1615
|
const labelSingle = labelOverride ?? resourceSingle;
|
|
1911
1616
|
const labelPlural = resolveResourceLabel(caps, resource, "plural", def);
|
|
1912
|
-
const computedTitle = title ?? (mode === "list" ? labelPlural : mode === "show" ? recordName ?? labelSingle : mode === "edit" ? recordName ? t
|
|
1617
|
+
const computedTitle = title ?? (mode === "list" ? labelPlural : mode === "show" ? recordName ?? labelSingle : mode === "edit" ? recordName ? resolveResourceTitle(caps, t, resource, "editTitleNamed", {
|
|
1913
1618
|
label: labelSingle,
|
|
1914
1619
|
name: recordName
|
|
1915
|
-
}
|
|
1620
|
+
}) : resolveResourceTitle(caps, t, resource, "editTitle", { label: labelSingle }) : resolveResourceTitle(caps, t, resource, "createTitle", { label: labelSingle }));
|
|
1916
1621
|
useDocumentTitle(app?.name ? `${computedTitle} · ${app.name}` : computedTitle);
|
|
1917
1622
|
const showBack = backLink && mode !== "list" && !breadcrumbs;
|
|
1918
1623
|
const computedBackHref = backHref ?? def?.basePath ?? `/${resource}`;
|
|
@@ -1943,9 +1648,10 @@ function PageHeader({ resource, mode, recordName, backHref, backLabel, actions,
|
|
|
1943
1648
|
]
|
|
1944
1649
|
});
|
|
1945
1650
|
}
|
|
1946
|
-
function ShowPageHeader({ resource, record, extraActions, breadcrumbs }) {
|
|
1651
|
+
function ShowPageHeader({ resource: resourceInput, record, extraActions, breadcrumbs }) {
|
|
1652
|
+
const resource = routeResourceKey(resourceInput);
|
|
1947
1653
|
const def = useApp().registry.get(resource);
|
|
1948
|
-
const recordName = useProjectedTitle(resource, record
|
|
1654
|
+
const recordName = useProjectedTitle(resource, recordKeyOf(def, record), record);
|
|
1949
1655
|
usePublishDocVariant(resource, record);
|
|
1950
1656
|
if (!def) return null;
|
|
1951
1657
|
return jsx(PageHeader, {
|
|
@@ -1960,16 +1666,18 @@ function ShowPageHeader({ resource, record, extraActions, breadcrumbs }) {
|
|
|
1960
1666
|
}), extraActions] })
|
|
1961
1667
|
});
|
|
1962
1668
|
}
|
|
1963
|
-
function EditPageHeader({ resource, record, backHref, breadcrumbs }) {
|
|
1669
|
+
function EditPageHeader({ resource: resourceInput, record, backHref, breadcrumbs }) {
|
|
1670
|
+
const resource = routeResourceKey(resourceInput);
|
|
1964
1671
|
const app = useApp();
|
|
1965
1672
|
const def = app.registry.get(resource);
|
|
1966
1673
|
const caps = useI18nCaps(resource);
|
|
1967
|
-
const
|
|
1674
|
+
const recordKey = recordKeyOf(def, record);
|
|
1675
|
+
const recordName = useProjectedTitle(resource, recordKey, record);
|
|
1968
1676
|
return jsx(PageHeader, {
|
|
1969
1677
|
resource,
|
|
1970
1678
|
mode: "edit",
|
|
1971
1679
|
recordName,
|
|
1972
|
-
backHref: editBackHref(app.registry, resource, String(
|
|
1680
|
+
backHref: editBackHref(app.registry, resource, String(recordKey ?? ""), backHref),
|
|
1973
1681
|
breadcrumbs,
|
|
1974
1682
|
backLabel: recordName ? `${resolveResourceLabel(caps, resource, "single", def)}: ${recordName}` : void 0,
|
|
1975
1683
|
actions: jsx(ActionOverflow, {
|
|
@@ -2169,7 +1877,8 @@ function ItemNode({ node, row, fallback }) {
|
|
|
2169
1877
|
const viewProps = {
|
|
2170
1878
|
props,
|
|
2171
1879
|
data: {},
|
|
2172
|
-
t: placement.viewProps?.t ?? ((name) => name)
|
|
1880
|
+
t: placement.viewProps?.t ?? ((name) => name),
|
|
1881
|
+
...placement.viewProps?.empty === void 0 ? {} : { empty: placement.viewProps.empty }
|
|
2173
1882
|
};
|
|
2174
1883
|
return jsx(View, { ...viewProps });
|
|
2175
1884
|
}
|
|
@@ -2251,6 +1960,7 @@ function BuroPanelFrame({ body, title, description, collapsible, open, onToggle,
|
|
|
2251
1960
|
const t = useOptionalT();
|
|
2252
1961
|
return jsxs("section", {
|
|
2253
1962
|
...nodeAttributes,
|
|
1963
|
+
...title === void 0 ? {} : { "aria-labelledby": titleId },
|
|
2254
1964
|
children: [
|
|
2255
1965
|
title === void 0 && info === null && !collapsible ? null : jsxs("div", {
|
|
2256
1966
|
"data-buro-region": "panel-title",
|
|
@@ -2310,7 +2020,6 @@ function BuroTabsFrame({ tabs, active, onSelect, info, nodeAttributes }) {
|
|
|
2310
2020
|
}), info]
|
|
2311
2021
|
}), tabs.map((tab) => jsx(TabsContent, {
|
|
2312
2022
|
value: tab.key,
|
|
2313
|
-
forceMount: true,
|
|
2314
2023
|
"data-buro-region": "tab-panel",
|
|
2315
2024
|
"data-buro-key": tab.key,
|
|
2316
2025
|
children: tab.panel
|
|
@@ -2398,17 +2107,17 @@ function BuroStepsFrame({ wizard, steps, goTo, back, next, headingRef, info, nod
|
|
|
2398
2107
|
//#region src/primitives/checkbox.tsx
|
|
2399
2108
|
const Checkbox = React.forwardRef(({ className, checked, ...props }, ref) => jsx(CheckboxPrimitive.Root, {
|
|
2400
2109
|
ref,
|
|
2401
|
-
className: cn("peer h-4 w-4 shrink-0 rounded-
|
|
2110
|
+
className: cn("peer inline-flex h-4 w-4 shrink-0 items-center justify-center align-middle rounded-[4px] border border-primary ring-offset-background focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground data-[state=indeterminate]:bg-primary data-[state=indeterminate]:text-primary-foreground", className),
|
|
2402
2111
|
checked,
|
|
2403
2112
|
...props,
|
|
2404
2113
|
children: jsx(CheckboxPrimitive.Indicator, {
|
|
2405
2114
|
className: cn("flex items-center justify-center text-current"),
|
|
2406
2115
|
children: checked === "indeterminate" ? jsx(IconMinus, {
|
|
2407
2116
|
"data-icon": "minus",
|
|
2408
|
-
className: "
|
|
2117
|
+
className: "size-3.5"
|
|
2409
2118
|
}) : jsx(IconCheck, {
|
|
2410
2119
|
"data-icon": "check",
|
|
2411
|
-
className: "
|
|
2120
|
+
className: "size-3.5"
|
|
2412
2121
|
})
|
|
2413
2122
|
})
|
|
2414
2123
|
}));
|
|
@@ -2433,6 +2142,17 @@ function useContainerNarrow(maxRem = 40) {
|
|
|
2433
2142
|
return [setElement, narrow];
|
|
2434
2143
|
}
|
|
2435
2144
|
//#endregion
|
|
2145
|
+
//#region src/data-table/fill-column.ts
|
|
2146
|
+
function fillColumnId(columns) {
|
|
2147
|
+
const eligible = columns.filter((c) => !c.pinned && !c.service);
|
|
2148
|
+
const declared = eligible.find((c) => c.fill);
|
|
2149
|
+
if (declared !== void 0) return declared.id;
|
|
2150
|
+
for (let i = eligible.length - 1; i >= 0; i--) {
|
|
2151
|
+
const c = eligible[i];
|
|
2152
|
+
if (c.resize && c.max === void 0) return c.id;
|
|
2153
|
+
}
|
|
2154
|
+
}
|
|
2155
|
+
//#endregion
|
|
2436
2156
|
//#region src/data-table/data-table.tsx
|
|
2437
2157
|
function pinEdgeClass(isLastLeft, isFirstRight) {
|
|
2438
2158
|
if (isLastLeft) return "after:pointer-events-none after:absolute after:inset-y-0 after:left-full after:z-10 after:w-1.5 after:bg-gradient-to-r after:from-overlay/[0.07] after:to-transparent dark:after:from-overlay/30";
|
|
@@ -2444,7 +2164,8 @@ function pinEdgeClassFor(column, overflowing) {
|
|
|
2444
2164
|
if (!pinned || !overflowing) return "";
|
|
2445
2165
|
return pinEdgeClass(pinned === "left" && column.getIsLastColumn("left"), pinned === "right" && column.getIsFirstColumn("right"));
|
|
2446
2166
|
}
|
|
2447
|
-
function getPinStyles(column, overflowing) {
|
|
2167
|
+
function getPinStyles(column, overflowing, fill = false) {
|
|
2168
|
+
if (fill) return {};
|
|
2448
2169
|
const explicitWidth = { width: `calc(var(--col-${column.id}-size) * 1px)` };
|
|
2449
2170
|
const pinned = column.getIsPinned();
|
|
2450
2171
|
if (!pinned || !overflowing) return explicitWidth;
|
|
@@ -2469,21 +2190,35 @@ function columnMetaNoRowNav(meta) {
|
|
|
2469
2190
|
function columnMetaNoClip(meta) {
|
|
2470
2191
|
return meta?.noClip === true;
|
|
2471
2192
|
}
|
|
2472
|
-
function
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
|
|
2476
|
-
|
|
2193
|
+
function columnMetaWrap(meta) {
|
|
2194
|
+
return meta?.wrap === true;
|
|
2195
|
+
}
|
|
2196
|
+
function columnMetaFill(meta) {
|
|
2197
|
+
return meta?.fill === true;
|
|
2477
2198
|
}
|
|
2478
|
-
function
|
|
2199
|
+
function tableFillColumnId(table) {
|
|
2200
|
+
if (table.options.enableColumnResizing !== true) return void 0;
|
|
2201
|
+
return fillColumnId(table.getVisibleLeafColumns().map((c) => {
|
|
2202
|
+
const max = c.columnDef.maxSize;
|
|
2203
|
+
return {
|
|
2204
|
+
id: c.id,
|
|
2205
|
+
fill: columnMetaFill(c.columnDef.meta),
|
|
2206
|
+
resize: c.getCanResize(),
|
|
2207
|
+
...max !== void 0 && max < Number.MAX_SAFE_INTEGER ? { max } : {},
|
|
2208
|
+
pinned: c.getIsPinned() !== false,
|
|
2209
|
+
service: c.id.startsWith("__")
|
|
2210
|
+
};
|
|
2211
|
+
}));
|
|
2212
|
+
}
|
|
2213
|
+
function renderCellMarkup({ cell, resizable, overflowing, fill, testId, onCellClick, noRowNav }) {
|
|
2479
2214
|
const liveColumn = cell.getContext().table.getColumn(cell.column.id) ?? cell.column;
|
|
2480
2215
|
const pinEdge = pinEdgeClassFor(liveColumn, overflowing);
|
|
2481
|
-
const noClip = columnMetaNoClip(cell.column.columnDef.meta);
|
|
2216
|
+
const noClip = columnMetaNoClip(cell.column.columnDef.meta) || columnMetaWrap(cell.column.columnDef.meta);
|
|
2482
2217
|
return jsx(TableCell, {
|
|
2483
2218
|
"data-testid": testId,
|
|
2484
2219
|
"data-no-row-nav": columnMetaNoRowNav(cell.column.columnDef.meta) || noRowNav ? "" : void 0,
|
|
2485
2220
|
onClick: onCellClick,
|
|
2486
|
-
style: getPinStyles(liveColumn, overflowing),
|
|
2221
|
+
style: getPinStyles(liveColumn, overflowing, fill),
|
|
2487
2222
|
className: cn(resizable && !pinEdge && !noClip && "overflow-hidden", pinEdge && "relative", pinEdge, columnMetaClassName(cell.column.columnDef.meta), onCellClick && "cursor-pointer"),
|
|
2488
2223
|
children: resizable && !noClip ? jsx("div", {
|
|
2489
2224
|
className: "truncate",
|
|
@@ -2495,7 +2230,7 @@ function renderCellMarkup({ cell, resizable, overflowing, testId, onCellClick, n
|
|
|
2495
2230
|
function MemoCellInner(props) {
|
|
2496
2231
|
return renderCellMarkup(props);
|
|
2497
2232
|
}
|
|
2498
|
-
const MemoCell = memo(MemoCellInner, (prev, next) => prev.rowOriginal === next.rowOriginal && prev.cell.column.id === next.cell.column.id && prev.resizable === next.resizable && (!prev.pinned && !next.pinned || prev.overflowing === next.overflowing) && prev.pinned === next.pinned && prev.testId === next.testId);
|
|
2233
|
+
const MemoCell = memo(MemoCellInner, (prev, next) => prev.rowOriginal === next.rowOriginal && prev.cell.column.id === next.cell.column.id && prev.resizable === next.resizable && (!prev.pinned && !next.pinned || prev.overflowing === next.overflowing) && prev.pinned === next.pinned && prev.fill === next.fill && prev.testId === next.testId);
|
|
2499
2234
|
function SelectionCellInner(props) {
|
|
2500
2235
|
const row = props.cell.row;
|
|
2501
2236
|
const toggle = (e) => {
|
|
@@ -2509,7 +2244,7 @@ function SelectionCellInner(props) {
|
|
|
2509
2244
|
});
|
|
2510
2245
|
}
|
|
2511
2246
|
const SelectionCell = memo(SelectionCellInner);
|
|
2512
|
-
function DataRowInner({ row, isSelected, resizable, overflowing, selectionColumnId, getCellTestId, getRowTestId, rowHref, onRowOpen, activateRow, rowClassName }) {
|
|
2247
|
+
function DataRowInner({ row, isSelected, resizable, overflowing, fillColumnId, selectionColumnId, getCellTestId, getRowTestId, rowHref, onRowOpen, activateRow, rowClassName }) {
|
|
2513
2248
|
const clickable = rowHref !== void 0 || onRowOpen !== void 0;
|
|
2514
2249
|
return jsx(TableRow, {
|
|
2515
2250
|
"data-state": isSelected ? "selected" : void 0,
|
|
@@ -2522,6 +2257,7 @@ function DataRowInner({ row, isSelected, resizable, overflowing, selectionColumn
|
|
|
2522
2257
|
isSelected,
|
|
2523
2258
|
resizable,
|
|
2524
2259
|
overflowing,
|
|
2260
|
+
fill: cell.column.id === fillColumnId,
|
|
2525
2261
|
pinned: cell.getContext().table.getColumn(cell.column.id)?.getIsPinned() ?? false,
|
|
2526
2262
|
testId: getCellTestId?.(row.original, cell.column.id)
|
|
2527
2263
|
}, cell.id) : jsx(MemoCell, {
|
|
@@ -2529,13 +2265,14 @@ function DataRowInner({ row, isSelected, resizable, overflowing, selectionColumn
|
|
|
2529
2265
|
rowOriginal: row.original,
|
|
2530
2266
|
resizable,
|
|
2531
2267
|
overflowing,
|
|
2268
|
+
fill: cell.column.id === fillColumnId,
|
|
2532
2269
|
pinned: cell.getContext().table.getColumn(cell.column.id)?.getIsPinned() ?? false,
|
|
2533
2270
|
testId: getCellTestId?.(row.original, cell.column.id)
|
|
2534
2271
|
}, cell.id))
|
|
2535
2272
|
});
|
|
2536
2273
|
}
|
|
2537
|
-
const MemoRow = memo(DataRowInner, (prev, next) => prev.row.original === next.row.original && prev.row.id === next.row.id && prev.isSelected === next.isSelected && prev.resizable === next.resizable && prev.overflowing === next.overflowing && prev.columnLayoutKey === next.columnLayoutKey && prev.selectionColumnId === next.selectionColumnId && prev.getCellTestId === next.getCellTestId && prev.getRowTestId === next.getRowTestId && prev.rowHref === next.rowHref && prev.onRowOpen === next.onRowOpen && prev.activateRow === next.activateRow && prev.rowClassName === next.rowClassName);
|
|
2538
|
-
function DataTableBodyInner({ rows, isLoading, loadingRowCount, visibleColumnCount, emptyState, emptyText, resizable, overflowing, columnLayoutKey, selectionColumnId, getCellTestId, getRowTestId, rowHref, onRowOpen, activateRow, rowClassName }) {
|
|
2274
|
+
const MemoRow = memo(DataRowInner, (prev, next) => prev.row.original === next.row.original && prev.row.id === next.row.id && prev.isSelected === next.isSelected && prev.resizable === next.resizable && prev.overflowing === next.overflowing && prev.columnLayoutKey === next.columnLayoutKey && prev.fillColumnId === next.fillColumnId && prev.selectionColumnId === next.selectionColumnId && prev.getCellTestId === next.getCellTestId && prev.getRowTestId === next.getRowTestId && prev.rowHref === next.rowHref && prev.onRowOpen === next.onRowOpen && prev.activateRow === next.activateRow && prev.rowClassName === next.rowClassName);
|
|
2275
|
+
function DataTableBodyInner({ rows, isLoading, loadingRowCount, visibleColumnCount, emptyState, emptyText, resizable, overflowing, columnLayoutKey, fillColumnId, selectionColumnId, getCellTestId, getRowTestId, rowHref, onRowOpen, activateRow, rowClassName }) {
|
|
2539
2276
|
return jsx(TableBody, { children: isLoading ? Array.from({ length: loadingRowCount }).map((_, i) => jsx(TableRow, {
|
|
2540
2277
|
"aria-busy": "true",
|
|
2541
2278
|
children: Array.from({ length: Math.max(1, visibleColumnCount) }).map((__, j) => jsx(TableCell, { children: jsx("div", { className: "h-4 w-3/4 animate-pulse rounded bg-muted" }) }, `skeleton-${i}-${j}`))
|
|
@@ -2552,6 +2289,7 @@ function DataTableBodyInner({ rows, isLoading, loadingRowCount, visibleColumnCou
|
|
|
2552
2289
|
resizable,
|
|
2553
2290
|
overflowing,
|
|
2554
2291
|
columnLayoutKey,
|
|
2292
|
+
fillColumnId,
|
|
2555
2293
|
selectionColumnId,
|
|
2556
2294
|
getCellTestId,
|
|
2557
2295
|
getRowTestId,
|
|
@@ -2563,7 +2301,7 @@ function DataTableBodyInner({ rows, isLoading, loadingRowCount, visibleColumnCou
|
|
|
2563
2301
|
}
|
|
2564
2302
|
const DataTableBody = memo(DataTableBodyInner, (prev, next) => {
|
|
2565
2303
|
if (!next.isResizing) return false;
|
|
2566
|
-
return prev.rows === next.rows && prev.overflowing === next.overflowing && prev.isLoading === next.isLoading && prev.columnLayoutKey === next.columnLayoutKey && prev.selectionColumnId === next.selectionColumnId;
|
|
2304
|
+
return prev.rows === next.rows && prev.overflowing === next.overflowing && prev.isLoading === next.isLoading && prev.columnLayoutKey === next.columnLayoutKey && prev.fillColumnId === next.fillColumnId && prev.selectionColumnId === next.selectionColumnId;
|
|
2567
2305
|
});
|
|
2568
2306
|
function DataTable({ table, tableTestId, getRowTestId, getCellTestId, isLoading = false, loadingRowCount = 5, emptyState, errorState, rowClassName, ariaLabel, onColumnReorder, rowHref, onRowOpen, selectionColumnId, className }) {
|
|
2569
2307
|
const t = useOptionalT();
|
|
@@ -2593,8 +2331,10 @@ function DataTable({ table, tableTestId, getRowTestId, getCellTestId, isLoading
|
|
|
2593
2331
|
const rows = table.getRowModel().rows;
|
|
2594
2332
|
const headerGroups = table.getHeaderGroups();
|
|
2595
2333
|
const visibleColumnCount = table.getVisibleLeafColumns().length;
|
|
2334
|
+
const fillId = tableFillColumnId(table);
|
|
2596
2335
|
const columnLayoutKey = table.getVisibleLeafColumns().map((c) => `${c.id}:${c.getIsPinned() || ""}`).join("|");
|
|
2597
2336
|
const resizable = table.options.enableColumnResizing === true;
|
|
2337
|
+
const widthSum = resizable ? table.getVisibleLeafColumns().reduce((sum, c) => sum + c.getSize(), 0) : 0;
|
|
2598
2338
|
const hasPinned = table.getState().columnPinning != null && (table.getState().columnPinning.left?.length || table.getState().columnPinning.right?.length);
|
|
2599
2339
|
const sizingState = table.getState().columnSizing;
|
|
2600
2340
|
const sizingInfo = table.getState().columnSizingInfo;
|
|
@@ -2686,7 +2426,7 @@ function DataTable({ table, tableTestId, getRowTestId, getCellTestId, isLoading
|
|
|
2686
2426
|
className,
|
|
2687
2427
|
style: resizable ? {
|
|
2688
2428
|
...columnSizeVars,
|
|
2689
|
-
|
|
2429
|
+
width: fillId !== void 0 ? `max(100%, ${widthSum}px)` : `${widthSum}px`,
|
|
2690
2430
|
tableLayout: "fixed"
|
|
2691
2431
|
} : void 0,
|
|
2692
2432
|
children: [jsx(TableHeader, { children: headerGroups.map((hg) => jsx(TableRow, { children: hg.headers.map((header) => {
|
|
@@ -2697,7 +2437,7 @@ function DataTable({ table, tableTestId, getRowTestId, getCellTestId, isLoading
|
|
|
2697
2437
|
return jsxs(TableHead, {
|
|
2698
2438
|
colSpan: header.colSpan,
|
|
2699
2439
|
"aria-sort": ariaSort,
|
|
2700
|
-
style: getPinStyles(header.column, overflowing),
|
|
2440
|
+
style: getPinStyles(header.column, overflowing, header.column.id === fillId),
|
|
2701
2441
|
className: cn("relative", resizable && !columnMetaNoClip(header.column.columnDef.meta) && (pinEdgeClassFor(header.column, overflowing) ? "[&>*:not([data-testid^=col-resize])]:truncate" : "overflow-hidden [&>*:not([data-testid^=col-resize])]:truncate"), canReorder && "cursor-grab", pinEdgeClassFor(header.column, overflowing), columnMetaClassName(header.column.columnDef.meta)),
|
|
2702
2442
|
draggable: canReorder || void 0,
|
|
2703
2443
|
onDragStart: canReorder ? (e) => {
|
|
@@ -2756,6 +2496,7 @@ function DataTable({ table, tableTestId, getRowTestId, getCellTestId, isLoading
|
|
|
2756
2496
|
resizable,
|
|
2757
2497
|
overflowing,
|
|
2758
2498
|
columnLayoutKey,
|
|
2499
|
+
fillColumnId: fillId,
|
|
2759
2500
|
selectionColumnId,
|
|
2760
2501
|
isResizing: Boolean(sizingInfo.isResizingColumn),
|
|
2761
2502
|
getCellTestId,
|
|
@@ -2979,63 +2720,247 @@ function resolveUpdater(updater, prev) {
|
|
|
2979
2720
|
}
|
|
2980
2721
|
//#endregion
|
|
2981
2722
|
//#region src/resource/list-error-state.tsx
|
|
2982
|
-
function ListErrorState(
|
|
2983
|
-
const
|
|
2984
|
-
|
|
2985
|
-
|
|
2986
|
-
|
|
2987
|
-
|
|
2988
|
-
|
|
2989
|
-
|
|
2990
|
-
|
|
2991
|
-
children: [
|
|
2992
|
-
jsx("div", {
|
|
2993
|
-
className: "rounded-full bg-destructive/10 p-3 text-destructive",
|
|
2994
|
-
children: jsx(IconAlertCircle, {
|
|
2995
|
-
className: "size-6",
|
|
2996
|
-
"aria-hidden": "true"
|
|
2997
|
-
})
|
|
2998
|
-
}),
|
|
2999
|
-
jsxs("div", {
|
|
3000
|
-
className: "space-y-1",
|
|
3001
|
-
children: [jsx("h3", {
|
|
3002
|
-
className: "text-base font-semibold tracking-tight text-destructive",
|
|
3003
|
-
children: title ?? t("list.errorTitle")
|
|
3004
|
-
}), jsx("p", {
|
|
3005
|
-
className: "max-w-md text-sm text-muted-foreground",
|
|
3006
|
-
children: friendlyError || t("list.errorBody")
|
|
3007
|
-
})]
|
|
3008
|
-
}),
|
|
3009
|
-
raw && raw !== friendlyError ? jsxs("details", {
|
|
3010
|
-
className: "max-w-md text-left",
|
|
3011
|
-
children: [jsx("summary", {
|
|
3012
|
-
className: "cursor-pointer text-xs text-muted-foreground hover:text-foreground",
|
|
3013
|
-
children: t("list.errorDetails")
|
|
3014
|
-
}), jsx("p", {
|
|
3015
|
-
className: "mt-1.5 break-all font-mono text-xs text-muted-foreground",
|
|
3016
|
-
"data-testid": "list-error-detail",
|
|
3017
|
-
children: raw
|
|
3018
|
-
})]
|
|
3019
|
-
}) : null,
|
|
3020
|
-
onRetry && jsx("div", {
|
|
3021
|
-
className: "flex justify-center pt-2",
|
|
3022
|
-
children: jsxs(Button, {
|
|
3023
|
-
type: "button",
|
|
3024
|
-
variant: "outline",
|
|
3025
|
-
size: "sm",
|
|
3026
|
-
"data-testid": resource ? `list-error-retry:${resource}` : "list-error-retry",
|
|
3027
|
-
onClick: onRetry,
|
|
3028
|
-
className: "gap-1.5",
|
|
3029
|
-
children: [jsx(IconRefresh, {
|
|
3030
|
-
className: "size-3.5",
|
|
3031
|
-
"aria-hidden": "true"
|
|
3032
|
-
}), t("actions.retry")]
|
|
3033
|
-
})
|
|
3034
|
-
})
|
|
3035
|
-
]
|
|
2723
|
+
function ListErrorState(props) {
|
|
2724
|
+
const { error, onRetry, resource, title, className } = props;
|
|
2725
|
+
return jsx(LoadError, {
|
|
2726
|
+
surface: "list",
|
|
2727
|
+
error,
|
|
2728
|
+
...onRetry === void 0 ? {} : { retry: onRetry },
|
|
2729
|
+
...resource === void 0 ? {} : { resource },
|
|
2730
|
+
...title === void 0 ? {} : { title },
|
|
2731
|
+
...className === void 0 ? {} : { className }
|
|
3036
2732
|
});
|
|
3037
2733
|
}
|
|
3038
2734
|
//#endregion
|
|
2735
|
+
//#region src/resource/use-table-layout.ts
|
|
2736
|
+
const WRITE_DEBOUNCE_MS = 200;
|
|
2737
|
+
function mutate(run, ok, then) {
|
|
2738
|
+
try {
|
|
2739
|
+
run().then((result) => {
|
|
2740
|
+
if (ok(result.status)) then();
|
|
2741
|
+
}, () => void 0);
|
|
2742
|
+
} catch {}
|
|
2743
|
+
}
|
|
2744
|
+
function resolve(updater, current) {
|
|
2745
|
+
return typeof updater === "function" ? updater(current) : updater;
|
|
2746
|
+
}
|
|
2747
|
+
function sameNames(a, b) {
|
|
2748
|
+
return a.length === b.length && a.every((name, index) => name === b[index]);
|
|
2749
|
+
}
|
|
2750
|
+
function readSizes(sizes, rule) {
|
|
2751
|
+
const out = {};
|
|
2752
|
+
for (const [column, size] of Object.entries(sizes)) {
|
|
2753
|
+
const of = rule(column);
|
|
2754
|
+
if (of === void 0 || !of.resize) continue;
|
|
2755
|
+
out[column] = Math.min(Math.max(size, of.min), of.max ?? Number.POSITIVE_INFINITY);
|
|
2756
|
+
}
|
|
2757
|
+
return out;
|
|
2758
|
+
}
|
|
2759
|
+
function useTableLayout({ resource, layoutId, rule, defaultPinning, resetLayout }) {
|
|
2760
|
+
const params = useMemo(() => layoutId === void 0 ? null : {
|
|
2761
|
+
resource,
|
|
2762
|
+
list: layoutId
|
|
2763
|
+
}, [resource, layoutId]);
|
|
2764
|
+
const settings = useOptionalUiSettings(tableLayoutSetting, params);
|
|
2765
|
+
const settingsRef = useRef(settings);
|
|
2766
|
+
settingsRef.current = settings;
|
|
2767
|
+
const key = layoutId === void 0 ? void 0 : `${resource}\u0000${layoutId}`;
|
|
2768
|
+
const fallback = useMemo(() => ({
|
|
2769
|
+
sizes: {},
|
|
2770
|
+
order: [],
|
|
2771
|
+
pinning: defaultPinning
|
|
2772
|
+
}), [defaultPinning]);
|
|
2773
|
+
const [local, setLocal] = useState();
|
|
2774
|
+
const stored = settings?.snapshot.entries[0]?.value;
|
|
2775
|
+
const settled = settings === void 0 || !settings.snapshot.pending;
|
|
2776
|
+
const held = useRef(null);
|
|
2777
|
+
const layout = (local !== void 0 && local.key === key ? local.value : void 0) ?? stored ?? fallback;
|
|
2778
|
+
const latest = useRef(layout);
|
|
2779
|
+
latest.current = layout;
|
|
2780
|
+
const sizing = useMemo(() => readSizes(layout.sizes, rule), [layout.sizes, rule]);
|
|
2781
|
+
const sizingRef = useRef(sizing);
|
|
2782
|
+
sizingRef.current = sizing;
|
|
2783
|
+
const generation = useRef(0);
|
|
2784
|
+
const timer = useRef(null);
|
|
2785
|
+
const pending = useRef(null);
|
|
2786
|
+
const confirm = useCallback((sent) => {
|
|
2787
|
+
if (generation.current === sent && pending.current === null) setLocal(void 0);
|
|
2788
|
+
}, []);
|
|
2789
|
+
const write = useCallback((target, value) => {
|
|
2790
|
+
const scopeId = target.snapshot.entries[0]?.scope.id ?? target.snapshot.writeScopes[0]?.id;
|
|
2791
|
+
if (scopeId === void 0) return;
|
|
2792
|
+
const sent = generation.current;
|
|
2793
|
+
mutate(() => target.set({
|
|
2794
|
+
scopeId,
|
|
2795
|
+
value
|
|
2796
|
+
}), (status) => status === "created" || status === "updated", () => confirm(sent));
|
|
2797
|
+
}, [confirm]);
|
|
2798
|
+
const store = useCallback((target, next) => {
|
|
2799
|
+
if (!(Object.keys(next.sizes).length === 0 && next.order.length === 0 && sameNames(next.pinning.left, defaultPinning.left) && sameNames(next.pinning.right, defaultPinning.right))) {
|
|
2800
|
+
write(target, next);
|
|
2801
|
+
return;
|
|
2802
|
+
}
|
|
2803
|
+
const entry = target.snapshot.entries[0];
|
|
2804
|
+
if (entry === void 0) {
|
|
2805
|
+
setLocal(void 0);
|
|
2806
|
+
return;
|
|
2807
|
+
}
|
|
2808
|
+
const sent = generation.current;
|
|
2809
|
+
mutate(() => target.remove({ id: entry.id }), (status) => status === "removed", () => confirm(sent));
|
|
2810
|
+
}, [
|
|
2811
|
+
defaultPinning,
|
|
2812
|
+
write,
|
|
2813
|
+
confirm
|
|
2814
|
+
]);
|
|
2815
|
+
const flush = useCallback(() => {
|
|
2816
|
+
if (timer.current !== null) clearTimeout(timer.current);
|
|
2817
|
+
timer.current = null;
|
|
2818
|
+
const due = pending.current;
|
|
2819
|
+
pending.current = null;
|
|
2820
|
+
if (due !== null) write(due.target, due.value);
|
|
2821
|
+
}, [write]);
|
|
2822
|
+
const change = useCallback((next, debounced, touch) => {
|
|
2823
|
+
latest.current = next;
|
|
2824
|
+
generation.current += 1;
|
|
2825
|
+
setLocal({
|
|
2826
|
+
key,
|
|
2827
|
+
value: next
|
|
2828
|
+
});
|
|
2829
|
+
const target = settingsRef.current;
|
|
2830
|
+
if (target === void 0) return;
|
|
2831
|
+
if (target.snapshot.pending || held.current !== null && held.current.key === key) {
|
|
2832
|
+
const edits = held.current !== null && held.current.key === key ? held.current : {
|
|
2833
|
+
key,
|
|
2834
|
+
resetAll: false,
|
|
2835
|
+
resetSizes: false,
|
|
2836
|
+
sizes: new Map()
|
|
2837
|
+
};
|
|
2838
|
+
held.current = edits;
|
|
2839
|
+
touch(edits);
|
|
2840
|
+
return;
|
|
2841
|
+
}
|
|
2842
|
+
pending.current = {
|
|
2843
|
+
target,
|
|
2844
|
+
value: next
|
|
2845
|
+
};
|
|
2846
|
+
if (!debounced) {
|
|
2847
|
+
flush();
|
|
2848
|
+
return;
|
|
2849
|
+
}
|
|
2850
|
+
if (timer.current !== null) clearTimeout(timer.current);
|
|
2851
|
+
timer.current = setTimeout(flush, WRITE_DEBOUNCE_MS);
|
|
2852
|
+
}, [key, flush]);
|
|
2853
|
+
useEffect(() => flush, [key]);
|
|
2854
|
+
useEffect(() => {
|
|
2855
|
+
const edits = held.current;
|
|
2856
|
+
if (edits === null) return;
|
|
2857
|
+
if (edits.key !== key) {
|
|
2858
|
+
held.current = null;
|
|
2859
|
+
return;
|
|
2860
|
+
}
|
|
2861
|
+
const target = settingsRef.current;
|
|
2862
|
+
if (!settled || target === void 0) return;
|
|
2863
|
+
held.current = null;
|
|
2864
|
+
const base = edits.resetAll ? fallback : stored ?? fallback;
|
|
2865
|
+
const sizes = edits.resetAll || edits.resetSizes ? {} : { ...readSizes(base.sizes, rule) };
|
|
2866
|
+
for (const [column, size] of edits.sizes) if (size === void 0) delete sizes[column];
|
|
2867
|
+
else sizes[column] = size;
|
|
2868
|
+
const next = {
|
|
2869
|
+
sizes,
|
|
2870
|
+
order: edits.order ?? base.order,
|
|
2871
|
+
pinning: edits.pinning ?? base.pinning
|
|
2872
|
+
};
|
|
2873
|
+
latest.current = next;
|
|
2874
|
+
generation.current += 1;
|
|
2875
|
+
setLocal({
|
|
2876
|
+
key,
|
|
2877
|
+
value: next
|
|
2878
|
+
});
|
|
2879
|
+
store(target, next);
|
|
2880
|
+
}, [settled, key]);
|
|
2881
|
+
const resetToken = resetLayout?.token ?? 0;
|
|
2882
|
+
const seenToken = useRef(resetToken);
|
|
2883
|
+
useEffect(() => {
|
|
2884
|
+
if (resetToken === seenToken.current) return;
|
|
2885
|
+
seenToken.current = resetToken;
|
|
2886
|
+
const all = resetLayout?.mode === "all";
|
|
2887
|
+
const next = all ? fallback : {
|
|
2888
|
+
...latest.current,
|
|
2889
|
+
sizes: {}
|
|
2890
|
+
};
|
|
2891
|
+
if (timer.current !== null) clearTimeout(timer.current);
|
|
2892
|
+
timer.current = null;
|
|
2893
|
+
pending.current = null;
|
|
2894
|
+
latest.current = next;
|
|
2895
|
+
generation.current += 1;
|
|
2896
|
+
setLocal({
|
|
2897
|
+
key,
|
|
2898
|
+
value: next
|
|
2899
|
+
});
|
|
2900
|
+
const target = settingsRef.current;
|
|
2901
|
+
if (target === void 0) return;
|
|
2902
|
+
if (target.snapshot.pending || held.current !== null && held.current.key === key) {
|
|
2903
|
+
const prior = held.current !== null && held.current.key === key ? held.current : void 0;
|
|
2904
|
+
held.current = all ? {
|
|
2905
|
+
key,
|
|
2906
|
+
resetAll: true,
|
|
2907
|
+
resetSizes: false,
|
|
2908
|
+
sizes: new Map()
|
|
2909
|
+
} : {
|
|
2910
|
+
key,
|
|
2911
|
+
resetAll: prior?.resetAll ?? false,
|
|
2912
|
+
resetSizes: true,
|
|
2913
|
+
sizes: new Map(),
|
|
2914
|
+
...prior?.order === void 0 ? {} : { order: prior.order },
|
|
2915
|
+
...prior?.pinning === void 0 ? {} : { pinning: prior.pinning }
|
|
2916
|
+
};
|
|
2917
|
+
return;
|
|
2918
|
+
}
|
|
2919
|
+
store(target, next);
|
|
2920
|
+
}, [resetToken]);
|
|
2921
|
+
const setColumnSizing = useCallback((updater) => {
|
|
2922
|
+
const before = sizingRef.current;
|
|
2923
|
+
const sizes = resolve(updater, before);
|
|
2924
|
+
sizingRef.current = sizes;
|
|
2925
|
+
change({
|
|
2926
|
+
...latest.current,
|
|
2927
|
+
sizes
|
|
2928
|
+
}, true, (edits) => {
|
|
2929
|
+
for (const column of new Set([...Object.keys(before), ...Object.keys(sizes)])) if (before[column] !== sizes[column]) edits.sizes.set(column, sizes[column]);
|
|
2930
|
+
});
|
|
2931
|
+
}, [change]);
|
|
2932
|
+
const setColumnOrder = useCallback((updater) => {
|
|
2933
|
+
const order = resolve(updater, latest.current.order);
|
|
2934
|
+
change({
|
|
2935
|
+
...latest.current,
|
|
2936
|
+
order
|
|
2937
|
+
}, false, (edits) => {
|
|
2938
|
+
edits.order = order;
|
|
2939
|
+
});
|
|
2940
|
+
}, [change]);
|
|
2941
|
+
const setColumnPinning = useCallback((updater) => {
|
|
2942
|
+
const resolved = resolve(updater, latest.current.pinning);
|
|
2943
|
+
const pinning = {
|
|
2944
|
+
left: resolved.left ?? [],
|
|
2945
|
+
right: resolved.right ?? []
|
|
2946
|
+
};
|
|
2947
|
+
change({
|
|
2948
|
+
...latest.current,
|
|
2949
|
+
pinning
|
|
2950
|
+
}, false, (edits) => {
|
|
2951
|
+
edits.pinning = pinning;
|
|
2952
|
+
});
|
|
2953
|
+
}, [change]);
|
|
2954
|
+
return {
|
|
2955
|
+
columnSizing: sizing,
|
|
2956
|
+
setColumnSizing,
|
|
2957
|
+
columnOrder: layout.order,
|
|
2958
|
+
setColumnOrder,
|
|
2959
|
+
columnPinning: layout.pinning,
|
|
2960
|
+
setColumnPinning
|
|
2961
|
+
};
|
|
2962
|
+
}
|
|
2963
|
+
//#endregion
|
|
3039
2964
|
//#region src/list-kit/cursor-load-more.tsx
|
|
3040
2965
|
function CursorLoadMore({ hasMore, loading, onMore }) {
|
|
3041
2966
|
const t = useT();
|
|
@@ -3077,6 +3002,10 @@ function CountRowsTrigger({ label, loading, onClick, testid, variant, className
|
|
|
3077
3002
|
}
|
|
3078
3003
|
const SELECT_COL_ID = "__select__";
|
|
3079
3004
|
const ACTIONS_COL_ID = "__actions__";
|
|
3005
|
+
const DEFAULT_PINNING = Object.freeze({
|
|
3006
|
+
left: Object.freeze([SELECT_COL_ID, "id"]),
|
|
3007
|
+
right: Object.freeze([ACTIONS_COL_ID])
|
|
3008
|
+
});
|
|
3080
3009
|
const ROW_ACTION_CONTROL_PX = 28;
|
|
3081
3010
|
const ROW_ACTION_GAP_PX = 4;
|
|
3082
3011
|
const ACTIONS_CELL_PADDING_PX = 16;
|
|
@@ -3088,29 +3017,30 @@ function actionsColumnSize(items) {
|
|
|
3088
3017
|
const strip = controls * ROW_ACTION_CONTROL_PX + (controls - 1) * ROW_ACTION_GAP_PX + ACTIONS_CELL_PADDING_PX;
|
|
3089
3018
|
return Math.max(ACTIONS_COL_MIN_PX, strip);
|
|
3090
3019
|
}
|
|
3091
|
-
const
|
|
3092
|
-
|
|
3093
|
-
|
|
3020
|
+
const ALIGN_CLASS = {
|
|
3021
|
+
start: "text-start",
|
|
3022
|
+
center: "text-center",
|
|
3023
|
+
end: "text-end"
|
|
3094
3024
|
};
|
|
3095
|
-
const
|
|
3096
|
-
|
|
3097
|
-
|
|
3098
|
-
|
|
3099
|
-
|
|
3100
|
-
|
|
3101
|
-
|
|
3102
|
-
|
|
3103
|
-
|
|
3104
|
-
}
|
|
3105
|
-
function ResourceList({ resource, data, total, page = 1, pageSize = 10, onPageSize, onRequestCount, isCountingRows = false, pageSizeOptions = [
|
|
3025
|
+
const STANDALONE_COLUMN_UI = Object.freeze({
|
|
3026
|
+
default: 160,
|
|
3027
|
+
min: 90,
|
|
3028
|
+
align: "start",
|
|
3029
|
+
resize: true,
|
|
3030
|
+
fill: false,
|
|
3031
|
+
display: Object.freeze({ params: Object.freeze({}) })
|
|
3032
|
+
});
|
|
3033
|
+
function ResourceList({ resource: resourceInput, data, total, page = 1, pageSize = 10, onPageSize, onRequestCount, isCountingRows = false, pageSizeOptions = [
|
|
3106
3034
|
10,
|
|
3107
3035
|
25,
|
|
3108
3036
|
50,
|
|
3109
3037
|
100
|
|
3110
|
-
], onPage, loadMore, selectable = false, rowActions = true, selectedIds, onSelectionChange, className, isLoading = false, error = null, onRetry, createHref, isFiltered = false, onClearFilter, emptyState, rowsView, rowHref, rowQuick, rowLinkKey, renderCell, sort, onSort, onHideColumn, requiredColumns, resetLayout, columns: columnsOverride, tableRef }) {
|
|
3038
|
+
], onPage, loadMore, selectable = false, rowActions = true, selectedIds, onSelectionChange, className, isLoading = false, error = null, onRetry, createHref, isFiltered = false, onClearFilter, emptyState, rowsView, rowHref, rowQuick, rowLinkKey, renderCell, sort, onSort, sortable, onHideColumn, requiredColumns, resetLayout, columns: columnsOverride, columnUi, layoutId, tableRef }) {
|
|
3039
|
+
const resource = routeResourceKey(resourceInput);
|
|
3111
3040
|
const app = useApp();
|
|
3112
3041
|
const tid = useTestIds();
|
|
3113
3042
|
const t = useOptionalT();
|
|
3043
|
+
const chromeT = useChromeT();
|
|
3114
3044
|
const locale = useLocale();
|
|
3115
3045
|
const nf = useMemo(() => new Intl.NumberFormat(locale), [locale]);
|
|
3116
3046
|
const i18nCaps = useI18nCaps(resource);
|
|
@@ -3132,23 +3062,6 @@ function ResourceList({ resource, data, total, page = 1, pageSize = 10, onPageSi
|
|
|
3132
3062
|
useEffect(() => {
|
|
3133
3063
|
emittedRef.current = null;
|
|
3134
3064
|
}, [controlledSelection]);
|
|
3135
|
-
const layoutKey = `buro:table-layout:${resource}`;
|
|
3136
|
-
const [columnSizing, setColumnSizing] = usePersistedState(`${layoutKey}:sizing`, {}, 200);
|
|
3137
|
-
const [columnOrder, setColumnOrder] = usePersistedState(`${layoutKey}:order`, []);
|
|
3138
|
-
const DEFAULT_PINNING = {
|
|
3139
|
-
left: [SELECT_COL_ID, "id"],
|
|
3140
|
-
right: [ACTIONS_COL_ID]
|
|
3141
|
-
};
|
|
3142
|
-
const [columnPinning, setColumnPinning] = usePersistedState(`${layoutKey}:pinning`, DEFAULT_PINNING);
|
|
3143
|
-
const resetToken = resetLayout?.token ?? 0;
|
|
3144
|
-
useEffect(() => {
|
|
3145
|
-
if (!resetToken) return;
|
|
3146
|
-
setColumnSizing({});
|
|
3147
|
-
if (resetLayout?.mode === "all") {
|
|
3148
|
-
setColumnOrder([]);
|
|
3149
|
-
setColumnPinning(DEFAULT_PINNING);
|
|
3150
|
-
}
|
|
3151
|
-
}, [resetToken]);
|
|
3152
3065
|
useEffect(() => {
|
|
3153
3066
|
if (controlled) return;
|
|
3154
3067
|
setOwnSelection({});
|
|
@@ -3158,7 +3071,7 @@ function ResourceList({ resource, data, total, page = 1, pageSize = 10, onPageSi
|
|
|
3158
3071
|
const accessPending = listAccess.status === "pending" || pathAccess.pending;
|
|
3159
3072
|
const declaredColumnKeys = useMemo(() => {
|
|
3160
3073
|
if (!def) return [];
|
|
3161
|
-
return [...columnsOverride ?? def.views?.list?.columns ?? Object.keys(def.fields)];
|
|
3074
|
+
return [...columnsOverride ?? def.views?.list?.columns?.map(columnName) ?? Object.keys(def.fields)];
|
|
3162
3075
|
}, [def, columnsOverride]);
|
|
3163
3076
|
const visibleColumnKeys = useMemo(() => accessPending ? [] : declaredColumnKeys.filter((col) => listAccess.columnVisible(col) && (columnTarget(app.registry, resource, col) === void 0 || listAccess.columnVisible(col.split(".")[0]) && pathAccess.visible(col))), [
|
|
3164
3077
|
accessPending,
|
|
@@ -3208,10 +3121,11 @@ function ResourceList({ resource, data, total, page = 1, pageSize = 10, onPageSi
|
|
|
3208
3121
|
const resourceLabel = resolveResourceLabel(i18nCaps, resource, "plural", def);
|
|
3209
3122
|
const recordSingular = resolveResourceLabel(i18nCaps, resource, "single", def);
|
|
3210
3123
|
const ariaLabel = resourceLabel;
|
|
3211
|
-
const actionsHeader =
|
|
3124
|
+
const actionsHeader = chromeT("actions.column", { defaultValue: "Actions" });
|
|
3212
3125
|
const viewRow = useViewRow(resource);
|
|
3213
3126
|
const volatileRef = useRef({
|
|
3214
3127
|
onSort,
|
|
3128
|
+
sortable,
|
|
3215
3129
|
onHideColumn,
|
|
3216
3130
|
requiredColumns,
|
|
3217
3131
|
renderCell,
|
|
@@ -3223,6 +3137,7 @@ function ResourceList({ resource, data, total, page = 1, pageSize = 10, onPageSi
|
|
|
3223
3137
|
});
|
|
3224
3138
|
volatileRef.current = {
|
|
3225
3139
|
onSort,
|
|
3140
|
+
sortable,
|
|
3226
3141
|
onHideColumn,
|
|
3227
3142
|
requiredColumns,
|
|
3228
3143
|
renderCell,
|
|
@@ -3246,6 +3161,45 @@ function ResourceList({ resource, data, total, page = 1, pageSize = 10, onPageSi
|
|
|
3246
3161
|
}, [rowLinkKey]);
|
|
3247
3162
|
const stableRowClassName = useCallback((row) => volatileRef.current.rowHref?.(volatileRef.current.viewRow(row.original)) || (volatileRef.current.rowQuick?.(row.original) ?? null) !== null ? "cursor-pointer" : void 0, [rowLinkKey]);
|
|
3248
3163
|
const [listRoot, narrow] = useContainerNarrow();
|
|
3164
|
+
const columnUiOf = useMemo(() => {
|
|
3165
|
+
const entries = new Map();
|
|
3166
|
+
for (const entry of [...def?.views?.list?.columns ?? [], ...def?.views?.list?.optionalColumns ?? []]) {
|
|
3167
|
+
const name = columnName(entry);
|
|
3168
|
+
if (!entries.has(name)) entries.set(name, entry);
|
|
3169
|
+
}
|
|
3170
|
+
return (col) => columnUi?.[col] ?? (def === void 0 ? STANDALONE_COLUMN_UI : resolveColumnUi(def, entries.get(col) ?? col, app.registry, columnTypeWidth));
|
|
3171
|
+
}, [
|
|
3172
|
+
app,
|
|
3173
|
+
def,
|
|
3174
|
+
columnUi
|
|
3175
|
+
]);
|
|
3176
|
+
const { columnSizing, setColumnSizing, columnOrder, setColumnOrder, columnPinning, setColumnPinning } = useTableLayout({
|
|
3177
|
+
resource,
|
|
3178
|
+
layoutId,
|
|
3179
|
+
rule: useMemo(() => {
|
|
3180
|
+
const known = new Set([
|
|
3181
|
+
...declaredColumnKeys,
|
|
3182
|
+
...(def?.views?.list?.columns ?? []).map(columnName),
|
|
3183
|
+
...(def?.views?.list?.optionalColumns ?? []).map(columnName),
|
|
3184
|
+
...Object.keys(def?.fields ?? {})
|
|
3185
|
+
]);
|
|
3186
|
+
return (col) => {
|
|
3187
|
+
if (!known.has(col)) return void 0;
|
|
3188
|
+
const ui = columnUiOf(col);
|
|
3189
|
+
return {
|
|
3190
|
+
min: ui.min,
|
|
3191
|
+
max: ui.max,
|
|
3192
|
+
resize: ui.resize
|
|
3193
|
+
};
|
|
3194
|
+
};
|
|
3195
|
+
}, [
|
|
3196
|
+
declaredColumnKeys,
|
|
3197
|
+
def,
|
|
3198
|
+
columnUiOf
|
|
3199
|
+
]),
|
|
3200
|
+
defaultPinning: DEFAULT_PINNING,
|
|
3201
|
+
resetLayout
|
|
3202
|
+
});
|
|
3249
3203
|
const tableColumns = useMemo(() => {
|
|
3250
3204
|
const cols = [];
|
|
3251
3205
|
if (selectable) cols.push({
|
|
@@ -3268,19 +3222,32 @@ function ResourceList({ resource, data, total, page = 1, pageSize = 10, onPageSi
|
|
|
3268
3222
|
});
|
|
3269
3223
|
for (const col of visibleColumnKeys) {
|
|
3270
3224
|
const target = columnTarget(app.registry, resource, col);
|
|
3271
|
-
const field = target
|
|
3225
|
+
const field = target !== void 0 ? target.field : def === void 0 ? void 0 : declaredField(def, col);
|
|
3272
3226
|
const declaredLabel = declaredText("field", field, "label");
|
|
3273
3227
|
const headerLabel = target === void 0 ? resolveFieldLabel(volatileRef.current.i18nCaps, col, declaredLabel) : resolveFieldLabel(capsOf(volatileRef.current.i18nCaps, target.resource), target.name, declaredLabel);
|
|
3274
|
-
const
|
|
3275
|
-
const
|
|
3276
|
-
|
|
3228
|
+
const ui = columnUiOf(col);
|
|
3229
|
+
const frameField = {
|
|
3230
|
+
...field ?? { type: "unknown" },
|
|
3231
|
+
name: target === void 0 ? col : target.name
|
|
3232
|
+
};
|
|
3233
|
+
const cellEntry = { display: ui.display };
|
|
3234
|
+
const wraps = ui.display.overflow === "wrap";
|
|
3235
|
+
const columnSortable = volatileRef.current.sortable?.(col) ?? (def !== void 0 && isColumnSortable(def, col, app, app.registry));
|
|
3277
3236
|
cols.push({
|
|
3278
3237
|
id: col,
|
|
3279
3238
|
...columnAccessor(col),
|
|
3280
|
-
|
|
3281
|
-
|
|
3239
|
+
size: ui.default,
|
|
3240
|
+
minSize: ui.min,
|
|
3241
|
+
maxSize: ui.max ?? Number.MAX_SAFE_INTEGER,
|
|
3242
|
+
enableResizing: ui.resize,
|
|
3243
|
+
enableSorting: columnSortable && Boolean(volatileRef.current.onSort),
|
|
3282
3244
|
enableHiding: true,
|
|
3283
|
-
meta: {
|
|
3245
|
+
meta: {
|
|
3246
|
+
testid: col,
|
|
3247
|
+
className: ALIGN_CLASS[ui.align],
|
|
3248
|
+
...wraps ? { wrap: true } : {},
|
|
3249
|
+
...ui.fill ? { fill: true } : {}
|
|
3250
|
+
},
|
|
3284
3251
|
header: ({ column, table }) => {
|
|
3285
3252
|
const v = volatileRef.current;
|
|
3286
3253
|
const pinSide = column.getIsPinned();
|
|
@@ -3311,9 +3278,9 @@ function ResourceList({ resource, data, total, page = 1, pageSize = 10, onPageSi
|
|
|
3311
3278
|
};
|
|
3312
3279
|
return jsx(ColumnHeaderMenu, {
|
|
3313
3280
|
label: headerLabel,
|
|
3314
|
-
sortable:
|
|
3281
|
+
sortable: columnSortable && Boolean(v.onSort),
|
|
3315
3282
|
sortDirection: v.sort?.[0]?.field === col ? v.sort[0].direction : null,
|
|
3316
|
-
onSort:
|
|
3283
|
+
onSort: columnSortable && v.onSort ? (dir) => v.onSort?.(col, dir) : void 0,
|
|
3317
3284
|
pinned: column.getIsPinned(),
|
|
3318
3285
|
onPin: (side) => column.pin(side),
|
|
3319
3286
|
canHide: Boolean(v.onHideColumn) && !v.requiredColumns?.includes(col),
|
|
@@ -3335,12 +3302,21 @@ function ResourceList({ resource, data, total, page = 1, pageSize = 10, onPageSi
|
|
|
3335
3302
|
field: field ?? { type: "unknown" },
|
|
3336
3303
|
value,
|
|
3337
3304
|
record,
|
|
3305
|
+
uiSurface: "column",
|
|
3306
|
+
columnEntry: cellEntry,
|
|
3338
3307
|
fallback: jsx("span", { children: String(value ?? "") })
|
|
3339
3308
|
});
|
|
3309
|
+
const framed = (node) => customCell !== void 0 ? node : jsx(FieldValueFrame, {
|
|
3310
|
+
field: frameField,
|
|
3311
|
+
value,
|
|
3312
|
+
surface: "column",
|
|
3313
|
+
entry: cellEntry,
|
|
3314
|
+
children: node
|
|
3315
|
+
});
|
|
3340
3316
|
const href = volatileRef.current.rowHref?.(record);
|
|
3341
3317
|
const isFirstContentCell = visibleColumnKeys.indexOf(col) === 0;
|
|
3342
3318
|
const quick = isFirstContentCell ? volatileRef.current.rowQuick?.(raw) ?? null : null;
|
|
3343
|
-
if (quick !== null) return href ? jsx(ClaimedQuickLink, {
|
|
3319
|
+
if (quick !== null) return framed(href ? jsx(ClaimedQuickLink, {
|
|
3344
3320
|
href,
|
|
3345
3321
|
className: relationLinkClassName,
|
|
3346
3322
|
onOpen: quick,
|
|
@@ -3354,13 +3330,13 @@ function ResourceList({ resource, data, total, page = 1, pageSize = 10, onPageSi
|
|
|
3354
3330
|
quick();
|
|
3355
3331
|
},
|
|
3356
3332
|
children: cellInner
|
|
3357
|
-
});
|
|
3358
|
-
return isFirstContentCell && href ? jsx(RouterLink, {
|
|
3333
|
+
}));
|
|
3334
|
+
return framed(isFirstContentCell && href ? jsx(RouterLink, {
|
|
3359
3335
|
href,
|
|
3360
3336
|
className: relationLinkClassName,
|
|
3361
3337
|
onClick: (e) => e.stopPropagation(),
|
|
3362
3338
|
children: cellInner
|
|
3363
|
-
}) : cellInner;
|
|
3339
|
+
}) : cellInner);
|
|
3364
3340
|
}
|
|
3365
3341
|
});
|
|
3366
3342
|
}
|
|
@@ -3369,7 +3345,7 @@ function ResourceList({ resource, data, total, page = 1, pageSize = 10, onPageSi
|
|
|
3369
3345
|
id: ACTIONS_COL_ID,
|
|
3370
3346
|
enableSorting: false,
|
|
3371
3347
|
enableResizing: false,
|
|
3372
|
-
size: narrow ? 44 : actionsColumnSize(def?.actions?.item),
|
|
3348
|
+
size: narrow || typeof rowActions === "function" ? 44 : actionsColumnSize(def?.actions?.item),
|
|
3373
3349
|
enableHiding: false,
|
|
3374
3350
|
header: () => jsx("span", {
|
|
3375
3351
|
className: "sr-only",
|
|
@@ -3380,7 +3356,7 @@ function ResourceList({ resource, data, total, page = 1, pageSize = 10, onPageSi
|
|
|
3380
3356
|
noRowNav: true,
|
|
3381
3357
|
noClip: true
|
|
3382
3358
|
},
|
|
3383
|
-
cell: ({ row }) => jsx(ActionOverflow, {
|
|
3359
|
+
cell: ({ row }) => typeof rowActions === "function" ? rowActions(row.original) : jsx(ActionOverflow, {
|
|
3384
3360
|
resource,
|
|
3385
3361
|
record: row.original,
|
|
3386
3362
|
surface: "row"
|
|
@@ -3400,7 +3376,8 @@ function ResourceList({ resource, data, total, page = 1, pageSize = 10, onPageSi
|
|
|
3400
3376
|
actionsHeader,
|
|
3401
3377
|
i18nCaps,
|
|
3402
3378
|
rowLinkKey,
|
|
3403
|
-
narrow
|
|
3379
|
+
narrow,
|
|
3380
|
+
columnUiOf
|
|
3404
3381
|
]);
|
|
3405
3382
|
const handleSelectionChange = (updater) => {
|
|
3406
3383
|
if (controlled) {
|
|
@@ -3503,10 +3480,25 @@ function ResourceList({ resource, data, total, page = 1, pageSize = 10, onPageSi
|
|
|
3503
3480
|
const isWrongPage = page > 1;
|
|
3504
3481
|
const EmptyIcon = isFiltered ? IconFilterX : IconInbox;
|
|
3505
3482
|
const items = resourceLabel.toLowerCase();
|
|
3506
|
-
const heading = isWrongPage ? t("pagination.emptyPage") : isFiltered ? t("list.emptyFilteredTitle", {
|
|
3507
|
-
|
|
3508
|
-
|
|
3509
|
-
|
|
3483
|
+
const heading = isWrongPage ? t("pagination.emptyPage") : isFiltered ? t("list.emptyFilteredTitle", {
|
|
3484
|
+
resource,
|
|
3485
|
+
params: { items }
|
|
3486
|
+
}) : t("list.emptyTitle", {
|
|
3487
|
+
resource,
|
|
3488
|
+
params: { items }
|
|
3489
|
+
});
|
|
3490
|
+
const body = isWrongPage ? null : isFiltered ? t("list.emptyFilteredBody", {
|
|
3491
|
+
resource,
|
|
3492
|
+
params: { items }
|
|
3493
|
+
}) : createHref ? t("list.emptyBody", {
|
|
3494
|
+
resource,
|
|
3495
|
+
params: { item: recordSingular.toLowerCase() }
|
|
3496
|
+
}) : t("list.emptyBodyReadOnly", {
|
|
3497
|
+
resource,
|
|
3498
|
+
params: { items }
|
|
3499
|
+
});
|
|
3500
|
+
return jsxs(Fragment$1, { children: [jsxs("div", {
|
|
3501
|
+
className: cn("flex flex-col items-center justify-center gap-3 px-6 py-16 rounded-lg border border-dashed text-center", className),
|
|
3510
3502
|
"data-testid": tid.listEmpty(resource),
|
|
3511
3503
|
children: [
|
|
3512
3504
|
jsx("div", {
|
|
@@ -3529,7 +3521,7 @@ function ResourceList({ resource, data, total, page = 1, pageSize = 10, onPageSi
|
|
|
3529
3521
|
jsxs("div", {
|
|
3530
3522
|
className: "flex justify-center gap-2 pt-2",
|
|
3531
3523
|
children: [
|
|
3532
|
-
|
|
3524
|
+
onClearFilter && jsx(Button, {
|
|
3533
3525
|
type: "button",
|
|
3534
3526
|
variant: "outline",
|
|
3535
3527
|
size: "sm",
|
|
@@ -3717,43 +3709,46 @@ function ResourceList({ resource, data, total, page = 1, pageSize = 10, onPageSi
|
|
|
3717
3709
|
})
|
|
3718
3710
|
});
|
|
3719
3711
|
}
|
|
3720
|
-
|
|
3721
|
-
|
|
3722
|
-
|
|
3723
|
-
|
|
3724
|
-
|
|
3725
|
-
|
|
3726
|
-
|
|
3727
|
-
|
|
3728
|
-
|
|
3712
|
+
//#endregion
|
|
3713
|
+
//#region src/blocks/related-list-unbound.tsx
|
|
3714
|
+
function UnboundRelatedList(props) {
|
|
3715
|
+
const t = useOptionalT();
|
|
3716
|
+
return jsxs("div", {
|
|
3717
|
+
className: "flex flex-col gap-2",
|
|
3718
|
+
"data-testid": `list-unbound:${props.resource}`,
|
|
3719
|
+
children: [props.title === void 0 ? null : jsx("h3", {
|
|
3720
|
+
className: "text-sm font-semibold",
|
|
3721
|
+
children: props.title
|
|
3722
|
+
}), jsx("p", {
|
|
3723
|
+
className: "text-sm text-muted-foreground",
|
|
3724
|
+
children: props.empty ?? t("table.empty", { defaultValue: "No records" })
|
|
3725
|
+
})]
|
|
3729
3726
|
});
|
|
3730
|
-
|
|
3731
|
-
|
|
3732
|
-
|
|
3733
|
-
|
|
3734
|
-
|
|
3735
|
-
|
|
3736
|
-
|
|
3737
|
-
|
|
3738
|
-
|
|
3739
|
-
|
|
3740
|
-
|
|
3741
|
-
|
|
3742
|
-
|
|
3743
|
-
|
|
3744
|
-
|
|
3745
|
-
|
|
3746
|
-
|
|
3747
|
-
|
|
3748
|
-
|
|
3749
|
-
|
|
3750
|
-
|
|
3751
|
-
|
|
3752
|
-
|
|
3753
|
-
|
|
3754
|
-
|
|
3755
|
-
};
|
|
3756
|
-
return [state, set];
|
|
3727
|
+
}
|
|
3728
|
+
function placementScope(scope, owner) {
|
|
3729
|
+
return scope ?? (owner === void 0 ? "session" : "none");
|
|
3730
|
+
}
|
|
3731
|
+
//#endregion
|
|
3732
|
+
//#region src/list-kit/pieces/bulk-reach.tsx
|
|
3733
|
+
function useBulkFlowReachable(resource) {
|
|
3734
|
+
const app = useApp();
|
|
3735
|
+
const control = useFlowRowResolver(resource);
|
|
3736
|
+
return (app.registry.get(resource)?.actions.bulk ?? []).some((action) => isFlowAction(action) && control(action.name, void 0).state !== "absent");
|
|
3737
|
+
}
|
|
3738
|
+
function ExportReach(props) {
|
|
3739
|
+
const { available } = useResourceExport(props.resource);
|
|
3740
|
+
const capability = useCapability("run", {
|
|
3741
|
+
resource: props.resource,
|
|
3742
|
+
target: {
|
|
3743
|
+
kind: "action",
|
|
3744
|
+
name: "export"
|
|
3745
|
+
},
|
|
3746
|
+
mode: "list"
|
|
3747
|
+
});
|
|
3748
|
+
return props.children(available && capability.allowed);
|
|
3749
|
+
}
|
|
3750
|
+
function BulkExportReach(props) {
|
|
3751
|
+
return useOptionalDataRuntime() !== null ? jsx(ExportReach, { ...props }) : props.children(false);
|
|
3757
3752
|
}
|
|
3758
3753
|
//#endregion
|
|
3759
3754
|
//#region src/list-kit/pieces/piece.tsx
|
|
@@ -3821,15 +3816,47 @@ function ForceTableView(props) {
|
|
|
3821
3816
|
}
|
|
3822
3817
|
//#endregion
|
|
3823
3818
|
//#region src/list-kit/pieces/list-body.tsx
|
|
3819
|
+
function LinkDetachRowButton(props) {
|
|
3820
|
+
const { resource, linkEditing } = useListSurface();
|
|
3821
|
+
const t = useOptionalT();
|
|
3822
|
+
const [busy, setBusy] = useState(false);
|
|
3823
|
+
const detach = linkEditing?.detach;
|
|
3824
|
+
if (detach === void 0) return null;
|
|
3825
|
+
const label = t("field.link.detach", { defaultValue: "Detach" });
|
|
3826
|
+
return jsx(Button, {
|
|
3827
|
+
type: "button",
|
|
3828
|
+
size: "icon",
|
|
3829
|
+
variant: "ghost",
|
|
3830
|
+
className: "size-7",
|
|
3831
|
+
"aria-label": label,
|
|
3832
|
+
title: label,
|
|
3833
|
+
disabled: busy,
|
|
3834
|
+
"data-testid": `list-link-detach:${resource}:${props.row.id}`,
|
|
3835
|
+
onClick: (event) => {
|
|
3836
|
+
event.stopPropagation();
|
|
3837
|
+
setBusy(true);
|
|
3838
|
+
detach.onDetach([props.row]).finally(() => setBusy(false));
|
|
3839
|
+
},
|
|
3840
|
+
children: jsx(IconUnlink, {
|
|
3841
|
+
className: "size-4",
|
|
3842
|
+
"aria-hidden": "true"
|
|
3843
|
+
})
|
|
3844
|
+
});
|
|
3845
|
+
}
|
|
3846
|
+
const linkDetachRow = (row) => jsx(LinkDetachRowButton, { row });
|
|
3824
3847
|
function ListBodyView() {
|
|
3825
3848
|
usePieceMount("body");
|
|
3826
3849
|
const list = useResourceListContext();
|
|
3827
|
-
const { resource, route, tableView, createAvailable, createPage, rowProps, rowsViewFor, emptyState, renderCell, readOnly } = useListSurface();
|
|
3850
|
+
const { resource, route, tableView, createAvailable, createPage, rowProps, rowsViewFor, emptyState, renderCell, readOnly, linkEditing, layoutId } = useListSurface();
|
|
3851
|
+
const detachable = readOnly && linkEditing?.detach !== void 0;
|
|
3852
|
+
const flowReachable = useBulkFlowReachable(resource);
|
|
3853
|
+
const selectable = (exportReachable) => tableView && (detachable || !readOnly && (flowReachable || exportReachable));
|
|
3828
3854
|
const item = useContext(ListItemContext);
|
|
3829
3855
|
const rowsView = useMemo(() => rowsViewFor?.(item), [rowsViewFor, item]);
|
|
3830
3856
|
const { query, pagination, cursor, count, columns, selection, filters, sorting, routing } = list;
|
|
3831
3857
|
const rows = policyRowsOf(list, list.rows);
|
|
3832
|
-
return jsx(
|
|
3858
|
+
if (query.unbound) return jsx(UnboundRelatedList, { resource });
|
|
3859
|
+
const view = (exportReachable) => jsx(ResourceList, {
|
|
3833
3860
|
resource,
|
|
3834
3861
|
data: rows,
|
|
3835
3862
|
total: pagination.total,
|
|
@@ -3846,29 +3873,50 @@ function ListBodyView() {
|
|
|
3846
3873
|
onRequestCount: count.available ? count.request : void 0,
|
|
3847
3874
|
isCountingRows: count.isLoading,
|
|
3848
3875
|
columns: columns.visible,
|
|
3876
|
+
columnUi: columns.ui,
|
|
3849
3877
|
isLoading: query.isLoading,
|
|
3850
3878
|
error: query.error,
|
|
3851
3879
|
onRetry: query.refetch,
|
|
3852
3880
|
createHref: route && createAvailable && createPage ? routing.createHref : void 0,
|
|
3853
3881
|
...rowProps,
|
|
3854
3882
|
...rowsView === void 0 ? {} : { rowsView },
|
|
3855
|
-
selectable:
|
|
3856
|
-
rowActions: !readOnly,
|
|
3883
|
+
selectable: selectable(exportReachable),
|
|
3884
|
+
rowActions: detachable ? linkDetachRow : !readOnly,
|
|
3857
3885
|
selectedIds: selection.ids,
|
|
3858
3886
|
onSelectionChange: selection.set,
|
|
3859
3887
|
isFiltered: filters.isFiltered,
|
|
3860
|
-
onClearFilter: filters.
|
|
3888
|
+
onClearFilter: filters.canReset ? filters.reset : void 0,
|
|
3861
3889
|
emptyState,
|
|
3862
3890
|
sort: sorting.value ?? [],
|
|
3863
3891
|
requiredColumns: columns.required,
|
|
3864
3892
|
resetLayout: columns.resetLayout,
|
|
3893
|
+
layoutId,
|
|
3865
3894
|
onHideColumn: columns.hide,
|
|
3866
3895
|
onSort: sorting.toggle,
|
|
3896
|
+
sortable: sorting.sortable,
|
|
3867
3897
|
renderCell
|
|
3868
3898
|
});
|
|
3899
|
+
return jsx(BulkExportReach, {
|
|
3900
|
+
resource,
|
|
3901
|
+
children: view
|
|
3902
|
+
});
|
|
3869
3903
|
}
|
|
3870
3904
|
const ListBody = listPiece("ListBody", ListBodyView);
|
|
3871
3905
|
//#endregion
|
|
3906
|
+
//#region src/resource/form-context.tsx
|
|
3907
|
+
const FormControlContext = createContext(null);
|
|
3908
|
+
const FormControlProvider = FormControlContext.Provider;
|
|
3909
|
+
function useFormControlOf(controller) {
|
|
3910
|
+
const latest = useRef(controller);
|
|
3911
|
+
latest.current = controller;
|
|
3912
|
+
return useMemo(() => ({ setValues: (patch) => {
|
|
3913
|
+
for (const [name, value] of Object.entries(patch)) latest.current.setValue(name, value);
|
|
3914
|
+
} }), []);
|
|
3915
|
+
}
|
|
3916
|
+
function useFormControl() {
|
|
3917
|
+
return useContext(FormControlContext);
|
|
3918
|
+
}
|
|
3919
|
+
//#endregion
|
|
3872
3920
|
//#region src/record/route-screen-context.ts
|
|
3873
3921
|
const RouteScreenClaimContext = createContext(false);
|
|
3874
3922
|
const RouteScreenContext = createContext(false);
|
|
@@ -3992,7 +4040,7 @@ function PanelHost({ node, region = "panel-body", renderChildren }) {
|
|
|
3992
4040
|
region,
|
|
3993
4041
|
form,
|
|
3994
4042
|
labelledBy: title === void 0 ? void 0 : titleId,
|
|
3995
|
-
children: renderChildren(node.children)
|
|
4043
|
+
children: renderChildren(node.children, node)
|
|
3996
4044
|
}) : null,
|
|
3997
4045
|
...title === void 0 ? {} : { title },
|
|
3998
4046
|
...description === void 0 ? {} : { description },
|
|
@@ -4027,10 +4075,12 @@ function TabsHost({ node, renderChildren }) {
|
|
|
4027
4075
|
const state = useScreenState();
|
|
4028
4076
|
const text = useNodeText();
|
|
4029
4077
|
const panels = node.children.filter((child) => child.state !== "absent");
|
|
4030
|
-
const
|
|
4078
|
+
const declared = compiled.nodes[node.index].options.default;
|
|
4079
|
+
const effectiveDefault = (declared === void 0 ? void 0 : panels.find((child) => child.key === declared)) ?? panels[0];
|
|
4080
|
+
const open = panels.find((child) => child.open === true) ?? effectiveDefault;
|
|
4031
4081
|
const urlSelect = useTabsUrlSelect();
|
|
4032
4082
|
const select = (panel, reveal) => {
|
|
4033
|
-
if (urlSelect !== null && node.key !== void 0 && panel.key !== void 0) urlSelect(node.key, panel.key, panel ===
|
|
4083
|
+
if (urlSelect !== null && node.key !== void 0 && panel.key !== void 0) urlSelect(node.key, panel.key, panel === effectiveDefault, reveal);
|
|
4034
4084
|
else state?.openPanel(node.id, panelValue(panel));
|
|
4035
4085
|
};
|
|
4036
4086
|
const onSelect = (key) => {
|
|
@@ -4042,7 +4092,7 @@ function TabsHost({ node, renderChildren }) {
|
|
|
4042
4092
|
key: panelValue(panel),
|
|
4043
4093
|
title: titleOf(panel),
|
|
4044
4094
|
errorCount: panel.key === void 0 ? 0 : controller.errorsIn(panel.key),
|
|
4045
|
-
panel: panels.length === 1 || panel.open === true ? jsx(Fragment$1, { children: renderChildren(panel.children) }) : null
|
|
4095
|
+
panel: panels.length === 1 || panel.open === true ? jsx(Fragment$1, { children: renderChildren(panel.children, panel) }) : null
|
|
4046
4096
|
}));
|
|
4047
4097
|
const { view: View, id } = useNodeView(node, "tabs");
|
|
4048
4098
|
const t = useFrameT(id);
|
|
@@ -4153,7 +4203,7 @@ function WizardHost({ node, renderChildren }) {
|
|
|
4153
4203
|
region: "step-panel",
|
|
4154
4204
|
form,
|
|
4155
4205
|
labelledBy: titleId,
|
|
4156
|
-
children: renderChildren(panel.children)
|
|
4206
|
+
children: renderChildren(panel.children, panel)
|
|
4157
4207
|
}),
|
|
4158
4208
|
...description === void 0 ? {} : { description },
|
|
4159
4209
|
titleId,
|
|
@@ -4199,18 +4249,23 @@ function WizardHost({ node, renderChildren }) {
|
|
|
4199
4249
|
//#region src/screen/nodes.tsx
|
|
4200
4250
|
function FailedNode({ node }) {
|
|
4201
4251
|
const { controller } = useScreenTree();
|
|
4202
|
-
|
|
4203
|
-
|
|
4204
|
-
|
|
4205
|
-
|
|
4206
|
-
|
|
4207
|
-
|
|
4208
|
-
|
|
4209
|
-
|
|
4210
|
-
|
|
4211
|
-
|
|
4212
|
-
|
|
4213
|
-
|
|
4252
|
+
let error;
|
|
4253
|
+
for (const name of node.failedOn) {
|
|
4254
|
+
const source = controller.sources[name];
|
|
4255
|
+
if (source?.status === "failed") {
|
|
4256
|
+
error = source.error;
|
|
4257
|
+
break;
|
|
4258
|
+
}
|
|
4259
|
+
}
|
|
4260
|
+
return jsx(LoadError, {
|
|
4261
|
+
surface: "tab",
|
|
4262
|
+
error,
|
|
4263
|
+
retry: () => controller.retry(node.failedOn),
|
|
4264
|
+
fallback: {
|
|
4265
|
+
key: "screen.failed",
|
|
4266
|
+
default: "This section could not be loaded."
|
|
4267
|
+
},
|
|
4268
|
+
attributes: nodeAttributes(node)
|
|
4214
4269
|
});
|
|
4215
4270
|
}
|
|
4216
4271
|
function failedOnlyOnMeta(node) {
|
|
@@ -4223,8 +4278,17 @@ function drawn(node, hiddenFields) {
|
|
|
4223
4278
|
if (node.state === "absent") return false;
|
|
4224
4279
|
return !(node.kind === "field" && node.field !== void 0 && hiddenFields?.has(node.field));
|
|
4225
4280
|
}
|
|
4226
|
-
function SectionChildren({ nodes }) {
|
|
4227
|
-
const { hiddenFields } = useScreenTree();
|
|
4281
|
+
function SectionChildren({ nodes, owner }) {
|
|
4282
|
+
const { compiled, hiddenFields } = useScreenTree();
|
|
4283
|
+
const text = useNodeText();
|
|
4284
|
+
if (owner !== void 0 && !nodes.some((node) => drawn(node, hiddenFields))) {
|
|
4285
|
+
const empty = text(compiled.nodes[owner.index]?.options.empty);
|
|
4286
|
+
return empty === void 0 || empty === "" ? null : jsx("p", {
|
|
4287
|
+
"data-buro-region": "empty",
|
|
4288
|
+
className: "text-sm text-muted-foreground",
|
|
4289
|
+
children: empty
|
|
4290
|
+
});
|
|
4291
|
+
}
|
|
4228
4292
|
const out = [];
|
|
4229
4293
|
let run = [];
|
|
4230
4294
|
const flush = () => {
|
|
@@ -4246,8 +4310,11 @@ function SectionChildren({ nodes }) {
|
|
|
4246
4310
|
flush();
|
|
4247
4311
|
return out;
|
|
4248
4312
|
}
|
|
4249
|
-
function renderSection(nodes) {
|
|
4250
|
-
return jsx(SectionChildren, {
|
|
4313
|
+
function renderSection(nodes, owner) {
|
|
4314
|
+
return jsx(SectionChildren, {
|
|
4315
|
+
nodes,
|
|
4316
|
+
owner
|
|
4317
|
+
});
|
|
4251
4318
|
}
|
|
4252
4319
|
function Layout({ node }) {
|
|
4253
4320
|
return jsx("div", {
|
|
@@ -4369,17 +4436,14 @@ function pageHost(page) {
|
|
|
4369
4436
|
}
|
|
4370
4437
|
//#endregion
|
|
4371
4438
|
//#region src/forms/declarative-form.tsx
|
|
4372
|
-
function
|
|
4373
|
-
const translated = t(key);
|
|
4374
|
-
return translated === key ? fallback : translated;
|
|
4375
|
-
}
|
|
4376
|
-
function issueText(issue, t, i18nPrefix) {
|
|
4439
|
+
function issueText(issue, caps, i18nPrefix) {
|
|
4377
4440
|
return resolveValidationMessage({
|
|
4378
4441
|
code: issue.code ?? "",
|
|
4379
4442
|
message: issue.message,
|
|
4380
|
-
...issue.
|
|
4443
|
+
...issue.check === void 0 ? {} : { check: issue.check }
|
|
4381
4444
|
}, {
|
|
4382
|
-
|
|
4445
|
+
has: caps.has,
|
|
4446
|
+
t: caps.t,
|
|
4383
4447
|
...issue.field === "_form" ? {} : { fieldKey: `resources.${i18nPrefix}.fields.${issue.field}` }
|
|
4384
4448
|
});
|
|
4385
4449
|
}
|
|
@@ -4446,7 +4510,9 @@ function DeclarativeFormBody(props) {
|
|
|
4446
4510
|
...props.surface === void 0 ? {} : { surface: props.surface },
|
|
4447
4511
|
...props.onSubmit === void 0 ? {} : { onSubmit: props.onSubmit }
|
|
4448
4512
|
});
|
|
4449
|
-
const
|
|
4513
|
+
const formControl = useFormControlOf(form);
|
|
4514
|
+
const chromeT = useChromeT();
|
|
4515
|
+
const caps = useI18nCaps();
|
|
4450
4516
|
const disabled = props.disabled === true;
|
|
4451
4517
|
const uid = useId();
|
|
4452
4518
|
const leafIds = useMemo(() => ({
|
|
@@ -4456,8 +4522,8 @@ function DeclarativeFormBody(props) {
|
|
|
4456
4522
|
const formErrors = form.errors.filter((issue) => issue.field === "_form");
|
|
4457
4523
|
const retryableChecks = [];
|
|
4458
4524
|
for (const issue of form.checkOutcome?.blockers ?? []) {
|
|
4459
|
-
const checkId = issue.
|
|
4460
|
-
if (issue.
|
|
4525
|
+
const checkId = issue.check?.id;
|
|
4526
|
+
if (issue.check?.cause === void 0 || typeof checkId !== "string") continue;
|
|
4461
4527
|
if (!retryableChecks.includes(checkId)) retryableChecks.push(checkId);
|
|
4462
4528
|
}
|
|
4463
4529
|
const formWarnings = form.warnings.filter((issue) => issue.field === "_form");
|
|
@@ -4470,7 +4536,7 @@ function DeclarativeFormBody(props) {
|
|
|
4470
4536
|
formErrors.length > 0 && jsx("div", {
|
|
4471
4537
|
role: "alert",
|
|
4472
4538
|
className: "mb-4 flex flex-col gap-1 rounded-md border border-destructive/30 bg-destructive/10 px-3 py-2 text-sm text-destructive",
|
|
4473
|
-
children: formErrors.map((issue, index) => jsx("p", { children: issueText(issue,
|
|
4539
|
+
children: formErrors.map((issue, index) => jsx("p", { children: issueText(issue, caps, prefix) }, index))
|
|
4474
4540
|
}),
|
|
4475
4541
|
retryableChecks.length > 0 && jsx("div", {
|
|
4476
4542
|
className: "mb-4 flex flex-wrap gap-2",
|
|
@@ -4480,25 +4546,28 @@ function DeclarativeFormBody(props) {
|
|
|
4480
4546
|
size: "sm",
|
|
4481
4547
|
"data-testid": `form-check-retry:${checkId}`,
|
|
4482
4548
|
onClick: () => void form.revalidate(),
|
|
4483
|
-
children:
|
|
4549
|
+
children: chromeT("actions.retry", { defaultValue: "Retry" })
|
|
4484
4550
|
}, checkId))
|
|
4485
4551
|
}),
|
|
4486
4552
|
formWarnings.length > 0 && jsx("div", {
|
|
4487
4553
|
role: "status",
|
|
4488
4554
|
"aria-live": "polite",
|
|
4489
4555
|
className: "mb-4 flex flex-col gap-1 rounded-md border border-warning/50 bg-warning/10 px-3 py-2 text-sm text-warning-emphasis",
|
|
4490
|
-
children: formWarnings.map((issue, index) => jsx("p", { children: issueText(issue,
|
|
4556
|
+
children: formWarnings.map((issue, index) => jsx("p", { children: issueText(issue, caps, prefix) }, index))
|
|
4491
4557
|
}),
|
|
4492
|
-
jsx(
|
|
4493
|
-
|
|
4494
|
-
|
|
4495
|
-
|
|
4496
|
-
|
|
4497
|
-
|
|
4498
|
-
|
|
4499
|
-
|
|
4500
|
-
|
|
4501
|
-
|
|
4558
|
+
jsx(FormControlProvider, {
|
|
4559
|
+
value: formControl,
|
|
4560
|
+
children: jsx(ScreenTree, {
|
|
4561
|
+
region: "form-body",
|
|
4562
|
+
resource: prefix,
|
|
4563
|
+
mode: "create",
|
|
4564
|
+
compiled: form.screen,
|
|
4565
|
+
controller: form,
|
|
4566
|
+
leafIds,
|
|
4567
|
+
...props.renderField === void 0 ? {} : { renderField: props.renderField },
|
|
4568
|
+
disabled,
|
|
4569
|
+
...props.bodyClassName === void 0 ? {} : { className: props.bodyClassName }
|
|
4570
|
+
})
|
|
4502
4571
|
}),
|
|
4503
4572
|
props.slots?.footer === void 0 ? jsx("div", {
|
|
4504
4573
|
className: "mt-4 flex justify-end",
|
|
@@ -4506,7 +4575,7 @@ function DeclarativeFormBody(props) {
|
|
|
4506
4575
|
type: "submit",
|
|
4507
4576
|
disabled: !form.canSubmit || disabled,
|
|
4508
4577
|
onMouseDown: (event) => event.preventDefault(),
|
|
4509
|
-
children: props.submitLabel ??
|
|
4578
|
+
children: props.submitLabel ?? chromeT("actions.save", { defaultValue: "Save" })
|
|
4510
4579
|
})
|
|
4511
4580
|
}) : typeof props.slots.footer === "function" ? props.slots.footer({ canSubmit: form.canSubmit && !disabled }) : props.slots.footer
|
|
4512
4581
|
]
|
|
@@ -4591,7 +4660,8 @@ function ExportOptionsDirty({ dirty }) {
|
|
|
4591
4660
|
return null;
|
|
4592
4661
|
}
|
|
4593
4662
|
function getNoRunState() {}
|
|
4594
|
-
function ExportDialog({ open, onOpenChange, resource, target, visibleColumns, estimatedCount, scope }) {
|
|
4663
|
+
function ExportDialog({ open, onOpenChange, resource: resourceInput, target, visibleColumns, estimatedCount, scope }) {
|
|
4664
|
+
const resource = routeResourceKey(resourceInput);
|
|
4595
4665
|
const t = useOptionalT();
|
|
4596
4666
|
const dialogTestId = useTestIds().exportDialog(resource);
|
|
4597
4667
|
const { formats, start, exporter, available, error, loading } = useResourceExport(resource, {
|
|
@@ -4713,14 +4783,15 @@ function ExportDialog({ open, onOpenChange, resource, target, visibleColumns, es
|
|
|
4713
4783
|
})] }), jsx(ExportProgressView, {
|
|
4714
4784
|
state: runState,
|
|
4715
4785
|
dialogTestId,
|
|
4716
|
-
t
|
|
4786
|
+
t,
|
|
4787
|
+
caps
|
|
4717
4788
|
})] }) : jsxs(Fragment$1, { children: [jsxs(DialogHeader, { children: [jsx(DialogTitle, { children: titleText }), jsx(DialogDescription, {
|
|
4718
4789
|
className: "sr-only",
|
|
4719
4790
|
children: titleText
|
|
4720
4791
|
})] }), !available ? jsx("p", {
|
|
4721
4792
|
role: "alert",
|
|
4722
4793
|
"data-testid": `${dialogTestId}:unavailable`,
|
|
4723
|
-
children: error
|
|
4794
|
+
children: error === void 0 || error === null ? t("export.dialog.unavailable") : resolveErrorText(error, caps)
|
|
4724
4795
|
}) : jsxs(Fragment$1, { children: [
|
|
4725
4796
|
loading ? jsx("p", {
|
|
4726
4797
|
role: "status",
|
|
@@ -4799,7 +4870,7 @@ function ExportDialog({ open, onOpenChange, resource, target, visibleColumns, es
|
|
|
4799
4870
|
})
|
|
4800
4871
|
});
|
|
4801
4872
|
}
|
|
4802
|
-
function ExportProgressView({ state, dialogTestId, t }) {
|
|
4873
|
+
function ExportProgressView({ state, dialogTestId, t, caps }) {
|
|
4803
4874
|
if (state === void 0) return null;
|
|
4804
4875
|
if (state.status === "running") {
|
|
4805
4876
|
const total = state.progress?.total;
|
|
@@ -4820,7 +4891,7 @@ function ExportProgressView({ state, dialogTestId, t }) {
|
|
|
4820
4891
|
if (state.status === "failed") return jsx("p", {
|
|
4821
4892
|
role: "alert",
|
|
4822
4893
|
"data-testid": `${dialogTestId}:error`,
|
|
4823
|
-
children: `${t("export.dialog.failed")}: ${state.error
|
|
4894
|
+
children: `${t("export.dialog.failed")}: ${resolveErrorText(state.error, caps)}`
|
|
4824
4895
|
});
|
|
4825
4896
|
if (state.status === "canceled") return jsx("p", {
|
|
4826
4897
|
"data-testid": `${dialogTestId}:canceled`,
|
|
@@ -4833,6 +4904,7 @@ function ExportProgressView({ state, dialogTestId, t }) {
|
|
|
4833
4904
|
function BulkSummary(props) {
|
|
4834
4905
|
const loaded = useLoaded(loadBulkSummary);
|
|
4835
4906
|
const t = useOptionalT();
|
|
4907
|
+
const chromeT = useChromeT();
|
|
4836
4908
|
const failed = (retry) => jsxs("div", {
|
|
4837
4909
|
role: "alert",
|
|
4838
4910
|
className: "flex items-center gap-2 text-sm",
|
|
@@ -4841,7 +4913,7 @@ function BulkSummary(props) {
|
|
|
4841
4913
|
size: "sm",
|
|
4842
4914
|
variant: "outline",
|
|
4843
4915
|
onClick: retry,
|
|
4844
|
-
children:
|
|
4916
|
+
children: chromeT("actions.retry", { defaultValue: "Retry" })
|
|
4845
4917
|
})]
|
|
4846
4918
|
});
|
|
4847
4919
|
if (loaded.error !== void 0) return failed(loaded.retry);
|
|
@@ -4859,13 +4931,14 @@ function BulkFlowButton({ resource, actionName, ids, onSelectionChange }) {
|
|
|
4859
4931
|
const control = useFlowRowResolver(resource)(actionName, void 0);
|
|
4860
4932
|
const chrome = useFlowChrome(resource, actionName);
|
|
4861
4933
|
const def = app.registry.get(resource);
|
|
4862
|
-
const
|
|
4934
|
+
const compiled = useMemo(() => {
|
|
4863
4935
|
try {
|
|
4864
|
-
return def === void 0 ?
|
|
4936
|
+
return def === void 0 ? void 0 : compileFlow(def, actionName);
|
|
4865
4937
|
} catch {
|
|
4866
|
-
return
|
|
4938
|
+
return;
|
|
4867
4939
|
}
|
|
4868
4940
|
}, [def, actionName]);
|
|
4941
|
+
const each = compiled?.each ?? false;
|
|
4869
4942
|
const [running, setRunning] = useState(false);
|
|
4870
4943
|
const [summary, setSummary] = useState(null);
|
|
4871
4944
|
const testid = tid.bulkAction(resource, actionName);
|
|
@@ -4873,7 +4946,10 @@ function BulkFlowButton({ resource, actionName, ids, onSelectionChange }) {
|
|
|
4873
4946
|
const variant = chrome.destructive ? "destructive" : "outline";
|
|
4874
4947
|
const overLimit = each && ids.length > BULK_EACH_LIMIT;
|
|
4875
4948
|
if (overLimit || control.state === "disabled") return jsx(DisabledAction, {
|
|
4876
|
-
reason: overLimit ?
|
|
4949
|
+
reason: overLimit ? t("flow.bulkLimit", {
|
|
4950
|
+
defaultValue: `Select at most ${BULK_EACH_LIMIT}.`,
|
|
4951
|
+
count: BULK_EACH_LIMIT
|
|
4952
|
+
}) : control.reason,
|
|
4877
4953
|
label: chrome.label,
|
|
4878
4954
|
icon: chrome.icon,
|
|
4879
4955
|
testid,
|
|
@@ -4935,14 +5011,15 @@ function BulkFlowButton({ resource, actionName, ids, onSelectionChange }) {
|
|
|
4935
5011
|
actionName,
|
|
4936
5012
|
label: chrome.labelText,
|
|
4937
5013
|
total: summary.total,
|
|
4938
|
-
progress: summary.progress
|
|
5014
|
+
progress: summary.progress,
|
|
5015
|
+
texts: compiled?.flow.texts
|
|
4939
5016
|
}),
|
|
4940
5017
|
jsx(DialogFooter, { children: jsx(Button, {
|
|
4941
5018
|
type: "button",
|
|
4942
5019
|
variant: "outline",
|
|
4943
5020
|
"data-testid": `${tid.bulkSummary(resource, actionName)}:close`,
|
|
4944
5021
|
onClick: () => setSummary(null),
|
|
4945
|
-
children:
|
|
5022
|
+
children: t("flow.close", { defaultValue: "Close" })
|
|
4946
5023
|
}) })
|
|
4947
5024
|
]
|
|
4948
5025
|
})
|
|
@@ -4958,10 +5035,12 @@ function orderBulkActions(actions) {
|
|
|
4958
5035
|
seq: index
|
|
4959
5036
|
})).sort((a, b) => a.weight - b.weight || a.seq - b.seq).map(({ action }) => action);
|
|
4960
5037
|
}
|
|
4961
|
-
function BulkActionToolbar({ resource, table, selectedRecords, selectedIds, onClearSelection, onSelectionChange, className, visibleColumns, scope }) {
|
|
5038
|
+
function BulkActionToolbar({ resource: resourceInput, table, selectedRecords, selectedIds, onClearSelection, onSelectionChange, className, visibleColumns, scope, actions }) {
|
|
5039
|
+
const resource = routeResourceKey(resourceInput);
|
|
4962
5040
|
const app = useApp();
|
|
4963
5041
|
const tid = useTestIds();
|
|
4964
5042
|
const t = useOptionalT();
|
|
5043
|
+
const chromeT = useChromeT();
|
|
4965
5044
|
const def = app.registry.get(resource);
|
|
4966
5045
|
const records = selectedRecords ?? (table ? table.getFilteredSelectedRowModel().rows.map((r) => r.original) : []);
|
|
4967
5046
|
const ids = selectedIds ?? records.map((record) => record.id);
|
|
@@ -4994,7 +5073,8 @@ function BulkActionToolbar({ resource, table, selectedRecords, selectedIds, onCl
|
|
|
4994
5073
|
className: "mx-0.5 h-6 w-px bg-border",
|
|
4995
5074
|
"aria-hidden": "true"
|
|
4996
5075
|
}),
|
|
4997
|
-
|
|
5076
|
+
actions !== void 0 ? actions : null,
|
|
5077
|
+
actions === void 0 && bulkActions.map((action) => jsx(BulkFlowButton, {
|
|
4998
5078
|
resource,
|
|
4999
5079
|
actionName: action.name,
|
|
5000
5080
|
ids,
|
|
@@ -5003,7 +5083,7 @@ function BulkActionToolbar({ resource, table, selectedRecords, selectedIds, onCl
|
|
|
5003
5083
|
else if (next.length === 0) handleClear();
|
|
5004
5084
|
}
|
|
5005
5085
|
}, action.name)),
|
|
5006
|
-
jsx(BulkExportButton, {
|
|
5086
|
+
actions === void 0 && jsx(BulkExportButton, {
|
|
5007
5087
|
resource,
|
|
5008
5088
|
ids,
|
|
5009
5089
|
visibleColumns: visibleColumns ?? [],
|
|
@@ -5020,7 +5100,7 @@ function BulkActionToolbar({ resource, table, selectedRecords, selectedIds, onCl
|
|
|
5020
5100
|
"aria-hidden": "true"
|
|
5021
5101
|
}), jsx("span", {
|
|
5022
5102
|
className: "hidden sm:inline",
|
|
5023
|
-
children:
|
|
5103
|
+
children: chromeT("actions.clearSelection", { defaultValue: "Clear" })
|
|
5024
5104
|
})]
|
|
5025
5105
|
})
|
|
5026
5106
|
]
|
|
@@ -5080,12 +5160,36 @@ function BulkExportButtonActive({ resource, ids, visibleColumns, scope }) {
|
|
|
5080
5160
|
}
|
|
5081
5161
|
//#endregion
|
|
5082
5162
|
//#region src/list-kit/pieces/list-bulk-bar.tsx
|
|
5163
|
+
function LinkDetachBulkButton(props) {
|
|
5164
|
+
const { selection } = useResourceListContext();
|
|
5165
|
+
const { resource } = useListSurface();
|
|
5166
|
+
const t = useOptionalT();
|
|
5167
|
+
const [busy, setBusy] = useState(false);
|
|
5168
|
+
return jsxs(Button, {
|
|
5169
|
+
type: "button",
|
|
5170
|
+
size: "sm",
|
|
5171
|
+
variant: "ghost",
|
|
5172
|
+
className: "h-8 gap-1.5 rounded-full px-2.5 text-destructive",
|
|
5173
|
+
disabled: busy,
|
|
5174
|
+
"data-testid": `list-link-detach-bulk:${resource}`,
|
|
5175
|
+
onClick: () => {
|
|
5176
|
+
setBusy(true);
|
|
5177
|
+
props.onDetach(selection.records).finally(() => setBusy(false));
|
|
5178
|
+
},
|
|
5179
|
+
children: [jsx(IconUnlink, {
|
|
5180
|
+
className: "size-4",
|
|
5181
|
+
"aria-hidden": "true"
|
|
5182
|
+
}), t("field.link.detach", { defaultValue: "Detach" })]
|
|
5183
|
+
});
|
|
5184
|
+
}
|
|
5083
5185
|
function ListBulkBarView() {
|
|
5084
5186
|
usePieceMount("bulk-bar");
|
|
5085
5187
|
const { selection, columns, scope } = useResourceListContext();
|
|
5086
|
-
const { resource, tableView, readOnly } = useListSurface();
|
|
5087
|
-
|
|
5188
|
+
const { resource, tableView, readOnly, linkEditing } = useListSurface();
|
|
5189
|
+
const detach = readOnly ? linkEditing?.detach : void 0;
|
|
5190
|
+
if (!tableView || readOnly && detach === void 0 || selection.ids.length === 0) return null;
|
|
5088
5191
|
return jsx(BulkActionToolbar, {
|
|
5192
|
+
...detach === void 0 ? {} : { actions: jsx(LinkDetachBulkButton, { onDetach: detach.onDetach }) },
|
|
5089
5193
|
resource,
|
|
5090
5194
|
selectedRecords: selection.records,
|
|
5091
5195
|
selectedIds: selection.ids,
|
|
@@ -5174,7 +5278,8 @@ function DataTableViewOptions({ table, getColumnLabel, isColumnRequired, getTest
|
|
|
5174
5278
|
}
|
|
5175
5279
|
//#endregion
|
|
5176
5280
|
//#region src/list-kit/columns-toggle.tsx
|
|
5177
|
-
function ColumnsToggle({ resource, def, allAvailable, required, visibleColumns, defaultVisible, onChange, onResetSizes, onRestoreDefaults }) {
|
|
5281
|
+
function ColumnsToggle({ resource: resourceInput, def, allAvailable, required, visibleColumns, defaultVisible, onChange, onResetSizes, onRestoreDefaults }) {
|
|
5282
|
+
const resource = routeResourceKey(resourceInput);
|
|
5178
5283
|
const t = useT();
|
|
5179
5284
|
const columnsCaps = useI18nCaps(resource);
|
|
5180
5285
|
const app = useApp();
|
|
@@ -5281,12 +5386,16 @@ function ListColumnsView() {
|
|
|
5281
5386
|
const ListColumns = listPiece("ListColumns", ListColumnsView);
|
|
5282
5387
|
//#endregion
|
|
5283
5388
|
//#region src/resource/create-button.tsx
|
|
5284
|
-
function CreateButton({ resource, href, testid, size = "sm", variant, label, className, onOpen }) {
|
|
5389
|
+
function CreateButton({ resource: resourceInput, href, testid, size = "sm", variant, label, className, onOpen }) {
|
|
5390
|
+
const resource = routeResourceKey(resourceInput);
|
|
5285
5391
|
const t = useOptionalT();
|
|
5286
5392
|
const Link = useLinkComponent();
|
|
5287
5393
|
const availability = useAvailability(resource, "create");
|
|
5288
5394
|
const gate = useEntryGate();
|
|
5289
|
-
const text = label ?? t("actions.create", {
|
|
5395
|
+
const text = label ?? t("actions.create", {
|
|
5396
|
+
resource,
|
|
5397
|
+
defaultValue: "Create"
|
|
5398
|
+
});
|
|
5290
5399
|
if (onOpen !== void 0) {
|
|
5291
5400
|
const answer = gate(resource, "create");
|
|
5292
5401
|
if (answer.state === "disabled") return jsx(DisabledAction, {
|
|
@@ -5357,7 +5466,8 @@ const ListCreate = listPiece("ListCreate", ListCreateView);
|
|
|
5357
5466
|
//#endregion
|
|
5358
5467
|
//#region src/export/export-button.tsx
|
|
5359
5468
|
const warnedProviderQueryResources = new Set();
|
|
5360
|
-
function ExportButton({ resource, className }) {
|
|
5469
|
+
function ExportButton({ resource: resourceInput, className }) {
|
|
5470
|
+
const resource = routeResourceKey(resourceInput);
|
|
5361
5471
|
if (!(useOptionalDataRuntime() !== null)) return null;
|
|
5362
5472
|
return jsx(ExportButtonActive, {
|
|
5363
5473
|
resource,
|
|
@@ -5378,36 +5488,19 @@ function ExportButtonActive({ resource, className }) {
|
|
|
5378
5488
|
const tid = useTestIds();
|
|
5379
5489
|
const t = useOptionalT();
|
|
5380
5490
|
const [open, setOpen] = React.useState(false);
|
|
5381
|
-
const
|
|
5382
|
-
resource,
|
|
5383
|
-
def: list.def
|
|
5384
|
-
}), [resource, list.def]);
|
|
5491
|
+
const sent = list.query.sent;
|
|
5385
5492
|
const target = React.useMemo(() => {
|
|
5386
|
-
|
|
5387
|
-
|
|
5388
|
-
pinned: list.filters.value,
|
|
5389
|
-
group: list.filters.group,
|
|
5390
|
-
search: list.filters.search,
|
|
5391
|
-
searchDecl: list.filters.searchEnabled ? list.filters.searchDecl ?? list.def?.views?.list?.search : void 0,
|
|
5392
|
-
searchContext
|
|
5393
|
-
});
|
|
5394
|
-
const sort = list.sorting.value;
|
|
5493
|
+
if (sent === null) return null;
|
|
5494
|
+
const { filter, sort } = sent;
|
|
5395
5495
|
return {
|
|
5396
5496
|
kind: "filter",
|
|
5397
5497
|
...filter === void 0 ? {} : { filter },
|
|
5398
5498
|
...sort.length > 0 ? { sort } : {}
|
|
5399
5499
|
};
|
|
5400
|
-
}, [
|
|
5401
|
-
|
|
5402
|
-
|
|
5403
|
-
|
|
5404
|
-
list.filters.search,
|
|
5405
|
-
list.filters.searchDecl,
|
|
5406
|
-
list.filters.searchEnabled,
|
|
5407
|
-
list.def,
|
|
5408
|
-
searchContext,
|
|
5409
|
-
list.sorting.value
|
|
5410
|
-
]);
|
|
5500
|
+
}, [sent]);
|
|
5501
|
+
const lastTarget = React.useRef(target);
|
|
5502
|
+
if (target !== null) lastTarget.current = target;
|
|
5503
|
+
const dialogTarget = target ?? lastTarget.current;
|
|
5411
5504
|
React.useEffect(() => {
|
|
5412
5505
|
if (!available || !capability.allowed) return;
|
|
5413
5506
|
if (list.providerQuery.value === void 0) return;
|
|
@@ -5421,7 +5514,9 @@ function ExportButtonActive({ resource, className }) {
|
|
|
5421
5514
|
resource
|
|
5422
5515
|
]);
|
|
5423
5516
|
if (!available || !capability.allowed || list.isForbidden) return null;
|
|
5424
|
-
|
|
5517
|
+
if (target === null && !open) return null;
|
|
5518
|
+
if (dialogTarget === null) return null;
|
|
5519
|
+
return jsxs(Fragment$1, { children: [target === null ? null : jsx(Button, {
|
|
5425
5520
|
type: "button",
|
|
5426
5521
|
variant: "outline",
|
|
5427
5522
|
size: "icon",
|
|
@@ -5440,7 +5535,7 @@ function ExportButtonActive({ resource, className }) {
|
|
|
5440
5535
|
open,
|
|
5441
5536
|
onOpenChange: setOpen,
|
|
5442
5537
|
resource,
|
|
5443
|
-
target,
|
|
5538
|
+
target: dialogTarget,
|
|
5444
5539
|
visibleColumns: list.columns.visible,
|
|
5445
5540
|
estimatedCount: list.pagination.total,
|
|
5446
5541
|
scope: list.scope
|
|
@@ -5458,8 +5553,9 @@ function ListExportView() {
|
|
|
5458
5553
|
const ListExport = listPiece("ListExport", ListExportView);
|
|
5459
5554
|
//#endregion
|
|
5460
5555
|
//#region src/list-kit/list-search-input.tsx
|
|
5461
|
-
function ListSearchInput({ resource
|
|
5462
|
-
const
|
|
5556
|
+
function ListSearchInput({ resource: resourceInput, value, onCommit, placeholder }) {
|
|
5557
|
+
const resource = routeResourceKey(resourceInput);
|
|
5558
|
+
const caps = useI18nCaps(resource);
|
|
5463
5559
|
const [text, setText] = useState(value);
|
|
5464
5560
|
const lastPushedRef = useRef(value);
|
|
5465
5561
|
const timerRef = useRef(null);
|
|
@@ -5474,7 +5570,7 @@ function ListSearchInput({ resource, label, value, onCommit, placeholder }) {
|
|
|
5474
5570
|
}, []);
|
|
5475
5571
|
return jsx(Input, {
|
|
5476
5572
|
type: "search",
|
|
5477
|
-
placeholder: placeholder ??
|
|
5573
|
+
placeholder: placeholder ?? resolveContentSlot(caps, ["list.search"]) ?? "Search",
|
|
5478
5574
|
value: text,
|
|
5479
5575
|
"data-testid": `list-search:${resource}`,
|
|
5480
5576
|
className: "h-8 w-full min-[420px]:w-72",
|
|
@@ -5492,24 +5588,18 @@ function ListSearchInput({ resource, label, value, onCommit, placeholder }) {
|
|
|
5492
5588
|
//#endregion
|
|
5493
5589
|
//#region src/list-kit/filter-builder/filter-region.tsx
|
|
5494
5590
|
function FilterRegion({ search } = {}) {
|
|
5495
|
-
const { resource, def, filters, pagination
|
|
5496
|
-
const
|
|
5497
|
-
const listCaps = useI18nCaps(resource);
|
|
5591
|
+
const { resource, def, filters, pagination } = useResourceListContext();
|
|
5592
|
+
const chromeT = useChromeT();
|
|
5498
5593
|
const testIdBase = `list-filter:${resource}`;
|
|
5594
|
+
const caps = useI18nCaps(resource);
|
|
5595
|
+
const placeholder = search === void 0 ? resolveContentSlot(caps, ["list.searchPlaceholder", "list.search"]) ?? "Search" : void 0;
|
|
5499
5596
|
const searchDecl = search === void 0 ? def?.views?.list?.search : search ?? void 0;
|
|
5500
|
-
const searchField = searchDecl !== void 0 && typeof searchDecl !== "function" ? (() => {
|
|
5501
|
-
return searchDecl.fields.map((entry) => typeof entry === "string" ? entry : entry.field).find((name) => access.fieldVisible(name));
|
|
5502
|
-
})() : void 0;
|
|
5503
|
-
const searchGenericLabel = resolveContentSlot(listCaps, ["list.search"]) ?? "Search";
|
|
5504
|
-
const searchLabel = searchField ? resolveFieldLabel(listCaps, searchField, declaredText("field", def?.fields[searchField], "label")) : searchGenericLabel;
|
|
5505
|
-
const searchPlaceholder = searchField ? t("toolbar.searchBy", { params: { name: searchLabel } }) : searchGenericLabel;
|
|
5506
5597
|
return jsxs("div", {
|
|
5507
5598
|
className: "flex flex-wrap items-center gap-2",
|
|
5508
5599
|
children: [
|
|
5509
5600
|
searchDecl !== void 0 && filters.searchEnabled && jsx(ListSearchInput, {
|
|
5510
5601
|
resource,
|
|
5511
|
-
|
|
5512
|
-
placeholder: searchPlaceholder,
|
|
5602
|
+
...placeholder === void 0 ? {} : { placeholder },
|
|
5513
5603
|
value: filters.search,
|
|
5514
5604
|
onCommit: (v) => {
|
|
5515
5605
|
pagination.setPage(1);
|
|
@@ -5521,19 +5611,21 @@ function FilterRegion({ search } = {}) {
|
|
|
5521
5611
|
setGroup: filters.setGroup,
|
|
5522
5612
|
available: filters.available,
|
|
5523
5613
|
fixed: filters.fixed,
|
|
5614
|
+
presets: filters.presets,
|
|
5615
|
+
setPreset: filters.setPreset,
|
|
5524
5616
|
pinnedFields: filters.pinnedFields,
|
|
5525
5617
|
pinnedValue: filters.value,
|
|
5526
5618
|
setPinnedValue: filters.set,
|
|
5527
5619
|
setPage: pagination.setPage,
|
|
5528
5620
|
testIdBase
|
|
5529
5621
|
}),
|
|
5530
|
-
filters.
|
|
5622
|
+
filters.canReset && jsx(Button, {
|
|
5531
5623
|
type: "button",
|
|
5532
5624
|
variant: "ghost",
|
|
5533
5625
|
size: "sm",
|
|
5534
5626
|
"data-testid": `${testIdBase}:clear-all`,
|
|
5535
|
-
onClick: filters.
|
|
5536
|
-
children:
|
|
5627
|
+
onClick: filters.reset,
|
|
5628
|
+
children: chromeT("filters.clearAll")
|
|
5537
5629
|
})
|
|
5538
5630
|
]
|
|
5539
5631
|
});
|
|
@@ -5663,6 +5755,7 @@ function ViewItem({ view, caps, resource, onClick, testid }) {
|
|
|
5663
5755
|
});
|
|
5664
5756
|
}
|
|
5665
5757
|
function EntryRow({ entry, onApply, onRename, onFork, onDelete, forkAvailable, applyTestId, renameTestId, forkTestId, deleteTestId, tr }) {
|
|
5758
|
+
const chromeT = useChromeT();
|
|
5666
5759
|
const canRename = entry.permissions.update && onRename !== void 0;
|
|
5667
5760
|
const canFork = forkAvailable && entry.permissions.fork && onFork !== void 0;
|
|
5668
5761
|
const canDelete = entry.permissions.remove && onDelete !== void 0;
|
|
@@ -5708,8 +5801,8 @@ function EntryRow({ entry, onApply, onRename, onFork, onDelete, forkAvailable, a
|
|
|
5708
5801
|
canDelete && jsx("button", {
|
|
5709
5802
|
type: "button",
|
|
5710
5803
|
onClick: onDelete,
|
|
5711
|
-
"aria-label":
|
|
5712
|
-
title:
|
|
5804
|
+
"aria-label": chromeT("actions.delete", { defaultValue: "Delete" }),
|
|
5805
|
+
title: chromeT("actions.delete", { defaultValue: "Delete" }),
|
|
5713
5806
|
"data-testid": deleteTestId,
|
|
5714
5807
|
className: "shrink-0 rounded-sm p-1 text-muted-foreground hover:bg-accent hover:text-accent-foreground focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring",
|
|
5715
5808
|
children: jsx(IconTrash, {
|
|
@@ -5749,7 +5842,8 @@ function groupByScope(entries) {
|
|
|
5749
5842
|
}
|
|
5750
5843
|
return order.map((id) => byId.get(id));
|
|
5751
5844
|
}
|
|
5752
|
-
function SavedViewsDropdown({ resource, adopterViews, entries, issues = [], onApply, onSaveCurrent, canSaveCurrent = true, onManage, onRenameEntry, onDeleteEntry, onForkEntry, canFork = true, getTestId, triggerLabel = "Views", iconOnly = false, triggerClassName = "h-9", popoverContentClassName = "w-64 p-1", popoverAlign = "start" }) {
|
|
5845
|
+
function SavedViewsDropdown({ resource: resourceInput, adopterViews, entries, issues = [], onApply, onSaveCurrent, canSaveCurrent = true, onManage, onRenameEntry, onDeleteEntry, onForkEntry, canFork = true, getTestId, triggerLabel = "Views", iconOnly = false, triggerClassName = "h-9", popoverContentClassName = "w-64 p-1", popoverAlign = "start" }) {
|
|
5846
|
+
const resource = resourceInput === void 0 ? void 0 : routeResourceKey(resourceInput);
|
|
5753
5847
|
const t = useOptionalT();
|
|
5754
5848
|
const caps = useI18nCaps(resource);
|
|
5755
5849
|
const tr = (key, fallback) => t?.(key, { defaultValue: fallback }) ?? fallback;
|
|
@@ -5963,9 +6057,10 @@ function useOptionalFrameChrome() {
|
|
|
5963
6057
|
//#endregion
|
|
5964
6058
|
//#region src/record/frame-pieces.tsx
|
|
5965
6059
|
function useFrameId() {
|
|
5966
|
-
const { recordId, record } = useRecordFrame();
|
|
6060
|
+
const { resource, recordId, record } = useRecordFrame();
|
|
6061
|
+
const def = useApp().registry.get(resource);
|
|
5967
6062
|
if (recordId !== void 0) return recordId;
|
|
5968
|
-
const id = record
|
|
6063
|
+
const id = recordKeyOf(def, record);
|
|
5969
6064
|
return typeof id === "string" || typeof id === "number" ? String(id) : void 0;
|
|
5970
6065
|
}
|
|
5971
6066
|
function RecordTitleView() {
|
|
@@ -5976,7 +6071,7 @@ function RecordTitleView() {
|
|
|
5976
6071
|
const t = useOptionalT();
|
|
5977
6072
|
const title = useRecordTitle(resource, mode === "create" ? void 0 : id, record);
|
|
5978
6073
|
if (useFrameChrome().hostTitle === true) return null;
|
|
5979
|
-
const text = mode === "create" ? t
|
|
6074
|
+
const text = mode === "create" ? resolveResourceTitle(caps, t, resource, "createTitle", { label: resolveResourceLabel(caps, resource, "single", app.registry.get(resource)) }) : title;
|
|
5980
6075
|
if (text === "") return null;
|
|
5981
6076
|
return jsx("h2", {
|
|
5982
6077
|
className: "min-w-0 break-words text-xl font-semibold tracking-tight",
|
|
@@ -6000,6 +6095,7 @@ function RecordActionsView() {
|
|
|
6000
6095
|
resource,
|
|
6001
6096
|
record: row,
|
|
6002
6097
|
surface: mode,
|
|
6098
|
+
sources: controller.sources,
|
|
6003
6099
|
...holder === void 0 ? {} : { hideFlows: (name) => holder.suppressed(name) }
|
|
6004
6100
|
}), mode === "show" ? extraActions : null]
|
|
6005
6101
|
});
|
|
@@ -6029,8 +6125,15 @@ function FormActionsView() {
|
|
|
6029
6125
|
const app = useApp();
|
|
6030
6126
|
const navigate = useRouteNavigate();
|
|
6031
6127
|
const t = useOptionalT();
|
|
6128
|
+
const chromeT = useChromeT();
|
|
6129
|
+
const caps = useI18nCaps();
|
|
6130
|
+
const labelCaps = useI18nCaps(resource);
|
|
6032
6131
|
const tid = useTestIds();
|
|
6033
6132
|
const id = useFrameId();
|
|
6133
|
+
const failedIssues = chrome.serverIssues;
|
|
6134
|
+
const livePlan = controller.plan;
|
|
6135
|
+
const alertIssues = useMemo(() => failedIssues === void 0 || failedIssues.length === 0 ? NO_ALERT_ISSUES : routeServerIssues({ issues: failedIssues }, (segment) => segment, (field) => field !== "_form" && (livePlan === null || livePlan.fields[field] === "active")).form, [failedIssues, livePlan]);
|
|
6136
|
+
const fieldDefs = app.registry.get(resource)?.fields;
|
|
6034
6137
|
if (mode === "show") return null;
|
|
6035
6138
|
const busy = controller.isSubmitting;
|
|
6036
6139
|
const cancel = () => {
|
|
@@ -6054,21 +6157,43 @@ function FormActionsView() {
|
|
|
6054
6157
|
const text = (issue) => resolveValidationMessage({
|
|
6055
6158
|
code: issue.code ?? "",
|
|
6056
6159
|
message: issue.message,
|
|
6057
|
-
...issue.
|
|
6058
|
-
}, {
|
|
6160
|
+
...issue.check === void 0 ? {} : { check: issue.check }
|
|
6161
|
+
}, {
|
|
6162
|
+
has: caps.has,
|
|
6163
|
+
t: caps.t
|
|
6164
|
+
});
|
|
6059
6165
|
const retryable = [];
|
|
6060
6166
|
for (const issue of controller.checkOutcome?.blockers ?? []) {
|
|
6061
|
-
const checkId = issue.
|
|
6062
|
-
if (issue.
|
|
6167
|
+
const checkId = issue.check?.id;
|
|
6168
|
+
if (issue.check?.cause === void 0 || typeof checkId !== "string") continue;
|
|
6063
6169
|
if (!retryable.includes(checkId)) retryable.push(checkId);
|
|
6064
6170
|
}
|
|
6065
|
-
const failure = chrome.submitError === void 0 ? void 0 : failureText(chrome.submitError, t);
|
|
6171
|
+
const failure = chrome.submitError === void 0 ? void 0 : failureText(chrome.submitError, chrome.submitApplied, t, caps);
|
|
6172
|
+
const serverIssues = bySeverity(alertIssues).map((issue) => {
|
|
6173
|
+
const head = issue.path[0];
|
|
6174
|
+
const name = typeof head === "string" ? head : void 0;
|
|
6175
|
+
const declared = name === void 0 ? void 0 : fieldDefs?.[name];
|
|
6176
|
+
const message = resolveValidationMessage({
|
|
6177
|
+
code: issue.code,
|
|
6178
|
+
message: issue.message,
|
|
6179
|
+
...issue.check === void 0 ? {} : { check: issue.check }
|
|
6180
|
+
}, {
|
|
6181
|
+
has: caps.has,
|
|
6182
|
+
t: caps.t,
|
|
6183
|
+
...declared === void 0 ? {} : { fieldKey: `resources.${resource}.fields.${name}` }
|
|
6184
|
+
});
|
|
6185
|
+
return {
|
|
6186
|
+
label: declared === void 0 || name === void 0 ? void 0 : resolveFieldLabel(labelCaps, name, declaredText("field", declared, "label")),
|
|
6187
|
+
message,
|
|
6188
|
+
severity: issue.severity
|
|
6189
|
+
};
|
|
6190
|
+
});
|
|
6066
6191
|
const steps = controller.steps;
|
|
6067
6192
|
const midSteps = steps !== null && steps.ids.length > 1 && steps.current !== void 0 && steps.current !== steps.ids[steps.ids.length - 1];
|
|
6068
6193
|
return jsxs("div", {
|
|
6069
6194
|
className: "flex flex-col gap-3",
|
|
6070
6195
|
children: [
|
|
6071
|
-
failure === void 0 && formErrors.length === 0 ? null : jsxs("div", {
|
|
6196
|
+
failure === void 0 && formErrors.length === 0 && serverIssues.length === 0 ? null : jsxs("div", {
|
|
6072
6197
|
role: "alert",
|
|
6073
6198
|
className: "flex items-start gap-2 rounded-md border border-destructive/30 bg-destructive/10 px-3 py-2 text-sm text-destructive",
|
|
6074
6199
|
children: [
|
|
@@ -6078,10 +6203,22 @@ function FormActionsView() {
|
|
|
6078
6203
|
}),
|
|
6079
6204
|
jsxs("div", {
|
|
6080
6205
|
className: "leading-snug",
|
|
6081
|
-
children: [
|
|
6082
|
-
|
|
6083
|
-
|
|
6084
|
-
|
|
6206
|
+
children: [
|
|
6207
|
+
failure === void 0 ? null : jsx("p", {
|
|
6208
|
+
className: "font-medium",
|
|
6209
|
+
children: failure
|
|
6210
|
+
}),
|
|
6211
|
+
serverIssues.map((issue, index) => jsxs("p", {
|
|
6212
|
+
"data-testid": `form-server-issue:${resource}:${mode}:${index}`,
|
|
6213
|
+
"data-buro-severity": issue.severity,
|
|
6214
|
+
className: issue.severity === "error" ? void 0 : "text-warning-emphasis",
|
|
6215
|
+
children: [issue.label === void 0 ? null : jsxs("span", {
|
|
6216
|
+
className: "font-medium",
|
|
6217
|
+
children: [issue.label, ": "]
|
|
6218
|
+
}), issue.message]
|
|
6219
|
+
}, index)),
|
|
6220
|
+
formErrors.map((issue) => jsx("p", { children: text(issue) }, issueKey(issue)))
|
|
6221
|
+
]
|
|
6085
6222
|
}),
|
|
6086
6223
|
failure !== void 0 && steps !== null ? jsx(Button, {
|
|
6087
6224
|
type: "button",
|
|
@@ -6090,7 +6227,7 @@ function FormActionsView() {
|
|
|
6090
6227
|
className: "ml-auto",
|
|
6091
6228
|
onMouseDown: (event) => event.preventDefault(),
|
|
6092
6229
|
onClick: () => void chrome.submit(),
|
|
6093
|
-
children:
|
|
6230
|
+
children: chromeT("actions.retry", { defaultValue: "Retry" })
|
|
6094
6231
|
}) : null
|
|
6095
6232
|
]
|
|
6096
6233
|
}),
|
|
@@ -6102,7 +6239,7 @@ function FormActionsView() {
|
|
|
6102
6239
|
size: "sm",
|
|
6103
6240
|
"data-testid": `form-check-retry:${checkId}`,
|
|
6104
6241
|
onClick: () => void controller.revalidate(),
|
|
6105
|
-
children:
|
|
6242
|
+
children: chromeT("actions.retry", { defaultValue: "Retry" })
|
|
6106
6243
|
}, checkId))
|
|
6107
6244
|
}),
|
|
6108
6245
|
formWarnings.length === 0 ? null : jsxs("div", {
|
|
@@ -6128,7 +6265,7 @@ function FormActionsView() {
|
|
|
6128
6265
|
children: [jsx(IconLoader2, {
|
|
6129
6266
|
className: "h-3.5 w-3.5 animate-spin",
|
|
6130
6267
|
"aria-hidden": "true"
|
|
6131
|
-
}), jsx("span", { children:
|
|
6268
|
+
}), jsx("span", { children: chromeT("actions.validating", { defaultValue: "Validating…" }) })]
|
|
6132
6269
|
}) : null,
|
|
6133
6270
|
jsx(Button, {
|
|
6134
6271
|
type: "button",
|
|
@@ -6136,7 +6273,7 @@ function FormActionsView() {
|
|
|
6136
6273
|
onClick: cancel,
|
|
6137
6274
|
onMouseDown: (event) => event.preventDefault(),
|
|
6138
6275
|
"data-testid": tid.formCancel(resource, mode),
|
|
6139
|
-
children:
|
|
6276
|
+
children: chromeT("actions.cancel", { defaultValue: "Cancel" })
|
|
6140
6277
|
}),
|
|
6141
6278
|
midSteps ? null : jsxs(Button, {
|
|
6142
6279
|
type: "submit",
|
|
@@ -6153,37 +6290,71 @@ function FormActionsView() {
|
|
|
6153
6290
|
children: [busy ? jsx(IconLoader2, {
|
|
6154
6291
|
className: "mr-2 h-4 w-4 animate-spin",
|
|
6155
6292
|
"aria-hidden": "true"
|
|
6156
|
-
}) : null, busy ? mode === "edit" ?
|
|
6293
|
+
}) : null, busy ? mode === "edit" ? chromeT("actions.saving", { defaultValue: "Saving…" }) : chromeT("actions.creating", { defaultValue: "Creating…" }) : chrome.submitLabel ?? (mode === "edit" ? chromeT("actions.save", { defaultValue: "Save" }) : chromeT("actions.create", { defaultValue: "Create" }))]
|
|
6157
6294
|
})
|
|
6158
6295
|
]
|
|
6159
6296
|
})
|
|
6160
6297
|
]
|
|
6161
6298
|
});
|
|
6162
6299
|
}
|
|
6300
|
+
const NO_ALERT_ISSUES = Object.freeze([]);
|
|
6301
|
+
const SEVERITY_RANK = {
|
|
6302
|
+
error: 0,
|
|
6303
|
+
warning: 1,
|
|
6304
|
+
info: 2
|
|
6305
|
+
};
|
|
6306
|
+
function bySeverity(issues) {
|
|
6307
|
+
return [...issues].sort((a, b) => (SEVERITY_RANK[a.severity] ?? 3) - (SEVERITY_RANK[b.severity] ?? 3));
|
|
6308
|
+
}
|
|
6163
6309
|
function firstPreferred(issues) {
|
|
6164
|
-
const first = issues.find((issue) => issue.
|
|
6310
|
+
const first = issues.find((issue) => issue.check?.id === void 0) ?? issues[0];
|
|
6165
6311
|
return first === void 0 ? [] : [first];
|
|
6166
6312
|
}
|
|
6167
6313
|
function issueKey(issue) {
|
|
6168
6314
|
return JSON.stringify([
|
|
6169
6315
|
issue.code ?? "",
|
|
6170
6316
|
issue.message.key,
|
|
6171
|
-
issue.
|
|
6317
|
+
issue.check?.id ?? null
|
|
6172
6318
|
]);
|
|
6173
6319
|
}
|
|
6174
|
-
function failureText(error, t) {
|
|
6175
|
-
const
|
|
6176
|
-
|
|
6320
|
+
function failureText(error, settled, t, caps) {
|
|
6321
|
+
const texts = technicalTexts(error).filter((text) => text.length <= 1024);
|
|
6322
|
+
const strings = friendlyStringsFrom(t);
|
|
6323
|
+
const friendly = texts.map((text) => toFriendlyError(text, void 0, strings)).find((f) => f.replaced) ?? toFriendlyError(texts[0], void 0, strings);
|
|
6324
|
+
const applied = settled ?? ownDataProperty(error, "applied");
|
|
6325
|
+
if (applied === "partial" || applied === "unknown") {
|
|
6326
|
+
const key = applied === "partial" ? "errors.partlyApplied" : "errors.mayHaveBeenApplied";
|
|
6327
|
+
const sentence = caps.has(key) ? caps.t(key) : applied === "partial" ? "Part of this may have been saved. Check the record before trying again." : "It may have been saved. Check the record before trying again.";
|
|
6328
|
+
const lead = texts.some(isNetworkFailure) ? strings.offline : "";
|
|
6329
|
+
return lead === "" ? sentence : `${lead} ${sentence}`;
|
|
6330
|
+
}
|
|
6331
|
+
const code = ownDataProperty(error, "code");
|
|
6332
|
+
const kind = ownDataProperty(error, "kind");
|
|
6333
|
+
const hasOwnSentence = typeof code === "string" && typeof kind === "string" && kind !== "transport" && Object.hasOwn(RECORD_KIND_KEYS, kind);
|
|
6334
|
+
const message = ownDataProperty(error, "message");
|
|
6335
|
+
if (friendly.replaced && !hasOwnSentence) return (typeof code === "string" ? specificErrorText({
|
|
6336
|
+
code,
|
|
6337
|
+
...isMessageReference(message) ? { message } : {}
|
|
6338
|
+
}, caps, { record: true }) : void 0) ?? friendly.title;
|
|
6339
|
+
if (typeof code !== "string") return strings.title;
|
|
6340
|
+
const reference = {
|
|
6341
|
+
code,
|
|
6342
|
+
...isMessageReference(message) ? { message } : {}
|
|
6343
|
+
};
|
|
6344
|
+
return specificErrorText(reference, caps, { record: true }) ?? (typeof kind === "string" ? kindErrorText(kind, caps, { record: true }) : void 0) ?? strings.title;
|
|
6177
6345
|
}
|
|
6178
|
-
function
|
|
6346
|
+
function ownDataProperty(error, name) {
|
|
6179
6347
|
if (error === null || typeof error !== "object") return void 0;
|
|
6180
6348
|
try {
|
|
6181
|
-
const descriptor = Object.getOwnPropertyDescriptor(error,
|
|
6182
|
-
return descriptor !== void 0 && "value" in descriptor
|
|
6349
|
+
const descriptor = Object.getOwnPropertyDescriptor(error, name);
|
|
6350
|
+
return descriptor !== void 0 && "value" in descriptor ? descriptor.value : void 0;
|
|
6183
6351
|
} catch {
|
|
6184
6352
|
return;
|
|
6185
6353
|
}
|
|
6186
6354
|
}
|
|
6355
|
+
function isMessageReference(value) {
|
|
6356
|
+
return value !== null && typeof value === "object" && typeof value.key === "string";
|
|
6357
|
+
}
|
|
6187
6358
|
function inlineFlowNames(screen, plan) {
|
|
6188
6359
|
if (screen === null) return [];
|
|
6189
6360
|
const names = [];
|
|
@@ -6456,31 +6627,35 @@ function PlannedFrame(props) {
|
|
|
6456
6627
|
})
|
|
6457
6628
|
}, name);
|
|
6458
6629
|
const { PlacementScope } = page;
|
|
6630
|
+
const claimedRoute = useContext(RouteSourceContext);
|
|
6459
6631
|
const itemNode = page.plan.slots.item?.[0];
|
|
6460
6632
|
const item = useMemo(() => itemNode === void 0 ? void 0 : itemHandle(itemNode), [itemNode]);
|
|
6461
6633
|
return jsx(RouteSourceContext.Provider, {
|
|
6462
6634
|
value: null,
|
|
6463
|
-
children: jsx(
|
|
6464
|
-
|
|
6465
|
-
|
|
6466
|
-
|
|
6467
|
-
|
|
6468
|
-
|
|
6469
|
-
|
|
6470
|
-
children: jsx(
|
|
6471
|
-
|
|
6472
|
-
|
|
6473
|
-
|
|
6635
|
+
children: jsx(ActionRouteContext.Provider, {
|
|
6636
|
+
value: claimedRoute,
|
|
6637
|
+
children: jsx(PlacementScope, { children: jsx(ScreenTreeScope, {
|
|
6638
|
+
resource,
|
|
6639
|
+
mode: "show",
|
|
6640
|
+
compiled,
|
|
6641
|
+
page,
|
|
6642
|
+
children: jsx(ListItemContext.Provider, {
|
|
6643
|
+
value: item,
|
|
6644
|
+
children: jsx(FrameView, {
|
|
6645
|
+
resource,
|
|
6646
|
+
compiled,
|
|
6647
|
+
slots
|
|
6648
|
+
})
|
|
6474
6649
|
})
|
|
6475
|
-
})
|
|
6476
|
-
})
|
|
6650
|
+
}) })
|
|
6651
|
+
})
|
|
6477
6652
|
});
|
|
6478
6653
|
}
|
|
6479
6654
|
function ListFrameHost({ resource }) {
|
|
6480
6655
|
const compiled = useCompiledListScreen(resource);
|
|
6481
6656
|
const hasDataRuntime = useOptionalDataRuntime() !== null;
|
|
6482
6657
|
const { def } = useResourceListContext();
|
|
6483
|
-
const rowsView =
|
|
6658
|
+
const rowsView = def?.views?.list?.initial?.view ?? "table";
|
|
6484
6659
|
if (compiled === null) return jsx(Arrangement, {});
|
|
6485
6660
|
if (compiled.nodes.length === 0 && rowsView === "table") {
|
|
6486
6661
|
const empty = {};
|
|
@@ -6499,8 +6674,12 @@ function ListFrameHost({ resource }) {
|
|
|
6499
6674
|
}
|
|
6500
6675
|
//#endregion
|
|
6501
6676
|
//#region src/list-kit/resource-list-surface.tsx
|
|
6502
|
-
|
|
6503
|
-
|
|
6677
|
+
function declaresDisplay(field) {
|
|
6678
|
+
return field !== void 0 && resolveSurfaceUi(field, "column").use !== void 0;
|
|
6679
|
+
}
|
|
6680
|
+
const NO_INITIAL_FILTERS = Object.freeze({});
|
|
6681
|
+
const RelatedCards = lazy(async () => ({ default: (await import("./related-list-cards-BIqqY69Y.js")).RelatedCards }));
|
|
6682
|
+
const RelatedTimeline = lazy(async () => ({ default: (await import("./related-list-timeline-CRh7xwLD.js")).RelatedTimeline }));
|
|
6504
6683
|
function discriminatorOptionLabel(field, value) {
|
|
6505
6684
|
const text = fieldOptionText(field, value);
|
|
6506
6685
|
if (text !== void 0) return text;
|
|
@@ -6508,10 +6687,17 @@ function discriminatorOptionLabel(field, value) {
|
|
|
6508
6687
|
const label = (Array.isArray(declared?.options) ? declared.options : Array.isArray(declared?.meta?.options) ? declared.meta.options : []).find((option) => option?.value === value)?.label;
|
|
6509
6688
|
return typeof label === "string" ? label : value;
|
|
6510
6689
|
}
|
|
6511
|
-
function
|
|
6690
|
+
function declaredInitialSort(def) {
|
|
6691
|
+
return def?.views?.list?.initial?.sort;
|
|
6692
|
+
}
|
|
6693
|
+
function declaredRouteView(def) {
|
|
6694
|
+
return def?.views?.list?.initial?.view;
|
|
6695
|
+
}
|
|
6696
|
+
function ResourceListSurface({ resource, variant, state, fixedFilters, initial, search: searchField, columns: columnEntries, open, title, empty, scope, view, item, placementKey, onCreate, count: countPolicy, rowTarget, readOnly, linkEditing, exportable, layoutId, meta, renderCell, emptyState }) {
|
|
6512
6697
|
const route = variant === "route";
|
|
6513
6698
|
const t = useT();
|
|
6514
6699
|
const optionalT = useOptionalT();
|
|
6700
|
+
const chromeT = useChromeT();
|
|
6515
6701
|
const tid = useTestIds();
|
|
6516
6702
|
const listCaps = useI18nCaps(resource);
|
|
6517
6703
|
const RouteFiltersSlot = useListSlot("filters", resource);
|
|
@@ -6526,17 +6712,45 @@ function ResourceListSurface({ resource, variant, state, fixedFilters, pageSize,
|
|
|
6526
6712
|
console.error(`[burojs] list.body-removed: list.body.${canonicalResourceName(resource)} is no longer rendered; a whole-body override is a list frame (views.list.frame).`);
|
|
6527
6713
|
}, [removedBody, resource]);
|
|
6528
6714
|
const searchDecl = useMemo(() => searchField === void 0 ? void 0 : { fields: [searchField] }, [searchField]);
|
|
6529
|
-
const
|
|
6715
|
+
const appRegistry = useApp().registry;
|
|
6716
|
+
const effectiveView = route ? declaredRouteView(appRegistry.get(resource)) ?? "table" : view ?? "table";
|
|
6717
|
+
const timeline = effectiveView === "timeline";
|
|
6718
|
+
const initialKey = JSON.stringify(initial ?? null);
|
|
6719
|
+
const placementInitial = useMemo(() => route ? void 0 : {
|
|
6720
|
+
...initial,
|
|
6721
|
+
filters: initial?.filters ?? NO_INITIAL_FILTERS
|
|
6722
|
+
}, [route, initialKey]);
|
|
6723
|
+
const timelineAxis = (placementInitial?.sort ?? declaredInitialSort(appRegistry.get(resource)))?.[0]?.field;
|
|
6724
|
+
const columnNames = useMemo(() => columnEntries?.map(columnName), [columnEntries]);
|
|
6725
|
+
const list = useResourceList(resource, route ? {
|
|
6726
|
+
meta,
|
|
6727
|
+
...timeline ? { timeline } : {}
|
|
6728
|
+
} : {
|
|
6729
|
+
...timeline ? { timeline } : {},
|
|
6530
6730
|
...state === void 0 ? {} : { state },
|
|
6531
6731
|
...fixedFilters === void 0 ? {} : { fixedFilters },
|
|
6532
|
-
...
|
|
6732
|
+
...placementInitial === void 0 ? {} : { initial: placementInitial },
|
|
6533
6733
|
...searchDecl === void 0 ? {} : { search: searchDecl },
|
|
6534
|
-
...
|
|
6734
|
+
...columnEntries === void 0 ? {} : { columns: columnEntries },
|
|
6535
6735
|
...scope === void 0 ? {} : { scope },
|
|
6536
6736
|
...countPolicy === void 0 ? {} : { countPolicy }
|
|
6537
6737
|
});
|
|
6538
6738
|
const { def, query, pagination, cursor, count, filters, sorting, columns, selection, savedViews, routing, live, droppedPredicates } = list;
|
|
6539
6739
|
policyRowsOf(list, list.rows);
|
|
6740
|
+
const selectionRef = useRef(selection);
|
|
6741
|
+
selectionRef.current = selection;
|
|
6742
|
+
const onDetach = linkEditing?.detach?.onDetach;
|
|
6743
|
+
const surfaceLinkEditing = useMemo(() => onDetach === void 0 ? void 0 : { detach: { onDetach: async (detached) => {
|
|
6744
|
+
const left = await onDetach(detached);
|
|
6745
|
+
if (left.length > 0) {
|
|
6746
|
+
const gone = new Set(left);
|
|
6747
|
+
const current = selectionRef.current;
|
|
6748
|
+
if (current.ids.some((id) => gone.has(id))) current.set(current.ids.filter((id) => !gone.has(id)));
|
|
6749
|
+
}
|
|
6750
|
+
return left;
|
|
6751
|
+
} } }, [onDetach]);
|
|
6752
|
+
const onAttach = linkEditing?.attach?.onAttach;
|
|
6753
|
+
const attachBusy = linkEditing?.attach?.busy === true;
|
|
6540
6754
|
const hasDataRuntime = useOptionalDataRuntime() !== null;
|
|
6541
6755
|
const scopeLabel = (scope) => scope.id === LOCAL_USER_SCOPE_ID ? t("toolbar.viewsMyScope") : scope.label;
|
|
6542
6756
|
const createAvailable = useAvailability(resource, "create").state === "available";
|
|
@@ -6584,7 +6798,6 @@ function ResourceListSurface({ resource, variant, state, fixedFilters, pageSize,
|
|
|
6584
6798
|
resource,
|
|
6585
6799
|
routing.rowHref
|
|
6586
6800
|
]);
|
|
6587
|
-
const effectiveView = route ? (def?.views?.list)?.view ?? "table" : view ?? "table";
|
|
6588
6801
|
const tableView = effectiveView === "table";
|
|
6589
6802
|
const rowsViewFor = useMemo(() => tableView ? void 0 : (planned) => ({
|
|
6590
6803
|
skeleton: effectiveView === "cards" ? jsx(RelatedCardsSkeleton, {}) : jsx(RelatedTimelineSkeleton, {}),
|
|
@@ -6596,11 +6809,15 @@ function ResourceListSurface({ resource, variant, state, fixedFilters, pageSize,
|
|
|
6596
6809
|
item: route ? planned : item,
|
|
6597
6810
|
columns: route ? columns.visible : columnNames,
|
|
6598
6811
|
rowQuick,
|
|
6599
|
-
rowHref
|
|
6812
|
+
rowHref,
|
|
6813
|
+
...empty === void 0 ? {} : { empty }
|
|
6600
6814
|
};
|
|
6601
6815
|
return jsx(Suspense, {
|
|
6602
6816
|
fallback: effectiveView === "cards" ? jsx(RelatedCardsSkeleton, {}) : jsx(RelatedTimelineSkeleton, {}),
|
|
6603
|
-
children: effectiveView === "cards" ? jsx(RelatedCards, { ...common }) : jsx(RelatedTimeline, {
|
|
6817
|
+
children: effectiveView === "cards" ? jsx(RelatedCards, { ...common }) : jsx(RelatedTimeline, {
|
|
6818
|
+
...common,
|
|
6819
|
+
axis: timelineAxis
|
|
6820
|
+
})
|
|
6604
6821
|
});
|
|
6605
6822
|
}
|
|
6606
6823
|
}), [
|
|
@@ -6613,7 +6830,9 @@ function ResourceListSurface({ resource, variant, state, fixedFilters, pageSize,
|
|
|
6613
6830
|
columns.visible,
|
|
6614
6831
|
columnNames,
|
|
6615
6832
|
rowQuick,
|
|
6616
|
-
rowHref
|
|
6833
|
+
rowHref,
|
|
6834
|
+
empty,
|
|
6835
|
+
timelineAxis
|
|
6617
6836
|
]);
|
|
6618
6837
|
const pageTitle = resolveResourceLabel(listCaps, resource, "plural", def);
|
|
6619
6838
|
const [saveOpen, setSaveOpen] = useState(false);
|
|
@@ -6695,6 +6914,10 @@ function ResourceListSurface({ resource, variant, state, fixedFilters, pageSize,
|
|
|
6695
6914
|
toast.error(t("toolbar.viewsForkFailed"));
|
|
6696
6915
|
}
|
|
6697
6916
|
};
|
|
6917
|
+
const columnNamesDisplay = (col, field) => {
|
|
6918
|
+
const resolved = list.columns.ui[col];
|
|
6919
|
+
return resolved === void 0 ? declaresDisplay(field) : resolved.display.use !== void 0;
|
|
6920
|
+
};
|
|
6698
6921
|
const renderCellLadder = (col, value, row) => {
|
|
6699
6922
|
if (def?.types && col === def.types.by) {
|
|
6700
6923
|
const raw = typeof value === "string" ? value : "";
|
|
@@ -6713,13 +6936,14 @@ function ResourceListSurface({ resource, variant, state, fixedFilters, pageSize,
|
|
|
6713
6936
|
children: label
|
|
6714
6937
|
});
|
|
6715
6938
|
}
|
|
6939
|
+
const custom = (renderCell ?? CellSlot)?.(col, value, row);
|
|
6940
|
+
if (custom !== void 0) return custom;
|
|
6716
6941
|
const fieldDef = def?.fields[col];
|
|
6717
|
-
const enumName = fieldDef?.opts?.enum ?? fieldDef?.ui?.enum;
|
|
6718
|
-
if (enumName && typeof value === "string") return jsx(EnumBadge, {
|
|
6942
|
+
const enumName = enumBindingKey(fieldDef?.opts?.enum ?? fieldDef?.ui?.enum);
|
|
6943
|
+
if (enumName && typeof value === "string" && !columnNamesDisplay(col, fieldDef)) return jsx(EnumBadge, {
|
|
6719
6944
|
enumName,
|
|
6720
6945
|
value
|
|
6721
6946
|
});
|
|
6722
|
-
return (renderCell ?? CellSlot)?.(col, value, row);
|
|
6723
6947
|
};
|
|
6724
6948
|
const surface = {
|
|
6725
6949
|
resource,
|
|
@@ -6747,10 +6971,16 @@ function ResourceListSurface({ resource, variant, state, fixedFilters, pageSize,
|
|
|
6747
6971
|
}
|
|
6748
6972
|
},
|
|
6749
6973
|
rowsViewFor,
|
|
6750
|
-
emptyState,
|
|
6974
|
+
emptyState: empty !== void 0 && !list.filters.isFiltered && list.pagination.page <= 1 && (list.cursor === null || list.cursor.next === null) ? jsx("p", {
|
|
6975
|
+
"data-buro-region": "empty",
|
|
6976
|
+
className: "text-sm text-muted-foreground",
|
|
6977
|
+
children: empty
|
|
6978
|
+
}) : emptyState,
|
|
6751
6979
|
renderCell: renderCellLadder,
|
|
6752
6980
|
readOnly: readOnly === true,
|
|
6753
|
-
exportable: exportable !== false
|
|
6981
|
+
exportable: exportable !== false,
|
|
6982
|
+
...layoutId === void 0 ? {} : { layoutId },
|
|
6983
|
+
...surfaceLinkEditing === void 0 ? {} : { linkEditing: surfaceLinkEditing }
|
|
6754
6984
|
};
|
|
6755
6985
|
return jsx(ResourceListProvider, {
|
|
6756
6986
|
value: list,
|
|
@@ -6763,22 +6993,44 @@ function ResourceListSurface({ resource, variant, state, fixedFilters, pageSize,
|
|
|
6763
6993
|
resource,
|
|
6764
6994
|
mode: "list",
|
|
6765
6995
|
title: pageTitle
|
|
6766
|
-
}) : title === void 0 && onCreate === void 0 ? null : jsxs("div", {
|
|
6996
|
+
}) : title === void 0 && onCreate === void 0 && onAttach === void 0 ? null : jsxs("div", {
|
|
6767
6997
|
className: "flex items-center justify-between gap-2",
|
|
6768
|
-
children: [
|
|
6769
|
-
|
|
6770
|
-
|
|
6771
|
-
|
|
6772
|
-
|
|
6773
|
-
|
|
6774
|
-
|
|
6775
|
-
|
|
6776
|
-
|
|
6777
|
-
|
|
6778
|
-
|
|
6779
|
-
|
|
6780
|
-
|
|
6781
|
-
|
|
6998
|
+
children: [
|
|
6999
|
+
title === void 0 ? jsx("span", {}) : jsx("h3", {
|
|
7000
|
+
className: "text-sm font-semibold",
|
|
7001
|
+
children: title
|
|
7002
|
+
}),
|
|
7003
|
+
onAttach === void 0 ? null : jsxs(Button, {
|
|
7004
|
+
type: "button",
|
|
7005
|
+
size: "sm",
|
|
7006
|
+
variant: "outline",
|
|
7007
|
+
"data-testid": `list-link-attach:${resource}`,
|
|
7008
|
+
onClick: onAttach,
|
|
7009
|
+
disabled: attachBusy,
|
|
7010
|
+
"aria-busy": attachBusy,
|
|
7011
|
+
children: [attachBusy ? jsx(IconLoader2, {
|
|
7012
|
+
className: "mr-1 h-4 w-4 animate-spin",
|
|
7013
|
+
"aria-hidden": "true"
|
|
7014
|
+
}) : jsx(IconLink, {
|
|
7015
|
+
className: "mr-1 h-4 w-4",
|
|
7016
|
+
"aria-hidden": "true"
|
|
7017
|
+
}), optionalT("field.link.attach", { defaultValue: "Attach" })]
|
|
7018
|
+
}),
|
|
7019
|
+
onCreate === void 0 ? null : jsxs(Button, {
|
|
7020
|
+
type: "button",
|
|
7021
|
+
size: "sm",
|
|
7022
|
+
variant: "outline",
|
|
7023
|
+
"data-testid": `list-create:${resource}`,
|
|
7024
|
+
onClick: onCreate,
|
|
7025
|
+
children: [jsx(IconPlus, {
|
|
7026
|
+
className: "mr-1 h-4 w-4",
|
|
7027
|
+
"aria-hidden": "true"
|
|
7028
|
+
}), optionalT("actions.create", {
|
|
7029
|
+
resource,
|
|
7030
|
+
defaultValue: "Create"
|
|
7031
|
+
})]
|
|
7032
|
+
})
|
|
7033
|
+
]
|
|
6782
7034
|
}),
|
|
6783
7035
|
hasDataRuntime && jsx(ExportRunWatcherMount, {}),
|
|
6784
7036
|
droppedPredicates > 0 && jsx("p", {
|
|
@@ -6827,13 +7079,13 @@ function ResourceListSurface({ resource, variant, state, fixedFilters, pageSize,
|
|
|
6827
7079
|
variant: "outline",
|
|
6828
7080
|
onClick: () => setSaveOpen(false),
|
|
6829
7081
|
"data-testid": `list-saved-view-save-cancel:${resource}`,
|
|
6830
|
-
children:
|
|
7082
|
+
children: chromeT("actions.cancel")
|
|
6831
7083
|
}), jsx(Button, {
|
|
6832
7084
|
type: "button",
|
|
6833
7085
|
onClick: handleSaveConfirm,
|
|
6834
7086
|
disabled: saveLabel.trim().length === 0,
|
|
6835
7087
|
"data-testid": `list-saved-view-save-confirm:${resource}`,
|
|
6836
|
-
children:
|
|
7088
|
+
children: chromeT("actions.save")
|
|
6837
7089
|
})] })
|
|
6838
7090
|
]
|
|
6839
7091
|
})
|
|
@@ -6870,7 +7122,7 @@ function ResourceListSurface({ resource, variant, state, fixedFilters, pageSize,
|
|
|
6870
7122
|
variant: "outline",
|
|
6871
7123
|
onClick: () => handleDeleteUserView(entry.id),
|
|
6872
7124
|
"data-testid": `list-saved-view-manage-delete:${resource}:${entry.id}`,
|
|
6873
|
-
children:
|
|
7125
|
+
children: chromeT("actions.delete")
|
|
6874
7126
|
}) : jsx("span", {
|
|
6875
7127
|
className: "shrink-0 text-xs text-muted-foreground",
|
|
6876
7128
|
children: t("toolbar.viewsReadOnly")
|
|
@@ -6882,7 +7134,7 @@ function ResourceListSurface({ resource, variant, state, fixedFilters, pageSize,
|
|
|
6882
7134
|
variant: "outline",
|
|
6883
7135
|
onClick: () => setManageOpen(false),
|
|
6884
7136
|
"data-testid": `list-saved-view-manage-close:${resource}`,
|
|
6885
|
-
children:
|
|
7137
|
+
children: chromeT("actions.close")
|
|
6886
7138
|
}) })
|
|
6887
7139
|
]
|
|
6888
7140
|
})
|
|
@@ -6915,13 +7167,13 @@ function ResourceListSurface({ resource, variant, state, fixedFilters, pageSize,
|
|
|
6915
7167
|
setRenameTarget(null);
|
|
6916
7168
|
},
|
|
6917
7169
|
"data-testid": `list-saved-view-rename-cancel:${resource}`,
|
|
6918
|
-
children:
|
|
7170
|
+
children: chromeT("actions.cancel")
|
|
6919
7171
|
}), jsx(Button, {
|
|
6920
7172
|
type: "button",
|
|
6921
7173
|
onClick: handleRenameConfirm,
|
|
6922
7174
|
disabled: renameLabel.trim().length === 0,
|
|
6923
7175
|
"data-testid": `list-saved-view-rename-confirm:${resource}`,
|
|
6924
|
-
children:
|
|
7176
|
+
children: chromeT("actions.save")
|
|
6925
7177
|
})] })
|
|
6926
7178
|
]
|
|
6927
7179
|
})
|
|
@@ -6956,7 +7208,7 @@ function ResourceListSurface({ resource, variant, state, fixedFilters, pageSize,
|
|
|
6956
7208
|
setForkTarget(null);
|
|
6957
7209
|
},
|
|
6958
7210
|
"data-testid": `list-saved-view-fork-cancel:${resource}`,
|
|
6959
|
-
children:
|
|
7211
|
+
children: chromeT("actions.cancel")
|
|
6960
7212
|
}), jsx(Button, {
|
|
6961
7213
|
type: "button",
|
|
6962
7214
|
onClick: handleForkConfirm,
|
|
@@ -6979,6 +7231,582 @@ function ExportRunWatcherMount() {
|
|
|
6979
7231
|
return null;
|
|
6980
7232
|
}
|
|
6981
7233
|
//#endregion
|
|
7234
|
+
//#region src/fields/use-relation-picker.tsx
|
|
7235
|
+
const noop = () => void 0;
|
|
7236
|
+
function safeKey(input) {
|
|
7237
|
+
if (input === void 0 || input === null) return void 0;
|
|
7238
|
+
try {
|
|
7239
|
+
return resourceKey(input);
|
|
7240
|
+
} catch {
|
|
7241
|
+
return;
|
|
7242
|
+
}
|
|
7243
|
+
}
|
|
7244
|
+
function sessionIdentity(runtime) {
|
|
7245
|
+
const state = runtime.dataSession.getState();
|
|
7246
|
+
const snapshot = state.status === "ready" ? state.snapshot : state.target;
|
|
7247
|
+
const selection = snapshot.scope?.selection;
|
|
7248
|
+
const partition = selection === void 0 ? snapshot.scopeKey : [snapshot.scopeKey, selection];
|
|
7249
|
+
let scopeKey;
|
|
7250
|
+
try {
|
|
7251
|
+
scopeKey = JSON.stringify(partition) ?? "";
|
|
7252
|
+
} catch {
|
|
7253
|
+
scopeKey = String(snapshot.scopeKey);
|
|
7254
|
+
}
|
|
7255
|
+
return {
|
|
7256
|
+
sessionKey: snapshot.sessionKey,
|
|
7257
|
+
scopeKey
|
|
7258
|
+
};
|
|
7259
|
+
}
|
|
7260
|
+
function PickerHost(props) {
|
|
7261
|
+
const { register } = props;
|
|
7262
|
+
React.useEffect(() => register(), [register]);
|
|
7263
|
+
return jsx(Fragment$1, { children: props.children });
|
|
7264
|
+
}
|
|
7265
|
+
function useRelationPicker(options) {
|
|
7266
|
+
const app = useApp();
|
|
7267
|
+
const runtime = useDataRuntime();
|
|
7268
|
+
const frame = useOptionalRecordFrame();
|
|
7269
|
+
const frames = useRecordScopeFrames();
|
|
7270
|
+
const multiple = options.multiple === true;
|
|
7271
|
+
const fieldName = options.field;
|
|
7272
|
+
let to;
|
|
7273
|
+
let declaredBy;
|
|
7274
|
+
let declaredCandidates;
|
|
7275
|
+
if (fieldName !== void 0) {
|
|
7276
|
+
const meta = ((frame === null ? void 0 : app.registry.get(frame.resource)?.fields)?.[fieldName])?.meta;
|
|
7277
|
+
to = safeKey(meta?.to);
|
|
7278
|
+
declaredBy = typeof meta?.by === "string" ? meta.by : void 0;
|
|
7279
|
+
declaredCandidates = Array.isArray(meta?.candidates) ? meta.candidates : void 0;
|
|
7280
|
+
} else to = safeKey(options.to);
|
|
7281
|
+
const target = to ?? "";
|
|
7282
|
+
const by = options.valueField ?? declaredBy;
|
|
7283
|
+
const scope = options.scope ?? "context";
|
|
7284
|
+
const selection = useCandidateSelection({
|
|
7285
|
+
to,
|
|
7286
|
+
candidates: options.candidates ?? declaredCandidates,
|
|
7287
|
+
sources: frame?.controller.sources,
|
|
7288
|
+
resource: frame?.resource ?? "",
|
|
7289
|
+
fieldName: fieldName ?? target,
|
|
7290
|
+
fixedFilters: options.fixedFilters
|
|
7291
|
+
});
|
|
7292
|
+
const held = selection.gate !== void 0;
|
|
7293
|
+
const pendingRef = React.useRef(null);
|
|
7294
|
+
const [active, setActive] = React.useState(null);
|
|
7295
|
+
const activeRef = React.useRef(null);
|
|
7296
|
+
activeRef.current = active;
|
|
7297
|
+
const rowsRef = React.useRef(new Map());
|
|
7298
|
+
const createdRowRef = React.useRef(void 0);
|
|
7299
|
+
const alive = React.useRef(true);
|
|
7300
|
+
const mounts = React.useRef(0);
|
|
7301
|
+
const sequence = React.useRef(0);
|
|
7302
|
+
const latest = React.useRef({
|
|
7303
|
+
multiple,
|
|
7304
|
+
select: options.select,
|
|
7305
|
+
idField: "id",
|
|
7306
|
+
valueField: "id",
|
|
7307
|
+
available: false,
|
|
7308
|
+
frames
|
|
7309
|
+
});
|
|
7310
|
+
const project = React.useCallback((row) => {
|
|
7311
|
+
const select = latest.current.select;
|
|
7312
|
+
if (select === void 0) return { ...row };
|
|
7313
|
+
const out = {};
|
|
7314
|
+
const key = row[latest.current.idField] !== void 0 ? row[latest.current.idField] : row.id;
|
|
7315
|
+
if (key !== void 0) out.id = key;
|
|
7316
|
+
const { valueField } = latest.current;
|
|
7317
|
+
if (Object.hasOwn(row, valueField)) out[valueField] = row[valueField];
|
|
7318
|
+
for (const name of select) if (Object.hasOwn(row, name)) out[name] = row[name];
|
|
7319
|
+
return out;
|
|
7320
|
+
}, []);
|
|
7321
|
+
const settle = React.useCallback((result) => {
|
|
7322
|
+
const pending = pendingRef.current;
|
|
7323
|
+
if (pending === null) return;
|
|
7324
|
+
pendingRef.current = null;
|
|
7325
|
+
pending.detach();
|
|
7326
|
+
if (alive.current) setActive(null);
|
|
7327
|
+
pending.resolve(result);
|
|
7328
|
+
}, []);
|
|
7329
|
+
const toggle = React.useCallback((key, row) => {
|
|
7330
|
+
setActive((current) => {
|
|
7331
|
+
if (current === null) return current;
|
|
7332
|
+
const has = current.selected.some((k) => String(k) === String(key));
|
|
7333
|
+
if (has) rowsRef.current.delete(String(key));
|
|
7334
|
+
else if (row !== void 0) rowsRef.current.set(String(key), row);
|
|
7335
|
+
const selected = has ? current.selected.filter((k) => String(k) !== String(key)) : [...current.selected, key];
|
|
7336
|
+
const next = {
|
|
7337
|
+
...current,
|
|
7338
|
+
selected
|
|
7339
|
+
};
|
|
7340
|
+
activeRef.current = next;
|
|
7341
|
+
return next;
|
|
7342
|
+
});
|
|
7343
|
+
}, []);
|
|
7344
|
+
const onValue = React.useCallback((value) => {
|
|
7345
|
+
const row = createdRowRef.current;
|
|
7346
|
+
createdRowRef.current = void 0;
|
|
7347
|
+
if (latest.current.multiple) {
|
|
7348
|
+
const current = activeRef.current;
|
|
7349
|
+
if (current !== null && !current.selected.some((k) => String(k) === String(value))) toggle(value, row);
|
|
7350
|
+
return;
|
|
7351
|
+
}
|
|
7352
|
+
settle({
|
|
7353
|
+
kind: "selected",
|
|
7354
|
+
keys: [value],
|
|
7355
|
+
rows: row === void 0 ? [] : [project(row)]
|
|
7356
|
+
});
|
|
7357
|
+
}, [
|
|
7358
|
+
project,
|
|
7359
|
+
settle,
|
|
7360
|
+
toggle
|
|
7361
|
+
]);
|
|
7362
|
+
const wiring = useRelationSelect({
|
|
7363
|
+
to: target,
|
|
7364
|
+
by,
|
|
7365
|
+
fixedFilters: selection.fixedFilters,
|
|
7366
|
+
scope,
|
|
7367
|
+
...fieldName === void 0 ? {} : { scopeField: fieldName },
|
|
7368
|
+
held,
|
|
7369
|
+
onValue
|
|
7370
|
+
});
|
|
7371
|
+
latest.current = {
|
|
7372
|
+
multiple,
|
|
7373
|
+
select: options.select,
|
|
7374
|
+
idField: wiring.idField,
|
|
7375
|
+
valueField: wiring.valueField,
|
|
7376
|
+
available: to !== void 0 && wiring.targetDef !== void 0 && !held,
|
|
7377
|
+
frames
|
|
7378
|
+
};
|
|
7379
|
+
const { create: shared } = wiring;
|
|
7380
|
+
const create = React.useMemo(() => ({
|
|
7381
|
+
...shared,
|
|
7382
|
+
start: (openCreate) => shared.start(() => {
|
|
7383
|
+
const opened = openCreate();
|
|
7384
|
+
opened.then((row) => {
|
|
7385
|
+
createdRowRef.current = row === null ? void 0 : { ...row };
|
|
7386
|
+
}, noop);
|
|
7387
|
+
return opened;
|
|
7388
|
+
})
|
|
7389
|
+
}), [shared]);
|
|
7390
|
+
React.useEffect(() => runtime.dataSession.subscribe(() => {
|
|
7391
|
+
const pending = pendingRef.current;
|
|
7392
|
+
if (pending === null) return;
|
|
7393
|
+
const now = sessionIdentity(runtime);
|
|
7394
|
+
if (now.sessionKey !== pending.sessionKey || now.scopeKey !== pending.scopeKey) settle({
|
|
7395
|
+
kind: "cancelled",
|
|
7396
|
+
reason: "session-changed"
|
|
7397
|
+
});
|
|
7398
|
+
}), [runtime, settle]);
|
|
7399
|
+
React.useEffect(() => {
|
|
7400
|
+
alive.current = true;
|
|
7401
|
+
return () => {
|
|
7402
|
+
alive.current = false;
|
|
7403
|
+
settle({
|
|
7404
|
+
kind: "cancelled",
|
|
7405
|
+
reason: "unmounted"
|
|
7406
|
+
});
|
|
7407
|
+
};
|
|
7408
|
+
}, [settle]);
|
|
7409
|
+
React.useEffect(() => {
|
|
7410
|
+
if (held) settle({
|
|
7411
|
+
kind: "cancelled",
|
|
7412
|
+
reason: "unavailable"
|
|
7413
|
+
});
|
|
7414
|
+
}, [held, settle]);
|
|
7415
|
+
const register = React.useCallback(() => {
|
|
7416
|
+
mounts.current += 1;
|
|
7417
|
+
return () => {
|
|
7418
|
+
mounts.current -= 1;
|
|
7419
|
+
if (mounts.current === 0) settle({
|
|
7420
|
+
kind: "cancelled",
|
|
7421
|
+
reason: "unmounted"
|
|
7422
|
+
});
|
|
7423
|
+
};
|
|
7424
|
+
}, [settle]);
|
|
7425
|
+
const open = React.useCallback((opts = {}) => new Promise((resolve) => {
|
|
7426
|
+
settle({
|
|
7427
|
+
kind: "cancelled",
|
|
7428
|
+
reason: "superseded"
|
|
7429
|
+
});
|
|
7430
|
+
if (mounts.current === 0) {
|
|
7431
|
+
if (process.env.NODE_ENV !== "production") console.error("[burojs] useRelationPicker: open() was called but its element is not rendered — render `handle.element` once in your tree.");
|
|
7432
|
+
resolve({
|
|
7433
|
+
kind: "cancelled",
|
|
7434
|
+
reason: "not-mounted"
|
|
7435
|
+
});
|
|
7436
|
+
return;
|
|
7437
|
+
}
|
|
7438
|
+
const { signal } = opts;
|
|
7439
|
+
if (signal?.aborted === true) {
|
|
7440
|
+
resolve({
|
|
7441
|
+
kind: "cancelled",
|
|
7442
|
+
reason: "aborted"
|
|
7443
|
+
});
|
|
7444
|
+
return;
|
|
7445
|
+
}
|
|
7446
|
+
if (!latest.current.available) {
|
|
7447
|
+
resolve({
|
|
7448
|
+
kind: "cancelled",
|
|
7449
|
+
reason: "unavailable"
|
|
7450
|
+
});
|
|
7451
|
+
return;
|
|
7452
|
+
}
|
|
7453
|
+
sequence.current += 1;
|
|
7454
|
+
const id = sequence.current;
|
|
7455
|
+
const onAbort = () => {
|
|
7456
|
+
if (pendingRef.current?.id === id) settle({
|
|
7457
|
+
kind: "cancelled",
|
|
7458
|
+
reason: "aborted"
|
|
7459
|
+
});
|
|
7460
|
+
};
|
|
7461
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
7462
|
+
pendingRef.current = {
|
|
7463
|
+
id,
|
|
7464
|
+
...sessionIdentity(runtime),
|
|
7465
|
+
resolve,
|
|
7466
|
+
detach: () => signal?.removeEventListener("abort", onAbort)
|
|
7467
|
+
};
|
|
7468
|
+
rowsRef.current = new Map();
|
|
7469
|
+
createdRowRef.current = void 0;
|
|
7470
|
+
const next = {
|
|
7471
|
+
id,
|
|
7472
|
+
frames: latest.current.frames,
|
|
7473
|
+
selected: opts.selected ?? []
|
|
7474
|
+
};
|
|
7475
|
+
activeRef.current = next;
|
|
7476
|
+
setActive(next);
|
|
7477
|
+
}), [runtime, settle]);
|
|
7478
|
+
const onSelectRow = React.useCallback((row) => {
|
|
7479
|
+
const field = wiring.valueField;
|
|
7480
|
+
const value = field === "id" ? row.id : row[field];
|
|
7481
|
+
if (typeof value !== "string" && typeof value !== "number") return;
|
|
7482
|
+
settle({
|
|
7483
|
+
kind: "selected",
|
|
7484
|
+
keys: [value],
|
|
7485
|
+
rows: [project(row)]
|
|
7486
|
+
});
|
|
7487
|
+
}, [
|
|
7488
|
+
wiring.valueField,
|
|
7489
|
+
settle,
|
|
7490
|
+
project
|
|
7491
|
+
]);
|
|
7492
|
+
const onDone = React.useCallback(() => {
|
|
7493
|
+
const keys = activeRef.current?.selected ?? [];
|
|
7494
|
+
const rows = keys.flatMap((key) => {
|
|
7495
|
+
const row = rowsRef.current.get(String(key));
|
|
7496
|
+
return row === void 0 ? [] : [project(row)];
|
|
7497
|
+
});
|
|
7498
|
+
settle({
|
|
7499
|
+
kind: "selected",
|
|
7500
|
+
keys,
|
|
7501
|
+
rows
|
|
7502
|
+
});
|
|
7503
|
+
}, [settle, project]);
|
|
7504
|
+
const onOpenChange = React.useCallback((next) => {
|
|
7505
|
+
if (!next) settle({
|
|
7506
|
+
kind: "cancelled",
|
|
7507
|
+
reason: "dismissed"
|
|
7508
|
+
});
|
|
7509
|
+
}, [settle]);
|
|
7510
|
+
return {
|
|
7511
|
+
open,
|
|
7512
|
+
element: jsx(PickerHost, {
|
|
7513
|
+
register,
|
|
7514
|
+
children: active === null || held ? null : jsxs(RecordScopeChain, {
|
|
7515
|
+
frames: active.frames,
|
|
7516
|
+
children: [jsx(LazyRelationPickerDialog, {
|
|
7517
|
+
to: target,
|
|
7518
|
+
open: true,
|
|
7519
|
+
onOpenChange,
|
|
7520
|
+
onSelect: noop,
|
|
7521
|
+
onSelectRow,
|
|
7522
|
+
valueField: wiring.valueField,
|
|
7523
|
+
...selection.fixedFilters === void 0 ? {} : { fixedFilters: selection.fixedFilters },
|
|
7524
|
+
scope,
|
|
7525
|
+
...fieldName === void 0 ? {} : { scopeField: fieldName },
|
|
7526
|
+
...options.create === void 0 ? {} : {
|
|
7527
|
+
create,
|
|
7528
|
+
...options.create.seeds === void 0 ? {} : { createSeeds: options.create.seeds }
|
|
7529
|
+
},
|
|
7530
|
+
fieldName: fieldName ?? target,
|
|
7531
|
+
...options.disabledKeys === void 0 ? {} : { disabledKeys: options.disabledKeys },
|
|
7532
|
+
...options.disabledLabel === void 0 ? {} : { disabledLabel: options.disabledLabel },
|
|
7533
|
+
...multiple ? {
|
|
7534
|
+
multiple: true,
|
|
7535
|
+
selected: active.selected,
|
|
7536
|
+
onToggle: (value, row) => toggle(value, row),
|
|
7537
|
+
onDone
|
|
7538
|
+
} : {}
|
|
7539
|
+
}), wiring.reader]
|
|
7540
|
+
}, active.id)
|
|
7541
|
+
})
|
|
7542
|
+
};
|
|
7543
|
+
}
|
|
7544
|
+
//#endregion
|
|
7545
|
+
//#region src/blocks/link-write-summary.tsx
|
|
7546
|
+
function LinkWriteSummary(props) {
|
|
7547
|
+
const { report, titles, onClose } = props;
|
|
7548
|
+
const testid = props.testid ?? "link-write-summary";
|
|
7549
|
+
const t = useOptionalT();
|
|
7550
|
+
const caps = useI18nCaps();
|
|
7551
|
+
const chromeT = useChromeT();
|
|
7552
|
+
const heading = t("field.link.partial", { defaultValue: "Some links were not changed" });
|
|
7553
|
+
return jsx(Dialog, {
|
|
7554
|
+
open: true,
|
|
7555
|
+
onOpenChange: (open) => {
|
|
7556
|
+
if (!open) onClose();
|
|
7557
|
+
},
|
|
7558
|
+
children: jsxs(DialogContent, {
|
|
7559
|
+
"data-testid": testid,
|
|
7560
|
+
className: "flex max-h-[85vh] flex-col overflow-hidden",
|
|
7561
|
+
children: [
|
|
7562
|
+
jsxs(DialogHeader, { children: [jsx(DialogTitle, { children: heading }), jsx(DialogDescription, {
|
|
7563
|
+
className: "sr-only",
|
|
7564
|
+
children: heading
|
|
7565
|
+
})] }),
|
|
7566
|
+
jsx("ul", {
|
|
7567
|
+
className: "flex min-h-0 flex-1 flex-col gap-2 overflow-y-auto overscroll-contain",
|
|
7568
|
+
children: report.failed.map(({ id, error }) => jsxs("li", {
|
|
7569
|
+
"data-testid": `${testid}:item:${id}`,
|
|
7570
|
+
className: "text-sm",
|
|
7571
|
+
children: [jsx("span", {
|
|
7572
|
+
className: "font-medium",
|
|
7573
|
+
children: titles[id] ?? id
|
|
7574
|
+
}), jsx("span", {
|
|
7575
|
+
className: "text-destructive",
|
|
7576
|
+
children: `: ${resolveErrorText(error, caps)}`
|
|
7577
|
+
})]
|
|
7578
|
+
}, id))
|
|
7579
|
+
}),
|
|
7580
|
+
jsx(DialogFooter, { children: jsx(Button, {
|
|
7581
|
+
type: "button",
|
|
7582
|
+
onClick: onClose,
|
|
7583
|
+
"data-testid": `${testid}:close`,
|
|
7584
|
+
children: chromeT("actions.close", { defaultValue: "Close" })
|
|
7585
|
+
}) })
|
|
7586
|
+
]
|
|
7587
|
+
})
|
|
7588
|
+
});
|
|
7589
|
+
}
|
|
7590
|
+
//#endregion
|
|
7591
|
+
//#region src/blocks/link-attach.tsx
|
|
7592
|
+
const NO_ISSUES = Object.freeze([]);
|
|
7593
|
+
function useLinkPanelWrites(options) {
|
|
7594
|
+
const { owner, junction, target, link, targetKey, valueField, junctionIdField, displayField, scope, record, ownerKey, placement } = options;
|
|
7595
|
+
const app = useApp();
|
|
7596
|
+
const runtime = useDataRuntime();
|
|
7597
|
+
const t = useOptionalT();
|
|
7598
|
+
const rights = useLinkRights(owner, link, {
|
|
7599
|
+
surface: "panel",
|
|
7600
|
+
record,
|
|
7601
|
+
...placement === void 0 ? {} : { placement }
|
|
7602
|
+
});
|
|
7603
|
+
const writer = useLinkWriter(owner, link);
|
|
7604
|
+
const linkedKeys = useLinkedKeys(owner, link);
|
|
7605
|
+
const reverse = useMemo(() => reverseLinkOf((name) => app.registry.get(name)?.fields, owner, link), [
|
|
7606
|
+
app.registry,
|
|
7607
|
+
owner,
|
|
7608
|
+
link
|
|
7609
|
+
]);
|
|
7610
|
+
let liveHides = false;
|
|
7611
|
+
if (reverse !== void 0) try {
|
|
7612
|
+
const { providerId } = runtime.app.resolveDataBinding(target);
|
|
7613
|
+
liveHides = runtime.providers.resolve(providerId).provider.readRelations?.links === true && linkCapabilitiesOf(runtime, providerId)?.links?.filter.none !== false;
|
|
7614
|
+
} catch {
|
|
7615
|
+
liveHides = false;
|
|
7616
|
+
}
|
|
7617
|
+
const liveHidesRef = useRef(liveHides);
|
|
7618
|
+
liveHidesRef.current = liveHides;
|
|
7619
|
+
const [frozenHides, setFrozenHides] = useState(null);
|
|
7620
|
+
const hides = frozenHides ?? liveHides;
|
|
7621
|
+
const ownerKeyId = JSON.stringify(ownerKey ?? null);
|
|
7622
|
+
const hidden = useMemo(() => hides && reverse !== void 0 ? [{
|
|
7623
|
+
field: reverse,
|
|
7624
|
+
op: "hasNone",
|
|
7625
|
+
value: [ownerKey]
|
|
7626
|
+
}] : void 0, [
|
|
7627
|
+
hides,
|
|
7628
|
+
reverse,
|
|
7629
|
+
ownerKeyId
|
|
7630
|
+
]);
|
|
7631
|
+
const disabledKeys = useCallback((keys, signal) => linkedKeys(record, keys, signal), [linkedKeys, record]);
|
|
7632
|
+
const disabledLabel = useCallback((count) => t("field.link.alreadyLinked", {
|
|
7633
|
+
count,
|
|
7634
|
+
defaultValue: `${count} already linked`
|
|
7635
|
+
}), [t]);
|
|
7636
|
+
const picker = useRelationPicker({
|
|
7637
|
+
to: target,
|
|
7638
|
+
multiple: true,
|
|
7639
|
+
valueField,
|
|
7640
|
+
scope,
|
|
7641
|
+
...hidden === void 0 ? {
|
|
7642
|
+
disabledKeys,
|
|
7643
|
+
disabledLabel
|
|
7644
|
+
} : { fixedFilters: hidden }
|
|
7645
|
+
});
|
|
7646
|
+
const openPicker = picker.open;
|
|
7647
|
+
const [summary, setSummary] = useState(null);
|
|
7648
|
+
const finish = useCallback((report, titles, done) => {
|
|
7649
|
+
if (report.outcome !== "success") {
|
|
7650
|
+
setSummary({
|
|
7651
|
+
report,
|
|
7652
|
+
titles
|
|
7653
|
+
});
|
|
7654
|
+
return;
|
|
7655
|
+
}
|
|
7656
|
+
const count = report.succeeded.length;
|
|
7657
|
+
if (count === 0) return;
|
|
7658
|
+
toast.success(t(`field.link.${done}`, {
|
|
7659
|
+
count,
|
|
7660
|
+
defaultValue: `${count} ${count === 1 ? "link" : "links"} ${done}`
|
|
7661
|
+
}));
|
|
7662
|
+
}, [t]);
|
|
7663
|
+
const attaching = useRef(false);
|
|
7664
|
+
const [attachWriting, setAttachWriting] = useState(false);
|
|
7665
|
+
const onAttach = useCallback(() => {
|
|
7666
|
+
if (attaching.current) return;
|
|
7667
|
+
attaching.current = true;
|
|
7668
|
+
(async () => {
|
|
7669
|
+
try {
|
|
7670
|
+
setFrozenHides(liveHidesRef.current);
|
|
7671
|
+
let result;
|
|
7672
|
+
try {
|
|
7673
|
+
result = await openPicker();
|
|
7674
|
+
} finally {
|
|
7675
|
+
setFrozenHides(null);
|
|
7676
|
+
}
|
|
7677
|
+
if (result.kind !== "selected" || result.keys.length === 0) return;
|
|
7678
|
+
const titles = {};
|
|
7679
|
+
for (const row of result.rows) {
|
|
7680
|
+
const key = valueField === "id" ? row.id : row[valueField];
|
|
7681
|
+
const title = row[displayField];
|
|
7682
|
+
if (key !== void 0 && key !== null && title !== void 0 && title !== null) titles[String(key)] = String(title);
|
|
7683
|
+
}
|
|
7684
|
+
setAttachWriting(true);
|
|
7685
|
+
try {
|
|
7686
|
+
finish(await writer.attach(result.keys, record), titles, "attached");
|
|
7687
|
+
} finally {
|
|
7688
|
+
setAttachWriting(false);
|
|
7689
|
+
}
|
|
7690
|
+
} finally {
|
|
7691
|
+
attaching.current = false;
|
|
7692
|
+
}
|
|
7693
|
+
})();
|
|
7694
|
+
}, [
|
|
7695
|
+
displayField,
|
|
7696
|
+
finish,
|
|
7697
|
+
openPicker,
|
|
7698
|
+
record,
|
|
7699
|
+
valueField,
|
|
7700
|
+
writer
|
|
7701
|
+
]);
|
|
7702
|
+
const pending = useRef(null);
|
|
7703
|
+
const [confirming, setConfirming] = useState(null);
|
|
7704
|
+
const detaching = useRef(false);
|
|
7705
|
+
const [busy, setBusy] = useState(false);
|
|
7706
|
+
const onDetach = useCallback((rows) => new Promise((resolve) => {
|
|
7707
|
+
pending.current?.resolve([]);
|
|
7708
|
+
pending.current = {
|
|
7709
|
+
rows,
|
|
7710
|
+
resolve
|
|
7711
|
+
};
|
|
7712
|
+
setConfirming(rows.length);
|
|
7713
|
+
}), []);
|
|
7714
|
+
useEffect(() => () => {
|
|
7715
|
+
pending.current?.resolve([]);
|
|
7716
|
+
pending.current = null;
|
|
7717
|
+
}, []);
|
|
7718
|
+
const cancel = useCallback(() => {
|
|
7719
|
+
if (detaching.current) return;
|
|
7720
|
+
pending.current?.resolve([]);
|
|
7721
|
+
pending.current = null;
|
|
7722
|
+
setConfirming(null);
|
|
7723
|
+
}, []);
|
|
7724
|
+
const proceed = useCallback(() => {
|
|
7725
|
+
const current = pending.current;
|
|
7726
|
+
if (current === null || detaching.current) return;
|
|
7727
|
+
detaching.current = true;
|
|
7728
|
+
setBusy(true);
|
|
7729
|
+
(async () => {
|
|
7730
|
+
try {
|
|
7731
|
+
const titles = {};
|
|
7732
|
+
for (const row of current.rows) {
|
|
7733
|
+
const id = row[junctionIdField];
|
|
7734
|
+
if (id === void 0 || id === null) continue;
|
|
7735
|
+
const title = row[`${targetKey}.${displayField}`] ?? row[targetKey];
|
|
7736
|
+
if (title !== void 0 && title !== null) titles[String(id)] = String(title);
|
|
7737
|
+
}
|
|
7738
|
+
const report = await writer.detach(current.rows, record);
|
|
7739
|
+
pending.current = null;
|
|
7740
|
+
setConfirming(null);
|
|
7741
|
+
finish(report, titles, "detached");
|
|
7742
|
+
const left = new Set(report.succeeded);
|
|
7743
|
+
current.resolve(current.rows.filter((row) => {
|
|
7744
|
+
const id = row[junctionIdField];
|
|
7745
|
+
return id !== void 0 && id !== null && left.has(String(id));
|
|
7746
|
+
}).map((row) => String(row.id)));
|
|
7747
|
+
} finally {
|
|
7748
|
+
detaching.current = false;
|
|
7749
|
+
setBusy(false);
|
|
7750
|
+
}
|
|
7751
|
+
})();
|
|
7752
|
+
}, [
|
|
7753
|
+
displayField,
|
|
7754
|
+
finish,
|
|
7755
|
+
junctionIdField,
|
|
7756
|
+
record,
|
|
7757
|
+
targetKey,
|
|
7758
|
+
writer
|
|
7759
|
+
]);
|
|
7760
|
+
const detachLabel = t("field.link.detach", { defaultValue: "Detach" });
|
|
7761
|
+
const count = confirming ?? 0;
|
|
7762
|
+
const gate = {
|
|
7763
|
+
resource: junction,
|
|
7764
|
+
open: confirming !== null,
|
|
7765
|
+
busy,
|
|
7766
|
+
header: t("field.link.confirmDetach", {
|
|
7767
|
+
count,
|
|
7768
|
+
defaultValue: `Detach ${count} ${count === 1 ? "link" : "links"}? The linked records themselves are not deleted.`
|
|
7769
|
+
}),
|
|
7770
|
+
blockers: NO_ISSUES,
|
|
7771
|
+
confirmable: NO_ISSUES,
|
|
7772
|
+
proceed,
|
|
7773
|
+
cancel
|
|
7774
|
+
};
|
|
7775
|
+
const attachAllowed = rights.attach.allowed;
|
|
7776
|
+
const detachAllowed = rights.detach.allowed;
|
|
7777
|
+
return {
|
|
7778
|
+
linkEditing: useMemo(() => !attachAllowed && !detachAllowed ? void 0 : {
|
|
7779
|
+
...attachAllowed ? { attach: {
|
|
7780
|
+
onAttach,
|
|
7781
|
+
busy: attachWriting
|
|
7782
|
+
} } : {},
|
|
7783
|
+
...detachAllowed ? { detach: { onDetach } } : {}
|
|
7784
|
+
}, [
|
|
7785
|
+
attachAllowed,
|
|
7786
|
+
attachWriting,
|
|
7787
|
+
detachAllowed,
|
|
7788
|
+
onAttach,
|
|
7789
|
+
onDetach
|
|
7790
|
+
]),
|
|
7791
|
+
element: jsxs(Fragment$1, { children: [
|
|
7792
|
+
picker.element,
|
|
7793
|
+
jsx(CheckConfirmDialog, {
|
|
7794
|
+
state: gate,
|
|
7795
|
+
testid: `link-detach-confirm:${junction}`,
|
|
7796
|
+
title: detachLabel,
|
|
7797
|
+
proceedLabel: detachLabel,
|
|
7798
|
+
tone: "destructive"
|
|
7799
|
+
}),
|
|
7800
|
+
summary === null ? null : jsx(LinkWriteSummary, {
|
|
7801
|
+
report: summary.report,
|
|
7802
|
+
titles: summary.titles,
|
|
7803
|
+
onClose: () => setSummary(null),
|
|
7804
|
+
testid: `link-write-summary:${junction}`
|
|
7805
|
+
})
|
|
7806
|
+
] })
|
|
7807
|
+
};
|
|
7808
|
+
}
|
|
7809
|
+
//#endregion
|
|
6982
7810
|
//#region src/quick/peek-filter.ts
|
|
6983
7811
|
function peekFieldDefinition(def, name) {
|
|
6984
7812
|
return declaredField(def, name);
|
|
@@ -7002,32 +7830,12 @@ function peekRecord(def, row, idField, capabilityFor, fieldVisible) {
|
|
|
7002
7830
|
return peekRow(def, row, idField, capabilityFor, fieldVisible);
|
|
7003
7831
|
}
|
|
7004
7832
|
//#endregion
|
|
7005
|
-
//#region src/blocks/related-list-unbound.tsx
|
|
7006
|
-
function UnboundRelatedList(props) {
|
|
7007
|
-
const t = useOptionalT();
|
|
7008
|
-
return jsxs("div", {
|
|
7009
|
-
className: "flex flex-col gap-2",
|
|
7010
|
-
"data-testid": `list-unbound:${props.resource}`,
|
|
7011
|
-
children: [props.title === void 0 ? null : jsx("h3", {
|
|
7012
|
-
className: "text-sm font-semibold",
|
|
7013
|
-
children: props.title
|
|
7014
|
-
}), jsx("p", {
|
|
7015
|
-
className: "text-sm text-muted-foreground",
|
|
7016
|
-
children: t("table.empty", { defaultValue: "No records" })
|
|
7017
|
-
})]
|
|
7018
|
-
});
|
|
7019
|
-
}
|
|
7020
|
-
function placementScope(scope, owner) {
|
|
7021
|
-
return scope ?? (owner === void 0 ? "session" : "none");
|
|
7022
|
-
}
|
|
7023
|
-
//#endregion
|
|
7024
7833
|
//#region src/blocks/link-panel.tsx
|
|
7025
7834
|
const isRecord = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
|
|
7026
7835
|
const isResourceHandle = (value) => isRecord(value) && value.kind === "resource";
|
|
7027
|
-
function qualify(ref
|
|
7836
|
+
function qualify(ref) {
|
|
7028
7837
|
try {
|
|
7029
|
-
|
|
7030
|
-
return resourceKey(normalizeEntityRef(isResourceHandle(ref) ? ref.ref : ref, { defaultNamespace: namespace }));
|
|
7838
|
+
return resourceKey(normalizeEntityRef(isResourceHandle(ref) ? ref.ref : ref));
|
|
7031
7839
|
} catch {
|
|
7032
7840
|
return;
|
|
7033
7841
|
}
|
|
@@ -7046,7 +7854,7 @@ function linkField(registry, owner, link) {
|
|
|
7046
7854
|
function linkTarget(registry, owner, link) {
|
|
7047
7855
|
const found = linkField(registry, owner, link);
|
|
7048
7856
|
if (found === void 0 || owner === void 0) return void 0;
|
|
7049
|
-
return qualify(found.field.meta?.to
|
|
7857
|
+
return qualify(found.field.meta?.to);
|
|
7050
7858
|
}
|
|
7051
7859
|
function idFieldOf(def) {
|
|
7052
7860
|
const declared = isRecord(def) && isRecord(def.data) ? def.data.idField : void 0;
|
|
@@ -7062,37 +7870,47 @@ function targetColumns(registry, target, def) {
|
|
|
7062
7870
|
const source = def;
|
|
7063
7871
|
const views = isRecord(def.views) ? def.views : void 0;
|
|
7064
7872
|
const list = views !== void 0 && isRecord(views.list) ? views.list : void 0;
|
|
7065
|
-
return (list !== void 0 && Array.isArray(list.columns) ? list.columns.
|
|
7873
|
+
return (list !== void 0 && Array.isArray(list.columns) ? list.columns.flatMap((entry) => {
|
|
7874
|
+
const name = isRecord(entry) ? entry.field : entry;
|
|
7875
|
+
return typeof name === "string" ? [name] : [];
|
|
7876
|
+
}) : dtoFields(source, "list")).filter((name) => {
|
|
7066
7877
|
if (name.includes(".")) return name.split(".").length - 1 <= MAX_TARGET_HOPS && resolveColumnField(registry, target, name) !== void 0;
|
|
7067
7878
|
const field = peekFieldDefinition(source, name);
|
|
7068
7879
|
return isRecord(field) && !isRelationField(field) && fieldSensitivity(field) === void 0;
|
|
7069
7880
|
});
|
|
7070
7881
|
}
|
|
7071
7882
|
function LinkPanel(props) {
|
|
7072
|
-
const { link, owner, own } = props;
|
|
7883
|
+
const { link, owner, own, empty } = props;
|
|
7073
7884
|
const app = useApp();
|
|
7074
7885
|
const frame = usePlacementFrame();
|
|
7075
7886
|
const routeScreen = useIsRouteScreen();
|
|
7076
7887
|
const record = useOptionalRecordFrame()?.record;
|
|
7888
|
+
const runtime = useOptionalDataRuntime();
|
|
7077
7889
|
const storage = useMemo(() => {
|
|
7078
7890
|
const found = linkField(app.registry, owner, link);
|
|
7079
7891
|
if (found === void 0) return void 0;
|
|
7080
7892
|
const { declaration } = found;
|
|
7081
|
-
const junction = qualify(declaration.junction
|
|
7082
|
-
const target = qualify(found.field.meta?.to
|
|
7893
|
+
const junction = qualify(declaration.junction);
|
|
7894
|
+
const target = qualify(found.field.meta?.to);
|
|
7083
7895
|
if (junction === void 0 || target === void 0) return void 0;
|
|
7084
7896
|
const junctionDef = app.registry.get(junction);
|
|
7085
7897
|
if (junctionDef === void 0) return void 0;
|
|
7086
7898
|
const junctionFields = junctionDef.fields;
|
|
7899
|
+
const targetDef = app.registry.get(target);
|
|
7900
|
+
const targetBy = byOf(junctionFields[declaration.targetKey]);
|
|
7901
|
+
const declaredScope = found.field.meta?.scope;
|
|
7087
7902
|
return {
|
|
7088
7903
|
junction,
|
|
7089
7904
|
target,
|
|
7090
7905
|
sourceKey: declaration.sourceKey,
|
|
7091
7906
|
targetKey: declaration.targetKey,
|
|
7092
7907
|
ownerField: byOf(junctionFields[declaration.sourceKey]) ?? idFieldOf(app.registry.get(owner)),
|
|
7093
|
-
targetKeyedBy:
|
|
7908
|
+
targetKeyedBy: targetBy !== void 0,
|
|
7909
|
+
targetValueField: targetBy ?? idFieldOf(targetDef),
|
|
7910
|
+
junctionIdField: idFieldOf(junctionDef),
|
|
7094
7911
|
where: declaration.where,
|
|
7095
|
-
targetDef
|
|
7912
|
+
targetDef,
|
|
7913
|
+
pickerScope: declaredScope === "context" || declaredScope === "session" || declaredScope === "none" ? declaredScope : void 0
|
|
7096
7914
|
};
|
|
7097
7915
|
}, [
|
|
7098
7916
|
app.registry,
|
|
@@ -7104,10 +7922,11 @@ function LinkPanel(props) {
|
|
|
7104
7922
|
const fixedFilters = useMemo(() => {
|
|
7105
7923
|
if (storage === void 0 || ownerKey === null || ownerKey === void 0) return void 0;
|
|
7106
7924
|
const { sourceKey, targetKey } = storage;
|
|
7925
|
+
const placed = whereToFilter(own.where ?? [], (field) => `${targetKey}.${field}`);
|
|
7107
7926
|
return [
|
|
7108
7927
|
whereEntryToFilter(sourceKey, "eq", ownerKey),
|
|
7109
7928
|
...storage.where.map((entry) => whereEntryToFilter(entry.field, entry.op, entry.value)),
|
|
7110
|
-
...
|
|
7929
|
+
...placed === void 0 ? [] : [placed]
|
|
7111
7930
|
];
|
|
7112
7931
|
}, [
|
|
7113
7932
|
storage,
|
|
@@ -7117,7 +7936,12 @@ function LinkPanel(props) {
|
|
|
7117
7936
|
const columnsKey = JSON.stringify([own.columns ?? null, own.linkColumns ?? null]);
|
|
7118
7937
|
const columns = useMemo(() => {
|
|
7119
7938
|
if (storage === void 0) return void 0;
|
|
7120
|
-
|
|
7939
|
+
const shown = own.columns ?? targetColumns(app.registry, storage.target, storage.targetDef);
|
|
7940
|
+
const across = (entry) => typeof entry === "string" ? `${storage.targetKey}.${entry}` : {
|
|
7941
|
+
...entry,
|
|
7942
|
+
field: `${storage.targetKey}.${entry.field}`
|
|
7943
|
+
};
|
|
7944
|
+
return [...shown.map(across), ...own.linkColumns ?? []];
|
|
7121
7945
|
}, [
|
|
7122
7946
|
storage,
|
|
7123
7947
|
columnsKey,
|
|
@@ -7127,30 +7951,75 @@ function LinkPanel(props) {
|
|
|
7127
7951
|
resource: storage.target,
|
|
7128
7952
|
field: storage.targetKey
|
|
7129
7953
|
}, [storage]);
|
|
7130
|
-
const unbound = fixedFilters === void 0 || own.where
|
|
7954
|
+
const unbound = fixedFilters === void 0 || own.where !== void 0 && hasNullRef(own.where);
|
|
7131
7955
|
if (storage === void 0 || unbound) return jsx(UnboundRelatedList, {
|
|
7132
7956
|
resource: storage?.target ?? "",
|
|
7133
|
-
title: own.title
|
|
7957
|
+
title: own.title,
|
|
7958
|
+
...empty === void 0 ? {} : { empty }
|
|
7134
7959
|
});
|
|
7135
7960
|
const state = frame?.state === "url" && routeScreen && !frame.nested ? { url: frame.key } : "memory";
|
|
7136
|
-
|
|
7137
|
-
|
|
7961
|
+
let providerCounts;
|
|
7962
|
+
if (runtime !== null) try {
|
|
7963
|
+
providerCounts = linkCapabilitiesOf(runtime, runtime.app.resolveDataBinding(storage.junction).providerId)?.links?.count;
|
|
7964
|
+
} catch {
|
|
7965
|
+
providerCounts = void 0;
|
|
7966
|
+
}
|
|
7967
|
+
const count = own.count === false ? "off" : own.count ?? (providerCounts === false ? "off" : void 0);
|
|
7968
|
+
const layoutId = frame === null ? void 0 : placementLayoutId({
|
|
7969
|
+
owner,
|
|
7970
|
+
key: frame.key
|
|
7971
|
+
});
|
|
7972
|
+
const surface = {
|
|
7138
7973
|
resource: storage.junction,
|
|
7139
7974
|
variant: "placement",
|
|
7140
7975
|
state,
|
|
7141
7976
|
fixedFilters,
|
|
7142
|
-
...own.pageSize === void 0 ? {} : { pageSize: own.pageSize },
|
|
7977
|
+
...own.initial?.pageSize === void 0 ? {} : { initial: { pageSize: own.initial.pageSize } },
|
|
7143
7978
|
...own.search === void 0 ? {} : { search: `${storage.targetKey}.${own.search}` },
|
|
7144
7979
|
...columns === void 0 ? {} : { columns },
|
|
7145
7980
|
open: rowTarget === void 0 || own.open === "none" ? "none" : "page",
|
|
7146
7981
|
...rowTarget === void 0 ? {} : { rowTarget },
|
|
7147
7982
|
...frame === null ? {} : { placementKey: frame.key },
|
|
7983
|
+
...layoutId === void 0 ? {} : { layoutId },
|
|
7148
7984
|
...own.title === void 0 ? {} : { title: own.title },
|
|
7985
|
+
...empty === void 0 ? {} : { empty },
|
|
7149
7986
|
scope: placementScope(own.scope, owner),
|
|
7150
7987
|
...count === void 0 ? {} : { count },
|
|
7151
7988
|
readOnly: true,
|
|
7152
7989
|
exportable: false
|
|
7990
|
+
};
|
|
7991
|
+
if (runtime === null || record === void 0) return jsx(ResourceListSurface, { ...surface });
|
|
7992
|
+
return jsx(WritableLinkPanel, {
|
|
7993
|
+
surface,
|
|
7994
|
+
owner,
|
|
7995
|
+
link,
|
|
7996
|
+
storage,
|
|
7997
|
+
record,
|
|
7998
|
+
ownerKey,
|
|
7999
|
+
placement: own.linkActions
|
|
8000
|
+
});
|
|
8001
|
+
}
|
|
8002
|
+
function WritableLinkPanel(props) {
|
|
8003
|
+
const { surface, owner, link, storage, record, ownerKey, placement } = props;
|
|
8004
|
+
const displayField = isRecord(storage.targetDef) && typeof storage.targetDef.displayField === "string" ? storage.targetDef.displayField : storage.targetValueField;
|
|
8005
|
+
const { linkEditing, element } = useLinkPanelWrites({
|
|
8006
|
+
owner,
|
|
8007
|
+
junction: storage.junction,
|
|
8008
|
+
target: storage.target,
|
|
8009
|
+
link,
|
|
8010
|
+
targetKey: storage.targetKey,
|
|
8011
|
+
valueField: storage.targetValueField,
|
|
8012
|
+
junctionIdField: storage.junctionIdField,
|
|
8013
|
+
displayField,
|
|
8014
|
+
scope: storage.pickerScope ?? "context",
|
|
8015
|
+
record,
|
|
8016
|
+
ownerKey,
|
|
8017
|
+
placement
|
|
7153
8018
|
});
|
|
8019
|
+
return jsxs(Fragment$1, { children: [jsx(ResourceListSurface, {
|
|
8020
|
+
...surface,
|
|
8021
|
+
...linkEditing === void 0 ? {} : { linkEditing }
|
|
8022
|
+
}), element] });
|
|
7154
8023
|
}
|
|
7155
8024
|
//#endregion
|
|
7156
8025
|
//#region src/blocks/route-list-context.ts
|
|
@@ -7160,7 +8029,7 @@ function useRouteList() {
|
|
|
7160
8029
|
}
|
|
7161
8030
|
//#endregion
|
|
7162
8031
|
//#region src/blocks/related-list-view.tsx
|
|
7163
|
-
function RelatedListView({ props }) {
|
|
8032
|
+
function RelatedListView({ props, empty }) {
|
|
7164
8033
|
const own = props;
|
|
7165
8034
|
const route = useRouteList();
|
|
7166
8035
|
const frame = usePlacementFrame();
|
|
@@ -7168,7 +8037,7 @@ function RelatedListView({ props }) {
|
|
|
7168
8037
|
const linked = own.link !== void 0;
|
|
7169
8038
|
const resource = linked ? linkTarget(app.registry, frame?.owner, own.link) ?? "" : resourceKey(own.resource);
|
|
7170
8039
|
const whereKey = JSON.stringify(own.where ?? []);
|
|
7171
|
-
const fixedFilters = useMemo(() => own.where === void 0
|
|
8040
|
+
const fixedFilters = useMemo(() => own.where === void 0 || own.where.length === 0 ? void 0 : [whereToFilter(own.where)], [whereKey]);
|
|
7172
8041
|
const columnsKey = JSON.stringify(own.columns ?? null);
|
|
7173
8042
|
const columns = useMemo(() => own.columns, [columnsKey]);
|
|
7174
8043
|
const routeScreen = useIsRouteScreen();
|
|
@@ -7180,20 +8049,23 @@ function RelatedListView({ props }) {
|
|
|
7180
8049
|
const ownerDef = frame?.owner === void 0 ? void 0 : app.registry.get(frame.owner);
|
|
7181
8050
|
const granted = ownerDef === void 0 ? "unknown" : grantedPlacementKeys(ownerDef);
|
|
7182
8051
|
const trusted = frame !== null && !frame.nested && granted !== "unknown" && !granted.has(frame.key);
|
|
7183
|
-
const onCreate = route === null && !linked && trusted && gate(resource, "create").state === "open" && frame !== null ? () => void quick.create(resource, { defaults: placementSeeds(frame.where ?? [], own.where ?? [], frame.closure ?? {}) }) : void 0;
|
|
8052
|
+
const onCreate = route === null && !linked && trusted && gate(resource, "create").state === "open" && own.create !== false && (own.create === void 0 || actionWhenHolds(own.create, frame?.sources ?? {})) && frame !== null ? () => void quick.create(resource, { defaults: placementSeeds(frame.where ?? [], own.where ?? [], frame.closure ?? {}) }) : void 0;
|
|
7184
8053
|
useDeepLinkOpen(frame?.key ?? null, resource, route === null && !linked && frame !== null && !frame.nested && routeScreen && (own.open ?? "quick") === "quick");
|
|
7185
|
-
const unbound = own.where
|
|
8054
|
+
const unbound = own.where !== void 0 && hasNullRef(own.where);
|
|
7186
8055
|
if (linked) return frame?.owner === void 0 ? jsx(UnboundRelatedList, {
|
|
7187
8056
|
resource,
|
|
7188
|
-
title: own.title
|
|
8057
|
+
title: own.title,
|
|
8058
|
+
...empty === void 0 ? {} : { empty }
|
|
7189
8059
|
}) : jsx(LinkPanel, {
|
|
7190
8060
|
link: own.link,
|
|
7191
8061
|
owner: frame.owner,
|
|
7192
|
-
own
|
|
8062
|
+
own,
|
|
8063
|
+
...empty === void 0 ? {} : { empty }
|
|
7193
8064
|
});
|
|
7194
8065
|
if (route !== null) return jsx(ResourceListSurface, {
|
|
7195
8066
|
resource,
|
|
7196
8067
|
variant: "route",
|
|
8068
|
+
layoutId: "route",
|
|
7197
8069
|
...route.meta === void 0 ? {} : { meta: route.meta },
|
|
7198
8070
|
...route.renderCell === void 0 ? {} : { renderCell: route.renderCell },
|
|
7199
8071
|
...route.emptyState === void 0 ? {} : { emptyState: route.emptyState }
|
|
@@ -7206,24 +8078,28 @@ function RelatedListView({ props }) {
|
|
|
7206
8078
|
});
|
|
7207
8079
|
if (unbound) return jsx(UnboundRelatedList, {
|
|
7208
8080
|
resource,
|
|
7209
|
-
title: own.title
|
|
8081
|
+
title: own.title,
|
|
8082
|
+
...empty === void 0 ? {} : { empty }
|
|
7210
8083
|
});
|
|
7211
8084
|
const state = frame?.state === "url" && routeScreen && !frame.nested ? { url: frame.key } : "memory";
|
|
7212
8085
|
const scope = placementScope(own.scope, frame?.owner);
|
|
7213
8086
|
const count = own.count === false ? "off" : own.count;
|
|
8087
|
+
const layoutId = placementLayoutId(frame);
|
|
7214
8088
|
return jsx(ResourceListSurface, {
|
|
7215
8089
|
resource,
|
|
7216
8090
|
variant: "placement",
|
|
7217
8091
|
state,
|
|
7218
8092
|
...fixedFilters === void 0 ? {} : { fixedFilters },
|
|
7219
|
-
...own.
|
|
8093
|
+
...own.initial === void 0 ? {} : { initial: own.initial },
|
|
7220
8094
|
...own.search === void 0 ? {} : { search: own.search },
|
|
7221
8095
|
...columns === void 0 ? {} : { columns },
|
|
7222
8096
|
open: own.open ?? "quick",
|
|
7223
8097
|
...own.view === void 0 ? {} : { view: own.view },
|
|
7224
8098
|
...isItemHandle(own.item) ? { item: own.item } : {},
|
|
7225
8099
|
...frame === null ? {} : { placementKey: frame.key },
|
|
8100
|
+
...layoutId === void 0 ? {} : { layoutId },
|
|
7226
8101
|
...own.title === void 0 ? {} : { title: own.title },
|
|
8102
|
+
...empty === void 0 ? {} : { empty },
|
|
7227
8103
|
scope,
|
|
7228
8104
|
...count === void 0 ? {} : { count },
|
|
7229
8105
|
...onCreate === void 0 ? {} : { onCreate }
|
|
@@ -7258,7 +8134,7 @@ function Missing({ text }) {
|
|
|
7258
8134
|
children: text ?? t("table.empty", { defaultValue: "No records" })
|
|
7259
8135
|
});
|
|
7260
8136
|
}
|
|
7261
|
-
function RelatedRecordView({ props }) {
|
|
8137
|
+
function RelatedRecordView({ props, empty }) {
|
|
7262
8138
|
const own = props;
|
|
7263
8139
|
const app = useApp();
|
|
7264
8140
|
const resource = resourceKey(own.resource);
|
|
@@ -7292,15 +8168,15 @@ function RelatedRecordView({ props }) {
|
|
|
7292
8168
|
capabilityFor,
|
|
7293
8169
|
access
|
|
7294
8170
|
]);
|
|
7295
|
-
if (id === null) return jsx(Missing, { text: own.missing });
|
|
8171
|
+
if (id === null) return jsx(Missing, { text: own.missing ?? empty });
|
|
7296
8172
|
if (result === void 0) {
|
|
7297
|
-
if (query.error != null) return jsx(Missing, { text: own.missing });
|
|
8173
|
+
if (query.error != null) return jsx(Missing, { text: own.missing ?? empty });
|
|
7298
8174
|
return jsx(Skeleton, {
|
|
7299
8175
|
"data-buro-skeleton": "",
|
|
7300
8176
|
className: "h-9 w-full"
|
|
7301
8177
|
});
|
|
7302
8178
|
}
|
|
7303
|
-
if (dtoRow === void 0) return jsx(Missing, { text: own.missing });
|
|
8179
|
+
if (dtoRow === void 0) return jsx(Missing, { text: own.missing ?? empty });
|
|
7304
8180
|
if (projected.status === "pending" || projected.value === null) return jsx(Skeleton, {
|
|
7305
8181
|
"data-buro-skeleton": "",
|
|
7306
8182
|
className: "h-9 w-full"
|
|
@@ -7333,13 +8209,21 @@ function RelatedRecordView({ props }) {
|
|
|
7333
8209
|
children: resolveFieldLabel(caps, name, declaredText("field", field, "label"))
|
|
7334
8210
|
}), jsx("dd", {
|
|
7335
8211
|
className: "min-w-0 break-words text-sm",
|
|
7336
|
-
children: jsx(
|
|
7337
|
-
|
|
7338
|
-
|
|
7339
|
-
|
|
8212
|
+
children: jsx(FieldValueFrame, {
|
|
8213
|
+
field: {
|
|
8214
|
+
...field ?? {},
|
|
8215
|
+
name
|
|
8216
|
+
},
|
|
7340
8217
|
value,
|
|
7341
|
-
|
|
7342
|
-
|
|
8218
|
+
surface: "display",
|
|
8219
|
+
children: jsx(ResolvedFieldDisplay, {
|
|
8220
|
+
resource: own.resource,
|
|
8221
|
+
name,
|
|
8222
|
+
field,
|
|
8223
|
+
value,
|
|
8224
|
+
record: row,
|
|
8225
|
+
fallback: toDisplayText(value).text
|
|
8226
|
+
})
|
|
7343
8227
|
})
|
|
7344
8228
|
})]
|
|
7345
8229
|
}, name);
|
|
@@ -7410,28 +8294,39 @@ function Chrome({ node, size, state, children }) {
|
|
|
7410
8294
|
});
|
|
7411
8295
|
}
|
|
7412
8296
|
function isAuthError(error) {
|
|
7413
|
-
return
|
|
8297
|
+
return normalizeDataError(error).kind === "auth";
|
|
7414
8298
|
}
|
|
7415
|
-
|
|
8299
|
+
const PLACE_NODE_MESSAGES = {
|
|
8300
|
+
failed: "This section could not be loaded.",
|
|
8301
|
+
sessionExpired: "Your session has expired. Sign in again to continue.",
|
|
8302
|
+
notImplemented: "This section is not available."
|
|
8303
|
+
};
|
|
8304
|
+
function FailedPlacement({ node, size, message, retry, error }) {
|
|
7416
8305
|
const t = useOptionalT();
|
|
8306
|
+
const chromeT = useChromeT();
|
|
8307
|
+
const fallback = {
|
|
8308
|
+
key: `block.${message}`,
|
|
8309
|
+
default: PLACE_NODE_MESSAGES[message]
|
|
8310
|
+
};
|
|
7417
8311
|
return jsx(Chrome, {
|
|
7418
8312
|
node,
|
|
7419
8313
|
size,
|
|
7420
8314
|
state: "failed",
|
|
7421
|
-
children: jsxs("div", {
|
|
8315
|
+
children: message === "notImplemented" ? jsxs("div", {
|
|
7422
8316
|
role: "alert",
|
|
7423
8317
|
className: "flex flex-wrap items-center gap-2 rounded-md border border-dashed px-3 py-2 text-sm text-muted-foreground",
|
|
7424
|
-
children: [jsx("span", { children: t(
|
|
7425
|
-
failed: "This section could not be loaded.",
|
|
7426
|
-
sessionExpired: "Your session has expired. Sign in again to continue.",
|
|
7427
|
-
notImplemented: "This section is not available."
|
|
7428
|
-
}[message] }) }), retry === void 0 ? null : jsx(Button, {
|
|
8318
|
+
children: [jsx("span", { children: t(fallback.key, { defaultValue: fallback.default }) }), retry === void 0 ? null : jsx(Button, {
|
|
7429
8319
|
type: "button",
|
|
7430
8320
|
variant: "outline",
|
|
7431
8321
|
size: "sm",
|
|
7432
8322
|
onClick: retry,
|
|
7433
|
-
children:
|
|
8323
|
+
children: chromeT("actions.retry", { defaultValue: "Retry" })
|
|
7434
8324
|
})]
|
|
8325
|
+
}) : jsx(LoadError, {
|
|
8326
|
+
surface: "block",
|
|
8327
|
+
error,
|
|
8328
|
+
...retry === void 0 ? {} : { retry },
|
|
8329
|
+
fallback
|
|
7435
8330
|
})
|
|
7436
8331
|
});
|
|
7437
8332
|
}
|
|
@@ -7511,7 +8406,8 @@ function ReadyPlacement({ node, size, placement, implementation }) {
|
|
|
7511
8406
|
nested,
|
|
7512
8407
|
...Array.isArray(declaredWhere) ? { where: declaredWhere } : {},
|
|
7513
8408
|
closure: compiled.sourceClosure,
|
|
7514
|
-
...owner === void 0 ? {} : { owner }
|
|
8409
|
+
...owner === void 0 ? {} : { owner },
|
|
8410
|
+
sources: controller.sources
|
|
7515
8411
|
}), [
|
|
7516
8412
|
node.key,
|
|
7517
8413
|
node.id,
|
|
@@ -7519,7 +8415,8 @@ function ReadyPlacement({ node, size, placement, implementation }) {
|
|
|
7519
8415
|
nested,
|
|
7520
8416
|
declaredWhere,
|
|
7521
8417
|
compiled.sourceClosure,
|
|
7522
|
-
owner
|
|
8418
|
+
owner,
|
|
8419
|
+
controller.sources
|
|
7523
8420
|
]);
|
|
7524
8421
|
return jsx(PlacementBoundary, {
|
|
7525
8422
|
node,
|
|
@@ -7577,6 +8474,7 @@ function PlaceNode({ node }) {
|
|
|
7577
8474
|
node,
|
|
7578
8475
|
size,
|
|
7579
8476
|
message: isAuthError(placement.error) ? "sessionExpired" : "failed",
|
|
8477
|
+
error: placement.error,
|
|
7580
8478
|
...placement.retry === void 0 ? {} : { retry: placement.retry }
|
|
7581
8479
|
});
|
|
7582
8480
|
if (implementation === void 0) return jsx(FailedPlacement, {
|
|
@@ -7592,6 +8490,6 @@ function PlaceNode({ node }) {
|
|
|
7592
8490
|
});
|
|
7593
8491
|
}
|
|
7594
8492
|
//#endregion
|
|
7595
|
-
export {
|
|
8493
|
+
export { CursorLoadMore as $, ListSearchInput as A, EnumBadge as At, ExportDialog as B, usePaletteState as Bt, ListControls as C, recordReturnTarget as Ct, ListFlows as D, useShownItemActions as Dt, ListLive as E, ActionOverflow as Et, ListColumns as F, i18nPrefixToRef$1 as Ft, RouteScreenClaimContext as G, ScreenTree as H, ColumnsToggle as I, FieldValueFrame as It, FormControlProvider as J, RouteScreenContext as K, DataTableViewOptions as L, toDisplayText as Lt, ExportButton as M, AppFlowButton as Mt, ListCreate as N, Field as Nt, ListFilters as O, useExportRunWatcher as Ot, CreateButton as P, FieldDisplay as Pt, ResourceList as Q, ListBulkBar as R, ShellProvider as Rt, useFrameChrome as S, listTarget as St, SavedViewsDropdown as T, showTarget as Tt, useTabsUrlState as U, DeclarativeForm as V, useShell as Vt, QuickScreenClaimContext as W, useFormControlOf as X, useFormControl as Y, ListBody as Z, CopyId as _, resolveResourceTitle as _t, peekFieldDefinition as a, DataTable as at, RecordTitle as b, typography as bt, peekRow as c, TabsContent as ct, resolveFrameView as d, RelatedItem as dt, ListErrorState as et, shadcnFrameViews as f, useItemDrawn as ft, UndeclaredTypeNotice as g, ShowPageHeader as gt, TypeChromeDrawnContext as h, PageHeader as ht, RouteListContext as i, useBuroDataTable as it, ListExport as j, DeepLinkScope as jt, FilterRegion as k, downloadArtifact as kt, useRelationPicker as l, TabsList as lt, HeaderTypeNotice as m, EditPageHeader as mt, viewOf as n, resolveUpdater as nt, peekLeafAllowed as o, Checkbox as ot, ChangeTypeButton as p, RELATED_GRID_CLASS as pt, useIsRouteScreen as q, shadcnBlockImplementations as r, tanStackSortingToBuro as rt, peekRecord as s, Tabs as st, selectImplementation as t, buroSortingToTanStack as tt, isBuiltinRecordView as u, TabsTrigger as ut, FormActions as v, DocumentTitleProvider as vt, ListSavedViews as w, recordRouteAvailable as wt, FrameChromeContext as x, isShowAvailable as xt, RecordActions as y, useDocumentTitle as yt, BulkActionToolbar as z, defaultUserMapper as zt };
|
|
7596
8494
|
|
|
7597
|
-
//# sourceMappingURL=place-node-
|
|
8495
|
+
//# sourceMappingURL=place-node-CfzQvh4Y.js.map
|