@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.
- package/README.md +85 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +244 -0
- package/metaData/components/action-sheet/doc.md +102 -0
- package/metaData/components/action-sheet/example/index.tsx +199 -0
- package/metaData/components/action-sheet/example/methods.tsx +6 -0
- package/metaData/components/avatar/doc.md +57 -0
- package/metaData/components/avatar/example/group.tsx +69 -0
- package/metaData/components/avatar/example/index.tsx +196 -0
- package/metaData/components/badge/doc.md +28 -0
- package/metaData/components/badge/example/index.tsx +128 -0
- package/metaData/components/button/doc.md +45 -0
- package/metaData/components/button/example/index.tsx +264 -0
- package/metaData/components/carousel/doc.md +89 -0
- package/metaData/components/carousel/example/index.tsx +1481 -0
- package/metaData/components/cell/doc.md +68 -0
- package/metaData/components/cell/example/arrow.tsx +13 -0
- package/metaData/components/cell/example/cell.tsx +71 -0
- package/metaData/components/cell/example/group.tsx +63 -0
- package/metaData/components/cell/example/index.tsx +19 -0
- package/metaData/components/checkbox/doc.md +91 -0
- package/metaData/components/checkbox/example/checkbox.tsx +158 -0
- package/metaData/components/checkbox/example/group.tsx +103 -0
- package/metaData/components/checkbox/example/index.tsx +25 -0
- package/metaData/components/circle-progress/doc.md +40 -0
- package/metaData/components/circle-progress/example/index.tsx +323 -0
- package/metaData/components/collapse/doc.md +74 -0
- package/metaData/components/collapse/example/collapse.tsx +183 -0
- package/metaData/components/collapse/example/group.tsx +136 -0
- package/metaData/components/collapse/example/index.tsx +16 -0
- package/metaData/components/collapse/example/utils.tsx +20 -0
- package/metaData/components/context-provider/doc.md +44 -0
- package/metaData/components/context-provider/example/index.tsx +208 -0
- package/metaData/components/count-down/doc.md +40 -0
- package/metaData/components/count-down/example/hooks.tsx +118 -0
- package/metaData/components/count-down/example/index.tsx +63 -0
- package/metaData/components/date-picker/doc.md +102 -0
- package/metaData/components/date-picker/example/index.tsx +545 -0
- package/metaData/components/dialog/doc.md +200 -0
- package/metaData/components/dialog/example/index.tsx +328 -0
- package/metaData/components/dialog/example/methods.tsx +56 -0
- package/metaData/components/divider/doc.md +25 -0
- package/metaData/components/divider/example/index.tsx +87 -0
- package/metaData/components/dropdown/doc.md +80 -0
- package/metaData/components/dropdown/example/dropdown.tsx +308 -0
- package/metaData/components/dropdown/example/index.tsx +19 -0
- package/metaData/components/dropdown/example/options.tsx +103 -0
- package/metaData/components/dropdown-menu/doc.md +91 -0
- package/metaData/components/dropdown-menu/example/dropdown-menu.tsx +270 -0
- package/metaData/components/dropdown-menu/example/index.tsx +16 -0
- package/metaData/components/ellipsis/doc.md +33 -0
- package/metaData/components/ellipsis/example/index.tsx +93 -0
- package/metaData/components/ellipsis/example/js-ellipsis.tsx +229 -0
- package/metaData/components/ellipsis/example/native-ellipsis.tsx +48 -0
- package/metaData/components/form/doc.md +90 -0
- package/metaData/components/form/example/form-item-context.tsx +8 -0
- package/metaData/components/form/example/form-item.tsx +378 -0
- package/metaData/components/form/example/index.tsx +87 -0
- package/metaData/components/form/example/linked-container.tsx +66 -0
- package/metaData/components/grid/doc.md +39 -0
- package/metaData/components/grid/example/index.tsx +252 -0
- package/metaData/components/icon/doc.md +15 -0
- package/metaData/components/icon/example/index.tsx +32 -0
- package/metaData/components/image/doc.md +62 -0
- package/metaData/components/image/example/index.tsx +528 -0
- package/metaData/components/image-picker/doc.md +116 -0
- package/metaData/components/image-picker/example/add-icon.tsx +14 -0
- package/metaData/components/image-picker/example/index.tsx +175 -0
- package/metaData/components/image-preview/doc.md +119 -0
- package/metaData/components/image-preview/example/index.tsx +1290 -0
- package/metaData/components/image-preview/example/methods.tsx +72 -0
- package/metaData/components/index-bar/doc.md +83 -0
- package/metaData/components/index-bar/example/group.tsx +93 -0
- package/metaData/components/index-bar/example/index.tsx +271 -0
- package/metaData/components/index-bar/example/side-bar.tsx +82 -0
- package/metaData/components/input/doc.md +60 -0
- package/metaData/components/input/example/hooks.tsx +259 -0
- package/metaData/components/input/example/index.tsx +134 -0
- package/metaData/components/input/example/props.tsx +183 -0
- package/metaData/components/keyboard/doc.md +73 -0
- package/metaData/components/keyboard/example/index.tsx +238 -0
- package/metaData/components/load-more/doc.md +48 -0
- package/metaData/components/load-more/example/index.tsx +395 -0
- package/metaData/components/loading/doc.md +34 -0
- package/metaData/components/loading/example/index.tsx +312 -0
- package/metaData/components/masking/doc.md +93 -0
- package/metaData/components/masking/example/index.tsx +410 -0
- package/metaData/components/masking/example/methods.tsx +88 -0
- package/metaData/components/nav-bar/doc.md +40 -0
- package/metaData/components/nav-bar/example/back-icon.tsx +28 -0
- package/metaData/components/nav-bar/example/index.tsx +305 -0
- package/metaData/components/notice-bar/doc.md +34 -0
- package/metaData/components/notice-bar/example/index.tsx +294 -0
- package/metaData/components/notify/doc.md +81 -0
- package/metaData/components/notify/example/index.tsx +171 -0
- package/metaData/components/notify/example/methods.tsx +83 -0
- package/metaData/components/pagination/doc.md +41 -0
- package/metaData/components/pagination/example/arrow.tsx +14 -0
- package/metaData/components/pagination/example/index.tsx +292 -0
- package/metaData/components/picker/doc.md +89 -0
- package/metaData/components/picker/example/index.tsx +217 -0
- package/metaData/components/picker-view/doc.md +57 -0
- package/metaData/components/picker-view/example/cascader.tsx +141 -0
- package/metaData/components/picker-view/example/index.tsx +297 -0
- package/metaData/components/picker-view/example/multi-picker.tsx +66 -0
- package/metaData/components/picker-view/example/picker-cell.tsx +416 -0
- package/metaData/components/popover/doc.md +144 -0
- package/metaData/components/popover/example/index.tsx +23 -0
- package/metaData/components/popover/example/menu.tsx +145 -0
- package/metaData/components/popover/example/popover-inner.tsx +147 -0
- package/metaData/components/popover/example/popover.tsx +268 -0
- package/metaData/components/popup/doc.md +101 -0
- package/metaData/components/popup/example/index.tsx +125 -0
- package/metaData/components/popup/example/methods.tsx +6 -0
- package/metaData/components/popup-swiper/doc.md +108 -0
- package/metaData/components/popup-swiper/example/index.tsx +332 -0
- package/metaData/components/popup-swiper/example/methods.tsx +6 -0
- package/metaData/components/portal/doc.md +14 -0
- package/metaData/components/portal/example/index.tsx +35 -0
- package/metaData/components/progress/doc.md +42 -0
- package/metaData/components/progress/example/index.tsx +256 -0
- package/metaData/components/pull-refresh/doc.md +37 -0
- package/metaData/components/pull-refresh/example/android-pull-refresh.tsx +260 -0
- package/metaData/components/pull-refresh/example/index.tsx +44 -0
- package/metaData/components/pull-refresh/example/ios-pull-refresh.tsx +216 -0
- package/metaData/components/radio/doc.md +89 -0
- package/metaData/components/radio/example/group.tsx +92 -0
- package/metaData/components/radio/example/index.tsx +25 -0
- package/metaData/components/radio/example/radio.tsx +22 -0
- package/metaData/components/rate/doc.md +33 -0
- package/metaData/components/rate/example/index.tsx +281 -0
- package/metaData/components/search-bar/doc.md +97 -0
- package/metaData/components/search-bar/example/association.tsx +89 -0
- package/metaData/components/search-bar/example/cancel-button.tsx +34 -0
- package/metaData/components/search-bar/example/highlight.tsx +66 -0
- package/metaData/components/search-bar/example/index.tsx +251 -0
- package/metaData/components/show-monitor/doc.md +34 -0
- package/metaData/components/show-monitor/example/index.tsx +646 -0
- package/metaData/components/skeleton/doc.md +101 -0
- package/metaData/components/skeleton/example/elements.tsx +290 -0
- package/metaData/components/skeleton/example/index.tsx +96 -0
- package/metaData/components/skeleton/example/skeleton-context.tsx +7 -0
- package/metaData/components/slider/doc.md +42 -0
- package/metaData/components/slider/example/index.tsx +326 -0
- package/metaData/components/slider/example/marks.tsx +50 -0
- package/metaData/components/slider/example/popover.tsx +38 -0
- package/metaData/components/slider/example/thumb.tsx +48 -0
- package/metaData/components/slider/example/useSliderIcon.tsx +38 -0
- package/metaData/components/slider/example/useSliderInit.tsx +170 -0
- package/metaData/components/stepper/doc.md +46 -0
- package/metaData/components/stepper/example/index.tsx +166 -0
- package/metaData/components/stepper/example/useButtonClick.tsx +74 -0
- package/metaData/components/stepper/example/useInputEvent.tsx +65 -0
- package/metaData/components/stepper/example/useValue.tsx +43 -0
- package/metaData/components/steps/doc.md +57 -0
- package/metaData/components/steps/example/index.tsx +123 -0
- package/metaData/components/steps/example/step.tsx +126 -0
- package/metaData/components/sticky/doc.md +45 -0
- package/metaData/components/sticky/example/index.tsx +430 -0
- package/metaData/components/swipe-action/doc.md +44 -0
- package/metaData/components/swipe-action/example/index.tsx +338 -0
- package/metaData/components/swipe-action/example/item.tsx +40 -0
- package/metaData/components/swipe-load/doc.md +43 -0
- package/metaData/components/swipe-load/example/index.tsx +339 -0
- package/metaData/components/swipe-load/example/type.tsx +153 -0
- package/metaData/components/switch/doc.md +38 -0
- package/metaData/components/switch/example/index.tsx +237 -0
- package/metaData/components/tab-bar/doc.md +57 -0
- package/metaData/components/tab-bar/example/index.tsx +18 -0
- package/metaData/components/tab-bar/example/item.tsx +74 -0
- package/metaData/components/tab-bar/example/tab-bar.tsx +105 -0
- package/metaData/components/tabs/doc.md +187 -0
- package/metaData/components/tabs/example/index.tsx +514 -0
- package/metaData/components/tabs/example/tab-cell-underline.tsx +320 -0
- package/metaData/components/tabs/example/tab-cell.tsx +433 -0
- package/metaData/components/tabs/example/tab-pane.tsx +349 -0
- package/metaData/components/tag/doc.md +80 -0
- package/metaData/components/tag/example/index.tsx +23 -0
- package/metaData/components/tag/example/list.tsx +97 -0
- package/metaData/components/tag/example/tag.tsx +85 -0
- package/metaData/components/textarea/doc.md +67 -0
- package/metaData/components/textarea/example/index.tsx +258 -0
- package/metaData/components/toast/doc.md +90 -0
- package/metaData/components/toast/example/index.tsx +335 -0
- package/metaData/components/toast/example/methods.tsx +87 -0
- package/metaData/components/transition/doc.md +19 -0
- package/metaData/components/transition/example/index.tsx +113 -0
- package/metaData/components/uploader/doc.md +82 -0
- package/metaData/components/uploader/example/index.tsx +208 -0
- package/metaData/components-index.json +242 -0
- 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;
|