@tendaui/components 1.0.0
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/LICENSE +21 -0
- package/README.md +176 -0
- package/alert/Alert.tsx +147 -0
- package/alert/defaultProps.ts +3 -0
- package/alert/index.ts +9 -0
- package/alert/style/css.js +1 -0
- package/alert/style/index.js +1 -0
- package/alert/type.ts +44 -0
- package/badge/Badge.tsx +85 -0
- package/badge/defaultProps.ts +10 -0
- package/badge/index.ts +9 -0
- package/badge/style/css.js +1 -0
- package/badge/style/index.js +1 -0
- package/badge/type.ts +51 -0
- package/button/Button.tsx +95 -0
- package/button/defaultProps.ts +13 -0
- package/button/index.ts +7 -0
- package/button/style/css.js +1 -0
- package/button/style/index.js +1 -0
- package/button/type.ts +82 -0
- package/checkbox/Checkbox.tsx +19 -0
- package/checkbox/CheckboxGroup.tsx +207 -0
- package/checkbox/defaultProps.ts +14 -0
- package/checkbox/index.ts +10 -0
- package/checkbox/style/css.js +1 -0
- package/checkbox/style/index.js +1 -0
- package/checkbox/type.ts +117 -0
- package/common/Check.tsx +131 -0
- package/common/FakeArrow.tsx +36 -0
- package/common/PluginContainer.tsx +21 -0
- package/common/Portal.tsx +67 -0
- package/common.ts +76 -0
- package/config-provider/ConfigContext.tsx +21 -0
- package/config-provider/ConfigProvider.tsx +53 -0
- package/config-provider/index.ts +9 -0
- package/config-provider/type.ts +1062 -0
- package/dialog/Dialog.tsx +254 -0
- package/dialog/DialogCard.tsx +152 -0
- package/dialog/defaultProps.ts +25 -0
- package/dialog/hooks/useDialogDrag.ts +50 -0
- package/dialog/hooks/useDialogEsc.ts +31 -0
- package/dialog/hooks/useDialogPosition.ts +36 -0
- package/dialog/hooks/useLockStyle.ts +54 -0
- package/dialog/index.ts +13 -0
- package/dialog/plugin.tsx +78 -0
- package/dialog/style/css.js +1 -0
- package/dialog/style/index.js +1 -0
- package/dialog/type.ts +241 -0
- package/dialog/utils.ts +4 -0
- package/form/Form.tsx +136 -0
- package/form/FormContext.tsx +64 -0
- package/form/FormItem.tsx +554 -0
- package/form/FormList.tsx +303 -0
- package/form/const.ts +6 -0
- package/form/defaultProps.ts +26 -0
- package/form/formModel.ts +117 -0
- package/form/hooks/interface.ts +20 -0
- package/form/hooks/useForm.ts +122 -0
- package/form/hooks/useFormItemInitialData.ts +95 -0
- package/form/hooks/useFormItemStyle.tsx +122 -0
- package/form/hooks/useInstance.tsx +275 -0
- package/form/hooks/useWatch.ts +42 -0
- package/form/index.ts +11 -0
- package/form/style/css.js +1 -0
- package/form/style/index.js +1 -0
- package/form/type.ts +519 -0
- package/form/utils/index.ts +69 -0
- package/hooks/useAttach.ts +24 -0
- package/hooks/useCommonClassName.ts +45 -0
- package/hooks/useConfig.ts +3 -0
- package/hooks/useControlled.ts +39 -0
- package/hooks/useDefaultProps.ts +16 -0
- package/hooks/useDomCallback.ts +13 -0
- package/hooks/useDomRefCallback.ts +12 -0
- package/hooks/useDragSorter.tsx +151 -0
- package/hooks/useEventCallback.ts +47 -0
- package/hooks/useGlobalConfig.ts +14 -0
- package/hooks/useGlobalIcon.ts +14 -0
- package/hooks/useLastest.ts +13 -0
- package/hooks/useLayoutEffect.ts +7 -0
- package/hooks/useMouseEvent.ts +142 -0
- package/hooks/useMutationObserver.ts +56 -0
- package/hooks/usePopper.ts +189 -0
- package/hooks/useRipple.ts +0 -0
- package/hooks/useSetState.ts +25 -0
- package/hooks/useVirtualScroll.ts +246 -0
- package/hooks/useWindowSize.ts +31 -0
- package/index.ts +70 -0
- package/input/Input.tsx +383 -0
- package/input/InputGroup.tsx +29 -0
- package/input/defaultProps.ts +22 -0
- package/input/index.ts +11 -0
- package/input/style/css.js +1 -0
- package/input/style/index.js +1 -0
- package/input/type.ts +219 -0
- package/loading/Gradient.tsx +36 -0
- package/loading/Loading.tsx +169 -0
- package/loading/circleAdapter.ts +44 -0
- package/loading/defaultProps.ts +12 -0
- package/loading/index.ts +13 -0
- package/loading/style/css.js +1 -0
- package/loading/style/index.js +1 -0
- package/loading/type.ts +71 -0
- package/loading/utils/setStyle.ts +13 -0
- package/myform/index.ts +0 -0
- package/notification/Notify.ts +24 -0
- package/notification/NotifyContainer.tsx +90 -0
- package/notification/NotifyContext.tsx +173 -0
- package/notification/NotifyItem.tsx +121 -0
- package/notification/index.ts +3 -0
- package/notification/style/css.js +1 -0
- package/notification/style/index.js +1 -0
- package/notification/type.ts +23 -0
- package/package.json +52 -0
- package/popup/Popup.tsx +264 -0
- package/popup/defaultProps.ts +13 -0
- package/popup/hooks/useTrigger.ts +276 -0
- package/popup/index.ts +6 -0
- package/popup/style/css.js +1 -0
- package/popup/style/index.js +1 -0
- package/popup/type.ts +130 -0
- package/portal/Portal.tsx +63 -0
- package/portal/index.ts +1 -0
- package/select/Option.tsx +162 -0
- package/select/OptionGroup.tsx +30 -0
- package/select/PopupContent.tsx +271 -0
- package/select/Select.tsx +586 -0
- package/select/defaultProps.ts +27 -0
- package/select/hooks/useOptions.ts +120 -0
- package/select/hooks/usePanelVirtualScroll.ts +111 -0
- package/select/index.ts +9 -0
- package/select/style/css.js +1 -0
- package/select/style/index.js +2 -0
- package/select/type.ts +382 -0
- package/select/utils/helper.ts +256 -0
- package/select-input/SelectInput.tsx +98 -0
- package/select-input/defaultProps.ts +15 -0
- package/select-input/hook/useMultiple.tsx +100 -0
- package/select-input/hook/useOverlayInnerStyle.ts +84 -0
- package/select-input/hook/useSingle.tsx +112 -0
- package/select-input/index.ts +6 -0
- package/select-input/interface.ts +18 -0
- package/select-input/style/css.js +1 -0
- package/select-input/style/index.js +1 -0
- package/select-input/type.ts +280 -0
- package/space/defaultProps.ts +0 -0
- package/space/index.ts +0 -0
- package/space/type.ts +0 -0
- package/style/index.js +2 -0
- package/styles/_global.scss +39 -0
- package/styles/_vars.scss +386 -0
- package/styles/components/alert/_index.scss +175 -0
- package/styles/components/alert/_vars.scss +39 -0
- package/styles/components/badge/_index.scss +70 -0
- package/styles/components/badge/_vars.scss +25 -0
- package/styles/components/button/_index.scss +511 -0
- package/styles/components/button/_mixins.scss +39 -0
- package/styles/components/button/_vars.scss +122 -0
- package/styles/components/checkbox/_index.scss +158 -0
- package/styles/components/checkbox/_mixin.scss +0 -0
- package/styles/components/checkbox/_var.scss +60 -0
- package/styles/components/dialog/_animate.scss +135 -0
- package/styles/components/dialog/_index.scss +311 -0
- package/styles/components/dialog/_mixins.scss +0 -0
- package/styles/components/dialog/_vars.scss +59 -0
- package/styles/components/form/_index.scss +174 -0
- package/styles/components/form/_mixins.scss +76 -0
- package/styles/components/form/_vars.scss +100 -0
- package/styles/components/input/_index.scss +349 -0
- package/styles/components/input/_map.scss +0 -0
- package/styles/components/input/_mixins.scss +116 -0
- package/styles/components/input/_vars.scss +134 -0
- package/styles/components/loading/_index.scss +112 -0
- package/styles/components/loading/_vars.scss +39 -0
- package/styles/components/notification/_index.scss +160 -0
- package/styles/components/notification/_mixins.scss +12 -0
- package/styles/components/notification/_vars.scss +59 -0
- package/styles/components/popup/_index.scss +82 -0
- package/styles/components/popup/_mixin.scss +149 -0
- package/styles/components/popup/_var.scss +31 -0
- package/styles/components/select/_index.scss +290 -0
- package/styles/components/select/_var.scss +65 -0
- package/styles/components/select-input/_index.scss +5 -0
- package/styles/components/select-input/_var.scss +3 -0
- package/styles/components/switch/_index.scss +279 -0
- package/styles/components/switch/_mixins.scss +0 -0
- package/styles/components/switch/_vars.scss +61 -0
- package/styles/components/tag/_index.scss +316 -0
- package/styles/components/tag/_var.scss +85 -0
- package/styles/components/tag-input/_index.scss +163 -0
- package/styles/components/tag-input/_vars.scss +16 -0
- package/styles/globals.css +250 -0
- package/styles/mixins/_focus.scss +7 -0
- package/styles/mixins/_layout.scss +32 -0
- package/styles/mixins/_reset.scss +10 -0
- package/styles/mixins/_scrollbar.scss +31 -0
- package/styles/mixins/_text.scss +48 -0
- package/styles/rillple.css +16 -0
- package/styles/scrollbar.css +42 -0
- package/styles/themes/_dark.scss +191 -0
- package/styles/themes/_font.scss +79 -0
- package/styles/themes/_index.scss +5 -0
- package/styles/themes/_light.scss +190 -0
- package/styles/themes/_radius.scss +9 -0
- package/styles/themes/_size.scss +68 -0
- package/styles/themes.css +66 -0
- package/styles/utilities/_animation.scss +57 -0
- package/styles/utilities/_tips.scss +9 -0
- package/switch/Switch.tsx +120 -0
- package/switch/defaultProps.ts +3 -0
- package/switch/index.ts +7 -0
- package/switch/style/css.js +1 -0
- package/switch/style/index.js +1 -0
- package/switch/type.ts +46 -0
- package/tag/Tag.tsx +149 -0
- package/tag/defaultProps.ts +19 -0
- package/tag/index.ts +8 -0
- package/tag/style/css.js +1 -0
- package/tag/style/index.js +1 -0
- package/tag/type.ts +170 -0
- package/tag-input/TagInput.tsx +215 -0
- package/tag-input/defaultProps.ts +15 -0
- package/tag-input/hooks/useHover.ts +28 -0
- package/tag-input/hooks/useTagList.tsx +131 -0
- package/tag-input/hooks/useTagScroll.ts +105 -0
- package/tag-input/index.ts +9 -0
- package/tag-input/style/css.js +1 -0
- package/tag-input/style/index.js +1 -0
- package/tag-input/type.ts +224 -0
- package/tag-input/useTagList.tsx +131 -0
- package/utils/composeRefs.ts +14 -0
- package/utils/dom.ts +29 -0
- package/utils/forwardRefWithStatics.ts +12 -0
- package/utils/getScrollbarWidth.ts +11 -0
- package/utils/helper.ts +161 -0
- package/utils/isFragment.ts +22 -0
- package/utils/listener.ts +37 -0
- package/utils/noop.ts +3 -0
- package/utils/parentTNode.ts +38 -0
- package/utils/parseTNode.ts +38 -0
- package/utils/react-render.ts +108 -0
- package/utils/ref.ts +6 -0
- package/utils/refs.ts +81 -0
- package/utils/style.ts +60 -0
- package/utils/transition.ts +28 -0
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import React, { useEffect, FC } from "react";
|
|
2
|
+
import classnames from "classnames";
|
|
3
|
+
import circleAdapter from "./circleAdapter";
|
|
4
|
+
import useDomRefCallback from "../hooks/useDomRefCallback";
|
|
5
|
+
import useConfig from "../hooks/useConfig";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Loading组件 渐变部分实现
|
|
9
|
+
*/
|
|
10
|
+
const GradientLoading: FC = () => {
|
|
11
|
+
const { classPrefix } = useConfig();
|
|
12
|
+
const [conicRef, setConicRef] = useDomRefCallback();
|
|
13
|
+
const gradientClass = `${classPrefix}-loading__gradient`;
|
|
14
|
+
|
|
15
|
+
useEffect(() => {
|
|
16
|
+
const el = conicRef;
|
|
17
|
+
circleAdapter(el);
|
|
18
|
+
}, [conicRef]);
|
|
19
|
+
|
|
20
|
+
return (
|
|
21
|
+
<svg
|
|
22
|
+
className={classnames(gradientClass, `${classPrefix}-icon-loading`)}
|
|
23
|
+
viewBox="0 0 12 12"
|
|
24
|
+
version="1.1"
|
|
25
|
+
width="1em"
|
|
26
|
+
height="1em"
|
|
27
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
28
|
+
>
|
|
29
|
+
<foreignObject x="0" y="0" width="12" height="12">
|
|
30
|
+
<div className={`${gradientClass}-conic`} ref={setConicRef} />
|
|
31
|
+
</foreignObject>
|
|
32
|
+
</svg>
|
|
33
|
+
);
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export default GradientLoading;
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
import React, { useState, useEffect, useMemo, CSSProperties } from "react";
|
|
2
|
+
import classnames from "classnames";
|
|
3
|
+
import { canUseDocument } from "../utils/dom";
|
|
4
|
+
import useConfig from "../hooks/useConfig";
|
|
5
|
+
import { StyledProps } from "../common";
|
|
6
|
+
import { TdLoadingProps } from "./type";
|
|
7
|
+
import Portal from "../common/Portal";
|
|
8
|
+
import Gradient from "./Gradient";
|
|
9
|
+
import { loadingDefaultProps } from "./defaultProps";
|
|
10
|
+
import useDefaultProps from "../hooks/useDefaultProps";
|
|
11
|
+
import { addClass, removeClass } from "../utils/style";
|
|
12
|
+
|
|
13
|
+
export interface LoadingProps extends TdLoadingProps, StyledProps {}
|
|
14
|
+
|
|
15
|
+
const Loading: React.FC<LoadingProps> = (props) => {
|
|
16
|
+
const {
|
|
17
|
+
attach,
|
|
18
|
+
indicator,
|
|
19
|
+
text,
|
|
20
|
+
loading,
|
|
21
|
+
size,
|
|
22
|
+
delay,
|
|
23
|
+
fullscreen,
|
|
24
|
+
preventScrollThrough,
|
|
25
|
+
showOverlay,
|
|
26
|
+
content,
|
|
27
|
+
children,
|
|
28
|
+
inheritColor,
|
|
29
|
+
zIndex,
|
|
30
|
+
className,
|
|
31
|
+
style
|
|
32
|
+
} = useDefaultProps<LoadingProps>(props, loadingDefaultProps);
|
|
33
|
+
|
|
34
|
+
const [showLoading, setShowLoading] = useState(() => (delay ? false : loading));
|
|
35
|
+
|
|
36
|
+
const { classPrefix } = useConfig();
|
|
37
|
+
|
|
38
|
+
const name = `${classPrefix}-loading`;
|
|
39
|
+
const centerClass = `${classPrefix}-loading--center`;
|
|
40
|
+
const inheritColorClass = `${classPrefix}-loading--inherit-color`;
|
|
41
|
+
const fullClass = `${classPrefix}-loading--full`;
|
|
42
|
+
const fullscreenClass = `${classPrefix}-loading__fullscreen`;
|
|
43
|
+
const lockClass = `${classPrefix}-loading--lock`;
|
|
44
|
+
const overlayClass = `${classPrefix}-loading__overlay`;
|
|
45
|
+
const relativeClass = `${classPrefix}-loading__parent`;
|
|
46
|
+
const textClass = `${classPrefix}-loading__text`;
|
|
47
|
+
|
|
48
|
+
useEffect(() => {
|
|
49
|
+
let timer: NodeJS.Timeout;
|
|
50
|
+
|
|
51
|
+
if (delay && loading) {
|
|
52
|
+
timer = setTimeout(() => {
|
|
53
|
+
setShowLoading(loading);
|
|
54
|
+
}, delay);
|
|
55
|
+
} else {
|
|
56
|
+
// Use setTimeout to avoid calling setState synchronously within an effect
|
|
57
|
+
setTimeout(() => {
|
|
58
|
+
setShowLoading(loading);
|
|
59
|
+
}, 0);
|
|
60
|
+
}
|
|
61
|
+
return () => {
|
|
62
|
+
clearTimeout(timer);
|
|
63
|
+
};
|
|
64
|
+
}, [delay, loading]);
|
|
65
|
+
|
|
66
|
+
const calcStyles = useMemo<React.CSSProperties>(() => {
|
|
67
|
+
const styles: CSSProperties = {};
|
|
68
|
+
|
|
69
|
+
if (zIndex !== undefined) {
|
|
70
|
+
styles.zIndex = zIndex;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (!["small", "medium", "large"].includes(size)) {
|
|
74
|
+
styles.fontSize = size;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return styles;
|
|
78
|
+
}, [size, zIndex]);
|
|
79
|
+
|
|
80
|
+
const sizeMap = {
|
|
81
|
+
large: `${classPrefix}-size-l`,
|
|
82
|
+
small: `${classPrefix}-size-s`,
|
|
83
|
+
medium: `${classPrefix}-size-m`
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
const baseClasses = classnames(
|
|
87
|
+
centerClass,
|
|
88
|
+
sizeMap[size],
|
|
89
|
+
{
|
|
90
|
+
[inheritColorClass]: inheritColor
|
|
91
|
+
},
|
|
92
|
+
className
|
|
93
|
+
);
|
|
94
|
+
|
|
95
|
+
useEffect(() => {
|
|
96
|
+
if (preventScrollThrough && fullscreen && canUseDocument && loading) {
|
|
97
|
+
addClass(document.body, lockClass);
|
|
98
|
+
}
|
|
99
|
+
return () => {
|
|
100
|
+
removeClass(document.body, lockClass);
|
|
101
|
+
};
|
|
102
|
+
}, [loading, preventScrollThrough, fullscreen, lockClass]);
|
|
103
|
+
|
|
104
|
+
const commonContent = () => {
|
|
105
|
+
let renderIndicator = <Gradient />;
|
|
106
|
+
|
|
107
|
+
if (indicator && typeof indicator !== "boolean") {
|
|
108
|
+
renderIndicator = indicator as React.ReactElement;
|
|
109
|
+
}
|
|
110
|
+
return (
|
|
111
|
+
<>
|
|
112
|
+
{indicator ? renderIndicator : null}
|
|
113
|
+
{text ? <div className={textClass}>{text}</div> : null}
|
|
114
|
+
</>
|
|
115
|
+
);
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
if (fullscreen) {
|
|
119
|
+
return loading ? (
|
|
120
|
+
<div className={classnames(name, fullscreenClass, centerClass, overlayClass)} style={{ ...calcStyles, ...style }}>
|
|
121
|
+
<div className={baseClasses}>{commonContent()}</div>
|
|
122
|
+
</div>
|
|
123
|
+
) : null;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if (content || children) {
|
|
127
|
+
return (
|
|
128
|
+
<div className={relativeClass} style={style}>
|
|
129
|
+
{content || children}
|
|
130
|
+
{showLoading ? (
|
|
131
|
+
<div
|
|
132
|
+
className={classnames(name, baseClasses, fullClass, {
|
|
133
|
+
[overlayClass]: showOverlay
|
|
134
|
+
})}
|
|
135
|
+
style={calcStyles}
|
|
136
|
+
>
|
|
137
|
+
{commonContent()}
|
|
138
|
+
</div>
|
|
139
|
+
) : null}
|
|
140
|
+
</div>
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
if (attach) {
|
|
144
|
+
return (
|
|
145
|
+
<Portal attach={attach}>
|
|
146
|
+
{loading ? (
|
|
147
|
+
<div
|
|
148
|
+
className={classnames(name, baseClasses, fullClass, {
|
|
149
|
+
[overlayClass]: showOverlay
|
|
150
|
+
})}
|
|
151
|
+
style={{ ...calcStyles, ...style }}
|
|
152
|
+
>
|
|
153
|
+
{commonContent()}
|
|
154
|
+
</div>
|
|
155
|
+
) : null}
|
|
156
|
+
</Portal>
|
|
157
|
+
);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
return loading ? (
|
|
161
|
+
<div className={classnames(name, baseClasses)} style={{ ...calcStyles, ...style }}>
|
|
162
|
+
{commonContent()}
|
|
163
|
+
</div>
|
|
164
|
+
) : null;
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
Loading.displayName = "Loading";
|
|
168
|
+
|
|
169
|
+
export default Loading;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import setStyle from "./utils/setStyle";
|
|
2
|
+
import { getIEVersion } from "../utils/helper";
|
|
3
|
+
|
|
4
|
+
export default function circleAdapter(circleElem: HTMLElement) {
|
|
5
|
+
let basicStyle = {};
|
|
6
|
+
|
|
7
|
+
if (!circleElem || typeof window === "undefined") {
|
|
8
|
+
return;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const computedStyle = window?.getComputedStyle?.(circleElem);
|
|
12
|
+
const { color, fontSize } = computedStyle || {};
|
|
13
|
+
|
|
14
|
+
// to fix the browser compat of foreignObject in Safari,
|
|
15
|
+
// https://bugs.webkit.org/show_bug.cgi?id=23113
|
|
16
|
+
const ua = window?.navigator?.userAgent || "";
|
|
17
|
+
const isSafari = /Safari/.test(ua) && !/Chrome/.test(ua);
|
|
18
|
+
// 判断是否为 iOS 下的微信和企业微信
|
|
19
|
+
const isIosWechat = /(?=.*iPhone)[?=.*MicroMessenger]/.test(ua) && !/Chrome/.test(ua);
|
|
20
|
+
// 判断是否为 iPadOS 下的微信和企业微信
|
|
21
|
+
const isIpadWechat = /(?=.*iPad)[?=.*MicroMessenger]/.test(ua) && !/Chrome/.test(ua);
|
|
22
|
+
|
|
23
|
+
// 注意:chrome上调试mobile/ipad端时,loading出现异常,属于正常现象,不需要修改。
|
|
24
|
+
if (isSafari || isIosWechat || isIpadWechat) {
|
|
25
|
+
basicStyle = {
|
|
26
|
+
transformOrigin: "0px 0px",
|
|
27
|
+
transform: `scale(${parseInt(fontSize, 10) / 12})`
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
// 添加:判断是否为IE浏览器
|
|
31
|
+
if (color && getIEVersion() > 11) {
|
|
32
|
+
const matched = color.match(/[\d.]+/g);
|
|
33
|
+
const endColor = matched ? `rgba(${matched[0]}, ${matched[1]}, ${matched[2]}, 0)` : "";
|
|
34
|
+
setStyle(circleElem, {
|
|
35
|
+
...basicStyle,
|
|
36
|
+
background: `conic-gradient(from 90deg at 50% 50%,${endColor} 0deg, ${color} 360deg)`
|
|
37
|
+
});
|
|
38
|
+
} else {
|
|
39
|
+
setStyle(circleElem, {
|
|
40
|
+
...basicStyle,
|
|
41
|
+
background: ""
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { TdLoadingProps } from "./type";
|
|
2
|
+
|
|
3
|
+
export const loadingDefaultProps: TdLoadingProps = {
|
|
4
|
+
delay: 0,
|
|
5
|
+
fullscreen: false,
|
|
6
|
+
indicator: true,
|
|
7
|
+
inheritColor: false,
|
|
8
|
+
loading: true,
|
|
9
|
+
preventScrollThrough: true,
|
|
10
|
+
showOverlay: true,
|
|
11
|
+
size: "medium"
|
|
12
|
+
};
|
package/loading/index.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import _Loading from "./Loading";
|
|
2
|
+
// import { LoadingPlugin as _LoadingPlugin } from './plugin';
|
|
3
|
+
|
|
4
|
+
import "./style/index.js";
|
|
5
|
+
|
|
6
|
+
export type { LoadingProps } from "./Loading";
|
|
7
|
+
export * from "./type";
|
|
8
|
+
|
|
9
|
+
export const Loading = _Loading;
|
|
10
|
+
// export const loading = _LoadingPlugin;
|
|
11
|
+
// export const LoadingPlugin = _LoadingPlugin;
|
|
12
|
+
|
|
13
|
+
export default Loading;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import "./index.css";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import "../../styles/components/loading/_index.scss";
|
package/loading/type.ts
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { TNode, AttachNode } from "../common";
|
|
2
|
+
|
|
3
|
+
export interface TdLoadingProps {
|
|
4
|
+
/**
|
|
5
|
+
* 挂载元素,默认挂载到组件本身所在的位置。数据类型为 String 时,会被当作选择器处理,进行节点查询。示例:'body' 或 () => document.body
|
|
6
|
+
* @default ''
|
|
7
|
+
*/
|
|
8
|
+
attach?: AttachNode;
|
|
9
|
+
/**
|
|
10
|
+
* 子元素,同 content
|
|
11
|
+
*/
|
|
12
|
+
children?: TNode;
|
|
13
|
+
/**
|
|
14
|
+
* 子元素
|
|
15
|
+
*/
|
|
16
|
+
content?: TNode;
|
|
17
|
+
/**
|
|
18
|
+
* 延迟显示加载效果的时间,用于防止请求速度过快引起的加载闪烁,单位:毫秒
|
|
19
|
+
* @default 0
|
|
20
|
+
*/
|
|
21
|
+
delay?: number;
|
|
22
|
+
/**
|
|
23
|
+
* 是否显示为全屏加载
|
|
24
|
+
* @default false
|
|
25
|
+
*/
|
|
26
|
+
fullscreen?: boolean;
|
|
27
|
+
/**
|
|
28
|
+
* 加载指示符,值为 true 显示默认指示符,值为 false 则不显示,也可以自定义指示符
|
|
29
|
+
* @default true
|
|
30
|
+
*/
|
|
31
|
+
indicator?: TNode;
|
|
32
|
+
/**
|
|
33
|
+
* 是否继承父元素颜色
|
|
34
|
+
* @default false
|
|
35
|
+
*/
|
|
36
|
+
inheritColor?: boolean;
|
|
37
|
+
/**
|
|
38
|
+
* 是否处于加载状态
|
|
39
|
+
* @default true
|
|
40
|
+
*/
|
|
41
|
+
loading?: boolean;
|
|
42
|
+
/**
|
|
43
|
+
* 防止滚动穿透,全屏加载模式有效
|
|
44
|
+
* @default true
|
|
45
|
+
*/
|
|
46
|
+
preventScrollThrough?: boolean;
|
|
47
|
+
/**
|
|
48
|
+
* 是否需要遮罩层,遮罩层对包裹元素才有效
|
|
49
|
+
* @default true
|
|
50
|
+
*/
|
|
51
|
+
showOverlay?: boolean;
|
|
52
|
+
/**
|
|
53
|
+
* 尺寸,示例:small/medium/large/12px/56px/0.3em
|
|
54
|
+
* @default medium
|
|
55
|
+
*/
|
|
56
|
+
size?: string;
|
|
57
|
+
/**
|
|
58
|
+
* 加载提示文案
|
|
59
|
+
*/
|
|
60
|
+
text?: TNode;
|
|
61
|
+
/**
|
|
62
|
+
* 消息通知层级,样式默认为 3500
|
|
63
|
+
*/
|
|
64
|
+
zIndex?: number;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export interface LoadingInstance {
|
|
68
|
+
hide: () => void;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export type LoadingMethod = (options: boolean | TdLoadingProps) => LoadingInstance;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 用于为节点增加styles
|
|
3
|
+
* @param el HTMLElement
|
|
4
|
+
* @param style Styles
|
|
5
|
+
*/
|
|
6
|
+
function setStyle(el: HTMLElement, styles): void {
|
|
7
|
+
const keys = Object.keys(styles);
|
|
8
|
+
keys.forEach((key) => {
|
|
9
|
+
el.style[key] = styles[key];
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export default setStyle;
|
package/myform/index.ts
ADDED
|
File without changes
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
// notification.ts
|
|
2
|
+
type NotificationType = "default" | "success" | "error" | "info" | "warning";
|
|
3
|
+
type NotificationMessage = {
|
|
4
|
+
id: string;
|
|
5
|
+
type: NotificationType;
|
|
6
|
+
message: string;
|
|
7
|
+
title: string;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
let addNotification: (notification: NotificationMessage) => void = () => {};
|
|
11
|
+
|
|
12
|
+
export function notification(message: string, type: NotificationType = "default", title: string) {
|
|
13
|
+
const id = new Date().getTime().toString();
|
|
14
|
+
addNotification({
|
|
15
|
+
id,
|
|
16
|
+
message,
|
|
17
|
+
type,
|
|
18
|
+
title
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function registerNotificationHandler(cb: typeof addNotification) {
|
|
23
|
+
addNotification = cb;
|
|
24
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import React, { useCallback, useState } from "react";
|
|
2
|
+
import { TdNotificationProps } from "./type";
|
|
3
|
+
import NotificationItem from "./NotifyItem";
|
|
4
|
+
import useConfig from "../hooks/useConfig";
|
|
5
|
+
export const GAP = 14;
|
|
6
|
+
export const TOAST_WIDTH = 356;
|
|
7
|
+
const NotificationContainer = ({
|
|
8
|
+
notifications,
|
|
9
|
+
onRemove,
|
|
10
|
+
onHoverStart,
|
|
11
|
+
onHoverEnd,
|
|
12
|
+
maxStack,
|
|
13
|
+
position
|
|
14
|
+
}: {
|
|
15
|
+
notifications: TdNotificationProps[];
|
|
16
|
+
onRemove: (id: string) => void;
|
|
17
|
+
onHoverStart: () => void;
|
|
18
|
+
onHoverEnd: () => void;
|
|
19
|
+
maxStack: number;
|
|
20
|
+
position: string;
|
|
21
|
+
}) => {
|
|
22
|
+
const [isHovering, setIsHovering] = useState(false);
|
|
23
|
+
const latestNotifications = notifications.slice(0, maxStack);
|
|
24
|
+
const [y, x] = position.split("-");
|
|
25
|
+
const handleMouseEnter = useCallback(() => {
|
|
26
|
+
setIsHovering(true);
|
|
27
|
+
onHoverStart();
|
|
28
|
+
}, [onHoverStart]);
|
|
29
|
+
|
|
30
|
+
const handleMouseLeave = useCallback(() => {
|
|
31
|
+
setIsHovering(false);
|
|
32
|
+
onHoverEnd();
|
|
33
|
+
}, [onHoverEnd]);
|
|
34
|
+
|
|
35
|
+
const [heights, setHeights] = useState<{ toastId: string; height: number }[]>([]);
|
|
36
|
+
|
|
37
|
+
const { classPrefix: prefix } = useConfig();
|
|
38
|
+
|
|
39
|
+
return (
|
|
40
|
+
<div
|
|
41
|
+
className={`${prefix}-notify`}
|
|
42
|
+
style={
|
|
43
|
+
{
|
|
44
|
+
height: isHovering ? `${notifications.length * 100 + 16}px` : "auto",
|
|
45
|
+
minHeight: "80px",
|
|
46
|
+
"--front-toast-height": (heights[0]?.height || 0) + "px",
|
|
47
|
+
"--toast-width": TOAST_WIDTH + "px"
|
|
48
|
+
} as React.CSSProperties
|
|
49
|
+
}
|
|
50
|
+
data-toaster
|
|
51
|
+
onMouseEnter={handleMouseEnter}
|
|
52
|
+
onMouseLeave={handleMouseLeave}
|
|
53
|
+
data-x-position={x}
|
|
54
|
+
data-y-position={y}
|
|
55
|
+
>
|
|
56
|
+
<div
|
|
57
|
+
className={`${prefix}-notify__container`}
|
|
58
|
+
style={{
|
|
59
|
+
pointerEvents: "all"
|
|
60
|
+
}}
|
|
61
|
+
>
|
|
62
|
+
{latestNotifications.map((notification) => {
|
|
63
|
+
// const stackedStyle = !isHovering && !isLast;
|
|
64
|
+
|
|
65
|
+
// let offsetY = isHovering
|
|
66
|
+
// ? index * 100 // 展开时,索引0在顶部
|
|
67
|
+
// : stackedStyle
|
|
68
|
+
// ? index * 8
|
|
69
|
+
// : 0;
|
|
70
|
+
|
|
71
|
+
// const scale = stackedStyle ? 1 - index * 0.01 : 1;
|
|
72
|
+
// const opacity = stackedStyle ? 1 - index * 0.15 : 1;
|
|
73
|
+
return (
|
|
74
|
+
<NotificationItem
|
|
75
|
+
key={notification.id}
|
|
76
|
+
heights={heights}
|
|
77
|
+
setHeights={setHeights}
|
|
78
|
+
gap={GAP}
|
|
79
|
+
{...notification}
|
|
80
|
+
onRemove={onRemove}
|
|
81
|
+
isExpanded={isHovering}
|
|
82
|
+
/>
|
|
83
|
+
);
|
|
84
|
+
})}
|
|
85
|
+
</div>
|
|
86
|
+
</div>
|
|
87
|
+
);
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
export default NotificationContainer;
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import React, { createContext, useState, useRef, useCallback, useContext } from "react";
|
|
2
|
+
import { createPortal } from "react-dom";
|
|
3
|
+
import NotifyContainer from "./NotifyContainer";
|
|
4
|
+
|
|
5
|
+
// 1. 定义类型
|
|
6
|
+
type NotificationType = "success" | "error" | "warning" | "info";
|
|
7
|
+
|
|
8
|
+
interface Notification {
|
|
9
|
+
id: string;
|
|
10
|
+
type: NotificationType;
|
|
11
|
+
message: string;
|
|
12
|
+
createdAt: number;
|
|
13
|
+
isRemoved: boolean;
|
|
14
|
+
title: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// 2. 定义 Context 类型
|
|
18
|
+
interface NotificationContextType {
|
|
19
|
+
notify: (type: NotificationType, message: Message) => void;
|
|
20
|
+
success: (message: Message) => void;
|
|
21
|
+
error: (message: Message) => void;
|
|
22
|
+
warning: (message: Message) => void;
|
|
23
|
+
info: (message: Message) => void;
|
|
24
|
+
removeNotification: (id: string) => void;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// 3. 创建 Context
|
|
28
|
+
const NotificationContext = createContext<NotificationContextType | null>(null);
|
|
29
|
+
|
|
30
|
+
// 4. 生成唯一 ID 的辅助函数
|
|
31
|
+
const generateId = () => Math.random().toString(36).substr(2, 9);
|
|
32
|
+
|
|
33
|
+
// 5. Provider 组件
|
|
34
|
+
interface NotificationProviderProps {
|
|
35
|
+
children: React.ReactNode;
|
|
36
|
+
maxStack?: number;
|
|
37
|
+
displayDuration?: number;
|
|
38
|
+
position?: string;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
type Message = {
|
|
42
|
+
title: string;
|
|
43
|
+
message: string;
|
|
44
|
+
};
|
|
45
|
+
export const NotificationProvider: React.FC<NotificationProviderProps> = ({
|
|
46
|
+
children,
|
|
47
|
+
maxStack = 5,
|
|
48
|
+
displayDuration = 3000,
|
|
49
|
+
position = "top-right"
|
|
50
|
+
}) => {
|
|
51
|
+
const [notifications, setNotifications] = useState<Notification[]>([]);
|
|
52
|
+
const timersRef = useRef<Map<string, NodeJS.Timeout>>(new Map());
|
|
53
|
+
const pausedAtRef = useRef<Map<string, number>>(new Map());
|
|
54
|
+
|
|
55
|
+
// 定时器相关函数
|
|
56
|
+
const clearNotificationTimer = useCallback((id: string) => {
|
|
57
|
+
const timer = timersRef.current.get(id);
|
|
58
|
+
if (timer) {
|
|
59
|
+
clearTimeout(timer);
|
|
60
|
+
timersRef.current.delete(id);
|
|
61
|
+
}
|
|
62
|
+
}, []);
|
|
63
|
+
|
|
64
|
+
const startTimer = useCallback(
|
|
65
|
+
(notification: Notification, remainingTime?: number) => {
|
|
66
|
+
const duration = remainingTime ?? displayDuration;
|
|
67
|
+
const timer = setTimeout(() => {
|
|
68
|
+
setNotifications((prev) => prev.map((n) => (n.id === notification.id ? { ...n, isRemoved: true } : n)));
|
|
69
|
+
setTimeout(() => {
|
|
70
|
+
setNotifications((prev) => prev.filter((t) => t.id !== notification.id));
|
|
71
|
+
clearNotificationTimer(notification.id);
|
|
72
|
+
pausedAtRef.current.delete(notification.id);
|
|
73
|
+
}, 400);
|
|
74
|
+
}, duration);
|
|
75
|
+
timersRef.current.set(notification.id, timer);
|
|
76
|
+
},
|
|
77
|
+
[displayDuration, clearNotificationTimer]
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
// 6. 核心通知函数
|
|
81
|
+
const addNotification = useCallback(
|
|
82
|
+
(type: NotificationType, message: Message) => {
|
|
83
|
+
const newNotification: Notification = {
|
|
84
|
+
id: generateId(),
|
|
85
|
+
type,
|
|
86
|
+
title: message.title,
|
|
87
|
+
message: message.message,
|
|
88
|
+
createdAt: Date.now(),
|
|
89
|
+
isRemoved: false
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
setNotifications((prev) => {
|
|
93
|
+
const newNotifications = [newNotification, ...prev];
|
|
94
|
+
const removedNotifications = newNotifications.slice(maxStack);
|
|
95
|
+
|
|
96
|
+
removedNotifications.forEach((notification) => {
|
|
97
|
+
clearNotificationTimer(notification.id);
|
|
98
|
+
pausedAtRef.current.delete(notification.id);
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
return newNotifications.slice(0, maxStack);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
startTimer(newNotification);
|
|
105
|
+
},
|
|
106
|
+
[maxStack, startTimer, clearNotificationTimer]
|
|
107
|
+
);
|
|
108
|
+
|
|
109
|
+
// 7. 提供的 Context 值
|
|
110
|
+
const contextValue = React.useMemo(
|
|
111
|
+
() => ({
|
|
112
|
+
notify: addNotification,
|
|
113
|
+
success: (message: Message) => addNotification("success", message),
|
|
114
|
+
error: (message: Message) => addNotification("error", message),
|
|
115
|
+
warning: (message: Message) => addNotification("warning", message),
|
|
116
|
+
info: (message: Message) => addNotification("info", message),
|
|
117
|
+
removeNotification: (id: string) => {
|
|
118
|
+
setNotifications((prev) => prev.filter((t) => t.id !== id));
|
|
119
|
+
clearNotificationTimer(id);
|
|
120
|
+
pausedAtRef.current.delete(id);
|
|
121
|
+
}
|
|
122
|
+
}),
|
|
123
|
+
[addNotification, clearNotificationTimer]
|
|
124
|
+
);
|
|
125
|
+
|
|
126
|
+
// 悬停处理
|
|
127
|
+
const clearAllTimers = useCallback(() => {
|
|
128
|
+
const now = Date.now();
|
|
129
|
+
notifications.forEach((notification) => {
|
|
130
|
+
pausedAtRef.current.set(notification.id, now);
|
|
131
|
+
clearNotificationTimer(notification.id);
|
|
132
|
+
});
|
|
133
|
+
}, [notifications, clearNotificationTimer]);
|
|
134
|
+
|
|
135
|
+
const restartAllTimers = useCallback(() => {
|
|
136
|
+
notifications.forEach((notification) => {
|
|
137
|
+
const pausedAt = pausedAtRef.current.get(notification.id);
|
|
138
|
+
if (pausedAt) {
|
|
139
|
+
const elapsedTime = pausedAt - notification.createdAt;
|
|
140
|
+
const remainingTime = Math.max(0, displayDuration - elapsedTime);
|
|
141
|
+
startTimer(notification, remainingTime);
|
|
142
|
+
pausedAtRef.current.delete(notification.id);
|
|
143
|
+
}
|
|
144
|
+
});
|
|
145
|
+
}, [notifications, displayDuration, startTimer]);
|
|
146
|
+
|
|
147
|
+
return (
|
|
148
|
+
<NotificationContext.Provider value={contextValue}>
|
|
149
|
+
{children}
|
|
150
|
+
{createPortal(
|
|
151
|
+
<NotifyContainer
|
|
152
|
+
notifications={notifications as unknown as TdNotificationProps[]}
|
|
153
|
+
onRemove={(id) => contextValue.removeNotification(id)}
|
|
154
|
+
onHoverStart={clearAllTimers}
|
|
155
|
+
onHoverEnd={restartAllTimers}
|
|
156
|
+
data-testid="notification-container"
|
|
157
|
+
maxStack={maxStack}
|
|
158
|
+
position={position}
|
|
159
|
+
/>,
|
|
160
|
+
document.body
|
|
161
|
+
)}
|
|
162
|
+
</NotificationContext.Provider>
|
|
163
|
+
);
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
// 8. 创建自定义 Hook
|
|
167
|
+
export const useNotification = () => {
|
|
168
|
+
const context = useContext(NotificationContext);
|
|
169
|
+
if (!context) {
|
|
170
|
+
throw new Error("useNotification must be used within a NotificationProvider");
|
|
171
|
+
}
|
|
172
|
+
return context;
|
|
173
|
+
};
|