@pointcloud/pcloud-components 0.1.24 → 0.1.26
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 +337 -337
- package/dist/esm/ContextMenu/index.d.ts +28 -0
- package/dist/esm/ContextMenu/index.js +146 -0
- package/dist/esm/ContextMenu/index.less +73 -0
- package/dist/esm/DSelect/index.d.ts +1 -1
- package/dist/esm/IPAddress/index.less +75 -75
- package/dist/esm/SignaturePad/index.d.ts +32 -0
- package/dist/esm/SignaturePad/index.js +194 -0
- package/dist/esm/SignaturePad/style/index.less +47 -0
- package/dist/esm/index.d.ts +4 -0
- package/dist/esm/index.js +3 -1
- package/dist/umd/pcloud-components.min.css +1 -1
- package/dist/umd/pcloud-components.min.js +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { ReactNode } from 'react';
|
|
3
|
+
import './index.less';
|
|
4
|
+
export interface ContextMenuProps {
|
|
5
|
+
/** 自定义类名 */
|
|
6
|
+
className?: string;
|
|
7
|
+
/** 自定义样式 */
|
|
8
|
+
style?: React.CSSProperties;
|
|
9
|
+
/** 菜单内容 */
|
|
10
|
+
children: ReactNode;
|
|
11
|
+
/** 触发区域内容 */
|
|
12
|
+
trigger: ReactNode;
|
|
13
|
+
/** 点击外部是否关闭 */
|
|
14
|
+
closeOnOutside?: boolean;
|
|
15
|
+
/** 展示位置偏移量 */
|
|
16
|
+
offset?: {
|
|
17
|
+
x?: number;
|
|
18
|
+
y?: number;
|
|
19
|
+
};
|
|
20
|
+
/** 指定菜单挂载的父节点 */
|
|
21
|
+
getPopupContainer?: (props: any) => HTMLElement;
|
|
22
|
+
/** 菜单显示时的回调 */
|
|
23
|
+
onShow?: () => void;
|
|
24
|
+
/** 菜单隐藏时的回调 */
|
|
25
|
+
onHide?: () => void;
|
|
26
|
+
}
|
|
27
|
+
declare const ContextMenu: React.FC<ContextMenuProps>;
|
|
28
|
+
export default ContextMenu;
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import _objectSpread from "@babel/runtime/helpers/esm/objectSpread2";
|
|
2
|
+
import _defineProperty from "@babel/runtime/helpers/esm/defineProperty";
|
|
3
|
+
import _slicedToArray from "@babel/runtime/helpers/esm/slicedToArray";
|
|
4
|
+
import React, { useMemo, useEffect, useCallback, useRef, useState, useContext } from 'react';
|
|
5
|
+
import { createPortal } from 'react-dom';
|
|
6
|
+
import classNames from 'classnames';
|
|
7
|
+
import { ConfigContext } from "../ConfigProvider";
|
|
8
|
+
import "./index.less";
|
|
9
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
10
|
+
import { Fragment as _Fragment } from "react/jsx-runtime";
|
|
11
|
+
import { jsxs as _jsxs } from "react/jsx-runtime";
|
|
12
|
+
var ContextMenu = function ContextMenu(_ref) {
|
|
13
|
+
var className = _ref.className,
|
|
14
|
+
style = _ref.style,
|
|
15
|
+
children = _ref.children,
|
|
16
|
+
trigger = _ref.trigger,
|
|
17
|
+
_ref$closeOnOutside = _ref.closeOnOutside,
|
|
18
|
+
closeOnOutside = _ref$closeOnOutside === void 0 ? true : _ref$closeOnOutside,
|
|
19
|
+
_ref$offset = _ref.offset,
|
|
20
|
+
offset = _ref$offset === void 0 ? {
|
|
21
|
+
x: 0,
|
|
22
|
+
y: 0
|
|
23
|
+
} : _ref$offset,
|
|
24
|
+
onShow = _ref.onShow,
|
|
25
|
+
onHide = _ref.onHide,
|
|
26
|
+
getPopupContainer = _ref.getPopupContainer;
|
|
27
|
+
var _useState = useState(false),
|
|
28
|
+
_useState2 = _slicedToArray(_useState, 2),
|
|
29
|
+
visible = _useState2[0],
|
|
30
|
+
setVisible = _useState2[1];
|
|
31
|
+
var _useState3 = useState({
|
|
32
|
+
x: 0,
|
|
33
|
+
y: 0
|
|
34
|
+
}),
|
|
35
|
+
_useState4 = _slicedToArray(_useState3, 2),
|
|
36
|
+
position = _useState4[0],
|
|
37
|
+
setPosition = _useState4[1];
|
|
38
|
+
var _useContext = useContext(ConfigContext),
|
|
39
|
+
prefixCls = _useContext.prefixCls,
|
|
40
|
+
getPrefixCls = _useContext.getPrefixCls;
|
|
41
|
+
var triggerRef = useRef(null);
|
|
42
|
+
var menuRef = useRef(null);
|
|
43
|
+
var classname = getPrefixCls('context-menu');
|
|
44
|
+
var wrapperClass = classNames(_defineProperty({}, "".concat(prefixCls, "-context-menu"), !!prefixCls), classname, className);
|
|
45
|
+
var handleGetPopupContainer = useCallback(function (triggerNode) {
|
|
46
|
+
if (typeof getPopupContainer === 'function') {
|
|
47
|
+
var container = getPopupContainer(triggerNode);
|
|
48
|
+
return container instanceof HTMLElement ? container : document.body;
|
|
49
|
+
}
|
|
50
|
+
return document.body;
|
|
51
|
+
}, [getPopupContainer]);
|
|
52
|
+
var popContainer = useMemo(function () {
|
|
53
|
+
return triggerRef.current ? handleGetPopupContainer(triggerRef.current) : document.body;
|
|
54
|
+
}, [visible, handleGetPopupContainer]);
|
|
55
|
+
|
|
56
|
+
// 处理右键点击事件
|
|
57
|
+
var handleContextMenu = useCallback(function (e) {
|
|
58
|
+
var _menuRef$current, _menuRef$current2;
|
|
59
|
+
e.preventDefault();
|
|
60
|
+
var triggerElement = triggerRef.current;
|
|
61
|
+
if (!triggerElement) return;
|
|
62
|
+
var container = triggerElement ? handleGetPopupContainer(triggerElement) : document.body;
|
|
63
|
+
var x, y;
|
|
64
|
+
if (container === document.body) {
|
|
65
|
+
// 如果是body容器,使用相对于视窗的位置(fixed定位)
|
|
66
|
+
x = e.clientX + (offset.x || 0);
|
|
67
|
+
y = e.clientY + (offset.y || 0);
|
|
68
|
+
} else {
|
|
69
|
+
// 如果指定了容器,使用相对于该容器的位置(absolute定位)
|
|
70
|
+
var containerRect = container.getBoundingClientRect();
|
|
71
|
+
x = e.clientX - containerRect.left + container.scrollLeft + (offset.x || 0);
|
|
72
|
+
y = e.clientY - containerRect.top + container.scrollTop + (offset.y || 0);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// 确保菜单不超出容器边界
|
|
76
|
+
var menuWidth = ((_menuRef$current = menuRef.current) === null || _menuRef$current === void 0 ? void 0 : _menuRef$current.offsetWidth) || 0;
|
|
77
|
+
var menuHeight = ((_menuRef$current2 = menuRef.current) === null || _menuRef$current2 === void 0 ? void 0 : _menuRef$current2.offsetHeight) || 0;
|
|
78
|
+
var finalX, finalY;
|
|
79
|
+
if (container === document.body) {
|
|
80
|
+
// body容器情况,相对于视窗边界
|
|
81
|
+
finalX = Math.max(0, Math.min(x, window.innerWidth - menuWidth));
|
|
82
|
+
finalY = Math.max(0, Math.min(y, window.innerHeight - menuHeight));
|
|
83
|
+
} else {
|
|
84
|
+
// 自定义容器情况,相对于容器边界
|
|
85
|
+
var maxX = container.clientWidth - menuWidth;
|
|
86
|
+
var maxY = container.clientHeight - menuHeight;
|
|
87
|
+
finalX = Math.max(0, Math.min(x, maxX));
|
|
88
|
+
finalY = Math.max(0, Math.min(y, maxY));
|
|
89
|
+
}
|
|
90
|
+
setPosition({
|
|
91
|
+
x: finalX,
|
|
92
|
+
y: finalY
|
|
93
|
+
});
|
|
94
|
+
setVisible(true);
|
|
95
|
+
onShow === null || onShow === void 0 || onShow();
|
|
96
|
+
}, [offset, onShow, handleGetPopupContainer]);
|
|
97
|
+
|
|
98
|
+
// 处理点击事件
|
|
99
|
+
var handleMouseDown = useCallback(function (e) {
|
|
100
|
+
// 检查点击目标是否在菜单内部
|
|
101
|
+
if (menuRef.current && menuRef.current.contains(e.target)) {
|
|
102
|
+
// 如果点击在菜单内部,不关闭菜单,允许事件正常处理
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
if (closeOnOutside && menuRef.current && !menuRef.current.contains(e.target) && triggerRef.current && !triggerRef.current.contains(e.target)) {
|
|
106
|
+
setVisible(false);
|
|
107
|
+
onHide === null || onHide === void 0 || onHide();
|
|
108
|
+
}
|
|
109
|
+
}, [closeOnOutside, onHide]);
|
|
110
|
+
|
|
111
|
+
// 处理 ESC 键盘事件
|
|
112
|
+
var handleKeyDown = useCallback(function (e) {
|
|
113
|
+
if (e.key === 'Escape' && visible) {
|
|
114
|
+
setVisible(false);
|
|
115
|
+
onHide === null || onHide === void 0 || onHide();
|
|
116
|
+
}
|
|
117
|
+
}, [visible, onHide]);
|
|
118
|
+
useEffect(function () {
|
|
119
|
+
if (visible) {
|
|
120
|
+
document.addEventListener('mousedown', handleMouseDown);
|
|
121
|
+
document.addEventListener('keydown', handleKeyDown);
|
|
122
|
+
}
|
|
123
|
+
return function () {
|
|
124
|
+
document.removeEventListener('mousedown', handleMouseDown);
|
|
125
|
+
document.removeEventListener('keydown', handleKeyDown);
|
|
126
|
+
};
|
|
127
|
+
}, [visible, handleMouseDown, handleKeyDown]);
|
|
128
|
+
return /*#__PURE__*/_jsxs(_Fragment, {
|
|
129
|
+
children: [/*#__PURE__*/_jsx("div", {
|
|
130
|
+
ref: triggerRef,
|
|
131
|
+
onContextMenu: handleContextMenu,
|
|
132
|
+
children: trigger
|
|
133
|
+
}), visible && /*#__PURE__*/createPortal( /*#__PURE__*/_jsx("div", {
|
|
134
|
+
ref: menuRef,
|
|
135
|
+
className: wrapperClass,
|
|
136
|
+
style: _objectSpread(_objectSpread({}, style), {}, {
|
|
137
|
+
position: popContainer === document.body ? 'fixed' : 'absolute',
|
|
138
|
+
left: position.x,
|
|
139
|
+
top: position.y,
|
|
140
|
+
zIndex: 1050
|
|
141
|
+
}),
|
|
142
|
+
children: children
|
|
143
|
+
}), popContainer)]
|
|
144
|
+
});
|
|
145
|
+
};
|
|
146
|
+
export default ContextMenu;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
@import '../commonStyle/index.less';
|
|
2
|
+
|
|
3
|
+
@context-menu-bg: #ffffff;
|
|
4
|
+
@context-menu-border-color: #f0f0f0;
|
|
5
|
+
@context-menu-shadow: 0 3px 6px -4px rgba(0, 0, 0, 0.12), 0 6px 16px 0 rgba(0, 0, 0, 0.08), 0 9px 28px 8px rgba(0, 0, 0, 0.05);
|
|
6
|
+
@border-radius-base: 1px;
|
|
7
|
+
|
|
8
|
+
@keyframes contextmenu-show {
|
|
9
|
+
0% {
|
|
10
|
+
opacity: 0;
|
|
11
|
+
transform: scale(0.8);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
100% {
|
|
15
|
+
opacity: 1;
|
|
16
|
+
transform: scale(1);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
.@{prefix}-context-menu {
|
|
21
|
+
background: @context-menu-bg;
|
|
22
|
+
border-radius: @border-radius-base;
|
|
23
|
+
border: 1px solid @context-menu-border-color;
|
|
24
|
+
box-shadow: @context-menu-shadow;
|
|
25
|
+
padding: 4px 0;
|
|
26
|
+
min-width: 120px;
|
|
27
|
+
opacity: 0;
|
|
28
|
+
transform: scale(0.8);
|
|
29
|
+
transform-origin: top left;
|
|
30
|
+
animation: contextMenuShow 0.2s ease-out forwards;
|
|
31
|
+
|
|
32
|
+
&-item {
|
|
33
|
+
padding: 5px 12px;
|
|
34
|
+
cursor: pointer;
|
|
35
|
+
user-select: none;
|
|
36
|
+
transition: all 0.3s;
|
|
37
|
+
display: flex;
|
|
38
|
+
align-items: center;
|
|
39
|
+
gap: 8px;
|
|
40
|
+
color: rgba(0, 0, 0, 88%);
|
|
41
|
+
|
|
42
|
+
&:hover {
|
|
43
|
+
background: rgba(0, 0, 0, 4%);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
&-disabled {
|
|
47
|
+
color: rgba(0, 0, 0, 25%);
|
|
48
|
+
cursor: not-allowed;
|
|
49
|
+
|
|
50
|
+
&:hover {
|
|
51
|
+
background: none;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
&-divider {
|
|
56
|
+
height: 1px;
|
|
57
|
+
margin: 4px 0;
|
|
58
|
+
background: #f0f0f0;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
@keyframes contextmenu-show {
|
|
64
|
+
0% {
|
|
65
|
+
opacity: 0;
|
|
66
|
+
transform: scale(0.8);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
100% {
|
|
70
|
+
opacity: 1;
|
|
71
|
+
transform: scale(1);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
@@ -11,7 +11,7 @@ export declare type DSelectProps = Omit<SelectProps, 'options' | 'onSearch' | 'l
|
|
|
11
11
|
/** 是否开启防抖: true表示800毫秒,true表示默认值,false或0表示不开启 */
|
|
12
12
|
debounce?: boolean | number;
|
|
13
13
|
};
|
|
14
|
-
declare const DSelect: React.ForwardRefExoticComponent<Omit<SelectProps<any, DefaultOptionType>, "
|
|
14
|
+
declare const DSelect: React.ForwardRefExoticComponent<Omit<SelectProps<any, DefaultOptionType>, "options" | "onSearch" | "loading"> & {
|
|
15
15
|
/** antd的onSearch属性,onSearch有效时showSearch自动为true */
|
|
16
16
|
onSearch?: ((params?: any) => Promise<DefaultOptionType[] | any[]>) | undefined;
|
|
17
17
|
/** antd的options属性,可以是一个options数组,或一个返回等价options数组的promise */
|
|
@@ -1,75 +1,75 @@
|
|
|
1
|
-
@import '../commonStyle/index.less';
|
|
2
|
-
|
|
3
|
-
.@{prefix}-ip-address {
|
|
4
|
-
border: 1px solid #d9d9d9;
|
|
5
|
-
border-radius: 2px;
|
|
6
|
-
transition: all 0.3s;
|
|
7
|
-
display: inline-block;
|
|
8
|
-
|
|
9
|
-
&.small {
|
|
10
|
-
height: 24px;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
&.large {
|
|
14
|
-
padding: 6.5px 10px;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
&.middle {
|
|
18
|
-
padding: 4px 10px;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
&.disabled {
|
|
22
|
-
color: rgba(0, 0, 0, 25%);
|
|
23
|
-
background-color: #f5f5f5;
|
|
24
|
-
border-color: #d9d9d9;
|
|
25
|
-
box-shadow: none;
|
|
26
|
-
cursor: not-allowed;
|
|
27
|
-
opacity: 1;
|
|
28
|
-
|
|
29
|
-
&:hover {
|
|
30
|
-
border-color: #d9d9d9;
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
&:hover {
|
|
35
|
-
border-color: #4d90ff;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
&:focus-within {
|
|
39
|
-
border-color: #40a9ff;
|
|
40
|
-
box-shadow: 0 0 0 2px rgba(24, 144, 255, 20%);
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
input {
|
|
44
|
-
border: none;
|
|
45
|
-
background: none;
|
|
46
|
-
text-align: center;
|
|
47
|
-
outline: 0;
|
|
48
|
-
padding: 0;
|
|
49
|
-
margin: 0;
|
|
50
|
-
box-sizing: border-box;
|
|
51
|
-
font-variant: tabular-nums;
|
|
52
|
-
font-feature-settings: 'tnum';
|
|
53
|
-
position: relative;
|
|
54
|
-
display: inline-block;
|
|
55
|
-
color: #000000d9;
|
|
56
|
-
font-size: 14px;
|
|
57
|
-
line-height: 1.5715;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
input[disabled] {
|
|
61
|
-
cursor: not-allowed;
|
|
62
|
-
color: inherit;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
input[type='number'] {
|
|
66
|
-
appearance: textfield; /* 移除Firefox的特殊样式 */
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
/* 移除上下箭头 */
|
|
70
|
-
input[type='number']::-webkit-outer-spin-button,
|
|
71
|
-
input[type='number']::-webkit-inner-spin-button {
|
|
72
|
-
appearance: none;
|
|
73
|
-
margin: 0;
|
|
74
|
-
}
|
|
75
|
-
}
|
|
1
|
+
@import '../commonStyle/index.less';
|
|
2
|
+
|
|
3
|
+
.@{prefix}-ip-address {
|
|
4
|
+
border: 1px solid #d9d9d9;
|
|
5
|
+
border-radius: 2px;
|
|
6
|
+
transition: all 0.3s;
|
|
7
|
+
display: inline-block;
|
|
8
|
+
|
|
9
|
+
&.small {
|
|
10
|
+
height: 24px;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
&.large {
|
|
14
|
+
padding: 6.5px 10px;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
&.middle {
|
|
18
|
+
padding: 4px 10px;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
&.disabled {
|
|
22
|
+
color: rgba(0, 0, 0, 25%);
|
|
23
|
+
background-color: #f5f5f5;
|
|
24
|
+
border-color: #d9d9d9;
|
|
25
|
+
box-shadow: none;
|
|
26
|
+
cursor: not-allowed;
|
|
27
|
+
opacity: 1;
|
|
28
|
+
|
|
29
|
+
&:hover {
|
|
30
|
+
border-color: #d9d9d9;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
&:hover {
|
|
35
|
+
border-color: #4d90ff;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
&:focus-within {
|
|
39
|
+
border-color: #40a9ff;
|
|
40
|
+
box-shadow: 0 0 0 2px rgba(24, 144, 255, 20%);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
input {
|
|
44
|
+
border: none;
|
|
45
|
+
background: none;
|
|
46
|
+
text-align: center;
|
|
47
|
+
outline: 0;
|
|
48
|
+
padding: 0;
|
|
49
|
+
margin: 0;
|
|
50
|
+
box-sizing: border-box;
|
|
51
|
+
font-variant: tabular-nums;
|
|
52
|
+
font-feature-settings: 'tnum';
|
|
53
|
+
position: relative;
|
|
54
|
+
display: inline-block;
|
|
55
|
+
color: #000000d9;
|
|
56
|
+
font-size: 14px;
|
|
57
|
+
line-height: 1.5715;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
input[disabled] {
|
|
61
|
+
cursor: not-allowed;
|
|
62
|
+
color: inherit;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
input[type='number'] {
|
|
66
|
+
appearance: textfield; /* 移除Firefox的特殊样式 */
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/* 移除上下箭头 */
|
|
70
|
+
input[type='number']::-webkit-outer-spin-button,
|
|
71
|
+
input[type='number']::-webkit-inner-spin-button {
|
|
72
|
+
appearance: none;
|
|
73
|
+
margin: 0;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/// <reference types="react" />
|
|
2
|
+
import './style/index.less';
|
|
3
|
+
export interface SignaturePadHandle {
|
|
4
|
+
/** 清除画布 */
|
|
5
|
+
clear: () => void;
|
|
6
|
+
/** 获取签名图片的 base64 数据 */
|
|
7
|
+
getDataURL: () => string | undefined;
|
|
8
|
+
}
|
|
9
|
+
export interface SignaturePadProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
10
|
+
/** 画布宽度 */
|
|
11
|
+
width?: number;
|
|
12
|
+
/** 画布高度 */
|
|
13
|
+
height?: number;
|
|
14
|
+
/** 线条颜色 */
|
|
15
|
+
penColor?: string;
|
|
16
|
+
/** 线条粗细 */
|
|
17
|
+
penWidth?: number;
|
|
18
|
+
/** 背景颜色 */
|
|
19
|
+
backgroundColor?: string;
|
|
20
|
+
/** 清除按钮文字 */
|
|
21
|
+
clearText?: string;
|
|
22
|
+
/** 完成按钮文字 */
|
|
23
|
+
doneText?: string;
|
|
24
|
+
/** 是否显示工具栏 */
|
|
25
|
+
showToolbar?: boolean;
|
|
26
|
+
/** 签名完成回调 */
|
|
27
|
+
onDone?: (_dataUrl: string) => void;
|
|
28
|
+
/** 默认的签名图片(base64或图片URL) */
|
|
29
|
+
defaultValue?: string;
|
|
30
|
+
}
|
|
31
|
+
declare const SignaturePad: import("react").ForwardRefExoticComponent<SignaturePadProps & import("react").RefAttributes<SignaturePadHandle>>;
|
|
32
|
+
export default SignaturePad;
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
import _defineProperty from "@babel/runtime/helpers/esm/defineProperty";
|
|
2
|
+
import { useCallback, useContext, useEffect, useRef, forwardRef, useImperativeHandle } from 'react';
|
|
3
|
+
import classNames from 'classnames';
|
|
4
|
+
import { ConfigContext } from "../ConfigProvider";
|
|
5
|
+
import "./style/index.less";
|
|
6
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
7
|
+
import { jsxs as _jsxs } from "react/jsx-runtime";
|
|
8
|
+
var SignaturePad = /*#__PURE__*/forwardRef(function (_ref, ref) {
|
|
9
|
+
var className = _ref.className,
|
|
10
|
+
_ref$width = _ref.width,
|
|
11
|
+
width = _ref$width === void 0 ? 600 : _ref$width,
|
|
12
|
+
_ref$height = _ref.height,
|
|
13
|
+
height = _ref$height === void 0 ? 200 : _ref$height,
|
|
14
|
+
_ref$penColor = _ref.penColor,
|
|
15
|
+
penColor = _ref$penColor === void 0 ? '#000000' : _ref$penColor,
|
|
16
|
+
_ref$penWidth = _ref.penWidth,
|
|
17
|
+
penWidth = _ref$penWidth === void 0 ? 2 : _ref$penWidth,
|
|
18
|
+
_ref$backgroundColor = _ref.backgroundColor,
|
|
19
|
+
backgroundColor = _ref$backgroundColor === void 0 ? '#ffffff' : _ref$backgroundColor,
|
|
20
|
+
_ref$clearText = _ref.clearText,
|
|
21
|
+
clearText = _ref$clearText === void 0 ? '清除' : _ref$clearText,
|
|
22
|
+
_ref$doneText = _ref.doneText,
|
|
23
|
+
doneText = _ref$doneText === void 0 ? '完成' : _ref$doneText,
|
|
24
|
+
_ref$showToolbar = _ref.showToolbar,
|
|
25
|
+
showToolbar = _ref$showToolbar === void 0 ? true : _ref$showToolbar,
|
|
26
|
+
onDone = _ref.onDone,
|
|
27
|
+
defaultValue = _ref.defaultValue;
|
|
28
|
+
var canvasRef = useRef(null);
|
|
29
|
+
var isDrawing = useRef(false);
|
|
30
|
+
var lastX = useRef(0);
|
|
31
|
+
var lastY = useRef(0);
|
|
32
|
+
var _useContext = useContext(ConfigContext),
|
|
33
|
+
prefixCls = _useContext.prefixCls,
|
|
34
|
+
getPrefixCls = _useContext.getPrefixCls;
|
|
35
|
+
var classname = getPrefixCls('signature-pad');
|
|
36
|
+
var wrapperClass = classNames(_defineProperty({}, "".concat(prefixCls, "-signature-pad"), !!prefixCls), classname, className);
|
|
37
|
+
var initCanvas = useCallback(function () {
|
|
38
|
+
var canvas = canvasRef.current;
|
|
39
|
+
if (!canvas) return;
|
|
40
|
+
|
|
41
|
+
// 设置画布大小和背景色
|
|
42
|
+
canvas.width = width;
|
|
43
|
+
canvas.height = height;
|
|
44
|
+
var ctx = canvas.getContext('2d');
|
|
45
|
+
if (ctx) {
|
|
46
|
+
ctx.fillStyle = backgroundColor;
|
|
47
|
+
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
|
48
|
+
}
|
|
49
|
+
}, [width, height, backgroundColor]);
|
|
50
|
+
var loadImage = useCallback(function (src) {
|
|
51
|
+
var canvas = canvasRef.current;
|
|
52
|
+
if (!canvas) return;
|
|
53
|
+
var ctx = canvas.getContext('2d');
|
|
54
|
+
if (!ctx) return;
|
|
55
|
+
var image = new Image();
|
|
56
|
+
image.onload = function () {
|
|
57
|
+
// 先初始化画布
|
|
58
|
+
initCanvas();
|
|
59
|
+
// 在画布中心绘制图片
|
|
60
|
+
var scale = Math.min(canvas.width / image.width, canvas.height / image.height);
|
|
61
|
+
var x = (canvas.width - image.width * scale) / 2;
|
|
62
|
+
var y = (canvas.height - image.height * scale) / 2;
|
|
63
|
+
ctx.drawImage(image, x, y, image.width * scale, image.height * scale);
|
|
64
|
+
};
|
|
65
|
+
image.src = src;
|
|
66
|
+
}, [initCanvas]);
|
|
67
|
+
useEffect(function () {
|
|
68
|
+
initCanvas();
|
|
69
|
+
}, [initCanvas]);
|
|
70
|
+
useEffect(function () {
|
|
71
|
+
if (defaultValue) {
|
|
72
|
+
loadImage(defaultValue);
|
|
73
|
+
}
|
|
74
|
+
}, [defaultValue, loadImage]);
|
|
75
|
+
var startDrawing = useCallback(function (e) {
|
|
76
|
+
var canvas = canvasRef.current;
|
|
77
|
+
if (!canvas) return;
|
|
78
|
+
isDrawing.current = true;
|
|
79
|
+
var rect = canvas.getBoundingClientRect();
|
|
80
|
+
var x = ('touches' in e ? e.touches[0].clientX : e.clientX) - rect.left;
|
|
81
|
+
var y = ('touches' in e ? e.touches[0].clientY : e.clientY) - rect.top;
|
|
82
|
+
lastX.current = x;
|
|
83
|
+
lastY.current = y;
|
|
84
|
+
}, []);
|
|
85
|
+
var draw = useCallback(function (e) {
|
|
86
|
+
if (!isDrawing.current) return;
|
|
87
|
+
var canvas = canvasRef.current;
|
|
88
|
+
if (!canvas) return;
|
|
89
|
+
var rect = canvas.getBoundingClientRect();
|
|
90
|
+
var x = ('touches' in e ? e.touches[0].clientX : e.clientX) - rect.left;
|
|
91
|
+
var y = ('touches' in e ? e.touches[0].clientY : e.clientY) - rect.top;
|
|
92
|
+
var ctx = canvas.getContext('2d');
|
|
93
|
+
if (ctx) {
|
|
94
|
+
ctx.beginPath();
|
|
95
|
+
ctx.strokeStyle = penColor;
|
|
96
|
+
ctx.lineWidth = penWidth;
|
|
97
|
+
ctx.lineCap = 'round';
|
|
98
|
+
ctx.lineJoin = 'round';
|
|
99
|
+
ctx.moveTo(lastX.current, lastY.current);
|
|
100
|
+
ctx.lineTo(x, y);
|
|
101
|
+
ctx.stroke();
|
|
102
|
+
}
|
|
103
|
+
lastX.current = x;
|
|
104
|
+
lastY.current = y;
|
|
105
|
+
}, [penColor, penWidth]);
|
|
106
|
+
var stopDrawing = useCallback(function () {
|
|
107
|
+
isDrawing.current = false;
|
|
108
|
+
}, []);
|
|
109
|
+
useEffect(function () {
|
|
110
|
+
var canvas = canvasRef.current;
|
|
111
|
+
if (!canvas) return;
|
|
112
|
+
var handleEvent = function handleEvent(e) {
|
|
113
|
+
e.preventDefault();
|
|
114
|
+
var event = e;
|
|
115
|
+
switch (e.type) {
|
|
116
|
+
case 'mousedown':
|
|
117
|
+
case 'touchstart':
|
|
118
|
+
startDrawing(event);
|
|
119
|
+
break;
|
|
120
|
+
case 'mousemove':
|
|
121
|
+
case 'touchmove':
|
|
122
|
+
draw(event);
|
|
123
|
+
break;
|
|
124
|
+
case 'mouseup':
|
|
125
|
+
case 'mouseleave':
|
|
126
|
+
case 'touchend':
|
|
127
|
+
stopDrawing();
|
|
128
|
+
break;
|
|
129
|
+
default:
|
|
130
|
+
break;
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
var events = ['mousedown', 'mousemove', 'mouseup', 'mouseleave', 'touchstart', 'touchmove', 'touchend'];
|
|
134
|
+
|
|
135
|
+
// 添加事件监听
|
|
136
|
+
events.forEach(function (eventName) {
|
|
137
|
+
canvas.addEventListener(eventName, handleEvent);
|
|
138
|
+
});
|
|
139
|
+
return function () {
|
|
140
|
+
// 清除事件监听
|
|
141
|
+
events.forEach(function (eventName) {
|
|
142
|
+
canvas.removeEventListener(eventName, handleEvent);
|
|
143
|
+
});
|
|
144
|
+
};
|
|
145
|
+
}, [draw, startDrawing, stopDrawing]);
|
|
146
|
+
var handleClear = useCallback(function () {
|
|
147
|
+
var canvas = canvasRef.current;
|
|
148
|
+
if (!canvas) return;
|
|
149
|
+
var ctx = canvas.getContext('2d');
|
|
150
|
+
if (ctx) {
|
|
151
|
+
ctx.fillStyle = backgroundColor;
|
|
152
|
+
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
|
153
|
+
}
|
|
154
|
+
}, [backgroundColor]);
|
|
155
|
+
var handleDone = useCallback(function () {
|
|
156
|
+
var canvas = canvasRef.current;
|
|
157
|
+
if (!canvas) return;
|
|
158
|
+
var dataUrl = canvas.toDataURL('image/png');
|
|
159
|
+
onDone === null || onDone === void 0 || onDone(dataUrl);
|
|
160
|
+
}, [onDone]);
|
|
161
|
+
useImperativeHandle(ref, function () {
|
|
162
|
+
return {
|
|
163
|
+
clear: handleClear,
|
|
164
|
+
getDataURL: function getDataURL() {
|
|
165
|
+
var _canvasRef$current;
|
|
166
|
+
return (_canvasRef$current = canvasRef.current) === null || _canvasRef$current === void 0 ? void 0 : _canvasRef$current.toDataURL('image/png');
|
|
167
|
+
}
|
|
168
|
+
};
|
|
169
|
+
});
|
|
170
|
+
return /*#__PURE__*/_jsxs("div", {
|
|
171
|
+
className: wrapperClass,
|
|
172
|
+
children: [/*#__PURE__*/_jsx("canvas", {
|
|
173
|
+
ref: canvasRef,
|
|
174
|
+
className: "".concat(classname, "-canvas"),
|
|
175
|
+
style: {
|
|
176
|
+
touchAction: 'none'
|
|
177
|
+
}
|
|
178
|
+
}), showToolbar && /*#__PURE__*/_jsxs("div", {
|
|
179
|
+
className: "".concat(classname, "-toolbar"),
|
|
180
|
+
children: [/*#__PURE__*/_jsx("button", {
|
|
181
|
+
type: "button",
|
|
182
|
+
className: "".concat(classname, "-button"),
|
|
183
|
+
onClick: handleClear,
|
|
184
|
+
children: clearText
|
|
185
|
+
}), /*#__PURE__*/_jsx("button", {
|
|
186
|
+
type: "button",
|
|
187
|
+
className: "".concat(classname, "-button"),
|
|
188
|
+
onClick: handleDone,
|
|
189
|
+
children: doneText
|
|
190
|
+
})]
|
|
191
|
+
})]
|
|
192
|
+
});
|
|
193
|
+
});
|
|
194
|
+
export default SignaturePad;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
@import '../../commonStyle/index.less';
|
|
2
|
+
|
|
3
|
+
.@{prefix}-signature-pad {
|
|
4
|
+
display: flex;
|
|
5
|
+
flex-direction: column;
|
|
6
|
+
align-items: center;
|
|
7
|
+
justify-content: center;
|
|
8
|
+
position: relative;
|
|
9
|
+
|
|
10
|
+
&-canvas {
|
|
11
|
+
border: 1px solid #d9d9d9;
|
|
12
|
+
border-radius: 4px;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
&-toolbar {
|
|
16
|
+
margin-top: 8px;
|
|
17
|
+
display: flex;
|
|
18
|
+
justify-content: flex-end;
|
|
19
|
+
gap: 8px;
|
|
20
|
+
text-align: center;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
&-button {
|
|
24
|
+
min-width: 64px;
|
|
25
|
+
height: 32px;
|
|
26
|
+
padding: 4px 16px;
|
|
27
|
+
border: 1px solid #1677ff;
|
|
28
|
+
border-radius: 4px;
|
|
29
|
+
background-color: #fff;
|
|
30
|
+
color: #1677ff;
|
|
31
|
+
cursor: pointer;
|
|
32
|
+
transition: all 0.3s;
|
|
33
|
+
|
|
34
|
+
&:hover {
|
|
35
|
+
background-color: #f0f5ff;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
&:last-child {
|
|
39
|
+
background-color: #1677ff;
|
|
40
|
+
color: #fff;
|
|
41
|
+
|
|
42
|
+
&:hover {
|
|
43
|
+
background-color: #4096ff;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
package/dist/esm/index.d.ts
CHANGED
|
@@ -48,3 +48,7 @@ export { default as IconFont } from './IconFont';
|
|
|
48
48
|
export type { IconFontProps } from './IconFont';
|
|
49
49
|
export { default as AspectRatio } from './AspectRatio';
|
|
50
50
|
export type { AspectRatioProps } from './AspectRatio';
|
|
51
|
+
export { default as SignaturePad } from './SignaturePad';
|
|
52
|
+
export type { SignaturePadProps } from './SignaturePad';
|
|
53
|
+
export { default as ContextMenu } from './ContextMenu';
|
|
54
|
+
export type { ContextMenuProps } from './ContextMenu';
|