@fuf-stack/megapixels 0.7.3 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/Filter/index.cjs +6 -12
- package/dist/Filter/index.d.cts +2 -328
- package/dist/Filter/index.d.ts +2 -328
- package/dist/Filter/index.js +3 -13
- package/dist/Filter-Bc0LWCnZ.cjs +811 -0
- package/dist/Filter-Bc0LWCnZ.cjs.map +1 -0
- package/dist/Filter-nSuQco2t.js +751 -0
- package/dist/Filter-nSuQco2t.js.map +1 -0
- package/dist/index-BYqvJZ2C.d.ts +382 -0
- package/dist/index-BYqvJZ2C.d.ts.map +1 -0
- package/dist/index-_lmq8laN.d.cts +382 -0
- package/dist/index-_lmq8laN.d.cts.map +1 -0
- package/dist/index.cjs +7 -8
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -5
- package/dist/index.d.ts +2 -5
- package/dist/index.js +7 -10
- package/dist/index.js.map +1 -1
- package/package.json +20 -21
- package/dist/Filter/index.cjs.map +0 -1
- package/dist/Filter/index.js.map +0 -1
- package/dist/chunk-KHENACVH.cjs +0 -723
- package/dist/chunk-KHENACVH.cjs.map +0 -1
- package/dist/chunk-YX3ABWOU.js +0 -723
- package/dist/chunk-YX3ABWOU.js.map +0 -1
|
@@ -0,0 +1,811 @@
|
|
|
1
|
+
//#region rolldown:runtime
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __copyProps = (to, from, except, desc) => {
|
|
9
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
10
|
+
for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
11
|
+
key = keys[i];
|
|
12
|
+
if (!__hasOwnProp.call(to, key) && key !== except) {
|
|
13
|
+
__defProp(to, key, {
|
|
14
|
+
get: ((k) => from[k]).bind(null, key),
|
|
15
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
return to;
|
|
21
|
+
};
|
|
22
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
|
|
23
|
+
value: mod,
|
|
24
|
+
enumerable: true
|
|
25
|
+
}) : target, mod));
|
|
26
|
+
|
|
27
|
+
//#endregion
|
|
28
|
+
let debug = require("debug");
|
|
29
|
+
debug = __toESM(debug);
|
|
30
|
+
let __fuf_stack_pixel_utils = require("@fuf-stack/pixel-utils");
|
|
31
|
+
let __fuf_stack_uniform_Form = require("@fuf-stack/uniform/Form");
|
|
32
|
+
__fuf_stack_uniform_Form = __toESM(__fuf_stack_uniform_Form);
|
|
33
|
+
let react = require("react");
|
|
34
|
+
let __fuf_stack_veto = require("@fuf-stack/veto");
|
|
35
|
+
let __fuf_stack_pixels_Label = require("@fuf-stack/pixels/Label");
|
|
36
|
+
__fuf_stack_pixels_Label = __toESM(__fuf_stack_pixels_Label);
|
|
37
|
+
let __fuf_stack_uniform_hooks_useFormContext = require("@fuf-stack/uniform/hooks/useFormContext");
|
|
38
|
+
let react_jsx_runtime = require("react/jsx-runtime");
|
|
39
|
+
let react_icons_fa6 = require("react-icons/fa6");
|
|
40
|
+
let __fuf_stack_pixels_Menu = require("@fuf-stack/pixels/Menu");
|
|
41
|
+
__fuf_stack_pixels_Menu = __toESM(__fuf_stack_pixels_Menu);
|
|
42
|
+
let react_icons_pi = require("react-icons/pi");
|
|
43
|
+
let __fuf_stack_pixels_Button = require("@fuf-stack/pixels/Button");
|
|
44
|
+
__fuf_stack_pixels_Button = __toESM(__fuf_stack_pixels_Button);
|
|
45
|
+
let __fuf_stack_pixels_Modal = require("@fuf-stack/pixels/Modal");
|
|
46
|
+
__fuf_stack_pixels_Modal = __toESM(__fuf_stack_pixels_Modal);
|
|
47
|
+
let __fuf_stack_uniform_SubmitButton = require("@fuf-stack/uniform/SubmitButton");
|
|
48
|
+
__fuf_stack_uniform_SubmitButton = __toESM(__fuf_stack_uniform_SubmitButton);
|
|
49
|
+
let react_icons_fa = require("react-icons/fa");
|
|
50
|
+
let __fuf_stack_pixel_motion = require("@fuf-stack/pixel-motion");
|
|
51
|
+
let __fuf_stack_uniform_Input = require("@fuf-stack/uniform/Input");
|
|
52
|
+
__fuf_stack_uniform_Input = __toESM(__fuf_stack_uniform_Input);
|
|
53
|
+
let __fuf_stack_uniform_Switch = require("@fuf-stack/uniform/Switch");
|
|
54
|
+
__fuf_stack_uniform_Switch = __toESM(__fuf_stack_uniform_Switch);
|
|
55
|
+
let __fuf_stack_uniform_Checkboxes = require("@fuf-stack/uniform/Checkboxes");
|
|
56
|
+
__fuf_stack_uniform_Checkboxes = __toESM(__fuf_stack_uniform_Checkboxes);
|
|
57
|
+
|
|
58
|
+
//#region src/Filter/hooks/useFilterValidation.ts
|
|
59
|
+
/**
|
|
60
|
+
* useFilterValidation
|
|
61
|
+
*
|
|
62
|
+
* Builds a composite validation schema from all provided filter definitions
|
|
63
|
+
* under "filter" and optionally includes a "search" string field.
|
|
64
|
+
* Memoized by inputs.
|
|
65
|
+
*/
|
|
66
|
+
const useFilterValidation = (filters$1, withSearch) => {
|
|
67
|
+
return (0, react.useMemo)(() => {
|
|
68
|
+
let filterSchema = {};
|
|
69
|
+
filters$1.forEach((f) => {
|
|
70
|
+
filterSchema = {
|
|
71
|
+
...filterSchema,
|
|
72
|
+
[f.name]: f.validation(f.config)
|
|
73
|
+
};
|
|
74
|
+
});
|
|
75
|
+
return (0, __fuf_stack_veto.veto)({
|
|
76
|
+
filter: (0, __fuf_stack_veto.stringToJSON)().pipe((0, __fuf_stack_veto.object)(filterSchema)).or((0, __fuf_stack_veto.object)(filterSchema)).optional().nullable().transform((val) => {
|
|
77
|
+
return val ?? void 0;
|
|
78
|
+
}),
|
|
79
|
+
...withSearch ? { search: (0, __fuf_stack_veto.string)({ min: 0 }).nullable().optional() } : {}
|
|
80
|
+
});
|
|
81
|
+
}, [filters$1, withSearch]);
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
//#endregion
|
|
85
|
+
//#region src/Filter/Subcomponents/FiltersContext.tsx
|
|
86
|
+
/**
|
|
87
|
+
* FiltersContext
|
|
88
|
+
*
|
|
89
|
+
* Central state for the filter UI with a clear boundary:
|
|
90
|
+
* - The parent component controls committed filter values (via value/onChange)
|
|
91
|
+
* - The form acts as an edit buffer (used by the modal)
|
|
92
|
+
*
|
|
93
|
+
* Design:
|
|
94
|
+
* - activeFilters/unusedFilters are names-only and derived from the controlled
|
|
95
|
+
* form state
|
|
96
|
+
* - getFilterInstanceByName gives access to the concrete registry entry to
|
|
97
|
+
* retrieve the correct Form/Display components
|
|
98
|
+
* - add seeds defaults in the form and opens the modal
|
|
99
|
+
* - remove un-registers the form field; if the removed filter is currently
|
|
100
|
+
* open in the modal, the modal is closed without rollback
|
|
101
|
+
* - on a new successful form submit (Apply), the modal closes without rollback
|
|
102
|
+
* by subscribing to ex-forms submit state
|
|
103
|
+
*/
|
|
104
|
+
const FiltersContext = (0, react.createContext)(void 0);
|
|
105
|
+
const FiltersContextProvider = ({ children, config: config$2 }) => {
|
|
106
|
+
const { formState, getFieldState, setValue, triggerSubmit, unregister, watch } = (0, __fuf_stack_uniform_hooks_useFormContext.useFormContext)();
|
|
107
|
+
/**
|
|
108
|
+
* currentModalFilter
|
|
109
|
+
*
|
|
110
|
+
* Single source of truth for the filter edit modal and its rollback snapshot.
|
|
111
|
+
* - name: which filter's modal is currently open (null when closed)
|
|
112
|
+
* - hadValue/previousValue: snapshot of the controlled value taken when the
|
|
113
|
+
* modal is opened; used to restore state if the user cancels/closes without
|
|
114
|
+
* applying.
|
|
115
|
+
*
|
|
116
|
+
* Lifecycle semantics:
|
|
117
|
+
* - showFilterModal(name): capture snapshot (current controlled value) and open
|
|
118
|
+
* the modal for that filter.
|
|
119
|
+
* - closeFilterModal(): if a snapshot exists, roll back un-applied edits by
|
|
120
|
+
* restoring the previous value (setValue) or removing the field (unregister)
|
|
121
|
+
* when it did not exist before; then clear currentModalFilter.
|
|
122
|
+
* - On successful submit (Apply): close and clear currentModalFilter WITHOUT rollback
|
|
123
|
+
* so edits remain committed.
|
|
124
|
+
* - removeFilter(name): unregisters the field; when removing the filter that is
|
|
125
|
+
* currently open, close the modal WITHOUT rollback (since removal is explicit).
|
|
126
|
+
*/
|
|
127
|
+
const [currentModalFilter, setCurrentModalFilter] = (0, react.useState)(null);
|
|
128
|
+
const filterValue = watch("filter", {});
|
|
129
|
+
/**
|
|
130
|
+
* getFilterFormFieldName
|
|
131
|
+
*
|
|
132
|
+
* Returns the fully-qualified field path for a given filter name,
|
|
133
|
+
* e.g., `${filterUrlParam}.status`.
|
|
134
|
+
*/
|
|
135
|
+
const getFilterFormFieldName = (0, react.useCallback)((name) => {
|
|
136
|
+
return `filter.${name}`;
|
|
137
|
+
}, []);
|
|
138
|
+
/**
|
|
139
|
+
* getFilterValueByName
|
|
140
|
+
*
|
|
141
|
+
* Returns the committed value for a filter from the controlled state.
|
|
142
|
+
*/
|
|
143
|
+
const getFilterValueByName = (0, react.useCallback)((name) => {
|
|
144
|
+
return filterValue[name];
|
|
145
|
+
}, [filterValue]);
|
|
146
|
+
/** Open the filter edit modal for the given filter name. */
|
|
147
|
+
const showFilterModal = (0, react.useCallback)((name) => {
|
|
148
|
+
const prev = getFilterValueByName(name);
|
|
149
|
+
setCurrentModalFilter({
|
|
150
|
+
name,
|
|
151
|
+
hadValue: typeof prev !== "undefined",
|
|
152
|
+
previousValue: prev
|
|
153
|
+
});
|
|
154
|
+
}, [getFilterValueByName]);
|
|
155
|
+
/** Close the filter edit modal. Rollback un-applied edits to controlled state. */
|
|
156
|
+
const closeFilterModal = (0, react.useCallback)(() => {
|
|
157
|
+
if (currentModalFilter?.name) {
|
|
158
|
+
const fieldName = getFilterFormFieldName(currentModalFilter.name);
|
|
159
|
+
if (currentModalFilter.hadValue) setValue(fieldName, currentModalFilter.previousValue);
|
|
160
|
+
else unregister(fieldName);
|
|
161
|
+
}
|
|
162
|
+
setCurrentModalFilter(null);
|
|
163
|
+
}, [
|
|
164
|
+
getFilterFormFieldName,
|
|
165
|
+
currentModalFilter,
|
|
166
|
+
setValue,
|
|
167
|
+
unregister
|
|
168
|
+
]);
|
|
169
|
+
/**
|
|
170
|
+
* Auto-close on submit success
|
|
171
|
+
*
|
|
172
|
+
* Close the modal only on new successful submissions. We track the last
|
|
173
|
+
* submitCount and only react when it changes AND the form reports a
|
|
174
|
+
* successful submit. This prevents closing when `isSubmitSuccessful` remains
|
|
175
|
+
* true without a new submit event.
|
|
176
|
+
*/
|
|
177
|
+
const lastSubmitCountRef = (0, react.useRef)(0);
|
|
178
|
+
(0, react.useEffect)(() => {
|
|
179
|
+
if (formState.submitCount !== lastSubmitCountRef.current && formState.isSubmitSuccessful) setCurrentModalFilter(null);
|
|
180
|
+
lastSubmitCountRef.current = formState.submitCount;
|
|
181
|
+
}, [
|
|
182
|
+
formState.submitCount,
|
|
183
|
+
formState.isSubmitSuccessful,
|
|
184
|
+
setCurrentModalFilter
|
|
185
|
+
]);
|
|
186
|
+
/**
|
|
187
|
+
* activeFilters
|
|
188
|
+
*
|
|
189
|
+
* Filter names derived from the controlled form state. A filter is considered
|
|
190
|
+
* active when a field exists at `filter.<name>`. Newly added filters become
|
|
191
|
+
* active immediately (seeded default), and will be rolled back on cancel.
|
|
192
|
+
*/
|
|
193
|
+
const activeFilters = (0, react.useMemo)(() => {
|
|
194
|
+
return config$2.filter((f) => {
|
|
195
|
+
return Object.hasOwn(filterValue ?? {}, f.name);
|
|
196
|
+
}).map((f) => {
|
|
197
|
+
return f.name;
|
|
198
|
+
});
|
|
199
|
+
}, [config$2, filterValue]);
|
|
200
|
+
/**
|
|
201
|
+
* unusedFilters
|
|
202
|
+
*
|
|
203
|
+
* Complement of activeFilters (names without a corresponding `filter.<name>`
|
|
204
|
+
* field in the controlled form state).
|
|
205
|
+
*/
|
|
206
|
+
const unusedFilters = (0, react.useMemo)(() => {
|
|
207
|
+
return config$2.filter((f) => {
|
|
208
|
+
return !Object.hasOwn(filterValue ?? {}, f.name);
|
|
209
|
+
}).map((f) => {
|
|
210
|
+
return f.name;
|
|
211
|
+
});
|
|
212
|
+
}, [config$2, filterValue]);
|
|
213
|
+
/**
|
|
214
|
+
* getRegistryFilterByName
|
|
215
|
+
*
|
|
216
|
+
* Looks up the concrete registry entry for a filter by name, enabling access
|
|
217
|
+
* to typed Form/Display components and other registry-level metadata.
|
|
218
|
+
*/
|
|
219
|
+
const getFilterInstanceByName = (0, react.useCallback)((name) => {
|
|
220
|
+
return config$2.find((f) => {
|
|
221
|
+
return f.name === name;
|
|
222
|
+
});
|
|
223
|
+
}, [config$2]);
|
|
224
|
+
/**
|
|
225
|
+
* addFilter
|
|
226
|
+
*
|
|
227
|
+
* Seeds the filter with its registry default value inside the form and opens
|
|
228
|
+
* the modal for immediate editing. No URL writes happen here.
|
|
229
|
+
*/
|
|
230
|
+
const addFilter = (0, react.useCallback)((name) => {
|
|
231
|
+
const inst = getFilterInstanceByName(name);
|
|
232
|
+
showFilterModal(name);
|
|
233
|
+
setValue(getFilterFormFieldName(name), inst.defaultValue);
|
|
234
|
+
}, [
|
|
235
|
+
getFilterFormFieldName,
|
|
236
|
+
getFilterInstanceByName,
|
|
237
|
+
setValue,
|
|
238
|
+
showFilterModal
|
|
239
|
+
]);
|
|
240
|
+
/**
|
|
241
|
+
* removeFilter
|
|
242
|
+
*
|
|
243
|
+
* Unregisters the filter field from the form. This immediately removes the
|
|
244
|
+
* filter from the active list since derived state watches the form. It
|
|
245
|
+
* closes the modal without rollback if the removed filter is currently open.
|
|
246
|
+
*/
|
|
247
|
+
const removeFilter = (0, react.useCallback)((name) => {
|
|
248
|
+
unregister(getFilterFormFieldName(name));
|
|
249
|
+
if (currentModalFilter?.name === name) setCurrentModalFilter(null);
|
|
250
|
+
triggerSubmit();
|
|
251
|
+
}, [
|
|
252
|
+
getFilterFormFieldName,
|
|
253
|
+
currentModalFilter,
|
|
254
|
+
setCurrentModalFilter,
|
|
255
|
+
triggerSubmit,
|
|
256
|
+
unregister
|
|
257
|
+
]);
|
|
258
|
+
/**
|
|
259
|
+
* hasError
|
|
260
|
+
*
|
|
261
|
+
* Helper that checks the ex-forms field state for a specific filter and
|
|
262
|
+
* reports whether the field is currently invalid.
|
|
263
|
+
*/
|
|
264
|
+
const hasError = (0, react.useCallback)((name) => {
|
|
265
|
+
return getFieldState(getFilterFormFieldName(name)).invalid;
|
|
266
|
+
}, [getFieldState, getFilterFormFieldName]);
|
|
267
|
+
const contextValue = (0, react.useMemo)(() => {
|
|
268
|
+
return {
|
|
269
|
+
activeFilters,
|
|
270
|
+
addFilter,
|
|
271
|
+
closeFilterModal,
|
|
272
|
+
getFilterFormFieldName,
|
|
273
|
+
getFilterValueByName,
|
|
274
|
+
getFilterInstanceByName,
|
|
275
|
+
hasError,
|
|
276
|
+
modalFilterName: currentModalFilter?.name,
|
|
277
|
+
removeFilter,
|
|
278
|
+
showFilterModal,
|
|
279
|
+
unusedFilters
|
|
280
|
+
};
|
|
281
|
+
}, [
|
|
282
|
+
activeFilters,
|
|
283
|
+
addFilter,
|
|
284
|
+
closeFilterModal,
|
|
285
|
+
getFilterFormFieldName,
|
|
286
|
+
getFilterValueByName,
|
|
287
|
+
getFilterInstanceByName,
|
|
288
|
+
hasError,
|
|
289
|
+
currentModalFilter,
|
|
290
|
+
removeFilter,
|
|
291
|
+
showFilterModal,
|
|
292
|
+
unusedFilters
|
|
293
|
+
]);
|
|
294
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(FiltersContext.Provider, {
|
|
295
|
+
value: contextValue,
|
|
296
|
+
children
|
|
297
|
+
});
|
|
298
|
+
};
|
|
299
|
+
/**
|
|
300
|
+
* useFilters
|
|
301
|
+
*
|
|
302
|
+
* Convenience hook to consume the FiltersContext. Throws a descriptive error
|
|
303
|
+
* when used outside of a FiltersContextProvider to make integration mistakes
|
|
304
|
+
* obvious during development.
|
|
305
|
+
*/
|
|
306
|
+
const useFilters = () => {
|
|
307
|
+
const ctx = (0, react.useContext)(FiltersContext);
|
|
308
|
+
if (!ctx) throw new Error("useFilters must be used within FiltersContextProvider");
|
|
309
|
+
return ctx;
|
|
310
|
+
};
|
|
311
|
+
|
|
312
|
+
//#endregion
|
|
313
|
+
//#region src/Filter/Subcomponents/ActiveFilters.tsx
|
|
314
|
+
const ActiveFilters = ({ className = void 0 }) => {
|
|
315
|
+
const { activeFilters, getFilterValueByName, getFilterInstanceByName, hasError, removeFilter, showFilterModal } = useFilters();
|
|
316
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_jsx_runtime.Fragment, { children: activeFilters.map((name) => {
|
|
317
|
+
const instance = getFilterInstanceByName(name);
|
|
318
|
+
const value = getFilterValueByName(name);
|
|
319
|
+
const DisplayComponent = instance.components.Display;
|
|
320
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
321
|
+
"aria-label": `Open ${name} filter`,
|
|
322
|
+
type: "button",
|
|
323
|
+
onClick: () => {
|
|
324
|
+
showFilterModal(name);
|
|
325
|
+
},
|
|
326
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(__fuf_stack_pixels_Label.default, {
|
|
327
|
+
className,
|
|
328
|
+
color: hasError(name) ? "danger" : "primary",
|
|
329
|
+
variant: "flat",
|
|
330
|
+
onClose: () => {
|
|
331
|
+
removeFilter(name);
|
|
332
|
+
},
|
|
333
|
+
children: [instance.icon, /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DisplayComponent, {
|
|
334
|
+
config: instance.config,
|
|
335
|
+
value
|
|
336
|
+
})]
|
|
337
|
+
})
|
|
338
|
+
}, name);
|
|
339
|
+
}) });
|
|
340
|
+
};
|
|
341
|
+
var ActiveFilters_default = ActiveFilters;
|
|
342
|
+
|
|
343
|
+
//#endregion
|
|
344
|
+
//#region src/Filter/Subcomponents/AddFilterMenu.tsx
|
|
345
|
+
/**
|
|
346
|
+
* AddFilterMenu
|
|
347
|
+
*
|
|
348
|
+
* Renders a menu trigger that opens a list of addable filters. Selecting an
|
|
349
|
+
* item triggers the parent to seed a default value and open the modal.
|
|
350
|
+
*/
|
|
351
|
+
const AddFilterMenu = ({ classNames = {} }) => {
|
|
352
|
+
const { unusedFilters, addFilter, getFilterInstanceByName } = useFilters();
|
|
353
|
+
const menuItems = unusedFilters.map((name) => {
|
|
354
|
+
const instance = getFilterInstanceByName(name);
|
|
355
|
+
const label = instance.config?.text ?? name;
|
|
356
|
+
return {
|
|
357
|
+
key: name,
|
|
358
|
+
icon: instance.icon,
|
|
359
|
+
label,
|
|
360
|
+
onClick: () => {
|
|
361
|
+
addFilter(name);
|
|
362
|
+
}
|
|
363
|
+
};
|
|
364
|
+
});
|
|
365
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(__fuf_stack_pixels_Menu.default, {
|
|
366
|
+
isDisabled: !menuItems.length,
|
|
367
|
+
items: menuItems,
|
|
368
|
+
placement: "bottom-start",
|
|
369
|
+
className: {
|
|
370
|
+
item: classNames.addFilterMenuItem,
|
|
371
|
+
trigger: classNames.addFilterMenuButton
|
|
372
|
+
},
|
|
373
|
+
triggerButtonProps: {
|
|
374
|
+
"aria-label": "Add Filter",
|
|
375
|
+
"data-testid": "add_filter_button",
|
|
376
|
+
disableRipple: true,
|
|
377
|
+
size: "sm",
|
|
378
|
+
variant: "bordered"
|
|
379
|
+
},
|
|
380
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_icons_fa6.FaSliders, {}), "Filter"]
|
|
381
|
+
});
|
|
382
|
+
};
|
|
383
|
+
var AddFilterMenu_default = AddFilterMenu;
|
|
384
|
+
|
|
385
|
+
//#endregion
|
|
386
|
+
//#region src/Filter/Subcomponents/FilterModal.tsx
|
|
387
|
+
const FilterModal = ({ classNames = {} }) => {
|
|
388
|
+
const { closeFilterModal, getFilterFormFieldName, getFilterInstanceByName, modalFilterName, removeFilter } = useFilters();
|
|
389
|
+
if (!modalFilterName) return null;
|
|
390
|
+
const instance = getFilterInstanceByName(modalFilterName);
|
|
391
|
+
const config$2 = instance.config;
|
|
392
|
+
const FormComponent = instance.components.Form;
|
|
393
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__fuf_stack_pixels_Modal.default, {
|
|
394
|
+
isOpen: true,
|
|
395
|
+
onClose: closeFilterModal,
|
|
396
|
+
className: {
|
|
397
|
+
body: classNames.body,
|
|
398
|
+
footer: classNames.footer,
|
|
399
|
+
header: classNames.header
|
|
400
|
+
},
|
|
401
|
+
footer: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(__fuf_stack_pixels_Button.default, {
|
|
402
|
+
ariaLabel: "Remove filter",
|
|
403
|
+
color: "danger",
|
|
404
|
+
testId: "remove_filter_button",
|
|
405
|
+
variant: "flat",
|
|
406
|
+
onClick: () => {
|
|
407
|
+
removeFilter(modalFilterName);
|
|
408
|
+
},
|
|
409
|
+
children: "Remove"
|
|
410
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__fuf_stack_uniform_SubmitButton.default, {
|
|
411
|
+
ariaLabel: "Apply filter",
|
|
412
|
+
testId: "apply_filter_button",
|
|
413
|
+
children: "Apply Filter"
|
|
414
|
+
})] }),
|
|
415
|
+
header: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [instance.icon ?? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_icons_pi.PiSlidersHorizontalBold, {}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { children: `${config$2?.text ?? modalFilterName} Filter` })] }),
|
|
416
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react.Suspense, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(FormComponent, {
|
|
417
|
+
config: config$2,
|
|
418
|
+
fieldName: getFilterFormFieldName(modalFilterName)
|
|
419
|
+
}) })
|
|
420
|
+
});
|
|
421
|
+
};
|
|
422
|
+
var FilterModal_default = FilterModal;
|
|
423
|
+
|
|
424
|
+
//#endregion
|
|
425
|
+
//#region src/Filter/Subcomponents/SearchInput.tsx
|
|
426
|
+
/**
|
|
427
|
+
* SearchInput
|
|
428
|
+
*
|
|
429
|
+
* By default renders only a search button. When clicked, the text input animates in
|
|
430
|
+
* and a trailing submit button is shown.
|
|
431
|
+
*/
|
|
432
|
+
const SearchInput = ({ classNames = {}, config: config$2 }) => {
|
|
433
|
+
const { formState, setFocus, triggerSubmit } = (0, __fuf_stack_uniform_hooks_useFormContext.useFormContext)();
|
|
434
|
+
const isInitiallyVisible = !!formState?.defaultValues?.search;
|
|
435
|
+
const [isVisible, setIsVisible] = (0, react.useState)(isInitiallyVisible);
|
|
436
|
+
const placeholder = typeof config$2 === "object" ? config$2.placeholder : void 0;
|
|
437
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
438
|
+
className: classNames.searchWrapper,
|
|
439
|
+
children: [!isVisible && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__fuf_stack_pixels_Button.default, {
|
|
440
|
+
ariaLabel: "Show search input",
|
|
441
|
+
className: classNames.searchShowButton,
|
|
442
|
+
icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_icons_fa.FaSearch, {}),
|
|
443
|
+
size: "sm",
|
|
444
|
+
testId: "show_search_input_button",
|
|
445
|
+
variant: "bordered",
|
|
446
|
+
onClick: () => {
|
|
447
|
+
setIsVisible(true);
|
|
448
|
+
}
|
|
449
|
+
}), isVisible ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(__fuf_stack_pixel_motion.motion.div, {
|
|
450
|
+
animate: { opacity: 1 },
|
|
451
|
+
className: classNames.searchMotionDiv,
|
|
452
|
+
initial: !isInitiallyVisible ? { opacity: .5 } : false,
|
|
453
|
+
onAnimationComplete: () => {
|
|
454
|
+
if (!isInitiallyVisible) setFocus("search");
|
|
455
|
+
},
|
|
456
|
+
transition: {
|
|
457
|
+
duration: .3,
|
|
458
|
+
ease: "circOut"
|
|
459
|
+
},
|
|
460
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(__fuf_stack_uniform_Input.default, {
|
|
461
|
+
clearable: true,
|
|
462
|
+
debounceDelay: 0,
|
|
463
|
+
name: "search",
|
|
464
|
+
placeholder,
|
|
465
|
+
size: "sm",
|
|
466
|
+
className: {
|
|
467
|
+
input: classNames.searchInput,
|
|
468
|
+
inputWrapper: classNames.searchInputWrapper
|
|
469
|
+
},
|
|
470
|
+
onClear: () => {
|
|
471
|
+
triggerSubmit();
|
|
472
|
+
}
|
|
473
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__fuf_stack_uniform_SubmitButton.default, {
|
|
474
|
+
ariaLabel: "Submit search",
|
|
475
|
+
children: null,
|
|
476
|
+
className: classNames.searchSubmitButton,
|
|
477
|
+
color: "primary",
|
|
478
|
+
icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_icons_fa.FaSearch, {}),
|
|
479
|
+
size: "sm",
|
|
480
|
+
testId: "submit_search_button"
|
|
481
|
+
})]
|
|
482
|
+
}, "search-input") : null]
|
|
483
|
+
});
|
|
484
|
+
};
|
|
485
|
+
var SearchInput_default = SearchInput;
|
|
486
|
+
|
|
487
|
+
//#endregion
|
|
488
|
+
//#region src/Filter/Filter.tsx
|
|
489
|
+
const debug$1 = (0, debug.default)("megapixels:Filter");
|
|
490
|
+
const filterVariants = (0, __fuf_stack_pixel_utils.tv)({ slots: {
|
|
491
|
+
base: "flex flex-auto flex-col",
|
|
492
|
+
addFilterMenuButton: "",
|
|
493
|
+
addFilterMenuItem: "",
|
|
494
|
+
activeFilterLabel: "h-8 cursor-pointer rounded-md dark:text-foreground",
|
|
495
|
+
filterModalBody: "",
|
|
496
|
+
filterModalHeader: "flex items-center gap-3 text-default-700",
|
|
497
|
+
filterModalFooter: "justify-between",
|
|
498
|
+
form: "mb-3 flex flex-wrap gap-3",
|
|
499
|
+
searchInput: "",
|
|
500
|
+
searchInputWrapper: "",
|
|
501
|
+
searchMotionDiv: "flex w-72 gap-2",
|
|
502
|
+
searchShowButton: "",
|
|
503
|
+
searchSubmitButton: "",
|
|
504
|
+
searchWrapper: "flex items-center"
|
|
505
|
+
} });
|
|
506
|
+
/**
|
|
507
|
+
* Renders the filter UI bound to a single ex-forms `Form`.
|
|
508
|
+
* The form is the source of truth during user interaction; the committed
|
|
509
|
+
* state is controlled by the parent via `values`/`onChange`.
|
|
510
|
+
*/
|
|
511
|
+
const Filter = ({ children = void 0, className = void 0, config: config$2, formName = "filterComponentForm", onChange, values }) => {
|
|
512
|
+
const handleSubmit = (nextValues) => {
|
|
513
|
+
debug$1("handleSubmit", { nextValues });
|
|
514
|
+
onChange(nextValues);
|
|
515
|
+
};
|
|
516
|
+
const validation = useFilterValidation(config$2.filters, Boolean(config$2.search));
|
|
517
|
+
const { data: valuesValidated, errors, success } = validation.validate(values);
|
|
518
|
+
const classNames = (0, __fuf_stack_pixel_utils.variantsToClassNames)(filterVariants(), className, "base");
|
|
519
|
+
debug$1("render", {
|
|
520
|
+
props: {
|
|
521
|
+
config: config$2,
|
|
522
|
+
values
|
|
523
|
+
},
|
|
524
|
+
validation: {
|
|
525
|
+
errors,
|
|
526
|
+
success,
|
|
527
|
+
valuesValidated
|
|
528
|
+
}
|
|
529
|
+
});
|
|
530
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
531
|
+
className: classNames.base,
|
|
532
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(__fuf_stack_uniform_Form.default, {
|
|
533
|
+
className: classNames.form,
|
|
534
|
+
debug: { disable: true },
|
|
535
|
+
initialValues: valuesValidated ?? {},
|
|
536
|
+
name: formName,
|
|
537
|
+
onSubmit: handleSubmit,
|
|
538
|
+
validation,
|
|
539
|
+
children: [config$2.search ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SearchInput_default, {
|
|
540
|
+
config: config$2.search,
|
|
541
|
+
classNames: {
|
|
542
|
+
searchInput: classNames.searchInput,
|
|
543
|
+
searchInputWrapper: classNames.searchInputWrapper,
|
|
544
|
+
searchMotionDiv: classNames.searchMotionDiv,
|
|
545
|
+
searchShowButton: classNames.searchShowButton,
|
|
546
|
+
searchSubmitButton: classNames.searchSubmitButton,
|
|
547
|
+
searchWrapper: classNames.searchWrapper
|
|
548
|
+
}
|
|
549
|
+
}) : null, /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(FiltersContextProvider, {
|
|
550
|
+
config: config$2.filters,
|
|
551
|
+
children: [
|
|
552
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ActiveFilters_default, { className: classNames.activeFilterLabel }),
|
|
553
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(AddFilterMenu_default, { classNames: {
|
|
554
|
+
addFilterMenuButton: classNames.addFilterMenuButton,
|
|
555
|
+
addFilterMenuItem: classNames.addFilterMenuItem
|
|
556
|
+
} }),
|
|
557
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(FilterModal_default, { classNames: {
|
|
558
|
+
body: classNames.filterModalBody,
|
|
559
|
+
footer: classNames.filterModalFooter,
|
|
560
|
+
header: classNames.filterModalHeader
|
|
561
|
+
} })
|
|
562
|
+
]
|
|
563
|
+
})]
|
|
564
|
+
}), children?.(valuesValidated ?? {})]
|
|
565
|
+
});
|
|
566
|
+
};
|
|
567
|
+
var Filter_default = Filter;
|
|
568
|
+
|
|
569
|
+
//#endregion
|
|
570
|
+
//#region src/Filter/filters/createFilter.ts
|
|
571
|
+
/**
|
|
572
|
+
* createFilter
|
|
573
|
+
*
|
|
574
|
+
* Builds a filter factory from a static FilterDefinition. The returned factory
|
|
575
|
+
* accepts a usage descriptor (name/icon and optional partial config) and
|
|
576
|
+
* produces a concrete FilterInstance with:
|
|
577
|
+
* - merged config (shallow: definition.defaults.config overlaid by overrides)
|
|
578
|
+
* - Form/Display components
|
|
579
|
+
* - validate function (forwarded from the definition)
|
|
580
|
+
* - defaultValue (forwarded from the definition)
|
|
581
|
+
* - name and icon for UI integration
|
|
582
|
+
*
|
|
583
|
+
* @typeParam Config - Configuration object shape for the filter
|
|
584
|
+
* @typeParam Value - Runtime value type for the filter
|
|
585
|
+
* @param definition - Static description of the filter (components, defaults, validate)
|
|
586
|
+
* @returns FilterFactory that creates FilterInstance<Config, Value>
|
|
587
|
+
*/
|
|
588
|
+
const createFilter = (definition) => {
|
|
589
|
+
return ({ name, icon, config: config$2 }) => {
|
|
590
|
+
return {
|
|
591
|
+
components: definition.components,
|
|
592
|
+
config: {
|
|
593
|
+
...definition.defaults.config,
|
|
594
|
+
...config$2 ?? {}
|
|
595
|
+
},
|
|
596
|
+
defaultValue: definition.defaults.value,
|
|
597
|
+
icon,
|
|
598
|
+
name,
|
|
599
|
+
validation: definition.validation
|
|
600
|
+
};
|
|
601
|
+
};
|
|
602
|
+
};
|
|
603
|
+
var createFilter_default = createFilter;
|
|
604
|
+
|
|
605
|
+
//#endregion
|
|
606
|
+
//#region src/Filter/filters/boolean/Display.tsx
|
|
607
|
+
/**
|
|
608
|
+
* Read-only presentation for the boolean filter.
|
|
609
|
+
* Displays human-readable text based on the current boolean `value`
|
|
610
|
+
* and the provided `config` strings.
|
|
611
|
+
*/
|
|
612
|
+
const Display$1 = ({ value, config: { text, textPrefix, textNoWord } }) => {
|
|
613
|
+
if (typeof value === "boolean") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_jsx_runtime.Fragment, { children: `${value ? textPrefix : `${textPrefix} ${textNoWord ?? "no"}`} ${text}` });
|
|
614
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_jsx_runtime.Fragment, { children: `${text}...` });
|
|
615
|
+
};
|
|
616
|
+
var Display_default$1 = Display$1;
|
|
617
|
+
|
|
618
|
+
//#endregion
|
|
619
|
+
//#region src/Filter/filters/boolean/Form.tsx
|
|
620
|
+
/**
|
|
621
|
+
* Renders the form control for the boolean filter.
|
|
622
|
+
* Uses a `Switch` to toggle the boolean value and composes
|
|
623
|
+
* the label from the provided `config` and `fieldName`.
|
|
624
|
+
*/
|
|
625
|
+
const Form$1 = ({ fieldName, config: { text, textPrefix } }) => {
|
|
626
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__fuf_stack_uniform_Switch.default, {
|
|
627
|
+
label: `${textPrefix} ${text}`,
|
|
628
|
+
name: fieldName
|
|
629
|
+
});
|
|
630
|
+
};
|
|
631
|
+
var Form_default$1 = Form$1;
|
|
632
|
+
|
|
633
|
+
//#endregion
|
|
634
|
+
//#region src/Filter/filters/boolean/schema.ts
|
|
635
|
+
/** configuration of the filter */
|
|
636
|
+
const config$1 = (0, __fuf_stack_veto.object)({
|
|
637
|
+
text: (0, __fuf_stack_veto.string)(),
|
|
638
|
+
textPrefix: (0, __fuf_stack_veto.string)().optional(),
|
|
639
|
+
textNoWord: (0, __fuf_stack_veto.string)().optional()
|
|
640
|
+
});
|
|
641
|
+
/** validate the filter value */
|
|
642
|
+
const validate$1 = (_config) => {
|
|
643
|
+
return (0, __fuf_stack_veto.boolean)().optional();
|
|
644
|
+
};
|
|
645
|
+
|
|
646
|
+
//#endregion
|
|
647
|
+
//#region src/Filter/filters/boolean/boolean.ts
|
|
648
|
+
/**
|
|
649
|
+
* Boolean filter definition for the Filter system.
|
|
650
|
+
* Provides Display and Form components, default value/config, and validation.
|
|
651
|
+
*
|
|
652
|
+
* Defaults:
|
|
653
|
+
* - value: true
|
|
654
|
+
* - config: { text: 'Active', textPrefix: 'is', textNoWord: 'no' }
|
|
655
|
+
*
|
|
656
|
+
* @see Display
|
|
657
|
+
* @see Form
|
|
658
|
+
* @see validate
|
|
659
|
+
*/
|
|
660
|
+
const boolean = createFilter_default({
|
|
661
|
+
components: {
|
|
662
|
+
Display: Display_default$1,
|
|
663
|
+
Form: Form_default$1
|
|
664
|
+
},
|
|
665
|
+
defaults: {
|
|
666
|
+
value: true,
|
|
667
|
+
config: {
|
|
668
|
+
text: "Active",
|
|
669
|
+
textPrefix: "is",
|
|
670
|
+
textNoWord: "no"
|
|
671
|
+
}
|
|
672
|
+
},
|
|
673
|
+
validation: validate$1
|
|
674
|
+
});
|
|
675
|
+
|
|
676
|
+
//#endregion
|
|
677
|
+
//#region src/Filter/filters/checkboxes/Display.tsx
|
|
678
|
+
/**
|
|
679
|
+
* Read-only presentation for the checkboxes filter.
|
|
680
|
+
* Resolves and displays selected option labels based on `value` and `config`.
|
|
681
|
+
* Supports string, ReactNode, and function labels (mode: 'display').
|
|
682
|
+
*/
|
|
683
|
+
const Display = ({ value, config: { text, options } }) => {
|
|
684
|
+
if (value && value.length > 0) return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
685
|
+
className: "flex items-center gap-1",
|
|
686
|
+
children: [
|
|
687
|
+
text,
|
|
688
|
+
" is",
|
|
689
|
+
value.map((val) => {
|
|
690
|
+
const label = options.find((op) => {
|
|
691
|
+
return op.value === val;
|
|
692
|
+
})?.label ?? val;
|
|
693
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: typeof label === "function" ? label("display") : label }, val);
|
|
694
|
+
})
|
|
695
|
+
]
|
|
696
|
+
});
|
|
697
|
+
return `${text} is ...`;
|
|
698
|
+
};
|
|
699
|
+
var Display_default = Display;
|
|
700
|
+
|
|
701
|
+
//#endregion
|
|
702
|
+
//#region src/Filter/filters/checkboxes/Form.tsx
|
|
703
|
+
/**
|
|
704
|
+
* Renders the form control for the checkboxes filter.
|
|
705
|
+
* Uses a `Checkboxes` to select multiple options.
|
|
706
|
+
* Resolves function labels with 'form' mode.
|
|
707
|
+
*/
|
|
708
|
+
const Form = ({ fieldName, config: config$2 }) => {
|
|
709
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__fuf_stack_uniform_Checkboxes.default, {
|
|
710
|
+
name: fieldName,
|
|
711
|
+
options: config$2.options.map((option) => {
|
|
712
|
+
return {
|
|
713
|
+
...option,
|
|
714
|
+
label: typeof option.label === "function" ? option.label("form") : option.label
|
|
715
|
+
};
|
|
716
|
+
})
|
|
717
|
+
});
|
|
718
|
+
};
|
|
719
|
+
var Form_default = Form;
|
|
720
|
+
|
|
721
|
+
//#endregion
|
|
722
|
+
//#region src/Filter/filters/checkboxes/schema.ts
|
|
723
|
+
/** configuration of the filter */
|
|
724
|
+
const config = (0, __fuf_stack_veto.object)({
|
|
725
|
+
text: (0, __fuf_stack_veto.string)(),
|
|
726
|
+
options: (0, __fuf_stack_veto.array)((0, __fuf_stack_veto.object)({
|
|
727
|
+
label: (0, __fuf_stack_veto.any)(),
|
|
728
|
+
value: (0, __fuf_stack_veto.string)()
|
|
729
|
+
}))
|
|
730
|
+
});
|
|
731
|
+
/** validate the filter value */
|
|
732
|
+
const validate = (cfg) => {
|
|
733
|
+
return (0, __fuf_stack_veto.refineArray)((0, __fuf_stack_veto.array)((0, __fuf_stack_veto.string)()).optional())({
|
|
734
|
+
unique: true,
|
|
735
|
+
custom: (values, ctx) => {
|
|
736
|
+
if (!cfg) return;
|
|
737
|
+
values.forEach((value) => {
|
|
738
|
+
if (!cfg.options.find((option) => {
|
|
739
|
+
return option?.value === value;
|
|
740
|
+
})) ctx.addIssue({
|
|
741
|
+
code: "custom",
|
|
742
|
+
message: `Invalid value: ${value}`
|
|
743
|
+
});
|
|
744
|
+
});
|
|
745
|
+
}
|
|
746
|
+
});
|
|
747
|
+
};
|
|
748
|
+
|
|
749
|
+
//#endregion
|
|
750
|
+
//#region src/Filter/filters/checkboxes/checkboxes.ts
|
|
751
|
+
/**
|
|
752
|
+
* Checkboxes filter definition for the Filter system.
|
|
753
|
+
* Provides Display and Form components, default value/config, and validation.
|
|
754
|
+
*
|
|
755
|
+
* Defaults:
|
|
756
|
+
* - value: []
|
|
757
|
+
* - config: { text: 'Options', options: [] }
|
|
758
|
+
*
|
|
759
|
+
* @see Display
|
|
760
|
+
* @see Form
|
|
761
|
+
* @see validate
|
|
762
|
+
*/
|
|
763
|
+
const checkboxes = createFilter_default({
|
|
764
|
+
components: {
|
|
765
|
+
Display: Display_default,
|
|
766
|
+
Form: Form_default
|
|
767
|
+
},
|
|
768
|
+
defaults: {
|
|
769
|
+
value: [],
|
|
770
|
+
config: {
|
|
771
|
+
text: "Options",
|
|
772
|
+
options: []
|
|
773
|
+
}
|
|
774
|
+
},
|
|
775
|
+
validation: validate
|
|
776
|
+
});
|
|
777
|
+
|
|
778
|
+
//#endregion
|
|
779
|
+
//#region src/Filter/index.ts
|
|
780
|
+
const filters = {
|
|
781
|
+
boolean,
|
|
782
|
+
checkboxes
|
|
783
|
+
};
|
|
784
|
+
var Filter_default$1 = Filter_default;
|
|
785
|
+
|
|
786
|
+
//#endregion
|
|
787
|
+
Object.defineProperty(exports, 'Filter_default', {
|
|
788
|
+
enumerable: true,
|
|
789
|
+
get: function () {
|
|
790
|
+
return Filter_default$1;
|
|
791
|
+
}
|
|
792
|
+
});
|
|
793
|
+
Object.defineProperty(exports, 'createFilter_default', {
|
|
794
|
+
enumerable: true,
|
|
795
|
+
get: function () {
|
|
796
|
+
return createFilter_default;
|
|
797
|
+
}
|
|
798
|
+
});
|
|
799
|
+
Object.defineProperty(exports, 'filterVariants', {
|
|
800
|
+
enumerable: true,
|
|
801
|
+
get: function () {
|
|
802
|
+
return filterVariants;
|
|
803
|
+
}
|
|
804
|
+
});
|
|
805
|
+
Object.defineProperty(exports, 'filters', {
|
|
806
|
+
enumerable: true,
|
|
807
|
+
get: function () {
|
|
808
|
+
return filters;
|
|
809
|
+
}
|
|
810
|
+
});
|
|
811
|
+
//# sourceMappingURL=Filter-Bc0LWCnZ.cjs.map
|