reshaped 3.9.0-canary.3 → 3.9.0-canary.30
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.d.ts +2 -0
- package/dist/bundle.js +2 -2
- package/dist/components/Actionable/Actionable.module.css +1 -1
- package/dist/components/Avatar/Avatar.js +7 -24
- package/dist/components/Avatar/Avatar.module.css +1 -1
- package/dist/components/Badge/Badge.js +2 -2
- package/dist/components/Badge/Badge.module.css +1 -1
- package/dist/components/Badge/Badge.types.d.ts +1 -1
- package/dist/components/Button/Button.module.css +1 -1
- package/dist/components/Calendar/Calendar.module.css +1 -1
- package/dist/components/Calendar/Calendar.types.d.ts +4 -2
- package/dist/components/Calendar/CalendarControlled.js +2 -2
- package/dist/components/Calendar/CalendarDate.js +9 -7
- package/dist/components/Calendar/CalendarMonth.js +2 -2
- package/dist/components/Card/Card.d.ts +1 -1
- package/dist/components/Card/Card.types.d.ts +5 -5
- package/dist/components/Checkbox/Checkbox.js +2 -12
- package/dist/components/FileUpload/FileUpload.js +7 -5
- package/dist/components/FileUpload/FileUpload.module.css +1 -1
- package/dist/components/FileUpload/FileUpload.types.d.ts +2 -0
- package/dist/components/Flyout/Flyout.constants.d.ts +3 -3
- package/dist/components/Flyout/Flyout.constants.js +1 -0
- package/dist/components/Flyout/Flyout.module.css +1 -1
- package/dist/components/Flyout/Flyout.types.d.ts +6 -7
- package/dist/components/Flyout/FlyoutContent.js +3 -3
- package/dist/components/Flyout/FlyoutControlled.js +31 -10
- package/dist/components/Flyout/useFlyout.d.ts +2 -2
- package/dist/components/Flyout/useFlyout.js +8 -21
- package/dist/components/Flyout/utilities/calculatePosition.d.ts +6 -6
- package/dist/components/Flyout/utilities/calculatePosition.js +24 -17
- 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 +37 -6
- package/dist/components/Flyout/utilities/isFullyVisible.d.ts +4 -3
- package/dist/components/Flyout/utilities/isFullyVisible.js +5 -4
- package/dist/components/Grid/Grid.types.d.ts +4 -4
- package/dist/components/HiddenInput/HiddenInput.js +33 -0
- package/dist/components/HiddenInput/HiddenInput.types.d.ts +26 -0
- package/dist/components/Image/Image.js +13 -8
- package/dist/components/Image/Image.module.css +1 -1
- package/dist/components/Image/Image.types.d.ts +3 -1
- package/dist/components/Modal/Modal.js +2 -5
- package/dist/components/Modal/Modal.module.css +1 -1
- package/dist/components/Popover/Popover.module.css +1 -1
- package/dist/components/Radio/Radio.js +2 -12
- package/dist/components/Reshaped/Reshaped.css +1 -1
- package/dist/components/Scrim/Scrim.js +4 -3
- package/dist/components/Scrim/Scrim.module.css +1 -1
- package/dist/components/Scrim/Scrim.types.d.ts +2 -1
- package/dist/components/ScrollArea/ScrollArea.js +7 -7
- package/dist/components/Slider/SliderControlled.js +5 -4
- package/dist/components/Tabs/Tabs.module.css +1 -1
- package/dist/components/Tabs/Tabs.types.d.ts +3 -1
- package/dist/components/Tabs/TabsContext.d.ts +1 -0
- package/dist/components/Tabs/TabsControlled.js +2 -1
- package/dist/components/Tabs/TabsItem.js +3 -3
- package/dist/components/Tabs/TabsList.js +9 -5
- package/dist/components/Tabs/TabsPanel.js +1 -1
- package/dist/components/Text/Text.d.ts +1 -1
- package/dist/components/Text/Text.js +2 -2
- package/dist/components/Text/Text.module.css +1 -1
- package/dist/components/Text/Text.types.d.ts +5 -3
- 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/dist/components/View/View.types.d.ts +4 -4
- package/dist/hooks/_private/useDrag.js +0 -3
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -0
- package/dist/types/global.d.ts +1 -1
- package/dist/utilities/dom/index.d.ts +0 -1
- package/dist/utilities/dom/index.js +0 -1
- package/dist/utilities/scroll/disable.js +4 -2
- package/package.json +4 -98
- package/README.md +0 -24
- package/dist/components/_private/HiddenInput/HiddenInput.js +0 -10
- package/dist/components/_private/HiddenInput/HiddenInput.types.d.ts +0 -15
- package/dist/utilities/dom/userSelect.d.ts +0 -2
- package/dist/utilities/dom/userSelect.js +0 -6
- /package/dist/components/{_private/HiddenInput → HiddenInput}/HiddenInput.d.ts +0 -0
- /package/dist/components/{_private/HiddenInput → HiddenInput}/HiddenInput.module.css +0 -0
- /package/dist/components/{_private/HiddenInput → HiddenInput}/HiddenInput.types.js +0 -0
- /package/dist/components/{_private/HiddenInput → HiddenInput}/index.d.ts +0 -0
- /package/dist/components/{_private/HiddenInput → HiddenInput}/index.js +0 -0
|
@@ -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,
|
|
@@ -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"
|
|
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
|
-
|
|
153
|
-
|
|
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]);
|
|
@@ -332,6 +352,7 @@ const FlyoutControlled = (props) => {
|
|
|
332
352
|
contentAttributes,
|
|
333
353
|
contentGap,
|
|
334
354
|
contentMaxHeight,
|
|
355
|
+
contentMaxWidth,
|
|
335
356
|
containerRef,
|
|
336
357
|
disableContentHover,
|
|
337
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
|
-
|
|
10
|
-
}) => Pick<T.State, "
|
|
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"
|
|
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 ?
|
|
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
|
-
|
|
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,
|
|
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
|
-
|
|
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.
|
|
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:
|
|
17
|
-
right:
|
|
18
|
-
top:
|
|
19
|
-
bottom:
|
|
16
|
+
left: string | null;
|
|
17
|
+
right: string | null;
|
|
18
|
+
top: string | null;
|
|
19
|
+
bottom: string | null;
|
|
20
20
|
transform: string;
|
|
21
|
-
height:
|
|
22
|
-
width: string |
|
|
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,
|
|
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
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
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 ?
|
|
163
|
-
right: right === null ?
|
|
164
|
-
top: bottom === null ?
|
|
165
|
-
bottom: bottom === null ?
|
|
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 ??
|
|
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
|
|
45
|
-
|
|
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
|
-
|
|
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:
|
|
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;
|
|
@@ -2,7 +2,7 @@ import type { Property } from "csstype";
|
|
|
2
2
|
import type React from "react";
|
|
3
3
|
import type * as TStyles from "../../styles/types";
|
|
4
4
|
import type * as G from "../../types/global";
|
|
5
|
-
export type Props<TagName extends keyof React.JSX.IntrinsicElements =
|
|
5
|
+
export type Props<TagName extends keyof React.JSX.IntrinsicElements | void = void> = {
|
|
6
6
|
/** Gap between grid items */
|
|
7
7
|
gap?: G.Responsive<number>;
|
|
8
8
|
/** Horizontal gap between grid items */
|
|
@@ -34,13 +34,13 @@ export type Props<TagName extends keyof React.JSX.IntrinsicElements = "div"> = {
|
|
|
34
34
|
/** Node for inserting children */
|
|
35
35
|
children?: React.ReactNode;
|
|
36
36
|
/** Custom root element html tag */
|
|
37
|
-
as?: TagName;
|
|
37
|
+
as?: TagName extends keyof React.JSX.IntrinsicElements ? TagName : keyof React.JSX.IntrinsicElements;
|
|
38
38
|
/** Additional classname for the root element */
|
|
39
39
|
className?: G.ClassName;
|
|
40
40
|
/** Additional attributes for the root element */
|
|
41
41
|
attributes?: G.Attributes<TagName>;
|
|
42
42
|
};
|
|
43
|
-
export type ItemProps<TagName extends keyof React.JSX.IntrinsicElements =
|
|
43
|
+
export type ItemProps<TagName extends keyof React.JSX.IntrinsicElements | void = void> = {
|
|
44
44
|
/** Grid area for template syntax */
|
|
45
45
|
area?: string;
|
|
46
46
|
/** Starting column position */
|
|
@@ -58,7 +58,7 @@ export type ItemProps<TagName extends keyof React.JSX.IntrinsicElements = "div">
|
|
|
58
58
|
/** Node for inserting children */
|
|
59
59
|
children?: React.ReactNode;
|
|
60
60
|
/** Custom item element html tag */
|
|
61
|
-
as?: TagName;
|
|
61
|
+
as?: TagName extends keyof React.JSX.IntrinsicElements ? TagName : keyof React.JSX.IntrinsicElements;
|
|
62
62
|
/** Additional classname for the item element */
|
|
63
63
|
className?: G.ClassName;
|
|
64
64
|
/** Additional attributes for the item element */
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useCheckboxGroup } from "../CheckboxGroup/index.js";
|
|
3
|
+
import { useFormControl } from "../FormControl/index.js";
|
|
4
|
+
import { useRadioGroup } from "../RadioGroup/index.js";
|
|
5
|
+
import { classNames } from "../../utilities/props.js";
|
|
6
|
+
import s from "./HiddenInput.module.css";
|
|
7
|
+
const HiddenInput = (props) => {
|
|
8
|
+
const { type, value, className, onBlur, onFocus, onChange, attributes } = props;
|
|
9
|
+
const rootClassNames = classNames(s.root, className);
|
|
10
|
+
const checkboxGroup = useCheckboxGroup();
|
|
11
|
+
const radioGroup = useRadioGroup();
|
|
12
|
+
const formControl = useFormControl();
|
|
13
|
+
const name = checkboxGroup?.name ?? radioGroup?.name ?? props.name;
|
|
14
|
+
const disabled = formControl?.disabled ?? props.disabled ?? checkboxGroup?.disabled ?? radioGroup?.disabled;
|
|
15
|
+
const checked = (value && (checkboxGroup?.value?.includes(value) ?? radioGroup?.value === value)) ||
|
|
16
|
+
props.checked;
|
|
17
|
+
const defaultChecked = checkboxGroup ? undefined : props.defaultChecked;
|
|
18
|
+
const handleChange = (event) => {
|
|
19
|
+
if (!name)
|
|
20
|
+
return;
|
|
21
|
+
const { checked } = event.target;
|
|
22
|
+
const changeArgs = { name, value, checked, event };
|
|
23
|
+
if (onChange)
|
|
24
|
+
onChange(changeArgs);
|
|
25
|
+
if (checkboxGroup?.onChange)
|
|
26
|
+
checkboxGroup.onChange(changeArgs);
|
|
27
|
+
if (radioGroup?.onChange)
|
|
28
|
+
radioGroup.onChange(changeArgs);
|
|
29
|
+
};
|
|
30
|
+
return (_jsx("input", { ...attributes, className: rootClassNames, type: type, name: name, value: value, checked: checked, defaultChecked: defaultChecked, disabled: disabled, onChange: handleChange, onFocus: onFocus || attributes?.onFocus, onBlur: onBlur || attributes?.onBlur, "data-rs-hidden-input": true }));
|
|
31
|
+
};
|
|
32
|
+
HiddenInput.displayName = "HiddenInput";
|
|
33
|
+
export default HiddenInput;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import type * as G from "../../types/global";
|
|
3
|
+
export type Props = {
|
|
4
|
+
/** Name of the input element */
|
|
5
|
+
name?: string;
|
|
6
|
+
/** Value of the input element that is used for form submission */
|
|
7
|
+
value?: string;
|
|
8
|
+
/** Checked state of the input element, enables controlled mode */
|
|
9
|
+
checked?: boolean;
|
|
10
|
+
/** Default checked state of the input element, enables uncontrolled mode */
|
|
11
|
+
defaultChecked?: boolean;
|
|
12
|
+
/** Disable the input element */
|
|
13
|
+
disabled?: boolean;
|
|
14
|
+
/** Callback when the input value changes */
|
|
15
|
+
onChange?: G.ChangeHandler<boolean>;
|
|
16
|
+
/** Callback when the input or label is focused */
|
|
17
|
+
onFocus?: (e: React.FocusEvent) => void;
|
|
18
|
+
/** Callback when the input or label is blurred */
|
|
19
|
+
onBlur?: (e: React.FocusEvent) => void;
|
|
20
|
+
/** Type of the input element */
|
|
21
|
+
type: "checkbox" | "radio";
|
|
22
|
+
/** Additional classname for the root element */
|
|
23
|
+
className?: G.ClassName;
|
|
24
|
+
/** Additional attributes for the input element */
|
|
25
|
+
attributes?: G.Attributes<"input">;
|
|
26
|
+
};
|
|
@@ -5,12 +5,14 @@ import { resolveMixin } from "../../styles/mixin.js";
|
|
|
5
5
|
import { classNames } from "../../utilities/props.js";
|
|
6
6
|
import s from "./Image.module.css";
|
|
7
7
|
const Image = (props) => {
|
|
8
|
-
const { src, alt, width, maxWidth, height, aspectRatio, onLoad, onError, fallback, displayMode = "cover", borderRadius, className, attributes, imageAttributes: passedImageAttributes, renderImage, } = props;
|
|
8
|
+
const { src, alt, width, maxWidth, height, aspectRatio, onLoad, onError, fallback, outline, displayMode = "cover", borderRadius, className, attributes, imageAttributes: passedImageAttributes, renderImage, } = props;
|
|
9
9
|
const [status, setStatus] = React.useState("loading");
|
|
10
10
|
const mixinStyles = resolveMixin({ radius: borderRadius, width, height, maxWidth, aspectRatio });
|
|
11
|
-
const
|
|
12
|
-
const
|
|
13
|
-
|
|
11
|
+
const rootClassNames = classNames(s.root, mixinStyles.classNames, outline && s["--outline"], className);
|
|
12
|
+
const imageClassNames = classNames([
|
|
13
|
+
s.image,
|
|
14
|
+
displayMode && s[`image--display-mode-${displayMode}`],
|
|
15
|
+
]);
|
|
14
16
|
const isFallback = (status === "error" || !src) && !!fallback;
|
|
15
17
|
const style = {
|
|
16
18
|
...attributes?.style,
|
|
@@ -19,10 +21,12 @@ const Image = (props) => {
|
|
|
19
21
|
const handleLoad = (e) => {
|
|
20
22
|
setStatus("success");
|
|
21
23
|
onLoad?.(e);
|
|
24
|
+
passedImageAttributes?.onLoad?.(e);
|
|
22
25
|
};
|
|
23
26
|
const handleError = (e) => {
|
|
24
27
|
setStatus("error");
|
|
25
28
|
onError?.(e);
|
|
29
|
+
passedImageAttributes?.onError?.(e);
|
|
26
30
|
};
|
|
27
31
|
React.useEffect(() => {
|
|
28
32
|
setStatus("loading");
|
|
@@ -34,13 +38,13 @@ const Image = (props) => {
|
|
|
34
38
|
src: fallback ?? "",
|
|
35
39
|
alt: alt ?? "",
|
|
36
40
|
role: alt ? undefined : "presentation",
|
|
37
|
-
className:
|
|
41
|
+
className: rootClassNames,
|
|
38
42
|
style,
|
|
39
43
|
};
|
|
40
44
|
// eslint-disable-next-line jsx-a11y/alt-text
|
|
41
45
|
return renderImage ? renderImage(imageAttributes) : _jsx("img", { ...imageAttributes });
|
|
42
46
|
}
|
|
43
|
-
return (_jsx("div", { ...attributes, className:
|
|
47
|
+
return (_jsx("div", { ...attributes, className: classNames([s.fallback, rootClassNames]), style: style, children: fallback }));
|
|
44
48
|
}
|
|
45
49
|
const imageAttributes = {
|
|
46
50
|
...attributes,
|
|
@@ -50,11 +54,12 @@ const Image = (props) => {
|
|
|
50
54
|
role: alt ? undefined : "presentation",
|
|
51
55
|
onLoad: handleLoad,
|
|
52
56
|
onError: handleError,
|
|
53
|
-
className:
|
|
57
|
+
className: outline ? imageClassNames : classNames([imageClassNames, rootClassNames]),
|
|
54
58
|
style,
|
|
55
59
|
};
|
|
56
60
|
// eslint-disable-next-line jsx-a11y/alt-text
|
|
57
|
-
|
|
61
|
+
const imageNode = renderImage ? renderImage(imageAttributes) : _jsx("img", { ...imageAttributes });
|
|
62
|
+
return outline ? (_jsx("div", { ...attributes, className: rootClassNames, style: style, children: imageNode })) : (imageNode);
|
|
58
63
|
};
|
|
59
64
|
Image.displayName = "Image";
|
|
60
65
|
export default Image;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
.root{display:block;max-width:100%}.fallback{align-items:center;background:var(--rs-color-background-neutral-faded);color:var(--rs-color-foreground-disabled);display:flex;justify-content:center}.--display-mode-cover{object-fit:cover!important}
|
|
1
|
+
.root{position:relative}.root .image{border-radius:inherit;height:100%;width:100%}.image{display:block;max-width:100%}.fallback{align-items:center;background:var(--rs-color-background-neutral-faded);color:var(--rs-color-foreground-disabled);display:flex;justify-content:center}.--outline:after{border:1px solid var(--rs-color-border-neutral-faded);border-radius:inherit;content:"";inset:0;pointer-events:none;position:absolute}.image--display-mode-cover{object-fit:cover!important}.image--display-mode-contain{object-fit:scale-down!important}
|
|
@@ -15,9 +15,11 @@ export type Props = {
|
|
|
15
15
|
/** Image aspect ratio, width / height */
|
|
16
16
|
aspectRatio?: G.Responsive<number>;
|
|
17
17
|
/** Image border radius, based on the radius tokens */
|
|
18
|
-
borderRadius?: Extract<TStyles.Radius, "small" | "medium" | "large">;
|
|
18
|
+
borderRadius?: Extract<TStyles.Radius, "small" | "medium" | "large" | "circular">;
|
|
19
19
|
/** Image display mode for controlling how it fits into the provided boundaries */
|
|
20
20
|
displayMode?: "cover" | "contain";
|
|
21
|
+
/** Add a semi-transparent border on top of the image for better background contrast */
|
|
22
|
+
outline?: boolean;
|
|
21
23
|
/** Image on load event */
|
|
22
24
|
onLoad?: (e: React.SyntheticEvent) => void;
|
|
23
25
|
/** Image on error event */
|
|
@@ -7,8 +7,7 @@ import useElementId from "../../hooks/useElementId.js";
|
|
|
7
7
|
import useHandlerRef from "../../hooks/useHandlerRef.js";
|
|
8
8
|
import useResponsiveClientValue from "../../hooks/useResponsiveClientValue.js";
|
|
9
9
|
import { resolveMixin } from "../../styles/mixin.js";
|
|
10
|
-
import {
|
|
11
|
-
import { classNames, responsiveVariables, responsiveClassNames, responsivePropDependency, } from "../../utilities/props.js";
|
|
10
|
+
import { classNames, responsiveVariables, responsiveClassNames } from "../../utilities/props.js";
|
|
12
11
|
import { enableScroll, disableScroll } from "../../utilities/scroll/index.js";
|
|
13
12
|
import s from "./Modal.module.css";
|
|
14
13
|
const DRAG_THRESHOLD = 32;
|
|
@@ -90,7 +89,6 @@ const Modal = (props) => {
|
|
|
90
89
|
// Prevent the drag handling when browser is trying to navigate to a previous page
|
|
91
90
|
if (clientPosition === "start" && e.targetTouches[0].clientX < DRAG_EDGE_BOUNDARY)
|
|
92
91
|
return;
|
|
93
|
-
disableUserSelect();
|
|
94
92
|
disableScroll();
|
|
95
93
|
setDragging(true);
|
|
96
94
|
};
|
|
@@ -108,7 +106,6 @@ const Modal = (props) => {
|
|
|
108
106
|
if (!dragging)
|
|
109
107
|
return;
|
|
110
108
|
const handleDragEnd = () => {
|
|
111
|
-
enableUserSelect();
|
|
112
109
|
enableScroll();
|
|
113
110
|
setDragging(false);
|
|
114
111
|
// Close only when dragging in the closing direction
|
|
@@ -168,7 +165,7 @@ const Modal = (props) => {
|
|
|
168
165
|
setHideProgress(progress / 2);
|
|
169
166
|
dragDistanceRef.current = dragDistance;
|
|
170
167
|
}, [dragDistance, clientPosition, rootRef]);
|
|
171
|
-
return (_jsx(Overlay, { onClose: onClose, onOpen: onOpen, onAfterClose: onAfterClose, onAfterOpen: onAfterOpen, disableCloseOnClick: disableCloseOnOutsideClick, active: active, transparent: transparentOverlay || hideProgress, blurred: blurredOverlay, overflow:
|
|
168
|
+
return (_jsx(Overlay, { onClose: onClose, onOpen: onOpen, onAfterClose: onAfterClose, onAfterOpen: onAfterOpen, disableCloseOnClick: disableCloseOnOutsideClick, active: active, transparent: transparentOverlay || hideProgress, blurred: blurredOverlay, overflow: clientPosition === "center" ? "auto" : "hidden", className: overlayClassName, containerRef: containerRef, attributes: {
|
|
172
169
|
onTouchStart: handleDragStart,
|
|
173
170
|
}, children: ({ active }) => {
|
|
174
171
|
const rootClassNames = classNames(s.root, className, active && s["--active"], dragging && s["--dragging"], overflow && s[`--overflow-${overflow}`], containerRef && s["--contained"], responsiveClassNames(s, "--position", position), mixinStyles.classNames);
|