@ringcentral/spring-ui 1.8.0 → 1.9.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.
Files changed (77) hide show
  1. package/Autocomplete.js +46 -11
  2. package/Dialog.js +16 -4
  3. package/Modal.js +1 -1
  4. package/StepButton.js +2 -3
  5. package/StepContent.js +3 -8
  6. package/SwipeableDrawer.js +30 -17
  7. package/Tab.js +1 -1
  8. package/TableRow.js +2 -3
  9. package/Tabs.js +2 -3
  10. package/VirtualizedList.js +5 -10
  11. package/VirtualizedMenu.js +1 -1
  12. package/chunks/Menu.js +3 -2
  13. package/chunks/MenuList.js +3 -3
  14. package/chunks/Modal.js +1 -1
  15. package/chunks/VirtualizedMenu.js +44 -22
  16. package/chunks/getFade.js +3 -1
  17. package/chunks/useListNavigation.js +3 -0
  18. package/chunks/useMenuItemSelect.js +3 -3
  19. package/chunks/useNestedParentMenu.js +5 -16
  20. package/chunks/usePrefersReducedMotion.js +6 -2
  21. package/index.css +49 -22
  22. package/package.json +4 -4
  23. package/src/Accordion/Accordion.d.ts +1 -1
  24. package/src/AccordionHeader/AccordionHeader.d.ts +1 -1
  25. package/src/Alert/Alert.d.ts +1 -1
  26. package/src/Autocomplete/SuggestionList/utils/useHighlightScroll.d.ts +4 -3
  27. package/src/Autocomplete/useAutocomplete.d.ts +4 -4
  28. package/src/Autocomplete/useAutocompleteTag.d.ts +3 -3
  29. package/src/CircularProgressIndicator/CircularProgressIndicator.d.ts +1 -1
  30. package/src/DatePicker/Day.d.ts +1 -1
  31. package/src/DatePicker/Year.d.ts +1 -1
  32. package/src/Dialog/Dialog.classes.d.ts +1 -1
  33. package/src/Dialog/Dialog.d.ts +1 -1
  34. package/src/Dialog/Dialog.type.d.ts +3 -0
  35. package/src/Drawer/Drawer.d.ts +1 -1
  36. package/src/EmptyState/EmptyState.d.ts +1 -1
  37. package/src/FormLabel/FormLabel.d.ts +1 -1
  38. package/src/Icon/Icon.d.ts +1 -1
  39. package/src/IconButton/IconButtonLabel.d.ts +1 -1
  40. package/src/ListItemText/ListItemText.d.ts +1 -1
  41. package/src/Menu/Menu.d.ts +1 -1
  42. package/src/MenuItem/MenuItem.d.ts +1 -1
  43. package/src/MenuItemText/MenuItemText.d.ts +1 -1
  44. package/src/Modal/InnerModal.d.ts +2 -2
  45. package/src/Modal/Modal.classes.d.ts +1 -1
  46. package/src/Modal/Modal.d.ts +3 -3
  47. package/src/NestedMenuContext/NestedMenuContext.d.ts +1 -1
  48. package/src/Option/Option.d.ts +1 -1
  49. package/src/Popover/Popover.d.ts +1 -1
  50. package/src/Popper/Popper.d.ts +1 -1
  51. package/src/PopperPaper/PopperPaper.d.ts +1 -1
  52. package/src/RadioGroup/RadioGroup.d.ts +1 -1
  53. package/src/Rating/Rating.d.ts +1 -1
  54. package/src/Rating/RatingItem.d.ts +1 -1
  55. package/src/SingleFilter/FilterButton/FilterButton.d.ts +1 -1
  56. package/src/SingleFilter/SingleFilter.d.ts +1 -1
  57. package/src/Slider/Slider.d.ts +2 -2
  58. package/src/SortableListItem/SortableListItemOverlay.d.ts +1 -1
  59. package/src/SplitButton/SplitButton.d.ts +1 -1
  60. package/src/StepContent/StepContent.d.ts +1 -1
  61. package/src/StepLabel/StepLabel.d.ts +1 -1
  62. package/src/SubMenu/PopperMenu.d.ts +1 -1
  63. package/src/SubMenu/SubMenu.d.ts +1 -1
  64. package/src/SwipeableDrawer/SwipeableDrawer.d.ts +1 -1
  65. package/src/Tab/Tab.d.ts +1 -1
  66. package/src/TabPanel/TabPanel.d.ts +1 -1
  67. package/src/Tabs/TabScrollButton.d.ts +1 -1
  68. package/src/Tabs/Tabs.d.ts +1 -1
  69. package/src/Tag/Tag.d.ts +1 -1
  70. package/src/Textarea/Textarea.d.ts +1 -1
  71. package/src/TimePicker/NumberPicker.d.ts +1 -1
  72. package/src/TimePicker/TimeButton.d.ts +1 -1
  73. package/src/TimePicker/ToggleText.d.ts +1 -1
  74. package/src/Tray/Tray.d.ts +1 -1
  75. package/src/TrayMenu/TrayMenu.d.ts +1 -1
  76. package/src/TrayMenu/TrayMenuItem.d.ts +1 -1
  77. package/src/VirtualizedMenu/VirtualizedMenu.classes.d.ts +1 -1
package/Autocomplete.js CHANGED
@@ -2,7 +2,7 @@ import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
2
2
  import { forwardRef, useRef, useMemo, useCallback, useEffect, useLayoutEffect, useState, useImperativeHandle } from 'react';
3
3
  import clsx from 'clsx';
4
4
  import { Virtuoso } from 'react-virtuoso';
5
- import { usePrevious, useWindowContext, useRefState, useEventCallback, logInDev, combineProps, useDepsChange, useControlled, useId, useForceUpdate, useKeyboardMoveFocus, useForkRef, useSleep, useRetry, useChange, getSelectionPosition, useClasses } from '@ringcentral/spring-base';
5
+ import { usePrevious, useRefState, useEventCallback, logInDev, combineProps, useDepsChange, useControlled, useId, useForceUpdate, useKeyboardMoveFocus, useForkRef, useVirtuosoExternalWindow, useSleep, useRetry, useChange, getSelectionPosition, useClasses } from '@ringcentral/spring-base';
6
6
  import { useClassesWithMode, useThemeProps } from '@ringcentral/spring-theme';
7
7
  import { E as ExpandCollapseCaret } from './chunks/ExpandCollapseCaret.js';
8
8
  import { AnimatePresence, motion } from 'framer-motion';
@@ -86,25 +86,32 @@ const useDynamicHeight = ({ itemCount, maxContainerHeight, onContainerHeightChan
86
86
  };
87
87
  };
88
88
 
89
- function useHighlightScroll({ containerHeighRef, scrollToIndex, }) {
90
- const { externalWindow } = useWindowContext();
89
+ function useHighlightScroll({ virtualize = true, containerHeighRef, scrollToIndex, }) {
91
90
  const renderedItemsRef = useRef([]);
92
91
  const scrollerRef = useRef();
93
92
  const scrollToHighlightedIndex = (prevHighlightedIndex, currHighlightedIndex, topHighlightIndex = 0) => {
94
93
  const needsScrollReset = prevHighlightedIndex === -1;
95
94
  if (!scrollerRef.current) {
96
95
  if (currHighlightedIndex !== topHighlightIndex) {
97
- const win = externalWindow || window;
98
- win.requestAnimationFrame(() => {
99
- scrollToIndex({
100
- index: currHighlightedIndex,
101
- align: 'start',
102
- });
96
+ scrollToIndex({
97
+ index: currHighlightedIndex,
98
+ align: 'start',
103
99
  });
104
100
  }
105
101
  return;
106
102
  }
107
103
  const scrollElm = scrollerRef.current;
104
+ if (!virtualize) {
105
+ if (needsScrollReset && currHighlightedIndex === topHighlightIndex) {
106
+ scrollElm.scrollTop = 0;
107
+ return;
108
+ }
109
+ scrollToIndex({
110
+ index: currHighlightedIndex,
111
+ align: 'start',
112
+ });
113
+ return;
114
+ }
108
115
  const renderedItems = renderedItemsRef.current;
109
116
  const lastHighlightedIndex = needsScrollReset
110
117
  ? currHighlightedIndex
@@ -643,6 +650,7 @@ const SuggestionList = forwardRef((inProps, ref) => {
643
650
  const listRef = useRef(null);
644
651
  const itemData = options;
645
652
  const itemCount = options.length;
653
+ const externalWindowProps = useVirtuosoExternalWindow();
646
654
  const internalPrevHighlightedIndex = usePrevious(() => highlightedIndex, true);
647
655
  const actualPrevHighlightedIndex = prevHighlightedIndex ?? internalPrevHighlightedIndex;
648
656
  const cls = useClassesWithMode('suggestion-list', classesProp, classNameProp);
@@ -679,7 +687,33 @@ const SuggestionList = forwardRef((inProps, ref) => {
679
687
  return true;
680
688
  }
681
689
  const toElm = scroller?.querySelector(`[data-item-index="${toIndex}"]`);
682
- toElm?.scrollIntoView({ block: 'nearest', inline: 'nearest' });
690
+ if (!toElm || !scroller) {
691
+ return false;
692
+ }
693
+ const scrollerRect = scroller.getBoundingClientRect();
694
+ const elementRect = toElm.getBoundingClientRect();
695
+ const scrollerTop = scrollerRect.top;
696
+ const scrollerBottom = scrollerRect.bottom;
697
+ const elementTop = elementRect.top;
698
+ const elementBottom = elementRect.bottom;
699
+ const isFullyVisible = elementTop >= scrollerTop && elementBottom <= scrollerBottom;
700
+ if (!isFullyVisible) {
701
+ let scrollOffset = 0;
702
+ if (elementTop < scrollerTop) {
703
+ scrollOffset = toElm.offsetTop - paddingValue;
704
+ }
705
+ else if (elementBottom > scrollerBottom) {
706
+ scrollOffset =
707
+ toElm.offsetTop -
708
+ scroller.clientHeight +
709
+ toElm.offsetHeight +
710
+ paddingValue;
711
+ }
712
+ scroller.scrollTo({
713
+ top: scrollOffset,
714
+ behavior: 'auto',
715
+ });
716
+ }
683
717
  return true;
684
718
  }
685
719
  if (paddingValue > 0 &&
@@ -698,6 +732,7 @@ const SuggestionList = forwardRef((inProps, ref) => {
698
732
  intervalTime: 20,
699
733
  });
700
734
  const { scrollerRef, scrollerRefFn, itemsRendered: itemsRenderedFromHook, scrollToHighlightedIndex, } = useHighlightScroll({
735
+ virtualize,
701
736
  containerHeighRef,
702
737
  scrollToIndex: scrollToIndexWithRetry,
703
738
  });
@@ -786,7 +821,7 @@ const SuggestionList = forwardRef((inProps, ref) => {
786
821
  };
787
822
  const rootClass = cls('root');
788
823
  const context = { listRef, rootClass, paddingValue };
789
- return virtualize ? (jsx(Virtuoso, { ref: forkVlRef, totalCount: itemCount, data: itemData, context: context, components: components, itemContent: itemContent, ...getSuggestionListProps(), ...combineProps({
824
+ return virtualize ? (jsx(Virtuoso, { ref: forkVlRef, totalCount: itemCount, data: itemData, context: context, components: components, itemContent: itemContent, ...externalWindowProps, ...getSuggestionListProps(), ...combineProps({
790
825
  scrollerRef: (scrollElm) => {
791
826
  scrollerRefFn(scrollElm);
792
827
  },
package/Dialog.js CHANGED
@@ -2,7 +2,7 @@ import { jsx, jsxs } from 'react/jsx-runtime';
2
2
  import { forwardRef, useMemo } from 'react';
3
3
  import { motion } from 'framer-motion';
4
4
  import kebabCase from 'lodash/kebabCase';
5
- import { useEventCallback, useClasses } from '@ringcentral/spring-base';
5
+ import { useEventCallback, combineProps, useClasses } from '@ringcentral/spring-base';
6
6
  import { Xsm } from '@ringcentral/spring-icon';
7
7
  import { useThemeProps, useClassesWithMode, motionTransition } from '@ringcentral/spring-theme';
8
8
  import './chunks/Icon.js';
@@ -22,11 +22,11 @@ import './chunks/PopperPaper.js';
22
22
  import './chunks/useFocusTrapStack.js';
23
23
  import './chunks/Backdrop.js';
24
24
 
25
- var Dialog$1 = {"sui-dialog-body":"sui-dialog-body","sui-dialog-root":"sui-dialog-root","sui-dialog-small":"sui-dialog-small","sui-dialog-medium":"sui-dialog-medium","sui-dialog-large":"sui-dialog-large","sui-dialog-title-root":"sui-dialog-title-root","sui-dialog-content-root":"sui-dialog-content-root","sui-dialog-actions-root":"sui-dialog-actions-root","sui-dialog-right":"sui-dialog-right","sui-dialog-right-close-button":"sui-dialog-right-close-button","sui-dialog-left":"sui-dialog-left","sui-dialog-left-close-button":"sui-dialog-left-close-button","sui-dialog-full-height":"sui-dialog-full-height","sui-dialog-close-button":"sui-dialog-close-button","sui-dialog-full-screen":"sui-dialog-full-screen","sui-compact":"sui-compact"};
25
+ var Dialog$1 = {"sui-dialog-body":"sui-dialog-body","sui-dialog-root":"sui-dialog-root","sui-dialog-non-blocking":"sui-dialog-non-blocking","sui-dialog-small":"sui-dialog-small","sui-dialog-medium":"sui-dialog-medium","sui-dialog-large":"sui-dialog-large","sui-dialog-title-root":"sui-dialog-title-root","sui-dialog-content-root":"sui-dialog-content-root","sui-dialog-actions-root":"sui-dialog-actions-root","sui-dialog-right":"sui-dialog-right","sui-dialog-right-close-button":"sui-dialog-right-close-button","sui-dialog-left":"sui-dialog-left","sui-dialog-left-close-button":"sui-dialog-left-close-button","sui-dialog-full-height":"sui-dialog-full-height","sui-dialog-close-button":"sui-dialog-close-button","sui-dialog-full-screen":"sui-dialog-full-screen","sui-compact":"sui-compact"};
26
26
 
27
27
  const Dialog = forwardRef((inProps, ref) => {
28
28
  const props = useThemeProps({ props: inProps, name: 'Dialog' });
29
- const { bodyProps, classes, className, children, size = 'medium', placement = 'right', open, onClose, ...rest } = props;
29
+ const { bodyProps, classes, className, children, size = 'medium', placement = 'right', open, onClose, blocking = true, CloseButtonProps, ...rest } = props;
30
30
  const cls = useClassesWithMode('dialog', classes, className);
31
31
  const toSize = useMemo(() => kebabCase(size), [size]);
32
32
  const isFullHeight = size === 'fullHeight';
@@ -40,7 +40,19 @@ const Dialog = forwardRef((inProps, ref) => {
40
40
  const handleCloseButtonClick = useEventCallback((e) => {
41
41
  onClose?.(e, 'closeButtonClick');
42
42
  });
43
- return (jsx(Modal, { open: open, className: cls('root'), ...rest, onClose: onClose, children: jsxs(motion.div, { role: "dialog", "aria-modal": true, ...(isFullHeight && slideInProps), ...bodyProps, ref: ref, tabIndex: -1, className: cls('body', toSize, isFullHeight && placement), children: [children, isFullHeight && (jsx(IconButton, { className: cls('close-button', `${placement}-close-button`), size: 'large', color: 'secondary', symbol: Xsm, variant: "contained", onClick: handleCloseButtonClick }))] }) }));
43
+ const nonBlockingProps = !blocking
44
+ ? {
45
+ disableScrollLock: true,
46
+ disableBackdropClick: true,
47
+ disableEscapeKeyDown: true,
48
+ hideBackdrop: true,
49
+ }
50
+ : {};
51
+ return (jsx(Modal, { open: open, onClose: onClose, blocking: blocking, ...nonBlockingProps, className: cls('root', { 'non-blocking': !blocking }), ...rest, children: jsxs(motion.div, { role: "dialog", "aria-modal": blocking, ...(isFullHeight && slideInProps), ...bodyProps, ref: ref, tabIndex: blocking ? -1 : undefined, className: cls('body', toSize, isFullHeight && placement), children: [children, isFullHeight && (jsx(IconButton, { size: 'large', color: 'secondary', symbol: Xsm, variant: "contained", ...combineProps({
52
+ className: cls('close-button', `${placement}-close-button`),
53
+ onClick: handleCloseButtonClick,
54
+ 'aria-label': 'Close',
55
+ }, CloseButtonProps) }))] }) }));
44
56
  });
45
57
  Dialog.displayName = 'SuiDialog';
46
58
  const DialogTitle = forwardRef(({ className, ...rest }, ref) => {
package/Modal.js CHANGED
@@ -11,6 +11,6 @@ import './chunks/getFade.js';
11
11
  import './chunks/useFocusTrapStack.js';
12
12
  import './chunks/Backdrop.js';
13
13
 
14
- var Modal = {"sui-modal-root":"sui-modal-root"};
14
+ var Modal = {"sui-modal-root":"sui-modal-root","sui-modal-non-blocking":"sui-modal-non-blocking","sui-backdrop-root":"sui-backdrop-root"};
15
15
 
16
16
  export { Modal as modalStyles };
package/StepButton.js CHANGED
@@ -1,7 +1,6 @@
1
1
  import { jsx } from 'react/jsx-runtime';
2
2
  import { forwardRef, cloneElement } from 'react';
3
- import { useButton } from '@mui/base';
4
- import { useClasses, isSuiElement, combineProps } from '@ringcentral/spring-base';
3
+ import { useClasses, useButton, isSuiElement, combineProps } from '@ringcentral/spring-base';
5
4
  import { useThemeProps } from '@ringcentral/spring-theme';
6
5
  import { b as useStepperContext, u as useStepContext } from './chunks/StepperContext.js';
7
6
  import { S as StepLabel } from './chunks/StepLabel.js';
@@ -34,7 +33,7 @@ const StepButton = forwardRef((inProps, ref) => {
34
33
  active,
35
34
  highlighted: activeStep && variant === 'outlined',
36
35
  'hide-connector': !connector,
37
- }), ...getRootProps(), ...rest, ref: ref, children: child }));
36
+ }), ...getRootProps(rest), ref: ref, children: child }));
38
37
  });
39
38
  StepButton.displayName = 'SuiStepButton';
40
39
 
package/StepContent.js CHANGED
@@ -16,9 +16,7 @@ const StepContent = forwardRef((inProps, ref) => {
16
16
  const cls = useClasses('step-content', classes, className);
17
17
  const { orientation } = useStepperContext();
18
18
  const { disabled, active, last, expanded: expandedProp, connector, id, } = useStepContext();
19
- const expandedTransition = useMotionSafeTransition(motionTransition['spring-01-fast']);
20
- const collapsedTransition = useMotionSafeTransition(motionTransition['spring-01-slow']);
21
- const collapsedHeightTransition = useMotionSafeTransition(motionTransition['haste-01-slow']);
19
+ const transition = useMotionSafeTransition(motionTransition['haste-01-fast']);
22
20
  if (process.env.NODE_ENV !== 'production' && orientation !== 'vertical') {
23
21
  logInDev({
24
22
  scope: 'AvatarGroup',
@@ -31,16 +29,13 @@ const StepContent = forwardRef((inProps, ref) => {
31
29
  const keepAlive = keepMounted && isEverShown;
32
30
  const motionVariants = {
33
31
  expanded: {
34
- transition: expandedTransition,
32
+ transition,
35
33
  opacity: 1,
36
34
  height: 'auto',
37
35
  paddingTop: 12,
38
36
  },
39
37
  collapsed: {
40
- transition: {
41
- ...collapsedTransition,
42
- height: collapsedHeightTransition,
43
- },
38
+ transition,
44
39
  opacity: 0,
45
40
  height: 0,
46
41
  paddingTop: 0,
@@ -48,6 +48,8 @@ const observerOptions = {
48
48
  box: 'border-box',
49
49
  };
50
50
  function useElementSizeObserver(ref, { enabled, resizeSourceRef, }) {
51
+ const { externalWindow, globalThis } = useWindowContext();
52
+ const win = externalWindow ?? globalThis;
51
53
  const [size, setSize] = useState(0);
52
54
  const handleResize = useCallback((entries) => {
53
55
  const entry = entries?.[0];
@@ -72,6 +74,8 @@ function useElementSizeObserver(ref, { enabled, resizeSourceRef, }) {
72
74
  if (!enabled) {
73
75
  return;
74
76
  }
77
+ const rAF = win.requestAnimationFrame.bind(win);
78
+ const cAF = win.cancelAnimationFrame.bind(win);
75
79
  let resizeObserver = null;
76
80
  let rafId = 0;
77
81
  let initialMeasurementRafId = 0;
@@ -81,7 +85,7 @@ function useElementSizeObserver(ref, { enabled, resizeSourceRef, }) {
81
85
  return;
82
86
  resizeObserver = new ResizeObserver(handleResize);
83
87
  resizeObserver.observe(el, observerOptions);
84
- initialMeasurementRafId = requestAnimationFrame(() => {
88
+ initialMeasurementRafId = rAF(() => {
85
89
  const initial = Math.max(el.scrollHeight, el.offsetHeight, el.getBoundingClientRect().height);
86
90
  if (initial > 0) {
87
91
  setSize(initial);
@@ -93,62 +97,65 @@ function useElementSizeObserver(ref, { enabled, resizeSourceRef, }) {
93
97
  startObserving();
94
98
  }
95
99
  else {
96
- rafId = requestAnimationFrame(waitForElement);
100
+ rafId = rAF(waitForElement);
97
101
  }
98
102
  };
99
103
  if (ref.current) {
100
104
  startObserving();
101
105
  }
102
106
  else {
103
- rafId = requestAnimationFrame(waitForElement);
107
+ rafId = rAF(waitForElement);
104
108
  }
105
109
  return () => {
106
110
  if (rafId)
107
- cancelAnimationFrame(rafId);
111
+ cAF(rafId);
108
112
  if (initialMeasurementRafId)
109
- cancelAnimationFrame(initialMeasurementRafId);
113
+ cAF(initialMeasurementRafId);
110
114
  if (resizeObserver) {
111
115
  resizeObserver.disconnect();
112
116
  }
113
117
  };
114
- }, [ref, handleResize, enabled]);
118
+ }, [ref, handleResize, enabled, win]);
115
119
  return enabled ? size : 0;
116
120
  }
117
121
 
118
122
  function useMaxHeight(registerReady) {
119
- const { globalThis } = useWindowContext();
123
+ const { externalWindow, globalThis } = useWindowContext();
124
+ const win = externalWindow ?? globalThis;
120
125
  const setReady = useMemo(() => {
121
126
  return registerReady ? registerReady('maxHeight') : undefined;
122
127
  }, [registerReady]);
123
- const [maxHeight, setMaxHeight] = useState(() => globalThis.innerHeight ?? 0);
128
+ const [maxHeight, setMaxHeight] = useState(() => win.innerHeight ?? 0);
124
129
  const raf = useRef(0);
125
130
  const maxReadyCalledRef = useRef(false);
126
131
  useLayoutEffect(() => {
127
132
  maxReadyCalledRef.current = false;
128
133
  }, [setReady]);
129
134
  useLayoutEffect(() => {
135
+ const rAF = win.requestAnimationFrame.bind(win);
136
+ const cAF = win.cancelAnimationFrame.bind(win);
130
137
  const handleResize = () => {
131
138
  if (raf.current) {
132
139
  return;
133
140
  }
134
- raf.current = requestAnimationFrame(() => {
141
+ raf.current = rAF(() => {
135
142
  if (raf.current !== 0) {
136
- setMaxHeight(globalThis.innerHeight);
143
+ setMaxHeight(win.innerHeight);
137
144
  raf.current = 0;
138
145
  }
139
146
  });
140
147
  };
141
- globalThis.addEventListener('resize', handleResize);
142
- setMaxHeight(globalThis.innerHeight);
148
+ win.addEventListener('resize', handleResize);
149
+ setMaxHeight(win.innerHeight);
143
150
  if (setReady && !maxReadyCalledRef.current) {
144
151
  maxReadyCalledRef.current = true;
145
152
  setReady();
146
153
  }
147
154
  return () => {
148
- globalThis.removeEventListener('resize', handleResize);
149
- cancelAnimationFrame(raf.current);
155
+ win.removeEventListener('resize', handleResize);
156
+ cAF(raf.current);
150
157
  };
151
- }, [setReady, globalThis]);
158
+ }, [setReady, win]);
152
159
  return maxHeight;
153
160
  }
154
161
  function usePreservedHeight(currentHeight) {
@@ -331,6 +338,8 @@ function defaultInitialSnap({ snapPoints, lastSnap: _lastSnap, }) {
331
338
  }
332
339
  function useDrawerGestures(options) {
333
340
  const { anchor, open, onClose, snapPoints: getSnapPoints = defaultSnapPoints, initialSnap: getInitialSnap = defaultInitialSnap, contentRef, headerRef, footerRef, headerEnabled, footerEnabled, } = options;
341
+ const { externalWindow } = useWindowContext();
342
+ const gestureWindow = externalWindow;
334
343
  const { ready, registerReady } = useReady();
335
344
  const heightRef = useRef(0);
336
345
  const lastSnapRef = useRef(null);
@@ -515,9 +524,13 @@ function useDrawerGestures(options) {
515
524
  return memo;
516
525
  });
517
526
  const isVertical = anchor === 'top' || anchor === 'bottom';
518
- const bind = useDrag(handleDrag, {
527
+ const gestureConfig = useMemo(() => ({
519
528
  filterTaps: true,
520
- });
529
+ ...(gestureWindow && {
530
+ window: gestureWindow,
531
+ }),
532
+ }), [gestureWindow]);
533
+ const bind = useDrag(handleDrag, gestureConfig);
521
534
  const heightStyle = useTransform(heightMotionValue, (y) => {
522
535
  const clamped = Math.max(0, Math.min(y, maxHeight));
523
536
  return `${Math.round(clamped)}px`;
package/Tab.js CHANGED
@@ -21,7 +21,7 @@ import './chunks/useFocusTrapStack.js';
21
21
  import './chunks/Backdrop.js';
22
22
  import './chunks/PopperPaper.js';
23
23
 
24
- var Tab$1 = {"sui-tab-container":"sui-tab-container","sui-tab-pill-badge":"sui-tab-pill-badge","sui-tab-badge-root":"sui-tab-badge-root","sui-tab-root":"sui-tab-root","sui-selected":"sui-selected","sui-focus-visible":"sui-focus-visible","sui-tab-badge":"sui-tab-badge","sui-active":"sui-active","sui-tab-label":"sui-tab-label","sui-tab-label-container":"sui-tab-label-container","sui-tab-label-text":"sui-tab-label-text","sui-tab-label-badge-root":"sui-tab-label-badge-root","sui-tab-icon":"sui-tab-icon","sui-tab-icon-container":"sui-tab-icon-container","sui-tab-icon-pill":"sui-tab-icon-pill","sui-tab-icon-badge-root":"sui-tab-icon-badge-root","sui-tab-large":"sui-tab-large","sui-tab-pill":"sui-tab-pill","sui-tab-pill-small":"sui-tab-pill-small","sui-tab-label-pill-cobranding":"sui-tab-label-pill-cobranding","sui-tab-icon-pill-cobranding":"sui-tab-icon-pill-cobranding","sui-compact":"sui-compact"};
24
+ var Tab$1 = {"sui-tab-container":"sui-tab-container","sui-tab-pill-badge":"sui-tab-pill-badge","sui-tab-badge-root":"sui-tab-badge-root","sui-tab-root":"sui-tab-root","sui-selected":"sui-selected","sui-focus-visible":"sui-focus-visible","sui-tab-badge":"sui-tab-badge","sui-tab-label":"sui-tab-label","sui-tab-label-container":"sui-tab-label-container","sui-tab-label-text":"sui-tab-label-text","sui-tab-label-badge-root":"sui-tab-label-badge-root","sui-tab-icon":"sui-tab-icon","sui-tab-icon-container":"sui-tab-icon-container","sui-tab-icon-pill":"sui-tab-icon-pill","sui-tab-icon-badge-root":"sui-tab-icon-badge-root","sui-tab-large":"sui-tab-large","sui-tab-pill":"sui-tab-pill","sui-tab-pill-small":"sui-tab-pill-small","sui-tab-label-pill-cobranding":"sui-tab-label-pill-cobranding","sui-tab-icon-pill-cobranding":"sui-tab-icon-pill-cobranding","sui-compact":"sui-compact"};
25
25
 
26
26
  const Tab = forwardRef((inProps, ref) => {
27
27
  const props = useThemeProps({ props: inProps, name: 'Tab' });
package/TableRow.js CHANGED
@@ -1,7 +1,6 @@
1
1
  import { jsx } from 'react/jsx-runtime';
2
2
  import { forwardRef } from 'react';
3
- import { useButton } from '@mui/base';
4
- import { useClasses } from '@ringcentral/spring-base';
3
+ import { useClasses, useButton } from '@ringcentral/spring-base';
5
4
  import { useThemeProps } from '@ringcentral/spring-theme';
6
5
  import { u as useTablelvl2Context } from './chunks/Tablelvl2Context.js';
7
6
 
@@ -21,7 +20,7 @@ const TableRow = forwardRef((inProps, ref) => {
21
20
  rootElementName: TableRowRoot,
22
21
  rootRef: ref,
23
22
  });
24
- const rootProps = getRootProps();
23
+ const rootProps = getRootProps(rest);
25
24
  const role = roleProp ?? (TableRowRoot !== defaultComponent ? 'row' : undefined);
26
25
  return (jsx(TableRowRoot, { className: cls('root', variant, {
27
26
  hoverable,
package/Tabs.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
2
2
  import React, { forwardRef, useState, useMemo, useRef, Children, isValidElement, cloneElement, useEffect, useLayoutEffect, useCallback } from 'react';
3
3
  import { useTab, useTabsList, TabsListProvider } from '@mui/base';
4
- import { useClasses, combineClasses, getTitle, useFontLoaded, useWindowContext, logInDev, usePrevious, useEventCallback, useOnReRender, useForkRef } from '@ringcentral/spring-base';
4
+ import { useClasses, combineClasses, getTitle, combineProps, useFontLoaded, useWindowContext, logInDev, usePrevious, useEventCallback, useOnReRender, useForkRef } from '@ringcentral/spring-base';
5
5
  import { OverflowMd, CaretLeftMd, CaretRightMd } from '@ringcentral/spring-icon';
6
6
  import { useMode, motionTransition, useThemeProps, useClassesWithMode } from '@ringcentral/spring-theme';
7
7
  import { B as Badge } from './chunks/Badge.js';
@@ -92,12 +92,11 @@ const TabMoreMenu = forwardRef((props, ref) => {
92
92
  };
93
93
  const handleClose = (event, reason) => {
94
94
  setAnchor(null);
95
- MenuProps?.onClose?.(event, reason);
96
95
  };
97
96
  const iconButton = (jsx(IconButton, { color: 'secondary', symbol: OverflowMd, ...iconButtonProps, onClick: (e) => {
98
97
  setAnchor((prev) => (prev ? null : e.currentTarget));
99
98
  }, "aria-expanded": Boolean(anchor), "aria-label": getTitle(TooltipProps?.title), ...ButtonProps, classes: iconButtonClasses }));
100
- return (jsxs(Fragment, { children: [jsxs("div", { className: cls('root'), ref: ref, ...rest, children: [TooltipProps ? (jsx(Tooltip, { ...TooltipProps, children: iconButton })) : (iconButton), Number(BadgeProps?.count) > 0 ? jsx(Badge, { ...badgeProps }) : null] }), jsx(Menu, { open: Boolean(anchor), anchorEl: anchor, onClose: handleClose, children: jsx(MenuList, { children: menuItems.map(({ ref, ...props }, index) => (jsx(TabMenuItem, { ...props, ref: ref, component: MenuItemComponent }, props.value || `TabMenuItem-${index}`))) }) })] }));
99
+ return (jsxs(Fragment, { children: [jsxs("div", { className: cls('root'), ref: ref, ...rest, children: [TooltipProps ? (jsx(Tooltip, { ...TooltipProps, children: iconButton })) : (iconButton), Number(BadgeProps?.count) > 0 ? jsx(Badge, { ...badgeProps }) : null] }), jsx(Menu, { open: Boolean(anchor), anchorEl: anchor, ...combineProps({ onClose: handleClose }, MenuProps), children: jsx(MenuList, { children: menuItems.map(({ ref, ...props }, index) => (jsx(TabMenuItem, { ...props, ref: ref, component: MenuItemComponent }, props.value || `TabMenuItem-${index}`))) }) })] }));
101
100
  });
102
101
  TabMoreMenu.displayName = 'SuiTabMoreMenu';
103
102
 
@@ -1,6 +1,6 @@
1
1
  import { createContext, useContext, forwardRef, isValidElement, useRef, useState, useImperativeHandle, useEffect } from 'react';
2
2
  import { jsx } from 'react/jsx-runtime';
3
- import { useForkRef, useEventCallback, useWindowContext, logInDev } from '@ringcentral/spring-base';
3
+ import { useForkRef, useWindowContext, useVirtuosoExternalWindow, useEventCallback, logInDev } from '@ringcentral/spring-base';
4
4
  import { useThemeProps } from '@ringcentral/spring-theme';
5
5
  import { Virtuoso } from 'react-virtuoso';
6
6
  import { L as List } from './chunks/List.js';
@@ -85,6 +85,8 @@ const VirtualizedList = forwardRef((inProps, ref) => {
85
85
  const handleVirtuosoRef = useForkRef(virtuosoRef, virtuosoActions);
86
86
  const initialFocusItemByIndexPromiseRef = useRef(null);
87
87
  const itemRenderedPromiseRef = useRef(null);
88
+ const { globalThis: contextWindow } = useWindowContext();
89
+ const externalWindowProps = useVirtuosoExternalWindow();
88
90
  const isItemRendered = (index) => {
89
91
  const { startIndex, endIndex } = listRangeRef.current;
90
92
  return index <= endIndex && index >= startIndex;
@@ -94,7 +96,7 @@ const VirtualizedList = forwardRef((inProps, ref) => {
94
96
  new Promise((resolve) => {
95
97
  initialFocusItemByIndexPromiseRef.current = resolve;
96
98
  }).then(() => {
97
- requestAnimationFrame(() => focusByIndexAction(index));
99
+ contextWindow.requestAnimationFrame(() => focusByIndexAction(index));
98
100
  });
99
101
  }
100
102
  else {
@@ -235,14 +237,7 @@ const VirtualizedList = forwardRef((inProps, ref) => {
235
237
  itemContentRenderer: children,
236
238
  computeItemKey: computeItemKey,
237
239
  };
238
- const { externalWindow } = useWindowContext();
239
- const externalProps = externalWindow &&
240
- externalWindow !== (window || globalThis.window)
241
- ? {
242
- externalWindow,
243
- }
244
- : undefined;
245
- return (jsx(Virtuoso, { className: "sui-virtualized-list", ...externalProps, ref: handleVirtuosoRef, increaseViewportBy: 200, ...(computeItemKey ? { computeItemKey } : undefined), ...rest, context: { ...context, ...virtuosoContext }, components: { ...VirtuosoComponents, ...components }, totalCount: totalCount, data: data, rangeChanged: (range) => {
240
+ return (jsx(Virtuoso, { className: "sui-virtualized-list", ...externalWindowProps, ref: handleVirtuosoRef, increaseViewportBy: 200, ...(computeItemKey ? { computeItemKey } : undefined), ...rest, context: { ...context, ...virtuosoContext }, components: { ...VirtuosoComponents, ...components }, totalCount: totalCount, data: data, rangeChanged: (range) => {
246
241
  listRangeRef.current = range;
247
242
  const itemRenderedPromise = itemRenderedPromiseRef.current;
248
243
  if (itemRenderedPromise) {
@@ -23,6 +23,6 @@ import './chunks/List.js';
23
23
  import './chunks/useListNavigation.js';
24
24
  import 'tabbable';
25
25
 
26
- var VirtualizedMenu = {"sui-virtualized-menu-paper":"sui-virtualized-menu-paper","sui-virtualized-menu-paper-content-container":"sui-virtualized-menu-paper-content-container"};
26
+ var VirtualizedMenu = {"sui-virtualized-menu-paper":"sui-virtualized-menu-paper","sui-virtualized-menu-paper-content-container":"sui-virtualized-menu-paper-content-container","sui-virtualized-menu-list":"sui-virtualized-menu-list"};
27
27
 
28
28
  export { VirtualizedMenu as virtualizedMenuStyle };
package/chunks/Menu.js CHANGED
@@ -30,8 +30,9 @@ const Menu = forwardRef((inProps, ref) => {
30
30
  onClose?.(undefined, 'Other');
31
31
  });
32
32
  const menuContextValue = useMemo(() => {
33
- return { close: closeHandler, autoFocus };
34
- }, [autoFocus, closeHandler]);
33
+ const effectiveAutoFocus = open ? autoFocus : false;
34
+ return { close: closeHandler, autoFocus: effectiveAutoFocus };
35
+ }, [autoFocus, closeHandler, open]);
35
36
  return (jsx(Popover, { bordered: true, className: cls('root'), PopperProps: { ...PopperProps, padding: popperPadding }, ...rest, open: open, onClose: (e, reason) => {
36
37
  onClose?.(e, reason);
37
38
  }, ref: ref, children: jsx("div", { className: cls('paper'), children: jsx(MenuContext.Provider, { value: menuContextValue, children: jsx(NestedMenuContext.Provider, { value: contextValue, children: children }) }) }) }));
@@ -1,5 +1,5 @@
1
1
  import { jsx } from 'react/jsx-runtime';
2
- import { forwardRef, useMemo, useRef, useEffect } from 'react';
2
+ import { forwardRef, useMemo, useRef, useLayoutEffect } from 'react';
3
3
  import { useClasses, useForkRef, useWindowContext } from '@ringcentral/spring-base';
4
4
  import { useThemeProps } from '@ringcentral/spring-theme';
5
5
  import { L as List } from './List.js';
@@ -29,12 +29,12 @@ const MenuList = forwardRef((inProps, ref) => {
29
29
  const listRef = useRef(null);
30
30
  const handleListRef = useForkRef(listRef, ref);
31
31
  const { document } = useWindowContext();
32
- useEffect(() => {
32
+ useLayoutEffect(() => {
33
33
  if (autoFocus &&
34
34
  !listRef.current?.contains(document?.activeElement ?? null)) {
35
35
  actions.current?.focus();
36
36
  }
37
- }, []);
37
+ }, [autoFocus]);
38
38
  const listRole = widget === 'select' ? 'listbox' : 'menu';
39
39
  const listboxProps = widget === 'select'
40
40
  ? { 'aria-multiselectable': selectMode === 'multiple' }
package/chunks/Modal.js CHANGED
@@ -31,7 +31,7 @@ const InnerModal = forwardRef((props, ref) => {
31
31
  onBackdropClick?.(event);
32
32
  onClose?.(event, 'backdropClick');
33
33
  });
34
- return (jsx(ModalContext.Provider, { value: { ariaHideEnabled }, children: jsxs(motion.div, { ...rest, ...motionProps, role: "presentation", className: cls('root'), onKeyDown: (e) => {
34
+ return (jsx(ModalContext.Provider, { value: { ariaHideEnabled }, children: jsxs(motion.div, { ...rest, ...motionProps, role: "presentation", className: cls('root', { 'non-blocking': !blocking }), onKeyDown: (e) => {
35
35
  onKeyDown?.(e);
36
36
  if (!disableEscapeKeyDown &&
37
37
  e.key === 'Escape' &&
@@ -1,5 +1,5 @@
1
1
  import { jsx } from 'react/jsx-runtime';
2
- import { createContext, useContext, useRef, useEffect, forwardRef, useState, useMemo } from 'react';
2
+ import { createContext, useContext, useRef, useLayoutEffect, forwardRef, useState, useMemo, useEffect } from 'react';
3
3
  import { useForkRef, combineProps, useClasses, useEventCallback } from '@ringcentral/spring-base';
4
4
  import { useThemeProps } from '@ringcentral/spring-theme';
5
5
  import clsx from 'clsx';
@@ -14,7 +14,11 @@ const VirtualizedMenuList = (props) => {
14
14
  const actions = useRef(null);
15
15
  const handleListActions = useForkRef(actions, actionsProp);
16
16
  const listRef = useRef(null);
17
- useEffect(() => {
17
+ const virtualizedMenuContext = useVirtualizedMenuContext();
18
+ useLayoutEffect(() => {
19
+ if (!virtualizedMenuContext?.open) {
20
+ return;
21
+ }
18
22
  const initialFocus = typeof initialFocusProps === 'function'
19
23
  ? initialFocusProps()
20
24
  : initialFocusProps;
@@ -24,20 +28,21 @@ const VirtualizedMenuList = (props) => {
24
28
  else if (typeof initialFocus === 'number') {
25
29
  actions.current?.focusByIndex(initialFocus);
26
30
  }
27
- }, []);
31
+ }, [virtualizedMenuContext?.open]);
28
32
  return (jsx(VirtualizedList, { actions: handleListActions, ...rest, ListProps: combineProps({
29
33
  role: 'menu',
30
34
  }, rest.ListProps), ref: listRef, totalListHeightChanged: totalListHeightChangedProp, children: children }));
31
35
  };
32
36
 
37
+ const MENU_VERTICAL_PADDING = 8;
33
38
  const VirtualizedMenu = forwardRef((inProps, ref) => {
34
39
  const props = useThemeProps({ props: inProps, name: 'VirtualizedMenu' });
35
40
  const { classes, className, children, open, onClose, data, totalCount, isItemDisabled, VirtualizedListProps, popperActions: popperActionsProp, initialFocus = true, maxHeight, PopperProps, ...rest } = props;
36
41
  const popperPaperContentRef = useRef(null);
37
42
  const paperContentContainerRef = useRef(null);
38
- const waitForListHeightComputeCallsCount = useRef(0);
43
+ const hasItemsRenderedRef = useRef(false);
39
44
  const [waitForHeightCompute, setWaitForHeightCompute] = useState(true);
40
- const [menuHeight, setMenuHeight] = useState(0);
45
+ const [menuHeight, setMenuHeight] = useState(MENU_VERTICAL_PADDING * 2);
41
46
  const [deferPendingVirtualizedListActions, setDeferPendingVirtualizedListActions,] = useState(true);
42
47
  const computedListHeight = useRef(-1);
43
48
  const availableHeight = useRef(-1);
@@ -75,13 +80,14 @@ const VirtualizedMenu = forwardRef((inProps, ref) => {
75
80
  }
76
81
  });
77
82
  const handleTotalListHeightChanged = useEventCallback((listHeight) => {
78
- if (waitForListHeightComputeCallsCount.current < 2) {
79
- waitForListHeightComputeCallsCount.current += 1;
83
+ if (!hasItemsRenderedRef.current) {
80
84
  return;
81
85
  }
82
- computedListHeight.current = listHeight;
86
+ const adjustedHeight = listHeight + MENU_VERTICAL_PADDING * 2
87
+ ;
88
+ computedListHeight.current = adjustedHeight;
83
89
  if (paperContentContainerRef.current) {
84
- paperContentContainerRef.current.style.height = `${listHeight}px`;
90
+ paperContentContainerRef.current.style.height = `${adjustedHeight}px`;
85
91
  }
86
92
  updateMenuHeight();
87
93
  popperActions.current?.update();
@@ -95,7 +101,7 @@ const VirtualizedMenu = forwardRef((inProps, ref) => {
95
101
  }
96
102
  });
97
103
  const handleAvailableHeightChange = useEventCallback((value) => {
98
- if (computedListHeight.current === -1) {
104
+ if (computedListHeight.current === -1 || !hasItemsRenderedRef.current) {
99
105
  return;
100
106
  }
101
107
  availableHeight.current = value;
@@ -104,13 +110,20 @@ const VirtualizedMenu = forwardRef((inProps, ref) => {
104
110
  });
105
111
  useEffect(() => {
106
112
  if (!open) {
107
- waitForListHeightComputeCallsCount.current = 0;
108
- computedListHeight.current = -1;
109
- availableHeight.current = -1;
113
+ hasItemsRenderedRef.current = false;
110
114
  }
111
115
  if (open) {
112
- setWaitForHeightCompute(true);
113
- setDeferPendingVirtualizedListActions(true);
116
+ if (computedListHeight.current !== -1) {
117
+ updateMenuHeight();
118
+ setWaitForHeightCompute(false);
119
+ setDeferPendingVirtualizedListActions(false);
120
+ popperActions.current?.update();
121
+ }
122
+ else {
123
+ setMenuHeight(MENU_VERTICAL_PADDING * 2);
124
+ setWaitForHeightCompute(true);
125
+ setDeferPendingVirtualizedListActions(true);
126
+ }
114
127
  if (popperPaperContentRef.current) {
115
128
  popperPaperContentRef.current.style.maxHeight = 'none';
116
129
  }
@@ -119,11 +132,23 @@ const VirtualizedMenu = forwardRef((inProps, ref) => {
119
132
  return (jsx(Popover, { bordered: true, PopperProps: { ...PopperProps, padding: popperPadding }, ...rest, open: open, className: clsx(cls('root'), waitForHeightCompute && 'opacity-0'), PopperPaperProps: {
120
133
  contentRef: popperPaperContentRef,
121
134
  }, onClose: onClose, onAvailableHeightChange: handleAvailableHeightChange, ref: ref, motionProps: { variants: undefined }, actions: handlePopperActions, children: jsx("div", { className: cls('paper'), children: jsx("div", { ref: paperContentContainerRef, className: cls('paper-content-container'), children: jsx(VirtualizedMenuContext.Provider, { value: menuContextValue, children: jsx(VirtualizedMenuList, { data: data, totalCount: totalCount, isReady: !deferPendingVirtualizedListActions, isItemDisabled: isItemDisabled, initialFocus: initialFocus, style: { height: menuHeight }, ...combineProps(VirtualizedListProps, {
122
- components: {
123
- Header: VirtualizedListVerticalPadding,
124
- Footer: VirtualizedListVerticalPadding,
125
- },
135
+ ListProps: combineProps(VirtualizedListProps?.ListProps, {
136
+ className: cls('list'),
137
+ style: {
138
+ ['--sui-virtualized-menu-vertical-padding']: `${MENU_VERTICAL_PADDING}px`,
139
+ },
140
+ }),
126
141
  totalListHeightChanged: handleTotalListHeightChanged,
142
+ itemsRendered: (items) => {
143
+ if (totalCount === 0 ||
144
+ (totalCount === undefined &&
145
+ data !== undefined &&
146
+ data.length === 0) ||
147
+ (items && items.length > 0)) {
148
+ hasItemsRenderedRef.current = true;
149
+ }
150
+ VirtualizedListProps?.itemsRendered?.(items);
151
+ },
127
152
  onKeyDown: (e) => {
128
153
  if (e.key === 'Tab') {
129
154
  e.preventDefault();
@@ -132,9 +157,6 @@ const VirtualizedMenu = forwardRef((inProps, ref) => {
132
157
  },
133
158
  }), children: children }) }) }) }) }));
134
159
  });
135
- function VirtualizedListVerticalPadding() {
136
- return jsx("div", { className: "h-2" });
137
- }
138
160
  VirtualizedMenu.displayName = 'SuiVirtualizedMenu';
139
161
 
140
162
  export { VirtualizedMenuContext as V, VirtualizedMenu as a, VirtualizedMenuList as b, useVirtualizedMenuContext as u };