@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/dist/Popover/index.d.ts +5 -5
- package/dist/Popover/index.d.ts.map +1 -1
- package/dist/Popover/index.js +125 -45
- package/dist/Popover/utils/usePopoverPosition.d.ts +19 -9
- package/dist/Popover/utils/usePopoverPosition.d.ts.map +1 -1
- package/dist/Popover/utils/usePopoverPosition.js +90 -72
- package/dist/Select/index.d.ts.map +1 -1
- package/dist/Select/index.js +4 -26
- package/package.json +3 -3
- package/src/Popover/index.tsx +156 -57
- package/src/Popover/utils/usePopoverPosition.ts +123 -83
- package/src/Select/index.tsx +3 -34
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@os-design/core",
|
|
3
|
-
"version": "1.0.
|
|
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.
|
|
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": "
|
|
61
|
+
"gitHead": "d7b2d28720454ff073af76be76e9dedd1486fe52"
|
|
62
62
|
}
|
package/src/Popover/index.tsx
CHANGED
|
@@ -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 [
|
|
142
|
-
const [
|
|
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
|
-
//
|
|
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
|
-
|
|
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
|
-
//
|
|
166
|
-
|
|
167
|
-
const triggerResizeListener = useCallback(() => {
|
|
175
|
+
// Update the popover size when it has been resized
|
|
176
|
+
const popoverResizeListener = useCallback(() => {
|
|
168
177
|
window.requestAnimationFrame(() => {
|
|
169
|
-
if (!
|
|
170
|
-
const
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
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
|
-
}, [
|
|
226
|
+
}, [containerRef, visible]);
|
|
184
227
|
useBrowserLayoutEffect(() => {
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
}, [triggerResizeListener, visible]);
|
|
188
|
-
useResizeObserver(trigger as never, triggerResizeListener);
|
|
228
|
+
containerListener();
|
|
229
|
+
}, [containerListener]);
|
|
189
230
|
useEvent(
|
|
190
|
-
|
|
231
|
+
typeof window !== 'undefined' && containerRef.current === document.body
|
|
232
|
+
? window
|
|
233
|
+
: null,
|
|
191
234
|
'resize',
|
|
192
|
-
|
|
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 (
|
|
198
|
-
|
|
199
|
-
|
|
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
|
-
|
|
227
|
-
|
|
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={
|
|
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
|
|
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
|
|
27
|
+
* The size of the popover.
|
|
20
28
|
*/
|
|
21
|
-
|
|
29
|
+
popoverSize: Size | null;
|
|
22
30
|
/**
|
|
23
|
-
* The rect of the
|
|
31
|
+
* The rect of the container.
|
|
24
32
|
*/
|
|
25
|
-
|
|
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
|
-
|
|
45
|
-
|
|
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
|
-
'
|
|
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
|
|
64
|
-
const windowOffsetKey = rectKey === 'top' ? 'pageYOffset' : 'pageXOffset';
|
|
76
|
+
const scrollOffsetKey = rectKey === 'top' ? 'scrollTop' : 'scrollLeft';
|
|
65
77
|
|
|
66
|
-
const
|
|
78
|
+
const fitToContainer = (
|
|
67
79
|
start: number,
|
|
68
|
-
{
|
|
80
|
+
{ popoverSize, containerRect, triggerRect }: FitToWindow
|
|
69
81
|
): number => {
|
|
70
82
|
let popoverStart = start;
|
|
71
|
-
const
|
|
72
|
-
const
|
|
73
|
-
const
|
|
74
|
-
const
|
|
75
|
-
const popoverEnd = popoverStart +
|
|
76
|
-
|
|
77
|
-
// Fit the popover to the end of the
|
|
78
|
-
if (popoverEnd >
|
|
79
|
-
if (
|
|
80
|
-
popoverStart =
|
|
81
|
-
else if (
|
|
82
|
-
popoverStart =
|
|
83
|
-
else if (
|
|
84
|
-
popoverStart =
|
|
85
|
-
else popoverStart =
|
|
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
|
|
89
|
-
if (popoverStart <
|
|
90
|
-
if (
|
|
91
|
-
else if (
|
|
92
|
-
popoverStart =
|
|
93
|
-
else if (
|
|
94
|
-
popoverStart =
|
|
95
|
-
else popoverStart =
|
|
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 {
|
|
104
|
-
const
|
|
105
|
-
const
|
|
106
|
-
const
|
|
107
|
-
const popoverStart =
|
|
108
|
-
if (flip && popoverStart <
|
|
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 +
|
|
111
|
-
const diffStart =
|
|
112
|
-
const diffEnd = afterPopoverEnd -
|
|
113
|
-
if (afterPopoverEnd <=
|
|
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 {
|
|
120
|
-
const
|
|
121
|
-
const
|
|
122
|
-
const
|
|
123
|
-
const
|
|
124
|
-
const popoverStart =
|
|
125
|
-
const popoverEnd = popoverStart +
|
|
126
|
-
|
|
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 =
|
|
129
|
-
const diffEnd = popoverEnd -
|
|
130
|
-
if (beforePopoverStart >=
|
|
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: ({
|
|
136
|
-
const
|
|
137
|
-
const
|
|
138
|
-
return
|
|
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: ({
|
|
141
|
-
const
|
|
142
|
-
const
|
|
143
|
-
const
|
|
144
|
-
const popoverStart =
|
|
145
|
-
return
|
|
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: ({
|
|
148
|
-
const
|
|
149
|
-
const
|
|
168
|
+
center: ({ popoverSize, containerRect, triggerRect }) => {
|
|
169
|
+
const containerStart = containerRect[scrollOffsetKey];
|
|
170
|
+
const triggerStart = containerStart + triggerRect[rectKey];
|
|
150
171
|
const popoverStart =
|
|
151
|
-
|
|
152
|
-
return
|
|
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
|
-
|
|
194
|
-
|
|
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
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
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
|
-
|
|
210
|
-
|
|
248
|
+
popoverSize,
|
|
249
|
+
containerRect,
|
|
250
|
+
triggerRect,
|
|
211
251
|
gap: gapPx,
|
|
212
252
|
flip,
|
|
213
253
|
});
|