@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/dist/Popover/index.d.ts
CHANGED
|
@@ -3,16 +3,16 @@ import { type RefObject } from 'react';
|
|
|
3
3
|
import { type Placement, type Rect } from './utils/usePopoverPosition.js';
|
|
4
4
|
type JsxDivProps = Omit<JSX.IntrinsicElements['div'], 'ref'>;
|
|
5
5
|
export interface PopoverProps extends JsxDivProps, WithSize {
|
|
6
|
-
/**
|
|
7
|
-
* The element next to which the popover appears.
|
|
8
|
-
* @default undefined
|
|
9
|
-
*/
|
|
10
|
-
trigger?: RefObject<Element> | Rect;
|
|
11
6
|
/**
|
|
12
7
|
* Where the popover will be rendered.
|
|
13
8
|
* @default document.body
|
|
14
9
|
*/
|
|
15
10
|
container?: RefObject<Element> | Element | null;
|
|
11
|
+
/**
|
|
12
|
+
* The element next to which the popover appears.
|
|
13
|
+
* @default undefined
|
|
14
|
+
*/
|
|
15
|
+
trigger?: RefObject<Element> | Element | Rect | null;
|
|
16
16
|
/**
|
|
17
17
|
* On which side of the element the popover will appear.
|
|
18
18
|
* @default top
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/Popover/index.tsx"],"names":[],"mappings":"AAGA,OAAO,EAAc,KAAK,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAW9D,OAAO,EAGL,KAAK,SAAS,EAKf,MAAM,OAAO,CAAC;AACf,OAA2B,EACzB,KAAK,SAAS,EACd,KAAK,IAAI,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/Popover/index.tsx"],"names":[],"mappings":"AAGA,OAAO,EAAc,KAAK,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAW9D,OAAO,EAGL,KAAK,SAAS,EAKf,MAAM,OAAO,CAAC;AACf,OAA2B,EACzB,KAAK,SAAS,EACd,KAAK,IAAI,EAGV,MAAM,+BAA+B,CAAC;AAEvC,KAAK,WAAW,GAAG,IAAI,CAAC,GAAG,CAAC,iBAAiB,CAAC,KAAK,CAAC,EAAE,KAAK,CAAC,CAAC;AAC7D,MAAM,WAAW,YAAa,SAAQ,WAAW,EAAE,QAAQ;IACzD;;;OAGG;IACH,SAAS,CAAC,EAAE,SAAS,CAAC,OAAO,CAAC,GAAG,OAAO,GAAG,IAAI,CAAC;IAChD;;;OAGG;IACH,OAAO,CAAC,EAAE,SAAS,CAAC,OAAO,CAAC,GAAG,OAAO,GAAG,IAAI,GAAG,IAAI,CAAC;IACrD;;;OAGG;IACH,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB;;;OAGG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;;;OAGG;IACH,IAAI,CAAC,EAAE,OAAO,CAAC;IACf;;;OAGG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;CACtB;AAgED;;GAEG;AACH,QAAA,MAAM,OAAO,yGA0NZ,CAAC;AAIF,eAAe,OAAO,CAAC"}
|
package/dist/Popover/index.js
CHANGED
|
@@ -15,6 +15,12 @@ const fadeOut = keyframes`
|
|
|
15
15
|
from { opacity: 1; }
|
|
16
16
|
to { opacity: 0; }
|
|
17
17
|
`;
|
|
18
|
+
const widthStyles = p => p.width !== null && css`
|
|
19
|
+
width: ${p.width}px;
|
|
20
|
+
`;
|
|
21
|
+
const heightStyles = p => p.height !== null && css`
|
|
22
|
+
height: ${p.height}px;
|
|
23
|
+
`;
|
|
18
24
|
const visibleStyles = p => p.visible && css`
|
|
19
25
|
animation: ${fadeIn} ${p.theme.transitionDelay}ms forwards;
|
|
20
26
|
`;
|
|
@@ -33,23 +39,19 @@ const Container = styled('div', omitEmotionProps('top', 'left', 'visible', 'size
|
|
|
33
39
|
box-shadow: 0 0.15em 0.8em ${p => clr(p.theme.popoverColorBoxShadow)};
|
|
34
40
|
z-index: 1000; // Greater than the z-index of the Drawer
|
|
35
41
|
|
|
42
|
+
${widthStyles};
|
|
43
|
+
${heightStyles};
|
|
36
44
|
${visibleStyles};
|
|
37
45
|
${invisibleStyles};
|
|
38
46
|
${sizeStyles};
|
|
39
47
|
`;
|
|
40
|
-
const emptyRect = {
|
|
41
|
-
top: 0,
|
|
42
|
-
left: 0,
|
|
43
|
-
width: 0,
|
|
44
|
-
height: 0
|
|
45
|
-
};
|
|
46
48
|
|
|
47
49
|
/**
|
|
48
50
|
* The pop-up window located next to the element.
|
|
49
51
|
*/
|
|
50
52
|
const Popover = /*#__PURE__*/forwardRef(({
|
|
51
|
-
trigger,
|
|
52
53
|
container,
|
|
54
|
+
trigger,
|
|
53
55
|
placement = 'top',
|
|
54
56
|
gap = 0.2,
|
|
55
57
|
flip = true,
|
|
@@ -60,59 +62,126 @@ const Popover = /*#__PURE__*/forwardRef(({
|
|
|
60
62
|
...rest
|
|
61
63
|
}, ref) => {
|
|
62
64
|
const [popoverRef, mergedPopoverRef] = useForwardedRef(ref);
|
|
63
|
-
const [
|
|
64
|
-
const [
|
|
65
|
+
const [popoverSize, setPopoverSize] = useState(null);
|
|
66
|
+
const [containerRect, setContainerRect] = useState(null);
|
|
67
|
+
const [triggerRect, setTriggerRect] = useState(null);
|
|
65
68
|
const {
|
|
66
69
|
theme
|
|
67
70
|
} = useTheme();
|
|
68
71
|
const mounted = useClosable(visible, theme.transitionDelay);
|
|
69
72
|
|
|
70
|
-
//
|
|
71
|
-
const popoverResizeListener = useCallback(() => {
|
|
72
|
-
if (!popoverRef.current) return;
|
|
73
|
-
setPopoverRect(popoverRef.current.getBoundingClientRect());
|
|
74
|
-
}, [popoverRef]);
|
|
75
|
-
useResizeObserver(popoverRef.current, popoverResizeListener);
|
|
73
|
+
// Save the popover size when it has been rendered
|
|
76
74
|
const measuredPopoverRef = useCallback(node => {
|
|
77
75
|
if (node === null) return;
|
|
78
|
-
|
|
76
|
+
const rect = node.getBoundingClientRect();
|
|
77
|
+
setPopoverSize({
|
|
78
|
+
width: rect.width,
|
|
79
|
+
height: rect.height
|
|
80
|
+
});
|
|
79
81
|
mergedPopoverRef(node);
|
|
80
82
|
}, [mergedPopoverRef]);
|
|
81
83
|
|
|
82
|
-
//
|
|
83
|
-
|
|
84
|
-
|
|
84
|
+
// Update the popover size when it has been resized
|
|
85
|
+
const popoverResizeListener = useCallback(() => {
|
|
86
|
+
window.requestAnimationFrame(() => {
|
|
87
|
+
if (!popoverRef.current) return;
|
|
88
|
+
const rect = popoverRef.current.getBoundingClientRect();
|
|
89
|
+
setPopoverSize({
|
|
90
|
+
width: rect.width,
|
|
91
|
+
height: rect.height
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
}, [popoverRef]);
|
|
95
|
+
useResizeObserver(popoverRef.current, popoverResizeListener);
|
|
96
|
+
const containerRef = useMemo(() => {
|
|
97
|
+
if (typeof window === 'undefined') {
|
|
98
|
+
return {
|
|
99
|
+
current: null
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
if (!container) {
|
|
103
|
+
return {
|
|
104
|
+
current: document.body
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
return 'current' in container ? container : {
|
|
108
|
+
current: container
|
|
109
|
+
};
|
|
110
|
+
}, [container]);
|
|
111
|
+
|
|
112
|
+
// Update the container size and its scroll offsets initially and when it has been changed.
|
|
113
|
+
// Must depend on `visible` to update the position when the popover is visible.
|
|
114
|
+
const containerListener = useCallback(() => {
|
|
115
|
+
if (!visible) return;
|
|
85
116
|
window.requestAnimationFrame(() => {
|
|
86
|
-
if (
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
117
|
+
if (containerRef.current) {
|
|
118
|
+
if (containerRef.current === document.body) {
|
|
119
|
+
setContainerRect({
|
|
120
|
+
top: 0,
|
|
121
|
+
left: 0,
|
|
122
|
+
width: window.innerWidth,
|
|
123
|
+
height: window.innerHeight,
|
|
124
|
+
scrollTop: window.scrollY,
|
|
125
|
+
scrollLeft: window.scrollX
|
|
126
|
+
});
|
|
127
|
+
} else {
|
|
128
|
+
const rect = containerRef.current.getBoundingClientRect();
|
|
129
|
+
setContainerRect({
|
|
130
|
+
top: rect.top,
|
|
131
|
+
left: rect.left,
|
|
132
|
+
width: rect.width,
|
|
133
|
+
height: rect.height,
|
|
134
|
+
scrollTop: containerRef.current.scrollTop,
|
|
135
|
+
scrollLeft: containerRef.current.scrollLeft
|
|
136
|
+
});
|
|
137
|
+
}
|
|
98
138
|
}
|
|
99
|
-
setTriggerRect(rect);
|
|
100
139
|
});
|
|
101
|
-
}, [
|
|
140
|
+
}, [containerRef, visible]);
|
|
102
141
|
useBrowserLayoutEffect(() => {
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
useResizeObserver(
|
|
107
|
-
useEvent(typeof window
|
|
108
|
-
|
|
142
|
+
containerListener();
|
|
143
|
+
}, [containerListener]);
|
|
144
|
+
useEvent(typeof window !== 'undefined' && containerRef.current === document.body ? window : null, 'resize', containerListener);
|
|
145
|
+
useResizeObserver(typeof window !== 'undefined' && containerRef.current !== document.body ? containerRef.current : null, containerListener);
|
|
146
|
+
useEvent(typeof window !== 'undefined' ? containerRef.current === document.body ? document : containerRef.current : null, 'scroll', containerListener);
|
|
147
|
+
const triggerRef = useMemo(() => {
|
|
148
|
+
if (!trigger || 'top' in trigger) {
|
|
149
|
+
return {
|
|
150
|
+
current: null
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
return 'current' in trigger ? trigger : {
|
|
154
|
+
current: trigger
|
|
155
|
+
};
|
|
156
|
+
}, [trigger]);
|
|
157
|
+
|
|
158
|
+
// Update the trigger size and its position initially and when it has been changed
|
|
159
|
+
const triggerListener = useCallback(() => {
|
|
160
|
+
if (!containerRect) return;
|
|
161
|
+
window.requestAnimationFrame(() => {
|
|
162
|
+
if (triggerRef.current) {
|
|
163
|
+
const triggerRect = triggerRef.current.getBoundingClientRect();
|
|
164
|
+
setTriggerRect({
|
|
165
|
+
top: triggerRect.top - containerRect.top,
|
|
166
|
+
left: triggerRect.left - containerRect.left,
|
|
167
|
+
width: triggerRect.width,
|
|
168
|
+
height: triggerRect.height
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
});
|
|
172
|
+
}, [containerRect, triggerRef]);
|
|
173
|
+
useBrowserLayoutEffect(() => {
|
|
174
|
+
triggerListener();
|
|
175
|
+
}, [triggerListener]);
|
|
176
|
+
useResizeObserver(triggerRef.current, triggerListener);
|
|
109
177
|
useEffect(() => {
|
|
110
|
-
if (
|
|
111
|
-
|
|
178
|
+
if (trigger && 'top' in trigger) {
|
|
179
|
+
setTriggerRect(trigger);
|
|
180
|
+
}
|
|
112
181
|
}, [trigger]);
|
|
113
|
-
const popoverId = useMemo(() => id || `popover-${Math.random().toString(36).slice(2, 11)}`, [id]);
|
|
114
182
|
|
|
115
183
|
// Set the aria tags to support accessibility features
|
|
184
|
+
const popoverId = useMemo(() => id || `popover-${Math.random().toString(36).slice(2, 11)}`, [id]);
|
|
116
185
|
useBrowserLayoutEffect(() => {
|
|
117
186
|
if (!trigger) return;
|
|
118
187
|
const {
|
|
@@ -138,21 +207,32 @@ const Popover = /*#__PURE__*/forwardRef(({
|
|
|
138
207
|
top,
|
|
139
208
|
left
|
|
140
209
|
} = usePopoverPosition({
|
|
141
|
-
|
|
142
|
-
|
|
210
|
+
popoverSize,
|
|
211
|
+
containerRect,
|
|
212
|
+
triggerRect,
|
|
143
213
|
placement,
|
|
144
214
|
gap,
|
|
145
215
|
flip
|
|
146
216
|
});
|
|
217
|
+
const width = useMemo(() => {
|
|
218
|
+
if (!triggerRect) return null;
|
|
219
|
+
return placement === 'top-full' || placement === 'bottom-full' ? triggerRect.width : null;
|
|
220
|
+
}, [placement, triggerRect]);
|
|
221
|
+
const height = useMemo(() => {
|
|
222
|
+
if (!triggerRect) return null;
|
|
223
|
+
return placement === 'left-full' || placement === 'right-full' ? triggerRect.width : null;
|
|
224
|
+
}, [placement, triggerRect]);
|
|
147
225
|
|
|
148
226
|
// Close the popover when the user clicks outside of it
|
|
149
227
|
useClickOutside(popoverRef, onClose);
|
|
150
228
|
if (!mounted) return null;
|
|
151
229
|
return /*#__PURE__*/_jsx(Portal, {
|
|
152
|
-
container:
|
|
230
|
+
container: containerRef.current,
|
|
153
231
|
children: /*#__PURE__*/_jsx(Container, {
|
|
154
232
|
top: top,
|
|
155
233
|
left: left,
|
|
234
|
+
width: width,
|
|
235
|
+
height: height,
|
|
156
236
|
visible: visible,
|
|
157
237
|
id: popoverId,
|
|
158
238
|
role: "dialog",
|
|
@@ -1,22 +1,32 @@
|
|
|
1
1
|
type Side = 'top' | 'left' | 'right' | 'bottom';
|
|
2
|
-
type Alignment = 'start' | 'end';
|
|
2
|
+
type Alignment = 'start' | 'end' | 'full';
|
|
3
3
|
type AlignedPlacement = `${Side}-${Alignment}`;
|
|
4
4
|
export type Placement = Side | AlignedPlacement;
|
|
5
|
-
export interface
|
|
6
|
-
top: number;
|
|
7
|
-
left: number;
|
|
5
|
+
export interface Size {
|
|
8
6
|
width: number;
|
|
9
7
|
height: number;
|
|
10
8
|
}
|
|
9
|
+
export interface Rect extends Size {
|
|
10
|
+
top: number;
|
|
11
|
+
left: number;
|
|
12
|
+
}
|
|
13
|
+
export interface ScrollableRect extends Rect {
|
|
14
|
+
scrollTop: number;
|
|
15
|
+
scrollLeft: number;
|
|
16
|
+
}
|
|
11
17
|
interface UsePopoverPositionProps {
|
|
12
18
|
/**
|
|
13
|
-
* The
|
|
19
|
+
* The size of the popover.
|
|
14
20
|
*/
|
|
15
|
-
|
|
21
|
+
popoverSize: Size | null;
|
|
16
22
|
/**
|
|
17
|
-
* The rect of the
|
|
23
|
+
* The rect of the container.
|
|
24
|
+
*/
|
|
25
|
+
containerRect: ScrollableRect | null;
|
|
26
|
+
/**
|
|
27
|
+
* The rect of the element.
|
|
18
28
|
*/
|
|
19
|
-
|
|
29
|
+
triggerRect: Rect | null;
|
|
20
30
|
/**
|
|
21
31
|
* On which side of the element the popover will appear.
|
|
22
32
|
* @default top
|
|
@@ -42,6 +52,6 @@ interface PopoverPosition {
|
|
|
42
52
|
* Note that you must change the elementRect when the scroll and resize events of the parent scrollable container occur.
|
|
43
53
|
* In most cases, it will be the window.
|
|
44
54
|
*/
|
|
45
|
-
declare const usePopoverPosition: ({
|
|
55
|
+
declare const usePopoverPosition: ({ popoverSize, containerRect, triggerRect, placement, gap, flip, }: UsePopoverPositionProps) => PopoverPosition;
|
|
46
56
|
export default usePopoverPosition;
|
|
47
57
|
//# sourceMappingURL=usePopoverPosition.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"usePopoverPosition.d.ts","sourceRoot":"","sources":["../../../src/Popover/utils/usePopoverPosition.ts"],"names":[],"mappings":"AAGA,KAAK,IAAI,GAAG,KAAK,GAAG,MAAM,GAAG,OAAO,GAAG,QAAQ,CAAC;AAChD,KAAK,SAAS,GAAG,OAAO,GAAG,KAAK,CAAC;
|
|
1
|
+
{"version":3,"file":"usePopoverPosition.d.ts","sourceRoot":"","sources":["../../../src/Popover/utils/usePopoverPosition.ts"],"names":[],"mappings":"AAGA,KAAK,IAAI,GAAG,KAAK,GAAG,MAAM,GAAG,OAAO,GAAG,QAAQ,CAAC;AAChD,KAAK,SAAS,GAAG,OAAO,GAAG,KAAK,GAAG,MAAM,CAAC;AAC1C,KAAK,gBAAgB,GAAG,GAAG,IAAI,IAAI,SAAS,EAAE,CAAC;AAE/C,MAAM,MAAM,SAAS,GAAG,IAAI,GAAG,gBAAgB,CAAC;AAEhD,MAAM,WAAW,IAAI;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,IAAK,SAAQ,IAAI;IAChC,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,cAAe,SAAQ,IAAI;IAC1C,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,UAAU,uBAAuB;IAC/B;;OAEG;IACH,WAAW,EAAE,IAAI,GAAG,IAAI,CAAC;IACzB;;OAEG;IACH,aAAa,EAAE,cAAc,GAAG,IAAI,CAAC;IACrC;;OAEG;IACH,WAAW,EAAE,IAAI,GAAG,IAAI,CAAC;IACzB;;;OAGG;IACH,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB;;;OAGG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;;;OAGG;IACH,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AA8JD,UAAU,eAAe;IACvB,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;;;GAIG;AACH,QAAA,MAAM,kBAAkB,uEAOrB,uBAAuB,KAAG,eA0B5B,CAAC;AAEF,eAAe,kBAAkB,CAAC"}
|
|
@@ -2,112 +2,121 @@ import { useFontSize } from '@os-design/utils';
|
|
|
2
2
|
import { useMemo } from 'react';
|
|
3
3
|
const popoverPositionGetters = rectKey => {
|
|
4
4
|
const sizeKey = rectKey === 'top' ? 'height' : 'width';
|
|
5
|
-
const
|
|
6
|
-
const
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
5
|
+
const scrollOffsetKey = rectKey === 'top' ? 'scrollTop' : 'scrollLeft';
|
|
6
|
+
const fitToContainer = (start, {
|
|
7
|
+
popoverSize,
|
|
8
|
+
containerRect,
|
|
9
|
+
triggerRect
|
|
10
10
|
}) => {
|
|
11
11
|
let popoverStart = start;
|
|
12
|
-
const
|
|
13
|
-
const
|
|
14
|
-
const
|
|
15
|
-
const
|
|
16
|
-
const popoverEnd = popoverStart +
|
|
12
|
+
const containerStart = containerRect[scrollOffsetKey];
|
|
13
|
+
const containerEnd = containerStart + containerRect[sizeKey];
|
|
14
|
+
const triggerStart = containerStart + triggerRect[rectKey];
|
|
15
|
+
const triggerEnd = triggerStart + triggerRect[sizeKey];
|
|
16
|
+
const popoverEnd = popoverStart + popoverSize[sizeKey];
|
|
17
17
|
|
|
18
|
-
// Fit the popover to the end of the
|
|
19
|
-
if (popoverEnd >
|
|
20
|
-
if (
|
|
18
|
+
// Fit the popover to the end of the container
|
|
19
|
+
if (popoverEnd > containerEnd) {
|
|
20
|
+
if (triggerEnd < containerEnd) popoverStart = containerEnd - popoverSize[sizeKey];else if (popoverSize[sizeKey] > triggerRect[sizeKey]) popoverStart = triggerEnd - popoverSize[sizeKey];else if (containerEnd - triggerStart > popoverSize[sizeKey]) popoverStart = containerEnd - popoverSize[sizeKey];else popoverStart = triggerStart;
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
// Fit the popover to the beginning of the
|
|
24
|
-
if (popoverStart <
|
|
25
|
-
if (
|
|
23
|
+
// Fit the popover to the beginning of the container
|
|
24
|
+
if (popoverStart < containerStart) {
|
|
25
|
+
if (triggerStart > containerStart) popoverStart = containerStart;else if (popoverSize[sizeKey] > triggerRect[sizeKey]) popoverStart = triggerStart;else if (triggerEnd - containerStart > popoverSize[sizeKey]) popoverStart = containerStart;else popoverStart = triggerEnd - popoverSize[sizeKey];
|
|
26
26
|
}
|
|
27
27
|
return popoverStart;
|
|
28
28
|
};
|
|
29
29
|
return {
|
|
30
30
|
before(options) {
|
|
31
31
|
const {
|
|
32
|
-
|
|
33
|
-
|
|
32
|
+
popoverSize,
|
|
33
|
+
containerRect,
|
|
34
|
+
triggerRect,
|
|
34
35
|
gap,
|
|
35
36
|
flip
|
|
36
37
|
} = options;
|
|
37
|
-
const
|
|
38
|
-
const
|
|
39
|
-
const
|
|
40
|
-
const popoverStart =
|
|
41
|
-
if (flip && popoverStart <
|
|
38
|
+
const containerStart = containerRect[scrollOffsetKey];
|
|
39
|
+
const containerEnd = containerStart + containerRect[sizeKey];
|
|
40
|
+
const triggerStart = containerStart + triggerRect[rectKey];
|
|
41
|
+
const popoverStart = triggerStart - popoverSize[sizeKey] - gap;
|
|
42
|
+
if (flip && popoverStart < containerStart) {
|
|
42
43
|
const afterPopoverStart = this.after({
|
|
43
44
|
...options,
|
|
44
45
|
flip: false
|
|
45
46
|
});
|
|
46
|
-
const afterPopoverEnd = afterPopoverStart +
|
|
47
|
-
const diffStart =
|
|
48
|
-
const diffEnd = afterPopoverEnd -
|
|
49
|
-
if (afterPopoverEnd <=
|
|
47
|
+
const afterPopoverEnd = afterPopoverStart + popoverSize[sizeKey];
|
|
48
|
+
const diffStart = containerStart - popoverStart;
|
|
49
|
+
const diffEnd = afterPopoverEnd - containerEnd;
|
|
50
|
+
if (afterPopoverEnd <= containerEnd || diffStart > diffEnd) return afterPopoverStart;
|
|
50
51
|
}
|
|
51
52
|
return popoverStart;
|
|
52
53
|
},
|
|
53
54
|
after(options) {
|
|
54
55
|
const {
|
|
55
|
-
|
|
56
|
-
|
|
56
|
+
popoverSize,
|
|
57
|
+
containerRect,
|
|
58
|
+
triggerRect,
|
|
57
59
|
gap,
|
|
58
60
|
flip
|
|
59
61
|
} = options;
|
|
60
|
-
const
|
|
61
|
-
const
|
|
62
|
-
const
|
|
63
|
-
const
|
|
64
|
-
const popoverStart =
|
|
65
|
-
const popoverEnd = popoverStart +
|
|
66
|
-
|
|
62
|
+
const containerStart = containerRect[scrollOffsetKey];
|
|
63
|
+
const containerEnd = containerStart + containerRect[sizeKey];
|
|
64
|
+
const triggerStart = containerStart + triggerRect[rectKey];
|
|
65
|
+
const triggerEnd = triggerStart + triggerRect[sizeKey];
|
|
66
|
+
const popoverStart = triggerEnd + gap;
|
|
67
|
+
const popoverEnd = popoverStart + popoverSize[sizeKey];
|
|
68
|
+
console.log(popoverEnd, containerEnd);
|
|
69
|
+
if (flip && popoverEnd > containerEnd) {
|
|
67
70
|
const beforePopoverStart = this.before({
|
|
68
71
|
...options,
|
|
69
72
|
flip: false
|
|
70
73
|
});
|
|
71
|
-
const diffStart =
|
|
72
|
-
const diffEnd = popoverEnd -
|
|
73
|
-
if (beforePopoverStart >=
|
|
74
|
+
const diffStart = containerStart - beforePopoverStart;
|
|
75
|
+
const diffEnd = popoverEnd - containerEnd;
|
|
76
|
+
if (beforePopoverStart >= containerStart || diffEnd > diffStart) return beforePopoverStart;
|
|
74
77
|
}
|
|
75
78
|
return popoverStart;
|
|
76
79
|
},
|
|
77
80
|
start: ({
|
|
78
|
-
|
|
79
|
-
|
|
81
|
+
popoverSize,
|
|
82
|
+
containerRect,
|
|
83
|
+
triggerRect
|
|
80
84
|
}) => {
|
|
81
|
-
const
|
|
82
|
-
const
|
|
83
|
-
return
|
|
84
|
-
|
|
85
|
-
|
|
85
|
+
const containerStart = containerRect[scrollOffsetKey];
|
|
86
|
+
const triggerStart = containerStart + triggerRect[rectKey];
|
|
87
|
+
return fitToContainer(triggerStart, {
|
|
88
|
+
popoverSize,
|
|
89
|
+
containerRect,
|
|
90
|
+
triggerRect
|
|
86
91
|
});
|
|
87
92
|
},
|
|
88
93
|
end: ({
|
|
89
|
-
|
|
90
|
-
|
|
94
|
+
popoverSize,
|
|
95
|
+
containerRect,
|
|
96
|
+
triggerRect
|
|
91
97
|
}) => {
|
|
92
|
-
const
|
|
93
|
-
const
|
|
94
|
-
const
|
|
95
|
-
const popoverStart =
|
|
96
|
-
return
|
|
97
|
-
|
|
98
|
-
|
|
98
|
+
const containerStart = containerRect[scrollOffsetKey];
|
|
99
|
+
const triggerStart = containerStart + triggerRect[rectKey];
|
|
100
|
+
const triggerEnd = triggerStart + triggerRect[sizeKey];
|
|
101
|
+
const popoverStart = triggerEnd - popoverSize[sizeKey];
|
|
102
|
+
return fitToContainer(popoverStart, {
|
|
103
|
+
popoverSize,
|
|
104
|
+
containerRect,
|
|
105
|
+
triggerRect
|
|
99
106
|
});
|
|
100
107
|
},
|
|
101
108
|
center: ({
|
|
102
|
-
|
|
103
|
-
|
|
109
|
+
popoverSize,
|
|
110
|
+
containerRect,
|
|
111
|
+
triggerRect
|
|
104
112
|
}) => {
|
|
105
|
-
const
|
|
106
|
-
const
|
|
107
|
-
const popoverStart =
|
|
108
|
-
return
|
|
109
|
-
|
|
110
|
-
|
|
113
|
+
const containerStart = containerRect[scrollOffsetKey];
|
|
114
|
+
const triggerStart = containerStart + triggerRect[rectKey];
|
|
115
|
+
const popoverStart = triggerStart + (triggerRect[sizeKey] - popoverSize[sizeKey]) / 2;
|
|
116
|
+
return fitToContainer(popoverStart, {
|
|
117
|
+
popoverSize,
|
|
118
|
+
containerRect,
|
|
119
|
+
triggerRect
|
|
111
120
|
});
|
|
112
121
|
}
|
|
113
122
|
};
|
|
@@ -123,12 +132,16 @@ const placementPositionKeysMap = {
|
|
|
123
132
|
right: ['center', 'after'],
|
|
124
133
|
'top-start': ['before', 'start'],
|
|
125
134
|
'top-end': ['before', 'end'],
|
|
135
|
+
'top-full': ['before', 'start'],
|
|
126
136
|
'bottom-start': ['after', 'start'],
|
|
127
137
|
'bottom-end': ['after', 'end'],
|
|
138
|
+
'bottom-full': ['after', 'start'],
|
|
128
139
|
'left-start': ['start', 'before'],
|
|
129
140
|
'left-end': ['end', 'before'],
|
|
141
|
+
'left-full': ['start', 'before'],
|
|
130
142
|
'right-start': ['start', 'after'],
|
|
131
|
-
'right-end': ['end', 'after']
|
|
143
|
+
'right-end': ['end', 'after'],
|
|
144
|
+
'right-full': ['start', 'after']
|
|
132
145
|
};
|
|
133
146
|
/**
|
|
134
147
|
* Computes the position of the popover.
|
|
@@ -136,21 +149,26 @@ const placementPositionKeysMap = {
|
|
|
136
149
|
* In most cases, it will be the window.
|
|
137
150
|
*/
|
|
138
151
|
const usePopoverPosition = ({
|
|
139
|
-
|
|
140
|
-
|
|
152
|
+
popoverSize,
|
|
153
|
+
containerRect,
|
|
154
|
+
triggerRect,
|
|
141
155
|
placement = 'top',
|
|
142
156
|
gap = 0.5,
|
|
143
157
|
flip = true
|
|
144
158
|
}) => {
|
|
145
159
|
const bodyFontSize = useFontSize(document.body);
|
|
146
160
|
const gapPx = useMemo(() => gap * bodyFontSize, [gap, bodyFontSize]);
|
|
147
|
-
const positionKeys = useMemo(() =>
|
|
148
|
-
|
|
149
|
-
return
|
|
150
|
-
|
|
161
|
+
const positionKeys = useMemo(() => typeof placement === 'string' && !!placementPositionKeysMap[placement] ? placementPositionKeysMap[placement] : placementPositionKeysMap.top, [placement]);
|
|
162
|
+
if (!popoverSize || !containerRect || !triggerRect) {
|
|
163
|
+
return {
|
|
164
|
+
top: -10000,
|
|
165
|
+
left: -10000
|
|
166
|
+
};
|
|
167
|
+
}
|
|
151
168
|
return getPopoverPosition(...positionKeys, {
|
|
152
|
-
|
|
153
|
-
|
|
169
|
+
popoverSize,
|
|
170
|
+
containerRect,
|
|
171
|
+
triggerRect,
|
|
154
172
|
gap: gapPx,
|
|
155
173
|
flip
|
|
156
174
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/Select/index.tsx"],"names":[],"mappings":"AAIA,OAAO,EACL,KAAK,QAAQ,EAId,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/Select/index.tsx"],"names":[],"mappings":"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"}
|
package/dist/Select/index.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { css } from '@emotion/react';
|
|
2
2
|
import styled from '@emotion/styled';
|
|
3
3
|
import { Close, CloseCircle, Down, Loading, Up } from '@os-design/icons';
|
|
4
|
-
import {
|
|
4
|
+
import { useIsMinWidth } from '@os-design/media';
|
|
5
5
|
import { ellipsisStyles, resetButtonStyles, transitionStyles } from '@os-design/styles';
|
|
6
6
|
import { ThemeOverrider, clr, useTheme } from '@os-design/theming';
|
|
7
|
-
import { omitEmotionProps, useBrowserLayoutEffect,
|
|
7
|
+
import { omitEmotionProps, useBrowserLayoutEffect, useFontSize, useForwardedRef, useForwardedState, useSize } from '@os-design/utils';
|
|
8
8
|
import React, { forwardRef, useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
9
9
|
import { FixedSizeList } from 'react-window';
|
|
10
10
|
import Button from '../Button/index.js';
|
|
@@ -54,14 +54,10 @@ export const SelectContainer = styled(InputContainer, omitEmotionProps('opened',
|
|
|
54
54
|
${selectContainerUnborderedStyles};
|
|
55
55
|
${selectContainerUnborderedHoverStyles};
|
|
56
56
|
`;
|
|
57
|
-
const SelectMenu = styled(Menu
|
|
57
|
+
const SelectMenu = styled(Menu)`
|
|
58
58
|
padding-top: 0;
|
|
59
59
|
padding-bottom: 0;
|
|
60
60
|
max-height: unset;
|
|
61
|
-
|
|
62
|
-
${m.min.xs} {
|
|
63
|
-
width: ${p => p.width}px;
|
|
64
|
-
}
|
|
65
61
|
`;
|
|
66
62
|
const NotFound = styled.div`
|
|
67
63
|
height: ${p => p.theme.menuItemHeight}em;
|
|
@@ -219,11 +215,10 @@ const Select = /*#__PURE__*/forwardRef(({
|
|
|
219
215
|
onClose = () => {},
|
|
220
216
|
onBlur = () => {},
|
|
221
217
|
size,
|
|
222
|
-
placement,
|
|
218
|
+
placement = 'bottom-full',
|
|
223
219
|
...rest
|
|
224
220
|
}, ref) => {
|
|
225
221
|
const [containerRef, mergedContainerRef] = useForwardedRef(ref);
|
|
226
|
-
const [width, setWidth] = useState(0);
|
|
227
222
|
const [opened, setOpened] = useState(autoOpen);
|
|
228
223
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
229
224
|
const [forwardedValue, setForwardedValue] = useForwardedState({
|
|
@@ -247,22 +242,6 @@ const Select = /*#__PURE__*/forwardRef(({
|
|
|
247
242
|
if (!opened) onCloseRef.current();
|
|
248
243
|
}, [opened]);
|
|
249
244
|
|
|
250
|
-
/**
|
|
251
|
-
* Detect the width of the container when the select was opened and update
|
|
252
|
-
* it when either the container size or the window size has been changed.
|
|
253
|
-
*/
|
|
254
|
-
const resizeHandler = useCallback(() => {
|
|
255
|
-
window.requestAnimationFrame(() => {
|
|
256
|
-
if (!opened || !containerRef.current) return;
|
|
257
|
-
const nextWidth = containerRef.current.getBoundingClientRect().width;
|
|
258
|
-
if (width === nextWidth) return;
|
|
259
|
-
setWidth(nextWidth);
|
|
260
|
-
});
|
|
261
|
-
}, [opened, containerRef, width]);
|
|
262
|
-
useBrowserLayoutEffect(() => resizeHandler(), [resizeHandler]);
|
|
263
|
-
useResizeObserver(containerRef, resizeHandler);
|
|
264
|
-
useEvent(typeof window !== 'undefined' ? window : undefined, 'resize', resizeHandler);
|
|
265
|
-
|
|
266
245
|
// Replace the aria-haspopup attribute from menu to listbox
|
|
267
246
|
useBrowserLayoutEffect(() => {
|
|
268
247
|
if (!containerRef.current) return;
|
|
@@ -529,7 +508,6 @@ const Select = /*#__PURE__*/forwardRef(({
|
|
|
529
508
|
visible: opened,
|
|
530
509
|
onClose: () => setOpened(false),
|
|
531
510
|
size: size,
|
|
532
|
-
width: width,
|
|
533
511
|
closeOnSelect: !multiple,
|
|
534
512
|
modalTitle: placeholder,
|
|
535
513
|
placement: placement,
|