reshaped 3.2.0-canary.5 → 3.2.0-canary.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.
Files changed (151) hide show
  1. package/CHANGELOG.md +26 -1
  2. package/dist/bundle.css +1 -1
  3. package/dist/bundle.d.ts +2 -0
  4. package/dist/bundle.js +16 -16
  5. package/dist/cjs/constants/breakpoints.d.ts +6 -0
  6. package/dist/cjs/constants/breakpoints.js +7 -0
  7. package/dist/cjs/themes/_generator/definitions/reshaped.js +8 -4
  8. package/dist/cjs/themes/_generator/tokens/viewport/viewport.transforms.js +10 -1
  9. package/dist/cjs/themes/figma/theme.css +1 -1
  10. package/dist/cjs/themes/reshaped/theme.css +1 -1
  11. package/dist/cjs/themes/slate/theme.css +1 -1
  12. package/dist/components/Actionable/Actionable.d.ts +1 -1
  13. package/dist/components/Actionable/Actionable.module.css +1 -1
  14. package/dist/components/Autocomplete/Autocomplete.js +2 -2
  15. package/dist/components/Button/Button.js +1 -1
  16. package/dist/components/Button/Button.module.css +1 -1
  17. package/dist/components/Card/Card.d.ts +1 -1
  18. package/dist/components/Card/Card.module.css +1 -1
  19. package/dist/components/Card/tests/Card.stories.d.ts +1 -1
  20. package/dist/components/Checkbox/Checkbox.module.css +1 -1
  21. package/dist/components/Divider/Divider.js +4 -3
  22. package/dist/components/Divider/Divider.module.css +1 -1
  23. package/dist/components/Divider/Divider.types.d.ts +3 -0
  24. package/dist/components/Divider/tests/Divider.stories.d.ts +1 -0
  25. package/dist/components/Divider/tests/Divider.stories.js +21 -0
  26. package/dist/components/DropdownMenu/DropdownMenu.types.d.ts +1 -1
  27. package/dist/components/FormControl/FormControl.context.d.ts +2 -1
  28. package/dist/components/Grid/Grid.d.ts +6 -0
  29. package/dist/components/Grid/Grid.js +46 -0
  30. package/dist/components/Grid/Grid.module.css +1 -0
  31. package/dist/components/Grid/Grid.types.d.ts +31 -0
  32. package/dist/components/Grid/Grid.types.js +1 -0
  33. package/dist/components/Grid/index.d.ts +2 -0
  34. package/dist/components/Grid/index.js +1 -0
  35. package/dist/components/Grid/tests/Grid.stories.d.ts +18 -0
  36. package/dist/components/Grid/tests/Grid.stories.js +170 -0
  37. package/dist/components/Icon/Icon.module.css +1 -1
  38. package/dist/components/Link/Link.d.ts +1 -1
  39. package/dist/components/Link/tests/Link.stories.d.ts +1 -1
  40. package/dist/components/Loader/Loader.module.css +1 -1
  41. package/dist/components/Loader/Loader.types.d.ts +1 -1
  42. package/dist/components/Loader/tests/Loader.stories.js +5 -3
  43. package/dist/components/Modal/Modal.js +7 -4
  44. package/dist/components/Modal/Modal.module.css +1 -1
  45. package/dist/components/Modal/Modal.types.d.ts +1 -1
  46. package/dist/components/Modal/tests/Modal.stories.d.ts +1 -0
  47. package/dist/components/Modal/tests/Modal.stories.js +28 -1
  48. package/dist/components/Overlay/Overlay.js +45 -27
  49. package/dist/components/Overlay/Overlay.module.css +1 -1
  50. package/dist/components/Overlay/Overlay.types.d.ts +1 -0
  51. package/dist/components/Overlay/tests/Overlay.stories.js +1 -1
  52. package/dist/components/Popover/Popover.js +2 -4
  53. package/dist/components/Popover/Popover.types.d.ts +1 -1
  54. package/dist/components/Radio/Radio.module.css +1 -1
  55. package/dist/components/Resizable/Resizable.module.css +1 -1
  56. package/dist/components/ScrollArea/ScrollArea.js +1 -1
  57. package/dist/components/Slider/Slider.module.css +1 -1
  58. package/dist/components/Slider/SliderControlled.js +2 -1
  59. package/dist/components/Switch/Switch.module.css +1 -1
  60. package/dist/components/Tabs/Tabs.d.ts +1 -1
  61. package/dist/components/Tabs/Tabs.module.css +1 -1
  62. package/dist/components/Tabs/TabsContext.d.ts +1 -1
  63. package/dist/components/Tabs/TabsItem.d.ts +1 -1
  64. package/dist/components/Tabs/TabsItem.js +2 -3
  65. package/dist/components/Tabs/TabsList.js +1 -1
  66. package/dist/components/Tabs/tests/Tabs.stories.d.ts +15 -13
  67. package/dist/components/Tabs/tests/Tabs.stories.js +71 -8
  68. package/dist/components/TextField/TextField.js +5 -1
  69. package/dist/components/TextField/TextField.module.css +1 -1
  70. package/dist/components/TextField/tests/TextField.stories.js +4 -0
  71. package/dist/components/Toast/ToastContainer.js +1 -2
  72. package/dist/components/Toast/ToastRegion.js +1 -1
  73. package/dist/components/Tooltip/Tooltip.js +1 -1
  74. package/dist/components/View/View.js +7 -3
  75. package/dist/components/View/View.module.css +1 -1
  76. package/dist/components/View/View.types.d.ts +2 -2
  77. package/dist/components/_private/Expandable/Expandable.js +9 -5
  78. package/dist/components/_private/Flyout/Flyout.module.css +1 -1
  79. package/dist/components/_private/Flyout/Flyout.types.d.ts +12 -3
  80. package/dist/components/_private/Flyout/FlyoutContent.js +1 -1
  81. package/dist/components/_private/Flyout/FlyoutControlled.js +34 -21
  82. package/dist/components/_private/Flyout/tests/Flyout.stories.d.ts +3 -1
  83. package/dist/components/_private/Flyout/tests/Flyout.stories.js +54 -32
  84. package/dist/components/_private/Flyout/useFlyout.d.ts +2 -1
  85. package/dist/components/_private/Flyout/useFlyout.js +45 -55
  86. package/dist/components/_private/Flyout/utilities/calculatePosition.js +16 -11
  87. package/dist/components/_private/Flyout/utilities/cooldown.d.ts +1 -1
  88. package/dist/components/_private/Flyout/utilities/cooldown.js +17 -5
  89. package/dist/components/_private/Flyout/utilities/getPositionFallbacks.d.ts +3 -0
  90. package/dist/components/_private/Flyout/utilities/getPositionFallbacks.js +39 -0
  91. package/dist/components/_private/Portal/Portal.js +3 -3
  92. package/dist/config/tailwind.d.ts +1 -1
  93. package/dist/constants/breakpoints.d.ts +6 -0
  94. package/dist/constants/breakpoints.js +5 -0
  95. package/dist/hooks/_private/useIsDismissible.js +7 -14
  96. package/dist/hooks/_private/useSingletonHotkeys.js +5 -3
  97. package/dist/hooks/_private/useSingletonKeyboardMode.js +1 -1
  98. package/dist/hooks/tests/useHotkeys.stories.js +3 -0
  99. package/dist/hooks/tests/useResponsiveClientValue.stories.d.ts +1 -2
  100. package/dist/hooks/tests/useResponsiveClientValue.stories.js +1 -2
  101. package/dist/hooks/useDrag.js +2 -1
  102. package/dist/hooks/useResponsiveClientValue.js +22 -11
  103. package/dist/hooks/useScrollLock.d.ts +4 -1
  104. package/dist/hooks/useScrollLock.js +14 -40
  105. package/dist/index.d.ts +2 -0
  106. package/dist/index.js +1 -0
  107. package/dist/styles/align/align.module.css +1 -0
  108. package/dist/styles/align/index.d.ts +3 -0
  109. package/dist/styles/align/index.js +10 -0
  110. package/dist/styles/justify/index.d.ts +3 -0
  111. package/dist/styles/justify/index.js +10 -0
  112. package/dist/styles/justify/justify.module.css +1 -0
  113. package/dist/styles/types.d.ts +2 -0
  114. package/dist/themes/_generator/definitions/reshaped.js +5 -4
  115. package/dist/themes/_generator/tokens/viewport/viewport.transforms.js +10 -1
  116. package/dist/themes/figma/theme.css +1 -1
  117. package/dist/themes/reshaped/theme.css +1 -1
  118. package/dist/themes/slate/theme.css +1 -1
  119. package/dist/utilities/a11y/TrapFocus.d.ts +1 -1
  120. package/dist/utilities/a11y/TrapFocus.js +1 -1
  121. package/dist/utilities/a11y/focus.js +1 -1
  122. package/dist/utilities/a11y/index.d.ts +4 -0
  123. package/dist/utilities/a11y/index.js +3 -0
  124. package/dist/utilities/css.d.ts +7 -0
  125. package/dist/utilities/css.js +18 -0
  126. package/dist/utilities/dom/flyout.d.ts +1 -0
  127. package/dist/utilities/{dom.js → dom/flyout.js} +1 -19
  128. package/dist/utilities/dom/index.d.ts +3 -0
  129. package/dist/utilities/dom/index.js +3 -0
  130. package/dist/utilities/dom/shadowDom.d.ts +1 -0
  131. package/dist/utilities/dom/shadowDom.js +4 -0
  132. package/dist/utilities/dom/userSelect.d.ts +2 -0
  133. package/dist/utilities/dom/userSelect.js +6 -0
  134. package/dist/utilities/platform.d.ts +1 -0
  135. package/dist/utilities/platform.js +15 -0
  136. package/dist/utilities/scroll/disable.d.ts +7 -0
  137. package/dist/utilities/scroll/disable.js +13 -0
  138. package/dist/utilities/scroll/helpers.d.ts +1 -0
  139. package/dist/utilities/scroll/helpers.js +17 -0
  140. package/dist/utilities/scroll/index.d.ts +2 -0
  141. package/dist/utilities/scroll/index.js +2 -0
  142. package/dist/utilities/scroll/lock.d.ts +5 -0
  143. package/dist/utilities/scroll/lock.js +24 -0
  144. package/dist/utilities/scroll/lockSafari.d.ts +2 -0
  145. package/dist/utilities/scroll/lockSafari.js +21 -0
  146. package/dist/utilities/scroll/lockStandard.d.ts +2 -0
  147. package/dist/utilities/scroll/lockStandard.js +17 -0
  148. package/dist/utilities/storybook/Placeholder.d.ts +1 -0
  149. package/dist/utilities/storybook/Placeholder.js +2 -2
  150. package/package.json +47 -50
  151. package/dist/utilities/dom.d.ts +0 -6
@@ -8,13 +8,14 @@ type PassedFlyoutOptions = {
8
8
  width?: T.Width;
9
9
  position?: T.Position;
10
10
  defaultActive?: boolean;
11
- forcePosition?: boolean;
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, getShadowRoot } from "../../../utilities/dom.js";
3
+ import { getClosestFlyoutTarget, getShadowRoot } from "../../../utilities/dom/index.js";
4
4
  import calculatePosition from "./utilities/calculatePosition.js";
5
- const topPos = ["top-start", "top", "top-end"];
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, forcePosition, width, container } = options;
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();
@@ -88,37 +72,30 @@ const flyout = (args) => {
88
72
  top: containerBounds.top + document.documentElement.scrollTop - containerParent.scrollTop,
89
73
  left: containerBounds.left + document.documentElement.scrollLeft - containerParent.scrollLeft,
90
74
  };
91
- let calculated = calculatePosition({ triggerBounds, flyoutBounds, scopeOffset, ...options });
92
- if (!fullyVisible(calculated) && !forcePosition) {
93
- const order = getPositionOrder(position);
94
- const mobileOrder = order.filter((position) => position === "top" || position === "bottom");
95
- const test = (testOrder, extraOptions = {}) => {
96
- const { fullWidth } = extraOptions;
97
- testOrder.some((currentPosition) => {
98
- const calculateOptions = {
99
- ...options,
100
- width: fullWidth ? "full" : options.width,
101
- position: currentPosition,
102
- };
103
- const tested = calculatePosition({
104
- triggerBounds,
105
- flyoutBounds,
106
- scopeOffset,
107
- ...calculateOptions,
108
- });
109
- if (fullyVisible(tested)) {
110
- calculated = tested;
111
- return true;
112
- }
113
- return false;
114
- });
115
- };
116
- test(order);
117
- if (!fullyVisible(calculated)) {
118
- 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);
119
92
  }
93
+ return validPosition;
94
+ });
95
+ if (!calculated) {
96
+ throw new Error(`Reshaped: Can't calculate styles for the ${position} position`);
120
97
  }
121
- targetClone.parentNode.removeChild(targetClone);
98
+ targetClone.parentNode?.removeChild(targetClone);
122
99
  return calculated;
123
100
  };
124
101
  const flyoutReducer = (state, action) => {
@@ -156,8 +133,13 @@ const flyoutReducer = (state, action) => {
156
133
  }
157
134
  };
158
135
  const useFlyout = (args) => {
159
- const { triggerElRef, flyoutElRef, triggerBoundsRef, ...options } = args;
160
- const { position: defaultPosition = "bottom", forcePosition, width, container } = options;
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(" ")]);
161
143
  const [isRTL] = useRTL();
162
144
  const [state, dispatch] = React.useReducer(flyoutReducer, {
163
145
  position: defaultPosition,
@@ -176,6 +158,9 @@ const useFlyout = (args) => {
176
158
  const remove = React.useCallback(() => {
177
159
  dispatch({ type: "remove" });
178
160
  }, []);
161
+ const handleFallback = React.useCallback((position) => {
162
+ lastUsedFallbackRef.current = position;
163
+ }, []);
179
164
  const updatePosition = React.useCallback((options) => {
180
165
  if (!triggerElRef.current || !flyoutElRef.current)
181
166
  return;
@@ -185,21 +170,26 @@ const useFlyout = (args) => {
185
170
  triggerBounds: triggerBoundsRef.current,
186
171
  width,
187
172
  position: defaultPosition,
188
- forcePosition,
173
+ fallbackPositions: cachedFallbackPositions,
174
+ lastUsedFallback: lastUsedFallbackRef.current,
175
+ onFallback: handleFallback,
189
176
  rtl: isRTL,
190
177
  container,
178
+ contentGap,
191
179
  });
192
180
  if (nextFlyoutData)
193
181
  dispatch({ type: "position", payload: { ...nextFlyoutData, sync: options?.sync } });
194
182
  }, [
195
183
  container,
196
184
  defaultPosition,
197
- forcePosition,
185
+ cachedFallbackPositions,
198
186
  isRTL,
199
187
  flyoutElRef,
200
188
  triggerElRef,
201
189
  triggerBoundsRef,
202
190
  width,
191
+ contentGap,
192
+ handleFallback,
203
193
  ]);
204
194
  React.useEffect(() => {
205
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 (width === "full" || width === "trigger") {
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, flyoutBounds.width) + triggerBounds.left;
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 - flyoutBounds.width;
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 - flyoutBounds.width;
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 - flyoutBounds.height;
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, flyoutBounds.height) + triggerBounds.top;
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 - flyoutBounds.height;
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(flyoutBounds.width);
86
- const height = Math.ceil(flyoutBounds.height);
87
- if (width === "full") {
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,5 +1,5 @@
1
1
  declare class Cooldown {
2
- status: "cold" | "warm" | "cooling";
2
+ status: "warming" | "warm" | "cooling" | "cold";
3
3
  timer?: ReturnType<typeof setTimeout>;
4
4
  warm: () => void;
5
5
  cool: () => void;
@@ -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 = "warm";
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
- const currentTimer = setTimeout(() => {
24
+ this.timer = setTimeout(() => {
11
25
  this.status = "cold";
12
- if (currentTimer === this.timer)
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,3 @@
1
+ import type * as T from "../Flyout.types";
2
+ declare const getPositionFallbacks: (position: T.Position, availableFallbacks?: T.Position[]) => T.Position[];
3
+ export default getPositionFallbacks;
@@ -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;
@@ -3,7 +3,6 @@ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-run
3
3
  import React from "react";
4
4
  import ReactDOM from "react-dom";
5
5
  import Theme from "../../Theme/index.js";
6
- import { getShadowRoot } from "../../../utilities/dom.js";
7
6
  import s from "./Portal.module.css";
8
7
  const PortalScopeContext = React.createContext({});
9
8
  export const usePortalScope = () => {
@@ -16,8 +15,9 @@ export const usePortalScope = () => {
16
15
  const Portal = (props) => {
17
16
  const { children, targetRef } = props;
18
17
  const rootRef = React.useRef(null);
19
- const shadowRoot = getShadowRoot(rootRef.current);
20
- const defaultTargetEl = shadowRoot ?? document.body;
18
+ const rootNode = rootRef.current?.getRootNode();
19
+ const isShadowDom = rootNode instanceof ShadowRoot;
20
+ const defaultTargetEl = isShadowDom ? rootNode : document.body;
21
21
  /**
22
22
  * Check for parent portal to render inside it
23
23
  * To avoid z-iondex issues
@@ -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" | "spacing" | "boxShadow" | "textColor" | "colors" | "screens", Record<string, string>>;
2
+ export declare const getTheme: (theme?: ThemeDefinition) => Record<"borderRadius" | "backgroundColor" | "borderColor" | "boxShadow" | "textColor" | "colors" | "spacing" | "screens", Record<string, string>>;
@@ -0,0 +1,6 @@
1
+ declare const _default: {
2
+ m: number;
3
+ l: number;
4
+ xl: number;
5
+ };
6
+ export default _default;
@@ -0,0 +1,5 @@
1
+ export default {
2
+ m: 660,
3
+ l: 900,
4
+ xl: 1280,
5
+ };
@@ -18,26 +18,19 @@ const removeFromQueue = (id) => {
18
18
  queue = {};
19
19
  };
20
20
  const addToQueue = (id, contentRef, triggerRef) => {
21
- const parentItem = latestId ? queue[latestId] : undefined;
22
- const insideParent = triggerRef?.current &&
23
- parentItem &&
24
- parentItem.contentRef.current?.contains(triggerRef.current);
25
- if (!insideParent && triggerRef && latestId) {
26
- removeFromQueue(latestId);
27
- }
28
21
  queue[id] = { parentId: latestId, triggerRef, contentRef };
29
22
  latestId = id;
30
23
  };
31
24
  const useIsDismissible = (active = false, contentRef, triggerRef) => {
32
25
  const id = useElementId();
33
- const isDismissible = React.useCallback(() => latestId === id, [id]);
26
+ const isDismissible = React.useCallback(() => {
27
+ return active ? latestId === id : true;
28
+ }, [id, active]);
34
29
  React.useEffect(() => {
35
- if (active) {
36
- addToQueue(id, contentRef, triggerRef);
37
- }
38
- else {
39
- removeFromQueue(id);
40
- }
30
+ if (!active)
31
+ return;
32
+ addToQueue(id, contentRef, triggerRef);
33
+ return () => removeFromQueue(id);
41
34
  }, [active, id, contentRef, triggerRef]);
42
35
  return isDismissible;
43
36
  };
@@ -70,10 +70,12 @@ 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 hotkeyControlModData = pressedFormattedKeys.includes("control") &&
75
- this.hotkeyMap[pressedId.replace("control", "mod")];
76
- const hotkeyMetaModData = pressedFormattedKeys.includes("meta") && this.hotkeyMap[pressedId.replace("meta", "mod")];
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;
@@ -1,5 +1,5 @@
1
1
  import React from "react";
2
- import { enableKeyboardMode, disableKeyboardMode } from "../../utilities/a11y/keyboardMode.js";
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"),
@@ -1,6 +1,5 @@
1
- import React from "react";
2
1
  declare const _default: {
3
2
  title: string;
4
3
  };
5
4
  export default _default;
6
- export declare const state: () => React.JSX.Element;
5
+ export declare const base: () => import("react").JSX.Element;
@@ -1,4 +1,3 @@
1
- import React from "react";
2
1
  import useResponsiveClientValue from "../useResponsiveClientValue.js";
3
2
  import View from "../../components/View/index.js";
4
3
  export default { title: "Hooks/useResponsiveClientValue" };
@@ -10,4 +9,4 @@ function Example() {
10
9
  });
11
10
  return <View width={25} height={25} backgroundColor={value}/>;
12
11
  }
13
- export const state = () => <Example />;
12
+ export const base = () => <Example />;
@@ -1,6 +1,7 @@
1
1
  "use client";
2
2
  import React from "react";
3
- import { disableUserSelect, enableUserSelect, disableScroll, enableScroll } from "../utilities/dom.js";
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";
@@ -2,21 +2,32 @@
2
2
  import React from "react";
3
3
  import { SingletonEnvironmentContext } from "./_private/useSingletonEnvironment.js";
4
4
  import useIsomorphicLayoutEffect from "./useIsomorphicLayoutEffect.js";
5
- const breakpoints = {
6
- m: 660,
7
- l: 900,
8
- xl: 1280,
9
- };
10
- const mediaQueries = {
11
- s: `(max-width: ${breakpoints.m - 1}px)`,
12
- m: `(min-width: ${breakpoints.m}px) and (max-width: ${breakpoints.l - 1}px)`,
13
- l: `(min-width: ${breakpoints.l}px) and (max-width: ${breakpoints.xl - 1}px)`,
14
- xl: `(min-width: ${breakpoints.xl}px)`,
15
- };
5
+ import defaultBreakpoints from "../constants/breakpoints.js";
16
6
  const useResponsiveClientValue = (value) => {
17
7
  const { defaultViewport } = React.useContext(SingletonEnvironmentContext);
18
8
  const [viewport, setViewport] = React.useState(defaultViewport);
19
9
  useIsomorphicLayoutEffect(() => {
10
+ const rootThemeEl = document.querySelector("[data-rs-theme]");
11
+ const rootStyle = rootThemeEl && window.getComputedStyle(rootThemeEl);
12
+ /**
13
+ * We generate variables for the viewport breakpoints in the themes
14
+ * We use them here in case they're custom and fallback to the default values
15
+ * in case there is no SSR passing the data-rs-theme attribute
16
+ */
17
+ const breakpoints = {
18
+ m: (rootStyle && Number(rootStyle.getPropertyValue("--rs-viewport-m-min"))) ||
19
+ defaultBreakpoints.m,
20
+ l: (rootStyle && Number(rootStyle.getPropertyValue("--rs-viewport-l-min"))) ||
21
+ defaultBreakpoints.l,
22
+ xl: (rootStyle && Number(rootStyle.getPropertyValue("--rs-viewport-xl-min"))) ||
23
+ defaultBreakpoints.xl,
24
+ };
25
+ const mediaQueries = {
26
+ s: `(max-width: ${breakpoints.m - 1}px)`,
27
+ m: `(min-width: ${breakpoints.m}px) and (max-width: ${breakpoints.l - 1}px)`,
28
+ l: `(min-width: ${breakpoints.l}px) and (max-width: ${breakpoints.xl - 1}px)`,
29
+ xl: `(min-width: ${breakpoints.xl}px)`,
30
+ };
20
31
  const viewports = Object.keys(mediaQueries);
21
32
  const matchers = viewports.map((viewport) => {
22
33
  const mq = window.matchMedia(mediaQueries[viewport]);
@@ -1,4 +1,7 @@
1
- declare const useScrollLock: () => {
1
+ import React from "react";
2
+ declare const useScrollLock: (props?: {
3
+ containerRef?: React.RefObject<HTMLElement>;
4
+ }) => {
2
5
  scrollLocked: boolean;
3
6
  lockScroll: () => void;
4
7
  unlockScroll: () => void;
@@ -1,45 +1,19 @@
1
1
  "use client";
2
2
  import React from "react";
3
- const getScrollbarWidth = (() => {
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
- })();
20
- const useScrollLock = () => {
3
+ import { lockScroll, unlockScroll } from "../utilities/scroll/index.js";
4
+ const useScrollLock = (props) => {
5
+ const { containerRef } = props || {};
21
6
  const [locked, setLocked] = React.useState(false);
22
- const overflowStyleRef = React.useRef();
23
- const isOverflowingRef = React.useRef(false);
24
- const lockScroll = React.useCallback(() => {
25
- const targetEl = document.body;
26
- const rect = targetEl.getBoundingClientRect();
27
- isOverflowingRef.current = rect.left + rect.right < window.innerWidth;
28
- overflowStyleRef.current = targetEl.style.overflow;
29
- targetEl.style.overflow = "hidden";
30
- if (isOverflowingRef.current) {
31
- const scrollBarWidth = getScrollbarWidth();
32
- targetEl.style.paddingRight = `${scrollBarWidth}px`;
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 };
7
+ const handleLockScroll = React.useCallback(() => {
8
+ lockScroll({ containerEl: containerRef?.current, cb: () => setLocked(true) });
9
+ }, [containerRef]);
10
+ const handleUnlockScroll = React.useCallback(() => {
11
+ unlockScroll(() => setLocked(false));
12
+ }, []);
13
+ return React.useMemo(() => ({
14
+ scrollLocked: locked,
15
+ lockScroll: handleLockScroll,
16
+ unlockScroll: handleUnlockScroll,
17
+ }), [locked, handleLockScroll, handleUnlockScroll]);
44
18
  };
45
19
  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}}
@@ -0,0 +1,3 @@
1
+ import * as T from "../types";
2
+ declare const getAlignStyles: T.StaticStyleUtility<T.Align>;
3
+ export default getAlignStyles;