@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,326 @@
|
|
|
1
|
+
import React, {
|
|
2
|
+
useRef,
|
|
3
|
+
forwardRef,
|
|
4
|
+
Ref,
|
|
5
|
+
useImperativeHandle,
|
|
6
|
+
useContext,
|
|
7
|
+
useState,
|
|
8
|
+
useMemo,
|
|
9
|
+
useCallback,
|
|
10
|
+
ReactNode,
|
|
11
|
+
} from 'react';
|
|
12
|
+
import { cls, componentWrapper } from '@arco-design/mobile-utils';
|
|
13
|
+
import { GlobalContext } from '../context-provider';
|
|
14
|
+
import {
|
|
15
|
+
useSliderStyle,
|
|
16
|
+
useSliderIcon,
|
|
17
|
+
SliderContext,
|
|
18
|
+
baseRenderThumb,
|
|
19
|
+
useSliderInit,
|
|
20
|
+
useSliderEvents,
|
|
21
|
+
} from './hooks';
|
|
22
|
+
import Thumb from './thumb';
|
|
23
|
+
import Marks from './marks';
|
|
24
|
+
|
|
25
|
+
export interface SliderProps {
|
|
26
|
+
/**
|
|
27
|
+
* 自定义类名
|
|
28
|
+
* @en Custom classname
|
|
29
|
+
* @default ""
|
|
30
|
+
*/
|
|
31
|
+
className?: string;
|
|
32
|
+
/**
|
|
33
|
+
* 自定义样式
|
|
34
|
+
* @en Custom stylesheet
|
|
35
|
+
* @default {}
|
|
36
|
+
*/
|
|
37
|
+
style?: React.CSSProperties;
|
|
38
|
+
/**
|
|
39
|
+
* 是否禁用
|
|
40
|
+
* @en Disabled
|
|
41
|
+
* @default false
|
|
42
|
+
*/
|
|
43
|
+
disabled?: boolean;
|
|
44
|
+
/**
|
|
45
|
+
* 使用范围选择
|
|
46
|
+
* @en Use range selection
|
|
47
|
+
* @default 根据 传入初始参数判断,没有则为 false
|
|
48
|
+
* @default_en Judging by the incoming initial parameters, false if not
|
|
49
|
+
*/
|
|
50
|
+
useRange?: boolean;
|
|
51
|
+
/**
|
|
52
|
+
* 非点击状态使用动画
|
|
53
|
+
* @en Use animation for non-click state
|
|
54
|
+
* @default true
|
|
55
|
+
*/
|
|
56
|
+
useAnimation?: boolean;
|
|
57
|
+
/**
|
|
58
|
+
* 最小值
|
|
59
|
+
* @en Minimum value
|
|
60
|
+
* @default 0
|
|
61
|
+
*/
|
|
62
|
+
min?: number;
|
|
63
|
+
/**
|
|
64
|
+
* 最大值
|
|
65
|
+
* @en Maximum value
|
|
66
|
+
* @default 100
|
|
67
|
+
*/
|
|
68
|
+
max?: number;
|
|
69
|
+
/**
|
|
70
|
+
* 默认初始值
|
|
71
|
+
* @en Default initial value
|
|
72
|
+
*/
|
|
73
|
+
defaultValue?: number | [number, number];
|
|
74
|
+
/**
|
|
75
|
+
* 基础值,组件受控
|
|
76
|
+
* @en Value, component will be controlled
|
|
77
|
+
*/
|
|
78
|
+
value?: number | [number, number];
|
|
79
|
+
/**
|
|
80
|
+
* 最小单位
|
|
81
|
+
* @en Step
|
|
82
|
+
* @default 1
|
|
83
|
+
*/
|
|
84
|
+
step?: number;
|
|
85
|
+
/**
|
|
86
|
+
* 组件方向
|
|
87
|
+
* @en Component direction
|
|
88
|
+
* @default "horizontal"
|
|
89
|
+
*/
|
|
90
|
+
type?: 'horizontal' | 'vertical';
|
|
91
|
+
/**
|
|
92
|
+
* 是否展示气泡
|
|
93
|
+
* @en Whether to show tooltips
|
|
94
|
+
* @default "auto"
|
|
95
|
+
*/
|
|
96
|
+
showTooltip?: 'auto' | 'always' | 'never';
|
|
97
|
+
/**
|
|
98
|
+
* 滑动条节点配置
|
|
99
|
+
* @en Slider node configuration
|
|
100
|
+
*/
|
|
101
|
+
marks?: number | number[] | Record<string, ReactNode>;
|
|
102
|
+
/**
|
|
103
|
+
* 是否展示标签底部文案
|
|
104
|
+
* @en Whether to display the bottom mark text
|
|
105
|
+
* @default false
|
|
106
|
+
*/
|
|
107
|
+
showMarks?: boolean;
|
|
108
|
+
/**
|
|
109
|
+
* 是否只可用标志的值,覆盖 step
|
|
110
|
+
* @en Whether only the value of the mark is available, overriding step
|
|
111
|
+
* @default false
|
|
112
|
+
*/
|
|
113
|
+
useMarkOnly?: boolean;
|
|
114
|
+
/**
|
|
115
|
+
* 前缀 icon 或者 文案
|
|
116
|
+
* @en prefix icon or label
|
|
117
|
+
*/
|
|
118
|
+
prefixLabel?: boolean | ((value: number | [number, number]) => ReactNode) | ReactNode;
|
|
119
|
+
/**
|
|
120
|
+
* 后缀 icon 或者 文案
|
|
121
|
+
* @en Suffix icon or label
|
|
122
|
+
*/
|
|
123
|
+
suffixLabel?: boolean | ((value: number | [number, number]) => ReactNode) | ReactNode;
|
|
124
|
+
/**
|
|
125
|
+
* 组件横条粗细,单位 px
|
|
126
|
+
* @en Slider bar thickness (unit: px)
|
|
127
|
+
* @default 2
|
|
128
|
+
*/
|
|
129
|
+
size?: number | string;
|
|
130
|
+
/**
|
|
131
|
+
* 只允许拖动滑块 默认 false
|
|
132
|
+
* @en Only allow dragging of sliders, the default is false
|
|
133
|
+
* @default false
|
|
134
|
+
*/
|
|
135
|
+
draggableTrackOnly?: boolean;
|
|
136
|
+
/**
|
|
137
|
+
* 自定义气泡内容
|
|
138
|
+
* @en Custom tooltip content
|
|
139
|
+
*/
|
|
140
|
+
formatTooltip?: (value: number) => string | ReactNode;
|
|
141
|
+
/**
|
|
142
|
+
* 修改后事件,时机同 touchend
|
|
143
|
+
* @en Callback after value changes, the timing is the same as touchend
|
|
144
|
+
*/
|
|
145
|
+
onAfterChange?: (value: number | [number, number]) => void;
|
|
146
|
+
/**
|
|
147
|
+
* 实时修改事件
|
|
148
|
+
* @en Callback when value changes in real time
|
|
149
|
+
*/
|
|
150
|
+
onChange?: (value: number | [number, number]) => void;
|
|
151
|
+
/**
|
|
152
|
+
* 自定义滑块
|
|
153
|
+
* @en Custom slider
|
|
154
|
+
*/
|
|
155
|
+
renderThumb?: (value: number) => ReactNode;
|
|
156
|
+
/**
|
|
157
|
+
* 自定义滑块的Popover,优先级高于formatTooltip。value表示当前滑动进度,visible表示气泡的可见性,index表示当前滑块索引,thumbEl表示当前滑块元素。
|
|
158
|
+
* @en Custom slider popover,priority is higher than formatTooltip. value - current sliding progress, visible - visibility of the bubble, index - current slider index, thumbEl - current slider element.
|
|
159
|
+
*/
|
|
160
|
+
renderThumbPopover?: (config: {
|
|
161
|
+
value: number;
|
|
162
|
+
visible: boolean;
|
|
163
|
+
index: number;
|
|
164
|
+
thumbEl: React.ReactNode;
|
|
165
|
+
}) => ReactNode;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
export interface SliderRef {
|
|
169
|
+
/**
|
|
170
|
+
* 最外层元素 DOM
|
|
171
|
+
* @en The outermost element DOM
|
|
172
|
+
*/
|
|
173
|
+
dom: HTMLDivElement | null;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
const Slider = forwardRef((_, ref: Ref<SliderRef>) => {
|
|
177
|
+
const { prefixCls = '', useRtl } = useContext(GlobalContext);
|
|
178
|
+
const { className, style, disabled, min, max, type, showTooltip, showMarks } =
|
|
179
|
+
useContext(SliderContext);
|
|
180
|
+
const domRef = useRef<HTMLDivElement>(null);
|
|
181
|
+
const lineRef = useRef<HTMLDivElement>(null);
|
|
182
|
+
// 拖动滑块的 index 为当前的 isTouching
|
|
183
|
+
// @en The index of the slider is the current isTouching
|
|
184
|
+
const [commonIsTouching, setCommonIsTouching] = useState<number>(-1);
|
|
185
|
+
const isHorizontal = /^horizontal/g.test(type);
|
|
186
|
+
|
|
187
|
+
useImperativeHandle(ref, () => ({
|
|
188
|
+
dom: domRef.current,
|
|
189
|
+
}));
|
|
190
|
+
|
|
191
|
+
const { firstRender, valueGroup, setValueGroup, runTimeConfig, isMultiThumb } = useSliderInit({
|
|
192
|
+
commonIsTouching,
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
// 最长线 div 位置
|
|
196
|
+
const getLinePosition = useCallback(() => {
|
|
197
|
+
const { width, left, top, height } = lineRef.current?.getBoundingClientRect() ?? {
|
|
198
|
+
width: 0,
|
|
199
|
+
left: 0,
|
|
200
|
+
top: 0,
|
|
201
|
+
height: 0,
|
|
202
|
+
};
|
|
203
|
+
const leftValue = useRtl ? left + width : left;
|
|
204
|
+
return {
|
|
205
|
+
length: isHorizontal ? width : height,
|
|
206
|
+
start: isHorizontal ? leftValue : top,
|
|
207
|
+
};
|
|
208
|
+
}, [isHorizontal, firstRender]);
|
|
209
|
+
|
|
210
|
+
const { heightStyle, transitionClass, activeLineStyle } = useSliderStyle({
|
|
211
|
+
getLinePosition,
|
|
212
|
+
commonIsTouching,
|
|
213
|
+
valueGroup,
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
useSliderEvents({
|
|
217
|
+
lineRef,
|
|
218
|
+
valueGroup,
|
|
219
|
+
setValueGroup,
|
|
220
|
+
getLinePosition,
|
|
221
|
+
setCommonIsTouching,
|
|
222
|
+
});
|
|
223
|
+
const { renderPrefixLabel, renderSuffixLabel } = useSliderIcon({ valueGroup });
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* 为了减少 dom 元素计算,只改变激活 line 的样式,让对应的 thumb 附着在两边,所以需要实时调整 className
|
|
227
|
+
* @en In order to reduce the calculation of dom elements, only the style of the active line is changed, so that the corresponding thumb is attached to both sides, so the className needs to be adjusted in real time
|
|
228
|
+
*/
|
|
229
|
+
const thumbConfig = useMemo(() => {
|
|
230
|
+
if (isMultiThumb) {
|
|
231
|
+
const classNames = ['is-end', 'is-start'];
|
|
232
|
+
return Array(2)
|
|
233
|
+
.fill('')
|
|
234
|
+
.map((_, index) => {
|
|
235
|
+
let childClassName = classNames[index];
|
|
236
|
+
if (valueGroup[index] > valueGroup[1 - index]) {
|
|
237
|
+
[childClassName] = classNames;
|
|
238
|
+
} else if (valueGroup[index] < valueGroup[1 - index]) {
|
|
239
|
+
[, childClassName] = classNames;
|
|
240
|
+
}
|
|
241
|
+
return {
|
|
242
|
+
className: childClassName,
|
|
243
|
+
};
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
return [
|
|
247
|
+
{
|
|
248
|
+
className: 'is-end',
|
|
249
|
+
},
|
|
250
|
+
];
|
|
251
|
+
/**
|
|
252
|
+
* 只有 range 状态才监听 valueGroup
|
|
253
|
+
* @en Only listen to valueGroup in the range state
|
|
254
|
+
*/
|
|
255
|
+
}, [isMultiThumb && valueGroup]);
|
|
256
|
+
|
|
257
|
+
return (
|
|
258
|
+
<div
|
|
259
|
+
className={cls(`${prefixCls}-slider ${className}`, `is-${type}`, {
|
|
260
|
+
'is-disabled': disabled,
|
|
261
|
+
'show-tooltip': showTooltip !== 'never' && !disabled,
|
|
262
|
+
'show-marks': showMarks,
|
|
263
|
+
})}
|
|
264
|
+
style={style}
|
|
265
|
+
ref={domRef}
|
|
266
|
+
role="slider"
|
|
267
|
+
aria-valuemin={min}
|
|
268
|
+
aria-valuemax={max}
|
|
269
|
+
aria-valuenow={Array.isArray(valueGroup) ? valueGroup[0] : valueGroup}
|
|
270
|
+
aria-disabled={disabled}
|
|
271
|
+
>
|
|
272
|
+
{isHorizontal ? renderPrefixLabel : renderSuffixLabel}
|
|
273
|
+
<div className={`${prefixCls}-slider-wrapper`}>
|
|
274
|
+
<Marks runTimeConfig={runTimeConfig} className={transitionClass} />
|
|
275
|
+
<div className={`${prefixCls}-slider-line`} style={heightStyle} ref={lineRef}>
|
|
276
|
+
<div
|
|
277
|
+
className={`${prefixCls}-slider-line is-activated ${transitionClass}`}
|
|
278
|
+
style={{ ...heightStyle, ...activeLineStyle }}
|
|
279
|
+
>
|
|
280
|
+
{thumbConfig.map((cfg, idx) => (
|
|
281
|
+
<Thumb
|
|
282
|
+
key={idx}
|
|
283
|
+
{...{
|
|
284
|
+
value: Array.isArray(valueGroup) ? valueGroup[idx] : valueGroup,
|
|
285
|
+
idx,
|
|
286
|
+
min,
|
|
287
|
+
max,
|
|
288
|
+
isTouching: commonIsTouching === idx,
|
|
289
|
+
...cfg,
|
|
290
|
+
}}
|
|
291
|
+
/>
|
|
292
|
+
))}
|
|
293
|
+
</div>
|
|
294
|
+
</div>
|
|
295
|
+
</div>
|
|
296
|
+
{isHorizontal ? renderSuffixLabel : renderPrefixLabel}
|
|
297
|
+
</div>
|
|
298
|
+
);
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
const SliderWrapper = forwardRef((props: SliderProps, ref: Ref<SliderRef>) => {
|
|
302
|
+
const { prefixCls = '' } = useContext(GlobalContext);
|
|
303
|
+
const defaultProps = useContext(SliderContext);
|
|
304
|
+
const newProps = {
|
|
305
|
+
...defaultProps,
|
|
306
|
+
renderThumb: baseRenderThumb(prefixCls),
|
|
307
|
+
...props,
|
|
308
|
+
};
|
|
309
|
+
|
|
310
|
+
return (
|
|
311
|
+
<SliderContext.Provider value={newProps}>
|
|
312
|
+
<Slider ref={ref} />
|
|
313
|
+
</SliderContext.Provider>
|
|
314
|
+
);
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
/**
|
|
318
|
+
* 滑动型输入器,展示当前值和可选范围。
|
|
319
|
+
* @en Slide input component, displays the current value and optional range.
|
|
320
|
+
* @name 滑动输入条
|
|
321
|
+
* @name_en Slider
|
|
322
|
+
* @type 数据录入
|
|
323
|
+
* @type_en Data Entry
|
|
324
|
+
* @displayName Slider
|
|
325
|
+
*/
|
|
326
|
+
export default componentWrapper(SliderWrapper, 'Slider');
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import React, { useContext, CSSProperties, useCallback, ReactNode } from 'react';
|
|
2
|
+
import { cls } from '@arco-design/mobile-utils';
|
|
3
|
+
import { GlobalContext } from '../context-provider';
|
|
4
|
+
import { SliderContext } from './hooks';
|
|
5
|
+
|
|
6
|
+
function Marks({
|
|
7
|
+
runTimeConfig,
|
|
8
|
+
className = '',
|
|
9
|
+
style = {},
|
|
10
|
+
}: {
|
|
11
|
+
runTimeConfig: Array<{ value: number; activated: boolean; label: ReactNode }>;
|
|
12
|
+
className?: string;
|
|
13
|
+
style?: CSSProperties;
|
|
14
|
+
}) {
|
|
15
|
+
const { prefixCls = '', useRtl } = useContext(GlobalContext);
|
|
16
|
+
const { type, showMarks, max, min } = useContext(SliderContext);
|
|
17
|
+
const isHorizontal = /^horizontal/g.test(type);
|
|
18
|
+
|
|
19
|
+
const genMarkStyle = useCallback(
|
|
20
|
+
(value: number) => {
|
|
21
|
+
const leftStyle = useRtl ? 'right' : 'left';
|
|
22
|
+
return {
|
|
23
|
+
[isHorizontal ? leftStyle : 'bottom']: `${((value - min) / (max - min)) * 100}%`,
|
|
24
|
+
};
|
|
25
|
+
},
|
|
26
|
+
[isHorizontal, min, max],
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
return (
|
|
30
|
+
<div className={`${prefixCls}-slider-marks`}>
|
|
31
|
+
{runTimeConfig.map(({ value, label, activated }) => (
|
|
32
|
+
<div
|
|
33
|
+
key={value}
|
|
34
|
+
className={cls(
|
|
35
|
+
`${prefixCls}-slider-marks-item`,
|
|
36
|
+
{ 'is-activated': activated },
|
|
37
|
+
className,
|
|
38
|
+
)}
|
|
39
|
+
style={{ ...genMarkStyle(value), ...style }}
|
|
40
|
+
>
|
|
41
|
+
{showMarks && (
|
|
42
|
+
<div className={`${prefixCls}-slider-marks-item-label`}>{label}</div>
|
|
43
|
+
)}
|
|
44
|
+
</div>
|
|
45
|
+
))}
|
|
46
|
+
</div>
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export default Marks;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import React, { useContext, ReactNode, useRef } from 'react';
|
|
2
|
+
import Transition from '../transition';
|
|
3
|
+
import { GlobalContext } from '../context-provider';
|
|
4
|
+
|
|
5
|
+
export function Popover({
|
|
6
|
+
visible,
|
|
7
|
+
content,
|
|
8
|
+
children,
|
|
9
|
+
}: {
|
|
10
|
+
visible: boolean;
|
|
11
|
+
content: ReactNode;
|
|
12
|
+
children: ReactNode;
|
|
13
|
+
}) {
|
|
14
|
+
const { prefixCls = '' } = useContext(GlobalContext);
|
|
15
|
+
const domRef = useRef<HTMLDivElement | null>(null);
|
|
16
|
+
|
|
17
|
+
return (
|
|
18
|
+
<div className={`${prefixCls}-slider-popover-wrapper`}>
|
|
19
|
+
<Transition
|
|
20
|
+
in={visible}
|
|
21
|
+
timeout={300}
|
|
22
|
+
type="fade"
|
|
23
|
+
mountOnEnter
|
|
24
|
+
unmountOnExit
|
|
25
|
+
nodeRef={domRef}
|
|
26
|
+
>
|
|
27
|
+
<div
|
|
28
|
+
className={`${prefixCls}-slider-popover${content ? '' : ' no-content'}`}
|
|
29
|
+
ref={domRef}
|
|
30
|
+
>
|
|
31
|
+
<div className={`${prefixCls}-slider-popover-content`}>{content}</div>
|
|
32
|
+
<div className={`${prefixCls}-slider-popover-arrow`} />
|
|
33
|
+
</div>
|
|
34
|
+
</Transition>
|
|
35
|
+
{children}
|
|
36
|
+
</div>
|
|
37
|
+
);
|
|
38
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import React, { useMemo, CSSProperties, useContext } from 'react';
|
|
2
|
+
import { Popover } from './popover';
|
|
3
|
+
import { SliderContext } from './hooks';
|
|
4
|
+
import { GlobalContext } from '../context-provider';
|
|
5
|
+
|
|
6
|
+
function Thumb({
|
|
7
|
+
className = '',
|
|
8
|
+
value,
|
|
9
|
+
style,
|
|
10
|
+
isTouching,
|
|
11
|
+
idx,
|
|
12
|
+
}: {
|
|
13
|
+
className?: string;
|
|
14
|
+
value: number;
|
|
15
|
+
isTouching: boolean;
|
|
16
|
+
style?: CSSProperties;
|
|
17
|
+
idx: number;
|
|
18
|
+
}) {
|
|
19
|
+
const { prefixCls = '' } = useContext(GlobalContext);
|
|
20
|
+
const { showTooltip, renderThumb, formatTooltip, renderThumbPopover } =
|
|
21
|
+
useContext(SliderContext);
|
|
22
|
+
const isHidden = showTooltip === 'never';
|
|
23
|
+
|
|
24
|
+
const renderValue = useMemo(() => formatTooltip(value), [formatTooltip, value]);
|
|
25
|
+
const isTooltipVisible = useMemo(() => {
|
|
26
|
+
if (isHidden) return false;
|
|
27
|
+
if (showTooltip === 'always') return true;
|
|
28
|
+
return isTouching;
|
|
29
|
+
}, [showTooltip, isTouching]);
|
|
30
|
+
|
|
31
|
+
const thumbEl = typeof renderThumb === 'function' ? renderThumb(value) : renderThumb;
|
|
32
|
+
const popoverEl =
|
|
33
|
+
typeof renderThumbPopover === 'function' ? (
|
|
34
|
+
renderThumbPopover({ value, visible: isTooltipVisible, thumbEl, index: idx })
|
|
35
|
+
) : (
|
|
36
|
+
<Popover visible={isTooltipVisible} content={renderValue}>
|
|
37
|
+
{thumbEl}
|
|
38
|
+
</Popover>
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
return (
|
|
42
|
+
<div className={`${prefixCls}-slider-thumb-wrapper ${className}`} style={style}>
|
|
43
|
+
{isHidden ? thumbEl : popoverEl}
|
|
44
|
+
</div>
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export default Thumb;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import React, { useMemo, useContext, ReactNode } from 'react';
|
|
2
|
+
import { GlobalContext } from '../../context-provider';
|
|
3
|
+
import { SliderContext } from '.';
|
|
4
|
+
|
|
5
|
+
export const useSliderIcon = ({ valueGroup }: { valueGroup: number | [number, number] }) => {
|
|
6
|
+
const { prefixCls = '' } = useContext(GlobalContext);
|
|
7
|
+
|
|
8
|
+
const { max, suffixLabel, min, prefixLabel } = useContext(SliderContext);
|
|
9
|
+
const renderPrefixLabel = useMemo(() => {
|
|
10
|
+
if (!prefixLabel) return null;
|
|
11
|
+
let inner: ReactNode;
|
|
12
|
+
if (typeof prefixLabel === 'boolean' && prefixLabel) {
|
|
13
|
+
inner = min;
|
|
14
|
+
} else if (typeof prefixLabel === 'function') {
|
|
15
|
+
inner = prefixLabel(valueGroup);
|
|
16
|
+
} else {
|
|
17
|
+
inner = prefixLabel;
|
|
18
|
+
}
|
|
19
|
+
return <div className={`${prefixCls}-slider-label is-prefix`}>{inner}</div>;
|
|
20
|
+
}, [min, prefixLabel, valueGroup]);
|
|
21
|
+
|
|
22
|
+
const renderSuffixLabel = useMemo(() => {
|
|
23
|
+
if (!suffixLabel) return null;
|
|
24
|
+
let inner: ReactNode;
|
|
25
|
+
if (typeof suffixLabel === 'boolean' && suffixLabel) {
|
|
26
|
+
inner = max;
|
|
27
|
+
} else if (typeof suffixLabel === 'function') {
|
|
28
|
+
inner = suffixLabel(valueGroup);
|
|
29
|
+
} else {
|
|
30
|
+
inner = suffixLabel;
|
|
31
|
+
}
|
|
32
|
+
return <div className={`${prefixCls}-slider-label is-suffix`}>{inner}</div>;
|
|
33
|
+
}, [max, suffixLabel, valueGroup]);
|
|
34
|
+
return {
|
|
35
|
+
renderPrefixLabel,
|
|
36
|
+
renderSuffixLabel,
|
|
37
|
+
};
|
|
38
|
+
};
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import React, { useState, useEffect, useMemo, useContext, useCallback } from 'react';
|
|
2
|
+
import { SliderContext } from '.';
|
|
3
|
+
|
|
4
|
+
export const useSliderInit = ({ commonIsTouching }: { commonIsTouching: number }) => {
|
|
5
|
+
const ctx = useContext(SliderContext);
|
|
6
|
+
const { value, min, max, onAfterChange, onChange, useMarkOnly, marks, useRange } = ctx;
|
|
7
|
+
const baseValue = ctx?.value ?? ctx?.defaultValue ?? min;
|
|
8
|
+
const [valueGroup, setValueGroup] = useState<number | [number, number]>(baseValue);
|
|
9
|
+
const [firstRender, setFirstRender] = useState(false);
|
|
10
|
+
const [firstTouch, setFirstTouch] = useState(false);
|
|
11
|
+
|
|
12
|
+
// 基础标签数组
|
|
13
|
+
// @en Base mark list
|
|
14
|
+
const baseConfig = useMemo(() => {
|
|
15
|
+
if (marks === void 0) return [];
|
|
16
|
+
let res: number[] = [];
|
|
17
|
+
if (typeof marks === 'number') {
|
|
18
|
+
res = Array((max - min) / marks + 1)
|
|
19
|
+
.fill('')
|
|
20
|
+
.map((_, idx) => idx * marks);
|
|
21
|
+
} else if (Array.isArray(marks)) {
|
|
22
|
+
res = marks;
|
|
23
|
+
} else {
|
|
24
|
+
res = Object.keys(marks).map(e => parseInt(e));
|
|
25
|
+
}
|
|
26
|
+
return [...res].filter(e => e <= max && e >= min).sort((a, b) => (a > b ? 1 : -1));
|
|
27
|
+
}, [marks, max, min]);
|
|
28
|
+
|
|
29
|
+
const validate = useCallback(
|
|
30
|
+
(newValue: number) => {
|
|
31
|
+
if (typeof valueGroup === 'number') return newValue <= valueGroup;
|
|
32
|
+
const [small, large] = [...valueGroup].sort((a, b) => (a > b ? 1 : -1));
|
|
33
|
+
return newValue >= small && newValue <= large;
|
|
34
|
+
},
|
|
35
|
+
[valueGroup],
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
// 实际 mark 标签
|
|
39
|
+
// @en Actual mark tags
|
|
40
|
+
const runTimeConfig = useMemo(() => {
|
|
41
|
+
return baseConfig.map(e => {
|
|
42
|
+
const label =
|
|
43
|
+
marks && !Array.isArray(marks) && marks[String(e)] && typeof marks !== 'number' ? (
|
|
44
|
+
marks[String(e)]
|
|
45
|
+
) : (
|
|
46
|
+
<p>{e}</p>
|
|
47
|
+
);
|
|
48
|
+
return {
|
|
49
|
+
activated: validate(e),
|
|
50
|
+
value: e,
|
|
51
|
+
label,
|
|
52
|
+
};
|
|
53
|
+
});
|
|
54
|
+
}, [baseConfig, valueGroup, validate, marks]);
|
|
55
|
+
|
|
56
|
+
// 当前值最近的标签值
|
|
57
|
+
// @en The mark value closest to the current value
|
|
58
|
+
const formatMarksValue = useCallback(
|
|
59
|
+
(newValue: number | [number, number]) => {
|
|
60
|
+
if (!useMarkOnly || marks === void 0) return newValue;
|
|
61
|
+
const format = (v: number) => {
|
|
62
|
+
let idx: number | undefined;
|
|
63
|
+
baseConfig.forEach((e, i) => {
|
|
64
|
+
if (v >= e) {
|
|
65
|
+
idx = i;
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
if (idx !== void 0) {
|
|
69
|
+
const largerIdx = idx + 1;
|
|
70
|
+
if (
|
|
71
|
+
largerIdx < baseConfig.length &&
|
|
72
|
+
Math.abs(baseConfig[largerIdx] - v) < Math.abs(baseConfig[idx] - v)
|
|
73
|
+
) {
|
|
74
|
+
return baseConfig[largerIdx];
|
|
75
|
+
}
|
|
76
|
+
return baseConfig[idx];
|
|
77
|
+
}
|
|
78
|
+
return baseConfig[0];
|
|
79
|
+
};
|
|
80
|
+
if (typeof newValue === 'number') return format(newValue);
|
|
81
|
+
return newValue.map(v => format(v)) as [number, number];
|
|
82
|
+
},
|
|
83
|
+
[useMarkOnly, marks, baseConfig],
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* 标记第一次渲染,方便获取位置
|
|
88
|
+
* @en Mark the first render for easy location
|
|
89
|
+
*/
|
|
90
|
+
useEffect(() => {
|
|
91
|
+
if (commonIsTouching) setFirstTouch(true);
|
|
92
|
+
}, [commonIsTouching]);
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* 是否为 range,多个游标
|
|
96
|
+
* @en Whether it is range, multiple cursors
|
|
97
|
+
*/
|
|
98
|
+
const isMultiThumb = useMemo(
|
|
99
|
+
() => useRange || Array.isArray(baseValue),
|
|
100
|
+
[useRange, typeof baseValue === 'number'],
|
|
101
|
+
);
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* 为 range 组件初始化数据
|
|
105
|
+
* @en Initialize data for the range component
|
|
106
|
+
*/
|
|
107
|
+
useEffect(() => {
|
|
108
|
+
setFirstRender(true);
|
|
109
|
+
/**
|
|
110
|
+
* 初始化数据
|
|
111
|
+
* @en Initialize data
|
|
112
|
+
*/
|
|
113
|
+
const newValue =
|
|
114
|
+
isMultiThumb && typeof valueGroup === 'number'
|
|
115
|
+
? formatMarksValue([valueGroup, valueGroup])
|
|
116
|
+
: formatMarksValue(valueGroup);
|
|
117
|
+
setValueGroup(newValue);
|
|
118
|
+
}, [isMultiThumb]);
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* 监听数据 value 变化
|
|
122
|
+
* @en Monitor data value changes
|
|
123
|
+
*/
|
|
124
|
+
useEffect(() => {
|
|
125
|
+
if (value === void 0 || !firstRender || commonIsTouching !== -1) return;
|
|
126
|
+
const newValue =
|
|
127
|
+
isMultiThumb && typeof value === 'number'
|
|
128
|
+
? formatMarksValue([value, value])
|
|
129
|
+
: formatMarksValue(value);
|
|
130
|
+
setValueGroup(newValue);
|
|
131
|
+
// 如果受控需要还原
|
|
132
|
+
// @en If controlled, it needs to restore
|
|
133
|
+
}, [firstRender && value, commonIsTouching]);
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* 上报排序后的值
|
|
137
|
+
* @en report sorted value
|
|
138
|
+
*/
|
|
139
|
+
const formatValueGroup = useMemo(
|
|
140
|
+
() =>
|
|
141
|
+
typeof valueGroup === 'number'
|
|
142
|
+
? valueGroup
|
|
143
|
+
: ([...valueGroup].sort((a, b) => (a > b ? 1 : -1)) as [number, number]),
|
|
144
|
+
[valueGroup],
|
|
145
|
+
);
|
|
146
|
+
|
|
147
|
+
useEffect(() => {
|
|
148
|
+
if (!firstTouch) return;
|
|
149
|
+
if (commonIsTouching > -1) onChange(formatValueGroup);
|
|
150
|
+
}, [valueGroup]);
|
|
151
|
+
|
|
152
|
+
useEffect(() => {
|
|
153
|
+
if (!firstTouch) return;
|
|
154
|
+
if (commonIsTouching === -1) {
|
|
155
|
+
onAfterChange(formatMarksValue(formatValueGroup));
|
|
156
|
+
if (value === void 0) {
|
|
157
|
+
const newValue = formatMarksValue(valueGroup);
|
|
158
|
+
setValueGroup(newValue);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}, [commonIsTouching, formatMarksValue]);
|
|
162
|
+
|
|
163
|
+
return {
|
|
164
|
+
valueGroup,
|
|
165
|
+
setValueGroup,
|
|
166
|
+
firstRender,
|
|
167
|
+
runTimeConfig,
|
|
168
|
+
isMultiThumb,
|
|
169
|
+
};
|
|
170
|
+
};
|