@os-design/core 1.0.277 → 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.277",
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": "73c07308561addb0b095734a9a9bd3e3a773fa61"
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
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,73 +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
- const rect = current.getBoundingClientRect();
173
- if (container) {
174
- const containerElement =
175
- container instanceof Element ? container : container.current;
176
- if (!containerElement) return;
177
- const containerRect = containerElement.getBoundingClientRect();
178
- rect.x -= containerRect.x;
179
- rect.y -= containerRect.y;
178
+ if (!popoverRef.current) return;
179
+ const rect = popoverRef.current.getBoundingClientRect();
180
+ setPopoverSize({
181
+ width: rect.width,
182
+ height: rect.height,
183
+ });
184
+ });
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(() => {
201
+ if (!visible) return;
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
+ }
180
224
  }
181
- setTriggerRect(rect);
182
225
  });
183
- }, [container, trigger]);
226
+ }, [containerRef, visible]);
184
227
  useBrowserLayoutEffect(() => {
185
- if (!visible) return;
186
- triggerResizeListener();
187
- }, [triggerResizeListener, visible]);
188
- useResizeObserver(trigger as never, triggerResizeListener);
228
+ containerListener();
229
+ }, [containerListener]);
189
230
  useEvent(
190
- (typeof window === 'undefined' ? null : window) as never,
231
+ typeof window !== 'undefined' && containerRef.current === document.body
232
+ ? window
233
+ : null,
191
234
  'resize',
192
- 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
193
251
  );
194
- useEvent(document, 'scroll', triggerResizeListener);
195
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);
196
279
  useEffect(() => {
197
- if (!trigger || (trigger as RefObject<Element>).current !== undefined)
198
- return;
199
- setTriggerRect(trigger as Rect);
280
+ if (trigger && 'top' in trigger) {
281
+ setTriggerRect(trigger);
282
+ }
200
283
  }, [trigger]);
201
284
 
285
+ // Set the aria tags to support accessibility features
202
286
  const popoverId = useMemo(
203
287
  () => id || `popover-${Math.random().toString(36).slice(2, 11)}`,
204
288
  [id]
205
289
  );
206
-
207
- // Set the aria tags to support accessibility features
208
290
  useBrowserLayoutEffect(() => {
209
291
  if (!trigger) return;
210
292
  const { current } = trigger as RefObject<Element>;
@@ -223,23 +305,40 @@ const Popover = forwardRef<HTMLDivElement, PopoverProps>(
223
305
 
224
306
  // Get the popover coordinates
225
307
  const { top, left } = usePopoverPosition({
226
- elementRect: triggerRect,
227
- popoverRect,
308
+ popoverSize,
309
+ containerRect,
310
+ triggerRect,
228
311
  placement,
229
312
  gap,
230
313
  flip,
231
314
  });
232
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
+
233
330
  // Close the popover when the user clicks outside of it
234
331
  useClickOutside(popoverRef, onClose);
235
332
 
236
333
  if (!mounted) return null;
237
334
 
238
335
  return (
239
- <Portal container={container}>
336
+ <Portal container={containerRef.current}>
240
337
  <Container
241
338
  top={top}
242
339
  left={left}
340
+ width={width}
341
+ height={height}
243
342
  visible={visible}
244
343
  id={popoverId}
245
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
  });