@akinon/akifilter 1.7.1 → 1.8.0-260803-0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/akifilter.d.ts +0 -10
- package/dist/cjs/akifilter.d.ts.map +1 -1
- package/dist/cjs/akifilter.js +14 -108
- package/dist/cjs/common/storage.d.ts +1 -0
- package/dist/cjs/common/storage.d.ts.map +1 -1
- package/dist/cjs/common/storage.js +3 -1
- package/dist/cjs/components/section-fieldset.d.ts +29 -0
- package/dist/cjs/components/section-fieldset.d.ts.map +1 -0
- package/dist/cjs/components/section-fieldset.js +97 -0
- package/dist/cjs/hooks/use-field-visibility.d.ts +45 -0
- package/dist/cjs/hooks/use-field-visibility.d.ts.map +1 -0
- package/dist/cjs/hooks/use-field-visibility.js +129 -0
- package/dist/cjs/styles.css +23 -0
- package/dist/cjs/types.d.ts +8 -1
- package/dist/cjs/types.d.ts.map +1 -1
- package/dist/cjs/utils/schema.d.ts +3 -13
- package/dist/cjs/utils/schema.d.ts.map +1 -1
- package/dist/esm/akifilter.d.ts +0 -10
- package/dist/esm/akifilter.d.ts.map +1 -1
- package/dist/esm/akifilter.js +16 -109
- package/dist/esm/common/storage.d.ts +1 -0
- package/dist/esm/common/storage.d.ts.map +1 -1
- package/dist/esm/common/storage.js +1 -0
- package/dist/esm/components/section-fieldset.d.ts +29 -0
- package/dist/esm/components/section-fieldset.d.ts.map +1 -0
- package/dist/esm/components/section-fieldset.js +93 -0
- package/dist/esm/hooks/use-field-visibility.d.ts +45 -0
- package/dist/esm/hooks/use-field-visibility.d.ts.map +1 -0
- package/dist/esm/hooks/use-field-visibility.js +125 -0
- package/dist/esm/styles.css +23 -0
- package/dist/esm/types.d.ts +8 -1
- package/dist/esm/types.d.ts.map +1 -1
- package/dist/esm/utils/schema.d.ts +3 -13
- package/dist/esm/utils/schema.d.ts.map +1 -1
- package/package.json +6 -6
package/dist/cjs/akifilter.d.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import './styles.css';
|
|
2
2
|
import { FieldValues, Path } from '@akinon/akiform';
|
|
3
|
-
import { type PanelProps } from '@akinon/ui-collapse';
|
|
4
3
|
import React from 'react';
|
|
5
4
|
import { type AppliedFilter } from './components/applied-filters';
|
|
6
5
|
import type { AkifilterActionsRef, AkifilterButton, AkifilterSchema } from './types';
|
|
@@ -73,15 +72,6 @@ export type AkifilterProps<TFieldValues extends AkifilterFieldValues = Akifilter
|
|
|
73
72
|
*/
|
|
74
73
|
onFilterButtonsChange?: (activeButtons: AkifilterButton[]) => void;
|
|
75
74
|
};
|
|
76
|
-
export declare const ExpandIcon: ({ isActive }: PanelProps) => {
|
|
77
|
-
name: "chevron_down";
|
|
78
|
-
size: number;
|
|
79
|
-
style: {
|
|
80
|
-
transform: string;
|
|
81
|
-
transition: string;
|
|
82
|
-
marginTop: number;
|
|
83
|
-
};
|
|
84
|
-
};
|
|
85
75
|
export declare const Akifilter: {
|
|
86
76
|
<TFieldValues extends AkifilterFieldValues = FieldValues>(props: AkifilterProps<TFieldValues>): React.JSX.Element;
|
|
87
77
|
displayName: string;
|
|
@@ -1 +1 @@
|
|
|
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;
|
|
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;AAYzB,OAAO,KAAK,MAAM,OAAO,CAAC;AAc1B,OAAO,EACL,KAAK,aAAa,EAEnB,MAAM,8BAA8B,CAAC;AAgBtC,OAAO,KAAK,EACV,mBAAmB,EACnB,eAAe,EAEf,eAAe,EAChB,MAAM,SAAS,CAAC;AAWjB,KAAK,oBAAoB,GAAG,WAAW,CAAC;AA2KxC,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;;;OAGG;IACH,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,IAAI,CAAC;IACnC;;;OAGG;IACH,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,IAAI,CAAC;IACnC;;OAEG;IACH,UAAU,CAAC,EAAE,MAAM,IAAI,CAAC;IACxB;;OAEG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B;;OAEG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B;;OAEG;IACH,gBAAgB,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,mBAAmB,CAAC,CAAC;IAClD;;;OAGG;IACH,sBAAsB,CAAC,EAAE,aAAa,EAAE,CAAC;IACzC;;OAEG;IACH,sBAAsB,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/C;;;;;OAKG;IACH,aAAa,CAAC,EAAE,eAAe,EAAE,CAAC;IAClC;;OAEG;IACH,qBAAqB,CAAC,EAAE,CAAC,aAAa,EAAE,eAAe,EAAE,KAAK,IAAI,CAAC;CACpE,CAAC;AA+3BF,eAAO,MAAM,SAAS;KACpB,YAAY,SAAS,oBAAoB,uBAElC,cAAc,CAAC,YAAY,CAAC;;CAoBpC,CAAC"}
|
package/dist/cjs/akifilter.js
CHANGED
|
@@ -11,14 +11,13 @@ var __rest = (this && this.__rest) || function (s, e) {
|
|
|
11
11
|
return t;
|
|
12
12
|
};
|
|
13
13
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
14
|
-
exports.Akifilter =
|
|
14
|
+
exports.Akifilter = void 0;
|
|
15
15
|
require("./styles.css");
|
|
16
16
|
const akidate_1 = require("@akinon/akidate");
|
|
17
17
|
const akiform_1 = require("@akinon/akiform");
|
|
18
18
|
const ui_button_1 = require("@akinon/ui-button");
|
|
19
19
|
const ui_card_1 = require("@akinon/ui-card");
|
|
20
20
|
const ui_checkbox_1 = require("@akinon/ui-checkbox");
|
|
21
|
-
const ui_collapse_1 = require("@akinon/ui-collapse");
|
|
22
21
|
const ui_date_picker_1 = require("@akinon/ui-date-picker");
|
|
23
22
|
const ui_input_1 = require("@akinon/ui-input");
|
|
24
23
|
const ui_input_number_1 = require("@akinon/ui-input-number");
|
|
@@ -33,10 +32,12 @@ const theme_overrides_1 = require("./common/theme-overrides");
|
|
|
33
32
|
const applied_filters_1 = require("./components/applied-filters");
|
|
34
33
|
const filter_buttons_1 = require("./components/filter-buttons");
|
|
35
34
|
const filter_toolbar_1 = require("./components/filter-toolbar");
|
|
35
|
+
const section_fieldset_1 = require("./components/section-fieldset");
|
|
36
36
|
const visibility_modal_1 = require("./components/visibility-modal");
|
|
37
37
|
const constants_1 = require("./constants");
|
|
38
38
|
const use_debounced_value_1 = require("./hooks/use-debounced-value");
|
|
39
39
|
const use_dynamic_visibility_1 = require("./hooks/use-dynamic-visibility");
|
|
40
|
+
const use_field_visibility_1 = require("./hooks/use-field-visibility");
|
|
40
41
|
const use_visibility_cleanup_1 = require("./hooks/use-visibility-cleanup");
|
|
41
42
|
const i18n_1 = require("./i18n");
|
|
42
43
|
const schema_1 = require("./utils/schema");
|
|
@@ -152,10 +153,6 @@ const AkifilterContent = ({ filterSchema, storageNamespace, defaultValues, onVal
|
|
|
152
153
|
defaultValues: mergedDefaultValues
|
|
153
154
|
});
|
|
154
155
|
const formValues = (0, akiform_1.useWatch)({ control: formMethods.control });
|
|
155
|
-
const [isModalOpen, setIsModalOpen] = react_1.default.useState(false);
|
|
156
|
-
const [searchTerm, setSearchTerm] = react_1.default.useState('');
|
|
157
|
-
const [modalPage, setModalPage] = react_1.default.useState(1);
|
|
158
|
-
const [modalPageSize, setModalPageSize] = react_1.default.useState(constants_1.DEFAULT_MODAL_PAGE_SIZE);
|
|
159
156
|
// --- Filter buttons (quick-filter toggles) ---------------------------------
|
|
160
157
|
// A button is identified by its `value`, which must be unique across the
|
|
161
158
|
// whole `filterButtons` array.
|
|
@@ -283,41 +280,14 @@ const AkifilterContent = ({ filterSchema, storageNamespace, defaultValues, onVal
|
|
|
283
280
|
value: button.label
|
|
284
281
|
}));
|
|
285
282
|
}, [filterButtons, activeButtonValues]);
|
|
286
|
-
const
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
const explicitVisibility = (0, schema_1.resolveFieldVisibility)(field);
|
|
295
|
-
if (explicitVisibility !== undefined) {
|
|
296
|
-
if (explicitVisibility)
|
|
297
|
-
acc.push(key);
|
|
298
|
-
return acc;
|
|
299
|
-
}
|
|
300
|
-
// No explicit boolean visibility, use stored/default
|
|
301
|
-
if (baseKeys.includes(key)) {
|
|
302
|
-
acc.push(key);
|
|
303
|
-
}
|
|
304
|
-
return acc;
|
|
305
|
-
}, []);
|
|
306
|
-
}, [regularFields, storageKey]);
|
|
307
|
-
const [visibleKeys, setVisibleKeys] = react_1.default.useState(resolveInitialVisibleKeys);
|
|
308
|
-
react_1.default.useEffect(() => {
|
|
309
|
-
setVisibleKeys(previous => {
|
|
310
|
-
const ordered = (0, schema_1.ensureSchemaOrder)(regularFields, previous);
|
|
311
|
-
if (ordered.length) {
|
|
312
|
-
return ordered;
|
|
313
|
-
}
|
|
314
|
-
return resolveInitialVisibleKeys();
|
|
315
|
-
});
|
|
316
|
-
}, [regularFields, resolveInitialVisibleKeys]);
|
|
317
|
-
react_1.default.useEffect(() => {
|
|
318
|
-
(0, storage_1.writeVisibleKeys)(storageKey, visibleKeys);
|
|
319
|
-
onVisibleFieldsChange === null || onVisibleFieldsChange === void 0 ? void 0 : onVisibleFieldsChange(visibleKeys);
|
|
320
|
-
}, [visibleKeys, onVisibleFieldsChange, storageKey]);
|
|
283
|
+
const handleVisibleKeysChange = react_1.default.useCallback((keys) => {
|
|
284
|
+
onVisibleFieldsChange === null || onVisibleFieldsChange === void 0 ? void 0 : onVisibleFieldsChange(keys);
|
|
285
|
+
}, [onVisibleFieldsChange]);
|
|
286
|
+
const { visibleKeys, setVisibleKeys, visibleFields, openModal, modalProps } = (0, use_field_visibility_1.useFieldVisibility)({
|
|
287
|
+
fields: regularFields,
|
|
288
|
+
storageKey,
|
|
289
|
+
onVisibleKeysChange: handleVisibleKeysChange
|
|
290
|
+
});
|
|
321
291
|
const normalisedValues = react_1.default.useMemo(() => (0, values_1.normaliseOutputValues)(flattenedSchema, formValues), [flattenedSchema, formValues]);
|
|
322
292
|
const hasInitialValuesRef = react_1.default.useRef(false);
|
|
323
293
|
react_1.default.useEffect(() => {
|
|
@@ -448,26 +418,6 @@ const AkifilterContent = ({ filterSchema, storageNamespace, defaultValues, onVal
|
|
|
448
418
|
emitValues,
|
|
449
419
|
persistValues
|
|
450
420
|
]);
|
|
451
|
-
const handleOpenModal = react_1.default.useCallback(() => {
|
|
452
|
-
setIsModalOpen(true);
|
|
453
|
-
setSearchTerm('');
|
|
454
|
-
setModalPage(1);
|
|
455
|
-
}, []);
|
|
456
|
-
const handleCloseModal = react_1.default.useCallback(() => {
|
|
457
|
-
setIsModalOpen(false);
|
|
458
|
-
}, []);
|
|
459
|
-
const handleToggleVisibility = react_1.default.useCallback((key) => {
|
|
460
|
-
setVisibleKeys(prev => {
|
|
461
|
-
const exists = prev.includes(key);
|
|
462
|
-
if (exists && prev.length === 1) {
|
|
463
|
-
return prev;
|
|
464
|
-
}
|
|
465
|
-
const nextKeys = exists
|
|
466
|
-
? prev.filter(item => item !== key)
|
|
467
|
-
: [...prev, key];
|
|
468
|
-
return (0, schema_1.ensureSchemaOrder)(regularFields, nextKeys);
|
|
469
|
-
});
|
|
470
|
-
}, [regularFields]);
|
|
471
421
|
const handleRemoveFilter = react_1.default.useCallback((key) => {
|
|
472
422
|
const schemaField = flattenedSchema.find(field => String(field.key) === key);
|
|
473
423
|
if (!schemaField) {
|
|
@@ -548,23 +498,6 @@ const AkifilterContent = ({ filterSchema, storageNamespace, defaultValues, onVal
|
|
|
548
498
|
keysArray.forEach(key => handleRemoveFilter(key));
|
|
549
499
|
}
|
|
550
500
|
}), [handleRemoveFilter]);
|
|
551
|
-
const filteredFields = react_1.default.useMemo(() => {
|
|
552
|
-
const normalisedTerm = searchTerm.trim().toLowerCase();
|
|
553
|
-
if (!normalisedTerm) {
|
|
554
|
-
return regularFields;
|
|
555
|
-
}
|
|
556
|
-
return regularFields.filter(field => {
|
|
557
|
-
const searchable = [field.label, field.placeholder, String(field.key)]
|
|
558
|
-
.filter(Boolean)
|
|
559
|
-
.map(value => String(value).toLowerCase());
|
|
560
|
-
return searchable.some(text => text.includes(normalisedTerm));
|
|
561
|
-
});
|
|
562
|
-
}, [regularFields, searchTerm]);
|
|
563
|
-
const paginatedFields = react_1.default.useMemo(() => {
|
|
564
|
-
const start = (modalPage - 1) * modalPageSize;
|
|
565
|
-
return filteredFields.slice(start, start + modalPageSize);
|
|
566
|
-
}, [filteredFields, modalPage, modalPageSize]);
|
|
567
|
-
const visibleFields = react_1.default.useMemo(() => regularFields.filter(field => visibleKeys.includes(String(field.key))), [regularFields, visibleKeys]);
|
|
568
501
|
// Dynamic visibility
|
|
569
502
|
(0, use_dynamic_visibility_1.useDynamicVisibility)({
|
|
570
503
|
regularFields,
|
|
@@ -655,7 +588,7 @@ const AkifilterContent = ({ filterSchema, storageNamespace, defaultValues, onVal
|
|
|
655
588
|
};
|
|
656
589
|
return (react_1.default.createElement(ui_card_1.Card, { size: "small", className: "akinon-filter shadow", "data-testid": "akifilter-root" },
|
|
657
590
|
react_1.default.createElement(antd_1.ConfigProvider, { theme: theme_overrides_1.themeOverrides },
|
|
658
|
-
react_1.default.createElement(filter_toolbar_1.FilterToolbar, { onOpenModal:
|
|
591
|
+
react_1.default.createElement(filter_toolbar_1.FilterToolbar, { onOpenModal: openModal, enableImportCsv: enableImportCsv, enableImportXls: enableImportXls, onImportCsv: onImportCsv, onImportXls: onImportXls }),
|
|
659
592
|
react_1.default.createElement(applied_filters_1.AppliedFilters, { filters: [
|
|
660
593
|
...appliedFilters,
|
|
661
594
|
...buttonAppliedFilters,
|
|
@@ -666,36 +599,9 @@ const AkifilterContent = ({ filterSchema, storageNamespace, defaultValues, onVal
|
|
|
666
599
|
react_1.default.createElement("div", { className: "akinon-filter__form-grid", "data-testid": "akifilter-form-grid" },
|
|
667
600
|
visibleFields.map(renderFormField),
|
|
668
601
|
visibleFields.length === 0 && regularFields.length > 0 ? (react_1.default.createElement("div", { className: "akinon-filter__empty" }, i18n_1.i18n.t('form.noVisibleFields'))) : null),
|
|
669
|
-
sectionFields.length > 0 && (react_1.default.createElement("div", { className: "akinon-filter__section-fields" }, sectionFields.map(section => {
|
|
670
|
-
|
|
671
|
-
{
|
|
672
|
-
key: section.key,
|
|
673
|
-
label: (react_1.default.createElement(ui_typography_1.Title, { className: "akinon-filter__title", level: 4 }, section.label)),
|
|
674
|
-
headerClass: 'akinon-filter__section-header',
|
|
675
|
-
children: (react_1.default.createElement("div", { className: "akinon-filter__form-grid" }, section.fields.map(renderFormField)))
|
|
676
|
-
}
|
|
677
|
-
] }));
|
|
678
|
-
}))))),
|
|
679
|
-
react_1.default.createElement(visibility_modal_1.VisibilityModal, { open: isModalOpen, onClose: handleCloseModal, searchTerm: searchTerm, paginatedFields: paginatedFields, filteredCount: filteredFields.length, visibleKeys: visibleKeys, onSearchTermChange: value => {
|
|
680
|
-
setSearchTerm(value);
|
|
681
|
-
setModalPage(1);
|
|
682
|
-
}, onToggleVisibility: handleToggleVisibility, onPaginationChange: (page, pageSize) => {
|
|
683
|
-
setModalPage(page);
|
|
684
|
-
if (pageSize !== modalPageSize) {
|
|
685
|
-
setModalPageSize(pageSize);
|
|
686
|
-
}
|
|
687
|
-
}, page: modalPage, pageSize: modalPageSize }))));
|
|
602
|
+
sectionFields.length > 0 && (react_1.default.createElement("div", { className: "akinon-filter__section-fields" }, sectionFields.map(section => (react_1.default.createElement(section_fieldset_1.SectionFieldset, { key: String(section.key), section: section, storageKey: storageKey, renderFormField: renderFormField, onRemoveField: handleRemoveFilter, formValues: normalisedValues }))))))),
|
|
603
|
+
react_1.default.createElement(visibility_modal_1.VisibilityModal, Object.assign({}, modalProps)))));
|
|
688
604
|
};
|
|
689
|
-
const ExpandIcon = ({ isActive }) => ({
|
|
690
|
-
name: 'chevron_down',
|
|
691
|
-
size: 10,
|
|
692
|
-
style: {
|
|
693
|
-
transform: isActive ? 'rotate(-180deg)' : 'rotate(0deg)',
|
|
694
|
-
transition: 'transform 0.2s ease-in-out',
|
|
695
|
-
marginTop: 8
|
|
696
|
-
}
|
|
697
|
-
});
|
|
698
|
-
exports.ExpandIcon = ExpandIcon;
|
|
699
605
|
const AkifilterEmptyState = () => {
|
|
700
606
|
const { t } = i18n_1.i18n;
|
|
701
607
|
return (react_1.default.createElement(ui_card_1.Card, { size: "small", className: "akinon-filter shadow", "data-testid": "akifilter-empty" },
|
|
@@ -3,6 +3,7 @@ import type { AkifilterField } from '../types';
|
|
|
3
3
|
export declare const buildStorageKey: <TFieldValues extends FieldValues>(schema: AkifilterField<TFieldValues>[], namespace?: string) => string;
|
|
4
4
|
export declare const readVisibleKeys: <TFieldValues extends FieldValues>(schema: AkifilterField<TFieldValues>[], storageKey: string) => string[] | null;
|
|
5
5
|
export declare const writeVisibleKeys: (storageKey: string, keys: string[]) => void;
|
|
6
|
+
export declare const buildSectionVisibilityKey: (storageKey: string, sectionKey: string | number) => string;
|
|
6
7
|
export declare const clearVisibleKeys: (storageKey: string) => void;
|
|
7
8
|
export declare const readStoredValues: <TFieldValues extends FieldValues>(schema: AkifilterField<TFieldValues>[], storageKey: string) => Partial<TFieldValues> | null;
|
|
8
9
|
export declare const writeStoredValues: <TFieldValues extends FieldValues>(storageKey: string, values: Partial<TFieldValues>) => void;
|
|
@@ -1 +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;AAGnD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AA4B/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,IAoBb,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;AAKF,eAAO,MAAM,sBAAsB,GACjC,YAAY,MAAM,KACjB,KAAK,CAAC,MAAM,GAAG,MAAM,CAAC,GAAG,IAwB3B,CAAC;AAEF,eAAO,MAAM,uBAAuB,GAClC,YAAY,MAAM,EAClB,QAAQ,KAAK,CAAC,MAAM,GAAG,MAAM,CAAC,KAC7B,IAcF,CAAC;AAEF,eAAO,MAAM,uBAAuB,GAAI,YAAY,MAAM,KAAG,IAQ5D,CAAC"}
|
|
1
|
+
{"version":3,"file":"storage.d.ts","sourceRoot":"","sources":["../../../src/common/storage.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAGnD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AA4B/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,IAoBb,CAAC;AAEF,eAAO,MAAM,gBAAgB,GAAI,YAAY,MAAM,EAAE,MAAM,MAAM,EAAE,KAAG,IAQrE,CAAC;AAEF,eAAO,MAAM,yBAAyB,GACpC,YAAY,MAAM,EAClB,YAAY,MAAM,GAAG,MAAM,KAC1B,MAAuD,CAAC;AAE3D,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;AAKF,eAAO,MAAM,sBAAsB,GACjC,YAAY,MAAM,KACjB,KAAK,CAAC,MAAM,GAAG,MAAM,CAAC,GAAG,IAwB3B,CAAC;AAEF,eAAO,MAAM,uBAAuB,GAClC,YAAY,MAAM,EAClB,QAAQ,KAAK,CAAC,MAAM,GAAG,MAAM,CAAC,KAC7B,IAcF,CAAC;AAEF,eAAO,MAAM,uBAAuB,GAAI,YAAY,MAAM,KAAG,IAQ5D,CAAC"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.clearStoredButtonValues = exports.writeStoredButtonValues = exports.readStoredButtonValues = exports.clearStoredValues = exports.writeStoredValues = exports.readStoredValues = exports.clearVisibleKeys = exports.writeVisibleKeys = exports.readVisibleKeys = exports.buildStorageKey = void 0;
|
|
3
|
+
exports.clearStoredButtonValues = exports.writeStoredButtonValues = exports.readStoredButtonValues = exports.clearStoredValues = exports.writeStoredValues = exports.readStoredValues = exports.clearVisibleKeys = exports.buildSectionVisibilityKey = exports.writeVisibleKeys = exports.readVisibleKeys = exports.buildStorageKey = void 0;
|
|
4
4
|
const i18n_1 = require("../i18n");
|
|
5
5
|
const STORAGE_PREFIX = 'akifilter:v1';
|
|
6
6
|
const VALUES_SUFFIX = ':values';
|
|
@@ -62,6 +62,8 @@ const writeVisibleKeys = (storageKey, keys) => {
|
|
|
62
62
|
}
|
|
63
63
|
};
|
|
64
64
|
exports.writeVisibleKeys = writeVisibleKeys;
|
|
65
|
+
const buildSectionVisibilityKey = (storageKey, sectionKey) => `${storageKey}:section:${String(sectionKey)}`;
|
|
66
|
+
exports.buildSectionVisibilityKey = buildSectionVisibilityKey;
|
|
65
67
|
const clearVisibleKeys = (storageKey) => {
|
|
66
68
|
if (!isBrowser)
|
|
67
69
|
return;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { FieldValues } from '@akinon/akiform';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import type { AkifilterField, AkifilterSectionField } from '../types';
|
|
4
|
+
type SectionFieldsetProps<TFieldValues extends FieldValues = FieldValues> = {
|
|
5
|
+
section: AkifilterSectionField<TFieldValues>;
|
|
6
|
+
/**
|
|
7
|
+
* Base storage key of the host Akifilter; combined with the section key to
|
|
8
|
+
* persist this section's visible-field selection.
|
|
9
|
+
*/
|
|
10
|
+
storageKey: string;
|
|
11
|
+
renderFormField: (field: AkifilterField<TFieldValues>) => React.ReactNode;
|
|
12
|
+
/**
|
|
13
|
+
* Clears a field's form value; used to drop the value of a field that gets
|
|
14
|
+
* hidden from the section.
|
|
15
|
+
*/
|
|
16
|
+
onRemoveField: (key: string) => void;
|
|
17
|
+
/**
|
|
18
|
+
* Current normalised form values, needed to resolve dynamic visibility.
|
|
19
|
+
*/
|
|
20
|
+
formValues: Partial<TFieldValues> | undefined;
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* Renders a single section field. Sections flagged with `selectableFields`
|
|
24
|
+
* expose a funnel icon + visibility modal; all others render every field
|
|
25
|
+
* inline as before.
|
|
26
|
+
*/
|
|
27
|
+
export declare const SectionFieldset: <TFieldValues extends FieldValues = FieldValues>(props: SectionFieldsetProps<TFieldValues>) => React.JSX.Element;
|
|
28
|
+
export {};
|
|
29
|
+
//# sourceMappingURL=section-fieldset.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"section-fieldset.d.ts","sourceRoot":"","sources":["../../../src/components/section-fieldset.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAKnD,OAAO,KAAK,MAAM,OAAO,CAAC;AAO1B,OAAO,KAAK,EAAE,cAAc,EAAE,qBAAqB,EAAE,MAAM,UAAU,CAAC;AA8CtE,KAAK,oBAAoB,CAAC,YAAY,SAAS,WAAW,GAAG,WAAW,IAAI;IAC1E,OAAO,EAAE,qBAAqB,CAAC,YAAY,CAAC,CAAC;IAC7C;;;OAGG;IACH,UAAU,EAAE,MAAM,CAAC;IACnB,eAAe,EAAE,CAAC,KAAK,EAAE,cAAc,CAAC,YAAY,CAAC,KAAK,KAAK,CAAC,SAAS,CAAC;IAC1E;;;OAGG;IACH,aAAa,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IACrC;;OAEG;IACH,UAAU,EAAE,OAAO,CAAC,YAAY,CAAC,GAAG,SAAS,CAAC;CAC/C,CAAC;AAwGF;;;;GAIG;AACH,eAAO,MAAM,eAAe,GAAI,YAAY,SAAS,WAAW,GAAG,WAAW,EAC5E,OAAO,oBAAoB,CAAC,YAAY,CAAC,sBAuB1C,CAAC"}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SectionFieldset = void 0;
|
|
4
|
+
const icons_1 = require("@akinon/icons");
|
|
5
|
+
const ui_button_1 = require("@akinon/ui-button");
|
|
6
|
+
const ui_collapse_1 = require("@akinon/ui-collapse");
|
|
7
|
+
const ui_typography_1 = require("@akinon/ui-typography");
|
|
8
|
+
const react_1 = require("react");
|
|
9
|
+
const storage_1 = require("../common/storage");
|
|
10
|
+
const use_dynamic_visibility_1 = require("../hooks/use-dynamic-visibility");
|
|
11
|
+
const use_field_visibility_1 = require("../hooks/use-field-visibility");
|
|
12
|
+
const use_visibility_cleanup_1 = require("../hooks/use-visibility-cleanup");
|
|
13
|
+
const i18n_1 = require("../i18n");
|
|
14
|
+
const visibility_modal_1 = require("./visibility-modal");
|
|
15
|
+
const ExpandIcon = ({ isActive }) => ({
|
|
16
|
+
name: 'chevron_down',
|
|
17
|
+
size: 10,
|
|
18
|
+
style: {
|
|
19
|
+
transform: isActive ? 'rotate(-180deg)' : 'rotate(0deg)',
|
|
20
|
+
transition: 'transform 0.2s ease-in-out',
|
|
21
|
+
marginTop: 8
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
/**
|
|
25
|
+
* Presentational wrapper around the collapsible panel used by every section,
|
|
26
|
+
* so the selectable and plain variants share one markup.
|
|
27
|
+
*/
|
|
28
|
+
const SectionCollapse = ({ sectionKey, label, defaultExpanded, children }) => (react_1.default.createElement(ui_collapse_1.Collapse, { expandIconPosition: "end", expandIcon: ExpandIcon, defaultActiveKey: defaultExpanded === false ? [] : [sectionKey], ghost: true, items: [
|
|
29
|
+
{
|
|
30
|
+
key: sectionKey,
|
|
31
|
+
label,
|
|
32
|
+
headerClass: 'akinon-filter__section-header',
|
|
33
|
+
children: react_1.default.createElement("div", { className: "akinon-filter__form-grid" }, children)
|
|
34
|
+
}
|
|
35
|
+
] }));
|
|
36
|
+
/**
|
|
37
|
+
* A section whose header exposes a funnel icon that opens the visibility modal,
|
|
38
|
+
* mirroring the top-level filter field-selection behaviour.
|
|
39
|
+
*/
|
|
40
|
+
const SelectableSectionFieldset = ({ section, storageKey, renderFormField, onRemoveField, formValues }) => {
|
|
41
|
+
const sectionStorageKey = react_1.default.useMemo(() => (0, storage_1.buildSectionVisibilityKey)(storageKey, section.key), [storageKey, section.key]);
|
|
42
|
+
const { visibleKeys, setVisibleKeys, visibleFields, openModal, modalProps } = (0, use_field_visibility_1.useFieldVisibility)({
|
|
43
|
+
fields: section.fields,
|
|
44
|
+
storageKey: sectionStorageKey
|
|
45
|
+
});
|
|
46
|
+
(0, use_dynamic_visibility_1.useDynamicVisibility)({
|
|
47
|
+
regularFields: section.fields,
|
|
48
|
+
formValues,
|
|
49
|
+
setVisibleKeys
|
|
50
|
+
});
|
|
51
|
+
(0, use_visibility_cleanup_1.useVisibilityCleanup)({
|
|
52
|
+
visibleKeys,
|
|
53
|
+
regularFields: section.fields,
|
|
54
|
+
onRemove: onRemoveField
|
|
55
|
+
});
|
|
56
|
+
// Controlled so we can render our own chevron in the header and keep the
|
|
57
|
+
// funnel button pinned to the far right (after the arrow).
|
|
58
|
+
const sectionKey = String(section.key);
|
|
59
|
+
const [activeKeys, setActiveKeys] = react_1.default.useState(section.defaultExpanded === false ? [] : [sectionKey]);
|
|
60
|
+
const isExpanded = activeKeys.includes(sectionKey);
|
|
61
|
+
const label = (react_1.default.createElement("div", { className: "akinon-filter__section-header-content" },
|
|
62
|
+
react_1.default.createElement(ui_typography_1.Title, { className: "akinon-filter__title", level: 4 }, section.label),
|
|
63
|
+
react_1.default.createElement(icons_1.Icon, { icon: "chevron_down", size: 10, className: "akinon-filter__section-arrow", style: {
|
|
64
|
+
transform: isExpanded ? 'rotate(-180deg)' : 'rotate(0deg)',
|
|
65
|
+
transition: 'transform 0.2s ease-in-out'
|
|
66
|
+
} }),
|
|
67
|
+
react_1.default.createElement(ui_button_1.Button, { className: "akinon-filter__toolbar-item akinon-filter__section-funnel", icon: "filter", iconSize: 16, type: "primary", title: i18n_1.i18n.t('header.actions.selectFilters'), "aria-label": i18n_1.i18n.t('header.actions.selectFilters'), onClick: event => {
|
|
68
|
+
// Prevent the header click from toggling the collapse.
|
|
69
|
+
event.stopPropagation();
|
|
70
|
+
openModal();
|
|
71
|
+
} })));
|
|
72
|
+
return (react_1.default.createElement(react_1.default.Fragment, null,
|
|
73
|
+
react_1.default.createElement(ui_collapse_1.Collapse, { activeKey: activeKeys, onChange: keys => setActiveKeys((Array.isArray(keys) ? keys : [keys]).map(String)), ghost: true, items: [
|
|
74
|
+
{
|
|
75
|
+
key: sectionKey,
|
|
76
|
+
label,
|
|
77
|
+
headerClass: 'akinon-filter__section-header akinon-filter__section-header--selectable',
|
|
78
|
+
showArrow: false,
|
|
79
|
+
children: (react_1.default.createElement("div", { className: "akinon-filter__form-grid" }, visibleFields.map(renderFormField)))
|
|
80
|
+
}
|
|
81
|
+
] }),
|
|
82
|
+
react_1.default.createElement(visibility_modal_1.VisibilityModal, Object.assign({}, modalProps))));
|
|
83
|
+
};
|
|
84
|
+
/**
|
|
85
|
+
* Renders a single section field. Sections flagged with `selectableFields`
|
|
86
|
+
* expose a funnel icon + visibility modal; all others render every field
|
|
87
|
+
* inline as before.
|
|
88
|
+
*/
|
|
89
|
+
const SectionFieldset = (props) => {
|
|
90
|
+
const { section, renderFormField } = props;
|
|
91
|
+
if (section.selectableFields) {
|
|
92
|
+
return react_1.default.createElement(SelectableSectionFieldset, Object.assign({}, props));
|
|
93
|
+
}
|
|
94
|
+
const label = (react_1.default.createElement(ui_typography_1.Title, { className: "akinon-filter__title", level: 4 }, section.label));
|
|
95
|
+
return (react_1.default.createElement(SectionCollapse, { sectionKey: String(section.key), label: label, defaultExpanded: section.defaultExpanded }, section.fields.map(renderFormField)));
|
|
96
|
+
};
|
|
97
|
+
exports.SectionFieldset = SectionFieldset;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { type FieldValues } from '@akinon/akiform';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import type { AkifilterSchema } from '../types';
|
|
4
|
+
type UseFieldVisibilityParams<TFieldValues extends FieldValues> = {
|
|
5
|
+
/**
|
|
6
|
+
* Fields whose visibility this hook manages (top-level filters or the fields
|
|
7
|
+
* of a single section).
|
|
8
|
+
*/
|
|
9
|
+
fields: AkifilterSchema<TFieldValues>;
|
|
10
|
+
/**
|
|
11
|
+
* Storage key under which the visible-field selection is persisted. Callers
|
|
12
|
+
* pass distinct keys per group so selections never collide.
|
|
13
|
+
*/
|
|
14
|
+
storageKey: string;
|
|
15
|
+
/**
|
|
16
|
+
* Notified whenever the resolved visible keys change.
|
|
17
|
+
*/
|
|
18
|
+
onVisibleKeysChange?: (keys: string[]) => void;
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* Encapsulates the visible-field selection and its accompanying modal state for
|
|
22
|
+
* a single group of fields. Used both for the top-level filters and for each
|
|
23
|
+
* selectable section so the two share one implementation.
|
|
24
|
+
*/
|
|
25
|
+
export declare const useFieldVisibility: <TFieldValues extends FieldValues = FieldValues>({ fields, storageKey, onVisibleKeysChange }: UseFieldVisibilityParams<TFieldValues>) => {
|
|
26
|
+
visibleKeys: string[];
|
|
27
|
+
setVisibleKeys: React.Dispatch<React.SetStateAction<string[]>>;
|
|
28
|
+
visibleFields: import("../types").AkifilterField<TFieldValues>[];
|
|
29
|
+
openModal: () => void;
|
|
30
|
+
modalProps: {
|
|
31
|
+
open: boolean;
|
|
32
|
+
searchTerm: string;
|
|
33
|
+
paginatedFields: import("../types").AkifilterField<TFieldValues>[];
|
|
34
|
+
filteredCount: number;
|
|
35
|
+
visibleKeys: string[];
|
|
36
|
+
onClose: () => void;
|
|
37
|
+
onSearchTermChange: (value: string) => void;
|
|
38
|
+
onToggleVisibility: (key: string) => void;
|
|
39
|
+
onPaginationChange: (page: number, pageSize: number) => void;
|
|
40
|
+
page: number;
|
|
41
|
+
pageSize: number;
|
|
42
|
+
};
|
|
43
|
+
};
|
|
44
|
+
export {};
|
|
45
|
+
//# sourceMappingURL=use-field-visibility.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-field-visibility.d.ts","sourceRoot":"","sources":["../../../src/hooks/use-field-visibility.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,WAAW,EAAE,MAAM,iBAAiB,CAAC;AACnD,OAAO,KAAK,MAAM,OAAO,CAAC;AAI1B,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAOhD,KAAK,wBAAwB,CAAC,YAAY,SAAS,WAAW,IAAI;IAChE;;;OAGG;IACH,MAAM,EAAE,eAAe,CAAC,YAAY,CAAC,CAAC;IACtC;;;OAGG;IACH,UAAU,EAAE,MAAM,CAAC;IACnB;;OAEG;IACH,mBAAmB,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;CAChD,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,kBAAkB,GAC7B,YAAY,SAAS,WAAW,GAAG,WAAW,EAC9C,6CAIC,wBAAwB,CAAC,YAAY,CAAC;;;;;;;;;;;;oCAiHP,MAAM;kCAlD9B,MAAM;mCAuDiB,MAAM,YAAY,MAAM;;;;CA6BxD,CAAC"}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.useFieldVisibility = void 0;
|
|
4
|
+
const react_1 = require("react");
|
|
5
|
+
const storage_1 = require("../common/storage");
|
|
6
|
+
const constants_1 = require("../constants");
|
|
7
|
+
const schema_1 = require("../utils/schema");
|
|
8
|
+
/**
|
|
9
|
+
* Encapsulates the visible-field selection and its accompanying modal state for
|
|
10
|
+
* a single group of fields. Used both for the top-level filters and for each
|
|
11
|
+
* selectable section so the two share one implementation.
|
|
12
|
+
*/
|
|
13
|
+
const useFieldVisibility = ({ fields, storageKey, onVisibleKeysChange }) => {
|
|
14
|
+
const [isModalOpen, setIsModalOpen] = react_1.default.useState(false);
|
|
15
|
+
const [searchTerm, setSearchTerm] = react_1.default.useState('');
|
|
16
|
+
const [modalPage, setModalPage] = react_1.default.useState(1);
|
|
17
|
+
const [modalPageSize, setModalPageSize] = react_1.default.useState(constants_1.DEFAULT_MODAL_PAGE_SIZE);
|
|
18
|
+
const resolveInitialVisibleKeys = react_1.default.useCallback(() => {
|
|
19
|
+
const storedKeys = (0, storage_1.readVisibleKeys)(fields, storageKey);
|
|
20
|
+
const defaultKeys = (0, schema_1.deriveDefaultVisibleKeys)(fields);
|
|
21
|
+
const baseKeys = storedKeys && storedKeys.length > 0
|
|
22
|
+
? (0, schema_1.ensureSchemaOrder)(fields, storedKeys)
|
|
23
|
+
: defaultKeys;
|
|
24
|
+
return fields.reduce((acc, field) => {
|
|
25
|
+
const key = String(field.key);
|
|
26
|
+
const explicitVisibility = (0, schema_1.resolveFieldVisibility)(field);
|
|
27
|
+
if (explicitVisibility !== undefined) {
|
|
28
|
+
if (explicitVisibility)
|
|
29
|
+
acc.push(key);
|
|
30
|
+
return acc;
|
|
31
|
+
}
|
|
32
|
+
// No explicit boolean visibility, use stored/default
|
|
33
|
+
if (baseKeys.includes(key)) {
|
|
34
|
+
acc.push(key);
|
|
35
|
+
}
|
|
36
|
+
return acc;
|
|
37
|
+
}, []);
|
|
38
|
+
}, [fields, storageKey]);
|
|
39
|
+
const [visibleKeys, setVisibleKeys] = react_1.default.useState(resolveInitialVisibleKeys);
|
|
40
|
+
react_1.default.useEffect(() => {
|
|
41
|
+
setVisibleKeys(previous => {
|
|
42
|
+
const ordered = (0, schema_1.ensureSchemaOrder)(fields, previous);
|
|
43
|
+
if (ordered.length) {
|
|
44
|
+
return ordered;
|
|
45
|
+
}
|
|
46
|
+
return resolveInitialVisibleKeys();
|
|
47
|
+
});
|
|
48
|
+
}, [fields, resolveInitialVisibleKeys]);
|
|
49
|
+
react_1.default.useEffect(() => {
|
|
50
|
+
(0, storage_1.writeVisibleKeys)(storageKey, visibleKeys);
|
|
51
|
+
onVisibleKeysChange === null || onVisibleKeysChange === void 0 ? void 0 : onVisibleKeysChange(visibleKeys);
|
|
52
|
+
}, [visibleKeys, onVisibleKeysChange, storageKey]);
|
|
53
|
+
const openModal = react_1.default.useCallback(() => {
|
|
54
|
+
setIsModalOpen(true);
|
|
55
|
+
setSearchTerm('');
|
|
56
|
+
setModalPage(1);
|
|
57
|
+
}, []);
|
|
58
|
+
const closeModal = react_1.default.useCallback(() => {
|
|
59
|
+
setIsModalOpen(false);
|
|
60
|
+
}, []);
|
|
61
|
+
const handleToggleVisibility = react_1.default.useCallback((key) => {
|
|
62
|
+
setVisibleKeys(prev => {
|
|
63
|
+
const exists = prev.includes(key);
|
|
64
|
+
if (exists && prev.length === 1) {
|
|
65
|
+
return prev;
|
|
66
|
+
}
|
|
67
|
+
const nextKeys = exists
|
|
68
|
+
? prev.filter(item => item !== key)
|
|
69
|
+
: [...prev, key];
|
|
70
|
+
return (0, schema_1.ensureSchemaOrder)(fields, nextKeys);
|
|
71
|
+
});
|
|
72
|
+
}, [fields]);
|
|
73
|
+
const filteredFields = react_1.default.useMemo(() => {
|
|
74
|
+
const normalisedTerm = searchTerm.trim().toLowerCase();
|
|
75
|
+
if (!normalisedTerm) {
|
|
76
|
+
return fields;
|
|
77
|
+
}
|
|
78
|
+
return fields.filter(field => {
|
|
79
|
+
const searchable = [field.label, field.placeholder, String(field.key)]
|
|
80
|
+
.filter(Boolean)
|
|
81
|
+
.map(value => String(value).toLowerCase());
|
|
82
|
+
return searchable.some(text => text.includes(normalisedTerm));
|
|
83
|
+
});
|
|
84
|
+
}, [fields, searchTerm]);
|
|
85
|
+
const paginatedFields = react_1.default.useMemo(() => {
|
|
86
|
+
const start = (modalPage - 1) * modalPageSize;
|
|
87
|
+
return filteredFields.slice(start, start + modalPageSize);
|
|
88
|
+
}, [filteredFields, modalPage, modalPageSize]);
|
|
89
|
+
const visibleFields = react_1.default.useMemo(() => fields.filter(field => visibleKeys.includes(String(field.key))), [fields, visibleKeys]);
|
|
90
|
+
const modalProps = react_1.default.useMemo(() => ({
|
|
91
|
+
open: isModalOpen,
|
|
92
|
+
searchTerm,
|
|
93
|
+
paginatedFields,
|
|
94
|
+
filteredCount: filteredFields.length,
|
|
95
|
+
visibleKeys,
|
|
96
|
+
onClose: closeModal,
|
|
97
|
+
onSearchTermChange: (value) => {
|
|
98
|
+
setSearchTerm(value);
|
|
99
|
+
setModalPage(1);
|
|
100
|
+
},
|
|
101
|
+
onToggleVisibility: handleToggleVisibility,
|
|
102
|
+
onPaginationChange: (page, pageSize) => {
|
|
103
|
+
setModalPage(page);
|
|
104
|
+
if (pageSize !== modalPageSize) {
|
|
105
|
+
setModalPageSize(pageSize);
|
|
106
|
+
}
|
|
107
|
+
},
|
|
108
|
+
page: modalPage,
|
|
109
|
+
pageSize: modalPageSize
|
|
110
|
+
}), [
|
|
111
|
+
isModalOpen,
|
|
112
|
+
searchTerm,
|
|
113
|
+
paginatedFields,
|
|
114
|
+
filteredFields.length,
|
|
115
|
+
visibleKeys,
|
|
116
|
+
closeModal,
|
|
117
|
+
handleToggleVisibility,
|
|
118
|
+
modalPage,
|
|
119
|
+
modalPageSize
|
|
120
|
+
]);
|
|
121
|
+
return {
|
|
122
|
+
visibleKeys,
|
|
123
|
+
setVisibleKeys,
|
|
124
|
+
visibleFields,
|
|
125
|
+
openModal,
|
|
126
|
+
modalProps
|
|
127
|
+
};
|
|
128
|
+
};
|
|
129
|
+
exports.useFieldVisibility = useFieldVisibility;
|
package/dist/cjs/styles.css
CHANGED
|
@@ -70,6 +70,29 @@
|
|
|
70
70
|
flex: unset !important;
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
+
.akinon-filter__section-header-content {
|
|
74
|
+
display: flex;
|
|
75
|
+
align-items: center;
|
|
76
|
+
gap: 0.5rem;
|
|
77
|
+
width: 100%;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/* Let the header text span fill the header so the funnel can sit at the far right. */
|
|
81
|
+
.akinon-filter__section-header--selectable .akinon-collapse-header-text {
|
|
82
|
+
flex: 1 1 auto !important;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
.akinon-filter__section-arrow {
|
|
86
|
+
color: var(--color-gray-500);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/* Funnel pinned to the far right of the header, after the title and arrow. */
|
|
90
|
+
.akinon-filter__section-funnel.akinon-filter__toolbar-item {
|
|
91
|
+
width: 36px;
|
|
92
|
+
justify-content: center;
|
|
93
|
+
margin-left: auto;
|
|
94
|
+
}
|
|
95
|
+
|
|
73
96
|
.akinon-filter__buttons {
|
|
74
97
|
display: flex;
|
|
75
98
|
align-items: center;
|
package/dist/cjs/types.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { FieldValues, Path } from '@akinon/akiform';
|
|
2
|
-
import type { FormField } from '@akinon/akiform-builder';
|
|
2
|
+
import type { FormField, SectionField } from '@akinon/akiform-builder';
|
|
3
3
|
import type { IconName } from '@akinon/icons';
|
|
4
4
|
export interface AkifilterFieldConfig<TFieldValues extends FieldValues = FieldValues> {
|
|
5
5
|
/**
|
|
@@ -25,6 +25,13 @@ export type AkifilterField<TFieldValues extends FieldValues = FieldValues> = Dis
|
|
|
25
25
|
*/
|
|
26
26
|
fileAccept?: string;
|
|
27
27
|
};
|
|
28
|
+
/**
|
|
29
|
+
* Section field as consumed by Akifilter. Derived from the shared
|
|
30
|
+
* `SectionField` so flags like `selectableFields`/`isExcludeSection` stay
|
|
31
|
+
* single-sourced, but omits `collapsible` — Akifilter always renders sections
|
|
32
|
+
* as collapsible panels and does not honor that prop.
|
|
33
|
+
*/
|
|
34
|
+
export type AkifilterSectionField<TFieldValues extends FieldValues = FieldValues> = Omit<SectionField<TFieldValues>, 'collapsible'>;
|
|
28
35
|
export type AkifilterFieldKey<TFieldValues extends FieldValues = FieldValues> = Path<TFieldValues>;
|
|
29
36
|
export type AkifilterSchema<TFieldValues extends FieldValues = FieldValues> = AkifilterField<TFieldValues>[];
|
|
30
37
|
/**
|
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;
|
|
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,YAAY,EAAE,MAAM,yBAAyB,CAAC;AACvE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAE9C,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,EACJ,OAAO,GACP,CAAC,CAAC,UAAU,EAAE,YAAY,EAAE,cAAc,CAAC,EAAE,OAAO,KAAK,OAAO,CAAC,CAAC;CACvE;AAED,KAAK,gBAAgB,CAAC,CAAC,EAAE,CAAC,SAAS,WAAW,IAAI,CAAC,SAAS,OAAO,GAC/D,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,GACV,KAAK,CAAC;AAEV,MAAM,MAAM,cAAc,CAAC,YAAY,SAAS,WAAW,GAAG,WAAW,IACvE,gBAAgB,CAAC,SAAS,CAAC,YAAY,CAAC,EAAE,QAAQ,CAAC,GAAG;IACpD;;OAEG;IACH,MAAM,CAAC,EAAE,oBAAoB,CAAC,YAAY,CAAC,CAAC;IAC5C;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC;AAEJ;;;;;GAKG;AACH,MAAM,MAAM,qBAAqB,CAC/B,YAAY,SAAS,WAAW,GAAG,WAAW,IAC5C,IAAI,CAAC,YAAY,CAAC,YAAY,CAAC,EAAE,aAAa,CAAC,CAAC;AAEpD,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;AAEjC;;;;;;;GAOG;AACH,MAAM,WAAW,eAAe;IAC9B;;;;;;OAMG;IACH,GAAG,EAAE,MAAM,CAAC;IACZ;;;;;OAKG;IACH,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC;IACvB;;;OAGG;IACH,KAAK,EAAE,MAAM,CAAC;IACd;;;OAGG;IACH,aAAa,EAAE,MAAM,CAAC;IACtB;;;OAGG;IACH,IAAI,EAAE,QAAQ,CAAC;IACf;;OAEG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC;;;OAGG;IACH,UAAU,EAAE,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,KAAK,IAAI,CAAC;CAC/C"}
|