@burojs/ui-shadcn 0.6.0 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -0
- package/dist/bulk-summary-2HLgz7Il.cjs +169 -0
- package/dist/bulk-summary-2HLgz7Il.cjs.map +1 -0
- package/dist/bulk-summary-D6A0ua21.js +169 -0
- package/dist/bulk-summary-D6A0ua21.js.map +1 -0
- package/dist/{candidates-relation-picker-Cy0rTxvI.cjs → candidates-relation-picker-C47mF260.cjs} +4 -4
- package/dist/{candidates-relation-picker-Cy0rTxvI.cjs.map → candidates-relation-picker-C47mF260.cjs.map} +1 -1
- package/dist/{candidates-relation-picker-B116Bmqi.js → candidates-relation-picker-CGXItMN0.js} +3 -3
- package/dist/{candidates-relation-picker-B116Bmqi.js.map → candidates-relation-picker-CGXItMN0.js.map} +1 -1
- package/dist/{check-confirm-dialog--gPFnEeg.cjs → check-confirm-dialog-BT04Mieo.cjs} +475 -107
- package/dist/check-confirm-dialog-BT04Mieo.cjs.map +1 -0
- package/dist/{check-confirm-dialog-bRT8lt0d.js → check-confirm-dialog-CtiRzOAt.js} +391 -108
- package/dist/check-confirm-dialog-CtiRzOAt.js.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-ByGhxLf_.cjs} +1021 -154
- package/dist/filter-builder-ByGhxLf_.cjs.map +1 -0
- package/dist/{filter-builder-Bhn-dhZV.js → filter-builder-z3Axzf9Y.js} +929 -140
- package/dist/filter-builder-z3Axzf9Y.js.map +1 -0
- package/dist/{flow-runtime-D3avVH0I.cjs → flow-runtime-BVFjMU5-.cjs} +64 -29
- package/dist/flow-runtime-BVFjMU5-.cjs.map +1 -0
- package/dist/{flow-runtime-s6lkRPKF.js → flow-runtime-DrUWyTS0.js} +65 -30
- package/dist/flow-runtime-DrUWyTS0.js.map +1 -0
- package/dist/{flow-screen-CCvgDI9G.js → flow-screen-3fk6J33y.js} +130 -87
- package/dist/flow-screen-3fk6J33y.js.map +1 -0
- package/dist/{flow-screen-Ci_Sik4Y.cjs → flow-screen-6y5E_HT6.cjs} +130 -87
- package/dist/flow-screen-6y5E_HT6.cjs.map +1 -0
- package/dist/index.cjs +1186 -950
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +631 -322
- package/dist/index.d.ts +631 -322
- package/dist/index.js +1100 -870
- package/dist/index.js.map +1 -1
- package/dist/{inline-flow-host-DV1et3c6.cjs → inline-flow-host-C4KmZIYE.cjs} +5 -5
- package/dist/inline-flow-host-C4KmZIYE.cjs.map +1 -0
- package/dist/{inline-flow-host-B6FI0ctQ.js → inline-flow-host-YnnvWjdU.js} +5 -5
- package/dist/inline-flow-host-YnnvWjdU.js.map +1 -0
- package/dist/{candidates-gate-D33jXLHO.cjs → lazy-relation-picker-dialog-BAKBRn3V.cjs} +10 -10
- package/dist/lazy-relation-picker-dialog-BAKBRn3V.cjs.map +1 -0
- package/dist/{candidates-gate-Bb70LPAy.js → lazy-relation-picker-dialog-DN4bvQfg.js} +11 -11
- package/dist/lazy-relation-picker-dialog-DN4bvQfg.js.map +1 -0
- package/dist/{page-flow-route-i3ZfJ1sd.js → page-flow-route-CtEfqAvU.js} +35 -23
- package/dist/page-flow-route-CtEfqAvU.js.map +1 -0
- package/dist/{page-flow-route-hdnULWqd.cjs → page-flow-route-DEjAxHe3.cjs} +42 -30
- package/dist/page-flow-route-DEjAxHe3.cjs.map +1 -0
- package/dist/{place-node-NraNYKPx.js → place-node-BdxT-30h.js} +1980 -1041
- package/dist/place-node-BdxT-30h.js.map +1 -0
- package/dist/{place-node-C5SmIAYU.cjs → place-node-Bhl8B3fF.cjs} +2038 -1087
- package/dist/place-node-Bhl8B3fF.cjs.map +1 -0
- package/dist/{related-list-cards-CFq3meUn.cjs → related-list-cards-Bc_vfCE_.cjs} +9 -4
- package/dist/{related-list-cards-CFq3meUn.cjs.map → related-list-cards-Bc_vfCE_.cjs.map} +1 -1
- package/dist/{related-list-cards-BWZ9GfOG.js → related-list-cards-BvkD5OcC.js} +9 -4
- package/dist/{related-list-cards-BWZ9GfOG.js.map → related-list-cards-BvkD5OcC.js.map} +1 -1
- package/dist/related-list-timeline-DCrwN9oO.js +167 -0
- package/dist/related-list-timeline-DCrwN9oO.js.map +1 -0
- package/dist/related-list-timeline-k0DtnxTs.cjs +167 -0
- package/dist/related-list-timeline-k0DtnxTs.cjs.map +1 -0
- package/dist/{related-rows-ea43qhhy.cjs → related-rows-BW9Jr07T.cjs} +27 -16
- package/dist/related-rows-BW9Jr07T.cjs.map +1 -0
- package/dist/{related-rows-o0_t-Yce.js → related-rows-CPE-9FFr.js} +25 -14
- package/dist/related-rows-CPE-9FFr.js.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-BlrGRwMV.js} +210 -104
- package/dist/relation-many-picker-BlrGRwMV.js.map +1 -0
- package/dist/{relation-many-picker-C8zFEjwd.cjs → relation-many-picker-Br-zBHxT.cjs} +218 -112
- package/dist/relation-many-picker-Br-zBHxT.cjs.map +1 -0
- package/dist/{relation-picker-gag0qSR5.cjs → relation-picker-ADjxI_TI.cjs} +36 -73
- package/dist/relation-picker-ADjxI_TI.cjs.map +1 -0
- package/dist/{relation-picker-dialog-B6nIiJBf.js → relation-picker-dialog-CyKliBkN.js} +134 -36
- package/dist/relation-picker-dialog-CyKliBkN.js.map +1 -0
- package/dist/{relation-picker-dialog-0IzkjmiA.cjs → relation-picker-dialog-DimVQF9Y.cjs} +133 -35
- package/dist/relation-picker-dialog-DimVQF9Y.cjs.map +1 -0
- package/dist/{relation-picker-Z0ZJJ5uS.js → relation-picker-x7P5HKie.js} +30 -61
- package/dist/relation-picker-x7P5HKie.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-3RUf9d1j.js → resource-page-router-BRpUnhay.js} +634 -264
- package/dist/resource-page-router-BRpUnhay.js.map +1 -0
- package/dist/{resource-page-router-PXfMW9nh.cjs → resource-page-router-qEd2xb55.cjs} +694 -312
- package/dist/resource-page-router-qEd2xb55.cjs.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/dist/use-record-subtitle-bfwIoUme.cjs +70 -0
- package/dist/use-record-subtitle-bfwIoUme.cjs.map +1 -0
- package/dist/use-record-subtitle-gIaCGgbI.js +53 -0
- package/dist/use-record-subtitle-gIaCGgbI.js.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 +0 -142
- package/dist/related-list-timeline-CCzl71aA.cjs.map +0 -1
- package/dist/related-list-timeline-yrPsjA3F.js +0 -142
- 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-CtiRzOAt.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 {
|
|
9
|
+
import { A as TableHeader, C as SelectTrigger, E as TableBody, F as ResolvedFieldDisplay, I as ResolvedFieldInput, L as useFieldMetaDiagnostic, M as Badge, O as TableCell, P as MetaPlaceholder, R as fieldDescription, T as Table$1, g as SelectContent, h as Select, j as TableRow, k as TableHead, t as FilterBuilder, v as SelectItem, w as SelectValue, z as lowerLegacyField } from "./filter-builder-z3Axzf9Y.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-DN4bvQfg.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, 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, deriveRecord, implementFrame, isObjectColumn, kindErrorText, linkCapabilitiesOf, policyRecordOf, policyRowsOf, readColumnValue, recordKeyOf, resolveColumnField, resolveErrorText, resolveObjectColumn, 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, useReturnTarget, useRouteNavigate, useScreenState, useSearchParamsWriter, useT, useTestIds, useViewRow, withRecordKeys } 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, deepestPlacedPath, enumBindingKey, hasNullRef, isFlowAction, pathHead, 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(null);
|
|
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 === null) return;
|
|
149
|
+
const timer = setTimeout(() => setCopied(null), 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("copied"), () => setCopied("failed"));
|
|
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 === "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 === "copied" ? t("field.copied", { defaultValue: "Copied" }) : copied === "failed" ? t("field.copyFailed", { defaultValue: "Could not copy" }) : ""
|
|
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({
|
|
@@ -606,6 +281,10 @@ function leafAttributes(size, weight) {
|
|
|
606
281
|
...weight === void 0 ? {} : { "data-buro-weight": String(weight) }
|
|
607
282
|
};
|
|
608
283
|
}
|
|
284
|
+
function labelHiddenOf(compiled, node) {
|
|
285
|
+
return compiled.nodes[node.index]?.options.label === "hidden";
|
|
286
|
+
}
|
|
287
|
+
const labelRegion = (hidden, visible) => hidden ? "sr-only" : visible;
|
|
609
288
|
function FieldLeaf({ node }) {
|
|
610
289
|
const { leafIds } = useScreenTree();
|
|
611
290
|
if (node.state === "failed" && node.failedOn.length > 0 && node.failedOn.every((name) => name.startsWith("meta:"))) return jsx(MisconfiguredLeaf, { node });
|
|
@@ -617,7 +296,8 @@ function FieldLeaf({ node }) {
|
|
|
617
296
|
return jsx(RecordLeaf, { node });
|
|
618
297
|
}
|
|
619
298
|
function MisconfiguredLeaf({ node }) {
|
|
620
|
-
const { leafIds, resource, mode, controller } = useScreenTree();
|
|
299
|
+
const { leafIds, resource, mode, compiled, controller } = useScreenTree();
|
|
300
|
+
const hidden = labelHiddenOf(compiled, node);
|
|
621
301
|
const name = node.field;
|
|
622
302
|
const binding = controller.field(name);
|
|
623
303
|
const caps = useI18nCaps(resource);
|
|
@@ -633,7 +313,7 @@ function MisconfiguredLeaf({ node }) {
|
|
|
633
313
|
values: controller.values,
|
|
634
314
|
extra: controller.sources
|
|
635
315
|
});
|
|
636
|
-
const state = useFieldMeta(field, resolved.implementation, sources);
|
|
316
|
+
const state = useFieldMeta(field, resolved.implementation, sources, resolved.params);
|
|
637
317
|
useFieldMetaDiagnostic(state, binding.resource, name, field, surface);
|
|
638
318
|
const label = resolveFieldLabel(caps, name, declaredText("field", binding.field, "label"));
|
|
639
319
|
const testId = leafIds === "record" ? tid.formField(resource, mode, name) : `${leafIds.prefix}:field:${name}`;
|
|
@@ -644,7 +324,7 @@ function MisconfiguredLeaf({ node }) {
|
|
|
644
324
|
"data-buro-field-meta": "failed",
|
|
645
325
|
children: [jsx("div", {
|
|
646
326
|
"data-buro-region": "field-label",
|
|
647
|
-
className: "text-sm font-medium leading-none",
|
|
327
|
+
className: labelRegion(hidden, "text-sm font-medium leading-none"),
|
|
648
328
|
children: label
|
|
649
329
|
}), jsx("div", {
|
|
650
330
|
"data-buro-region": "field-control",
|
|
@@ -652,12 +332,22 @@ function MisconfiguredLeaf({ node }) {
|
|
|
652
332
|
})]
|
|
653
333
|
});
|
|
654
334
|
}
|
|
335
|
+
function valueAt(record, path) {
|
|
336
|
+
let at = record;
|
|
337
|
+
for (const segment of path.split(".")) {
|
|
338
|
+
if (at === null || typeof at !== "object") return void 0;
|
|
339
|
+
at = at[segment];
|
|
340
|
+
}
|
|
341
|
+
return at;
|
|
342
|
+
}
|
|
655
343
|
function RecordLeaf({ node }) {
|
|
656
344
|
const { resource, mode, compiled, controller } = useScreenTree();
|
|
657
|
-
const
|
|
345
|
+
const head = node.field;
|
|
346
|
+
const name = node.fieldPath ?? head;
|
|
658
347
|
const binding = controller.field(name);
|
|
659
348
|
const size = useLeafSize(binding.field);
|
|
660
349
|
const weight = compiled.nodes[node.index]?.options.weight;
|
|
350
|
+
const hidden = labelHiddenOf(compiled, node);
|
|
661
351
|
const caps = useI18nCaps(resource);
|
|
662
352
|
const tid = useTestIds();
|
|
663
353
|
const label = resolveFieldLabel(caps, name, declaredText("field", binding.field, "label"));
|
|
@@ -665,10 +355,7 @@ function RecordLeaf({ node }) {
|
|
|
665
355
|
const t = useOptionalT();
|
|
666
356
|
if (node.restricted === true) {
|
|
667
357
|
const rawReason = node.restrictedReason;
|
|
668
|
-
const reason = rawReason === void 0 || rawReason === "" ? void 0 :
|
|
669
|
-
resource,
|
|
670
|
-
default: rawReason
|
|
671
|
-
});
|
|
358
|
+
const reason = rawReason === void 0 || rawReason === "" ? void 0 : reasonText(caps, rawReason, resource);
|
|
672
359
|
const restrictedId = tid.formFieldRestricted(resource, mode, name);
|
|
673
360
|
return jsxs("div", {
|
|
674
361
|
...attributes,
|
|
@@ -677,7 +364,7 @@ function RecordLeaf({ node }) {
|
|
|
677
364
|
"data-buro-restricted": "true",
|
|
678
365
|
children: [jsx("div", {
|
|
679
366
|
"data-buro-region": "field-label",
|
|
680
|
-
className: "text-sm font-medium leading-none",
|
|
367
|
+
className: labelRegion(hidden, "text-sm font-medium leading-none"),
|
|
681
368
|
children: label
|
|
682
369
|
}), jsx("div", {
|
|
683
370
|
"data-buro-region": "field-control",
|
|
@@ -697,7 +384,7 @@ function RecordLeaf({ node }) {
|
|
|
697
384
|
});
|
|
698
385
|
}
|
|
699
386
|
const record = controller.record;
|
|
700
|
-
const value = mode === "show" ? record
|
|
387
|
+
const value = mode === "show" ? valueAt(record, name) ?? binding.value : mode === "edit" && controller.plan?.fields[head] !== "active" ? valueAt(record, name) ?? binding.value : binding.value;
|
|
701
388
|
const display = jsx(FieldDisplay, {
|
|
702
389
|
resource: binding.resource,
|
|
703
390
|
name,
|
|
@@ -705,6 +392,8 @@ function RecordLeaf({ node }) {
|
|
|
705
392
|
value,
|
|
706
393
|
metaSources: controller.sources,
|
|
707
394
|
...record === void 0 ? {} : { record },
|
|
395
|
+
...binding.absentPaths === void 0 ? {} : { absentPaths: binding.absentPaths },
|
|
396
|
+
...binding.pathStatus === void 0 ? {} : { pathStatus: binding.pathStatus },
|
|
708
397
|
fallback: value == null || value === "" ? jsx("span", {
|
|
709
398
|
className: "text-muted-foreground",
|
|
710
399
|
children: "—"
|
|
@@ -715,11 +404,11 @@ function RecordLeaf({ node }) {
|
|
|
715
404
|
"data-testid": `show-field:${resource}:${name}`,
|
|
716
405
|
children: [jsxs("dt", {
|
|
717
406
|
"data-buro-region": "field-label",
|
|
718
|
-
className: cn("flex items-center gap-0.5", FIELD_LABEL_SHOW),
|
|
407
|
+
className: labelRegion(hidden, cn("flex items-center gap-0.5", FIELD_LABEL_SHOW)),
|
|
719
408
|
children: [jsx("span", {
|
|
720
409
|
className: "min-w-0",
|
|
721
410
|
children: label
|
|
722
|
-
}), jsx(DocInfo, {
|
|
411
|
+
}), hidden ? null : jsx(DocInfo, {
|
|
723
412
|
address: `${resource}/fields/${name}`,
|
|
724
413
|
name: label
|
|
725
414
|
})]
|
|
@@ -731,10 +420,7 @@ function RecordLeaf({ node }) {
|
|
|
731
420
|
});
|
|
732
421
|
if (node.editable !== true) {
|
|
733
422
|
const rawReason = node.lockReason;
|
|
734
|
-
const reason = rawReason === void 0 ? void 0 :
|
|
735
|
-
resource,
|
|
736
|
-
default: rawReason
|
|
737
|
-
});
|
|
423
|
+
const reason = rawReason === void 0 ? void 0 : reasonText(caps, rawReason, resource);
|
|
738
424
|
const locked = tid.formFieldLocked(resource, mode, name);
|
|
739
425
|
return jsxs("div", {
|
|
740
426
|
...attributes,
|
|
@@ -742,8 +428,8 @@ function RecordLeaf({ node }) {
|
|
|
742
428
|
"data-testid": tid.formField(resource, mode, name),
|
|
743
429
|
children: [jsxs("div", {
|
|
744
430
|
"data-buro-region": "field-label",
|
|
745
|
-
className: "flex items-center gap-2",
|
|
746
|
-
children: [jsx(Label, { children: label }), jsx(DocInfo, {
|
|
431
|
+
className: labelRegion(hidden, "flex items-center gap-2"),
|
|
432
|
+
children: [jsx(Label, { children: label }), hidden ? null : jsx(DocInfo, {
|
|
747
433
|
address: `${resource}/fields/${name}`,
|
|
748
434
|
name: label,
|
|
749
435
|
testId: `${tid.formField(resource, mode, name)}:info`
|
|
@@ -779,14 +465,14 @@ function RecordLeaf({ node }) {
|
|
|
779
465
|
node,
|
|
780
466
|
binding,
|
|
781
467
|
label,
|
|
468
|
+
labelHidden: hidden,
|
|
782
469
|
attributes,
|
|
783
470
|
inputId
|
|
784
471
|
});
|
|
785
472
|
}
|
|
786
|
-
function ActiveLeaf({ node, binding, label, attributes, inputId }) {
|
|
473
|
+
function ActiveLeaf({ node, binding, label, labelHidden, attributes, inputId }) {
|
|
787
474
|
const { resource, mode, controller, disabled: locked } = useScreenTree();
|
|
788
475
|
const caps = useI18nCaps(resource);
|
|
789
|
-
const t = useOptionalT();
|
|
790
476
|
const tid = useTestIds();
|
|
791
477
|
const name = binding.name;
|
|
792
478
|
const record = controller.record;
|
|
@@ -802,24 +488,21 @@ function ActiveLeaf({ node, binding, label, attributes, inputId }) {
|
|
|
802
488
|
const messageId = `${mode}-${name}-msg`;
|
|
803
489
|
const placeholder = resolveFieldSlot(caps, name, "placeholder");
|
|
804
490
|
const hint = resolveFieldSlot(caps, name, "description", fieldDescription(binding.field));
|
|
491
|
+
const chromeT = useChromeT();
|
|
805
492
|
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 }
|
|
493
|
+
const text = (issue) => resolveValidationMessage({
|
|
494
|
+
code: issue.code ?? "",
|
|
495
|
+
message: issue.message,
|
|
496
|
+
...issue.check === void 0 ? {} : { check: issue.check }
|
|
819
497
|
}, {
|
|
820
|
-
|
|
498
|
+
has: caps.has,
|
|
499
|
+
t: caps.t,
|
|
821
500
|
fieldKey
|
|
822
501
|
});
|
|
502
|
+
const shown = binding.issues.slice(0, SHOWN_ISSUES);
|
|
503
|
+
const more = binding.issues.length - shown.length;
|
|
504
|
+
const firstError = shown.find((issue) => issue.severity === "error");
|
|
505
|
+
const errorText = firstError === void 0 ? null : text(firstError);
|
|
823
506
|
const disabled = binding.disabled || controller.isSubmitting || locked === true;
|
|
824
507
|
const describedBy = errorText === null && hint ? messageId : void 0;
|
|
825
508
|
const errorMessageId = errorText === null ? void 0 : messageId;
|
|
@@ -831,12 +514,13 @@ function ActiveLeaf({ node, binding, label, attributes, inputId }) {
|
|
|
831
514
|
onBlur: binding.onBlur,
|
|
832
515
|
children: [jsxs("div", {
|
|
833
516
|
"data-buro-region": "field-label",
|
|
834
|
-
className: "flex items-center gap-2",
|
|
517
|
+
className: labelRegion(labelHidden, "flex items-center gap-2"),
|
|
835
518
|
children: [jsx(Label, {
|
|
519
|
+
id: `${inputId}-label`,
|
|
836
520
|
htmlFor: inputId,
|
|
837
521
|
required: binding.required,
|
|
838
522
|
children: label
|
|
839
|
-
}), jsx(DocInfo, {
|
|
523
|
+
}), labelHidden ? null : jsx(DocInfo, {
|
|
840
524
|
address: `${resource}/fields/${name}`,
|
|
841
525
|
name: label,
|
|
842
526
|
testId: `${tid.formField(resource, mode, name)}:info`
|
|
@@ -848,6 +532,7 @@ function ActiveLeaf({ node, binding, label, attributes, inputId }) {
|
|
|
848
532
|
resource: binding.resource,
|
|
849
533
|
name,
|
|
850
534
|
inputId,
|
|
535
|
+
labelId: `${inputId}-label`,
|
|
851
536
|
field: binding.field,
|
|
852
537
|
value: binding.value,
|
|
853
538
|
onChange: binding.onChange,
|
|
@@ -858,11 +543,17 @@ function ActiveLeaf({ node, binding, label, attributes, inputId }) {
|
|
|
858
543
|
...mode === "create" || record === void 0 ? {} : { record },
|
|
859
544
|
form: controller.values,
|
|
860
545
|
metaSources: controller.sources,
|
|
546
|
+
...controller.baseline === void 0 ? {} : { baseline: controller.baseline },
|
|
861
547
|
...describedBy === void 0 ? {} : { descriptionId: describedBy },
|
|
862
548
|
...errorMessageId === void 0 ? {} : { errorId: errorMessageId },
|
|
863
549
|
errorMessage: errorText,
|
|
550
|
+
issues: binding.issues,
|
|
864
551
|
...binding.fixedFilters === void 0 ? {} : { fixedFilters: binding.fixedFilters },
|
|
552
|
+
...binding.optionSubtitle === void 0 ? {} : { optionSubtitle: binding.optionSubtitle },
|
|
865
553
|
...binding.relationSeeds === void 0 ? {} : { relationSeeds: binding.relationSeeds },
|
|
554
|
+
...binding.link === void 0 ? {} : { link: binding.link },
|
|
555
|
+
...binding.absentPaths === void 0 ? {} : { absentPaths: binding.absentPaths },
|
|
556
|
+
...binding.pathStatus === void 0 ? {} : { pathStatus: binding.pathStatus },
|
|
866
557
|
fallback: jsx(Input, {
|
|
867
558
|
id: inputId,
|
|
868
559
|
name: inputId,
|
|
@@ -877,18 +568,23 @@ function ActiveLeaf({ node, binding, label, attributes, inputId }) {
|
|
|
877
568
|
...errorMessageId === void 0 ? {} : { "aria-errormessage": errorMessageId },
|
|
878
569
|
onChange: (event) => binding.onChange(event.target.value)
|
|
879
570
|
})
|
|
880
|
-
}),
|
|
571
|
+
}), shown.length > 0 ? jsxs("div", {
|
|
881
572
|
id: messageId,
|
|
882
573
|
"aria-live": "polite",
|
|
883
|
-
className: "text-sm text-destructive",
|
|
884
|
-
"data-testid": tid.formFieldError(resource, mode, name),
|
|
885
|
-
children:
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
574
|
+
className: cn("space-y-0.5 text-sm", errorText === null ? "text-warning-emphasis" : "text-destructive"),
|
|
575
|
+
"data-testid": errorText === null ? `form-field-warning:${resource}:${mode}:${name}` : tid.formFieldError(resource, mode, name),
|
|
576
|
+
children: [shown.map((issue, index) => jsx("p", {
|
|
577
|
+
"data-buro-severity": issue.severity,
|
|
578
|
+
className: issue.severity === "error" ? "text-destructive" : "text-warning-emphasis",
|
|
579
|
+
children: text(issue)
|
|
580
|
+
}, index)), more > 0 ? jsx("p", {
|
|
581
|
+
className: "text-muted-foreground",
|
|
582
|
+
"data-testid": `form-field-more:${resource}:${mode}:${name}`,
|
|
583
|
+
children: chromeT("validation.moreIssues", {
|
|
584
|
+
count: more,
|
|
585
|
+
defaultValue: "+{{count}} more"
|
|
586
|
+
})
|
|
587
|
+
}) : null]
|
|
892
588
|
}) : hint ? jsx("p", {
|
|
893
589
|
id: messageId,
|
|
894
590
|
"data-testid": tid.formFieldHelp(resource, mode, name),
|
|
@@ -904,11 +600,12 @@ function DeclarativeLeaf({ node, prefix, inputIds }) {
|
|
|
904
600
|
const binding = controller.field(name);
|
|
905
601
|
const size = useLeafSize(binding.field);
|
|
906
602
|
const weight = compiled.nodes[node.index]?.options.weight;
|
|
603
|
+
const hidden = labelHiddenOf(compiled, node);
|
|
907
604
|
const attributes = leafAttributes(size, weight);
|
|
908
605
|
const docRef = i18nPrefixToRef(prefix);
|
|
909
606
|
const docScope = docRef.namespace === "default" ? docRef.id : formatEntityRef(docRef);
|
|
910
607
|
const caps = useI18nCaps(docScope);
|
|
911
|
-
const
|
|
608
|
+
const issueCaps = useI18nCaps();
|
|
912
609
|
const uid = useId();
|
|
913
610
|
const root = useRef(null);
|
|
914
611
|
const inputId = `${inputIds ?? uid}-${name}`;
|
|
@@ -922,7 +619,7 @@ function DeclarativeLeaf({ node, prefix, inputIds }) {
|
|
|
922
619
|
});
|
|
923
620
|
const label = resolveFieldLabel(caps, name, declaredText("field", binding.field, "label"));
|
|
924
621
|
const hint = resolveFieldSlot(caps, name, "description", fieldDescription(binding.field));
|
|
925
|
-
const info = jsx(DocInfo, {
|
|
622
|
+
const info = hidden ? null : jsx(DocInfo, {
|
|
926
623
|
address: `${docScope}/fields/${name}`,
|
|
927
624
|
name: label,
|
|
928
625
|
testId: `${prefix}:field:${name}:info`
|
|
@@ -939,7 +636,7 @@ function DeclarativeLeaf({ node, prefix, inputIds }) {
|
|
|
939
636
|
children: [
|
|
940
637
|
jsxs("div", {
|
|
941
638
|
"data-buro-region": "field-label",
|
|
942
|
-
className: "flex items-center gap-2",
|
|
639
|
+
className: labelRegion(hidden, "flex items-center gap-2"),
|
|
943
640
|
children: [jsx("span", {
|
|
944
641
|
className: FIELD_LABEL_SHOW,
|
|
945
642
|
children: label
|
|
@@ -962,16 +659,19 @@ function DeclarativeLeaf({ node, prefix, inputIds }) {
|
|
|
962
659
|
const text = (issue) => resolveValidationMessage({
|
|
963
660
|
code: issue.code ?? "",
|
|
964
661
|
message: issue.message,
|
|
965
|
-
...issue.
|
|
662
|
+
...issue.check === void 0 ? {} : { check: issue.check }
|
|
966
663
|
}, {
|
|
967
|
-
|
|
664
|
+
has: issueCaps.has,
|
|
665
|
+
t: issueCaps.t,
|
|
968
666
|
fieldKey
|
|
969
667
|
});
|
|
970
668
|
const defaultNode = jsx(Field, {
|
|
971
669
|
...binding,
|
|
972
670
|
field: binding.field,
|
|
973
671
|
metaSources: controller.sources,
|
|
672
|
+
...controller.baseline === void 0 ? {} : { baseline: controller.baseline },
|
|
974
673
|
inputId,
|
|
674
|
+
labelId: `${inputId}-label`,
|
|
975
675
|
i18nPrefix: prefix,
|
|
976
676
|
disabled: disabled === true || binding.disabled
|
|
977
677
|
});
|
|
@@ -985,8 +685,9 @@ function DeclarativeLeaf({ node, prefix, inputIds }) {
|
|
|
985
685
|
children: [
|
|
986
686
|
jsxs("div", {
|
|
987
687
|
"data-buro-region": "field-label",
|
|
988
|
-
className: "flex items-center gap-2",
|
|
688
|
+
className: labelRegion(hidden, "flex items-center gap-2"),
|
|
989
689
|
children: [jsx(Label, {
|
|
690
|
+
id: `${inputId}-label`,
|
|
990
691
|
htmlFor: inputId,
|
|
991
692
|
required: binding.required,
|
|
992
693
|
children: label
|
|
@@ -1027,9 +728,10 @@ function Notice({ children }) {
|
|
|
1027
728
|
children
|
|
1028
729
|
});
|
|
1029
730
|
}
|
|
1030
|
-
function FactsView({ props }) {
|
|
731
|
+
function FactsView({ props, empty }) {
|
|
1031
732
|
const own = props;
|
|
1032
733
|
const t = useOptionalT();
|
|
734
|
+
const chromeT = useChromeT();
|
|
1033
735
|
const snapshot = useApplicationSnapshot();
|
|
1034
736
|
const queryId = formatEntityRef(own.query);
|
|
1035
737
|
const caps = useI18nCaps();
|
|
@@ -1046,7 +748,10 @@ function FactsView({ props }) {
|
|
|
1046
748
|
kind: "custom-query",
|
|
1047
749
|
ref: own.query
|
|
1048
750
|
}), [own.query]);
|
|
1049
|
-
const result = useCustomQuery(handle, blocked ? {} : input, {
|
|
751
|
+
const result = useCustomQuery(handle, blocked ? {} : input, {
|
|
752
|
+
enabled: !blocked,
|
|
753
|
+
...own.refetchInterval === void 0 ? {} : { refetchInterval: own.refetchInterval }
|
|
754
|
+
});
|
|
1050
755
|
const shown = useMemo(() => {
|
|
1051
756
|
const byName = new Map(fields.map((field) => [field.name, field]));
|
|
1052
757
|
return (own.rows ?? fields.map((field) => field.name)).flatMap((name) => {
|
|
@@ -1054,7 +759,7 @@ function FactsView({ props }) {
|
|
|
1054
759
|
return field === void 0 ? [] : [field];
|
|
1055
760
|
});
|
|
1056
761
|
}, [fields, own.rows]);
|
|
1057
|
-
if (blocked) return jsx(Notice, { children: own.missing ?? t("facts.empty", { defaultValue: "No data" }) });
|
|
762
|
+
if (blocked) return jsx(Notice, { children: own.missing ?? empty ?? t("facts.empty", { defaultValue: "No data" }) });
|
|
1058
763
|
if (result.isError) return jsxs("div", {
|
|
1059
764
|
role: "alert",
|
|
1060
765
|
className: "flex flex-wrap items-center gap-2 rounded-md border border-dashed px-3 py-2 text-sm text-muted-foreground",
|
|
@@ -1063,7 +768,7 @@ function FactsView({ props }) {
|
|
|
1063
768
|
variant: "outline",
|
|
1064
769
|
size: "sm",
|
|
1065
770
|
onClick: () => void result.refetch(),
|
|
1066
|
-
children:
|
|
771
|
+
children: chromeT("actions.retry", { defaultValue: "Retry" })
|
|
1067
772
|
})]
|
|
1068
773
|
});
|
|
1069
774
|
if (result.data === void 0) return own.view === "list" ? jsx(Skeleton, {
|
|
@@ -1077,7 +782,7 @@ function FactsView({ props }) {
|
|
|
1077
782
|
}, field.name))
|
|
1078
783
|
});
|
|
1079
784
|
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" }) });
|
|
785
|
+
if (data === null || typeof data !== "object" || shown.length === 0) return jsx(Notice, { children: own.missing ?? empty ?? t("facts.empty", { defaultValue: "No data" }) });
|
|
1081
786
|
const pairs = shown.map((field) => {
|
|
1082
787
|
const value = data[field.name];
|
|
1083
788
|
const label = resolveContentSlot(caps, [`queries.${queryId}.fields.${field.name}.label`, `queries.${queryId}.fields.${field.name}`], {
|
|
@@ -1087,13 +792,18 @@ function FactsView({ props }) {
|
|
|
1087
792
|
const body = value === null || value === void 0 ? jsx("span", {
|
|
1088
793
|
className: "text-muted-foreground",
|
|
1089
794
|
children: "—"
|
|
1090
|
-
}) : jsx(
|
|
1091
|
-
resource: own.query,
|
|
1092
|
-
name: field.name,
|
|
795
|
+
}) : jsx(FieldValueFrame, {
|
|
1093
796
|
field,
|
|
1094
797
|
value,
|
|
1095
|
-
|
|
1096
|
-
|
|
798
|
+
surface: "display",
|
|
799
|
+
children: jsx(ResolvedFieldDisplay, {
|
|
800
|
+
resource: own.query,
|
|
801
|
+
name: field.name,
|
|
802
|
+
field,
|
|
803
|
+
value,
|
|
804
|
+
record: data,
|
|
805
|
+
fallback: toDisplayText(value).text
|
|
806
|
+
})
|
|
1097
807
|
});
|
|
1098
808
|
return {
|
|
1099
809
|
name: field.name,
|
|
@@ -1147,6 +857,9 @@ const PlacementFrameContext = createContext(null);
|
|
|
1147
857
|
function usePlacementFrame() {
|
|
1148
858
|
return useContext(PlacementFrameContext);
|
|
1149
859
|
}
|
|
860
|
+
function placementLayoutId(frame) {
|
|
861
|
+
return frame?.owner === void 0 ? void 0 : `${frame.owner}/${frame.key.split(".").join("/")}`;
|
|
862
|
+
}
|
|
1150
863
|
//#endregion
|
|
1151
864
|
//#region src/flows/app-flow-button.tsx
|
|
1152
865
|
function AppFlowButton({ action, variant = "ghost", size = "sm", className }) {
|
|
@@ -1234,10 +947,7 @@ function RecordInlineFlow({ name }) {
|
|
|
1234
947
|
const id = frame?.recordId;
|
|
1235
948
|
const asRead = frame === null ? void 0 : policyRecordOf(frame.controller, frame.record);
|
|
1236
949
|
const key = placement?.key ?? flowName;
|
|
1237
|
-
const state = frame === null || id === void 0 || asRead === void 0 ? null : resolve(flowName, {
|
|
1238
|
-
...asRead,
|
|
1239
|
-
id
|
|
1240
|
-
});
|
|
950
|
+
const state = frame === null || id === void 0 || asRead === void 0 ? null : resolve(flowName, withRecordKeys(asRead, { id }));
|
|
1241
951
|
const kept = holder?.get(key);
|
|
1242
952
|
const stale = state?.state === "absent" && state.cause === "when" && kept !== void 0 && kept.dirty;
|
|
1243
953
|
const gone = state?.state === "absent" && !stale;
|
|
@@ -1249,10 +959,7 @@ function RecordInlineFlow({ name }) {
|
|
|
1249
959
|
key
|
|
1250
960
|
]);
|
|
1251
961
|
if (frame === null || asRead === void 0 || id === void 0 || compiled === null) return null;
|
|
1252
|
-
const row = {
|
|
1253
|
-
...asRead,
|
|
1254
|
-
id
|
|
1255
|
-
};
|
|
962
|
+
const row = withRecordKeys(asRead, { id });
|
|
1256
963
|
const control = resolve(flowName, row);
|
|
1257
964
|
switch (control.state) {
|
|
1258
965
|
case "undecided":
|
|
@@ -1266,7 +973,7 @@ function RecordInlineFlow({ name }) {
|
|
|
1266
973
|
return jsx(InlineHostLoader, {
|
|
1267
974
|
resource,
|
|
1268
975
|
name: flowName,
|
|
1269
|
-
|
|
976
|
+
...hostRecord(flowRecordOf(compiled, row)),
|
|
1270
977
|
placementKey: key,
|
|
1271
978
|
lease: kept,
|
|
1272
979
|
stale: true,
|
|
@@ -1282,7 +989,7 @@ function RecordInlineFlow({ name }) {
|
|
|
1282
989
|
if (control.state === "disabled" && kept !== void 0 && kept.dirty) return jsx(InlineHostLoader, {
|
|
1283
990
|
resource,
|
|
1284
991
|
name: flowName,
|
|
1285
|
-
|
|
992
|
+
...hostRecord(flowRecordOf(compiled, row)),
|
|
1286
993
|
placementKey: key,
|
|
1287
994
|
lease: kept,
|
|
1288
995
|
stale: true,
|
|
@@ -1294,11 +1001,12 @@ function RecordInlineFlow({ name }) {
|
|
|
1294
1001
|
return jsx(InlineHostLoader, {
|
|
1295
1002
|
resource,
|
|
1296
1003
|
name: flowName,
|
|
1297
|
-
|
|
1004
|
+
...hostRecord(flowRecordOf(compiled, control.record ?? row)),
|
|
1298
1005
|
placementKey: key,
|
|
1299
1006
|
...lease === void 0 ? {} : { lease }
|
|
1300
1007
|
});
|
|
1301
1008
|
}
|
|
1009
|
+
const hostRecord = (record) => record === void 0 ? {} : { record };
|
|
1302
1010
|
function InlineHostLoader(props) {
|
|
1303
1011
|
const loaded = useLoaded(loadInlineFlowHost);
|
|
1304
1012
|
if (loaded.error !== void 0) return jsx("p", {
|
|
@@ -1514,6 +1222,7 @@ function placementSeeds(where, resolved, closure) {
|
|
|
1514
1222
|
where.forEach((entry, index) => {
|
|
1515
1223
|
const value = resolved[index]?.value;
|
|
1516
1224
|
if (entry.op !== "eq" || value === null || value === void 0) return;
|
|
1225
|
+
if (typeof entry.field !== "string" || entry.field.includes(".")) return;
|
|
1517
1226
|
if (resolved[index]?.field !== entry.field) return;
|
|
1518
1227
|
const source = refSource(entry.value);
|
|
1519
1228
|
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;
|
|
@@ -1521,6 +1230,28 @@ function placementSeeds(where, resolved, closure) {
|
|
|
1521
1230
|
return Object.freeze(seeds);
|
|
1522
1231
|
}
|
|
1523
1232
|
//#endregion
|
|
1233
|
+
//#region src/enum/enum-settling.tsx
|
|
1234
|
+
function unknownEnumValueLabel(caps, def, ns, value) {
|
|
1235
|
+
const declared = enumFallbackText(def);
|
|
1236
|
+
if (def.fallback === void 0 && declared === void 0) return {
|
|
1237
|
+
label: value,
|
|
1238
|
+
fallback: false
|
|
1239
|
+
};
|
|
1240
|
+
return {
|
|
1241
|
+
label: resolveContentSlot(caps, [enumFallbackKey(ns)], { default: declared }) ?? value,
|
|
1242
|
+
fallback: true
|
|
1243
|
+
};
|
|
1244
|
+
}
|
|
1245
|
+
function EnumSettlingRow({ status, testid }) {
|
|
1246
|
+
const t = useOptionalT();
|
|
1247
|
+
return jsx("div", {
|
|
1248
|
+
role: "status",
|
|
1249
|
+
"data-testid": testid === void 0 ? void 0 : `${testid}:${status}`,
|
|
1250
|
+
className: "px-2 py-1.5 text-sm text-muted-foreground",
|
|
1251
|
+
children: status === "pending" ? t("field.loading", { defaultValue: "Loading…" }) : t("relation.failed", { defaultValue: "Failed to load options." })
|
|
1252
|
+
});
|
|
1253
|
+
}
|
|
1254
|
+
//#endregion
|
|
1524
1255
|
//#region src/enum/enum-badge.tsx
|
|
1525
1256
|
function BadgeBody({ icon, label }) {
|
|
1526
1257
|
return jsxs("span", {
|
|
@@ -1531,7 +1262,8 @@ function BadgeBody({ icon, label }) {
|
|
|
1531
1262
|
}) : null, label]
|
|
1532
1263
|
});
|
|
1533
1264
|
}
|
|
1534
|
-
function EnumBadge({ enumName, value, testid, className, presentation }) {
|
|
1265
|
+
function EnumBadge({ enumName: enumInput, value, testid, className, presentation }) {
|
|
1266
|
+
const enumName = enumBindingKey(enumInput) ?? "";
|
|
1535
1267
|
const app = useApp();
|
|
1536
1268
|
const caps = useI18nCaps();
|
|
1537
1269
|
const def = app.registry.enums.get(enumName);
|
|
@@ -1590,9 +1322,9 @@ function EnumBadge({ enumName, value, testid, className, presentation }) {
|
|
|
1590
1322
|
children: body
|
|
1591
1323
|
});
|
|
1592
1324
|
}
|
|
1593
|
-
const
|
|
1594
|
-
if (
|
|
1595
|
-
const fallbackLabel =
|
|
1325
|
+
const unknown = unknownEnumValueLabel(caps, def, ns, value);
|
|
1326
|
+
if (unknown.fallback) {
|
|
1327
|
+
const fallbackLabel = unknown.label;
|
|
1596
1328
|
if (presentation === "plain") return jsx("span", {
|
|
1597
1329
|
"data-testid": `${id}:fallback`,
|
|
1598
1330
|
className,
|
|
@@ -1677,6 +1409,7 @@ function useExportRunWatcher() {
|
|
|
1677
1409
|
const runtime = useDataRuntime();
|
|
1678
1410
|
const runs = useExportRuns();
|
|
1679
1411
|
const t = useOptionalT();
|
|
1412
|
+
const caps = useI18nCaps();
|
|
1680
1413
|
React.useEffect(() => {
|
|
1681
1414
|
const announced = announcedRunsFor(runtime);
|
|
1682
1415
|
const unsubscribes = runs.map((run) => {
|
|
@@ -1695,7 +1428,7 @@ function useExportRunWatcher() {
|
|
|
1695
1428
|
label: t("export.toast.download"),
|
|
1696
1429
|
onClick: () => downloadArtifact(artifact)
|
|
1697
1430
|
} });
|
|
1698
|
-
} else if (state.status === "failed") toast.error(t("export.toast.failed", { params: { message: state.error
|
|
1431
|
+
} else if (state.status === "failed") toast.error(t("export.toast.failed", { params: { message: resolveErrorText(state.error, caps) } }));
|
|
1699
1432
|
};
|
|
1700
1433
|
check();
|
|
1701
1434
|
return run.handle.subscribe(check);
|
|
@@ -1706,24 +1439,27 @@ function useExportRunWatcher() {
|
|
|
1706
1439
|
}, [
|
|
1707
1440
|
runs,
|
|
1708
1441
|
runtime,
|
|
1709
|
-
t
|
|
1442
|
+
t,
|
|
1443
|
+
caps
|
|
1710
1444
|
]);
|
|
1711
1445
|
}
|
|
1712
1446
|
//#endregion
|
|
1713
1447
|
//#region src/actions/action-overflow.tsx
|
|
1714
1448
|
const RowActionsCollapseContext = createContext(false);
|
|
1715
|
-
function ActionOverflow({ resource, record, surface, className, hideFlows, collapse }) {
|
|
1716
|
-
const
|
|
1449
|
+
function ActionOverflow({ resource: resourceInput, record, surface, className, hideFlows, collapse, sources }) {
|
|
1450
|
+
const resource = routeResourceKey(resourceInput);
|
|
1451
|
+
const chromeT = useChromeT();
|
|
1717
1452
|
const tid = useTestIds();
|
|
1453
|
+
const recordKey = String(recordKeyOf(useApp().registry.get(resource), record) ?? "");
|
|
1718
1454
|
const collapsedRows = useContext(RowActionsCollapseContext);
|
|
1719
1455
|
const [flowFor, setFlowFor] = useState(null);
|
|
1720
|
-
const actions = useShownItemActions(resource, record, surface, hideFlows);
|
|
1456
|
+
const actions = useShownItemActions(resource, record, surface, hideFlows, sources);
|
|
1721
1457
|
if (actions.length === 0) return null;
|
|
1722
1458
|
const isRow = surface === "row";
|
|
1723
1459
|
const folded = isRow && (collapse ?? collapsedRows);
|
|
1724
1460
|
const standalone = folded ? [] : actions.filter((a) => a.placement === "standalone");
|
|
1725
1461
|
const menu = folded ? actions : actions.filter((a) => a.placement === "menu");
|
|
1726
|
-
const menuTestId = isRow ? tid.listRowActionsMenu(resource,
|
|
1462
|
+
const menuTestId = isRow ? tid.listRowActionsMenu(resource, recordKey) : tid.formActionsMenu(resource, surface);
|
|
1727
1463
|
const firstDestructive = menu.findIndex((a) => a.variant === "destructive");
|
|
1728
1464
|
const overlays = jsx(Fragment$1, { children: flowFor != null ? jsx(RuntimeFlowRunner, {
|
|
1729
1465
|
resource,
|
|
@@ -1737,6 +1473,7 @@ function ActionOverflow({ resource, record, surface, className, hideFlows, colla
|
|
|
1737
1473
|
actionName: a.name,
|
|
1738
1474
|
record,
|
|
1739
1475
|
scope: surface,
|
|
1476
|
+
...sources === void 0 ? {} : { sources },
|
|
1740
1477
|
density: a.density
|
|
1741
1478
|
}, a.name));
|
|
1742
1479
|
return jsxs("div", {
|
|
@@ -1760,7 +1497,7 @@ function ActionOverflow({ resource, record, surface, className, hideFlows, colla
|
|
|
1760
1497
|
size: "icon",
|
|
1761
1498
|
className: "size-7",
|
|
1762
1499
|
"data-testid": menuTestId,
|
|
1763
|
-
"aria-label":
|
|
1500
|
+
"aria-label": chromeT("actions.more", { defaultValue: "More" }),
|
|
1764
1501
|
children: jsx(IconDots, {
|
|
1765
1502
|
className: "h-4 w-4",
|
|
1766
1503
|
"aria-hidden": "true"
|
|
@@ -1774,15 +1511,16 @@ function ActionOverflow({ resource, record, surface, className, hideFlows, colla
|
|
|
1774
1511
|
children: [jsx(IconDots, {
|
|
1775
1512
|
className: "h-4 w-4",
|
|
1776
1513
|
"aria-hidden": "true"
|
|
1777
|
-
}),
|
|
1514
|
+
}), chromeT("actions.more", { defaultValue: "More" })]
|
|
1778
1515
|
})
|
|
1779
1516
|
}), jsx(DropdownMenuContent, {
|
|
1780
1517
|
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}:${
|
|
1518
|
+
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
1519
|
resource,
|
|
1783
1520
|
actionName: a.name,
|
|
1784
1521
|
record,
|
|
1785
1522
|
scope: surface,
|
|
1523
|
+
...sources === void 0 ? {} : { sources },
|
|
1786
1524
|
asMenuItem: true
|
|
1787
1525
|
}) : jsx(FlowMenuItem, {
|
|
1788
1526
|
resource,
|
|
@@ -1802,46 +1540,18 @@ function ActionOverflow({ resource, record, surface, className, hideFlows, colla
|
|
|
1802
1540
|
]
|
|
1803
1541
|
});
|
|
1804
1542
|
}
|
|
1805
|
-
function useShownItemActions(resource, record, surface, hideFlows) {
|
|
1543
|
+
function useShownItemActions(resource, record, surface, hideFlows, sources) {
|
|
1806
1544
|
const resolved = useItemActions({
|
|
1807
1545
|
resource,
|
|
1808
1546
|
record,
|
|
1809
|
-
surface
|
|
1547
|
+
surface,
|
|
1548
|
+
...sources === void 0 ? {} : { sources }
|
|
1810
1549
|
});
|
|
1811
1550
|
const def = useApp().registry.get(resource);
|
|
1812
1551
|
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;
|
|
1813
1552
|
return hideFlows === void 0 ? shown : shown.filter((a) => a.flow === void 0 || !hideFlows(a.name));
|
|
1814
1553
|
}
|
|
1815
1554
|
//#endregion
|
|
1816
|
-
//#region src/layout/record-targets.ts
|
|
1817
|
-
function recordRouteAvailable(registry, resource, action) {
|
|
1818
|
-
if (resolveStaticAvailability(registry, resource, action).state !== "available") return false;
|
|
1819
|
-
const def = registry.get(resource);
|
|
1820
|
-
return def !== void 0 && hasRecordPage(def, action);
|
|
1821
|
-
}
|
|
1822
|
-
function isShowAvailable(registry, resource) {
|
|
1823
|
-
return recordRouteAvailable(registry, resource, "show");
|
|
1824
|
-
}
|
|
1825
|
-
function showTarget(resource, id) {
|
|
1826
|
-
return {
|
|
1827
|
-
resource,
|
|
1828
|
-
action: "show",
|
|
1829
|
-
params: { id }
|
|
1830
|
-
};
|
|
1831
|
-
}
|
|
1832
|
-
function listTarget(resource) {
|
|
1833
|
-
return {
|
|
1834
|
-
resource,
|
|
1835
|
-
action: "list"
|
|
1836
|
-
};
|
|
1837
|
-
}
|
|
1838
|
-
function recordReturnTarget(registry, resource, id) {
|
|
1839
|
-
return isShowAvailable(registry, resource) ? showTarget(resource, id) : listTarget(resource);
|
|
1840
|
-
}
|
|
1841
|
-
function editBackHref(registry, resource, id, backHref) {
|
|
1842
|
-
return backHref ?? resolveRoute(recordReturnTarget(registry, resource, id), registry);
|
|
1843
|
-
}
|
|
1844
|
-
//#endregion
|
|
1845
1555
|
//#region src/lib/typography.ts
|
|
1846
1556
|
const typography = {
|
|
1847
1557
|
h1: "text-2xl font-semibold tracking-tight",
|
|
@@ -1899,8 +1609,14 @@ function DocumentTitleProvider({ routeKey, children }) {
|
|
|
1899
1609
|
});
|
|
1900
1610
|
}
|
|
1901
1611
|
//#endregion
|
|
1612
|
+
//#region src/resource/resource-title.ts
|
|
1613
|
+
function resolveResourceTitle(caps, t, resource, slot, params) {
|
|
1614
|
+
return resolveContentSlot(caps, [`resources.${resource}.${slot}`], { params: { ...params } }) ?? t(`pageHeader.${slot}`, { params: { ...params } });
|
|
1615
|
+
}
|
|
1616
|
+
//#endregion
|
|
1902
1617
|
//#region src/resource/page-header.tsx
|
|
1903
|
-
function PageHeader({ resource, mode, recordName, backHref, backLabel, actions, title, breadcrumbs, backLink = false, visibleTitle = false, labelOverride, className }) {
|
|
1618
|
+
function PageHeader({ resource: resourceInput, mode, recordName, backHref, backLabel, actions, title, breadcrumbs, backLink = false, visibleTitle = false, labelOverride, className }) {
|
|
1619
|
+
const resource = routeResourceKey(resourceInput);
|
|
1904
1620
|
const app = useApp();
|
|
1905
1621
|
const t = useT();
|
|
1906
1622
|
const def = app.registry.get(resource);
|
|
@@ -1909,10 +1625,10 @@ function PageHeader({ resource, mode, recordName, backHref, backLabel, actions,
|
|
|
1909
1625
|
const resourceSingle = resolveResourceLabel(caps, resource, "single", def);
|
|
1910
1626
|
const labelSingle = labelOverride ?? resourceSingle;
|
|
1911
1627
|
const labelPlural = resolveResourceLabel(caps, resource, "plural", def);
|
|
1912
|
-
const computedTitle = title ?? (mode === "list" ? labelPlural : mode === "show" ? recordName ?? labelSingle : mode === "edit" ? recordName ? t
|
|
1628
|
+
const computedTitle = title ?? (mode === "list" ? labelPlural : mode === "show" ? recordName ?? labelSingle : mode === "edit" ? recordName ? resolveResourceTitle(caps, t, resource, "editTitleNamed", {
|
|
1913
1629
|
label: labelSingle,
|
|
1914
1630
|
name: recordName
|
|
1915
|
-
}
|
|
1631
|
+
}) : resolveResourceTitle(caps, t, resource, "editTitle", { label: labelSingle }) : resolveResourceTitle(caps, t, resource, "createTitle", { label: labelSingle }));
|
|
1916
1632
|
useDocumentTitle(app?.name ? `${computedTitle} · ${app.name}` : computedTitle);
|
|
1917
1633
|
const showBack = backLink && mode !== "list" && !breadcrumbs;
|
|
1918
1634
|
const computedBackHref = backHref ?? def?.basePath ?? `/${resource}`;
|
|
@@ -1943,9 +1659,10 @@ function PageHeader({ resource, mode, recordName, backHref, backLabel, actions,
|
|
|
1943
1659
|
]
|
|
1944
1660
|
});
|
|
1945
1661
|
}
|
|
1946
|
-
function ShowPageHeader({ resource, record, extraActions, breadcrumbs }) {
|
|
1662
|
+
function ShowPageHeader({ resource: resourceInput, record, extraActions, breadcrumbs }) {
|
|
1663
|
+
const resource = routeResourceKey(resourceInput);
|
|
1947
1664
|
const def = useApp().registry.get(resource);
|
|
1948
|
-
const recordName = useProjectedTitle(resource, record
|
|
1665
|
+
const recordName = useProjectedTitle(resource, recordKeyOf(def, record), record);
|
|
1949
1666
|
usePublishDocVariant(resource, record);
|
|
1950
1667
|
if (!def) return null;
|
|
1951
1668
|
return jsx(PageHeader, {
|
|
@@ -1960,16 +1677,18 @@ function ShowPageHeader({ resource, record, extraActions, breadcrumbs }) {
|
|
|
1960
1677
|
}), extraActions] })
|
|
1961
1678
|
});
|
|
1962
1679
|
}
|
|
1963
|
-
function EditPageHeader({ resource, record, backHref, breadcrumbs }) {
|
|
1964
|
-
const
|
|
1965
|
-
const def =
|
|
1680
|
+
function EditPageHeader({ resource: resourceInput, record, backHref, breadcrumbs }) {
|
|
1681
|
+
const resource = routeResourceKey(resourceInput);
|
|
1682
|
+
const def = useApp().registry.get(resource);
|
|
1966
1683
|
const caps = useI18nCaps(resource);
|
|
1967
|
-
const
|
|
1684
|
+
const recordKey = recordKeyOf(def, record);
|
|
1685
|
+
const recordName = useProjectedTitle(resource, recordKey, record);
|
|
1686
|
+
const back = useReturnTarget(resource, "edit", String(recordKey ?? ""));
|
|
1968
1687
|
return jsx(PageHeader, {
|
|
1969
1688
|
resource,
|
|
1970
1689
|
mode: "edit",
|
|
1971
1690
|
recordName,
|
|
1972
|
-
backHref:
|
|
1691
|
+
backHref: backHref ?? back,
|
|
1973
1692
|
breadcrumbs,
|
|
1974
1693
|
backLabel: recordName ? `${resolveResourceLabel(caps, resource, "single", def)}: ${recordName}` : void 0,
|
|
1975
1694
|
actions: jsx(ActionOverflow, {
|
|
@@ -2169,7 +1888,8 @@ function ItemNode({ node, row, fallback }) {
|
|
|
2169
1888
|
const viewProps = {
|
|
2170
1889
|
props,
|
|
2171
1890
|
data: {},
|
|
2172
|
-
t: placement.viewProps?.t ?? ((name) => name)
|
|
1891
|
+
t: placement.viewProps?.t ?? ((name) => name),
|
|
1892
|
+
...placement.viewProps?.empty === void 0 ? {} : { empty: placement.viewProps.empty }
|
|
2173
1893
|
};
|
|
2174
1894
|
return jsx(View, { ...viewProps });
|
|
2175
1895
|
}
|
|
@@ -2251,24 +1971,32 @@ function BuroPanelFrame({ body, title, description, collapsible, open, onToggle,
|
|
|
2251
1971
|
const t = useOptionalT();
|
|
2252
1972
|
return jsxs("section", {
|
|
2253
1973
|
...nodeAttributes,
|
|
1974
|
+
...title === void 0 ? {} : { "aria-labelledby": titleId },
|
|
2254
1975
|
children: [
|
|
2255
1976
|
title === void 0 && info === null && !collapsible ? null : jsxs("div", {
|
|
2256
1977
|
"data-buro-region": "panel-title",
|
|
2257
1978
|
className: "flex items-center gap-1",
|
|
2258
1979
|
children: [
|
|
2259
|
-
collapsible ? jsx("
|
|
1980
|
+
collapsible && title !== void 0 ? jsx("h3", {
|
|
1981
|
+
id: titleId,
|
|
1982
|
+
className: "min-w-0 flex-1 text-sm font-semibold",
|
|
1983
|
+
children: jsxs("button", {
|
|
1984
|
+
type: "button",
|
|
1985
|
+
"aria-expanded": open,
|
|
1986
|
+
"aria-controls": bodyId,
|
|
1987
|
+
onClick: () => onToggle(!open),
|
|
1988
|
+
className: "-mx-1 flex w-full items-center gap-1 rounded px-1 text-left hover:bg-muted focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring",
|
|
1989
|
+
children: [jsx(Chevron, { open }), title]
|
|
1990
|
+
})
|
|
1991
|
+
}) : collapsible ? jsx("button", {
|
|
2260
1992
|
type: "button",
|
|
2261
1993
|
"aria-expanded": open,
|
|
2262
1994
|
"aria-controls": bodyId,
|
|
2263
1995
|
"aria-label": open ? t("screen.collapse", { defaultValue: "Collapse" }) : t("screen.expand", { defaultValue: "Expand" }),
|
|
2264
1996
|
onClick: () => onToggle(!open),
|
|
2265
1997
|
className: "inline-flex size-6 items-center justify-center rounded text-muted-foreground hover:bg-muted",
|
|
2266
|
-
children: jsx(
|
|
2267
|
-
|
|
2268
|
-
"aria-hidden": "true"
|
|
2269
|
-
})
|
|
2270
|
-
}) : null,
|
|
2271
|
-
title === void 0 ? null : jsx("h3", {
|
|
1998
|
+
children: jsx(Chevron, { open })
|
|
1999
|
+
}) : title === void 0 ? null : jsx("h3", {
|
|
2272
2000
|
id: titleId,
|
|
2273
2001
|
className: "text-sm font-semibold",
|
|
2274
2002
|
children: title
|
|
@@ -2286,6 +2014,12 @@ function BuroPanelFrame({ body, title, description, collapsible, open, onToggle,
|
|
|
2286
2014
|
]
|
|
2287
2015
|
});
|
|
2288
2016
|
}
|
|
2017
|
+
function Chevron({ open }) {
|
|
2018
|
+
return jsx(IconChevronRight, {
|
|
2019
|
+
className: open ? "h-4 w-4 shrink-0 rotate-90 text-muted-foreground transition-transform" : "h-4 w-4 shrink-0 text-muted-foreground transition-transform",
|
|
2020
|
+
"aria-hidden": "true"
|
|
2021
|
+
});
|
|
2022
|
+
}
|
|
2289
2023
|
function BuroTabsFrame({ tabs, active, onSelect, info, nodeAttributes }) {
|
|
2290
2024
|
if (tabs.length === 1) return jsx("div", {
|
|
2291
2025
|
...nodeAttributes,
|
|
@@ -2310,7 +2044,6 @@ function BuroTabsFrame({ tabs, active, onSelect, info, nodeAttributes }) {
|
|
|
2310
2044
|
}), info]
|
|
2311
2045
|
}), tabs.map((tab) => jsx(TabsContent, {
|
|
2312
2046
|
value: tab.key,
|
|
2313
|
-
forceMount: true,
|
|
2314
2047
|
"data-buro-region": "tab-panel",
|
|
2315
2048
|
"data-buro-key": tab.key,
|
|
2316
2049
|
children: tab.panel
|
|
@@ -2398,17 +2131,17 @@ function BuroStepsFrame({ wizard, steps, goTo, back, next, headingRef, info, nod
|
|
|
2398
2131
|
//#region src/primitives/checkbox.tsx
|
|
2399
2132
|
const Checkbox = React.forwardRef(({ className, checked, ...props }, ref) => jsx(CheckboxPrimitive.Root, {
|
|
2400
2133
|
ref,
|
|
2401
|
-
className: cn("peer h-4 w-4 shrink-0 rounded-
|
|
2134
|
+
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
2135
|
checked,
|
|
2403
2136
|
...props,
|
|
2404
2137
|
children: jsx(CheckboxPrimitive.Indicator, {
|
|
2405
2138
|
className: cn("flex items-center justify-center text-current"),
|
|
2406
2139
|
children: checked === "indeterminate" ? jsx(IconMinus, {
|
|
2407
2140
|
"data-icon": "minus",
|
|
2408
|
-
className: "
|
|
2141
|
+
className: "size-3.5"
|
|
2409
2142
|
}) : jsx(IconCheck, {
|
|
2410
2143
|
"data-icon": "check",
|
|
2411
|
-
className: "
|
|
2144
|
+
className: "size-3.5"
|
|
2412
2145
|
})
|
|
2413
2146
|
})
|
|
2414
2147
|
}));
|
|
@@ -2433,6 +2166,17 @@ function useContainerNarrow(maxRem = 40) {
|
|
|
2433
2166
|
return [setElement, narrow];
|
|
2434
2167
|
}
|
|
2435
2168
|
//#endregion
|
|
2169
|
+
//#region src/data-table/fill-column.ts
|
|
2170
|
+
function fillColumnId(columns) {
|
|
2171
|
+
const eligible = columns.filter((c) => !c.pinned && !c.service);
|
|
2172
|
+
const declared = eligible.find((c) => c.fill);
|
|
2173
|
+
if (declared !== void 0) return declared.id;
|
|
2174
|
+
for (let i = eligible.length - 1; i >= 0; i--) {
|
|
2175
|
+
const c = eligible[i];
|
|
2176
|
+
if (c.resize && c.max === void 0) return c.id;
|
|
2177
|
+
}
|
|
2178
|
+
}
|
|
2179
|
+
//#endregion
|
|
2436
2180
|
//#region src/data-table/data-table.tsx
|
|
2437
2181
|
function pinEdgeClass(isLastLeft, isFirstRight) {
|
|
2438
2182
|
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 +2188,8 @@ function pinEdgeClassFor(column, overflowing) {
|
|
|
2444
2188
|
if (!pinned || !overflowing) return "";
|
|
2445
2189
|
return pinEdgeClass(pinned === "left" && column.getIsLastColumn("left"), pinned === "right" && column.getIsFirstColumn("right"));
|
|
2446
2190
|
}
|
|
2447
|
-
function getPinStyles(column, overflowing) {
|
|
2191
|
+
function getPinStyles(column, overflowing, fill = false) {
|
|
2192
|
+
if (fill) return {};
|
|
2448
2193
|
const explicitWidth = { width: `calc(var(--col-${column.id}-size) * 1px)` };
|
|
2449
2194
|
const pinned = column.getIsPinned();
|
|
2450
2195
|
if (!pinned || !overflowing) return explicitWidth;
|
|
@@ -2469,21 +2214,35 @@ function columnMetaNoRowNav(meta) {
|
|
|
2469
2214
|
function columnMetaNoClip(meta) {
|
|
2470
2215
|
return meta?.noClip === true;
|
|
2471
2216
|
}
|
|
2472
|
-
function
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
|
|
2476
|
-
|
|
2217
|
+
function columnMetaWrap(meta) {
|
|
2218
|
+
return meta?.wrap === true;
|
|
2219
|
+
}
|
|
2220
|
+
function columnMetaFill(meta) {
|
|
2221
|
+
return meta?.fill === true;
|
|
2222
|
+
}
|
|
2223
|
+
function tableFillColumnId(table) {
|
|
2224
|
+
if (table.options.enableColumnResizing !== true) return void 0;
|
|
2225
|
+
return fillColumnId(table.getVisibleLeafColumns().map((c) => {
|
|
2226
|
+
const max = c.columnDef.maxSize;
|
|
2227
|
+
return {
|
|
2228
|
+
id: c.id,
|
|
2229
|
+
fill: columnMetaFill(c.columnDef.meta),
|
|
2230
|
+
resize: c.getCanResize(),
|
|
2231
|
+
...max !== void 0 && max < Number.MAX_SAFE_INTEGER ? { max } : {},
|
|
2232
|
+
pinned: c.getIsPinned() !== false,
|
|
2233
|
+
service: c.id.startsWith("__")
|
|
2234
|
+
};
|
|
2235
|
+
}));
|
|
2477
2236
|
}
|
|
2478
|
-
function renderCellMarkup({ cell, resizable, overflowing, testId, onCellClick, noRowNav }) {
|
|
2237
|
+
function renderCellMarkup({ cell, resizable, overflowing, fill, testId, onCellClick, noRowNav }) {
|
|
2479
2238
|
const liveColumn = cell.getContext().table.getColumn(cell.column.id) ?? cell.column;
|
|
2480
2239
|
const pinEdge = pinEdgeClassFor(liveColumn, overflowing);
|
|
2481
|
-
const noClip = columnMetaNoClip(cell.column.columnDef.meta);
|
|
2240
|
+
const noClip = columnMetaNoClip(cell.column.columnDef.meta) || columnMetaWrap(cell.column.columnDef.meta);
|
|
2482
2241
|
return jsx(TableCell, {
|
|
2483
2242
|
"data-testid": testId,
|
|
2484
2243
|
"data-no-row-nav": columnMetaNoRowNav(cell.column.columnDef.meta) || noRowNav ? "" : void 0,
|
|
2485
2244
|
onClick: onCellClick,
|
|
2486
|
-
style: getPinStyles(liveColumn, overflowing),
|
|
2245
|
+
style: getPinStyles(liveColumn, overflowing, fill),
|
|
2487
2246
|
className: cn(resizable && !pinEdge && !noClip && "overflow-hidden", pinEdge && "relative", pinEdge, columnMetaClassName(cell.column.columnDef.meta), onCellClick && "cursor-pointer"),
|
|
2488
2247
|
children: resizable && !noClip ? jsx("div", {
|
|
2489
2248
|
className: "truncate",
|
|
@@ -2495,7 +2254,7 @@ function renderCellMarkup({ cell, resizable, overflowing, testId, onCellClick, n
|
|
|
2495
2254
|
function MemoCellInner(props) {
|
|
2496
2255
|
return renderCellMarkup(props);
|
|
2497
2256
|
}
|
|
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);
|
|
2257
|
+
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
2258
|
function SelectionCellInner(props) {
|
|
2500
2259
|
const row = props.cell.row;
|
|
2501
2260
|
const toggle = (e) => {
|
|
@@ -2509,7 +2268,7 @@ function SelectionCellInner(props) {
|
|
|
2509
2268
|
});
|
|
2510
2269
|
}
|
|
2511
2270
|
const SelectionCell = memo(SelectionCellInner);
|
|
2512
|
-
function DataRowInner({ row, isSelected, resizable, overflowing, selectionColumnId, getCellTestId, getRowTestId, rowHref, onRowOpen, activateRow, rowClassName }) {
|
|
2271
|
+
function DataRowInner({ row, isSelected, resizable, overflowing, fillColumnId, selectionColumnId, getCellTestId, getRowTestId, rowHref, onRowOpen, activateRow, rowClassName }) {
|
|
2513
2272
|
const clickable = rowHref !== void 0 || onRowOpen !== void 0;
|
|
2514
2273
|
return jsx(TableRow, {
|
|
2515
2274
|
"data-state": isSelected ? "selected" : void 0,
|
|
@@ -2522,6 +2281,7 @@ function DataRowInner({ row, isSelected, resizable, overflowing, selectionColumn
|
|
|
2522
2281
|
isSelected,
|
|
2523
2282
|
resizable,
|
|
2524
2283
|
overflowing,
|
|
2284
|
+
fill: cell.column.id === fillColumnId,
|
|
2525
2285
|
pinned: cell.getContext().table.getColumn(cell.column.id)?.getIsPinned() ?? false,
|
|
2526
2286
|
testId: getCellTestId?.(row.original, cell.column.id)
|
|
2527
2287
|
}, cell.id) : jsx(MemoCell, {
|
|
@@ -2529,13 +2289,14 @@ function DataRowInner({ row, isSelected, resizable, overflowing, selectionColumn
|
|
|
2529
2289
|
rowOriginal: row.original,
|
|
2530
2290
|
resizable,
|
|
2531
2291
|
overflowing,
|
|
2292
|
+
fill: cell.column.id === fillColumnId,
|
|
2532
2293
|
pinned: cell.getContext().table.getColumn(cell.column.id)?.getIsPinned() ?? false,
|
|
2533
2294
|
testId: getCellTestId?.(row.original, cell.column.id)
|
|
2534
2295
|
}, cell.id))
|
|
2535
2296
|
});
|
|
2536
2297
|
}
|
|
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 }) {
|
|
2298
|
+
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);
|
|
2299
|
+
function DataTableBodyInner({ rows, isLoading, loadingRowCount, visibleColumnCount, emptyState, emptyText, resizable, overflowing, columnLayoutKey, fillColumnId, selectionColumnId, getCellTestId, getRowTestId, rowHref, onRowOpen, activateRow, rowClassName }) {
|
|
2539
2300
|
return jsx(TableBody, { children: isLoading ? Array.from({ length: loadingRowCount }).map((_, i) => jsx(TableRow, {
|
|
2540
2301
|
"aria-busy": "true",
|
|
2541
2302
|
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 +2313,7 @@ function DataTableBodyInner({ rows, isLoading, loadingRowCount, visibleColumnCou
|
|
|
2552
2313
|
resizable,
|
|
2553
2314
|
overflowing,
|
|
2554
2315
|
columnLayoutKey,
|
|
2316
|
+
fillColumnId,
|
|
2555
2317
|
selectionColumnId,
|
|
2556
2318
|
getCellTestId,
|
|
2557
2319
|
getRowTestId,
|
|
@@ -2563,7 +2325,7 @@ function DataTableBodyInner({ rows, isLoading, loadingRowCount, visibleColumnCou
|
|
|
2563
2325
|
}
|
|
2564
2326
|
const DataTableBody = memo(DataTableBodyInner, (prev, next) => {
|
|
2565
2327
|
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;
|
|
2328
|
+
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
2329
|
});
|
|
2568
2330
|
function DataTable({ table, tableTestId, getRowTestId, getCellTestId, isLoading = false, loadingRowCount = 5, emptyState, errorState, rowClassName, ariaLabel, onColumnReorder, rowHref, onRowOpen, selectionColumnId, className }) {
|
|
2569
2331
|
const t = useOptionalT();
|
|
@@ -2593,8 +2355,10 @@ function DataTable({ table, tableTestId, getRowTestId, getCellTestId, isLoading
|
|
|
2593
2355
|
const rows = table.getRowModel().rows;
|
|
2594
2356
|
const headerGroups = table.getHeaderGroups();
|
|
2595
2357
|
const visibleColumnCount = table.getVisibleLeafColumns().length;
|
|
2358
|
+
const fillId = tableFillColumnId(table);
|
|
2596
2359
|
const columnLayoutKey = table.getVisibleLeafColumns().map((c) => `${c.id}:${c.getIsPinned() || ""}`).join("|");
|
|
2597
2360
|
const resizable = table.options.enableColumnResizing === true;
|
|
2361
|
+
const widthSum = resizable ? table.getVisibleLeafColumns().reduce((sum, c) => sum + c.getSize(), 0) : 0;
|
|
2598
2362
|
const hasPinned = table.getState().columnPinning != null && (table.getState().columnPinning.left?.length || table.getState().columnPinning.right?.length);
|
|
2599
2363
|
const sizingState = table.getState().columnSizing;
|
|
2600
2364
|
const sizingInfo = table.getState().columnSizingInfo;
|
|
@@ -2686,7 +2450,7 @@ function DataTable({ table, tableTestId, getRowTestId, getCellTestId, isLoading
|
|
|
2686
2450
|
className,
|
|
2687
2451
|
style: resizable ? {
|
|
2688
2452
|
...columnSizeVars,
|
|
2689
|
-
|
|
2453
|
+
width: fillId !== void 0 ? `max(100%, ${widthSum}px)` : `${widthSum}px`,
|
|
2690
2454
|
tableLayout: "fixed"
|
|
2691
2455
|
} : void 0,
|
|
2692
2456
|
children: [jsx(TableHeader, { children: headerGroups.map((hg) => jsx(TableRow, { children: hg.headers.map((header) => {
|
|
@@ -2697,7 +2461,7 @@ function DataTable({ table, tableTestId, getRowTestId, getCellTestId, isLoading
|
|
|
2697
2461
|
return jsxs(TableHead, {
|
|
2698
2462
|
colSpan: header.colSpan,
|
|
2699
2463
|
"aria-sort": ariaSort,
|
|
2700
|
-
style: getPinStyles(header.column, overflowing),
|
|
2464
|
+
style: getPinStyles(header.column, overflowing, header.column.id === fillId),
|
|
2701
2465
|
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
2466
|
draggable: canReorder || void 0,
|
|
2703
2467
|
onDragStart: canReorder ? (e) => {
|
|
@@ -2756,6 +2520,7 @@ function DataTable({ table, tableTestId, getRowTestId, getCellTestId, isLoading
|
|
|
2756
2520
|
resizable,
|
|
2757
2521
|
overflowing,
|
|
2758
2522
|
columnLayoutKey,
|
|
2523
|
+
fillColumnId: fillId,
|
|
2759
2524
|
selectionColumnId,
|
|
2760
2525
|
isResizing: Boolean(sizingInfo.isResizingColumn),
|
|
2761
2526
|
getCellTestId,
|
|
@@ -2872,7 +2637,8 @@ function ColumnHeaderMenu({ label, sortDirection, sortable, onSort, pinned, onPi
|
|
|
2872
2637
|
}
|
|
2873
2638
|
//#endregion
|
|
2874
2639
|
//#region src/resource/column-accessor.ts
|
|
2875
|
-
function columnAccessor(column) {
|
|
2640
|
+
function columnAccessor(column, objectPath = false) {
|
|
2641
|
+
if (objectPath) return { accessorFn: (row) => readColumnValue(row, column, true) };
|
|
2876
2642
|
return column.includes(".") ? { accessorFn: (row) => row[column] } : { accessorKey: column };
|
|
2877
2643
|
}
|
|
2878
2644
|
//#endregion
|
|
@@ -2979,96 +2745,280 @@ function resolveUpdater(updater, prev) {
|
|
|
2979
2745
|
}
|
|
2980
2746
|
//#endregion
|
|
2981
2747
|
//#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
|
-
]
|
|
2748
|
+
function ListErrorState(props) {
|
|
2749
|
+
const { error, onRetry, resource, title, className } = props;
|
|
2750
|
+
return jsx(LoadError, {
|
|
2751
|
+
surface: "list",
|
|
2752
|
+
error,
|
|
2753
|
+
...onRetry === void 0 ? {} : { retry: onRetry },
|
|
2754
|
+
...resource === void 0 ? {} : { resource },
|
|
2755
|
+
...title === void 0 ? {} : { title },
|
|
2756
|
+
...className === void 0 ? {} : { className }
|
|
3036
2757
|
});
|
|
3037
2758
|
}
|
|
3038
2759
|
//#endregion
|
|
3039
|
-
//#region src/
|
|
3040
|
-
|
|
3041
|
-
|
|
3042
|
-
|
|
3043
|
-
|
|
3044
|
-
|
|
3045
|
-
|
|
3046
|
-
|
|
3047
|
-
variant: "outline",
|
|
3048
|
-
size: "sm",
|
|
3049
|
-
disabled: loading,
|
|
3050
|
-
onClick: onMore,
|
|
3051
|
-
"data-testid": "cursor-load-more",
|
|
3052
|
-
children: loading ? t("pagination.loadingMore") : t("pagination.loadMore")
|
|
3053
|
-
})
|
|
3054
|
-
});
|
|
2760
|
+
//#region src/resource/use-table-layout.ts
|
|
2761
|
+
const WRITE_DEBOUNCE_MS = 200;
|
|
2762
|
+
function mutate(run, ok, then) {
|
|
2763
|
+
try {
|
|
2764
|
+
run().then((result) => {
|
|
2765
|
+
if (ok(result.status)) then();
|
|
2766
|
+
}, () => void 0);
|
|
2767
|
+
} catch {}
|
|
3055
2768
|
}
|
|
3056
|
-
|
|
3057
|
-
|
|
3058
|
-
|
|
3059
|
-
|
|
3060
|
-
|
|
3061
|
-
|
|
3062
|
-
|
|
3063
|
-
|
|
3064
|
-
|
|
3065
|
-
|
|
3066
|
-
|
|
3067
|
-
|
|
3068
|
-
|
|
3069
|
-
|
|
3070
|
-
|
|
3071
|
-
|
|
2769
|
+
function resolve(updater, current) {
|
|
2770
|
+
return typeof updater === "function" ? updater(current) : updater;
|
|
2771
|
+
}
|
|
2772
|
+
function sameNames(a, b) {
|
|
2773
|
+
return a.length === b.length && a.every((name, index) => name === b[index]);
|
|
2774
|
+
}
|
|
2775
|
+
function readSizes(sizes, rule) {
|
|
2776
|
+
const out = {};
|
|
2777
|
+
for (const [column, size] of Object.entries(sizes)) {
|
|
2778
|
+
const of = rule(column);
|
|
2779
|
+
if (of === void 0 || !of.resize) continue;
|
|
2780
|
+
out[column] = Math.min(Math.max(size, of.min), of.max ?? Number.POSITIVE_INFINITY);
|
|
2781
|
+
}
|
|
2782
|
+
return out;
|
|
2783
|
+
}
|
|
2784
|
+
function useTableLayout({ resource, layoutId, rule, defaultPinning, resetLayout }) {
|
|
2785
|
+
const params = useMemo(() => layoutId === void 0 ? null : {
|
|
2786
|
+
resource,
|
|
2787
|
+
list: layoutId
|
|
2788
|
+
}, [resource, layoutId]);
|
|
2789
|
+
const settings = useOptionalUiSettings(tableLayoutSetting, params);
|
|
2790
|
+
const settingsRef = useRef(settings);
|
|
2791
|
+
settingsRef.current = settings;
|
|
2792
|
+
const key = layoutId === void 0 ? void 0 : `${resource}\u0000${layoutId}`;
|
|
2793
|
+
const fallback = useMemo(() => ({
|
|
2794
|
+
sizes: {},
|
|
2795
|
+
order: [],
|
|
2796
|
+
pinning: defaultPinning
|
|
2797
|
+
}), [defaultPinning]);
|
|
2798
|
+
const [local, setLocal] = useState();
|
|
2799
|
+
const stored = settings?.snapshot.entries[0]?.value;
|
|
2800
|
+
const settled = settings === void 0 || !settings.snapshot.pending;
|
|
2801
|
+
const held = useRef(null);
|
|
2802
|
+
const layout = (local !== void 0 && local.key === key ? local.value : void 0) ?? stored ?? fallback;
|
|
2803
|
+
const latest = useRef(layout);
|
|
2804
|
+
latest.current = layout;
|
|
2805
|
+
const sizing = useMemo(() => readSizes(layout.sizes, rule), [layout.sizes, rule]);
|
|
2806
|
+
const sizingRef = useRef(sizing);
|
|
2807
|
+
sizingRef.current = sizing;
|
|
2808
|
+
const generation = useRef(0);
|
|
2809
|
+
const timer = useRef(null);
|
|
2810
|
+
const pending = useRef(null);
|
|
2811
|
+
const confirm = useCallback((sent) => {
|
|
2812
|
+
if (generation.current === sent && pending.current === null) setLocal(void 0);
|
|
2813
|
+
}, []);
|
|
2814
|
+
const write = useCallback((target, value) => {
|
|
2815
|
+
const scopeId = target.snapshot.entries[0]?.scope.id ?? target.snapshot.writeScopes[0]?.id;
|
|
2816
|
+
if (scopeId === void 0) return;
|
|
2817
|
+
const sent = generation.current;
|
|
2818
|
+
mutate(() => target.set({
|
|
2819
|
+
scopeId,
|
|
2820
|
+
value
|
|
2821
|
+
}), (status) => status === "created" || status === "updated", () => confirm(sent));
|
|
2822
|
+
}, [confirm]);
|
|
2823
|
+
const store = useCallback((target, next) => {
|
|
2824
|
+
if (!(Object.keys(next.sizes).length === 0 && next.order.length === 0 && sameNames(next.pinning.left, defaultPinning.left) && sameNames(next.pinning.right, defaultPinning.right))) {
|
|
2825
|
+
write(target, next);
|
|
2826
|
+
return;
|
|
2827
|
+
}
|
|
2828
|
+
const entry = target.snapshot.entries[0];
|
|
2829
|
+
if (entry === void 0) {
|
|
2830
|
+
setLocal(void 0);
|
|
2831
|
+
return;
|
|
2832
|
+
}
|
|
2833
|
+
const sent = generation.current;
|
|
2834
|
+
mutate(() => target.remove({ id: entry.id }), (status) => status === "removed", () => confirm(sent));
|
|
2835
|
+
}, [
|
|
2836
|
+
defaultPinning,
|
|
2837
|
+
write,
|
|
2838
|
+
confirm
|
|
2839
|
+
]);
|
|
2840
|
+
const flush = useCallback(() => {
|
|
2841
|
+
if (timer.current !== null) clearTimeout(timer.current);
|
|
2842
|
+
timer.current = null;
|
|
2843
|
+
const due = pending.current;
|
|
2844
|
+
pending.current = null;
|
|
2845
|
+
if (due !== null) write(due.target, due.value);
|
|
2846
|
+
}, [write]);
|
|
2847
|
+
const change = useCallback((next, debounced, touch) => {
|
|
2848
|
+
latest.current = next;
|
|
2849
|
+
generation.current += 1;
|
|
2850
|
+
setLocal({
|
|
2851
|
+
key,
|
|
2852
|
+
value: next
|
|
2853
|
+
});
|
|
2854
|
+
const target = settingsRef.current;
|
|
2855
|
+
if (target === void 0) return;
|
|
2856
|
+
if (target.snapshot.pending || held.current !== null && held.current.key === key) {
|
|
2857
|
+
const edits = held.current !== null && held.current.key === key ? held.current : {
|
|
2858
|
+
key,
|
|
2859
|
+
resetAll: false,
|
|
2860
|
+
resetSizes: false,
|
|
2861
|
+
sizes: new Map()
|
|
2862
|
+
};
|
|
2863
|
+
held.current = edits;
|
|
2864
|
+
touch(edits);
|
|
2865
|
+
return;
|
|
2866
|
+
}
|
|
2867
|
+
pending.current = {
|
|
2868
|
+
target,
|
|
2869
|
+
value: next
|
|
2870
|
+
};
|
|
2871
|
+
if (!debounced) {
|
|
2872
|
+
flush();
|
|
2873
|
+
return;
|
|
2874
|
+
}
|
|
2875
|
+
if (timer.current !== null) clearTimeout(timer.current);
|
|
2876
|
+
timer.current = setTimeout(flush, WRITE_DEBOUNCE_MS);
|
|
2877
|
+
}, [key, flush]);
|
|
2878
|
+
useEffect(() => flush, [key]);
|
|
2879
|
+
useEffect(() => {
|
|
2880
|
+
const edits = held.current;
|
|
2881
|
+
if (edits === null) return;
|
|
2882
|
+
if (edits.key !== key) {
|
|
2883
|
+
held.current = null;
|
|
2884
|
+
return;
|
|
2885
|
+
}
|
|
2886
|
+
const target = settingsRef.current;
|
|
2887
|
+
if (!settled || target === void 0) return;
|
|
2888
|
+
held.current = null;
|
|
2889
|
+
const base = edits.resetAll ? fallback : stored ?? fallback;
|
|
2890
|
+
const sizes = edits.resetAll || edits.resetSizes ? {} : { ...readSizes(base.sizes, rule) };
|
|
2891
|
+
for (const [column, size] of edits.sizes) if (size === void 0) delete sizes[column];
|
|
2892
|
+
else sizes[column] = size;
|
|
2893
|
+
const next = {
|
|
2894
|
+
sizes,
|
|
2895
|
+
order: edits.order ?? base.order,
|
|
2896
|
+
pinning: edits.pinning ?? base.pinning
|
|
2897
|
+
};
|
|
2898
|
+
latest.current = next;
|
|
2899
|
+
generation.current += 1;
|
|
2900
|
+
setLocal({
|
|
2901
|
+
key,
|
|
2902
|
+
value: next
|
|
2903
|
+
});
|
|
2904
|
+
store(target, next);
|
|
2905
|
+
}, [settled, key]);
|
|
2906
|
+
const resetToken = resetLayout?.token ?? 0;
|
|
2907
|
+
const seenToken = useRef(resetToken);
|
|
2908
|
+
useEffect(() => {
|
|
2909
|
+
if (resetToken === seenToken.current) return;
|
|
2910
|
+
seenToken.current = resetToken;
|
|
2911
|
+
const all = resetLayout?.mode === "all";
|
|
2912
|
+
const next = all ? fallback : {
|
|
2913
|
+
...latest.current,
|
|
2914
|
+
sizes: {}
|
|
2915
|
+
};
|
|
2916
|
+
if (timer.current !== null) clearTimeout(timer.current);
|
|
2917
|
+
timer.current = null;
|
|
2918
|
+
pending.current = null;
|
|
2919
|
+
latest.current = next;
|
|
2920
|
+
generation.current += 1;
|
|
2921
|
+
setLocal({
|
|
2922
|
+
key,
|
|
2923
|
+
value: next
|
|
2924
|
+
});
|
|
2925
|
+
const target = settingsRef.current;
|
|
2926
|
+
if (target === void 0) return;
|
|
2927
|
+
if (target.snapshot.pending || held.current !== null && held.current.key === key) {
|
|
2928
|
+
const prior = held.current !== null && held.current.key === key ? held.current : void 0;
|
|
2929
|
+
held.current = all ? {
|
|
2930
|
+
key,
|
|
2931
|
+
resetAll: true,
|
|
2932
|
+
resetSizes: false,
|
|
2933
|
+
sizes: new Map()
|
|
2934
|
+
} : {
|
|
2935
|
+
key,
|
|
2936
|
+
resetAll: prior?.resetAll ?? false,
|
|
2937
|
+
resetSizes: true,
|
|
2938
|
+
sizes: new Map(),
|
|
2939
|
+
...prior?.order === void 0 ? {} : { order: prior.order },
|
|
2940
|
+
...prior?.pinning === void 0 ? {} : { pinning: prior.pinning }
|
|
2941
|
+
};
|
|
2942
|
+
return;
|
|
2943
|
+
}
|
|
2944
|
+
store(target, next);
|
|
2945
|
+
}, [resetToken]);
|
|
2946
|
+
const setColumnSizing = useCallback((updater) => {
|
|
2947
|
+
const before = sizingRef.current;
|
|
2948
|
+
const sizes = resolve(updater, before);
|
|
2949
|
+
sizingRef.current = sizes;
|
|
2950
|
+
change({
|
|
2951
|
+
...latest.current,
|
|
2952
|
+
sizes
|
|
2953
|
+
}, true, (edits) => {
|
|
2954
|
+
for (const column of new Set([...Object.keys(before), ...Object.keys(sizes)])) if (before[column] !== sizes[column]) edits.sizes.set(column, sizes[column]);
|
|
2955
|
+
});
|
|
2956
|
+
}, [change]);
|
|
2957
|
+
const setColumnOrder = useCallback((updater) => {
|
|
2958
|
+
const order = resolve(updater, latest.current.order);
|
|
2959
|
+
change({
|
|
2960
|
+
...latest.current,
|
|
2961
|
+
order
|
|
2962
|
+
}, false, (edits) => {
|
|
2963
|
+
edits.order = order;
|
|
2964
|
+
});
|
|
2965
|
+
}, [change]);
|
|
2966
|
+
const setColumnPinning = useCallback((updater) => {
|
|
2967
|
+
const resolved = resolve(updater, latest.current.pinning);
|
|
2968
|
+
const pinning = {
|
|
2969
|
+
left: resolved.left ?? [],
|
|
2970
|
+
right: resolved.right ?? []
|
|
2971
|
+
};
|
|
2972
|
+
change({
|
|
2973
|
+
...latest.current,
|
|
2974
|
+
pinning
|
|
2975
|
+
}, false, (edits) => {
|
|
2976
|
+
edits.pinning = pinning;
|
|
2977
|
+
});
|
|
2978
|
+
}, [change]);
|
|
2979
|
+
return {
|
|
2980
|
+
columnSizing: sizing,
|
|
2981
|
+
setColumnSizing,
|
|
2982
|
+
columnOrder: layout.order,
|
|
2983
|
+
setColumnOrder,
|
|
2984
|
+
columnPinning: layout.pinning,
|
|
2985
|
+
setColumnPinning
|
|
2986
|
+
};
|
|
2987
|
+
}
|
|
2988
|
+
//#endregion
|
|
2989
|
+
//#region src/list-kit/cursor-load-more.tsx
|
|
2990
|
+
function CursorLoadMore({ hasMore, loading, onMore }) {
|
|
2991
|
+
const t = useT();
|
|
2992
|
+
if (!hasMore) return null;
|
|
2993
|
+
return jsx("div", {
|
|
2994
|
+
className: "flex justify-center py-4",
|
|
2995
|
+
children: jsx(Button, {
|
|
2996
|
+
type: "button",
|
|
2997
|
+
variant: "outline",
|
|
2998
|
+
size: "sm",
|
|
2999
|
+
disabled: loading,
|
|
3000
|
+
onClick: onMore,
|
|
3001
|
+
"data-testid": "cursor-load-more",
|
|
3002
|
+
children: loading ? t("pagination.loadingMore") : t("pagination.loadMore")
|
|
3003
|
+
})
|
|
3004
|
+
});
|
|
3005
|
+
}
|
|
3006
|
+
//#endregion
|
|
3007
|
+
//#region src/resource/resource-list.tsx
|
|
3008
|
+
function CountRowsTrigger({ label, loading, onClick, testid, variant, className }) {
|
|
3009
|
+
return jsxs(Button, {
|
|
3010
|
+
type: "button",
|
|
3011
|
+
variant,
|
|
3012
|
+
size: "sm",
|
|
3013
|
+
"data-testid": testid,
|
|
3014
|
+
onClick,
|
|
3015
|
+
disabled: loading,
|
|
3016
|
+
"aria-label": label,
|
|
3017
|
+
"aria-busy": loading || void 0,
|
|
3018
|
+
className: cn("relative", className),
|
|
3019
|
+
children: [jsx("span", {
|
|
3020
|
+
className: loading ? "invisible" : void 0,
|
|
3021
|
+
children: label
|
|
3072
3022
|
}), loading && jsx(IconLoader2, {
|
|
3073
3023
|
className: "absolute inset-0 m-auto size-3.5 animate-spin",
|
|
3074
3024
|
"aria-hidden": "true"
|
|
@@ -3077,6 +3027,10 @@ function CountRowsTrigger({ label, loading, onClick, testid, variant, className
|
|
|
3077
3027
|
}
|
|
3078
3028
|
const SELECT_COL_ID = "__select__";
|
|
3079
3029
|
const ACTIONS_COL_ID = "__actions__";
|
|
3030
|
+
const DEFAULT_PINNING = Object.freeze({
|
|
3031
|
+
left: Object.freeze([SELECT_COL_ID, "id"]),
|
|
3032
|
+
right: Object.freeze([ACTIONS_COL_ID])
|
|
3033
|
+
});
|
|
3080
3034
|
const ROW_ACTION_CONTROL_PX = 28;
|
|
3081
3035
|
const ROW_ACTION_GAP_PX = 4;
|
|
3082
3036
|
const ACTIONS_CELL_PADDING_PX = 16;
|
|
@@ -3088,29 +3042,30 @@ function actionsColumnSize(items) {
|
|
|
3088
3042
|
const strip = controls * ROW_ACTION_CONTROL_PX + (controls - 1) * ROW_ACTION_GAP_PX + ACTIONS_CELL_PADDING_PX;
|
|
3089
3043
|
return Math.max(ACTIONS_COL_MIN_PX, strip);
|
|
3090
3044
|
}
|
|
3091
|
-
const
|
|
3092
|
-
|
|
3093
|
-
|
|
3094
|
-
|
|
3095
|
-
const COLUMN_SIZE_BY_FORMAT = {
|
|
3096
|
-
date: 132,
|
|
3097
|
-
"date-time": 132
|
|
3045
|
+
const ALIGN_CLASS = {
|
|
3046
|
+
start: "text-start",
|
|
3047
|
+
center: "text-center",
|
|
3048
|
+
end: "text-end"
|
|
3098
3049
|
};
|
|
3099
|
-
|
|
3100
|
-
|
|
3101
|
-
|
|
3102
|
-
|
|
3103
|
-
|
|
3104
|
-
|
|
3105
|
-
|
|
3050
|
+
const STANDALONE_COLUMN_UI = Object.freeze({
|
|
3051
|
+
default: 160,
|
|
3052
|
+
min: 90,
|
|
3053
|
+
align: "start",
|
|
3054
|
+
resize: true,
|
|
3055
|
+
fill: false,
|
|
3056
|
+
display: Object.freeze({ params: Object.freeze({}) })
|
|
3057
|
+
});
|
|
3058
|
+
function ResourceList({ resource: resourceInput, data, total, page = 1, pageSize = 10, onPageSize, onRequestCount, isCountingRows = false, pageSizeOptions = [
|
|
3106
3059
|
10,
|
|
3107
3060
|
25,
|
|
3108
3061
|
50,
|
|
3109
3062
|
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 }) {
|
|
3063
|
+
], 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 }) {
|
|
3064
|
+
const resource = routeResourceKey(resourceInput);
|
|
3111
3065
|
const app = useApp();
|
|
3112
3066
|
const tid = useTestIds();
|
|
3113
3067
|
const t = useOptionalT();
|
|
3068
|
+
const chromeT = useChromeT();
|
|
3114
3069
|
const locale = useLocale();
|
|
3115
3070
|
const nf = useMemo(() => new Intl.NumberFormat(locale), [locale]);
|
|
3116
3071
|
const i18nCaps = useI18nCaps(resource);
|
|
@@ -3132,23 +3087,6 @@ function ResourceList({ resource, data, total, page = 1, pageSize = 10, onPageSi
|
|
|
3132
3087
|
useEffect(() => {
|
|
3133
3088
|
emittedRef.current = null;
|
|
3134
3089
|
}, [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
3090
|
useEffect(() => {
|
|
3153
3091
|
if (controlled) return;
|
|
3154
3092
|
setOwnSelection({});
|
|
@@ -3158,7 +3096,7 @@ function ResourceList({ resource, data, total, page = 1, pageSize = 10, onPageSi
|
|
|
3158
3096
|
const accessPending = listAccess.status === "pending" || pathAccess.pending;
|
|
3159
3097
|
const declaredColumnKeys = useMemo(() => {
|
|
3160
3098
|
if (!def) return [];
|
|
3161
|
-
return [...columnsOverride ?? def.views?.list?.columns ?? Object.keys(def.fields)];
|
|
3099
|
+
return [...columnsOverride ?? def.views?.list?.columns?.map(columnName) ?? Object.keys(def.fields)];
|
|
3162
3100
|
}, [def, columnsOverride]);
|
|
3163
3101
|
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
3102
|
accessPending,
|
|
@@ -3208,10 +3146,11 @@ function ResourceList({ resource, data, total, page = 1, pageSize = 10, onPageSi
|
|
|
3208
3146
|
const resourceLabel = resolveResourceLabel(i18nCaps, resource, "plural", def);
|
|
3209
3147
|
const recordSingular = resolveResourceLabel(i18nCaps, resource, "single", def);
|
|
3210
3148
|
const ariaLabel = resourceLabel;
|
|
3211
|
-
const actionsHeader =
|
|
3149
|
+
const actionsHeader = chromeT("actions.column", { defaultValue: "Actions" });
|
|
3212
3150
|
const viewRow = useViewRow(resource);
|
|
3213
3151
|
const volatileRef = useRef({
|
|
3214
3152
|
onSort,
|
|
3153
|
+
sortable,
|
|
3215
3154
|
onHideColumn,
|
|
3216
3155
|
requiredColumns,
|
|
3217
3156
|
renderCell,
|
|
@@ -3223,6 +3162,7 @@ function ResourceList({ resource, data, total, page = 1, pageSize = 10, onPageSi
|
|
|
3223
3162
|
});
|
|
3224
3163
|
volatileRef.current = {
|
|
3225
3164
|
onSort,
|
|
3165
|
+
sortable,
|
|
3226
3166
|
onHideColumn,
|
|
3227
3167
|
requiredColumns,
|
|
3228
3168
|
renderCell,
|
|
@@ -3246,6 +3186,45 @@ function ResourceList({ resource, data, total, page = 1, pageSize = 10, onPageSi
|
|
|
3246
3186
|
}, [rowLinkKey]);
|
|
3247
3187
|
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
3188
|
const [listRoot, narrow] = useContainerNarrow();
|
|
3189
|
+
const columnUiOf = useMemo(() => {
|
|
3190
|
+
const entries = new Map();
|
|
3191
|
+
for (const entry of [...def?.views?.list?.columns ?? [], ...def?.views?.list?.optionalColumns ?? []]) {
|
|
3192
|
+
const name = columnName(entry);
|
|
3193
|
+
if (!entries.has(name)) entries.set(name, entry);
|
|
3194
|
+
}
|
|
3195
|
+
return (col) => columnUi?.[col] ?? (def === void 0 ? STANDALONE_COLUMN_UI : resolveColumnUi(def, entries.get(col) ?? col, app.registry, columnTypeWidth));
|
|
3196
|
+
}, [
|
|
3197
|
+
app,
|
|
3198
|
+
def,
|
|
3199
|
+
columnUi
|
|
3200
|
+
]);
|
|
3201
|
+
const { columnSizing, setColumnSizing, columnOrder, setColumnOrder, columnPinning, setColumnPinning } = useTableLayout({
|
|
3202
|
+
resource,
|
|
3203
|
+
layoutId,
|
|
3204
|
+
rule: useMemo(() => {
|
|
3205
|
+
const known = new Set([
|
|
3206
|
+
...declaredColumnKeys,
|
|
3207
|
+
...(def?.views?.list?.columns ?? []).map(columnName),
|
|
3208
|
+
...(def?.views?.list?.optionalColumns ?? []).map(columnName),
|
|
3209
|
+
...Object.keys(def?.fields ?? {})
|
|
3210
|
+
]);
|
|
3211
|
+
return (col) => {
|
|
3212
|
+
if (!known.has(col)) return void 0;
|
|
3213
|
+
const ui = columnUiOf(col);
|
|
3214
|
+
return {
|
|
3215
|
+
min: ui.min,
|
|
3216
|
+
max: ui.max,
|
|
3217
|
+
resize: ui.resize
|
|
3218
|
+
};
|
|
3219
|
+
};
|
|
3220
|
+
}, [
|
|
3221
|
+
declaredColumnKeys,
|
|
3222
|
+
def,
|
|
3223
|
+
columnUiOf
|
|
3224
|
+
]),
|
|
3225
|
+
defaultPinning: DEFAULT_PINNING,
|
|
3226
|
+
resetLayout
|
|
3227
|
+
});
|
|
3249
3228
|
const tableColumns = useMemo(() => {
|
|
3250
3229
|
const cols = [];
|
|
3251
3230
|
if (selectable) cols.push({
|
|
@@ -3268,19 +3247,36 @@ function ResourceList({ resource, data, total, page = 1, pageSize = 10, onPageSi
|
|
|
3268
3247
|
});
|
|
3269
3248
|
for (const col of visibleColumnKeys) {
|
|
3270
3249
|
const target = columnTarget(app.registry, resource, col);
|
|
3271
|
-
const
|
|
3250
|
+
const objectPath = isObjectColumn(app.registry, resource, col);
|
|
3251
|
+
const field = target !== void 0 ? target.field : objectPath ? resolveObjectColumn(app.registry, resource, col)?.field : def === void 0 ? void 0 : declaredField(def, col);
|
|
3272
3252
|
const declaredLabel = declaredText("field", field, "label");
|
|
3273
3253
|
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
|
-
|
|
3254
|
+
const ui = columnUiOf(col);
|
|
3255
|
+
const frameField = {
|
|
3256
|
+
...field ?? { type: "unknown" },
|
|
3257
|
+
name: target === void 0 ? col : target.name
|
|
3258
|
+
};
|
|
3259
|
+
const cellEntry = { display: {
|
|
3260
|
+
...ui.display,
|
|
3261
|
+
...ui.display.dateDisplay
|
|
3262
|
+
} };
|
|
3263
|
+
const wraps = ui.display.overflow === "wrap";
|
|
3264
|
+
const columnSortable = volatileRef.current.sortable?.(col) ?? (def !== void 0 && isColumnSortable(def, col, app, app.registry));
|
|
3277
3265
|
cols.push({
|
|
3278
3266
|
id: col,
|
|
3279
|
-
...columnAccessor(col),
|
|
3280
|
-
|
|
3281
|
-
|
|
3267
|
+
...columnAccessor(col, objectPath),
|
|
3268
|
+
size: ui.default,
|
|
3269
|
+
minSize: ui.min,
|
|
3270
|
+
maxSize: ui.max ?? Number.MAX_SAFE_INTEGER,
|
|
3271
|
+
enableResizing: ui.resize,
|
|
3272
|
+
enableSorting: columnSortable && Boolean(volatileRef.current.onSort),
|
|
3282
3273
|
enableHiding: true,
|
|
3283
|
-
meta: {
|
|
3274
|
+
meta: {
|
|
3275
|
+
testid: col,
|
|
3276
|
+
className: ALIGN_CLASS[ui.align],
|
|
3277
|
+
...wraps ? { wrap: true } : {},
|
|
3278
|
+
...ui.fill ? { fill: true } : {}
|
|
3279
|
+
},
|
|
3284
3280
|
header: ({ column, table }) => {
|
|
3285
3281
|
const v = volatileRef.current;
|
|
3286
3282
|
const pinSide = column.getIsPinned();
|
|
@@ -3311,9 +3307,9 @@ function ResourceList({ resource, data, total, page = 1, pageSize = 10, onPageSi
|
|
|
3311
3307
|
};
|
|
3312
3308
|
return jsx(ColumnHeaderMenu, {
|
|
3313
3309
|
label: headerLabel,
|
|
3314
|
-
sortable:
|
|
3310
|
+
sortable: columnSortable && Boolean(v.onSort),
|
|
3315
3311
|
sortDirection: v.sort?.[0]?.field === col ? v.sort[0].direction : null,
|
|
3316
|
-
onSort:
|
|
3312
|
+
onSort: columnSortable && v.onSort ? (dir) => v.onSort?.(col, dir) : void 0,
|
|
3317
3313
|
pinned: column.getIsPinned(),
|
|
3318
3314
|
onPin: (side) => column.pin(side),
|
|
3319
3315
|
canHide: Boolean(v.onHideColumn) && !v.requiredColumns?.includes(col),
|
|
@@ -3327,7 +3323,7 @@ function ResourceList({ resource, data, total, page = 1, pageSize = 10, onPageSi
|
|
|
3327
3323
|
cell: ({ row }) => {
|
|
3328
3324
|
const raw = row.original;
|
|
3329
3325
|
const record = volatileRef.current.viewRow(raw);
|
|
3330
|
-
const value = record
|
|
3326
|
+
const value = readColumnValue(record, col, objectPath);
|
|
3331
3327
|
const customCell = volatileRef.current.renderCell?.(col, value, record);
|
|
3332
3328
|
const cellInner = customCell !== void 0 ? customCell : jsx(ResolvedFieldDisplay, {
|
|
3333
3329
|
resource: target === void 0 ? resourceRef : target.ref,
|
|
@@ -3335,12 +3331,21 @@ function ResourceList({ resource, data, total, page = 1, pageSize = 10, onPageSi
|
|
|
3335
3331
|
field: field ?? { type: "unknown" },
|
|
3336
3332
|
value,
|
|
3337
3333
|
record,
|
|
3334
|
+
uiSurface: "column",
|
|
3335
|
+
columnEntry: cellEntry,
|
|
3338
3336
|
fallback: jsx("span", { children: String(value ?? "") })
|
|
3339
3337
|
});
|
|
3338
|
+
const framed = (node) => customCell !== void 0 ? node : jsx(FieldValueFrame, {
|
|
3339
|
+
field: frameField,
|
|
3340
|
+
value,
|
|
3341
|
+
surface: "column",
|
|
3342
|
+
entry: cellEntry,
|
|
3343
|
+
children: node
|
|
3344
|
+
});
|
|
3340
3345
|
const href = volatileRef.current.rowHref?.(record);
|
|
3341
3346
|
const isFirstContentCell = visibleColumnKeys.indexOf(col) === 0;
|
|
3342
3347
|
const quick = isFirstContentCell ? volatileRef.current.rowQuick?.(raw) ?? null : null;
|
|
3343
|
-
if (quick !== null) return href ? jsx(ClaimedQuickLink, {
|
|
3348
|
+
if (quick !== null) return framed(href ? jsx(ClaimedQuickLink, {
|
|
3344
3349
|
href,
|
|
3345
3350
|
className: relationLinkClassName,
|
|
3346
3351
|
onOpen: quick,
|
|
@@ -3354,13 +3359,13 @@ function ResourceList({ resource, data, total, page = 1, pageSize = 10, onPageSi
|
|
|
3354
3359
|
quick();
|
|
3355
3360
|
},
|
|
3356
3361
|
children: cellInner
|
|
3357
|
-
});
|
|
3358
|
-
return isFirstContentCell && href ? jsx(RouterLink, {
|
|
3362
|
+
}));
|
|
3363
|
+
return framed(isFirstContentCell && href ? jsx(RouterLink, {
|
|
3359
3364
|
href,
|
|
3360
3365
|
className: relationLinkClassName,
|
|
3361
3366
|
onClick: (e) => e.stopPropagation(),
|
|
3362
3367
|
children: cellInner
|
|
3363
|
-
}) : cellInner;
|
|
3368
|
+
}) : cellInner);
|
|
3364
3369
|
}
|
|
3365
3370
|
});
|
|
3366
3371
|
}
|
|
@@ -3369,7 +3374,7 @@ function ResourceList({ resource, data, total, page = 1, pageSize = 10, onPageSi
|
|
|
3369
3374
|
id: ACTIONS_COL_ID,
|
|
3370
3375
|
enableSorting: false,
|
|
3371
3376
|
enableResizing: false,
|
|
3372
|
-
size: narrow ? 44 : actionsColumnSize(def?.actions?.item),
|
|
3377
|
+
size: narrow || typeof rowActions === "function" ? 44 : actionsColumnSize(def?.actions?.item),
|
|
3373
3378
|
enableHiding: false,
|
|
3374
3379
|
header: () => jsx("span", {
|
|
3375
3380
|
className: "sr-only",
|
|
@@ -3380,7 +3385,7 @@ function ResourceList({ resource, data, total, page = 1, pageSize = 10, onPageSi
|
|
|
3380
3385
|
noRowNav: true,
|
|
3381
3386
|
noClip: true
|
|
3382
3387
|
},
|
|
3383
|
-
cell: ({ row }) => jsx(ActionOverflow, {
|
|
3388
|
+
cell: ({ row }) => typeof rowActions === "function" ? rowActions(row.original) : jsx(ActionOverflow, {
|
|
3384
3389
|
resource,
|
|
3385
3390
|
record: row.original,
|
|
3386
3391
|
surface: "row"
|
|
@@ -3400,7 +3405,8 @@ function ResourceList({ resource, data, total, page = 1, pageSize = 10, onPageSi
|
|
|
3400
3405
|
actionsHeader,
|
|
3401
3406
|
i18nCaps,
|
|
3402
3407
|
rowLinkKey,
|
|
3403
|
-
narrow
|
|
3408
|
+
narrow,
|
|
3409
|
+
columnUiOf
|
|
3404
3410
|
]);
|
|
3405
3411
|
const handleSelectionChange = (updater) => {
|
|
3406
3412
|
if (controlled) {
|
|
@@ -3503,8 +3509,23 @@ function ResourceList({ resource, data, total, page = 1, pageSize = 10, onPageSi
|
|
|
3503
3509
|
const isWrongPage = page > 1;
|
|
3504
3510
|
const EmptyIcon = isFiltered ? IconFilterX : IconInbox;
|
|
3505
3511
|
const items = resourceLabel.toLowerCase();
|
|
3506
|
-
const heading = isWrongPage ? t("pagination.emptyPage") : isFiltered ? t("list.emptyFilteredTitle", {
|
|
3507
|
-
|
|
3512
|
+
const heading = isWrongPage ? t("pagination.emptyPage") : isFiltered ? t("list.emptyFilteredTitle", {
|
|
3513
|
+
resource,
|
|
3514
|
+
params: { items }
|
|
3515
|
+
}) : t("list.emptyTitle", {
|
|
3516
|
+
resource,
|
|
3517
|
+
params: { items }
|
|
3518
|
+
});
|
|
3519
|
+
const body = isWrongPage ? null : isFiltered ? t("list.emptyFilteredBody", {
|
|
3520
|
+
resource,
|
|
3521
|
+
params: { items }
|
|
3522
|
+
}) : createHref ? t("list.emptyBody", {
|
|
3523
|
+
resource,
|
|
3524
|
+
params: { item: recordSingular.toLowerCase() }
|
|
3525
|
+
}) : t("list.emptyBodyReadOnly", {
|
|
3526
|
+
resource,
|
|
3527
|
+
params: { items }
|
|
3528
|
+
});
|
|
3508
3529
|
return jsxs(Fragment$1, { children: [jsxs("div", {
|
|
3509
3530
|
className: cn("flex flex-col items-center justify-center gap-3 px-6 py-16 rounded-lg border border-dashed text-center", className),
|
|
3510
3531
|
"data-testid": tid.listEmpty(resource),
|
|
@@ -3529,7 +3550,7 @@ function ResourceList({ resource, data, total, page = 1, pageSize = 10, onPageSi
|
|
|
3529
3550
|
jsxs("div", {
|
|
3530
3551
|
className: "flex justify-center gap-2 pt-2",
|
|
3531
3552
|
children: [
|
|
3532
|
-
|
|
3553
|
+
onClearFilter && jsx(Button, {
|
|
3533
3554
|
type: "button",
|
|
3534
3555
|
variant: "outline",
|
|
3535
3556
|
size: "sm",
|
|
@@ -3717,43 +3738,46 @@ function ResourceList({ resource, data, total, page = 1, pageSize = 10, onPageSi
|
|
|
3717
3738
|
})
|
|
3718
3739
|
});
|
|
3719
3740
|
}
|
|
3720
|
-
|
|
3721
|
-
|
|
3722
|
-
|
|
3723
|
-
|
|
3724
|
-
|
|
3725
|
-
|
|
3726
|
-
|
|
3727
|
-
|
|
3728
|
-
|
|
3741
|
+
//#endregion
|
|
3742
|
+
//#region src/blocks/related-list-unbound.tsx
|
|
3743
|
+
function UnboundRelatedList(props) {
|
|
3744
|
+
const t = useOptionalT();
|
|
3745
|
+
return jsxs("div", {
|
|
3746
|
+
className: "flex flex-col gap-2",
|
|
3747
|
+
"data-testid": `list-unbound:${props.resource}`,
|
|
3748
|
+
children: [props.title === void 0 ? null : jsx("h3", {
|
|
3749
|
+
className: "text-sm font-semibold",
|
|
3750
|
+
children: props.title
|
|
3751
|
+
}), jsx("p", {
|
|
3752
|
+
className: "text-sm text-muted-foreground",
|
|
3753
|
+
children: props.empty ?? t("table.empty", { defaultValue: "No records" })
|
|
3754
|
+
})]
|
|
3729
3755
|
});
|
|
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];
|
|
3756
|
+
}
|
|
3757
|
+
function placementScope(scope, owner) {
|
|
3758
|
+
return scope ?? (owner === void 0 ? "session" : "none");
|
|
3759
|
+
}
|
|
3760
|
+
//#endregion
|
|
3761
|
+
//#region src/list-kit/pieces/bulk-reach.tsx
|
|
3762
|
+
function useBulkFlowReachable(resource) {
|
|
3763
|
+
const app = useApp();
|
|
3764
|
+
const control = useFlowRowResolver(resource);
|
|
3765
|
+
return (app.registry.get(resource)?.actions.bulk ?? []).some((action) => isFlowAction(action) && control(action.name, void 0).state !== "absent");
|
|
3766
|
+
}
|
|
3767
|
+
function ExportReach(props) {
|
|
3768
|
+
const { available } = useResourceExport(props.resource);
|
|
3769
|
+
const capability = useCapability("run", {
|
|
3770
|
+
resource: props.resource,
|
|
3771
|
+
target: {
|
|
3772
|
+
kind: "action",
|
|
3773
|
+
name: "export"
|
|
3774
|
+
},
|
|
3775
|
+
mode: "list"
|
|
3776
|
+
});
|
|
3777
|
+
return props.children(available && capability.allowed);
|
|
3778
|
+
}
|
|
3779
|
+
function BulkExportReach(props) {
|
|
3780
|
+
return useOptionalDataRuntime() !== null ? jsx(ExportReach, { ...props }) : props.children(false);
|
|
3757
3781
|
}
|
|
3758
3782
|
//#endregion
|
|
3759
3783
|
//#region src/list-kit/pieces/piece.tsx
|
|
@@ -3821,15 +3845,47 @@ function ForceTableView(props) {
|
|
|
3821
3845
|
}
|
|
3822
3846
|
//#endregion
|
|
3823
3847
|
//#region src/list-kit/pieces/list-body.tsx
|
|
3848
|
+
function LinkDetachRowButton(props) {
|
|
3849
|
+
const { resource, linkEditing } = useListSurface();
|
|
3850
|
+
const t = useOptionalT();
|
|
3851
|
+
const [busy, setBusy] = useState(false);
|
|
3852
|
+
const detach = linkEditing?.detach;
|
|
3853
|
+
if (detach === void 0) return null;
|
|
3854
|
+
const label = t("field.link.detach", { defaultValue: "Detach" });
|
|
3855
|
+
return jsx(Button, {
|
|
3856
|
+
type: "button",
|
|
3857
|
+
size: "icon",
|
|
3858
|
+
variant: "ghost",
|
|
3859
|
+
className: "size-7",
|
|
3860
|
+
"aria-label": label,
|
|
3861
|
+
title: label,
|
|
3862
|
+
disabled: busy,
|
|
3863
|
+
"data-testid": `list-link-detach:${resource}:${props.row.id}`,
|
|
3864
|
+
onClick: (event) => {
|
|
3865
|
+
event.stopPropagation();
|
|
3866
|
+
setBusy(true);
|
|
3867
|
+
detach.onDetach([props.row]).finally(() => setBusy(false));
|
|
3868
|
+
},
|
|
3869
|
+
children: jsx(IconUnlink, {
|
|
3870
|
+
className: "size-4",
|
|
3871
|
+
"aria-hidden": "true"
|
|
3872
|
+
})
|
|
3873
|
+
});
|
|
3874
|
+
}
|
|
3875
|
+
const linkDetachRow = (row) => jsx(LinkDetachRowButton, { row });
|
|
3824
3876
|
function ListBodyView() {
|
|
3825
3877
|
usePieceMount("body");
|
|
3826
3878
|
const list = useResourceListContext();
|
|
3827
|
-
const { resource, route, tableView, createAvailable, createPage, rowProps, rowsViewFor, emptyState, renderCell, readOnly } = useListSurface();
|
|
3879
|
+
const { resource, route, tableView, createAvailable, createPage, rowProps, rowsViewFor, emptyState, renderCell, readOnly, linkEditing, layoutId } = useListSurface();
|
|
3880
|
+
const detachable = readOnly && linkEditing?.detach !== void 0;
|
|
3881
|
+
const flowReachable = useBulkFlowReachable(resource);
|
|
3882
|
+
const selectable = (exportReachable) => tableView && (detachable || !readOnly && (flowReachable || exportReachable));
|
|
3828
3883
|
const item = useContext(ListItemContext);
|
|
3829
3884
|
const rowsView = useMemo(() => rowsViewFor?.(item), [rowsViewFor, item]);
|
|
3830
3885
|
const { query, pagination, cursor, count, columns, selection, filters, sorting, routing } = list;
|
|
3831
3886
|
const rows = policyRowsOf(list, list.rows);
|
|
3832
|
-
return jsx(
|
|
3887
|
+
if (query.unbound) return jsx(UnboundRelatedList, { resource });
|
|
3888
|
+
const view = (exportReachable) => jsx(ResourceList, {
|
|
3833
3889
|
resource,
|
|
3834
3890
|
data: rows,
|
|
3835
3891
|
total: pagination.total,
|
|
@@ -3846,29 +3902,50 @@ function ListBodyView() {
|
|
|
3846
3902
|
onRequestCount: count.available ? count.request : void 0,
|
|
3847
3903
|
isCountingRows: count.isLoading,
|
|
3848
3904
|
columns: columns.visible,
|
|
3905
|
+
columnUi: columns.ui,
|
|
3849
3906
|
isLoading: query.isLoading,
|
|
3850
3907
|
error: query.error,
|
|
3851
3908
|
onRetry: query.refetch,
|
|
3852
3909
|
createHref: route && createAvailable && createPage ? routing.createHref : void 0,
|
|
3853
3910
|
...rowProps,
|
|
3854
3911
|
...rowsView === void 0 ? {} : { rowsView },
|
|
3855
|
-
selectable:
|
|
3856
|
-
rowActions: !readOnly,
|
|
3912
|
+
selectable: selectable(exportReachable),
|
|
3913
|
+
rowActions: detachable ? linkDetachRow : !readOnly,
|
|
3857
3914
|
selectedIds: selection.ids,
|
|
3858
3915
|
onSelectionChange: selection.set,
|
|
3859
3916
|
isFiltered: filters.isFiltered,
|
|
3860
|
-
onClearFilter: filters.
|
|
3917
|
+
onClearFilter: filters.canReset ? filters.reset : void 0,
|
|
3861
3918
|
emptyState,
|
|
3862
3919
|
sort: sorting.value ?? [],
|
|
3863
3920
|
requiredColumns: columns.required,
|
|
3864
3921
|
resetLayout: columns.resetLayout,
|
|
3922
|
+
layoutId,
|
|
3865
3923
|
onHideColumn: columns.hide,
|
|
3866
3924
|
onSort: sorting.toggle,
|
|
3925
|
+
sortable: sorting.sortable,
|
|
3867
3926
|
renderCell
|
|
3868
3927
|
});
|
|
3928
|
+
return jsx(BulkExportReach, {
|
|
3929
|
+
resource,
|
|
3930
|
+
children: view
|
|
3931
|
+
});
|
|
3869
3932
|
}
|
|
3870
3933
|
const ListBody = listPiece("ListBody", ListBodyView);
|
|
3871
3934
|
//#endregion
|
|
3935
|
+
//#region src/resource/form-context.tsx
|
|
3936
|
+
const FormControlContext = createContext(null);
|
|
3937
|
+
const FormControlProvider = FormControlContext.Provider;
|
|
3938
|
+
function useFormControlOf(controller) {
|
|
3939
|
+
const latest = useRef(controller);
|
|
3940
|
+
latest.current = controller;
|
|
3941
|
+
return useMemo(() => ({ setValues: (patch) => {
|
|
3942
|
+
for (const [name, value] of Object.entries(patch)) latest.current.setValue(name, value);
|
|
3943
|
+
} }), []);
|
|
3944
|
+
}
|
|
3945
|
+
function useFormControl() {
|
|
3946
|
+
return useContext(FormControlContext);
|
|
3947
|
+
}
|
|
3948
|
+
//#endregion
|
|
3872
3949
|
//#region src/record/route-screen-context.ts
|
|
3873
3950
|
const RouteScreenClaimContext = createContext(false);
|
|
3874
3951
|
const RouteScreenContext = createContext(false);
|
|
@@ -3992,7 +4069,7 @@ function PanelHost({ node, region = "panel-body", renderChildren }) {
|
|
|
3992
4069
|
region,
|
|
3993
4070
|
form,
|
|
3994
4071
|
labelledBy: title === void 0 ? void 0 : titleId,
|
|
3995
|
-
children: renderChildren(node.children)
|
|
4072
|
+
children: renderChildren(node.children, node)
|
|
3996
4073
|
}) : null,
|
|
3997
4074
|
...title === void 0 ? {} : { title },
|
|
3998
4075
|
...description === void 0 ? {} : { description },
|
|
@@ -4027,10 +4104,12 @@ function TabsHost({ node, renderChildren }) {
|
|
|
4027
4104
|
const state = useScreenState();
|
|
4028
4105
|
const text = useNodeText();
|
|
4029
4106
|
const panels = node.children.filter((child) => child.state !== "absent");
|
|
4030
|
-
const
|
|
4107
|
+
const declared = compiled.nodes[node.index].options.default;
|
|
4108
|
+
const effectiveDefault = (declared === void 0 ? void 0 : panels.find((child) => child.key === declared)) ?? panels[0];
|
|
4109
|
+
const open = panels.find((child) => child.open === true) ?? effectiveDefault;
|
|
4031
4110
|
const urlSelect = useTabsUrlSelect();
|
|
4032
4111
|
const select = (panel, reveal) => {
|
|
4033
|
-
if (urlSelect !== null && node.key !== void 0 && panel.key !== void 0) urlSelect(node.key, panel.key, panel ===
|
|
4112
|
+
if (urlSelect !== null && node.key !== void 0 && panel.key !== void 0) urlSelect(node.key, panel.key, panel === effectiveDefault, reveal);
|
|
4034
4113
|
else state?.openPanel(node.id, panelValue(panel));
|
|
4035
4114
|
};
|
|
4036
4115
|
const onSelect = (key) => {
|
|
@@ -4042,7 +4121,7 @@ function TabsHost({ node, renderChildren }) {
|
|
|
4042
4121
|
key: panelValue(panel),
|
|
4043
4122
|
title: titleOf(panel),
|
|
4044
4123
|
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
|
|
4124
|
+
panel: panels.length === 1 || panel.open === true ? jsx(Fragment$1, { children: renderChildren(panel.children, panel) }) : null
|
|
4046
4125
|
}));
|
|
4047
4126
|
const { view: View, id } = useNodeView(node, "tabs");
|
|
4048
4127
|
const t = useFrameT(id);
|
|
@@ -4153,7 +4232,7 @@ function WizardHost({ node, renderChildren }) {
|
|
|
4153
4232
|
region: "step-panel",
|
|
4154
4233
|
form,
|
|
4155
4234
|
labelledBy: titleId,
|
|
4156
|
-
children: renderChildren(panel.children)
|
|
4235
|
+
children: renderChildren(panel.children, panel)
|
|
4157
4236
|
}),
|
|
4158
4237
|
...description === void 0 ? {} : { description },
|
|
4159
4238
|
titleId,
|
|
@@ -4199,18 +4278,23 @@ function WizardHost({ node, renderChildren }) {
|
|
|
4199
4278
|
//#region src/screen/nodes.tsx
|
|
4200
4279
|
function FailedNode({ node }) {
|
|
4201
4280
|
const { controller } = useScreenTree();
|
|
4202
|
-
|
|
4203
|
-
|
|
4204
|
-
|
|
4205
|
-
|
|
4206
|
-
|
|
4207
|
-
|
|
4208
|
-
|
|
4209
|
-
|
|
4210
|
-
|
|
4211
|
-
|
|
4212
|
-
|
|
4213
|
-
|
|
4281
|
+
let error;
|
|
4282
|
+
for (const name of node.failedOn) {
|
|
4283
|
+
const source = controller.sources[name];
|
|
4284
|
+
if (source?.status === "failed") {
|
|
4285
|
+
error = source.error;
|
|
4286
|
+
break;
|
|
4287
|
+
}
|
|
4288
|
+
}
|
|
4289
|
+
return jsx(LoadError, {
|
|
4290
|
+
surface: "tab",
|
|
4291
|
+
error,
|
|
4292
|
+
retry: () => controller.retry(node.failedOn),
|
|
4293
|
+
fallback: {
|
|
4294
|
+
key: "screen.failed",
|
|
4295
|
+
default: "This section could not be loaded."
|
|
4296
|
+
},
|
|
4297
|
+
attributes: nodeAttributes(node)
|
|
4214
4298
|
});
|
|
4215
4299
|
}
|
|
4216
4300
|
function failedOnlyOnMeta(node) {
|
|
@@ -4223,8 +4307,17 @@ function drawn(node, hiddenFields) {
|
|
|
4223
4307
|
if (node.state === "absent") return false;
|
|
4224
4308
|
return !(node.kind === "field" && node.field !== void 0 && hiddenFields?.has(node.field));
|
|
4225
4309
|
}
|
|
4226
|
-
function SectionChildren({ nodes }) {
|
|
4227
|
-
const { hiddenFields } = useScreenTree();
|
|
4310
|
+
function SectionChildren({ nodes, owner }) {
|
|
4311
|
+
const { compiled, hiddenFields } = useScreenTree();
|
|
4312
|
+
const text = useNodeText();
|
|
4313
|
+
if (owner !== void 0 && !nodes.some((node) => drawn(node, hiddenFields))) {
|
|
4314
|
+
const empty = text(compiled.nodes[owner.index]?.options.empty);
|
|
4315
|
+
return empty === void 0 || empty === "" ? null : jsx("p", {
|
|
4316
|
+
"data-buro-region": "empty",
|
|
4317
|
+
className: "text-sm text-muted-foreground",
|
|
4318
|
+
children: empty
|
|
4319
|
+
});
|
|
4320
|
+
}
|
|
4228
4321
|
const out = [];
|
|
4229
4322
|
let run = [];
|
|
4230
4323
|
const flush = () => {
|
|
@@ -4246,8 +4339,11 @@ function SectionChildren({ nodes }) {
|
|
|
4246
4339
|
flush();
|
|
4247
4340
|
return out;
|
|
4248
4341
|
}
|
|
4249
|
-
function renderSection(nodes) {
|
|
4250
|
-
return jsx(SectionChildren, {
|
|
4342
|
+
function renderSection(nodes, owner) {
|
|
4343
|
+
return jsx(SectionChildren, {
|
|
4344
|
+
nodes,
|
|
4345
|
+
owner
|
|
4346
|
+
});
|
|
4251
4347
|
}
|
|
4252
4348
|
function Layout({ node }) {
|
|
4253
4349
|
return jsx("div", {
|
|
@@ -4369,17 +4465,14 @@ function pageHost(page) {
|
|
|
4369
4465
|
}
|
|
4370
4466
|
//#endregion
|
|
4371
4467
|
//#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) {
|
|
4468
|
+
function issueText(issue, caps, i18nPrefix) {
|
|
4377
4469
|
return resolveValidationMessage({
|
|
4378
4470
|
code: issue.code ?? "",
|
|
4379
4471
|
message: issue.message,
|
|
4380
|
-
...issue.
|
|
4472
|
+
...issue.check === void 0 ? {} : { check: issue.check }
|
|
4381
4473
|
}, {
|
|
4382
|
-
|
|
4474
|
+
has: caps.has,
|
|
4475
|
+
t: caps.t,
|
|
4383
4476
|
...issue.field === "_form" ? {} : { fieldKey: `resources.${i18nPrefix}.fields.${issue.field}` }
|
|
4384
4477
|
});
|
|
4385
4478
|
}
|
|
@@ -4446,7 +4539,9 @@ function DeclarativeFormBody(props) {
|
|
|
4446
4539
|
...props.surface === void 0 ? {} : { surface: props.surface },
|
|
4447
4540
|
...props.onSubmit === void 0 ? {} : { onSubmit: props.onSubmit }
|
|
4448
4541
|
});
|
|
4449
|
-
const
|
|
4542
|
+
const formControl = useFormControlOf(form);
|
|
4543
|
+
const chromeT = useChromeT();
|
|
4544
|
+
const caps = useI18nCaps();
|
|
4450
4545
|
const disabled = props.disabled === true;
|
|
4451
4546
|
const uid = useId();
|
|
4452
4547
|
const leafIds = useMemo(() => ({
|
|
@@ -4456,8 +4551,8 @@ function DeclarativeFormBody(props) {
|
|
|
4456
4551
|
const formErrors = form.errors.filter((issue) => issue.field === "_form");
|
|
4457
4552
|
const retryableChecks = [];
|
|
4458
4553
|
for (const issue of form.checkOutcome?.blockers ?? []) {
|
|
4459
|
-
const checkId = issue.
|
|
4460
|
-
if (issue.
|
|
4554
|
+
const checkId = issue.check?.id;
|
|
4555
|
+
if (issue.check?.cause === void 0 || typeof checkId !== "string") continue;
|
|
4461
4556
|
if (!retryableChecks.includes(checkId)) retryableChecks.push(checkId);
|
|
4462
4557
|
}
|
|
4463
4558
|
const formWarnings = form.warnings.filter((issue) => issue.field === "_form");
|
|
@@ -4470,7 +4565,7 @@ function DeclarativeFormBody(props) {
|
|
|
4470
4565
|
formErrors.length > 0 && jsx("div", {
|
|
4471
4566
|
role: "alert",
|
|
4472
4567
|
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,
|
|
4568
|
+
children: formErrors.map((issue, index) => jsx("p", { children: issueText(issue, caps, prefix) }, index))
|
|
4474
4569
|
}),
|
|
4475
4570
|
retryableChecks.length > 0 && jsx("div", {
|
|
4476
4571
|
className: "mb-4 flex flex-wrap gap-2",
|
|
@@ -4480,25 +4575,28 @@ function DeclarativeFormBody(props) {
|
|
|
4480
4575
|
size: "sm",
|
|
4481
4576
|
"data-testid": `form-check-retry:${checkId}`,
|
|
4482
4577
|
onClick: () => void form.revalidate(),
|
|
4483
|
-
children:
|
|
4578
|
+
children: chromeT("actions.retry", { defaultValue: "Retry" })
|
|
4484
4579
|
}, checkId))
|
|
4485
4580
|
}),
|
|
4486
4581
|
formWarnings.length > 0 && jsx("div", {
|
|
4487
4582
|
role: "status",
|
|
4488
4583
|
"aria-live": "polite",
|
|
4489
4584
|
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,
|
|
4585
|
+
children: formWarnings.map((issue, index) => jsx("p", { children: issueText(issue, caps, prefix) }, index))
|
|
4491
4586
|
}),
|
|
4492
|
-
jsx(
|
|
4493
|
-
|
|
4494
|
-
|
|
4495
|
-
|
|
4496
|
-
|
|
4497
|
-
|
|
4498
|
-
|
|
4499
|
-
|
|
4500
|
-
|
|
4501
|
-
|
|
4587
|
+
jsx(FormControlProvider, {
|
|
4588
|
+
value: formControl,
|
|
4589
|
+
children: jsx(ScreenTree, {
|
|
4590
|
+
region: "form-body",
|
|
4591
|
+
resource: prefix,
|
|
4592
|
+
mode: "create",
|
|
4593
|
+
compiled: form.screen,
|
|
4594
|
+
controller: form,
|
|
4595
|
+
leafIds,
|
|
4596
|
+
...props.renderField === void 0 ? {} : { renderField: props.renderField },
|
|
4597
|
+
disabled,
|
|
4598
|
+
...props.bodyClassName === void 0 ? {} : { className: props.bodyClassName }
|
|
4599
|
+
})
|
|
4502
4600
|
}),
|
|
4503
4601
|
props.slots?.footer === void 0 ? jsx("div", {
|
|
4504
4602
|
className: "mt-4 flex justify-end",
|
|
@@ -4506,7 +4604,7 @@ function DeclarativeFormBody(props) {
|
|
|
4506
4604
|
type: "submit",
|
|
4507
4605
|
disabled: !form.canSubmit || disabled,
|
|
4508
4606
|
onMouseDown: (event) => event.preventDefault(),
|
|
4509
|
-
children: props.submitLabel ??
|
|
4607
|
+
children: props.submitLabel ?? chromeT("actions.save", { defaultValue: "Save" })
|
|
4510
4608
|
})
|
|
4511
4609
|
}) : typeof props.slots.footer === "function" ? props.slots.footer({ canSubmit: form.canSubmit && !disabled }) : props.slots.footer
|
|
4512
4610
|
]
|
|
@@ -4591,7 +4689,8 @@ function ExportOptionsDirty({ dirty }) {
|
|
|
4591
4689
|
return null;
|
|
4592
4690
|
}
|
|
4593
4691
|
function getNoRunState() {}
|
|
4594
|
-
function ExportDialog({ open, onOpenChange, resource, target, visibleColumns, estimatedCount, scope }) {
|
|
4692
|
+
function ExportDialog({ open, onOpenChange, resource: resourceInput, target, visibleColumns, estimatedCount, scope }) {
|
|
4693
|
+
const resource = routeResourceKey(resourceInput);
|
|
4595
4694
|
const t = useOptionalT();
|
|
4596
4695
|
const dialogTestId = useTestIds().exportDialog(resource);
|
|
4597
4696
|
const { formats, start, exporter, available, error, loading } = useResourceExport(resource, {
|
|
@@ -4713,14 +4812,15 @@ function ExportDialog({ open, onOpenChange, resource, target, visibleColumns, es
|
|
|
4713
4812
|
})] }), jsx(ExportProgressView, {
|
|
4714
4813
|
state: runState,
|
|
4715
4814
|
dialogTestId,
|
|
4716
|
-
t
|
|
4815
|
+
t,
|
|
4816
|
+
caps
|
|
4717
4817
|
})] }) : jsxs(Fragment$1, { children: [jsxs(DialogHeader, { children: [jsx(DialogTitle, { children: titleText }), jsx(DialogDescription, {
|
|
4718
4818
|
className: "sr-only",
|
|
4719
4819
|
children: titleText
|
|
4720
4820
|
})] }), !available ? jsx("p", {
|
|
4721
4821
|
role: "alert",
|
|
4722
4822
|
"data-testid": `${dialogTestId}:unavailable`,
|
|
4723
|
-
children: error
|
|
4823
|
+
children: error === void 0 || error === null ? t("export.dialog.unavailable") : resolveErrorText(error, caps)
|
|
4724
4824
|
}) : jsxs(Fragment$1, { children: [
|
|
4725
4825
|
loading ? jsx("p", {
|
|
4726
4826
|
role: "status",
|
|
@@ -4799,7 +4899,7 @@ function ExportDialog({ open, onOpenChange, resource, target, visibleColumns, es
|
|
|
4799
4899
|
})
|
|
4800
4900
|
});
|
|
4801
4901
|
}
|
|
4802
|
-
function ExportProgressView({ state, dialogTestId, t }) {
|
|
4902
|
+
function ExportProgressView({ state, dialogTestId, t, caps }) {
|
|
4803
4903
|
if (state === void 0) return null;
|
|
4804
4904
|
if (state.status === "running") {
|
|
4805
4905
|
const total = state.progress?.total;
|
|
@@ -4820,7 +4920,7 @@ function ExportProgressView({ state, dialogTestId, t }) {
|
|
|
4820
4920
|
if (state.status === "failed") return jsx("p", {
|
|
4821
4921
|
role: "alert",
|
|
4822
4922
|
"data-testid": `${dialogTestId}:error`,
|
|
4823
|
-
children: `${t("export.dialog.failed")}: ${state.error
|
|
4923
|
+
children: `${t("export.dialog.failed")}: ${resolveErrorText(state.error, caps)}`
|
|
4824
4924
|
});
|
|
4825
4925
|
if (state.status === "canceled") return jsx("p", {
|
|
4826
4926
|
"data-testid": `${dialogTestId}:canceled`,
|
|
@@ -4833,6 +4933,7 @@ function ExportProgressView({ state, dialogTestId, t }) {
|
|
|
4833
4933
|
function BulkSummary(props) {
|
|
4834
4934
|
const loaded = useLoaded(loadBulkSummary);
|
|
4835
4935
|
const t = useOptionalT();
|
|
4936
|
+
const chromeT = useChromeT();
|
|
4836
4937
|
const failed = (retry) => jsxs("div", {
|
|
4837
4938
|
role: "alert",
|
|
4838
4939
|
className: "flex items-center gap-2 text-sm",
|
|
@@ -4841,7 +4942,7 @@ function BulkSummary(props) {
|
|
|
4841
4942
|
size: "sm",
|
|
4842
4943
|
variant: "outline",
|
|
4843
4944
|
onClick: retry,
|
|
4844
|
-
children:
|
|
4945
|
+
children: chromeT("actions.retry", { defaultValue: "Retry" })
|
|
4845
4946
|
})]
|
|
4846
4947
|
});
|
|
4847
4948
|
if (loaded.error !== void 0) return failed(loaded.retry);
|
|
@@ -4859,21 +4960,26 @@ function BulkFlowButton({ resource, actionName, ids, onSelectionChange }) {
|
|
|
4859
4960
|
const control = useFlowRowResolver(resource)(actionName, void 0);
|
|
4860
4961
|
const chrome = useFlowChrome(resource, actionName);
|
|
4861
4962
|
const def = app.registry.get(resource);
|
|
4862
|
-
const
|
|
4963
|
+
const compiled = useMemo(() => {
|
|
4863
4964
|
try {
|
|
4864
|
-
return def === void 0 ?
|
|
4965
|
+
return def === void 0 ? void 0 : compileFlow(def, actionName);
|
|
4865
4966
|
} catch {
|
|
4866
|
-
return
|
|
4967
|
+
return;
|
|
4867
4968
|
}
|
|
4868
4969
|
}, [def, actionName]);
|
|
4970
|
+
const each = compiled?.each ?? false;
|
|
4869
4971
|
const [running, setRunning] = useState(false);
|
|
4870
4972
|
const [summary, setSummary] = useState(null);
|
|
4973
|
+
const afterSummary = useRef(null);
|
|
4871
4974
|
const testid = tid.bulkAction(resource, actionName);
|
|
4872
4975
|
if (control.state === "absent") return null;
|
|
4873
4976
|
const variant = chrome.destructive ? "destructive" : "outline";
|
|
4874
4977
|
const overLimit = each && ids.length > BULK_EACH_LIMIT;
|
|
4875
4978
|
if (overLimit || control.state === "disabled") return jsx(DisabledAction, {
|
|
4876
|
-
reason: overLimit ?
|
|
4979
|
+
reason: overLimit ? t("flow.bulkLimit", {
|
|
4980
|
+
defaultValue: `Select at most ${BULK_EACH_LIMIT}.`,
|
|
4981
|
+
count: BULK_EACH_LIMIT
|
|
4982
|
+
}) : control.reason,
|
|
4877
4983
|
label: chrome.label,
|
|
4878
4984
|
icon: chrome.icon,
|
|
4879
4985
|
testid,
|
|
@@ -4887,11 +4993,20 @@ function BulkFlowButton({ resource, actionName, ids, onSelectionChange }) {
|
|
|
4887
4993
|
const finish = (result, progress) => {
|
|
4888
4994
|
setRunning(false);
|
|
4889
4995
|
if (progress === void 0) return;
|
|
4890
|
-
|
|
4996
|
+
const summarize = result.outcome !== "success" || progress.rereadSkipped.some((entry) => entry.policy === "warn");
|
|
4997
|
+
if (summarize) setSummary({
|
|
4891
4998
|
progress,
|
|
4892
4999
|
total: ids.length
|
|
4893
5000
|
});
|
|
4894
|
-
|
|
5001
|
+
const remaining = ids.filter((id) => !progress.success.includes(id));
|
|
5002
|
+
if (summarize && remaining.length === 0) afterSummary.current = () => onSelectionChange?.([]);
|
|
5003
|
+
else onSelectionChange?.(remaining);
|
|
5004
|
+
};
|
|
5005
|
+
const closeSummary = () => {
|
|
5006
|
+
setSummary(null);
|
|
5007
|
+
const pending = afterSummary.current;
|
|
5008
|
+
afterSummary.current = null;
|
|
5009
|
+
pending?.();
|
|
4895
5010
|
};
|
|
4896
5011
|
return jsxs(Fragment$1, { children: [
|
|
4897
5012
|
jsx(Button, {
|
|
@@ -4921,7 +5036,7 @@ function BulkFlowButton({ resource, actionName, ids, onSelectionChange }) {
|
|
|
4921
5036
|
jsx(Dialog, {
|
|
4922
5037
|
open: summary !== null,
|
|
4923
5038
|
onOpenChange: (open) => {
|
|
4924
|
-
if (!open)
|
|
5039
|
+
if (!open) closeSummary();
|
|
4925
5040
|
},
|
|
4926
5041
|
children: jsxs(DialogContent, {
|
|
4927
5042
|
"data-testid": `${tid.bulkSummary(resource, actionName)}:dialog`,
|
|
@@ -4935,14 +5050,15 @@ function BulkFlowButton({ resource, actionName, ids, onSelectionChange }) {
|
|
|
4935
5050
|
actionName,
|
|
4936
5051
|
label: chrome.labelText,
|
|
4937
5052
|
total: summary.total,
|
|
4938
|
-
progress: summary.progress
|
|
5053
|
+
progress: summary.progress,
|
|
5054
|
+
texts: compiled?.flow.texts
|
|
4939
5055
|
}),
|
|
4940
5056
|
jsx(DialogFooter, { children: jsx(Button, {
|
|
4941
5057
|
type: "button",
|
|
4942
5058
|
variant: "outline",
|
|
4943
5059
|
"data-testid": `${tid.bulkSummary(resource, actionName)}:close`,
|
|
4944
|
-
onClick:
|
|
4945
|
-
children:
|
|
5060
|
+
onClick: closeSummary,
|
|
5061
|
+
children: t("flow.close", { defaultValue: "Close" })
|
|
4946
5062
|
}) })
|
|
4947
5063
|
]
|
|
4948
5064
|
})
|
|
@@ -4958,10 +5074,12 @@ function orderBulkActions(actions) {
|
|
|
4958
5074
|
seq: index
|
|
4959
5075
|
})).sort((a, b) => a.weight - b.weight || a.seq - b.seq).map(({ action }) => action);
|
|
4960
5076
|
}
|
|
4961
|
-
function BulkActionToolbar({ resource, table, selectedRecords, selectedIds, onClearSelection, onSelectionChange, className, visibleColumns, scope }) {
|
|
5077
|
+
function BulkActionToolbar({ resource: resourceInput, table, selectedRecords, selectedIds, onClearSelection, onSelectionChange, className, visibleColumns, scope, actions }) {
|
|
5078
|
+
const resource = routeResourceKey(resourceInput);
|
|
4962
5079
|
const app = useApp();
|
|
4963
5080
|
const tid = useTestIds();
|
|
4964
5081
|
const t = useOptionalT();
|
|
5082
|
+
const chromeT = useChromeT();
|
|
4965
5083
|
const def = app.registry.get(resource);
|
|
4966
5084
|
const records = selectedRecords ?? (table ? table.getFilteredSelectedRowModel().rows.map((r) => r.original) : []);
|
|
4967
5085
|
const ids = selectedIds ?? records.map((record) => record.id);
|
|
@@ -4994,7 +5112,8 @@ function BulkActionToolbar({ resource, table, selectedRecords, selectedIds, onCl
|
|
|
4994
5112
|
className: "mx-0.5 h-6 w-px bg-border",
|
|
4995
5113
|
"aria-hidden": "true"
|
|
4996
5114
|
}),
|
|
4997
|
-
|
|
5115
|
+
actions !== void 0 ? actions : null,
|
|
5116
|
+
actions === void 0 && bulkActions.map((action) => jsx(BulkFlowButton, {
|
|
4998
5117
|
resource,
|
|
4999
5118
|
actionName: action.name,
|
|
5000
5119
|
ids,
|
|
@@ -5003,7 +5122,7 @@ function BulkActionToolbar({ resource, table, selectedRecords, selectedIds, onCl
|
|
|
5003
5122
|
else if (next.length === 0) handleClear();
|
|
5004
5123
|
}
|
|
5005
5124
|
}, action.name)),
|
|
5006
|
-
jsx(BulkExportButton, {
|
|
5125
|
+
actions === void 0 && jsx(BulkExportButton, {
|
|
5007
5126
|
resource,
|
|
5008
5127
|
ids,
|
|
5009
5128
|
visibleColumns: visibleColumns ?? [],
|
|
@@ -5020,7 +5139,7 @@ function BulkActionToolbar({ resource, table, selectedRecords, selectedIds, onCl
|
|
|
5020
5139
|
"aria-hidden": "true"
|
|
5021
5140
|
}), jsx("span", {
|
|
5022
5141
|
className: "hidden sm:inline",
|
|
5023
|
-
children:
|
|
5142
|
+
children: chromeT("actions.clearSelection", { defaultValue: "Clear" })
|
|
5024
5143
|
})]
|
|
5025
5144
|
})
|
|
5026
5145
|
]
|
|
@@ -5080,12 +5199,36 @@ function BulkExportButtonActive({ resource, ids, visibleColumns, scope }) {
|
|
|
5080
5199
|
}
|
|
5081
5200
|
//#endregion
|
|
5082
5201
|
//#region src/list-kit/pieces/list-bulk-bar.tsx
|
|
5202
|
+
function LinkDetachBulkButton(props) {
|
|
5203
|
+
const { selection } = useResourceListContext();
|
|
5204
|
+
const { resource } = useListSurface();
|
|
5205
|
+
const t = useOptionalT();
|
|
5206
|
+
const [busy, setBusy] = useState(false);
|
|
5207
|
+
return jsxs(Button, {
|
|
5208
|
+
type: "button",
|
|
5209
|
+
size: "sm",
|
|
5210
|
+
variant: "ghost",
|
|
5211
|
+
className: "h-8 gap-1.5 rounded-full px-2.5 text-destructive",
|
|
5212
|
+
disabled: busy,
|
|
5213
|
+
"data-testid": `list-link-detach-bulk:${resource}`,
|
|
5214
|
+
onClick: () => {
|
|
5215
|
+
setBusy(true);
|
|
5216
|
+
props.onDetach(selection.records).finally(() => setBusy(false));
|
|
5217
|
+
},
|
|
5218
|
+
children: [jsx(IconUnlink, {
|
|
5219
|
+
className: "size-4",
|
|
5220
|
+
"aria-hidden": "true"
|
|
5221
|
+
}), t("field.link.detach", { defaultValue: "Detach" })]
|
|
5222
|
+
});
|
|
5223
|
+
}
|
|
5083
5224
|
function ListBulkBarView() {
|
|
5084
5225
|
usePieceMount("bulk-bar");
|
|
5085
5226
|
const { selection, columns, scope } = useResourceListContext();
|
|
5086
|
-
const { resource, tableView, readOnly } = useListSurface();
|
|
5087
|
-
|
|
5227
|
+
const { resource, tableView, readOnly, linkEditing } = useListSurface();
|
|
5228
|
+
const detach = readOnly ? linkEditing?.detach : void 0;
|
|
5229
|
+
if (!tableView || readOnly && detach === void 0 || selection.ids.length === 0) return null;
|
|
5088
5230
|
return jsx(BulkActionToolbar, {
|
|
5231
|
+
...detach === void 0 ? {} : { actions: jsx(LinkDetachBulkButton, { onDetach: detach.onDetach }) },
|
|
5089
5232
|
resource,
|
|
5090
5233
|
selectedRecords: selection.records,
|
|
5091
5234
|
selectedIds: selection.ids,
|
|
@@ -5174,7 +5317,8 @@ function DataTableViewOptions({ table, getColumnLabel, isColumnRequired, getTest
|
|
|
5174
5317
|
}
|
|
5175
5318
|
//#endregion
|
|
5176
5319
|
//#region src/list-kit/columns-toggle.tsx
|
|
5177
|
-
function ColumnsToggle({ resource, def, allAvailable, required, visibleColumns, defaultVisible, onChange, onResetSizes, onRestoreDefaults }) {
|
|
5320
|
+
function ColumnsToggle({ resource: resourceInput, def, allAvailable, required, visibleColumns, defaultVisible, onChange, onResetSizes, onRestoreDefaults }) {
|
|
5321
|
+
const resource = routeResourceKey(resourceInput);
|
|
5178
5322
|
const t = useT();
|
|
5179
5323
|
const columnsCaps = useI18nCaps(resource);
|
|
5180
5324
|
const app = useApp();
|
|
@@ -5281,12 +5425,16 @@ function ListColumnsView() {
|
|
|
5281
5425
|
const ListColumns = listPiece("ListColumns", ListColumnsView);
|
|
5282
5426
|
//#endregion
|
|
5283
5427
|
//#region src/resource/create-button.tsx
|
|
5284
|
-
function CreateButton({ resource, href, testid, size = "sm", variant, label, className, onOpen }) {
|
|
5428
|
+
function CreateButton({ resource: resourceInput, href, testid, size = "sm", variant, label, className, onOpen }) {
|
|
5429
|
+
const resource = routeResourceKey(resourceInput);
|
|
5285
5430
|
const t = useOptionalT();
|
|
5286
5431
|
const Link = useLinkComponent();
|
|
5287
5432
|
const availability = useAvailability(resource, "create");
|
|
5288
5433
|
const gate = useEntryGate();
|
|
5289
|
-
const text = label ?? t("actions.create", {
|
|
5434
|
+
const text = label ?? t("actions.create", {
|
|
5435
|
+
resource,
|
|
5436
|
+
defaultValue: "Create"
|
|
5437
|
+
});
|
|
5290
5438
|
if (onOpen !== void 0) {
|
|
5291
5439
|
const answer = gate(resource, "create");
|
|
5292
5440
|
if (answer.state === "disabled") return jsx(DisabledAction, {
|
|
@@ -5357,7 +5505,8 @@ const ListCreate = listPiece("ListCreate", ListCreateView);
|
|
|
5357
5505
|
//#endregion
|
|
5358
5506
|
//#region src/export/export-button.tsx
|
|
5359
5507
|
const warnedProviderQueryResources = new Set();
|
|
5360
|
-
function ExportButton({ resource, className }) {
|
|
5508
|
+
function ExportButton({ resource: resourceInput, className }) {
|
|
5509
|
+
const resource = routeResourceKey(resourceInput);
|
|
5361
5510
|
if (!(useOptionalDataRuntime() !== null)) return null;
|
|
5362
5511
|
return jsx(ExportButtonActive, {
|
|
5363
5512
|
resource,
|
|
@@ -5378,36 +5527,19 @@ function ExportButtonActive({ resource, className }) {
|
|
|
5378
5527
|
const tid = useTestIds();
|
|
5379
5528
|
const t = useOptionalT();
|
|
5380
5529
|
const [open, setOpen] = React.useState(false);
|
|
5381
|
-
const
|
|
5382
|
-
resource,
|
|
5383
|
-
def: list.def
|
|
5384
|
-
}), [resource, list.def]);
|
|
5530
|
+
const sent = list.query.sent;
|
|
5385
5531
|
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;
|
|
5532
|
+
if (sent === null) return null;
|
|
5533
|
+
const { filter, sort } = sent;
|
|
5395
5534
|
return {
|
|
5396
5535
|
kind: "filter",
|
|
5397
5536
|
...filter === void 0 ? {} : { filter },
|
|
5398
5537
|
...sort.length > 0 ? { sort } : {}
|
|
5399
5538
|
};
|
|
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
|
-
]);
|
|
5539
|
+
}, [sent]);
|
|
5540
|
+
const lastTarget = React.useRef(target);
|
|
5541
|
+
if (target !== null) lastTarget.current = target;
|
|
5542
|
+
const dialogTarget = target ?? lastTarget.current;
|
|
5411
5543
|
React.useEffect(() => {
|
|
5412
5544
|
if (!available || !capability.allowed) return;
|
|
5413
5545
|
if (list.providerQuery.value === void 0) return;
|
|
@@ -5421,7 +5553,9 @@ function ExportButtonActive({ resource, className }) {
|
|
|
5421
5553
|
resource
|
|
5422
5554
|
]);
|
|
5423
5555
|
if (!available || !capability.allowed || list.isForbidden) return null;
|
|
5424
|
-
|
|
5556
|
+
if (target === null && !open) return null;
|
|
5557
|
+
if (dialogTarget === null) return null;
|
|
5558
|
+
return jsxs(Fragment$1, { children: [target === null ? null : jsx(Button, {
|
|
5425
5559
|
type: "button",
|
|
5426
5560
|
variant: "outline",
|
|
5427
5561
|
size: "icon",
|
|
@@ -5440,7 +5574,7 @@ function ExportButtonActive({ resource, className }) {
|
|
|
5440
5574
|
open,
|
|
5441
5575
|
onOpenChange: setOpen,
|
|
5442
5576
|
resource,
|
|
5443
|
-
target,
|
|
5577
|
+
target: dialogTarget,
|
|
5444
5578
|
visibleColumns: list.columns.visible,
|
|
5445
5579
|
estimatedCount: list.pagination.total,
|
|
5446
5580
|
scope: list.scope
|
|
@@ -5458,8 +5592,9 @@ function ListExportView() {
|
|
|
5458
5592
|
const ListExport = listPiece("ListExport", ListExportView);
|
|
5459
5593
|
//#endregion
|
|
5460
5594
|
//#region src/list-kit/list-search-input.tsx
|
|
5461
|
-
function ListSearchInput({ resource
|
|
5462
|
-
const
|
|
5595
|
+
function ListSearchInput({ resource: resourceInput, value, onCommit, placeholder }) {
|
|
5596
|
+
const resource = routeResourceKey(resourceInput);
|
|
5597
|
+
const caps = useI18nCaps(resource);
|
|
5463
5598
|
const [text, setText] = useState(value);
|
|
5464
5599
|
const lastPushedRef = useRef(value);
|
|
5465
5600
|
const timerRef = useRef(null);
|
|
@@ -5474,7 +5609,7 @@ function ListSearchInput({ resource, label, value, onCommit, placeholder }) {
|
|
|
5474
5609
|
}, []);
|
|
5475
5610
|
return jsx(Input, {
|
|
5476
5611
|
type: "search",
|
|
5477
|
-
placeholder: placeholder ??
|
|
5612
|
+
placeholder: placeholder ?? resolveContentSlot(caps, ["list.search"]) ?? "Search",
|
|
5478
5613
|
value: text,
|
|
5479
5614
|
"data-testid": `list-search:${resource}`,
|
|
5480
5615
|
className: "h-8 w-full min-[420px]:w-72",
|
|
@@ -5492,24 +5627,18 @@ function ListSearchInput({ resource, label, value, onCommit, placeholder }) {
|
|
|
5492
5627
|
//#endregion
|
|
5493
5628
|
//#region src/list-kit/filter-builder/filter-region.tsx
|
|
5494
5629
|
function FilterRegion({ search } = {}) {
|
|
5495
|
-
const { resource, def, filters, pagination
|
|
5496
|
-
const
|
|
5497
|
-
const listCaps = useI18nCaps(resource);
|
|
5630
|
+
const { resource, def, filters, pagination } = useResourceListContext();
|
|
5631
|
+
const chromeT = useChromeT();
|
|
5498
5632
|
const testIdBase = `list-filter:${resource}`;
|
|
5633
|
+
const caps = useI18nCaps(resource);
|
|
5634
|
+
const placeholder = search === void 0 ? resolveContentSlot(caps, ["list.searchPlaceholder", "list.search"]) ?? "Search" : void 0;
|
|
5499
5635
|
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
5636
|
return jsxs("div", {
|
|
5507
5637
|
className: "flex flex-wrap items-center gap-2",
|
|
5508
5638
|
children: [
|
|
5509
5639
|
searchDecl !== void 0 && filters.searchEnabled && jsx(ListSearchInput, {
|
|
5510
5640
|
resource,
|
|
5511
|
-
|
|
5512
|
-
placeholder: searchPlaceholder,
|
|
5641
|
+
...placeholder === void 0 ? {} : { placeholder },
|
|
5513
5642
|
value: filters.search,
|
|
5514
5643
|
onCommit: (v) => {
|
|
5515
5644
|
pagination.setPage(1);
|
|
@@ -5521,19 +5650,21 @@ function FilterRegion({ search } = {}) {
|
|
|
5521
5650
|
setGroup: filters.setGroup,
|
|
5522
5651
|
available: filters.available,
|
|
5523
5652
|
fixed: filters.fixed,
|
|
5653
|
+
presets: filters.presets,
|
|
5654
|
+
setPreset: filters.setPreset,
|
|
5524
5655
|
pinnedFields: filters.pinnedFields,
|
|
5525
5656
|
pinnedValue: filters.value,
|
|
5526
5657
|
setPinnedValue: filters.set,
|
|
5527
5658
|
setPage: pagination.setPage,
|
|
5528
5659
|
testIdBase
|
|
5529
5660
|
}),
|
|
5530
|
-
filters.
|
|
5661
|
+
filters.canReset && jsx(Button, {
|
|
5531
5662
|
type: "button",
|
|
5532
5663
|
variant: "ghost",
|
|
5533
5664
|
size: "sm",
|
|
5534
5665
|
"data-testid": `${testIdBase}:clear-all`,
|
|
5535
|
-
onClick: filters.
|
|
5536
|
-
children:
|
|
5666
|
+
onClick: filters.reset,
|
|
5667
|
+
children: chromeT("filters.clearAll")
|
|
5537
5668
|
})
|
|
5538
5669
|
]
|
|
5539
5670
|
});
|
|
@@ -5663,6 +5794,7 @@ function ViewItem({ view, caps, resource, onClick, testid }) {
|
|
|
5663
5794
|
});
|
|
5664
5795
|
}
|
|
5665
5796
|
function EntryRow({ entry, onApply, onRename, onFork, onDelete, forkAvailable, applyTestId, renameTestId, forkTestId, deleteTestId, tr }) {
|
|
5797
|
+
const chromeT = useChromeT();
|
|
5666
5798
|
const canRename = entry.permissions.update && onRename !== void 0;
|
|
5667
5799
|
const canFork = forkAvailable && entry.permissions.fork && onFork !== void 0;
|
|
5668
5800
|
const canDelete = entry.permissions.remove && onDelete !== void 0;
|
|
@@ -5708,8 +5840,8 @@ function EntryRow({ entry, onApply, onRename, onFork, onDelete, forkAvailable, a
|
|
|
5708
5840
|
canDelete && jsx("button", {
|
|
5709
5841
|
type: "button",
|
|
5710
5842
|
onClick: onDelete,
|
|
5711
|
-
"aria-label":
|
|
5712
|
-
title:
|
|
5843
|
+
"aria-label": chromeT("actions.delete", { defaultValue: "Delete" }),
|
|
5844
|
+
title: chromeT("actions.delete", { defaultValue: "Delete" }),
|
|
5713
5845
|
"data-testid": deleteTestId,
|
|
5714
5846
|
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
5847
|
children: jsx(IconTrash, {
|
|
@@ -5749,7 +5881,8 @@ function groupByScope(entries) {
|
|
|
5749
5881
|
}
|
|
5750
5882
|
return order.map((id) => byId.get(id));
|
|
5751
5883
|
}
|
|
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" }) {
|
|
5884
|
+
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" }) {
|
|
5885
|
+
const resource = resourceInput === void 0 ? void 0 : routeResourceKey(resourceInput);
|
|
5753
5886
|
const t = useOptionalT();
|
|
5754
5887
|
const caps = useI18nCaps(resource);
|
|
5755
5888
|
const tr = (key, fallback) => t?.(key, { defaultValue: fallback }) ?? fallback;
|
|
@@ -5963,9 +6096,10 @@ function useOptionalFrameChrome() {
|
|
|
5963
6096
|
//#endregion
|
|
5964
6097
|
//#region src/record/frame-pieces.tsx
|
|
5965
6098
|
function useFrameId() {
|
|
5966
|
-
const { recordId, record } = useRecordFrame();
|
|
6099
|
+
const { resource, recordId, record } = useRecordFrame();
|
|
6100
|
+
const def = useApp().registry.get(resource);
|
|
5967
6101
|
if (recordId !== void 0) return recordId;
|
|
5968
|
-
const id = record
|
|
6102
|
+
const id = recordKeyOf(def, record);
|
|
5969
6103
|
return typeof id === "string" || typeof id === "number" ? String(id) : void 0;
|
|
5970
6104
|
}
|
|
5971
6105
|
function RecordTitleView() {
|
|
@@ -5976,7 +6110,7 @@ function RecordTitleView() {
|
|
|
5976
6110
|
const t = useOptionalT();
|
|
5977
6111
|
const title = useRecordTitle(resource, mode === "create" ? void 0 : id, record);
|
|
5978
6112
|
if (useFrameChrome().hostTitle === true) return null;
|
|
5979
|
-
const text = mode === "create" ? t
|
|
6113
|
+
const text = mode === "create" ? resolveResourceTitle(caps, t, resource, "createTitle", { label: resolveResourceLabel(caps, resource, "single", app.registry.get(resource)) }) : title;
|
|
5980
6114
|
if (text === "") return null;
|
|
5981
6115
|
return jsx("h2", {
|
|
5982
6116
|
className: "min-w-0 break-words text-xl font-semibold tracking-tight",
|
|
@@ -5989,10 +6123,7 @@ function RecordActionsView() {
|
|
|
5989
6123
|
const id = useFrameId();
|
|
5990
6124
|
const holder = useScreenState()?.inline;
|
|
5991
6125
|
if (mode === "create" || record === void 0 || id === void 0) return null;
|
|
5992
|
-
const row = {
|
|
5993
|
-
...policyRecordOf(controller, record),
|
|
5994
|
-
id
|
|
5995
|
-
};
|
|
6126
|
+
const row = withRecordKeys(policyRecordOf(controller) ?? record, { id });
|
|
5996
6127
|
holder?.declare(inlineFlowNames(controller.screen, controller.plan));
|
|
5997
6128
|
return jsxs("div", {
|
|
5998
6129
|
className: "flex flex-wrap items-center gap-2",
|
|
@@ -6000,6 +6131,7 @@ function RecordActionsView() {
|
|
|
6000
6131
|
resource,
|
|
6001
6132
|
record: row,
|
|
6002
6133
|
surface: mode,
|
|
6134
|
+
sources: controller.sources,
|
|
6003
6135
|
...holder === void 0 ? {} : { hideFlows: (name) => holder.suppressed(name) }
|
|
6004
6136
|
}), mode === "show" ? extraActions : null]
|
|
6005
6137
|
});
|
|
@@ -6029,24 +6161,38 @@ function FormActionsView() {
|
|
|
6029
6161
|
const app = useApp();
|
|
6030
6162
|
const navigate = useRouteNavigate();
|
|
6031
6163
|
const t = useOptionalT();
|
|
6164
|
+
const chromeT = useChromeT();
|
|
6165
|
+
const caps = useI18nCaps();
|
|
6166
|
+
const labelCaps = useI18nCaps(resource);
|
|
6032
6167
|
const tid = useTestIds();
|
|
6033
6168
|
const id = useFrameId();
|
|
6169
|
+
const back = useReturnTarget(resource, mode === "create" ? "create" : "edit", mode === "edit" ? id : void 0);
|
|
6170
|
+
const failedIssues = chrome.serverIssues;
|
|
6171
|
+
const livePlan = controller.plan;
|
|
6172
|
+
const placed = useMemo(() => new Set(livePlan?.placedPaths ?? []), [livePlan]);
|
|
6173
|
+
const alertIssues = useMemo(() => failedIssues === void 0 || failedIssues.length === 0 ? NO_ALERT_ISSUES : routeServerIssues({ issues: failedIssues }, (segment) => segment, (field, path) => field !== "_form" && (livePlan === null || livePlan.fields[field] === "active" && deepestPlacedPath(path, placed) !== void 0)).form, [
|
|
6174
|
+
failedIssues,
|
|
6175
|
+
livePlan,
|
|
6176
|
+
placed
|
|
6177
|
+
]);
|
|
6178
|
+
const fieldDefs = app.registry.get(resource)?.fields;
|
|
6034
6179
|
if (mode === "show") return null;
|
|
6035
|
-
const busy = controller.isSubmitting;
|
|
6180
|
+
const busy = controller.isSubmitting || chrome.created === true;
|
|
6036
6181
|
const cancel = () => {
|
|
6037
6182
|
if (chrome.onCancel !== void 0) chrome.onCancel();
|
|
6038
|
-
else navigate(
|
|
6183
|
+
else navigate(back, { origin: "inherit" });
|
|
6039
6184
|
};
|
|
6040
6185
|
const plan = controller.plan;
|
|
6041
6186
|
const undrawn = (field) => plan === null || plan.fields[field] === void 0 || plan.fields[field] === "inactive";
|
|
6042
|
-
const recordLevel = (issue) => issue.field === "_form" || undrawn(issue.field);
|
|
6187
|
+
const recordLevel = (issue) => issue.field === "_form" || undrawn(pathHead(issue.field)) || !placed.has(issue.field);
|
|
6043
6188
|
const undrawnBlockers = (controller.checkOutcome?.blockers ?? []).flatMap((issue) => {
|
|
6044
6189
|
const field = typeof issue.path?.[0] === "string" ? issue.path[0] : "_form";
|
|
6045
|
-
if (field !== "_form" && !undrawn(field)) return [];
|
|
6190
|
+
if (field !== "_form" && !undrawn(field) && deepestPlacedPath(issue.path, placed) !== void 0) return [];
|
|
6046
6191
|
if (controller.errors.some((own) => own.code === issue.code && own.field === field)) return [];
|
|
6047
6192
|
return [{
|
|
6048
6193
|
...issue,
|
|
6049
|
-
field
|
|
6194
|
+
field,
|
|
6195
|
+
path: field === "_form" ? [] : issue.path.slice(1)
|
|
6050
6196
|
}];
|
|
6051
6197
|
});
|
|
6052
6198
|
const formErrors = firstPreferred([...controller.errors.filter(recordLevel), ...undrawnBlockers]);
|
|
@@ -6054,21 +6200,44 @@ function FormActionsView() {
|
|
|
6054
6200
|
const text = (issue) => resolveValidationMessage({
|
|
6055
6201
|
code: issue.code ?? "",
|
|
6056
6202
|
message: issue.message,
|
|
6057
|
-
...issue.
|
|
6058
|
-
}, {
|
|
6203
|
+
...issue.check === void 0 ? {} : { check: issue.check }
|
|
6204
|
+
}, {
|
|
6205
|
+
has: caps.has,
|
|
6206
|
+
t: caps.t
|
|
6207
|
+
});
|
|
6059
6208
|
const retryable = [];
|
|
6060
6209
|
for (const issue of controller.checkOutcome?.blockers ?? []) {
|
|
6061
|
-
const checkId = issue.
|
|
6062
|
-
if (issue.
|
|
6210
|
+
const checkId = issue.check?.id;
|
|
6211
|
+
if (issue.check?.cause === void 0 || typeof checkId !== "string") continue;
|
|
6063
6212
|
if (!retryable.includes(checkId)) retryable.push(checkId);
|
|
6064
6213
|
}
|
|
6065
|
-
const failure = chrome.submitError === void 0 ? void 0 : failureText(chrome.submitError, t);
|
|
6214
|
+
const failure = chrome.submitError === void 0 ? void 0 : failureText(chrome.submitError, chrome.submitApplied, t, caps);
|
|
6215
|
+
const serverIssues = bySeverity(alertIssues).flatMap((issue) => {
|
|
6216
|
+
const head = issue.path[0];
|
|
6217
|
+
const name = typeof head === "string" ? head : void 0;
|
|
6218
|
+
const declared = name === void 0 ? void 0 : fieldDefs?.[name];
|
|
6219
|
+
const message = resolveValidationMessage({
|
|
6220
|
+
code: issue.code,
|
|
6221
|
+
message: issue.message,
|
|
6222
|
+
...issue.check === void 0 ? {} : { check: issue.check }
|
|
6223
|
+
}, {
|
|
6224
|
+
has: caps.has,
|
|
6225
|
+
t: caps.t,
|
|
6226
|
+
...declared === void 0 ? {} : { fieldKey: `resources.${resource}.fields.${name}` }
|
|
6227
|
+
});
|
|
6228
|
+
if (issue.path.length === 0 && message === failure) return [];
|
|
6229
|
+
return [{
|
|
6230
|
+
label: declared === void 0 || name === void 0 ? void 0 : resolveFieldLabel(labelCaps, name, declaredText("field", declared, "label")),
|
|
6231
|
+
message,
|
|
6232
|
+
severity: issue.severity
|
|
6233
|
+
}];
|
|
6234
|
+
});
|
|
6066
6235
|
const steps = controller.steps;
|
|
6067
6236
|
const midSteps = steps !== null && steps.ids.length > 1 && steps.current !== void 0 && steps.current !== steps.ids[steps.ids.length - 1];
|
|
6068
6237
|
return jsxs("div", {
|
|
6069
6238
|
className: "flex flex-col gap-3",
|
|
6070
6239
|
children: [
|
|
6071
|
-
failure === void 0 && formErrors.length === 0 ? null : jsxs("div", {
|
|
6240
|
+
failure === void 0 && formErrors.length === 0 && serverIssues.length === 0 ? null : jsxs("div", {
|
|
6072
6241
|
role: "alert",
|
|
6073
6242
|
className: "flex items-start gap-2 rounded-md border border-destructive/30 bg-destructive/10 px-3 py-2 text-sm text-destructive",
|
|
6074
6243
|
children: [
|
|
@@ -6078,10 +6247,22 @@ function FormActionsView() {
|
|
|
6078
6247
|
}),
|
|
6079
6248
|
jsxs("div", {
|
|
6080
6249
|
className: "leading-snug",
|
|
6081
|
-
children: [
|
|
6082
|
-
|
|
6083
|
-
|
|
6084
|
-
|
|
6250
|
+
children: [
|
|
6251
|
+
failure === void 0 ? null : jsx("p", {
|
|
6252
|
+
className: "font-medium",
|
|
6253
|
+
children: failure
|
|
6254
|
+
}),
|
|
6255
|
+
serverIssues.map((issue, index) => jsxs("p", {
|
|
6256
|
+
"data-testid": `form-server-issue:${resource}:${mode}:${index}`,
|
|
6257
|
+
"data-buro-severity": issue.severity,
|
|
6258
|
+
className: issue.severity === "error" ? void 0 : "text-warning-emphasis",
|
|
6259
|
+
children: [issue.label === void 0 ? null : jsxs("span", {
|
|
6260
|
+
className: "font-medium",
|
|
6261
|
+
children: [issue.label, ": "]
|
|
6262
|
+
}), issue.message]
|
|
6263
|
+
}, index)),
|
|
6264
|
+
formErrors.map((issue) => jsx("p", { children: text(issue) }, issueKey(issue)))
|
|
6265
|
+
]
|
|
6085
6266
|
}),
|
|
6086
6267
|
failure !== void 0 && steps !== null ? jsx(Button, {
|
|
6087
6268
|
type: "button",
|
|
@@ -6090,7 +6271,7 @@ function FormActionsView() {
|
|
|
6090
6271
|
className: "ml-auto",
|
|
6091
6272
|
onMouseDown: (event) => event.preventDefault(),
|
|
6092
6273
|
onClick: () => void chrome.submit(),
|
|
6093
|
-
children:
|
|
6274
|
+
children: chromeT("actions.retry", { defaultValue: "Retry" })
|
|
6094
6275
|
}) : null
|
|
6095
6276
|
]
|
|
6096
6277
|
}),
|
|
@@ -6102,7 +6283,7 @@ function FormActionsView() {
|
|
|
6102
6283
|
size: "sm",
|
|
6103
6284
|
"data-testid": `form-check-retry:${checkId}`,
|
|
6104
6285
|
onClick: () => void controller.revalidate(),
|
|
6105
|
-
children:
|
|
6286
|
+
children: chromeT("actions.retry", { defaultValue: "Retry" })
|
|
6106
6287
|
}, checkId))
|
|
6107
6288
|
}),
|
|
6108
6289
|
formWarnings.length === 0 ? null : jsxs("div", {
|
|
@@ -6128,7 +6309,7 @@ function FormActionsView() {
|
|
|
6128
6309
|
children: [jsx(IconLoader2, {
|
|
6129
6310
|
className: "h-3.5 w-3.5 animate-spin",
|
|
6130
6311
|
"aria-hidden": "true"
|
|
6131
|
-
}), jsx("span", { children:
|
|
6312
|
+
}), jsx("span", { children: chromeT("actions.validating", { defaultValue: "Validating…" }) })]
|
|
6132
6313
|
}) : null,
|
|
6133
6314
|
jsx(Button, {
|
|
6134
6315
|
type: "button",
|
|
@@ -6136,7 +6317,7 @@ function FormActionsView() {
|
|
|
6136
6317
|
onClick: cancel,
|
|
6137
6318
|
onMouseDown: (event) => event.preventDefault(),
|
|
6138
6319
|
"data-testid": tid.formCancel(resource, mode),
|
|
6139
|
-
children:
|
|
6320
|
+
children: chromeT("actions.cancel", { defaultValue: "Cancel" })
|
|
6140
6321
|
}),
|
|
6141
6322
|
midSteps ? null : jsxs(Button, {
|
|
6142
6323
|
type: "submit",
|
|
@@ -6153,37 +6334,71 @@ function FormActionsView() {
|
|
|
6153
6334
|
children: [busy ? jsx(IconLoader2, {
|
|
6154
6335
|
className: "mr-2 h-4 w-4 animate-spin",
|
|
6155
6336
|
"aria-hidden": "true"
|
|
6156
|
-
}) : null, busy ? mode === "edit" ?
|
|
6337
|
+
}) : 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
6338
|
})
|
|
6158
6339
|
]
|
|
6159
6340
|
})
|
|
6160
6341
|
]
|
|
6161
6342
|
});
|
|
6162
6343
|
}
|
|
6344
|
+
const NO_ALERT_ISSUES = Object.freeze([]);
|
|
6345
|
+
const SEVERITY_RANK = {
|
|
6346
|
+
error: 0,
|
|
6347
|
+
warning: 1,
|
|
6348
|
+
info: 2
|
|
6349
|
+
};
|
|
6350
|
+
function bySeverity(issues) {
|
|
6351
|
+
return [...issues].sort((a, b) => (SEVERITY_RANK[a.severity] ?? 3) - (SEVERITY_RANK[b.severity] ?? 3));
|
|
6352
|
+
}
|
|
6163
6353
|
function firstPreferred(issues) {
|
|
6164
|
-
const first = issues.find((issue) => issue.
|
|
6354
|
+
const first = issues.find((issue) => issue.check?.id === void 0) ?? issues[0];
|
|
6165
6355
|
return first === void 0 ? [] : [first];
|
|
6166
6356
|
}
|
|
6167
6357
|
function issueKey(issue) {
|
|
6168
6358
|
return JSON.stringify([
|
|
6169
6359
|
issue.code ?? "",
|
|
6170
6360
|
issue.message.key,
|
|
6171
|
-
issue.
|
|
6361
|
+
issue.check?.id ?? null
|
|
6172
6362
|
]);
|
|
6173
6363
|
}
|
|
6174
|
-
function failureText(error, t) {
|
|
6175
|
-
const
|
|
6176
|
-
|
|
6364
|
+
function failureText(error, settled, t, caps) {
|
|
6365
|
+
const texts = technicalTexts(error).filter((text) => text.length <= 1024);
|
|
6366
|
+
const strings = friendlyStringsFrom(t);
|
|
6367
|
+
const friendly = texts.map((text) => toFriendlyError(text, void 0, strings)).find((f) => f.replaced) ?? toFriendlyError(texts[0], void 0, strings);
|
|
6368
|
+
const applied = settled ?? ownDataProperty(error, "applied");
|
|
6369
|
+
if (applied === "partial" || applied === "unknown") {
|
|
6370
|
+
const key = applied === "partial" ? "errors.partlyApplied" : "errors.mayHaveBeenApplied";
|
|
6371
|
+
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.";
|
|
6372
|
+
const lead = texts.some(isNetworkFailure) ? strings.offline : "";
|
|
6373
|
+
return lead === "" ? sentence : `${lead} ${sentence}`;
|
|
6374
|
+
}
|
|
6375
|
+
const code = ownDataProperty(error, "code");
|
|
6376
|
+
const kind = ownDataProperty(error, "kind");
|
|
6377
|
+
const hasOwnSentence = typeof code === "string" && typeof kind === "string" && kind !== "transport" && Object.hasOwn(RECORD_KIND_KEYS, kind);
|
|
6378
|
+
const message = ownDataProperty(error, "message");
|
|
6379
|
+
if (friendly.replaced && !hasOwnSentence) return (typeof code === "string" ? specificErrorText({
|
|
6380
|
+
code,
|
|
6381
|
+
...isMessageReference(message) ? { message } : {}
|
|
6382
|
+
}, caps, { record: true }) : void 0) ?? friendly.title;
|
|
6383
|
+
if (typeof code !== "string") return strings.title;
|
|
6384
|
+
const reference = {
|
|
6385
|
+
code,
|
|
6386
|
+
...isMessageReference(message) ? { message } : {}
|
|
6387
|
+
};
|
|
6388
|
+
return specificErrorText(reference, caps, { record: true }) ?? (typeof kind === "string" ? kindErrorText(kind, caps, { record: true }) : void 0) ?? strings.title;
|
|
6177
6389
|
}
|
|
6178
|
-
function
|
|
6390
|
+
function ownDataProperty(error, name) {
|
|
6179
6391
|
if (error === null || typeof error !== "object") return void 0;
|
|
6180
6392
|
try {
|
|
6181
|
-
const descriptor = Object.getOwnPropertyDescriptor(error,
|
|
6182
|
-
return descriptor !== void 0 && "value" in descriptor
|
|
6393
|
+
const descriptor = Object.getOwnPropertyDescriptor(error, name);
|
|
6394
|
+
return descriptor !== void 0 && "value" in descriptor ? descriptor.value : void 0;
|
|
6183
6395
|
} catch {
|
|
6184
6396
|
return;
|
|
6185
6397
|
}
|
|
6186
6398
|
}
|
|
6399
|
+
function isMessageReference(value) {
|
|
6400
|
+
return value !== null && typeof value === "object" && typeof value.key === "string";
|
|
6401
|
+
}
|
|
6187
6402
|
function inlineFlowNames(screen, plan) {
|
|
6188
6403
|
if (screen === null) return [];
|
|
6189
6404
|
const names = [];
|
|
@@ -6456,31 +6671,35 @@ function PlannedFrame(props) {
|
|
|
6456
6671
|
})
|
|
6457
6672
|
}, name);
|
|
6458
6673
|
const { PlacementScope } = page;
|
|
6674
|
+
const claimedRoute = useContext(RouteSourceContext);
|
|
6459
6675
|
const itemNode = page.plan.slots.item?.[0];
|
|
6460
6676
|
const item = useMemo(() => itemNode === void 0 ? void 0 : itemHandle(itemNode), [itemNode]);
|
|
6461
6677
|
return jsx(RouteSourceContext.Provider, {
|
|
6462
6678
|
value: null,
|
|
6463
|
-
children: jsx(
|
|
6464
|
-
|
|
6465
|
-
|
|
6466
|
-
|
|
6467
|
-
|
|
6468
|
-
|
|
6469
|
-
|
|
6470
|
-
children: jsx(
|
|
6471
|
-
|
|
6472
|
-
|
|
6473
|
-
|
|
6679
|
+
children: jsx(ActionRouteContext.Provider, {
|
|
6680
|
+
value: claimedRoute,
|
|
6681
|
+
children: jsx(PlacementScope, { children: jsx(ScreenTreeScope, {
|
|
6682
|
+
resource,
|
|
6683
|
+
mode: "show",
|
|
6684
|
+
compiled,
|
|
6685
|
+
page,
|
|
6686
|
+
children: jsx(ListItemContext.Provider, {
|
|
6687
|
+
value: item,
|
|
6688
|
+
children: jsx(FrameView, {
|
|
6689
|
+
resource,
|
|
6690
|
+
compiled,
|
|
6691
|
+
slots
|
|
6692
|
+
})
|
|
6474
6693
|
})
|
|
6475
|
-
})
|
|
6476
|
-
})
|
|
6694
|
+
}) })
|
|
6695
|
+
})
|
|
6477
6696
|
});
|
|
6478
6697
|
}
|
|
6479
6698
|
function ListFrameHost({ resource }) {
|
|
6480
6699
|
const compiled = useCompiledListScreen(resource);
|
|
6481
6700
|
const hasDataRuntime = useOptionalDataRuntime() !== null;
|
|
6482
6701
|
const { def } = useResourceListContext();
|
|
6483
|
-
const rowsView =
|
|
6702
|
+
const rowsView = def?.views?.list?.initial?.view ?? "table";
|
|
6484
6703
|
if (compiled === null) return jsx(Arrangement, {});
|
|
6485
6704
|
if (compiled.nodes.length === 0 && rowsView === "table") {
|
|
6486
6705
|
const empty = {};
|
|
@@ -6499,8 +6718,12 @@ function ListFrameHost({ resource }) {
|
|
|
6499
6718
|
}
|
|
6500
6719
|
//#endregion
|
|
6501
6720
|
//#region src/list-kit/resource-list-surface.tsx
|
|
6502
|
-
|
|
6503
|
-
|
|
6721
|
+
function declaresDisplay(field) {
|
|
6722
|
+
return field !== void 0 && resolveSurfaceUi(field, "column").use !== void 0;
|
|
6723
|
+
}
|
|
6724
|
+
const NO_INITIAL_FILTERS = Object.freeze({});
|
|
6725
|
+
const RelatedCards = lazy(async () => ({ default: (await import("./related-list-cards-BvkD5OcC.js")).RelatedCards }));
|
|
6726
|
+
const RelatedTimeline = lazy(async () => ({ default: (await import("./related-list-timeline-DCrwN9oO.js")).RelatedTimeline }));
|
|
6504
6727
|
function discriminatorOptionLabel(field, value) {
|
|
6505
6728
|
const text = fieldOptionText(field, value);
|
|
6506
6729
|
if (text !== void 0) return text;
|
|
@@ -6508,10 +6731,17 @@ function discriminatorOptionLabel(field, value) {
|
|
|
6508
6731
|
const label = (Array.isArray(declared?.options) ? declared.options : Array.isArray(declared?.meta?.options) ? declared.meta.options : []).find((option) => option?.value === value)?.label;
|
|
6509
6732
|
return typeof label === "string" ? label : value;
|
|
6510
6733
|
}
|
|
6511
|
-
function
|
|
6734
|
+
function declaredInitialSort(def) {
|
|
6735
|
+
return def?.views?.list?.initial?.sort;
|
|
6736
|
+
}
|
|
6737
|
+
function declaredRouteView(def) {
|
|
6738
|
+
return def?.views?.list?.initial?.view;
|
|
6739
|
+
}
|
|
6740
|
+
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
6741
|
const route = variant === "route";
|
|
6513
6742
|
const t = useT();
|
|
6514
6743
|
const optionalT = useOptionalT();
|
|
6744
|
+
const chromeT = useChromeT();
|
|
6515
6745
|
const tid = useTestIds();
|
|
6516
6746
|
const listCaps = useI18nCaps(resource);
|
|
6517
6747
|
const RouteFiltersSlot = useListSlot("filters", resource);
|
|
@@ -6526,17 +6756,45 @@ function ResourceListSurface({ resource, variant, state, fixedFilters, pageSize,
|
|
|
6526
6756
|
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
6757
|
}, [removedBody, resource]);
|
|
6528
6758
|
const searchDecl = useMemo(() => searchField === void 0 ? void 0 : { fields: [searchField] }, [searchField]);
|
|
6529
|
-
const
|
|
6759
|
+
const appRegistry = useApp().registry;
|
|
6760
|
+
const effectiveView = route ? declaredRouteView(appRegistry.get(resource)) ?? "table" : view ?? "table";
|
|
6761
|
+
const timeline = effectiveView === "timeline";
|
|
6762
|
+
const initialKey = JSON.stringify(initial ?? null);
|
|
6763
|
+
const placementInitial = useMemo(() => route ? void 0 : {
|
|
6764
|
+
...initial,
|
|
6765
|
+
filters: initial?.filters ?? NO_INITIAL_FILTERS
|
|
6766
|
+
}, [route, initialKey]);
|
|
6767
|
+
const timelineAxis = (placementInitial?.sort ?? declaredInitialSort(appRegistry.get(resource)))?.[0]?.field;
|
|
6768
|
+
const columnNames = useMemo(() => columnEntries?.map(columnName), [columnEntries]);
|
|
6769
|
+
const list = useResourceList(resource, route ? {
|
|
6770
|
+
meta,
|
|
6771
|
+
...timeline ? { timeline } : {}
|
|
6772
|
+
} : {
|
|
6773
|
+
...timeline ? { timeline } : {},
|
|
6530
6774
|
...state === void 0 ? {} : { state },
|
|
6531
6775
|
...fixedFilters === void 0 ? {} : { fixedFilters },
|
|
6532
|
-
...
|
|
6776
|
+
...placementInitial === void 0 ? {} : { initial: placementInitial },
|
|
6533
6777
|
...searchDecl === void 0 ? {} : { search: searchDecl },
|
|
6534
|
-
...
|
|
6778
|
+
...columnEntries === void 0 ? {} : { columns: columnEntries },
|
|
6535
6779
|
...scope === void 0 ? {} : { scope },
|
|
6536
6780
|
...countPolicy === void 0 ? {} : { countPolicy }
|
|
6537
6781
|
});
|
|
6538
6782
|
const { def, query, pagination, cursor, count, filters, sorting, columns, selection, savedViews, routing, live, droppedPredicates } = list;
|
|
6539
6783
|
policyRowsOf(list, list.rows);
|
|
6784
|
+
const selectionRef = useRef(selection);
|
|
6785
|
+
selectionRef.current = selection;
|
|
6786
|
+
const onDetach = linkEditing?.detach?.onDetach;
|
|
6787
|
+
const surfaceLinkEditing = useMemo(() => onDetach === void 0 ? void 0 : { detach: { onDetach: async (detached) => {
|
|
6788
|
+
const left = await onDetach(detached);
|
|
6789
|
+
if (left.length > 0) {
|
|
6790
|
+
const gone = new Set(left);
|
|
6791
|
+
const current = selectionRef.current;
|
|
6792
|
+
if (current.ids.some((id) => gone.has(id))) current.set(current.ids.filter((id) => !gone.has(id)));
|
|
6793
|
+
}
|
|
6794
|
+
return left;
|
|
6795
|
+
} } }, [onDetach]);
|
|
6796
|
+
const onAttach = linkEditing?.attach?.onAttach;
|
|
6797
|
+
const attachBusy = linkEditing?.attach?.busy === true;
|
|
6540
6798
|
const hasDataRuntime = useOptionalDataRuntime() !== null;
|
|
6541
6799
|
const scopeLabel = (scope) => scope.id === LOCAL_USER_SCOPE_ID ? t("toolbar.viewsMyScope") : scope.label;
|
|
6542
6800
|
const createAvailable = useAvailability(resource, "create").state === "available";
|
|
@@ -6584,7 +6842,6 @@ function ResourceListSurface({ resource, variant, state, fixedFilters, pageSize,
|
|
|
6584
6842
|
resource,
|
|
6585
6843
|
routing.rowHref
|
|
6586
6844
|
]);
|
|
6587
|
-
const effectiveView = route ? (def?.views?.list)?.view ?? "table" : view ?? "table";
|
|
6588
6845
|
const tableView = effectiveView === "table";
|
|
6589
6846
|
const rowsViewFor = useMemo(() => tableView ? void 0 : (planned) => ({
|
|
6590
6847
|
skeleton: effectiveView === "cards" ? jsx(RelatedCardsSkeleton, {}) : jsx(RelatedTimelineSkeleton, {}),
|
|
@@ -6596,11 +6853,15 @@ function ResourceListSurface({ resource, variant, state, fixedFilters, pageSize,
|
|
|
6596
6853
|
item: route ? planned : item,
|
|
6597
6854
|
columns: route ? columns.visible : columnNames,
|
|
6598
6855
|
rowQuick,
|
|
6599
|
-
rowHref
|
|
6856
|
+
rowHref,
|
|
6857
|
+
...empty === void 0 ? {} : { empty }
|
|
6600
6858
|
};
|
|
6601
6859
|
return jsx(Suspense, {
|
|
6602
6860
|
fallback: effectiveView === "cards" ? jsx(RelatedCardsSkeleton, {}) : jsx(RelatedTimelineSkeleton, {}),
|
|
6603
|
-
children: effectiveView === "cards" ? jsx(RelatedCards, { ...common }) : jsx(RelatedTimeline, {
|
|
6861
|
+
children: effectiveView === "cards" ? jsx(RelatedCards, { ...common }) : jsx(RelatedTimeline, {
|
|
6862
|
+
...common,
|
|
6863
|
+
axis: timelineAxis
|
|
6864
|
+
})
|
|
6604
6865
|
});
|
|
6605
6866
|
}
|
|
6606
6867
|
}), [
|
|
@@ -6613,7 +6874,9 @@ function ResourceListSurface({ resource, variant, state, fixedFilters, pageSize,
|
|
|
6613
6874
|
columns.visible,
|
|
6614
6875
|
columnNames,
|
|
6615
6876
|
rowQuick,
|
|
6616
|
-
rowHref
|
|
6877
|
+
rowHref,
|
|
6878
|
+
empty,
|
|
6879
|
+
timelineAxis
|
|
6617
6880
|
]);
|
|
6618
6881
|
const pageTitle = resolveResourceLabel(listCaps, resource, "plural", def);
|
|
6619
6882
|
const [saveOpen, setSaveOpen] = useState(false);
|
|
@@ -6695,6 +6958,10 @@ function ResourceListSurface({ resource, variant, state, fixedFilters, pageSize,
|
|
|
6695
6958
|
toast.error(t("toolbar.viewsForkFailed"));
|
|
6696
6959
|
}
|
|
6697
6960
|
};
|
|
6961
|
+
const columnNamesDisplay = (col, field) => {
|
|
6962
|
+
const resolved = list.columns.ui[col];
|
|
6963
|
+
return resolved === void 0 ? declaresDisplay(field) : resolved.display.use !== void 0;
|
|
6964
|
+
};
|
|
6698
6965
|
const renderCellLadder = (col, value, row) => {
|
|
6699
6966
|
if (def?.types && col === def.types.by) {
|
|
6700
6967
|
const raw = typeof value === "string" ? value : "";
|
|
@@ -6713,13 +6980,14 @@ function ResourceListSurface({ resource, variant, state, fixedFilters, pageSize,
|
|
|
6713
6980
|
children: label
|
|
6714
6981
|
});
|
|
6715
6982
|
}
|
|
6983
|
+
const custom = (renderCell ?? CellSlot)?.(col, value, row);
|
|
6984
|
+
if (custom !== void 0) return custom;
|
|
6716
6985
|
const fieldDef = def?.fields[col];
|
|
6717
|
-
const enumName = fieldDef?.opts?.enum ?? fieldDef?.ui?.enum;
|
|
6718
|
-
if (enumName && typeof value === "string") return jsx(EnumBadge, {
|
|
6986
|
+
const enumName = enumBindingKey(fieldDef?.opts?.enum ?? fieldDef?.ui?.enum);
|
|
6987
|
+
if (enumName && typeof value === "string" && !columnNamesDisplay(col, fieldDef)) return jsx(EnumBadge, {
|
|
6719
6988
|
enumName,
|
|
6720
6989
|
value
|
|
6721
6990
|
});
|
|
6722
|
-
return (renderCell ?? CellSlot)?.(col, value, row);
|
|
6723
6991
|
};
|
|
6724
6992
|
const surface = {
|
|
6725
6993
|
resource,
|
|
@@ -6747,10 +7015,16 @@ function ResourceListSurface({ resource, variant, state, fixedFilters, pageSize,
|
|
|
6747
7015
|
}
|
|
6748
7016
|
},
|
|
6749
7017
|
rowsViewFor,
|
|
6750
|
-
emptyState,
|
|
7018
|
+
emptyState: empty !== void 0 && !list.filters.isFiltered && list.pagination.page <= 1 && (list.cursor === null || list.cursor.next === null) ? jsx("p", {
|
|
7019
|
+
"data-buro-region": "empty",
|
|
7020
|
+
className: "text-sm text-muted-foreground",
|
|
7021
|
+
children: empty
|
|
7022
|
+
}) : emptyState,
|
|
6751
7023
|
renderCell: renderCellLadder,
|
|
6752
7024
|
readOnly: readOnly === true,
|
|
6753
|
-
exportable: exportable !== false
|
|
7025
|
+
exportable: exportable !== false,
|
|
7026
|
+
...layoutId === void 0 ? {} : { layoutId },
|
|
7027
|
+
...surfaceLinkEditing === void 0 ? {} : { linkEditing: surfaceLinkEditing }
|
|
6754
7028
|
};
|
|
6755
7029
|
return jsx(ResourceListProvider, {
|
|
6756
7030
|
value: list,
|
|
@@ -6763,22 +7037,44 @@ function ResourceListSurface({ resource, variant, state, fixedFilters, pageSize,
|
|
|
6763
7037
|
resource,
|
|
6764
7038
|
mode: "list",
|
|
6765
7039
|
title: pageTitle
|
|
6766
|
-
}) : title === void 0 && onCreate === void 0 ? null : jsxs("div", {
|
|
7040
|
+
}) : title === void 0 && onCreate === void 0 && onAttach === void 0 ? null : jsxs("div", {
|
|
6767
7041
|
className: "flex items-center justify-between gap-2",
|
|
6768
|
-
children: [
|
|
6769
|
-
|
|
6770
|
-
|
|
6771
|
-
|
|
6772
|
-
|
|
6773
|
-
|
|
6774
|
-
|
|
6775
|
-
|
|
6776
|
-
|
|
6777
|
-
|
|
6778
|
-
|
|
6779
|
-
|
|
6780
|
-
|
|
6781
|
-
|
|
7042
|
+
children: [
|
|
7043
|
+
title === void 0 ? jsx("span", {}) : jsx("h3", {
|
|
7044
|
+
className: "text-sm font-semibold",
|
|
7045
|
+
children: title
|
|
7046
|
+
}),
|
|
7047
|
+
onAttach === void 0 ? null : jsxs(Button, {
|
|
7048
|
+
type: "button",
|
|
7049
|
+
size: "sm",
|
|
7050
|
+
variant: "outline",
|
|
7051
|
+
"data-testid": `list-link-attach:${resource}`,
|
|
7052
|
+
onClick: onAttach,
|
|
7053
|
+
disabled: attachBusy,
|
|
7054
|
+
"aria-busy": attachBusy,
|
|
7055
|
+
children: [attachBusy ? jsx(IconLoader2, {
|
|
7056
|
+
className: "mr-1 h-4 w-4 animate-spin",
|
|
7057
|
+
"aria-hidden": "true"
|
|
7058
|
+
}) : jsx(IconLink, {
|
|
7059
|
+
className: "mr-1 h-4 w-4",
|
|
7060
|
+
"aria-hidden": "true"
|
|
7061
|
+
}), optionalT("field.link.attach", { defaultValue: "Attach" })]
|
|
7062
|
+
}),
|
|
7063
|
+
onCreate === void 0 ? null : jsxs(Button, {
|
|
7064
|
+
type: "button",
|
|
7065
|
+
size: "sm",
|
|
7066
|
+
variant: "outline",
|
|
7067
|
+
"data-testid": `list-create:${resource}`,
|
|
7068
|
+
onClick: onCreate,
|
|
7069
|
+
children: [jsx(IconPlus, {
|
|
7070
|
+
className: "mr-1 h-4 w-4",
|
|
7071
|
+
"aria-hidden": "true"
|
|
7072
|
+
}), optionalT("actions.create", {
|
|
7073
|
+
resource,
|
|
7074
|
+
defaultValue: "Create"
|
|
7075
|
+
})]
|
|
7076
|
+
})
|
|
7077
|
+
]
|
|
6782
7078
|
}),
|
|
6783
7079
|
hasDataRuntime && jsx(ExportRunWatcherMount, {}),
|
|
6784
7080
|
droppedPredicates > 0 && jsx("p", {
|
|
@@ -6827,13 +7123,13 @@ function ResourceListSurface({ resource, variant, state, fixedFilters, pageSize,
|
|
|
6827
7123
|
variant: "outline",
|
|
6828
7124
|
onClick: () => setSaveOpen(false),
|
|
6829
7125
|
"data-testid": `list-saved-view-save-cancel:${resource}`,
|
|
6830
|
-
children:
|
|
7126
|
+
children: chromeT("actions.cancel")
|
|
6831
7127
|
}), jsx(Button, {
|
|
6832
7128
|
type: "button",
|
|
6833
7129
|
onClick: handleSaveConfirm,
|
|
6834
7130
|
disabled: saveLabel.trim().length === 0,
|
|
6835
7131
|
"data-testid": `list-saved-view-save-confirm:${resource}`,
|
|
6836
|
-
children:
|
|
7132
|
+
children: chromeT("actions.save")
|
|
6837
7133
|
})] })
|
|
6838
7134
|
]
|
|
6839
7135
|
})
|
|
@@ -6870,7 +7166,7 @@ function ResourceListSurface({ resource, variant, state, fixedFilters, pageSize,
|
|
|
6870
7166
|
variant: "outline",
|
|
6871
7167
|
onClick: () => handleDeleteUserView(entry.id),
|
|
6872
7168
|
"data-testid": `list-saved-view-manage-delete:${resource}:${entry.id}`,
|
|
6873
|
-
children:
|
|
7169
|
+
children: chromeT("actions.delete")
|
|
6874
7170
|
}) : jsx("span", {
|
|
6875
7171
|
className: "shrink-0 text-xs text-muted-foreground",
|
|
6876
7172
|
children: t("toolbar.viewsReadOnly")
|
|
@@ -6882,7 +7178,7 @@ function ResourceListSurface({ resource, variant, state, fixedFilters, pageSize,
|
|
|
6882
7178
|
variant: "outline",
|
|
6883
7179
|
onClick: () => setManageOpen(false),
|
|
6884
7180
|
"data-testid": `list-saved-view-manage-close:${resource}`,
|
|
6885
|
-
children:
|
|
7181
|
+
children: chromeT("actions.close")
|
|
6886
7182
|
}) })
|
|
6887
7183
|
]
|
|
6888
7184
|
})
|
|
@@ -6915,13 +7211,13 @@ function ResourceListSurface({ resource, variant, state, fixedFilters, pageSize,
|
|
|
6915
7211
|
setRenameTarget(null);
|
|
6916
7212
|
},
|
|
6917
7213
|
"data-testid": `list-saved-view-rename-cancel:${resource}`,
|
|
6918
|
-
children:
|
|
7214
|
+
children: chromeT("actions.cancel")
|
|
6919
7215
|
}), jsx(Button, {
|
|
6920
7216
|
type: "button",
|
|
6921
7217
|
onClick: handleRenameConfirm,
|
|
6922
7218
|
disabled: renameLabel.trim().length === 0,
|
|
6923
7219
|
"data-testid": `list-saved-view-rename-confirm:${resource}`,
|
|
6924
|
-
children:
|
|
7220
|
+
children: chromeT("actions.save")
|
|
6925
7221
|
})] })
|
|
6926
7222
|
]
|
|
6927
7223
|
})
|
|
@@ -6956,7 +7252,7 @@ function ResourceListSurface({ resource, variant, state, fixedFilters, pageSize,
|
|
|
6956
7252
|
setForkTarget(null);
|
|
6957
7253
|
},
|
|
6958
7254
|
"data-testid": `list-saved-view-fork-cancel:${resource}`,
|
|
6959
|
-
children:
|
|
7255
|
+
children: chromeT("actions.cancel")
|
|
6960
7256
|
}), jsx(Button, {
|
|
6961
7257
|
type: "button",
|
|
6962
7258
|
onClick: handleForkConfirm,
|
|
@@ -6979,6 +7275,582 @@ function ExportRunWatcherMount() {
|
|
|
6979
7275
|
return null;
|
|
6980
7276
|
}
|
|
6981
7277
|
//#endregion
|
|
7278
|
+
//#region src/fields/use-relation-picker.tsx
|
|
7279
|
+
const noop = () => void 0;
|
|
7280
|
+
function safeKey(input) {
|
|
7281
|
+
if (input === void 0 || input === null) return void 0;
|
|
7282
|
+
try {
|
|
7283
|
+
return resourceKey(input);
|
|
7284
|
+
} catch {
|
|
7285
|
+
return;
|
|
7286
|
+
}
|
|
7287
|
+
}
|
|
7288
|
+
function sessionIdentity(runtime) {
|
|
7289
|
+
const state = runtime.dataSession.getState();
|
|
7290
|
+
const snapshot = state.status === "ready" ? state.snapshot : state.target;
|
|
7291
|
+
const selection = snapshot.scope?.selection;
|
|
7292
|
+
const partition = selection === void 0 ? snapshot.scopeKey : [snapshot.scopeKey, selection];
|
|
7293
|
+
let scopeKey;
|
|
7294
|
+
try {
|
|
7295
|
+
scopeKey = JSON.stringify(partition) ?? "";
|
|
7296
|
+
} catch {
|
|
7297
|
+
scopeKey = String(snapshot.scopeKey);
|
|
7298
|
+
}
|
|
7299
|
+
return {
|
|
7300
|
+
sessionKey: snapshot.sessionKey,
|
|
7301
|
+
scopeKey
|
|
7302
|
+
};
|
|
7303
|
+
}
|
|
7304
|
+
function PickerHost(props) {
|
|
7305
|
+
const { register } = props;
|
|
7306
|
+
React.useEffect(() => register(), [register]);
|
|
7307
|
+
return jsx(Fragment$1, { children: props.children });
|
|
7308
|
+
}
|
|
7309
|
+
function useRelationPicker(options) {
|
|
7310
|
+
const app = useApp();
|
|
7311
|
+
const runtime = useDataRuntime();
|
|
7312
|
+
const frame = useOptionalRecordFrame();
|
|
7313
|
+
const frames = useRecordScopeFrames();
|
|
7314
|
+
const multiple = options.multiple === true;
|
|
7315
|
+
const fieldName = options.field;
|
|
7316
|
+
let to;
|
|
7317
|
+
let declaredBy;
|
|
7318
|
+
let declaredCandidates;
|
|
7319
|
+
if (fieldName !== void 0) {
|
|
7320
|
+
const meta = ((frame === null ? void 0 : app.registry.get(frame.resource)?.fields)?.[fieldName])?.meta;
|
|
7321
|
+
to = safeKey(meta?.to);
|
|
7322
|
+
declaredBy = typeof meta?.by === "string" ? meta.by : void 0;
|
|
7323
|
+
declaredCandidates = Array.isArray(meta?.candidates) ? meta.candidates : void 0;
|
|
7324
|
+
} else to = safeKey(options.to);
|
|
7325
|
+
const target = to ?? "";
|
|
7326
|
+
const by = options.valueField ?? declaredBy;
|
|
7327
|
+
const scope = options.scope ?? "context";
|
|
7328
|
+
const selection = useCandidateSelection({
|
|
7329
|
+
to,
|
|
7330
|
+
candidates: options.candidates ?? declaredCandidates,
|
|
7331
|
+
sources: frame?.controller.sources,
|
|
7332
|
+
resource: frame?.resource ?? "",
|
|
7333
|
+
fieldName: fieldName ?? target,
|
|
7334
|
+
fixedFilters: options.fixedFilters
|
|
7335
|
+
});
|
|
7336
|
+
const held = selection.gate !== void 0;
|
|
7337
|
+
const pendingRef = React.useRef(null);
|
|
7338
|
+
const [active, setActive] = React.useState(null);
|
|
7339
|
+
const activeRef = React.useRef(null);
|
|
7340
|
+
activeRef.current = active;
|
|
7341
|
+
const rowsRef = React.useRef(new Map());
|
|
7342
|
+
const createdRowRef = React.useRef(void 0);
|
|
7343
|
+
const alive = React.useRef(true);
|
|
7344
|
+
const mounts = React.useRef(0);
|
|
7345
|
+
const sequence = React.useRef(0);
|
|
7346
|
+
const latest = React.useRef({
|
|
7347
|
+
multiple,
|
|
7348
|
+
select: options.select,
|
|
7349
|
+
idField: "id",
|
|
7350
|
+
valueField: "id",
|
|
7351
|
+
available: false,
|
|
7352
|
+
frames
|
|
7353
|
+
});
|
|
7354
|
+
const project = React.useCallback((row) => {
|
|
7355
|
+
const select = latest.current.select;
|
|
7356
|
+
if (select === void 0) return { ...row };
|
|
7357
|
+
const out = {};
|
|
7358
|
+
const key = row[latest.current.idField] !== void 0 ? row[latest.current.idField] : row.id;
|
|
7359
|
+
if (key !== void 0) out.id = key;
|
|
7360
|
+
const { valueField } = latest.current;
|
|
7361
|
+
if (Object.hasOwn(row, valueField)) out[valueField] = row[valueField];
|
|
7362
|
+
for (const name of select) if (Object.hasOwn(row, name)) out[name] = row[name];
|
|
7363
|
+
return out;
|
|
7364
|
+
}, []);
|
|
7365
|
+
const settle = React.useCallback((result) => {
|
|
7366
|
+
const pending = pendingRef.current;
|
|
7367
|
+
if (pending === null) return;
|
|
7368
|
+
pendingRef.current = null;
|
|
7369
|
+
pending.detach();
|
|
7370
|
+
if (alive.current) setActive(null);
|
|
7371
|
+
pending.resolve(result);
|
|
7372
|
+
}, []);
|
|
7373
|
+
const toggle = React.useCallback((key, row) => {
|
|
7374
|
+
setActive((current) => {
|
|
7375
|
+
if (current === null) return current;
|
|
7376
|
+
const has = current.selected.some((k) => String(k) === String(key));
|
|
7377
|
+
if (has) rowsRef.current.delete(String(key));
|
|
7378
|
+
else if (row !== void 0) rowsRef.current.set(String(key), row);
|
|
7379
|
+
const selected = has ? current.selected.filter((k) => String(k) !== String(key)) : [...current.selected, key];
|
|
7380
|
+
const next = {
|
|
7381
|
+
...current,
|
|
7382
|
+
selected
|
|
7383
|
+
};
|
|
7384
|
+
activeRef.current = next;
|
|
7385
|
+
return next;
|
|
7386
|
+
});
|
|
7387
|
+
}, []);
|
|
7388
|
+
const onValue = React.useCallback((value) => {
|
|
7389
|
+
const row = createdRowRef.current;
|
|
7390
|
+
createdRowRef.current = void 0;
|
|
7391
|
+
if (latest.current.multiple) {
|
|
7392
|
+
const current = activeRef.current;
|
|
7393
|
+
if (current !== null && !current.selected.some((k) => String(k) === String(value))) toggle(value, row);
|
|
7394
|
+
return;
|
|
7395
|
+
}
|
|
7396
|
+
settle({
|
|
7397
|
+
kind: "selected",
|
|
7398
|
+
keys: [value],
|
|
7399
|
+
rows: row === void 0 ? [] : [project(row)]
|
|
7400
|
+
});
|
|
7401
|
+
}, [
|
|
7402
|
+
project,
|
|
7403
|
+
settle,
|
|
7404
|
+
toggle
|
|
7405
|
+
]);
|
|
7406
|
+
const wiring = useRelationSelect({
|
|
7407
|
+
to: target,
|
|
7408
|
+
by,
|
|
7409
|
+
fixedFilters: selection.fixedFilters,
|
|
7410
|
+
scope,
|
|
7411
|
+
...fieldName === void 0 ? {} : { scopeField: fieldName },
|
|
7412
|
+
held,
|
|
7413
|
+
onValue
|
|
7414
|
+
});
|
|
7415
|
+
latest.current = {
|
|
7416
|
+
multiple,
|
|
7417
|
+
select: options.select,
|
|
7418
|
+
idField: wiring.idField,
|
|
7419
|
+
valueField: wiring.valueField,
|
|
7420
|
+
available: to !== void 0 && wiring.targetDef !== void 0 && !held,
|
|
7421
|
+
frames
|
|
7422
|
+
};
|
|
7423
|
+
const { create: shared } = wiring;
|
|
7424
|
+
const create = React.useMemo(() => ({
|
|
7425
|
+
...shared,
|
|
7426
|
+
start: (openCreate) => shared.start(() => {
|
|
7427
|
+
const opened = openCreate();
|
|
7428
|
+
opened.then((row) => {
|
|
7429
|
+
createdRowRef.current = row === null ? void 0 : { ...row };
|
|
7430
|
+
}, noop);
|
|
7431
|
+
return opened;
|
|
7432
|
+
})
|
|
7433
|
+
}), [shared]);
|
|
7434
|
+
React.useEffect(() => runtime.dataSession.subscribe(() => {
|
|
7435
|
+
const pending = pendingRef.current;
|
|
7436
|
+
if (pending === null) return;
|
|
7437
|
+
const now = sessionIdentity(runtime);
|
|
7438
|
+
if (now.sessionKey !== pending.sessionKey || now.scopeKey !== pending.scopeKey) settle({
|
|
7439
|
+
kind: "cancelled",
|
|
7440
|
+
reason: "session-changed"
|
|
7441
|
+
});
|
|
7442
|
+
}), [runtime, settle]);
|
|
7443
|
+
React.useEffect(() => {
|
|
7444
|
+
alive.current = true;
|
|
7445
|
+
return () => {
|
|
7446
|
+
alive.current = false;
|
|
7447
|
+
settle({
|
|
7448
|
+
kind: "cancelled",
|
|
7449
|
+
reason: "unmounted"
|
|
7450
|
+
});
|
|
7451
|
+
};
|
|
7452
|
+
}, [settle]);
|
|
7453
|
+
React.useEffect(() => {
|
|
7454
|
+
if (held) settle({
|
|
7455
|
+
kind: "cancelled",
|
|
7456
|
+
reason: "unavailable"
|
|
7457
|
+
});
|
|
7458
|
+
}, [held, settle]);
|
|
7459
|
+
const register = React.useCallback(() => {
|
|
7460
|
+
mounts.current += 1;
|
|
7461
|
+
return () => {
|
|
7462
|
+
mounts.current -= 1;
|
|
7463
|
+
if (mounts.current === 0) settle({
|
|
7464
|
+
kind: "cancelled",
|
|
7465
|
+
reason: "unmounted"
|
|
7466
|
+
});
|
|
7467
|
+
};
|
|
7468
|
+
}, [settle]);
|
|
7469
|
+
const open = React.useCallback((opts = {}) => new Promise((resolve) => {
|
|
7470
|
+
settle({
|
|
7471
|
+
kind: "cancelled",
|
|
7472
|
+
reason: "superseded"
|
|
7473
|
+
});
|
|
7474
|
+
if (mounts.current === 0) {
|
|
7475
|
+
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.");
|
|
7476
|
+
resolve({
|
|
7477
|
+
kind: "cancelled",
|
|
7478
|
+
reason: "not-mounted"
|
|
7479
|
+
});
|
|
7480
|
+
return;
|
|
7481
|
+
}
|
|
7482
|
+
const { signal } = opts;
|
|
7483
|
+
if (signal?.aborted === true) {
|
|
7484
|
+
resolve({
|
|
7485
|
+
kind: "cancelled",
|
|
7486
|
+
reason: "aborted"
|
|
7487
|
+
});
|
|
7488
|
+
return;
|
|
7489
|
+
}
|
|
7490
|
+
if (!latest.current.available) {
|
|
7491
|
+
resolve({
|
|
7492
|
+
kind: "cancelled",
|
|
7493
|
+
reason: "unavailable"
|
|
7494
|
+
});
|
|
7495
|
+
return;
|
|
7496
|
+
}
|
|
7497
|
+
sequence.current += 1;
|
|
7498
|
+
const id = sequence.current;
|
|
7499
|
+
const onAbort = () => {
|
|
7500
|
+
if (pendingRef.current?.id === id) settle({
|
|
7501
|
+
kind: "cancelled",
|
|
7502
|
+
reason: "aborted"
|
|
7503
|
+
});
|
|
7504
|
+
};
|
|
7505
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
7506
|
+
pendingRef.current = {
|
|
7507
|
+
id,
|
|
7508
|
+
...sessionIdentity(runtime),
|
|
7509
|
+
resolve,
|
|
7510
|
+
detach: () => signal?.removeEventListener("abort", onAbort)
|
|
7511
|
+
};
|
|
7512
|
+
rowsRef.current = new Map();
|
|
7513
|
+
createdRowRef.current = void 0;
|
|
7514
|
+
const next = {
|
|
7515
|
+
id,
|
|
7516
|
+
frames: latest.current.frames,
|
|
7517
|
+
selected: opts.selected ?? []
|
|
7518
|
+
};
|
|
7519
|
+
activeRef.current = next;
|
|
7520
|
+
setActive(next);
|
|
7521
|
+
}), [runtime, settle]);
|
|
7522
|
+
const onSelectRow = React.useCallback((row) => {
|
|
7523
|
+
const field = wiring.valueField;
|
|
7524
|
+
const value = field === "id" ? row.id : row[field];
|
|
7525
|
+
if (typeof value !== "string" && typeof value !== "number") return;
|
|
7526
|
+
settle({
|
|
7527
|
+
kind: "selected",
|
|
7528
|
+
keys: [value],
|
|
7529
|
+
rows: [project(row)]
|
|
7530
|
+
});
|
|
7531
|
+
}, [
|
|
7532
|
+
wiring.valueField,
|
|
7533
|
+
settle,
|
|
7534
|
+
project
|
|
7535
|
+
]);
|
|
7536
|
+
const onDone = React.useCallback(() => {
|
|
7537
|
+
const keys = activeRef.current?.selected ?? [];
|
|
7538
|
+
const rows = keys.flatMap((key) => {
|
|
7539
|
+
const row = rowsRef.current.get(String(key));
|
|
7540
|
+
return row === void 0 ? [] : [project(row)];
|
|
7541
|
+
});
|
|
7542
|
+
settle({
|
|
7543
|
+
kind: "selected",
|
|
7544
|
+
keys,
|
|
7545
|
+
rows
|
|
7546
|
+
});
|
|
7547
|
+
}, [settle, project]);
|
|
7548
|
+
const onOpenChange = React.useCallback((next) => {
|
|
7549
|
+
if (!next) settle({
|
|
7550
|
+
kind: "cancelled",
|
|
7551
|
+
reason: "dismissed"
|
|
7552
|
+
});
|
|
7553
|
+
}, [settle]);
|
|
7554
|
+
return {
|
|
7555
|
+
open,
|
|
7556
|
+
element: jsx(PickerHost, {
|
|
7557
|
+
register,
|
|
7558
|
+
children: active === null || held ? null : jsxs(RecordScopeChain, {
|
|
7559
|
+
frames: active.frames,
|
|
7560
|
+
children: [jsx(LazyRelationPickerDialog, {
|
|
7561
|
+
to: target,
|
|
7562
|
+
open: true,
|
|
7563
|
+
onOpenChange,
|
|
7564
|
+
onSelect: noop,
|
|
7565
|
+
onSelectRow,
|
|
7566
|
+
valueField: wiring.valueField,
|
|
7567
|
+
...selection.fixedFilters === void 0 ? {} : { fixedFilters: selection.fixedFilters },
|
|
7568
|
+
scope,
|
|
7569
|
+
...fieldName === void 0 ? {} : { scopeField: fieldName },
|
|
7570
|
+
...options.create === void 0 ? {} : {
|
|
7571
|
+
create,
|
|
7572
|
+
...options.create.seeds === void 0 ? {} : { createSeeds: options.create.seeds }
|
|
7573
|
+
},
|
|
7574
|
+
fieldName: fieldName ?? target,
|
|
7575
|
+
...options.disabledKeys === void 0 ? {} : { disabledKeys: options.disabledKeys },
|
|
7576
|
+
...options.disabledLabel === void 0 ? {} : { disabledLabel: options.disabledLabel },
|
|
7577
|
+
...multiple ? {
|
|
7578
|
+
multiple: true,
|
|
7579
|
+
selected: active.selected,
|
|
7580
|
+
onToggle: (value, row) => toggle(value, row),
|
|
7581
|
+
onDone
|
|
7582
|
+
} : {}
|
|
7583
|
+
}), wiring.reader]
|
|
7584
|
+
}, active.id)
|
|
7585
|
+
})
|
|
7586
|
+
};
|
|
7587
|
+
}
|
|
7588
|
+
//#endregion
|
|
7589
|
+
//#region src/blocks/link-write-summary.tsx
|
|
7590
|
+
function LinkWriteSummary(props) {
|
|
7591
|
+
const { report, titles, onClose } = props;
|
|
7592
|
+
const testid = props.testid ?? "link-write-summary";
|
|
7593
|
+
const t = useOptionalT();
|
|
7594
|
+
const caps = useI18nCaps();
|
|
7595
|
+
const chromeT = useChromeT();
|
|
7596
|
+
const heading = t("field.link.partial", { defaultValue: "Some links were not changed" });
|
|
7597
|
+
return jsx(Dialog, {
|
|
7598
|
+
open: true,
|
|
7599
|
+
onOpenChange: (open) => {
|
|
7600
|
+
if (!open) onClose();
|
|
7601
|
+
},
|
|
7602
|
+
children: jsxs(DialogContent, {
|
|
7603
|
+
"data-testid": testid,
|
|
7604
|
+
className: "flex max-h-[85vh] flex-col overflow-hidden",
|
|
7605
|
+
children: [
|
|
7606
|
+
jsxs(DialogHeader, { children: [jsx(DialogTitle, { children: heading }), jsx(DialogDescription, {
|
|
7607
|
+
className: "sr-only",
|
|
7608
|
+
children: heading
|
|
7609
|
+
})] }),
|
|
7610
|
+
jsx("ul", {
|
|
7611
|
+
className: "flex min-h-0 flex-1 flex-col gap-2 overflow-y-auto overscroll-contain",
|
|
7612
|
+
children: report.failed.map(({ id, error }) => jsxs("li", {
|
|
7613
|
+
"data-testid": `${testid}:item:${id}`,
|
|
7614
|
+
className: "text-sm",
|
|
7615
|
+
children: [jsx("span", {
|
|
7616
|
+
className: "font-medium",
|
|
7617
|
+
children: titles[id] ?? id
|
|
7618
|
+
}), jsx("span", {
|
|
7619
|
+
className: "text-destructive",
|
|
7620
|
+
children: `: ${resolveErrorText(error, caps)}`
|
|
7621
|
+
})]
|
|
7622
|
+
}, id))
|
|
7623
|
+
}),
|
|
7624
|
+
jsx(DialogFooter, { children: jsx(Button, {
|
|
7625
|
+
type: "button",
|
|
7626
|
+
onClick: onClose,
|
|
7627
|
+
"data-testid": `${testid}:close`,
|
|
7628
|
+
children: chromeT("actions.close", { defaultValue: "Close" })
|
|
7629
|
+
}) })
|
|
7630
|
+
]
|
|
7631
|
+
})
|
|
7632
|
+
});
|
|
7633
|
+
}
|
|
7634
|
+
//#endregion
|
|
7635
|
+
//#region src/blocks/link-attach.tsx
|
|
7636
|
+
const NO_ISSUES = Object.freeze([]);
|
|
7637
|
+
function useLinkPanelWrites(options) {
|
|
7638
|
+
const { owner, junction, target, link, targetKey, valueField, junctionIdField, displayField, scope, record, ownerKey, placement } = options;
|
|
7639
|
+
const app = useApp();
|
|
7640
|
+
const runtime = useDataRuntime();
|
|
7641
|
+
const t = useOptionalT();
|
|
7642
|
+
const rights = useLinkRights(owner, link, {
|
|
7643
|
+
surface: "panel",
|
|
7644
|
+
record,
|
|
7645
|
+
...placement === void 0 ? {} : { placement }
|
|
7646
|
+
});
|
|
7647
|
+
const writer = useLinkWriter(owner, link);
|
|
7648
|
+
const linkedKeys = useLinkedKeys(owner, link);
|
|
7649
|
+
const reverse = useMemo(() => reverseLinkOf((name) => app.registry.get(name)?.fields, owner, link), [
|
|
7650
|
+
app.registry,
|
|
7651
|
+
owner,
|
|
7652
|
+
link
|
|
7653
|
+
]);
|
|
7654
|
+
let liveHides = false;
|
|
7655
|
+
if (reverse !== void 0) try {
|
|
7656
|
+
const { providerId } = runtime.app.resolveDataBinding(target);
|
|
7657
|
+
liveHides = runtime.providers.resolve(providerId).provider.readRelations?.links === true && linkCapabilitiesOf(runtime, providerId)?.links?.filter.none !== false;
|
|
7658
|
+
} catch {
|
|
7659
|
+
liveHides = false;
|
|
7660
|
+
}
|
|
7661
|
+
const liveHidesRef = useRef(liveHides);
|
|
7662
|
+
liveHidesRef.current = liveHides;
|
|
7663
|
+
const [frozenHides, setFrozenHides] = useState(null);
|
|
7664
|
+
const hides = frozenHides ?? liveHides;
|
|
7665
|
+
const ownerKeyId = JSON.stringify(ownerKey ?? null);
|
|
7666
|
+
const hidden = useMemo(() => hides && reverse !== void 0 ? [{
|
|
7667
|
+
field: reverse,
|
|
7668
|
+
op: "hasNone",
|
|
7669
|
+
value: [ownerKey]
|
|
7670
|
+
}] : void 0, [
|
|
7671
|
+
hides,
|
|
7672
|
+
reverse,
|
|
7673
|
+
ownerKeyId
|
|
7674
|
+
]);
|
|
7675
|
+
const disabledKeys = useCallback((keys, signal) => linkedKeys(record, keys, signal), [linkedKeys, record]);
|
|
7676
|
+
const disabledLabel = useCallback((count) => t("field.link.alreadyLinked", {
|
|
7677
|
+
count,
|
|
7678
|
+
defaultValue: `${count} already linked`
|
|
7679
|
+
}), [t]);
|
|
7680
|
+
const picker = useRelationPicker({
|
|
7681
|
+
to: target,
|
|
7682
|
+
multiple: true,
|
|
7683
|
+
valueField,
|
|
7684
|
+
scope,
|
|
7685
|
+
...hidden === void 0 ? {
|
|
7686
|
+
disabledKeys,
|
|
7687
|
+
disabledLabel
|
|
7688
|
+
} : { fixedFilters: hidden }
|
|
7689
|
+
});
|
|
7690
|
+
const openPicker = picker.open;
|
|
7691
|
+
const [summary, setSummary] = useState(null);
|
|
7692
|
+
const finish = useCallback((report, titles, done) => {
|
|
7693
|
+
if (report.outcome !== "success") {
|
|
7694
|
+
setSummary({
|
|
7695
|
+
report,
|
|
7696
|
+
titles
|
|
7697
|
+
});
|
|
7698
|
+
return;
|
|
7699
|
+
}
|
|
7700
|
+
const count = report.succeeded.length;
|
|
7701
|
+
if (count === 0) return;
|
|
7702
|
+
toast.success(t(`field.link.${done}`, {
|
|
7703
|
+
count,
|
|
7704
|
+
defaultValue: `${count} ${count === 1 ? "link" : "links"} ${done}`
|
|
7705
|
+
}));
|
|
7706
|
+
}, [t]);
|
|
7707
|
+
const attaching = useRef(false);
|
|
7708
|
+
const [attachWriting, setAttachWriting] = useState(false);
|
|
7709
|
+
const onAttach = useCallback(() => {
|
|
7710
|
+
if (attaching.current) return;
|
|
7711
|
+
attaching.current = true;
|
|
7712
|
+
(async () => {
|
|
7713
|
+
try {
|
|
7714
|
+
setFrozenHides(liveHidesRef.current);
|
|
7715
|
+
let result;
|
|
7716
|
+
try {
|
|
7717
|
+
result = await openPicker();
|
|
7718
|
+
} finally {
|
|
7719
|
+
setFrozenHides(null);
|
|
7720
|
+
}
|
|
7721
|
+
if (result.kind !== "selected" || result.keys.length === 0) return;
|
|
7722
|
+
const titles = {};
|
|
7723
|
+
for (const row of result.rows) {
|
|
7724
|
+
const key = valueField === "id" ? row.id : row[valueField];
|
|
7725
|
+
const title = row[displayField];
|
|
7726
|
+
if (key !== void 0 && key !== null && title !== void 0 && title !== null) titles[String(key)] = String(title);
|
|
7727
|
+
}
|
|
7728
|
+
setAttachWriting(true);
|
|
7729
|
+
try {
|
|
7730
|
+
finish(await writer.attach(result.keys, record), titles, "attached");
|
|
7731
|
+
} finally {
|
|
7732
|
+
setAttachWriting(false);
|
|
7733
|
+
}
|
|
7734
|
+
} finally {
|
|
7735
|
+
attaching.current = false;
|
|
7736
|
+
}
|
|
7737
|
+
})();
|
|
7738
|
+
}, [
|
|
7739
|
+
displayField,
|
|
7740
|
+
finish,
|
|
7741
|
+
openPicker,
|
|
7742
|
+
record,
|
|
7743
|
+
valueField,
|
|
7744
|
+
writer
|
|
7745
|
+
]);
|
|
7746
|
+
const pending = useRef(null);
|
|
7747
|
+
const [confirming, setConfirming] = useState(null);
|
|
7748
|
+
const detaching = useRef(false);
|
|
7749
|
+
const [busy, setBusy] = useState(false);
|
|
7750
|
+
const onDetach = useCallback((rows) => new Promise((resolve) => {
|
|
7751
|
+
pending.current?.resolve([]);
|
|
7752
|
+
pending.current = {
|
|
7753
|
+
rows,
|
|
7754
|
+
resolve
|
|
7755
|
+
};
|
|
7756
|
+
setConfirming(rows.length);
|
|
7757
|
+
}), []);
|
|
7758
|
+
useEffect(() => () => {
|
|
7759
|
+
pending.current?.resolve([]);
|
|
7760
|
+
pending.current = null;
|
|
7761
|
+
}, []);
|
|
7762
|
+
const cancel = useCallback(() => {
|
|
7763
|
+
if (detaching.current) return;
|
|
7764
|
+
pending.current?.resolve([]);
|
|
7765
|
+
pending.current = null;
|
|
7766
|
+
setConfirming(null);
|
|
7767
|
+
}, []);
|
|
7768
|
+
const proceed = useCallback(() => {
|
|
7769
|
+
const current = pending.current;
|
|
7770
|
+
if (current === null || detaching.current) return;
|
|
7771
|
+
detaching.current = true;
|
|
7772
|
+
setBusy(true);
|
|
7773
|
+
(async () => {
|
|
7774
|
+
try {
|
|
7775
|
+
const titles = {};
|
|
7776
|
+
for (const row of current.rows) {
|
|
7777
|
+
const id = row[junctionIdField];
|
|
7778
|
+
if (id === void 0 || id === null) continue;
|
|
7779
|
+
const title = row[`${targetKey}.${displayField}`] ?? row[targetKey];
|
|
7780
|
+
if (title !== void 0 && title !== null) titles[String(id)] = String(title);
|
|
7781
|
+
}
|
|
7782
|
+
const report = await writer.detach(current.rows, record);
|
|
7783
|
+
pending.current = null;
|
|
7784
|
+
setConfirming(null);
|
|
7785
|
+
finish(report, titles, "detached");
|
|
7786
|
+
const left = new Set(report.succeeded);
|
|
7787
|
+
current.resolve(current.rows.filter((row) => {
|
|
7788
|
+
const id = row[junctionIdField];
|
|
7789
|
+
return id !== void 0 && id !== null && left.has(String(id));
|
|
7790
|
+
}).map((row) => String(row.id)));
|
|
7791
|
+
} finally {
|
|
7792
|
+
detaching.current = false;
|
|
7793
|
+
setBusy(false);
|
|
7794
|
+
}
|
|
7795
|
+
})();
|
|
7796
|
+
}, [
|
|
7797
|
+
displayField,
|
|
7798
|
+
finish,
|
|
7799
|
+
junctionIdField,
|
|
7800
|
+
record,
|
|
7801
|
+
targetKey,
|
|
7802
|
+
writer
|
|
7803
|
+
]);
|
|
7804
|
+
const detachLabel = t("field.link.detach", { defaultValue: "Detach" });
|
|
7805
|
+
const count = confirming ?? 0;
|
|
7806
|
+
const gate = {
|
|
7807
|
+
resource: junction,
|
|
7808
|
+
open: confirming !== null,
|
|
7809
|
+
busy,
|
|
7810
|
+
header: t("field.link.confirmDetach", {
|
|
7811
|
+
count,
|
|
7812
|
+
defaultValue: `Detach ${count} ${count === 1 ? "link" : "links"}? The linked records themselves are not deleted.`
|
|
7813
|
+
}),
|
|
7814
|
+
blockers: NO_ISSUES,
|
|
7815
|
+
confirmable: NO_ISSUES,
|
|
7816
|
+
proceed,
|
|
7817
|
+
cancel
|
|
7818
|
+
};
|
|
7819
|
+
const attachAllowed = rights.attach.allowed;
|
|
7820
|
+
const detachAllowed = rights.detach.allowed;
|
|
7821
|
+
return {
|
|
7822
|
+
linkEditing: useMemo(() => !attachAllowed && !detachAllowed ? void 0 : {
|
|
7823
|
+
...attachAllowed ? { attach: {
|
|
7824
|
+
onAttach,
|
|
7825
|
+
busy: attachWriting
|
|
7826
|
+
} } : {},
|
|
7827
|
+
...detachAllowed ? { detach: { onDetach } } : {}
|
|
7828
|
+
}, [
|
|
7829
|
+
attachAllowed,
|
|
7830
|
+
attachWriting,
|
|
7831
|
+
detachAllowed,
|
|
7832
|
+
onAttach,
|
|
7833
|
+
onDetach
|
|
7834
|
+
]),
|
|
7835
|
+
element: jsxs(Fragment$1, { children: [
|
|
7836
|
+
picker.element,
|
|
7837
|
+
jsx(CheckConfirmDialog, {
|
|
7838
|
+
state: gate,
|
|
7839
|
+
testid: `link-detach-confirm:${junction}`,
|
|
7840
|
+
title: detachLabel,
|
|
7841
|
+
proceedLabel: detachLabel,
|
|
7842
|
+
tone: "destructive"
|
|
7843
|
+
}),
|
|
7844
|
+
summary === null ? null : jsx(LinkWriteSummary, {
|
|
7845
|
+
report: summary.report,
|
|
7846
|
+
titles: summary.titles,
|
|
7847
|
+
onClose: () => setSummary(null),
|
|
7848
|
+
testid: `link-write-summary:${junction}`
|
|
7849
|
+
})
|
|
7850
|
+
] })
|
|
7851
|
+
};
|
|
7852
|
+
}
|
|
7853
|
+
//#endregion
|
|
6982
7854
|
//#region src/quick/peek-filter.ts
|
|
6983
7855
|
function peekFieldDefinition(def, name) {
|
|
6984
7856
|
return declaredField(def, name);
|
|
@@ -7002,32 +7874,12 @@ function peekRecord(def, row, idField, capabilityFor, fieldVisible) {
|
|
|
7002
7874
|
return peekRow(def, row, idField, capabilityFor, fieldVisible);
|
|
7003
7875
|
}
|
|
7004
7876
|
//#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
7877
|
//#region src/blocks/link-panel.tsx
|
|
7025
7878
|
const isRecord = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
|
|
7026
7879
|
const isResourceHandle = (value) => isRecord(value) && value.kind === "resource";
|
|
7027
|
-
function qualify(ref
|
|
7880
|
+
function qualify(ref) {
|
|
7028
7881
|
try {
|
|
7029
|
-
|
|
7030
|
-
return resourceKey(normalizeEntityRef(isResourceHandle(ref) ? ref.ref : ref, { defaultNamespace: namespace }));
|
|
7882
|
+
return resourceKey(normalizeEntityRef(isResourceHandle(ref) ? ref.ref : ref));
|
|
7031
7883
|
} catch {
|
|
7032
7884
|
return;
|
|
7033
7885
|
}
|
|
@@ -7046,7 +7898,7 @@ function linkField(registry, owner, link) {
|
|
|
7046
7898
|
function linkTarget(registry, owner, link) {
|
|
7047
7899
|
const found = linkField(registry, owner, link);
|
|
7048
7900
|
if (found === void 0 || owner === void 0) return void 0;
|
|
7049
|
-
return qualify(found.field.meta?.to
|
|
7901
|
+
return qualify(found.field.meta?.to);
|
|
7050
7902
|
}
|
|
7051
7903
|
function idFieldOf(def) {
|
|
7052
7904
|
const declared = isRecord(def) && isRecord(def.data) ? def.data.idField : void 0;
|
|
@@ -7062,37 +7914,47 @@ function targetColumns(registry, target, def) {
|
|
|
7062
7914
|
const source = def;
|
|
7063
7915
|
const views = isRecord(def.views) ? def.views : void 0;
|
|
7064
7916
|
const list = views !== void 0 && isRecord(views.list) ? views.list : void 0;
|
|
7065
|
-
return (list !== void 0 && Array.isArray(list.columns) ? list.columns.
|
|
7917
|
+
return (list !== void 0 && Array.isArray(list.columns) ? list.columns.flatMap((entry) => {
|
|
7918
|
+
const name = isRecord(entry) ? entry.field : entry;
|
|
7919
|
+
return typeof name === "string" ? [name] : [];
|
|
7920
|
+
}) : dtoFields(source, "list")).filter((name) => {
|
|
7066
7921
|
if (name.includes(".")) return name.split(".").length - 1 <= MAX_TARGET_HOPS && resolveColumnField(registry, target, name) !== void 0;
|
|
7067
7922
|
const field = peekFieldDefinition(source, name);
|
|
7068
7923
|
return isRecord(field) && !isRelationField(field) && fieldSensitivity(field) === void 0;
|
|
7069
7924
|
});
|
|
7070
7925
|
}
|
|
7071
7926
|
function LinkPanel(props) {
|
|
7072
|
-
const { link, owner, own } = props;
|
|
7927
|
+
const { link, owner, own, empty } = props;
|
|
7073
7928
|
const app = useApp();
|
|
7074
7929
|
const frame = usePlacementFrame();
|
|
7075
7930
|
const routeScreen = useIsRouteScreen();
|
|
7076
7931
|
const record = useOptionalRecordFrame()?.record;
|
|
7932
|
+
const runtime = useOptionalDataRuntime();
|
|
7077
7933
|
const storage = useMemo(() => {
|
|
7078
7934
|
const found = linkField(app.registry, owner, link);
|
|
7079
7935
|
if (found === void 0) return void 0;
|
|
7080
7936
|
const { declaration } = found;
|
|
7081
|
-
const junction = qualify(declaration.junction
|
|
7082
|
-
const target = qualify(found.field.meta?.to
|
|
7937
|
+
const junction = qualify(declaration.junction);
|
|
7938
|
+
const target = qualify(found.field.meta?.to);
|
|
7083
7939
|
if (junction === void 0 || target === void 0) return void 0;
|
|
7084
7940
|
const junctionDef = app.registry.get(junction);
|
|
7085
7941
|
if (junctionDef === void 0) return void 0;
|
|
7086
7942
|
const junctionFields = junctionDef.fields;
|
|
7943
|
+
const targetDef = app.registry.get(target);
|
|
7944
|
+
const targetBy = byOf(junctionFields[declaration.targetKey]);
|
|
7945
|
+
const declaredScope = found.field.meta?.scope;
|
|
7087
7946
|
return {
|
|
7088
7947
|
junction,
|
|
7089
7948
|
target,
|
|
7090
7949
|
sourceKey: declaration.sourceKey,
|
|
7091
7950
|
targetKey: declaration.targetKey,
|
|
7092
7951
|
ownerField: byOf(junctionFields[declaration.sourceKey]) ?? idFieldOf(app.registry.get(owner)),
|
|
7093
|
-
targetKeyedBy:
|
|
7952
|
+
targetKeyedBy: targetBy !== void 0,
|
|
7953
|
+
targetValueField: targetBy ?? idFieldOf(targetDef),
|
|
7954
|
+
junctionIdField: idFieldOf(junctionDef),
|
|
7094
7955
|
where: declaration.where,
|
|
7095
|
-
targetDef
|
|
7956
|
+
targetDef,
|
|
7957
|
+
pickerScope: declaredScope === "context" || declaredScope === "session" || declaredScope === "none" ? declaredScope : void 0
|
|
7096
7958
|
};
|
|
7097
7959
|
}, [
|
|
7098
7960
|
app.registry,
|
|
@@ -7104,10 +7966,11 @@ function LinkPanel(props) {
|
|
|
7104
7966
|
const fixedFilters = useMemo(() => {
|
|
7105
7967
|
if (storage === void 0 || ownerKey === null || ownerKey === void 0) return void 0;
|
|
7106
7968
|
const { sourceKey, targetKey } = storage;
|
|
7969
|
+
const placed = whereToFilter(own.where ?? [], (field) => `${targetKey}.${field}`);
|
|
7107
7970
|
return [
|
|
7108
7971
|
whereEntryToFilter(sourceKey, "eq", ownerKey),
|
|
7109
7972
|
...storage.where.map((entry) => whereEntryToFilter(entry.field, entry.op, entry.value)),
|
|
7110
|
-
...
|
|
7973
|
+
...placed === void 0 ? [] : [placed]
|
|
7111
7974
|
];
|
|
7112
7975
|
}, [
|
|
7113
7976
|
storage,
|
|
@@ -7117,7 +7980,12 @@ function LinkPanel(props) {
|
|
|
7117
7980
|
const columnsKey = JSON.stringify([own.columns ?? null, own.linkColumns ?? null]);
|
|
7118
7981
|
const columns = useMemo(() => {
|
|
7119
7982
|
if (storage === void 0) return void 0;
|
|
7120
|
-
|
|
7983
|
+
const shown = own.columns ?? targetColumns(app.registry, storage.target, storage.targetDef);
|
|
7984
|
+
const across = (entry) => typeof entry === "string" ? `${storage.targetKey}.${entry}` : {
|
|
7985
|
+
...entry,
|
|
7986
|
+
field: `${storage.targetKey}.${entry.field}`
|
|
7987
|
+
};
|
|
7988
|
+
return [...shown.map(across), ...own.linkColumns ?? []];
|
|
7121
7989
|
}, [
|
|
7122
7990
|
storage,
|
|
7123
7991
|
columnsKey,
|
|
@@ -7127,31 +7995,76 @@ function LinkPanel(props) {
|
|
|
7127
7995
|
resource: storage.target,
|
|
7128
7996
|
field: storage.targetKey
|
|
7129
7997
|
}, [storage]);
|
|
7130
|
-
const unbound = fixedFilters === void 0 || own.where
|
|
7998
|
+
const unbound = fixedFilters === void 0 || own.where !== void 0 && hasNullRef(own.where);
|
|
7131
7999
|
if (storage === void 0 || unbound) return jsx(UnboundRelatedList, {
|
|
7132
8000
|
resource: storage?.target ?? "",
|
|
7133
|
-
title: own.title
|
|
8001
|
+
title: own.title,
|
|
8002
|
+
...empty === void 0 ? {} : { empty }
|
|
7134
8003
|
});
|
|
7135
8004
|
const state = frame?.state === "url" && routeScreen && !frame.nested ? { url: frame.key } : "memory";
|
|
7136
|
-
|
|
7137
|
-
|
|
8005
|
+
let providerCounts;
|
|
8006
|
+
if (runtime !== null) try {
|
|
8007
|
+
providerCounts = linkCapabilitiesOf(runtime, runtime.app.resolveDataBinding(storage.junction).providerId)?.links?.count;
|
|
8008
|
+
} catch {
|
|
8009
|
+
providerCounts = void 0;
|
|
8010
|
+
}
|
|
8011
|
+
const count = own.count === false ? "off" : own.count ?? (providerCounts === false ? "off" : void 0);
|
|
8012
|
+
const layoutId = frame === null ? void 0 : placementLayoutId({
|
|
8013
|
+
owner,
|
|
8014
|
+
key: frame.key
|
|
8015
|
+
});
|
|
8016
|
+
const surface = {
|
|
7138
8017
|
resource: storage.junction,
|
|
7139
8018
|
variant: "placement",
|
|
7140
8019
|
state,
|
|
7141
8020
|
fixedFilters,
|
|
7142
|
-
...own.pageSize === void 0 ? {} : { pageSize: own.pageSize },
|
|
8021
|
+
...own.initial?.pageSize === void 0 ? {} : { initial: { pageSize: own.initial.pageSize } },
|
|
7143
8022
|
...own.search === void 0 ? {} : { search: `${storage.targetKey}.${own.search}` },
|
|
7144
8023
|
...columns === void 0 ? {} : { columns },
|
|
7145
8024
|
open: rowTarget === void 0 || own.open === "none" ? "none" : "page",
|
|
7146
8025
|
...rowTarget === void 0 ? {} : { rowTarget },
|
|
7147
8026
|
...frame === null ? {} : { placementKey: frame.key },
|
|
8027
|
+
...layoutId === void 0 ? {} : { layoutId },
|
|
7148
8028
|
...own.title === void 0 ? {} : { title: own.title },
|
|
8029
|
+
...empty === void 0 ? {} : { empty },
|
|
7149
8030
|
scope: placementScope(own.scope, owner),
|
|
7150
8031
|
...count === void 0 ? {} : { count },
|
|
7151
8032
|
readOnly: true,
|
|
7152
8033
|
exportable: false
|
|
8034
|
+
};
|
|
8035
|
+
if (runtime === null || record === void 0) return jsx(ResourceListSurface, { ...surface });
|
|
8036
|
+
return jsx(WritableLinkPanel, {
|
|
8037
|
+
surface,
|
|
8038
|
+
owner,
|
|
8039
|
+
link,
|
|
8040
|
+
storage,
|
|
8041
|
+
record,
|
|
8042
|
+
ownerKey,
|
|
8043
|
+
placement: own.linkActions
|
|
7153
8044
|
});
|
|
7154
8045
|
}
|
|
8046
|
+
function WritableLinkPanel(props) {
|
|
8047
|
+
const { surface, owner, link, storage, record, ownerKey, placement } = props;
|
|
8048
|
+
const displayField = isRecord(storage.targetDef) && typeof storage.targetDef.displayField === "string" ? storage.targetDef.displayField : storage.targetValueField;
|
|
8049
|
+
const { linkEditing, element } = useLinkPanelWrites({
|
|
8050
|
+
owner,
|
|
8051
|
+
junction: storage.junction,
|
|
8052
|
+
target: storage.target,
|
|
8053
|
+
link,
|
|
8054
|
+
targetKey: storage.targetKey,
|
|
8055
|
+
valueField: storage.targetValueField,
|
|
8056
|
+
junctionIdField: storage.junctionIdField,
|
|
8057
|
+
displayField,
|
|
8058
|
+
scope: storage.pickerScope ?? "context",
|
|
8059
|
+
record,
|
|
8060
|
+
ownerKey,
|
|
8061
|
+
placement
|
|
8062
|
+
});
|
|
8063
|
+
return jsxs(Fragment$1, { children: [jsx(ResourceListSurface, {
|
|
8064
|
+
...surface,
|
|
8065
|
+
...linkEditing === void 0 ? {} : { linkEditing }
|
|
8066
|
+
}), element] });
|
|
8067
|
+
}
|
|
7155
8068
|
//#endregion
|
|
7156
8069
|
//#region src/blocks/route-list-context.ts
|
|
7157
8070
|
const RouteListContext = createContext(null);
|
|
@@ -7160,7 +8073,7 @@ function useRouteList() {
|
|
|
7160
8073
|
}
|
|
7161
8074
|
//#endregion
|
|
7162
8075
|
//#region src/blocks/related-list-view.tsx
|
|
7163
|
-
function RelatedListView({ props }) {
|
|
8076
|
+
function RelatedListView({ props, empty }) {
|
|
7164
8077
|
const own = props;
|
|
7165
8078
|
const route = useRouteList();
|
|
7166
8079
|
const frame = usePlacementFrame();
|
|
@@ -7168,7 +8081,7 @@ function RelatedListView({ props }) {
|
|
|
7168
8081
|
const linked = own.link !== void 0;
|
|
7169
8082
|
const resource = linked ? linkTarget(app.registry, frame?.owner, own.link) ?? "" : resourceKey(own.resource);
|
|
7170
8083
|
const whereKey = JSON.stringify(own.where ?? []);
|
|
7171
|
-
const fixedFilters = useMemo(() => own.where === void 0
|
|
8084
|
+
const fixedFilters = useMemo(() => own.where === void 0 || own.where.length === 0 ? void 0 : [whereToFilter(own.where)], [whereKey]);
|
|
7172
8085
|
const columnsKey = JSON.stringify(own.columns ?? null);
|
|
7173
8086
|
const columns = useMemo(() => own.columns, [columnsKey]);
|
|
7174
8087
|
const routeScreen = useIsRouteScreen();
|
|
@@ -7180,20 +8093,23 @@ function RelatedListView({ props }) {
|
|
|
7180
8093
|
const ownerDef = frame?.owner === void 0 ? void 0 : app.registry.get(frame.owner);
|
|
7181
8094
|
const granted = ownerDef === void 0 ? "unknown" : grantedPlacementKeys(ownerDef);
|
|
7182
8095
|
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;
|
|
8096
|
+
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
8097
|
useDeepLinkOpen(frame?.key ?? null, resource, route === null && !linked && frame !== null && !frame.nested && routeScreen && (own.open ?? "quick") === "quick");
|
|
7185
|
-
const unbound = own.where
|
|
8098
|
+
const unbound = own.where !== void 0 && hasNullRef(own.where);
|
|
7186
8099
|
if (linked) return frame?.owner === void 0 ? jsx(UnboundRelatedList, {
|
|
7187
8100
|
resource,
|
|
7188
|
-
title: own.title
|
|
8101
|
+
title: own.title,
|
|
8102
|
+
...empty === void 0 ? {} : { empty }
|
|
7189
8103
|
}) : jsx(LinkPanel, {
|
|
7190
8104
|
link: own.link,
|
|
7191
8105
|
owner: frame.owner,
|
|
7192
|
-
own
|
|
8106
|
+
own,
|
|
8107
|
+
...empty === void 0 ? {} : { empty }
|
|
7193
8108
|
});
|
|
7194
8109
|
if (route !== null) return jsx(ResourceListSurface, {
|
|
7195
8110
|
resource,
|
|
7196
8111
|
variant: "route",
|
|
8112
|
+
layoutId: "route",
|
|
7197
8113
|
...route.meta === void 0 ? {} : { meta: route.meta },
|
|
7198
8114
|
...route.renderCell === void 0 ? {} : { renderCell: route.renderCell },
|
|
7199
8115
|
...route.emptyState === void 0 ? {} : { emptyState: route.emptyState }
|
|
@@ -7206,24 +8122,28 @@ function RelatedListView({ props }) {
|
|
|
7206
8122
|
});
|
|
7207
8123
|
if (unbound) return jsx(UnboundRelatedList, {
|
|
7208
8124
|
resource,
|
|
7209
|
-
title: own.title
|
|
8125
|
+
title: own.title,
|
|
8126
|
+
...empty === void 0 ? {} : { empty }
|
|
7210
8127
|
});
|
|
7211
8128
|
const state = frame?.state === "url" && routeScreen && !frame.nested ? { url: frame.key } : "memory";
|
|
7212
8129
|
const scope = placementScope(own.scope, frame?.owner);
|
|
7213
8130
|
const count = own.count === false ? "off" : own.count;
|
|
8131
|
+
const layoutId = placementLayoutId(frame);
|
|
7214
8132
|
return jsx(ResourceListSurface, {
|
|
7215
8133
|
resource,
|
|
7216
8134
|
variant: "placement",
|
|
7217
8135
|
state,
|
|
7218
8136
|
...fixedFilters === void 0 ? {} : { fixedFilters },
|
|
7219
|
-
...own.
|
|
8137
|
+
...own.initial === void 0 ? {} : { initial: own.initial },
|
|
7220
8138
|
...own.search === void 0 ? {} : { search: own.search },
|
|
7221
8139
|
...columns === void 0 ? {} : { columns },
|
|
7222
8140
|
open: own.open ?? "quick",
|
|
7223
8141
|
...own.view === void 0 ? {} : { view: own.view },
|
|
7224
8142
|
...isItemHandle(own.item) ? { item: own.item } : {},
|
|
7225
8143
|
...frame === null ? {} : { placementKey: frame.key },
|
|
8144
|
+
...layoutId === void 0 ? {} : { layoutId },
|
|
7226
8145
|
...own.title === void 0 ? {} : { title: own.title },
|
|
8146
|
+
...empty === void 0 ? {} : { empty },
|
|
7227
8147
|
scope,
|
|
7228
8148
|
...count === void 0 ? {} : { count },
|
|
7229
8149
|
...onCreate === void 0 ? {} : { onCreate }
|
|
@@ -7258,7 +8178,7 @@ function Missing({ text }) {
|
|
|
7258
8178
|
children: text ?? t("table.empty", { defaultValue: "No records" })
|
|
7259
8179
|
});
|
|
7260
8180
|
}
|
|
7261
|
-
function RelatedRecordView({ props }) {
|
|
8181
|
+
function RelatedRecordView({ props, empty }) {
|
|
7262
8182
|
const own = props;
|
|
7263
8183
|
const app = useApp();
|
|
7264
8184
|
const resource = resourceKey(own.resource);
|
|
@@ -7274,7 +8194,7 @@ function RelatedRecordView({ props }) {
|
|
|
7274
8194
|
});
|
|
7275
8195
|
const result = query.data;
|
|
7276
8196
|
const raw = result?.data;
|
|
7277
|
-
const dtoRow = useMemo(() => raw !== null && typeof raw === "object" && projection !== void 0 ?
|
|
8197
|
+
const dtoRow = useMemo(() => raw !== null && typeof raw === "object" && projection !== void 0 ? deriveRecord(raw, (record) => projectDto(record, projection)) : void 0, [raw, projection]);
|
|
7278
8198
|
const access = useFieldAccess(resource, "record", dtoRow);
|
|
7279
8199
|
const projected = useProjectedRecord(resource, dtoRow, { placeholder: query.isPlaceholderData === true });
|
|
7280
8200
|
const fieldsKey = JSON.stringify(own.fields ?? null);
|
|
@@ -7292,15 +8212,15 @@ function RelatedRecordView({ props }) {
|
|
|
7292
8212
|
capabilityFor,
|
|
7293
8213
|
access
|
|
7294
8214
|
]);
|
|
7295
|
-
if (id === null) return jsx(Missing, { text: own.missing });
|
|
8215
|
+
if (id === null) return jsx(Missing, { text: own.missing ?? empty });
|
|
7296
8216
|
if (result === void 0) {
|
|
7297
|
-
if (query.error != null) return jsx(Missing, { text: own.missing });
|
|
8217
|
+
if (query.error != null) return jsx(Missing, { text: own.missing ?? empty });
|
|
7298
8218
|
return jsx(Skeleton, {
|
|
7299
8219
|
"data-buro-skeleton": "",
|
|
7300
8220
|
className: "h-9 w-full"
|
|
7301
8221
|
});
|
|
7302
8222
|
}
|
|
7303
|
-
if (dtoRow === void 0) return jsx(Missing, { text: own.missing });
|
|
8223
|
+
if (dtoRow === void 0) return jsx(Missing, { text: own.missing ?? empty });
|
|
7304
8224
|
if (projected.status === "pending" || projected.value === null) return jsx(Skeleton, {
|
|
7305
8225
|
"data-buro-skeleton": "",
|
|
7306
8226
|
className: "h-9 w-full"
|
|
@@ -7315,10 +8235,7 @@ function RelatedRecordView({ props }) {
|
|
|
7315
8235
|
children: own.title
|
|
7316
8236
|
}), own.toolbar === "item" ? jsx(ActionOverflow, {
|
|
7317
8237
|
resource,
|
|
7318
|
-
record: {
|
|
7319
|
-
...dtoRow,
|
|
7320
|
-
id
|
|
7321
|
-
},
|
|
8238
|
+
record: withRecordKeys(dtoRow, { id }),
|
|
7322
8239
|
surface: "show"
|
|
7323
8240
|
}) : null]
|
|
7324
8241
|
}), jsx("dl", {
|
|
@@ -7333,13 +8250,21 @@ function RelatedRecordView({ props }) {
|
|
|
7333
8250
|
children: resolveFieldLabel(caps, name, declaredText("field", field, "label"))
|
|
7334
8251
|
}), jsx("dd", {
|
|
7335
8252
|
className: "min-w-0 break-words text-sm",
|
|
7336
|
-
children: jsx(
|
|
7337
|
-
|
|
7338
|
-
|
|
7339
|
-
|
|
8253
|
+
children: jsx(FieldValueFrame, {
|
|
8254
|
+
field: {
|
|
8255
|
+
...field ?? {},
|
|
8256
|
+
name
|
|
8257
|
+
},
|
|
7340
8258
|
value,
|
|
7341
|
-
|
|
7342
|
-
|
|
8259
|
+
surface: "display",
|
|
8260
|
+
children: jsx(ResolvedFieldDisplay, {
|
|
8261
|
+
resource: own.resource,
|
|
8262
|
+
name,
|
|
8263
|
+
field,
|
|
8264
|
+
value,
|
|
8265
|
+
record: row,
|
|
8266
|
+
fallback: toDisplayText(value).text
|
|
8267
|
+
})
|
|
7343
8268
|
})
|
|
7344
8269
|
})]
|
|
7345
8270
|
}, name);
|
|
@@ -7410,28 +8335,39 @@ function Chrome({ node, size, state, children }) {
|
|
|
7410
8335
|
});
|
|
7411
8336
|
}
|
|
7412
8337
|
function isAuthError(error) {
|
|
7413
|
-
return
|
|
8338
|
+
return normalizeDataError(error).kind === "auth";
|
|
7414
8339
|
}
|
|
7415
|
-
|
|
8340
|
+
const PLACE_NODE_MESSAGES = {
|
|
8341
|
+
failed: "This section could not be loaded.",
|
|
8342
|
+
sessionExpired: "Your session has expired. Sign in again to continue.",
|
|
8343
|
+
notImplemented: "This section is not available."
|
|
8344
|
+
};
|
|
8345
|
+
function FailedPlacement({ node, size, message, retry, error }) {
|
|
7416
8346
|
const t = useOptionalT();
|
|
8347
|
+
const chromeT = useChromeT();
|
|
8348
|
+
const fallback = {
|
|
8349
|
+
key: `block.${message}`,
|
|
8350
|
+
default: PLACE_NODE_MESSAGES[message]
|
|
8351
|
+
};
|
|
7417
8352
|
return jsx(Chrome, {
|
|
7418
8353
|
node,
|
|
7419
8354
|
size,
|
|
7420
8355
|
state: "failed",
|
|
7421
|
-
children: jsxs("div", {
|
|
8356
|
+
children: message === "notImplemented" ? jsxs("div", {
|
|
7422
8357
|
role: "alert",
|
|
7423
8358
|
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, {
|
|
8359
|
+
children: [jsx("span", { children: t(fallback.key, { defaultValue: fallback.default }) }), retry === void 0 ? null : jsx(Button, {
|
|
7429
8360
|
type: "button",
|
|
7430
8361
|
variant: "outline",
|
|
7431
8362
|
size: "sm",
|
|
7432
8363
|
onClick: retry,
|
|
7433
|
-
children:
|
|
8364
|
+
children: chromeT("actions.retry", { defaultValue: "Retry" })
|
|
7434
8365
|
})]
|
|
8366
|
+
}) : jsx(LoadError, {
|
|
8367
|
+
surface: "block",
|
|
8368
|
+
error,
|
|
8369
|
+
...retry === void 0 ? {} : { retry },
|
|
8370
|
+
fallback
|
|
7435
8371
|
})
|
|
7436
8372
|
});
|
|
7437
8373
|
}
|
|
@@ -7511,7 +8447,8 @@ function ReadyPlacement({ node, size, placement, implementation }) {
|
|
|
7511
8447
|
nested,
|
|
7512
8448
|
...Array.isArray(declaredWhere) ? { where: declaredWhere } : {},
|
|
7513
8449
|
closure: compiled.sourceClosure,
|
|
7514
|
-
...owner === void 0 ? {} : { owner }
|
|
8450
|
+
...owner === void 0 ? {} : { owner },
|
|
8451
|
+
sources: controller.sources
|
|
7515
8452
|
}), [
|
|
7516
8453
|
node.key,
|
|
7517
8454
|
node.id,
|
|
@@ -7519,7 +8456,8 @@ function ReadyPlacement({ node, size, placement, implementation }) {
|
|
|
7519
8456
|
nested,
|
|
7520
8457
|
declaredWhere,
|
|
7521
8458
|
compiled.sourceClosure,
|
|
7522
|
-
owner
|
|
8459
|
+
owner,
|
|
8460
|
+
controller.sources
|
|
7523
8461
|
]);
|
|
7524
8462
|
return jsx(PlacementBoundary, {
|
|
7525
8463
|
node,
|
|
@@ -7577,6 +8515,7 @@ function PlaceNode({ node }) {
|
|
|
7577
8515
|
node,
|
|
7578
8516
|
size,
|
|
7579
8517
|
message: isAuthError(placement.error) ? "sessionExpired" : "failed",
|
|
8518
|
+
error: placement.error,
|
|
7580
8519
|
...placement.retry === void 0 ? {} : { retry: placement.retry }
|
|
7581
8520
|
});
|
|
7582
8521
|
if (implementation === void 0) return jsx(FailedPlacement, {
|
|
@@ -7592,6 +8531,6 @@ function PlaceNode({ node }) {
|
|
|
7592
8531
|
});
|
|
7593
8532
|
}
|
|
7594
8533
|
//#endregion
|
|
7595
|
-
export {
|
|
8534
|
+
export { CursorLoadMore as $, ListSearchInput as A, AppFlowButton as At, ExportDialog as B, ListControls as C, useShownItemActions as Ct, ListFlows as D, EnumSettlingRow as Dt, ListLive as E, EnumBadge as Et, ListColumns as F, toDisplayText as Ft, RouteScreenClaimContext as G, ScreenTree as H, ColumnsToggle as I, ShellProvider as It, FormControlProvider as J, RouteScreenContext as K, DataTableViewOptions as L, defaultUserMapper as Lt, ExportButton as M, FieldDisplay as Mt, ListCreate as N, i18nPrefixToRef$1 as Nt, ListFilters as O, unknownEnumValueLabel as Ot, CreateButton as P, FieldValueFrame as Pt, ResourceList as Q, ListBulkBar as R, usePaletteState as Rt, useFrameChrome as S, ActionOverflow as St, SavedViewsDropdown as T, downloadArtifact as Tt, useTabsUrlState as U, DeclarativeForm as V, QuickScreenClaimContext as W, useFormControlOf as X, useFormControl as Y, ListBody as Z, CopyId as _, ShowPageHeader as _t, peekFieldDefinition as a, DataTable as at, RecordTitle as b, useDocumentTitle 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, PageHeader as gt, TypeChromeDrawnContext as h, EditPageHeader as ht, RouteListContext as i, useBuroDataTable as it, ListExport as j, Field as jt, FilterRegion as k, DeepLinkScope as kt, useRelationPicker as l, TabsList as lt, HeaderTypeNotice as m, RelatedTimelineSkeleton 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, resolveResourceTitle as vt, ListSavedViews as w, useExportRunWatcher as wt, FrameChromeContext as x, typography as xt, RecordActions as y, DocumentTitleProvider as yt, BulkActionToolbar as z, useShell as zt };
|
|
7596
8535
|
|
|
7597
|
-
//# sourceMappingURL=place-node-
|
|
8536
|
+
//# sourceMappingURL=place-node-BdxT-30h.js.map
|