@fjyueke/arco-mcp 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 (191) hide show
  1. package/README.md +85 -0
  2. package/dist/index.d.ts +1 -0
  3. package/dist/index.js +244 -0
  4. package/metaData/components/action-sheet/doc.md +102 -0
  5. package/metaData/components/action-sheet/example/index.tsx +199 -0
  6. package/metaData/components/action-sheet/example/methods.tsx +6 -0
  7. package/metaData/components/avatar/doc.md +57 -0
  8. package/metaData/components/avatar/example/group.tsx +69 -0
  9. package/metaData/components/avatar/example/index.tsx +196 -0
  10. package/metaData/components/badge/doc.md +28 -0
  11. package/metaData/components/badge/example/index.tsx +128 -0
  12. package/metaData/components/button/doc.md +45 -0
  13. package/metaData/components/button/example/index.tsx +264 -0
  14. package/metaData/components/carousel/doc.md +89 -0
  15. package/metaData/components/carousel/example/index.tsx +1481 -0
  16. package/metaData/components/cell/doc.md +68 -0
  17. package/metaData/components/cell/example/arrow.tsx +13 -0
  18. package/metaData/components/cell/example/cell.tsx +71 -0
  19. package/metaData/components/cell/example/group.tsx +63 -0
  20. package/metaData/components/cell/example/index.tsx +19 -0
  21. package/metaData/components/checkbox/doc.md +91 -0
  22. package/metaData/components/checkbox/example/checkbox.tsx +158 -0
  23. package/metaData/components/checkbox/example/group.tsx +103 -0
  24. package/metaData/components/checkbox/example/index.tsx +25 -0
  25. package/metaData/components/circle-progress/doc.md +40 -0
  26. package/metaData/components/circle-progress/example/index.tsx +323 -0
  27. package/metaData/components/collapse/doc.md +74 -0
  28. package/metaData/components/collapse/example/collapse.tsx +183 -0
  29. package/metaData/components/collapse/example/group.tsx +136 -0
  30. package/metaData/components/collapse/example/index.tsx +16 -0
  31. package/metaData/components/collapse/example/utils.tsx +20 -0
  32. package/metaData/components/context-provider/doc.md +44 -0
  33. package/metaData/components/context-provider/example/index.tsx +208 -0
  34. package/metaData/components/count-down/doc.md +40 -0
  35. package/metaData/components/count-down/example/hooks.tsx +118 -0
  36. package/metaData/components/count-down/example/index.tsx +63 -0
  37. package/metaData/components/date-picker/doc.md +102 -0
  38. package/metaData/components/date-picker/example/index.tsx +545 -0
  39. package/metaData/components/dialog/doc.md +200 -0
  40. package/metaData/components/dialog/example/index.tsx +328 -0
  41. package/metaData/components/dialog/example/methods.tsx +56 -0
  42. package/metaData/components/divider/doc.md +25 -0
  43. package/metaData/components/divider/example/index.tsx +87 -0
  44. package/metaData/components/dropdown/doc.md +80 -0
  45. package/metaData/components/dropdown/example/dropdown.tsx +308 -0
  46. package/metaData/components/dropdown/example/index.tsx +19 -0
  47. package/metaData/components/dropdown/example/options.tsx +103 -0
  48. package/metaData/components/dropdown-menu/doc.md +91 -0
  49. package/metaData/components/dropdown-menu/example/dropdown-menu.tsx +270 -0
  50. package/metaData/components/dropdown-menu/example/index.tsx +16 -0
  51. package/metaData/components/ellipsis/doc.md +33 -0
  52. package/metaData/components/ellipsis/example/index.tsx +93 -0
  53. package/metaData/components/ellipsis/example/js-ellipsis.tsx +229 -0
  54. package/metaData/components/ellipsis/example/native-ellipsis.tsx +48 -0
  55. package/metaData/components/form/doc.md +90 -0
  56. package/metaData/components/form/example/form-item-context.tsx +8 -0
  57. package/metaData/components/form/example/form-item.tsx +378 -0
  58. package/metaData/components/form/example/index.tsx +87 -0
  59. package/metaData/components/form/example/linked-container.tsx +66 -0
  60. package/metaData/components/grid/doc.md +39 -0
  61. package/metaData/components/grid/example/index.tsx +252 -0
  62. package/metaData/components/icon/doc.md +15 -0
  63. package/metaData/components/icon/example/index.tsx +32 -0
  64. package/metaData/components/image/doc.md +62 -0
  65. package/metaData/components/image/example/index.tsx +528 -0
  66. package/metaData/components/image-picker/doc.md +116 -0
  67. package/metaData/components/image-picker/example/add-icon.tsx +14 -0
  68. package/metaData/components/image-picker/example/index.tsx +175 -0
  69. package/metaData/components/image-preview/doc.md +119 -0
  70. package/metaData/components/image-preview/example/index.tsx +1290 -0
  71. package/metaData/components/image-preview/example/methods.tsx +72 -0
  72. package/metaData/components/index-bar/doc.md +83 -0
  73. package/metaData/components/index-bar/example/group.tsx +93 -0
  74. package/metaData/components/index-bar/example/index.tsx +271 -0
  75. package/metaData/components/index-bar/example/side-bar.tsx +82 -0
  76. package/metaData/components/input/doc.md +60 -0
  77. package/metaData/components/input/example/hooks.tsx +259 -0
  78. package/metaData/components/input/example/index.tsx +134 -0
  79. package/metaData/components/input/example/props.tsx +183 -0
  80. package/metaData/components/keyboard/doc.md +73 -0
  81. package/metaData/components/keyboard/example/index.tsx +238 -0
  82. package/metaData/components/load-more/doc.md +48 -0
  83. package/metaData/components/load-more/example/index.tsx +395 -0
  84. package/metaData/components/loading/doc.md +34 -0
  85. package/metaData/components/loading/example/index.tsx +312 -0
  86. package/metaData/components/masking/doc.md +93 -0
  87. package/metaData/components/masking/example/index.tsx +410 -0
  88. package/metaData/components/masking/example/methods.tsx +88 -0
  89. package/metaData/components/nav-bar/doc.md +40 -0
  90. package/metaData/components/nav-bar/example/back-icon.tsx +28 -0
  91. package/metaData/components/nav-bar/example/index.tsx +305 -0
  92. package/metaData/components/notice-bar/doc.md +34 -0
  93. package/metaData/components/notice-bar/example/index.tsx +294 -0
  94. package/metaData/components/notify/doc.md +81 -0
  95. package/metaData/components/notify/example/index.tsx +171 -0
  96. package/metaData/components/notify/example/methods.tsx +83 -0
  97. package/metaData/components/pagination/doc.md +41 -0
  98. package/metaData/components/pagination/example/arrow.tsx +14 -0
  99. package/metaData/components/pagination/example/index.tsx +292 -0
  100. package/metaData/components/picker/doc.md +89 -0
  101. package/metaData/components/picker/example/index.tsx +217 -0
  102. package/metaData/components/picker-view/doc.md +57 -0
  103. package/metaData/components/picker-view/example/cascader.tsx +141 -0
  104. package/metaData/components/picker-view/example/index.tsx +297 -0
  105. package/metaData/components/picker-view/example/multi-picker.tsx +66 -0
  106. package/metaData/components/picker-view/example/picker-cell.tsx +416 -0
  107. package/metaData/components/popover/doc.md +144 -0
  108. package/metaData/components/popover/example/index.tsx +23 -0
  109. package/metaData/components/popover/example/menu.tsx +145 -0
  110. package/metaData/components/popover/example/popover-inner.tsx +147 -0
  111. package/metaData/components/popover/example/popover.tsx +268 -0
  112. package/metaData/components/popup/doc.md +101 -0
  113. package/metaData/components/popup/example/index.tsx +125 -0
  114. package/metaData/components/popup/example/methods.tsx +6 -0
  115. package/metaData/components/popup-swiper/doc.md +108 -0
  116. package/metaData/components/popup-swiper/example/index.tsx +332 -0
  117. package/metaData/components/popup-swiper/example/methods.tsx +6 -0
  118. package/metaData/components/portal/doc.md +14 -0
  119. package/metaData/components/portal/example/index.tsx +35 -0
  120. package/metaData/components/progress/doc.md +42 -0
  121. package/metaData/components/progress/example/index.tsx +256 -0
  122. package/metaData/components/pull-refresh/doc.md +37 -0
  123. package/metaData/components/pull-refresh/example/android-pull-refresh.tsx +260 -0
  124. package/metaData/components/pull-refresh/example/index.tsx +44 -0
  125. package/metaData/components/pull-refresh/example/ios-pull-refresh.tsx +216 -0
  126. package/metaData/components/radio/doc.md +89 -0
  127. package/metaData/components/radio/example/group.tsx +92 -0
  128. package/metaData/components/radio/example/index.tsx +25 -0
  129. package/metaData/components/radio/example/radio.tsx +22 -0
  130. package/metaData/components/rate/doc.md +33 -0
  131. package/metaData/components/rate/example/index.tsx +281 -0
  132. package/metaData/components/search-bar/doc.md +97 -0
  133. package/metaData/components/search-bar/example/association.tsx +89 -0
  134. package/metaData/components/search-bar/example/cancel-button.tsx +34 -0
  135. package/metaData/components/search-bar/example/highlight.tsx +66 -0
  136. package/metaData/components/search-bar/example/index.tsx +251 -0
  137. package/metaData/components/show-monitor/doc.md +34 -0
  138. package/metaData/components/show-monitor/example/index.tsx +646 -0
  139. package/metaData/components/skeleton/doc.md +101 -0
  140. package/metaData/components/skeleton/example/elements.tsx +290 -0
  141. package/metaData/components/skeleton/example/index.tsx +96 -0
  142. package/metaData/components/skeleton/example/skeleton-context.tsx +7 -0
  143. package/metaData/components/slider/doc.md +42 -0
  144. package/metaData/components/slider/example/index.tsx +326 -0
  145. package/metaData/components/slider/example/marks.tsx +50 -0
  146. package/metaData/components/slider/example/popover.tsx +38 -0
  147. package/metaData/components/slider/example/thumb.tsx +48 -0
  148. package/metaData/components/slider/example/useSliderIcon.tsx +38 -0
  149. package/metaData/components/slider/example/useSliderInit.tsx +170 -0
  150. package/metaData/components/stepper/doc.md +46 -0
  151. package/metaData/components/stepper/example/index.tsx +166 -0
  152. package/metaData/components/stepper/example/useButtonClick.tsx +74 -0
  153. package/metaData/components/stepper/example/useInputEvent.tsx +65 -0
  154. package/metaData/components/stepper/example/useValue.tsx +43 -0
  155. package/metaData/components/steps/doc.md +57 -0
  156. package/metaData/components/steps/example/index.tsx +123 -0
  157. package/metaData/components/steps/example/step.tsx +126 -0
  158. package/metaData/components/sticky/doc.md +45 -0
  159. package/metaData/components/sticky/example/index.tsx +430 -0
  160. package/metaData/components/swipe-action/doc.md +44 -0
  161. package/metaData/components/swipe-action/example/index.tsx +338 -0
  162. package/metaData/components/swipe-action/example/item.tsx +40 -0
  163. package/metaData/components/swipe-load/doc.md +43 -0
  164. package/metaData/components/swipe-load/example/index.tsx +339 -0
  165. package/metaData/components/swipe-load/example/type.tsx +153 -0
  166. package/metaData/components/switch/doc.md +38 -0
  167. package/metaData/components/switch/example/index.tsx +237 -0
  168. package/metaData/components/tab-bar/doc.md +57 -0
  169. package/metaData/components/tab-bar/example/index.tsx +18 -0
  170. package/metaData/components/tab-bar/example/item.tsx +74 -0
  171. package/metaData/components/tab-bar/example/tab-bar.tsx +105 -0
  172. package/metaData/components/tabs/doc.md +187 -0
  173. package/metaData/components/tabs/example/index.tsx +514 -0
  174. package/metaData/components/tabs/example/tab-cell-underline.tsx +320 -0
  175. package/metaData/components/tabs/example/tab-cell.tsx +433 -0
  176. package/metaData/components/tabs/example/tab-pane.tsx +349 -0
  177. package/metaData/components/tag/doc.md +80 -0
  178. package/metaData/components/tag/example/index.tsx +23 -0
  179. package/metaData/components/tag/example/list.tsx +97 -0
  180. package/metaData/components/tag/example/tag.tsx +85 -0
  181. package/metaData/components/textarea/doc.md +67 -0
  182. package/metaData/components/textarea/example/index.tsx +258 -0
  183. package/metaData/components/toast/doc.md +90 -0
  184. package/metaData/components/toast/example/index.tsx +335 -0
  185. package/metaData/components/toast/example/methods.tsx +87 -0
  186. package/metaData/components/transition/doc.md +19 -0
  187. package/metaData/components/transition/example/index.tsx +113 -0
  188. package/metaData/components/uploader/doc.md +82 -0
  189. package/metaData/components/uploader/example/index.tsx +208 -0
  190. package/metaData/components-index.json +242 -0
  191. package/package.json +56 -0
@@ -0,0 +1,87 @@
1
+ import React, { useRef, forwardRef, Ref, useImperativeHandle, useContext } from 'react';
2
+ import { cls } from '@arco-design/mobile-utils';
3
+ import { ContextLayout, GlobalContext } from '../context-provider';
4
+ import { BaseProps, getStyleWithVendor } from '../_helpers';
5
+
6
+ export interface DividerProps extends BaseProps {
7
+ /**
8
+ * 是否使用 0.5px 线
9
+ * @en Whether to use hairline
10
+ * @default true
11
+ */
12
+ hairline?: boolean;
13
+ /**
14
+ * 文字
15
+ * @en Divider text
16
+ */
17
+ content?: React.ReactNode;
18
+ /**
19
+ * 文字位置
20
+ * @en The position of text inside divider
21
+ * @default 'center'
22
+ */
23
+ align?: 'left' | 'center' | 'right';
24
+ /**
25
+ * 分割线宽度(单边),仅在`align`为`center`时生效
26
+ * @en The width of divider, only take effect when `align` is `center`.
27
+ */
28
+ width?: string | number;
29
+ /**
30
+ * 子元素,同 content,优先级低于 content
31
+ * @en Children element, same as `content`, with the priority lower than `content`
32
+ * @default null
33
+ */
34
+ children?: React.ReactNode;
35
+ }
36
+
37
+ export interface DividerRef {
38
+ /**
39
+ * 最外层 DOM 元素
40
+ * @en The outer DOM element of the component
41
+ */
42
+ dom: HTMLDivElement | null;
43
+ }
44
+
45
+ /**
46
+ * 划分内容的装饰线
47
+ * @en A partitioning line element
48
+ * @type 布局
49
+ * @type_en Layout
50
+ * @name 分割线
51
+ * @name_en Divider
52
+ */
53
+ const Divider = forwardRef((props: DividerProps, ref: Ref<DividerRef>) => {
54
+ const { className = '', style, hairline = true, align = 'center', width } = props;
55
+ const domRef = useRef<HTMLDivElement | null>(null);
56
+ const { prefixCls } = useContext(GlobalContext);
57
+ const prefix = `${prefixCls}-divider`;
58
+ const children = props.content || props.children;
59
+ const hasCustomWidth = children && width !== undefined && align === 'center';
60
+
61
+ useImperativeHandle(ref, () => ({
62
+ dom: domRef.current,
63
+ }));
64
+
65
+ return (
66
+ <ContextLayout>
67
+ {() => (
68
+ <div
69
+ className={cls(prefix, className, {
70
+ [`${prefix}--hairline`]: hairline,
71
+ [`${prefix}--with-text ${prefix}--text-${align}`]: children,
72
+ [`${prefix}--with-width`]: hasCustomWidth,
73
+ })}
74
+ style={{
75
+ ...style,
76
+ ...(hasCustomWidth ? getStyleWithVendor({ flexBasis: width }) : {}),
77
+ }}
78
+ ref={domRef}
79
+ >
80
+ {children}
81
+ </div>
82
+ )}
83
+ </ContextLayout>
84
+ );
85
+ });
86
+
87
+ export default Divider;
@@ -0,0 +1,80 @@
1
+ ### 导航
2
+
3
+ # 下拉面板 Dropdown
4
+
5
+ 下拉面板,展示供选择的选择项
6
+
7
+ ======
8
+
9
+ > 属性/Props
10
+
11
+ |参数|描述|类型|默认值|
12
+ |----------|-------------|------|------|
13
+ |showDropdown|是否展开下拉框|boolean|必填|
14
+ |options|展示的选项,优先级低于dropdownNode|OptionsItem\[\]|[]|
15
+ |children|自定义下拉框元素|ReactNode|-|
16
+ |onCancel|取消选择|() =\> void|必填|
17
+ |className|自定义类名|string|-|
18
+ |style|自定义样式|CSSProperties|-|
19
+ |extraNode|下拉框底部元素|ReactNode|-|
20
+ |top|定位下拉框出现的位置|number|-|
21
+ |bottom|向上展开时下拉框出现的位置|number|-|
22
+ |direction|展开方向|"up" \| "down"|"down"|
23
+ |height|受控模式,下拉框高度|number|-|
24
+ |maxHeight|下拉框最大的高度|number|500|
25
+ |clickOtherToClose|点击其他区域是否取消选择|boolean|true|
26
+ |touchToClose|是否在触发touchstart时就取消选择,否则在click之后再取消选择|boolean|true|
27
+ |dropdownAnimationTimeout|展开收起动画的时间|number|300|
28
+ |dropdownAnimationFunction|展开收起动画曲线函数|string|"cubic-bezier(0.32, 0.96, 0.6, 1)"|
29
+ |showMask|是否展示遮罩层|boolean|true|
30
+ |maskAnimationTimeout|蒙层动画时长|number|500|
31
+ |maskAnimationFunction|蒙层动画函数|string|"cubic-bezier(0.32, 0.96, 0.6, 1)"|
32
+ |useColumn|使用多列标签样式,传 true 为 4 列,传数字为指定 n 列|number \| boolean|false|
33
+ |optionIcon|选项附带图标|ReactNode|-|
34
+ |mountOnEnter|是否在打开下拉框时再加载内容|boolean|true|
35
+ |unmountOnExit|是否在退出时卸载内容|boolean|true|
36
+ |preventBodyScroll|弹窗打开时是否禁止body的滚动|boolean|true|
37
+ |initialBodyOverflow|页面初始 overflow 状态,即关闭弹窗时 overflow 应该还原的状态|string|第一个全屏组件(弹窗、toast等)打开时页面overflow值|
38
+ |getAnchorElement|用于定位的元素,优先级低于top/bottom|() =\> HTMLElement|当前DOM所在位置的父元素|
39
+ |isStopTouchEl|点击某元素时是否阻止关闭面板, 优先级高于\`getStopTouchElement\`,当 clickOtherToClose=true 时有效|(el: HTMLElement) =\> boolean|-|
40
+ |getStopTouchElements|可阻止关闭面板的元素,当 clickOtherToClose=true 时有效|() =\> HTMLElement\[\]|当前组件的父元素|
41
+ |getScrollContainer|内容内部滚动区域容器,在该容器中未滚动到顶部或底部时会释放滚动|() =\> HTMLElement \| HTMLElement\[\]|-|
42
+ |getPortalContainer|获取挂载容器|() =\> HTMLElement|-|
43
+ |multiple|是否支持多选|boolean|false|
44
+ |defaultSelectedValue|默认选中值|string \| number \| ReactText\[\]|-|
45
+ |selectedValue|当前选择的选项标识|string \| number \| ReactText\[\]|-|
46
+ |onOptionClick|点击选项时触发的回调函数|((val: ReactText, op: OptionsItem) =\> void) \| ((selected: boolean, val: ReactText, op: OptionsItem) =\> void)|-|
47
+ |onOptionChange|当选项改变时触发的回调函数|((val: ReactText, op: OptionsItem) =\> void) \| ((vals: ReactText\[\], op: OptionsItem) =\> void)|-|
48
+
49
+ > 引用/Refs
50
+
51
+ |参数|描述|类型|
52
+ |----------|-------------|------|
53
+ |dom|最外层元素 DOM|HTMLDivElement|
54
+
55
+ > Dropdown.Options
56
+
57
+ |参数|描述|类型|默认值|
58
+ |----------|-------------|------|------|
59
+ |options|展示的选项列表|OptionsItem\[\]|必填|
60
+ |useColumn|使用多列标签样式,传 true 为 4 列,传数字为指定 n 列|number \| boolean|false|
61
+ |icon|选项列表右侧图标|ReactNode|-|
62
+ |multiple|是否支持多选|boolean|false|
63
+ |defaultSelectedValue|默认选中值|string \| number \| ReactText\[\]|-|
64
+ |selectedValue|当前选择的选项标识|string \| number \| ReactText\[\]|-|
65
+ |onOptionClick|点击选项时触发的回调函数|((val: ReactText, op: OptionsItem) =\> void) \| ((selected: boolean, val: ReactText, op: OptionsItem) =\> void)|-|
66
+ |onOptionChange|当选项改变时触发的回调函数|((val: ReactText, op: OptionsItem) =\> void) \| ((vals: ReactText\[\], op: OptionsItem) =\> void)|-|
67
+
68
+ > OptionsItem
69
+
70
+ |参数|描述|类型|
71
+ |----------|-------------|------|
72
+ |label|选项名称|ReactNode|
73
+ |value|选项标识|ReactText|
74
+ |disabled|选项是否可用,默认false表示可用|boolean|
75
+
76
+ > DropdownOptionsRef
77
+
78
+ |参数|描述|类型|
79
+ |----------|-------------|------|
80
+ |dom|最外层元素 DOM|HTMLDivElement|
@@ -0,0 +1,308 @@
1
+ import React, {
2
+ useRef,
3
+ useEffect,
4
+ useCallback,
5
+ forwardRef,
6
+ Ref,
7
+ useImperativeHandle,
8
+ useState,
9
+ CSSProperties,
10
+ } from 'react';
11
+ import { cls, nextTick } from '@arco-design/mobile-utils';
12
+ import { CSSTransition } from 'react-transition-group';
13
+ import { ContextLayout } from '../context-provider';
14
+ import {
15
+ getStyleWithVendor,
16
+ usePopupScroll,
17
+ usePreventBodyScroll,
18
+ useSystem,
19
+ useWindowSize,
20
+ } from '../_helpers';
21
+ import Portal from '../portal';
22
+ import {
23
+ DropdownOptionsProps,
24
+ DropdownOptionsRef,
25
+ DropdownProps,
26
+ DropdownRef,
27
+ ValueType,
28
+ } from './type';
29
+
30
+ export function componentGenerator<
31
+ P extends DropdownOptionsProps = DropdownOptionsProps,
32
+ R extends DropdownOptionsRef = DropdownOptionsRef,
33
+ >(Comp: React.ForwardRefExoticComponent<P & React.RefAttributes<R>>) {
34
+ return forwardRef((props: DropdownProps, ref: Ref<DropdownRef>) => {
35
+ const optionsWrapper = useRef<HTMLDivElement>(null);
36
+ const domRef = useRef<HTMLDivElement | null>(null);
37
+ const {
38
+ className = '',
39
+ style = {},
40
+ options = [],
41
+ showDropdown = false,
42
+ showMask = true,
43
+ direction = 'down',
44
+ height,
45
+ maxHeight = 500,
46
+ touchToClose = true,
47
+ useColumn,
48
+ optionIcon,
49
+ multiple,
50
+ defaultSelectedValue,
51
+ onCancel,
52
+ getAnchorElement,
53
+ isStopTouchEl,
54
+ getStopTouchElements,
55
+ getScrollContainer = () => optionsWrapper.current,
56
+ getPortalContainer,
57
+ clickOtherToClose = true,
58
+ dropdownAnimationTimeout = 300,
59
+ dropdownAnimationFunction = 'cubic-bezier(0.32, 0.96, 0.6, 1)',
60
+ // 坑:ios touchstart会停留300ms 导致点击关闭蒙层后出现点击穿透 触发底部可点区域事件 这里设置更长的timeout规避
61
+ // @en In iOS, touchstart will stay for 300ms, resulting in click penetration after clicking to close the mask, triggering the bottom clickable area event, and setting a longer timeout to avoid it
62
+ maskAnimationTimeout = 500,
63
+ maskAnimationFunction = 'cubic-bezier(0.32, 0.96, 0.6, 1)',
64
+ mountOnEnter = true,
65
+ unmountOnExit = true,
66
+ preventBodyScroll = true,
67
+ initialBodyOverflow,
68
+ } = props;
69
+ const system = useSystem();
70
+ const { windowHeight, windowWidth } = useWindowSize();
71
+ const [positionStyle, setPositionStyle] = useState({ top: '', bottom: '' });
72
+ const [innerValue, setInnerValue] = useState<ValueType>(() => {
73
+ const defaultValue = multiple ? [] : 0;
74
+ return defaultSelectedValue !== void 0 ? defaultSelectedValue : defaultValue;
75
+ });
76
+
77
+ // down=0为向上展开,1为向下展开
78
+ // @en down=0 is to expand upwards, 1 is to expand downwards
79
+ const [down, setDown] = useState<boolean | null>(null);
80
+ const maskHeight = useRef(0);
81
+ const optionsContainer = useRef<HTMLDivElement>(null);
82
+ const dropdownContainer = useRef<HTMLDivElement>(null);
83
+ const [optionsWrapperHeight, setOptionsWrapperHeight] = useState(0);
84
+
85
+ /**
86
+ * 判断是否阻止touch事件
87
+ * @en Determine whether to block touch events
88
+ * */
89
+
90
+ const ifStopTouch = (el: HTMLElement) => {
91
+ if (isStopTouchEl) return isStopTouchEl(el);
92
+ let tempEl: HTMLElement | null = el;
93
+ const elements = getStopTouchElements
94
+ ? getStopTouchElements()
95
+ : [dropdownContainer.current?.parentElement];
96
+ for (let i = 0; i < elements.length; i++) {
97
+ while (tempEl && elements[i]) {
98
+ if (tempEl === elements[i]) {
99
+ return true;
100
+ }
101
+ tempEl = tempEl.parentElement;
102
+ }
103
+ }
104
+ return false;
105
+ };
106
+
107
+ usePopupScroll(showDropdown, domRef.current, getScrollContainer);
108
+
109
+ usePreventBodyScroll(showDropdown, preventBodyScroll, initialBodyOverflow);
110
+
111
+ const updateDown = useCallback(
112
+ (wrapperHeight: number) => {
113
+ // 为0的时候不改变
114
+ // @en Does not change when it is 0
115
+ if (wrapperHeight === 0) {
116
+ return;
117
+ }
118
+ const tempEl = getAnchorElement
119
+ ? getAnchorElement()
120
+ : dropdownContainer.current?.parentElement;
121
+ const { bottom, top } = tempEl?.getBoundingClientRect() || {
122
+ bottom: 0,
123
+ top: 0,
124
+ };
125
+ const tempTop = props.top || bottom;
126
+ const tempBottom = props.bottom || windowHeight - top;
127
+ const tempMaskHeight = windowHeight - tempTop;
128
+ // 没有指定方向,且空间足够时,或向上展开的空间不够,向下展开
129
+ // @en If there is no specified direction and there is enough space, or there is not enough space to expand upward, expand downward
130
+ const tempDown =
131
+ (direction !== 'up' && wrapperHeight < tempMaskHeight) ||
132
+ wrapperHeight > tempTop;
133
+ if (tempDown) {
134
+ maskHeight.current = tempMaskHeight;
135
+ setPositionStyle({ top: `${tempTop}px`, bottom: '' });
136
+ } else {
137
+ maskHeight.current = windowHeight - tempBottom;
138
+ setPositionStyle({ top: '', bottom: `${tempBottom}px` });
139
+ }
140
+ setDown(tempDown);
141
+ },
142
+ [props.top, props.bottom, Boolean(getAnchorElement), direction, windowHeight],
143
+ );
144
+
145
+ /**
146
+ * 取消选择
147
+ * @en Cancel selection
148
+ * */
149
+ const handleCancel = useCallback(
150
+ e => {
151
+ const optionsWrapperDom = optionsWrapper.current;
152
+ if (!e || !optionsWrapperDom) {
153
+ return;
154
+ }
155
+ const target = e.target as HTMLElement;
156
+ if (!optionsWrapperDom.contains(target) && !ifStopTouch(target)) {
157
+ onCancel();
158
+ }
159
+ e.stopPropagation();
160
+ },
161
+ [ifStopTouch, onCancel],
162
+ );
163
+
164
+ /**
165
+ * 当面板放下时,给body添加事件监听,面板收起时,移除
166
+ * @en When the panel is down, add an event listener to the body, and when the panel is retracted, remove it
167
+ * */
168
+ useEffect(() => {
169
+ const needBind = showDropdown && clickOtherToClose;
170
+ const event = touchToClose ? 'touchstart' : 'click';
171
+ if (needBind) {
172
+ document.addEventListener(event, handleCancel);
173
+ }
174
+ return () => {
175
+ if (needBind) {
176
+ document.removeEventListener(event, handleCancel);
177
+ }
178
+ };
179
+ }, [showDropdown, clickOtherToClose, handleCancel]);
180
+
181
+ useEffect(() => {
182
+ let wrapperHeight = 0;
183
+ if (height !== void 0) {
184
+ // 受控模式下,完全交由外层控制
185
+ // @en In controlled mode, it is completely controlled by the outer layer
186
+ wrapperHeight = height;
187
+ } else {
188
+ if (!optionsContainer.current) return;
189
+ if (!showDropdown) {
190
+ wrapperHeight = 0;
191
+ } else {
192
+ wrapperHeight = optionsContainer.current.getBoundingClientRect().height;
193
+ }
194
+ }
195
+ updateDown(wrapperHeight);
196
+ nextTick(() => {
197
+ setOptionsWrapperHeight(wrapperHeight);
198
+ });
199
+ }, [showDropdown, options.length, height, updateDown]);
200
+
201
+ useImperativeHandle(ref, () => ({
202
+ dom: domRef.current,
203
+ }));
204
+
205
+ function getOptionsStyle() {
206
+ const trans = down ? 'translateY(-100%)' : 'translateY(100%)';
207
+ const transStyle: CSSProperties =
208
+ down === null
209
+ ? {
210
+ opacity: 0,
211
+ }
212
+ : {
213
+ opacity: 1,
214
+ transform: optionsWrapperHeight ? 'translateY(0)' : trans,
215
+ transition: `all ${dropdownAnimationTimeout}ms ${dropdownAnimationFunction}`,
216
+ };
217
+ return getStyleWithVendor({
218
+ // height: `${optionsWrapperHeight}px`,
219
+ ...transStyle,
220
+ overflow: optionsWrapperHeight > maxHeight || height ? 'auto' : 'hidden',
221
+ maxHeight: `${maxHeight}px`,
222
+ });
223
+ }
224
+
225
+ function renderInnerOptions() {
226
+ const value = props.selectedValue !== void 0 ? props.selectedValue : innerValue;
227
+ const optionProps = {
228
+ options,
229
+ useColumn,
230
+ icon: optionIcon,
231
+ selectedValue: value,
232
+ multiple: props.multiple,
233
+ onOptionClick: props.onOptionClick,
234
+ onOptionChange: (vals, op) => {
235
+ setInnerValue(vals);
236
+ props.onOptionChange?.(vals, op);
237
+ },
238
+ } as P;
239
+ return <Comp {...optionProps} />;
240
+ }
241
+
242
+ return (
243
+ <ContextLayout>
244
+ {({ prefixCls }) => (
245
+ <div ref={dropdownContainer}>
246
+ <Portal getContainer={getPortalContainer}>
247
+ <CSSTransition
248
+ in={showDropdown}
249
+ timeout={Math.max(dropdownAnimationTimeout, maskAnimationTimeout)}
250
+ onEnter={el => {
251
+ el.style.visibility = 'visible';
252
+ }}
253
+ onExited={el => {
254
+ el.style.visibility = 'hidden';
255
+ setDown(null);
256
+ }}
257
+ mountOnEnter={mountOnEnter}
258
+ unmountOnExit={unmountOnExit}
259
+ >
260
+ <div
261
+ className={cls(
262
+ `${prefixCls}-dropdown all-border-box`,
263
+ system,
264
+ className,
265
+ { 'drop-up': !down },
266
+ )}
267
+ ref={domRef}
268
+ style={{
269
+ height: `${maskHeight.current}px`,
270
+ maxWidth: `${windowWidth}px`,
271
+ ...style,
272
+ ...positionStyle,
273
+ }}
274
+ >
275
+ <div
276
+ className={cls(`${prefixCls}-dropdown-options`)}
277
+ ref={optionsWrapper}
278
+ style={getOptionsStyle()}
279
+ >
280
+ <div
281
+ className={cls(
282
+ `${prefixCls}-dropdown-options-container`,
283
+ )}
284
+ ref={optionsContainer}
285
+ >
286
+ {props.children || renderInnerOptions()}
287
+ {props.extraNode}
288
+ </div>
289
+ </div>
290
+ {showMask && (
291
+ <div
292
+ className={cls(`${prefixCls}-dropdown-mask`, {
293
+ 'is-show': optionsWrapperHeight,
294
+ })}
295
+ style={{
296
+ transition: `opacity ${maskAnimationTimeout}ms ${maskAnimationFunction}`,
297
+ }}
298
+ />
299
+ )}
300
+ </div>
301
+ </CSSTransition>
302
+ </Portal>
303
+ </div>
304
+ )}
305
+ </ContextLayout>
306
+ );
307
+ });
308
+ }
@@ -0,0 +1,19 @@
1
+ import { componentWrapper } from '@arco-design/mobile-utils';
2
+ import Options from './options';
3
+ import { componentGenerator } from './dropdown';
4
+
5
+ export * from './type';
6
+
7
+ const Dropdown = componentGenerator(Options);
8
+
9
+ /**
10
+ * 下拉面板,展示供选择的选择项
11
+ * @en Dropdown panel, showing options to choose from
12
+ * @type 导航
13
+ * @type_en Navigation
14
+ * @name 下拉面板
15
+ * @name_en Dropdown
16
+ */
17
+ export default componentWrapper(Dropdown, {
18
+ Options,
19
+ });
@@ -0,0 +1,103 @@
1
+ import React, {
2
+ forwardRef,
3
+ Ref,
4
+ useContext,
5
+ useImperativeHandle,
6
+ useMemo,
7
+ useRef,
8
+ useState,
9
+ } from 'react';
10
+ import { cls } from '@arco-design/mobile-utils';
11
+ import { GlobalContext } from '../context-provider';
12
+ import IconCheck from '../icon/IconCheck';
13
+ import { OptionsItem, DropdownOptionsProps, DropdownOptionsRef, ValueType } from './type';
14
+
15
+ const Options = forwardRef((props: DropdownOptionsProps, ref: Ref<DropdownOptionsRef | null>) => {
16
+ const { options, useColumn, icon, selectedValue, defaultSelectedValue, multiple } = props;
17
+ const { prefixCls } = useContext(GlobalContext);
18
+ const [innerValue, setInnerValue] = useState<ValueType>(() => {
19
+ const defaultValue = multiple ? [] : 0;
20
+ return defaultSelectedValue !== void 0 ? defaultSelectedValue : defaultValue;
21
+ });
22
+ const value = useMemo<ValueType>(
23
+ () => (selectedValue === void 0 ? innerValue : selectedValue),
24
+ [selectedValue, innerValue],
25
+ );
26
+ const colCount = useColumn === true ? 4 : Number(useColumn) || 0;
27
+ const fakeOpts = new Array(
28
+ colCount && options.length % colCount ? colCount - (options.length % colCount) : 0,
29
+ ).fill('');
30
+ const minWidthStyle = useColumn ? { minWidth: `${(1 / (colCount + 1)) * 100}%` } : {};
31
+ const domRef = useRef<HTMLDivElement | null>(null);
32
+
33
+ useImperativeHandle(ref, () => ({
34
+ dom: domRef.current,
35
+ }));
36
+
37
+ /**
38
+ * 当选项改变时触发的回调函数
39
+ * @en callback when options change
40
+ * */
41
+ function handleOptionChange(e, op: OptionsItem) {
42
+ if (op.disabled) return;
43
+ if (props.multiple === true) {
44
+ if (!Array.isArray(value)) {
45
+ return;
46
+ }
47
+ const index = value.indexOf(op.value);
48
+ const checked = index >= 0;
49
+ props.onOptionClick?.(checked, op.value, op);
50
+ const newValue = [...value];
51
+ checked ? newValue.splice(index, 1) : newValue.push(op.value);
52
+ setInnerValue(newValue);
53
+ props.onOptionChange?.(newValue, op);
54
+ } else {
55
+ const newValue = op.value;
56
+ props.onOptionClick?.(newValue, op);
57
+ setInnerValue(newValue);
58
+ newValue !== value && props.onOptionChange?.(newValue, op);
59
+ }
60
+ e.stopPropagation();
61
+ }
62
+
63
+ function checkSelected(op: OptionsItem) {
64
+ return multiple && Array.isArray(value) ? value.includes(op.value) : op.value === value;
65
+ }
66
+
67
+ return (
68
+ <div
69
+ ref={domRef}
70
+ className={cls(`${prefixCls}-dropdown-options-wrap`, {
71
+ 'use-column': useColumn,
72
+ })}
73
+ >
74
+ {options.map(op => (
75
+ <div
76
+ className={cls(`${prefixCls}-dropdown-options-item${useColumn ? '-col' : ''}`, {
77
+ disabled: op.disabled,
78
+ selected: checkSelected(op),
79
+ })}
80
+ key={op.value}
81
+ onClick={e => handleOptionChange(e, op)}
82
+ style={minWidthStyle}
83
+ >
84
+ {op.label}
85
+ {icon !== void 0 ? (
86
+ icon
87
+ ) : (
88
+ <IconCheck className={`${prefixCls}-dropdown-options-item-icon`} />
89
+ )}
90
+ </div>
91
+ ))}
92
+ {fakeOpts.map((_, i) => (
93
+ <div
94
+ className={cls(`${prefixCls}-dropdown-options-item-fake-col`)}
95
+ key={i}
96
+ style={minWidthStyle}
97
+ />
98
+ ))}
99
+ </div>
100
+ );
101
+ });
102
+
103
+ export default Options;