@rc-component/trigger 3.4.1 → 3.5.1
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/assets/index/Mobile.less +36 -6
- package/assets/index.css +28 -4
- package/es/Popup/Mask.d.ts +1 -0
- package/es/Popup/Mask.js +3 -2
- package/es/Popup/index.d.ts +8 -0
- package/es/Popup/index.js +22 -6
- package/es/hooks/useAction.d.ts +3 -2
- package/es/hooks/useAction.js +7 -11
- package/es/hooks/useAlign.d.ts +1 -1
- package/es/hooks/useAlign.js +9 -3
- package/es/hooks/useWinClick.d.ts +4 -0
- package/es/hooks/useWinClick.js +5 -0
- package/es/index.d.ts +8 -0
- package/es/index.js +52 -21
- package/es/interface.d.ts +0 -8
- package/lib/Popup/Mask.d.ts +1 -0
- package/lib/Popup/Mask.js +3 -2
- package/lib/Popup/index.d.ts +8 -0
- package/lib/Popup/index.js +22 -6
- package/lib/hooks/useAction.d.ts +3 -2
- package/lib/hooks/useAction.js +7 -11
- package/lib/hooks/useAlign.d.ts +1 -1
- package/lib/hooks/useAlign.js +9 -3
- package/lib/hooks/useWinClick.d.ts +4 -0
- package/lib/hooks/useWinClick.js +4 -0
- package/lib/index.d.ts +8 -0
- package/lib/index.js +52 -21
- package/lib/interface.d.ts +0 -8
- package/package.json +3 -3
package/assets/index/Mobile.less
CHANGED
|
@@ -1,25 +1,55 @@
|
|
|
1
1
|
.@{triggerPrefixCls} {
|
|
2
2
|
&-mobile {
|
|
3
|
-
transition: all 0.3s;
|
|
4
3
|
position: fixed;
|
|
5
4
|
left: 0;
|
|
6
5
|
right: 0;
|
|
7
6
|
bottom: 0;
|
|
8
7
|
top: auto;
|
|
9
8
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
&-enter
|
|
13
|
-
|
|
14
|
-
|
|
9
|
+
// Motion
|
|
10
|
+
&.raise {
|
|
11
|
+
&-enter,
|
|
12
|
+
&-appear {
|
|
13
|
+
transform: translateY(100%);
|
|
14
|
+
|
|
15
|
+
&-active {
|
|
16
|
+
transition: all 0.3s;
|
|
17
|
+
transform: translateY(0);
|
|
15
18
|
}
|
|
16
19
|
}
|
|
17
20
|
|
|
18
21
|
&-leave {
|
|
22
|
+
transform: translateY(0);
|
|
23
|
+
|
|
19
24
|
&-active {
|
|
25
|
+
transition: all 0.3s;
|
|
20
26
|
transform: translateY(100%);
|
|
21
27
|
}
|
|
22
28
|
}
|
|
23
29
|
}
|
|
30
|
+
|
|
31
|
+
// Mask
|
|
32
|
+
&-mask {
|
|
33
|
+
&.fade {
|
|
34
|
+
&-enter,
|
|
35
|
+
&-appear {
|
|
36
|
+
opacity: 0;
|
|
37
|
+
|
|
38
|
+
&-active {
|
|
39
|
+
transition: all 0.3s;
|
|
40
|
+
opacity: 1;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
&-leave {
|
|
45
|
+
opacity: 1;
|
|
46
|
+
|
|
47
|
+
&-active {
|
|
48
|
+
transition: all 0.3s;
|
|
49
|
+
opacity: 0;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
24
54
|
}
|
|
25
55
|
}
|
package/assets/index.css
CHANGED
|
@@ -117,17 +117,41 @@
|
|
|
117
117
|
}
|
|
118
118
|
}
|
|
119
119
|
.rc-trigger-popup-mobile {
|
|
120
|
-
transition: all 0.3s;
|
|
121
120
|
position: fixed;
|
|
122
121
|
left: 0;
|
|
123
122
|
right: 0;
|
|
124
123
|
bottom: 0;
|
|
125
124
|
top: auto;
|
|
126
125
|
}
|
|
127
|
-
.rc-trigger-popup-mobile-
|
|
128
|
-
.rc-trigger-popup-mobile-
|
|
126
|
+
.rc-trigger-popup-mobile.raise-enter,
|
|
127
|
+
.rc-trigger-popup-mobile.raise-appear {
|
|
129
128
|
transform: translateY(100%);
|
|
130
129
|
}
|
|
131
|
-
.rc-trigger-popup-mobile-
|
|
130
|
+
.rc-trigger-popup-mobile.raise-enter-active,
|
|
131
|
+
.rc-trigger-popup-mobile.raise-appear-active {
|
|
132
|
+
transition: all 0.3s;
|
|
133
|
+
transform: translateY(0);
|
|
134
|
+
}
|
|
135
|
+
.rc-trigger-popup-mobile.raise-leave {
|
|
136
|
+
transform: translateY(0);
|
|
137
|
+
}
|
|
138
|
+
.rc-trigger-popup-mobile.raise-leave-active {
|
|
139
|
+
transition: all 0.3s;
|
|
132
140
|
transform: translateY(100%);
|
|
133
141
|
}
|
|
142
|
+
.rc-trigger-popup-mobile-mask.fade-enter,
|
|
143
|
+
.rc-trigger-popup-mobile-mask.fade-appear {
|
|
144
|
+
opacity: 0;
|
|
145
|
+
}
|
|
146
|
+
.rc-trigger-popup-mobile-mask.fade-enter-active,
|
|
147
|
+
.rc-trigger-popup-mobile-mask.fade-appear-active {
|
|
148
|
+
transition: all 0.3s;
|
|
149
|
+
opacity: 1;
|
|
150
|
+
}
|
|
151
|
+
.rc-trigger-popup-mobile-mask.fade-leave {
|
|
152
|
+
opacity: 1;
|
|
153
|
+
}
|
|
154
|
+
.rc-trigger-popup-mobile-mask.fade-leave-active {
|
|
155
|
+
transition: all 0.3s;
|
|
156
|
+
opacity: 0;
|
|
157
|
+
}
|
package/es/Popup/Mask.d.ts
CHANGED
package/es/Popup/Mask.js
CHANGED
|
@@ -8,7 +8,8 @@ export default function Mask(props) {
|
|
|
8
8
|
open,
|
|
9
9
|
zIndex,
|
|
10
10
|
mask,
|
|
11
|
-
motion
|
|
11
|
+
motion,
|
|
12
|
+
mobile
|
|
12
13
|
} = props;
|
|
13
14
|
if (!mask) {
|
|
14
15
|
return null;
|
|
@@ -23,6 +24,6 @@ export default function Mask(props) {
|
|
|
23
24
|
style: {
|
|
24
25
|
zIndex
|
|
25
26
|
},
|
|
26
|
-
className: classNames(`${prefixCls}-mask`, className)
|
|
27
|
+
className: classNames(`${prefixCls}-mask`, mobile && `${prefixCls}-mobile-mask`, className)
|
|
27
28
|
}));
|
|
28
29
|
}
|
package/es/Popup/index.d.ts
CHANGED
|
@@ -2,6 +2,13 @@ import type { CSSMotionProps } from '@rc-component/motion';
|
|
|
2
2
|
import * as React from 'react';
|
|
3
3
|
import type { TriggerProps } from '../';
|
|
4
4
|
import type { AlignType, ArrowPos, ArrowTypeOuter } from '../interface';
|
|
5
|
+
export interface MobileConfig {
|
|
6
|
+
mask?: boolean;
|
|
7
|
+
/** Set popup motion. You can ref `rc-motion` for more info. */
|
|
8
|
+
motion?: CSSMotionProps;
|
|
9
|
+
/** Set mask motion. You can ref `rc-motion` for more info. */
|
|
10
|
+
maskMotion?: CSSMotionProps;
|
|
11
|
+
}
|
|
5
12
|
export interface PopupProps {
|
|
6
13
|
prefixCls: string;
|
|
7
14
|
className?: string;
|
|
@@ -39,6 +46,7 @@ export interface PopupProps {
|
|
|
39
46
|
stretch?: string;
|
|
40
47
|
targetWidth?: number;
|
|
41
48
|
targetHeight?: number;
|
|
49
|
+
mobile?: MobileConfig;
|
|
42
50
|
}
|
|
43
51
|
declare const Popup: React.ForwardRefExoticComponent<PopupProps & React.RefAttributes<HTMLDivElement>>;
|
|
44
52
|
export default Popup;
|
package/es/Popup/index.js
CHANGED
|
@@ -31,6 +31,8 @@ const Popup = /*#__PURE__*/React.forwardRef((props, ref) => {
|
|
|
31
31
|
// Motion
|
|
32
32
|
motion,
|
|
33
33
|
maskMotion,
|
|
34
|
+
// Mobile
|
|
35
|
+
mobile,
|
|
34
36
|
// Portal
|
|
35
37
|
forceRender,
|
|
36
38
|
getPopupContainer,
|
|
@@ -57,6 +59,17 @@ const Popup = /*#__PURE__*/React.forwardRef((props, ref) => {
|
|
|
57
59
|
// We can not remove holder only when motion finished.
|
|
58
60
|
const isNodeVisible = open || keepDom;
|
|
59
61
|
|
|
62
|
+
// ========================= Mobile =========================
|
|
63
|
+
const isMobile = !!mobile;
|
|
64
|
+
|
|
65
|
+
// ========================== Mask ==========================
|
|
66
|
+
const [mergedMask, mergedMaskMotion, mergedPopupMotion] = React.useMemo(() => {
|
|
67
|
+
if (mobile) {
|
|
68
|
+
return [mobile.mask, mobile.maskMotion, mobile.motion];
|
|
69
|
+
}
|
|
70
|
+
return [mask, maskMotion, motion];
|
|
71
|
+
}, [mobile, mask, maskMotion, motion]);
|
|
72
|
+
|
|
60
73
|
// ======================= Container ========================
|
|
61
74
|
const getPopupContainerNeedParams = getPopupContainer?.length > 0;
|
|
62
75
|
const [show, setShow] = React.useState(!getPopupContainer || !getPopupContainerNeedParams);
|
|
@@ -75,7 +88,7 @@ const Popup = /*#__PURE__*/React.forwardRef((props, ref) => {
|
|
|
75
88
|
|
|
76
89
|
// >>>>> Offset
|
|
77
90
|
const AUTO = 'auto';
|
|
78
|
-
const offsetStyle = {
|
|
91
|
+
const offsetStyle = isMobile ? {} : {
|
|
79
92
|
left: '-1000vw',
|
|
80
93
|
top: '-1000vh',
|
|
81
94
|
right: AUTO,
|
|
@@ -83,7 +96,7 @@ const Popup = /*#__PURE__*/React.forwardRef((props, ref) => {
|
|
|
83
96
|
};
|
|
84
97
|
|
|
85
98
|
// Set align style
|
|
86
|
-
if (ready || !open) {
|
|
99
|
+
if (!isMobile && (ready || !open)) {
|
|
87
100
|
const {
|
|
88
101
|
points
|
|
89
102
|
} = align;
|
|
@@ -131,8 +144,9 @@ const Popup = /*#__PURE__*/React.forwardRef((props, ref) => {
|
|
|
131
144
|
prefixCls: prefixCls,
|
|
132
145
|
open: open,
|
|
133
146
|
zIndex: zIndex,
|
|
134
|
-
mask:
|
|
135
|
-
motion:
|
|
147
|
+
mask: mergedMask,
|
|
148
|
+
motion: mergedMaskMotion,
|
|
149
|
+
mobile: isMobile
|
|
136
150
|
}), /*#__PURE__*/React.createElement(ResizeObserver, {
|
|
137
151
|
onResize: onAlign,
|
|
138
152
|
disabled: !open
|
|
@@ -144,7 +158,7 @@ const Popup = /*#__PURE__*/React.forwardRef((props, ref) => {
|
|
|
144
158
|
removeOnLeave: false,
|
|
145
159
|
forceRender: forceRender,
|
|
146
160
|
leavedClassName: `${prefixCls}-hidden`
|
|
147
|
-
},
|
|
161
|
+
}, mergedPopupMotion, {
|
|
148
162
|
onAppearPrepare: onPrepare,
|
|
149
163
|
onEnterPrepare: onPrepare,
|
|
150
164
|
visible: open,
|
|
@@ -156,7 +170,9 @@ const Popup = /*#__PURE__*/React.forwardRef((props, ref) => {
|
|
|
156
170
|
className: motionClassName,
|
|
157
171
|
style: motionStyle
|
|
158
172
|
}, motionRef) => {
|
|
159
|
-
const cls = classNames(prefixCls, motionClassName, className
|
|
173
|
+
const cls = classNames(prefixCls, motionClassName, className, {
|
|
174
|
+
[`${prefixCls}-mobile`]: isMobile
|
|
175
|
+
});
|
|
160
176
|
return /*#__PURE__*/React.createElement("div", {
|
|
161
177
|
ref: composeRef(resizeObserverRef, ref, motionRef),
|
|
162
178
|
className: cls,
|
package/es/hooks/useAction.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ActionType } from '../interface';
|
|
2
|
-
type
|
|
3
|
-
|
|
2
|
+
type InternalActionType = ActionType | 'touch';
|
|
3
|
+
type ActionTypes = InternalActionType | InternalActionType[];
|
|
4
|
+
export default function useAction(action: ActionTypes, showAction?: ActionTypes, hideAction?: ActionTypes): [showAction: Set<InternalActionType>, hideAction: Set<InternalActionType>];
|
|
4
5
|
export {};
|
package/es/hooks/useAction.js
CHANGED
|
@@ -2,22 +2,18 @@ import * as React from 'react';
|
|
|
2
2
|
function toArray(val) {
|
|
3
3
|
return val ? Array.isArray(val) ? val : [val] : [];
|
|
4
4
|
}
|
|
5
|
-
export default function useAction(
|
|
5
|
+
export default function useAction(action, showAction, hideAction) {
|
|
6
6
|
return React.useMemo(() => {
|
|
7
7
|
const mergedShowAction = toArray(showAction ?? action);
|
|
8
8
|
const mergedHideAction = toArray(hideAction ?? action);
|
|
9
9
|
const showActionSet = new Set(mergedShowAction);
|
|
10
10
|
const hideActionSet = new Set(mergedHideAction);
|
|
11
|
-
if (
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
if (hideActionSet.has('hover')) {
|
|
17
|
-
hideActionSet.delete('hover');
|
|
18
|
-
hideActionSet.add('click');
|
|
19
|
-
}
|
|
11
|
+
if (showActionSet.has('hover') && !showActionSet.has('click')) {
|
|
12
|
+
showActionSet.add('touch');
|
|
13
|
+
}
|
|
14
|
+
if (hideActionSet.has('hover') && !hideActionSet.has('click')) {
|
|
15
|
+
hideActionSet.add('touch');
|
|
20
16
|
}
|
|
21
17
|
return [showActionSet, hideActionSet];
|
|
22
|
-
}, [
|
|
18
|
+
}, [action, showAction, hideAction]);
|
|
23
19
|
}
|
package/es/hooks/useAlign.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { TriggerProps } from '..';
|
|
2
2
|
import type { AlignType } from '../interface';
|
|
3
|
-
export default function useAlign(open: boolean, popupEle: HTMLElement, target: HTMLElement | [x: number, y: number], placement: string, builtinPlacements: any, popupAlign?: AlignType, onPopupAlign?: TriggerProps['onPopupAlign']): [
|
|
3
|
+
export default function useAlign(open: boolean, popupEle: HTMLElement, target: HTMLElement | [x: number, y: number], placement: string, builtinPlacements: any, popupAlign?: AlignType, onPopupAlign?: TriggerProps['onPopupAlign'], mobile?: boolean): [
|
|
4
4
|
ready: boolean,
|
|
5
5
|
offsetX: number,
|
|
6
6
|
offsetY: number,
|
package/es/hooks/useAlign.js
CHANGED
|
@@ -61,7 +61,7 @@ function reversePoints(points, index) {
|
|
|
61
61
|
return point;
|
|
62
62
|
}).join('');
|
|
63
63
|
}
|
|
64
|
-
export default function useAlign(open, popupEle, target, placement, builtinPlacements, popupAlign, onPopupAlign) {
|
|
64
|
+
export default function useAlign(open, popupEle, target, placement, builtinPlacements, popupAlign, onPopupAlign, mobile) {
|
|
65
65
|
const [offsetInfo, setOffsetInfo] = React.useState({
|
|
66
66
|
ready: false,
|
|
67
67
|
offsetX: 0,
|
|
@@ -76,7 +76,7 @@ export default function useAlign(open, popupEle, target, placement, builtinPlace
|
|
|
76
76
|
});
|
|
77
77
|
const alignCountRef = React.useRef(0);
|
|
78
78
|
const scrollerList = React.useMemo(() => {
|
|
79
|
-
if (!popupEle) {
|
|
79
|
+
if (!popupEle || mobile) {
|
|
80
80
|
return [];
|
|
81
81
|
}
|
|
82
82
|
return collectScroller(popupEle);
|
|
@@ -95,7 +95,7 @@ export default function useAlign(open, popupEle, target, placement, builtinPlace
|
|
|
95
95
|
|
|
96
96
|
// ========================= Align =========================
|
|
97
97
|
const onAlign = useEvent(() => {
|
|
98
|
-
if (popupEle && target && open) {
|
|
98
|
+
if (popupEle && target && open && !mobile) {
|
|
99
99
|
const popupElement = popupEle;
|
|
100
100
|
const doc = popupElement.ownerDocument;
|
|
101
101
|
const win = getWin(popupElement);
|
|
@@ -458,9 +458,15 @@ export default function useAlign(open, popupEle, target, placement, builtinPlace
|
|
|
458
458
|
const targetRight = targetLeft + targetWidth;
|
|
459
459
|
const targetTop = targetRect.y;
|
|
460
460
|
const targetBottom = targetTop + targetHeight;
|
|
461
|
+
|
|
462
|
+
/** Max left of the popup and target element */
|
|
461
463
|
const maxLeft = Math.max(popupLeft, targetLeft);
|
|
464
|
+
/** Min right of the popup and target element */
|
|
462
465
|
const minRight = Math.min(popupRight, targetRight);
|
|
466
|
+
|
|
467
|
+
/** The center X of popup & target cross area */
|
|
463
468
|
const xCenter = (maxLeft + minRight) / 2;
|
|
469
|
+
/** Arrow X of popup offset */
|
|
464
470
|
const nextArrowX = xCenter - popupLeft;
|
|
465
471
|
const maxTop = Math.max(popupTop, targetTop);
|
|
466
472
|
const minBottom = Math.min(popupBottom, targetBottom);
|
|
@@ -1 +1,5 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Close if click on the window.
|
|
3
|
+
* Return the function that click on the Popup element.
|
|
4
|
+
*/
|
|
1
5
|
export default function useWinClick(open: boolean, clickToHide: boolean, targetEle: HTMLElement, popupEle: HTMLElement, mask: boolean, maskClosable: boolean, inPopupOrChild: (target: EventTarget) => boolean, triggerOpen: (open: boolean) => void): () => void;
|
package/es/hooks/useWinClick.js
CHANGED
|
@@ -2,6 +2,11 @@ import { getShadowRoot } from "@rc-component/util/es/Dom/shadow";
|
|
|
2
2
|
import { warning } from "@rc-component/util/es/warning";
|
|
3
3
|
import * as React from 'react';
|
|
4
4
|
import { getWin } from "../util";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Close if click on the window.
|
|
8
|
+
* Return the function that click on the Popup element.
|
|
9
|
+
*/
|
|
5
10
|
export default function useWinClick(open, clickToHide, targetEle, popupEle, mask, maskClosable, inPopupOrChild, triggerOpen) {
|
|
6
11
|
const openRef = React.useRef(open);
|
|
7
12
|
openRef.current = open;
|
package/es/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { CSSMotionProps } from '@rc-component/motion';
|
|
2
2
|
import * as React from 'react';
|
|
3
|
+
import { type MobileConfig } from './Popup';
|
|
3
4
|
import type { ActionType, AlignType, ArrowTypeOuter, BuildInPlacements } from './interface';
|
|
4
5
|
export type { ActionType, AlignType, ArrowTypeOuter as ArrowType, BuildInPlacements, };
|
|
5
6
|
export interface TriggerRef {
|
|
@@ -53,6 +54,13 @@ export interface TriggerProps {
|
|
|
53
54
|
*/
|
|
54
55
|
fresh?: boolean;
|
|
55
56
|
arrow?: boolean | ArrowTypeOuter;
|
|
57
|
+
/**
|
|
58
|
+
* @private Bump fixed position at bottom in mobile.
|
|
59
|
+
* Will replace the config of root props.
|
|
60
|
+
* This will directly trade as mobile view which will not check what real is.
|
|
61
|
+
* This is internal usage currently, do not use in your prod.
|
|
62
|
+
*/
|
|
63
|
+
mobile?: MobileConfig;
|
|
56
64
|
}
|
|
57
65
|
export declare function generateTrigger(PortalComponent?: React.ComponentType<any>): React.ForwardRefExoticComponent<TriggerProps & React.RefAttributes<TriggerRef>>;
|
|
58
66
|
declare const _default: React.ForwardRefExoticComponent<TriggerProps & React.RefAttributes<TriggerRef>>;
|
package/es/index.js
CHANGED
|
@@ -6,7 +6,6 @@ import { getShadowRoot } from "@rc-component/util/es/Dom/shadow";
|
|
|
6
6
|
import useEvent from "@rc-component/util/es/hooks/useEvent";
|
|
7
7
|
import useId from "@rc-component/util/es/hooks/useId";
|
|
8
8
|
import useLayoutEffect from "@rc-component/util/es/hooks/useLayoutEffect";
|
|
9
|
-
import isMobile from "@rc-component/util/es/isMobile";
|
|
10
9
|
import * as React from 'react';
|
|
11
10
|
import Popup from "./Popup";
|
|
12
11
|
import TriggerContext from "./context";
|
|
@@ -69,15 +68,14 @@ export function generateTrigger(PortalComponent = Portal) {
|
|
|
69
68
|
// Motion
|
|
70
69
|
popupMotion,
|
|
71
70
|
maskMotion,
|
|
71
|
+
// Private
|
|
72
|
+
mobile,
|
|
72
73
|
...restProps
|
|
73
74
|
} = props;
|
|
74
75
|
const mergedAutoDestroy = autoDestroy || false;
|
|
75
76
|
|
|
76
77
|
// =========================== Mobile ===========================
|
|
77
|
-
const
|
|
78
|
-
useLayoutEffect(() => {
|
|
79
|
-
setMobile(isMobile());
|
|
80
|
-
}, []);
|
|
78
|
+
const isMobile = !!mobile;
|
|
81
79
|
|
|
82
80
|
// ========================== Context ===========================
|
|
83
81
|
const subPopupElements = React.useRef({});
|
|
@@ -190,8 +188,8 @@ export function generateTrigger(PortalComponent = Portal) {
|
|
|
190
188
|
const setMousePosByEvent = event => {
|
|
191
189
|
setMousePos([event.clientX, event.clientY]);
|
|
192
190
|
};
|
|
193
|
-
const [ready, offsetX, offsetY, offsetR, offsetB, arrowX, arrowY, scaleX, scaleY, alignInfo, onAlign] = useAlign(mergedOpen, popupEle, alignPoint && mousePos !== null ? mousePos : targetEle, popupPlacement, builtinPlacements, popupAlign, onPopupAlign);
|
|
194
|
-
const [showActions, hideActions] = useAction(
|
|
191
|
+
const [ready, offsetX, offsetY, offsetR, offsetB, arrowX, arrowY, scaleX, scaleY, alignInfo, onAlign] = useAlign(mergedOpen, popupEle, alignPoint && mousePos !== null ? mousePos : targetEle, popupPlacement, builtinPlacements, popupAlign, onPopupAlign, isMobile);
|
|
192
|
+
const [showActions, hideActions] = useAction(action, showAction, hideAction);
|
|
195
193
|
const clickToShow = showActions.has('click');
|
|
196
194
|
const clickToHide = hideActions.has('click') || hideActions.has('contextMenu');
|
|
197
195
|
const triggerAlign = useEvent(() => {
|
|
@@ -266,17 +264,44 @@ export function generateTrigger(PortalComponent = Portal) {
|
|
|
266
264
|
// =========================== Action ===========================
|
|
267
265
|
/**
|
|
268
266
|
* Util wrapper for trigger action
|
|
267
|
+
* @param eventName Listen event name
|
|
268
|
+
* @param nextOpen Next open state after trigger
|
|
269
|
+
* @param delay Delay to trigger open change
|
|
270
|
+
* @param callback Callback if current event need additional action
|
|
271
|
+
* @param ignoreCheck Ignore current event if check return true
|
|
269
272
|
*/
|
|
270
|
-
function wrapperAction(eventName, nextOpen, delay,
|
|
273
|
+
function wrapperAction(eventName, nextOpen, delay, callback, ignoreCheck) {
|
|
271
274
|
cloneProps[eventName] = (event, ...args) => {
|
|
272
|
-
|
|
273
|
-
|
|
275
|
+
if (!ignoreCheck || !ignoreCheck()) {
|
|
276
|
+
callback?.(event);
|
|
277
|
+
triggerOpen(nextOpen, delay);
|
|
278
|
+
}
|
|
274
279
|
|
|
275
280
|
// Pass to origin
|
|
276
281
|
originChildProps[eventName]?.(event, ...args);
|
|
277
282
|
};
|
|
278
283
|
}
|
|
279
284
|
|
|
285
|
+
// ======================= Action: Touch ========================
|
|
286
|
+
const touchToShow = showActions.has('touch');
|
|
287
|
+
const touchToHide = hideActions.has('touch');
|
|
288
|
+
|
|
289
|
+
/** Used for prevent `hover` event conflict with mobile env */
|
|
290
|
+
const touchedRef = React.useRef(false);
|
|
291
|
+
if (touchToShow || touchToHide) {
|
|
292
|
+
cloneProps.onTouchStart = (...args) => {
|
|
293
|
+
touchedRef.current = true;
|
|
294
|
+
if (openRef.current && touchToHide) {
|
|
295
|
+
triggerOpen(false);
|
|
296
|
+
} else if (!openRef.current && touchToShow) {
|
|
297
|
+
triggerOpen(true);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// Pass to origin
|
|
301
|
+
originChildProps.onTouchStart?.(...args);
|
|
302
|
+
};
|
|
303
|
+
}
|
|
304
|
+
|
|
280
305
|
// ======================= Action: Click ========================
|
|
281
306
|
if (clickToShow || clickToHide) {
|
|
282
307
|
cloneProps.onClick = (event, ...args) => {
|
|
@@ -289,25 +314,29 @@ export function generateTrigger(PortalComponent = Portal) {
|
|
|
289
314
|
|
|
290
315
|
// Pass to origin
|
|
291
316
|
originChildProps.onClick?.(event, ...args);
|
|
317
|
+
touchedRef.current = false;
|
|
292
318
|
};
|
|
293
319
|
}
|
|
294
320
|
|
|
295
321
|
// Click to hide is special action since click popup element should not hide
|
|
296
|
-
const onPopupPointerDown = useWinClick(mergedOpen, clickToHide, targetEle, popupEle, mask, maskClosable, inPopupOrChild, triggerOpen);
|
|
322
|
+
const onPopupPointerDown = useWinClick(mergedOpen, clickToHide || touchToHide, targetEle, popupEle, mask, maskClosable, inPopupOrChild, triggerOpen);
|
|
297
323
|
|
|
298
324
|
// ======================= Action: Hover ========================
|
|
299
325
|
const hoverToShow = showActions.has('hover');
|
|
300
326
|
const hoverToHide = hideActions.has('hover');
|
|
301
327
|
let onPopupMouseEnter;
|
|
302
328
|
let onPopupMouseLeave;
|
|
329
|
+
const ignoreMouseTrigger = () => {
|
|
330
|
+
return touchedRef.current;
|
|
331
|
+
};
|
|
303
332
|
if (hoverToShow) {
|
|
304
|
-
|
|
305
|
-
wrapperAction('onMouseEnter', true, mouseEnterDelay, event => {
|
|
306
|
-
setMousePosByEvent(event);
|
|
307
|
-
});
|
|
308
|
-
wrapperAction('onPointerEnter', true, mouseEnterDelay, event => {
|
|
333
|
+
const onMouseEnterCallback = event => {
|
|
309
334
|
setMousePosByEvent(event);
|
|
310
|
-
}
|
|
335
|
+
};
|
|
336
|
+
|
|
337
|
+
// Compatible with old browser which not support pointer event
|
|
338
|
+
wrapperAction('onMouseEnter', true, mouseEnterDelay, onMouseEnterCallback, ignoreMouseTrigger);
|
|
339
|
+
wrapperAction('onPointerEnter', true, mouseEnterDelay, onMouseEnterCallback, ignoreMouseTrigger);
|
|
311
340
|
onPopupMouseEnter = event => {
|
|
312
341
|
// Only trigger re-open when popup is visible
|
|
313
342
|
if ((mergedOpen || inMotion) && popupEle?.contains(event.target)) {
|
|
@@ -318,14 +347,13 @@ export function generateTrigger(PortalComponent = Portal) {
|
|
|
318
347
|
// Align Point
|
|
319
348
|
if (alignPoint) {
|
|
320
349
|
cloneProps.onMouseMove = event => {
|
|
321
|
-
// setMousePosByEvent(event);
|
|
322
350
|
originChildProps.onMouseMove?.(event);
|
|
323
351
|
};
|
|
324
352
|
}
|
|
325
353
|
}
|
|
326
354
|
if (hoverToHide) {
|
|
327
|
-
wrapperAction('onMouseLeave', false, mouseLeaveDelay);
|
|
328
|
-
wrapperAction('onPointerLeave', false, mouseLeaveDelay);
|
|
355
|
+
wrapperAction('onMouseLeave', false, mouseLeaveDelay, undefined, ignoreMouseTrigger);
|
|
356
|
+
wrapperAction('onPointerLeave', false, mouseLeaveDelay, undefined, ignoreMouseTrigger);
|
|
329
357
|
onPopupMouseLeave = () => {
|
|
330
358
|
triggerOpen(false, mouseLeaveDelay);
|
|
331
359
|
};
|
|
@@ -403,7 +431,7 @@ export function generateTrigger(PortalComponent = Portal) {
|
|
|
403
431
|
ref: setPopupRef,
|
|
404
432
|
prefixCls: prefixCls,
|
|
405
433
|
popup: popup,
|
|
406
|
-
className: classNames(popupClassName, alignedClassName),
|
|
434
|
+
className: classNames(popupClassName, !isMobile && alignedClassName),
|
|
407
435
|
style: popupStyle,
|
|
408
436
|
target: targetEle,
|
|
409
437
|
onMouseEnter: onPopupMouseEnter,
|
|
@@ -453,6 +481,9 @@ export function generateTrigger(PortalComponent = Portal) {
|
|
|
453
481
|
stretch: stretch,
|
|
454
482
|
targetWidth: targetWidth / scaleX,
|
|
455
483
|
targetHeight: targetHeight / scaleY
|
|
484
|
+
// Mobile
|
|
485
|
+
,
|
|
486
|
+
mobile: mobile
|
|
456
487
|
})));
|
|
457
488
|
});
|
|
458
489
|
if (process.env.NODE_ENV !== 'production') {
|
package/es/interface.d.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
/// <reference types="react" />
|
|
2
|
-
import type { CSSMotionProps } from '@rc-component/motion';
|
|
3
2
|
export type Placement = 'top' | 'left' | 'right' | 'bottom' | 'topLeft' | 'topRight' | 'bottomLeft' | 'bottomRight' | 'leftTop' | 'leftBottom' | 'rightTop' | 'rightBottom';
|
|
4
3
|
export type AlignPointTopBottom = 't' | 'b' | 'c';
|
|
5
4
|
export type AlignPointLeftRight = 'l' | 'r' | 'c';
|
|
@@ -90,10 +89,3 @@ export interface Point {
|
|
|
90
89
|
export interface CommonEventHandler {
|
|
91
90
|
remove: () => void;
|
|
92
91
|
}
|
|
93
|
-
export interface MobileConfig {
|
|
94
|
-
/** Set popup motion. You can ref `rc-motion` for more info. */
|
|
95
|
-
popupMotion?: CSSMotionProps;
|
|
96
|
-
popupClassName?: string;
|
|
97
|
-
popupStyle?: React.CSSProperties;
|
|
98
|
-
popupRender?: (originNode: React.ReactNode) => React.ReactNode;
|
|
99
|
-
}
|
package/lib/Popup/Mask.d.ts
CHANGED
package/lib/Popup/Mask.js
CHANGED
|
@@ -17,7 +17,8 @@ function Mask(props) {
|
|
|
17
17
|
open,
|
|
18
18
|
zIndex,
|
|
19
19
|
mask,
|
|
20
|
-
motion
|
|
20
|
+
motion,
|
|
21
|
+
mobile
|
|
21
22
|
} = props;
|
|
22
23
|
if (!mask) {
|
|
23
24
|
return null;
|
|
@@ -32,6 +33,6 @@ function Mask(props) {
|
|
|
32
33
|
style: {
|
|
33
34
|
zIndex
|
|
34
35
|
},
|
|
35
|
-
className: (0, _classnames.default)(`${prefixCls}-mask`, className)
|
|
36
|
+
className: (0, _classnames.default)(`${prefixCls}-mask`, mobile && `${prefixCls}-mobile-mask`, className)
|
|
36
37
|
}));
|
|
37
38
|
}
|
package/lib/Popup/index.d.ts
CHANGED
|
@@ -2,6 +2,13 @@ import type { CSSMotionProps } from '@rc-component/motion';
|
|
|
2
2
|
import * as React from 'react';
|
|
3
3
|
import type { TriggerProps } from '../';
|
|
4
4
|
import type { AlignType, ArrowPos, ArrowTypeOuter } from '../interface';
|
|
5
|
+
export interface MobileConfig {
|
|
6
|
+
mask?: boolean;
|
|
7
|
+
/** Set popup motion. You can ref `rc-motion` for more info. */
|
|
8
|
+
motion?: CSSMotionProps;
|
|
9
|
+
/** Set mask motion. You can ref `rc-motion` for more info. */
|
|
10
|
+
maskMotion?: CSSMotionProps;
|
|
11
|
+
}
|
|
5
12
|
export interface PopupProps {
|
|
6
13
|
prefixCls: string;
|
|
7
14
|
className?: string;
|
|
@@ -39,6 +46,7 @@ export interface PopupProps {
|
|
|
39
46
|
stretch?: string;
|
|
40
47
|
targetWidth?: number;
|
|
41
48
|
targetHeight?: number;
|
|
49
|
+
mobile?: MobileConfig;
|
|
42
50
|
}
|
|
43
51
|
declare const Popup: React.ForwardRefExoticComponent<PopupProps & React.RefAttributes<HTMLDivElement>>;
|
|
44
52
|
export default Popup;
|
package/lib/Popup/index.js
CHANGED
|
@@ -40,6 +40,8 @@ const Popup = /*#__PURE__*/React.forwardRef((props, ref) => {
|
|
|
40
40
|
// Motion
|
|
41
41
|
motion,
|
|
42
42
|
maskMotion,
|
|
43
|
+
// Mobile
|
|
44
|
+
mobile,
|
|
43
45
|
// Portal
|
|
44
46
|
forceRender,
|
|
45
47
|
getPopupContainer,
|
|
@@ -66,6 +68,17 @@ const Popup = /*#__PURE__*/React.forwardRef((props, ref) => {
|
|
|
66
68
|
// We can not remove holder only when motion finished.
|
|
67
69
|
const isNodeVisible = open || keepDom;
|
|
68
70
|
|
|
71
|
+
// ========================= Mobile =========================
|
|
72
|
+
const isMobile = !!mobile;
|
|
73
|
+
|
|
74
|
+
// ========================== Mask ==========================
|
|
75
|
+
const [mergedMask, mergedMaskMotion, mergedPopupMotion] = React.useMemo(() => {
|
|
76
|
+
if (mobile) {
|
|
77
|
+
return [mobile.mask, mobile.maskMotion, mobile.motion];
|
|
78
|
+
}
|
|
79
|
+
return [mask, maskMotion, motion];
|
|
80
|
+
}, [mobile, mask, maskMotion, motion]);
|
|
81
|
+
|
|
69
82
|
// ======================= Container ========================
|
|
70
83
|
const getPopupContainerNeedParams = getPopupContainer?.length > 0;
|
|
71
84
|
const [show, setShow] = React.useState(!getPopupContainer || !getPopupContainerNeedParams);
|
|
@@ -84,7 +97,7 @@ const Popup = /*#__PURE__*/React.forwardRef((props, ref) => {
|
|
|
84
97
|
|
|
85
98
|
// >>>>> Offset
|
|
86
99
|
const AUTO = 'auto';
|
|
87
|
-
const offsetStyle = {
|
|
100
|
+
const offsetStyle = isMobile ? {} : {
|
|
88
101
|
left: '-1000vw',
|
|
89
102
|
top: '-1000vh',
|
|
90
103
|
right: AUTO,
|
|
@@ -92,7 +105,7 @@ const Popup = /*#__PURE__*/React.forwardRef((props, ref) => {
|
|
|
92
105
|
};
|
|
93
106
|
|
|
94
107
|
// Set align style
|
|
95
|
-
if (ready || !open) {
|
|
108
|
+
if (!isMobile && (ready || !open)) {
|
|
96
109
|
const {
|
|
97
110
|
points
|
|
98
111
|
} = align;
|
|
@@ -140,8 +153,9 @@ const Popup = /*#__PURE__*/React.forwardRef((props, ref) => {
|
|
|
140
153
|
prefixCls: prefixCls,
|
|
141
154
|
open: open,
|
|
142
155
|
zIndex: zIndex,
|
|
143
|
-
mask:
|
|
144
|
-
motion:
|
|
156
|
+
mask: mergedMask,
|
|
157
|
+
motion: mergedMaskMotion,
|
|
158
|
+
mobile: isMobile
|
|
145
159
|
}), /*#__PURE__*/React.createElement(_resizeObserver.default, {
|
|
146
160
|
onResize: onAlign,
|
|
147
161
|
disabled: !open
|
|
@@ -153,7 +167,7 @@ const Popup = /*#__PURE__*/React.forwardRef((props, ref) => {
|
|
|
153
167
|
removeOnLeave: false,
|
|
154
168
|
forceRender: forceRender,
|
|
155
169
|
leavedClassName: `${prefixCls}-hidden`
|
|
156
|
-
},
|
|
170
|
+
}, mergedPopupMotion, {
|
|
157
171
|
onAppearPrepare: onPrepare,
|
|
158
172
|
onEnterPrepare: onPrepare,
|
|
159
173
|
visible: open,
|
|
@@ -165,7 +179,9 @@ const Popup = /*#__PURE__*/React.forwardRef((props, ref) => {
|
|
|
165
179
|
className: motionClassName,
|
|
166
180
|
style: motionStyle
|
|
167
181
|
}, motionRef) => {
|
|
168
|
-
const cls = (0, _classnames.default)(prefixCls, motionClassName, className
|
|
182
|
+
const cls = (0, _classnames.default)(prefixCls, motionClassName, className, {
|
|
183
|
+
[`${prefixCls}-mobile`]: isMobile
|
|
184
|
+
});
|
|
169
185
|
return /*#__PURE__*/React.createElement("div", {
|
|
170
186
|
ref: (0, _ref.composeRef)(resizeObserverRef, ref, motionRef),
|
|
171
187
|
className: cls,
|
package/lib/hooks/useAction.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ActionType } from '../interface';
|
|
2
|
-
type
|
|
3
|
-
|
|
2
|
+
type InternalActionType = ActionType | 'touch';
|
|
3
|
+
type ActionTypes = InternalActionType | InternalActionType[];
|
|
4
|
+
export default function useAction(action: ActionTypes, showAction?: ActionTypes, hideAction?: ActionTypes): [showAction: Set<InternalActionType>, hideAction: Set<InternalActionType>];
|
|
4
5
|
export {};
|
package/lib/hooks/useAction.js
CHANGED
|
@@ -10,22 +10,18 @@ function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e;
|
|
|
10
10
|
function toArray(val) {
|
|
11
11
|
return val ? Array.isArray(val) ? val : [val] : [];
|
|
12
12
|
}
|
|
13
|
-
function useAction(
|
|
13
|
+
function useAction(action, showAction, hideAction) {
|
|
14
14
|
return React.useMemo(() => {
|
|
15
15
|
const mergedShowAction = toArray(showAction ?? action);
|
|
16
16
|
const mergedHideAction = toArray(hideAction ?? action);
|
|
17
17
|
const showActionSet = new Set(mergedShowAction);
|
|
18
18
|
const hideActionSet = new Set(mergedHideAction);
|
|
19
|
-
if (
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
if (hideActionSet.has('hover')) {
|
|
25
|
-
hideActionSet.delete('hover');
|
|
26
|
-
hideActionSet.add('click');
|
|
27
|
-
}
|
|
19
|
+
if (showActionSet.has('hover') && !showActionSet.has('click')) {
|
|
20
|
+
showActionSet.add('touch');
|
|
21
|
+
}
|
|
22
|
+
if (hideActionSet.has('hover') && !hideActionSet.has('click')) {
|
|
23
|
+
hideActionSet.add('touch');
|
|
28
24
|
}
|
|
29
25
|
return [showActionSet, hideActionSet];
|
|
30
|
-
}, [
|
|
26
|
+
}, [action, showAction, hideAction]);
|
|
31
27
|
}
|
package/lib/hooks/useAlign.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { TriggerProps } from '..';
|
|
2
2
|
import type { AlignType } from '../interface';
|
|
3
|
-
export default function useAlign(open: boolean, popupEle: HTMLElement, target: HTMLElement | [x: number, y: number], placement: string, builtinPlacements: any, popupAlign?: AlignType, onPopupAlign?: TriggerProps['onPopupAlign']): [
|
|
3
|
+
export default function useAlign(open: boolean, popupEle: HTMLElement, target: HTMLElement | [x: number, y: number], placement: string, builtinPlacements: any, popupAlign?: AlignType, onPopupAlign?: TriggerProps['onPopupAlign'], mobile?: boolean): [
|
|
4
4
|
ready: boolean,
|
|
5
5
|
offsetX: number,
|
|
6
6
|
offsetY: number,
|
package/lib/hooks/useAlign.js
CHANGED
|
@@ -70,7 +70,7 @@ function reversePoints(points, index) {
|
|
|
70
70
|
return point;
|
|
71
71
|
}).join('');
|
|
72
72
|
}
|
|
73
|
-
function useAlign(open, popupEle, target, placement, builtinPlacements, popupAlign, onPopupAlign) {
|
|
73
|
+
function useAlign(open, popupEle, target, placement, builtinPlacements, popupAlign, onPopupAlign, mobile) {
|
|
74
74
|
const [offsetInfo, setOffsetInfo] = React.useState({
|
|
75
75
|
ready: false,
|
|
76
76
|
offsetX: 0,
|
|
@@ -85,7 +85,7 @@ function useAlign(open, popupEle, target, placement, builtinPlacements, popupAli
|
|
|
85
85
|
});
|
|
86
86
|
const alignCountRef = React.useRef(0);
|
|
87
87
|
const scrollerList = React.useMemo(() => {
|
|
88
|
-
if (!popupEle) {
|
|
88
|
+
if (!popupEle || mobile) {
|
|
89
89
|
return [];
|
|
90
90
|
}
|
|
91
91
|
return (0, _util.collectScroller)(popupEle);
|
|
@@ -104,7 +104,7 @@ function useAlign(open, popupEle, target, placement, builtinPlacements, popupAli
|
|
|
104
104
|
|
|
105
105
|
// ========================= Align =========================
|
|
106
106
|
const onAlign = (0, _useEvent.default)(() => {
|
|
107
|
-
if (popupEle && target && open) {
|
|
107
|
+
if (popupEle && target && open && !mobile) {
|
|
108
108
|
const popupElement = popupEle;
|
|
109
109
|
const doc = popupElement.ownerDocument;
|
|
110
110
|
const win = (0, _util.getWin)(popupElement);
|
|
@@ -467,9 +467,15 @@ function useAlign(open, popupEle, target, placement, builtinPlacements, popupAli
|
|
|
467
467
|
const targetRight = targetLeft + targetWidth;
|
|
468
468
|
const targetTop = targetRect.y;
|
|
469
469
|
const targetBottom = targetTop + targetHeight;
|
|
470
|
+
|
|
471
|
+
/** Max left of the popup and target element */
|
|
470
472
|
const maxLeft = Math.max(popupLeft, targetLeft);
|
|
473
|
+
/** Min right of the popup and target element */
|
|
471
474
|
const minRight = Math.min(popupRight, targetRight);
|
|
475
|
+
|
|
476
|
+
/** The center X of popup & target cross area */
|
|
472
477
|
const xCenter = (maxLeft + minRight) / 2;
|
|
478
|
+
/** Arrow X of popup offset */
|
|
473
479
|
const nextArrowX = xCenter - popupLeft;
|
|
474
480
|
const maxTop = Math.max(popupTop, targetTop);
|
|
475
481
|
const minBottom = Math.min(popupBottom, targetBottom);
|
|
@@ -1 +1,5 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Close if click on the window.
|
|
3
|
+
* Return the function that click on the Popup element.
|
|
4
|
+
*/
|
|
1
5
|
export default function useWinClick(open: boolean, clickToHide: boolean, targetEle: HTMLElement, popupEle: HTMLElement, mask: boolean, maskClosable: boolean, inPopupOrChild: (target: EventTarget) => boolean, triggerOpen: (open: boolean) => void): () => void;
|
package/lib/hooks/useWinClick.js
CHANGED
|
@@ -10,6 +10,10 @@ var React = _interopRequireWildcard(require("react"));
|
|
|
10
10
|
var _util = require("../util");
|
|
11
11
|
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
|
|
12
12
|
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
|
|
13
|
+
/**
|
|
14
|
+
* Close if click on the window.
|
|
15
|
+
* Return the function that click on the Popup element.
|
|
16
|
+
*/
|
|
13
17
|
function useWinClick(open, clickToHide, targetEle, popupEle, mask, maskClosable, inPopupOrChild, triggerOpen) {
|
|
14
18
|
const openRef = React.useRef(open);
|
|
15
19
|
openRef.current = open;
|
package/lib/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { CSSMotionProps } from '@rc-component/motion';
|
|
2
2
|
import * as React from 'react';
|
|
3
|
+
import { type MobileConfig } from './Popup';
|
|
3
4
|
import type { ActionType, AlignType, ArrowTypeOuter, BuildInPlacements } from './interface';
|
|
4
5
|
export type { ActionType, AlignType, ArrowTypeOuter as ArrowType, BuildInPlacements, };
|
|
5
6
|
export interface TriggerRef {
|
|
@@ -53,6 +54,13 @@ export interface TriggerProps {
|
|
|
53
54
|
*/
|
|
54
55
|
fresh?: boolean;
|
|
55
56
|
arrow?: boolean | ArrowTypeOuter;
|
|
57
|
+
/**
|
|
58
|
+
* @private Bump fixed position at bottom in mobile.
|
|
59
|
+
* Will replace the config of root props.
|
|
60
|
+
* This will directly trade as mobile view which will not check what real is.
|
|
61
|
+
* This is internal usage currently, do not use in your prod.
|
|
62
|
+
*/
|
|
63
|
+
mobile?: MobileConfig;
|
|
56
64
|
}
|
|
57
65
|
export declare function generateTrigger(PortalComponent?: React.ComponentType<any>): React.ForwardRefExoticComponent<TriggerProps & React.RefAttributes<TriggerRef>>;
|
|
58
66
|
declare const _default: React.ForwardRefExoticComponent<TriggerProps & React.RefAttributes<TriggerRef>>;
|
package/lib/index.js
CHANGED
|
@@ -13,7 +13,6 @@ var _shadow = require("@rc-component/util/lib/Dom/shadow");
|
|
|
13
13
|
var _useEvent = _interopRequireDefault(require("@rc-component/util/lib/hooks/useEvent"));
|
|
14
14
|
var _useId = _interopRequireDefault(require("@rc-component/util/lib/hooks/useId"));
|
|
15
15
|
var _useLayoutEffect = _interopRequireDefault(require("@rc-component/util/lib/hooks/useLayoutEffect"));
|
|
16
|
-
var _isMobile = _interopRequireDefault(require("@rc-component/util/lib/isMobile"));
|
|
17
16
|
var React = _interopRequireWildcard(require("react"));
|
|
18
17
|
var _Popup = _interopRequireDefault(require("./Popup"));
|
|
19
18
|
var _context = _interopRequireDefault(require("./context"));
|
|
@@ -78,15 +77,14 @@ function generateTrigger(PortalComponent = _portal.default) {
|
|
|
78
77
|
// Motion
|
|
79
78
|
popupMotion,
|
|
80
79
|
maskMotion,
|
|
80
|
+
// Private
|
|
81
|
+
mobile,
|
|
81
82
|
...restProps
|
|
82
83
|
} = props;
|
|
83
84
|
const mergedAutoDestroy = autoDestroy || false;
|
|
84
85
|
|
|
85
86
|
// =========================== Mobile ===========================
|
|
86
|
-
const
|
|
87
|
-
(0, _useLayoutEffect.default)(() => {
|
|
88
|
-
setMobile((0, _isMobile.default)());
|
|
89
|
-
}, []);
|
|
87
|
+
const isMobile = !!mobile;
|
|
90
88
|
|
|
91
89
|
// ========================== Context ===========================
|
|
92
90
|
const subPopupElements = React.useRef({});
|
|
@@ -199,8 +197,8 @@ function generateTrigger(PortalComponent = _portal.default) {
|
|
|
199
197
|
const setMousePosByEvent = event => {
|
|
200
198
|
setMousePos([event.clientX, event.clientY]);
|
|
201
199
|
};
|
|
202
|
-
const [ready, offsetX, offsetY, offsetR, offsetB, arrowX, arrowY, scaleX, scaleY, alignInfo, onAlign] = (0, _useAlign.default)(mergedOpen, popupEle, alignPoint && mousePos !== null ? mousePos : targetEle, popupPlacement, builtinPlacements, popupAlign, onPopupAlign);
|
|
203
|
-
const [showActions, hideActions] = (0, _useAction.default)(
|
|
200
|
+
const [ready, offsetX, offsetY, offsetR, offsetB, arrowX, arrowY, scaleX, scaleY, alignInfo, onAlign] = (0, _useAlign.default)(mergedOpen, popupEle, alignPoint && mousePos !== null ? mousePos : targetEle, popupPlacement, builtinPlacements, popupAlign, onPopupAlign, isMobile);
|
|
201
|
+
const [showActions, hideActions] = (0, _useAction.default)(action, showAction, hideAction);
|
|
204
202
|
const clickToShow = showActions.has('click');
|
|
205
203
|
const clickToHide = hideActions.has('click') || hideActions.has('contextMenu');
|
|
206
204
|
const triggerAlign = (0, _useEvent.default)(() => {
|
|
@@ -275,17 +273,44 @@ function generateTrigger(PortalComponent = _portal.default) {
|
|
|
275
273
|
// =========================== Action ===========================
|
|
276
274
|
/**
|
|
277
275
|
* Util wrapper for trigger action
|
|
276
|
+
* @param eventName Listen event name
|
|
277
|
+
* @param nextOpen Next open state after trigger
|
|
278
|
+
* @param delay Delay to trigger open change
|
|
279
|
+
* @param callback Callback if current event need additional action
|
|
280
|
+
* @param ignoreCheck Ignore current event if check return true
|
|
278
281
|
*/
|
|
279
|
-
function wrapperAction(eventName, nextOpen, delay,
|
|
282
|
+
function wrapperAction(eventName, nextOpen, delay, callback, ignoreCheck) {
|
|
280
283
|
cloneProps[eventName] = (event, ...args) => {
|
|
281
|
-
|
|
282
|
-
|
|
284
|
+
if (!ignoreCheck || !ignoreCheck()) {
|
|
285
|
+
callback?.(event);
|
|
286
|
+
triggerOpen(nextOpen, delay);
|
|
287
|
+
}
|
|
283
288
|
|
|
284
289
|
// Pass to origin
|
|
285
290
|
originChildProps[eventName]?.(event, ...args);
|
|
286
291
|
};
|
|
287
292
|
}
|
|
288
293
|
|
|
294
|
+
// ======================= Action: Touch ========================
|
|
295
|
+
const touchToShow = showActions.has('touch');
|
|
296
|
+
const touchToHide = hideActions.has('touch');
|
|
297
|
+
|
|
298
|
+
/** Used for prevent `hover` event conflict with mobile env */
|
|
299
|
+
const touchedRef = React.useRef(false);
|
|
300
|
+
if (touchToShow || touchToHide) {
|
|
301
|
+
cloneProps.onTouchStart = (...args) => {
|
|
302
|
+
touchedRef.current = true;
|
|
303
|
+
if (openRef.current && touchToHide) {
|
|
304
|
+
triggerOpen(false);
|
|
305
|
+
} else if (!openRef.current && touchToShow) {
|
|
306
|
+
triggerOpen(true);
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
// Pass to origin
|
|
310
|
+
originChildProps.onTouchStart?.(...args);
|
|
311
|
+
};
|
|
312
|
+
}
|
|
313
|
+
|
|
289
314
|
// ======================= Action: Click ========================
|
|
290
315
|
if (clickToShow || clickToHide) {
|
|
291
316
|
cloneProps.onClick = (event, ...args) => {
|
|
@@ -298,25 +323,29 @@ function generateTrigger(PortalComponent = _portal.default) {
|
|
|
298
323
|
|
|
299
324
|
// Pass to origin
|
|
300
325
|
originChildProps.onClick?.(event, ...args);
|
|
326
|
+
touchedRef.current = false;
|
|
301
327
|
};
|
|
302
328
|
}
|
|
303
329
|
|
|
304
330
|
// Click to hide is special action since click popup element should not hide
|
|
305
|
-
const onPopupPointerDown = (0, _useWinClick.default)(mergedOpen, clickToHide, targetEle, popupEle, mask, maskClosable, inPopupOrChild, triggerOpen);
|
|
331
|
+
const onPopupPointerDown = (0, _useWinClick.default)(mergedOpen, clickToHide || touchToHide, targetEle, popupEle, mask, maskClosable, inPopupOrChild, triggerOpen);
|
|
306
332
|
|
|
307
333
|
// ======================= Action: Hover ========================
|
|
308
334
|
const hoverToShow = showActions.has('hover');
|
|
309
335
|
const hoverToHide = hideActions.has('hover');
|
|
310
336
|
let onPopupMouseEnter;
|
|
311
337
|
let onPopupMouseLeave;
|
|
338
|
+
const ignoreMouseTrigger = () => {
|
|
339
|
+
return touchedRef.current;
|
|
340
|
+
};
|
|
312
341
|
if (hoverToShow) {
|
|
313
|
-
|
|
314
|
-
wrapperAction('onMouseEnter', true, mouseEnterDelay, event => {
|
|
315
|
-
setMousePosByEvent(event);
|
|
316
|
-
});
|
|
317
|
-
wrapperAction('onPointerEnter', true, mouseEnterDelay, event => {
|
|
342
|
+
const onMouseEnterCallback = event => {
|
|
318
343
|
setMousePosByEvent(event);
|
|
319
|
-
}
|
|
344
|
+
};
|
|
345
|
+
|
|
346
|
+
// Compatible with old browser which not support pointer event
|
|
347
|
+
wrapperAction('onMouseEnter', true, mouseEnterDelay, onMouseEnterCallback, ignoreMouseTrigger);
|
|
348
|
+
wrapperAction('onPointerEnter', true, mouseEnterDelay, onMouseEnterCallback, ignoreMouseTrigger);
|
|
320
349
|
onPopupMouseEnter = event => {
|
|
321
350
|
// Only trigger re-open when popup is visible
|
|
322
351
|
if ((mergedOpen || inMotion) && popupEle?.contains(event.target)) {
|
|
@@ -327,14 +356,13 @@ function generateTrigger(PortalComponent = _portal.default) {
|
|
|
327
356
|
// Align Point
|
|
328
357
|
if (alignPoint) {
|
|
329
358
|
cloneProps.onMouseMove = event => {
|
|
330
|
-
// setMousePosByEvent(event);
|
|
331
359
|
originChildProps.onMouseMove?.(event);
|
|
332
360
|
};
|
|
333
361
|
}
|
|
334
362
|
}
|
|
335
363
|
if (hoverToHide) {
|
|
336
|
-
wrapperAction('onMouseLeave', false, mouseLeaveDelay);
|
|
337
|
-
wrapperAction('onPointerLeave', false, mouseLeaveDelay);
|
|
364
|
+
wrapperAction('onMouseLeave', false, mouseLeaveDelay, undefined, ignoreMouseTrigger);
|
|
365
|
+
wrapperAction('onPointerLeave', false, mouseLeaveDelay, undefined, ignoreMouseTrigger);
|
|
338
366
|
onPopupMouseLeave = () => {
|
|
339
367
|
triggerOpen(false, mouseLeaveDelay);
|
|
340
368
|
};
|
|
@@ -412,7 +440,7 @@ function generateTrigger(PortalComponent = _portal.default) {
|
|
|
412
440
|
ref: setPopupRef,
|
|
413
441
|
prefixCls: prefixCls,
|
|
414
442
|
popup: popup,
|
|
415
|
-
className: (0, _classnames.default)(popupClassName, alignedClassName),
|
|
443
|
+
className: (0, _classnames.default)(popupClassName, !isMobile && alignedClassName),
|
|
416
444
|
style: popupStyle,
|
|
417
445
|
target: targetEle,
|
|
418
446
|
onMouseEnter: onPopupMouseEnter,
|
|
@@ -462,6 +490,9 @@ function generateTrigger(PortalComponent = _portal.default) {
|
|
|
462
490
|
stretch: stretch,
|
|
463
491
|
targetWidth: targetWidth / scaleX,
|
|
464
492
|
targetHeight: targetHeight / scaleY
|
|
493
|
+
// Mobile
|
|
494
|
+
,
|
|
495
|
+
mobile: mobile
|
|
465
496
|
})));
|
|
466
497
|
});
|
|
467
498
|
if (process.env.NODE_ENV !== 'production') {
|
package/lib/interface.d.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
/// <reference types="react" />
|
|
2
|
-
import type { CSSMotionProps } from '@rc-component/motion';
|
|
3
2
|
export type Placement = 'top' | 'left' | 'right' | 'bottom' | 'topLeft' | 'topRight' | 'bottomLeft' | 'bottomRight' | 'leftTop' | 'leftBottom' | 'rightTop' | 'rightBottom';
|
|
4
3
|
export type AlignPointTopBottom = 't' | 'b' | 'c';
|
|
5
4
|
export type AlignPointLeftRight = 'l' | 'r' | 'c';
|
|
@@ -90,10 +89,3 @@ export interface Point {
|
|
|
90
89
|
export interface CommonEventHandler {
|
|
91
90
|
remove: () => void;
|
|
92
91
|
}
|
|
93
|
-
export interface MobileConfig {
|
|
94
|
-
/** Set popup motion. You can ref `rc-motion` for more info. */
|
|
95
|
-
popupMotion?: CSSMotionProps;
|
|
96
|
-
popupClassName?: string;
|
|
97
|
-
popupStyle?: React.CSSProperties;
|
|
98
|
-
popupRender?: (originNode: React.ReactNode) => React.ReactNode;
|
|
99
|
-
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rc-component/trigger",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.5.1",
|
|
4
4
|
"description": "base abstract trigger component for react",
|
|
5
5
|
"engines": {
|
|
6
6
|
"node": ">=8.x"
|
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
"@testing-library/react": "^16.0.0",
|
|
55
55
|
"@types/classnames": "^2.2.10",
|
|
56
56
|
"@types/jest": "^29.5.2",
|
|
57
|
-
"@types/node": "^
|
|
57
|
+
"@types/node": "^24.0.3",
|
|
58
58
|
"@types/react": "^19.1.2",
|
|
59
59
|
"@types/react-dom": "^19.1.2",
|
|
60
60
|
"@umijs/fabric": "^4.0.1",
|
|
@@ -68,7 +68,7 @@
|
|
|
68
68
|
"react": "^19.1.0",
|
|
69
69
|
"react-dom": "^19.1.0",
|
|
70
70
|
"regenerator-runtime": "^0.14.0",
|
|
71
|
-
"typescript": "
|
|
71
|
+
"typescript": "~5.1.6"
|
|
72
72
|
},
|
|
73
73
|
"peerDependencies": {
|
|
74
74
|
"react": ">=18.0.0",
|