tango-ui-cw 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/dist/index.js +30 -0
- package/dist/index.mjs +661 -0
- package/package.json +37 -0
- package/src/component/CSSFab/useTangoStyle.jsx +182 -0
- package/src/component/MaterialButton/MaterialButton.css +64 -0
- package/src/component/MaterialButton/index.jsx +58 -0
- package/src/component/MaterialInput/MaterialInput.css +33 -0
- package/src/component/MaterialInput/index.jsx +29 -0
- package/src/component/TButton/TButton.css +270 -0
- package/src/component/TButton/index.jsx +74 -0
- package/src/component/TColorPicker/TColorPicker.css +24 -0
- package/src/component/TColorPicker/index.jsx +106 -0
- package/src/component/TDate/TDate.css +0 -0
- package/src/component/TDate/index.jsx +148 -0
- package/src/component/TDatePicker/TDatePicker.css +13 -0
- package/src/component/TDatePicker/index.jsx +60 -0
- package/src/component/TDrawer/TDrawer.css +202 -0
- package/src/component/TDrawer/index.jsx +74 -0
- package/src/component/TInput/TInput.css +80 -0
- package/src/component/TInput/index.jsx +102 -0
- package/src/component/TLayout/TLayout.css +88 -0
- package/src/component/TLayout/index.jsx +77 -0
- package/src/component/TLine/TLine.css +54 -0
- package/src/component/TLine/index.jsx +57 -0
- package/src/component/TMark/TMark.css +6 -0
- package/src/component/TMark/index.jsx +78 -0
- package/src/component/TModal/TModal.css +108 -0
- package/src/component/TModal/index.jsx +69 -0
- package/src/component/TNotice/TNotice.css +52 -0
- package/src/component/TNotice/index.jsx +38 -0
- package/src/component/TNotice/useNotice.jsx +54 -0
- package/src/component/TSearch/TSearch.css +90 -0
- package/src/component/TSearch/index.jsx +100 -0
- package/src/component/TSpace/TSpace.css +43 -0
- package/src/component/TSpace/index.jsx +60 -0
- package/src/component/TTable/TTable.css +26 -0
- package/src/component/TTable/index.jsx +77 -0
- package/src/component/TTooltip/TTooltip.css +105 -0
- package/src/component/TTooltip/index.jsx +25 -0
- package/src/component/Tango/store.js +105 -0
- package/src/component/Tools/WaterMark/WaterMark.jsx +78 -0
@@ -0,0 +1,38 @@
|
|
1
|
+
// TNotice.jsx
|
2
|
+
import React, { useEffect, useState } from "react";
|
3
|
+
import PropTypes from "prop-types";
|
4
|
+
import "./TNotice.css"; // 样式文件
|
5
|
+
|
6
|
+
export default function Notice({ type = "success", icon, message }) { // 使用默认参数
|
7
|
+
const [visible, setVisible] = useState(true);
|
8
|
+
const [isFading, setIsFading] = useState(false);
|
9
|
+
|
10
|
+
useEffect(() => {
|
11
|
+
const timer = setTimeout(() => {
|
12
|
+
setIsFading(true);
|
13
|
+
setTimeout(() => {
|
14
|
+
setVisible(false);
|
15
|
+
}, 300);
|
16
|
+
}, 3000);
|
17
|
+
|
18
|
+
return () => clearTimeout(timer);
|
19
|
+
}, []);
|
20
|
+
|
21
|
+
if (!visible) return null;
|
22
|
+
|
23
|
+
const fadeClass = isFading ? "fade-out" : "";
|
24
|
+
|
25
|
+
return (
|
26
|
+
<div className={`notice ${fadeClass}`}>
|
27
|
+
<img src={icon} alt={type} />
|
28
|
+
<span>{message}</span>
|
29
|
+
</div>
|
30
|
+
);
|
31
|
+
};
|
32
|
+
|
33
|
+
Notice.propTypes = {
|
34
|
+
type: PropTypes.oneOf(["success", "fail", "caution"]),
|
35
|
+
icon: PropTypes.string.isRequired,
|
36
|
+
message: PropTypes.string.isRequired,
|
37
|
+
};
|
38
|
+
|
@@ -0,0 +1,54 @@
|
|
1
|
+
import React from "react";
|
2
|
+
import { createRoot } from "react-dom/client"; // 使用 React 18 的 createRoot
|
3
|
+
import ClayNotice from "./index"; // 引入 ClayNotice 组件
|
4
|
+
import successIcon from "../../assets/Notice/成功.png";
|
5
|
+
import failIcon from "../../assets/Notice/失败.png";
|
6
|
+
import cautionIcon from "../../assets/Notice/警告.png";
|
7
|
+
|
8
|
+
// 创建全局通知容器
|
9
|
+
let root; // 用于缓存 React 根实例
|
10
|
+
|
11
|
+
export function useNotice() {
|
12
|
+
// 初始化通知容器
|
13
|
+
const initializeContainer = () => {
|
14
|
+
let container = document.getElementById("notification-container");
|
15
|
+
if (!container) {
|
16
|
+
container = document.createElement("div");
|
17
|
+
container.id = "notification-container";
|
18
|
+
document.body.appendChild(container);
|
19
|
+
}
|
20
|
+
|
21
|
+
if (!root) {
|
22
|
+
root = createRoot(container); // 仅初始化一次 createRoot
|
23
|
+
}
|
24
|
+
|
25
|
+
return container;
|
26
|
+
};
|
27
|
+
|
28
|
+
// 通知生成函数
|
29
|
+
const addNotice = (type, message) => {
|
30
|
+
initializeContainer(); // 确保容器已初始化
|
31
|
+
|
32
|
+
const iconMap = {
|
33
|
+
success: successIcon,
|
34
|
+
fail: failIcon,
|
35
|
+
caution: cautionIcon,
|
36
|
+
};
|
37
|
+
|
38
|
+
const icon = iconMap[type] || successIcon;
|
39
|
+
|
40
|
+
// 创建通知组件
|
41
|
+
const notice = (
|
42
|
+
<ClayNotice key={Date.now()} type={type} icon={icon} message={message} />
|
43
|
+
);
|
44
|
+
|
45
|
+
// 渲染通知
|
46
|
+
root.render(notice);
|
47
|
+
};
|
48
|
+
|
49
|
+
return {
|
50
|
+
success: (message) => addNotice("success", message),
|
51
|
+
fail: (message) => addNotice("fail", message),
|
52
|
+
caution: (message) => addNotice("caution", message),
|
53
|
+
};
|
54
|
+
}
|
@@ -0,0 +1,90 @@
|
|
1
|
+
.search-input {
|
2
|
+
width: 300px;
|
3
|
+
height: 40px;
|
4
|
+
border-radius: 8px;
|
5
|
+
border: 1px solid #ccc;
|
6
|
+
padding: 8px 12px;
|
7
|
+
font-size: 14px;
|
8
|
+
color: black;
|
9
|
+
outline: none;
|
10
|
+
transition: all 0.3s ease;
|
11
|
+
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
12
|
+
background-color: #fff;
|
13
|
+
}
|
14
|
+
|
15
|
+
.search-input:hover {
|
16
|
+
border-color: #888;
|
17
|
+
}
|
18
|
+
|
19
|
+
.search-input:focus {
|
20
|
+
border-color: #4caf50; /* Modern blue accent */
|
21
|
+
box-shadow: 0 0 5px rgba(0, 120, 212, 0.5); /* Subtle glow on focus */
|
22
|
+
}
|
23
|
+
|
24
|
+
.search-input-small {
|
25
|
+
height: 32px;
|
26
|
+
font-size: 12px;
|
27
|
+
padding: 6px 10px;
|
28
|
+
}
|
29
|
+
|
30
|
+
.search-input-medium {
|
31
|
+
height: 40px;
|
32
|
+
font-size: 14px;
|
33
|
+
}
|
34
|
+
|
35
|
+
.search-input-large {
|
36
|
+
height: 48px;
|
37
|
+
font-size: 16px;
|
38
|
+
padding: 10px 14px;
|
39
|
+
}
|
40
|
+
|
41
|
+
.search-input-huge {
|
42
|
+
height: 56px;
|
43
|
+
font-size: 18px;
|
44
|
+
padding: 12px 16px;
|
45
|
+
}
|
46
|
+
|
47
|
+
.search-input-disabled {
|
48
|
+
background-color: #f5f5f5;
|
49
|
+
color: #999;
|
50
|
+
border-color: #ddd;
|
51
|
+
cursor: not-allowed;
|
52
|
+
box-shadow: none;
|
53
|
+
}
|
54
|
+
|
55
|
+
.search-input-error {
|
56
|
+
border-color: #d93025;
|
57
|
+
background-color: #fef2f2;
|
58
|
+
color: #d93025;
|
59
|
+
}
|
60
|
+
|
61
|
+
.search-input-error:focus {
|
62
|
+
box-shadow: 0 0 5px rgba(217, 48, 37, 0.5);
|
63
|
+
}
|
64
|
+
.search-button {
|
65
|
+
position: absolute;
|
66
|
+
right: 10px;
|
67
|
+
top: 50%;
|
68
|
+
transform: translateY(-50%);
|
69
|
+
font-family: Arial, sans-serif;
|
70
|
+
border: none;
|
71
|
+
cursor: pointer;
|
72
|
+
padding: 4px 8px;
|
73
|
+
border-radius: 6px;
|
74
|
+
transition: all 0.3s;
|
75
|
+
-webkit-border-radius: 6px;
|
76
|
+
-moz-border-radius: 6px;
|
77
|
+
-ms-border-radius: 6px;
|
78
|
+
-o-border-radius: 6px;
|
79
|
+
letter-spacing: 2px;
|
80
|
+
background: #4caf50;
|
81
|
+
color: white;
|
82
|
+
}
|
83
|
+
.search-button:hover {
|
84
|
+
transition: 0.3s;
|
85
|
+
-webkit-animation: btn-content 1s;
|
86
|
+
animation: btn-content 1s;
|
87
|
+
outline: 0.1em solid transparent;
|
88
|
+
outline-offset: 0.2em;
|
89
|
+
box-shadow: 0 0 5px #4caf50;
|
90
|
+
}
|
@@ -0,0 +1,100 @@
|
|
1
|
+
import React, { useRef } from "react";
|
2
|
+
import PropTypes from "prop-types";
|
3
|
+
import "./TSearch.css"; // 样式文件
|
4
|
+
|
5
|
+
export default function Search(props) {
|
6
|
+
const {
|
7
|
+
size = "medium",
|
8
|
+
sx = {},
|
9
|
+
style: userStyle = {},
|
10
|
+
className: userClassName = "", // 允许用户自定义 className
|
11
|
+
onSearch,
|
12
|
+
value,
|
13
|
+
defaultValue = "",
|
14
|
+
disabled = false,
|
15
|
+
placeholder = "",
|
16
|
+
path = "",
|
17
|
+
} = props;
|
18
|
+
|
19
|
+
const inputRef = useRef(null); // 用于引用输入框的 DOM 节点
|
20
|
+
|
21
|
+
// 使用类名控制输入框的样式
|
22
|
+
const className = `search-input search-input-${size} ${
|
23
|
+
disabled ? "search-input-disabled" : ""
|
24
|
+
} ${userClassName}`;
|
25
|
+
|
26
|
+
// 合并 sx 和 style,确保 style 优先级更高
|
27
|
+
const combinedStyle = { ...sx, ...userStyle };
|
28
|
+
|
29
|
+
// 判断是否走受控模式
|
30
|
+
const isControlled = value !== undefined && onSearch !== undefined;
|
31
|
+
|
32
|
+
// 定义属性类型
|
33
|
+
Search.propTypes = {
|
34
|
+
size: PropTypes.oneOf(["small", "medium", "large", "huge"]),
|
35
|
+
sx: PropTypes.object, // 自定义样式对象
|
36
|
+
style: PropTypes.object, // 用户传入的 style 属性,保证 style 属性优先级最高
|
37
|
+
onSearch: PropTypes.func.isRequired, // 搜索事件回调
|
38
|
+
className: PropTypes.string, // 添加 className PropTypes
|
39
|
+
value: PropTypes.string, // 输入框的值 (受控模式)
|
40
|
+
defaultValue: PropTypes.string, // 输入框的初始值 (非受控模式)
|
41
|
+
disabled: PropTypes.bool, // 是否禁用
|
42
|
+
placeholder: PropTypes.string, // 占位符
|
43
|
+
path: PropTypes.string, // 占位符
|
44
|
+
};
|
45
|
+
|
46
|
+
// 默认属性
|
47
|
+
Search.defaultProps = {
|
48
|
+
size: "medium",
|
49
|
+
sx: {},
|
50
|
+
style: {},
|
51
|
+
onSearch: () => {},
|
52
|
+
className: "", // 默认值为空字符串
|
53
|
+
value: undefined,
|
54
|
+
defaultValue: "",
|
55
|
+
disabled: false,
|
56
|
+
placeholder: "请输入内容",
|
57
|
+
path: "",
|
58
|
+
};
|
59
|
+
|
60
|
+
return (
|
61
|
+
<div
|
62
|
+
className="search-container"
|
63
|
+
style={{ position: "relative", width: 300 }}
|
64
|
+
>
|
65
|
+
<input
|
66
|
+
className={className}
|
67
|
+
style={combinedStyle}
|
68
|
+
ref={!isControlled ? inputRef : undefined} // 仅在非受控模式下绑定 ref
|
69
|
+
onKeyDown={(e) =>
|
70
|
+
e.key === "Enter" &&
|
71
|
+
onSearch(isControlled ? value : inputRef.current.value)
|
72
|
+
} // 回车触发 onSearch
|
73
|
+
value={isControlled ? value : undefined}
|
74
|
+
defaultValue={!isControlled ? defaultValue : undefined}
|
75
|
+
disabled={disabled}
|
76
|
+
placeholder={placeholder}
|
77
|
+
/>
|
78
|
+
<button
|
79
|
+
className="search-button"
|
80
|
+
onClick={() => onSearch(isControlled ? value : inputRef.current.value)} // 点击按钮触发 onSearch
|
81
|
+
>
|
82
|
+
{path ? (
|
83
|
+
<img
|
84
|
+
src={path}
|
85
|
+
alt="search-logo"
|
86
|
+
style={{
|
87
|
+
width: 16,
|
88
|
+
height: 16,
|
89
|
+
marginTop: 2,
|
90
|
+
marginBottom: -3,
|
91
|
+
}}
|
92
|
+
/>
|
93
|
+
) : (
|
94
|
+
"搜索"
|
95
|
+
)}
|
96
|
+
</button>
|
97
|
+
</div>
|
98
|
+
);
|
99
|
+
};
|
100
|
+
|
@@ -0,0 +1,43 @@
|
|
1
|
+
.space,.space-default{
|
2
|
+
width: auto;
|
3
|
+
height: auto;;
|
4
|
+
}
|
5
|
+
.space-inline{
|
6
|
+
display: inline-block;
|
7
|
+
}
|
8
|
+
.space-fixed{
|
9
|
+
position: fixed
|
10
|
+
}
|
11
|
+
.space-absolute{
|
12
|
+
position: absolute
|
13
|
+
}
|
14
|
+
.space-relative{
|
15
|
+
position: relative
|
16
|
+
}
|
17
|
+
.space-circle{
|
18
|
+
border-radius: 50%;
|
19
|
+
-webkit-border-radius: 50%;
|
20
|
+
-moz-border-radius: 50%;
|
21
|
+
-ms-border-radius: 50%;
|
22
|
+
-o-border-radius: 50%;
|
23
|
+
}
|
24
|
+
/* 三角形盒子 */
|
25
|
+
.space-triangle {
|
26
|
+
width: 0;
|
27
|
+
height: 0;
|
28
|
+
border-left: 50px solid transparent;
|
29
|
+
border-right: 50px solid transparent;
|
30
|
+
border-bottom: 100px solid #4caf50;
|
31
|
+
}
|
32
|
+
|
33
|
+
/* 半圆盒子 */
|
34
|
+
.space-halfCircle {
|
35
|
+
width: 100px;
|
36
|
+
height: 50px;
|
37
|
+
background: #4caf50;
|
38
|
+
border-top-left-radius: 100px;
|
39
|
+
border-top-right-radius: 100px;
|
40
|
+
border-bottom-left-radius: 0;
|
41
|
+
border-bottom-right-radius: 0;
|
42
|
+
}
|
43
|
+
|
@@ -0,0 +1,60 @@
|
|
1
|
+
import React from "react";
|
2
|
+
import PropTypes from "prop-types";
|
3
|
+
import "./TSpace.css"; // 引入样式文件
|
4
|
+
import { useTangoStyle } from "../CSSFab/useTangoStyle"; // 导入 useTStyle 函数
|
5
|
+
|
6
|
+
const Space = React.forwardRef((props, ref) => {
|
7
|
+
const {
|
8
|
+
sx = {},
|
9
|
+
style: userStyle = {},
|
10
|
+
className: userClassName = "",
|
11
|
+
onClick,
|
12
|
+
children,
|
13
|
+
type = "default",
|
14
|
+
} = props;
|
15
|
+
|
16
|
+
const className = `space space-${type} ${userClassName}`;
|
17
|
+
const sxStyle = useTangoStyle(sx); // 用于接收 sx 属性设置的样式
|
18
|
+
const combinedStyle = { ...sxStyle, ...userStyle }; // 合并 sx 和 style
|
19
|
+
|
20
|
+
return (
|
21
|
+
<div
|
22
|
+
ref={ref}
|
23
|
+
style={combinedStyle}
|
24
|
+
onClick={onClick}
|
25
|
+
className={className}
|
26
|
+
>
|
27
|
+
{children}
|
28
|
+
</div>
|
29
|
+
);
|
30
|
+
});
|
31
|
+
|
32
|
+
// 定义 PropTypes
|
33
|
+
Space.propTypes = {
|
34
|
+
type: PropTypes.oneOf([
|
35
|
+
"default",
|
36
|
+
"inline",
|
37
|
+
"circle",
|
38
|
+
"triangle",
|
39
|
+
"halfCircle",
|
40
|
+
"fixed",
|
41
|
+
"absolute",
|
42
|
+
"relative",
|
43
|
+
]),
|
44
|
+
sx: PropTypes.object,
|
45
|
+
style: PropTypes.object,
|
46
|
+
onClick: PropTypes.func,
|
47
|
+
className: PropTypes.string,
|
48
|
+
children: PropTypes.node.isRequired,
|
49
|
+
};
|
50
|
+
|
51
|
+
// 默认属性
|
52
|
+
Space.defaultProps = {
|
53
|
+
type: "default",
|
54
|
+
sx: {},
|
55
|
+
style: {},
|
56
|
+
onClick: () => {},
|
57
|
+
className: "",
|
58
|
+
};
|
59
|
+
|
60
|
+
export { Space }; // 使用命名导出
|
@@ -0,0 +1,26 @@
|
|
1
|
+
.custom-table-wrapper {
|
2
|
+
overflow-x: auto;
|
3
|
+
}
|
4
|
+
|
5
|
+
.custom-table {
|
6
|
+
width: 100%;
|
7
|
+
border-collapse: collapse;
|
8
|
+
font-size: 14px;
|
9
|
+
color: #333;
|
10
|
+
}
|
11
|
+
|
12
|
+
.custom-table thead {
|
13
|
+
background-color: #4caf50;
|
14
|
+
color: white;
|
15
|
+
}
|
16
|
+
|
17
|
+
.custom-table th,
|
18
|
+
.custom-table td {
|
19
|
+
border: 1px solid #ddd;
|
20
|
+
padding: 10px;
|
21
|
+
text-align: left;
|
22
|
+
}
|
23
|
+
|
24
|
+
.custom-table tbody tr:hover {
|
25
|
+
background-color: #f1f1f1;
|
26
|
+
}
|
@@ -0,0 +1,77 @@
|
|
1
|
+
import React from "react";
|
2
|
+
import PropTypes from "prop-types";
|
3
|
+
import { useTangoStyle } from "../CSSFab/useTangoStyle";
|
4
|
+
import "./TTable.css";
|
5
|
+
|
6
|
+
export default function Table({
|
7
|
+
dataSource,
|
8
|
+
columns,
|
9
|
+
sx = {},
|
10
|
+
style = {},
|
11
|
+
className = "",
|
12
|
+
}) {
|
13
|
+
const sxStyle = useTangoStyle(sx);
|
14
|
+
const combinedStyle = { ...sxStyle, ...style };
|
15
|
+
|
16
|
+
return (
|
17
|
+
<div
|
18
|
+
className={`custom-table-wrapper ${className}`}
|
19
|
+
style={{
|
20
|
+
...combinedStyle,
|
21
|
+
borderRadius: combinedStyle.borderRadius || 0,
|
22
|
+
overflow: "hidden",
|
23
|
+
border: "1px solid #ddd",
|
24
|
+
}}
|
25
|
+
>
|
26
|
+
<table className="custom-table">
|
27
|
+
<thead>
|
28
|
+
<tr>
|
29
|
+
{columns?.map((col) => (
|
30
|
+
<th key={col.key || col.dataIndex}>{col.title}</th>
|
31
|
+
))}
|
32
|
+
</tr>
|
33
|
+
</thead>
|
34
|
+
<tbody>
|
35
|
+
{dataSource.map((row) => (
|
36
|
+
<tr key={row.key}>
|
37
|
+
{columns.map((col) => (
|
38
|
+
<td key={col.key || col.dataIndex}>
|
39
|
+
{col.render
|
40
|
+
? col.render(
|
41
|
+
row[col.dataIndex],
|
42
|
+
row,
|
43
|
+
dataSource.indexOf(row)
|
44
|
+
)
|
45
|
+
: row[col.dataIndex]}
|
46
|
+
</td>
|
47
|
+
))}
|
48
|
+
</tr>
|
49
|
+
))}
|
50
|
+
</tbody>
|
51
|
+
</table>
|
52
|
+
</div>
|
53
|
+
);
|
54
|
+
};
|
55
|
+
|
56
|
+
Table.propTypes = {
|
57
|
+
dataSource: PropTypes.array.isRequired,
|
58
|
+
columns: PropTypes.arrayOf(
|
59
|
+
PropTypes.shape({
|
60
|
+
title: PropTypes.string.isRequired,
|
61
|
+
dataIndex: PropTypes.string.isRequired,
|
62
|
+
key: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
|
63
|
+
render: PropTypes.func,
|
64
|
+
})
|
65
|
+
).isRequired,
|
66
|
+
sx: PropTypes.object,
|
67
|
+
style: PropTypes.object,
|
68
|
+
className: PropTypes.string,
|
69
|
+
};
|
70
|
+
|
71
|
+
Table.defaultProps = {
|
72
|
+
dataSource: [],
|
73
|
+
sx: {},
|
74
|
+
style: {},
|
75
|
+
className: "",
|
76
|
+
};
|
77
|
+
|
@@ -0,0 +1,105 @@
|
|
1
|
+
/* Tooltip 容器样式 */
|
2
|
+
.tooltip-wrapper {
|
3
|
+
position: relative; /* 相对定位,便于控制 tooltip 的位置 */
|
4
|
+
display: inline-block; /* 适配内联内容 */
|
5
|
+
}
|
6
|
+
|
7
|
+
/* Tooltip 的默认样式 */
|
8
|
+
.tooltip {
|
9
|
+
position: absolute;
|
10
|
+
bottom: 150%; /* 定位到父元素上方 */
|
11
|
+
left: 50%; /* 水平居中 */
|
12
|
+
transform: translateX(-50%);
|
13
|
+
background: black;
|
14
|
+
color: white;
|
15
|
+
font-size: 16px;
|
16
|
+
padding: 8px 15px;
|
17
|
+
border-radius: 8px;
|
18
|
+
white-space: nowrap;
|
19
|
+
display: none;
|
20
|
+
z-index: 999; /* 确保在顶层 */
|
21
|
+
box-shadow: 0px 4px 6px rgba(0, 0, 0, 0.1); /* 添加阴影效果 */
|
22
|
+
}
|
23
|
+
|
24
|
+
/* 添加 Tooltip 的倒三角 */
|
25
|
+
.tooltip::after {
|
26
|
+
content: ""; /* 空内容用于绘制形状 */
|
27
|
+
position: absolute;
|
28
|
+
top: 100%; /* 定位到 Tooltip 的下方 */
|
29
|
+
left: 50%; /* 水平居中 */
|
30
|
+
transform: translateX(-50%);
|
31
|
+
border-width: 8px; /* 调整三角形大小 */
|
32
|
+
border-style: solid;
|
33
|
+
border-color: black transparent transparent transparent; /* 黑色三角形,透明边框 */
|
34
|
+
}
|
35
|
+
|
36
|
+
/* Hover 状态时显示 Tooltip */
|
37
|
+
.tooltip-wrapper:hover .tooltip {
|
38
|
+
display: block; /* Hover 时显示 */
|
39
|
+
}
|
40
|
+
|
41
|
+
/* Placement 样式 */
|
42
|
+
/* Tooltip 显示在顶部 */
|
43
|
+
.tooltip-wrapper.tooltip-top .tooltip {
|
44
|
+
bottom: 130%; /* 提示框在父元素上方 */
|
45
|
+
left: 47%;
|
46
|
+
transform: translateX(-50%);
|
47
|
+
margin-bottom: 8px; /* 与父元素的间距 */
|
48
|
+
}
|
49
|
+
|
50
|
+
.tooltip-wrapper.tooltip-top .tooltip::after {
|
51
|
+
top: 100%;
|
52
|
+
left: 50%;
|
53
|
+
transform: translateX(-50%);
|
54
|
+
border-color: black transparent transparent transparent;
|
55
|
+
}
|
56
|
+
|
57
|
+
/* Tooltip 显示在底部 */
|
58
|
+
.tooltip-wrapper.tooltip-bottom .tooltip {
|
59
|
+
top: 130%; /* 提示框在父元素下方 */
|
60
|
+
left: 45%;
|
61
|
+
height: 40px;
|
62
|
+
transform: translateX(-50%);
|
63
|
+
margin-top: 8px;
|
64
|
+
}
|
65
|
+
|
66
|
+
.tooltip-wrapper.tooltip-bottom .tooltip::after {
|
67
|
+
bottom: 100%;
|
68
|
+
left: 42%;
|
69
|
+
transform: translateY(-351%);
|
70
|
+
border-color: transparent transparent black transparent;
|
71
|
+
}
|
72
|
+
|
73
|
+
/* Tooltip 显示在左侧,有问题,暂时屏蔽,不提供左侧显示 */
|
74
|
+
/* .tooltip-wrapper.tooltip-left .tooltip {
|
75
|
+
right: 130%;
|
76
|
+
top: 50%;
|
77
|
+
height: 40px;
|
78
|
+
|
79
|
+
transform: translateY(-50%);
|
80
|
+
margin-right: 8px;
|
81
|
+
}
|
82
|
+
|
83
|
+
.tooltip-wrapper.tooltip-left .tooltip::after {
|
84
|
+
right: -8px;
|
85
|
+
top: 50%;
|
86
|
+
transform: translateY(-50%);
|
87
|
+
border-color: transparent transparent transparent black;
|
88
|
+
} */
|
89
|
+
|
90
|
+
/* Tooltip 显示在右侧 */
|
91
|
+
.tooltip-wrapper.tooltip-right .tooltip {
|
92
|
+
left: 110%; /* 提示框在父元素右侧 */
|
93
|
+
top: 50%;
|
94
|
+
height: 40px;
|
95
|
+
|
96
|
+
transform: translateY(-50%);
|
97
|
+
margin-left: 8px;
|
98
|
+
}
|
99
|
+
|
100
|
+
.tooltip-wrapper.tooltip-right .tooltip::after {
|
101
|
+
left: -8px;
|
102
|
+
top: 28%;
|
103
|
+
transform: translateX(-52%);
|
104
|
+
border-color: transparent black transparent transparent;
|
105
|
+
}
|
@@ -0,0 +1,25 @@
|
|
1
|
+
import React from "react";
|
2
|
+
import PropTypes from "prop-types";
|
3
|
+
import "./TTooltip.css"; // 引入样式文件
|
4
|
+
|
5
|
+
export default function Tooltip({ children, tooltipText, placement = "top" }) {
|
6
|
+
const className = `tooltip-wrapper tooltip-${placement}`;
|
7
|
+
|
8
|
+
return (
|
9
|
+
<div className={className}>
|
10
|
+
{children}
|
11
|
+
<div className="tooltip">{tooltipText}</div>
|
12
|
+
</div>
|
13
|
+
);
|
14
|
+
};
|
15
|
+
|
16
|
+
Tooltip.propTypes = {
|
17
|
+
children: PropTypes.node.isRequired, // 包裹的子内容
|
18
|
+
tooltipText: PropTypes.string.isRequired, // Tooltip 显示的文字内容
|
19
|
+
placement: PropTypes.oneOf(["top", "bottom", "right"]), // 支持的方向
|
20
|
+
};
|
21
|
+
|
22
|
+
Tooltip.defaultProps = {
|
23
|
+
placement: "top",
|
24
|
+
};
|
25
|
+
|