@snack-uikit/toolbar 0.10.3 → 0.10.4-preview-985e2518.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 +1 -0
- package/dist/cjs/components/Toolbar/Toolbar.d.ts +6 -3
- package/dist/cjs/components/Toolbar/Toolbar.js +46 -33
- package/dist/cjs/components/Toolbar/helpers.d.ts +2 -1
- package/dist/cjs/components/Toolbar/hooks.d.ts +12 -0
- package/dist/cjs/components/Toolbar/hooks.js +65 -0
- package/dist/cjs/components/Toolbar/styles.module.css +10 -0
- package/dist/cjs/components/Toolbar/types.d.ts +5 -0
- package/dist/cjs/constants.d.ts +2 -0
- package/dist/cjs/constants.js +2 -0
- package/dist/cjs/helperComponents/BulkActions/BulkActions.js +1 -1
- package/dist/cjs/helperComponents/FilterButton/FilterButton.d.ts +6 -0
- package/dist/cjs/helperComponents/FilterButton/FilterButton.js +35 -0
- package/dist/cjs/helperComponents/FilterButton/index.d.ts +1 -0
- package/dist/cjs/helperComponents/FilterButton/index.js +25 -0
- package/dist/cjs/helperComponents/MoreActions/MoreActions.js +1 -1
- package/dist/cjs/helperComponents/index.d.ts +1 -0
- package/dist/cjs/helperComponents/index.js +1 -0
- package/dist/esm/components/Toolbar/Toolbar.d.ts +6 -3
- package/dist/esm/components/Toolbar/Toolbar.js +6 -3
- package/dist/esm/components/Toolbar/helpers.d.ts +2 -1
- package/dist/esm/components/Toolbar/hooks.d.ts +12 -0
- package/dist/esm/components/Toolbar/hooks.js +38 -0
- package/dist/esm/components/Toolbar/styles.module.css +10 -0
- package/dist/esm/components/Toolbar/types.d.ts +5 -0
- package/dist/esm/constants.d.ts +2 -0
- package/dist/esm/constants.js +2 -0
- package/dist/esm/helperComponents/BulkActions/BulkActions.js +1 -1
- package/dist/esm/helperComponents/FilterButton/FilterButton.d.ts +6 -0
- package/dist/esm/helperComponents/FilterButton/FilterButton.js +10 -0
- package/dist/esm/helperComponents/FilterButton/index.d.ts +1 -0
- package/dist/esm/helperComponents/FilterButton/index.js +1 -0
- package/dist/esm/helperComponents/MoreActions/MoreActions.js +2 -2
- package/dist/esm/helperComponents/index.d.ts +1 -0
- package/dist/esm/helperComponents/index.js +1 -0
- package/package.json +10 -5
- package/src/components/Toolbar/Toolbar.tsx +62 -49
- package/src/components/Toolbar/helpers.ts +5 -1
- package/src/components/Toolbar/hooks.ts +82 -0
- package/src/components/Toolbar/styles.module.scss +11 -0
- package/src/components/Toolbar/types.ts +7 -0
- package/src/constants.ts +2 -0
- package/src/helperComponents/BulkActions/BulkActions.tsx +1 -1
- package/src/helperComponents/FilterButton/FilterButton.tsx +28 -0
- package/src/helperComponents/FilterButton/index.ts +1 -0
- package/src/helperComponents/MoreActions/MoreActions.tsx +2 -2
- package/src/helperComponents/index.ts +1 -0
package/README.md
CHANGED
|
@@ -24,6 +24,7 @@ Toolbar
|
|
|
24
24
|
| onRefresh | `() => void` | - | Колбек обновления |
|
|
25
25
|
| after | `ReactNode` | - | Дополнительный слот в конце тулбара |
|
|
26
26
|
| moreActions | `Action[]` | - | Элементы выпадающего списка кнопки с действиями |
|
|
27
|
+
| filterRow | `FilterRow<TState>` | - | |
|
|
27
28
|
|
|
28
29
|
|
|
29
30
|
[//]: DOCUMENTATION_SECTION_END
|
|
@@ -1,4 +1,7 @@
|
|
|
1
|
+
import { FiltersState } from '@snack-uikit/chips';
|
|
1
2
|
import { WithSupportProps } from '@snack-uikit/utils';
|
|
2
|
-
import { CheckedToolbarProps, DefaultToolbarProps } from './types';
|
|
3
|
-
export type ToolbarProps = WithSupportProps<DefaultToolbarProps | CheckedToolbarProps
|
|
4
|
-
|
|
3
|
+
import { CheckedToolbarProps, DefaultToolbarProps, FilterRow } from './types';
|
|
4
|
+
export type ToolbarProps<TState extends FiltersState> = WithSupportProps<DefaultToolbarProps | CheckedToolbarProps> & {
|
|
5
|
+
filterRow?: FilterRow<TState>;
|
|
6
|
+
};
|
|
7
|
+
export declare function Toolbar<TState extends FiltersState>({ className, after, outline, moreActions, onRefresh, search, filterRow: filterRowProps, ...rest }: ToolbarProps<TState>): import("react/jsx-runtime").JSX.Element;
|
|
@@ -21,12 +21,14 @@ const jsx_runtime_1 = require("react/jsx-runtime");
|
|
|
21
21
|
const classnames_1 = __importDefault(require("classnames"));
|
|
22
22
|
const react_1 = require("react");
|
|
23
23
|
const button_1 = require("@snack-uikit/button");
|
|
24
|
+
const chips_1 = require("@snack-uikit/chips");
|
|
24
25
|
const icons_1 = require("@snack-uikit/icons");
|
|
25
26
|
const search_private_1 = require("@snack-uikit/search-private");
|
|
26
27
|
const utils_1 = require("@snack-uikit/utils");
|
|
27
28
|
const constants_1 = require("../../constants");
|
|
28
29
|
const helperComponents_1 = require("../../helperComponents");
|
|
29
30
|
const helpers_1 = require("./helpers");
|
|
31
|
+
const hooks_1 = require("./hooks");
|
|
30
32
|
const styles_module_scss_1 = __importDefault(require('./styles.module.css'));
|
|
31
33
|
function Toolbar(_a) {
|
|
32
34
|
var {
|
|
@@ -35,48 +37,59 @@ function Toolbar(_a) {
|
|
|
35
37
|
outline,
|
|
36
38
|
moreActions,
|
|
37
39
|
onRefresh,
|
|
38
|
-
search
|
|
40
|
+
search,
|
|
41
|
+
filterRow: filterRowProps
|
|
39
42
|
} = _a,
|
|
40
|
-
rest = __rest(_a, ["className", "after", "outline", "moreActions", "onRefresh", "search"]);
|
|
43
|
+
rest = __rest(_a, ["className", "after", "outline", "moreActions", "onRefresh", "search", "filterRow"]);
|
|
41
44
|
const needsBulkActions = (0, helpers_1.isBulkActionsProps)(rest);
|
|
42
45
|
const hasLeftSideElements = Boolean(needsBulkActions || onRefresh);
|
|
43
46
|
const resizingContainerRef = (0, react_1.useRef)(null);
|
|
47
|
+
const {
|
|
48
|
+
filterButton,
|
|
49
|
+
filterRow
|
|
50
|
+
} = (0, hooks_1.useFilters)({
|
|
51
|
+
filterRow: filterRowProps
|
|
52
|
+
});
|
|
44
53
|
return (0, jsx_runtime_1.jsxs)("div", Object.assign({
|
|
45
|
-
className:
|
|
54
|
+
className: styles_module_scss_1.default.containerWrapper
|
|
46
55
|
}, (0, utils_1.extractSupportProps)(rest), {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
children: [
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
className: styles_module_scss_1.default.search,
|
|
64
|
-
size: 'm',
|
|
65
|
-
"data-test-id": constants_1.TEST_IDS.search
|
|
66
|
-
})), (moreActions || after) && (0, jsx_runtime_1.jsxs)("div", {
|
|
67
|
-
className: styles_module_scss_1.default.flexRow,
|
|
68
|
-
"data-align-right": !search && !hasLeftSideElements || undefined,
|
|
69
|
-
children: [after && (0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, {
|
|
70
|
-
children: [(search || hasLeftSideElements) && (0, jsx_runtime_1.jsx)(helperComponents_1.Separator, {}), (0, jsx_runtime_1.jsx)("div", {
|
|
71
|
-
"data-test-id": constants_1.TEST_IDS.after,
|
|
72
|
-
className: styles_module_scss_1.default.actions,
|
|
73
|
-
children: after
|
|
56
|
+
children: [(0, jsx_runtime_1.jsxs)("div", {
|
|
57
|
+
className: (0, classnames_1.default)(styles_module_scss_1.default.container, className),
|
|
58
|
+
"data-outline": outline || undefined,
|
|
59
|
+
ref: resizingContainerRef,
|
|
60
|
+
children: [hasLeftSideElements && (0, jsx_runtime_1.jsxs)("div", {
|
|
61
|
+
className: styles_module_scss_1.default.beforeSearch,
|
|
62
|
+
children: [needsBulkActions && (0, jsx_runtime_1.jsx)(helperComponents_1.BulkActions, Object.assign({}, (0, helpers_1.extractBulkActionsProps)(rest), {
|
|
63
|
+
resizingContainerRef: resizingContainerRef
|
|
64
|
+
})), needsBulkActions && (0, jsx_runtime_1.jsx)(helperComponents_1.Separator, {}), onRefresh && (0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, {
|
|
65
|
+
children: [(0, jsx_runtime_1.jsx)(button_1.ButtonFunction, {
|
|
66
|
+
icon: (0, jsx_runtime_1.jsx)(icons_1.UpdateSVG, {}),
|
|
67
|
+
size: 'm',
|
|
68
|
+
className: styles_module_scss_1.default.updateButton,
|
|
69
|
+
onClick: onRefresh,
|
|
70
|
+
"data-test-id": constants_1.TEST_IDS.refreshButton
|
|
71
|
+
}), (0, jsx_runtime_1.jsx)(helperComponents_1.Separator, {})]
|
|
74
72
|
})]
|
|
75
|
-
}),
|
|
76
|
-
|
|
73
|
+
}), search && (0, jsx_runtime_1.jsx)(search_private_1.SearchPrivate, Object.assign({}, search, {
|
|
74
|
+
className: styles_module_scss_1.default.search,
|
|
75
|
+
size: 'm',
|
|
76
|
+
"data-test-id": constants_1.TEST_IDS.search
|
|
77
|
+
})), (moreActions || after || filterButton) && (0, jsx_runtime_1.jsxs)("div", {
|
|
78
|
+
className: styles_module_scss_1.default.flexRow,
|
|
79
|
+
"data-align-right": !search && !hasLeftSideElements || undefined,
|
|
80
|
+
children: [after && (0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, {
|
|
81
|
+
children: [(search || hasLeftSideElements) && (0, jsx_runtime_1.jsx)(helperComponents_1.Separator, {}), (0, jsx_runtime_1.jsx)("div", {
|
|
82
|
+
"data-test-id": constants_1.TEST_IDS.after,
|
|
83
|
+
className: styles_module_scss_1.default.actions,
|
|
84
|
+
children: after
|
|
85
|
+
})]
|
|
86
|
+
}), (moreActions || filterButton) && (0, jsx_runtime_1.jsx)(helperComponents_1.Separator, {}), filterButton && (0, jsx_runtime_1.jsx)(helperComponents_1.FilterButton, Object.assign({}, filterButton)), moreActions && (0, jsx_runtime_1.jsx)(helperComponents_1.MoreActions, {
|
|
77
87
|
moreActions: moreActions
|
|
78
88
|
})]
|
|
79
89
|
})]
|
|
80
|
-
})
|
|
90
|
+
}), filterRow && (0, jsx_runtime_1.jsx)(chips_1.ChipChoiceRow, Object.assign({}, filterRow, {
|
|
91
|
+
size: 'xs',
|
|
92
|
+
"data-test-id": constants_1.TEST_IDS.filterRow
|
|
93
|
+
}))]
|
|
81
94
|
}));
|
|
82
95
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { FiltersState } from '@snack-uikit/chips';
|
|
1
2
|
import { ToolbarProps } from './Toolbar';
|
|
2
3
|
import { ToolbarBulkActionProps } from './types';
|
|
3
4
|
export declare function extractBulkActionsProps({ onCheck, checked, indeterminate, bulkActions, selectionMode, }: ToolbarBulkActionProps): {
|
|
@@ -7,4 +8,4 @@ export declare function extractBulkActionsProps({ onCheck, checked, indeterminat
|
|
|
7
8
|
actions: import("../../helperComponents").BulkAction[];
|
|
8
9
|
selectionMode: import("../../helperComponents").SelectionMode | undefined;
|
|
9
10
|
};
|
|
10
|
-
export declare function isBulkActionsProps(props: Partial<ToolbarProps
|
|
11
|
+
export declare function isBulkActionsProps<TState extends FiltersState>(props: Partial<ToolbarProps<TState>>): props is ToolbarBulkActionProps;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { FiltersState } from '@snack-uikit/chips';
|
|
2
|
+
import { FilterButtonProps } from '../../helperComponents';
|
|
3
|
+
import { FilterRow } from './types';
|
|
4
|
+
type UseFiltersProps<TState extends FiltersState> = {
|
|
5
|
+
filterRow?: FilterRow<TState>;
|
|
6
|
+
};
|
|
7
|
+
type UseFiltersReturnType<TState extends FiltersState> = {
|
|
8
|
+
filterButton?: FilterButtonProps;
|
|
9
|
+
filterRow?: FilterRow<TState>;
|
|
10
|
+
};
|
|
11
|
+
export declare function useFilters<TState extends FiltersState>({ filterRow, }: UseFiltersProps<TState>): UseFiltersReturnType<TState>;
|
|
12
|
+
export {};
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var __importDefault = void 0 && (void 0).__importDefault || function (mod) {
|
|
4
|
+
return mod && mod.__esModule ? mod : {
|
|
5
|
+
"default": mod
|
|
6
|
+
};
|
|
7
|
+
};
|
|
8
|
+
Object.defineProperty(exports, "__esModule", {
|
|
9
|
+
value: true
|
|
10
|
+
});
|
|
11
|
+
exports.useFilters = useFilters;
|
|
12
|
+
const classnames_1 = __importDefault(require("classnames"));
|
|
13
|
+
const react_1 = require("react");
|
|
14
|
+
const uncontrollable_1 = require("uncontrollable");
|
|
15
|
+
const chips_1 = require("@snack-uikit/chips");
|
|
16
|
+
const styles_module_scss_1 = __importDefault(require('./styles.module.css'));
|
|
17
|
+
function useFilters(_ref) {
|
|
18
|
+
let {
|
|
19
|
+
filterRow
|
|
20
|
+
} = _ref;
|
|
21
|
+
var _a;
|
|
22
|
+
const [filtersOpen, setFiltersOpen] = (0, uncontrollable_1.useUncontrolledProp)(filterRow === null || filterRow === void 0 ? void 0 : filterRow.open, false, newValue => {
|
|
23
|
+
var _a;
|
|
24
|
+
const result = typeof newValue === 'function' ? newValue(filtersOpen) : newValue;
|
|
25
|
+
(_a = filterRow === null || filterRow === void 0 ? void 0 : filterRow.onOpenChange) === null || _a === void 0 ? void 0 : _a.call(filterRow, result);
|
|
26
|
+
});
|
|
27
|
+
const [value, setValue] = (0, uncontrollable_1.useUncontrolledProp)(filterRow === null || filterRow === void 0 ? void 0 : filterRow.value, (_a = filterRow === null || filterRow === void 0 ? void 0 : filterRow.defaultValue) !== null && _a !== void 0 ? _a : {}, newValue => {
|
|
28
|
+
var _a;
|
|
29
|
+
const result = typeof newValue === 'function' ? newValue(value) : newValue;
|
|
30
|
+
(_a = filterRow === null || filterRow === void 0 ? void 0 : filterRow.onChange) === null || _a === void 0 ? void 0 : _a.call(filterRow, result);
|
|
31
|
+
});
|
|
32
|
+
const [visibleFilters, setVisibleFilters] = (0, uncontrollable_1.useUncontrolledProp)(filterRow === null || filterRow === void 0 ? void 0 : filterRow.visibleFilters, Object.keys(value), newState => {
|
|
33
|
+
var _a;
|
|
34
|
+
const result = typeof newState === 'function' ? newState(visibleFilters) : newState;
|
|
35
|
+
(_a = filterRow === null || filterRow === void 0 ? void 0 : filterRow.onVisibleFiltersChange) === null || _a === void 0 ? void 0 : _a.call(filterRow, result);
|
|
36
|
+
});
|
|
37
|
+
const patchedFilters = (0, react_1.useMemo)(() => {
|
|
38
|
+
var _a;
|
|
39
|
+
return ((_a = filterRow === null || filterRow === void 0 ? void 0 : filterRow.filters) !== null && _a !== void 0 ? _a : []).map(filter => {
|
|
40
|
+
if (['single', 'multiple'].includes(filter.type)) {
|
|
41
|
+
return Object.assign(Object.assign({}, filter), {
|
|
42
|
+
dropDownClassName: (0, classnames_1.default)(filter.dropDownClassName, styles_module_scss_1.default.list)
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
return filter;
|
|
46
|
+
});
|
|
47
|
+
}, [filterRow === null || filterRow === void 0 ? void 0 : filterRow.filters]);
|
|
48
|
+
const numberOfFilters = (0, react_1.useMemo)(() => Object.keys(value).reduce((result, filterKey) => result + Number((0, chips_1.hasFilterBeenApplied)(value[filterKey])), 0), [value]);
|
|
49
|
+
return {
|
|
50
|
+
filterButton: filterRow ? {
|
|
51
|
+
open: filtersOpen,
|
|
52
|
+
onOpenChange: setFiltersOpen,
|
|
53
|
+
numberOfFilters
|
|
54
|
+
} : undefined,
|
|
55
|
+
filterRow: filtersOpen && filterRow ? Object.assign(Object.assign({}, filterRow), {
|
|
56
|
+
open: undefined,
|
|
57
|
+
onOpenChange: undefined,
|
|
58
|
+
filters: patchedFilters,
|
|
59
|
+
value,
|
|
60
|
+
onChange: setValue,
|
|
61
|
+
visibleFilters,
|
|
62
|
+
onVisibleFiltersChange: setVisibleFilters
|
|
63
|
+
}) : undefined
|
|
64
|
+
};
|
|
65
|
+
}
|
|
@@ -2,6 +2,12 @@
|
|
|
2
2
|
box-sizing:border-box;
|
|
3
3
|
}
|
|
4
4
|
|
|
5
|
+
.containerWrapper{
|
|
6
|
+
gap:var(--space-toolbar-container-wrapper-gap, 8px);
|
|
7
|
+
display:flex;
|
|
8
|
+
flex-direction:column;
|
|
9
|
+
}
|
|
10
|
+
|
|
5
11
|
.container{
|
|
6
12
|
border-radius:var(--radius-toolbar-container, 14px);
|
|
7
13
|
position:relative;
|
|
@@ -71,4 +77,8 @@
|
|
|
71
77
|
.actions{
|
|
72
78
|
flex-shrink:0;
|
|
73
79
|
box-sizing:border-box;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.list{
|
|
83
|
+
width:200px;
|
|
74
84
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ReactNode } from 'react';
|
|
2
|
+
import { ChipChoiceRowProps, FiltersState } from '@snack-uikit/chips';
|
|
2
3
|
import { BulkActionsProps, MoreActionsProps } from '../../helperComponents';
|
|
3
4
|
import { NeverOrUndefined, RequireAtLeastOne } from './typesUtils';
|
|
4
5
|
type OptionalProps = {
|
|
@@ -35,4 +36,8 @@ export type ToolbarBulkActionProps = Omit<BulkActionsProps, 'actions'> & {
|
|
|
35
36
|
};
|
|
36
37
|
export type CheckedToolbarProps = CommonToolbarProps & ToolbarBulkActionProps & OptionalProps;
|
|
37
38
|
export type DefaultToolbarProps = CommonToolbarProps & NeverOrUndefined<ToolbarBulkActionProps> & RequireAtLeastOne<OptionalProps>;
|
|
39
|
+
export type FilterRow<TState extends FiltersState> = Omit<ChipChoiceRowProps<TState>, 'size' | 'data-test-id'> & {
|
|
40
|
+
open?: boolean;
|
|
41
|
+
onOpenChange?(isOpen: boolean): void;
|
|
42
|
+
};
|
|
38
43
|
export {};
|
package/dist/cjs/constants.d.ts
CHANGED
package/dist/cjs/constants.js
CHANGED
|
@@ -16,6 +16,8 @@ exports.TEST_IDS = {
|
|
|
16
16
|
moreBulkActionsButton: 'toolbar__more-bulk-actions-button',
|
|
17
17
|
refreshButton: 'toolbar__refresh-button',
|
|
18
18
|
search: 'toolbar__search',
|
|
19
|
+
filterButton: 'toolbar__filter-button',
|
|
20
|
+
filterRow: 'toolbar__filter-row',
|
|
19
21
|
moreActionsButton: 'toolbar__more-actions-button',
|
|
20
22
|
droplist: 'toolbar__droplist',
|
|
21
23
|
option: 'toolbar__droplist-option',
|
|
@@ -30,7 +30,7 @@ function BulkActions(_ref) {
|
|
|
30
30
|
resizingContainerRef
|
|
31
31
|
} = _ref;
|
|
32
32
|
const handleKeyDown = (0, react_1.useCallback)(e => {
|
|
33
|
-
if (e.key === ' ') {
|
|
33
|
+
if (e.key === ' ' || e.key === 'Enter') {
|
|
34
34
|
onCheck === null || onCheck === void 0 ? void 0 : onCheck();
|
|
35
35
|
}
|
|
36
36
|
}, [onCheck]);
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.FilterButton = FilterButton;
|
|
7
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
8
|
+
const button_1 = require("@snack-uikit/button");
|
|
9
|
+
const icons_1 = require("@snack-uikit/icons");
|
|
10
|
+
const locale_1 = require("@snack-uikit/locale");
|
|
11
|
+
const tooltip_1 = require("@snack-uikit/tooltip");
|
|
12
|
+
const constants_1 = require("../../constants");
|
|
13
|
+
function FilterButton(_ref) {
|
|
14
|
+
let {
|
|
15
|
+
open,
|
|
16
|
+
onOpenChange,
|
|
17
|
+
numberOfFilters
|
|
18
|
+
} = _ref;
|
|
19
|
+
const {
|
|
20
|
+
t
|
|
21
|
+
} = (0, locale_1.useLocale)('Toolbar');
|
|
22
|
+
return (0, jsx_runtime_1.jsx)(tooltip_1.Tooltip, {
|
|
23
|
+
tip: open ? t('hideFilters') : t('showFilters'),
|
|
24
|
+
children: (0, jsx_runtime_1.jsx)(button_1.ButtonFunction, {
|
|
25
|
+
size: 'm',
|
|
26
|
+
icon: (0, jsx_runtime_1.jsx)(icons_1.FilterSVG, {}),
|
|
27
|
+
onClick: () => onOpenChange(!open),
|
|
28
|
+
counter: numberOfFilters ? {
|
|
29
|
+
value: numberOfFilters,
|
|
30
|
+
appearance: 'neutral'
|
|
31
|
+
} : undefined,
|
|
32
|
+
"data-test-id": constants_1.TEST_IDS.filterButton
|
|
33
|
+
})
|
|
34
|
+
});
|
|
35
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './FilterButton';
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var __createBinding = void 0 && (void 0).__createBinding || (Object.create ? function (o, m, k, k2) {
|
|
4
|
+
if (k2 === undefined) k2 = k;
|
|
5
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
6
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
7
|
+
desc = {
|
|
8
|
+
enumerable: true,
|
|
9
|
+
get: function () {
|
|
10
|
+
return m[k];
|
|
11
|
+
}
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
Object.defineProperty(o, k2, desc);
|
|
15
|
+
} : function (o, m, k, k2) {
|
|
16
|
+
if (k2 === undefined) k2 = k;
|
|
17
|
+
o[k2] = m[k];
|
|
18
|
+
});
|
|
19
|
+
var __exportStar = void 0 && (void 0).__exportStar || function (m, exports) {
|
|
20
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
21
|
+
};
|
|
22
|
+
Object.defineProperty(exports, "__esModule", {
|
|
23
|
+
value: true
|
|
24
|
+
});
|
|
25
|
+
__exportStar(require("./FilterButton"), exports);
|
|
@@ -40,7 +40,7 @@ function MoreActions(_ref) {
|
|
|
40
40
|
'data-test-id': constants_1.TEST_IDS.option
|
|
41
41
|
})),
|
|
42
42
|
children: (0, jsx_runtime_1.jsx)(button_1.ButtonFunction, {
|
|
43
|
-
icon: (0, jsx_runtime_1.jsx)(icons_1.
|
|
43
|
+
icon: (0, jsx_runtime_1.jsx)(icons_1.MoreSVG, {
|
|
44
44
|
size: 24
|
|
45
45
|
}),
|
|
46
46
|
size: 'm',
|
|
@@ -23,5 +23,6 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
23
23
|
value: true
|
|
24
24
|
});
|
|
25
25
|
__exportStar(require("./BulkActions"), exports);
|
|
26
|
+
__exportStar(require("./FilterButton"), exports);
|
|
26
27
|
__exportStar(require("./Separator"), exports);
|
|
27
28
|
__exportStar(require("./MoreActions"), exports);
|
|
@@ -1,4 +1,7 @@
|
|
|
1
|
+
import { FiltersState } from '@snack-uikit/chips';
|
|
1
2
|
import { WithSupportProps } from '@snack-uikit/utils';
|
|
2
|
-
import { CheckedToolbarProps, DefaultToolbarProps } from './types';
|
|
3
|
-
export type ToolbarProps = WithSupportProps<DefaultToolbarProps | CheckedToolbarProps
|
|
4
|
-
|
|
3
|
+
import { CheckedToolbarProps, DefaultToolbarProps, FilterRow } from './types';
|
|
4
|
+
export type ToolbarProps<TState extends FiltersState> = WithSupportProps<DefaultToolbarProps | CheckedToolbarProps> & {
|
|
5
|
+
filterRow?: FilterRow<TState>;
|
|
6
|
+
};
|
|
7
|
+
export declare function Toolbar<TState extends FiltersState>({ className, after, outline, moreActions, onRefresh, search, filterRow: filterRowProps, ...rest }: ToolbarProps<TState>): import("react/jsx-runtime").JSX.Element;
|
|
@@ -13,17 +13,20 @@ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-run
|
|
|
13
13
|
import cn from 'classnames';
|
|
14
14
|
import { useRef } from 'react';
|
|
15
15
|
import { ButtonFunction } from '@snack-uikit/button';
|
|
16
|
+
import { ChipChoiceRow } from '@snack-uikit/chips';
|
|
16
17
|
import { UpdateSVG } from '@snack-uikit/icons';
|
|
17
18
|
import { SearchPrivate } from '@snack-uikit/search-private';
|
|
18
19
|
import { extractSupportProps } from '@snack-uikit/utils';
|
|
19
20
|
import { TEST_IDS } from '../../constants';
|
|
20
|
-
import { BulkActions, MoreActions, Separator } from '../../helperComponents';
|
|
21
|
+
import { BulkActions, FilterButton, MoreActions, Separator } from '../../helperComponents';
|
|
21
22
|
import { extractBulkActionsProps, isBulkActionsProps } from './helpers';
|
|
23
|
+
import { useFilters } from './hooks';
|
|
22
24
|
import styles from './styles.module.css';
|
|
23
25
|
export function Toolbar(_a) {
|
|
24
|
-
var { className, after, outline, moreActions, onRefresh, search } = _a, rest = __rest(_a, ["className", "after", "outline", "moreActions", "onRefresh", "search"]);
|
|
26
|
+
var { className, after, outline, moreActions, onRefresh, search, filterRow: filterRowProps } = _a, rest = __rest(_a, ["className", "after", "outline", "moreActions", "onRefresh", "search", "filterRow"]);
|
|
25
27
|
const needsBulkActions = isBulkActionsProps(rest);
|
|
26
28
|
const hasLeftSideElements = Boolean(needsBulkActions || onRefresh);
|
|
27
29
|
const resizingContainerRef = useRef(null);
|
|
28
|
-
|
|
30
|
+
const { filterButton, filterRow } = useFilters({ filterRow: filterRowProps });
|
|
31
|
+
return (_jsxs("div", Object.assign({ className: styles.containerWrapper }, extractSupportProps(rest), { children: [_jsxs("div", { className: cn(styles.container, className), "data-outline": outline || undefined, ref: resizingContainerRef, children: [hasLeftSideElements && (_jsxs("div", { className: styles.beforeSearch, children: [needsBulkActions && (_jsx(BulkActions, Object.assign({}, extractBulkActionsProps(rest), { resizingContainerRef: resizingContainerRef }))), needsBulkActions && _jsx(Separator, {}), onRefresh && (_jsxs(_Fragment, { children: [_jsx(ButtonFunction, { icon: _jsx(UpdateSVG, {}), size: 'm', className: styles.updateButton, onClick: onRefresh, "data-test-id": TEST_IDS.refreshButton }), _jsx(Separator, {})] }))] })), search && _jsx(SearchPrivate, Object.assign({}, search, { className: styles.search, size: 'm', "data-test-id": TEST_IDS.search })), (moreActions || after || filterButton) && (_jsxs("div", { className: styles.flexRow, "data-align-right": (!search && !hasLeftSideElements) || undefined, children: [after && (_jsxs(_Fragment, { children: [(search || hasLeftSideElements) && _jsx(Separator, {}), _jsx("div", { "data-test-id": TEST_IDS.after, className: styles.actions, children: after })] })), (moreActions || filterButton) && _jsx(Separator, {}), filterButton && _jsx(FilterButton, Object.assign({}, filterButton)), moreActions && _jsx(MoreActions, { moreActions: moreActions })] }))] }), filterRow && _jsx(ChipChoiceRow, Object.assign({}, filterRow, { size: 'xs', "data-test-id": TEST_IDS.filterRow }))] })));
|
|
29
32
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { FiltersState } from '@snack-uikit/chips';
|
|
1
2
|
import { ToolbarProps } from './Toolbar';
|
|
2
3
|
import { ToolbarBulkActionProps } from './types';
|
|
3
4
|
export declare function extractBulkActionsProps({ onCheck, checked, indeterminate, bulkActions, selectionMode, }: ToolbarBulkActionProps): {
|
|
@@ -7,4 +8,4 @@ export declare function extractBulkActionsProps({ onCheck, checked, indeterminat
|
|
|
7
8
|
actions: import("../../helperComponents").BulkAction[];
|
|
8
9
|
selectionMode: import("../../helperComponents").SelectionMode | undefined;
|
|
9
10
|
};
|
|
10
|
-
export declare function isBulkActionsProps(props: Partial<ToolbarProps
|
|
11
|
+
export declare function isBulkActionsProps<TState extends FiltersState>(props: Partial<ToolbarProps<TState>>): props is ToolbarBulkActionProps;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { FiltersState } from '@snack-uikit/chips';
|
|
2
|
+
import { FilterButtonProps } from '../../helperComponents';
|
|
3
|
+
import { FilterRow } from './types';
|
|
4
|
+
type UseFiltersProps<TState extends FiltersState> = {
|
|
5
|
+
filterRow?: FilterRow<TState>;
|
|
6
|
+
};
|
|
7
|
+
type UseFiltersReturnType<TState extends FiltersState> = {
|
|
8
|
+
filterButton?: FilterButtonProps;
|
|
9
|
+
filterRow?: FilterRow<TState>;
|
|
10
|
+
};
|
|
11
|
+
export declare function useFilters<TState extends FiltersState>({ filterRow, }: UseFiltersProps<TState>): UseFiltersReturnType<TState>;
|
|
12
|
+
export {};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import cn from 'classnames';
|
|
2
|
+
import { useMemo } from 'react';
|
|
3
|
+
import { useUncontrolledProp } from 'uncontrollable';
|
|
4
|
+
import { hasFilterBeenApplied } from '@snack-uikit/chips';
|
|
5
|
+
import styles from './styles.module.css';
|
|
6
|
+
export function useFilters({ filterRow, }) {
|
|
7
|
+
var _a;
|
|
8
|
+
const [filtersOpen, setFiltersOpen] = useUncontrolledProp(filterRow === null || filterRow === void 0 ? void 0 : filterRow.open, false, newValue => {
|
|
9
|
+
var _a;
|
|
10
|
+
const result = typeof newValue === 'function' ? newValue(filtersOpen) : newValue;
|
|
11
|
+
(_a = filterRow === null || filterRow === void 0 ? void 0 : filterRow.onOpenChange) === null || _a === void 0 ? void 0 : _a.call(filterRow, result);
|
|
12
|
+
});
|
|
13
|
+
const [value, setValue] = useUncontrolledProp(filterRow === null || filterRow === void 0 ? void 0 : filterRow.value, ((_a = filterRow === null || filterRow === void 0 ? void 0 : filterRow.defaultValue) !== null && _a !== void 0 ? _a : {}), newValue => {
|
|
14
|
+
var _a;
|
|
15
|
+
const result = typeof newValue === 'function' ? newValue(value) : newValue;
|
|
16
|
+
(_a = filterRow === null || filterRow === void 0 ? void 0 : filterRow.onChange) === null || _a === void 0 ? void 0 : _a.call(filterRow, result);
|
|
17
|
+
});
|
|
18
|
+
const [visibleFilters, setVisibleFilters] = useUncontrolledProp(filterRow === null || filterRow === void 0 ? void 0 : filterRow.visibleFilters, Object.keys(value), newState => {
|
|
19
|
+
var _a;
|
|
20
|
+
const result = typeof newState === 'function' ? newState(visibleFilters) : newState;
|
|
21
|
+
(_a = filterRow === null || filterRow === void 0 ? void 0 : filterRow.onVisibleFiltersChange) === null || _a === void 0 ? void 0 : _a.call(filterRow, result);
|
|
22
|
+
});
|
|
23
|
+
const patchedFilters = useMemo(() => {
|
|
24
|
+
var _a;
|
|
25
|
+
return ((_a = filterRow === null || filterRow === void 0 ? void 0 : filterRow.filters) !== null && _a !== void 0 ? _a : []).map(filter => {
|
|
26
|
+
if (['single', 'multiple'].includes(filter.type)) {
|
|
27
|
+
return Object.assign(Object.assign({}, filter), { dropDownClassName: cn(filter.dropDownClassName, styles.list) });
|
|
28
|
+
}
|
|
29
|
+
return filter;
|
|
30
|
+
});
|
|
31
|
+
}, [filterRow === null || filterRow === void 0 ? void 0 : filterRow.filters]);
|
|
32
|
+
const numberOfFilters = useMemo(() => Object.keys(value).reduce((result, filterKey) => result + Number(hasFilterBeenApplied(value[filterKey])), 0), [value]);
|
|
33
|
+
return {
|
|
34
|
+
filterButton: filterRow ? { open: filtersOpen, onOpenChange: setFiltersOpen, numberOfFilters } : undefined,
|
|
35
|
+
filterRow: filtersOpen && filterRow
|
|
36
|
+
? Object.assign(Object.assign({}, filterRow), { open: undefined, onOpenChange: undefined, filters: patchedFilters, value, onChange: setValue, visibleFilters, onVisibleFiltersChange: setVisibleFilters }) : undefined,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
@@ -2,6 +2,12 @@
|
|
|
2
2
|
box-sizing:border-box;
|
|
3
3
|
}
|
|
4
4
|
|
|
5
|
+
.containerWrapper{
|
|
6
|
+
gap:var(--space-toolbar-container-wrapper-gap, 8px);
|
|
7
|
+
display:flex;
|
|
8
|
+
flex-direction:column;
|
|
9
|
+
}
|
|
10
|
+
|
|
5
11
|
.container{
|
|
6
12
|
border-radius:var(--radius-toolbar-container, 14px);
|
|
7
13
|
position:relative;
|
|
@@ -71,4 +77,8 @@
|
|
|
71
77
|
.actions{
|
|
72
78
|
flex-shrink:0;
|
|
73
79
|
box-sizing:border-box;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.list{
|
|
83
|
+
width:200px;
|
|
74
84
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ReactNode } from 'react';
|
|
2
|
+
import { ChipChoiceRowProps, FiltersState } from '@snack-uikit/chips';
|
|
2
3
|
import { BulkActionsProps, MoreActionsProps } from '../../helperComponents';
|
|
3
4
|
import { NeverOrUndefined, RequireAtLeastOne } from './typesUtils';
|
|
4
5
|
type OptionalProps = {
|
|
@@ -35,4 +36,8 @@ export type ToolbarBulkActionProps = Omit<BulkActionsProps, 'actions'> & {
|
|
|
35
36
|
};
|
|
36
37
|
export type CheckedToolbarProps = CommonToolbarProps & ToolbarBulkActionProps & OptionalProps;
|
|
37
38
|
export type DefaultToolbarProps = CommonToolbarProps & NeverOrUndefined<ToolbarBulkActionProps> & RequireAtLeastOne<OptionalProps>;
|
|
39
|
+
export type FilterRow<TState extends FiltersState> = Omit<ChipChoiceRowProps<TState>, 'size' | 'data-test-id'> & {
|
|
40
|
+
open?: boolean;
|
|
41
|
+
onOpenChange?(isOpen: boolean): void;
|
|
42
|
+
};
|
|
38
43
|
export {};
|
package/dist/esm/constants.d.ts
CHANGED
package/dist/esm/constants.js
CHANGED
|
@@ -10,6 +10,8 @@ export const TEST_IDS = {
|
|
|
10
10
|
moreBulkActionsButton: 'toolbar__more-bulk-actions-button',
|
|
11
11
|
refreshButton: 'toolbar__refresh-button',
|
|
12
12
|
search: 'toolbar__search',
|
|
13
|
+
filterButton: 'toolbar__filter-button',
|
|
14
|
+
filterRow: 'toolbar__filter-row',
|
|
13
15
|
moreActionsButton: 'toolbar__more-actions-button',
|
|
14
16
|
droplist: 'toolbar__droplist',
|
|
15
17
|
option: 'toolbar__droplist-option',
|
|
@@ -11,7 +11,7 @@ import { SELECTION_MODE } from './constants';
|
|
|
11
11
|
import styles from './styles.module.css';
|
|
12
12
|
export function BulkActions({ actions, checked, onCheck, indeterminate, selectionMode = SELECTION_MODE.Multiple, resizingContainerRef, }) {
|
|
13
13
|
const handleKeyDown = useCallback((e) => {
|
|
14
|
-
if (e.key === ' ') {
|
|
14
|
+
if (e.key === ' ' || e.key === 'Enter') {
|
|
15
15
|
onCheck === null || onCheck === void 0 ? void 0 : onCheck();
|
|
16
16
|
}
|
|
17
17
|
}, [onCheck]);
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { ButtonFunction } from '@snack-uikit/button';
|
|
3
|
+
import { FilterSVG } from '@snack-uikit/icons';
|
|
4
|
+
import { useLocale } from '@snack-uikit/locale';
|
|
5
|
+
import { Tooltip } from '@snack-uikit/tooltip';
|
|
6
|
+
import { TEST_IDS } from '../../constants';
|
|
7
|
+
export function FilterButton({ open, onOpenChange, numberOfFilters }) {
|
|
8
|
+
const { t } = useLocale('Toolbar');
|
|
9
|
+
return (_jsx(Tooltip, { tip: open ? t('hideFilters') : t('showFilters'), children: _jsx(ButtonFunction, { size: 'm', icon: _jsx(FilterSVG, {}), onClick: () => onOpenChange(!open), counter: numberOfFilters ? { value: numberOfFilters, appearance: 'neutral' } : undefined, "data-test-id": TEST_IDS.filterButton }) }));
|
|
10
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './FilterButton';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './FilterButton';
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { useState } from 'react';
|
|
3
3
|
import { ButtonFunction } from '@snack-uikit/button';
|
|
4
|
-
import {
|
|
4
|
+
import { MoreSVG } from '@snack-uikit/icons';
|
|
5
5
|
import { Droplist } from '@snack-uikit/list';
|
|
6
6
|
import { Tag } from '@snack-uikit/tag';
|
|
7
7
|
import { TEST_IDS } from '../../constants';
|
|
@@ -19,5 +19,5 @@ export function MoreActions({ moreActions }) {
|
|
|
19
19
|
beforeContent: item.icon,
|
|
20
20
|
afterContent: item.tagLabel ? _jsx(Tag, { label: item.tagLabel }) : undefined,
|
|
21
21
|
'data-test-id': TEST_IDS.option,
|
|
22
|
-
})), children: _jsx(ButtonFunction, { icon: _jsx(
|
|
22
|
+
})), children: _jsx(ButtonFunction, { icon: _jsx(MoreSVG, { size: 24 }), size: 'm', "data-test-id": TEST_IDS.moreActionsButton }) }));
|
|
23
23
|
}
|
package/package.json
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"access": "public"
|
|
5
5
|
},
|
|
6
6
|
"title": "Toolbar",
|
|
7
|
-
"version": "0.10.
|
|
7
|
+
"version": "0.10.4-preview-985e2518.0",
|
|
8
8
|
"sideEffects": [
|
|
9
9
|
"*.css",
|
|
10
10
|
"*.woff",
|
|
@@ -36,15 +36,20 @@
|
|
|
36
36
|
"license": "Apache-2.0",
|
|
37
37
|
"scripts": {},
|
|
38
38
|
"dependencies": {
|
|
39
|
-
"@snack-uikit/button": "0.19.
|
|
39
|
+
"@snack-uikit/button": "0.19.7-preview-985e2518.0",
|
|
40
|
+
"@snack-uikit/chips": "0.24.4-preview-985e2518.0",
|
|
40
41
|
"@snack-uikit/icons": "0.24.2",
|
|
41
|
-
"@snack-uikit/list": "0.24.
|
|
42
|
+
"@snack-uikit/list": "0.24.2-preview-985e2518.0",
|
|
42
43
|
"@snack-uikit/search-private": "0.4.9",
|
|
43
44
|
"@snack-uikit/tag": "0.12.2",
|
|
44
45
|
"@snack-uikit/toggles": "0.13.5",
|
|
45
46
|
"@snack-uikit/tooltip": "0.16.0",
|
|
46
47
|
"@snack-uikit/utils": "3.7.0",
|
|
47
|
-
"classnames": "2.5.1"
|
|
48
|
+
"classnames": "2.5.1",
|
|
49
|
+
"uncontrollable": "8.0.4"
|
|
48
50
|
},
|
|
49
|
-
"
|
|
51
|
+
"peerDependencies": {
|
|
52
|
+
"@snack-uikit/locale": "*"
|
|
53
|
+
},
|
|
54
|
+
"gitHead": "a09c8f36ad95bbe977dd1eeadd594b40b3458bdc"
|
|
50
55
|
}
|
|
@@ -2,75 +2,88 @@ import cn from 'classnames';
|
|
|
2
2
|
import { useRef } from 'react';
|
|
3
3
|
|
|
4
4
|
import { ButtonFunction } from '@snack-uikit/button';
|
|
5
|
+
import { ChipChoiceRow, FiltersState } from '@snack-uikit/chips';
|
|
5
6
|
import { UpdateSVG } from '@snack-uikit/icons';
|
|
6
7
|
import { SearchPrivate } from '@snack-uikit/search-private';
|
|
7
8
|
import { extractSupportProps, WithSupportProps } from '@snack-uikit/utils';
|
|
8
9
|
|
|
9
10
|
import { TEST_IDS } from '../../constants';
|
|
10
|
-
import { BulkActions, MoreActions, Separator } from '../../helperComponents';
|
|
11
|
+
import { BulkActions, FilterButton, MoreActions, Separator } from '../../helperComponents';
|
|
11
12
|
import { extractBulkActionsProps, isBulkActionsProps } from './helpers';
|
|
13
|
+
import { useFilters } from './hooks';
|
|
12
14
|
import styles from './styles.module.scss';
|
|
13
|
-
import { CheckedToolbarProps, DefaultToolbarProps } from './types';
|
|
15
|
+
import { CheckedToolbarProps, DefaultToolbarProps, FilterRow } from './types';
|
|
14
16
|
|
|
15
|
-
export type ToolbarProps = WithSupportProps<DefaultToolbarProps | CheckedToolbarProps
|
|
17
|
+
export type ToolbarProps<TState extends FiltersState> = WithSupportProps<DefaultToolbarProps | CheckedToolbarProps> & {
|
|
18
|
+
filterRow?: FilterRow<TState>;
|
|
19
|
+
};
|
|
16
20
|
|
|
17
|
-
export function Toolbar
|
|
21
|
+
export function Toolbar<TState extends FiltersState>({
|
|
22
|
+
className,
|
|
23
|
+
after,
|
|
24
|
+
outline,
|
|
25
|
+
moreActions,
|
|
26
|
+
onRefresh,
|
|
27
|
+
search,
|
|
28
|
+
filterRow: filterRowProps,
|
|
29
|
+
...rest
|
|
30
|
+
}: ToolbarProps<TState>) {
|
|
18
31
|
const needsBulkActions = isBulkActionsProps(rest);
|
|
19
32
|
const hasLeftSideElements = Boolean(needsBulkActions || onRefresh);
|
|
20
33
|
const resizingContainerRef = useRef<HTMLDivElement>(null);
|
|
21
34
|
|
|
35
|
+
const { filterButton, filterRow } = useFilters<TState>({ filterRow: filterRowProps });
|
|
36
|
+
|
|
22
37
|
return (
|
|
23
|
-
<div
|
|
24
|
-
className={cn(styles.container, className)}
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
38
|
+
<div className={styles.containerWrapper} {...extractSupportProps(rest)}>
|
|
39
|
+
<div className={cn(styles.container, className)} data-outline={outline || undefined} ref={resizingContainerRef}>
|
|
40
|
+
{hasLeftSideElements && (
|
|
41
|
+
<div className={styles.beforeSearch}>
|
|
42
|
+
{needsBulkActions && (
|
|
43
|
+
<BulkActions {...extractBulkActionsProps(rest)} resizingContainerRef={resizingContainerRef} />
|
|
44
|
+
)}
|
|
45
|
+
|
|
46
|
+
{needsBulkActions && <Separator />}
|
|
47
|
+
|
|
48
|
+
{onRefresh && (
|
|
49
|
+
<>
|
|
50
|
+
<ButtonFunction
|
|
51
|
+
icon={<UpdateSVG />}
|
|
52
|
+
size='m'
|
|
53
|
+
className={styles.updateButton}
|
|
54
|
+
onClick={onRefresh}
|
|
55
|
+
data-test-id={TEST_IDS.refreshButton}
|
|
56
|
+
/>
|
|
57
|
+
<Separator />
|
|
58
|
+
</>
|
|
59
|
+
)}
|
|
60
|
+
</div>
|
|
61
|
+
)}
|
|
62
|
+
|
|
63
|
+
{search && <SearchPrivate {...search} className={styles.search} size='m' data-test-id={TEST_IDS.search} />}
|
|
34
64
|
|
|
35
|
-
|
|
65
|
+
{(moreActions || after || filterButton) && (
|
|
66
|
+
<div className={styles.flexRow} data-align-right={(!search && !hasLeftSideElements) || undefined}>
|
|
67
|
+
{after && (
|
|
68
|
+
<>
|
|
69
|
+
{(search || hasLeftSideElements) && <Separator />}
|
|
36
70
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
className={styles.updateButton}
|
|
43
|
-
onClick={onRefresh}
|
|
44
|
-
data-test-id={TEST_IDS.refreshButton}
|
|
45
|
-
/>
|
|
46
|
-
<Separator />
|
|
47
|
-
</>
|
|
48
|
-
)}
|
|
49
|
-
</div>
|
|
50
|
-
)}
|
|
71
|
+
<div data-test-id={TEST_IDS.after} className={styles.actions}>
|
|
72
|
+
{after}
|
|
73
|
+
</div>
|
|
74
|
+
</>
|
|
75
|
+
)}
|
|
51
76
|
|
|
52
|
-
|
|
77
|
+
{(moreActions || filterButton) && <Separator />}
|
|
53
78
|
|
|
54
|
-
|
|
55
|
-
<div className={styles.flexRow} data-align-right={(!search && !hasLeftSideElements) || undefined}>
|
|
56
|
-
{after && (
|
|
57
|
-
<>
|
|
58
|
-
{(search || hasLeftSideElements) && <Separator />}
|
|
79
|
+
{filterButton && <FilterButton {...filterButton} />}
|
|
59
80
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
)}
|
|
81
|
+
{moreActions && <MoreActions moreActions={moreActions} />}
|
|
82
|
+
</div>
|
|
83
|
+
)}
|
|
84
|
+
</div>
|
|
65
85
|
|
|
66
|
-
|
|
67
|
-
<>
|
|
68
|
-
<Separator />
|
|
69
|
-
<MoreActions moreActions={moreActions} />
|
|
70
|
-
</>
|
|
71
|
-
)}
|
|
72
|
-
</div>
|
|
73
|
-
)}
|
|
86
|
+
{filterRow && <ChipChoiceRow<TState> {...filterRow} size='xs' data-test-id={TEST_IDS.filterRow} />}
|
|
74
87
|
</div>
|
|
75
88
|
);
|
|
76
89
|
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { FiltersState } from '@snack-uikit/chips';
|
|
2
|
+
|
|
1
3
|
import { ToolbarProps } from './Toolbar';
|
|
2
4
|
import { ToolbarBulkActionProps } from './types';
|
|
3
5
|
|
|
@@ -11,7 +13,9 @@ export function extractBulkActionsProps({
|
|
|
11
13
|
return { onCheck, checked, indeterminate, actions: bulkActions, selectionMode };
|
|
12
14
|
}
|
|
13
15
|
|
|
14
|
-
export function isBulkActionsProps
|
|
16
|
+
export function isBulkActionsProps<TState extends FiltersState>(
|
|
17
|
+
props: Partial<ToolbarProps<TState>>,
|
|
18
|
+
): props is ToolbarBulkActionProps {
|
|
15
19
|
return (
|
|
16
20
|
'bulkActions' in props &&
|
|
17
21
|
Array.isArray(props.bulkActions) &&
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import cn from 'classnames';
|
|
2
|
+
import { useMemo } from 'react';
|
|
3
|
+
import { useUncontrolledProp } from 'uncontrollable';
|
|
4
|
+
|
|
5
|
+
import { FiltersState, hasFilterBeenApplied } from '@snack-uikit/chips';
|
|
6
|
+
|
|
7
|
+
import { FilterButtonProps } from '../../helperComponents';
|
|
8
|
+
import styles from './styles.module.scss';
|
|
9
|
+
import { FilterRow } from './types';
|
|
10
|
+
|
|
11
|
+
type UseFiltersProps<TState extends FiltersState> = {
|
|
12
|
+
filterRow?: FilterRow<TState>;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
type UseFiltersReturnType<TState extends FiltersState> = {
|
|
16
|
+
filterButton?: FilterButtonProps;
|
|
17
|
+
filterRow?: FilterRow<TState>;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export function useFilters<TState extends FiltersState>({
|
|
21
|
+
filterRow,
|
|
22
|
+
}: UseFiltersProps<TState>): UseFiltersReturnType<TState> {
|
|
23
|
+
const [filtersOpen, setFiltersOpen] = useUncontrolledProp<boolean>(filterRow?.open, false, newValue => {
|
|
24
|
+
const result = typeof newValue === 'function' ? newValue(filtersOpen) : newValue;
|
|
25
|
+
filterRow?.onOpenChange?.(result);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
const [value, setValue] = useUncontrolledProp<TState>(
|
|
29
|
+
filterRow?.value,
|
|
30
|
+
(filterRow?.defaultValue ?? {}) as TState,
|
|
31
|
+
newValue => {
|
|
32
|
+
const result = typeof newValue === 'function' ? newValue(value) : newValue;
|
|
33
|
+
filterRow?.onChange?.(result);
|
|
34
|
+
},
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
const [visibleFilters, setVisibleFilters] = useUncontrolledProp<string[]>(
|
|
38
|
+
filterRow?.visibleFilters,
|
|
39
|
+
Object.keys(value),
|
|
40
|
+
newState => {
|
|
41
|
+
const result = typeof newState === 'function' ? newState(visibleFilters) : newState;
|
|
42
|
+
filterRow?.onVisibleFiltersChange?.(result);
|
|
43
|
+
},
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
const patchedFilters = useMemo(
|
|
47
|
+
() =>
|
|
48
|
+
(filterRow?.filters ?? []).map(filter => {
|
|
49
|
+
if (['single', 'multiple'].includes(filter.type)) {
|
|
50
|
+
return {
|
|
51
|
+
...filter,
|
|
52
|
+
dropDownClassName: cn(filter.dropDownClassName, styles.list),
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return filter;
|
|
57
|
+
}),
|
|
58
|
+
[filterRow?.filters],
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
const numberOfFilters = useMemo(
|
|
62
|
+
() => Object.keys(value).reduce((result, filterKey) => result + Number(hasFilterBeenApplied(value[filterKey])), 0),
|
|
63
|
+
[value],
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
return {
|
|
67
|
+
filterButton: filterRow ? { open: filtersOpen, onOpenChange: setFiltersOpen, numberOfFilters } : undefined,
|
|
68
|
+
filterRow:
|
|
69
|
+
filtersOpen && filterRow
|
|
70
|
+
? {
|
|
71
|
+
...filterRow,
|
|
72
|
+
open: undefined,
|
|
73
|
+
onOpenChange: undefined,
|
|
74
|
+
filters: patchedFilters,
|
|
75
|
+
value,
|
|
76
|
+
onChange: setValue,
|
|
77
|
+
visibleFilters,
|
|
78
|
+
onVisibleFiltersChange: setVisibleFilters,
|
|
79
|
+
}
|
|
80
|
+
: undefined,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
@@ -5,6 +5,13 @@
|
|
|
5
5
|
box-sizing: border-box;
|
|
6
6
|
}
|
|
7
7
|
|
|
8
|
+
.containerWrapper {
|
|
9
|
+
@include element.composite-var(toolbar.$toolbar-container-wrapper);
|
|
10
|
+
|
|
11
|
+
display: flex;
|
|
12
|
+
flex-direction: column;
|
|
13
|
+
}
|
|
14
|
+
|
|
8
15
|
.container {
|
|
9
16
|
@include element.composite-var(toolbar.$toolbar-container);
|
|
10
17
|
|
|
@@ -87,3 +94,7 @@
|
|
|
87
94
|
flex-shrink: 0;
|
|
88
95
|
box-sizing: border-box;
|
|
89
96
|
}
|
|
97
|
+
|
|
98
|
+
.list {
|
|
99
|
+
width: 200px;
|
|
100
|
+
}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { ReactNode } from 'react';
|
|
2
2
|
|
|
3
|
+
import { ChipChoiceRowProps, FiltersState } from '@snack-uikit/chips';
|
|
4
|
+
|
|
3
5
|
import { BulkActionsProps, MoreActionsProps } from '../../helperComponents';
|
|
4
6
|
import { NeverOrUndefined, RequireAtLeastOne } from './typesUtils';
|
|
5
7
|
|
|
@@ -43,3 +45,8 @@ export type CheckedToolbarProps = CommonToolbarProps & ToolbarBulkActionProps &
|
|
|
43
45
|
export type DefaultToolbarProps = CommonToolbarProps &
|
|
44
46
|
NeverOrUndefined<ToolbarBulkActionProps> &
|
|
45
47
|
RequireAtLeastOne<OptionalProps>;
|
|
48
|
+
|
|
49
|
+
export type FilterRow<TState extends FiltersState> = Omit<ChipChoiceRowProps<TState>, 'size' | 'data-test-id'> & {
|
|
50
|
+
open?: boolean;
|
|
51
|
+
onOpenChange?(isOpen: boolean): void;
|
|
52
|
+
};
|
package/src/constants.ts
CHANGED
|
@@ -10,6 +10,8 @@ export const TEST_IDS = {
|
|
|
10
10
|
moreBulkActionsButton: 'toolbar__more-bulk-actions-button',
|
|
11
11
|
refreshButton: 'toolbar__refresh-button',
|
|
12
12
|
search: 'toolbar__search',
|
|
13
|
+
filterButton: 'toolbar__filter-button',
|
|
14
|
+
filterRow: 'toolbar__filter-row',
|
|
13
15
|
moreActionsButton: 'toolbar__more-actions-button',
|
|
14
16
|
droplist: 'toolbar__droplist',
|
|
15
17
|
option: 'toolbar__droplist-option',
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { ButtonFunction } from '@snack-uikit/button';
|
|
2
|
+
import { FilterSVG } from '@snack-uikit/icons';
|
|
3
|
+
import { useLocale } from '@snack-uikit/locale';
|
|
4
|
+
import { Tooltip } from '@snack-uikit/tooltip';
|
|
5
|
+
|
|
6
|
+
import { TEST_IDS } from '../../constants';
|
|
7
|
+
|
|
8
|
+
export type FilterButtonProps = {
|
|
9
|
+
open: boolean;
|
|
10
|
+
onOpenChange(open: boolean): void;
|
|
11
|
+
numberOfFilters?: number;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export function FilterButton({ open, onOpenChange, numberOfFilters }: FilterButtonProps) {
|
|
15
|
+
const { t } = useLocale('Toolbar');
|
|
16
|
+
|
|
17
|
+
return (
|
|
18
|
+
<Tooltip tip={open ? t('hideFilters') : t('showFilters')}>
|
|
19
|
+
<ButtonFunction
|
|
20
|
+
size='m'
|
|
21
|
+
icon={<FilterSVG />}
|
|
22
|
+
onClick={() => onOpenChange(!open)}
|
|
23
|
+
counter={numberOfFilters ? { value: numberOfFilters, appearance: 'neutral' } : undefined}
|
|
24
|
+
data-test-id={TEST_IDS.filterButton}
|
|
25
|
+
/>
|
|
26
|
+
</Tooltip>
|
|
27
|
+
);
|
|
28
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './FilterButton';
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { ReactNode, useState } from 'react';
|
|
2
2
|
|
|
3
3
|
import { ButtonFunction } from '@snack-uikit/button';
|
|
4
|
-
import {
|
|
4
|
+
import { MoreSVG } from '@snack-uikit/icons';
|
|
5
5
|
import { BaseItemProps, Droplist } from '@snack-uikit/list';
|
|
6
6
|
import { Tag } from '@snack-uikit/tag';
|
|
7
7
|
|
|
@@ -41,7 +41,7 @@ export function MoreActions({ moreActions }: MoreActionsProps) {
|
|
|
41
41
|
'data-test-id': TEST_IDS.option,
|
|
42
42
|
}))}
|
|
43
43
|
>
|
|
44
|
-
<ButtonFunction icon={<
|
|
44
|
+
<ButtonFunction icon={<MoreSVG size={24} />} size='m' data-test-id={TEST_IDS.moreActionsButton} />
|
|
45
45
|
</Droplist>
|
|
46
46
|
);
|
|
47
47
|
}
|