@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.
Files changed (37) hide show
  1. package/build/cjs/index.js +85 -10
  2. package/build/cjs/index.js.map +1 -1
  3. package/build/esm/Menu/ContextMenuTrigger.d.ts +11 -0
  4. package/build/esm/Menu/ContextMenuTrigger.js +51 -0
  5. package/build/esm/Menu/ContextMenuTrigger.js.map +1 -0
  6. package/build/esm/Menu/Menu.js +11 -2
  7. package/build/esm/Menu/Menu.js.map +1 -1
  8. package/build/esm/Menu/MenuItem.js +0 -1
  9. package/build/esm/Menu/MenuItem.js.map +1 -1
  10. package/build/esm/Menu/MenuList.js +23 -6
  11. package/build/esm/Menu/MenuList.js.map +1 -1
  12. package/build/esm/Menu/MenuPopover.js +6 -2
  13. package/build/esm/Menu/MenuPopover.js.map +1 -1
  14. package/build/esm/Menu/context.d.ts +5 -1
  15. package/build/esm/Menu/context.js.map +1 -1
  16. package/build/esm/Menu/fixtures/countryList.d.ts +1 -0
  17. package/build/esm/Menu/fixtures/countryList.js +2 -0
  18. package/build/esm/Menu/fixtures/countryList.js.map +1 -0
  19. package/build/esm/Menu/index.d.ts +1 -0
  20. package/build/esm/Menu/index.js +1 -0
  21. package/build/esm/Menu/index.js.map +1 -1
  22. package/build/esm/hooks/useControlledState.js +3 -1
  23. package/build/esm/hooks/useControlledState.js.map +1 -1
  24. package/build/tsconfig-build.tsbuildinfo +1 -1
  25. package/package.json +2 -2
  26. package/src/Menu/ContextMenu.story.tsx +73 -0
  27. package/src/Menu/ContextMenuTrigger.tsx +63 -0
  28. package/src/Menu/Menu.story.tsx +2 -2
  29. package/src/Menu/Menu.tsx +14 -1
  30. package/src/Menu/MenuComplex.story.tsx +58 -0
  31. package/src/Menu/MenuItem.tsx +0 -1
  32. package/src/Menu/MenuList.tsx +38 -9
  33. package/src/Menu/MenuPopover.tsx +9 -2
  34. package/src/Menu/context.ts +5 -1
  35. package/src/Menu/fixtures/countryList.ts +198 -0
  36. package/src/Menu/index.ts +1 -0
  37. package/src/hooks/useControlledState.ts +7 -1
@@ -216,11 +216,13 @@ function wrapEvent(theirHandler, ourHandler) {
216
216
  function useControlledState(valueProp, onChangeProp, defaultValue, defaultOnChange) {
217
217
  const isControlled = valueProp !== undefined;
218
218
  const wasControlled = react.useRef(isControlled);
219
+ const hasWarned = react.useRef(false);
219
220
  const [valueState, setValueState] = react.useState(defaultValue);
220
221
 
221
222
  if (isControlled) {
222
- if (wasControlled.current && process.env.NODE_ENV !== 'production') {
223
+ if (wasControlled.current && process.env.NODE_ENV !== 'production' && !hasWarned.current) {
223
224
  console.warn('Trying to change from controlled to uncontrolled.');
225
+ hasWarned.current = true;
224
226
  }
225
227
 
226
228
  return [// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
@@ -1914,12 +1916,20 @@ const Menu = /*#__PURE__*/react.forwardRef(function Menu(props, forwardedRef) {
1914
1916
  setState(isOpen);
1915
1917
  });
1916
1918
  menuListIdRef.current = react.useId();
1919
+ const offset = react.useRef([0, 0]);
1920
+ const offsetFn = react.useCallback(({
1921
+ reference
1922
+ }) => [offset.current[0], offset.current[1] - reference.height], []);
1923
+ const isContextMenu = react.useRef(false);
1917
1924
  const value = {
1918
1925
  menuListIdRef,
1919
1926
  openWithArrowKeyRef,
1920
1927
  open,
1921
1928
  onChange,
1922
- buttonRef
1929
+ buttonRef,
1930
+ offset,
1931
+ offsetFn,
1932
+ isContextMenu
1923
1933
  };
1924
1934
  return /*#__PURE__*/jsxRuntime.jsx(MenuProvider, {
1925
1935
  value: value,
@@ -1998,6 +2008,51 @@ const MenuButton = /*#__PURE__*/react.forwardRef(function MenuButton(props, forw
1998
2008
  });
1999
2009
  });
2000
2010
 
2011
+ const ContextMenuTrigger = /*#__PURE__*/react.forwardRef(function MenuButton(props, forwardedRef) {
2012
+ const {
2013
+ as: Comp = 'div',
2014
+ id: preferredId,
2015
+ onContextMenu,
2016
+ disabled,
2017
+ ...otherProps
2018
+ } = props;
2019
+ const {
2020
+ menuListIdRef,
2021
+ open,
2022
+ buttonRef,
2023
+ onChange,
2024
+ offset,
2025
+ isContextMenu
2026
+ } = useMenuContext();
2027
+ const idGenerated = react.useId();
2028
+ const id = preferredId || idGenerated;
2029
+
2030
+ const handleContextMenu = e => {
2031
+ if (disabled) {
2032
+ return;
2033
+ }
2034
+
2035
+ const rect = e.currentTarget.getBoundingClientRect();
2036
+ offset.current = [e.nativeEvent.x - rect.x, e.nativeEvent.y - rect.y];
2037
+ isContextMenu.current = true;
2038
+ buttonRef.current = e.currentTarget;
2039
+ onChange && onChange(e, !open);
2040
+ e.preventDefault();
2041
+ };
2042
+
2043
+ return /*#__PURE__*/jsxRuntime.jsx(Comp, {
2044
+ ref: forwardedRef,
2045
+ id: id,
2046
+ "aria-haspopup": true,
2047
+ "aria-controls": menuListIdRef.current,
2048
+ "aria-expanded": open ? true : undefined,
2049
+ "data-menu-trigger": "",
2050
+ onContextMenu: wrapEvent(onContextMenu, handleContextMenu),
2051
+ disabled: disabled,
2052
+ ...otherProps
2053
+ });
2054
+ });
2055
+
2001
2056
  const MenuItem = /*#__PURE__*/react.forwardRef(function MenuItem(props, forwardedRef) {
2002
2057
  const {
2003
2058
  as: Comp = 'li',
@@ -2035,7 +2090,6 @@ const MenuItem = /*#__PURE__*/react.forwardRef(function MenuItem(props, forwarde
2035
2090
  const handleKeyDown = e => {
2036
2091
  switch (e.key) {
2037
2092
  case 'Enter':
2038
- case ' ':
2039
2093
  if (!disabled) {
2040
2094
  handleSelect(e);
2041
2095
  }
@@ -2073,12 +2127,15 @@ const MenuList = /*#__PURE__*/react.forwardRef(function MenuList(props, forwarde
2073
2127
  defaultActiveItemValue,
2074
2128
  ...otherProps
2075
2129
  } = props;
2130
+ const itemSearchStr = react.useRef('');
2131
+ const itemSearchClearTimeout = react.useRef();
2076
2132
  const {
2077
2133
  menuListIdRef,
2078
2134
  buttonRef,
2079
2135
  onChange,
2080
2136
  openWithArrowKeyRef,
2081
- open
2137
+ open,
2138
+ isContextMenu
2082
2139
  } = useMenuContext();
2083
2140
  const [navigationItem, setNavigationItem] = react.useState();
2084
2141
  const [mounted, setMounted] = react.useState(false);
@@ -2114,7 +2171,9 @@ const MenuList = /*#__PURE__*/react.forwardRef(function MenuList(props, forwarde
2114
2171
  setMounted(true);
2115
2172
  }, [mounted, navigationItem, onNavigate, openWithArrowKeyRef, scope, defaultActiveItemValue]);
2116
2173
  useOnClickOutside(menuListRef, e => {
2117
- if (e.target instanceof HTMLElement && e.target !== buttonRef.current && !buttonRef.current?.contains(e.target)) {
2174
+ console.log(isContextMenu.current);
2175
+
2176
+ if (isContextMenu.current || e.target instanceof HTMLElement && e.target !== buttonRef.current && !buttonRef.current?.contains(e.target)) {
2118
2177
  onChange && onChange(e, false);
2119
2178
  }
2120
2179
 
@@ -2129,6 +2188,7 @@ const MenuList = /*#__PURE__*/react.forwardRef(function MenuList(props, forwarde
2129
2188
  onChange && onChange(e, false);
2130
2189
  e.preventDefault(); // prevents focusing on next element, because we will be handling it
2131
2190
 
2191
+ itemSearchStr.current = '';
2132
2192
  buttonRef.current?.focus();
2133
2193
  break;
2134
2194
  }
@@ -2138,6 +2198,7 @@ const MenuList = /*#__PURE__*/react.forwardRef(function MenuList(props, forwarde
2138
2198
  case 'ArrowDown':
2139
2199
  case 'ArrowUp':
2140
2200
  e.preventDefault();
2201
+ itemSearchStr.current = '';
2141
2202
  const allItems = scope ? scope.current.queryAllNodes(queryScope) : [];
2142
2203
  const currentIndex = allItems.findIndex(e => e === navigationItem);
2143
2204
 
@@ -2173,21 +2234,30 @@ const MenuList = /*#__PURE__*/react.forwardRef(function MenuList(props, forwarde
2173
2234
 
2174
2235
  default:
2175
2236
  {
2176
- if (e.key.length === 1) {
2237
+ if (e.key.length === 1 && !e.ctrlKey && !e.altKey) {
2177
2238
  // A-Z
2178
2239
  e.preventDefault();
2240
+
2241
+ if (itemSearchStr.current.length === 0 || itemSearchStr.current.slice(-1) !== e.key) {
2242
+ itemSearchStr.current = itemSearchStr.current + e.key;
2243
+ }
2244
+
2245
+ clearTimeout(itemSearchClearTimeout.current);
2246
+ itemSearchClearTimeout.current = setTimeout(() => {
2247
+ itemSearchStr.current = '';
2248
+ }, 500);
2179
2249
  const allItems = scope ? scope.current.queryAllNodes(queryScope) : [];
2180
2250
  const currentIndex = allItems.findIndex(e => e === navigationItem);
2181
- const firstLetter = e.key.toLowerCase();
2251
+ const searchStr = itemSearchStr.current;
2182
2252
  let nextIndex = -1;
2183
2253
 
2184
- for (let i = 1; i < allItems.length; i++) {
2254
+ for (let i = searchStr.length === 1 ? 1 : 0; i < allItems.length; i++) {
2185
2255
  // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
2186
2256
  const idx = getCircularIndex(currentIndex + i, allItems.length);
2187
2257
  const dom = allItems[idx];
2188
2258
  const domText = dom.innerText.toLowerCase();
2189
2259
 
2190
- if (domText.length > 0 && domText.charAt(0) === firstLetter) {
2260
+ if (domText.length > 0 && domText.startsWith(searchStr)) {
2191
2261
  nextIndex = idx;
2192
2262
  break;
2193
2263
  }
@@ -2360,7 +2430,9 @@ const MenuPopover = /*#__PURE__*/react.forwardRef(function MenuPopover(props, fo
2360
2430
  } = props;
2361
2431
  const {
2362
2432
  buttonRef,
2363
- open
2433
+ open,
2434
+ offsetFn,
2435
+ isContextMenu
2364
2436
  } = useMenuContext();
2365
2437
 
2366
2438
  if (!open) {
@@ -2371,6 +2443,8 @@ const MenuPopover = /*#__PURE__*/react.forwardRef(function MenuPopover(props, fo
2371
2443
  as: as,
2372
2444
  ref: forwardedRef,
2373
2445
  anchorEl: buttonRef,
2446
+ offsetFn: offsetFn,
2447
+ placement: isContextMenu.current ? 'bottom-start' : undefined,
2374
2448
  ...otherProps
2375
2449
  });
2376
2450
  });
@@ -3963,6 +4037,7 @@ exports.ComboboxLabel = ComboboxLabel;
3963
4037
  exports.ComboboxList = ComboboxList;
3964
4038
  exports.ComboboxOption = ComboboxOption;
3965
4039
  exports.ComboboxPopover = ComboboxPopover;
4040
+ exports.ContextMenuTrigger = ContextMenuTrigger;
3966
4041
  exports.FocusLock = FocusLock;
3967
4042
  exports.Menu = Menu;
3968
4043
  exports.MenuButton = MenuButton;