reshaped 3.9.0-canary.2 → 3.9.0-canary.24

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 (90) hide show
  1. package/dist/bundle.css +1 -1
  2. package/dist/bundle.d.ts +2 -0
  3. package/dist/bundle.js +2 -31
  4. package/dist/components/Actionable/Actionable.module.css +1 -1
  5. package/dist/components/Autocomplete/Autocomplete.js +2 -2
  6. package/dist/components/Autocomplete/Autocomplete.types.d.ts +1 -1
  7. package/dist/components/Avatar/Avatar.js +7 -24
  8. package/dist/components/Avatar/Avatar.module.css +1 -1
  9. package/dist/components/Badge/Badge.js +2 -2
  10. package/dist/components/Badge/Badge.module.css +1 -1
  11. package/dist/components/Badge/Badge.types.d.ts +1 -1
  12. package/dist/components/Button/Button.module.css +1 -1
  13. package/dist/components/Calendar/Calendar.module.css +1 -1
  14. package/dist/components/Calendar/Calendar.types.d.ts +19 -6
  15. package/dist/components/Calendar/CalendarControlled.js +43 -8
  16. package/dist/components/Calendar/CalendarControls.js +9 -9
  17. package/dist/components/Calendar/CalendarDate.js +9 -7
  18. package/dist/components/Calendar/CalendarMonth.js +2 -2
  19. package/dist/components/Card/Card.d.ts +1 -1
  20. package/dist/components/Card/Card.types.d.ts +5 -5
  21. package/dist/components/Checkbox/Checkbox.js +2 -12
  22. package/dist/components/DropdownMenu/DropdownMenu.types.d.ts +1 -1
  23. package/dist/components/FileUpload/FileUpload.js +7 -5
  24. package/dist/components/FileUpload/FileUpload.module.css +1 -1
  25. package/dist/components/FileUpload/FileUpload.types.d.ts +2 -0
  26. package/dist/components/Flyout/Flyout.constants.d.ts +3 -3
  27. package/dist/components/Flyout/Flyout.constants.js +1 -0
  28. package/dist/components/Flyout/Flyout.module.css +1 -1
  29. package/dist/components/Flyout/Flyout.types.d.ts +8 -7
  30. package/dist/components/Flyout/FlyoutContent.js +4 -3
  31. package/dist/components/Flyout/FlyoutControlled.js +33 -11
  32. package/dist/components/Flyout/useFlyout.d.ts +2 -2
  33. package/dist/components/Flyout/useFlyout.js +8 -21
  34. package/dist/components/Flyout/utilities/calculatePosition.d.ts +6 -6
  35. package/dist/components/Flyout/utilities/calculatePosition.js +24 -17
  36. package/dist/components/Flyout/utilities/constants.d.ts +1 -0
  37. package/dist/components/Flyout/utilities/constants.js +1 -0
  38. package/dist/components/Flyout/utilities/flyout.js +37 -6
  39. package/dist/components/Flyout/utilities/isFullyVisible.d.ts +4 -3
  40. package/dist/components/Flyout/utilities/isFullyVisible.js +5 -4
  41. package/dist/components/Grid/Grid.types.d.ts +4 -4
  42. package/dist/components/HiddenInput/HiddenInput.js +33 -0
  43. package/dist/components/HiddenInput/HiddenInput.types.d.ts +26 -0
  44. package/dist/components/Image/Image.js +13 -8
  45. package/dist/components/Image/Image.module.css +1 -1
  46. package/dist/components/Image/Image.types.d.ts +3 -1
  47. package/dist/components/Modal/Modal.js +2 -5
  48. package/dist/components/Modal/Modal.module.css +1 -1
  49. package/dist/components/Popover/Popover.module.css +1 -1
  50. package/dist/components/Popover/Popover.types.d.ts +1 -1
  51. package/dist/components/Radio/Radio.js +2 -12
  52. package/dist/components/Reshaped/Reshaped.css +1 -1
  53. package/dist/components/Scrim/Scrim.js +4 -3
  54. package/dist/components/Scrim/Scrim.module.css +1 -1
  55. package/dist/components/Scrim/Scrim.types.d.ts +2 -1
  56. package/dist/components/ScrollArea/ScrollArea.js +7 -7
  57. package/dist/components/Slider/SliderControlled.js +4 -4
  58. package/dist/components/Tabs/Tabs.module.css +1 -1
  59. package/dist/components/Tabs/Tabs.types.d.ts +3 -1
  60. package/dist/components/Tabs/TabsContext.d.ts +1 -0
  61. package/dist/components/Tabs/TabsControlled.js +2 -1
  62. package/dist/components/Tabs/TabsItem.js +3 -3
  63. package/dist/components/Tabs/TabsList.js +9 -5
  64. package/dist/components/Tabs/TabsPanel.js +1 -1
  65. package/dist/components/Text/Text.d.ts +1 -1
  66. package/dist/components/Text/Text.js +2 -2
  67. package/dist/components/Text/Text.module.css +1 -1
  68. package/dist/components/Text/Text.types.d.ts +5 -3
  69. package/dist/components/Tooltip/Tooltip.js +2 -2
  70. package/dist/components/Tooltip/Tooltip.module.css +1 -1
  71. package/dist/components/Tooltip/Tooltip.types.d.ts +1 -1
  72. package/dist/components/View/View.types.d.ts +4 -4
  73. package/dist/hooks/_private/useDrag.js +0 -3
  74. package/dist/index.d.ts +2 -0
  75. package/dist/index.js +1 -0
  76. package/dist/types/global.d.ts +1 -1
  77. package/dist/utilities/dom/index.d.ts +0 -1
  78. package/dist/utilities/dom/index.js +0 -1
  79. package/dist/utilities/scroll/disable.js +4 -2
  80. package/package.json +4 -98
  81. package/README.md +0 -24
  82. package/dist/components/_private/HiddenInput/HiddenInput.js +0 -10
  83. package/dist/components/_private/HiddenInput/HiddenInput.types.d.ts +0 -15
  84. package/dist/utilities/dom/userSelect.d.ts +0 -2
  85. package/dist/utilities/dom/userSelect.js +0 -6
  86. /package/dist/components/{_private/HiddenInput → HiddenInput}/HiddenInput.d.ts +0 -0
  87. /package/dist/components/{_private/HiddenInput → HiddenInput}/HiddenInput.module.css +0 -0
  88. /package/dist/components/{_private/HiddenInput → HiddenInput}/HiddenInput.types.js +0 -0
  89. /package/dist/components/{_private/HiddenInput → HiddenInput}/index.d.ts +0 -0
  90. /package/dist/components/{_private/HiddenInput → HiddenInput}/index.js +0 -0
@@ -1,9 +1,9 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import React from "react";
4
- import HiddenInput from "../_private/HiddenInput/index.js";
5
4
  import { useCheckboxGroup } from "../CheckboxGroup/index.js";
6
5
  import { useFormControl } from "../FormControl/index.js";
6
+ import HiddenInput from "../HiddenInput/index.js";
7
7
  import Icon from "../Icon/index.js";
8
8
  import Text from "../Text/index.js";
9
9
  import useIsomorphicLayoutEffect from "../../hooks/useIsomorphicLayoutEffect.js";
@@ -21,20 +21,10 @@ const Checkbox = (props) => {
21
21
  const name = checkboxGroup ? checkboxGroup.name : props.name;
22
22
  const inputRef = React.useRef(null);
23
23
  const rootClassName = classNames(s.root, className, size && hasError && s["--error"], disabled && s["--disabled"], size && responsiveClassNames(s, "--size", size));
24
- const handleChange = (event) => {
25
- if (!name)
26
- return;
27
- const { checked } = event.target;
28
- const changeArgs = { name, value, checked, event };
29
- if (onChange)
30
- onChange(changeArgs);
31
- if (checkboxGroup?.onChange)
32
- checkboxGroup.onChange(changeArgs);
33
- };
34
24
  useIsomorphicLayoutEffect(() => {
35
25
  inputRef.current.indeterminate = indeterminate || false;
36
26
  }, [indeterminate, checked]);
37
- return (_jsxs("label", { ...attributes, className: rootClassName, children: [_jsxs("span", { className: s.field, children: [_jsx(HiddenInput, { className: s.input, type: "checkbox", checked: checked, defaultChecked: defaultChecked, name: name, disabled: disabled, value: value, onChange: handleChange, onFocus: onFocus, onBlur: onBlur, attributes: {
27
+ return (_jsxs("label", { ...attributes, className: rootClassName, children: [_jsxs("span", { className: s.field, children: [_jsx(HiddenInput, { className: s.input, type: "checkbox", checked: checked, defaultChecked: defaultChecked, name: name, disabled: disabled, value: value, onChange: onChange, onFocus: onFocus, onBlur: onBlur, attributes: {
38
28
  ...inputAttributes,
39
29
  ref: inputRef,
40
30
  } }), _jsx("div", { className: s.decorator, children: _jsx(Icon, { svg: IconCheckmark, className: s.icon, size: responsivePropDependency(size, (size) => {
@@ -3,7 +3,7 @@ import type { MenuItemProps } from "../MenuItem";
3
3
  import type { PopoverProps, PopoverInstance } from "../Popover";
4
4
  import type React from "react";
5
5
  export type Instance = PopoverInstance;
6
- export type Props = Pick<PopoverProps, "children" | "position" | "forcePosition" | "fallbackPositions" | "fallbackAdjustLayout" | "fallbackMinWidth" | "fallbackMinHeight" | "triggerType" | "contentGap" | "contentShift" | "onOpen" | "onClose" | "active" | "defaultActive" | "width" | "disableHideAnimation" | "disableCloseOnOutsideClick" | "instanceRef" | "containerRef" | "positionRef" | "originCoordinates" | "borderRadius" | "elevation" | "initialFocusRef"> & {
6
+ export type Props = Pick<PopoverProps, "children" | "position" | "forcePosition" | "fallbackPositions" | "fallbackAdjustLayout" | "fallbackMinWidth" | "fallbackMinHeight" | "triggerType" | "contentGap" | "contentShift" | "contentMaxHeight" | "onOpen" | "onClose" | "active" | "defaultActive" | "width" | "disableHideAnimation" | "disableCloseOnOutsideClick" | "instanceRef" | "containerRef" | "positionRef" | "originCoordinates" | "borderRadius" | "elevation" | "initialFocusRef"> & {
7
7
  /** Change component trap focus keyboard behavior and shortcuts */
8
8
  trapFocusMode?: Extract<PopoverProps["trapFocusMode"], "action-menu" | "selection-menu"> | false;
9
9
  };
@@ -10,9 +10,9 @@ export const FileUploadTrigger = (props) => {
10
10
  return _jsx("span", { className: s.trigger, children: children });
11
11
  };
12
12
  const FileUpload = (props) => {
13
- const { name, children, height, variant = "outline", inline, className, attributes, inputAttributes, onChange, } = props;
13
+ const { name, children, height, variant = "outline", inline, className, disabled, attributes, inputAttributes, onChange, } = props;
14
14
  const highlightToggle = useToggle();
15
- const rootClassNames = classNames(s.root, variant && s[`--variant-${variant}`], inline && s[`--inline`], highlightToggle.active && s["--highlighted"], className);
15
+ const rootClassNames = classNames(s.root, variant && s[`--variant-${variant}`], inline && s[`--inline`], highlightToggle.active && s["--highlighted"], disabled && s["--disabled"], className);
16
16
  const handleDragOver = (event) => {
17
17
  event.preventDefault();
18
18
  attributes?.onDragOver?.(event);
@@ -41,15 +41,17 @@ const FileUpload = (props) => {
41
41
  onChange?.({ name, event, value: Array.from(nextValue) });
42
42
  inputAttributes?.onChange?.(event);
43
43
  };
44
- const inputNode = (_jsx(HiddenVisually, { children: _jsx("input", { ...inputAttributes, type: "file", className: s.field, name: name, onChange: handleChange }) }));
44
+ const inputNode = (_jsx(HiddenVisually, { children: _jsx("input", { ...inputAttributes, type: "file", className: s.field, name: name, disabled: disabled, onChange: handleChange }) }));
45
45
  const childrenNode = typeof children === "function" ? children({ highlighted: highlightToggle.active }) : children;
46
- return (_jsx(View, { className: rootClassNames, height: height, attributes: {
46
+ return (_jsx(View, { className: rootClassNames, height: height,
47
+ // For the focus ring radius
48
+ borderRadius: "medium", attributes: {
47
49
  ...attributes,
48
50
  onDragOver: handleDragOver,
49
51
  onDragEnter: handleDragEnter,
50
52
  onDragLeave: handleDragLeave,
51
53
  onDrop: handleDrop,
52
- }, children: variant === "outline" && !inline ? (_jsxs(View, { as: "label", className: s.triggerLayer, padding: 6, borderRadius: "medium", gap: 2, align: "center", justify: "center", textAlign: "center", animated: true, height: "100%", children: [inputNode, _jsx(View.Item, { children: childrenNode })] })) : (_jsxs("label", { className: s.triggerLayer, children: [inputNode, childrenNode] })) }));
54
+ }, children: variant === "outline" && !inline ? (_jsxs(View, { as: "label", className: s.triggerLayer, padding: 6, borderRadius: "medium", gap: 2, align: "center", justify: "center", textAlign: "center", height: "100%", children: [inputNode, _jsx(View.Item, { children: childrenNode })] })) : (_jsxs("label", { className: s.triggerLayer, children: [inputNode, childrenNode] })) }));
53
55
  };
54
56
  FileUpload.displayName = "FileUpload";
55
57
  FileUploadTrigger.displayName = "FileUpload.Trigger";
@@ -1 +1 @@
1
- .root{--rs-file-upload-radius:var(--rs-radius-medium);display:block}[data-rs-keyboard] .root:focus-within{box-shadow:var(--rs-shadow-focus)}.--inline{--rs-file-upload-radius:var(--rs-radius-small)}.--inline,.--inline .triggerLayer{display:inline-block;vertical-align:top}[data-rs-keyboard] .--inline:focus-within{box-shadow:none}[data-rs-keyboard] .--inline:focus-within .triggerLayer>*{box-shadow:var(--rs-shadow-focus)}.--variant-outline .triggerLayer{border:1px dashed var(--rs-color-border-neutral);border-radius:var(--rs-file-upload-radius)}.--highlighted.--variant-outline .triggerLayer{background:rgba(var(--rs-color-rgb-background-primary),.08);border-color:var(--rs-color-border-primary)}@media (hover:hover) and (pointer:fine){.--variant-outline .triggerLayer:hover:not(:has(.trigger)){background:rgba(var(--rs-color-rgb-background-neutral),.16)}}.triggerLayer:has(.trigger){pointer-events:none}.triggerLayer:has(.trigger) .trigger{pointer-events:all}.trigger{display:contents}
1
+ .root{--rs-file-upload-radius:var(--rs-radius-medium);--rs-file-upload-border-color:var(--rs-color-border-neutral);--rs-file-upload-hover-background-color:rgba(var(--rs-color-rgb-background-neutral),0.16);display:block}[data-rs-keyboard] .root:focus-within{box-shadow:var(--rs-shadow-focus)}.triggerLayer:has(.trigger){pointer-events:none}.triggerLayer:has(.trigger) .trigger,.triggerLayer:has(.trigger) a,.triggerLayer:has(.trigger) button{pointer-events:all}.trigger{display:contents}.--inline{--rs-file-upload-radius:var(--rs-radius-small)}.--inline,.--inline .triggerLayer{display:inline-block;vertical-align:top}[data-rs-keyboard] .--inline:focus-within{box-shadow:none}[data-rs-keyboard] .--inline:focus-within .triggerLayer>*{box-shadow:var(--rs-shadow-focus)}.--variant-outline .triggerLayer{border:1px dashed var(--rs-file-upload-border-color);border-radius:var(--rs-file-upload-radius);transition:var(--rs-duration-fast) var(--rs-easing-standard);transition-property:background-color,border-color}.--highlighted.--variant-outline .triggerLayer{background:rgba(var(--rs-color-rgb-background-primary),.08);border-color:var(--rs-color-border-primary)}@media (hover:hover) and (pointer:fine){.--variant-outline .triggerLayer:hover:not(:has(.trigger)){background:var(--rs-file-upload-hover-background-color)}}.--disabled{--rs-file-upload-border-color:var(--rs-color-border-disabled);--rs-file-upload-hover-background-color:transparent}.--disabled .triggerLayer{cursor:not-allowed;opacity:.4}
@@ -4,6 +4,8 @@ import type * as G from "../../types/global";
4
4
  export type Props = {
5
5
  /** Name of the input element */
6
6
  name: string;
7
+ /** Disable the file upload input */
8
+ disabled?: boolean;
7
9
  /** Node for inserting children, can be a render function that receives component state */
8
10
  children?: React.ReactNode | ((props: {
9
11
  highlighted?: boolean;
@@ -1,4 +1,4 @@
1
- import type * as T from "./Flyout.types";
2
1
  export declare const mouseEnter = 600;
3
- export declare const defaultStyles: T.Styles;
4
- export declare const resetStyles: T.Styles;
2
+ export declare const mouseLeave = 150;
3
+ export declare const defaultStyles: React.CSSProperties;
4
+ export declare const resetStyles: React.CSSProperties;
@@ -1,4 +1,5 @@
1
1
  export const mouseEnter = 600;
2
+ export const mouseLeave = 150;
2
3
  export const defaultStyles = {
3
4
  left: 0,
4
5
  top: 0,
@@ -1 +1 @@
1
- .content{--rs-flyout-gap:0;--rs-flyout-origin-x:50%;--rs-flyout-origin-y:50%;isolation:isolate;pointer-events:none;position:absolute}.content.--hover{pointer-events:all}.content.--hover-disabled,.content.--hover-disabled .inner{pointer-events:none}.inner{backface-visibility:hidden;height:100%;max-height:100%;max-width:100%;opacity:0;outline:none;overflow:auto;pointer-events:all;transform:scale(.92) translateY(0);transform-origin:var(--rs-flyout-origin-x) var(--rs-flyout-origin-y);transition:1ms var(--rs-easing-accelerate)}[data-rs-keyboard] .inner:focus{box-shadow:var(--rs-shadow-focus)}.content.--width-trigger .inner{transform:scale(1) translateY(var(--rs-unit-x2))}.content.--position-top,.content.--position-top-end,.content.--position-top-start{--rs-flyout-origin-y:100%;padding-bottom:calc(var(--rs-unit-x1) * var(--rs-flyout-gap))}.content.--position-bottom,.content.--position-bottom-end,.content.--position-bottom-start{--rs-flyout-origin-y:0%;padding-top:calc(var(--rs-unit-x1) * var(--rs-flyout-gap))}.content.--position-bottom-start,.content.--position-top-start{--rs-flyout-origin-x:0%}.content.--position-bottom-end,.content.--position-top-end{--rs-flyout-origin-x:100%}.content.--position-start,.content.--position-start-bottom,.content.--position-start-top{--rs-flyout-origin-x:100%;padding-right:calc(var(--rs-unit-x1) * var(--rs-flyout-gap))}.content.--position-end,.content.--position-end-bottom,.content.--position-end-top{--rs-flyout-origin-x:0%;padding-left:calc(var(--rs-unit-x1) * var(--rs-flyout-gap))}.content.--position-end-top,.content.--position-start-top{--rs-flyout-origin-y:0%}.content.--position-end-bottom,.content.--position-start-bottom{--rs-flyout-origin-y:100%}.content.--visible .inner{opacity:1;transform:scale(1) translateY(0)}.content.--animated .inner{transition-duration:var(--rs-duration-rapid);transition-property:opacity,transform}.content.--animated.--visible .inner{transition-duration:var(--rs-duration-fast);transition-timing-function:var(--rs-easing-decelerate)}
1
+ .content{--rs-flyout-gap:0;--rs-flyout-max-h:100%;--rs-flyout-max-w:100%;--rs-flyout-origin-x:50%;--rs-flyout-origin-y:50%;display:flex;flex-direction:column;isolation:isolate;pointer-events:none;position:absolute}.content.--hover{pointer-events:all}.content.--hover-disabled,.content.--hover-disabled .inner{pointer-events:none}.inner{backface-visibility:hidden;flex-grow:1;height:100%;max-height:var(--rs-flyout-max-h);max-width:var(--rs-flyout-max-w);opacity:0;outline:none;overflow:auto;pointer-events:all;transform:scale(.92) translateY(0);transform-origin:var(--rs-flyout-origin-x) var(--rs-flyout-origin-y);transition:1ms var(--rs-easing-accelerate)}[data-rs-keyboard] .inner:focus{box-shadow:var(--rs-shadow-focus)}.content.--width-trigger .inner{transform:scale(1) translateY(var(--rs-unit-x2))}.content.--position-top,.content.--position-top-end,.content.--position-top-start{--rs-flyout-origin-y:100%;padding-bottom:calc(var(--rs-unit-x1) * var(--rs-flyout-gap))}.content.--position-bottom,.content.--position-bottom-end,.content.--position-bottom-start{--rs-flyout-origin-y:0%;padding-top:calc(var(--rs-unit-x1) * var(--rs-flyout-gap))}.content.--position-bottom-start,.content.--position-top-start{--rs-flyout-origin-x:0%}.content.--position-bottom-end,.content.--position-top-end{--rs-flyout-origin-x:100%}.content.--position-start,.content.--position-start-bottom,.content.--position-start-top{--rs-flyout-origin-x:100%;padding-right:calc(var(--rs-unit-x1) * var(--rs-flyout-gap))}.content.--position-end,.content.--position-end-bottom,.content.--position-end-top{--rs-flyout-origin-x:0%;padding-left:calc(var(--rs-unit-x1) * var(--rs-flyout-gap))}.content.--position-end-top,.content.--position-start-top{--rs-flyout-origin-y:0%}.content.--position-end-bottom,.content.--position-start-bottom{--rs-flyout-origin-y:100%}.content.--visible .inner{opacity:1;transform:scale(1) translateY(0)}.content.--animated .inner{transition-duration:var(--rs-duration-rapid);transition-property:opacity,transform}.content.--animated.--visible .inner{transition-duration:var(--rs-duration-fast);transition-timing-function:var(--rs-easing-decelerate)}
@@ -23,17 +23,14 @@ export type Options = Pick<BaseProps, "width" | "fallbackAdjustLayout" | "fallba
23
23
  lastUsedPosition: Position;
24
24
  onPositionChoose: (position: Position) => void;
25
25
  };
26
- export type Styles = React.CSSProperties;
27
26
  export type State = {
28
- styles: Styles;
29
27
  position?: Position;
30
28
  status: "idle" | "rendered" | "positioned" | "visible" | "hidden";
31
29
  };
32
30
  export type FlyoutData = {
33
- styles: Styles;
34
31
  position: Position;
35
32
  };
36
- export type UseFlyoutData = Pick<State, "styles" | "position" | "status"> & {
33
+ export type UseFlyoutData = Pick<State, "position" | "status"> & {
37
34
  updatePosition: (args?: {
38
35
  sync?: boolean;
39
36
  fallback?: boolean;
@@ -72,7 +69,7 @@ export type TriggerAttributes = {
72
69
  onFocus?: () => void;
73
70
  onMouseDown?: () => void;
74
71
  onMouseEnter?: () => void;
75
- onMouseLeave?: () => void;
72
+ onMouseLeave?: (e: React.MouseEvent) => void;
76
73
  onTouchStart?: () => void;
77
74
  onClick?: () => void;
78
75
  "aria-describedby"?: string;
@@ -132,6 +129,10 @@ type BaseProps = {
132
129
  contentGap?: number;
133
130
  /** Shift the content on the secondary axis, relative to its original position */
134
131
  contentShift?: number;
132
+ /** Maximum height for the content */
133
+ contentMaxHeight?: string;
134
+ /** Maximum width for the content */
135
+ contentMaxWidth?: string;
135
136
  /** Additional classname for the content element */
136
137
  contentClassName?: string;
137
138
  /** Additional attributes for the content element */
@@ -178,7 +179,7 @@ export type ContextProps = {
178
179
  }) => void;
179
180
  handleOpen: () => void;
180
181
  handleMouseEnter: () => void;
181
- handleMouseLeave: () => void;
182
+ handleMouseLeave: (e: React.MouseEvent) => void;
182
183
  handleMouseDown: () => void;
183
184
  handleTransitionEnd: (e: React.TransitionEvent) => void;
184
185
  handleTransitionStart: (e: TransitionEvent) => void;
@@ -189,7 +190,7 @@ export type ContextProps = {
189
190
  handleContentMouseDown: () => void;
190
191
  handleContentMouseUp: () => void;
191
192
  isSubmenu: boolean;
192
- } & Pick<Props, "triggerType" | "contentClassName" | "contentAttributes" | "contentGap" | "trapFocusMode" | "containerRef" | "disableContentHover" | "autoFocus">;
193
+ } & Pick<Props, "triggerType" | "contentClassName" | "contentAttributes" | "contentGap" | "contentMaxHeight" | "contentMaxWidth" | "trapFocusMode" | "containerRef" | "disableContentHover" | "autoFocus">;
193
194
  export type TriggerContextProps = {
194
195
  elRef?: ContextProps["triggerElRef"];
195
196
  };
@@ -11,8 +11,8 @@ import s from "./Flyout.module.css";
11
11
  import cooldown from "./utilities/cooldown.js";
12
12
  const FlyoutContent = (props) => {
13
13
  const { children, className, attributes } = props;
14
- const { flyout, id, flyoutElRef, triggerElRef, handleClose, handleTransitionEnd, handleTransitionStart, triggerType, handleMouseEnter, handleMouseLeave, handleContentMouseDown, handleContentMouseUp, contentClassName, contentAttributes, contentGap, trapFocusMode, disableContentHover, autoFocus, width, containerRef: passedContainerRef, isSubmenu, } = useFlyoutContext();
15
- const { styles, status, position } = flyout;
14
+ const { flyout, id, flyoutElRef, triggerElRef, handleClose, handleTransitionEnd, handleTransitionStart, triggerType, handleMouseEnter, handleMouseLeave, handleContentMouseDown, handleContentMouseUp, contentClassName, contentAttributes, contentGap, contentMaxHeight, contentMaxWidth, trapFocusMode, disableContentHover, autoFocus, width, containerRef: passedContainerRef, isSubmenu, } = useFlyoutContext();
15
+ const { status, position } = flyout;
16
16
  const [mounted, setMounted] = React.useState(false);
17
17
  const closestFixedContainer = React.useMemo(() => {
18
18
  if (!mounted)
@@ -95,8 +95,9 @@ const FlyoutContent = (props) => {
95
95
  role = "menubar";
96
96
  }
97
97
  const content = (_jsx(ContentProvider, { value: { elRef: flyoutElRef }, children: _jsx("div", { className: rootClassNames, style: {
98
- ...styles,
99
98
  "--rs-flyout-gap": contentGap,
99
+ "--rs-flyout-max-h": contentMaxHeight,
100
+ "--rs-flyout-max-w": contentMaxWidth,
100
101
  }, ref: flyoutElRef, onTransitionEnd: handleTransitionEnd, onMouseEnter: triggerType === "hover" ? handleMouseEnter : undefined, onMouseLeave: triggerType === "hover" ? handleMouseLeave : undefined, onMouseDown: handleContentMouseDown, onTouchStart: handleContentMouseDown, onMouseUp: handleContentMouseUp, onTouchEnd: handleContentMouseUp, children: _jsx("div", { role: role, ...attributes, id: id, tabIndex: !autoFocus ? -1 : undefined, "aria-modal": role === "dialog" ? true : undefined, style: { ...attributes?.style, ...contentAttributes?.style }, className: innerClassNames, children: children }) }) }));
101
102
  return _jsx(Portal, { targetRef: containerRef, children: content });
102
103
  };
@@ -15,7 +15,7 @@ import { Provider, useFlyoutTriggerContext, useFlyoutContext, useFlyoutContentCo
15
15
  import useFlyout from "./useFlyout.js";
16
16
  import cooldown from "./utilities/cooldown.js";
17
17
  const FlyoutControlled = (props) => {
18
- const { triggerType = "click", groupTimeouts, onOpen, onClose, children, disabled, forcePosition, fallbackAdjustLayout, fallbackMinWidth, fallbackMinHeight, trapFocusMode = "dialog", width, disableHideAnimation, disableContentHover, disableCloseOnOutsideClick, autoFocus = true, originCoordinates, contentGap = 2, contentShift, contentClassName, contentAttributes, position: passedPosition, active: passedActive, id: passedId, instanceRef, containerRef, initialFocusRef, positionRef, } = props;
18
+ const { triggerType = "click", groupTimeouts, onOpen, onClose, children, disabled, forcePosition, fallbackAdjustLayout, fallbackMinWidth, fallbackMinHeight, trapFocusMode = "dialog", width, disableHideAnimation, disableContentHover, disableCloseOnOutsideClick, autoFocus = true, originCoordinates, contentGap = 2, contentShift, contentMaxHeight, contentMaxWidth, contentClassName, contentAttributes, position: passedPosition, active: passedActive, id: passedId, instanceRef, containerRef, initialFocusRef, positionRef, } = props;
19
19
  const fallbackPositions = props.fallbackPositions === false || forcePosition ? [] : props.fallbackPositions;
20
20
  const onOpenRef = useHandlerRef(onOpen);
21
21
  const onCloseRef = useHandlerRef(onClose);
@@ -55,12 +55,13 @@ const FlyoutControlled = (props) => {
55
55
  // Touch devices trigger onMouseEnter but we don't need to apply regular hover timeouts
56
56
  // So we're saving a flag on touch start and then change the mouse enter behavior
57
57
  const hoverTriggeredWithTouchEventRef = React.useRef(false);
58
+ const originCoordinatesRef = React.useRef(originCoordinates ?? null);
58
59
  // eslint-disable-next-line react-hooks/refs
60
+ originCoordinatesRef.current = originCoordinates ?? null;
59
61
  const flyout = useFlyout({
60
62
  triggerElRef: positionRef ?? triggerElRef,
61
63
  flyoutElRef,
62
- // eslint-disable-next-line react-hooks/refs
63
- triggerBounds: originCoordinates ?? triggerBoundsRef.current,
64
+ triggerBoundsRef: originCoordinates ? originCoordinatesRef : triggerBoundsRef,
64
65
  width,
65
66
  position: passedPosition,
66
67
  defaultActive: resolvedActive,
@@ -103,7 +104,7 @@ const FlyoutControlled = (props) => {
103
104
  return;
104
105
  onCloseRef.current?.({ reason: options.reason });
105
106
  if (options?.closeParents) {
106
- parentFlyoutContext?.handleClose?.({});
107
+ parentFlyoutContext?.handleClose?.({ closeParents: true, reason: options.reason });
107
108
  }
108
109
  }, [isRendered, isDismissible, triggerType, onCloseRef, disabled, parentFlyoutContext]);
109
110
  /**
@@ -143,14 +144,31 @@ const FlyoutControlled = (props) => {
143
144
  cooldown.warm();
144
145
  timerRef.current = setTimeout(() => {
145
146
  handleOpen();
146
- }, groupTimeouts && cooldown.status === "warming" ? timeouts.mouseEnter : 0);
147
+ }, groupTimeouts && cooldown.status === "warming"
148
+ ? timeouts.mouseEnter
149
+ : isSubmenu
150
+ ? timeouts.mouseEnter
151
+ : 0);
147
152
  }
148
- }, [clearTimer, handleOpen, groupTimeouts]);
149
- const handleMouseLeave = React.useCallback(() => {
153
+ }, [clearTimer, handleOpen, groupTimeouts, isSubmenu]);
154
+ const handleMouseLeave = React.useCallback((e) => {
155
+ if (e.relatedTarget === flyoutElRef.current ||
156
+ (e.relatedTarget instanceof Node && flyoutElRef.current?.contains(e.relatedTarget)))
157
+ return;
158
+ if (e.relatedTarget === triggerElRef.current ||
159
+ (e.relatedTarget instanceof Node && triggerElRef.current?.contains(e.relatedTarget)))
160
+ return;
150
161
  cooldown.cool();
151
162
  clearTimer();
152
- handleClose({});
153
- }, [clearTimer, handleClose]);
163
+ if (isSubmenu) {
164
+ timerRef.current = setTimeout(() => {
165
+ handleClose({});
166
+ }, timeouts.mouseLeave);
167
+ }
168
+ else {
169
+ handleClose({});
170
+ }
171
+ }, [clearTimer, handleClose, triggerElRef, flyoutElRef, isSubmenu]);
154
172
  const handleTriggerClick = React.useCallback(() => {
155
173
  if (!isRendered) {
156
174
  handleOpen();
@@ -286,8 +304,10 @@ const FlyoutControlled = (props) => {
286
304
  resizeObserver.observe(document.body);
287
305
  if (triggerElRef.current)
288
306
  resizeObserver.observe(triggerElRef.current);
307
+ if (flyoutElRef.current)
308
+ resizeObserver.observe(flyoutElRef.current);
289
309
  return () => resizeObserver.disconnect();
290
- }, [updatePosition, triggerElRef, isRendered]);
310
+ }, [updatePosition, triggerElRef, isRendered, flyoutElRef]);
291
311
  React.useEffect(() => {
292
312
  updatePosition({ sync: true });
293
313
  }, [isRTL, updatePosition]);
@@ -305,7 +325,7 @@ const FlyoutControlled = (props) => {
305
325
  shouldReturnFocusRef.current = false;
306
326
  handleClose({ reason: "outside-click" });
307
327
  }, {
308
- disabled: Boolean(status !== "visible" || disableCloseOnOutsideClick),
328
+ disabled: !isRendered || disableCloseOnOutsideClick,
309
329
  });
310
330
  return (_jsx(Provider, { value: {
311
331
  id,
@@ -331,6 +351,8 @@ const FlyoutControlled = (props) => {
331
351
  contentClassName,
332
352
  contentAttributes,
333
353
  contentGap,
354
+ contentMaxHeight,
355
+ contentMaxWidth,
334
356
  containerRef,
335
357
  disableContentHover,
336
358
  autoFocus,
@@ -6,8 +6,8 @@ type UseFlyout = (args: Pick<T.Props, "width" | "position" | "defaultActive" | "
6
6
  container?: HTMLElement | null;
7
7
  triggerElRef: React.RefObject<HTMLElement | null>;
8
8
  flyoutElRef: React.RefObject<HTMLElement | null>;
9
- triggerBounds?: DOMRect | G.Coordinates | null;
10
- }) => Pick<T.State, "styles" | "position" | "status"> & {
9
+ triggerBoundsRef: React.RefObject<DOMRect | G.Coordinates | null>;
10
+ }) => Pick<T.State, "position" | "status"> & {
11
11
  updatePosition: (options?: {
12
12
  sync?: boolean;
13
13
  }) => void;
@@ -1,43 +1,32 @@
1
1
  import React from "react";
2
2
  import useRTL from "../../hooks/useRTL.js";
3
- import { defaultStyles, resetStyles } from "./Flyout.constants.js";
4
3
  import flyout from "./utilities/flyout.js";
5
4
  const flyoutReducer = (state, action) => {
6
5
  switch (action.type) {
7
6
  case "render":
8
- if (state.status !== "idle")
9
- return state;
10
7
  // Disable events before it's positioned to avoid mouseleave getting triggered
11
- return { ...state, status: "rendered", styles: { pointerEvents: "none", ...resetStyles } };
8
+ return { ...state, status: "rendered" };
12
9
  case "position":
13
- if (!action.payload.sync && state.status !== "rendered")
14
- return state;
15
- if (action.payload.sync && state.status !== "visible")
16
- return state;
17
10
  return {
18
11
  ...state,
19
- status: action.payload.sync ? "visible" : "positioned",
12
+ status: action.payload.sync ? state.status : "positioned",
20
13
  position: action.payload.position,
21
- styles: { ...defaultStyles, ...action.payload.styles },
22
14
  };
23
15
  case "show":
16
+ // Checking because we're positioning inside nextAnimationFrame
24
17
  if (state.status !== "positioned")
25
18
  return state;
26
19
  return { ...state, status: "visible" };
27
20
  case "hide":
28
- if (state.status !== "visible")
29
- return state;
30
21
  return { ...state, status: "hidden" };
31
22
  case "remove":
32
- if (state.status !== "hidden" && state.status !== "visible")
33
- return state;
34
- return { ...state, status: "idle", styles: resetStyles };
23
+ return { ...state, status: "idle" };
35
24
  default:
36
25
  throw new Error("[Reshaped] Invalid flyout reducer type");
37
26
  }
38
27
  };
39
28
  const useFlyout = (args) => {
40
- const { triggerElRef, flyoutElRef, triggerBounds, contentGap, contentShift, ...options } = args;
29
+ const { triggerElRef, flyoutElRef, triggerBoundsRef, contentGap, contentShift, ...options } = args;
41
30
  const { position: defaultPosition = "bottom", fallbackPositions, fallbackAdjustLayout, fallbackMinWidth, fallbackMinHeight, width, container, } = options;
42
31
  const lastUsedPositionRef = React.useRef(defaultPosition);
43
32
  // Memo the array internally to avoid new arrays triggering useCallback
@@ -47,7 +36,6 @@ const useFlyout = (args) => {
47
36
  const [isRTL] = useRTL();
48
37
  const [state, dispatch] = React.useReducer(flyoutReducer, {
49
38
  position: defaultPosition,
50
- styles: defaultStyles,
51
39
  status: "idle",
52
40
  });
53
41
  const render = React.useCallback(() => {
@@ -72,7 +60,7 @@ const useFlyout = (args) => {
72
60
  const nextFlyoutData = flyout({
73
61
  triggerEl: triggerElRef.current,
74
62
  flyoutEl: flyoutElRef.current,
75
- triggerBounds,
63
+ triggerBounds: triggerBoundsRef.current,
76
64
  width,
77
65
  position: changePositon ? defaultPosition : lastUsedPositionRef.current,
78
66
  fallbackPositions: changePositon ? cachedFallbackPositions : [],
@@ -100,7 +88,7 @@ const useFlyout = (args) => {
100
88
  isRTL,
101
89
  flyoutElRef,
102
90
  triggerElRef,
103
- triggerBounds,
91
+ triggerBoundsRef,
104
92
  width,
105
93
  contentGap,
106
94
  contentShift,
@@ -114,13 +102,12 @@ const useFlyout = (args) => {
114
102
  }, [state.status, updatePosition]);
115
103
  return React.useMemo(() => ({
116
104
  position: state.position,
117
- styles: state.styles,
118
105
  status: state.status,
119
106
  updatePosition,
120
107
  render,
121
108
  hide,
122
109
  remove,
123
110
  show,
124
- }), [render, updatePosition, hide, remove, show, state.position, state.styles, state.status]);
111
+ }), [render, updatePosition, hide, remove, show, state.position, state.status]);
125
112
  };
126
113
  export default useFlyout;
@@ -13,13 +13,13 @@ declare const calculatePosition: (args: {
13
13
  } & Pick<T.Options, "position" | "rtl" | "width" | "contentGap" | "contentShift" | "fallbackAdjustLayout" | "fallbackMinWidth" | "fallbackMinHeight">) => {
14
14
  position: T.Position;
15
15
  styles: {
16
- left: number | undefined;
17
- right: number | undefined;
18
- top: number | undefined;
19
- bottom: number | undefined;
16
+ left: string | null;
17
+ right: string | null;
18
+ top: string | null;
19
+ bottom: string | null;
20
20
  transform: string;
21
- height: number | undefined;
22
- width: string | number | undefined;
21
+ height: string | null;
22
+ width: string | null;
23
23
  };
24
24
  boundaries: {
25
25
  left: number;
@@ -1,10 +1,12 @@
1
+ import { SCREEN_OFFSET } from "./constants.js";
1
2
  import { getRTLPosition, centerBySize } from "./helpers.js";
2
- const SCREEN_OFFSET = 8;
3
3
  /**
4
4
  * Calculate styles for the current position
5
5
  */
6
6
  const calculatePosition = (args) => {
7
- const { triggerBounds, flyoutBounds, containerBounds, position: passedPosition, rtl, width: passedWidth, contentGap = 0, contentShift = 0, passedContainer, fallbackAdjustLayout, fallbackMinWidth, fallbackMinHeight, } = args;
7
+ const { triggerBounds, flyoutBounds, containerBounds, position: passedPosition, rtl, width: passedWidth, contentGap = 0, contentShift = 0, passedContainer, fallbackAdjustLayout,
8
+ // fallbackMinWidth,
9
+ fallbackMinHeight, } = args;
8
10
  const isFullWidth = passedWidth === "full" || passedWidth === "100%";
9
11
  let left = 0;
10
12
  let top = 0;
@@ -137,15 +139,20 @@ const calculatePosition = (args) => {
137
139
  if (bottom !== null)
138
140
  bottom = bottom + (flyoutHeight - height);
139
141
  }
140
- if (updatedOverflow.left > 0) {
141
- width = Math.max(fallbackMinWidth ? parseInt(fallbackMinWidth) : 0, flyoutWidth - updatedOverflow.left);
142
- left = left + (flyoutWidth - width);
143
- }
144
- else if (updatedOverflow.right > 0) {
145
- width = Math.max(fallbackMinWidth ? parseInt(fallbackMinWidth) : 0, flyoutWidth - updatedOverflow.right);
146
- if (right !== null)
147
- right = right + (flyoutWidth - width);
148
- }
142
+ // TODO: Decide if we need horizontal scrolling for the fallbacks, might be a bad practice anyways
143
+ // if (updatedOverflow.left > 0) {
144
+ // width = Math.max(
145
+ // fallbackMinWidth ? parseInt(fallbackMinWidth) : 0,
146
+ // flyoutWidth - updatedOverflow.left
147
+ // );
148
+ // left = left + (flyoutWidth - width);
149
+ // } else if (updatedOverflow.right > 0) {
150
+ // width = Math.max(
151
+ // fallbackMinWidth ? parseInt(fallbackMinWidth) : 0,
152
+ // flyoutWidth - updatedOverflow.right
153
+ // );
154
+ // if (right !== null) right = right + (flyoutWidth - width);
155
+ // }
149
156
  }
150
157
  if (isFullWidth) {
151
158
  left = SCREEN_OFFSET;
@@ -159,13 +166,13 @@ const calculatePosition = (args) => {
159
166
  return {
160
167
  position,
161
168
  styles: {
162
- left: right === null ? 0 : undefined,
163
- right: right === null ? undefined : 0,
164
- top: bottom === null ? 0 : undefined,
165
- bottom: bottom === null ? undefined : 0,
169
+ left: right === null ? "0px" : null,
170
+ right: right === null ? null : "0px",
171
+ top: bottom === null ? "0px" : null,
172
+ bottom: bottom === null ? null : "0px",
166
173
  transform: `translate(${translateX}px, ${translateY}px)`,
167
- height,
168
- width: width ?? passedWidth,
174
+ height: height !== undefined ? `${height}px` : null,
175
+ width: width !== undefined ? `${width}px` : (passedWidth ?? null),
169
176
  },
170
177
  boundaries: {
171
178
  left,
@@ -0,0 +1 @@
1
+ export declare const SCREEN_OFFSET = 8;
@@ -0,0 +1 @@
1
+ export const SCREEN_OFFSET = 8;
@@ -1,6 +1,7 @@
1
1
  import { getRectFromCoordinates, getShadowRoot, findClosestPositionContainer } from "../../../utilities/dom/index.js";
2
2
  import { resetStyles } from "../Flyout.constants.js";
3
3
  import calculatePosition from "./calculatePosition.js";
4
+ import { SCREEN_OFFSET } from "./constants.js";
4
5
  import getPositionFallbacks from "./getPositionFallbacks.js";
5
6
  import isFullyVisible from "./isFullyVisible.js";
6
7
  /**
@@ -33,16 +34,26 @@ const flyout = (args) => {
33
34
  const shadowRoot = triggerEl && getShadowRoot(triggerEl);
34
35
  // Insert inside shadow root if possible to make sure styles are applied correctly
35
36
  (shadowRoot || document.body).appendChild(targetClone);
36
- const cloneRect = targetClone.getBoundingClientRect();
37
- const flyoutBounds = { width: cloneRect.width, height: cloneRect.height };
38
37
  const closestFixedContainer = !passedContainer && triggerEl ? findClosestPositionContainer({ el: triggerEl }) : undefined;
39
38
  const container = passedContainer ||
40
39
  // Render inside fixed position container automatically to keep their position synced on scroll
41
40
  closestFixedContainer ||
42
41
  document.body;
43
42
  const renderContainerBounds = container.getBoundingClientRect();
44
- const visualContainerBounds = (passedContainer || document.body).getBoundingClientRect();
45
- const applyPosition = (position) => {
43
+ const applyPosition = (position, options) => {
44
+ const widthOption = options?.width || width;
45
+ // If there is a width override, apply it to calculate the position and the height correctly
46
+ if (widthOption === "full") {
47
+ targetClone.style.width = `calc(100% - ${SCREEN_OFFSET * 2}px)`;
48
+ }
49
+ else if (widthOption === "trigger") {
50
+ targetClone.style.width = `${resolvedTriggerBounds.width}px`;
51
+ }
52
+ else {
53
+ targetClone.style.width = widthOption || "";
54
+ }
55
+ const cloneRect = targetClone.getBoundingClientRect();
56
+ const flyoutBounds = { width: cloneRect.width, height: cloneRect.height };
46
57
  return calculatePosition({
47
58
  triggerBounds: resolvedTriggerBounds,
48
59
  flyoutBounds,
@@ -51,7 +62,7 @@ const flyout = (args) => {
51
62
  contentGap: contentGap * unitModifier,
52
63
  contentShift: contentShift * unitModifier,
53
64
  rtl,
54
- width,
65
+ width: widthOption,
55
66
  passedContainer: passedContainer ||
56
67
  (closestFixedContainer !== document.body ? closestFixedContainer : undefined),
57
68
  fallbackAdjustLayout,
@@ -60,6 +71,12 @@ const flyout = (args) => {
60
71
  });
61
72
  };
62
73
  const testVisibility = (calculated) => {
74
+ const visualContainerBounds = passedContainer?.getBoundingClientRect() ?? {
75
+ width: window.innerWidth,
76
+ height: window.innerHeight,
77
+ left: window.scrollX,
78
+ top: window.scrollY,
79
+ };
63
80
  return isFullyVisible({
64
81
  flyoutBounds: calculated.boundaries,
65
82
  visualContainerBounds,
@@ -75,10 +92,24 @@ const flyout = (args) => {
75
92
  calculated = tested;
76
93
  return visible;
77
94
  });
95
+ // Try full width positions in case it doesn't fit on any side
96
+ if (!calculated) {
97
+ const smallScreenFallbackPositions = ["top", "bottom"].filter((position) => testOrder.includes(position));
98
+ smallScreenFallbackPositions.some((position) => {
99
+ const tested = applyPosition(position, { width: "full" });
100
+ const visible = testVisibility(tested);
101
+ if (visible)
102
+ calculated = tested;
103
+ return visible;
104
+ });
105
+ }
78
106
  if (!calculated)
79
107
  calculated = applyPosition(lastUsedPosition);
80
108
  onPositionChoose(calculated.position);
81
109
  targetClone.parentNode?.removeChild(targetClone);
82
- return calculated;
110
+ Object.entries(calculated.styles).forEach(([key, value]) => {
111
+ flyoutEl.style.setProperty(key, value);
112
+ });
113
+ return { position: calculated.position };
83
114
  };
84
115
  export default flyout;
@@ -1,12 +1,13 @@
1
+ type Bounds = Pick<DOMRect, "left" | "top" | "width" | "height">;
1
2
  /**
2
3
  * Check if element visually fits within its render container
3
4
  */
4
5
  declare const isFullyVisible: (args: {
5
6
  /** Bounds of the flyout content */
6
- flyoutBounds: Pick<DOMRect, "left" | "top" | "width" | "height">;
7
+ flyoutBounds: Bounds;
7
8
  /** Bounds of the container where the flyout content should fit */
8
- visualContainerBounds: DOMRect;
9
+ visualContainerBounds: Bounds;
9
10
  /** Bounds of the container where flyout content is rendered */
10
- renderContainerBounds: DOMRect;
11
+ renderContainerBounds: Bounds;
11
12
  }) => boolean;
12
13
  export default isFullyVisible;
@@ -1,20 +1,21 @@
1
+ import { SCREEN_OFFSET } from "./constants.js";
1
2
  /**
2
3
  * Check if element visually fits within its render container
3
4
  */
4
5
  const isFullyVisible = (args) => {
5
6
  const { flyoutBounds, visualContainerBounds, renderContainerBounds } = args;
6
- if (renderContainerBounds.left + flyoutBounds.left < visualContainerBounds.left) {
7
+ if (renderContainerBounds.left + flyoutBounds.left < visualContainerBounds.left + SCREEN_OFFSET) {
7
8
  return false;
8
9
  }
9
- if (renderContainerBounds.top + flyoutBounds.top < visualContainerBounds.top) {
10
+ if (renderContainerBounds.top + flyoutBounds.top < visualContainerBounds.top + SCREEN_OFFSET) {
10
11
  return false;
11
12
  }
12
13
  if (renderContainerBounds.left + flyoutBounds.left + flyoutBounds.width >
13
- visualContainerBounds.right) {
14
+ visualContainerBounds.left + visualContainerBounds.width - SCREEN_OFFSET) {
14
15
  return false;
15
16
  }
16
17
  if (renderContainerBounds.top + flyoutBounds.top + flyoutBounds.height >
17
- visualContainerBounds.bottom) {
18
+ visualContainerBounds.top + visualContainerBounds.height - SCREEN_OFFSET) {
18
19
  return false;
19
20
  }
20
21
  return true;