@alifd/chat 0.0.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/README.md +1 -0
- package/es/button/index.d.ts +1 -0
- package/es/button/index.js +1 -0
- package/es/button/main.scss +0 -0
- package/es/button/style.d.ts +1 -0
- package/es/button/style.js +1 -0
- package/es/button/types.d.ts +123 -0
- package/es/button/types.js +1 -0
- package/es/button/view/button.d.ts +23 -0
- package/es/button/view/button.js +26 -0
- package/es/float-button/hooks/useAutoAlign.d.ts +11 -0
- package/es/float-button/hooks/useAutoAlign.js +45 -0
- package/es/float-button/hooks/useAutoHide.d.ts +15 -0
- package/es/float-button/hooks/useAutoHide.js +157 -0
- package/es/float-button/hooks/useDragable.d.ts +9 -0
- package/es/float-button/hooks/useDragable.js +128 -0
- package/es/float-button/hooks/useNestleEdge.d.ts +13 -0
- package/es/float-button/hooks/useNestleEdge.js +77 -0
- package/es/float-button/index.d.ts +6 -0
- package/es/float-button/index.js +5 -0
- package/es/float-button/main.scss +16 -0
- package/es/float-button/style.d.ts +2 -0
- package/es/float-button/style.js +2 -0
- package/es/float-button/types.d.ts +225 -0
- package/es/float-button/types.js +1 -0
- package/es/float-button/util.d.ts +8 -0
- package/es/float-button/util.js +67 -0
- package/es/float-button/view/backtop.d.ts +4 -0
- package/es/float-button/view/backtop.js +73 -0
- package/es/float-button/view/balloon.d.ts +8 -0
- package/es/float-button/view/balloon.js +12 -0
- package/es/float-button/view/drawer.d.ts +2 -0
- package/es/float-button/view/drawer.js +4 -0
- package/es/float-button/view/float-button.d.ts +4 -0
- package/es/float-button/view/float-button.js +117 -0
- package/es/float-button/view/inner-drawer.d.ts +2 -0
- package/es/float-button/view/inner-drawer.js +4 -0
- package/es/index.d.ts +2 -0
- package/es/index.js +2 -0
- package/es/utils/common.d.ts +2 -0
- package/es/utils/common.js +8 -0
- package/es/utils/func.d.ts +3 -0
- package/es/utils/func.js +30 -0
- package/es/utils/hooks/useControlable.d.ts +6 -0
- package/es/utils/hooks/useControlable.js +27 -0
- package/es/utils/hooks/useDebounce.d.ts +2 -0
- package/es/utils/hooks/useDebounce.js +9 -0
- package/es/utils/hooks/useThrottle.d.ts +2 -0
- package/es/utils/hooks/useThrottle.js +9 -0
- package/es/utils/index.d.ts +6 -0
- package/es/utils/index.js +6 -0
- package/es/utils/types.d.ts +2 -0
- package/es/utils/types.js +1 -0
- package/lib/button/index.d.ts +1 -0
- package/lib/button/index.js +8 -0
- package/lib/button/main.scss +0 -0
- package/lib/button/style.d.ts +1 -0
- package/lib/button/style.js +3 -0
- package/lib/button/types.d.ts +123 -0
- package/lib/button/types.js +2 -0
- package/lib/button/view/button.d.ts +23 -0
- package/lib/button/view/button.js +29 -0
- package/lib/float-button/hooks/useAutoAlign.d.ts +11 -0
- package/lib/float-button/hooks/useAutoAlign.js +49 -0
- package/lib/float-button/hooks/useAutoHide.d.ts +15 -0
- package/lib/float-button/hooks/useAutoHide.js +161 -0
- package/lib/float-button/hooks/useDragable.d.ts +9 -0
- package/lib/float-button/hooks/useDragable.js +132 -0
- package/lib/float-button/hooks/useNestleEdge.d.ts +13 -0
- package/lib/float-button/hooks/useNestleEdge.js +81 -0
- package/lib/float-button/index.d.ts +6 -0
- package/lib/float-button/index.js +8 -0
- package/lib/float-button/main.scss +16 -0
- package/lib/float-button/style.d.ts +2 -0
- package/lib/float-button/style.js +4 -0
- package/lib/float-button/types.d.ts +225 -0
- package/lib/float-button/types.js +2 -0
- package/lib/float-button/util.d.ts +8 -0
- package/lib/float-button/util.js +74 -0
- package/lib/float-button/view/backtop.d.ts +4 -0
- package/lib/float-button/view/backtop.js +76 -0
- package/lib/float-button/view/balloon.d.ts +8 -0
- package/lib/float-button/view/balloon.js +16 -0
- package/lib/float-button/view/drawer.d.ts +2 -0
- package/lib/float-button/view/drawer.js +9 -0
- package/lib/float-button/view/float-button.d.ts +4 -0
- package/lib/float-button/view/float-button.js +119 -0
- package/lib/float-button/view/inner-drawer.d.ts +2 -0
- package/lib/float-button/view/inner-drawer.js +9 -0
- package/lib/index.d.ts +2 -0
- package/lib/index.js +9 -0
- package/lib/utils/common.d.ts +2 -0
- package/lib/utils/common.js +12 -0
- package/lib/utils/func.d.ts +3 -0
- package/lib/utils/func.js +35 -0
- package/lib/utils/hooks/useControlable.d.ts +6 -0
- package/lib/utils/hooks/useControlable.js +31 -0
- package/lib/utils/hooks/useDebounce.d.ts +2 -0
- package/lib/utils/hooks/useDebounce.js +13 -0
- package/lib/utils/hooks/useThrottle.d.ts +2 -0
- package/lib/utils/hooks/useThrottle.js +13 -0
- package/lib/utils/index.d.ts +6 -0
- package/lib/utils/index.js +13 -0
- package/lib/utils/types.d.ts +2 -0
- package/lib/utils/types.js +2 -0
- package/package.json +103 -0
package/README.md
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
## Fusion Chat
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from './view/button.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from './view/button.js';
|
|
File without changes
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import './main.scss';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import './main.scss';
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import type React from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* 按钮类型
|
|
4
|
+
* @api
|
|
5
|
+
*/
|
|
6
|
+
export type ButtonSize = 'small' | 'medium' | 'large';
|
|
7
|
+
/**
|
|
8
|
+
* @api Button.Group
|
|
9
|
+
*/
|
|
10
|
+
export interface GroupProps extends React.HTMLAttributes<HTMLElement> {
|
|
11
|
+
/**
|
|
12
|
+
* 统一设置 Button 组件的按钮大小
|
|
13
|
+
*/
|
|
14
|
+
size?: ButtonSize;
|
|
15
|
+
}
|
|
16
|
+
type HTMLAttributesWeak = Omit<React.ButtonHTMLAttributes<HTMLElement>, 'type' | 'onClick'>;
|
|
17
|
+
/**
|
|
18
|
+
* @api Button
|
|
19
|
+
*/
|
|
20
|
+
export interface ButtonProps extends HTMLAttributesWeak {
|
|
21
|
+
/**
|
|
22
|
+
* 按钮的类型
|
|
23
|
+
* @en Typeo of button
|
|
24
|
+
* @defaultValue 'normal'
|
|
25
|
+
*/
|
|
26
|
+
type?: 'primary' | 'secondary' | 'normal';
|
|
27
|
+
/**
|
|
28
|
+
* @deprecated use `warning` `text` `ghost` insteaded
|
|
29
|
+
* @skip
|
|
30
|
+
*/
|
|
31
|
+
shape?: 'warning' | 'text' | 'ghost';
|
|
32
|
+
/**
|
|
33
|
+
* 按钮的尺寸
|
|
34
|
+
* @en Size of button
|
|
35
|
+
* @defaultValue 'medium'
|
|
36
|
+
*/
|
|
37
|
+
size?: ButtonSize;
|
|
38
|
+
/**
|
|
39
|
+
* 按钮中可配置的 Icon
|
|
40
|
+
* @en Available icons in button
|
|
41
|
+
* @example
|
|
42
|
+
* \{ loading: <Icon type="loading"/> \}
|
|
43
|
+
*/
|
|
44
|
+
icons?: {
|
|
45
|
+
loading?: React.ReactNode;
|
|
46
|
+
};
|
|
47
|
+
/**
|
|
48
|
+
* 按钮中 Icon 的尺寸
|
|
49
|
+
* @en Size of icon in button
|
|
50
|
+
* @defaultValue 默认根据 size 自动映射,映射规则:
|
|
51
|
+
* size:large -\> `small`
|
|
52
|
+
* size:medium -\> `xs`
|
|
53
|
+
* size:small -\> `xs`
|
|
54
|
+
*/
|
|
55
|
+
iconSize?: number | 'xxs' | 'xs' | 'small' | 'medium' | 'large' | 'xl' | 'xxl' | 'xxxl' | 'inherit';
|
|
56
|
+
/**
|
|
57
|
+
* button 标签的 type 值
|
|
58
|
+
* @en Original html type for button element
|
|
59
|
+
* @defaultValue 'button'
|
|
60
|
+
* @remarks 仅当 component = 'button' 时使用
|
|
61
|
+
*/
|
|
62
|
+
htmlType?: 'submit' | 'reset' | 'button';
|
|
63
|
+
/**
|
|
64
|
+
* 最终渲染的 jsx 标签标签类型
|
|
65
|
+
* @en The jsx tag to be rendered
|
|
66
|
+
* @remarks 直接去掉原先的 ReactNode 类型,因为即使传递了非法类型运行时也会报错,不如在类型提示阶段就将错误抛出
|
|
67
|
+
*/
|
|
68
|
+
component?: 'button' | 'a' | React.ComponentType<unknown>;
|
|
69
|
+
/**
|
|
70
|
+
* 设置按钮的载入状态
|
|
71
|
+
* @en Loading state of a button
|
|
72
|
+
* @defaultValue false
|
|
73
|
+
*/
|
|
74
|
+
loading?: boolean;
|
|
75
|
+
/**
|
|
76
|
+
* 是否为幽灵按钮
|
|
77
|
+
* @en Setting ghost button
|
|
78
|
+
* @defaultValue false
|
|
79
|
+
*/
|
|
80
|
+
ghost?: true | false | 'light' | 'dark';
|
|
81
|
+
/**
|
|
82
|
+
* 是否为文本按钮
|
|
83
|
+
* @en Is text button
|
|
84
|
+
* @defaultValue false
|
|
85
|
+
*/
|
|
86
|
+
text?: boolean;
|
|
87
|
+
/**
|
|
88
|
+
* 是否为警告按钮
|
|
89
|
+
* @en Is warning button
|
|
90
|
+
* @defaultValue false
|
|
91
|
+
*/
|
|
92
|
+
warning?: boolean;
|
|
93
|
+
/**
|
|
94
|
+
* 是否禁用
|
|
95
|
+
* @en Is disabled
|
|
96
|
+
* @defaultValue false
|
|
97
|
+
*/
|
|
98
|
+
disabled?: boolean;
|
|
99
|
+
/**
|
|
100
|
+
* 点击按钮的回调
|
|
101
|
+
* @en Callback of click event
|
|
102
|
+
*/
|
|
103
|
+
onClick?: React.MouseEventHandler;
|
|
104
|
+
/**
|
|
105
|
+
* 'a' 标签的 href 属性
|
|
106
|
+
* @remarks 仅在 component = 'a' 时使用
|
|
107
|
+
* @skip
|
|
108
|
+
*/
|
|
109
|
+
href?: string;
|
|
110
|
+
/**
|
|
111
|
+
* 'a' 标签的 target 属性
|
|
112
|
+
* @remarks 仅在 component = 'a' 时使用
|
|
113
|
+
* @skip
|
|
114
|
+
*/
|
|
115
|
+
target?: string;
|
|
116
|
+
/**
|
|
117
|
+
* 'ReactRouterLink' 组件的 to 属性
|
|
118
|
+
* @remarks 仅在 component 支持 to 属性时使用
|
|
119
|
+
* @skip
|
|
120
|
+
*/
|
|
121
|
+
to?: string;
|
|
122
|
+
}
|
|
123
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import type { ButtonProps } from '../types.js';
|
|
3
|
+
/**
|
|
4
|
+
* @component 按钮
|
|
5
|
+
* @en Button
|
|
6
|
+
* @type 通用 - General
|
|
7
|
+
* @remarks 按钮用于开始一个即时操作。- Button used to trigger an action.
|
|
8
|
+
* @when 标记一个(或封装一组)操作命令,响应用户点击行为,触发相应的业务逻辑。- Buttons are used for emphasizing important functions on your page.
|
|
9
|
+
* @others
|
|
10
|
+
* ## 无障碍键盘操作指南
|
|
11
|
+
* | 按键 | 说明 |
|
|
12
|
+
* | :---- | :---------- |
|
|
13
|
+
* | Enter | 触发 onClick 事件 |
|
|
14
|
+
* | SPACE | 触发 onClick 事件 |
|
|
15
|
+
* @othersEn
|
|
16
|
+
* ## ARIA and KeyBoard
|
|
17
|
+
* | KeyBoard | Description |
|
|
18
|
+
* | :---------- | :------------------------------ |
|
|
19
|
+
* | Enter | Trigger the onClick event |
|
|
20
|
+
* | SPACE | Trigger the onClick event |
|
|
21
|
+
*/
|
|
22
|
+
declare const Button: React.ForwardRefExoticComponent<ButtonProps & React.RefAttributes<{}>>;
|
|
23
|
+
export default Button;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { forwardRef } from 'react';
|
|
2
|
+
import * as React from 'react';
|
|
3
|
+
/**
|
|
4
|
+
* @component 按钮
|
|
5
|
+
* @en Button
|
|
6
|
+
* @type 通用 - General
|
|
7
|
+
* @remarks 按钮用于开始一个即时操作。- Button used to trigger an action.
|
|
8
|
+
* @when 标记一个(或封装一组)操作命令,响应用户点击行为,触发相应的业务逻辑。- Buttons are used for emphasizing important functions on your page.
|
|
9
|
+
* @others
|
|
10
|
+
* ## 无障碍键盘操作指南
|
|
11
|
+
* | 按键 | 说明 |
|
|
12
|
+
* | :---- | :---------- |
|
|
13
|
+
* | Enter | 触发 onClick 事件 |
|
|
14
|
+
* | SPACE | 触发 onClick 事件 |
|
|
15
|
+
* @othersEn
|
|
16
|
+
* ## ARIA and KeyBoard
|
|
17
|
+
* | KeyBoard | Description |
|
|
18
|
+
* | :---------- | :------------------------------ |
|
|
19
|
+
* | Enter | Trigger the onClick event |
|
|
20
|
+
* | SPACE | Trigger the onClick event |
|
|
21
|
+
*/
|
|
22
|
+
const Button = forwardRef((props, ref) => {
|
|
23
|
+
return React.createElement("div", null, props.children);
|
|
24
|
+
});
|
|
25
|
+
Button.displayName = 'Button';
|
|
26
|
+
export default Button;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { BalloonProps } from '../types';
|
|
2
|
+
/**
|
|
3
|
+
* 自动调整弹层 align 位置逻辑
|
|
4
|
+
*/
|
|
5
|
+
export declare function useAutoAlign(dom: HTMLElement | null | undefined, trigger: HTMLElement | null, { enable, defaultAlign, }: {
|
|
6
|
+
enable?: boolean;
|
|
7
|
+
defaultAlign: BalloonProps['align'];
|
|
8
|
+
}): {
|
|
9
|
+
align: "b" | "br" | "rt" | "tr" | "t" | "r" | "l" | "tl" | "bl" | "lt" | "lb" | "rb" | undefined;
|
|
10
|
+
update: () => void;
|
|
11
|
+
};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { useState } from 'react';
|
|
2
|
+
import { getNearlyEdge, isInScreen } from '../util';
|
|
3
|
+
import { useThrottle } from '../../utils';
|
|
4
|
+
/**
|
|
5
|
+
* 自动调整弹层 align 位置逻辑
|
|
6
|
+
*/
|
|
7
|
+
export function useAutoAlign(dom, trigger, { enable, defaultAlign, }) {
|
|
8
|
+
const [align, setAlign] = useState(defaultAlign);
|
|
9
|
+
const update = () => {
|
|
10
|
+
if (!enable || !dom || !trigger) {
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
if (isInScreen(dom)) {
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
const [edge, subEdge] = getNearlyEdge(trigger, ['top', 'right', 'bottom', 'left']);
|
|
17
|
+
let newAlign = align;
|
|
18
|
+
switch (edge) {
|
|
19
|
+
case 'top': {
|
|
20
|
+
newAlign = subEdge === 'left' ? 'bl' : 'br';
|
|
21
|
+
break;
|
|
22
|
+
}
|
|
23
|
+
case 'right': {
|
|
24
|
+
newAlign = subEdge === 'top' ? 'lt' : 'lb';
|
|
25
|
+
break;
|
|
26
|
+
}
|
|
27
|
+
case 'bottom': {
|
|
28
|
+
newAlign = subEdge === 'left' ? 'tl' : 'tr';
|
|
29
|
+
break;
|
|
30
|
+
}
|
|
31
|
+
case 'left': {
|
|
32
|
+
newAlign = subEdge === 'top' ? 'rt' : 'rb';
|
|
33
|
+
break;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
if (newAlign !== align) {
|
|
37
|
+
setAlign(newAlign);
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
const throttleUpdate = useThrottle(update, 100);
|
|
41
|
+
return {
|
|
42
|
+
align,
|
|
43
|
+
update: throttleUpdate,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/// <reference types="react" />
|
|
2
|
+
import type { Edge, Position } from '../types';
|
|
3
|
+
/**
|
|
4
|
+
* 自动隐藏悬浮球逻辑
|
|
5
|
+
*/
|
|
6
|
+
export declare function useAutoHide(dom: HTMLElement | null, { enable, leftSize, interactiveEdges, }: {
|
|
7
|
+
enable?: boolean;
|
|
8
|
+
leftSize?: number;
|
|
9
|
+
interactiveEdges?: Edge[];
|
|
10
|
+
}): {
|
|
11
|
+
isHideRef: import("react").MutableRefObject<boolean>;
|
|
12
|
+
handleMove: (e: MouseEvent) => void;
|
|
13
|
+
setHide: (hide: boolean) => void;
|
|
14
|
+
saveRestorePosition: (position?: Position) => void;
|
|
15
|
+
};
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import { useEffect, useRef } from 'react';
|
|
2
|
+
import { getScreenSize, getNearlyEdge } from '../util';
|
|
3
|
+
import { useThrottle, useDebounce } from '../../utils';
|
|
4
|
+
/**
|
|
5
|
+
* 自动隐藏悬浮球逻辑
|
|
6
|
+
*/
|
|
7
|
+
export function useAutoHide(dom, { enable, leftSize, interactiveEdges, }) {
|
|
8
|
+
const isHideRef = useRef(false);
|
|
9
|
+
const positionRef = useRef();
|
|
10
|
+
const setByMouseEnterRef = useRef(false);
|
|
11
|
+
const setPosition = ({ left, top }, animation = true) => {
|
|
12
|
+
if (!dom) {
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
dom.style.transition = animation ? 'transform 0.25s' : '';
|
|
16
|
+
dom.style.transform = `translate(${left}px, ${top}px)`;
|
|
17
|
+
dom.style.left = '0px';
|
|
18
|
+
dom.style.top = '0px';
|
|
19
|
+
};
|
|
20
|
+
const saveRestorePosition = (position) => {
|
|
21
|
+
if (!dom && !position) {
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
const { left, top } = position || dom.getBoundingClientRect();
|
|
25
|
+
positionRef.current = { left, top };
|
|
26
|
+
};
|
|
27
|
+
const triggerHide = (hide, rect) => {
|
|
28
|
+
if (!dom) {
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
isHideRef.current = hide;
|
|
32
|
+
if (hide) {
|
|
33
|
+
const [edge] = getNearlyEdge(dom, interactiveEdges, rect);
|
|
34
|
+
const { left, top, width, height } = rect || dom.getBoundingClientRect();
|
|
35
|
+
const screenSize = getScreenSize();
|
|
36
|
+
if (edge === 'left' || edge === 'right') {
|
|
37
|
+
const resolvedLeftSize = typeof leftSize !== 'undefined'
|
|
38
|
+
? Math.max(Math.min(leftSize, width - 10), 10)
|
|
39
|
+
: width / 2;
|
|
40
|
+
setPosition({
|
|
41
|
+
left: edge === 'left'
|
|
42
|
+
? resolvedLeftSize - width
|
|
43
|
+
: screenSize.width - resolvedLeftSize,
|
|
44
|
+
top,
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
else if (edge === 'top' || edge === 'bottom') {
|
|
48
|
+
const resolvedLeftSize = typeof leftSize !== 'undefined'
|
|
49
|
+
? Math.max(Math.min(leftSize, height - 10), 10)
|
|
50
|
+
: height / 2;
|
|
51
|
+
setPosition({
|
|
52
|
+
left,
|
|
53
|
+
top: edge === 'top'
|
|
54
|
+
? resolvedLeftSize - height
|
|
55
|
+
: screenSize.height - resolvedLeftSize,
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
positionRef.current && setPosition(positionRef.current);
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
const setHide = (hide, rect) => {
|
|
64
|
+
if (isHideRef.current === hide || !dom || !enable) {
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
triggerHide(hide, rect);
|
|
68
|
+
};
|
|
69
|
+
const setHideByMouse = (hide) => {
|
|
70
|
+
if (hide) {
|
|
71
|
+
if (setByMouseEnterRef.current) {
|
|
72
|
+
setHide(true);
|
|
73
|
+
setByMouseEnterRef.current = false;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
else if (isHideRef.current) {
|
|
77
|
+
setByMouseEnterRef.current = true;
|
|
78
|
+
setHide(false);
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
const setHideByApi = (hide) => {
|
|
82
|
+
setByMouseEnterRef.current = false;
|
|
83
|
+
setHide(hide);
|
|
84
|
+
};
|
|
85
|
+
const debounceTriggerHide = useDebounce(triggerHide, 100);
|
|
86
|
+
const throttleSetHideByMouse = useThrottle(setHideByMouse, 250);
|
|
87
|
+
const throttleSetHideByApi = useThrottle(setHideByApi, 250);
|
|
88
|
+
useEffect(() => {
|
|
89
|
+
if (!dom) {
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
const enter = () => {
|
|
93
|
+
throttleSetHideByMouse(false);
|
|
94
|
+
};
|
|
95
|
+
const leave = () => {
|
|
96
|
+
throttleSetHideByMouse(true);
|
|
97
|
+
};
|
|
98
|
+
dom.addEventListener('mouseenter', enter);
|
|
99
|
+
dom.addEventListener('mouseleave', leave);
|
|
100
|
+
return () => {
|
|
101
|
+
dom.removeEventListener('mouseenter', enter);
|
|
102
|
+
dom.removeEventListener('mouseleave', leave);
|
|
103
|
+
};
|
|
104
|
+
}, [dom]);
|
|
105
|
+
useEffect(() => {
|
|
106
|
+
if (!dom) {
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
const onResize = () => {
|
|
110
|
+
if (isHideRef.current) {
|
|
111
|
+
debounceTriggerHide(true);
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
window.addEventListener('resize', onResize);
|
|
115
|
+
return () => {
|
|
116
|
+
window.removeEventListener('resize', onResize);
|
|
117
|
+
};
|
|
118
|
+
}, [dom]);
|
|
119
|
+
const handleMove = (e) => {
|
|
120
|
+
if (!enable || !dom) {
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
setByMouseEnterRef.current = false;
|
|
124
|
+
const { clientX, clientY } = e;
|
|
125
|
+
const rect = dom.getBoundingClientRect();
|
|
126
|
+
const { width, height } = rect;
|
|
127
|
+
const screenSize = getScreenSize();
|
|
128
|
+
// 判断是否贴近边缘
|
|
129
|
+
if (clientX > width / 2 &&
|
|
130
|
+
clientX < screenSize.width - width / 2 &&
|
|
131
|
+
clientY > height / 2 &&
|
|
132
|
+
clientY < screenSize.height - height / 2) {
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
if (isHideRef.current) {
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
const [edge] = getNearlyEdge(dom, interactiveEdges, rect);
|
|
139
|
+
const conditions = {
|
|
140
|
+
left: clientX <= width / 2,
|
|
141
|
+
right: clientX >= screenSize.width - width / 2,
|
|
142
|
+
top: clientY <= height / 2,
|
|
143
|
+
bottom: clientY >= screenSize.height - height / 2,
|
|
144
|
+
};
|
|
145
|
+
if (edge && conditions[edge]) {
|
|
146
|
+
saveRestorePosition(rect);
|
|
147
|
+
setHide(true, rect);
|
|
148
|
+
}
|
|
149
|
+
};
|
|
150
|
+
const throttleHandleMove = useThrottle(handleMove, 100);
|
|
151
|
+
return {
|
|
152
|
+
isHideRef,
|
|
153
|
+
handleMove: throttleHandleMove,
|
|
154
|
+
setHide: throttleSetHideByApi,
|
|
155
|
+
saveRestorePosition,
|
|
156
|
+
};
|
|
157
|
+
}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { useEffect, useRef, useState } from 'react';
|
|
2
|
+
import { adjustBySafeAreaMargin, getScreenSize } from '../util';
|
|
3
|
+
function normalizePositionRatio(ratio) {
|
|
4
|
+
ratio = Number(ratio);
|
|
5
|
+
if (typeof ratio !== 'number' || isNaN(ratio)) {
|
|
6
|
+
return 1;
|
|
7
|
+
}
|
|
8
|
+
return Math.max(0, Math.min(1, ratio));
|
|
9
|
+
}
|
|
10
|
+
function parsePosition(position, element) {
|
|
11
|
+
if (!position || !element) {
|
|
12
|
+
return undefined;
|
|
13
|
+
}
|
|
14
|
+
if (Array.isArray(position)) {
|
|
15
|
+
const [widthRatio, heightRatio] = position;
|
|
16
|
+
const { width, height } = getScreenSize();
|
|
17
|
+
const rect = element.getBoundingClientRect();
|
|
18
|
+
const maxLeft = width - rect.width;
|
|
19
|
+
const maxTop = height - rect.height;
|
|
20
|
+
return {
|
|
21
|
+
left: Math.min(maxLeft, normalizePositionRatio(widthRatio) * width),
|
|
22
|
+
top: Math.min(maxTop, normalizePositionRatio(heightRatio) * height),
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
return position;
|
|
26
|
+
}
|
|
27
|
+
function normalizePositionStyle(position) {
|
|
28
|
+
if (!position) {
|
|
29
|
+
return undefined;
|
|
30
|
+
}
|
|
31
|
+
return {
|
|
32
|
+
transform: `translate(${position.left}px, ${position.top}px)`,
|
|
33
|
+
left: 0,
|
|
34
|
+
top: 0,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* 拖拽逻辑
|
|
39
|
+
*/
|
|
40
|
+
export function useDragable(dom, options) {
|
|
41
|
+
const optionsRef = useRef(options);
|
|
42
|
+
optionsRef.current = options;
|
|
43
|
+
const [position, setPosition] = useState();
|
|
44
|
+
useEffect(() => {
|
|
45
|
+
if (!dom || !options.enable) {
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
let isDown = false;
|
|
49
|
+
let isMoving = false;
|
|
50
|
+
let leftOffset = 0;
|
|
51
|
+
let topOffset = 0;
|
|
52
|
+
let domSize;
|
|
53
|
+
let domRect;
|
|
54
|
+
let lastPosition;
|
|
55
|
+
const down = (e) => {
|
|
56
|
+
var _a, _b;
|
|
57
|
+
isDown = true;
|
|
58
|
+
lastPosition = undefined;
|
|
59
|
+
const rect = dom.getBoundingClientRect();
|
|
60
|
+
leftOffset = rect.left - e.clientX;
|
|
61
|
+
topOffset = rect.top - e.clientY;
|
|
62
|
+
domSize = { width: rect.width, height: rect.height };
|
|
63
|
+
(_b = (_a = optionsRef.current).onDragBefore) === null || _b === void 0 ? void 0 : _b.call(_a, dom);
|
|
64
|
+
domRect = dom.getBoundingClientRect();
|
|
65
|
+
};
|
|
66
|
+
const move = (e) => {
|
|
67
|
+
var _a, _b, _c, _d;
|
|
68
|
+
if (!isDown) {
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
e.preventDefault();
|
|
72
|
+
isMoving = true;
|
|
73
|
+
// do update dom position
|
|
74
|
+
const position = Object.assign({ left: e.clientX + leftOffset, top: e.clientY + topOffset }, domSize);
|
|
75
|
+
const adjustPosition = adjustBySafeAreaMargin(position, domRect, optionsRef.current.safeAreaMargin);
|
|
76
|
+
const { left, top } = adjustPosition || position;
|
|
77
|
+
if (lastPosition && lastPosition.left === left && lastPosition.top === top) {
|
|
78
|
+
(_b = (_a = optionsRef.current).onDraging) === null || _b === void 0 ? void 0 : _b.call(_a, dom, e);
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
lastPosition = { left, top };
|
|
82
|
+
const res = (_d = (_c = optionsRef.current).onDraging) === null || _d === void 0 ? void 0 : _d.call(_c, dom, e);
|
|
83
|
+
if (res === false) {
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
dom.style.transition = 'unset';
|
|
87
|
+
dom.style.transform = `translate(${left}px, ${top}px)`;
|
|
88
|
+
dom.style.left = `0px`;
|
|
89
|
+
dom.style.top = `0px`;
|
|
90
|
+
};
|
|
91
|
+
const up = (e) => {
|
|
92
|
+
var _a, _b;
|
|
93
|
+
if (!isDown) {
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
if (!isMoving) {
|
|
97
|
+
isDown = false;
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
dom.style.transition = '';
|
|
101
|
+
isDown = false;
|
|
102
|
+
isMoving = false;
|
|
103
|
+
leftOffset = 0;
|
|
104
|
+
topOffset = 0;
|
|
105
|
+
(_b = (_a = optionsRef.current).onDragend) === null || _b === void 0 ? void 0 : _b.call(_a, dom);
|
|
106
|
+
if (dom !== e.target && !dom.contains(e.target)) {
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
// do update position
|
|
110
|
+
const rect = dom.getBoundingClientRect();
|
|
111
|
+
setPosition({ left: rect.left, top: rect.top });
|
|
112
|
+
};
|
|
113
|
+
dom.addEventListener('mousedown', down);
|
|
114
|
+
window.addEventListener('mousemove', move);
|
|
115
|
+
window.addEventListener('mouseup', up);
|
|
116
|
+
return () => {
|
|
117
|
+
dom.removeEventListener('mousedown', down);
|
|
118
|
+
window.removeEventListener('move', move);
|
|
119
|
+
window.removeEventListener('up', up);
|
|
120
|
+
};
|
|
121
|
+
}, [dom, options.enable]);
|
|
122
|
+
return {
|
|
123
|
+
className: '',
|
|
124
|
+
style: (position ? normalizePositionStyle(position) : undefined) ||
|
|
125
|
+
parsePosition(options.defaultPosition, dom) ||
|
|
126
|
+
{},
|
|
127
|
+
};
|
|
128
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { Margin, Edge, Position } from '../types';
|
|
2
|
+
/**
|
|
3
|
+
* 吸边逻辑
|
|
4
|
+
*/
|
|
5
|
+
export declare function useNestleEdge(dom: HTMLElement | null, { enable, safeAreaMargin, interactiveEdges, onUpdate, onUpdateEnd, }: {
|
|
6
|
+
enable?: boolean;
|
|
7
|
+
safeAreaMargin?: Margin;
|
|
8
|
+
interactiveEdges?: Edge[];
|
|
9
|
+
onUpdate?: (position: Position) => void | false;
|
|
10
|
+
onUpdateEnd?: (position: Position) => void;
|
|
11
|
+
}): {
|
|
12
|
+
update: (animation?: boolean) => void;
|
|
13
|
+
};
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { useEffect, useRef } from 'react';
|
|
2
|
+
import { useDebounce } from '../../utils';
|
|
3
|
+
import { getScreenSize, getNearlyEdge, adjustBySafeAreaMargin } from '../util';
|
|
4
|
+
/**
|
|
5
|
+
* 吸边逻辑
|
|
6
|
+
*/
|
|
7
|
+
export function useNestleEdge(dom, { enable, safeAreaMargin, interactiveEdges, onUpdate, onUpdateEnd, }) {
|
|
8
|
+
const eventTimerRef = useRef();
|
|
9
|
+
const triggerUpdateEnd = (position) => {
|
|
10
|
+
clearTimeout(eventTimerRef.current);
|
|
11
|
+
eventTimerRef.current = setTimeout(() => {
|
|
12
|
+
onUpdateEnd === null || onUpdateEnd === void 0 ? void 0 : onUpdateEnd(position);
|
|
13
|
+
}, 300);
|
|
14
|
+
};
|
|
15
|
+
const update = (animation = true) => {
|
|
16
|
+
if (!enable || !dom) {
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
const rect = dom.getBoundingClientRect();
|
|
20
|
+
const { left, top, width, height } = rect;
|
|
21
|
+
const { width: screenWidth, height: screenHeight } = getScreenSize();
|
|
22
|
+
const [edge] = getNearlyEdge(dom, interactiveEdges, rect);
|
|
23
|
+
const position = { left, top };
|
|
24
|
+
switch (edge) {
|
|
25
|
+
case 'top': {
|
|
26
|
+
position.top = 0;
|
|
27
|
+
break;
|
|
28
|
+
}
|
|
29
|
+
case 'right': {
|
|
30
|
+
position.left = screenWidth - width;
|
|
31
|
+
break;
|
|
32
|
+
}
|
|
33
|
+
case 'bottom': {
|
|
34
|
+
position.top = screenHeight - height;
|
|
35
|
+
break;
|
|
36
|
+
}
|
|
37
|
+
case 'left': {
|
|
38
|
+
position.left = 0;
|
|
39
|
+
break;
|
|
40
|
+
}
|
|
41
|
+
// no default
|
|
42
|
+
}
|
|
43
|
+
if (position.left !== left || position.left !== top) {
|
|
44
|
+
const { left, top } = adjustBySafeAreaMargin(position, dom.getBoundingClientRect(), safeAreaMargin) ||
|
|
45
|
+
position;
|
|
46
|
+
const shouldContinue = onUpdate === null || onUpdate === void 0 ? void 0 : onUpdate({ left, top });
|
|
47
|
+
if (shouldContinue === false) {
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
dom.style.left = '0px';
|
|
51
|
+
dom.style.top = '0px';
|
|
52
|
+
if (animation) {
|
|
53
|
+
dom.style.transition = 'transform 0.25s';
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
dom.style.transition = '';
|
|
57
|
+
}
|
|
58
|
+
dom.style.transform = `translate(${left}px, ${top}px)`;
|
|
59
|
+
triggerUpdateEnd({ left, top });
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
const debounceUpdate = useDebounce(() => update(), 100);
|
|
63
|
+
useEffect(() => {
|
|
64
|
+
if (dom) {
|
|
65
|
+
update(false);
|
|
66
|
+
}
|
|
67
|
+
}, [dom]);
|
|
68
|
+
useEffect(() => {
|
|
69
|
+
window.addEventListener('resize', debounceUpdate);
|
|
70
|
+
return () => {
|
|
71
|
+
window.removeEventListener('resize', debounceUpdate);
|
|
72
|
+
};
|
|
73
|
+
}, []);
|
|
74
|
+
return {
|
|
75
|
+
update,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/// <reference types="react" />
|
|
2
|
+
declare const FloatButtonWithSub: import("@alifd/next/types/config-provider/types").ConfiguredComponentClass<Pick<import("./types").FloatButtonProps & import("react").RefAttributes<import("./types").FloatButtonRef>, keyof import("./types").FloatButtonProps | "key"> & import("@alifd/next/types/config-provider/types").ComponentCommonProps, import("./types").FloatButtonRef, {}> & {
|
|
3
|
+
Backtop: import("@alifd/next/types/config-provider/types").ConfiguredComponentClass<Pick<import("./types").BacktopProps & import("react").RefAttributes<import("./types").BacktopRef>, "key" | keyof import("./types").BacktopProps> & import("@alifd/next/types/config-provider/types").ComponentCommonProps, import("./types").BacktopRef, {}>;
|
|
4
|
+
};
|
|
5
|
+
export type { BacktopRef, BacktopProps, FloatButtonRef, FloatButtonProps } from './types';
|
|
6
|
+
export default FloatButtonWithSub;
|