@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
|
@@ -1,141 +0,0 @@
|
|
|
1
|
-
import { i as useProjectedRows, n as RowContent, r as RowOpener, t as ITEM_LAYER_CLASS } from "./related-rows--uP2iKQC.js";
|
|
2
|
-
import { useOptionalLocale, useOptionalT, useTestIds } from "@burojs/react";
|
|
3
|
-
import { useMemo } from "react";
|
|
4
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
5
|
-
//#region src/blocks/related-list-timeline.tsx
|
|
6
|
-
function dayOf(value) {
|
|
7
|
-
if (typeof value === "string") {
|
|
8
|
-
const only = /^(\d{4})-(\d{2})-(\d{2})$/u.exec(value);
|
|
9
|
-
if (only !== null) return {
|
|
10
|
-
date: new Date(Number(only[1]), Number(only[2]) - 1, Number(only[3])),
|
|
11
|
-
timed: false
|
|
12
|
-
};
|
|
13
|
-
}
|
|
14
|
-
if (typeof value !== "string" && typeof value !== "number" && !(value instanceof Date)) return void 0;
|
|
15
|
-
const date = new Date(value);
|
|
16
|
-
return Number.isNaN(date.getTime()) ? void 0 : {
|
|
17
|
-
date,
|
|
18
|
-
timed: true
|
|
19
|
-
};
|
|
20
|
-
}
|
|
21
|
-
const dayKey = (date) => `${date.getFullYear()}-${date.getMonth()}-${date.getDate()}`;
|
|
22
|
-
function RelatedTimeline({ resource, placementKey, rows, item, columns, rowQuick, rowHref, empty, axis }) {
|
|
23
|
-
const ids = useTestIds();
|
|
24
|
-
const t = useOptionalT();
|
|
25
|
-
const locale = useOptionalLocale();
|
|
26
|
-
const { project, drawn, title } = useProjectedRows(resource, rows, item);
|
|
27
|
-
const formats = useMemo(() => {
|
|
28
|
-
try {
|
|
29
|
-
return {
|
|
30
|
-
day: new Intl.DateTimeFormat(locale, { dateStyle: "full" }),
|
|
31
|
-
time: new Intl.DateTimeFormat(locale, { timeStyle: "short" })
|
|
32
|
-
};
|
|
33
|
-
} catch {
|
|
34
|
-
return {
|
|
35
|
-
day: new Intl.DateTimeFormat(void 0, { dateStyle: "full" }),
|
|
36
|
-
time: new Intl.DateTimeFormat(void 0, { timeStyle: "short" })
|
|
37
|
-
};
|
|
38
|
-
}
|
|
39
|
-
}, [locale]);
|
|
40
|
-
const groups = useMemo(() => {
|
|
41
|
-
const out = [];
|
|
42
|
-
if (axis === void 0) return out;
|
|
43
|
-
const undated = [];
|
|
44
|
-
for (const row of rows) {
|
|
45
|
-
const day = dayOf(project(row)[axis]);
|
|
46
|
-
if (day === void 0) {
|
|
47
|
-
undated.push({
|
|
48
|
-
row,
|
|
49
|
-
at: void 0,
|
|
50
|
-
timed: false
|
|
51
|
-
});
|
|
52
|
-
continue;
|
|
53
|
-
}
|
|
54
|
-
const key = dayKey(day.date);
|
|
55
|
-
let group = out.find((entry) => entry.key === key);
|
|
56
|
-
if (group === void 0) {
|
|
57
|
-
group = {
|
|
58
|
-
key,
|
|
59
|
-
heading: day.date,
|
|
60
|
-
entries: []
|
|
61
|
-
};
|
|
62
|
-
out.push(group);
|
|
63
|
-
}
|
|
64
|
-
group.entries.push({
|
|
65
|
-
row,
|
|
66
|
-
at: day.date,
|
|
67
|
-
timed: day.timed
|
|
68
|
-
});
|
|
69
|
-
}
|
|
70
|
-
if (undated.length > 0) out.push({
|
|
71
|
-
key: "undated",
|
|
72
|
-
heading: void 0,
|
|
73
|
-
entries: undated
|
|
74
|
-
});
|
|
75
|
-
return out;
|
|
76
|
-
}, [
|
|
77
|
-
rows,
|
|
78
|
-
axis,
|
|
79
|
-
project
|
|
80
|
-
]);
|
|
81
|
-
const headingOf = (group) => group.heading === void 0 ? t("timeline.undated", { defaultValue: "Undated" }) : formats.day.format(group.heading);
|
|
82
|
-
if (groups.length === 0) return jsx("p", {
|
|
83
|
-
"data-buro-region": "empty",
|
|
84
|
-
className: "text-sm text-muted-foreground",
|
|
85
|
-
children: (rows.length === 0 ? empty : void 0) ?? t("timeline.empty", { defaultValue: "No entries" })
|
|
86
|
-
});
|
|
87
|
-
return jsxs("div", {
|
|
88
|
-
className: "relative flex flex-col gap-4 pl-5",
|
|
89
|
-
children: [jsx("span", {
|
|
90
|
-
"data-buro-timeline-rail": "",
|
|
91
|
-
"aria-hidden": "true",
|
|
92
|
-
className: "absolute top-1 bottom-1 left-2 w-px bg-border"
|
|
93
|
-
}), groups.map((group) => jsxs("section", {
|
|
94
|
-
"aria-label": headingOf(group),
|
|
95
|
-
className: "flex flex-col gap-1",
|
|
96
|
-
children: [jsx("h3", {
|
|
97
|
-
className: "text-xs text-muted-foreground",
|
|
98
|
-
children: headingOf(group)
|
|
99
|
-
}), jsx("ol", {
|
|
100
|
-
className: "flex flex-col",
|
|
101
|
-
children: group.entries.map(({ row, at, timed }) => jsxs("li", {
|
|
102
|
-
"data-testid": ids.timelineEntry(placementKey, String(row.id)),
|
|
103
|
-
className: "relative flex min-w-0 items-baseline gap-3 rounded-md py-1.5 pr-2",
|
|
104
|
-
children: [
|
|
105
|
-
jsx("span", {
|
|
106
|
-
"data-buro-timeline-dot": "",
|
|
107
|
-
"aria-hidden": "true",
|
|
108
|
-
className: "absolute top-2.5 -left-[1.1rem] size-3 rounded-full border-2 border-background bg-muted-foreground"
|
|
109
|
-
}),
|
|
110
|
-
jsx(RowOpener, {
|
|
111
|
-
title: title(row),
|
|
112
|
-
href: rowHref?.(row),
|
|
113
|
-
quick: rowQuick?.(row) ?? null
|
|
114
|
-
}),
|
|
115
|
-
timed && at !== void 0 ? jsx("time", {
|
|
116
|
-
dateTime: at.toISOString(),
|
|
117
|
-
className: "w-16 shrink-0 whitespace-nowrap text-xs tabular-nums text-muted-foreground",
|
|
118
|
-
children: formats.time.format(at)
|
|
119
|
-
}) : null,
|
|
120
|
-
jsx("div", {
|
|
121
|
-
className: `${ITEM_LAYER_CLASS} min-w-0 flex-1`,
|
|
122
|
-
children: jsx(RowContent, {
|
|
123
|
-
resource,
|
|
124
|
-
raw: row,
|
|
125
|
-
row: project(row),
|
|
126
|
-
item,
|
|
127
|
-
drawn,
|
|
128
|
-
columns,
|
|
129
|
-
variant: "entry"
|
|
130
|
-
})
|
|
131
|
-
})
|
|
132
|
-
]
|
|
133
|
-
}, row.id))
|
|
134
|
-
})]
|
|
135
|
-
}, group.key))]
|
|
136
|
-
});
|
|
137
|
-
}
|
|
138
|
-
//#endregion
|
|
139
|
-
export { RelatedTimeline };
|
|
140
|
-
|
|
141
|
-
//# sourceMappingURL=related-list-timeline-CRh7xwLD.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"related-list-timeline-CRh7xwLD.js","names":[],"sources":["../src/blocks/related-list-timeline.tsx"],"sourcesContent":["'use client';\nimport { useOptionalLocale, useOptionalT, useTestIds } from '@burojs/react';\nimport { type ReactElement, useMemo } from 'react';\nimport {\n ITEM_LAYER_CLASS,\n type ListRow,\n type RelatedRowsProps,\n RowContent,\n RowOpener,\n useProjectedRows,\n} from './related-rows.js';\n\n/** The date of a row's axis value, else undefined. A date-only value is the local day it names. */\nfunction dayOf(value: unknown): { readonly date: Date; readonly timed: boolean } | undefined {\n if (typeof value === 'string') {\n const only = /^(\\d{4})-(\\d{2})-(\\d{2})$/u.exec(value);\n if (only !== null)\n return {\n date: new Date(Number(only[1]), Number(only[2]) - 1, Number(only[3])),\n timed: false,\n };\n }\n if (typeof value !== 'string' && typeof value !== 'number' && !(value instanceof Date))\n return undefined;\n const date = new Date(value);\n return Number.isNaN(date.getTime()) ? undefined : { date, timed: true };\n}\n\nconst dayKey = (date: Date) => `${date.getFullYear()}-${date.getMonth()}-${date.getDate()}`;\n\ninterface Group {\n readonly key: string;\n /** The day the group names; undefined for the undated group. */\n readonly heading: Date | undefined;\n readonly entries: {\n readonly row: ListRow;\n readonly at: Date | undefined;\n readonly timed: boolean;\n }[];\n}\n\n/**\n * A related list as a timeline (`view: 'timeline'`, P6 3.13): one column grouped by the local\n * day of `axis`, the first field of the list's effective initial sort (a placement's own\n * `initial.sort`, else the target's `views.list.initial.sort`, T4 D9), headings formatted by the active locale,\n * entries in the list's own (declared) order, each the time and then the item. There is no sort\n * control: the axis is the declared sort.\n *\n * The axis is read from the projected row, as every other field is: a viewer who may not see it\n * gets no day and no time. A row whose axis reads nothing (hidden, empty or unparseable) is kept\n * in one trailing \"Undated\" group, so the entries drawn are the rows the list counts.\n */\nexport function RelatedTimeline({\n resource,\n placementKey,\n rows,\n item,\n columns,\n rowQuick,\n rowHref,\n empty,\n axis,\n}: RelatedRowsProps & {\n /** The axis field; undefined (no initial sort) draws no group. */\n readonly axis: string | undefined;\n}): ReactElement {\n const ids = useTestIds();\n const t = useOptionalT();\n const locale = useOptionalLocale();\n const { project, drawn, title } = useProjectedRows(resource, rows, item);\n const formats = useMemo(() => {\n try {\n return {\n day: new Intl.DateTimeFormat(locale, { dateStyle: 'full' }),\n time: new Intl.DateTimeFormat(locale, { timeStyle: 'short' }),\n };\n } catch {\n return {\n day: new Intl.DateTimeFormat(undefined, { dateStyle: 'full' }),\n time: new Intl.DateTimeFormat(undefined, { timeStyle: 'short' }),\n };\n }\n }, [locale]);\n const groups = useMemo(() => {\n const out: Group[] = [];\n if (axis === undefined) return out;\n const undated: Group['entries'] = [];\n for (const row of rows) {\n const day = dayOf(project(row)[axis]);\n if (day === undefined) {\n undated.push({ row, at: undefined, timed: false });\n continue;\n }\n const key = dayKey(day.date);\n let group = out.find((entry) => entry.key === key);\n if (group === undefined) {\n group = { key, heading: day.date, entries: [] };\n out.push(group);\n }\n group.entries.push({ row, at: day.date, timed: day.timed });\n }\n if (undated.length > 0) out.push({ key: 'undated', heading: undefined, entries: undated });\n return out;\n }, [rows, axis, project]);\n const headingOf = (group: Group): string =>\n group.heading === undefined\n ? t('timeline.undated', { defaultValue: 'Undated' })\n : formats.day.format(group.heading);\n if (groups.length === 0)\n return (\n <p data-buro-region=\"empty\" className=\"text-sm text-muted-foreground\">\n {(rows.length === 0 ? empty : undefined) ??\n t('timeline.empty', { defaultValue: 'No entries' })}\n </p>\n );\n // One rail down the whole list (a border per day broke at every heading), a dot per entry on it;\n // the day headings are the axis' labels, small and muted, and the entries carry the weight.\n return (\n <div className=\"relative flex flex-col gap-4 pl-5\">\n <span\n data-buro-timeline-rail=\"\"\n aria-hidden=\"true\"\n className=\"absolute top-1 bottom-1 left-2 w-px bg-border\"\n />\n {groups.map((group) => (\n <section key={group.key} aria-label={headingOf(group)} className=\"flex flex-col gap-1\">\n <h3 className=\"text-xs text-muted-foreground\">{headingOf(group)}</h3>\n <ol className=\"flex flex-col\">\n {group.entries.map(({ row, at, timed }) => (\n <li\n key={row.id}\n data-testid={ids.timelineEntry(placementKey, String(row.id))}\n className=\"relative flex min-w-0 items-baseline gap-3 rounded-md py-1.5 pr-2\"\n >\n <span\n data-buro-timeline-dot=\"\"\n aria-hidden=\"true\"\n className=\"absolute top-2.5 -left-[1.1rem] size-3 rounded-full border-2 border-background bg-muted-foreground\"\n />\n <RowOpener\n title={title(row)}\n href={rowHref?.(row)}\n quick={rowQuick?.(row) ?? null}\n />\n {timed && at !== undefined ? (\n <time\n dateTime={at.toISOString()}\n className=\"w-16 shrink-0 whitespace-nowrap text-xs tabular-nums text-muted-foreground\"\n >\n {formats.time.format(at)}\n </time>\n ) : null}\n <div className={`${ITEM_LAYER_CLASS} min-w-0 flex-1`}>\n <RowContent\n resource={resource}\n raw={row}\n row={project(row)}\n item={item}\n drawn={drawn}\n columns={columns}\n variant=\"entry\"\n />\n </div>\n </li>\n ))}\n </ol>\n </section>\n ))}\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1,141 +0,0 @@
|
|
|
1
|
-
const require_related_rows = require("./related-rows-MPnLCmPF.cjs");
|
|
2
|
-
let _burojs_react = require("@burojs/react");
|
|
3
|
-
let react = require("react");
|
|
4
|
-
let react_jsx_runtime = require("react/jsx-runtime");
|
|
5
|
-
//#region src/blocks/related-list-timeline.tsx
|
|
6
|
-
function dayOf(value) {
|
|
7
|
-
if (typeof value === "string") {
|
|
8
|
-
const only = /^(\d{4})-(\d{2})-(\d{2})$/u.exec(value);
|
|
9
|
-
if (only !== null) return {
|
|
10
|
-
date: new Date(Number(only[1]), Number(only[2]) - 1, Number(only[3])),
|
|
11
|
-
timed: false
|
|
12
|
-
};
|
|
13
|
-
}
|
|
14
|
-
if (typeof value !== "string" && typeof value !== "number" && !(value instanceof Date)) return void 0;
|
|
15
|
-
const date = new Date(value);
|
|
16
|
-
return Number.isNaN(date.getTime()) ? void 0 : {
|
|
17
|
-
date,
|
|
18
|
-
timed: true
|
|
19
|
-
};
|
|
20
|
-
}
|
|
21
|
-
const dayKey = (date) => `${date.getFullYear()}-${date.getMonth()}-${date.getDate()}`;
|
|
22
|
-
function RelatedTimeline({ resource, placementKey, rows, item, columns, rowQuick, rowHref, empty, axis }) {
|
|
23
|
-
const ids = (0, _burojs_react.useTestIds)();
|
|
24
|
-
const t = (0, _burojs_react.useOptionalT)();
|
|
25
|
-
const locale = (0, _burojs_react.useOptionalLocale)();
|
|
26
|
-
const { project, drawn, title } = require_related_rows.useProjectedRows(resource, rows, item);
|
|
27
|
-
const formats = (0, react.useMemo)(() => {
|
|
28
|
-
try {
|
|
29
|
-
return {
|
|
30
|
-
day: new Intl.DateTimeFormat(locale, { dateStyle: "full" }),
|
|
31
|
-
time: new Intl.DateTimeFormat(locale, { timeStyle: "short" })
|
|
32
|
-
};
|
|
33
|
-
} catch {
|
|
34
|
-
return {
|
|
35
|
-
day: new Intl.DateTimeFormat(void 0, { dateStyle: "full" }),
|
|
36
|
-
time: new Intl.DateTimeFormat(void 0, { timeStyle: "short" })
|
|
37
|
-
};
|
|
38
|
-
}
|
|
39
|
-
}, [locale]);
|
|
40
|
-
const groups = (0, react.useMemo)(() => {
|
|
41
|
-
const out = [];
|
|
42
|
-
if (axis === void 0) return out;
|
|
43
|
-
const undated = [];
|
|
44
|
-
for (const row of rows) {
|
|
45
|
-
const day = dayOf(project(row)[axis]);
|
|
46
|
-
if (day === void 0) {
|
|
47
|
-
undated.push({
|
|
48
|
-
row,
|
|
49
|
-
at: void 0,
|
|
50
|
-
timed: false
|
|
51
|
-
});
|
|
52
|
-
continue;
|
|
53
|
-
}
|
|
54
|
-
const key = dayKey(day.date);
|
|
55
|
-
let group = out.find((entry) => entry.key === key);
|
|
56
|
-
if (group === void 0) {
|
|
57
|
-
group = {
|
|
58
|
-
key,
|
|
59
|
-
heading: day.date,
|
|
60
|
-
entries: []
|
|
61
|
-
};
|
|
62
|
-
out.push(group);
|
|
63
|
-
}
|
|
64
|
-
group.entries.push({
|
|
65
|
-
row,
|
|
66
|
-
at: day.date,
|
|
67
|
-
timed: day.timed
|
|
68
|
-
});
|
|
69
|
-
}
|
|
70
|
-
if (undated.length > 0) out.push({
|
|
71
|
-
key: "undated",
|
|
72
|
-
heading: void 0,
|
|
73
|
-
entries: undated
|
|
74
|
-
});
|
|
75
|
-
return out;
|
|
76
|
-
}, [
|
|
77
|
-
rows,
|
|
78
|
-
axis,
|
|
79
|
-
project
|
|
80
|
-
]);
|
|
81
|
-
const headingOf = (group) => group.heading === void 0 ? t("timeline.undated", { defaultValue: "Undated" }) : formats.day.format(group.heading);
|
|
82
|
-
if (groups.length === 0) return (0, react_jsx_runtime.jsx)("p", {
|
|
83
|
-
"data-buro-region": "empty",
|
|
84
|
-
className: "text-sm text-muted-foreground",
|
|
85
|
-
children: (rows.length === 0 ? empty : void 0) ?? t("timeline.empty", { defaultValue: "No entries" })
|
|
86
|
-
});
|
|
87
|
-
return (0, react_jsx_runtime.jsxs)("div", {
|
|
88
|
-
className: "relative flex flex-col gap-4 pl-5",
|
|
89
|
-
children: [(0, react_jsx_runtime.jsx)("span", {
|
|
90
|
-
"data-buro-timeline-rail": "",
|
|
91
|
-
"aria-hidden": "true",
|
|
92
|
-
className: "absolute top-1 bottom-1 left-2 w-px bg-border"
|
|
93
|
-
}), groups.map((group) => (0, react_jsx_runtime.jsxs)("section", {
|
|
94
|
-
"aria-label": headingOf(group),
|
|
95
|
-
className: "flex flex-col gap-1",
|
|
96
|
-
children: [(0, react_jsx_runtime.jsx)("h3", {
|
|
97
|
-
className: "text-xs text-muted-foreground",
|
|
98
|
-
children: headingOf(group)
|
|
99
|
-
}), (0, react_jsx_runtime.jsx)("ol", {
|
|
100
|
-
className: "flex flex-col",
|
|
101
|
-
children: group.entries.map(({ row, at, timed }) => (0, react_jsx_runtime.jsxs)("li", {
|
|
102
|
-
"data-testid": ids.timelineEntry(placementKey, String(row.id)),
|
|
103
|
-
className: "relative flex min-w-0 items-baseline gap-3 rounded-md py-1.5 pr-2",
|
|
104
|
-
children: [
|
|
105
|
-
(0, react_jsx_runtime.jsx)("span", {
|
|
106
|
-
"data-buro-timeline-dot": "",
|
|
107
|
-
"aria-hidden": "true",
|
|
108
|
-
className: "absolute top-2.5 -left-[1.1rem] size-3 rounded-full border-2 border-background bg-muted-foreground"
|
|
109
|
-
}),
|
|
110
|
-
(0, react_jsx_runtime.jsx)(require_related_rows.RowOpener, {
|
|
111
|
-
title: title(row),
|
|
112
|
-
href: rowHref?.(row),
|
|
113
|
-
quick: rowQuick?.(row) ?? null
|
|
114
|
-
}),
|
|
115
|
-
timed && at !== void 0 ? (0, react_jsx_runtime.jsx)("time", {
|
|
116
|
-
dateTime: at.toISOString(),
|
|
117
|
-
className: "w-16 shrink-0 whitespace-nowrap text-xs tabular-nums text-muted-foreground",
|
|
118
|
-
children: formats.time.format(at)
|
|
119
|
-
}) : null,
|
|
120
|
-
(0, react_jsx_runtime.jsx)("div", {
|
|
121
|
-
className: `${require_related_rows.ITEM_LAYER_CLASS} min-w-0 flex-1`,
|
|
122
|
-
children: (0, react_jsx_runtime.jsx)(require_related_rows.RowContent, {
|
|
123
|
-
resource,
|
|
124
|
-
raw: row,
|
|
125
|
-
row: project(row),
|
|
126
|
-
item,
|
|
127
|
-
drawn,
|
|
128
|
-
columns,
|
|
129
|
-
variant: "entry"
|
|
130
|
-
})
|
|
131
|
-
})
|
|
132
|
-
]
|
|
133
|
-
}, row.id))
|
|
134
|
-
})]
|
|
135
|
-
}, group.key))]
|
|
136
|
-
});
|
|
137
|
-
}
|
|
138
|
-
//#endregion
|
|
139
|
-
exports.RelatedTimeline = RelatedTimeline;
|
|
140
|
-
|
|
141
|
-
//# sourceMappingURL=related-list-timeline-DsLdi8sv.cjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"related-list-timeline-DsLdi8sv.cjs","names":["useTestIds","useOptionalT","useOptionalLocale","useProjectedRows","useMemo","RowOpener","ITEM_LAYER_CLASS","RowContent"],"sources":["../src/blocks/related-list-timeline.tsx"],"sourcesContent":["'use client';\nimport { useOptionalLocale, useOptionalT, useTestIds } from '@burojs/react';\nimport { type ReactElement, useMemo } from 'react';\nimport {\n ITEM_LAYER_CLASS,\n type ListRow,\n type RelatedRowsProps,\n RowContent,\n RowOpener,\n useProjectedRows,\n} from './related-rows.js';\n\n/** The date of a row's axis value, else undefined. A date-only value is the local day it names. */\nfunction dayOf(value: unknown): { readonly date: Date; readonly timed: boolean } | undefined {\n if (typeof value === 'string') {\n const only = /^(\\d{4})-(\\d{2})-(\\d{2})$/u.exec(value);\n if (only !== null)\n return {\n date: new Date(Number(only[1]), Number(only[2]) - 1, Number(only[3])),\n timed: false,\n };\n }\n if (typeof value !== 'string' && typeof value !== 'number' && !(value instanceof Date))\n return undefined;\n const date = new Date(value);\n return Number.isNaN(date.getTime()) ? undefined : { date, timed: true };\n}\n\nconst dayKey = (date: Date) => `${date.getFullYear()}-${date.getMonth()}-${date.getDate()}`;\n\ninterface Group {\n readonly key: string;\n /** The day the group names; undefined for the undated group. */\n readonly heading: Date | undefined;\n readonly entries: {\n readonly row: ListRow;\n readonly at: Date | undefined;\n readonly timed: boolean;\n }[];\n}\n\n/**\n * A related list as a timeline (`view: 'timeline'`, P6 3.13): one column grouped by the local\n * day of `axis`, the first field of the list's effective initial sort (a placement's own\n * `initial.sort`, else the target's `views.list.initial.sort`, T4 D9), headings formatted by the active locale,\n * entries in the list's own (declared) order, each the time and then the item. There is no sort\n * control: the axis is the declared sort.\n *\n * The axis is read from the projected row, as every other field is: a viewer who may not see it\n * gets no day and no time. A row whose axis reads nothing (hidden, empty or unparseable) is kept\n * in one trailing \"Undated\" group, so the entries drawn are the rows the list counts.\n */\nexport function RelatedTimeline({\n resource,\n placementKey,\n rows,\n item,\n columns,\n rowQuick,\n rowHref,\n empty,\n axis,\n}: RelatedRowsProps & {\n /** The axis field; undefined (no initial sort) draws no group. */\n readonly axis: string | undefined;\n}): ReactElement {\n const ids = useTestIds();\n const t = useOptionalT();\n const locale = useOptionalLocale();\n const { project, drawn, title } = useProjectedRows(resource, rows, item);\n const formats = useMemo(() => {\n try {\n return {\n day: new Intl.DateTimeFormat(locale, { dateStyle: 'full' }),\n time: new Intl.DateTimeFormat(locale, { timeStyle: 'short' }),\n };\n } catch {\n return {\n day: new Intl.DateTimeFormat(undefined, { dateStyle: 'full' }),\n time: new Intl.DateTimeFormat(undefined, { timeStyle: 'short' }),\n };\n }\n }, [locale]);\n const groups = useMemo(() => {\n const out: Group[] = [];\n if (axis === undefined) return out;\n const undated: Group['entries'] = [];\n for (const row of rows) {\n const day = dayOf(project(row)[axis]);\n if (day === undefined) {\n undated.push({ row, at: undefined, timed: false });\n continue;\n }\n const key = dayKey(day.date);\n let group = out.find((entry) => entry.key === key);\n if (group === undefined) {\n group = { key, heading: day.date, entries: [] };\n out.push(group);\n }\n group.entries.push({ row, at: day.date, timed: day.timed });\n }\n if (undated.length > 0) out.push({ key: 'undated', heading: undefined, entries: undated });\n return out;\n }, [rows, axis, project]);\n const headingOf = (group: Group): string =>\n group.heading === undefined\n ? t('timeline.undated', { defaultValue: 'Undated' })\n : formats.day.format(group.heading);\n if (groups.length === 0)\n return (\n <p data-buro-region=\"empty\" className=\"text-sm text-muted-foreground\">\n {(rows.length === 0 ? empty : undefined) ??\n t('timeline.empty', { defaultValue: 'No entries' })}\n </p>\n );\n // One rail down the whole list (a border per day broke at every heading), a dot per entry on it;\n // the day headings are the axis' labels, small and muted, and the entries carry the weight.\n return (\n <div className=\"relative flex flex-col gap-4 pl-5\">\n <span\n data-buro-timeline-rail=\"\"\n aria-hidden=\"true\"\n className=\"absolute top-1 bottom-1 left-2 w-px bg-border\"\n />\n {groups.map((group) => (\n <section key={group.key} aria-label={headingOf(group)} className=\"flex flex-col gap-1\">\n <h3 className=\"text-xs text-muted-foreground\">{headingOf(group)}</h3>\n <ol className=\"flex flex-col\">\n {group.entries.map(({ row, at, timed }) => (\n <li\n key={row.id}\n data-testid={ids.timelineEntry(placementKey, String(row.id))}\n className=\"relative flex min-w-0 items-baseline gap-3 rounded-md py-1.5 pr-2\"\n >\n <span\n data-buro-timeline-dot=\"\"\n aria-hidden=\"true\"\n className=\"absolute top-2.5 -left-[1.1rem] size-3 rounded-full border-2 border-background bg-muted-foreground\"\n />\n <RowOpener\n title={title(row)}\n href={rowHref?.(row)}\n quick={rowQuick?.(row) ?? null}\n />\n {timed && at !== undefined ? (\n <time\n dateTime={at.toISOString()}\n className=\"w-16 shrink-0 whitespace-nowrap text-xs tabular-nums text-muted-foreground\"\n >\n {formats.time.format(at)}\n </time>\n ) : null}\n <div className={`${ITEM_LAYER_CLASS} min-w-0 flex-1`}>\n <RowContent\n resource={resource}\n raw={row}\n row={project(row)}\n item={item}\n drawn={drawn}\n columns={columns}\n variant=\"entry\"\n />\n </div>\n </li>\n ))}\n </ol>\n </section>\n ))}\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"relation-many-picker-BJhaEqxz.js","names":[],"sources":["../src/lib/error-title.ts","../src/fields/relation-many-picker.tsx"],"sourcesContent":["import { type ErrorTextCaps, resolveErrorText } from '@burojs/react';\n\n/**\n * The text a user may read for a failure a hook handed to the kit as an `Error`-typed value.\n * The value is a `DataError` at runtime when it came from a data provider (its `message` is a\n * reference, so it is resolved through the bundle) and a plain `Error` when the framework raised\n * it (its `message` is already a sentence). Anything else has nothing to show.\n */\nexport function errorTitle(error: unknown, caps: ErrorTextCaps): string | undefined {\n if (error instanceof Error) return error.message;\n if (error !== null && typeof error === 'object') {\n const { code, message } = error as { readonly code?: unknown; readonly message?: unknown };\n if (typeof code === 'string') {\n return resolveErrorText(\n {\n code,\n ...(message !== null && typeof message === 'object' ? { message } : {}),\n } as Parameters<typeof resolveErrorText>[0],\n caps,\n );\n }\n }\n return undefined;\n}\n","'use client';\n\nimport {\n type ErasedResourceHandle,\n type FilterExpression,\n type FixedFilterInput,\n type RelationCandidate,\n type RelationOpen,\n relationOpenOf,\n type ResourceRefInput,\n type ScopeSource,\n type SearchDecl,\n composeListFilter,\n normalizeFixedFilters,\n resolveResourceLabel,\n resourceKey,\n} from '@burojs/core';\nimport {\n useApp,\n useI18nCaps,\n useList,\n useListAccess,\n useOptionalT,\n useRelatedRecord,\n useTestIds,\n} from '@burojs/react';\nimport { errorTitle } from '../lib/error-title.js';\nimport { LoadError } from '../load-error/load-error-view.js';\nimport { IconCheck, IconChevronDown, IconLoader2, IconX } from '@tabler/icons-react';\nimport * as React from 'react';\nimport { cn } from '../lib/cn.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 { useRowTitle } from '../resource/use-row-title.js';\nimport { useCandidatesGate } from './candidates-gate.js';\nimport { RelationCreateButton, useSelectCreate } from './relation-create.js';\nimport { LazyRelationPickerDialog } from './lazy-relation-picker-dialog.js';\nimport { RelationViewButton, selectKey } from './relation-view-button.js';\nimport type { FieldInputProps } from './registry.js';\nimport { namesHiddenField, narrowSearchDecl } from './search-access.js';\n\ntype ErasedExpression = FilterExpression<ErasedResourceHandle>;\ntype Key = string | number;\n\nconst DEBOUNCE_MS = 250;\nconst NO_KEYS: readonly Key[] = Object.freeze([]);\n\n/** The keys a to-many field holds: strings and finite numbers, the user's order, no repeats. */\nfunction keysOf(value: unknown): readonly Key[] {\n if (!Array.isArray(value)) return [];\n const seen = new Set<string>();\n const out: Key[] = [];\n for (const entry of value) {\n const key = selectKey(entry);\n if (key === null || seen.has(key)) continue;\n seen.add(key);\n out.push(entry as Key);\n }\n return out;\n}\n\n/**\n * Form input of `f.relationMany` (P7 3.5): the chosen rows as chips (titles through the relation\n * aggregator, one batched read), a popover that searches the same option query a single select\n * does (candidates, field access and the create hand-back included), \"Advanced search\" in a\n * multiple-choice dialog, a \"view\" on every chip and \"+ create\" that appends what it created.\n * The value is `row[by ?? id]` for each chosen row. Loaded lazily: it is not on any screen's\n * first paint.\n */\nexport function RelationManyPickerInput(props: FieldInputProps): React.ReactElement {\n const {\n name,\n id,\n value,\n onChange,\n testid,\n field,\n disabled,\n required,\n ariaInvalid,\n ariaDescribedBy,\n ariaErrorMessage,\n relationSeeds,\n fixedFilters,\n metaSources,\n resource,\n link,\n } = props;\n const meta = field?.meta as\n | {\n to?: ResourceRefInput;\n by?: unknown;\n searchField?: string;\n candidates?: unknown;\n scope?: ScopeSource;\n }\n | undefined;\n let to: string | undefined;\n try {\n to = meta?.to === undefined ? undefined : resourceKey(meta.to);\n } catch {\n to = undefined;\n }\n const gate = useCandidatesGate({\n to,\n candidates: Array.isArray(meta?.candidates)\n ? (meta.candidates as readonly RelationCandidate[])\n : undefined,\n sources: metaSources,\n resource: resource ?? '',\n fieldName: field?.name ?? name,\n });\n if (!to) {\n return (\n <Button\n type=\"button\"\n variant=\"outline\"\n disabled\n data-testid={`${testid ?? `relation-many:${name}`}:no-target`}\n className=\"w-full justify-between font-normal\"\n name={name}\n id={id}\n >\n <span className=\"truncate text-muted-foreground\">—</span>\n </Button>\n );\n }\n // An inline link: the form controller's rights decide what the select offers, and the keys the\n // user cannot see ride along in the value. A link denied `not-atomic` is read-only with a notice.\n const linkRights = link?.rights;\n const notAtomic =\n linkRights !== undefined &&\n [linkRights.edit, linkRights.attach, linkRights.detach].some(\n (right) => !right.allowed && right.reason === 'not-atomic',\n );\n const denied =\n linkRights !== undefined &&\n !notAtomic &&\n !linkRights.edit.allowed &&\n ['field-readonly', 'rule-denied', 'operation-absent'].includes(linkRights.edit.reason);\n // The form holds a link's keys only once they are read whole: a failed or capped read, or one\n // still running, leaves the field read-only, and the user is told why.\n const keyRead = link?.keys;\n const keyNotice: KeyNotice | undefined =\n link === undefined\n ? undefined\n : keyRead?.error\n ? { kind: 'error', error: keyRead.error }\n : keyRead !== undefined &&\n !keyRead.isLoading &&\n keyRead.keys !== undefined &&\n !keyRead.complete\n ? { kind: 'incomplete', count: keyRead.keys.length }\n : !link.ready\n ? { kind: 'pending' }\n : undefined;\n const filters =\n gate.filters.length === 0 ? fixedFilters : [...gate.filters, ...(fixedFilters ?? [])];\n return (\n <ManyPicker\n to={to}\n keys={keysOf(value)}\n onChange={(next) => onChange(next)}\n {...(typeof meta?.by === 'string' ? { by: meta.by } : {})}\n searchField={meta?.searchField}\n {...(meta?.scope === undefined ? {} : { scope: meta.scope })}\n fixedFilters={filters}\n {...(gate.gate === undefined ? {} : { gate: gate.gate })}\n createSeeds={relationSeeds}\n name={name}\n id={id}\n testid={testid ?? `relation-many:${to}`}\n resource={resource ?? ''}\n fieldName={field?.name ?? name}\n open={relationOpenOf(field ?? {})}\n {...(linkRights === undefined\n ? {}\n : {\n canAdd: linkRights.attach.allowed,\n canRemove: linkRights.detach.allowed,\n hidden: link?.hidden ?? [],\n notAtomic,\n readOnly: denied,\n ...(keyNotice === undefined ? {} : { keyNotice }),\n })}\n disabled={disabled}\n required={required}\n ariaInvalid={ariaInvalid}\n ariaDescribedBy={ariaDescribedBy}\n ariaErrorMessage={ariaErrorMessage}\n />\n );\n}\n\n/** Why a link whose keys the form does not hold whole is read-only. */\ntype KeyNotice =\n | { readonly kind: 'error'; readonly error: unknown }\n | { readonly kind: 'incomplete'; readonly count: number }\n | { readonly kind: 'pending' };\n\ninterface ManyPickerProps {\n readonly to: string;\n readonly keys: readonly Key[];\n readonly onChange: (next: readonly Key[]) => void;\n readonly by?: string;\n readonly searchField?: string | undefined;\n readonly fixedFilters: readonly FixedFilterInput[] | undefined;\n readonly gate?: import('./candidates-gate.js').CandidatesGate;\n readonly createSeeds: Readonly<Record<string, unknown>> | undefined;\n readonly name: string;\n readonly id: string | undefined;\n readonly testid: string;\n readonly resource: string;\n readonly fieldName: string;\n readonly open: RelationOpen;\n /** False: no search box, no \"Advanced search\", no create, and no key can be added. Default true. */\n readonly canAdd?: boolean;\n /** False: no chip remove button, no Backspace removal, no clear-all. Default true. */\n readonly canRemove?: boolean;\n /**\n * Keys of `keys` whose target the user cannot see: one \"N not visible\" chip stands for them, it\n * has no remove button, and neither Backspace nor clear-all drops them.\n */\n readonly hidden?: readonly Key[];\n /** The link is edited with the record but its provider does not write it so: read-only, say so. */\n readonly notAtomic?: boolean;\n /** The user may see the link but not change it: say so. */\n readonly readOnly?: boolean;\n /** The link's keys are not held whole (failed, cut at the read cap, or still loading). */\n readonly keyNotice?: KeyNotice;\n /**\n * The read scope of the option reads (popover, dialog, create hand-back). Default `'context'`:\n * the record around the select narrows them, except by the field the select writes.\n */\n readonly scope?: ScopeSource;\n readonly disabled: boolean | undefined;\n readonly required: boolean | undefined;\n readonly ariaInvalid: boolean | undefined;\n readonly ariaDescribedBy: string | undefined;\n readonly ariaErrorMessage: string | undefined;\n}\n\nfunction ManyPicker({\n to,\n keys,\n onChange,\n by,\n searchField,\n fixedFilters,\n gate,\n createSeeds,\n name,\n id,\n testid,\n resource,\n fieldName,\n open: openMode,\n canAdd = true,\n canRemove = true,\n hidden = NO_KEYS,\n notAtomic = false,\n readOnly = false,\n keyNotice,\n scope = 'context',\n disabled,\n required,\n ariaInvalid,\n ariaDescribedBy,\n ariaErrorMessage,\n}: ManyPickerProps): React.ReactElement {\n const app = useApp();\n const caps = useI18nCaps();\n const t = useOptionalT();\n const tid = useTestIds();\n const targetDef = app.registry.get(to);\n const rowTitle = useRowTitle(to, targetDef?.displayField);\n const targetLabel = resolveResourceLabel(caps, to, 'single', targetDef).toLowerCase();\n const childPrefix = `relation-picker:${to}`;\n const effectiveSearchField = searchField ?? targetDef?.displayField ?? 'name';\n const [open, setOpen] = React.useState(false);\n const [dialogOpen, setDialogOpen] = React.useState(false);\n // Mounted on the first open, and only while no candidates gate holds the read: without the\n // candidate filters the dialog would list rows the field must not offer.\n const gated = gate !== 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 const triggerRef = React.useRef<HTMLButtonElement | null>(null);\n const focusTrigger = React.useCallback(() => triggerRef.current?.focus(), []);\n\n React.useEffect(() => {\n const handle = setTimeout(() => setDebouncedSearch(search), DEBOUNCE_MS);\n return () => clearTimeout(handle);\n }, [search]);\n React.useEffect(() => {\n if (!open) {\n setSearch('');\n setDebouncedSearch('');\n }\n }, [open]);\n\n const targetIdField = (targetDef?.data as { readonly idField?: unknown } | undefined)?.idField;\n const idField =\n typeof targetIdField === 'string' && targetIdField.length > 0 ? targetIdField : 'id';\n const valueField = by ?? idField;\n const access = useListAccess(to);\n const accessReady = access.status === 'ready';\n const searchDecl = React.useMemo<SearchDecl | undefined>(\n () =>\n narrowSearchDecl(\n targetDef?.views?.list?.search ?? { fields: [effectiveSearchField] },\n (field) => access.fieldVisible(field),\n ),\n [targetDef, effectiveSearchField, access],\n );\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 const blocked =\n !accessReady ||\n gate !== undefined ||\n namesHiddenField(filter, (field) => access.fieldVisible(field));\n\n const hiddenSet = React.useMemo(() => new Set(hidden.map(String)), [hidden]);\n const visibleKeys = React.useMemo(\n () => keys.filter((key) => !hiddenSet.has(String(key))),\n [keys, hiddenSet],\n );\n const hiddenHeld = keys.length - visibleKeys.length;\n const append = React.useCallback(\n (next: Key) => {\n if (!canAdd || keys.some((key) => String(key) === String(next))) return;\n onChange([...keys, next]);\n },\n [keys, onChange, canAdd],\n );\n const toggle = React.useCallback(\n (next: Key) => {\n const held = keys.some((key) => String(key) === String(next));\n if (held ? !canRemove || hiddenSet.has(String(next)) : !canAdd) return;\n onChange(held ? keys.filter((key) => String(key) !== String(next)) : [...keys, next]);\n },\n [keys, onChange, canAdd, canRemove, hiddenSet],\n );\n const { create, reader: createReader } = useSelectCreate({\n to,\n by,\n valueField,\n idField,\n fixedFilters,\n scope,\n scopeField: fieldName,\n held: gated,\n searchDecl,\n searchContext: { resource: to, def: targetDef },\n // What was created and fits is appended, as a pick would be; the dialog it came from is done.\n onValue: (picked) => {\n append(picked);\n setDialogOpen(false);\n },\n });\n\n const listQuery = useList(to, {\n ...(filter ? { filter } : {}),\n pagination: { kind: 'offset', page: 1, pageSize: 10 },\n scope,\n scopeField: fieldName,\n enabled: open && targetDef != null && !blocked,\n });\n const rows = (listQuery.data?.data ?? []) as Array<Record<string, unknown>>;\n const isLoading = listQuery.isLoading || listQuery.isFetching;\n const error = listQuery.isError ? (listQuery.error as unknown as Error) : null;\n\n if (!targetDef) {\n return (\n <Button\n type=\"button\"\n variant=\"outline\"\n disabled\n data-testid={`${childPrefix}:unregistered`}\n className=\"w-full justify-between font-normal\"\n name={name}\n id={id}\n >\n <span className=\"truncate text-muted-foreground\">{keys.map(String).join(', ') || '—'}</span>\n </Button>\n );\n }\n\n const placeholder = t('relation.select', {\n params: { name: targetLabel },\n defaultValue: `Select ${targetLabel}…`,\n });\n // Nothing held, nothing drawn: an empty row would still take the column's gap above the trigger.\n const chips =\n visibleKeys.length === 0 && hiddenHeld === 0 ? null : (\n <div className=\"flex flex-wrap items-center gap-1\" data-testid={`${testid}:chips`}>\n {visibleKeys.map((key) => (\n <Chip\n key={String(key)}\n to={to}\n keyValue={key}\n by={by}\n resource={resource}\n fieldName={fieldName}\n open={openMode}\n testId={tid.relationManyChip(resource, fieldName, String(key))}\n disabled={disabled === true || !canRemove}\n focusTrigger={focusTrigger}\n onRemove={() => onChange(keys.filter((entry) => String(entry) !== String(key)))}\n />\n ))}\n {hiddenHeld > 0 ? (\n <span\n data-testid={tid.linkHidden(resource, fieldName)}\n className=\"inline-flex items-center rounded-md border bg-secondary px-2 py-0.5 text-sm text-muted-foreground\"\n >\n {t('field.link.hidden', {\n count: hiddenHeld,\n defaultValue: '{{count}} not visible',\n })}\n </span>\n ) : null}\n {canRemove && disabled !== true && visibleKeys.length > 0 ? (\n <button\n type=\"button\"\n onClick={() => {\n // The button leaves with the chips it clears: focus goes where chip removal sends it.\n focusTrigger();\n onChange(keys.filter((key) => hiddenSet.has(String(key))));\n }}\n data-testid={`${testid}:clear-all`}\n className=\"rounded-sm px-1 text-xs text-muted-foreground hover:text-foreground focus:outline-hidden focus-visible:ring-2 focus-visible:ring-ring\"\n >\n {t('relationPicker.clearAll', { defaultValue: 'Clear all' })}\n </button>\n ) : null}\n </div>\n );\n const notice = keyNotice ? (\n <p\n data-testid={`${testid}:keys-${keyNotice.kind}`}\n role={keyNotice.kind === 'error' ? 'status' : undefined}\n className={cn(\n 'text-xs',\n keyNotice.kind === 'error' ? 'text-destructive' : 'text-muted-foreground',\n )}\n >\n {keyNotice.kind === 'error'\n ? `${t('field.link.failed', { defaultValue: 'Links could not be loaded' })}: ${errorTitle(keyNotice.error, caps) ?? ''}`\n : keyNotice.kind === 'incomplete'\n ? t('field.link.incomplete', {\n count: keyNotice.count,\n defaultValue: 'More than {{count}} links',\n })\n : t('table.loading', { defaultValue: 'Loading…' })}\n </p>\n ) : readOnly ? (\n <p data-testid={`${testid}:read-only`} className=\"text-xs text-muted-foreground\">\n {t('field.link.readOnly', { defaultValue: 'You cannot change this link.' })}\n </p>\n ) : notAtomic ? (\n <p data-testid={`${testid}:not-atomic`} className=\"text-xs text-muted-foreground\">\n {t('field.link.notAtomic', {\n defaultValue:\n 'This link cannot be edited here: its data source does not save it with the record.',\n })}\n </p>\n ) : null;\n\n if (gate?.kind === 'pending')\n return (\n <div className=\"flex flex-col gap-1.5\">\n {chips}\n {notice}\n <Button\n type=\"button\"\n variant=\"outline\"\n disabled\n aria-busy=\"true\"\n data-testid={testid}\n data-buro-candidates=\"pending\"\n className=\"w-full justify-between font-normal\"\n >\n <span className=\"truncate text-muted-foreground\">{placeholder}</span>\n <IconLoader2 className=\"h-4 w-4 shrink-0 animate-spin opacity-50\" aria-hidden />\n </Button>\n </div>\n );\n if (gate?.kind === 'forbidden')\n return (\n <div className=\"flex flex-col gap-1.5\">\n {chips}\n {notice}\n <Button\n type=\"button\"\n variant=\"outline\"\n disabled\n data-testid={testid}\n data-buro-candidates=\"forbidden\"\n aria-describedby={gate.testId}\n className=\"w-full justify-between font-normal\"\n >\n <span className=\"truncate text-muted-foreground\">{placeholder}</span>\n <IconChevronDown className=\"h-4 w-4 shrink-0 opacity-50\" aria-hidden />\n </Button>\n <p id={gate.testId} data-testid={gate.testId} className=\"text-xs text-muted-foreground\">\n {gate.message}\n </p>\n </div>\n );\n\n return (\n <Popover open={open} onOpenChange={setOpen}>\n <div className=\"flex flex-col gap-1.5\">\n {chips}\n {notice}\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={testid}\n name={name}\n id={id}\n className=\"w-full justify-between font-normal\"\n >\n <span className=\"truncate text-left text-muted-foreground\">{placeholder}</span>\n <IconChevronDown className=\"h-4 w-4 shrink-0 opacity-50\" aria-hidden />\n </Button>\n </PopoverTrigger>\n </div>\n <PopoverContent className=\"w-[var(--radix-popover-trigger-width)] p-0\" align=\"start\">\n {gate?.kind === 'empty' ? (\n <p\n className=\"px-3 py-6 text-center text-sm text-muted-foreground\"\n data-testid={gate.testId}\n >\n {gate.message}\n </p>\n ) : (\n <Command shouldFilter={false}>\n {searchDecl !== undefined && canAdd && (\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 onKeyDown={(event) => {\n // Backspace in an empty search takes the last chip off.\n if (event.key === 'Backspace' && search === '' && canRemove) {\n event.preventDefault();\n const last = visibleKeys.at(-1);\n if (last !== undefined)\n onChange(keys.filter((key) => String(key) !== String(last)));\n }\n // Space toggles the highlighted option while nothing is typed.\n if (event.key === ' ' && search === '') {\n event.preventDefault();\n document\n .querySelector<HTMLElement>(\n `[data-testid=\"${childPrefix}:list\"] [aria-selected=\"true\"]`,\n )\n ?.click();\n }\n }}\n />\n )}\n <CommandList\n data-testid={`${childPrefix}:list`}\n aria-multiselectable=\"true\"\n aria-label={targetLabel}\n >\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 rowValue = row[valueField];\n if (rowValue === null || rowValue === undefined || rowValue === '') return null;\n const idStr = String(rowValue);\n const label = rowTitle(row);\n const chosen = keys.some((key) => String(key) === idStr);\n return (\n <CommandItem\n key={idStr}\n value={`${label} ${idStr}`}\n data-testid={`${childPrefix}:option:${idStr}`}\n aria-checked={chosen}\n disabled={chosen ? !canRemove : !canAdd}\n onSelect={() => toggle(rowValue as Key)}\n >\n <IconCheck\n className={cn(\n 'mr-2 h-4 w-4 shrink-0',\n chosen ? 'opacity-100' : 'opacity-0',\n )}\n aria-hidden\n />\n <span className=\"truncate\">{label}</span>\n {/* A label that already is the id would print it twice (REF-40). */}\n {label === idStr ? null : (\n <span className=\"ml-auto shrink-0 pl-2 text-xs text-muted-foreground\">\n #{idStr}\n </span>\n )}\n </CommandItem>\n );\n })}\n </CommandList>\n <div className=\"flex items-center justify-between gap-2 border-t px-3 py-2 text-sm\">\n {canAdd ? (\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 ) : (\n <span />\n )}\n {!disabled && canAdd && (\n <RelationCreateButton\n to={to}\n fieldName={fieldName}\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 </div>\n </Command>\n )}\n </PopoverContent>\n {dialogMounted && !gated && canAdd && (\n <LazyRelationPickerDialog\n to={to}\n open={dialogOpen}\n onOpenChange={setDialogOpen}\n onSelect={() => undefined}\n multiple\n selected={keys}\n onToggle={toggle}\n valueField={valueField}\n searchField={searchField}\n fixedFilters={fixedFilters}\n scope={scope}\n scopeField={fieldName}\n {...(disabled ? {} : { create })}\n createSeeds={createSeeds}\n fieldName={fieldName}\n onBeforeCreate={focusTrigger}\n />\n )}\n {createReader}\n </Popover>\n );\n}\n\n/** One chosen row: its title (the aggregator batches every chip's read), which views it, and remove. */\nfunction Chip({\n to,\n keyValue,\n by,\n resource,\n fieldName,\n testId,\n open: openMode,\n disabled,\n focusTrigger,\n onRemove,\n}: {\n readonly to: string;\n readonly keyValue: Key;\n readonly by: string | undefined;\n readonly resource: string;\n readonly fieldName: string;\n readonly testId: string;\n readonly open: RelationOpen;\n readonly disabled: boolean;\n readonly focusTrigger: () => void;\n readonly onRemove: () => void;\n}): React.ReactElement {\n const t = useOptionalT();\n const app = useApp();\n const targetDef = app.registry.get(to);\n const rowTitle = useRowTitle(to, targetDef?.displayField);\n const { record, isLoading } = useRelatedRecord(\n to,\n keyValue,\n by === undefined ? undefined : { by },\n );\n const title = record ? rowTitle(record as Record<string, unknown>) : String(keyValue);\n return (\n <span\n data-testid={testId}\n data-resource={resource}\n className=\"inline-flex items-center gap-0.5 rounded-md border bg-secondary pl-2 text-sm text-secondary-foreground\"\n >\n <RelationViewButton\n to={to}\n value={keyValue}\n by={by}\n fieldName={`${fieldName}:${String(keyValue)}`}\n beforeOpen={focusTrigger}\n open={openMode}\n title={isLoading ? t('table.loading', { defaultValue: 'Loading…' }) : title}\n />\n {!disabled && (\n <button\n type=\"button\"\n onClick={onRemove}\n data-testid={`${testId}:remove`}\n aria-label={`${t('relationPicker.clear', { defaultValue: 'Clear' })} ${title}`}\n className=\"inline-flex size-6 items-center justify-center rounded-sm text-muted-foreground hover:text-foreground focus:outline-hidden focus-visible:ring-2 focus-visible:ring-ring\"\n >\n <IconX className=\"size-3.5\" aria-hidden />\n </button>\n )}\n </span>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"relation-many-picker-DzuGJpEl.cjs","names":["resolveErrorText","selectKey","resourceKey","useCandidatesGate","Button","relationOpenOf","useApp","useI18nCaps","useOptionalT","useTestIds","useRowTitle","resolveResourceLabel","React","useListAccess","narrowSearchDecl","composeListFilter","normalizeFixedFilters","namesHiddenField","useSelectCreate","useList","cn","IconLoader2","IconChevronDown","Popover","PopoverTrigger","PopoverContent","Command","CommandInput","CommandList","LoadError","CommandEmpty","CommandItem","IconCheck","RelationCreateButton","LazyRelationPickerDialog","useRelatedRecord","RelationViewButton","IconX"],"sources":["../src/lib/error-title.ts","../src/fields/relation-many-picker.tsx"],"sourcesContent":["import { type ErrorTextCaps, resolveErrorText } from '@burojs/react';\n\n/**\n * The text a user may read for a failure a hook handed to the kit as an `Error`-typed value.\n * The value is a `DataError` at runtime when it came from a data provider (its `message` is a\n * reference, so it is resolved through the bundle) and a plain `Error` when the framework raised\n * it (its `message` is already a sentence). Anything else has nothing to show.\n */\nexport function errorTitle(error: unknown, caps: ErrorTextCaps): string | undefined {\n if (error instanceof Error) return error.message;\n if (error !== null && typeof error === 'object') {\n const { code, message } = error as { readonly code?: unknown; readonly message?: unknown };\n if (typeof code === 'string') {\n return resolveErrorText(\n {\n code,\n ...(message !== null && typeof message === 'object' ? { message } : {}),\n } as Parameters<typeof resolveErrorText>[0],\n caps,\n );\n }\n }\n return undefined;\n}\n","'use client';\n\nimport {\n type ErasedResourceHandle,\n type FilterExpression,\n type FixedFilterInput,\n type RelationCandidate,\n type RelationOpen,\n relationOpenOf,\n type ResourceRefInput,\n type ScopeSource,\n type SearchDecl,\n composeListFilter,\n normalizeFixedFilters,\n resolveResourceLabel,\n resourceKey,\n} from '@burojs/core';\nimport {\n useApp,\n useI18nCaps,\n useList,\n useListAccess,\n useOptionalT,\n useRelatedRecord,\n useTestIds,\n} from '@burojs/react';\nimport { errorTitle } from '../lib/error-title.js';\nimport { LoadError } from '../load-error/load-error-view.js';\nimport { IconCheck, IconChevronDown, IconLoader2, IconX } from '@tabler/icons-react';\nimport * as React from 'react';\nimport { cn } from '../lib/cn.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 { useRowTitle } from '../resource/use-row-title.js';\nimport { useCandidatesGate } from './candidates-gate.js';\nimport { RelationCreateButton, useSelectCreate } from './relation-create.js';\nimport { LazyRelationPickerDialog } from './lazy-relation-picker-dialog.js';\nimport { RelationViewButton, selectKey } from './relation-view-button.js';\nimport type { FieldInputProps } from './registry.js';\nimport { namesHiddenField, narrowSearchDecl } from './search-access.js';\n\ntype ErasedExpression = FilterExpression<ErasedResourceHandle>;\ntype Key = string | number;\n\nconst DEBOUNCE_MS = 250;\nconst NO_KEYS: readonly Key[] = Object.freeze([]);\n\n/** The keys a to-many field holds: strings and finite numbers, the user's order, no repeats. */\nfunction keysOf(value: unknown): readonly Key[] {\n if (!Array.isArray(value)) return [];\n const seen = new Set<string>();\n const out: Key[] = [];\n for (const entry of value) {\n const key = selectKey(entry);\n if (key === null || seen.has(key)) continue;\n seen.add(key);\n out.push(entry as Key);\n }\n return out;\n}\n\n/**\n * Form input of `f.relationMany` (P7 3.5): the chosen rows as chips (titles through the relation\n * aggregator, one batched read), a popover that searches the same option query a single select\n * does (candidates, field access and the create hand-back included), \"Advanced search\" in a\n * multiple-choice dialog, a \"view\" on every chip and \"+ create\" that appends what it created.\n * The value is `row[by ?? id]` for each chosen row. Loaded lazily: it is not on any screen's\n * first paint.\n */\nexport function RelationManyPickerInput(props: FieldInputProps): React.ReactElement {\n const {\n name,\n id,\n value,\n onChange,\n testid,\n field,\n disabled,\n required,\n ariaInvalid,\n ariaDescribedBy,\n ariaErrorMessage,\n relationSeeds,\n fixedFilters,\n metaSources,\n resource,\n link,\n } = props;\n const meta = field?.meta as\n | {\n to?: ResourceRefInput;\n by?: unknown;\n searchField?: string;\n candidates?: unknown;\n scope?: ScopeSource;\n }\n | undefined;\n let to: string | undefined;\n try {\n to = meta?.to === undefined ? undefined : resourceKey(meta.to);\n } catch {\n to = undefined;\n }\n const gate = useCandidatesGate({\n to,\n candidates: Array.isArray(meta?.candidates)\n ? (meta.candidates as readonly RelationCandidate[])\n : undefined,\n sources: metaSources,\n resource: resource ?? '',\n fieldName: field?.name ?? name,\n });\n if (!to) {\n return (\n <Button\n type=\"button\"\n variant=\"outline\"\n disabled\n data-testid={`${testid ?? `relation-many:${name}`}:no-target`}\n className=\"w-full justify-between font-normal\"\n name={name}\n id={id}\n >\n <span className=\"truncate text-muted-foreground\">—</span>\n </Button>\n );\n }\n // An inline link: the form controller's rights decide what the select offers, and the keys the\n // user cannot see ride along in the value. A link denied `not-atomic` is read-only with a notice.\n const linkRights = link?.rights;\n const notAtomic =\n linkRights !== undefined &&\n [linkRights.edit, linkRights.attach, linkRights.detach].some(\n (right) => !right.allowed && right.reason === 'not-atomic',\n );\n const denied =\n linkRights !== undefined &&\n !notAtomic &&\n !linkRights.edit.allowed &&\n ['field-readonly', 'rule-denied', 'operation-absent'].includes(linkRights.edit.reason);\n // The form holds a link's keys only once they are read whole: a failed or capped read, or one\n // still running, leaves the field read-only, and the user is told why.\n const keyRead = link?.keys;\n const keyNotice: KeyNotice | undefined =\n link === undefined\n ? undefined\n : keyRead?.error\n ? { kind: 'error', error: keyRead.error }\n : keyRead !== undefined &&\n !keyRead.isLoading &&\n keyRead.keys !== undefined &&\n !keyRead.complete\n ? { kind: 'incomplete', count: keyRead.keys.length }\n : !link.ready\n ? { kind: 'pending' }\n : undefined;\n const filters =\n gate.filters.length === 0 ? fixedFilters : [...gate.filters, ...(fixedFilters ?? [])];\n return (\n <ManyPicker\n to={to}\n keys={keysOf(value)}\n onChange={(next) => onChange(next)}\n {...(typeof meta?.by === 'string' ? { by: meta.by } : {})}\n searchField={meta?.searchField}\n {...(meta?.scope === undefined ? {} : { scope: meta.scope })}\n fixedFilters={filters}\n {...(gate.gate === undefined ? {} : { gate: gate.gate })}\n createSeeds={relationSeeds}\n name={name}\n id={id}\n testid={testid ?? `relation-many:${to}`}\n resource={resource ?? ''}\n fieldName={field?.name ?? name}\n open={relationOpenOf(field ?? {})}\n {...(linkRights === undefined\n ? {}\n : {\n canAdd: linkRights.attach.allowed,\n canRemove: linkRights.detach.allowed,\n hidden: link?.hidden ?? [],\n notAtomic,\n readOnly: denied,\n ...(keyNotice === undefined ? {} : { keyNotice }),\n })}\n disabled={disabled}\n required={required}\n ariaInvalid={ariaInvalid}\n ariaDescribedBy={ariaDescribedBy}\n ariaErrorMessage={ariaErrorMessage}\n />\n );\n}\n\n/** Why a link whose keys the form does not hold whole is read-only. */\ntype KeyNotice =\n | { readonly kind: 'error'; readonly error: unknown }\n | { readonly kind: 'incomplete'; readonly count: number }\n | { readonly kind: 'pending' };\n\ninterface ManyPickerProps {\n readonly to: string;\n readonly keys: readonly Key[];\n readonly onChange: (next: readonly Key[]) => void;\n readonly by?: string;\n readonly searchField?: string | undefined;\n readonly fixedFilters: readonly FixedFilterInput[] | undefined;\n readonly gate?: import('./candidates-gate.js').CandidatesGate;\n readonly createSeeds: Readonly<Record<string, unknown>> | undefined;\n readonly name: string;\n readonly id: string | undefined;\n readonly testid: string;\n readonly resource: string;\n readonly fieldName: string;\n readonly open: RelationOpen;\n /** False: no search box, no \"Advanced search\", no create, and no key can be added. Default true. */\n readonly canAdd?: boolean;\n /** False: no chip remove button, no Backspace removal, no clear-all. Default true. */\n readonly canRemove?: boolean;\n /**\n * Keys of `keys` whose target the user cannot see: one \"N not visible\" chip stands for them, it\n * has no remove button, and neither Backspace nor clear-all drops them.\n */\n readonly hidden?: readonly Key[];\n /** The link is edited with the record but its provider does not write it so: read-only, say so. */\n readonly notAtomic?: boolean;\n /** The user may see the link but not change it: say so. */\n readonly readOnly?: boolean;\n /** The link's keys are not held whole (failed, cut at the read cap, or still loading). */\n readonly keyNotice?: KeyNotice;\n /**\n * The read scope of the option reads (popover, dialog, create hand-back). Default `'context'`:\n * the record around the select narrows them, except by the field the select writes.\n */\n readonly scope?: ScopeSource;\n readonly disabled: boolean | undefined;\n readonly required: boolean | undefined;\n readonly ariaInvalid: boolean | undefined;\n readonly ariaDescribedBy: string | undefined;\n readonly ariaErrorMessage: string | undefined;\n}\n\nfunction ManyPicker({\n to,\n keys,\n onChange,\n by,\n searchField,\n fixedFilters,\n gate,\n createSeeds,\n name,\n id,\n testid,\n resource,\n fieldName,\n open: openMode,\n canAdd = true,\n canRemove = true,\n hidden = NO_KEYS,\n notAtomic = false,\n readOnly = false,\n keyNotice,\n scope = 'context',\n disabled,\n required,\n ariaInvalid,\n ariaDescribedBy,\n ariaErrorMessage,\n}: ManyPickerProps): React.ReactElement {\n const app = useApp();\n const caps = useI18nCaps();\n const t = useOptionalT();\n const tid = useTestIds();\n const targetDef = app.registry.get(to);\n const rowTitle = useRowTitle(to, targetDef?.displayField);\n const targetLabel = resolveResourceLabel(caps, to, 'single', targetDef).toLowerCase();\n const childPrefix = `relation-picker:${to}`;\n const effectiveSearchField = searchField ?? targetDef?.displayField ?? 'name';\n const [open, setOpen] = React.useState(false);\n const [dialogOpen, setDialogOpen] = React.useState(false);\n // Mounted on the first open, and only while no candidates gate holds the read: without the\n // candidate filters the dialog would list rows the field must not offer.\n const gated = gate !== 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 const triggerRef = React.useRef<HTMLButtonElement | null>(null);\n const focusTrigger = React.useCallback(() => triggerRef.current?.focus(), []);\n\n React.useEffect(() => {\n const handle = setTimeout(() => setDebouncedSearch(search), DEBOUNCE_MS);\n return () => clearTimeout(handle);\n }, [search]);\n React.useEffect(() => {\n if (!open) {\n setSearch('');\n setDebouncedSearch('');\n }\n }, [open]);\n\n const targetIdField = (targetDef?.data as { readonly idField?: unknown } | undefined)?.idField;\n const idField =\n typeof targetIdField === 'string' && targetIdField.length > 0 ? targetIdField : 'id';\n const valueField = by ?? idField;\n const access = useListAccess(to);\n const accessReady = access.status === 'ready';\n const searchDecl = React.useMemo<SearchDecl | undefined>(\n () =>\n narrowSearchDecl(\n targetDef?.views?.list?.search ?? { fields: [effectiveSearchField] },\n (field) => access.fieldVisible(field),\n ),\n [targetDef, effectiveSearchField, access],\n );\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 const blocked =\n !accessReady ||\n gate !== undefined ||\n namesHiddenField(filter, (field) => access.fieldVisible(field));\n\n const hiddenSet = React.useMemo(() => new Set(hidden.map(String)), [hidden]);\n const visibleKeys = React.useMemo(\n () => keys.filter((key) => !hiddenSet.has(String(key))),\n [keys, hiddenSet],\n );\n const hiddenHeld = keys.length - visibleKeys.length;\n const append = React.useCallback(\n (next: Key) => {\n if (!canAdd || keys.some((key) => String(key) === String(next))) return;\n onChange([...keys, next]);\n },\n [keys, onChange, canAdd],\n );\n const toggle = React.useCallback(\n (next: Key) => {\n const held = keys.some((key) => String(key) === String(next));\n if (held ? !canRemove || hiddenSet.has(String(next)) : !canAdd) return;\n onChange(held ? keys.filter((key) => String(key) !== String(next)) : [...keys, next]);\n },\n [keys, onChange, canAdd, canRemove, hiddenSet],\n );\n const { create, reader: createReader } = useSelectCreate({\n to,\n by,\n valueField,\n idField,\n fixedFilters,\n scope,\n scopeField: fieldName,\n held: gated,\n searchDecl,\n searchContext: { resource: to, def: targetDef },\n // What was created and fits is appended, as a pick would be; the dialog it came from is done.\n onValue: (picked) => {\n append(picked);\n setDialogOpen(false);\n },\n });\n\n const listQuery = useList(to, {\n ...(filter ? { filter } : {}),\n pagination: { kind: 'offset', page: 1, pageSize: 10 },\n scope,\n scopeField: fieldName,\n enabled: open && targetDef != null && !blocked,\n });\n const rows = (listQuery.data?.data ?? []) as Array<Record<string, unknown>>;\n const isLoading = listQuery.isLoading || listQuery.isFetching;\n const error = listQuery.isError ? (listQuery.error as unknown as Error) : null;\n\n if (!targetDef) {\n return (\n <Button\n type=\"button\"\n variant=\"outline\"\n disabled\n data-testid={`${childPrefix}:unregistered`}\n className=\"w-full justify-between font-normal\"\n name={name}\n id={id}\n >\n <span className=\"truncate text-muted-foreground\">{keys.map(String).join(', ') || '—'}</span>\n </Button>\n );\n }\n\n const placeholder = t('relation.select', {\n params: { name: targetLabel },\n defaultValue: `Select ${targetLabel}…`,\n });\n // Nothing held, nothing drawn: an empty row would still take the column's gap above the trigger.\n const chips =\n visibleKeys.length === 0 && hiddenHeld === 0 ? null : (\n <div className=\"flex flex-wrap items-center gap-1\" data-testid={`${testid}:chips`}>\n {visibleKeys.map((key) => (\n <Chip\n key={String(key)}\n to={to}\n keyValue={key}\n by={by}\n resource={resource}\n fieldName={fieldName}\n open={openMode}\n testId={tid.relationManyChip(resource, fieldName, String(key))}\n disabled={disabled === true || !canRemove}\n focusTrigger={focusTrigger}\n onRemove={() => onChange(keys.filter((entry) => String(entry) !== String(key)))}\n />\n ))}\n {hiddenHeld > 0 ? (\n <span\n data-testid={tid.linkHidden(resource, fieldName)}\n className=\"inline-flex items-center rounded-md border bg-secondary px-2 py-0.5 text-sm text-muted-foreground\"\n >\n {t('field.link.hidden', {\n count: hiddenHeld,\n defaultValue: '{{count}} not visible',\n })}\n </span>\n ) : null}\n {canRemove && disabled !== true && visibleKeys.length > 0 ? (\n <button\n type=\"button\"\n onClick={() => {\n // The button leaves with the chips it clears: focus goes where chip removal sends it.\n focusTrigger();\n onChange(keys.filter((key) => hiddenSet.has(String(key))));\n }}\n data-testid={`${testid}:clear-all`}\n className=\"rounded-sm px-1 text-xs text-muted-foreground hover:text-foreground focus:outline-hidden focus-visible:ring-2 focus-visible:ring-ring\"\n >\n {t('relationPicker.clearAll', { defaultValue: 'Clear all' })}\n </button>\n ) : null}\n </div>\n );\n const notice = keyNotice ? (\n <p\n data-testid={`${testid}:keys-${keyNotice.kind}`}\n role={keyNotice.kind === 'error' ? 'status' : undefined}\n className={cn(\n 'text-xs',\n keyNotice.kind === 'error' ? 'text-destructive' : 'text-muted-foreground',\n )}\n >\n {keyNotice.kind === 'error'\n ? `${t('field.link.failed', { defaultValue: 'Links could not be loaded' })}: ${errorTitle(keyNotice.error, caps) ?? ''}`\n : keyNotice.kind === 'incomplete'\n ? t('field.link.incomplete', {\n count: keyNotice.count,\n defaultValue: 'More than {{count}} links',\n })\n : t('table.loading', { defaultValue: 'Loading…' })}\n </p>\n ) : readOnly ? (\n <p data-testid={`${testid}:read-only`} className=\"text-xs text-muted-foreground\">\n {t('field.link.readOnly', { defaultValue: 'You cannot change this link.' })}\n </p>\n ) : notAtomic ? (\n <p data-testid={`${testid}:not-atomic`} className=\"text-xs text-muted-foreground\">\n {t('field.link.notAtomic', {\n defaultValue:\n 'This link cannot be edited here: its data source does not save it with the record.',\n })}\n </p>\n ) : null;\n\n if (gate?.kind === 'pending')\n return (\n <div className=\"flex flex-col gap-1.5\">\n {chips}\n {notice}\n <Button\n type=\"button\"\n variant=\"outline\"\n disabled\n aria-busy=\"true\"\n data-testid={testid}\n data-buro-candidates=\"pending\"\n className=\"w-full justify-between font-normal\"\n >\n <span className=\"truncate text-muted-foreground\">{placeholder}</span>\n <IconLoader2 className=\"h-4 w-4 shrink-0 animate-spin opacity-50\" aria-hidden />\n </Button>\n </div>\n );\n if (gate?.kind === 'forbidden')\n return (\n <div className=\"flex flex-col gap-1.5\">\n {chips}\n {notice}\n <Button\n type=\"button\"\n variant=\"outline\"\n disabled\n data-testid={testid}\n data-buro-candidates=\"forbidden\"\n aria-describedby={gate.testId}\n className=\"w-full justify-between font-normal\"\n >\n <span className=\"truncate text-muted-foreground\">{placeholder}</span>\n <IconChevronDown className=\"h-4 w-4 shrink-0 opacity-50\" aria-hidden />\n </Button>\n <p id={gate.testId} data-testid={gate.testId} className=\"text-xs text-muted-foreground\">\n {gate.message}\n </p>\n </div>\n );\n\n return (\n <Popover open={open} onOpenChange={setOpen}>\n <div className=\"flex flex-col gap-1.5\">\n {chips}\n {notice}\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={testid}\n name={name}\n id={id}\n className=\"w-full justify-between font-normal\"\n >\n <span className=\"truncate text-left text-muted-foreground\">{placeholder}</span>\n <IconChevronDown className=\"h-4 w-4 shrink-0 opacity-50\" aria-hidden />\n </Button>\n </PopoverTrigger>\n </div>\n <PopoverContent className=\"w-[var(--radix-popover-trigger-width)] p-0\" align=\"start\">\n {gate?.kind === 'empty' ? (\n <p\n className=\"px-3 py-6 text-center text-sm text-muted-foreground\"\n data-testid={gate.testId}\n >\n {gate.message}\n </p>\n ) : (\n <Command shouldFilter={false}>\n {searchDecl !== undefined && canAdd && (\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 onKeyDown={(event) => {\n // Backspace in an empty search takes the last chip off.\n if (event.key === 'Backspace' && search === '' && canRemove) {\n event.preventDefault();\n const last = visibleKeys.at(-1);\n if (last !== undefined)\n onChange(keys.filter((key) => String(key) !== String(last)));\n }\n // Space toggles the highlighted option while nothing is typed.\n if (event.key === ' ' && search === '') {\n event.preventDefault();\n document\n .querySelector<HTMLElement>(\n `[data-testid=\"${childPrefix}:list\"] [aria-selected=\"true\"]`,\n )\n ?.click();\n }\n }}\n />\n )}\n <CommandList\n data-testid={`${childPrefix}:list`}\n aria-multiselectable=\"true\"\n aria-label={targetLabel}\n >\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 rowValue = row[valueField];\n if (rowValue === null || rowValue === undefined || rowValue === '') return null;\n const idStr = String(rowValue);\n const label = rowTitle(row);\n const chosen = keys.some((key) => String(key) === idStr);\n return (\n <CommandItem\n key={idStr}\n value={`${label} ${idStr}`}\n data-testid={`${childPrefix}:option:${idStr}`}\n aria-checked={chosen}\n disabled={chosen ? !canRemove : !canAdd}\n onSelect={() => toggle(rowValue as Key)}\n >\n <IconCheck\n className={cn(\n 'mr-2 h-4 w-4 shrink-0',\n chosen ? 'opacity-100' : 'opacity-0',\n )}\n aria-hidden\n />\n <span className=\"truncate\">{label}</span>\n {/* A label that already is the id would print it twice (REF-40). */}\n {label === idStr ? null : (\n <span className=\"ml-auto shrink-0 pl-2 text-xs text-muted-foreground\">\n #{idStr}\n </span>\n )}\n </CommandItem>\n );\n })}\n </CommandList>\n <div className=\"flex items-center justify-between gap-2 border-t px-3 py-2 text-sm\">\n {canAdd ? (\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 ) : (\n <span />\n )}\n {!disabled && canAdd && (\n <RelationCreateButton\n to={to}\n fieldName={fieldName}\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 </div>\n </Command>\n )}\n </PopoverContent>\n {dialogMounted && !gated && canAdd && (\n <LazyRelationPickerDialog\n to={to}\n open={dialogOpen}\n onOpenChange={setDialogOpen}\n onSelect={() => undefined}\n multiple\n selected={keys}\n onToggle={toggle}\n valueField={valueField}\n searchField={searchField}\n fixedFilters={fixedFilters}\n scope={scope}\n scopeField={fieldName}\n {...(disabled ? {} : { create })}\n createSeeds={createSeeds}\n fieldName={fieldName}\n onBeforeCreate={focusTrigger}\n />\n )}\n {createReader}\n </Popover>\n );\n}\n\n/** One chosen row: its title (the aggregator batches every chip's read), which views it, and remove. */\nfunction Chip({\n to,\n keyValue,\n by,\n resource,\n fieldName,\n testId,\n open: openMode,\n disabled,\n focusTrigger,\n onRemove,\n}: {\n readonly to: string;\n readonly keyValue: Key;\n readonly by: string | undefined;\n readonly resource: string;\n readonly fieldName: string;\n readonly testId: string;\n readonly open: RelationOpen;\n readonly disabled: boolean;\n readonly focusTrigger: () => void;\n readonly onRemove: () => void;\n}): React.ReactElement {\n const t = useOptionalT();\n const app = useApp();\n const targetDef = app.registry.get(to);\n const rowTitle = useRowTitle(to, targetDef?.displayField);\n const { record, isLoading } = useRelatedRecord(\n to,\n keyValue,\n by === undefined ? undefined : { by },\n );\n const title = record ? rowTitle(record as Record<string, unknown>) : String(keyValue);\n return (\n <span\n data-testid={testId}\n data-resource={resource}\n className=\"inline-flex items-center gap-0.5 rounded-md border bg-secondary pl-2 text-sm text-secondary-foreground\"\n >\n <RelationViewButton\n to={to}\n value={keyValue}\n by={by}\n fieldName={`${fieldName}:${String(keyValue)}`}\n beforeOpen={focusTrigger}\n open={openMode}\n title={isLoading ? t('table.loading', { defaultValue: 'Loading…' }) : title}\n />\n {!disabled && (\n <button\n type=\"button\"\n onClick={onRemove}\n data-testid={`${testId}:remove`}\n aria-label={`${t('relationPicker.clear', { defaultValue: 'Clear' })} ${title}`}\n className=\"inline-flex size-6 items-center justify-center rounded-sm text-muted-foreground hover:text-foreground focus:outline-hidden focus-visible:ring-2 focus-visible:ring-ring\"\n >\n <IconX className=\"size-3.5\" aria-hidden />\n </button>\n )}\n </span>\n );\n}\n"],"mappings":";;;;;;;;;;;;AAQA,SAAgB,WAAW,OAAgB,MAAyC;CAClF,IAAI,iBAAiB,OAAO,OAAO,MAAM;CACzC,IAAI,UAAU,QAAQ,OAAO,UAAU,UAAU;EAC/C,MAAM,EAAE,MAAM,YAAY;EAC1B,IAAI,OAAO,SAAS,UAClB,QAAA,GAAOA,cAAAA,iBAAAA,CACL;GACE;GACA,GAAI,YAAY,QAAQ,OAAO,YAAY,WAAW,EAAE,QAAQ,IAAI,CAAC;EACvE,GACA,IACF;CAEJ;AAEF;;;AC4BA,MAAM,cAAc;AACpB,MAAM,UAA0B,OAAO,OAAO,CAAC,CAAC;AAGhD,SAAS,OAAO,OAAgC;CAC9C,IAAI,CAAC,MAAM,QAAQ,KAAK,GAAG,OAAO,CAAC;CACnC,MAAM,OAAO,IAAI,IAAY;CAC7B,MAAM,MAAa,CAAC;CACpB,KAAK,MAAM,SAAS,OAAO;EACzB,MAAM,MAAMC,wBAAAA,UAAU,KAAK;EAC3B,IAAI,QAAQ,QAAQ,KAAK,IAAI,GAAG,GAAG;EACnC,KAAK,IAAI,GAAG;EACZ,IAAI,KAAK,KAAY;CACvB;CACA,OAAO;AACT;AAUA,SAAgB,wBAAwB,OAA4C;CAClF,MAAM,EACJ,MACA,IACA,OACA,UACA,QACA,OACA,UACA,UACA,aACA,iBACA,kBACA,eACA,cACA,aACA,UACA,SACE;CACJ,MAAM,OAAO,OAAO;CASpB,IAAI;CACJ,IAAI;EACF,KAAK,MAAM,OAAO,KAAA,IAAY,KAAA,KAAA,GAAYC,aAAAA,YAAAA,CAAY,KAAK,EAAE;CAC/D,QAAQ;EACN,KAAK,KAAA;CACP;CACA,MAAM,OAAOC,oCAAAA,kBAAkB;EAC7B;EACA,YAAY,MAAM,QAAQ,MAAM,UAAU,IACrC,KAAK,aACN,KAAA;EACJ,SAAS;EACT,UAAU,YAAY;EACtB,WAAW,OAAO,QAAQ;CAC5B,CAAC;CACD,IAAI,CAAC,IACH,QACE,GAAA,kBAAA,IAAA,CAACC,eAAAA,QAAD;EACE,MAAK;EACL,SAAQ;EACR,UAAA;EACA,eAAa,GAAG,UAAU,iBAAiB,OAAO;EAClD,WAAU;EACJ;EACF;EAEJ,WAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;GAAM,WAAU;GAAiC,UAAA;EAAO,CAAA;CAClD,CAAA;CAKZ,MAAM,aAAa,MAAM;CACzB,MAAM,YACJ,eAAe,KAAA,KACf;EAAC,WAAW;EAAM,WAAW;EAAQ,WAAW;CAAM,CAAC,CAAC,MACrD,UAAU,CAAC,MAAM,WAAW,MAAM,WAAW,YAChD;CACF,MAAM,SACJ,eAAe,KAAA,KACf,CAAC,aACD,CAAC,WAAW,KAAK,WACjB;EAAC;EAAkB;EAAe;CAAkB,CAAC,CAAC,SAAS,WAAW,KAAK,MAAM;CAGvF,MAAM,UAAU,MAAM;CACtB,MAAM,YACJ,SAAS,KAAA,IACL,KAAA,IACA,SAAS,QACP;EAAE,MAAM;EAAS,OAAO,QAAQ;CAAM,IACtC,YAAY,KAAA,KACV,CAAC,QAAQ,aACT,QAAQ,SAAS,KAAA,KACjB,CAAC,QAAQ,WACT;EAAE,MAAM;EAAc,OAAO,QAAQ,KAAK;CAAO,IACjD,CAAC,KAAK,QACJ,EAAE,MAAM,UAAU,IAClB,KAAA;CACZ,MAAM,UACJ,KAAK,QAAQ,WAAW,IAAI,eAAe,CAAC,GAAG,KAAK,SAAS,GAAI,gBAAgB,CAAC,CAAE;CACtF,QACE,GAAA,kBAAA,IAAA,CAAC,YAAD;EACM;EACJ,MAAM,OAAO,KAAK;EAClB,WAAW,SAAS,SAAS,IAAI;EACjC,GAAK,OAAO,MAAM,OAAO,WAAW,EAAE,IAAI,KAAK,GAAG,IAAI,CAAC;EACvD,aAAa,MAAM;EACnB,GAAK,MAAM,UAAU,KAAA,IAAY,CAAC,IAAI,EAAE,OAAO,KAAK,MAAM;EAC1D,cAAc;EACd,GAAK,KAAK,SAAS,KAAA,IAAY,CAAC,IAAI,EAAE,MAAM,KAAK,KAAK;EACtD,aAAa;EACP;EACF;EACJ,QAAQ,UAAU,iBAAiB;EACnC,UAAU,YAAY;EACtB,WAAW,OAAO,QAAQ;EAC1B,OAAA,GAAMC,aAAAA,eAAAA,CAAe,SAAS,CAAC,CAAC;EAChC,GAAK,eAAe,KAAA,IAChB,CAAC,IACD;GACE,QAAQ,WAAW,OAAO;GAC1B,WAAW,WAAW,OAAO;GAC7B,QAAQ,MAAM,UAAU,CAAC;GACzB;GACA,UAAU;GACV,GAAI,cAAc,KAAA,IAAY,CAAC,IAAI,EAAE,UAAU;EACjD;EACM;EACA;EACG;EACI;EACC;CACnB,CAAA;AAEL;AAkDA,SAAS,WAAW,EAClB,IACA,MACA,UACA,IACA,aACA,cACA,MACA,aACA,MACA,IACA,QACA,UACA,WACA,MAAM,UACN,SAAS,MACT,YAAY,MACZ,SAAS,SACT,YAAY,OACZ,WAAW,OACX,WACA,QAAQ,WACR,UACA,UACA,aACA,iBACA,oBACsC;CACtC,MAAM,OAAA,GAAMC,cAAAA,OAAAA,CAAO;CACnB,MAAM,QAAA,GAAOC,cAAAA,YAAAA,CAAY;CACzB,MAAM,KAAA,GAAIC,cAAAA,aAAAA,CAAa;CACvB,MAAM,OAAA,GAAMC,cAAAA,WAAAA,CAAW;CACvB,MAAM,YAAY,IAAI,SAAS,IAAI,EAAE;CACrC,MAAM,WAAWC,wBAAAA,YAAY,IAAI,WAAW,YAAY;CACxD,MAAM,eAAA,GAAcC,aAAAA,qBAAAA,CAAqB,MAAM,IAAI,UAAU,SAAS,CAAC,CAAC,YAAY;CACpF,MAAM,cAAc,mBAAmB;CACvC,MAAM,uBAAuB,eAAe,WAAW,gBAAgB;CACvE,MAAM,CAAC,MAAM,WAAWC,MAAM,SAAS,KAAK;CAC5C,MAAM,CAAC,YAAY,iBAAiBA,MAAM,SAAS,KAAK;CAGxD,MAAM,QAAQ,SAAS,KAAA;CACvB,MAAM,CAAC,eAAe,oBAAoBA,MAAM,SAAS,KAAK;CAC9D,MAAM,gBAAgB;EACpB,IAAI,OAAO,cAAc,KAAK;CAChC,GAAG,CAAC,KAAK,CAAC;CACV,MAAM,gBAAgB;EACpB,IAAI,YAAY,iBAAiB,IAAI;CACvC,GAAG,CAAC,UAAU,CAAC;CACf,MAAM,CAAC,QAAQ,aAAaA,MAAM,SAAS,EAAE;CAC7C,MAAM,CAAC,iBAAiB,sBAAsBA,MAAM,SAAS,EAAE;CAC/D,MAAM,aAAaA,MAAM,OAAiC,IAAI;CAC9D,MAAM,eAAeA,MAAM,kBAAkB,WAAW,SAAS,MAAM,GAAG,CAAC,CAAC;CAE5E,MAAM,gBAAgB;EACpB,MAAM,SAAS,iBAAiB,mBAAmB,MAAM,GAAG,WAAW;EACvE,aAAa,aAAa,MAAM;CAClC,GAAG,CAAC,MAAM,CAAC;CACX,MAAM,gBAAgB;EACpB,IAAI,CAAC,MAAM;GACT,UAAU,EAAE;GACZ,mBAAmB,EAAE;EACvB;CACF,GAAG,CAAC,IAAI,CAAC;CAET,MAAM,iBAAiB,WAAW,KAAA,EAAqD;CACvF,MAAM,UACJ,OAAO,kBAAkB,YAAY,cAAc,SAAS,IAAI,gBAAgB;CAClF,MAAM,aAAa,MAAM;CACzB,MAAM,UAAA,GAASC,cAAAA,cAAAA,CAAc,EAAE;CAC/B,MAAM,cAAc,OAAO,WAAW;CACtC,MAAM,aAAaD,MAAM,cAErBE,wBAAAA,iBACE,WAAW,OAAO,MAAM,UAAU,EAAE,QAAQ,CAAC,oBAAoB,EAAE,IAClE,UAAU,OAAO,aAAa,KAAK,CACtC,GACF;EAAC;EAAW;EAAsB;CAAM,CAC1C;CACA,MAAM,SAASF,MAAM,eAAA,GAEjBG,aAAAA,kBAAAA,CAAkB;EAChB,QAAA,GAAOC,aAAAA,sBAAAA,CAAsB,YAAY;EACzC,QAAQ;EACR;EACA,eAAe;GAAE,UAAU;GAAI,KAAK;EAAU;CAChD,CAAC,GACH;EAAC;EAAc;EAAiB;EAAY;EAAI;CAAS,CAC3D;CACA,MAAM,UACJ,CAAC,eACD,SAAS,KAAA,KACTC,wBAAAA,iBAAiB,SAAS,UAAU,OAAO,aAAa,KAAK,CAAC;CAEhE,MAAM,YAAYL,MAAM,cAAc,IAAI,IAAI,OAAO,IAAI,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC;CAC3E,MAAM,cAAcA,MAAM,cAClB,KAAK,QAAQ,QAAQ,CAAC,UAAU,IAAI,OAAO,GAAG,CAAC,CAAC,GACtD,CAAC,MAAM,SAAS,CAClB;CACA,MAAM,aAAa,KAAK,SAAS,YAAY;CAC7C,MAAM,SAASA,MAAM,aAClB,SAAc;EACb,IAAI,CAAC,UAAU,KAAK,MAAM,QAAQ,OAAO,GAAG,MAAM,OAAO,IAAI,CAAC,GAAG;EACjE,SAAS,CAAC,GAAG,MAAM,IAAI,CAAC;CAC1B,GACA;EAAC;EAAM;EAAU;CAAM,CACzB;CACA,MAAM,SAASA,MAAM,aAClB,SAAc;EACb,MAAM,OAAO,KAAK,MAAM,QAAQ,OAAO,GAAG,MAAM,OAAO,IAAI,CAAC;EAC5D,IAAI,OAAO,CAAC,aAAa,UAAU,IAAI,OAAO,IAAI,CAAC,IAAI,CAAC,QAAQ;EAChE,SAAS,OAAO,KAAK,QAAQ,QAAQ,OAAO,GAAG,MAAM,OAAO,IAAI,CAAC,IAAI,CAAC,GAAG,MAAM,IAAI,CAAC;CACtF,GACA;EAAC;EAAM;EAAU;EAAQ;EAAW;CAAS,CAC/C;CACA,MAAM,EAAE,QAAQ,QAAQ,iBAAiBM,wBAAAA,gBAAgB;EACvD;EACA;EACA;EACA;EACA;EACA;EACA,YAAY;EACZ,MAAM;EACN;EACA,eAAe;GAAE,UAAU;GAAI,KAAK;EAAU;EAE9C,UAAU,WAAW;GACnB,OAAO,MAAM;GACb,cAAc,KAAK;EACrB;CACF,CAAC;CAED,MAAM,aAAA,GAAYC,cAAAA,QAAAA,CAAQ,IAAI;EAC5B,GAAI,SAAS,EAAE,OAAO,IAAI,CAAC;EAC3B,YAAY;GAAE,MAAM;GAAU,MAAM;GAAG,UAAU;EAAG;EACpD;EACA,YAAY;EACZ,SAAS,QAAQ,aAAa,QAAQ,CAAC;CACzC,CAAC;CACD,MAAM,OAAQ,UAAU,MAAM,QAAQ,CAAC;CACvC,MAAM,YAAY,UAAU,aAAa,UAAU;CACnD,MAAM,QAAQ,UAAU,UAAW,UAAU,QAA6B;CAE1E,IAAI,CAAC,WACH,QACE,GAAA,kBAAA,IAAA,CAACf,eAAAA,QAAD;EACE,MAAK;EACL,SAAQ;EACR,UAAA;EACA,eAAa,GAAG,YAAY;EAC5B,WAAU;EACJ;EACF;EAEJ,WAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;GAAM,WAAU;GAAkC,UAAA,KAAK,IAAI,MAAM,CAAC,CAAC,KAAK,IAAI,KAAK;EAAU,CAAA;CACrF,CAAA;CAIZ,MAAM,cAAc,EAAE,mBAAmB;EACvC,QAAQ,EAAE,MAAM,YAAY;EAC5B,cAAc,UAAU,YAAY;CACtC,CAAC;CAED,MAAM,QACJ,YAAY,WAAW,KAAK,eAAe,IAAI,QAC7C,GAAA,kBAAA,KAAA,CAAC,OAAD;EAAK,WAAU;EAAoC,eAAa,GAAG,OAAO;EAA1E,UAAA;GACG,YAAY,KAAK,SAChB,GAAA,kBAAA,IAAA,CAAC,MAAD;IAEM;IACJ,UAAU;IACN;IACM;IACC;IACX,MAAM;IACN,QAAQ,IAAI,iBAAiB,UAAU,WAAW,OAAO,GAAG,CAAC;IAC7D,UAAU,aAAa,QAAQ,CAAC;IAClB;IACd,gBAAgB,SAAS,KAAK,QAAQ,UAAU,OAAO,KAAK,MAAM,OAAO,GAAG,CAAC,CAAC;GAC/E,GAXM,OAAO,GAAG,CAWhB,CACF;GACA,aAAa,KACZ,GAAA,kBAAA,IAAA,CAAC,QAAD;IACE,eAAa,IAAI,WAAW,UAAU,SAAS;IAC/C,WAAU;IAET,UAAA,EAAE,qBAAqB;KACtB,OAAO;KACP,cAAc;IAChB,CAAC;GACG,CAAA,IACJ;GACH,aAAa,aAAa,QAAQ,YAAY,SAAS,KACtD,GAAA,kBAAA,IAAA,CAAC,UAAD;IACE,MAAK;IACL,eAAe;KAEb,aAAa;KACb,SAAS,KAAK,QAAQ,QAAQ,UAAU,IAAI,OAAO,GAAG,CAAC,CAAC,CAAC;IAC3D;IACA,eAAa,GAAG,OAAO;IACvB,WAAU;IAET,UAAA,EAAE,2BAA2B,EAAE,cAAc,YAAY,CAAC;GACrD,CAAA,IACN;EACD;;CAET,MAAM,SAAS,aACb,GAAA,kBAAA,IAAA,CAAC,KAAD;EACE,eAAa,GAAG,OAAO,QAAQ,UAAU;EACzC,MAAM,UAAU,SAAS,UAAU,WAAW,KAAA;EAC9C,WAAWgB,kBAAAA,GACT,WACA,UAAU,SAAS,UAAU,qBAAqB,uBACpD;EAEC,UAAA,UAAU,SAAS,UAChB,GAAG,EAAE,qBAAqB,EAAE,cAAc,4BAA4B,CAAC,EAAE,IAAI,WAAW,UAAU,OAAO,IAAI,KAAK,OAClH,UAAU,SAAS,eACjB,EAAE,yBAAyB;GACzB,OAAO,UAAU;GACjB,cAAc;EAChB,CAAC,IACD,EAAE,iBAAiB,EAAE,cAAc,WAAW,CAAC;CACpD,CAAA,IACD,YACF,GAAA,kBAAA,IAAA,CAAC,KAAD;EAAG,eAAa,GAAG,OAAO;EAAa,WAAU;EAC9C,UAAA,EAAE,uBAAuB,EAAE,cAAc,+BAA+B,CAAC;CACzE,CAAA,IACD,aACF,GAAA,kBAAA,IAAA,CAAC,KAAD;EAAG,eAAa,GAAG,OAAO;EAAc,WAAU;EAC/C,UAAA,EAAE,wBAAwB,EACzB,cACE,qFACJ,CAAC;CACA,CAAA,IACD;CAEJ,IAAI,MAAM,SAAS,WACjB,QACE,GAAA,kBAAA,KAAA,CAAC,OAAD;EAAK,WAAU;EAAf,UAAA;GACG;GACA;IACD,GAAA,kBAAA,KAAA,CAAChB,eAAAA,QAAD;IACE,MAAK;IACL,SAAQ;IACR,UAAA;IACA,aAAU;IACV,eAAa;IACb,wBAAqB;IACrB,WAAU;IAPZ,UAAA,EASE,GAAA,kBAAA,IAAA,CAAC,QAAD;KAAM,WAAU;KAAkC,UAAA;IAAkB,CAAA,IACpE,GAAA,kBAAA,IAAA,CAACiB,oBAAAA,aAAD;KAAa,WAAU;KAA2C,eAAA;IAAa,CAAA,CACzE;;EACL;;CAET,IAAI,MAAM,SAAS,aACjB,QACE,GAAA,kBAAA,KAAA,CAAC,OAAD;EAAK,WAAU;EAAf,UAAA;GACG;GACA;IACD,GAAA,kBAAA,KAAA,CAACjB,eAAAA,QAAD;IACE,MAAK;IACL,SAAQ;IACR,UAAA;IACA,eAAa;IACb,wBAAqB;IACrB,oBAAkB,KAAK;IACvB,WAAU;IAPZ,UAAA,EASE,GAAA,kBAAA,IAAA,CAAC,QAAD;KAAM,WAAU;KAAkC,UAAA;IAAkB,CAAA,IACpE,GAAA,kBAAA,IAAA,CAACkB,oBAAAA,iBAAD;KAAiB,WAAU;KAA8B,eAAA;IAAa,CAAA,CAChE;;IACR,GAAA,kBAAA,IAAA,CAAC,KAAD;IAAG,IAAI,KAAK;IAAQ,eAAa,KAAK;IAAQ,WAAU;IACrD,UAAA,KAAK;GACL,CAAA;EACA;;CAGT,QACE,GAAA,kBAAA,KAAA,CAACC,gBAAAA,SAAD;EAAe;EAAM,cAAc;EAAnC,UAAA;IACE,GAAA,kBAAA,KAAA,CAAC,OAAD;IAAK,WAAU;IAAf,UAAA;KACG;KACA;MACD,GAAA,kBAAA,IAAA,CAACC,gBAAAA,gBAAD;MAAgB,SAAA;MACd,WAAA,GAAA,kBAAA,KAAA,CAACpB,eAAAA,QAAD;OACE,KAAK;OACL,MAAK;OACL,SAAQ;OACR,MAAK;OACL,iBAAe;OACf,iBAAc;OACd,iBAAe;OACf,gBAAc,eAAe,KAAA;OAC7B,oBAAkB;OAClB,GAAK,mBAAmB,EAAE,qBAAqB,iBAAiB,IAAI,CAAC;OAC3D;OACV,eAAa;OACP;OACF;OACJ,WAAU;OAfZ,UAAA,EAiBE,GAAA,kBAAA,IAAA,CAAC,QAAD;QAAM,WAAU;QAA4C,UAAA;OAAkB,CAAA,IAC9E,GAAA,kBAAA,IAAA,CAACkB,oBAAAA,iBAAD;QAAiB,WAAU;QAA8B,eAAA;OAAa,CAAA,CAChE;;KACM,CAAA;IACb;;IACL,GAAA,kBAAA,IAAA,CAACG,gBAAAA,gBAAD;IAAgB,WAAU;IAA6C,OAAM;IAC1E,UAAA,MAAM,SAAS,WACd,GAAA,kBAAA,IAAA,CAAC,KAAD;KACE,WAAU;KACV,eAAa,KAAK;KAEjB,UAAA,KAAK;IACL,CAAA,KAEH,GAAA,kBAAA,KAAA,CAACC,wBAAAA,SAAD;KAAS,cAAc;KAAvB,UAAA;MACG,eAAe,KAAA,KAAa,WAC3B,GAAA,kBAAA,IAAA,CAACC,wBAAAA,cAAD;OACE,OAAO;OACP,eAAe;OACf,aAAa,EAAE,kBAAkB;QAC/B,QAAQ,EAAE,MAAM,YAAY;QAC5B,cAAc,UAAU,YAAY;OACtC,CAAC;OACD,eAAa,GAAG,YAAY;OAC5B,cAAY,EAAE,kBAAkB;QAC9B,QAAQ,EAAE,MAAM,YAAY;QAC5B,cAAc,UAAU;OAC1B,CAAC;OACD,YAAY,UAAU;QAEpB,IAAI,MAAM,QAAQ,eAAe,WAAW,MAAM,WAAW;SAC3D,MAAM,eAAe;SACrB,MAAM,OAAO,YAAY,GAAG,EAAE;SAC9B,IAAI,SAAS,KAAA,GACX,SAAS,KAAK,QAAQ,QAAQ,OAAO,GAAG,MAAM,OAAO,IAAI,CAAC,CAAC;QAC/D;QAEA,IAAI,MAAM,QAAQ,OAAO,WAAW,IAAI;SACtC,MAAM,eAAe;SACrB,SACG,cACC,iBAAiB,YAAY,+BAC/B,CAAC,EACC,MAAM;QACZ;OACF;MACD,CAAA;OAEH,GAAA,kBAAA,KAAA,CAACC,wBAAAA,aAAD;OACE,eAAa,GAAG,YAAY;OAC5B,wBAAqB;OACrB,cAAY;OAHd,UAAA;QAKG,cACC,GAAA,kBAAA,KAAA,CAAC,OAAD;SACE,WAAU;SACV,eAAa,GAAG,YAAY;SAF9B,UAAA,EAIE,GAAA,kBAAA,IAAA,CAACP,oBAAAA,aAAD;UAAa,WAAU;UAAuB,eAAA;SAAa,CAAA,IAC3D,GAAA,kBAAA,IAAA,CAAC,QAAD,EAAA,UAAO,EAAE,iBAAiB,EAAE,cAAc,WAAW,CAAC,EAAQ,CAAA,CAC3D;;QAEN,CAAC,aAAa,UACb,GAAA,kBAAA,IAAA,CAACQ,wBAAAA,WAAD;SACE,SAAQ;SACD;SACP,QAAQ,GAAG,YAAY;SACvB,WAAU;SACV,UAAU;UAAE,KAAK;UAAmB,SAAS;SAA0B;QACxE,CAAA;QAEF,CAAC,aAAa,CAAC,SAAS,KAAK,WAAW,MACvC,GAAA,kBAAA,IAAA,CAACC,wBAAAA,cAAD;SAAc,eAAa,GAAG,YAAY;SACvC,UAAA,EAAE,sBAAsB,EAAE,cAAc,cAAc,CAAC;QAC5C,CAAA;QAEf,CAAC,aACA,CAAC,SACD,KAAK,KAAK,QAAQ;SAChB,MAAM,WAAW,IAAI;SACrB,IAAI,aAAa,QAAQ,aAAa,KAAA,KAAa,aAAa,IAAI,OAAO;SAC3E,MAAM,QAAQ,OAAO,QAAQ;SAC7B,MAAM,QAAQ,SAAS,GAAG;SAC1B,MAAM,SAAS,KAAK,MAAM,QAAQ,OAAO,GAAG,MAAM,KAAK;SACvD,QACE,GAAA,kBAAA,KAAA,CAACC,wBAAAA,aAAD;UAEE,OAAO,GAAG,MAAM,GAAG;UACnB,eAAa,GAAG,YAAY,UAAU;UACtC,gBAAc;UACd,UAAU,SAAS,CAAC,YAAY,CAAC;UACjC,gBAAgB,OAAO,QAAe;UANxC,UAAA;YAQE,GAAA,kBAAA,IAAA,CAACC,oBAAAA,WAAD;YACE,WAAWZ,kBAAAA,GACT,yBACA,SAAS,gBAAgB,WAC3B;YACA,eAAA;WACD,CAAA;YACD,GAAA,kBAAA,IAAA,CAAC,QAAD;YAAM,WAAU;YAAY,UAAA;WAAY,CAAA;WAEvC,UAAU,QAAQ,QACjB,GAAA,kBAAA,KAAA,CAAC,QAAD;YAAM,WAAU;YAAhB,UAAA,CAAsE,KAClE,KACE;;UAEG;SArBN,GAAA,KAqBM;QAEjB,CAAC;OACQ;;OACb,GAAA,kBAAA,KAAA,CAAC,OAAD;OAAK,WAAU;OAAf,UAAA,CACG,UACC,GAAA,kBAAA,IAAA,CAAC,UAAD;QACE,MAAK;QACL,eAAe;SACb,QAAQ,KAAK;SACb,cAAc,IAAI;QACpB;QACA,eAAa,GAAG,YAAY;QAC5B,WAAU;QAET,UAAA,EAAE,iCAAiC,EAAE,cAAc,kBAAkB,CAAC;OACjE,CAAA,KAER,GAAA,kBAAA,IAAA,CAAC,QAAD,CAAO,CAAA,GAER,CAAC,YAAY,WACZ,GAAA,kBAAA,IAAA,CAACa,wBAAAA,sBAAD;QACM;QACO;QACH;QACR,OAAO;QACC;QACR,kBAAkB;SAChB,QAAQ,KAAK;SACb,aAAa;QACf;QACA,WAAU;OACX,CAAA,CAEA;;KACE;;GAEG,CAAA;GACf,iBAAiB,CAAC,SAAS,WAC1B,GAAA,kBAAA,IAAA,CAACC,oCAAAA,0BAAD;IACM;IACJ,MAAM;IACN,cAAc;IACd,gBAAgB,KAAA;IAChB,UAAA;IACA,UAAU;IACV,UAAU;IACE;IACC;IACC;IACP;IACP,YAAY;IACZ,GAAK,WAAW,CAAC,IAAI,EAAE,OAAO;IACjB;IACF;IACX,gBAAgB;GACjB,CAAA;GAEF;EACM;;AAEb;AAGA,SAAS,KAAK,EACZ,IACA,UACA,IACA,UACA,WACA,QACA,MAAM,UACN,UACA,cACA,YAYqB;CACrB,MAAM,KAAA,GAAI1B,cAAAA,aAAAA,CAAa;CAEvB,MAAM,aAAA,GADMF,cAAAA,OAAAA,CACQ,CAAC,CAAC,SAAS,IAAI,EAAE;CACrC,MAAM,WAAWI,wBAAAA,YAAY,IAAI,WAAW,YAAY;CACxD,MAAM,EAAE,QAAQ,eAAA,GAAcyB,cAAAA,iBAAAA,CAC5B,IACA,UACA,OAAO,KAAA,IAAY,KAAA,IAAY,EAAE,GAAG,CACtC;CACA,MAAM,QAAQ,SAAS,SAAS,MAAiC,IAAI,OAAO,QAAQ;CACpF,QACE,GAAA,kBAAA,KAAA,CAAC,QAAD;EACE,eAAa;EACb,iBAAe;EACf,WAAU;EAHZ,UAAA,EAKE,GAAA,kBAAA,IAAA,CAACC,wBAAAA,oBAAD;GACM;GACJ,OAAO;GACH;GACJ,WAAW,GAAG,UAAU,GAAG,OAAO,QAAQ;GAC1C,YAAY;GACZ,MAAM;GACN,OAAO,YAAY,EAAE,iBAAiB,EAAE,cAAc,WAAW,CAAC,IAAI;EACvE,CAAA,GACA,CAAC,aACA,GAAA,kBAAA,IAAA,CAAC,UAAD;GACE,MAAK;GACL,SAAS;GACT,eAAa,GAAG,OAAO;GACvB,cAAY,GAAG,EAAE,wBAAwB,EAAE,cAAc,QAAQ,CAAC,EAAE,GAAG;GACvE,WAAU;GAEV,WAAA,GAAA,kBAAA,IAAA,CAACC,oBAAAA,OAAD;IAAO,WAAU;IAAW,eAAA;GAAa,CAAA;EACnC,CAAA,CAEN;;AAEV"}
|