@os-design/core 1.0.276 → 1.0.278

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@os-design/core",
3
- "version": "1.0.276",
3
+ "version": "1.0.278",
4
4
  "license": "UNLICENSED",
5
5
  "repository": "git@gitlab.com:os-team/libs/os-design.git",
6
6
  "type": "module",
@@ -39,7 +39,7 @@
39
39
  "@os-design/styles": "^1.0.65",
40
40
  "@os-design/theming": "^1.0.61",
41
41
  "@os-design/time-picker-utils": "^1.0.23",
42
- "@os-design/utils": "^1.0.86",
42
+ "@os-design/utils": "^1.0.87",
43
43
  "facepaint": "^1.2.1",
44
44
  "react-focus-lock": "^2.13.2",
45
45
  "react-window": "^1.8.10"
@@ -58,5 +58,5 @@
58
58
  "react": "18",
59
59
  "react-dom": "18"
60
60
  },
61
- "gitHead": "59dea037dfe43e783a9e6f5e3d45788fdcc0ce97"
61
+ "gitHead": "d7b2d28720454ff073af76be76e9dedd1486fe52"
62
62
  }
@@ -24,20 +24,22 @@ import {
24
24
  import usePopoverPosition, {
25
25
  type Placement,
26
26
  type Rect,
27
+ type ScrollableRect,
28
+ type Size,
27
29
  } from './utils/usePopoverPosition.js';
28
30
 
29
31
  type JsxDivProps = Omit<JSX.IntrinsicElements['div'], 'ref'>;
30
32
  export interface PopoverProps extends JsxDivProps, WithSize {
31
- /**
32
- * The element next to which the popover appears.
33
- * @default undefined
34
- */
35
- trigger?: RefObject<Element> | Rect;
36
33
  /**
37
34
  * Where the popover will be rendered.
38
35
  * @default document.body
39
36
  */
40
- container?: Element | RefObject<Element>;
37
+ container?: RefObject<Element> | Element | null;
38
+ /**
39
+ * The element next to which the popover appears.
40
+ * @default undefined
41
+ */
42
+ trigger?: RefObject<Element> | Element | Rect | null;
41
43
  /**
42
44
  * On which side of the element the popover will appear.
43
45
  * @default top
@@ -75,6 +77,18 @@ const fadeOut = keyframes`
75
77
  to { opacity: 0; }
76
78
  `;
77
79
 
80
+ const widthStyles = (p) =>
81
+ p.width !== null &&
82
+ css`
83
+ width: ${p.width}px;
84
+ `;
85
+
86
+ const heightStyles = (p) =>
87
+ p.height !== null &&
88
+ css`
89
+ height: ${p.height}px;
90
+ `;
91
+
78
92
  const visibleStyles = (p) =>
79
93
  p.visible &&
80
94
  css`
@@ -90,6 +104,8 @@ const invisibleStyles = (p) =>
90
104
  interface ContainerProps extends Pick<PopoverProps, 'visible' | 'size'> {
91
105
  top: number;
92
106
  left: number;
107
+ width: number | null;
108
+ height: number | null;
93
109
  }
94
110
  const Container = styled(
95
111
  'div',
@@ -106,26 +122,21 @@ const Container = styled(
106
122
  box-shadow: 0 0.15em 0.8em ${(p) => clr(p.theme.popoverColorBoxShadow)};
107
123
  z-index: 1000; // Greater than the z-index of the Drawer
108
124
 
125
+ ${widthStyles};
126
+ ${heightStyles};
109
127
  ${visibleStyles};
110
128
  ${invisibleStyles};
111
129
  ${sizeStyles};
112
130
  `;
113
131
 
114
- const emptyRect: Rect = {
115
- top: 0,
116
- left: 0,
117
- width: 0,
118
- height: 0,
119
- };
120
-
121
132
  /**
122
133
  * The pop-up window located next to the element.
123
134
  */
124
135
  const Popover = forwardRef<HTMLDivElement, PopoverProps>(
125
136
  (
126
137
  {
127
- trigger,
128
138
  container,
139
+ trigger,
129
140
  placement = 'top',
130
141
  gap = 0.2,
131
142
  flip = true,
@@ -138,64 +149,144 @@ const Popover = forwardRef<HTMLDivElement, PopoverProps>(
138
149
  ref
139
150
  ) => {
140
151
  const [popoverRef, mergedPopoverRef] = useForwardedRef(ref);
141
- const [popoverRect, setPopoverRect] = useState(emptyRect);
142
- const [triggerRect, setTriggerRect] = useState(emptyRect);
152
+ const [popoverSize, setPopoverSize] = useState<Size | null>(null);
153
+ const [containerRect, setContainerRect] = useState<ScrollableRect | null>(
154
+ null
155
+ );
156
+ const [triggerRect, setTriggerRect] = useState<Rect | null>(null);
157
+
143
158
  const { theme } = useTheme();
144
159
  const mounted = useClosable(visible, theme.transitionDelay);
145
160
 
146
- // Init the rect of the popover and update it when the popover size changes
147
- const popoverResizeListener = useCallback(() => {
148
- if (!popoverRef.current) return;
149
- setPopoverRect(popoverRef.current.getBoundingClientRect());
150
- }, [popoverRef]);
151
- useResizeObserver(
152
- popoverRef.current as HTMLDivElement,
153
- popoverResizeListener
154
- );
155
-
161
+ // Save the popover size when it has been rendered
156
162
  const measuredPopoverRef = useCallback<RefCallback<HTMLDivElement>>(
157
163
  (node) => {
158
164
  if (node === null) return;
159
- setPopoverRect(node.getBoundingClientRect());
165
+ const rect = node.getBoundingClientRect();
166
+ setPopoverSize({
167
+ width: rect.width,
168
+ height: rect.height,
169
+ });
160
170
  mergedPopoverRef(node);
161
171
  },
162
172
  [mergedPopoverRef]
163
173
  );
164
174
 
165
- // Init the rect of the trigger and update it when the window was resized
166
- // or scrolled
167
- const triggerResizeListener = useCallback(() => {
175
+ // Update the popover size when it has been resized
176
+ const popoverResizeListener = useCallback(() => {
168
177
  window.requestAnimationFrame(() => {
169
- if (!trigger) return;
170
- const { current } = trigger as RefObject<Element>;
171
- if (!current) return;
172
- setTriggerRect(current.getBoundingClientRect());
178
+ if (!popoverRef.current) return;
179
+ const rect = popoverRef.current.getBoundingClientRect();
180
+ setPopoverSize({
181
+ width: rect.width,
182
+ height: rect.height,
183
+ });
173
184
  });
174
- }, [trigger]);
175
- useBrowserLayoutEffect(() => {
185
+ }, [popoverRef]);
186
+ useResizeObserver(popoverRef.current, popoverResizeListener);
187
+
188
+ const containerRef = useMemo<RefObject<Element>>(() => {
189
+ if (typeof window === 'undefined') {
190
+ return { current: null };
191
+ }
192
+ if (!container) {
193
+ return { current: document.body };
194
+ }
195
+ return 'current' in container ? container : { current: container };
196
+ }, [container]);
197
+
198
+ // Update the container size and its scroll offsets initially and when it has been changed.
199
+ // Must depend on `visible` to update the position when the popover is visible.
200
+ const containerListener = useCallback(() => {
176
201
  if (!visible) return;
177
- triggerResizeListener();
178
- }, [triggerResizeListener, visible]);
179
- useResizeObserver(trigger as never, triggerResizeListener);
202
+ window.requestAnimationFrame(() => {
203
+ if (containerRef.current) {
204
+ if (containerRef.current === document.body) {
205
+ setContainerRect({
206
+ top: 0,
207
+ left: 0,
208
+ width: window.innerWidth,
209
+ height: window.innerHeight,
210
+ scrollTop: window.scrollY,
211
+ scrollLeft: window.scrollX,
212
+ });
213
+ } else {
214
+ const rect = containerRef.current.getBoundingClientRect();
215
+ setContainerRect({
216
+ top: rect.top,
217
+ left: rect.left,
218
+ width: rect.width,
219
+ height: rect.height,
220
+ scrollTop: containerRef.current.scrollTop,
221
+ scrollLeft: containerRef.current.scrollLeft,
222
+ });
223
+ }
224
+ }
225
+ });
226
+ }, [containerRef, visible]);
227
+ useBrowserLayoutEffect(() => {
228
+ containerListener();
229
+ }, [containerListener]);
180
230
  useEvent(
181
- (typeof window === 'undefined' ? null : window) as never,
231
+ typeof window !== 'undefined' && containerRef.current === document.body
232
+ ? window
233
+ : null,
182
234
  'resize',
183
- triggerResizeListener
235
+ containerListener
236
+ );
237
+ useResizeObserver(
238
+ typeof window !== 'undefined' && containerRef.current !== document.body
239
+ ? containerRef.current
240
+ : null,
241
+ containerListener
242
+ );
243
+ useEvent(
244
+ typeof window !== 'undefined'
245
+ ? containerRef.current === document.body
246
+ ? document
247
+ : containerRef.current
248
+ : null,
249
+ 'scroll',
250
+ containerListener
184
251
  );
185
- useEvent(document, 'scroll', triggerResizeListener);
186
252
 
253
+ const triggerRef = useMemo<RefObject<Element>>(() => {
254
+ if (!trigger || 'top' in trigger) {
255
+ return { current: null };
256
+ }
257
+ return 'current' in trigger ? trigger : { current: trigger };
258
+ }, [trigger]);
259
+
260
+ // Update the trigger size and its position initially and when it has been changed
261
+ const triggerListener = useCallback(() => {
262
+ if (!containerRect) return;
263
+ window.requestAnimationFrame(() => {
264
+ if (triggerRef.current) {
265
+ const triggerRect = triggerRef.current.getBoundingClientRect();
266
+ setTriggerRect({
267
+ top: triggerRect.top - containerRect.top,
268
+ left: triggerRect.left - containerRect.left,
269
+ width: triggerRect.width,
270
+ height: triggerRect.height,
271
+ });
272
+ }
273
+ });
274
+ }, [containerRect, triggerRef]);
275
+ useBrowserLayoutEffect(() => {
276
+ triggerListener();
277
+ }, [triggerListener]);
278
+ useResizeObserver(triggerRef.current, triggerListener);
187
279
  useEffect(() => {
188
- if (!trigger || (trigger as RefObject<Element>).current !== undefined)
189
- return;
190
- setTriggerRect(trigger as Rect);
280
+ if (trigger && 'top' in trigger) {
281
+ setTriggerRect(trigger);
282
+ }
191
283
  }, [trigger]);
192
284
 
285
+ // Set the aria tags to support accessibility features
193
286
  const popoverId = useMemo(
194
287
  () => id || `popover-${Math.random().toString(36).slice(2, 11)}`,
195
288
  [id]
196
289
  );
197
-
198
- // Set the aria tags to support accessibility features
199
290
  useBrowserLayoutEffect(() => {
200
291
  if (!trigger) return;
201
292
  const { current } = trigger as RefObject<Element>;
@@ -214,23 +305,40 @@ const Popover = forwardRef<HTMLDivElement, PopoverProps>(
214
305
 
215
306
  // Get the popover coordinates
216
307
  const { top, left } = usePopoverPosition({
217
- elementRect: triggerRect,
218
- popoverRect,
308
+ popoverSize,
309
+ containerRect,
310
+ triggerRect,
219
311
  placement,
220
312
  gap,
221
313
  flip,
222
314
  });
223
315
 
316
+ const width = useMemo(() => {
317
+ if (!triggerRect) return null;
318
+ return placement === 'top-full' || placement === 'bottom-full'
319
+ ? triggerRect.width
320
+ : null;
321
+ }, [placement, triggerRect]);
322
+
323
+ const height = useMemo(() => {
324
+ if (!triggerRect) return null;
325
+ return placement === 'left-full' || placement === 'right-full'
326
+ ? triggerRect.width
327
+ : null;
328
+ }, [placement, triggerRect]);
329
+
224
330
  // Close the popover when the user clicks outside of it
225
331
  useClickOutside(popoverRef, onClose);
226
332
 
227
333
  if (!mounted) return null;
228
334
 
229
335
  return (
230
- <Portal container={container}>
336
+ <Portal container={containerRef.current}>
231
337
  <Container
232
338
  top={top}
233
339
  left={left}
340
+ width={width}
341
+ height={height}
234
342
  visible={visible}
235
343
  id={popoverId}
236
344
  role='dialog'
@@ -2,27 +2,39 @@ import { useFontSize } from '@os-design/utils';
2
2
  import { useMemo } from 'react';
3
3
 
4
4
  type Side = 'top' | 'left' | 'right' | 'bottom';
5
- type Alignment = 'start' | 'end';
5
+ type Alignment = 'start' | 'end' | 'full';
6
6
  type AlignedPlacement = `${Side}-${Alignment}`;
7
7
 
8
8
  export type Placement = Side | AlignedPlacement;
9
9
 
10
- export interface Rect {
11
- top: number;
12
- left: number;
10
+ export interface Size {
13
11
  width: number;
14
12
  height: number;
15
13
  }
16
14
 
15
+ export interface Rect extends Size {
16
+ top: number;
17
+ left: number;
18
+ }
19
+
20
+ export interface ScrollableRect extends Rect {
21
+ scrollTop: number;
22
+ scrollLeft: number;
23
+ }
24
+
17
25
  interface UsePopoverPositionProps {
18
26
  /**
19
- * The rect of the element.
27
+ * The size of the popover.
20
28
  */
21
- elementRect: Rect;
29
+ popoverSize: Size | null;
22
30
  /**
23
- * The rect of the popover.
31
+ * The rect of the container.
24
32
  */
25
- popoverRect: Rect;
33
+ containerRect: ScrollableRect | null;
34
+ /**
35
+ * The rect of the element.
36
+ */
37
+ triggerRect: Rect | null;
26
38
  /**
27
39
  * On which side of the element the popover will appear.
28
40
  * @default top
@@ -41,8 +53,9 @@ interface UsePopoverPositionProps {
41
53
  }
42
54
 
43
55
  interface PopoverPositionGetterOptions {
44
- elementRect: Rect;
45
- popoverRect: Rect;
56
+ popoverSize: Size;
57
+ containerRect: ScrollableRect;
58
+ triggerRect: Rect;
46
59
  gap: number;
47
60
  flip: boolean;
48
61
  }
@@ -53,46 +66,45 @@ type PopoverPositionGetterFn = (
53
66
  type PopoverPositionGetters = Record<PositionKeys, PopoverPositionGetterFn>;
54
67
  type FitToWindow = Pick<
55
68
  PopoverPositionGetterOptions,
56
- 'elementRect' | 'popoverRect'
69
+ 'popoverSize' | 'containerRect' | 'triggerRect'
57
70
  >;
58
71
 
59
72
  const popoverPositionGetters = (
60
73
  rectKey: 'top' | 'left'
61
74
  ): PopoverPositionGetters => {
62
75
  const sizeKey = rectKey === 'top' ? 'height' : 'width';
63
- const windowSizeKey = rectKey === 'top' ? 'innerHeight' : 'innerWidth';
64
- const windowOffsetKey = rectKey === 'top' ? 'pageYOffset' : 'pageXOffset';
76
+ const scrollOffsetKey = rectKey === 'top' ? 'scrollTop' : 'scrollLeft';
65
77
 
66
- const fitToWindow = (
78
+ const fitToContainer = (
67
79
  start: number,
68
- { elementRect, popoverRect }: FitToWindow
80
+ { popoverSize, containerRect, triggerRect }: FitToWindow
69
81
  ): number => {
70
82
  let popoverStart = start;
71
- const windowStart = window[windowOffsetKey];
72
- const windowEnd = windowStart + window[windowSizeKey];
73
- const elementStart = windowStart + elementRect[rectKey];
74
- const elementEnd = elementStart + elementRect[sizeKey];
75
- const popoverEnd = popoverStart + popoverRect[sizeKey];
76
-
77
- // Fit the popover to the end of the window
78
- if (popoverEnd > windowEnd) {
79
- if (elementEnd < windowEnd)
80
- popoverStart = windowEnd - popoverRect[sizeKey];
81
- else if (popoverRect[sizeKey] > elementRect[sizeKey])
82
- popoverStart = elementEnd - popoverRect[sizeKey];
83
- else if (windowEnd - elementStart > popoverRect[sizeKey])
84
- popoverStart = windowEnd - popoverRect[sizeKey];
85
- else popoverStart = elementStart;
83
+ const containerStart = containerRect[scrollOffsetKey];
84
+ const containerEnd = containerStart + containerRect[sizeKey];
85
+ const triggerStart = containerStart + triggerRect[rectKey];
86
+ const triggerEnd = triggerStart + triggerRect[sizeKey];
87
+ const popoverEnd = popoverStart + popoverSize[sizeKey];
88
+
89
+ // Fit the popover to the end of the container
90
+ if (popoverEnd > containerEnd) {
91
+ if (triggerEnd < containerEnd)
92
+ popoverStart = containerEnd - popoverSize[sizeKey];
93
+ else if (popoverSize[sizeKey] > triggerRect[sizeKey])
94
+ popoverStart = triggerEnd - popoverSize[sizeKey];
95
+ else if (containerEnd - triggerStart > popoverSize[sizeKey])
96
+ popoverStart = containerEnd - popoverSize[sizeKey];
97
+ else popoverStart = triggerStart;
86
98
  }
87
99
 
88
- // Fit the popover to the beginning of the window
89
- if (popoverStart < windowStart) {
90
- if (elementStart > windowStart) popoverStart = windowStart;
91
- else if (popoverRect[sizeKey] > elementRect[sizeKey])
92
- popoverStart = elementStart;
93
- else if (elementEnd - windowStart > popoverRect[sizeKey])
94
- popoverStart = windowStart;
95
- else popoverStart = elementEnd - popoverRect[sizeKey];
100
+ // Fit the popover to the beginning of the container
101
+ if (popoverStart < containerStart) {
102
+ if (triggerStart > containerStart) popoverStart = containerStart;
103
+ else if (popoverSize[sizeKey] > triggerRect[sizeKey])
104
+ popoverStart = triggerStart;
105
+ else if (triggerEnd - containerStart > popoverSize[sizeKey])
106
+ popoverStart = containerStart;
107
+ else popoverStart = triggerEnd - popoverSize[sizeKey];
96
108
  }
97
109
 
98
110
  return popoverStart;
@@ -100,56 +112,69 @@ const popoverPositionGetters = (
100
112
 
101
113
  return {
102
114
  before(options) {
103
- const { elementRect, popoverRect, gap, flip } = options;
104
- const windowStart = window[windowOffsetKey];
105
- const windowEnd = windowStart + window[windowSizeKey];
106
- const elementStart = windowStart + elementRect[rectKey];
107
- const popoverStart = elementStart - popoverRect[sizeKey] - gap;
108
- if (flip && popoverStart < windowStart) {
115
+ const { popoverSize, containerRect, triggerRect, gap, flip } = options;
116
+ const containerStart = containerRect[scrollOffsetKey];
117
+ const containerEnd = containerStart + containerRect[sizeKey];
118
+ const triggerStart = containerStart + triggerRect[rectKey];
119
+ const popoverStart = triggerStart - popoverSize[sizeKey] - gap;
120
+ if (flip && popoverStart < containerStart) {
109
121
  const afterPopoverStart = this.after({ ...options, flip: false });
110
- const afterPopoverEnd = afterPopoverStart + popoverRect[sizeKey];
111
- const diffStart = windowStart - popoverStart;
112
- const diffEnd = afterPopoverEnd - windowEnd;
113
- if (afterPopoverEnd <= windowEnd || diffStart > diffEnd)
122
+ const afterPopoverEnd = afterPopoverStart + popoverSize[sizeKey];
123
+ const diffStart = containerStart - popoverStart;
124
+ const diffEnd = afterPopoverEnd - containerEnd;
125
+ if (afterPopoverEnd <= containerEnd || diffStart > diffEnd)
114
126
  return afterPopoverStart;
115
127
  }
116
128
  return popoverStart;
117
129
  },
118
130
  after(options) {
119
- const { elementRect, popoverRect, gap, flip } = options;
120
- const windowStart = window[windowOffsetKey];
121
- const windowEnd = windowStart + window[windowSizeKey];
122
- const elementStart = windowStart + elementRect[rectKey];
123
- const elementEnd = elementStart + elementRect[sizeKey];
124
- const popoverStart = elementEnd + gap;
125
- const popoverEnd = popoverStart + popoverRect[sizeKey];
126
- if (flip && popoverEnd > windowEnd) {
131
+ const { popoverSize, containerRect, triggerRect, gap, flip } = options;
132
+ const containerStart = containerRect[scrollOffsetKey];
133
+ const containerEnd = containerStart + containerRect[sizeKey];
134
+ const triggerStart = containerStart + triggerRect[rectKey];
135
+ const triggerEnd = triggerStart + triggerRect[sizeKey];
136
+ const popoverStart = triggerEnd + gap;
137
+ const popoverEnd = popoverStart + popoverSize[sizeKey];
138
+ console.log(popoverEnd, containerEnd);
139
+ if (flip && popoverEnd > containerEnd) {
127
140
  const beforePopoverStart = this.before({ ...options, flip: false });
128
- const diffStart = windowStart - beforePopoverStart;
129
- const diffEnd = popoverEnd - windowEnd;
130
- if (beforePopoverStart >= windowStart || diffEnd > diffStart)
141
+ const diffStart = containerStart - beforePopoverStart;
142
+ const diffEnd = popoverEnd - containerEnd;
143
+ if (beforePopoverStart >= containerStart || diffEnd > diffStart)
131
144
  return beforePopoverStart;
132
145
  }
133
146
  return popoverStart;
134
147
  },
135
- start: ({ elementRect, popoverRect }) => {
136
- const windowStart = window[windowOffsetKey];
137
- const elementStart = windowStart + elementRect[rectKey];
138
- return fitToWindow(elementStart, { elementRect, popoverRect });
148
+ start: ({ popoverSize, containerRect, triggerRect }) => {
149
+ const containerStart = containerRect[scrollOffsetKey];
150
+ const triggerStart = containerStart + triggerRect[rectKey];
151
+ return fitToContainer(triggerStart, {
152
+ popoverSize,
153
+ containerRect,
154
+ triggerRect,
155
+ });
139
156
  },
140
- end: ({ elementRect, popoverRect }) => {
141
- const windowStart = window[windowOffsetKey];
142
- const elementStart = windowStart + elementRect[rectKey];
143
- const elementEnd = elementStart + elementRect[sizeKey];
144
- const popoverStart = elementEnd - popoverRect[sizeKey];
145
- return fitToWindow(popoverStart, { elementRect, popoverRect });
157
+ end: ({ popoverSize, containerRect, triggerRect }) => {
158
+ const containerStart = containerRect[scrollOffsetKey];
159
+ const triggerStart = containerStart + triggerRect[rectKey];
160
+ const triggerEnd = triggerStart + triggerRect[sizeKey];
161
+ const popoverStart = triggerEnd - popoverSize[sizeKey];
162
+ return fitToContainer(popoverStart, {
163
+ popoverSize,
164
+ containerRect,
165
+ triggerRect,
166
+ });
146
167
  },
147
- center: ({ elementRect, popoverRect }) => {
148
- const windowStart = window[windowOffsetKey];
149
- const elementStart = windowStart + elementRect[rectKey];
168
+ center: ({ popoverSize, containerRect, triggerRect }) => {
169
+ const containerStart = containerRect[scrollOffsetKey];
170
+ const triggerStart = containerStart + triggerRect[rectKey];
150
171
  const popoverStart =
151
- elementStart + (elementRect[sizeKey] - popoverRect[sizeKey]) / 2;
152
- return fitToWindow(popoverStart, { elementRect, popoverRect });
172
+ triggerStart + (triggerRect[sizeKey] - popoverSize[sizeKey]) / 2;
173
+ return fitToContainer(popoverStart, {
174
+ popoverSize,
175
+ containerRect,
176
+ triggerRect,
177
+ });
153
178
  },
154
179
  };
155
180
  };
@@ -171,12 +196,16 @@ const placementPositionKeysMap: PlacementPositionKeysMap = {
171
196
  right: ['center', 'after'],
172
197
  'top-start': ['before', 'start'],
173
198
  'top-end': ['before', 'end'],
199
+ 'top-full': ['before', 'start'],
174
200
  'bottom-start': ['after', 'start'],
175
201
  'bottom-end': ['after', 'end'],
202
+ 'bottom-full': ['after', 'start'],
176
203
  'left-start': ['start', 'before'],
177
204
  'left-end': ['end', 'before'],
205
+ 'left-full': ['start', 'before'],
178
206
  'right-start': ['start', 'after'],
179
207
  'right-end': ['end', 'after'],
208
+ 'right-full': ['start', 'after'],
180
209
  };
181
210
 
182
211
  interface PopoverPosition {
@@ -190,8 +219,9 @@ interface PopoverPosition {
190
219
  * In most cases, it will be the window.
191
220
  */
192
221
  const usePopoverPosition = ({
193
- elementRect,
194
- popoverRect,
222
+ popoverSize,
223
+ containerRect,
224
+ triggerRect,
195
225
  placement = 'top',
196
226
  gap = 0.5,
197
227
  flip = true,
@@ -199,15 +229,25 @@ const usePopoverPosition = ({
199
229
  const bodyFontSize = useFontSize(document.body);
200
230
  const gapPx = useMemo(() => gap * bodyFontSize, [gap, bodyFontSize]);
201
231
 
202
- const positionKeys = useMemo(() => {
203
- if (typeof placement === 'string' && !!placementPositionKeysMap[placement])
204
- return placementPositionKeysMap[placement];
205
- return placementPositionKeysMap.top;
206
- }, [placement]);
232
+ const positionKeys = useMemo(
233
+ () =>
234
+ typeof placement === 'string' && !!placementPositionKeysMap[placement]
235
+ ? placementPositionKeysMap[placement]
236
+ : placementPositionKeysMap.top,
237
+ [placement]
238
+ );
239
+
240
+ if (!popoverSize || !containerRect || !triggerRect) {
241
+ return {
242
+ top: -10000,
243
+ left: -10000,
244
+ };
245
+ }
207
246
 
208
247
  return getPopoverPosition(...positionKeys, {
209
- elementRect,
210
- popoverRect,
248
+ popoverSize,
249
+ containerRect,
250
+ triggerRect,
211
251
  gap: gapPx,
212
252
  flip,
213
253
  });