@stackcraftmx/calcita 0.56.0 → 0.57.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 +43 -2
- package/dist/DataTable/ServerTableFilterDrawer.js +3 -1
- package/dist/DataTable/ServerTableFilterDrawer.js.map +1 -1
- package/dist/locales/en.js +2 -0
- package/dist/locales/en.js.map +1 -1
- package/dist/locales/es.js +2 -0
- package/dist/locales/es.js.map +1 -1
- package/dist/theme/components/minimalComponents.js +15 -0
- package/dist/theme/components/minimalComponents.js.map +1 -1
- package/dist/theme/glassSurface.js +10 -12
- package/dist/theme/glassSurface.js.map +1 -1
- package/dist/theme/themePieces.js +10 -4
- package/dist/theme/themePieces.js.map +1 -1
- package/dist/types/DataTable/ServerTableFilterDrawer.d.ts +2 -0
- package/dist/types/locales/en.d.ts +2 -0
- package/dist/types/locales/es.d.ts +2 -0
- package/dist/types/ui/filters/FilterDrawer.d.ts +1 -0
- package/dist/types/ui/filters/filterDrawerFields.d.ts +77 -2
- package/dist/ui/filters/FilterDrawer.js +118 -53
- package/dist/ui/filters/FilterDrawer.js.map +1 -1
- package/dist/ui/filters/filterDrawerFields.js +65 -14
- package/dist/ui/filters/filterDrawerFields.js.map +1 -1
- package/package.json +1 -1
|
@@ -15,20 +15,48 @@ import NumberInput from "../../inputs/NumberInput.js";
|
|
|
15
15
|
import SelectInput from "../../inputs/SelectInput.js";
|
|
16
16
|
import TextInput from "../../inputs/TextInput.js";
|
|
17
17
|
import { piecePart, withPieceSlots, usePieceProps } from "../../theme/themePieces.js";
|
|
18
|
+
import { useToolkitSettings } from "../../toolkit/toolkitContext.js";
|
|
19
|
+
import { useFormatters } from "../../toolkit/useFormatters.js";
|
|
18
20
|
import { useToolkitTranslation } from "../../toolkit/useToolkitTranslation.js";
|
|
19
21
|
import { warnUnknownProps } from "../../toolkit/unknownProps.js";
|
|
20
|
-
import { drawerValuesOf,
|
|
22
|
+
import { drawerValuesOf, drawerProblemsOf, emptyDrawerValuesOf, filterKeysOf, FILTER_FIELD_KIND, comparisonOf, COMPARISON_OPERATOR, comparisonTextOf, dateRangeBoundsOf } from "./filterDrawerFields.js";
|
|
23
|
+
import { todayInZone } from "../metrics/heatmapScale.js";
|
|
21
24
|
const FILTER_DRAWER_ANCHOR = Object.freeze({ left: "left", right: "right" });
|
|
22
25
|
const DEFAULT_WIDTH = 380;
|
|
23
26
|
const ANY_OPTION = "__calcita_any__";
|
|
27
|
+
const DRAWER_CONTAINER_NAME = "calcita-filter-drawer";
|
|
28
|
+
function BrowserDateInput({ name, label, value, onChange, error, helperText, minDate, maxDate, className }) {
|
|
29
|
+
return /* @__PURE__ */ jsx(
|
|
30
|
+
TextInput,
|
|
31
|
+
{
|
|
32
|
+
type: "date",
|
|
33
|
+
name,
|
|
34
|
+
label,
|
|
35
|
+
value,
|
|
36
|
+
onChange,
|
|
37
|
+
error,
|
|
38
|
+
helperText,
|
|
39
|
+
clearable: false,
|
|
40
|
+
className,
|
|
41
|
+
slotProps: { htmlInput: { min: minDate, max: maxDate } }
|
|
42
|
+
}
|
|
43
|
+
);
|
|
44
|
+
}
|
|
24
45
|
const THEME_NAME = "CalcitaFilterDrawer";
|
|
25
46
|
const FilterDrawerHeader = piecePart(THEME_NAME, "Header", Stack);
|
|
26
47
|
const FilterDrawerBody = piecePart(THEME_NAME, "Body", Stack);
|
|
27
48
|
const FilterDrawerFooter = piecePart(THEME_NAME, "Footer", Stack);
|
|
28
|
-
|
|
29
|
-
|
|
49
|
+
const FilterDrawerDateInput = piecePart(THEME_NAME, "DateInput", BrowserDateInput);
|
|
50
|
+
function PairedField({ label, inputId, children, template = "minmax(0, 1fr) minmax(0, 1fr)", stackPairsBelow }) {
|
|
51
|
+
return /* @__PURE__ */ jsx(FieldFrame, { inputId, label, children: /* @__PURE__ */ jsx(Box, { sx: {
|
|
52
|
+
display: "grid",
|
|
53
|
+
gridTemplateColumns: template,
|
|
54
|
+
gap: 1.5,
|
|
55
|
+
alignItems: "start",
|
|
56
|
+
...stackPairsBelow && { [`@container ${DRAWER_CONTAINER_NAME} (width < ${stackPairsBelow}px)`]: { gridTemplateColumns: "minmax(0, 1fr)" } }
|
|
57
|
+
}, children }) });
|
|
30
58
|
}
|
|
31
|
-
function DrawerField({ field, values, setValue,
|
|
59
|
+
function DrawerField({ field, values, setValue, errorOf, today, stackPairsBelow, t }) {
|
|
32
60
|
const fieldId = useId();
|
|
33
61
|
const [key] = filterKeysOf(field);
|
|
34
62
|
switch (field.kind) {
|
|
@@ -66,7 +94,7 @@ function DrawerField({ field, values, setValue, invalidRanges, t }) {
|
|
|
66
94
|
case FILTER_FIELD_KIND.numberRange: {
|
|
67
95
|
const [fromKey, toKey] = filterKeysOf(field);
|
|
68
96
|
const numberProps = { decimals: field.decimals ?? 0, prefix: field.prefix, suffix: field.suffix, helperText: "" };
|
|
69
|
-
return /* @__PURE__ */ jsxs(PairedField, { label: field.label, inputId: `${fieldId}-from`, children: [
|
|
97
|
+
return /* @__PURE__ */ jsxs(PairedField, { label: field.label, inputId: `${fieldId}-from`, stackPairsBelow, children: [
|
|
70
98
|
/* @__PURE__ */ jsx(NumberInput, { label: t("filterDrawer.from"), value: values[fromKey], onChange: (number) => setValue(fromKey, number), ...numberProps }),
|
|
71
99
|
/* @__PURE__ */ jsx(
|
|
72
100
|
NumberInput,
|
|
@@ -75,24 +103,36 @@ function DrawerField({ field, values, setValue, invalidRanges, t }) {
|
|
|
75
103
|
value: values[toKey],
|
|
76
104
|
onChange: (number) => setValue(toKey, number),
|
|
77
105
|
...numberProps,
|
|
78
|
-
error:
|
|
106
|
+
error: errorOf(toKey)
|
|
79
107
|
}
|
|
80
108
|
)
|
|
81
109
|
] });
|
|
82
110
|
}
|
|
83
111
|
case FILTER_FIELD_KIND.dateRange: {
|
|
84
112
|
const [fromKey, toKey] = filterKeysOf(field);
|
|
85
|
-
|
|
86
|
-
|
|
113
|
+
const bounds = dateRangeBoundsOf(field, values, today);
|
|
114
|
+
return /* @__PURE__ */ jsxs(PairedField, { label: field.label, inputId: `${fieldId}-from`, stackPairsBelow, children: [
|
|
115
|
+
/* @__PURE__ */ jsx(
|
|
116
|
+
FilterDrawerDateInput,
|
|
117
|
+
{
|
|
118
|
+
name: fromKey,
|
|
119
|
+
label: t("filterDrawer.from"),
|
|
120
|
+
value: values[fromKey],
|
|
121
|
+
onChange: (date) => setValue(fromKey, date),
|
|
122
|
+
error: errorOf(fromKey),
|
|
123
|
+
helperText: "",
|
|
124
|
+
...bounds.from
|
|
125
|
+
}
|
|
126
|
+
),
|
|
87
127
|
/* @__PURE__ */ jsx(
|
|
88
|
-
|
|
128
|
+
FilterDrawerDateInput,
|
|
89
129
|
{
|
|
90
|
-
|
|
130
|
+
name: toKey,
|
|
91
131
|
label: t("filterDrawer.to"),
|
|
92
132
|
value: values[toKey],
|
|
93
133
|
onChange: (date) => setValue(toKey, date),
|
|
94
|
-
|
|
95
|
-
|
|
134
|
+
error: errorOf(toKey),
|
|
135
|
+
...bounds.to
|
|
96
136
|
}
|
|
97
137
|
)
|
|
98
138
|
] });
|
|
@@ -101,7 +141,7 @@ function DrawerField({ field, values, setValue, invalidRanges, t }) {
|
|
|
101
141
|
const parts = comparisonOf(values[key]);
|
|
102
142
|
const operators = field.operators ?? Object.values(COMPARISON_OPERATOR);
|
|
103
143
|
const operator = parts?.operator ?? field.defaultOperator ?? operators[0];
|
|
104
|
-
return /* @__PURE__ */ jsxs(PairedField, { label: field.label, inputId: `${fieldId}-value`, template: "minmax(0, 1fr) minmax(0, 1.2fr)", children: [
|
|
144
|
+
return /* @__PURE__ */ jsxs(PairedField, { label: field.label, inputId: `${fieldId}-value`, template: "minmax(0, 1fr) minmax(0, 1.2fr)", stackPairsBelow, children: [
|
|
105
145
|
/* @__PURE__ */ jsx(
|
|
106
146
|
SelectInput,
|
|
107
147
|
{
|
|
@@ -126,8 +166,11 @@ function DrawerField({ field, values, setValue, invalidRanges, t }) {
|
|
|
126
166
|
)
|
|
127
167
|
] });
|
|
128
168
|
}
|
|
129
|
-
case FILTER_FIELD_KIND.custom:
|
|
130
|
-
|
|
169
|
+
case FILTER_FIELD_KIND.custom: {
|
|
170
|
+
const keys = filterKeysOf(field);
|
|
171
|
+
const errors = Object.fromEntries(keys.map((fieldKey) => [fieldKey, errorOf(fieldKey)]).filter(([, text]) => text));
|
|
172
|
+
return field.render({ value: values[key], onChange: (next) => setValue(key, next), values, setValue, keys, errors });
|
|
173
|
+
}
|
|
131
174
|
default:
|
|
132
175
|
return null;
|
|
133
176
|
}
|
|
@@ -144,12 +187,15 @@ function FilterDrawerContent(props) {
|
|
|
144
187
|
title,
|
|
145
188
|
applyLabel,
|
|
146
189
|
closeOnApply = true,
|
|
190
|
+
stackPairsBelow,
|
|
147
191
|
children,
|
|
148
192
|
texts,
|
|
149
193
|
...unknownProps
|
|
150
194
|
} = usePieceProps(props, THEME_NAME);
|
|
151
195
|
warnUnknownProps(THEME_NAME, unknownProps);
|
|
152
196
|
const { t } = useToolkitTranslation("common", texts);
|
|
197
|
+
const { timeZone } = useToolkitSettings();
|
|
198
|
+
const { formatDate } = useFormatters();
|
|
153
199
|
const titleId = useId();
|
|
154
200
|
const [draft, setDraft] = useState(() => drawerValuesOf(fields, values));
|
|
155
201
|
useEffect(() => {
|
|
@@ -157,8 +203,10 @@ function FilterDrawerContent(props) {
|
|
|
157
203
|
setDraft(drawerValuesOf(fields, values));
|
|
158
204
|
}
|
|
159
205
|
}, [open]);
|
|
160
|
-
const
|
|
161
|
-
const
|
|
206
|
+
const today = todayInZone(timeZone);
|
|
207
|
+
const problems = drawerProblemsOf(fields, draft, { today });
|
|
208
|
+
const hasProblems = Object.keys(problems).length > 0;
|
|
209
|
+
const errorOf = (key) => problems[key] ? t(`filterDrawer.${problems[key].problem}`, { date: formatDate(problems[key].date) }) : void 0;
|
|
162
210
|
const setValue = (key, value) => setDraft((current) => ({ ...current, [key]: value }));
|
|
163
211
|
const apply = () => {
|
|
164
212
|
onApply(draft);
|
|
@@ -166,42 +214,59 @@ function FilterDrawerContent(props) {
|
|
|
166
214
|
onClose();
|
|
167
215
|
}
|
|
168
216
|
};
|
|
169
|
-
return
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
{
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
217
|
+
return (
|
|
218
|
+
// en la capa de las ventanas (modal), no en la del menú lateral (drawer): la barra de la app va encima del menú a
|
|
219
|
+
// propósito y, en la misma capa, tapaba el título y la × del cajón (medido en Chrome sin vidrio y con el minimalista)
|
|
220
|
+
/* @__PURE__ */ jsx(
|
|
221
|
+
Drawer,
|
|
222
|
+
{
|
|
223
|
+
anchor,
|
|
224
|
+
open,
|
|
225
|
+
onClose,
|
|
226
|
+
sx: { zIndex: (theme) => theme.zIndex.modal },
|
|
227
|
+
slotProps: { paper: { "aria-labelledby": titleId, className: `${THEME_NAME}-paper`, sx: { width: { xs: "100%", sm: width }, maxWidth: "100%" } } },
|
|
228
|
+
children: /* @__PURE__ */ jsxs(
|
|
229
|
+
Box,
|
|
230
|
+
{
|
|
231
|
+
component: "form",
|
|
232
|
+
noValidate: true,
|
|
233
|
+
sx: { display: "flex", flexDirection: "column", height: "100%", ...stackPairsBelow && { containerType: "inline-size", containerName: DRAWER_CONTAINER_NAME } },
|
|
234
|
+
onSubmit: (event) => {
|
|
235
|
+
event.preventDefault();
|
|
236
|
+
if (!hasProblems) {
|
|
237
|
+
apply();
|
|
238
|
+
}
|
|
239
|
+
},
|
|
240
|
+
children: [
|
|
241
|
+
/* @__PURE__ */ jsxs(FilterDrawerHeader, { direction: "row", ownSx: { alignItems: "center", gap: 1, px: 2, py: 1.5, borderBottom: 1, borderColor: "divider" }, children: [
|
|
242
|
+
/* @__PURE__ */ jsx(IconButton, { "aria-label": t("filterDrawer.close"), onClick: onClose, edge: "start", children: /* @__PURE__ */ jsx(CloseRoundedIcon, {}) }),
|
|
243
|
+
/* @__PURE__ */ jsx(Typography, { id: titleId, variant: "h6", component: "h2", sx: { flexGrow: 1 }, children: title ?? t("filterDrawer.title") })
|
|
244
|
+
] }),
|
|
245
|
+
/* @__PURE__ */ jsxs(FilterDrawerBody, { spacing: 1, ownSx: { flex: 1, overflowY: "auto", px: 2.5, py: 2 }, children: [
|
|
246
|
+
fields.map((field) => /* @__PURE__ */ jsx(
|
|
247
|
+
DrawerField,
|
|
248
|
+
{
|
|
249
|
+
field,
|
|
250
|
+
values: draft,
|
|
251
|
+
setValue,
|
|
252
|
+
errorOf,
|
|
253
|
+
today,
|
|
254
|
+
stackPairsBelow,
|
|
255
|
+
t
|
|
256
|
+
},
|
|
257
|
+
field.name
|
|
258
|
+
)),
|
|
259
|
+
children
|
|
260
|
+
] }),
|
|
261
|
+
/* @__PURE__ */ jsxs(FilterDrawerFooter, { direction: "row", ownSx: { gap: 1, justifyContent: "flex-end", px: 2.5, py: 1.5, borderTop: 1, borderColor: "divider" }, children: [
|
|
262
|
+
/* @__PURE__ */ jsx(Button, { onClick: () => setDraft(emptyDrawerValuesOf(fields)), children: t("filterDrawer.clear") }),
|
|
263
|
+
/* @__PURE__ */ jsx(Button, { type: "submit", variant: "contained", disabled: hasProblems, children: applyLabel ?? t("filterDrawer.apply") })
|
|
264
|
+
] })
|
|
265
|
+
]
|
|
266
|
+
}
|
|
267
|
+
)
|
|
268
|
+
}
|
|
269
|
+
)
|
|
205
270
|
);
|
|
206
271
|
}
|
|
207
272
|
const FilterDrawer = withPieceSlots(FilterDrawerContent, THEME_NAME);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FilterDrawer.js","sources":["../../../src/ui/filters/FilterDrawer.jsx"],"sourcesContent":["// El cajón de búsqueda: un panel que sale de un lado con los campos que filtran una lista (las listas, un rango\n// «desde-hasta», unas fechas, un monto con su operador), para cuando los filtros ya no caben arriba de la tabla.\n//\n// Lo que se cambia adentro es un BORRADOR: la lista no se mueve hasta «Buscar» (no se consulta al servidor con cada tecla).\n// «Limpiar» vacía los campos del cajón; «Buscar» manda todos juntos (onApply) y cierra. Un rango al revés («hasta» menor que\n// «desde») se dice en su campo y no deja buscar.\n//\n// Los campos se declaran (filterDrawerFields.js dice cómo viaja cada uno); con kind 'custom' va uno propio (render). Para\n// conectarlo a ServerTable con su botón y sus chips: ServerTableFilterDrawer de /table.\n//\n// Configurable: de qué lado sale (anchor), su ancho, su título y los textos de sus botones. En el teléfono ocupa todo el\n// ancho.\n//\n// Se cambia desde el tema del proyecto, como las piezas de MUI: components.CalcitaFilterDrawer con defaultProps y\n// styleOverrides de sus partes: paper, header, body, footer.\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useEffect, useId, useState} from 'react';\nimport Box from '@mui/material/Box';\nimport Button from '@mui/material/Button';\nimport Drawer from '@mui/material/Drawer';\nimport IconButton from '@mui/material/IconButton';\nimport Stack from '@mui/material/Stack';\nimport Typography from '@mui/material/Typography';\nimport CloseRoundedIcon from '@mui/icons-material/CloseRounded';\n\nimport CatalogInput from '@/inputs/CatalogInput.jsx';\nimport CheckboxGroupInput from '@/inputs/CheckboxGroupInput.jsx';\nimport FieldFrame from '@/inputs/FieldFrame.jsx';\nimport NumberInput from '@/inputs/NumberInput.jsx';\nimport SelectInput from '@/inputs/SelectInput.jsx';\nimport TextInput from '@/inputs/TextInput.jsx';\nimport {piecePart, usePieceProps, withPieceSlots} from '@/theme/themePieces.js';\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\nimport {warnUnknownProps} from '@/toolkit/unknownProps.js';\nimport {\n COMPARISON_OPERATOR, FILTER_FIELD_KIND, comparisonOf, comparisonTextOf, drawerValuesOf, emptyDrawerValuesOf, filterKeysOf,\n invalidRangesOf,\n} from '@/ui/filters/filterDrawerFields.js';\n\n/** De qué lado sale el cajón. */\nexport const FILTER_DRAWER_ANCHOR = Object.freeze({left: 'left', right: 'right'});\nconst DEFAULT_WIDTH = 380;\n// «Cualquiera» de una lista de una sola opción: no es una opción y viaja como '' (sin filtro)\nconst ANY_OPTION = '__calcita_any__';\n\n// el nombre de la pieza en el tema y sus partes (theme.components.CalcitaFilterDrawer)\nconst THEME_NAME = 'CalcitaFilterDrawer';\nconst FilterDrawerHeader = piecePart(THEME_NAME, 'Header', Stack);\nconst FilterDrawerBody = piecePart(THEME_NAME, 'Body', Stack);\nconst FilterDrawerFooter = piecePart(THEME_NAME, 'Footer', Stack);\n\n/**\n * Dos campos lado a lado (desde y hasta, operador y número); en un cajón angosto se ven bien juntos.\n *\n * @param {{label: import('react').ReactNode, inputId: string, children: import('react').ReactNode, template?: string}} props\n */\nfunction PairedField({label, inputId, children, template = '1fr 1fr'}) {\n return (\n <FieldFrame inputId={inputId} label={label}>\n <Box sx={{display: 'grid', gridTemplateColumns: template, gap: 1.5, alignItems: 'start'}}>{children}</Box>\n </FieldFrame>\n );\n}\n\n/**\n * Un campo del cajón según su tipo, con las piezas de la casa.\n *\n * @param {object} props\n */\nfunction DrawerField({field, values, setValue, invalidRanges, t}) {\n const fieldId = useId();\n const [key] = filterKeysOf(field);\n switch (field.kind) {\n case FILTER_FIELD_KIND.text:\n return <TextInput label={field.label} value={values[key]} onChange={(text) => setValue(key, text)} placeholder={field.placeholder} />;\n case FILTER_FIELD_KIND.select:\n return (\n <SelectInput label={field.label} value={values[key] || ANY_OPTION} onChange={(chosen) => setValue(key, chosen === ANY_OPTION ? '' : chosen)}\n options={[{value: ANY_OPTION, label: t('filterDrawer.any')}, ...field.options]} />\n );\n case FILTER_FIELD_KIND.multiSelect:\n return <SelectInput label={field.label} value={values[key]} onChange={(chosen) => setValue(key, chosen)} options={field.options} multiple selectAll checkboxes />;\n case FILTER_FIELD_KIND.catalog:\n return (\n <CatalogInput label={field.label} value={values[key]} onChange={(chosen) => setValue(key, chosen)} options={field.options}\n multiple={field.multiple} selectAll={field.multiple} checkboxes={field.multiple} formatLabel={field.formatLabel}\n placeholder={field.placeholder} />\n );\n case FILTER_FIELD_KIND.checkboxes:\n return <CheckboxGroupInput label={field.label} value={values[key]} onChange={(chosen) => setValue(key, chosen)} options={field.options} selectAll={field.selectAll} />;\n case FILTER_FIELD_KIND.numberRange: {\n const [fromKey, toKey] = filterKeysOf(field);\n const numberProps = {decimals: field.decimals ?? 0, prefix: field.prefix, suffix: field.suffix, helperText: ''};\n return (\n <PairedField label={field.label} inputId={`${fieldId}-from`}>\n <NumberInput label={t('filterDrawer.from')} value={values[fromKey]} onChange={(number) => setValue(fromKey, number)} {...numberProps} />\n <NumberInput label={t('filterDrawer.to')} value={values[toKey]} onChange={(number) => setValue(toKey, number)} {...numberProps}\n error={invalidRanges[toKey] ? t('filterDrawer.rangeBackwards') : undefined} />\n </PairedField>\n );\n }\n case FILTER_FIELD_KIND.dateRange: {\n const [fromKey, toKey] = filterKeysOf(field);\n // el calendario del navegador (como DateRangeFilter): viaja AAAA-MM-DD, sin pedir MUI X\n return (\n <PairedField label={field.label} inputId={`${fieldId}-from`}>\n <TextInput type=\"date\" label={t('filterDrawer.from')} value={values[fromKey]} onChange={(date) => setValue(fromKey, date)} clearable={false} helperText=\"\" />\n <TextInput type=\"date\" label={t('filterDrawer.to')} value={values[toKey]} onChange={(date) => setValue(toKey, date)} clearable={false}\n error={invalidRanges[toKey] ? t('filterDrawer.rangeBackwards') : undefined} />\n </PairedField>\n );\n }\n case FILTER_FIELD_KIND.comparison: {\n const parts = comparisonOf(values[key]);\n const operators = field.operators ?? Object.values(COMPARISON_OPERATOR);\n const operator = parts?.operator ?? field.defaultOperator ?? operators[0];\n return (\n <PairedField label={field.label} inputId={`${fieldId}-value`} template=\"minmax(0, 1fr) minmax(0, 1.2fr)\">\n <SelectInput label={t('filterDrawer.operator')} value={operator} helperText=\"\"\n options={operators.map((option) => ({value: option, label: t(`filterDrawer.operators.${option}`)}))}\n onChange={(chosen) => setValue(key, comparisonTextOf(chosen, parts?.value ?? ''))} />\n <NumberInput label={t('filterDrawer.value')} value={parts?.value ?? ''} decimals={field.decimals ?? 2} prefix={field.prefix} suffix={field.suffix}\n helperText=\"\" onChange={(number) => setValue(key, comparisonTextOf(operator, number))} />\n </PairedField>\n );\n }\n case FILTER_FIELD_KIND.custom:\n return field.render({value: values[key], onChange: (next) => setValue(key, next), values, setValue});\n default:\n return null;\n }\n}\n\n/**\n * @param {object} props\n * @param {boolean} props.open\n * @param {() => void} props.onClose\n * @param {object[]} props.fields los campos (filterDrawerFields.js): {name, label, kind, options?, keys?, prefix?, suffix?, decimals?, operators?, render?…}\n * @param {Record<string, unknown>} props.values los filtros puestos hoy (de ahí sale el borrador al abrir)\n * @param {(values: Record<string, unknown>) => void} props.onApply todos los campos del cajón juntos, al picar «Buscar»\n * @param {'left'|'right'} [props.anchor] de qué lado sale (FILTER_DRAWER_ANCHOR); por omisión la derecha\n * @param {number} [props.width] su ancho en escritorio, en px\n * @param {string} [props.title] ya traducido; por omisión «Filtros»\n * @param {string} [props.applyLabel] ya traducido; por omisión «Buscar»\n * @param {boolean} [props.closeOnApply] por omisión sí\n * @param {import('react').ReactNode} [props.children] algo más abajo de los campos (una nota, un enlace)\n * @param {object} [props.texts] los textos que cambian, con la forma de `common` en los textos del paquete\n */\nfunction FilterDrawerContent(props) {\n const {\n open, onClose, fields, values, onApply, anchor = FILTER_DRAWER_ANCHOR.right, width = DEFAULT_WIDTH, title, applyLabel,\n closeOnApply = true, children, texts,\n ...unknownProps\n } = usePieceProps(props, THEME_NAME);\n warnUnknownProps(THEME_NAME, unknownProps);\n const {t} = useToolkitTranslation('common', texts);\n const titleId = useId();\n const [draft, setDraft] = useState(() => drawerValuesOf(fields, values));\n // cada vez que se abre, el borrador empieza de lo que hay puesto (lo de la vez pasada sin buscar no se queda)\n useEffect(() => {\n if (open) {\n setDraft(drawerValuesOf(fields, values));\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [open]);\n const invalidRanges = invalidRangesOf(fields, draft);\n const hasInvalidRange = Object.keys(invalidRanges).length > 0;\n const setValue = (key, value) => setDraft((current) => ({...current, [key]: value}));\n const apply = () => {\n onApply(draft);\n if (closeOnApply) {\n onClose();\n }\n };\n\n return (\n <Drawer\n anchor={anchor}\n open={open}\n onClose={onClose}\n slotProps={{paper: {'aria-labelledby': titleId, className: `${THEME_NAME}-paper`, sx: {width: {xs: '100%', sm: width}, maxWidth: '100%'}}}}\n >\n <Box component=\"form\" noValidate sx={{display: 'flex', flexDirection: 'column', height: '100%'}}\n onSubmit={(event) => {\n event.preventDefault();\n if (!hasInvalidRange) {\n apply();\n }\n }}>\n <FilterDrawerHeader direction=\"row\" ownSx={{alignItems: 'center', gap: 1, px: 2, py: 1.5, borderBottom: 1, borderColor: 'divider'}}>\n <IconButton aria-label={t('filterDrawer.close')} onClick={onClose} edge=\"start\"><CloseRoundedIcon /></IconButton>\n <Typography id={titleId} variant=\"h6\" component=\"h2\" sx={{flexGrow: 1}}>{title ?? t('filterDrawer.title')}</Typography>\n </FilterDrawerHeader>\n <FilterDrawerBody spacing={1} ownSx={{flex: 1, overflowY: 'auto', px: 2.5, py: 2}}>\n {fields.map((field) => (\n <DrawerField key={field.name} field={field} values={draft} setValue={setValue} invalidRanges={invalidRanges} t={t} />\n ))}\n {children}\n </FilterDrawerBody>\n <FilterDrawerFooter direction=\"row\" ownSx={{gap: 1, justifyContent: 'flex-end', px: 2.5, py: 1.5, borderTop: 1, borderColor: 'divider'}}>\n <Button onClick={() => setDraft(emptyDrawerValuesOf(fields))}>{t('filterDrawer.clear')}</Button>\n <Button type=\"submit\" variant=\"contained\" disabled={hasInvalidRange}>{applyLabel ?? t('filterDrawer.apply')}</Button>\n </FilterDrawerFooter>\n </Box>\n </Drawer>\n );\n}\n\n// lo que se exporta: la pieza con slots y slotProps para cambiar sus partes (themePieces.js)\nconst FilterDrawer = withPieceSlots(FilterDrawerContent, THEME_NAME);\nexport default FilterDrawer;\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;AAyCO,MAAM,uBAAuB,OAAO,OAAO,EAAC,MAAM,QAAQ,OAAO,SAAQ;AAChF,MAAM,gBAAgB;AAEtB,MAAM,aAAa;AAGnB,MAAM,aAAa;AACnB,MAAM,qBAAqB,UAAU,YAAY,UAAU,KAAK;AAChE,MAAM,mBAAmB,UAAU,YAAY,QAAQ,KAAK;AAC5D,MAAM,qBAAqB,UAAU,YAAY,UAAU,KAAK;AAOhE,SAAS,YAAY,EAAC,OAAO,SAAS,UAAU,WAAW,aAAY;AACnE,6BACK,YAAA,EAAW,SAAkB,OAC1B,UAAA,oBAAC,KAAA,EAAI,IAAI,EAAC,SAAS,QAAQ,qBAAqB,UAAU,KAAK,KAAK,YAAY,QAAA,GAAW,UAAS,GACxG;AAER;AAOA,SAAS,YAAY,EAAC,OAAO,QAAQ,UAAU,eAAe,KAAI;AAC9D,QAAM,UAAU,MAAA;AAChB,QAAM,CAAC,GAAG,IAAI,aAAa,KAAK;AAChC,UAAQ,MAAM,MAAA;AAAA,IACV,KAAK,kBAAkB;AACnB,iCAAQ,WAAA,EAAU,OAAO,MAAM,OAAO,OAAO,OAAO,GAAG,GAAG,UAAU,CAAC,SAAS,SAAS,KAAK,IAAI,GAAG,aAAa,MAAM,aAAa;AAAA,IACvI,KAAK,kBAAkB;AACnB,aACI;AAAA,QAAC;AAAA,QAAA;AAAA,UAAY,OAAO,MAAM;AAAA,UAAO,OAAO,OAAO,GAAG,KAAK;AAAA,UAAY,UAAU,CAAC,WAAW,SAAS,KAAK,WAAW,aAAa,KAAK,MAAM;AAAA,UAC7H,SAAS,CAAC,EAAC,OAAO,YAAY,OAAO,EAAE,kBAAkB,EAAA,GAAI,GAAG,MAAM,OAAO;AAAA,QAAA;AAAA,MAAA;AAAA,IAElG,KAAK,kBAAkB;AACnB,aAAO,oBAAC,aAAA,EAAY,OAAO,MAAM,OAAO,OAAO,OAAO,GAAG,GAAG,UAAU,CAAC,WAAW,SAAS,KAAK,MAAM,GAAG,SAAS,MAAM,SAAS,UAAQ,MAAC,WAAS,MAAC,YAAU,KAAA,CAAC;AAAA,IACnK,KAAK,kBAAkB;AACnB,aACI;AAAA,QAAC;AAAA,QAAA;AAAA,UAAa,OAAO,MAAM;AAAA,UAAO,OAAO,OAAO,GAAG;AAAA,UAAG,UAAU,CAAC,WAAW,SAAS,KAAK,MAAM;AAAA,UAAG,SAAS,MAAM;AAAA,UACpG,UAAU,MAAM;AAAA,UAAU,WAAW,MAAM;AAAA,UAAU,YAAY,MAAM;AAAA,UAAU,aAAa,MAAM;AAAA,UACpG,aAAa,MAAM;AAAA,QAAA;AAAA,MAAA;AAAA,IAEzC,KAAK,kBAAkB;AACnB,aAAO,oBAAC,sBAAmB,OAAO,MAAM,OAAO,OAAO,OAAO,GAAG,GAAG,UAAU,CAAC,WAAW,SAAS,KAAK,MAAM,GAAG,SAAS,MAAM,SAAS,WAAW,MAAM,WAAW;AAAA,IACxK,KAAK,kBAAkB,aAAa;AAChC,YAAM,CAAC,SAAS,KAAK,IAAI,aAAa,KAAK;AAC3C,YAAM,cAAc,EAAC,UAAU,MAAM,YAAY,GAAG,QAAQ,MAAM,QAAQ,QAAQ,MAAM,QAAQ,YAAY,GAAA;AAC5G,aACI,qBAAC,eAAY,OAAO,MAAM,OAAO,SAAS,GAAG,OAAO,SAChD,UAAA;AAAA,QAAA,oBAAC,eAAY,OAAO,EAAE,mBAAmB,GAAG,OAAO,OAAO,OAAO,GAAG,UAAU,CAAC,WAAW,SAAS,SAAS,MAAM,GAAI,GAAG,aAAa;AAAA,QACtI;AAAA,UAAC;AAAA,UAAA;AAAA,YAAY,OAAO,EAAE,iBAAiB;AAAA,YAAG,OAAO,OAAO,KAAK;AAAA,YAAG,UAAU,CAAC,WAAW,SAAS,OAAO,MAAM;AAAA,YAAI,GAAG;AAAA,YACtG,OAAO,cAAc,KAAK,IAAI,EAAE,6BAA6B,IAAI;AAAA,UAAA;AAAA,QAAA;AAAA,MAAW,GAC7F;AAAA,IAER;AAAA,IACA,KAAK,kBAAkB,WAAW;AAC9B,YAAM,CAAC,SAAS,KAAK,IAAI,aAAa,KAAK;AAE3C,aACI,qBAAC,eAAY,OAAO,MAAM,OAAO,SAAS,GAAG,OAAO,SAChD,UAAA;AAAA,QAAA,oBAAC,WAAA,EAAU,MAAK,QAAO,OAAO,EAAE,mBAAmB,GAAG,OAAO,OAAO,OAAO,GAAG,UAAU,CAAC,SAAS,SAAS,SAAS,IAAI,GAAG,WAAW,OAAO,YAAW,IAAG;AAAA,QAC3J;AAAA,UAAC;AAAA,UAAA;AAAA,YAAU,MAAK;AAAA,YAAO,OAAO,EAAE,iBAAiB;AAAA,YAAG,OAAO,OAAO,KAAK;AAAA,YAAG,UAAU,CAAC,SAAS,SAAS,OAAO,IAAI;AAAA,YAAG,WAAW;AAAA,YACrH,OAAO,cAAc,KAAK,IAAI,EAAE,6BAA6B,IAAI;AAAA,UAAA;AAAA,QAAA;AAAA,MAAW,GAC3F;AAAA,IAER;AAAA,IACA,KAAK,kBAAkB,YAAY;AAC/B,YAAM,QAAQ,aAAa,OAAO,GAAG,CAAC;AACtC,YAAM,YAAY,MAAM,aAAa,OAAO,OAAO,mBAAmB;AACtE,YAAM,WAAW,OAAO,YAAY,MAAM,mBAAmB,UAAU,CAAC;AACxE,aACI,qBAAC,aAAA,EAAY,OAAO,MAAM,OAAO,SAAS,GAAG,OAAO,UAAU,UAAS,mCACnE,UAAA;AAAA,QAAA;AAAA,UAAC;AAAA,UAAA;AAAA,YAAY,OAAO,EAAE,uBAAuB;AAAA,YAAG,OAAO;AAAA,YAAU,YAAW;AAAA,YAC/D,SAAS,UAAU,IAAI,CAAC,YAAY,EAAC,OAAO,QAAQ,OAAO,EAAE,0BAA0B,MAAM,EAAE,IAAG;AAAA,YAClG,UAAU,CAAC,WAAW,SAAS,KAAK,iBAAiB,QAAQ,OAAO,SAAS,EAAE,CAAC;AAAA,UAAA;AAAA,QAAA;AAAA,QAC7F;AAAA,UAAC;AAAA,UAAA;AAAA,YAAY,OAAO,EAAE,oBAAoB;AAAA,YAAG,OAAO,OAAO,SAAS;AAAA,YAAI,UAAU,MAAM,YAAY;AAAA,YAAG,QAAQ,MAAM;AAAA,YAAQ,QAAQ,MAAM;AAAA,YAC9H,YAAW;AAAA,YAAG,UAAU,CAAC,WAAW,SAAS,KAAK,iBAAiB,UAAU,MAAM,CAAC;AAAA,UAAA;AAAA,QAAA;AAAA,MAAG,GACxG;AAAA,IAER;AAAA,IACA,KAAK,kBAAkB;AACnB,aAAO,MAAM,OAAO,EAAC,OAAO,OAAO,GAAG,GAAG,UAAU,CAAC,SAAS,SAAS,KAAK,IAAI,GAAG,QAAQ,UAAS;AAAA,IACvG;AACI,aAAO;AAAA,EAAA;AAEnB;AAiBA,SAAS,oBAAoB,OAAO;AAChC,QAAM;AAAA,IACF;AAAA,IAAM;AAAA,IAAS;AAAA,IAAQ;AAAA,IAAQ;AAAA,IAAS,SAAS,qBAAqB;AAAA,IAAO,QAAQ;AAAA,IAAe;AAAA,IAAO;AAAA,IAC3G,eAAe;AAAA,IAAM;AAAA,IAAU;AAAA,IAC/B,GAAG;AAAA,EAAA,IACH,cAAc,OAAO,UAAU;AACnC,mBAAiB,YAAY,YAAY;AACzC,QAAM,EAAC,EAAA,IAAK,sBAAsB,UAAU,KAAK;AACjD,QAAM,UAAU,MAAA;AAChB,QAAM,CAAC,OAAO,QAAQ,IAAI,SAAS,MAAM,eAAe,QAAQ,MAAM,CAAC;AAEvE,YAAU,MAAM;AACZ,QAAI,MAAM;AACN,eAAS,eAAe,QAAQ,MAAM,CAAC;AAAA,IAC3C;AAAA,EAEJ,GAAG,CAAC,IAAI,CAAC;AACT,QAAM,gBAAgB,gBAAgB,QAAQ,KAAK;AACnD,QAAM,kBAAkB,OAAO,KAAK,aAAa,EAAE,SAAS;AAC5D,QAAM,WAAW,CAAC,KAAK,UAAU,SAAS,CAAC,aAAa,EAAC,GAAG,SAAS,CAAC,GAAG,GAAG,QAAO;AACnF,QAAM,QAAQ,MAAM;AAChB,YAAQ,KAAK;AACb,QAAI,cAAc;AACd,cAAA;AAAA,IACJ;AAAA,EACJ;AAEA,SACI;AAAA,IAAC;AAAA,IAAA;AAAA,MACG;AAAA,MACA;AAAA,MACA;AAAA,MACA,WAAW,EAAC,OAAO,EAAC,mBAAmB,SAAS,WAAW,GAAG,UAAU,UAAU,IAAI,EAAC,OAAO,EAAC,IAAI,QAAQ,IAAI,SAAQ,UAAU,OAAA,IAAO;AAAA,MAExI,UAAA;AAAA,QAAC;AAAA,QAAA;AAAA,UAAI,WAAU;AAAA,UAAO,YAAU;AAAA,UAAC,IAAI,EAAC,SAAS,QAAQ,eAAe,UAAU,QAAQ,OAAA;AAAA,UACnF,UAAU,CAAC,UAAU;AACjB,kBAAM,eAAA;AACN,gBAAI,CAAC,iBAAiB;AAClB,oBAAA;AAAA,YACJ;AAAA,UACJ;AAAA,UACD,UAAA;AAAA,YAAA,qBAAC,sBAAmB,WAAU,OAAM,OAAO,EAAC,YAAY,UAAU,KAAK,GAAG,IAAI,GAAG,IAAI,KAAK,cAAc,GAAG,aAAa,aACpH,UAAA;AAAA,cAAA,oBAAC,YAAA,EAAW,cAAY,EAAE,oBAAoB,GAAG,SAAS,SAAS,MAAK,SAAQ,UAAA,oBAAC,kBAAA,CAAA,CAAiB,GAAE;AAAA,kCACnG,YAAA,EAAW,IAAI,SAAS,SAAQ,MAAK,WAAU,MAAK,IAAI,EAAC,UAAU,EAAA,GAAK,UAAA,SAAS,EAAE,oBAAoB,EAAA,CAAE;AAAA,YAAA,GAC9G;AAAA,YACA,qBAAC,kBAAA,EAAiB,SAAS,GAAG,OAAO,EAAC,MAAM,GAAG,WAAW,QAAQ,IAAI,KAAK,IAAI,KAC1E,UAAA;AAAA,cAAA,OAAO,IAAI,CAAC,UACT,oBAAC,aAAA,EAA6B,OAAc,QAAQ,OAAO,UAAoB,eAA8B,EAAA,GAA3F,MAAM,IAA2F,CACtH;AAAA,cACA;AAAA,YAAA,GACL;AAAA,iCACC,oBAAA,EAAmB,WAAU,OAAM,OAAO,EAAC,KAAK,GAAG,gBAAgB,YAAY,IAAI,KAAK,IAAI,KAAK,WAAW,GAAG,aAAa,aACzH,UAAA;AAAA,cAAA,oBAAC,QAAA,EAAO,SAAS,MAAM,SAAS,oBAAoB,MAAM,CAAC,GAAI,UAAA,EAAE,oBAAoB,EAAA,CAAE;AAAA,cACvF,oBAAC,QAAA,EAAO,MAAK,UAAS,SAAQ,aAAY,UAAU,iBAAkB,UAAA,cAAc,EAAE,oBAAoB,EAAA,CAAE;AAAA,YAAA,EAAA,CAChH;AAAA,UAAA;AAAA,QAAA;AAAA,MAAA;AAAA,IACJ;AAAA,EAAA;AAGZ;AAGA,MAAM,eAAe,eAAe,qBAAqB,UAAU;"}
|
|
1
|
+
{"version":3,"file":"FilterDrawer.js","sources":["../../../src/ui/filters/FilterDrawer.jsx"],"sourcesContent":["// El cajón de búsqueda: un panel que sale de un lado con los campos que filtran una lista (las listas, un rango\n// «desde-hasta», unas fechas, un monto con su operador), para cuando los filtros ya no caben arriba de la tabla.\n//\n// Lo que se cambia adentro es un BORRADOR: la lista no se mueve hasta «Buscar» (no se consulta al servidor con cada tecla).\n// «Limpiar» vacía los campos del cajón; «Buscar» manda todos juntos (onApply) y cierra. Un rango al revés («hasta» menor que\n// «desde») o una fecha fuera de sus topes se dice en su campo y no deja buscar; nada se borra solo.\n//\n// Los campos se declaran (filterDrawerFields.js dice cómo viaja cada uno); con kind 'custom' va uno propio (render), o un\n// rango propio con sus dos llaves (keys). Para conectarlo a ServerTable con su botón y sus chips: ServerTableFilterDrawer de\n// /table.\n//\n// LAS FECHAS: por omisión, el calendario del navegador (así el cajón no obliga a instalar MUI X). Una app que ya trae\n// /date-pickers pone el suyo para que el cajón y sus formularios se vean igual: slots={{dateInput: DateInput}} en el cajón,\n// o una vez en el tema para todas las listas (components.CalcitaFilterDrawer.defaultProps.slots). El campo recibe name,\n// label, value, onChange, error, helperText, minDate y maxDate: lo mismo que DateInput.\n//\n// Configurable: de qué lado sale (anchor), su ancho, su título, los textos de sus botones y si los pares («desde» y\n// «hasta», operador y número) se ponen uno arriba del otro cuando el cajón es angosto (stackPairsBelow). En el teléfono\n// ocupa todo el ancho.\n//\n// Se cambia desde el tema del proyecto, como las piezas de MUI: components.CalcitaFilterDrawer con defaultProps y\n// styleOverrides de sus partes: paper, header, body, footer y dateInput.\n//\n// Genérico del paquete: function (regla de la casa).\nimport {useEffect, useId, useState} from 'react';\nimport Box from '@mui/material/Box';\nimport Button from '@mui/material/Button';\nimport Drawer from '@mui/material/Drawer';\nimport IconButton from '@mui/material/IconButton';\nimport Stack from '@mui/material/Stack';\nimport Typography from '@mui/material/Typography';\nimport CloseRoundedIcon from '@mui/icons-material/CloseRounded';\n\nimport CatalogInput from '@/inputs/CatalogInput.jsx';\nimport CheckboxGroupInput from '@/inputs/CheckboxGroupInput.jsx';\nimport FieldFrame from '@/inputs/FieldFrame.jsx';\nimport NumberInput from '@/inputs/NumberInput.jsx';\nimport SelectInput from '@/inputs/SelectInput.jsx';\nimport TextInput from '@/inputs/TextInput.jsx';\nimport {piecePart, usePieceProps, withPieceSlots} from '@/theme/themePieces.js';\nimport {useToolkitSettings} from '@/toolkit/toolkitContext.js';\nimport {useFormatters} from '@/toolkit/useFormatters.js';\nimport {useToolkitTranslation} from '@/toolkit/useToolkitTranslation.js';\nimport {warnUnknownProps} from '@/toolkit/unknownProps.js';\nimport {\n COMPARISON_OPERATOR, FILTER_FIELD_KIND, comparisonOf, comparisonTextOf, dateRangeBoundsOf, drawerProblemsOf, drawerValuesOf,\n emptyDrawerValuesOf, filterKeysOf,\n} from '@/ui/filters/filterDrawerFields.js';\nimport {todayInZone} from '@/ui/metrics/heatmapScale.js';\n\n/** De qué lado sale el cajón. */\nexport const FILTER_DRAWER_ANCHOR = Object.freeze({left: 'left', right: 'right'});\nconst DEFAULT_WIDTH = 380;\n// «Cualquiera» de una lista de una sola opción: no es una opción y viaja como '' (sin filtro)\nconst ANY_OPTION = '__calcita_any__';\n// el nombre del cajón para medir su ancho (stackPairsBelow): los pares se apilan con una regla de CSS sobre el cajón, no\n// sobre la pantalla (en escritorio el cajón es angosto aunque la pantalla no)\nconst DRAWER_CONTAINER_NAME = 'calcita-filter-drawer';\n\n/**\n * El campo de fecha de fábrica: el calendario del navegador, sin pedir MUI X; viaja AAAA-MM-DD. Recibe lo mismo que DateInput\n * de /date-pickers (name, label, value, onChange, error, helperText, minDate, maxDate), así que se cambia por ese con\n * slots={{dateInput: DateInput}}.\n *\n * @param {object} props\n */\nfunction BrowserDateInput({name, label, value, onChange, error, helperText, minDate, maxDate, className}) {\n return (\n <TextInput type=\"date\" name={name} label={label} value={value} onChange={onChange} error={error} helperText={helperText} clearable={false}\n className={className} slotProps={{htmlInput: {min: minDate, max: maxDate}}} />\n );\n}\n\n// el nombre de la pieza en el tema y sus partes (theme.components.CalcitaFilterDrawer)\nconst THEME_NAME = 'CalcitaFilterDrawer';\nconst FilterDrawerHeader = piecePart(THEME_NAME, 'Header', Stack);\nconst FilterDrawerBody = piecePart(THEME_NAME, 'Body', Stack);\nconst FilterDrawerFooter = piecePart(THEME_NAME, 'Footer', Stack);\nconst FilterDrawerDateInput = piecePart(THEME_NAME, 'DateInput', BrowserDateInput);\n\n/**\n * Dos campos lado a lado (desde y hasta, operador y número). Con stackPairsBelow, si el cajón mide menos, uno arriba del\n * otro: con un calendario de MUI X, en un cajón angosto cada fecha quedaba muy apretada.\n *\n * OJO, trampa pagada: las columnas van con minmax(0, …). Con '1fr 1fr' una columna no baja del ancho mínimo de su campo, y\n * el calendario de MUI X no se encoge: empujaba la columna y el «hasta» se salía del cajón (medido en Chrome, 460 px).\n *\n * @param {{label: import('react').ReactNode, inputId: string, children: import('react').ReactNode, template?: string, stackPairsBelow?: number}} props\n */\nfunction PairedField({label, inputId, children, template = 'minmax(0, 1fr) minmax(0, 1fr)', stackPairsBelow}) {\n return (\n <FieldFrame inputId={inputId} label={label}>\n <Box sx={{\n display: 'grid', gridTemplateColumns: template, gap: 1.5, alignItems: 'start',\n ...(stackPairsBelow && {[`@container ${DRAWER_CONTAINER_NAME} (width < ${stackPairsBelow}px)`]: {gridTemplateColumns: 'minmax(0, 1fr)'}}),\n }}>\n {children}\n </Box>\n </FieldFrame>\n );\n}\n\n/**\n * Un campo del cajón según su tipo, con las piezas de la casa.\n *\n * @param {object} props\n */\nfunction DrawerField({field, values, setValue, errorOf, today, stackPairsBelow, t}) {\n const fieldId = useId();\n const [key] = filterKeysOf(field);\n switch (field.kind) {\n case FILTER_FIELD_KIND.text:\n return <TextInput label={field.label} value={values[key]} onChange={(text) => setValue(key, text)} placeholder={field.placeholder} />;\n case FILTER_FIELD_KIND.select:\n return (\n <SelectInput label={field.label} value={values[key] || ANY_OPTION} onChange={(chosen) => setValue(key, chosen === ANY_OPTION ? '' : chosen)}\n options={[{value: ANY_OPTION, label: t('filterDrawer.any')}, ...field.options]} />\n );\n case FILTER_FIELD_KIND.multiSelect:\n return <SelectInput label={field.label} value={values[key]} onChange={(chosen) => setValue(key, chosen)} options={field.options} multiple selectAll checkboxes />;\n case FILTER_FIELD_KIND.catalog:\n return (\n <CatalogInput label={field.label} value={values[key]} onChange={(chosen) => setValue(key, chosen)} options={field.options}\n multiple={field.multiple} selectAll={field.multiple} checkboxes={field.multiple} formatLabel={field.formatLabel}\n placeholder={field.placeholder} />\n );\n case FILTER_FIELD_KIND.checkboxes:\n return <CheckboxGroupInput label={field.label} value={values[key]} onChange={(chosen) => setValue(key, chosen)} options={field.options} selectAll={field.selectAll} />;\n case FILTER_FIELD_KIND.numberRange: {\n const [fromKey, toKey] = filterKeysOf(field);\n const numberProps = {decimals: field.decimals ?? 0, prefix: field.prefix, suffix: field.suffix, helperText: ''};\n return (\n <PairedField label={field.label} inputId={`${fieldId}-from`} stackPairsBelow={stackPairsBelow}>\n <NumberInput label={t('filterDrawer.from')} value={values[fromKey]} onChange={(number) => setValue(fromKey, number)} {...numberProps} />\n <NumberInput label={t('filterDrawer.to')} value={values[toKey]} onChange={(number) => setValue(toKey, number)} {...numberProps}\n error={errorOf(toKey)} />\n </PairedField>\n );\n }\n case FILTER_FIELD_KIND.dateRange: {\n const [fromKey, toKey] = filterKeysOf(field);\n // los topes del campo y cada extremo acota al otro: el calendario no deja escoger fuera ni un rango al revés\n const bounds = dateRangeBoundsOf(field, values, today);\n // el «hasta» sin helperText: deja su renglón vacío debajo del par, como siempre\n return (\n <PairedField label={field.label} inputId={`${fieldId}-from`} stackPairsBelow={stackPairsBelow}>\n <FilterDrawerDateInput name={fromKey} label={t('filterDrawer.from')} value={values[fromKey]} onChange={(date) => setValue(fromKey, date)}\n error={errorOf(fromKey)} helperText=\"\" {...bounds.from} />\n <FilterDrawerDateInput name={toKey} label={t('filterDrawer.to')} value={values[toKey]} onChange={(date) => setValue(toKey, date)}\n error={errorOf(toKey)} {...bounds.to} />\n </PairedField>\n );\n }\n case FILTER_FIELD_KIND.comparison: {\n const parts = comparisonOf(values[key]);\n const operators = field.operators ?? Object.values(COMPARISON_OPERATOR);\n const operator = parts?.operator ?? field.defaultOperator ?? operators[0];\n return (\n <PairedField label={field.label} inputId={`${fieldId}-value`} template=\"minmax(0, 1fr) minmax(0, 1.2fr)\" stackPairsBelow={stackPairsBelow}>\n <SelectInput label={t('filterDrawer.operator')} value={operator} helperText=\"\"\n options={operators.map((option) => ({value: option, label: t(`filterDrawer.operators.${option}`)}))}\n onChange={(chosen) => setValue(key, comparisonTextOf(chosen, parts?.value ?? ''))} />\n <NumberInput label={t('filterDrawer.value')} value={parts?.value ?? ''} decimals={field.decimals ?? 2} prefix={field.prefix} suffix={field.suffix}\n helperText=\"\" onChange={(number) => setValue(key, comparisonTextOf(operator, number))} />\n </PairedField>\n );\n }\n case FILTER_FIELD_KIND.custom: {\n // el contrato del campo propio: su valor, todo el borrador (values y setValue: un rango propio acota un extremo con\n // el otro o escribe en la otra llave), sus llaves y lo que esté mal en cada una, ya dicho (errors)\n const keys = filterKeysOf(field);\n const errors = Object.fromEntries(keys.map((fieldKey) => [fieldKey, errorOf(fieldKey)]).filter(([, text]) => text));\n return field.render({value: values[key], onChange: (next) => setValue(key, next), values, setValue, keys, errors});\n }\n default:\n return null;\n }\n}\n\n/**\n * @param {object} props\n * @param {boolean} props.open\n * @param {() => void} props.onClose\n * @param {object[]} props.fields los campos (filterDrawerFields.js): {name, label, kind, options?, keys?, prefix?, suffix?, decimals?, operators?,\n * minDate?, maxDate?, disablePast?, disableFuture?, render?, isBackwards?…}\n * @param {Record<string, unknown>} props.values los filtros puestos hoy (de ahí sale el borrador al abrir)\n * @param {(values: Record<string, unknown>) => void} props.onApply todos los campos del cajón juntos, al picar «Buscar»\n * @param {'left'|'right'} [props.anchor] de qué lado sale (FILTER_DRAWER_ANCHOR); por omisión la derecha\n * @param {number} [props.width] su ancho en escritorio, en px\n * @param {string} [props.title] ya traducido; por omisión «Filtros»\n * @param {string} [props.applyLabel] ya traducido; por omisión «Buscar»\n * @param {boolean} [props.closeOnApply] por omisión sí\n * @param {number} [props.stackPairsBelow] en px: si el cajón mide menos, cada par («desde» y «hasta», operador y número) va uno\n * arriba del otro (también en el teléfono, donde ocupa todo el ancho). Sin la opción, lado a lado como siempre\n * @param {import('react').ReactNode} [props.children] algo más abajo de los campos (una nota, un enlace)\n * @param {object} [props.texts] los textos que cambian, con la forma de `common` en los textos del paquete\n */\nfunction FilterDrawerContent(props) {\n const {\n open, onClose, fields, values, onApply, anchor = FILTER_DRAWER_ANCHOR.right, width = DEFAULT_WIDTH, title, applyLabel,\n closeOnApply = true, stackPairsBelow, children, texts,\n ...unknownProps\n } = usePieceProps(props, THEME_NAME);\n warnUnknownProps(THEME_NAME, unknownProps);\n const {t} = useToolkitTranslation('common', texts);\n const {timeZone} = useToolkitSettings();\n const {formatDate} = useFormatters();\n const titleId = useId();\n const [draft, setDraft] = useState(() => drawerValuesOf(fields, values));\n // cada vez que se abre, el borrador empieza de lo que hay puesto (lo de la vez pasada sin buscar no se queda)\n useEffect(() => {\n if (open) {\n setDraft(drawerValuesOf(fields, values));\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [open]);\n // hoy en la zona del producto: el tope de disableFuture y disablePast\n const today = todayInZone(timeZone);\n const problems = drawerProblemsOf(fields, draft, {today});\n const hasProblems = Object.keys(problems).length > 0;\n // lo que está mal en una llave, ya dicho (con la fecha del tope que se pasó)\n const errorOf = (key) => (problems[key] ? t(`filterDrawer.${problems[key].problem}`, {date: formatDate(problems[key].date)}) : undefined);\n const setValue = (key, value) => setDraft((current) => ({...current, [key]: value}));\n const apply = () => {\n onApply(draft);\n if (closeOnApply) {\n onClose();\n }\n };\n\n return (\n // en la capa de las ventanas (modal), no en la del menú lateral (drawer): la barra de la app va encima del menú a\n // propósito y, en la misma capa, tapaba el título y la × del cajón (medido en Chrome sin vidrio y con el minimalista)\n <Drawer\n anchor={anchor}\n open={open}\n onClose={onClose}\n sx={{zIndex: (theme) => theme.zIndex.modal}}\n slotProps={{paper: {'aria-labelledby': titleId, className: `${THEME_NAME}-paper`, sx: {width: {xs: '100%', sm: width}, maxWidth: '100%'}}}}\n >\n <Box component=\"form\" noValidate\n sx={{display: 'flex', flexDirection: 'column', height: '100%', ...(stackPairsBelow && {containerType: 'inline-size', containerName: DRAWER_CONTAINER_NAME})}}\n onSubmit={(event) => {\n event.preventDefault();\n if (!hasProblems) {\n apply();\n }\n }}>\n <FilterDrawerHeader direction=\"row\" ownSx={{alignItems: 'center', gap: 1, px: 2, py: 1.5, borderBottom: 1, borderColor: 'divider'}}>\n <IconButton aria-label={t('filterDrawer.close')} onClick={onClose} edge=\"start\"><CloseRoundedIcon /></IconButton>\n <Typography id={titleId} variant=\"h6\" component=\"h2\" sx={{flexGrow: 1}}>{title ?? t('filterDrawer.title')}</Typography>\n </FilterDrawerHeader>\n <FilterDrawerBody spacing={1} ownSx={{flex: 1, overflowY: 'auto', px: 2.5, py: 2}}>\n {fields.map((field) => (\n <DrawerField key={field.name} field={field} values={draft} setValue={setValue} errorOf={errorOf} today={today}\n stackPairsBelow={stackPairsBelow} t={t} />\n ))}\n {children}\n </FilterDrawerBody>\n <FilterDrawerFooter direction=\"row\" ownSx={{gap: 1, justifyContent: 'flex-end', px: 2.5, py: 1.5, borderTop: 1, borderColor: 'divider'}}>\n <Button onClick={() => setDraft(emptyDrawerValuesOf(fields))}>{t('filterDrawer.clear')}</Button>\n <Button type=\"submit\" variant=\"contained\" disabled={hasProblems}>{applyLabel ?? t('filterDrawer.apply')}</Button>\n </FilterDrawerFooter>\n </Box>\n </Drawer>\n );\n}\n\n// lo que se exporta: la pieza con slots y slotProps para cambiar sus partes (themePieces.js)\nconst FilterDrawer = withPieceSlots(FilterDrawerContent, THEME_NAME);\nexport default FilterDrawer;\n"],"names":[],"mappings":"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"}
|
|
@@ -9,6 +9,7 @@ const FILTER_FIELD_KIND = Object.freeze({
|
|
|
9
9
|
comparison: "comparison",
|
|
10
10
|
custom: "custom"
|
|
11
11
|
});
|
|
12
|
+
const FILTER_FIELD_PROBLEM = Object.freeze({ rangeBackwards: "rangeBackwards", beforeMinDate: "dateBeforeMin", afterMaxDate: "dateAfterMax" });
|
|
12
13
|
const COMPARISON_OPERATOR = Object.freeze({ equals: "eq", greaterThan: "gt", greaterOrEqual: "gte", lessThan: "lt", lessOrEqual: "lte" });
|
|
13
14
|
const OPERATOR_SYMBOLS = Object.freeze({ eq: "=", gt: ">", gte: "≥", lt: "<", lte: "≤" });
|
|
14
15
|
const COMPARISON_SEPARATOR = ":";
|
|
@@ -16,6 +17,12 @@ const LIST_KINDS = /* @__PURE__ */ new Set([FILTER_FIELD_KIND.multiSelect, FILTE
|
|
|
16
17
|
function isListField(field) {
|
|
17
18
|
return LIST_KINDS.has(field.kind) || field.kind === FILTER_FIELD_KIND.catalog && Boolean(field.multiple);
|
|
18
19
|
}
|
|
20
|
+
function isCustomRange(field) {
|
|
21
|
+
return field.kind === FILTER_FIELD_KIND.custom && Boolean(field.keys?.from) && Boolean(field.keys?.to);
|
|
22
|
+
}
|
|
23
|
+
function isRangeField(field) {
|
|
24
|
+
return field.kind === FILTER_FIELD_KIND.numberRange || field.kind === FILTER_FIELD_KIND.dateRange || isCustomRange(field);
|
|
25
|
+
}
|
|
19
26
|
function filterKeysOf(field) {
|
|
20
27
|
if (field.kind === FILTER_FIELD_KIND.numberRange) {
|
|
21
28
|
return [field.keys?.from ?? `${field.name}_min`, field.keys?.to ?? `${field.name}_max`];
|
|
@@ -23,6 +30,9 @@ function filterKeysOf(field) {
|
|
|
23
30
|
if (field.kind === FILTER_FIELD_KIND.dateRange) {
|
|
24
31
|
return [field.keys?.from ?? `${field.name}_from`, field.keys?.to ?? `${field.name}_to`];
|
|
25
32
|
}
|
|
33
|
+
if (isCustomRange(field)) {
|
|
34
|
+
return [field.keys.from, field.keys.to];
|
|
35
|
+
}
|
|
26
36
|
return [field.name];
|
|
27
37
|
}
|
|
28
38
|
function comparisonOf(text) {
|
|
@@ -51,25 +61,61 @@ function filterDrawerDefaultsOf(fields) {
|
|
|
51
61
|
function activeDrawerFilterCountOf(fields, filters) {
|
|
52
62
|
return fields.filter((field) => filterKeysOf(field).some((key) => isFilterValueSet(filters?.[key]))).length;
|
|
53
63
|
}
|
|
64
|
+
function isBackwardsRange(field, from, to) {
|
|
65
|
+
if (field.kind === FILTER_FIELD_KIND.numberRange) {
|
|
66
|
+
return Number(to) < Number(from);
|
|
67
|
+
}
|
|
68
|
+
if (field.kind === FILTER_FIELD_KIND.dateRange) {
|
|
69
|
+
return String(to) < String(from);
|
|
70
|
+
}
|
|
71
|
+
return Boolean(field.isBackwards?.(from, to));
|
|
72
|
+
}
|
|
54
73
|
function invalidRangesOf(fields, values) {
|
|
55
74
|
const invalid = {};
|
|
56
|
-
for (const field of fields) {
|
|
57
|
-
if (field.kind !== FILTER_FIELD_KIND.numberRange && field.kind !== FILTER_FIELD_KIND.dateRange) {
|
|
58
|
-
continue;
|
|
59
|
-
}
|
|
75
|
+
for (const field of fields.filter(isRangeField)) {
|
|
60
76
|
const [fromKey, toKey] = filterKeysOf(field);
|
|
61
77
|
const from = values?.[fromKey];
|
|
62
78
|
const to = values?.[toKey];
|
|
63
|
-
if (
|
|
64
|
-
continue;
|
|
65
|
-
}
|
|
66
|
-
const isBackwards = field.kind === FILTER_FIELD_KIND.numberRange ? Number(to) < Number(from) : String(to) < String(from);
|
|
67
|
-
if (isBackwards) {
|
|
79
|
+
if (isFilterValueSet(from) && isFilterValueSet(to) && isBackwardsRange(field, from, to)) {
|
|
68
80
|
invalid[toKey] = true;
|
|
69
81
|
}
|
|
70
82
|
}
|
|
71
83
|
return invalid;
|
|
72
84
|
}
|
|
85
|
+
const earliestDateOf = (...dates) => dates.filter(Boolean).sort()[0];
|
|
86
|
+
const latestDateOf = (...dates) => dates.filter(Boolean).sort().at(-1);
|
|
87
|
+
function dateLimitsOf(field, today) {
|
|
88
|
+
return {
|
|
89
|
+
minDate: latestDateOf(field.minDate, field.disablePast && today),
|
|
90
|
+
maxDate: earliestDateOf(field.maxDate, field.disableFuture && today)
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
function dateRangeBoundsOf(field, values, today) {
|
|
94
|
+
const [fromKey, toKey] = filterKeysOf(field);
|
|
95
|
+
const { minDate, maxDate } = dateLimitsOf(field, today);
|
|
96
|
+
return {
|
|
97
|
+
from: { minDate, maxDate: earliestDateOf(maxDate, values?.[toKey] || void 0) },
|
|
98
|
+
to: { minDate: latestDateOf(minDate, values?.[fromKey] || void 0), maxDate }
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
function drawerProblemsOf(fields, values, { today } = {}) {
|
|
102
|
+
const problems = Object.fromEntries(Object.keys(invalidRangesOf(fields, values)).map((key) => [key, { problem: FILTER_FIELD_PROBLEM.rangeBackwards }]));
|
|
103
|
+
for (const field of fields.filter((candidate) => candidate.kind === FILTER_FIELD_KIND.dateRange)) {
|
|
104
|
+
const { minDate, maxDate } = dateLimitsOf(field, today);
|
|
105
|
+
for (const key of filterKeysOf(field)) {
|
|
106
|
+
const date = values?.[key];
|
|
107
|
+
if (!isFilterValueSet(date)) {
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
if (minDate && String(date) < minDate) {
|
|
111
|
+
problems[key] = { problem: FILTER_FIELD_PROBLEM.beforeMinDate, date: minDate };
|
|
112
|
+
} else if (maxDate && String(date) > maxDate) {
|
|
113
|
+
problems[key] = { problem: FILTER_FIELD_PROBLEM.afterMaxDate, date: maxDate };
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
return problems;
|
|
118
|
+
}
|
|
73
119
|
function filterDrawerChipsOf(fields, { t, formatDate = (date) => date }) {
|
|
74
120
|
const text = [];
|
|
75
121
|
const multiSelect = [];
|
|
@@ -78,11 +124,11 @@ function filterDrawerChipsOf(fields, { t, formatDate = (date) => date }) {
|
|
|
78
124
|
const withUnit = (value) => `${field.prefix ?? ""}${value}${field.suffix ? ` ${field.suffix}` : ""}`;
|
|
79
125
|
if (isListField(field)) {
|
|
80
126
|
multiSelect.push({ key: field.name, label: field.label, itemLabel: (value) => optionLabelOf(field, value) });
|
|
81
|
-
} else if (field
|
|
127
|
+
} else if (isRangeField(field)) {
|
|
82
128
|
const [fromKey, toKey] = filterKeysOf(field);
|
|
83
|
-
const format = field.kind === FILTER_FIELD_KIND.dateRange ? formatDate : withUnit;
|
|
84
|
-
text.push({ key: fromKey, label: t("filterDrawer.chipFrom", { label: field.label }), format });
|
|
85
|
-
text.push({ key: toKey, label: t("filterDrawer.chipTo", { label: field.label }), format });
|
|
129
|
+
const format = field.kind === FILTER_FIELD_KIND.dateRange ? formatDate : isCustomRange(field) ? field.chipFormat : withUnit;
|
|
130
|
+
text.push({ key: fromKey, label: t("filterDrawer.chipFrom", { label: field.label }), ...format && { format } });
|
|
131
|
+
text.push({ key: toKey, label: t("filterDrawer.chipTo", { label: field.label }), ...format && { format } });
|
|
86
132
|
} else if (field.kind === FILTER_FIELD_KIND.comparison) {
|
|
87
133
|
text.push({
|
|
88
134
|
key: field.name,
|
|
@@ -103,9 +149,13 @@ function filterDrawerChipsOf(fields, { t, formatDate = (date) => date }) {
|
|
|
103
149
|
export {
|
|
104
150
|
COMPARISON_OPERATOR,
|
|
105
151
|
FILTER_FIELD_KIND,
|
|
152
|
+
FILTER_FIELD_PROBLEM,
|
|
106
153
|
activeDrawerFilterCountOf,
|
|
107
154
|
comparisonOf,
|
|
108
155
|
comparisonTextOf,
|
|
156
|
+
dateLimitsOf,
|
|
157
|
+
dateRangeBoundsOf,
|
|
158
|
+
drawerProblemsOf,
|
|
109
159
|
drawerValuesOf,
|
|
110
160
|
emptyDrawerValuesOf,
|
|
111
161
|
filterDrawerChipsOf,
|
|
@@ -114,6 +164,7 @@ export {
|
|
|
114
164
|
filterKeysOf,
|
|
115
165
|
invalidRangesOf,
|
|
116
166
|
isFilterValueSet,
|
|
117
|
-
isListField
|
|
167
|
+
isListField,
|
|
168
|
+
isRangeField
|
|
118
169
|
};
|
|
119
170
|
//# sourceMappingURL=filterDrawerFields.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"filterDrawerFields.js","sources":["../../../src/ui/filters/filterDrawerFields.js"],"sourcesContent":["// Los campos del cajón de búsqueda (FilterDrawer): cómo viaja cada uno en los filtros de la lista, cuántos hay puestos y\n// cómo se dicen en sus chips. Un campo se declara una vez y de ahí sale todo:\n//\n// text un texto {name}: 'ana'\n// select una opción {name}: 'card'\n// multiSelect varias opciones (lista) {name}: ['card', 'cash']\n// catalog una o varias de un catálogo largo {name}: 'NL' o ['NL', 'JA'] (multiple)\n// checkboxes varias, a la vista {name}: ['email', 'sms']\n// numberRange desde y hasta {name}_min: '3', {name}_max: '10'\n// dateRange del día al día (AAAA-MM-DD) {name}_from: '2026-11-01', {name}_to: '2026-11-30'\n// comparison un número con su operador {name}: 'gte:500' (mayor o igual que 500)\n// custom uno propio (render) {name}: lo que el proyecto decida\n//\n// Los nombres de un rango se cambian con keys ({from: 'created_from', to: 'created_to'}) para usar los que ya tiene el\n// backend. Los textos llegan ya traducidos (label, options[].label); los de la casa salen de `t` (common.filterDrawer).\n//\n// Puro (sin React): se prueba con node (filterDrawerFields.test.js).\n//\n// Genérico del paquete: function (regla de la casa).\n\n/** Los tipos de campo del cajón. */\nexport const FILTER_FIELD_KIND = Object.freeze({\n text: 'text', select: 'select', multiSelect: 'multiSelect', catalog: 'catalog', checkboxes: 'checkboxes',\n numberRange: 'numberRange', dateRange: 'dateRange', comparison: 'comparison', custom: 'custom',\n});\n\n/** Los operadores de un campo de comparación, como viajan ('gte:500'). */\nexport const COMPARISON_OPERATOR = Object.freeze({equals: 'eq', greaterThan: 'gt', greaterOrEqual: 'gte', lessThan: 'lt', lessOrEqual: 'lte'});\n\n// cómo se dice cada operador en un chip, corto\nconst OPERATOR_SYMBOLS = Object.freeze({eq: '=', gt: '>', gte: '≥', lt: '<', lte: '≤'});\nconst COMPARISON_SEPARATOR = ':';\n// los que guardan una lista: así los lee la tabla en la dirección\nconst LIST_KINDS = new Set([FILTER_FIELD_KIND.multiSelect, FILTER_FIELD_KIND.checkboxes]);\n\n/**\n * ¿El campo guarda una lista?\n *\n * @param {{kind: string, multiple?: boolean}} field\n * @returns {boolean}\n */\nexport function isListField(field) {\n return LIST_KINDS.has(field.kind) || (field.kind === FILTER_FIELD_KIND.catalog && Boolean(field.multiple));\n}\n\n/**\n * Los nombres con que viaja un campo en los filtros: uno, o dos para un rango.\n *\n * @param {{name: string, kind: string, keys?: {from?: string, to?: string}}} field\n * @returns {string[]}\n */\nexport function filterKeysOf(field) {\n if (field.kind === FILTER_FIELD_KIND.numberRange) {\n return [field.keys?.from ?? `${field.name}_min`, field.keys?.to ?? `${field.name}_max`];\n }\n if (field.kind === FILTER_FIELD_KIND.dateRange) {\n return [field.keys?.from ?? `${field.name}_from`, field.keys?.to ?? `${field.name}_to`];\n }\n return [field.name];\n}\n\n/**\n * Un valor de comparación ('gte:500') en sus partes; null si viene vacío o no se entiende.\n *\n * @param {unknown} text\n * @returns {{operator: string, value: string}|null}\n */\nexport function comparisonOf(text) {\n const [operator, ...rest] = String(text ?? '').split(COMPARISON_SEPARATOR);\n const value = rest.join(COMPARISON_SEPARATOR);\n return OPERATOR_SYMBOLS[operator] && value !== '' ? {operator, value} : null;\n}\n\n/**\n * Las partes de una comparación como viajan; '' si falta el número.\n *\n * @param {string} operator COMPARISON_OPERATOR\n * @param {string} value\n * @returns {string}\n */\nexport function comparisonTextOf(operator, value) {\n return value === '' || value === null || value === undefined ? '' : `${operator}${COMPARISON_SEPARATOR}${value}`;\n}\n\n/**\n * ¿Ese valor de filtro está puesto? Un texto vacío o una lista vacía no cuentan.\n *\n * @param {unknown} value\n * @returns {boolean}\n */\nexport function isFilterValueSet(value) {\n return Array.isArray(value) ? value.length > 0 : value !== '' && value !== null && value !== undefined;\n}\n\n/**\n * Los tipos de filtro para ServerTable (filterKinds): los de lista se leen como lista en la dirección.\n *\n * @param {object[]} fields\n * @returns {Record<string, string>}\n */\nexport function filterDrawerKindsOf(fields) {\n return Object.fromEntries(fields.filter(isListField).map((field) => [field.name, 'multiSelect']));\n}\n\n/**\n * Lo que hay en los filtros para los campos del cajón (lo demás no se toca).\n *\n * @param {object[]} fields\n * @param {Record<string, unknown>} filters\n * @returns {Record<string, unknown>}\n */\nexport function drawerValuesOf(fields, filters) {\n return Object.fromEntries(fields.flatMap((field) => filterKeysOf(field).map((key) => [key, filters?.[key] ?? (isListField(field) ? [] : '')])));\n}\n\n/**\n * Los campos del cajón vacíos: lo que deja «Limpiar».\n *\n * @param {object[]} fields\n * @returns {Record<string, unknown>}\n */\nexport function emptyDrawerValuesOf(fields) {\n return drawerValuesOf(fields, {});\n}\n\n/**\n * Los campos del cajón vacíos, para sumarlos a filterDefaults de ServerTable: la tabla solo guarda en la dirección los\n * filtros que declara ahí.\n *\n * @param {object[]} fields\n * @returns {Record<string, unknown>}\n */\nexport function filterDrawerDefaultsOf(fields) {\n return emptyDrawerValuesOf(fields);\n}\n\n/**\n * Cuántos campos del cajón tienen algo puesto (un rango con «desde» y «hasta» cuenta uno): el número del botón «Filtros».\n *\n * @param {object[]} fields\n * @param {Record<string, unknown>} filters\n * @returns {number}\n */\nexport function activeDrawerFilterCountOf(fields, filters) {\n return fields.filter((field) => filterKeysOf(field).some((key) => isFilterValueSet(filters?.[key]))).length;\n}\n\n/**\n * Los rangos que no tienen sentido («hasta» menor que «desde»): {llave de «hasta»: true}.\n *\n * @param {object[]} fields\n * @param {Record<string, unknown>} values\n * @returns {Record<string, boolean>}\n */\nexport function invalidRangesOf(fields, values) {\n const invalid = {};\n for (const field of fields) {\n if (field.kind !== FILTER_FIELD_KIND.numberRange && field.kind !== FILTER_FIELD_KIND.dateRange) {\n continue;\n }\n const [fromKey, toKey] = filterKeysOf(field);\n const from = values?.[fromKey];\n const to = values?.[toKey];\n if (!isFilterValueSet(from) || !isFilterValueSet(to)) {\n continue;\n }\n // los números como números y las fechas AAAA-MM-DD como texto (ya se ordenan solas)\n const isBackwards = field.kind === FILTER_FIELD_KIND.numberRange ? Number(to) < Number(from) : String(to) < String(from);\n if (isBackwards) {\n invalid[toKey] = true;\n }\n }\n return invalid;\n}\n\n/**\n * Los chips de los campos del cajón, con la forma de buildChipConfig: cada valor con su etiqueta, ya dicho en palabras.\n *\n * @param {object[]} fields\n * @param {object} helpers\n * @param {(key: string, values?: object) => string} helpers.t el de common (filterDrawer.chipFrom, chipTo)\n * @param {(date: string) => string} [helpers.formatDate] cómo se ve una fecha\n * @returns {{text: Array<{key: string, label: string, format?: (value: string) => string}>, multiSelect: Array<{key: string, label: string, itemLabel?: (value: string) => string}>}}\n */\nexport function filterDrawerChipsOf(fields, {t, formatDate = (date) => date}) {\n const text = [];\n const multiSelect = [];\n const optionLabelOf = (field, value) => field.options?.find((option) => (option.value ?? option.code) === value)?.label ?? String(value);\n for (const field of fields) {\n const withUnit = (value) => `${field.prefix ?? ''}${value}${field.suffix ? ` ${field.suffix}` : ''}`;\n if (isListField(field)) {\n multiSelect.push({key: field.name, label: field.label, itemLabel: (value) => optionLabelOf(field, value)});\n } else if (field.kind === FILTER_FIELD_KIND.numberRange || field.kind === FILTER_FIELD_KIND.dateRange) {\n const [fromKey, toKey] = filterKeysOf(field);\n const format = field.kind === FILTER_FIELD_KIND.dateRange ? formatDate : withUnit;\n text.push({key: fromKey, label: t('filterDrawer.chipFrom', {label: field.label}), format});\n text.push({key: toKey, label: t('filterDrawer.chipTo', {label: field.label}), format});\n } else if (field.kind === FILTER_FIELD_KIND.comparison) {\n text.push({\n key: field.name, label: field.label,\n format: (value) => {\n const parts = comparisonOf(value);\n return parts ? `${OPERATOR_SYMBOLS[parts.operator]} ${withUnit(parts.value)}` : String(value);\n },\n });\n } else if (field.kind === FILTER_FIELD_KIND.select || field.kind === FILTER_FIELD_KIND.catalog) {\n text.push({key: field.name, label: field.label, format: (value) => optionLabelOf(field, value)});\n } else {\n text.push({key: field.name, label: field.label, ...(field.chipFormat && {format: field.chipFormat})});\n }\n }\n return {text, multiSelect};\n}\n"],"names":[],"mappings":"AAqBY,MAAC,oBAAoB,OAAO,OAAO;AAAA,EAC3C,MAAM;AAAA,EAAQ,QAAQ;AAAA,EAAU,aAAa;AAAA,EAAe,SAAS;AAAA,EAAW,YAAY;AAAA,EAC5F,aAAa;AAAA,EAAe,WAAW;AAAA,EAAa,YAAY;AAAA,EAAc,QAAQ;AAC1F,CAAC;AAGW,MAAC,sBAAsB,OAAO,OAAO,EAAC,QAAQ,MAAM,aAAa,MAAM,gBAAgB,OAAO,UAAU,MAAM,aAAa,MAAK,CAAC;AAG7I,MAAM,mBAAmB,OAAO,OAAO,EAAC,IAAI,KAAK,IAAI,KAAK,KAAK,KAAK,IAAI,KAAK,KAAK,IAAG,CAAC;AACtF,MAAM,uBAAuB;AAE7B,MAAM,aAAa,oBAAI,IAAI,CAAC,kBAAkB,aAAa,kBAAkB,UAAU,CAAC;AAQjF,SAAS,YAAY,OAAO;AAC/B,SAAO,WAAW,IAAI,MAAM,IAAI,KAAM,MAAM,SAAS,kBAAkB,WAAW,QAAQ,MAAM,QAAQ;AAC5G;AAQO,SAAS,aAAa,OAAO;AAChC,MAAI,MAAM,SAAS,kBAAkB,aAAa;AAC9C,WAAO,CAAC,MAAM,MAAM,QAAQ,GAAG,MAAM,IAAI,QAAQ,MAAM,MAAM,MAAM,GAAG,MAAM,IAAI,MAAM;AAAA,EAC1F;AACA,MAAI,MAAM,SAAS,kBAAkB,WAAW;AAC5C,WAAO,CAAC,MAAM,MAAM,QAAQ,GAAG,MAAM,IAAI,SAAS,MAAM,MAAM,MAAM,GAAG,MAAM,IAAI,KAAK;AAAA,EAC1F;AACA,SAAO,CAAC,MAAM,IAAI;AACtB;AAQO,SAAS,aAAa,MAAM;AAC/B,QAAM,CAAC,UAAU,GAAG,IAAI,IAAI,OAAO,QAAQ,EAAE,EAAE,MAAM,oBAAoB;AACzE,QAAM,QAAQ,KAAK,KAAK,oBAAoB;AAC5C,SAAO,iBAAiB,QAAQ,KAAK,UAAU,KAAK,EAAC,UAAU,MAAK,IAAI;AAC5E;AASO,SAAS,iBAAiB,UAAU,OAAO;AAC9C,SAAO,UAAU,MAAM,UAAU,QAAQ,UAAU,SAAY,KAAK,GAAG,QAAQ,GAAG,oBAAoB,GAAG,KAAK;AAClH;AAQO,SAAS,iBAAiB,OAAO;AACpC,SAAO,MAAM,QAAQ,KAAK,IAAI,MAAM,SAAS,IAAI,UAAU,MAAM,UAAU,QAAQ,UAAU;AACjG;AAQO,SAAS,oBAAoB,QAAQ;AACxC,SAAO,OAAO,YAAY,OAAO,OAAO,WAAW,EAAE,IAAI,CAAC,UAAU,CAAC,MAAM,MAAM,aAAa,CAAC,CAAC;AACpG;AASO,SAAS,eAAe,QAAQ,SAAS;AAC5C,SAAO,OAAO,YAAY,OAAO,QAAQ,CAAC,UAAU,aAAa,KAAK,EAAE,IAAI,CAAC,QAAQ,CAAC,KAAK,UAAU,GAAG,MAAM,YAAY,KAAK,IAAI,KAAK,GAAG,CAAC,CAAC,CAAC;AAClJ;AAQO,SAAS,oBAAoB,QAAQ;AACxC,SAAO,eAAe,QAAQ,EAAE;AACpC;AASO,SAAS,uBAAuB,QAAQ;AAC3C,SAAO,oBAAoB,MAAM;AACrC;AASO,SAAS,0BAA0B,QAAQ,SAAS;AACvD,SAAO,OAAO,OAAO,CAAC,UAAU,aAAa,KAAK,EAAE,KAAK,CAAC,QAAQ,iBAAiB,UAAU,GAAG,CAAC,CAAC,CAAC,EAAE;AACzG;AASO,SAAS,gBAAgB,QAAQ,QAAQ;AAC5C,QAAM,UAAU,CAAA;AAChB,aAAW,SAAS,QAAQ;AACxB,QAAI,MAAM,SAAS,kBAAkB,eAAe,MAAM,SAAS,kBAAkB,WAAW;AAC5F;AAAA,IACJ;AACA,UAAM,CAAC,SAAS,KAAK,IAAI,aAAa,KAAK;AAC3C,UAAM,OAAO,SAAS,OAAO;AAC7B,UAAM,KAAK,SAAS,KAAK;AACzB,QAAI,CAAC,iBAAiB,IAAI,KAAK,CAAC,iBAAiB,EAAE,GAAG;AAClD;AAAA,IACJ;AAEA,UAAM,cAAc,MAAM,SAAS,kBAAkB,cAAc,OAAO,EAAE,IAAI,OAAO,IAAI,IAAI,OAAO,EAAE,IAAI,OAAO,IAAI;AACvH,QAAI,aAAa;AACb,cAAQ,KAAK,IAAI;AAAA,IACrB;AAAA,EACJ;AACA,SAAO;AACX;AAWO,SAAS,oBAAoB,QAAQ,EAAC,GAAG,aAAa,CAAC,SAAS,KAAI,GAAG;AAC1E,QAAM,OAAO,CAAA;AACb,QAAM,cAAc,CAAA;AACpB,QAAM,gBAAgB,CAAC,OAAO,UAAU,MAAM,SAAS,KAAK,CAAC,YAAY,OAAO,SAAS,OAAO,UAAU,KAAK,GAAG,SAAS,OAAO,KAAK;AACvI,aAAW,SAAS,QAAQ;AACxB,UAAM,WAAW,CAAC,UAAU,GAAG,MAAM,UAAU,EAAE,GAAG,KAAK,GAAG,MAAM,SAAS,IAAI,MAAM,MAAM,KAAK,EAAE;AAClG,QAAI,YAAY,KAAK,GAAG;AACpB,kBAAY,KAAK,EAAC,KAAK,MAAM,MAAM,OAAO,MAAM,OAAO,WAAW,CAAC,UAAU,cAAc,OAAO,KAAK,EAAC,CAAC;AAAA,IAC7G,WAAW,MAAM,SAAS,kBAAkB,eAAe,MAAM,SAAS,kBAAkB,WAAW;AACnG,YAAM,CAAC,SAAS,KAAK,IAAI,aAAa,KAAK;AAC3C,YAAM,SAAS,MAAM,SAAS,kBAAkB,YAAY,aAAa;AACzE,WAAK,KAAK,EAAC,KAAK,SAAS,OAAO,EAAE,yBAAyB,EAAC,OAAO,MAAM,MAAK,CAAC,GAAG,OAAM,CAAC;AACzF,WAAK,KAAK,EAAC,KAAK,OAAO,OAAO,EAAE,uBAAuB,EAAC,OAAO,MAAM,MAAK,CAAC,GAAG,OAAM,CAAC;AAAA,IACzF,WAAW,MAAM,SAAS,kBAAkB,YAAY;AACpD,WAAK,KAAK;AAAA,QACN,KAAK,MAAM;AAAA,QAAM,OAAO,MAAM;AAAA,QAC9B,QAAQ,CAAC,UAAU;AACf,gBAAM,QAAQ,aAAa,KAAK;AAChC,iBAAO,QAAQ,GAAG,iBAAiB,MAAM,QAAQ,CAAC,IAAI,SAAS,MAAM,KAAK,CAAC,KAAK,OAAO,KAAK;AAAA,QAChG;AAAA,MAChB,CAAa;AAAA,IACL,WAAW,MAAM,SAAS,kBAAkB,UAAU,MAAM,SAAS,kBAAkB,SAAS;AAC5F,WAAK,KAAK,EAAC,KAAK,MAAM,MAAM,OAAO,MAAM,OAAO,QAAQ,CAAC,UAAU,cAAc,OAAO,KAAK,EAAC,CAAC;AAAA,IACnG,OAAO;AACH,WAAK,KAAK,EAAC,KAAK,MAAM,MAAM,OAAO,MAAM,OAAO,GAAI,MAAM,cAAc,EAAC,QAAQ,MAAM,WAAU,EAAE,CAAC;AAAA,IACxG;AAAA,EACJ;AACA,SAAO,EAAC,MAAM,YAAW;AAC7B;"}
|
|
1
|
+
{"version":3,"file":"filterDrawerFields.js","sources":["../../../src/ui/filters/filterDrawerFields.js"],"sourcesContent":["// Los campos del cajón de búsqueda (FilterDrawer): cómo viaja cada uno en los filtros de la lista, cuántos hay puestos y\n// cómo se dicen en sus chips. Un campo se declara una vez y de ahí sale todo:\n//\n// text un texto {name}: 'ana'\n// select una opción {name}: 'card'\n// multiSelect varias opciones (lista) {name}: ['card', 'cash']\n// catalog una o varias de un catálogo largo {name}: 'NL' o ['NL', 'JA'] (multiple)\n// checkboxes varias, a la vista {name}: ['email', 'sms']\n// numberRange desde y hasta {name}_min: '3', {name}_max: '10'\n// dateRange del día al día (AAAA-MM-DD) {name}_from: '2026-11-01', {name}_to: '2026-11-30'\n// comparison un número con su operador {name}: 'gte:500' (mayor o igual que 500)\n// custom uno propio (render) {name}: lo que el proyecto decida\n// o un rango propio, con keys {keys.from}: …, {keys.to}: …\n//\n// Los nombres de un rango se cambian con keys ({from: 'created_from', to: 'created_to'}) para usar los que ya tiene el\n// backend. Un custom con keys es un rango propio: guarda en sus dos llaves, cuenta como un filtro, da dos chips («desde» y\n// «hasta») y, con isBackwards(from, to), no deja buscar si está al revés, como los de la casa.\n//\n// Un dateRange se acota con minDate y maxDate ('AAAA-MM-DD'), disablePast y disableFuture (hoy en la zona del producto): el\n// calendario no deja escoger fuera, cada extremo acota al otro y lo que se teclee fuera se dice en su campo y no deja buscar\n// (drawerProblemsOf). Nada se borra solo.\n//\n// Los textos llegan ya traducidos (label, options[].label); los de la casa salen de `t` (common.filterDrawer).\n//\n// Puro (sin React): se prueba con node (filterDrawerFields.test.js).\n//\n// Genérico del paquete: function (regla de la casa).\n\n/** Los tipos de campo del cajón. */\nexport const FILTER_FIELD_KIND = Object.freeze({\n text: 'text', select: 'select', multiSelect: 'multiSelect', catalog: 'catalog', checkboxes: 'checkboxes',\n numberRange: 'numberRange', dateRange: 'dateRange', comparison: 'comparison', custom: 'custom',\n});\n\n/**\n * Lo que puede estar mal en un campo del cajón; cada uno es su texto en common.filterDrawer. Mientras haya uno, «Buscar» no\n * busca.\n */\nexport const FILTER_FIELD_PROBLEM = Object.freeze({rangeBackwards: 'rangeBackwards', beforeMinDate: 'dateBeforeMin', afterMaxDate: 'dateAfterMax'});\n\n/** Los operadores de un campo de comparación, como viajan ('gte:500'). */\nexport const COMPARISON_OPERATOR = Object.freeze({equals: 'eq', greaterThan: 'gt', greaterOrEqual: 'gte', lessThan: 'lt', lessOrEqual: 'lte'});\n\n// cómo se dice cada operador en un chip, corto\nconst OPERATOR_SYMBOLS = Object.freeze({eq: '=', gt: '>', gte: '≥', lt: '<', lte: '≤'});\nconst COMPARISON_SEPARATOR = ':';\n// los que guardan una lista: así los lee la tabla en la dirección\nconst LIST_KINDS = new Set([FILTER_FIELD_KIND.multiSelect, FILTER_FIELD_KIND.checkboxes]);\n\n/**\n * ¿El campo guarda una lista?\n *\n * @param {{kind: string, multiple?: boolean}} field\n * @returns {boolean}\n */\nexport function isListField(field) {\n return LIST_KINDS.has(field.kind) || (field.kind === FILTER_FIELD_KIND.catalog && Boolean(field.multiple));\n}\n\n/**\n * ¿El campo es un rango propio? Un custom con sus dos llaves (keys.from y keys.to).\n *\n * @param {{kind: string, keys?: {from?: string, to?: string}}} field\n * @returns {boolean}\n */\nfunction isCustomRange(field) {\n return field.kind === FILTER_FIELD_KIND.custom && Boolean(field.keys?.from) && Boolean(field.keys?.to);\n}\n\n/**\n * ¿El campo guarda un «desde» y un «hasta»? Los rangos de la casa y los propios.\n *\n * @param {{kind: string, keys?: {from?: string, to?: string}}} field\n * @returns {boolean}\n */\nexport function isRangeField(field) {\n return field.kind === FILTER_FIELD_KIND.numberRange || field.kind === FILTER_FIELD_KIND.dateRange || isCustomRange(field);\n}\n\n/**\n * Los nombres con que viaja un campo en los filtros: uno, o dos para un rango (también uno propio: custom con keys).\n *\n * @param {{name: string, kind: string, keys?: {from?: string, to?: string}}} field\n * @returns {string[]}\n */\nexport function filterKeysOf(field) {\n if (field.kind === FILTER_FIELD_KIND.numberRange) {\n return [field.keys?.from ?? `${field.name}_min`, field.keys?.to ?? `${field.name}_max`];\n }\n if (field.kind === FILTER_FIELD_KIND.dateRange) {\n return [field.keys?.from ?? `${field.name}_from`, field.keys?.to ?? `${field.name}_to`];\n }\n if (isCustomRange(field)) {\n return [field.keys.from, field.keys.to];\n }\n return [field.name];\n}\n\n/**\n * Un valor de comparación ('gte:500') en sus partes; null si viene vacío o no se entiende.\n *\n * @param {unknown} text\n * @returns {{operator: string, value: string}|null}\n */\nexport function comparisonOf(text) {\n const [operator, ...rest] = String(text ?? '').split(COMPARISON_SEPARATOR);\n const value = rest.join(COMPARISON_SEPARATOR);\n return OPERATOR_SYMBOLS[operator] && value !== '' ? {operator, value} : null;\n}\n\n/**\n * Las partes de una comparación como viajan; '' si falta el número.\n *\n * @param {string} operator COMPARISON_OPERATOR\n * @param {string} value\n * @returns {string}\n */\nexport function comparisonTextOf(operator, value) {\n return value === '' || value === null || value === undefined ? '' : `${operator}${COMPARISON_SEPARATOR}${value}`;\n}\n\n/**\n * ¿Ese valor de filtro está puesto? Un texto vacío o una lista vacía no cuentan.\n *\n * @param {unknown} value\n * @returns {boolean}\n */\nexport function isFilterValueSet(value) {\n return Array.isArray(value) ? value.length > 0 : value !== '' && value !== null && value !== undefined;\n}\n\n/**\n * Los tipos de filtro para ServerTable (filterKinds): los de lista se leen como lista en la dirección.\n *\n * @param {object[]} fields\n * @returns {Record<string, string>}\n */\nexport function filterDrawerKindsOf(fields) {\n return Object.fromEntries(fields.filter(isListField).map((field) => [field.name, 'multiSelect']));\n}\n\n/**\n * Lo que hay en los filtros para los campos del cajón (lo demás no se toca).\n *\n * @param {object[]} fields\n * @param {Record<string, unknown>} filters\n * @returns {Record<string, unknown>}\n */\nexport function drawerValuesOf(fields, filters) {\n return Object.fromEntries(fields.flatMap((field) => filterKeysOf(field).map((key) => [key, filters?.[key] ?? (isListField(field) ? [] : '')])));\n}\n\n/**\n * Los campos del cajón vacíos: lo que deja «Limpiar».\n *\n * @param {object[]} fields\n * @returns {Record<string, unknown>}\n */\nexport function emptyDrawerValuesOf(fields) {\n return drawerValuesOf(fields, {});\n}\n\n/**\n * Los campos del cajón vacíos, para sumarlos a filterDefaults de ServerTable: la tabla solo guarda en la dirección los\n * filtros que declara ahí.\n *\n * @param {object[]} fields\n * @returns {Record<string, unknown>}\n */\nexport function filterDrawerDefaultsOf(fields) {\n return emptyDrawerValuesOf(fields);\n}\n\n/**\n * Cuántos campos del cajón tienen algo puesto (un rango con «desde» y «hasta» cuenta uno): el número del botón «Filtros».\n *\n * @param {object[]} fields\n * @param {Record<string, unknown>} filters\n * @returns {number}\n */\nexport function activeDrawerFilterCountOf(fields, filters) {\n return fields.filter((field) => filterKeysOf(field).some((key) => isFilterValueSet(filters?.[key]))).length;\n}\n\n/**\n * ¿Ese rango está al revés («hasta» antes que «desde»)? Los números como números, las fechas AAAA-MM-DD como texto (ya se\n * ordenan solas) y uno propio con su isBackwards (sin él, nunca).\n */\nfunction isBackwardsRange(field, from, to) {\n if (field.kind === FILTER_FIELD_KIND.numberRange) {\n return Number(to) < Number(from);\n }\n if (field.kind === FILTER_FIELD_KIND.dateRange) {\n return String(to) < String(from);\n }\n return Boolean(field.isBackwards?.(from, to));\n}\n\n/**\n * Los rangos que no tienen sentido («hasta» menor que «desde»): {llave de «hasta»: true}. También los propios (custom con\n * keys) que traen isBackwards.\n *\n * @param {object[]} fields\n * @param {Record<string, unknown>} values\n * @returns {Record<string, boolean>}\n */\nexport function invalidRangesOf(fields, values) {\n const invalid = {};\n for (const field of fields.filter(isRangeField)) {\n const [fromKey, toKey] = filterKeysOf(field);\n const from = values?.[fromKey];\n const to = values?.[toKey];\n if (isFilterValueSet(from) && isFilterValueSet(to) && isBackwardsRange(field, from, to)) {\n invalid[toKey] = true;\n }\n }\n return invalid;\n}\n\n// la más temprana y la más tardía de unas fechas AAAA-MM-DD (las vacías no cuentan)\nconst earliestDateOf = (...dates) => dates.filter(Boolean).sort()[0];\nconst latestDateOf = (...dates) => dates.filter(Boolean).sort().at(-1);\n\n/**\n * Los topes de un dateRange: minDate y maxDate del campo, y hoy si pide disablePast o disableFuture (el más estrecho gana).\n *\n * @param {{minDate?: string, maxDate?: string, disablePast?: boolean, disableFuture?: boolean}} field\n * @param {string} today AAAA-MM-DD en la zona del producto (todayInZone)\n * @returns {{minDate?: string, maxDate?: string}}\n */\nexport function dateLimitsOf(field, today) {\n return {\n minDate: latestDateOf(field.minDate, field.disablePast && today),\n maxDate: earliestDateOf(field.maxDate, field.disableFuture && today),\n };\n}\n\n/**\n * Los topes de cada extremo de un dateRange para su calendario: los del campo y, además, cada extremo acota al otro (el\n * «desde» no pasa del «hasta» puesto y el «hasta» no baja del «desde»).\n *\n * @param {object} field\n * @param {Record<string, unknown>} values el borrador del cajón\n * @param {string} today AAAA-MM-DD en la zona del producto\n * @returns {{from: {minDate?: string, maxDate?: string}, to: {minDate?: string, maxDate?: string}}}\n */\nexport function dateRangeBoundsOf(field, values, today) {\n const [fromKey, toKey] = filterKeysOf(field);\n const {minDate, maxDate} = dateLimitsOf(field, today);\n return {\n from: {minDate, maxDate: earliestDateOf(maxDate, values?.[toKey] || undefined)},\n to: {minDate: latestDateOf(minDate, values?.[fromKey] || undefined), maxDate},\n };\n}\n\n/**\n * Lo que está mal en los campos del cajón, por llave: un rango al revés (en su «hasta») y una fecha fuera de los topes de\n * su dateRange (la que se tecleó: el calendario ya no deja escogerla). Mientras haya algo, «Buscar» no busca.\n *\n * {created_to: {problem: 'rangeBackwards'}, paid_from: {problem: 'dateBeforeMin', date: '2026-01-01'}}\n *\n * @param {object[]} fields\n * @param {Record<string, unknown>} values\n * @param {{today?: string}} [options] hoy AAAA-MM-DD en la zona del producto (para disablePast y disableFuture)\n * @returns {Record<string, {problem: string, date?: string}>} problem es FILTER_FIELD_PROBLEM; date, el tope que se pasó\n */\nexport function drawerProblemsOf(fields, values, {today} = {}) {\n const problems = Object.fromEntries(Object.keys(invalidRangesOf(fields, values)).map((key) => [key, {problem: FILTER_FIELD_PROBLEM.rangeBackwards}]));\n for (const field of fields.filter((candidate) => candidate.kind === FILTER_FIELD_KIND.dateRange)) {\n const {minDate, maxDate} = dateLimitsOf(field, today);\n for (const key of filterKeysOf(field)) {\n const date = values?.[key];\n if (!isFilterValueSet(date)) {\n continue;\n }\n if (minDate && String(date) < minDate) {\n problems[key] = {problem: FILTER_FIELD_PROBLEM.beforeMinDate, date: minDate};\n } else if (maxDate && String(date) > maxDate) {\n problems[key] = {problem: FILTER_FIELD_PROBLEM.afterMaxDate, date: maxDate};\n }\n }\n }\n return problems;\n}\n\n/**\n * Los chips de los campos del cajón, con la forma de buildChipConfig: cada valor con su etiqueta, ya dicho en palabras.\n *\n * @param {object[]} fields\n * @param {object} helpers\n * @param {(key: string, values?: object) => string} helpers.t el de common (filterDrawer.chipFrom, chipTo)\n * @param {(date: string) => string} [helpers.formatDate] cómo se ve una fecha\n * @returns {{text: Array<{key: string, label: string, format?: (value: string) => string}>, multiSelect: Array<{key: string, label: string, itemLabel?: (value: string) => string}>}}\n */\nexport function filterDrawerChipsOf(fields, {t, formatDate = (date) => date}) {\n const text = [];\n const multiSelect = [];\n const optionLabelOf = (field, value) => field.options?.find((option) => (option.value ?? option.code) === value)?.label ?? String(value);\n for (const field of fields) {\n const withUnit = (value) => `${field.prefix ?? ''}${value}${field.suffix ? ` ${field.suffix}` : ''}`;\n if (isListField(field)) {\n multiSelect.push({key: field.name, label: field.label, itemLabel: (value) => optionLabelOf(field, value)});\n } else if (isRangeField(field)) {\n const [fromKey, toKey] = filterKeysOf(field);\n // un rango propio se dice con su chipFormat (o tal cual)\n const format = field.kind === FILTER_FIELD_KIND.dateRange ? formatDate : (isCustomRange(field) ? field.chipFormat : withUnit);\n text.push({key: fromKey, label: t('filterDrawer.chipFrom', {label: field.label}), ...(format && {format})});\n text.push({key: toKey, label: t('filterDrawer.chipTo', {label: field.label}), ...(format && {format})});\n } else if (field.kind === FILTER_FIELD_KIND.comparison) {\n text.push({\n key: field.name, label: field.label,\n format: (value) => {\n const parts = comparisonOf(value);\n return parts ? `${OPERATOR_SYMBOLS[parts.operator]} ${withUnit(parts.value)}` : String(value);\n },\n });\n } else if (field.kind === FILTER_FIELD_KIND.select || field.kind === FILTER_FIELD_KIND.catalog) {\n text.push({key: field.name, label: field.label, format: (value) => optionLabelOf(field, value)});\n } else {\n text.push({key: field.name, label: field.label, ...(field.chipFormat && {format: field.chipFormat})});\n }\n }\n return {text, multiSelect};\n}\n"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stackcraftmx/calcita",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.57.0",
|
|
4
4
|
"description": "Componentes y utilidades de React con MUI para arrancar proyectos rápido: tabla de servidor, diálogos, avisos, descargas, formularios, formatos y reportes en Excel.",
|
|
5
5
|
"license": "FSL-1.1-MIT",
|
|
6
6
|
"author": "calcifux",
|