@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,185 @@
|
|
|
1
|
+
import CircularProgress from "@material-ui/core/CircularProgress";
|
|
2
|
+
import Grow from "@material-ui/core/Grow";
|
|
3
|
+
import Paper from "@material-ui/core/Paper";
|
|
4
|
+
import Popper, { PopperPlacementType } from "@material-ui/core/Popper";
|
|
5
|
+
import TextField, { StandardTextFieldProps } from "@material-ui/core/TextField";
|
|
6
|
+
import clsx from "clsx";
|
|
7
|
+
import { UseComboboxGetItemPropsOptions } from "downshift";
|
|
8
|
+
import React from "react";
|
|
9
|
+
|
|
10
|
+
import { SyntheticChangeEvent } from "../../types/utils";
|
|
11
|
+
import { ChipRemovable } from "../Chip";
|
|
12
|
+
import { Choice } from "../Filter";
|
|
13
|
+
import { IconButton } from "../IconButton";
|
|
14
|
+
import { PlusIcon } from "../icons";
|
|
15
|
+
import {
|
|
16
|
+
isScrolledToBottom,
|
|
17
|
+
useElementScroll,
|
|
18
|
+
} from "../tools/useElementScroll";
|
|
19
|
+
import { mergeRefs } from "../utils/mergeRefs";
|
|
20
|
+
import useStyles from "./styles";
|
|
21
|
+
import useMultipleValueAutocomplete from "./useMultipleValueAutocomplete";
|
|
22
|
+
|
|
23
|
+
export interface MultipleValueAutocompleteProps
|
|
24
|
+
extends Omit<StandardTextFieldProps, "onChange"> {
|
|
25
|
+
children: (data: {
|
|
26
|
+
getItemProps: (opts: UseComboboxGetItemPropsOptions<Choice>) => any;
|
|
27
|
+
highlightedIndex: number;
|
|
28
|
+
inputValue: string;
|
|
29
|
+
choices: Choice[];
|
|
30
|
+
}) => React.ReactNode | React.ReactNodeArray;
|
|
31
|
+
className?: string;
|
|
32
|
+
enableReinitialize?: boolean;
|
|
33
|
+
styles?: React.CSSProperties;
|
|
34
|
+
choices: Choice[];
|
|
35
|
+
label?: string;
|
|
36
|
+
loading?: boolean;
|
|
37
|
+
popperPlacement?: PopperPlacementType;
|
|
38
|
+
initialValue?: Choice[];
|
|
39
|
+
onChange?: (event: SyntheticChangeEvent<string[]>) => void;
|
|
40
|
+
onInputChange?: (value: string) => void;
|
|
41
|
+
onScrollToBottom?: () => void;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export const MultipleValueAutocomplete: React.FC<MultipleValueAutocompleteProps> =
|
|
45
|
+
({
|
|
46
|
+
choices,
|
|
47
|
+
children,
|
|
48
|
+
enableReinitialize,
|
|
49
|
+
name,
|
|
50
|
+
InputProps,
|
|
51
|
+
initialValue = [],
|
|
52
|
+
loading,
|
|
53
|
+
popperPlacement = "bottom-start",
|
|
54
|
+
onChange,
|
|
55
|
+
onInputChange,
|
|
56
|
+
onScrollToBottom,
|
|
57
|
+
...rest
|
|
58
|
+
}) => {
|
|
59
|
+
const classes = useStyles();
|
|
60
|
+
const {
|
|
61
|
+
anchor,
|
|
62
|
+
comboboxProps,
|
|
63
|
+
filteredChoices,
|
|
64
|
+
getItemProps,
|
|
65
|
+
getSelectedItemProps,
|
|
66
|
+
getToggleButtonProps,
|
|
67
|
+
highlightedIndex,
|
|
68
|
+
inputProps,
|
|
69
|
+
inputRef,
|
|
70
|
+
inputValue,
|
|
71
|
+
inputWidth,
|
|
72
|
+
isOpen,
|
|
73
|
+
labelProps,
|
|
74
|
+
menuProps,
|
|
75
|
+
ref,
|
|
76
|
+
removeSelectedItem,
|
|
77
|
+
selectedItems,
|
|
78
|
+
} = useMultipleValueAutocomplete({
|
|
79
|
+
choices,
|
|
80
|
+
enableReinitialize,
|
|
81
|
+
initialValue,
|
|
82
|
+
name,
|
|
83
|
+
onChange,
|
|
84
|
+
onInputChange,
|
|
85
|
+
});
|
|
86
|
+
const { anchor: dropdownRef, position, setAnchor } = useElementScroll();
|
|
87
|
+
|
|
88
|
+
React.useEffect(() => {
|
|
89
|
+
if (
|
|
90
|
+
isOpen &&
|
|
91
|
+
onScrollToBottom &&
|
|
92
|
+
dropdownRef &&
|
|
93
|
+
isScrolledToBottom(dropdownRef, position!, 5)
|
|
94
|
+
) {
|
|
95
|
+
onScrollToBottom();
|
|
96
|
+
}
|
|
97
|
+
}, [position?.y, dropdownRef]);
|
|
98
|
+
|
|
99
|
+
return (
|
|
100
|
+
<>
|
|
101
|
+
<TextField
|
|
102
|
+
{...rest}
|
|
103
|
+
{...comboboxProps}
|
|
104
|
+
name={name}
|
|
105
|
+
InputLabelProps={{
|
|
106
|
+
shrink: isOpen || selectedItems.length || inputValue.length,
|
|
107
|
+
...labelProps,
|
|
108
|
+
}}
|
|
109
|
+
ref={ref}
|
|
110
|
+
InputProps={{
|
|
111
|
+
...InputProps,
|
|
112
|
+
...inputProps,
|
|
113
|
+
classes: {
|
|
114
|
+
...(InputProps?.classes ?? {}),
|
|
115
|
+
root: clsx(classes.inputContainer, InputProps?.classes?.root, {
|
|
116
|
+
[classes.inputContainerWithChips]: selectedItems.length > 0,
|
|
117
|
+
}),
|
|
118
|
+
input: clsx(classes.input, InputProps?.classes?.input),
|
|
119
|
+
},
|
|
120
|
+
startAdornment: selectedItems.map((item, index) => (
|
|
121
|
+
<ChipRemovable
|
|
122
|
+
key={`selected-item-${index}`}
|
|
123
|
+
{...getSelectedItemProps({ selectedItem: item, index })}
|
|
124
|
+
onRemove={() => removeSelectedItem(item)}
|
|
125
|
+
>
|
|
126
|
+
{item.label}
|
|
127
|
+
</ChipRemovable>
|
|
128
|
+
)),
|
|
129
|
+
endAdornment: (
|
|
130
|
+
<>
|
|
131
|
+
{loading && (
|
|
132
|
+
<div className={classes.loader}>
|
|
133
|
+
<CircularProgress size={24} />
|
|
134
|
+
</div>
|
|
135
|
+
)}
|
|
136
|
+
<IconButton
|
|
137
|
+
{...getToggleButtonProps()}
|
|
138
|
+
aria-label="toggle menu"
|
|
139
|
+
className={classes.icon}
|
|
140
|
+
hoverOutline={false}
|
|
141
|
+
type="button"
|
|
142
|
+
variant="secondary"
|
|
143
|
+
>
|
|
144
|
+
<PlusIcon />
|
|
145
|
+
</IconButton>
|
|
146
|
+
</>
|
|
147
|
+
),
|
|
148
|
+
}}
|
|
149
|
+
inputProps={{ ref: inputRef, style: { width: inputWidth } }}
|
|
150
|
+
/>
|
|
151
|
+
<Popper
|
|
152
|
+
className={clsx(classes.popper, menuProps.className)}
|
|
153
|
+
open={isOpen}
|
|
154
|
+
anchorEl={anchor.current}
|
|
155
|
+
transition
|
|
156
|
+
placement={popperPlacement}
|
|
157
|
+
>
|
|
158
|
+
{({ TransitionProps, placement }) => (
|
|
159
|
+
<Grow
|
|
160
|
+
{...TransitionProps}
|
|
161
|
+
style={{
|
|
162
|
+
transformOrigin:
|
|
163
|
+
placement === "bottom" ? "left top" : "left bottom",
|
|
164
|
+
}}
|
|
165
|
+
>
|
|
166
|
+
<Paper
|
|
167
|
+
className={classes.dropdown}
|
|
168
|
+
elevation={8}
|
|
169
|
+
style={{ width: anchor.current?.clientWidth }}
|
|
170
|
+
{...menuProps}
|
|
171
|
+
ref={mergeRefs(setAnchor, menuProps.ref)}
|
|
172
|
+
>
|
|
173
|
+
{children({
|
|
174
|
+
choices: filteredChoices,
|
|
175
|
+
highlightedIndex,
|
|
176
|
+
getItemProps,
|
|
177
|
+
inputValue,
|
|
178
|
+
})}
|
|
179
|
+
</Paper>
|
|
180
|
+
</Grow>
|
|
181
|
+
)}
|
|
182
|
+
</Popper>
|
|
183
|
+
</>
|
|
184
|
+
);
|
|
185
|
+
};
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
export const choices = [
|
|
2
|
+
{
|
|
3
|
+
label: "Hazel, Direct Integration Producer",
|
|
4
|
+
value: "hazel, direct integration producer",
|
|
5
|
+
},
|
|
6
|
+
{
|
|
7
|
+
label: "Audra, District Functionality Facilitator",
|
|
8
|
+
value: "audra, district functionality facilitator",
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
label: "Emilie, Product Integration Assistant",
|
|
12
|
+
value: "emilie, product integration assistant",
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
label: "Kyla, Central Interactions Director",
|
|
16
|
+
value: "kyla, central interactions director",
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
label: "Vida, Senior Usability Producer",
|
|
20
|
+
value: "vida, senior usability producer",
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
label: "Lonie, Chief Infrastructure Director",
|
|
24
|
+
value: "lonie, chief infrastructure director",
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
label: "Zola, Lead Operations Producer",
|
|
28
|
+
value: "zola, lead operations producer",
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
label: "Angel, District Operations Orchestrator",
|
|
32
|
+
value: "angel, district operations orchestrator",
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
label: "Glenda, Lead Accountability Technician",
|
|
36
|
+
value: "glenda, lead accountability technician",
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
label: "Kayley, National Web Facilitator",
|
|
40
|
+
value: "kayley, national web facilitator",
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
label: "Kali, National Configuration Planner",
|
|
44
|
+
value: "kali, national configuration planner",
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
label: "Lilian, Internal Branding Planner",
|
|
48
|
+
value: "lilian, internal branding planner",
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
label: "Lamar, Dynamic Integration Executive",
|
|
52
|
+
value: "lamar, dynamic integration executive",
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
label: "Leonel, Central Brand Strategist",
|
|
56
|
+
value: "leonel, central brand strategist",
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
label: "Broderick, Chief Division Liaison",
|
|
60
|
+
value: "broderick, chief division liaison",
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
label: "Kavon, Future Marketing Representative",
|
|
64
|
+
value: "kavon, future marketing representative",
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
label: "Sydnee, Corporate Marketing Liaison",
|
|
68
|
+
value: "sydnee, corporate marketing liaison",
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
label: "Jett, Future Data Specialist",
|
|
72
|
+
value: "jett, future data specialist",
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
label: "Theresia, International Tactics Assistant",
|
|
76
|
+
value: "theresia, international tactics assistant",
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
label: "Cesar, Direct Mobility Director",
|
|
80
|
+
value: "cesar, direct mobility director",
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
label: "Madonna, Investor Assurance Executive",
|
|
84
|
+
value: "madonna, investor assurance executive",
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
label: "Ima, Internal Research Facilitator",
|
|
88
|
+
value: "ima, internal research facilitator",
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
label: "Joanne, Investor Identity Coordinator",
|
|
92
|
+
value: "joanne, investor identity coordinator",
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
label: "Gavin, Future Web Assistant",
|
|
96
|
+
value: "gavin, future web assistant",
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
label: "Maverick, Internal Optimization Assistant",
|
|
100
|
+
value: "maverick, internal optimization assistant",
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
label: "Hudson, Regional Branding Representative",
|
|
104
|
+
value: "hudson, regional branding representative",
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
label: "Brooklyn, Human Paradigm Producer",
|
|
108
|
+
value: "brooklyn, human paradigm producer",
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
label: "Gussie, Future Configuration Supervisor",
|
|
112
|
+
value: "gussie, future configuration supervisor",
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
label: "Michel, Corporate Tactics Orchestrator",
|
|
116
|
+
value: "michel, corporate tactics orchestrator",
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
label: "Maxime, Customer Mobility Analyst",
|
|
120
|
+
value: "maxime, customer mobility analyst",
|
|
121
|
+
},
|
|
122
|
+
];
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./MultipleValueAutocomplete";
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { makeStyles } from "../theme";
|
|
2
|
+
|
|
3
|
+
const useStyles = makeStyles(
|
|
4
|
+
(theme) => ({
|
|
5
|
+
dropdown: {
|
|
6
|
+
maxHeight: 220,
|
|
7
|
+
overflow: "scroll",
|
|
8
|
+
},
|
|
9
|
+
icon: {
|
|
10
|
+
position: "absolute",
|
|
11
|
+
bottom: 4,
|
|
12
|
+
right: 4,
|
|
13
|
+
},
|
|
14
|
+
inputContainer: {
|
|
15
|
+
padding: "23px 80px 10px 12px",
|
|
16
|
+
flexWrap: "wrap",
|
|
17
|
+
gap: theme.spacing(1),
|
|
18
|
+
},
|
|
19
|
+
inputContainerWithChips: {
|
|
20
|
+
paddingTop: 27,
|
|
21
|
+
},
|
|
22
|
+
input: {
|
|
23
|
+
height: "1.1875em",
|
|
24
|
+
minWidth: "3rem",
|
|
25
|
+
padding: 0,
|
|
26
|
+
},
|
|
27
|
+
loader: {
|
|
28
|
+
position: "absolute",
|
|
29
|
+
bottom: theme.spacing(1),
|
|
30
|
+
right: theme.spacing(6),
|
|
31
|
+
},
|
|
32
|
+
popper: {
|
|
33
|
+
marginTop: theme.spacing(1),
|
|
34
|
+
},
|
|
35
|
+
}),
|
|
36
|
+
{ name: "MultipleValueAutocomplete" }
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
export default useStyles;
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import {
|
|
2
|
+
useCombobox,
|
|
3
|
+
UseComboboxGetItemPropsOptions,
|
|
4
|
+
useMultipleSelection,
|
|
5
|
+
} from "downshift";
|
|
6
|
+
import { useCallback, useEffect, useMemo, useRef } from "react";
|
|
7
|
+
|
|
8
|
+
import { SyntheticChangeEvent } from "../../types/utils";
|
|
9
|
+
import { Choice } from "../Filter";
|
|
10
|
+
import { useTextWidth } from "../tools/useTextWidth";
|
|
11
|
+
import { mergeRefs } from "../utils/mergeRefs";
|
|
12
|
+
|
|
13
|
+
export interface UseMultipleValueAutocomplete {
|
|
14
|
+
choices: Choice[];
|
|
15
|
+
enableReinitialize?: boolean;
|
|
16
|
+
name?: string;
|
|
17
|
+
initialValue: Choice[];
|
|
18
|
+
onChange?: (event: SyntheticChangeEvent<string[]>) => void;
|
|
19
|
+
onInputChange?: (value: string) => void;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function useMultipleValueAutocomplete({
|
|
23
|
+
choices,
|
|
24
|
+
enableReinitialize,
|
|
25
|
+
initialValue,
|
|
26
|
+
name,
|
|
27
|
+
onChange,
|
|
28
|
+
onInputChange,
|
|
29
|
+
}: UseMultipleValueAutocomplete) {
|
|
30
|
+
const anchor = useRef<HTMLDivElement>();
|
|
31
|
+
const input = useRef<HTMLInputElement>();
|
|
32
|
+
|
|
33
|
+
const [inputWidth, setInputText] = useTextWidth(
|
|
34
|
+
window
|
|
35
|
+
.getComputedStyle(input.current ?? document.body, null)
|
|
36
|
+
.getPropertyValue("font")
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
const {
|
|
40
|
+
getSelectedItemProps,
|
|
41
|
+
getDropdownProps,
|
|
42
|
+
addSelectedItem,
|
|
43
|
+
removeSelectedItem,
|
|
44
|
+
selectedItems,
|
|
45
|
+
setSelectedItems,
|
|
46
|
+
} = useMultipleSelection({
|
|
47
|
+
initialSelectedItems: initialValue,
|
|
48
|
+
onSelectedItemsChange: ({ selectedItems }) => {
|
|
49
|
+
if (onChange) {
|
|
50
|
+
onChange({
|
|
51
|
+
target: {
|
|
52
|
+
name: name ?? "",
|
|
53
|
+
value: selectedItems!.map((choice) => choice.value),
|
|
54
|
+
},
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
});
|
|
59
|
+
const filteredChoices = useMemo(
|
|
60
|
+
() =>
|
|
61
|
+
choices.filter(
|
|
62
|
+
(choice) =>
|
|
63
|
+
!selectedItems.find(
|
|
64
|
+
(selectedChoice) => selectedChoice.value === choice.value
|
|
65
|
+
)
|
|
66
|
+
),
|
|
67
|
+
[choices, selectedItems]
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
useEffect(() => {
|
|
71
|
+
if (enableReinitialize) setSelectedItems(initialValue);
|
|
72
|
+
}, [initialValue]);
|
|
73
|
+
|
|
74
|
+
const {
|
|
75
|
+
isOpen,
|
|
76
|
+
getToggleButtonProps,
|
|
77
|
+
getLabelProps,
|
|
78
|
+
getMenuProps,
|
|
79
|
+
getInputProps,
|
|
80
|
+
getComboboxProps,
|
|
81
|
+
highlightedIndex,
|
|
82
|
+
getItemProps: baseGetItemProps,
|
|
83
|
+
openMenu,
|
|
84
|
+
inputValue,
|
|
85
|
+
setInputValue,
|
|
86
|
+
selectItem,
|
|
87
|
+
} = useCombobox({
|
|
88
|
+
circularNavigation: false,
|
|
89
|
+
defaultHighlightedIndex: 0,
|
|
90
|
+
items: filteredChoices,
|
|
91
|
+
onInputValueChange: ({ inputValue }) => {
|
|
92
|
+
setInputText(inputValue || "");
|
|
93
|
+
if (onInputChange && inputValue) {
|
|
94
|
+
onInputChange(inputValue);
|
|
95
|
+
}
|
|
96
|
+
},
|
|
97
|
+
onSelectedItemChange: ({ selectedItem }) => {
|
|
98
|
+
if (selectedItem) {
|
|
99
|
+
addSelectedItem(selectedItem);
|
|
100
|
+
setInputValue("");
|
|
101
|
+
}
|
|
102
|
+
},
|
|
103
|
+
stateReducer: (_, actionAndChanges) => {
|
|
104
|
+
const { changes, type } = actionAndChanges;
|
|
105
|
+
switch (type) {
|
|
106
|
+
case useCombobox.stateChangeTypes.InputKeyDownEnter:
|
|
107
|
+
case useCombobox.stateChangeTypes.FunctionSelectItem:
|
|
108
|
+
const index = filteredChoices.findIndex(
|
|
109
|
+
(choice) => choice.value === changes.selectedItem?.value
|
|
110
|
+
);
|
|
111
|
+
return {
|
|
112
|
+
...changes,
|
|
113
|
+
highlightedIndex: index > 0 ? index - 1 : 0,
|
|
114
|
+
isOpen: true,
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
return changes;
|
|
118
|
+
},
|
|
119
|
+
selectedItem: null,
|
|
120
|
+
itemToString: () => "",
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
// Downshift doesn't like portals like popper
|
|
124
|
+
// https://github.com/downshift-js/downshift/issues/287
|
|
125
|
+
const getItemProps = useCallback(
|
|
126
|
+
(options: UseComboboxGetItemPropsOptions<Choice>) => {
|
|
127
|
+
const baseProps = baseGetItemProps(options);
|
|
128
|
+
|
|
129
|
+
return {
|
|
130
|
+
...baseProps,
|
|
131
|
+
onClick: () => selectItem(options.item),
|
|
132
|
+
selected: highlightedIndex === options.index,
|
|
133
|
+
};
|
|
134
|
+
},
|
|
135
|
+
[baseGetItemProps, highlightedIndex]
|
|
136
|
+
);
|
|
137
|
+
|
|
138
|
+
const labelProps = getLabelProps();
|
|
139
|
+
const { ref: comboboxDownshiftRef, ...comboboxProps } = getComboboxProps();
|
|
140
|
+
const { ref: downshiftRef, ...inputProps } = getInputProps({
|
|
141
|
+
...getDropdownProps(),
|
|
142
|
+
onFocus: () => {
|
|
143
|
+
if (!isOpen) {
|
|
144
|
+
input.current?.select();
|
|
145
|
+
openMenu();
|
|
146
|
+
}
|
|
147
|
+
},
|
|
148
|
+
});
|
|
149
|
+
const menuProps = getMenuProps({}, { suppressRefError: true });
|
|
150
|
+
|
|
151
|
+
return {
|
|
152
|
+
anchor,
|
|
153
|
+
comboboxProps,
|
|
154
|
+
filteredChoices,
|
|
155
|
+
getItemProps,
|
|
156
|
+
getSelectedItemProps,
|
|
157
|
+
getToggleButtonProps,
|
|
158
|
+
highlightedIndex,
|
|
159
|
+
inputProps,
|
|
160
|
+
inputRef: mergeRefs(downshiftRef, input),
|
|
161
|
+
inputValue,
|
|
162
|
+
inputWidth,
|
|
163
|
+
isOpen,
|
|
164
|
+
labelProps,
|
|
165
|
+
menuProps,
|
|
166
|
+
ref: mergeRefs(comboboxDownshiftRef, anchor),
|
|
167
|
+
removeSelectedItem,
|
|
168
|
+
selectedItems,
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
export default useMultipleValueAutocomplete;
|
package/src/Savebar/Savebar.tsx
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
|
|
3
3
|
import { Button } from "..";
|
|
4
|
-
import { useActionBar } from "../ActionBar";
|
|
5
4
|
import { ActionBar } from "../ActionBar/ActionBar";
|
|
6
5
|
import {
|
|
7
6
|
ConfirmButton,
|
|
@@ -21,6 +20,7 @@ export interface SavebarProps {
|
|
|
21
20
|
state: ConfirmButtonTransitionState;
|
|
22
21
|
labels: SavebarLabels;
|
|
23
22
|
tooltips?: SavebarTooltips;
|
|
23
|
+
className?: string;
|
|
24
24
|
onCancel: () => void;
|
|
25
25
|
onDelete?: () => void;
|
|
26
26
|
onSubmit: () => void;
|
|
@@ -31,15 +31,15 @@ export const Savebar: React.FC<SavebarProps> = ({
|
|
|
31
31
|
labels,
|
|
32
32
|
tooltips,
|
|
33
33
|
state,
|
|
34
|
+
className,
|
|
34
35
|
onCancel,
|
|
35
36
|
onDelete,
|
|
36
37
|
onSubmit,
|
|
37
38
|
}) => {
|
|
38
39
|
const classes = useStyles();
|
|
39
|
-
const { setDocked } = useActionBar();
|
|
40
40
|
|
|
41
41
|
return (
|
|
42
|
-
<ActionBar state={state} disabled={disabled}>
|
|
42
|
+
<ActionBar state={state} disabled={disabled} className={className}>
|
|
43
43
|
{!!onDelete && (
|
|
44
44
|
<ButtonTooltipDecorator tooltip={tooltips?.delete}>
|
|
45
45
|
<Button
|
|
@@ -70,7 +70,6 @@ export const Savebar: React.FC<SavebarProps> = ({
|
|
|
70
70
|
onClick={onSubmit}
|
|
71
71
|
transitionState={state}
|
|
72
72
|
data-test="button-bar-confirm"
|
|
73
|
-
onTransitionToDefault={() => setDocked(true)}
|
|
74
73
|
/>
|
|
75
74
|
</ButtonTooltipDecorator>
|
|
76
75
|
</ActionBar>
|
package/src/Sidebar/MenuItem.tsx
CHANGED
|
@@ -9,15 +9,27 @@ import React from "react";
|
|
|
9
9
|
import SVG from "react-inlinesvg";
|
|
10
10
|
|
|
11
11
|
import { makeStyles } from "../theme";
|
|
12
|
-
import { SidebarMenuItem } from "./types";
|
|
12
|
+
import { CustomLinkComponent, SidebarMenuItem } from "./types";
|
|
13
|
+
import { getLinkComponent, getLinkProps } from "./utils";
|
|
13
14
|
|
|
14
|
-
export interface
|
|
15
|
+
export interface MenuItemCommonProps {
|
|
15
16
|
activeId: string;
|
|
16
17
|
isMenuShrunk: boolean;
|
|
17
18
|
menuItem: SidebarMenuItem;
|
|
18
|
-
onClick: (menuItem: SidebarMenuItem) => void;
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
+
export type MenuItemProps = MenuItemCommonProps &
|
|
22
|
+
(
|
|
23
|
+
| {
|
|
24
|
+
onClick: (menuItem: SidebarMenuItem) => void;
|
|
25
|
+
linkComponent?: never;
|
|
26
|
+
}
|
|
27
|
+
| {
|
|
28
|
+
onClick?: never;
|
|
29
|
+
linkComponent: CustomLinkComponent;
|
|
30
|
+
}
|
|
31
|
+
);
|
|
32
|
+
|
|
21
33
|
export const menuWidth = 210;
|
|
22
34
|
export const shrunkMenuWidth = 72;
|
|
23
35
|
|
|
@@ -139,23 +151,33 @@ export const MenuItem: React.FC<MenuItemProps> = ({
|
|
|
139
151
|
menuItem,
|
|
140
152
|
isMenuShrunk,
|
|
141
153
|
onClick,
|
|
154
|
+
linkComponent,
|
|
142
155
|
}) => {
|
|
143
156
|
const classes = useStyles({});
|
|
144
157
|
const [open, setOpen] = React.useState(false);
|
|
145
|
-
const anchor = React.useRef<
|
|
158
|
+
const anchor = React.useRef<any>(null);
|
|
146
159
|
|
|
147
160
|
const handleClick = (event: React.MouseEvent, menuItem: SidebarMenuItem) => {
|
|
148
161
|
event.stopPropagation();
|
|
149
162
|
if (menuItem.children) {
|
|
150
163
|
setOpen(true);
|
|
151
164
|
} else {
|
|
152
|
-
onClick
|
|
165
|
+
if (onClick) {
|
|
166
|
+
onClick(menuItem);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
if (menuItem.onClick) {
|
|
170
|
+
menuItem.onClick();
|
|
171
|
+
}
|
|
172
|
+
|
|
153
173
|
setOpen(false);
|
|
154
174
|
}
|
|
155
175
|
};
|
|
156
176
|
|
|
177
|
+
const RootNavComponent = menuItem.children ? "div" : linkComponent ?? "div";
|
|
178
|
+
|
|
157
179
|
return (
|
|
158
|
-
<
|
|
180
|
+
<RootNavComponent
|
|
159
181
|
className={clsx(classes.root, {
|
|
160
182
|
[classes.rootOpen]: open,
|
|
161
183
|
[classes.rootActive]: [
|
|
@@ -165,9 +187,10 @@ export const MenuItem: React.FC<MenuItemProps> = ({
|
|
|
165
187
|
[classes.rootExpanded]: !isMenuShrunk,
|
|
166
188
|
})}
|
|
167
189
|
ref={anchor}
|
|
168
|
-
onClick={(event) => handleClick(event, menuItem)}
|
|
190
|
+
onClick={(event: React.MouseEvent) => handleClick(event, menuItem)}
|
|
191
|
+
{...getLinkProps(menuItem)}
|
|
169
192
|
>
|
|
170
|
-
<
|
|
193
|
+
<span
|
|
171
194
|
className={classes.menuItemBtn}
|
|
172
195
|
data-test="menu-item-label"
|
|
173
196
|
data-test-id={menuItem.id}
|
|
@@ -184,7 +207,7 @@ export const MenuItem: React.FC<MenuItemProps> = ({
|
|
|
184
207
|
>
|
|
185
208
|
{menuItem.label}
|
|
186
209
|
</Typography>
|
|
187
|
-
</
|
|
210
|
+
</span>
|
|
188
211
|
{menuItem.children && (
|
|
189
212
|
<Popper
|
|
190
213
|
className={clsx(classes.popper, {
|
|
@@ -199,14 +222,12 @@ export const MenuItem: React.FC<MenuItemProps> = ({
|
|
|
199
222
|
<Paper className={classes.paper}>
|
|
200
223
|
{menuItem.children.map((subMenuItem) => {
|
|
201
224
|
if (subMenuItem.url || subMenuItem.children) {
|
|
202
|
-
const linkProps = subMenuItem
|
|
203
|
-
? { href: subMenuItem.url, target: "_blank" }
|
|
204
|
-
: {};
|
|
225
|
+
const linkProps = getLinkProps(subMenuItem);
|
|
205
226
|
|
|
206
227
|
return (
|
|
207
228
|
<MuiMenuItem
|
|
208
229
|
aria-label={subMenuItem.ariaLabel}
|
|
209
|
-
component={subMenuItem
|
|
230
|
+
component={getLinkComponent(subMenuItem, linkComponent)}
|
|
210
231
|
className={clsx(classes.label, classes.subMenuLabel)}
|
|
211
232
|
key={subMenuItem.url}
|
|
212
233
|
onClick={(event: React.MouseEvent) =>
|
|
@@ -236,7 +257,7 @@ export const MenuItem: React.FC<MenuItemProps> = ({
|
|
|
236
257
|
</ClickAwayListener>
|
|
237
258
|
</Popper>
|
|
238
259
|
)}
|
|
239
|
-
</
|
|
260
|
+
</RootNavComponent>
|
|
240
261
|
);
|
|
241
262
|
};
|
|
242
263
|
|