import range from 'lodash/range'; import isArray from 'lodash/isArray'; import concat from 'lodash/concat'; import uniq from 'lodash/uniq'; import sortBy from 'lodash/sortBy'; import * as React from 'react'; import MinuteView from '../../views/MinuteView'; import { BasePickerOnChangeData, BasePickerProps, DisableValuesProps, MinMaxValueProps, OptionalHeaderProps, ProvideHeadingValue, SingleSelectionPicker, TimeFormat, TimePickerProps, } from '../BasePicker'; import { buildTimeStringWithSuffix, getCurrentDate, isNextPageAvailable, isPrevPageAvailable, } from './sharedFunctions'; import moment from 'moment'; const MINUTES_STEP = 5; const MINUTES_ON_PAGE = 12; const PAGE_WIDTH = 3; type MinutePickerProps = BasePickerProps & MinMaxValueProps & DisableValuesProps & TimePickerProps & OptionalHeaderProps & { enable?: moment.Moment[], hour?: number; }; export interface MinutePickerOnChangeData extends BasePickerOnChangeData { value: { year: number, month: number, date: number, hour: number, minute: number, }; } class MinutePicker extends SingleSelectionPicker implements ProvideHeadingValue { public static readonly defaultProps: { timeFormat: TimeFormat } = { timeFormat: '24', }; constructor(props) { super(props); this.PAGE_WIDTH = PAGE_WIDTH; } public render() { const { onChange, value, hour, initializeWith, closePopup, inline, isPickerInFocus, isTriggerInFocus, onCalendarViewMount, minDate, maxDate, disable, timeFormat, localization, ...rest } = this.props; return ( ); } public getCurrentDate(): string { /* Return currently selected month, date and year(string) to display in calendar header. */ return getCurrentDate(this.state.date); } protected buildCalendarValues(): string[] { const houresInDay = this.props.enable.filter(date => date.isSame(this.state.date, 'day')); const same = houresInDay.filter(h => h.hour() == this.state.date.hour()); return Array.from(new Set(same.map(h => h.format('HH:mm')))); } // protected buildCalendarValues(): string[] { // /* // Return array of minutes (strings) like ['16:15', '16:20', ...] // that used to populate calendar's page. // */ // const hour = this.state.date.hour() < 10 // ? '0' + this.state.date.hour().toString() // : this.state.date.hour().toString(); // return range(0, 60, MINUTES_STEP) // .map((minute) => `${minute < 10 ? '0' : ''}${minute}`) // .map((minute) => buildTimeStringWithSuffix(hour, minute, this.props.timeFormat)); // } protected getSelectableCellPositions(): number[] { const disabled = this.getDisabledPositions(); const all = range(0, MINUTES_ON_PAGE); if (disabled) { return all.filter((pos) => { return disabled.indexOf(pos) < 0; }); } return all; } protected getInitialDatePosition(): number { const selectable = this.getSelectableCellPositions(); if (selectable.indexOf(getMinuteCellPosition(this.state.date.minute())) < 0) { return selectable[0]; } return getMinuteCellPosition(this.state.date.minute()); } protected getDisabledPositions(): number[] { const { disable, minDate, maxDate, } = this.props; let disabledByDisable = []; let disabledByMaxDate = []; let disabledByMinDate = []; if (isArray(disable)) { disabledByDisable = concat( disabledByDisable, disable.filter((date) => date.isSame(this.state.date, 'day')) .map((date) => getMinuteCellPosition(date.minute()))); } if (minDate) { if (minDate.isSame(this.state.date, 'hour')) { disabledByMinDate = concat( disabledByMinDate, range(0 , minDate.minute()).map((m) => getMinuteCellPosition(m))); } } if (maxDate) { if (maxDate.isSame(this.state.date, 'hour')) { disabledByMaxDate = concat( disabledByMaxDate, range(maxDate.minute() + MINUTES_STEP, 60).map((m) => getMinuteCellPosition(m))); } } const result = sortBy( uniq( concat(disabledByDisable, disabledByMaxDate, disabledByMinDate))); if (result.length > 0) { return result; } } // protected getActiveCellPosition(): number { // /* // Return position of a minute that should be displayed as active // (position in array returned by `this.buildCalendarValues`). // */ // const { value } = this.props; // if (value && value.isSame(this.state.date, 'date')) { // return Math.floor(this.props.value.minutes() / MINUTES_STEP); // } // } protected getActiveCellPosition(): number { /* Return position of a minute that should be displayed as active (position in array returned by `this.buildCalendarValues`). */ const { value } = this.props; if (value && value.isSame(this.state.date, 'date')) { return this.buildCalendarValues().findIndex((item, i) => this.props.value.minute() === Number(item.split(':')[1])); } } protected isNextPageAvailable(): boolean { return isNextPageAvailable(this.state.date, this.props.maxDate); } protected isPrevPageAvailable(): boolean { return isPrevPageAvailable(this.state.date, this.props.minDate); } protected handleChange = (e: React.SyntheticEvent, { value }): void => { const data: MinutePickerOnChangeData = { ...this.props, value: { year: this.state.date.year(), month: this.state.date.month(), date: this.state.date.date(), hour: this.state.date.hour(), minute: Number((value as string).split(':')[1]), }, }; this.props.onChange(e, data); } protected switchToNextPage = (e: React.SyntheticEvent, data: any, callback: () => void): void => { this.setState(({ date }) => { const nextDate = date.clone(); nextDate.add(1, 'day'); return { date: nextDate }; }, callback); } protected switchToPrevPage = (e: React.SyntheticEvent, data: any, callback: () => void): void => { this.setState(({ date }) => { const prevDate = date.clone(); prevDate.subtract(1, 'day'); return { date: prevDate }; }, callback); } } function getMinuteCellPosition(minute: number): number { return Math.floor(minute / MINUTES_STEP); } export default MinutePicker;