@akinon/akifilter 1.8.0 → 1.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +306 -0
- package/dist/cjs/akifilter.d.ts +19 -0
- package/dist/cjs/akifilter.d.ts.map +1 -1
- package/dist/cjs/akifilter.js +74 -17
- package/dist/cjs/common/storage.d.ts.map +1 -1
- package/dist/cjs/common/storage.js +47 -2
- package/dist/cjs/components/file-import-button.d.ts +33 -0
- package/dist/cjs/components/file-import-button.d.ts.map +1 -0
- package/dist/cjs/components/file-import-button.js +37 -0
- package/dist/cjs/components/filter-toolbar.d.ts.map +1 -1
- package/dist/cjs/components/filter-toolbar.js +4 -18
- package/dist/cjs/components/section-fieldset.d.ts +16 -2
- package/dist/cjs/components/section-fieldset.d.ts.map +1 -1
- package/dist/cjs/components/section-fieldset.js +104 -32
- package/dist/cjs/components/visibility-modal.js +1 -1
- package/dist/cjs/constants.d.ts +6 -0
- package/dist/cjs/constants.d.ts.map +1 -1
- package/dist/cjs/constants.js +7 -1
- package/dist/cjs/hooks/use-field-visibility.d.ts.map +1 -1
- package/dist/cjs/hooks/use-field-visibility.js +10 -1
- package/dist/cjs/i18n/translations/en.d.ts +8 -0
- package/dist/cjs/i18n/translations/en.d.ts.map +1 -1
- package/dist/cjs/i18n/translations/en.js +8 -0
- package/dist/cjs/i18n/translations/tr.d.ts +8 -0
- package/dist/cjs/i18n/translations/tr.d.ts.map +1 -1
- package/dist/cjs/i18n/translations/tr.js +8 -0
- package/dist/cjs/styles.css +36 -11
- package/dist/cjs/types.d.ts +146 -3
- package/dist/cjs/types.d.ts.map +1 -1
- package/dist/cjs/utils/schema.d.ts +10 -0
- package/dist/cjs/utils/schema.d.ts.map +1 -1
- package/dist/cjs/utils/schema.js +13 -1
- package/dist/esm/akifilter.d.ts +19 -0
- package/dist/esm/akifilter.d.ts.map +1 -1
- package/dist/esm/akifilter.js +75 -18
- package/dist/esm/common/storage.d.ts.map +1 -1
- package/dist/esm/common/storage.js +47 -2
- package/dist/esm/components/file-import-button.d.ts +33 -0
- package/dist/esm/components/file-import-button.d.ts.map +1 -0
- package/dist/esm/components/file-import-button.js +33 -0
- package/dist/esm/components/filter-toolbar.d.ts.map +1 -1
- package/dist/esm/components/filter-toolbar.js +4 -18
- package/dist/esm/components/section-fieldset.d.ts +16 -2
- package/dist/esm/components/section-fieldset.d.ts.map +1 -1
- package/dist/esm/components/section-fieldset.js +104 -32
- package/dist/esm/components/visibility-modal.js +2 -2
- package/dist/esm/constants.d.ts +6 -0
- package/dist/esm/constants.d.ts.map +1 -1
- package/dist/esm/constants.js +6 -0
- package/dist/esm/hooks/use-field-visibility.d.ts.map +1 -1
- package/dist/esm/hooks/use-field-visibility.js +10 -1
- package/dist/esm/i18n/translations/en.d.ts +8 -0
- package/dist/esm/i18n/translations/en.d.ts.map +1 -1
- package/dist/esm/i18n/translations/en.js +8 -0
- package/dist/esm/i18n/translations/tr.d.ts +8 -0
- package/dist/esm/i18n/translations/tr.d.ts.map +1 -1
- package/dist/esm/i18n/translations/tr.js +8 -0
- package/dist/esm/styles.css +36 -11
- package/dist/esm/types.d.ts +146 -3
- package/dist/esm/types.d.ts.map +1 -1
- package/dist/esm/utils/schema.d.ts +10 -0
- package/dist/esm/utils/schema.d.ts.map +1 -1
- package/dist/esm/utils/schema.js +10 -0
- package/package.json +7 -7
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.FileImportButton = void 0;
|
|
4
|
+
const ui_button_1 = require("@akinon/ui-button");
|
|
5
|
+
const react_1 = require("react");
|
|
6
|
+
/**
|
|
7
|
+
* Button that opens the native file picker and hands the selected file to
|
|
8
|
+
* `onImport`. Shared by the top-level filter toolbar and the section headers so
|
|
9
|
+
* both surfaces behave identically.
|
|
10
|
+
*/
|
|
11
|
+
const FileImportButton = ({ accept, label, title, onImport, className }) => {
|
|
12
|
+
const inputRef = react_1.default.useRef(null);
|
|
13
|
+
const resolvedTitle = title !== null && title !== void 0 ? title : label;
|
|
14
|
+
const handleClick = () => { var _a; return (_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.click(); };
|
|
15
|
+
const handleChange = (event) => {
|
|
16
|
+
var _a;
|
|
17
|
+
const file = (_a = event.target.files) === null || _a === void 0 ? void 0 : _a[0];
|
|
18
|
+
if (file) {
|
|
19
|
+
onImport(file);
|
|
20
|
+
}
|
|
21
|
+
// Reset so same file can be re-selected
|
|
22
|
+
event.target.value = '';
|
|
23
|
+
};
|
|
24
|
+
return (react_1.default.createElement(react_1.default.Fragment, null,
|
|
25
|
+
react_1.default.createElement("input", { ref: inputRef, type: "file", accept: accept, style: { display: 'none' }, onChange: handleChange }),
|
|
26
|
+
react_1.default.createElement(ui_button_1.Button, { className: [
|
|
27
|
+
'akinon-filter__toolbar-item',
|
|
28
|
+
'akinon-filter__toolbar-item--file',
|
|
29
|
+
className
|
|
30
|
+
]
|
|
31
|
+
.filter(Boolean)
|
|
32
|
+
.join(' '), icon: "filter",
|
|
33
|
+
// Smaller than the icon-only toolbar buttons: the icon shares its 36px
|
|
34
|
+
// square with the label underneath it.
|
|
35
|
+
iconSize: 12, onClick: handleClick, type: "primary", title: resolvedTitle, "aria-label": resolvedTitle }, label)));
|
|
36
|
+
};
|
|
37
|
+
exports.FileImportButton = FileImportButton;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"filter-toolbar.d.ts","sourceRoot":"","sources":["../../../src/components/filter-toolbar.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"filter-toolbar.d.ts","sourceRoot":"","sources":["../../../src/components/filter-toolbar.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,MAAM,OAAO,CAAC;AAM1B,KAAK,kBAAkB,GAAG;IACxB,WAAW,EAAE,MAAM,IAAI,CAAC;IACxB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,IAAI,CAAC;IACnC,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,IAAI,CAAC;CACpC,CAAC;AAEF,eAAO,MAAM,aAAa,GAAI,6EAM3B,kBAAkB,sBAoCpB,CAAC"}
|
|
@@ -5,29 +5,15 @@ const ui_button_1 = require("@akinon/ui-button");
|
|
|
5
5
|
const ui_space_1 = require("@akinon/ui-space");
|
|
6
6
|
const ui_typography_1 = require("@akinon/ui-typography");
|
|
7
7
|
const react_1 = require("react");
|
|
8
|
+
const constants_1 = require("../constants");
|
|
8
9
|
const i18n_1 = require("../i18n");
|
|
9
|
-
const
|
|
10
|
-
const inputRef = react_1.default.useRef(null);
|
|
11
|
-
const handleClick = () => { var _a; return (_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.click(); };
|
|
12
|
-
const handleChange = (e) => {
|
|
13
|
-
var _a;
|
|
14
|
-
const file = (_a = e.target.files) === null || _a === void 0 ? void 0 : _a[0];
|
|
15
|
-
if (file) {
|
|
16
|
-
onImport(file);
|
|
17
|
-
}
|
|
18
|
-
// Reset so same file can be re-selected
|
|
19
|
-
e.target.value = '';
|
|
20
|
-
};
|
|
21
|
-
return (react_1.default.createElement(react_1.default.Fragment, null,
|
|
22
|
-
react_1.default.createElement("input", { ref: inputRef, type: "file", accept: accept, style: { display: 'none' }, onChange: handleChange }),
|
|
23
|
-
react_1.default.createElement(ui_button_1.Button, { className: "akinon-filter__toolbar-item akinon-filter__toolbar-item--file", icon: "filter", iconSize: 13, onClick: handleClick, type: "primary", title: label, "aria-label": label }, label)));
|
|
24
|
-
};
|
|
10
|
+
const file_import_button_1 = require("./file-import-button");
|
|
25
11
|
const FilterToolbar = ({ onOpenModal, enableImportCsv, enableImportXls, onImportCsv, onImportXls }) => {
|
|
26
12
|
return (react_1.default.createElement("div", { className: "akinon-filter__top" },
|
|
27
13
|
react_1.default.createElement(ui_typography_1.Title, { className: "akinon-filter__title", level: 4 }, i18n_1.i18n.t('header.title')),
|
|
28
14
|
react_1.default.createElement(ui_space_1.Space, { className: "akinon-filter__toolbar", size: 6 },
|
|
29
|
-
enableImportCsv && onImportCsv ? (react_1.default.createElement(FileImportButton, { accept:
|
|
30
|
-
enableImportXls && onImportXls ? (react_1.default.createElement(FileImportButton, { accept:
|
|
15
|
+
enableImportCsv && onImportCsv ? (react_1.default.createElement(file_import_button_1.FileImportButton, { accept: constants_1.CSV_ACCEPT, label: i18n_1.i18n.t('header.actions.importCsv'), onImport: onImportCsv })) : null,
|
|
16
|
+
enableImportXls && onImportXls ? (react_1.default.createElement(file_import_button_1.FileImportButton, { accept: constants_1.XLS_ACCEPT, label: i18n_1.i18n.t('header.actions.importXls'), onImport: onImportXls })) : null,
|
|
31
17
|
react_1.default.createElement(ui_button_1.Button, { className: "akinon-filter__toolbar-item", icon: "filter", iconSize: 16, onClick: onOpenModal, type: "primary", title: i18n_1.i18n.t('header.actions.selectFilters'), "aria-label": i18n_1.i18n.t('header.actions.selectFilters') }))));
|
|
32
18
|
};
|
|
33
19
|
exports.FilterToolbar = FilterToolbar;
|
|
@@ -1,7 +1,19 @@
|
|
|
1
1
|
import type { FieldValues } from '@akinon/akiform';
|
|
2
2
|
import React from 'react';
|
|
3
3
|
import type { AkifilterField, AkifilterSectionField } from '../types';
|
|
4
|
-
type
|
|
4
|
+
type SectionImportHandlers = {
|
|
5
|
+
/**
|
|
6
|
+
* Called with the CSV file picked from the section header. Only rendered when
|
|
7
|
+
* the section also opts in through `enableImportCsv`.
|
|
8
|
+
*/
|
|
9
|
+
onImportCsv?: (file: File) => void;
|
|
10
|
+
/**
|
|
11
|
+
* Called with the XLS/XLSX file picked from the section header. Only rendered
|
|
12
|
+
* when the section also opts in through `enableImportXls`.
|
|
13
|
+
*/
|
|
14
|
+
onImportXls?: (file: File) => void;
|
|
15
|
+
};
|
|
16
|
+
type SectionFieldsetProps<TFieldValues extends FieldValues = FieldValues> = SectionImportHandlers & {
|
|
5
17
|
section: AkifilterSectionField<TFieldValues>;
|
|
6
18
|
/**
|
|
7
19
|
* Base storage key of the host Akifilter; combined with the section key to
|
|
@@ -22,7 +34,9 @@ type SectionFieldsetProps<TFieldValues extends FieldValues = FieldValues> = {
|
|
|
22
34
|
/**
|
|
23
35
|
* Renders a single section field. Sections flagged with `selectableFields`
|
|
24
36
|
* expose a funnel icon + visibility modal; all others render every field
|
|
25
|
-
* inline as before.
|
|
37
|
+
* inline as before. Both variants render CSV/XLS import buttons in the header
|
|
38
|
+
* when the section sets `enableImportCsv` / `enableImportXls` and the host
|
|
39
|
+
* supplies the matching handler.
|
|
26
40
|
*/
|
|
27
41
|
export declare const SectionFieldset: <TFieldValues extends FieldValues = FieldValues>(props: SectionFieldsetProps<TFieldValues>) => React.JSX.Element;
|
|
28
42
|
export {};
|
|
@@ -1 +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;
|
|
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;AAQ1B,OAAO,KAAK,EAAE,cAAc,EAAE,qBAAqB,EAAE,MAAM,UAAU,CAAC;AA+CtE,KAAK,qBAAqB,GAAG;IAC3B;;;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;CACpC,CAAC;AA8JF,KAAK,oBAAoB,CAAC,YAAY,SAAS,WAAW,GAAG,WAAW,IACtE,qBAAqB,GAAG;IACtB,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;AAiHJ;;;;;;GAMG;AACH,eAAO,MAAM,eAAe,GAAI,YAAY,SAAS,WAAW,GAAG,WAAW,EAC5E,OAAO,oBAAoB,CAAC,YAAY,CAAC,sBAO1C,CAAC"}
|
|
@@ -7,10 +7,12 @@ const ui_collapse_1 = require("@akinon/ui-collapse");
|
|
|
7
7
|
const ui_typography_1 = require("@akinon/ui-typography");
|
|
8
8
|
const react_1 = require("react");
|
|
9
9
|
const storage_1 = require("../common/storage");
|
|
10
|
+
const constants_1 = require("../constants");
|
|
10
11
|
const use_dynamic_visibility_1 = require("../hooks/use-dynamic-visibility");
|
|
11
12
|
const use_field_visibility_1 = require("../hooks/use-field-visibility");
|
|
12
13
|
const use_visibility_cleanup_1 = require("../hooks/use-visibility-cleanup");
|
|
13
14
|
const i18n_1 = require("../i18n");
|
|
15
|
+
const file_import_button_1 = require("./file-import-button");
|
|
14
16
|
const visibility_modal_1 = require("./visibility-modal");
|
|
15
17
|
const ExpandIcon = ({ isActive }) => ({
|
|
16
18
|
name: 'chevron_down',
|
|
@@ -33,11 +35,83 @@ const SectionCollapse = ({ sectionKey, label, defaultExpanded, children }) => (r
|
|
|
33
35
|
children: react_1.default.createElement("div", { className: "akinon-filter__form-grid" }, children)
|
|
34
36
|
}
|
|
35
37
|
] }));
|
|
38
|
+
/**
|
|
39
|
+
* Resolves the action buttons a section header should render. Import buttons
|
|
40
|
+
* require both the schema flag and a handler — mirroring how the top-level
|
|
41
|
+
* toolbar pairs `enableImportCsv` with `onImportCsv` — so a flag without a
|
|
42
|
+
* handler renders nothing instead of a dead button.
|
|
43
|
+
*/
|
|
44
|
+
const resolveSectionHeaderActions = ({ section, onImportCsv, onImportXls, onOpenVisibilityModal }) => {
|
|
45
|
+
var _a;
|
|
46
|
+
const sectionLabel = String((_a = section.label) !== null && _a !== void 0 ? _a : section.key);
|
|
47
|
+
const showCsv = Boolean(section.enableImportCsv && onImportCsv);
|
|
48
|
+
const showXls = Boolean(section.enableImportXls && onImportXls);
|
|
49
|
+
const showFunnel = Boolean(section.selectableFields && onOpenVisibilityModal);
|
|
50
|
+
if (!showCsv && !showXls && !showFunnel) {
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
return (
|
|
54
|
+
// rc-collapse puts both `onClick` and an Enter-handling `onKeyDown` on the
|
|
55
|
+
// panel header, and the header is this group's ancestor. Guarding the group
|
|
56
|
+
// once covers every header action — mouse and keyboard alike — so
|
|
57
|
+
// activating one never toggles the panel underneath it.
|
|
58
|
+
react_1.default.createElement("div", { className: "akinon-filter__section-actions", onClick: event => event.stopPropagation(), onKeyDown: event => {
|
|
59
|
+
if (event.key === 'Enter') {
|
|
60
|
+
// Only the bubbling is stopped; the button's own activation still runs.
|
|
61
|
+
event.stopPropagation();
|
|
62
|
+
}
|
|
63
|
+
}, role: "presentation" },
|
|
64
|
+
showCsv && onImportCsv ? (react_1.default.createElement(file_import_button_1.FileImportButton, { accept: constants_1.CSV_ACCEPT, label: i18n_1.i18n.t('section.actions.importCsv'), title: i18n_1.i18n.t('section.actions.importCsvTooltip', {
|
|
65
|
+
section: sectionLabel
|
|
66
|
+
}), onImport: onImportCsv, className: "akinon-filter__section-import akinon-filter__section-import--csv" })) : null,
|
|
67
|
+
showXls && onImportXls ? (react_1.default.createElement(file_import_button_1.FileImportButton, { accept: constants_1.XLS_ACCEPT, label: i18n_1.i18n.t('section.actions.importXls'), title: i18n_1.i18n.t('section.actions.importXlsTooltip', {
|
|
68
|
+
section: sectionLabel
|
|
69
|
+
}), onImport: onImportXls, className: "akinon-filter__section-import akinon-filter__section-import--xls" })) : null,
|
|
70
|
+
showFunnel && onOpenVisibilityModal ? (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 => {
|
|
71
|
+
// Prevent the header click from toggling the collapse.
|
|
72
|
+
event.stopPropagation();
|
|
73
|
+
onOpenVisibilityModal();
|
|
74
|
+
} })) : null));
|
|
75
|
+
};
|
|
76
|
+
/**
|
|
77
|
+
* Collapse variant used whenever a section header carries action buttons.
|
|
78
|
+
* Controlled so we can render our own chevron and keep the actions pinned to
|
|
79
|
+
* the far right of the header (after the arrow).
|
|
80
|
+
*/
|
|
81
|
+
const SectionActionsCollapse = ({ sectionKey, label, defaultExpanded, actions, isSelectable, children }) => {
|
|
82
|
+
const [activeKeys, setActiveKeys] = react_1.default.useState(defaultExpanded === false ? [] : [sectionKey]);
|
|
83
|
+
const isExpanded = activeKeys.includes(sectionKey);
|
|
84
|
+
const header = (react_1.default.createElement("div", { className: "akinon-filter__section-header-content" },
|
|
85
|
+
react_1.default.createElement(ui_typography_1.Title, { className: "akinon-filter__title", level: 4 }, label),
|
|
86
|
+
react_1.default.createElement(icons_1.Icon, { icon: "chevron_down", size: 10, className: "akinon-filter__section-arrow", style: {
|
|
87
|
+
transform: isExpanded ? 'rotate(-180deg)' : 'rotate(0deg)',
|
|
88
|
+
transition: 'transform 0.2s ease-in-out'
|
|
89
|
+
} }),
|
|
90
|
+
actions));
|
|
91
|
+
return (react_1.default.createElement(ui_collapse_1.Collapse, { activeKey: activeKeys, onChange: keys => setActiveKeys((Array.isArray(keys) ? keys : [keys]).map(String)), ghost: true, items: [
|
|
92
|
+
{
|
|
93
|
+
key: sectionKey,
|
|
94
|
+
label: header,
|
|
95
|
+
headerClass: [
|
|
96
|
+
'akinon-filter__section-header',
|
|
97
|
+
'akinon-filter__section-header--actions',
|
|
98
|
+
// Kept for backwards compatibility with host styles that target
|
|
99
|
+
// the original selectable-section modifier.
|
|
100
|
+
isSelectable ? 'akinon-filter__section-header--selectable' : null
|
|
101
|
+
]
|
|
102
|
+
.filter(Boolean)
|
|
103
|
+
.join(' '),
|
|
104
|
+
showArrow: false,
|
|
105
|
+
children: react_1.default.createElement("div", { className: "akinon-filter__form-grid" }, children)
|
|
106
|
+
}
|
|
107
|
+
] }));
|
|
108
|
+
};
|
|
36
109
|
/**
|
|
37
110
|
* A section whose header exposes a funnel icon that opens the visibility modal,
|
|
38
|
-
* mirroring the top-level filter field-selection behaviour.
|
|
111
|
+
* mirroring the top-level filter field-selection behaviour. Import buttons are
|
|
112
|
+
* rendered alongside the funnel when the section opts into them.
|
|
39
113
|
*/
|
|
40
|
-
const SelectableSectionFieldset = ({ section, storageKey, renderFormField, onRemoveField, formValues }) => {
|
|
114
|
+
const SelectableSectionFieldset = ({ section, storageKey, renderFormField, onRemoveField, formValues, onImportCsv, onImportXls }) => {
|
|
41
115
|
const sectionStorageKey = react_1.default.useMemo(() => (0, storage_1.buildSectionVisibilityKey)(storageKey, section.key), [storageKey, section.key]);
|
|
42
116
|
const { visibleKeys, setVisibleKeys, visibleFields, openModal, modalProps } = (0, use_field_visibility_1.useFieldVisibility)({
|
|
43
117
|
fields: section.fields,
|
|
@@ -53,45 +127,43 @@ const SelectableSectionFieldset = ({ section, storageKey, renderFormField, onRem
|
|
|
53
127
|
regularFields: section.fields,
|
|
54
128
|
onRemove: onRemoveField
|
|
55
129
|
});
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
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
|
-
} })));
|
|
130
|
+
const actions = resolveSectionHeaderActions({
|
|
131
|
+
section,
|
|
132
|
+
onImportCsv,
|
|
133
|
+
onImportXls,
|
|
134
|
+
onOpenVisibilityModal: openModal
|
|
135
|
+
});
|
|
72
136
|
return (react_1.default.createElement(react_1.default.Fragment, null,
|
|
73
|
-
react_1.default.createElement(
|
|
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
|
-
] }),
|
|
137
|
+
react_1.default.createElement(SectionActionsCollapse, { sectionKey: String(section.key), label: section.label, defaultExpanded: section.defaultExpanded, actions: actions, isSelectable: true }, visibleFields.map(renderFormField)),
|
|
82
138
|
react_1.default.createElement(visibility_modal_1.VisibilityModal, Object.assign({}, modalProps))));
|
|
83
139
|
};
|
|
140
|
+
/**
|
|
141
|
+
* A section that always renders every one of its fields. Falls back to the
|
|
142
|
+
* plain collapse markup unless the header needs action buttons (e.g. import).
|
|
143
|
+
*/
|
|
144
|
+
const PlainSectionFieldset = ({ section, renderFormField, onImportCsv, onImportXls }) => {
|
|
145
|
+
const actions = resolveSectionHeaderActions({
|
|
146
|
+
section,
|
|
147
|
+
onImportCsv,
|
|
148
|
+
onImportXls
|
|
149
|
+
});
|
|
150
|
+
const fields = section.fields.map(field => renderFormField(field));
|
|
151
|
+
if (actions) {
|
|
152
|
+
return (react_1.default.createElement(SectionActionsCollapse, { sectionKey: String(section.key), label: section.label, defaultExpanded: section.defaultExpanded, actions: actions, isSelectable: false }, fields));
|
|
153
|
+
}
|
|
154
|
+
return (react_1.default.createElement(SectionCollapse, { sectionKey: String(section.key), label: react_1.default.createElement(ui_typography_1.Title, { className: "akinon-filter__title", level: 4 }, section.label), defaultExpanded: section.defaultExpanded }, fields));
|
|
155
|
+
};
|
|
84
156
|
/**
|
|
85
157
|
* Renders a single section field. Sections flagged with `selectableFields`
|
|
86
158
|
* expose a funnel icon + visibility modal; all others render every field
|
|
87
|
-
* inline as before.
|
|
159
|
+
* inline as before. Both variants render CSV/XLS import buttons in the header
|
|
160
|
+
* when the section sets `enableImportCsv` / `enableImportXls` and the host
|
|
161
|
+
* supplies the matching handler.
|
|
88
162
|
*/
|
|
89
163
|
const SectionFieldset = (props) => {
|
|
90
|
-
|
|
91
|
-
if (section.selectableFields) {
|
|
164
|
+
if (props.section.selectableFields) {
|
|
92
165
|
return react_1.default.createElement(SelectableSectionFieldset, Object.assign({}, props));
|
|
93
166
|
}
|
|
94
|
-
|
|
95
|
-
return (react_1.default.createElement(SectionCollapse, { sectionKey: String(section.key), label: label, defaultExpanded: section.defaultExpanded }, section.fields.map(renderFormField)));
|
|
167
|
+
return react_1.default.createElement(PlainSectionFieldset, Object.assign({}, props));
|
|
96
168
|
};
|
|
97
169
|
exports.SectionFieldset = SectionFieldset;
|
|
@@ -22,7 +22,7 @@ const VisibilityModal = ({ open, searchTerm, paginatedFields, filteredCount, vis
|
|
|
22
22
|
}, size: "large" })),
|
|
23
23
|
react_1.default.createElement("div", { className: "akinon-filter__modal-list" }, paginatedFields.length === 0 ? (react_1.default.createElement(ui_typography_1.Text, { type: "secondary" }, i18n_1.i18n.t('modal.empty'))) : (paginatedFields.map(field => {
|
|
24
24
|
const key = String(field.key);
|
|
25
|
-
return (react_1.default.createElement(ui_checkbox_1.Checkbox, { key: key, checked: visibleKeys.includes(key), onChange: () => onToggleVisibility(key), className: "akinon-filter__modal-checkbox" }, (0, schema_1.
|
|
25
|
+
return (react_1.default.createElement(ui_checkbox_1.Checkbox, { key: key, checked: visibleKeys.includes(key), onChange: () => onToggleVisibility(key), className: "akinon-filter__modal-checkbox" }, (0, schema_1.getVisibilityLabel)(field)));
|
|
26
26
|
}))),
|
|
27
27
|
filteredCount > pageSize ? (react_1.default.createElement("div", { className: "akinon-filter__modal-pagination" },
|
|
28
28
|
react_1.default.createElement(ui_pagination_1.Pagination, { current: page, pageSize: pageSize, total: filteredCount, showSizeChanger: false, onChange: onPaginationChange }))) : null));
|
package/dist/cjs/constants.d.ts
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
export declare const DEFAULT_VISIBLE_COUNT = 8;
|
|
2
2
|
export declare const DEFAULT_MODAL_PAGE_SIZE = 40;
|
|
3
3
|
export declare const FILTER_DEBOUNCE_DELAY = 300;
|
|
4
|
+
/**
|
|
5
|
+
* `accept` values for the CSV / XLS import buttons. Shared by the top-level
|
|
6
|
+
* toolbar and the per-section import buttons so both accept the same files.
|
|
7
|
+
*/
|
|
8
|
+
export declare const CSV_ACCEPT = ".csv";
|
|
9
|
+
export declare const XLS_ACCEPT = ".xls,.xlsx";
|
|
4
10
|
export declare const DATE_FORMAT = "YYYY-MM-DD";
|
|
5
11
|
export declare const DATETIME_FORMAT = "YYYY-MM-DD HH:mm:ss";
|
|
6
12
|
export declare const BOOLEAN_STRING: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../src/constants.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,qBAAqB,IAAI,CAAC;AACvC,eAAO,MAAM,uBAAuB,KAAK,CAAC;AAC1C,eAAO,MAAM,qBAAqB,MAAM,CAAC;AAEzC,eAAO,MAAM,WAAW,eAAe,CAAC;AACxC,eAAO,MAAM,eAAe,wBAAwB,CAAC;AAErD,eAAO,MAAM,cAAc;;;CAGjB,CAAC;AAEX,eAAO,MAAM,WAAW;;;;;;;;;;CAUd,CAAC;AAEX,eAAO,MAAM,WAAW;;;CAGd,CAAC"}
|
|
1
|
+
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../src/constants.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,qBAAqB,IAAI,CAAC;AACvC,eAAO,MAAM,uBAAuB,KAAK,CAAC;AAC1C,eAAO,MAAM,qBAAqB,MAAM,CAAC;AAEzC;;;GAGG;AACH,eAAO,MAAM,UAAU,SAAS,CAAC;AACjC,eAAO,MAAM,UAAU,eAAe,CAAC;AAEvC,eAAO,MAAM,WAAW,eAAe,CAAC;AACxC,eAAO,MAAM,eAAe,wBAAwB,CAAC;AAErD,eAAO,MAAM,cAAc;;;CAGjB,CAAC;AAEX,eAAO,MAAM,WAAW;;;;;;;;;;CAUd,CAAC;AAEX,eAAO,MAAM,WAAW;;;CAGd,CAAC"}
|
package/dist/cjs/constants.js
CHANGED
|
@@ -1,9 +1,15 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.FIELD_MODES = exports.FIELD_TYPES = exports.BOOLEAN_STRING = exports.DATETIME_FORMAT = exports.DATE_FORMAT = exports.FILTER_DEBOUNCE_DELAY = exports.DEFAULT_MODAL_PAGE_SIZE = exports.DEFAULT_VISIBLE_COUNT = void 0;
|
|
3
|
+
exports.FIELD_MODES = exports.FIELD_TYPES = exports.BOOLEAN_STRING = exports.DATETIME_FORMAT = exports.DATE_FORMAT = exports.XLS_ACCEPT = exports.CSV_ACCEPT = exports.FILTER_DEBOUNCE_DELAY = exports.DEFAULT_MODAL_PAGE_SIZE = exports.DEFAULT_VISIBLE_COUNT = void 0;
|
|
4
4
|
exports.DEFAULT_VISIBLE_COUNT = 8;
|
|
5
5
|
exports.DEFAULT_MODAL_PAGE_SIZE = 40;
|
|
6
6
|
exports.FILTER_DEBOUNCE_DELAY = 300;
|
|
7
|
+
/**
|
|
8
|
+
* `accept` values for the CSV / XLS import buttons. Shared by the top-level
|
|
9
|
+
* toolbar and the per-section import buttons so both accept the same files.
|
|
10
|
+
*/
|
|
11
|
+
exports.CSV_ACCEPT = '.csv';
|
|
12
|
+
exports.XLS_ACCEPT = '.xls,.xlsx';
|
|
7
13
|
exports.DATE_FORMAT = 'YYYY-MM-DD';
|
|
8
14
|
exports.DATETIME_FORMAT = 'YYYY-MM-DD HH:mm:ss';
|
|
9
15
|
exports.BOOLEAN_STRING = {
|
|
@@ -1 +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;;;;;;;;;;;;
|
|
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;;;;;;;;;;;;oCA0HP,MAAM;kCA3D9B,MAAM;mCAgEiB,MAAM,YAAY,MAAM;;;;CA6BxD,CAAC"}
|
|
@@ -76,7 +76,16 @@ const useFieldVisibility = ({ fields, storageKey, onVisibleKeysChange }) => {
|
|
|
76
76
|
return fields;
|
|
77
77
|
}
|
|
78
78
|
return fields.filter(field => {
|
|
79
|
-
|
|
79
|
+
// `visibilityLabel` first: the modal renders it in place of `label`, so
|
|
80
|
+
// searching has to match the text the user can actually see. `label`
|
|
81
|
+
// stays searchable too — an overridden field is still findable by the
|
|
82
|
+
// name the rest of the form uses.
|
|
83
|
+
const searchable = [
|
|
84
|
+
field.visibilityLabel,
|
|
85
|
+
field.label,
|
|
86
|
+
field.placeholder,
|
|
87
|
+
String(field.key)
|
|
88
|
+
]
|
|
80
89
|
.filter(Boolean)
|
|
81
90
|
.map(value => String(value).toLowerCase());
|
|
82
91
|
return searchable.some(text => text.includes(normalisedTerm));
|
|
@@ -7,6 +7,14 @@ declare const translations: {
|
|
|
7
7
|
readonly importXls: "XLS";
|
|
8
8
|
};
|
|
9
9
|
};
|
|
10
|
+
readonly section: {
|
|
11
|
+
readonly actions: {
|
|
12
|
+
readonly importCsv: "CSV";
|
|
13
|
+
readonly importXls: "XLS";
|
|
14
|
+
readonly importCsvTooltip: "Import {{section}} filters from a CSV file";
|
|
15
|
+
readonly importXlsTooltip: "Import {{section}} filters from an XLS file";
|
|
16
|
+
};
|
|
17
|
+
};
|
|
10
18
|
readonly applied: {
|
|
11
19
|
readonly label: "Active filters:";
|
|
12
20
|
readonly empty: "No filters applied yet.";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"en.d.ts","sourceRoot":"","sources":["../../../../src/i18n/translations/en.ts"],"names":[],"mappings":"AAAA,QAAA,MAAM,YAAY
|
|
1
|
+
{"version":3,"file":"en.d.ts","sourceRoot":"","sources":["../../../../src/i18n/translations/en.ts"],"names":[],"mappings":"AAAA,QAAA,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAoDR,CAAC;AAEX,eAAe,YAAY,CAAC"}
|
|
@@ -9,6 +9,14 @@ const translations = {
|
|
|
9
9
|
importXls: 'XLS'
|
|
10
10
|
}
|
|
11
11
|
},
|
|
12
|
+
section: {
|
|
13
|
+
actions: {
|
|
14
|
+
importCsv: 'CSV',
|
|
15
|
+
importXls: 'XLS',
|
|
16
|
+
importCsvTooltip: 'Import {{section}} filters from a CSV file',
|
|
17
|
+
importXlsTooltip: 'Import {{section}} filters from an XLS file'
|
|
18
|
+
}
|
|
19
|
+
},
|
|
12
20
|
applied: {
|
|
13
21
|
label: 'Active filters:',
|
|
14
22
|
empty: 'No filters applied yet.',
|
|
@@ -7,6 +7,14 @@ declare const translations: {
|
|
|
7
7
|
readonly importXls: "XLS";
|
|
8
8
|
};
|
|
9
9
|
};
|
|
10
|
+
readonly section: {
|
|
11
|
+
readonly actions: {
|
|
12
|
+
readonly importCsv: "CSV";
|
|
13
|
+
readonly importXls: "XLS";
|
|
14
|
+
readonly importCsvTooltip: "{{section}} filtrelerini CSV dosyasından içe aktar";
|
|
15
|
+
readonly importXlsTooltip: "{{section}} filtrelerini XLS dosyasından içe aktar";
|
|
16
|
+
};
|
|
17
|
+
};
|
|
10
18
|
readonly applied: {
|
|
11
19
|
readonly label: "Aktif filtreler:";
|
|
12
20
|
readonly empty: "Henüz filtre uygulanmadı.";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tr.d.ts","sourceRoot":"","sources":["../../../../src/i18n/translations/tr.ts"],"names":[],"mappings":"AAAA,QAAA,MAAM,YAAY
|
|
1
|
+
{"version":3,"file":"tr.d.ts","sourceRoot":"","sources":["../../../../src/i18n/translations/tr.ts"],"names":[],"mappings":"AAAA,QAAA,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAoDR,CAAC;AAEX,eAAe,YAAY,CAAC"}
|
|
@@ -9,6 +9,14 @@ const translations = {
|
|
|
9
9
|
importXls: 'XLS'
|
|
10
10
|
}
|
|
11
11
|
},
|
|
12
|
+
section: {
|
|
13
|
+
actions: {
|
|
14
|
+
importCsv: 'CSV',
|
|
15
|
+
importXls: 'XLS',
|
|
16
|
+
importCsvTooltip: '{{section}} filtrelerini CSV dosyasından içe aktar',
|
|
17
|
+
importXlsTooltip: '{{section}} filtrelerini XLS dosyasından içe aktar'
|
|
18
|
+
}
|
|
19
|
+
},
|
|
12
20
|
applied: {
|
|
13
21
|
label: 'Aktif filtreler:',
|
|
14
22
|
empty: 'Henüz filtre uygulanmadı.',
|
package/dist/cjs/styles.css
CHANGED
|
@@ -26,15 +26,39 @@
|
|
|
26
26
|
height: 36px;
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
+
/*
|
|
30
|
+
* Every filter action button is the same 36px square so the toolbar and the
|
|
31
|
+
* section headers line up on both axes. The `.akinon-filter` prefix is what
|
|
32
|
+
* makes the width stick: antd sizes icon-only buttons from its own
|
|
33
|
+
* `controlHeight` at two-class specificity, which a lone class cannot beat.
|
|
34
|
+
*/
|
|
35
|
+
.akinon-filter .akinon-filter__toolbar-item {
|
|
36
|
+
width: 36px;
|
|
37
|
+
justify-content: center;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/*
|
|
41
|
+
* File import buttons stay square like every other toolbar action, with the
|
|
42
|
+
* icon stacked above its short label. Both must be centred by hand: antd lays
|
|
43
|
+
* the button out as a flex row and separates icon from label with
|
|
44
|
+
* `margin-inline-*`, which reads as a stray horizontal offset — the icon
|
|
45
|
+
* drifting off centre — once the direction is column.
|
|
46
|
+
*/
|
|
29
47
|
.akinon-filter__toolbar-item--file {
|
|
30
48
|
width: 36px;
|
|
31
|
-
font-size: 11px;
|
|
32
|
-
font-weight: bold;
|
|
33
49
|
flex-direction: column;
|
|
34
|
-
|
|
50
|
+
align-items: center;
|
|
51
|
+
justify-content: center;
|
|
35
52
|
gap: 2px;
|
|
36
|
-
padding
|
|
37
|
-
|
|
53
|
+
padding: 0;
|
|
54
|
+
font-size: 9px;
|
|
55
|
+
font-weight: 700;
|
|
56
|
+
line-height: 1;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
.akinon-filter__toolbar-item--file > span {
|
|
60
|
+
margin-inline: 0 !important;
|
|
61
|
+
line-height: 1;
|
|
38
62
|
}
|
|
39
63
|
|
|
40
64
|
.akinon-filter__applied {
|
|
@@ -77,8 +101,8 @@
|
|
|
77
101
|
width: 100%;
|
|
78
102
|
}
|
|
79
103
|
|
|
80
|
-
/* Let the header text span fill the header so the
|
|
81
|
-
.akinon-filter__section-header--
|
|
104
|
+
/* Let the header text span fill the header so the actions can sit at the far right. */
|
|
105
|
+
.akinon-filter__section-header--actions .akinon-collapse-header-text {
|
|
82
106
|
flex: 1 1 auto !important;
|
|
83
107
|
}
|
|
84
108
|
|
|
@@ -86,10 +110,11 @@
|
|
|
86
110
|
color: var(--color-gray-500);
|
|
87
111
|
}
|
|
88
112
|
|
|
89
|
-
/*
|
|
90
|
-
.akinon-filter__section-
|
|
91
|
-
|
|
92
|
-
|
|
113
|
+
/* Action buttons pinned to the far right of the header, after title and arrow. */
|
|
114
|
+
.akinon-filter__section-actions {
|
|
115
|
+
display: flex;
|
|
116
|
+
align-items: center;
|
|
117
|
+
gap: 6px;
|
|
93
118
|
margin-left: auto;
|
|
94
119
|
}
|
|
95
120
|
|