@soul-refiner/design-system 0.2.73 → 0.2.74
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.
|
@@ -6,18 +6,20 @@ import { FilterOptionProps } from './filter_option';
|
|
|
6
6
|
type FilterTriggerTypes = 'outlined' | 'pill' | 'labeled' | 'avatar' | 'text';
|
|
7
7
|
export interface FilterProps {
|
|
8
8
|
closeOnBlur?: boolean;
|
|
9
|
-
defaultValue?: string;
|
|
9
|
+
defaultValue?: string | string[];
|
|
10
10
|
disabled?: boolean;
|
|
11
11
|
graphic?: React.ReactElement;
|
|
12
|
-
label
|
|
12
|
+
label: string;
|
|
13
13
|
sx?: string;
|
|
14
14
|
triggerType: FilterTriggerTypes;
|
|
15
15
|
options: (Omit<FilterOptionProps, 'selected' | 'onClick'> | 'break')[];
|
|
16
16
|
closeOnChange?: boolean;
|
|
17
|
-
|
|
17
|
+
optionOnChange?: (event: React.MouseEvent) => void;
|
|
18
|
+
checkbox?: boolean;
|
|
19
|
+
applyOnClick?: (event: React.MouseEvent) => void;
|
|
18
20
|
}
|
|
19
21
|
declare const Filter: {
|
|
20
|
-
({ closeOnBlur, disabled, graphic, label, sx, triggerType, options, defaultValue, closeOnChange,
|
|
22
|
+
({ closeOnBlur, disabled, graphic, label, sx, triggerType, options, defaultValue, closeOnChange, optionOnChange, checkbox, applyOnClick, }: FilterProps): React.JSX.Element;
|
|
21
23
|
Trigger: {
|
|
22
24
|
({ disabled, graphic, label, onClick, open, type, value, }: FilterTriggerProps): React.JSX.Element;
|
|
23
25
|
displayName: string;
|
|
@@ -1,20 +1,40 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import React from 'react';
|
|
3
3
|
import styled, { css } from 'styled-components';
|
|
4
|
-
import { P } from '../../';
|
|
4
|
+
import { Box, Button, P } from '../../';
|
|
5
5
|
import { ChevronDown, ChevronUp } from '../../../icons';
|
|
6
6
|
import { Styles } from '../../../common';
|
|
7
7
|
import { Spacer, Option } from './filter_option';
|
|
8
|
-
const Filter = ({ closeOnBlur = true, disabled, graphic, label, sx, triggerType, options, defaultValue, closeOnChange = true,
|
|
8
|
+
const Filter = ({ closeOnBlur = true, disabled, graphic, label, sx, triggerType, options, defaultValue = '', closeOnChange = true, optionOnChange, checkbox, applyOnClick, }) => {
|
|
9
|
+
const multiple = !!checkbox;
|
|
9
10
|
const wrapperRef = React.useRef(null);
|
|
10
11
|
const optionsRef = React.useRef(null);
|
|
11
12
|
const [open, setOpen] = React.useState(false);
|
|
12
|
-
const [selected, setSelected] = React.useState(defaultValue
|
|
13
|
+
const [selected, setSelected] = React.useState(() => defaultValue ? defaultValue : multiple ? [] : '');
|
|
14
|
+
const [filterLabel, setFilterLabel] = React.useState('');
|
|
13
15
|
React.useEffect(() => {
|
|
14
16
|
if (disabled) {
|
|
15
17
|
setOpen(false);
|
|
16
18
|
}
|
|
17
19
|
}, [disabled]);
|
|
20
|
+
React.useEffect(() => {
|
|
21
|
+
if (open) {
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
if (!selected.length) {
|
|
25
|
+
setFilterLabel(label);
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
// not using selected[0] since it is required to show the first
|
|
29
|
+
const firstSelected = options.find((option) => typeof option !== 'string' && isSelected(option.value));
|
|
30
|
+
if (typeof selected === 'string') {
|
|
31
|
+
setFilterLabel(firstSelected.label);
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
setFilterLabel(selected.length > 1
|
|
35
|
+
? `${firstSelected.label} +${selected.length - 1}`
|
|
36
|
+
: firstSelected.label);
|
|
37
|
+
}, [selected, options, open]);
|
|
18
38
|
const handleOpen = () => {
|
|
19
39
|
if (disabled) {
|
|
20
40
|
return;
|
|
@@ -28,7 +48,7 @@ const Filter = ({ closeOnBlur = true, disabled, graphic, label, sx, triggerType,
|
|
|
28
48
|
setOpen(false);
|
|
29
49
|
}
|
|
30
50
|
};
|
|
31
|
-
const
|
|
51
|
+
const handleChangeSingle = (event) => {
|
|
32
52
|
event.stopPropagation();
|
|
33
53
|
const value = event.target.getAttribute('data-value') ?? '';
|
|
34
54
|
if (selected !== value) {
|
|
@@ -37,27 +57,52 @@ const Filter = ({ closeOnBlur = true, disabled, graphic, label, sx, triggerType,
|
|
|
37
57
|
else {
|
|
38
58
|
setSelected('');
|
|
39
59
|
}
|
|
40
|
-
if (closeOnChange) {
|
|
60
|
+
if (closeOnChange && !applyOnClick) {
|
|
41
61
|
setOpen(false);
|
|
42
62
|
}
|
|
43
|
-
if (
|
|
44
|
-
|
|
63
|
+
if (optionOnChange)
|
|
64
|
+
optionOnChange(event);
|
|
45
65
|
};
|
|
46
|
-
const
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
66
|
+
const handleChangeMultiple = (event) => {
|
|
67
|
+
event.stopPropagation();
|
|
68
|
+
const value = event.target.getAttribute('data-value') ?? '';
|
|
69
|
+
if (Array.isArray(selected) && selected.includes(value)) {
|
|
70
|
+
setSelected((prev) => prev.filter((item) => item !== value));
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
setSelected((prev) => [value, ...prev]);
|
|
74
|
+
}
|
|
75
|
+
if (closeOnChange && !applyOnClick) {
|
|
76
|
+
setOpen(false);
|
|
50
77
|
}
|
|
51
|
-
|
|
52
|
-
|
|
78
|
+
if (optionOnChange)
|
|
79
|
+
optionOnChange(event);
|
|
80
|
+
};
|
|
81
|
+
const handleChange = multiple ? handleChangeMultiple : handleChangeSingle;
|
|
82
|
+
const isSelected = (value) => multiple ? selected.includes(value) : value === selected;
|
|
83
|
+
const dropdownOptions = [
|
|
84
|
+
...options.map((option, index) => {
|
|
85
|
+
if (typeof option === 'string' && option === 'break') {
|
|
86
|
+
return React.createElement(Spacer, { key: `${React.useId()}-spacer-${index}` });
|
|
87
|
+
}
|
|
88
|
+
return (React.createElement(Option, { key: option.value, onClick: handleChange, selected: isSelected(option.value), checkbox: checkbox, radio: !multiple && triggerType === 'pill', showClose: index === 0 && triggerType === 'pill', onClose: () => setOpen(false), ...option }));
|
|
89
|
+
}),
|
|
90
|
+
triggerType === 'pill' && React.createElement(Spacer, { key: `spacer-${options.length}` }),
|
|
91
|
+
];
|
|
53
92
|
return (React.createElement(Wrapper, { "$sx": sx, tabIndex: 0, ref: wrapperRef, onBlurCapture: handleBlur },
|
|
54
|
-
React.createElement(FilterTrigger, { disabled: disabled, graphic: graphic, label: label, onClick: handleOpen, open: open, type: triggerType, value:
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
93
|
+
React.createElement(FilterTrigger, { disabled: disabled, graphic: graphic, label: label, onClick: handleOpen, open: open, type: triggerType, value: filterLabel }),
|
|
94
|
+
open && (React.createElement(Container, { ref: optionsRef },
|
|
95
|
+
React.createElement(React.Fragment, null,
|
|
96
|
+
dropdownOptions,
|
|
97
|
+
triggerType === 'pill' && (React.createElement(Box, { "$display": "flex", "$dir": "row", "$align": "center", "$justify": "flex-end", "$gap": 8, sx: "margin-top: 8px; padding-right: 8px; padding-bottom: 8px" },
|
|
98
|
+
React.createElement(Button, { onClick: () => {
|
|
99
|
+
setSelected(multiple ? [] : '');
|
|
100
|
+
setOpen(false);
|
|
101
|
+
}, size: "small", type: "text" }, "Reset"),
|
|
102
|
+
React.createElement(Button, { onClick: (event) => {
|
|
103
|
+
applyOnClick && applyOnClick(event);
|
|
104
|
+
setOpen(false);
|
|
105
|
+
}, size: "small", type: "primary" }, "Apply"))))))));
|
|
61
106
|
};
|
|
62
107
|
const Wrapper = styled.div `
|
|
63
108
|
position: relative;
|
|
@@ -98,7 +143,7 @@ const wrapperProps = {
|
|
|
98
143
|
};
|
|
99
144
|
const FilterTrigger = ({ disabled, graphic, label, onClick, open, type, value, }) => {
|
|
100
145
|
if (type === 'pill') {
|
|
101
|
-
return (React.createElement(Pill, { disabled: disabled, onClick: onClick, ...wrapperProps },
|
|
146
|
+
return (React.createElement(Pill, { disabled: disabled, onClick: onClick, "$labelIsDefault": value === label, ...wrapperProps },
|
|
102
147
|
graphic,
|
|
103
148
|
React.createElement(P, { ...textProps }, value),
|
|
104
149
|
!graphic && (open ? React.createElement(ChevronUp, null) : React.createElement(ChevronDown, null))));
|
|
@@ -164,18 +209,28 @@ const Outlined = styled.button `
|
|
|
164
209
|
const Pill = styled.button `
|
|
165
210
|
${sharedStyles}
|
|
166
211
|
${Styles.flex}
|
|
212
|
+
border: 1px solid var(--color-grey-8);
|
|
167
213
|
border-radius: 24px;
|
|
168
|
-
background-color: var(--color-
|
|
214
|
+
background-color: var(--color-white);
|
|
169
215
|
font-family: var(--font-inter);
|
|
170
216
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
217
|
+
${({ $labelIsDefault }) => !$labelIsDefault
|
|
218
|
+
? `
|
|
219
|
+
& > * {
|
|
220
|
+
color: var(--color-primary-blue-2);
|
|
221
|
+
fill: currentColor;
|
|
174
222
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
223
|
+
&:hover {
|
|
224
|
+
color: var(--color-primary-blue-1);
|
|
225
|
+
background-color: var(--color-primary-blue-4);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
&:hover {
|
|
230
|
+
background-color: var(--color-primary-blue-4);
|
|
231
|
+
}
|
|
232
|
+
`
|
|
233
|
+
: ''}
|
|
179
234
|
`;
|
|
180
235
|
const Labeled = styled.button `
|
|
181
236
|
${sharedStyles}
|
|
@@ -6,9 +6,13 @@ export interface FilterOptionProps {
|
|
|
6
6
|
selected: boolean;
|
|
7
7
|
sx?: string;
|
|
8
8
|
value: string;
|
|
9
|
+
checkbox?: boolean;
|
|
10
|
+
radio?: boolean;
|
|
11
|
+
showClose?: boolean;
|
|
12
|
+
onClose?: () => void;
|
|
9
13
|
}
|
|
10
14
|
declare const Option: {
|
|
11
|
-
({ label, onClick, selected, sx, value }: FilterOptionProps): React.JSX.Element;
|
|
15
|
+
({ label, onClick, selected, sx, value, checkbox, radio, showClose, onClose, }: FilterOptionProps): React.JSX.Element;
|
|
12
16
|
displayName: string;
|
|
13
17
|
};
|
|
14
18
|
export interface FilterSpacerProps {
|
|
@@ -1,10 +1,17 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import React from 'react';
|
|
3
3
|
import styled from 'styled-components';
|
|
4
|
-
import { Box, P } from '../..';
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
4
|
+
import { Box, P, Checkbox, Radio } from '../..';
|
|
5
|
+
import { Close } from '../../../icons';
|
|
6
|
+
const Option = ({ label, onClick, selected, sx, value, checkbox, radio = false, showClose = false, onClose, }) => {
|
|
7
|
+
return (React.createElement(OptionWrapper, { "data-value": value, onClick: onClick, "$selected": selected, "$sx": sx, "$hasCheckbox": !!checkbox },
|
|
8
|
+
checkbox && React.createElement(Checkbox, { selected: selected }),
|
|
9
|
+
radio && React.createElement(Radio, { checked: selected, value: value }),
|
|
10
|
+
React.createElement(Box, { "$dir": "column", "$display": "flex" }, label && (React.createElement(P, { "$color": "grey-4", "$fontSize": 14, "$lineHeight": 20, "$mediumBold": true, "$textClampLines": 1 }, label))),
|
|
11
|
+
showClose && (React.createElement(Close, { sx: "margin-left: auto; pointer-events: auto !important;", size: 16, onClick: (event) => {
|
|
12
|
+
event.stopPropagation();
|
|
13
|
+
onClose && onClose();
|
|
14
|
+
} }))));
|
|
8
15
|
};
|
|
9
16
|
Option.displayName = 'Filter.Option';
|
|
10
17
|
const OptionWrapper = styled.div `
|
|
@@ -15,13 +22,13 @@ const OptionWrapper = styled.div `
|
|
|
15
22
|
display: flex;
|
|
16
23
|
align-items: center;
|
|
17
24
|
gap: 8px;
|
|
18
|
-
min-width:
|
|
25
|
+
min-width: 217px;
|
|
19
26
|
|
|
20
27
|
& > * {
|
|
21
28
|
pointer-events: none;
|
|
22
29
|
}
|
|
23
30
|
|
|
24
|
-
${({ $selected }) => {
|
|
31
|
+
${({ $selected, $hasCheckbox }) => {
|
|
25
32
|
if ($selected) {
|
|
26
33
|
return `
|
|
27
34
|
background-color: var(--color-grey-9);
|
|
@@ -29,6 +36,11 @@ const OptionWrapper = styled.div `
|
|
|
29
36
|
background-color: var(--color-grey-8);
|
|
30
37
|
}`;
|
|
31
38
|
}
|
|
39
|
+
if ($hasCheckbox) {
|
|
40
|
+
return `&:hover {
|
|
41
|
+
background-color: var(--color-grey-10);
|
|
42
|
+
}`;
|
|
43
|
+
}
|
|
32
44
|
return `
|
|
33
45
|
&:hover {
|
|
34
46
|
background-color: var(--color-grey-10);
|