reshaped 3.1.5 → 3.1.7
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.md +24 -1
- package/dist/bundle.css +1 -1
- package/dist/bundle.d.ts +2 -0
- package/dist/bundle.js +10 -11
- package/dist/cjs/themes/_generator/definitions/reshaped.js +1 -1
- package/dist/cjs/themes/reshaped/theme.css +1 -1
- package/dist/components/Actionable/Actionable.d.ts +1 -1
- package/dist/components/Actionable/Actionable.js +2 -2
- package/dist/components/Actionable/Actionable.module.css +1 -1
- package/dist/components/Actionable/Actionable.types.d.ts +1 -0
- package/dist/components/Autocomplete/Autocomplete.js +12 -6
- package/dist/components/Button/Button.js +1 -1
- package/dist/components/Button/Button.module.css +1 -1
- package/dist/components/Card/Card.d.ts +1 -1
- package/dist/components/Card/Card.module.css +1 -1
- package/dist/components/Card/tests/Card.stories.d.ts +1 -1
- package/dist/components/Checkbox/Checkbox.module.css +1 -1
- package/dist/components/DropdownMenu/DropdownMenu.types.d.ts +1 -1
- package/dist/components/FormControl/FormControl.context.d.ts +2 -1
- package/dist/components/Grid/Grid.d.ts +6 -0
- package/dist/components/Grid/Grid.js +46 -0
- package/dist/components/Grid/Grid.module.css +1 -0
- package/dist/components/Grid/Grid.types.d.ts +31 -0
- package/dist/components/Grid/Grid.types.js +1 -0
- package/dist/components/Grid/index.d.ts +2 -0
- package/dist/components/Grid/index.js +1 -0
- package/dist/components/Grid/tests/Grid.stories.d.ts +18 -0
- package/dist/components/Grid/tests/Grid.stories.js +170 -0
- package/dist/components/Icon/Icon.module.css +1 -1
- package/dist/components/Link/Link.d.ts +1 -1
- package/dist/components/Loader/Loader.module.css +1 -1
- package/dist/components/Loader/Loader.types.d.ts +1 -1
- package/dist/components/Loader/tests/Loader.stories.js +5 -3
- package/dist/components/Modal/Modal.js +1 -1
- package/dist/components/Modal/Modal.module.css +1 -1
- package/dist/components/Overlay/Overlay.js +1 -1
- package/dist/components/Overlay/tests/Overlay.stories.js +1 -1
- package/dist/components/Popover/Popover.js +2 -4
- package/dist/components/Popover/Popover.types.d.ts +1 -1
- package/dist/components/Radio/Radio.module.css +1 -1
- package/dist/components/Resizable/Resizable.module.css +1 -1
- package/dist/components/ScrollArea/ScrollArea.js +1 -1
- package/dist/components/Select/Select.js +1 -1
- package/dist/components/Slider/Slider.module.css +1 -1
- package/dist/components/Slider/SliderControlled.js +2 -1
- package/dist/components/Switch/Switch.module.css +1 -1
- package/dist/components/Tabs/Tabs.d.ts +1 -1
- package/dist/components/Tabs/Tabs.module.css +1 -1
- package/dist/components/Tabs/TabsItem.d.ts +1 -1
- package/dist/components/Tabs/TabsItem.js +2 -3
- package/dist/components/Tabs/TabsList.js +1 -1
- package/dist/components/Tabs/tests/Tabs.stories.d.ts +15 -13
- package/dist/components/Tabs/tests/Tabs.stories.js +71 -8
- package/dist/components/Toast/ToastContainer.js +1 -2
- package/dist/components/Toast/ToastRegion.js +1 -1
- package/dist/components/Tooltip/Tooltip.js +1 -1
- package/dist/components/View/View.js +7 -3
- package/dist/components/View/View.module.css +1 -1
- package/dist/components/View/View.types.d.ts +2 -2
- package/dist/components/_private/Expandable/Expandable.js +9 -5
- package/dist/components/_private/Flyout/Flyout.module.css +1 -1
- package/dist/components/_private/Flyout/Flyout.types.d.ts +12 -3
- package/dist/components/_private/Flyout/FlyoutContent.js +1 -1
- package/dist/components/_private/Flyout/FlyoutControlled.js +34 -20
- package/dist/components/_private/Flyout/tests/Flyout.stories.d.ts +1 -0
- package/dist/components/_private/Flyout/tests/Flyout.stories.js +28 -18
- package/dist/components/_private/Flyout/useFlyout.d.ts +2 -1
- package/dist/components/_private/Flyout/useFlyout.js +46 -57
- package/dist/components/_private/Flyout/utilities/calculatePosition.js +16 -11
- package/dist/components/_private/Flyout/utilities/cooldown.d.ts +1 -1
- package/dist/components/_private/Flyout/utilities/cooldown.js +17 -5
- package/dist/components/_private/Flyout/utilities/getPositionFallbacks.d.ts +3 -0
- package/dist/components/_private/Flyout/utilities/getPositionFallbacks.js +39 -0
- package/dist/config/tailwind.d.ts +1 -1
- package/dist/hooks/_private/useOnClickOutside.js +3 -2
- package/dist/hooks/_private/useSingletonHotkeys.js +15 -12
- package/dist/hooks/_private/useSingletonKeyboardMode.js +1 -1
- package/dist/hooks/tests/useHotkeys.stories.js +3 -0
- package/dist/hooks/useDrag.js +2 -1
- package/dist/hooks/useScrollLock.js +12 -39
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -0
- package/dist/styles/align/align.module.css +1 -0
- package/dist/styles/align/index.d.ts +3 -0
- package/dist/styles/align/index.js +10 -0
- package/dist/styles/justify/index.d.ts +3 -0
- package/dist/styles/justify/index.js +10 -0
- package/dist/styles/justify/justify.module.css +1 -0
- package/dist/styles/types.d.ts +2 -0
- package/dist/tests/ShadowDOM.stories.d.ts +6 -0
- package/dist/tests/ShadowDOM.stories.js +110 -0
- package/dist/themes/_generator/definitions/reshaped.js +1 -1
- package/dist/themes/_generator/tests/themes.stories.js +1 -1
- package/dist/themes/reshaped/theme.css +1 -1
- package/dist/utilities/a11y/TrapFocus.d.ts +1 -1
- package/dist/utilities/a11y/TrapFocus.js +14 -5
- package/dist/utilities/a11y/focus.d.ts +1 -1
- package/dist/utilities/a11y/focus.js +10 -5
- package/dist/utilities/a11y/index.d.ts +4 -0
- package/dist/utilities/a11y/index.js +3 -0
- package/dist/utilities/dom/flyout.d.ts +1 -0
- package/dist/utilities/dom/flyout.js +19 -0
- package/dist/utilities/dom/index.d.ts +3 -0
- package/dist/utilities/dom/index.js +3 -0
- package/dist/utilities/dom/shadowDom.d.ts +1 -0
- package/dist/utilities/dom/shadowDom.js +4 -0
- package/dist/utilities/dom/userSelect.d.ts +2 -0
- package/dist/utilities/dom/userSelect.js +6 -0
- package/dist/utilities/platform.d.ts +1 -0
- package/dist/utilities/platform.js +15 -0
- package/dist/utilities/scroll/disable.d.ts +7 -0
- package/dist/utilities/scroll/disable.js +13 -0
- package/dist/utilities/scroll/helpers.d.ts +1 -0
- package/dist/utilities/scroll/helpers.js +17 -0
- package/dist/utilities/scroll/index.d.ts +2 -0
- package/dist/utilities/scroll/index.js +2 -0
- package/dist/utilities/scroll/lock.d.ts +2 -0
- package/dist/utilities/scroll/lock.js +24 -0
- package/dist/utilities/scroll/lockSafari.d.ts +2 -0
- package/dist/utilities/scroll/lockSafari.js +31 -0
- package/dist/utilities/scroll/lockStandard.d.ts +2 -0
- package/dist/utilities/scroll/lockStandard.js +19 -0
- package/package.json +31 -29
- package/dist/utilities/dom.d.ts +0 -5
- package/dist/utilities/dom.js +0 -27
@@ -6,6 +6,7 @@ import View from "../../../View/index.js";
|
|
6
6
|
import Theme from "../../../Theme/index.js";
|
7
7
|
import Button from "../../../Button/index.js";
|
8
8
|
import Flyout from "../index.js";
|
9
|
+
import TextField from "../../../TextField/index.js";
|
9
10
|
export default { title: "Utilities/Internal/Flyout" };
|
10
11
|
const Demo = (props) => {
|
11
12
|
const { position = "bottom-start", children, ...rest } = props;
|
@@ -17,7 +18,7 @@ const Demo = (props) => {
|
|
17
18
|
<div style={{
|
18
19
|
background: "var(--rs-color-background-elevation-overlay)",
|
19
20
|
padding: "var(--rs-unit-x4)",
|
20
|
-
height:
|
21
|
+
height: 150,
|
21
22
|
width: 160,
|
22
23
|
borderRadius: "var(--rs-radius-medium)",
|
23
24
|
border: "1px solid var(--rs-color-border-neutral-faded)",
|
@@ -120,6 +121,19 @@ export const disableFlags = () => (<Example>
|
|
120
121
|
<Demo disableHideAnimation>Content</Demo>
|
121
122
|
</Example.Item>
|
122
123
|
</Example>);
|
124
|
+
export const initialFocus = () => {
|
125
|
+
const initialFocusRef = React.useRef(null);
|
126
|
+
return (<Example>
|
127
|
+
<Example.Item title="focuses input on open">
|
128
|
+
<Demo initialFocusRef={initialFocusRef}>
|
129
|
+
<View gap={4}>
|
130
|
+
<Button onClick={() => { }}>Click me</Button>
|
131
|
+
<TextField name="foo" inputAttributes={{ ref: initialFocusRef }}/>
|
132
|
+
</View>
|
133
|
+
</Demo>
|
134
|
+
</Example.Item>
|
135
|
+
</Example>);
|
136
|
+
};
|
123
137
|
class CustomElement extends window.HTMLElement {
|
124
138
|
constructor() {
|
125
139
|
super();
|
@@ -199,36 +213,32 @@ export const testInsideFixed = () => (<Example>
|
|
199
213
|
</Example.Item>
|
200
214
|
</Example>);
|
201
215
|
export const testDynamicBounds = () => {
|
202
|
-
const [left, setLeft] = React.useState(
|
216
|
+
const [left, setLeft] = React.useState(50);
|
217
|
+
const [top, setTop] = React.useState(50);
|
203
218
|
const [size, setSize] = React.useState("medium");
|
204
219
|
const flyoutRef = React.useRef();
|
205
220
|
React.useEffect(() => {
|
206
221
|
flyoutRef.current?.updatePosition();
|
207
|
-
}, [left]);
|
222
|
+
}, [left, top]);
|
208
223
|
return (<View gap={4}>
|
209
224
|
<View direction="row" gap={2}>
|
225
|
+
<Button onClick={() => setLeft((prev) => prev - 10)}>Left</Button>
|
226
|
+
<Button onClick={() => setLeft((prev) => prev + 10)}>Right</Button>
|
227
|
+
<Button onClick={() => setTop((prev) => prev - 10)}>Up</Button>
|
228
|
+
<Button onClick={() => setTop((prev) => prev + 10)}>Down</Button>
|
210
229
|
<Button onClick={() => {
|
211
|
-
setLeft(
|
212
|
-
|
213
|
-
Left
|
214
|
-
</Button>
|
215
|
-
<Button onClick={() => {
|
216
|
-
setLeft("50%");
|
230
|
+
setLeft(50);
|
231
|
+
setTop(50);
|
217
232
|
}}>
|
218
233
|
Center
|
219
234
|
</Button>
|
220
|
-
<Button onClick={() => {
|
221
|
-
setLeft("70%");
|
222
|
-
}}>
|
223
|
-
Right
|
224
|
-
</Button>
|
225
235
|
<Button onClick={() => setSize("large")}>Large button</Button>
|
226
236
|
<Button onClick={() => setSize("medium")}>Small button</Button>
|
227
237
|
</View>
|
228
238
|
<View height={100}>
|
229
|
-
<Flyout position="bottom"
|
239
|
+
<Flyout position="bottom" instanceRef={flyoutRef} disableCloseOnOutsideClick fallbackPositions={["top", "bottom"]}>
|
230
240
|
<Flyout.Trigger>
|
231
|
-
{(attributes) => (<div style={{ position: "absolute", left
|
241
|
+
{(attributes) => (<div style={{ position: "absolute", left: `${left}%`, top: `${top}%` }}>
|
232
242
|
<Button color="primary" attributes={attributes} size={size}>
|
233
243
|
Open
|
234
244
|
</Button>
|
@@ -239,12 +249,12 @@ export const testDynamicBounds = () => {
|
|
239
249
|
background: "var(--rs-color-background-elevation-overlay)",
|
240
250
|
padding: "var(--rs-unit-x4)",
|
241
251
|
height: 100,
|
242
|
-
|
252
|
+
minWidth: 160,
|
243
253
|
borderRadius: "var(--rs-radius-medium)",
|
244
254
|
border: "1px solid var(--rs-color-border-neutral-faded)",
|
245
255
|
boxSizing: "border-box",
|
246
256
|
}}>
|
247
|
-
|
257
|
+
Content
|
248
258
|
</div>
|
249
259
|
</Flyout.Content>
|
250
260
|
</Flyout>
|
@@ -8,13 +8,14 @@ type PassedFlyoutOptions = {
|
|
8
8
|
width?: T.Width;
|
9
9
|
position?: T.Position;
|
10
10
|
defaultActive?: boolean;
|
11
|
-
|
11
|
+
fallbackPositions?: T.Position[];
|
12
12
|
container?: HTMLElement | null;
|
13
13
|
};
|
14
14
|
type UseFlyout = (args: PassedFlyoutOptions & {
|
15
15
|
triggerElRef: ElementRef;
|
16
16
|
flyoutElRef: ElementRef;
|
17
17
|
triggerBoundsRef: React.RefObject<DOMRect | undefined>;
|
18
|
+
contentGap?: number;
|
18
19
|
}) => Pick<T.State, "styles" | "position" | "status"> & {
|
19
20
|
updatePosition: (options?: {
|
20
21
|
sync?: boolean;
|
@@ -1,25 +1,8 @@
|
|
1
1
|
import React from "react";
|
2
2
|
import useRTL from "../../../hooks/useRTL.js";
|
3
|
-
import { getClosestFlyoutTarget } from "../../../utilities/dom.js";
|
3
|
+
import { getClosestFlyoutTarget, getShadowRoot } from "../../../utilities/dom/index.js";
|
4
4
|
import calculatePosition from "./utilities/calculatePosition.js";
|
5
|
-
|
6
|
-
const bottomPos = ["bottom-start", "bottom", "bottom-end"];
|
7
|
-
const startPos = ["start", "start-bottom", "start-top"];
|
8
|
-
const endPos = ["end", "end-bottom", "end-top"];
|
9
|
-
const order = {
|
10
|
-
top: [...topPos, ...bottomPos, ...endPos, ...startPos],
|
11
|
-
bottom: [...bottomPos, ...topPos, ...endPos, ...startPos],
|
12
|
-
start: [...startPos, ...endPos, ...topPos, ...bottomPos],
|
13
|
-
end: [...endPos, ...startPos, ...topPos, ...bottomPos],
|
14
|
-
};
|
15
|
-
/**
|
16
|
-
* Get an order of positions to try to fit popover on the screen based on its starting position
|
17
|
-
*/
|
18
|
-
const getPositionOrder = (position) => {
|
19
|
-
const types = ["top", "bottom", "start", "end"];
|
20
|
-
const type = types.find((type) => position.startsWith(type)) || "bottom";
|
21
|
-
return order[type];
|
22
|
-
};
|
5
|
+
import getPositionFallbacks from "./utilities/getPositionFallbacks.js";
|
23
6
|
/**
|
24
7
|
* Check if element visually fits on the screen
|
25
8
|
*/
|
@@ -60,12 +43,13 @@ const resetStyles = {
|
|
60
43
|
* Set position of the target element to fit on the screen
|
61
44
|
*/
|
62
45
|
const flyout = (args) => {
|
63
|
-
const { triggerEl, flyoutEl, triggerBounds: passedTriggerBounds, ...options } = args;
|
64
|
-
const { position,
|
46
|
+
const { triggerEl, flyoutEl, triggerBounds: passedTriggerBounds, contentGap = 0, ...options } = args;
|
47
|
+
const { position, fallbackPositions, width, container, lastUsedFallback, onFallback } = options;
|
65
48
|
const targetClone = flyoutEl.cloneNode(true);
|
66
49
|
const triggerBounds = passedTriggerBounds || triggerEl.getBoundingClientRect();
|
50
|
+
const contentGapModifier = parseInt(getComputedStyle(flyoutEl).getPropertyValue("--rs-unit-x1"));
|
67
51
|
// Reset all styles applied on the previous hook execution
|
68
|
-
targetClone.style = "";
|
52
|
+
targetClone.style.cssText = "";
|
69
53
|
Object.keys(resetStyles).forEach((key) => {
|
70
54
|
const value = resetStyles[key];
|
71
55
|
targetClone.style[key] = value.toString();
|
@@ -78,8 +62,7 @@ const flyout = (args) => {
|
|
78
62
|
targetClone.style.width = width;
|
79
63
|
}
|
80
64
|
}
|
81
|
-
const
|
82
|
-
const shadowRoot = rootNode instanceof ShadowRoot ? rootNode : null;
|
65
|
+
const shadowRoot = getShadowRoot(triggerEl);
|
83
66
|
// Insert inside shadow root if possible to make sure styles are applied correctly
|
84
67
|
(shadowRoot || document.body).appendChild(targetClone);
|
85
68
|
const flyoutBounds = targetClone.getBoundingClientRect();
|
@@ -89,37 +72,30 @@ const flyout = (args) => {
|
|
89
72
|
top: containerBounds.top + document.documentElement.scrollTop - containerParent.scrollTop,
|
90
73
|
left: containerBounds.left + document.documentElement.scrollLeft - containerParent.scrollLeft,
|
91
74
|
};
|
92
|
-
let calculated =
|
93
|
-
|
94
|
-
|
95
|
-
const
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
});
|
110
|
-
if (fullyVisible(tested)) {
|
111
|
-
calculated = tested;
|
112
|
-
return true;
|
113
|
-
}
|
114
|
-
return false;
|
115
|
-
});
|
116
|
-
};
|
117
|
-
test(order);
|
118
|
-
if (!fullyVisible(calculated)) {
|
119
|
-
test(mobileOrder, { fullWidth: true });
|
75
|
+
let calculated = null;
|
76
|
+
const testOrder = getPositionFallbacks(position, fallbackPositions);
|
77
|
+
testOrder.some((currentPosition, index) => {
|
78
|
+
const tested = calculatePosition({
|
79
|
+
...options,
|
80
|
+
triggerBounds,
|
81
|
+
flyoutBounds,
|
82
|
+
scopeOffset,
|
83
|
+
position: currentPosition,
|
84
|
+
contentGap: contentGap * contentGapModifier,
|
85
|
+
});
|
86
|
+
const visible = fullyVisible(tested);
|
87
|
+
const validPosition = visible || fallbackPositions?.length === 0;
|
88
|
+
// Saving first try in case non of the options work
|
89
|
+
if (validPosition || lastUsedFallback === currentPosition) {
|
90
|
+
calculated = tested;
|
91
|
+
onFallback(currentPosition);
|
120
92
|
}
|
93
|
+
return validPosition;
|
94
|
+
});
|
95
|
+
if (!calculated) {
|
96
|
+
throw new Error(`Reshaped: Can't calculate styles for the ${position} position`);
|
121
97
|
}
|
122
|
-
targetClone.parentNode
|
98
|
+
targetClone.parentNode?.removeChild(targetClone);
|
123
99
|
return calculated;
|
124
100
|
};
|
125
101
|
const flyoutReducer = (state, action) => {
|
@@ -157,8 +133,13 @@ const flyoutReducer = (state, action) => {
|
|
157
133
|
}
|
158
134
|
};
|
159
135
|
const useFlyout = (args) => {
|
160
|
-
const { triggerElRef, flyoutElRef, triggerBoundsRef, ...options } = args;
|
161
|
-
const { position: defaultPosition = "bottom",
|
136
|
+
const { triggerElRef, flyoutElRef, triggerBoundsRef, contentGap, ...options } = args;
|
137
|
+
const { position: defaultPosition = "bottom", fallbackPositions, width, container } = options;
|
138
|
+
const lastUsedFallbackRef = React.useRef(defaultPosition);
|
139
|
+
// Memo the array internally to avoid new arrays triggering useCallback
|
140
|
+
const cachedFallbackPositions = React.useMemo(() => fallbackPositions,
|
141
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
142
|
+
[fallbackPositions?.join(" ")]);
|
162
143
|
const [isRTL] = useRTL();
|
163
144
|
const [state, dispatch] = React.useReducer(flyoutReducer, {
|
164
145
|
position: defaultPosition,
|
@@ -177,6 +158,9 @@ const useFlyout = (args) => {
|
|
177
158
|
const remove = React.useCallback(() => {
|
178
159
|
dispatch({ type: "remove" });
|
179
160
|
}, []);
|
161
|
+
const handleFallback = React.useCallback((position) => {
|
162
|
+
lastUsedFallbackRef.current = position;
|
163
|
+
}, []);
|
180
164
|
const updatePosition = React.useCallback((options) => {
|
181
165
|
if (!triggerElRef.current || !flyoutElRef.current)
|
182
166
|
return;
|
@@ -186,21 +170,26 @@ const useFlyout = (args) => {
|
|
186
170
|
triggerBounds: triggerBoundsRef.current,
|
187
171
|
width,
|
188
172
|
position: defaultPosition,
|
189
|
-
|
173
|
+
fallbackPositions: cachedFallbackPositions,
|
174
|
+
lastUsedFallback: lastUsedFallbackRef.current,
|
175
|
+
onFallback: handleFallback,
|
190
176
|
rtl: isRTL,
|
191
177
|
container,
|
178
|
+
contentGap,
|
192
179
|
});
|
193
180
|
if (nextFlyoutData)
|
194
181
|
dispatch({ type: "position", payload: { ...nextFlyoutData, sync: options?.sync } });
|
195
182
|
}, [
|
196
183
|
container,
|
197
184
|
defaultPosition,
|
198
|
-
|
185
|
+
cachedFallbackPositions,
|
199
186
|
isRTL,
|
200
187
|
flyoutElRef,
|
201
188
|
triggerElRef,
|
202
189
|
triggerBoundsRef,
|
203
190
|
width,
|
191
|
+
contentGap,
|
192
|
+
handleFallback,
|
204
193
|
]);
|
205
194
|
React.useEffect(() => {
|
206
195
|
if (state.status === "rendered")
|
@@ -16,24 +16,29 @@ const centerBySize = (originSize, targetSize) => {
|
|
16
16
|
* Calculate styles for the current position
|
17
17
|
*/
|
18
18
|
const calculatePosition = (args) => {
|
19
|
-
const { triggerBounds, flyoutBounds, scopeOffset, position: passedPosition, rtl, width } = args;
|
19
|
+
const { triggerBounds, flyoutBounds, scopeOffset, position: passedPosition, rtl, width, contentGap = 0, } = args;
|
20
|
+
const isFullWidth = width === "full" || width === "100%";
|
20
21
|
let left = 0;
|
21
22
|
let top = 0;
|
22
23
|
let position = passedPosition;
|
23
24
|
if (rtl)
|
24
25
|
position = getRTLPosition(position);
|
25
|
-
if (
|
26
|
+
if (isFullWidth || width === "trigger") {
|
26
27
|
position = position.includes("top") ? "top" : "bottom";
|
27
28
|
}
|
29
|
+
const isHorizontalPosition = position.match(/^(start|end)/);
|
30
|
+
const isVerticalPosition = position.match(/^(top|bottom)/);
|
31
|
+
const flyoutWidth = flyoutBounds.width + (isHorizontalPosition ? contentGap : 0);
|
32
|
+
const flyoutHeight = flyoutBounds.height + (isVerticalPosition ? contentGap : 0);
|
28
33
|
switch (position) {
|
29
34
|
case "bottom":
|
30
35
|
case "top":
|
31
|
-
left = centerBySize(triggerBounds.width,
|
36
|
+
left = centerBySize(triggerBounds.width, flyoutWidth) + triggerBounds.left;
|
32
37
|
break;
|
33
38
|
case "start":
|
34
39
|
case "start-top":
|
35
40
|
case "start-bottom":
|
36
|
-
left = triggerBounds.left -
|
41
|
+
left = triggerBounds.left - flyoutWidth;
|
37
42
|
break;
|
38
43
|
case "end":
|
39
44
|
case "end-top":
|
@@ -46,7 +51,7 @@ const calculatePosition = (args) => {
|
|
46
51
|
break;
|
47
52
|
case "top-end":
|
48
53
|
case "bottom-end":
|
49
|
-
left = triggerBounds.right -
|
54
|
+
left = triggerBounds.right - flyoutWidth;
|
50
55
|
break;
|
51
56
|
default:
|
52
57
|
break;
|
@@ -55,7 +60,7 @@ const calculatePosition = (args) => {
|
|
55
60
|
case "top":
|
56
61
|
case "top-start":
|
57
62
|
case "top-end":
|
58
|
-
top = triggerBounds.top -
|
63
|
+
top = triggerBounds.top - flyoutHeight;
|
59
64
|
break;
|
60
65
|
case "bottom":
|
61
66
|
case "bottom-start":
|
@@ -64,7 +69,7 @@ const calculatePosition = (args) => {
|
|
64
69
|
break;
|
65
70
|
case "start":
|
66
71
|
case "end":
|
67
|
-
top = centerBySize(triggerBounds.height,
|
72
|
+
top = centerBySize(triggerBounds.height, flyoutHeight) + triggerBounds.top;
|
68
73
|
break;
|
69
74
|
case "start-top":
|
70
75
|
case "end-top":
|
@@ -72,7 +77,7 @@ const calculatePosition = (args) => {
|
|
72
77
|
break;
|
73
78
|
case "start-bottom":
|
74
79
|
case "end-bottom":
|
75
|
-
top = triggerBounds.bottom -
|
80
|
+
top = triggerBounds.bottom - flyoutHeight;
|
76
81
|
break;
|
77
82
|
default:
|
78
83
|
break;
|
@@ -82,9 +87,9 @@ const calculatePosition = (args) => {
|
|
82
87
|
}
|
83
88
|
top = Math.round(top + (window.scrollY || 0) - scopeOffset.top);
|
84
89
|
left = Math.round(left + (window.scrollX || 0) - scopeOffset.left);
|
85
|
-
let widthStyle = Math.ceil(
|
86
|
-
const height = Math.ceil(
|
87
|
-
if (
|
90
|
+
let widthStyle = Math.ceil(flyoutWidth);
|
91
|
+
const height = Math.ceil(flyoutHeight);
|
92
|
+
if (isFullWidth) {
|
88
93
|
left = SCREEN_OFFSET;
|
89
94
|
widthStyle = window.innerWidth - SCREEN_OFFSET * 2;
|
90
95
|
}
|
@@ -1,18 +1,30 @@
|
|
1
|
+
import * as timeouts from "../Flyout.constants.js";
|
1
2
|
class Cooldown {
|
2
3
|
status = "cold";
|
3
4
|
timer;
|
4
5
|
warm = () => {
|
5
6
|
clearTimeout(this.timer);
|
6
|
-
this.status
|
7
|
+
if (this.status === "cooling") {
|
8
|
+
this.status = "warm";
|
9
|
+
return;
|
10
|
+
}
|
11
|
+
this.status = "warming";
|
12
|
+
this.timer = setTimeout(() => {
|
13
|
+
this.status = "warm";
|
14
|
+
this.timer = undefined;
|
15
|
+
}, timeouts.mouseEnterShort);
|
7
16
|
};
|
8
17
|
cool = () => {
|
18
|
+
clearTimeout(this.timer);
|
19
|
+
if (this.status === "warming") {
|
20
|
+
this.status = "cold";
|
21
|
+
return;
|
22
|
+
}
|
9
23
|
this.status = "cooling";
|
10
|
-
|
24
|
+
this.timer = setTimeout(() => {
|
11
25
|
this.status = "cold";
|
12
|
-
|
13
|
-
this.timer = undefined;
|
26
|
+
this.timer = undefined;
|
14
27
|
}, 500);
|
15
|
-
this.timer = currentTimer;
|
16
28
|
};
|
17
29
|
}
|
18
30
|
export default new Cooldown();
|
@@ -0,0 +1,39 @@
|
|
1
|
+
// All available positions for each side
|
2
|
+
const positions = {
|
3
|
+
top: ["top-start", "top-end", "top"],
|
4
|
+
bottom: ["bottom-start", "bottom-end", "bottom"],
|
5
|
+
start: ["start-top", "start-bottom", "start"],
|
6
|
+
end: ["end-top", "end-bottom", "end"],
|
7
|
+
};
|
8
|
+
// Order of sides to try depending on the starting side
|
9
|
+
const fallbackOrder = {
|
10
|
+
top: ["bottom", "start", "end"],
|
11
|
+
bottom: ["top", "end", "start"],
|
12
|
+
start: ["end", "top", "bottom"],
|
13
|
+
end: ["start", "bottom", "top"],
|
14
|
+
};
|
15
|
+
// Get an order of positions to try to fit flyout on the screen based on its starting position
|
16
|
+
const getPositionFallbacks = (position, availableFallbacks) => {
|
17
|
+
const result = [position];
|
18
|
+
const chunks = position.split("-");
|
19
|
+
const [firstChunk] = chunks;
|
20
|
+
const passedPositionOrder = positions[firstChunk];
|
21
|
+
const startingFallbackIndex = passedPositionOrder.indexOf(position);
|
22
|
+
const fallbackIndexOrder = [startingFallbackIndex];
|
23
|
+
passedPositionOrder.forEach((_, index) => {
|
24
|
+
if (index === startingFallbackIndex)
|
25
|
+
return;
|
26
|
+
fallbackIndexOrder.push(index);
|
27
|
+
});
|
28
|
+
[firstChunk, ...fallbackOrder[firstChunk]].forEach((fallbackSide) => {
|
29
|
+
const fallbackOrder = positions[fallbackSide];
|
30
|
+
fallbackIndexOrder.forEach((index) => {
|
31
|
+
const position = fallbackOrder[index];
|
32
|
+
if (availableFallbacks?.indexOf(position) === -1)
|
33
|
+
return;
|
34
|
+
result.push(position);
|
35
|
+
});
|
36
|
+
});
|
37
|
+
return result;
|
38
|
+
};
|
39
|
+
export default getPositionFallbacks;
|
@@ -1,2 +1,2 @@
|
|
1
1
|
import type { ThemeDefinition } from "../themes/_generator/tokens/types";
|
2
|
-
export declare const getTheme: (theme?: ThemeDefinition) => Record<"borderRadius" | "backgroundColor" | "borderColor" | "
|
2
|
+
export declare const getTheme: (theme?: ThemeDefinition) => Record<"borderRadius" | "backgroundColor" | "borderColor" | "boxShadow" | "textColor" | "colors" | "spacing" | "screens", Record<string, string>>;
|
@@ -7,10 +7,11 @@ const useOnClickOutside = (refs, handler) => {
|
|
7
7
|
return;
|
8
8
|
const handleClick = (event) => {
|
9
9
|
let isInside = false;
|
10
|
+
const clickedEl = event.composedPath()[0];
|
10
11
|
refs.forEach((elRef) => {
|
11
12
|
if (!elRef.current ||
|
12
|
-
elRef.current ===
|
13
|
-
elRef.current.contains(
|
13
|
+
elRef.current === clickedEl ||
|
14
|
+
elRef.current.contains(clickedEl)) {
|
14
15
|
isInside = true;
|
15
16
|
}
|
16
17
|
});
|
@@ -70,17 +70,20 @@ export class HotkeyStore {
|
|
70
70
|
const hotkeyData = this.hotkeyMap[pressedId];
|
71
71
|
/**
|
72
72
|
* Support for `mod` that represents both Mac and Win keyboards
|
73
|
+
* We create the hotkeyId again to sort the mod key correctly
|
73
74
|
*/
|
74
|
-
const
|
75
|
-
|
76
|
-
const
|
75
|
+
const controlToModPressedId = getHotkeyId(pressedId.replace("control", "mod"));
|
76
|
+
const metaToModPressedId = getHotkeyId(pressedId.replace("meta", "mod"));
|
77
|
+
const hotkeyControlModData = pressedFormattedKeys.includes("control") && this.hotkeyMap[controlToModPressedId];
|
78
|
+
const hotkeyMetaModData = pressedFormattedKeys.includes("meta") && this.hotkeyMap[metaToModPressedId];
|
77
79
|
[hotkeyData, hotkeyControlModData, hotkeyMetaModData].forEach((hotkeyData) => {
|
78
80
|
if (!hotkeyData)
|
79
81
|
return;
|
80
82
|
if (hotkeyData?.size) {
|
81
83
|
hotkeyData.forEach((data) => {
|
84
|
+
const eventTarget = e.composedPath()[0];
|
82
85
|
if (data.ref?.current &&
|
83
|
-
!(
|
86
|
+
!(eventTarget === data.ref.current || data.ref.current.contains(eventTarget))) {
|
84
87
|
return;
|
85
88
|
}
|
86
89
|
const resolvedEvent = pressedMap[pressedId];
|
@@ -146,14 +149,6 @@ export const SingletonHotkeysProvider = (props) => {
|
|
146
149
|
return false;
|
147
150
|
return true;
|
148
151
|
};
|
149
|
-
const addHotkeys = React.useCallback((hotkeys, ref, options = {}) => {
|
150
|
-
setHooksCount((prev) => prev + 1);
|
151
|
-
globalHotkeyStore.bindHotkeys(hotkeys, ref, options);
|
152
|
-
return () => {
|
153
|
-
setHooksCount((prev) => prev - 1);
|
154
|
-
globalHotkeyStore.unbindHotkeys(hotkeys);
|
155
|
-
};
|
156
|
-
}, []);
|
157
152
|
const handleWindowKeyDown = React.useCallback((e) => {
|
158
153
|
// Browsers trigger keyboard event without passing e.key when you click on autocomplete
|
159
154
|
if (!e.key)
|
@@ -166,6 +161,14 @@ export const SingletonHotkeysProvider = (props) => {
|
|
166
161
|
return;
|
167
162
|
removePressedKey(e);
|
168
163
|
}, [removePressedKey]);
|
164
|
+
const addHotkeys = React.useCallback((hotkeys, ref, options = {}) => {
|
165
|
+
setHooksCount((prev) => prev + 1);
|
166
|
+
globalHotkeyStore.bindHotkeys(hotkeys, ref, options);
|
167
|
+
return () => {
|
168
|
+
setHooksCount((prev) => prev - 1);
|
169
|
+
globalHotkeyStore.unbindHotkeys(hotkeys);
|
170
|
+
};
|
171
|
+
}, []);
|
169
172
|
React.useEffect(() => {
|
170
173
|
window.addEventListener("keydown", handleWindowKeyDown);
|
171
174
|
window.addEventListener("keyup", handleWindowKeyUp);
|
@@ -1,5 +1,5 @@
|
|
1
1
|
import React from "react";
|
2
|
-
import { enableKeyboardMode, disableKeyboardMode } from "../../utilities/a11y/
|
2
|
+
import { enableKeyboardMode, disableKeyboardMode } from "../../utilities/a11y/index.js";
|
3
3
|
const useSingletonKeyboardMode = () => {
|
4
4
|
React.useEffect(() => {
|
5
5
|
const handleKeyDown = (e) => {
|
@@ -6,6 +6,9 @@ function Example() {
|
|
6
6
|
"shift + b + n": () => console.log("pressed"),
|
7
7
|
"c + v": () => console.log(111),
|
8
8
|
"Meta + v": () => console.log(222),
|
9
|
+
"control + enter": () => console.log("control + enter"),
|
10
|
+
"meta + enter": () => console.log("meta + enter"),
|
11
|
+
"mod + enter": () => console.log("mod + enter"),
|
9
12
|
"mod + ArrowRight": () => console.log("right"),
|
10
13
|
"mod + ArrowUp": () => console.log("top"),
|
11
14
|
"shift + ArrowRight": () => console.log("right"),
|
package/dist/hooks/useDrag.js
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
"use client";
|
2
2
|
import React from "react";
|
3
|
-
import { disableUserSelect, enableUserSelect
|
3
|
+
import { disableUserSelect, enableUserSelect } from "../utilities/dom/index.js";
|
4
|
+
import { disableScroll, enableScroll } from "../utilities/scroll/index.js";
|
4
5
|
import useToggle from "./useToggle.js";
|
5
6
|
import useHotkeys from "./useHotkeys.js";
|
6
7
|
import * as keys from "../constants/keys.js";
|
@@ -1,45 +1,18 @@
|
|
1
1
|
"use client";
|
2
2
|
import React from "react";
|
3
|
-
|
4
|
-
let scrollbarWidth;
|
5
|
-
return () => {
|
6
|
-
if (scrollbarWidth)
|
7
|
-
return scrollbarWidth;
|
8
|
-
const scrollDiv = document.createElement("div");
|
9
|
-
scrollDiv.style.position = "absolute";
|
10
|
-
scrollDiv.style.top = "-9999px";
|
11
|
-
scrollDiv.style.width = "50px";
|
12
|
-
scrollDiv.style.height = "50px";
|
13
|
-
scrollDiv.style.overflow = "scroll";
|
14
|
-
document.body.appendChild(scrollDiv);
|
15
|
-
scrollbarWidth = scrollDiv.getBoundingClientRect().width - scrollDiv.clientWidth;
|
16
|
-
document.body.removeChild(scrollDiv);
|
17
|
-
return scrollbarWidth;
|
18
|
-
};
|
19
|
-
})();
|
3
|
+
import { lockScroll, unlockScroll } from "../utilities/scroll/index.js";
|
20
4
|
const useScrollLock = () => {
|
21
5
|
const [locked, setLocked] = React.useState(false);
|
22
|
-
const
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
}
|
34
|
-
setLocked(true);
|
35
|
-
}, [setLocked, isOverflowingRef, overflowStyleRef]);
|
36
|
-
const unlockScroll = React.useCallback(() => {
|
37
|
-
const targetEl = document.body;
|
38
|
-
targetEl.style.overflow = overflowStyleRef.current || "";
|
39
|
-
if (isOverflowingRef.current)
|
40
|
-
targetEl.style.paddingRight = "";
|
41
|
-
setLocked(false);
|
42
|
-
}, [setLocked, isOverflowingRef, overflowStyleRef]);
|
43
|
-
return { scrollLocked: locked, lockScroll, unlockScroll };
|
6
|
+
const handleLockScroll = React.useCallback(() => {
|
7
|
+
lockScroll(() => setLocked(true));
|
8
|
+
}, []);
|
9
|
+
const handleUnlockScroll = React.useCallback(() => {
|
10
|
+
unlockScroll(() => setLocked(false));
|
11
|
+
}, []);
|
12
|
+
return React.useMemo(() => ({
|
13
|
+
scrollLocked: locked,
|
14
|
+
lockScroll: handleLockScroll,
|
15
|
+
unlockScroll: handleUnlockScroll,
|
16
|
+
}), [locked, handleLockScroll, handleUnlockScroll]);
|
44
17
|
};
|
45
18
|
export default useScrollLock;
|
package/dist/index.d.ts
CHANGED
@@ -41,6 +41,8 @@ export { default as FileUpload } from "./components/FileUpload";
|
|
41
41
|
export type { FileUploadProps } from "./components/FileUpload";
|
42
42
|
export { default as FormControl } from "./components/FormControl";
|
43
43
|
export type { FormControlProps } from "./components/FormControl";
|
44
|
+
export { default as Grid } from "./components/Grid";
|
45
|
+
export type { GridProps, GridItemProps } from "./components/Grid";
|
44
46
|
export { default as Hidden } from "./components/Hidden";
|
45
47
|
export type { HiddenProps } from "./components/Hidden";
|
46
48
|
export { default as HiddenVisually } from "./components/HiddenVisually";
|
package/dist/index.js
CHANGED
@@ -21,6 +21,7 @@ export { default as Divider } from "./components/Divider/index.js";
|
|
21
21
|
export { default as DropdownMenu } from "./components/DropdownMenu/index.js";
|
22
22
|
export { default as FileUpload } from "./components/FileUpload/index.js";
|
23
23
|
export { default as FormControl } from "./components/FormControl/index.js";
|
24
|
+
export { default as Grid } from "./components/Grid/index.js";
|
24
25
|
export { default as Hidden } from "./components/Hidden/index.js";
|
25
26
|
export { default as HiddenVisually } from "./components/HiddenVisually/index.js";
|
26
27
|
export { default as Hotkey } from "./components/Hotkey/index.js";
|
@@ -0,0 +1 @@
|
|
1
|
+
.--align-start{align-items:flex-start!important}.--align-end{align-items:flex-end!important}.--align-center{align-items:center!important}.--align-stretch{align-items:stretch!important}.--align-baseline{align-items:baseline!important}@media (--rs-viewport-m ){.--align-start--m{align-items:flex-start!important}.--align-end--m{align-items:flex-end!important}.--align-center--m{align-items:center!important}.--align-stretch--m{align-items:stretch!important}.--align-baseline--m{align-items:baseline!important}}@media (--rs-viewport-l ){.--align-start--l{align-items:flex-start!important}.--align-end--l{align-items:flex-end!important}.--align-center--l{align-items:center!important}.--align-stretch--l{align-items:stretch!important}.--align-baseline--l{align-items:baseline!important}}@media (--rs-viewport-xl ){.--align-start--xl{align-items:flex-start!important}.--align-end--xl{align-items:flex-end!important}.--align-center--xl{align-items:center!important}.--align-stretch--xl{align-items:stretch!important}.--align-baseline--xl{align-items:baseline!important}}
|