Skip to content

Commit

Permalink
feat: datetimepicker (#16)
Browse files Browse the repository at this point in the history
feat: 优化datetimepicker
  • Loading branch information
zuolung authored Sep 24, 2021
1 parent 06182dd commit 732a251
Show file tree
Hide file tree
Showing 9 changed files with 525 additions and 30 deletions.
17 changes: 17 additions & 0 deletions packages/vantui-demo/src/pages/index/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import {
GoodsAction,
GoodsActionIcon,
GoodsActionButton,
DatetimePicker,
// NavBar,
} from '@antmjs/vantui'

Expand All @@ -50,14 +51,19 @@ const option2 = [
{ text: '销量排序', value: 'c' },
]
const columns = ['杭州', '宁波', '温州', '绍兴', '湖州', '嘉兴', '金华', '衢州']
const minDate = new Date().getTime()
const maxDate = new Date(2019, 10, 1).getTime()

export default function Index() {
const [rate, setRate] = useState(2.5)
const NotifyInstance = useRef<any>(null)
const [serachValue] = useState('ff')
const [currentDate, setCurrentDate] = useState<any>()

const value1 = 0
const value2 = 'a'
useEffect(function () {
setCurrentDate(new Date().getTime())
console.info('index page load.')
return function () {
console.info('index page unload.')
Expand Down Expand Up @@ -85,6 +91,10 @@ export default function Index() {
console.info(a, 'picker onConfirm')
}, [])

const onInput = function (e: any) {
console.info(e, 'DatetimePicker onInput')
}

// const x = useRef<ICountDownRef | undefined>()

return (
Expand Down Expand Up @@ -116,6 +126,13 @@ export default function Index() {
<Icon name="chat" size={40} info="9" />
<Icon name="chat" size={40} info="99+" />
</View>
<DatetimePicker
type="datetime"
value={currentDate}
min-date={minDate}
max-date={maxDate}
onInput={onInput}
/>
<View>
<Skeleton title avatar row={3} />
</View>
Expand Down
298 changes: 298 additions & 0 deletions packages/vantui/src/components/datetimePicker/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,298 @@
/* eslint-disable react-hooks/exhaustive-deps */
import { useState, useCallback, useEffect, useRef } from 'react'
import VanPicker from '../picker/index'
import { DatetimePickerProps } from '../../../types/datetime-picker'
import * as utils from '../wxs/utils'
import {
defaultFormatter,
getMonthEndDay,
getTrueValue,
times,
padZero,
range,
isValidDate,
currentYear,
diff,
} from './wxs'

export default function Index(props: DatetimePickerProps) {
const {
value = null,
filter,
type = 'datetime',
showToolbar = true,
formatter = defaultFormatter,
minDate = new Date(currentYear - 10, 0, 1).getTime(),
maxDate = new Date(currentYear + 10, 11, 31).getTime(),
minHour = 0,
maxHour = 23,
minMinute = 0,
maxMinute = 59,
title,
itemHeight,
visibleItemCount,
confirmButtonText,
cancelButtonText,
onInput,
onChange,
onCancel,
onConfirm,
...others
} = props

const PickRef = useRef<any>(null)
const [innerValue, setInnerValue] = useState<any>(Date.now())
const [columns, setColumns] = useState([])
const minHour_ = minHour
const maxHour_ = maxHour
const minMinute_ = minMinute
const maxMinute_ = maxMinute

const getPicker = useCallback(function () {
if (PickRef.current) {
const { setColumnValues } = PickRef.current
PickRef.current.setColumnValues = (...args: any) =>
setColumnValues.apply(PickRef.current, [...args, false])
}
return PickRef.current
}, [])

const updateColumns = function () {
const results = getOriginColumns().map((column: any) => ({
values: column.values.map((value: any) => formatter(column.type, value)),
}))
if (!diff(results, columns)) {
return setColumns(results)
}
}

const updateColumnValue = useCallback(
function (value: any) {
let values: Array<any> = []
const picker = getPicker()
if (type === 'time') {
const pair: any = value.split(':')
values = [formatter('hour', pair[0]), formatter('minute', pair[1])]
} else {
const date = new Date(value)
values = [
formatter('year', `${date.getFullYear()}`),
formatter('month', padZero(date.getMonth() + 1)),
]
if (type === 'date') {
values.push(formatter('day', padZero(date.getDate())))
}
if (type === 'datetime') {
values.push(
formatter('day', padZero(date.getDate())),
formatter('hour', padZero(date.getHours())),
formatter('minute', padZero(date.getMinutes())),
)
}
}
setInnerValue(value)
updateColumns()

return new Promise((resolve) => {
setTimeout(() => {
picker.setValues(values)
resolve(value)
}, 16)
})
},
[formatter, getPicker, type, updateColumns],
)

const correctValue = function (value: any) {
// validate value
const isDateType = type !== 'time'
if (isDateType && !isValidDate(value)) {
value = minDate
} else if (!isDateType && !value) {
value = `${padZero(minHour)}:00`
}
// time type
if (!isDateType) {
let [hour, minute] = value.split(':')
hour = padZero(range(hour, minHour, maxHour))
minute = padZero(range(minute, minMinute, maxMinute))
return `${hour}:${minute}`
}
// date type
value = Math.max(value, minDate as number)
value = Math.min(value, maxDate as number)
return value
}

useEffect(
function () {
const val = correctValue(value)
const isEqual = val === innerValue
if (!isEqual) {
updateColumnValue(val).then(() => {
if (onInput) onInput(val)
})
}
},
[value, correctValue, innerValue],
)

const getOriginColumns = function () {
const results = getRanges().map(({ type, range }: any) => {
let values = times(range[1] - range[0] + 1, (index: number) => {
const value = range[0] + index
return type === 'year' ? `${value}` : padZero(value)
})
if (filter) {
values = filter(type, values)
}

return { type, values }
})
return results
}

const getRanges = function (): any {
const res = [
{
type: 'hour',
range: [minHour_, maxHour_],
},
{
type: 'minute',
range: [minMinute_, maxMinute_],
},
]
if (type === 'time') {
return res
}
const { maxYear, maxDate, maxMonth, maxHour, maxMinute } = getBoundary(
'max',
innerValue,
)
const { minYear, minDate, minMonth, minHour, minMinute } = getBoundary(
'min',
innerValue,
)
const result = [
{
type: 'year',
range: [minYear, maxYear],
},
{
type: 'month',
range: [minMonth, maxMonth],
},
{
type: 'day',
range: [minDate, maxDate],
},
{
type: 'hour',
range: [minHour, maxHour],
},
{
type: 'minute',
range: [minMinute, maxMinute],
},
]
if (type === 'date') result.splice(3, 2)
if (type === 'year-month') result.splice(2, 3)

return result
}

const getBoundary = useCallback(function (type, innerValue) {
const value = new Date(innerValue)
// const boundary = new Date(data[`${type}Date`])
const boundary = new Date()
let year = boundary.getFullYear() - 5
let month = 1
let date = 1
let hour = 0
let minute = 0
if (type === 'max') {
month = 12
date = getMonthEndDay(value.getFullYear(), value.getMonth() + 1)
hour = 23
minute = 59
year = year + 10
}
// if (value.getFullYear() === year) {
// month = boundary.getMonth() + 1
// if (value.getMonth() + 1 === month) {
// date = boundary.getDate()
// if (value.getDate() === date) {
// hour = boundary.getHours()
// if (value.getHours() === hour) {
// minute = boundary.getMinutes()
// }
// }
// }
// }
return {
[`${type}Year`]: year,
[`${type}Month`]: month,
[`${type}Date`]: date,
[`${type}Hour`]: hour,
[`${type}Minute`]: minute,
}
}, [])

const onChange_ = function () {
let value: any
const picker = getPicker()
const originColumns = getOriginColumns()
if (type === 'time') {
const indexes = picker.getIndexes()
value = `${+originColumns[0].values[indexes[0]]}:${+originColumns[1]
.values[indexes[1]]}`
} else {
const indexes = picker.getIndexes()
const values = indexes.map(
(value: number, index: number) => originColumns[index].values[value],
)
const year = getTrueValue(values[0])
const month = getTrueValue(values[1])
const maxDate = getMonthEndDay(year, month)
let date = getTrueValue(values[2])
if (type === 'year-month') {
date = 1
}
date = date > maxDate ? maxDate : date
let hour = 0
let minute = 0
if (type === 'datetime') {
hour = getTrueValue(values[3])
minute = getTrueValue(values[4])
}
value = new Date(year, month - 1, date, hour, minute)
}
value = correctValue(value)

updateColumnValue(value).then(() => {
if (onInput) onInput(value)
if (onChange) onChange(picker)
})
}

return (
<VanPicker
ref={PickRef}
className={`van-datetime-picker column-class ${others.className || ''}`}
style={utils.style([others.style])}
title={title}
columns={columns}
itemHeight={itemHeight}
showToolbar={showToolbar}
visibleItemCount={visibleItemCount}
confirmButtonText={confirmButtonText}
cancelButtonText={cancelButtonText}
onChange={onChange_}
onConfirm={useCallback(function () {
if (onConfirm) onConfirm(innerValue)
}, [])}
onCancel={onCancel}
></VanPicker>
)
}
Loading

0 comments on commit 732a251

Please sign in to comment.