@akinon/akifilter 1.1.1-rc.0 → 1.2.0-next.10
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/cjs/akifilter.d.ts.map +1 -1
- package/dist/cjs/akifilter.js +136 -26
- package/dist/cjs/types.d.ts +16 -0
- package/dist/cjs/types.d.ts.map +1 -1
- package/dist/esm/akifilter.d.ts.map +1 -1
- package/dist/esm/akifilter.js +138 -28
- package/dist/esm/types.d.ts +16 -0
- package/dist/esm/types.d.ts.map +1 -1
- package/package.json +19 -19
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"akifilter.d.ts","sourceRoot":"","sources":["../../src/akifilter.tsx"],"names":[],"mappings":"AAAA,OAAO,cAAc,CAAC;AAGtB,OAAO,EAIL,WAAW,
|
|
1
|
+
{"version":3,"file":"akifilter.d.ts","sourceRoot":"","sources":["../../src/akifilter.tsx"],"names":[],"mappings":"AAAA,OAAO,cAAc,CAAC;AAGtB,OAAO,EAIL,WAAW,EACX,IAAI,EAIL,MAAM,iBAAiB,CAAC;AAWzB,OAAO,KAAK,MAAM,OAAO,CAAC;AA0B1B,OAAO,KAAK,EAAkB,eAAe,EAAE,MAAM,SAAS,CAAC;AAY/D,KAAK,oBAAoB,GAAG,WAAW,CAAC;AA0HxC,MAAM,MAAM,cAAc,CACxB,YAAY,SAAS,oBAAoB,GAAG,oBAAoB,IAC9D;IACF;;OAEG;IACH,YAAY,CAAC,EAAE,eAAe,CAAC,YAAY,CAAC,CAAC;IAC7C;;OAEG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B;;OAEG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC,YAAY,CAAC,CAAC;IACtC;;OAEG;IACH,cAAc,CAAC,EAAE,CAAC,MAAM,EAAE,OAAO,CAAC,YAAY,CAAC,KAAK,IAAI,CAAC;IACzD;;OAEG;IACH,qBAAqB,CAAC,EAAE,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,KAAK,IAAI,CAAC;IAClE;;OAEG;IACH,WAAW,CAAC,EAAE,MAAM,IAAI,CAAC;IACzB;;OAEG;IACH,WAAW,CAAC,EAAE,MAAM,IAAI,CAAC;IACzB;;OAEG;IACH,UAAU,CAAC,EAAE,MAAM,IAAI,CAAC;IACxB;;OAEG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B;;OAEG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B,CAAC;AAswBF,eAAO,MAAM,SAAS;KACpB,YAAY,SAAS,oBAAoB,uBAElC,cAAc,CAAC,YAAY,CAAC;;CAmBpC,CAAC"}
|
package/dist/cjs/akifilter.js
CHANGED
|
@@ -37,6 +37,54 @@ const use_debounced_value_1 = require("./hooks/use-debounced-value");
|
|
|
37
37
|
const i18n_1 = require("./i18n");
|
|
38
38
|
const schema_1 = require("./utils/schema");
|
|
39
39
|
const values_1 = require("./utils/values");
|
|
40
|
+
const FilterFormItem = (_a) => {
|
|
41
|
+
var _b, _c;
|
|
42
|
+
var { children, control, name, valuePropName } = _a, props = __rest(_a, ["children", "control", "name", "valuePropName"]);
|
|
43
|
+
const { field, fieldState } = (0, akiform_1.useController)({ name, control });
|
|
44
|
+
const childrenWithProps = react_1.default.Children.map(children, child => {
|
|
45
|
+
if (react_1.default.isValidElement(child)) {
|
|
46
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
47
|
+
const typedChild = child;
|
|
48
|
+
return react_1.default.cloneElement(typedChild, Object.assign(Object.assign(Object.assign({}, field), {
|
|
49
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
50
|
+
onChange: (...params) => {
|
|
51
|
+
var _a, _b;
|
|
52
|
+
(_b = (_a = typedChild.props).onChange) === null || _b === void 0 ? void 0 : _b.call(_a, params);
|
|
53
|
+
field.onChange(...params);
|
|
54
|
+
}, onBlur: () => {
|
|
55
|
+
var _a, _b;
|
|
56
|
+
(_b = (_a = typedChild.props).onBlur) === null || _b === void 0 ? void 0 : _b.call(_a);
|
|
57
|
+
field.onBlur();
|
|
58
|
+
}, ref: undefined }), (valuePropName && {
|
|
59
|
+
[valuePropName]: field.value
|
|
60
|
+
})));
|
|
61
|
+
}
|
|
62
|
+
return child;
|
|
63
|
+
});
|
|
64
|
+
return (react_1.default.createElement(akiform_1.Akiform.Item, Object.assign({}, props, { validateStatus: fieldState.invalid ? 'error' : undefined, help: (_c = (_b = fieldState.error) === null || _b === void 0 ? void 0 : _b.message) !== null && _c !== void 0 ? _c : props.help, className: props.className }), childrenWithProps));
|
|
65
|
+
};
|
|
66
|
+
/**
|
|
67
|
+
* Checks if a field should be disabled based on config.disabled property.
|
|
68
|
+
*/
|
|
69
|
+
const checkIsDisabled = (field, formValues) => {
|
|
70
|
+
var _a;
|
|
71
|
+
const configDisabled = (_a = field.config) === null || _a === void 0 ? void 0 : _a.disabled;
|
|
72
|
+
if (typeof configDisabled === 'function') {
|
|
73
|
+
return configDisabled(formValues);
|
|
74
|
+
}
|
|
75
|
+
return Boolean(configDisabled);
|
|
76
|
+
};
|
|
77
|
+
/**
|
|
78
|
+
* Checks if a field should be visible based on config.visible property.
|
|
79
|
+
*/
|
|
80
|
+
const checkIsVisible = (field, formValues) => {
|
|
81
|
+
var _a;
|
|
82
|
+
const configVisible = (_a = field.config) === null || _a === void 0 ? void 0 : _a.visible;
|
|
83
|
+
if (typeof configVisible === 'function') {
|
|
84
|
+
return configVisible(formValues);
|
|
85
|
+
}
|
|
86
|
+
return configVisible !== false;
|
|
87
|
+
};
|
|
40
88
|
const resolveClearedFieldValue = (field, defaultValue) => {
|
|
41
89
|
if (defaultValue !== undefined) {
|
|
42
90
|
return defaultValue;
|
|
@@ -64,9 +112,15 @@ const AkifilterContent = ({ filterSchema, storageNamespace, defaultValues, onVal
|
|
|
64
112
|
const schemaDefaults = react_1.default.useMemo(() => (0, schema_1.normaliseValuesBySchema)(flattenedSchema, (0, schema_1.extractDefaultValues)(flattenedSchema)), [flattenedSchema]);
|
|
65
113
|
const externalDefaults = react_1.default.useMemo(() => (0, schema_1.normaliseValuesBySchema)(flattenedSchema, defaultValues), [flattenedSchema, defaultValues]);
|
|
66
114
|
const baseDefaultValues = react_1.default.useMemo(() => (Object.assign(Object.assign({}, schemaDefaults), externalDefaults)), [schemaDefaults, externalDefaults]);
|
|
67
|
-
|
|
115
|
+
// Use state for persistedDefaults to make it reactive to localStorage changes
|
|
116
|
+
const [persistedDefaults, setPersistedDefaults] = react_1.default.useState(() => {
|
|
68
117
|
var _a;
|
|
69
118
|
return (0, schema_1.normaliseValuesBySchema)(flattenedSchema, (_a = (0, storage_1.readStoredValues)(regularFields, storageKey)) !== null && _a !== void 0 ? _a : undefined);
|
|
119
|
+
});
|
|
120
|
+
// Re-read from localStorage when storageKey changes
|
|
121
|
+
react_1.default.useEffect(() => {
|
|
122
|
+
var _a;
|
|
123
|
+
setPersistedDefaults((0, schema_1.normaliseValuesBySchema)(flattenedSchema, (_a = (0, storage_1.readStoredValues)(regularFields, storageKey)) !== null && _a !== void 0 ? _a : undefined));
|
|
70
124
|
}, [flattenedSchema, regularFields, storageKey]);
|
|
71
125
|
const mergedDefaultValues = react_1.default.useMemo(() => (Object.assign(Object.assign({}, baseDefaultValues), persistedDefaults)), [baseDefaultValues, persistedDefaults]);
|
|
72
126
|
const formMethods = (0, akiform_1.useForm)({
|
|
@@ -109,12 +163,17 @@ const AkifilterContent = ({ filterSchema, storageNamespace, defaultValues, onVal
|
|
|
109
163
|
if (field.type === 'select') {
|
|
110
164
|
return resolveSelectLabel();
|
|
111
165
|
}
|
|
166
|
+
// For custom fields with options (like custom selects), try to find the label
|
|
167
|
+
if (field.type === 'custom' && 'options' in field && field.options) {
|
|
168
|
+
return resolveSelectLabel();
|
|
169
|
+
}
|
|
112
170
|
if (field.type === 'date') {
|
|
113
171
|
const iso = akidate_1.akidate.toIsoDate(currentValue);
|
|
114
172
|
if (iso) {
|
|
115
173
|
// Use localized format with time if showTime is enabled
|
|
116
174
|
const hasShowTime = 'showTime' in field && field.showTime;
|
|
117
|
-
|
|
175
|
+
// L = localized date, LTS = localized time with seconds
|
|
176
|
+
const format = hasShowTime ? 'L LTS' : 'L';
|
|
118
177
|
return akidate_1.akidate.formatIsoDate(iso, format);
|
|
119
178
|
}
|
|
120
179
|
}
|
|
@@ -130,7 +189,8 @@ const AkifilterContent = ({ filterSchema, storageNamespace, defaultValues, onVal
|
|
|
130
189
|
}
|
|
131
190
|
const iso = akidate_1.akidate.toIsoDate(currentValue);
|
|
132
191
|
if (iso) {
|
|
133
|
-
|
|
192
|
+
// Use localized date format for fallback
|
|
193
|
+
return akidate_1.akidate.formatIsoDate(iso, 'L');
|
|
134
194
|
}
|
|
135
195
|
return String(currentValue);
|
|
136
196
|
};
|
|
@@ -159,7 +219,20 @@ const AkifilterContent = ({ filterSchema, storageNamespace, defaultValues, onVal
|
|
|
159
219
|
(0, storage_1.writeVisibleKeys)(storageKey, visibleKeys);
|
|
160
220
|
onVisibleFieldsChange === null || onVisibleFieldsChange === void 0 ? void 0 : onVisibleFieldsChange(visibleKeys);
|
|
161
221
|
}, [visibleKeys, onVisibleFieldsChange, storageKey]);
|
|
222
|
+
// Track previous mergedDefaultValues to avoid unnecessary resets
|
|
223
|
+
const previousMergedDefaultValuesRef = react_1.default.useRef(null);
|
|
162
224
|
react_1.default.useEffect(() => {
|
|
225
|
+
const serialised = JSON.stringify(mergedDefaultValues);
|
|
226
|
+
// Skip if the values haven't actually changed (deep comparison)
|
|
227
|
+
if (previousMergedDefaultValuesRef.current === serialised) {
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
// Skip reset on initial mount - useForm already uses defaultValues
|
|
231
|
+
if (previousMergedDefaultValuesRef.current === null) {
|
|
232
|
+
previousMergedDefaultValuesRef.current = serialised;
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
previousMergedDefaultValuesRef.current = serialised;
|
|
163
236
|
formMethods.reset(mergedDefaultValues);
|
|
164
237
|
}, [formMethods, mergedDefaultValues]);
|
|
165
238
|
const normalisedValues = react_1.default.useMemo(() => (0, values_1.normaliseOutputValues)(flattenedSchema, formValues), [flattenedSchema, formValues]);
|
|
@@ -217,11 +290,43 @@ const AkifilterContent = ({ filterSchema, storageNamespace, defaultValues, onVal
|
|
|
217
290
|
react_1.default.useEffect(() => {
|
|
218
291
|
lastPersistedValuesRef.current = null;
|
|
219
292
|
}, [storageKey]);
|
|
293
|
+
// Track previous serialised values to detect field removals
|
|
294
|
+
const previousSerialisedValuesRef = react_1.default.useRef(serialisedValues);
|
|
295
|
+
// Immediate effect for field removals (bypasses debounce)
|
|
296
|
+
react_1.default.useEffect(() => {
|
|
297
|
+
const previous = previousSerialisedValuesRef.current;
|
|
298
|
+
const current = serialisedValues;
|
|
299
|
+
previousSerialisedValuesRef.current = current;
|
|
300
|
+
// Skip if this is the initial render
|
|
301
|
+
if (!hasEmittedValuesRef.current) {
|
|
302
|
+
return;
|
|
303
|
+
}
|
|
304
|
+
// Skip if values haven't actually changed
|
|
305
|
+
if (previous === current) {
|
|
306
|
+
return;
|
|
307
|
+
}
|
|
308
|
+
const prevParsed = JSON.parse(previous);
|
|
309
|
+
const currParsed = JSON.parse(current);
|
|
310
|
+
// Detect if a field was removed (existed in prev but not in current)
|
|
311
|
+
const prevKeys = Object.keys(prevParsed);
|
|
312
|
+
const currKeys = Object.keys(currParsed);
|
|
313
|
+
const removedKeys = prevKeys.filter(key => !currKeys.includes(key));
|
|
314
|
+
// If a field was removed, immediately persist and emit (bypass debounce)
|
|
315
|
+
if (removedKeys.length > 0) {
|
|
316
|
+
const nextSerialised = current;
|
|
317
|
+
persistValues(currParsed, nextSerialised);
|
|
318
|
+
lastPersistedValuesRef.current = nextSerialised;
|
|
319
|
+
currentSerialisedValuesRef.current = nextSerialised;
|
|
320
|
+
// Update persistedDefaults to prevent stale mergedDefaultValues on re-render
|
|
321
|
+
setPersistedDefaults(currParsed);
|
|
322
|
+
onValuesChange === null || onValuesChange === void 0 ? void 0 : onValuesChange(currParsed);
|
|
323
|
+
}
|
|
324
|
+
}, [serialisedValues, onValuesChange, persistValues]);
|
|
220
325
|
const handleClearAll = react_1.default.useCallback(() => {
|
|
221
|
-
const clearedDefaults = Object.assign({},
|
|
326
|
+
const clearedDefaults = Object.assign({}, schemaDefaults);
|
|
222
327
|
flattenedSchema.forEach(field => {
|
|
223
328
|
const key = String(field.key);
|
|
224
|
-
const defaultValue =
|
|
329
|
+
const defaultValue = schemaDefaults[key];
|
|
225
330
|
const resolved = resolveClearedFieldValue(field, defaultValue);
|
|
226
331
|
if (resolved === undefined) {
|
|
227
332
|
delete clearedDefaults[key];
|
|
@@ -242,7 +347,7 @@ const AkifilterContent = ({ filterSchema, storageNamespace, defaultValues, onVal
|
|
|
242
347
|
lastPersistedValuesRef.current = nextSerialised !== null && nextSerialised !== void 0 ? nextSerialised : null;
|
|
243
348
|
onValuesChange === null || onValuesChange === void 0 ? void 0 : onValuesChange(nextValues);
|
|
244
349
|
}, [
|
|
245
|
-
|
|
350
|
+
schemaDefaults,
|
|
246
351
|
flattenedSchema,
|
|
247
352
|
formMethods,
|
|
248
353
|
onClearAll,
|
|
@@ -274,31 +379,33 @@ const AkifilterContent = ({ filterSchema, storageNamespace, defaultValues, onVal
|
|
|
274
379
|
if (!schemaField) {
|
|
275
380
|
return;
|
|
276
381
|
}
|
|
277
|
-
const defaultValue =
|
|
278
|
-
const fieldPath = schemaField.key;
|
|
382
|
+
const defaultValue = schemaDefaults[String(schemaField.key)];
|
|
279
383
|
const nextValue = resolveClearedFieldValue(schemaField, defaultValue);
|
|
280
|
-
//
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
});
|
|
286
|
-
// Compute the updated values immediately (don't wait for debounce)
|
|
287
|
-
const currentValues = formMethods.getValues() || {};
|
|
288
|
-
const updatedFormValues = Object.assign(Object.assign({}, currentValues), { [fieldPath]: nextValue });
|
|
384
|
+
// Manually construct the updated form values instead of relying on setValue + getValues
|
|
385
|
+
// because setValue is batched and getValues() wouldn't reflect the change immediately
|
|
386
|
+
const currentFormValues = formMethods.getValues();
|
|
387
|
+
const updatedFormValues = Object.assign(Object.assign({}, currentFormValues), { [String(schemaField.key)]: nextValue });
|
|
388
|
+
// Normalize the values (removes empty/undefined entries)
|
|
289
389
|
const nextValues = (0, values_1.normaliseOutputValues)(flattenedSchema, updatedFormValues);
|
|
390
|
+
// Update form state
|
|
391
|
+
formMethods.reset(updatedFormValues, {
|
|
392
|
+
keepDirty: false,
|
|
393
|
+
keepTouched: false,
|
|
394
|
+
keepValues: false
|
|
395
|
+
});
|
|
290
396
|
const nextSerialised = JSON.stringify(nextValues);
|
|
291
397
|
// Persist immediately (bypass debounce for remove action)
|
|
292
398
|
persistValues(nextValues);
|
|
293
399
|
// Update refs to mark this as the latest persisted state
|
|
294
400
|
lastPersistedValuesRef.current = nextSerialised;
|
|
295
401
|
currentSerialisedValuesRef.current = nextSerialised;
|
|
402
|
+
previousSerialisedValuesRef.current = nextSerialised; // Prevent immediate effect from re-emitting
|
|
296
403
|
hasEmittedValuesRef.current = true;
|
|
297
404
|
hasInitialValuesRef.current = Object.keys(nextValues).length > 0;
|
|
298
405
|
// Emit the change to parent
|
|
299
406
|
onValuesChange === null || onValuesChange === void 0 ? void 0 : onValuesChange(nextValues);
|
|
300
407
|
}, [
|
|
301
|
-
|
|
408
|
+
schemaDefaults,
|
|
302
409
|
flattenedSchema,
|
|
303
410
|
formMethods,
|
|
304
411
|
onValuesChange,
|
|
@@ -320,22 +427,25 @@ const AkifilterContent = ({ filterSchema, storageNamespace, defaultValues, onVal
|
|
|
320
427
|
const start = (modalPage - 1) * modalPageSize;
|
|
321
428
|
return filteredFields.slice(start, start + modalPageSize);
|
|
322
429
|
}, [filteredFields, modalPage, modalPageSize]);
|
|
323
|
-
const visibleFields = react_1.default.useMemo(() => regularFields
|
|
430
|
+
const visibleFields = react_1.default.useMemo(() => regularFields
|
|
431
|
+
.filter(field => visibleKeys.includes(String(field.key)))
|
|
432
|
+
.filter(field => checkIsVisible(field, formValues !== null && formValues !== void 0 ? formValues : {})), [regularFields, visibleKeys, formValues]);
|
|
324
433
|
const renderFieldComponent = (field) => {
|
|
325
434
|
const ariaLabel = (0, schema_1.getFieldAriaLabel)(field);
|
|
435
|
+
const isDisabled = checkIsDisabled(field, formValues !== null && formValues !== void 0 ? formValues : {});
|
|
326
436
|
switch (field.type) {
|
|
327
437
|
case 'text':
|
|
328
|
-
return (react_1.default.createElement(ui_input_1.Input, { placeholder: field.placeholder, size: "large", allowClear: true, "aria-label": ariaLabel }));
|
|
438
|
+
return (react_1.default.createElement(ui_input_1.Input, { placeholder: field.placeholder, size: "large", allowClear: true, "aria-label": ariaLabel, disabled: isDisabled }));
|
|
329
439
|
case 'number':
|
|
330
|
-
return (react_1.default.createElement(ui_input_number_1.InputNumber, { placeholder: field.placeholder, size: "large", className: "akinon-filter__field--number", "aria-label": ariaLabel }));
|
|
440
|
+
return (react_1.default.createElement(ui_input_number_1.InputNumber, { placeholder: field.placeholder, size: "large", className: "akinon-filter__field--number", "aria-label": ariaLabel, disabled: isDisabled }));
|
|
331
441
|
case 'select':
|
|
332
|
-
return (react_1.default.createElement(ui_select_1.Select, { placeholder: field.placeholder, size: "large", options: field.options, showSearch: true, optionFilterProp: "label", "aria-label": ariaLabel }));
|
|
442
|
+
return (react_1.default.createElement(ui_select_1.Select, { placeholder: field.placeholder, size: "large", options: field.options, showSearch: true, optionFilterProp: "label", "aria-label": ariaLabel, disabled: isDisabled }));
|
|
333
443
|
case 'checkbox':
|
|
334
|
-
return react_1.default.createElement(ui_checkbox_1.Checkbox,
|
|
444
|
+
return react_1.default.createElement(ui_checkbox_1.Checkbox, { disabled: isDisabled }, field.label);
|
|
335
445
|
case 'date':
|
|
336
|
-
return (react_1.default.createElement(ui_date_picker_1.DatePicker, { placeholder: field.placeholder, showTime: field.showTime, suffixIcon: "calendar", suffixIconSize: "16px", "aria-label": ariaLabel }));
|
|
446
|
+
return (react_1.default.createElement(ui_date_picker_1.DatePicker, { placeholder: field.placeholder, showTime: field.showTime, suffixIcon: "calendar", suffixIconSize: "16px", "aria-label": ariaLabel, disabled: isDisabled }));
|
|
337
447
|
case 'textarea':
|
|
338
|
-
return (react_1.default.createElement(ui_input_1.InputTextArea, { placeholder: field.placeholder, autoSize: { minRows: 3, maxRows: 6 }, "aria-label": ariaLabel }));
|
|
448
|
+
return (react_1.default.createElement(ui_input_1.InputTextArea, { placeholder: field.placeholder, autoSize: { minRows: 3, maxRows: 6 }, "aria-label": ariaLabel, disabled: isDisabled }));
|
|
339
449
|
case 'custom':
|
|
340
450
|
if (typeof field.render === 'function') {
|
|
341
451
|
return field.render({
|
|
@@ -356,7 +466,7 @@ const AkifilterContent = ({ filterSchema, storageNamespace, defaultValues, onVal
|
|
|
356
466
|
}
|
|
357
467
|
};
|
|
358
468
|
const renderFormField = (field) => {
|
|
359
|
-
return (react_1.default.createElement(
|
|
469
|
+
return (react_1.default.createElement(FilterFormItem, { key: String(field.key), control: formMethods.control, name: field.key, tooltip: field.tooltip, help: field.help, labelDescription: field.labelDescription, required: Boolean(field.validation), className: "mb-0", valuePropName: field.type === 'checkbox' ? 'checked' : undefined }, renderFieldComponent(field)));
|
|
360
470
|
};
|
|
361
471
|
return (react_1.default.createElement(ui_card_1.Card, { size: "small", className: "akinon-filter shadow", "data-testid": "akifilter-root" },
|
|
362
472
|
react_1.default.createElement(antd_1.ConfigProvider, { theme: theme_overrides_1.themeOverrides },
|
package/dist/cjs/types.d.ts
CHANGED
|
@@ -1,10 +1,26 @@
|
|
|
1
1
|
import type { FieldValues, Path } from '@akinon/akiform';
|
|
2
2
|
import type { FormField } from '@akinon/akiform-builder';
|
|
3
|
+
export interface AkifilterFieldConfig<TFieldValues extends FieldValues = FieldValues> {
|
|
4
|
+
/**
|
|
5
|
+
* Controls whether the field is disabled.
|
|
6
|
+
* Can be a boolean or a function that receives form values and returns a boolean.
|
|
7
|
+
*/
|
|
8
|
+
disabled?: boolean | ((formValues: TFieldValues) => boolean);
|
|
9
|
+
/**
|
|
10
|
+
* Controls whether the field is visible.
|
|
11
|
+
* Can be a boolean or a function that receives form values and returns a boolean.
|
|
12
|
+
*/
|
|
13
|
+
visible?: boolean | ((formValues: TFieldValues) => boolean);
|
|
14
|
+
}
|
|
3
15
|
export type AkifilterField<TFieldValues extends FieldValues = FieldValues> = FormField<TFieldValues> & {
|
|
4
16
|
/**
|
|
5
17
|
* Controls whether the field is shown in the main filter form by default.
|
|
6
18
|
*/
|
|
7
19
|
isVisible?: boolean;
|
|
20
|
+
/**
|
|
21
|
+
* Configuration options for field behavior (disabled, visible).
|
|
22
|
+
*/
|
|
23
|
+
config?: AkifilterFieldConfig<TFieldValues>;
|
|
8
24
|
};
|
|
9
25
|
export type AkifilterFieldKey<TFieldValues extends FieldValues = FieldValues> = Path<TFieldValues>;
|
|
10
26
|
export type AkifilterSchema<TFieldValues extends FieldValues = FieldValues> = AkifilterField<TFieldValues>[];
|
package/dist/cjs/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AACzD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AAEzD,MAAM,MAAM,cAAc,CAAC,YAAY,SAAS,WAAW,GAAG,WAAW,IACvE,SAAS,CAAC,YAAY,CAAC,GAAG;IACxB;;OAEG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AACzD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AAEzD,MAAM,WAAW,oBAAoB,CACnC,YAAY,SAAS,WAAW,GAAG,WAAW;IAE9C;;;OAGG;IACH,QAAQ,CAAC,EAAE,OAAO,GAAG,CAAC,CAAC,UAAU,EAAE,YAAY,KAAK,OAAO,CAAC,CAAC;IAC7D;;;OAGG;IACH,OAAO,CAAC,EAAE,OAAO,GAAG,CAAC,CAAC,UAAU,EAAE,YAAY,KAAK,OAAO,CAAC,CAAC;CAC7D;AAED,MAAM,MAAM,cAAc,CAAC,YAAY,SAAS,WAAW,GAAG,WAAW,IACvE,SAAS,CAAC,YAAY,CAAC,GAAG;IACxB;;OAEG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB;;OAEG;IACH,MAAM,CAAC,EAAE,oBAAoB,CAAC,YAAY,CAAC,CAAC;CAC7C,CAAC;AAEJ,MAAM,MAAM,iBAAiB,CAAC,YAAY,SAAS,WAAW,GAAG,WAAW,IAC1E,IAAI,CAAC,YAAY,CAAC,CAAC;AAErB,MAAM,MAAM,eAAe,CAAC,YAAY,SAAS,WAAW,GAAG,WAAW,IACxE,cAAc,CAAC,YAAY,CAAC,EAAE,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"akifilter.d.ts","sourceRoot":"","sources":["../../src/akifilter.tsx"],"names":[],"mappings":"AAAA,OAAO,cAAc,CAAC;AAGtB,OAAO,EAIL,WAAW,
|
|
1
|
+
{"version":3,"file":"akifilter.d.ts","sourceRoot":"","sources":["../../src/akifilter.tsx"],"names":[],"mappings":"AAAA,OAAO,cAAc,CAAC;AAGtB,OAAO,EAIL,WAAW,EACX,IAAI,EAIL,MAAM,iBAAiB,CAAC;AAWzB,OAAO,KAAK,MAAM,OAAO,CAAC;AA0B1B,OAAO,KAAK,EAAkB,eAAe,EAAE,MAAM,SAAS,CAAC;AAY/D,KAAK,oBAAoB,GAAG,WAAW,CAAC;AA0HxC,MAAM,MAAM,cAAc,CACxB,YAAY,SAAS,oBAAoB,GAAG,oBAAoB,IAC9D;IACF;;OAEG;IACH,YAAY,CAAC,EAAE,eAAe,CAAC,YAAY,CAAC,CAAC;IAC7C;;OAEG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B;;OAEG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC,YAAY,CAAC,CAAC;IACtC;;OAEG;IACH,cAAc,CAAC,EAAE,CAAC,MAAM,EAAE,OAAO,CAAC,YAAY,CAAC,KAAK,IAAI,CAAC;IACzD;;OAEG;IACH,qBAAqB,CAAC,EAAE,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,KAAK,IAAI,CAAC;IAClE;;OAEG;IACH,WAAW,CAAC,EAAE,MAAM,IAAI,CAAC;IACzB;;OAEG;IACH,WAAW,CAAC,EAAE,MAAM,IAAI,CAAC;IACzB;;OAEG;IACH,UAAU,CAAC,EAAE,MAAM,IAAI,CAAC;IACxB;;OAEG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B;;OAEG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B,CAAC;AAswBF,eAAO,MAAM,SAAS;KACpB,YAAY,SAAS,oBAAoB,uBAElC,cAAc,CAAC,YAAY,CAAC;;CAmBpC,CAAC"}
|
package/dist/esm/akifilter.js
CHANGED
|
@@ -11,7 +11,7 @@ var __rest = (this && this.__rest) || function (s, e) {
|
|
|
11
11
|
};
|
|
12
12
|
import './styles.css';
|
|
13
13
|
import { akidate } from '@akinon/akidate';
|
|
14
|
-
import { Akiform,
|
|
14
|
+
import { Akiform, useController, useForm, useWatch } from '@akinon/akiform';
|
|
15
15
|
import { Button } from '@akinon/ui-button';
|
|
16
16
|
import { Card } from '@akinon/ui-card';
|
|
17
17
|
import { Checkbox } from '@akinon/ui-checkbox';
|
|
@@ -29,11 +29,59 @@ import { themeOverrides } from './common/theme-overrides';
|
|
|
29
29
|
import { AppliedFilters } from './components/applied-filters';
|
|
30
30
|
import { FilterToolbar } from './components/filter-toolbar';
|
|
31
31
|
import { VisibilityModal } from './components/visibility-modal';
|
|
32
|
-
import { BOOLEAN_STRING,
|
|
32
|
+
import { BOOLEAN_STRING, DEFAULT_MODAL_PAGE_SIZE, FILTER_DEBOUNCE_DELAY } from './constants';
|
|
33
33
|
import { useDebouncedValue } from './hooks/use-debounced-value';
|
|
34
34
|
import { i18n } from './i18n';
|
|
35
35
|
import { deriveDefaultVisibleKeys, ensureSchemaOrder, extractDefaultValues, flattenSchema, getFieldAriaLabel, normaliseValuesBySchema, partitionSchema } from './utils/schema';
|
|
36
36
|
import { normaliseOutputValues } from './utils/values';
|
|
37
|
+
const FilterFormItem = (_a) => {
|
|
38
|
+
var _b, _c;
|
|
39
|
+
var { children, control, name, valuePropName } = _a, props = __rest(_a, ["children", "control", "name", "valuePropName"]);
|
|
40
|
+
const { field, fieldState } = useController({ name, control });
|
|
41
|
+
const childrenWithProps = React.Children.map(children, child => {
|
|
42
|
+
if (React.isValidElement(child)) {
|
|
43
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
44
|
+
const typedChild = child;
|
|
45
|
+
return React.cloneElement(typedChild, Object.assign(Object.assign(Object.assign({}, field), {
|
|
46
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
47
|
+
onChange: (...params) => {
|
|
48
|
+
var _a, _b;
|
|
49
|
+
(_b = (_a = typedChild.props).onChange) === null || _b === void 0 ? void 0 : _b.call(_a, params);
|
|
50
|
+
field.onChange(...params);
|
|
51
|
+
}, onBlur: () => {
|
|
52
|
+
var _a, _b;
|
|
53
|
+
(_b = (_a = typedChild.props).onBlur) === null || _b === void 0 ? void 0 : _b.call(_a);
|
|
54
|
+
field.onBlur();
|
|
55
|
+
}, ref: undefined }), (valuePropName && {
|
|
56
|
+
[valuePropName]: field.value
|
|
57
|
+
})));
|
|
58
|
+
}
|
|
59
|
+
return child;
|
|
60
|
+
});
|
|
61
|
+
return (React.createElement(Akiform.Item, Object.assign({}, props, { validateStatus: fieldState.invalid ? 'error' : undefined, help: (_c = (_b = fieldState.error) === null || _b === void 0 ? void 0 : _b.message) !== null && _c !== void 0 ? _c : props.help, className: props.className }), childrenWithProps));
|
|
62
|
+
};
|
|
63
|
+
/**
|
|
64
|
+
* Checks if a field should be disabled based on config.disabled property.
|
|
65
|
+
*/
|
|
66
|
+
const checkIsDisabled = (field, formValues) => {
|
|
67
|
+
var _a;
|
|
68
|
+
const configDisabled = (_a = field.config) === null || _a === void 0 ? void 0 : _a.disabled;
|
|
69
|
+
if (typeof configDisabled === 'function') {
|
|
70
|
+
return configDisabled(formValues);
|
|
71
|
+
}
|
|
72
|
+
return Boolean(configDisabled);
|
|
73
|
+
};
|
|
74
|
+
/**
|
|
75
|
+
* Checks if a field should be visible based on config.visible property.
|
|
76
|
+
*/
|
|
77
|
+
const checkIsVisible = (field, formValues) => {
|
|
78
|
+
var _a;
|
|
79
|
+
const configVisible = (_a = field.config) === null || _a === void 0 ? void 0 : _a.visible;
|
|
80
|
+
if (typeof configVisible === 'function') {
|
|
81
|
+
return configVisible(formValues);
|
|
82
|
+
}
|
|
83
|
+
return configVisible !== false;
|
|
84
|
+
};
|
|
37
85
|
const resolveClearedFieldValue = (field, defaultValue) => {
|
|
38
86
|
if (defaultValue !== undefined) {
|
|
39
87
|
return defaultValue;
|
|
@@ -61,9 +109,15 @@ const AkifilterContent = ({ filterSchema, storageNamespace, defaultValues, onVal
|
|
|
61
109
|
const schemaDefaults = React.useMemo(() => normaliseValuesBySchema(flattenedSchema, extractDefaultValues(flattenedSchema)), [flattenedSchema]);
|
|
62
110
|
const externalDefaults = React.useMemo(() => normaliseValuesBySchema(flattenedSchema, defaultValues), [flattenedSchema, defaultValues]);
|
|
63
111
|
const baseDefaultValues = React.useMemo(() => (Object.assign(Object.assign({}, schemaDefaults), externalDefaults)), [schemaDefaults, externalDefaults]);
|
|
64
|
-
|
|
112
|
+
// Use state for persistedDefaults to make it reactive to localStorage changes
|
|
113
|
+
const [persistedDefaults, setPersistedDefaults] = React.useState(() => {
|
|
65
114
|
var _a;
|
|
66
115
|
return normaliseValuesBySchema(flattenedSchema, (_a = readStoredValues(regularFields, storageKey)) !== null && _a !== void 0 ? _a : undefined);
|
|
116
|
+
});
|
|
117
|
+
// Re-read from localStorage when storageKey changes
|
|
118
|
+
React.useEffect(() => {
|
|
119
|
+
var _a;
|
|
120
|
+
setPersistedDefaults(normaliseValuesBySchema(flattenedSchema, (_a = readStoredValues(regularFields, storageKey)) !== null && _a !== void 0 ? _a : undefined));
|
|
67
121
|
}, [flattenedSchema, regularFields, storageKey]);
|
|
68
122
|
const mergedDefaultValues = React.useMemo(() => (Object.assign(Object.assign({}, baseDefaultValues), persistedDefaults)), [baseDefaultValues, persistedDefaults]);
|
|
69
123
|
const formMethods = useForm({
|
|
@@ -106,12 +160,17 @@ const AkifilterContent = ({ filterSchema, storageNamespace, defaultValues, onVal
|
|
|
106
160
|
if (field.type === 'select') {
|
|
107
161
|
return resolveSelectLabel();
|
|
108
162
|
}
|
|
163
|
+
// For custom fields with options (like custom selects), try to find the label
|
|
164
|
+
if (field.type === 'custom' && 'options' in field && field.options) {
|
|
165
|
+
return resolveSelectLabel();
|
|
166
|
+
}
|
|
109
167
|
if (field.type === 'date') {
|
|
110
168
|
const iso = akidate.toIsoDate(currentValue);
|
|
111
169
|
if (iso) {
|
|
112
170
|
// Use localized format with time if showTime is enabled
|
|
113
171
|
const hasShowTime = 'showTime' in field && field.showTime;
|
|
114
|
-
|
|
172
|
+
// L = localized date, LTS = localized time with seconds
|
|
173
|
+
const format = hasShowTime ? 'L LTS' : 'L';
|
|
115
174
|
return akidate.formatIsoDate(iso, format);
|
|
116
175
|
}
|
|
117
176
|
}
|
|
@@ -127,7 +186,8 @@ const AkifilterContent = ({ filterSchema, storageNamespace, defaultValues, onVal
|
|
|
127
186
|
}
|
|
128
187
|
const iso = akidate.toIsoDate(currentValue);
|
|
129
188
|
if (iso) {
|
|
130
|
-
|
|
189
|
+
// Use localized date format for fallback
|
|
190
|
+
return akidate.formatIsoDate(iso, 'L');
|
|
131
191
|
}
|
|
132
192
|
return String(currentValue);
|
|
133
193
|
};
|
|
@@ -156,7 +216,20 @@ const AkifilterContent = ({ filterSchema, storageNamespace, defaultValues, onVal
|
|
|
156
216
|
writeVisibleKeys(storageKey, visibleKeys);
|
|
157
217
|
onVisibleFieldsChange === null || onVisibleFieldsChange === void 0 ? void 0 : onVisibleFieldsChange(visibleKeys);
|
|
158
218
|
}, [visibleKeys, onVisibleFieldsChange, storageKey]);
|
|
219
|
+
// Track previous mergedDefaultValues to avoid unnecessary resets
|
|
220
|
+
const previousMergedDefaultValuesRef = React.useRef(null);
|
|
159
221
|
React.useEffect(() => {
|
|
222
|
+
const serialised = JSON.stringify(mergedDefaultValues);
|
|
223
|
+
// Skip if the values haven't actually changed (deep comparison)
|
|
224
|
+
if (previousMergedDefaultValuesRef.current === serialised) {
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
// Skip reset on initial mount - useForm already uses defaultValues
|
|
228
|
+
if (previousMergedDefaultValuesRef.current === null) {
|
|
229
|
+
previousMergedDefaultValuesRef.current = serialised;
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
previousMergedDefaultValuesRef.current = serialised;
|
|
160
233
|
formMethods.reset(mergedDefaultValues);
|
|
161
234
|
}, [formMethods, mergedDefaultValues]);
|
|
162
235
|
const normalisedValues = React.useMemo(() => normaliseOutputValues(flattenedSchema, formValues), [flattenedSchema, formValues]);
|
|
@@ -214,11 +287,43 @@ const AkifilterContent = ({ filterSchema, storageNamespace, defaultValues, onVal
|
|
|
214
287
|
React.useEffect(() => {
|
|
215
288
|
lastPersistedValuesRef.current = null;
|
|
216
289
|
}, [storageKey]);
|
|
290
|
+
// Track previous serialised values to detect field removals
|
|
291
|
+
const previousSerialisedValuesRef = React.useRef(serialisedValues);
|
|
292
|
+
// Immediate effect for field removals (bypasses debounce)
|
|
293
|
+
React.useEffect(() => {
|
|
294
|
+
const previous = previousSerialisedValuesRef.current;
|
|
295
|
+
const current = serialisedValues;
|
|
296
|
+
previousSerialisedValuesRef.current = current;
|
|
297
|
+
// Skip if this is the initial render
|
|
298
|
+
if (!hasEmittedValuesRef.current) {
|
|
299
|
+
return;
|
|
300
|
+
}
|
|
301
|
+
// Skip if values haven't actually changed
|
|
302
|
+
if (previous === current) {
|
|
303
|
+
return;
|
|
304
|
+
}
|
|
305
|
+
const prevParsed = JSON.parse(previous);
|
|
306
|
+
const currParsed = JSON.parse(current);
|
|
307
|
+
// Detect if a field was removed (existed in prev but not in current)
|
|
308
|
+
const prevKeys = Object.keys(prevParsed);
|
|
309
|
+
const currKeys = Object.keys(currParsed);
|
|
310
|
+
const removedKeys = prevKeys.filter(key => !currKeys.includes(key));
|
|
311
|
+
// If a field was removed, immediately persist and emit (bypass debounce)
|
|
312
|
+
if (removedKeys.length > 0) {
|
|
313
|
+
const nextSerialised = current;
|
|
314
|
+
persistValues(currParsed, nextSerialised);
|
|
315
|
+
lastPersistedValuesRef.current = nextSerialised;
|
|
316
|
+
currentSerialisedValuesRef.current = nextSerialised;
|
|
317
|
+
// Update persistedDefaults to prevent stale mergedDefaultValues on re-render
|
|
318
|
+
setPersistedDefaults(currParsed);
|
|
319
|
+
onValuesChange === null || onValuesChange === void 0 ? void 0 : onValuesChange(currParsed);
|
|
320
|
+
}
|
|
321
|
+
}, [serialisedValues, onValuesChange, persistValues]);
|
|
217
322
|
const handleClearAll = React.useCallback(() => {
|
|
218
|
-
const clearedDefaults = Object.assign({},
|
|
323
|
+
const clearedDefaults = Object.assign({}, schemaDefaults);
|
|
219
324
|
flattenedSchema.forEach(field => {
|
|
220
325
|
const key = String(field.key);
|
|
221
|
-
const defaultValue =
|
|
326
|
+
const defaultValue = schemaDefaults[key];
|
|
222
327
|
const resolved = resolveClearedFieldValue(field, defaultValue);
|
|
223
328
|
if (resolved === undefined) {
|
|
224
329
|
delete clearedDefaults[key];
|
|
@@ -239,7 +344,7 @@ const AkifilterContent = ({ filterSchema, storageNamespace, defaultValues, onVal
|
|
|
239
344
|
lastPersistedValuesRef.current = nextSerialised !== null && nextSerialised !== void 0 ? nextSerialised : null;
|
|
240
345
|
onValuesChange === null || onValuesChange === void 0 ? void 0 : onValuesChange(nextValues);
|
|
241
346
|
}, [
|
|
242
|
-
|
|
347
|
+
schemaDefaults,
|
|
243
348
|
flattenedSchema,
|
|
244
349
|
formMethods,
|
|
245
350
|
onClearAll,
|
|
@@ -271,31 +376,33 @@ const AkifilterContent = ({ filterSchema, storageNamespace, defaultValues, onVal
|
|
|
271
376
|
if (!schemaField) {
|
|
272
377
|
return;
|
|
273
378
|
}
|
|
274
|
-
const defaultValue =
|
|
275
|
-
const fieldPath = schemaField.key;
|
|
379
|
+
const defaultValue = schemaDefaults[String(schemaField.key)];
|
|
276
380
|
const nextValue = resolveClearedFieldValue(schemaField, defaultValue);
|
|
277
|
-
//
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
});
|
|
283
|
-
// Compute the updated values immediately (don't wait for debounce)
|
|
284
|
-
const currentValues = formMethods.getValues() || {};
|
|
285
|
-
const updatedFormValues = Object.assign(Object.assign({}, currentValues), { [fieldPath]: nextValue });
|
|
381
|
+
// Manually construct the updated form values instead of relying on setValue + getValues
|
|
382
|
+
// because setValue is batched and getValues() wouldn't reflect the change immediately
|
|
383
|
+
const currentFormValues = formMethods.getValues();
|
|
384
|
+
const updatedFormValues = Object.assign(Object.assign({}, currentFormValues), { [String(schemaField.key)]: nextValue });
|
|
385
|
+
// Normalize the values (removes empty/undefined entries)
|
|
286
386
|
const nextValues = normaliseOutputValues(flattenedSchema, updatedFormValues);
|
|
387
|
+
// Update form state
|
|
388
|
+
formMethods.reset(updatedFormValues, {
|
|
389
|
+
keepDirty: false,
|
|
390
|
+
keepTouched: false,
|
|
391
|
+
keepValues: false
|
|
392
|
+
});
|
|
287
393
|
const nextSerialised = JSON.stringify(nextValues);
|
|
288
394
|
// Persist immediately (bypass debounce for remove action)
|
|
289
395
|
persistValues(nextValues);
|
|
290
396
|
// Update refs to mark this as the latest persisted state
|
|
291
397
|
lastPersistedValuesRef.current = nextSerialised;
|
|
292
398
|
currentSerialisedValuesRef.current = nextSerialised;
|
|
399
|
+
previousSerialisedValuesRef.current = nextSerialised; // Prevent immediate effect from re-emitting
|
|
293
400
|
hasEmittedValuesRef.current = true;
|
|
294
401
|
hasInitialValuesRef.current = Object.keys(nextValues).length > 0;
|
|
295
402
|
// Emit the change to parent
|
|
296
403
|
onValuesChange === null || onValuesChange === void 0 ? void 0 : onValuesChange(nextValues);
|
|
297
404
|
}, [
|
|
298
|
-
|
|
405
|
+
schemaDefaults,
|
|
299
406
|
flattenedSchema,
|
|
300
407
|
formMethods,
|
|
301
408
|
onValuesChange,
|
|
@@ -317,22 +424,25 @@ const AkifilterContent = ({ filterSchema, storageNamespace, defaultValues, onVal
|
|
|
317
424
|
const start = (modalPage - 1) * modalPageSize;
|
|
318
425
|
return filteredFields.slice(start, start + modalPageSize);
|
|
319
426
|
}, [filteredFields, modalPage, modalPageSize]);
|
|
320
|
-
const visibleFields = React.useMemo(() => regularFields
|
|
427
|
+
const visibleFields = React.useMemo(() => regularFields
|
|
428
|
+
.filter(field => visibleKeys.includes(String(field.key)))
|
|
429
|
+
.filter(field => checkIsVisible(field, formValues !== null && formValues !== void 0 ? formValues : {})), [regularFields, visibleKeys, formValues]);
|
|
321
430
|
const renderFieldComponent = (field) => {
|
|
322
431
|
const ariaLabel = getFieldAriaLabel(field);
|
|
432
|
+
const isDisabled = checkIsDisabled(field, formValues !== null && formValues !== void 0 ? formValues : {});
|
|
323
433
|
switch (field.type) {
|
|
324
434
|
case 'text':
|
|
325
|
-
return (React.createElement(Input, { placeholder: field.placeholder, size: "large", allowClear: true, "aria-label": ariaLabel }));
|
|
435
|
+
return (React.createElement(Input, { placeholder: field.placeholder, size: "large", allowClear: true, "aria-label": ariaLabel, disabled: isDisabled }));
|
|
326
436
|
case 'number':
|
|
327
|
-
return (React.createElement(InputNumber, { placeholder: field.placeholder, size: "large", className: "akinon-filter__field--number", "aria-label": ariaLabel }));
|
|
437
|
+
return (React.createElement(InputNumber, { placeholder: field.placeholder, size: "large", className: "akinon-filter__field--number", "aria-label": ariaLabel, disabled: isDisabled }));
|
|
328
438
|
case 'select':
|
|
329
|
-
return (React.createElement(Select, { placeholder: field.placeholder, size: "large", options: field.options, showSearch: true, optionFilterProp: "label", "aria-label": ariaLabel }));
|
|
439
|
+
return (React.createElement(Select, { placeholder: field.placeholder, size: "large", options: field.options, showSearch: true, optionFilterProp: "label", "aria-label": ariaLabel, disabled: isDisabled }));
|
|
330
440
|
case 'checkbox':
|
|
331
|
-
return React.createElement(Checkbox,
|
|
441
|
+
return React.createElement(Checkbox, { disabled: isDisabled }, field.label);
|
|
332
442
|
case 'date':
|
|
333
|
-
return (React.createElement(DatePicker, { placeholder: field.placeholder, showTime: field.showTime, suffixIcon: "calendar", suffixIconSize: "16px", "aria-label": ariaLabel }));
|
|
443
|
+
return (React.createElement(DatePicker, { placeholder: field.placeholder, showTime: field.showTime, suffixIcon: "calendar", suffixIconSize: "16px", "aria-label": ariaLabel, disabled: isDisabled }));
|
|
334
444
|
case 'textarea':
|
|
335
|
-
return (React.createElement(InputTextArea, { placeholder: field.placeholder, autoSize: { minRows: 3, maxRows: 6 }, "aria-label": ariaLabel }));
|
|
445
|
+
return (React.createElement(InputTextArea, { placeholder: field.placeholder, autoSize: { minRows: 3, maxRows: 6 }, "aria-label": ariaLabel, disabled: isDisabled }));
|
|
336
446
|
case 'custom':
|
|
337
447
|
if (typeof field.render === 'function') {
|
|
338
448
|
return field.render({
|
|
@@ -353,7 +463,7 @@ const AkifilterContent = ({ filterSchema, storageNamespace, defaultValues, onVal
|
|
|
353
463
|
}
|
|
354
464
|
};
|
|
355
465
|
const renderFormField = (field) => {
|
|
356
|
-
return (React.createElement(
|
|
466
|
+
return (React.createElement(FilterFormItem, { key: String(field.key), control: formMethods.control, name: field.key, tooltip: field.tooltip, help: field.help, labelDescription: field.labelDescription, required: Boolean(field.validation), className: "mb-0", valuePropName: field.type === 'checkbox' ? 'checked' : undefined }, renderFieldComponent(field)));
|
|
357
467
|
};
|
|
358
468
|
return (React.createElement(Card, { size: "small", className: "akinon-filter shadow", "data-testid": "akifilter-root" },
|
|
359
469
|
React.createElement(ConfigProvider, { theme: themeOverrides },
|
package/dist/esm/types.d.ts
CHANGED
|
@@ -1,10 +1,26 @@
|
|
|
1
1
|
import type { FieldValues, Path } from '@akinon/akiform';
|
|
2
2
|
import type { FormField } from '@akinon/akiform-builder';
|
|
3
|
+
export interface AkifilterFieldConfig<TFieldValues extends FieldValues = FieldValues> {
|
|
4
|
+
/**
|
|
5
|
+
* Controls whether the field is disabled.
|
|
6
|
+
* Can be a boolean or a function that receives form values and returns a boolean.
|
|
7
|
+
*/
|
|
8
|
+
disabled?: boolean | ((formValues: TFieldValues) => boolean);
|
|
9
|
+
/**
|
|
10
|
+
* Controls whether the field is visible.
|
|
11
|
+
* Can be a boolean or a function that receives form values and returns a boolean.
|
|
12
|
+
*/
|
|
13
|
+
visible?: boolean | ((formValues: TFieldValues) => boolean);
|
|
14
|
+
}
|
|
3
15
|
export type AkifilterField<TFieldValues extends FieldValues = FieldValues> = FormField<TFieldValues> & {
|
|
4
16
|
/**
|
|
5
17
|
* Controls whether the field is shown in the main filter form by default.
|
|
6
18
|
*/
|
|
7
19
|
isVisible?: boolean;
|
|
20
|
+
/**
|
|
21
|
+
* Configuration options for field behavior (disabled, visible).
|
|
22
|
+
*/
|
|
23
|
+
config?: AkifilterFieldConfig<TFieldValues>;
|
|
8
24
|
};
|
|
9
25
|
export type AkifilterFieldKey<TFieldValues extends FieldValues = FieldValues> = Path<TFieldValues>;
|
|
10
26
|
export type AkifilterSchema<TFieldValues extends FieldValues = FieldValues> = AkifilterField<TFieldValues>[];
|
package/dist/esm/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AACzD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AAEzD,MAAM,MAAM,cAAc,CAAC,YAAY,SAAS,WAAW,GAAG,WAAW,IACvE,SAAS,CAAC,YAAY,CAAC,GAAG;IACxB;;OAEG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AACzD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AAEzD,MAAM,WAAW,oBAAoB,CACnC,YAAY,SAAS,WAAW,GAAG,WAAW;IAE9C;;;OAGG;IACH,QAAQ,CAAC,EAAE,OAAO,GAAG,CAAC,CAAC,UAAU,EAAE,YAAY,KAAK,OAAO,CAAC,CAAC;IAC7D;;;OAGG;IACH,OAAO,CAAC,EAAE,OAAO,GAAG,CAAC,CAAC,UAAU,EAAE,YAAY,KAAK,OAAO,CAAC,CAAC;CAC7D;AAED,MAAM,MAAM,cAAc,CAAC,YAAY,SAAS,WAAW,GAAG,WAAW,IACvE,SAAS,CAAC,YAAY,CAAC,GAAG;IACxB;;OAEG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB;;OAEG;IACH,MAAM,CAAC,EAAE,oBAAoB,CAAC,YAAY,CAAC,CAAC;CAC7C,CAAC;AAEJ,MAAM,MAAM,iBAAiB,CAAC,YAAY,SAAS,WAAW,GAAG,WAAW,IAC1E,IAAI,CAAC,YAAY,CAAC,CAAC;AAErB,MAAM,MAAM,eAAe,CAAC,YAAY,SAAS,WAAW,GAAG,WAAW,IACxE,cAAc,CAAC,YAAY,CAAC,EAAE,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@akinon/akifilter",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0-next.10",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Akifilter is a filtering library for Akinon frontend applications.",
|
|
6
6
|
"type": "module",
|
|
@@ -13,32 +13,32 @@
|
|
|
13
13
|
"antd": "^5.27.0",
|
|
14
14
|
"react-error-boundary": "^6.0.0",
|
|
15
15
|
"@akinon/akiform": "1.1.2",
|
|
16
|
-
"@akinon/akidate": "1.1.
|
|
17
|
-
"@akinon/
|
|
16
|
+
"@akinon/akidate": "1.1.3-next.0",
|
|
17
|
+
"@akinon/ui-button": "1.4.0-next.1",
|
|
18
|
+
"@akinon/icons": "1.1.2-next.1",
|
|
19
|
+
"@akinon/ui-card": "1.1.3-next.1",
|
|
20
|
+
"@akinon/ui-checkbox": "1.3.3-next.1",
|
|
18
21
|
"@akinon/akilocale": "1.2.1",
|
|
19
|
-
"@akinon/ui-
|
|
20
|
-
"@akinon/ui-
|
|
21
|
-
"@akinon/ui-
|
|
22
|
-
"@akinon/ui-date-picker": "1.3.3-
|
|
23
|
-
"@akinon/ui-
|
|
24
|
-
"@akinon/ui-
|
|
25
|
-
"@akinon/ui-
|
|
26
|
-
"@akinon/ui-
|
|
27
|
-
"@akinon/ui-
|
|
28
|
-
"@akinon/ui-pagination": "1.3.4-rc.0",
|
|
29
|
-
"@akinon/ui-modal": "1.1.3-rc.0",
|
|
30
|
-
"@akinon/ui-space": "1.3.3-rc.0"
|
|
22
|
+
"@akinon/ui-collapse": "1.3.2-next.1",
|
|
23
|
+
"@akinon/ui-input": "1.1.3-next.1",
|
|
24
|
+
"@akinon/ui-modal": "1.1.3-next.1",
|
|
25
|
+
"@akinon/ui-date-picker": "1.3.3-next.1",
|
|
26
|
+
"@akinon/ui-input-number": "1.3.3-next.1",
|
|
27
|
+
"@akinon/ui-select": "1.3.4-next.1",
|
|
28
|
+
"@akinon/ui-pagination": "1.3.4-next.1",
|
|
29
|
+
"@akinon/ui-typography": "1.1.2-next.0",
|
|
30
|
+
"@akinon/ui-space": "1.3.3-next.1"
|
|
31
31
|
},
|
|
32
32
|
"devDependencies": {
|
|
33
33
|
"clean-package": "2.2.0",
|
|
34
34
|
"copyfiles": "^2.4.1",
|
|
35
35
|
"rimraf": "^5.0.5",
|
|
36
36
|
"typescript": "*",
|
|
37
|
-
"@akinon/akiform-builder": "1.3.5-
|
|
38
|
-
"@akinon/
|
|
39
|
-
"@akinon/
|
|
37
|
+
"@akinon/akiform-builder": "1.3.5-next.1",
|
|
38
|
+
"@akinon/ui-theme": "1.1.3-next.1",
|
|
39
|
+
"@akinon/typescript-config": "1.1.1",
|
|
40
40
|
"@akinon/vitest-config": "1.1.1",
|
|
41
|
-
"@akinon/
|
|
41
|
+
"@akinon/utils": "1.1.4-next.1"
|
|
42
42
|
},
|
|
43
43
|
"peerDependencies": {
|
|
44
44
|
"react": "^18 || ^19",
|