@saleor/macaw-ui 0.3.2 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +20 -0
- package/dist/cjs/index.js +2 -2
- package/dist/cjs/index.js.map +3 -3
- package/dist/esm/index.js +2 -2
- package/dist/esm/index.js.map +3 -3
- package/dist/types/ActionBar/ActionBar.d.ts +1 -0
- package/dist/types/ActionBar/context.d.ts +6 -3
- package/dist/types/Autocomplete/Autocomplete.d.ts +23 -0
- package/dist/types/Autocomplete/Autocomplete.stories.d.ts +5 -0
- package/dist/types/Autocomplete/fixtures.d.ts +4 -0
- package/dist/types/Autocomplete/index.d.ts +1 -0
- package/dist/types/Autocomplete/styles.d.ts +2 -0
- package/dist/types/Chip/Chip.d.ts +8 -0
- package/dist/types/Chip/ChipAdornment.d.ts +6 -0
- package/dist/types/Chip/ChipMovable.d.ts +5 -0
- package/dist/types/Chip/ChipRemovable.d.ts +6 -0
- package/dist/types/Chip/ChipSwatch.d.ts +7 -0
- package/dist/types/Chip/index.d.ts +5 -0
- package/dist/types/Chip/private/ColorSwatch.d.ts +6 -0
- package/dist/types/Chip/styles.d.ts +2 -0
- package/dist/types/CircleIndicator/index.d.ts +1 -1
- package/dist/types/Filter/FilterField/AutocompleteFilterField.d.ts +6 -0
- package/dist/types/Filter/FilterField/MultipleValueAutocompleteFilterField.d.ts +6 -0
- package/dist/types/Filter/stories/Filter.stories.d.ts +5 -0
- package/dist/types/Filter/stories/FilterInteractive.stories.d.ts +4 -0
- package/dist/types/Filter/stories/labels.d.ts +8 -0
- package/dist/types/Filter/styles.d.ts +1 -1
- package/dist/types/Filter/types.d.ts +8 -2
- package/dist/types/Filter/utils.d.ts +4 -1
- package/dist/types/IconButton/IconButton.d.ts +2 -0
- package/dist/types/IconButton/partials.d.ts +1 -1
- package/dist/types/IconButton/styles.d.ts +1 -1
- package/dist/types/MultipleValueAutocomplete/MultipleValueAutocomplete.d.ts +26 -0
- package/dist/types/MultipleValueAutocomplete/MultipleValueAutocomplete.stories.d.ts +6 -0
- package/dist/types/MultipleValueAutocomplete/fixtures.d.ts +4 -0
- package/dist/types/MultipleValueAutocomplete/index.d.ts +1 -0
- package/dist/types/MultipleValueAutocomplete/styles.d.ts +2 -0
- package/dist/types/MultipleValueAutocomplete/useMultipleValueAutocomplete.d.ts +32 -0
- package/dist/types/Savebar/Savebar.d.ts +1 -0
- package/dist/types/Sidebar/MenuItem.d.ts +9 -3
- package/dist/types/Sidebar/types.d.ts +8 -0
- package/dist/types/Sidebar/utils.d.ts +12 -0
- package/dist/types/SidebarDrawer/MenuItemBtn.d.ts +2 -1
- package/dist/types/SwitchSelector/SwitchSelector.d.ts +2 -0
- package/dist/types/SwitchSelector/SwitchSelector.stories.d.ts +4 -0
- package/dist/types/SwitchSelector/SwitchSelectorButton.d.ts +8 -0
- package/dist/types/SwitchSelector/index.d.ts +2 -0
- package/dist/types/index.d.ts +3 -0
- package/dist/types/theme/ThemeProvider.d.ts +4 -0
- package/dist/types/theme/types.d.ts +1 -0
- package/dist/types/tools/useTextWidth.d.ts +2 -0
- package/dist/types/utils/guideStyles.d.ts +1 -1
- package/dist/types/utils/mergeRefs.d.ts +2 -0
- package/dist/types/utils/useMockAutocomplete.d.ts +10 -0
- package/package.json +11 -13
- package/src/ActionBar/ActionBar.tsx +8 -11
- package/src/ActionBar/context.tsx +15 -6
- package/src/ActionBar/styles.ts +1 -1
- package/src/Autocomplete/Autocomplete.stories.tsx +43 -0
- package/src/Autocomplete/Autocomplete.tsx +187 -0
- package/src/Autocomplete/fixtures.ts +122 -0
- package/src/Autocomplete/index.ts +1 -0
- package/src/Autocomplete/styles.ts +19 -0
- package/src/Backlink/Backlink.tsx +3 -1
- package/src/Chip/Chip.tsx +52 -0
- package/src/Chip/ChipAdornment.tsx +53 -0
- package/src/Chip/ChipMovable.tsx +40 -0
- package/src/Chip/ChipRemovable.tsx +29 -0
- package/src/Chip/ChipSwatch.tsx +42 -0
- package/src/Chip/index.ts +5 -0
- package/src/Chip/private/ColorSwatch.tsx +21 -0
- package/src/Chip/styles.ts +46 -0
- package/src/CircleIndicator/CircleIndicator.stories.tsx +6 -6
- package/src/CircleIndicator/index.ts +1 -1
- package/src/Filter/Filter.tsx +88 -44
- package/src/Filter/FilterBar.tsx +15 -9
- package/src/Filter/FilterContent.tsx +8 -1
- package/src/Filter/FilterField/AutocompleteFilterField.tsx +61 -0
- package/src/Filter/FilterField/MultipleSelectFilterField.tsx +9 -3
- package/src/Filter/FilterField/MultipleValueAutocompleteFilterField.tsx +60 -0
- package/src/Filter/context.tsx +1 -1
- package/src/Filter/{Filter.stories.tsx → stories/Filter.stories.tsx} +47 -13
- package/src/Filter/stories/FilterInteractive.stories.tsx +97 -0
- package/src/Filter/stories/labels.ts +8 -0
- package/src/Filter/styles.ts +37 -6
- package/src/Filter/types.ts +8 -1
- package/src/Filter/utils.ts +71 -5
- package/src/IconButton/IconButton.tsx +17 -2
- package/src/IconButton/partials.ts +1 -1
- package/src/IconButton/styles.ts +38 -16
- package/src/MultipleValueAutocomplete/MultipleValueAutocomplete.stories.tsx +76 -0
- package/src/MultipleValueAutocomplete/MultipleValueAutocomplete.tsx +185 -0
- package/src/MultipleValueAutocomplete/fixtures.ts +122 -0
- package/src/MultipleValueAutocomplete/index.ts +1 -0
- package/src/MultipleValueAutocomplete/styles.ts +39 -0
- package/src/MultipleValueAutocomplete/useMultipleValueAutocomplete.ts +172 -0
- package/src/Savebar/Savebar.tsx +3 -4
- package/src/Sidebar/MenuItem.tsx +35 -14
- package/src/Sidebar/Sidebar.tsx +27 -11
- package/src/Sidebar/types.ts +9 -0
- package/src/Sidebar/utils.ts +23 -0
- package/src/SidebarDrawer/MenuItemBtn.tsx +12 -6
- package/src/SidebarDrawer/SidebarDrawer.tsx +8 -2
- package/src/SwitchSelector/SwitchSelector.stories.tsx +63 -0
- package/src/SwitchSelector/SwitchSelector.tsx +19 -0
- package/src/SwitchSelector/SwitchSelectorButton.tsx +59 -0
- package/src/SwitchSelector/index.ts +2 -0
- package/src/index.tsx +4 -1
- package/src/theme/ThemeProvider.tsx +6 -0
- package/src/theme/createSaleorTheme/createSaleorTheme.tsx +2 -1
- package/src/theme/createSaleorTheme/overrides/controls.ts +4 -1
- package/src/theme/createSaleorTheme/overrides/inputs.ts +1 -1
- package/src/theme/themes.ts +1 -1
- package/src/theme/types.ts +1 -0
- package/src/tools/useTextWidth.ts +20 -0
- package/src/utils/guideStyles.ts +5 -0
- package/src/utils/mergeRefs.ts +14 -0
- package/src/utils/useMockAutocomplete.ts +37 -0
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
|
|
3
|
+
import { CloseIcon } from "../icons";
|
|
4
|
+
import { makeStyles } from "../theme";
|
|
5
|
+
import { Chip, ChipProps } from "./Chip";
|
|
6
|
+
import { ChipAdornment } from "./ChipAdornment";
|
|
7
|
+
import { ColorSwatch } from "./private/ColorSwatch";
|
|
8
|
+
|
|
9
|
+
export interface ChipSwatchProps extends Omit<ChipProps, "startAdornment"> {
|
|
10
|
+
onRemove?: () => void;
|
|
11
|
+
color: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const useStyles = makeStyles({
|
|
15
|
+
swatchPosition: {
|
|
16
|
+
marginLeft: "-10px",
|
|
17
|
+
},
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
export const ChipSwatch = React.forwardRef<HTMLDivElement, ChipSwatchProps>(
|
|
21
|
+
({ endAdornment, color, onRemove, ...props }, ref) => {
|
|
22
|
+
const classes = useStyles();
|
|
23
|
+
|
|
24
|
+
return (
|
|
25
|
+
<Chip
|
|
26
|
+
startAdornment={<ColorSwatch color={color} />}
|
|
27
|
+
startAdornmentClassName={classes.swatchPosition}
|
|
28
|
+
endAdornment={
|
|
29
|
+
onRemove && (
|
|
30
|
+
<ChipAdornment>
|
|
31
|
+
<CloseIcon role="button" onClick={() => onRemove()} />
|
|
32
|
+
</ChipAdornment>
|
|
33
|
+
)
|
|
34
|
+
}
|
|
35
|
+
{...props}
|
|
36
|
+
ref={ref}
|
|
37
|
+
/>
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
ChipSwatch.displayName = "ChipSwatch";
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import clsx from "clsx";
|
|
2
|
+
import React from "react";
|
|
3
|
+
|
|
4
|
+
import useStyles from "../styles";
|
|
5
|
+
|
|
6
|
+
export interface ColorSwatchProps {
|
|
7
|
+
color: string;
|
|
8
|
+
className?: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export const ColorSwatch = ({ color, className }: ColorSwatchProps) => {
|
|
12
|
+
const classes = useStyles();
|
|
13
|
+
|
|
14
|
+
return (
|
|
15
|
+
<div
|
|
16
|
+
className={clsx(classes.swatch, className)}
|
|
17
|
+
style={{ background: color }}
|
|
18
|
+
aria-hidden="true"
|
|
19
|
+
/>
|
|
20
|
+
);
|
|
21
|
+
};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { makeStyles } from "../theme";
|
|
2
|
+
|
|
3
|
+
const useStyles = makeStyles(
|
|
4
|
+
(theme) => ({
|
|
5
|
+
chip: {
|
|
6
|
+
...theme.typography.body2,
|
|
7
|
+
background: theme.palette.saleor.active[1],
|
|
8
|
+
color: theme.palette.primary.contrastText,
|
|
9
|
+
borderRadius: "8px",
|
|
10
|
+
display: "flex",
|
|
11
|
+
alignItems: "center",
|
|
12
|
+
padding: theme.spacing(0, 2),
|
|
13
|
+
gap: "4px",
|
|
14
|
+
lineHeight: 1,
|
|
15
|
+
minHeight: theme.spacing(4),
|
|
16
|
+
cursor: "default",
|
|
17
|
+
userSelect: "none",
|
|
18
|
+
textAlign: "center",
|
|
19
|
+
"& > span": {
|
|
20
|
+
overflow: "hidden",
|
|
21
|
+
textOverflow: "ellipsis",
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
startAdornment: {
|
|
25
|
+
marginLeft: "-8px",
|
|
26
|
+
flexShrink: 0,
|
|
27
|
+
},
|
|
28
|
+
endAdornment: {
|
|
29
|
+
marginRight: "-8px",
|
|
30
|
+
flexShrink: 0,
|
|
31
|
+
},
|
|
32
|
+
swatch: {
|
|
33
|
+
width: theme.spacing(3),
|
|
34
|
+
height: theme.spacing(3),
|
|
35
|
+
border: `2px solid #fff`,
|
|
36
|
+
flexShrink: 0,
|
|
37
|
+
marginRight: "7px",
|
|
38
|
+
borderRadius: "4px",
|
|
39
|
+
// default background when no style specified
|
|
40
|
+
background: "transparent",
|
|
41
|
+
},
|
|
42
|
+
}),
|
|
43
|
+
{ name: "Chip" }
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
export default useStyles;
|
|
@@ -2,9 +2,9 @@ import { Typography } from "@material-ui/core";
|
|
|
2
2
|
import { Meta, Story } from "@storybook/react";
|
|
3
3
|
import clsx from "clsx";
|
|
4
4
|
import React from "react";
|
|
5
|
+
|
|
5
6
|
import { Decorator, GuideDecorator } from "../utils/Decorator";
|
|
6
7
|
import useGuideStyles from "../utils/guideStyles";
|
|
7
|
-
|
|
8
8
|
import { CircleIndicator } from "./CircleIndicator";
|
|
9
9
|
|
|
10
10
|
const StoryWrapper: React.FC = () => {
|
|
@@ -16,7 +16,7 @@ const StoryWrapper: React.FC = () => {
|
|
|
16
16
|
Circle Indicators
|
|
17
17
|
</Typography>
|
|
18
18
|
<Typography className={guideClasses.paragraph} component="p">
|
|
19
|
-
Circle Indicators represent status of a certain element.
|
|
19
|
+
Circle Indicators represent status of a certain element.
|
|
20
20
|
</Typography>
|
|
21
21
|
<div style={{ display: "flex", gap: "24px" }}>
|
|
22
22
|
<div
|
|
@@ -30,10 +30,10 @@ const StoryWrapper: React.FC = () => {
|
|
|
30
30
|
padding: 24,
|
|
31
31
|
}}
|
|
32
32
|
>
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
33
|
+
Error <CircleIndicator color="error" />
|
|
34
|
+
Warning <CircleIndicator color="warning" />
|
|
35
|
+
Success <CircleIndicator color="success" />
|
|
36
|
+
Info <CircleIndicator color="info" />
|
|
37
37
|
</div>
|
|
38
38
|
</div>
|
|
39
39
|
</div>
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export * from
|
|
1
|
+
export * from "./CircleIndicator";
|
package/src/Filter/Filter.tsx
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import IconButton from "@material-ui/core/IconButton";
|
|
2
1
|
import MenuItem from "@material-ui/core/MenuItem";
|
|
3
|
-
import Select from "@material-ui/core/Select";
|
|
2
|
+
import Select, { SelectProps } from "@material-ui/core/Select";
|
|
4
3
|
import Typography from "@material-ui/core/Typography";
|
|
5
|
-
import { difference } from "lodash";
|
|
4
|
+
import { difference, uniqBy } from "lodash";
|
|
6
5
|
import React from "react";
|
|
7
6
|
|
|
7
|
+
import { IconButton } from "../IconButton";
|
|
8
8
|
import { DeleteIcon } from "../icons";
|
|
9
9
|
import { useFilters } from "./context";
|
|
10
10
|
import { FilterContent } from "./FilterContent";
|
|
@@ -19,9 +19,16 @@ import {
|
|
|
19
19
|
import * as utils from "./utils";
|
|
20
20
|
|
|
21
21
|
export type FilterProps = FilterOptions & FilterDetailedOptions;
|
|
22
|
-
export const Filter: React.FC<FilterProps> = ({
|
|
23
|
-
|
|
22
|
+
export const Filter: React.FC<FilterProps> = ({
|
|
23
|
+
name: nameProp,
|
|
24
|
+
label,
|
|
25
|
+
...options
|
|
26
|
+
}) => {
|
|
27
|
+
const name = utils.getFilterName(nameProp, options);
|
|
28
|
+
const { filters, register, set, unregister } = useFilters();
|
|
24
29
|
const registered = React.useRef(false);
|
|
30
|
+
const filter = filters.find((fd) => fd.name === name);
|
|
31
|
+
|
|
25
32
|
React.useEffect(() => {
|
|
26
33
|
register(name, label, options);
|
|
27
34
|
registered.current = true;
|
|
@@ -33,7 +40,8 @@ export const Filter: React.FC<FilterProps> = ({ name, label, ...options }) => {
|
|
|
33
40
|
if (
|
|
34
41
|
registered.current &&
|
|
35
42
|
options.choices !== undefined &&
|
|
36
|
-
|
|
43
|
+
filter &&
|
|
44
|
+
difference(options.choices, filter!.options.choices!).length
|
|
37
45
|
) {
|
|
38
46
|
set(name, {
|
|
39
47
|
options: {
|
|
@@ -58,7 +66,7 @@ export const FilterRow: React.FC<FilterRowProps> = ({
|
|
|
58
66
|
labels,
|
|
59
67
|
}) => {
|
|
60
68
|
const classes = useStyles();
|
|
61
|
-
const { filters, toggle, toggleRange } = useFilters();
|
|
69
|
+
const { filters, toggle, toggleRange, swap } = useFilters();
|
|
62
70
|
|
|
63
71
|
const filter = filters.find((filter) => filter.name === name);
|
|
64
72
|
|
|
@@ -67,50 +75,86 @@ export const FilterRow: React.FC<FilterRowProps> = ({
|
|
|
67
75
|
}
|
|
68
76
|
|
|
69
77
|
const availableFilters = utils.getAvailableFilters(filters);
|
|
70
|
-
const options =
|
|
78
|
+
const options = uniqBy(
|
|
79
|
+
[filter.options.group ?? filter, ...availableFilters],
|
|
80
|
+
"name"
|
|
81
|
+
);
|
|
82
|
+
const groupOptions = [
|
|
83
|
+
filter,
|
|
84
|
+
...filters.filter(
|
|
85
|
+
(f) =>
|
|
86
|
+
f.name !== filter.name &&
|
|
87
|
+
f.options.group?.name === filter.options.group?.name &&
|
|
88
|
+
!f.active
|
|
89
|
+
),
|
|
90
|
+
];
|
|
71
91
|
|
|
72
92
|
const change = (event: React.ChangeEvent<EventTarget<unknown>>) => {
|
|
73
|
-
|
|
74
|
-
|
|
93
|
+
const targetFilterName = event.target.value as string;
|
|
94
|
+
|
|
95
|
+
swap(name, targetFilterName);
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
const selectProps: SelectProps = {
|
|
99
|
+
classes: {
|
|
100
|
+
selectMenu: classes.filterInputInner,
|
|
101
|
+
},
|
|
102
|
+
variant: "outlined",
|
|
75
103
|
};
|
|
76
104
|
|
|
77
105
|
return (
|
|
78
106
|
<div className={classes.filter}>
|
|
79
|
-
<
|
|
80
|
-
{
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
107
|
+
<div className={classes.filterOptions}>
|
|
108
|
+
<Typography className={classes.filterConjunction}>
|
|
109
|
+
{first ? labels.where : labels.and}
|
|
110
|
+
</Typography>
|
|
111
|
+
<Select
|
|
112
|
+
{...selectProps}
|
|
113
|
+
className={classes.filterName}
|
|
114
|
+
onChange={change}
|
|
115
|
+
value={filter.options.group?.name ?? filter.name}
|
|
116
|
+
>
|
|
117
|
+
{options.map((option) => (
|
|
118
|
+
<MenuItem key={option.name} value={option.name}>
|
|
119
|
+
{option.label}
|
|
120
|
+
</MenuItem>
|
|
121
|
+
))}
|
|
122
|
+
</Select>
|
|
123
|
+
{!!filter.options.group && (
|
|
124
|
+
<Select
|
|
125
|
+
{...selectProps}
|
|
126
|
+
className={classes.filterName}
|
|
127
|
+
onChange={change}
|
|
128
|
+
value={filter.name}
|
|
129
|
+
>
|
|
130
|
+
{groupOptions.map((option) => (
|
|
131
|
+
<MenuItem key={option.name} value={option.name}>
|
|
132
|
+
{option.label}
|
|
133
|
+
</MenuItem>
|
|
134
|
+
))}
|
|
135
|
+
</Select>
|
|
136
|
+
)}
|
|
137
|
+
<Select
|
|
138
|
+
{...selectProps}
|
|
139
|
+
disabled={filter.options.type !== FilterType.Range}
|
|
140
|
+
className={classes.filterRange}
|
|
141
|
+
value={filter.range.toString()}
|
|
142
|
+
onChange={() => toggleRange(name)}
|
|
143
|
+
>
|
|
144
|
+
<MenuItem value="false">{labels.is}</MenuItem>
|
|
145
|
+
<MenuItem value="true">{labels.range}</MenuItem>
|
|
146
|
+
</Select>
|
|
147
|
+
</div>
|
|
110
148
|
<FilterContent filter={filter} labels={labels} />
|
|
111
|
-
<
|
|
112
|
-
<
|
|
113
|
-
|
|
149
|
+
<div className={classes.filterDeleteContainer}>
|
|
150
|
+
<IconButton
|
|
151
|
+
variant="secondary"
|
|
152
|
+
className={classes.filterDelete}
|
|
153
|
+
onClick={() => toggle(name)}
|
|
154
|
+
>
|
|
155
|
+
<DeleteIcon />
|
|
156
|
+
</IconButton>
|
|
157
|
+
</div>
|
|
114
158
|
</div>
|
|
115
159
|
);
|
|
116
160
|
};
|
package/src/Filter/FilterBar.tsx
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import Button from "@material-ui/core/Button";
|
|
2
1
|
import Card from "@material-ui/core/Card";
|
|
3
2
|
import CardContent from "@material-ui/core/CardContent";
|
|
4
3
|
import CardHeader from "@material-ui/core/CardHeader";
|
|
5
|
-
import IconButton from "@material-ui/core/IconButton";
|
|
6
|
-
import Close from "@material-ui/icons/Close";
|
|
7
4
|
import React from "react";
|
|
8
5
|
|
|
6
|
+
import { Button } from "../Button";
|
|
7
|
+
import { IconButton } from "../IconButton";
|
|
8
|
+
import { CloseIcon, PlusIcon } from "../icons";
|
|
9
9
|
import { FilterContext } from "./context";
|
|
10
10
|
import { FilterRow } from "./Filter";
|
|
11
11
|
import { FilterMenu, FilterMenuLabels } from "./FilterMenu";
|
|
@@ -56,8 +56,10 @@ export const FilterBar: React.FC<FilterBarProps> = React.forwardRef(
|
|
|
56
56
|
setFilterData((fd) => fd.filter((filter) => filter.name !== name));
|
|
57
57
|
const set = (name: string, filter: Partial<FilterData>) =>
|
|
58
58
|
setFilterData((fd) =>
|
|
59
|
-
fd.map((f) => (f.name === name ? { ...f, filter } : f))
|
|
59
|
+
fd.map((f) => (f.name === name ? { ...f, ...filter } : f))
|
|
60
60
|
);
|
|
61
|
+
const swap = (previousFilterName: string, nextFilterName: string) =>
|
|
62
|
+
setFilterData((fd) => utils.swap(fd, previousFilterName, nextFilterName));
|
|
61
63
|
|
|
62
64
|
const availableFilters = utils.getAvailableFilters(filterData);
|
|
63
65
|
|
|
@@ -75,16 +77,17 @@ export const FilterBar: React.FC<FilterBarProps> = React.forwardRef(
|
|
|
75
77
|
toggle,
|
|
76
78
|
toggleRange,
|
|
77
79
|
unregister,
|
|
80
|
+
swap,
|
|
78
81
|
onChange,
|
|
79
82
|
}}
|
|
80
83
|
>
|
|
81
84
|
{children}
|
|
82
|
-
<Card className={classes.bar} ref={ref} elevation={
|
|
85
|
+
<Card className={classes.bar} ref={ref} elevation={8}>
|
|
83
86
|
<CardHeader
|
|
84
87
|
title={labels.header}
|
|
85
88
|
action={
|
|
86
|
-
<IconButton onClick={onClose}>
|
|
87
|
-
<
|
|
89
|
+
<IconButton variant="secondary" onClick={onClose}>
|
|
90
|
+
<CloseIcon />
|
|
88
91
|
</IconButton>
|
|
89
92
|
}
|
|
90
93
|
/>
|
|
@@ -105,11 +108,14 @@ export const FilterBar: React.FC<FilterBarProps> = React.forwardRef(
|
|
|
105
108
|
{!!availableFilters.length && (
|
|
106
109
|
<>
|
|
107
110
|
<Button
|
|
108
|
-
|
|
111
|
+
className={classes.barAddBtn}
|
|
112
|
+
color="text"
|
|
113
|
+
variant="secondary"
|
|
109
114
|
ref={button}
|
|
110
115
|
onClick={() => setMenuOpen(true)}
|
|
111
116
|
>
|
|
112
|
-
|
|
117
|
+
{labels.addButton}
|
|
118
|
+
<PlusIcon />
|
|
113
119
|
</Button>
|
|
114
120
|
</>
|
|
115
121
|
)}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
|
|
3
|
+
import { AutocompleteFilterField } from "./FilterField/AutocompleteFilterField";
|
|
3
4
|
import { MultipleSelectFilterField } from "./FilterField/MultipleSelectFilterField";
|
|
5
|
+
import { MultipleValueAutocompleteFilterField } from "./FilterField/MultipleValueAutocompleteFilterField";
|
|
4
6
|
import { RangeFilterField } from "./FilterField/RangeFilterField";
|
|
5
7
|
import { SelectFilterField } from "./FilterField/SelectFilterField";
|
|
6
8
|
import { TextFilterField } from "./FilterField/TextFilterField";
|
|
@@ -18,7 +20,12 @@ export const FilterContent: React.FC<FilterContentProps> = ({
|
|
|
18
20
|
const { options, range } = filter;
|
|
19
21
|
const { type, multiple } = options;
|
|
20
22
|
|
|
21
|
-
if (type === FilterType.
|
|
23
|
+
if (type === FilterType.Autocomplete) {
|
|
24
|
+
if (multiple) {
|
|
25
|
+
return <MultipleValueAutocompleteFilterField filter={filter} />;
|
|
26
|
+
}
|
|
27
|
+
return <AutocompleteFilterField filter={filter} />;
|
|
28
|
+
} else if (type === FilterType.Choice) {
|
|
22
29
|
if (multiple) {
|
|
23
30
|
return <MultipleSelectFilterField filter={filter} />;
|
|
24
31
|
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import MenuItem from "@material-ui/core/MenuItem";
|
|
2
|
+
import React from "react";
|
|
3
|
+
|
|
4
|
+
import { SyntheticChangeEvent } from "../../../types/utils";
|
|
5
|
+
import { Autocomplete } from "../../Autocomplete/Autocomplete";
|
|
6
|
+
import { useFilters } from "../context";
|
|
7
|
+
import useStyles from "../styles";
|
|
8
|
+
import { FilterData } from "../types";
|
|
9
|
+
|
|
10
|
+
export interface FilterContentProps {
|
|
11
|
+
filter: FilterData;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export const AutocompleteFilterField: React.FC<FilterContentProps> = ({
|
|
15
|
+
filter,
|
|
16
|
+
}) => {
|
|
17
|
+
const classes = useStyles();
|
|
18
|
+
const { onChange } = useFilters();
|
|
19
|
+
|
|
20
|
+
const { name, options } = filter;
|
|
21
|
+
const { choices } = options;
|
|
22
|
+
|
|
23
|
+
if (options.choices === undefined) {
|
|
24
|
+
throw new Error("FilterType.Autocomplete must be used with choices prop");
|
|
25
|
+
}
|
|
26
|
+
if (options.onInputChange === undefined) {
|
|
27
|
+
throw new Error(
|
|
28
|
+
"FilterType.Autocomplete must be used with onInputChange prop"
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const handleChoiceChange = (event: SyntheticChangeEvent) =>
|
|
33
|
+
onChange(name, event.target.value);
|
|
34
|
+
|
|
35
|
+
return (
|
|
36
|
+
<Autocomplete
|
|
37
|
+
className={classes.filterValue}
|
|
38
|
+
choices={options.choices!}
|
|
39
|
+
InputProps={{
|
|
40
|
+
classes: {
|
|
41
|
+
input: classes.filterInputInner,
|
|
42
|
+
},
|
|
43
|
+
}}
|
|
44
|
+
onChange={handleChoiceChange}
|
|
45
|
+
onInputChange={options.onInputChange}
|
|
46
|
+
value={filter.values}
|
|
47
|
+
>
|
|
48
|
+
{({ getItemProps, highlightedIndex }) =>
|
|
49
|
+
choices!.map((choice, choiceIndex) => (
|
|
50
|
+
<MenuItem
|
|
51
|
+
selected={highlightedIndex === choiceIndex}
|
|
52
|
+
{...getItemProps({ item: choice, index: choiceIndex })}
|
|
53
|
+
>
|
|
54
|
+
{choice.label}
|
|
55
|
+
</MenuItem>
|
|
56
|
+
))
|
|
57
|
+
}
|
|
58
|
+
</Autocomplete>
|
|
59
|
+
);
|
|
60
|
+
};
|
|
61
|
+
AutocompleteFilterField.displayName = "AutocompleteFilterField";
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import Checkbox from "@material-ui/core/Checkbox";
|
|
2
|
-
import Chip from "@material-ui/core/Chip";
|
|
3
2
|
import MenuItem from "@material-ui/core/MenuItem";
|
|
4
3
|
import Select from "@material-ui/core/Select";
|
|
5
4
|
import clsx from "clsx";
|
|
6
5
|
import React from "react";
|
|
7
6
|
|
|
7
|
+
import { Chip } from "../../Chip";
|
|
8
8
|
import { useFilters } from "../context";
|
|
9
9
|
import useStyles from "../styles";
|
|
10
10
|
import { EventTarget, FilterData } from "../types";
|
|
@@ -18,6 +18,9 @@ export const MultipleSelectFilterField: React.FC<FilterContentProps> = ({
|
|
|
18
18
|
}) => {
|
|
19
19
|
const classes = useStyles();
|
|
20
20
|
const { onChange } = useFilters();
|
|
21
|
+
const [displayValues] = React.useState(
|
|
22
|
+
filter.options.displayValues || filter.options.choices
|
|
23
|
+
);
|
|
21
24
|
|
|
22
25
|
const { name, options } = filter;
|
|
23
26
|
const { choices } = options;
|
|
@@ -29,7 +32,7 @@ export const MultipleSelectFilterField: React.FC<FilterContentProps> = ({
|
|
|
29
32
|
<Select
|
|
30
33
|
className={classes.filterValue}
|
|
31
34
|
classes={{
|
|
32
|
-
root: classes.
|
|
35
|
+
root: classes.filterMultipleValueInputInner,
|
|
33
36
|
}}
|
|
34
37
|
multiple
|
|
35
38
|
variant="outlined"
|
|
@@ -41,7 +44,10 @@ export const MultipleSelectFilterField: React.FC<FilterContentProps> = ({
|
|
|
41
44
|
return (
|
|
42
45
|
<div className={classes.filterChipContainer}>
|
|
43
46
|
{typedValues.map((value) => (
|
|
44
|
-
<Chip className={classes.filterChip} key={value}
|
|
47
|
+
<Chip className={classes.filterChip} key={value}>
|
|
48
|
+
{displayValues?.find((dv) => dv.value === value)?.label ??
|
|
49
|
+
value}
|
|
50
|
+
</Chip>
|
|
45
51
|
))}
|
|
46
52
|
</div>
|
|
47
53
|
);
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import MenuItem from "@material-ui/core/MenuItem";
|
|
2
|
+
import React from "react";
|
|
3
|
+
|
|
4
|
+
import { SyntheticChangeEvent } from "../../../types/utils";
|
|
5
|
+
import { MultipleValueAutocomplete } from "../../MultipleValueAutocomplete";
|
|
6
|
+
import { useFilters } from "../context";
|
|
7
|
+
import useStyles from "../styles";
|
|
8
|
+
import { FilterData } from "../types";
|
|
9
|
+
|
|
10
|
+
export interface FilterContentProps {
|
|
11
|
+
filter: FilterData;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export const MultipleValueAutocompleteFilterField: React.FC<FilterContentProps> =
|
|
15
|
+
({ filter }) => {
|
|
16
|
+
const classes = useStyles();
|
|
17
|
+
const { onChange } = useFilters();
|
|
18
|
+
|
|
19
|
+
const { name, options } = filter;
|
|
20
|
+
|
|
21
|
+
if (options.choices === undefined) {
|
|
22
|
+
throw new Error("FilterType.Autocomplete must be used with choices prop");
|
|
23
|
+
}
|
|
24
|
+
if (options.onInputChange === undefined) {
|
|
25
|
+
throw new Error(
|
|
26
|
+
"FilterType.Autocomplete must be used with onInputChange prop"
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const handleChoiceChange = (event: SyntheticChangeEvent<string[]>) =>
|
|
31
|
+
onChange(name, event.target.value);
|
|
32
|
+
|
|
33
|
+
return (
|
|
34
|
+
<MultipleValueAutocomplete
|
|
35
|
+
className={classes.filterValue}
|
|
36
|
+
choices={options.choices!}
|
|
37
|
+
InputProps={{
|
|
38
|
+
classes: {
|
|
39
|
+
root: classes.filterMultipleValueInputInner,
|
|
40
|
+
},
|
|
41
|
+
}}
|
|
42
|
+
onChange={handleChoiceChange}
|
|
43
|
+
onInputChange={options.onInputChange}
|
|
44
|
+
value={filter.values}
|
|
45
|
+
>
|
|
46
|
+
{({ choices: filteredChoices, getItemProps, highlightedIndex }) =>
|
|
47
|
+
filteredChoices.map((choice, choiceIndex) => (
|
|
48
|
+
<MenuItem
|
|
49
|
+
selected={highlightedIndex === choiceIndex}
|
|
50
|
+
{...getItemProps({ item: choice, index: choiceIndex })}
|
|
51
|
+
>
|
|
52
|
+
{choice.label}
|
|
53
|
+
</MenuItem>
|
|
54
|
+
))
|
|
55
|
+
}
|
|
56
|
+
</MultipleValueAutocomplete>
|
|
57
|
+
);
|
|
58
|
+
};
|
|
59
|
+
MultipleValueAutocompleteFilterField.displayName =
|
|
60
|
+
"MultipleValueAutocompleteFilterField";
|
package/src/Filter/context.tsx
CHANGED
|
@@ -5,7 +5,7 @@ import { FilterContextType } from "./types";
|
|
|
5
5
|
export const FilterContext = createContext<FilterContextType | null>(null);
|
|
6
6
|
export const useFilters = (): FilterContextType => {
|
|
7
7
|
const ctx = useContext(FilterContext);
|
|
8
|
-
if (ctx
|
|
8
|
+
if (!ctx) {
|
|
9
9
|
throw new Error("useFilters must be used within a FilterContext");
|
|
10
10
|
}
|
|
11
11
|
|