@basic-ui/core 0.0.45 → 0.0.47
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/build/cjs/index.js +85 -10
- package/build/cjs/index.js.map +1 -1
- package/build/esm/Menu/ContextMenuTrigger.d.ts +11 -0
- package/build/esm/Menu/ContextMenuTrigger.js +51 -0
- package/build/esm/Menu/ContextMenuTrigger.js.map +1 -0
- package/build/esm/Menu/Menu.js +11 -2
- package/build/esm/Menu/Menu.js.map +1 -1
- package/build/esm/Menu/MenuItem.js +0 -1
- package/build/esm/Menu/MenuItem.js.map +1 -1
- package/build/esm/Menu/MenuList.js +23 -6
- package/build/esm/Menu/MenuList.js.map +1 -1
- package/build/esm/Menu/MenuPopover.js +6 -2
- package/build/esm/Menu/MenuPopover.js.map +1 -1
- package/build/esm/Menu/context.d.ts +5 -1
- package/build/esm/Menu/context.js.map +1 -1
- package/build/esm/Menu/fixtures/countryList.d.ts +1 -0
- package/build/esm/Menu/fixtures/countryList.js +2 -0
- package/build/esm/Menu/fixtures/countryList.js.map +1 -0
- package/build/esm/Menu/index.d.ts +1 -0
- package/build/esm/Menu/index.js +1 -0
- package/build/esm/Menu/index.js.map +1 -1
- package/build/esm/hooks/useControlledState.js +3 -1
- package/build/esm/hooks/useControlledState.js.map +1 -1
- package/build/tsconfig-build.tsbuildinfo +1 -1
- package/package.json +2 -2
- package/src/Menu/ContextMenu.story.tsx +73 -0
- package/src/Menu/ContextMenuTrigger.tsx +63 -0
- package/src/Menu/Menu.story.tsx +2 -2
- package/src/Menu/Menu.tsx +14 -1
- package/src/Menu/MenuComplex.story.tsx +58 -0
- package/src/Menu/MenuItem.tsx +0 -1
- package/src/Menu/MenuList.tsx +38 -9
- package/src/Menu/MenuPopover.tsx +9 -2
- package/src/Menu/context.ts +5 -1
- package/src/Menu/fixtures/countryList.ts +198 -0
- package/src/Menu/index.ts +1 -0
- package/src/hooks/useControlledState.ts +7 -1
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import type { MouseEvent, ReactNode } from 'react';
|
|
2
|
+
|
|
3
|
+
import { Menu, MenuButton, MenuItem, MenuList, MenuPopover } from '.';
|
|
4
|
+
import { countryList } from './fixtures/countryList';
|
|
5
|
+
import './styles.css';
|
|
6
|
+
|
|
7
|
+
export default {
|
|
8
|
+
title: 'components/Menu/Complex',
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
const Wrapper = ({ children }: { children: ReactNode }) => {
|
|
12
|
+
const handleLinkClick = (e: MouseEvent<HTMLAnchorElement>) => {
|
|
13
|
+
console.log('Clicked ' + e.currentTarget.innerText);
|
|
14
|
+
e.preventDefault();
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
return (
|
|
18
|
+
<div
|
|
19
|
+
style={{
|
|
20
|
+
boxSizing: 'border-box',
|
|
21
|
+
display: 'flex',
|
|
22
|
+
alignItems: 'flex-start',
|
|
23
|
+
justifyContent: 'space-around',
|
|
24
|
+
width: '100%',
|
|
25
|
+
position: 'relative',
|
|
26
|
+
}}
|
|
27
|
+
>
|
|
28
|
+
<a href="#" onClick={handleLinkClick}>
|
|
29
|
+
Link 1
|
|
30
|
+
</a>
|
|
31
|
+
<div style={{ minHeight: 120, width: 100 }}>{children}</div>
|
|
32
|
+
<a href="#" onClick={handleLinkClick}>
|
|
33
|
+
Link 2
|
|
34
|
+
</a>
|
|
35
|
+
</div>
|
|
36
|
+
);
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const MenuControlled = () => {
|
|
40
|
+
return (
|
|
41
|
+
<Menu>
|
|
42
|
+
<MenuButton>Click me</MenuButton>
|
|
43
|
+
<MenuPopover>
|
|
44
|
+
<MenuList>
|
|
45
|
+
{countryList.map((country) => (
|
|
46
|
+
<MenuItem key={country}>{country}</MenuItem>
|
|
47
|
+
))}
|
|
48
|
+
</MenuList>
|
|
49
|
+
</MenuPopover>
|
|
50
|
+
</Menu>
|
|
51
|
+
);
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
export const CountryList = () => (
|
|
55
|
+
<Wrapper>
|
|
56
|
+
<MenuControlled />
|
|
57
|
+
</Wrapper>
|
|
58
|
+
);
|
package/src/Menu/MenuItem.tsx
CHANGED
package/src/Menu/MenuList.tsx
CHANGED
|
@@ -33,8 +33,17 @@ export const MenuList = forwardRef<HTMLUListElement, MenuListProps>(
|
|
|
33
33
|
...otherProps
|
|
34
34
|
} = props;
|
|
35
35
|
|
|
36
|
-
const
|
|
37
|
-
|
|
36
|
+
const itemSearchStr = useRef('');
|
|
37
|
+
const itemSearchClearTimeout = useRef<ReturnType<typeof setTimeout>>();
|
|
38
|
+
|
|
39
|
+
const {
|
|
40
|
+
menuListIdRef,
|
|
41
|
+
buttonRef,
|
|
42
|
+
onChange,
|
|
43
|
+
openWithArrowKeyRef,
|
|
44
|
+
open,
|
|
45
|
+
isContextMenu,
|
|
46
|
+
} = useMenuContext();
|
|
38
47
|
|
|
39
48
|
const [navigationItem, setNavigationItem] = useState<
|
|
40
49
|
HTMLElement | undefined
|
|
@@ -89,10 +98,12 @@ export const MenuList = forwardRef<HTMLUListElement, MenuListProps>(
|
|
|
89
98
|
useOnClickOutside(
|
|
90
99
|
menuListRef,
|
|
91
100
|
(e) => {
|
|
101
|
+
console.log(isContextMenu.current);
|
|
92
102
|
if (
|
|
93
|
-
|
|
94
|
-
e.target
|
|
95
|
-
|
|
103
|
+
isContextMenu.current ||
|
|
104
|
+
(e.target instanceof HTMLElement &&
|
|
105
|
+
e.target !== buttonRef.current &&
|
|
106
|
+
!buttonRef.current?.contains(e.target))
|
|
96
107
|
) {
|
|
97
108
|
onChange && onChange(e as any, false);
|
|
98
109
|
}
|
|
@@ -107,6 +118,7 @@ export const MenuList = forwardRef<HTMLUListElement, MenuListProps>(
|
|
|
107
118
|
case 'Tab': {
|
|
108
119
|
onChange && onChange(e, false);
|
|
109
120
|
e.preventDefault(); // prevents focusing on next element, because we will be handling it
|
|
121
|
+
itemSearchStr.current = '';
|
|
110
122
|
buttonRef.current?.focus();
|
|
111
123
|
break;
|
|
112
124
|
}
|
|
@@ -115,6 +127,7 @@ export const MenuList = forwardRef<HTMLUListElement, MenuListProps>(
|
|
|
115
127
|
case 'ArrowDown':
|
|
116
128
|
case 'ArrowUp':
|
|
117
129
|
e.preventDefault();
|
|
130
|
+
itemSearchStr.current = '';
|
|
118
131
|
const allItems = scope ? scope.current.queryAllNodes(queryScope) : [];
|
|
119
132
|
const currentIndex = allItems.findIndex((e) => e === navigationItem);
|
|
120
133
|
if (allItems.length === 0) {
|
|
@@ -141,23 +154,39 @@ export const MenuList = forwardRef<HTMLUListElement, MenuListProps>(
|
|
|
141
154
|
onNavigate && onNavigate(allItems[nextIndex]);
|
|
142
155
|
break;
|
|
143
156
|
default: {
|
|
144
|
-
if (e.key.length === 1) {
|
|
157
|
+
if (e.key.length === 1 && !e.ctrlKey && !e.altKey) {
|
|
145
158
|
// A-Z
|
|
146
159
|
e.preventDefault();
|
|
160
|
+
|
|
161
|
+
if (
|
|
162
|
+
itemSearchStr.current.length === 0 ||
|
|
163
|
+
itemSearchStr.current.slice(-1) !== e.key
|
|
164
|
+
) {
|
|
165
|
+
itemSearchStr.current = itemSearchStr.current + e.key;
|
|
166
|
+
}
|
|
167
|
+
clearTimeout(itemSearchClearTimeout.current as any);
|
|
168
|
+
itemSearchClearTimeout.current = setTimeout(() => {
|
|
169
|
+
itemSearchStr.current = '';
|
|
170
|
+
}, 500);
|
|
171
|
+
|
|
147
172
|
const allItems = scope
|
|
148
173
|
? scope.current.queryAllNodes(queryScope)
|
|
149
174
|
: [];
|
|
150
175
|
const currentIndex = allItems.findIndex(
|
|
151
176
|
(e) => e === navigationItem
|
|
152
177
|
);
|
|
153
|
-
const
|
|
178
|
+
const searchStr = itemSearchStr.current;
|
|
154
179
|
let nextIndex = -1;
|
|
155
|
-
for (
|
|
180
|
+
for (
|
|
181
|
+
let i = searchStr.length === 1 ? 1 : 0;
|
|
182
|
+
i < allItems.length;
|
|
183
|
+
i++
|
|
184
|
+
) {
|
|
156
185
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
157
186
|
const idx = getCircularIndex(currentIndex + i, allItems.length)!;
|
|
158
187
|
const dom = allItems[idx];
|
|
159
188
|
const domText = dom.innerText.toLowerCase();
|
|
160
|
-
if (domText.length > 0 && domText.
|
|
189
|
+
if (domText.length > 0 && domText.startsWith(searchStr)) {
|
|
161
190
|
nextIndex = idx;
|
|
162
191
|
break;
|
|
163
192
|
}
|
package/src/Menu/MenuPopover.tsx
CHANGED
|
@@ -14,14 +14,21 @@ export interface MenuPopoverProps extends Omit<PopperProps, 'anchorEl'> {
|
|
|
14
14
|
export const MenuPopover = forwardRef<HTMLDivElement, MenuPopoverProps>(
|
|
15
15
|
function MenuPopover(props, forwardedRef) {
|
|
16
16
|
const { as = 'div', ...otherProps } = props;
|
|
17
|
-
const { buttonRef, open } = useMenuContext();
|
|
17
|
+
const { buttonRef, open, offsetFn, isContextMenu } = useMenuContext();
|
|
18
18
|
|
|
19
19
|
if (!open) {
|
|
20
20
|
return null;
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
return (
|
|
24
|
-
<Popper
|
|
24
|
+
<Popper
|
|
25
|
+
as={as}
|
|
26
|
+
ref={forwardedRef}
|
|
27
|
+
anchorEl={buttonRef}
|
|
28
|
+
offsetFn={offsetFn}
|
|
29
|
+
placement={isContextMenu.current ? 'bottom-start' : undefined}
|
|
30
|
+
{...otherProps}
|
|
31
|
+
/>
|
|
25
32
|
);
|
|
26
33
|
}
|
|
27
34
|
);
|
package/src/Menu/context.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { OffsetsFunction } from '@popperjs/core/lib/modifiers/offset';
|
|
1
2
|
import type {
|
|
2
3
|
KeyboardEvent,
|
|
3
4
|
MouseEvent,
|
|
@@ -10,7 +11,7 @@ export type ItemObject = { text: string; value: any; id: string | undefined };
|
|
|
10
11
|
|
|
11
12
|
// MenuRoot
|
|
12
13
|
export interface MenuContextProps {
|
|
13
|
-
buttonRef: MutableRefObject<
|
|
14
|
+
buttonRef: MutableRefObject<HTMLElement | null>;
|
|
14
15
|
menuListIdRef: MutableRefObject<undefined | string>;
|
|
15
16
|
openWithArrowKeyRef: MutableRefObject<string | null>;
|
|
16
17
|
onChange?: (
|
|
@@ -21,6 +22,9 @@ export interface MenuContextProps {
|
|
|
21
22
|
isOpen: boolean
|
|
22
23
|
) => void;
|
|
23
24
|
open: boolean;
|
|
25
|
+
offset: MutableRefObject<[number, number]>;
|
|
26
|
+
offsetFn: OffsetsFunction;
|
|
27
|
+
isContextMenu: MutableRefObject<boolean>;
|
|
24
28
|
}
|
|
25
29
|
|
|
26
30
|
const menuContext = createContext<MenuContextProps>(null as any);
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
export const countryList = [
|
|
2
|
+
'Afghanistan',
|
|
3
|
+
'Albania',
|
|
4
|
+
'Algeria',
|
|
5
|
+
'Andorra',
|
|
6
|
+
'Angola',
|
|
7
|
+
'Antigua and Barbuda',
|
|
8
|
+
'Argentina',
|
|
9
|
+
'Armenia',
|
|
10
|
+
'Australia',
|
|
11
|
+
'Austria',
|
|
12
|
+
'Azerbaijan',
|
|
13
|
+
'Bahamas',
|
|
14
|
+
'Bahrain',
|
|
15
|
+
'Bangladesh',
|
|
16
|
+
'Barbados',
|
|
17
|
+
'Belarus',
|
|
18
|
+
'Belgium',
|
|
19
|
+
'Belize',
|
|
20
|
+
'Benin',
|
|
21
|
+
'Bhutan',
|
|
22
|
+
'Bolivia',
|
|
23
|
+
'Bosnia and Herzegovina',
|
|
24
|
+
'Botswana',
|
|
25
|
+
'Brazil',
|
|
26
|
+
'Brunei',
|
|
27
|
+
'Bulgaria',
|
|
28
|
+
'Burkina Faso',
|
|
29
|
+
'Burundi',
|
|
30
|
+
"Côte d'Ivoire",
|
|
31
|
+
'Cabo Verde',
|
|
32
|
+
'Cambodia',
|
|
33
|
+
'Cameroon',
|
|
34
|
+
'Canada',
|
|
35
|
+
'Central African Republic',
|
|
36
|
+
'Central American Republic',
|
|
37
|
+
'Chad',
|
|
38
|
+
'Chile',
|
|
39
|
+
'China',
|
|
40
|
+
'Colombia',
|
|
41
|
+
'Comoros',
|
|
42
|
+
'Congo (Congo-Brazzaville)',
|
|
43
|
+
'Costa Rica',
|
|
44
|
+
'Croatia',
|
|
45
|
+
'Cuba',
|
|
46
|
+
'Cyprus',
|
|
47
|
+
'Czechia (Czech Republic)',
|
|
48
|
+
'Democratic Republic of the Congo',
|
|
49
|
+
'Denmark',
|
|
50
|
+
'Djibouti',
|
|
51
|
+
'Dominica',
|
|
52
|
+
'Dominican Republic',
|
|
53
|
+
'Ecuador',
|
|
54
|
+
'Egypt',
|
|
55
|
+
'El Salvador',
|
|
56
|
+
'Equatorial Guinea',
|
|
57
|
+
'Eritrea',
|
|
58
|
+
'Estonia',
|
|
59
|
+
'Eswatini (fmr. "Swaziland")',
|
|
60
|
+
'Ethiopia',
|
|
61
|
+
'Fiji',
|
|
62
|
+
'Finland',
|
|
63
|
+
'France',
|
|
64
|
+
'Gabon',
|
|
65
|
+
'Gambia',
|
|
66
|
+
'Georgia',
|
|
67
|
+
'Germany',
|
|
68
|
+
'Ghana',
|
|
69
|
+
'Greece',
|
|
70
|
+
'Grenada',
|
|
71
|
+
'Guatemala',
|
|
72
|
+
'Guinea',
|
|
73
|
+
'Guinea-Bissau',
|
|
74
|
+
'Guyana',
|
|
75
|
+
'Haiti',
|
|
76
|
+
'Holy See',
|
|
77
|
+
'Honduras',
|
|
78
|
+
'Hungary',
|
|
79
|
+
'Iceland',
|
|
80
|
+
'India',
|
|
81
|
+
'Indonesia',
|
|
82
|
+
'Iran',
|
|
83
|
+
'Iraq',
|
|
84
|
+
'Ireland',
|
|
85
|
+
'Israel',
|
|
86
|
+
'Italy',
|
|
87
|
+
'Jamaica',
|
|
88
|
+
'Japan',
|
|
89
|
+
'Jordan',
|
|
90
|
+
'Kazakhstan',
|
|
91
|
+
'Kenya',
|
|
92
|
+
'Kiribati',
|
|
93
|
+
'Kuwait',
|
|
94
|
+
'Kyrgyzstan',
|
|
95
|
+
'Laos',
|
|
96
|
+
'Latvia',
|
|
97
|
+
'Lebanon',
|
|
98
|
+
'Lesotho',
|
|
99
|
+
'Liberia',
|
|
100
|
+
'Libya',
|
|
101
|
+
'Liechtenstein',
|
|
102
|
+
'Lithuania',
|
|
103
|
+
'Luxembourg',
|
|
104
|
+
'Madagascar',
|
|
105
|
+
'Malawi',
|
|
106
|
+
'Malaysia',
|
|
107
|
+
'Maldives',
|
|
108
|
+
'Mali',
|
|
109
|
+
'Malta',
|
|
110
|
+
'Marshall Islands',
|
|
111
|
+
'Mauritania',
|
|
112
|
+
'Mauritius',
|
|
113
|
+
'Mexico',
|
|
114
|
+
'Micronesia',
|
|
115
|
+
'Moldova',
|
|
116
|
+
'Monaco',
|
|
117
|
+
'Mongolia',
|
|
118
|
+
'Montenegro',
|
|
119
|
+
'Morocco',
|
|
120
|
+
'Mozambique',
|
|
121
|
+
'Myanmar (formerly Burma)',
|
|
122
|
+
'Namibia',
|
|
123
|
+
'Nauru',
|
|
124
|
+
'Nepal',
|
|
125
|
+
'Netherlands',
|
|
126
|
+
'New Zealand',
|
|
127
|
+
'Nicaragua',
|
|
128
|
+
'Niger',
|
|
129
|
+
'Nigeria',
|
|
130
|
+
'North Korea',
|
|
131
|
+
'North Macedonia',
|
|
132
|
+
'Norway',
|
|
133
|
+
'Oman',
|
|
134
|
+
'Pakistan',
|
|
135
|
+
'Palau',
|
|
136
|
+
'Palestine State',
|
|
137
|
+
'Panama',
|
|
138
|
+
'Papua New Guinea',
|
|
139
|
+
'Paraguay',
|
|
140
|
+
'Peru',
|
|
141
|
+
'Philippines',
|
|
142
|
+
'Poland',
|
|
143
|
+
'Portugal',
|
|
144
|
+
'Qatar',
|
|
145
|
+
'Romania',
|
|
146
|
+
'Russia',
|
|
147
|
+
'Rwanda',
|
|
148
|
+
'Saint Kitts and Nevis',
|
|
149
|
+
'Saint Lucia',
|
|
150
|
+
'Saint Vincent and the Grenadines',
|
|
151
|
+
'Samoa',
|
|
152
|
+
'San Marino',
|
|
153
|
+
'Sao Tome and Principe',
|
|
154
|
+
'Saudi Arabia',
|
|
155
|
+
'Senegal',
|
|
156
|
+
'Serbia',
|
|
157
|
+
'Seychelles',
|
|
158
|
+
'Sierra Leone',
|
|
159
|
+
'Singapore',
|
|
160
|
+
'Slovakia',
|
|
161
|
+
'Slovenia',
|
|
162
|
+
'Solomon Islands',
|
|
163
|
+
'Somalia',
|
|
164
|
+
'South Africa',
|
|
165
|
+
'South Korea',
|
|
166
|
+
'South Sudan',
|
|
167
|
+
'Spain',
|
|
168
|
+
'Sri Lanka',
|
|
169
|
+
'Sudan',
|
|
170
|
+
'Suriname',
|
|
171
|
+
'Sweden',
|
|
172
|
+
'Switzerland',
|
|
173
|
+
'Syria',
|
|
174
|
+
'Tajikistan',
|
|
175
|
+
'Tanzania',
|
|
176
|
+
'Thailand',
|
|
177
|
+
'Timor-Leste',
|
|
178
|
+
'Togo',
|
|
179
|
+
'Tonga',
|
|
180
|
+
'Trinidad and Tobago',
|
|
181
|
+
'Tunisia',
|
|
182
|
+
'Turkey',
|
|
183
|
+
'Turkmenistan',
|
|
184
|
+
'Tuvalu',
|
|
185
|
+
'Uganda',
|
|
186
|
+
'Ukraine',
|
|
187
|
+
'United Arab Emirates',
|
|
188
|
+
'United Kingdom',
|
|
189
|
+
'United States of America',
|
|
190
|
+
'Uruguay',
|
|
191
|
+
'Uzbekistan',
|
|
192
|
+
'Vanuatu',
|
|
193
|
+
'Venezuela',
|
|
194
|
+
'Vietnam',
|
|
195
|
+
'Yemen',
|
|
196
|
+
'Zambia',
|
|
197
|
+
'Zimbabwe',
|
|
198
|
+
];
|
package/src/Menu/index.ts
CHANGED
|
@@ -18,11 +18,17 @@ export function useControlledState<
|
|
|
18
18
|
): [V, CustomEventHandler<E, H>] {
|
|
19
19
|
const isControlled = valueProp !== undefined;
|
|
20
20
|
const wasControlled = useRef(isControlled);
|
|
21
|
+
const hasWarned = useRef(false);
|
|
21
22
|
const [valueState, setValueState] = useState<V>(defaultValue);
|
|
22
23
|
|
|
23
24
|
if (isControlled) {
|
|
24
|
-
if (
|
|
25
|
+
if (
|
|
26
|
+
wasControlled.current &&
|
|
27
|
+
process.env.NODE_ENV !== 'production' &&
|
|
28
|
+
!hasWarned.current
|
|
29
|
+
) {
|
|
25
30
|
console.warn('Trying to change from controlled to uncontrolled.');
|
|
31
|
+
hasWarned.current = true;
|
|
26
32
|
}
|
|
27
33
|
return [
|
|
28
34
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|