@basic-ui/core 0.0.46 → 0.0.48

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.
@@ -21,6 +21,7 @@ export const MenuButton = forwardRef<HTMLButtonElement, MenuButtonProps>(
21
21
  function MenuButton(props, forwardedRef) {
22
22
  const {
23
23
  as: Comp = 'button',
24
+ innerAs,
24
25
  id: preferredId,
25
26
  onClick,
26
27
  onKeyDown,
@@ -48,7 +49,7 @@ export const MenuButton = forwardRef<HTMLButtonElement, MenuButtonProps>(
48
49
  // Used to make it open at the end or begining of the list
49
50
  openWithArrowKeyRef.current = e.key;
50
51
  }
51
- onChange && onChange(e, true);
52
+ onChange(e, true);
52
53
  e.preventDefault();
53
54
  }
54
55
  };
@@ -58,13 +59,13 @@ export const MenuButton = forwardRef<HTMLButtonElement, MenuButtonProps>(
58
59
  return;
59
60
  }
60
61
  buttonRef.current = e.currentTarget;
61
-
62
- onChange && onChange(e, !open);
62
+ onChange(e, !open);
63
63
  };
64
64
 
65
65
  return (
66
66
  <Comp
67
67
  ref={forwardedRef}
68
+ as={innerAs}
68
69
  id={buttonId}
69
70
  role="button"
70
71
  type="button"
@@ -1,4 +1,4 @@
1
- import type { MouseEvent } from 'react';
1
+ import type { MouseEvent, ReactNode } from 'react';
2
2
 
3
3
  import { Menu, MenuButton, MenuItem, MenuList, MenuPopover } from '.';
4
4
  import { countryList } from './fixtures/countryList';
@@ -8,7 +8,7 @@ export default {
8
8
  title: 'components/Menu/Complex',
9
9
  };
10
10
 
11
- const Wrapper = ({ children }) => {
11
+ const Wrapper = ({ children }: { children: ReactNode }) => {
12
12
  const handleLinkClick = (e: MouseEvent<HTMLAnchorElement>) => {
13
13
  console.log('Clicked ' + e.currentTarget.innerText);
14
14
  e.preventDefault();
@@ -26,6 +26,7 @@ export const MenuItem = forwardRef<any, MenuItemProps>(function MenuItem(
26
26
  ) {
27
27
  const {
28
28
  as: Comp = 'li',
29
+ innerAs,
29
30
  disabled,
30
31
  onSelect,
31
32
  onClick,
@@ -42,7 +43,7 @@ export const MenuItem = forwardRef<any, MenuItemProps>(function MenuItem(
42
43
  const handleSelect = wrapEvent(
43
44
  onSelect,
44
45
  (e: KeyboardEvent<HTMLLIElement> | MouseEvent<HTMLLIElement>) => {
45
- onChange && onChange(e, false);
46
+ onChange(e, false);
46
47
  buttonRef.current?.focus();
47
48
  e.preventDefault();
48
49
  }
@@ -69,6 +70,7 @@ export const MenuItem = forwardRef<any, MenuItemProps>(function MenuItem(
69
70
  return (
70
71
  <Comp
71
72
  ref={assignMultipleRefs(ref, forwardedRef)}
73
+ as={innerAs}
72
74
  id={disabled ? undefined : itemId}
73
75
  data-menu-item=""
74
76
  data-highlighted={isActive ? '' : undefined}
@@ -1,5 +1,6 @@
1
1
  import type { HTMLAttributes, ElementType, KeyboardEvent } from 'react';
2
2
  import {
3
+ useCallback,
3
4
  forwardRef,
4
5
  useEffect,
5
6
  useRef,
@@ -27,17 +28,25 @@ export const MenuList = forwardRef<HTMLUListElement, MenuListProps>(
27
28
  function MenuList(props, forwardedRef) {
28
29
  const {
29
30
  as: Comp = 'ul',
31
+ innerAs,
30
32
  onKeyDown,
31
33
  id: preferredId,
32
34
  defaultActiveItemValue,
33
35
  ...otherProps
34
36
  } = props;
35
37
 
38
+ const interactedOutside = useRef(false);
36
39
  const itemSearchStr = useRef('');
37
40
  const itemSearchClearTimeout = useRef<ReturnType<typeof setTimeout>>();
38
41
 
39
- const { menuListIdRef, buttonRef, onChange, openWithArrowKeyRef, open } =
40
- useMenuContext();
42
+ const {
43
+ menuListIdRef,
44
+ buttonRef,
45
+ onChange,
46
+ openWithArrowKeyRef,
47
+ open,
48
+ isContextMenu,
49
+ } = useMenuContext();
41
50
 
42
51
  const [navigationItem, setNavigationItem] = useState<
43
52
  HTMLElement | undefined
@@ -89,26 +98,40 @@ export const MenuList = forwardRef<HTMLUListElement, MenuListProps>(
89
98
  defaultActiveItemValue,
90
99
  ]);
91
100
 
92
- useOnClickOutside(
93
- menuListRef,
94
- (e) => {
95
- if (
96
- e.target instanceof HTMLElement &&
97
- e.target !== buttonRef.current &&
98
- !buttonRef.current?.contains(e.target)
99
- ) {
100
- onChange && onChange(e as any, false);
101
+ const handleClickOutside = useCallback(
102
+ (e: PointerEvent) => {
103
+ if (!interactedOutside.current) {
104
+ // First interaction should be ignored, because
105
+ // this is what triggered the context menu to open
106
+ interactedOutside.current = true;
107
+ return;
108
+ }
109
+
110
+ if (isContextMenu.current) {
111
+ if (e.button === 0) {
112
+ onChange(e as any, false);
113
+ }
114
+ } else {
115
+ if (
116
+ e.target instanceof HTMLElement &&
117
+ e.target !== buttonRef.current &&
118
+ !buttonRef.current?.contains(e.target)
119
+ ) {
120
+ onChange(e as any, false);
121
+ }
101
122
  }
102
123
  e.preventDefault();
103
124
  },
104
- true
125
+ [buttonRef, isContextMenu, onChange]
105
126
  );
106
127
 
128
+ useOnClickOutside(menuListRef, handleClickOutside, open);
129
+
107
130
  function handleKeyDown(e: KeyboardEvent<HTMLUListElement>) {
108
131
  switch (e.key) {
109
132
  case 'Escape':
110
133
  case 'Tab': {
111
- onChange && onChange(e, false);
134
+ onChange(e, false);
112
135
  e.preventDefault(); // prevents focusing on next element, because we will be handling it
113
136
  itemSearchStr.current = '';
114
137
  buttonRef.current?.focus();
@@ -192,6 +215,7 @@ export const MenuList = forwardRef<HTMLUListElement, MenuListProps>(
192
215
  }
193
216
 
194
217
  if (!open) {
218
+ interactedOutside.current = false;
195
219
  return null;
196
220
  }
197
221
 
@@ -204,6 +228,7 @@ export const MenuList = forwardRef<HTMLUListElement, MenuListProps>(
204
228
  >
205
229
  <Comp
206
230
  ref={assignMultipleRefs(forwardedRef, menuListRef)}
231
+ as={innerAs}
207
232
  id={menuListIdRef.current}
208
233
  role="menu"
209
234
  aria-labelledby={buttonRef.current?.id}
@@ -13,15 +13,23 @@ export interface MenuPopoverProps extends Omit<PopperProps, 'anchorEl'> {
13
13
 
14
14
  export const MenuPopover = forwardRef<HTMLDivElement, MenuPopoverProps>(
15
15
  function MenuPopover(props, forwardedRef) {
16
- const { as = 'div', ...otherProps } = props;
17
- const { buttonRef, open } = useMenuContext();
16
+ const { as = 'div', innerAs, ...otherProps } = props;
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 as={as} ref={forwardedRef} anchorEl={buttonRef} {...otherProps} />
24
+ <Popper
25
+ as={as}
26
+ innerAs={innerAs}
27
+ ref={forwardedRef}
28
+ anchorEl={buttonRef}
29
+ offsetFn={offsetFn}
30
+ placement={isContextMenu.current ? 'bottom-start' : undefined}
31
+ {...otherProps}
32
+ />
25
33
  );
26
34
  }
27
35
  );
@@ -1,8 +1,11 @@
1
+ import type { OffsetsFunction } from '@popperjs/core/lib/modifiers/offset';
1
2
  import type {
3
+ Dispatch,
2
4
  KeyboardEvent,
3
5
  MouseEvent,
4
6
  MutableRefObject,
5
7
  PointerEvent,
8
+ SetStateAction,
6
9
  } from 'react';
7
10
  import { createContext, useContext } from 'react';
8
11
 
@@ -10,10 +13,10 @@ export type ItemObject = { text: string; value: any; id: string | undefined };
10
13
 
11
14
  // MenuRoot
12
15
  export interface MenuContextProps {
13
- buttonRef: MutableRefObject<HTMLButtonElement | null>;
16
+ buttonRef: MutableRefObject<HTMLElement | null>;
14
17
  menuListIdRef: MutableRefObject<undefined | string>;
15
18
  openWithArrowKeyRef: MutableRefObject<string | null>;
16
- onChange?: (
19
+ onChange: (
17
20
  e:
18
21
  | KeyboardEvent<HTMLElement>
19
22
  | MouseEvent<HTMLElement>
@@ -21,6 +24,9 @@ export interface MenuContextProps {
21
24
  isOpen: boolean
22
25
  ) => void;
23
26
  open: boolean;
27
+ offsetFn: OffsetsFunction | undefined;
28
+ setOffsetFn: Dispatch<SetStateAction<OffsetsFunction | undefined>>;
29
+ isContextMenu: MutableRefObject<boolean>;
24
30
  }
25
31
 
26
32
  const menuContext = createContext<MenuContextProps>(null as any);
package/src/Menu/index.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  export * from './Menu';
2
2
  export * from './MenuButton';
3
+ export * from './ContextMenuTrigger';
3
4
  export * from './MenuItem';
4
5
  export * from './MenuList';
5
6
  export * from './MenuPopover';