@burojs/ui-shadcn 0.5.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -0
- package/dist/bulk-summary-BWSAS7k2.js +143 -0
- package/dist/bulk-summary-BWSAS7k2.js.map +1 -0
- package/dist/bulk-summary-CrEij5vq.cjs +143 -0
- package/dist/bulk-summary-CrEij5vq.cjs.map +1 -0
- package/dist/candidates-relation-picker-CS_JZ4Fd.js +31 -0
- package/dist/candidates-relation-picker-CS_JZ4Fd.js.map +1 -0
- package/dist/candidates-relation-picker-y5M6_oe4.cjs +31 -0
- package/dist/candidates-relation-picker-y5M6_oe4.cjs.map +1 -0
- package/dist/{check-confirm-dialog-CkaVTPT9.js → check-confirm-dialog-2j0gOd7x.js} +392 -103
- package/dist/check-confirm-dialog-2j0gOd7x.js.map +1 -0
- package/dist/{check-confirm-dialog-BRByTFpg.cjs → check-confirm-dialog-C-NHgMST.cjs} +476 -102
- package/dist/check-confirm-dialog-C-NHgMST.cjs.map +1 -0
- package/dist/{dialog-B_rt9S1c.js → dialog-C123sVQ8.js} +4 -4
- package/dist/dialog-C123sVQ8.js.map +1 -0
- package/dist/{dialog-DRNUq9EA.cjs → dialog-vmMdfUWs.cjs} +3 -3
- package/dist/dialog-vmMdfUWs.cjs.map +1 -0
- package/dist/{filter-builder-B3mZFL-K.cjs → filter-builder-DFu0JP-1.cjs} +959 -128
- package/dist/filter-builder-DFu0JP-1.cjs.map +1 -0
- package/dist/{filter-builder-DOnaXNfP.js → filter-builder-DaWHUZuV.js} +863 -116
- package/dist/filter-builder-DaWHUZuV.js.map +1 -0
- package/dist/{flow-runtime-B6zOHYqI.cjs → flow-runtime-BpmrIUb6.cjs} +61 -29
- package/dist/flow-runtime-BpmrIUb6.cjs.map +1 -0
- package/dist/{flow-runtime-D8h99FSg.js → flow-runtime-DyqTmK3H.js} +62 -30
- package/dist/flow-runtime-DyqTmK3H.js.map +1 -0
- package/dist/{flow-screen-CX05b3G9.cjs → flow-screen-Bcz9NF2V.cjs} +132 -88
- package/dist/flow-screen-Bcz9NF2V.cjs.map +1 -0
- package/dist/{flow-screen-CmbARSqg.js → flow-screen-DCdli6Ha.js} +132 -88
- package/dist/flow-screen-DCdli6Ha.js.map +1 -0
- package/dist/index.cjs +951 -605
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +618 -332
- package/dist/index.d.ts +618 -332
- package/dist/index.js +863 -522
- package/dist/index.js.map +1 -1
- package/dist/{inline-flow-host-B7U1OJUe.cjs → inline-flow-host-3vw7LzzD.cjs} +5 -5
- package/dist/inline-flow-host-3vw7LzzD.cjs.map +1 -0
- package/dist/{inline-flow-host-C3H2UpSA.js → inline-flow-host-C0rI9B7f.js} +5 -5
- package/dist/inline-flow-host-C0rI9B7f.js.map +1 -0
- package/dist/label-66W1qbke.js.map +1 -1
- package/dist/label-w6JYf2cF.cjs.map +1 -1
- package/dist/lazy-relation-picker-dialog-Bt8lrEIu.cjs +153 -0
- package/dist/lazy-relation-picker-dialog-Bt8lrEIu.cjs.map +1 -0
- package/dist/lazy-relation-picker-dialog-CAScLPb2.js +135 -0
- package/dist/lazy-relation-picker-dialog-CAScLPb2.js.map +1 -0
- package/dist/markdown/index.d.cts +2 -3
- package/dist/markdown/index.d.ts +2 -3
- package/dist/{page-flow-route-CyXSvXSE.js → page-flow-route-BHUEpn0I.js} +36 -23
- package/dist/page-flow-route-BHUEpn0I.js.map +1 -0
- package/dist/{page-flow-route-CQ7HsbBm.cjs → page-flow-route-DS8IVTVH.cjs} +41 -28
- package/dist/page-flow-route-DS8IVTVH.cjs.map +1 -0
- package/dist/{place-node-SIp4XYQH.cjs → place-node-CNb3w-c7.cjs} +2227 -999
- package/dist/place-node-CNb3w-c7.cjs.map +1 -0
- package/dist/{place-node--GdsP688.js → place-node-CfzQvh4Y.js} +2164 -984
- package/dist/place-node-CfzQvh4Y.js.map +1 -0
- package/dist/{related-list-cards-B6x9HQQp.js → related-list-cards-BIqqY69Y.js} +9 -4
- package/dist/{related-list-cards-B6x9HQQp.js.map → related-list-cards-BIqqY69Y.js.map} +1 -1
- package/dist/{related-list-cards-nxuODlIA.cjs → related-list-cards-CelMxl4q.cjs} +9 -4
- package/dist/{related-list-cards-nxuODlIA.cjs.map → related-list-cards-CelMxl4q.cjs.map} +1 -1
- package/dist/{related-list-timeline-DEdfd-rr.js → related-list-timeline-CRh7xwLD.js} +6 -7
- package/dist/related-list-timeline-CRh7xwLD.js.map +1 -0
- package/dist/{related-list-timeline-BYJKEbmA.cjs → related-list-timeline-DsLdi8sv.cjs} +5 -6
- package/dist/related-list-timeline-DsLdi8sv.cjs.map +1 -0
- package/dist/{related-rows-B4levHcR.js → related-rows--uP2iKQC.js} +27 -17
- package/dist/related-rows--uP2iKQC.js.map +1 -0
- package/dist/{related-rows-zh5f_4QR.cjs → related-rows-MPnLCmPF.cjs} +32 -22
- package/dist/related-rows-MPnLCmPF.cjs.map +1 -0
- package/dist/{search-access-CURsWn-U.js → relation-create-C43cdPQt.js} +596 -29
- package/dist/relation-create-C43cdPQt.js.map +1 -0
- package/dist/{search-access-B7G5oR8n.cjs → relation-create-DDEYcKfw.cjs} +630 -32
- package/dist/relation-create-DDEYcKfw.cjs.map +1 -0
- package/dist/{relation-many-picker-gdAOD-qK.js → relation-many-picker-BJhaEqxz.js} +210 -107
- package/dist/relation-many-picker-BJhaEqxz.js.map +1 -0
- package/dist/{relation-many-picker-B-NCEUgX.cjs → relation-many-picker-DzuGJpEl.cjs} +218 -115
- package/dist/relation-many-picker-DzuGJpEl.cjs.map +1 -0
- package/dist/{relation-picker-BAY77ivO.js → relation-picker-D-tBBZW_.js} +40 -45
- package/dist/relation-picker-D-tBBZW_.js.map +1 -0
- package/dist/{relation-picker-Da_uhFvA.cjs → relation-picker-DaIMzf2H.cjs} +45 -50
- package/dist/relation-picker-DaIMzf2H.cjs.map +1 -0
- package/dist/{relation-picker-dialog-M0pQaWlp.cjs → relation-picker-dialog-9Vg5vvR1.cjs} +142 -40
- package/dist/relation-picker-dialog-9Vg5vvR1.cjs.map +1 -0
- package/dist/{relation-picker-dialog-hA9rYrZB.js → relation-picker-dialog-DoEY8EkH.js} +143 -41
- package/dist/relation-picker-dialog-DoEY8EkH.js.map +1 -0
- package/dist/relation-select-B8Fq4o-t.js +48 -0
- package/dist/relation-select-B8Fq4o-t.js.map +1 -0
- package/dist/relation-select-Cr3CfNLy.cjs +55 -0
- package/dist/relation-select-Cr3CfNLy.cjs.map +1 -0
- package/dist/{resource-page-router-OSQyRA5b.cjs → resource-page-router-C_kGW2o_.cjs} +552 -303
- package/dist/resource-page-router-C_kGW2o_.cjs.map +1 -0
- package/dist/{resource-page-router-T4vS5gbJ.js → resource-page-router-Diw6S6TK.js} +507 -258
- package/dist/resource-page-router-Diw6S6TK.js.map +1 -0
- package/dist/{rich-text-vTyVjDoa.cjs → rich-text-C_7JCvRo.cjs} +2 -2
- package/dist/{rich-text-vTyVjDoa.cjs.map → rich-text-C_7JCvRo.cjs.map} +1 -1
- package/dist/{rich-text-DFerywgz.js → rich-text-RcPBf6hr.js} +2 -2
- package/dist/{rich-text-DFerywgz.js.map → rich-text-RcPBf6hr.js.map} +1 -1
- package/dist/success-notices-Dl3M1fZP.cjs +46 -0
- package/dist/success-notices-Dl3M1fZP.cjs.map +1 -0
- package/dist/success-notices-LDEgEkc4.js +41 -0
- package/dist/success-notices-LDEgEkc4.js.map +1 -0
- package/dist/{toaster-C7CiYA4Z.cjs → toaster-BbByOytP.cjs} +72 -80
- package/dist/toaster-BbByOytP.cjs.map +1 -0
- package/dist/{toaster-CfCRLgR-.js → toaster-Ct_YHGs6.js} +66 -68
- package/dist/toaster-Ct_YHGs6.js.map +1 -0
- package/dist/{use-dialog-filter-state-BDcgS5Oz.js → use-dialog-filter-state-B_MqzHaT.js} +5 -4
- package/dist/use-dialog-filter-state-B_MqzHaT.js.map +1 -0
- package/dist/{use-dialog-filter-state-BBCC0OEK.cjs → use-dialog-filter-state-tNvqAAjh.cjs} +6 -5
- package/dist/use-dialog-filter-state-tNvqAAjh.cjs.map +1 -0
- package/dist/use-theme-DFlpXils.cjs.map +1 -1
- package/dist/use-theme-dnE3Od3I.js.map +1 -1
- package/package.json +11 -11
- package/dist/bulk-summary-CwSd6Yjb.cjs +0 -108
- package/dist/bulk-summary-CwSd6Yjb.cjs.map +0 -1
- package/dist/bulk-summary-GxMDP4bw.js +0 -108
- package/dist/bulk-summary-GxMDP4bw.js.map +0 -1
- package/dist/candidates-gate-CX2PBu6l.js +0 -63
- package/dist/candidates-gate-CX2PBu6l.js.map +0 -1
- package/dist/candidates-gate-DiFI52r7.cjs +0 -70
- package/dist/candidates-gate-DiFI52r7.cjs.map +0 -1
- package/dist/candidates-relation-picker-CBsVcnPR.js +0 -82
- package/dist/candidates-relation-picker-CBsVcnPR.js.map +0 -1
- package/dist/candidates-relation-picker-Dd_GVLlu.cjs +0 -84
- package/dist/candidates-relation-picker-Dd_GVLlu.cjs.map +0 -1
- package/dist/check-confirm-dialog-BRByTFpg.cjs.map +0 -1
- package/dist/check-confirm-dialog-CkaVTPT9.js.map +0 -1
- package/dist/dialog-B_rt9S1c.js.map +0 -1
- package/dist/dialog-DRNUq9EA.cjs.map +0 -1
- package/dist/filter-builder-B3mZFL-K.cjs.map +0 -1
- package/dist/filter-builder-DOnaXNfP.js.map +0 -1
- package/dist/flow-runtime-B6zOHYqI.cjs.map +0 -1
- package/dist/flow-runtime-D8h99FSg.js.map +0 -1
- package/dist/flow-screen-CX05b3G9.cjs.map +0 -1
- package/dist/flow-screen-CmbARSqg.js.map +0 -1
- package/dist/inline-flow-host-B7U1OJUe.cjs.map +0 -1
- package/dist/inline-flow-host-C3H2UpSA.js.map +0 -1
- package/dist/lazy-relation-picker-dialog-2oxnA6IR.js +0 -14
- package/dist/lazy-relation-picker-dialog-2oxnA6IR.js.map +0 -1
- package/dist/lazy-relation-picker-dialog-BfAGKzUG.cjs +0 -19
- package/dist/lazy-relation-picker-dialog-BfAGKzUG.cjs.map +0 -1
- package/dist/page-flow-route-CQ7HsbBm.cjs.map +0 -1
- package/dist/page-flow-route-CyXSvXSE.js.map +0 -1
- package/dist/peek-filter-CnqHwTMU.cjs +0 -51
- package/dist/peek-filter-CnqHwTMU.cjs.map +0 -1
- package/dist/peek-filter-ErLfO153.js +0 -28
- package/dist/peek-filter-ErLfO153.js.map +0 -1
- package/dist/place-node--GdsP688.js.map +0 -1
- package/dist/place-node-SIp4XYQH.cjs.map +0 -1
- package/dist/related-list-timeline-BYJKEbmA.cjs.map +0 -1
- package/dist/related-list-timeline-DEdfd-rr.js.map +0 -1
- package/dist/related-rows-B4levHcR.js.map +0 -1
- package/dist/related-rows-zh5f_4QR.cjs.map +0 -1
- package/dist/relation-create-BOaUe12G.cjs +0 -362
- package/dist/relation-create-BOaUe12G.cjs.map +0 -1
- package/dist/relation-create-Bg-eEJLs.js +0 -337
- package/dist/relation-create-Bg-eEJLs.js.map +0 -1
- package/dist/relation-many-picker-B-NCEUgX.cjs.map +0 -1
- package/dist/relation-many-picker-gdAOD-qK.js.map +0 -1
- package/dist/relation-picker-BAY77ivO.js.map +0 -1
- package/dist/relation-picker-Da_uhFvA.cjs.map +0 -1
- package/dist/relation-picker-dialog-M0pQaWlp.cjs.map +0 -1
- package/dist/relation-picker-dialog-hA9rYrZB.js.map +0 -1
- package/dist/resource-page-router-OSQyRA5b.cjs.map +0 -1
- package/dist/resource-page-router-T4vS5gbJ.js.map +0 -1
- package/dist/search-access-B7G5oR8n.cjs.map +0 -1
- package/dist/search-access-CURsWn-U.js.map +0 -1
- package/dist/shell-BPlk8hEa.js +0 -205
- package/dist/shell-BPlk8hEa.js.map +0 -1
- package/dist/shell-DxhlsPwm.cjs +0 -271
- package/dist/shell-DxhlsPwm.cjs.map +0 -1
- package/dist/toaster-C7CiYA4Z.cjs.map +0 -1
- package/dist/toaster-CfCRLgR-.js.map +0 -1
- package/dist/use-dialog-filter-state-BBCC0OEK.cjs.map +0 -1
- package/dist/use-dialog-filter-state-BDcgS5Oz.js.map +0 -1
|
@@ -1,16 +1,532 @@
|
|
|
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";
|
|
15
|
+
//#region src/layout/language-storage.ts
|
|
16
|
+
function loadLocale(storageKey = DEFAULT_LANGUAGE_STORAGE_KEY) {
|
|
17
|
+
if (typeof window === "undefined") return void 0;
|
|
18
|
+
try {
|
|
19
|
+
return window.localStorage.getItem(storageKey) ?? void 0;
|
|
20
|
+
} catch {
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
function saveLocale(code, storageKey = DEFAULT_LANGUAGE_STORAGE_KEY) {
|
|
25
|
+
if (typeof window === "undefined") return;
|
|
26
|
+
try {
|
|
27
|
+
window.localStorage.setItem(storageKey, code);
|
|
28
|
+
} catch {}
|
|
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
|
|
14
530
|
//#region src/primitives/badge.tsx
|
|
15
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", {
|
|
16
532
|
variants: { variant: {
|
|
@@ -88,11 +604,11 @@ const SelectGroup = SelectPrimitive.Group;
|
|
|
88
604
|
const SelectValue = SelectPrimitive.Value;
|
|
89
605
|
const SelectTrigger = React.forwardRef(({ className, children, ...props }, ref) => jsxs(SelectPrimitive.Trigger, {
|
|
90
606
|
ref,
|
|
91
|
-
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),
|
|
92
608
|
...props,
|
|
93
609
|
children: [children, jsx(SelectPrimitive.Icon, {
|
|
94
610
|
asChild: true,
|
|
95
|
-
children: jsx(IconChevronDown, { className: "h-4 w-4 opacity-50" })
|
|
611
|
+
children: jsx(IconChevronDown, { className: "h-4 w-4 shrink-0 opacity-50" })
|
|
96
612
|
})]
|
|
97
613
|
}));
|
|
98
614
|
SelectTrigger.displayName = SelectPrimitive.Trigger.displayName;
|
|
@@ -131,15 +647,24 @@ const SelectLabel = React.forwardRef(({ className, ...props }, ref) => jsx(Selec
|
|
|
131
647
|
...props
|
|
132
648
|
}));
|
|
133
649
|
SelectLabel.displayName = SelectPrimitive.Label.displayName;
|
|
134
|
-
const SelectItem = React.forwardRef(({ className, children, ...props }, ref) =>
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
children: jsx(
|
|
141
|
-
|
|
142
|
-
}))
|
|
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
|
+
});
|
|
143
668
|
SelectItem.displayName = SelectPrimitive.Item.displayName;
|
|
144
669
|
const SelectSeparator = React.forwardRef(({ className, ...props }, ref) => jsx(SelectPrimitive.Separator, {
|
|
145
670
|
ref,
|
|
@@ -149,13 +674,8 @@ const SelectSeparator = React.forwardRef(({ className, ...props }, ref) => jsx(S
|
|
|
149
674
|
SelectSeparator.displayName = SelectPrimitive.Separator.displayName;
|
|
150
675
|
//#endregion
|
|
151
676
|
//#region src/fields/format.ts
|
|
152
|
-
const LOCALE_TAG = {
|
|
153
|
-
ru: "ru-RU",
|
|
154
|
-
en: "en-US"
|
|
155
|
-
};
|
|
156
677
|
function localeTag(locale) {
|
|
157
|
-
|
|
158
|
-
return LOCALE_TAG[loadLanguage()] ?? "en-US";
|
|
678
|
+
return locale || loadLocale() || "en-US";
|
|
159
679
|
}
|
|
160
680
|
const dateCache = new Map();
|
|
161
681
|
const dateTimeCache = new Map();
|
|
@@ -197,9 +717,9 @@ function formatNumber(n, locale) {
|
|
|
197
717
|
//#endregion
|
|
198
718
|
//#region src/list-kit/filter-builder/describe-row.ts
|
|
199
719
|
const MAX_DESCRIBE_DEPTH = 2;
|
|
200
|
-
function describeValue(value,
|
|
720
|
+
function describeValue(value, chromeT, spec, formatValue) {
|
|
201
721
|
if (value === void 0) return null;
|
|
202
|
-
if (Array.isArray(value)) return
|
|
722
|
+
if (Array.isArray(value)) return chromeT("filters.selectedCount", { count: value.length }, `${value.length} selected`);
|
|
203
723
|
const optionLabel = spec?.options?.find((o) => o.value === String(value))?.label;
|
|
204
724
|
if (optionLabel !== void 0) return optionLabel;
|
|
205
725
|
if (spec && formatValue) {
|
|
@@ -208,20 +728,20 @@ function describeValue(value, t, spec, formatValue) {
|
|
|
208
728
|
}
|
|
209
729
|
return String(value);
|
|
210
730
|
}
|
|
211
|
-
function describeAt(row, available,
|
|
731
|
+
function describeAt(row, available, chromeT, depth, formatValue) {
|
|
212
732
|
if (isFilterGroup(row)) {
|
|
213
733
|
if (depth > MAX_DESCRIBE_DEPTH) return "…";
|
|
214
|
-
const joinLabel =
|
|
215
|
-
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} `)})`;
|
|
216
736
|
}
|
|
217
737
|
const spec = available.find((s) => s.field === row.field);
|
|
218
738
|
const label = spec?.label ?? row.field;
|
|
219
|
-
const opLabel =
|
|
220
|
-
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);
|
|
221
741
|
return valueText === null ? `${label} ${opLabel}` : `${label} ${opLabel} ${valueText}`;
|
|
222
742
|
}
|
|
223
|
-
function describeRow(row, available,
|
|
224
|
-
return describeAt(row, available,
|
|
743
|
+
function describeRow(row, available, chromeT, formatValue) {
|
|
744
|
+
return describeAt(row, available, chromeT, 1, formatValue);
|
|
225
745
|
}
|
|
226
746
|
function relationLabelKey(to, by, value) {
|
|
227
747
|
return `${by === void 0 ? to : `${to}#${by}`}::${value}`;
|
|
@@ -265,15 +785,17 @@ function EnumLabelResolver(props) {
|
|
|
265
785
|
const { enumName, onResolve } = props;
|
|
266
786
|
const { values } = useEnumValues(enumName);
|
|
267
787
|
const caps = useI18nCaps();
|
|
788
|
+
const def = useApp().registry.enums.get(enumName);
|
|
268
789
|
useEffect(() => {
|
|
269
790
|
if (!values) return;
|
|
270
791
|
const labels = {};
|
|
271
|
-
for (const entry of values) labels[entry.value] = resolveEnumLabel(caps, enumName, entry.value, entry.label);
|
|
792
|
+
for (const entry of values) labels[entry.value] = resolveEnumLabel(caps, enumName, entry.value, enumValueText(def, entry.value) ?? entry.label);
|
|
272
793
|
onResolve(enumName, labels);
|
|
273
794
|
}, [
|
|
274
795
|
values,
|
|
275
796
|
enumName,
|
|
276
797
|
caps,
|
|
798
|
+
def,
|
|
277
799
|
onResolve
|
|
278
800
|
]);
|
|
279
801
|
return null;
|
|
@@ -352,15 +874,18 @@ function badgeKey(row, index) {
|
|
|
352
874
|
if (isFilterGroup(row)) return `${index}:group:${row.join}`;
|
|
353
875
|
return `${index}:${row.field}:${row.op}`;
|
|
354
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";
|
|
355
879
|
function FilterBadges(props) {
|
|
356
880
|
const { group, setGroup, available, fixed, testIdBase, onOpenAt, onRemoved, pinnedFields = [], pinnedValue = [], setPinnedValue } = props;
|
|
357
|
-
const
|
|
358
|
-
const
|
|
881
|
+
const chromeT = useChromeT();
|
|
882
|
+
const locale = useOptionalLocale();
|
|
883
|
+
const tt = (key, params, fallback) => chromeT(key, {
|
|
359
884
|
params,
|
|
360
885
|
...typeof params?.count === "number" ? { count: params.count } : {},
|
|
361
886
|
defaultValue: fallback
|
|
362
887
|
});
|
|
363
|
-
const removeLabel =
|
|
888
|
+
const removeLabel = chromeT("filters.removeRow", { defaultValue: "Remove filter" });
|
|
364
889
|
const { enumLabels, relationLabels, onEnumResolved, onRelationResolved } = useFormatValueMaps();
|
|
365
890
|
const formatValue = (kind, value) => {
|
|
366
891
|
if (kind.kind === "enum") return enumLabels[kind.enumName]?.[String(value)];
|
|
@@ -368,7 +893,7 @@ function FilterBadges(props) {
|
|
|
368
893
|
if (kind.kind === "date") {
|
|
369
894
|
const d = new Date(String(value));
|
|
370
895
|
if (Number.isNaN(d.getTime())) return void 0;
|
|
371
|
-
return kind.time ? formatDateTime(d) : formatDate(d);
|
|
896
|
+
return kind.time ? formatDateTime(d, locale) : formatDate(d, locale);
|
|
372
897
|
}
|
|
373
898
|
};
|
|
374
899
|
const orphanPinned = useMemo(() => {
|
|
@@ -402,49 +927,56 @@ function FilterBadges(props) {
|
|
|
402
927
|
onEnumResolved,
|
|
403
928
|
onRelationResolved
|
|
404
929
|
}),
|
|
405
|
-
group.rows.map((row, index) =>
|
|
930
|
+
group.rows.map((row, index) => jsx(Badge, {
|
|
406
931
|
variant: "secondary",
|
|
407
|
-
className: "
|
|
408
|
-
children:
|
|
409
|
-
|
|
410
|
-
"
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
"
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
className:
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
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
|
+
})
|
|
425
953
|
}, badgeKey(row, index))),
|
|
426
954
|
labelledFixed.map((entry, index) => jsx(Badge, {
|
|
427
955
|
variant: "outline",
|
|
428
956
|
"data-testid": `${testIdBase}:badge:fixed:${index}`,
|
|
429
957
|
children: entry.label
|
|
430
958
|
}, `fixed-${index}`)),
|
|
431
|
-
orphanPinned.map(({ expr, index }) =>
|
|
959
|
+
orphanPinned.map(({ expr, index }) => jsx(Badge, {
|
|
432
960
|
variant: "secondary",
|
|
433
|
-
className: "
|
|
434
|
-
children:
|
|
435
|
-
|
|
436
|
-
children:
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
"
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
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
|
+
})
|
|
448
980
|
}, `pinned-${index}`))
|
|
449
981
|
]
|
|
450
982
|
});
|
|
@@ -634,14 +1166,15 @@ const OPTIONS = ["true", "false"];
|
|
|
634
1166
|
function BooleanValueControl(props) {
|
|
635
1167
|
const { value, onChange, testId, arity, autoFocus, fieldLabel, fill } = props;
|
|
636
1168
|
const t = useOptionalT();
|
|
637
|
-
const
|
|
1169
|
+
const chromeT = useChromeT();
|
|
1170
|
+
const ariaLabel = fieldLabel ? chromeT("filters.valueFor", {
|
|
638
1171
|
defaultValue: `Value for ${fieldLabel}`,
|
|
639
1172
|
params: { field: fieldLabel }
|
|
640
|
-
}) :
|
|
1173
|
+
}) : chromeT("filters.value", { defaultValue: "Value" });
|
|
641
1174
|
const yesLabel = t("boolean.yes", { defaultValue: "Yes" });
|
|
642
1175
|
const noLabel = t("boolean.no", { defaultValue: "No" });
|
|
643
1176
|
const labelOf = (token) => token === "true" ? yesLabel : noLabel;
|
|
644
|
-
const clearLabel =
|
|
1177
|
+
const clearLabel = chromeT("filters.clear", { defaultValue: "Clear" });
|
|
645
1178
|
const { open, setOpen, selected, select, triggerLabel, clear } = useSelectableValueControl({
|
|
646
1179
|
value,
|
|
647
1180
|
arity,
|
|
@@ -661,7 +1194,7 @@ function BooleanValueControl(props) {
|
|
|
661
1194
|
"data-testid": testId,
|
|
662
1195
|
"aria-label": ariaLabel,
|
|
663
1196
|
className: cn("h-8 justify-between", fill && "w-full"),
|
|
664
|
-
children: triggerLabel(labelOf, ariaLabel)
|
|
1197
|
+
children: triggerLabel(labelOf, props.placeholder ?? ariaLabel)
|
|
665
1198
|
})
|
|
666
1199
|
}), jsx(PopoverContent, {
|
|
667
1200
|
className: "w-[140px] p-0",
|
|
@@ -694,12 +1227,13 @@ function BooleanValueControl(props) {
|
|
|
694
1227
|
function SelectListValueControl(props) {
|
|
695
1228
|
const { options, value, onChange, testId, arity, autoFocus, fieldLabel, isLoading = false } = props;
|
|
696
1229
|
const t = useOptionalT();
|
|
697
|
-
const
|
|
1230
|
+
const chromeT = useChromeT();
|
|
1231
|
+
const ariaLabel = fieldLabel ? chromeT("filters.valueFor", {
|
|
698
1232
|
defaultValue: `Value for ${fieldLabel}`,
|
|
699
1233
|
params: { field: fieldLabel }
|
|
700
|
-
}) :
|
|
1234
|
+
}) : chromeT("filters.value", { defaultValue: "Value" });
|
|
701
1235
|
const loadingLabel = t("table.loading", { defaultValue: "Loading…" });
|
|
702
|
-
const clearLabel =
|
|
1236
|
+
const clearLabel = chromeT("filters.clear", { defaultValue: "Clear" });
|
|
703
1237
|
const labelFor = props.labelFor ?? ((raw) => options.find((o) => o.value === raw)?.label ?? raw);
|
|
704
1238
|
const { open, setOpen, selected, select, triggerLabel, clear } = useSelectableValueControl({
|
|
705
1239
|
value,
|
|
@@ -721,7 +1255,7 @@ function SelectListValueControl(props) {
|
|
|
721
1255
|
className: cn("h-8 justify-between truncate", props.fill ? "w-full" : "max-w-[200px]"),
|
|
722
1256
|
children: jsx("span", {
|
|
723
1257
|
className: "truncate",
|
|
724
|
-
children: isLoading ? loadingLabel : triggerLabel(labelFor, ariaLabel)
|
|
1258
|
+
children: isLoading ? loadingLabel : triggerLabel(labelFor, props.placeholder ?? ariaLabel)
|
|
725
1259
|
})
|
|
726
1260
|
})
|
|
727
1261
|
}), jsx(PopoverContent, {
|
|
@@ -770,8 +1304,9 @@ function SelectListValueControl(props) {
|
|
|
770
1304
|
function EnumValueControl(props) {
|
|
771
1305
|
const { enumName, value, onChange, testId, arity, autoFocus, fieldLabel, fill } = props;
|
|
772
1306
|
const caps = useI18nCaps();
|
|
1307
|
+
const def = useApp().registry.enums.get(enumName);
|
|
773
1308
|
const { values, isLoading } = useEnumValues(enumName);
|
|
774
|
-
const labelFor = (raw) => resolveEnumLabel(caps, enumName, raw, values?.find((v) => v.value === raw)?.label);
|
|
1309
|
+
const labelFor = (raw) => resolveEnumLabel(caps, enumName, raw, enumValueText(def, raw) ?? values?.find((v) => v.value === raw)?.label);
|
|
775
1310
|
const options = (values ?? []).map((entry) => ({
|
|
776
1311
|
value: entry.value,
|
|
777
1312
|
label: entry.value
|
|
@@ -783,6 +1318,7 @@ function EnumValueControl(props) {
|
|
|
783
1318
|
arity,
|
|
784
1319
|
autoFocus,
|
|
785
1320
|
fieldLabel,
|
|
1321
|
+
placeholder: props.placeholder,
|
|
786
1322
|
options,
|
|
787
1323
|
labelFor,
|
|
788
1324
|
isLoading,
|
|
@@ -807,6 +1343,7 @@ function useRelatedLabels(to, ids, by) {
|
|
|
807
1343
|
op: "in",
|
|
808
1344
|
value: ids
|
|
809
1345
|
} } : {},
|
|
1346
|
+
scope: "none",
|
|
810
1347
|
enabled: ids.length > 0
|
|
811
1348
|
});
|
|
812
1349
|
return useMemo(() => {
|
|
@@ -828,6 +1365,7 @@ function RelationValueControl(props) {
|
|
|
828
1365
|
const { to, by, value, onChange, testId, arity, autoFocus, fieldLabel } = props;
|
|
829
1366
|
const app = useApp();
|
|
830
1367
|
const t = useOptionalT();
|
|
1368
|
+
const chromeT = useChromeT();
|
|
831
1369
|
const targetDef = app.registry.get(to);
|
|
832
1370
|
const displayField = targetDef?.displayField ?? "name";
|
|
833
1371
|
const [search, setSearch] = useState("");
|
|
@@ -850,6 +1388,7 @@ function RelationValueControl(props) {
|
|
|
850
1388
|
op: "contains",
|
|
851
1389
|
value: search.trim()
|
|
852
1390
|
} } : {},
|
|
1391
|
+
scope: "session",
|
|
853
1392
|
enabled: open && access.status === "ready"
|
|
854
1393
|
});
|
|
855
1394
|
const rows = listQuery.data?.data ?? [];
|
|
@@ -865,11 +1404,11 @@ function RelationValueControl(props) {
|
|
|
865
1404
|
if (fromRows) return rowTitle(fromRows);
|
|
866
1405
|
return fallbackLabels[id] ?? id;
|
|
867
1406
|
};
|
|
868
|
-
const ariaLabel = fieldLabel ?
|
|
1407
|
+
const ariaLabel = fieldLabel ? chromeT("filters.valueFor", {
|
|
869
1408
|
defaultValue: `Value for ${fieldLabel}`,
|
|
870
1409
|
params: { field: fieldLabel }
|
|
871
|
-
}) :
|
|
872
|
-
const clearLabel =
|
|
1410
|
+
}) : chromeT("filters.value", { defaultValue: "Value" });
|
|
1411
|
+
const clearLabel = chromeT("filters.clear", { defaultValue: "Clear" });
|
|
873
1412
|
return jsxs(Popover, {
|
|
874
1413
|
open,
|
|
875
1414
|
onOpenChange: setOpen,
|
|
@@ -885,7 +1424,7 @@ function RelationValueControl(props) {
|
|
|
885
1424
|
className: cn("h-8 justify-between truncate", props.fill ? "w-full" : "max-w-[220px]"),
|
|
886
1425
|
children: jsx("span", {
|
|
887
1426
|
className: "truncate",
|
|
888
|
-
children: triggerLabel(labelFor, ariaLabel)
|
|
1427
|
+
children: triggerLabel(labelFor, props.placeholder ?? ariaLabel)
|
|
889
1428
|
})
|
|
890
1429
|
})
|
|
891
1430
|
}), jsx(PopoverContent, {
|
|
@@ -941,6 +1480,20 @@ function RelationValueControl(props) {
|
|
|
941
1480
|
});
|
|
942
1481
|
}
|
|
943
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
|
|
944
1497
|
//#region src/list-kit/filter-builder/values/scalar-value-input.tsx
|
|
945
1498
|
function isDateTime(kind) {
|
|
946
1499
|
return kind.kind === "date" && kind.time === true;
|
|
@@ -953,12 +1506,9 @@ function inputTypeFor(kind) {
|
|
|
953
1506
|
}
|
|
954
1507
|
}
|
|
955
1508
|
function toText(kind, value) {
|
|
1509
|
+
if (isDateTime(kind)) return toLocalDateTimeText(value);
|
|
956
1510
|
if (value === void 0 || value === null || value === "") return "";
|
|
957
|
-
|
|
958
|
-
const d = new Date(String(value));
|
|
959
|
-
if (Number.isNaN(d.getTime())) return "";
|
|
960
|
-
const pad = (n) => String(n).padStart(2, "0");
|
|
961
|
-
return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}T${pad(d.getHours())}:${pad(d.getMinutes())}`;
|
|
1511
|
+
return String(value);
|
|
962
1512
|
}
|
|
963
1513
|
function parseOne(kind, raw) {
|
|
964
1514
|
if (raw === "") return void 0;
|
|
@@ -966,19 +1516,16 @@ function parseOne(kind, raw) {
|
|
|
966
1516
|
const n = Number(raw);
|
|
967
1517
|
return Number.isFinite(n) ? n : void 0;
|
|
968
1518
|
}
|
|
969
|
-
if (isDateTime(kind))
|
|
970
|
-
const d = new Date(raw);
|
|
971
|
-
return Number.isNaN(d.getTime()) ? void 0 : d.toISOString();
|
|
972
|
-
}
|
|
1519
|
+
if (isDateTime(kind)) return fromLocalDateTimeText(raw);
|
|
973
1520
|
return raw;
|
|
974
1521
|
}
|
|
975
1522
|
function ScalarValueInput(props) {
|
|
976
|
-
const
|
|
1523
|
+
const chromeT = useChromeT();
|
|
977
1524
|
const inputType = inputTypeFor(props.kind);
|
|
978
|
-
const ariaLabel = props.fieldLabel ?
|
|
1525
|
+
const ariaLabel = props.fieldLabel ? chromeT("filters.valueFor", {
|
|
979
1526
|
defaultValue: `Value for ${props.fieldLabel}`,
|
|
980
1527
|
params: { field: props.fieldLabel }
|
|
981
|
-
}) :
|
|
1528
|
+
}) : chromeT("filters.value", { defaultValue: "Value" });
|
|
982
1529
|
if (props.arity === "multi") return jsx(ScalarMultiInput, {
|
|
983
1530
|
...props,
|
|
984
1531
|
inputType,
|
|
@@ -1108,6 +1655,7 @@ function FilterValueControl(props) {
|
|
|
1108
1655
|
arity,
|
|
1109
1656
|
autoFocus: props.autoFocus,
|
|
1110
1657
|
fieldLabel: props.fieldLabel,
|
|
1658
|
+
placeholder: props.placeholder,
|
|
1111
1659
|
options: props.options,
|
|
1112
1660
|
fill: props.fill
|
|
1113
1661
|
});
|
|
@@ -1124,6 +1672,7 @@ function FilterValueControl(props) {
|
|
|
1124
1672
|
arity,
|
|
1125
1673
|
autoFocus: props.autoFocus,
|
|
1126
1674
|
fieldLabel: props.fieldLabel,
|
|
1675
|
+
placeholder: props.placeholder,
|
|
1127
1676
|
options: props.kind.values.map((entry) => ({
|
|
1128
1677
|
value: entry,
|
|
1129
1678
|
label: entry
|
|
@@ -1152,10 +1701,10 @@ const FIELD_CLASS = "h-8 min-w-0 shrink basis-40";
|
|
|
1152
1701
|
const OPERATOR_CLASS = "h-8 min-w-0 shrink basis-[9.5rem]";
|
|
1153
1702
|
function FilterRow(props) {
|
|
1154
1703
|
const { row, path, available, onPatch, onRemove, testIdBase, debounceMs } = props;
|
|
1155
|
-
const
|
|
1704
|
+
const chromeT = useChromeT();
|
|
1156
1705
|
const base = `${testIdBase}:row:${path.join("-")}`;
|
|
1157
1706
|
const spec = available.find((s) => s.field === row.field);
|
|
1158
|
-
const removeLabel =
|
|
1707
|
+
const removeLabel = chromeT("filters.removeRow", { defaultValue: "Remove filter" });
|
|
1159
1708
|
if (!spec) return jsxs("div", {
|
|
1160
1709
|
className: ROW_CLASS,
|
|
1161
1710
|
children: [
|
|
@@ -1195,8 +1744,8 @@ function FilterRow(props) {
|
|
|
1195
1744
|
};
|
|
1196
1745
|
const handleOpChange = (op) => onPatch(path, { op });
|
|
1197
1746
|
const handleValueChange = (value) => onPatch(path, { value });
|
|
1198
|
-
const fieldAriaLabel =
|
|
1199
|
-
const operatorAriaLabel =
|
|
1747
|
+
const fieldAriaLabel = chromeT("filters.field", { defaultValue: "Field" });
|
|
1748
|
+
const operatorAriaLabel = chromeT("filters.operator", { defaultValue: "Operator" });
|
|
1200
1749
|
return jsxs("div", {
|
|
1201
1750
|
className: ROW_CLASS,
|
|
1202
1751
|
children: [
|
|
@@ -1225,7 +1774,7 @@ function FilterRow(props) {
|
|
|
1225
1774
|
}), jsx(SelectContent, { children: spec.operators.map((op) => jsx(SelectItem, {
|
|
1226
1775
|
value: op,
|
|
1227
1776
|
"data-testid": `${opTestId}:option:${op}`,
|
|
1228
|
-
children:
|
|
1777
|
+
children: chromeT(`filters.operators.${op}`, { defaultValue: op })
|
|
1229
1778
|
}, op)) })]
|
|
1230
1779
|
}),
|
|
1231
1780
|
jsx("div", {
|
|
@@ -1273,10 +1822,10 @@ function FilterGroupEditor(props) {
|
|
|
1273
1822
|
}
|
|
1274
1823
|
function GroupEditorInner(props) {
|
|
1275
1824
|
const { group, path, available, onChange, testIdBase, onRemove, addGroupAt, debounceMs } = props;
|
|
1276
|
-
const
|
|
1825
|
+
const chromeT = useChromeT();
|
|
1277
1826
|
const base = `${testIdBase}:group:${path.join("-")}`;
|
|
1278
1827
|
const canNestGroup = depthOf(path) < 3;
|
|
1279
|
-
const joinLabel = (join) =>
|
|
1828
|
+
const joinLabel = (join) => chromeT(`filters.${join}`, { defaultValue: join });
|
|
1280
1829
|
const handlePatch = (rowPath, patch) => {
|
|
1281
1830
|
const index = rowPath[rowPath.length - 1];
|
|
1282
1831
|
if (index === void 0) return;
|
|
@@ -1309,11 +1858,11 @@ function GroupEditorInner(props) {
|
|
|
1309
1858
|
return jsxs(Fragment, { children: [jsxs("div", {
|
|
1310
1859
|
className: CONNECTOR_CLASS,
|
|
1311
1860
|
children: [
|
|
1312
|
-
index === 0 &&
|
|
1861
|
+
index === 0 && chromeT("filters.where", { defaultValue: "Where" }),
|
|
1313
1862
|
index === 1 && jsx("button", {
|
|
1314
1863
|
type: "button",
|
|
1315
1864
|
"data-testid": `${base}:join`,
|
|
1316
|
-
"aria-label":
|
|
1865
|
+
"aria-label": chromeT("filters.toggleJoin", {
|
|
1317
1866
|
defaultValue: `Join: ${joinLabel(group.join)} — activate to switch to ${joinLabel(otherJoin)}`,
|
|
1318
1867
|
params: {
|
|
1319
1868
|
current: joinLabel(group.join),
|
|
@@ -1365,7 +1914,7 @@ function GroupEditorInner(props) {
|
|
|
1365
1914
|
children: [jsx(IconPlus, {
|
|
1366
1915
|
className: "h-4 w-4",
|
|
1367
1916
|
"aria-hidden": "true"
|
|
1368
|
-
}),
|
|
1917
|
+
}), chromeT("filters.addRow", { defaultValue: "Add filter" })]
|
|
1369
1918
|
}),
|
|
1370
1919
|
canNestGroup && jsxs(Button, {
|
|
1371
1920
|
type: "button",
|
|
@@ -1376,7 +1925,7 @@ function GroupEditorInner(props) {
|
|
|
1376
1925
|
children: [jsx(IconFolderPlus, {
|
|
1377
1926
|
className: "h-4 w-4",
|
|
1378
1927
|
"aria-hidden": "true"
|
|
1379
|
-
}),
|
|
1928
|
+
}), chromeT("filters.addGroup", { defaultValue: "Add group" })]
|
|
1380
1929
|
}),
|
|
1381
1930
|
onRemove && jsxs(Button, {
|
|
1382
1931
|
type: "button",
|
|
@@ -1387,7 +1936,7 @@ function GroupEditorInner(props) {
|
|
|
1387
1936
|
children: [jsx(IconX, {
|
|
1388
1937
|
className: "h-4 w-4",
|
|
1389
1938
|
"aria-hidden": "true"
|
|
1390
|
-
}),
|
|
1939
|
+
}), chromeT("filters.removeGroup", { defaultValue: "Remove group" })]
|
|
1391
1940
|
})
|
|
1392
1941
|
]
|
|
1393
1942
|
})
|
|
@@ -1403,7 +1952,7 @@ function countGroupRows(group) {
|
|
|
1403
1952
|
}
|
|
1404
1953
|
function FilterPanel(props) {
|
|
1405
1954
|
const { group, setGroup, available, setPage, testIdBase, open, onOpenChange, focusPath, externalRemoval } = props;
|
|
1406
|
-
const
|
|
1955
|
+
const chromeT = useChromeT();
|
|
1407
1956
|
const [draft, setDraft] = useState(() => toDraft(group));
|
|
1408
1957
|
const wasOpenRef = useRef(open);
|
|
1409
1958
|
const topLevelIdsRef = useRef([]);
|
|
@@ -1455,7 +2004,7 @@ function FilterPanel(props) {
|
|
|
1455
2004
|
className: "h-4 w-4",
|
|
1456
2005
|
"aria-hidden": "true"
|
|
1457
2006
|
}),
|
|
1458
|
-
|
|
2007
|
+
chromeT("filters.title", { defaultValue: "Filters" }),
|
|
1459
2008
|
appliedCount > 0 && jsx("span", {
|
|
1460
2009
|
"data-testid": `${testIdBase}:count`,
|
|
1461
2010
|
className: "ml-1 rounded-full bg-primary px-1.5 text-xs text-primary-foreground",
|
|
@@ -1483,13 +2032,13 @@ function FilterPanel(props) {
|
|
|
1483
2032
|
size: "sm",
|
|
1484
2033
|
"data-testid": `${testIdBase}:reset`,
|
|
1485
2034
|
onClick: handleReset,
|
|
1486
|
-
children:
|
|
2035
|
+
children: chromeT("filters.reset", { defaultValue: "Reset" })
|
|
1487
2036
|
}), jsx(Button, {
|
|
1488
2037
|
type: "button",
|
|
1489
2038
|
size: "sm",
|
|
1490
2039
|
"data-testid": `${testIdBase}:apply`,
|
|
1491
2040
|
onClick: handleApply,
|
|
1492
|
-
children:
|
|
2041
|
+
children: chromeT("filters.apply", { defaultValue: "Apply" })
|
|
1493
2042
|
})]
|
|
1494
2043
|
})]
|
|
1495
2044
|
})]
|
|
@@ -1544,11 +2093,204 @@ function PinnedControl(props) {
|
|
|
1544
2093
|
});
|
|
1545
2094
|
}
|
|
1546
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
|
|
1547
2289
|
//#region src/list-kit/filter-builder/filter-builder.tsx
|
|
1548
2290
|
const EMPTY_PINNED_FIELDS = Object.freeze([]);
|
|
1549
2291
|
const EMPTY_PINNED_VALUE = Object.freeze([]);
|
|
1550
2292
|
function FilterBuilder(props) {
|
|
1551
|
-
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;
|
|
1552
2294
|
const [open, setOpen] = useState(false);
|
|
1553
2295
|
const [focusPath, setFocusPath] = useState(void 0);
|
|
1554
2296
|
const [externalRemoval, setExternalRemoval] = useState(void 0);
|
|
@@ -1565,6 +2307,11 @@ function FilterBuilder(props) {
|
|
|
1565
2307
|
setPinnedValue?.(next);
|
|
1566
2308
|
};
|
|
1567
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
|
+
}),
|
|
1568
2315
|
jsx(PinnedFilterControls, {
|
|
1569
2316
|
pinnedFields,
|
|
1570
2317
|
value: pinnedValue,
|
|
@@ -1600,6 +2347,6 @@ function FilterBuilder(props) {
|
|
|
1600
2347
|
] });
|
|
1601
2348
|
}
|
|
1602
2349
|
//#endregion
|
|
1603
|
-
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 };
|
|
1604
2351
|
|
|
1605
|
-
//# sourceMappingURL=filter-builder-
|
|
2352
|
+
//# sourceMappingURL=filter-builder-DaWHUZuV.js.map
|