@akinon/akifilter 0.5.4 → 1.0.1

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.
Files changed (160) hide show
  1. package/README.md +44 -0
  2. package/dist/cjs/akifilter.d.ts +53 -0
  3. package/dist/cjs/akifilter.d.ts.map +1 -0
  4. package/dist/cjs/akifilter.js +360 -0
  5. package/dist/cjs/common/storage.d.ts +10 -0
  6. package/dist/cjs/common/storage.d.ts.map +1 -0
  7. package/dist/cjs/common/storage.js +124 -0
  8. package/dist/cjs/common/theme-overrides.d.ts +3 -0
  9. package/dist/cjs/common/theme-overrides.d.ts.map +1 -0
  10. package/dist/cjs/common/theme-overrides.js +6 -0
  11. package/dist/cjs/components/applied-filters.d.ts +14 -0
  12. package/dist/cjs/components/applied-filters.d.ts.map +1 -0
  13. package/dist/cjs/components/applied-filters.js +26 -0
  14. package/dist/cjs/components/filter-toolbar.d.ts +11 -0
  15. package/dist/cjs/components/filter-toolbar.d.ts.map +1 -0
  16. package/dist/cjs/components/filter-toolbar.js +17 -0
  17. package/dist/cjs/components/visibility-modal.d.ts +19 -0
  18. package/dist/cjs/components/visibility-modal.d.ts.map +1 -0
  19. package/dist/cjs/components/visibility-modal.js +30 -0
  20. package/dist/cjs/constants.d.ts +4 -0
  21. package/dist/cjs/constants.d.ts.map +1 -0
  22. package/dist/cjs/constants.js +6 -0
  23. package/dist/cjs/hooks/use-debounced-value.d.ts +2 -0
  24. package/dist/cjs/hooks/use-debounced-value.d.ts.map +1 -0
  25. package/dist/cjs/hooks/use-debounced-value.js +17 -0
  26. package/dist/cjs/i18n/index.d.ts +2 -4
  27. package/dist/cjs/i18n/index.d.ts.map +1 -1
  28. package/dist/cjs/i18n/index.js +1 -1
  29. package/dist/cjs/i18n/translations/en.d.ts +33 -3
  30. package/dist/cjs/i18n/translations/en.d.ts.map +1 -1
  31. package/dist/cjs/i18n/translations/en.js +33 -3
  32. package/dist/cjs/i18n/translations/tr.d.ts +33 -3
  33. package/dist/cjs/i18n/translations/tr.d.ts.map +1 -1
  34. package/dist/cjs/i18n/translations/tr.js +33 -3
  35. package/dist/cjs/index.d.ts +3 -14
  36. package/dist/cjs/index.d.ts.map +1 -1
  37. package/dist/cjs/index.js +17 -32
  38. package/dist/cjs/styles.css +179 -0
  39. package/dist/cjs/types.d.ts +11 -0
  40. package/dist/cjs/types.d.ts.map +1 -0
  41. package/dist/cjs/types.js +2 -0
  42. package/dist/cjs/utils/schema.d.ts +9 -0
  43. package/dist/cjs/utils/schema.d.ts.map +1 -0
  44. package/dist/cjs/utils/schema.js +61 -0
  45. package/dist/cjs/utils/values.d.ts +5 -0
  46. package/dist/cjs/utils/values.d.ts.map +1 -0
  47. package/dist/cjs/utils/values.js +44 -0
  48. package/dist/esm/akifilter.d.ts +53 -0
  49. package/dist/esm/akifilter.d.ts.map +1 -0
  50. package/dist/esm/akifilter.js +356 -0
  51. package/dist/esm/common/storage.d.ts +10 -0
  52. package/dist/esm/common/storage.d.ts.map +1 -0
  53. package/dist/esm/common/storage.js +114 -0
  54. package/dist/esm/common/theme-overrides.d.ts +3 -0
  55. package/dist/esm/common/theme-overrides.d.ts.map +1 -0
  56. package/dist/esm/common/theme-overrides.js +3 -0
  57. package/dist/esm/components/applied-filters.d.ts +14 -0
  58. package/dist/esm/components/applied-filters.d.ts.map +1 -0
  59. package/dist/esm/components/applied-filters.js +22 -0
  60. package/dist/esm/components/filter-toolbar.d.ts +11 -0
  61. package/dist/esm/components/filter-toolbar.d.ts.map +1 -0
  62. package/dist/esm/components/filter-toolbar.js +13 -0
  63. package/dist/esm/components/visibility-modal.d.ts +19 -0
  64. package/dist/esm/components/visibility-modal.d.ts.map +1 -0
  65. package/dist/esm/components/visibility-modal.js +26 -0
  66. package/dist/esm/constants.d.ts +4 -0
  67. package/dist/esm/constants.d.ts.map +1 -0
  68. package/dist/esm/constants.js +3 -0
  69. package/dist/esm/hooks/use-debounced-value.d.ts +2 -0
  70. package/dist/esm/hooks/use-debounced-value.d.ts.map +1 -0
  71. package/dist/esm/hooks/use-debounced-value.js +13 -0
  72. package/dist/esm/i18n/index.d.ts +2 -4
  73. package/dist/esm/i18n/index.d.ts.map +1 -1
  74. package/dist/esm/i18n/index.js +5 -5
  75. package/dist/esm/i18n/translations/en.d.ts +33 -3
  76. package/dist/esm/i18n/translations/en.d.ts.map +1 -1
  77. package/dist/esm/i18n/translations/en.js +33 -3
  78. package/dist/esm/i18n/translations/tr.d.ts +33 -3
  79. package/dist/esm/i18n/translations/tr.d.ts.map +1 -1
  80. package/dist/esm/i18n/translations/tr.js +33 -3
  81. package/dist/esm/index.d.ts +3 -14
  82. package/dist/esm/index.d.ts.map +1 -1
  83. package/dist/esm/index.js +3 -30
  84. package/dist/esm/styles.css +179 -0
  85. package/dist/esm/types.d.ts +11 -0
  86. package/dist/esm/types.d.ts.map +1 -0
  87. package/dist/esm/types.js +1 -0
  88. package/dist/esm/utils/schema.d.ts +9 -0
  89. package/dist/esm/utils/schema.d.ts.map +1 -0
  90. package/dist/esm/utils/schema.js +52 -0
  91. package/dist/esm/utils/values.d.ts +5 -0
  92. package/dist/esm/utils/values.d.ts.map +1 -0
  93. package/dist/esm/utils/values.js +39 -0
  94. package/package.json +39 -33
  95. package/dist/cjs/components/AppliedFilters/AppliedFilterItem.d.ts +0 -12
  96. package/dist/cjs/components/AppliedFilters/AppliedFilterItem.d.ts.map +0 -1
  97. package/dist/cjs/components/AppliedFilters/AppliedFilterItem.js +0 -62
  98. package/dist/cjs/components/AppliedFilters/common.d.ts +0 -6
  99. package/dist/cjs/components/AppliedFilters/common.d.ts.map +0 -1
  100. package/dist/cjs/components/AppliedFilters/common.js +0 -34
  101. package/dist/cjs/components/AppliedFilters/index.d.ts +0 -4
  102. package/dist/cjs/components/AppliedFilters/index.d.ts.map +0 -1
  103. package/dist/cjs/components/AppliedFilters/index.js +0 -26
  104. package/dist/cjs/components/AppliedFilters/styles.css +0 -65
  105. package/dist/cjs/components/ConditionalFilters/index.d.ts +0 -3
  106. package/dist/cjs/components/ConditionalFilters/index.d.ts.map +0 -1
  107. package/dist/cjs/components/ConditionalFilters/index.js +0 -32
  108. package/dist/cjs/components/FilterContext/index.d.ts +0 -45
  109. package/dist/cjs/components/FilterContext/index.d.ts.map +0 -1
  110. package/dist/cjs/components/FilterContext/index.js +0 -85
  111. package/dist/cjs/components/SelectShownFilters/index.d.ts +0 -7
  112. package/dist/cjs/components/SelectShownFilters/index.d.ts.map +0 -1
  113. package/dist/cjs/components/SelectShownFilters/index.js +0 -111
  114. package/dist/cjs/components/SelectShownFilters/styles.css +0 -99
  115. package/dist/cjs/components/ShownFilters/index.d.ts +0 -3
  116. package/dist/cjs/components/ShownFilters/index.d.ts.map +0 -1
  117. package/dist/cjs/components/ShownFilters/index.js +0 -45
  118. package/dist/cjs/components/index.d.ts +0 -6
  119. package/dist/cjs/components/index.d.ts.map +0 -1
  120. package/dist/cjs/components/index.js +0 -21
  121. package/dist/cjs/constants/index.d.ts +0 -56
  122. package/dist/cjs/constants/index.d.ts.map +0 -1
  123. package/dist/cjs/constants/index.js +0 -58
  124. package/dist/cjs/index.css +0 -67
  125. package/dist/cjs/utils/index.d.ts +0 -25
  126. package/dist/cjs/utils/index.d.ts.map +0 -1
  127. package/dist/cjs/utils/index.js +0 -40
  128. package/dist/esm/components/AppliedFilters/AppliedFilterItem.d.ts +0 -12
  129. package/dist/esm/components/AppliedFilters/AppliedFilterItem.d.ts.map +0 -1
  130. package/dist/esm/components/AppliedFilters/AppliedFilterItem.js +0 -58
  131. package/dist/esm/components/AppliedFilters/common.d.ts +0 -6
  132. package/dist/esm/components/AppliedFilters/common.d.ts.map +0 -1
  133. package/dist/esm/components/AppliedFilters/common.js +0 -29
  134. package/dist/esm/components/AppliedFilters/index.d.ts +0 -4
  135. package/dist/esm/components/AppliedFilters/index.d.ts.map +0 -1
  136. package/dist/esm/components/AppliedFilters/index.js +0 -22
  137. package/dist/esm/components/AppliedFilters/styles.css +0 -65
  138. package/dist/esm/components/ConditionalFilters/index.d.ts +0 -3
  139. package/dist/esm/components/ConditionalFilters/index.d.ts.map +0 -1
  140. package/dist/esm/components/ConditionalFilters/index.js +0 -28
  141. package/dist/esm/components/FilterContext/index.d.ts +0 -45
  142. package/dist/esm/components/FilterContext/index.d.ts.map +0 -1
  143. package/dist/esm/components/FilterContext/index.js +0 -80
  144. package/dist/esm/components/SelectShownFilters/index.d.ts +0 -7
  145. package/dist/esm/components/SelectShownFilters/index.d.ts.map +0 -1
  146. package/dist/esm/components/SelectShownFilters/index.js +0 -107
  147. package/dist/esm/components/SelectShownFilters/styles.css +0 -99
  148. package/dist/esm/components/ShownFilters/index.d.ts +0 -3
  149. package/dist/esm/components/ShownFilters/index.d.ts.map +0 -1
  150. package/dist/esm/components/ShownFilters/index.js +0 -41
  151. package/dist/esm/components/index.d.ts +0 -6
  152. package/dist/esm/components/index.d.ts.map +0 -1
  153. package/dist/esm/components/index.js +0 -5
  154. package/dist/esm/constants/index.d.ts +0 -56
  155. package/dist/esm/constants/index.d.ts.map +0 -1
  156. package/dist/esm/constants/index.js +0 -55
  157. package/dist/esm/index.css +0 -67
  158. package/dist/esm/utils/index.d.ts +0 -25
  159. package/dist/esm/utils/index.d.ts.map +0 -1
  160. package/dist/esm/utils/index.js +0 -35
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../../src/utils/schema.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAGnD,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAEhE,eAAO,MAAM,eAAe,GAAI,YAAY,SAAS,WAAW,GAAG,WAAW,EAC5E,OAAO,cAAc,CAAC,YAAY,CAAC,KAClC,MAEF,CAAC;AAEF,eAAO,MAAM,iBAAiB,GAC5B,YAAY,SAAS,WAAW,GAAG,WAAW,EAE9C,OAAO,cAAc,CAAC,YAAY,CAAC,KAClC,MAEF,CAAC;AAEF,eAAO,MAAM,iBAAiB,GAC5B,YAAY,SAAS,WAAW,GAAG,WAAW,EAE9C,QAAQ,eAAe,CAAC,YAAY,CAAC,EACrC,MAAM,MAAM,EAAE,KACb,MAAM,EAGR,CAAC;AAEF,eAAO,MAAM,wBAAwB,GACnC,YAAY,SAAS,WAAW,GAAG,WAAW,EAE9C,QAAQ,eAAe,CAAC,YAAY,CAAC,KACpC,MAAM,EAUR,CAAC;AAEF,eAAO,MAAM,oBAAoB,GAC/B,YAAY,SAAS,WAAW,GAAG,WAAW,EAE9C,QAAQ,eAAe,CAAC,YAAY,CAAC,KACpC,OAAO,CAAC,YAAY,CAQtB,CAAC;AAEF,eAAO,MAAM,uBAAuB,GAClC,YAAY,SAAS,WAAW,GAAG,WAAW,EAE9C,QAAQ,eAAe,CAAC,YAAY,CAAC,EACrC,SAAS,OAAO,CAAC,YAAY,CAAC,KAC7B,OAAO,CAAC,YAAY,CA+BtB,CAAC"}
@@ -0,0 +1,61 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.normaliseValuesBySchema = exports.extractDefaultValues = exports.deriveDefaultVisibleKeys = exports.ensureSchemaOrder = exports.getFieldAriaLabel = exports.getDisplayLabel = void 0;
4
+ const akidate_1 = require("@akinon/akidate");
5
+ const constants_1 = require("../constants");
6
+ const getDisplayLabel = (field) => {
7
+ return field.label || field.placeholder || String(field.key);
8
+ };
9
+ exports.getDisplayLabel = getDisplayLabel;
10
+ const getFieldAriaLabel = (field) => {
11
+ return field.label || field.placeholder || String(field.key);
12
+ };
13
+ exports.getFieldAriaLabel = getFieldAriaLabel;
14
+ const ensureSchemaOrder = (schema, keys) => {
15
+ const keySet = new Set(keys.map(String));
16
+ return schema.map(field => String(field.key)).filter(key => keySet.has(key));
17
+ };
18
+ exports.ensureSchemaOrder = ensureSchemaOrder;
19
+ const deriveDefaultVisibleKeys = (schema) => {
20
+ const explicitKeys = schema
21
+ .filter(field => field.isVisible)
22
+ .map(field => String(field.key));
23
+ if (explicitKeys.length > 0) {
24
+ return (0, exports.ensureSchemaOrder)(schema, Array.from(new Set(explicitKeys)));
25
+ }
26
+ return schema.slice(0, constants_1.DEFAULT_VISIBLE_COUNT).map(field => String(field.key));
27
+ };
28
+ exports.deriveDefaultVisibleKeys = deriveDefaultVisibleKeys;
29
+ const extractDefaultValues = (schema) => {
30
+ return schema.reduce((acc, field) => {
31
+ if (field.defaultValue !== undefined) {
32
+ const key = field.key;
33
+ acc[key] = field.defaultValue;
34
+ }
35
+ return acc;
36
+ }, {});
37
+ };
38
+ exports.extractDefaultValues = extractDefaultValues;
39
+ const normaliseValuesBySchema = (schema, values) => {
40
+ if (!values) {
41
+ return {};
42
+ }
43
+ return Object.keys(values).reduce((acc, key) => {
44
+ const field = schema.find(schemaField => String(schemaField.key) === String(key));
45
+ if (!field) {
46
+ acc[key] = values[key];
47
+ return acc;
48
+ }
49
+ const currentValue = values[key];
50
+ if (field.type === 'date') {
51
+ const parsed = akidate_1.akidate.parse(currentValue);
52
+ if (parsed) {
53
+ acc[key] = parsed;
54
+ return acc;
55
+ }
56
+ }
57
+ acc[key] = currentValue;
58
+ return acc;
59
+ }, {});
60
+ };
61
+ exports.normaliseValuesBySchema = normaliseValuesBySchema;
@@ -0,0 +1,5 @@
1
+ import type { FieldValues } from '@akinon/akiform';
2
+ import type { AkifilterField, AkifilterSchema } from '../types';
3
+ export declare const shouldPersistValue: <TFieldValues extends FieldValues = FieldValues>(field: AkifilterField<TFieldValues> | undefined, value: unknown) => boolean;
4
+ export declare const normaliseOutputValues: <TFieldValues extends FieldValues = FieldValues>(schema: AkifilterSchema<TFieldValues>, values?: Partial<TFieldValues> | FieldValues) => Partial<TFieldValues>;
5
+ //# sourceMappingURL=values.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"values.d.ts","sourceRoot":"","sources":["../../../src/utils/values.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAEnD,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAEhE,eAAO,MAAM,kBAAkB,GAC7B,YAAY,SAAS,WAAW,GAAG,WAAW,EAE9C,OAAO,cAAc,CAAC,YAAY,CAAC,GAAG,SAAS,EAC/C,OAAO,OAAO,KACb,OAkBF,CAAC;AAEF,eAAO,MAAM,qBAAqB,GAChC,YAAY,SAAS,WAAW,GAAG,WAAW,EAE9C,QAAQ,eAAe,CAAC,YAAY,CAAC,EACrC,SAAS,OAAO,CAAC,YAAY,CAAC,GAAG,WAAW,KAC3C,OAAO,CAAC,YAAY,CA8BtB,CAAC"}
@@ -0,0 +1,44 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.normaliseOutputValues = exports.shouldPersistValue = void 0;
4
+ const akidate_1 = require("@akinon/akidate");
5
+ const shouldPersistValue = (field, value) => {
6
+ if (value === undefined || value === null) {
7
+ return false;
8
+ }
9
+ if (typeof value === 'string' && value.trim() === '') {
10
+ return false;
11
+ }
12
+ if (Array.isArray(value) && value.length === 0) {
13
+ return false;
14
+ }
15
+ if ((field === null || field === void 0 ? void 0 : field.type) === 'checkbox' && value !== true) {
16
+ return false;
17
+ }
18
+ return true;
19
+ };
20
+ exports.shouldPersistValue = shouldPersistValue;
21
+ const normaliseOutputValues = (schema, values) => {
22
+ if (!values) {
23
+ return {};
24
+ }
25
+ const entries = Object.entries(values);
26
+ return entries.reduce((acc, [key, rawValue]) => {
27
+ const field = schema.find(schemaField => String(schemaField.key) === String(key));
28
+ if (!(0, exports.shouldPersistValue)(field, rawValue)) {
29
+ return acc;
30
+ }
31
+ let normalisedValue = rawValue;
32
+ if ((field === null || field === void 0 ? void 0 : field.type) === 'date') {
33
+ const normalised = akidate_1.akidate.toIsoDate(rawValue);
34
+ if (!normalised) {
35
+ return acc;
36
+ }
37
+ normalisedValue = akidate_1.akidate.formatIsoDate(normalised, 'YYYY-MM-DD');
38
+ }
39
+ acc[key] =
40
+ normalisedValue;
41
+ return acc;
42
+ }, {});
43
+ };
44
+ exports.normaliseOutputValues = normaliseOutputValues;
@@ -0,0 +1,53 @@
1
+ import './styles.css';
2
+ import { FieldValues, Path } from '@akinon/akiform';
3
+ import React from 'react';
4
+ import type { AkifilterSchema } from './types';
5
+ type AkifilterFieldValues = FieldValues;
6
+ export type AkifilterProps<TFieldValues extends AkifilterFieldValues = AkifilterFieldValues> = {
7
+ /**
8
+ * Declarative description of the filter fields.
9
+ */
10
+ filterSchema?: AkifilterSchema<TFieldValues>;
11
+ /**
12
+ * Optional namespace for local storage persistence.
13
+ */
14
+ storageNamespace?: string;
15
+ /**
16
+ * Default values supplied by the host application.
17
+ */
18
+ defaultValues?: Partial<TFieldValues>;
19
+ /**
20
+ * Notified on every value change with the normalised payload.
21
+ */
22
+ onValuesChange?: (values: Partial<TFieldValues>) => void;
23
+ /**
24
+ * Notified whenever visible field keys change.
25
+ */
26
+ onVisibleFieldsChange?: (keys: Array<Path<TFieldValues>>) => void;
27
+ /**
28
+ * Triggered when user opts to import filters via CSV.
29
+ */
30
+ onImportCsv?: () => void;
31
+ /**
32
+ * Triggered when user opts to import filters via XLS/XLSX.
33
+ */
34
+ onImportXls?: () => void;
35
+ /**
36
+ * Triggered when user requests clearing all filters.
37
+ */
38
+ onClearAll?: () => void;
39
+ /**
40
+ * Shows the CSV import button in the toolbar. Hidden by default.
41
+ */
42
+ enableImportCsv?: boolean;
43
+ /**
44
+ * Shows the XLS/XLSX import button in the toolbar. Hidden by default.
45
+ */
46
+ enableImportXls?: boolean;
47
+ };
48
+ export declare const Akifilter: {
49
+ <TFieldValues extends AkifilterFieldValues = FieldValues>(props: AkifilterProps<TFieldValues>): React.JSX.Element;
50
+ displayName: string;
51
+ };
52
+ export {};
53
+ //# sourceMappingURL=akifilter.d.ts.map
@@ -0,0 +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;AAUzB,OAAO,KAAK,MAAM,OAAO,CAAC;AAsB1B,OAAO,KAAK,EAAkB,eAAe,EAAE,MAAM,SAAS,CAAC;AAU/D,KAAK,oBAAoB,GAAG,WAAW,CAAC;AA0BxC,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;AAulBF,eAAO,MAAM,SAAS;KACpB,YAAY,SAAS,oBAAoB,uBAElC,cAAc,CAAC,YAAY,CAAC;;CAmBpC,CAAC"}
@@ -0,0 +1,356 @@
1
+ var __rest = (this && this.__rest) || function (s, e) {
2
+ var t = {};
3
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
4
+ t[p] = s[p];
5
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
6
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
7
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
8
+ t[p[i]] = s[p[i]];
9
+ }
10
+ return t;
11
+ };
12
+ import './styles.css';
13
+ import { akidate } from '@akinon/akidate';
14
+ import { Akiform, FormItem, useForm, useWatch } from '@akinon/akiform';
15
+ import { Button } from '@akinon/ui-button';
16
+ import { Card } from '@akinon/ui-card';
17
+ import { Checkbox } from '@akinon/ui-checkbox';
18
+ import { DatePicker } from '@akinon/ui-date-picker';
19
+ import { Input, InputTextArea } from '@akinon/ui-input';
20
+ import { InputNumber } from '@akinon/ui-input-number';
21
+ import { Select } from '@akinon/ui-select';
22
+ import { Text, Title } from '@akinon/ui-typography';
23
+ import { ConfigProvider } from 'antd';
24
+ import React from 'react';
25
+ import { ErrorBoundary } from 'react-error-boundary';
26
+ import { buildStorageKey, clearStoredValues, readStoredValues, readVisibleKeys, writeStoredValues, writeVisibleKeys } from './common/storage';
27
+ import { themeOverrides } from './common/theme-overrides';
28
+ import { AppliedFilters } from './components/applied-filters';
29
+ import { FilterToolbar } from './components/filter-toolbar';
30
+ import { VisibilityModal } from './components/visibility-modal';
31
+ import { DEFAULT_MODAL_PAGE_SIZE, FILTER_DEBOUNCE_DELAY } from './constants';
32
+ import { useDebouncedValue } from './hooks/use-debounced-value';
33
+ import { i18n } from './i18n';
34
+ import { deriveDefaultVisibleKeys, ensureSchemaOrder, extractDefaultValues, getFieldAriaLabel, normaliseValuesBySchema } from './utils/schema';
35
+ import { normaliseOutputValues } from './utils/values';
36
+ const resolveClearedFieldValue = (field, defaultValue) => {
37
+ if (defaultValue !== undefined) {
38
+ return defaultValue;
39
+ }
40
+ switch (field.type) {
41
+ case 'text':
42
+ case 'textarea':
43
+ return '';
44
+ case 'checkbox':
45
+ return false;
46
+ case 'date':
47
+ case 'select':
48
+ return null;
49
+ default:
50
+ return undefined;
51
+ }
52
+ };
53
+ const AkifilterContent = ({ filterSchema, storageNamespace, defaultValues, onValuesChange, onVisibleFieldsChange, onImportCsv, onImportXls, onClearAll, enableImportCsv, enableImportXls }) => {
54
+ const storageKey = React.useMemo(() => buildStorageKey(filterSchema, storageNamespace), [filterSchema, storageNamespace]);
55
+ const schemaDefaults = React.useMemo(() => normaliseValuesBySchema(filterSchema, extractDefaultValues(filterSchema)), [filterSchema]);
56
+ const externalDefaults = React.useMemo(() => normaliseValuesBySchema(filterSchema, defaultValues), [filterSchema, defaultValues]);
57
+ const baseDefaultValues = React.useMemo(() => (Object.assign(Object.assign({}, schemaDefaults), externalDefaults)), [schemaDefaults, externalDefaults]);
58
+ const persistedDefaults = React.useMemo(() => {
59
+ var _a;
60
+ return normaliseValuesBySchema(filterSchema, (_a = readStoredValues(filterSchema, storageKey)) !== null && _a !== void 0 ? _a : undefined);
61
+ }, [filterSchema, storageKey]);
62
+ const mergedDefaultValues = React.useMemo(() => (Object.assign(Object.assign({}, baseDefaultValues), persistedDefaults)), [baseDefaultValues, persistedDefaults]);
63
+ const formMethods = useForm({
64
+ defaultValues: mergedDefaultValues
65
+ });
66
+ const formValues = useWatch({ control: formMethods.control });
67
+ const [isModalOpen, setIsModalOpen] = React.useState(false);
68
+ const [searchTerm, setSearchTerm] = React.useState('');
69
+ const [modalPage, setModalPage] = React.useState(1);
70
+ const [modalPageSize, setModalPageSize] = React.useState(DEFAULT_MODAL_PAGE_SIZE);
71
+ const appliedFilters = React.useMemo(() => {
72
+ if (!formValues) {
73
+ return [];
74
+ }
75
+ return filterSchema.reduce((acc, field) => {
76
+ const key = String(field.key);
77
+ const currentValue = formValues[key];
78
+ if (currentValue === undefined || currentValue === null) {
79
+ return acc;
80
+ }
81
+ if (typeof currentValue === 'string' && currentValue.trim() === '') {
82
+ return acc;
83
+ }
84
+ if (Array.isArray(currentValue) && currentValue.length === 0) {
85
+ return acc;
86
+ }
87
+ if (field.type === 'checkbox' && currentValue !== true) {
88
+ return acc;
89
+ }
90
+ const label = field.label || field.placeholder || key;
91
+ const resolveSelectLabel = () => {
92
+ var _a;
93
+ if (!('options' in field) || !field.options) {
94
+ return String(currentValue);
95
+ }
96
+ const match = field.options.find(option => option.value === currentValue);
97
+ return String((_a = match === null || match === void 0 ? void 0 : match.label) !== null && _a !== void 0 ? _a : currentValue);
98
+ };
99
+ const resolveValue = () => {
100
+ if (field.type === 'select') {
101
+ return resolveSelectLabel();
102
+ }
103
+ if (field.type === 'date') {
104
+ const iso = akidate.toIsoDate(currentValue);
105
+ if (iso) {
106
+ return akidate.formatIsoDate(iso, 'YYYY-MM-DD');
107
+ }
108
+ }
109
+ if (Array.isArray(currentValue)) {
110
+ return currentValue.map((value) => String(value)).join(', ');
111
+ }
112
+ if (typeof currentValue === 'boolean') {
113
+ return currentValue ? 'true' : 'false';
114
+ }
115
+ if (typeof currentValue === 'string' ||
116
+ typeof currentValue === 'number') {
117
+ return String(currentValue);
118
+ }
119
+ const iso = akidate.toIsoDate(currentValue);
120
+ if (iso) {
121
+ return akidate.formatIsoDate(iso, 'YYYY-MM-DD');
122
+ }
123
+ return String(currentValue);
124
+ };
125
+ acc.push({ key, label, value: resolveValue() });
126
+ return acc;
127
+ }, []);
128
+ }, [filterSchema, formValues]);
129
+ const resolveInitialVisibleKeys = React.useCallback(() => {
130
+ const storedKeys = readVisibleKeys(filterSchema, storageKey);
131
+ if (storedKeys && storedKeys.length > 0) {
132
+ return ensureSchemaOrder(filterSchema, storedKeys);
133
+ }
134
+ return deriveDefaultVisibleKeys(filterSchema);
135
+ }, [filterSchema, storageKey]);
136
+ const [visibleKeys, setVisibleKeys] = React.useState(resolveInitialVisibleKeys);
137
+ React.useEffect(() => {
138
+ setVisibleKeys(previous => {
139
+ const ordered = ensureSchemaOrder(filterSchema, previous);
140
+ if (ordered.length) {
141
+ return ordered;
142
+ }
143
+ return resolveInitialVisibleKeys();
144
+ });
145
+ }, [filterSchema, resolveInitialVisibleKeys]);
146
+ React.useEffect(() => {
147
+ writeVisibleKeys(storageKey, visibleKeys);
148
+ onVisibleFieldsChange === null || onVisibleFieldsChange === void 0 ? void 0 : onVisibleFieldsChange(visibleKeys);
149
+ }, [visibleKeys, onVisibleFieldsChange, storageKey]);
150
+ React.useEffect(() => {
151
+ formMethods.reset(mergedDefaultValues);
152
+ }, [formMethods, mergedDefaultValues]);
153
+ const normalisedValues = React.useMemo(() => normaliseOutputValues(filterSchema, formValues), [filterSchema, formValues]);
154
+ const hasInitialValuesRef = React.useRef(false);
155
+ React.useEffect(() => {
156
+ const initialValues = normaliseOutputValues(filterSchema, mergedDefaultValues);
157
+ hasInitialValuesRef.current = Object.keys(initialValues).length > 0;
158
+ }, [filterSchema, mergedDefaultValues]);
159
+ const serialisedValues = React.useMemo(() => JSON.stringify(normalisedValues), [normalisedValues]);
160
+ const debouncedSerialisedValues = useDebouncedValue(serialisedValues, FILTER_DEBOUNCE_DELAY);
161
+ const lastPersistedValuesRef = React.useRef(null);
162
+ const hasEmittedValuesRef = React.useRef(false);
163
+ const persistValues = React.useCallback((values, serialised) => {
164
+ const nextSerialised = serialised !== null && serialised !== void 0 ? serialised : JSON.stringify(values);
165
+ lastPersistedValuesRef.current = nextSerialised;
166
+ if (Object.keys(values).length === 0) {
167
+ clearStoredValues(storageKey);
168
+ return nextSerialised;
169
+ }
170
+ writeStoredValues(storageKey, values);
171
+ return nextSerialised;
172
+ }, [storageKey]);
173
+ React.useEffect(() => {
174
+ if (debouncedSerialisedValues == null) {
175
+ return;
176
+ }
177
+ if (hasEmittedValuesRef.current &&
178
+ lastPersistedValuesRef.current === debouncedSerialisedValues) {
179
+ return;
180
+ }
181
+ const parsedValues = JSON.parse(debouncedSerialisedValues);
182
+ const isEmpty = Object.keys(parsedValues).length === 0;
183
+ if (!hasEmittedValuesRef.current &&
184
+ isEmpty &&
185
+ !hasInitialValuesRef.current) {
186
+ hasEmittedValuesRef.current = true;
187
+ persistValues(parsedValues, debouncedSerialisedValues);
188
+ return;
189
+ }
190
+ hasEmittedValuesRef.current = true;
191
+ onValuesChange === null || onValuesChange === void 0 ? void 0 : onValuesChange(parsedValues);
192
+ persistValues(parsedValues, debouncedSerialisedValues);
193
+ }, [debouncedSerialisedValues, onValuesChange, persistValues]);
194
+ React.useEffect(() => {
195
+ lastPersistedValuesRef.current = null;
196
+ }, [storageKey]);
197
+ const handleClearAll = React.useCallback(() => {
198
+ const clearedDefaults = Object.assign({}, baseDefaultValues);
199
+ filterSchema.forEach(field => {
200
+ const key = String(field.key);
201
+ const defaultValue = baseDefaultValues[key];
202
+ const resolved = resolveClearedFieldValue(field, defaultValue);
203
+ if (resolved === undefined) {
204
+ delete clearedDefaults[key];
205
+ }
206
+ else {
207
+ clearedDefaults[key] = resolved;
208
+ }
209
+ });
210
+ formMethods.reset(clearedDefaults, {
211
+ keepDirty: false,
212
+ keepTouched: false
213
+ });
214
+ onClearAll === null || onClearAll === void 0 ? void 0 : onClearAll();
215
+ const nextValues = normaliseOutputValues(filterSchema, clearedDefaults);
216
+ hasInitialValuesRef.current = Object.keys(nextValues).length > 0;
217
+ const nextSerialised = persistValues(nextValues);
218
+ hasEmittedValuesRef.current = true;
219
+ lastPersistedValuesRef.current = nextSerialised !== null && nextSerialised !== void 0 ? nextSerialised : null;
220
+ onValuesChange === null || onValuesChange === void 0 ? void 0 : onValuesChange(nextValues);
221
+ }, [baseDefaultValues, filterSchema, formMethods, onClearAll, persistValues]);
222
+ const handleOpenModal = React.useCallback(() => {
223
+ setIsModalOpen(true);
224
+ setSearchTerm('');
225
+ setModalPage(1);
226
+ }, []);
227
+ const handleCloseModal = React.useCallback(() => {
228
+ setIsModalOpen(false);
229
+ }, []);
230
+ const handleToggleVisibility = React.useCallback((key) => {
231
+ setVisibleKeys(prev => {
232
+ const exists = prev.includes(key);
233
+ if (exists && prev.length === 1) {
234
+ return prev;
235
+ }
236
+ const nextKeys = exists
237
+ ? prev.filter(item => item !== key)
238
+ : [...prev, key];
239
+ return ensureSchemaOrder(filterSchema, nextKeys);
240
+ });
241
+ }, [filterSchema]);
242
+ const handleRemoveFilter = React.useCallback((key) => {
243
+ const schemaField = filterSchema.find(field => String(field.key) === key);
244
+ if (!schemaField) {
245
+ return;
246
+ }
247
+ const defaultValue = baseDefaultValues[String(schemaField.key)];
248
+ const fieldPath = schemaField.key;
249
+ const nextValue = resolveClearedFieldValue(schemaField, defaultValue);
250
+ formMethods.setValue(fieldPath, nextValue, {
251
+ shouldDirty: false,
252
+ shouldTouch: false,
253
+ shouldValidate: false
254
+ });
255
+ const nextValues = normaliseOutputValues(filterSchema, formMethods.getValues());
256
+ const nextSerialised = persistValues(nextValues);
257
+ hasEmittedValuesRef.current = true;
258
+ lastPersistedValuesRef.current = nextSerialised !== null && nextSerialised !== void 0 ? nextSerialised : null;
259
+ hasInitialValuesRef.current = Object.keys(nextValues).length > 0;
260
+ onValuesChange === null || onValuesChange === void 0 ? void 0 : onValuesChange(nextValues);
261
+ }, [
262
+ baseDefaultValues,
263
+ filterSchema,
264
+ formMethods,
265
+ onValuesChange,
266
+ persistValues
267
+ ]);
268
+ const filteredFields = React.useMemo(() => {
269
+ const normalisedTerm = searchTerm.trim().toLowerCase();
270
+ if (!normalisedTerm) {
271
+ return filterSchema;
272
+ }
273
+ return filterSchema.filter(field => {
274
+ const searchable = [field.label, field.placeholder, String(field.key)]
275
+ .filter(Boolean)
276
+ .map(value => String(value).toLowerCase());
277
+ return searchable.some(text => text.includes(normalisedTerm));
278
+ });
279
+ }, [filterSchema, searchTerm]);
280
+ const paginatedFields = React.useMemo(() => {
281
+ const start = (modalPage - 1) * modalPageSize;
282
+ return filteredFields.slice(start, start + modalPageSize);
283
+ }, [filteredFields, modalPage, modalPageSize]);
284
+ const visibleFields = React.useMemo(() => filterSchema.filter(field => visibleKeys.includes(String(field.key))), [filterSchema, visibleKeys]);
285
+ const renderFieldComponent = (field) => {
286
+ const ariaLabel = getFieldAriaLabel(field);
287
+ switch (field.type) {
288
+ case 'text':
289
+ return (React.createElement(Input, { placeholder: field.placeholder, size: "large", allowClear: true, "aria-label": ariaLabel }));
290
+ case 'number':
291
+ return (React.createElement(InputNumber, { placeholder: field.placeholder, size: "large", className: "akinon-filter__field--number", "aria-label": ariaLabel }));
292
+ case 'select':
293
+ return (React.createElement(Select, { placeholder: field.placeholder, size: "large", options: field.options, showSearch: true, optionFilterProp: "label", "aria-label": ariaLabel }));
294
+ case 'checkbox':
295
+ return React.createElement(Checkbox, null, field.label);
296
+ case 'date':
297
+ return (React.createElement(DatePicker, { placeholder: field.placeholder, showTime: field.showTime, suffixIcon: "calendar", suffixIconSize: "16px", "aria-label": ariaLabel }));
298
+ case 'textarea':
299
+ return (React.createElement(InputTextArea, { placeholder: field.placeholder, autoSize: { minRows: 3, maxRows: 6 }, "aria-label": ariaLabel }));
300
+ case 'custom':
301
+ if (typeof field.render === 'function') {
302
+ return field.render({
303
+ field,
304
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
305
+ control: formMethods.control,
306
+ formValues: formMethods.getValues(),
307
+ formState: formMethods.formState
308
+ });
309
+ }
310
+ return (React.createElement(Text, { type: "secondary", className: "akinon-filter__unsupported" }, i18n.t('form.unsupportedField', { field: String(field.type) })));
311
+ default:
312
+ return (React.createElement(Text, { type: "secondary", className: "akinon-filter__unsupported" }, i18n.t('form.unsupportedField', { field: String(field.type) })));
313
+ }
314
+ };
315
+ return (React.createElement(Card, { size: "small", className: "akinon-filter shadow", "data-testid": "akifilter-root" },
316
+ React.createElement(ConfigProvider, { theme: themeOverrides },
317
+ React.createElement(FilterToolbar, { onOpenModal: handleOpenModal, enableImportCsv: enableImportCsv, enableImportXls: enableImportXls, onImportCsv: onImportCsv, onImportXls: onImportXls }),
318
+ React.createElement(AppliedFilters, { filters: appliedFilters, onRemove: handleRemoveFilter, onClearAll: handleClearAll }),
319
+ React.createElement("div", { className: "akinon-filter__body" },
320
+ React.createElement(Akiform, { layout: "vertical", className: "akinon-filter__form" },
321
+ React.createElement("div", { className: "akinon-filter__form-grid", "data-testid": "akifilter-form-grid" },
322
+ visibleFields.map(field => (React.createElement(FormItem, { 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)))),
323
+ visibleFields.length === 0 ? (React.createElement("div", { className: "akinon-filter__empty" }, i18n.t('form.noVisibleFields'))) : null))),
324
+ React.createElement(VisibilityModal, { open: isModalOpen, onClose: handleCloseModal, searchTerm: searchTerm, paginatedFields: paginatedFields, filteredCount: filteredFields.length, visibleKeys: visibleKeys, onSearchTermChange: value => {
325
+ setSearchTerm(value);
326
+ setModalPage(1);
327
+ }, onToggleVisibility: handleToggleVisibility, onPaginationChange: (page, pageSize) => {
328
+ setModalPage(page);
329
+ if (pageSize !== modalPageSize) {
330
+ setModalPageSize(pageSize);
331
+ }
332
+ }, page: modalPage, pageSize: modalPageSize }))));
333
+ };
334
+ const AkifilterEmptyState = () => {
335
+ const { t } = i18n;
336
+ return (React.createElement(Card, { size: "small", className: "akinon-filter shadow", "data-testid": "akifilter-empty" },
337
+ React.createElement(ConfigProvider, { theme: themeOverrides },
338
+ React.createElement("div", { className: "akinon-filter__empty-state" },
339
+ React.createElement(Text, null, t('emptyState.message'))))));
340
+ };
341
+ const renderErrorFallback = ({ resetErrorBoundary }) => {
342
+ const { t } = i18n;
343
+ return (React.createElement(Card, { size: "small", className: "akinon-filter shadow", "data-testid": "akifilter-error" },
344
+ React.createElement(ConfigProvider, { theme: themeOverrides },
345
+ React.createElement("div", { className: "akinon-filter__error-state" },
346
+ React.createElement(Title, { level: 5, className: "akinon-filter__error-title" }, t('errors.title')),
347
+ React.createElement(Text, { type: "secondary", className: "akinon-filter__error-description" }, t('errors.description')),
348
+ React.createElement(Button, { type: "primary", onClick: resetErrorBoundary, size: "small" }, t('errors.retry'))))));
349
+ };
350
+ export const Akifilter = (props) => {
351
+ const { filterSchema } = props, restProps = __rest(props, ["filterSchema"]);
352
+ const resolvedSchema = (filterSchema !== null && filterSchema !== void 0 ? filterSchema : []);
353
+ const hasSchema = resolvedSchema.length > 0;
354
+ return (React.createElement(ErrorBoundary, { fallbackRender: renderErrorFallback }, hasSchema ? (React.createElement(AkifilterContent, Object.assign({}, restProps, { filterSchema: resolvedSchema }))) : (React.createElement(AkifilterEmptyState, null))));
355
+ };
356
+ Akifilter.displayName = 'Akifilter';
@@ -0,0 +1,10 @@
1
+ import type { FieldValues } from '@akinon/akiform';
2
+ import type { AkifilterField } from '../types';
3
+ export declare const buildStorageKey: <TFieldValues extends FieldValues>(schema: AkifilterField<TFieldValues>[], namespace?: string) => string;
4
+ export declare const readVisibleKeys: <TFieldValues extends FieldValues>(schema: AkifilterField<TFieldValues>[], storageKey: string) => string[] | null;
5
+ export declare const writeVisibleKeys: (storageKey: string, keys: string[]) => void;
6
+ export declare const clearVisibleKeys: (storageKey: string) => void;
7
+ export declare const readStoredValues: <TFieldValues extends FieldValues>(schema: AkifilterField<TFieldValues>[], storageKey: string) => Partial<TFieldValues> | null;
8
+ export declare const writeStoredValues: <TFieldValues extends FieldValues>(storageKey: string, values: Partial<TFieldValues>) => void;
9
+ export declare const clearStoredValues: (storageKey: string) => void;
10
+ //# sourceMappingURL=storage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"storage.d.ts","sourceRoot":"","sources":["../../../src/common/storage.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAEnD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAqB/C,eAAO,MAAM,eAAe,GAAI,YAAY,SAAS,WAAW,EAC9D,QAAQ,cAAc,CAAC,YAAY,CAAC,EAAE,EACtC,YAAY,MAAM,KACjB,MAKF,CAAC;AAIF,eAAO,MAAM,eAAe,GAAI,YAAY,SAAS,WAAW,EAC9D,QAAQ,cAAc,CAAC,YAAY,CAAC,EAAE,EACtC,YAAY,MAAM,KACjB,MAAM,EAAE,GAAG,IAuBb,CAAC;AAEF,eAAO,MAAM,gBAAgB,GAAI,YAAY,MAAM,EAAE,MAAM,MAAM,EAAE,KAAG,IAQrE,CAAC;AAEF,eAAO,MAAM,gBAAgB,GAAI,YAAY,MAAM,KAAG,IAQrD,CAAC;AAKF,eAAO,MAAM,gBAAgB,GAAI,YAAY,SAAS,WAAW,EAC/D,QAAQ,cAAc,CAAC,YAAY,CAAC,EAAE,EACtC,YAAY,MAAM,KACjB,OAAO,CAAC,YAAY,CAAC,GAAG,IA+B1B,CAAC;AAEF,eAAO,MAAM,iBAAiB,GAAI,YAAY,SAAS,WAAW,EAChE,YAAY,MAAM,EAClB,QAAQ,OAAO,CAAC,YAAY,CAAC,KAC5B,IAeF,CAAC;AAEF,eAAO,MAAM,iBAAiB,GAAI,YAAY,MAAM,KAAG,IAQtD,CAAC"}
@@ -0,0 +1,114 @@
1
+ const STORAGE_PREFIX = 'akifilter:v1';
2
+ const VALUES_SUFFIX = ':values';
3
+ const hashString = (value) => {
4
+ let hash = 0;
5
+ for (let i = 0; i < value.length; i += 1) {
6
+ const chr = value.charCodeAt(i);
7
+ hash = (hash << 5) - hash + chr;
8
+ hash |= 0; // Convert to 32bit integer
9
+ }
10
+ return Math.abs(hash).toString(36);
11
+ };
12
+ const buildSchemaSignature = (schema) => {
13
+ return schema.map(field => `${String(field.key)}:${field.type}`).join('|');
14
+ };
15
+ export const buildStorageKey = (schema, namespace) => {
16
+ const signature = buildSchemaSignature(schema);
17
+ const hashedSignature = hashString(signature);
18
+ const base = namespace ? namespace.trim() : 'default';
19
+ return `${STORAGE_PREFIX}:${base}:${hashedSignature}`;
20
+ };
21
+ const isBrowser = typeof window !== 'undefined';
22
+ export const readVisibleKeys = (schema, storageKey) => {
23
+ if (!isBrowser)
24
+ return null;
25
+ try {
26
+ const rawValue = window.localStorage.getItem(storageKey);
27
+ if (!rawValue) {
28
+ return null;
29
+ }
30
+ const parsed = JSON.parse(rawValue);
31
+ if (!Array.isArray(parsed)) {
32
+ return null;
33
+ }
34
+ const schemaKeys = new Set(schema.map(field => String(field.key)));
35
+ return parsed.filter(key => schemaKeys.has(String(key)));
36
+ }
37
+ catch (error) {
38
+ console.warn('Akifilter: unable to read visibility state from storage', error);
39
+ return null;
40
+ }
41
+ };
42
+ export const writeVisibleKeys = (storageKey, keys) => {
43
+ if (!isBrowser)
44
+ return;
45
+ try {
46
+ window.localStorage.setItem(storageKey, JSON.stringify(keys));
47
+ }
48
+ catch (error) {
49
+ console.warn('Akifilter: unable to persist visibility state', error);
50
+ }
51
+ };
52
+ export const clearVisibleKeys = (storageKey) => {
53
+ if (!isBrowser)
54
+ return;
55
+ try {
56
+ window.localStorage.removeItem(storageKey);
57
+ }
58
+ catch (error) {
59
+ console.warn('Akifilter: unable to clear visibility state', error);
60
+ }
61
+ };
62
+ const buildValuesStorageKey = (storageKey) => `${storageKey}${VALUES_SUFFIX}`;
63
+ export const readStoredValues = (schema, storageKey) => {
64
+ if (!isBrowser)
65
+ return null;
66
+ try {
67
+ const rawValue = window.localStorage.getItem(buildValuesStorageKey(storageKey));
68
+ if (!rawValue) {
69
+ return null;
70
+ }
71
+ const parsed = JSON.parse(rawValue);
72
+ if (!parsed || typeof parsed !== 'object') {
73
+ return null;
74
+ }
75
+ const schemaKeys = new Set(schema.map(field => String(field.key)));
76
+ return Object.entries(parsed).reduce((acc, [key, value]) => {
77
+ if (schemaKeys.has(String(key))) {
78
+ acc[key] =
79
+ value;
80
+ }
81
+ return acc;
82
+ }, {});
83
+ }
84
+ catch (error) {
85
+ console.warn('Akifilter: unable to read values from storage', error);
86
+ return null;
87
+ }
88
+ };
89
+ export const writeStoredValues = (storageKey, values) => {
90
+ if (!isBrowser)
91
+ return;
92
+ const valuesKey = buildValuesStorageKey(storageKey);
93
+ const entries = Object.entries(values);
94
+ try {
95
+ if (entries.length === 0) {
96
+ window.localStorage.removeItem(valuesKey);
97
+ return;
98
+ }
99
+ window.localStorage.setItem(valuesKey, JSON.stringify(values));
100
+ }
101
+ catch (error) {
102
+ console.warn('Akifilter: unable to persist values', error);
103
+ }
104
+ };
105
+ export const clearStoredValues = (storageKey) => {
106
+ if (!isBrowser)
107
+ return;
108
+ try {
109
+ window.localStorage.removeItem(buildValuesStorageKey(storageKey));
110
+ }
111
+ catch (error) {
112
+ console.warn('Akifilter: unable to clear stored values', error);
113
+ }
114
+ };