@bitrise/bitkit 12.70.0-alpha-filter-1.1 → 12.70.0-alpha-filter-2.2
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/package.json +30 -30
- package/src/Components/Avatar/Avatar.tsx +4 -1
- package/src/Components/DatePicker/DatePickerFooter.tsx +1 -1
- package/src/Components/DatePicker/DatePickerHeader.tsx +2 -0
- package/src/Components/DatePicker/DatePickerMonth.tsx +8 -1
- package/src/Components/Filter/Filter.storyData.ts +28 -3
- package/src/Components/Filter/Filter.tsx +57 -40
- package/src/Components/Filter/Filter.types.ts +9 -2
- package/src/Components/Filter/Filter.utils.ts +9 -2
- package/src/Components/Filter/FilterAdd/FilterAdd.tsx +23 -14
- package/src/Components/Filter/FilterDate/FilterDate.tsx +7 -8
- package/src/Components/Filter/FilterForm/FilterForm.tsx +4 -1
- package/src/Components/Filter/FilterItem/FilterItem.tsx +14 -17
- package/src/Components/Filter/FilterSearch/FilterSearch.tsx +7 -6
- package/src/Components/Filter/FilterSwitch/FilterSwitch.theme.ts +16 -1
- package/src/Components/Filter/FilterSwitch/FilterSwitch.tsx +2 -2
- package/src/Components/Filter/FilterSwitch/FilterSwitchGroup.tsx +4 -18
- package/src/Components/Filter/FilterSwitchAdapter/FilterSwitchAdapter.tsx +34 -0
- package/src/Components/Form/Checkbox/Checkbox.theme.ts +3 -0
- package/src/Components/Form/Input/Input.tsx +1 -0
- package/src/Components/Form/Radio/Radio.theme.ts +3 -0
- package/src/Components/Form/Textarea/Textarea.tsx +10 -1
- package/src/Components/Icons/16x16/Filter.tsx +3 -4
- package/src/Components/Icons/24x24/Filter.tsx +3 -4
- package/src/Components/Select/Select.tsx +1 -0
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bitrise/bitkit",
|
|
3
3
|
"description": "Bitrise React component library",
|
|
4
|
-
"version": "12.70.0-alpha-filter-
|
|
4
|
+
"version": "12.70.0-alpha-filter-2.2",
|
|
5
5
|
"repository": "git@github.com:bitrise-io/bitkit.git",
|
|
6
6
|
"main": "src/index.ts",
|
|
7
7
|
"license": "UNLICENSED",
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
"@emotion/react": "^11.11.1",
|
|
29
29
|
"@emotion/styled": "^11.11.0",
|
|
30
30
|
"@floating-ui/react-dom-interactions": "^0.8.1",
|
|
31
|
-
"framer-motion": "^10.16.
|
|
31
|
+
"framer-motion": "^10.16.16",
|
|
32
32
|
"luxon": "^3.4.4",
|
|
33
33
|
"react": "^18.2.0",
|
|
34
34
|
"react-dom": "^18.2.0",
|
|
@@ -40,38 +40,38 @@
|
|
|
40
40
|
"react-dom": "^18.2.0"
|
|
41
41
|
},
|
|
42
42
|
"devDependencies": {
|
|
43
|
-
"@babel/core": "^7.23.
|
|
44
|
-
"@babel/preset-env": "^7.23.
|
|
43
|
+
"@babel/core": "^7.23.6",
|
|
44
|
+
"@babel/preset-env": "^7.23.6",
|
|
45
45
|
"@babel/preset-react": "^7.23.3",
|
|
46
46
|
"@babel/preset-typescript": "^7.23.3",
|
|
47
|
-
"@bitrise/eslint-plugin": "^2.3.
|
|
47
|
+
"@bitrise/eslint-plugin": "^2.3.3",
|
|
48
48
|
"@commitlint/cli": "^17.8.0",
|
|
49
49
|
"@commitlint/config-conventional": "^17.8.0",
|
|
50
|
-
"@google-cloud/storage": "^7.
|
|
50
|
+
"@google-cloud/storage": "^7.7.0",
|
|
51
51
|
"@semantic-release/commit-analyzer": "^11.1.0",
|
|
52
52
|
"@semantic-release/git": "^10.0.1",
|
|
53
|
-
"@storybook/addon-actions": "^7.
|
|
54
|
-
"@storybook/addon-essentials": "^7.
|
|
55
|
-
"@storybook/addon-interactions": "^7.
|
|
56
|
-
"@storybook/addon-links": "^7.
|
|
57
|
-
"@storybook/addons": "^7.
|
|
58
|
-
"@storybook/blocks": "^7.
|
|
59
|
-
"@storybook/react": "^7.
|
|
60
|
-
"@storybook/react-webpack5": "^7.
|
|
53
|
+
"@storybook/addon-actions": "^7.6.4",
|
|
54
|
+
"@storybook/addon-essentials": "^7.6.4",
|
|
55
|
+
"@storybook/addon-interactions": "^7.6.4",
|
|
56
|
+
"@storybook/addon-links": "^7.6.4",
|
|
57
|
+
"@storybook/addons": "^7.6.4",
|
|
58
|
+
"@storybook/blocks": "^7.6.4",
|
|
59
|
+
"@storybook/react": "^7.6.4",
|
|
60
|
+
"@storybook/react-webpack5": "^7.6.4",
|
|
61
61
|
"@storybook/testing-library": "^0.2.2",
|
|
62
|
-
"@storybook/theming": "^7.
|
|
62
|
+
"@storybook/theming": "^7.6.4",
|
|
63
63
|
"@testing-library/dom": "^9.3.3",
|
|
64
|
-
"@testing-library/jest-dom": "^6.1.
|
|
65
|
-
"@testing-library/react": "^14.1.
|
|
64
|
+
"@testing-library/jest-dom": "^6.1.5",
|
|
65
|
+
"@testing-library/react": "^14.1.2",
|
|
66
66
|
"@testing-library/user-event": "^14.5.1",
|
|
67
|
-
"@types/jest": "^29.5.
|
|
68
|
-
"@types/luxon": "^3.3.
|
|
69
|
-
"@types/react": "^18.2.
|
|
70
|
-
"@types/react-dom": "^18.2.
|
|
71
|
-
"@typescript-eslint/eslint-plugin": "^6.
|
|
72
|
-
"@typescript-eslint/parser": "^6.
|
|
67
|
+
"@types/jest": "^29.5.11",
|
|
68
|
+
"@types/luxon": "^3.3.7",
|
|
69
|
+
"@types/react": "^18.2.43",
|
|
70
|
+
"@types/react-dom": "^18.2.17",
|
|
71
|
+
"@typescript-eslint/eslint-plugin": "^6.14.0",
|
|
72
|
+
"@typescript-eslint/parser": "^6.14.0",
|
|
73
73
|
"axios": "^1.6.2",
|
|
74
|
-
"eslint": "^8.
|
|
74
|
+
"eslint": "^8.55.0",
|
|
75
75
|
"eslint-plugin-import": "^2.29.0",
|
|
76
76
|
"eslint-plugin-jest": "^27.6.0",
|
|
77
77
|
"eslint-plugin-jsx-a11y": "^6.8.0",
|
|
@@ -79,15 +79,15 @@
|
|
|
79
79
|
"eslint-plugin-react": "^7.33.2",
|
|
80
80
|
"eslint-plugin-react-hooks": "^4.6.0",
|
|
81
81
|
"eslint-plugin-storybook": "^0.6.15",
|
|
82
|
-
"eslint-plugin-testing-library": "^6.
|
|
82
|
+
"eslint-plugin-testing-library": "^6.2.0",
|
|
83
83
|
"glob": "^10.3.10",
|
|
84
84
|
"jest": "^29.7.0",
|
|
85
85
|
"jest-environment-jsdom": "^29.7.0",
|
|
86
|
-
"jsdom": "^
|
|
87
|
-
"prettier": "^3.1.
|
|
88
|
-
"react-hook-form": "^7.
|
|
89
|
-
"semantic-release": "^22.0.
|
|
90
|
-
"storybook": "^7.
|
|
86
|
+
"jsdom": "^23.0.1",
|
|
87
|
+
"prettier": "^3.1.1",
|
|
88
|
+
"react-hook-form": "^7.49.0",
|
|
89
|
+
"semantic-release": "^22.0.12",
|
|
90
|
+
"storybook": "^7.6.4",
|
|
91
91
|
"ts-jest": "^29.1.1",
|
|
92
92
|
"typescript": "^4.8.4"
|
|
93
93
|
},
|
|
@@ -12,7 +12,10 @@ const getInitials = (name: string) => {
|
|
|
12
12
|
if (name.length < 3) {
|
|
13
13
|
return name;
|
|
14
14
|
}
|
|
15
|
-
|
|
15
|
+
|
|
16
|
+
const nameArray = Array.from(name);
|
|
17
|
+
|
|
18
|
+
return `${nameArray[0]}${nameArray[nameArray.length - 1]}`;
|
|
16
19
|
};
|
|
17
20
|
|
|
18
21
|
const Avatar = forwardRef<AvatarProps, 'span'>((props, ref) => (
|
|
@@ -23,7 +23,7 @@ const DatePickerFooter = ({
|
|
|
23
23
|
gap="24"
|
|
24
24
|
>
|
|
25
25
|
{!!onClear && (
|
|
26
|
-
<Button size="small" variant="tertiary" width="fit-content" onClick={() => onClear()}>
|
|
26
|
+
<Button size="small" variant="tertiary" color="purple.10" width="fit-content" onClick={() => onClear()}>
|
|
27
27
|
Clear
|
|
28
28
|
</Button>
|
|
29
29
|
)}
|
|
@@ -21,6 +21,7 @@ export const DatePickerHeaderPrevious = ({ label }: { label: string }) => {
|
|
|
21
21
|
visibility={controls === 'right' ? 'hidden' : undefined}
|
|
22
22
|
aria-label={label}
|
|
23
23
|
variant="tertiary"
|
|
24
|
+
color="purple.10"
|
|
24
25
|
as="button"
|
|
25
26
|
onClick={onPrevious}
|
|
26
27
|
iconName="ChevronLeft"
|
|
@@ -37,6 +38,7 @@ export const DatePickerHeaderNext = ({ label }: { label: string }) => {
|
|
|
37
38
|
size="small"
|
|
38
39
|
aria-label={label}
|
|
39
40
|
variant="tertiary"
|
|
41
|
+
color="purple.10"
|
|
40
42
|
onClick={onNext}
|
|
41
43
|
iconName="ChevronRight"
|
|
42
44
|
isTooltipDisabled
|
|
@@ -50,7 +50,14 @@ const DatePickerMonth = ({ controls, onViewDateChange, viewDate, onMonthClick }:
|
|
|
50
50
|
<DatePickerHeader onPrevious={onPreviousMonth} onNext={onNextMonth} controls={controls}>
|
|
51
51
|
<DatePickerHeaderPrevious label="previous month" />
|
|
52
52
|
<DatePickerHeaderContent id={monthLabelId}>
|
|
53
|
-
<Button
|
|
53
|
+
<Button
|
|
54
|
+
onClick={onMonthClick}
|
|
55
|
+
size="small"
|
|
56
|
+
variant="tertiary"
|
|
57
|
+
color="purple.10"
|
|
58
|
+
flexShrink={0}
|
|
59
|
+
rightIconName="ChevronDown"
|
|
60
|
+
>
|
|
54
61
|
{viewDate.monthLong}
|
|
55
62
|
</Button>
|
|
56
63
|
<NumberInput
|
|
@@ -9,23 +9,26 @@ export const FILTER_STORY_DATA: FilterData = {
|
|
|
9
9
|
date_range: {
|
|
10
10
|
categoryName: 'Date',
|
|
11
11
|
categoryNamePlural: 'dates',
|
|
12
|
-
|
|
12
|
+
type: 'dateRange',
|
|
13
13
|
},
|
|
14
14
|
pipeline: {
|
|
15
15
|
categoryName: 'Pipeline',
|
|
16
16
|
categoryNamePlural: 'Pipelines',
|
|
17
17
|
options: FILTER_STORY_OPTIONS,
|
|
18
|
+
type: 'tag',
|
|
18
19
|
},
|
|
19
20
|
stage: {
|
|
20
21
|
categoryName: 'Stage',
|
|
21
22
|
categoryNamePlural: 'Stages',
|
|
22
23
|
options: FILTER_STORY_OPTIONS,
|
|
23
|
-
dependsOn:
|
|
24
|
+
dependsOn: { pipeline: 'Select a pipeline first' },
|
|
25
|
+
type: 'tag',
|
|
24
26
|
},
|
|
25
27
|
workflow: {
|
|
26
28
|
categoryName: 'Workflow',
|
|
27
29
|
isMultiple: true,
|
|
28
30
|
options: FILTER_STORY_OPTIONS,
|
|
31
|
+
type: 'tag',
|
|
29
32
|
},
|
|
30
33
|
branch: {
|
|
31
34
|
options: [
|
|
@@ -34,16 +37,22 @@ export const FILTER_STORY_DATA: FilterData = {
|
|
|
34
37
|
'master',
|
|
35
38
|
'CI-2264-consolidate-other-provider-type-to-custom',
|
|
36
39
|
],
|
|
40
|
+
type: 'tag',
|
|
37
41
|
},
|
|
38
42
|
app: {
|
|
39
43
|
categoryName: 'App',
|
|
40
|
-
|
|
44
|
+
iconsMap: {
|
|
45
|
+
'46b6b9a78a418ee8': 'AppleFill',
|
|
46
|
+
'32b14416be4b7b24': 'Android',
|
|
47
|
+
'0a248b278e135ea7': 'Other',
|
|
48
|
+
},
|
|
41
49
|
options: ['46b6b9a78a418ee8', '32b14416be4b7b24', '0a248b278e135ea7'],
|
|
42
50
|
optionsMap: {
|
|
43
51
|
'46b6b9a78a418ee8': 'bitrise-website',
|
|
44
52
|
'32b14416be4b7b24': 'bitkit',
|
|
45
53
|
'0a248b278e135ea7': 'pipeline-service',
|
|
46
54
|
},
|
|
55
|
+
type: 'select',
|
|
47
56
|
},
|
|
48
57
|
test_case: {
|
|
49
58
|
categoryName: 'Test case',
|
|
@@ -56,16 +65,32 @@ export const FILTER_STORY_DATA: FilterData = {
|
|
|
56
65
|
});
|
|
57
66
|
},
|
|
58
67
|
options: ['default 1', 'default 2', 'default 3'],
|
|
68
|
+
type: 'tag',
|
|
69
|
+
},
|
|
70
|
+
cache_type: {
|
|
71
|
+
iconsMap: {
|
|
72
|
+
bazel: 'Bazel',
|
|
73
|
+
gradle: 'Gradle',
|
|
74
|
+
},
|
|
75
|
+
options: ['gradle', 'bazel'],
|
|
76
|
+
optionsMap: {
|
|
77
|
+
bazel: 'Bazel',
|
|
78
|
+
gradle: 'Gradle',
|
|
79
|
+
},
|
|
80
|
+
type: 'switch',
|
|
59
81
|
},
|
|
60
82
|
};
|
|
61
83
|
|
|
62
84
|
export const FILTER_STORY_INIT_STATE: FilterState = {
|
|
63
85
|
pipeline: ['ipsum'],
|
|
64
86
|
app: ['46b6b9a78a418ee8'],
|
|
87
|
+
cache_type: ['gradle'],
|
|
65
88
|
};
|
|
66
89
|
|
|
67
90
|
export const FILTER_STORY_CONTEXT: FilterContextType = {
|
|
68
91
|
data: FILTER_STORY_DATA,
|
|
69
92
|
setPopoverOpen: () => {},
|
|
70
93
|
state: FILTER_STORY_INIT_STATE,
|
|
94
|
+
onFilterChange: () => {},
|
|
95
|
+
onFilterClear: () => {},
|
|
71
96
|
};
|
|
@@ -6,29 +6,47 @@ import Divider from '../Divider/Divider';
|
|
|
6
6
|
import Icon from '../Icon/Icon';
|
|
7
7
|
import { FilterContext } from './Filter.context';
|
|
8
8
|
import { FilterStyle } from './Filter.theme';
|
|
9
|
-
import {
|
|
10
|
-
|
|
9
|
+
import {
|
|
10
|
+
FilterCategoryProps,
|
|
11
|
+
FilterContextType,
|
|
12
|
+
FilterData,
|
|
13
|
+
FilterState,
|
|
14
|
+
FilterType,
|
|
15
|
+
FilterValue,
|
|
16
|
+
} from './Filter.types';
|
|
17
|
+
import { getDependents } from './Filter.utils';
|
|
11
18
|
import FilterAdd from './FilterAdd/FilterAdd';
|
|
12
19
|
import FilterItem from './FilterItem/FilterItem';
|
|
13
20
|
import FilterSearch from './FilterSearch/FilterSearch';
|
|
14
21
|
import FilterDate from './FilterDate/FilterDate';
|
|
22
|
+
import FilterSwitchAdapter from './FilterSwitchAdapter/FilterSwitchAdapter';
|
|
15
23
|
|
|
16
24
|
export interface FilterProps extends Omit<BoxProps, 'onChange'> {
|
|
17
25
|
filtersDependOn?: string[];
|
|
18
26
|
initialData: FilterData;
|
|
27
|
+
initialState: FilterState;
|
|
19
28
|
isLoading?: boolean;
|
|
20
29
|
onChange: (state: FilterState) => void;
|
|
21
30
|
showSearch?: boolean;
|
|
22
|
-
state: FilterState;
|
|
23
31
|
}
|
|
24
32
|
|
|
25
33
|
const Filter = (props: FilterProps) => {
|
|
26
|
-
const { filtersDependOn, initialData, isLoading, onChange, showSearch,
|
|
34
|
+
const { filtersDependOn, initialData, initialState, isLoading, onChange, showSearch, ...rest } = props;
|
|
27
35
|
|
|
28
36
|
const isInited = useRef<boolean>(false);
|
|
29
37
|
|
|
30
38
|
const filterStyle = useMultiStyleConfig('Filter') as FilterStyle;
|
|
31
39
|
|
|
40
|
+
const state: FilterState = {};
|
|
41
|
+
Object.entries(initialState).forEach(([category, values]) => {
|
|
42
|
+
if (values?.length) {
|
|
43
|
+
const cleanValues = values.filter((v) => v !== null && v !== '' && v !== undefined);
|
|
44
|
+
if (cleanValues.length) {
|
|
45
|
+
state[category] = cleanValues;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
|
|
32
50
|
const [data] = useState<FilterData>(initialData);
|
|
33
51
|
const [isPopoverOpen, setPopoverOpen] = useState<boolean>(false);
|
|
34
52
|
|
|
@@ -58,7 +76,7 @@ const Filter = (props: FilterProps) => {
|
|
|
58
76
|
onChange(newState);
|
|
59
77
|
};
|
|
60
78
|
|
|
61
|
-
const
|
|
79
|
+
const onFilterClear = (category: string) => {
|
|
62
80
|
onChange(deleteFromState(category, state));
|
|
63
81
|
};
|
|
64
82
|
|
|
@@ -66,14 +84,19 @@ const Filter = (props: FilterProps) => {
|
|
|
66
84
|
onChange({});
|
|
67
85
|
};
|
|
68
86
|
|
|
69
|
-
const
|
|
70
|
-
|
|
71
|
-
|
|
87
|
+
const filters = {
|
|
88
|
+
search: {},
|
|
89
|
+
select: {},
|
|
90
|
+
switch: {},
|
|
91
|
+
dateRange: {},
|
|
92
|
+
tag: {},
|
|
93
|
+
} as Record<FilterType, Record<string, FilterCategoryProps>>;
|
|
72
94
|
|
|
73
|
-
|
|
74
|
-
|
|
95
|
+
Object.entries(data).forEach(([category, value]) => {
|
|
96
|
+
filters[value.type || 'tag'][category] = value;
|
|
97
|
+
});
|
|
75
98
|
|
|
76
|
-
const
|
|
99
|
+
const stateCategories = Object.keys(state).filter((c) => !['date_range', 'search'].includes(c));
|
|
77
100
|
|
|
78
101
|
const showClearFilters = stateCategories.length > 0 || (state.search && state.search.length > 0);
|
|
79
102
|
|
|
@@ -82,10 +105,12 @@ const Filter = (props: FilterProps) => {
|
|
|
82
105
|
data: initialData,
|
|
83
106
|
filtersDependOn,
|
|
84
107
|
isLoading,
|
|
108
|
+
onFilterChange,
|
|
109
|
+
onFilterClear,
|
|
85
110
|
setPopoverOpen,
|
|
86
111
|
state,
|
|
87
112
|
}),
|
|
88
|
-
[filtersDependOn, isLoading, initialData, setPopoverOpen, state],
|
|
113
|
+
[filtersDependOn, isLoading, initialData, onFilterChange, onFilterClear, setPopoverOpen, state],
|
|
89
114
|
);
|
|
90
115
|
|
|
91
116
|
useEffect(() => {
|
|
@@ -102,27 +127,25 @@ const Filter = (props: FilterProps) => {
|
|
|
102
127
|
</Modal>
|
|
103
128
|
<Box sx={filterStyle.content}>
|
|
104
129
|
<Icon name="Filter" sx={filterStyle.icon} />
|
|
105
|
-
|
|
106
|
-
{
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
return null;
|
|
125
|
-
})}
|
|
130
|
+
|
|
131
|
+
{Object.keys(filters.switch).map((category) => (
|
|
132
|
+
<FilterSwitchAdapter category={category} key={category} />
|
|
133
|
+
))}
|
|
134
|
+
|
|
135
|
+
{Object.keys(filters.dateRange).map((category) => (
|
|
136
|
+
<FilterDate category={category} key={category} />
|
|
137
|
+
))}
|
|
138
|
+
|
|
139
|
+
{Object.keys(filters.select).map((category) => (
|
|
140
|
+
<FilterItem category={category} key={category} />
|
|
141
|
+
))}
|
|
142
|
+
{Object.keys(filters.tag).map((category) => {
|
|
143
|
+
if (!state[category]) {
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
return <FilterItem category={category} key={category} />;
|
|
147
|
+
})}
|
|
148
|
+
|
|
126
149
|
<FilterAdd onChange={onFilterChange} />
|
|
127
150
|
</Box>
|
|
128
151
|
{(showClearFilters || showSearch) && (
|
|
@@ -142,13 +165,7 @@ const Filter = (props: FilterProps) => {
|
|
|
142
165
|
{showClearFilters && showSearch && (
|
|
143
166
|
<Divider orientation="vertical" size="1" variant="solid" flexShrink="0" />
|
|
144
167
|
)}
|
|
145
|
-
{showSearch && (
|
|
146
|
-
<FilterSearch
|
|
147
|
-
onChange={onFilterChange}
|
|
148
|
-
onClear={onClear}
|
|
149
|
-
value={(state.Search && state.Search[0]) || ''}
|
|
150
|
-
/>
|
|
151
|
-
)}
|
|
168
|
+
{showSearch && <FilterSearch onChange={onFilterChange} value={(state.Search && state.Search[0]) || ''} />}
|
|
152
169
|
</Box>
|
|
153
170
|
)}
|
|
154
171
|
</Box>
|
|
@@ -1,20 +1,25 @@
|
|
|
1
1
|
import { Dispatch, SetStateAction } from 'react';
|
|
2
|
+
import { TypeIconName } from '../Icon/Icon';
|
|
3
|
+
|
|
4
|
+
export type FilterType = 'dateRange' | 'search' | 'select' | 'switch' | 'tag';
|
|
2
5
|
|
|
3
6
|
export type FilterOptions = string[];
|
|
4
7
|
export type FilterValue = string[];
|
|
5
8
|
export type FilterOptionsMap = Record<string, string>;
|
|
9
|
+
export type FilterIconsMap = Record<string, TypeIconName>;
|
|
6
10
|
|
|
7
11
|
export type FilterSearchCallback = (category: string, q: string) => Promise<FilterValue>;
|
|
8
12
|
|
|
9
13
|
export type FilterCategoryProps = {
|
|
10
14
|
categoryName?: string;
|
|
11
15
|
categoryNamePlural?: string;
|
|
12
|
-
dependsOn?: string
|
|
16
|
+
dependsOn?: Record<string, string>;
|
|
17
|
+
iconsMap?: FilterIconsMap;
|
|
13
18
|
isMultiple?: boolean;
|
|
14
|
-
isPermanent?: boolean;
|
|
15
19
|
onAsyncSearch?: FilterSearchCallback;
|
|
16
20
|
options?: FilterOptions;
|
|
17
21
|
optionsMap?: FilterOptionsMap;
|
|
22
|
+
type?: FilterType;
|
|
18
23
|
};
|
|
19
24
|
|
|
20
25
|
export type FilterData = Record<string, FilterCategoryProps>;
|
|
@@ -24,6 +29,8 @@ export interface FilterContextType {
|
|
|
24
29
|
data: FilterData;
|
|
25
30
|
filtersDependOn?: string[];
|
|
26
31
|
isLoading?: boolean;
|
|
32
|
+
onFilterClear: (category: string) => void;
|
|
33
|
+
onFilterChange: (category: string, value: FilterValue) => void;
|
|
27
34
|
setPopoverOpen: Dispatch<SetStateAction<boolean>>;
|
|
28
35
|
state: FilterState;
|
|
29
36
|
}
|
|
@@ -7,17 +7,24 @@ export const hasAllDependencies = (stateKeys: string[], dependsOn?: string[]): b
|
|
|
7
7
|
return dependsOn.every((key) => stateKeys.includes(key));
|
|
8
8
|
};
|
|
9
9
|
|
|
10
|
+
export const getMissingDependencies = (stateKeys: string[], dependsOn?: string[]): string[] => {
|
|
11
|
+
if (!dependsOn || dependsOn.length === 0) {
|
|
12
|
+
return [];
|
|
13
|
+
}
|
|
14
|
+
return dependsOn.filter((category) => !stateKeys.includes(category));
|
|
15
|
+
};
|
|
16
|
+
|
|
10
17
|
export const getDependents = (data: FilterData, categoryKey: string, filtersDependOn?: string[]): string[] => {
|
|
11
18
|
const dependents: string[] = [];
|
|
12
19
|
if (filtersDependOn && filtersDependOn.includes(categoryKey)) {
|
|
13
20
|
Object.keys(data).forEach((category) => {
|
|
14
|
-
if (
|
|
21
|
+
if (data[category].type !== 'select') {
|
|
15
22
|
dependents.push(category);
|
|
16
23
|
}
|
|
17
24
|
});
|
|
18
25
|
} else {
|
|
19
26
|
Object.keys(data).forEach((category) => {
|
|
20
|
-
if (data[category].dependsOn
|
|
27
|
+
if (Object.keys(data[category].dependsOn || {}).includes(categoryKey)) {
|
|
21
28
|
dependents.push(category);
|
|
22
29
|
}
|
|
23
30
|
});
|
|
@@ -2,10 +2,11 @@ import { useState } from 'react';
|
|
|
2
2
|
import { Menu, MenuButton, MenuList, useDisclosure } from '@chakra-ui/react';
|
|
3
3
|
import Button from '../../Button/Button';
|
|
4
4
|
import MenuItem from '../../Menu/MenuItem';
|
|
5
|
+
import Tooltip from '../../Tooltip/Tooltip';
|
|
5
6
|
import { useFilterContext } from '../Filter.context';
|
|
6
7
|
import FilterForm from '../FilterForm/FilterForm';
|
|
7
8
|
import { FilterValue } from '../Filter.types';
|
|
8
|
-
import { hasAllDependencies } from '../Filter.utils';
|
|
9
|
+
import { getMissingDependencies, hasAllDependencies } from '../Filter.utils';
|
|
9
10
|
|
|
10
11
|
export interface FilterAddProps {
|
|
11
12
|
onChange: (category: string, selected: FilterValue) => void;
|
|
@@ -42,7 +43,11 @@ const FilterAdd = (props: FilterAddProps) => {
|
|
|
42
43
|
|
|
43
44
|
const stateKeys = Object.keys(state);
|
|
44
45
|
|
|
45
|
-
const
|
|
46
|
+
const categoryList = Object.keys(data).filter((category) => {
|
|
47
|
+
return !stateKeys.includes(category) && data[category].type === 'tag';
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
const isDisabled = !hasAllDependencies(stateKeys, filtersDependOn) || categoryList.length === 0;
|
|
46
51
|
|
|
47
52
|
return (
|
|
48
53
|
<Menu closeOnSelect={false} isOpen={isOpen} onClose={onClose} onOpen={onOpen}>
|
|
@@ -66,21 +71,25 @@ const FilterAdd = (props: FilterAddProps) => {
|
|
|
66
71
|
{selectedCategory ? (
|
|
67
72
|
<FilterForm category={selectedCategory} onChange={onFilterChange} onCancel={onClose} />
|
|
68
73
|
) : (
|
|
69
|
-
|
|
70
|
-
const { categoryName, dependsOn
|
|
71
|
-
|
|
72
|
-
return null;
|
|
73
|
-
}
|
|
74
|
+
categoryList.map((category) => {
|
|
75
|
+
const { categoryName, dependsOn } = data[category];
|
|
76
|
+
const missingDependencies = getMissingDependencies(stateKeys, Object.keys(dependsOn || []));
|
|
74
77
|
return (
|
|
75
|
-
<
|
|
76
|
-
isDisabled={
|
|
78
|
+
<Tooltip
|
|
79
|
+
isDisabled={missingDependencies.length === 0}
|
|
80
|
+
label={dependsOn?.[missingDependencies[0]]}
|
|
81
|
+
placement="right"
|
|
77
82
|
key={category}
|
|
78
|
-
onClick={() => onCategorySelect(category)}
|
|
79
|
-
pointerEvents="all"
|
|
80
|
-
rightIconName="ChevronRight"
|
|
81
83
|
>
|
|
82
|
-
|
|
83
|
-
|
|
84
|
+
<MenuItem
|
|
85
|
+
isDisabled={missingDependencies.length > 0}
|
|
86
|
+
onClick={() => onCategorySelect(category)}
|
|
87
|
+
pointerEvents="all"
|
|
88
|
+
rightIconName="ChevronRight"
|
|
89
|
+
>
|
|
90
|
+
{categoryName || category}
|
|
91
|
+
</MenuItem>
|
|
92
|
+
</Tooltip>
|
|
84
93
|
);
|
|
85
94
|
})
|
|
86
95
|
)}
|
|
@@ -9,30 +9,29 @@ import Text from '../../Text/Text';
|
|
|
9
9
|
import Tooltip from '../../Tooltip/Tooltip';
|
|
10
10
|
import { useFilterContext } from '../Filter.context';
|
|
11
11
|
import { FilterStyle } from '../Filter.theme';
|
|
12
|
-
import { FilterValue } from '../Filter.types';
|
|
13
12
|
|
|
14
13
|
export type FilterDateProps = {
|
|
15
|
-
|
|
16
|
-
onClear: (category: string) => void;
|
|
17
|
-
value: FilterValue;
|
|
14
|
+
category: string;
|
|
18
15
|
};
|
|
19
16
|
|
|
20
17
|
const FilterDate = (props: FilterDateProps) => {
|
|
21
|
-
const {
|
|
18
|
+
const { category } = props;
|
|
22
19
|
const filterStyle = useMultiStyleConfig('Filter') as FilterStyle;
|
|
23
20
|
|
|
24
|
-
const { isLoading, setPopoverOpen } = useFilterContext();
|
|
21
|
+
const { isLoading, onFilterChange, onFilterClear, setPopoverOpen, state } = useFilterContext();
|
|
25
22
|
|
|
26
23
|
const { isOpen, onClose, onToggle } = useDisclosure();
|
|
27
24
|
|
|
25
|
+
const value = state[category];
|
|
26
|
+
|
|
28
27
|
const onDateRangeApply = (range: DateRange) => {
|
|
29
28
|
if (range.from && range.to) {
|
|
30
|
-
|
|
29
|
+
onFilterChange('date_range', [String(range.from.toMillis()), String(range.to.toMillis())]);
|
|
31
30
|
}
|
|
32
31
|
};
|
|
33
32
|
|
|
34
33
|
const onClearClick = () => {
|
|
35
|
-
|
|
34
|
+
onFilterClear('date_range');
|
|
36
35
|
onClose();
|
|
37
36
|
};
|
|
38
37
|
|
|
@@ -6,6 +6,7 @@ import Button from '../../Button/Button';
|
|
|
6
6
|
import ButtonGroup from '../../ButtonGroup/ButtonGroup';
|
|
7
7
|
import Checkbox from '../../Form/Checkbox/Checkbox';
|
|
8
8
|
import CheckboxGroup from '../../Form/Checkbox/CheckboxGroup';
|
|
9
|
+
import Icon from '../../Icon/Icon';
|
|
9
10
|
import Radio from '../../Form/Radio/Radio';
|
|
10
11
|
import RadioGroup from '../../Form/Radio/RadioGroup';
|
|
11
12
|
import SearchInput from '../../SearchInput/SearchInput';
|
|
@@ -29,7 +30,7 @@ const FilterForm = (props: FilterFormProps) => {
|
|
|
29
30
|
const filterStyle = useMultiStyleConfig('Filter') as FilterStyle;
|
|
30
31
|
|
|
31
32
|
const { data, state } = useFilterContext();
|
|
32
|
-
const { isMultiple, onAsyncSearch, options, optionsMap } = data[category];
|
|
33
|
+
const { iconsMap, isMultiple, onAsyncSearch, options, optionsMap } = data[category];
|
|
33
34
|
const value = state[category] || [];
|
|
34
35
|
|
|
35
36
|
const [selected, setSelected] = useState<FilterValue>(value);
|
|
@@ -129,6 +130,7 @@ const FilterForm = (props: FilterFormProps) => {
|
|
|
129
130
|
{items.length
|
|
130
131
|
? items.map((opt) => (
|
|
131
132
|
<Checkbox key={opt} value={opt}>
|
|
133
|
+
{iconsMap && iconsMap[opt] && <Icon name={iconsMap[opt]} />}
|
|
132
134
|
{getOptionLabel(opt, optionsMap)}
|
|
133
135
|
</Checkbox>
|
|
134
136
|
))
|
|
@@ -145,6 +147,7 @@ const FilterForm = (props: FilterFormProps) => {
|
|
|
145
147
|
{items.length
|
|
146
148
|
? items.map((opt) => (
|
|
147
149
|
<Radio key={opt} value={opt}>
|
|
150
|
+
{iconsMap && iconsMap[opt] && <Icon name={iconsMap[opt]} />}
|
|
148
151
|
{getOptionLabel(opt, optionsMap)}
|
|
149
152
|
</Radio>
|
|
150
153
|
))
|
|
@@ -9,24 +9,23 @@ import PopoverTrigger from '../../Popover/PopoverTrigger';
|
|
|
9
9
|
import Text from '../../Text/Text';
|
|
10
10
|
import Tooltip from '../../Tooltip/Tooltip';
|
|
11
11
|
import { FilterStyle } from '../Filter.theme';
|
|
12
|
-
import {
|
|
12
|
+
import { FilterValue } from '../Filter.types';
|
|
13
13
|
import FilterForm from '../FilterForm/FilterForm';
|
|
14
14
|
import { useFilterContext } from '../Filter.context';
|
|
15
15
|
import { getOptionLabel } from '../Filter.utils';
|
|
16
16
|
|
|
17
17
|
export type FilterItemProps = {
|
|
18
18
|
category: string;
|
|
19
|
-
categoryName?: string;
|
|
20
|
-
categoryNamePlural?: string;
|
|
21
|
-
isPermanent?: boolean;
|
|
22
|
-
onChange: (category: string, value: FilterValue) => void;
|
|
23
|
-
onClear: (category: string) => void;
|
|
24
|
-
optionsMap?: FilterOptionsMap;
|
|
25
|
-
value: FilterValue;
|
|
26
19
|
};
|
|
27
20
|
|
|
28
21
|
const FilterItem = (props: FilterItemProps) => {
|
|
29
|
-
const { category
|
|
22
|
+
const { category } = props;
|
|
23
|
+
|
|
24
|
+
const { data, isLoading, onFilterChange, onFilterClear, setPopoverOpen, state } = useFilterContext();
|
|
25
|
+
|
|
26
|
+
const { categoryName, categoryNamePlural, optionsMap, type } = data[category];
|
|
27
|
+
|
|
28
|
+
const value = state[category];
|
|
30
29
|
|
|
31
30
|
const pluralCategoryString = (categoryNamePlural || `${category}s`).toLowerCase();
|
|
32
31
|
|
|
@@ -34,8 +33,6 @@ const FilterItem = (props: FilterItemProps) => {
|
|
|
34
33
|
|
|
35
34
|
const filterStyle = useMultiStyleConfig('Filter') as FilterStyle;
|
|
36
35
|
|
|
37
|
-
const { isLoading, setPopoverOpen } = useFilterContext();
|
|
38
|
-
|
|
39
36
|
const onToggle = () => {
|
|
40
37
|
togglePopover();
|
|
41
38
|
};
|
|
@@ -45,9 +42,9 @@ const FilterItem = (props: FilterItemProps) => {
|
|
|
45
42
|
setPopoverOpen(false);
|
|
46
43
|
};
|
|
47
44
|
|
|
48
|
-
const
|
|
45
|
+
const onChange = (newCategory: string, newValue: FilterValue) => {
|
|
49
46
|
onClose();
|
|
50
|
-
|
|
47
|
+
onFilterChange(newCategory, newValue);
|
|
51
48
|
};
|
|
52
49
|
|
|
53
50
|
const getText = () => {
|
|
@@ -73,15 +70,15 @@ const FilterItem = (props: FilterItemProps) => {
|
|
|
73
70
|
<Tooltip isDisabled={isLoading} label="Edit">
|
|
74
71
|
<Text as="button" disabled={isLoading} onClick={onToggle} size="2" sx={filterStyle.tagEdit}>
|
|
75
72
|
{getText()}
|
|
76
|
-
{
|
|
73
|
+
{type === 'select' && <Icon name="ChevronDown" size="16" />}
|
|
77
74
|
</Text>
|
|
78
75
|
</Tooltip>
|
|
79
|
-
{
|
|
76
|
+
{type !== 'select' && (
|
|
80
77
|
<IconButton
|
|
81
78
|
aria-label={isLoading ? '' : 'Clear'}
|
|
82
79
|
iconName="CloseSmall"
|
|
83
80
|
isDisabled={isLoading}
|
|
84
|
-
onClick={() =>
|
|
81
|
+
onClick={() => onFilterClear(category)}
|
|
85
82
|
size="small"
|
|
86
83
|
variant="tertiary"
|
|
87
84
|
sx={filterStyle.tagClear}
|
|
@@ -91,7 +88,7 @@ const FilterItem = (props: FilterItemProps) => {
|
|
|
91
88
|
</Box>
|
|
92
89
|
</PopoverTrigger>
|
|
93
90
|
<PopoverContent>
|
|
94
|
-
<FilterForm category={category} categoryName={categoryName} onChange={
|
|
91
|
+
<FilterForm category={category} categoryName={categoryName} onChange={onChange} onCancel={onClose} />
|
|
95
92
|
</PopoverContent>
|
|
96
93
|
</Popover>
|
|
97
94
|
);
|
|
@@ -15,12 +15,11 @@ import { FilterValue } from '../Filter.types';
|
|
|
15
15
|
|
|
16
16
|
export interface FilterSearchProps extends Omit<InputProps, 'onChange' | 'value'> {
|
|
17
17
|
onChange: (category: string, selected: FilterValue) => void;
|
|
18
|
-
onClear: (category: string) => void;
|
|
19
18
|
value: string;
|
|
20
19
|
}
|
|
21
20
|
|
|
22
21
|
const FilterSearch = (props: FilterSearchProps) => {
|
|
23
|
-
const { onChange,
|
|
22
|
+
const { onChange, value, ...rest } = props;
|
|
24
23
|
const filterStyle = useMultiStyleConfig('Filter') as FilterStyle;
|
|
25
24
|
|
|
26
25
|
const [searchValue, setSearchValue] = useState(value);
|
|
@@ -31,7 +30,7 @@ const FilterSearch = (props: FilterSearchProps) => {
|
|
|
31
30
|
};
|
|
32
31
|
|
|
33
32
|
const onClearClick = () => {
|
|
34
|
-
|
|
33
|
+
setSearchValue('');
|
|
35
34
|
};
|
|
36
35
|
|
|
37
36
|
useEffect(() => {
|
|
@@ -55,9 +54,11 @@ const FilterSearch = (props: FilterSearchProps) => {
|
|
|
55
54
|
<Icon color="neutral.60" name="Magnifier" size="16" />
|
|
56
55
|
</InputLeftElement>
|
|
57
56
|
<Input {...inputProps} />
|
|
58
|
-
|
|
59
|
-
<
|
|
60
|
-
|
|
57
|
+
{!!searchValue && (
|
|
58
|
+
<InputRightElement>
|
|
59
|
+
<IconButton aria-label="Clear" iconName="CloseSmall" onClick={onClearClick} size="small" variant="tertiary" />
|
|
60
|
+
</InputRightElement>
|
|
61
|
+
)}
|
|
61
62
|
</InputGroup>
|
|
62
63
|
);
|
|
63
64
|
};
|
|
@@ -6,9 +6,12 @@ const FilterSwitch = {
|
|
|
6
6
|
container: {
|
|
7
7
|
color: 'neutral.40',
|
|
8
8
|
background: 'neutral.95',
|
|
9
|
-
borderRadius: '4',
|
|
10
9
|
border: '1px solid',
|
|
11
10
|
borderColor: 'neutral.80',
|
|
11
|
+
display: 'flex',
|
|
12
|
+
borderRadius: '4',
|
|
13
|
+
},
|
|
14
|
+
item: {
|
|
12
15
|
cursor: 'pointer',
|
|
13
16
|
position: 'relative',
|
|
14
17
|
textOverflow: 'ellipsis',
|
|
@@ -19,6 +22,18 @@ const FilterSwitch = {
|
|
|
19
22
|
zIndex: 0,
|
|
20
23
|
fontSize: rem(14),
|
|
21
24
|
lineHeight: rem(20),
|
|
25
|
+
display: 'flex',
|
|
26
|
+
alignItems: 'center',
|
|
27
|
+
gap: '4',
|
|
28
|
+
borderLeft: '1px solid',
|
|
29
|
+
borderLeftColor: 'neutral.80',
|
|
30
|
+
_first: {
|
|
31
|
+
borderLeftRadius: '4',
|
|
32
|
+
borderLeft: 'none',
|
|
33
|
+
},
|
|
34
|
+
_last: {
|
|
35
|
+
borderRightRadius: '4',
|
|
36
|
+
},
|
|
22
37
|
_focusVisible: {
|
|
23
38
|
boxShadow: 'outline',
|
|
24
39
|
zIndex: 1,
|
|
@@ -20,7 +20,7 @@ const FilterSwitch = forwardRef<FilterSwitchProps, 'input'>((props, ref) => {
|
|
|
20
20
|
const group = useRadioGroupContext();
|
|
21
21
|
const { value: valueProp } = props;
|
|
22
22
|
|
|
23
|
-
const styles = useMultiStyleConfig('FilterSwitch'
|
|
23
|
+
const styles = useMultiStyleConfig('FilterSwitch');
|
|
24
24
|
|
|
25
25
|
const ownProps = omitThemingProps(props);
|
|
26
26
|
|
|
@@ -46,7 +46,7 @@ const FilterSwitch = forwardRef<FilterSwitchProps, 'input'>((props, ref) => {
|
|
|
46
46
|
});
|
|
47
47
|
|
|
48
48
|
return (
|
|
49
|
-
<chakra.label {...getLabelProps()} {...getCheckboxProps()} __css={styles.
|
|
49
|
+
<chakra.label {...getLabelProps()} {...getCheckboxProps()} __css={styles.item}>
|
|
50
50
|
<chakra.input {...getInputProps(htmlInputProps, ref)} />
|
|
51
51
|
{children}
|
|
52
52
|
</chakra.label>
|
|
@@ -1,25 +1,11 @@
|
|
|
1
|
-
import { RadioGroup as ChakraRadioGroup, RadioGroupProps } from '@chakra-ui/react';
|
|
1
|
+
import { RadioGroup as ChakraRadioGroup, RadioGroupProps, useMultiStyleConfig } from '@chakra-ui/react';
|
|
2
2
|
|
|
3
3
|
export type { RadioGroupProps };
|
|
4
4
|
|
|
5
5
|
const FilterSwitchGroup = (props: RadioGroupProps) => {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
{...props}
|
|
10
|
-
sx={{
|
|
11
|
-
label: {
|
|
12
|
-
_notFirst: {
|
|
13
|
-
borderLeftRadius: 0,
|
|
14
|
-
borderLeft: 'none',
|
|
15
|
-
},
|
|
16
|
-
_notLast: {
|
|
17
|
-
borderRightRadius: 0,
|
|
18
|
-
},
|
|
19
|
-
},
|
|
20
|
-
}}
|
|
21
|
-
/>
|
|
22
|
-
);
|
|
6
|
+
const { container } = useMultiStyleConfig('FilterSwitch');
|
|
7
|
+
|
|
8
|
+
return <ChakraRadioGroup sx={container} {...props} />;
|
|
23
9
|
};
|
|
24
10
|
|
|
25
11
|
export default FilterSwitchGroup;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { useFilterContext } from '../Filter.context';
|
|
2
|
+
import { getOptionLabel } from '../Filter.utils';
|
|
3
|
+
import Icon from '../../Icon/Icon';
|
|
4
|
+
import FilterSwitch from '../FilterSwitch/FilterSwitch';
|
|
5
|
+
import FilterSwitchGroup from '../FilterSwitch/FilterSwitchGroup';
|
|
6
|
+
|
|
7
|
+
type FilterSwitchAdapterProps = {
|
|
8
|
+
category: string;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
const FilterSwitchAdapter = (props: FilterSwitchAdapterProps) => {
|
|
12
|
+
const { category } = props;
|
|
13
|
+
const { data, onFilterChange, state } = useFilterContext();
|
|
14
|
+
const { iconsMap, options, optionsMap } = data[category];
|
|
15
|
+
|
|
16
|
+
if (!options?.length) {
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const value = state[category]?.[0] || '';
|
|
21
|
+
|
|
22
|
+
return (
|
|
23
|
+
<FilterSwitchGroup onChange={(newValue) => onFilterChange(category, [newValue])} value={value}>
|
|
24
|
+
{options.map((opt) => (
|
|
25
|
+
<FilterSwitch key={opt} value={opt}>
|
|
26
|
+
{iconsMap && iconsMap[opt] && <Icon name={iconsMap[opt]} size="16" />}
|
|
27
|
+
{getOptionLabel(opt, optionsMap)}
|
|
28
|
+
</FilterSwitch>
|
|
29
|
+
))}
|
|
30
|
+
</FilterSwitchGroup>
|
|
31
|
+
);
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export default FilterSwitchAdapter;
|
|
@@ -17,7 +17,16 @@ import Tooltip, { TooltipProps } from '../../Tooltip/Tooltip';
|
|
|
17
17
|
type UsedFormControlProps = Omit<FormControlProps, 'label' | 'onBlur' | 'onChange'>;
|
|
18
18
|
type UsedChakraTextProps = Pick<
|
|
19
19
|
ChakraTextareaProps,
|
|
20
|
-
|
|
20
|
+
| 'onBlur'
|
|
21
|
+
| 'onChange'
|
|
22
|
+
| 'role'
|
|
23
|
+
| 'name'
|
|
24
|
+
| 'value'
|
|
25
|
+
| 'autoComplete'
|
|
26
|
+
| 'autoFocus'
|
|
27
|
+
| 'maxLength'
|
|
28
|
+
| 'minLength'
|
|
29
|
+
| 'placeholder'
|
|
21
30
|
>;
|
|
22
31
|
|
|
23
32
|
export interface TextareaProps extends UsedFormControlProps, UsedChakraTextProps {
|
|
@@ -2,10 +2,9 @@ import { Icon, IconProps, forwardRef } from '@chakra-ui/react';
|
|
|
2
2
|
|
|
3
3
|
const Filter = forwardRef<IconProps, 'svg'>((props, ref) => (
|
|
4
4
|
<Icon ref={ref} viewBox="0 0 16 16" {...props}>
|
|
5
|
-
<path
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
/>
|
|
5
|
+
<path d="M2 4.5H14V6H2V4.5Z" fill="currentColor" />
|
|
6
|
+
<path d="M6 10.5H10V12H6V10.5Z" fill="currentColor" />
|
|
7
|
+
<path d="M12 7.5H4V9H12V7.5Z" fill="currentColor" />
|
|
9
8
|
</Icon>
|
|
10
9
|
));
|
|
11
10
|
|
|
@@ -2,10 +2,9 @@ import { Icon, IconProps, forwardRef } from '@chakra-ui/react';
|
|
|
2
2
|
|
|
3
3
|
const Filter = forwardRef<IconProps, 'svg'>((props, ref) => (
|
|
4
4
|
<Icon ref={ref} viewBox="0 0 24 24" {...props}>
|
|
5
|
-
<path
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
/>
|
|
5
|
+
<path d="m3 7h18v2h-18z" fill="currentColor" />
|
|
6
|
+
<path d="m9 15h6v2h-6z" fill="currentColor" />
|
|
7
|
+
<path d="m18 11h-12v2h12z" fill="currentColor" />
|
|
9
8
|
</Icon>
|
|
10
9
|
));
|
|
11
10
|
|
|
@@ -20,6 +20,7 @@ export interface SelectProps extends Omit<FormControlProps, 'label' | 'onBlur' |
|
|
|
20
20
|
isLoading?: boolean;
|
|
21
21
|
label?: ReactNode;
|
|
22
22
|
name?: string;
|
|
23
|
+
placeholder?: ChakraSelectProps['placeholder'];
|
|
23
24
|
onBlur?: ChakraSelectProps['onBlur'];
|
|
24
25
|
onChange?: ChakraSelectProps['onChange'];
|
|
25
26
|
size?: 'small' | 'medium';
|