@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 @@
1
+ import "../../styles/components/button/_index.scss";
package/button/type.ts ADDED
@@ -0,0 +1,82 @@
1
+ import { TNode, TElement, SizeEnum } from "../common";
2
+ import { MouseEvent } from "react";
3
+
4
+ export interface TdButtonProps {
5
+ /**
6
+ * 是否为块级元素
7
+ * @default false
8
+ */
9
+ block?: boolean;
10
+ /**
11
+ * 按钮内容,同 content
12
+ */
13
+ children?: TNode;
14
+ /**
15
+ * 按钮内容
16
+ */
17
+ content?: TNode;
18
+ /**
19
+ * 禁用状态
20
+ * @default false
21
+ */
22
+ disabled?: boolean;
23
+ /**
24
+ * 原生的form属性,支持用于通过 form 属性触发对应 id 的 form 的表单事件
25
+ */
26
+ form?: string;
27
+ /**
28
+ * 是否为幽灵按钮(镂空按钮)
29
+ * @default false
30
+ */
31
+ ghost?: boolean;
32
+ /**
33
+ * 跳转地址。href 存在时,按钮标签默认使用 `<a>` 渲染;如果指定了 `tag` 则使用指定的标签渲染
34
+ * @default ''
35
+ */
36
+ href?: string;
37
+ /**
38
+ * 按钮内部图标,可完全自定义
39
+ */
40
+ icon?: TElement;
41
+ /**
42
+ * 是否显示为加载状态
43
+ * @default false
44
+ */
45
+ loading?: boolean;
46
+ /**
47
+ * 按钮形状,有 4 种:长方形、正方形、圆角长方形、圆形
48
+ * @default rectangle
49
+ */
50
+ shape?: "rectangle" | "square" | "round" | "circle";
51
+ /**
52
+ * 组件尺寸
53
+ * @default medium
54
+ */
55
+ size?: SizeEnum;
56
+ /**
57
+ * 右侧内容,可用于定义右侧图标
58
+ */
59
+ suffix?: TElement;
60
+ /**
61
+ * 渲染按钮的 HTML 标签,默认使用标签 `<button>` 渲染,可以自定义为 `<a>` `<div>` 等。透传全部 HTML 属性,如:`href/target/data-*` 等。⚠️ 禁用按钮 `<button disabled>`无法显示 Popup 浮层信息,可通过修改 `tag=div` 解决这个问题
62
+ */
63
+ tag?: "button" | "a" | "div";
64
+ /**
65
+ * 组件风格,依次为默认色、品牌色、危险色、警告色、成功色
66
+ */
67
+ theme?: "default" | "primary" | "danger" | "warning" | "success";
68
+ /**
69
+ * 按钮类型
70
+ * @default button
71
+ */
72
+ type?: "submit" | "reset" | "button";
73
+ /**
74
+ * 按钮形式,基础、线框、虚线、文字
75
+ * @default base
76
+ */
77
+ variant?: "base" | "outline" | "dashed" | "text";
78
+ /**
79
+ * 点击时触发
80
+ */
81
+ onClick?: (e: MouseEvent<HTMLElement>) => void;
82
+ }
@@ -0,0 +1,19 @@
1
+ import React from "react";
2
+ import forwardRefWithStatics from "../utils/forwardRefWithStatics";
3
+ import Check, { CheckProps } from "../common/Check";
4
+ import CheckboxGroup from "./CheckboxGroup";
5
+ import { checkboxDefaultProps } from "./defaultProps";
6
+ import useDefaultProps from "../hooks/useDefaultProps";
7
+
8
+ export type CheckboxProps = Omit<CheckProps, "type">;
9
+
10
+ const Checkbox = forwardRefWithStatics(
11
+ (props: CheckboxProps, ref: React.Ref<HTMLLabelElement>) => (
12
+ <Check ref={ref} type="checkbox" {...useDefaultProps<CheckboxProps>(props, checkboxDefaultProps)} />
13
+ ),
14
+ { Group: CheckboxGroup }
15
+ );
16
+
17
+ Checkbox.displayName = "Checkbox";
18
+
19
+ export default Checkbox;
@@ -0,0 +1,207 @@
1
+ import React, { ReactElement, useCallback, useEffect, useMemo, useState } from "react";
2
+ import classNames from "classnames";
3
+ import { isNumber } from "lodash-es";
4
+ import useConfig from "../hooks/useConfig";
5
+ import { CheckContext, CheckContextValue, CheckProps } from "../common/Check";
6
+ import { CheckboxGroupValue, CheckboxOption, CheckboxOptionObj, TdCheckboxGroupProps, TdCheckboxProps } from "./type";
7
+ import { StyledProps } from "../common";
8
+ import useControlled from "../hooks/useControlled";
9
+ import Checkbox from "./Checkbox";
10
+ import { checkboxGroupDefaultProps } from "./defaultProps";
11
+ import useDefaultProps from "../hooks/useDefaultProps";
12
+
13
+ import type { CheckboxProps } from "./Checkbox";
14
+
15
+ export interface CheckboxGroupProps<T extends CheckboxGroupValue = CheckboxGroupValue>
16
+ extends TdCheckboxGroupProps<T>,
17
+ StyledProps {
18
+ children?: React.ReactNode;
19
+ }
20
+
21
+ // 将 checkBox 的 value 转换为 string|number
22
+ const getCheckboxValue = (v: CheckboxOption): string | number => {
23
+ switch (typeof v) {
24
+ case "number":
25
+ return v as number;
26
+ case "string":
27
+ return v as string;
28
+ case "object": {
29
+ const vs = v as CheckboxOptionObj;
30
+ return vs.value;
31
+ }
32
+ default:
33
+ return undefined;
34
+ }
35
+ };
36
+
37
+ /**
38
+ * 多选选项组,里面可以嵌套 <Checkbox />
39
+ */
40
+ const CheckboxGroup = <T extends CheckboxGroupValue = CheckboxGroupValue>(props: CheckboxGroupProps<T>) => {
41
+ type ItemType = T[number];
42
+ const { classPrefix } = useConfig();
43
+ const {
44
+ onChange,
45
+ disabled,
46
+ className,
47
+ style,
48
+ children,
49
+ max,
50
+ options = []
51
+ } = useDefaultProps<CheckboxGroupProps<T>>(props, checkboxGroupDefaultProps as Partial<CheckboxGroupProps<T>>);
52
+
53
+ // 去掉所有 checkAll 之后的 options
54
+ const intervalOptions =
55
+ Array.isArray(options) && options.length > 0
56
+ ? options
57
+ : React.Children.map(
58
+ children,
59
+ (child: ReactElement<CheckboxProps>) =>
60
+ typeof child.type === "function" &&
61
+ "displayName" in child.type &&
62
+ child.type.displayName === "Checkbox" &&
63
+ child.props
64
+ ) || [];
65
+
66
+ const optionsWithoutCheckAll = intervalOptions.filter((t) => typeof t !== "object" || !t.checkAll);
67
+ const optionsWithoutCheckAllValues = [];
68
+ optionsWithoutCheckAll.forEach((v: string | number) => {
69
+ const vs = getCheckboxValue(v);
70
+ optionsWithoutCheckAllValues.push(vs);
71
+ });
72
+
73
+ const [internalValue, setInternalValue] = useControlled(props, "value", onChange);
74
+ const [localMax, setLocalMax] = useState(max);
75
+
76
+ const getCheckedSet = useCallback(() => {
77
+ if (!Array.isArray(internalValue)) {
78
+ return new Set<ItemType>([]);
79
+ }
80
+ return new Set<ItemType>([].concat(internalValue));
81
+ }, [internalValue]);
82
+ const checkedSet = useMemo(() => getCheckedSet(), [getCheckedSet]);
83
+
84
+ // 用于决定全选状态的属性
85
+ const indeterminate = useMemo(() => {
86
+ const list = Array.from(checkedSet);
87
+ return list.length !== 0 && list.length !== optionsWithoutCheckAll.length;
88
+ }, [checkedSet, optionsWithoutCheckAll]);
89
+
90
+ const checkAllChecked = useMemo(() => {
91
+ const list = Array.from(checkedSet);
92
+ return list.length === optionsWithoutCheckAll.length;
93
+ }, [checkedSet, optionsWithoutCheckAll]);
94
+
95
+ useEffect(() => {
96
+ if (!isNumber(max)) {
97
+ return;
98
+ }
99
+ if (max < checkedSet.size) {
100
+ console.warn("[TDesign] max should be less than the length of value, change is invalid");
101
+ } else {
102
+ // Use setTimeout to avoid calling setState synchronously within an effect
103
+ setTimeout(() => {
104
+ setLocalMax(max);
105
+ }, 0);
106
+ }
107
+ }, [max, checkedSet]);
108
+
109
+ const context: CheckContextValue = {
110
+ inject: (
111
+ checkProps: CheckProps & {
112
+ // check 组件不关心 value 的类型,只关心是否存在,所以为了兼容 checkbox group 的类型
113
+ // 此处覆盖 checkbox 默认 value 的类型,使用 checkbox group 的 generic type 代替
114
+ value: ItemType;
115
+ }
116
+ ) => {
117
+ // 如果已经受控,则不注入
118
+ if (typeof checkProps.checked !== "undefined") {
119
+ return checkProps;
120
+ }
121
+
122
+ const { value: checkValue } = checkProps;
123
+
124
+ return {
125
+ ...checkProps,
126
+ checked: checkProps.checkAll ? checkAllChecked : checkedSet.has(checkValue),
127
+ indeterminate: checkProps.checkAll ? indeterminate : checkProps.indeterminate,
128
+ disabled: checkProps.disabled || disabled || (checkedSet.size >= localMax && !checkedSet.has(checkValue)),
129
+ onChange(checked, { e }) {
130
+ if (typeof checkProps.onChange === "function") {
131
+ checkProps.onChange(checked, { e });
132
+ }
133
+
134
+ const checkedSet = getCheckedSet();
135
+ // 全选时的逻辑处理
136
+ if (checkProps.checkAll) {
137
+ checkedSet.clear();
138
+ if (checked) {
139
+ optionsWithoutCheckAllValues.forEach((v) => {
140
+ checkedSet.add(v);
141
+ });
142
+ }
143
+ } else if (checked) {
144
+ if (checkedSet.size >= localMax && isNumber(max)) return;
145
+ checkedSet.add(checkValue);
146
+ } else {
147
+ checkedSet.delete(checkValue);
148
+ }
149
+
150
+ const currentOptionChecked = optionsWithoutCheckAll.find((item: CheckboxProps) => item.value === checkValue);
151
+
152
+ // 此处 `as` 是因为 `Array.from` 会导致 `checkSet` 的 generic type 丢失
153
+ setInternalValue(Array.from(checkedSet) as T, {
154
+ e,
155
+ current: checkProps.checkAll ? undefined : (checkValue as TdCheckboxProps),
156
+ type: checked ? "check" : "uncheck",
157
+ option: checkProps.checkAll ? undefined : currentOptionChecked
158
+ });
159
+ }
160
+ };
161
+ }
162
+ };
163
+
164
+ // options 和 children 的抉择,在未明确说明时,暂时以 options 优先
165
+ const useOptions = Array.isArray(options) && options.length !== 0;
166
+
167
+ return (
168
+ <div className={classNames(`${classPrefix}-checkbox-group`, className)} style={style}>
169
+ <CheckContext.Provider value={context}>
170
+ {useOptions
171
+ ? options.map((v: CheckboxOption, index) => {
172
+ switch (typeof v) {
173
+ case "string":
174
+ return (
175
+ <Checkbox key={index} label={v} value={v}>
176
+ {v}
177
+ </Checkbox>
178
+ );
179
+ case "number": {
180
+ return (
181
+ <Checkbox key={index} label={v} value={v}>
182
+ {String(v)}
183
+ </Checkbox>
184
+ );
185
+ }
186
+ case "object": {
187
+ const vs = v as CheckboxOptionObj;
188
+ // CheckAll 的 checkBox 不存在 value,故用 checkAll_index 来保证尽量不和用户的 value 冲突.
189
+ return vs.checkAll ? (
190
+ <Checkbox {...vs} key={`checkAll_${index}`} indeterminate={indeterminate} />
191
+ ) : (
192
+ <Checkbox {...vs} key={index} disabled={vs.disabled || disabled} />
193
+ );
194
+ }
195
+ default:
196
+ return null;
197
+ }
198
+ })
199
+ : children}
200
+ </CheckContext.Provider>
201
+ </div>
202
+ );
203
+ };
204
+
205
+ CheckboxGroup.displayName = "CheckboxGroup";
206
+
207
+ export default CheckboxGroup;
@@ -0,0 +1,14 @@
1
+ import { CheckboxGroupValue, TdCheckboxProps, TdCheckboxGroupProps } from "./type";
2
+
3
+ export const checkboxDefaultProps: TdCheckboxProps = {
4
+ checkAll: false,
5
+ defaultChecked: false,
6
+ disabled: undefined,
7
+ indeterminate: false,
8
+ readonly: false
9
+ };
10
+
11
+ export const checkboxGroupDefaultProps: Partial<TdCheckboxGroupProps> = {
12
+ max: undefined,
13
+ defaultValue: [] as CheckboxGroupValue
14
+ };
@@ -0,0 +1,10 @@
1
+ import _Checkbox from "./Checkbox";
2
+
3
+ import "./style/index.js";
4
+
5
+ export type { CheckboxProps } from "./Checkbox";
6
+ export type { CheckboxGroupProps } from "./CheckboxGroup";
7
+ export * from "./type";
8
+
9
+ export const Checkbox = _Checkbox;
10
+ export default Checkbox;
@@ -0,0 +1 @@
1
+ import "./index.css";
@@ -0,0 +1 @@
1
+ import "../../styles/components/checkbox/_index.scss";
@@ -0,0 +1,117 @@
1
+ import { TNode } from "../common";
2
+ import { MouseEvent, ChangeEvent } from "react";
3
+
4
+ export interface TdCheckboxProps {
5
+ /**
6
+ * 用于标识是否为「全选选项」。单独使用无效,需在 CheckboxGroup 中使用
7
+ * @default false
8
+ */
9
+ checkAll?: boolean;
10
+ /**
11
+ * 是否选中
12
+ * @default false
13
+ */
14
+ checked?: boolean;
15
+ /**
16
+ * 是否选中,非受控属性
17
+ * @default false
18
+ */
19
+ defaultChecked?: boolean;
20
+ /**
21
+ * 多选框内容,同 label
22
+ */
23
+ children?: TNode;
24
+ /**
25
+ * 是否禁用组件。如果父组件存在 CheckboxGroup,默认值由 CheckboxGroup.disabled 控制。Checkbox.disabled 优先级高于 CheckboxGroup.disabled
26
+ */
27
+ disabled?: boolean;
28
+ /**
29
+ * 是否为半选
30
+ * @default false
31
+ */
32
+ indeterminate?: boolean;
33
+ /**
34
+ * 主文案
35
+ */
36
+ label?: TNode;
37
+ /**
38
+ * HTML 元素原生属性
39
+ * @default ''
40
+ */
41
+ name?: string;
42
+ /**
43
+ * 只读状态
44
+ * @default false
45
+ */
46
+ readonly?: boolean;
47
+ /**
48
+ * HTML 原生属性
49
+ * @default ''
50
+ */
51
+ title?: string;
52
+ /**
53
+ * 多选框的值
54
+ */
55
+ value?: string | number | boolean;
56
+ /**
57
+ * 值变化时触发
58
+ */
59
+ onChange?: (checked: boolean, context: { e: ChangeEvent<HTMLInputElement> }) => void;
60
+ /**
61
+ * 点击时触发,一般用于外层阻止冒泡场景
62
+ */
63
+ onClick?: (context: { e: MouseEvent<HTMLLabelElement> }) => void;
64
+ }
65
+
66
+ export interface TdCheckboxGroupProps<T = CheckboxGroupValue> {
67
+ /**
68
+ * 是否禁用组件,默认为 false。CheckboxGroup.disabled 优先级低于 Checkbox.disabled
69
+ */
70
+ disabled?: boolean;
71
+ /**
72
+ * 支持最多选中的数量
73
+ */
74
+ max?: number;
75
+ /**
76
+ * 统一设置内部复选框 HTML 属性
77
+ * @default ''
78
+ */
79
+ name?: string;
80
+ /**
81
+ * 以配置形式设置子元素。示例1:`['北京', '上海']` ,示例2: `[{ label: '全选', checkAll: true }, { label: '上海', value: 'shanghai' }]`。checkAll 值为 true 表示当前选项为「全选选项」
82
+ */
83
+ options?: Array<CheckboxOption>;
84
+ /**
85
+ * 选中值
86
+ * @default []
87
+ */
88
+ value?: T;
89
+ /**
90
+ * 选中值,非受控属性
91
+ * @default []
92
+ */
93
+ defaultValue?: T;
94
+ /**
95
+ * 值变化时触发,`context.current` 表示当前变化的数据值,如果是全选则为空;`context.type` 表示引起选中数据变化的是选中或是取消选中;`context.option` 表示当前变化的数据项
96
+ */
97
+ onChange?: (value: T, context: CheckboxGroupChangeContext) => void;
98
+ }
99
+
100
+ export type CheckboxOption = string | number | CheckboxOptionObj;
101
+
102
+ export interface CheckboxOptionObj {
103
+ label?: string | TNode;
104
+ value?: string | number;
105
+ disabled?: boolean;
106
+ name?: string;
107
+ checkAll?: true;
108
+ }
109
+
110
+ export type CheckboxGroupValue = Array<string | number | boolean>;
111
+
112
+ export interface CheckboxGroupChangeContext {
113
+ e: ChangeEvent<HTMLDivElement>;
114
+ current: CheckboxOption | TdCheckboxProps;
115
+ option: CheckboxOption | TdCheckboxProps;
116
+ type: "check" | "uncheck";
117
+ }
@@ -0,0 +1,131 @@
1
+ import React, { forwardRef, useContext, MouseEvent, ChangeEvent } from "react";
2
+ import classNames from "classnames";
3
+ import { isBoolean } from "lodash-es";
4
+ import { omit } from "../utils/helper";
5
+ import { StyledProps } from "../common";
6
+ import useConfig from "../hooks/useConfig";
7
+ import useControlled from "../hooks/useControlled";
8
+ import { TdCheckboxProps } from "../checkbox/type";
9
+
10
+ export interface CheckProps extends TdCheckboxProps, StyledProps {
11
+ type: "radio" | "radio-button" | "checkbox";
12
+ allowUncheck?: boolean;
13
+ title?: string;
14
+ children?: React.ReactNode;
15
+ stopLabelTrigger?: boolean;
16
+ }
17
+
18
+ /**
19
+ * Check 组件支持使用 CheckContext 进行状态托管
20
+ */
21
+ export const CheckContext = React.createContext<CheckContextValue>(null);
22
+
23
+ /**
24
+ * 托管 Check 组件的状态,请提供 inject() 方法注入托管好的 props
25
+ */
26
+ export interface CheckContextValue {
27
+ inject: (props: CheckProps) => CheckProps;
28
+ }
29
+
30
+ const Check = forwardRef<HTMLLabelElement, CheckProps>((_props, ref) => {
31
+ // 支持从 Context 注入
32
+ const context = useContext(CheckContext);
33
+ const props = context ? context.inject(_props) : _props;
34
+
35
+ const {
36
+ allowUncheck = false,
37
+ type,
38
+ disabled,
39
+ name,
40
+ value,
41
+ onChange,
42
+ indeterminate,
43
+ children,
44
+ label,
45
+ className,
46
+ style,
47
+ readonly,
48
+ onClick,
49
+ ...htmlProps
50
+ } = props;
51
+
52
+ const { classPrefix } = useConfig();
53
+
54
+ const TOnChange: (
55
+ checked: boolean,
56
+ context: { e: ChangeEvent<HTMLInputElement> | MouseEvent<HTMLInputElement> }
57
+ ) => void = onChange;
58
+
59
+ const [internalChecked, setInternalChecked] = useControlled(props, "checked", TOnChange);
60
+
61
+ const labelClassName = classNames(`${classPrefix}-${type}`, className, {
62
+ [`${classPrefix}-is-checked`]: internalChecked,
63
+ [`${classPrefix}-is-disabled`]: disabled,
64
+ [`${classPrefix}-is-indeterminate`]: indeterminate
65
+ });
66
+
67
+ const isDisabled = disabled || readonly;
68
+
69
+ const input = (
70
+ <input
71
+ readOnly={readonly}
72
+ type={type === "radio-button" ? "radio" : type}
73
+ className={`${classPrefix}-${type}__former`}
74
+ checked={internalChecked}
75
+ disabled={disabled}
76
+ name={name}
77
+ tabIndex={-1}
78
+ value={isBoolean(value) ? Number(value) : value}
79
+ data-value={typeof value === "string" ? `'${value}'` : value}
80
+ data-allow-uncheck={allowUncheck || undefined}
81
+ onClick={(e) => {
82
+ e.stopPropagation();
83
+ if ((type === "radio-button" || type === "radio") && allowUncheck && internalChecked) {
84
+ setInternalChecked(!e.currentTarget.checked, { e });
85
+ }
86
+ }}
87
+ onChange={(e) => {
88
+ if (isDisabled) return;
89
+ setInternalChecked(e.currentTarget.checked, { e });
90
+ }}
91
+ />
92
+ );
93
+ // Checkbox/ Radio 内容为空则不再渲染 span,不存在 0:Number 的情况
94
+ const showLabel = !!(children || label);
95
+
96
+ const handleLabelClick = (event: MouseEvent<HTMLSpanElement>) => {
97
+ // 在tree等组件中使用 阻止label触发checked 与expand冲突
98
+ if (props.stopLabelTrigger) {
99
+ event.preventDefault();
100
+ }
101
+ };
102
+
103
+ const onInnerClick = (e: MouseEvent<HTMLLabelElement>) => {
104
+ if (isDisabled) return;
105
+ onClick?.({ e });
106
+ };
107
+
108
+ return (
109
+ <label
110
+ ref={ref}
111
+ tabIndex={disabled ? undefined : 0}
112
+ className={labelClassName}
113
+ title={props.title}
114
+ style={style}
115
+ {...omit(htmlProps, ["checkAll", "stopLabelTrigger"])}
116
+ onClick={onInnerClick}
117
+ >
118
+ {input}
119
+ <span className={`${classPrefix}-${type}__input`} />
120
+ {showLabel && (
121
+ <span key="label" className={`${classPrefix}-${type}__label`} onClick={handleLabelClick}>
122
+ {children || label}
123
+ </span>
124
+ )}
125
+ </label>
126
+ );
127
+ });
128
+
129
+ Check.displayName = "Check";
130
+
131
+ export default Check;
@@ -0,0 +1,36 @@
1
+ import React from "react";
2
+ import classNames from "classnames";
3
+ import { ClassName } from "../common";
4
+
5
+ import useConfig from "../hooks/useConfig";
6
+
7
+ // 翻转箭头统一组件
8
+ function FakeArrow(props: {
9
+ className?: ClassName;
10
+ isActive?: boolean;
11
+ disabled?: boolean;
12
+ style?: React.CSSProperties;
13
+ }) {
14
+ const { classPrefix } = useConfig();
15
+ return (
16
+ <svg
17
+ style={props.style}
18
+ className={classNames(
19
+ `${classPrefix}-fake-arrow`,
20
+ {
21
+ [`${classPrefix}-fake-arrow--active`]: props?.isActive && !props?.disabled
22
+ },
23
+ props?.className
24
+ )}
25
+ width="16"
26
+ height="16"
27
+ viewBox="0 0 16 16"
28
+ fill="none"
29
+ xmlns="http://www.w3.org/2000/svg"
30
+ >
31
+ <path d="M3.75 5.7998L7.99274 10.0425L12.2361 5.79921" stroke="black" strokeOpacity="0.9" strokeWidth="1.3" />
32
+ </svg>
33
+ );
34
+ }
35
+
36
+ export default FakeArrow;
@@ -0,0 +1,21 @@
1
+ import React from "react";
2
+ import ConfigProvider, { ConfigProviderProps } from "../config-provider";
3
+
4
+ /**
5
+ *
6
+ * @param notSet 默认不传设置为true,函数是调用唤起组件不需要同步设置全局上下文信息
7
+ * @returns
8
+ */
9
+ const PluginContainer: React.FC<ConfigProviderProps> = (props) => (
10
+ <>
11
+ {props?.globalConfig?.isContextEffectPlugin ? (
12
+ <ConfigProvider notSet {...props}>
13
+ {props?.children}
14
+ </ConfigProvider>
15
+ ) : (
16
+ props?.children
17
+ )}
18
+ </>
19
+ );
20
+
21
+ export default PluginContainer;