Skip to content

Commit

Permalink
feat: picker (#8)
Browse files Browse the repository at this point in the history
feat: 添加picker组件
  • Loading branch information
zuolung authored Sep 17, 2021
1 parent 8544b6b commit aba52e3
Show file tree
Hide file tree
Showing 9 changed files with 568 additions and 1 deletion.
20 changes: 19 additions & 1 deletion packages/vantui-demo/src/pages/index/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { View } from '@tarojs/components'
import { useEffect, useState } from 'react'
import { useEffect, useState, useCallback } from 'react'
import { useDidHide, useDidShow } from '@tarojs/taro'
import {
Progress,
Expand All @@ -18,12 +18,15 @@ import {
Skeleton,
Tag,
CountDown,
Picker,
Tabbar,
TabbarItem,
} from '@antmjs/vantui'

import './index.less'

const columns = ['杭州', '宁波', '温州', '绍兴', '湖州', '嘉兴', '金华', '衢州']

export default function Index() {
const [rate, setRate] = useState(2.5)
const [serachValue] = useState('ff')
Expand All @@ -40,6 +43,14 @@ export default function Index() {
console.info('index page hide.')
})

const onChange = useCallback(function (a) {
console.info(a, 'picker onChange')
}, [])

const onConfirm = useCallback(function (a) {
console.info(a, 'picker onConfirm')
}, [])

return (
<View className="pages-index-index">
<NavBar
Expand Down Expand Up @@ -181,6 +192,13 @@ export default function Index() {
<Loading type="circular" size={80}>
加载中...
</Loading>

<Picker
title="标题"
columns={columns}
onChange={onChange}
onConfirm={onConfirm}
/>
<Tabbar active={1}>
<TabbarItem info={null} name={null} icon="home-o">
标签
Expand Down
200 changes: 200 additions & 0 deletions packages/vantui/src/components/picker-column/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
/* eslint-disable react-hooks/exhaustive-deps */
import {
useEffect,
useState,
useCallback,
useImperativeHandle,
forwardRef,
} from 'react'
import { View } from '@tarojs/components'
import * as utils from '../wxs/utils'
import { PickerColumnProps } from '../../../types/picker-column'
import { range } from '../common/utils.js'
import { isObj } from '../common/validator.js'
import * as computed from './wxs'

const DEFAULT_DURATION = 200

function Index(props: PickerColumnProps, ref: any): JSX.Element {
const {
valueKey,
itemHeight = 88,
visibleItemCount = 5,
initialOptions,
defaultIndex,
className,
style,
onChange,
...others
} = props

const [options, setOptions] = useState<Array<any>>([])
const [currentIndex, setCurrentIndex] = useState(0)
const [duration, setDuration] = useState(0)
const [startY, setStartY] = useState(0)
const [offset, setOffset] = useState(0)
const [startOffset, setStartOffset] = useState(0)

const adjustIndex = useCallback(
function (index: number): any {
const initialOptions_ = initialOptions as Array<any>
const count = initialOptions_.length
index = range(index, 0, count)
for (let i = index; i < count; i++) {
if (!isDisabled(initialOptions_[i])) {
return i
}
}
for (let i = index - 1; i >= 0; i--) {
if (!isDisabled(initialOptions_[i])) {
return i
}
}
},
[initialOptions],
)

const setIndex = useCallback(
function (index: number, userAction?: boolean) {
index = adjustIndex(index) || 0
const offset = -index * Number(itemHeight)
if (index !== currentIndex) {
setCurrentIndex(currentIndex)
setOffset(offset)
if (onChange && userAction) onChange(index)
return
}
return setOffset(offset)
},
[adjustIndex, currentIndex, itemHeight],
)

useEffect(
function () {
setCurrentIndex((defaultIndex as number) || 0)
setOptions(initialOptions || [])
setTimeout(() => {
setIndex((defaultIndex as number) || 0)
})
},
[defaultIndex, initialOptions, setIndex],
)

const isDisabled = useCallback(function (option) {
return isObj(option) && option.disabled
}, [])

const onTouchMove = useCallback(
function (event) {
event.preventDefault()
const deltaY = event.touches[0].clientY - startY
setOffset(
range(
startOffset + deltaY,
-(options.length * Number(itemHeight)),
itemHeight,
),
)
},
[startOffset, itemHeight, options],
)

const onTouchStart = useCallback(
function (event) {
event.preventDefault()
setStartY(event.touches[0].clientY)
setStartOffset(offset)
setDuration(0)
},
[offset],
)

const onTouchEnd = useCallback(
function () {
if (offset !== startOffset) {
setDuration(DEFAULT_DURATION)
const index = range(
Math.round(-offset / Number(itemHeight)),
0,
options.length - 1,
)
setIndex(index, true)
}
},
[startOffset, offset, itemHeight],
)

const onClickItem = useCallback(function (event) {
const { index } = event.currentTarget.dataset
setIndex(Number(index), true)
}, [])

const getCurrentIndex = useCallback(function () {
return currentIndex
}, [])

const getValue = useCallback(
function () {
return options[currentIndex]
},
[options, currentIndex],
)

useImperativeHandle(ref, () => {
return {
getCurrentIndex,
getValue,
}
})

return (
<View
className={`van-picker-column custom-class ${className}`}
style={utils.style([
computed.rootStyle({
itemHeight,
visibleItemCount,
}),
style,
])}
onTouchStart={onTouchStart}
onTouchMove={onTouchMove}
onTouchEnd={onTouchEnd}
onTouchCancel={onTouchEnd}
{...others}
>
<View
style={computed.wrapperStyle({
offset,
itemHeight,
visibleItemCount,
duration,
})}
>
{options.map((option: any, index: number) => {
return (
<View
key={`picker-column__item${index}`}
data-index={index}
style={computed.styleTran({ height: itemHeight })}
className={
'van-ellipsis ' +
utils.bem('picker-column__item', {
disabled: option && option.disabled,
selected: index === currentIndex,
}) +
' ' +
(index === currentIndex ? 'active-class' : '')
}
onClick={onClickItem}
>
{computed.optionText(option, valueKey)}
</View>
)
})}
</View>
</View>
)
}

export default forwardRef(Index)
39 changes: 39 additions & 0 deletions packages/vantui/src/components/picker-column/wxs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { style } from '../wxs/utils'
import { addUnit } from '../wxs/add-unit'

function isObj(x: any) {
const type = typeof x
return x !== null && (type === 'object' || type === 'function')
}

function optionText(option: any, valueKey: any) {
return isObj(option) && option[valueKey] != null ? option[valueKey] : option
}

function rootStyle(data: any) {
return style({
height: addUnit(data.itemHeight * data.visibleItemCount),
})
}

function wrapperStyle(data: any) {
const offset = addUnit(
data.offset + (data.itemHeight * (data.visibleItemCount - 1)) / 2,
)

return style({
transition: 'transform ' + data.duration + 'ms',
'line-height': addUnit(data.itemHeight),
transform: 'translate3d(0, ' + offset + ', 0)',
})
}

function styleTran(data: any) {
const res: any = {}
Object.keys(data).map((key) => {
res[key] = addUnit(data[key])
})
return style(res)
}

export { optionText, rootStyle, wrapperStyle, styleTran }
Loading

0 comments on commit aba52e3

Please sign in to comment.