@burojs/ui-shadcn 0.5.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -0
- package/dist/bulk-summary-BWSAS7k2.js +143 -0
- package/dist/bulk-summary-BWSAS7k2.js.map +1 -0
- package/dist/bulk-summary-CrEij5vq.cjs +143 -0
- package/dist/bulk-summary-CrEij5vq.cjs.map +1 -0
- package/dist/candidates-relation-picker-CS_JZ4Fd.js +31 -0
- package/dist/candidates-relation-picker-CS_JZ4Fd.js.map +1 -0
- package/dist/candidates-relation-picker-y5M6_oe4.cjs +31 -0
- package/dist/candidates-relation-picker-y5M6_oe4.cjs.map +1 -0
- package/dist/{check-confirm-dialog-CkaVTPT9.js → check-confirm-dialog-2j0gOd7x.js} +392 -103
- package/dist/check-confirm-dialog-2j0gOd7x.js.map +1 -0
- package/dist/{check-confirm-dialog-BRByTFpg.cjs → check-confirm-dialog-C-NHgMST.cjs} +476 -102
- package/dist/check-confirm-dialog-C-NHgMST.cjs.map +1 -0
- package/dist/{dialog-B_rt9S1c.js → dialog-C123sVQ8.js} +4 -4
- package/dist/dialog-C123sVQ8.js.map +1 -0
- package/dist/{dialog-DRNUq9EA.cjs → dialog-vmMdfUWs.cjs} +3 -3
- package/dist/dialog-vmMdfUWs.cjs.map +1 -0
- package/dist/{filter-builder-B3mZFL-K.cjs → filter-builder-DFu0JP-1.cjs} +959 -128
- package/dist/filter-builder-DFu0JP-1.cjs.map +1 -0
- package/dist/{filter-builder-DOnaXNfP.js → filter-builder-DaWHUZuV.js} +863 -116
- package/dist/filter-builder-DaWHUZuV.js.map +1 -0
- package/dist/{flow-runtime-B6zOHYqI.cjs → flow-runtime-BpmrIUb6.cjs} +61 -29
- package/dist/flow-runtime-BpmrIUb6.cjs.map +1 -0
- package/dist/{flow-runtime-D8h99FSg.js → flow-runtime-DyqTmK3H.js} +62 -30
- package/dist/flow-runtime-DyqTmK3H.js.map +1 -0
- package/dist/{flow-screen-CX05b3G9.cjs → flow-screen-Bcz9NF2V.cjs} +132 -88
- package/dist/flow-screen-Bcz9NF2V.cjs.map +1 -0
- package/dist/{flow-screen-CmbARSqg.js → flow-screen-DCdli6Ha.js} +132 -88
- package/dist/flow-screen-DCdli6Ha.js.map +1 -0
- package/dist/index.cjs +951 -605
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +618 -332
- package/dist/index.d.ts +618 -332
- package/dist/index.js +863 -522
- package/dist/index.js.map +1 -1
- package/dist/{inline-flow-host-B7U1OJUe.cjs → inline-flow-host-3vw7LzzD.cjs} +5 -5
- package/dist/inline-flow-host-3vw7LzzD.cjs.map +1 -0
- package/dist/{inline-flow-host-C3H2UpSA.js → inline-flow-host-C0rI9B7f.js} +5 -5
- package/dist/inline-flow-host-C0rI9B7f.js.map +1 -0
- package/dist/label-66W1qbke.js.map +1 -1
- package/dist/label-w6JYf2cF.cjs.map +1 -1
- package/dist/lazy-relation-picker-dialog-Bt8lrEIu.cjs +153 -0
- package/dist/lazy-relation-picker-dialog-Bt8lrEIu.cjs.map +1 -0
- package/dist/lazy-relation-picker-dialog-CAScLPb2.js +135 -0
- package/dist/lazy-relation-picker-dialog-CAScLPb2.js.map +1 -0
- package/dist/markdown/index.d.cts +2 -3
- package/dist/markdown/index.d.ts +2 -3
- package/dist/{page-flow-route-CyXSvXSE.js → page-flow-route-BHUEpn0I.js} +36 -23
- package/dist/page-flow-route-BHUEpn0I.js.map +1 -0
- package/dist/{page-flow-route-CQ7HsbBm.cjs → page-flow-route-DS8IVTVH.cjs} +41 -28
- package/dist/page-flow-route-DS8IVTVH.cjs.map +1 -0
- package/dist/{place-node-SIp4XYQH.cjs → place-node-CNb3w-c7.cjs} +2227 -999
- package/dist/place-node-CNb3w-c7.cjs.map +1 -0
- package/dist/{place-node--GdsP688.js → place-node-CfzQvh4Y.js} +2164 -984
- package/dist/place-node-CfzQvh4Y.js.map +1 -0
- package/dist/{related-list-cards-B6x9HQQp.js → related-list-cards-BIqqY69Y.js} +9 -4
- package/dist/{related-list-cards-B6x9HQQp.js.map → related-list-cards-BIqqY69Y.js.map} +1 -1
- package/dist/{related-list-cards-nxuODlIA.cjs → related-list-cards-CelMxl4q.cjs} +9 -4
- package/dist/{related-list-cards-nxuODlIA.cjs.map → related-list-cards-CelMxl4q.cjs.map} +1 -1
- package/dist/{related-list-timeline-DEdfd-rr.js → related-list-timeline-CRh7xwLD.js} +6 -7
- package/dist/related-list-timeline-CRh7xwLD.js.map +1 -0
- package/dist/{related-list-timeline-BYJKEbmA.cjs → related-list-timeline-DsLdi8sv.cjs} +5 -6
- package/dist/related-list-timeline-DsLdi8sv.cjs.map +1 -0
- package/dist/{related-rows-B4levHcR.js → related-rows--uP2iKQC.js} +27 -17
- package/dist/related-rows--uP2iKQC.js.map +1 -0
- package/dist/{related-rows-zh5f_4QR.cjs → related-rows-MPnLCmPF.cjs} +32 -22
- package/dist/related-rows-MPnLCmPF.cjs.map +1 -0
- package/dist/{search-access-CURsWn-U.js → relation-create-C43cdPQt.js} +596 -29
- package/dist/relation-create-C43cdPQt.js.map +1 -0
- package/dist/{search-access-B7G5oR8n.cjs → relation-create-DDEYcKfw.cjs} +630 -32
- package/dist/relation-create-DDEYcKfw.cjs.map +1 -0
- package/dist/{relation-many-picker-gdAOD-qK.js → relation-many-picker-BJhaEqxz.js} +210 -107
- package/dist/relation-many-picker-BJhaEqxz.js.map +1 -0
- package/dist/{relation-many-picker-B-NCEUgX.cjs → relation-many-picker-DzuGJpEl.cjs} +218 -115
- package/dist/relation-many-picker-DzuGJpEl.cjs.map +1 -0
- package/dist/{relation-picker-BAY77ivO.js → relation-picker-D-tBBZW_.js} +40 -45
- package/dist/relation-picker-D-tBBZW_.js.map +1 -0
- package/dist/{relation-picker-Da_uhFvA.cjs → relation-picker-DaIMzf2H.cjs} +45 -50
- package/dist/relation-picker-DaIMzf2H.cjs.map +1 -0
- package/dist/{relation-picker-dialog-M0pQaWlp.cjs → relation-picker-dialog-9Vg5vvR1.cjs} +142 -40
- package/dist/relation-picker-dialog-9Vg5vvR1.cjs.map +1 -0
- package/dist/{relation-picker-dialog-hA9rYrZB.js → relation-picker-dialog-DoEY8EkH.js} +143 -41
- package/dist/relation-picker-dialog-DoEY8EkH.js.map +1 -0
- package/dist/relation-select-B8Fq4o-t.js +48 -0
- package/dist/relation-select-B8Fq4o-t.js.map +1 -0
- package/dist/relation-select-Cr3CfNLy.cjs +55 -0
- package/dist/relation-select-Cr3CfNLy.cjs.map +1 -0
- package/dist/{resource-page-router-OSQyRA5b.cjs → resource-page-router-C_kGW2o_.cjs} +552 -303
- package/dist/resource-page-router-C_kGW2o_.cjs.map +1 -0
- package/dist/{resource-page-router-T4vS5gbJ.js → resource-page-router-Diw6S6TK.js} +507 -258
- package/dist/resource-page-router-Diw6S6TK.js.map +1 -0
- package/dist/{rich-text-vTyVjDoa.cjs → rich-text-C_7JCvRo.cjs} +2 -2
- package/dist/{rich-text-vTyVjDoa.cjs.map → rich-text-C_7JCvRo.cjs.map} +1 -1
- package/dist/{rich-text-DFerywgz.js → rich-text-RcPBf6hr.js} +2 -2
- package/dist/{rich-text-DFerywgz.js.map → rich-text-RcPBf6hr.js.map} +1 -1
- package/dist/success-notices-Dl3M1fZP.cjs +46 -0
- package/dist/success-notices-Dl3M1fZP.cjs.map +1 -0
- package/dist/success-notices-LDEgEkc4.js +41 -0
- package/dist/success-notices-LDEgEkc4.js.map +1 -0
- package/dist/{toaster-C7CiYA4Z.cjs → toaster-BbByOytP.cjs} +72 -80
- package/dist/toaster-BbByOytP.cjs.map +1 -0
- package/dist/{toaster-CfCRLgR-.js → toaster-Ct_YHGs6.js} +66 -68
- package/dist/toaster-Ct_YHGs6.js.map +1 -0
- package/dist/{use-dialog-filter-state-BDcgS5Oz.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-BBCC0OEK.cjs → use-dialog-filter-state-tNvqAAjh.cjs} +6 -5
- package/dist/use-dialog-filter-state-tNvqAAjh.cjs.map +1 -0
- package/dist/use-theme-DFlpXils.cjs.map +1 -1
- package/dist/use-theme-dnE3Od3I.js.map +1 -1
- 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-CX2PBu6l.js +0 -63
- package/dist/candidates-gate-CX2PBu6l.js.map +0 -1
- package/dist/candidates-gate-DiFI52r7.cjs +0 -70
- package/dist/candidates-gate-DiFI52r7.cjs.map +0 -1
- package/dist/candidates-relation-picker-CBsVcnPR.js +0 -82
- package/dist/candidates-relation-picker-CBsVcnPR.js.map +0 -1
- package/dist/candidates-relation-picker-Dd_GVLlu.cjs +0 -84
- package/dist/candidates-relation-picker-Dd_GVLlu.cjs.map +0 -1
- package/dist/check-confirm-dialog-BRByTFpg.cjs.map +0 -1
- package/dist/check-confirm-dialog-CkaVTPT9.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-B3mZFL-K.cjs.map +0 -1
- package/dist/filter-builder-DOnaXNfP.js.map +0 -1
- package/dist/flow-runtime-B6zOHYqI.cjs.map +0 -1
- package/dist/flow-runtime-D8h99FSg.js.map +0 -1
- package/dist/flow-screen-CX05b3G9.cjs.map +0 -1
- package/dist/flow-screen-CmbARSqg.js.map +0 -1
- package/dist/inline-flow-host-B7U1OJUe.cjs.map +0 -1
- package/dist/inline-flow-host-C3H2UpSA.js.map +0 -1
- package/dist/lazy-relation-picker-dialog-2oxnA6IR.js +0 -14
- package/dist/lazy-relation-picker-dialog-2oxnA6IR.js.map +0 -1
- package/dist/lazy-relation-picker-dialog-BfAGKzUG.cjs +0 -19
- package/dist/lazy-relation-picker-dialog-BfAGKzUG.cjs.map +0 -1
- package/dist/page-flow-route-CQ7HsbBm.cjs.map +0 -1
- package/dist/page-flow-route-CyXSvXSE.js.map +0 -1
- package/dist/peek-filter-CnqHwTMU.cjs +0 -51
- package/dist/peek-filter-CnqHwTMU.cjs.map +0 -1
- package/dist/peek-filter-ErLfO153.js +0 -28
- package/dist/peek-filter-ErLfO153.js.map +0 -1
- package/dist/place-node--GdsP688.js.map +0 -1
- package/dist/place-node-SIp4XYQH.cjs.map +0 -1
- package/dist/related-list-timeline-BYJKEbmA.cjs.map +0 -1
- package/dist/related-list-timeline-DEdfd-rr.js.map +0 -1
- package/dist/related-rows-B4levHcR.js.map +0 -1
- package/dist/related-rows-zh5f_4QR.cjs.map +0 -1
- package/dist/relation-create-BOaUe12G.cjs +0 -362
- package/dist/relation-create-BOaUe12G.cjs.map +0 -1
- package/dist/relation-create-Bg-eEJLs.js +0 -337
- package/dist/relation-create-Bg-eEJLs.js.map +0 -1
- package/dist/relation-many-picker-B-NCEUgX.cjs.map +0 -1
- package/dist/relation-many-picker-gdAOD-qK.js.map +0 -1
- package/dist/relation-picker-BAY77ivO.js.map +0 -1
- package/dist/relation-picker-Da_uhFvA.cjs.map +0 -1
- package/dist/relation-picker-dialog-M0pQaWlp.cjs.map +0 -1
- package/dist/relation-picker-dialog-hA9rYrZB.js.map +0 -1
- package/dist/resource-page-router-OSQyRA5b.cjs.map +0 -1
- package/dist/resource-page-router-T4vS5gbJ.js.map +0 -1
- package/dist/search-access-B7G5oR8n.cjs.map +0 -1
- package/dist/search-access-CURsWn-U.js.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-C7CiYA4Z.cjs.map +0 -1
- package/dist/toaster-CfCRLgR-.js.map +0 -1
- package/dist/use-dialog-filter-state-BBCC0OEK.cjs.map +0 -1
- package/dist/use-dialog-filter-state-BDcgS5Oz.js.map +0 -1
|
@@ -1,20 +1,22 @@
|
|
|
1
1
|
import { l as cn } from "./use-theme-dnE3Od3I.js";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { i as isNetworkFailure, l as useLinkComponent, m as PopoverTrigger, n as toast, o as technicalTexts, p as PopoverContent, r as friendlyStringsFrom, s as toFriendlyError, u as Popover } from "./toaster-Ct_YHGs6.js";
|
|
3
|
+
import { B as DropdownMenuContent, C as loadBulkSummary, E as loadInlineFlowHost, F as Icon, H as DropdownMenuItem, O as preloadFlowRuntime, Q as DropdownMenuTrigger, R as DropdownMenu, S as RuntimeFlowRunner, U as DropdownMenuLabel, V as DropdownMenuGroup, _ as reasonText, b as RuntimeAppFlowRunner, c as useListSlot, d as useToBrowserHref, dt as DocInfo, f as FlowActionButton, g as ActionInner, k as useLoaded, m as useFlowChrome, n as ActionButton, o as canonicalResourceName, p as FlowMenuItem, q as DropdownMenuSeparator, r as DisabledAction, t as CheckConfirmDialog, v as FlowBoundary, w as loadFailedText, x as RuntimeBulkFlowRunner } from "./check-confirm-dialog-2j0gOd7x.js";
|
|
4
4
|
import { t as Button } from "./button-MWVeZaqj.js";
|
|
5
5
|
import { t as Input } from "./input-CUPStzZO.js";
|
|
6
|
-
import { a as DialogFooter, i as DialogDescription, o as DialogHeader, r as DialogContent, s as DialogTitle, t as Dialog } from "./dialog-
|
|
7
|
-
import {
|
|
6
|
+
import { a as DialogFooter, i as DialogDescription, o as DialogHeader, r as DialogContent, s as DialogTitle, t as Dialog } from "./dialog-C123sVQ8.js";
|
|
7
|
+
import { g as ClaimedQuickLink, l as relationLinkClassName, y as LoadError } from "./relation-create-C43cdPQt.js";
|
|
8
8
|
import { t as Skeleton } from "./skeleton-U2noefj5.js";
|
|
9
|
+
import { A as TableRow, C as SelectValue, D as TableCell, F as ResolvedFieldInput, I as useFieldMetaDiagnostic, L as fieldDescription, N as MetaPlaceholder, O as TableHead, P as ResolvedFieldDisplay, R as lowerLegacyField, S as SelectTrigger, T as TableBody, _ as SelectItem, h as SelectContent, j as Badge, k as TableHeader, m as Select, t as FilterBuilder, w as Table$1 } from "./filter-builder-DaWHUZuV.js";
|
|
9
10
|
import { t as Label } from "./label-66W1qbke.js";
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
11
|
+
import { n as useCandidateSelection, t as LazyRelationPickerDialog } from "./lazy-relation-picker-dialog-CAScLPb2.js";
|
|
12
|
+
import { t as useRelationSelect } from "./relation-select-B8Fq4o-t.js";
|
|
13
|
+
import { BULK_EACH_LIMIT, FORM_SLOT, LAYOUT_SIZES, builtinFrames, canonicalEntityNodeKey, columnName, compileFlow, compiledScreenFor, declaredField, declaredText, displayClassOf, dtoFields, enumFallbackKey, enumFallbackText, enumNamespaceOf, enumValueText, exportFormatFieldKey, exportFormatLabelKey, exportFormatOptionKey, facts, fieldOptionText, fieldSensitivity, flowRecordOf, formatEntityRef, grantedPlacementKeys, hasRecordPage, humanizeKey, i18nPrefixToRef, i18nPrefixToRef as i18nPrefixToRef$1, inlineFlow, isBuiltinItemAction, isColumnSortable, isRelationField, isScreenFailure, linkOf, normalizeDataError, normalizeEntityRef, planItemActions, projectDto, recordEntries, relatedList, relatedRecord, resolveActionDensity, resolveActionSurfaceMode, resolveColumnUi, resolveContentSlot, resolveEnumLabel, resolveFieldLabel, resolveFieldOptionLabel, resolveFieldSlot, resolveNavLabel, resolveResourceLabel, resolveStaticAvailability, resolveSurfaceUi, resolveTextSlot, resourceKey, resourceShowHref, reverseLinkOf, routeResourceKey, routeServerIssues, savedViewKey, validatePlacementProps, whereEntryToFilter } from "@burojs/core";
|
|
14
|
+
import { ActionRouteContext, DismissGuardScope, FlowRowBatchContext, LOCAL_USER_SCOPE_ID, QUICK_ANCHOR_ATTRIBUTE, RECORD_KIND_KEYS, RecordScopeChain, ResourceListProvider, RouteSourceContext, ScreenStateProvider, columnTypeWidth, implementFrame, kindErrorText, linkCapabilitiesOf, policyRecordOf, policyRowsOf, recordKeyOf, resolveColumnField, resolveErrorText, resolveRoute, resolveValidationMessage, resourceFlowsOn, specificErrorText, tableLayoutSetting, useApp, useApplicationSnapshot, useAvailability, useAvailabilityResolver, useCapability, useCapabilityResolver, useChromeT, useClipboard, useComponent, useCtx, useCurrentUserId, useCustomQuery, useDataRuntime, useDismissGuard, useEntryGate, useEnumValues, useExportRuns, useFieldAccess, useFieldImplementation, useFieldMeta, useFieldMetaSources, useFlowRowBatch, useFlowRowResolver, useFormController, useFrameImplementations, useFramePiece, useFrameT, useI18nCaps, useItemActions, useLinkRights, useLinkWriter, useLinkedKeys, useListAccess, useLocale, useNavigationBlocker, useOne, useOptionalDataRuntime, useOptionalRecordFrame, useOptionalRouterAdapter, useOptionalT, useOptionalUiSettings, usePageFlowGate, usePageScreen, usePathAccess, usePlacement, useProjectedRecord, useProjectedTitle, usePublishDocVariant, useQuick, useQuickEntry, useQuickOpener, useRecordFrame, useRecordScopeFrames, useRecordTitle, useResource, useResourceExport, useResourceList, useResourceListContext, useRouteNavigate, useScreenState, useSearchParamsWriter, useT, useTestIds, useViewRow } from "@burojs/react";
|
|
15
|
+
import { IconAlertCircle, IconAlertTriangle, IconArrowDown, IconArrowLeft, IconArrowRight, IconArrowUp, IconBookmark, IconCheck, IconChevronLeft, IconChevronRight, IconChevronsLeft, IconChevronsRight, IconColumns3, IconCopy, IconDots, IconDownload, IconEyeOff, IconFilterX, IconInbox, IconLink, IconLoader2, IconLock, IconMinus, IconPencil, IconPinned, IconPinnedOff, IconPlus, IconRefresh, IconSelector, IconSettings2, IconTrash, IconUnlink, IconX } from "@tabler/icons-react";
|
|
14
16
|
import * as React from "react";
|
|
15
17
|
import { Component, Fragment, Suspense, createContext, createElement, lazy, memo, useCallback, useContext, useEffect, useId, useMemo, useRef, useState, useSyncExternalStore } from "react";
|
|
16
18
|
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
17
|
-
import { isFlowAction } from "@burojs/core/internal";
|
|
19
|
+
import { actionWhenHolds, enumBindingKey, hasNullRef, isFlowAction, whereToFilter } from "@burojs/core/internal";
|
|
18
20
|
import * as TabsPrimitive from "@radix-ui/react-tabs";
|
|
19
21
|
import * as CheckboxPrimitive from "@radix-ui/react-checkbox";
|
|
20
22
|
import { flexRender, getCoreRowModel, getFacetedRowModel, getFacetedUniqueValues, useReactTable } from "@tanstack/react-table";
|
|
@@ -117,432 +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
|
-
"label",
|
|
192
|
-
"description",
|
|
193
|
-
"options",
|
|
194
|
-
"items",
|
|
195
|
-
"properties",
|
|
196
|
-
"additionalProperties",
|
|
197
|
-
"variants"
|
|
198
|
-
]);
|
|
199
|
-
function ownData(value, key) {
|
|
200
|
-
if (value === null || typeof value !== "object" && typeof value !== "function") return { present: false };
|
|
201
|
-
const descriptor = Object.getOwnPropertyDescriptor(value, key);
|
|
202
|
-
if (descriptor === void 0 || !Object.prototype.propertyIsEnumerable.call(value, key) || !("value" in descriptor)) return { present: false };
|
|
203
|
-
return {
|
|
204
|
-
present: true,
|
|
205
|
-
value: descriptor.value
|
|
206
|
-
};
|
|
207
|
-
}
|
|
208
|
-
function ownString(value, key) {
|
|
209
|
-
const candidate = ownData(value, key).value;
|
|
210
|
-
return typeof candidate === "string" && candidate.length > 0 ? candidate : void 0;
|
|
211
|
-
}
|
|
212
|
-
function projectUiSelection(input) {
|
|
213
|
-
const selection = {};
|
|
214
|
-
const inputId = ownString(input, "input");
|
|
215
|
-
const displayId = ownString(input, "display");
|
|
216
|
-
if (inputId !== void 0) selection.input = inputId;
|
|
217
|
-
if (displayId !== void 0) selection.display = displayId;
|
|
218
|
-
return inputId === void 0 && displayId === void 0 ? void 0 : Object.freeze(selection);
|
|
219
|
-
}
|
|
220
|
-
function projectFieldDeclaration(input) {
|
|
221
|
-
const type = ownString(input, "type");
|
|
222
|
-
if (type === void 0) return void 0;
|
|
223
|
-
const projected = { type };
|
|
224
|
-
for (const key of SEMANTIC_KEYS) {
|
|
225
|
-
if (key === "type" || key === "items" || key === "properties" || key === "variants") continue;
|
|
226
|
-
const candidate = ownData(input, key);
|
|
227
|
-
if (candidate.present && candidate.value !== void 0) projected[key] = candidate.value;
|
|
228
|
-
}
|
|
229
|
-
const ui = projectUiSelection(ownData(input, "ui").value);
|
|
230
|
-
if (ui !== void 0) projected.ui = ui;
|
|
231
|
-
if (type === "array") {
|
|
232
|
-
const item = projectFieldDeclaration(ownData(input, "items").value);
|
|
233
|
-
if (item !== void 0) projected.items = item;
|
|
234
|
-
}
|
|
235
|
-
if (type === "object") {
|
|
236
|
-
const properties = ownData(input, "properties").value;
|
|
237
|
-
if (properties !== null && typeof properties === "object" && !Array.isArray(properties)) {
|
|
238
|
-
const output = {};
|
|
239
|
-
for (const key of Object.keys(properties)) {
|
|
240
|
-
const child = projectFieldDeclaration(ownData(properties, key).value);
|
|
241
|
-
if (child !== void 0) output[key] = child;
|
|
242
|
-
}
|
|
243
|
-
projected.properties = Object.freeze(output);
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
if (type === "union") {
|
|
247
|
-
const variants = ownData(input, "variants").value;
|
|
248
|
-
if (Array.isArray(variants)) {
|
|
249
|
-
const output = [];
|
|
250
|
-
for (let index = 0; index < variants.length; index += 1) {
|
|
251
|
-
const child = projectFieldDeclaration(ownData(variants, index).value);
|
|
252
|
-
if (child !== void 0) output.push(child);
|
|
253
|
-
}
|
|
254
|
-
projected.variants = Object.freeze(output);
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
return Object.freeze(projected);
|
|
258
|
-
}
|
|
259
|
-
function projectUiHints(input) {
|
|
260
|
-
if (input === null || typeof input !== "object" || Array.isArray(input)) return void 0;
|
|
261
|
-
const projected = {};
|
|
262
|
-
for (const key of Object.keys(input)) {
|
|
263
|
-
const candidate = ownData(input, key);
|
|
264
|
-
if (candidate.present) projected[key] = candidate.value;
|
|
265
|
-
}
|
|
266
|
-
return Object.freeze(projected);
|
|
267
|
-
}
|
|
268
|
-
function lowerLegacyField(field) {
|
|
269
|
-
const declaration = projectFieldDeclaration(ownData(field, "declaration").value);
|
|
270
|
-
if (declaration !== void 0) return declaration;
|
|
271
|
-
const direct = projectFieldDeclaration(field);
|
|
272
|
-
const kind = ownString(field, "kind");
|
|
273
|
-
const mapping = kind === void 0 ? void 0 : LEGACY_KIND_DECLARATIONS[kind];
|
|
274
|
-
if (direct !== void 0) {
|
|
275
|
-
const format = direct.format ?? mapping?.format ?? kind;
|
|
276
|
-
return format === void 0 ? direct : Object.freeze({
|
|
277
|
-
...direct,
|
|
278
|
-
format
|
|
279
|
-
});
|
|
280
|
-
}
|
|
281
|
-
const ui = projectUiHints(ownData(field, "ui").value);
|
|
282
|
-
const type = kind === "file" && ui?.multiple !== true ? "unknown" : mapping?.type ?? "unknown";
|
|
283
|
-
const format = ownString(field, "format") ?? mapping?.format ?? kind;
|
|
284
|
-
const items = type === "array" ? projectFieldDeclaration(ownData(field, "items").value) ?? mapping?.items ?? STRING_ITEM : void 0;
|
|
285
|
-
const meta = ownData(field, "meta");
|
|
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
|
-
...requiredValue === true ? { required: true } : {},
|
|
299
|
-
...nullableValue === true ? { nullable: true } : {}
|
|
300
|
-
});
|
|
301
|
-
}
|
|
302
|
-
function lowerLegacyFieldSource(field) {
|
|
303
|
-
const declaration = lowerLegacyField(field);
|
|
304
|
-
const ui = projectUiHints(ownData(field, "ui").value);
|
|
305
|
-
if (ui === void 0) return declaration;
|
|
306
|
-
return Object.freeze({
|
|
307
|
-
...declaration,
|
|
308
|
-
ui
|
|
309
|
-
});
|
|
310
|
-
}
|
|
311
|
-
function fieldDescription(field) {
|
|
312
|
-
const source = field;
|
|
313
|
-
for (const value of [
|
|
314
|
-
source?.declaration?.description,
|
|
315
|
-
source?.description,
|
|
316
|
-
source?.opts?.description,
|
|
317
|
-
source?.meta?.description
|
|
318
|
-
]) if (typeof value === "string" && value !== "") return value;
|
|
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");
|
|
319
128
|
}
|
|
320
129
|
//#endregion
|
|
321
|
-
//#region src/fields/
|
|
322
|
-
const
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
const
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
if (item === null || typeof item !== "object" || Array.isArray(item)) continue;
|
|
336
|
-
const raw = item;
|
|
337
|
-
if (typeof raw.value !== "string" && typeof raw.value !== "number" && typeof raw.value !== "boolean" && raw.value !== null) continue;
|
|
338
|
-
const { value, label, description, ...legacyMeta } = raw;
|
|
339
|
-
if (typeof label !== "string" && process.env.NODE_ENV !== "production" && !warnedObjectLabels.has(`${name}:${value}`)) {
|
|
340
|
-
warnedObjectLabels.add(`${name}:${value}`);
|
|
341
|
-
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).`);
|
|
342
|
-
}
|
|
343
|
-
output.push(Object.freeze({
|
|
344
|
-
value,
|
|
345
|
-
label: typeof label === "string" ? label : String(value),
|
|
346
|
-
...typeof description === "string" ? { description } : {},
|
|
347
|
-
...Object.keys(legacyMeta).length === 0 ? {} : { meta: Object.freeze(legacyMeta) }
|
|
348
|
-
}));
|
|
349
|
-
}
|
|
350
|
-
return Object.freeze(output);
|
|
351
|
-
}
|
|
352
|
-
const readScope = (ctx) => ctx.scope;
|
|
353
|
-
function useFieldRuntimeContext(resource, record, values, metaSources) {
|
|
354
|
-
const scope = useCtx(readScope);
|
|
355
|
-
const locale = useLocale();
|
|
356
|
-
const caps = useI18nCaps(formatEntityRef(resource));
|
|
357
|
-
const metaSourcesOut = useFieldMetaSources({
|
|
358
|
-
record,
|
|
359
|
-
values,
|
|
360
|
-
extra: metaSources
|
|
361
|
-
});
|
|
362
|
-
return useMemo(() => Object.freeze({
|
|
363
|
-
i18n: Object.freeze({
|
|
364
|
-
locale,
|
|
365
|
-
has: caps.has,
|
|
366
|
-
t: caps.t
|
|
367
|
-
}),
|
|
368
|
-
sources: Object.freeze({
|
|
369
|
-
record: record ?? EMPTY,
|
|
370
|
-
values: values ?? EMPTY,
|
|
371
|
-
scope: scope ?? EMPTY
|
|
372
|
-
}),
|
|
373
|
-
metaSources: metaSourcesOut
|
|
374
|
-
}), [
|
|
375
|
-
caps,
|
|
376
|
-
locale,
|
|
377
|
-
record,
|
|
378
|
-
scope,
|
|
379
|
-
values,
|
|
380
|
-
metaSourcesOut
|
|
381
|
-
]);
|
|
382
|
-
}
|
|
383
|
-
function useFieldMetaDiagnostic(state, resource, name, field, surface) {
|
|
384
|
-
const reported = useRef(void 0);
|
|
385
|
-
const reason = state.status === "failed" ? state.reason : void 0;
|
|
386
|
-
useEffect(() => {
|
|
387
|
-
if (reason === void 0 || reported.current === reason || process.env.NODE_ENV === "production") return;
|
|
388
|
-
reported.current = reason;
|
|
389
|
-
const named = field.ui?.[surface];
|
|
390
|
-
const implementation = typeof named === "string" ? named : field.format ?? field.type;
|
|
391
|
-
console.error(`[burojs] field.meta-invalid: ${formatEntityRef(resource)}.${name} (${implementation}): ${reason}`);
|
|
392
|
-
}, [
|
|
393
|
-
reason,
|
|
394
|
-
resource,
|
|
395
|
-
name,
|
|
396
|
-
field,
|
|
397
|
-
surface
|
|
398
|
-
]);
|
|
399
|
-
}
|
|
400
|
-
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 }) {
|
|
401
144
|
const t = useOptionalT();
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
"data-buro-field-meta": "pending",
|
|
405
|
-
className: "h-9 w-full"
|
|
406
|
-
});
|
|
407
|
-
return jsx("div", {
|
|
408
|
-
role: "alert",
|
|
409
|
-
"data-buro-field-meta": "failed",
|
|
410
|
-
className: "rounded-md border border-dashed px-3 py-2 text-sm text-muted-foreground",
|
|
411
|
-
children: t("field.misconfigured", { defaultValue: "This field is misconfigured" })
|
|
412
|
-
});
|
|
413
|
-
}
|
|
414
|
-
function useFieldImplementationDiagnostic(diagnostic) {
|
|
415
|
-
const reported = useRef(void 0);
|
|
145
|
+
const clipboard = useClipboard();
|
|
146
|
+
const [copied, setCopied] = useState(false);
|
|
416
147
|
useEffect(() => {
|
|
417
|
-
if (
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
}, [
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
const
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
});
|
|
429
|
-
const runtimeContext = useFieldRuntimeContext(resource, record, record, metaSources);
|
|
430
|
-
const metaState = useFieldMeta(field, resolved.implementation, runtimeContext.metaSources);
|
|
431
|
-
useFieldMetaDiagnostic(metaState, resource, name, field, "display");
|
|
432
|
-
useFieldImplementationDiagnostic(resolved.diagnostic);
|
|
433
|
-
if (resolved.Component === void 0) return jsx(Fragment$1, { children: fallback });
|
|
434
|
-
if (metaState.status !== "ready") return jsx(MetaPlaceholder, { state: metaState });
|
|
435
|
-
const Component = resolved.Component;
|
|
436
|
-
const context = {
|
|
437
|
-
owner: resource,
|
|
438
|
-
resource,
|
|
148
|
+
if (!copied) return;
|
|
149
|
+
const timer = setTimeout(() => setCopied(false), COPIED_MS);
|
|
150
|
+
return () => clearTimeout(timer);
|
|
151
|
+
}, [copied]);
|
|
152
|
+
const ui = isRecord$1(field.ui) ? field.ui : void 0;
|
|
153
|
+
const asksCopy = ui?.copy === true;
|
|
154
|
+
const valueClass = useMemo(() => asksCopy ? displayClassOf({
|
|
155
|
+
...lowerLegacyField(field),
|
|
156
|
+
...ui === void 0 ? {} : { ui }
|
|
157
|
+
}) : void 0, [
|
|
158
|
+
asksCopy,
|
|
439
159
|
field,
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
};
|
|
455
|
-
if (resolved.multiple) return createElement(Component, {
|
|
456
|
-
...context,
|
|
457
|
-
multiple: true,
|
|
458
|
-
value: Array.isArray(value) ? value : []
|
|
459
|
-
});
|
|
460
|
-
return createElement(Component, {
|
|
461
|
-
...context,
|
|
462
|
-
multiple: false,
|
|
463
|
-
value
|
|
464
|
-
});
|
|
465
|
-
}
|
|
466
|
-
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 }) {
|
|
467
|
-
const sourceAtEdge = lowerLegacyFieldSource(source);
|
|
468
|
-
const base = canonical(sourceAtEdge);
|
|
469
|
-
const field = required === void 0 || required === base.required ? base : Object.freeze({
|
|
470
|
-
...base,
|
|
471
|
-
required
|
|
472
|
-
});
|
|
473
|
-
const ui = Object.freeze({
|
|
474
|
-
...sourceAtEdge.ui ?? EMPTY,
|
|
475
|
-
...placeholder === void 0 ? {} : { placeholder }
|
|
476
|
-
});
|
|
477
|
-
const resolved = useFieldImplementation({
|
|
478
|
-
field: {
|
|
479
|
-
...field,
|
|
480
|
-
ui
|
|
481
|
-
},
|
|
482
|
-
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
|
|
483
174
|
});
|
|
484
|
-
|
|
485
|
-
const
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
const Component = resolved.Component;
|
|
491
|
-
const issues = suppliedIssues ?? (errorMessage == null ? EMPTY_ISSUES$1 : Object.freeze([Object.freeze({
|
|
492
|
-
code: "ui.field-error",
|
|
493
|
-
message: {
|
|
494
|
-
key: "ui.field-error",
|
|
495
|
-
default: errorMessage
|
|
496
|
-
},
|
|
497
|
-
path: Object.freeze([]),
|
|
498
|
-
source: "field",
|
|
499
|
-
severity: "error"
|
|
500
|
-
})]));
|
|
501
|
-
const context = {
|
|
502
|
-
owner: resource,
|
|
503
|
-
resource,
|
|
504
|
-
field,
|
|
505
|
-
path: Object.freeze([name]),
|
|
506
|
-
surface: "input",
|
|
507
|
-
presentation: "default",
|
|
508
|
-
record,
|
|
509
|
-
form,
|
|
510
|
-
meta: metaState.meta,
|
|
511
|
-
ui,
|
|
512
|
-
options: optionsFor(field, name),
|
|
513
|
-
i18n: runtimeContext.i18n,
|
|
514
|
-
sources: runtimeContext.sources,
|
|
515
|
-
pending,
|
|
516
|
-
touched,
|
|
517
|
-
issues,
|
|
518
|
-
accessibility: Object.freeze({
|
|
519
|
-
input: inputId ?? name,
|
|
520
|
-
description: descriptionId,
|
|
521
|
-
error: errorId
|
|
522
|
-
}),
|
|
523
|
-
testIds: testId === void 0 ? EMPTY : Object.freeze({ input: testId })
|
|
175
|
+
if (!copyable) return jsx(Fragment$1, { children: body });
|
|
176
|
+
const label = t("field.copy", { defaultValue: "Copy" });
|
|
177
|
+
const copy = (event) => {
|
|
178
|
+
event.preventDefault();
|
|
179
|
+
event.stopPropagation();
|
|
180
|
+
clipboard(copyText(value)).then(() => setCopied(true), () => void 0);
|
|
524
181
|
};
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
182
|
+
return jsxs("span", {
|
|
183
|
+
className: "inline-flex min-w-0 max-w-full items-center gap-1 align-bottom",
|
|
184
|
+
children: [
|
|
185
|
+
body,
|
|
186
|
+
jsx("button", {
|
|
187
|
+
type: "button",
|
|
188
|
+
"data-testid": `field-copy:${field.name}`,
|
|
189
|
+
"aria-label": label,
|
|
190
|
+
title: label,
|
|
191
|
+
onClick: copy,
|
|
192
|
+
className: "inline-flex size-6 shrink-0 items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-accent hover:text-accent-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
|
|
193
|
+
children: copied ? jsx(IconCheck, {
|
|
194
|
+
"aria-hidden": "true",
|
|
195
|
+
className: "size-3.5"
|
|
196
|
+
}) : jsx(IconCopy, {
|
|
197
|
+
"aria-hidden": "true",
|
|
198
|
+
className: "size-3.5"
|
|
199
|
+
})
|
|
200
|
+
}),
|
|
201
|
+
jsx("span", {
|
|
202
|
+
"aria-live": "polite",
|
|
203
|
+
className: "sr-only",
|
|
204
|
+
children: copied ? t("field.copied", { defaultValue: "Copied" }) : ""
|
|
205
|
+
})
|
|
206
|
+
]
|
|
546
207
|
});
|
|
547
208
|
}
|
|
548
209
|
//#endregion
|
|
@@ -582,16 +243,26 @@ function Field({ resource, i18nPrefix, fallback, fixedFilters, field, doc, ...re
|
|
|
582
243
|
});
|
|
583
244
|
}
|
|
584
245
|
function FieldDisplay({ resource, i18nPrefix, fallback, value, ...rest }) {
|
|
585
|
-
|
|
586
|
-
...rest,
|
|
246
|
+
const frameField = useMemo(() => ({
|
|
247
|
+
...rest.field,
|
|
248
|
+
name: rest.name
|
|
249
|
+
}), [rest.field, rest.name]);
|
|
250
|
+
return jsx(FieldValueFrame, {
|
|
251
|
+
field: frameField,
|
|
587
252
|
value,
|
|
588
|
-
|
|
589
|
-
|
|
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
|
+
})
|
|
590
260
|
});
|
|
591
261
|
}
|
|
592
262
|
//#endregion
|
|
593
263
|
//#region src/screen/field-leaf.tsx
|
|
594
264
|
const SIZES = new Set(LAYOUT_SIZES);
|
|
265
|
+
const SHOWN_ISSUES = 3;
|
|
595
266
|
const FOCUSABLE = "input:not([type=\"hidden\"]):not([disabled]), textarea:not([disabled]), select:not([disabled]), button:not([disabled]), [contenteditable=\"true\"], [tabindex]:not([tabindex=\"-1\"])";
|
|
596
267
|
function useLeafSize(field) {
|
|
597
268
|
const implementation = useFieldImplementation({
|
|
@@ -637,9 +308,9 @@ function MisconfiguredLeaf({ node }) {
|
|
|
637
308
|
values: controller.values,
|
|
638
309
|
extra: controller.sources
|
|
639
310
|
});
|
|
640
|
-
const state = useFieldMeta(field, resolved.implementation, sources);
|
|
311
|
+
const state = useFieldMeta(field, resolved.implementation, sources, resolved.params);
|
|
641
312
|
useFieldMetaDiagnostic(state, binding.resource, name, field, surface);
|
|
642
|
-
const label = resolveFieldLabel(caps, name,
|
|
313
|
+
const label = resolveFieldLabel(caps, name, declaredText("field", binding.field, "label"));
|
|
643
314
|
const testId = leafIds === "record" ? tid.formField(resource, mode, name) : `${leafIds.prefix}:field:${name}`;
|
|
644
315
|
return jsxs("div", {
|
|
645
316
|
...leafAttributes("md", void 0),
|
|
@@ -664,16 +335,12 @@ function RecordLeaf({ node }) {
|
|
|
664
335
|
const weight = compiled.nodes[node.index]?.options.weight;
|
|
665
336
|
const caps = useI18nCaps(resource);
|
|
666
337
|
const tid = useTestIds();
|
|
667
|
-
const
|
|
668
|
-
const label = resolveFieldLabel(caps, name, typeof declared.label === "string" ? declared.label : void 0);
|
|
338
|
+
const label = resolveFieldLabel(caps, name, declaredText("field", binding.field, "label"));
|
|
669
339
|
const attributes = leafAttributes(size, weight);
|
|
670
340
|
const t = useOptionalT();
|
|
671
341
|
if (node.restricted === true) {
|
|
672
342
|
const rawReason = node.restrictedReason;
|
|
673
|
-
const reason = rawReason === void 0 || rawReason === "" ? void 0 :
|
|
674
|
-
resource,
|
|
675
|
-
default: rawReason
|
|
676
|
-
});
|
|
343
|
+
const reason = rawReason === void 0 || rawReason === "" ? void 0 : reasonText(caps, rawReason, resource);
|
|
677
344
|
const restrictedId = tid.formFieldRestricted(resource, mode, name);
|
|
678
345
|
return jsxs("div", {
|
|
679
346
|
...attributes,
|
|
@@ -736,10 +403,7 @@ function RecordLeaf({ node }) {
|
|
|
736
403
|
});
|
|
737
404
|
if (node.editable !== true) {
|
|
738
405
|
const rawReason = node.lockReason;
|
|
739
|
-
const reason = rawReason === void 0 ? void 0 :
|
|
740
|
-
resource,
|
|
741
|
-
default: rawReason
|
|
742
|
-
});
|
|
406
|
+
const reason = rawReason === void 0 ? void 0 : reasonText(caps, rawReason, resource);
|
|
743
407
|
const locked = tid.formFieldLocked(resource, mode, name);
|
|
744
408
|
return jsxs("div", {
|
|
745
409
|
...attributes,
|
|
@@ -791,7 +455,6 @@ function RecordLeaf({ node }) {
|
|
|
791
455
|
function ActiveLeaf({ node, binding, label, attributes, inputId }) {
|
|
792
456
|
const { resource, mode, controller, disabled: locked } = useScreenTree();
|
|
793
457
|
const caps = useI18nCaps(resource);
|
|
794
|
-
const t = useOptionalT();
|
|
795
458
|
const tid = useTestIds();
|
|
796
459
|
const name = binding.name;
|
|
797
460
|
const record = controller.record;
|
|
@@ -807,24 +470,21 @@ function ActiveLeaf({ node, binding, label, attributes, inputId }) {
|
|
|
807
470
|
const messageId = `${mode}-${name}-msg`;
|
|
808
471
|
const placeholder = resolveFieldSlot(caps, name, "placeholder");
|
|
809
472
|
const hint = resolveFieldSlot(caps, name, "description", fieldDescription(binding.field));
|
|
473
|
+
const chromeT = useChromeT();
|
|
810
474
|
const fieldKey = `resources.${resource}.fields.${name}`;
|
|
811
|
-
const
|
|
812
|
-
code:
|
|
813
|
-
message:
|
|
814
|
-
...
|
|
815
|
-
}, {
|
|
816
|
-
t,
|
|
817
|
-
fieldKey
|
|
818
|
-
});
|
|
819
|
-
const warning = errorText === null ? binding.warnings[0] : void 0;
|
|
820
|
-
const warningText = warning === void 0 ? null : resolveValidationMessage({
|
|
821
|
-
code: warning.code ?? "",
|
|
822
|
-
message: warning.message,
|
|
823
|
-
...warning.params === void 0 ? {} : { params: warning.params }
|
|
475
|
+
const text = (issue) => resolveValidationMessage({
|
|
476
|
+
code: issue.code ?? "",
|
|
477
|
+
message: issue.message,
|
|
478
|
+
...issue.check === void 0 ? {} : { check: issue.check }
|
|
824
479
|
}, {
|
|
825
|
-
|
|
480
|
+
has: caps.has,
|
|
481
|
+
t: caps.t,
|
|
826
482
|
fieldKey
|
|
827
483
|
});
|
|
484
|
+
const shown = binding.issues.slice(0, SHOWN_ISSUES);
|
|
485
|
+
const more = binding.issues.length - shown.length;
|
|
486
|
+
const firstError = shown.find((issue) => issue.severity === "error");
|
|
487
|
+
const errorText = firstError === void 0 ? null : text(firstError);
|
|
828
488
|
const disabled = binding.disabled || controller.isSubmitting || locked === true;
|
|
829
489
|
const describedBy = errorText === null && hint ? messageId : void 0;
|
|
830
490
|
const errorMessageId = errorText === null ? void 0 : messageId;
|
|
@@ -838,6 +498,7 @@ function ActiveLeaf({ node, binding, label, attributes, inputId }) {
|
|
|
838
498
|
"data-buro-region": "field-label",
|
|
839
499
|
className: "flex items-center gap-2",
|
|
840
500
|
children: [jsx(Label, {
|
|
501
|
+
id: `${inputId}-label`,
|
|
841
502
|
htmlFor: inputId,
|
|
842
503
|
required: binding.required,
|
|
843
504
|
children: label
|
|
@@ -853,6 +514,7 @@ function ActiveLeaf({ node, binding, label, attributes, inputId }) {
|
|
|
853
514
|
resource: binding.resource,
|
|
854
515
|
name,
|
|
855
516
|
inputId,
|
|
517
|
+
labelId: `${inputId}-label`,
|
|
856
518
|
field: binding.field,
|
|
857
519
|
value: binding.value,
|
|
858
520
|
onChange: binding.onChange,
|
|
@@ -866,8 +528,10 @@ function ActiveLeaf({ node, binding, label, attributes, inputId }) {
|
|
|
866
528
|
...describedBy === void 0 ? {} : { descriptionId: describedBy },
|
|
867
529
|
...errorMessageId === void 0 ? {} : { errorId: errorMessageId },
|
|
868
530
|
errorMessage: errorText,
|
|
531
|
+
issues: binding.issues,
|
|
869
532
|
...binding.fixedFilters === void 0 ? {} : { fixedFilters: binding.fixedFilters },
|
|
870
533
|
...binding.relationSeeds === void 0 ? {} : { relationSeeds: binding.relationSeeds },
|
|
534
|
+
...binding.link === void 0 ? {} : { link: binding.link },
|
|
871
535
|
fallback: jsx(Input, {
|
|
872
536
|
id: inputId,
|
|
873
537
|
name: inputId,
|
|
@@ -882,18 +546,23 @@ function ActiveLeaf({ node, binding, label, attributes, inputId }) {
|
|
|
882
546
|
...errorMessageId === void 0 ? {} : { "aria-errormessage": errorMessageId },
|
|
883
547
|
onChange: (event) => binding.onChange(event.target.value)
|
|
884
548
|
})
|
|
885
|
-
}),
|
|
886
|
-
id: messageId,
|
|
887
|
-
"aria-live": "polite",
|
|
888
|
-
className: "text-sm text-destructive",
|
|
889
|
-
"data-testid": tid.formFieldError(resource, mode, name),
|
|
890
|
-
children: errorText
|
|
891
|
-
}) : warningText !== null ? jsx("p", {
|
|
549
|
+
}), shown.length > 0 ? jsxs("div", {
|
|
892
550
|
id: messageId,
|
|
893
551
|
"aria-live": "polite",
|
|
894
|
-
className: "text-sm text-warning-emphasis",
|
|
895
|
-
"data-testid": `form-field-warning:${resource}:${mode}:${name}
|
|
896
|
-
children:
|
|
552
|
+
className: cn("space-y-0.5 text-sm", errorText === null ? "text-warning-emphasis" : "text-destructive"),
|
|
553
|
+
"data-testid": errorText === null ? `form-field-warning:${resource}:${mode}:${name}` : tid.formFieldError(resource, mode, name),
|
|
554
|
+
children: [shown.map((issue, index) => jsx("p", {
|
|
555
|
+
"data-buro-severity": issue.severity,
|
|
556
|
+
className: issue.severity === "error" ? "text-destructive" : "text-warning-emphasis",
|
|
557
|
+
children: text(issue)
|
|
558
|
+
}, index)), more > 0 ? jsx("p", {
|
|
559
|
+
className: "text-muted-foreground",
|
|
560
|
+
"data-testid": `form-field-more:${resource}:${mode}:${name}`,
|
|
561
|
+
children: chromeT("validation.moreIssues", {
|
|
562
|
+
count: more,
|
|
563
|
+
defaultValue: "+{{count}} more"
|
|
564
|
+
})
|
|
565
|
+
}) : null]
|
|
897
566
|
}) : hint ? jsx("p", {
|
|
898
567
|
id: messageId,
|
|
899
568
|
"data-testid": tid.formFieldHelp(resource, mode, name),
|
|
@@ -913,7 +582,7 @@ function DeclarativeLeaf({ node, prefix, inputIds }) {
|
|
|
913
582
|
const docRef = i18nPrefixToRef(prefix);
|
|
914
583
|
const docScope = docRef.namespace === "default" ? docRef.id : formatEntityRef(docRef);
|
|
915
584
|
const caps = useI18nCaps(docScope);
|
|
916
|
-
const
|
|
585
|
+
const issueCaps = useI18nCaps();
|
|
917
586
|
const uid = useId();
|
|
918
587
|
const root = useRef(null);
|
|
919
588
|
const inputId = `${inputIds ?? uid}-${name}`;
|
|
@@ -925,7 +594,7 @@ function DeclarativeLeaf({ node, prefix, inputIds }) {
|
|
|
925
594
|
if (byId !== null && element.ownerDocument.activeElement === byId) return;
|
|
926
595
|
element.querySelector(FOCUSABLE)?.focus();
|
|
927
596
|
});
|
|
928
|
-
const label = resolveFieldLabel(caps, name, binding.field
|
|
597
|
+
const label = resolveFieldLabel(caps, name, declaredText("field", binding.field, "label"));
|
|
929
598
|
const hint = resolveFieldSlot(caps, name, "description", fieldDescription(binding.field));
|
|
930
599
|
const info = jsx(DocInfo, {
|
|
931
600
|
address: `${docScope}/fields/${name}`,
|
|
@@ -967,9 +636,10 @@ function DeclarativeLeaf({ node, prefix, inputIds }) {
|
|
|
967
636
|
const text = (issue) => resolveValidationMessage({
|
|
968
637
|
code: issue.code ?? "",
|
|
969
638
|
message: issue.message,
|
|
970
|
-
...issue.
|
|
639
|
+
...issue.check === void 0 ? {} : { check: issue.check }
|
|
971
640
|
}, {
|
|
972
|
-
|
|
641
|
+
has: issueCaps.has,
|
|
642
|
+
t: issueCaps.t,
|
|
973
643
|
fieldKey
|
|
974
644
|
});
|
|
975
645
|
const defaultNode = jsx(Field, {
|
|
@@ -977,6 +647,7 @@ function DeclarativeLeaf({ node, prefix, inputIds }) {
|
|
|
977
647
|
field: binding.field,
|
|
978
648
|
metaSources: controller.sources,
|
|
979
649
|
inputId,
|
|
650
|
+
labelId: `${inputId}-label`,
|
|
980
651
|
i18nPrefix: prefix,
|
|
981
652
|
disabled: disabled === true || binding.disabled
|
|
982
653
|
});
|
|
@@ -992,6 +663,7 @@ function DeclarativeLeaf({ node, prefix, inputIds }) {
|
|
|
992
663
|
"data-buro-region": "field-label",
|
|
993
664
|
className: "flex items-center gap-2",
|
|
994
665
|
children: [jsx(Label, {
|
|
666
|
+
id: `${inputId}-label`,
|
|
995
667
|
htmlFor: inputId,
|
|
996
668
|
required: binding.required,
|
|
997
669
|
children: label
|
|
@@ -1032,9 +704,10 @@ function Notice({ children }) {
|
|
|
1032
704
|
children
|
|
1033
705
|
});
|
|
1034
706
|
}
|
|
1035
|
-
function FactsView({ props }) {
|
|
707
|
+
function FactsView({ props, empty }) {
|
|
1036
708
|
const own = props;
|
|
1037
709
|
const t = useOptionalT();
|
|
710
|
+
const chromeT = useChromeT();
|
|
1038
711
|
const snapshot = useApplicationSnapshot();
|
|
1039
712
|
const queryId = formatEntityRef(own.query);
|
|
1040
713
|
const caps = useI18nCaps();
|
|
@@ -1051,7 +724,10 @@ function FactsView({ props }) {
|
|
|
1051
724
|
kind: "custom-query",
|
|
1052
725
|
ref: own.query
|
|
1053
726
|
}), [own.query]);
|
|
1054
|
-
const result = useCustomQuery(handle, blocked ? {} : input, {
|
|
727
|
+
const result = useCustomQuery(handle, blocked ? {} : input, {
|
|
728
|
+
enabled: !blocked,
|
|
729
|
+
...own.refetchInterval === void 0 ? {} : { refetchInterval: own.refetchInterval }
|
|
730
|
+
});
|
|
1055
731
|
const shown = useMemo(() => {
|
|
1056
732
|
const byName = new Map(fields.map((field) => [field.name, field]));
|
|
1057
733
|
return (own.rows ?? fields.map((field) => field.name)).flatMap((name) => {
|
|
@@ -1059,7 +735,7 @@ function FactsView({ props }) {
|
|
|
1059
735
|
return field === void 0 ? [] : [field];
|
|
1060
736
|
});
|
|
1061
737
|
}, [fields, own.rows]);
|
|
1062
|
-
if (blocked) return jsx(Notice, { children: own.missing ?? t("facts.empty", { defaultValue: "No data" }) });
|
|
738
|
+
if (blocked) return jsx(Notice, { children: own.missing ?? empty ?? t("facts.empty", { defaultValue: "No data" }) });
|
|
1063
739
|
if (result.isError) return jsxs("div", {
|
|
1064
740
|
role: "alert",
|
|
1065
741
|
className: "flex flex-wrap items-center gap-2 rounded-md border border-dashed px-3 py-2 text-sm text-muted-foreground",
|
|
@@ -1068,7 +744,7 @@ function FactsView({ props }) {
|
|
|
1068
744
|
variant: "outline",
|
|
1069
745
|
size: "sm",
|
|
1070
746
|
onClick: () => void result.refetch(),
|
|
1071
|
-
children:
|
|
747
|
+
children: chromeT("actions.retry", { defaultValue: "Retry" })
|
|
1072
748
|
})]
|
|
1073
749
|
});
|
|
1074
750
|
if (result.data === void 0) return own.view === "list" ? jsx(Skeleton, {
|
|
@@ -1082,23 +758,28 @@ function FactsView({ props }) {
|
|
|
1082
758
|
}, field.name))
|
|
1083
759
|
});
|
|
1084
760
|
const data = result.data;
|
|
1085
|
-
if (data === null || typeof data !== "object" || shown.length === 0) return jsx(Notice, { children: own.missing ?? t("facts.empty", { defaultValue: "No data" }) });
|
|
761
|
+
if (data === null || typeof data !== "object" || shown.length === 0) return jsx(Notice, { children: own.missing ?? empty ?? t("facts.empty", { defaultValue: "No data" }) });
|
|
1086
762
|
const pairs = shown.map((field) => {
|
|
1087
763
|
const value = data[field.name];
|
|
1088
764
|
const label = resolveContentSlot(caps, [`queries.${queryId}.fields.${field.name}.label`, `queries.${queryId}.fields.${field.name}`], {
|
|
1089
|
-
...
|
|
765
|
+
...declaredText("field", field, "label") === void 0 ? {} : { default: declaredText("field", field, "label") },
|
|
1090
766
|
humanize: () => humanizeKey(field.name)
|
|
1091
767
|
});
|
|
1092
768
|
const body = value === null || value === void 0 ? jsx("span", {
|
|
1093
769
|
className: "text-muted-foreground",
|
|
1094
770
|
children: "—"
|
|
1095
|
-
}) : jsx(
|
|
1096
|
-
resource: own.query,
|
|
1097
|
-
name: field.name,
|
|
771
|
+
}) : jsx(FieldValueFrame, {
|
|
1098
772
|
field,
|
|
1099
773
|
value,
|
|
1100
|
-
|
|
1101
|
-
|
|
774
|
+
surface: "display",
|
|
775
|
+
children: jsx(ResolvedFieldDisplay, {
|
|
776
|
+
resource: own.query,
|
|
777
|
+
name: field.name,
|
|
778
|
+
field,
|
|
779
|
+
value,
|
|
780
|
+
record: data,
|
|
781
|
+
fallback: toDisplayText(value).text
|
|
782
|
+
})
|
|
1102
783
|
});
|
|
1103
784
|
return {
|
|
1104
785
|
name: field.name,
|
|
@@ -1152,6 +833,9 @@ const PlacementFrameContext = createContext(null);
|
|
|
1152
833
|
function usePlacementFrame() {
|
|
1153
834
|
return useContext(PlacementFrameContext);
|
|
1154
835
|
}
|
|
836
|
+
function placementLayoutId(frame) {
|
|
837
|
+
return frame?.owner === void 0 ? void 0 : `${frame.owner}/${frame.key.split(".").join("/")}`;
|
|
838
|
+
}
|
|
1155
839
|
//#endregion
|
|
1156
840
|
//#region src/flows/app-flow-button.tsx
|
|
1157
841
|
function AppFlowButton({ action, variant = "ghost", size = "sm", className }) {
|
|
@@ -1271,7 +955,7 @@ function RecordInlineFlow({ name }) {
|
|
|
1271
955
|
return jsx(InlineHostLoader, {
|
|
1272
956
|
resource,
|
|
1273
957
|
name: flowName,
|
|
1274
|
-
|
|
958
|
+
...hostRecord(flowRecordOf(compiled, row)),
|
|
1275
959
|
placementKey: key,
|
|
1276
960
|
lease: kept,
|
|
1277
961
|
stale: true,
|
|
@@ -1287,7 +971,7 @@ function RecordInlineFlow({ name }) {
|
|
|
1287
971
|
if (control.state === "disabled" && kept !== void 0 && kept.dirty) return jsx(InlineHostLoader, {
|
|
1288
972
|
resource,
|
|
1289
973
|
name: flowName,
|
|
1290
|
-
|
|
974
|
+
...hostRecord(flowRecordOf(compiled, row)),
|
|
1291
975
|
placementKey: key,
|
|
1292
976
|
lease: kept,
|
|
1293
977
|
stale: true,
|
|
@@ -1299,11 +983,12 @@ function RecordInlineFlow({ name }) {
|
|
|
1299
983
|
return jsx(InlineHostLoader, {
|
|
1300
984
|
resource,
|
|
1301
985
|
name: flowName,
|
|
1302
|
-
|
|
986
|
+
...hostRecord(flowRecordOf(compiled, control.record ?? row)),
|
|
1303
987
|
placementKey: key,
|
|
1304
988
|
...lease === void 0 ? {} : { lease }
|
|
1305
989
|
});
|
|
1306
990
|
}
|
|
991
|
+
const hostRecord = (record) => record === void 0 ? {} : { record };
|
|
1307
992
|
function InlineHostLoader(props) {
|
|
1308
993
|
const loaded = useLoaded(loadInlineFlowHost);
|
|
1309
994
|
if (loaded.error !== void 0) return jsx("p", {
|
|
@@ -1357,11 +1042,11 @@ function PageInlineFlow({ flow }) {
|
|
|
1357
1042
|
"data-buro-flow-state": "pending"
|
|
1358
1043
|
});
|
|
1359
1044
|
const appAction = "appAction" in target ? app.registry.appActions.get(target.appAction) : void 0;
|
|
1360
|
-
const label = appAction === void 0 ? void 0 : resolveNavLabel(caps, `appActions.${"appAction" in target ? target.appAction : ""}`, appAction
|
|
1045
|
+
const label = appAction === void 0 ? void 0 : resolveNavLabel(caps, `appActions.${"appAction" in target ? target.appAction : ""}`, declaredText("flow", appAction, "label"));
|
|
1361
1046
|
const button = "appAction" in target ? jsx(AppFlowButton, {
|
|
1362
1047
|
action: {
|
|
1363
1048
|
name: target.appAction,
|
|
1364
|
-
label: appAction
|
|
1049
|
+
label: declaredText("flow", appAction, "label") ?? target.appAction,
|
|
1365
1050
|
...appAction?.icon === void 0 ? {} : { icon: appAction.icon },
|
|
1366
1051
|
...answer.state === "disabled" ? { disabledReason: answer.reason ?? "" } : {}
|
|
1367
1052
|
},
|
|
@@ -1519,6 +1204,7 @@ function placementSeeds(where, resolved, closure) {
|
|
|
1519
1204
|
where.forEach((entry, index) => {
|
|
1520
1205
|
const value = resolved[index]?.value;
|
|
1521
1206
|
if (entry.op !== "eq" || value === null || value === void 0) return;
|
|
1207
|
+
if (typeof entry.field !== "string" || entry.field.includes(".")) return;
|
|
1522
1208
|
if (resolved[index]?.field !== entry.field) return;
|
|
1523
1209
|
const source = refSource(entry.value);
|
|
1524
1210
|
if (source === null ? isLiteral(entry.value) : source === "record" || source === "user" || !UNTRUSTED.includes(source) && Object.hasOwn(closure, source) && !(closure[source] ?? []).some((input) => UNTRUSTED.includes(input))) seeds[entry.field] = value;
|
|
@@ -1536,7 +1222,8 @@ function BadgeBody({ icon, label }) {
|
|
|
1536
1222
|
}) : null, label]
|
|
1537
1223
|
});
|
|
1538
1224
|
}
|
|
1539
|
-
function EnumBadge({ enumName, value, testid, className, presentation }) {
|
|
1225
|
+
function EnumBadge({ enumName: enumInput, value, testid, className, presentation }) {
|
|
1226
|
+
const enumName = enumBindingKey(enumInput) ?? "";
|
|
1540
1227
|
const app = useApp();
|
|
1541
1228
|
const caps = useI18nCaps();
|
|
1542
1229
|
const def = app.registry.enums.get(enumName);
|
|
@@ -1579,7 +1266,7 @@ function EnumBadge({ enumName, value, testid, className, presentation }) {
|
|
|
1579
1266
|
if (entry) {
|
|
1580
1267
|
const body = jsx(BadgeBody, {
|
|
1581
1268
|
icon: entry.icon,
|
|
1582
|
-
label: resolveEnumLabel(caps, ns, value, entry.label)
|
|
1269
|
+
label: resolveEnumLabel(caps, ns, value, enumValueText(def, value) ?? entry.label)
|
|
1583
1270
|
});
|
|
1584
1271
|
if (presentation === "plain") return jsx("span", {
|
|
1585
1272
|
"data-testid": id,
|
|
@@ -1595,15 +1282,16 @@ function EnumBadge({ enumName, value, testid, className, presentation }) {
|
|
|
1595
1282
|
children: body
|
|
1596
1283
|
});
|
|
1597
1284
|
}
|
|
1598
|
-
|
|
1599
|
-
|
|
1285
|
+
const declaredFallback = enumFallbackText(def);
|
|
1286
|
+
if (def?.fallback !== void 0 || declaredFallback !== void 0) {
|
|
1287
|
+
const fallbackLabel = resolveContentSlot(caps, [enumFallbackKey(ns)], { default: declaredFallback }) ?? value;
|
|
1600
1288
|
if (presentation === "plain") return jsx("span", {
|
|
1601
1289
|
"data-testid": `${id}:fallback`,
|
|
1602
1290
|
className,
|
|
1603
1291
|
children: fallbackLabel
|
|
1604
1292
|
});
|
|
1605
1293
|
return jsx(Badge, {
|
|
1606
|
-
variant: def
|
|
1294
|
+
variant: def?.fallback?.color ?? "outline",
|
|
1607
1295
|
className,
|
|
1608
1296
|
"data-testid": `${id}:fallback`,
|
|
1609
1297
|
children: fallbackLabel
|
|
@@ -1681,6 +1369,7 @@ function useExportRunWatcher() {
|
|
|
1681
1369
|
const runtime = useDataRuntime();
|
|
1682
1370
|
const runs = useExportRuns();
|
|
1683
1371
|
const t = useOptionalT();
|
|
1372
|
+
const caps = useI18nCaps();
|
|
1684
1373
|
React.useEffect(() => {
|
|
1685
1374
|
const announced = announcedRunsFor(runtime);
|
|
1686
1375
|
const unsubscribes = runs.map((run) => {
|
|
@@ -1699,7 +1388,7 @@ function useExportRunWatcher() {
|
|
|
1699
1388
|
label: t("export.toast.download"),
|
|
1700
1389
|
onClick: () => downloadArtifact(artifact)
|
|
1701
1390
|
} });
|
|
1702
|
-
} else if (state.status === "failed") toast.error(t("export.toast.failed", { params: { message: state.error
|
|
1391
|
+
} else if (state.status === "failed") toast.error(t("export.toast.failed", { params: { message: resolveErrorText(state.error, caps) } }));
|
|
1703
1392
|
};
|
|
1704
1393
|
check();
|
|
1705
1394
|
return run.handle.subscribe(check);
|
|
@@ -1710,24 +1399,27 @@ function useExportRunWatcher() {
|
|
|
1710
1399
|
}, [
|
|
1711
1400
|
runs,
|
|
1712
1401
|
runtime,
|
|
1713
|
-
t
|
|
1402
|
+
t,
|
|
1403
|
+
caps
|
|
1714
1404
|
]);
|
|
1715
1405
|
}
|
|
1716
1406
|
//#endregion
|
|
1717
1407
|
//#region src/actions/action-overflow.tsx
|
|
1718
1408
|
const RowActionsCollapseContext = createContext(false);
|
|
1719
|
-
function ActionOverflow({ resource, record, surface, className, hideFlows, collapse }) {
|
|
1720
|
-
const
|
|
1409
|
+
function ActionOverflow({ resource: resourceInput, record, surface, className, hideFlows, collapse, sources }) {
|
|
1410
|
+
const resource = routeResourceKey(resourceInput);
|
|
1411
|
+
const chromeT = useChromeT();
|
|
1721
1412
|
const tid = useTestIds();
|
|
1413
|
+
const recordKey = String(recordKeyOf(useApp().registry.get(resource), record) ?? "");
|
|
1722
1414
|
const collapsedRows = useContext(RowActionsCollapseContext);
|
|
1723
1415
|
const [flowFor, setFlowFor] = useState(null);
|
|
1724
|
-
const actions = useShownItemActions(resource, record, surface, hideFlows);
|
|
1416
|
+
const actions = useShownItemActions(resource, record, surface, hideFlows, sources);
|
|
1725
1417
|
if (actions.length === 0) return null;
|
|
1726
1418
|
const isRow = surface === "row";
|
|
1727
1419
|
const folded = isRow && (collapse ?? collapsedRows);
|
|
1728
1420
|
const standalone = folded ? [] : actions.filter((a) => a.placement === "standalone");
|
|
1729
1421
|
const menu = folded ? actions : actions.filter((a) => a.placement === "menu");
|
|
1730
|
-
const menuTestId = isRow ? tid.listRowActionsMenu(resource,
|
|
1422
|
+
const menuTestId = isRow ? tid.listRowActionsMenu(resource, recordKey) : tid.formActionsMenu(resource, surface);
|
|
1731
1423
|
const firstDestructive = menu.findIndex((a) => a.variant === "destructive");
|
|
1732
1424
|
const overlays = jsx(Fragment$1, { children: flowFor != null ? jsx(RuntimeFlowRunner, {
|
|
1733
1425
|
resource,
|
|
@@ -1741,6 +1433,7 @@ function ActionOverflow({ resource, record, surface, className, hideFlows, colla
|
|
|
1741
1433
|
actionName: a.name,
|
|
1742
1434
|
record,
|
|
1743
1435
|
scope: surface,
|
|
1436
|
+
...sources === void 0 ? {} : { sources },
|
|
1744
1437
|
density: a.density
|
|
1745
1438
|
}, a.name));
|
|
1746
1439
|
return jsxs("div", {
|
|
@@ -1764,7 +1457,7 @@ function ActionOverflow({ resource, record, surface, className, hideFlows, colla
|
|
|
1764
1457
|
size: "icon",
|
|
1765
1458
|
className: "size-7",
|
|
1766
1459
|
"data-testid": menuTestId,
|
|
1767
|
-
"aria-label":
|
|
1460
|
+
"aria-label": chromeT("actions.more", { defaultValue: "More" }),
|
|
1768
1461
|
children: jsx(IconDots, {
|
|
1769
1462
|
className: "h-4 w-4",
|
|
1770
1463
|
"aria-hidden": "true"
|
|
@@ -1778,15 +1471,16 @@ function ActionOverflow({ resource, record, surface, className, hideFlows, colla
|
|
|
1778
1471
|
children: [jsx(IconDots, {
|
|
1779
1472
|
className: "h-4 w-4",
|
|
1780
1473
|
"aria-hidden": "true"
|
|
1781
|
-
}),
|
|
1474
|
+
}), chromeT("actions.more", { defaultValue: "More" })]
|
|
1782
1475
|
})
|
|
1783
1476
|
}), jsx(DropdownMenuContent, {
|
|
1784
1477
|
align: "end",
|
|
1785
|
-
children: menu.map((a, index) => jsxs(Fragment, { children: [index === firstDestructive && index > 0 && jsx(DropdownMenuSeparator, { "data-testid": isRow ? `list-row-actions-separator:${resource}:${
|
|
1478
|
+
children: menu.map((a, index) => jsxs(Fragment, { children: [index === firstDestructive && index > 0 && jsx(DropdownMenuSeparator, { "data-testid": isRow ? `list-row-actions-separator:${resource}:${recordKey}` : `form-actions-separator:${resource}:${surface}` }), a.flow === void 0 ? jsx(ActionButton, {
|
|
1786
1479
|
resource,
|
|
1787
1480
|
actionName: a.name,
|
|
1788
1481
|
record,
|
|
1789
1482
|
scope: surface,
|
|
1483
|
+
...sources === void 0 ? {} : { sources },
|
|
1790
1484
|
asMenuItem: true
|
|
1791
1485
|
}) : jsx(FlowMenuItem, {
|
|
1792
1486
|
resource,
|
|
@@ -1806,11 +1500,12 @@ function ActionOverflow({ resource, record, surface, className, hideFlows, colla
|
|
|
1806
1500
|
]
|
|
1807
1501
|
});
|
|
1808
1502
|
}
|
|
1809
|
-
function useShownItemActions(resource, record, surface, hideFlows) {
|
|
1503
|
+
function useShownItemActions(resource, record, surface, hideFlows, sources) {
|
|
1810
1504
|
const resolved = useItemActions({
|
|
1811
1505
|
resource,
|
|
1812
1506
|
record,
|
|
1813
|
-
surface
|
|
1507
|
+
surface,
|
|
1508
|
+
...sources === void 0 ? {} : { sources }
|
|
1814
1509
|
});
|
|
1815
1510
|
const def = useApp().registry.get(resource);
|
|
1816
1511
|
const shown = def !== void 0 && isBuiltinItemAction((def.actions?.item)?.find((action) => action.name === "edit"), "edit") && !recordEntries(def).page.edit && recordEntries(def).quick.edit === null ? resolved.filter((a) => a.name !== "edit") : resolved;
|
|
@@ -1903,8 +1598,14 @@ function DocumentTitleProvider({ routeKey, children }) {
|
|
|
1903
1598
|
});
|
|
1904
1599
|
}
|
|
1905
1600
|
//#endregion
|
|
1601
|
+
//#region src/resource/resource-title.ts
|
|
1602
|
+
function resolveResourceTitle(caps, t, resource, slot, params) {
|
|
1603
|
+
return resolveContentSlot(caps, [`resources.${resource}.${slot}`], { params: { ...params } }) ?? t(`pageHeader.${slot}`, { params: { ...params } });
|
|
1604
|
+
}
|
|
1605
|
+
//#endregion
|
|
1906
1606
|
//#region src/resource/page-header.tsx
|
|
1907
|
-
function PageHeader({ resource, mode, recordName, backHref, backLabel, actions, title, breadcrumbs, backLink = false, visibleTitle = false, labelOverride, className }) {
|
|
1607
|
+
function PageHeader({ resource: resourceInput, mode, recordName, backHref, backLabel, actions, title, breadcrumbs, backLink = false, visibleTitle = false, labelOverride, className }) {
|
|
1608
|
+
const resource = routeResourceKey(resourceInput);
|
|
1908
1609
|
const app = useApp();
|
|
1909
1610
|
const t = useT();
|
|
1910
1611
|
const def = app.registry.get(resource);
|
|
@@ -1913,10 +1614,10 @@ function PageHeader({ resource, mode, recordName, backHref, backLabel, actions,
|
|
|
1913
1614
|
const resourceSingle = resolveResourceLabel(caps, resource, "single", def);
|
|
1914
1615
|
const labelSingle = labelOverride ?? resourceSingle;
|
|
1915
1616
|
const labelPlural = resolveResourceLabel(caps, resource, "plural", def);
|
|
1916
|
-
const computedTitle = title ?? (mode === "list" ? labelPlural : mode === "show" ? recordName ?? labelSingle : mode === "edit" ? recordName ? t
|
|
1617
|
+
const computedTitle = title ?? (mode === "list" ? labelPlural : mode === "show" ? recordName ?? labelSingle : mode === "edit" ? recordName ? resolveResourceTitle(caps, t, resource, "editTitleNamed", {
|
|
1917
1618
|
label: labelSingle,
|
|
1918
1619
|
name: recordName
|
|
1919
|
-
}
|
|
1620
|
+
}) : resolveResourceTitle(caps, t, resource, "editTitle", { label: labelSingle }) : resolveResourceTitle(caps, t, resource, "createTitle", { label: labelSingle }));
|
|
1920
1621
|
useDocumentTitle(app?.name ? `${computedTitle} · ${app.name}` : computedTitle);
|
|
1921
1622
|
const showBack = backLink && mode !== "list" && !breadcrumbs;
|
|
1922
1623
|
const computedBackHref = backHref ?? def?.basePath ?? `/${resource}`;
|
|
@@ -1947,9 +1648,10 @@ function PageHeader({ resource, mode, recordName, backHref, backLabel, actions,
|
|
|
1947
1648
|
]
|
|
1948
1649
|
});
|
|
1949
1650
|
}
|
|
1950
|
-
function ShowPageHeader({ resource, record, extraActions, breadcrumbs }) {
|
|
1651
|
+
function ShowPageHeader({ resource: resourceInput, record, extraActions, breadcrumbs }) {
|
|
1652
|
+
const resource = routeResourceKey(resourceInput);
|
|
1951
1653
|
const def = useApp().registry.get(resource);
|
|
1952
|
-
const recordName = useProjectedTitle(resource, record
|
|
1654
|
+
const recordName = useProjectedTitle(resource, recordKeyOf(def, record), record);
|
|
1953
1655
|
usePublishDocVariant(resource, record);
|
|
1954
1656
|
if (!def) return null;
|
|
1955
1657
|
return jsx(PageHeader, {
|
|
@@ -1964,16 +1666,18 @@ function ShowPageHeader({ resource, record, extraActions, breadcrumbs }) {
|
|
|
1964
1666
|
}), extraActions] })
|
|
1965
1667
|
});
|
|
1966
1668
|
}
|
|
1967
|
-
function EditPageHeader({ resource, record, backHref, breadcrumbs }) {
|
|
1669
|
+
function EditPageHeader({ resource: resourceInput, record, backHref, breadcrumbs }) {
|
|
1670
|
+
const resource = routeResourceKey(resourceInput);
|
|
1968
1671
|
const app = useApp();
|
|
1969
1672
|
const def = app.registry.get(resource);
|
|
1970
1673
|
const caps = useI18nCaps(resource);
|
|
1971
|
-
const
|
|
1674
|
+
const recordKey = recordKeyOf(def, record);
|
|
1675
|
+
const recordName = useProjectedTitle(resource, recordKey, record);
|
|
1972
1676
|
return jsx(PageHeader, {
|
|
1973
1677
|
resource,
|
|
1974
1678
|
mode: "edit",
|
|
1975
1679
|
recordName,
|
|
1976
|
-
backHref: editBackHref(app.registry, resource, String(
|
|
1680
|
+
backHref: editBackHref(app.registry, resource, String(recordKey ?? ""), backHref),
|
|
1977
1681
|
breadcrumbs,
|
|
1978
1682
|
backLabel: recordName ? `${resolveResourceLabel(caps, resource, "single", def)}: ${recordName}` : void 0,
|
|
1979
1683
|
actions: jsx(ActionOverflow, {
|
|
@@ -2173,7 +1877,8 @@ function ItemNode({ node, row, fallback }) {
|
|
|
2173
1877
|
const viewProps = {
|
|
2174
1878
|
props,
|
|
2175
1879
|
data: {},
|
|
2176
|
-
t: placement.viewProps?.t ?? ((name) => name)
|
|
1880
|
+
t: placement.viewProps?.t ?? ((name) => name),
|
|
1881
|
+
...placement.viewProps?.empty === void 0 ? {} : { empty: placement.viewProps.empty }
|
|
2177
1882
|
};
|
|
2178
1883
|
return jsx(View, { ...viewProps });
|
|
2179
1884
|
}
|
|
@@ -2210,15 +1915,7 @@ function nodeAttributes(node, kind = node.kind, tone) {
|
|
|
2210
1915
|
}
|
|
2211
1916
|
function useNodeText() {
|
|
2212
1917
|
const caps = useI18nCaps();
|
|
2213
|
-
return (slot) =>
|
|
2214
|
-
if (slot === void 0) return void 0;
|
|
2215
|
-
if (caps.has(slot.key)) return caps.t(slot.key);
|
|
2216
|
-
if (slot.entry !== void 0 && caps.has(slot.entry)) {
|
|
2217
|
-
const entry = caps.t(slot.entry);
|
|
2218
|
-
if (typeof entry === "string" && entry !== slot.entry) return entry;
|
|
2219
|
-
}
|
|
2220
|
-
return slot.default;
|
|
2221
|
-
};
|
|
1918
|
+
return (slot) => slot === void 0 ? void 0 : resolveTextSlot(caps, slot);
|
|
2222
1919
|
}
|
|
2223
1920
|
function NodeInfo({ node, name }) {
|
|
2224
1921
|
const { resource } = useScreenTree();
|
|
@@ -2263,6 +1960,7 @@ function BuroPanelFrame({ body, title, description, collapsible, open, onToggle,
|
|
|
2263
1960
|
const t = useOptionalT();
|
|
2264
1961
|
return jsxs("section", {
|
|
2265
1962
|
...nodeAttributes,
|
|
1963
|
+
...title === void 0 ? {} : { "aria-labelledby": titleId },
|
|
2266
1964
|
children: [
|
|
2267
1965
|
title === void 0 && info === null && !collapsible ? null : jsxs("div", {
|
|
2268
1966
|
"data-buro-region": "panel-title",
|
|
@@ -2322,7 +2020,6 @@ function BuroTabsFrame({ tabs, active, onSelect, info, nodeAttributes }) {
|
|
|
2322
2020
|
}), info]
|
|
2323
2021
|
}), tabs.map((tab) => jsx(TabsContent, {
|
|
2324
2022
|
value: tab.key,
|
|
2325
|
-
forceMount: true,
|
|
2326
2023
|
"data-buro-region": "tab-panel",
|
|
2327
2024
|
"data-buro-key": tab.key,
|
|
2328
2025
|
children: tab.panel
|
|
@@ -2410,17 +2107,17 @@ function BuroStepsFrame({ wizard, steps, goTo, back, next, headingRef, info, nod
|
|
|
2410
2107
|
//#region src/primitives/checkbox.tsx
|
|
2411
2108
|
const Checkbox = React.forwardRef(({ className, checked, ...props }, ref) => jsx(CheckboxPrimitive.Root, {
|
|
2412
2109
|
ref,
|
|
2413
|
-
className: cn("peer h-4 w-4 shrink-0 rounded-
|
|
2110
|
+
className: cn("peer inline-flex h-4 w-4 shrink-0 items-center justify-center align-middle rounded-[4px] border border-primary ring-offset-background focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground data-[state=indeterminate]:bg-primary data-[state=indeterminate]:text-primary-foreground", className),
|
|
2414
2111
|
checked,
|
|
2415
2112
|
...props,
|
|
2416
2113
|
children: jsx(CheckboxPrimitive.Indicator, {
|
|
2417
2114
|
className: cn("flex items-center justify-center text-current"),
|
|
2418
2115
|
children: checked === "indeterminate" ? jsx(IconMinus, {
|
|
2419
2116
|
"data-icon": "minus",
|
|
2420
|
-
className: "
|
|
2117
|
+
className: "size-3.5"
|
|
2421
2118
|
}) : jsx(IconCheck, {
|
|
2422
2119
|
"data-icon": "check",
|
|
2423
|
-
className: "
|
|
2120
|
+
className: "size-3.5"
|
|
2424
2121
|
})
|
|
2425
2122
|
})
|
|
2426
2123
|
}));
|
|
@@ -2445,6 +2142,17 @@ function useContainerNarrow(maxRem = 40) {
|
|
|
2445
2142
|
return [setElement, narrow];
|
|
2446
2143
|
}
|
|
2447
2144
|
//#endregion
|
|
2145
|
+
//#region src/data-table/fill-column.ts
|
|
2146
|
+
function fillColumnId(columns) {
|
|
2147
|
+
const eligible = columns.filter((c) => !c.pinned && !c.service);
|
|
2148
|
+
const declared = eligible.find((c) => c.fill);
|
|
2149
|
+
if (declared !== void 0) return declared.id;
|
|
2150
|
+
for (let i = eligible.length - 1; i >= 0; i--) {
|
|
2151
|
+
const c = eligible[i];
|
|
2152
|
+
if (c.resize && c.max === void 0) return c.id;
|
|
2153
|
+
}
|
|
2154
|
+
}
|
|
2155
|
+
//#endregion
|
|
2448
2156
|
//#region src/data-table/data-table.tsx
|
|
2449
2157
|
function pinEdgeClass(isLastLeft, isFirstRight) {
|
|
2450
2158
|
if (isLastLeft) return "after:pointer-events-none after:absolute after:inset-y-0 after:left-full after:z-10 after:w-1.5 after:bg-gradient-to-r after:from-overlay/[0.07] after:to-transparent dark:after:from-overlay/30";
|
|
@@ -2456,7 +2164,8 @@ function pinEdgeClassFor(column, overflowing) {
|
|
|
2456
2164
|
if (!pinned || !overflowing) return "";
|
|
2457
2165
|
return pinEdgeClass(pinned === "left" && column.getIsLastColumn("left"), pinned === "right" && column.getIsFirstColumn("right"));
|
|
2458
2166
|
}
|
|
2459
|
-
function getPinStyles(column, overflowing) {
|
|
2167
|
+
function getPinStyles(column, overflowing, fill = false) {
|
|
2168
|
+
if (fill) return {};
|
|
2460
2169
|
const explicitWidth = { width: `calc(var(--col-${column.id}-size) * 1px)` };
|
|
2461
2170
|
const pinned = column.getIsPinned();
|
|
2462
2171
|
if (!pinned || !overflowing) return explicitWidth;
|
|
@@ -2481,21 +2190,35 @@ function columnMetaNoRowNav(meta) {
|
|
|
2481
2190
|
function columnMetaNoClip(meta) {
|
|
2482
2191
|
return meta?.noClip === true;
|
|
2483
2192
|
}
|
|
2484
|
-
function
|
|
2485
|
-
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
|
|
2193
|
+
function columnMetaWrap(meta) {
|
|
2194
|
+
return meta?.wrap === true;
|
|
2195
|
+
}
|
|
2196
|
+
function columnMetaFill(meta) {
|
|
2197
|
+
return meta?.fill === true;
|
|
2198
|
+
}
|
|
2199
|
+
function tableFillColumnId(table) {
|
|
2200
|
+
if (table.options.enableColumnResizing !== true) return void 0;
|
|
2201
|
+
return fillColumnId(table.getVisibleLeafColumns().map((c) => {
|
|
2202
|
+
const max = c.columnDef.maxSize;
|
|
2203
|
+
return {
|
|
2204
|
+
id: c.id,
|
|
2205
|
+
fill: columnMetaFill(c.columnDef.meta),
|
|
2206
|
+
resize: c.getCanResize(),
|
|
2207
|
+
...max !== void 0 && max < Number.MAX_SAFE_INTEGER ? { max } : {},
|
|
2208
|
+
pinned: c.getIsPinned() !== false,
|
|
2209
|
+
service: c.id.startsWith("__")
|
|
2210
|
+
};
|
|
2211
|
+
}));
|
|
2489
2212
|
}
|
|
2490
|
-
function renderCellMarkup({ cell, resizable, overflowing, testId, onCellClick, noRowNav }) {
|
|
2213
|
+
function renderCellMarkup({ cell, resizable, overflowing, fill, testId, onCellClick, noRowNav }) {
|
|
2491
2214
|
const liveColumn = cell.getContext().table.getColumn(cell.column.id) ?? cell.column;
|
|
2492
2215
|
const pinEdge = pinEdgeClassFor(liveColumn, overflowing);
|
|
2493
|
-
const noClip = columnMetaNoClip(cell.column.columnDef.meta);
|
|
2216
|
+
const noClip = columnMetaNoClip(cell.column.columnDef.meta) || columnMetaWrap(cell.column.columnDef.meta);
|
|
2494
2217
|
return jsx(TableCell, {
|
|
2495
2218
|
"data-testid": testId,
|
|
2496
2219
|
"data-no-row-nav": columnMetaNoRowNav(cell.column.columnDef.meta) || noRowNav ? "" : void 0,
|
|
2497
2220
|
onClick: onCellClick,
|
|
2498
|
-
style: getPinStyles(liveColumn, overflowing),
|
|
2221
|
+
style: getPinStyles(liveColumn, overflowing, fill),
|
|
2499
2222
|
className: cn(resizable && !pinEdge && !noClip && "overflow-hidden", pinEdge && "relative", pinEdge, columnMetaClassName(cell.column.columnDef.meta), onCellClick && "cursor-pointer"),
|
|
2500
2223
|
children: resizable && !noClip ? jsx("div", {
|
|
2501
2224
|
className: "truncate",
|
|
@@ -2507,7 +2230,7 @@ function renderCellMarkup({ cell, resizable, overflowing, testId, onCellClick, n
|
|
|
2507
2230
|
function MemoCellInner(props) {
|
|
2508
2231
|
return renderCellMarkup(props);
|
|
2509
2232
|
}
|
|
2510
|
-
const MemoCell = memo(MemoCellInner, (prev, next) => prev.rowOriginal === next.rowOriginal && prev.cell.column.id === next.cell.column.id && prev.resizable === next.resizable && (!prev.pinned && !next.pinned || prev.overflowing === next.overflowing) && prev.pinned === next.pinned && prev.testId === next.testId);
|
|
2233
|
+
const MemoCell = memo(MemoCellInner, (prev, next) => prev.rowOriginal === next.rowOriginal && prev.cell.column.id === next.cell.column.id && prev.resizable === next.resizable && (!prev.pinned && !next.pinned || prev.overflowing === next.overflowing) && prev.pinned === next.pinned && prev.fill === next.fill && prev.testId === next.testId);
|
|
2511
2234
|
function SelectionCellInner(props) {
|
|
2512
2235
|
const row = props.cell.row;
|
|
2513
2236
|
const toggle = (e) => {
|
|
@@ -2521,7 +2244,7 @@ function SelectionCellInner(props) {
|
|
|
2521
2244
|
});
|
|
2522
2245
|
}
|
|
2523
2246
|
const SelectionCell = memo(SelectionCellInner);
|
|
2524
|
-
function DataRowInner({ row, isSelected, resizable, overflowing, selectionColumnId, getCellTestId, getRowTestId, rowHref, onRowOpen, activateRow, rowClassName }) {
|
|
2247
|
+
function DataRowInner({ row, isSelected, resizable, overflowing, fillColumnId, selectionColumnId, getCellTestId, getRowTestId, rowHref, onRowOpen, activateRow, rowClassName }) {
|
|
2525
2248
|
const clickable = rowHref !== void 0 || onRowOpen !== void 0;
|
|
2526
2249
|
return jsx(TableRow, {
|
|
2527
2250
|
"data-state": isSelected ? "selected" : void 0,
|
|
@@ -2534,6 +2257,7 @@ function DataRowInner({ row, isSelected, resizable, overflowing, selectionColumn
|
|
|
2534
2257
|
isSelected,
|
|
2535
2258
|
resizable,
|
|
2536
2259
|
overflowing,
|
|
2260
|
+
fill: cell.column.id === fillColumnId,
|
|
2537
2261
|
pinned: cell.getContext().table.getColumn(cell.column.id)?.getIsPinned() ?? false,
|
|
2538
2262
|
testId: getCellTestId?.(row.original, cell.column.id)
|
|
2539
2263
|
}, cell.id) : jsx(MemoCell, {
|
|
@@ -2541,13 +2265,14 @@ function DataRowInner({ row, isSelected, resizable, overflowing, selectionColumn
|
|
|
2541
2265
|
rowOriginal: row.original,
|
|
2542
2266
|
resizable,
|
|
2543
2267
|
overflowing,
|
|
2268
|
+
fill: cell.column.id === fillColumnId,
|
|
2544
2269
|
pinned: cell.getContext().table.getColumn(cell.column.id)?.getIsPinned() ?? false,
|
|
2545
2270
|
testId: getCellTestId?.(row.original, cell.column.id)
|
|
2546
2271
|
}, cell.id))
|
|
2547
2272
|
});
|
|
2548
2273
|
}
|
|
2549
|
-
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);
|
|
2550
|
-
function DataTableBodyInner({ rows, isLoading, loadingRowCount, visibleColumnCount, emptyState, emptyText, resizable, overflowing, columnLayoutKey, selectionColumnId, getCellTestId, getRowTestId, rowHref, onRowOpen, activateRow, rowClassName }) {
|
|
2274
|
+
const MemoRow = memo(DataRowInner, (prev, next) => prev.row.original === next.row.original && prev.row.id === next.row.id && prev.isSelected === next.isSelected && prev.resizable === next.resizable && prev.overflowing === next.overflowing && prev.columnLayoutKey === next.columnLayoutKey && prev.fillColumnId === next.fillColumnId && prev.selectionColumnId === next.selectionColumnId && prev.getCellTestId === next.getCellTestId && prev.getRowTestId === next.getRowTestId && prev.rowHref === next.rowHref && prev.onRowOpen === next.onRowOpen && prev.activateRow === next.activateRow && prev.rowClassName === next.rowClassName);
|
|
2275
|
+
function DataTableBodyInner({ rows, isLoading, loadingRowCount, visibleColumnCount, emptyState, emptyText, resizable, overflowing, columnLayoutKey, fillColumnId, selectionColumnId, getCellTestId, getRowTestId, rowHref, onRowOpen, activateRow, rowClassName }) {
|
|
2551
2276
|
return jsx(TableBody, { children: isLoading ? Array.from({ length: loadingRowCount }).map((_, i) => jsx(TableRow, {
|
|
2552
2277
|
"aria-busy": "true",
|
|
2553
2278
|
children: Array.from({ length: Math.max(1, visibleColumnCount) }).map((__, j) => jsx(TableCell, { children: jsx("div", { className: "h-4 w-3/4 animate-pulse rounded bg-muted" }) }, `skeleton-${i}-${j}`))
|
|
@@ -2564,6 +2289,7 @@ function DataTableBodyInner({ rows, isLoading, loadingRowCount, visibleColumnCou
|
|
|
2564
2289
|
resizable,
|
|
2565
2290
|
overflowing,
|
|
2566
2291
|
columnLayoutKey,
|
|
2292
|
+
fillColumnId,
|
|
2567
2293
|
selectionColumnId,
|
|
2568
2294
|
getCellTestId,
|
|
2569
2295
|
getRowTestId,
|
|
@@ -2575,7 +2301,7 @@ function DataTableBodyInner({ rows, isLoading, loadingRowCount, visibleColumnCou
|
|
|
2575
2301
|
}
|
|
2576
2302
|
const DataTableBody = memo(DataTableBodyInner, (prev, next) => {
|
|
2577
2303
|
if (!next.isResizing) return false;
|
|
2578
|
-
return prev.rows === next.rows && prev.overflowing === next.overflowing && prev.isLoading === next.isLoading && prev.columnLayoutKey === next.columnLayoutKey && prev.selectionColumnId === next.selectionColumnId;
|
|
2304
|
+
return prev.rows === next.rows && prev.overflowing === next.overflowing && prev.isLoading === next.isLoading && prev.columnLayoutKey === next.columnLayoutKey && prev.fillColumnId === next.fillColumnId && prev.selectionColumnId === next.selectionColumnId;
|
|
2579
2305
|
});
|
|
2580
2306
|
function DataTable({ table, tableTestId, getRowTestId, getCellTestId, isLoading = false, loadingRowCount = 5, emptyState, errorState, rowClassName, ariaLabel, onColumnReorder, rowHref, onRowOpen, selectionColumnId, className }) {
|
|
2581
2307
|
const t = useOptionalT();
|
|
@@ -2605,8 +2331,10 @@ function DataTable({ table, tableTestId, getRowTestId, getCellTestId, isLoading
|
|
|
2605
2331
|
const rows = table.getRowModel().rows;
|
|
2606
2332
|
const headerGroups = table.getHeaderGroups();
|
|
2607
2333
|
const visibleColumnCount = table.getVisibleLeafColumns().length;
|
|
2334
|
+
const fillId = tableFillColumnId(table);
|
|
2608
2335
|
const columnLayoutKey = table.getVisibleLeafColumns().map((c) => `${c.id}:${c.getIsPinned() || ""}`).join("|");
|
|
2609
2336
|
const resizable = table.options.enableColumnResizing === true;
|
|
2337
|
+
const widthSum = resizable ? table.getVisibleLeafColumns().reduce((sum, c) => sum + c.getSize(), 0) : 0;
|
|
2610
2338
|
const hasPinned = table.getState().columnPinning != null && (table.getState().columnPinning.left?.length || table.getState().columnPinning.right?.length);
|
|
2611
2339
|
const sizingState = table.getState().columnSizing;
|
|
2612
2340
|
const sizingInfo = table.getState().columnSizingInfo;
|
|
@@ -2698,7 +2426,7 @@ function DataTable({ table, tableTestId, getRowTestId, getCellTestId, isLoading
|
|
|
2698
2426
|
className,
|
|
2699
2427
|
style: resizable ? {
|
|
2700
2428
|
...columnSizeVars,
|
|
2701
|
-
|
|
2429
|
+
width: fillId !== void 0 ? `max(100%, ${widthSum}px)` : `${widthSum}px`,
|
|
2702
2430
|
tableLayout: "fixed"
|
|
2703
2431
|
} : void 0,
|
|
2704
2432
|
children: [jsx(TableHeader, { children: headerGroups.map((hg) => jsx(TableRow, { children: hg.headers.map((header) => {
|
|
@@ -2709,7 +2437,7 @@ function DataTable({ table, tableTestId, getRowTestId, getCellTestId, isLoading
|
|
|
2709
2437
|
return jsxs(TableHead, {
|
|
2710
2438
|
colSpan: header.colSpan,
|
|
2711
2439
|
"aria-sort": ariaSort,
|
|
2712
|
-
style: getPinStyles(header.column, overflowing),
|
|
2440
|
+
style: getPinStyles(header.column, overflowing, header.column.id === fillId),
|
|
2713
2441
|
className: cn("relative", resizable && !columnMetaNoClip(header.column.columnDef.meta) && (pinEdgeClassFor(header.column, overflowing) ? "[&>*:not([data-testid^=col-resize])]:truncate" : "overflow-hidden [&>*:not([data-testid^=col-resize])]:truncate"), canReorder && "cursor-grab", pinEdgeClassFor(header.column, overflowing), columnMetaClassName(header.column.columnDef.meta)),
|
|
2714
2442
|
draggable: canReorder || void 0,
|
|
2715
2443
|
onDragStart: canReorder ? (e) => {
|
|
@@ -2768,6 +2496,7 @@ function DataTable({ table, tableTestId, getRowTestId, getCellTestId, isLoading
|
|
|
2768
2496
|
resizable,
|
|
2769
2497
|
overflowing,
|
|
2770
2498
|
columnLayoutKey,
|
|
2499
|
+
fillColumnId: fillId,
|
|
2771
2500
|
selectionColumnId,
|
|
2772
2501
|
isResizing: Boolean(sizingInfo.isResizingColumn),
|
|
2773
2502
|
getCellTestId,
|
|
@@ -2820,69 +2549,103 @@ function ColumnHeaderMenu({ label, sortDirection, sortable, onSort, pinned, onPi
|
|
|
2820
2549
|
sortable && onSort && jsxs(DropdownMenuGroup, { children: [
|
|
2821
2550
|
jsx(DropdownMenuLabel, {
|
|
2822
2551
|
className: "text-xs font-medium text-muted-foreground",
|
|
2823
|
-
children: tr("columnMenu.sortSection", "
|
|
2552
|
+
children: tr("columnMenu.sortSection", "Sort")
|
|
2824
2553
|
}),
|
|
2825
2554
|
jsxs(DropdownMenuItem, {
|
|
2826
2555
|
onClick: () => onSort("asc"),
|
|
2827
2556
|
"data-testid": testid ? `${testid}:sort-asc` : void 0,
|
|
2828
|
-
children: [jsx(IconArrowUp, { className: "mr-2 size-4 text-muted-foreground" }), tr("columnMenu.sortAsc", "
|
|
2557
|
+
children: [jsx(IconArrowUp, { className: "mr-2 size-4 text-muted-foreground" }), tr("columnMenu.sortAsc", "Ascending")]
|
|
2829
2558
|
}),
|
|
2830
2559
|
jsxs(DropdownMenuItem, {
|
|
2831
2560
|
onClick: () => onSort("desc"),
|
|
2832
2561
|
"data-testid": testid ? `${testid}:sort-desc` : void 0,
|
|
2833
|
-
children: [jsx(IconArrowDown, { className: "mr-2 size-4 text-muted-foreground" }), tr("columnMenu.sortDesc", "
|
|
2562
|
+
children: [jsx(IconArrowDown, { className: "mr-2 size-4 text-muted-foreground" }), tr("columnMenu.sortDesc", "Descending")]
|
|
2834
2563
|
}),
|
|
2835
2564
|
sortDirection && jsxs(DropdownMenuItem, {
|
|
2836
2565
|
onClick: () => onSort(null),
|
|
2837
|
-
children: [jsx(IconX, { className: "mr-2 size-4 text-muted-foreground" }), tr("columnMenu.clearSort", "
|
|
2566
|
+
children: [jsx(IconX, { className: "mr-2 size-4 text-muted-foreground" }), tr("columnMenu.clearSort", "Clear")]
|
|
2838
2567
|
})
|
|
2839
2568
|
] }),
|
|
2840
2569
|
onPin && jsxs(Fragment$1, { children: [sortable && onSort && jsx(DropdownMenuSeparator, {}), jsxs(DropdownMenuGroup, { children: [
|
|
2841
2570
|
jsx(DropdownMenuLabel, {
|
|
2842
2571
|
className: "text-xs font-medium text-muted-foreground",
|
|
2843
|
-
children: tr("columnMenu.pinSection", "
|
|
2572
|
+
children: tr("columnMenu.pinSection", "Pin")
|
|
2844
2573
|
}),
|
|
2845
2574
|
pinned !== "left" && jsxs(DropdownMenuItem, {
|
|
2846
2575
|
onClick: () => onPin("left"),
|
|
2847
2576
|
"data-testid": testid ? `${testid}:pin-left` : void 0,
|
|
2848
|
-
children: [jsx(IconPinned, { className: "mr-2 size-4 text-muted-foreground" }), tr("columnMenu.pinLeft", "
|
|
2577
|
+
children: [jsx(IconPinned, { className: "mr-2 size-4 text-muted-foreground" }), tr("columnMenu.pinLeft", "Left")]
|
|
2849
2578
|
}),
|
|
2850
2579
|
pinned !== "right" && jsxs(DropdownMenuItem, {
|
|
2851
2580
|
onClick: () => onPin("right"),
|
|
2852
|
-
children: [jsx(IconPinned, { className: "mr-2 size-4 -scale-x-100 text-muted-foreground" }), tr("columnMenu.pinRight", "
|
|
2581
|
+
children: [jsx(IconPinned, { className: "mr-2 size-4 -scale-x-100 text-muted-foreground" }), tr("columnMenu.pinRight", "Right")]
|
|
2853
2582
|
}),
|
|
2854
2583
|
pinned !== false && jsxs(DropdownMenuItem, {
|
|
2855
2584
|
onClick: () => onPin(false),
|
|
2856
2585
|
"data-testid": testid ? `${testid}:unpin` : void 0,
|
|
2857
|
-
children: [jsx(IconPinnedOff, { className: "mr-2 size-4 text-muted-foreground" }), tr("columnMenu.unpin", "
|
|
2586
|
+
children: [jsx(IconPinnedOff, { className: "mr-2 size-4 text-muted-foreground" }), tr("columnMenu.unpin", "Unpin")]
|
|
2858
2587
|
})
|
|
2859
2588
|
] })] }),
|
|
2860
2589
|
canMove && jsxs(Fragment$1, { children: [jsx(DropdownMenuSeparator, {}), jsxs(DropdownMenuGroup, { children: [
|
|
2861
2590
|
jsx(DropdownMenuLabel, {
|
|
2862
2591
|
className: "text-xs font-medium text-muted-foreground",
|
|
2863
|
-
children: tr("columnMenu.moveSection", "
|
|
2592
|
+
children: tr("columnMenu.moveSection", "Move")
|
|
2864
2593
|
}),
|
|
2865
2594
|
canMoveLeft && jsxs(DropdownMenuItem, {
|
|
2866
2595
|
onClick: () => onMove?.("left"),
|
|
2867
2596
|
"data-testid": testid ? `${testid}:move-left` : void 0,
|
|
2868
|
-
children: [jsx(IconArrowLeft, { className: "mr-2 size-4 text-muted-foreground" }), tr("columnMenu.moveLeft", "
|
|
2597
|
+
children: [jsx(IconArrowLeft, { className: "mr-2 size-4 text-muted-foreground" }), tr("columnMenu.moveLeft", "Left")]
|
|
2869
2598
|
}),
|
|
2870
2599
|
canMoveRight && jsxs(DropdownMenuItem, {
|
|
2871
2600
|
onClick: () => onMove?.("right"),
|
|
2872
2601
|
"data-testid": testid ? `${testid}:move-right` : void 0,
|
|
2873
|
-
children: [jsx(IconArrowRight, { className: "mr-2 size-4 text-muted-foreground" }), tr("columnMenu.moveRight", "
|
|
2602
|
+
children: [jsx(IconArrowRight, { className: "mr-2 size-4 text-muted-foreground" }), tr("columnMenu.moveRight", "Right")]
|
|
2874
2603
|
})
|
|
2875
2604
|
] })] }),
|
|
2876
2605
|
canHide && onHide && jsxs(Fragment$1, { children: [jsx(DropdownMenuSeparator, {}), jsxs(DropdownMenuItem, {
|
|
2877
2606
|
onClick: onHide,
|
|
2878
2607
|
"data-testid": testid ? `${testid}:hide` : void 0,
|
|
2879
|
-
children: [jsx(IconEyeOff, { className: "mr-2 size-4 text-muted-foreground" }), tr("columnMenu.hide", "
|
|
2608
|
+
children: [jsx(IconEyeOff, { className: "mr-2 size-4 text-muted-foreground" }), tr("columnMenu.hide", "Hide column")]
|
|
2880
2609
|
})] })
|
|
2881
2610
|
]
|
|
2882
2611
|
})]
|
|
2883
2612
|
});
|
|
2884
2613
|
}
|
|
2885
2614
|
//#endregion
|
|
2615
|
+
//#region src/resource/column-accessor.ts
|
|
2616
|
+
function columnAccessor(column) {
|
|
2617
|
+
return column.includes(".") ? { accessorFn: (row) => row[column] } : { accessorKey: column };
|
|
2618
|
+
}
|
|
2619
|
+
//#endregion
|
|
2620
|
+
//#region src/resource/column-target.ts
|
|
2621
|
+
function columnTarget(registry, resource, column) {
|
|
2622
|
+
if (!column.includes(".")) return void 0;
|
|
2623
|
+
const resolved = resolveColumnField(registry, resource, column);
|
|
2624
|
+
if (resolved === void 0) return void 0;
|
|
2625
|
+
const ref = registry.get(resolved.resource)?.ref ?? {
|
|
2626
|
+
namespace: "default",
|
|
2627
|
+
id: resolved.resource
|
|
2628
|
+
};
|
|
2629
|
+
return {
|
|
2630
|
+
field: resolved.field,
|
|
2631
|
+
name: column.slice(column.lastIndexOf(".") + 1),
|
|
2632
|
+
resource: resolved.resource,
|
|
2633
|
+
ref
|
|
2634
|
+
};
|
|
2635
|
+
}
|
|
2636
|
+
function capsOf(caps, resource) {
|
|
2637
|
+
return {
|
|
2638
|
+
has: (key, options) => caps.has(key, {
|
|
2639
|
+
...options,
|
|
2640
|
+
resource: options?.resource ?? resource
|
|
2641
|
+
}),
|
|
2642
|
+
t: (key, options) => caps.t(key, {
|
|
2643
|
+
...options,
|
|
2644
|
+
resource: options?.resource ?? resource
|
|
2645
|
+
})
|
|
2646
|
+
};
|
|
2647
|
+
}
|
|
2648
|
+
//#endregion
|
|
2886
2649
|
//#region src/data-table/use-buro-data-table.ts
|
|
2887
2650
|
function useBuroDataTable(opts) {
|
|
2888
2651
|
const { data, columns, total, getRowId, pagination, onPaginationChange, sorting, onSortingChange, columnFilters, onColumnFiltersChange, columnVisibility, onColumnVisibilityChange, rowSelection, onRowSelectionChange, enableRowSelection, columnPinning, onColumnPinningChange, columnSizing, onColumnSizingChange, columnOrder, onColumnOrderChange, enableColumnResizing } = opts;
|
|
@@ -2957,64 +2720,247 @@ function resolveUpdater(updater, prev) {
|
|
|
2957
2720
|
}
|
|
2958
2721
|
//#endregion
|
|
2959
2722
|
//#region src/resource/list-error-state.tsx
|
|
2960
|
-
function ListErrorState(
|
|
2961
|
-
const
|
|
2962
|
-
|
|
2963
|
-
|
|
2964
|
-
|
|
2965
|
-
|
|
2966
|
-
|
|
2967
|
-
|
|
2968
|
-
className
|
|
2969
|
-
"data-testid": "error",
|
|
2970
|
-
children: [
|
|
2971
|
-
jsx("div", {
|
|
2972
|
-
className: "rounded-full bg-destructive/10 p-3 text-destructive",
|
|
2973
|
-
children: jsx(IconAlertCircle, {
|
|
2974
|
-
className: "size-6",
|
|
2975
|
-
"aria-hidden": "true"
|
|
2976
|
-
})
|
|
2977
|
-
}),
|
|
2978
|
-
jsxs("div", {
|
|
2979
|
-
className: "space-y-1",
|
|
2980
|
-
children: [jsx("h3", {
|
|
2981
|
-
className: "text-base font-semibold tracking-tight text-destructive",
|
|
2982
|
-
children: title ?? t("list.errorTitle")
|
|
2983
|
-
}), jsx("p", {
|
|
2984
|
-
className: "max-w-md text-sm text-muted-foreground",
|
|
2985
|
-
children: friendlyError || t("list.errorBody")
|
|
2986
|
-
})]
|
|
2987
|
-
}),
|
|
2988
|
-
raw && raw !== friendlyError ? jsxs("details", {
|
|
2989
|
-
className: "max-w-md text-left",
|
|
2990
|
-
children: [jsx("summary", {
|
|
2991
|
-
className: "cursor-pointer text-xs text-muted-foreground hover:text-foreground",
|
|
2992
|
-
children: t("list.errorDetails")
|
|
2993
|
-
}), jsx("p", {
|
|
2994
|
-
className: "mt-1.5 break-all font-mono text-xs text-muted-foreground",
|
|
2995
|
-
"data-testid": "list-error-detail",
|
|
2996
|
-
children: raw
|
|
2997
|
-
})]
|
|
2998
|
-
}) : null,
|
|
2999
|
-
onRetry && jsx("div", {
|
|
3000
|
-
className: "flex justify-center pt-2",
|
|
3001
|
-
children: jsxs(Button, {
|
|
3002
|
-
type: "button",
|
|
3003
|
-
variant: "outline",
|
|
3004
|
-
size: "sm",
|
|
3005
|
-
"data-testid": resource ? `list-error-retry:${resource}` : "list-error-retry",
|
|
3006
|
-
onClick: onRetry,
|
|
3007
|
-
className: "gap-1.5",
|
|
3008
|
-
children: [jsx(IconRefresh, {
|
|
3009
|
-
className: "size-3.5",
|
|
3010
|
-
"aria-hidden": "true"
|
|
3011
|
-
}), t("actions.retry")]
|
|
3012
|
-
})
|
|
3013
|
-
})
|
|
3014
|
-
]
|
|
2723
|
+
function ListErrorState(props) {
|
|
2724
|
+
const { error, onRetry, resource, title, className } = props;
|
|
2725
|
+
return jsx(LoadError, {
|
|
2726
|
+
surface: "list",
|
|
2727
|
+
error,
|
|
2728
|
+
...onRetry === void 0 ? {} : { retry: onRetry },
|
|
2729
|
+
...resource === void 0 ? {} : { resource },
|
|
2730
|
+
...title === void 0 ? {} : { title },
|
|
2731
|
+
...className === void 0 ? {} : { className }
|
|
3015
2732
|
});
|
|
3016
2733
|
}
|
|
3017
2734
|
//#endregion
|
|
2735
|
+
//#region src/resource/use-table-layout.ts
|
|
2736
|
+
const WRITE_DEBOUNCE_MS = 200;
|
|
2737
|
+
function mutate(run, ok, then) {
|
|
2738
|
+
try {
|
|
2739
|
+
run().then((result) => {
|
|
2740
|
+
if (ok(result.status)) then();
|
|
2741
|
+
}, () => void 0);
|
|
2742
|
+
} catch {}
|
|
2743
|
+
}
|
|
2744
|
+
function resolve(updater, current) {
|
|
2745
|
+
return typeof updater === "function" ? updater(current) : updater;
|
|
2746
|
+
}
|
|
2747
|
+
function sameNames(a, b) {
|
|
2748
|
+
return a.length === b.length && a.every((name, index) => name === b[index]);
|
|
2749
|
+
}
|
|
2750
|
+
function readSizes(sizes, rule) {
|
|
2751
|
+
const out = {};
|
|
2752
|
+
for (const [column, size] of Object.entries(sizes)) {
|
|
2753
|
+
const of = rule(column);
|
|
2754
|
+
if (of === void 0 || !of.resize) continue;
|
|
2755
|
+
out[column] = Math.min(Math.max(size, of.min), of.max ?? Number.POSITIVE_INFINITY);
|
|
2756
|
+
}
|
|
2757
|
+
return out;
|
|
2758
|
+
}
|
|
2759
|
+
function useTableLayout({ resource, layoutId, rule, defaultPinning, resetLayout }) {
|
|
2760
|
+
const params = useMemo(() => layoutId === void 0 ? null : {
|
|
2761
|
+
resource,
|
|
2762
|
+
list: layoutId
|
|
2763
|
+
}, [resource, layoutId]);
|
|
2764
|
+
const settings = useOptionalUiSettings(tableLayoutSetting, params);
|
|
2765
|
+
const settingsRef = useRef(settings);
|
|
2766
|
+
settingsRef.current = settings;
|
|
2767
|
+
const key = layoutId === void 0 ? void 0 : `${resource}\u0000${layoutId}`;
|
|
2768
|
+
const fallback = useMemo(() => ({
|
|
2769
|
+
sizes: {},
|
|
2770
|
+
order: [],
|
|
2771
|
+
pinning: defaultPinning
|
|
2772
|
+
}), [defaultPinning]);
|
|
2773
|
+
const [local, setLocal] = useState();
|
|
2774
|
+
const stored = settings?.snapshot.entries[0]?.value;
|
|
2775
|
+
const settled = settings === void 0 || !settings.snapshot.pending;
|
|
2776
|
+
const held = useRef(null);
|
|
2777
|
+
const layout = (local !== void 0 && local.key === key ? local.value : void 0) ?? stored ?? fallback;
|
|
2778
|
+
const latest = useRef(layout);
|
|
2779
|
+
latest.current = layout;
|
|
2780
|
+
const sizing = useMemo(() => readSizes(layout.sizes, rule), [layout.sizes, rule]);
|
|
2781
|
+
const sizingRef = useRef(sizing);
|
|
2782
|
+
sizingRef.current = sizing;
|
|
2783
|
+
const generation = useRef(0);
|
|
2784
|
+
const timer = useRef(null);
|
|
2785
|
+
const pending = useRef(null);
|
|
2786
|
+
const confirm = useCallback((sent) => {
|
|
2787
|
+
if (generation.current === sent && pending.current === null) setLocal(void 0);
|
|
2788
|
+
}, []);
|
|
2789
|
+
const write = useCallback((target, value) => {
|
|
2790
|
+
const scopeId = target.snapshot.entries[0]?.scope.id ?? target.snapshot.writeScopes[0]?.id;
|
|
2791
|
+
if (scopeId === void 0) return;
|
|
2792
|
+
const sent = generation.current;
|
|
2793
|
+
mutate(() => target.set({
|
|
2794
|
+
scopeId,
|
|
2795
|
+
value
|
|
2796
|
+
}), (status) => status === "created" || status === "updated", () => confirm(sent));
|
|
2797
|
+
}, [confirm]);
|
|
2798
|
+
const store = useCallback((target, next) => {
|
|
2799
|
+
if (!(Object.keys(next.sizes).length === 0 && next.order.length === 0 && sameNames(next.pinning.left, defaultPinning.left) && sameNames(next.pinning.right, defaultPinning.right))) {
|
|
2800
|
+
write(target, next);
|
|
2801
|
+
return;
|
|
2802
|
+
}
|
|
2803
|
+
const entry = target.snapshot.entries[0];
|
|
2804
|
+
if (entry === void 0) {
|
|
2805
|
+
setLocal(void 0);
|
|
2806
|
+
return;
|
|
2807
|
+
}
|
|
2808
|
+
const sent = generation.current;
|
|
2809
|
+
mutate(() => target.remove({ id: entry.id }), (status) => status === "removed", () => confirm(sent));
|
|
2810
|
+
}, [
|
|
2811
|
+
defaultPinning,
|
|
2812
|
+
write,
|
|
2813
|
+
confirm
|
|
2814
|
+
]);
|
|
2815
|
+
const flush = useCallback(() => {
|
|
2816
|
+
if (timer.current !== null) clearTimeout(timer.current);
|
|
2817
|
+
timer.current = null;
|
|
2818
|
+
const due = pending.current;
|
|
2819
|
+
pending.current = null;
|
|
2820
|
+
if (due !== null) write(due.target, due.value);
|
|
2821
|
+
}, [write]);
|
|
2822
|
+
const change = useCallback((next, debounced, touch) => {
|
|
2823
|
+
latest.current = next;
|
|
2824
|
+
generation.current += 1;
|
|
2825
|
+
setLocal({
|
|
2826
|
+
key,
|
|
2827
|
+
value: next
|
|
2828
|
+
});
|
|
2829
|
+
const target = settingsRef.current;
|
|
2830
|
+
if (target === void 0) return;
|
|
2831
|
+
if (target.snapshot.pending || held.current !== null && held.current.key === key) {
|
|
2832
|
+
const edits = held.current !== null && held.current.key === key ? held.current : {
|
|
2833
|
+
key,
|
|
2834
|
+
resetAll: false,
|
|
2835
|
+
resetSizes: false,
|
|
2836
|
+
sizes: new Map()
|
|
2837
|
+
};
|
|
2838
|
+
held.current = edits;
|
|
2839
|
+
touch(edits);
|
|
2840
|
+
return;
|
|
2841
|
+
}
|
|
2842
|
+
pending.current = {
|
|
2843
|
+
target,
|
|
2844
|
+
value: next
|
|
2845
|
+
};
|
|
2846
|
+
if (!debounced) {
|
|
2847
|
+
flush();
|
|
2848
|
+
return;
|
|
2849
|
+
}
|
|
2850
|
+
if (timer.current !== null) clearTimeout(timer.current);
|
|
2851
|
+
timer.current = setTimeout(flush, WRITE_DEBOUNCE_MS);
|
|
2852
|
+
}, [key, flush]);
|
|
2853
|
+
useEffect(() => flush, [key]);
|
|
2854
|
+
useEffect(() => {
|
|
2855
|
+
const edits = held.current;
|
|
2856
|
+
if (edits === null) return;
|
|
2857
|
+
if (edits.key !== key) {
|
|
2858
|
+
held.current = null;
|
|
2859
|
+
return;
|
|
2860
|
+
}
|
|
2861
|
+
const target = settingsRef.current;
|
|
2862
|
+
if (!settled || target === void 0) return;
|
|
2863
|
+
held.current = null;
|
|
2864
|
+
const base = edits.resetAll ? fallback : stored ?? fallback;
|
|
2865
|
+
const sizes = edits.resetAll || edits.resetSizes ? {} : { ...readSizes(base.sizes, rule) };
|
|
2866
|
+
for (const [column, size] of edits.sizes) if (size === void 0) delete sizes[column];
|
|
2867
|
+
else sizes[column] = size;
|
|
2868
|
+
const next = {
|
|
2869
|
+
sizes,
|
|
2870
|
+
order: edits.order ?? base.order,
|
|
2871
|
+
pinning: edits.pinning ?? base.pinning
|
|
2872
|
+
};
|
|
2873
|
+
latest.current = next;
|
|
2874
|
+
generation.current += 1;
|
|
2875
|
+
setLocal({
|
|
2876
|
+
key,
|
|
2877
|
+
value: next
|
|
2878
|
+
});
|
|
2879
|
+
store(target, next);
|
|
2880
|
+
}, [settled, key]);
|
|
2881
|
+
const resetToken = resetLayout?.token ?? 0;
|
|
2882
|
+
const seenToken = useRef(resetToken);
|
|
2883
|
+
useEffect(() => {
|
|
2884
|
+
if (resetToken === seenToken.current) return;
|
|
2885
|
+
seenToken.current = resetToken;
|
|
2886
|
+
const all = resetLayout?.mode === "all";
|
|
2887
|
+
const next = all ? fallback : {
|
|
2888
|
+
...latest.current,
|
|
2889
|
+
sizes: {}
|
|
2890
|
+
};
|
|
2891
|
+
if (timer.current !== null) clearTimeout(timer.current);
|
|
2892
|
+
timer.current = null;
|
|
2893
|
+
pending.current = null;
|
|
2894
|
+
latest.current = next;
|
|
2895
|
+
generation.current += 1;
|
|
2896
|
+
setLocal({
|
|
2897
|
+
key,
|
|
2898
|
+
value: next
|
|
2899
|
+
});
|
|
2900
|
+
const target = settingsRef.current;
|
|
2901
|
+
if (target === void 0) return;
|
|
2902
|
+
if (target.snapshot.pending || held.current !== null && held.current.key === key) {
|
|
2903
|
+
const prior = held.current !== null && held.current.key === key ? held.current : void 0;
|
|
2904
|
+
held.current = all ? {
|
|
2905
|
+
key,
|
|
2906
|
+
resetAll: true,
|
|
2907
|
+
resetSizes: false,
|
|
2908
|
+
sizes: new Map()
|
|
2909
|
+
} : {
|
|
2910
|
+
key,
|
|
2911
|
+
resetAll: prior?.resetAll ?? false,
|
|
2912
|
+
resetSizes: true,
|
|
2913
|
+
sizes: new Map(),
|
|
2914
|
+
...prior?.order === void 0 ? {} : { order: prior.order },
|
|
2915
|
+
...prior?.pinning === void 0 ? {} : { pinning: prior.pinning }
|
|
2916
|
+
};
|
|
2917
|
+
return;
|
|
2918
|
+
}
|
|
2919
|
+
store(target, next);
|
|
2920
|
+
}, [resetToken]);
|
|
2921
|
+
const setColumnSizing = useCallback((updater) => {
|
|
2922
|
+
const before = sizingRef.current;
|
|
2923
|
+
const sizes = resolve(updater, before);
|
|
2924
|
+
sizingRef.current = sizes;
|
|
2925
|
+
change({
|
|
2926
|
+
...latest.current,
|
|
2927
|
+
sizes
|
|
2928
|
+
}, true, (edits) => {
|
|
2929
|
+
for (const column of new Set([...Object.keys(before), ...Object.keys(sizes)])) if (before[column] !== sizes[column]) edits.sizes.set(column, sizes[column]);
|
|
2930
|
+
});
|
|
2931
|
+
}, [change]);
|
|
2932
|
+
const setColumnOrder = useCallback((updater) => {
|
|
2933
|
+
const order = resolve(updater, latest.current.order);
|
|
2934
|
+
change({
|
|
2935
|
+
...latest.current,
|
|
2936
|
+
order
|
|
2937
|
+
}, false, (edits) => {
|
|
2938
|
+
edits.order = order;
|
|
2939
|
+
});
|
|
2940
|
+
}, [change]);
|
|
2941
|
+
const setColumnPinning = useCallback((updater) => {
|
|
2942
|
+
const resolved = resolve(updater, latest.current.pinning);
|
|
2943
|
+
const pinning = {
|
|
2944
|
+
left: resolved.left ?? [],
|
|
2945
|
+
right: resolved.right ?? []
|
|
2946
|
+
};
|
|
2947
|
+
change({
|
|
2948
|
+
...latest.current,
|
|
2949
|
+
pinning
|
|
2950
|
+
}, false, (edits) => {
|
|
2951
|
+
edits.pinning = pinning;
|
|
2952
|
+
});
|
|
2953
|
+
}, [change]);
|
|
2954
|
+
return {
|
|
2955
|
+
columnSizing: sizing,
|
|
2956
|
+
setColumnSizing,
|
|
2957
|
+
columnOrder: layout.order,
|
|
2958
|
+
setColumnOrder,
|
|
2959
|
+
columnPinning: layout.pinning,
|
|
2960
|
+
setColumnPinning
|
|
2961
|
+
};
|
|
2962
|
+
}
|
|
2963
|
+
//#endregion
|
|
3018
2964
|
//#region src/list-kit/cursor-load-more.tsx
|
|
3019
2965
|
function CursorLoadMore({ hasMore, loading, onMore }) {
|
|
3020
2966
|
const t = useT();
|
|
@@ -3056,6 +3002,10 @@ function CountRowsTrigger({ label, loading, onClick, testid, variant, className
|
|
|
3056
3002
|
}
|
|
3057
3003
|
const SELECT_COL_ID = "__select__";
|
|
3058
3004
|
const ACTIONS_COL_ID = "__actions__";
|
|
3005
|
+
const DEFAULT_PINNING = Object.freeze({
|
|
3006
|
+
left: Object.freeze([SELECT_COL_ID, "id"]),
|
|
3007
|
+
right: Object.freeze([ACTIONS_COL_ID])
|
|
3008
|
+
});
|
|
3059
3009
|
const ROW_ACTION_CONTROL_PX = 28;
|
|
3060
3010
|
const ROW_ACTION_GAP_PX = 4;
|
|
3061
3011
|
const ACTIONS_CELL_PADDING_PX = 16;
|
|
@@ -3067,29 +3017,30 @@ function actionsColumnSize(items) {
|
|
|
3067
3017
|
const strip = controls * ROW_ACTION_CONTROL_PX + (controls - 1) * ROW_ACTION_GAP_PX + ACTIONS_CELL_PADDING_PX;
|
|
3068
3018
|
return Math.max(ACTIONS_COL_MIN_PX, strip);
|
|
3069
3019
|
}
|
|
3070
|
-
const
|
|
3071
|
-
|
|
3072
|
-
|
|
3020
|
+
const ALIGN_CLASS = {
|
|
3021
|
+
start: "text-start",
|
|
3022
|
+
center: "text-center",
|
|
3023
|
+
end: "text-end"
|
|
3073
3024
|
};
|
|
3074
|
-
const
|
|
3075
|
-
|
|
3076
|
-
|
|
3077
|
-
|
|
3078
|
-
|
|
3079
|
-
|
|
3080
|
-
|
|
3081
|
-
|
|
3082
|
-
|
|
3083
|
-
}
|
|
3084
|
-
function ResourceList({ resource, data, total, page = 1, pageSize = 10, onPageSize, onRequestCount, isCountingRows = false, pageSizeOptions = [
|
|
3025
|
+
const STANDALONE_COLUMN_UI = Object.freeze({
|
|
3026
|
+
default: 160,
|
|
3027
|
+
min: 90,
|
|
3028
|
+
align: "start",
|
|
3029
|
+
resize: true,
|
|
3030
|
+
fill: false,
|
|
3031
|
+
display: Object.freeze({ params: Object.freeze({}) })
|
|
3032
|
+
});
|
|
3033
|
+
function ResourceList({ resource: resourceInput, data, total, page = 1, pageSize = 10, onPageSize, onRequestCount, isCountingRows = false, pageSizeOptions = [
|
|
3085
3034
|
10,
|
|
3086
3035
|
25,
|
|
3087
3036
|
50,
|
|
3088
3037
|
100
|
|
3089
|
-
], onPage, loadMore, selectable = false, selectedIds, onSelectionChange, className, isLoading = false, error = null, onRetry, createHref, isFiltered = false, onClearFilter, emptyState, rowsView, rowHref, rowQuick, rowLinkKey, renderCell, sort, onSort, onHideColumn, requiredColumns, resetLayout, columns: columnsOverride, tableRef }) {
|
|
3038
|
+
], onPage, loadMore, selectable = false, rowActions = true, selectedIds, onSelectionChange, className, isLoading = false, error = null, onRetry, createHref, isFiltered = false, onClearFilter, emptyState, rowsView, rowHref, rowQuick, rowLinkKey, renderCell, sort, onSort, sortable, onHideColumn, requiredColumns, resetLayout, columns: columnsOverride, columnUi, layoutId, tableRef }) {
|
|
3039
|
+
const resource = routeResourceKey(resourceInput);
|
|
3090
3040
|
const app = useApp();
|
|
3091
3041
|
const tid = useTestIds();
|
|
3092
3042
|
const t = useOptionalT();
|
|
3043
|
+
const chromeT = useChromeT();
|
|
3093
3044
|
const locale = useLocale();
|
|
3094
3045
|
const nf = useMemo(() => new Intl.NumberFormat(locale), [locale]);
|
|
3095
3046
|
const i18nCaps = useI18nCaps(resource);
|
|
@@ -3111,38 +3062,25 @@ function ResourceList({ resource, data, total, page = 1, pageSize = 10, onPageSi
|
|
|
3111
3062
|
useEffect(() => {
|
|
3112
3063
|
emittedRef.current = null;
|
|
3113
3064
|
}, [controlledSelection]);
|
|
3114
|
-
const layoutKey = `buro:table-layout:${resource}`;
|
|
3115
|
-
const [columnSizing, setColumnSizing] = usePersistedState(`${layoutKey}:sizing`, {}, 200);
|
|
3116
|
-
const [columnOrder, setColumnOrder] = usePersistedState(`${layoutKey}:order`, []);
|
|
3117
|
-
const DEFAULT_PINNING = {
|
|
3118
|
-
left: [SELECT_COL_ID, "id"],
|
|
3119
|
-
right: [ACTIONS_COL_ID]
|
|
3120
|
-
};
|
|
3121
|
-
const [columnPinning, setColumnPinning] = usePersistedState(`${layoutKey}:pinning`, DEFAULT_PINNING);
|
|
3122
|
-
const resetToken = resetLayout?.token ?? 0;
|
|
3123
|
-
useEffect(() => {
|
|
3124
|
-
if (!resetToken) return;
|
|
3125
|
-
setColumnSizing({});
|
|
3126
|
-
if (resetLayout?.mode === "all") {
|
|
3127
|
-
setColumnOrder([]);
|
|
3128
|
-
setColumnPinning(DEFAULT_PINNING);
|
|
3129
|
-
}
|
|
3130
|
-
}, [resetToken]);
|
|
3131
3065
|
useEffect(() => {
|
|
3132
3066
|
if (controlled) return;
|
|
3133
3067
|
setOwnSelection({});
|
|
3134
3068
|
}, [data]);
|
|
3135
3069
|
const listAccess = useListAccess(resource);
|
|
3136
|
-
const
|
|
3070
|
+
const pathAccess = usePathAccess(resource);
|
|
3071
|
+
const accessPending = listAccess.status === "pending" || pathAccess.pending;
|
|
3137
3072
|
const declaredColumnKeys = useMemo(() => {
|
|
3138
3073
|
if (!def) return [];
|
|
3139
|
-
return [...columnsOverride ?? def.views?.list?.columns ?? Object.keys(def.fields)];
|
|
3074
|
+
return [...columnsOverride ?? def.views?.list?.columns?.map(columnName) ?? Object.keys(def.fields)];
|
|
3140
3075
|
}, [def, columnsOverride]);
|
|
3141
|
-
const visibleColumnKeys = useMemo(() => accessPending ? [] : declaredColumnKeys.filter((col) => listAccess.columnVisible(col)), [
|
|
3076
|
+
const visibleColumnKeys = useMemo(() => accessPending ? [] : declaredColumnKeys.filter((col) => listAccess.columnVisible(col) && (columnTarget(app.registry, resource, col) === void 0 || listAccess.columnVisible(col.split(".")[0]) && pathAccess.visible(col))), [
|
|
3142
3077
|
accessPending,
|
|
3143
3078
|
declaredColumnKeys,
|
|
3144
|
-
listAccess
|
|
3145
|
-
|
|
3079
|
+
listAccess,
|
|
3080
|
+
pathAccess,
|
|
3081
|
+
app,
|
|
3082
|
+
resource
|
|
3083
|
+
]);
|
|
3146
3084
|
const skeletonColumnKeys = accessPending ? declaredColumnKeys : visibleColumnKeys;
|
|
3147
3085
|
const rows = useMemo(() => data ?? [], [data]);
|
|
3148
3086
|
const rowFlows = useMemo(() => {
|
|
@@ -3183,10 +3121,11 @@ function ResourceList({ resource, data, total, page = 1, pageSize = 10, onPageSi
|
|
|
3183
3121
|
const resourceLabel = resolveResourceLabel(i18nCaps, resource, "plural", def);
|
|
3184
3122
|
const recordSingular = resolveResourceLabel(i18nCaps, resource, "single", def);
|
|
3185
3123
|
const ariaLabel = resourceLabel;
|
|
3186
|
-
const actionsHeader =
|
|
3124
|
+
const actionsHeader = chromeT("actions.column", { defaultValue: "Actions" });
|
|
3187
3125
|
const viewRow = useViewRow(resource);
|
|
3188
3126
|
const volatileRef = useRef({
|
|
3189
3127
|
onSort,
|
|
3128
|
+
sortable,
|
|
3190
3129
|
onHideColumn,
|
|
3191
3130
|
requiredColumns,
|
|
3192
3131
|
renderCell,
|
|
@@ -3198,6 +3137,7 @@ function ResourceList({ resource, data, total, page = 1, pageSize = 10, onPageSi
|
|
|
3198
3137
|
});
|
|
3199
3138
|
volatileRef.current = {
|
|
3200
3139
|
onSort,
|
|
3140
|
+
sortable,
|
|
3201
3141
|
onHideColumn,
|
|
3202
3142
|
requiredColumns,
|
|
3203
3143
|
renderCell,
|
|
@@ -3221,6 +3161,45 @@ function ResourceList({ resource, data, total, page = 1, pageSize = 10, onPageSi
|
|
|
3221
3161
|
}, [rowLinkKey]);
|
|
3222
3162
|
const stableRowClassName = useCallback((row) => volatileRef.current.rowHref?.(volatileRef.current.viewRow(row.original)) || (volatileRef.current.rowQuick?.(row.original) ?? null) !== null ? "cursor-pointer" : void 0, [rowLinkKey]);
|
|
3223
3163
|
const [listRoot, narrow] = useContainerNarrow();
|
|
3164
|
+
const columnUiOf = useMemo(() => {
|
|
3165
|
+
const entries = new Map();
|
|
3166
|
+
for (const entry of [...def?.views?.list?.columns ?? [], ...def?.views?.list?.optionalColumns ?? []]) {
|
|
3167
|
+
const name = columnName(entry);
|
|
3168
|
+
if (!entries.has(name)) entries.set(name, entry);
|
|
3169
|
+
}
|
|
3170
|
+
return (col) => columnUi?.[col] ?? (def === void 0 ? STANDALONE_COLUMN_UI : resolveColumnUi(def, entries.get(col) ?? col, app.registry, columnTypeWidth));
|
|
3171
|
+
}, [
|
|
3172
|
+
app,
|
|
3173
|
+
def,
|
|
3174
|
+
columnUi
|
|
3175
|
+
]);
|
|
3176
|
+
const { columnSizing, setColumnSizing, columnOrder, setColumnOrder, columnPinning, setColumnPinning } = useTableLayout({
|
|
3177
|
+
resource,
|
|
3178
|
+
layoutId,
|
|
3179
|
+
rule: useMemo(() => {
|
|
3180
|
+
const known = new Set([
|
|
3181
|
+
...declaredColumnKeys,
|
|
3182
|
+
...(def?.views?.list?.columns ?? []).map(columnName),
|
|
3183
|
+
...(def?.views?.list?.optionalColumns ?? []).map(columnName),
|
|
3184
|
+
...Object.keys(def?.fields ?? {})
|
|
3185
|
+
]);
|
|
3186
|
+
return (col) => {
|
|
3187
|
+
if (!known.has(col)) return void 0;
|
|
3188
|
+
const ui = columnUiOf(col);
|
|
3189
|
+
return {
|
|
3190
|
+
min: ui.min,
|
|
3191
|
+
max: ui.max,
|
|
3192
|
+
resize: ui.resize
|
|
3193
|
+
};
|
|
3194
|
+
};
|
|
3195
|
+
}, [
|
|
3196
|
+
declaredColumnKeys,
|
|
3197
|
+
def,
|
|
3198
|
+
columnUiOf
|
|
3199
|
+
]),
|
|
3200
|
+
defaultPinning: DEFAULT_PINNING,
|
|
3201
|
+
resetLayout
|
|
3202
|
+
});
|
|
3224
3203
|
const tableColumns = useMemo(() => {
|
|
3225
3204
|
const cols = [];
|
|
3226
3205
|
if (selectable) cols.push({
|
|
@@ -3242,16 +3221,33 @@ function ResourceList({ resource, data, total, page = 1, pageSize = 10, onPageSi
|
|
|
3242
3221
|
})
|
|
3243
3222
|
});
|
|
3244
3223
|
for (const col of visibleColumnKeys) {
|
|
3245
|
-
const
|
|
3246
|
-
const
|
|
3247
|
-
const
|
|
3224
|
+
const target = columnTarget(app.registry, resource, col);
|
|
3225
|
+
const field = target !== void 0 ? target.field : def === void 0 ? void 0 : declaredField(def, col);
|
|
3226
|
+
const declaredLabel = declaredText("field", field, "label");
|
|
3227
|
+
const headerLabel = target === void 0 ? resolveFieldLabel(volatileRef.current.i18nCaps, col, declaredLabel) : resolveFieldLabel(capsOf(volatileRef.current.i18nCaps, target.resource), target.name, declaredLabel);
|
|
3228
|
+
const ui = columnUiOf(col);
|
|
3229
|
+
const frameField = {
|
|
3230
|
+
...field ?? { type: "unknown" },
|
|
3231
|
+
name: target === void 0 ? col : target.name
|
|
3232
|
+
};
|
|
3233
|
+
const cellEntry = { display: ui.display };
|
|
3234
|
+
const wraps = ui.display.overflow === "wrap";
|
|
3235
|
+
const columnSortable = volatileRef.current.sortable?.(col) ?? (def !== void 0 && isColumnSortable(def, col, app, app.registry));
|
|
3248
3236
|
cols.push({
|
|
3249
3237
|
id: col,
|
|
3250
|
-
|
|
3251
|
-
|
|
3252
|
-
|
|
3238
|
+
...columnAccessor(col),
|
|
3239
|
+
size: ui.default,
|
|
3240
|
+
minSize: ui.min,
|
|
3241
|
+
maxSize: ui.max ?? Number.MAX_SAFE_INTEGER,
|
|
3242
|
+
enableResizing: ui.resize,
|
|
3243
|
+
enableSorting: columnSortable && Boolean(volatileRef.current.onSort),
|
|
3253
3244
|
enableHiding: true,
|
|
3254
|
-
meta: {
|
|
3245
|
+
meta: {
|
|
3246
|
+
testid: col,
|
|
3247
|
+
className: ALIGN_CLASS[ui.align],
|
|
3248
|
+
...wraps ? { wrap: true } : {},
|
|
3249
|
+
...ui.fill ? { fill: true } : {}
|
|
3250
|
+
},
|
|
3255
3251
|
header: ({ column, table }) => {
|
|
3256
3252
|
const v = volatileRef.current;
|
|
3257
3253
|
const pinSide = column.getIsPinned();
|
|
@@ -3282,9 +3278,9 @@ function ResourceList({ resource, data, total, page = 1, pageSize = 10, onPageSi
|
|
|
3282
3278
|
};
|
|
3283
3279
|
return jsx(ColumnHeaderMenu, {
|
|
3284
3280
|
label: headerLabel,
|
|
3285
|
-
sortable: Boolean(v.onSort),
|
|
3281
|
+
sortable: columnSortable && Boolean(v.onSort),
|
|
3286
3282
|
sortDirection: v.sort?.[0]?.field === col ? v.sort[0].direction : null,
|
|
3287
|
-
onSort: v.onSort ? (dir) => v.onSort?.(col, dir) : void 0,
|
|
3283
|
+
onSort: columnSortable && v.onSort ? (dir) => v.onSort?.(col, dir) : void 0,
|
|
3288
3284
|
pinned: column.getIsPinned(),
|
|
3289
3285
|
onPin: (side) => column.pin(side),
|
|
3290
3286
|
canHide: Boolean(v.onHideColumn) && !v.requiredColumns?.includes(col),
|
|
@@ -3301,17 +3297,26 @@ function ResourceList({ resource, data, total, page = 1, pageSize = 10, onPageSi
|
|
|
3301
3297
|
const value = record[col];
|
|
3302
3298
|
const customCell = volatileRef.current.renderCell?.(col, value, record);
|
|
3303
3299
|
const cellInner = customCell !== void 0 ? customCell : jsx(ResolvedFieldDisplay, {
|
|
3304
|
-
resource: resourceRef,
|
|
3305
|
-
name: col,
|
|
3300
|
+
resource: target === void 0 ? resourceRef : target.ref,
|
|
3301
|
+
name: target === void 0 ? col : target.name,
|
|
3306
3302
|
field: field ?? { type: "unknown" },
|
|
3307
3303
|
value,
|
|
3308
3304
|
record,
|
|
3305
|
+
uiSurface: "column",
|
|
3306
|
+
columnEntry: cellEntry,
|
|
3309
3307
|
fallback: jsx("span", { children: String(value ?? "") })
|
|
3310
3308
|
});
|
|
3309
|
+
const framed = (node) => customCell !== void 0 ? node : jsx(FieldValueFrame, {
|
|
3310
|
+
field: frameField,
|
|
3311
|
+
value,
|
|
3312
|
+
surface: "column",
|
|
3313
|
+
entry: cellEntry,
|
|
3314
|
+
children: node
|
|
3315
|
+
});
|
|
3311
3316
|
const href = volatileRef.current.rowHref?.(record);
|
|
3312
3317
|
const isFirstContentCell = visibleColumnKeys.indexOf(col) === 0;
|
|
3313
3318
|
const quick = isFirstContentCell ? volatileRef.current.rowQuick?.(raw) ?? null : null;
|
|
3314
|
-
if (quick !== null) return href ? jsx(ClaimedQuickLink, {
|
|
3319
|
+
if (quick !== null) return framed(href ? jsx(ClaimedQuickLink, {
|
|
3315
3320
|
href,
|
|
3316
3321
|
className: relationLinkClassName,
|
|
3317
3322
|
onOpen: quick,
|
|
@@ -3325,21 +3330,22 @@ function ResourceList({ resource, data, total, page = 1, pageSize = 10, onPageSi
|
|
|
3325
3330
|
quick();
|
|
3326
3331
|
},
|
|
3327
3332
|
children: cellInner
|
|
3328
|
-
});
|
|
3329
|
-
return isFirstContentCell && href ? jsx(RouterLink, {
|
|
3333
|
+
}));
|
|
3334
|
+
return framed(isFirstContentCell && href ? jsx(RouterLink, {
|
|
3330
3335
|
href,
|
|
3331
3336
|
className: relationLinkClassName,
|
|
3332
3337
|
onClick: (e) => e.stopPropagation(),
|
|
3333
3338
|
children: cellInner
|
|
3334
|
-
}) : cellInner;
|
|
3339
|
+
}) : cellInner);
|
|
3335
3340
|
}
|
|
3336
3341
|
});
|
|
3337
3342
|
}
|
|
3343
|
+
if (!rowActions) return cols;
|
|
3338
3344
|
cols.push({
|
|
3339
3345
|
id: ACTIONS_COL_ID,
|
|
3340
3346
|
enableSorting: false,
|
|
3341
3347
|
enableResizing: false,
|
|
3342
|
-
size: narrow ? 44 : actionsColumnSize(def?.actions?.item),
|
|
3348
|
+
size: narrow || typeof rowActions === "function" ? 44 : actionsColumnSize(def?.actions?.item),
|
|
3343
3349
|
enableHiding: false,
|
|
3344
3350
|
header: () => jsx("span", {
|
|
3345
3351
|
className: "sr-only",
|
|
@@ -3350,7 +3356,7 @@ function ResourceList({ resource, data, total, page = 1, pageSize = 10, onPageSi
|
|
|
3350
3356
|
noRowNav: true,
|
|
3351
3357
|
noClip: true
|
|
3352
3358
|
},
|
|
3353
|
-
cell: ({ row }) => jsx(ActionOverflow, {
|
|
3359
|
+
cell: ({ row }) => typeof rowActions === "function" ? rowActions(row.original) : jsx(ActionOverflow, {
|
|
3354
3360
|
resource,
|
|
3355
3361
|
record: row.original,
|
|
3356
3362
|
surface: "row"
|
|
@@ -3358,7 +3364,9 @@ function ResourceList({ resource, data, total, page = 1, pageSize = 10, onPageSi
|
|
|
3358
3364
|
});
|
|
3359
3365
|
return cols;
|
|
3360
3366
|
}, [
|
|
3367
|
+
app,
|
|
3361
3368
|
selectable,
|
|
3369
|
+
rowActions,
|
|
3362
3370
|
resourceRef,
|
|
3363
3371
|
visibleColumnKeys,
|
|
3364
3372
|
def,
|
|
@@ -3368,7 +3376,8 @@ function ResourceList({ resource, data, total, page = 1, pageSize = 10, onPageSi
|
|
|
3368
3376
|
actionsHeader,
|
|
3369
3377
|
i18nCaps,
|
|
3370
3378
|
rowLinkKey,
|
|
3371
|
-
narrow
|
|
3379
|
+
narrow,
|
|
3380
|
+
columnUiOf
|
|
3372
3381
|
]);
|
|
3373
3382
|
const handleSelectionChange = (updater) => {
|
|
3374
3383
|
if (controlled) {
|
|
@@ -3471,8 +3480,23 @@ function ResourceList({ resource, data, total, page = 1, pageSize = 10, onPageSi
|
|
|
3471
3480
|
const isWrongPage = page > 1;
|
|
3472
3481
|
const EmptyIcon = isFiltered ? IconFilterX : IconInbox;
|
|
3473
3482
|
const items = resourceLabel.toLowerCase();
|
|
3474
|
-
const heading = isWrongPage ? t("pagination.emptyPage") : isFiltered ? t("list.emptyFilteredTitle", {
|
|
3475
|
-
|
|
3483
|
+
const heading = isWrongPage ? t("pagination.emptyPage") : isFiltered ? t("list.emptyFilteredTitle", {
|
|
3484
|
+
resource,
|
|
3485
|
+
params: { items }
|
|
3486
|
+
}) : t("list.emptyTitle", {
|
|
3487
|
+
resource,
|
|
3488
|
+
params: { items }
|
|
3489
|
+
});
|
|
3490
|
+
const body = isWrongPage ? null : isFiltered ? t("list.emptyFilteredBody", {
|
|
3491
|
+
resource,
|
|
3492
|
+
params: { items }
|
|
3493
|
+
}) : createHref ? t("list.emptyBody", {
|
|
3494
|
+
resource,
|
|
3495
|
+
params: { item: recordSingular.toLowerCase() }
|
|
3496
|
+
}) : t("list.emptyBodyReadOnly", {
|
|
3497
|
+
resource,
|
|
3498
|
+
params: { items }
|
|
3499
|
+
});
|
|
3476
3500
|
return jsxs(Fragment$1, { children: [jsxs("div", {
|
|
3477
3501
|
className: cn("flex flex-col items-center justify-center gap-3 px-6 py-16 rounded-lg border border-dashed text-center", className),
|
|
3478
3502
|
"data-testid": tid.listEmpty(resource),
|
|
@@ -3497,7 +3521,7 @@ function ResourceList({ resource, data, total, page = 1, pageSize = 10, onPageSi
|
|
|
3497
3521
|
jsxs("div", {
|
|
3498
3522
|
className: "flex justify-center gap-2 pt-2",
|
|
3499
3523
|
children: [
|
|
3500
|
-
|
|
3524
|
+
onClearFilter && jsx(Button, {
|
|
3501
3525
|
type: "button",
|
|
3502
3526
|
variant: "outline",
|
|
3503
3527
|
size: "sm",
|
|
@@ -3685,43 +3709,46 @@ function ResourceList({ resource, data, total, page = 1, pageSize = 10, onPageSi
|
|
|
3685
3709
|
})
|
|
3686
3710
|
});
|
|
3687
3711
|
}
|
|
3688
|
-
|
|
3689
|
-
|
|
3690
|
-
|
|
3691
|
-
|
|
3692
|
-
|
|
3693
|
-
|
|
3694
|
-
|
|
3695
|
-
|
|
3696
|
-
|
|
3712
|
+
//#endregion
|
|
3713
|
+
//#region src/blocks/related-list-unbound.tsx
|
|
3714
|
+
function UnboundRelatedList(props) {
|
|
3715
|
+
const t = useOptionalT();
|
|
3716
|
+
return jsxs("div", {
|
|
3717
|
+
className: "flex flex-col gap-2",
|
|
3718
|
+
"data-testid": `list-unbound:${props.resource}`,
|
|
3719
|
+
children: [props.title === void 0 ? null : jsx("h3", {
|
|
3720
|
+
className: "text-sm font-semibold",
|
|
3721
|
+
children: props.title
|
|
3722
|
+
}), jsx("p", {
|
|
3723
|
+
className: "text-sm text-muted-foreground",
|
|
3724
|
+
children: props.empty ?? t("table.empty", { defaultValue: "No records" })
|
|
3725
|
+
})]
|
|
3697
3726
|
});
|
|
3698
|
-
|
|
3699
|
-
|
|
3700
|
-
|
|
3701
|
-
|
|
3702
|
-
|
|
3703
|
-
|
|
3704
|
-
|
|
3705
|
-
|
|
3706
|
-
|
|
3707
|
-
|
|
3708
|
-
|
|
3709
|
-
|
|
3710
|
-
|
|
3711
|
-
|
|
3712
|
-
|
|
3713
|
-
|
|
3714
|
-
|
|
3715
|
-
|
|
3716
|
-
|
|
3717
|
-
|
|
3718
|
-
|
|
3719
|
-
|
|
3720
|
-
|
|
3721
|
-
|
|
3722
|
-
|
|
3723
|
-
};
|
|
3724
|
-
return [state, set];
|
|
3727
|
+
}
|
|
3728
|
+
function placementScope(scope, owner) {
|
|
3729
|
+
return scope ?? (owner === void 0 ? "session" : "none");
|
|
3730
|
+
}
|
|
3731
|
+
//#endregion
|
|
3732
|
+
//#region src/list-kit/pieces/bulk-reach.tsx
|
|
3733
|
+
function useBulkFlowReachable(resource) {
|
|
3734
|
+
const app = useApp();
|
|
3735
|
+
const control = useFlowRowResolver(resource);
|
|
3736
|
+
return (app.registry.get(resource)?.actions.bulk ?? []).some((action) => isFlowAction(action) && control(action.name, void 0).state !== "absent");
|
|
3737
|
+
}
|
|
3738
|
+
function ExportReach(props) {
|
|
3739
|
+
const { available } = useResourceExport(props.resource);
|
|
3740
|
+
const capability = useCapability("run", {
|
|
3741
|
+
resource: props.resource,
|
|
3742
|
+
target: {
|
|
3743
|
+
kind: "action",
|
|
3744
|
+
name: "export"
|
|
3745
|
+
},
|
|
3746
|
+
mode: "list"
|
|
3747
|
+
});
|
|
3748
|
+
return props.children(available && capability.allowed);
|
|
3749
|
+
}
|
|
3750
|
+
function BulkExportReach(props) {
|
|
3751
|
+
return useOptionalDataRuntime() !== null ? jsx(ExportReach, { ...props }) : props.children(false);
|
|
3725
3752
|
}
|
|
3726
3753
|
//#endregion
|
|
3727
3754
|
//#region src/list-kit/pieces/piece.tsx
|
|
@@ -3789,15 +3816,47 @@ function ForceTableView(props) {
|
|
|
3789
3816
|
}
|
|
3790
3817
|
//#endregion
|
|
3791
3818
|
//#region src/list-kit/pieces/list-body.tsx
|
|
3819
|
+
function LinkDetachRowButton(props) {
|
|
3820
|
+
const { resource, linkEditing } = useListSurface();
|
|
3821
|
+
const t = useOptionalT();
|
|
3822
|
+
const [busy, setBusy] = useState(false);
|
|
3823
|
+
const detach = linkEditing?.detach;
|
|
3824
|
+
if (detach === void 0) return null;
|
|
3825
|
+
const label = t("field.link.detach", { defaultValue: "Detach" });
|
|
3826
|
+
return jsx(Button, {
|
|
3827
|
+
type: "button",
|
|
3828
|
+
size: "icon",
|
|
3829
|
+
variant: "ghost",
|
|
3830
|
+
className: "size-7",
|
|
3831
|
+
"aria-label": label,
|
|
3832
|
+
title: label,
|
|
3833
|
+
disabled: busy,
|
|
3834
|
+
"data-testid": `list-link-detach:${resource}:${props.row.id}`,
|
|
3835
|
+
onClick: (event) => {
|
|
3836
|
+
event.stopPropagation();
|
|
3837
|
+
setBusy(true);
|
|
3838
|
+
detach.onDetach([props.row]).finally(() => setBusy(false));
|
|
3839
|
+
},
|
|
3840
|
+
children: jsx(IconUnlink, {
|
|
3841
|
+
className: "size-4",
|
|
3842
|
+
"aria-hidden": "true"
|
|
3843
|
+
})
|
|
3844
|
+
});
|
|
3845
|
+
}
|
|
3846
|
+
const linkDetachRow = (row) => jsx(LinkDetachRowButton, { row });
|
|
3792
3847
|
function ListBodyView() {
|
|
3793
3848
|
usePieceMount("body");
|
|
3794
3849
|
const list = useResourceListContext();
|
|
3795
|
-
const { resource, route, tableView, createAvailable, createPage, rowProps, rowsViewFor, emptyState, renderCell } = useListSurface();
|
|
3850
|
+
const { resource, route, tableView, createAvailable, createPage, rowProps, rowsViewFor, emptyState, renderCell, readOnly, linkEditing, layoutId } = useListSurface();
|
|
3851
|
+
const detachable = readOnly && linkEditing?.detach !== void 0;
|
|
3852
|
+
const flowReachable = useBulkFlowReachable(resource);
|
|
3853
|
+
const selectable = (exportReachable) => tableView && (detachable || !readOnly && (flowReachable || exportReachable));
|
|
3796
3854
|
const item = useContext(ListItemContext);
|
|
3797
3855
|
const rowsView = useMemo(() => rowsViewFor?.(item), [rowsViewFor, item]);
|
|
3798
3856
|
const { query, pagination, cursor, count, columns, selection, filters, sorting, routing } = list;
|
|
3799
3857
|
const rows = policyRowsOf(list, list.rows);
|
|
3800
|
-
return jsx(
|
|
3858
|
+
if (query.unbound) return jsx(UnboundRelatedList, { resource });
|
|
3859
|
+
const view = (exportReachable) => jsx(ResourceList, {
|
|
3801
3860
|
resource,
|
|
3802
3861
|
data: rows,
|
|
3803
3862
|
total: pagination.total,
|
|
@@ -3814,28 +3873,50 @@ function ListBodyView() {
|
|
|
3814
3873
|
onRequestCount: count.available ? count.request : void 0,
|
|
3815
3874
|
isCountingRows: count.isLoading,
|
|
3816
3875
|
columns: columns.visible,
|
|
3876
|
+
columnUi: columns.ui,
|
|
3817
3877
|
isLoading: query.isLoading,
|
|
3818
3878
|
error: query.error,
|
|
3819
3879
|
onRetry: query.refetch,
|
|
3820
3880
|
createHref: route && createAvailable && createPage ? routing.createHref : void 0,
|
|
3821
3881
|
...rowProps,
|
|
3822
3882
|
...rowsView === void 0 ? {} : { rowsView },
|
|
3823
|
-
selectable:
|
|
3883
|
+
selectable: selectable(exportReachable),
|
|
3884
|
+
rowActions: detachable ? linkDetachRow : !readOnly,
|
|
3824
3885
|
selectedIds: selection.ids,
|
|
3825
3886
|
onSelectionChange: selection.set,
|
|
3826
3887
|
isFiltered: filters.isFiltered,
|
|
3827
|
-
onClearFilter: filters.
|
|
3888
|
+
onClearFilter: filters.canReset ? filters.reset : void 0,
|
|
3828
3889
|
emptyState,
|
|
3829
3890
|
sort: sorting.value ?? [],
|
|
3830
3891
|
requiredColumns: columns.required,
|
|
3831
3892
|
resetLayout: columns.resetLayout,
|
|
3893
|
+
layoutId,
|
|
3832
3894
|
onHideColumn: columns.hide,
|
|
3833
3895
|
onSort: sorting.toggle,
|
|
3896
|
+
sortable: sorting.sortable,
|
|
3834
3897
|
renderCell
|
|
3835
3898
|
});
|
|
3899
|
+
return jsx(BulkExportReach, {
|
|
3900
|
+
resource,
|
|
3901
|
+
children: view
|
|
3902
|
+
});
|
|
3836
3903
|
}
|
|
3837
3904
|
const ListBody = listPiece("ListBody", ListBodyView);
|
|
3838
3905
|
//#endregion
|
|
3906
|
+
//#region src/resource/form-context.tsx
|
|
3907
|
+
const FormControlContext = createContext(null);
|
|
3908
|
+
const FormControlProvider = FormControlContext.Provider;
|
|
3909
|
+
function useFormControlOf(controller) {
|
|
3910
|
+
const latest = useRef(controller);
|
|
3911
|
+
latest.current = controller;
|
|
3912
|
+
return useMemo(() => ({ setValues: (patch) => {
|
|
3913
|
+
for (const [name, value] of Object.entries(patch)) latest.current.setValue(name, value);
|
|
3914
|
+
} }), []);
|
|
3915
|
+
}
|
|
3916
|
+
function useFormControl() {
|
|
3917
|
+
return useContext(FormControlContext);
|
|
3918
|
+
}
|
|
3919
|
+
//#endregion
|
|
3839
3920
|
//#region src/record/route-screen-context.ts
|
|
3840
3921
|
const RouteScreenClaimContext = createContext(false);
|
|
3841
3922
|
const RouteScreenContext = createContext(false);
|
|
@@ -3931,7 +4012,10 @@ function PanelHost({ node, region = "panel-body", renderChildren }) {
|
|
|
3931
4012
|
const state = useScreenState();
|
|
3932
4013
|
const text = useNodeText();
|
|
3933
4014
|
const options = compiled.nodes[node.index].options;
|
|
3934
|
-
const title =
|
|
4015
|
+
const title = options.title?.humanized === true ? text({
|
|
4016
|
+
...options.title,
|
|
4017
|
+
default: ""
|
|
4018
|
+
}) || void 0 : text(options.title);
|
|
3935
4019
|
const description = text(options.description);
|
|
3936
4020
|
const collapsible = options.collapse !== void 0;
|
|
3937
4021
|
const open = !collapsible || node.open !== false;
|
|
@@ -3956,7 +4040,7 @@ function PanelHost({ node, region = "panel-body", renderChildren }) {
|
|
|
3956
4040
|
region,
|
|
3957
4041
|
form,
|
|
3958
4042
|
labelledBy: title === void 0 ? void 0 : titleId,
|
|
3959
|
-
children: renderChildren(node.children)
|
|
4043
|
+
children: renderChildren(node.children, node)
|
|
3960
4044
|
}) : null,
|
|
3961
4045
|
...title === void 0 ? {} : { title },
|
|
3962
4046
|
...description === void 0 ? {} : { description },
|
|
@@ -3991,22 +4075,24 @@ function TabsHost({ node, renderChildren }) {
|
|
|
3991
4075
|
const state = useScreenState();
|
|
3992
4076
|
const text = useNodeText();
|
|
3993
4077
|
const panels = node.children.filter((child) => child.state !== "absent");
|
|
3994
|
-
const
|
|
4078
|
+
const declared = compiled.nodes[node.index].options.default;
|
|
4079
|
+
const effectiveDefault = (declared === void 0 ? void 0 : panels.find((child) => child.key === declared)) ?? panels[0];
|
|
4080
|
+
const open = panels.find((child) => child.open === true) ?? effectiveDefault;
|
|
3995
4081
|
const urlSelect = useTabsUrlSelect();
|
|
3996
4082
|
const select = (panel, reveal) => {
|
|
3997
|
-
if (urlSelect !== null && node.key !== void 0 && panel.key !== void 0) urlSelect(node.key, panel.key, panel ===
|
|
4083
|
+
if (urlSelect !== null && node.key !== void 0 && panel.key !== void 0) urlSelect(node.key, panel.key, panel === effectiveDefault, reveal);
|
|
3998
4084
|
else state?.openPanel(node.id, panelValue(panel));
|
|
3999
4085
|
};
|
|
4000
4086
|
const onSelect = (key) => {
|
|
4001
4087
|
const panel = panels.find((candidate) => panelValue(candidate) === key);
|
|
4002
4088
|
if (panel !== void 0) select(panel, false);
|
|
4003
4089
|
};
|
|
4004
|
-
const titleOf = (panel) => text(compiled.nodes[panel.index].options.title) ?? panel.
|
|
4090
|
+
const titleOf = (panel) => text(compiled.nodes[panel.index].options.title) ?? panel.id;
|
|
4005
4091
|
const tabs = panels.map((panel) => ({
|
|
4006
4092
|
key: panelValue(panel),
|
|
4007
4093
|
title: titleOf(panel),
|
|
4008
4094
|
errorCount: panel.key === void 0 ? 0 : controller.errorsIn(panel.key),
|
|
4009
|
-
panel: panels.length === 1 || panel.open === true ? jsx(Fragment$1, { children: renderChildren(panel.children) }) : null
|
|
4095
|
+
panel: panels.length === 1 || panel.open === true ? jsx(Fragment$1, { children: renderChildren(panel.children, panel) }) : null
|
|
4010
4096
|
}));
|
|
4011
4097
|
const { view: View, id } = useNodeView(node, "tabs");
|
|
4012
4098
|
const t = useFrameT(id);
|
|
@@ -4044,7 +4130,7 @@ function StackedStepsHost({ node, renderChildren }) {
|
|
|
4044
4130
|
const t = useFrameT(id);
|
|
4045
4131
|
const steps = node.children.filter((child) => child.state !== "absent").map((panel) => {
|
|
4046
4132
|
const options = compiled.nodes[panel.index].options;
|
|
4047
|
-
const title = text(options.title) ?? panel.
|
|
4133
|
+
const title = text(options.title) ?? panel.id;
|
|
4048
4134
|
const description = text(options.description);
|
|
4049
4135
|
return {
|
|
4050
4136
|
key: panel.key ?? panel.id,
|
|
@@ -4092,7 +4178,7 @@ function WizardHost({ node, renderChildren }) {
|
|
|
4092
4178
|
const current = panels.find((child) => child.open === true) ?? panels[0];
|
|
4093
4179
|
const index = current === void 0 ? -1 : panels.indexOf(current);
|
|
4094
4180
|
const idOf = (panel) => panel.key ?? panel.id;
|
|
4095
|
-
const titleOf = (panel) => text(compiled.nodes[panel.index].options.title) ?? panel.
|
|
4181
|
+
const titleOf = (panel) => text(compiled.nodes[panel.index].options.title) ?? panel.id;
|
|
4096
4182
|
const headingRef = useRef(null);
|
|
4097
4183
|
const shown = useRef(void 0);
|
|
4098
4184
|
const currentId = current === void 0 ? void 0 : idOf(current);
|
|
@@ -4117,7 +4203,7 @@ function WizardHost({ node, renderChildren }) {
|
|
|
4117
4203
|
region: "step-panel",
|
|
4118
4204
|
form,
|
|
4119
4205
|
labelledBy: titleId,
|
|
4120
|
-
children: renderChildren(panel.children)
|
|
4206
|
+
children: renderChildren(panel.children, panel)
|
|
4121
4207
|
}),
|
|
4122
4208
|
...description === void 0 ? {} : { description },
|
|
4123
4209
|
titleId,
|
|
@@ -4163,18 +4249,23 @@ function WizardHost({ node, renderChildren }) {
|
|
|
4163
4249
|
//#region src/screen/nodes.tsx
|
|
4164
4250
|
function FailedNode({ node }) {
|
|
4165
4251
|
const { controller } = useScreenTree();
|
|
4166
|
-
|
|
4167
|
-
|
|
4168
|
-
|
|
4169
|
-
|
|
4170
|
-
|
|
4171
|
-
|
|
4172
|
-
|
|
4173
|
-
|
|
4174
|
-
|
|
4175
|
-
|
|
4176
|
-
|
|
4177
|
-
|
|
4252
|
+
let error;
|
|
4253
|
+
for (const name of node.failedOn) {
|
|
4254
|
+
const source = controller.sources[name];
|
|
4255
|
+
if (source?.status === "failed") {
|
|
4256
|
+
error = source.error;
|
|
4257
|
+
break;
|
|
4258
|
+
}
|
|
4259
|
+
}
|
|
4260
|
+
return jsx(LoadError, {
|
|
4261
|
+
surface: "tab",
|
|
4262
|
+
error,
|
|
4263
|
+
retry: () => controller.retry(node.failedOn),
|
|
4264
|
+
fallback: {
|
|
4265
|
+
key: "screen.failed",
|
|
4266
|
+
default: "This section could not be loaded."
|
|
4267
|
+
},
|
|
4268
|
+
attributes: nodeAttributes(node)
|
|
4178
4269
|
});
|
|
4179
4270
|
}
|
|
4180
4271
|
function failedOnlyOnMeta(node) {
|
|
@@ -4187,8 +4278,17 @@ function drawn(node, hiddenFields) {
|
|
|
4187
4278
|
if (node.state === "absent") return false;
|
|
4188
4279
|
return !(node.kind === "field" && node.field !== void 0 && hiddenFields?.has(node.field));
|
|
4189
4280
|
}
|
|
4190
|
-
function SectionChildren({ nodes }) {
|
|
4191
|
-
const { hiddenFields } = useScreenTree();
|
|
4281
|
+
function SectionChildren({ nodes, owner }) {
|
|
4282
|
+
const { compiled, hiddenFields } = useScreenTree();
|
|
4283
|
+
const text = useNodeText();
|
|
4284
|
+
if (owner !== void 0 && !nodes.some((node) => drawn(node, hiddenFields))) {
|
|
4285
|
+
const empty = text(compiled.nodes[owner.index]?.options.empty);
|
|
4286
|
+
return empty === void 0 || empty === "" ? null : jsx("p", {
|
|
4287
|
+
"data-buro-region": "empty",
|
|
4288
|
+
className: "text-sm text-muted-foreground",
|
|
4289
|
+
children: empty
|
|
4290
|
+
});
|
|
4291
|
+
}
|
|
4192
4292
|
const out = [];
|
|
4193
4293
|
let run = [];
|
|
4194
4294
|
const flush = () => {
|
|
@@ -4210,8 +4310,11 @@ function SectionChildren({ nodes }) {
|
|
|
4210
4310
|
flush();
|
|
4211
4311
|
return out;
|
|
4212
4312
|
}
|
|
4213
|
-
function renderSection(nodes) {
|
|
4214
|
-
return jsx(SectionChildren, {
|
|
4313
|
+
function renderSection(nodes, owner) {
|
|
4314
|
+
return jsx(SectionChildren, {
|
|
4315
|
+
nodes,
|
|
4316
|
+
owner
|
|
4317
|
+
});
|
|
4215
4318
|
}
|
|
4216
4319
|
function Layout({ node }) {
|
|
4217
4320
|
return jsx("div", {
|
|
@@ -4333,17 +4436,14 @@ function pageHost(page) {
|
|
|
4333
4436
|
}
|
|
4334
4437
|
//#endregion
|
|
4335
4438
|
//#region src/forms/declarative-form.tsx
|
|
4336
|
-
function
|
|
4337
|
-
const translated = t(key);
|
|
4338
|
-
return translated === key ? fallback : translated;
|
|
4339
|
-
}
|
|
4340
|
-
function issueText(issue, t, i18nPrefix) {
|
|
4439
|
+
function issueText(issue, caps, i18nPrefix) {
|
|
4341
4440
|
return resolveValidationMessage({
|
|
4342
4441
|
code: issue.code ?? "",
|
|
4343
4442
|
message: issue.message,
|
|
4344
|
-
...issue.
|
|
4443
|
+
...issue.check === void 0 ? {} : { check: issue.check }
|
|
4345
4444
|
}, {
|
|
4346
|
-
|
|
4445
|
+
has: caps.has,
|
|
4446
|
+
t: caps.t,
|
|
4347
4447
|
...issue.field === "_form" ? {} : { fieldKey: `resources.${i18nPrefix}.fields.${issue.field}` }
|
|
4348
4448
|
});
|
|
4349
4449
|
}
|
|
@@ -4410,7 +4510,9 @@ function DeclarativeFormBody(props) {
|
|
|
4410
4510
|
...props.surface === void 0 ? {} : { surface: props.surface },
|
|
4411
4511
|
...props.onSubmit === void 0 ? {} : { onSubmit: props.onSubmit }
|
|
4412
4512
|
});
|
|
4413
|
-
const
|
|
4513
|
+
const formControl = useFormControlOf(form);
|
|
4514
|
+
const chromeT = useChromeT();
|
|
4515
|
+
const caps = useI18nCaps();
|
|
4414
4516
|
const disabled = props.disabled === true;
|
|
4415
4517
|
const uid = useId();
|
|
4416
4518
|
const leafIds = useMemo(() => ({
|
|
@@ -4420,8 +4522,8 @@ function DeclarativeFormBody(props) {
|
|
|
4420
4522
|
const formErrors = form.errors.filter((issue) => issue.field === "_form");
|
|
4421
4523
|
const retryableChecks = [];
|
|
4422
4524
|
for (const issue of form.checkOutcome?.blockers ?? []) {
|
|
4423
|
-
const checkId = issue.
|
|
4424
|
-
if (issue.
|
|
4525
|
+
const checkId = issue.check?.id;
|
|
4526
|
+
if (issue.check?.cause === void 0 || typeof checkId !== "string") continue;
|
|
4425
4527
|
if (!retryableChecks.includes(checkId)) retryableChecks.push(checkId);
|
|
4426
4528
|
}
|
|
4427
4529
|
const formWarnings = form.warnings.filter((issue) => issue.field === "_form");
|
|
@@ -4434,7 +4536,7 @@ function DeclarativeFormBody(props) {
|
|
|
4434
4536
|
formErrors.length > 0 && jsx("div", {
|
|
4435
4537
|
role: "alert",
|
|
4436
4538
|
className: "mb-4 flex flex-col gap-1 rounded-md border border-destructive/30 bg-destructive/10 px-3 py-2 text-sm text-destructive",
|
|
4437
|
-
children: formErrors.map((issue, index) => jsx("p", { children: issueText(issue,
|
|
4539
|
+
children: formErrors.map((issue, index) => jsx("p", { children: issueText(issue, caps, prefix) }, index))
|
|
4438
4540
|
}),
|
|
4439
4541
|
retryableChecks.length > 0 && jsx("div", {
|
|
4440
4542
|
className: "mb-4 flex flex-wrap gap-2",
|
|
@@ -4444,25 +4546,28 @@ function DeclarativeFormBody(props) {
|
|
|
4444
4546
|
size: "sm",
|
|
4445
4547
|
"data-testid": `form-check-retry:${checkId}`,
|
|
4446
4548
|
onClick: () => void form.revalidate(),
|
|
4447
|
-
children:
|
|
4549
|
+
children: chromeT("actions.retry", { defaultValue: "Retry" })
|
|
4448
4550
|
}, checkId))
|
|
4449
4551
|
}),
|
|
4450
4552
|
formWarnings.length > 0 && jsx("div", {
|
|
4451
4553
|
role: "status",
|
|
4452
4554
|
"aria-live": "polite",
|
|
4453
4555
|
className: "mb-4 flex flex-col gap-1 rounded-md border border-warning/50 bg-warning/10 px-3 py-2 text-sm text-warning-emphasis",
|
|
4454
|
-
children: formWarnings.map((issue, index) => jsx("p", { children: issueText(issue,
|
|
4556
|
+
children: formWarnings.map((issue, index) => jsx("p", { children: issueText(issue, caps, prefix) }, index))
|
|
4455
4557
|
}),
|
|
4456
|
-
jsx(
|
|
4457
|
-
|
|
4458
|
-
|
|
4459
|
-
|
|
4460
|
-
|
|
4461
|
-
|
|
4462
|
-
|
|
4463
|
-
|
|
4464
|
-
|
|
4465
|
-
|
|
4558
|
+
jsx(FormControlProvider, {
|
|
4559
|
+
value: formControl,
|
|
4560
|
+
children: jsx(ScreenTree, {
|
|
4561
|
+
region: "form-body",
|
|
4562
|
+
resource: prefix,
|
|
4563
|
+
mode: "create",
|
|
4564
|
+
compiled: form.screen,
|
|
4565
|
+
controller: form,
|
|
4566
|
+
leafIds,
|
|
4567
|
+
...props.renderField === void 0 ? {} : { renderField: props.renderField },
|
|
4568
|
+
disabled,
|
|
4569
|
+
...props.bodyClassName === void 0 ? {} : { className: props.bodyClassName }
|
|
4570
|
+
})
|
|
4466
4571
|
}),
|
|
4467
4572
|
props.slots?.footer === void 0 ? jsx("div", {
|
|
4468
4573
|
className: "mt-4 flex justify-end",
|
|
@@ -4470,7 +4575,7 @@ function DeclarativeFormBody(props) {
|
|
|
4470
4575
|
type: "submit",
|
|
4471
4576
|
disabled: !form.canSubmit || disabled,
|
|
4472
4577
|
onMouseDown: (event) => event.preventDefault(),
|
|
4473
|
-
children: props.submitLabel ??
|
|
4578
|
+
children: props.submitLabel ?? chromeT("actions.save", { defaultValue: "Save" })
|
|
4474
4579
|
})
|
|
4475
4580
|
}) : typeof props.slots.footer === "function" ? props.slots.footer({ canSubmit: form.canSubmit && !disabled }) : props.slots.footer
|
|
4476
4581
|
]
|
|
@@ -4484,28 +4589,39 @@ function DeclarativeFormBody(props) {
|
|
|
4484
4589
|
}
|
|
4485
4590
|
//#endregion
|
|
4486
4591
|
//#region src/export/format-option-labels.ts
|
|
4487
|
-
function exportFormatOptionKey(formatId, field, value) {
|
|
4488
|
-
return value === void 0 ? `export.format.${formatId}.${field}.label` : `export.format.${formatId}.${field}.option.${value}`;
|
|
4489
|
-
}
|
|
4490
4592
|
function localizeFormatOptions(caps, format) {
|
|
4491
4593
|
const fields = format.options;
|
|
4492
4594
|
if (fields === void 0) return void 0;
|
|
4493
4595
|
const out = {};
|
|
4494
4596
|
for (const [name, field] of Object.entries(fields)) {
|
|
4495
4597
|
const declared = field;
|
|
4598
|
+
const label = resolveContentSlot(caps, [exportFormatFieldKey(format.id, name)], {
|
|
4599
|
+
default: declaredText("field", field, "label"),
|
|
4600
|
+
humanize: () => humanizeKey(name)
|
|
4601
|
+
});
|
|
4602
|
+
const options = declared.options?.map((option) => ({
|
|
4603
|
+
value: String(option.value),
|
|
4604
|
+
label: resolveContentSlot(caps, [exportFormatOptionKey(format.id, name, String(option.value))], {
|
|
4605
|
+
default: fieldOptionText(field, String(option.value)),
|
|
4606
|
+
humanize: () => String(option.value)
|
|
4607
|
+
})
|
|
4608
|
+
}));
|
|
4609
|
+
const declaredTexts = typeof declared.texts === "object" && declared.texts !== null ? declared.texts : {};
|
|
4610
|
+
const declaredOptions = declaredTexts.options ?? {};
|
|
4611
|
+
const texts = {
|
|
4612
|
+
...declaredTexts,
|
|
4613
|
+
label,
|
|
4614
|
+
...options === void 0 ? {} : { options: Object.fromEntries(options.map(({ value, label: optionLabel }) => {
|
|
4615
|
+
const entry = declaredOptions[value];
|
|
4616
|
+
return [value, typeof entry === "object" && entry !== null ? {
|
|
4617
|
+
...entry,
|
|
4618
|
+
label: optionLabel
|
|
4619
|
+
} : optionLabel];
|
|
4620
|
+
})) }
|
|
4621
|
+
};
|
|
4496
4622
|
out[name] = {
|
|
4497
4623
|
...field,
|
|
4498
|
-
|
|
4499
|
-
default: typeof declared.label === "string" ? declared.label : void 0,
|
|
4500
|
-
humanize: () => humanizeKey(name)
|
|
4501
|
-
}),
|
|
4502
|
-
...declared.options === void 0 ? {} : { options: declared.options.map((option) => ({
|
|
4503
|
-
...option,
|
|
4504
|
-
label: resolveContentSlot(caps, [exportFormatOptionKey(format.id, name, String(option.value))], {
|
|
4505
|
-
default: option.label,
|
|
4506
|
-
humanize: () => String(option.value)
|
|
4507
|
-
})
|
|
4508
|
-
})) }
|
|
4624
|
+
texts
|
|
4509
4625
|
};
|
|
4510
4626
|
}
|
|
4511
4627
|
return out;
|
|
@@ -4544,10 +4660,14 @@ function ExportOptionsDirty({ dirty }) {
|
|
|
4544
4660
|
return null;
|
|
4545
4661
|
}
|
|
4546
4662
|
function getNoRunState() {}
|
|
4547
|
-
function ExportDialog({ open, onOpenChange, resource, target, visibleColumns, estimatedCount }) {
|
|
4663
|
+
function ExportDialog({ open, onOpenChange, resource: resourceInput, target, visibleColumns, estimatedCount, scope }) {
|
|
4664
|
+
const resource = routeResourceKey(resourceInput);
|
|
4548
4665
|
const t = useOptionalT();
|
|
4549
4666
|
const dialogTestId = useTestIds().exportDialog(resource);
|
|
4550
|
-
const { formats, start, exporter, available, error, loading } = useResourceExport(resource, {
|
|
4667
|
+
const { formats, start, exporter, available, error, loading } = useResourceExport(resource, {
|
|
4668
|
+
load: open,
|
|
4669
|
+
...scope === void 0 ? {} : { scope }
|
|
4670
|
+
});
|
|
4551
4671
|
const [mode, setMode] = React.useState("form");
|
|
4552
4672
|
const [formatIdOverride, setFormatIdOverride] = React.useState(void 0);
|
|
4553
4673
|
const [columnsMode, setColumnsMode] = React.useState("visible");
|
|
@@ -4569,6 +4689,15 @@ function ExportDialog({ open, onOpenChange, resource, target, visibleColumns, es
|
|
|
4569
4689
|
]);
|
|
4570
4690
|
const requestColumns = columnsMode === "visible" && visibleColumns.length > 0 ? exportableVisible : allowedListColumns;
|
|
4571
4691
|
const selectedFormat = formats.find((f) => f.id === formatIdOverride) ?? formats[0];
|
|
4692
|
+
const reportedLabels = React.useRef(new Set());
|
|
4693
|
+
React.useEffect(() => {
|
|
4694
|
+
if (process.env.NODE_ENV === "production") return;
|
|
4695
|
+
for (const format of formats) {
|
|
4696
|
+
if ((Object.hasOwn(format, "label") ? format.label : void 0) === void 0 || reportedLabels.current.has(format.id)) continue;
|
|
4697
|
+
reportedLabels.current.add(format.id);
|
|
4698
|
+
console.error(`[ExportDialog] export format '${format.id}' declares label, which was removed: write texts — the text the bundle holds at common.${exportFormatLabelKey(format.id)} (see docs/i18n-guide.md).`);
|
|
4699
|
+
}
|
|
4700
|
+
}, [formats]);
|
|
4572
4701
|
const caps = useI18nCaps();
|
|
4573
4702
|
const localizedOptions = React.useMemo(() => selectedFormat === void 0 ? void 0 : localizeFormatOptions(caps, selectedFormat), [caps, selectedFormat]);
|
|
4574
4703
|
const wasOpenRef = React.useRef(open);
|
|
@@ -4654,14 +4783,15 @@ function ExportDialog({ open, onOpenChange, resource, target, visibleColumns, es
|
|
|
4654
4783
|
})] }), jsx(ExportProgressView, {
|
|
4655
4784
|
state: runState,
|
|
4656
4785
|
dialogTestId,
|
|
4657
|
-
t
|
|
4786
|
+
t,
|
|
4787
|
+
caps
|
|
4658
4788
|
})] }) : jsxs(Fragment$1, { children: [jsxs(DialogHeader, { children: [jsx(DialogTitle, { children: titleText }), jsx(DialogDescription, {
|
|
4659
4789
|
className: "sr-only",
|
|
4660
4790
|
children: titleText
|
|
4661
4791
|
})] }), !available ? jsx("p", {
|
|
4662
4792
|
role: "alert",
|
|
4663
4793
|
"data-testid": `${dialogTestId}:unavailable`,
|
|
4664
|
-
children: error
|
|
4794
|
+
children: error === void 0 || error === null ? t("export.dialog.unavailable") : resolveErrorText(error, caps)
|
|
4665
4795
|
}) : jsxs(Fragment$1, { children: [
|
|
4666
4796
|
loading ? jsx("p", {
|
|
4667
4797
|
role: "status",
|
|
@@ -4682,7 +4812,10 @@ function ExportDialog({ open, onOpenChange, resource, target, visibleColumns, es
|
|
|
4682
4812
|
}), jsx(SelectContent, { children: formats.map((format) => jsx(SelectItem, {
|
|
4683
4813
|
value: format.id,
|
|
4684
4814
|
"data-testid": `${dialogTestId}:format:option:${format.id}`,
|
|
4685
|
-
children:
|
|
4815
|
+
children: resolveContentSlot(caps, [exportFormatLabelKey(format.id)], {
|
|
4816
|
+
default: declaredText("export-format", format, "label"),
|
|
4817
|
+
humanize: () => format.id
|
|
4818
|
+
})
|
|
4686
4819
|
}, format.id)) })]
|
|
4687
4820
|
})]
|
|
4688
4821
|
}),
|
|
@@ -4737,7 +4870,7 @@ function ExportDialog({ open, onOpenChange, resource, target, visibleColumns, es
|
|
|
4737
4870
|
})
|
|
4738
4871
|
});
|
|
4739
4872
|
}
|
|
4740
|
-
function ExportProgressView({ state, dialogTestId, t }) {
|
|
4873
|
+
function ExportProgressView({ state, dialogTestId, t, caps }) {
|
|
4741
4874
|
if (state === void 0) return null;
|
|
4742
4875
|
if (state.status === "running") {
|
|
4743
4876
|
const total = state.progress?.total;
|
|
@@ -4758,7 +4891,7 @@ function ExportProgressView({ state, dialogTestId, t }) {
|
|
|
4758
4891
|
if (state.status === "failed") return jsx("p", {
|
|
4759
4892
|
role: "alert",
|
|
4760
4893
|
"data-testid": `${dialogTestId}:error`,
|
|
4761
|
-
children: `${t("export.dialog.failed")}: ${state.error
|
|
4894
|
+
children: `${t("export.dialog.failed")}: ${resolveErrorText(state.error, caps)}`
|
|
4762
4895
|
});
|
|
4763
4896
|
if (state.status === "canceled") return jsx("p", {
|
|
4764
4897
|
"data-testid": `${dialogTestId}:canceled`,
|
|
@@ -4771,6 +4904,7 @@ function ExportProgressView({ state, dialogTestId, t }) {
|
|
|
4771
4904
|
function BulkSummary(props) {
|
|
4772
4905
|
const loaded = useLoaded(loadBulkSummary);
|
|
4773
4906
|
const t = useOptionalT();
|
|
4907
|
+
const chromeT = useChromeT();
|
|
4774
4908
|
const failed = (retry) => jsxs("div", {
|
|
4775
4909
|
role: "alert",
|
|
4776
4910
|
className: "flex items-center gap-2 text-sm",
|
|
@@ -4779,7 +4913,7 @@ function BulkSummary(props) {
|
|
|
4779
4913
|
size: "sm",
|
|
4780
4914
|
variant: "outline",
|
|
4781
4915
|
onClick: retry,
|
|
4782
|
-
children:
|
|
4916
|
+
children: chromeT("actions.retry", { defaultValue: "Retry" })
|
|
4783
4917
|
})]
|
|
4784
4918
|
});
|
|
4785
4919
|
if (loaded.error !== void 0) return failed(loaded.retry);
|
|
@@ -4797,13 +4931,14 @@ function BulkFlowButton({ resource, actionName, ids, onSelectionChange }) {
|
|
|
4797
4931
|
const control = useFlowRowResolver(resource)(actionName, void 0);
|
|
4798
4932
|
const chrome = useFlowChrome(resource, actionName);
|
|
4799
4933
|
const def = app.registry.get(resource);
|
|
4800
|
-
const
|
|
4934
|
+
const compiled = useMemo(() => {
|
|
4801
4935
|
try {
|
|
4802
|
-
return def === void 0 ?
|
|
4936
|
+
return def === void 0 ? void 0 : compileFlow(def, actionName);
|
|
4803
4937
|
} catch {
|
|
4804
|
-
return
|
|
4938
|
+
return;
|
|
4805
4939
|
}
|
|
4806
4940
|
}, [def, actionName]);
|
|
4941
|
+
const each = compiled?.each ?? false;
|
|
4807
4942
|
const [running, setRunning] = useState(false);
|
|
4808
4943
|
const [summary, setSummary] = useState(null);
|
|
4809
4944
|
const testid = tid.bulkAction(resource, actionName);
|
|
@@ -4811,7 +4946,10 @@ function BulkFlowButton({ resource, actionName, ids, onSelectionChange }) {
|
|
|
4811
4946
|
const variant = chrome.destructive ? "destructive" : "outline";
|
|
4812
4947
|
const overLimit = each && ids.length > BULK_EACH_LIMIT;
|
|
4813
4948
|
if (overLimit || control.state === "disabled") return jsx(DisabledAction, {
|
|
4814
|
-
reason: overLimit ?
|
|
4949
|
+
reason: overLimit ? t("flow.bulkLimit", {
|
|
4950
|
+
defaultValue: `Select at most ${BULK_EACH_LIMIT}.`,
|
|
4951
|
+
count: BULK_EACH_LIMIT
|
|
4952
|
+
}) : control.reason,
|
|
4815
4953
|
label: chrome.label,
|
|
4816
4954
|
icon: chrome.icon,
|
|
4817
4955
|
testid,
|
|
@@ -4873,14 +5011,15 @@ function BulkFlowButton({ resource, actionName, ids, onSelectionChange }) {
|
|
|
4873
5011
|
actionName,
|
|
4874
5012
|
label: chrome.labelText,
|
|
4875
5013
|
total: summary.total,
|
|
4876
|
-
progress: summary.progress
|
|
5014
|
+
progress: summary.progress,
|
|
5015
|
+
texts: compiled?.flow.texts
|
|
4877
5016
|
}),
|
|
4878
5017
|
jsx(DialogFooter, { children: jsx(Button, {
|
|
4879
5018
|
type: "button",
|
|
4880
5019
|
variant: "outline",
|
|
4881
5020
|
"data-testid": `${tid.bulkSummary(resource, actionName)}:close`,
|
|
4882
5021
|
onClick: () => setSummary(null),
|
|
4883
|
-
children:
|
|
5022
|
+
children: t("flow.close", { defaultValue: "Close" })
|
|
4884
5023
|
}) })
|
|
4885
5024
|
]
|
|
4886
5025
|
})
|
|
@@ -4896,10 +5035,12 @@ function orderBulkActions(actions) {
|
|
|
4896
5035
|
seq: index
|
|
4897
5036
|
})).sort((a, b) => a.weight - b.weight || a.seq - b.seq).map(({ action }) => action);
|
|
4898
5037
|
}
|
|
4899
|
-
function BulkActionToolbar({ resource, table, selectedRecords, selectedIds, onClearSelection, onSelectionChange, className, visibleColumns }) {
|
|
5038
|
+
function BulkActionToolbar({ resource: resourceInput, table, selectedRecords, selectedIds, onClearSelection, onSelectionChange, className, visibleColumns, scope, actions }) {
|
|
5039
|
+
const resource = routeResourceKey(resourceInput);
|
|
4900
5040
|
const app = useApp();
|
|
4901
5041
|
const tid = useTestIds();
|
|
4902
5042
|
const t = useOptionalT();
|
|
5043
|
+
const chromeT = useChromeT();
|
|
4903
5044
|
const def = app.registry.get(resource);
|
|
4904
5045
|
const records = selectedRecords ?? (table ? table.getFilteredSelectedRowModel().rows.map((r) => r.original) : []);
|
|
4905
5046
|
const ids = selectedIds ?? records.map((record) => record.id);
|
|
@@ -4932,7 +5073,8 @@ function BulkActionToolbar({ resource, table, selectedRecords, selectedIds, onCl
|
|
|
4932
5073
|
className: "mx-0.5 h-6 w-px bg-border",
|
|
4933
5074
|
"aria-hidden": "true"
|
|
4934
5075
|
}),
|
|
4935
|
-
|
|
5076
|
+
actions !== void 0 ? actions : null,
|
|
5077
|
+
actions === void 0 && bulkActions.map((action) => jsx(BulkFlowButton, {
|
|
4936
5078
|
resource,
|
|
4937
5079
|
actionName: action.name,
|
|
4938
5080
|
ids,
|
|
@@ -4941,10 +5083,11 @@ function BulkActionToolbar({ resource, table, selectedRecords, selectedIds, onCl
|
|
|
4941
5083
|
else if (next.length === 0) handleClear();
|
|
4942
5084
|
}
|
|
4943
5085
|
}, action.name)),
|
|
4944
|
-
jsx(BulkExportButton, {
|
|
5086
|
+
actions === void 0 && jsx(BulkExportButton, {
|
|
4945
5087
|
resource,
|
|
4946
5088
|
ids,
|
|
4947
|
-
visibleColumns: visibleColumns ?? []
|
|
5089
|
+
visibleColumns: visibleColumns ?? [],
|
|
5090
|
+
...scope === void 0 ? {} : { scope }
|
|
4948
5091
|
}),
|
|
4949
5092
|
jsxs(Button, {
|
|
4950
5093
|
type: "button",
|
|
@@ -4957,7 +5100,7 @@ function BulkActionToolbar({ resource, table, selectedRecords, selectedIds, onCl
|
|
|
4957
5100
|
"aria-hidden": "true"
|
|
4958
5101
|
}), jsx("span", {
|
|
4959
5102
|
className: "hidden sm:inline",
|
|
4960
|
-
children:
|
|
5103
|
+
children: chromeT("actions.clearSelection", { defaultValue: "Clear" })
|
|
4961
5104
|
})]
|
|
4962
5105
|
})
|
|
4963
5106
|
]
|
|
@@ -4967,15 +5110,16 @@ function BulkActionToolbar({ resource, table, selectedRecords, selectedIds, onCl
|
|
|
4967
5110
|
className: "h-20 sm:h-24"
|
|
4968
5111
|
})] });
|
|
4969
5112
|
}
|
|
4970
|
-
function BulkExportButton({ resource, ids, visibleColumns }) {
|
|
5113
|
+
function BulkExportButton({ resource, ids, visibleColumns, scope }) {
|
|
4971
5114
|
if (!(useOptionalDataRuntime() !== null)) return null;
|
|
4972
5115
|
return jsx(BulkExportButtonActive, {
|
|
4973
5116
|
resource,
|
|
4974
5117
|
ids,
|
|
4975
|
-
visibleColumns
|
|
5118
|
+
visibleColumns,
|
|
5119
|
+
...scope === void 0 ? {} : { scope }
|
|
4976
5120
|
});
|
|
4977
5121
|
}
|
|
4978
|
-
function BulkExportButtonActive({ resource, ids, visibleColumns }) {
|
|
5122
|
+
function BulkExportButtonActive({ resource, ids, visibleColumns, scope }) {
|
|
4979
5123
|
const tid = useTestIds();
|
|
4980
5124
|
const t = useOptionalT();
|
|
4981
5125
|
const { available, prefetch } = useResourceExport(resource);
|
|
@@ -5010,23 +5154,49 @@ function BulkExportButtonActive({ resource, ids, visibleColumns }) {
|
|
|
5010
5154
|
onOpenChange: setOpen,
|
|
5011
5155
|
resource,
|
|
5012
5156
|
target,
|
|
5013
|
-
visibleColumns
|
|
5157
|
+
visibleColumns,
|
|
5158
|
+
...scope === void 0 ? {} : { scope }
|
|
5014
5159
|
})] });
|
|
5015
5160
|
}
|
|
5016
5161
|
//#endregion
|
|
5017
5162
|
//#region src/list-kit/pieces/list-bulk-bar.tsx
|
|
5163
|
+
function LinkDetachBulkButton(props) {
|
|
5164
|
+
const { selection } = useResourceListContext();
|
|
5165
|
+
const { resource } = useListSurface();
|
|
5166
|
+
const t = useOptionalT();
|
|
5167
|
+
const [busy, setBusy] = useState(false);
|
|
5168
|
+
return jsxs(Button, {
|
|
5169
|
+
type: "button",
|
|
5170
|
+
size: "sm",
|
|
5171
|
+
variant: "ghost",
|
|
5172
|
+
className: "h-8 gap-1.5 rounded-full px-2.5 text-destructive",
|
|
5173
|
+
disabled: busy,
|
|
5174
|
+
"data-testid": `list-link-detach-bulk:${resource}`,
|
|
5175
|
+
onClick: () => {
|
|
5176
|
+
setBusy(true);
|
|
5177
|
+
props.onDetach(selection.records).finally(() => setBusy(false));
|
|
5178
|
+
},
|
|
5179
|
+
children: [jsx(IconUnlink, {
|
|
5180
|
+
className: "size-4",
|
|
5181
|
+
"aria-hidden": "true"
|
|
5182
|
+
}), t("field.link.detach", { defaultValue: "Detach" })]
|
|
5183
|
+
});
|
|
5184
|
+
}
|
|
5018
5185
|
function ListBulkBarView() {
|
|
5019
5186
|
usePieceMount("bulk-bar");
|
|
5020
|
-
const { selection, columns } = useResourceListContext();
|
|
5021
|
-
const { resource, tableView } = useListSurface();
|
|
5022
|
-
|
|
5187
|
+
const { selection, columns, scope } = useResourceListContext();
|
|
5188
|
+
const { resource, tableView, readOnly, linkEditing } = useListSurface();
|
|
5189
|
+
const detach = readOnly ? linkEditing?.detach : void 0;
|
|
5190
|
+
if (!tableView || readOnly && detach === void 0 || selection.ids.length === 0) return null;
|
|
5023
5191
|
return jsx(BulkActionToolbar, {
|
|
5192
|
+
...detach === void 0 ? {} : { actions: jsx(LinkDetachBulkButton, { onDetach: detach.onDetach }) },
|
|
5024
5193
|
resource,
|
|
5025
5194
|
selectedRecords: selection.records,
|
|
5026
5195
|
selectedIds: selection.ids,
|
|
5027
5196
|
onClearSelection: selection.clear,
|
|
5028
5197
|
onSelectionChange: selection.set,
|
|
5029
|
-
visibleColumns: columns.visible
|
|
5198
|
+
visibleColumns: columns.visible,
|
|
5199
|
+
scope
|
|
5030
5200
|
});
|
|
5031
5201
|
}
|
|
5032
5202
|
const ListBulkBar = listPiece("ListBulkBar", ListBulkBarView);
|
|
@@ -5108,9 +5278,11 @@ function DataTableViewOptions({ table, getColumnLabel, isColumnRequired, getTest
|
|
|
5108
5278
|
}
|
|
5109
5279
|
//#endregion
|
|
5110
5280
|
//#region src/list-kit/columns-toggle.tsx
|
|
5111
|
-
function ColumnsToggle({ resource, def, allAvailable, required, visibleColumns, defaultVisible, onChange, onResetSizes, onRestoreDefaults }) {
|
|
5281
|
+
function ColumnsToggle({ resource: resourceInput, def, allAvailable, required, visibleColumns, defaultVisible, onChange, onResetSizes, onRestoreDefaults }) {
|
|
5282
|
+
const resource = routeResourceKey(resourceInput);
|
|
5112
5283
|
const t = useT();
|
|
5113
5284
|
const columnsCaps = useI18nCaps(resource);
|
|
5285
|
+
const app = useApp();
|
|
5114
5286
|
const visibleSet = useMemo(() => new Set(visibleColumns), [visibleColumns]);
|
|
5115
5287
|
const requiredSet = useMemo(() => new Set(required), [required]);
|
|
5116
5288
|
const defaultSet = useMemo(() => new Set(defaultVisible), [defaultVisible]);
|
|
@@ -5175,8 +5347,9 @@ function ColumnsToggle({ resource, def, allAvailable, required, visibleColumns,
|
|
|
5175
5347
|
testid: `list-columns-restore-defaults:${resource}`
|
|
5176
5348
|
}] : []],
|
|
5177
5349
|
getColumnLabel: (column) => {
|
|
5178
|
-
const
|
|
5179
|
-
return resolveFieldLabel(columnsCaps,
|
|
5350
|
+
const target = columnTarget(app.registry, resource, column.id);
|
|
5351
|
+
if (target !== void 0) return resolveFieldLabel(capsOf(columnsCaps, target.resource), target.name, declaredText("field", target.field, "label"));
|
|
5352
|
+
return resolveFieldLabel(columnsCaps, column.id, declaredText("field", def.fields?.[column.id], "label"));
|
|
5180
5353
|
},
|
|
5181
5354
|
isColumnRequired: (column) => requiredSet.has(column.id),
|
|
5182
5355
|
getTestId: (role, columnId) => {
|
|
@@ -5213,12 +5386,16 @@ function ListColumnsView() {
|
|
|
5213
5386
|
const ListColumns = listPiece("ListColumns", ListColumnsView);
|
|
5214
5387
|
//#endregion
|
|
5215
5388
|
//#region src/resource/create-button.tsx
|
|
5216
|
-
function CreateButton({ resource, href, testid, size = "sm", variant, label, className, onOpen }) {
|
|
5389
|
+
function CreateButton({ resource: resourceInput, href, testid, size = "sm", variant, label, className, onOpen }) {
|
|
5390
|
+
const resource = routeResourceKey(resourceInput);
|
|
5217
5391
|
const t = useOptionalT();
|
|
5218
5392
|
const Link = useLinkComponent();
|
|
5219
5393
|
const availability = useAvailability(resource, "create");
|
|
5220
5394
|
const gate = useEntryGate();
|
|
5221
|
-
const text = label ?? t("actions.create", {
|
|
5395
|
+
const text = label ?? t("actions.create", {
|
|
5396
|
+
resource,
|
|
5397
|
+
defaultValue: "Create"
|
|
5398
|
+
});
|
|
5222
5399
|
if (onOpen !== void 0) {
|
|
5223
5400
|
const answer = gate(resource, "create");
|
|
5224
5401
|
if (answer.state === "disabled") return jsx(DisabledAction, {
|
|
@@ -5289,7 +5466,8 @@ const ListCreate = listPiece("ListCreate", ListCreateView);
|
|
|
5289
5466
|
//#endregion
|
|
5290
5467
|
//#region src/export/export-button.tsx
|
|
5291
5468
|
const warnedProviderQueryResources = new Set();
|
|
5292
|
-
function ExportButton({ resource, className }) {
|
|
5469
|
+
function ExportButton({ resource: resourceInput, className }) {
|
|
5470
|
+
const resource = routeResourceKey(resourceInput);
|
|
5293
5471
|
if (!(useOptionalDataRuntime() !== null)) return null;
|
|
5294
5472
|
return jsx(ExportButtonActive, {
|
|
5295
5473
|
resource,
|
|
@@ -5310,36 +5488,19 @@ function ExportButtonActive({ resource, className }) {
|
|
|
5310
5488
|
const tid = useTestIds();
|
|
5311
5489
|
const t = useOptionalT();
|
|
5312
5490
|
const [open, setOpen] = React.useState(false);
|
|
5313
|
-
const
|
|
5314
|
-
resource,
|
|
5315
|
-
def: list.def
|
|
5316
|
-
}), [resource, list.def]);
|
|
5491
|
+
const sent = list.query.sent;
|
|
5317
5492
|
const target = React.useMemo(() => {
|
|
5318
|
-
|
|
5319
|
-
|
|
5320
|
-
pinned: list.filters.value,
|
|
5321
|
-
group: list.filters.group,
|
|
5322
|
-
search: list.filters.search,
|
|
5323
|
-
searchDecl: list.filters.searchEnabled ? list.filters.searchDecl ?? list.def?.views?.list?.search : void 0,
|
|
5324
|
-
searchContext
|
|
5325
|
-
});
|
|
5326
|
-
const sort = list.sorting.value;
|
|
5493
|
+
if (sent === null) return null;
|
|
5494
|
+
const { filter, sort } = sent;
|
|
5327
5495
|
return {
|
|
5328
5496
|
kind: "filter",
|
|
5329
5497
|
...filter === void 0 ? {} : { filter },
|
|
5330
5498
|
...sort.length > 0 ? { sort } : {}
|
|
5331
5499
|
};
|
|
5332
|
-
}, [
|
|
5333
|
-
|
|
5334
|
-
|
|
5335
|
-
|
|
5336
|
-
list.filters.search,
|
|
5337
|
-
list.filters.searchDecl,
|
|
5338
|
-
list.filters.searchEnabled,
|
|
5339
|
-
list.def,
|
|
5340
|
-
searchContext,
|
|
5341
|
-
list.sorting.value
|
|
5342
|
-
]);
|
|
5500
|
+
}, [sent]);
|
|
5501
|
+
const lastTarget = React.useRef(target);
|
|
5502
|
+
if (target !== null) lastTarget.current = target;
|
|
5503
|
+
const dialogTarget = target ?? lastTarget.current;
|
|
5343
5504
|
React.useEffect(() => {
|
|
5344
5505
|
if (!available || !capability.allowed) return;
|
|
5345
5506
|
if (list.providerQuery.value === void 0) return;
|
|
@@ -5353,7 +5514,9 @@ function ExportButtonActive({ resource, className }) {
|
|
|
5353
5514
|
resource
|
|
5354
5515
|
]);
|
|
5355
5516
|
if (!available || !capability.allowed || list.isForbidden) return null;
|
|
5356
|
-
|
|
5517
|
+
if (target === null && !open) return null;
|
|
5518
|
+
if (dialogTarget === null) return null;
|
|
5519
|
+
return jsxs(Fragment$1, { children: [target === null ? null : jsx(Button, {
|
|
5357
5520
|
type: "button",
|
|
5358
5521
|
variant: "outline",
|
|
5359
5522
|
size: "icon",
|
|
@@ -5372,9 +5535,10 @@ function ExportButtonActive({ resource, className }) {
|
|
|
5372
5535
|
open,
|
|
5373
5536
|
onOpenChange: setOpen,
|
|
5374
5537
|
resource,
|
|
5375
|
-
target,
|
|
5538
|
+
target: dialogTarget,
|
|
5376
5539
|
visibleColumns: list.columns.visible,
|
|
5377
|
-
estimatedCount: list.pagination.total
|
|
5540
|
+
estimatedCount: list.pagination.total,
|
|
5541
|
+
scope: list.scope
|
|
5378
5542
|
})] });
|
|
5379
5543
|
}
|
|
5380
5544
|
//#endregion
|
|
@@ -5382,15 +5546,16 @@ function ExportButtonActive({ resource, className }) {
|
|
|
5382
5546
|
function ListExportView() {
|
|
5383
5547
|
usePieceMount("export");
|
|
5384
5548
|
const { def } = useResourceListContext();
|
|
5385
|
-
const { resource } = useListSurface();
|
|
5386
|
-
if (!def) return null;
|
|
5549
|
+
const { resource, exportable } = useListSurface();
|
|
5550
|
+
if (!def || !exportable) return null;
|
|
5387
5551
|
return jsx(ExportButton, { resource });
|
|
5388
5552
|
}
|
|
5389
5553
|
const ListExport = listPiece("ListExport", ListExportView);
|
|
5390
5554
|
//#endregion
|
|
5391
5555
|
//#region src/list-kit/list-search-input.tsx
|
|
5392
|
-
function ListSearchInput({ resource
|
|
5393
|
-
const
|
|
5556
|
+
function ListSearchInput({ resource: resourceInput, value, onCommit, placeholder }) {
|
|
5557
|
+
const resource = routeResourceKey(resourceInput);
|
|
5558
|
+
const caps = useI18nCaps(resource);
|
|
5394
5559
|
const [text, setText] = useState(value);
|
|
5395
5560
|
const lastPushedRef = useRef(value);
|
|
5396
5561
|
const timerRef = useRef(null);
|
|
@@ -5405,7 +5570,7 @@ function ListSearchInput({ resource, label, value, onCommit, placeholder }) {
|
|
|
5405
5570
|
}, []);
|
|
5406
5571
|
return jsx(Input, {
|
|
5407
5572
|
type: "search",
|
|
5408
|
-
placeholder: placeholder ??
|
|
5573
|
+
placeholder: placeholder ?? resolveContentSlot(caps, ["list.search"]) ?? "Search",
|
|
5409
5574
|
value: text,
|
|
5410
5575
|
"data-testid": `list-search:${resource}`,
|
|
5411
5576
|
className: "h-8 w-full min-[420px]:w-72",
|
|
@@ -5423,24 +5588,18 @@ function ListSearchInput({ resource, label, value, onCommit, placeholder }) {
|
|
|
5423
5588
|
//#endregion
|
|
5424
5589
|
//#region src/list-kit/filter-builder/filter-region.tsx
|
|
5425
5590
|
function FilterRegion({ search } = {}) {
|
|
5426
|
-
const { resource, def, filters, pagination
|
|
5427
|
-
const
|
|
5428
|
-
const listCaps = useI18nCaps(resource);
|
|
5591
|
+
const { resource, def, filters, pagination } = useResourceListContext();
|
|
5592
|
+
const chromeT = useChromeT();
|
|
5429
5593
|
const testIdBase = `list-filter:${resource}`;
|
|
5594
|
+
const caps = useI18nCaps(resource);
|
|
5595
|
+
const placeholder = search === void 0 ? resolveContentSlot(caps, ["list.searchPlaceholder", "list.search"]) ?? "Search" : void 0;
|
|
5430
5596
|
const searchDecl = search === void 0 ? def?.views?.list?.search : search ?? void 0;
|
|
5431
|
-
const searchField = searchDecl !== void 0 && typeof searchDecl !== "function" ? (() => {
|
|
5432
|
-
return searchDecl.fields.map((entry) => typeof entry === "string" ? entry : entry.field).find((name) => access.fieldVisible(name));
|
|
5433
|
-
})() : void 0;
|
|
5434
|
-
const searchGenericLabel = resolveContentSlot(listCaps, ["list.search"]) ?? "Search";
|
|
5435
|
-
const searchLabel = searchField ? resolveFieldLabel(listCaps, searchField, (def?.fields[searchField])?.label) : searchGenericLabel;
|
|
5436
|
-
const searchPlaceholder = searchField ? t("toolbar.searchBy", { params: { name: searchLabel } }) : searchGenericLabel;
|
|
5437
5597
|
return jsxs("div", {
|
|
5438
5598
|
className: "flex flex-wrap items-center gap-2",
|
|
5439
5599
|
children: [
|
|
5440
5600
|
searchDecl !== void 0 && filters.searchEnabled && jsx(ListSearchInput, {
|
|
5441
5601
|
resource,
|
|
5442
|
-
|
|
5443
|
-
placeholder: searchPlaceholder,
|
|
5602
|
+
...placeholder === void 0 ? {} : { placeholder },
|
|
5444
5603
|
value: filters.search,
|
|
5445
5604
|
onCommit: (v) => {
|
|
5446
5605
|
pagination.setPage(1);
|
|
@@ -5452,19 +5611,21 @@ function FilterRegion({ search } = {}) {
|
|
|
5452
5611
|
setGroup: filters.setGroup,
|
|
5453
5612
|
available: filters.available,
|
|
5454
5613
|
fixed: filters.fixed,
|
|
5614
|
+
presets: filters.presets,
|
|
5615
|
+
setPreset: filters.setPreset,
|
|
5455
5616
|
pinnedFields: filters.pinnedFields,
|
|
5456
5617
|
pinnedValue: filters.value,
|
|
5457
5618
|
setPinnedValue: filters.set,
|
|
5458
5619
|
setPage: pagination.setPage,
|
|
5459
5620
|
testIdBase
|
|
5460
5621
|
}),
|
|
5461
|
-
filters.
|
|
5622
|
+
filters.canReset && jsx(Button, {
|
|
5462
5623
|
type: "button",
|
|
5463
5624
|
variant: "ghost",
|
|
5464
5625
|
size: "sm",
|
|
5465
5626
|
"data-testid": `${testIdBase}:clear-all`,
|
|
5466
|
-
onClick: filters.
|
|
5467
|
-
children:
|
|
5627
|
+
onClick: filters.reset,
|
|
5628
|
+
children: chromeT("filters.clearAll")
|
|
5468
5629
|
})
|
|
5469
5630
|
]
|
|
5470
5631
|
});
|
|
@@ -5578,16 +5739,23 @@ function ListLiveView() {
|
|
|
5578
5739
|
const ListLive = listPiece("ListLive", ListLiveView);
|
|
5579
5740
|
//#endregion
|
|
5580
5741
|
//#region src/saved-views/saved-views-dropdown.tsx
|
|
5581
|
-
function ViewItem({ view, onClick, testid }) {
|
|
5742
|
+
function ViewItem({ view, caps, resource, onClick, testid }) {
|
|
5743
|
+
const own = resource === void 0 ? void 0 : `resources.${resource}.${savedViewKey(view.id)}`;
|
|
5744
|
+
const declared = declaredText("saved-view", view, "label");
|
|
5745
|
+
const label = own === void 0 ? declared ?? humanizeKey(view.id) : resolveContentSlot(caps, [[`${own}.label`, own]], {
|
|
5746
|
+
default: declared,
|
|
5747
|
+
humanize: () => humanizeKey(view.id)
|
|
5748
|
+
});
|
|
5582
5749
|
return jsx("button", {
|
|
5583
5750
|
type: "button",
|
|
5584
5751
|
onClick,
|
|
5585
5752
|
"data-testid": testid,
|
|
5586
5753
|
className: "w-full text-left rounded-sm px-2 py-1.5 text-sm hover:bg-accent hover:text-accent-foreground focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring",
|
|
5587
|
-
children:
|
|
5754
|
+
children: label
|
|
5588
5755
|
});
|
|
5589
5756
|
}
|
|
5590
5757
|
function EntryRow({ entry, onApply, onRename, onFork, onDelete, forkAvailable, applyTestId, renameTestId, forkTestId, deleteTestId, tr }) {
|
|
5758
|
+
const chromeT = useChromeT();
|
|
5591
5759
|
const canRename = entry.permissions.update && onRename !== void 0;
|
|
5592
5760
|
const canFork = forkAvailable && entry.permissions.fork && onFork !== void 0;
|
|
5593
5761
|
const canDelete = entry.permissions.remove && onDelete !== void 0;
|
|
@@ -5633,8 +5801,8 @@ function EntryRow({ entry, onApply, onRename, onFork, onDelete, forkAvailable, a
|
|
|
5633
5801
|
canDelete && jsx("button", {
|
|
5634
5802
|
type: "button",
|
|
5635
5803
|
onClick: onDelete,
|
|
5636
|
-
"aria-label":
|
|
5637
|
-
title:
|
|
5804
|
+
"aria-label": chromeT("actions.delete", { defaultValue: "Delete" }),
|
|
5805
|
+
title: chromeT("actions.delete", { defaultValue: "Delete" }),
|
|
5638
5806
|
"data-testid": deleteTestId,
|
|
5639
5807
|
className: "shrink-0 rounded-sm p-1 text-muted-foreground hover:bg-accent hover:text-accent-foreground focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring",
|
|
5640
5808
|
children: jsx(IconTrash, {
|
|
@@ -5674,8 +5842,10 @@ function groupByScope(entries) {
|
|
|
5674
5842
|
}
|
|
5675
5843
|
return order.map((id) => byId.get(id));
|
|
5676
5844
|
}
|
|
5677
|
-
function SavedViewsDropdown({ adopterViews, entries, issues = [], onApply, onSaveCurrent, canSaveCurrent = true, onManage, onRenameEntry, onDeleteEntry, onForkEntry, canFork = true, getTestId, triggerLabel = "Views", iconOnly = false, triggerClassName = "h-9", popoverContentClassName = "w-64 p-1", popoverAlign = "start" }) {
|
|
5845
|
+
function SavedViewsDropdown({ resource: resourceInput, adopterViews, entries, issues = [], onApply, onSaveCurrent, canSaveCurrent = true, onManage, onRenameEntry, onDeleteEntry, onForkEntry, canFork = true, getTestId, triggerLabel = "Views", iconOnly = false, triggerClassName = "h-9", popoverContentClassName = "w-64 p-1", popoverAlign = "start" }) {
|
|
5846
|
+
const resource = resourceInput === void 0 ? void 0 : routeResourceKey(resourceInput);
|
|
5678
5847
|
const t = useOptionalT();
|
|
5848
|
+
const caps = useI18nCaps(resource);
|
|
5679
5849
|
const tr = (key, fallback) => t?.(key, { defaultValue: fallback }) ?? fallback;
|
|
5680
5850
|
const [open, setOpen] = useState(false);
|
|
5681
5851
|
const hasAdopter = adopterViews.length > 0;
|
|
@@ -5729,6 +5899,8 @@ function SavedViewsDropdown({ adopterViews, entries, issues = [], onApply, onSav
|
|
|
5729
5899
|
className: "max-h-[200px] overflow-auto",
|
|
5730
5900
|
children: adopterViews.map((v) => jsx(ViewItem, {
|
|
5731
5901
|
view: v,
|
|
5902
|
+
caps,
|
|
5903
|
+
resource,
|
|
5732
5904
|
testid: getTestId?.("view-item", v.id),
|
|
5733
5905
|
onClick: closeAfter(() => onApply(v))
|
|
5734
5906
|
}, v.id))
|
|
@@ -5785,6 +5957,7 @@ function ListSavedViewsView() {
|
|
|
5785
5957
|
const { resource, route, savedViewsUi } = useListSurface();
|
|
5786
5958
|
if (!def || !route) return null;
|
|
5787
5959
|
return jsx(SavedViewsDropdown, {
|
|
5960
|
+
resource,
|
|
5788
5961
|
adopterViews: savedViews.adopter,
|
|
5789
5962
|
entries: savedViews.entries,
|
|
5790
5963
|
issues: savedViews.issues,
|
|
@@ -5884,9 +6057,10 @@ function useOptionalFrameChrome() {
|
|
|
5884
6057
|
//#endregion
|
|
5885
6058
|
//#region src/record/frame-pieces.tsx
|
|
5886
6059
|
function useFrameId() {
|
|
5887
|
-
const { recordId, record } = useRecordFrame();
|
|
6060
|
+
const { resource, recordId, record } = useRecordFrame();
|
|
6061
|
+
const def = useApp().registry.get(resource);
|
|
5888
6062
|
if (recordId !== void 0) return recordId;
|
|
5889
|
-
const id = record
|
|
6063
|
+
const id = recordKeyOf(def, record);
|
|
5890
6064
|
return typeof id === "string" || typeof id === "number" ? String(id) : void 0;
|
|
5891
6065
|
}
|
|
5892
6066
|
function RecordTitleView() {
|
|
@@ -5897,7 +6071,7 @@ function RecordTitleView() {
|
|
|
5897
6071
|
const t = useOptionalT();
|
|
5898
6072
|
const title = useRecordTitle(resource, mode === "create" ? void 0 : id, record);
|
|
5899
6073
|
if (useFrameChrome().hostTitle === true) return null;
|
|
5900
|
-
const text = mode === "create" ? t
|
|
6074
|
+
const text = mode === "create" ? resolveResourceTitle(caps, t, resource, "createTitle", { label: resolveResourceLabel(caps, resource, "single", app.registry.get(resource)) }) : title;
|
|
5901
6075
|
if (text === "") return null;
|
|
5902
6076
|
return jsx("h2", {
|
|
5903
6077
|
className: "min-w-0 break-words text-xl font-semibold tracking-tight",
|
|
@@ -5921,6 +6095,7 @@ function RecordActionsView() {
|
|
|
5921
6095
|
resource,
|
|
5922
6096
|
record: row,
|
|
5923
6097
|
surface: mode,
|
|
6098
|
+
sources: controller.sources,
|
|
5924
6099
|
...holder === void 0 ? {} : { hideFlows: (name) => holder.suppressed(name) }
|
|
5925
6100
|
}), mode === "show" ? extraActions : null]
|
|
5926
6101
|
});
|
|
@@ -5950,9 +6125,15 @@ function FormActionsView() {
|
|
|
5950
6125
|
const app = useApp();
|
|
5951
6126
|
const navigate = useRouteNavigate();
|
|
5952
6127
|
const t = useOptionalT();
|
|
6128
|
+
const chromeT = useChromeT();
|
|
6129
|
+
const caps = useI18nCaps();
|
|
6130
|
+
const labelCaps = useI18nCaps(resource);
|
|
5953
6131
|
const tid = useTestIds();
|
|
5954
|
-
const locale = useLocale();
|
|
5955
6132
|
const id = useFrameId();
|
|
6133
|
+
const failedIssues = chrome.serverIssues;
|
|
6134
|
+
const livePlan = controller.plan;
|
|
6135
|
+
const alertIssues = useMemo(() => failedIssues === void 0 || failedIssues.length === 0 ? NO_ALERT_ISSUES : routeServerIssues({ issues: failedIssues }, (segment) => segment, (field) => field !== "_form" && (livePlan === null || livePlan.fields[field] === "active")).form, [failedIssues, livePlan]);
|
|
6136
|
+
const fieldDefs = app.registry.get(resource)?.fields;
|
|
5956
6137
|
if (mode === "show") return null;
|
|
5957
6138
|
const busy = controller.isSubmitting;
|
|
5958
6139
|
const cancel = () => {
|
|
@@ -5976,21 +6157,43 @@ function FormActionsView() {
|
|
|
5976
6157
|
const text = (issue) => resolveValidationMessage({
|
|
5977
6158
|
code: issue.code ?? "",
|
|
5978
6159
|
message: issue.message,
|
|
5979
|
-
...issue.
|
|
5980
|
-
}, {
|
|
6160
|
+
...issue.check === void 0 ? {} : { check: issue.check }
|
|
6161
|
+
}, {
|
|
6162
|
+
has: caps.has,
|
|
6163
|
+
t: caps.t
|
|
6164
|
+
});
|
|
5981
6165
|
const retryable = [];
|
|
5982
6166
|
for (const issue of controller.checkOutcome?.blockers ?? []) {
|
|
5983
|
-
const checkId = issue.
|
|
5984
|
-
if (issue.
|
|
6167
|
+
const checkId = issue.check?.id;
|
|
6168
|
+
if (issue.check?.cause === void 0 || typeof checkId !== "string") continue;
|
|
5985
6169
|
if (!retryable.includes(checkId)) retryable.push(checkId);
|
|
5986
6170
|
}
|
|
5987
|
-
const failure = chrome.submitError === void 0 ? void 0 : failureText(chrome.submitError, t,
|
|
6171
|
+
const failure = chrome.submitError === void 0 ? void 0 : failureText(chrome.submitError, chrome.submitApplied, t, caps);
|
|
6172
|
+
const serverIssues = bySeverity(alertIssues).map((issue) => {
|
|
6173
|
+
const head = issue.path[0];
|
|
6174
|
+
const name = typeof head === "string" ? head : void 0;
|
|
6175
|
+
const declared = name === void 0 ? void 0 : fieldDefs?.[name];
|
|
6176
|
+
const message = resolveValidationMessage({
|
|
6177
|
+
code: issue.code,
|
|
6178
|
+
message: issue.message,
|
|
6179
|
+
...issue.check === void 0 ? {} : { check: issue.check }
|
|
6180
|
+
}, {
|
|
6181
|
+
has: caps.has,
|
|
6182
|
+
t: caps.t,
|
|
6183
|
+
...declared === void 0 ? {} : { fieldKey: `resources.${resource}.fields.${name}` }
|
|
6184
|
+
});
|
|
6185
|
+
return {
|
|
6186
|
+
label: declared === void 0 || name === void 0 ? void 0 : resolveFieldLabel(labelCaps, name, declaredText("field", declared, "label")),
|
|
6187
|
+
message,
|
|
6188
|
+
severity: issue.severity
|
|
6189
|
+
};
|
|
6190
|
+
});
|
|
5988
6191
|
const steps = controller.steps;
|
|
5989
6192
|
const midSteps = steps !== null && steps.ids.length > 1 && steps.current !== void 0 && steps.current !== steps.ids[steps.ids.length - 1];
|
|
5990
6193
|
return jsxs("div", {
|
|
5991
6194
|
className: "flex flex-col gap-3",
|
|
5992
6195
|
children: [
|
|
5993
|
-
failure === void 0 && formErrors.length === 0 ? null : jsxs("div", {
|
|
6196
|
+
failure === void 0 && formErrors.length === 0 && serverIssues.length === 0 ? null : jsxs("div", {
|
|
5994
6197
|
role: "alert",
|
|
5995
6198
|
className: "flex items-start gap-2 rounded-md border border-destructive/30 bg-destructive/10 px-3 py-2 text-sm text-destructive",
|
|
5996
6199
|
children: [
|
|
@@ -6000,10 +6203,22 @@ function FormActionsView() {
|
|
|
6000
6203
|
}),
|
|
6001
6204
|
jsxs("div", {
|
|
6002
6205
|
className: "leading-snug",
|
|
6003
|
-
children: [
|
|
6004
|
-
|
|
6005
|
-
|
|
6006
|
-
|
|
6206
|
+
children: [
|
|
6207
|
+
failure === void 0 ? null : jsx("p", {
|
|
6208
|
+
className: "font-medium",
|
|
6209
|
+
children: failure
|
|
6210
|
+
}),
|
|
6211
|
+
serverIssues.map((issue, index) => jsxs("p", {
|
|
6212
|
+
"data-testid": `form-server-issue:${resource}:${mode}:${index}`,
|
|
6213
|
+
"data-buro-severity": issue.severity,
|
|
6214
|
+
className: issue.severity === "error" ? void 0 : "text-warning-emphasis",
|
|
6215
|
+
children: [issue.label === void 0 ? null : jsxs("span", {
|
|
6216
|
+
className: "font-medium",
|
|
6217
|
+
children: [issue.label, ": "]
|
|
6218
|
+
}), issue.message]
|
|
6219
|
+
}, index)),
|
|
6220
|
+
formErrors.map((issue) => jsx("p", { children: text(issue) }, issueKey(issue)))
|
|
6221
|
+
]
|
|
6007
6222
|
}),
|
|
6008
6223
|
failure !== void 0 && steps !== null ? jsx(Button, {
|
|
6009
6224
|
type: "button",
|
|
@@ -6012,7 +6227,7 @@ function FormActionsView() {
|
|
|
6012
6227
|
className: "ml-auto",
|
|
6013
6228
|
onMouseDown: (event) => event.preventDefault(),
|
|
6014
6229
|
onClick: () => void chrome.submit(),
|
|
6015
|
-
children:
|
|
6230
|
+
children: chromeT("actions.retry", { defaultValue: "Retry" })
|
|
6016
6231
|
}) : null
|
|
6017
6232
|
]
|
|
6018
6233
|
}),
|
|
@@ -6024,7 +6239,7 @@ function FormActionsView() {
|
|
|
6024
6239
|
size: "sm",
|
|
6025
6240
|
"data-testid": `form-check-retry:${checkId}`,
|
|
6026
6241
|
onClick: () => void controller.revalidate(),
|
|
6027
|
-
children:
|
|
6242
|
+
children: chromeT("actions.retry", { defaultValue: "Retry" })
|
|
6028
6243
|
}, checkId))
|
|
6029
6244
|
}),
|
|
6030
6245
|
formWarnings.length === 0 ? null : jsxs("div", {
|
|
@@ -6050,7 +6265,7 @@ function FormActionsView() {
|
|
|
6050
6265
|
children: [jsx(IconLoader2, {
|
|
6051
6266
|
className: "h-3.5 w-3.5 animate-spin",
|
|
6052
6267
|
"aria-hidden": "true"
|
|
6053
|
-
}), jsx("span", { children:
|
|
6268
|
+
}), jsx("span", { children: chromeT("actions.validating", { defaultValue: "Validating…" }) })]
|
|
6054
6269
|
}) : null,
|
|
6055
6270
|
jsx(Button, {
|
|
6056
6271
|
type: "button",
|
|
@@ -6058,7 +6273,7 @@ function FormActionsView() {
|
|
|
6058
6273
|
onClick: cancel,
|
|
6059
6274
|
onMouseDown: (event) => event.preventDefault(),
|
|
6060
6275
|
"data-testid": tid.formCancel(resource, mode),
|
|
6061
|
-
children:
|
|
6276
|
+
children: chromeT("actions.cancel", { defaultValue: "Cancel" })
|
|
6062
6277
|
}),
|
|
6063
6278
|
midSteps ? null : jsxs(Button, {
|
|
6064
6279
|
type: "submit",
|
|
@@ -6075,37 +6290,71 @@ function FormActionsView() {
|
|
|
6075
6290
|
children: [busy ? jsx(IconLoader2, {
|
|
6076
6291
|
className: "mr-2 h-4 w-4 animate-spin",
|
|
6077
6292
|
"aria-hidden": "true"
|
|
6078
|
-
}) : null, busy ? mode === "edit" ?
|
|
6293
|
+
}) : null, busy ? mode === "edit" ? chromeT("actions.saving", { defaultValue: "Saving…" }) : chromeT("actions.creating", { defaultValue: "Creating…" }) : chrome.submitLabel ?? (mode === "edit" ? chromeT("actions.save", { defaultValue: "Save" }) : chromeT("actions.create", { defaultValue: "Create" }))]
|
|
6079
6294
|
})
|
|
6080
6295
|
]
|
|
6081
6296
|
})
|
|
6082
6297
|
]
|
|
6083
6298
|
});
|
|
6084
6299
|
}
|
|
6300
|
+
const NO_ALERT_ISSUES = Object.freeze([]);
|
|
6301
|
+
const SEVERITY_RANK = {
|
|
6302
|
+
error: 0,
|
|
6303
|
+
warning: 1,
|
|
6304
|
+
info: 2
|
|
6305
|
+
};
|
|
6306
|
+
function bySeverity(issues) {
|
|
6307
|
+
return [...issues].sort((a, b) => (SEVERITY_RANK[a.severity] ?? 3) - (SEVERITY_RANK[b.severity] ?? 3));
|
|
6308
|
+
}
|
|
6085
6309
|
function firstPreferred(issues) {
|
|
6086
|
-
const first = issues.find((issue) => issue.
|
|
6310
|
+
const first = issues.find((issue) => issue.check?.id === void 0) ?? issues[0];
|
|
6087
6311
|
return first === void 0 ? [] : [first];
|
|
6088
6312
|
}
|
|
6089
6313
|
function issueKey(issue) {
|
|
6090
6314
|
return JSON.stringify([
|
|
6091
6315
|
issue.code ?? "",
|
|
6092
6316
|
issue.message.key,
|
|
6093
|
-
issue.
|
|
6317
|
+
issue.check?.id ?? null
|
|
6094
6318
|
]);
|
|
6095
6319
|
}
|
|
6096
|
-
function failureText(error, t,
|
|
6097
|
-
const
|
|
6098
|
-
|
|
6320
|
+
function failureText(error, settled, t, caps) {
|
|
6321
|
+
const texts = technicalTexts(error).filter((text) => text.length <= 1024);
|
|
6322
|
+
const strings = friendlyStringsFrom(t);
|
|
6323
|
+
const friendly = texts.map((text) => toFriendlyError(text, void 0, strings)).find((f) => f.replaced) ?? toFriendlyError(texts[0], void 0, strings);
|
|
6324
|
+
const applied = settled ?? ownDataProperty(error, "applied");
|
|
6325
|
+
if (applied === "partial" || applied === "unknown") {
|
|
6326
|
+
const key = applied === "partial" ? "errors.partlyApplied" : "errors.mayHaveBeenApplied";
|
|
6327
|
+
const sentence = caps.has(key) ? caps.t(key) : applied === "partial" ? "Part of this may have been saved. Check the record before trying again." : "It may have been saved. Check the record before trying again.";
|
|
6328
|
+
const lead = texts.some(isNetworkFailure) ? strings.offline : "";
|
|
6329
|
+
return lead === "" ? sentence : `${lead} ${sentence}`;
|
|
6330
|
+
}
|
|
6331
|
+
const code = ownDataProperty(error, "code");
|
|
6332
|
+
const kind = ownDataProperty(error, "kind");
|
|
6333
|
+
const hasOwnSentence = typeof code === "string" && typeof kind === "string" && kind !== "transport" && Object.hasOwn(RECORD_KIND_KEYS, kind);
|
|
6334
|
+
const message = ownDataProperty(error, "message");
|
|
6335
|
+
if (friendly.replaced && !hasOwnSentence) return (typeof code === "string" ? specificErrorText({
|
|
6336
|
+
code,
|
|
6337
|
+
...isMessageReference(message) ? { message } : {}
|
|
6338
|
+
}, caps, { record: true }) : void 0) ?? friendly.title;
|
|
6339
|
+
if (typeof code !== "string") return strings.title;
|
|
6340
|
+
const reference = {
|
|
6341
|
+
code,
|
|
6342
|
+
...isMessageReference(message) ? { message } : {}
|
|
6343
|
+
};
|
|
6344
|
+
return specificErrorText(reference, caps, { record: true }) ?? (typeof kind === "string" ? kindErrorText(kind, caps, { record: true }) : void 0) ?? strings.title;
|
|
6099
6345
|
}
|
|
6100
|
-
function
|
|
6346
|
+
function ownDataProperty(error, name) {
|
|
6101
6347
|
if (error === null || typeof error !== "object") return void 0;
|
|
6102
6348
|
try {
|
|
6103
|
-
const descriptor = Object.getOwnPropertyDescriptor(error,
|
|
6104
|
-
return descriptor !== void 0 && "value" in descriptor
|
|
6349
|
+
const descriptor = Object.getOwnPropertyDescriptor(error, name);
|
|
6350
|
+
return descriptor !== void 0 && "value" in descriptor ? descriptor.value : void 0;
|
|
6105
6351
|
} catch {
|
|
6106
6352
|
return;
|
|
6107
6353
|
}
|
|
6108
6354
|
}
|
|
6355
|
+
function isMessageReference(value) {
|
|
6356
|
+
return value !== null && typeof value === "object" && typeof value.key === "string";
|
|
6357
|
+
}
|
|
6109
6358
|
function inlineFlowNames(screen, plan) {
|
|
6110
6359
|
if (screen === null) return [];
|
|
6111
6360
|
const names = [];
|
|
@@ -6378,31 +6627,35 @@ function PlannedFrame(props) {
|
|
|
6378
6627
|
})
|
|
6379
6628
|
}, name);
|
|
6380
6629
|
const { PlacementScope } = page;
|
|
6630
|
+
const claimedRoute = useContext(RouteSourceContext);
|
|
6381
6631
|
const itemNode = page.plan.slots.item?.[0];
|
|
6382
6632
|
const item = useMemo(() => itemNode === void 0 ? void 0 : itemHandle(itemNode), [itemNode]);
|
|
6383
6633
|
return jsx(RouteSourceContext.Provider, {
|
|
6384
6634
|
value: null,
|
|
6385
|
-
children: jsx(
|
|
6386
|
-
|
|
6387
|
-
|
|
6388
|
-
|
|
6389
|
-
|
|
6390
|
-
|
|
6391
|
-
|
|
6392
|
-
children: jsx(
|
|
6393
|
-
|
|
6394
|
-
|
|
6395
|
-
|
|
6635
|
+
children: jsx(ActionRouteContext.Provider, {
|
|
6636
|
+
value: claimedRoute,
|
|
6637
|
+
children: jsx(PlacementScope, { children: jsx(ScreenTreeScope, {
|
|
6638
|
+
resource,
|
|
6639
|
+
mode: "show",
|
|
6640
|
+
compiled,
|
|
6641
|
+
page,
|
|
6642
|
+
children: jsx(ListItemContext.Provider, {
|
|
6643
|
+
value: item,
|
|
6644
|
+
children: jsx(FrameView, {
|
|
6645
|
+
resource,
|
|
6646
|
+
compiled,
|
|
6647
|
+
slots
|
|
6648
|
+
})
|
|
6396
6649
|
})
|
|
6397
|
-
})
|
|
6398
|
-
})
|
|
6650
|
+
}) })
|
|
6651
|
+
})
|
|
6399
6652
|
});
|
|
6400
6653
|
}
|
|
6401
6654
|
function ListFrameHost({ resource }) {
|
|
6402
6655
|
const compiled = useCompiledListScreen(resource);
|
|
6403
6656
|
const hasDataRuntime = useOptionalDataRuntime() !== null;
|
|
6404
6657
|
const { def } = useResourceListContext();
|
|
6405
|
-
const rowsView =
|
|
6658
|
+
const rowsView = def?.views?.list?.initial?.view ?? "table";
|
|
6406
6659
|
if (compiled === null) return jsx(Arrangement, {});
|
|
6407
6660
|
if (compiled.nodes.length === 0 && rowsView === "table") {
|
|
6408
6661
|
const empty = {};
|
|
@@ -6421,17 +6674,30 @@ function ListFrameHost({ resource }) {
|
|
|
6421
6674
|
}
|
|
6422
6675
|
//#endregion
|
|
6423
6676
|
//#region src/list-kit/resource-list-surface.tsx
|
|
6424
|
-
|
|
6425
|
-
|
|
6677
|
+
function declaresDisplay(field) {
|
|
6678
|
+
return field !== void 0 && resolveSurfaceUi(field, "column").use !== void 0;
|
|
6679
|
+
}
|
|
6680
|
+
const NO_INITIAL_FILTERS = Object.freeze({});
|
|
6681
|
+
const RelatedCards = lazy(async () => ({ default: (await import("./related-list-cards-BIqqY69Y.js")).RelatedCards }));
|
|
6682
|
+
const RelatedTimeline = lazy(async () => ({ default: (await import("./related-list-timeline-CRh7xwLD.js")).RelatedTimeline }));
|
|
6426
6683
|
function discriminatorOptionLabel(field, value) {
|
|
6684
|
+
const text = fieldOptionText(field, value);
|
|
6685
|
+
if (text !== void 0) return text;
|
|
6427
6686
|
const declared = field;
|
|
6428
6687
|
const label = (Array.isArray(declared?.options) ? declared.options : Array.isArray(declared?.meta?.options) ? declared.meta.options : []).find((option) => option?.value === value)?.label;
|
|
6429
6688
|
return typeof label === "string" ? label : value;
|
|
6430
6689
|
}
|
|
6431
|
-
function
|
|
6690
|
+
function declaredInitialSort(def) {
|
|
6691
|
+
return def?.views?.list?.initial?.sort;
|
|
6692
|
+
}
|
|
6693
|
+
function declaredRouteView(def) {
|
|
6694
|
+
return def?.views?.list?.initial?.view;
|
|
6695
|
+
}
|
|
6696
|
+
function ResourceListSurface({ resource, variant, state, fixedFilters, initial, search: searchField, columns: columnEntries, open, title, empty, scope, view, item, placementKey, onCreate, count: countPolicy, rowTarget, readOnly, linkEditing, exportable, layoutId, meta, renderCell, emptyState }) {
|
|
6432
6697
|
const route = variant === "route";
|
|
6433
6698
|
const t = useT();
|
|
6434
6699
|
const optionalT = useOptionalT();
|
|
6700
|
+
const chromeT = useChromeT();
|
|
6435
6701
|
const tid = useTestIds();
|
|
6436
6702
|
const listCaps = useI18nCaps(resource);
|
|
6437
6703
|
const RouteFiltersSlot = useListSlot("filters", resource);
|
|
@@ -6446,15 +6712,45 @@ function ResourceListSurface({ resource, variant, state, fixedFilters, pageSize,
|
|
|
6446
6712
|
console.error(`[burojs] list.body-removed: list.body.${canonicalResourceName(resource)} is no longer rendered; a whole-body override is a list frame (views.list.frame).`);
|
|
6447
6713
|
}, [removedBody, resource]);
|
|
6448
6714
|
const searchDecl = useMemo(() => searchField === void 0 ? void 0 : { fields: [searchField] }, [searchField]);
|
|
6449
|
-
const
|
|
6715
|
+
const appRegistry = useApp().registry;
|
|
6716
|
+
const effectiveView = route ? declaredRouteView(appRegistry.get(resource)) ?? "table" : view ?? "table";
|
|
6717
|
+
const timeline = effectiveView === "timeline";
|
|
6718
|
+
const initialKey = JSON.stringify(initial ?? null);
|
|
6719
|
+
const placementInitial = useMemo(() => route ? void 0 : {
|
|
6720
|
+
...initial,
|
|
6721
|
+
filters: initial?.filters ?? NO_INITIAL_FILTERS
|
|
6722
|
+
}, [route, initialKey]);
|
|
6723
|
+
const timelineAxis = (placementInitial?.sort ?? declaredInitialSort(appRegistry.get(resource)))?.[0]?.field;
|
|
6724
|
+
const columnNames = useMemo(() => columnEntries?.map(columnName), [columnEntries]);
|
|
6725
|
+
const list = useResourceList(resource, route ? {
|
|
6726
|
+
meta,
|
|
6727
|
+
...timeline ? { timeline } : {}
|
|
6728
|
+
} : {
|
|
6729
|
+
...timeline ? { timeline } : {},
|
|
6450
6730
|
...state === void 0 ? {} : { state },
|
|
6451
6731
|
...fixedFilters === void 0 ? {} : { fixedFilters },
|
|
6452
|
-
...
|
|
6732
|
+
...placementInitial === void 0 ? {} : { initial: placementInitial },
|
|
6453
6733
|
...searchDecl === void 0 ? {} : { search: searchDecl },
|
|
6454
|
-
...
|
|
6734
|
+
...columnEntries === void 0 ? {} : { columns: columnEntries },
|
|
6735
|
+
...scope === void 0 ? {} : { scope },
|
|
6736
|
+
...countPolicy === void 0 ? {} : { countPolicy }
|
|
6455
6737
|
});
|
|
6456
6738
|
const { def, query, pagination, cursor, count, filters, sorting, columns, selection, savedViews, routing, live, droppedPredicates } = list;
|
|
6457
6739
|
policyRowsOf(list, list.rows);
|
|
6740
|
+
const selectionRef = useRef(selection);
|
|
6741
|
+
selectionRef.current = selection;
|
|
6742
|
+
const onDetach = linkEditing?.detach?.onDetach;
|
|
6743
|
+
const surfaceLinkEditing = useMemo(() => onDetach === void 0 ? void 0 : { detach: { onDetach: async (detached) => {
|
|
6744
|
+
const left = await onDetach(detached);
|
|
6745
|
+
if (left.length > 0) {
|
|
6746
|
+
const gone = new Set(left);
|
|
6747
|
+
const current = selectionRef.current;
|
|
6748
|
+
if (current.ids.some((id) => gone.has(id))) current.set(current.ids.filter((id) => !gone.has(id)));
|
|
6749
|
+
}
|
|
6750
|
+
return left;
|
|
6751
|
+
} } }, [onDetach]);
|
|
6752
|
+
const onAttach = linkEditing?.attach?.onAttach;
|
|
6753
|
+
const attachBusy = linkEditing?.attach?.busy === true;
|
|
6458
6754
|
const hasDataRuntime = useOptionalDataRuntime() !== null;
|
|
6459
6755
|
const scopeLabel = (scope) => scope.id === LOCAL_USER_SCOPE_ID ? t("toolbar.viewsMyScope") : scope.label;
|
|
6460
6756
|
const createAvailable = useAvailability(resource, "create").state === "available";
|
|
@@ -6464,7 +6760,7 @@ function ResourceListSurface({ resource, variant, state, fixedFilters, pageSize,
|
|
|
6464
6760
|
const gate = useEntryGate();
|
|
6465
6761
|
const openQuick = useQuickOpener();
|
|
6466
6762
|
const effectiveOpen = open ?? (route ? def !== void 0 && !hasRecordPage(def, "show") ? "quick" : "page" : "quick");
|
|
6467
|
-
const quickRows = effectiveOpen === "quick";
|
|
6763
|
+
const quickRows = effectiveOpen === "quick" && rowTarget === void 0;
|
|
6468
6764
|
useDeepLinkOpen(resource, resource, route && quickRows);
|
|
6469
6765
|
const rowQuick = useMemo(() => quickRows ? (row) => gate(resource, "show", row).state === "open" ? () => void openQuick(resource, String(row.id), { row }) : null : void 0, [
|
|
6470
6766
|
quickRows,
|
|
@@ -6472,14 +6768,36 @@ function ResourceListSurface({ resource, variant, state, fixedFilters, pageSize,
|
|
|
6472
6768
|
openQuick,
|
|
6473
6769
|
resource
|
|
6474
6770
|
]);
|
|
6475
|
-
const
|
|
6771
|
+
const registry = useApp().registry;
|
|
6772
|
+
const targetName = rowTarget?.resource;
|
|
6773
|
+
const targetField = rowTarget?.field;
|
|
6774
|
+
const targetGate = useAvailabilityResolver(targetName ?? resource);
|
|
6775
|
+
const targetHref = useMemo(() => {
|
|
6776
|
+
if (targetName === void 0 || targetField === void 0) return void 0;
|
|
6777
|
+
const targetDef = registry.get(targetName);
|
|
6778
|
+
const delegate = targetDef?.routesTo?.show;
|
|
6779
|
+
const showDef = delegate === void 0 ? targetDef : registry.get(delegate);
|
|
6780
|
+
const showPage = targetDef !== void 0 && showDef !== void 0 && hasRecordPage(showDef, "show");
|
|
6781
|
+
return (row) => {
|
|
6782
|
+
const key = row[targetField];
|
|
6783
|
+
if (!showPage || key === null || key === void 0 || key === "") return void 0;
|
|
6784
|
+
if (targetGate("show").state !== "available") return void 0;
|
|
6785
|
+
return resourceShowHref(registry, targetDef, targetName, String(key));
|
|
6786
|
+
};
|
|
6787
|
+
}, [
|
|
6788
|
+
registry,
|
|
6789
|
+
targetName,
|
|
6790
|
+
targetField,
|
|
6791
|
+
targetGate
|
|
6792
|
+
]);
|
|
6793
|
+
const rowHref = useMemo(() => effectiveOpen === "none" ? void 0 : targetHref !== void 0 ? targetHref : quickRows ? (row) => gate(resource, "show", row).state === "undecided" ? void 0 : routing.rowHref(row) : routing.rowHref, [
|
|
6476
6794
|
effectiveOpen,
|
|
6795
|
+
targetHref,
|
|
6477
6796
|
quickRows,
|
|
6478
6797
|
gate,
|
|
6479
6798
|
resource,
|
|
6480
6799
|
routing.rowHref
|
|
6481
6800
|
]);
|
|
6482
|
-
const effectiveView = route ? (def?.views?.list)?.view ?? "table" : view ?? "table";
|
|
6483
6801
|
const tableView = effectiveView === "table";
|
|
6484
6802
|
const rowsViewFor = useMemo(() => tableView ? void 0 : (planned) => ({
|
|
6485
6803
|
skeleton: effectiveView === "cards" ? jsx(RelatedCardsSkeleton, {}) : jsx(RelatedTimelineSkeleton, {}),
|
|
@@ -6491,11 +6809,15 @@ function ResourceListSurface({ resource, variant, state, fixedFilters, pageSize,
|
|
|
6491
6809
|
item: route ? planned : item,
|
|
6492
6810
|
columns: route ? columns.visible : columnNames,
|
|
6493
6811
|
rowQuick,
|
|
6494
|
-
rowHref
|
|
6812
|
+
rowHref,
|
|
6813
|
+
...empty === void 0 ? {} : { empty }
|
|
6495
6814
|
};
|
|
6496
6815
|
return jsx(Suspense, {
|
|
6497
6816
|
fallback: effectiveView === "cards" ? jsx(RelatedCardsSkeleton, {}) : jsx(RelatedTimelineSkeleton, {}),
|
|
6498
|
-
children: effectiveView === "cards" ? jsx(RelatedCards, { ...common }) : jsx(RelatedTimeline, {
|
|
6817
|
+
children: effectiveView === "cards" ? jsx(RelatedCards, { ...common }) : jsx(RelatedTimeline, {
|
|
6818
|
+
...common,
|
|
6819
|
+
axis: timelineAxis
|
|
6820
|
+
})
|
|
6499
6821
|
});
|
|
6500
6822
|
}
|
|
6501
6823
|
}), [
|
|
@@ -6508,7 +6830,9 @@ function ResourceListSurface({ resource, variant, state, fixedFilters, pageSize,
|
|
|
6508
6830
|
columns.visible,
|
|
6509
6831
|
columnNames,
|
|
6510
6832
|
rowQuick,
|
|
6511
|
-
rowHref
|
|
6833
|
+
rowHref,
|
|
6834
|
+
empty,
|
|
6835
|
+
timelineAxis
|
|
6512
6836
|
]);
|
|
6513
6837
|
const pageTitle = resolveResourceLabel(listCaps, resource, "plural", def);
|
|
6514
6838
|
const [saveOpen, setSaveOpen] = useState(false);
|
|
@@ -6590,6 +6914,10 @@ function ResourceListSurface({ resource, variant, state, fixedFilters, pageSize,
|
|
|
6590
6914
|
toast.error(t("toolbar.viewsForkFailed"));
|
|
6591
6915
|
}
|
|
6592
6916
|
};
|
|
6917
|
+
const columnNamesDisplay = (col, field) => {
|
|
6918
|
+
const resolved = list.columns.ui[col];
|
|
6919
|
+
return resolved === void 0 ? declaresDisplay(field) : resolved.display.use !== void 0;
|
|
6920
|
+
};
|
|
6593
6921
|
const renderCellLadder = (col, value, row) => {
|
|
6594
6922
|
if (def?.types && col === def.types.by) {
|
|
6595
6923
|
const raw = typeof value === "string" ? value : "";
|
|
@@ -6601,20 +6929,21 @@ function ResourceListSurface({ resource, variant, state, fixedFilters, pageSize,
|
|
|
6601
6929
|
const fieldDef = def.fields[col];
|
|
6602
6930
|
const inline = discriminatorOptionLabel(fieldDef, raw);
|
|
6603
6931
|
const namespace = enumNamespaceOf(fieldDef);
|
|
6604
|
-
const label = namespace === void 0 ?
|
|
6932
|
+
const label = namespace === void 0 ? resolveFieldOptionLabel(listCaps, col, raw, inline) : resolveEnumLabel(listCaps, namespace, raw, inline);
|
|
6605
6933
|
return jsx(Badge, {
|
|
6606
6934
|
variant: "secondary",
|
|
6607
6935
|
"data-testid": `list-cell-type:${resource}:${raw}`,
|
|
6608
6936
|
children: label
|
|
6609
6937
|
});
|
|
6610
6938
|
}
|
|
6939
|
+
const custom = (renderCell ?? CellSlot)?.(col, value, row);
|
|
6940
|
+
if (custom !== void 0) return custom;
|
|
6611
6941
|
const fieldDef = def?.fields[col];
|
|
6612
|
-
const enumName = fieldDef?.opts?.enum ?? fieldDef?.ui?.enum;
|
|
6613
|
-
if (enumName && typeof value === "string") return jsx(EnumBadge, {
|
|
6942
|
+
const enumName = enumBindingKey(fieldDef?.opts?.enum ?? fieldDef?.ui?.enum);
|
|
6943
|
+
if (enumName && typeof value === "string" && !columnNamesDisplay(col, fieldDef)) return jsx(EnumBadge, {
|
|
6614
6944
|
enumName,
|
|
6615
6945
|
value
|
|
6616
6946
|
});
|
|
6617
|
-
return (renderCell ?? CellSlot)?.(col, value, row);
|
|
6618
6947
|
};
|
|
6619
6948
|
const surface = {
|
|
6620
6949
|
resource,
|
|
@@ -6642,8 +6971,16 @@ function ResourceListSurface({ resource, variant, state, fixedFilters, pageSize,
|
|
|
6642
6971
|
}
|
|
6643
6972
|
},
|
|
6644
6973
|
rowsViewFor,
|
|
6645
|
-
emptyState,
|
|
6646
|
-
|
|
6974
|
+
emptyState: empty !== void 0 && !list.filters.isFiltered && list.pagination.page <= 1 && (list.cursor === null || list.cursor.next === null) ? jsx("p", {
|
|
6975
|
+
"data-buro-region": "empty",
|
|
6976
|
+
className: "text-sm text-muted-foreground",
|
|
6977
|
+
children: empty
|
|
6978
|
+
}) : emptyState,
|
|
6979
|
+
renderCell: renderCellLadder,
|
|
6980
|
+
readOnly: readOnly === true,
|
|
6981
|
+
exportable: exportable !== false,
|
|
6982
|
+
...layoutId === void 0 ? {} : { layoutId },
|
|
6983
|
+
...surfaceLinkEditing === void 0 ? {} : { linkEditing: surfaceLinkEditing }
|
|
6647
6984
|
};
|
|
6648
6985
|
return jsx(ResourceListProvider, {
|
|
6649
6986
|
value: list,
|
|
@@ -6656,22 +6993,44 @@ function ResourceListSurface({ resource, variant, state, fixedFilters, pageSize,
|
|
|
6656
6993
|
resource,
|
|
6657
6994
|
mode: "list",
|
|
6658
6995
|
title: pageTitle
|
|
6659
|
-
}) : title === void 0 && onCreate === void 0 ? null : jsxs("div", {
|
|
6996
|
+
}) : title === void 0 && onCreate === void 0 && onAttach === void 0 ? null : jsxs("div", {
|
|
6660
6997
|
className: "flex items-center justify-between gap-2",
|
|
6661
|
-
children: [
|
|
6662
|
-
|
|
6663
|
-
|
|
6664
|
-
|
|
6665
|
-
|
|
6666
|
-
|
|
6667
|
-
|
|
6668
|
-
|
|
6669
|
-
|
|
6670
|
-
|
|
6671
|
-
|
|
6672
|
-
|
|
6673
|
-
|
|
6674
|
-
|
|
6998
|
+
children: [
|
|
6999
|
+
title === void 0 ? jsx("span", {}) : jsx("h3", {
|
|
7000
|
+
className: "text-sm font-semibold",
|
|
7001
|
+
children: title
|
|
7002
|
+
}),
|
|
7003
|
+
onAttach === void 0 ? null : jsxs(Button, {
|
|
7004
|
+
type: "button",
|
|
7005
|
+
size: "sm",
|
|
7006
|
+
variant: "outline",
|
|
7007
|
+
"data-testid": `list-link-attach:${resource}`,
|
|
7008
|
+
onClick: onAttach,
|
|
7009
|
+
disabled: attachBusy,
|
|
7010
|
+
"aria-busy": attachBusy,
|
|
7011
|
+
children: [attachBusy ? jsx(IconLoader2, {
|
|
7012
|
+
className: "mr-1 h-4 w-4 animate-spin",
|
|
7013
|
+
"aria-hidden": "true"
|
|
7014
|
+
}) : jsx(IconLink, {
|
|
7015
|
+
className: "mr-1 h-4 w-4",
|
|
7016
|
+
"aria-hidden": "true"
|
|
7017
|
+
}), optionalT("field.link.attach", { defaultValue: "Attach" })]
|
|
7018
|
+
}),
|
|
7019
|
+
onCreate === void 0 ? null : jsxs(Button, {
|
|
7020
|
+
type: "button",
|
|
7021
|
+
size: "sm",
|
|
7022
|
+
variant: "outline",
|
|
7023
|
+
"data-testid": `list-create:${resource}`,
|
|
7024
|
+
onClick: onCreate,
|
|
7025
|
+
children: [jsx(IconPlus, {
|
|
7026
|
+
className: "mr-1 h-4 w-4",
|
|
7027
|
+
"aria-hidden": "true"
|
|
7028
|
+
}), optionalT("actions.create", {
|
|
7029
|
+
resource,
|
|
7030
|
+
defaultValue: "Create"
|
|
7031
|
+
})]
|
|
7032
|
+
})
|
|
7033
|
+
]
|
|
6675
7034
|
}),
|
|
6676
7035
|
hasDataRuntime && jsx(ExportRunWatcherMount, {}),
|
|
6677
7036
|
droppedPredicates > 0 && jsx("p", {
|
|
@@ -6720,13 +7079,13 @@ function ResourceListSurface({ resource, variant, state, fixedFilters, pageSize,
|
|
|
6720
7079
|
variant: "outline",
|
|
6721
7080
|
onClick: () => setSaveOpen(false),
|
|
6722
7081
|
"data-testid": `list-saved-view-save-cancel:${resource}`,
|
|
6723
|
-
children:
|
|
7082
|
+
children: chromeT("actions.cancel")
|
|
6724
7083
|
}), jsx(Button, {
|
|
6725
7084
|
type: "button",
|
|
6726
7085
|
onClick: handleSaveConfirm,
|
|
6727
7086
|
disabled: saveLabel.trim().length === 0,
|
|
6728
7087
|
"data-testid": `list-saved-view-save-confirm:${resource}`,
|
|
6729
|
-
children:
|
|
7088
|
+
children: chromeT("actions.save")
|
|
6730
7089
|
})] })
|
|
6731
7090
|
]
|
|
6732
7091
|
})
|
|
@@ -6763,7 +7122,7 @@ function ResourceListSurface({ resource, variant, state, fixedFilters, pageSize,
|
|
|
6763
7122
|
variant: "outline",
|
|
6764
7123
|
onClick: () => handleDeleteUserView(entry.id),
|
|
6765
7124
|
"data-testid": `list-saved-view-manage-delete:${resource}:${entry.id}`,
|
|
6766
|
-
children:
|
|
7125
|
+
children: chromeT("actions.delete")
|
|
6767
7126
|
}) : jsx("span", {
|
|
6768
7127
|
className: "shrink-0 text-xs text-muted-foreground",
|
|
6769
7128
|
children: t("toolbar.viewsReadOnly")
|
|
@@ -6775,7 +7134,7 @@ function ResourceListSurface({ resource, variant, state, fixedFilters, pageSize,
|
|
|
6775
7134
|
variant: "outline",
|
|
6776
7135
|
onClick: () => setManageOpen(false),
|
|
6777
7136
|
"data-testid": `list-saved-view-manage-close:${resource}`,
|
|
6778
|
-
children:
|
|
7137
|
+
children: chromeT("actions.close")
|
|
6779
7138
|
}) })
|
|
6780
7139
|
]
|
|
6781
7140
|
})
|
|
@@ -6808,13 +7167,13 @@ function ResourceListSurface({ resource, variant, state, fixedFilters, pageSize,
|
|
|
6808
7167
|
setRenameTarget(null);
|
|
6809
7168
|
},
|
|
6810
7169
|
"data-testid": `list-saved-view-rename-cancel:${resource}`,
|
|
6811
|
-
children:
|
|
7170
|
+
children: chromeT("actions.cancel")
|
|
6812
7171
|
}), jsx(Button, {
|
|
6813
7172
|
type: "button",
|
|
6814
7173
|
onClick: handleRenameConfirm,
|
|
6815
7174
|
disabled: renameLabel.trim().length === 0,
|
|
6816
7175
|
"data-testid": `list-saved-view-rename-confirm:${resource}`,
|
|
6817
|
-
children:
|
|
7176
|
+
children: chromeT("actions.save")
|
|
6818
7177
|
})] })
|
|
6819
7178
|
]
|
|
6820
7179
|
})
|
|
@@ -6849,7 +7208,7 @@ function ResourceListSurface({ resource, variant, state, fixedFilters, pageSize,
|
|
|
6849
7208
|
setForkTarget(null);
|
|
6850
7209
|
},
|
|
6851
7210
|
"data-testid": `list-saved-view-fork-cancel:${resource}`,
|
|
6852
|
-
children:
|
|
7211
|
+
children: chromeT("actions.cancel")
|
|
6853
7212
|
}), jsx(Button, {
|
|
6854
7213
|
type: "button",
|
|
6855
7214
|
onClick: handleForkConfirm,
|
|
@@ -6872,6 +7231,797 @@ function ExportRunWatcherMount() {
|
|
|
6872
7231
|
return null;
|
|
6873
7232
|
}
|
|
6874
7233
|
//#endregion
|
|
7234
|
+
//#region src/fields/use-relation-picker.tsx
|
|
7235
|
+
const noop = () => void 0;
|
|
7236
|
+
function safeKey(input) {
|
|
7237
|
+
if (input === void 0 || input === null) return void 0;
|
|
7238
|
+
try {
|
|
7239
|
+
return resourceKey(input);
|
|
7240
|
+
} catch {
|
|
7241
|
+
return;
|
|
7242
|
+
}
|
|
7243
|
+
}
|
|
7244
|
+
function sessionIdentity(runtime) {
|
|
7245
|
+
const state = runtime.dataSession.getState();
|
|
7246
|
+
const snapshot = state.status === "ready" ? state.snapshot : state.target;
|
|
7247
|
+
const selection = snapshot.scope?.selection;
|
|
7248
|
+
const partition = selection === void 0 ? snapshot.scopeKey : [snapshot.scopeKey, selection];
|
|
7249
|
+
let scopeKey;
|
|
7250
|
+
try {
|
|
7251
|
+
scopeKey = JSON.stringify(partition) ?? "";
|
|
7252
|
+
} catch {
|
|
7253
|
+
scopeKey = String(snapshot.scopeKey);
|
|
7254
|
+
}
|
|
7255
|
+
return {
|
|
7256
|
+
sessionKey: snapshot.sessionKey,
|
|
7257
|
+
scopeKey
|
|
7258
|
+
};
|
|
7259
|
+
}
|
|
7260
|
+
function PickerHost(props) {
|
|
7261
|
+
const { register } = props;
|
|
7262
|
+
React.useEffect(() => register(), [register]);
|
|
7263
|
+
return jsx(Fragment$1, { children: props.children });
|
|
7264
|
+
}
|
|
7265
|
+
function useRelationPicker(options) {
|
|
7266
|
+
const app = useApp();
|
|
7267
|
+
const runtime = useDataRuntime();
|
|
7268
|
+
const frame = useOptionalRecordFrame();
|
|
7269
|
+
const frames = useRecordScopeFrames();
|
|
7270
|
+
const multiple = options.multiple === true;
|
|
7271
|
+
const fieldName = options.field;
|
|
7272
|
+
let to;
|
|
7273
|
+
let declaredBy;
|
|
7274
|
+
let declaredCandidates;
|
|
7275
|
+
if (fieldName !== void 0) {
|
|
7276
|
+
const meta = ((frame === null ? void 0 : app.registry.get(frame.resource)?.fields)?.[fieldName])?.meta;
|
|
7277
|
+
to = safeKey(meta?.to);
|
|
7278
|
+
declaredBy = typeof meta?.by === "string" ? meta.by : void 0;
|
|
7279
|
+
declaredCandidates = Array.isArray(meta?.candidates) ? meta.candidates : void 0;
|
|
7280
|
+
} else to = safeKey(options.to);
|
|
7281
|
+
const target = to ?? "";
|
|
7282
|
+
const by = options.valueField ?? declaredBy;
|
|
7283
|
+
const scope = options.scope ?? "context";
|
|
7284
|
+
const selection = useCandidateSelection({
|
|
7285
|
+
to,
|
|
7286
|
+
candidates: options.candidates ?? declaredCandidates,
|
|
7287
|
+
sources: frame?.controller.sources,
|
|
7288
|
+
resource: frame?.resource ?? "",
|
|
7289
|
+
fieldName: fieldName ?? target,
|
|
7290
|
+
fixedFilters: options.fixedFilters
|
|
7291
|
+
});
|
|
7292
|
+
const held = selection.gate !== void 0;
|
|
7293
|
+
const pendingRef = React.useRef(null);
|
|
7294
|
+
const [active, setActive] = React.useState(null);
|
|
7295
|
+
const activeRef = React.useRef(null);
|
|
7296
|
+
activeRef.current = active;
|
|
7297
|
+
const rowsRef = React.useRef(new Map());
|
|
7298
|
+
const createdRowRef = React.useRef(void 0);
|
|
7299
|
+
const alive = React.useRef(true);
|
|
7300
|
+
const mounts = React.useRef(0);
|
|
7301
|
+
const sequence = React.useRef(0);
|
|
7302
|
+
const latest = React.useRef({
|
|
7303
|
+
multiple,
|
|
7304
|
+
select: options.select,
|
|
7305
|
+
idField: "id",
|
|
7306
|
+
valueField: "id",
|
|
7307
|
+
available: false,
|
|
7308
|
+
frames
|
|
7309
|
+
});
|
|
7310
|
+
const project = React.useCallback((row) => {
|
|
7311
|
+
const select = latest.current.select;
|
|
7312
|
+
if (select === void 0) return { ...row };
|
|
7313
|
+
const out = {};
|
|
7314
|
+
const key = row[latest.current.idField] !== void 0 ? row[latest.current.idField] : row.id;
|
|
7315
|
+
if (key !== void 0) out.id = key;
|
|
7316
|
+
const { valueField } = latest.current;
|
|
7317
|
+
if (Object.hasOwn(row, valueField)) out[valueField] = row[valueField];
|
|
7318
|
+
for (const name of select) if (Object.hasOwn(row, name)) out[name] = row[name];
|
|
7319
|
+
return out;
|
|
7320
|
+
}, []);
|
|
7321
|
+
const settle = React.useCallback((result) => {
|
|
7322
|
+
const pending = pendingRef.current;
|
|
7323
|
+
if (pending === null) return;
|
|
7324
|
+
pendingRef.current = null;
|
|
7325
|
+
pending.detach();
|
|
7326
|
+
if (alive.current) setActive(null);
|
|
7327
|
+
pending.resolve(result);
|
|
7328
|
+
}, []);
|
|
7329
|
+
const toggle = React.useCallback((key, row) => {
|
|
7330
|
+
setActive((current) => {
|
|
7331
|
+
if (current === null) return current;
|
|
7332
|
+
const has = current.selected.some((k) => String(k) === String(key));
|
|
7333
|
+
if (has) rowsRef.current.delete(String(key));
|
|
7334
|
+
else if (row !== void 0) rowsRef.current.set(String(key), row);
|
|
7335
|
+
const selected = has ? current.selected.filter((k) => String(k) !== String(key)) : [...current.selected, key];
|
|
7336
|
+
const next = {
|
|
7337
|
+
...current,
|
|
7338
|
+
selected
|
|
7339
|
+
};
|
|
7340
|
+
activeRef.current = next;
|
|
7341
|
+
return next;
|
|
7342
|
+
});
|
|
7343
|
+
}, []);
|
|
7344
|
+
const onValue = React.useCallback((value) => {
|
|
7345
|
+
const row = createdRowRef.current;
|
|
7346
|
+
createdRowRef.current = void 0;
|
|
7347
|
+
if (latest.current.multiple) {
|
|
7348
|
+
const current = activeRef.current;
|
|
7349
|
+
if (current !== null && !current.selected.some((k) => String(k) === String(value))) toggle(value, row);
|
|
7350
|
+
return;
|
|
7351
|
+
}
|
|
7352
|
+
settle({
|
|
7353
|
+
kind: "selected",
|
|
7354
|
+
keys: [value],
|
|
7355
|
+
rows: row === void 0 ? [] : [project(row)]
|
|
7356
|
+
});
|
|
7357
|
+
}, [
|
|
7358
|
+
project,
|
|
7359
|
+
settle,
|
|
7360
|
+
toggle
|
|
7361
|
+
]);
|
|
7362
|
+
const wiring = useRelationSelect({
|
|
7363
|
+
to: target,
|
|
7364
|
+
by,
|
|
7365
|
+
fixedFilters: selection.fixedFilters,
|
|
7366
|
+
scope,
|
|
7367
|
+
...fieldName === void 0 ? {} : { scopeField: fieldName },
|
|
7368
|
+
held,
|
|
7369
|
+
onValue
|
|
7370
|
+
});
|
|
7371
|
+
latest.current = {
|
|
7372
|
+
multiple,
|
|
7373
|
+
select: options.select,
|
|
7374
|
+
idField: wiring.idField,
|
|
7375
|
+
valueField: wiring.valueField,
|
|
7376
|
+
available: to !== void 0 && wiring.targetDef !== void 0 && !held,
|
|
7377
|
+
frames
|
|
7378
|
+
};
|
|
7379
|
+
const { create: shared } = wiring;
|
|
7380
|
+
const create = React.useMemo(() => ({
|
|
7381
|
+
...shared,
|
|
7382
|
+
start: (openCreate) => shared.start(() => {
|
|
7383
|
+
const opened = openCreate();
|
|
7384
|
+
opened.then((row) => {
|
|
7385
|
+
createdRowRef.current = row === null ? void 0 : { ...row };
|
|
7386
|
+
}, noop);
|
|
7387
|
+
return opened;
|
|
7388
|
+
})
|
|
7389
|
+
}), [shared]);
|
|
7390
|
+
React.useEffect(() => runtime.dataSession.subscribe(() => {
|
|
7391
|
+
const pending = pendingRef.current;
|
|
7392
|
+
if (pending === null) return;
|
|
7393
|
+
const now = sessionIdentity(runtime);
|
|
7394
|
+
if (now.sessionKey !== pending.sessionKey || now.scopeKey !== pending.scopeKey) settle({
|
|
7395
|
+
kind: "cancelled",
|
|
7396
|
+
reason: "session-changed"
|
|
7397
|
+
});
|
|
7398
|
+
}), [runtime, settle]);
|
|
7399
|
+
React.useEffect(() => {
|
|
7400
|
+
alive.current = true;
|
|
7401
|
+
return () => {
|
|
7402
|
+
alive.current = false;
|
|
7403
|
+
settle({
|
|
7404
|
+
kind: "cancelled",
|
|
7405
|
+
reason: "unmounted"
|
|
7406
|
+
});
|
|
7407
|
+
};
|
|
7408
|
+
}, [settle]);
|
|
7409
|
+
React.useEffect(() => {
|
|
7410
|
+
if (held) settle({
|
|
7411
|
+
kind: "cancelled",
|
|
7412
|
+
reason: "unavailable"
|
|
7413
|
+
});
|
|
7414
|
+
}, [held, settle]);
|
|
7415
|
+
const register = React.useCallback(() => {
|
|
7416
|
+
mounts.current += 1;
|
|
7417
|
+
return () => {
|
|
7418
|
+
mounts.current -= 1;
|
|
7419
|
+
if (mounts.current === 0) settle({
|
|
7420
|
+
kind: "cancelled",
|
|
7421
|
+
reason: "unmounted"
|
|
7422
|
+
});
|
|
7423
|
+
};
|
|
7424
|
+
}, [settle]);
|
|
7425
|
+
const open = React.useCallback((opts = {}) => new Promise((resolve) => {
|
|
7426
|
+
settle({
|
|
7427
|
+
kind: "cancelled",
|
|
7428
|
+
reason: "superseded"
|
|
7429
|
+
});
|
|
7430
|
+
if (mounts.current === 0) {
|
|
7431
|
+
if (process.env.NODE_ENV !== "production") console.error("[burojs] useRelationPicker: open() was called but its element is not rendered — render `handle.element` once in your tree.");
|
|
7432
|
+
resolve({
|
|
7433
|
+
kind: "cancelled",
|
|
7434
|
+
reason: "not-mounted"
|
|
7435
|
+
});
|
|
7436
|
+
return;
|
|
7437
|
+
}
|
|
7438
|
+
const { signal } = opts;
|
|
7439
|
+
if (signal?.aborted === true) {
|
|
7440
|
+
resolve({
|
|
7441
|
+
kind: "cancelled",
|
|
7442
|
+
reason: "aborted"
|
|
7443
|
+
});
|
|
7444
|
+
return;
|
|
7445
|
+
}
|
|
7446
|
+
if (!latest.current.available) {
|
|
7447
|
+
resolve({
|
|
7448
|
+
kind: "cancelled",
|
|
7449
|
+
reason: "unavailable"
|
|
7450
|
+
});
|
|
7451
|
+
return;
|
|
7452
|
+
}
|
|
7453
|
+
sequence.current += 1;
|
|
7454
|
+
const id = sequence.current;
|
|
7455
|
+
const onAbort = () => {
|
|
7456
|
+
if (pendingRef.current?.id === id) settle({
|
|
7457
|
+
kind: "cancelled",
|
|
7458
|
+
reason: "aborted"
|
|
7459
|
+
});
|
|
7460
|
+
};
|
|
7461
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
7462
|
+
pendingRef.current = {
|
|
7463
|
+
id,
|
|
7464
|
+
...sessionIdentity(runtime),
|
|
7465
|
+
resolve,
|
|
7466
|
+
detach: () => signal?.removeEventListener("abort", onAbort)
|
|
7467
|
+
};
|
|
7468
|
+
rowsRef.current = new Map();
|
|
7469
|
+
createdRowRef.current = void 0;
|
|
7470
|
+
const next = {
|
|
7471
|
+
id,
|
|
7472
|
+
frames: latest.current.frames,
|
|
7473
|
+
selected: opts.selected ?? []
|
|
7474
|
+
};
|
|
7475
|
+
activeRef.current = next;
|
|
7476
|
+
setActive(next);
|
|
7477
|
+
}), [runtime, settle]);
|
|
7478
|
+
const onSelectRow = React.useCallback((row) => {
|
|
7479
|
+
const field = wiring.valueField;
|
|
7480
|
+
const value = field === "id" ? row.id : row[field];
|
|
7481
|
+
if (typeof value !== "string" && typeof value !== "number") return;
|
|
7482
|
+
settle({
|
|
7483
|
+
kind: "selected",
|
|
7484
|
+
keys: [value],
|
|
7485
|
+
rows: [project(row)]
|
|
7486
|
+
});
|
|
7487
|
+
}, [
|
|
7488
|
+
wiring.valueField,
|
|
7489
|
+
settle,
|
|
7490
|
+
project
|
|
7491
|
+
]);
|
|
7492
|
+
const onDone = React.useCallback(() => {
|
|
7493
|
+
const keys = activeRef.current?.selected ?? [];
|
|
7494
|
+
const rows = keys.flatMap((key) => {
|
|
7495
|
+
const row = rowsRef.current.get(String(key));
|
|
7496
|
+
return row === void 0 ? [] : [project(row)];
|
|
7497
|
+
});
|
|
7498
|
+
settle({
|
|
7499
|
+
kind: "selected",
|
|
7500
|
+
keys,
|
|
7501
|
+
rows
|
|
7502
|
+
});
|
|
7503
|
+
}, [settle, project]);
|
|
7504
|
+
const onOpenChange = React.useCallback((next) => {
|
|
7505
|
+
if (!next) settle({
|
|
7506
|
+
kind: "cancelled",
|
|
7507
|
+
reason: "dismissed"
|
|
7508
|
+
});
|
|
7509
|
+
}, [settle]);
|
|
7510
|
+
return {
|
|
7511
|
+
open,
|
|
7512
|
+
element: jsx(PickerHost, {
|
|
7513
|
+
register,
|
|
7514
|
+
children: active === null || held ? null : jsxs(RecordScopeChain, {
|
|
7515
|
+
frames: active.frames,
|
|
7516
|
+
children: [jsx(LazyRelationPickerDialog, {
|
|
7517
|
+
to: target,
|
|
7518
|
+
open: true,
|
|
7519
|
+
onOpenChange,
|
|
7520
|
+
onSelect: noop,
|
|
7521
|
+
onSelectRow,
|
|
7522
|
+
valueField: wiring.valueField,
|
|
7523
|
+
...selection.fixedFilters === void 0 ? {} : { fixedFilters: selection.fixedFilters },
|
|
7524
|
+
scope,
|
|
7525
|
+
...fieldName === void 0 ? {} : { scopeField: fieldName },
|
|
7526
|
+
...options.create === void 0 ? {} : {
|
|
7527
|
+
create,
|
|
7528
|
+
...options.create.seeds === void 0 ? {} : { createSeeds: options.create.seeds }
|
|
7529
|
+
},
|
|
7530
|
+
fieldName: fieldName ?? target,
|
|
7531
|
+
...options.disabledKeys === void 0 ? {} : { disabledKeys: options.disabledKeys },
|
|
7532
|
+
...options.disabledLabel === void 0 ? {} : { disabledLabel: options.disabledLabel },
|
|
7533
|
+
...multiple ? {
|
|
7534
|
+
multiple: true,
|
|
7535
|
+
selected: active.selected,
|
|
7536
|
+
onToggle: (value, row) => toggle(value, row),
|
|
7537
|
+
onDone
|
|
7538
|
+
} : {}
|
|
7539
|
+
}), wiring.reader]
|
|
7540
|
+
}, active.id)
|
|
7541
|
+
})
|
|
7542
|
+
};
|
|
7543
|
+
}
|
|
7544
|
+
//#endregion
|
|
7545
|
+
//#region src/blocks/link-write-summary.tsx
|
|
7546
|
+
function LinkWriteSummary(props) {
|
|
7547
|
+
const { report, titles, onClose } = props;
|
|
7548
|
+
const testid = props.testid ?? "link-write-summary";
|
|
7549
|
+
const t = useOptionalT();
|
|
7550
|
+
const caps = useI18nCaps();
|
|
7551
|
+
const chromeT = useChromeT();
|
|
7552
|
+
const heading = t("field.link.partial", { defaultValue: "Some links were not changed" });
|
|
7553
|
+
return jsx(Dialog, {
|
|
7554
|
+
open: true,
|
|
7555
|
+
onOpenChange: (open) => {
|
|
7556
|
+
if (!open) onClose();
|
|
7557
|
+
},
|
|
7558
|
+
children: jsxs(DialogContent, {
|
|
7559
|
+
"data-testid": testid,
|
|
7560
|
+
className: "flex max-h-[85vh] flex-col overflow-hidden",
|
|
7561
|
+
children: [
|
|
7562
|
+
jsxs(DialogHeader, { children: [jsx(DialogTitle, { children: heading }), jsx(DialogDescription, {
|
|
7563
|
+
className: "sr-only",
|
|
7564
|
+
children: heading
|
|
7565
|
+
})] }),
|
|
7566
|
+
jsx("ul", {
|
|
7567
|
+
className: "flex min-h-0 flex-1 flex-col gap-2 overflow-y-auto overscroll-contain",
|
|
7568
|
+
children: report.failed.map(({ id, error }) => jsxs("li", {
|
|
7569
|
+
"data-testid": `${testid}:item:${id}`,
|
|
7570
|
+
className: "text-sm",
|
|
7571
|
+
children: [jsx("span", {
|
|
7572
|
+
className: "font-medium",
|
|
7573
|
+
children: titles[id] ?? id
|
|
7574
|
+
}), jsx("span", {
|
|
7575
|
+
className: "text-destructive",
|
|
7576
|
+
children: `: ${resolveErrorText(error, caps)}`
|
|
7577
|
+
})]
|
|
7578
|
+
}, id))
|
|
7579
|
+
}),
|
|
7580
|
+
jsx(DialogFooter, { children: jsx(Button, {
|
|
7581
|
+
type: "button",
|
|
7582
|
+
onClick: onClose,
|
|
7583
|
+
"data-testid": `${testid}:close`,
|
|
7584
|
+
children: chromeT("actions.close", { defaultValue: "Close" })
|
|
7585
|
+
}) })
|
|
7586
|
+
]
|
|
7587
|
+
})
|
|
7588
|
+
});
|
|
7589
|
+
}
|
|
7590
|
+
//#endregion
|
|
7591
|
+
//#region src/blocks/link-attach.tsx
|
|
7592
|
+
const NO_ISSUES = Object.freeze([]);
|
|
7593
|
+
function useLinkPanelWrites(options) {
|
|
7594
|
+
const { owner, junction, target, link, targetKey, valueField, junctionIdField, displayField, scope, record, ownerKey, placement } = options;
|
|
7595
|
+
const app = useApp();
|
|
7596
|
+
const runtime = useDataRuntime();
|
|
7597
|
+
const t = useOptionalT();
|
|
7598
|
+
const rights = useLinkRights(owner, link, {
|
|
7599
|
+
surface: "panel",
|
|
7600
|
+
record,
|
|
7601
|
+
...placement === void 0 ? {} : { placement }
|
|
7602
|
+
});
|
|
7603
|
+
const writer = useLinkWriter(owner, link);
|
|
7604
|
+
const linkedKeys = useLinkedKeys(owner, link);
|
|
7605
|
+
const reverse = useMemo(() => reverseLinkOf((name) => app.registry.get(name)?.fields, owner, link), [
|
|
7606
|
+
app.registry,
|
|
7607
|
+
owner,
|
|
7608
|
+
link
|
|
7609
|
+
]);
|
|
7610
|
+
let liveHides = false;
|
|
7611
|
+
if (reverse !== void 0) try {
|
|
7612
|
+
const { providerId } = runtime.app.resolveDataBinding(target);
|
|
7613
|
+
liveHides = runtime.providers.resolve(providerId).provider.readRelations?.links === true && linkCapabilitiesOf(runtime, providerId)?.links?.filter.none !== false;
|
|
7614
|
+
} catch {
|
|
7615
|
+
liveHides = false;
|
|
7616
|
+
}
|
|
7617
|
+
const liveHidesRef = useRef(liveHides);
|
|
7618
|
+
liveHidesRef.current = liveHides;
|
|
7619
|
+
const [frozenHides, setFrozenHides] = useState(null);
|
|
7620
|
+
const hides = frozenHides ?? liveHides;
|
|
7621
|
+
const ownerKeyId = JSON.stringify(ownerKey ?? null);
|
|
7622
|
+
const hidden = useMemo(() => hides && reverse !== void 0 ? [{
|
|
7623
|
+
field: reverse,
|
|
7624
|
+
op: "hasNone",
|
|
7625
|
+
value: [ownerKey]
|
|
7626
|
+
}] : void 0, [
|
|
7627
|
+
hides,
|
|
7628
|
+
reverse,
|
|
7629
|
+
ownerKeyId
|
|
7630
|
+
]);
|
|
7631
|
+
const disabledKeys = useCallback((keys, signal) => linkedKeys(record, keys, signal), [linkedKeys, record]);
|
|
7632
|
+
const disabledLabel = useCallback((count) => t("field.link.alreadyLinked", {
|
|
7633
|
+
count,
|
|
7634
|
+
defaultValue: `${count} already linked`
|
|
7635
|
+
}), [t]);
|
|
7636
|
+
const picker = useRelationPicker({
|
|
7637
|
+
to: target,
|
|
7638
|
+
multiple: true,
|
|
7639
|
+
valueField,
|
|
7640
|
+
scope,
|
|
7641
|
+
...hidden === void 0 ? {
|
|
7642
|
+
disabledKeys,
|
|
7643
|
+
disabledLabel
|
|
7644
|
+
} : { fixedFilters: hidden }
|
|
7645
|
+
});
|
|
7646
|
+
const openPicker = picker.open;
|
|
7647
|
+
const [summary, setSummary] = useState(null);
|
|
7648
|
+
const finish = useCallback((report, titles, done) => {
|
|
7649
|
+
if (report.outcome !== "success") {
|
|
7650
|
+
setSummary({
|
|
7651
|
+
report,
|
|
7652
|
+
titles
|
|
7653
|
+
});
|
|
7654
|
+
return;
|
|
7655
|
+
}
|
|
7656
|
+
const count = report.succeeded.length;
|
|
7657
|
+
if (count === 0) return;
|
|
7658
|
+
toast.success(t(`field.link.${done}`, {
|
|
7659
|
+
count,
|
|
7660
|
+
defaultValue: `${count} ${count === 1 ? "link" : "links"} ${done}`
|
|
7661
|
+
}));
|
|
7662
|
+
}, [t]);
|
|
7663
|
+
const attaching = useRef(false);
|
|
7664
|
+
const [attachWriting, setAttachWriting] = useState(false);
|
|
7665
|
+
const onAttach = useCallback(() => {
|
|
7666
|
+
if (attaching.current) return;
|
|
7667
|
+
attaching.current = true;
|
|
7668
|
+
(async () => {
|
|
7669
|
+
try {
|
|
7670
|
+
setFrozenHides(liveHidesRef.current);
|
|
7671
|
+
let result;
|
|
7672
|
+
try {
|
|
7673
|
+
result = await openPicker();
|
|
7674
|
+
} finally {
|
|
7675
|
+
setFrozenHides(null);
|
|
7676
|
+
}
|
|
7677
|
+
if (result.kind !== "selected" || result.keys.length === 0) return;
|
|
7678
|
+
const titles = {};
|
|
7679
|
+
for (const row of result.rows) {
|
|
7680
|
+
const key = valueField === "id" ? row.id : row[valueField];
|
|
7681
|
+
const title = row[displayField];
|
|
7682
|
+
if (key !== void 0 && key !== null && title !== void 0 && title !== null) titles[String(key)] = String(title);
|
|
7683
|
+
}
|
|
7684
|
+
setAttachWriting(true);
|
|
7685
|
+
try {
|
|
7686
|
+
finish(await writer.attach(result.keys, record), titles, "attached");
|
|
7687
|
+
} finally {
|
|
7688
|
+
setAttachWriting(false);
|
|
7689
|
+
}
|
|
7690
|
+
} finally {
|
|
7691
|
+
attaching.current = false;
|
|
7692
|
+
}
|
|
7693
|
+
})();
|
|
7694
|
+
}, [
|
|
7695
|
+
displayField,
|
|
7696
|
+
finish,
|
|
7697
|
+
openPicker,
|
|
7698
|
+
record,
|
|
7699
|
+
valueField,
|
|
7700
|
+
writer
|
|
7701
|
+
]);
|
|
7702
|
+
const pending = useRef(null);
|
|
7703
|
+
const [confirming, setConfirming] = useState(null);
|
|
7704
|
+
const detaching = useRef(false);
|
|
7705
|
+
const [busy, setBusy] = useState(false);
|
|
7706
|
+
const onDetach = useCallback((rows) => new Promise((resolve) => {
|
|
7707
|
+
pending.current?.resolve([]);
|
|
7708
|
+
pending.current = {
|
|
7709
|
+
rows,
|
|
7710
|
+
resolve
|
|
7711
|
+
};
|
|
7712
|
+
setConfirming(rows.length);
|
|
7713
|
+
}), []);
|
|
7714
|
+
useEffect(() => () => {
|
|
7715
|
+
pending.current?.resolve([]);
|
|
7716
|
+
pending.current = null;
|
|
7717
|
+
}, []);
|
|
7718
|
+
const cancel = useCallback(() => {
|
|
7719
|
+
if (detaching.current) return;
|
|
7720
|
+
pending.current?.resolve([]);
|
|
7721
|
+
pending.current = null;
|
|
7722
|
+
setConfirming(null);
|
|
7723
|
+
}, []);
|
|
7724
|
+
const proceed = useCallback(() => {
|
|
7725
|
+
const current = pending.current;
|
|
7726
|
+
if (current === null || detaching.current) return;
|
|
7727
|
+
detaching.current = true;
|
|
7728
|
+
setBusy(true);
|
|
7729
|
+
(async () => {
|
|
7730
|
+
try {
|
|
7731
|
+
const titles = {};
|
|
7732
|
+
for (const row of current.rows) {
|
|
7733
|
+
const id = row[junctionIdField];
|
|
7734
|
+
if (id === void 0 || id === null) continue;
|
|
7735
|
+
const title = row[`${targetKey}.${displayField}`] ?? row[targetKey];
|
|
7736
|
+
if (title !== void 0 && title !== null) titles[String(id)] = String(title);
|
|
7737
|
+
}
|
|
7738
|
+
const report = await writer.detach(current.rows, record);
|
|
7739
|
+
pending.current = null;
|
|
7740
|
+
setConfirming(null);
|
|
7741
|
+
finish(report, titles, "detached");
|
|
7742
|
+
const left = new Set(report.succeeded);
|
|
7743
|
+
current.resolve(current.rows.filter((row) => {
|
|
7744
|
+
const id = row[junctionIdField];
|
|
7745
|
+
return id !== void 0 && id !== null && left.has(String(id));
|
|
7746
|
+
}).map((row) => String(row.id)));
|
|
7747
|
+
} finally {
|
|
7748
|
+
detaching.current = false;
|
|
7749
|
+
setBusy(false);
|
|
7750
|
+
}
|
|
7751
|
+
})();
|
|
7752
|
+
}, [
|
|
7753
|
+
displayField,
|
|
7754
|
+
finish,
|
|
7755
|
+
junctionIdField,
|
|
7756
|
+
record,
|
|
7757
|
+
targetKey,
|
|
7758
|
+
writer
|
|
7759
|
+
]);
|
|
7760
|
+
const detachLabel = t("field.link.detach", { defaultValue: "Detach" });
|
|
7761
|
+
const count = confirming ?? 0;
|
|
7762
|
+
const gate = {
|
|
7763
|
+
resource: junction,
|
|
7764
|
+
open: confirming !== null,
|
|
7765
|
+
busy,
|
|
7766
|
+
header: t("field.link.confirmDetach", {
|
|
7767
|
+
count,
|
|
7768
|
+
defaultValue: `Detach ${count} ${count === 1 ? "link" : "links"}? The linked records themselves are not deleted.`
|
|
7769
|
+
}),
|
|
7770
|
+
blockers: NO_ISSUES,
|
|
7771
|
+
confirmable: NO_ISSUES,
|
|
7772
|
+
proceed,
|
|
7773
|
+
cancel
|
|
7774
|
+
};
|
|
7775
|
+
const attachAllowed = rights.attach.allowed;
|
|
7776
|
+
const detachAllowed = rights.detach.allowed;
|
|
7777
|
+
return {
|
|
7778
|
+
linkEditing: useMemo(() => !attachAllowed && !detachAllowed ? void 0 : {
|
|
7779
|
+
...attachAllowed ? { attach: {
|
|
7780
|
+
onAttach,
|
|
7781
|
+
busy: attachWriting
|
|
7782
|
+
} } : {},
|
|
7783
|
+
...detachAllowed ? { detach: { onDetach } } : {}
|
|
7784
|
+
}, [
|
|
7785
|
+
attachAllowed,
|
|
7786
|
+
attachWriting,
|
|
7787
|
+
detachAllowed,
|
|
7788
|
+
onAttach,
|
|
7789
|
+
onDetach
|
|
7790
|
+
]),
|
|
7791
|
+
element: jsxs(Fragment$1, { children: [
|
|
7792
|
+
picker.element,
|
|
7793
|
+
jsx(CheckConfirmDialog, {
|
|
7794
|
+
state: gate,
|
|
7795
|
+
testid: `link-detach-confirm:${junction}`,
|
|
7796
|
+
title: detachLabel,
|
|
7797
|
+
proceedLabel: detachLabel,
|
|
7798
|
+
tone: "destructive"
|
|
7799
|
+
}),
|
|
7800
|
+
summary === null ? null : jsx(LinkWriteSummary, {
|
|
7801
|
+
report: summary.report,
|
|
7802
|
+
titles: summary.titles,
|
|
7803
|
+
onClose: () => setSummary(null),
|
|
7804
|
+
testid: `link-write-summary:${junction}`
|
|
7805
|
+
})
|
|
7806
|
+
] })
|
|
7807
|
+
};
|
|
7808
|
+
}
|
|
7809
|
+
//#endregion
|
|
7810
|
+
//#region src/quick/peek-filter.ts
|
|
7811
|
+
function peekFieldDefinition(def, name) {
|
|
7812
|
+
return declaredField(def, name);
|
|
7813
|
+
}
|
|
7814
|
+
function peekLeafAllowed(def, name, capabilityFor, fieldVisible) {
|
|
7815
|
+
const field = peekFieldDefinition(def, name);
|
|
7816
|
+
if (field === void 0 || fieldSensitivity(field) !== void 0) return false;
|
|
7817
|
+
if (capabilityFor("view", {
|
|
7818
|
+
kind: "column",
|
|
7819
|
+
name
|
|
7820
|
+
}).allowed === false) return false;
|
|
7821
|
+
return fieldVisible(name);
|
|
7822
|
+
}
|
|
7823
|
+
function peekRow(def, row, idField, capabilityFor, fieldVisible, drawn) {
|
|
7824
|
+
const out = {};
|
|
7825
|
+
for (const [name, value] of Object.entries(row)) out[name] = name === idField || (drawn === void 0 || drawn.has(name)) && peekLeafAllowed(def, name, capabilityFor, fieldVisible) ? value : null;
|
|
7826
|
+
return Object.freeze(out);
|
|
7827
|
+
}
|
|
7828
|
+
function peekRecord(def, row, idField, capabilityFor, fieldVisible) {
|
|
7829
|
+
if (def === void 0) return Object.freeze({ [idField]: row[idField] });
|
|
7830
|
+
return peekRow(def, row, idField, capabilityFor, fieldVisible);
|
|
7831
|
+
}
|
|
7832
|
+
//#endregion
|
|
7833
|
+
//#region src/blocks/link-panel.tsx
|
|
7834
|
+
const isRecord = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
|
|
7835
|
+
const isResourceHandle = (value) => isRecord(value) && value.kind === "resource";
|
|
7836
|
+
function qualify(ref) {
|
|
7837
|
+
try {
|
|
7838
|
+
return resourceKey(normalizeEntityRef(isResourceHandle(ref) ? ref.ref : ref));
|
|
7839
|
+
} catch {
|
|
7840
|
+
return;
|
|
7841
|
+
}
|
|
7842
|
+
}
|
|
7843
|
+
function linkField(registry, owner, link) {
|
|
7844
|
+
if (owner === void 0) return void 0;
|
|
7845
|
+
const def = registry.get(owner);
|
|
7846
|
+
const field = def === void 0 ? void 0 : peekFieldDefinition(def, link);
|
|
7847
|
+
if (!isRecord(field)) return void 0;
|
|
7848
|
+
const declaration = linkOf(field);
|
|
7849
|
+
return declaration === void 0 ? void 0 : {
|
|
7850
|
+
field,
|
|
7851
|
+
declaration
|
|
7852
|
+
};
|
|
7853
|
+
}
|
|
7854
|
+
function linkTarget(registry, owner, link) {
|
|
7855
|
+
const found = linkField(registry, owner, link);
|
|
7856
|
+
if (found === void 0 || owner === void 0) return void 0;
|
|
7857
|
+
return qualify(found.field.meta?.to);
|
|
7858
|
+
}
|
|
7859
|
+
function idFieldOf(def) {
|
|
7860
|
+
const declared = isRecord(def) && isRecord(def.data) ? def.data.idField : void 0;
|
|
7861
|
+
return typeof declared === "string" && declared.length > 0 ? declared : "id";
|
|
7862
|
+
}
|
|
7863
|
+
function byOf(field) {
|
|
7864
|
+
const by = isRecord(field) && isRecord(field.meta) ? field.meta.by : void 0;
|
|
7865
|
+
return typeof by === "string" && by.length > 0 ? by : void 0;
|
|
7866
|
+
}
|
|
7867
|
+
const MAX_TARGET_HOPS = 2;
|
|
7868
|
+
function targetColumns(registry, target, def) {
|
|
7869
|
+
if (!isRecord(def) || !isRecord(def.fields)) return [];
|
|
7870
|
+
const source = def;
|
|
7871
|
+
const views = isRecord(def.views) ? def.views : void 0;
|
|
7872
|
+
const list = views !== void 0 && isRecord(views.list) ? views.list : void 0;
|
|
7873
|
+
return (list !== void 0 && Array.isArray(list.columns) ? list.columns.flatMap((entry) => {
|
|
7874
|
+
const name = isRecord(entry) ? entry.field : entry;
|
|
7875
|
+
return typeof name === "string" ? [name] : [];
|
|
7876
|
+
}) : dtoFields(source, "list")).filter((name) => {
|
|
7877
|
+
if (name.includes(".")) return name.split(".").length - 1 <= MAX_TARGET_HOPS && resolveColumnField(registry, target, name) !== void 0;
|
|
7878
|
+
const field = peekFieldDefinition(source, name);
|
|
7879
|
+
return isRecord(field) && !isRelationField(field) && fieldSensitivity(field) === void 0;
|
|
7880
|
+
});
|
|
7881
|
+
}
|
|
7882
|
+
function LinkPanel(props) {
|
|
7883
|
+
const { link, owner, own, empty } = props;
|
|
7884
|
+
const app = useApp();
|
|
7885
|
+
const frame = usePlacementFrame();
|
|
7886
|
+
const routeScreen = useIsRouteScreen();
|
|
7887
|
+
const record = useOptionalRecordFrame()?.record;
|
|
7888
|
+
const runtime = useOptionalDataRuntime();
|
|
7889
|
+
const storage = useMemo(() => {
|
|
7890
|
+
const found = linkField(app.registry, owner, link);
|
|
7891
|
+
if (found === void 0) return void 0;
|
|
7892
|
+
const { declaration } = found;
|
|
7893
|
+
const junction = qualify(declaration.junction);
|
|
7894
|
+
const target = qualify(found.field.meta?.to);
|
|
7895
|
+
if (junction === void 0 || target === void 0) return void 0;
|
|
7896
|
+
const junctionDef = app.registry.get(junction);
|
|
7897
|
+
if (junctionDef === void 0) return void 0;
|
|
7898
|
+
const junctionFields = junctionDef.fields;
|
|
7899
|
+
const targetDef = app.registry.get(target);
|
|
7900
|
+
const targetBy = byOf(junctionFields[declaration.targetKey]);
|
|
7901
|
+
const declaredScope = found.field.meta?.scope;
|
|
7902
|
+
return {
|
|
7903
|
+
junction,
|
|
7904
|
+
target,
|
|
7905
|
+
sourceKey: declaration.sourceKey,
|
|
7906
|
+
targetKey: declaration.targetKey,
|
|
7907
|
+
ownerField: byOf(junctionFields[declaration.sourceKey]) ?? idFieldOf(app.registry.get(owner)),
|
|
7908
|
+
targetKeyedBy: targetBy !== void 0,
|
|
7909
|
+
targetValueField: targetBy ?? idFieldOf(targetDef),
|
|
7910
|
+
junctionIdField: idFieldOf(junctionDef),
|
|
7911
|
+
where: declaration.where,
|
|
7912
|
+
targetDef,
|
|
7913
|
+
pickerScope: declaredScope === "context" || declaredScope === "session" || declaredScope === "none" ? declaredScope : void 0
|
|
7914
|
+
};
|
|
7915
|
+
}, [
|
|
7916
|
+
app.registry,
|
|
7917
|
+
owner,
|
|
7918
|
+
link
|
|
7919
|
+
]);
|
|
7920
|
+
const ownerKey = storage === void 0 ? void 0 : record?.[storage.ownerField];
|
|
7921
|
+
const whereKey = JSON.stringify(own.where ?? []);
|
|
7922
|
+
const fixedFilters = useMemo(() => {
|
|
7923
|
+
if (storage === void 0 || ownerKey === null || ownerKey === void 0) return void 0;
|
|
7924
|
+
const { sourceKey, targetKey } = storage;
|
|
7925
|
+
const placed = whereToFilter(own.where ?? [], (field) => `${targetKey}.${field}`);
|
|
7926
|
+
return [
|
|
7927
|
+
whereEntryToFilter(sourceKey, "eq", ownerKey),
|
|
7928
|
+
...storage.where.map((entry) => whereEntryToFilter(entry.field, entry.op, entry.value)),
|
|
7929
|
+
...placed === void 0 ? [] : [placed]
|
|
7930
|
+
];
|
|
7931
|
+
}, [
|
|
7932
|
+
storage,
|
|
7933
|
+
ownerKey,
|
|
7934
|
+
whereKey
|
|
7935
|
+
]);
|
|
7936
|
+
const columnsKey = JSON.stringify([own.columns ?? null, own.linkColumns ?? null]);
|
|
7937
|
+
const columns = useMemo(() => {
|
|
7938
|
+
if (storage === void 0) return void 0;
|
|
7939
|
+
const shown = own.columns ?? targetColumns(app.registry, storage.target, storage.targetDef);
|
|
7940
|
+
const across = (entry) => typeof entry === "string" ? `${storage.targetKey}.${entry}` : {
|
|
7941
|
+
...entry,
|
|
7942
|
+
field: `${storage.targetKey}.${entry.field}`
|
|
7943
|
+
};
|
|
7944
|
+
return [...shown.map(across), ...own.linkColumns ?? []];
|
|
7945
|
+
}, [
|
|
7946
|
+
storage,
|
|
7947
|
+
columnsKey,
|
|
7948
|
+
app.registry
|
|
7949
|
+
]);
|
|
7950
|
+
const rowTarget = useMemo(() => storage === void 0 || storage.targetKeyedBy ? void 0 : {
|
|
7951
|
+
resource: storage.target,
|
|
7952
|
+
field: storage.targetKey
|
|
7953
|
+
}, [storage]);
|
|
7954
|
+
const unbound = fixedFilters === void 0 || own.where !== void 0 && hasNullRef(own.where);
|
|
7955
|
+
if (storage === void 0 || unbound) return jsx(UnboundRelatedList, {
|
|
7956
|
+
resource: storage?.target ?? "",
|
|
7957
|
+
title: own.title,
|
|
7958
|
+
...empty === void 0 ? {} : { empty }
|
|
7959
|
+
});
|
|
7960
|
+
const state = frame?.state === "url" && routeScreen && !frame.nested ? { url: frame.key } : "memory";
|
|
7961
|
+
let providerCounts;
|
|
7962
|
+
if (runtime !== null) try {
|
|
7963
|
+
providerCounts = linkCapabilitiesOf(runtime, runtime.app.resolveDataBinding(storage.junction).providerId)?.links?.count;
|
|
7964
|
+
} catch {
|
|
7965
|
+
providerCounts = void 0;
|
|
7966
|
+
}
|
|
7967
|
+
const count = own.count === false ? "off" : own.count ?? (providerCounts === false ? "off" : void 0);
|
|
7968
|
+
const layoutId = frame === null ? void 0 : placementLayoutId({
|
|
7969
|
+
owner,
|
|
7970
|
+
key: frame.key
|
|
7971
|
+
});
|
|
7972
|
+
const surface = {
|
|
7973
|
+
resource: storage.junction,
|
|
7974
|
+
variant: "placement",
|
|
7975
|
+
state,
|
|
7976
|
+
fixedFilters,
|
|
7977
|
+
...own.initial?.pageSize === void 0 ? {} : { initial: { pageSize: own.initial.pageSize } },
|
|
7978
|
+
...own.search === void 0 ? {} : { search: `${storage.targetKey}.${own.search}` },
|
|
7979
|
+
...columns === void 0 ? {} : { columns },
|
|
7980
|
+
open: rowTarget === void 0 || own.open === "none" ? "none" : "page",
|
|
7981
|
+
...rowTarget === void 0 ? {} : { rowTarget },
|
|
7982
|
+
...frame === null ? {} : { placementKey: frame.key },
|
|
7983
|
+
...layoutId === void 0 ? {} : { layoutId },
|
|
7984
|
+
...own.title === void 0 ? {} : { title: own.title },
|
|
7985
|
+
...empty === void 0 ? {} : { empty },
|
|
7986
|
+
scope: placementScope(own.scope, owner),
|
|
7987
|
+
...count === void 0 ? {} : { count },
|
|
7988
|
+
readOnly: true,
|
|
7989
|
+
exportable: false
|
|
7990
|
+
};
|
|
7991
|
+
if (runtime === null || record === void 0) return jsx(ResourceListSurface, { ...surface });
|
|
7992
|
+
return jsx(WritableLinkPanel, {
|
|
7993
|
+
surface,
|
|
7994
|
+
owner,
|
|
7995
|
+
link,
|
|
7996
|
+
storage,
|
|
7997
|
+
record,
|
|
7998
|
+
ownerKey,
|
|
7999
|
+
placement: own.linkActions
|
|
8000
|
+
});
|
|
8001
|
+
}
|
|
8002
|
+
function WritableLinkPanel(props) {
|
|
8003
|
+
const { surface, owner, link, storage, record, ownerKey, placement } = props;
|
|
8004
|
+
const displayField = isRecord(storage.targetDef) && typeof storage.targetDef.displayField === "string" ? storage.targetDef.displayField : storage.targetValueField;
|
|
8005
|
+
const { linkEditing, element } = useLinkPanelWrites({
|
|
8006
|
+
owner,
|
|
8007
|
+
junction: storage.junction,
|
|
8008
|
+
target: storage.target,
|
|
8009
|
+
link,
|
|
8010
|
+
targetKey: storage.targetKey,
|
|
8011
|
+
valueField: storage.targetValueField,
|
|
8012
|
+
junctionIdField: storage.junctionIdField,
|
|
8013
|
+
displayField,
|
|
8014
|
+
scope: storage.pickerScope ?? "context",
|
|
8015
|
+
record,
|
|
8016
|
+
ownerKey,
|
|
8017
|
+
placement
|
|
8018
|
+
});
|
|
8019
|
+
return jsxs(Fragment$1, { children: [jsx(ResourceListSurface, {
|
|
8020
|
+
...surface,
|
|
8021
|
+
...linkEditing === void 0 ? {} : { linkEditing }
|
|
8022
|
+
}), element] });
|
|
8023
|
+
}
|
|
8024
|
+
//#endregion
|
|
6875
8025
|
//#region src/blocks/route-list-context.ts
|
|
6876
8026
|
const RouteListContext = createContext(null);
|
|
6877
8027
|
function useRouteList() {
|
|
@@ -6879,36 +8029,43 @@ function useRouteList() {
|
|
|
6879
8029
|
}
|
|
6880
8030
|
//#endregion
|
|
6881
8031
|
//#region src/blocks/related-list-view.tsx
|
|
6882
|
-
function RelatedListView({ props }) {
|
|
8032
|
+
function RelatedListView({ props, empty }) {
|
|
6883
8033
|
const own = props;
|
|
6884
8034
|
const route = useRouteList();
|
|
6885
8035
|
const frame = usePlacementFrame();
|
|
6886
|
-
const
|
|
8036
|
+
const app = useApp();
|
|
8037
|
+
const linked = own.link !== void 0;
|
|
8038
|
+
const resource = linked ? linkTarget(app.registry, frame?.owner, own.link) ?? "" : resourceKey(own.resource);
|
|
6887
8039
|
const whereKey = JSON.stringify(own.where ?? []);
|
|
6888
|
-
const fixedFilters = useMemo(() => own.where === void 0 ? void 0 : own.where
|
|
6889
|
-
field,
|
|
6890
|
-
op,
|
|
6891
|
-
value
|
|
6892
|
-
})), [whereKey]);
|
|
8040
|
+
const fixedFilters = useMemo(() => own.where === void 0 || own.where.length === 0 ? void 0 : [whereToFilter(own.where)], [whereKey]);
|
|
6893
8041
|
const columnsKey = JSON.stringify(own.columns ?? null);
|
|
6894
8042
|
const columns = useMemo(() => own.columns, [columnsKey]);
|
|
6895
8043
|
const routeScreen = useIsRouteScreen();
|
|
6896
8044
|
const inQuick = useQuickEntry() !== null;
|
|
6897
8045
|
useMemoryNotice(frame, routeScreen || inQuick, route !== null);
|
|
6898
|
-
const t = useOptionalT();
|
|
6899
8046
|
const permissions = useCtx((c) => c.permissions);
|
|
6900
|
-
const app = useApp();
|
|
6901
8047
|
const gate = useEntryGate();
|
|
6902
8048
|
const quick = useQuick();
|
|
6903
8049
|
const ownerDef = frame?.owner === void 0 ? void 0 : app.registry.get(frame.owner);
|
|
6904
8050
|
const granted = ownerDef === void 0 ? "unknown" : grantedPlacementKeys(ownerDef);
|
|
6905
8051
|
const trusted = frame !== null && !frame.nested && granted !== "unknown" && !granted.has(frame.key);
|
|
6906
|
-
const onCreate = route === null && trusted && gate(resource, "create").state === "open" && frame !== null ? () => void quick.create(resource, { defaults: placementSeeds(frame.where ?? [], own.where ?? [], frame.closure ?? {}) }) : void 0;
|
|
6907
|
-
useDeepLinkOpen(frame?.key ?? null, resource, route === null && frame !== null && !frame.nested && routeScreen && (own.open ?? "quick") === "quick");
|
|
6908
|
-
const unbound = own.where
|
|
8052
|
+
const onCreate = route === null && !linked && trusted && gate(resource, "create").state === "open" && own.create !== false && (own.create === void 0 || actionWhenHolds(own.create, frame?.sources ?? {})) && frame !== null ? () => void quick.create(resource, { defaults: placementSeeds(frame.where ?? [], own.where ?? [], frame.closure ?? {}) }) : void 0;
|
|
8053
|
+
useDeepLinkOpen(frame?.key ?? null, resource, route === null && !linked && frame !== null && !frame.nested && routeScreen && (own.open ?? "quick") === "quick");
|
|
8054
|
+
const unbound = own.where !== void 0 && hasNullRef(own.where);
|
|
8055
|
+
if (linked) return frame?.owner === void 0 ? jsx(UnboundRelatedList, {
|
|
8056
|
+
resource,
|
|
8057
|
+
title: own.title,
|
|
8058
|
+
...empty === void 0 ? {} : { empty }
|
|
8059
|
+
}) : jsx(LinkPanel, {
|
|
8060
|
+
link: own.link,
|
|
8061
|
+
owner: frame.owner,
|
|
8062
|
+
own,
|
|
8063
|
+
...empty === void 0 ? {} : { empty }
|
|
8064
|
+
});
|
|
6909
8065
|
if (route !== null) return jsx(ResourceListSurface, {
|
|
6910
8066
|
resource,
|
|
6911
8067
|
variant: "route",
|
|
8068
|
+
layoutId: "route",
|
|
6912
8069
|
...route.meta === void 0 ? {} : { meta: route.meta },
|
|
6913
8070
|
...route.renderCell === void 0 ? {} : { renderCell: route.renderCell },
|
|
6914
8071
|
...route.emptyState === void 0 ? {} : { emptyState: route.emptyState }
|
|
@@ -6919,31 +8076,32 @@ function RelatedListView({ props }) {
|
|
|
6919
8076
|
"data-testid": `list-undecided:${resource}`,
|
|
6920
8077
|
children: own.view === "cards" ? jsx(RelatedCardsSkeleton, {}) : jsx(RelatedTimelineSkeleton, {})
|
|
6921
8078
|
});
|
|
6922
|
-
if (unbound) return
|
|
6923
|
-
|
|
6924
|
-
|
|
6925
|
-
|
|
6926
|
-
className: "text-sm font-semibold",
|
|
6927
|
-
children: own.title
|
|
6928
|
-
}), jsx("p", {
|
|
6929
|
-
className: "text-sm text-muted-foreground",
|
|
6930
|
-
children: t("table.empty", { defaultValue: "No records" })
|
|
6931
|
-
})]
|
|
8079
|
+
if (unbound) return jsx(UnboundRelatedList, {
|
|
8080
|
+
resource,
|
|
8081
|
+
title: own.title,
|
|
8082
|
+
...empty === void 0 ? {} : { empty }
|
|
6932
8083
|
});
|
|
6933
8084
|
const state = frame?.state === "url" && routeScreen && !frame.nested ? { url: frame.key } : "memory";
|
|
8085
|
+
const scope = placementScope(own.scope, frame?.owner);
|
|
8086
|
+
const count = own.count === false ? "off" : own.count;
|
|
8087
|
+
const layoutId = placementLayoutId(frame);
|
|
6934
8088
|
return jsx(ResourceListSurface, {
|
|
6935
8089
|
resource,
|
|
6936
8090
|
variant: "placement",
|
|
6937
8091
|
state,
|
|
6938
8092
|
...fixedFilters === void 0 ? {} : { fixedFilters },
|
|
6939
|
-
...own.
|
|
8093
|
+
...own.initial === void 0 ? {} : { initial: own.initial },
|
|
6940
8094
|
...own.search === void 0 ? {} : { search: own.search },
|
|
6941
8095
|
...columns === void 0 ? {} : { columns },
|
|
6942
8096
|
open: own.open ?? "quick",
|
|
6943
8097
|
...own.view === void 0 ? {} : { view: own.view },
|
|
6944
8098
|
...isItemHandle(own.item) ? { item: own.item } : {},
|
|
6945
8099
|
...frame === null ? {} : { placementKey: frame.key },
|
|
8100
|
+
...layoutId === void 0 ? {} : { layoutId },
|
|
6946
8101
|
...own.title === void 0 ? {} : { title: own.title },
|
|
8102
|
+
...empty === void 0 ? {} : { empty },
|
|
8103
|
+
scope,
|
|
8104
|
+
...count === void 0 ? {} : { count },
|
|
6947
8105
|
...onCreate === void 0 ? {} : { onCreate }
|
|
6948
8106
|
});
|
|
6949
8107
|
}
|
|
@@ -6976,7 +8134,7 @@ function Missing({ text }) {
|
|
|
6976
8134
|
children: text ?? t("table.empty", { defaultValue: "No records" })
|
|
6977
8135
|
});
|
|
6978
8136
|
}
|
|
6979
|
-
function RelatedRecordView({ props }) {
|
|
8137
|
+
function RelatedRecordView({ props, empty }) {
|
|
6980
8138
|
const own = props;
|
|
6981
8139
|
const app = useApp();
|
|
6982
8140
|
const resource = resourceKey(own.resource);
|
|
@@ -7010,15 +8168,15 @@ function RelatedRecordView({ props }) {
|
|
|
7010
8168
|
capabilityFor,
|
|
7011
8169
|
access
|
|
7012
8170
|
]);
|
|
7013
|
-
if (id === null) return jsx(Missing, { text: own.missing });
|
|
8171
|
+
if (id === null) return jsx(Missing, { text: own.missing ?? empty });
|
|
7014
8172
|
if (result === void 0) {
|
|
7015
|
-
if (query.error != null) return jsx(Missing, { text: own.missing });
|
|
8173
|
+
if (query.error != null) return jsx(Missing, { text: own.missing ?? empty });
|
|
7016
8174
|
return jsx(Skeleton, {
|
|
7017
8175
|
"data-buro-skeleton": "",
|
|
7018
8176
|
className: "h-9 w-full"
|
|
7019
8177
|
});
|
|
7020
8178
|
}
|
|
7021
|
-
if (dtoRow === void 0) return jsx(Missing, { text: own.missing });
|
|
8179
|
+
if (dtoRow === void 0) return jsx(Missing, { text: own.missing ?? empty });
|
|
7022
8180
|
if (projected.status === "pending" || projected.value === null) return jsx(Skeleton, {
|
|
7023
8181
|
"data-buro-skeleton": "",
|
|
7024
8182
|
className: "h-9 w-full"
|
|
@@ -7048,16 +8206,24 @@ function RelatedRecordView({ props }) {
|
|
|
7048
8206
|
className: "flex min-w-0 flex-col gap-0.5",
|
|
7049
8207
|
children: [jsx("dt", {
|
|
7050
8208
|
className: FIELD_LABEL_SHOW,
|
|
7051
|
-
children: resolveFieldLabel(caps, name, field
|
|
8209
|
+
children: resolveFieldLabel(caps, name, declaredText("field", field, "label"))
|
|
7052
8210
|
}), jsx("dd", {
|
|
7053
8211
|
className: "min-w-0 break-words text-sm",
|
|
7054
|
-
children: jsx(
|
|
7055
|
-
|
|
7056
|
-
|
|
7057
|
-
|
|
8212
|
+
children: jsx(FieldValueFrame, {
|
|
8213
|
+
field: {
|
|
8214
|
+
...field ?? {},
|
|
8215
|
+
name
|
|
8216
|
+
},
|
|
7058
8217
|
value,
|
|
7059
|
-
|
|
7060
|
-
|
|
8218
|
+
surface: "display",
|
|
8219
|
+
children: jsx(ResolvedFieldDisplay, {
|
|
8220
|
+
resource: own.resource,
|
|
8221
|
+
name,
|
|
8222
|
+
field,
|
|
8223
|
+
value,
|
|
8224
|
+
record: row,
|
|
8225
|
+
fallback: toDisplayText(value).text
|
|
8226
|
+
})
|
|
7061
8227
|
})
|
|
7062
8228
|
})]
|
|
7063
8229
|
}, name);
|
|
@@ -7128,28 +8294,39 @@ function Chrome({ node, size, state, children }) {
|
|
|
7128
8294
|
});
|
|
7129
8295
|
}
|
|
7130
8296
|
function isAuthError(error) {
|
|
7131
|
-
return
|
|
8297
|
+
return normalizeDataError(error).kind === "auth";
|
|
7132
8298
|
}
|
|
7133
|
-
|
|
8299
|
+
const PLACE_NODE_MESSAGES = {
|
|
8300
|
+
failed: "This section could not be loaded.",
|
|
8301
|
+
sessionExpired: "Your session has expired. Sign in again to continue.",
|
|
8302
|
+
notImplemented: "This section is not available."
|
|
8303
|
+
};
|
|
8304
|
+
function FailedPlacement({ node, size, message, retry, error }) {
|
|
7134
8305
|
const t = useOptionalT();
|
|
8306
|
+
const chromeT = useChromeT();
|
|
8307
|
+
const fallback = {
|
|
8308
|
+
key: `block.${message}`,
|
|
8309
|
+
default: PLACE_NODE_MESSAGES[message]
|
|
8310
|
+
};
|
|
7135
8311
|
return jsx(Chrome, {
|
|
7136
8312
|
node,
|
|
7137
8313
|
size,
|
|
7138
8314
|
state: "failed",
|
|
7139
|
-
children: jsxs("div", {
|
|
8315
|
+
children: message === "notImplemented" ? jsxs("div", {
|
|
7140
8316
|
role: "alert",
|
|
7141
8317
|
className: "flex flex-wrap items-center gap-2 rounded-md border border-dashed px-3 py-2 text-sm text-muted-foreground",
|
|
7142
|
-
children: [jsx("span", { children: t(
|
|
7143
|
-
failed: "This section could not be loaded.",
|
|
7144
|
-
sessionExpired: "Your session has expired. Sign in again to continue.",
|
|
7145
|
-
notImplemented: "This section is not available."
|
|
7146
|
-
}[message] }) }), retry === void 0 ? null : jsx(Button, {
|
|
8318
|
+
children: [jsx("span", { children: t(fallback.key, { defaultValue: fallback.default }) }), retry === void 0 ? null : jsx(Button, {
|
|
7147
8319
|
type: "button",
|
|
7148
8320
|
variant: "outline",
|
|
7149
8321
|
size: "sm",
|
|
7150
8322
|
onClick: retry,
|
|
7151
|
-
children:
|
|
8323
|
+
children: chromeT("actions.retry", { defaultValue: "Retry" })
|
|
7152
8324
|
})]
|
|
8325
|
+
}) : jsx(LoadError, {
|
|
8326
|
+
surface: "block",
|
|
8327
|
+
error,
|
|
8328
|
+
...retry === void 0 ? {} : { retry },
|
|
8329
|
+
fallback
|
|
7153
8330
|
})
|
|
7154
8331
|
});
|
|
7155
8332
|
}
|
|
@@ -7229,7 +8406,8 @@ function ReadyPlacement({ node, size, placement, implementation }) {
|
|
|
7229
8406
|
nested,
|
|
7230
8407
|
...Array.isArray(declaredWhere) ? { where: declaredWhere } : {},
|
|
7231
8408
|
closure: compiled.sourceClosure,
|
|
7232
|
-
...owner === void 0 ? {} : { owner }
|
|
8409
|
+
...owner === void 0 ? {} : { owner },
|
|
8410
|
+
sources: controller.sources
|
|
7233
8411
|
}), [
|
|
7234
8412
|
node.key,
|
|
7235
8413
|
node.id,
|
|
@@ -7237,7 +8415,8 @@ function ReadyPlacement({ node, size, placement, implementation }) {
|
|
|
7237
8415
|
nested,
|
|
7238
8416
|
declaredWhere,
|
|
7239
8417
|
compiled.sourceClosure,
|
|
7240
|
-
owner
|
|
8418
|
+
owner,
|
|
8419
|
+
controller.sources
|
|
7241
8420
|
]);
|
|
7242
8421
|
return jsx(PlacementBoundary, {
|
|
7243
8422
|
node,
|
|
@@ -7295,6 +8474,7 @@ function PlaceNode({ node }) {
|
|
|
7295
8474
|
node,
|
|
7296
8475
|
size,
|
|
7297
8476
|
message: isAuthError(placement.error) ? "sessionExpired" : "failed",
|
|
8477
|
+
error: placement.error,
|
|
7298
8478
|
...placement.retry === void 0 ? {} : { retry: placement.retry }
|
|
7299
8479
|
});
|
|
7300
8480
|
if (implementation === void 0) return jsx(FailedPlacement, {
|
|
@@ -7310,6 +8490,6 @@ function PlaceNode({ node }) {
|
|
|
7310
8490
|
});
|
|
7311
8491
|
}
|
|
7312
8492
|
//#endregion
|
|
7313
|
-
export {
|
|
8493
|
+
export { CursorLoadMore as $, ListSearchInput as A, EnumBadge as At, ExportDialog as B, usePaletteState as Bt, ListControls as C, recordReturnTarget as Ct, ListFlows as D, useShownItemActions as Dt, ListLive as E, ActionOverflow as Et, ListColumns as F, i18nPrefixToRef$1 as Ft, RouteScreenClaimContext as G, ScreenTree as H, ColumnsToggle as I, FieldValueFrame as It, FormControlProvider as J, RouteScreenContext as K, DataTableViewOptions as L, toDisplayText as Lt, ExportButton as M, AppFlowButton as Mt, ListCreate as N, Field as Nt, ListFilters as O, useExportRunWatcher as Ot, CreateButton as P, FieldDisplay as Pt, ResourceList as Q, ListBulkBar as R, ShellProvider as Rt, useFrameChrome as S, listTarget as St, SavedViewsDropdown as T, showTarget as Tt, useTabsUrlState as U, DeclarativeForm as V, useShell as Vt, QuickScreenClaimContext as W, useFormControlOf as X, useFormControl as Y, ListBody as Z, CopyId as _, resolveResourceTitle as _t, peekFieldDefinition as a, DataTable as at, RecordTitle as b, typography as bt, peekRow as c, TabsContent as ct, resolveFrameView as d, RelatedItem as dt, ListErrorState as et, shadcnFrameViews as f, useItemDrawn as ft, UndeclaredTypeNotice as g, ShowPageHeader as gt, TypeChromeDrawnContext as h, PageHeader as ht, RouteListContext as i, useBuroDataTable as it, ListExport as j, DeepLinkScope as jt, FilterRegion as k, downloadArtifact as kt, useRelationPicker as l, TabsList as lt, HeaderTypeNotice as m, EditPageHeader as mt, viewOf as n, resolveUpdater as nt, peekLeafAllowed as o, Checkbox as ot, ChangeTypeButton as p, RELATED_GRID_CLASS as pt, useIsRouteScreen as q, shadcnBlockImplementations as r, tanStackSortingToBuro as rt, peekRecord as s, Tabs as st, selectImplementation as t, buroSortingToTanStack as tt, isBuiltinRecordView as u, TabsTrigger as ut, FormActions as v, DocumentTitleProvider as vt, ListSavedViews as w, recordRouteAvailable as wt, FrameChromeContext as x, isShowAvailable as xt, RecordActions as y, useDocumentTitle as yt, BulkActionToolbar as z, defaultUserMapper as zt };
|
|
7314
8494
|
|
|
7315
|
-
//# sourceMappingURL=place-node
|
|
8495
|
+
//# sourceMappingURL=place-node-CfzQvh4Y.js.map
|