import React, { HTMLAttributes, InputHTMLAttributes, ReactNode } from 'react'
import moment, { Moment } from 'moment'
import RCPicker, { RangePicker as RCRangePicker } from 'rc-picker'
import { RangeValue } from 'rc-picker/lib/interface'
import classnames from 'classnames'
import Button, { ButtonProps } from '../Button'
import { CloseCircleFilledIcon, ClockCircleIcon, CalendarIcon, SwapRightIcon } from '../icons'
import { useLangData, usePrefix, useUtcOffset } from '../ConfigProvider'
import { useGetPopupContainer } from '../PopupContainerProvider'
import { concat } from '../utils'
import { CommonProps, useInputColorStyle, useInputSize } from '../Input/input'
import { useFocused, useValue } from '../utils/hooks'
import { getGenerateConfigWithUtcOffset } from './generate'
function PickerButton(props: ButtonProps) {
return
}
export type DateType = 'date' | 'time' | 'datetime' | 'year-month' | 'year-day' | 'hour-second' | 'hour-minute' | 'year-second' | 'minute-second' | 'year-minute'
interface PickerProps extends Omit, 'placeholder'> {
/**
* 日期选择器的类型
* @default date
*/
type?: DateType
/**
* UTC 偏移量。
*
* 设置后使用 utcOffset 对应的时区,若不设置则默认使用全局配置。
*
* utcOffset 可设置范围: (-16, 16) ,单位为小时
*/
utcOffset?: number
/**
* 日期选择器开启、关闭时的回调
*/
onOpenChange?: (open: boolean) => void
/**
* 不可选择的日期
*/
disableWhen?: (currentDate: number) => boolean
/** 是否有清空功能 */
clearable?: boolean
/**
* 自定义日期单元格的内容
*/
dateRender?: (currentDate: number) => ReactNode
/**
* 自定义月份单元格的内容
*/
monthCellRender?: (currentDate: number) => ReactNode
/**
* 在面板中添加额外的页脚
*/
extraFooterRender?: () => ReactNode
/**
* 根元素的 html 属性
*/
rootHtmlProps?: Omit
}
export type DatePickerProps = PickerProps>
export type RangeDatePickerValue = { start: number | null, end: number | null }
export type RangeDatePickerProps =
PickerProps>
export function RangeDatePicker(props: RangeDatePickerProps) {
const { value: valueFromProps, defaultValue = { start: null, end: null }, onChange, rootHtmlProps } = props
const { value, setValue } = useValue({ valueFromProps, defaultValue, onChange })
const sharedProps = useSharedProps(props)
const { rangeDateText, rangeTimeText } = useLangData('datePicker')
const placeholder: [string, string] = sharedProps.picker === 'time' ? rangeTimeText : rangeDateText
const handleChange = (values: RangeValue, _formatString: [string, string]) => {
setValue({ start: values?.[0] ? values[0].valueOf() : null, end: values?.[1] ? values[1].valueOf() : null })
}
return (
{...rootHtmlProps}
{...sharedProps}
value={[value?.start ? moment(value.start) : null, value?.end ? moment(value.end) : null]}
placeholder={placeholder}
separator={}
onChange={handleChange}
/>
)
}
export function DatePicker(props: DatePickerProps) {
const { value: valueFromProps, defaultValue = null, onChange, rootHtmlProps } = props
const { value, setValue } = useValue({ valueFromProps, defaultValue, onChange })
const sharedProps = useSharedProps(props)
const { dateText, timeText } = useLangData('datePicker')
const placeholder = sharedProps.picker === 'time' ? timeText : dateText
const handleChange = (val: Moment | null, _dateString: string) => {
setValue(val && val.valueOf())
}
return (
{...rootHtmlProps}
{...sharedProps}
value={value ? moment(value) : null}
placeholder={placeholder}
onChange={handleChange}
/>
)
}
/** 不同类型 DatePicker 共享的 props */
function useSharedProps({
type = 'date',
utcOffset: utcOffsetFromProps,
disableWhen,
disabled = false,
size,
validateStatus,
className: cls,
style,
clearable = false,
onOpenChange,
dateRender,
monthCellRender,
extraFooterRender
}: T) {
const prefix = usePrefix('picker')
const { focused } = useFocused({}, disabled)
const colorStyle = useInputColorStyle({
focused,
validateStatus,
disabled
})
const inputSize = useInputSize(size)
const className = classnames(colorStyle, prefix, cls, concat(prefix, inputSize))
const lang = useLangData('datePicker')
const globalUtcOffset = useUtcOffset()
const utcOffset = utcOffsetFromProps ?? globalUtcOffset
const { picker, showTime, format } = parseDateType(type)
const getPopupContainer = useGetPopupContainer()
return {
picker,
format,
showTime,
prefixCls: prefix,
className,
style,
disabled,
size,
components: { button: PickerButton },
prevIcon: ,
nextIcon: ,
superPrevIcon: ,
superNextIcon: ,
clearIcon: ,
suffixIcon: picker === 'time' ? : ,
generateConfig: getGenerateConfigWithUtcOffset(utcOffset),
locale: lang,
allowClear: clearable,
disabledDate: disableWhen ? (date: Moment) => disableWhen(date.valueOf()) : undefined,
dropdownClassName: concat(prefix, 'dropdown'),
onOpenChange,
getPopupContainer,
dateRender: dateRender ? (v: Moment) => dateRender(v.valueOf()) : undefined,
monthCellRender: monthCellRender ? (v: Moment) => monthCellRender(v.valueOf()) : undefined,
renderExtraFooter: extraFooterRender
}
}
type ShowTime = {
format: string
showSecond?: boolean
showMinute?: boolean
showHour?: boolean
}
// type的格式为startFormat-endFormat,date、time和datetime是其中三种常用格式的缩写
const typeMap: Partial> = {
date: 'year-day',
time: 'hour-second',
datetime: 'year-second'
}
// 以month-day举例,得到的是format.substring(5, 10),即为format.substring(startPosition[1], endPosition[2] + 1)
const dateSet = ['year', 'month', 'day', 'hour', 'minute', 'second']
const startPosition = [0, 5, 8, 11, 14, 17]
const endPosition = [3, 6, 9, 12, 15, 18]
const format = 'YYYY-MM-DD HH:mm:ss'
function parseDateType(type: DateType): { picker: 'time' | 'date' | 'month', showTime?: ShowTime, format: string } {
const dateType = typeMap[type] || type
const [startType, endType] = dateType.split('-')
let picker: 'time' | 'date' | 'month' = 'date'
let showTime: ShowTime | undefined
const startIndex = dateSet.findIndex(_type => _type === startType)
const endIndex = dateSet.findIndex(_type => _type === endType)
if (startIndex === -1 || endIndex === -1) {
throw Error('you should use correct type to date picker')
}
if (startIndex > 2) {
picker = 'time'
}
if (type === 'year-month') {
picker = 'month'
}
if (endIndex > 2) {
showTime = { format: format.substring(startPosition[Math.max(3, startIndex)], endPosition[endIndex] + 1) }
if (!showTime.format.includes('s')) {
showTime.showSecond = false
}
if (!showTime.format.includes('m')) {
showTime.showMinute = false
}
if (!showTime.format.includes('H')) {
showTime.showHour = false
}
}
return { picker, showTime, format: format.substring(startPosition[startIndex], endPosition[endIndex] + 1) }
}