@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.
Files changed (245) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +176 -0
  3. package/alert/Alert.tsx +147 -0
  4. package/alert/defaultProps.ts +3 -0
  5. package/alert/index.ts +9 -0
  6. package/alert/style/css.js +1 -0
  7. package/alert/style/index.js +1 -0
  8. package/alert/type.ts +44 -0
  9. package/badge/Badge.tsx +85 -0
  10. package/badge/defaultProps.ts +10 -0
  11. package/badge/index.ts +9 -0
  12. package/badge/style/css.js +1 -0
  13. package/badge/style/index.js +1 -0
  14. package/badge/type.ts +51 -0
  15. package/button/Button.tsx +95 -0
  16. package/button/defaultProps.ts +13 -0
  17. package/button/index.ts +7 -0
  18. package/button/style/css.js +1 -0
  19. package/button/style/index.js +1 -0
  20. package/button/type.ts +82 -0
  21. package/checkbox/Checkbox.tsx +19 -0
  22. package/checkbox/CheckboxGroup.tsx +207 -0
  23. package/checkbox/defaultProps.ts +14 -0
  24. package/checkbox/index.ts +10 -0
  25. package/checkbox/style/css.js +1 -0
  26. package/checkbox/style/index.js +1 -0
  27. package/checkbox/type.ts +117 -0
  28. package/common/Check.tsx +131 -0
  29. package/common/FakeArrow.tsx +36 -0
  30. package/common/PluginContainer.tsx +21 -0
  31. package/common/Portal.tsx +67 -0
  32. package/common.ts +76 -0
  33. package/config-provider/ConfigContext.tsx +21 -0
  34. package/config-provider/ConfigProvider.tsx +53 -0
  35. package/config-provider/index.ts +9 -0
  36. package/config-provider/type.ts +1062 -0
  37. package/dialog/Dialog.tsx +254 -0
  38. package/dialog/DialogCard.tsx +152 -0
  39. package/dialog/defaultProps.ts +25 -0
  40. package/dialog/hooks/useDialogDrag.ts +50 -0
  41. package/dialog/hooks/useDialogEsc.ts +31 -0
  42. package/dialog/hooks/useDialogPosition.ts +36 -0
  43. package/dialog/hooks/useLockStyle.ts +54 -0
  44. package/dialog/index.ts +13 -0
  45. package/dialog/plugin.tsx +78 -0
  46. package/dialog/style/css.js +1 -0
  47. package/dialog/style/index.js +1 -0
  48. package/dialog/type.ts +241 -0
  49. package/dialog/utils.ts +4 -0
  50. package/form/Form.tsx +136 -0
  51. package/form/FormContext.tsx +64 -0
  52. package/form/FormItem.tsx +554 -0
  53. package/form/FormList.tsx +303 -0
  54. package/form/const.ts +6 -0
  55. package/form/defaultProps.ts +26 -0
  56. package/form/formModel.ts +117 -0
  57. package/form/hooks/interface.ts +20 -0
  58. package/form/hooks/useForm.ts +122 -0
  59. package/form/hooks/useFormItemInitialData.ts +95 -0
  60. package/form/hooks/useFormItemStyle.tsx +122 -0
  61. package/form/hooks/useInstance.tsx +275 -0
  62. package/form/hooks/useWatch.ts +42 -0
  63. package/form/index.ts +11 -0
  64. package/form/style/css.js +1 -0
  65. package/form/style/index.js +1 -0
  66. package/form/type.ts +519 -0
  67. package/form/utils/index.ts +69 -0
  68. package/hooks/useAttach.ts +24 -0
  69. package/hooks/useCommonClassName.ts +45 -0
  70. package/hooks/useConfig.ts +3 -0
  71. package/hooks/useControlled.ts +39 -0
  72. package/hooks/useDefaultProps.ts +16 -0
  73. package/hooks/useDomCallback.ts +13 -0
  74. package/hooks/useDomRefCallback.ts +12 -0
  75. package/hooks/useDragSorter.tsx +151 -0
  76. package/hooks/useEventCallback.ts +47 -0
  77. package/hooks/useGlobalConfig.ts +14 -0
  78. package/hooks/useGlobalIcon.ts +14 -0
  79. package/hooks/useLastest.ts +13 -0
  80. package/hooks/useLayoutEffect.ts +7 -0
  81. package/hooks/useMouseEvent.ts +142 -0
  82. package/hooks/useMutationObserver.ts +56 -0
  83. package/hooks/usePopper.ts +189 -0
  84. package/hooks/useRipple.ts +0 -0
  85. package/hooks/useSetState.ts +25 -0
  86. package/hooks/useVirtualScroll.ts +246 -0
  87. package/hooks/useWindowSize.ts +31 -0
  88. package/index.ts +70 -0
  89. package/input/Input.tsx +383 -0
  90. package/input/InputGroup.tsx +29 -0
  91. package/input/defaultProps.ts +22 -0
  92. package/input/index.ts +11 -0
  93. package/input/style/css.js +1 -0
  94. package/input/style/index.js +1 -0
  95. package/input/type.ts +219 -0
  96. package/loading/Gradient.tsx +36 -0
  97. package/loading/Loading.tsx +169 -0
  98. package/loading/circleAdapter.ts +44 -0
  99. package/loading/defaultProps.ts +12 -0
  100. package/loading/index.ts +13 -0
  101. package/loading/style/css.js +1 -0
  102. package/loading/style/index.js +1 -0
  103. package/loading/type.ts +71 -0
  104. package/loading/utils/setStyle.ts +13 -0
  105. package/myform/index.ts +0 -0
  106. package/notification/Notify.ts +24 -0
  107. package/notification/NotifyContainer.tsx +90 -0
  108. package/notification/NotifyContext.tsx +173 -0
  109. package/notification/NotifyItem.tsx +121 -0
  110. package/notification/index.ts +3 -0
  111. package/notification/style/css.js +1 -0
  112. package/notification/style/index.js +1 -0
  113. package/notification/type.ts +23 -0
  114. package/package.json +52 -0
  115. package/popup/Popup.tsx +264 -0
  116. package/popup/defaultProps.ts +13 -0
  117. package/popup/hooks/useTrigger.ts +276 -0
  118. package/popup/index.ts +6 -0
  119. package/popup/style/css.js +1 -0
  120. package/popup/style/index.js +1 -0
  121. package/popup/type.ts +130 -0
  122. package/portal/Portal.tsx +63 -0
  123. package/portal/index.ts +1 -0
  124. package/select/Option.tsx +162 -0
  125. package/select/OptionGroup.tsx +30 -0
  126. package/select/PopupContent.tsx +271 -0
  127. package/select/Select.tsx +586 -0
  128. package/select/defaultProps.ts +27 -0
  129. package/select/hooks/useOptions.ts +120 -0
  130. package/select/hooks/usePanelVirtualScroll.ts +111 -0
  131. package/select/index.ts +9 -0
  132. package/select/style/css.js +1 -0
  133. package/select/style/index.js +2 -0
  134. package/select/type.ts +382 -0
  135. package/select/utils/helper.ts +256 -0
  136. package/select-input/SelectInput.tsx +98 -0
  137. package/select-input/defaultProps.ts +15 -0
  138. package/select-input/hook/useMultiple.tsx +100 -0
  139. package/select-input/hook/useOverlayInnerStyle.ts +84 -0
  140. package/select-input/hook/useSingle.tsx +112 -0
  141. package/select-input/index.ts +6 -0
  142. package/select-input/interface.ts +18 -0
  143. package/select-input/style/css.js +1 -0
  144. package/select-input/style/index.js +1 -0
  145. package/select-input/type.ts +280 -0
  146. package/space/defaultProps.ts +0 -0
  147. package/space/index.ts +0 -0
  148. package/space/type.ts +0 -0
  149. package/style/index.js +2 -0
  150. package/styles/_global.scss +39 -0
  151. package/styles/_vars.scss +386 -0
  152. package/styles/components/alert/_index.scss +175 -0
  153. package/styles/components/alert/_vars.scss +39 -0
  154. package/styles/components/badge/_index.scss +70 -0
  155. package/styles/components/badge/_vars.scss +25 -0
  156. package/styles/components/button/_index.scss +511 -0
  157. package/styles/components/button/_mixins.scss +39 -0
  158. package/styles/components/button/_vars.scss +122 -0
  159. package/styles/components/checkbox/_index.scss +158 -0
  160. package/styles/components/checkbox/_mixin.scss +0 -0
  161. package/styles/components/checkbox/_var.scss +60 -0
  162. package/styles/components/dialog/_animate.scss +135 -0
  163. package/styles/components/dialog/_index.scss +311 -0
  164. package/styles/components/dialog/_mixins.scss +0 -0
  165. package/styles/components/dialog/_vars.scss +59 -0
  166. package/styles/components/form/_index.scss +174 -0
  167. package/styles/components/form/_mixins.scss +76 -0
  168. package/styles/components/form/_vars.scss +100 -0
  169. package/styles/components/input/_index.scss +349 -0
  170. package/styles/components/input/_map.scss +0 -0
  171. package/styles/components/input/_mixins.scss +116 -0
  172. package/styles/components/input/_vars.scss +134 -0
  173. package/styles/components/loading/_index.scss +112 -0
  174. package/styles/components/loading/_vars.scss +39 -0
  175. package/styles/components/notification/_index.scss +160 -0
  176. package/styles/components/notification/_mixins.scss +12 -0
  177. package/styles/components/notification/_vars.scss +59 -0
  178. package/styles/components/popup/_index.scss +82 -0
  179. package/styles/components/popup/_mixin.scss +149 -0
  180. package/styles/components/popup/_var.scss +31 -0
  181. package/styles/components/select/_index.scss +290 -0
  182. package/styles/components/select/_var.scss +65 -0
  183. package/styles/components/select-input/_index.scss +5 -0
  184. package/styles/components/select-input/_var.scss +3 -0
  185. package/styles/components/switch/_index.scss +279 -0
  186. package/styles/components/switch/_mixins.scss +0 -0
  187. package/styles/components/switch/_vars.scss +61 -0
  188. package/styles/components/tag/_index.scss +316 -0
  189. package/styles/components/tag/_var.scss +85 -0
  190. package/styles/components/tag-input/_index.scss +163 -0
  191. package/styles/components/tag-input/_vars.scss +16 -0
  192. package/styles/globals.css +250 -0
  193. package/styles/mixins/_focus.scss +7 -0
  194. package/styles/mixins/_layout.scss +32 -0
  195. package/styles/mixins/_reset.scss +10 -0
  196. package/styles/mixins/_scrollbar.scss +31 -0
  197. package/styles/mixins/_text.scss +48 -0
  198. package/styles/rillple.css +16 -0
  199. package/styles/scrollbar.css +42 -0
  200. package/styles/themes/_dark.scss +191 -0
  201. package/styles/themes/_font.scss +79 -0
  202. package/styles/themes/_index.scss +5 -0
  203. package/styles/themes/_light.scss +190 -0
  204. package/styles/themes/_radius.scss +9 -0
  205. package/styles/themes/_size.scss +68 -0
  206. package/styles/themes.css +66 -0
  207. package/styles/utilities/_animation.scss +57 -0
  208. package/styles/utilities/_tips.scss +9 -0
  209. package/switch/Switch.tsx +120 -0
  210. package/switch/defaultProps.ts +3 -0
  211. package/switch/index.ts +7 -0
  212. package/switch/style/css.js +1 -0
  213. package/switch/style/index.js +1 -0
  214. package/switch/type.ts +46 -0
  215. package/tag/Tag.tsx +149 -0
  216. package/tag/defaultProps.ts +19 -0
  217. package/tag/index.ts +8 -0
  218. package/tag/style/css.js +1 -0
  219. package/tag/style/index.js +1 -0
  220. package/tag/type.ts +170 -0
  221. package/tag-input/TagInput.tsx +215 -0
  222. package/tag-input/defaultProps.ts +15 -0
  223. package/tag-input/hooks/useHover.ts +28 -0
  224. package/tag-input/hooks/useTagList.tsx +131 -0
  225. package/tag-input/hooks/useTagScroll.ts +105 -0
  226. package/tag-input/index.ts +9 -0
  227. package/tag-input/style/css.js +1 -0
  228. package/tag-input/style/index.js +1 -0
  229. package/tag-input/type.ts +224 -0
  230. package/tag-input/useTagList.tsx +131 -0
  231. package/utils/composeRefs.ts +14 -0
  232. package/utils/dom.ts +29 -0
  233. package/utils/forwardRefWithStatics.ts +12 -0
  234. package/utils/getScrollbarWidth.ts +11 -0
  235. package/utils/helper.ts +161 -0
  236. package/utils/isFragment.ts +22 -0
  237. package/utils/listener.ts +37 -0
  238. package/utils/noop.ts +3 -0
  239. package/utils/parentTNode.ts +38 -0
  240. package/utils/parseTNode.ts +38 -0
  241. package/utils/react-render.ts +108 -0
  242. package/utils/ref.ts +6 -0
  243. package/utils/refs.ts +81 -0
  244. package/utils/style.ts +60 -0
  245. package/utils/transition.ts +28 -0
@@ -0,0 +1,254 @@
1
+ import React, { forwardRef, useEffect, useImperativeHandle, useRef } from "react";
2
+ import { CSSTransition } from "react-transition-group";
3
+ import classNames from "classnames";
4
+ import { isUndefined } from "lodash-es";
5
+ import type { StyledProps } from "../common";
6
+ import Portal from "../common/Portal";
7
+ import useAttach from "../hooks/useAttach";
8
+ import useConfig from "../hooks/useConfig";
9
+ import useDefaultProps from "../hooks/useDefaultProps";
10
+ import useSetState from "../hooks/useSetState";
11
+ // import { useLocaleReceiver } from '../locale/LocalReceiver';
12
+ import { dialogDefaultProps } from "./defaultProps";
13
+ import DialogCard from "./DialogCard";
14
+ import useDialogDrag from "./hooks/useDialogDrag";
15
+ import useDialogEsc from "./hooks/useDialogEsc";
16
+ import useDialogPosition from "./hooks/useDialogPosition";
17
+ import useLockStyle from "./hooks/useLockStyle";
18
+ import type { DialogInstance, TdDialogProps } from "./type";
19
+ import { parseValueToPx } from "./utils";
20
+
21
+ export interface DialogProps extends TdDialogProps, StyledProps {
22
+ isPlugin?: boolean; // 是否以插件形式调用
23
+ }
24
+
25
+ const Dialog = forwardRef<DialogInstance, DialogProps>((originalProps, ref) => {
26
+ const props = useDefaultProps<DialogProps>(originalProps, dialogDefaultProps);
27
+ const { children, ...restProps } = props;
28
+ const { classPrefix } = useConfig();
29
+
30
+ const componentCls = `${classPrefix}-dialog`;
31
+ const wrapRef = useRef<HTMLDivElement>(null);
32
+ const contentClickRef = useRef(false);
33
+ const dialogCardRef = useRef<HTMLDivElement>(null);
34
+ const dialogPosition = useRef(null);
35
+ const portalRef = useRef(null);
36
+ const maskRef = useRef(null);
37
+ const [state, setState] = useSetState<DialogProps>({ isPlugin: false, ...restProps });
38
+ // const [local] = useLocaleReceiver('dialog');
39
+
40
+ const {
41
+ className,
42
+ dialogClassName,
43
+ style,
44
+ width,
45
+ mode,
46
+ zIndex,
47
+ visible,
48
+ attach,
49
+ onBeforeOpen,
50
+ onBeforeClose,
51
+ onOpened,
52
+ onCancel,
53
+ onConfirm,
54
+ onClose,
55
+ onClosed,
56
+ isPlugin,
57
+ draggable,
58
+ onOverlayClick,
59
+ onEscKeydown,
60
+ closeOnEscKeydown,
61
+ confirmOnEnter,
62
+ showOverlay,
63
+ showInAttachedElement,
64
+ closeOnOverlayClick,
65
+ destroyOnClose,
66
+ preventScrollThrough,
67
+ onCloseBtnClick,
68
+ forceRender,
69
+ lazy,
70
+ ...restState
71
+ } = state;
72
+
73
+ const dialogAttach = useAttach("dialog", attach);
74
+
75
+ useLockStyle({ preventScrollThrough, visible, mode, showInAttachedElement });
76
+ useDialogEsc(visible, wrapRef);
77
+ useDialogPosition(visible, dialogCardRef);
78
+ useDialogDrag({
79
+ dialogCardRef,
80
+ canDraggable: draggable && mode === "modeless"
81
+ });
82
+
83
+ useEffect(() => {
84
+ if (isPlugin) {
85
+ return;
86
+ }
87
+ // 插件式调用不会更新props, 只有组件式调用才会更新props
88
+ setState((prevState) => ({ ...prevState, ...props }));
89
+ }, [props, setState, isPlugin]);
90
+
91
+ useImperativeHandle(ref, () => ({
92
+ show() {
93
+ setState({ visible: true });
94
+ },
95
+ hide() {
96
+ setState({ visible: false });
97
+ },
98
+ setConfirmLoading: (loading: boolean) => {
99
+ setState({ confirmLoading: loading });
100
+ },
101
+ destroy() {
102
+ setState({ visible: false, destroyOnClose: true });
103
+ },
104
+ update(newOptions) {
105
+ setState((prevState) => ({ ...prevState, ...newOptions }));
106
+ }
107
+ }));
108
+
109
+ const onMaskClick = (e: React.MouseEvent<HTMLDivElement>) => {
110
+ if (showOverlay && closeOnOverlayClick) {
111
+ // 判断点击事件初次点击是否为内容区域
112
+ if (contentClickRef.current) {
113
+ contentClickRef.current = false;
114
+ } else if (e.target === dialogPosition.current) {
115
+ onOverlayClick?.({ e });
116
+ onClose?.({ e, trigger: "overlay" });
117
+ }
118
+ }
119
+ };
120
+
121
+ const handleCancel = ({ e }) => {
122
+ onCancel?.({ e });
123
+ onClose?.({ e, trigger: "cancel" });
124
+ };
125
+
126
+ const handleClose = ({ e }) => {
127
+ onCloseBtnClick?.({ e });
128
+ onClose?.({ e, trigger: "close-btn" });
129
+ };
130
+
131
+ const handleKeyDown = (e: React.KeyboardEvent<HTMLDivElement>) => {
132
+ // https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/keyCode
133
+ if (e.key === "Escape") {
134
+ e.stopPropagation();
135
+ onEscKeydown?.({ e });
136
+ if (closeOnEscKeydown) {
137
+ onClose?.({ e, trigger: "esc" });
138
+ }
139
+ } else if (e.key === "Enter" || e.key === "NumpadEnter") {
140
+ // 回车键触发点击确认事件
141
+ e.stopPropagation();
142
+ if (confirmOnEnter) {
143
+ onConfirm?.({ e });
144
+ }
145
+ }
146
+ };
147
+
148
+ const onAnimateLeave = () => {
149
+ onClosed?.();
150
+
151
+ if (!wrapRef.current) return;
152
+ wrapRef.current.style.display = "none";
153
+ };
154
+
155
+ const onAnimateStart = () => {
156
+ if (!wrapRef.current) return;
157
+ onBeforeOpen?.();
158
+ wrapRef.current.style.display = "block";
159
+ };
160
+
161
+ const onInnerAnimateStart = () => {
162
+ if (!dialogCardRef.current) return;
163
+ dialogCardRef.current.style.display = "block";
164
+ };
165
+
166
+ const onInnerAnimateLeave = () => {
167
+ if (!dialogCardRef.current) return;
168
+ dialogCardRef.current.style.display = "none";
169
+ };
170
+
171
+ const renderMask = () => {
172
+ if (mode !== "modal") return null;
173
+
174
+ return showOverlay ? (
175
+ <CSSTransition
176
+ in={visible}
177
+ appear
178
+ timeout={300}
179
+ classNames={`${componentCls}-fade`}
180
+ mountOnEnter
181
+ unmountOnExit
182
+ nodeRef={maskRef}
183
+ >
184
+ <div ref={maskRef} className={`${componentCls}__mask`} />
185
+ </CSSTransition>
186
+ ) : null;
187
+ };
188
+ return (
189
+ <CSSTransition
190
+ in={visible}
191
+ appear
192
+ timeout={300}
193
+ mountOnEnter={isUndefined(forceRender) ? lazy : !forceRender}
194
+ unmountOnExit={destroyOnClose}
195
+ nodeRef={portalRef}
196
+ onEnter={onAnimateStart}
197
+ onEntered={onOpened}
198
+ onExit={() => onBeforeClose?.()}
199
+ onExited={onAnimateLeave}
200
+ >
201
+ <Portal attach={dialogAttach} ref={portalRef}>
202
+ <div
203
+ ref={wrapRef}
204
+ className={classNames(className, `${componentCls}__ctx`, `${componentCls}__${mode}`, {
205
+ [`${componentCls}__ctx--fixed`]: !showInAttachedElement,
206
+ [`${componentCls}__ctx--absolute`]: showInAttachedElement
207
+ })}
208
+ style={{ zIndex, display: "none" }}
209
+ onKeyDown={handleKeyDown}
210
+ tabIndex={0}
211
+ >
212
+ {renderMask()}
213
+ <div className={`${componentCls}__wrap`}>
214
+ <div
215
+ ref={dialogPosition}
216
+ className={classNames(`${componentCls}__position`, {
217
+ [`${componentCls}--top`]: !!props.top || props.placement === "top",
218
+ [`${componentCls}--center`]: props.placement === "center" && !props.top
219
+ })}
220
+ style={{ paddingTop: parseValueToPx(props.top) }}
221
+ onClick={onMaskClick}
222
+ >
223
+ <CSSTransition
224
+ in={visible}
225
+ appear
226
+ timeout={300}
227
+ classNames={`${componentCls}-zoom`}
228
+ nodeRef={dialogCardRef}
229
+ onEnter={onInnerAnimateStart}
230
+ onExited={onInnerAnimateLeave}
231
+ >
232
+ <DialogCard
233
+ ref={dialogCardRef}
234
+ {...restState}
235
+ className={dialogClassName}
236
+ style={{ ...style, width: parseValueToPx(width || style?.width) }}
237
+ onConfirm={onConfirm}
238
+ onCancel={handleCancel}
239
+ onCloseBtnClick={handleClose}
240
+ >
241
+ {children as React.ReactNode}
242
+ </DialogCard>
243
+ </CSSTransition>
244
+ </div>
245
+ </div>
246
+ </div>
247
+ </Portal>
248
+ </CSSTransition>
249
+ );
250
+ });
251
+
252
+ Dialog.displayName = "Dialog";
253
+
254
+ export default Dialog;
@@ -0,0 +1,152 @@
1
+ import React, { forwardRef, isValidElement } from "react";
2
+ import classNames from "classnames";
3
+ import { isString, isObject, isFunction } from "lodash-es";
4
+ import {
5
+ IconClose as TdCloseIcon,
6
+ IconInfoCircle as TdInfoCircleFilledIcon,
7
+ IconCheckCircleStroked as TdCheckCircleFilledIcon
8
+ } from "@tendaui/icons";
9
+ import Button, { ButtonProps } from "../button";
10
+ import { TdDialogCardProps } from "./type";
11
+ import { StyledProps, TNode } from "../common";
12
+ import parseTNode from "../utils/parseTNode";
13
+ import useConfig from "../hooks/useConfig";
14
+ import useGlobalIcon from "../hooks/useGlobalIcon";
15
+ // import { useLocaleReceiver } from '../locale/LocalReceiver';
16
+ import { dialogCardDefaultProps } from "./defaultProps";
17
+ import useDefaultProps from "../hooks/useDefaultProps";
18
+
19
+ export interface DialogCardProps extends TdDialogCardProps, StyledProps, React.HTMLAttributes<HTMLDivElement> {
20
+ children?: React.ReactNode;
21
+ }
22
+
23
+ const renderDialogButton = (btn: DialogCardProps["cancelBtn"], defaultProps: ButtonProps) => {
24
+ let result = null;
25
+
26
+ if (isString(btn)) {
27
+ result = <Button {...defaultProps}>{btn as TNode}</Button>;
28
+ } else if (isValidElement(btn)) {
29
+ result = btn;
30
+ } else if (isObject(btn)) {
31
+ result = <Button {...defaultProps} {...(btn as Record<string, unknown>)} />;
32
+ } else if (isFunction(btn)) {
33
+ result = (btn as () => React.ReactNode)();
34
+ }
35
+
36
+ return result;
37
+ };
38
+
39
+ const DialogCard = forwardRef<HTMLDivElement, DialogCardProps>((props, ref) => {
40
+ const { classPrefix } = useConfig();
41
+ const componentCls = `${classPrefix}-dialog`;
42
+ useGlobalIcon({
43
+ CloseIcon: TdCloseIcon,
44
+ InfoCircleFilledIcon: TdInfoCircleFilledIcon,
45
+ CheckCircleFilledIcon: TdCheckCircleFilledIcon
46
+ });
47
+ // const [local, t] = useLocaleReceiver('dialog');
48
+ // const confirmText = t(local.confirm);
49
+ // const cancelText = t(local.cancel);
50
+ const confirmText = "确认";
51
+ const cancelText = "取消";
52
+ const {
53
+ theme,
54
+ header,
55
+ closeBtn,
56
+ footer,
57
+ body,
58
+ children,
59
+ className,
60
+ onCancel,
61
+ onConfirm,
62
+ onCloseBtnClick,
63
+ cancelBtn = cancelText,
64
+ confirmBtn = confirmText,
65
+ confirmLoading,
66
+ ...otherProps
67
+ } = useDefaultProps<DialogCardProps>(props, dialogCardDefaultProps);
68
+
69
+ const renderHeaderContent = () => {
70
+ const iconMap = {
71
+ info: <TdInfoCircleFilledIcon className={`${classPrefix}-is-info`} />,
72
+ warning: <TdInfoCircleFilledIcon className={`${classPrefix}-is-warning`} />,
73
+ // error is going to deprecated
74
+ error: <TdInfoCircleFilledIcon className={`${classPrefix}-is-error`} />,
75
+ danger: <TdInfoCircleFilledIcon className={`${classPrefix}-is-error`} />,
76
+ success: <TdCheckCircleFilledIcon className={`${classPrefix}-is-success`} />
77
+ };
78
+
79
+ return (
80
+ <div className={`${componentCls}__header-content`}>
81
+ {iconMap[theme]}
82
+ {header}
83
+ </div>
84
+ );
85
+ };
86
+
87
+ const renderCloseBtn = () => {
88
+ if (!closeBtn) {
89
+ return null;
90
+ }
91
+
92
+ const closeIcon = () => (closeBtn === true ? <TdCloseIcon /> : closeBtn);
93
+
94
+ return (
95
+ <span
96
+ className={`${componentCls}__close`}
97
+ style={{
98
+ marginLeft: "auto"
99
+ }}
100
+ onClick={(e: React.MouseEvent<HTMLDivElement>) => onCloseBtnClick?.({ e })}
101
+ >
102
+ {closeIcon() as React.ReactNode}
103
+ </span>
104
+ );
105
+ };
106
+
107
+ const renderHeader = () => (
108
+ <div className={classNames(`${componentCls}__header`)}>
109
+ {renderHeaderContent()}
110
+ {renderCloseBtn()}
111
+ </div>
112
+ );
113
+
114
+ const renderFooter = () => {
115
+ const defaultFooter = () => {
116
+ const renderCancelBtn = renderDialogButton(cancelBtn, {
117
+ variant: "outline",
118
+ onClick: (e: React.MouseEvent<HTMLButtonElement>) => onCancel?.({ e }),
119
+ className: classNames(
120
+ `${componentCls}__cancel`,
121
+ (cancelBtn as { props?: { className?: string } })?.props?.className
122
+ )
123
+ });
124
+ const renderConfirmBtn = renderDialogButton(confirmBtn, {
125
+ theme: "primary",
126
+ loading: confirmLoading,
127
+ onClick: (e: React.MouseEvent<HTMLButtonElement>) => onConfirm?.({ e }),
128
+ className: classNames(`${componentCls}__confirm`, (confirmBtn as { className?: string })?.className)
129
+ });
130
+
131
+ return (
132
+ <>
133
+ {renderCancelBtn}
134
+ {renderConfirmBtn}
135
+ </>
136
+ );
137
+ };
138
+
139
+ return <div className={`${componentCls}__footer`}>{parseTNode(footer, null, defaultFooter())}</div>;
140
+ };
141
+ return (
142
+ <div ref={ref} {...otherProps} className={classNames(componentCls, `${componentCls}--default`, className)}>
143
+ {!!header && renderHeader()}
144
+ <div className={`${componentCls}__body`}>{(body || children) as React.ReactNode}</div>
145
+ {!!footer && renderFooter()}
146
+ </div>
147
+ );
148
+ });
149
+
150
+ DialogCard.displayName = "DialogCard";
151
+
152
+ export default DialogCard;
@@ -0,0 +1,25 @@
1
+ /**
2
+ * 该文件为脚本自动生成文件,请勿随意修改。如需修改请联系 PMC
3
+ * */
4
+
5
+ import { TdDialogCardProps, TdDialogProps } from "./type";
6
+
7
+ export const dialogCardDefaultProps: TdDialogCardProps = {
8
+ closeBtn: true,
9
+ footer: true,
10
+ header: true,
11
+ theme: "default"
12
+ };
13
+
14
+ export const dialogDefaultProps: TdDialogProps = {
15
+ closeOnEscKeydown: undefined,
16
+ closeOnOverlayClick: undefined,
17
+ destroyOnClose: false,
18
+ draggable: false,
19
+ mode: "modal",
20
+ placement: "top",
21
+ preventScrollThrough: true,
22
+ showInAttachedElement: false,
23
+ showOverlay: true,
24
+ lazy: true
25
+ };
@@ -0,0 +1,50 @@
1
+ import { RefObject, useRef } from "react";
2
+ import useMouseEvent from "../../hooks/useMouseEvent";
3
+
4
+ interface DialogDragProps {
5
+ dialogCardRef: RefObject<HTMLDivElement | null>;
6
+ canDraggable?: boolean;
7
+ }
8
+
9
+ const useDialogDrag = (props: DialogDragProps) => {
10
+ const { dialogCardRef, canDraggable } = props;
11
+
12
+ const validWindow = typeof window === "object";
13
+ const screenHeight = validWindow ? window.innerHeight || document.documentElement.clientHeight : undefined;
14
+ const screenWidth = validWindow ? window.innerWidth || document.documentElement.clientWidth : undefined;
15
+
16
+ const dragOffset = useRef({ x: 0, y: 0 });
17
+
18
+ useMouseEvent(dialogCardRef, {
19
+ enabled: canDraggable,
20
+ onDown: (e) => {
21
+ if (!validWindow || screenWidth === undefined || screenHeight === undefined || !dialogCardRef.current) return;
22
+ const { offsetLeft, offsetTop, offsetWidth, offsetHeight, style } = dialogCardRef.current;
23
+ if (offsetWidth > screenWidth || offsetHeight > screenHeight) return;
24
+ style.cursor = "move";
25
+ dragOffset.current = {
26
+ x: e.clientX - offsetLeft,
27
+ y: e.clientY - offsetTop
28
+ };
29
+ },
30
+ onMove: (e) => {
31
+ if (!validWindow || screenWidth === undefined || screenHeight === undefined || !dialogCardRef.current) return;
32
+ const { offsetWidth, offsetHeight, style } = dialogCardRef.current;
33
+ let diffX = e.clientX - dragOffset.current.x;
34
+ let diffY = e.clientY - dragOffset.current.y;
35
+ // 拖拽上左边界限制
36
+ if (diffX < 0) diffX = 0;
37
+ if (diffY < 0) diffY = 0;
38
+ if (screenWidth - offsetWidth - diffX < 0) diffX = screenWidth - offsetWidth;
39
+ if (screenHeight - offsetHeight - diffY < 0) diffY = screenHeight - offsetHeight;
40
+ style.position = "absolute";
41
+ style.left = `${diffX}px`;
42
+ style.top = `${diffY}px`;
43
+ },
44
+ onUp: () => {
45
+ if (dialogCardRef.current) dialogCardRef.current.style.cursor = "default";
46
+ }
47
+ });
48
+ };
49
+
50
+ export default useDialogDrag;
@@ -0,0 +1,31 @@
1
+ import { useEffect, type RefObject } from "react";
2
+
3
+ const dialogSet: Set<RefObject<HTMLElement | null>> = new Set();
4
+
5
+ const useDialogEsc = (visible: boolean | undefined, dialog: RefObject<HTMLElement | null>) => {
6
+ useEffect(() => {
7
+ if (visible) {
8
+ // 将 dialog 添加至 Set 对象
9
+ if (dialog?.current) {
10
+ dialogSet.add(dialog);
11
+ dialog?.current?.focus();
12
+ }
13
+ } else if (dialogSet.has(dialog)) {
14
+ // 将 dialog 从 Set 对象删除
15
+ dialogSet.delete(dialog);
16
+ const dialogList = [...dialogSet];
17
+ // 将 Set 对象中最后一个 dialog 设置为 focus
18
+ dialogList[dialogList.length - 1]?.current?.focus();
19
+ }
20
+ return () => {
21
+ // 从 Set 对象删除无效的 dialog
22
+ dialogSet.forEach((item) => {
23
+ if (item?.current === null) {
24
+ dialogSet.delete(item);
25
+ }
26
+ });
27
+ };
28
+ }, [visible, dialog]);
29
+ };
30
+
31
+ export default useDialogEsc;
@@ -0,0 +1,36 @@
1
+ import { RefObject, useEffect, useRef } from "react";
2
+ import useIsomorphicLayoutEffect from "../../hooks/useLayoutEffect";
3
+ type Position = { x: number; y: number };
4
+ export default function useDialogPosition(visible: boolean | undefined, dialogCardRef: RefObject<HTMLElement | null>) {
5
+ const mousePosRef = useRef<Position>(null);
6
+
7
+ const getClickPosition = (e: MouseEvent) => {
8
+ if (mousePosRef) {
9
+ mousePosRef.current = {
10
+ x: e.clientX,
11
+ y: e.clientY
12
+ };
13
+ setTimeout(() => {
14
+ mousePosRef.current = null;
15
+ }, 100);
16
+ }
17
+ };
18
+
19
+ useIsomorphicLayoutEffect(() => {
20
+ document.addEventListener("click", getClickPosition, true);
21
+ return () => {
22
+ document.removeEventListener("click", getClickPosition, true);
23
+ };
24
+ }, []);
25
+
26
+ useEffect(() => {
27
+ if (!visible) return;
28
+ // 动画渲染初始位置
29
+ if (mousePosRef.current && dialogCardRef.current) {
30
+ // eslint-disable-next-line
31
+ dialogCardRef.current.style.transformOrigin = `${mousePosRef.current.x - dialogCardRef.current.offsetLeft}px ${
32
+ mousePosRef.current.y - dialogCardRef.current.offsetTop
33
+ }px`;
34
+ }
35
+ }, [visible, dialogCardRef]);
36
+ }
@@ -0,0 +1,54 @@
1
+ import { useRef, useCallback } from "react";
2
+ import { getScrollbarWidth } from "../../utils/getScrollbarWidth";
3
+ import useLayoutEffect from "../../hooks/useLayoutEffect";
4
+ import type { TdDialogProps } from "../type";
5
+ let key = 1;
6
+
7
+ export default function useDialogLockStyle({
8
+ preventScrollThrough,
9
+ visible,
10
+ mode,
11
+ showInAttachedElement
12
+ }: Partial<TdDialogProps>) {
13
+ const lockStyleRef = useRef<HTMLStyleElement>(null);
14
+ const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
15
+
16
+ const clearStyleFunc = useCallback(() => {
17
+ if (timerRef.current) {
18
+ clearTimeout(timerRef.current);
19
+ }
20
+ timerRef.current = setTimeout(() => {
21
+ lockStyleRef.current?.parentNode?.removeChild?.(lockStyleRef.current);
22
+ }, 150);
23
+ }, []);
24
+
25
+ useLayoutEffect(() => {
26
+ if (typeof document === "undefined" || !visible) return;
27
+ if (!lockStyleRef.current) {
28
+ lockStyleRef.current = document.createElement("style");
29
+ }
30
+ const hasScrollBar = document.documentElement.scrollHeight > document.documentElement.clientHeight;
31
+ const scrollbarWidth = hasScrollBar ? getScrollbarWidth() : 0;
32
+
33
+ lockStyleRef.current.dataset.id = `td_dialog_${+new Date()}_${(key += 1)}`;
34
+ lockStyleRef.current.innerHTML = `
35
+ html body {
36
+ overflow-y: hidden;
37
+ width: calc(100% - ${scrollbarWidth}px);
38
+ }
39
+ `;
40
+
41
+ return clearStyleFunc;
42
+ }, [visible, clearStyleFunc]);
43
+
44
+ useLayoutEffect(() => {
45
+ if (typeof document === "undefined") return;
46
+ if (mode !== "modal" || !preventScrollThrough || showInAttachedElement) return;
47
+
48
+ if (visible) {
49
+ if (lockStyleRef.current) document.head.appendChild(lockStyleRef.current);
50
+ } else {
51
+ clearStyleFunc();
52
+ }
53
+ }, [preventScrollThrough, visible, mode, showInAttachedElement, clearStyleFunc]);
54
+ }
@@ -0,0 +1,13 @@
1
+ import _Dialog from "./Dialog";
2
+ import _DialogCard from "./DialogCard";
3
+ import { DialogPlugin as _DialogPlugin } from "./plugin";
4
+ import "./style/index.js";
5
+
6
+ export type { DialogProps } from "./Dialog";
7
+ export * from "./type";
8
+ export const Dialog = _Dialog;
9
+ export const DialogCard = _DialogCard;
10
+
11
+ export const dialog = _DialogPlugin;
12
+ export const DialogPlugin = _DialogPlugin;
13
+ export default Dialog;
@@ -0,0 +1,78 @@
1
+ import React from "react";
2
+ import { render } from "../utils/react-render";
3
+ import DialogComponent, { DialogProps } from "./Dialog";
4
+ import { getAttach } from "../utils/dom";
5
+ import { DialogOptions, DialogMethod, DialogConfirmMethod, DialogAlertMethod, DialogInstance } from "./type";
6
+ import PluginContainer from "../common/PluginContainer";
7
+ import ConfigProvider from "../config-provider";
8
+
9
+ export interface DialogPluginType extends DialogMethod {
10
+ alert: DialogAlertMethod;
11
+ confirm: DialogConfirmMethod;
12
+ }
13
+
14
+ const createDialog: DialogPluginType = (props: DialogOptions): DialogInstance => {
15
+ const dialogRef = React.createRef<DialogInstance>();
16
+ const options = { ...props };
17
+ const { visible = true } = options;
18
+
19
+ const fragment = document.createDocumentFragment();
20
+
21
+ const dGlobalConfig = ConfigProvider.getGlobalConfig();
22
+ render(
23
+ <PluginContainer globalConfig={dGlobalConfig}>
24
+ <DialogComponent {...(options as DialogProps)} visible={visible} ref={dialogRef} isPlugin />
25
+ </PluginContainer>,
26
+ fragment
27
+ );
28
+ const container = getAttach(options.attach);
29
+
30
+ if (container) {
31
+ container.appendChild(fragment);
32
+ } else {
33
+ console.error("Dialog", "attach is not exist");
34
+ }
35
+
36
+ const dialogNode: DialogInstance = {
37
+ show: () => {
38
+ requestAnimationFrame(() => {
39
+ container?.appendChild(fragment);
40
+ dialogRef.current?.show();
41
+ });
42
+ },
43
+ hide: () => {
44
+ requestAnimationFrame(() => {
45
+ dialogRef.current?.destroy();
46
+ });
47
+ },
48
+ setConfirmLoading: (loading: boolean) => {
49
+ requestAnimationFrame(() => {
50
+ dialogRef.current?.setConfirmLoading(loading);
51
+ });
52
+ },
53
+ update: (updateOptions: DialogOptions) => {
54
+ requestAnimationFrame(() => {
55
+ dialogRef.current?.update(updateOptions);
56
+ });
57
+ },
58
+ destroy: () => {
59
+ requestAnimationFrame(() => {
60
+ dialogRef.current?.destroy();
61
+ });
62
+ }
63
+ };
64
+ return dialogNode;
65
+ };
66
+
67
+ const confirm: DialogConfirmMethod = (props: DialogOptions) => createDialog(props);
68
+
69
+ const alert: DialogAlertMethod = (props: Omit<DialogOptions, "confirmBtn">) => {
70
+ const options = { ...props };
71
+ options.cancelBtn = null;
72
+ return createDialog(options);
73
+ };
74
+
75
+ createDialog.alert = alert;
76
+ createDialog.confirm = confirm;
77
+
78
+ export const DialogPlugin = createDialog;