@burojs/ui-shadcn 0.8.0 → 0.10.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/dist/{bulk-summary-2HLgz7Il.cjs → bulk-summary-Chc61Pyw.cjs} +9 -7
- package/dist/bulk-summary-Chc61Pyw.cjs.map +1 -0
- package/dist/{bulk-summary-D6A0ua21.js → bulk-summary-DpSQD2Fq.js} +9 -7
- package/dist/bulk-summary-DpSQD2Fq.js.map +1 -0
- package/dist/{candidates-relation-picker-CGXItMN0.js → candidates-relation-picker-DRsHKe6N.js} +3 -3
- package/dist/{candidates-relation-picker-CGXItMN0.js.map → candidates-relation-picker-DRsHKe6N.js.map} +1 -1
- package/dist/{candidates-relation-picker-C47mF260.cjs → candidates-relation-picker-GIxsp7D_.cjs} +3 -3
- package/dist/{candidates-relation-picker-C47mF260.cjs.map → candidates-relation-picker-GIxsp7D_.cjs.map} +1 -1
- package/dist/{check-confirm-dialog-BT04Mieo.cjs → check-confirm-dialog-BMSb7wFv.cjs} +76 -9
- package/dist/check-confirm-dialog-BMSb7wFv.cjs.map +1 -0
- package/dist/{check-confirm-dialog-CtiRzOAt.js → check-confirm-dialog-B_u3YVHS.js} +72 -11
- package/dist/{check-confirm-dialog-CtiRzOAt.js.map → check-confirm-dialog-B_u3YVHS.js.map} +1 -1
- package/dist/{filter-builder-z3Axzf9Y.js → filter-builder-DX-xC3Yd.js} +14 -6
- package/dist/filter-builder-DX-xC3Yd.js.map +1 -0
- package/dist/{filter-builder-ByGhxLf_.cjs → filter-builder-Dt5E8PSX.cjs} +14 -6
- package/dist/filter-builder-Dt5E8PSX.cjs.map +1 -0
- package/dist/{flow-runtime-BVFjMU5-.cjs → flow-runtime-CQ4wf8co.cjs} +3 -3
- package/dist/{flow-runtime-BVFjMU5-.cjs.map → flow-runtime-CQ4wf8co.cjs.map} +1 -1
- package/dist/{flow-runtime-DrUWyTS0.js → flow-runtime-Q2vD7R3c.js} +3 -3
- package/dist/{flow-runtime-DrUWyTS0.js.map → flow-runtime-Q2vD7R3c.js.map} +1 -1
- package/dist/{flow-screen-3fk6J33y.js → flow-screen-B1y2yizP.js} +68 -29
- package/dist/flow-screen-B1y2yizP.js.map +1 -0
- package/dist/{flow-screen-6y5E_HT6.cjs → flow-screen-sw2cP70t.cjs} +67 -28
- package/dist/flow-screen-sw2cP70t.cjs.map +1 -0
- package/dist/index.cjs +2304 -247
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +119 -12
- package/dist/index.d.ts +119 -12
- package/dist/index.js +2225 -172
- package/dist/index.js.map +1 -1
- package/dist/{inline-flow-host-C4KmZIYE.cjs → inline-flow-host-CkhoA69R.cjs} +3 -3
- package/dist/{inline-flow-host-C4KmZIYE.cjs.map → inline-flow-host-CkhoA69R.cjs.map} +1 -1
- package/dist/{inline-flow-host-YnnvWjdU.js → inline-flow-host-DqbXqcPS.js} +3 -3
- package/dist/{inline-flow-host-YnnvWjdU.js.map → inline-flow-host-DqbXqcPS.js.map} +1 -1
- package/dist/{lazy-relation-picker-dialog-DN4bvQfg.js → lazy-relation-picker-dialog-BaIEWLbF.js} +3 -52
- package/dist/lazy-relation-picker-dialog-BaIEWLbF.js.map +1 -0
- package/dist/{lazy-relation-picker-dialog-BAKBRn3V.cjs → lazy-relation-picker-dialog-CeRzOAi0.cjs} +3 -52
- package/dist/lazy-relation-picker-dialog-CeRzOAi0.cjs.map +1 -0
- package/dist/{page-flow-route-CtEfqAvU.js → page-flow-route-CP7cS7FY.js} +7 -7
- package/dist/page-flow-route-CP7cS7FY.js.map +1 -0
- package/dist/{page-flow-route-DEjAxHe3.cjs → page-flow-route-UDx8qvdM.cjs} +10 -10
- package/dist/page-flow-route-UDx8qvdM.cjs.map +1 -0
- package/dist/{place-node-Bhl8B3fF.cjs → place-node-C5ZmuHaN.cjs} +208 -55
- package/dist/place-node-C5ZmuHaN.cjs.map +1 -0
- package/dist/{place-node-BdxT-30h.js → place-node-DwWAigv0.js} +205 -58
- package/dist/place-node-DwWAigv0.js.map +1 -0
- package/dist/record-trail-store-Cjo9vtod.cjs +126 -0
- package/dist/record-trail-store-Cjo9vtod.cjs.map +1 -0
- package/dist/record-trail-store-Dn6bVry4.js +91 -0
- package/dist/record-trail-store-Dn6bVry4.js.map +1 -0
- package/dist/{related-list-cards-Bc_vfCE_.cjs → related-list-cards-BypKfm9t.cjs} +3 -3
- package/dist/{related-list-cards-Bc_vfCE_.cjs.map → related-list-cards-BypKfm9t.cjs.map} +1 -1
- package/dist/{related-list-cards-BvkD5OcC.js → related-list-cards-Dcmruy09.js} +3 -3
- package/dist/{related-list-cards-BvkD5OcC.js.map → related-list-cards-Dcmruy09.js.map} +1 -1
- package/dist/{related-list-timeline-k0DtnxTs.cjs → related-list-timeline-D-KClgKb.cjs} +4 -4
- package/dist/related-list-timeline-D-KClgKb.cjs.map +1 -0
- package/dist/{related-list-timeline-DCrwN9oO.js → related-list-timeline-DZON_qSc.js} +4 -4
- package/dist/related-list-timeline-DZON_qSc.js.map +1 -0
- package/dist/{related-rows-BW9Jr07T.cjs → related-rows-DgF05ssi.cjs} +4 -4
- package/dist/{related-rows-BW9Jr07T.cjs.map → related-rows-DgF05ssi.cjs.map} +1 -1
- package/dist/{related-rows-CPE-9FFr.js → related-rows-ETZl5CB9.js} +4 -4
- package/dist/{related-rows-CPE-9FFr.js.map → related-rows-ETZl5CB9.js.map} +1 -1
- package/dist/{relation-create-DDEYcKfw.cjs → relation-create-B8zngAcA.cjs} +82 -13
- package/dist/relation-create-B8zngAcA.cjs.map +1 -0
- package/dist/{relation-create-C43cdPQt.js → relation-create-CeCWyxIb.js} +79 -16
- package/dist/relation-create-CeCWyxIb.js.map +1 -0
- package/dist/{relation-many-picker-BlrGRwMV.js → relation-many-picker-CT7nKfE2.js} +12 -10
- package/dist/relation-many-picker-CT7nKfE2.js.map +1 -0
- package/dist/{relation-many-picker-Br-zBHxT.cjs → relation-many-picker-Cq0EHY5I.cjs} +12 -10
- package/dist/relation-many-picker-Cq0EHY5I.cjs.map +1 -0
- package/dist/{relation-picker-x7P5HKie.js → relation-picker-CCyuMeGq.js} +13 -10
- package/dist/relation-picker-CCyuMeGq.js.map +1 -0
- package/dist/{relation-picker-ADjxI_TI.cjs → relation-picker-DmwTzgsd.cjs} +13 -10
- package/dist/relation-picker-DmwTzgsd.cjs.map +1 -0
- package/dist/{relation-picker-dialog-CyKliBkN.js → relation-picker-dialog-CoWKjNv8.js} +5 -5
- package/dist/relation-picker-dialog-CoWKjNv8.js.map +1 -0
- package/dist/{relation-picker-dialog-DimVQF9Y.cjs → relation-picker-dialog-mbwyNK43.cjs} +5 -5
- package/dist/relation-picker-dialog-mbwyNK43.cjs.map +1 -0
- package/dist/{relation-select-Cr3CfNLy.cjs → relation-select-CguIo2M5.cjs} +2 -2
- package/dist/{relation-select-Cr3CfNLy.cjs.map → relation-select-CguIo2M5.cjs.map} +1 -1
- package/dist/{relation-select-B8Fq4o-t.js → relation-select-nAYxhopN.js} +2 -2
- package/dist/{relation-select-B8Fq4o-t.js.map → relation-select-nAYxhopN.js.map} +1 -1
- package/dist/{success-notices-Dl3M1fZP.cjs → success-notices-Cl4b6MLQ.cjs} +3 -2
- package/dist/success-notices-Cl4b6MLQ.cjs.map +1 -0
- package/dist/{success-notices-LDEgEkc4.js → success-notices-CrcWOGSo.js} +3 -2
- package/dist/success-notices-CrcWOGSo.js.map +1 -0
- package/dist/{use-dialog-filter-state-tNvqAAjh.cjs → use-dialog-filter-state-CpWblz3C.cjs} +2 -2
- package/dist/{use-dialog-filter-state-tNvqAAjh.cjs.map → use-dialog-filter-state-CpWblz3C.cjs.map} +1 -1
- package/dist/{use-dialog-filter-state-B_MqzHaT.js → use-dialog-filter-state-ZYxOp5WO.js} +2 -2
- package/dist/{use-dialog-filter-state-B_MqzHaT.js.map → use-dialog-filter-state-ZYxOp5WO.js.map} +1 -1
- package/dist/{use-record-subtitle-bfwIoUme.cjs → use-record-subtitle-B4lTR7pf.cjs} +5 -8
- package/dist/use-record-subtitle-B4lTR7pf.cjs.map +1 -0
- package/dist/{use-record-subtitle-gIaCGgbI.js → use-record-subtitle-pWMBqQx9.js} +5 -8
- package/dist/use-record-subtitle-pWMBqQx9.js.map +1 -0
- package/package.json +9 -9
- package/dist/bulk-summary-2HLgz7Il.cjs.map +0 -1
- package/dist/bulk-summary-D6A0ua21.js.map +0 -1
- package/dist/check-confirm-dialog-BT04Mieo.cjs.map +0 -1
- package/dist/filter-builder-ByGhxLf_.cjs.map +0 -1
- package/dist/filter-builder-z3Axzf9Y.js.map +0 -1
- package/dist/flow-screen-3fk6J33y.js.map +0 -1
- package/dist/flow-screen-6y5E_HT6.cjs.map +0 -1
- package/dist/lazy-relation-picker-dialog-BAKBRn3V.cjs.map +0 -1
- package/dist/lazy-relation-picker-dialog-DN4bvQfg.js.map +0 -1
- package/dist/page-flow-route-CtEfqAvU.js.map +0 -1
- package/dist/page-flow-route-DEjAxHe3.cjs.map +0 -1
- package/dist/place-node-BdxT-30h.js.map +0 -1
- package/dist/place-node-Bhl8B3fF.cjs.map +0 -1
- package/dist/related-list-timeline-DCrwN9oO.js.map +0 -1
- package/dist/related-list-timeline-k0DtnxTs.cjs.map +0 -1
- package/dist/relation-create-C43cdPQt.js.map +0 -1
- package/dist/relation-create-DDEYcKfw.cjs.map +0 -1
- package/dist/relation-many-picker-BlrGRwMV.js.map +0 -1
- package/dist/relation-many-picker-Br-zBHxT.cjs.map +0 -1
- package/dist/relation-picker-ADjxI_TI.cjs.map +0 -1
- package/dist/relation-picker-dialog-CyKliBkN.js.map +0 -1
- package/dist/relation-picker-dialog-DimVQF9Y.cjs.map +0 -1
- package/dist/relation-picker-x7P5HKie.js.map +0 -1
- package/dist/resource-page-router-BRpUnhay.js +0 -1958
- package/dist/resource-page-router-BRpUnhay.js.map +0 -1
- package/dist/resource-page-router-qEd2xb55.cjs +0 -2167
- package/dist/resource-page-router-qEd2xb55.cjs.map +0 -1
- package/dist/success-notices-Dl3M1fZP.cjs.map +0 -1
- package/dist/success-notices-LDEgEkc4.js.map +0 -1
- package/dist/use-record-subtitle-bfwIoUme.cjs.map +0 -1
- package/dist/use-record-subtitle-gIaCGgbI.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,26 +1,27 @@
|
|
|
1
1
|
import { c as scrollToDocAnchor, l as cn, n as useTheme, s as readDocHash, t as THEME_CHOICES } from "./use-theme-dnE3Od3I.js";
|
|
2
2
|
import { a as technicalText, c as LinkComponentProvider, d as PopoverAnchor, f as PopoverClose, l as useLinkComponent, m as PopoverTrigger, n as toast, p as PopoverContent, r as friendlyStringsFrom, s as toFriendlyError, t as Toaster, u as Popover } from "./toaster-Ct_YHGs6.js";
|
|
3
|
-
import { $ as
|
|
3
|
+
import { $ as DropdownMenuTrigger, A as useLoaded, B as DropdownMenuCheckboxItem, E as loadFlowScreen, F as SheetPresentationShell, G as DropdownMenuPortal, H as DropdownMenuGroup, I as Icon, J as DropdownMenuSeparator, K as DropdownMenuRadioGroup, L as hasIcon, N as DialogPresentationShell, O as loadPageFlowRoute, P as OwnTitleContext, Q as DropdownMenuSubTrigger, R as registerIcons, T as loadFailedText, U as DropdownMenuItem, V as DropdownMenuContent, W as DropdownMenuLabel, X as DropdownMenuSub, Y as DropdownMenuShortcut, Z as DropdownMenuSubContent, at as SheetClose, b as FlowLoadFailed, c as listSlot, ct as SheetFooter, d as openExternal, dt as SheetTrigger, et as Tooltip, ft as DocInfo, it as Sheet, j as OwnTitleHeading, l as useListSlot, lt as SheetHeader, n as ActionButton, nt as TooltipProvider, o as modeForSurface, ot as SheetContent, p as FlowActionButton, q as DropdownMenuRadioItem, r as DisabledAction, rt as TooltipTrigger, s as canonicalResourceName, st as SheetDescription, t as CheckConfirmDialog, tt as TooltipContent, u as isExternalHref, ut as SheetTitle, v as reasonText, y as FlowBoundary, z as DropdownMenu } from "./check-confirm-dialog-B_u3YVHS.js";
|
|
4
4
|
import { n as buttonVariants, t as Button } from "./button-MWVeZaqj.js";
|
|
5
5
|
import { t as Input } from "./input-CUPStzZO.js";
|
|
6
6
|
import { a as DialogFooter, c as DialogTrigger, i as DialogDescription, n as DialogClose, o as DialogHeader, r as DialogContent, s as DialogTitle, t as Dialog } from "./dialog-C123sVQ8.js";
|
|
7
|
-
import { C as
|
|
7
|
+
import { A as CommandSeparator, C as CommandDialog, D as CommandItem, E as CommandInput, O as CommandList, S as Command, T as CommandGroup, b as LoadError, c as RelationField, d as peekAnchorOpener, f as PeekContext, g as recordDisplayName, k as CommandLoading, l as relationLinkClassName, m as useRowTitle, p as PeekHostContext, s as RelationDisplay, u as focusWithoutPeek, w as CommandEmpty, x as RecordState, y as InvalidRouteId } from "./relation-create-CeCWyxIb.js";
|
|
8
8
|
import { t as Skeleton } from "./skeleton-U2noefj5.js";
|
|
9
|
-
import { $ as
|
|
9
|
+
import { $ as ResourceList, A as FilterRegion, At as DeepLinkScope, B as BulkActionToolbar, Bt as useShell, C as useFrameChrome, Ct as ActionOverflow, D as ListLive, Dt as EnumBadge, E as SavedViewsDropdown, Et as downloadArtifact, F as CreateButton, Ft as FieldValueFrame, G as QuickScreenClaimContext, H as DeclarativeForm, I as ListColumns, It as toDisplayText, J as useIsRouteScreen, K as RouteScreenClaimContext, L as ColumnsToggle, Lt as ShellProvider, M as ListExport, Mt as Field, N as ExportButton, Nt as FieldDisplay, O as ListFlows, Ot as EnumSettlingRow, P as ListCreate, Pt as i18nPrefixToRef, Q as ListBody, R as DataTableViewOptions, Rt as defaultUserMapper, S as ItemActionsOffContext, St as typography, T as ListSavedViews, Tt as useExportRunWatcher, U as ScreenTree, V as ExportDialog, W as useTabsUrlState, X as useFormControl, Y as FormControlProvider, Z as useFormControlOf, _ as CopyId, _t as PageHeader, a as peekFieldDefinition, at as useBuroDataTable, b as RecordTitle, bt as DocumentTitleProvider, c as peekRow, ct as Tabs, d as resolveFrameView, dt as TabsTrigger, et as CursorLoadMore, f as shadcnFrameViews, g as UndeclaredTypeNotice, gt as EditPageHeader, h as TypeChromeDrawnContext, i as RouteListContext, it as tanStackSortingToBuro, j as ListSearchInput, jt as AppFlowButton, k as ListFilters, kt as unknownEnumValueLabel, l as useRelationPicker, lt as TabsContent, m as HeaderTypeNotice, n as viewOf, nt as buroSortingToTanStack, o as peekLeafAllowed, ot as DataTable, p as ChangeTypeButton, q as RouteScreenContext, r as shadcnBlockImplementations, rt as resolveUpdater, s as peekRecord, st as Checkbox, t as selectImplementation, tt as ListErrorState, u as isBuiltinRecordView, ut as TabsList, v as FormActions, vt as ShowPageHeader, w as ListControls, wt as useShownItemActions, x as FrameChromeContext, xt as useDocumentTitle, y as RecordActions, yt as resolveResourceTitle, z as ListBulkBar, zt as usePaletteState } from "./place-node-DwWAigv0.js";
|
|
10
10
|
import { THEME_STORAGE_KEY } from "./layout/theme-storage-key.js";
|
|
11
|
-
import { A as TableHeader, B as DEFAULT_LANGUAGE_STORAGE_KEY, C as SelectTrigger, D as TableCaption, E as TableBody, F as ResolvedFieldDisplay, H as saveLocale, M as Badge, N as badgeVariants, O as TableCell, S as SelectSeparator, T as Table, V as loadLocale, _ as SelectGroup, a as PinnedFilterControls, b as SelectScrollDownButton, c as fromLocalDateTimeText, d as useFieldTimeZone, f as FilterBadges, g as SelectContent, h as Select, i as FacetedFilterPicker, j as TableRow, k as TableHead, l as toLocalDateTimeText, m as localeTag, n as PresetFilterControls, o as FilterPanel, p as formatNumber, r as DataTableFacetedFilter, s as FilterValueControl, t as FilterBuilder, u as dateDisplayParams, v as SelectItem, w as SelectValue, x as SelectScrollUpButton, y as SelectLabel, z as lowerLegacyField } from "./filter-builder-
|
|
12
|
-
import {
|
|
11
|
+
import { A as TableHeader, B as DEFAULT_LANGUAGE_STORAGE_KEY, C as SelectTrigger, D as TableCaption, E as TableBody, F as ResolvedFieldDisplay, H as saveLocale, M as Badge, N as badgeVariants, O as TableCell, S as SelectSeparator, T as Table, V as loadLocale, _ as SelectGroup, a as PinnedFilterControls, b as SelectScrollDownButton, c as fromLocalDateTimeText, d as useFieldTimeZone, f as FilterBadges, g as SelectContent, h as Select, i as FacetedFilterPicker, j as TableRow, k as TableHead, l as toLocalDateTimeText, m as localeTag, n as PresetFilterControls, o as FilterPanel, p as formatNumber, r as DataTableFacetedFilter, s as FilterValueControl, t as FilterBuilder, u as dateDisplayParams, v as SelectItem, w as SelectValue, x as SelectScrollUpButton, y as SelectLabel, z as lowerLegacyField } from "./filter-builder-DX-xC3Yd.js";
|
|
12
|
+
import { a as canonicalNavKey, i as NavAreaNotFound, n as usePublishRecordTrail, o as canonicalResourceKey$1, r as usePublishedRecordTrail, t as recordTrailKey } from "./record-trail-store-Dn6bVry4.js";
|
|
13
13
|
import { t as Label } from "./label-66W1qbke.js";
|
|
14
|
-
import { t as LazyRelationPickerDialog } from "./lazy-relation-picker-dialog-
|
|
15
|
-
import {
|
|
16
|
-
import { n as
|
|
17
|
-
import {
|
|
14
|
+
import { t as LazyRelationPickerDialog } from "./lazy-relation-picker-dialog-BaIEWLbF.js";
|
|
15
|
+
import { t as useNoticeSink } from "./success-notices-CrcWOGSo.js";
|
|
16
|
+
import { n as useRecordSubtitle, r as useRowSubtitle } from "./use-record-subtitle-pWMBqQx9.js";
|
|
17
|
+
import { n as RelationPicker, r as RelationPickerInput } from "./relation-picker-CCyuMeGq.js";
|
|
18
|
+
import { t as useDialogFilterState } from "./use-dialog-filter-state-ZYxOp5WO.js";
|
|
18
19
|
import { useGlobalShortcut, webPlatform } from "@burojs/react/web";
|
|
19
|
-
import { ENUM_CONTROLS, builtinFrames, canonicalInstant, columnName, compilePageScreen, compiledScreenFor, declaredField, declaredText, dtoFields, effectiveFlow, enumFallbackKey, enumFallbackText, enumNamespaceOf, enumValueText, epochMsToInstant, evaluateVisible, fieldOptionText, formatEntityRef, formatTimeValue, hasListPage, hasRecordPage, hrefFor, humanizeKey, isCalendarDate, isCustomPageDef, isEnumControl, isRelationField, isScreenFailure, linkOf, navHrefFor, nodeKind, normalizeDataError, normalizeEntityRef, projectDto, recordEntries, relationAmbiguousError, resolveConceptLabel, resolveContentSlot, resolveDoc, resolveEnumLabel, resolveFieldLabel, resolveNavLabel, resolveResourceLabel, resolveResourceNavLabel, resolveStaticAvailability, resourceCreateHref, resourceKey, resourceShowHref, routeResourceKey, snapshotAppFrames, snapshotBlockLookup, snapshotFrameLookup, taskRunFraction, taskRunOutcome, zoneShortName } from "@burojs/core";
|
|
20
|
-
import { ComponentsContext, DispatcherProvider, I18nContext, NO_RECORD_SCOPE_FRAMES, QUICK_ANCHOR_ATTRIBUTE, QuickEntryContext, RecordScopeChain, RouteSourceContext, ScreenStateProvider, formatBytes, optionFieldName, recordRouteAvailable, resolveErrorText, resolveOptionLabel, retryRecordRead, useApp, useAppActions, useApplicationSnapshot, useAvailabilityGate, useAvailableEnumValues, useBoundEnum, useCapabilityResolver, useChangeLocale, useChromeT, useCommandPalette, useCurrentUserId, useDebouncedValue, useDispatcherEffects, useDocPanel, useDocScopes, useDocTopics, useDocTrailScopes, useDocsConfig, useDocumentedScopes, useEntryGate, useEnumValues, useFieldAccess, useFieldMetaSources, useFileField, useFileUrls, useFormController, useFrameImplementations, useFrameT, useI18nCaps, useIdentity, useLanguageStorageKey, useLinkKeys, useList, useListAccess, useLocale, useLogout, useNavigationGuardText, useNavigationOrigin, useOne, useOptionCaps, useOptionalApp, useOptionalAuthController, useOptionalDataRuntime, useOptionalFileStorage, useOptionalLocale, useOptionalRouterAdapter, useOptionalT, useOriginIntent, usePlatformStorage, usePresentBlock, useProjectedRecord, useProjectedTitle, usePublishOriginTitle, useQuick, useQuickEntry, useQuickEntryValue, useQuickOpener, useRecordTitle, useRefresh, useRegisterNavigationGuardDialog, useRelatedRecord, useRouteNavigate, useRouteRecordId, useRouterAdapter, useScope, useT, useTestIds, useTimeZone, useUIInstance, useUIPresentationRuntime, useVisibilityCtx } from "@burojs/react";
|
|
20
|
+
import { ENUM_CONTROLS, FORM_SLOT, addressesCapability, builtinFrames, canonicalInstant, collectionFactOf, columnName, compilePageScreen, compiledScreenFor, declaredField, declaredText, dtoFields, effectiveFlow, enumFallbackKey, enumFallbackText, enumNamespaceOf, enumValueText, epochMsToInstant, evaluateVisible, fieldOptionText, fieldSensitivity, formatEntityRef, formatTimeValue, hasListPage, hasMenuEntry, hasRecordPage, hrefFor, humanizeKey, isCalendarDate, isCustomPageDef, isEnumControl, isRelationField, isScreenFailure, issueOfInput, linkOf, matchPath, memberFactOf, navHrefFor, nodeKind, normalizeDataError, normalizeEntityRef, normalizeIssueInputs, pageParamNames, parsePageParams, permissionsUndecided, projectDto, recordEntries, relationAmbiguousError, resolveConceptLabel, resolveContentSlot, resolveDoc, resolveEnumLabel, resolveFieldLabel, resolveNavLabel, resolveResourceLabel, resolveResourceNavLabel, resolveStaticAvailability, resourceCreateHref, resourceKey, resourceShowHref, routeResourceKey, routeServerIssues, sameEntityRef, scopeColumnOf, snapshotAppFrames, snapshotBlockLookup, snapshotFrameLookup, successNoticesOf, taskRunFraction, taskRunOutcome, zoneShortName } from "@burojs/core";
|
|
21
|
+
import { ActionRouteContext, ComponentsContext, DispatcherProvider, FrameCheckContext, I18nContext, NO_RECORD_SCOPE_FRAMES, ORIGIN_DEPTH, QUICK_ANCHOR_ATTRIBUTE, QuickEntryContext, RecordFrameContext, RecordScopeChain, RecordScopeProvider, ResourceContext, RouteSourceContext, ScreenStateProvider, formatBytes, maybeThen, optionFieldName, pageAccessFor, policyRecordOf, recordKeyOf, recordRouteAvailable, resolveErrorText, resolveOptionLabel, resolveRoute, retryRecordRead, settle, useApp, useAppActions, useApplicationSnapshot, useAvailabilityGate, useAvailabilityResolver, useAvailableEnumValues, useBoundEnum, useCapabilityResolver, useChangeLocale, useChromeT, useCollectionMember, useCommandPalette, useComponent, useCtx, useCurrentUserId, useDebouncedValue, useDispatcherEffects, useDocPanel, useDocScopes, useDocTopics, useDocTrailScopes, useDocsConfig, useDocumentedScopes, useEntryGate, useEnumValues, useFieldAccess, useFieldMetaSources, useFileField, useFileUrls, useFormController, useFrameImplementations, useFrameT, useI18nCaps, useIdentity, useLanguageStorageKey, useLinkKeys, useList, useListAccess, useLocale, useLogout, useNavigationGuardText, useNavigationOrigin, useOne, useOptionCaps, useOptionalApp, useOptionalAuthController, useOptionalDataRuntime, useOptionalFileStorage, useOptionalLocale, useOptionalRouterAdapter, useOptionalT, useOriginIntent, usePageAccess, usePageScreen, usePermissions, usePermissionsUndecided, usePlatformStorage, usePresentBlock, useProjectedRecord, useProjectedTitle, usePublishDocVariant, usePublishOriginTitle, useQuick, useQuickEntry, useQuickEntryValue, useQuickOpener, useRecordFrame, useRecordTitle, useRefresh, useRegisterNavigationGuardDialog, useRelatedRecord, useReturnTarget, useRouteListImplementation, useRouteNavigate, useRouteRecordId, useRouterAdapter, useScope, useT, useTestIds, useTimeZone, useTranslate, useUIInstance, useUIPresentationRuntime, useVisibilityCtx } from "@burojs/react";
|
|
21
22
|
import { IconAlertCircle, IconArrowDown, IconArrowLeft, IconArrowUp, IconArrowUpRight, IconBook2, IconCheck, IconChevronDown, IconChevronRight, IconDeviceDesktop, IconDots, IconExternalLink, IconHelp, IconHelpHexagon, IconLayoutSidebar, IconLoader2, IconLogin, IconLogout, IconMaximize, IconMinimize, IconMoon, IconPlus, IconRefresh, IconRotate, IconSearch, IconSelector, IconSun, IconTrash, IconUpload, IconUser, IconX } from "@tabler/icons-react";
|
|
22
23
|
import * as React from "react";
|
|
23
|
-
import { Children, Fragment, Suspense, cloneElement, createContext, createElement, isValidElement, lazy, useCallback, useContext, useEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
|
|
24
|
+
import { Children, Fragment, Suspense, cloneElement, createContext, createElement, isValidElement, lazy, useCallback, useContext, useEffect, useId, useMemo, useRef, useState, useSyncExternalStore } from "react";
|
|
24
25
|
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
25
26
|
import { Slot } from "@radix-ui/react-slot";
|
|
26
27
|
import { cva } from "class-variance-authority";
|
|
@@ -30,7 +31,7 @@ import * as SeparatorPrimitive from "@radix-ui/react-separator";
|
|
|
30
31
|
import * as AlertDialogPrimitive from "@radix-ui/react-alert-dialog";
|
|
31
32
|
import * as AvatarPrimitive from "@radix-ui/react-avatar";
|
|
32
33
|
import * as CollapsiblePrimitive from "@radix-ui/react-collapsible";
|
|
33
|
-
import { enumBindingKey, enumParamsOf, narrowEntriesOf } from "@burojs/core/internal";
|
|
34
|
+
import { enumBindingKey, enumParamsOf, narrowEntriesOf, resolveEnumParams } from "@burojs/core/internal";
|
|
34
35
|
import { Controller, FormProvider, useForm, useFormContext } from "react-hook-form";
|
|
35
36
|
import { zodResolver } from "@hookform/resolvers/zod";
|
|
36
37
|
import { z } from "zod";
|
|
@@ -296,7 +297,7 @@ function DocBrowser({ className }) {
|
|
|
296
297
|
label,
|
|
297
298
|
summary: resolveDoc(caps, entry.scope)?.summary,
|
|
298
299
|
nested: entry.kind === "variant",
|
|
299
|
-
group: entry.kind === "concept" ? glossaryLabel : entry.kind === "resource" ? areasLabel : void 0
|
|
300
|
+
group: entry.kind === "concept" ? glossaryLabel : entry.kind === "resource" || entry.kind === "base" ? areasLabel : void 0
|
|
300
301
|
};
|
|
301
302
|
}), [
|
|
302
303
|
app,
|
|
@@ -1712,7 +1713,21 @@ function useNavIcon() {
|
|
|
1712
1713
|
return useCallback((name) => resolveNavIcon(name, (key) => components[key]), [components]);
|
|
1713
1714
|
}
|
|
1714
1715
|
//#endregion
|
|
1716
|
+
//#region src/shell/use-shell-waiting.ts
|
|
1717
|
+
function useShellWaiting() {
|
|
1718
|
+
return usePermissionsUndecided();
|
|
1719
|
+
}
|
|
1720
|
+
//#endregion
|
|
1715
1721
|
//#region src/layout/build-nav-items.ts
|
|
1722
|
+
function groupNavHref(registry, group, isVisible) {
|
|
1723
|
+
let href;
|
|
1724
|
+
try {
|
|
1725
|
+
href = navHrefFor(registry, group, { isVisible });
|
|
1726
|
+
} catch {
|
|
1727
|
+
return;
|
|
1728
|
+
}
|
|
1729
|
+
return matchPath(registry, href, { isVisible }) === null ? void 0 : href;
|
|
1730
|
+
}
|
|
1716
1731
|
function buildNavItems(args) {
|
|
1717
1732
|
const { registry, caps, groupName, match, isVisible, entryFilter, icons } = args;
|
|
1718
1733
|
const chain = match?.groupChain ?? [];
|
|
@@ -1727,8 +1742,9 @@ function buildNavItems(args) {
|
|
|
1727
1742
|
if (!isEligible(child)) continue;
|
|
1728
1743
|
const kind = nodeKind(child);
|
|
1729
1744
|
const childKey = canonicalNavKey(child);
|
|
1730
|
-
const href = navHrefFor(registry, childKey, { isVisible: isEligible });
|
|
1731
1745
|
if (kind === "group") {
|
|
1746
|
+
const href = groupNavHref(registry, childKey, isEligible);
|
|
1747
|
+
if (href === void 0) continue;
|
|
1732
1748
|
const label = resolveNavLabel(caps, `groups.${childKey}`, declaredText("chrome", child, "label"));
|
|
1733
1749
|
const active = chain.some((g) => canonicalNavKey(g) === childKey);
|
|
1734
1750
|
out.push({
|
|
@@ -1739,7 +1755,10 @@ function buildNavItems(args) {
|
|
|
1739
1755
|
icon: icons?.group?.(childKey),
|
|
1740
1756
|
active
|
|
1741
1757
|
});
|
|
1742
|
-
|
|
1758
|
+
continue;
|
|
1759
|
+
}
|
|
1760
|
+
const href = navHrefFor(registry, childKey, { isVisible: isEligible });
|
|
1761
|
+
if (kind === "custom-page") out.push({
|
|
1743
1762
|
kind: "custom-page",
|
|
1744
1763
|
name: childKey,
|
|
1745
1764
|
label: resolveNavLabel(caps, `pages.${childKey}`, declaredText("chrome", child, "label")),
|
|
@@ -1759,6 +1778,171 @@ function buildNavItems(args) {
|
|
|
1759
1778
|
return out;
|
|
1760
1779
|
}
|
|
1761
1780
|
//#endregion
|
|
1781
|
+
//#region src/layout/layouts/secondary-sidebar-layout.tsx
|
|
1782
|
+
function Row({ it }) {
|
|
1783
|
+
const RouterLink = useLinkComponent();
|
|
1784
|
+
return jsx("li", { children: jsxs(RouterLink, {
|
|
1785
|
+
href: it.href,
|
|
1786
|
+
"data-testid": `nav-secondary-item:${it.name}`,
|
|
1787
|
+
"aria-current": it.active ? "page" : void 0,
|
|
1788
|
+
className: cn("flex items-center gap-3 rounded-lg px-3 py-2 text-sm font-medium outline-hidden ring-offset-background transition-colors", "focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2", it.active ? "bg-secondary text-secondary-foreground" : "text-muted-foreground hover:bg-secondary/50 hover:text-foreground"),
|
|
1789
|
+
children: [it.icon != null && jsx("span", {
|
|
1790
|
+
className: "inline-flex h-4 w-4 shrink-0 items-center justify-center",
|
|
1791
|
+
"aria-hidden": "true",
|
|
1792
|
+
children: it.icon
|
|
1793
|
+
}), jsx("span", {
|
|
1794
|
+
className: "truncate",
|
|
1795
|
+
title: it.label,
|
|
1796
|
+
children: it.label
|
|
1797
|
+
})]
|
|
1798
|
+
}) });
|
|
1799
|
+
}
|
|
1800
|
+
function SecondarySidebarLayout({ group, groupKey, items, children }) {
|
|
1801
|
+
const RouterLink = useLinkComponent();
|
|
1802
|
+
const caps = useI18nCaps();
|
|
1803
|
+
const id = groupKey ?? group.name;
|
|
1804
|
+
const label = resolveNavLabel(caps, `groups.${id}`, declaredText("chrome", group, "label"));
|
|
1805
|
+
return jsxs("div", {
|
|
1806
|
+
className: "md:-m-6 md:flex md:min-h-[calc(100%+3rem)]",
|
|
1807
|
+
"data-testid": `nav-secondary:${id}`,
|
|
1808
|
+
children: [
|
|
1809
|
+
jsx("aside", {
|
|
1810
|
+
className: "hidden w-64 shrink-0 border-r bg-background text-foreground md:block",
|
|
1811
|
+
children: jsxs("div", {
|
|
1812
|
+
className: "space-y-4 p-4",
|
|
1813
|
+
children: [jsx("p", {
|
|
1814
|
+
className: "px-3 text-base font-semibold tracking-tight",
|
|
1815
|
+
children: label
|
|
1816
|
+
}), jsx("nav", {
|
|
1817
|
+
"aria-label": label,
|
|
1818
|
+
children: jsx("ul", {
|
|
1819
|
+
className: "space-y-0.5",
|
|
1820
|
+
children: items.map((it) => jsx(Row, { it }, it.name))
|
|
1821
|
+
})
|
|
1822
|
+
})]
|
|
1823
|
+
})
|
|
1824
|
+
}),
|
|
1825
|
+
jsx("nav", {
|
|
1826
|
+
"aria-label": label,
|
|
1827
|
+
"data-testid": `nav-secondary-select:${id}`,
|
|
1828
|
+
className: "-mx-1 mb-4 flex snap-x snap-proximity gap-1.5 overflow-x-auto scroll-smooth px-1 pb-1 [-ms-overflow-style:none] [mask-image:linear-gradient(to_right,transparent,black_8px,black_calc(100%-8px),transparent)] [scrollbar-width:none] md:hidden [&::-webkit-scrollbar]:hidden",
|
|
1829
|
+
children: items.map((it) => jsx(RouterLink, {
|
|
1830
|
+
href: it.href,
|
|
1831
|
+
"aria-current": it.active ? "page" : void 0,
|
|
1832
|
+
className: cn("snap-start whitespace-nowrap rounded-full border px-3 py-1.5 text-sm outline-hidden ring-offset-background transition-colors duration-150", "focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2", it.active ? "border-primary/40 bg-primary/10 font-medium text-foreground" : "border-border text-muted-foreground hover:bg-accent/60 hover:text-accent-foreground"),
|
|
1833
|
+
children: it.label
|
|
1834
|
+
}, it.name))
|
|
1835
|
+
}),
|
|
1836
|
+
jsx("div", {
|
|
1837
|
+
className: "min-w-0 flex-1 md:overflow-x-hidden md:p-6",
|
|
1838
|
+
children
|
|
1839
|
+
})
|
|
1840
|
+
]
|
|
1841
|
+
});
|
|
1842
|
+
}
|
|
1843
|
+
//#endregion
|
|
1844
|
+
//#region src/layout/layouts/tabs-layout.tsx
|
|
1845
|
+
function TabsLayout({ group, groupKey, items, children }) {
|
|
1846
|
+
const RouterLink = useLinkComponent();
|
|
1847
|
+
const caps = useI18nCaps();
|
|
1848
|
+
const id = groupKey ?? group.name;
|
|
1849
|
+
const label = resolveNavLabel(caps, `groups.${id}`, declaredText("chrome", group, "label"));
|
|
1850
|
+
return jsxs("div", {
|
|
1851
|
+
className: "flex flex-col gap-4",
|
|
1852
|
+
"data-testid": `nav-tabs:${id}`,
|
|
1853
|
+
children: [jsx("nav", {
|
|
1854
|
+
"aria-label": label,
|
|
1855
|
+
className: "flex gap-1 overflow-x-auto scroll-smooth border-b border-border [-ms-overflow-style:none] [mask-image:linear-gradient(to_right,transparent,black_8px,black_calc(100%-8px),transparent)] [scrollbar-width:none] [&::-webkit-scrollbar]:hidden",
|
|
1856
|
+
children: items.map((it) => jsxs(RouterLink, {
|
|
1857
|
+
href: it.href,
|
|
1858
|
+
"aria-current": it.active ? "page" : void 0,
|
|
1859
|
+
"data-testid": `nav-tab:${it.name}`,
|
|
1860
|
+
className: cn("group/tab relative flex shrink-0 items-center gap-1.5 whitespace-nowrap rounded-t-sm px-3 py-2.5 text-sm outline-hidden ring-offset-background transition-colors duration-150", "focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2", it.active ? "font-medium text-foreground" : "text-muted-foreground hover:text-foreground"),
|
|
1861
|
+
children: [
|
|
1862
|
+
it.icon != null && jsx("span", {
|
|
1863
|
+
className: "inline-flex h-4 w-4 shrink-0 items-center justify-center",
|
|
1864
|
+
"aria-hidden": "true",
|
|
1865
|
+
children: it.icon
|
|
1866
|
+
}),
|
|
1867
|
+
it.label,
|
|
1868
|
+
jsx("span", {
|
|
1869
|
+
"aria-hidden": "true",
|
|
1870
|
+
className: cn("pointer-events-none absolute inset-x-2 -bottom-px h-0.5 origin-center scale-x-0 rounded-full bg-primary opacity-0 transition-all duration-300 ease-out", it.active ? "scale-x-100 opacity-100" : "group-hover/tab:scale-x-100 group-hover/tab:opacity-30")
|
|
1871
|
+
})
|
|
1872
|
+
]
|
|
1873
|
+
}, it.name))
|
|
1874
|
+
}), jsx("div", {
|
|
1875
|
+
className: "min-w-0",
|
|
1876
|
+
children
|
|
1877
|
+
})]
|
|
1878
|
+
});
|
|
1879
|
+
}
|
|
1880
|
+
//#endregion
|
|
1881
|
+
//#region src/layout/nav-slots.ts
|
|
1882
|
+
function layoutSlot(key) {
|
|
1883
|
+
return `layout.${key}`;
|
|
1884
|
+
}
|
|
1885
|
+
function useLayout(key) {
|
|
1886
|
+
return useComponent(layoutSlot(key));
|
|
1887
|
+
}
|
|
1888
|
+
const BUILTIN_LAYOUTS = {
|
|
1889
|
+
[layoutSlot("secondary-sidebar")]: SecondarySidebarLayout,
|
|
1890
|
+
[layoutSlot("tabs")]: TabsLayout
|
|
1891
|
+
};
|
|
1892
|
+
function useLayoutResolver() {
|
|
1893
|
+
const components = useContext(ComponentsContext);
|
|
1894
|
+
return (key) => components[layoutSlot(key)] ?? BUILTIN_LAYOUTS[layoutSlot(key)];
|
|
1895
|
+
}
|
|
1896
|
+
const unresolvedLayoutReported = new WeakMap();
|
|
1897
|
+
function useReportUnresolvedLayouts(groups, resolve) {
|
|
1898
|
+
const app = useApp();
|
|
1899
|
+
useEffect(() => {
|
|
1900
|
+
if (process.env.NODE_ENV === "production") return;
|
|
1901
|
+
for (const group of groups) {
|
|
1902
|
+
const layout = group.layout;
|
|
1903
|
+
if (layout === "flat" || layout === "collapsible" || resolve(layout)) continue;
|
|
1904
|
+
const key = canonicalNavKey(group);
|
|
1905
|
+
let seen = unresolvedLayoutReported.get(app);
|
|
1906
|
+
if (!seen) {
|
|
1907
|
+
seen = new Set();
|
|
1908
|
+
unresolvedLayoutReported.set(app, seen);
|
|
1909
|
+
}
|
|
1910
|
+
if (seen.has(key)) continue;
|
|
1911
|
+
seen.add(key);
|
|
1912
|
+
console.error(`[burojs] Nav group "${key}" declares layout "${layout}", and no renderer is registered for it. Register one as components["layout.${layout}"] on <BuroProvider>, or use a built-in layout: "secondary-sidebar", "tabs", "flat", "collapsible".`);
|
|
1913
|
+
}
|
|
1914
|
+
});
|
|
1915
|
+
}
|
|
1916
|
+
//#endregion
|
|
1917
|
+
//#region src/layout/use-nav-routing.ts
|
|
1918
|
+
function useNavVisible() {
|
|
1919
|
+
const ctx = useVisibilityCtx();
|
|
1920
|
+
const allowed = usePageAllowed();
|
|
1921
|
+
return useCallback((node) => evaluateVisible(node.visible, ctx) && (!isCustomPageDef(node) || allowed(node)), [ctx, allowed]);
|
|
1922
|
+
}
|
|
1923
|
+
function useResourceVisible() {
|
|
1924
|
+
const ctx = useVisibilityCtx();
|
|
1925
|
+
return useCallback((def) => evaluateVisible(def.visible, ctx), [ctx]);
|
|
1926
|
+
}
|
|
1927
|
+
function usePageAllowed() {
|
|
1928
|
+
const permissions = usePermissions();
|
|
1929
|
+
return useCallback((page) => pageAccessFor(page, permissions) === "allowed", [permissions]);
|
|
1930
|
+
}
|
|
1931
|
+
function useHref(name, opts) {
|
|
1932
|
+
const app = useApp();
|
|
1933
|
+
const isVisible = useNavVisible();
|
|
1934
|
+
return hrefFor(app.registry, name, {
|
|
1935
|
+
...opts,
|
|
1936
|
+
isVisible
|
|
1937
|
+
});
|
|
1938
|
+
}
|
|
1939
|
+
function useMatch() {
|
|
1940
|
+
const app = useApp();
|
|
1941
|
+
const isVisible = useNavVisible();
|
|
1942
|
+
const path = useOptionalRouterAdapter()?.pathname;
|
|
1943
|
+
return path === void 0 ? null : matchPath(app.registry, path, { isVisible });
|
|
1944
|
+
}
|
|
1945
|
+
//#endregion
|
|
1762
1946
|
//#region src/layout/use-nav-tree.ts
|
|
1763
1947
|
function isActiveHref(href, currentPath) {
|
|
1764
1948
|
if (!currentPath) return false;
|
|
@@ -1783,7 +1967,7 @@ function useNavTree(opts = {}) {
|
|
|
1783
1967
|
const sections = app.registry.sections.list();
|
|
1784
1968
|
const groups = app.registry.groups.rootNodes().filter((n) => nodeKind(n) === "group");
|
|
1785
1969
|
useReportUnresolvedLayouts(app.registry.groups.list(), resolveLayout);
|
|
1786
|
-
const topLevelResources = allResources.filter((r) =>
|
|
1970
|
+
const topLevelResources = allResources.filter((r) => hasMenuEntry(r)).filter(isResourceVisible).filter(passesEntryFilter).filter((r) => !r.section && !r.group).map((r, idx) => ({
|
|
1787
1971
|
r,
|
|
1788
1972
|
idx
|
|
1789
1973
|
})).sort((a, b) => {
|
|
@@ -1868,7 +2052,7 @@ function useNavTree(opts = {}) {
|
|
|
1868
2052
|
}];
|
|
1869
2053
|
for (const section of sections) {
|
|
1870
2054
|
const sectionKey = canonicalNavKey(section);
|
|
1871
|
-
const entries = app.registry.sections.childrenOf(sectionKey).filter((c) => !("parent" in c) ||
|
|
2055
|
+
const entries = app.registry.sections.childrenOf(sectionKey).filter((c) => !("parent" in c) || hasMenuEntry(c)).filter((c) => isCustomPageDef(c) ? pageAllowed(c) : isResourceVisible(c)).filter(passesEntryFilter).map((c) => isCustomPageDef(c) ? pageEntry(c) : resourceEntry(c));
|
|
1872
2056
|
if (entries.length === 0) continue;
|
|
1873
2057
|
const active = entries.some((e) => e.active);
|
|
1874
2058
|
out.push({
|
|
@@ -1886,7 +2070,8 @@ function useNavTree(opts = {}) {
|
|
|
1886
2070
|
if (!isNavEligible(group)) continue;
|
|
1887
2071
|
const gLabel = groupLabel(group);
|
|
1888
2072
|
if (resolveLayout(group.layout)) {
|
|
1889
|
-
const href =
|
|
2073
|
+
const href = groupNavHref(app.registry, groupKey, isNavEligible);
|
|
2074
|
+
if (href === void 0) continue;
|
|
1890
2075
|
const entry = groupEntry(group, gLabel, href, isActiveHref(href, currentPath));
|
|
1891
2076
|
out.push({
|
|
1892
2077
|
key: groupKey,
|
|
@@ -1965,155 +2150,2008 @@ function CollapsibleNavSection({ name, label, icon, hasActiveChild, declaredOpen
|
|
|
1965
2150
|
})
|
|
1966
2151
|
}), jsx(CollapsibleContent, { children: jsx(SidebarMenuSub, { children }) })]
|
|
1967
2152
|
})
|
|
1968
|
-
}) }) })
|
|
2153
|
+
}) }) })
|
|
2154
|
+
});
|
|
2155
|
+
}
|
|
2156
|
+
function NavMain({ topLabel, iconForResource, iconForCustomPage, iconForSection, entryFilter }) {
|
|
2157
|
+
const tree = useNavTree({ entryFilter });
|
|
2158
|
+
const app = useApp();
|
|
2159
|
+
const tid = useTestIds();
|
|
2160
|
+
const t = useT();
|
|
2161
|
+
const RouterLink = useLinkComponent();
|
|
2162
|
+
const navIcon = useNavIcon();
|
|
2163
|
+
const waiting = useShellWaiting();
|
|
2164
|
+
const intent = useOriginIntent();
|
|
2165
|
+
const jump = (entry) => () => {
|
|
2166
|
+
if (entry.href !== void 0) intent(entry.href, "jump");
|
|
2167
|
+
};
|
|
2168
|
+
const ownOr = (own, name) => own !== void 0 ? own : navIcon(name);
|
|
2169
|
+
const groupAsSection = (key) => app.registry.sections.get(key) ?? app.registry.groups.get(key);
|
|
2170
|
+
const sectionIcon = (key, name) => {
|
|
2171
|
+
const def = iconForSection ? groupAsSection(key) : void 0;
|
|
2172
|
+
return ownOr(def ? iconForSection?.(def) : void 0, name);
|
|
2173
|
+
};
|
|
2174
|
+
const entryIcon = (entry) => {
|
|
2175
|
+
if (entry.kind === "group") return sectionIcon(entry.key, entry.icon);
|
|
2176
|
+
if (entry.kind === "page") {
|
|
2177
|
+
const page = iconForCustomPage ? app.registry.customPages.get(entry.key) : void 0;
|
|
2178
|
+
return ownOr(page ? iconForCustomPage?.(page) : void 0, entry.icon);
|
|
2179
|
+
}
|
|
2180
|
+
const def = iconForResource ? app.registry.get(entry.key) : void 0;
|
|
2181
|
+
return ownOr(def ? iconForResource?.(def) : void 0, entry.icon);
|
|
2182
|
+
};
|
|
2183
|
+
const entryTestId = (entry) => entry.kind === "page" ? tid.sidebarCustomPage(entry.key) : entry.kind === "group" ? tid.navGroup(entry.key) : tid.navMenuItem(entry.key);
|
|
2184
|
+
const menuItem = (entry) => {
|
|
2185
|
+
const icon = entryIcon(entry);
|
|
2186
|
+
return jsx(SidebarMenuItem, { children: jsx(SidebarMenuButton, {
|
|
2187
|
+
asChild: true,
|
|
2188
|
+
isActive: entry.active,
|
|
2189
|
+
tooltip: entry.label,
|
|
2190
|
+
children: jsxs(RouterLink, {
|
|
2191
|
+
href: entry.href ?? "",
|
|
2192
|
+
"aria-current": entry.active ? "page" : void 0,
|
|
2193
|
+
"data-testid": entryTestId(entry),
|
|
2194
|
+
onClick: jump(entry),
|
|
2195
|
+
children: [icon != null && jsx("span", {
|
|
2196
|
+
className: "inline-flex h-4 w-4 items-center justify-center",
|
|
2197
|
+
"aria-hidden": "true",
|
|
2198
|
+
children: icon
|
|
2199
|
+
}), jsx("span", { children: entry.label })]
|
|
2200
|
+
})
|
|
2201
|
+
}) }, entry.key);
|
|
2202
|
+
};
|
|
2203
|
+
const subItem = (entry) => jsx(SidebarMenuSubItem, { children: jsx(SidebarMenuSubButton, {
|
|
2204
|
+
asChild: true,
|
|
2205
|
+
isActive: entry.active,
|
|
2206
|
+
children: jsx(RouterLink, {
|
|
2207
|
+
href: entry.href ?? "",
|
|
2208
|
+
"aria-current": entry.active ? "page" : void 0,
|
|
2209
|
+
"data-testid": entryTestId(entry),
|
|
2210
|
+
onClick: jump(entry),
|
|
2211
|
+
children: jsx("span", { children: entry.label })
|
|
2212
|
+
})
|
|
2213
|
+
}) }, entry.key);
|
|
2214
|
+
if (waiting) return jsx(SidebarGroup, {
|
|
2215
|
+
"data-testid": "sidebar-nav-loading",
|
|
2216
|
+
"aria-busy": "true",
|
|
2217
|
+
children: jsx(SidebarGroupContent, { children: jsx(SidebarMenu, { children: [
|
|
2218
|
+
0,
|
|
2219
|
+
1,
|
|
2220
|
+
2,
|
|
2221
|
+
3
|
|
2222
|
+
].map((i) => jsx(SidebarMenuItem, { children: jsx(SidebarMenuSkeleton, { showIcon: true }) }, i)) }) })
|
|
2223
|
+
});
|
|
2224
|
+
const [top, ...blocks] = tree;
|
|
2225
|
+
return jsxs(Fragment$1, { children: [top !== void 0 && top.entries.length > 0 && jsxs(SidebarGroup, { children: [topLabel !== null && jsx(SidebarGroupLabel, { children: topLabel ?? t("nav.resources") }), jsx(SidebarGroupContent, { children: jsx(SidebarMenu, { children: top.entries.map(menuItem) }) })] }), blocks.map((block) => {
|
|
2226
|
+
const key = block.key ?? "";
|
|
2227
|
+
if (block.label === null) return jsx(SidebarGroup, { children: jsx(SidebarGroupContent, { children: jsx(SidebarMenu, { children: block.entries.map(menuItem) }) }) }, key);
|
|
2228
|
+
if (block.collapsible) return jsx(CollapsibleNavSection, {
|
|
2229
|
+
name: key,
|
|
2230
|
+
label: block.label,
|
|
2231
|
+
icon: sectionIcon(key, block.icon),
|
|
2232
|
+
hasActiveChild: block.active,
|
|
2233
|
+
declaredOpen: block.defaultOpen,
|
|
2234
|
+
children: block.entries.map(subItem)
|
|
2235
|
+
}, key);
|
|
2236
|
+
return jsxs(SidebarGroup, {
|
|
2237
|
+
"data-testid": `sidebar-section-group:${key}`,
|
|
2238
|
+
children: [jsx(SidebarGroupLabel, {
|
|
2239
|
+
"data-testid": `sidebar-section:${key}`,
|
|
2240
|
+
children: block.label
|
|
2241
|
+
}), jsx(SidebarGroupContent, { children: jsx(SidebarMenu, { children: block.entries.map(menuItem) }) })]
|
|
2242
|
+
}, key);
|
|
2243
|
+
})] });
|
|
2244
|
+
}
|
|
2245
|
+
//#endregion
|
|
2246
|
+
//#region src/layout/app-sidebar.tsx
|
|
2247
|
+
function AppSidebar({ header, filters, nav, footer }) {
|
|
2248
|
+
const headerNode = fill(header, () => jsx(SidebarBrand, {}));
|
|
2249
|
+
const filtersNode = fill(filters, () => null);
|
|
2250
|
+
const navNode = fill(nav, () => jsx(NavMain, {}));
|
|
2251
|
+
const footerNode = fill(footer, () => jsx(UserMenu, {}));
|
|
2252
|
+
return jsxs(Sidebar, {
|
|
2253
|
+
collapsible: "icon",
|
|
2254
|
+
children: [
|
|
2255
|
+
(headerNode != null || filtersNode != null) && jsxs(SidebarHeader, {
|
|
2256
|
+
className: "empty:hidden",
|
|
2257
|
+
children: [headerNode, filtersNode]
|
|
2258
|
+
}),
|
|
2259
|
+
jsx(SidebarContent, { children: navNode }),
|
|
2260
|
+
footerNode != null && jsx(SidebarFooter, {
|
|
2261
|
+
className: "empty:hidden",
|
|
2262
|
+
children: footerNode
|
|
2263
|
+
}),
|
|
2264
|
+
jsx(SidebarRail, {})
|
|
2265
|
+
]
|
|
2266
|
+
});
|
|
2267
|
+
}
|
|
2268
|
+
//#endregion
|
|
2269
|
+
//#region src/layout/nav-chrome-mounted.ts
|
|
2270
|
+
const NavChromeMountedContext = createContext(false);
|
|
2271
|
+
const NavChromeMountedProvider = NavChromeMountedContext.Provider;
|
|
2272
|
+
function useNavChromeMounted() {
|
|
2273
|
+
return useContext(NavChromeMountedContext);
|
|
2274
|
+
}
|
|
2275
|
+
//#endregion
|
|
2276
|
+
//#region src/layout/nav-chrome.tsx
|
|
2277
|
+
const DETAIL_ACTIONS = new Set([
|
|
2278
|
+
"show",
|
|
2279
|
+
"edit",
|
|
2280
|
+
"create"
|
|
2281
|
+
]);
|
|
2282
|
+
function NavChrome({ children, entryFilter, icons }) {
|
|
2283
|
+
const app = useApp();
|
|
2284
|
+
const caps = useI18nCaps();
|
|
2285
|
+
const isVisible = useNavVisible();
|
|
2286
|
+
const match = useMatch();
|
|
2287
|
+
const resolveLayout = useLayoutResolver();
|
|
2288
|
+
useReportUnresolvedLayouts(match?.groupChain ?? [], resolveLayout);
|
|
2289
|
+
if (!match) return jsx(NavChromeMountedProvider, {
|
|
2290
|
+
value: true,
|
|
2291
|
+
children
|
|
2292
|
+
});
|
|
2293
|
+
let content = children;
|
|
2294
|
+
for (let i = match.groupChain.length - 1; i >= 0; i--) {
|
|
2295
|
+
const group = match.groupChain[i];
|
|
2296
|
+
if (!group) continue;
|
|
2297
|
+
const groupKey = canonicalNavKey(group);
|
|
2298
|
+
if (group.chromeOn === "list" && match.kind === "resource" && DETAIL_ACTIONS.has(match.action)) continue;
|
|
2299
|
+
const Layout = resolveLayout(group.layout);
|
|
2300
|
+
if (!Layout) continue;
|
|
2301
|
+
const items = buildNavItems({
|
|
2302
|
+
registry: app.registry,
|
|
2303
|
+
caps,
|
|
2304
|
+
groupName: groupKey,
|
|
2305
|
+
match,
|
|
2306
|
+
isVisible,
|
|
2307
|
+
entryFilter,
|
|
2308
|
+
icons
|
|
2309
|
+
});
|
|
2310
|
+
content = jsx(Layout, {
|
|
2311
|
+
group,
|
|
2312
|
+
groupKey,
|
|
2313
|
+
items,
|
|
2314
|
+
children: content
|
|
2315
|
+
});
|
|
2316
|
+
}
|
|
2317
|
+
return jsx(NavChromeMountedProvider, {
|
|
2318
|
+
value: true,
|
|
2319
|
+
children: content
|
|
2320
|
+
});
|
|
2321
|
+
}
|
|
2322
|
+
//#endregion
|
|
2323
|
+
//#region src/flows/page-flow-loader.tsx
|
|
2324
|
+
function PageFlowRouteLoader({ match }) {
|
|
2325
|
+
const t = useOptionalT();
|
|
2326
|
+
const chromeT = useChromeT();
|
|
2327
|
+
const loaded = useLoaded(loadPageFlowRoute);
|
|
2328
|
+
const failed = (retry) => jsxs("div", {
|
|
2329
|
+
role: "alert",
|
|
2330
|
+
"data-buro-flow-load": "failed",
|
|
2331
|
+
className: "flex flex-col gap-3 p-6 text-sm",
|
|
2332
|
+
children: [jsx("p", { children: loadFailedText(t) }), jsx("div", { children: jsx(Button, {
|
|
2333
|
+
type: "button",
|
|
2334
|
+
onClick: retry,
|
|
2335
|
+
children: chromeT("actions.retry", { defaultValue: "Retry" })
|
|
2336
|
+
}) })]
|
|
2337
|
+
});
|
|
2338
|
+
if (loaded.error !== void 0) return failed(loaded.retry);
|
|
2339
|
+
if (loaded.module === void 0) return jsx(RecordState, { state: "loading" });
|
|
2340
|
+
const Route = loaded.module.PageFlowRoute;
|
|
2341
|
+
return jsx(FlowBoundary, {
|
|
2342
|
+
fallback: (_error, reset) => failed(reset),
|
|
2343
|
+
children: jsx(Route, { match })
|
|
2344
|
+
});
|
|
2345
|
+
}
|
|
2346
|
+
//#endregion
|
|
2347
|
+
//#region src/list-kit/resource-list-page.tsx
|
|
2348
|
+
function ResourceListPage({ resource: resourceInput, meta, renderCell, emptyState }) {
|
|
2349
|
+
const resource = routeResourceKey(resourceInput);
|
|
2350
|
+
const app = useRouteListImplementation();
|
|
2351
|
+
const implementation = selectImplementation("buro:related-list", app);
|
|
2352
|
+
const View = implementation === void 0 ? void 0 : viewOf(implementation);
|
|
2353
|
+
const translate = useTranslate();
|
|
2354
|
+
const route = useMemo(() => ({
|
|
2355
|
+
meta,
|
|
2356
|
+
renderCell,
|
|
2357
|
+
emptyState
|
|
2358
|
+
}), [
|
|
2359
|
+
meta,
|
|
2360
|
+
renderCell,
|
|
2361
|
+
emptyState
|
|
2362
|
+
]);
|
|
2363
|
+
const viewProps = useMemo(() => ({
|
|
2364
|
+
props: { resource: normalizeEntityRef(resource) },
|
|
2365
|
+
data: {},
|
|
2366
|
+
t: (key, options) => translate(`blocks.buro:related-list.${key}`, options)
|
|
2367
|
+
}), [resource, translate]);
|
|
2368
|
+
return jsx("div", {
|
|
2369
|
+
"data-buro-node": "place",
|
|
2370
|
+
"data-buro-size": "full",
|
|
2371
|
+
className: "min-w-0",
|
|
2372
|
+
children: jsx(RouteListContext.Provider, {
|
|
2373
|
+
value: route,
|
|
2374
|
+
children: jsx(Suspense, {
|
|
2375
|
+
fallback: null,
|
|
2376
|
+
children: View === void 0 ? null : jsx(View, { ...viewProps })
|
|
2377
|
+
})
|
|
2378
|
+
})
|
|
2379
|
+
});
|
|
2380
|
+
}
|
|
2381
|
+
//#endregion
|
|
2382
|
+
//#region src/resource/collection-create-picker.tsx
|
|
2383
|
+
function CollectionCreatePicker({ collection }) {
|
|
2384
|
+
const app = useApp();
|
|
2385
|
+
const t = useOptionalT();
|
|
2386
|
+
const caps = useI18nCaps();
|
|
2387
|
+
const gate = useAvailabilityGate("create");
|
|
2388
|
+
const Link = useLinkComponent();
|
|
2389
|
+
const key = routeResourceKey(collection);
|
|
2390
|
+
const testid = `collection-create-picker:${key}`;
|
|
2391
|
+
let undecided = false;
|
|
2392
|
+
const members = (collectionFactOf(app.registry.get(key))?.members ?? []).flatMap((entry) => {
|
|
2393
|
+
const def = app.registry.get(entry.ref);
|
|
2394
|
+
if (def === void 0 || !hasRecordPage(def, "create")) return [];
|
|
2395
|
+
const member = routeResourceKey(entry.ref);
|
|
2396
|
+
const state = gate(member, "create").state;
|
|
2397
|
+
if (state === "undecided") undecided = true;
|
|
2398
|
+
if (state !== "available") return [];
|
|
2399
|
+
return [{
|
|
2400
|
+
member,
|
|
2401
|
+
href: resourceCreateHref(app.registry, def, member),
|
|
2402
|
+
label: resolveResourceLabel(caps, member, "single", def)
|
|
2403
|
+
}];
|
|
2404
|
+
});
|
|
2405
|
+
return jsxs("div", {
|
|
2406
|
+
"data-testid": testid,
|
|
2407
|
+
className: "mx-auto flex max-w-md flex-col gap-3 px-6 py-12",
|
|
2408
|
+
children: [jsx("h2", {
|
|
2409
|
+
className: "text-base font-semibold tracking-tight",
|
|
2410
|
+
children: t("collection.createTitle", { defaultValue: "What do you want to create?" })
|
|
2411
|
+
}), members.length > 0 ? jsx("ul", {
|
|
2412
|
+
className: "flex flex-col gap-2",
|
|
2413
|
+
children: members.map((entry) => jsx("li", { children: jsx(Button, {
|
|
2414
|
+
asChild: true,
|
|
2415
|
+
variant: "outline",
|
|
2416
|
+
className: "w-full justify-start",
|
|
2417
|
+
"data-testid": `${testid}:${entry.member}`,
|
|
2418
|
+
children: jsxs(Link, {
|
|
2419
|
+
href: entry.href,
|
|
2420
|
+
children: [jsx(Icon, {
|
|
2421
|
+
name: "Plus",
|
|
2422
|
+
className: "mr-1 h-4 w-4"
|
|
2423
|
+
}), entry.label]
|
|
2424
|
+
})
|
|
2425
|
+
}) }, entry.member))
|
|
2426
|
+
}) : undecided ? jsx("div", {
|
|
2427
|
+
role: "status",
|
|
2428
|
+
"aria-busy": "true",
|
|
2429
|
+
className: "h-9 w-full animate-pulse rounded bg-muted/40"
|
|
2430
|
+
}) : jsx("p", {
|
|
2431
|
+
className: "text-sm text-muted-foreground",
|
|
2432
|
+
children: t("collection.createNone", { defaultValue: "There is nothing here you can create." })
|
|
2433
|
+
})]
|
|
2434
|
+
});
|
|
2435
|
+
}
|
|
2436
|
+
//#endregion
|
|
2437
|
+
//#region src/frames/obligations.tsx
|
|
2438
|
+
const SlotMountsContext = createContext(null);
|
|
2439
|
+
function useSlotOnce(slot) {
|
|
2440
|
+
const mounts = useContext(SlotMountsContext);
|
|
2441
|
+
const token = useId();
|
|
2442
|
+
const first = mounts === null || mounts.claim(slot, token);
|
|
2443
|
+
useEffect(() => {
|
|
2444
|
+
if (mounts === null) return;
|
|
2445
|
+
mounts.claim(slot, token);
|
|
2446
|
+
return () => mounts.release(slot, token);
|
|
2447
|
+
}, [
|
|
2448
|
+
mounts,
|
|
2449
|
+
slot,
|
|
2450
|
+
token
|
|
2451
|
+
]);
|
|
2452
|
+
useEffect(() => {
|
|
2453
|
+
if (!first) mounts?.twice(slot);
|
|
2454
|
+
}, [
|
|
2455
|
+
first,
|
|
2456
|
+
mounts,
|
|
2457
|
+
slot
|
|
2458
|
+
]);
|
|
2459
|
+
return first;
|
|
2460
|
+
}
|
|
2461
|
+
function FrameObligations({ id, mode, showActions, root, children }) {
|
|
2462
|
+
const told = useRef(new Set());
|
|
2463
|
+
const counts = useRef(new Map());
|
|
2464
|
+
const owners = useRef(new Map());
|
|
2465
|
+
const tell = (key, write) => {
|
|
2466
|
+
if (told.current.has(key) || process.env.NODE_ENV === "production") return;
|
|
2467
|
+
told.current.add(key);
|
|
2468
|
+
write();
|
|
2469
|
+
};
|
|
2470
|
+
const latestId = useRef(id);
|
|
2471
|
+
latestId.current = id;
|
|
2472
|
+
const slotMounts = useMemo(() => ({
|
|
2473
|
+
claim(slot, token) {
|
|
2474
|
+
const owner = owners.current.get(slot);
|
|
2475
|
+
if (owner === void 0) {
|
|
2476
|
+
owners.current.set(slot, token);
|
|
2477
|
+
return true;
|
|
2478
|
+
}
|
|
2479
|
+
return owner === token;
|
|
2480
|
+
},
|
|
2481
|
+
release(slot, token) {
|
|
2482
|
+
if (owners.current.get(slot) === token) owners.current.delete(slot);
|
|
2483
|
+
},
|
|
2484
|
+
twice(slot) {
|
|
2485
|
+
tell(`slot-twice:${slot}`, () => console.error(`[burojs] frame.slot-twice: ${latestId.current} renders slots.${slot} twice; a slot renders at most once (body twice would mount every field twice under one form).`));
|
|
2486
|
+
}
|
|
2487
|
+
}), []);
|
|
2488
|
+
const pieces = useMemo(() => ({
|
|
2489
|
+
stub: false,
|
|
2490
|
+
mount(piece) {
|
|
2491
|
+
counts.current.set(piece, (counts.current.get(piece) ?? 0) + 1);
|
|
2492
|
+
return () => counts.current.set(piece, (counts.current.get(piece) ?? 1) - 1);
|
|
2493
|
+
}
|
|
2494
|
+
}), []);
|
|
2495
|
+
return jsx(SlotMountsContext.Provider, {
|
|
2496
|
+
value: slotMounts,
|
|
2497
|
+
children: jsxs(FrameCheckContext.Provider, {
|
|
2498
|
+
value: pieces,
|
|
2499
|
+
children: [children, jsx(ObligationCheck, { check: () => {
|
|
2500
|
+
const mounted = (piece) => (counts.current.get(piece) ?? 0) > 0;
|
|
2501
|
+
if (mode !== "show" && !mounted("FormActions")) tell("no-form-actions", () => console.error(`[burojs] frame.no-form-actions: ${id} renders no <FormActions/> in ${mode}; nothing can submit or cancel the form.`));
|
|
2502
|
+
if (mode === "show" && showActions && !mounted("RecordActions")) tell("no-actions", () => console.warn(`[burojs] screen.no-actions: ${id} renders no <RecordActions/> in show, and the resource has item actions there.`));
|
|
2503
|
+
if (root.current?.querySelector("h1") != null) tell("h1", () => console.error(`[burojs] ${id} renders an h1; the route draws the screen's one h1.`));
|
|
2504
|
+
} })]
|
|
2505
|
+
})
|
|
2506
|
+
});
|
|
2507
|
+
}
|
|
2508
|
+
function ObligationCheck({ check }) {
|
|
2509
|
+
useEffect(() => {
|
|
2510
|
+
check();
|
|
2511
|
+
});
|
|
2512
|
+
return null;
|
|
2513
|
+
}
|
|
2514
|
+
//#endregion
|
|
2515
|
+
//#region src/frames/slot-wrapper.tsx
|
|
2516
|
+
function DraftBanner() {
|
|
2517
|
+
const { resource, mode, controller } = useRecordFrame();
|
|
2518
|
+
const t = useOptionalT();
|
|
2519
|
+
const tid = useTestIds();
|
|
2520
|
+
const { draft } = controller;
|
|
2521
|
+
if (mode === "show" || draft.pending === null) return null;
|
|
2522
|
+
return jsxs("div", {
|
|
2523
|
+
"data-testid": tid.formDraftBanner(resource, mode),
|
|
2524
|
+
role: "status",
|
|
2525
|
+
"aria-live": "polite",
|
|
2526
|
+
className: "flex flex-wrap items-center gap-2 rounded-md border border-warning/50 bg-warning/10 px-3 py-2 text-sm",
|
|
2527
|
+
children: [
|
|
2528
|
+
jsx("span", {
|
|
2529
|
+
className: "font-medium",
|
|
2530
|
+
children: draft.recordChanged ? t("draft.recordChanged") : t("draft.found", { params: { time: timeAgo(draft.pending.savedAt, t) } })
|
|
2531
|
+
}),
|
|
2532
|
+
draft.recordChanged ? null : jsx("span", {
|
|
2533
|
+
className: "text-muted-foreground",
|
|
2534
|
+
children: t("draft.restorePrompt")
|
|
2535
|
+
}),
|
|
2536
|
+
jsxs("div", {
|
|
2537
|
+
className: "ml-auto flex gap-2",
|
|
2538
|
+
children: [draft.recordChanged ? null : jsx(Button, {
|
|
2539
|
+
type: "button",
|
|
2540
|
+
size: "sm",
|
|
2541
|
+
variant: "outline",
|
|
2542
|
+
"data-testid": tid.formDraftRestore(resource, mode),
|
|
2543
|
+
onClick: draft.restore,
|
|
2544
|
+
children: t("draft.restore")
|
|
2545
|
+
}), jsx(Button, {
|
|
2546
|
+
type: "button",
|
|
2547
|
+
size: "sm",
|
|
2548
|
+
variant: "ghost",
|
|
2549
|
+
"data-testid": tid.formDraftDiscard(resource, mode),
|
|
2550
|
+
onClick: draft.discard,
|
|
2551
|
+
children: t("draft.discard")
|
|
2552
|
+
})]
|
|
2553
|
+
})
|
|
2554
|
+
]
|
|
2555
|
+
});
|
|
2556
|
+
}
|
|
2557
|
+
function timeAgo(savedAt, t) {
|
|
2558
|
+
const then = new Date(savedAt).getTime();
|
|
2559
|
+
if (!Number.isFinite(then)) return t("draft.timeAgoRecently");
|
|
2560
|
+
const diff = Date.now() - then;
|
|
2561
|
+
if (diff < 6e4) return t("draft.timeAgoMoment");
|
|
2562
|
+
if (diff < 36e5) return t("draft.timeAgoMinutes", { count: Math.round(diff / 6e4) });
|
|
2563
|
+
if (diff < 864e5) return t("draft.timeAgoHours", { count: Math.round(diff / 36e5) });
|
|
2564
|
+
return t("draft.timeAgoDays", { count: Math.round(diff / 864e5) });
|
|
2565
|
+
}
|
|
2566
|
+
function frameRecordId(frame, def) {
|
|
2567
|
+
if (frame.recordId !== void 0) return frame.recordId;
|
|
2568
|
+
const id = recordKeyOf(def, frame.record);
|
|
2569
|
+
return typeof id === "string" || typeof id === "number" ? String(id) : void 0;
|
|
2570
|
+
}
|
|
2571
|
+
function BodySlot() {
|
|
2572
|
+
const frame = useRecordFrame();
|
|
2573
|
+
const { resource, mode, controller } = frame;
|
|
2574
|
+
const chrome = useFrameChrome();
|
|
2575
|
+
const tid = useTestIds();
|
|
2576
|
+
const id = frameRecordId(frame, useApp().registry.get(resource));
|
|
2577
|
+
const tree = jsx("div", {
|
|
2578
|
+
"data-buro-region": "record-body",
|
|
2579
|
+
"data-buro-slot": FORM_SLOT,
|
|
2580
|
+
children: jsx(ScreenTree, {
|
|
2581
|
+
region: "record",
|
|
2582
|
+
slot: FORM_SLOT,
|
|
2583
|
+
resource,
|
|
2584
|
+
mode,
|
|
2585
|
+
compiled: controller.screen,
|
|
2586
|
+
controller
|
|
2587
|
+
})
|
|
2588
|
+
});
|
|
2589
|
+
if (mode === "show") return tree;
|
|
2590
|
+
return jsxs("form", {
|
|
2591
|
+
id: chrome.formId,
|
|
2592
|
+
noValidate: true,
|
|
2593
|
+
"data-testid": tid.form(resource, mode, id),
|
|
2594
|
+
onSubmit: (event) => {
|
|
2595
|
+
event.preventDefault();
|
|
2596
|
+
const steps = controller.steps;
|
|
2597
|
+
if (steps !== null && steps.current !== void 0 && steps.current !== steps.ids[steps.ids.length - 1]) steps.next();
|
|
2598
|
+
else chrome.submit();
|
|
2599
|
+
},
|
|
2600
|
+
className: "flex flex-col gap-4",
|
|
2601
|
+
children: [jsx(DraftBanner, {}), tree]
|
|
2602
|
+
});
|
|
2603
|
+
}
|
|
2604
|
+
function DisplaySlot({ name }) {
|
|
2605
|
+
const { resource, mode, controller } = useRecordFrame();
|
|
2606
|
+
return jsx(ScreenTree, {
|
|
2607
|
+
region: "record",
|
|
2608
|
+
slot: name,
|
|
2609
|
+
resource,
|
|
2610
|
+
mode,
|
|
2611
|
+
compiled: controller.screen,
|
|
2612
|
+
controller
|
|
2613
|
+
});
|
|
2614
|
+
}
|
|
2615
|
+
function SlotWrapper({ name }) {
|
|
2616
|
+
if (!useSlotOnce(name)) return null;
|
|
2617
|
+
return name === FORM_SLOT ? jsx(BodySlot, {}) : jsx(DisplaySlot, { name });
|
|
2618
|
+
}
|
|
2619
|
+
//#endregion
|
|
2620
|
+
//#region src/frames/record-frame-host.tsx
|
|
2621
|
+
const NO_ROOT = { current: null };
|
|
2622
|
+
function drawnFields(roots, compiled, out) {
|
|
2623
|
+
for (const node of roots) {
|
|
2624
|
+
if (node.state === "absent") continue;
|
|
2625
|
+
const field = node.field ?? compiled.nodes[node.index]?.field;
|
|
2626
|
+
if (node.kind === "field" && field !== void 0 && node.state === "ready") out.add(field);
|
|
2627
|
+
drawnFields(node.children, compiled, out);
|
|
2628
|
+
}
|
|
2629
|
+
}
|
|
2630
|
+
function frameRecord(record, compiled, slots, idField, visible = () => true) {
|
|
2631
|
+
if (record === void 0 || compiled === null || slots === void 0) return void 0;
|
|
2632
|
+
const drawn = new Set();
|
|
2633
|
+
for (const roots of Object.values(slots)) drawnFields(roots, compiled, drawn);
|
|
2634
|
+
const declarations = new Map(compiled.fields.map((entry) => [entry.name, entry.declaration]));
|
|
2635
|
+
const out = {};
|
|
2636
|
+
if (Object.hasOwn(record, idField)) out[idField] = record[idField];
|
|
2637
|
+
for (const name of drawn) {
|
|
2638
|
+
if (!Object.hasOwn(record, name) || !visible(name)) continue;
|
|
2639
|
+
if (fieldSensitivity(declarations.get(name)) !== void 0) continue;
|
|
2640
|
+
out[name] = record[name];
|
|
2641
|
+
}
|
|
2642
|
+
return Object.freeze(out);
|
|
2643
|
+
}
|
|
2644
|
+
function declaredSlots(compiled) {
|
|
2645
|
+
if (compiled === null) return [];
|
|
2646
|
+
return Object.entries(compiled.slots).filter(([, nodes]) => nodes.length > 0).map(([name]) => name);
|
|
2647
|
+
}
|
|
2648
|
+
function useEffectiveFrame(instance) {
|
|
2649
|
+
const { resource, mode, controller } = useRecordFrame();
|
|
2650
|
+
const compiled = controller.screen;
|
|
2651
|
+
const missing = useMemo(() => {
|
|
2652
|
+
if (instance === void 0 || instance.for !== "record") return void 0;
|
|
2653
|
+
const own = new Set(Object.keys(instance.slots));
|
|
2654
|
+
return declaredSlots(compiled).find((slot) => !own.has(slot));
|
|
2655
|
+
}, [instance, compiled]);
|
|
2656
|
+
const told = useRef(false);
|
|
2657
|
+
const instanceId = instance === void 0 ? void 0 : formatEntityRef(instance.ref);
|
|
2658
|
+
useEffect(() => {
|
|
2659
|
+
if (missing === void 0 || told.current || process.env.NODE_ENV === "production") return;
|
|
2660
|
+
told.current = true;
|
|
2661
|
+
console.error(`[burojs] <RecordScreen frame={${instanceId}}> lacks the slot ${missing} that ${resource}'s ${mode} screen fills; the screen's own frame draws it.`);
|
|
2662
|
+
}, [
|
|
2663
|
+
missing,
|
|
2664
|
+
instanceId,
|
|
2665
|
+
resource,
|
|
2666
|
+
mode
|
|
2667
|
+
]);
|
|
2668
|
+
if (instance !== void 0 && instance.for === "record" && missing === void 0 && compiled !== null) return {
|
|
2669
|
+
ref: instance.ref,
|
|
2670
|
+
slotNames: Object.keys(instance.slots)
|
|
2671
|
+
};
|
|
2672
|
+
return {
|
|
2673
|
+
ref: compiled?.frame ?? builtinFrames.record.ref,
|
|
2674
|
+
slotNames: compiled === null ? ["header", FORM_SLOT] : Object.keys(compiled.slots)
|
|
2675
|
+
};
|
|
2676
|
+
}
|
|
2677
|
+
function RecordFrameView({ frame: instance }) {
|
|
2678
|
+
const { resource, mode, controller, record } = useRecordFrame();
|
|
2679
|
+
const chrome = useFrameChrome();
|
|
2680
|
+
const quick = useQuickEntry();
|
|
2681
|
+
const app = useApp();
|
|
2682
|
+
const catalog = useFrameImplementations();
|
|
2683
|
+
const { ref, slotNames } = useEffectiveFrame(instance);
|
|
2684
|
+
const id = formatEntityRef(ref);
|
|
2685
|
+
const resolved = useMemo(() => resolveFrameView(ref, catalog, "record"), [ref, catalog]);
|
|
2686
|
+
const t = useFrameT(id);
|
|
2687
|
+
const compiled = controller.screen;
|
|
2688
|
+
const plan = controller.plan;
|
|
2689
|
+
const slots = {};
|
|
2690
|
+
for (const name of slotNames) {
|
|
2691
|
+
if (name === FORM_SLOT) {
|
|
2692
|
+
slots[name] = jsx(SlotWrapper, { name }, name);
|
|
2693
|
+
continue;
|
|
2694
|
+
}
|
|
2695
|
+
slots[name] = (plan?.slots[name] ?? []).every((node) => node.state === "absent") ? null : jsx(SlotWrapper, { name }, name);
|
|
2696
|
+
}
|
|
2697
|
+
slots[FORM_SLOT] ??= jsx(SlotWrapper, { name: FORM_SLOT });
|
|
2698
|
+
const idField = app.registry.get(resource)?.data?.idField ?? "id";
|
|
2699
|
+
const access = useFieldAccess(resource, "record", record === void 0 ? void 0 : policyRecordOf(controller, record));
|
|
2700
|
+
const projected = useMemo(() => frameRecord(record, compiled, plan?.slots, idField, (name) => access[name]?.view === "visible"), [
|
|
2701
|
+
record,
|
|
2702
|
+
compiled,
|
|
2703
|
+
plan,
|
|
2704
|
+
idField,
|
|
2705
|
+
access
|
|
2706
|
+
]);
|
|
2707
|
+
const quickHost = chrome.hostTitle === true && quick !== null;
|
|
2708
|
+
const props = {
|
|
2709
|
+
slots,
|
|
2710
|
+
host: quickHost ? quick.host : "page",
|
|
2711
|
+
mode,
|
|
2712
|
+
screen: {
|
|
2713
|
+
resource,
|
|
2714
|
+
entry: quickHost ? "quick" : "route"
|
|
2715
|
+
},
|
|
2716
|
+
record: mode === "create" ? void 0 : projected,
|
|
2717
|
+
t
|
|
2718
|
+
};
|
|
2719
|
+
const policyRecord = policyRecordOf(controller, record);
|
|
2720
|
+
const rowId = record?.[idField];
|
|
2721
|
+
const actionRow = useMemo(() => ({
|
|
2722
|
+
...policyRecord ?? {},
|
|
2723
|
+
id: typeof rowId === "string" || typeof rowId === "number" ? String(rowId) : ""
|
|
2724
|
+
}), [policyRecord, rowId]);
|
|
2725
|
+
const showItemActions = useShownItemActions(resource, actionRow, "show", void 0, controller.sources);
|
|
2726
|
+
const showActions = mode === "show" && record !== void 0 && showItemActions.length > 0;
|
|
2727
|
+
const View = resolved.view;
|
|
2728
|
+
const builtin = isBuiltinRecordView(resolved);
|
|
2729
|
+
return jsxs(Fragment$1, { children: [builtin ? null : jsxs(Fragment$1, { children: [jsx(HeaderTypeNotice, { resource }), jsx(ChangeTypeButton, {})] }), jsx(TypeChromeDrawnContext.Provider, {
|
|
2730
|
+
value: !builtin,
|
|
2731
|
+
children: jsx(Suspense, {
|
|
2732
|
+
fallback: jsx(RecordState, { state: "loading" }),
|
|
2733
|
+
children: jsx(FrameObligations, {
|
|
2734
|
+
id,
|
|
2735
|
+
mode,
|
|
2736
|
+
showActions,
|
|
2737
|
+
root: chrome.root ?? NO_ROOT,
|
|
2738
|
+
children: jsx(View, { ...props })
|
|
2739
|
+
})
|
|
2740
|
+
})
|
|
2741
|
+
})] });
|
|
2742
|
+
}
|
|
2743
|
+
function useResourceDimensionOptions(dimension, resource, labelField) {
|
|
2744
|
+
const def = useApp().registry.get(resource);
|
|
2745
|
+
const rowTitle = useRowTitle(resource, labelField ?? def?.displayField);
|
|
2746
|
+
const rows = useList(resource, {
|
|
2747
|
+
pagination: {
|
|
2748
|
+
kind: "offset",
|
|
2749
|
+
page: 1,
|
|
2750
|
+
pageSize: 100
|
|
2751
|
+
},
|
|
2752
|
+
total: false,
|
|
2753
|
+
scope: {
|
|
2754
|
+
default: "session",
|
|
2755
|
+
dimensions: { [dimension]: "none" }
|
|
2756
|
+
}
|
|
2757
|
+
}).data?.data;
|
|
2758
|
+
return useMemo(() => (rows ?? []).flatMap((row) => {
|
|
2759
|
+
const key = recordKeyOf(def, row);
|
|
2760
|
+
return key === null || key === void 0 ? [] : [{
|
|
2761
|
+
value: key,
|
|
2762
|
+
label: rowTitle(row)
|
|
2763
|
+
}];
|
|
2764
|
+
}), [
|
|
2765
|
+
rows,
|
|
2766
|
+
def,
|
|
2767
|
+
rowTitle
|
|
2768
|
+
]);
|
|
2769
|
+
}
|
|
2770
|
+
//#endregion
|
|
2771
|
+
//#region src/record/scope-mismatch-banner.tsx
|
|
2772
|
+
const asList = (value) => value === null ? [] : Array.isArray(value) ? value : [value];
|
|
2773
|
+
const isScalar = (value) => typeof value === "string" || typeof value === "number" || typeof value === "boolean";
|
|
2774
|
+
function ScopeMismatchBanner(props) {
|
|
2775
|
+
const app = useApp();
|
|
2776
|
+
const mapping = app.registry.get(props.resource)?.data?.scope;
|
|
2777
|
+
if (app.scope === void 0 || mapping === void 0 || props.record === void 0) return null;
|
|
2778
|
+
return jsx(Banner, {
|
|
2779
|
+
...props,
|
|
2780
|
+
record: props.record,
|
|
2781
|
+
mapping
|
|
2782
|
+
});
|
|
2783
|
+
}
|
|
2784
|
+
function Banner({ record, mapping, kind = "record" }) {
|
|
2785
|
+
const scope = useScope();
|
|
2786
|
+
const chromeT = useChromeT();
|
|
2787
|
+
const differences = useMemo(() => {
|
|
2788
|
+
const out = [];
|
|
2789
|
+
for (const [dimension, target] of Object.entries(mapping)) {
|
|
2790
|
+
const definition = scope.definition?.dimensions[dimension];
|
|
2791
|
+
if (definition === void 0 || definition.select === "none") continue;
|
|
2792
|
+
const selected = asList(scope.selection[dimension] ?? null);
|
|
2793
|
+
if (selected.length === 0) continue;
|
|
2794
|
+
const column = scopeColumnOf(target);
|
|
2795
|
+
if (!Object.hasOwn(record, column)) continue;
|
|
2796
|
+
const held = record[column];
|
|
2797
|
+
if (held === null || held === void 0 || held === "") {
|
|
2798
|
+
if (kind === "created" || "field" in target && target.empty === "shared") continue;
|
|
2799
|
+
out.push({
|
|
2800
|
+
dimension,
|
|
2801
|
+
value: null,
|
|
2802
|
+
selected,
|
|
2803
|
+
select: definition.select
|
|
2804
|
+
});
|
|
2805
|
+
continue;
|
|
2806
|
+
}
|
|
2807
|
+
if (!isScalar(held)) continue;
|
|
2808
|
+
if (selected.some((value) => String(value) === String(held))) continue;
|
|
2809
|
+
out.push({
|
|
2810
|
+
dimension,
|
|
2811
|
+
value: held,
|
|
2812
|
+
selected,
|
|
2813
|
+
select: definition.select
|
|
2814
|
+
});
|
|
2815
|
+
}
|
|
2816
|
+
return out;
|
|
2817
|
+
}, [
|
|
2818
|
+
mapping,
|
|
2819
|
+
record,
|
|
2820
|
+
scope.definition,
|
|
2821
|
+
scope.selection,
|
|
2822
|
+
kind
|
|
2823
|
+
]);
|
|
2824
|
+
if (differences.length === 0) return null;
|
|
2825
|
+
const apply = () => {
|
|
2826
|
+
for (const difference of differences) scope.select(difference.dimension, difference.value === null ? null : difference.select === "multiple" ? [difference.value] : difference.value);
|
|
2827
|
+
};
|
|
2828
|
+
return jsxs("div", {
|
|
2829
|
+
role: "status",
|
|
2830
|
+
"data-testid": "scope-mismatch:banner",
|
|
2831
|
+
className: "flex flex-wrap items-center justify-between gap-3 rounded-md border bg-muted/50 px-3 py-2 text-sm",
|
|
2832
|
+
children: [jsxs("div", {
|
|
2833
|
+
className: "space-y-1",
|
|
2834
|
+
children: [jsx("p", {
|
|
2835
|
+
className: "font-medium",
|
|
2836
|
+
children: kind === "created" ? chromeT("scope.mismatch.createdOutside", { defaultValue: "Created outside the current scope" }) : chromeT("scope.mismatch.title", { defaultValue: "This record is outside the current scope" })
|
|
2837
|
+
}), jsx("ul", {
|
|
2838
|
+
className: "text-muted-foreground",
|
|
2839
|
+
children: differences.map((difference) => jsx(DifferenceLine, {
|
|
2840
|
+
difference,
|
|
2841
|
+
definition: scope.definition.dimensions[difference.dimension]
|
|
2842
|
+
}, difference.dimension))
|
|
2843
|
+
})]
|
|
2844
|
+
}), jsx(Button, {
|
|
2845
|
+
type: "button",
|
|
2846
|
+
size: "sm",
|
|
2847
|
+
"data-testid": "scope-mismatch:switch",
|
|
2848
|
+
onClick: apply,
|
|
2849
|
+
children: chromeT("scope.mismatch.switch", { defaultValue: "Switch" })
|
|
2850
|
+
})]
|
|
2851
|
+
});
|
|
2852
|
+
}
|
|
2853
|
+
function DifferenceLine(props) {
|
|
2854
|
+
const source = props.definition.values;
|
|
2855
|
+
if (source !== void 0 && !Array.isArray(source)) {
|
|
2856
|
+
const { resource, label } = source;
|
|
2857
|
+
return jsx(ResourceLine, {
|
|
2858
|
+
...props,
|
|
2859
|
+
resource,
|
|
2860
|
+
...label ? { labelField: label } : {}
|
|
2861
|
+
});
|
|
2862
|
+
}
|
|
2863
|
+
return jsx(LineText, {
|
|
2864
|
+
difference: props.difference,
|
|
2865
|
+
options: source ?? []
|
|
2866
|
+
});
|
|
2867
|
+
}
|
|
2868
|
+
function ResourceLine({ difference, resource, labelField }) {
|
|
2869
|
+
const options = useResourceDimensionOptions(difference.dimension, resource, labelField);
|
|
2870
|
+
return jsx(LineText, {
|
|
2871
|
+
difference,
|
|
2872
|
+
options
|
|
2873
|
+
});
|
|
2874
|
+
}
|
|
2875
|
+
function LineText({ difference, options }) {
|
|
2876
|
+
const chromeT = useChromeT();
|
|
2877
|
+
const labelOf = (value) => options.find((option) => String(option.value) === String(value))?.label ?? String(value);
|
|
2878
|
+
const field = humanizeKey(difference.dimension);
|
|
2879
|
+
const item = difference.value === null ? chromeT("scope.mismatch.empty", { defaultValue: "not set" }) : labelOf(difference.value);
|
|
2880
|
+
const items = difference.selected.map(labelOf).join(", ");
|
|
2881
|
+
return jsx("li", {
|
|
2882
|
+
"data-testid": `scope-mismatch:line:${difference.dimension}`,
|
|
2883
|
+
children: chromeT("scope.mismatch.line", {
|
|
2884
|
+
params: {
|
|
2885
|
+
field,
|
|
2886
|
+
item,
|
|
2887
|
+
items
|
|
2888
|
+
},
|
|
2889
|
+
defaultValue: `${field}: ${item} (selected: ${items})`
|
|
2890
|
+
})
|
|
2891
|
+
});
|
|
2892
|
+
}
|
|
2893
|
+
//#endregion
|
|
2894
|
+
//#region src/record/type-offer.tsx
|
|
2895
|
+
function TypeOffer({ resource, offer, onChoose }) {
|
|
2896
|
+
const app = useApp();
|
|
2897
|
+
const t = useOptionalT();
|
|
2898
|
+
const tid = useTestIds();
|
|
2899
|
+
const def = app.registry.get(resource);
|
|
2900
|
+
const by = def?.types?.by;
|
|
2901
|
+
const field = by === void 0 ? void 0 : def?.fields[by];
|
|
2902
|
+
const ref = def?.ref ?? {
|
|
2903
|
+
namespace: "default",
|
|
2904
|
+
id: resource
|
|
2905
|
+
};
|
|
2906
|
+
return jsxs("section", {
|
|
2907
|
+
"aria-labelledby": "type-offer-title",
|
|
2908
|
+
className: "flex flex-col gap-3",
|
|
2909
|
+
children: [jsxs("div", {
|
|
2910
|
+
className: "flex flex-col gap-1",
|
|
2911
|
+
children: [jsx("h2", {
|
|
2912
|
+
id: "type-offer-title",
|
|
2913
|
+
className: "text-base font-semibold",
|
|
2914
|
+
children: t("create.typeOffer.title", { defaultValue: "Choose a type" })
|
|
2915
|
+
}), jsx("p", {
|
|
2916
|
+
className: "text-sm text-muted-foreground",
|
|
2917
|
+
children: t("create.typeOffer.hint", { defaultValue: "The type decides which fields the record has." })
|
|
2918
|
+
})]
|
|
2919
|
+
}), offer.status === "pending" ? jsxs("div", {
|
|
2920
|
+
"aria-busy": "true",
|
|
2921
|
+
className: "flex flex-col gap-2",
|
|
2922
|
+
children: [jsx(Skeleton, { className: "h-10 w-full" }), jsx(Skeleton, { className: "h-10 w-full" })]
|
|
2923
|
+
}) : offer.types.length === 0 ? jsx("p", {
|
|
2924
|
+
role: "status",
|
|
2925
|
+
className: "text-sm text-muted-foreground",
|
|
2926
|
+
children: t("create.typeOffer.empty", { defaultValue: "No type can be created here." })
|
|
2927
|
+
}) : jsx("ul", {
|
|
2928
|
+
className: "grid grid-cols-1 gap-2 sm:grid-cols-2",
|
|
2929
|
+
children: offer.types.map((type) => jsx("li", { children: jsx("button", {
|
|
2930
|
+
type: "button",
|
|
2931
|
+
"data-testid": tid.typeOffer(resource, type),
|
|
2932
|
+
onClick: () => onChoose(type),
|
|
2933
|
+
className: "flex w-full items-center gap-2 rounded-md border px-3 py-2 text-left text-sm hover:bg-muted focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring",
|
|
2934
|
+
children: by === void 0 || field === void 0 ? type : jsx(FieldDisplay, {
|
|
2935
|
+
resource: ref,
|
|
2936
|
+
name: by,
|
|
2937
|
+
field,
|
|
2938
|
+
value: type,
|
|
2939
|
+
fallback: jsx("span", { children: type })
|
|
2940
|
+
})
|
|
2941
|
+
}) }, type))
|
|
2942
|
+
})]
|
|
2943
|
+
});
|
|
2944
|
+
}
|
|
2945
|
+
//#endregion
|
|
2946
|
+
//#region src/record/record-screen.tsx
|
|
2947
|
+
function isAbort(error) {
|
|
2948
|
+
if (error === null || typeof error !== "object" && typeof error !== "function") return false;
|
|
2949
|
+
try {
|
|
2950
|
+
return error.name === "AbortError";
|
|
2951
|
+
} catch {
|
|
2952
|
+
return false;
|
|
2953
|
+
}
|
|
2954
|
+
}
|
|
2955
|
+
const NO_ISSUES = Object.freeze([]);
|
|
2956
|
+
const APPLIED_UNREADABLE = Object.freeze({
|
|
2957
|
+
code: "applied-unreadable",
|
|
2958
|
+
message: Object.freeze({
|
|
2959
|
+
key: "errors.appliedUnreadable",
|
|
2960
|
+
default: "Saved. The server's answer could not be read, so the screen may not show the latest values."
|
|
2961
|
+
}),
|
|
2962
|
+
path: Object.freeze([]),
|
|
2963
|
+
source: "server",
|
|
2964
|
+
severity: "info"
|
|
2965
|
+
});
|
|
2966
|
+
function issuesOf(error) {
|
|
2967
|
+
if (error === null || typeof error !== "object") return void 0;
|
|
2968
|
+
try {
|
|
2969
|
+
const descriptor = Object.getOwnPropertyDescriptor(error, "issues");
|
|
2970
|
+
return descriptor !== void 0 && "value" in descriptor ? descriptor.value : void 0;
|
|
2971
|
+
} catch {
|
|
2972
|
+
return;
|
|
2973
|
+
}
|
|
2974
|
+
}
|
|
2975
|
+
const NO_EDITED = new Set();
|
|
2976
|
+
function sameValue(a, b) {
|
|
2977
|
+
if (Object.is(a, b)) return true;
|
|
2978
|
+
try {
|
|
2979
|
+
return JSON.stringify(a) === JSON.stringify(b);
|
|
2980
|
+
} catch {
|
|
2981
|
+
return false;
|
|
2982
|
+
}
|
|
2983
|
+
}
|
|
2984
|
+
function RecordFrameHost(props) {
|
|
2985
|
+
const { resource, mode } = props;
|
|
2986
|
+
const [failed, setFailed] = useState(NO_ISSUES);
|
|
2987
|
+
const named = useMemo(() => failed.length === 0 ? NO_ISSUES : routeServerIssues({ issues: failed }, (segment) => segment, (field) => field !== "_form").fields, [failed]);
|
|
2988
|
+
const atFailure = useRef({});
|
|
2989
|
+
const [edited, setEdited] = useState(NO_EDITED);
|
|
2990
|
+
const controller = useFormController({
|
|
2991
|
+
resource,
|
|
2992
|
+
mode,
|
|
2993
|
+
...props.recordId === void 0 ? {} : { recordId: props.recordId },
|
|
2994
|
+
...props.type === void 0 ? {} : { type: props.type },
|
|
2995
|
+
...props.record === void 0 ? {} : { record: props.record },
|
|
2996
|
+
...props.defaults === void 0 ? {} : { defaults: props.defaults },
|
|
2997
|
+
...props.onSubmit === void 0 ? {} : { onSubmit: props.onSubmit },
|
|
2998
|
+
...props.hostTitle ? { entry: "quick" } : {},
|
|
2999
|
+
serverIssues: named
|
|
3000
|
+
});
|
|
3001
|
+
const inputApplied = useRef(false);
|
|
3002
|
+
const { input } = props;
|
|
3003
|
+
const plan = controller.plan;
|
|
3004
|
+
useEffect(() => {
|
|
3005
|
+
if (inputApplied.current || input === void 0 || mode !== "create" || plan === null) return;
|
|
3006
|
+
inputApplied.current = true;
|
|
3007
|
+
for (const [key, value] of Object.entries(input)) if (plan.fields[key] === "active") controller.setValue(key, value);
|
|
3008
|
+
}, [
|
|
3009
|
+
input,
|
|
3010
|
+
mode,
|
|
3011
|
+
plan,
|
|
3012
|
+
controller
|
|
3013
|
+
]);
|
|
3014
|
+
const tid = useTestIds();
|
|
3015
|
+
const formId = `record-form-${useId()}`;
|
|
3016
|
+
const root = useRef(null);
|
|
3017
|
+
const [submitError, setSubmitError] = useState(void 0);
|
|
3018
|
+
const [submitApplied, setSubmitApplied] = useState(void 0);
|
|
3019
|
+
useEffect(() => {
|
|
3020
|
+
if (plan === null) return;
|
|
3021
|
+
const now = Object.keys(atFailure.current).filter((name) => !edited.has(name) && plan.fields[name] === "active" && !sameValue(controller.field(name).value, atFailure.current[name]));
|
|
3022
|
+
if (now.length > 0) setEdited((previous) => new Set([...previous, ...now]));
|
|
3023
|
+
}, [
|
|
3024
|
+
plan,
|
|
3025
|
+
controller,
|
|
3026
|
+
edited
|
|
3027
|
+
]);
|
|
3028
|
+
const notify = useNoticeSink(resource);
|
|
3029
|
+
const routeScreen = useIsRouteScreen();
|
|
3030
|
+
const leavesOnCreate = mode === "create" && routeScreen;
|
|
3031
|
+
const [created, setCreated] = useState(false);
|
|
3032
|
+
const createdRef = useRef(false);
|
|
3033
|
+
const latest = useRef({
|
|
3034
|
+
controller,
|
|
3035
|
+
onSuccess: props.onSuccess,
|
|
3036
|
+
notify,
|
|
3037
|
+
leavesOnCreate
|
|
3038
|
+
});
|
|
3039
|
+
latest.current = {
|
|
3040
|
+
controller,
|
|
3041
|
+
onSuccess: props.onSuccess,
|
|
3042
|
+
notify,
|
|
3043
|
+
leavesOnCreate
|
|
3044
|
+
};
|
|
3045
|
+
const mounted = useRef(true);
|
|
3046
|
+
useEffect(() => {
|
|
3047
|
+
mounted.current = true;
|
|
3048
|
+
return () => {
|
|
3049
|
+
mounted.current = false;
|
|
3050
|
+
};
|
|
3051
|
+
}, []);
|
|
3052
|
+
const submit = useCallback(async () => {
|
|
3053
|
+
if (createdRef.current) return;
|
|
3054
|
+
setSubmitError(void 0);
|
|
3055
|
+
setSubmitApplied(void 0);
|
|
3056
|
+
setFailed(NO_ISSUES);
|
|
3057
|
+
setEdited(NO_EDITED);
|
|
3058
|
+
atFailure.current = {};
|
|
3059
|
+
const outcome = await latest.current.controller.submit();
|
|
3060
|
+
if (!mounted.current) return;
|
|
3061
|
+
if (latest.current.leavesOnCreate && (outcome.status === "submitted" || outcome.status === "failed" && outcome.applied === "yes")) {
|
|
3062
|
+
createdRef.current = true;
|
|
3063
|
+
setCreated(true);
|
|
3064
|
+
}
|
|
3065
|
+
if (outcome.status === "submitted") {
|
|
3066
|
+
const notices = successNoticesOf(outcome.result);
|
|
3067
|
+
if (notices.length > 0) latest.current.notify(notices);
|
|
3068
|
+
latest.current.onSuccess?.(outcome.result);
|
|
3069
|
+
} else if (outcome.status === "failed" && outcome.applied === "yes") {
|
|
3070
|
+
latest.current.notify([APPLIED_UNREADABLE]);
|
|
3071
|
+
latest.current.onSuccess?.(void 0);
|
|
3072
|
+
} else if (outcome.status === "failed" && !isAbort(outcome.error)) {
|
|
3073
|
+
setSubmitError(outcome.error ?? null);
|
|
3074
|
+
setSubmitApplied(outcome.applied);
|
|
3075
|
+
const issues = normalizeIssueInputs(issuesOf(outcome.error)).issues;
|
|
3076
|
+
const next = issues.length === 0 ? NO_ISSUES : Object.freeze(issues.map(issueOfInput));
|
|
3077
|
+
const held = {};
|
|
3078
|
+
const now = latest.current.controller;
|
|
3079
|
+
for (const issue of next) {
|
|
3080
|
+
const head = issue.path[0];
|
|
3081
|
+
if (typeof head === "string" && head !== "_form" && now.plan?.fields[head] === "active") held[head] = now.field(head).value;
|
|
3082
|
+
}
|
|
3083
|
+
atFailure.current = held;
|
|
3084
|
+
setFailed(next);
|
|
3085
|
+
}
|
|
3086
|
+
}, []);
|
|
3087
|
+
const record = controller.record;
|
|
3088
|
+
const recordText = props.recordId === void 0 ? void 0 : String(props.recordId);
|
|
3089
|
+
const frame = useMemo(() => ({
|
|
3090
|
+
resource,
|
|
3091
|
+
recordId: recordText,
|
|
3092
|
+
mode,
|
|
3093
|
+
record,
|
|
3094
|
+
controller
|
|
3095
|
+
}), [
|
|
3096
|
+
resource,
|
|
3097
|
+
recordText,
|
|
3098
|
+
mode,
|
|
3099
|
+
record,
|
|
3100
|
+
controller
|
|
3101
|
+
]);
|
|
3102
|
+
const { chooseType } = props;
|
|
3103
|
+
const typedAndAccepted = mode === "create" && controller.typeOffer !== null && controller.failure === null;
|
|
3104
|
+
const adapter = useOptionalRouterAdapter();
|
|
3105
|
+
const hostRouter = routeScreen ? adapter : null;
|
|
3106
|
+
const guardText = useNavigationGuardText();
|
|
3107
|
+
const changeType = useMemo(() => typedAndAccepted ? () => {
|
|
3108
|
+
if (hostRouter === null && latest.current.controller.isDirty && !window.confirm(`${guardText.title}\n\n${guardText.body}`)) return;
|
|
3109
|
+
chooseType(void 0);
|
|
3110
|
+
} : void 0, [
|
|
3111
|
+
typedAndAccepted,
|
|
3112
|
+
chooseType,
|
|
3113
|
+
hostRouter,
|
|
3114
|
+
guardText.title,
|
|
3115
|
+
guardText.body
|
|
3116
|
+
]);
|
|
3117
|
+
const app = useApp();
|
|
3118
|
+
const typesDecl = app.registry.get(resource)?.types;
|
|
3119
|
+
const loaded = controller.record;
|
|
3120
|
+
const unsupportedType = useMemo(() => {
|
|
3121
|
+
if (mode === "create" || typesDecl === void 0 || loaded === void 0) return void 0;
|
|
3122
|
+
const value = loaded[typesDecl.by];
|
|
3123
|
+
const name = typeof value === "string" && value !== "" ? value : null;
|
|
3124
|
+
return name !== null && typesDecl.items.some((item) => item.name === name) ? void 0 : name;
|
|
3125
|
+
}, [
|
|
3126
|
+
mode,
|
|
3127
|
+
typesDecl,
|
|
3128
|
+
loaded
|
|
3129
|
+
]);
|
|
3130
|
+
const shownFailed = useMemo(() => edited.size === 0 ? failed : Object.freeze(failed.filter((issue) => {
|
|
3131
|
+
const head = issue.path[0];
|
|
3132
|
+
return typeof head !== "string" || !edited.has(head);
|
|
3133
|
+
})), [failed, edited]);
|
|
3134
|
+
const chrome = useMemo(() => ({
|
|
3135
|
+
...props.onCancel === void 0 ? {} : { onCancel: props.onCancel },
|
|
3136
|
+
...props.submitLabel === void 0 ? {} : { submitLabel: props.submitLabel },
|
|
3137
|
+
...props.extraActions === void 0 ? {} : { extraActions: props.extraActions },
|
|
3138
|
+
...changeType === void 0 ? {} : { changeType },
|
|
3139
|
+
...unsupportedType === void 0 ? {} : { unsupportedType },
|
|
3140
|
+
...props.hostTitle ? { hostTitle: true } : {},
|
|
3141
|
+
formId,
|
|
3142
|
+
submitError,
|
|
3143
|
+
...submitApplied === void 0 ? {} : { submitApplied },
|
|
3144
|
+
serverIssues: shownFailed,
|
|
3145
|
+
...created ? { created: true } : {},
|
|
3146
|
+
submit,
|
|
3147
|
+
root
|
|
3148
|
+
}), [
|
|
3149
|
+
props.onCancel,
|
|
3150
|
+
props.submitLabel,
|
|
3151
|
+
props.extraActions,
|
|
3152
|
+
changeType,
|
|
3153
|
+
unsupportedType,
|
|
3154
|
+
props.hostTitle,
|
|
3155
|
+
formId,
|
|
3156
|
+
submitError,
|
|
3157
|
+
submitApplied,
|
|
3158
|
+
shownFailed,
|
|
3159
|
+
created,
|
|
3160
|
+
submit
|
|
3161
|
+
]);
|
|
3162
|
+
usePublishDocVariant(mode === "show" ? resource : void 0, record);
|
|
3163
|
+
const failure = controller.failure;
|
|
3164
|
+
const offering = failure?.kind === "type-required" && controller.typeOffer !== null;
|
|
3165
|
+
useEffect(() => {
|
|
3166
|
+
if (failure === null || offering || process.env.NODE_ENV === "production") return;
|
|
3167
|
+
console.error(`[burojs] <RecordScreen resource="${resource}" mode="${mode}"> cannot render its screen: ${failure.kind === "screen" ? `${failure.failure.code} at ${failure.failure.path.join(".")}: ${failure.failure.message}` : "a typed create needs an accepted type"}.`);
|
|
3168
|
+
}, [
|
|
3169
|
+
failure,
|
|
3170
|
+
offering,
|
|
3171
|
+
resource,
|
|
3172
|
+
mode
|
|
3173
|
+
]);
|
|
3174
|
+
const formControl = useFormControlOf(controller);
|
|
3175
|
+
const id = frameRecordId(frame, app.registry.get(frame.resource));
|
|
3176
|
+
const rootId = mode === "show" ? tid.pageShow(resource, id ?? "") : mode === "edit" ? tid.pageEdit(resource, id ?? "") : tid.pageCreate(resource);
|
|
3177
|
+
const claimedRoute = useContext(RouteSourceContext);
|
|
3178
|
+
const scopeValues = mode === "show" ? controller.record : controller.values;
|
|
3179
|
+
return jsx(RecordScopeProvider, {
|
|
3180
|
+
resource,
|
|
3181
|
+
...recordText === void 0 ? {} : { recordId: recordText },
|
|
3182
|
+
values: scopeValues,
|
|
3183
|
+
create: mode === "create",
|
|
3184
|
+
children: jsx(RouteSourceContext.Provider, {
|
|
3185
|
+
value: null,
|
|
3186
|
+
children: jsx(ActionRouteContext.Provider, {
|
|
3187
|
+
value: claimedRoute,
|
|
3188
|
+
children: jsx(RecordFrameContext.Provider, {
|
|
3189
|
+
value: frame,
|
|
3190
|
+
children: jsx(FrameChromeContext.Provider, {
|
|
3191
|
+
value: chrome,
|
|
3192
|
+
children: jsxs(FormControlProvider, {
|
|
3193
|
+
value: formControl,
|
|
3194
|
+
children: [jsx(DeepLinkScope, {
|
|
3195
|
+
plan: controller.plan,
|
|
3196
|
+
enabled: routeScreen && mode !== "create",
|
|
3197
|
+
children: jsx("div", {
|
|
3198
|
+
ref: root,
|
|
3199
|
+
"data-testid": rootId,
|
|
3200
|
+
className: "flex flex-col gap-[var(--buro-section-gap)]",
|
|
3201
|
+
children: offering && controller.typeOffer !== null ? jsx(TypeOffer, {
|
|
3202
|
+
resource,
|
|
3203
|
+
offer: controller.typeOffer,
|
|
3204
|
+
onChoose: (type) => chooseType(type)
|
|
3205
|
+
}) : failure !== null ? jsxs(Fragment$1, { children: [jsx(RecordState, { state: "error" }), jsx(FormActions, {})] }) : mode === "edit" && unsupportedType !== void 0 ? jsx(UndeclaredTypeNotice, {
|
|
3206
|
+
resource,
|
|
3207
|
+
typeValue: unsupportedType
|
|
3208
|
+
}) : jsxs(Fragment$1, { children: [jsx(ScopeMismatchBanner, {
|
|
3209
|
+
resource,
|
|
3210
|
+
record: mode === "create" ? controller.trustedSeeds : controller.record,
|
|
3211
|
+
kind: mode === "create" ? "created" : "record"
|
|
3212
|
+
}), jsx(RecordFrameView, { ...props.frame === void 0 ? {} : { frame: props.frame } })] })
|
|
3213
|
+
})
|
|
3214
|
+
}), controller.checkGate === null ? null : jsx(CheckConfirmDialog, {
|
|
3215
|
+
state: controller.checkGate,
|
|
3216
|
+
testid: `${tid.form(resource, mode, id)}:checks`
|
|
3217
|
+
})]
|
|
3218
|
+
})
|
|
3219
|
+
})
|
|
3220
|
+
})
|
|
3221
|
+
})
|
|
3222
|
+
})
|
|
3223
|
+
});
|
|
3224
|
+
}
|
|
3225
|
+
function RecordScreen(props) {
|
|
3226
|
+
const app = useApp();
|
|
3227
|
+
const routeScreen = useContext(RouteScreenClaimContext);
|
|
3228
|
+
const quickScreen = useContext(QuickScreenClaimContext);
|
|
3229
|
+
const route = useContext(RouteSourceContext);
|
|
3230
|
+
const resourceInput = props.resource;
|
|
3231
|
+
const resource = resourceKey(resourceInput);
|
|
3232
|
+
const tabs = useTabsUrlState(props.mode, routeScreen);
|
|
3233
|
+
const adapter = useOptionalRouterAdapter();
|
|
3234
|
+
const router = routeScreen ? adapter : null;
|
|
3235
|
+
const navigate = useRouteNavigate();
|
|
3236
|
+
const [local, setLocal] = useState(props.type);
|
|
3237
|
+
const [typeProp, setTypeProp] = useState(props.type);
|
|
3238
|
+
if (props.type !== typeProp) {
|
|
3239
|
+
setTypeProp(props.type);
|
|
3240
|
+
setLocal(props.type);
|
|
3241
|
+
}
|
|
3242
|
+
const [chose, setChose] = useState(false);
|
|
3243
|
+
const fromUrl = router?.searchParams.get("type") ?? void 0;
|
|
3244
|
+
const type = props.mode !== "create" ? props.type : router === null ? local : chose ? fromUrl : props.type ?? fromUrl;
|
|
3245
|
+
const chooseType = useCallback((next) => {
|
|
3246
|
+
if (router === null) {
|
|
3247
|
+
setLocal(next);
|
|
3248
|
+
return;
|
|
3249
|
+
}
|
|
3250
|
+
setChose(true);
|
|
3251
|
+
const params = new URLSearchParams(router.searchParams);
|
|
3252
|
+
if (next === void 0) params.delete("type");
|
|
3253
|
+
else params.set("type", next);
|
|
3254
|
+
const query = params.toString();
|
|
3255
|
+
navigate(query === "" ? router.pathname : `${router.pathname}?${query}`);
|
|
3256
|
+
}, [router, navigate]);
|
|
3257
|
+
if (app.registry.get(resource) === void 0) return jsx(RecordState, { state: "not-found" });
|
|
3258
|
+
const { type: _declared, ...rest } = props;
|
|
3259
|
+
return jsx(RouteScreenClaimContext.Provider, {
|
|
3260
|
+
value: false,
|
|
3261
|
+
children: jsx(QuickScreenClaimContext.Provider, {
|
|
3262
|
+
value: false,
|
|
3263
|
+
children: jsx(RouteSourceContext.Provider, {
|
|
3264
|
+
value: routeScreen || quickScreen ? route : null,
|
|
3265
|
+
children: jsx(RouteScreenContext.Provider, {
|
|
3266
|
+
value: routeScreen,
|
|
3267
|
+
children: jsx(ResourceContext.Provider, {
|
|
3268
|
+
value: { resource },
|
|
3269
|
+
children: jsx(ScreenStateProvider, {
|
|
3270
|
+
open: tabs.open,
|
|
3271
|
+
onOpenChange: tabs.onOpenChange,
|
|
3272
|
+
children: jsx(RecordFrameHost, {
|
|
3273
|
+
...rest,
|
|
3274
|
+
...type === void 0 ? {} : { type },
|
|
3275
|
+
resource,
|
|
3276
|
+
chooseType,
|
|
3277
|
+
hostTitle: quickScreen
|
|
3278
|
+
}, type ?? "")
|
|
3279
|
+
})
|
|
3280
|
+
})
|
|
3281
|
+
})
|
|
3282
|
+
})
|
|
3283
|
+
})
|
|
3284
|
+
});
|
|
3285
|
+
}
|
|
3286
|
+
//#endregion
|
|
3287
|
+
//#region src/layout/record-targets.ts
|
|
3288
|
+
function isShowAvailable(registry, resource) {
|
|
3289
|
+
return recordRouteAvailable(registry, resource, "show");
|
|
3290
|
+
}
|
|
3291
|
+
function showTarget(resource, id) {
|
|
3292
|
+
return {
|
|
3293
|
+
resource,
|
|
3294
|
+
action: "show",
|
|
3295
|
+
params: { id }
|
|
3296
|
+
};
|
|
3297
|
+
}
|
|
3298
|
+
function listTarget(resource) {
|
|
3299
|
+
return {
|
|
3300
|
+
resource,
|
|
3301
|
+
action: "list"
|
|
3302
|
+
};
|
|
3303
|
+
}
|
|
3304
|
+
//#endregion
|
|
3305
|
+
//#region src/layout/route-trail.ts
|
|
3306
|
+
function useTrailContext() {
|
|
3307
|
+
const app = useApp();
|
|
3308
|
+
const caps = useI18nCaps();
|
|
3309
|
+
const isVisible = useNavVisible();
|
|
3310
|
+
const t = useT();
|
|
3311
|
+
return {
|
|
3312
|
+
registry: app.registry,
|
|
3313
|
+
caps,
|
|
3314
|
+
isVisible,
|
|
3315
|
+
t
|
|
3316
|
+
};
|
|
3317
|
+
}
|
|
3318
|
+
function groupCrumbs(ctx, chain) {
|
|
3319
|
+
return chain.map((group) => {
|
|
3320
|
+
const key = canonicalNavKey(group);
|
|
3321
|
+
return {
|
|
3322
|
+
label: resolveNavLabel(ctx.caps, `groups.${key}`, declaredText("chrome", group, "label")),
|
|
3323
|
+
href: groupNavHref(ctx.registry, key, ctx.isVisible),
|
|
3324
|
+
jump: true
|
|
3325
|
+
};
|
|
3326
|
+
});
|
|
3327
|
+
}
|
|
3328
|
+
function nonEmpty(crumbs) {
|
|
3329
|
+
return crumbs.length > 0 ? crumbs : void 0;
|
|
3330
|
+
}
|
|
3331
|
+
function recordActionOf(match) {
|
|
3332
|
+
if (match === null || match.kind !== "resource") return void 0;
|
|
3333
|
+
switch (match.action) {
|
|
3334
|
+
case "show":
|
|
3335
|
+
case "edit":
|
|
3336
|
+
case "create": return match.action;
|
|
3337
|
+
default: return;
|
|
3338
|
+
}
|
|
3339
|
+
}
|
|
3340
|
+
function pathOf(href) {
|
|
3341
|
+
const path = href.split(/[?#]/, 1)[0] ?? "";
|
|
3342
|
+
return path.length > 1 && path.endsWith("/") ? path.slice(0, -1) : path;
|
|
3343
|
+
}
|
|
3344
|
+
function originTrail(ctx, origin, depth = ORIGIN_DEPTH) {
|
|
3345
|
+
const match = matchPath(ctx.registry, pathOf(origin.href), { isVisible: ctx.isVisible });
|
|
3346
|
+
if (match === null) return void 0;
|
|
3347
|
+
switch (match.kind) {
|
|
3348
|
+
case "resource": {
|
|
3349
|
+
const key = canonicalNavKey(match);
|
|
3350
|
+
if (match.action === "list") return {
|
|
3351
|
+
leading: groupCrumbs(ctx, match.groupChain),
|
|
3352
|
+
resource: key,
|
|
3353
|
+
resourceHref: origin.href,
|
|
3354
|
+
segments: []
|
|
3355
|
+
};
|
|
3356
|
+
const def = ctx.registry.get(key);
|
|
3357
|
+
const crumb = {
|
|
3358
|
+
label: match.action === "create" ? ctx.t("breadcrumbs.create") : origin.title || (match.id === void 0 ? "" : `#${match.id}`),
|
|
3359
|
+
href: origin.href
|
|
3360
|
+
};
|
|
3361
|
+
const parent = depth > 1 && origin.origin !== void 0 ? originTrail(ctx, origin.origin, depth - 1) : void 0;
|
|
3362
|
+
if (parent !== void 0) return {
|
|
3363
|
+
...parent,
|
|
3364
|
+
segments: [...parent.segments, crumb]
|
|
3365
|
+
};
|
|
3366
|
+
return {
|
|
3367
|
+
leading: groupCrumbs(ctx, match.groupChain),
|
|
3368
|
+
resource: key,
|
|
3369
|
+
...def !== void 0 && hasListPage(def) ? { resourceHref: hrefFor(ctx.registry, key) } : {},
|
|
3370
|
+
segments: [crumb]
|
|
3371
|
+
};
|
|
3372
|
+
}
|
|
3373
|
+
case "custom-page": {
|
|
3374
|
+
const trail = headerTrail(ctx, match);
|
|
3375
|
+
if (trail === null || trail.pageLabel === void 0) return void 0;
|
|
3376
|
+
return {
|
|
3377
|
+
leading: trail.leadingSegments ?? [],
|
|
3378
|
+
segments: [{
|
|
3379
|
+
label: trail.pageLabel,
|
|
3380
|
+
href: origin.href
|
|
3381
|
+
}]
|
|
3382
|
+
};
|
|
3383
|
+
}
|
|
3384
|
+
case "group-index": {
|
|
3385
|
+
const crumbs = groupCrumbs(ctx, match.groupChain);
|
|
3386
|
+
const own = crumbs[crumbs.length - 1];
|
|
3387
|
+
if (own === void 0) return void 0;
|
|
3388
|
+
return {
|
|
3389
|
+
leading: crumbs.slice(0, -1),
|
|
3390
|
+
segments: [{
|
|
3391
|
+
label: own.label,
|
|
3392
|
+
href: origin.href
|
|
3393
|
+
}]
|
|
3394
|
+
};
|
|
3395
|
+
}
|
|
3396
|
+
default: return;
|
|
3397
|
+
}
|
|
3398
|
+
}
|
|
3399
|
+
function recordTrail(ctx, input) {
|
|
3400
|
+
const { resource, action, id, origin } = input;
|
|
3401
|
+
if (origin !== void 0 && (action === "create" || id !== "")) return originRooted(ctx, input, origin);
|
|
3402
|
+
const leadingSegments = nonEmpty(groupCrumbs(ctx, input.groupChain));
|
|
3403
|
+
if (action !== "create" && id === "") return {
|
|
3404
|
+
resource,
|
|
3405
|
+
leadingSegments
|
|
3406
|
+
};
|
|
3407
|
+
const recordLabel = input.title || id;
|
|
3408
|
+
const middleSegments = action === "edit" ? [isShowAvailable(ctx.registry, resource) ? {
|
|
3409
|
+
label: recordLabel,
|
|
3410
|
+
href: resolveRoute(showTarget(resource, id), ctx.registry)
|
|
3411
|
+
} : { label: recordLabel }] : void 0;
|
|
3412
|
+
const pageLabel = action === "create" ? ctx.t("breadcrumbs.create") : action === "edit" ? ctx.t("breadcrumbs.edit") : recordLabel;
|
|
3413
|
+
return {
|
|
3414
|
+
resource,
|
|
3415
|
+
recordId: action === "create" ? void 0 : id,
|
|
3416
|
+
pageLabel,
|
|
3417
|
+
leadingSegments,
|
|
3418
|
+
middleSegments
|
|
3419
|
+
};
|
|
3420
|
+
}
|
|
3421
|
+
function originRooted(ctx, input, origin) {
|
|
3422
|
+
const { resource, action, id } = input;
|
|
3423
|
+
const recordLabel = input.title || id;
|
|
3424
|
+
const drawn = new Set(origin.segments.flatMap((crumb) => crumb.href === void 0 ? [] : [pathOf(crumb.href)]));
|
|
3425
|
+
const own = action === "edit" ? [isShowAvailable(ctx.registry, resource) ? {
|
|
3426
|
+
label: recordLabel,
|
|
3427
|
+
href: resolveRoute(showTarget(resource, id), ctx.registry)
|
|
3428
|
+
} : { label: recordLabel }].filter((crumb) => crumb.href === void 0 || !drawn.has(pathOf(crumb.href))) : [];
|
|
3429
|
+
const pageLabel = action === "create" ? ctx.t("breadcrumbs.create") : action === "edit" ? ctx.t("breadcrumbs.edit") : recordLabel;
|
|
3430
|
+
const recordId = action === "create" ? void 0 : id;
|
|
3431
|
+
const middleSegments = nonEmpty([...origin.segments, ...own]);
|
|
3432
|
+
if (origin.resource !== void 0 && ctx.registry.get(origin.resource) === ctx.registry.get(resource)) return {
|
|
3433
|
+
resource,
|
|
3434
|
+
recordId,
|
|
3435
|
+
pageLabel,
|
|
3436
|
+
leadingSegments: nonEmpty([...origin.leading]),
|
|
3437
|
+
...origin.resourceHref === void 0 ? {} : { resourceHref: origin.resourceHref },
|
|
3438
|
+
middleSegments
|
|
3439
|
+
};
|
|
3440
|
+
const def = origin.resource === void 0 ? void 0 : ctx.registry.get(origin.resource);
|
|
3441
|
+
const resourceCrumb = origin.resource === void 0 ? [] : [{
|
|
3442
|
+
label: resolveResourceLabel(ctx.caps, origin.resource, "plural", def),
|
|
3443
|
+
...origin.resourceHref === void 0 ? {} : { href: origin.resourceHref }
|
|
3444
|
+
}];
|
|
3445
|
+
return {
|
|
3446
|
+
resource,
|
|
3447
|
+
recordId,
|
|
3448
|
+
pageLabel,
|
|
3449
|
+
rootSegments: [
|
|
3450
|
+
...origin.leading,
|
|
3451
|
+
...resourceCrumb,
|
|
3452
|
+
...origin.segments,
|
|
3453
|
+
...own
|
|
3454
|
+
]
|
|
3455
|
+
};
|
|
3456
|
+
}
|
|
3457
|
+
function recordTrailRoute(registry, match) {
|
|
3458
|
+
if (match === null || match.kind !== "resource") return null;
|
|
3459
|
+
const resource = canonicalNavKey(match);
|
|
3460
|
+
const { groupChain } = match;
|
|
3461
|
+
if (match.action === "flow") return {
|
|
3462
|
+
resource,
|
|
3463
|
+
action: "show",
|
|
3464
|
+
id: match.id ?? "",
|
|
3465
|
+
flow: match.flow ?? "",
|
|
3466
|
+
groupChain
|
|
3467
|
+
};
|
|
3468
|
+
const action = recordActionOf(match);
|
|
3469
|
+
if (action === void 0 || !recordRouteAvailable(registry, resource, action)) return null;
|
|
3470
|
+
return {
|
|
3471
|
+
resource,
|
|
3472
|
+
action,
|
|
3473
|
+
id: action === "create" ? "" : match.id ?? "",
|
|
3474
|
+
groupChain
|
|
3475
|
+
};
|
|
3476
|
+
}
|
|
3477
|
+
function headerTrail(ctx, match) {
|
|
3478
|
+
if (match === null) return null;
|
|
3479
|
+
const key = canonicalNavKey(match);
|
|
3480
|
+
switch (match.kind) {
|
|
3481
|
+
case "resource": {
|
|
3482
|
+
const leadingSegments = nonEmpty(groupCrumbs(ctx, match.groupChain));
|
|
3483
|
+
const action = recordActionOf(match);
|
|
3484
|
+
if (recordTrailRoute(ctx.registry, match) !== null) return null;
|
|
3485
|
+
if (action === void 0) return {
|
|
3486
|
+
resource: key,
|
|
3487
|
+
leadingSegments
|
|
3488
|
+
};
|
|
3489
|
+
return {
|
|
3490
|
+
resource: key,
|
|
3491
|
+
recordId: match.id,
|
|
3492
|
+
pageLabel: ctx.t("router.notFoundTitle"),
|
|
3493
|
+
leadingSegments
|
|
3494
|
+
};
|
|
3495
|
+
}
|
|
3496
|
+
case "custom-page": {
|
|
3497
|
+
const page = ctx.registry.customPages.get(match.name);
|
|
3498
|
+
if (page === void 0 || !ctx.isVisible(page)) return null;
|
|
3499
|
+
return {
|
|
3500
|
+
name: key,
|
|
3501
|
+
pageLabel: resolveNavLabel(ctx.caps, `pages.${key}`, declaredText("chrome", page, "label")),
|
|
3502
|
+
leadingSegments: nonEmpty(groupCrumbs(ctx, match.groupChain))
|
|
3503
|
+
};
|
|
3504
|
+
}
|
|
3505
|
+
case "group-index": {
|
|
3506
|
+
const own = match.groupChain[match.groupChain.length - 1];
|
|
3507
|
+
if (own === void 0) return null;
|
|
3508
|
+
const crumbs = groupCrumbs(ctx, match.groupChain);
|
|
3509
|
+
const current = crumbs[crumbs.length - 1];
|
|
3510
|
+
return {
|
|
3511
|
+
name: canonicalNavKey(own),
|
|
3512
|
+
pageLabel: current.label,
|
|
3513
|
+
leadingSegments: nonEmpty(crumbs.slice(0, -1))
|
|
3514
|
+
};
|
|
3515
|
+
}
|
|
3516
|
+
default: return null;
|
|
3517
|
+
}
|
|
3518
|
+
}
|
|
3519
|
+
function recentStorageKey(userId) {
|
|
3520
|
+
return `buro:recent:v2:${userId}`;
|
|
3521
|
+
}
|
|
3522
|
+
function isRecent(value) {
|
|
3523
|
+
if (typeof value !== "object" || value === null) return false;
|
|
3524
|
+
const entry = value;
|
|
3525
|
+
return typeof entry.canonicalRef === "string" && typeof entry.id === "string" && typeof entry.label === "string" && (entry.labelField === void 0 || typeof entry.labelField === "string") && typeof entry.at === "string";
|
|
3526
|
+
}
|
|
3527
|
+
function parse(raw) {
|
|
3528
|
+
if (raw === null) return [];
|
|
3529
|
+
try {
|
|
3530
|
+
const parsed = JSON.parse(raw);
|
|
3531
|
+
if (typeof parsed !== "object" || parsed === null) return [];
|
|
3532
|
+
const envelope = parsed;
|
|
3533
|
+
if (envelope.version !== 2 || !Array.isArray(envelope.items)) return [];
|
|
3534
|
+
return envelope.items.filter(isRecent);
|
|
3535
|
+
} catch {
|
|
3536
|
+
return [];
|
|
3537
|
+
}
|
|
3538
|
+
}
|
|
3539
|
+
function readRecentRecords(store, userId) {
|
|
3540
|
+
return maybeThen(settle(() => store.get(recentStorageKey(userId)), null), parse);
|
|
3541
|
+
}
|
|
3542
|
+
function writeRecentRecord(store, userId, entry) {
|
|
3543
|
+
const key = recentStorageKey(userId);
|
|
3544
|
+
return maybeThen(settle(() => store.get(key), null), (raw) => {
|
|
3545
|
+
const envelope = {
|
|
3546
|
+
version: 2,
|
|
3547
|
+
items: [entry, ...parse(raw).filter((item) => !(item.canonicalRef === entry.canonicalRef && item.id === entry.id))].slice(0, 5)
|
|
3548
|
+
};
|
|
3549
|
+
return settle(() => store.set(key, JSON.stringify(envelope)), void 0);
|
|
3550
|
+
});
|
|
3551
|
+
}
|
|
3552
|
+
function text$1(value) {
|
|
3553
|
+
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
3554
|
+
}
|
|
3555
|
+
const RECENT_LABEL_KEYS = [
|
|
3556
|
+
"alias",
|
|
3557
|
+
"name",
|
|
3558
|
+
"title",
|
|
3559
|
+
"label"
|
|
3560
|
+
];
|
|
3561
|
+
function recentLabel(record, id, isGuarded = () => false) {
|
|
3562
|
+
for (const key of RECENT_LABEL_KEYS) {
|
|
3563
|
+
if (isGuarded(key)) continue;
|
|
3564
|
+
const value = text$1(record[key]);
|
|
3565
|
+
if (value !== void 0) return {
|
|
3566
|
+
label: value,
|
|
3567
|
+
field: key
|
|
3568
|
+
};
|
|
3569
|
+
}
|
|
3570
|
+
return { label: id };
|
|
3571
|
+
}
|
|
3572
|
+
//#endregion
|
|
3573
|
+
//#region src/layout/use-record-visit.ts
|
|
3574
|
+
function useRecordVisit(resource, record, opts) {
|
|
3575
|
+
const userId = useCurrentUserId();
|
|
3576
|
+
const store = usePlatformStorage().local;
|
|
3577
|
+
const app = useApp();
|
|
3578
|
+
const def = app.registry.get(resource);
|
|
3579
|
+
const raw = recordKeyOf(def, record);
|
|
3580
|
+
const id = typeof raw === "string" || typeof raw === "number" ? String(raw) : void 0;
|
|
3581
|
+
const fields = def?.fields;
|
|
3582
|
+
const rules = app.ruleSource?.rules(resource) ?? [];
|
|
3583
|
+
const isGuarded = (field) => fields !== void 0 && Object.hasOwn(fields, field) && fieldSensitivity(fields[field]) !== void 0 || addressesCapability(rules, "view", {
|
|
3584
|
+
kind: "field",
|
|
3585
|
+
name: field
|
|
3586
|
+
});
|
|
3587
|
+
const picked = record != null && id !== void 0 ? recentLabel(record, id, isGuarded) : void 0;
|
|
3588
|
+
const label = picked?.label;
|
|
3589
|
+
const labelField = picked?.field;
|
|
3590
|
+
const canonicalRef = canonicalResourceName(resource);
|
|
3591
|
+
const { isCurrentData } = opts;
|
|
3592
|
+
useEffect(() => {
|
|
3593
|
+
if (userId === null || !isCurrentData || id === void 0 || id === "") return;
|
|
3594
|
+
if (label === void 0) return;
|
|
3595
|
+
writeRecentRecord(store, userId, {
|
|
3596
|
+
canonicalRef,
|
|
3597
|
+
id,
|
|
3598
|
+
label,
|
|
3599
|
+
...labelField === void 0 ? {} : { labelField },
|
|
3600
|
+
at: new Date().toISOString()
|
|
3601
|
+
});
|
|
3602
|
+
}, [
|
|
3603
|
+
store,
|
|
3604
|
+
userId,
|
|
3605
|
+
isCurrentData,
|
|
3606
|
+
canonicalRef,
|
|
3607
|
+
id,
|
|
3608
|
+
label,
|
|
3609
|
+
labelField
|
|
3610
|
+
]);
|
|
3611
|
+
}
|
|
3612
|
+
//#endregion
|
|
3613
|
+
//#region src/layout/record-page.tsx
|
|
3614
|
+
function useMountedRef() {
|
|
3615
|
+
const mounted = useRef(true);
|
|
3616
|
+
useEffect(() => {
|
|
3617
|
+
mounted.current = true;
|
|
3618
|
+
return () => {
|
|
3619
|
+
mounted.current = false;
|
|
3620
|
+
};
|
|
3621
|
+
}, []);
|
|
3622
|
+
return mounted;
|
|
3623
|
+
}
|
|
3624
|
+
function RecordRouteHeader({ resource, action, id, record }) {
|
|
3625
|
+
const recordName = useRecordTitle(resource, action === "create" ? void 0 : id, record);
|
|
3626
|
+
usePublishRecordTrail(action === "create" ? null : {
|
|
3627
|
+
key: recordTrailKey(resource, id, action),
|
|
3628
|
+
title: recordName
|
|
3629
|
+
});
|
|
3630
|
+
return jsx(PageHeader, {
|
|
3631
|
+
resource,
|
|
3632
|
+
mode: action,
|
|
3633
|
+
...action === "create" ? {} : { recordName }
|
|
3634
|
+
});
|
|
3635
|
+
}
|
|
3636
|
+
function RecordShow({ resource, id, recordId, record, shown }) {
|
|
3637
|
+
return jsxs("div", { children: [jsx(RecordRouteHeader, {
|
|
3638
|
+
resource,
|
|
3639
|
+
action: "show",
|
|
3640
|
+
id,
|
|
3641
|
+
record: shown
|
|
3642
|
+
}), jsx(RecordScreen, {
|
|
3643
|
+
resource,
|
|
3644
|
+
mode: "show",
|
|
3645
|
+
recordId,
|
|
3646
|
+
record
|
|
3647
|
+
})] });
|
|
3648
|
+
}
|
|
3649
|
+
function RecordEdit({ resource, id, recordId, record, shown }) {
|
|
3650
|
+
const navigate = useRouteNavigate();
|
|
3651
|
+
const mounted = useMountedRef();
|
|
3652
|
+
const back = useReturnTarget(resource, "edit", id);
|
|
3653
|
+
return jsxs("div", { children: [jsx(RecordRouteHeader, {
|
|
3654
|
+
resource,
|
|
3655
|
+
action: "edit",
|
|
3656
|
+
id,
|
|
3657
|
+
record: shown
|
|
3658
|
+
}), jsx(RecordScreen, {
|
|
3659
|
+
resource,
|
|
3660
|
+
mode: "edit",
|
|
3661
|
+
recordId,
|
|
3662
|
+
record,
|
|
3663
|
+
onCancel: () => navigate(back, { origin: "inherit" }),
|
|
3664
|
+
onSuccess: () => {
|
|
3665
|
+
if (mounted.current) navigate(back, { origin: "inherit" });
|
|
3666
|
+
}
|
|
3667
|
+
})] });
|
|
3668
|
+
}
|
|
3669
|
+
function createdId(result) {
|
|
3670
|
+
const id = result?.id;
|
|
3671
|
+
return typeof id === "string" || typeof id === "number" ? String(id) : void 0;
|
|
3672
|
+
}
|
|
3673
|
+
function RecordCreate({ resource, defaults }) {
|
|
3674
|
+
const app = useApp();
|
|
3675
|
+
const navigate = useRouteNavigate();
|
|
3676
|
+
const mounted = useMountedRef();
|
|
3677
|
+
const adapter = useOptionalRouterAdapter();
|
|
3678
|
+
const list = listTarget(resource);
|
|
3679
|
+
const back = useReturnTarget(resource, "create");
|
|
3680
|
+
const type = adapter?.searchParams.get("type") ?? void 0;
|
|
3681
|
+
return jsxs("div", { children: [jsx(RecordRouteHeader, {
|
|
3682
|
+
resource,
|
|
3683
|
+
action: "create",
|
|
3684
|
+
id: ""
|
|
3685
|
+
}), jsx(RouteScreenClaimContext.Provider, {
|
|
3686
|
+
value: true,
|
|
3687
|
+
children: jsx(RecordScreen, {
|
|
3688
|
+
resource,
|
|
3689
|
+
mode: "create",
|
|
3690
|
+
...type === void 0 ? {} : { type },
|
|
3691
|
+
...defaults === void 0 ? {} : { defaults },
|
|
3692
|
+
onCancel: () => navigate(back, { origin: "inherit" }),
|
|
3693
|
+
onSuccess: (result) => {
|
|
3694
|
+
if (!mounted.current) return;
|
|
3695
|
+
const id = createdId(result);
|
|
3696
|
+
navigate(id !== void 0 && isShowAvailable(app.registry, resource) ? showTarget(resource, id) : list, { origin: "inherit" });
|
|
3697
|
+
}
|
|
3698
|
+
})
|
|
3699
|
+
})] });
|
|
3700
|
+
}
|
|
3701
|
+
function RecordRead({ resource, action, id, recordId }) {
|
|
3702
|
+
const app = useApp();
|
|
3703
|
+
const def = app.registry.get(resource);
|
|
3704
|
+
const projection = useMemo(() => def === void 0 ? void 0 : dtoFields(def, "record"), [def]);
|
|
3705
|
+
const query = useOne(resource, {
|
|
3706
|
+
id: recordId,
|
|
3707
|
+
retry: retryRecordRead,
|
|
3708
|
+
...projection === void 0 ? {} : { projection }
|
|
3709
|
+
});
|
|
3710
|
+
const result = query.data;
|
|
3711
|
+
const read = result?.data;
|
|
3712
|
+
const projected = useProjectedRecord(resource, read, { placeholder: query.isPlaceholderData });
|
|
3713
|
+
const showAvailability = useAvailabilityResolver(resource, "show");
|
|
3714
|
+
const permissions = useCtx((c) => c.permissions);
|
|
3715
|
+
const showRuled = addressesCapability(app.ruleSource.rules(resource), "run", {
|
|
3716
|
+
kind: "action",
|
|
3717
|
+
name: "show"
|
|
3718
|
+
});
|
|
3719
|
+
const showHidden = showRuled && read !== null && typeof read === "object" && showAvailability("show", read).state !== "available";
|
|
3720
|
+
const showSettled = !showRuled || !query.isPlaceholderData && !permissionsUndecided(permissions) && permissions?.status !== "error";
|
|
3721
|
+
useRecordVisit(resource, projected.status === "ready" && showSettled && !showHidden ? projected.value : null, { isCurrentData: !query.isPlaceholderData });
|
|
3722
|
+
const route = useMemo(() => Object.freeze({ id }), [id]);
|
|
3723
|
+
const availability = useAvailabilityResolver(resource, "edit");
|
|
3724
|
+
if (result !== void 0) {
|
|
3725
|
+
const record = result.data;
|
|
3726
|
+
if (record === null || typeof record !== "object") return jsx(RecordState, { state: "not-found" });
|
|
3727
|
+
const loaded = record;
|
|
3728
|
+
if (showRuled) {
|
|
3729
|
+
if (query.isPlaceholderData || permissionsUndecided(permissions)) return jsx(RecordState, { state: "loading" });
|
|
3730
|
+
if (permissions?.status === "error") return jsx(RecordState, { state: "not-found" });
|
|
3731
|
+
if (showHidden) return jsx(RecordState, { state: "not-found" });
|
|
3732
|
+
}
|
|
3733
|
+
if (projected.status === "pending" || projected.value === null) return jsx(RecordState, { state: "loading" });
|
|
3734
|
+
const shown = projected.value;
|
|
3735
|
+
if (action === "edit") {
|
|
3736
|
+
const edit = availability("edit", loaded).state;
|
|
3737
|
+
if (edit === "hidden" || edit === "absent") return jsx(RecordState, { state: "not-found" });
|
|
3738
|
+
if (edit === "undecided") return jsx(RecordState, { state: "loading" });
|
|
3739
|
+
}
|
|
3740
|
+
return jsx(RouteSourceContext.Provider, {
|
|
3741
|
+
value: route,
|
|
3742
|
+
children: jsx(RouteScreenClaimContext.Provider, {
|
|
3743
|
+
value: true,
|
|
3744
|
+
children: action === "edit" ? jsx(RecordEdit, {
|
|
3745
|
+
resource,
|
|
3746
|
+
id,
|
|
3747
|
+
recordId,
|
|
3748
|
+
record: loaded,
|
|
3749
|
+
shown
|
|
3750
|
+
}) : jsx(RecordShow, {
|
|
3751
|
+
resource,
|
|
3752
|
+
id,
|
|
3753
|
+
recordId,
|
|
3754
|
+
record: loaded,
|
|
3755
|
+
shown
|
|
3756
|
+
})
|
|
3757
|
+
})
|
|
3758
|
+
});
|
|
3759
|
+
}
|
|
3760
|
+
if (query.error != null) return jsx(LoadError, {
|
|
3761
|
+
surface: "record",
|
|
3762
|
+
resource,
|
|
3763
|
+
error: query.error,
|
|
3764
|
+
retry: () => void query.refetch()
|
|
1969
3765
|
});
|
|
3766
|
+
return jsx(RecordState, { state: "loading" });
|
|
1970
3767
|
}
|
|
1971
|
-
function
|
|
1972
|
-
|
|
3768
|
+
function RecordRouteBody(props) {
|
|
3769
|
+
if (props.action === "create") return jsx(RecordCreate, { ...props });
|
|
3770
|
+
if (props.id === "") return jsx(RecordState, { state: "not-found" });
|
|
3771
|
+
return jsx(RecordReadRoute, { ...props });
|
|
3772
|
+
}
|
|
3773
|
+
function RecordReadRoute(props) {
|
|
3774
|
+
const recordId = useRouteRecordId(props.resource, props.id);
|
|
3775
|
+
if (recordId === void 0) return jsx(InvalidRouteId, { resource: props.resource });
|
|
3776
|
+
return jsx(RecordRead, {
|
|
3777
|
+
...props,
|
|
3778
|
+
recordId
|
|
3779
|
+
});
|
|
3780
|
+
}
|
|
3781
|
+
function RecordRoute(props) {
|
|
3782
|
+
const missingRuntime = useOptionalDataRuntime() === null;
|
|
3783
|
+
useEffect(() => {
|
|
3784
|
+
if (!missingRuntime || process.env.NODE_ENV === "production") return;
|
|
3785
|
+
console.error(`[burojs] The '${props.resource}' record page has no data runtime to read or write with: BuroProvider.dataRuntime is not set. Pass <BuroProvider dataRuntime={…}> (createDataRuntime from @burojs/react); until then record routes render the error state.`);
|
|
3786
|
+
}, [missingRuntime, props.resource]);
|
|
3787
|
+
if (missingRuntime) return jsx(RecordState, { state: "error" });
|
|
3788
|
+
return jsx(RecordRouteBody, { ...props }, `${props.resource}:${props.id}`);
|
|
3789
|
+
}
|
|
3790
|
+
function recordPageProblem(input) {
|
|
3791
|
+
const { props, resource, action } = input;
|
|
3792
|
+
if (resource === void 0 || action === void 0) return "[burojs] <RecordPage> needs both `resource` and `action`. Pass them; until then it renders not-found.";
|
|
3793
|
+
if (!input.onRecordRoute) return `[burojs] <RecordPage resource="${resource}" action="${action}"> is not on a record route (show, edit or create), so it has no record to read: render it on one, under a RouterAdapterProvider. Until then it renders not-found.`;
|
|
3794
|
+
if (props.defaults !== void 0 && action !== "create") return `[burojs] <RecordPage defaults> applies to create only; on this '${action}' route it is ignored.`;
|
|
3795
|
+
return null;
|
|
3796
|
+
}
|
|
3797
|
+
const recordPageProblemsLogged = new WeakMap();
|
|
3798
|
+
function RecordPage(props) {
|
|
1973
3799
|
const app = useApp();
|
|
1974
|
-
const
|
|
1975
|
-
const
|
|
1976
|
-
const
|
|
1977
|
-
const
|
|
1978
|
-
const
|
|
1979
|
-
const
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
const page = iconForCustomPage ? app.registry.customPages.get(entry.key) : void 0;
|
|
1992
|
-
return ownOr(page ? iconForCustomPage?.(page) : void 0, entry.icon);
|
|
3800
|
+
const match = useMatch();
|
|
3801
|
+
const id = match === null ? null : match.id ?? "";
|
|
3802
|
+
const { action } = props;
|
|
3803
|
+
const resource = props.resource === void 0 ? void 0 : routeResourceKey(props.resource);
|
|
3804
|
+
const onRecordRoute = recordActionOf(match) !== void 0;
|
|
3805
|
+
const problem = recordPageProblem({
|
|
3806
|
+
props,
|
|
3807
|
+
resource,
|
|
3808
|
+
action,
|
|
3809
|
+
onRecordRoute
|
|
3810
|
+
});
|
|
3811
|
+
useEffect(() => {
|
|
3812
|
+
if (problem === null || process.env.NODE_ENV === "production") return;
|
|
3813
|
+
let logged = recordPageProblemsLogged.get(app);
|
|
3814
|
+
if (logged === void 0) {
|
|
3815
|
+
logged = new Set();
|
|
3816
|
+
recordPageProblemsLogged.set(app, logged);
|
|
1993
3817
|
}
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
"aria-current": entry.active ? "page" : void 0,
|
|
2007
|
-
"data-testid": entryTestId(entry),
|
|
2008
|
-
onClick: jump(entry),
|
|
2009
|
-
children: [icon != null && jsx("span", {
|
|
2010
|
-
className: "inline-flex h-4 w-4 items-center justify-center",
|
|
2011
|
-
"aria-hidden": "true",
|
|
2012
|
-
children: icon
|
|
2013
|
-
}), jsx("span", { children: entry.label })]
|
|
2014
|
-
})
|
|
2015
|
-
}) }, entry.key);
|
|
3818
|
+
if (logged.has(problem)) return;
|
|
3819
|
+
logged.add(problem);
|
|
3820
|
+
console.error(problem);
|
|
3821
|
+
}, [app, problem]);
|
|
3822
|
+
if (resource === void 0 || action === void 0 || id === null || !onRecordRoute) return jsx(RecordState, { state: "not-found" });
|
|
3823
|
+
const route = action === "create" ? {
|
|
3824
|
+
resource,
|
|
3825
|
+
action,
|
|
3826
|
+
defaults: props.defaults
|
|
3827
|
+
} : {
|
|
3828
|
+
resource,
|
|
3829
|
+
action
|
|
2016
3830
|
};
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
href: entry.href ?? "",
|
|
2022
|
-
"aria-current": entry.active ? "page" : void 0,
|
|
2023
|
-
"data-testid": entryTestId(entry),
|
|
2024
|
-
onClick: jump(entry),
|
|
2025
|
-
children: jsx("span", { children: entry.label })
|
|
2026
|
-
})
|
|
2027
|
-
}) }, entry.key);
|
|
2028
|
-
const [top, ...blocks] = tree;
|
|
2029
|
-
return jsxs(Fragment$1, { children: [top !== void 0 && top.entries.length > 0 && jsxs(SidebarGroup, { children: [topLabel !== null && jsx(SidebarGroupLabel, { children: topLabel ?? t("nav.resources") }), jsx(SidebarGroupContent, { children: jsx(SidebarMenu, { children: top.entries.map(menuItem) }) })] }), blocks.map((block) => {
|
|
2030
|
-
const key = block.key ?? "";
|
|
2031
|
-
if (block.label === null) return jsx(SidebarGroup, { children: jsx(SidebarGroupContent, { children: jsx(SidebarMenu, { children: block.entries.map(menuItem) }) }) }, key);
|
|
2032
|
-
if (block.collapsible) return jsx(CollapsibleNavSection, {
|
|
2033
|
-
name: key,
|
|
2034
|
-
label: block.label,
|
|
2035
|
-
icon: sectionIcon(key, block.icon),
|
|
2036
|
-
hasActiveChild: block.active,
|
|
2037
|
-
declaredOpen: block.defaultOpen,
|
|
2038
|
-
children: block.entries.map(subItem)
|
|
2039
|
-
}, key);
|
|
2040
|
-
return jsxs(SidebarGroup, {
|
|
2041
|
-
"data-testid": `sidebar-section-group:${key}`,
|
|
2042
|
-
children: [jsx(SidebarGroupLabel, {
|
|
2043
|
-
"data-testid": `sidebar-section:${key}`,
|
|
2044
|
-
children: block.label
|
|
2045
|
-
}), jsx(SidebarGroupContent, { children: jsx(SidebarMenu, { children: block.entries.map(menuItem) }) })]
|
|
2046
|
-
}, key);
|
|
2047
|
-
})] });
|
|
3831
|
+
return jsx(RecordRoute, {
|
|
3832
|
+
...route,
|
|
3833
|
+
id
|
|
3834
|
+
});
|
|
2048
3835
|
}
|
|
2049
3836
|
//#endregion
|
|
2050
|
-
//#region src/layout/
|
|
2051
|
-
function
|
|
2052
|
-
const
|
|
2053
|
-
const
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
3837
|
+
//#region src/layout/collection-record-route.tsx
|
|
3838
|
+
function addressTail(searchParams, routerHash) {
|
|
3839
|
+
const query = searchParams?.toString() ?? "";
|
|
3840
|
+
const hash = routerHash ?? (typeof window === "undefined" ? "" : window.location.hash);
|
|
3841
|
+
return `${query === "" ? "" : `?${query}`}${hash === "#" ? "" : hash}`;
|
|
3842
|
+
}
|
|
3843
|
+
function CollectionRecordRoute(props) {
|
|
3844
|
+
const { collection, action, id } = props;
|
|
3845
|
+
const app = useApp();
|
|
3846
|
+
const navigate = useRouteNavigate();
|
|
3847
|
+
const adapter = useOptionalRouterAdapter();
|
|
3848
|
+
const state = useCollectionMember(collection, id);
|
|
3849
|
+
const flow = props.action === "flow" ? props.flow : void 0;
|
|
3850
|
+
const target = state.status === "member" ? hrefFor(app.registry, state.member, {
|
|
3851
|
+
action,
|
|
3852
|
+
id,
|
|
3853
|
+
...flow === void 0 ? {} : { flow }
|
|
3854
|
+
}) : void 0;
|
|
3855
|
+
const current = useRef(adapter);
|
|
3856
|
+
current.current = adapter;
|
|
3857
|
+
const sent = useRef(void 0);
|
|
3858
|
+
useEffect(() => {
|
|
3859
|
+
if (target === void 0 || sent.current === target) return;
|
|
3860
|
+
sent.current = target;
|
|
3861
|
+
navigate(`${target}${addressTail(current.current?.searchParams, current.current?.hash)}`, { replace: true });
|
|
3862
|
+
}, [target, navigate]);
|
|
3863
|
+
if (state.status === "unknown") return jsx(UnknownMemberCard, {
|
|
3864
|
+
collection,
|
|
3865
|
+
id,
|
|
3866
|
+
value: state.value
|
|
3867
|
+
});
|
|
3868
|
+
if (state.status === "not-found") return jsx(NavAreaNotFound, {});
|
|
3869
|
+
if (state.status === "error") return jsx(LoadError, {
|
|
3870
|
+
surface: "record",
|
|
3871
|
+
resource: routeResourceKey(collection),
|
|
3872
|
+
error: state.error
|
|
3873
|
+
});
|
|
3874
|
+
return null;
|
|
3875
|
+
}
|
|
3876
|
+
function UnknownMemberCard({ collection, id, value }) {
|
|
3877
|
+
const app = useApp();
|
|
3878
|
+
const t = useOptionalT();
|
|
3879
|
+
const key = routeResourceKey(collection);
|
|
3880
|
+
const fact = memberFactOf(app.registry.get(key));
|
|
3881
|
+
const shown = value === void 0 || value === null ? "" : String(value);
|
|
3882
|
+
const known = (fact?.collection?.members ?? []).flatMap((member) => member.values).join(", ");
|
|
3883
|
+
return jsxs("div", {
|
|
3884
|
+
"data-testid": `unknown-member-card:${key}:${id}`,
|
|
3885
|
+
className: "flex flex-col gap-2",
|
|
2058
3886
|
children: [
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
3887
|
+
process.env.NODE_ENV === "production" ? null : jsx("div", {
|
|
3888
|
+
role: "note",
|
|
3889
|
+
"data-testid": "unknown-member-notice",
|
|
3890
|
+
className: "mx-6 mt-4 rounded-md border border-dashed border-amber-500/60 bg-amber-500/10 px-3 py-2 text-sm",
|
|
3891
|
+
children: t("collection.unknownMemberNotice", {
|
|
3892
|
+
params: {
|
|
3893
|
+
base: fact === void 0 ? key : formatEntityRef(fact.ref),
|
|
3894
|
+
value: shown,
|
|
3895
|
+
known
|
|
3896
|
+
},
|
|
3897
|
+
defaultValue: "No member of {{base}} claims the type “{{value}}”. Known types: {{known}}."
|
|
3898
|
+
})
|
|
2062
3899
|
}),
|
|
2063
|
-
jsx(
|
|
2064
|
-
|
|
2065
|
-
className: "
|
|
2066
|
-
children:
|
|
3900
|
+
jsx("p", {
|
|
3901
|
+
"data-testid": "unknown-member-type",
|
|
3902
|
+
className: "mx-6 text-sm text-muted-foreground",
|
|
3903
|
+
children: t("collection.unknownType", {
|
|
3904
|
+
params: { value: shown },
|
|
3905
|
+
defaultValue: "Unknown type: {{value}}"
|
|
3906
|
+
})
|
|
2067
3907
|
}),
|
|
2068
|
-
jsx(
|
|
3908
|
+
jsx(ItemActionsOffContext.Provider, {
|
|
3909
|
+
value: true,
|
|
3910
|
+
children: jsx(RecordRoute, {
|
|
3911
|
+
resource: key,
|
|
3912
|
+
action: "show",
|
|
3913
|
+
id
|
|
3914
|
+
})
|
|
3915
|
+
})
|
|
2069
3916
|
]
|
|
2070
3917
|
});
|
|
2071
3918
|
}
|
|
2072
3919
|
//#endregion
|
|
2073
|
-
//#region src/layout/
|
|
2074
|
-
|
|
2075
|
-
"show",
|
|
2076
|
-
"edit",
|
|
2077
|
-
"create"
|
|
2078
|
-
]);
|
|
2079
|
-
function NavChrome({ children, entryFilter, icons }) {
|
|
2080
|
-
const app = useApp();
|
|
2081
|
-
const caps = useI18nCaps();
|
|
3920
|
+
//#region src/layout/page-host.tsx
|
|
3921
|
+
function PageHost({ page, match, notFound }) {
|
|
2082
3922
|
const isVisible = useNavVisible();
|
|
2083
|
-
const
|
|
2084
|
-
const
|
|
2085
|
-
|
|
2086
|
-
if (
|
|
2087
|
-
|
|
2088
|
-
|
|
3923
|
+
const ctx = useVisibilityCtx();
|
|
3924
|
+
const access = usePageAccess(page);
|
|
3925
|
+
if (!(evaluateVisible(page.visible, ctx) && match.groupChain.every((group) => isVisible(group)))) return notFound;
|
|
3926
|
+
if (access === "pending") return jsx(RecordState, { state: "loading" });
|
|
3927
|
+
if (access === "denied") return notFound;
|
|
3928
|
+
return jsx(PageParams, {
|
|
3929
|
+
page,
|
|
3930
|
+
notFound
|
|
3931
|
+
});
|
|
3932
|
+
}
|
|
3933
|
+
const NO_SEARCH = new URLSearchParams();
|
|
3934
|
+
function PageParams({ page, notFound }) {
|
|
3935
|
+
const search = useOptionalRouterAdapter()?.searchParams ?? (typeof window === "undefined" ? NO_SEARCH : new URLSearchParams(window.location.search));
|
|
3936
|
+
const raw = JSON.stringify(pageParamNames(page.params).map((name) => search.get(name)));
|
|
3937
|
+
const parsed = useMemo(() => parsePageParams(page, search), [page, raw]);
|
|
3938
|
+
if (!parsed.ok) return notFound;
|
|
3939
|
+
return jsx(RouteSourceContext.Provider, {
|
|
3940
|
+
value: parsed.route,
|
|
3941
|
+
children: jsx(PageBody, { page })
|
|
2089
3942
|
});
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
children:
|
|
2112
|
-
})
|
|
3943
|
+
}
|
|
3944
|
+
function PageBody({ page }) {
|
|
3945
|
+
const snapshot = useApplicationSnapshot();
|
|
3946
|
+
const compiled = useMemo(() => compilePageScreen(page, {
|
|
3947
|
+
blocks: snapshotBlockLookup(snapshot),
|
|
3948
|
+
frames: snapshotFrameLookup(snapshot),
|
|
3949
|
+
appFrames: snapshotAppFrames(snapshot)
|
|
3950
|
+
}), [page, snapshot]);
|
|
3951
|
+
const failure = isScreenFailure(compiled) ? compiled : null;
|
|
3952
|
+
const screen = failure === null && !isScreenFailure(compiled) ? compiled : null;
|
|
3953
|
+
const caps = useI18nCaps();
|
|
3954
|
+
const name = canonicalNavKey(page);
|
|
3955
|
+
const label = resolveNavLabel(caps, `pages.${name}`, declaredText("chrome", page, "label"));
|
|
3956
|
+
useEffect(() => {
|
|
3957
|
+
if (failure === null || process.env.NODE_ENV === "production") return;
|
|
3958
|
+
console.error(`[burojs] The page "${name}" cannot render its body: ${failure.code} at ${failure.path.join(".")}: ${failure.message}.`);
|
|
3959
|
+
}, [failure, name]);
|
|
3960
|
+
return jsxs("div", {
|
|
3961
|
+
className: "flex flex-col gap-4",
|
|
3962
|
+
children: [jsx("h1", {
|
|
3963
|
+
className: "text-2xl font-semibold tracking-tight",
|
|
3964
|
+
children: label
|
|
3965
|
+
}), screen === null ? jsx(RecordState, { state: "error" }) : jsx(PageRouteScreen, { children: jsx(PlannedBody, {
|
|
3966
|
+
screen,
|
|
3967
|
+
name,
|
|
3968
|
+
label
|
|
3969
|
+
}) })]
|
|
3970
|
+
});
|
|
3971
|
+
}
|
|
3972
|
+
function PageRouteScreen({ children }) {
|
|
3973
|
+
const tabs = useTabsUrlState("show", true);
|
|
3974
|
+
return jsx(RouteScreenClaimContext.Provider, {
|
|
3975
|
+
value: false,
|
|
3976
|
+
children: jsx(RouteScreenContext.Provider, {
|
|
3977
|
+
value: true,
|
|
3978
|
+
children: jsx(ScreenStateProvider, {
|
|
3979
|
+
open: tabs.open,
|
|
3980
|
+
onOpenChange: tabs.onOpenChange,
|
|
3981
|
+
children
|
|
3982
|
+
})
|
|
3983
|
+
})
|
|
3984
|
+
});
|
|
3985
|
+
}
|
|
3986
|
+
function PlannedBody({ screen, name, label, record }) {
|
|
3987
|
+
const pageOptions = useMemo(() => record === void 0 ? {} : { record }, [record]);
|
|
3988
|
+
const page = usePageScreen(screen, pageOptions);
|
|
3989
|
+
const { PlacementScope } = page;
|
|
3990
|
+
const route = useContext(RouteSourceContext);
|
|
3991
|
+
const catalog = useFrameImplementations();
|
|
3992
|
+
const id = formatEntityRef(screen.frame);
|
|
3993
|
+
const resolved = useMemo(() => resolveFrameView(screen.frame, catalog, "page"), [screen.frame, catalog]);
|
|
3994
|
+
const t = useFrameT(id);
|
|
3995
|
+
const slots = {};
|
|
3996
|
+
for (const slot of Object.keys(screen.slots)) slots[slot] = (page.plan.slots[slot] ?? []).every((node) => node.state === "absent") ? null : jsx(ScreenTree, {
|
|
3997
|
+
region: slot === "body" ? "page-body" : "record",
|
|
3998
|
+
slot,
|
|
3999
|
+
resource: name,
|
|
4000
|
+
mode: "show",
|
|
4001
|
+
compiled: screen,
|
|
4002
|
+
page
|
|
4003
|
+
}, slot);
|
|
4004
|
+
const View = resolved.view;
|
|
4005
|
+
const viewProps = {
|
|
4006
|
+
slots,
|
|
4007
|
+
label,
|
|
4008
|
+
params: route ?? {},
|
|
4009
|
+
t
|
|
4010
|
+
};
|
|
4011
|
+
return jsx(RouteSourceContext.Provider, {
|
|
4012
|
+
value: null,
|
|
4013
|
+
children: jsx(ActionRouteContext.Provider, {
|
|
4014
|
+
value: route,
|
|
4015
|
+
children: jsx(PlacementScope, { children: jsx(DeepLinkScope, {
|
|
4016
|
+
plan: page.plan,
|
|
4017
|
+
enabled: true,
|
|
4018
|
+
children: jsx("div", {
|
|
4019
|
+
"data-buro-region": "page-body",
|
|
4020
|
+
children: jsx(Suspense, {
|
|
4021
|
+
fallback: null,
|
|
4022
|
+
children: jsx(View, { ...viewProps })
|
|
4023
|
+
})
|
|
4024
|
+
})
|
|
4025
|
+
}) })
|
|
4026
|
+
})
|
|
4027
|
+
});
|
|
4028
|
+
}
|
|
4029
|
+
//#endregion
|
|
4030
|
+
//#region src/layout/resource-page-router.tsx
|
|
4031
|
+
function ResourcePageRouterEmpty() {
|
|
4032
|
+
const t = useT();
|
|
4033
|
+
return jsxs("div", {
|
|
4034
|
+
"data-testid": "resource-page-router-empty",
|
|
4035
|
+
className: "flex flex-col items-center justify-center gap-1 px-6 py-24 text-center",
|
|
4036
|
+
children: [jsx("h2", {
|
|
4037
|
+
className: "text-base font-semibold tracking-tight",
|
|
4038
|
+
children: t("router.emptyTitle")
|
|
4039
|
+
}), jsx("p", {
|
|
4040
|
+
className: "max-w-sm text-sm text-muted-foreground",
|
|
4041
|
+
children: t("router.emptyBody")
|
|
4042
|
+
})]
|
|
4043
|
+
});
|
|
4044
|
+
}
|
|
4045
|
+
function ResourcePageRouter({ match }) {
|
|
4046
|
+
const app = useApp();
|
|
4047
|
+
const qualifiedMatch = match;
|
|
4048
|
+
const resource = canonicalNavKey(qualifiedMatch);
|
|
4049
|
+
const target = canonicalNavKey({
|
|
4050
|
+
name: match.target ?? match.name,
|
|
4051
|
+
ref: qualifiedMatch.targetRef
|
|
4052
|
+
});
|
|
4053
|
+
const recordAction = recordActionOf(match);
|
|
4054
|
+
switch (match.kind) {
|
|
4055
|
+
case "resource": {
|
|
4056
|
+
if (match.action === "list") {
|
|
4057
|
+
const def = app.registry.get(resource);
|
|
4058
|
+
return def !== void 0 && !hasListPage(def) ? jsx(NavAreaNotFound, {}) : jsx(ResourceListPage, { resource });
|
|
4059
|
+
}
|
|
4060
|
+
const collection = collectionFactOf(app.registry.get(resource)) !== void 0;
|
|
4061
|
+
if (collection && match.id !== void 0 && (match.action === "show" || match.action === "edit" || match.action === "flow")) {
|
|
4062
|
+
if (!recordRouteAvailable(app.registry, resource, "show")) return jsx(NavAreaNotFound, {});
|
|
4063
|
+
return match.action === "flow" ? jsx(CollectionRecordRoute, {
|
|
4064
|
+
collection: resource,
|
|
4065
|
+
action: "flow",
|
|
4066
|
+
id: match.id,
|
|
4067
|
+
flow: match.flow ?? ""
|
|
4068
|
+
}) : jsx(CollectionRecordRoute, {
|
|
4069
|
+
collection: resource,
|
|
4070
|
+
action: match.action,
|
|
4071
|
+
id: match.id
|
|
4072
|
+
});
|
|
4073
|
+
}
|
|
4074
|
+
if (match.action === "flow") return jsx(PageFlowRouteLoader, { match });
|
|
4075
|
+
if (recordAction === void 0) return jsx(ResourcePageRouterEmpty, {});
|
|
4076
|
+
if (!recordRouteAvailable(app.registry, resource, recordAction)) return jsx(NavAreaNotFound, {});
|
|
4077
|
+
if (collection && recordAction === "create") return jsx(CollectionCreatePicker, { collection: resource });
|
|
4078
|
+
return jsx(RecordRoute, {
|
|
4079
|
+
resource,
|
|
4080
|
+
action: recordAction,
|
|
4081
|
+
id: match.id ?? ""
|
|
4082
|
+
});
|
|
4083
|
+
}
|
|
4084
|
+
case "custom-page": {
|
|
4085
|
+
const page = app.registry.customPages.get(resource);
|
|
4086
|
+
if (page === void 0) return jsx(ResourcePageRouterEmpty, {});
|
|
4087
|
+
if (!page.body?.length) return jsx(NavAreaNotFound, {});
|
|
4088
|
+
return jsx(PageHost, {
|
|
4089
|
+
page,
|
|
4090
|
+
match,
|
|
4091
|
+
notFound: jsx(NavAreaNotFound, {})
|
|
4092
|
+
}, resource);
|
|
4093
|
+
}
|
|
4094
|
+
case "group-index": {
|
|
4095
|
+
const indexPage = app.registry.customPages.get(target);
|
|
4096
|
+
if (indexPage?.body?.length) return jsx(PageHost, {
|
|
4097
|
+
page: indexPage,
|
|
4098
|
+
match,
|
|
4099
|
+
notFound: jsx(NavAreaNotFound, {})
|
|
4100
|
+
}, target);
|
|
4101
|
+
const targetDef = app.registry.get(target);
|
|
4102
|
+
if (targetDef !== void 0) return hasListPage(targetDef) ? jsx(ResourceListPage, { resource: target }) : jsx(NavAreaNotFound, {});
|
|
4103
|
+
return jsx(ResourcePageRouterEmpty, {});
|
|
4104
|
+
}
|
|
4105
|
+
default: return jsx(ResourcePageRouterEmpty, {});
|
|
2113
4106
|
}
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
4107
|
+
}
|
|
4108
|
+
function NavAreaPageSkeleton() {
|
|
4109
|
+
return jsxs("div", {
|
|
4110
|
+
className: "space-y-3 p-6",
|
|
4111
|
+
role: "status",
|
|
4112
|
+
"aria-busy": "true",
|
|
4113
|
+
"aria-live": "polite",
|
|
4114
|
+
"data-testid": "nav-area-skeleton",
|
|
4115
|
+
children: [
|
|
4116
|
+
jsx("span", {
|
|
4117
|
+
className: "sr-only",
|
|
4118
|
+
children: "Loading…"
|
|
4119
|
+
}),
|
|
4120
|
+
jsx("div", { className: "h-6 w-48 animate-pulse rounded bg-muted/60" }),
|
|
4121
|
+
jsx("div", { className: "h-4 w-full animate-pulse rounded bg-muted/40" }),
|
|
4122
|
+
jsx("div", { className: "h-4 w-5/6 animate-pulse rounded bg-muted/40" }),
|
|
4123
|
+
jsx("div", { className: "h-4 w-2/3 animate-pulse rounded bg-muted/40" })
|
|
4124
|
+
]
|
|
4125
|
+
});
|
|
4126
|
+
}
|
|
4127
|
+
const adapterNoticeGiven = new WeakSet();
|
|
4128
|
+
const chromeNoticeGiven = new WeakSet();
|
|
4129
|
+
function NavAreaPage() {
|
|
4130
|
+
const match = useMatch();
|
|
4131
|
+
const app = useApp();
|
|
4132
|
+
const noAdapter = useOptionalRouterAdapter() === null;
|
|
4133
|
+
const chromeMounted = useNavChromeMounted();
|
|
4134
|
+
const resolveLayout = useLayoutResolver();
|
|
4135
|
+
const chromeGroup = match?.groupChain.find((g) => resolveLayout(g.layout)) ?? null;
|
|
4136
|
+
useEffect(() => {
|
|
4137
|
+
if (process.env.NODE_ENV === "production") return;
|
|
4138
|
+
if (chromeMounted || !chromeGroup || chromeNoticeGiven.has(app)) return;
|
|
4139
|
+
chromeNoticeGiven.add(app);
|
|
4140
|
+
console.error(`[burojs] <NavAreaPage> is inside the nav group "${canonicalNavKey(chromeGroup)}" whose layout "${chromeGroup.layout}" draws area chrome, and no <NavChrome> is mounted above it. <AppShell> mounts one by default; with your own frame, wrap your routes in <NavChrome>.`);
|
|
4141
|
+
}, [
|
|
4142
|
+
app,
|
|
4143
|
+
chromeMounted,
|
|
4144
|
+
chromeGroup
|
|
4145
|
+
]);
|
|
4146
|
+
useEffect(() => {
|
|
4147
|
+
if (!noAdapter || process.env.NODE_ENV === "production") return;
|
|
4148
|
+
if (adapterNoticeGiven.has(app)) return;
|
|
4149
|
+
adapterNoticeGiven.add(app);
|
|
4150
|
+
console.error("[burojs] <NavAreaPage> has no location to match: no RouterAdapterProvider is mounted. Mount your router’s adapter (RouterAdapterProvider from @burojs/react) above <BuroProvider>; until then every route renders not-found.");
|
|
4151
|
+
}, [app, noAdapter]);
|
|
4152
|
+
return jsx(Suspense, {
|
|
4153
|
+
fallback: jsx(NavAreaPageSkeleton, {}),
|
|
4154
|
+
children: match ? jsx(ResourcePageRouter, { match }) : jsx(NavAreaNotFound, {})
|
|
2117
4155
|
});
|
|
2118
4156
|
}
|
|
2119
4157
|
//#endregion
|
|
@@ -2252,9 +4290,12 @@ function Breadcrumbs(input) {
|
|
|
2252
4290
|
//#region src/layout/record-breadcrumbs.tsx
|
|
2253
4291
|
function viewOrigin(ctx, trail, resource) {
|
|
2254
4292
|
if (trail === void 0 || trail.resource === void 0 || trail.segments.length > 0) return void 0;
|
|
2255
|
-
const
|
|
2256
|
-
|
|
2257
|
-
return trail.resource;
|
|
4293
|
+
const def = ctx.registry.get(trail.resource);
|
|
4294
|
+
const target = def?.routesTo?.show;
|
|
4295
|
+
if (target !== void 0 && ctx.registry.get(target) === ctx.registry.get(resource)) return trail.resource;
|
|
4296
|
+
const collected = collectionFactOf(def) === void 0 ? void 0 : memberFactOf(def)?.ref;
|
|
4297
|
+
const memberBase = memberFactOf(ctx.registry.get(resource))?.ref;
|
|
4298
|
+
if (collected !== void 0 && memberBase !== void 0 && sameEntityRef(collected, memberBase)) return trail.resource;
|
|
2258
4299
|
}
|
|
2259
4300
|
function RecordBreadcrumbs() {
|
|
2260
4301
|
const ctx = useTrailContext();
|
|
@@ -2638,11 +4679,11 @@ function CommandPaletteNavigationGroups({ onSelect, getTestId }) {
|
|
|
2638
4679
|
const allResources = app.registry.list();
|
|
2639
4680
|
const allPages = app.registry.customPages.list();
|
|
2640
4681
|
const allSections = app.registry.sections.list();
|
|
2641
|
-
const tlResources = allResources.filter((r) =>
|
|
4682
|
+
const tlResources = allResources.filter((r) => hasMenuEntry(r)).filter(isResourceVisible).filter((r) => !r.section && !r.group);
|
|
2642
4683
|
const tlPages = allPages.filter((p) => !p.section && !p.group).filter(isPageVisible);
|
|
2643
4684
|
const groups = [];
|
|
2644
4685
|
for (const section of allSections) {
|
|
2645
|
-
const children = app.registry.sections.childrenOf(canonicalNavKey(section)).filter((c) => !c.group).filter((c) => !("parent" in c) ||
|
|
4686
|
+
const children = app.registry.sections.childrenOf(canonicalNavKey(section)).filter((c) => !c.group).filter((c) => !("parent" in c) || hasMenuEntry(c)).filter((c) => isCustomPageDef(c) ? isPageVisible(c) : isResourceVisible(c));
|
|
2646
4687
|
if (children.length === 0) continue;
|
|
2647
4688
|
groups.push({
|
|
2648
4689
|
section,
|
|
@@ -2729,10 +4770,10 @@ function CommandPaletteNavigationGroups({ onSelect, getTestId }) {
|
|
|
2729
4770
|
}
|
|
2730
4771
|
const collectGroupItems = (group, idPath, depth) => {
|
|
2731
4772
|
const items = [];
|
|
2732
|
-
|
|
4773
|
+
const href = !!resolveLayout(group.layout) ? groupNavHref(app.registry, canonicalNavKey(group), isVisible) : void 0;
|
|
4774
|
+
if (href !== void 0) {
|
|
2733
4775
|
const groupKey = canonicalNavKey(group);
|
|
2734
4776
|
const label = resolveNavLabel(caps, `groups.${groupKey}`, declaredText("chrome", group, "label"));
|
|
2735
|
-
const href = navHrefFor(app.registry, groupKey, { isVisible });
|
|
2736
4777
|
items.push(jsx(CommandItem, {
|
|
2737
4778
|
value: `group:${idPath}:${label}`,
|
|
2738
4779
|
onSelect: () => goTo(href),
|
|
@@ -2751,6 +4792,7 @@ function CommandPaletteNavigationGroups({ onSelect, getTestId }) {
|
|
|
2751
4792
|
}
|
|
2752
4793
|
if (kind === "custom-page") {
|
|
2753
4794
|
const page = child;
|
|
4795
|
+
if (!isPageVisible(page)) continue;
|
|
2754
4796
|
const pageKey = canonicalNavKey(page);
|
|
2755
4797
|
const label = pageLabel(page);
|
|
2756
4798
|
const href = page.path;
|
|
@@ -2990,7 +5032,7 @@ function PerResourceSearchHits({ def, displayField, query, pageSize, goTo, recor
|
|
|
2990
5032
|
const display = titleVisible ? row[displayField] : void 0;
|
|
2991
5033
|
const labelText = typeof display === "string" && display.length > 0 ? display : id;
|
|
2992
5034
|
const subtitle = rowSubtitle(row) ?? null;
|
|
2993
|
-
const href = recordHref ? recordHref(resource, id) : isShowAvailable(registry, resource) ? resourceShowHref(registry, def, resource, id) : null;
|
|
5035
|
+
const href = recordHref ? recordHref(resource, id) : isShowAvailable(registry, resource) ? resourceShowHref(registry, def, resource, id, row) : null;
|
|
2994
5036
|
return jsxs(CommandItem, {
|
|
2995
5037
|
value: `record:${resource}:${id}:${labelText}`,
|
|
2996
5038
|
forceMount: true,
|
|
@@ -3069,7 +5111,7 @@ function CommandPaletteActionsGroup({ actions, onSelect, getTestId, heading }) {
|
|
|
3069
5111
|
const propActions = actions ?? EMPTY_ACTIONS;
|
|
3070
5112
|
const resolvedHeading = heading ?? t("commandPalette.actions", { defaultValue: "Actions" });
|
|
3071
5113
|
const { items, collidingIds } = useMemo(() => {
|
|
3072
|
-
return mergeActions(registryEntries.map((entry) => appActionToItem(entry, present, caps)), propActions);
|
|
5114
|
+
return mergeActions(registryEntries.filter((entry) => entry.undecided !== true).map((entry) => appActionToItem(entry, present, caps)), propActions);
|
|
3073
5115
|
}, [
|
|
3074
5116
|
registryEntries,
|
|
3075
5117
|
propActions,
|
|
@@ -3146,7 +5188,7 @@ function CommandPaletteCreateGroup({ resourceFilter, onSelect, getTestId, headin
|
|
|
3146
5188
|
const resolvedHeading = heading ?? t("commandPalette.create", { defaultValue: "Create" });
|
|
3147
5189
|
const resolvedPrefix = prefix ?? t("commandPalette.createNew", { defaultValue: "Create new" });
|
|
3148
5190
|
const visibleResources = useMemo(() => {
|
|
3149
|
-
return app.registry.list().filter((r) => hasListPage(r)).filter((r) => gate(canonicalResourceKey$1(r), "create").state === "available").filter((r) => hasRecordPage(r, "create") || opensInQuick(app.registry, canonicalResourceKey$1(r), "create")).filter(isResourceVisible).filter((r) => resourceFilter ? resourceFilter(r) : true);
|
|
5191
|
+
return app.registry.list().filter((r) => hasListPage(r)).filter((r) => collectionFactOf(r) === void 0).filter((r) => gate(canonicalResourceKey$1(r), "create").state === "available").filter((r) => hasRecordPage(r, "create") || opensInQuick(app.registry, canonicalResourceKey$1(r), "create")).filter(isResourceVisible).filter((r) => resourceFilter ? resourceFilter(r) : true);
|
|
3150
5192
|
}, [
|
|
3151
5193
|
app,
|
|
3152
5194
|
gate,
|
|
@@ -3222,19 +5264,21 @@ function AppActions() {
|
|
|
3222
5264
|
className: "flex items-center gap-1",
|
|
3223
5265
|
"data-testid": "app-actions",
|
|
3224
5266
|
children: links.map(({ action, href }) => {
|
|
5267
|
+
const inert = action.disabledReason !== void 0 || action.undecided === true;
|
|
3225
5268
|
const label = resolveNavLabel(caps, `appActions.${action.name}`, action.label);
|
|
3226
5269
|
const content = action.icon === void 0 ? label : jsxs(Fragment$1, { children: [jsx(Icon, { name: action.icon }), jsx("span", {
|
|
3227
5270
|
className: "max-md:sr-only",
|
|
3228
5271
|
children: label
|
|
3229
5272
|
})] });
|
|
3230
5273
|
return jsx(Button, {
|
|
3231
|
-
asChild:
|
|
5274
|
+
asChild: !inert,
|
|
3232
5275
|
variant: "outline",
|
|
3233
5276
|
size: "sm",
|
|
3234
|
-
disabled:
|
|
5277
|
+
disabled: inert,
|
|
5278
|
+
"aria-busy": action.undecided === true ? "true" : void 0,
|
|
3235
5279
|
title: action.disabledReason === void 0 ? void 0 : reasonText(caps, action.disabledReason),
|
|
3236
5280
|
"data-testid": `app-action:${action.name}`,
|
|
3237
|
-
children:
|
|
5281
|
+
children: !inert ? jsx(RouterLink, {
|
|
3238
5282
|
href,
|
|
3239
5283
|
children: content
|
|
3240
5284
|
}) : jsx("span", { children: content })
|
|
@@ -3615,6 +5659,7 @@ function EnumChoiceGroup({ control, options, current, settling, value, onChange,
|
|
|
3615
5659
|
//#endregion
|
|
3616
5660
|
//#region src/enum/enum-picker.tsx
|
|
3617
5661
|
const ENUM_PICKER_CLEAR_SENTINEL = "__buro_clear__";
|
|
5662
|
+
const NO_VALUES = Object.freeze([]);
|
|
3618
5663
|
function withSummary(summary) {
|
|
3619
5664
|
return summary === void 0 ? {} : { summary };
|
|
3620
5665
|
}
|
|
@@ -3625,7 +5670,10 @@ function EnumPicker({ enumName: enumInput, value, onChange, control = "select",
|
|
|
3625
5670
|
const def = app.registry.enums.get(enumName);
|
|
3626
5671
|
const caps = useI18nCaps();
|
|
3627
5672
|
const ns = def?.i18nNamespace ?? enumName;
|
|
3628
|
-
const
|
|
5673
|
+
const loaderParams = def !== void 0 && def.values === void 0 && use !== void 0 ? resolveEnumParams(def.params, use.params, use.sources) : void 0;
|
|
5674
|
+
const unsettledParams = loaderParams !== void 0 && loaderParams.status !== "ready";
|
|
5675
|
+
const { values: loadedValues, isLoading, error } = useEnumValues(enumName, loaderParams?.status === "ready" ? loaderParams.params : void 0, { enabled: !unsettledParams });
|
|
5676
|
+
const values = unsettledParams ? NO_VALUES : loadedValues;
|
|
3629
5677
|
const available = useAvailableEnumValues({
|
|
3630
5678
|
values: isLoading || values === void 0 ? "pending" : values,
|
|
3631
5679
|
...def === void 0 ? {} : { enum: def },
|
|
@@ -4473,7 +6521,7 @@ function JsonInput({ name, id, value, onChange, testid, disabled, required, aria
|
|
|
4473
6521
|
}
|
|
4474
6522
|
//#endregion
|
|
4475
6523
|
//#region src/fields/lazy-relation-many.tsx
|
|
4476
|
-
const RelationManyPickerInput = lazy(() => import("./relation-many-picker-
|
|
6524
|
+
const RelationManyPickerInput = lazy(() => import("./relation-many-picker-CT7nKfE2.js").then((module) => ({ default: module.RelationManyPickerInput })));
|
|
4477
6525
|
function LazyRelationManyInput(props) {
|
|
4478
6526
|
return jsx(Suspense, {
|
|
4479
6527
|
fallback: jsx(Skeleton, {
|
|
@@ -6915,6 +8963,7 @@ const spacing = {
|
|
|
6915
8963
|
const groupTestId = (role, id) => `command-palette:${role}:${id}`;
|
|
6916
8964
|
function DefaultCommandPalette({ open, onOpenChange, actions, children }) {
|
|
6917
8965
|
const t = useOptionalT();
|
|
8966
|
+
const waiting = useShellWaiting();
|
|
6918
8967
|
const locale = useLocale();
|
|
6919
8968
|
const canSearchRecords = useOptionalDataRuntime() !== null;
|
|
6920
8969
|
const close = () => onOpenChange(false);
|
|
@@ -6953,21 +9002,21 @@ function DefaultCommandPalette({ open, onOpenChange, actions, children }) {
|
|
|
6953
9002
|
locale,
|
|
6954
9003
|
onSelect: close
|
|
6955
9004
|
}),
|
|
6956
|
-
jsx(CommandPaletteRecentGroup, {
|
|
9005
|
+
waiting ? null : jsx(CommandPaletteRecentGroup, {
|
|
6957
9006
|
onSelect: close,
|
|
6958
9007
|
refreshKey: open,
|
|
6959
9008
|
getTestId: groupTestId
|
|
6960
9009
|
}),
|
|
6961
|
-
canSearchRecords ? jsx(CommandPaletteRecordsGroup, {
|
|
9010
|
+
canSearchRecords && !waiting ? jsx(CommandPaletteRecordsGroup, {
|
|
6962
9011
|
enabled: open,
|
|
6963
9012
|
onSelect: close,
|
|
6964
9013
|
getTestId: groupTestId
|
|
6965
9014
|
}) : null,
|
|
6966
|
-
jsx(CommandPaletteNavigationGroups, {
|
|
9015
|
+
waiting ? null : jsx(CommandPaletteNavigationGroups, {
|
|
6967
9016
|
onSelect: close,
|
|
6968
9017
|
getTestId: (role, id) => `command-palette:nav:${role}:${id}`
|
|
6969
9018
|
}),
|
|
6970
|
-
jsx(CommandPaletteCreateGroup, {
|
|
9019
|
+
waiting ? null : jsx(CommandPaletteCreateGroup, {
|
|
6971
9020
|
onSelect: close,
|
|
6972
9021
|
getTestId: groupTestId
|
|
6973
9022
|
}),
|
|
@@ -6983,6 +9032,10 @@ function DefaultCommandPalette({ open, onOpenChange, actions, children }) {
|
|
|
6983
9032
|
//#region src/shell/app-shell.tsx
|
|
6984
9033
|
function AppShell({ brand, user, loginPath, sidebar, header, palette, docPanel, navChrome, children }) {
|
|
6985
9034
|
const enabled = palette !== false;
|
|
9035
|
+
const waiting = useShellWaiting();
|
|
9036
|
+
const decidedOnce = useRef(false);
|
|
9037
|
+
if (!waiting) decidedOnce.current = true;
|
|
9038
|
+
const holding = waiting && !decidedOnce.current;
|
|
6986
9039
|
const { open, setOpen, toggle } = useCommandPalette();
|
|
6987
9040
|
useGlobalShortcut({
|
|
6988
9041
|
key: "k",
|
|
@@ -7005,7 +9058,7 @@ function AppShell({ brand, user, loginPath, sidebar, header, palette, docPanel,
|
|
|
7005
9058
|
sidebar: fill(sidebar, () => jsx(AppSidebar, {})),
|
|
7006
9059
|
header: fill(header, () => jsx(AppHeader, {})),
|
|
7007
9060
|
...docPanel !== void 0 ? { docPanel } : {},
|
|
7008
|
-
children: navChrome === false ? children : jsx(NavChrome, { children })
|
|
9061
|
+
children: holding ? jsx(RecordState, { state: "loading" }) : navChrome === false ? children : jsx(NavChrome, { children })
|
|
7009
9062
|
}), paletteNode]
|
|
7010
9063
|
});
|
|
7011
9064
|
}
|
|
@@ -7113,6 +9166,6 @@ function ScopeSwitcher({ options, value, onChange, label, extra, placeholder })
|
|
|
7113
9166
|
const PACKAGE_NAME = "@burojs/ui-shadcn";
|
|
7114
9167
|
const PACKAGE_VERSION = "0.0.0";
|
|
7115
9168
|
//#endregion
|
|
7116
|
-
export { ActionButton, ActionOverflow, AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogOverlay, AlertDialogPortal, AlertDialogTitle, AlertDialogTrigger, AppActions, AppFlowButton, AppHeader, AppLayout, AppShell, AppSidebar, AuthPage, Avatar, AvatarFallback, AvatarImage, Badge, BooleanDisplay, BooleanInput, BrandMark, Breadcrumbs, BulkActionToolbar, Button, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, CheckConfirmDialog, Checkbox, Collapsible, CollapsibleContent, CollapsibleTrigger, ColumnsToggle, Command, CommandDialog, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, CommandLoading, CommandPalette, CommandPaletteActionsGroup, CommandPaletteCreateGroup, CommandPaletteDocsGroup, CommandPaletteNavigationGroups, CommandPaletteRecentGroup, CommandPaletteRecordsGroup, CommandPaletteTrigger, CommandSeparator, ContextPicker, CopyId, CreateButton, CursorLoadMore, DEFAULT_LANGUAGE_STORAGE_KEY, DataTable, DataTableDateRangeFilter, DataTableEnumFilter, DataTableFacetedFilter, DataTableNumRangeFilter, DataTableOperatorFilter, DataTableRowActions, DataTableTextFilter, DataTableToolbar, DataTableViewOptions, DateDisplay, DateInput, DeclarativeDisplay, DeclarativeForm, DefaultCommandPalette, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogPresentationHost, DialogPresentationShell, DialogTitle, DialogTrigger, DisabledAction, DocBrowser, DocInfo, DocPanel, DocTrailHelp, DocumentTitleProvider, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, ENUM_PICKER_CLEAR_SENTINEL, EditPageHeader, EnumBadge, EnumDisplay, EnumInput, EnumPicker, EnumDisplay$1 as EnumValueDisplay, ExportButton, ExportDialog, FacetedFilterPicker, Field, FieldDisplay, FileDisplay, FileField, FileInput, FilterBadges, FilterBuilder, FilterPanel, FilterRegion, FilterValueControl, FlowActionButton, Form, FormActions, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage, HeaderBreadcrumbs, HelpButton, Icon, Input, Label, LanguageSubmenu, LanguageSwitch, LinkComponentProvider, LinkKeysDisplay, ListBody, ListBulkBar, ListColumns, ListControls, ListCreate, ListErrorState, ListExport, ListFilters, ListFlows, ListLive, ListSavedViews, ListSearchInput, LoginForm, NavAreaPage, NavChrome, NavMain, NavigationGuardDialog, NumberDisplay, NumberInput, PACKAGE_NAME, PACKAGE_VERSION, PageHeader, PinnedFilterControls, Popover, PopoverAnchor, PopoverClose, PopoverContent, PopoverPresentationHost, PopoverTrigger, PresetFilterControls, Progress, RecordActions, RecordBreadcrumbs, RecordPage, RecordScreen, RecordTitle, RefreshButton, RelationDisplay, RelationField, RelationManyDisplay, RelationPicker, LazyRelationPickerDialog as RelationPickerDialog, RelationPickerInput, ResourceList, ResourceListPage, ResourcePageRouter, SavedViewsDropdown, ScopeDimensionPicker, ScopeSwitcher, SecondarySidebarLayout, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, Separator, Sheet, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetPresentationHost, SheetPresentationShell, SheetTitle, SheetTrigger, ShellProvider, ShowPageHeader, Sidebar, SidebarBrand, SidebarContent, SidebarFooter, SidebarGroup, SidebarGroupAction, SidebarGroupContent, SidebarGroupLabel, SidebarHeader, SidebarInput, SidebarInset, SidebarMenu, SidebarMenuAction, SidebarMenuBadge, SidebarMenuButton, SidebarMenuItem, SidebarMenuSkeleton, SidebarMenuSub, SidebarMenuSubButton, SidebarMenuSubItem, SidebarProvider, SidebarRail, SidebarSeparator, SidebarTrigger, Skeleton, THEME_CHOICES, THEME_STORAGE_KEY, Table, TableBody, TableCaption, TableCell, TableHead, TableHeader, TableRow, Tabs, TabsContent, TabsLayout, TabsList, TabsTrigger, TaskRunPanel, TextDisplay, TextInput, Textarea, TextareaDisplay, TextareaInput, ThemeSubmenu, ThemeToggle, Toaster, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, UserMenu, badgeVariants, buildNavItems, buroSortingToTanStack, buttonVariants, cn, defaultNavLayouts, defaultUserMapper, downloadArtifact, formatOperatorLabel, hasIcon, i18nPrefixToRef, isImage, layoutSlot, listSlot, loadLocale, localeLabel, modeForSurface, recordDisplayName, registerIcons, relationLinkClassName, resolveNavIcon, resolveUpdater, saveLocale, shadcnBlockImplementations, shadcnFieldImplementations, shadcnPresentationHosts, spacing, tanStackSortingToBuro, toast, typography, useBuroDataTable, useCurrentUser, useDialogFilterState, useDocumentTitle, useExportRunWatcher, useFormControl, useFormField, useHref, useIsMobile, useLanguagePaletteActions, useLanguageSwitch, useLayout, useLayoutResolver, useLinkComponent, useListSlot, useMatch, useNavIcon, useNavTree, useNavVisible, useOptionalSidebar, usePaletteState, usePresentationCopy, useRelationPicker, useShell, useSidebar, useTheme, useThemePaletteActions, webPlatform, wrapperEnabled };
|
|
9169
|
+
export { ActionButton, ActionOverflow, AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogOverlay, AlertDialogPortal, AlertDialogTitle, AlertDialogTrigger, AppActions, AppFlowButton, AppHeader, AppLayout, AppShell, AppSidebar, AuthPage, Avatar, AvatarFallback, AvatarImage, Badge, BooleanDisplay, BooleanInput, BrandMark, Breadcrumbs, BulkActionToolbar, Button, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, CheckConfirmDialog, Checkbox, Collapsible, CollapsibleContent, CollapsibleTrigger, CollectionCreatePicker, CollectionRecordRoute, ColumnsToggle, Command, CommandDialog, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, CommandLoading, CommandPalette, CommandPaletteActionsGroup, CommandPaletteCreateGroup, CommandPaletteDocsGroup, CommandPaletteNavigationGroups, CommandPaletteRecentGroup, CommandPaletteRecordsGroup, CommandPaletteTrigger, CommandSeparator, ContextPicker, CopyId, CreateButton, CursorLoadMore, DEFAULT_LANGUAGE_STORAGE_KEY, DataTable, DataTableDateRangeFilter, DataTableEnumFilter, DataTableFacetedFilter, DataTableNumRangeFilter, DataTableOperatorFilter, DataTableRowActions, DataTableTextFilter, DataTableToolbar, DataTableViewOptions, DateDisplay, DateInput, DeclarativeDisplay, DeclarativeForm, DefaultCommandPalette, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogPresentationHost, DialogPresentationShell, DialogTitle, DialogTrigger, DisabledAction, DocBrowser, DocInfo, DocPanel, DocTrailHelp, DocumentTitleProvider, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, ENUM_PICKER_CLEAR_SENTINEL, EditPageHeader, EnumBadge, EnumDisplay, EnumInput, EnumPicker, EnumDisplay$1 as EnumValueDisplay, ExportButton, ExportDialog, FacetedFilterPicker, Field, FieldDisplay, FileDisplay, FileField, FileInput, FilterBadges, FilterBuilder, FilterPanel, FilterRegion, FilterValueControl, FlowActionButton, Form, FormActions, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage, HeaderBreadcrumbs, HelpButton, Icon, Input, Label, LanguageSubmenu, LanguageSwitch, LinkComponentProvider, LinkKeysDisplay, ListBody, ListBulkBar, ListColumns, ListControls, ListCreate, ListErrorState, ListExport, ListFilters, ListFlows, ListLive, ListSavedViews, ListSearchInput, LoginForm, NavAreaNotFound, NavAreaPage, NavChrome, NavMain, NavigationGuardDialog, NumberDisplay, NumberInput, PACKAGE_NAME, PACKAGE_VERSION, PageHeader, PinnedFilterControls, Popover, PopoverAnchor, PopoverClose, PopoverContent, PopoverPresentationHost, PopoverTrigger, PresetFilterControls, Progress, RecordActions, RecordBreadcrumbs, RecordPage, RecordScreen, RecordTitle, RefreshButton, RelationDisplay, RelationField, RelationManyDisplay, RelationPicker, LazyRelationPickerDialog as RelationPickerDialog, RelationPickerInput, ResourceList, ResourceListPage, ResourcePageRouter, SavedViewsDropdown, ScopeDimensionPicker, ScopeSwitcher, SecondarySidebarLayout, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, Separator, Sheet, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetPresentationHost, SheetPresentationShell, SheetTitle, SheetTrigger, ShellProvider, ShowPageHeader, Sidebar, SidebarBrand, SidebarContent, SidebarFooter, SidebarGroup, SidebarGroupAction, SidebarGroupContent, SidebarGroupLabel, SidebarHeader, SidebarInput, SidebarInset, SidebarMenu, SidebarMenuAction, SidebarMenuBadge, SidebarMenuButton, SidebarMenuItem, SidebarMenuSkeleton, SidebarMenuSub, SidebarMenuSubButton, SidebarMenuSubItem, SidebarProvider, SidebarRail, SidebarSeparator, SidebarTrigger, Skeleton, THEME_CHOICES, THEME_STORAGE_KEY, Table, TableBody, TableCaption, TableCell, TableHead, TableHeader, TableRow, Tabs, TabsContent, TabsLayout, TabsList, TabsTrigger, TaskRunPanel, TextDisplay, TextInput, Textarea, TextareaDisplay, TextareaInput, ThemeSubmenu, ThemeToggle, Toaster, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, UnknownMemberCard, UserMenu, badgeVariants, buildNavItems, buroSortingToTanStack, buttonVariants, cn, defaultNavLayouts, defaultUserMapper, downloadArtifact, formatOperatorLabel, hasIcon, i18nPrefixToRef, isImage, layoutSlot, listSlot, loadLocale, localeLabel, modeForSurface, recordDisplayName, registerIcons, relationLinkClassName, resolveNavIcon, resolveUpdater, saveLocale, shadcnBlockImplementations, shadcnFieldImplementations, shadcnPresentationHosts, spacing, tanStackSortingToBuro, toast, typography, useBuroDataTable, useCurrentUser, useDialogFilterState, useDocumentTitle, useExportRunWatcher, useFormControl, useFormField, useHref, useIsMobile, useLanguagePaletteActions, useLanguageSwitch, useLayout, useLayoutResolver, useLinkComponent, useListSlot, useMatch, useNavIcon, useNavTree, useNavVisible, useOptionalSidebar, usePaletteState, usePresentationCopy, useRelationPicker, useShell, useSidebar, useTheme, useThemePaletteActions, webPlatform, wrapperEnabled };
|
|
7117
9170
|
|
|
7118
9171
|
//# sourceMappingURL=index.js.map
|