UNPKG

@gechiui/components

Version:
266 lines (243 loc) 8.48 kB
import { Fragment } from "@gechiui/element"; /** * External dependencies */ import classnames from 'classnames'; import { isInteger } from 'lodash'; import moment from 'moment'; /** * GeChiUI dependencies */ import { createElement, useState, useMemo, useEffect } from '@gechiui/element'; import { __ } from '@gechiui/i18n'; /** * Internal dependencies */ import Button from '../button'; import ButtonGroup from '../button-group'; import TimeZone from './timezone'; /** * Module Constants */ const TIMEZONELESS_FORMAT = 'YYYY-MM-DDTHH:mm:ss'; function from12hTo24h(hours, isPm) { return isPm ? (hours % 12 + 12) % 24 : hours % 12; } /** * <UpdateOnBlurAsIntegerField> * A shared component to parse, validate, and handle remounting of the underlying form field element like <input> and <select>. * * @param {Object} props Component props. * @param {string} props.as Render the component as specific element tag, defaults to "input". * @param {number|string} props.value The default value of the component which will be parsed to integer. * @param {Function} props.onUpdate Call back when blurred and validated. */ function UpdateOnBlurAsIntegerField(_ref) { let { as, value, onUpdate, ...props } = _ref; function handleBlur(event) { const { target } = event; if (value === target.value) { return; } const parsedValue = parseInt(target.value, 10); // Run basic number validation on the input. if (!isInteger(parsedValue) || typeof props.max !== 'undefined' && parsedValue > props.max || typeof props.min !== 'undefined' && parsedValue < props.min) { // If validation failed, reset the value to the previous valid value. target.value = value; } else { // Otherwise, it's valid, call onUpdate. onUpdate(target.name, parsedValue); } } return createElement(as || 'input', { // Re-mount the input value to accept the latest value as the defaultValue. key: value, defaultValue: value, onBlur: handleBlur, ...props }); } /** * <TimePicker> * * @typedef {Date|string|number} GCValidDateTimeFormat * * @param {Object} props Component props. * @param {boolean} props.is12Hour Should the time picker showed in 12 hour format or 24 hour format. * @param {GCValidDateTimeFormat} props.currentTime The initial current time the time picker should render. * @param {Function} props.onChange Callback function when the date changed. */ export function TimePicker(_ref2) { let { is12Hour, currentTime, onChange } = _ref2; const [date, setDate] = useState(() => // Truncate the date at the minutes, see: #15495. moment(currentTime).startOf('minutes')); // Reset the state when currentTime changed. useEffect(() => { setDate(currentTime ? moment(currentTime).startOf('minutes') : moment()); }, [currentTime]); const { day, month, year, minutes, hours, am } = useMemo(() => ({ day: date.format('DD'), month: date.format('MM'), year: date.format('YYYY'), minutes: date.format('mm'), hours: date.format(is12Hour ? 'hh' : 'HH'), am: date.format('H') <= 11 ? 'AM' : 'PM' }), [date, is12Hour]); /** * Function that sets the date state and calls the onChange with a new date. * The date is truncated at the minutes. * * @param {Object} newDate The date object. */ function changeDate(newDate) { setDate(newDate); onChange(newDate.format(TIMEZONELESS_FORMAT)); } function update(name, value) { // If the 12-hour format is being used and the 'PM' period is selected, then // the incoming value (which ranges 1-12) should be increased by 12 to match // the expected 24-hour format. let adjustedValue = value; if (name === 'hours' && is12Hour) { adjustedValue = from12hTo24h(value, am === 'PM'); } // Clone the date and call the specific setter function according to `name`. const newDate = date.clone()[name](adjustedValue); changeDate(newDate); } function updateAmPm(value) { return () => { if (am === value) { return; } const parsedHours = parseInt(hours, 10); const newDate = date.clone().hours(from12hTo24h(parsedHours, value === 'PM')); changeDate(newDate); }; } const dayFormat = createElement("div", { className: "components-datetime__time-field components-datetime__time-field-day" }, createElement(UpdateOnBlurAsIntegerField, { "aria-label": __('日'), className: "components-datetime__time-field-day-input", type: "number" // The correct function to call in moment.js is "date" not "day". , name: "date", value: day, step: 1, min: 1, max: 31, onUpdate: update })); const monthFormat = createElement("div", { className: "components-datetime__time-field components-datetime__time-field-month" }, createElement(UpdateOnBlurAsIntegerField, { as: "select", "aria-label": __('月'), className: "components-datetime__time-field-month-select", name: "month", value: month // The value starts from 0, so we have to -1 when setting month. , onUpdate: (key, value) => update(key, value - 1) }, createElement("option", { value: "01" }, __('1月')), createElement("option", { value: "02" }, __('2月')), createElement("option", { value: "03" }, __('3月')), createElement("option", { value: "04" }, __('4月')), createElement("option", { value: "05" }, __('5月')), createElement("option", { value: "06" }, __('6月')), createElement("option", { value: "07" }, __('7月')), createElement("option", { value: "08" }, __('8月')), createElement("option", { value: "09" }, __('9月')), createElement("option", { value: "10" }, __('10月')), createElement("option", { value: "11" }, __('11月')), createElement("option", { value: "12" }, __('12月')))); const dayMonthFormat = is12Hour ? createElement(Fragment, null, dayFormat, monthFormat) : createElement(Fragment, null, monthFormat, dayFormat); return createElement("div", { className: classnames('components-datetime__time') }, createElement("fieldset", null, createElement("legend", { className: "components-datetime__time-legend invisible" }, __('日期')), createElement("div", { className: "components-datetime__time-wrapper" }, dayMonthFormat, createElement("div", { className: "components-datetime__time-field components-datetime__time-field-year" }, createElement(UpdateOnBlurAsIntegerField, { "aria-label": __('年'), className: "components-datetime__time-field-year-input", type: "number", name: "year", step: 1, min: 0, max: 9999, value: year, onUpdate: update })))), createElement("fieldset", null, createElement("legend", { className: "components-datetime__time-legend invisible" }, __('时间')), createElement("div", { className: "components-datetime__time-wrapper" }, createElement("div", { className: "components-datetime__time-field components-datetime__time-field-time" }, createElement(UpdateOnBlurAsIntegerField, { "aria-label": __('小时'), className: "components-datetime__time-field-hours-input", type: "number", name: "hours", step: 1, min: is12Hour ? 1 : 0, max: is12Hour ? 12 : 23, value: hours, onUpdate: update }), createElement("span", { className: "components-datetime__time-separator", "aria-hidden": "true" }, ":"), createElement(UpdateOnBlurAsIntegerField, { "aria-label": __('分钟'), className: "components-datetime__time-field-minutes-input", type: "number", name: "minutes", step: 1, min: 0, max: 59, value: minutes, onUpdate: update })), is12Hour && createElement(ButtonGroup, { className: "components-datetime__time-field components-datetime__time-field-am-pm" }, createElement(Button, { variant: am === 'AM' ? 'primary' : 'secondary', onClick: updateAmPm('AM'), className: "components-datetime__time-am-button" }, __('上午')), createElement(Button, { variant: am === 'PM' ? 'primary' : 'secondary', onClick: updateAmPm('PM'), className: "components-datetime__time-pm-button" }, __('下午'))), createElement(TimeZone, null)))); } export default TimePicker; //# sourceMappingURL=time.js.map