@tarojs/components 3.6.0-canary.6 → 3.6.0-canary.7

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.
@@ -1,589 +0,0 @@
1
- import { getRTLOffsetType } from '../domHelpers'
2
- import { memoizeOne } from '../memoize'
3
- import { cancelTimeout, requestTimeout } from '../timer'
4
-
5
- const IS_SCROLLING_DEBOUNCE_INTERVAL = 150
6
-
7
- const defaultItemKey = (index) => index
8
-
9
- function createListComponent ({
10
- getItemOffset,
11
- getEstimatedTotalSize,
12
- getItemSize,
13
- getOffsetForIndexAndAlignment,
14
- getStartIndexForOffset,
15
- getStopIndexForStartIndex,
16
- initInstanceProps,
17
- shouldResetStyleCacheOnItemSizeChange,
18
- Vue
19
- }) {
20
- return {
21
- props: {
22
- direction: {
23
- type: String,
24
- default: 'ltr'
25
- },
26
- itemData: Array,
27
- layout: {
28
- type: String,
29
- default: 'vertical'
30
- },
31
- useIsScrolling: {
32
- type: Boolean,
33
- default: false
34
- },
35
- overscanCount: {
36
- type: Number,
37
- default: 1
38
- },
39
- wclass: String,
40
- height: {},
41
- innerRef: String,
42
- innerElementType: {
43
- type: String,
44
- default: 'view'
45
- },
46
- itemCount: Number,
47
- wstyle: String,
48
- width: String,
49
- itemSize: {
50
- required: true
51
- },
52
- item: {
53
- required: true
54
- },
55
- initialScrollOffset: {
56
- type: String,
57
- defalt: 0
58
- },
59
- scrollNative: Function
60
- },
61
- data () {
62
- return {
63
- instance: this,
64
- isScrolling: false,
65
- scrollDirection: 'forward',
66
- scrollOffset:
67
- typeof this.$props.initialScrollOffset === 'number'
68
- ? this.$props.initialScrollOffset
69
- : 0,
70
- scrollUpdateWasRequested: false,
71
- resetIsScrollingTimeoutId: null
72
- }
73
- },
74
- methods: {
75
- _instanceProps () {
76
- initInstanceProps(this.$props, this)
77
- },
78
- scrollTo (scrollOffset) {
79
- scrollOffset = Math.max(0, scrollOffset)
80
-
81
- if (this.scrollOffset === scrollOffset) {
82
- return
83
- }
84
-
85
- this.scrollDirection = this.scrollOffset < scrollOffset ? 'forward' : 'backward'
86
- this.scrollOffset = scrollOffset
87
- this.scrollUpdateWasRequested = true
88
-
89
- Vue.nextTick(this._resetIsScrollingDebounced)
90
- },
91
-
92
- scrollToItem (index, align = 'auto') {
93
- const { itemCount } = this.$props
94
- const { scrollOffset } = this.$data
95
-
96
- index = Math.max(0, Math.min(index, itemCount - 1))
97
-
98
- this.scrollTo(
99
- getOffsetForIndexAndAlignment(
100
- this.$props,
101
- index,
102
- align,
103
- scrollOffset,
104
- this._instanceProps()
105
- )
106
- )
107
- },
108
-
109
- _callOnItemsRendered: memoizeOne(
110
- function (
111
- overscanStartIndex,
112
- overscanStopIndex,
113
- visibleStartIndex,
114
- visibleStopIndex
115
- ) {
116
- return this.$props.onItemsRendered({
117
- overscanStartIndex,
118
- overscanStopIndex,
119
- visibleStartIndex,
120
- visibleStopIndex
121
- })
122
- }
123
- ),
124
-
125
- _callOnScroll: memoizeOne(
126
- function (
127
- scrollDirection,
128
- scrollOffset,
129
- scrollUpdateWasRequested
130
- ) {
131
- this.$emit('scroll', {
132
- scrollDirection,
133
- scrollOffset,
134
- scrollUpdateWasRequested
135
- })
136
- }
137
- ),
138
-
139
- _callPropsCallbacks () {
140
- if (typeof this.$props.onItemsRendered === 'function') {
141
- const { itemCount } = this.$props
142
- if (itemCount > 0) {
143
- const [
144
- overscanStartIndex,
145
- overscanStopIndex,
146
- visibleStartIndex,
147
- visibleStopIndex
148
- ] = this._getRangeToRender()
149
- this._callOnItemsRendered(
150
- overscanStartIndex,
151
- overscanStopIndex,
152
- visibleStartIndex,
153
- visibleStopIndex
154
- )
155
- }
156
- }
157
-
158
- this._callOnScroll(
159
- this.scrollDirection,
160
- this.scrollOffset,
161
- this.scrollUpdateWasRequested
162
- )
163
- },
164
-
165
- _getStyleValue (value) {
166
- return typeof value === 'number'
167
- ? value + 'px'
168
- : value == null
169
- ? ''
170
- : value
171
- },
172
-
173
- _getItemStyle (index) {
174
- const { direction, itemSize, layout } = this.$props
175
-
176
- const itemStyleCache = this._getItemStyleCache(
177
- shouldResetStyleCacheOnItemSizeChange && itemSize,
178
- shouldResetStyleCacheOnItemSizeChange && layout,
179
- shouldResetStyleCacheOnItemSizeChange && direction
180
- )
181
-
182
- let style
183
- if (itemStyleCache.hasOwnProperty(index)) {
184
- style = itemStyleCache[index]
185
- } else {
186
- const offset = getItemOffset(this.$props, index, this._instanceProps())
187
- const size = getItemSize(this.$props, index, this._instanceProps())
188
-
189
- // TODO Deprecate direction "horizontal"
190
- const isHorizontal =
191
- direction === 'horizontal' || layout === 'horizontal'
192
-
193
- const isRtl = direction === 'rtl'
194
- const offsetHorizontal = isHorizontal ? offset : 0
195
- itemStyleCache[index] = style = {
196
- position: 'absolute',
197
- left: isRtl ? undefined : offsetHorizontal,
198
- right: isRtl ? offsetHorizontal : undefined,
199
- top: !isHorizontal ? offset : 0,
200
- height: !isHorizontal ? size : '100%',
201
- width: isHorizontal ? size : '100%'
202
- }
203
- }
204
-
205
- for (const k in style) {
206
- if (style.hasOwnProperty(k)) {
207
- style[k] = this._getStyleValue(style[k])
208
- }
209
- }
210
-
211
- return style
212
- },
213
-
214
- _getItemStyleCache: memoizeOne(() => ({})),
215
-
216
- _getRangeToRender () {
217
- const { itemCount, overscanCount } = this.$props
218
- const { isScrolling, scrollDirection, scrollOffset } = this.$data
219
-
220
- if (itemCount === 0) {
221
- return [0, 0, 0, 0]
222
- }
223
-
224
- const startIndex = getStartIndexForOffset(
225
- this.$props,
226
- scrollOffset,
227
- this._instanceProps()
228
- )
229
- const stopIndex = getStopIndexForStartIndex(
230
- this.$props,
231
- startIndex,
232
- scrollOffset,
233
- this._instanceProps()
234
- )
235
-
236
- // Overscan by one item in each direction so that tab/focus works.
237
- // If there isn't at least one extra item, tab loops back around.
238
- const overscanBackward =
239
- !isScrolling || scrollDirection === 'backward'
240
- ? Math.max(1, overscanCount)
241
- : 1
242
- const overscanForward =
243
- !isScrolling || scrollDirection === 'forward'
244
- ? Math.max(1, overscanCount)
245
- : 1
246
-
247
- return [
248
- Math.max(0, startIndex - overscanBackward),
249
- Math.max(0, Math.min(itemCount - 1, stopIndex + overscanForward)),
250
- startIndex,
251
- stopIndex
252
- ]
253
- },
254
-
255
- _onScrollHorizontal (event) {
256
- const clientWidth = this.$props.width
257
- const { scrollLeft, scrollWidth } = event.currentTarget
258
- if (this.$props.scrollNative) {
259
- this.$props.scrollNative(event)
260
- }
261
- if (this.scrollOffset === scrollLeft) {
262
- return
263
- }
264
- const { direction } = this.$props
265
-
266
- let scrollOffset = scrollLeft
267
- if (direction === 'rtl') {
268
- // TRICKY According to the spec, scrollLeft should be negative for RTL aligned elements.
269
- // This is not the case for all browsers though (e.g. Chrome reports values as positive, measured relative to the left).
270
- // It's also easier for this component if we convert offsets to the same format as they would be in for ltr.
271
- // So the simplest solution is to determine which browser behavior we're dealing with, and convert based on it.
272
- switch (getRTLOffsetType()) {
273
- case 'negative':
274
- scrollOffset = -scrollLeft
275
- break
276
- case 'positive-descending':
277
- scrollOffset = scrollWidth - clientWidth - scrollLeft
278
- break
279
- }
280
- }
281
-
282
- // Prevent Safari's elastic scrolling from causing visual shaking when scrolling past bounds.
283
- scrollOffset = Math.max(
284
- 0,
285
- Math.min(scrollOffset, scrollWidth - clientWidth)
286
- )
287
- this.isScrolling = true
288
- this.scrollDirection = this.scrollOffset < scrollLeft ? 'forward' : 'backward'
289
- this.scrollOffset = scrollOffset
290
- this.scrollUpdateWasRequested = false
291
- Vue.nextTick(this._resetIsScrollingDebounced)
292
- },
293
-
294
- _onScrollVertical (event) {
295
- const clientHeight = this.$props.height
296
- const { scrollHeight, scrollTop } = event.currentTarget
297
- if (this.$props.scrollNative) {
298
- this.$props.scrollNative(event)
299
- }
300
- if (this.scrollOffset === scrollTop) {
301
- return
302
- }
303
-
304
- // Prevent Safari's elastic scrolling from causing visual shaking when scrolling past bounds.
305
- const scrollOffset = Math.max(
306
- 0,
307
- Math.min(scrollTop, scrollHeight - clientHeight)
308
- )
309
-
310
- this.isScrolling = true
311
- this.scrollDirection = this.scrollOffset < scrollOffset ? 'forward' : 'backward'
312
- this.scrollOffset = scrollOffset
313
- this.scrollUpdateWasRequested = false
314
- Vue.nextTick(this._resetIsScrollingDebounced)
315
- },
316
-
317
- _resetIsScrollingDebounced () {
318
- if (this.resetIsScrollingTimeoutId !== null) {
319
- cancelTimeout(this.resetIsScrollingTimeoutId)
320
- }
321
-
322
- this.resetIsScrollingTimeoutId = requestTimeout(
323
- this._resetIsScrolling,
324
- IS_SCROLLING_DEBOUNCE_INTERVAL
325
- )
326
- },
327
-
328
- _resetIsScrolling () {
329
- this.resetIsScrollingTimeoutId = null
330
- this.isScrolling = false
331
- Vue.nextTick(() => {
332
- this._getItemStyleCache(-1, null)
333
- })
334
- }
335
- },
336
- mounted () {
337
- const { direction, initialScrollOffset, layout } = this.$props
338
-
339
- if (typeof initialScrollOffset === 'number' && this._outerRef != null) {
340
- const outerRef = this._outerRef
341
- // TODO Deprecate direction "horizontal"
342
- if (direction === 'horizontal' || layout === 'horizontal') {
343
- outerRef.scrollLeft = initialScrollOffset
344
- } else {
345
- outerRef.scrollTop = initialScrollOffset
346
- }
347
- }
348
-
349
- this._callPropsCallbacks()
350
- },
351
- updated () {
352
- const { direction, layout } = this.$props
353
- const { scrollOffset, scrollUpdateWasRequested } = this.$data
354
-
355
- if (scrollUpdateWasRequested && this._outerRef != null) {
356
- const outerRef = this._outerRef
357
-
358
- // TODO Deprecate direction "horizontal"
359
- if (direction === 'horizontal' || layout === 'horizontal') {
360
- if (direction === 'rtl') {
361
- // TRICKY According to the spec, scrollLeft should be negative for RTL aligned elements.
362
- // This is not the case for all browsers though (e.g. Chrome reports values as positive, measured relative to the left).
363
- // So we need to determine which browser behavior we're dealing with, and mimic it.
364
- switch (getRTLOffsetType()) {
365
- case 'negative':
366
- outerRef.scrollLeft = -scrollOffset
367
- break
368
- case 'positive-ascending':
369
- outerRef.scrollLeft = scrollOffset
370
- break
371
- default: {
372
- const { clientWidth, scrollWidth } = outerRef
373
- outerRef.scrollLeft = scrollWidth - clientWidth - scrollOffset
374
- break
375
- }
376
- }
377
- } else {
378
- outerRef.scrollLeft = scrollOffset
379
- }
380
- } else {
381
- outerRef.scrollTop = scrollOffset
382
- }
383
- }
384
-
385
- this._callPropsCallbacks()
386
- },
387
-
388
- beforeDestroy () {
389
- if (this.resetIsScrollingTimeoutId !== null) {
390
- cancelTimeout(this.resetIsScrollingTimeoutId)
391
- }
392
- },
393
-
394
- render (h) {
395
- const {
396
- item,
397
- wclass,
398
- direction,
399
- height,
400
- innerRef,
401
- innerElementType,
402
- itemCount,
403
- itemData,
404
- itemKey = defaultItemKey,
405
- layout,
406
- wstyle,
407
- useIsScrolling,
408
- width
409
- } = this.$props
410
- const { isScrolling } = this.$data
411
-
412
- // TODO Deprecate direction "horizontal"
413
- const isHorizontal =
414
- direction === 'horizontal' || layout === 'horizontal'
415
-
416
- const onScroll = isHorizontal
417
- ? this._onScrollHorizontal
418
- : this._onScrollVertical
419
-
420
- const [startIndex, stopIndex] = this._getRangeToRender()
421
-
422
- const items = []
423
- if (itemCount > 0) {
424
- for (let index = startIndex; index <= stopIndex; index++) {
425
- items.push(
426
- h(item, {
427
- key: itemKey(index, itemData),
428
- props: {
429
- data: itemData,
430
- index,
431
- isScrolling: useIsScrolling ? isScrolling : undefined,
432
- css: this._getItemStyle(index)
433
- }
434
- })
435
- )
436
- }
437
- }
438
-
439
- // Read this value AFTER items have been created,
440
- // So their actual sizes (if variable) are taken into consideration.
441
- const estimatedTotalSize = getEstimatedTotalSize(
442
- this.$props,
443
- this._instanceProps()
444
- )
445
-
446
- const scrollViewName = process.env.TARO_ENV === 'h5' ? 'taro-scroll-view' : 'scroll-view'
447
- return h(
448
- scrollViewName,
449
- {
450
- class: wclass,
451
- ref: this._outerRefSetter,
452
- style: {
453
- position: 'relative',
454
- height: this._getStyleValue(height),
455
- width: this._getStyleValue(width),
456
- overflow: 'auto',
457
- WebkitOverflowScrolling: 'touch',
458
- willChange: 'transform',
459
- direction,
460
- ...wstyle
461
- },
462
- attrs: {
463
- scrollY: layout === 'vertical',
464
- scrollX: layout === 'horizontal'
465
- },
466
- on: {
467
- scroll: onScroll
468
- }
469
- },
470
- [
471
- h(
472
- innerElementType,
473
- {
474
- ref: innerRef,
475
- style: {
476
- height: this._getStyleValue(isHorizontal ? '100%' : estimatedTotalSize),
477
- pointerEvents: isScrolling ? 'none' : undefined,
478
- width: this._getStyleValue(isHorizontal ? estimatedTotalSize : '100%')
479
- }
480
- },
481
- items
482
- )
483
- ]
484
- )
485
- }
486
- }
487
- }
488
-
489
- export default {
490
- install: (Vue) => {
491
- const VirtualList = createListComponent({
492
- Vue,
493
- getItemOffset: ({
494
- itemSize
495
- }, index) => index * itemSize,
496
- getItemSize: ({
497
- itemSize
498
- }) => itemSize,
499
- getEstimatedTotalSize: ({
500
- itemCount,
501
- itemSize
502
- }) => itemSize * itemCount,
503
- getOffsetForIndexAndAlignment: ({
504
- direction,
505
- height,
506
- itemCount,
507
- itemSize,
508
- layout,
509
- width
510
- }, index, align, scrollOffset) => {
511
- // TODO Deprecate direction "horizontal"
512
- const isHorizontal = direction === 'horizontal' || layout === 'horizontal'
513
- const size = isHorizontal ? width : height
514
- const lastItemOffset = Math.max(0, itemCount * itemSize - size)
515
- const maxOffset = Math.min(lastItemOffset, index * itemSize)
516
- const minOffset = Math.max(0, index * itemSize - size + itemSize)
517
-
518
- if (align === 'smart') {
519
- if (scrollOffset >= minOffset - size && scrollOffset <= maxOffset + size) {
520
- align = 'auto'
521
- } else {
522
- align = 'center'
523
- }
524
- }
525
-
526
- switch (align) {
527
- case 'start':
528
- return maxOffset
529
-
530
- case 'end':
531
- return minOffset
532
-
533
- case 'center':
534
- {
535
- // "Centered" offset is usually the average of the min and max.
536
- // But near the edges of the list, this doesn't hold true.
537
- const middleOffset = Math.round(minOffset + (maxOffset - minOffset) / 2)
538
-
539
- if (middleOffset < Math.ceil(size / 2)) {
540
- return 0 // near the beginning
541
- } else if (middleOffset > lastItemOffset + Math.floor(size / 2)) {
542
- return lastItemOffset // near the end
543
- } else {
544
- return middleOffset
545
- }
546
- }
547
-
548
- case 'auto':
549
- default:
550
- if (scrollOffset >= minOffset && scrollOffset <= maxOffset) {
551
- return scrollOffset
552
- } else if (scrollOffset < minOffset) {
553
- return minOffset
554
- } else {
555
- return maxOffset
556
- }
557
- }
558
- },
559
- getStartIndexForOffset: ({
560
- itemCount,
561
- itemSize
562
- }, offset) => {
563
- return Math.max(0, Math.min(itemCount - 1, Math.floor(offset / itemSize)))
564
- },
565
- getStopIndexForStartIndex: ({
566
- direction,
567
- height,
568
- itemCount,
569
- itemSize,
570
- layout,
571
- width
572
- }, startIndex, scrollOffset) => {
573
- // TODO Deprecate direction "horizontal"
574
- const isHorizontal = direction === 'horizontal' || layout === 'horizontal'
575
- const offset = startIndex * itemSize
576
- const size = isHorizontal ? width : height
577
- const numVisibleItems = Math.ceil((size + scrollOffset - offset) / itemSize)
578
- return Math.max(0, Math.min(itemCount - 1, startIndex + numVisibleItems - 1 // -1 is because stop index is inclusive
579
- ))
580
- },
581
-
582
- initInstanceProps () { // Noop
583
- },
584
-
585
- shouldResetStyleCacheOnItemSizeChange: true
586
- })
587
- Vue.component('virtual-list', VirtualList)
588
- }
589
- }