reshaped 3.9.0-canary.13 → 3.9.0-canary.15

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.
@@ -1 +1 @@
1
- .content{--rs-flyout-gap:0;--rs-flyout-max-h: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: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)}
@@ -131,6 +131,8 @@ type BaseProps = {
131
131
  contentShift?: number;
132
132
  /** Maximum height for the content */
133
133
  contentMaxHeight?: string;
134
+ /** Maximum width for the content */
135
+ contentMaxWidth?: string;
134
136
  /** Additional classname for the content element */
135
137
  contentClassName?: string;
136
138
  /** Additional attributes for the content element */
@@ -188,7 +190,7 @@ export type ContextProps = {
188
190
  handleContentMouseDown: () => void;
189
191
  handleContentMouseUp: () => void;
190
192
  isSubmenu: boolean;
191
- } & Pick<Props, "triggerType" | "contentClassName" | "contentAttributes" | "contentGap" | "contentMaxHeight" | "trapFocusMode" | "containerRef" | "disableContentHover" | "autoFocus">;
193
+ } & Pick<Props, "triggerType" | "contentClassName" | "contentAttributes" | "contentGap" | "contentMaxHeight" | "contentMaxWidth" | "trapFocusMode" | "containerRef" | "disableContentHover" | "autoFocus">;
192
194
  export type TriggerContextProps = {
193
195
  elRef?: ContextProps["triggerElRef"];
194
196
  };
@@ -11,7 +11,7 @@ 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, contentMaxHeight, trapFocusMode, disableContentHover, autoFocus, width, containerRef: passedContainerRef, isSubmenu, } = useFlyoutContext();
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
15
  const { status, position } = flyout;
16
16
  const [mounted, setMounted] = React.useState(false);
17
17
  const closestFixedContainer = React.useMemo(() => {
@@ -97,6 +97,7 @@ const FlyoutContent = (props) => {
97
97
  const content = (_jsx(ContentProvider, { value: { elRef: flyoutElRef }, children: _jsx("div", { className: rootClassNames, style: {
98
98
  "--rs-flyout-gap": contentGap,
99
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, contentMaxHeight, 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,
@@ -147,9 +148,11 @@ const FlyoutControlled = (props) => {
147
148
  }
148
149
  }, [clearTimer, handleOpen, groupTimeouts]);
149
150
  const handleMouseLeave = React.useCallback((e) => {
150
- if (e.relatedTarget === flyoutElRef.current)
151
+ if (e.relatedTarget === flyoutElRef.current ||
152
+ (e.relatedTarget instanceof Node && flyoutElRef.current?.contains(e.relatedTarget)))
151
153
  return;
152
- if (e.relatedTarget === triggerElRef.current)
154
+ if (e.relatedTarget === triggerElRef.current ||
155
+ (e.relatedTarget instanceof Node && triggerElRef.current?.contains(e.relatedTarget)))
153
156
  return;
154
157
  cooldown.cool();
155
158
  clearTimer();
@@ -338,6 +341,7 @@ const FlyoutControlled = (props) => {
338
341
  contentAttributes,
339
342
  contentGap,
340
343
  contentMaxHeight,
344
+ contentMaxWidth,
341
345
  containerRef,
342
346
  disableContentHover,
343
347
  autoFocus,
@@ -6,7 +6,7 @@ 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;
9
+ triggerBoundsRef: React.RefObject<DOMRect | G.Coordinates | null>;
10
10
  }) => Pick<T.State, "position" | "status"> & {
11
11
  updatePosition: (options?: {
12
12
  sync?: boolean;
@@ -26,7 +26,7 @@ const flyoutReducer = (state, action) => {
26
26
  }
27
27
  };
28
28
  const useFlyout = (args) => {
29
- const { triggerElRef, flyoutElRef, triggerBounds, contentGap, contentShift, ...options } = args;
29
+ const { triggerElRef, flyoutElRef, triggerBoundsRef, contentGap, contentShift, ...options } = args;
30
30
  const { position: defaultPosition = "bottom", fallbackPositions, fallbackAdjustLayout, fallbackMinWidth, fallbackMinHeight, width, container, } = options;
31
31
  const lastUsedPositionRef = React.useRef(defaultPosition);
32
32
  // Memo the array internally to avoid new arrays triggering useCallback
@@ -60,7 +60,7 @@ const useFlyout = (args) => {
60
60
  const nextFlyoutData = flyout({
61
61
  triggerEl: triggerElRef.current,
62
62
  flyoutEl: flyoutElRef.current,
63
- triggerBounds,
63
+ triggerBounds: triggerBoundsRef.current,
64
64
  width,
65
65
  position: changePositon ? defaultPosition : lastUsedPositionRef.current,
66
66
  fallbackPositions: changePositon ? cachedFallbackPositions : [],
@@ -88,7 +88,7 @@ const useFlyout = (args) => {
88
88
  isRTL,
89
89
  flyoutElRef,
90
90
  triggerElRef,
91
- triggerBounds,
91
+ triggerBoundsRef,
92
92
  width,
93
93
  contentGap,
94
94
  contentShift,
@@ -1,5 +1,5 @@
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
  */
@@ -0,0 +1 @@
1
+ export declare const SCREEN_OFFSET = 8;
@@ -0,0 +1 @@
1
+ export const SCREEN_OFFSET = 8;
@@ -41,8 +41,7 @@ const flyout = (args) => {
41
41
  closestFixedContainer ||
42
42
  document.body;
43
43
  const renderContainerBounds = container.getBoundingClientRect();
44
- const visualContainerBounds = (passedContainer || document.body).getBoundingClientRect();
45
- const applyPosition = (position) => {
44
+ const applyPosition = (position, options) => {
46
45
  return calculatePosition({
47
46
  triggerBounds: resolvedTriggerBounds,
48
47
  flyoutBounds,
@@ -51,7 +50,7 @@ const flyout = (args) => {
51
50
  contentGap: contentGap * unitModifier,
52
51
  contentShift: contentShift * unitModifier,
53
52
  rtl,
54
- width,
53
+ width: options?.width || width,
55
54
  passedContainer: passedContainer ||
56
55
  (closestFixedContainer !== document.body ? closestFixedContainer : undefined),
57
56
  fallbackAdjustLayout,
@@ -60,6 +59,12 @@ const flyout = (args) => {
60
59
  });
61
60
  };
62
61
  const testVisibility = (calculated) => {
62
+ const visualContainerBounds = passedContainer?.getBoundingClientRect() ?? {
63
+ width: window.innerWidth,
64
+ height: window.innerHeight,
65
+ left: window.scrollX,
66
+ top: window.scrollY,
67
+ };
63
68
  return isFullyVisible({
64
69
  flyoutBounds: calculated.boundaries,
65
70
  visualContainerBounds,
@@ -75,6 +80,17 @@ const flyout = (args) => {
75
80
  calculated = tested;
76
81
  return visible;
77
82
  });
83
+ // Try full width positions in case it doesn't fit on any side
84
+ if (!calculated) {
85
+ const smallScreenFallbackPositions = ["top", "bottom"].filter((position) => testOrder.includes(position));
86
+ smallScreenFallbackPositions.some((position) => {
87
+ const tested = applyPosition(position, { width: "full" });
88
+ const visible = testVisibility(tested);
89
+ if (visible)
90
+ calculated = tested;
91
+ return visible;
92
+ });
93
+ }
78
94
  if (!calculated)
79
95
  calculated = applyPosition(lastUsedPosition);
80
96
  onPositionChoose(calculated.position);
@@ -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;
@@ -5,12 +5,12 @@ import Text from "../Text/index.js";
5
5
  import Theme from "../Theme/index.js";
6
6
  import s from "./Tooltip.module.css";
7
7
  const Tooltip = (props) => {
8
- const { text, children, position = "bottom", color = "inverted", ...flyoutProps } = props;
8
+ const { text, children, position = "bottom", color = "inverted", contentMaxWidth = "360px", ...flyoutProps } = props;
9
9
  if (!text)
10
10
  return children({ ref: null });
11
11
  return (_jsxs(Flyout, { ...flyoutProps, position: position, triggerType: "hover",
12
12
  // Disable group timeouts by default since it's not controlled by the default user events
13
- groupTimeouts: flyoutProps.active === undefined ? true : false, children: [_jsx(Flyout.Trigger, { children: children }), _jsx(Flyout.Content, { children: _jsx(Theme, { colorMode: color, children: _jsx(Text, { variant: "caption-1", className: s.root, children: text }) }) })] }));
13
+ groupTimeouts: flyoutProps.active === undefined ? true : false, contentMaxWidth: contentMaxWidth, children: [_jsx(Flyout.Trigger, { children: children }), _jsx(Flyout.Content, { children: _jsx(Theme, { colorMode: color, children: _jsx(Text, { variant: "caption-1", className: s.root, children: text }) }) })] }));
14
14
  };
15
15
  Tooltip.displayName = "Tooltip";
16
16
  export default Tooltip;
@@ -1 +1 @@
1
- .root{background:var(--rs-color-background-elevation-overlay);border-radius:var(--rs-radius-small);box-shadow:var(--rs-shadow-overlay);color:var(--rs-color-foreground-neutral);max-width:360px;padding:var(--rs-unit-x1) var(--rs-unit-x2)}
1
+ .root{background:var(--rs-color-background-elevation-overlay);border-radius:var(--rs-radius-small);box-shadow:var(--rs-shadow-overlay);color:var(--rs-color-foreground-neutral);padding:var(--rs-unit-x1) var(--rs-unit-x2)}
@@ -1,6 +1,6 @@
1
1
  import React from "react";
2
2
  import type { FlyoutProps, FlyoutTriggerAttributes } from "../Flyout";
3
- export type Props = Pick<FlyoutProps, "id" | "position" | "onOpen" | "onClose" | "active" | "disabled" | "disableContentHover" | "containerRef" | "positionRef" | "contentGap" | "contentShift" | "originCoordinates" | "contentAttributes" | "contentClassName" | "instanceRef"> & {
3
+ export type Props = Pick<FlyoutProps, "id" | "position" | "onOpen" | "onClose" | "active" | "disabled" | "disableContentHover" | "containerRef" | "positionRef" | "contentGap" | "contentShift" | "contentMaxWidth" | "originCoordinates" | "contentAttributes" | "contentClassName" | "instanceRef"> & {
4
4
  /** Node for inserting children */
5
5
  children: (attributes: FlyoutTriggerAttributes) => React.ReactNode;
6
6
  /** Text content for the tooltip */
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "reshaped",
3
3
  "description": "Professionally crafted design system in React & Figma for building products of any scale and complexity",
4
- "version": "3.9.0-canary.13",
4
+ "version": "3.9.0-canary.15",
5
5
  "license": "MIT",
6
6
  "email": "hello@reshaped.so",
7
7
  "homepage": "https://reshaped.so",