@akinon/akifilter 1.1.1-rc.0 → 1.2.0-next.2

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.
@@ -1 +1 @@
1
- {"version":3,"file":"akifilter.d.ts","sourceRoot":"","sources":["../../src/akifilter.tsx"],"names":[],"mappings":"AAAA,OAAO,cAAc,CAAC;AAGtB,OAAO,EAIL,WAAW,EAEX,IAAI,EAGL,MAAM,iBAAiB,CAAC;AAWzB,OAAO,KAAK,MAAM,OAAO,CAAC;AA2B1B,OAAO,KAAK,EAAkB,eAAe,EAAE,MAAM,SAAS,CAAC;AAY/D,KAAK,oBAAoB,GAAG,WAAW,CAAC;AA2BxC,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;AAirBF,eAAO,MAAM,SAAS;KACpB,YAAY,SAAS,oBAAoB,uBAElC,cAAc,CAAC,YAAY,CAAC;;CAmBpC,CAAC"}
1
+ {"version":3,"file":"akifilter.d.ts","sourceRoot":"","sources":["../../src/akifilter.tsx"],"names":[],"mappings":"AAAA,OAAO,cAAc,CAAC;AAGtB,OAAO,EAIL,WAAW,EAEX,IAAI,EAGL,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;AA2DxC,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;AAyrBF,eAAO,MAAM,SAAS;KACpB,YAAY,SAAS,oBAAoB,uBAElC,cAAc,CAAC,YAAY,CAAC;;CAmBpC,CAAC"}
@@ -37,6 +37,28 @@ 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
+ /**
41
+ * Checks if a field should be disabled based on config.disabled property.
42
+ */
43
+ const checkIsDisabled = (field, formValues) => {
44
+ var _a;
45
+ const configDisabled = (_a = field.config) === null || _a === void 0 ? void 0 : _a.disabled;
46
+ if (typeof configDisabled === 'function') {
47
+ return configDisabled(formValues);
48
+ }
49
+ return Boolean(configDisabled);
50
+ };
51
+ /**
52
+ * Checks if a field should be visible based on config.visible property.
53
+ */
54
+ const checkIsVisible = (field, formValues) => {
55
+ var _a;
56
+ const configVisible = (_a = field.config) === null || _a === void 0 ? void 0 : _a.visible;
57
+ if (typeof configVisible === 'function') {
58
+ return configVisible(formValues);
59
+ }
60
+ return configVisible !== false;
61
+ };
40
62
  const resolveClearedFieldValue = (field, defaultValue) => {
41
63
  if (defaultValue !== undefined) {
42
64
  return defaultValue;
@@ -109,12 +131,17 @@ const AkifilterContent = ({ filterSchema, storageNamespace, defaultValues, onVal
109
131
  if (field.type === 'select') {
110
132
  return resolveSelectLabel();
111
133
  }
134
+ // For custom fields with options (like custom selects), try to find the label
135
+ if (field.type === 'custom' && 'options' in field && field.options) {
136
+ return resolveSelectLabel();
137
+ }
112
138
  if (field.type === 'date') {
113
139
  const iso = akidate_1.akidate.toIsoDate(currentValue);
114
140
  if (iso) {
115
141
  // Use localized format with time if showTime is enabled
116
142
  const hasShowTime = 'showTime' in field && field.showTime;
117
- const format = hasShowTime ? 'L LTS' : 'YYYY-MM-DD';
143
+ // L = localized date, LTS = localized time with seconds
144
+ const format = hasShowTime ? 'L LTS' : 'L';
118
145
  return akidate_1.akidate.formatIsoDate(iso, format);
119
146
  }
120
147
  }
@@ -130,7 +157,8 @@ const AkifilterContent = ({ filterSchema, storageNamespace, defaultValues, onVal
130
157
  }
131
158
  const iso = akidate_1.akidate.toIsoDate(currentValue);
132
159
  if (iso) {
133
- return akidate_1.akidate.formatIsoDate(iso, constants_1.DATE_FORMAT);
160
+ // Use localized date format for fallback
161
+ return akidate_1.akidate.formatIsoDate(iso, 'L');
134
162
  }
135
163
  return String(currentValue);
136
164
  };
@@ -218,10 +246,10 @@ const AkifilterContent = ({ filterSchema, storageNamespace, defaultValues, onVal
218
246
  lastPersistedValuesRef.current = null;
219
247
  }, [storageKey]);
220
248
  const handleClearAll = react_1.default.useCallback(() => {
221
- const clearedDefaults = Object.assign({}, baseDefaultValues);
249
+ const clearedDefaults = Object.assign({}, schemaDefaults);
222
250
  flattenedSchema.forEach(field => {
223
251
  const key = String(field.key);
224
- const defaultValue = baseDefaultValues[key];
252
+ const defaultValue = schemaDefaults[key];
225
253
  const resolved = resolveClearedFieldValue(field, defaultValue);
226
254
  if (resolved === undefined) {
227
255
  delete clearedDefaults[key];
@@ -242,7 +270,7 @@ const AkifilterContent = ({ filterSchema, storageNamespace, defaultValues, onVal
242
270
  lastPersistedValuesRef.current = nextSerialised !== null && nextSerialised !== void 0 ? nextSerialised : null;
243
271
  onValuesChange === null || onValuesChange === void 0 ? void 0 : onValuesChange(nextValues);
244
272
  }, [
245
- baseDefaultValues,
273
+ schemaDefaults,
246
274
  flattenedSchema,
247
275
  formMethods,
248
276
  onClearAll,
@@ -274,7 +302,7 @@ const AkifilterContent = ({ filterSchema, storageNamespace, defaultValues, onVal
274
302
  if (!schemaField) {
275
303
  return;
276
304
  }
277
- const defaultValue = baseDefaultValues[String(schemaField.key)];
305
+ const defaultValue = schemaDefaults[String(schemaField.key)];
278
306
  const fieldPath = schemaField.key;
279
307
  const nextValue = resolveClearedFieldValue(schemaField, defaultValue);
280
308
  // Update the form value
@@ -283,10 +311,9 @@ const AkifilterContent = ({ filterSchema, storageNamespace, defaultValues, onVal
283
311
  shouldTouch: false,
284
312
  shouldValidate: false
285
313
  });
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 });
289
- const nextValues = (0, values_1.normaliseOutputValues)(flattenedSchema, updatedFormValues);
314
+ // Get all current form values (includes the updated field)
315
+ const allFormValues = formMethods.getValues();
316
+ const nextValues = (0, values_1.normaliseOutputValues)(flattenedSchema, allFormValues);
290
317
  const nextSerialised = JSON.stringify(nextValues);
291
318
  // Persist immediately (bypass debounce for remove action)
292
319
  persistValues(nextValues);
@@ -298,7 +325,7 @@ const AkifilterContent = ({ filterSchema, storageNamespace, defaultValues, onVal
298
325
  // Emit the change to parent
299
326
  onValuesChange === null || onValuesChange === void 0 ? void 0 : onValuesChange(nextValues);
300
327
  }, [
301
- baseDefaultValues,
328
+ schemaDefaults,
302
329
  flattenedSchema,
303
330
  formMethods,
304
331
  onValuesChange,
@@ -320,22 +347,25 @@ const AkifilterContent = ({ filterSchema, storageNamespace, defaultValues, onVal
320
347
  const start = (modalPage - 1) * modalPageSize;
321
348
  return filteredFields.slice(start, start + modalPageSize);
322
349
  }, [filteredFields, modalPage, modalPageSize]);
323
- const visibleFields = react_1.default.useMemo(() => regularFields.filter(field => visibleKeys.includes(String(field.key))), [regularFields, visibleKeys]);
350
+ const visibleFields = react_1.default.useMemo(() => regularFields
351
+ .filter(field => visibleKeys.includes(String(field.key)))
352
+ .filter(field => checkIsVisible(field, formValues !== null && formValues !== void 0 ? formValues : {})), [regularFields, visibleKeys, formValues]);
324
353
  const renderFieldComponent = (field) => {
325
354
  const ariaLabel = (0, schema_1.getFieldAriaLabel)(field);
355
+ const isDisabled = checkIsDisabled(field, formValues !== null && formValues !== void 0 ? formValues : {});
326
356
  switch (field.type) {
327
357
  case 'text':
328
- return (react_1.default.createElement(ui_input_1.Input, { placeholder: field.placeholder, size: "large", allowClear: true, "aria-label": ariaLabel }));
358
+ return (react_1.default.createElement(ui_input_1.Input, { placeholder: field.placeholder, size: "large", allowClear: true, "aria-label": ariaLabel, disabled: isDisabled }));
329
359
  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 }));
360
+ 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
361
  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 }));
362
+ 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
363
  case 'checkbox':
334
- return react_1.default.createElement(ui_checkbox_1.Checkbox, null, field.label);
364
+ return react_1.default.createElement(ui_checkbox_1.Checkbox, { disabled: isDisabled }, field.label);
335
365
  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 }));
366
+ 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
367
  case 'textarea':
338
- return (react_1.default.createElement(ui_input_1.InputTextArea, { placeholder: field.placeholder, autoSize: { minRows: 3, maxRows: 6 }, "aria-label": ariaLabel }));
368
+ return (react_1.default.createElement(ui_input_1.InputTextArea, { placeholder: field.placeholder, autoSize: { minRows: 3, maxRows: 6 }, "aria-label": ariaLabel, disabled: isDisabled }));
339
369
  case 'custom':
340
370
  if (typeof field.render === 'function') {
341
371
  return field.render({
@@ -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>[];
@@ -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;CACrB,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
+ {"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,EAEX,IAAI,EAGL,MAAM,iBAAiB,CAAC;AAWzB,OAAO,KAAK,MAAM,OAAO,CAAC;AA2B1B,OAAO,KAAK,EAAkB,eAAe,EAAE,MAAM,SAAS,CAAC;AAY/D,KAAK,oBAAoB,GAAG,WAAW,CAAC;AA2BxC,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;AAirBF,eAAO,MAAM,SAAS;KACpB,YAAY,SAAS,oBAAoB,uBAElC,cAAc,CAAC,YAAY,CAAC;;CAmBpC,CAAC"}
1
+ {"version":3,"file":"akifilter.d.ts","sourceRoot":"","sources":["../../src/akifilter.tsx"],"names":[],"mappings":"AAAA,OAAO,cAAc,CAAC;AAGtB,OAAO,EAIL,WAAW,EAEX,IAAI,EAGL,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;AA2DxC,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;AAyrBF,eAAO,MAAM,SAAS;KACpB,YAAY,SAAS,oBAAoB,uBAElC,cAAc,CAAC,YAAY,CAAC;;CAmBpC,CAAC"}
@@ -29,11 +29,33 @@ 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, DATE_FORMAT, DEFAULT_MODAL_PAGE_SIZE, FILTER_DEBOUNCE_DELAY } from './constants';
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
+ /**
38
+ * Checks if a field should be disabled based on config.disabled property.
39
+ */
40
+ const checkIsDisabled = (field, formValues) => {
41
+ var _a;
42
+ const configDisabled = (_a = field.config) === null || _a === void 0 ? void 0 : _a.disabled;
43
+ if (typeof configDisabled === 'function') {
44
+ return configDisabled(formValues);
45
+ }
46
+ return Boolean(configDisabled);
47
+ };
48
+ /**
49
+ * Checks if a field should be visible based on config.visible property.
50
+ */
51
+ const checkIsVisible = (field, formValues) => {
52
+ var _a;
53
+ const configVisible = (_a = field.config) === null || _a === void 0 ? void 0 : _a.visible;
54
+ if (typeof configVisible === 'function') {
55
+ return configVisible(formValues);
56
+ }
57
+ return configVisible !== false;
58
+ };
37
59
  const resolveClearedFieldValue = (field, defaultValue) => {
38
60
  if (defaultValue !== undefined) {
39
61
  return defaultValue;
@@ -106,12 +128,17 @@ const AkifilterContent = ({ filterSchema, storageNamespace, defaultValues, onVal
106
128
  if (field.type === 'select') {
107
129
  return resolveSelectLabel();
108
130
  }
131
+ // For custom fields with options (like custom selects), try to find the label
132
+ if (field.type === 'custom' && 'options' in field && field.options) {
133
+ return resolveSelectLabel();
134
+ }
109
135
  if (field.type === 'date') {
110
136
  const iso = akidate.toIsoDate(currentValue);
111
137
  if (iso) {
112
138
  // Use localized format with time if showTime is enabled
113
139
  const hasShowTime = 'showTime' in field && field.showTime;
114
- const format = hasShowTime ? 'L LTS' : 'YYYY-MM-DD';
140
+ // L = localized date, LTS = localized time with seconds
141
+ const format = hasShowTime ? 'L LTS' : 'L';
115
142
  return akidate.formatIsoDate(iso, format);
116
143
  }
117
144
  }
@@ -127,7 +154,8 @@ const AkifilterContent = ({ filterSchema, storageNamespace, defaultValues, onVal
127
154
  }
128
155
  const iso = akidate.toIsoDate(currentValue);
129
156
  if (iso) {
130
- return akidate.formatIsoDate(iso, DATE_FORMAT);
157
+ // Use localized date format for fallback
158
+ return akidate.formatIsoDate(iso, 'L');
131
159
  }
132
160
  return String(currentValue);
133
161
  };
@@ -215,10 +243,10 @@ const AkifilterContent = ({ filterSchema, storageNamespace, defaultValues, onVal
215
243
  lastPersistedValuesRef.current = null;
216
244
  }, [storageKey]);
217
245
  const handleClearAll = React.useCallback(() => {
218
- const clearedDefaults = Object.assign({}, baseDefaultValues);
246
+ const clearedDefaults = Object.assign({}, schemaDefaults);
219
247
  flattenedSchema.forEach(field => {
220
248
  const key = String(field.key);
221
- const defaultValue = baseDefaultValues[key];
249
+ const defaultValue = schemaDefaults[key];
222
250
  const resolved = resolveClearedFieldValue(field, defaultValue);
223
251
  if (resolved === undefined) {
224
252
  delete clearedDefaults[key];
@@ -239,7 +267,7 @@ const AkifilterContent = ({ filterSchema, storageNamespace, defaultValues, onVal
239
267
  lastPersistedValuesRef.current = nextSerialised !== null && nextSerialised !== void 0 ? nextSerialised : null;
240
268
  onValuesChange === null || onValuesChange === void 0 ? void 0 : onValuesChange(nextValues);
241
269
  }, [
242
- baseDefaultValues,
270
+ schemaDefaults,
243
271
  flattenedSchema,
244
272
  formMethods,
245
273
  onClearAll,
@@ -271,7 +299,7 @@ const AkifilterContent = ({ filterSchema, storageNamespace, defaultValues, onVal
271
299
  if (!schemaField) {
272
300
  return;
273
301
  }
274
- const defaultValue = baseDefaultValues[String(schemaField.key)];
302
+ const defaultValue = schemaDefaults[String(schemaField.key)];
275
303
  const fieldPath = schemaField.key;
276
304
  const nextValue = resolveClearedFieldValue(schemaField, defaultValue);
277
305
  // Update the form value
@@ -280,10 +308,9 @@ const AkifilterContent = ({ filterSchema, storageNamespace, defaultValues, onVal
280
308
  shouldTouch: false,
281
309
  shouldValidate: false
282
310
  });
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 });
286
- const nextValues = normaliseOutputValues(flattenedSchema, updatedFormValues);
311
+ // Get all current form values (includes the updated field)
312
+ const allFormValues = formMethods.getValues();
313
+ const nextValues = normaliseOutputValues(flattenedSchema, allFormValues);
287
314
  const nextSerialised = JSON.stringify(nextValues);
288
315
  // Persist immediately (bypass debounce for remove action)
289
316
  persistValues(nextValues);
@@ -295,7 +322,7 @@ const AkifilterContent = ({ filterSchema, storageNamespace, defaultValues, onVal
295
322
  // Emit the change to parent
296
323
  onValuesChange === null || onValuesChange === void 0 ? void 0 : onValuesChange(nextValues);
297
324
  }, [
298
- baseDefaultValues,
325
+ schemaDefaults,
299
326
  flattenedSchema,
300
327
  formMethods,
301
328
  onValuesChange,
@@ -317,22 +344,25 @@ const AkifilterContent = ({ filterSchema, storageNamespace, defaultValues, onVal
317
344
  const start = (modalPage - 1) * modalPageSize;
318
345
  return filteredFields.slice(start, start + modalPageSize);
319
346
  }, [filteredFields, modalPage, modalPageSize]);
320
- const visibleFields = React.useMemo(() => regularFields.filter(field => visibleKeys.includes(String(field.key))), [regularFields, visibleKeys]);
347
+ const visibleFields = React.useMemo(() => regularFields
348
+ .filter(field => visibleKeys.includes(String(field.key)))
349
+ .filter(field => checkIsVisible(field, formValues !== null && formValues !== void 0 ? formValues : {})), [regularFields, visibleKeys, formValues]);
321
350
  const renderFieldComponent = (field) => {
322
351
  const ariaLabel = getFieldAriaLabel(field);
352
+ const isDisabled = checkIsDisabled(field, formValues !== null && formValues !== void 0 ? formValues : {});
323
353
  switch (field.type) {
324
354
  case 'text':
325
- return (React.createElement(Input, { placeholder: field.placeholder, size: "large", allowClear: true, "aria-label": ariaLabel }));
355
+ return (React.createElement(Input, { placeholder: field.placeholder, size: "large", allowClear: true, "aria-label": ariaLabel, disabled: isDisabled }));
326
356
  case 'number':
327
- return (React.createElement(InputNumber, { placeholder: field.placeholder, size: "large", className: "akinon-filter__field--number", "aria-label": ariaLabel }));
357
+ return (React.createElement(InputNumber, { placeholder: field.placeholder, size: "large", className: "akinon-filter__field--number", "aria-label": ariaLabel, disabled: isDisabled }));
328
358
  case 'select':
329
- return (React.createElement(Select, { placeholder: field.placeholder, size: "large", options: field.options, showSearch: true, optionFilterProp: "label", "aria-label": ariaLabel }));
359
+ return (React.createElement(Select, { placeholder: field.placeholder, size: "large", options: field.options, showSearch: true, optionFilterProp: "label", "aria-label": ariaLabel, disabled: isDisabled }));
330
360
  case 'checkbox':
331
- return React.createElement(Checkbox, null, field.label);
361
+ return React.createElement(Checkbox, { disabled: isDisabled }, field.label);
332
362
  case 'date':
333
- return (React.createElement(DatePicker, { placeholder: field.placeholder, showTime: field.showTime, suffixIcon: "calendar", suffixIconSize: "16px", "aria-label": ariaLabel }));
363
+ return (React.createElement(DatePicker, { placeholder: field.placeholder, showTime: field.showTime, suffixIcon: "calendar", suffixIconSize: "16px", "aria-label": ariaLabel, disabled: isDisabled }));
334
364
  case 'textarea':
335
- return (React.createElement(InputTextArea, { placeholder: field.placeholder, autoSize: { minRows: 3, maxRows: 6 }, "aria-label": ariaLabel }));
365
+ return (React.createElement(InputTextArea, { placeholder: field.placeholder, autoSize: { minRows: 3, maxRows: 6 }, "aria-label": ariaLabel, disabled: isDisabled }));
336
366
  case 'custom':
337
367
  if (typeof field.render === 'function') {
338
368
  return field.render({
@@ -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>[];
@@ -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;CACrB,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
+ {"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.1.1-rc.0",
3
+ "version": "1.2.0-next.2",
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.2",
17
- "@akinon/icons": "1.1.2-rc.0",
16
+ "@akinon/akidate": "1.1.3-next.0",
18
17
  "@akinon/akilocale": "1.2.1",
19
- "@akinon/ui-button": "1.4.0-rc.0",
20
- "@akinon/ui-card": "1.1.3-rc.0",
21
- "@akinon/ui-checkbox": "1.3.3-rc.0",
22
- "@akinon/ui-date-picker": "1.3.3-rc.0",
23
- "@akinon/ui-collapse": "1.3.2-rc.0",
24
- "@akinon/ui-input": "1.1.3-rc.0",
25
- "@akinon/ui-typography": "1.1.1",
26
- "@akinon/ui-input-number": "1.3.3-rc.0",
27
- "@akinon/ui-select": "1.3.4-rc.0",
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"
18
+ "@akinon/icons": "1.1.2-next.1",
19
+ "@akinon/ui-button": "1.4.0-next.1",
20
+ "@akinon/ui-checkbox": "1.3.3-next.1",
21
+ "@akinon/ui-card": "1.1.3-next.1",
22
+ "@akinon/ui-date-picker": "1.3.3-next.1",
23
+ "@akinon/ui-collapse": "1.3.2-next.1",
24
+ "@akinon/ui-input-number": "1.3.3-next.1",
25
+ "@akinon/ui-typography": "1.1.2-next.0",
26
+ "@akinon/ui-modal": "1.1.3-next.1",
27
+ "@akinon/ui-input": "1.1.3-next.1",
28
+ "@akinon/ui-select": "1.3.4-next.1",
29
+ "@akinon/ui-pagination": "1.3.4-next.1",
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-rc.0",
38
- "@akinon/utils": "1.1.4-rc.0",
39
- "@akinon/ui-theme": "1.1.3-rc.0",
40
- "@akinon/vitest-config": "1.1.1",
41
- "@akinon/typescript-config": "1.1.1"
37
+ "@akinon/utils": "1.1.4-next.1",
38
+ "@akinon/akiform-builder": "1.3.5-next.1",
39
+ "@akinon/ui-theme": "1.1.3-next.1",
40
+ "@akinon/typescript-config": "1.1.1",
41
+ "@akinon/vitest-config": "1.1.1"
42
42
  },
43
43
  "peerDependencies": {
44
44
  "react": "^18 || ^19",