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.
- package/dist/bundle.css +1 -1
- package/dist/bundle.js +2 -2
- package/dist/components/Flyout/Flyout.module.css +1 -1
- package/dist/components/Flyout/Flyout.types.d.ts +3 -1
- package/dist/components/Flyout/FlyoutContent.js +2 -1
- package/dist/components/Flyout/FlyoutControlled.js +9 -5
- package/dist/components/Flyout/useFlyout.d.ts +1 -1
- package/dist/components/Flyout/useFlyout.js +3 -3
- package/dist/components/Flyout/utilities/calculatePosition.js +1 -1
- package/dist/components/Flyout/utilities/constants.d.ts +1 -0
- package/dist/components/Flyout/utilities/constants.js +1 -0
- package/dist/components/Flyout/utilities/flyout.js +19 -3
- package/dist/components/Flyout/utilities/isFullyVisible.d.ts +4 -3
- package/dist/components/Flyout/utilities/isFullyVisible.js +5 -4
- package/dist/components/Tooltip/Tooltip.js +2 -2
- package/dist/components/Tooltip/Tooltip.module.css +1 -1
- package/dist/components/Tooltip/Tooltip.types.d.ts +1 -1
- package/package.json +1 -1
|
@@ -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:
|
|
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
|
-
|
|
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
|
-
|
|
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,
|
|
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
|
-
|
|
91
|
+
triggerBoundsRef,
|
|
92
92
|
width,
|
|
93
93
|
contentGap,
|
|
94
94
|
contentShift,
|
|
@@ -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
|
|
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:
|
|
7
|
+
flyoutBounds: Bounds;
|
|
7
8
|
/** Bounds of the container where the flyout content should fit */
|
|
8
|
-
visualContainerBounds:
|
|
9
|
+
visualContainerBounds: Bounds;
|
|
9
10
|
/** Bounds of the container where flyout content is rendered */
|
|
10
|
-
renderContainerBounds:
|
|
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.
|
|
14
|
+
visualContainerBounds.left + visualContainerBounds.width - SCREEN_OFFSET) {
|
|
14
15
|
return false;
|
|
15
16
|
}
|
|
16
17
|
if (renderContainerBounds.top + flyoutBounds.top + flyoutBounds.height >
|
|
17
|
-
visualContainerBounds.
|
|
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);
|
|
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.
|
|
4
|
+
"version": "3.9.0-canary.15",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"email": "hello@reshaped.so",
|
|
7
7
|
"homepage": "https://reshaped.so",
|