@sproutsocial/seeds-react-menu 1.1.1 → 1.2.1
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/.turbo/turbo-build.log +10 -10
- package/CHANGELOG.md +20 -0
- package/dist/esm/index.js +110 -53
- package/dist/esm/index.js.map +1 -1
- package/dist/index.d.mts +4 -2
- package/dist/index.d.ts +4 -2
- package/dist/index.js +123 -69
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/Autocomplete/Autocomplete.stories.tsx +30 -1
- package/src/Autocomplete/AutocompleteSelect.tsx +24 -0
- package/src/Autocomplete/SingleAutocomplete.tsx +1 -1
- package/src/Autocomplete/index.ts +1 -0
- package/src/MenuContent/styles.tsx +5 -1
- package/src/MenuContext.tsx +3 -1
- package/src/MenuRoot/MenuRoot.tsx +39 -10
- package/src/NestedMenu/NestedMenu.stories.tsx +16 -8
- package/src/NestedMenu/NestedMenu.tsx +3 -0
- package/src/NestedMenu/NestedMenuItem.tsx +11 -8
package/package.json
CHANGED
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
MenuLabel,
|
|
13
13
|
MenuToggleButton,
|
|
14
14
|
type TypeInternalItemProps,
|
|
15
|
+
AutocompleteSelect,
|
|
15
16
|
} from "../index";
|
|
16
17
|
// eslint-disable-next-line no-restricted-imports
|
|
17
18
|
import { TEST_BOOKS } from "../__fixtures__/menu-test-data";
|
|
@@ -107,7 +108,7 @@ export const AutocompleteTextWithButtonExample: Story = {
|
|
|
107
108
|
Choose a book
|
|
108
109
|
</MenuLabel>
|
|
109
110
|
<Box display="flex" alignItems="center" gap={200}>
|
|
110
|
-
<AutocompleteInput />
|
|
111
|
+
<AutocompleteInput style={{ flex: 2 }} />
|
|
111
112
|
<MenuToggleButton>
|
|
112
113
|
<Icon name="chevron-down" aria-label="Open Menu" />
|
|
113
114
|
</MenuToggleButton>
|
|
@@ -161,6 +162,34 @@ export const AutocompleteTokenInputExample: Story = {
|
|
|
161
162
|
},
|
|
162
163
|
};
|
|
163
164
|
|
|
165
|
+
export const AutocompleteSelectExample: Story = {
|
|
166
|
+
name: "Autocomplete Select",
|
|
167
|
+
render: (args) => {
|
|
168
|
+
return (
|
|
169
|
+
<Autocomplete
|
|
170
|
+
itemToString={(item) => (item ? TEST_BOOKS[item.index].title : "")}
|
|
171
|
+
menuToggleElement={
|
|
172
|
+
<>
|
|
173
|
+
<MenuLabel htmlFor="my-combobox" mb={300}>
|
|
174
|
+
Choose a book
|
|
175
|
+
</MenuLabel>
|
|
176
|
+
<AutocompleteSelect />
|
|
177
|
+
</>
|
|
178
|
+
}
|
|
179
|
+
{...(args as object)}
|
|
180
|
+
>
|
|
181
|
+
<MenuContent>
|
|
182
|
+
{TEST_BOOKS.map(({ id, title }) => (
|
|
183
|
+
<MenuItem key={id} id={id}>
|
|
184
|
+
{title}
|
|
185
|
+
</MenuItem>
|
|
186
|
+
))}
|
|
187
|
+
</MenuContent>
|
|
188
|
+
</Autocomplete>
|
|
189
|
+
);
|
|
190
|
+
},
|
|
191
|
+
};
|
|
192
|
+
|
|
164
193
|
const TestControlledSingleAutocomplete = ({
|
|
165
194
|
...args
|
|
166
195
|
}: CustomAutocompleteArgs) => {
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import Icon from "@sproutsocial/seeds-react-icon";
|
|
2
|
+
import React from "react";
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
AutocompleteInput,
|
|
6
|
+
type TypeAutocompleteInputProps,
|
|
7
|
+
} from "./AutocompleteInput";
|
|
8
|
+
import styled from "styled-components";
|
|
9
|
+
import { Accessory } from "@sproutsocial/seeds-react-input";
|
|
10
|
+
|
|
11
|
+
const StyledAutocompleteInput = styled(AutocompleteInput)`
|
|
12
|
+
${Accessory} {
|
|
13
|
+
pointer-events: none;
|
|
14
|
+
}
|
|
15
|
+
`;
|
|
16
|
+
|
|
17
|
+
export const AutocompleteSelect = (props: TypeAutocompleteInputProps) => {
|
|
18
|
+
return (
|
|
19
|
+
<StyledAutocompleteInput
|
|
20
|
+
elemAfter={<Icon name="chevron-down" />}
|
|
21
|
+
{...props}
|
|
22
|
+
/>
|
|
23
|
+
);
|
|
24
|
+
};
|
package/src/MenuContext.tsx
CHANGED
|
@@ -125,7 +125,9 @@ export interface TypeMenuProviderProps {
|
|
|
125
125
|
}
|
|
126
126
|
|
|
127
127
|
export interface TypeMenuToggleContext extends TypeMenuContext {
|
|
128
|
-
ref?:
|
|
128
|
+
ref?:
|
|
129
|
+
| React.RefObject<any>
|
|
130
|
+
| ((arg0: React.ElementRef<any> | HTMLElement) => void);
|
|
129
131
|
toggle?: () => void;
|
|
130
132
|
show?: () => void;
|
|
131
133
|
hide?: () => void;
|
|
@@ -1,6 +1,12 @@
|
|
|
1
|
-
import React, {
|
|
1
|
+
import React, {
|
|
2
|
+
useCallback,
|
|
3
|
+
useEffect,
|
|
4
|
+
useLayoutEffect,
|
|
5
|
+
type RefObject,
|
|
6
|
+
} from "react";
|
|
2
7
|
import styled from "styled-components";
|
|
3
8
|
import { Popout, type TypePopoutProps } from "@sproutsocial/seeds-react-popout";
|
|
9
|
+
import { mergeRefs } from "@sproutsocial/seeds-react-utilities";
|
|
4
10
|
|
|
5
11
|
import {
|
|
6
12
|
MenuContentProvider,
|
|
@@ -111,7 +117,7 @@ export const MenuRoot = ({
|
|
|
111
117
|
children,
|
|
112
118
|
menuToggleElement,
|
|
113
119
|
popoutProps: { placement = "bottom", focusLockProps, ...popoutProps } = {},
|
|
114
|
-
width
|
|
120
|
+
width,
|
|
115
121
|
...contextProps
|
|
116
122
|
}: TypeMenuRootProps) => {
|
|
117
123
|
const {
|
|
@@ -121,17 +127,27 @@ export const MenuRoot = ({
|
|
|
121
127
|
inputValue,
|
|
122
128
|
hiddenItemsCount = 0,
|
|
123
129
|
items,
|
|
130
|
+
getMenuProps,
|
|
124
131
|
} = contextProps;
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
132
|
+
|
|
133
|
+
const toggleElementRef = React.useRef<HTMLElement>(null);
|
|
134
|
+
const [finalWidth, setFinalWidth] = React.useState<typeof width>();
|
|
135
|
+
|
|
136
|
+
const min_width = 200;
|
|
137
|
+
|
|
138
|
+
useLayoutEffect(() => {
|
|
139
|
+
if (width) {
|
|
140
|
+
setFinalWidth(width);
|
|
141
|
+
} else if (toggleElementRef.current?.offsetWidth) {
|
|
142
|
+
const toggleWidth = toggleElementRef.current.offsetWidth;
|
|
143
|
+
setFinalWidth(Math.max(toggleWidth, min_width));
|
|
144
|
+
} else {
|
|
145
|
+
setFinalWidth(min_width);
|
|
146
|
+
}
|
|
147
|
+
}, [width, toggleElementRef.current?.offsetWidth]);
|
|
131
148
|
|
|
132
149
|
useEffect(() => {
|
|
133
150
|
if (!inputValue) return;
|
|
134
|
-
|
|
135
151
|
if (items && hiddenItemsCount >= items.length && isOpen) {
|
|
136
152
|
closeMenu?.();
|
|
137
153
|
}
|
|
@@ -139,6 +155,9 @@ export const MenuRoot = ({
|
|
|
139
155
|
|
|
140
156
|
const menuContext = useMenu(contextProps);
|
|
141
157
|
|
|
158
|
+
// Hack to suppress error: "downshift: You forgot to call the getMenuProps getter function on your component / element."
|
|
159
|
+
getMenuProps?.({}, { suppressRefError: true });
|
|
160
|
+
|
|
142
161
|
return (
|
|
143
162
|
<MenuPopout
|
|
144
163
|
placement={placement}
|
|
@@ -150,10 +169,20 @@ export const MenuRoot = ({
|
|
|
150
169
|
{children}
|
|
151
170
|
</MenuContentProvider>
|
|
152
171
|
}
|
|
172
|
+
width={finalWidth}
|
|
153
173
|
{...popoutProps}
|
|
154
174
|
>
|
|
155
175
|
{(toggleProps) => (
|
|
156
|
-
<MenuToggleProvider
|
|
176
|
+
<MenuToggleProvider
|
|
177
|
+
menuContext={{
|
|
178
|
+
...menuContext,
|
|
179
|
+
...toggleProps,
|
|
180
|
+
ref: mergeRefs<HTMLElement>(
|
|
181
|
+
toggleProps.ref,
|
|
182
|
+
toggleElementRef
|
|
183
|
+
) as RefObject<HTMLElement>,
|
|
184
|
+
}}
|
|
185
|
+
>
|
|
157
186
|
{menuToggleElement}
|
|
158
187
|
</MenuToggleProvider>
|
|
159
188
|
)}
|
|
@@ -153,14 +153,22 @@ export const NestedMenuMultiLevelStory: Story = {
|
|
|
153
153
|
render: (args) => {
|
|
154
154
|
return (
|
|
155
155
|
<MotionConfig reducedMotion="user">
|
|
156
|
-
<
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
156
|
+
<Box
|
|
157
|
+
display="flex"
|
|
158
|
+
alignItems="center"
|
|
159
|
+
justifyContent="center"
|
|
160
|
+
height="100vh"
|
|
161
|
+
width="100vw"
|
|
162
|
+
>
|
|
163
|
+
<NestedMenu
|
|
164
|
+
initialMenuId="root"
|
|
165
|
+
menus={menus}
|
|
166
|
+
backArrowLabel="Go Back"
|
|
167
|
+
menuToggleElement={
|
|
168
|
+
<MenuToggleButton width="200px">Open Menu</MenuToggleButton>
|
|
169
|
+
}
|
|
170
|
+
/>
|
|
171
|
+
</Box>
|
|
164
172
|
</MotionConfig>
|
|
165
173
|
);
|
|
166
174
|
},
|
|
@@ -85,6 +85,9 @@ export const NestedMenu = <
|
|
|
85
85
|
onIsOpenChange: onNestedIsOpenChange,
|
|
86
86
|
};
|
|
87
87
|
|
|
88
|
+
// Hack to suppress error: "downshift: You forgot to call the getMenuProps getter function on your component / element."
|
|
89
|
+
rootMenuContextProps.getMenuProps?.({}, { suppressRefError: true });
|
|
90
|
+
|
|
88
91
|
/**
|
|
89
92
|
* Handling the state of the NestedMenu content
|
|
90
93
|
*/
|
|
@@ -13,14 +13,17 @@ const NestedMenuItem = ({
|
|
|
13
13
|
const { setSelectedMenuId } = useNestedMenuContext();
|
|
14
14
|
// This method of passing onClick is necessary because passing undefined for onClick
|
|
15
15
|
// behaves differently from not passing anything at all.
|
|
16
|
-
const onClickProps =
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
16
|
+
const onClickProps =
|
|
17
|
+
childMenuId || onClickProp
|
|
18
|
+
? {
|
|
19
|
+
onClick: (e) => {
|
|
20
|
+
onClickProp?.(e);
|
|
21
|
+
if (childMenuId) {
|
|
22
|
+
setSelectedMenuId?.(childMenuId);
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
}
|
|
26
|
+
: {};
|
|
24
27
|
|
|
25
28
|
return (
|
|
26
29
|
<MenuItem {...onClickProps} {...rest}>
|