@tarojs/components-react 4.2.1-beta.1 → 4.2.1-beta.3
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/dist/components/image/index.js +3 -5
- package/dist/components/image/index.js.map +1 -1
- package/dist/components/picker/index.js +35 -76
- package/dist/components/picker/index.js.map +1 -1
- package/dist/components/picker/picker-group.js +127 -477
- package/dist/components/picker/picker-group.js.map +1 -1
- package/dist/components/refresher/index.js +5 -5
- package/dist/components/refresher/index.js.map +1 -1
- package/dist/components/scroll-view/index.js +36 -80
- package/dist/components/scroll-view/index.js.map +1 -1
- package/dist/index.css +1 -1
- package/dist/index.js +2 -6
- package/dist/index.js.map +1 -1
- package/dist/original/components/image/index.js +3 -5
- package/dist/original/components/image/index.js.map +1 -1
- package/dist/original/components/image/style/index.scss +1 -5
- package/dist/original/components/picker/index.js +35 -76
- package/dist/original/components/picker/index.js.map +1 -1
- package/dist/original/components/picker/picker-group.js +127 -477
- package/dist/original/components/picker/picker-group.js.map +1 -1
- package/dist/original/components/picker/style/index.scss +8 -9
- package/dist/original/components/refresher/index.js +5 -5
- package/dist/original/components/refresher/index.js.map +1 -1
- package/dist/original/components/scroll-view/index.js +36 -80
- package/dist/original/components/scroll-view/index.js.map +1 -1
- package/dist/original/index.js +2 -6
- package/dist/original/index.js.map +1 -1
- package/dist/solid/components/image/index.js +3 -5
- package/dist/solid/components/image/index.js.map +1 -1
- package/dist/solid/components/picker/index.js +39 -82
- package/dist/solid/components/picker/index.js.map +1 -1
- package/dist/solid/components/picker/picker-group.js +151 -500
- package/dist/solid/components/picker/picker-group.js.map +1 -1
- package/dist/solid/components/refresher/index.js +5 -5
- package/dist/solid/components/refresher/index.js.map +1 -1
- package/dist/solid/components/scroll-view/index.js +40 -84
- package/dist/solid/components/scroll-view/index.js.map +1 -1
- package/dist/solid/index.css +1 -1
- package/dist/solid/index.js +1 -1
- package/package.json +6 -8
- package/dist/components/map/MapContext.js +0 -628
- package/dist/components/map/MapContext.js.map +0 -1
- package/dist/components/map/MapCustomCallout.js +0 -91
- package/dist/components/map/MapCustomCallout.js.map +0 -1
- package/dist/components/map/common.js +0 -4
- package/dist/components/map/common.js.map +0 -1
- package/dist/components/map/createMapContext.js +0 -34
- package/dist/components/map/createMapContext.js.map +0 -1
- package/dist/components/map/handler.js +0 -50
- package/dist/components/map/handler.js.map +0 -1
- package/dist/components/map/index.js +0 -329
- package/dist/components/map/index.js.map +0 -1
- package/dist/components/switch/index.js +0 -125
- package/dist/components/switch/index.js.map +0 -1
- package/dist/components/switch/style/index.scss.js +0 -4
- package/dist/components/switch/style/index.scss.js.map +0 -1
- package/dist/contexts/ScrollElementContext.js +0 -6
- package/dist/contexts/ScrollElementContext.js.map +0 -1
- package/dist/original/components/map/MapContext.js +0 -628
- package/dist/original/components/map/MapContext.js.map +0 -1
- package/dist/original/components/map/MapCustomCallout.js +0 -91
- package/dist/original/components/map/MapCustomCallout.js.map +0 -1
- package/dist/original/components/map/common.js +0 -4
- package/dist/original/components/map/common.js.map +0 -1
- package/dist/original/components/map/createMapContext.js +0 -34
- package/dist/original/components/map/createMapContext.js.map +0 -1
- package/dist/original/components/map/handler.js +0 -50
- package/dist/original/components/map/handler.js.map +0 -1
- package/dist/original/components/map/index.js +0 -329
- package/dist/original/components/map/index.js.map +0 -1
- package/dist/original/components/switch/index.js +0 -125
- package/dist/original/components/switch/index.js.map +0 -1
- package/dist/original/components/switch/style/index.scss +0 -35
- package/dist/original/contexts/ScrollElementContext.js +0 -6
- package/dist/original/contexts/ScrollElementContext.js.map +0 -1
- package/dist/solid/contexts/ScrollElementContext.js +0 -6
- package/dist/solid/contexts/ScrollElementContext.js.map +0 -1
|
@@ -3,31 +3,6 @@ import { View, ScrollView } from '@tarojs/components';
|
|
|
3
3
|
import Taro from '@tarojs/taro';
|
|
4
4
|
import * as React from 'react';
|
|
5
5
|
|
|
6
|
-
function requestAccessibilityFocusOnView(node) {
|
|
7
|
-
if (node == null) return;
|
|
8
|
-
const fn = Taro.setAccessibilityFocus;
|
|
9
|
-
if (typeof fn !== 'function') return;
|
|
10
|
-
fn({
|
|
11
|
-
viewRef: {
|
|
12
|
-
current: node
|
|
13
|
-
}
|
|
14
|
-
});
|
|
15
|
-
}
|
|
16
|
-
/** 部分端同 id 连续 scrollIntoView 不生效:先清空再在下一宏任务设回目标 id */
|
|
17
|
-
function usePickerItemScrollIntoView() {
|
|
18
|
-
const [scrollIntoView, setScrollIntoView] = React.useState('');
|
|
19
|
-
const pulseRef = React.useRef(0);
|
|
20
|
-
const scrollToItemId = React.useCallback(itemId => {
|
|
21
|
-
pulseRef.current += 1;
|
|
22
|
-
const token = pulseRef.current;
|
|
23
|
-
setScrollIntoView('');
|
|
24
|
-
setTimeout(() => {
|
|
25
|
-
if (pulseRef.current !== token) return;
|
|
26
|
-
setScrollIntoView(itemId);
|
|
27
|
-
}, 0);
|
|
28
|
-
}, []);
|
|
29
|
-
return [scrollIntoView, scrollToItemId];
|
|
30
|
-
}
|
|
31
6
|
// 定义常量
|
|
32
7
|
const PICKER_LINE_HEIGHT = 34; // px
|
|
33
8
|
const PICKER_VISIBLE_ITEMS = 7; // 可见行数
|
|
@@ -38,85 +13,42 @@ const getIndicatorStyle = lineColor => {
|
|
|
38
13
|
borderBottomColor: lineColor
|
|
39
14
|
};
|
|
40
15
|
};
|
|
41
|
-
// 大屏方案版本要求
|
|
42
|
-
const MIN_DESIGN_APP_VERSION = 16;
|
|
43
|
-
// 判断是否启用测量值缩放适配(true=启用, false=使用系统侧缩放)
|
|
44
|
-
const resolveUseMeasuredScale = res => {
|
|
45
|
-
// H5/weapp 不参与大屏系数
|
|
46
|
-
if (process.env.TARO_ENV === 'h5' || process.env.TARO_ENV === 'weapp') {
|
|
47
|
-
return false;
|
|
48
|
-
}
|
|
49
|
-
const designAppVersionRaw = res.designAppVersion;
|
|
50
|
-
const designAppVersionMajor = designAppVersionRaw != null ? parseInt(String(designAppVersionRaw).trim(), 10) : Number.NaN;
|
|
51
|
-
if (!Number.isFinite(designAppVersionMajor) || designAppVersionMajor < MIN_DESIGN_APP_VERSION) {
|
|
52
|
-
return false;
|
|
53
|
-
}
|
|
54
|
-
const platform = String(res.platform || '').toLowerCase();
|
|
55
|
-
if (platform === 'harmony' || platform === 'android' || platform === 'ios') {
|
|
56
|
-
return true;
|
|
57
|
-
}
|
|
58
|
-
return false;
|
|
59
|
-
};
|
|
60
|
-
// 辅助函数:计算 lengthScaleRatio
|
|
61
|
-
const calculateLengthScaleRatio = res => {
|
|
62
|
-
let lengthScaleRatio = res === null || res === void 0 ? void 0 : res.lengthScaleRatio;
|
|
63
|
-
if (lengthScaleRatio == null || lengthScaleRatio === 0) {
|
|
64
|
-
console.warn('Taro.getSystemInfo: lengthScaleRatio 不存在,使用计算值');
|
|
65
|
-
lengthScaleRatio = 1;
|
|
66
|
-
if (res.windowWidth < 320) {
|
|
67
|
-
lengthScaleRatio = res.windowWidth / 320;
|
|
68
|
-
} else if (res.windowWidth >= 400 && res.windowWidth < 600) {
|
|
69
|
-
lengthScaleRatio = res.windowWidth / 400;
|
|
70
|
-
}
|
|
71
|
-
const shortSide = res.windowWidth < res.windowHeight ? res.windowWidth : res.windowHeight;
|
|
72
|
-
const isBigScreen = shortSide >= 600;
|
|
73
|
-
if (isBigScreen) {
|
|
74
|
-
lengthScaleRatio = shortSide / 720;
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
return lengthScaleRatio;
|
|
78
|
-
};
|
|
79
16
|
// 辅助函数:获取系统信息的 lengthScaleRatio 并设置 targetScrollTop
|
|
80
|
-
const setTargetScrollTopWithScale =
|
|
81
|
-
let lengthScaleRatio = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 1;
|
|
17
|
+
const setTargetScrollTopWithScale = (setTargetScrollTop, baseValue, randomOffset) => {
|
|
82
18
|
// H5 和 weapp 不参与放大计算,直接使用 baseValue
|
|
83
19
|
if (process.env.TARO_ENV === 'h5' || process.env.TARO_ENV === 'weapp') {
|
|
84
20
|
const finalValue = randomOffset !== undefined ? baseValue + randomOffset : baseValue;
|
|
85
21
|
setTargetScrollTop(finalValue);
|
|
86
22
|
return;
|
|
87
23
|
}
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
return useMeasuredScale ? scrollView.scrollHeight / lengthScaleRatio / scrollView.childNodes.length : scrollView.scrollHeight / scrollView.childNodes.length;
|
|
117
|
-
}
|
|
118
|
-
console.warn('Height measurement anomaly');
|
|
119
|
-
return PICKER_LINE_HEIGHT;
|
|
24
|
+
Taro.getSystemInfo({
|
|
25
|
+
success: res => {
|
|
26
|
+
let lengthScaleRatio = res === null || res === void 0 ? void 0 : res.lengthScaleRatio;
|
|
27
|
+
if (lengthScaleRatio == null || lengthScaleRatio === 0) {
|
|
28
|
+
console.warn('Taro.getSystemInfo: lengthScaleRatio 不存在,使用计算值');
|
|
29
|
+
lengthScaleRatio = 1;
|
|
30
|
+
if (res.windowWidth < 320) {
|
|
31
|
+
lengthScaleRatio = res.windowWidth / 320;
|
|
32
|
+
} else if (res.windowWidth >= 400 && res.windowWidth < 600) {
|
|
33
|
+
lengthScaleRatio = res.windowWidth / 400;
|
|
34
|
+
}
|
|
35
|
+
const shortSide = res.windowWidth < res.windowHeight ? res.windowWidth : res.windowHeight;
|
|
36
|
+
const isBigScreen = shortSide >= 600;
|
|
37
|
+
if (isBigScreen) {
|
|
38
|
+
lengthScaleRatio = shortSide / 720;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
const scaledValue = baseValue * lengthScaleRatio;
|
|
42
|
+
const finalValue = randomOffset !== undefined ? scaledValue + randomOffset : scaledValue;
|
|
43
|
+
setTargetScrollTop(finalValue);
|
|
44
|
+
},
|
|
45
|
+
fail: err => {
|
|
46
|
+
console.error('获取系统信息失败:', err);
|
|
47
|
+
// 失败时使用默认值 1
|
|
48
|
+
const finalValue = randomOffset !== undefined ? baseValue + randomOffset : baseValue;
|
|
49
|
+
setTargetScrollTop(finalValue);
|
|
50
|
+
}
|
|
51
|
+
});
|
|
120
52
|
};
|
|
121
53
|
function PickerGroupBasic(props) {
|
|
122
54
|
const {
|
|
@@ -127,89 +59,66 @@ function PickerGroupBasic(props) {
|
|
|
127
59
|
onColumnChange,
|
|
128
60
|
selectedIndex = 0,
|
|
129
61
|
// 使用selectedIndex参数,默认为0
|
|
130
|
-
colors = {}
|
|
131
|
-
enableClickItemScroll = true
|
|
62
|
+
colors = {}
|
|
132
63
|
} = props;
|
|
133
64
|
const indicatorStyle = colors.lineColor ? getIndicatorStyle(colors.lineColor) : null;
|
|
134
65
|
const [targetScrollTop, setTargetScrollTop] = React.useState(0);
|
|
135
|
-
const [scrollIntoView, scrollToItemId] = usePickerItemScrollIntoView();
|
|
136
66
|
const scrollViewRef = React.useRef(null);
|
|
137
67
|
const itemRefs = React.useRef([]);
|
|
138
|
-
const selectedIndexPropRef = React.useRef(selectedIndex);
|
|
139
|
-
selectedIndexPropRef.current = selectedIndex;
|
|
140
|
-
const syncScrollFromPropsRef = React.useRef(false);
|
|
141
|
-
const pendingScrollSettleFocusRef = React.useRef(false);
|
|
142
68
|
// 使用selectedIndex初始化当前索引
|
|
143
69
|
const [currentIndex, setCurrentIndex] = React.useState(selectedIndex);
|
|
144
70
|
// 触摸状态用于优化用户体验
|
|
145
|
-
const
|
|
146
|
-
const lengthScaleRatioRef = React.useRef(1);
|
|
147
|
-
const useMeasuredScaleRef = React.useRef(false);
|
|
71
|
+
const [isTouching, setIsTouching] = React.useState(false);
|
|
148
72
|
const itemHeightRef = React.useRef(PICKER_LINE_HEIGHT);
|
|
149
|
-
// 初始化时计算 lengthScaleRatio 并判定缩放模式
|
|
150
73
|
React.useEffect(() => {
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
fail: () => {
|
|
158
|
-
lengthScaleRatioRef.current = 1;
|
|
159
|
-
useMeasuredScaleRef.current = false;
|
|
74
|
+
var _a;
|
|
75
|
+
if (process.env.TARO_PLATFORM !== 'harmony') {
|
|
76
|
+
if (scrollViewRef.current && ((_a = scrollViewRef.current) === null || _a === void 0 ? void 0 : _a.scrollHeight)) {
|
|
77
|
+
itemHeightRef.current = scrollViewRef.current.scrollHeight / scrollViewRef.current.childNodes.length;
|
|
78
|
+
} else {
|
|
79
|
+
console.warn('Height measurement anomaly');
|
|
160
80
|
}
|
|
161
|
-
}
|
|
162
|
-
}, []);
|
|
163
|
-
React.useEffect(() => {
|
|
164
|
-
itemHeightRef.current = calculateItemHeight(scrollViewRef.current, lengthScaleRatioRef.current, useMeasuredScaleRef.current);
|
|
81
|
+
}
|
|
165
82
|
}, [range.length]); // 只在range长度变化时重新计算
|
|
166
|
-
//
|
|
83
|
+
// 获取选中的索引
|
|
84
|
+
const getSelectedIndex = scrollTop => {
|
|
85
|
+
return Math.round(scrollTop / itemHeightRef.current);
|
|
86
|
+
};
|
|
87
|
+
// 当selectedIndex变化时,调整滚动位置
|
|
167
88
|
React.useEffect(() => {
|
|
168
|
-
if (scrollViewRef.current && range.length > 0 && !
|
|
169
|
-
syncScrollFromPropsRef.current = true;
|
|
89
|
+
if (scrollViewRef.current && range.length > 0 && !isTouching) {
|
|
170
90
|
const baseValue = selectedIndex * itemHeightRef.current;
|
|
171
|
-
setTargetScrollTopWithScale(setTargetScrollTop, baseValue
|
|
91
|
+
setTargetScrollTopWithScale(setTargetScrollTop, baseValue);
|
|
172
92
|
setCurrentIndex(selectedIndex);
|
|
173
|
-
const tid = setTimeout(() => {
|
|
174
|
-
syncScrollFromPropsRef.current = false;
|
|
175
|
-
}, 400);
|
|
176
|
-
return () => clearTimeout(tid);
|
|
177
93
|
}
|
|
178
94
|
}, [selectedIndex, range]);
|
|
179
95
|
// 是否处于归中状态
|
|
180
96
|
const isCenterTimerId = React.useRef(null);
|
|
181
|
-
//
|
|
97
|
+
// 简化为直接在滚动结束时通知父组件
|
|
182
98
|
const handleScrollEnd = () => {
|
|
183
99
|
if (!scrollViewRef.current) return;
|
|
184
100
|
if (isCenterTimerId.current) {
|
|
185
101
|
clearTimeout(isCenterTimerId.current);
|
|
186
102
|
isCenterTimerId.current = null;
|
|
187
103
|
}
|
|
188
|
-
//
|
|
104
|
+
// 做一个0.1s延时 0.1s之内没有新的滑动 则把选项归到中间 然后更新选中项
|
|
189
105
|
isCenterTimerId.current = setTimeout(() => {
|
|
190
106
|
if (!scrollViewRef.current) return;
|
|
191
107
|
const scrollTop = scrollViewRef.current.scrollTop;
|
|
192
|
-
const newIndex = getSelectedIndex(scrollTop
|
|
193
|
-
|
|
194
|
-
const shouldFocusOnScrollSettle = pendingScrollSettleFocusRef.current;
|
|
195
|
-
isTouchingRef.current = false;
|
|
108
|
+
const newIndex = getSelectedIndex(scrollTop);
|
|
109
|
+
setIsTouching(false);
|
|
196
110
|
const baseValue = newIndex * itemHeightRef.current;
|
|
197
111
|
const randomOffset = Math.random() * 0.001; // 随机数为了在一个项内滚动时强制刷新
|
|
198
|
-
setTargetScrollTopWithScale(setTargetScrollTop, baseValue, randomOffset
|
|
112
|
+
setTargetScrollTopWithScale(setTargetScrollTop, baseValue, randomOffset);
|
|
199
113
|
updateIndex(newIndex, columnId);
|
|
200
114
|
onColumnChange === null || onColumnChange === void 0 ? void 0 : onColumnChange({
|
|
201
115
|
columnId,
|
|
202
116
|
index: newIndex
|
|
203
117
|
});
|
|
204
|
-
pendingScrollSettleFocusRef.current = false;
|
|
205
|
-
if (allowA11yFocus && shouldFocusOnScrollSettle) {
|
|
206
|
-
requestAccessibilityFocusOnView(itemRefs.current[newIndex]);
|
|
207
|
-
}
|
|
208
|
-
syncScrollFromPropsRef.current = false;
|
|
209
118
|
isCenterTimerId.current = null;
|
|
210
119
|
}, 100);
|
|
211
120
|
};
|
|
212
|
-
//
|
|
121
|
+
// 滚动处理 - 在滚动时计算索引然后更新选中项样式
|
|
213
122
|
const handleScroll = () => {
|
|
214
123
|
if (!scrollViewRef.current) return;
|
|
215
124
|
if (isCenterTimerId.current) {
|
|
@@ -217,17 +126,7 @@ function PickerGroupBasic(props) {
|
|
|
217
126
|
isCenterTimerId.current = null;
|
|
218
127
|
}
|
|
219
128
|
const scrollTop = scrollViewRef.current.scrollTop;
|
|
220
|
-
const
|
|
221
|
-
const newIndex = getSelectedIndex(scrollTop, ih, lengthScaleRatioRef.current, useMeasuredScaleRef.current);
|
|
222
|
-
const spi = selectedIndexPropRef.current;
|
|
223
|
-
if (newIndex !== spi) {
|
|
224
|
-
if (!syncScrollFromPropsRef.current || isTouchingRef.current) {
|
|
225
|
-
pendingScrollSettleFocusRef.current = true;
|
|
226
|
-
}
|
|
227
|
-
if (isTouchingRef.current) {
|
|
228
|
-
syncScrollFromPropsRef.current = false;
|
|
229
|
-
}
|
|
230
|
-
}
|
|
129
|
+
const newIndex = getSelectedIndex(scrollTop);
|
|
231
130
|
if (newIndex !== currentIndex) {
|
|
232
131
|
setCurrentIndex(newIndex);
|
|
233
132
|
}
|
|
@@ -235,14 +134,11 @@ function PickerGroupBasic(props) {
|
|
|
235
134
|
// 渲染选项
|
|
236
135
|
const pickerItem = range.map((item, index) => {
|
|
237
136
|
const content = rangeKey && item && typeof item === 'object' ? item[rangeKey] : item;
|
|
238
|
-
return createComponent(View,
|
|
137
|
+
return createComponent(View, {
|
|
239
138
|
id: `picker-item-${columnId}-${index}`,
|
|
240
139
|
key: index,
|
|
241
140
|
ref: el => itemRefs.current[index] = el,
|
|
242
|
-
className: `taro-picker__item${index === currentIndex ? ' taro-picker__item--selected' : ''}
|
|
243
|
-
}, enableClickItemScroll ? {
|
|
244
|
-
onClick: () => scrollToItemId(`picker-item-${columnId}-${index}`)
|
|
245
|
-
} : {}, {
|
|
141
|
+
className: `taro-picker__item${index === currentIndex ? ' taro-picker__item--selected' : ''}`,
|
|
246
142
|
get style() {
|
|
247
143
|
return {
|
|
248
144
|
height: PICKER_LINE_HEIGHT,
|
|
@@ -250,7 +146,7 @@ function PickerGroupBasic(props) {
|
|
|
250
146
|
};
|
|
251
147
|
},
|
|
252
148
|
children: content
|
|
253
|
-
})
|
|
149
|
+
});
|
|
254
150
|
});
|
|
255
151
|
const realPickerItems = [...new Array(PICKER_BLANK_ITEMS).fill(null).map((_, idx) => createComponent(View, {
|
|
256
152
|
key: `blank-top-${idx}`,
|
|
@@ -265,10 +161,6 @@ function PickerGroupBasic(props) {
|
|
|
265
161
|
height: PICKER_LINE_HEIGHT
|
|
266
162
|
}
|
|
267
163
|
}))];
|
|
268
|
-
const clickScrollViewProps = enableClickItemScroll ? {
|
|
269
|
-
scrollIntoView,
|
|
270
|
-
scrollIntoViewAlignment: 'center'
|
|
271
|
-
} : {};
|
|
272
164
|
return createComponent(View, {
|
|
273
165
|
className: "taro-picker__group",
|
|
274
166
|
get children() {
|
|
@@ -278,7 +170,7 @@ function PickerGroupBasic(props) {
|
|
|
278
170
|
className: "taro-picker__indicator"
|
|
279
171
|
}, indicatorStyle ? {
|
|
280
172
|
style: indicatorStyle
|
|
281
|
-
} : {})), createComponent(ScrollView,
|
|
173
|
+
} : {})), createComponent(ScrollView, {
|
|
282
174
|
ref: scrollViewRef,
|
|
283
175
|
scrollY: true,
|
|
284
176
|
showScrollbar: false,
|
|
@@ -286,16 +178,13 @@ function PickerGroupBasic(props) {
|
|
|
286
178
|
style: {
|
|
287
179
|
height: PICKER_LINE_HEIGHT * PICKER_VISIBLE_ITEMS
|
|
288
180
|
},
|
|
289
|
-
scrollTop: targetScrollTop
|
|
290
|
-
}, clickScrollViewProps, {
|
|
181
|
+
scrollTop: targetScrollTop,
|
|
291
182
|
onScroll: handleScroll,
|
|
292
|
-
onTouchStart: () =>
|
|
293
|
-
isTouchingRef.current = true;
|
|
294
|
-
},
|
|
183
|
+
onTouchStart: () => setIsTouching(true),
|
|
295
184
|
onScrollEnd: handleScrollEnd,
|
|
296
185
|
scrollWithAnimation: true,
|
|
297
186
|
children: realPickerItems
|
|
298
|
-
})
|
|
187
|
+
})];
|
|
299
188
|
}
|
|
300
189
|
});
|
|
301
190
|
}
|
|
@@ -307,189 +196,63 @@ function PickerGroupTime(props) {
|
|
|
307
196
|
columnId,
|
|
308
197
|
updateIndex,
|
|
309
198
|
selectedIndex = 0,
|
|
310
|
-
colors = {}
|
|
311
|
-
timeA11yLimitFocus,
|
|
312
|
-
onTimeA11yLimitFocusConsumed,
|
|
313
|
-
timeA11yLimitEventNonce,
|
|
314
|
-
enableClickItemScroll = true
|
|
199
|
+
colors = {}
|
|
315
200
|
} = props;
|
|
316
201
|
const indicatorStyle = colors.lineColor ? getIndicatorStyle(colors.lineColor) : null;
|
|
317
202
|
const [targetScrollTop, setTargetScrollTop] = React.useState(0);
|
|
318
|
-
const [scrollIntoView, scrollToItemId] = usePickerItemScrollIntoView();
|
|
319
203
|
const scrollViewRef = React.useRef(null);
|
|
320
204
|
const itemRefs = React.useRef([]);
|
|
321
|
-
const selectedIndexPropRef = React.useRef(selectedIndex);
|
|
322
|
-
selectedIndexPropRef.current = selectedIndex;
|
|
323
|
-
const syncScrollFromPropsRef = React.useRef(false);
|
|
324
|
-
const prevLimitEventNonceRef = React.useRef(undefined);
|
|
325
|
-
const suppressScrollSettleFocusNonceRef = React.useRef(null);
|
|
326
|
-
const pendingScrollSettleFocusRef = React.useRef(false);
|
|
327
|
-
const pendingLimitFocusRef = React.useRef(null);
|
|
328
|
-
const limitFocusTimerRef = React.useRef(null);
|
|
329
205
|
const [currentIndex, setCurrentIndex] = React.useState(selectedIndex);
|
|
330
|
-
const
|
|
331
|
-
const lengthScaleRatioRef = React.useRef(1);
|
|
332
|
-
const useMeasuredScaleRef = React.useRef(false);
|
|
206
|
+
const [isTouching, setIsTouching] = React.useState(false);
|
|
333
207
|
const itemHeightRef = React.useRef(PICKER_LINE_HEIGHT);
|
|
334
|
-
const clearLimitFocusTimer = React.useCallback(() => {
|
|
335
|
-
if (limitFocusTimerRef.current) {
|
|
336
|
-
clearTimeout(limitFocusTimerRef.current);
|
|
337
|
-
limitFocusTimerRef.current = null;
|
|
338
|
-
}
|
|
339
|
-
}, []);
|
|
340
|
-
const tryFocusPendingLimit = React.useCallback(() => {
|
|
341
|
-
const pending = pendingLimitFocusRef.current;
|
|
342
|
-
const scrollView = scrollViewRef.current;
|
|
343
|
-
if (!pending || !scrollView) return;
|
|
344
|
-
const visualIndex = getSelectedIndex(scrollView.scrollTop, itemHeightRef.current, lengthScaleRatioRef.current, useMeasuredScaleRef.current);
|
|
345
|
-
const isStable = visualIndex === pending.index;
|
|
346
|
-
if (!isStable) {
|
|
347
|
-
if (pending.attempts >= 20) {
|
|
348
|
-
pendingLimitFocusRef.current = null;
|
|
349
|
-
if (suppressScrollSettleFocusNonceRef.current === pending.nonce) {
|
|
350
|
-
suppressScrollSettleFocusNonceRef.current = null;
|
|
351
|
-
}
|
|
352
|
-
onTimeA11yLimitFocusConsumed === null || onTimeA11yLimitFocusConsumed === void 0 ? void 0 : onTimeA11yLimitFocusConsumed();
|
|
353
|
-
return;
|
|
354
|
-
}
|
|
355
|
-
pending.attempts += 1;
|
|
356
|
-
clearLimitFocusTimer();
|
|
357
|
-
limitFocusTimerRef.current = setTimeout(() => {
|
|
358
|
-
tryFocusPendingLimit();
|
|
359
|
-
}, 50);
|
|
360
|
-
return;
|
|
361
|
-
}
|
|
362
|
-
const node = itemRefs.current[pending.index];
|
|
363
|
-
pendingLimitFocusRef.current = null;
|
|
364
|
-
if (suppressScrollSettleFocusNonceRef.current === pending.nonce) {
|
|
365
|
-
suppressScrollSettleFocusNonceRef.current = null;
|
|
366
|
-
}
|
|
367
|
-
clearLimitFocusTimer();
|
|
368
|
-
requestAccessibilityFocusOnView(node);
|
|
369
|
-
onTimeA11yLimitFocusConsumed === null || onTimeA11yLimitFocusConsumed === void 0 ? void 0 : onTimeA11yLimitFocusConsumed();
|
|
370
|
-
}, [clearLimitFocusTimer, onTimeA11yLimitFocusConsumed]);
|
|
371
|
-
const schedulePendingLimitFocus = React.useCallback(() => {
|
|
372
|
-
if (!pendingLimitFocusRef.current) return;
|
|
373
|
-
clearLimitFocusTimer();
|
|
374
|
-
limitFocusTimerRef.current = setTimeout(() => {
|
|
375
|
-
tryFocusPendingLimit();
|
|
376
|
-
}, 100);
|
|
377
|
-
}, [clearLimitFocusTimer, tryFocusPendingLimit]);
|
|
378
|
-
React.useEffect(() => clearLimitFocusTimer, [clearLimitFocusTimer]);
|
|
379
|
-
// 初始化时计算 lengthScaleRatio 并判定缩放模式
|
|
380
208
|
React.useEffect(() => {
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
fail: () => {
|
|
388
|
-
lengthScaleRatioRef.current = 1;
|
|
389
|
-
useMeasuredScaleRef.current = false;
|
|
209
|
+
var _a;
|
|
210
|
+
if (process.env.TARO_PLATFORM !== 'harmony') {
|
|
211
|
+
if (scrollViewRef.current && ((_a = scrollViewRef.current) === null || _a === void 0 ? void 0 : _a.scrollHeight)) {
|
|
212
|
+
itemHeightRef.current = scrollViewRef.current.scrollHeight / scrollViewRef.current.childNodes.length;
|
|
213
|
+
} else {
|
|
214
|
+
console.warn('Height measurement anomaly');
|
|
390
215
|
}
|
|
391
|
-
}
|
|
392
|
-
}, []);
|
|
393
|
-
React.useEffect(() => {
|
|
394
|
-
itemHeightRef.current = calculateItemHeight(scrollViewRef.current, lengthScaleRatioRef.current, useMeasuredScaleRef.current);
|
|
216
|
+
}
|
|
395
217
|
}, [range.length]); // 只在range长度变化时重新计算
|
|
396
|
-
|
|
218
|
+
const getSelectedIndex = scrollTop => {
|
|
219
|
+
return Math.round(scrollTop / itemHeightRef.current);
|
|
220
|
+
};
|
|
221
|
+
// 当selectedIndex变化时,调整滚动位置
|
|
397
222
|
React.useEffect(() => {
|
|
398
|
-
if (scrollViewRef.current && range.length > 0 && !
|
|
399
|
-
syncScrollFromPropsRef.current = true;
|
|
223
|
+
if (scrollViewRef.current && range.length > 0 && !isTouching) {
|
|
400
224
|
const baseValue = selectedIndex * itemHeightRef.current;
|
|
401
|
-
setTargetScrollTopWithScale(setTargetScrollTop, baseValue
|
|
225
|
+
setTargetScrollTopWithScale(setTargetScrollTop, baseValue);
|
|
402
226
|
setCurrentIndex(selectedIndex);
|
|
403
|
-
const tid = setTimeout(() => {
|
|
404
|
-
syncScrollFromPropsRef.current = false;
|
|
405
|
-
}, 400);
|
|
406
|
-
return () => clearTimeout(tid);
|
|
407
227
|
}
|
|
408
228
|
}, [selectedIndex, range]);
|
|
409
|
-
// time 限位 nonce 变更:强制对齐 scrollTop(避免 remount 打断读屏焦点)
|
|
410
|
-
React.useEffect(() => {
|
|
411
|
-
if (!timeA11yLimitEventNonce) return;
|
|
412
|
-
if (!scrollViewRef.current || range.length === 0 || isTouchingRef.current) return;
|
|
413
|
-
syncScrollFromPropsRef.current = true;
|
|
414
|
-
const baseValue = selectedIndex * itemHeightRef.current;
|
|
415
|
-
setTargetScrollTopWithScale(setTargetScrollTop, baseValue, Math.random() * 0.001, lengthScaleRatioRef.current);
|
|
416
|
-
setCurrentIndex(selectedIndex);
|
|
417
|
-
const tid = setTimeout(() => {
|
|
418
|
-
syncScrollFromPropsRef.current = false;
|
|
419
|
-
}, 400);
|
|
420
|
-
return () => clearTimeout(tid);
|
|
421
|
-
}, [timeA11yLimitEventNonce]); // 仅依赖 nonce,其余用 ref
|
|
422
|
-
React.useLayoutEffect(() => {
|
|
423
|
-
if (timeA11yLimitEventNonce === undefined) return;
|
|
424
|
-
const prev = prevLimitEventNonceRef.current;
|
|
425
|
-
prevLimitEventNonceRef.current = timeA11yLimitEventNonce;
|
|
426
|
-
if (timeA11yLimitEventNonce > 0 && (prev === undefined || timeA11yLimitEventNonce > prev)) {
|
|
427
|
-
suppressScrollSettleFocusNonceRef.current = timeA11yLimitEventNonce;
|
|
428
|
-
}
|
|
429
|
-
}, [timeA11yLimitEventNonce]);
|
|
430
|
-
// 限位后登记本列待聚焦项,稳定后再 requestAccessibilityFocus
|
|
431
|
-
React.useEffect(() => {
|
|
432
|
-
const req = timeA11yLimitFocus;
|
|
433
|
-
if (!req || req.columnId !== columnId) return;
|
|
434
|
-
const idx = selectedIndex;
|
|
435
|
-
const nonce = req.nonce;
|
|
436
|
-
pendingScrollSettleFocusRef.current = false;
|
|
437
|
-
pendingLimitFocusRef.current = {
|
|
438
|
-
index: idx,
|
|
439
|
-
nonce,
|
|
440
|
-
attempts: 0
|
|
441
|
-
};
|
|
442
|
-
schedulePendingLimitFocus();
|
|
443
|
-
}, [timeA11yLimitFocus, columnId, selectedIndex, schedulePendingLimitFocus]);
|
|
444
229
|
// 是否处于归中状态
|
|
445
230
|
const isCenterTimerId = React.useRef(null);
|
|
446
|
-
//
|
|
231
|
+
// 简化为直接在滚动结束时通知父组件
|
|
447
232
|
const handleScrollEnd = () => {
|
|
448
233
|
if (!scrollViewRef.current) return;
|
|
449
234
|
if (isCenterTimerId.current) {
|
|
450
235
|
clearTimeout(isCenterTimerId.current);
|
|
451
236
|
isCenterTimerId.current = null;
|
|
452
237
|
}
|
|
453
|
-
//
|
|
238
|
+
// 做一个0.1s延时 0.1s之内没有新的滑动 则把选项归到中间 然后更新选中项
|
|
454
239
|
isCenterTimerId.current = setTimeout(() => {
|
|
455
240
|
if (!scrollViewRef.current) return;
|
|
456
241
|
const scrollTop = scrollViewRef.current.scrollTop;
|
|
457
|
-
const newIndex = getSelectedIndex(scrollTop
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
isTouchingRef.current = false;
|
|
242
|
+
const newIndex = getSelectedIndex(scrollTop);
|
|
243
|
+
setIsTouching(false);
|
|
244
|
+
// 调用updateIndex执行限位逻辑,获取是否触发了限位
|
|
461
245
|
const isLimited = Boolean(updateIndex(newIndex, columnId, true));
|
|
462
|
-
|
|
463
|
-
if (isLimited) {
|
|
464
|
-
pendingScrollSettleFocusRef.current = false;
|
|
465
|
-
}
|
|
246
|
+
// 如果没有触发限位,才执行归中逻辑
|
|
466
247
|
if (!isLimited) {
|
|
467
248
|
const baseValue = newIndex * itemHeightRef.current;
|
|
468
249
|
const randomOffset = Math.random() * 0.001;
|
|
469
|
-
setTargetScrollTopWithScale(setTargetScrollTop, baseValue, randomOffset
|
|
470
|
-
}
|
|
471
|
-
if (!isLimited && hasPendingLimitFocus) {
|
|
472
|
-
pendingScrollSettleFocusRef.current = false;
|
|
473
|
-
schedulePendingLimitFocus();
|
|
474
|
-
syncScrollFromPropsRef.current = false;
|
|
475
|
-
isCenterTimerId.current = null;
|
|
476
|
-
return;
|
|
250
|
+
setTargetScrollTopWithScale(setTargetScrollTop, baseValue, randomOffset);
|
|
477
251
|
}
|
|
478
|
-
if (!isLimited && pendingLimitFocusRef.current == null && suppressScrollSettleFocusNonceRef.current != null) {
|
|
479
|
-
suppressScrollSettleFocusNonceRef.current = null;
|
|
480
|
-
}
|
|
481
|
-
const suppressByLimitNonce = suppressScrollSettleFocusNonceRef.current != null;
|
|
482
|
-
if (!isLimited && allowA11yFocus && shouldFocusOnScrollSettle) {
|
|
483
|
-
pendingScrollSettleFocusRef.current = false;
|
|
484
|
-
if (!suppressByLimitNonce) {
|
|
485
|
-
requestAccessibilityFocusOnView(itemRefs.current[newIndex]);
|
|
486
|
-
}
|
|
487
|
-
}
|
|
488
|
-
syncScrollFromPropsRef.current = false;
|
|
489
252
|
isCenterTimerId.current = null;
|
|
490
253
|
}, 100);
|
|
491
254
|
};
|
|
492
|
-
//
|
|
255
|
+
// 滚动处理
|
|
493
256
|
const handleScroll = () => {
|
|
494
257
|
if (!scrollViewRef.current) return;
|
|
495
258
|
if (isCenterTimerId.current) {
|
|
@@ -497,35 +260,19 @@ function PickerGroupTime(props) {
|
|
|
497
260
|
isCenterTimerId.current = null;
|
|
498
261
|
}
|
|
499
262
|
const scrollTop = scrollViewRef.current.scrollTop;
|
|
500
|
-
const
|
|
501
|
-
const newIndex = getSelectedIndex(scrollTop, ih, lengthScaleRatioRef.current, useMeasuredScaleRef.current);
|
|
502
|
-
const spi = selectedIndexPropRef.current;
|
|
503
|
-
if (newIndex !== spi) {
|
|
504
|
-
if (!syncScrollFromPropsRef.current || isTouchingRef.current) {
|
|
505
|
-
pendingScrollSettleFocusRef.current = true;
|
|
506
|
-
}
|
|
507
|
-
if (isTouchingRef.current) {
|
|
508
|
-
syncScrollFromPropsRef.current = false;
|
|
509
|
-
}
|
|
510
|
-
}
|
|
263
|
+
const newIndex = getSelectedIndex(scrollTop);
|
|
511
264
|
if (newIndex !== currentIndex) {
|
|
512
265
|
setCurrentIndex(newIndex);
|
|
513
266
|
}
|
|
514
|
-
if (pendingLimitFocusRef.current) {
|
|
515
|
-
schedulePendingLimitFocus();
|
|
516
|
-
}
|
|
517
267
|
};
|
|
518
268
|
// 渲染选项
|
|
519
269
|
const pickerItem = range.map((item, index) => {
|
|
520
270
|
const content = rangeKey && item && typeof item === 'object' ? item[rangeKey] : item;
|
|
521
|
-
return createComponent(View,
|
|
271
|
+
return createComponent(View, {
|
|
522
272
|
id: `picker-item-${columnId}-${index}`,
|
|
523
273
|
key: index,
|
|
524
274
|
ref: el => itemRefs.current[index] = el,
|
|
525
|
-
className: `taro-picker__item${index === currentIndex ? ' taro-picker__item--selected' : ''}
|
|
526
|
-
}, enableClickItemScroll ? {
|
|
527
|
-
onClick: () => scrollToItemId(`picker-item-${columnId}-${index}`)
|
|
528
|
-
} : {}, {
|
|
275
|
+
className: `taro-picker__item${index === currentIndex ? ' taro-picker__item--selected' : ''}`,
|
|
529
276
|
get style() {
|
|
530
277
|
return {
|
|
531
278
|
height: PICKER_LINE_HEIGHT,
|
|
@@ -533,7 +280,7 @@ function PickerGroupTime(props) {
|
|
|
533
280
|
};
|
|
534
281
|
},
|
|
535
282
|
children: content
|
|
536
|
-
})
|
|
283
|
+
});
|
|
537
284
|
});
|
|
538
285
|
const realPickerItems = [...new Array(PICKER_BLANK_ITEMS).fill(null).map((_, idx) => createComponent(View, {
|
|
539
286
|
key: `blank-top-${idx}`,
|
|
@@ -548,10 +295,6 @@ function PickerGroupTime(props) {
|
|
|
548
295
|
height: PICKER_LINE_HEIGHT
|
|
549
296
|
}
|
|
550
297
|
}))];
|
|
551
|
-
const clickScrollViewProps = enableClickItemScroll ? {
|
|
552
|
-
scrollIntoView,
|
|
553
|
-
scrollIntoViewAlignment: 'center'
|
|
554
|
-
} : {};
|
|
555
298
|
return createComponent(View, {
|
|
556
299
|
className: "taro-picker__group",
|
|
557
300
|
get children() {
|
|
@@ -561,7 +304,7 @@ function PickerGroupTime(props) {
|
|
|
561
304
|
className: "taro-picker__indicator"
|
|
562
305
|
}, indicatorStyle ? {
|
|
563
306
|
style: indicatorStyle
|
|
564
|
-
} : {})), createComponent(ScrollView,
|
|
307
|
+
} : {})), createComponent(ScrollView, {
|
|
565
308
|
ref: scrollViewRef,
|
|
566
309
|
scrollY: true,
|
|
567
310
|
showScrollbar: false,
|
|
@@ -569,16 +312,13 @@ function PickerGroupTime(props) {
|
|
|
569
312
|
style: {
|
|
570
313
|
height: PICKER_LINE_HEIGHT * PICKER_VISIBLE_ITEMS
|
|
571
314
|
},
|
|
572
|
-
scrollTop: targetScrollTop
|
|
573
|
-
}, clickScrollViewProps, {
|
|
315
|
+
scrollTop: targetScrollTop,
|
|
574
316
|
onScroll: handleScroll,
|
|
575
|
-
onTouchStart: () =>
|
|
576
|
-
isTouchingRef.current = true;
|
|
577
|
-
},
|
|
317
|
+
onTouchStart: () => setIsTouching(true),
|
|
578
318
|
onScrollEnd: handleScrollEnd,
|
|
579
319
|
scrollWithAnimation: true,
|
|
580
320
|
children: realPickerItems
|
|
581
|
-
})
|
|
321
|
+
})];
|
|
582
322
|
}
|
|
583
323
|
});
|
|
584
324
|
}
|
|
@@ -589,73 +329,53 @@ function PickerGroupDate(props) {
|
|
|
589
329
|
columnId,
|
|
590
330
|
updateDay,
|
|
591
331
|
selectedIndex = 0,
|
|
592
|
-
colors = {}
|
|
593
|
-
enableClickItemScroll = true
|
|
332
|
+
colors = {}
|
|
594
333
|
} = props;
|
|
595
334
|
const indicatorStyle = colors.lineColor ? getIndicatorStyle(colors.lineColor) : null;
|
|
596
335
|
const [targetScrollTop, setTargetScrollTop] = React.useState(0);
|
|
597
|
-
const [scrollIntoView, scrollToItemId] = usePickerItemScrollIntoView();
|
|
598
336
|
const scrollViewRef = React.useRef(null);
|
|
599
|
-
const itemRefs = React.useRef([]);
|
|
600
|
-
const selectedIndexPropRef = React.useRef(selectedIndex);
|
|
601
|
-
selectedIndexPropRef.current = selectedIndex;
|
|
602
|
-
const syncScrollFromPropsRef = React.useRef(false);
|
|
603
|
-
const pendingScrollSettleFocusRef = React.useRef(false);
|
|
604
337
|
const [currentIndex, setCurrentIndex] = React.useState(selectedIndex);
|
|
605
|
-
const
|
|
606
|
-
const lengthScaleRatioRef = React.useRef(1);
|
|
607
|
-
const useMeasuredScaleRef = React.useRef(false);
|
|
338
|
+
const [isTouching, setIsTouching] = React.useState(false);
|
|
608
339
|
const itemHeightRef = React.useRef(PICKER_LINE_HEIGHT);
|
|
609
|
-
// 初始化时计算 lengthScaleRatio 并判定缩放模式
|
|
610
340
|
React.useEffect(() => {
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
fail: () => {
|
|
618
|
-
lengthScaleRatioRef.current = 1;
|
|
619
|
-
useMeasuredScaleRef.current = false;
|
|
341
|
+
var _a;
|
|
342
|
+
if (process.env.TARO_PLATFORM !== 'harmony') {
|
|
343
|
+
if (scrollViewRef.current && ((_a = scrollViewRef.current) === null || _a === void 0 ? void 0 : _a.scrollHeight)) {
|
|
344
|
+
itemHeightRef.current = scrollViewRef.current.scrollHeight / scrollViewRef.current.childNodes.length;
|
|
345
|
+
} else {
|
|
346
|
+
console.warn('Height measurement anomaly');
|
|
620
347
|
}
|
|
621
|
-
}
|
|
622
|
-
}, []);
|
|
623
|
-
React.useEffect(() => {
|
|
624
|
-
itemHeightRef.current = calculateItemHeight(scrollViewRef.current, lengthScaleRatioRef.current, useMeasuredScaleRef.current);
|
|
348
|
+
}
|
|
625
349
|
}, [range.length]); // 只在range长度变化时重新计算
|
|
626
|
-
|
|
350
|
+
const getSelectedIndex = scrollTop => {
|
|
351
|
+
return Math.round(scrollTop / itemHeightRef.current);
|
|
352
|
+
};
|
|
353
|
+
// 当selectedIndex变化时,调整滚动位置
|
|
627
354
|
React.useEffect(() => {
|
|
628
|
-
if (scrollViewRef.current && range.length > 0 && !
|
|
629
|
-
syncScrollFromPropsRef.current = true;
|
|
355
|
+
if (scrollViewRef.current && range.length > 0 && !isTouching) {
|
|
630
356
|
const baseValue = selectedIndex * itemHeightRef.current;
|
|
631
|
-
setTargetScrollTopWithScale(setTargetScrollTop, baseValue
|
|
357
|
+
setTargetScrollTopWithScale(setTargetScrollTop, baseValue);
|
|
632
358
|
setCurrentIndex(selectedIndex);
|
|
633
|
-
const tid = setTimeout(() => {
|
|
634
|
-
syncScrollFromPropsRef.current = false;
|
|
635
|
-
}, 400);
|
|
636
|
-
return () => clearTimeout(tid);
|
|
637
359
|
}
|
|
638
360
|
}, [selectedIndex, range]);
|
|
639
361
|
// 是否处于归中状态
|
|
640
362
|
const isCenterTimerId = React.useRef(null);
|
|
641
|
-
//
|
|
363
|
+
// 简化为直接在滚动结束时通知父组件
|
|
642
364
|
const handleScrollEnd = () => {
|
|
643
365
|
if (!scrollViewRef.current) return;
|
|
644
366
|
if (isCenterTimerId.current) {
|
|
645
367
|
clearTimeout(isCenterTimerId.current);
|
|
646
368
|
isCenterTimerId.current = null;
|
|
647
369
|
}
|
|
648
|
-
//
|
|
370
|
+
// 做一个0.1s延时 0.1s之内没有新的滑动 则把选项归到中间 然后更新选中项
|
|
649
371
|
isCenterTimerId.current = setTimeout(() => {
|
|
650
372
|
if (!scrollViewRef.current) return;
|
|
651
373
|
const scrollTop = scrollViewRef.current.scrollTop;
|
|
652
|
-
const newIndex = getSelectedIndex(scrollTop
|
|
653
|
-
|
|
654
|
-
const shouldFocusOnScrollSettle = pendingScrollSettleFocusRef.current;
|
|
655
|
-
isTouchingRef.current = false;
|
|
374
|
+
const newIndex = getSelectedIndex(scrollTop);
|
|
375
|
+
setIsTouching(false);
|
|
656
376
|
const baseValue = newIndex * itemHeightRef.current;
|
|
657
377
|
const randomOffset = Math.random() * 0.001; // 随机数为了在一个项内滚动时强制刷新
|
|
658
|
-
setTargetScrollTopWithScale(setTargetScrollTop, baseValue, randomOffset
|
|
378
|
+
setTargetScrollTopWithScale(setTargetScrollTop, baseValue, randomOffset);
|
|
659
379
|
// 更新日期值
|
|
660
380
|
if (updateDay) {
|
|
661
381
|
// 解析文本中的数字(移除年、月、日等后缀)
|
|
@@ -663,15 +383,10 @@ function PickerGroupDate(props) {
|
|
|
663
383
|
const numericValue = parseInt(valueText.replace(/[^0-9]/g, ''));
|
|
664
384
|
updateDay(isNaN(numericValue) ? 0 : numericValue, parseInt(columnId));
|
|
665
385
|
}
|
|
666
|
-
pendingScrollSettleFocusRef.current = false;
|
|
667
|
-
if (allowA11yFocus && shouldFocusOnScrollSettle) {
|
|
668
|
-
requestAccessibilityFocusOnView(itemRefs.current[newIndex]);
|
|
669
|
-
}
|
|
670
|
-
syncScrollFromPropsRef.current = false;
|
|
671
386
|
isCenterTimerId.current = null;
|
|
672
387
|
}, 100);
|
|
673
388
|
};
|
|
674
|
-
//
|
|
389
|
+
// 滚动处理
|
|
675
390
|
const handleScroll = () => {
|
|
676
391
|
if (!scrollViewRef.current) return;
|
|
677
392
|
if (isCenterTimerId.current) {
|
|
@@ -679,31 +394,17 @@ function PickerGroupDate(props) {
|
|
|
679
394
|
isCenterTimerId.current = null;
|
|
680
395
|
}
|
|
681
396
|
const scrollTop = scrollViewRef.current.scrollTop;
|
|
682
|
-
const
|
|
683
|
-
const newIndex = getSelectedIndex(scrollTop, ih, lengthScaleRatioRef.current, useMeasuredScaleRef.current);
|
|
684
|
-
const spi = selectedIndexPropRef.current;
|
|
685
|
-
if (newIndex !== spi) {
|
|
686
|
-
if (!syncScrollFromPropsRef.current || isTouchingRef.current) {
|
|
687
|
-
pendingScrollSettleFocusRef.current = true;
|
|
688
|
-
}
|
|
689
|
-
if (isTouchingRef.current) {
|
|
690
|
-
syncScrollFromPropsRef.current = false;
|
|
691
|
-
}
|
|
692
|
-
}
|
|
397
|
+
const newIndex = getSelectedIndex(scrollTop);
|
|
693
398
|
if (newIndex !== currentIndex) {
|
|
694
399
|
setCurrentIndex(newIndex);
|
|
695
400
|
}
|
|
696
401
|
};
|
|
697
402
|
// 渲染选项
|
|
698
403
|
const pickerItem = range.map((item, index) => {
|
|
699
|
-
return createComponent(View,
|
|
404
|
+
return createComponent(View, {
|
|
700
405
|
id: `picker-item-${columnId}-${index}`,
|
|
701
406
|
key: index,
|
|
702
|
-
|
|
703
|
-
className: `taro-picker__item${index === currentIndex ? ' taro-picker__item--selected' : ''}`
|
|
704
|
-
}, enableClickItemScroll ? {
|
|
705
|
-
onClick: () => scrollToItemId(`picker-item-${columnId}-${index}`)
|
|
706
|
-
} : {}, {
|
|
407
|
+
className: `taro-picker__item${index === currentIndex ? ' taro-picker__item--selected' : ''}`,
|
|
707
408
|
get style() {
|
|
708
409
|
return {
|
|
709
410
|
height: PICKER_LINE_HEIGHT,
|
|
@@ -711,7 +412,7 @@ function PickerGroupDate(props) {
|
|
|
711
412
|
};
|
|
712
413
|
},
|
|
713
414
|
children: item
|
|
714
|
-
})
|
|
415
|
+
});
|
|
715
416
|
});
|
|
716
417
|
const realPickerItems = [...new Array(PICKER_BLANK_ITEMS).fill(null).map((_, idx) => createComponent(View, {
|
|
717
418
|
key: `blank-top-${idx}`,
|
|
@@ -726,10 +427,6 @@ function PickerGroupDate(props) {
|
|
|
726
427
|
height: PICKER_LINE_HEIGHT
|
|
727
428
|
}
|
|
728
429
|
}))];
|
|
729
|
-
const clickScrollViewProps = enableClickItemScroll ? {
|
|
730
|
-
scrollIntoView,
|
|
731
|
-
scrollIntoViewAlignment: 'center'
|
|
732
|
-
} : {};
|
|
733
430
|
return createComponent(View, {
|
|
734
431
|
className: "taro-picker__group",
|
|
735
432
|
get children() {
|
|
@@ -739,7 +436,7 @@ function PickerGroupDate(props) {
|
|
|
739
436
|
className: "taro-picker__indicator"
|
|
740
437
|
}, indicatorStyle ? {
|
|
741
438
|
style: indicatorStyle
|
|
742
|
-
} : {})), createComponent(ScrollView,
|
|
439
|
+
} : {})), createComponent(ScrollView, {
|
|
743
440
|
ref: scrollViewRef,
|
|
744
441
|
scrollY: true,
|
|
745
442
|
showScrollbar: false,
|
|
@@ -747,16 +444,13 @@ function PickerGroupDate(props) {
|
|
|
747
444
|
style: {
|
|
748
445
|
height: PICKER_LINE_HEIGHT * PICKER_VISIBLE_ITEMS
|
|
749
446
|
},
|
|
750
|
-
scrollTop: targetScrollTop
|
|
751
|
-
}, clickScrollViewProps, {
|
|
447
|
+
scrollTop: targetScrollTop,
|
|
752
448
|
onScroll: handleScroll,
|
|
753
|
-
onTouchStart: () =>
|
|
754
|
-
isTouchingRef.current = true;
|
|
755
|
-
},
|
|
449
|
+
onTouchStart: () => setIsTouching(true),
|
|
756
450
|
onScrollEnd: handleScrollEnd,
|
|
757
451
|
scrollWithAnimation: true,
|
|
758
452
|
children: realPickerItems
|
|
759
|
-
})
|
|
453
|
+
})];
|
|
760
454
|
}
|
|
761
455
|
});
|
|
762
456
|
}
|
|
@@ -769,54 +463,37 @@ function PickerGroupRegion(props) {
|
|
|
769
463
|
updateIndex,
|
|
770
464
|
selectedIndex = 0,
|
|
771
465
|
// 使用selectedIndex参数,默认为0
|
|
772
|
-
colors = {}
|
|
773
|
-
enableClickItemScroll = true
|
|
466
|
+
colors = {}
|
|
774
467
|
} = props;
|
|
775
468
|
const indicatorStyle = colors.lineColor ? getIndicatorStyle(colors.lineColor) : null;
|
|
776
469
|
const scrollViewRef = React.useRef(null);
|
|
777
470
|
const [targetScrollTop, setTargetScrollTop] = React.useState(0);
|
|
778
|
-
const [scrollIntoView, scrollToItemId] = usePickerItemScrollIntoView();
|
|
779
471
|
const [currentIndex, setCurrentIndex] = React.useState(selectedIndex);
|
|
780
|
-
const
|
|
781
|
-
const lengthScaleRatioRef = React.useRef(1);
|
|
782
|
-
const useMeasuredScaleRef = React.useRef(false);
|
|
472
|
+
const [isTouching, setIsTouching] = React.useState(false);
|
|
783
473
|
const itemHeightRef = React.useRef(PICKER_LINE_HEIGHT);
|
|
784
|
-
const
|
|
785
|
-
const selectedIndexPropRef = React.useRef(selectedIndex);
|
|
786
|
-
selectedIndexPropRef.current = selectedIndex;
|
|
787
|
-
const syncScrollFromPropsRef = React.useRef(false);
|
|
788
|
-
const pendingScrollSettleFocusRef = React.useRef(false);
|
|
789
|
-
// 初始化时计算 lengthScaleRatio 并判定缩放模式
|
|
474
|
+
const isUserBeginScrollRef = React.useRef(false);
|
|
790
475
|
React.useEffect(() => {
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
fail: () => {
|
|
798
|
-
lengthScaleRatioRef.current = 1;
|
|
799
|
-
useMeasuredScaleRef.current = false;
|
|
476
|
+
var _a;
|
|
477
|
+
if (process.env.TARO_PLATFORM !== 'harmony') {
|
|
478
|
+
if (scrollViewRef.current && ((_a = scrollViewRef.current) === null || _a === void 0 ? void 0 : _a.scrollHeight)) {
|
|
479
|
+
itemHeightRef.current = scrollViewRef.current.scrollHeight / scrollViewRef.current.childNodes.length;
|
|
480
|
+
} else {
|
|
481
|
+
console.warn('Height measurement anomaly');
|
|
800
482
|
}
|
|
801
|
-
}
|
|
802
|
-
}, []);
|
|
803
|
-
React.useEffect(() => {
|
|
804
|
-
itemHeightRef.current = calculateItemHeight(scrollViewRef.current, lengthScaleRatioRef.current, useMeasuredScaleRef.current);
|
|
483
|
+
}
|
|
805
484
|
}, [range.length]); // 只在range长度变化时重新计算
|
|
806
|
-
|
|
485
|
+
const getSelectedIndex = scrollTop => {
|
|
486
|
+
return Math.round(scrollTop / itemHeightRef.current);
|
|
487
|
+
};
|
|
488
|
+
// 当selectedIndex变化时,调整滚动位置
|
|
807
489
|
React.useEffect(() => {
|
|
808
|
-
if (scrollViewRef.current && range.length > 0 && !
|
|
809
|
-
syncScrollFromPropsRef.current = true;
|
|
490
|
+
if (scrollViewRef.current && range.length > 0 && !isTouching) {
|
|
810
491
|
const baseValue = selectedIndex * itemHeightRef.current;
|
|
811
|
-
setTargetScrollTopWithScale(setTargetScrollTop, baseValue
|
|
492
|
+
setTargetScrollTopWithScale(setTargetScrollTop, baseValue);
|
|
812
493
|
setCurrentIndex(selectedIndex);
|
|
813
|
-
const tid = setTimeout(() => {
|
|
814
|
-
syncScrollFromPropsRef.current = false;
|
|
815
|
-
}, 400);
|
|
816
|
-
return () => clearTimeout(tid);
|
|
817
494
|
}
|
|
818
495
|
}, [selectedIndex, range]);
|
|
819
|
-
//
|
|
496
|
+
// 滚动结束处理
|
|
820
497
|
const isCenterTimerId = React.useRef(null);
|
|
821
498
|
const handleScrollEnd = () => {
|
|
822
499
|
if (!scrollViewRef.current) return;
|
|
@@ -824,27 +501,19 @@ function PickerGroupRegion(props) {
|
|
|
824
501
|
clearTimeout(isCenterTimerId.current);
|
|
825
502
|
isCenterTimerId.current = null;
|
|
826
503
|
}
|
|
827
|
-
//
|
|
504
|
+
// 做一个0.1s延时 0.1s之内没有新的滑动 则把选项归到中间 然后更新选中项
|
|
828
505
|
isCenterTimerId.current = setTimeout(() => {
|
|
829
506
|
if (!scrollViewRef.current) return;
|
|
830
507
|
const scrollTop = scrollViewRef.current.scrollTop;
|
|
831
|
-
const newIndex = getSelectedIndex(scrollTop
|
|
832
|
-
|
|
833
|
-
const shouldFocusOnScrollSettle = pendingScrollSettleFocusRef.current;
|
|
834
|
-
isTouchingRef.current = false;
|
|
508
|
+
const newIndex = getSelectedIndex(scrollTop);
|
|
509
|
+
setIsTouching(false);
|
|
835
510
|
const baseValue = newIndex * itemHeightRef.current;
|
|
836
511
|
const randomOffset = Math.random() * 0.001; // 随机数为了在一个项内滚动时强制刷新
|
|
837
|
-
setTargetScrollTopWithScale(setTargetScrollTop, baseValue, randomOffset
|
|
838
|
-
updateIndex(newIndex, columnId, false,
|
|
839
|
-
pendingScrollSettleFocusRef.current = false;
|
|
840
|
-
if (allowA11yFocus && shouldFocusOnScrollSettle) {
|
|
841
|
-
requestAccessibilityFocusOnView(itemRefs.current[newIndex]);
|
|
842
|
-
}
|
|
843
|
-
syncScrollFromPropsRef.current = false;
|
|
844
|
-
isCenterTimerId.current = null;
|
|
512
|
+
setTargetScrollTopWithScale(setTargetScrollTop, baseValue, randomOffset);
|
|
513
|
+
updateIndex(newIndex, columnId, false, isUserBeginScrollRef.current);
|
|
845
514
|
}, 100);
|
|
846
515
|
};
|
|
847
|
-
//
|
|
516
|
+
// 滚动处理 - 在滚动时计算索引
|
|
848
517
|
const handleScroll = () => {
|
|
849
518
|
if (!scrollViewRef.current) return;
|
|
850
519
|
if (isCenterTimerId.current) {
|
|
@@ -852,17 +521,7 @@ function PickerGroupRegion(props) {
|
|
|
852
521
|
isCenterTimerId.current = null;
|
|
853
522
|
}
|
|
854
523
|
const scrollTop = scrollViewRef.current.scrollTop;
|
|
855
|
-
const
|
|
856
|
-
const newIndex = getSelectedIndex(scrollTop, ih, lengthScaleRatioRef.current, useMeasuredScaleRef.current);
|
|
857
|
-
const spi = selectedIndexPropRef.current;
|
|
858
|
-
if (newIndex !== spi) {
|
|
859
|
-
if (!syncScrollFromPropsRef.current || isTouchingRef.current) {
|
|
860
|
-
pendingScrollSettleFocusRef.current = true;
|
|
861
|
-
}
|
|
862
|
-
if (isTouchingRef.current) {
|
|
863
|
-
syncScrollFromPropsRef.current = false;
|
|
864
|
-
}
|
|
865
|
-
}
|
|
524
|
+
const newIndex = getSelectedIndex(scrollTop);
|
|
866
525
|
if (newIndex !== currentIndex) {
|
|
867
526
|
setCurrentIndex(newIndex);
|
|
868
527
|
}
|
|
@@ -870,14 +529,10 @@ function PickerGroupRegion(props) {
|
|
|
870
529
|
// 渲染选项
|
|
871
530
|
const pickerItem = range.map((item, index) => {
|
|
872
531
|
const content = rangeKey && item && typeof item === 'object' ? item[rangeKey] : item;
|
|
873
|
-
return createComponent(View,
|
|
532
|
+
return createComponent(View, {
|
|
874
533
|
id: `picker-item-${columnId}-${index}`,
|
|
875
534
|
key: index,
|
|
876
|
-
|
|
877
|
-
className: `taro-picker__item${index === currentIndex ? ' taro-picker__item--selected' : ''}`
|
|
878
|
-
}, enableClickItemScroll ? {
|
|
879
|
-
onClick: () => scrollToItemId(`picker-item-${columnId}-${index}`)
|
|
880
|
-
} : {}, {
|
|
535
|
+
className: `taro-picker__item${index === currentIndex ? ' taro-picker__item--selected' : ''}`,
|
|
881
536
|
get style() {
|
|
882
537
|
return {
|
|
883
538
|
height: PICKER_LINE_HEIGHT,
|
|
@@ -885,7 +540,7 @@ function PickerGroupRegion(props) {
|
|
|
885
540
|
};
|
|
886
541
|
},
|
|
887
542
|
children: content
|
|
888
|
-
})
|
|
543
|
+
});
|
|
889
544
|
});
|
|
890
545
|
const realPickerItems = [...new Array(PICKER_BLANK_ITEMS).fill(null).map((_, idx) => createComponent(View, {
|
|
891
546
|
key: `blank-top-${idx}`,
|
|
@@ -900,10 +555,6 @@ function PickerGroupRegion(props) {
|
|
|
900
555
|
height: PICKER_LINE_HEIGHT
|
|
901
556
|
}
|
|
902
557
|
}))];
|
|
903
|
-
const clickScrollViewProps = enableClickItemScroll ? {
|
|
904
|
-
scrollIntoView,
|
|
905
|
-
scrollIntoViewAlignment: 'center'
|
|
906
|
-
} : {};
|
|
907
558
|
return createComponent(View, {
|
|
908
559
|
className: "taro-picker__group",
|
|
909
560
|
get children() {
|
|
@@ -913,7 +564,7 @@ function PickerGroupRegion(props) {
|
|
|
913
564
|
className: "taro-picker__indicator"
|
|
914
565
|
}, indicatorStyle ? {
|
|
915
566
|
style: indicatorStyle
|
|
916
|
-
} : {})), createComponent(ScrollView,
|
|
567
|
+
} : {})), createComponent(ScrollView, {
|
|
917
568
|
ref: scrollViewRef,
|
|
918
569
|
scrollY: true,
|
|
919
570
|
showScrollbar: false,
|
|
@@ -921,16 +572,16 @@ function PickerGroupRegion(props) {
|
|
|
921
572
|
style: {
|
|
922
573
|
height: PICKER_LINE_HEIGHT * PICKER_VISIBLE_ITEMS
|
|
923
574
|
},
|
|
924
|
-
scrollTop: targetScrollTop
|
|
925
|
-
}, clickScrollViewProps, {
|
|
575
|
+
scrollTop: targetScrollTop,
|
|
926
576
|
onScroll: handleScroll,
|
|
927
577
|
onTouchStart: () => {
|
|
928
|
-
|
|
578
|
+
setIsTouching(true);
|
|
579
|
+
isUserBeginScrollRef.current = true;
|
|
929
580
|
},
|
|
930
581
|
onScrollEnd: handleScrollEnd,
|
|
931
582
|
scrollWithAnimation: true,
|
|
932
583
|
children: realPickerItems
|
|
933
|
-
})
|
|
584
|
+
})];
|
|
934
585
|
}
|
|
935
586
|
});
|
|
936
587
|
}
|