@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,305 @@
1
+ import React, {
2
+ useEffect,
3
+ useState,
4
+ forwardRef,
5
+ Ref,
6
+ useImperativeHandle,
7
+ useRef,
8
+ CSSProperties,
9
+ ReactNode,
10
+ } from 'react';
11
+ import {
12
+ cls,
13
+ getScrollContainerAttribute,
14
+ getValidScrollContainer,
15
+ } from '@arco-design/mobile-utils';
16
+ import { ContextLayout } from '../context-provider';
17
+ import BackArrow from './back-icon';
18
+ import { useSystem } from '../_helpers';
19
+
20
+ export interface NavBarRef {
21
+ /** @deprecated */
22
+ navBar: HTMLDivElement | null;
23
+ /**
24
+ * 最外层元素DOM
25
+ * @en The outermost element DOM
26
+ */
27
+ dom: HTMLDivElement | null;
28
+ }
29
+
30
+ export interface NavBarProps {
31
+ /**
32
+ * 导航栏中间文字,带居中和溢出省略效果
33
+ * @en Navigation bar middle text, with centering and overflow omitting effects
34
+ */
35
+ title?: React.ReactNode;
36
+ /**
37
+ * 自定义导航栏主内容,当导航栏内容为Tabs等非居中文字样式时可用
38
+ * @en Customize the main content of the navigation bar, available when the content of the navigation bar is a non-centered text style such as Tabs
39
+ */
40
+ children?: React.ReactNode;
41
+ /**
42
+ * 导航栏左侧内容
43
+ * @en Navigation bar left content
44
+ * @default 返回按钮
45
+ * @default_en Back button
46
+ */
47
+ leftContent?: React.ReactNode;
48
+ /**
49
+ * 导航栏右侧内容
50
+ * @en Content on the right side of the navigation bar
51
+ */
52
+ rightContent?: React.ReactNode;
53
+ /**
54
+ * 自定义样式,最外层元素的背景文字颜色可在此定义
55
+ * @en Custom stylesheet, background and text colors can be defined here
56
+ */
57
+ style?: React.CSSProperties;
58
+ /**
59
+ * 内层自定义类名
60
+ * @en Inner custom classname
61
+ */
62
+ className?: string;
63
+ /**
64
+ * 最外层元素自定义类名
65
+ * @en Outermost element custom classname
66
+ */
67
+ wrapClass?: string;
68
+ /**
69
+ * 是否吸顶
70
+ * @en Whether to fix
71
+ */
72
+ fixed?: boolean;
73
+ /**
74
+ * 沉浸式状态栏高度
75
+ * @en Immersive status bar height
76
+ * @default 0
77
+ */
78
+ statusBarHeight?: number;
79
+ /**
80
+ * 是否有底边框
81
+ * @en Whether there is a bottom border
82
+ * @default true
83
+ */
84
+ hasBottomLine?: boolean;
85
+ /**
86
+ * 点击左侧内容回调
87
+ * @en Callback when clicking the content on the left
88
+ */
89
+ onClickLeft?: (e: React.MouseEvent) => void;
90
+ /**
91
+ * 点击右侧内容回调
92
+ * @en Callback when clicking the content on the right
93
+ */
94
+ onClickRight?: (e: React.MouseEvent) => void;
95
+ /**
96
+ * 展示nav-bar的offset值,当scrollTop值小于该值时,将隐藏主内容和右侧内容,左侧内容保留
97
+ * @en Display the offset value of nav-bar. When the scrollTop value is less than this value, the main content and the right content will be hidden, and the left content will remain
98
+ * @default 0
99
+ */
100
+ showOffset?: number;
101
+ /**
102
+ * 设置showOffset后,当内容显示状态发生变化后触发
103
+ * @en After setting showOffset, triggered when the content display state changes
104
+ */
105
+ onShowChange?: (show: boolean) => void;
106
+ /**
107
+ * 设置fixed=true时,导航栏原本的位置是否要占住
108
+ * @en When fixed=true is set, whether the original position of the navigation bar should be occupied
109
+ * @default true
110
+ */
111
+ placeholder?: boolean;
112
+ /**
113
+ * 额外渲染元素,与inner平级
114
+ * @en Additional render elements, level with inner
115
+ */
116
+ extra?: ReactNode;
117
+ /**
118
+ * 自定义滚动元素,不传默认是window
119
+ * @en Custom scrolling element, default is window if not input
120
+ */
121
+ getScrollContainer?: () => HTMLElement | Window | null;
122
+ /**
123
+ * 根据滚动offset值设置自定义style,设置该属性后将监听滚动容器的滚动事件
124
+ * @en Set a custom style according to the scroll offset value. After setting this property, the scroll event of the scroll container will be monitored.
125
+ */
126
+ getComputedStyleByScroll?: (offset: number) => CSSProperties;
127
+ /**
128
+ * 滚动时回调,设置该属性后将监听滚动容器的滚动事件
129
+ * @en Callback when scrolling. After setting this property, the scroll event of the scroll container will be monitored.
130
+ */
131
+ onScrollChange?: (offset: number) => void;
132
+ /**
133
+ * 无障碍aria-label属性
134
+ * @en Accessibility attribute aria-label
135
+ * @default ""
136
+ */
137
+ ariaLabel?: string;
138
+ /**
139
+ * 无障碍role属性
140
+ * @en Accessibility attribute role
141
+ * @default "banner"
142
+ */
143
+ ariaRole?: string;
144
+ }
145
+
146
+ /**
147
+ * 导航栏组件,支持吸顶和沉浸式,支持在指定滚动位置展示,支持根据滚动位置实时更新style。
148
+ * @en Navigation bar, supports ceiling and immersion, supports display at specified scroll position, and supports real-time update of style according to scroll position.
149
+ * @type 导航
150
+ * @type_en Navigation
151
+ * @name 导航栏
152
+ * @name_en NavBar
153
+ */
154
+ const NavBar = forwardRef((props: NavBarProps, ref: Ref<NavBarRef>) => {
155
+ const {
156
+ children,
157
+ title = '',
158
+ onClickLeft,
159
+ leftContent = <BackArrow />,
160
+ onClickRight,
161
+ rightContent,
162
+ onShowChange,
163
+ style,
164
+ className = '',
165
+ wrapClass,
166
+ placeholder = true,
167
+ fixed = true,
168
+ hasBottomLine = true,
169
+ statusBarHeight = 0,
170
+ extra,
171
+ getScrollContainer,
172
+ showOffset = 0,
173
+ onScrollChange,
174
+ getComputedStyleByScroll,
175
+ ariaLabel = '',
176
+ ariaRole = 'banner',
177
+ } = props;
178
+ const navBarRef = useRef<HTMLDivElement | null>(null);
179
+
180
+ const [scrollToggleHide, setScrollToggleHide] = useState(showOffset > 0);
181
+ const relBackground = scrollToggleHide ? 'transparent' : '';
182
+ const [customStyle, setCustomStyle] = useState<CSSProperties>({});
183
+ const system = useSystem();
184
+
185
+ const onElementScroll = (curOffset: number) => {
186
+ setScrollToggleHide(curOffset < showOffset);
187
+ onScrollChange?.(curOffset);
188
+
189
+ if (getComputedStyleByScroll) {
190
+ const cstyle = getComputedStyleByScroll(curOffset);
191
+
192
+ setCustomStyle(cstyle);
193
+ }
194
+ };
195
+
196
+ useImperativeHandle(ref, () => ({
197
+ navBar: navBarRef.current,
198
+ dom: navBarRef.current,
199
+ }));
200
+
201
+ useEffect(() => {
202
+ onShowChange?.(!scrollToggleHide);
203
+ }, [scrollToggleHide]);
204
+
205
+ const handleEleScroll = () => {
206
+ const top = getScrollContainerAttribute('scrollTop', getScrollContainer) || 0;
207
+ onElementScroll(top);
208
+ };
209
+
210
+ useEffect(() => {
211
+ const needBind = showOffset || getComputedStyleByScroll || onScrollChange;
212
+ const container = getValidScrollContainer(getScrollContainer);
213
+ handleEleScroll();
214
+ if (needBind && container) {
215
+ container.addEventListener('scroll', handleEleScroll, false);
216
+ }
217
+ return () => {
218
+ if (needBind && container) {
219
+ container.removeEventListener('scroll', handleEleScroll, false);
220
+ }
221
+ };
222
+ }, [showOffset, getComputedStyleByScroll, onScrollChange, getScrollContainer]);
223
+
224
+ function handleClickLeft(e: React.MouseEvent) {
225
+ if (onClickLeft) {
226
+ onClickLeft(e);
227
+ }
228
+ }
229
+
230
+ function handleClickRight(e: React.MouseEvent) {
231
+ if (onClickRight) {
232
+ onClickRight(e);
233
+ }
234
+ }
235
+
236
+ // 最外层div是为了fixed时占位
237
+ // @en The outermost div is a placeholder for fixed
238
+ // 第二层div是为了沉浸式paddingTop
239
+ // @en The second layer of div is for immersive paddingTop
240
+ // 第三层是为了保证内容高度始终是44px
241
+ // @en The third layer is to ensure that the content height is always 44px
242
+ // 背景色放到wrapper层是为了兼容iOS下拉回弹的背景色
243
+ // @en The background color is placed on the wrapper layer to be compatible with the background color of the pull-down rebound in iOS
244
+ // fixed时用height+padding撑开nav-bar最外层高度;不fixed时内部wrapper元素撑开最外层高度
245
+ // @en When fixed, use height+padding to stretch the outermost height of nav-bar; when not fixed, the inner wrapper element stretches the outermost height
246
+ return (
247
+ <ContextLayout>
248
+ {({ prefixCls }) => (
249
+ <div
250
+ ref={navBarRef}
251
+ className={cls(wrapClass, `${prefixCls}-nav-bar`, {
252
+ [`${prefixCls}-nav-bar-fixed`]: fixed,
253
+ [`${prefixCls}-nav-bar-float`]: !placeholder,
254
+ [`${prefixCls}-nav-bar-hide`]: scrollToggleHide,
255
+ })}
256
+ style={{
257
+ paddingTop: fixed && statusBarHeight ? `${statusBarHeight}px` : '0px',
258
+ ...(style || {}),
259
+ ...(relBackground ? { background: relBackground } : {}),
260
+ }}
261
+ aria-label={ariaLabel}
262
+ role={ariaRole}
263
+ >
264
+ <div
265
+ className={cls(className, system, `${prefixCls}-nav-bar-wrapper`, {
266
+ [`${prefixCls}-nav-bar-wrapper-fixed`]: fixed,
267
+ [`${prefixCls}-nav-bar-wrapper-border`]: hasBottomLine,
268
+ })}
269
+ style={{
270
+ paddingTop: statusBarHeight ? `${statusBarHeight}px` : '0px',
271
+ ...customStyle,
272
+ }}
273
+ >
274
+ <div className={`${prefixCls}-nav-bar-inner all-border-box`}>
275
+ {leftContent ? (
276
+ <div
277
+ className={`${prefixCls}-nav-bar-left`}
278
+ onClick={handleClickLeft}
279
+ >
280
+ {leftContent}
281
+ </div>
282
+ ) : null}
283
+ {children || (
284
+ <div className={`${prefixCls}-nav-bar-title`}>
285
+ <div className={`${prefixCls}-nav-bar-title-text`}>{title}</div>
286
+ </div>
287
+ )}
288
+ {rightContent ? (
289
+ <div
290
+ className={`${prefixCls}-nav-bar-right`}
291
+ onClick={handleClickRight}
292
+ >
293
+ {rightContent}
294
+ </div>
295
+ ) : null}
296
+ </div>
297
+ {extra}
298
+ </div>
299
+ </div>
300
+ )}
301
+ </ContextLayout>
302
+ );
303
+ });
304
+
305
+ export default NavBar;
@@ -0,0 +1,34 @@
1
+ ### 信息展示
2
+
3
+ # 通知栏 NoticeBar
4
+
5
+ 可自定义换行或滚动效果,支持循环滚动。
6
+
7
+ ======
8
+
9
+ > 属性/Props
10
+
11
+ |参数|描述|类型|默认值|
12
+ |----------|-------------|------|------|
13
+ |className|自定义类名|string|-|
14
+ |style|自定义样式,背景颜色和文字颜色可直接在这里指定\`background\`和\`color\`|CSSProperties|-|
15
+ |children|通知内容文字,如需垂直滚动效果可传入一个Carousel|ReactNode|-|
16
+ |leftContent|左侧内容|ReactNode|-|
17
+ |rightContent|右侧内容|ReactNode|-|
18
+ |marquee|通知文字处理方式,为\`overflow\`则文字超出容器长度时才滚动,为\`none\`则文字始终不滚动,为\`always\`则文字始终滚动|"overflow" \| "none" \| "always"|"overflow"|
19
+ |closeable|是否可关闭|boolean|true|
20
+ |closeIcon|自定义关闭图标|ReactNode|\<IconClose /\>|
21
+ |wrapable|是否需要换行,当 marquee=none 且 wrapable=false 时,文字超出会有溢出省略(ellipsis)效果|boolean|true|
22
+ |speed|文字滚动速度,单位是px/s|number|50|
23
+ |delay|文字开始滚动之前的延迟(ms)|number|1000|
24
+ |autoSetGradientStyle|是否根据\`style\`属性中自定义的背景色自动设置渐变背景色|boolean|true|
25
+ |onClick|点击通知栏事件|(e: MouseEvent\<HTMLDivElement, MouseEvent\>) =\> void|-|
26
+ |onClose|点击关闭按钮回调|(e: MouseEvent\<HTMLDivElement, MouseEvent\>) =\> void|-|
27
+
28
+ > 引用/Refs
29
+
30
+ |参数|描述|类型|
31
+ |----------|-------------|------|
32
+ |dom|最外层元素 DOM|HTMLDivElement|
33
+ |close|手动关闭通知栏,即移除当前组件|() =\> void|
34
+ |updateData|手动更新组件布局|() =\> void|
@@ -0,0 +1,294 @@
1
+ import { cls, fadeColor, nextTick } from '@arco-design/mobile-utils';
2
+ import React, {
3
+ useRef,
4
+ forwardRef,
5
+ Ref,
6
+ useImperativeHandle,
7
+ ReactNode,
8
+ useState,
9
+ useEffect,
10
+ useMemo,
11
+ CSSProperties,
12
+ useContext,
13
+ } from 'react';
14
+ import { ContextLayout, GlobalContext } from '../context-provider';
15
+ import IconClose from '../icon/IconClose';
16
+
17
+ export interface NoticeBarProps {
18
+ /**
19
+ * 自定义类名
20
+ * @en Custom classname
21
+ */
22
+ className?: string;
23
+ /**
24
+ * 自定义样式,背景颜色和文字颜色可直接在这里指定`background`和`color`
25
+ * @en Custom stylesheet, background color and text color can be directly specified here `background` and `color`
26
+ */
27
+ style?: React.CSSProperties;
28
+ /**
29
+ * 通知内容文字,如需垂直滚动效果可传入一个Carousel
30
+ * @en Notification content text, if need vertical scrolling effect, you can input a Carousel
31
+ */
32
+ children?: ReactNode;
33
+ /**
34
+ * 左侧内容
35
+ * @en Content on the left
36
+ */
37
+ leftContent?: ReactNode;
38
+ /**
39
+ * 右侧内容
40
+ * @en Content on the right
41
+ */
42
+ rightContent?: ReactNode;
43
+ /**
44
+ * 通知文字处理方式,为`overflow`则文字超出容器长度时才滚动,为`none`则文字始终不滚动,为`always`则文字始终滚动
45
+ * @en The text processing method of the notification. When it is `overflow`, the text will only scroll when the length of the container exceeds the length of the container. If it is `none`, the text will never scroll, and if it is `always`, the text will always scroll.
46
+ * @default "overflow"
47
+ */
48
+ marquee?: 'overflow' | 'none' | 'always';
49
+ /**
50
+ * 是否可关闭
51
+ * @en Closeable
52
+ * @default true
53
+ */
54
+ closeable?: boolean;
55
+ /**
56
+ * 自定义关闭图标
57
+ * @en Custom close icon
58
+ * @default \<IconClose /\>
59
+ */
60
+ closeIcon?: ReactNode;
61
+ /**
62
+ * 是否需要换行,当 marquee=none 且 wrapable=false 时,文字超出会有溢出省略(ellipsis)效果
63
+ * @en Whether line wrapping is required, when marquee=none and wrapable=false, there will be overflow ellipsis effect when the text exceeds
64
+ * @default true
65
+ */
66
+ wrapable?: boolean;
67
+ /**
68
+ * 文字滚动速度,单位是px/s
69
+ * @en Text scrolling speed in px/s
70
+ * @default 50
71
+ */
72
+ speed?: number;
73
+ /**
74
+ * 文字开始滚动之前的延迟(ms)
75
+ * @en Delay before text starts scrolling (ms)
76
+ * @default 1000
77
+ */
78
+ delay?: number;
79
+ /**
80
+ * 是否根据`style`属性中自定义的背景色自动设置渐变背景色
81
+ * @en Whether to automatically set the gradient background color based on the custom background color in the `style` attribute
82
+ * @default true
83
+ */
84
+ autoSetGradientStyle?: boolean;
85
+ /**
86
+ * 点击通知栏事件
87
+ * @en Click on notification bar event
88
+ */
89
+ onClick?: (e: React.MouseEvent<HTMLDivElement, MouseEvent>) => void;
90
+ /**
91
+ * 点击关闭按钮回调
92
+ * @en Callback when clicking close button
93
+ */
94
+ onClose?: (e: React.MouseEvent<HTMLDivElement, MouseEvent>) => void;
95
+ }
96
+
97
+ export interface NoticeBarRef {
98
+ /**
99
+ * 最外层元素 DOM
100
+ * @en The outermost element DOM
101
+ */
102
+ dom: HTMLDivElement | null;
103
+ /**
104
+ * 手动关闭通知栏,即移除当前组件
105
+ * @en Manually close the notification bar, that is, remove the current component
106
+ */
107
+ close: () => void;
108
+ /**
109
+ * 手动更新组件布局
110
+ * @en Manually update the component layout
111
+ */
112
+ updateData: () => void;
113
+ }
114
+
115
+ /**
116
+ * 可自定义换行或滚动效果,支持循环滚动。
117
+ * @en Line wrapping or scrolling effects can be customized, and circular scrolling is supported.
118
+ * @type 信息展示
119
+ * @type_en Data Display
120
+ * @name 通知栏
121
+ * @name_en NoticeBar
122
+ */
123
+ const NoticeBar = forwardRef((props: NoticeBarProps, ref: Ref<NoticeBarRef>) => {
124
+ const {
125
+ className = '',
126
+ style,
127
+ children,
128
+ leftContent,
129
+ rightContent,
130
+ marquee = 'overflow',
131
+ closeable = true,
132
+ closeIcon = <IconClose />,
133
+ wrapable = true,
134
+ speed = 50,
135
+ delay = 1000,
136
+ autoSetGradientStyle = true,
137
+ onClick,
138
+ onClose,
139
+ } = props;
140
+ const { useRtl } = useContext(GlobalContext);
141
+ const domRef = useRef<HTMLDivElement>(null);
142
+ const wrapRef = useRef<HTMLDivElement>(null);
143
+ const contentRef = useRef<HTMLDivElement>(null);
144
+ const timerRef = useRef<number | null>(null);
145
+ const [visible, setVisible] = useState(true);
146
+ const [needMarquee, setNeedMarquee] = useState(false);
147
+ const extraClass = useMemo(() => {
148
+ const classList: string[] = [];
149
+ // 字幕可滚动时,或不可滚动且不可以换行时,添加不换行效果
150
+ // @en When the subtitle is scrollable, or when it is not scrollable and cannot wrap, add no wrapping effect
151
+ if (marquee !== 'none' || !wrapable) {
152
+ classList.push('no-wrap');
153
+ }
154
+ // 字幕不可滚动时,根据是否可换行决定换行或省略效果
155
+ // @en When the subtitle is not scrollable, decide whether to wrap or omit the effect according to whether it can wrap
156
+ if (marquee === 'none') {
157
+ classList.push(wrapable ? 'wrapable' : 'ellipsis');
158
+ }
159
+ return classList;
160
+ }, [marquee, wrapable]);
161
+ const gradientStyle = useMemo<CSSProperties>(() => {
162
+ if (!autoSetGradientStyle) {
163
+ return {};
164
+ }
165
+ const curBg = style?.backgroundColor || style?.background;
166
+ const fadeBg = fadeColor(String(curBg) || '');
167
+ return fadeBg
168
+ ? {
169
+ background: `linear-gradient(to right, ${curBg}, ${fadeBg})`,
170
+ }
171
+ : {};
172
+ }, [style]);
173
+
174
+ useImperativeHandle(
175
+ ref,
176
+ () => ({
177
+ dom: domRef.current,
178
+ close,
179
+ updateData,
180
+ }),
181
+ [updateData],
182
+ );
183
+
184
+ useEffect(() => {
185
+ updateData();
186
+ }, [useRtl]);
187
+
188
+ function close() {
189
+ setVisible(false);
190
+ clear();
191
+ }
192
+
193
+ function handleClose(e: React.MouseEvent<HTMLDivElement, MouseEvent>) {
194
+ e.stopPropagation();
195
+ onClose?.(e);
196
+ close();
197
+ }
198
+
199
+ function updateData() {
200
+ nextTick(() => {
201
+ setTimeout(
202
+ () => {
203
+ updateLayoutData();
204
+ },
205
+ needMarquee ? 0 : delay,
206
+ );
207
+ });
208
+ }
209
+
210
+ function updateLayoutData() {
211
+ if (!wrapRef.current || !contentRef.current) {
212
+ return;
213
+ }
214
+ const contentDom = contentRef.current;
215
+ let needMarq = marquee === 'always';
216
+ const wrapWidth = wrapRef.current.offsetWidth;
217
+ const contentWidth = contentRef.current.offsetWidth;
218
+ // 如果为overflow类型,则当文字溢出时再开始滚动
219
+ // @en If it is overflow type, start scrolling when the text overflows
220
+ if (marquee === 'overflow') {
221
+ if (wrapWidth < contentWidth) {
222
+ needMarq = true;
223
+ }
224
+ }
225
+ setNeedMarquee(needMarq);
226
+ clear();
227
+ if (needMarq) {
228
+ const duration = contentWidth / speed;
229
+ contentDom.style.animationDuration = `${duration}s`;
230
+ // 第一次没有padding,第二次及以后为了从容器右边缘无缝滑入需要加上padding
231
+ // @en The first time without padding, the second time and later need to add padding in order to seamlessly slide in from the right edge of the container
232
+ timerRef.current = window.setTimeout(() => {
233
+ // 这里暂时把animate类去掉,准备下一次从头开始
234
+ // @en Temporarily remove the animate classname, and prepare to start from scratch next time
235
+ setNeedMarquee(false);
236
+ contentDom.style.animationDuration = `${wrapWidth / speed + duration}s`;
237
+ contentDom.style.animationIterationCount = 'infinite';
238
+ const paddingAttr = useRtl ? 'paddingRight' : 'paddingLeft';
239
+ contentDom.style[paddingAttr] = `${wrapWidth}px`;
240
+ nextTick(() => {
241
+ setNeedMarquee(true);
242
+ });
243
+ }, duration * 1000);
244
+ }
245
+ }
246
+
247
+ function clear() {
248
+ if (timerRef.current) {
249
+ clearTimeout(timerRef.current);
250
+ timerRef.current = null;
251
+ }
252
+ }
253
+
254
+ function renderNoticeBar(prefix: string) {
255
+ return visible ? (
256
+ <div
257
+ className={cls(prefix, className, extraClass)}
258
+ style={style}
259
+ ref={domRef}
260
+ onClick={onClick}
261
+ >
262
+ {leftContent ? <div className={`${prefix}-left-part`}>{leftContent}</div> : null}
263
+ <div className={`${prefix}-content`} ref={wrapRef}>
264
+ {needMarquee ? (
265
+ <i className={`${prefix}-gradient left`} style={gradientStyle} />
266
+ ) : null}
267
+ <div
268
+ className={cls(`${prefix}-content-inner`, { animate: needMarquee })}
269
+ ref={contentRef}
270
+ >
271
+ {children}
272
+ </div>
273
+ {needMarquee ? (
274
+ <i className={`${prefix}-gradient right`} style={gradientStyle} />
275
+ ) : null}
276
+ </div>
277
+ {rightContent ? <div className={`${prefix}-right-part`}>{rightContent}</div> : null}
278
+ {closeable ? (
279
+ <div className={`${prefix}-close`} onClick={handleClose}>
280
+ {closeIcon}
281
+ </div>
282
+ ) : null}
283
+ </div>
284
+ ) : null;
285
+ }
286
+
287
+ return (
288
+ <ContextLayout>
289
+ {({ prefixCls }) => renderNoticeBar(`${prefixCls}-notice-bar`)}
290
+ </ContextLayout>
291
+ );
292
+ });
293
+
294
+ export default NoticeBar;