reshaped 3.2.0-canary.6 → 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 (143) hide show
  1. package/CHANGELOG.md +25 -2
  2. package/dist/bundle.css +1 -1
  3. package/dist/bundle.d.ts +2 -0
  4. package/dist/bundle.js +11 -12
  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/Popover/Popover.js +2 -4
  52. package/dist/components/Popover/Popover.types.d.ts +1 -1
  53. package/dist/components/Radio/Radio.module.css +1 -1
  54. package/dist/components/Resizable/Resizable.module.css +1 -1
  55. package/dist/components/ScrollArea/ScrollArea.js +1 -1
  56. package/dist/components/Slider/Slider.module.css +1 -1
  57. package/dist/components/Slider/SliderControlled.js +2 -1
  58. package/dist/components/Switch/Switch.module.css +1 -1
  59. package/dist/components/Tabs/Tabs.d.ts +1 -1
  60. package/dist/components/Tabs/Tabs.module.css +1 -1
  61. package/dist/components/Tabs/TabsContext.d.ts +1 -1
  62. package/dist/components/Tabs/TabsItem.d.ts +1 -1
  63. package/dist/components/Tabs/TabsItem.js +2 -3
  64. package/dist/components/Tabs/TabsList.js +1 -1
  65. package/dist/components/Tabs/tests/Tabs.stories.d.ts +15 -13
  66. package/dist/components/Tabs/tests/Tabs.stories.js +71 -8
  67. package/dist/components/TextField/TextField.js +5 -1
  68. package/dist/components/TextField/TextField.module.css +1 -1
  69. package/dist/components/TextField/tests/TextField.stories.js +4 -0
  70. package/dist/components/Toast/ToastContainer.js +1 -2
  71. package/dist/components/Toast/ToastRegion.js +1 -1
  72. package/dist/components/View/View.js +7 -3
  73. package/dist/components/View/View.module.css +1 -1
  74. package/dist/components/View/View.types.d.ts +2 -2
  75. package/dist/components/_private/Flyout/Flyout.module.css +1 -1
  76. package/dist/components/_private/Flyout/Flyout.types.d.ts +11 -3
  77. package/dist/components/_private/Flyout/FlyoutContent.js +1 -1
  78. package/dist/components/_private/Flyout/FlyoutControlled.js +25 -15
  79. package/dist/components/_private/Flyout/tests/Flyout.stories.d.ts +3 -1
  80. package/dist/components/_private/Flyout/tests/Flyout.stories.js +54 -32
  81. package/dist/components/_private/Flyout/useFlyout.d.ts +2 -1
  82. package/dist/components/_private/Flyout/useFlyout.js +45 -55
  83. package/dist/components/_private/Flyout/utilities/calculatePosition.js +16 -11
  84. package/dist/components/_private/Flyout/utilities/getPositionFallbacks.d.ts +3 -0
  85. package/dist/components/_private/Flyout/utilities/getPositionFallbacks.js +39 -0
  86. package/dist/config/tailwind.d.ts +1 -1
  87. package/dist/constants/breakpoints.d.ts +6 -0
  88. package/dist/constants/breakpoints.js +5 -0
  89. package/dist/hooks/_private/useIsDismissible.js +7 -14
  90. package/dist/hooks/_private/useSingletonKeyboardMode.js +1 -1
  91. package/dist/hooks/tests/useResponsiveClientValue.stories.d.ts +1 -2
  92. package/dist/hooks/tests/useResponsiveClientValue.stories.js +1 -2
  93. package/dist/hooks/useDrag.js +2 -1
  94. package/dist/hooks/useResponsiveClientValue.js +22 -11
  95. package/dist/hooks/useScrollLock.d.ts +4 -1
  96. package/dist/hooks/useScrollLock.js +14 -40
  97. package/dist/index.d.ts +2 -0
  98. package/dist/index.js +1 -0
  99. package/dist/styles/align/align.module.css +1 -0
  100. package/dist/styles/align/index.d.ts +3 -0
  101. package/dist/styles/align/index.js +10 -0
  102. package/dist/styles/justify/index.d.ts +3 -0
  103. package/dist/styles/justify/index.js +10 -0
  104. package/dist/styles/justify/justify.module.css +1 -0
  105. package/dist/styles/types.d.ts +2 -0
  106. package/dist/themes/_generator/definitions/reshaped.js +5 -4
  107. package/dist/themes/_generator/tokens/viewport/viewport.transforms.js +10 -1
  108. package/dist/themes/figma/theme.css +1 -1
  109. package/dist/themes/reshaped/theme.css +1 -1
  110. package/dist/themes/slate/theme.css +1 -1
  111. package/dist/utilities/a11y/TrapFocus.d.ts +1 -1
  112. package/dist/utilities/a11y/TrapFocus.js +1 -1
  113. package/dist/utilities/a11y/focus.js +1 -1
  114. package/dist/utilities/a11y/index.d.ts +4 -0
  115. package/dist/utilities/a11y/index.js +3 -0
  116. package/dist/utilities/css.d.ts +7 -0
  117. package/dist/utilities/css.js +18 -0
  118. package/dist/utilities/dom/flyout.d.ts +1 -0
  119. package/dist/utilities/{dom.js → dom/flyout.js} +1 -19
  120. package/dist/utilities/dom/index.d.ts +3 -0
  121. package/dist/utilities/dom/index.js +3 -0
  122. package/dist/utilities/dom/shadowDom.d.ts +1 -0
  123. package/dist/utilities/dom/shadowDom.js +4 -0
  124. package/dist/utilities/dom/userSelect.d.ts +2 -0
  125. package/dist/utilities/dom/userSelect.js +6 -0
  126. package/dist/utilities/platform.d.ts +1 -0
  127. package/dist/utilities/platform.js +15 -0
  128. package/dist/utilities/scroll/disable.d.ts +7 -0
  129. package/dist/utilities/scroll/disable.js +13 -0
  130. package/dist/utilities/scroll/helpers.d.ts +1 -0
  131. package/dist/utilities/scroll/helpers.js +17 -0
  132. package/dist/utilities/scroll/index.d.ts +2 -0
  133. package/dist/utilities/scroll/index.js +2 -0
  134. package/dist/utilities/scroll/lock.d.ts +5 -0
  135. package/dist/utilities/scroll/lock.js +24 -0
  136. package/dist/utilities/scroll/lockSafari.d.ts +2 -0
  137. package/dist/utilities/scroll/lockSafari.js +21 -0
  138. package/dist/utilities/scroll/lockStandard.d.ts +2 -0
  139. package/dist/utilities/scroll/lockStandard.js +17 -0
  140. package/dist/utilities/storybook/Placeholder.d.ts +1 -0
  141. package/dist/utilities/storybook/Placeholder.js +2 -2
  142. package/package.json +45 -48
  143. package/dist/utilities/dom.d.ts +0 -6
@@ -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
  }
@@ -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;
@@ -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
  };
@@ -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) => {
@@ -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;
@@ -0,0 +1,10 @@
1
+ import { responsiveClassNames } from "../../utilities/helpers.js";
2
+ import s from "./align.module.css";
3
+ const getAlignStyles = (value) => {
4
+ if (!value)
5
+ return null;
6
+ return {
7
+ classNames: responsiveClassNames(s, "--align", value),
8
+ };
9
+ };
10
+ export default getAlignStyles;
@@ -0,0 +1,3 @@
1
+ import * as T from "../types";
2
+ declare const getJustifyStyles: T.StaticStyleUtility<T.Justify>;
3
+ export default getJustifyStyles;
@@ -0,0 +1,10 @@
1
+ import { responsiveClassNames } from "../../utilities/helpers.js";
2
+ import s from "./justify.module.css";
3
+ const getJustifyStyles = (value) => {
4
+ if (!value)
5
+ return null;
6
+ return {
7
+ classNames: responsiveClassNames(s, "--justify", value),
8
+ };
9
+ };
10
+ export default getJustifyStyles;
@@ -0,0 +1 @@
1
+ .--justify-start{justify-content:flex-start}.--justify-end{justify-content:flex-end}.--justify-center{justify-content:center}.--justify-space-between{justify-content:space-between}@media (--rs-viewport-m ){.--justify-start--m{justify-content:flex-start}.--justify-end--m{justify-content:flex-end}.--justify-center--m{justify-content:center}.--justify-space-between--m{justify-content:space-between}}@media (--rs-viewport-l ){.--justify-start--l{justify-content:flex-start}.--justify-end--l{justify-content:flex-end}.--justify-center--l{justify-content:center}.--justify-space-between--l{justify-content:space-between}}@media (--rs-viewport-xl ){.--justify-start--xl{justify-content:flex-start}.--justify-end--xl{justify-content:flex-end}.--justify-center--xl{justify-content:center}.--justify-space-between--xl{justify-content:space-between}}
@@ -1,6 +1,8 @@
1
1
  import React from "react";
2
2
  import * as G from "../types/global";
3
3
  export type TextAlign = "start" | "center" | "end";
4
+ export type Justify = "start" | "center" | "end" | "space-between";
5
+ export type Align = "start" | "center" | "end" | "stretch" | "baseline";
4
6
  export type Radius = "small" | "medium" | "large" | "circular" | "none";
5
7
  export type Position = "relative" | "absolute" | "fixed" | "sticky" | "static";
6
8
  export type BorderColor = "neutral" | "neutral-faded" | "critical" | "critical-faded" | "warning" | "warning-faded" | "positive" | "positive-faded" | "primary" | "primary-faded" | "disabled" | "brand" | "transparent";
@@ -1,3 +1,4 @@
1
+ import breakpoints from "../../../constants/breakpoints.js";
1
2
  const theme = {
2
3
  fontFamily: {
3
4
  title: {
@@ -123,7 +124,7 @@ const theme = {
123
124
  borderPositiveFaded: { hex: "#d0f3e2", hexDark: "#293b2f" },
124
125
  foregroundPositive: { hex: "#0d7544", hexDark: "#18ab66" },
125
126
  backgroundNeutral: { hex: "#dfe2ea", hexDark: "#494f60" },
126
- backgroundNeutralFaded: { hex: "#f1f2f6", hexDark: "#222835" },
127
+ backgroundNeutralFaded: { hex: "#f3f3f5", hexDark: "#222835" },
127
128
  borderNeutral: { hex: "#0000001f", hexDark: "#ffffff24" },
128
129
  borderNeutralFaded: { hex: "#0000001a", hexDark: "#ffffff17" },
129
130
  foregroundNeutral: { hex: "#14181f", hexDark: "#eff1f5" },
@@ -187,9 +188,9 @@ const theme = {
187
188
  ],
188
189
  },
189
190
  viewport: {
190
- m: { minPx: 660 },
191
- l: { minPx: 900 },
192
- xl: { minPx: 1280 },
191
+ m: { minPx: breakpoints.m },
192
+ l: { minPx: breakpoints.l },
193
+ xl: { minPx: breakpoints.xl },
193
194
  },
194
195
  };
195
196
  export default theme;
@@ -4,7 +4,7 @@ const transformedToken = (name, token) => {
4
4
  value.push(`(min-width: ${token.minPx}px)`);
5
5
  if (token.maxPx)
6
6
  value.push(`(max-width: ${token.maxPx}px)`);
7
- return [
7
+ const result = [
8
8
  {
9
9
  name,
10
10
  tokenType: "viewport",
@@ -12,5 +12,14 @@ const transformedToken = (name, token) => {
12
12
  value: value.join(" and "),
13
13
  },
14
14
  ];
15
+ if (token.minPx) {
16
+ result.push({
17
+ name: `${name}-min`,
18
+ tokenType: "viewport",
19
+ type: "variable",
20
+ value: token.minPx.toString(),
21
+ });
22
+ }
23
+ return result;
15
24
  };
16
25
  export default transformedToken;