@burojs/ui-shadcn 0.7.0 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{bulk-summary-CrEij5vq.cjs → bulk-summary-2HLgz7Il.cjs} +31 -5
- package/dist/bulk-summary-2HLgz7Il.cjs.map +1 -0
- package/dist/{bulk-summary-BWSAS7k2.js → bulk-summary-D6A0ua21.js} +32 -6
- package/dist/bulk-summary-D6A0ua21.js.map +1 -0
- package/dist/{candidates-relation-picker-y5M6_oe4.cjs → candidates-relation-picker-C47mF260.cjs} +3 -3
- package/dist/{candidates-relation-picker-y5M6_oe4.cjs.map → candidates-relation-picker-C47mF260.cjs.map} +1 -1
- package/dist/{candidates-relation-picker-CS_JZ4Fd.js → candidates-relation-picker-CGXItMN0.js} +3 -3
- package/dist/{candidates-relation-picker-CS_JZ4Fd.js.map → candidates-relation-picker-CGXItMN0.js.map} +1 -1
- package/dist/{check-confirm-dialog-C-NHgMST.cjs → check-confirm-dialog-BT04Mieo.cjs} +13 -6
- package/dist/check-confirm-dialog-BT04Mieo.cjs.map +1 -0
- package/dist/{check-confirm-dialog-2j0gOd7x.js → check-confirm-dialog-CtiRzOAt.js} +14 -7
- package/dist/{check-confirm-dialog-2j0gOd7x.js.map → check-confirm-dialog-CtiRzOAt.js.map} +1 -1
- package/dist/{filter-builder-DFu0JP-1.cjs → filter-builder-ByGhxLf_.cjs} +127 -63
- package/dist/filter-builder-ByGhxLf_.cjs.map +1 -0
- package/dist/{filter-builder-DaWHUZuV.js → filter-builder-z3Axzf9Y.js} +114 -56
- package/dist/filter-builder-z3Axzf9Y.js.map +1 -0
- package/dist/{flow-runtime-BpmrIUb6.cjs → flow-runtime-BVFjMU5-.cjs} +6 -3
- package/dist/{flow-runtime-BpmrIUb6.cjs.map → flow-runtime-BVFjMU5-.cjs.map} +1 -1
- package/dist/{flow-runtime-DyqTmK3H.js → flow-runtime-DrUWyTS0.js} +6 -3
- package/dist/{flow-runtime-DyqTmK3H.js.map → flow-runtime-DrUWyTS0.js.map} +1 -1
- package/dist/{flow-screen-DCdli6Ha.js → flow-screen-3fk6J33y.js} +6 -4
- package/dist/flow-screen-3fk6J33y.js.map +1 -0
- package/dist/{flow-screen-Bcz9NF2V.cjs → flow-screen-6y5E_HT6.cjs} +5 -3
- package/dist/flow-screen-6y5E_HT6.cjs.map +1 -0
- package/dist/index.cjs +518 -107
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +187 -86
- package/dist/index.d.ts +187 -86
- package/dist/index.js +519 -108
- package/dist/index.js.map +1 -1
- package/dist/{inline-flow-host-3vw7LzzD.cjs → inline-flow-host-C4KmZIYE.cjs} +3 -3
- package/dist/{inline-flow-host-3vw7LzzD.cjs.map → inline-flow-host-C4KmZIYE.cjs.map} +1 -1
- package/dist/{inline-flow-host-C0rI9B7f.js → inline-flow-host-YnnvWjdU.js} +3 -3
- package/dist/{inline-flow-host-C0rI9B7f.js.map → inline-flow-host-YnnvWjdU.js.map} +1 -1
- package/dist/{lazy-relation-picker-dialog-Bt8lrEIu.cjs → lazy-relation-picker-dialog-BAKBRn3V.cjs} +2 -2
- package/dist/{lazy-relation-picker-dialog-Bt8lrEIu.cjs.map → lazy-relation-picker-dialog-BAKBRn3V.cjs.map} +1 -1
- package/dist/{lazy-relation-picker-dialog-CAScLPb2.js → lazy-relation-picker-dialog-DN4bvQfg.js} +2 -2
- package/dist/{lazy-relation-picker-dialog-CAScLPb2.js.map → lazy-relation-picker-dialog-DN4bvQfg.js.map} +1 -1
- package/dist/{page-flow-route-BHUEpn0I.js → page-flow-route-CtEfqAvU.js} +5 -6
- package/dist/page-flow-route-CtEfqAvU.js.map +1 -0
- package/dist/{page-flow-route-DS8IVTVH.cjs → page-flow-route-DEjAxHe3.cjs} +6 -7
- package/dist/page-flow-route-DEjAxHe3.cjs.map +1 -0
- package/dist/{place-node-CfzQvh4Y.js → place-node-BdxT-30h.js} +149 -108
- package/dist/place-node-BdxT-30h.js.map +1 -0
- package/dist/{place-node-CNb3w-c7.cjs → place-node-Bhl8B3fF.cjs} +163 -134
- package/dist/place-node-Bhl8B3fF.cjs.map +1 -0
- package/dist/{related-list-cards-CelMxl4q.cjs → related-list-cards-Bc_vfCE_.cjs} +3 -3
- package/dist/{related-list-cards-CelMxl4q.cjs.map → related-list-cards-Bc_vfCE_.cjs.map} +1 -1
- package/dist/{related-list-cards-BIqqY69Y.js → related-list-cards-BvkD5OcC.js} +3 -3
- package/dist/{related-list-cards-BIqqY69Y.js.map → related-list-cards-BvkD5OcC.js.map} +1 -1
- package/dist/related-list-timeline-DCrwN9oO.js +167 -0
- package/dist/related-list-timeline-DCrwN9oO.js.map +1 -0
- package/dist/related-list-timeline-k0DtnxTs.cjs +167 -0
- package/dist/related-list-timeline-k0DtnxTs.cjs.map +1 -0
- package/dist/{related-rows-MPnLCmPF.cjs → related-rows-BW9Jr07T.cjs} +3 -3
- package/dist/{related-rows-MPnLCmPF.cjs.map → related-rows-BW9Jr07T.cjs.map} +1 -1
- package/dist/{related-rows--uP2iKQC.js → related-rows-CPE-9FFr.js} +3 -3
- package/dist/{related-rows--uP2iKQC.js.map → related-rows-CPE-9FFr.js.map} +1 -1
- package/dist/{relation-many-picker-BJhaEqxz.js → relation-many-picker-BlrGRwMV.js} +10 -5
- package/dist/relation-many-picker-BlrGRwMV.js.map +1 -0
- package/dist/{relation-many-picker-DzuGJpEl.cjs → relation-many-picker-Br-zBHxT.cjs} +10 -5
- package/dist/relation-many-picker-Br-zBHxT.cjs.map +1 -0
- package/dist/{relation-picker-DaIMzf2H.cjs → relation-picker-ADjxI_TI.cjs} +11 -6
- package/dist/relation-picker-ADjxI_TI.cjs.map +1 -0
- package/dist/{relation-picker-dialog-DoEY8EkH.js → relation-picker-dialog-CyKliBkN.js} +2 -2
- package/dist/{relation-picker-dialog-DoEY8EkH.js.map → relation-picker-dialog-CyKliBkN.js.map} +1 -1
- package/dist/{relation-picker-dialog-9Vg5vvR1.cjs → relation-picker-dialog-DimVQF9Y.cjs} +2 -2
- package/dist/{relation-picker-dialog-9Vg5vvR1.cjs.map → relation-picker-dialog-DimVQF9Y.cjs.map} +1 -1
- package/dist/{relation-picker-D-tBBZW_.js → relation-picker-x7P5HKie.js} +11 -6
- package/dist/relation-picker-x7P5HKie.js.map +1 -0
- package/dist/{resource-page-router-Diw6S6TK.js → resource-page-router-BRpUnhay.js} +144 -17
- package/dist/resource-page-router-BRpUnhay.js.map +1 -0
- package/dist/{resource-page-router-C_kGW2o_.cjs → resource-page-router-qEd2xb55.cjs} +158 -19
- package/dist/resource-page-router-qEd2xb55.cjs.map +1 -0
- package/dist/use-record-subtitle-bfwIoUme.cjs +70 -0
- package/dist/use-record-subtitle-bfwIoUme.cjs.map +1 -0
- package/dist/use-record-subtitle-gIaCGgbI.js +53 -0
- package/dist/use-record-subtitle-gIaCGgbI.js.map +1 -0
- package/package.json +9 -9
- package/dist/bulk-summary-BWSAS7k2.js.map +0 -1
- package/dist/bulk-summary-CrEij5vq.cjs.map +0 -1
- package/dist/check-confirm-dialog-C-NHgMST.cjs.map +0 -1
- package/dist/filter-builder-DFu0JP-1.cjs.map +0 -1
- package/dist/filter-builder-DaWHUZuV.js.map +0 -1
- package/dist/flow-screen-Bcz9NF2V.cjs.map +0 -1
- package/dist/flow-screen-DCdli6Ha.js.map +0 -1
- package/dist/page-flow-route-BHUEpn0I.js.map +0 -1
- package/dist/page-flow-route-DS8IVTVH.cjs.map +0 -1
- package/dist/place-node-CNb3w-c7.cjs.map +0 -1
- package/dist/place-node-CfzQvh4Y.js.map +0 -1
- package/dist/related-list-timeline-CRh7xwLD.js +0 -141
- package/dist/related-list-timeline-CRh7xwLD.js.map +0 -1
- package/dist/related-list-timeline-DsLdi8sv.cjs +0 -141
- package/dist/related-list-timeline-DsLdi8sv.cjs.map +0 -1
- package/dist/relation-many-picker-BJhaEqxz.js.map +0 -1
- package/dist/relation-many-picker-DzuGJpEl.cjs.map +0 -1
- package/dist/relation-picker-D-tBBZW_.js.map +0 -1
- package/dist/relation-picker-DaIMzf2H.cjs.map +0 -1
- package/dist/resource-page-router-C_kGW2o_.cjs.map +0 -1
- package/dist/resource-page-router-Diw6S6TK.js.map +0 -1
|
@@ -3,8 +3,9 @@ import { l as useLinkComponent, m as PopoverTrigger, p as PopoverContent, u as P
|
|
|
3
3
|
import { t as Button } from "./button-MWVeZaqj.js";
|
|
4
4
|
import { C as CommandEmpty, D as CommandList, E as CommandItem, T as CommandInput, a as namesHiddenField, m as useRowTitle, r as RelationViewButton, t as RelationCreateButton, x as Command, y as LoadError } from "./relation-create-C43cdPQt.js";
|
|
5
5
|
import { t as Skeleton } from "./skeleton-U2noefj5.js";
|
|
6
|
-
import { t as LazyRelationPickerDialog } from "./lazy-relation-picker-dialog-
|
|
6
|
+
import { t as LazyRelationPickerDialog } from "./lazy-relation-picker-dialog-DN4bvQfg.js";
|
|
7
7
|
import { t as useRelationSelect } from "./relation-select-B8Fq4o-t.js";
|
|
8
|
+
import { r as useRowSubtitle, t as optionSubtitle } from "./use-record-subtitle-gIaCGgbI.js";
|
|
8
9
|
import { composeListFilter, normalizeFixedFilters, relationOpenOf, resolveResourceLabel, resourceKey, routeResourceKey } from "@burojs/core";
|
|
9
10
|
import { useApp, useI18nCaps, useList, useOptionalT, useRelatedRecord } from "@burojs/react";
|
|
10
11
|
import { IconCheck, IconChevronDown, IconLoader2 } from "@tabler/icons-react";
|
|
@@ -12,7 +13,7 @@ import * as React from "react";
|
|
|
12
13
|
import { Suspense, lazy } from "react";
|
|
13
14
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
14
15
|
//#region src/fields/lazy-candidates-picker.tsx
|
|
15
|
-
const CandidatesRelationPicker = lazy(() => import("./candidates-relation-picker-
|
|
16
|
+
const CandidatesRelationPicker = lazy(() => import("./candidates-relation-picker-CGXItMN0.js").then((module) => ({ default: module.CandidatesRelationPicker })));
|
|
16
17
|
function LazyCandidatesRelationPicker(props) {
|
|
17
18
|
return jsx(Suspense, {
|
|
18
19
|
fallback: jsx(Skeleton, {
|
|
@@ -26,13 +27,14 @@ function LazyCandidatesRelationPicker(props) {
|
|
|
26
27
|
//#endregion
|
|
27
28
|
//#region src/fields/relation-picker.tsx
|
|
28
29
|
const DEBOUNCE_MS = 250;
|
|
29
|
-
function RelationPicker({ to: toInput, value, onChange, by, searchField, pageSize = 10, clearable = true, placeholder, name, id, testid, fixedFilters, createSeeds, candidatesGate, rowFits, fieldName, open: openMode, scope = "context", disabled, required, className, ariaInvalid, ariaDescribedBy, ariaErrorMessage }) {
|
|
30
|
+
function RelationPicker({ to: toInput, value, onChange, by, searchField, pageSize = 10, clearable = true, placeholder, name, id, testid, fixedFilters, createSeeds, candidatesGate, rowFits, fieldName, open: openMode, optionSubtitle: optionSubtitle$1, scope = "context", disabled, required, className, ariaInvalid, ariaDescribedBy, ariaErrorMessage }) {
|
|
30
31
|
const to = routeResourceKey(toInput);
|
|
31
32
|
const app = useApp();
|
|
32
33
|
const caps = useI18nCaps();
|
|
33
34
|
const t = useOptionalT();
|
|
34
35
|
const targetDef = app.registry.get(to);
|
|
35
36
|
const rowTitle = useRowTitle(to, targetDef?.displayField);
|
|
37
|
+
const rowSubtitle = useRowSubtitle(to, optionSubtitle$1);
|
|
36
38
|
const targetLabel = resolveResourceLabel(caps, to, "single", targetDef).toLowerCase();
|
|
37
39
|
const RouterLink = useLinkComponent();
|
|
38
40
|
const tid = testid ?? `relation-picker:${to}`;
|
|
@@ -294,6 +296,7 @@ function RelationPicker({ to: toInput, value, onChange, by, searchField, pageSiz
|
|
|
294
296
|
if (rowId === null || rowId === void 0 || rowId === "") return null;
|
|
295
297
|
const idStr = String(rowId);
|
|
296
298
|
const label = rowTitle(row);
|
|
299
|
+
const subtitle = optionSubtitle(rowSubtitle(row), label, idStr);
|
|
297
300
|
const isSelected = value != null && String(value) === idStr;
|
|
298
301
|
return jsxs(CommandItem, {
|
|
299
302
|
value: `${label} ${idStr}`,
|
|
@@ -308,9 +311,9 @@ function RelationPicker({ to: toInput, value, onChange, by, searchField, pageSiz
|
|
|
308
311
|
className: "truncate",
|
|
309
312
|
children: label
|
|
310
313
|
}),
|
|
311
|
-
|
|
314
|
+
subtitle === null ? null : jsx("span", {
|
|
312
315
|
className: "ml-auto shrink-0 pl-2 text-xs text-muted-foreground",
|
|
313
|
-
children:
|
|
316
|
+
children: subtitle
|
|
314
317
|
})
|
|
315
318
|
]
|
|
316
319
|
}, idStr);
|
|
@@ -428,6 +431,7 @@ function FieldRelationPicker({ props, to, extra }) {
|
|
|
428
431
|
const { name, id, value, onChange, testid, field, disabled, required, relationSeeds } = props;
|
|
429
432
|
const { ariaInvalid, ariaDescribedBy, ariaErrorMessage } = props;
|
|
430
433
|
const meta = field?.meta;
|
|
434
|
+
const subtitle = props.optionSubtitle;
|
|
431
435
|
return jsx(RelationPicker, {
|
|
432
436
|
to,
|
|
433
437
|
value,
|
|
@@ -444,6 +448,7 @@ function FieldRelationPicker({ props, to, extra }) {
|
|
|
444
448
|
...relationSeeds === void 0 ? {} : { createSeeds: relationSeeds },
|
|
445
449
|
fieldName: field?.name ?? name,
|
|
446
450
|
open: relationOpenOf(field ?? {}),
|
|
451
|
+
...subtitle === void 0 ? {} : { optionSubtitle: subtitle },
|
|
447
452
|
disabled,
|
|
448
453
|
required,
|
|
449
454
|
ariaInvalid,
|
|
@@ -454,4 +459,4 @@ function FieldRelationPicker({ props, to, extra }) {
|
|
|
454
459
|
//#endregion
|
|
455
460
|
export { RelationPicker as n, RelationPickerInput as r, FieldRelationPicker as t };
|
|
456
461
|
|
|
457
|
-
//# sourceMappingURL=relation-picker-
|
|
462
|
+
//# sourceMappingURL=relation-picker-x7P5HKie.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"relation-picker-x7P5HKie.js","names":["optionSubtitle","optionSubtitleText"],"sources":["../src/fields/lazy-candidates-picker.tsx","../src/fields/relation-picker.tsx"],"sourcesContent":["import { type ReactElement, Suspense, lazy } from 'react';\nimport type { FieldInputProps } from './registry.js';\nimport { Skeleton } from '../primitives/skeleton.js';\n\nconst CandidatesRelationPicker = lazy(() =>\n import('./candidates-relation-picker.js').then((module) => ({\n default: module.CandidatesRelationPicker,\n })),\n);\n\n/**\n * A relation select whose field declares `candidates`: the candidates path is a chunk of its own,\n * so a form without candidates never loads it. Until it arrives a skeleton stands in; no read\n * is sent for the options meanwhile.\n */\nexport function LazyCandidatesRelationPicker(props: FieldInputProps): ReactElement {\n return (\n <Suspense\n fallback={\n <Skeleton\n data-buro-skeleton=\"\"\n data-testid=\"relation-candidates-loading\"\n className=\"h-9 w-full\"\n />\n }\n >\n <CandidatesRelationPicker {...props} />\n </Suspense>\n );\n}\n","'use client';\n\nimport * as React from 'react';\nimport { IconCheck, IconChevronDown, IconLoader2 } from '@tabler/icons-react';\nimport {\n composeListFilter,\n normalizeFixedFilters,\n resolveResourceLabel,\n resourceKey,\n type ErasedResourceHandle,\n type FilterExpression,\n type FixedFilterInput,\n type RelationOpen,\n relationOpenOf,\n type ResourceRefInput,\n type ScopeSource,\n routeResourceKey,\n type ResourceNameOrRef,\n} from '@burojs/core';\nimport { useApp, useI18nCaps, useList, useOptionalT, useRelatedRecord } from '@burojs/react';\nimport { LoadError } from '../load-error/load-error-view.js';\nimport type { CandidatesGate } from './candidates-gate.js';\nimport { namesHiddenField } from './search-access.js';\nimport {\n optionSubtitle as optionSubtitleText,\n type RecordSubtitleFn,\n useRowSubtitle,\n} from '../resource/use-record-subtitle.js';\nimport { useRowTitle } from '../resource/use-row-title.js';\nimport { cn } from '../lib/cn.js';\nimport { useLinkComponent } from '../layout/link-component.js';\nimport { Button } from '../primitives/button.js';\nimport {\n Command,\n CommandEmpty,\n CommandInput,\n CommandItem,\n CommandList,\n} from '../primitives/command.js';\nimport { Popover, PopoverContent, PopoverTrigger } from '../primitives/popover.js';\nimport { LazyCandidatesRelationPicker } from './lazy-candidates-picker.js';\nimport { LazyRelationPickerDialog } from './lazy-relation-picker-dialog.js';\nimport { RelationCreateButton } from './relation-create.js';\nimport { useRelationSelect } from './relation-select.js';\nimport { RelationViewButton } from './relation-view-button.js';\nimport type { FieldInputProps } from './registry.js';\n\n/** The portable erased AST — the only `fixedFilters` shape this module deals in. */\ntype ErasedExpression = FilterExpression<ErasedResourceHandle>;\n\n/**\n * Single-select relation picker (Relations item 2, v1).\n *\n * Renders a dropdown trigger labelled with the currently-selected\n * record's display name. Clicking opens a popover containing:\n * - a debounced search input that filters target rows by `searchField`\n * (defaults to the target's `displayField`) using the data\n * provider's `contains` operator,\n * - up to `pageSize` (default 10) result rows,\n * - a \"Clear\" button when `clearable` is true and a value is set,\n * - a \"View all\" link to the target's standalone list page so\n * operators can fall back to the full list when the inline search\n * can't find what they need.\n *\n * Multi-select / fuller modal / quick-create are deferred to v2 — see\n * docs/superpowers/specs/2026-05-04-relation-picker.md.\n *\n * Failure modes (mirrors RelationDisplay's fail-soft policy):\n * - target not registered → trigger renders the raw value, picker\n * is disabled. Caller still gets onChange(null) only via clear.\n * - useList errors → popover shows muted \"Failed to load\" with the\n * error message in the title; \"View all\" still works.\n * - target row for `value` not found → trigger shows the raw id\n * (so the operator can at least see what was stored).\n */\n\nexport interface RelationPickerProps<N extends string = string> {\n /** Target resource name (matches `f.relation({ to: '...' })`). */\n to: ResourceNameOrRef<N>;\n /** Currently-selected FK value, or null/undefined when empty. */\n value: string | number | null | undefined;\n /** Called with the new selected id (or `by` value), or null when cleared. */\n onChange: (next: string | number | null) => void;\n /**\n * The target field the value holds (`meta.by`, spec 4.2): options write `row[by]` and the\n * selected value is resolved by it. Absent: the target's id field.\n */\n by?: string;\n /**\n * Field whose value is matched against the search text. Defaults to\n * the target's `displayField` (which is what the trigger label\n * already shows, so it's the natural search target).\n */\n searchField?: string;\n /** Initial fetch / max visible rows. Defaults to 10. */\n pageSize?: number;\n /** Show a clear button. Defaults to true. */\n clearable?: boolean;\n /** Trigger placeholder when value is empty. */\n placeholder?: string;\n /** HTML name attribute for the trigger button. */\n name?: string;\n /** HTML id attribute. */\n id?: string;\n /** Test-id override. Defaults to `relation-picker:<to>`. */\n testid?: string;\n /**\n * Extra filters ANDed onto every query (inline popover + advanced modal) —\n * the same `FixedFilterInput` shape `useResourceList` takes: a bare AST\n * expression (`@burojs/core`'s `FilterExpression`, e.g.\n * `{ field: 'is_active', op: 'eq', value: true }`), or `{ filter, lock?,\n * label? }` for finer control. A bare expression defaults to `lock: true`\n * — the field is hidden from the modal's filter builder, with no badge —\n * which is what keeps the picker from ever offering an out-of-scope\n * record. Pass `{ filter, lock: false }` to keep the field open to the\n * user, and add `label` to render it as an undeletable badge instead.\n */\n fixedFilters?: readonly FixedFilterInput[];\n /**\n * The trusted `defaults` of a create opened from this select (P6-31): the registry field's\n * static `eq` literals, handed down by the form controller. Never read from `fixedFilters` or\n * `field.meta`.\n */\n createSeeds?: Readonly<Record<string, unknown>>;\n /**\n * What the field's `candidates` say about reading the target right now (P7 3.4): `pending`\n * draws a disabled trigger, `empty` a popover that says what to choose first, `forbidden` a\n * disabled trigger and why. While set, nothing is read for the options. Absent: read normally.\n */\n candidatesGate?: CandidatesGate;\n /**\n * Whether a loaded row fits the resolved candidate filters (already in `fixedFilters`):\n * `false` makes the field say the chosen row \"does not match the current filter\" (P7 3.4).\n */\n rowFits?: (row: Record<string, unknown>) => boolean | undefined;\n /** The form field holding the key (the \"view\" and \"+ create\" test ids); defaults to `name`. */\n fieldName?: string;\n /** The field's `ui.open` (P7 3.6): what the \"view\" button opens, or no button for `none`. */\n open?: RelationOpen;\n /**\n * The options' second line over the target row (a relation field's `optionSubtitle`): wins over\n * the target's `recordSubtitle`; without either, `#<id>`.\n */\n optionSubtitle?: RecordSubtitleFn;\n /**\n * The read scope of the option reads (the popover, \"Advanced search\" and the create hand-back).\n * Default `'context'`: the record around the picker narrows its options, except by the field\n * the picker itself edits (`fieldName`, else `name`). `'none'` reads every row the provider\n * allows; `'session'` the session's scope selection.\n */\n scope?: ScopeSource;\n disabled?: boolean;\n required?: boolean;\n className?: string;\n /** Forwarded to the trigger button when the field is in an invalid state. */\n ariaInvalid?: boolean;\n /** Forwarded to the trigger button as `aria-describedby` (help text only; error id goes via ariaErrorMessage). */\n ariaDescribedBy?: string;\n /** Forwarded to the trigger button as `aria-errormessage` (WAI-ARIA 1.2). */\n ariaErrorMessage?: string;\n}\n\nconst DEBOUNCE_MS = 250;\n\nexport function RelationPicker<const N extends string = string>({\n to: toInput,\n value,\n onChange,\n by,\n searchField,\n pageSize = 10,\n clearable = true,\n placeholder,\n name,\n id,\n testid,\n fixedFilters,\n createSeeds,\n candidatesGate,\n rowFits,\n fieldName,\n open: openMode,\n optionSubtitle,\n scope = 'context',\n disabled,\n required,\n className,\n ariaInvalid,\n ariaDescribedBy,\n ariaErrorMessage,\n}: RelationPickerProps<N>): React.ReactElement {\n const to = routeResourceKey(toInput);\n const app = useApp();\n const caps = useI18nCaps();\n const t = useOptionalT();\n const targetDef = app.registry.get(to);\n const rowTitle = useRowTitle(to, targetDef?.displayField);\n const rowSubtitle = useRowSubtitle(to, optionSubtitle);\n // Lowercased: the label sits mid-sentence in «Select location…».\n const targetLabel = resolveResourceLabel(caps, to, 'single', targetDef).toLowerCase();\n const RouterLink = useLinkComponent();\n // Trigger testid honours the prop override (so callers like\n // the record form get a deterministic per-field id like\n // `form-field-input:<resource>:<view>:<field>`), but child elements\n // (search, options, view-all, clear) keep a stable per-target prefix\n // so e2e tests don't have to know the parent form's testid path.\n const tid = testid ?? `relation-picker:${to}`;\n const childPrefix = `relation-picker:${to}`;\n\n const triggerRef = React.useRef<HTMLButtonElement | null>(null);\n // An overlay opened from this select returns focus to the trigger when it closes: the shell\n // records the focused element as it mounts.\n const focusTrigger = React.useCallback(() => triggerRef.current?.focus(), []);\n const [open, setOpen] = React.useState(false);\n const [dialogOpen, setDialogOpen] = React.useState(false);\n // The dialog is mounted on its first open, and only while no candidates gate holds the read:\n // without the candidate filters it would list rows the field must not offer.\n const gated = candidatesGate !== undefined;\n const [dialogMounted, setDialogMounted] = React.useState(false);\n React.useEffect(() => {\n if (gated) setDialogOpen(false);\n }, [gated]);\n React.useEffect(() => {\n if (dialogOpen) setDialogMounted(true);\n }, [dialogOpen]);\n const [search, setSearch] = React.useState('');\n const [debouncedSearch, setDebouncedSearch] = React.useState('');\n\n // Debounce the search text so each keystroke doesn't fire a request —\n // react-query will dedup but the popover can flicker.\n React.useEffect(() => {\n const handle = setTimeout(() => setDebouncedSearch(search), DEBOUNCE_MS);\n return () => clearTimeout(handle);\n }, [search]);\n\n // Reset search when the popover closes so reopening starts clean.\n React.useEffect(() => {\n if (!open) {\n setSearch('');\n setDebouncedSearch('');\n }\n }, [open]);\n\n // Trigger label: fetch the currently-selected target row to show its\n // display name. Skipped when value is empty (useRelatedRecord handles\n // that). Same hook RelationDisplay uses, so the cache is shared.\n // An input resolves the selected value whatever the field's `resolve` says (spec 4.3).\n const { record: selectedRecord, isLoading: selectedLoading } = useRelatedRecord(\n to,\n value,\n by === undefined ? undefined : { by },\n );\n // The record field this picker writes: a record's own scope field never narrows its options.\n const scopeField = fieldName ?? name;\n // The value field, the search set and the select's one pending create, shared by the popover\n // and the \"Advanced search\" dialog (and by `useRelationPicker`).\n const {\n access,\n valueField,\n searchDecl,\n create,\n reader: createReader,\n } = useRelationSelect({\n to,\n by,\n searchField,\n fixedFilters,\n scope,\n scopeField,\n held: gated,\n // As a pick does: the value is written and the \"Advanced search\" dialog, when the create\n // came from it, is done.\n onValue: (picked) => {\n onChange(picked);\n setDialogOpen(false);\n },\n });\n const accessReady = access.status === 'ready';\n // List the picker options. `useList` returns `{ data: { data, total } }`.\n //\n // Composed through the SAME `composeListFilter` core the dialog\n // (`RelationPickerDialog`/`useDialogFilterState`) and the list page use —\n // this popover is a distinct composition SITE (fixed scope + free-text\n // search only, no builder group), not a second set of rules. Search\n // resolution mirrors the dialog's: the target's declared\n // `views.list.search` always wins; the `searchField` prop, else\n // `displayField`, else `'name'` is the fallback (`useRelationSelect`),\n // wrapped as a single-field declaration so it goes through the exact same\n // `searchToFilter` path a real declaration would. Before this, the popover\n // hand-rolled `{ field: <search field>, op: 'contains', value }`\n // unconditionally, ignoring a declared `views.list.search` entirely — one\n // control (popover vs. \"advanced search\") giving two different answers\n // for the same typed text.\n const filter = React.useMemo<ErasedExpression | undefined>(\n () =>\n composeListFilter({\n fixed: normalizeFixedFilters(fixedFilters),\n search: debouncedSearch,\n searchDecl,\n searchContext: { resource: to, def: targetDef },\n }),\n [fixedFilters, debouncedSearch, searchDecl, to, targetDef],\n );\n // An author predicate or a function search naming a hidden field forbids the read.\n const blocked =\n !accessReady ||\n candidatesGate !== undefined ||\n namesHiddenField(filter, (name) => access.fieldVisible(name));\n\n const listQuery = useList(to, {\n ...(filter ? { filter } : {}),\n pagination: { kind: 'offset', page: 1, pageSize },\n scope,\n ...(scopeField === undefined ? {} : { scopeField }),\n enabled: open && targetDef != null && !blocked,\n });\n\n const rows = (listQuery.data?.data ?? []) as Array<{ id: unknown } & Record<string, unknown>>;\n // ⚠️ The rows the popover already loaded are a better label source than a\n // second round-trip. `selectedRecord` comes from the batched relation\n // aggregator; immediately after picking an option that batch has not\n // resolved the new id yet, so the trigger fell through to its raw-id\n // fallback and showed «4» instead of «Finished goods warehouse». The picker\n // is holding the chosen record in `rows` — use it.\n const triggerLabel = renderTriggerLabel({\n rowTitle,\n optionRows: rows,\n valueField,\n value,\n selectedRecord,\n selectedLoading,\n targetDef,\n targetLabel,\n placeholder,\n t,\n });\n\n // Target not registered — degrade gracefully: render a disabled\n // trigger with the raw value so the operator can still SEE the\n // stored FK, but they can't pick a new one because we have no\n // basePath to drive a \"View all\" link or a useList query.\n if (!targetDef) {\n if (process.env.NODE_ENV !== 'production') {\n // eslint-disable-next-line no-console\n console.warn(`[RelationPicker] target resource '${to}' not registered — picker disabled`);\n }\n return (\n <Button\n type=\"button\"\n variant=\"outline\"\n disabled\n data-testid={`${childPrefix}:unregistered`}\n className={cn('w-full justify-between font-normal', className)}\n name={name}\n id={id}\n aria-required={required}\n >\n <span className=\"truncate text-muted-foreground\">\n {value == null || value === ''\n ? (placeholder ?? t('field.select', { defaultValue: 'Select…' }))\n : String(value)}\n </span>\n <IconChevronDown className=\"h-4 w-4 opacity-50\" aria-hidden />\n </Button>\n );\n }\n\n // The candidates say the target must not be read yet (P7 3.4): a disabled trigger, no popover.\n if (candidatesGate?.kind === 'pending') {\n return (\n <Button\n type=\"button\"\n variant=\"outline\"\n disabled\n data-testid={tid}\n data-buro-candidates=\"pending\"\n className={cn('w-full justify-between font-normal', className)}\n name={name}\n id={id}\n aria-required={required}\n aria-busy=\"true\"\n >\n <span className=\"truncate text-muted-foreground\">{triggerLabel}</span>\n <IconLoader2 className=\"h-4 w-4 shrink-0 animate-spin opacity-50\" aria-hidden />\n </Button>\n );\n }\n if (candidatesGate?.kind === 'forbidden') {\n return (\n <div className=\"flex flex-col gap-1\">\n <Button\n type=\"button\"\n variant=\"outline\"\n disabled\n data-testid={tid}\n data-buro-candidates=\"forbidden\"\n className={cn('w-full justify-between font-normal', className)}\n name={name}\n id={id}\n aria-required={required}\n aria-describedby={candidatesGate.testId}\n >\n <span className={cn('truncate text-left', value == null && 'text-muted-foreground')}>\n {triggerLabel}\n </span>\n <IconChevronDown className=\"h-4 w-4 shrink-0 opacity-50\" aria-hidden />\n </Button>\n <p\n id={candidatesGate.testId}\n data-testid={candidatesGate.testId}\n className=\"text-xs text-muted-foreground\"\n >\n {candidatesGate.message}\n </p>\n </div>\n );\n }\n\n const targetBasePath = targetDef.basePath ?? `/${to}`;\n // The chosen row stays as it is when it falls outside the candidates; the field says so.\n const outside =\n rowFits !== undefined &&\n value != null &&\n value !== '' &&\n selectedRecord != null &&\n rowFits(selectedRecord as Record<string, unknown>) === false;\n const isLoading = listQuery.isLoading || listQuery.isFetching;\n const error = listQuery.isError ? (listQuery.error as unknown as Error) : null;\n\n function handleSelect(rowId: unknown): void {\n // IDs arrive as strings or numbers depending on the data provider.\n // Pass through as-is so callers can compare to their\n // schema (e.g. UUID strings stay strings).\n onChange(rowId as string | number);\n setOpen(false);\n }\n\n function handleClear(): void {\n onChange(null);\n setOpen(false);\n }\n\n return (\n <Popover open={open} onOpenChange={setOpen}>\n <div className=\"flex items-center gap-1\">\n <PopoverTrigger asChild>\n <Button\n ref={triggerRef}\n type=\"button\"\n variant=\"outline\"\n role=\"combobox\"\n aria-expanded={open}\n aria-haspopup=\"listbox\"\n aria-required={required}\n aria-invalid={ariaInvalid || undefined}\n aria-describedby={ariaDescribedBy}\n {...(ariaErrorMessage ? { 'aria-errormessage': ariaErrorMessage } : {})}\n disabled={disabled}\n data-testid={tid}\n name={name}\n id={id}\n className={cn('w-full justify-between font-normal', className)}\n >\n <span className={cn('truncate text-left', value == null && 'text-muted-foreground')}>\n {triggerLabel}\n </span>\n <IconChevronDown className=\"h-4 w-4 shrink-0 opacity-50\" aria-hidden />\n </Button>\n </PopoverTrigger>\n {!disabled && (\n <RelationViewButton\n to={to}\n value={value}\n by={by}\n fieldName={fieldName ?? name ?? to}\n beforeOpen={focusTrigger}\n {...(openMode === undefined ? {} : { open: openMode })}\n />\n )}\n </div>\n {outside && (\n <p\n className=\"mt-1 text-xs text-muted-foreground\"\n data-testid={`${childPrefix}:not-matching`}\n >\n {t('relationPicker.notMatching', { defaultValue: 'Does not match the current filter' })}\n </p>\n )}\n <PopoverContent className=\"w-[var(--radix-popover-trigger-width)] p-0\" align=\"start\">\n {candidatesGate?.kind === 'empty' ? (\n <p\n className=\"px-3 py-6 text-center text-sm text-muted-foreground\"\n data-testid={candidatesGate.testId}\n >\n {candidatesGate.message}\n </p>\n ) : (\n /* shouldFilter={false}: cmdk's built-in client filter is disabled\n because filtering happens server-side via useList's `contains`\n operator. cmdk still drives keyboard navigation, focus,\n highlighted-item state, and Enter-to-select. */\n <Command shouldFilter={false}>\n {searchDecl !== undefined && (\n <CommandInput\n value={search}\n onValueChange={setSearch}\n placeholder={t('toolbar.search', {\n params: { name: targetLabel },\n defaultValue: `Search ${targetLabel}…`,\n })}\n data-testid={`${childPrefix}:search`}\n aria-label={t('toolbar.search', {\n params: { name: targetLabel },\n defaultValue: `Search ${targetLabel}`,\n })}\n />\n )}\n <CommandList data-testid={`${childPrefix}:list`}>\n {isLoading && (\n <div\n className=\"flex items-center justify-center gap-2 py-6 text-sm text-muted-foreground\"\n data-testid={`${childPrefix}:loading`}\n >\n <IconLoader2 className=\"h-4 w-4 animate-spin\" aria-hidden />\n <span>{t('table.loading', { defaultValue: 'Loading…' })}</span>\n </div>\n )}\n {!isLoading && error && (\n <LoadError\n surface=\"picker\"\n error={error}\n testId={`${childPrefix}:error`}\n className=\"block px-3 py-6 text-center text-sm text-muted-foreground\"\n fallback={{ key: 'relation.failed', default: 'Failed to load options.' }}\n />\n )}\n {!isLoading && !error && rows.length === 0 && (\n <CommandEmpty data-testid={`${childPrefix}:empty`}>\n {t('relation.noResults', { defaultValue: 'No results.' })}\n </CommandEmpty>\n )}\n {!isLoading &&\n !error &&\n rows.map((row) => {\n const rowId = row[valueField];\n // A row without the value the field stores cannot be chosen (no fallback to id).\n if (rowId === null || rowId === undefined || rowId === '') return null;\n const idStr = String(rowId);\n const label = rowTitle(row);\n const subtitle = optionSubtitleText(rowSubtitle(row), label, idStr);\n const isSelected = value != null && String(value) === idStr;\n return (\n <CommandItem\n key={idStr}\n // cmdk uses `value` for keyboard-search semantics;\n // setting it to `${label} ${idStr}` lets users type\n // either the label OR the id and the highlighted\n // option still tracks correctly across renders.\n value={`${label} ${idStr}`}\n data-testid={`${childPrefix}:option:${idStr}`}\n onSelect={() => handleSelect(rowId)}\n >\n <IconCheck\n className={cn(\n 'mr-2 h-4 w-4 shrink-0',\n isSelected ? 'opacity-100' : 'opacity-0',\n )}\n aria-hidden\n />\n <span className=\"truncate\">{label}</span>\n {/* The declared subtitle, else `#<id>` unless the label already is the id\n (REF-40). */}\n {subtitle === null ? null : (\n <span className=\"ml-auto shrink-0 pl-2 text-xs text-muted-foreground\">\n {subtitle}\n </span>\n )}\n </CommandItem>\n );\n })}\n </CommandList>\n {/* Wraps at narrow widths: four actions do not fit one line of a small popover. */}\n <div className=\"flex flex-wrap items-center justify-between gap-x-2 gap-y-1 border-t px-3 py-2 text-sm\">\n {clearable && value != null && value !== '' ? (\n <button\n type=\"button\"\n onClick={handleClear}\n data-testid={`${childPrefix}:clear`}\n className=\"text-muted-foreground hover:text-foreground focus:outline-hidden focus-visible:underline\"\n >\n {t('relationPicker.clear', { defaultValue: 'Clear' })}\n </button>\n ) : (\n <span aria-hidden />\n )}\n <div className=\"flex flex-wrap items-center gap-x-3 gap-y-1\">\n {/* #11 — escalation to the full-list modal: search + sort +\n paginate + create, for when the inline popover isn't enough. */}\n <button\n type=\"button\"\n onClick={() => {\n setOpen(false);\n setDialogOpen(true);\n }}\n data-testid={`${childPrefix}:advanced`}\n className=\"text-foreground hover:underline focus:outline-hidden focus-visible:underline\"\n >\n {t('relationPicker.advancedSearch', { defaultValue: 'Advanced search' })}\n </button>\n {!disabled && (\n <RelationCreateButton\n to={to}\n fieldName={fieldName ?? name ?? to}\n create={create}\n seeds={createSeeds}\n search={search}\n beforeOpen={() => {\n setOpen(false);\n focusTrigger();\n }}\n className=\"text-foreground hover:underline focus:outline-hidden focus-visible:underline disabled:opacity-50\"\n />\n )}\n <RouterLink\n href={targetBasePath}\n data-testid={`${childPrefix}:view-all`}\n className=\"text-muted-foreground hover:text-foreground hover:underline focus:outline-hidden focus-visible:underline\"\n >\n {t('relationPicker.viewAll', { defaultValue: 'Open list' })} →\n </RouterLink>\n </div>\n </div>\n </Command>\n )}\n </PopoverContent>\n {/*\n The RAW `searchField` prop, not the resolved fallback — the dialog\n (Task 5, SP3) does its OWN target-declaration → prop → displayField\n resolution via `useDialogFilterState`, so handing it an already-\n resolved value here would just have this component's own\n `displayField ?? 'name'` fallback (built for the POPOVER's simpler\n contains-only search) silently stand in for the dialog's, rather\n than letting the target's declared `views.list.search` win when the\n caller passed no override at all.\n */}\n {dialogMounted && !gated && (\n <LazyRelationPickerDialog\n to={to}\n open={dialogOpen}\n onOpenChange={setDialogOpen}\n onSelect={(rowId) => onChange(rowId)}\n valueField={valueField}\n searchField={searchField}\n fixedFilters={fixedFilters}\n scope={scope}\n {...(scopeField === undefined ? {} : { scopeField })}\n {...(disabled ? {} : { create })}\n createSeeds={createSeeds}\n fieldName={fieldName ?? name ?? to}\n onBeforeCreate={focusTrigger}\n />\n )}\n {createReader}\n </Popover>\n );\n}\n\ninterface TriggerLabelArgs {\n value: string | number | null | undefined;\n selectedRecord: ReturnType<typeof useRelatedRecord>['record'];\n selectedLoading: boolean;\n /** Rows already loaded for the popover; used as a same-tick label source. */\n optionRows?: Array<{ id: unknown } & Record<string, unknown>>;\n /** The row field the value holds (`by`, else the id field). */\n valueField: string;\n targetDef: ReturnType<ReturnType<typeof useApp>['registry']['get']>;\n /** The resolved, lowercased target label. */\n targetLabel: string;\n placeholder?: string;\n /** The row title under the viewer's field view (id when the display field is hidden). */\n rowTitle: (row: Record<string, unknown>) => string;\n t: (key: string, options?: { params?: Record<string, unknown>; defaultValue?: string }) => string;\n}\n\nfunction renderTriggerLabel({\n value,\n selectedRecord,\n selectedLoading,\n targetLabel,\n placeholder,\n optionRows,\n valueField,\n t,\n rowTitle,\n}: TriggerLabelArgs): React.ReactNode {\n if (value == null || value === '') {\n return (\n placeholder ??\n t('relation.select', {\n params: { name: targetLabel },\n defaultValue: `Select ${targetLabel}…`,\n })\n );\n }\n if (selectedLoading) {\n return t('table.loading', { defaultValue: 'Loading…' });\n }\n if (selectedRecord) {\n return rowTitle(selectedRecord);\n }\n // Not resolved by the aggregator yet (or at all). If the popover already\n // loaded this record as an option — which it always has right after the\n // user picked it — label from that instead of showing a bare id.\n const fromOptions = optionRows?.find((r) => String(r[valueField]) === String(value));\n if (fromOptions) {\n return rowTitle(fromOptions);\n }\n // Genuinely unknown (404 / not in the loaded page) — show the raw id so the\n // operator can at least see what was stored.\n return String(value);\n}\n\n/**\n * Form-field adapter wrapping `<RelationPicker>` for use as a default\n * Input renderer in the field registry. Reads `field.meta.to` for the\n * target resource name. When `meta.to` is absent we degrade to a plain\n * (disabled) text representation of the value with a console warning —\n * same fail-soft as RelationDisplay.\n */\nexport function RelationPickerInput(props: FieldInputProps): React.ReactElement {\n const { name, id, value, testid, field, required } = props;\n const meta = field?.meta as { to?: ResourceRefInput; candidates?: unknown } | undefined;\n let to: string | undefined;\n try {\n to = meta?.to === undefined ? undefined : resourceKey(meta.to);\n } catch {\n to = undefined;\n }\n\n if (!to) {\n if (process.env.NODE_ENV !== 'production') {\n // eslint-disable-next-line no-console\n console.warn(\n `[RelationPickerInput] no meta.to on field '${field?.name ?? name}' — relation picker disabled`,\n );\n }\n return (\n <Button\n type=\"button\"\n variant=\"outline\"\n disabled\n data-testid={`${testid ?? `relation-picker:${name}`}:no-target`}\n className=\"w-full justify-between font-normal\"\n name={name}\n id={id}\n aria-required={required}\n >\n <span className=\"truncate text-muted-foreground\">\n {value == null || value === '' ? '—' : String(value)}\n </span>\n </Button>\n );\n }\n\n // The candidates path is loaded only for a field that declares candidates.\n if (Array.isArray(meta?.candidates) && meta.candidates.length > 0)\n return <LazyCandidatesRelationPicker {...props} />;\n return <FieldRelationPicker props={props} to={to} />;\n}\n\n/** The form field's props as `RelationPicker` takes them; `extra` carries the candidates' part. */\nexport function FieldRelationPicker({\n props,\n to,\n extra,\n}: {\n props: FieldInputProps;\n to: string;\n extra?: {\n fixedFilters: FieldInputProps['fixedFilters'];\n candidatesGate?: CandidatesGate;\n rowFits?: (row: Record<string, unknown>) => boolean | undefined;\n };\n}): React.ReactElement {\n const { name, id, value, onChange, testid, field, disabled, required, relationSeeds } = props;\n const { ariaInvalid, ariaDescribedBy, ariaErrorMessage } = props;\n const meta = field?.meta as\n | { by?: unknown; searchField?: string; scope?: ScopeSource }\n | undefined;\n const subtitle = props.optionSubtitle;\n return (\n <RelationPicker\n to={to}\n value={value as string | number | null | undefined}\n onChange={(v) => onChange(v)}\n {...(typeof meta?.by === 'string' ? { by: meta.by } : {})}\n name={name}\n id={id}\n testid={testid ?? `relation-picker:${to}`}\n searchField={meta?.searchField}\n {...(meta?.scope === undefined ? {} : { scope: meta.scope })}\n fixedFilters={extra === undefined ? props.fixedFilters : extra.fixedFilters}\n {...(extra?.candidatesGate === undefined ? {} : { candidatesGate: extra.candidatesGate })}\n {...(extra?.rowFits === undefined ? {} : { rowFits: extra.rowFits })}\n {...(relationSeeds === undefined ? {} : { createSeeds: relationSeeds })}\n fieldName={field?.name ?? name}\n open={relationOpenOf(field ?? {})}\n {...(subtitle === undefined ? {} : { optionSubtitle: subtitle })}\n disabled={disabled}\n required={required}\n ariaInvalid={ariaInvalid}\n ariaDescribedBy={ariaDescribedBy}\n ariaErrorMessage={ariaErrorMessage}\n />\n );\n}\n"],"mappings":"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"}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { l as cn } from "./use-theme-dnE3Od3I.js";
|
|
2
2
|
import { l as useLinkComponent } from "./toaster-Ct_YHGs6.js";
|
|
3
|
-
import { D as loadPageFlowRoute, k as useLoaded, o as canonicalResourceName, t as CheckConfirmDialog, v as FlowBoundary, w as loadFailedText } from "./check-confirm-dialog-
|
|
3
|
+
import { D as loadPageFlowRoute, k as useLoaded, o as canonicalResourceName, t as CheckConfirmDialog, v as FlowBoundary, w as loadFailedText } from "./check-confirm-dialog-CtiRzOAt.js";
|
|
4
4
|
import { t as Button } from "./button-MWVeZaqj.js";
|
|
5
5
|
import { b as RecordState, m as useRowTitle, v as InvalidRouteId, y as LoadError } from "./relation-create-C43cdPQt.js";
|
|
6
6
|
import { t as Skeleton } from "./skeleton-U2noefj5.js";
|
|
7
|
-
import { Ct as
|
|
7
|
+
import { Ct as useShownItemActions, G as RouteScreenClaimContext, H as ScreenTree, J as FormControlProvider, K as RouteScreenContext, Mt as FieldDisplay, S as useFrameChrome, U as useTabsUrlState, W as QuickScreenClaimContext, X as useFormControlOf, d as resolveFrameView, g as UndeclaredTypeNotice, gt as PageHeader, h as TypeChromeDrawnContext, i as RouteListContext, kt as DeepLinkScope, m as HeaderTypeNotice, n as viewOf, p as ChangeTypeButton, q as useIsRouteScreen, t as selectImplementation, u as isBuiltinRecordView, v as FormActions, x as FrameChromeContext } from "./place-node-BdxT-30h.js";
|
|
8
8
|
import { t as useNoticeSink } from "./success-notices-LDEgEkc4.js";
|
|
9
|
-
import { FORM_SLOT, addressesCapability, builtinFrames, canonicalEntityNodeKey, compilePageScreen, declaredText, dtoFields, evaluateVisible, fieldSensitivity, formatEntityRef, hasListPage, hrefFor, humanizeKey, isCustomPageDef, isScreenFailure, issueOfInput, matchPath, navHrefFor, normalizeEntityRef, normalizeIssueInputs, pageParamNames, parsePageParams, resolveNavLabel, resourceKey, routeResourceKey, routeServerIssues, scopeColumnOf, snapshotAppFrames, snapshotBlockLookup, snapshotFrameLookup, successNoticesOf } from "@burojs/core";
|
|
10
|
-
import { ActionRouteContext, ComponentsContext, FrameCheckContext, RecordFrameContext, RecordScopeProvider, ResourceContext, RouteSourceContext, ScreenStateProvider, maybeThen, pageAccessFor, policyRecordOf, recordKeyOf, resolveRoute, retryRecordRead, settle, useApp, useApplicationSnapshot, useAvailabilityResolver, useChromeT, useComponent, useCtx, useCurrentUserId, useFieldAccess, useFormController, useFrameImplementations, useFrameT, useI18nCaps, useList, useNavigationGuardText, useOne, useOptionalDataRuntime, useOptionalRouterAdapter, useOptionalT, usePageAccess, usePageScreen, usePermissions, usePlatformStorage, useProjectedRecord, usePublishDocVariant, useQuickEntry, useRecordFrame, useRecordTitle, useRouteListImplementation, useRouteNavigate, useRouteRecordId, useScope, useT, useTestIds, useTranslate, useVisibilityCtx } from "@burojs/react";
|
|
9
|
+
import { FORM_SLOT, addressesCapability, builtinFrames, canonicalEntityNodeKey, compilePageScreen, declaredText, dtoFields, evaluateVisible, fieldSensitivity, formatEntityRef, hasListPage, hrefFor, humanizeKey, isCustomPageDef, isScreenFailure, issueOfInput, matchPath, navHrefFor, normalizeEntityRef, normalizeIssueInputs, pageParamNames, parsePageParams, resolveNavLabel, resolveResourceLabel, resourceKey, routeResourceKey, routeServerIssues, scopeColumnOf, snapshotAppFrames, snapshotBlockLookup, snapshotFrameLookup, successNoticesOf } from "@burojs/core";
|
|
10
|
+
import { ActionRouteContext, ComponentsContext, FrameCheckContext, ORIGIN_DEPTH, RecordFrameContext, RecordScopeProvider, ResourceContext, RouteSourceContext, ScreenStateProvider, maybeThen, pageAccessFor, policyRecordOf, recordKeyOf, recordRouteAvailable, resolveRoute, retryRecordRead, settle, useApp, useApplicationSnapshot, useAvailabilityResolver, useChromeT, useComponent, useCtx, useCurrentUserId, useFieldAccess, useFormController, useFrameImplementations, useFrameT, useI18nCaps, useList, useNavigationGuardText, useOne, useOptionalDataRuntime, useOptionalRouterAdapter, useOptionalT, usePageAccess, usePageScreen, usePermissions, usePlatformStorage, useProjectedRecord, usePublishDocVariant, useQuickEntry, useRecordFrame, useRecordTitle, useReturnTarget, useRouteListImplementation, useRouteNavigate, useRouteRecordId, useScope, useT, useTestIds, useTranslate, useVisibilityCtx } from "@burojs/react";
|
|
11
11
|
import { Suspense, createContext, useCallback, useContext, useEffect, useId, useMemo, useRef, useState, useSyncExternalStore } from "react";
|
|
12
12
|
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
13
13
|
//#region src/resource-key.ts
|
|
@@ -951,15 +951,21 @@ function RecordFrameHost(props) {
|
|
|
951
951
|
edited
|
|
952
952
|
]);
|
|
953
953
|
const notify = useNoticeSink(resource);
|
|
954
|
+
const routeScreen = useIsRouteScreen();
|
|
955
|
+
const leavesOnCreate = mode === "create" && routeScreen;
|
|
956
|
+
const [created, setCreated] = useState(false);
|
|
957
|
+
const createdRef = useRef(false);
|
|
954
958
|
const latest = useRef({
|
|
955
959
|
controller,
|
|
956
960
|
onSuccess: props.onSuccess,
|
|
957
|
-
notify
|
|
961
|
+
notify,
|
|
962
|
+
leavesOnCreate
|
|
958
963
|
});
|
|
959
964
|
latest.current = {
|
|
960
965
|
controller,
|
|
961
966
|
onSuccess: props.onSuccess,
|
|
962
|
-
notify
|
|
967
|
+
notify,
|
|
968
|
+
leavesOnCreate
|
|
963
969
|
};
|
|
964
970
|
const mounted = useRef(true);
|
|
965
971
|
useEffect(() => {
|
|
@@ -969,6 +975,7 @@ function RecordFrameHost(props) {
|
|
|
969
975
|
};
|
|
970
976
|
}, []);
|
|
971
977
|
const submit = useCallback(async () => {
|
|
978
|
+
if (createdRef.current) return;
|
|
972
979
|
setSubmitError(void 0);
|
|
973
980
|
setSubmitApplied(void 0);
|
|
974
981
|
setFailed(NO_ISSUES);
|
|
@@ -976,6 +983,10 @@ function RecordFrameHost(props) {
|
|
|
976
983
|
atFailure.current = {};
|
|
977
984
|
const outcome = await latest.current.controller.submit();
|
|
978
985
|
if (!mounted.current) return;
|
|
986
|
+
if (latest.current.leavesOnCreate && (outcome.status === "submitted" || outcome.status === "failed" && outcome.applied === "yes")) {
|
|
987
|
+
createdRef.current = true;
|
|
988
|
+
setCreated(true);
|
|
989
|
+
}
|
|
979
990
|
if (outcome.status === "submitted") {
|
|
980
991
|
const notices = successNoticesOf(outcome.result);
|
|
981
992
|
if (notices.length > 0) latest.current.notify(notices);
|
|
@@ -1015,7 +1026,6 @@ function RecordFrameHost(props) {
|
|
|
1015
1026
|
]);
|
|
1016
1027
|
const { chooseType } = props;
|
|
1017
1028
|
const typedAndAccepted = mode === "create" && controller.typeOffer !== null && controller.failure === null;
|
|
1018
|
-
const routeScreen = useIsRouteScreen();
|
|
1019
1029
|
const adapter = useOptionalRouterAdapter();
|
|
1020
1030
|
const hostRouter = routeScreen ? adapter : null;
|
|
1021
1031
|
const guardText = useNavigationGuardText();
|
|
@@ -1057,6 +1067,7 @@ function RecordFrameHost(props) {
|
|
|
1057
1067
|
submitError,
|
|
1058
1068
|
...submitApplied === void 0 ? {} : { submitApplied },
|
|
1059
1069
|
serverIssues: shownFailed,
|
|
1070
|
+
...created ? { created: true } : {},
|
|
1060
1071
|
submit,
|
|
1061
1072
|
root
|
|
1062
1073
|
}), [
|
|
@@ -1070,6 +1081,7 @@ function RecordFrameHost(props) {
|
|
|
1070
1081
|
submitError,
|
|
1071
1082
|
submitApplied,
|
|
1072
1083
|
shownFailed,
|
|
1084
|
+
created,
|
|
1073
1085
|
submit
|
|
1074
1086
|
]);
|
|
1075
1087
|
usePublishDocVariant(mode === "show" ? resource : void 0, record);
|
|
@@ -1257,6 +1269,24 @@ function usePublishedRecordTrail(key) {
|
|
|
1257
1269
|
return current !== null && current.key === key ? current : null;
|
|
1258
1270
|
}
|
|
1259
1271
|
//#endregion
|
|
1272
|
+
//#region src/layout/record-targets.ts
|
|
1273
|
+
function isShowAvailable(registry, resource) {
|
|
1274
|
+
return recordRouteAvailable(registry, resource, "show");
|
|
1275
|
+
}
|
|
1276
|
+
function showTarget(resource, id) {
|
|
1277
|
+
return {
|
|
1278
|
+
resource,
|
|
1279
|
+
action: "show",
|
|
1280
|
+
params: { id }
|
|
1281
|
+
};
|
|
1282
|
+
}
|
|
1283
|
+
function listTarget(resource) {
|
|
1284
|
+
return {
|
|
1285
|
+
resource,
|
|
1286
|
+
action: "list"
|
|
1287
|
+
};
|
|
1288
|
+
}
|
|
1289
|
+
//#endregion
|
|
1260
1290
|
//#region src/layout/route-trail.ts
|
|
1261
1291
|
function useTrailContext() {
|
|
1262
1292
|
const app = useApp();
|
|
@@ -1284,7 +1314,8 @@ function groupCrumbs(ctx, chain) {
|
|
|
1284
1314
|
const key = canonicalNavKey(group);
|
|
1285
1315
|
return {
|
|
1286
1316
|
label: resolveNavLabel(ctx.caps, `groups.${key}`, declaredText("chrome", group, "label")),
|
|
1287
|
-
href: groupCrumbHref(ctx.registry, key, ctx.isVisible)
|
|
1317
|
+
href: groupCrumbHref(ctx.registry, key, ctx.isVisible),
|
|
1318
|
+
jump: true
|
|
1288
1319
|
};
|
|
1289
1320
|
});
|
|
1290
1321
|
}
|
|
@@ -1300,8 +1331,68 @@ function recordActionOf(match) {
|
|
|
1300
1331
|
default: return;
|
|
1301
1332
|
}
|
|
1302
1333
|
}
|
|
1334
|
+
function pathOf(href) {
|
|
1335
|
+
const path = href.split(/[?#]/, 1)[0] ?? "";
|
|
1336
|
+
return path.length > 1 && path.endsWith("/") ? path.slice(0, -1) : path;
|
|
1337
|
+
}
|
|
1338
|
+
function originTrail(ctx, origin, depth = ORIGIN_DEPTH) {
|
|
1339
|
+
const match = matchPath(ctx.registry, pathOf(origin.href), { isVisible: ctx.isVisible });
|
|
1340
|
+
if (match === null) return void 0;
|
|
1341
|
+
switch (match.kind) {
|
|
1342
|
+
case "resource": {
|
|
1343
|
+
const key = canonicalNavKey(match);
|
|
1344
|
+
if (match.action === "list") return {
|
|
1345
|
+
leading: groupCrumbs(ctx, match.groupChain),
|
|
1346
|
+
resource: key,
|
|
1347
|
+
resourceHref: origin.href,
|
|
1348
|
+
segments: []
|
|
1349
|
+
};
|
|
1350
|
+
const def = ctx.registry.get(key);
|
|
1351
|
+
const crumb = {
|
|
1352
|
+
label: match.action === "create" ? ctx.t("breadcrumbs.create") : origin.title || (match.id === void 0 ? "" : `#${match.id}`),
|
|
1353
|
+
href: origin.href
|
|
1354
|
+
};
|
|
1355
|
+
const parent = depth > 1 && origin.origin !== void 0 ? originTrail(ctx, origin.origin, depth - 1) : void 0;
|
|
1356
|
+
if (parent !== void 0) return {
|
|
1357
|
+
...parent,
|
|
1358
|
+
segments: [...parent.segments, crumb]
|
|
1359
|
+
};
|
|
1360
|
+
return {
|
|
1361
|
+
leading: groupCrumbs(ctx, match.groupChain),
|
|
1362
|
+
resource: key,
|
|
1363
|
+
...def !== void 0 && hasListPage(def) ? { resourceHref: hrefFor(ctx.registry, key) } : {},
|
|
1364
|
+
segments: [crumb]
|
|
1365
|
+
};
|
|
1366
|
+
}
|
|
1367
|
+
case "custom-page": {
|
|
1368
|
+
const trail = headerTrail(ctx, match);
|
|
1369
|
+
if (trail === null || trail.pageLabel === void 0) return void 0;
|
|
1370
|
+
return {
|
|
1371
|
+
leading: trail.leadingSegments ?? [],
|
|
1372
|
+
segments: [{
|
|
1373
|
+
label: trail.pageLabel,
|
|
1374
|
+
href: origin.href
|
|
1375
|
+
}]
|
|
1376
|
+
};
|
|
1377
|
+
}
|
|
1378
|
+
case "group-index": {
|
|
1379
|
+
const crumbs = groupCrumbs(ctx, match.groupChain);
|
|
1380
|
+
const own = crumbs[crumbs.length - 1];
|
|
1381
|
+
if (own === void 0) return void 0;
|
|
1382
|
+
return {
|
|
1383
|
+
leading: crumbs.slice(0, -1),
|
|
1384
|
+
segments: [{
|
|
1385
|
+
label: own.label,
|
|
1386
|
+
href: origin.href
|
|
1387
|
+
}]
|
|
1388
|
+
};
|
|
1389
|
+
}
|
|
1390
|
+
default: return;
|
|
1391
|
+
}
|
|
1392
|
+
}
|
|
1303
1393
|
function recordTrail(ctx, input) {
|
|
1304
|
-
const { resource, action, id } = input;
|
|
1394
|
+
const { resource, action, id, origin } = input;
|
|
1395
|
+
if (origin !== void 0 && (action === "create" || id !== "")) return originRooted(ctx, input, origin);
|
|
1305
1396
|
const leadingSegments = nonEmpty(groupCrumbs(ctx, input.groupChain));
|
|
1306
1397
|
if (action !== "create" && id === "") return {
|
|
1307
1398
|
resource,
|
|
@@ -1321,6 +1412,42 @@ function recordTrail(ctx, input) {
|
|
|
1321
1412
|
middleSegments
|
|
1322
1413
|
};
|
|
1323
1414
|
}
|
|
1415
|
+
function originRooted(ctx, input, origin) {
|
|
1416
|
+
const { resource, action, id } = input;
|
|
1417
|
+
const recordLabel = input.title || id;
|
|
1418
|
+
const drawn = new Set(origin.segments.flatMap((crumb) => crumb.href === void 0 ? [] : [pathOf(crumb.href)]));
|
|
1419
|
+
const own = action === "edit" ? [isShowAvailable(ctx.registry, resource) ? {
|
|
1420
|
+
label: recordLabel,
|
|
1421
|
+
href: resolveRoute(showTarget(resource, id), ctx.registry)
|
|
1422
|
+
} : { label: recordLabel }].filter((crumb) => crumb.href === void 0 || !drawn.has(pathOf(crumb.href))) : [];
|
|
1423
|
+
const pageLabel = action === "create" ? ctx.t("breadcrumbs.create") : action === "edit" ? ctx.t("breadcrumbs.edit") : recordLabel;
|
|
1424
|
+
const recordId = action === "create" ? void 0 : id;
|
|
1425
|
+
const middleSegments = nonEmpty([...origin.segments, ...own]);
|
|
1426
|
+
if (origin.resource !== void 0 && ctx.registry.get(origin.resource) === ctx.registry.get(resource)) return {
|
|
1427
|
+
resource,
|
|
1428
|
+
recordId,
|
|
1429
|
+
pageLabel,
|
|
1430
|
+
leadingSegments: nonEmpty([...origin.leading]),
|
|
1431
|
+
...origin.resourceHref === void 0 ? {} : { resourceHref: origin.resourceHref },
|
|
1432
|
+
middleSegments
|
|
1433
|
+
};
|
|
1434
|
+
const def = origin.resource === void 0 ? void 0 : ctx.registry.get(origin.resource);
|
|
1435
|
+
const resourceCrumb = origin.resource === void 0 ? [] : [{
|
|
1436
|
+
label: resolveResourceLabel(ctx.caps, origin.resource, "plural", def),
|
|
1437
|
+
...origin.resourceHref === void 0 ? {} : { href: origin.resourceHref }
|
|
1438
|
+
}];
|
|
1439
|
+
return {
|
|
1440
|
+
resource,
|
|
1441
|
+
recordId,
|
|
1442
|
+
pageLabel,
|
|
1443
|
+
rootSegments: [
|
|
1444
|
+
...origin.leading,
|
|
1445
|
+
...resourceCrumb,
|
|
1446
|
+
...origin.segments,
|
|
1447
|
+
...own
|
|
1448
|
+
]
|
|
1449
|
+
};
|
|
1450
|
+
}
|
|
1324
1451
|
function recordTrailRoute(registry, match) {
|
|
1325
1452
|
if (match === null || match.kind !== "resource") return null;
|
|
1326
1453
|
const resource = canonicalNavKey(match);
|
|
@@ -1514,10 +1641,9 @@ function RecordShow({ resource, id, recordId, record, shown }) {
|
|
|
1514
1641
|
})] });
|
|
1515
1642
|
}
|
|
1516
1643
|
function RecordEdit({ resource, id, recordId, record, shown }) {
|
|
1517
|
-
const app = useApp();
|
|
1518
1644
|
const navigate = useRouteNavigate();
|
|
1519
1645
|
const mounted = useMountedRef();
|
|
1520
|
-
const back =
|
|
1646
|
+
const back = useReturnTarget(resource, "edit", id);
|
|
1521
1647
|
return jsxs("div", { children: [jsx(RecordRouteHeader, {
|
|
1522
1648
|
resource,
|
|
1523
1649
|
action: "edit",
|
|
@@ -1528,9 +1654,9 @@ function RecordEdit({ resource, id, recordId, record, shown }) {
|
|
|
1528
1654
|
mode: "edit",
|
|
1529
1655
|
recordId,
|
|
1530
1656
|
record,
|
|
1531
|
-
onCancel: () => navigate(back),
|
|
1657
|
+
onCancel: () => navigate(back, { origin: "inherit" }),
|
|
1532
1658
|
onSuccess: () => {
|
|
1533
|
-
if (mounted.current) navigate(back);
|
|
1659
|
+
if (mounted.current) navigate(back, { origin: "inherit" });
|
|
1534
1660
|
}
|
|
1535
1661
|
})] });
|
|
1536
1662
|
}
|
|
@@ -1544,6 +1670,7 @@ function RecordCreate({ resource, defaults }) {
|
|
|
1544
1670
|
const mounted = useMountedRef();
|
|
1545
1671
|
const adapter = useOptionalRouterAdapter();
|
|
1546
1672
|
const list = listTarget(resource);
|
|
1673
|
+
const back = useReturnTarget(resource, "create");
|
|
1547
1674
|
const type = adapter?.searchParams.get("type") ?? void 0;
|
|
1548
1675
|
return jsxs("div", { children: [jsx(RecordRouteHeader, {
|
|
1549
1676
|
resource,
|
|
@@ -1556,11 +1683,11 @@ function RecordCreate({ resource, defaults }) {
|
|
|
1556
1683
|
mode: "create",
|
|
1557
1684
|
...type === void 0 ? {} : { type },
|
|
1558
1685
|
...defaults === void 0 ? {} : { defaults },
|
|
1559
|
-
onCancel: () => navigate(
|
|
1686
|
+
onCancel: () => navigate(back, { origin: "inherit" }),
|
|
1560
1687
|
onSuccess: (result) => {
|
|
1561
1688
|
if (!mounted.current) return;
|
|
1562
1689
|
const id = createdId(result);
|
|
1563
|
-
navigate(id
|
|
1690
|
+
navigate(id !== void 0 && isShowAvailable(app.registry, resource) ? showTarget(resource, id) : list, { origin: "inherit" });
|
|
1564
1691
|
}
|
|
1565
1692
|
})
|
|
1566
1693
|
})] });
|
|
@@ -1826,6 +1953,6 @@ function NavAreaPage() {
|
|
|
1826
1953
|
});
|
|
1827
1954
|
}
|
|
1828
1955
|
//#endregion
|
|
1829
|
-
export {
|
|
1956
|
+
export { useLayoutResolver as A, useMatch as C, BUILTIN_LAYOUTS as D, useResourceVisible as E, canonicalResourceKey$1 as F, TabsLayout as M, SecondarySidebarLayout as N, layoutSlot as O, canonicalNavKey as P, useHref as S, usePageAllowed as T, RecordScreen as _, useRecordVisit as a, ResourceListPage as b, headerTrail as c, recordTrailRoute as d, useTrailContext as f, usePublishedRecordTrail as g, usePublishRecordTrail as h, RecordPage as i, useReportUnresolvedLayouts as j, useLayout as k, originTrail as l, recordTrailKey as m, NavAreaPage as n, RECENT_LABEL_KEYS as o, isShowAvailable as p, ResourcePageRouter as r, readRecentRecords as s, NavAreaNotFound as t, recordTrail as u, useResourceDimensionOptions as v, useNavVisible as w, NavChromeMountedProvider as x, PlannedBody as y };
|
|
1830
1957
|
|
|
1831
|
-
//# sourceMappingURL=resource-page-router-
|
|
1958
|
+
//# sourceMappingURL=resource-page-router-BRpUnhay.js.map
|