| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521 | 
							- import React from 'react';
 
- import PropTypes from 'prop-types';
 
- import classNames from 'classnames';
 
- import { noop, get } from 'lodash';
 
- import ConfigContext from '../configProvider/context';
 
- import BaseComponent, { ValidateStatus } from '../_base/baseComponent';
 
- import { strings, cssClasses } from '@douyinfe/semi-foundation/timePicker/constants';
 
- import Popover, { PopoverProps } from '../popover';
 
- import { numbers as popoverNumbers } from '@douyinfe/semi-foundation/popover/constants';
 
- import TimePickerFoundation, {
 
-     TimePickerAdapter,
 
-     TimePickerFoundationProps,
 
-     TimePickerFoundationState,
 
- } from '@douyinfe/semi-foundation/timePicker/foundation';
 
- import isNullOrUndefined from '@douyinfe/semi-foundation/utils/isNullOrUndefined';
 
- import Combobox from './Combobox';
 
- import TimeInput from './TimeInput';
 
- import { PanelShape, PanelShapeDefaults } from './PanelShape';
 
- import { TimeShape } from './TimeShape';
 
- import '@douyinfe/semi-foundation/timePicker/timePicker.scss';
 
- import Trigger from '../trigger';
 
- import { InputSize } from '../input';
 
- import { Position } from '../tooltip';
 
- import { ScrollItemProps } from '../scrollList/scrollItem';
 
- import { Locale } from '../locale/interface';
 
- export interface Panel {
 
-     panelHeader?: React.ReactNode | React.ReactNode[];
 
-     panelFooter?: React.ReactNode | React.ReactNode[]
 
- }
 
- export type TimePickerProps = {
 
-     'aria-describedby'?: React.AriaAttributes['aria-describedby'];
 
-     'aria-errormessage'?: React.AriaAttributes['aria-errormessage'];
 
-     'aria-invalid'?: React.AriaAttributes['aria-invalid'];
 
-     'aria-labelledby'?: React.AriaAttributes['aria-labelledby'];
 
-     'aria-required'?: React.AriaAttributes['aria-required'];
 
-     clearIcon?: React.ReactNode;
 
-     dropdownMargin?: PopoverProps['margin'];
 
-     inputStyle?: React.CSSProperties;
 
-     insetLabel?: React.ReactNode;
 
-     locale?: Locale['TimePicker'];
 
-     popupStyle?: React.CSSProperties;
 
-     position?: Position;
 
-     scrollItemProps?: ScrollItemProps<any>;
 
-     size?: InputSize;
 
-     style?: React.CSSProperties;
 
-     triggerRender?: (props?: any) => React.ReactNode;
 
-     validateStatus?: ValidateStatus;
 
-     onBlur?: React.FocusEventHandler<HTMLInputElement>;
 
-     onChange?: TimePickerAdapter['notifyChange'];
 
-     onChangeWithDateFirst?: boolean;
 
-     onFocus?: React.FocusEventHandler<HTMLInputElement>;
 
-     onOpenChange?: (open: boolean) => void
 
- } & TimePickerFoundationProps;
 
- export interface TimePickerState extends TimePickerFoundationState { }
 
- export default class TimePicker extends BaseComponent<TimePickerProps, TimePickerState> {
 
-     static contextType = ConfigContext;
 
-     static propTypes = {
 
-         'aria-labelledby': PropTypes.string,
 
-         'aria-invalid': PropTypes.bool,
 
-         'aria-errormessage': PropTypes.string,
 
-         'aria-describedby': PropTypes.string,
 
-         'aria-required': PropTypes.bool,
 
-         prefixCls: PropTypes.string,
 
-         borderless: PropTypes.bool,
 
-         clearText: PropTypes.string,
 
-         clearIcon: PropTypes.node,
 
-         value: TimeShape,
 
-         inputReadOnly: PropTypes.bool,
 
-         disabled: PropTypes.bool,
 
-         showClear: PropTypes.bool,
 
-         defaultValue: TimeShape,
 
-         open: PropTypes.bool,
 
-         defaultOpen: PropTypes.bool,
 
-         onOpenChange: PropTypes.func,
 
-         position: PropTypes.any,
 
-         getPopupContainer: PropTypes.func,
 
-         placeholder: PropTypes.string,
 
-         format: PropTypes.string,
 
-         style: PropTypes.object,
 
-         className: PropTypes.string,
 
-         popupClassName: PropTypes.string,
 
-         popupStyle: PropTypes.object,
 
-         disabledHours: PropTypes.func,
 
-         disabledMinutes: PropTypes.func,
 
-         disabledSeconds: PropTypes.func,
 
-         dropdownMargin: PropTypes.oneOfType([PropTypes.number, PropTypes.object]),
 
-         hideDisabledOptions: PropTypes.bool,
 
-         onChange: PropTypes.func,
 
-         use12Hours: PropTypes.bool,
 
-         hourStep: PropTypes.number,
 
-         minuteStep: PropTypes.number,
 
-         secondStep: PropTypes.number,
 
-         focusOnOpen: PropTypes.bool,
 
-         autoFocus: PropTypes.bool,
 
-         size: PropTypes.oneOf(strings.SIZE),
 
-         stopPropagation: PropTypes.bool,
 
-         panels: PropTypes.arrayOf(PropTypes.shape(PanelShape)),
 
-         onFocus: PropTypes.func,
 
-         onBlur: PropTypes.func,
 
-         locale: PropTypes.object,
 
-         localeCode: PropTypes.string,
 
-         dateFnsLocale: PropTypes.object,
 
-         zIndex: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
 
-         insetLabel: PropTypes.node,
 
-         insetLabelId: PropTypes.string,
 
-         validateStatus: PropTypes.oneOf(strings.STATUS),
 
-         type: PropTypes.oneOf<TimePickerProps['type']>(strings.TYPES),
 
-         rangeSeparator: PropTypes.string,
 
-         triggerRender: PropTypes.func,
 
-         timeZone: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
 
-         scrollItemProps: PropTypes.object,
 
-         motion: PropTypes.oneOfType([PropTypes.bool, PropTypes.func, PropTypes.object]),
 
-         autoAdjustOverflow: PropTypes.bool,
 
-         ...PanelShape,
 
-         inputStyle: PropTypes.object,
 
-         preventScroll: PropTypes.bool,
 
-     };
 
-     static defaultProps = {
 
-         autoAdjustOverflow: true,
 
-         borderless: false,
 
-         getPopupContainer: () => document.body,
 
-         showClear: true,
 
-         zIndex: popoverNumbers.DEFAULT_Z_INDEX,
 
-         rangeSeparator: strings.DEFAULT_RANGE_SEPARATOR,
 
-         onOpenChange: noop,
 
-         clearText: 'clear',
 
-         prefixCls: cssClasses.PREFIX,
 
-         inputReadOnly: false,
 
-         style: {},
 
-         stopPropagation: true,
 
-         className: '',
 
-         popupClassName: '',
 
-         popupStyle: { left: '0px', top: '0px' },
 
-         disabledHours: () => [] as number[],
 
-         disabledMinutes: () => [] as number[],
 
-         disabledSeconds: () => [] as number[],
 
-         hideDisabledOptions: false,
 
-         // position: 'bottomLeft',
 
-         onFocus: noop,
 
-         onBlur: noop,
 
-         onChange: noop,
 
-         onChangeWithDateFirst: true,
 
-         use12Hours: false,
 
-         focusOnOpen: false,
 
-         onKeyDown: noop,
 
-         size: 'default' as const,
 
-         type: strings.DEFAULT_TYPE,
 
-         motion: true,
 
-         ...PanelShapeDefaults,
 
-         // format: strings.DEFAULT_FORMAT,
 
-         // open and value controlled
 
-     };
 
-     foundation: TimePickerFoundation;
 
-     timePickerRef: React.MutableRefObject<HTMLDivElement>;
 
-     savePanelRef: React.RefObject<HTMLDivElement>;
 
-     useCustomTrigger: boolean;
 
-     clickOutSideHandler: (e: MouseEvent) => void;
 
-     constructor(props: TimePickerProps) {
 
-         super(props);
 
-         const { format = strings.DEFAULT_FORMAT } = props;
 
-         this.state = {
 
-             open: props.open || props.defaultOpen || false,
 
-             value: [], // Date[]
 
-             inputValue: '', // time string
 
-             currentSelectPanel: 0,
 
-             isAM: [true, false],
 
-             showHour: Boolean(format.match(/HH|hh|H|h/g)),
 
-             showMinute: Boolean(format.match(/mm/g)),
 
-             showSecond: Boolean(format.match(/ss/g)),
 
-             invalid: undefined,
 
-         };
 
-         this.foundation = new TimePickerFoundation(this.adapter);
 
-         this.timePickerRef = React.createRef();
 
-         this.savePanelRef = React.createRef();
 
-         this.useCustomTrigger = typeof this.props.triggerRender === 'function';
 
-     }
 
-     get adapter(): TimePickerAdapter<TimePickerProps, TimePickerState> {
 
-         return {
 
-             ...super.adapter,
 
-             togglePanel: show => {
 
-                 this.setState({ open: show });
 
-             },
 
-             registerClickOutSide: () => {
 
-                 if (this.clickOutSideHandler) {
 
-                     this.adapter.unregisterClickOutSide();
 
-                 }
 
-                 this.clickOutSideHandler = e => {
 
-                     const panel = this.savePanelRef && this.savePanelRef.current;
 
-                     const trigger = this.timePickerRef && this.timePickerRef.current;
 
-                     const target = e.target as Element;
 
-                     const path = (e.composedPath && e.composedPath()) || [target];
 
-                     if (
 
-                         !(panel && panel.contains(target)) &&
 
-                         !(trigger && trigger.contains(target)) &&
 
-                         !(path.includes(trigger) || path.includes(panel))
 
-                     ) {
 
-                         this.foundation.handlePanelClose(true, e);
 
-                     }
 
-                 };
 
-                 document.addEventListener('mousedown', this.clickOutSideHandler);
 
-             },
 
-             setInputValue: (inputValue, cb) => this.setState({ inputValue }, cb),
 
-             unregisterClickOutSide: () => {
 
-                 if (this.clickOutSideHandler) {
 
-                     document.removeEventListener('mousedown', this.clickOutSideHandler);
 
-                     this.clickOutSideHandler = null;
 
-                 }
 
-             },
 
-             notifyOpenChange: (...args) => this.props.onOpenChange(...args),
 
-             notifyChange: (agr1, arg2) => this.props.onChange && this.props.onChange(agr1, arg2),
 
-             notifyFocus: (...args) => this.props.onFocus && this.props.onFocus(...args),
 
-             notifyBlur: (...args) => this.props.onBlur && this.props.onBlur(...args),
 
-             isRangePicker: () => this.props.type === strings.TYPE_TIME_RANGE_PICKER,
 
-         };
 
-     }
 
-     static getDerivedStateFromProps(nextProps: TimePickerProps, prevState: TimePickerState) {
 
-         if ('open' in nextProps && nextProps.open !== prevState.open) {
 
-             return {
 
-                 open: nextProps.open,
 
-             };
 
-         }
 
-         return null;
 
-     }
 
-     componentDidUpdate(prevProps: TimePickerProps) {
 
-         // if (this.isControlled('open') && this.props.open != null && this.props.open !== prevProps.open) {
 
-         //     this.foundation.setPanel(this.props.open);
 
-         // }
 
-         if (this.isControlled('value') && this.props.value !== prevProps.value) {
 
-             this.foundation.refreshProps({
 
-                 ...this.props,
 
-             });
 
-         } else if (this.props.timeZone !== prevProps.timeZone) {
 
-             this.foundation.refreshProps({
 
-                 timeZone: this.props.timeZone,
 
-                 __prevTimeZone: prevProps.timeZone,
 
-                 value: this.state.value,
 
-             });
 
-         }
 
-     }
 
-     onCurrentSelectPanelChange = (currentSelectPanel: string) => {
 
-         this.setState({ currentSelectPanel });
 
-     };
 
-     handlePanelChange = (value: { isAM: boolean; value: string; timeStampValue: number }, index: number) =>
 
-         this.foundation.handlePanelChange(value, index);
 
-     handleInput = (value: string) => this.foundation.handleInputChange(value);
 
-     createPanelProps = (index = 0) => {
 
-         const { panels, panelFooter, panelHeader, locale } = this.props;
 
-         const panelProps = {
 
-             panelHeader,
 
-             panelFooter,
 
-         };
 
-         if (this.adapter.isRangePicker()) {
 
-             const defaultHeaderMap = {
 
-                 0: locale.begin,
 
-                 1: locale.end,
 
-             };
 
-             panelProps.panelHeader = get(
 
-                 panels,
 
-                 index,
 
-                 isNullOrUndefined(panelHeader)
 
-                     ? get(defaultHeaderMap, index, null)
 
-                     : Array.isArray(panelHeader)
 
-                         ? panelHeader[index]
 
-                         : panelHeader
 
-             );
 
-             panelProps.panelFooter = get(
 
-                 panels,
 
-                 index,
 
-                 Array.isArray(panelFooter) ? panelFooter[index] : panelFooter
 
-             ) as React.ReactNode;
 
-         }
 
-         return panelProps;
 
-     };
 
-     getPanelElement() {
 
-         const { prefixCls, type } = this.props;
 
-         const { isAM, value } = this.state;
 
-         const format = this.foundation.getDefaultFormatIfNeed();
 
-         const timePanels = [
 
-             <Combobox
 
-                 {...this.props}
 
-                 key={0}
 
-                 format={format}
 
-                 isAM={isAM[0]}
 
-                 timeStampValue={value[0]}
 
-                 prefixCls={`${prefixCls}-panel`}
 
-                 onChange={v => this.handlePanelChange(v, 0)}
 
-                 onCurrentSelectPanelChange={this.onCurrentSelectPanelChange}
 
-                 {...this.createPanelProps(0)}
 
-             />,
 
-         ];
 
-         if (type === strings.TYPE_TIME_RANGE_PICKER) {
 
-             timePanels.push(
 
-                 <Combobox
 
-                     {...this.props}
 
-                     key={1}
 
-                     format={format}
 
-                     isAM={isAM[1]}
 
-                     timeStampValue={value[1]}
 
-                     prefixCls={`${prefixCls}-panel`}
 
-                     onChange={v => this.handlePanelChange(v, 1)}
 
-                     onCurrentSelectPanelChange={this.onCurrentSelectPanelChange}
 
-                     {...this.createPanelProps(1)}
 
-                 />
 
-             );
 
-         }
 
-         const wrapCls = classNames({
 
-             [cssClasses.RANGE_PANEL_LISTS]: this.adapter.isRangePicker(),
 
-         });
 
-         return (
 
-             <div ref={this.savePanelRef} className={wrapCls}>
 
-                 {timePanels.map(panel => panel)}
 
-             </div>
 
-         );
 
-     }
 
-     getPopupClassName() {
 
-         const { use12Hours, prefixCls, popupClassName } = this.props;
 
-         const { showHour, showMinute, showSecond } = this.state;
 
-         let selectColumnCount = 0;
 
-         if (showHour) {
 
-             selectColumnCount += 1;
 
-         }
 
-         if (showMinute) {
 
-             selectColumnCount += 1;
 
-         }
 
-         if (showSecond) {
 
-             selectColumnCount += 1;
 
-         }
 
-         if (use12Hours) {
 
-             selectColumnCount += 1;
 
-         }
 
-         return classNames(
 
-             `${prefixCls}-panel`,
 
-             popupClassName,
 
-             {
 
-                 [`${prefixCls}-panel-narrow`]: (!showHour || !showMinute || !showSecond) && !use12Hours,
 
-                 [cssClasses.RANGE_PICKER]: this.adapter.isRangePicker(),
 
-             },
 
-             `${prefixCls}-panel-column-${selectColumnCount}`
 
-         );
 
-     }
 
-     focus() {
 
-         // TODO this.picker is undefined, confirm keep this func or not
 
-         // this.picker.focus();
 
-     }
 
-     blur() {
 
-         // TODO this.picker is undefined, confirm keep this func or not
 
-         // this.picker.blur();
 
-     }
 
-     /* istanbul ignore next */
 
-     handlePanelVisibleChange = (visible: boolean) => this.foundation.handleVisibleChange(visible);
 
-     openPanel = () => {
 
-         this.foundation.handlePanelOpen();
 
-     };
 
-     handleFocus = (e: React.FocusEvent<HTMLInputElement>) => {
 
-         this.foundation.handleFocus(e);
 
-     };
 
-     handleBlur = (e: React.FocusEvent<HTMLInputElement>) => this.foundation.handleInputBlur(e);
 
-     setTimePickerRef: React.LegacyRef<HTMLDivElement> = node => (this.timePickerRef.current = node);
 
-     render() {
 
-         const {
 
-             prefixCls,
 
-             placeholder,
 
-             disabled,
 
-             defaultValue,
 
-             dropdownMargin,
 
-             className,
 
-             popupStyle,
 
-             size,
 
-             style,
 
-             locale,
 
-             localeCode,
 
-             zIndex,
 
-             getPopupContainer,
 
-             insetLabel,
 
-             insetLabelId,
 
-             inputStyle,
 
-             showClear,
 
-             panelHeader,
 
-             panelFooter,
 
-             rangeSeparator,
 
-             onOpenChange,
 
-             onChangeWithDateFirst,
 
-             popupClassName: propPopupClassName,
 
-             hideDisabledOptions,
 
-             use12Hours,
 
-             minuteStep,
 
-             hourStep,
 
-             secondStep,
 
-             scrollItemProps,
 
-             triggerRender,
 
-             motion,
 
-             autoAdjustOverflow,
 
-             stopPropagation,
 
-             ...rest
 
-         } = this.props;
 
-         const format = this.foundation.getDefaultFormatIfNeed();
 
-         const position = this.foundation.getPosition();
 
-         const { open, inputValue, invalid, value } = this.state;
 
-         const popupClassName = this.getPopupClassName();
 
-         const headerPrefix = classNames({
 
-             [`${prefixCls}-header`]: true,
 
-         });
 
-         const panelPrefix = classNames({
 
-             [`${prefixCls}-panel`]: true,
 
-             [`${prefixCls}-panel-${size}`]: size,
 
-         });
 
-         const inputProps = {
 
-             ...rest,
 
-             disabled,
 
-             prefixCls,
 
-             size,
 
-             showClear: disabled ? false : showClear,
 
-             style: inputStyle,
 
-             value: inputValue,
 
-             onFocus: this.handleFocus,
 
-             insetLabel,
 
-             insetLabelId,
 
-             format,
 
-             locale,
 
-             localeCode,
 
-             invalid,
 
-             placeholder,
 
-             onChange: this.handleInput,
 
-             onBlur: this.handleBlur,
 
-         };
 
-         const outerProps = {} as { onClick: () => void };
 
-         if (this.useCustomTrigger) {
 
-             outerProps.onClick = this.openPanel;
 
-         }
 
-         return (
 
-             <div
 
-                 ref={this.setTimePickerRef}
 
-                 className={classNames({ [prefixCls]: true }, className)}
 
-                 style={style}
 
-                 {...outerProps}
 
-             >
 
-                 <Popover
 
-                     getPopupContainer={getPopupContainer}
 
-                     zIndex={zIndex as number}
 
-                     prefixCls={panelPrefix}
 
-                     contentClassName={popupClassName}
 
-                     style={popupStyle}
 
-                     content={this.getPanelElement()}
 
-                     trigger={'custom'}
 
-                     position={position}
 
-                     visible={disabled ? false : Boolean(open)}
 
-                     motion={motion}
 
-                     margin={dropdownMargin}
 
-                     autoAdjustOverflow={autoAdjustOverflow}
 
-                     stopPropagation={stopPropagation}
 
-                 >
 
-                     {this.useCustomTrigger ? (
 
-                         <Trigger
 
-                             triggerRender={triggerRender}
 
-                             disabled={disabled}
 
-                             value={value}
 
-                             inputValue={inputValue}
 
-                             onChange={this.handleInput}
 
-                             placeholder={placeholder}
 
-                             componentName={'TimePicker'}
 
-                             componentProps={{ ...this.props }}
 
-                         />
 
-                     ) : (
 
-                         <span className={headerPrefix}>
 
-                             <TimeInput {...inputProps} />
 
-                         </span>
 
-                     )}
 
-                 </Popover>
 
-             </div>
 
-         );
 
-     }
 
- }
 
 
  |