@tarojs/components-react 4.1.12-beta.40 → 4.1.12-beta.41
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/picker/index.js +71 -34
- package/dist/components/picker/index.js.map +1 -1
- package/dist/components/picker/picker-group.js +329 -45
- package/dist/components/picker/picker-group.js.map +1 -1
- package/dist/components/scroll-view/index.js +29 -13
- package/dist/components/scroll-view/index.js.map +1 -1
- package/dist/original/components/picker/index.js +71 -34
- package/dist/original/components/picker/index.js.map +1 -1
- package/dist/original/components/picker/picker-group.js +329 -45
- package/dist/original/components/picker/picker-group.js.map +1 -1
- package/dist/original/components/scroll-view/index.js +29 -13
- package/dist/original/components/scroll-view/index.js.map +1 -1
- package/dist/solid/components/picker/index.js +75 -38
- package/dist/solid/components/picker/index.js.map +1 -1
- package/dist/solid/components/picker/picker-group.js +352 -69
- package/dist/solid/components/picker/picker-group.js.map +1 -1
- package/dist/solid/components/scroll-view/index.js +29 -13
- package/dist/solid/components/scroll-view/index.js.map +1 -1
- package/package.json +6 -6
|
@@ -3,6 +3,31 @@ 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
|
+
}
|
|
6
31
|
// 定义常量
|
|
7
32
|
const PICKER_LINE_HEIGHT = 34; // px
|
|
8
33
|
const PICKER_VISIBLE_ITEMS = 7; // 可见行数
|
|
@@ -157,16 +182,22 @@ function PickerGroupBasic(props) {
|
|
|
157
182
|
onColumnChange,
|
|
158
183
|
selectedIndex = 0,
|
|
159
184
|
// 使用selectedIndex参数,默认为0
|
|
160
|
-
colors = {}
|
|
185
|
+
colors = {},
|
|
186
|
+
enableClickItemScroll = true
|
|
161
187
|
} = props;
|
|
162
188
|
const indicatorStyle = colors.lineColor ? getIndicatorStyle(colors.lineColor) : null;
|
|
163
189
|
const [targetScrollTop, setTargetScrollTop] = React.useState(0);
|
|
190
|
+
const [scrollIntoView, scrollToItemId] = usePickerItemScrollIntoView();
|
|
164
191
|
const scrollViewRef = React.useRef(null);
|
|
165
192
|
const itemRefs = React.useRef([]);
|
|
193
|
+
const selectedIndexPropRef = React.useRef(selectedIndex);
|
|
194
|
+
selectedIndexPropRef.current = selectedIndex;
|
|
195
|
+
const syncScrollFromPropsRef = React.useRef(false);
|
|
196
|
+
const pendingScrollSettleFocusRef = React.useRef(false);
|
|
166
197
|
// 使用selectedIndex初始化当前索引
|
|
167
198
|
const [currentIndex, setCurrentIndex] = React.useState(selectedIndex);
|
|
168
199
|
// 触摸状态用于优化用户体验
|
|
169
|
-
const
|
|
200
|
+
const isTouchingRef = React.useRef(false);
|
|
170
201
|
const lengthScaleRatioRef = React.useRef(1);
|
|
171
202
|
const useMeasuredScaleRef = React.useRef(false);
|
|
172
203
|
const itemHeightRef = React.useRef(PICKER_LINE_HEIGHT);
|
|
@@ -187,29 +218,36 @@ function PickerGroupBasic(props) {
|
|
|
187
218
|
React.useEffect(() => {
|
|
188
219
|
itemHeightRef.current = calculateItemHeight(scrollViewRef.current, lengthScaleRatioRef.current, useMeasuredScaleRef.current);
|
|
189
220
|
}, [range.length]); // 只在range长度变化时重新计算
|
|
190
|
-
//
|
|
221
|
+
// props 的 selectedIndex 变化:滚至对应项并短时标记程序化滚动(syncScrollFromPropsRef)
|
|
191
222
|
React.useEffect(() => {
|
|
192
|
-
if (scrollViewRef.current && range.length > 0 && !
|
|
223
|
+
if (scrollViewRef.current && range.length > 0 && !isTouchingRef.current) {
|
|
224
|
+
syncScrollFromPropsRef.current = true;
|
|
193
225
|
const baseValue = selectedIndex * itemHeightRef.current;
|
|
194
226
|
setTargetScrollTopWithScale(setTargetScrollTop, baseValue, undefined, lengthScaleRatioRef.current);
|
|
195
227
|
setCurrentIndex(selectedIndex);
|
|
228
|
+
const tid = setTimeout(() => {
|
|
229
|
+
syncScrollFromPropsRef.current = false;
|
|
230
|
+
}, 400);
|
|
231
|
+
return () => clearTimeout(tid);
|
|
196
232
|
}
|
|
197
233
|
}, [selectedIndex, range]);
|
|
198
234
|
// 是否处于归中状态
|
|
199
235
|
const isCenterTimerId = React.useRef(null);
|
|
200
|
-
//
|
|
236
|
+
// 滚动静止后归中并同步选中
|
|
201
237
|
const handleScrollEnd = () => {
|
|
202
238
|
if (!scrollViewRef.current) return;
|
|
203
239
|
if (isCenterTimerId.current) {
|
|
204
240
|
clearTimeout(isCenterTimerId.current);
|
|
205
241
|
isCenterTimerId.current = null;
|
|
206
242
|
}
|
|
207
|
-
//
|
|
243
|
+
// 100ms 内无新滚动则归中并提交索引
|
|
208
244
|
isCenterTimerId.current = setTimeout(() => {
|
|
209
245
|
if (!scrollViewRef.current) return;
|
|
210
246
|
const scrollTop = scrollViewRef.current.scrollTop;
|
|
211
247
|
const newIndex = getSelectedIndex(scrollTop, itemHeightRef.current, lengthScaleRatioRef.current, useMeasuredScaleRef.current);
|
|
212
|
-
|
|
248
|
+
const allowA11yFocus = !syncScrollFromPropsRef.current;
|
|
249
|
+
const shouldFocusOnScrollSettle = pendingScrollSettleFocusRef.current;
|
|
250
|
+
isTouchingRef.current = false;
|
|
213
251
|
const baseValue = newIndex * itemHeightRef.current;
|
|
214
252
|
const randomOffset = Math.random() * 0.001; // 随机数为了在一个项内滚动时强制刷新
|
|
215
253
|
setTargetScrollTopWithScale(setTargetScrollTop, baseValue, randomOffset, lengthScaleRatioRef.current);
|
|
@@ -218,10 +256,15 @@ function PickerGroupBasic(props) {
|
|
|
218
256
|
columnId,
|
|
219
257
|
index: newIndex
|
|
220
258
|
});
|
|
259
|
+
pendingScrollSettleFocusRef.current = false;
|
|
260
|
+
if (allowA11yFocus && shouldFocusOnScrollSettle) {
|
|
261
|
+
requestAccessibilityFocusOnView(itemRefs.current[newIndex]);
|
|
262
|
+
}
|
|
263
|
+
syncScrollFromPropsRef.current = false;
|
|
221
264
|
isCenterTimerId.current = null;
|
|
222
265
|
}, 100);
|
|
223
266
|
};
|
|
224
|
-
//
|
|
267
|
+
// 滚动中:按 scrollTop 更新高亮索引
|
|
225
268
|
const handleScroll = () => {
|
|
226
269
|
if (!scrollViewRef.current) return;
|
|
227
270
|
if (isCenterTimerId.current) {
|
|
@@ -229,7 +272,17 @@ function PickerGroupBasic(props) {
|
|
|
229
272
|
isCenterTimerId.current = null;
|
|
230
273
|
}
|
|
231
274
|
const scrollTop = scrollViewRef.current.scrollTop;
|
|
232
|
-
const
|
|
275
|
+
const ih = itemHeightRef.current;
|
|
276
|
+
const newIndex = getSelectedIndex(scrollTop, ih, lengthScaleRatioRef.current, useMeasuredScaleRef.current);
|
|
277
|
+
const spi = selectedIndexPropRef.current;
|
|
278
|
+
if (newIndex !== spi) {
|
|
279
|
+
if (!syncScrollFromPropsRef.current || isTouchingRef.current) {
|
|
280
|
+
pendingScrollSettleFocusRef.current = true;
|
|
281
|
+
}
|
|
282
|
+
if (isTouchingRef.current) {
|
|
283
|
+
syncScrollFromPropsRef.current = false;
|
|
284
|
+
}
|
|
285
|
+
}
|
|
233
286
|
if (newIndex !== currentIndex) {
|
|
234
287
|
setCurrentIndex(newIndex);
|
|
235
288
|
}
|
|
@@ -237,11 +290,14 @@ function PickerGroupBasic(props) {
|
|
|
237
290
|
// 渲染选项
|
|
238
291
|
const pickerItem = range.map((item, index) => {
|
|
239
292
|
const content = rangeKey && item && typeof item === 'object' ? item[rangeKey] : item;
|
|
240
|
-
return createComponent(View, {
|
|
293
|
+
return createComponent(View, mergeProps({
|
|
241
294
|
id: `picker-item-${columnId}-${index}`,
|
|
242
295
|
key: index,
|
|
243
296
|
ref: el => itemRefs.current[index] = el,
|
|
244
|
-
className: `taro-picker__item${index === currentIndex ? ' taro-picker__item--selected' : ''}
|
|
297
|
+
className: `taro-picker__item${index === currentIndex ? ' taro-picker__item--selected' : ''}`
|
|
298
|
+
}, enableClickItemScroll ? {
|
|
299
|
+
onClick: () => scrollToItemId(`picker-item-${columnId}-${index}`)
|
|
300
|
+
} : {}, {
|
|
245
301
|
get style() {
|
|
246
302
|
return {
|
|
247
303
|
height: PICKER_LINE_HEIGHT,
|
|
@@ -249,7 +305,7 @@ function PickerGroupBasic(props) {
|
|
|
249
305
|
};
|
|
250
306
|
},
|
|
251
307
|
children: content
|
|
252
|
-
});
|
|
308
|
+
}));
|
|
253
309
|
});
|
|
254
310
|
const realPickerItems = [...new Array(PICKER_BLANK_ITEMS).fill(null).map((_, idx) => createComponent(View, {
|
|
255
311
|
key: `blank-top-${idx}`,
|
|
@@ -264,6 +320,10 @@ function PickerGroupBasic(props) {
|
|
|
264
320
|
height: PICKER_LINE_HEIGHT
|
|
265
321
|
}
|
|
266
322
|
}))];
|
|
323
|
+
const clickScrollViewProps = enableClickItemScroll ? {
|
|
324
|
+
scrollIntoView,
|
|
325
|
+
scrollIntoViewAlignment: 'center'
|
|
326
|
+
} : {};
|
|
267
327
|
return createComponent(View, {
|
|
268
328
|
className: "taro-picker__group",
|
|
269
329
|
get children() {
|
|
@@ -273,7 +333,7 @@ function PickerGroupBasic(props) {
|
|
|
273
333
|
className: "taro-picker__indicator"
|
|
274
334
|
}, indicatorStyle ? {
|
|
275
335
|
style: indicatorStyle
|
|
276
|
-
} : {})), createComponent(ScrollView, {
|
|
336
|
+
} : {})), createComponent(ScrollView, mergeProps({
|
|
277
337
|
ref: scrollViewRef,
|
|
278
338
|
scrollY: true,
|
|
279
339
|
showScrollbar: false,
|
|
@@ -281,13 +341,16 @@ function PickerGroupBasic(props) {
|
|
|
281
341
|
style: {
|
|
282
342
|
height: PICKER_LINE_HEIGHT * PICKER_VISIBLE_ITEMS
|
|
283
343
|
},
|
|
284
|
-
scrollTop: targetScrollTop
|
|
344
|
+
scrollTop: targetScrollTop
|
|
345
|
+
}, clickScrollViewProps, {
|
|
285
346
|
onScroll: handleScroll,
|
|
286
|
-
onTouchStart: () =>
|
|
347
|
+
onTouchStart: () => {
|
|
348
|
+
isTouchingRef.current = true;
|
|
349
|
+
},
|
|
287
350
|
onScrollEnd: handleScrollEnd,
|
|
288
351
|
scrollWithAnimation: true,
|
|
289
352
|
children: realPickerItems
|
|
290
|
-
})];
|
|
353
|
+
}))];
|
|
291
354
|
}
|
|
292
355
|
});
|
|
293
356
|
}
|
|
@@ -299,17 +362,75 @@ function PickerGroupTime(props) {
|
|
|
299
362
|
columnId,
|
|
300
363
|
updateIndex,
|
|
301
364
|
selectedIndex = 0,
|
|
302
|
-
colors = {}
|
|
365
|
+
colors = {},
|
|
366
|
+
timeA11yLimitFocus,
|
|
367
|
+
onTimeA11yLimitFocusConsumed,
|
|
368
|
+
timeA11yLimitEventNonce,
|
|
369
|
+
enableClickItemScroll = true
|
|
303
370
|
} = props;
|
|
304
371
|
const indicatorStyle = colors.lineColor ? getIndicatorStyle(colors.lineColor) : null;
|
|
305
372
|
const [targetScrollTop, setTargetScrollTop] = React.useState(0);
|
|
373
|
+
const [scrollIntoView, scrollToItemId] = usePickerItemScrollIntoView();
|
|
306
374
|
const scrollViewRef = React.useRef(null);
|
|
307
375
|
const itemRefs = React.useRef([]);
|
|
376
|
+
const selectedIndexPropRef = React.useRef(selectedIndex);
|
|
377
|
+
selectedIndexPropRef.current = selectedIndex;
|
|
378
|
+
const syncScrollFromPropsRef = React.useRef(false);
|
|
379
|
+
const prevLimitEventNonceRef = React.useRef(undefined);
|
|
380
|
+
const suppressScrollSettleFocusNonceRef = React.useRef(null);
|
|
381
|
+
const pendingScrollSettleFocusRef = React.useRef(false);
|
|
382
|
+
const pendingLimitFocusRef = React.useRef(null);
|
|
383
|
+
const limitFocusTimerRef = React.useRef(null);
|
|
308
384
|
const [currentIndex, setCurrentIndex] = React.useState(selectedIndex);
|
|
309
|
-
const
|
|
385
|
+
const isTouchingRef = React.useRef(false);
|
|
310
386
|
const lengthScaleRatioRef = React.useRef(1);
|
|
311
387
|
const useMeasuredScaleRef = React.useRef(false);
|
|
312
388
|
const itemHeightRef = React.useRef(PICKER_LINE_HEIGHT);
|
|
389
|
+
const clearLimitFocusTimer = React.useCallback(() => {
|
|
390
|
+
if (limitFocusTimerRef.current) {
|
|
391
|
+
clearTimeout(limitFocusTimerRef.current);
|
|
392
|
+
limitFocusTimerRef.current = null;
|
|
393
|
+
}
|
|
394
|
+
}, []);
|
|
395
|
+
const tryFocusPendingLimit = React.useCallback(() => {
|
|
396
|
+
const pending = pendingLimitFocusRef.current;
|
|
397
|
+
const scrollView = scrollViewRef.current;
|
|
398
|
+
if (!pending || !scrollView) return;
|
|
399
|
+
const visualIndex = getSelectedIndex(scrollView.scrollTop, itemHeightRef.current, lengthScaleRatioRef.current, useMeasuredScaleRef.current);
|
|
400
|
+
const isStable = visualIndex === pending.index;
|
|
401
|
+
if (!isStable) {
|
|
402
|
+
if (pending.attempts >= 20) {
|
|
403
|
+
pendingLimitFocusRef.current = null;
|
|
404
|
+
if (suppressScrollSettleFocusNonceRef.current === pending.nonce) {
|
|
405
|
+
suppressScrollSettleFocusNonceRef.current = null;
|
|
406
|
+
}
|
|
407
|
+
onTimeA11yLimitFocusConsumed === null || onTimeA11yLimitFocusConsumed === void 0 ? void 0 : onTimeA11yLimitFocusConsumed();
|
|
408
|
+
return;
|
|
409
|
+
}
|
|
410
|
+
pending.attempts += 1;
|
|
411
|
+
clearLimitFocusTimer();
|
|
412
|
+
limitFocusTimerRef.current = setTimeout(() => {
|
|
413
|
+
tryFocusPendingLimit();
|
|
414
|
+
}, 50);
|
|
415
|
+
return;
|
|
416
|
+
}
|
|
417
|
+
const node = itemRefs.current[pending.index];
|
|
418
|
+
pendingLimitFocusRef.current = null;
|
|
419
|
+
if (suppressScrollSettleFocusNonceRef.current === pending.nonce) {
|
|
420
|
+
suppressScrollSettleFocusNonceRef.current = null;
|
|
421
|
+
}
|
|
422
|
+
clearLimitFocusTimer();
|
|
423
|
+
requestAccessibilityFocusOnView(node);
|
|
424
|
+
onTimeA11yLimitFocusConsumed === null || onTimeA11yLimitFocusConsumed === void 0 ? void 0 : onTimeA11yLimitFocusConsumed();
|
|
425
|
+
}, [clearLimitFocusTimer, onTimeA11yLimitFocusConsumed]);
|
|
426
|
+
const schedulePendingLimitFocus = React.useCallback(() => {
|
|
427
|
+
if (!pendingLimitFocusRef.current) return;
|
|
428
|
+
clearLimitFocusTimer();
|
|
429
|
+
limitFocusTimerRef.current = setTimeout(() => {
|
|
430
|
+
tryFocusPendingLimit();
|
|
431
|
+
}, 100);
|
|
432
|
+
}, [clearLimitFocusTimer, tryFocusPendingLimit]);
|
|
433
|
+
React.useEffect(() => clearLimitFocusTimer, [clearLimitFocusTimer]);
|
|
313
434
|
// 初始化时计算 lengthScaleRatio 并判定缩放模式
|
|
314
435
|
React.useEffect(() => {
|
|
315
436
|
Taro.getSystemInfo({
|
|
@@ -327,41 +448,103 @@ function PickerGroupTime(props) {
|
|
|
327
448
|
React.useEffect(() => {
|
|
328
449
|
itemHeightRef.current = calculateItemHeight(scrollViewRef.current, lengthScaleRatioRef.current, useMeasuredScaleRef.current);
|
|
329
450
|
}, [range.length]); // 只在range长度变化时重新计算
|
|
330
|
-
//
|
|
451
|
+
// props 的 selectedIndex 变化:滚至对应项并短时标记程序化滚动(syncScrollFromPropsRef)
|
|
331
452
|
React.useEffect(() => {
|
|
332
|
-
if (scrollViewRef.current && range.length > 0 && !
|
|
453
|
+
if (scrollViewRef.current && range.length > 0 && !isTouchingRef.current) {
|
|
454
|
+
syncScrollFromPropsRef.current = true;
|
|
333
455
|
const baseValue = selectedIndex * itemHeightRef.current;
|
|
334
456
|
setTargetScrollTopWithScale(setTargetScrollTop, baseValue, undefined, lengthScaleRatioRef.current);
|
|
335
457
|
setCurrentIndex(selectedIndex);
|
|
458
|
+
const tid = setTimeout(() => {
|
|
459
|
+
syncScrollFromPropsRef.current = false;
|
|
460
|
+
}, 400);
|
|
461
|
+
return () => clearTimeout(tid);
|
|
336
462
|
}
|
|
337
463
|
}, [selectedIndex, range]);
|
|
464
|
+
// time 限位 nonce 变更:强制对齐 scrollTop(避免 remount 打断读屏焦点)
|
|
465
|
+
React.useEffect(() => {
|
|
466
|
+
if (!timeA11yLimitEventNonce) return;
|
|
467
|
+
if (!scrollViewRef.current || range.length === 0 || isTouchingRef.current) return;
|
|
468
|
+
syncScrollFromPropsRef.current = true;
|
|
469
|
+
const baseValue = selectedIndex * itemHeightRef.current;
|
|
470
|
+
setTargetScrollTopWithScale(setTargetScrollTop, baseValue, Math.random() * 0.001, lengthScaleRatioRef.current);
|
|
471
|
+
setCurrentIndex(selectedIndex);
|
|
472
|
+
const tid = setTimeout(() => {
|
|
473
|
+
syncScrollFromPropsRef.current = false;
|
|
474
|
+
}, 400);
|
|
475
|
+
return () => clearTimeout(tid);
|
|
476
|
+
}, [timeA11yLimitEventNonce]); // 仅依赖 nonce,其余用 ref
|
|
477
|
+
React.useLayoutEffect(() => {
|
|
478
|
+
if (timeA11yLimitEventNonce === undefined) return;
|
|
479
|
+
const prev = prevLimitEventNonceRef.current;
|
|
480
|
+
prevLimitEventNonceRef.current = timeA11yLimitEventNonce;
|
|
481
|
+
if (timeA11yLimitEventNonce > 0 && (prev === undefined || timeA11yLimitEventNonce > prev)) {
|
|
482
|
+
suppressScrollSettleFocusNonceRef.current = timeA11yLimitEventNonce;
|
|
483
|
+
}
|
|
484
|
+
}, [timeA11yLimitEventNonce]);
|
|
485
|
+
// 限位后登记本列待聚焦项,稳定后再 requestAccessibilityFocus
|
|
486
|
+
React.useEffect(() => {
|
|
487
|
+
const req = timeA11yLimitFocus;
|
|
488
|
+
if (!req || req.columnId !== columnId) return;
|
|
489
|
+
const idx = selectedIndex;
|
|
490
|
+
const nonce = req.nonce;
|
|
491
|
+
pendingScrollSettleFocusRef.current = false;
|
|
492
|
+
pendingLimitFocusRef.current = {
|
|
493
|
+
index: idx,
|
|
494
|
+
nonce,
|
|
495
|
+
attempts: 0
|
|
496
|
+
};
|
|
497
|
+
schedulePendingLimitFocus();
|
|
498
|
+
}, [timeA11yLimitFocus, columnId, selectedIndex, schedulePendingLimitFocus]);
|
|
338
499
|
// 是否处于归中状态
|
|
339
500
|
const isCenterTimerId = React.useRef(null);
|
|
340
|
-
//
|
|
501
|
+
// 滚动静止后归中并同步选中
|
|
341
502
|
const handleScrollEnd = () => {
|
|
342
503
|
if (!scrollViewRef.current) return;
|
|
343
504
|
if (isCenterTimerId.current) {
|
|
344
505
|
clearTimeout(isCenterTimerId.current);
|
|
345
506
|
isCenterTimerId.current = null;
|
|
346
507
|
}
|
|
347
|
-
//
|
|
508
|
+
// 100ms 内无新滚动则归中并提交索引
|
|
348
509
|
isCenterTimerId.current = setTimeout(() => {
|
|
349
510
|
if (!scrollViewRef.current) return;
|
|
350
511
|
const scrollTop = scrollViewRef.current.scrollTop;
|
|
351
512
|
const newIndex = getSelectedIndex(scrollTop, itemHeightRef.current, lengthScaleRatioRef.current, useMeasuredScaleRef.current);
|
|
352
|
-
|
|
353
|
-
|
|
513
|
+
const allowA11yFocus = !syncScrollFromPropsRef.current;
|
|
514
|
+
const shouldFocusOnScrollSettle = pendingScrollSettleFocusRef.current;
|
|
515
|
+
isTouchingRef.current = false;
|
|
354
516
|
const isLimited = Boolean(updateIndex(newIndex, columnId, true));
|
|
355
|
-
|
|
517
|
+
const hasPendingLimitFocus = pendingLimitFocusRef.current != null;
|
|
518
|
+
if (isLimited) {
|
|
519
|
+
pendingScrollSettleFocusRef.current = false;
|
|
520
|
+
}
|
|
356
521
|
if (!isLimited) {
|
|
357
522
|
const baseValue = newIndex * itemHeightRef.current;
|
|
358
523
|
const randomOffset = Math.random() * 0.001;
|
|
359
524
|
setTargetScrollTopWithScale(setTargetScrollTop, baseValue, randomOffset, lengthScaleRatioRef.current);
|
|
360
525
|
}
|
|
526
|
+
if (!isLimited && hasPendingLimitFocus) {
|
|
527
|
+
pendingScrollSettleFocusRef.current = false;
|
|
528
|
+
schedulePendingLimitFocus();
|
|
529
|
+
syncScrollFromPropsRef.current = false;
|
|
530
|
+
isCenterTimerId.current = null;
|
|
531
|
+
return;
|
|
532
|
+
}
|
|
533
|
+
if (!isLimited && pendingLimitFocusRef.current == null && suppressScrollSettleFocusNonceRef.current != null) {
|
|
534
|
+
suppressScrollSettleFocusNonceRef.current = null;
|
|
535
|
+
}
|
|
536
|
+
const suppressByLimitNonce = suppressScrollSettleFocusNonceRef.current != null;
|
|
537
|
+
if (!isLimited && allowA11yFocus && shouldFocusOnScrollSettle) {
|
|
538
|
+
pendingScrollSettleFocusRef.current = false;
|
|
539
|
+
if (!suppressByLimitNonce) {
|
|
540
|
+
requestAccessibilityFocusOnView(itemRefs.current[newIndex]);
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
syncScrollFromPropsRef.current = false;
|
|
361
544
|
isCenterTimerId.current = null;
|
|
362
545
|
}, 100);
|
|
363
546
|
};
|
|
364
|
-
//
|
|
547
|
+
// 滚动中:按 scrollTop 更新高亮索引
|
|
365
548
|
const handleScroll = () => {
|
|
366
549
|
if (!scrollViewRef.current) return;
|
|
367
550
|
if (isCenterTimerId.current) {
|
|
@@ -369,19 +552,35 @@ function PickerGroupTime(props) {
|
|
|
369
552
|
isCenterTimerId.current = null;
|
|
370
553
|
}
|
|
371
554
|
const scrollTop = scrollViewRef.current.scrollTop;
|
|
372
|
-
const
|
|
555
|
+
const ih = itemHeightRef.current;
|
|
556
|
+
const newIndex = getSelectedIndex(scrollTop, ih, lengthScaleRatioRef.current, useMeasuredScaleRef.current);
|
|
557
|
+
const spi = selectedIndexPropRef.current;
|
|
558
|
+
if (newIndex !== spi) {
|
|
559
|
+
if (!syncScrollFromPropsRef.current || isTouchingRef.current) {
|
|
560
|
+
pendingScrollSettleFocusRef.current = true;
|
|
561
|
+
}
|
|
562
|
+
if (isTouchingRef.current) {
|
|
563
|
+
syncScrollFromPropsRef.current = false;
|
|
564
|
+
}
|
|
565
|
+
}
|
|
373
566
|
if (newIndex !== currentIndex) {
|
|
374
567
|
setCurrentIndex(newIndex);
|
|
375
568
|
}
|
|
569
|
+
if (pendingLimitFocusRef.current) {
|
|
570
|
+
schedulePendingLimitFocus();
|
|
571
|
+
}
|
|
376
572
|
};
|
|
377
573
|
// 渲染选项
|
|
378
574
|
const pickerItem = range.map((item, index) => {
|
|
379
575
|
const content = rangeKey && item && typeof item === 'object' ? item[rangeKey] : item;
|
|
380
|
-
return createComponent(View, {
|
|
576
|
+
return createComponent(View, mergeProps({
|
|
381
577
|
id: `picker-item-${columnId}-${index}`,
|
|
382
578
|
key: index,
|
|
383
579
|
ref: el => itemRefs.current[index] = el,
|
|
384
|
-
className: `taro-picker__item${index === currentIndex ? ' taro-picker__item--selected' : ''}
|
|
580
|
+
className: `taro-picker__item${index === currentIndex ? ' taro-picker__item--selected' : ''}`
|
|
581
|
+
}, enableClickItemScroll ? {
|
|
582
|
+
onClick: () => scrollToItemId(`picker-item-${columnId}-${index}`)
|
|
583
|
+
} : {}, {
|
|
385
584
|
get style() {
|
|
386
585
|
return {
|
|
387
586
|
height: PICKER_LINE_HEIGHT,
|
|
@@ -389,7 +588,7 @@ function PickerGroupTime(props) {
|
|
|
389
588
|
};
|
|
390
589
|
},
|
|
391
590
|
children: content
|
|
392
|
-
});
|
|
591
|
+
}));
|
|
393
592
|
});
|
|
394
593
|
const realPickerItems = [...new Array(PICKER_BLANK_ITEMS).fill(null).map((_, idx) => createComponent(View, {
|
|
395
594
|
key: `blank-top-${idx}`,
|
|
@@ -404,6 +603,10 @@ function PickerGroupTime(props) {
|
|
|
404
603
|
height: PICKER_LINE_HEIGHT
|
|
405
604
|
}
|
|
406
605
|
}))];
|
|
606
|
+
const clickScrollViewProps = enableClickItemScroll ? {
|
|
607
|
+
scrollIntoView,
|
|
608
|
+
scrollIntoViewAlignment: 'center'
|
|
609
|
+
} : {};
|
|
407
610
|
return createComponent(View, {
|
|
408
611
|
className: "taro-picker__group",
|
|
409
612
|
get children() {
|
|
@@ -413,7 +616,7 @@ function PickerGroupTime(props) {
|
|
|
413
616
|
className: "taro-picker__indicator"
|
|
414
617
|
}, indicatorStyle ? {
|
|
415
618
|
style: indicatorStyle
|
|
416
|
-
} : {})), createComponent(ScrollView, {
|
|
619
|
+
} : {})), createComponent(ScrollView, mergeProps({
|
|
417
620
|
ref: scrollViewRef,
|
|
418
621
|
scrollY: true,
|
|
419
622
|
showScrollbar: false,
|
|
@@ -421,13 +624,16 @@ function PickerGroupTime(props) {
|
|
|
421
624
|
style: {
|
|
422
625
|
height: PICKER_LINE_HEIGHT * PICKER_VISIBLE_ITEMS
|
|
423
626
|
},
|
|
424
|
-
scrollTop: targetScrollTop
|
|
627
|
+
scrollTop: targetScrollTop
|
|
628
|
+
}, clickScrollViewProps, {
|
|
425
629
|
onScroll: handleScroll,
|
|
426
|
-
onTouchStart: () =>
|
|
630
|
+
onTouchStart: () => {
|
|
631
|
+
isTouchingRef.current = true;
|
|
632
|
+
},
|
|
427
633
|
onScrollEnd: handleScrollEnd,
|
|
428
634
|
scrollWithAnimation: true,
|
|
429
635
|
children: realPickerItems
|
|
430
|
-
})];
|
|
636
|
+
}))];
|
|
431
637
|
}
|
|
432
638
|
});
|
|
433
639
|
}
|
|
@@ -438,13 +644,20 @@ function PickerGroupDate(props) {
|
|
|
438
644
|
columnId,
|
|
439
645
|
updateDay,
|
|
440
646
|
selectedIndex = 0,
|
|
441
|
-
colors = {}
|
|
647
|
+
colors = {},
|
|
648
|
+
enableClickItemScroll = true
|
|
442
649
|
} = props;
|
|
443
650
|
const indicatorStyle = colors.lineColor ? getIndicatorStyle(colors.lineColor) : null;
|
|
444
651
|
const [targetScrollTop, setTargetScrollTop] = React.useState(0);
|
|
652
|
+
const [scrollIntoView, scrollToItemId] = usePickerItemScrollIntoView();
|
|
445
653
|
const scrollViewRef = React.useRef(null);
|
|
654
|
+
const itemRefs = React.useRef([]);
|
|
655
|
+
const selectedIndexPropRef = React.useRef(selectedIndex);
|
|
656
|
+
selectedIndexPropRef.current = selectedIndex;
|
|
657
|
+
const syncScrollFromPropsRef = React.useRef(false);
|
|
658
|
+
const pendingScrollSettleFocusRef = React.useRef(false);
|
|
446
659
|
const [currentIndex, setCurrentIndex] = React.useState(selectedIndex);
|
|
447
|
-
const
|
|
660
|
+
const isTouchingRef = React.useRef(false);
|
|
448
661
|
const lengthScaleRatioRef = React.useRef(1);
|
|
449
662
|
const useMeasuredScaleRef = React.useRef(false);
|
|
450
663
|
const itemHeightRef = React.useRef(PICKER_LINE_HEIGHT);
|
|
@@ -465,29 +678,36 @@ function PickerGroupDate(props) {
|
|
|
465
678
|
React.useEffect(() => {
|
|
466
679
|
itemHeightRef.current = calculateItemHeight(scrollViewRef.current, lengthScaleRatioRef.current, useMeasuredScaleRef.current);
|
|
467
680
|
}, [range.length]); // 只在range长度变化时重新计算
|
|
468
|
-
//
|
|
681
|
+
// props 的 selectedIndex 变化:滚至对应项并短时标记程序化滚动(syncScrollFromPropsRef)
|
|
469
682
|
React.useEffect(() => {
|
|
470
|
-
if (scrollViewRef.current && range.length > 0 && !
|
|
683
|
+
if (scrollViewRef.current && range.length > 0 && !isTouchingRef.current) {
|
|
684
|
+
syncScrollFromPropsRef.current = true;
|
|
471
685
|
const baseValue = selectedIndex * itemHeightRef.current;
|
|
472
686
|
setTargetScrollTopWithScale(setTargetScrollTop, baseValue, undefined, lengthScaleRatioRef.current);
|
|
473
687
|
setCurrentIndex(selectedIndex);
|
|
688
|
+
const tid = setTimeout(() => {
|
|
689
|
+
syncScrollFromPropsRef.current = false;
|
|
690
|
+
}, 400);
|
|
691
|
+
return () => clearTimeout(tid);
|
|
474
692
|
}
|
|
475
693
|
}, [selectedIndex, range]);
|
|
476
694
|
// 是否处于归中状态
|
|
477
695
|
const isCenterTimerId = React.useRef(null);
|
|
478
|
-
//
|
|
696
|
+
// 滚动静止后归中并同步选中
|
|
479
697
|
const handleScrollEnd = () => {
|
|
480
698
|
if (!scrollViewRef.current) return;
|
|
481
699
|
if (isCenterTimerId.current) {
|
|
482
700
|
clearTimeout(isCenterTimerId.current);
|
|
483
701
|
isCenterTimerId.current = null;
|
|
484
702
|
}
|
|
485
|
-
//
|
|
703
|
+
// 100ms 内无新滚动则归中并提交索引
|
|
486
704
|
isCenterTimerId.current = setTimeout(() => {
|
|
487
705
|
if (!scrollViewRef.current) return;
|
|
488
706
|
const scrollTop = scrollViewRef.current.scrollTop;
|
|
489
707
|
const newIndex = getSelectedIndex(scrollTop, itemHeightRef.current, lengthScaleRatioRef.current, useMeasuredScaleRef.current);
|
|
490
|
-
|
|
708
|
+
const allowA11yFocus = !syncScrollFromPropsRef.current;
|
|
709
|
+
const shouldFocusOnScrollSettle = pendingScrollSettleFocusRef.current;
|
|
710
|
+
isTouchingRef.current = false;
|
|
491
711
|
const baseValue = newIndex * itemHeightRef.current;
|
|
492
712
|
const randomOffset = Math.random() * 0.001; // 随机数为了在一个项内滚动时强制刷新
|
|
493
713
|
setTargetScrollTopWithScale(setTargetScrollTop, baseValue, randomOffset, lengthScaleRatioRef.current);
|
|
@@ -498,10 +718,15 @@ function PickerGroupDate(props) {
|
|
|
498
718
|
const numericValue = parseInt(valueText.replace(/[^0-9]/g, ''));
|
|
499
719
|
updateDay(isNaN(numericValue) ? 0 : numericValue, parseInt(columnId));
|
|
500
720
|
}
|
|
721
|
+
pendingScrollSettleFocusRef.current = false;
|
|
722
|
+
if (allowA11yFocus && shouldFocusOnScrollSettle) {
|
|
723
|
+
requestAccessibilityFocusOnView(itemRefs.current[newIndex]);
|
|
724
|
+
}
|
|
725
|
+
syncScrollFromPropsRef.current = false;
|
|
501
726
|
isCenterTimerId.current = null;
|
|
502
727
|
}, 100);
|
|
503
728
|
};
|
|
504
|
-
//
|
|
729
|
+
// 滚动中:按 scrollTop 更新高亮索引
|
|
505
730
|
const handleScroll = () => {
|
|
506
731
|
if (!scrollViewRef.current) return;
|
|
507
732
|
if (isCenterTimerId.current) {
|
|
@@ -509,17 +734,31 @@ function PickerGroupDate(props) {
|
|
|
509
734
|
isCenterTimerId.current = null;
|
|
510
735
|
}
|
|
511
736
|
const scrollTop = scrollViewRef.current.scrollTop;
|
|
512
|
-
const
|
|
737
|
+
const ih = itemHeightRef.current;
|
|
738
|
+
const newIndex = getSelectedIndex(scrollTop, ih, lengthScaleRatioRef.current, useMeasuredScaleRef.current);
|
|
739
|
+
const spi = selectedIndexPropRef.current;
|
|
740
|
+
if (newIndex !== spi) {
|
|
741
|
+
if (!syncScrollFromPropsRef.current || isTouchingRef.current) {
|
|
742
|
+
pendingScrollSettleFocusRef.current = true;
|
|
743
|
+
}
|
|
744
|
+
if (isTouchingRef.current) {
|
|
745
|
+
syncScrollFromPropsRef.current = false;
|
|
746
|
+
}
|
|
747
|
+
}
|
|
513
748
|
if (newIndex !== currentIndex) {
|
|
514
749
|
setCurrentIndex(newIndex);
|
|
515
750
|
}
|
|
516
751
|
};
|
|
517
752
|
// 渲染选项
|
|
518
753
|
const pickerItem = range.map((item, index) => {
|
|
519
|
-
return createComponent(View, {
|
|
754
|
+
return createComponent(View, mergeProps({
|
|
520
755
|
id: `picker-item-${columnId}-${index}`,
|
|
521
756
|
key: index,
|
|
522
|
-
|
|
757
|
+
ref: el => itemRefs.current[index] = el,
|
|
758
|
+
className: `taro-picker__item${index === currentIndex ? ' taro-picker__item--selected' : ''}`
|
|
759
|
+
}, enableClickItemScroll ? {
|
|
760
|
+
onClick: () => scrollToItemId(`picker-item-${columnId}-${index}`)
|
|
761
|
+
} : {}, {
|
|
523
762
|
get style() {
|
|
524
763
|
return {
|
|
525
764
|
height: PICKER_LINE_HEIGHT,
|
|
@@ -527,7 +766,7 @@ function PickerGroupDate(props) {
|
|
|
527
766
|
};
|
|
528
767
|
},
|
|
529
768
|
children: item
|
|
530
|
-
});
|
|
769
|
+
}));
|
|
531
770
|
});
|
|
532
771
|
const realPickerItems = [...new Array(PICKER_BLANK_ITEMS).fill(null).map((_, idx) => createComponent(View, {
|
|
533
772
|
key: `blank-top-${idx}`,
|
|
@@ -542,6 +781,10 @@ function PickerGroupDate(props) {
|
|
|
542
781
|
height: PICKER_LINE_HEIGHT
|
|
543
782
|
}
|
|
544
783
|
}))];
|
|
784
|
+
const clickScrollViewProps = enableClickItemScroll ? {
|
|
785
|
+
scrollIntoView,
|
|
786
|
+
scrollIntoViewAlignment: 'center'
|
|
787
|
+
} : {};
|
|
545
788
|
return createComponent(View, {
|
|
546
789
|
className: "taro-picker__group",
|
|
547
790
|
get children() {
|
|
@@ -551,7 +794,7 @@ function PickerGroupDate(props) {
|
|
|
551
794
|
className: "taro-picker__indicator"
|
|
552
795
|
}, indicatorStyle ? {
|
|
553
796
|
style: indicatorStyle
|
|
554
|
-
} : {})), createComponent(ScrollView, {
|
|
797
|
+
} : {})), createComponent(ScrollView, mergeProps({
|
|
555
798
|
ref: scrollViewRef,
|
|
556
799
|
scrollY: true,
|
|
557
800
|
showScrollbar: false,
|
|
@@ -559,13 +802,16 @@ function PickerGroupDate(props) {
|
|
|
559
802
|
style: {
|
|
560
803
|
height: PICKER_LINE_HEIGHT * PICKER_VISIBLE_ITEMS
|
|
561
804
|
},
|
|
562
|
-
scrollTop: targetScrollTop
|
|
805
|
+
scrollTop: targetScrollTop
|
|
806
|
+
}, clickScrollViewProps, {
|
|
563
807
|
onScroll: handleScroll,
|
|
564
|
-
onTouchStart: () =>
|
|
808
|
+
onTouchStart: () => {
|
|
809
|
+
isTouchingRef.current = true;
|
|
810
|
+
},
|
|
565
811
|
onScrollEnd: handleScrollEnd,
|
|
566
812
|
scrollWithAnimation: true,
|
|
567
813
|
children: realPickerItems
|
|
568
|
-
})];
|
|
814
|
+
}))];
|
|
569
815
|
}
|
|
570
816
|
});
|
|
571
817
|
}
|
|
@@ -578,17 +824,23 @@ function PickerGroupRegion(props) {
|
|
|
578
824
|
updateIndex,
|
|
579
825
|
selectedIndex = 0,
|
|
580
826
|
// 使用selectedIndex参数,默认为0
|
|
581
|
-
colors = {}
|
|
827
|
+
colors = {},
|
|
828
|
+
enableClickItemScroll = true
|
|
582
829
|
} = props;
|
|
583
830
|
const indicatorStyle = colors.lineColor ? getIndicatorStyle(colors.lineColor) : null;
|
|
584
831
|
const scrollViewRef = React.useRef(null);
|
|
585
832
|
const [targetScrollTop, setTargetScrollTop] = React.useState(0);
|
|
833
|
+
const [scrollIntoView, scrollToItemId] = usePickerItemScrollIntoView();
|
|
586
834
|
const [currentIndex, setCurrentIndex] = React.useState(selectedIndex);
|
|
587
|
-
const
|
|
835
|
+
const isTouchingRef = React.useRef(false);
|
|
588
836
|
const lengthScaleRatioRef = React.useRef(1);
|
|
589
837
|
const useMeasuredScaleRef = React.useRef(false);
|
|
590
838
|
const itemHeightRef = React.useRef(PICKER_LINE_HEIGHT);
|
|
591
|
-
const
|
|
839
|
+
const itemRefs = React.useRef([]);
|
|
840
|
+
const selectedIndexPropRef = React.useRef(selectedIndex);
|
|
841
|
+
selectedIndexPropRef.current = selectedIndex;
|
|
842
|
+
const syncScrollFromPropsRef = React.useRef(false);
|
|
843
|
+
const pendingScrollSettleFocusRef = React.useRef(false);
|
|
592
844
|
// 初始化时计算 lengthScaleRatio 并判定缩放模式
|
|
593
845
|
React.useEffect(() => {
|
|
594
846
|
Taro.getSystemInfo({
|
|
@@ -606,15 +858,20 @@ function PickerGroupRegion(props) {
|
|
|
606
858
|
React.useEffect(() => {
|
|
607
859
|
itemHeightRef.current = calculateItemHeight(scrollViewRef.current, lengthScaleRatioRef.current, useMeasuredScaleRef.current);
|
|
608
860
|
}, [range.length]); // 只在range长度变化时重新计算
|
|
609
|
-
//
|
|
861
|
+
// props 的 selectedIndex 变化:滚至对应项并短时标记程序化滚动(syncScrollFromPropsRef)
|
|
610
862
|
React.useEffect(() => {
|
|
611
|
-
if (scrollViewRef.current && range.length > 0 && !
|
|
863
|
+
if (scrollViewRef.current && range.length > 0 && !isTouchingRef.current) {
|
|
864
|
+
syncScrollFromPropsRef.current = true;
|
|
612
865
|
const baseValue = selectedIndex * itemHeightRef.current;
|
|
613
866
|
setTargetScrollTopWithScale(setTargetScrollTop, baseValue, undefined, lengthScaleRatioRef.current);
|
|
614
867
|
setCurrentIndex(selectedIndex);
|
|
868
|
+
const tid = setTimeout(() => {
|
|
869
|
+
syncScrollFromPropsRef.current = false;
|
|
870
|
+
}, 400);
|
|
871
|
+
return () => clearTimeout(tid);
|
|
615
872
|
}
|
|
616
873
|
}, [selectedIndex, range]);
|
|
617
|
-
//
|
|
874
|
+
// 滚动静止后归中(debounce)
|
|
618
875
|
const isCenterTimerId = React.useRef(null);
|
|
619
876
|
const handleScrollEnd = () => {
|
|
620
877
|
if (!scrollViewRef.current) return;
|
|
@@ -622,19 +879,27 @@ function PickerGroupRegion(props) {
|
|
|
622
879
|
clearTimeout(isCenterTimerId.current);
|
|
623
880
|
isCenterTimerId.current = null;
|
|
624
881
|
}
|
|
625
|
-
//
|
|
882
|
+
// 100ms 内无新滚动则归中并提交索引
|
|
626
883
|
isCenterTimerId.current = setTimeout(() => {
|
|
627
884
|
if (!scrollViewRef.current) return;
|
|
628
885
|
const scrollTop = scrollViewRef.current.scrollTop;
|
|
629
886
|
const newIndex = getSelectedIndex(scrollTop, itemHeightRef.current, lengthScaleRatioRef.current, useMeasuredScaleRef.current);
|
|
630
|
-
|
|
887
|
+
const allowA11yFocus = !syncScrollFromPropsRef.current;
|
|
888
|
+
const shouldFocusOnScrollSettle = pendingScrollSettleFocusRef.current;
|
|
889
|
+
isTouchingRef.current = false;
|
|
631
890
|
const baseValue = newIndex * itemHeightRef.current;
|
|
632
891
|
const randomOffset = Math.random() * 0.001; // 随机数为了在一个项内滚动时强制刷新
|
|
633
892
|
setTargetScrollTopWithScale(setTargetScrollTop, baseValue, randomOffset, lengthScaleRatioRef.current);
|
|
634
|
-
updateIndex(newIndex, columnId, false,
|
|
893
|
+
updateIndex(newIndex, columnId, false, allowA11yFocus);
|
|
894
|
+
pendingScrollSettleFocusRef.current = false;
|
|
895
|
+
if (allowA11yFocus && shouldFocusOnScrollSettle) {
|
|
896
|
+
requestAccessibilityFocusOnView(itemRefs.current[newIndex]);
|
|
897
|
+
}
|
|
898
|
+
syncScrollFromPropsRef.current = false;
|
|
899
|
+
isCenterTimerId.current = null;
|
|
635
900
|
}, 100);
|
|
636
901
|
};
|
|
637
|
-
//
|
|
902
|
+
// 滚动中:按 scrollTop 更新高亮索引
|
|
638
903
|
const handleScroll = () => {
|
|
639
904
|
if (!scrollViewRef.current) return;
|
|
640
905
|
if (isCenterTimerId.current) {
|
|
@@ -642,7 +907,17 @@ function PickerGroupRegion(props) {
|
|
|
642
907
|
isCenterTimerId.current = null;
|
|
643
908
|
}
|
|
644
909
|
const scrollTop = scrollViewRef.current.scrollTop;
|
|
645
|
-
const
|
|
910
|
+
const ih = itemHeightRef.current;
|
|
911
|
+
const newIndex = getSelectedIndex(scrollTop, ih, lengthScaleRatioRef.current, useMeasuredScaleRef.current);
|
|
912
|
+
const spi = selectedIndexPropRef.current;
|
|
913
|
+
if (newIndex !== spi) {
|
|
914
|
+
if (!syncScrollFromPropsRef.current || isTouchingRef.current) {
|
|
915
|
+
pendingScrollSettleFocusRef.current = true;
|
|
916
|
+
}
|
|
917
|
+
if (isTouchingRef.current) {
|
|
918
|
+
syncScrollFromPropsRef.current = false;
|
|
919
|
+
}
|
|
920
|
+
}
|
|
646
921
|
if (newIndex !== currentIndex) {
|
|
647
922
|
setCurrentIndex(newIndex);
|
|
648
923
|
}
|
|
@@ -650,10 +925,14 @@ function PickerGroupRegion(props) {
|
|
|
650
925
|
// 渲染选项
|
|
651
926
|
const pickerItem = range.map((item, index) => {
|
|
652
927
|
const content = rangeKey && item && typeof item === 'object' ? item[rangeKey] : item;
|
|
653
|
-
return createComponent(View, {
|
|
928
|
+
return createComponent(View, mergeProps({
|
|
654
929
|
id: `picker-item-${columnId}-${index}`,
|
|
655
930
|
key: index,
|
|
656
|
-
|
|
931
|
+
ref: el => itemRefs.current[index] = el,
|
|
932
|
+
className: `taro-picker__item${index === currentIndex ? ' taro-picker__item--selected' : ''}`
|
|
933
|
+
}, enableClickItemScroll ? {
|
|
934
|
+
onClick: () => scrollToItemId(`picker-item-${columnId}-${index}`)
|
|
935
|
+
} : {}, {
|
|
657
936
|
get style() {
|
|
658
937
|
return {
|
|
659
938
|
height: PICKER_LINE_HEIGHT,
|
|
@@ -661,7 +940,7 @@ function PickerGroupRegion(props) {
|
|
|
661
940
|
};
|
|
662
941
|
},
|
|
663
942
|
children: content
|
|
664
|
-
});
|
|
943
|
+
}));
|
|
665
944
|
});
|
|
666
945
|
const realPickerItems = [...new Array(PICKER_BLANK_ITEMS).fill(null).map((_, idx) => createComponent(View, {
|
|
667
946
|
key: `blank-top-${idx}`,
|
|
@@ -676,6 +955,10 @@ function PickerGroupRegion(props) {
|
|
|
676
955
|
height: PICKER_LINE_HEIGHT
|
|
677
956
|
}
|
|
678
957
|
}))];
|
|
958
|
+
const clickScrollViewProps = enableClickItemScroll ? {
|
|
959
|
+
scrollIntoView,
|
|
960
|
+
scrollIntoViewAlignment: 'center'
|
|
961
|
+
} : {};
|
|
679
962
|
return createComponent(View, {
|
|
680
963
|
className: "taro-picker__group",
|
|
681
964
|
get children() {
|
|
@@ -685,7 +968,7 @@ function PickerGroupRegion(props) {
|
|
|
685
968
|
className: "taro-picker__indicator"
|
|
686
969
|
}, indicatorStyle ? {
|
|
687
970
|
style: indicatorStyle
|
|
688
|
-
} : {})), createComponent(ScrollView, {
|
|
971
|
+
} : {})), createComponent(ScrollView, mergeProps({
|
|
689
972
|
ref: scrollViewRef,
|
|
690
973
|
scrollY: true,
|
|
691
974
|
showScrollbar: false,
|
|
@@ -693,16 +976,16 @@ function PickerGroupRegion(props) {
|
|
|
693
976
|
style: {
|
|
694
977
|
height: PICKER_LINE_HEIGHT * PICKER_VISIBLE_ITEMS
|
|
695
978
|
},
|
|
696
|
-
scrollTop: targetScrollTop
|
|
979
|
+
scrollTop: targetScrollTop
|
|
980
|
+
}, clickScrollViewProps, {
|
|
697
981
|
onScroll: handleScroll,
|
|
698
982
|
onTouchStart: () => {
|
|
699
|
-
|
|
700
|
-
isUserBeginScrollRef.current = true;
|
|
983
|
+
isTouchingRef.current = true;
|
|
701
984
|
},
|
|
702
985
|
onScrollEnd: handleScrollEnd,
|
|
703
986
|
scrollWithAnimation: true,
|
|
704
987
|
children: realPickerItems
|
|
705
|
-
})];
|
|
988
|
+
}))];
|
|
706
989
|
}
|
|
707
990
|
});
|
|
708
991
|
}
|