@snack-uikit/toolbar 0.10.3 → 0.11.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/CHANGELOG.md +11 -0
- package/README.md +1 -0
- package/dist/cjs/components/Toolbar/Toolbar.d.ts +6 -3
- package/dist/cjs/components/Toolbar/Toolbar.js +48 -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 +63 -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/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,17 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
# 0.11.0 (2025-02-05)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
### Features
|
|
10
|
+
|
|
11
|
+
* **PDS-1081:** add filter row props ([a818aba](https://github.com/cloud-ru-tech/snack-uikit/commit/a818abadd7cd4aea8f6cdcaac58b09ca19fa3d2c))
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
|
|
6
17
|
## 0.10.3 (2025-02-04)
|
|
7
18
|
|
|
8
19
|
### Only dependencies have been changed
|
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,61 @@ 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
|
-
|
|
64
|
-
|
|
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.jsxs)(jsx_runtime_1.Fragment, {
|
|
63
|
+
children: [(0, jsx_runtime_1.jsx)(helperComponents_1.BulkActions, Object.assign({}, (0, helpers_1.extractBulkActionsProps)(rest), {
|
|
64
|
+
resizingContainerRef: resizingContainerRef
|
|
65
|
+
})), (0, jsx_runtime_1.jsx)(helperComponents_1.Separator, {})]
|
|
66
|
+
}), onRefresh && (0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, {
|
|
67
|
+
children: [(0, jsx_runtime_1.jsx)(button_1.ButtonFunction, {
|
|
68
|
+
icon: (0, jsx_runtime_1.jsx)(icons_1.UpdateSVG, {}),
|
|
69
|
+
size: 'm',
|
|
70
|
+
className: styles_module_scss_1.default.updateButton,
|
|
71
|
+
onClick: onRefresh,
|
|
72
|
+
"data-test-id": constants_1.TEST_IDS.refreshButton
|
|
73
|
+
}), (0, jsx_runtime_1.jsx)(helperComponents_1.Separator, {})]
|
|
74
74
|
})]
|
|
75
|
-
}),
|
|
76
|
-
|
|
75
|
+
}), search && (0, jsx_runtime_1.jsx)(search_private_1.SearchPrivate, Object.assign({}, search, {
|
|
76
|
+
className: styles_module_scss_1.default.search,
|
|
77
|
+
size: 'm',
|
|
78
|
+
"data-test-id": constants_1.TEST_IDS.search
|
|
79
|
+
})), (moreActions || after || filterButton) && (0, jsx_runtime_1.jsxs)("div", {
|
|
80
|
+
className: styles_module_scss_1.default.flexRow,
|
|
81
|
+
"data-align-right": !search && !hasLeftSideElements || undefined,
|
|
82
|
+
children: [after && (0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, {
|
|
83
|
+
children: [(search || hasLeftSideElements) && (0, jsx_runtime_1.jsx)(helperComponents_1.Separator, {}), (0, jsx_runtime_1.jsx)("div", {
|
|
84
|
+
"data-test-id": constants_1.TEST_IDS.after,
|
|
85
|
+
className: styles_module_scss_1.default.actions,
|
|
86
|
+
children: after
|
|
87
|
+
})]
|
|
88
|
+
}), (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
89
|
moreActions: moreActions
|
|
78
90
|
})]
|
|
79
91
|
})]
|
|
80
|
-
})
|
|
92
|
+
}), filterRow && (0, jsx_runtime_1.jsx)(chips_1.ChipChoiceRow, Object.assign({}, filterRow, {
|
|
93
|
+
size: 'xs',
|
|
94
|
+
"data-test-id": constants_1.TEST_IDS.filterRow
|
|
95
|
+
}))]
|
|
81
96
|
}));
|
|
82
97
|
}
|
|
@@ -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 && (_jsxs(_Fragment, { children: [_jsx(BulkActions, Object.assign({}, extractBulkActionsProps(rest), { resizingContainerRef: resizingContainerRef })), _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.
|
|
7
|
+
"version": "0.11.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",
|
|
40
|
+
"@snack-uikit/chips": "0.25.0",
|
|
40
41
|
"@snack-uikit/icons": "0.24.2",
|
|
41
|
-
"@snack-uikit/list": "0.24.
|
|
42
|
+
"@snack-uikit/list": "0.24.2",
|
|
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": "70bed6c518009afe4292cb9d12baf08a16700781"
|
|
50
55
|
}
|
|
@@ -2,75 +2,89 @@ 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
|
+
<>
|
|
44
|
+
<BulkActions {...extractBulkActionsProps(rest)} resizingContainerRef={resizingContainerRef} />
|
|
45
|
+
<Separator />
|
|
46
|
+
</>
|
|
47
|
+
)}
|
|
48
|
+
|
|
49
|
+
{onRefresh && (
|
|
50
|
+
<>
|
|
51
|
+
<ButtonFunction
|
|
52
|
+
icon={<UpdateSVG />}
|
|
53
|
+
size='m'
|
|
54
|
+
className={styles.updateButton}
|
|
55
|
+
onClick={onRefresh}
|
|
56
|
+
data-test-id={TEST_IDS.refreshButton}
|
|
57
|
+
/>
|
|
58
|
+
<Separator />
|
|
59
|
+
</>
|
|
60
|
+
)}
|
|
61
|
+
</div>
|
|
62
|
+
)}
|
|
63
|
+
|
|
64
|
+
{search && <SearchPrivate {...search} className={styles.search} size='m' data-test-id={TEST_IDS.search} />}
|
|
34
65
|
|
|
35
|
-
|
|
66
|
+
{(moreActions || after || filterButton) && (
|
|
67
|
+
<div className={styles.flexRow} data-align-right={(!search && !hasLeftSideElements) || undefined}>
|
|
68
|
+
{after && (
|
|
69
|
+
<>
|
|
70
|
+
{(search || hasLeftSideElements) && <Separator />}
|
|
36
71
|
|
|
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
|
-
)}
|
|
72
|
+
<div data-test-id={TEST_IDS.after} className={styles.actions}>
|
|
73
|
+
{after}
|
|
74
|
+
</div>
|
|
75
|
+
</>
|
|
76
|
+
)}
|
|
51
77
|
|
|
52
|
-
|
|
78
|
+
{(moreActions || filterButton) && <Separator />}
|
|
53
79
|
|
|
54
|
-
|
|
55
|
-
<div className={styles.flexRow} data-align-right={(!search && !hasLeftSideElements) || undefined}>
|
|
56
|
-
{after && (
|
|
57
|
-
<>
|
|
58
|
-
{(search || hasLeftSideElements) && <Separator />}
|
|
80
|
+
{filterButton && <FilterButton {...filterButton} />}
|
|
59
81
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
)}
|
|
82
|
+
{moreActions && <MoreActions moreActions={moreActions} />}
|
|
83
|
+
</div>
|
|
84
|
+
)}
|
|
85
|
+
</div>
|
|
65
86
|
|
|
66
|
-
|
|
67
|
-
<>
|
|
68
|
-
<Separator />
|
|
69
|
-
<MoreActions moreActions={moreActions} />
|
|
70
|
-
</>
|
|
71
|
-
)}
|
|
72
|
-
</div>
|
|
73
|
-
)}
|
|
87
|
+
{filterRow && <ChipChoiceRow<TState> {...filterRow} size='xs' data-test-id={TEST_IDS.filterRow} />}
|
|
74
88
|
</div>
|
|
75
89
|
);
|
|
76
90
|
}
|
|
@@ -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
|
}
|