| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329 | import React from 'react';import PropTypes from 'prop-types';import { format as dateFnsFormat } from 'date-fns';import { noop } from 'lodash';import BaseComponent, { BaseProps } from '../_base/baseComponent';import { strings } from '@douyinfe/semi-foundation/timePicker/constants';import ScrollList from '../scrollList/index';import ScrollItem from '../scrollList/scrollItem';import ComboboxFoundation, { formatOption } from '@douyinfe/semi-foundation/timePicker/ComboxFoundation';import LocaleConsumer from '../locale/localeConsumer';import { TimePickerProps } from './TimePicker';import { Locale } from '../locale/interface';export type ComboboxProps = Pick<TimePickerProps, 'format' | 'prefixCls' | 'disabledHours' |'disabledMinutes' |'disabledSeconds' |'hideDisabledOptions' |'use12Hours' |'scrollItemProps' |'panelFooter' |'panelHeader'> & BaseProps & {    defaultOpenValue?: TimePickerProps['value'];    showHour?: boolean;    showMinute?: boolean;    showSecond?: boolean;    onChange?: (value: { isAM: boolean; value: string; timeStampValue: number }) => void;    onCurrentSelectPanelChange?: (range: string) => void;    isAM?: boolean;    timeStampValue?: any};export interface ComboboxState {    showHour: boolean;    showMinute: boolean;    showSecond: boolean;    hourOptions: number[];    minuteOptions: number[];    secondOptions: number[]}export type FormatOptionReturn = ReturnType<typeof formatOption>;export interface AMPMOptionItem {    value: string;    text: string}class Combobox extends BaseComponent<ComboboxProps, ComboboxState> {    static propTypes = {        format: PropTypes.string,        defaultOpenValue: PropTypes.object,        prefixCls: PropTypes.string,        onChange: PropTypes.func,        showHour: PropTypes.bool,        showMinute: PropTypes.bool,        showSecond: PropTypes.bool,        disabledHours: PropTypes.func,        disabledMinutes: PropTypes.func,        disabledSeconds: PropTypes.func,        hideDisabledOptions: PropTypes.bool,        onCurrentSelectPanelChange: PropTypes.func,        use12Hours: PropTypes.bool,        isAM: PropTypes.bool,        timeStampValue: PropTypes.any,        scrollItemProps: PropTypes.object,    };    static defaultProps = {        disabledHours: noop,        disabledMinutes: noop,        disabledSeconds: noop,        format: strings.DEFAULT_FORMAT,    };    foundation: ComboboxFoundation;    constructor(props: ComboboxProps) {        super(props);        this.foundation = new ComboboxFoundation(this.adapter);        this.state = {            ...this.foundation.initData(),        };    }    componentDidUpdate(prevProps: ComboboxProps, prevState: ComboboxState) {        if (prevProps.timeStampValue !== this.props.timeStampValue || prevProps.format !== this.props.format) {            this.setState({                ...this.foundation.initData(),            });        }    }    componentWillUnmount() {        // this.foundation.destroy();    }    componentDidMount() {        // this.foundation.init();    }    cacheRefCurrent = (key: string, current: ScrollItem<FormatOptionReturn> | ScrollItem<AMPMOptionItem>) => {        if (key && typeof key === 'string') {            this.adapter.setCache(key, current);        }    };    reselect = () => {        const currentKeys = ['ampm', 'hour', 'minute', 'second'];        currentKeys.forEach(key => {            const current = this.adapter.getCache(key);            if (current && current.scrollToIndex) {                current.scrollToIndex();            }        });    };    onItemChange = ({ type, value, disabled }: { type?: string; value: string; disabled?: boolean }) => {        let { onChange, use12Hours, isAM, format, timeStampValue } = this.props;        const transformValue = this.foundation.getDisplayDateFromTimeStamp(timeStampValue);        // TODO: foundation        if (type === 'hour') {            if (use12Hours) {                if (isAM) {                    transformValue.setHours(Number(value) % 12);                } else {                    transformValue.setHours((Number(value) % 12) + 12);                }            } else {                transformValue.setHours(Number(value));            }        } else if (type === 'minute') {            transformValue.setMinutes(Number(value));        } else if (type === 'ampm') {            const ampm = value.toUpperCase();            if (use12Hours) {                if (ampm === 'PM') {                    isAM = false;                    transformValue.getHours() < 12 && transformValue.setHours((transformValue.getHours() % 12) + 12);                }                if (ampm === 'AM') {                    isAM = true;                    transformValue.getHours() >= 12 && transformValue.setHours(transformValue.getHours() - 12);                }            }        } else {            transformValue.setSeconds(Number(value));        }        onChange &&            onChange({                isAM,                value: dateFnsFormat(transformValue, format && format.replace(/(\s+)A/g, '$1a')), // dateFns only supports "h: mm: ss a"                timeStampValue: Number(transformValue),            });    };    onEnterSelectPanel = (range: string) => {        const { onCurrentSelectPanelChange } = this.props;        onCurrentSelectPanelChange(range);    };    renderHourSelect(hour: number, locale: Locale['TimePicker']) {        const { prefixCls, disabledHours, use12Hours, scrollItemProps } = this.props;        const { showHour, hourOptions } = this.state;        if (!showHour) {            return null;        }        const disabledOptions = disabledHours();        let hourOptionsAdj,            hourAdj;        if (use12Hours) {            hourOptionsAdj = [12].concat(hourOptions.filter(h => h < 12 && h > 0));            hourAdj = hour % 12 || 12;        } else {            hourOptionsAdj = hourOptions;            hourAdj = hour;        }        const transformHour = (value: string) => value + locale.hour;        const className = `${prefixCls}-list-hour`;        return (            <ScrollItem<FormatOptionReturn>                ref={current => this.cacheRefCurrent('hour', current)}                mode={'normal'}                transform={transformHour}                className={className}                list={hourOptionsAdj.map(option => formatOption(option, disabledOptions))}                selectedIndex={hourOptionsAdj.indexOf(hourAdj)}                type="hour"                onSelect={this.onItemChange}                {...scrollItemProps}            />        );    }    renderMinuteSelect(minute: number, locale: Locale['TimePicker']) {        const { prefixCls, disabledMinutes, timeStampValue, scrollItemProps } = this.props;        const { showMinute, minuteOptions } = this.state;        if (!showMinute) {            return null;        }        const value = new Date(timeStampValue);        const disabledOptions = disabledMinutes && disabledMinutes(value.getHours());        const className = `${prefixCls}-list-minute`;        const transformMinute = (min: string) => min + locale.minute;        return (            <ScrollItem<FormatOptionReturn>                ref={current => this.cacheRefCurrent('minute', current)}                mode={'normal'}                transform={transformMinute}                list={minuteOptions.map(option => formatOption(option, disabledOptions))}                selectedIndex={minuteOptions.indexOf(minute)}                type="minute"                onSelect={this.onItemChange}                className={className}                {...scrollItemProps}            />        );    }    renderSecondSelect(second: number, locale: Locale['TimePicker']) {        const { prefixCls, disabledSeconds, timeStampValue, scrollItemProps } = this.props;        const { showSecond, secondOptions } = this.state;        if (!showSecond) {            return null;        }        const value = new Date(timeStampValue);        const disabledOptions = disabledSeconds && disabledSeconds(value.getHours(), value.getMinutes());        const className = `${prefixCls}-list-second`;        const transformSecond = (sec: number) => String(sec) + locale.second;        return (            <ScrollItem<FormatOptionReturn>                ref={current => this.cacheRefCurrent('second', current)}                mode={'normal'}                transform={transformSecond}                list={secondOptions.map(option => formatOption(option, disabledOptions))}                selectedIndex={secondOptions.indexOf(second)}                className={className}                type="second"                onSelect={this.onItemChange}                {...scrollItemProps}            />        );    }    renderAMPMSelect(locale: Locale['TimePicker'], localeCode: string) {        const { prefixCls, use12Hours, isAM, scrollItemProps } = this.props;        if (!use12Hours) {            return null;        }        const AMPMOptions: AMPMOptionItem[] = [            {                value: 'AM',                text: locale.AM || '上午',            },            {                value: 'PM',                text: locale.PM || '下午',            },        ];        const selected = isAM ? 0 : 1;        const className = `${prefixCls}-list-ampm`;        return (            <ScrollItem<AMPMOptionItem>                ref={current => this.cacheRefCurrent('ampm', current)}                mode={'normal'}                className={className}                list={AMPMOptions}                selectedIndex={selected}                type="ampm"                onSelect={this.onItemChange}                {...scrollItemProps}            />        );    }    getDisplayDateFromTimeStamp = (timeStampValue: Date | string) => this.foundation.getDisplayDateFromTimeStamp(timeStampValue);    render() {        const { timeStampValue, panelHeader, panelFooter } = this.props;        const value = this.getDisplayDateFromTimeStamp(timeStampValue);        return (            <LocaleConsumer componentName="TimePicker">                {(locale: Locale['TimePicker'], localeCode: Locale['code']) => (                    <ScrollList                        header={panelHeader}                        footer={panelFooter}                        x-semi-header-alias="panelHeader"                        x-semi-footer-alias="panelFooter"                    >                        {this.renderAMPMSelect(locale, localeCode)}                        {this.renderHourSelect(value.getHours(), locale)}                        {this.renderMinuteSelect(value.getMinutes(), locale)}                        {this.renderSecondSelect(value.getSeconds(), locale)}                    </ScrollList>                )}            </LocaleConsumer>        );    }}export default Combobox;
 |