@burojs/ui-shadcn 0.6.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -0
- package/dist/bulk-summary-BWSAS7k2.js +143 -0
- package/dist/bulk-summary-BWSAS7k2.js.map +1 -0
- package/dist/bulk-summary-CrEij5vq.cjs +143 -0
- package/dist/bulk-summary-CrEij5vq.cjs.map +1 -0
- package/dist/{candidates-relation-picker-B116Bmqi.js → candidates-relation-picker-CS_JZ4Fd.js} +3 -3
- package/dist/{candidates-relation-picker-B116Bmqi.js.map → candidates-relation-picker-CS_JZ4Fd.js.map} +1 -1
- package/dist/{candidates-relation-picker-Cy0rTxvI.cjs → candidates-relation-picker-y5M6_oe4.cjs} +4 -4
- package/dist/{candidates-relation-picker-Cy0rTxvI.cjs.map → candidates-relation-picker-y5M6_oe4.cjs.map} +1 -1
- package/dist/{check-confirm-dialog-bRT8lt0d.js → check-confirm-dialog-2j0gOd7x.js} +384 -108
- package/dist/check-confirm-dialog-2j0gOd7x.js.map +1 -0
- package/dist/{check-confirm-dialog--gPFnEeg.cjs → check-confirm-dialog-C-NHgMST.cjs} +468 -107
- package/dist/check-confirm-dialog-C-NHgMST.cjs.map +1 -0
- package/dist/{dialog-B_rt9S1c.js → dialog-C123sVQ8.js} +4 -4
- package/dist/dialog-C123sVQ8.js.map +1 -0
- package/dist/{dialog-DRNUq9EA.cjs → dialog-vmMdfUWs.cjs} +3 -3
- package/dist/dialog-vmMdfUWs.cjs.map +1 -0
- package/dist/{filter-builder-nXqtM_h4.cjs → filter-builder-DFu0JP-1.cjs} +923 -120
- package/dist/filter-builder-DFu0JP-1.cjs.map +1 -0
- package/dist/{filter-builder-Bhn-dhZV.js → filter-builder-DaWHUZuV.js} +839 -108
- package/dist/filter-builder-DaWHUZuV.js.map +1 -0
- package/dist/{flow-runtime-D3avVH0I.cjs → flow-runtime-BpmrIUb6.cjs} +61 -29
- package/dist/flow-runtime-BpmrIUb6.cjs.map +1 -0
- package/dist/{flow-runtime-s6lkRPKF.js → flow-runtime-DyqTmK3H.js} +62 -30
- package/dist/flow-runtime-DyqTmK3H.js.map +1 -0
- package/dist/{flow-screen-Ci_Sik4Y.cjs → flow-screen-Bcz9NF2V.cjs} +128 -87
- package/dist/flow-screen-Bcz9NF2V.cjs.map +1 -0
- package/dist/{flow-screen-CCvgDI9G.js → flow-screen-DCdli6Ha.js} +128 -87
- package/dist/flow-screen-DCdli6Ha.js.map +1 -0
- package/dist/index.cjs +731 -906
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +455 -247
- package/dist/index.d.ts +455 -247
- package/dist/index.js +647 -828
- package/dist/index.js.map +1 -1
- package/dist/{inline-flow-host-DV1et3c6.cjs → inline-flow-host-3vw7LzzD.cjs} +5 -5
- package/dist/inline-flow-host-3vw7LzzD.cjs.map +1 -0
- package/dist/{inline-flow-host-B6FI0ctQ.js → inline-flow-host-C0rI9B7f.js} +5 -5
- package/dist/inline-flow-host-C0rI9B7f.js.map +1 -0
- package/dist/{candidates-gate-D33jXLHO.cjs → lazy-relation-picker-dialog-Bt8lrEIu.cjs} +10 -10
- package/dist/lazy-relation-picker-dialog-Bt8lrEIu.cjs.map +1 -0
- package/dist/{candidates-gate-Bb70LPAy.js → lazy-relation-picker-dialog-CAScLPb2.js} +11 -11
- package/dist/lazy-relation-picker-dialog-CAScLPb2.js.map +1 -0
- package/dist/{page-flow-route-i3ZfJ1sd.js → page-flow-route-BHUEpn0I.js} +36 -23
- package/dist/page-flow-route-BHUEpn0I.js.map +1 -0
- package/dist/{page-flow-route-hdnULWqd.cjs → page-flow-route-DS8IVTVH.cjs} +41 -28
- package/dist/page-flow-route-DS8IVTVH.cjs.map +1 -0
- package/dist/{place-node-C5SmIAYU.cjs → place-node-CNb3w-c7.cjs} +1869 -947
- package/dist/place-node-CNb3w-c7.cjs.map +1 -0
- package/dist/{place-node-NraNYKPx.js → place-node-CfzQvh4Y.js} +1824 -926
- package/dist/place-node-CfzQvh4Y.js.map +1 -0
- package/dist/{related-list-cards-BWZ9GfOG.js → related-list-cards-BIqqY69Y.js} +9 -4
- package/dist/{related-list-cards-BWZ9GfOG.js.map → related-list-cards-BIqqY69Y.js.map} +1 -1
- package/dist/{related-list-cards-CFq3meUn.cjs → related-list-cards-CelMxl4q.cjs} +9 -4
- package/dist/{related-list-cards-CFq3meUn.cjs.map → related-list-cards-CelMxl4q.cjs.map} +1 -1
- package/dist/{related-list-timeline-yrPsjA3F.js → related-list-timeline-CRh7xwLD.js} +6 -7
- package/dist/related-list-timeline-CRh7xwLD.js.map +1 -0
- package/dist/{related-list-timeline-CCzl71aA.cjs → related-list-timeline-DsLdi8sv.cjs} +5 -6
- package/dist/related-list-timeline-DsLdi8sv.cjs.map +1 -0
- package/dist/{related-rows-o0_t-Yce.js → related-rows--uP2iKQC.js} +25 -14
- package/dist/related-rows--uP2iKQC.js.map +1 -0
- package/dist/{related-rows-ea43qhhy.cjs → related-rows-MPnLCmPF.cjs} +27 -16
- package/dist/related-rows-MPnLCmPF.cjs.map +1 -0
- package/dist/{search-access-CPPmfuvy.js → relation-create-C43cdPQt.js} +595 -13
- package/dist/relation-create-C43cdPQt.js.map +1 -0
- package/dist/{search-access-Dugvttlg.cjs → relation-create-DDEYcKfw.cjs} +633 -14
- package/dist/relation-create-DDEYcKfw.cjs.map +1 -0
- package/dist/{relation-many-picker-bvkjkv-k.js → relation-many-picker-BJhaEqxz.js} +203 -102
- package/dist/relation-many-picker-BJhaEqxz.js.map +1 -0
- package/dist/{relation-many-picker-C8zFEjwd.cjs → relation-many-picker-DzuGJpEl.cjs} +211 -110
- package/dist/relation-many-picker-DzuGJpEl.cjs.map +1 -0
- package/dist/{relation-picker-Z0ZJJ5uS.js → relation-picker-D-tBBZW_.js} +23 -59
- package/dist/relation-picker-D-tBBZW_.js.map +1 -0
- package/dist/{relation-picker-gag0qSR5.cjs → relation-picker-DaIMzf2H.cjs} +29 -71
- package/dist/relation-picker-DaIMzf2H.cjs.map +1 -0
- package/dist/{relation-picker-dialog-0IzkjmiA.cjs → relation-picker-dialog-9Vg5vvR1.cjs} +133 -35
- package/dist/relation-picker-dialog-9Vg5vvR1.cjs.map +1 -0
- package/dist/{relation-picker-dialog-B6nIiJBf.js → relation-picker-dialog-DoEY8EkH.js} +134 -36
- package/dist/relation-picker-dialog-DoEY8EkH.js.map +1 -0
- package/dist/relation-select-B8Fq4o-t.js +48 -0
- package/dist/relation-select-B8Fq4o-t.js.map +1 -0
- package/dist/relation-select-Cr3CfNLy.cjs +55 -0
- package/dist/relation-select-Cr3CfNLy.cjs.map +1 -0
- package/dist/{resource-page-router-PXfMW9nh.cjs → resource-page-router-C_kGW2o_.cjs} +543 -300
- package/dist/resource-page-router-C_kGW2o_.cjs.map +1 -0
- package/dist/{resource-page-router-3RUf9d1j.js → resource-page-router-Diw6S6TK.js} +498 -255
- package/dist/resource-page-router-Diw6S6TK.js.map +1 -0
- package/dist/{rich-text-vTyVjDoa.cjs → rich-text-C_7JCvRo.cjs} +2 -2
- package/dist/{rich-text-vTyVjDoa.cjs.map → rich-text-C_7JCvRo.cjs.map} +1 -1
- package/dist/{rich-text-DFerywgz.js → rich-text-RcPBf6hr.js} +2 -2
- package/dist/{rich-text-DFerywgz.js.map → rich-text-RcPBf6hr.js.map} +1 -1
- package/dist/success-notices-Dl3M1fZP.cjs +46 -0
- package/dist/success-notices-Dl3M1fZP.cjs.map +1 -0
- package/dist/success-notices-LDEgEkc4.js +41 -0
- package/dist/success-notices-LDEgEkc4.js.map +1 -0
- package/dist/{toaster-Cr51X7bz.cjs → toaster-BbByOytP.cjs} +60 -15
- package/dist/toaster-BbByOytP.cjs.map +1 -0
- package/dist/{toaster-C02XbB9r.js → toaster-Ct_YHGs6.js} +43 -16
- package/dist/toaster-Ct_YHGs6.js.map +1 -0
- package/dist/{use-dialog-filter-state-C--oMAZG.js → use-dialog-filter-state-B_MqzHaT.js} +5 -4
- package/dist/use-dialog-filter-state-B_MqzHaT.js.map +1 -0
- package/dist/{use-dialog-filter-state-Dhe8PcB5.cjs → use-dialog-filter-state-tNvqAAjh.cjs} +6 -5
- package/dist/use-dialog-filter-state-tNvqAAjh.cjs.map +1 -0
- package/package.json +11 -11
- package/dist/bulk-summary-CwSd6Yjb.cjs +0 -108
- package/dist/bulk-summary-CwSd6Yjb.cjs.map +0 -1
- package/dist/bulk-summary-GxMDP4bw.js +0 -108
- package/dist/bulk-summary-GxMDP4bw.js.map +0 -1
- package/dist/candidates-gate-Bb70LPAy.js.map +0 -1
- package/dist/candidates-gate-D33jXLHO.cjs.map +0 -1
- package/dist/check-confirm-dialog--gPFnEeg.cjs.map +0 -1
- package/dist/check-confirm-dialog-bRT8lt0d.js.map +0 -1
- package/dist/dialog-B_rt9S1c.js.map +0 -1
- package/dist/dialog-DRNUq9EA.cjs.map +0 -1
- package/dist/filter-builder-Bhn-dhZV.js.map +0 -1
- package/dist/filter-builder-nXqtM_h4.cjs.map +0 -1
- package/dist/flow-runtime-D3avVH0I.cjs.map +0 -1
- package/dist/flow-runtime-s6lkRPKF.js.map +0 -1
- package/dist/flow-screen-CCvgDI9G.js.map +0 -1
- package/dist/flow-screen-Ci_Sik4Y.cjs.map +0 -1
- package/dist/inline-flow-host-B6FI0ctQ.js.map +0 -1
- package/dist/inline-flow-host-DV1et3c6.cjs.map +0 -1
- package/dist/page-flow-route-hdnULWqd.cjs.map +0 -1
- package/dist/page-flow-route-i3ZfJ1sd.js.map +0 -1
- package/dist/place-node-C5SmIAYU.cjs.map +0 -1
- package/dist/place-node-NraNYKPx.js.map +0 -1
- package/dist/related-list-timeline-CCzl71aA.cjs.map +0 -1
- package/dist/related-list-timeline-yrPsjA3F.js.map +0 -1
- package/dist/related-rows-ea43qhhy.cjs.map +0 -1
- package/dist/related-rows-o0_t-Yce.js.map +0 -1
- package/dist/relation-create-IA40t1hi.cjs +0 -364
- package/dist/relation-create-IA40t1hi.cjs.map +0 -1
- package/dist/relation-create-h6F9B4C4.js +0 -339
- package/dist/relation-create-h6F9B4C4.js.map +0 -1
- package/dist/relation-many-picker-C8zFEjwd.cjs.map +0 -1
- package/dist/relation-many-picker-bvkjkv-k.js.map +0 -1
- package/dist/relation-picker-Z0ZJJ5uS.js.map +0 -1
- package/dist/relation-picker-dialog-0IzkjmiA.cjs.map +0 -1
- package/dist/relation-picker-dialog-B6nIiJBf.js.map +0 -1
- package/dist/relation-picker-gag0qSR5.cjs.map +0 -1
- package/dist/resource-page-router-3RUf9d1j.js.map +0 -1
- package/dist/resource-page-router-PXfMW9nh.cjs.map +0 -1
- package/dist/search-access-CPPmfuvy.js.map +0 -1
- package/dist/search-access-Dugvttlg.cjs.map +0 -1
- package/dist/shell-BPlk8hEa.js +0 -205
- package/dist/shell-BPlk8hEa.js.map +0 -1
- package/dist/shell-DxhlsPwm.cjs +0 -271
- package/dist/shell-DxhlsPwm.cjs.map +0 -1
- package/dist/toaster-C02XbB9r.js.map +0 -1
- package/dist/toaster-Cr51X7bz.cjs.map +0 -1
- package/dist/use-dialog-filter-state-C--oMAZG.js.map +0 -1
- package/dist/use-dialog-filter-state-Dhe8PcB5.cjs.map +0 -1
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import { l as cn } from "./use-theme-dnE3Od3I.js";
|
|
2
|
-
import {
|
|
2
|
+
import { m as PopoverTrigger, p as PopoverContent, u as Popover } from "./toaster-Ct_YHGs6.js";
|
|
3
3
|
import { t as Button } from "./button-MWVeZaqj.js";
|
|
4
4
|
import { t as Input } from "./input-CUPStzZO.js";
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
5
|
+
import { C as CommandEmpty, D as CommandList, E as CommandItem, T as CommandInput, m as useRowTitle, o as narrowSearchDecl, w as CommandGroup, x as Command } from "./relation-create-C43cdPQt.js";
|
|
6
|
+
import { t as Skeleton } from "./skeleton-U2noefj5.js";
|
|
7
|
+
import { EMPTY_FILTER_GROUP, canonicalResourceKey, convertValueForOp, declaredText, enumValueText, formatEntityRef, isCompletePredicate, isFilterGroup, opArity, resolveEnumLabel, resolveSurfaceUi } from "@burojs/core";
|
|
8
|
+
import { DEFAULT_LANGUAGE_STORAGE_KEY, scopeValueOf, useApp, useChromeT, useEnumValues, useFieldDisplayText, useFieldImplementation, useFieldMeta, useFieldMetaSources, useI18nCaps, useImplementationTexts, useList, useListAccess, useLocale, useOptionalLocale, useOptionalT, useRelatedRecord, useResolvedFieldOptions } from "@burojs/react";
|
|
9
|
+
import { IconCheck, IconChevronDown, IconChevronUp, IconCirclePlus, IconFilter, IconFolderPlus, IconLoader2, IconPlus, IconX } from "@tabler/icons-react";
|
|
9
10
|
import * as React from "react";
|
|
10
|
-
import { Fragment, useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
11
|
+
import { Fragment, createElement, useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
11
12
|
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
12
13
|
import { cva } from "class-variance-authority";
|
|
13
14
|
import * as SelectPrimitive from "@radix-ui/react-select";
|
|
@@ -27,6 +28,505 @@ function saveLocale(code, storageKey = DEFAULT_LANGUAGE_STORAGE_KEY) {
|
|
|
27
28
|
} catch {}
|
|
28
29
|
}
|
|
29
30
|
//#endregion
|
|
31
|
+
//#region src/fields/legacy-field.ts
|
|
32
|
+
const STRING_ITEM = Object.freeze({ type: "string" });
|
|
33
|
+
const LEGACY_KIND_DECLARATIONS = Object.freeze({
|
|
34
|
+
string: { type: "string" },
|
|
35
|
+
text: {
|
|
36
|
+
type: "string",
|
|
37
|
+
format: "text"
|
|
38
|
+
},
|
|
39
|
+
textarea: {
|
|
40
|
+
type: "string",
|
|
41
|
+
format: "textarea"
|
|
42
|
+
},
|
|
43
|
+
code: {
|
|
44
|
+
type: "string",
|
|
45
|
+
format: "code"
|
|
46
|
+
},
|
|
47
|
+
email: {
|
|
48
|
+
type: "string",
|
|
49
|
+
format: "email"
|
|
50
|
+
},
|
|
51
|
+
url: {
|
|
52
|
+
type: "string",
|
|
53
|
+
format: "url"
|
|
54
|
+
},
|
|
55
|
+
uuid: {
|
|
56
|
+
type: "string",
|
|
57
|
+
format: "uuid"
|
|
58
|
+
},
|
|
59
|
+
number: { type: "number" },
|
|
60
|
+
boolean: { type: "boolean" },
|
|
61
|
+
date: {
|
|
62
|
+
type: "string",
|
|
63
|
+
format: "date"
|
|
64
|
+
},
|
|
65
|
+
dateTime: {
|
|
66
|
+
type: "string",
|
|
67
|
+
format: "date-time"
|
|
68
|
+
},
|
|
69
|
+
enum: {
|
|
70
|
+
type: "string",
|
|
71
|
+
format: "enum"
|
|
72
|
+
},
|
|
73
|
+
json: {
|
|
74
|
+
type: "object",
|
|
75
|
+
format: "json"
|
|
76
|
+
},
|
|
77
|
+
richText: {
|
|
78
|
+
type: "string",
|
|
79
|
+
format: "rich-text"
|
|
80
|
+
},
|
|
81
|
+
file: {
|
|
82
|
+
type: "array",
|
|
83
|
+
format: "file",
|
|
84
|
+
items: STRING_ITEM
|
|
85
|
+
},
|
|
86
|
+
relation: { type: "relation" },
|
|
87
|
+
relationMany: {
|
|
88
|
+
type: "array",
|
|
89
|
+
format: "relation",
|
|
90
|
+
items: STRING_ITEM
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
const SEMANTIC_KEYS = Object.freeze([
|
|
94
|
+
"type",
|
|
95
|
+
"format",
|
|
96
|
+
"meta",
|
|
97
|
+
"required",
|
|
98
|
+
"nullable",
|
|
99
|
+
"empty",
|
|
100
|
+
"default",
|
|
101
|
+
"constraints",
|
|
102
|
+
"texts",
|
|
103
|
+
"options",
|
|
104
|
+
"items",
|
|
105
|
+
"properties",
|
|
106
|
+
"additionalProperties",
|
|
107
|
+
"variants"
|
|
108
|
+
]);
|
|
109
|
+
function ownData(value, key) {
|
|
110
|
+
if (value === null || typeof value !== "object" && typeof value !== "function") return { present: false };
|
|
111
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, key);
|
|
112
|
+
if (descriptor === void 0 || !Object.prototype.propertyIsEnumerable.call(value, key) || !("value" in descriptor)) return { present: false };
|
|
113
|
+
return {
|
|
114
|
+
present: true,
|
|
115
|
+
value: descriptor.value
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
function ownString(value, key) {
|
|
119
|
+
const candidate = ownData(value, key).value;
|
|
120
|
+
return typeof candidate === "string" && candidate.length > 0 ? candidate : void 0;
|
|
121
|
+
}
|
|
122
|
+
function ownSelection(value, key) {
|
|
123
|
+
const candidate = ownData(value, key).value;
|
|
124
|
+
if (typeof candidate === "string") return candidate.length > 0 ? candidate : void 0;
|
|
125
|
+
return candidate !== null && typeof candidate === "object" && !Array.isArray(candidate) ? candidate : void 0;
|
|
126
|
+
}
|
|
127
|
+
function projectUiSelection(input) {
|
|
128
|
+
const selection = {};
|
|
129
|
+
const inputId = ownSelection(input, "input");
|
|
130
|
+
const displayId = ownSelection(input, "display");
|
|
131
|
+
if (inputId !== void 0) selection.input = inputId;
|
|
132
|
+
if (displayId !== void 0) selection.display = displayId;
|
|
133
|
+
return inputId === void 0 && displayId === void 0 ? void 0 : Object.freeze(selection);
|
|
134
|
+
}
|
|
135
|
+
function projectFieldDeclaration(input) {
|
|
136
|
+
const type = ownString(input, "type");
|
|
137
|
+
if (type === void 0) return void 0;
|
|
138
|
+
const projected = { type };
|
|
139
|
+
for (const key of SEMANTIC_KEYS) {
|
|
140
|
+
if (key === "type" || key === "items" || key === "properties" || key === "variants") continue;
|
|
141
|
+
const candidate = ownData(input, key);
|
|
142
|
+
if (candidate.present && candidate.value !== void 0) projected[key] = candidate.value;
|
|
143
|
+
}
|
|
144
|
+
const ui = projectUiSelection(ownData(input, "ui").value);
|
|
145
|
+
if (ui !== void 0) projected.ui = ui;
|
|
146
|
+
if (type === "array") {
|
|
147
|
+
const item = projectFieldDeclaration(ownData(input, "items").value);
|
|
148
|
+
if (item !== void 0) projected.items = item;
|
|
149
|
+
}
|
|
150
|
+
if (type === "object") {
|
|
151
|
+
const properties = ownData(input, "properties").value;
|
|
152
|
+
if (properties !== null && typeof properties === "object" && !Array.isArray(properties)) {
|
|
153
|
+
const output = {};
|
|
154
|
+
for (const key of Object.keys(properties)) {
|
|
155
|
+
const child = projectFieldDeclaration(ownData(properties, key).value);
|
|
156
|
+
if (child !== void 0) output[key] = child;
|
|
157
|
+
}
|
|
158
|
+
projected.properties = Object.freeze(output);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
if (type === "union") {
|
|
162
|
+
const variants = ownData(input, "variants").value;
|
|
163
|
+
if (Array.isArray(variants)) {
|
|
164
|
+
const output = [];
|
|
165
|
+
for (let index = 0; index < variants.length; index += 1) {
|
|
166
|
+
const child = projectFieldDeclaration(ownData(variants, index).value);
|
|
167
|
+
if (child !== void 0) output.push(child);
|
|
168
|
+
}
|
|
169
|
+
projected.variants = Object.freeze(output);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
return Object.freeze(projected);
|
|
173
|
+
}
|
|
174
|
+
function projectUiHints(input) {
|
|
175
|
+
if (input === null || typeof input !== "object" || Array.isArray(input)) return void 0;
|
|
176
|
+
const projected = {};
|
|
177
|
+
for (const key of Object.keys(input)) {
|
|
178
|
+
const candidate = ownData(input, key);
|
|
179
|
+
if (candidate.present) projected[key] = candidate.value;
|
|
180
|
+
}
|
|
181
|
+
return Object.freeze(projected);
|
|
182
|
+
}
|
|
183
|
+
function enumValueOptions(field) {
|
|
184
|
+
const own = ownData(field, "values").value;
|
|
185
|
+
const values = Array.isArray(own) ? own : ownData(ownData(field, "ui").value, "values").value;
|
|
186
|
+
if (!Array.isArray(values)) return void 0;
|
|
187
|
+
const options = values.filter((value) => typeof value === "string").map((value) => Object.freeze({ value }));
|
|
188
|
+
return options.length === 0 ? void 0 : Object.freeze(options);
|
|
189
|
+
}
|
|
190
|
+
function lowerLegacyField(field) {
|
|
191
|
+
const declaration = projectFieldDeclaration(ownData(field, "declaration").value);
|
|
192
|
+
if (declaration !== void 0) return declaration;
|
|
193
|
+
const direct = projectFieldDeclaration(field);
|
|
194
|
+
const kind = ownString(field, "kind");
|
|
195
|
+
const mapping = kind === void 0 ? void 0 : LEGACY_KIND_DECLARATIONS[kind];
|
|
196
|
+
if (direct !== void 0) {
|
|
197
|
+
const format = direct.format ?? mapping?.format ?? kind;
|
|
198
|
+
return format === void 0 ? direct : Object.freeze({
|
|
199
|
+
...direct,
|
|
200
|
+
format
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
const ui = projectUiHints(ownData(field, "ui").value);
|
|
204
|
+
const type = kind === "file" && ui?.multiple !== true ? "unknown" : mapping?.type ?? "unknown";
|
|
205
|
+
const format = ownString(field, "format") ?? mapping?.format ?? kind;
|
|
206
|
+
const items = type === "array" ? projectFieldDeclaration(ownData(field, "items").value) ?? mapping?.items ?? STRING_ITEM : void 0;
|
|
207
|
+
const meta = ownData(field, "meta");
|
|
208
|
+
const texts = ownData(field, "texts");
|
|
209
|
+
const options = kind === "enum" ? enumValueOptions(field) : void 0;
|
|
210
|
+
const required = ownData(field, "required");
|
|
211
|
+
const nullable = ownData(field, "nullable");
|
|
212
|
+
const opts = ownData(field, "opts").value;
|
|
213
|
+
const optionRequired = ownData(opts, "required");
|
|
214
|
+
const optionNullable = ownData(opts, "nullable");
|
|
215
|
+
const requiredValue = required.present ? required.value : optionRequired.value;
|
|
216
|
+
const nullableValue = nullable.present ? nullable.value : optionNullable.value;
|
|
217
|
+
return Object.freeze({
|
|
218
|
+
type,
|
|
219
|
+
...format === void 0 ? {} : { format },
|
|
220
|
+
...items === void 0 ? {} : { items },
|
|
221
|
+
...meta.present && meta.value !== void 0 ? { meta: meta.value } : {},
|
|
222
|
+
...options === void 0 ? {} : { options },
|
|
223
|
+
...texts.present && texts.value !== void 0 ? { texts: texts.value } : {},
|
|
224
|
+
...requiredValue === true ? { required: true } : {},
|
|
225
|
+
...nullableValue === true ? { nullable: true } : {}
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
function lowerLegacyFieldSource(field) {
|
|
229
|
+
const declaration = lowerLegacyField(field);
|
|
230
|
+
const ui = projectUiHints(ownData(field, "ui").value);
|
|
231
|
+
if (ui === void 0) return declaration;
|
|
232
|
+
return Object.freeze({
|
|
233
|
+
...declaration,
|
|
234
|
+
ui
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
function fieldDescription(field) {
|
|
238
|
+
const declaration = field?.declaration;
|
|
239
|
+
return declaredText("field", declaration, "description") ?? declaredText("field", field, "description");
|
|
240
|
+
}
|
|
241
|
+
//#endregion
|
|
242
|
+
//#region src/fields/resolved-field.tsx
|
|
243
|
+
const EMPTY = Object.freeze({});
|
|
244
|
+
const EMPTY_ISSUES = Object.freeze([]);
|
|
245
|
+
const EMPTY_OPTIONS = Object.freeze([]);
|
|
246
|
+
function canonical(field) {
|
|
247
|
+
return lowerLegacyField(field);
|
|
248
|
+
}
|
|
249
|
+
function useLoweredField(source) {
|
|
250
|
+
return useMemo(() => {
|
|
251
|
+
const sourceAtEdge = lowerLegacyFieldSource(source);
|
|
252
|
+
return {
|
|
253
|
+
sourceAtEdge,
|
|
254
|
+
field: canonical(sourceAtEdge),
|
|
255
|
+
ui: sourceAtEdge.ui ?? EMPTY
|
|
256
|
+
};
|
|
257
|
+
}, [source]);
|
|
258
|
+
}
|
|
259
|
+
const warnedObjectLabels = new Set();
|
|
260
|
+
function optionsFor(field, name) {
|
|
261
|
+
if (Array.isArray(field.options)) return field.options;
|
|
262
|
+
const candidate = field.meta?.options;
|
|
263
|
+
if (!Array.isArray(candidate)) return EMPTY_OPTIONS;
|
|
264
|
+
const output = [];
|
|
265
|
+
for (const item of candidate) {
|
|
266
|
+
if (item === null || typeof item !== "object" || Array.isArray(item)) continue;
|
|
267
|
+
const raw = item;
|
|
268
|
+
if (typeof raw.value !== "string" && typeof raw.value !== "number" && typeof raw.value !== "boolean" && raw.value !== null) continue;
|
|
269
|
+
const { value, label, description, ...legacyMeta } = raw;
|
|
270
|
+
if (typeof label !== "string" && process.env.NODE_ENV !== "production" && !warnedObjectLabels.has(`${name}:${value}`)) {
|
|
271
|
+
warnedObjectLabels.add(`${name}:${value}`);
|
|
272
|
+
console.warn(`[buro/ui-shadcn] field '${name}': option '${value}' has a non-string label, so it renders its raw value. Declare a plain string label and put the translations under fields.${name}.options.${value} (see docs/i18n-guide.md).`);
|
|
273
|
+
}
|
|
274
|
+
output.push(Object.freeze({
|
|
275
|
+
value,
|
|
276
|
+
label: typeof label === "string" ? label : String(value),
|
|
277
|
+
...typeof description === "string" ? { description } : {},
|
|
278
|
+
...Object.keys(legacyMeta).length === 0 ? {} : { meta: Object.freeze(legacyMeta) }
|
|
279
|
+
}));
|
|
280
|
+
}
|
|
281
|
+
return Object.freeze(output);
|
|
282
|
+
}
|
|
283
|
+
function useContextOptions(field, name, meta, ui, resource) {
|
|
284
|
+
const legacy = field.meta?.options;
|
|
285
|
+
const options = useMemo(() => optionsFor(field, name), [
|
|
286
|
+
field.options,
|
|
287
|
+
legacy,
|
|
288
|
+
name
|
|
289
|
+
]);
|
|
290
|
+
return useResolvedFieldOptions({
|
|
291
|
+
name,
|
|
292
|
+
meta,
|
|
293
|
+
ui,
|
|
294
|
+
texts: field.texts
|
|
295
|
+
}, options, resource.id);
|
|
296
|
+
}
|
|
297
|
+
function useContextTexts(field, name, resolved, resource) {
|
|
298
|
+
return useImplementationTexts({
|
|
299
|
+
fieldName: name,
|
|
300
|
+
fieldTexts: field.texts,
|
|
301
|
+
id: resolved.id,
|
|
302
|
+
implementation: resolved.implementation
|
|
303
|
+
}, resource.id);
|
|
304
|
+
}
|
|
305
|
+
function useFieldRuntimeContext(resource, record, values, metaSources) {
|
|
306
|
+
const locale = useLocale();
|
|
307
|
+
const caps = useI18nCaps(canonicalResourceKey(resource));
|
|
308
|
+
const metaSourcesOut = useFieldMetaSources({
|
|
309
|
+
record,
|
|
310
|
+
values,
|
|
311
|
+
extra: metaSources
|
|
312
|
+
});
|
|
313
|
+
const scopeState = metaSourcesOut.scope;
|
|
314
|
+
const scope = scopeState === void 0 ? void 0 : scopeValueOf(scopeState);
|
|
315
|
+
return useMemo(() => Object.freeze({
|
|
316
|
+
i18n: Object.freeze({
|
|
317
|
+
locale,
|
|
318
|
+
has: caps.has,
|
|
319
|
+
t: caps.t
|
|
320
|
+
}),
|
|
321
|
+
sources: Object.freeze({
|
|
322
|
+
record: record ?? EMPTY,
|
|
323
|
+
values: values ?? EMPTY,
|
|
324
|
+
scope: scope ?? EMPTY
|
|
325
|
+
}),
|
|
326
|
+
metaSources: metaSourcesOut
|
|
327
|
+
}), [
|
|
328
|
+
caps,
|
|
329
|
+
locale,
|
|
330
|
+
record,
|
|
331
|
+
scope,
|
|
332
|
+
values,
|
|
333
|
+
metaSourcesOut
|
|
334
|
+
]);
|
|
335
|
+
}
|
|
336
|
+
function useFieldMetaDiagnostic(state, resource, name, field, surface, entry) {
|
|
337
|
+
const reported = useRef(void 0);
|
|
338
|
+
const reason = state.status === "failed" ? state.reason : void 0;
|
|
339
|
+
useEffect(() => {
|
|
340
|
+
if (reason === void 0 || reported.current === reason || process.env.NODE_ENV === "production") return;
|
|
341
|
+
reported.current = reason;
|
|
342
|
+
const implementation = resolveSurfaceUi(field, surface, entry).use ?? field.format ?? field.type;
|
|
343
|
+
console.error(`[burojs] field.meta-invalid: ${formatEntityRef(resource)}.${name} (${implementation}): ${reason}`);
|
|
344
|
+
}, [
|
|
345
|
+
reason,
|
|
346
|
+
resource,
|
|
347
|
+
name,
|
|
348
|
+
field,
|
|
349
|
+
surface,
|
|
350
|
+
entry
|
|
351
|
+
]);
|
|
352
|
+
}
|
|
353
|
+
function MetaPlaceholder({ state }) {
|
|
354
|
+
const t = useOptionalT();
|
|
355
|
+
if (state.status === "pending") return jsx(Skeleton, {
|
|
356
|
+
"data-buro-skeleton": "",
|
|
357
|
+
"data-buro-field-meta": "pending",
|
|
358
|
+
className: "h-9 w-full"
|
|
359
|
+
});
|
|
360
|
+
return jsx("div", {
|
|
361
|
+
role: "alert",
|
|
362
|
+
"data-buro-field-meta": "failed",
|
|
363
|
+
className: "rounded-md border border-dashed px-3 py-2 text-sm text-muted-foreground",
|
|
364
|
+
children: t("field.misconfigured", { defaultValue: "This field is misconfigured" })
|
|
365
|
+
});
|
|
366
|
+
}
|
|
367
|
+
function useFieldImplementationDiagnostic(diagnostic) {
|
|
368
|
+
const reported = useRef(void 0);
|
|
369
|
+
useEffect(() => {
|
|
370
|
+
if (diagnostic === void 0 || reported.current === diagnostic || process.env.NODE_ENV === "production") return;
|
|
371
|
+
reported.current = diagnostic;
|
|
372
|
+
console.warn("[buro/ui-shadcn] missing-field-implementation; rendering fallback.", diagnostic);
|
|
373
|
+
}, [diagnostic]);
|
|
374
|
+
}
|
|
375
|
+
function ResolvedFieldDisplay({ resource, name, field: source, value, record, testId, fallback, presentation, metaSources, uiSurface = "display", columnEntry }) {
|
|
376
|
+
const { sourceAtEdge, field, ui } = useLoweredField(source);
|
|
377
|
+
const surfaceField = useMemo(() => sourceAtEdge.ui === void 0 ? field : Object.freeze({
|
|
378
|
+
...field,
|
|
379
|
+
ui
|
|
380
|
+
}), [
|
|
381
|
+
sourceAtEdge,
|
|
382
|
+
field,
|
|
383
|
+
ui
|
|
384
|
+
]);
|
|
385
|
+
const resolved = useFieldImplementation({
|
|
386
|
+
field: surfaceField,
|
|
387
|
+
surface: "display",
|
|
388
|
+
uiSurface,
|
|
389
|
+
...columnEntry === void 0 ? {} : { uiEntry: columnEntry }
|
|
390
|
+
});
|
|
391
|
+
const runtimeContext = useFieldRuntimeContext(resource, record, record, metaSources);
|
|
392
|
+
const metaState = useFieldMeta(field, resolved.implementation, runtimeContext.metaSources, resolved.params);
|
|
393
|
+
const textField = useMemo(() => Object.freeze({
|
|
394
|
+
...surfaceField,
|
|
395
|
+
name
|
|
396
|
+
}), [surfaceField, name]);
|
|
397
|
+
const text = useFieldDisplayText(textField, value, {
|
|
398
|
+
surface: uiSurface,
|
|
399
|
+
...columnEntry === void 0 ? {} : { entry: columnEntry },
|
|
400
|
+
resource: canonicalResourceKey(resource)
|
|
401
|
+
});
|
|
402
|
+
const options = useContextOptions(field, name, metaState.status === "ready" ? metaState.meta : void 0, ui, resource);
|
|
403
|
+
const texts = useContextTexts(field, name, resolved, resource);
|
|
404
|
+
useFieldMetaDiagnostic(metaState, resource, name, surfaceField, uiSurface, columnEntry);
|
|
405
|
+
useFieldImplementationDiagnostic(resolved.diagnostic);
|
|
406
|
+
if (resolved.Component === void 0) return jsx(Fragment$1, { children: fallback });
|
|
407
|
+
if (metaState.status !== "ready") return jsx(MetaPlaceholder, { state: metaState });
|
|
408
|
+
const Component = resolved.Component;
|
|
409
|
+
const context = {
|
|
410
|
+
owner: resource,
|
|
411
|
+
resource,
|
|
412
|
+
field,
|
|
413
|
+
path: Object.freeze([name]),
|
|
414
|
+
surface: "display",
|
|
415
|
+
presentation: presentation ?? "default",
|
|
416
|
+
record,
|
|
417
|
+
meta: metaState.meta,
|
|
418
|
+
ui,
|
|
419
|
+
options,
|
|
420
|
+
texts,
|
|
421
|
+
...text === void 0 ? {} : { text },
|
|
422
|
+
i18n: runtimeContext.i18n,
|
|
423
|
+
sources: runtimeContext.sources,
|
|
424
|
+
pending: false,
|
|
425
|
+
touched: false,
|
|
426
|
+
issues: EMPTY_ISSUES,
|
|
427
|
+
accessibility: EMPTY,
|
|
428
|
+
testIds: testId === void 0 ? EMPTY : Object.freeze({ display: testId })
|
|
429
|
+
};
|
|
430
|
+
if (resolved.multiple) return createElement(Component, {
|
|
431
|
+
...context,
|
|
432
|
+
multiple: true,
|
|
433
|
+
value: Array.isArray(value) ? value : []
|
|
434
|
+
});
|
|
435
|
+
return createElement(Component, {
|
|
436
|
+
...context,
|
|
437
|
+
multiple: false,
|
|
438
|
+
value
|
|
439
|
+
});
|
|
440
|
+
}
|
|
441
|
+
function ResolvedFieldInput({ resource, name, field: source, value, record, form, testId, inputId, onChange, onBlur, disabled, required, placeholder, descriptionId, labelId, errorId, errorMessage, pending = false, touched = errorMessage != null, issues: suppliedIssues, relationSeeds, fixedFilters, link, metaSources, fallback }) {
|
|
442
|
+
const { sourceAtEdge, field: base } = useLoweredField(source);
|
|
443
|
+
const field = useMemo(() => required === void 0 || required === base.required ? base : Object.freeze({
|
|
444
|
+
...base,
|
|
445
|
+
required
|
|
446
|
+
}), [base, required]);
|
|
447
|
+
const ui = useMemo(() => Object.freeze({
|
|
448
|
+
...sourceAtEdge.ui ?? EMPTY,
|
|
449
|
+
...placeholder === void 0 ? {} : { placeholder }
|
|
450
|
+
}), [sourceAtEdge, placeholder]);
|
|
451
|
+
const inputField = useMemo(() => ({
|
|
452
|
+
...field,
|
|
453
|
+
ui
|
|
454
|
+
}), [field, ui]);
|
|
455
|
+
const resolved = useFieldImplementation({
|
|
456
|
+
field: inputField,
|
|
457
|
+
surface: "input"
|
|
458
|
+
});
|
|
459
|
+
const runtimeContext = useFieldRuntimeContext(resource, record, form, metaSources);
|
|
460
|
+
const metaState = useFieldMeta(field, resolved.implementation, runtimeContext.metaSources, resolved.params);
|
|
461
|
+
const options = useContextOptions(field, name, metaState.status === "ready" ? metaState.meta : void 0, ui, resource);
|
|
462
|
+
const texts = useContextTexts(field, name, resolved, resource);
|
|
463
|
+
useFieldMetaDiagnostic(metaState, resource, name, inputField, "input");
|
|
464
|
+
useFieldImplementationDiagnostic(resolved.diagnostic);
|
|
465
|
+
if (resolved.Component === void 0) return jsx(Fragment$1, { children: fallback });
|
|
466
|
+
if (metaState.status !== "ready") return jsx(MetaPlaceholder, { state: metaState });
|
|
467
|
+
const Component = resolved.Component;
|
|
468
|
+
const issues = suppliedIssues ?? (errorMessage == null ? EMPTY_ISSUES : Object.freeze([Object.freeze({
|
|
469
|
+
code: "ui.field-error",
|
|
470
|
+
message: {
|
|
471
|
+
key: "ui.field-error",
|
|
472
|
+
default: errorMessage
|
|
473
|
+
},
|
|
474
|
+
path: Object.freeze([]),
|
|
475
|
+
source: "field",
|
|
476
|
+
severity: "error"
|
|
477
|
+
})]));
|
|
478
|
+
const context = {
|
|
479
|
+
owner: resource,
|
|
480
|
+
resource,
|
|
481
|
+
field,
|
|
482
|
+
path: Object.freeze([name]),
|
|
483
|
+
surface: "input",
|
|
484
|
+
presentation: "default",
|
|
485
|
+
record,
|
|
486
|
+
form,
|
|
487
|
+
meta: metaState.meta,
|
|
488
|
+
ui,
|
|
489
|
+
options,
|
|
490
|
+
texts,
|
|
491
|
+
i18n: runtimeContext.i18n,
|
|
492
|
+
sources: runtimeContext.sources,
|
|
493
|
+
pending,
|
|
494
|
+
touched,
|
|
495
|
+
issues,
|
|
496
|
+
accessibility: Object.freeze({
|
|
497
|
+
input: inputId ?? name,
|
|
498
|
+
label: labelId,
|
|
499
|
+
description: descriptionId,
|
|
500
|
+
error: errorId
|
|
501
|
+
}),
|
|
502
|
+
testIds: testId === void 0 ? EMPTY : Object.freeze({ input: testId })
|
|
503
|
+
};
|
|
504
|
+
if (resolved.multiple) return createElement(Component, {
|
|
505
|
+
...context,
|
|
506
|
+
multiple: true,
|
|
507
|
+
value: Array.isArray(value) ? value : [],
|
|
508
|
+
onChange,
|
|
509
|
+
onBlur,
|
|
510
|
+
disabled,
|
|
511
|
+
relationSeeds,
|
|
512
|
+
fixedFilters,
|
|
513
|
+
link,
|
|
514
|
+
metaSources: runtimeContext.metaSources
|
|
515
|
+
});
|
|
516
|
+
return createElement(Component, {
|
|
517
|
+
...context,
|
|
518
|
+
multiple: false,
|
|
519
|
+
value,
|
|
520
|
+
onChange,
|
|
521
|
+
onBlur,
|
|
522
|
+
disabled,
|
|
523
|
+
relationSeeds,
|
|
524
|
+
fixedFilters,
|
|
525
|
+
link,
|
|
526
|
+
metaSources: runtimeContext.metaSources
|
|
527
|
+
});
|
|
528
|
+
}
|
|
529
|
+
//#endregion
|
|
30
530
|
//#region src/primitives/badge.tsx
|
|
31
531
|
const badgeVariants = cva("inline-flex max-w-full items-center truncate rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-hidden focus:ring-2 focus:ring-ring focus:ring-offset-2", {
|
|
32
532
|
variants: { variant: {
|
|
@@ -104,11 +604,11 @@ const SelectGroup = SelectPrimitive.Group;
|
|
|
104
604
|
const SelectValue = SelectPrimitive.Value;
|
|
105
605
|
const SelectTrigger = React.forwardRef(({ className, children, ...props }, ref) => jsxs(SelectPrimitive.Trigger, {
|
|
106
606
|
ref,
|
|
107
|
-
className: cn("flex h-9 w-full items-center justify-between rounded-md border border-input bg-background text-foreground px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus:outline-hidden focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 disabled:bg-input-disabled [&>span]:
|
|
607
|
+
className: cn("flex h-9 w-full items-center justify-between rounded-md border border-input bg-background text-foreground px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus:outline-hidden focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 disabled:bg-input-disabled [&>span]:block [&>span]:truncate [&>span]:min-w-0 [&>span]:max-h-[1lh] [&>span]:overflow-hidden [&>span]:text-left", className),
|
|
108
608
|
...props,
|
|
109
609
|
children: [children, jsx(SelectPrimitive.Icon, {
|
|
110
610
|
asChild: true,
|
|
111
|
-
children: jsx(IconChevronDown, { className: "h-4 w-4 opacity-50" })
|
|
611
|
+
children: jsx(IconChevronDown, { className: "h-4 w-4 shrink-0 opacity-50" })
|
|
112
612
|
})]
|
|
113
613
|
}));
|
|
114
614
|
SelectTrigger.displayName = SelectPrimitive.Trigger.displayName;
|
|
@@ -147,15 +647,24 @@ const SelectLabel = React.forwardRef(({ className, ...props }, ref) => jsx(Selec
|
|
|
147
647
|
...props
|
|
148
648
|
}));
|
|
149
649
|
SelectLabel.displayName = SelectPrimitive.Label.displayName;
|
|
150
|
-
const SelectItem = React.forwardRef(({ className, children, ...props }, ref) =>
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
children: jsx(
|
|
157
|
-
|
|
158
|
-
}))
|
|
650
|
+
const SelectItem = React.forwardRef(({ className, children, description, ...props }, ref) => {
|
|
651
|
+
const hasDescription = description !== void 0 && description !== null;
|
|
652
|
+
return jsxs(SelectPrimitive.Item, {
|
|
653
|
+
ref,
|
|
654
|
+
className: cn("relative flex w-full cursor-default select-none rounded-sm py-1.5 pl-8 pr-2 text-sm outline-hidden focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50", hasDescription ? "items-start" : "items-center", className),
|
|
655
|
+
...props,
|
|
656
|
+
children: [jsx("span", {
|
|
657
|
+
className: cn("absolute left-2 flex h-3.5 w-3.5 items-center justify-center", hasDescription && "top-[9px]"),
|
|
658
|
+
children: jsx(SelectPrimitive.ItemIndicator, { children: jsx(IconCheck, { className: "h-4 w-4" }) })
|
|
659
|
+
}), !hasDescription ? jsx(SelectPrimitive.ItemText, { children }) : jsxs("span", {
|
|
660
|
+
className: "flex min-w-0 flex-col gap-0.5",
|
|
661
|
+
children: [jsx(SelectPrimitive.ItemText, { children }), jsx("span", {
|
|
662
|
+
className: "text-xs leading-snug text-muted-foreground",
|
|
663
|
+
children: description
|
|
664
|
+
})]
|
|
665
|
+
})]
|
|
666
|
+
});
|
|
667
|
+
});
|
|
159
668
|
SelectItem.displayName = SelectPrimitive.Item.displayName;
|
|
160
669
|
const SelectSeparator = React.forwardRef(({ className, ...props }, ref) => jsx(SelectPrimitive.Separator, {
|
|
161
670
|
ref,
|
|
@@ -208,9 +717,9 @@ function formatNumber(n, locale) {
|
|
|
208
717
|
//#endregion
|
|
209
718
|
//#region src/list-kit/filter-builder/describe-row.ts
|
|
210
719
|
const MAX_DESCRIBE_DEPTH = 2;
|
|
211
|
-
function describeValue(value,
|
|
720
|
+
function describeValue(value, chromeT, spec, formatValue) {
|
|
212
721
|
if (value === void 0) return null;
|
|
213
|
-
if (Array.isArray(value)) return
|
|
722
|
+
if (Array.isArray(value)) return chromeT("filters.selectedCount", { count: value.length }, `${value.length} selected`);
|
|
214
723
|
const optionLabel = spec?.options?.find((o) => o.value === String(value))?.label;
|
|
215
724
|
if (optionLabel !== void 0) return optionLabel;
|
|
216
725
|
if (spec && formatValue) {
|
|
@@ -219,20 +728,20 @@ function describeValue(value, t, spec, formatValue) {
|
|
|
219
728
|
}
|
|
220
729
|
return String(value);
|
|
221
730
|
}
|
|
222
|
-
function describeAt(row, available,
|
|
731
|
+
function describeAt(row, available, chromeT, depth, formatValue) {
|
|
223
732
|
if (isFilterGroup(row)) {
|
|
224
733
|
if (depth > MAX_DESCRIBE_DEPTH) return "…";
|
|
225
|
-
const joinLabel =
|
|
226
|
-
return `(${row.rows.map((child) => describeAt(child, available,
|
|
734
|
+
const joinLabel = chromeT(`filters.${row.join}`, void 0, row.join);
|
|
735
|
+
return `(${row.rows.map((child) => describeAt(child, available, chromeT, depth + 1, formatValue)).join(` ${joinLabel} `)})`;
|
|
227
736
|
}
|
|
228
737
|
const spec = available.find((s) => s.field === row.field);
|
|
229
738
|
const label = spec?.label ?? row.field;
|
|
230
|
-
const opLabel =
|
|
231
|
-
const valueText = opArity(row.op) === "none" ? null : describeValue(row.value,
|
|
739
|
+
const opLabel = chromeT(`filters.operators.${row.op}`, void 0, row.op);
|
|
740
|
+
const valueText = opArity(row.op) === "none" ? null : describeValue(row.value, chromeT, spec, formatValue);
|
|
232
741
|
return valueText === null ? `${label} ${opLabel}` : `${label} ${opLabel} ${valueText}`;
|
|
233
742
|
}
|
|
234
|
-
function describeRow(row, available,
|
|
235
|
-
return describeAt(row, available,
|
|
743
|
+
function describeRow(row, available, chromeT, formatValue) {
|
|
744
|
+
return describeAt(row, available, chromeT, 1, formatValue);
|
|
236
745
|
}
|
|
237
746
|
function relationLabelKey(to, by, value) {
|
|
238
747
|
return `${by === void 0 ? to : `${to}#${by}`}::${value}`;
|
|
@@ -365,15 +874,18 @@ function badgeKey(row, index) {
|
|
|
365
874
|
if (isFilterGroup(row)) return `${index}:group:${row.join}`;
|
|
366
875
|
return `${index}:${row.field}:${row.op}`;
|
|
367
876
|
}
|
|
877
|
+
const BADGE_ROW = "inline-flex max-w-full items-center gap-1 align-middle";
|
|
878
|
+
const REMOVE_BUTTON = "inline-flex size-4 shrink-0 items-center justify-center rounded-full text-muted-foreground hover:bg-foreground/10 hover:text-foreground focus:outline-hidden focus-visible:ring-2 focus-visible:ring-ring";
|
|
368
879
|
function FilterBadges(props) {
|
|
369
880
|
const { group, setGroup, available, fixed, testIdBase, onOpenAt, onRemoved, pinnedFields = [], pinnedValue = [], setPinnedValue } = props;
|
|
370
|
-
const
|
|
371
|
-
const
|
|
881
|
+
const chromeT = useChromeT();
|
|
882
|
+
const locale = useOptionalLocale();
|
|
883
|
+
const tt = (key, params, fallback) => chromeT(key, {
|
|
372
884
|
params,
|
|
373
885
|
...typeof params?.count === "number" ? { count: params.count } : {},
|
|
374
886
|
defaultValue: fallback
|
|
375
887
|
});
|
|
376
|
-
const removeLabel =
|
|
888
|
+
const removeLabel = chromeT("filters.removeRow", { defaultValue: "Remove filter" });
|
|
377
889
|
const { enumLabels, relationLabels, onEnumResolved, onRelationResolved } = useFormatValueMaps();
|
|
378
890
|
const formatValue = (kind, value) => {
|
|
379
891
|
if (kind.kind === "enum") return enumLabels[kind.enumName]?.[String(value)];
|
|
@@ -381,7 +893,7 @@ function FilterBadges(props) {
|
|
|
381
893
|
if (kind.kind === "date") {
|
|
382
894
|
const d = new Date(String(value));
|
|
383
895
|
if (Number.isNaN(d.getTime())) return void 0;
|
|
384
|
-
return kind.time ? formatDateTime(d) : formatDate(d);
|
|
896
|
+
return kind.time ? formatDateTime(d, locale) : formatDate(d, locale);
|
|
385
897
|
}
|
|
386
898
|
};
|
|
387
899
|
const orphanPinned = useMemo(() => {
|
|
@@ -415,49 +927,56 @@ function FilterBadges(props) {
|
|
|
415
927
|
onEnumResolved,
|
|
416
928
|
onRelationResolved
|
|
417
929
|
}),
|
|
418
|
-
group.rows.map((row, index) =>
|
|
930
|
+
group.rows.map((row, index) => jsx(Badge, {
|
|
419
931
|
variant: "secondary",
|
|
420
|
-
className: "
|
|
421
|
-
children:
|
|
422
|
-
|
|
423
|
-
"
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
"
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
className:
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
932
|
+
className: "py-1 pr-1",
|
|
933
|
+
children: jsxs("span", {
|
|
934
|
+
className: BADGE_ROW,
|
|
935
|
+
children: [jsx("button", {
|
|
936
|
+
type: "button",
|
|
937
|
+
"data-testid": `${testIdBase}:badge:${index}`,
|
|
938
|
+
onClick: () => onOpenAt([index]),
|
|
939
|
+
className: "min-w-0 cursor-pointer truncate bg-transparent p-0 text-left",
|
|
940
|
+
children: describeRow(row, available, tt, formatValue)
|
|
941
|
+
}), jsx("button", {
|
|
942
|
+
type: "button",
|
|
943
|
+
"data-testid": `${testIdBase}:badge:${index}:remove`,
|
|
944
|
+
"aria-label": removeLabel,
|
|
945
|
+
onClick: () => handleRemove(index),
|
|
946
|
+
className: REMOVE_BUTTON,
|
|
947
|
+
children: jsx(IconX, {
|
|
948
|
+
className: "size-3",
|
|
949
|
+
"aria-hidden": "true"
|
|
950
|
+
})
|
|
951
|
+
})]
|
|
952
|
+
})
|
|
438
953
|
}, badgeKey(row, index))),
|
|
439
954
|
labelledFixed.map((entry, index) => jsx(Badge, {
|
|
440
955
|
variant: "outline",
|
|
441
956
|
"data-testid": `${testIdBase}:badge:fixed:${index}`,
|
|
442
957
|
children: entry.label
|
|
443
958
|
}, `fixed-${index}`)),
|
|
444
|
-
orphanPinned.map(({ expr, index }) =>
|
|
959
|
+
orphanPinned.map(({ expr, index }) => jsx(Badge, {
|
|
445
960
|
variant: "secondary",
|
|
446
|
-
className: "
|
|
447
|
-
children:
|
|
448
|
-
|
|
449
|
-
children:
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
"
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
961
|
+
className: "py-1 pr-1",
|
|
962
|
+
children: jsxs("span", {
|
|
963
|
+
className: BADGE_ROW,
|
|
964
|
+
children: [jsx("span", {
|
|
965
|
+
"data-testid": `${testIdBase}:badge:pinned:${index}`,
|
|
966
|
+
className: "min-w-0 truncate",
|
|
967
|
+
children: describeRow(expr, available, tt, formatValue)
|
|
968
|
+
}), jsx("button", {
|
|
969
|
+
type: "button",
|
|
970
|
+
"data-testid": `${testIdBase}:badge:pinned:${index}:remove`,
|
|
971
|
+
"aria-label": removeLabel,
|
|
972
|
+
onClick: () => handleRemovePinned(index),
|
|
973
|
+
className: REMOVE_BUTTON,
|
|
974
|
+
children: jsx(IconX, {
|
|
975
|
+
className: "size-3",
|
|
976
|
+
"aria-hidden": "true"
|
|
977
|
+
})
|
|
978
|
+
})]
|
|
979
|
+
})
|
|
461
980
|
}, `pinned-${index}`))
|
|
462
981
|
]
|
|
463
982
|
});
|
|
@@ -647,14 +1166,15 @@ const OPTIONS = ["true", "false"];
|
|
|
647
1166
|
function BooleanValueControl(props) {
|
|
648
1167
|
const { value, onChange, testId, arity, autoFocus, fieldLabel, fill } = props;
|
|
649
1168
|
const t = useOptionalT();
|
|
650
|
-
const
|
|
1169
|
+
const chromeT = useChromeT();
|
|
1170
|
+
const ariaLabel = fieldLabel ? chromeT("filters.valueFor", {
|
|
651
1171
|
defaultValue: `Value for ${fieldLabel}`,
|
|
652
1172
|
params: { field: fieldLabel }
|
|
653
|
-
}) :
|
|
1173
|
+
}) : chromeT("filters.value", { defaultValue: "Value" });
|
|
654
1174
|
const yesLabel = t("boolean.yes", { defaultValue: "Yes" });
|
|
655
1175
|
const noLabel = t("boolean.no", { defaultValue: "No" });
|
|
656
1176
|
const labelOf = (token) => token === "true" ? yesLabel : noLabel;
|
|
657
|
-
const clearLabel =
|
|
1177
|
+
const clearLabel = chromeT("filters.clear", { defaultValue: "Clear" });
|
|
658
1178
|
const { open, setOpen, selected, select, triggerLabel, clear } = useSelectableValueControl({
|
|
659
1179
|
value,
|
|
660
1180
|
arity,
|
|
@@ -674,7 +1194,7 @@ function BooleanValueControl(props) {
|
|
|
674
1194
|
"data-testid": testId,
|
|
675
1195
|
"aria-label": ariaLabel,
|
|
676
1196
|
className: cn("h-8 justify-between", fill && "w-full"),
|
|
677
|
-
children: triggerLabel(labelOf, ariaLabel)
|
|
1197
|
+
children: triggerLabel(labelOf, props.placeholder ?? ariaLabel)
|
|
678
1198
|
})
|
|
679
1199
|
}), jsx(PopoverContent, {
|
|
680
1200
|
className: "w-[140px] p-0",
|
|
@@ -707,12 +1227,13 @@ function BooleanValueControl(props) {
|
|
|
707
1227
|
function SelectListValueControl(props) {
|
|
708
1228
|
const { options, value, onChange, testId, arity, autoFocus, fieldLabel, isLoading = false } = props;
|
|
709
1229
|
const t = useOptionalT();
|
|
710
|
-
const
|
|
1230
|
+
const chromeT = useChromeT();
|
|
1231
|
+
const ariaLabel = fieldLabel ? chromeT("filters.valueFor", {
|
|
711
1232
|
defaultValue: `Value for ${fieldLabel}`,
|
|
712
1233
|
params: { field: fieldLabel }
|
|
713
|
-
}) :
|
|
1234
|
+
}) : chromeT("filters.value", { defaultValue: "Value" });
|
|
714
1235
|
const loadingLabel = t("table.loading", { defaultValue: "Loading…" });
|
|
715
|
-
const clearLabel =
|
|
1236
|
+
const clearLabel = chromeT("filters.clear", { defaultValue: "Clear" });
|
|
716
1237
|
const labelFor = props.labelFor ?? ((raw) => options.find((o) => o.value === raw)?.label ?? raw);
|
|
717
1238
|
const { open, setOpen, selected, select, triggerLabel, clear } = useSelectableValueControl({
|
|
718
1239
|
value,
|
|
@@ -734,7 +1255,7 @@ function SelectListValueControl(props) {
|
|
|
734
1255
|
className: cn("h-8 justify-between truncate", props.fill ? "w-full" : "max-w-[200px]"),
|
|
735
1256
|
children: jsx("span", {
|
|
736
1257
|
className: "truncate",
|
|
737
|
-
children: isLoading ? loadingLabel : triggerLabel(labelFor, ariaLabel)
|
|
1258
|
+
children: isLoading ? loadingLabel : triggerLabel(labelFor, props.placeholder ?? ariaLabel)
|
|
738
1259
|
})
|
|
739
1260
|
})
|
|
740
1261
|
}), jsx(PopoverContent, {
|
|
@@ -797,6 +1318,7 @@ function EnumValueControl(props) {
|
|
|
797
1318
|
arity,
|
|
798
1319
|
autoFocus,
|
|
799
1320
|
fieldLabel,
|
|
1321
|
+
placeholder: props.placeholder,
|
|
800
1322
|
options,
|
|
801
1323
|
labelFor,
|
|
802
1324
|
isLoading,
|
|
@@ -843,6 +1365,7 @@ function RelationValueControl(props) {
|
|
|
843
1365
|
const { to, by, value, onChange, testId, arity, autoFocus, fieldLabel } = props;
|
|
844
1366
|
const app = useApp();
|
|
845
1367
|
const t = useOptionalT();
|
|
1368
|
+
const chromeT = useChromeT();
|
|
846
1369
|
const targetDef = app.registry.get(to);
|
|
847
1370
|
const displayField = targetDef?.displayField ?? "name";
|
|
848
1371
|
const [search, setSearch] = useState("");
|
|
@@ -881,11 +1404,11 @@ function RelationValueControl(props) {
|
|
|
881
1404
|
if (fromRows) return rowTitle(fromRows);
|
|
882
1405
|
return fallbackLabels[id] ?? id;
|
|
883
1406
|
};
|
|
884
|
-
const ariaLabel = fieldLabel ?
|
|
1407
|
+
const ariaLabel = fieldLabel ? chromeT("filters.valueFor", {
|
|
885
1408
|
defaultValue: `Value for ${fieldLabel}`,
|
|
886
1409
|
params: { field: fieldLabel }
|
|
887
|
-
}) :
|
|
888
|
-
const clearLabel =
|
|
1410
|
+
}) : chromeT("filters.value", { defaultValue: "Value" });
|
|
1411
|
+
const clearLabel = chromeT("filters.clear", { defaultValue: "Clear" });
|
|
889
1412
|
return jsxs(Popover, {
|
|
890
1413
|
open,
|
|
891
1414
|
onOpenChange: setOpen,
|
|
@@ -901,7 +1424,7 @@ function RelationValueControl(props) {
|
|
|
901
1424
|
className: cn("h-8 justify-between truncate", props.fill ? "w-full" : "max-w-[220px]"),
|
|
902
1425
|
children: jsx("span", {
|
|
903
1426
|
className: "truncate",
|
|
904
|
-
children: triggerLabel(labelFor, ariaLabel)
|
|
1427
|
+
children: triggerLabel(labelFor, props.placeholder ?? ariaLabel)
|
|
905
1428
|
})
|
|
906
1429
|
})
|
|
907
1430
|
}), jsx(PopoverContent, {
|
|
@@ -957,6 +1480,20 @@ function RelationValueControl(props) {
|
|
|
957
1480
|
});
|
|
958
1481
|
}
|
|
959
1482
|
//#endregion
|
|
1483
|
+
//#region src/fields/local-date-time.ts
|
|
1484
|
+
function toLocalDateTimeText(value) {
|
|
1485
|
+
if (value === void 0 || value === null || value === "") return "";
|
|
1486
|
+
const d = value instanceof Date ? value : new Date(String(value));
|
|
1487
|
+
if (Number.isNaN(d.getTime())) return "";
|
|
1488
|
+
const pad = (n) => String(n).padStart(2, "0");
|
|
1489
|
+
return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}T${pad(d.getHours())}:${pad(d.getMinutes())}`;
|
|
1490
|
+
}
|
|
1491
|
+
function fromLocalDateTimeText(text) {
|
|
1492
|
+
if (text === "") return void 0;
|
|
1493
|
+
const d = new Date(text);
|
|
1494
|
+
return Number.isNaN(d.getTime()) ? void 0 : d.toISOString();
|
|
1495
|
+
}
|
|
1496
|
+
//#endregion
|
|
960
1497
|
//#region src/list-kit/filter-builder/values/scalar-value-input.tsx
|
|
961
1498
|
function isDateTime(kind) {
|
|
962
1499
|
return kind.kind === "date" && kind.time === true;
|
|
@@ -969,12 +1506,9 @@ function inputTypeFor(kind) {
|
|
|
969
1506
|
}
|
|
970
1507
|
}
|
|
971
1508
|
function toText(kind, value) {
|
|
1509
|
+
if (isDateTime(kind)) return toLocalDateTimeText(value);
|
|
972
1510
|
if (value === void 0 || value === null || value === "") return "";
|
|
973
|
-
|
|
974
|
-
const d = new Date(String(value));
|
|
975
|
-
if (Number.isNaN(d.getTime())) return "";
|
|
976
|
-
const pad = (n) => String(n).padStart(2, "0");
|
|
977
|
-
return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}T${pad(d.getHours())}:${pad(d.getMinutes())}`;
|
|
1511
|
+
return String(value);
|
|
978
1512
|
}
|
|
979
1513
|
function parseOne(kind, raw) {
|
|
980
1514
|
if (raw === "") return void 0;
|
|
@@ -982,19 +1516,16 @@ function parseOne(kind, raw) {
|
|
|
982
1516
|
const n = Number(raw);
|
|
983
1517
|
return Number.isFinite(n) ? n : void 0;
|
|
984
1518
|
}
|
|
985
|
-
if (isDateTime(kind))
|
|
986
|
-
const d = new Date(raw);
|
|
987
|
-
return Number.isNaN(d.getTime()) ? void 0 : d.toISOString();
|
|
988
|
-
}
|
|
1519
|
+
if (isDateTime(kind)) return fromLocalDateTimeText(raw);
|
|
989
1520
|
return raw;
|
|
990
1521
|
}
|
|
991
1522
|
function ScalarValueInput(props) {
|
|
992
|
-
const
|
|
1523
|
+
const chromeT = useChromeT();
|
|
993
1524
|
const inputType = inputTypeFor(props.kind);
|
|
994
|
-
const ariaLabel = props.fieldLabel ?
|
|
1525
|
+
const ariaLabel = props.fieldLabel ? chromeT("filters.valueFor", {
|
|
995
1526
|
defaultValue: `Value for ${props.fieldLabel}`,
|
|
996
1527
|
params: { field: props.fieldLabel }
|
|
997
|
-
}) :
|
|
1528
|
+
}) : chromeT("filters.value", { defaultValue: "Value" });
|
|
998
1529
|
if (props.arity === "multi") return jsx(ScalarMultiInput, {
|
|
999
1530
|
...props,
|
|
1000
1531
|
inputType,
|
|
@@ -1124,6 +1655,7 @@ function FilterValueControl(props) {
|
|
|
1124
1655
|
arity,
|
|
1125
1656
|
autoFocus: props.autoFocus,
|
|
1126
1657
|
fieldLabel: props.fieldLabel,
|
|
1658
|
+
placeholder: props.placeholder,
|
|
1127
1659
|
options: props.options,
|
|
1128
1660
|
fill: props.fill
|
|
1129
1661
|
});
|
|
@@ -1140,6 +1672,7 @@ function FilterValueControl(props) {
|
|
|
1140
1672
|
arity,
|
|
1141
1673
|
autoFocus: props.autoFocus,
|
|
1142
1674
|
fieldLabel: props.fieldLabel,
|
|
1675
|
+
placeholder: props.placeholder,
|
|
1143
1676
|
options: props.kind.values.map((entry) => ({
|
|
1144
1677
|
value: entry,
|
|
1145
1678
|
label: entry
|
|
@@ -1168,10 +1701,10 @@ const FIELD_CLASS = "h-8 min-w-0 shrink basis-40";
|
|
|
1168
1701
|
const OPERATOR_CLASS = "h-8 min-w-0 shrink basis-[9.5rem]";
|
|
1169
1702
|
function FilterRow(props) {
|
|
1170
1703
|
const { row, path, available, onPatch, onRemove, testIdBase, debounceMs } = props;
|
|
1171
|
-
const
|
|
1704
|
+
const chromeT = useChromeT();
|
|
1172
1705
|
const base = `${testIdBase}:row:${path.join("-")}`;
|
|
1173
1706
|
const spec = available.find((s) => s.field === row.field);
|
|
1174
|
-
const removeLabel =
|
|
1707
|
+
const removeLabel = chromeT("filters.removeRow", { defaultValue: "Remove filter" });
|
|
1175
1708
|
if (!spec) return jsxs("div", {
|
|
1176
1709
|
className: ROW_CLASS,
|
|
1177
1710
|
children: [
|
|
@@ -1211,8 +1744,8 @@ function FilterRow(props) {
|
|
|
1211
1744
|
};
|
|
1212
1745
|
const handleOpChange = (op) => onPatch(path, { op });
|
|
1213
1746
|
const handleValueChange = (value) => onPatch(path, { value });
|
|
1214
|
-
const fieldAriaLabel =
|
|
1215
|
-
const operatorAriaLabel =
|
|
1747
|
+
const fieldAriaLabel = chromeT("filters.field", { defaultValue: "Field" });
|
|
1748
|
+
const operatorAriaLabel = chromeT("filters.operator", { defaultValue: "Operator" });
|
|
1216
1749
|
return jsxs("div", {
|
|
1217
1750
|
className: ROW_CLASS,
|
|
1218
1751
|
children: [
|
|
@@ -1241,7 +1774,7 @@ function FilterRow(props) {
|
|
|
1241
1774
|
}), jsx(SelectContent, { children: spec.operators.map((op) => jsx(SelectItem, {
|
|
1242
1775
|
value: op,
|
|
1243
1776
|
"data-testid": `${opTestId}:option:${op}`,
|
|
1244
|
-
children:
|
|
1777
|
+
children: chromeT(`filters.operators.${op}`, { defaultValue: op })
|
|
1245
1778
|
}, op)) })]
|
|
1246
1779
|
}),
|
|
1247
1780
|
jsx("div", {
|
|
@@ -1289,10 +1822,10 @@ function FilterGroupEditor(props) {
|
|
|
1289
1822
|
}
|
|
1290
1823
|
function GroupEditorInner(props) {
|
|
1291
1824
|
const { group, path, available, onChange, testIdBase, onRemove, addGroupAt, debounceMs } = props;
|
|
1292
|
-
const
|
|
1825
|
+
const chromeT = useChromeT();
|
|
1293
1826
|
const base = `${testIdBase}:group:${path.join("-")}`;
|
|
1294
1827
|
const canNestGroup = depthOf(path) < 3;
|
|
1295
|
-
const joinLabel = (join) =>
|
|
1828
|
+
const joinLabel = (join) => chromeT(`filters.${join}`, { defaultValue: join });
|
|
1296
1829
|
const handlePatch = (rowPath, patch) => {
|
|
1297
1830
|
const index = rowPath[rowPath.length - 1];
|
|
1298
1831
|
if (index === void 0) return;
|
|
@@ -1325,11 +1858,11 @@ function GroupEditorInner(props) {
|
|
|
1325
1858
|
return jsxs(Fragment, { children: [jsxs("div", {
|
|
1326
1859
|
className: CONNECTOR_CLASS,
|
|
1327
1860
|
children: [
|
|
1328
|
-
index === 0 &&
|
|
1861
|
+
index === 0 && chromeT("filters.where", { defaultValue: "Where" }),
|
|
1329
1862
|
index === 1 && jsx("button", {
|
|
1330
1863
|
type: "button",
|
|
1331
1864
|
"data-testid": `${base}:join`,
|
|
1332
|
-
"aria-label":
|
|
1865
|
+
"aria-label": chromeT("filters.toggleJoin", {
|
|
1333
1866
|
defaultValue: `Join: ${joinLabel(group.join)} — activate to switch to ${joinLabel(otherJoin)}`,
|
|
1334
1867
|
params: {
|
|
1335
1868
|
current: joinLabel(group.join),
|
|
@@ -1381,7 +1914,7 @@ function GroupEditorInner(props) {
|
|
|
1381
1914
|
children: [jsx(IconPlus, {
|
|
1382
1915
|
className: "h-4 w-4",
|
|
1383
1916
|
"aria-hidden": "true"
|
|
1384
|
-
}),
|
|
1917
|
+
}), chromeT("filters.addRow", { defaultValue: "Add filter" })]
|
|
1385
1918
|
}),
|
|
1386
1919
|
canNestGroup && jsxs(Button, {
|
|
1387
1920
|
type: "button",
|
|
@@ -1392,7 +1925,7 @@ function GroupEditorInner(props) {
|
|
|
1392
1925
|
children: [jsx(IconFolderPlus, {
|
|
1393
1926
|
className: "h-4 w-4",
|
|
1394
1927
|
"aria-hidden": "true"
|
|
1395
|
-
}),
|
|
1928
|
+
}), chromeT("filters.addGroup", { defaultValue: "Add group" })]
|
|
1396
1929
|
}),
|
|
1397
1930
|
onRemove && jsxs(Button, {
|
|
1398
1931
|
type: "button",
|
|
@@ -1403,7 +1936,7 @@ function GroupEditorInner(props) {
|
|
|
1403
1936
|
children: [jsx(IconX, {
|
|
1404
1937
|
className: "h-4 w-4",
|
|
1405
1938
|
"aria-hidden": "true"
|
|
1406
|
-
}),
|
|
1939
|
+
}), chromeT("filters.removeGroup", { defaultValue: "Remove group" })]
|
|
1407
1940
|
})
|
|
1408
1941
|
]
|
|
1409
1942
|
})
|
|
@@ -1419,7 +1952,7 @@ function countGroupRows(group) {
|
|
|
1419
1952
|
}
|
|
1420
1953
|
function FilterPanel(props) {
|
|
1421
1954
|
const { group, setGroup, available, setPage, testIdBase, open, onOpenChange, focusPath, externalRemoval } = props;
|
|
1422
|
-
const
|
|
1955
|
+
const chromeT = useChromeT();
|
|
1423
1956
|
const [draft, setDraft] = useState(() => toDraft(group));
|
|
1424
1957
|
const wasOpenRef = useRef(open);
|
|
1425
1958
|
const topLevelIdsRef = useRef([]);
|
|
@@ -1471,7 +2004,7 @@ function FilterPanel(props) {
|
|
|
1471
2004
|
className: "h-4 w-4",
|
|
1472
2005
|
"aria-hidden": "true"
|
|
1473
2006
|
}),
|
|
1474
|
-
|
|
2007
|
+
chromeT("filters.title", { defaultValue: "Filters" }),
|
|
1475
2008
|
appliedCount > 0 && jsx("span", {
|
|
1476
2009
|
"data-testid": `${testIdBase}:count`,
|
|
1477
2010
|
className: "ml-1 rounded-full bg-primary px-1.5 text-xs text-primary-foreground",
|
|
@@ -1499,13 +2032,13 @@ function FilterPanel(props) {
|
|
|
1499
2032
|
size: "sm",
|
|
1500
2033
|
"data-testid": `${testIdBase}:reset`,
|
|
1501
2034
|
onClick: handleReset,
|
|
1502
|
-
children:
|
|
2035
|
+
children: chromeT("filters.reset", { defaultValue: "Reset" })
|
|
1503
2036
|
}), jsx(Button, {
|
|
1504
2037
|
type: "button",
|
|
1505
2038
|
size: "sm",
|
|
1506
2039
|
"data-testid": `${testIdBase}:apply`,
|
|
1507
2040
|
onClick: handleApply,
|
|
1508
|
-
children:
|
|
2041
|
+
children: chromeT("filters.apply", { defaultValue: "Apply" })
|
|
1509
2042
|
})]
|
|
1510
2043
|
})]
|
|
1511
2044
|
})]
|
|
@@ -1560,11 +2093,204 @@ function PinnedControl(props) {
|
|
|
1560
2093
|
});
|
|
1561
2094
|
}
|
|
1562
2095
|
//#endregion
|
|
2096
|
+
//#region src/data-table/data-table-faceted-filter.tsx
|
|
2097
|
+
function DataTableFacetedFilter({ column, getTestId, ...rest }) {
|
|
2098
|
+
const filterValue = column.getFilterValue();
|
|
2099
|
+
return jsx(FacetedFilterPicker, {
|
|
2100
|
+
...rest,
|
|
2101
|
+
...getTestId === void 0 ? {} : { getTestId: (role, value) => role === "hint" ? void 0 : getTestId(role, value) },
|
|
2102
|
+
selected: Array.isArray(filterValue) ? filterValue.map(String) : [],
|
|
2103
|
+
onChange: (next) => column.setFilterValue(next.length === 0 ? void 0 : next)
|
|
2104
|
+
});
|
|
2105
|
+
}
|
|
2106
|
+
function FacetedFilterPicker({ title, options = [], loadOptions, selectedLabelFor, searchPlaceholder, emptyText, getTestId, className, selected: selectedList, onChange, hint, emptyLabel, clearable = true }) {
|
|
2107
|
+
const t = useOptionalT();
|
|
2108
|
+
const chromeT = useChromeT();
|
|
2109
|
+
const selected = useMemo(() => new Set(selectedList), [selectedList]);
|
|
2110
|
+
const [search, setSearch] = useState("");
|
|
2111
|
+
const [asyncOptions, setAsyncOptions] = useState([]);
|
|
2112
|
+
const [loading, setLoading] = useState(false);
|
|
2113
|
+
const debounceRef = useRef(null);
|
|
2114
|
+
const loadOptionsRef = useRef(loadOptions);
|
|
2115
|
+
useEffect(() => {
|
|
2116
|
+
loadOptionsRef.current = loadOptions;
|
|
2117
|
+
});
|
|
2118
|
+
useEffect(() => {
|
|
2119
|
+
if (!loadOptionsRef.current) return;
|
|
2120
|
+
setLoading(true);
|
|
2121
|
+
if (debounceRef.current !== null) clearTimeout(debounceRef.current);
|
|
2122
|
+
let cancelled = false;
|
|
2123
|
+
debounceRef.current = setTimeout(() => {
|
|
2124
|
+
const loader = loadOptionsRef.current;
|
|
2125
|
+
if (!loader) return;
|
|
2126
|
+
loader(search).then((results) => {
|
|
2127
|
+
if (!cancelled) {
|
|
2128
|
+
setAsyncOptions(results);
|
|
2129
|
+
setLoading(false);
|
|
2130
|
+
}
|
|
2131
|
+
}).catch(() => {
|
|
2132
|
+
if (!cancelled) setLoading(false);
|
|
2133
|
+
});
|
|
2134
|
+
}, 250);
|
|
2135
|
+
return () => {
|
|
2136
|
+
cancelled = true;
|
|
2137
|
+
if (debounceRef.current !== null) clearTimeout(debounceRef.current);
|
|
2138
|
+
};
|
|
2139
|
+
}, [search]);
|
|
2140
|
+
const effectiveOptions = loadOptions ? asyncOptions : options;
|
|
2141
|
+
const labelFor = (value) => {
|
|
2142
|
+
const opt = effectiveOptions.find((o) => o.value === value);
|
|
2143
|
+
if (opt) return opt.label;
|
|
2144
|
+
return selectedLabelFor?.(value) ?? value;
|
|
2145
|
+
};
|
|
2146
|
+
const toggle = (value) => {
|
|
2147
|
+
const next = new Set(selected);
|
|
2148
|
+
if (next.has(value)) next.delete(value);
|
|
2149
|
+
else next.add(value);
|
|
2150
|
+
onChange(Array.from(next));
|
|
2151
|
+
};
|
|
2152
|
+
const selectedValues = Array.from(selected);
|
|
2153
|
+
return jsxs(Popover, { children: [jsx(PopoverTrigger, {
|
|
2154
|
+
asChild: true,
|
|
2155
|
+
children: jsxs(Button, {
|
|
2156
|
+
type: "button",
|
|
2157
|
+
variant: "outline",
|
|
2158
|
+
size: "sm",
|
|
2159
|
+
"data-testid": getTestId?.("trigger"),
|
|
2160
|
+
title: hint,
|
|
2161
|
+
className: cn("h-8 border-dashed", className),
|
|
2162
|
+
children: [
|
|
2163
|
+
jsx(IconCirclePlus, {
|
|
2164
|
+
className: "mr-2 h-4 w-4",
|
|
2165
|
+
"aria-hidden": "true"
|
|
2166
|
+
}),
|
|
2167
|
+
title,
|
|
2168
|
+
selected.size === 0 && emptyLabel !== void 0 && jsxs(Fragment$1, { children: [jsx("span", {
|
|
2169
|
+
className: "mx-2 h-4 w-px bg-border",
|
|
2170
|
+
"aria-hidden": "true"
|
|
2171
|
+
}), jsx(Badge, {
|
|
2172
|
+
variant: "secondary",
|
|
2173
|
+
className: "rounded-sm px-1 font-normal",
|
|
2174
|
+
children: emptyLabel
|
|
2175
|
+
})] }),
|
|
2176
|
+
selected.size > 0 && jsxs(Fragment$1, { children: [
|
|
2177
|
+
jsx("span", {
|
|
2178
|
+
className: "mx-2 h-4 w-px bg-border",
|
|
2179
|
+
"aria-hidden": "true"
|
|
2180
|
+
}),
|
|
2181
|
+
jsx(Badge, {
|
|
2182
|
+
variant: "secondary",
|
|
2183
|
+
className: "rounded-sm px-1 font-normal lg:hidden",
|
|
2184
|
+
children: selected.size
|
|
2185
|
+
}),
|
|
2186
|
+
jsx("div", {
|
|
2187
|
+
className: "hidden gap-1 lg:flex",
|
|
2188
|
+
children: selected.size > 2 ? jsx(Badge, {
|
|
2189
|
+
variant: "secondary",
|
|
2190
|
+
className: "rounded-sm px-1 font-normal",
|
|
2191
|
+
children: t("table.selectedCount", {
|
|
2192
|
+
count: selected.size,
|
|
2193
|
+
defaultValue: "{{count}} selected"
|
|
2194
|
+
})
|
|
2195
|
+
}) : selectedValues.map((value) => jsx(Badge, {
|
|
2196
|
+
variant: "secondary",
|
|
2197
|
+
className: "rounded-sm px-1 font-normal",
|
|
2198
|
+
children: labelFor(value)
|
|
2199
|
+
}, value))
|
|
2200
|
+
})
|
|
2201
|
+
] })
|
|
2202
|
+
]
|
|
2203
|
+
})
|
|
2204
|
+
}), jsx(PopoverContent, {
|
|
2205
|
+
className: "w-[220px] p-0",
|
|
2206
|
+
align: "start",
|
|
2207
|
+
children: jsxs(Command, {
|
|
2208
|
+
shouldFilter: !loadOptions,
|
|
2209
|
+
children: [
|
|
2210
|
+
hint !== void 0 && jsx("p", {
|
|
2211
|
+
"data-testid": getTestId?.("hint"),
|
|
2212
|
+
className: "border-b px-3 py-2 text-xs text-muted-foreground",
|
|
2213
|
+
children: hint
|
|
2214
|
+
}),
|
|
2215
|
+
jsx(CommandInput, {
|
|
2216
|
+
placeholder: searchPlaceholder ?? title,
|
|
2217
|
+
"data-testid": getTestId?.("search"),
|
|
2218
|
+
...loadOptions ? {
|
|
2219
|
+
value: search,
|
|
2220
|
+
onValueChange: setSearch
|
|
2221
|
+
} : {}
|
|
2222
|
+
}),
|
|
2223
|
+
jsxs(CommandList, { children: [
|
|
2224
|
+
loading && jsx("div", {
|
|
2225
|
+
className: "py-6 text-center text-sm text-muted-foreground",
|
|
2226
|
+
children: t("list.loadingShort", { defaultValue: "Loading…" })
|
|
2227
|
+
}),
|
|
2228
|
+
jsx(CommandEmpty, { children: emptyText ?? t("relation.noResults", { defaultValue: "No results." }) }),
|
|
2229
|
+
jsx(CommandGroup, { children: effectiveOptions.map((opt) => {
|
|
2230
|
+
const isSelected = selected.has(opt.value);
|
|
2231
|
+
const Icon = opt.icon;
|
|
2232
|
+
return jsxs(CommandItem, {
|
|
2233
|
+
value: opt.label,
|
|
2234
|
+
"data-testid": getTestId?.("option", opt.value),
|
|
2235
|
+
role: "option",
|
|
2236
|
+
"aria-selected": isSelected,
|
|
2237
|
+
"aria-checked": isSelected,
|
|
2238
|
+
onSelect: () => toggle(opt.value),
|
|
2239
|
+
children: [
|
|
2240
|
+
jsx("span", {
|
|
2241
|
+
className: cn("mr-2 flex h-4 w-4 shrink-0 items-center justify-center rounded-sm border", isSelected ? "border-primary bg-primary text-primary-foreground" : "border-input opacity-50"),
|
|
2242
|
+
"aria-hidden": "true",
|
|
2243
|
+
children: isSelected && jsx(IconCheck, { className: "h-3.5 w-3.5" })
|
|
2244
|
+
}),
|
|
2245
|
+
Icon && jsx(Icon, {
|
|
2246
|
+
className: "mr-2 h-4 w-4 text-muted-foreground",
|
|
2247
|
+
"aria-hidden": "true"
|
|
2248
|
+
}),
|
|
2249
|
+
jsxs("span", {
|
|
2250
|
+
className: "flex min-w-0 flex-1 flex-col text-left",
|
|
2251
|
+
children: [jsx("span", { children: opt.label }), opt.summary !== void 0 && jsx("span", {
|
|
2252
|
+
className: "text-xs text-muted-foreground",
|
|
2253
|
+
children: opt.summary
|
|
2254
|
+
})]
|
|
2255
|
+
})
|
|
2256
|
+
]
|
|
2257
|
+
}, opt.value);
|
|
2258
|
+
}) })
|
|
2259
|
+
] }),
|
|
2260
|
+
clearable && selected.size > 0 && jsx("button", {
|
|
2261
|
+
type: "button",
|
|
2262
|
+
"data-testid": getTestId?.("clear"),
|
|
2263
|
+
onClick: () => onChange([]),
|
|
2264
|
+
className: "flex w-full items-center justify-center border-t px-2 py-1.5 text-sm hover:bg-accent hover:text-accent-foreground",
|
|
2265
|
+
children: chromeT("filters.clearAll", { defaultValue: "Clear filters" })
|
|
2266
|
+
})
|
|
2267
|
+
]
|
|
2268
|
+
})
|
|
2269
|
+
})] });
|
|
2270
|
+
}
|
|
2271
|
+
//#endregion
|
|
2272
|
+
//#region src/list-kit/filter-builder/preset-filter-controls.tsx
|
|
2273
|
+
function PresetFilterControls(props) {
|
|
2274
|
+
const { presets, setPreset, testIdBase } = props;
|
|
2275
|
+
return jsx(Fragment$1, { children: presets.map((filter) => jsx(FacetedFilterPicker, {
|
|
2276
|
+
title: filter.label,
|
|
2277
|
+
...filter.hint === void 0 ? {} : { hint: filter.hint },
|
|
2278
|
+
options: filter.options.map((option) => ({
|
|
2279
|
+
value: option.id,
|
|
2280
|
+
label: option.label,
|
|
2281
|
+
...option.summary === void 0 ? {} : { summary: option.summary }
|
|
2282
|
+
})),
|
|
2283
|
+
selected: filter.selected,
|
|
2284
|
+
onChange: (next) => setPreset(filter.id, filter.options.map((option) => option.id).filter((id) => next.includes(id))),
|
|
2285
|
+
getTestId: (role, value) => `${testIdBase}:preset:${filter.id}:${role}${value === void 0 ? "" : `:${value}`}`
|
|
2286
|
+
}, filter.id)) });
|
|
2287
|
+
}
|
|
2288
|
+
//#endregion
|
|
1563
2289
|
//#region src/list-kit/filter-builder/filter-builder.tsx
|
|
1564
2290
|
const EMPTY_PINNED_FIELDS = Object.freeze([]);
|
|
1565
2291
|
const EMPTY_PINNED_VALUE = Object.freeze([]);
|
|
1566
2292
|
function FilterBuilder(props) {
|
|
1567
|
-
const { group, setGroup, available, fixed, pinnedFields = EMPTY_PINNED_FIELDS, pinnedValue = EMPTY_PINNED_VALUE, setPinnedValue, setPage, testIdBase } = props;
|
|
2293
|
+
const { group, setGroup, available, fixed, pinnedFields = EMPTY_PINNED_FIELDS, pinnedValue = EMPTY_PINNED_VALUE, setPinnedValue, presets, setPreset, setPage, testIdBase } = props;
|
|
1568
2294
|
const [open, setOpen] = useState(false);
|
|
1569
2295
|
const [focusPath, setFocusPath] = useState(void 0);
|
|
1570
2296
|
const [externalRemoval, setExternalRemoval] = useState(void 0);
|
|
@@ -1581,6 +2307,11 @@ function FilterBuilder(props) {
|
|
|
1581
2307
|
setPinnedValue?.(next);
|
|
1582
2308
|
};
|
|
1583
2309
|
return jsxs(Fragment$1, { children: [
|
|
2310
|
+
presets !== void 0 && presets.length > 0 && setPreset !== void 0 && jsx(PresetFilterControls, {
|
|
2311
|
+
presets,
|
|
2312
|
+
setPreset,
|
|
2313
|
+
testIdBase
|
|
2314
|
+
}),
|
|
1584
2315
|
jsx(PinnedFilterControls, {
|
|
1585
2316
|
pinnedFields,
|
|
1586
2317
|
value: pinnedValue,
|
|
@@ -1616,6 +2347,6 @@ function FilterBuilder(props) {
|
|
|
1616
2347
|
] });
|
|
1617
2348
|
}
|
|
1618
2349
|
//#endregion
|
|
1619
|
-
export {
|
|
2350
|
+
export { TableRow as A, loadLocale as B, SelectValue as C, TableCell as D, TableCaption as E, ResolvedFieldInput as F, useFieldMetaDiagnostic as I, fieldDescription as L, badgeVariants as M, MetaPlaceholder as N, TableHead as O, ResolvedFieldDisplay as P, lowerLegacyField as R, SelectTrigger as S, TableBody as T, saveLocale as V, SelectItem as _, PinnedFilterControls as a, SelectScrollUpButton as b, fromLocalDateTimeText as c, formatDate as d, formatDateTime as f, SelectGroup as g, SelectContent as h, FacetedFilterPicker as i, Badge as j, TableHeader as k, toLocalDateTimeText as l, Select as m, PresetFilterControls as n, FilterPanel as o, formatNumber as p, DataTableFacetedFilter as r, FilterValueControl as s, FilterBuilder as t, FilterBadges as u, SelectLabel as v, Table as w, SelectSeparator as x, SelectScrollDownButton as y, DEFAULT_LANGUAGE_STORAGE_KEY as z };
|
|
1620
2351
|
|
|
1621
|
-
//# sourceMappingURL=filter-builder-
|
|
2352
|
+
//# sourceMappingURL=filter-builder-DaWHUZuV.js.map
|