reshaped 3.7.4 → 3.8.0-canary.1
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/CHANGELOG-extra.md +3 -0
- package/CHANGELOG.md +68 -55
- package/LICENSE.md +1 -1
- package/README.md +24 -0
- package/dist/bundle.css +1 -1
- package/dist/bundle.js +11 -11
- package/dist/cjs/themes/index.d.ts +1 -0
- package/dist/cjs/themes/index.js +3 -1
- package/dist/components/Accordion/Accordion.types.d.ts +13 -2
- package/dist/components/ActionBar/ActionBar.types.d.ts +10 -0
- package/dist/components/Actionable/Actionable.types.d.ts +14 -0
- package/dist/components/Alert/Alert.types.d.ts +11 -0
- package/dist/components/Autocomplete/Autocomplete.types.d.ts +7 -0
- package/dist/components/Avatar/Avatar.types.d.ts +18 -0
- package/dist/components/Avatar/tests/Avatar.stories.js +1 -1
- package/dist/components/Badge/Badge.types.d.ts +25 -1
- package/dist/components/Badge/tests/Badge.test.stories.js +3 -1
- package/dist/components/Breadcrumbs/Breadcrumbs.types.d.ts +11 -0
- package/dist/components/Button/Button.types.d.ts +20 -0
- package/dist/components/Calendar/Calendar.types.d.ts +31 -0
- package/dist/components/Calendar/tests/Calendar.stories.js +1 -1
- package/dist/components/Card/Card.types.d.ts +12 -0
- package/dist/components/Carousel/Carousel.types.d.ts +13 -0
- package/dist/components/Checkbox/Checkbox.types.d.ts +19 -0
- package/dist/components/Checkbox/tests/Checkbox.stories.js +1 -1
- package/dist/components/CheckboxGroup/CheckboxGroup.types.d.ts +10 -0
- package/dist/components/Container/Container.types.d.ts +11 -1
- package/dist/components/Dismissible/Dismissible.types.d.ts +10 -0
- package/dist/components/Divider/Divider.types.d.ts +6 -0
- package/dist/components/DropdownMenu/DropdownMenu.types.d.ts +4 -1
- package/dist/components/FileUpload/FileUpload.types.d.ts +10 -0
- package/dist/components/Flyout/Flyout.types.d.ts +41 -0
- package/dist/components/Flyout/FlyoutContent.js +1 -1
- package/dist/components/Flyout/FlyoutControlled.js +2 -1
- package/dist/components/Flyout/tests/Flyout.stories.d.ts +4 -0
- package/dist/components/Flyout/tests/Flyout.stories.js +59 -1
- package/dist/components/Flyout/useFlyout.d.ts +1 -0
- package/dist/components/Flyout/useFlyout.js +3 -1
- package/dist/components/Flyout/utilities/calculatePosition.d.ts +1 -1
- package/dist/components/Flyout/utilities/calculatePosition.js +52 -28
- package/dist/components/Flyout/utilities/flyout.js +22 -18
- package/dist/components/Flyout/utilities/helpers.d.ts +7 -0
- package/dist/components/Flyout/utilities/helpers.js +14 -0
- package/dist/components/Flyout/utilities/isFullyVisible.d.ts +5 -1
- package/dist/components/Flyout/utilities/isFullyVisible.js +1 -1
- package/dist/components/FormControl/FormControl.types.d.ts +9 -0
- package/dist/components/Grid/Grid.js +3 -1
- package/dist/components/Grid/Grid.module.css +1 -1
- package/dist/components/Grid/Grid.types.d.ts +31 -0
- package/dist/components/Grid/tests/Grid.stories.d.ts +3 -0
- package/dist/components/Grid/tests/Grid.stories.js +39 -1
- package/dist/components/Hidden/Hidden.types.d.ts +4 -0
- package/dist/components/HiddenVisually/HiddenVisually.types.d.ts +1 -0
- package/dist/components/Hotkey/Hotkey.types.d.ts +4 -0
- package/dist/components/Icon/Icon.types.d.ts +6 -0
- package/dist/components/Image/Image.types.d.ts +15 -0
- package/dist/components/Link/Link.types.d.ts +7 -0
- package/dist/components/Loader/Loader.types.d.ts +5 -0
- package/dist/components/MenuItem/MenuItem.types.d.ts +13 -1
- package/dist/components/Modal/Modal.types.d.ts +19 -0
- package/dist/components/NumberField/NumberField.types.d.ts +10 -0
- package/dist/components/Overlay/Overlay.types.d.ts +13 -0
- package/dist/components/Pagination/Pagination.types.d.ts +11 -29
- package/dist/components/PinField/PinField.module.css +1 -1
- package/dist/components/PinField/PinField.types.d.ts +13 -0
- package/dist/components/PinField/tests/PinField.stories.d.ts +24 -3
- package/dist/components/PinField/tests/PinField.stories.js +194 -47
- package/dist/components/Popover/Popover.types.d.ts +4 -1
- package/dist/components/Progress/Progress.types.d.ts +9 -0
- package/dist/components/ProgressIndicator/ProgressIndicator.js +13 -84
- package/dist/components/ProgressIndicator/ProgressIndicator.module.css +1 -1
- package/dist/components/ProgressIndicator/ProgressIndicator.types.d.ts +8 -0
- package/dist/components/ProgressIndicator/tests/ProgressIndicator.stories.js +8 -1
- package/dist/components/Radio/Radio.types.d.ts +12 -0
- package/dist/components/RadioGroup/RadioGroup.types.d.ts +14 -0
- package/dist/components/Reshaped/Reshaped.css +1 -1
- package/dist/components/Reshaped/Reshaped.types.d.ts +12 -0
- package/dist/components/Resizable/Resizable.types.d.ts +7 -0
- package/dist/components/Scrim/Scrim.types.d.ts +6 -1
- package/dist/components/ScrollArea/ScrollArea.types.d.ts +7 -0
- package/dist/components/Select/Select.types.d.ts +27 -0
- package/dist/components/Skeleton/Skeleton.types.d.ts +2 -0
- package/dist/components/Slider/Slider.types.d.ts +56 -0
- package/dist/components/Stepper/Stepper.types.d.ts +13 -0
- package/dist/components/Switch/Switch.types.d.ts +16 -0
- package/dist/components/Table/Table.types.d.ts +29 -0
- package/dist/components/Tabs/Tabs.module.css +1 -1
- package/dist/components/Tabs/Tabs.types.d.ts +24 -0
- package/dist/components/Tabs/TabsItem.js +2 -2
- package/dist/components/Tabs/tests/Tabs.stories.js +6 -6
- package/dist/components/Text/Text.types.d.ts +12 -0
- package/dist/components/TextArea/TextArea.types.d.ts +17 -0
- package/dist/components/TextField/TextField.types.d.ts +29 -0
- package/dist/components/Theme/Theme.js +4 -1
- package/dist/components/Theme/Theme.types.d.ts +5 -0
- package/dist/components/Timeline/Timeline.types.d.ts +7 -0
- package/dist/components/Toast/Toast.types.d.ts +18 -0
- package/dist/components/ToggleButton/ToggleButton.types.d.ts +9 -0
- package/dist/components/ToggleButtonGroup/ToggleButtonGroup.types.d.ts +8 -0
- package/dist/components/Tooltip/Tooltip.types.d.ts +5 -0
- package/dist/components/View/View.types.d.ts +56 -1
- package/dist/components/_private/Aligner/Aligner.types.d.ts +6 -0
- package/dist/themes/index.d.ts +1 -0
- package/dist/themes/index.js +1 -0
- package/package.json +47 -32
- package/LICENSE-SOURCE.md +0 -40
- package/dist/components/Grid/tests/Grid.test.stories.d.ts +0 -23
- package/dist/components/Grid/tests/Grid.test.stories.js +0 -42
- package/dist/components/PinField/tests/PinField.test.stories.d.ts +0 -29
- package/dist/components/PinField/tests/PinField.test.stories.js +0 -177
@@ -1,20 +1,30 @@
|
|
1
1
|
import type React from "react";
|
2
2
|
import type * as G from "../../types/global";
|
3
3
|
type WithClose = {
|
4
|
+
/** Hide the close button */
|
4
5
|
hideCloseButton: true;
|
6
|
+
/** aria-label attribute for the close button */
|
5
7
|
closeAriaLabel?: string;
|
6
8
|
};
|
7
9
|
type WithoutClose = {
|
10
|
+
/** Show the close button */
|
8
11
|
hideCloseButton?: false;
|
12
|
+
/** aria-label attribute for the close button */
|
9
13
|
closeAriaLabel: string;
|
10
14
|
};
|
11
15
|
export type CloseProps = WithClose | WithoutClose;
|
12
16
|
export type Props = CloseProps & {
|
17
|
+
/** Component render variant */
|
13
18
|
variant?: "media";
|
19
|
+
/** Close button alignment */
|
14
20
|
align?: "top" | "center";
|
21
|
+
/** Node for inserting children */
|
15
22
|
children?: React.ReactNode;
|
23
|
+
/** Callback when the component is dismissed */
|
16
24
|
onClose?: () => void;
|
25
|
+
/** Additional classname for the root element */
|
17
26
|
className?: G.ClassName;
|
27
|
+
/** Additional attributes for the root element */
|
18
28
|
attributes?: G.Attributes<"div">;
|
19
29
|
};
|
20
30
|
export {};
|
@@ -1,10 +1,16 @@
|
|
1
1
|
import type React from "react";
|
2
2
|
import type * as G from "../../types/global";
|
3
3
|
export type Props = {
|
4
|
+
/** Change component to take no space, useful for using it as a border in components like Tabs */
|
4
5
|
blank?: boolean;
|
6
|
+
/** Change component to render vertically */
|
5
7
|
vertical?: G.Responsive<boolean>;
|
8
|
+
/** Position for rendering children */
|
6
9
|
contentPosition?: "start" | "center" | "end";
|
10
|
+
/** Node for inserting text labels or custom components as a part of divider */
|
7
11
|
children?: React.ReactNode;
|
12
|
+
/** Additional classname for the root element */
|
8
13
|
className?: G.ClassName;
|
14
|
+
/** Additional attributes for the root element */
|
9
15
|
attributes?: G.Attributes<"hr">;
|
10
16
|
};
|
@@ -3,15 +3,18 @@ import type { PopoverProps, PopoverInstance } from "../Popover";
|
|
3
3
|
import type { MenuItemProps } from "../MenuItem";
|
4
4
|
import type { FlyoutContentProps } from "../Flyout";
|
5
5
|
export type Instance = PopoverInstance;
|
6
|
-
export type Props = Pick<PopoverProps, "children" | "position" | "forcePosition" | "fallbackPositions" | "triggerType" | "contentGap" | "contentShift" | "onOpen" | "onClose" | "active" | "defaultActive" | "width" | "disableHideAnimation" | "disableCloseOnOutsideClick" | "instanceRef" | "containerRef" | "originCoordinates"> & {
|
6
|
+
export type Props = Pick<PopoverProps, "children" | "position" | "forcePosition" | "fallbackPositions" | "fallbackAdjustLayout" | "triggerType" | "contentGap" | "contentShift" | "onOpen" | "onClose" | "active" | "defaultActive" | "width" | "disableHideAnimation" | "disableCloseOnOutsideClick" | "instanceRef" | "containerRef" | "originCoordinates"> & {
|
7
|
+
/** Change component trap focus keyboard behavior and shortcuts */
|
7
8
|
trapFocusMode?: Extract<PopoverProps["trapFocusMode"], "action-menu" | "selection-menu"> | false;
|
8
9
|
};
|
9
10
|
export type ContentProps = Pick<FlyoutContentProps, "attributes" | "children" | "className">;
|
10
11
|
export type ItemProps = Omit<MenuItemProps, "roundedCorners">;
|
11
12
|
export type SectionProps = {
|
13
|
+
/** Node for inserting children */
|
12
14
|
children: React.ReactNode;
|
13
15
|
};
|
14
16
|
export type SubMenuProps = {
|
17
|
+
/** Node for inserting children */
|
15
18
|
children: React.ReactNode;
|
16
19
|
};
|
17
20
|
export type SubTriggerProps = Omit<MenuItemProps, "endSlot" | "roundedCorners">;
|
@@ -2,18 +2,28 @@ import type React from "react";
|
|
2
2
|
import type { ViewProps } from "../View";
|
3
3
|
import type * as G from "../../types/global";
|
4
4
|
export type Props = {
|
5
|
+
/** Name of the input element */
|
5
6
|
name: string;
|
7
|
+
/** Node for inserting children, can be a render function that receives component state */
|
6
8
|
children?: React.ReactNode | ((props: {
|
7
9
|
highlighted?: boolean;
|
8
10
|
}) => React.ReactNode);
|
11
|
+
/** Callback when the component value is changed */
|
9
12
|
onChange?: G.ChangeHandler<File[], React.DragEvent<HTMLDivElement> | React.ChangeEvent<HTMLInputElement>>;
|
13
|
+
/** Component height, literal css value or unit token multiplier */
|
10
14
|
height?: ViewProps["height"];
|
15
|
+
/** Component variant, headless variant is useful for rendering custom triggers like a Button */
|
11
16
|
variant?: "outline" | "headless";
|
17
|
+
/** Change component to render inline making it more compact */
|
12
18
|
inline?: boolean;
|
19
|
+
/** Additional classname for the root element */
|
13
20
|
className?: G.ClassName;
|
21
|
+
/** Additional attributes for the root element */
|
14
22
|
attributes?: G.Attributes<"div">;
|
23
|
+
/** Additional attributes for the input element */
|
15
24
|
inputAttributes?: G.Attributes<"input">;
|
16
25
|
};
|
17
26
|
export type TriggerProps = {
|
27
|
+
/** Node for inserting children */
|
18
28
|
children: React.ReactNode;
|
19
29
|
};
|
@@ -21,6 +21,7 @@ export type Options = {
|
|
21
21
|
container?: HTMLElement | null;
|
22
22
|
rtl: boolean;
|
23
23
|
fallbackPositions?: Position[];
|
24
|
+
fallbackAdjustLayout?: boolean;
|
24
25
|
lastUsedPosition: Position;
|
25
26
|
onPositionChoose: (position: Position) => void;
|
26
27
|
contentGap?: number;
|
@@ -50,16 +51,23 @@ export type UseFlyoutData = Pick<State, "styles" | "position" | "status"> & {
|
|
50
51
|
* Component
|
51
52
|
*/
|
52
53
|
export type Instance = {
|
54
|
+
/** Open the flyout content */
|
53
55
|
open: () => void;
|
56
|
+
/** Close the flyout content */
|
54
57
|
close: () => void;
|
58
|
+
/** Sync the flyout content position with the trigger element position */
|
55
59
|
updatePosition: () => void;
|
56
60
|
} | null;
|
57
61
|
type WithUncontrolled = {
|
62
|
+
/** Control the content visibility, enables controlled mode */
|
58
63
|
active?: never;
|
64
|
+
/** Control the content default visibility, enables uncontrolled mode */
|
59
65
|
defaultActive?: boolean;
|
60
66
|
};
|
61
67
|
type WithControlled = {
|
68
|
+
/** Control the content visibility, enables controlled mode */
|
62
69
|
active: boolean;
|
70
|
+
/** Control the content default visibility, enables uncontrolled mode */
|
63
71
|
defaultActive?: never;
|
64
72
|
};
|
65
73
|
export type TriggerAttributes = {
|
@@ -78,34 +86,63 @@ export type TriggerAttributes = {
|
|
78
86
|
"aria-controls"?: string;
|
79
87
|
};
|
80
88
|
type BaseProps = {
|
89
|
+
/** Unique id for the flyout content and trigger */
|
81
90
|
id?: string;
|
91
|
+
/** Event used for displaying the content */
|
82
92
|
triggerType?: "hover" | "click" | "focus";
|
93
|
+
/** Removes the content display delay if another flyout is already active */
|
83
94
|
groupTimeouts?: boolean;
|
95
|
+
/** Content position relative to the trigger element */
|
84
96
|
position?: Position;
|
85
97
|
/**
|
86
98
|
* @deprecated Use fallbackPosition={false} instead, will be removed in v4
|
87
99
|
*/
|
88
100
|
forcePosition?: boolean;
|
101
|
+
/** Fallback positions for the content when it doesn't fit into the viewport or container */
|
89
102
|
fallbackPositions?: Position[] | false;
|
103
|
+
/** Adjust the content size and shift its position to fit into the container when none of the fallback positions work */
|
104
|
+
fallbackAdjustLayout?: boolean;
|
105
|
+
/** Change component trap focus keyboard behavior and shortcuts */
|
90
106
|
trapFocusMode?: TrapMode | false;
|
107
|
+
/** Disable the flyout content interactivity */
|
91
108
|
disabled?: boolean;
|
109
|
+
/** Disable the flyout content hide animation */
|
92
110
|
disableHideAnimation?: boolean;
|
111
|
+
/** Ignore the content hover events and hide it if the triggerType is hover */
|
93
112
|
disableContentHover?: boolean;
|
113
|
+
/** Disable the flyout content close on outside click */
|
94
114
|
disableCloseOnOutsideClick?: boolean;
|
115
|
+
/** Automatically focus the first focusable element in the content, when false the content container will be focused instead
|
116
|
+
* @default true
|
117
|
+
*/
|
95
118
|
autoFocus?: boolean;
|
119
|
+
/** Origin coordinates for the content when there is no trigger element */
|
96
120
|
originCoordinates?: G.Coordinates;
|
121
|
+
/** Node for inserting children */
|
97
122
|
children?: React.ReactNode;
|
123
|
+
/** Callback when the content is opened */
|
98
124
|
onOpen?: () => void;
|
125
|
+
/** Callback when the content is closed */
|
99
126
|
onClose?: (args: {
|
100
127
|
reason?: CloseReason;
|
101
128
|
}) => void;
|
129
|
+
/** Content width, literal css value or unit token multiplier */
|
102
130
|
width?: Width;
|
131
|
+
/** Gap between the content and the trigger element */
|
103
132
|
contentGap?: number;
|
133
|
+
/** Shift the content on the secondary axis, relative to its original position */
|
104
134
|
contentShift?: number;
|
135
|
+
/** Additional classname for the content element */
|
105
136
|
contentClassName?: string;
|
137
|
+
/** Additional attributes for the content element */
|
106
138
|
contentAttributes?: G.Attributes<"div">;
|
139
|
+
/** Ref accessor for the flyout methods */
|
107
140
|
instanceRef?: React.Ref<Instance>;
|
141
|
+
/** Container to render the content in using a portal, position is calculated based on the container bounds
|
142
|
+
* @default document.body
|
143
|
+
*/
|
108
144
|
containerRef?: React.RefObject<HTMLElement | null>;
|
145
|
+
/** Element to focus when the content is opened */
|
109
146
|
initialFocusRef?: React.RefObject<HTMLElement | null>;
|
110
147
|
};
|
111
148
|
export type DefaultProps = Required<{
|
@@ -116,11 +153,15 @@ export type UncontrolledProps = BaseProps & WithUncontrolled;
|
|
116
153
|
export type ControlledProps = BaseProps & WithControlled;
|
117
154
|
export type Props = ControlledProps | UncontrolledProps;
|
118
155
|
export type TriggerProps = {
|
156
|
+
/** Node for inserting children, provides attributes for the trigger element */
|
119
157
|
children: (attributes: TriggerAttributes) => React.ReactNode;
|
120
158
|
};
|
121
159
|
export type ContentProps = {
|
160
|
+
/** Node for inserting children */
|
122
161
|
children?: React.ReactNode;
|
162
|
+
/** Additional classname for the content element */
|
123
163
|
className?: G.ClassName;
|
164
|
+
/** Additional attributes for the content element */
|
124
165
|
attributes?: G.Attributes<"div">;
|
125
166
|
};
|
126
167
|
export type ContextProps = {
|
@@ -94,7 +94,7 @@ const FlyoutContent = (props) => {
|
|
94
94
|
const content = (_jsx(ContentProvider, { value: { elRef: flyoutElRef }, children: _jsx("div", { className: rootClassNames, style: {
|
95
95
|
...styles,
|
96
96
|
"--rs-flyout-gap": contentGap,
|
97
|
-
}, 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: contentAttributes?.style, className: innerClassNames, children: children }) }) }));
|
97
|
+
}, 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 }) }) }));
|
98
98
|
return _jsx(Portal, { targetRef: containerRef, children: content });
|
99
99
|
};
|
100
100
|
FlyoutContent.displayName = "Flyout.Content";
|
@@ -15,7 +15,7 @@ import cooldown from "./utilities/cooldown.js";
|
|
15
15
|
import { Provider, useFlyoutTriggerContext, useFlyoutContext, useFlyoutContentContext, } from "./Flyout.context.js";
|
16
16
|
import useHandlerRef from "../../hooks/useHandlerRef.js";
|
17
17
|
const FlyoutControlled = (props) => {
|
18
|
-
const { triggerType = "click", groupTimeouts, onOpen, onClose, children, disabled, forcePosition, trapFocusMode = "dialog", width, disableHideAnimation, disableContentHover, disableCloseOnOutsideClick, autoFocus = true, originCoordinates, contentGap = 2, contentShift, contentClassName, contentAttributes, position: passedPosition, active: passedActive, id: passedId, instanceRef, containerRef, initialFocusRef, } = props;
|
18
|
+
const { triggerType = "click", groupTimeouts, onOpen, onClose, children, disabled, forcePosition, fallbackAdjustLayout, trapFocusMode = "dialog", width, disableHideAnimation, disableContentHover, disableCloseOnOutsideClick, autoFocus = true, originCoordinates, contentGap = 2, contentShift, contentClassName, contentAttributes, position: passedPosition, active: passedActive, id: passedId, instanceRef, containerRef, initialFocusRef, } = props;
|
19
19
|
const fallbackPositions = props.fallbackPositions === false || forcePosition ? [] : props.fallbackPositions;
|
20
20
|
const onOpenRef = useHandlerRef(onOpen);
|
21
21
|
const onCloseRef = useHandlerRef(onClose);
|
@@ -62,6 +62,7 @@ const FlyoutControlled = (props) => {
|
|
62
62
|
defaultActive: resolvedActive,
|
63
63
|
container: containerRef?.current,
|
64
64
|
fallbackPositions,
|
65
|
+
fallbackAdjustLayout,
|
65
66
|
contentGap,
|
66
67
|
contentShift,
|
67
68
|
});
|
@@ -29,6 +29,10 @@ export declare const positionFallbacks: {
|
|
29
29
|
name: string;
|
30
30
|
render: () => React.JSX.Element;
|
31
31
|
};
|
32
|
+
export declare const fallbackAdjustLayout: {
|
33
|
+
name: string;
|
34
|
+
render: () => React.JSX.Element;
|
35
|
+
};
|
32
36
|
export declare const originCoordinates: {
|
33
37
|
name: string;
|
34
38
|
render: () => React.JSX.Element;
|
@@ -40,7 +40,7 @@ const Demo = (props) => {
|
|
40
40
|
export const position = {
|
41
41
|
name: "position",
|
42
42
|
render: () => {
|
43
|
-
return (<View gap={4} padding={50} align="center" justify="center" height="
|
43
|
+
return (<View gap={4} padding={50} align="center" justify="center" height="100vh" width="120%">
|
44
44
|
<View gap={4} direction="row">
|
45
45
|
<Demo position="top-start" defaultActive/>
|
46
46
|
<Demo position="top"/>
|
@@ -222,6 +222,64 @@ export const positionFallbacks = {
|
|
222
222
|
</Example>);
|
223
223
|
},
|
224
224
|
};
|
225
|
+
const FallbackAdjustLayoutControls = ({ containerRef, }) => (<>
|
226
|
+
{/* Left side */}
|
227
|
+
<View position="absolute" insetStart={4} insetTop={10} gap={2}>
|
228
|
+
<Demo contentHeight="200px" position="end" fallbackPositions={false} fallbackAdjustLayout containerRef={containerRef}/>
|
229
|
+
<Demo contentHeight="200px" position="end-bottom" fallbackPositions={false} fallbackAdjustLayout containerRef={containerRef}/>
|
230
|
+
</View>
|
231
|
+
|
232
|
+
<View position="absolute" insetStart={4} insetTop={80} gap={2}>
|
233
|
+
<Demo position="bottom-end" fallbackPositions={false} fallbackAdjustLayout contentWidth={300} containerRef={containerRef}/>
|
234
|
+
<Demo position="bottom" fallbackPositions={false} fallbackAdjustLayout contentWidth={300} containerRef={containerRef}/>
|
235
|
+
</View>
|
236
|
+
|
237
|
+
<View position="absolute" insetBottom={4} insetStart={4} gap={2}>
|
238
|
+
<Demo contentHeight="200px" position="end-top" fallbackPositions={false} fallbackAdjustLayout containerRef={containerRef}/>
|
239
|
+
<Demo contentHeight="200px" position="end" fallbackPositions={false} fallbackAdjustLayout containerRef={containerRef}/>
|
240
|
+
</View>
|
241
|
+
|
242
|
+
{/* Right side */}
|
243
|
+
|
244
|
+
<View position="absolute" insetTop={10} insetEnd={4} gap={2}>
|
245
|
+
<Demo contentHeight="200px" position="start" fallbackPositions={false} fallbackAdjustLayout/>
|
246
|
+
<Demo contentHeight="200px" position="start-bottom" fallbackPositions={false} fallbackAdjustLayout containerRef={containerRef}/>
|
247
|
+
</View>
|
248
|
+
|
249
|
+
<View position="absolute" insetEnd={4} insetTop={80} gap={2}>
|
250
|
+
<Demo position="top-start" fallbackPositions={false} fallbackAdjustLayout contentWidth={300} containerRef={containerRef}/>
|
251
|
+
<Demo position="top" fallbackPositions={false} fallbackAdjustLayout contentWidth={300} containerRef={containerRef}/>
|
252
|
+
</View>
|
253
|
+
|
254
|
+
<View position="absolute" insetBottom={4} insetEnd={4} gap={2}>
|
255
|
+
<Demo contentHeight="200px" position="start-top" fallbackPositions={false} fallbackAdjustLayout containerRef={containerRef}/>
|
256
|
+
<Demo contentHeight="200px" position="start" fallbackPositions={false} fallbackAdjustLayout containerRef={containerRef}/>
|
257
|
+
</View>
|
258
|
+
</>);
|
259
|
+
export const fallbackAdjustLayout = {
|
260
|
+
name: "fallbackAdjustLayout",
|
261
|
+
render: () => {
|
262
|
+
const containerRef = React.useRef(null);
|
263
|
+
return (<View gap={10}>
|
264
|
+
<View height="95vh" width="100%" align="center" justify="center">
|
265
|
+
<View backgroundColor="neutral-faded" borderRadius="medium" height="1000px" width="600px" padding={4} paddingBlock={15} overflow="auto">
|
266
|
+
<FallbackAdjustLayoutControls />
|
267
|
+
<View height="150%" width="150%" attributes={{ style: { pointerEvents: "none" } }}/>
|
268
|
+
</View>
|
269
|
+
</View>
|
270
|
+
|
271
|
+
<View height="95vh" width="100%" align="center" justify="center">
|
272
|
+
<View backgroundColor="neutral-faded" borderRadius="medium" height="1000px" width="600px" attributes={{ ref: containerRef }} padding={4} paddingBlock={15} overflow="auto">
|
273
|
+
<FallbackAdjustLayoutControls containerRef={containerRef}/>
|
274
|
+
<View height="150%" width="150%" attributes={{ style: { pointerEvents: "none" } }}/>
|
275
|
+
</View>
|
276
|
+
</View>
|
277
|
+
|
278
|
+
<FallbackAdjustLayoutControls />
|
279
|
+
<div style={{ height: "100vh", width: "250%" }}/>
|
280
|
+
</View>);
|
281
|
+
},
|
282
|
+
};
|
225
283
|
export const originCoordinates = {
|
226
284
|
name: "originCoordinates",
|
227
285
|
render: () => {
|
@@ -38,7 +38,7 @@ const flyoutReducer = (state, action) => {
|
|
38
38
|
};
|
39
39
|
const useFlyout = (args) => {
|
40
40
|
const { triggerElRef, flyoutElRef, triggerBounds, contentGap, contentShift, ...options } = args;
|
41
|
-
const { position: defaultPosition = "bottom", fallbackPositions, width, container } = options;
|
41
|
+
const { position: defaultPosition = "bottom", fallbackPositions, width, container, fallbackAdjustLayout, } = options;
|
42
42
|
const lastUsedPositionRef = React.useRef(defaultPosition);
|
43
43
|
// Memo the array internally to avoid new arrays triggering useCallback
|
44
44
|
const cachedFallbackPositions = React.useMemo(() => fallbackPositions,
|
@@ -76,6 +76,7 @@ const useFlyout = (args) => {
|
|
76
76
|
width,
|
77
77
|
position: changePositon ? defaultPosition : lastUsedPositionRef.current,
|
78
78
|
fallbackPositions: changePositon ? cachedFallbackPositions : [],
|
79
|
+
fallbackAdjustLayout,
|
79
80
|
lastUsedPosition: lastUsedPositionRef.current,
|
80
81
|
onPositionChoose: handlePosition,
|
81
82
|
rtl: isRTL,
|
@@ -93,6 +94,7 @@ const useFlyout = (args) => {
|
|
93
94
|
container,
|
94
95
|
defaultPosition,
|
95
96
|
cachedFallbackPositions,
|
97
|
+
fallbackAdjustLayout,
|
96
98
|
isRTL,
|
97
99
|
flyoutElRef,
|
98
100
|
triggerElRef,
|
@@ -10,7 +10,7 @@ declare const calculatePosition: (args: {
|
|
10
10
|
};
|
11
11
|
passedContainer?: HTMLElement | null;
|
12
12
|
containerBounds: DOMRect;
|
13
|
-
} & Pick<T.Options, "position" | "rtl" | "width" | "contentGap" | "contentShift">) => {
|
13
|
+
} & Pick<T.Options, "position" | "rtl" | "width" | "contentGap" | "contentShift" | "fallbackAdjustLayout">) => {
|
14
14
|
position: T.Position;
|
15
15
|
styles: {
|
16
16
|
width: string | number | undefined;
|
@@ -1,22 +1,10 @@
|
|
1
|
-
|
2
|
-
const
|
3
|
-
if (position.includes("start"))
|
4
|
-
return position.replace("start", "end");
|
5
|
-
if (position.includes("end"))
|
6
|
-
return position.replace("end", "start");
|
7
|
-
return position;
|
8
|
-
};
|
9
|
-
/**
|
10
|
-
* Get a position value which centers 2 elements vertically or horizontally
|
11
|
-
*/
|
12
|
-
const centerBySize = (originSize, targetSize) => {
|
13
|
-
return Math.floor(originSize / 2 - targetSize / 2);
|
14
|
-
};
|
1
|
+
import { getRTLPosition, centerBySize } from "./helpers.js";
|
2
|
+
const SCREEN_OFFSET = 8;
|
15
3
|
/**
|
16
4
|
* Calculate styles for the current position
|
17
5
|
*/
|
18
6
|
const calculatePosition = (args) => {
|
19
|
-
const { triggerBounds, flyoutBounds, containerBounds, position: passedPosition, rtl, width, contentGap = 0, contentShift = 0, passedContainer, } = args;
|
7
|
+
const { triggerBounds, flyoutBounds, containerBounds, position: passedPosition, rtl, width, contentGap = 0, contentShift = 0, passedContainer, fallbackAdjustLayout, } = args;
|
20
8
|
const isFullWidth = width === "full" || width === "100%";
|
21
9
|
let left = 0;
|
22
10
|
let top = 0;
|
@@ -29,25 +17,34 @@ const calculatePosition = (args) => {
|
|
29
17
|
position = position.includes("top") ? "top" : "bottom";
|
30
18
|
}
|
31
19
|
const isHorizontalPosition = !!position.match(/^(start|end)/);
|
32
|
-
const isVerticalPosition = !!position.match(/^(top|bottom)/);
|
33
20
|
// contentGap adds padding to the flyout to make sure it doesn't disapper while moving the mouse to the content
|
34
21
|
// So its width/height is bigger than the visible part of the content
|
35
22
|
const flyoutWidth = flyoutBounds.width + (isHorizontalPosition ? contentGap : 0);
|
36
|
-
const flyoutHeight = flyoutBounds.height + (
|
37
|
-
const triggerHeight = triggerBounds.height;
|
23
|
+
const flyoutHeight = flyoutBounds.height + (!isHorizontalPosition ? contentGap : 0);
|
38
24
|
const triggerWidth = triggerBounds.width;
|
39
|
-
const
|
40
|
-
|
41
|
-
const
|
42
|
-
const
|
43
|
-
const
|
44
|
-
const
|
25
|
+
const triggerHeight = triggerBounds.height;
|
26
|
+
// Detect passed container scroll to sync the flyout position with it
|
27
|
+
const containerX = passedContainer?.scrollLeft;
|
28
|
+
const containerY = passedContainer?.scrollTop;
|
29
|
+
const scrollX = containerX ?? window.scrollX;
|
30
|
+
const scrollY = containerY ?? window.scrollY;
|
31
|
+
const renderContainerHeight = passedContainer?.clientHeight ?? window.innerHeight;
|
32
|
+
const renderContainerWidth = passedContainer?.clientWidth ?? window.innerWidth;
|
33
|
+
// When rendering in the body, bottom bounds will be larrger than the viewport so we calculate it manually
|
34
|
+
const containerBoundsBottom = passedContainer
|
35
|
+
? containerBounds.bottom
|
36
|
+
: window.innerHeight - scrollY;
|
37
|
+
// When inside a container, adjut position based on the container scroll since flyout is rendered outside the scroll area
|
38
|
+
const relativeLeft = triggerBounds.left - containerBounds.left + (containerX || 0);
|
39
|
+
const relativeRight = containerBounds.right - triggerBounds.right - (containerX || 0);
|
40
|
+
const relativeTop = triggerBounds.top - containerBounds.top + (containerY || 0);
|
41
|
+
const relativeBottom = containerBoundsBottom - triggerBounds.bottom - (containerY || 0);
|
45
42
|
switch (position) {
|
46
43
|
case "start":
|
47
44
|
case "start-top":
|
48
45
|
case "start-bottom":
|
49
|
-
right = relativeRight + triggerWidth;
|
50
46
|
left = relativeLeft - flyoutWidth;
|
47
|
+
right = relativeRight + triggerWidth;
|
51
48
|
break;
|
52
49
|
case "end":
|
53
50
|
case "end-top":
|
@@ -64,8 +61,8 @@ const calculatePosition = (args) => {
|
|
64
61
|
break;
|
65
62
|
case "top-end":
|
66
63
|
case "bottom-end":
|
67
|
-
right = relativeRight - contentShift;
|
68
64
|
left = relativeLeft + triggerWidth - flyoutWidth + contentShift;
|
65
|
+
right = relativeRight - contentShift;
|
69
66
|
break;
|
70
67
|
default:
|
71
68
|
break;
|
@@ -74,8 +71,8 @@ const calculatePosition = (args) => {
|
|
74
71
|
case "top":
|
75
72
|
case "top-start":
|
76
73
|
case "top-end":
|
77
|
-
bottom = relativeBottom + triggerHeight;
|
78
74
|
top = relativeTop - flyoutHeight;
|
75
|
+
bottom = relativeBottom + triggerHeight;
|
79
76
|
break;
|
80
77
|
case "bottom":
|
81
78
|
case "bottom-start":
|
@@ -92,12 +89,39 @@ const calculatePosition = (args) => {
|
|
92
89
|
break;
|
93
90
|
case "start-bottom":
|
94
91
|
case "end-bottom":
|
95
|
-
bottom = relativeBottom - contentShift;
|
96
92
|
top = relativeTop + triggerHeight - flyoutHeight + contentShift;
|
93
|
+
bottom = relativeBottom - contentShift;
|
97
94
|
break;
|
98
95
|
default:
|
99
96
|
break;
|
100
97
|
}
|
98
|
+
if (fallbackAdjustLayout) {
|
99
|
+
const topOverflowSize = -top + scrollY + SCREEN_OFFSET;
|
100
|
+
const bottomOverflowSize = top + flyoutHeight + SCREEN_OFFSET - scrollY - renderContainerHeight;
|
101
|
+
const leftOverflowSize = -left + scrollX + SCREEN_OFFSET;
|
102
|
+
const rightOverflowSize = left + flyoutWidth + SCREEN_OFFSET - scrollX - renderContainerWidth;
|
103
|
+
if (isHorizontalPosition) {
|
104
|
+
if (topOverflowSize > 0) {
|
105
|
+
top = SCREEN_OFFSET + scrollY;
|
106
|
+
if (bottom !== null)
|
107
|
+
bottom = bottom - topOverflowSize;
|
108
|
+
}
|
109
|
+
else if (bottomOverflowSize > 0) {
|
110
|
+
console.log({ bottomOverflowSize, renderContainerHeight });
|
111
|
+
top = top - bottomOverflowSize;
|
112
|
+
}
|
113
|
+
}
|
114
|
+
else {
|
115
|
+
if (leftOverflowSize > 0) {
|
116
|
+
left = SCREEN_OFFSET + scrollX;
|
117
|
+
if (right !== null)
|
118
|
+
right = right - leftOverflowSize;
|
119
|
+
}
|
120
|
+
else if (rightOverflowSize > 0) {
|
121
|
+
left = left - rightOverflowSize;
|
122
|
+
}
|
123
|
+
}
|
124
|
+
}
|
101
125
|
let widthStyle;
|
102
126
|
if (isFullWidth) {
|
103
127
|
left = SCREEN_OFFSET;
|
@@ -7,7 +7,7 @@ import { resetStyles } from "../Flyout.constants.js";
|
|
7
7
|
* Set position of the target element to fit on the screen
|
8
8
|
*/
|
9
9
|
const flyout = (args) => {
|
10
|
-
const { triggerEl, flyoutEl, triggerBounds: passedTriggerBounds, contentShift = 0, contentGap = 0, position, fallbackPositions, width, container: passedContainer, lastUsedPosition, onPositionChoose, rtl, } = args;
|
10
|
+
const { triggerEl, flyoutEl, triggerBounds: passedTriggerBounds, contentShift = 0, contentGap = 0, position, fallbackPositions, fallbackAdjustLayout, width, container: passedContainer, lastUsedPosition, onPositionChoose, rtl, } = args;
|
11
11
|
const targetClone = flyoutEl.cloneNode(true);
|
12
12
|
const baseUnit = getComputedStyle(flyoutEl).getPropertyValue("--rs-unit-x1");
|
13
13
|
const unitModifier = baseUnit ? parseInt(baseUnit) : 4;
|
@@ -42,37 +42,41 @@ const flyout = (args) => {
|
|
42
42
|
document.body;
|
43
43
|
const renderContainerBounds = container.getBoundingClientRect();
|
44
44
|
const visualContainerBounds = (passedContainer || document.body).getBoundingClientRect();
|
45
|
-
|
46
|
-
|
47
|
-
testOrder.some((currentPosition) => {
|
48
|
-
const tested = calculatePosition({
|
45
|
+
const applyPosition = (position) => {
|
46
|
+
return calculatePosition({
|
49
47
|
triggerBounds: resolvedTriggerBounds,
|
50
48
|
flyoutBounds,
|
51
49
|
containerBounds: renderContainerBounds,
|
52
|
-
position
|
50
|
+
position,
|
53
51
|
contentGap: contentGap * unitModifier,
|
54
52
|
contentShift: contentShift * unitModifier,
|
55
53
|
rtl,
|
56
54
|
width,
|
57
|
-
passedContainer
|
55
|
+
passedContainer: passedContainer ||
|
56
|
+
(closestFixedContainer !== document.body ? closestFixedContainer : undefined),
|
57
|
+
fallbackAdjustLayout,
|
58
58
|
});
|
59
|
-
|
60
|
-
|
59
|
+
};
|
60
|
+
const testVisibility = (calculated) => {
|
61
|
+
return isFullyVisible({
|
62
|
+
flyoutBounds: calculated.boundaries,
|
61
63
|
visualContainerBounds,
|
62
64
|
renderContainerBounds,
|
63
65
|
container,
|
64
66
|
});
|
65
|
-
|
66
|
-
|
67
|
-
|
67
|
+
};
|
68
|
+
let calculated = null;
|
69
|
+
const testOrder = getPositionFallbacks(position, fallbackPositions);
|
70
|
+
testOrder.some((currentPosition) => {
|
71
|
+
const tested = applyPosition(currentPosition);
|
72
|
+
const visible = testVisibility(tested);
|
73
|
+
if (visible)
|
68
74
|
calculated = tested;
|
69
|
-
|
70
|
-
}
|
71
|
-
return validPosition;
|
75
|
+
return visible;
|
72
76
|
});
|
73
|
-
if (!calculated)
|
74
|
-
|
75
|
-
|
77
|
+
if (!calculated)
|
78
|
+
calculated = applyPosition(lastUsedPosition);
|
79
|
+
onPositionChoose(calculated.position);
|
76
80
|
targetClone.parentNode?.removeChild(targetClone);
|
77
81
|
return calculated;
|
78
82
|
};
|
@@ -0,0 +1,7 @@
|
|
1
|
+
import type * as T from "../Flyout.types";
|
2
|
+
/** Mirror the position for RTL */
|
3
|
+
export declare const getRTLPosition: (position: T.Position) => T.Position;
|
4
|
+
/**
|
5
|
+
* Get a position value which centers 2 elements vertically or horizontally
|
6
|
+
*/
|
7
|
+
export declare const centerBySize: (originSize: number, targetSize: number) => number;
|
@@ -0,0 +1,14 @@
|
|
1
|
+
/** Mirror the position for RTL */
|
2
|
+
export const getRTLPosition = (position) => {
|
3
|
+
if (position.includes("start"))
|
4
|
+
return position.replace("start", "end");
|
5
|
+
if (position.includes("end"))
|
6
|
+
return position.replace("end", "start");
|
7
|
+
return position;
|
8
|
+
};
|
9
|
+
/**
|
10
|
+
* Get a position value which centers 2 elements vertically or horizontally
|
11
|
+
*/
|
12
|
+
export const centerBySize = (originSize, targetSize) => {
|
13
|
+
return Math.floor(originSize / 2 - targetSize / 2);
|
14
|
+
};
|
@@ -1,10 +1,14 @@
|
|
1
1
|
/**
|
2
|
-
* Check if element visually fits
|
2
|
+
* Check if element visually fits within its render container
|
3
3
|
*/
|
4
4
|
declare const isFullyVisible: (args: {
|
5
|
+
/** Bounds of the flyout content */
|
5
6
|
flyoutBounds: Pick<DOMRect, "left" | "top" | "width" | "height">;
|
7
|
+
/** Bounds of the container where the flyout content should fit */
|
6
8
|
visualContainerBounds: DOMRect;
|
9
|
+
/** Bounds of the container where flyout content is rendered */
|
7
10
|
renderContainerBounds: DOMRect;
|
11
|
+
/** Container where the flyout content is rendered */
|
8
12
|
container: HTMLElement;
|
9
13
|
}) => boolean;
|
10
14
|
export default isFullyVisible;
|
@@ -1,17 +1,26 @@
|
|
1
1
|
import type React from "react";
|
2
2
|
export type Props = {
|
3
|
+
/** Node for inserting children */
|
3
4
|
children: React.ReactNode;
|
5
|
+
/** Component size, to be used together with the other form component sizes */
|
4
6
|
size?: "medium" | "large";
|
7
|
+
/** Change component to show an error state and display FormControl.Error */
|
5
8
|
hasError?: boolean;
|
9
|
+
/** Change component to show a required indicator */
|
6
10
|
required?: boolean;
|
11
|
+
/** Apply disabled styles */
|
7
12
|
disabled?: boolean;
|
13
|
+
/** Apply semantic html markup when used for displaying multiple form fields together with a single label */
|
8
14
|
group?: boolean;
|
15
|
+
/** Custom id for the form control */
|
9
16
|
id?: string;
|
10
17
|
};
|
11
18
|
export type LabelProps = {
|
19
|
+
/** Node for inserting the label text */
|
12
20
|
children: React.ReactNode;
|
13
21
|
};
|
14
22
|
export type CaptionProps = {
|
23
|
+
/** Node for inserting the caption text */
|
15
24
|
children: React.ReactNode;
|
16
25
|
};
|
17
26
|
export type PrivateCaptionProps = CaptionProps & {
|