UNPKG

baseui

Version:

A React Component library implementing the Base design language

315 lines (312 loc) • 13 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = void 0; var React = _interopRequireWildcard(require("react")); var _overrides = require("../helpers/overrides"); var _locale = require("../locale"); var _select = require("../select"); var _dateHelpers = _interopRequireDefault(require("../datepicker/utils/date-helpers")); var _dateFnsAdapter = _interopRequireDefault(require("../datepicker/utils/date-fns-adapter")); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); } function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; } function _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); } function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : String(i); } function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); } /* Copyright (c) Uber Technologies, Inc. This source code is licensed under the MIT license found in the LICENSE file in the root directory of this source tree. */ const MINUTE = 60; const HOUR = MINUTE * 60; const DAY = HOUR * 24; const NOON = DAY / 2; class TimePicker extends React.Component { constructor(props) { super(props); _defineProperty(this, "dateHelpers", void 0); // @ts-ignore _defineProperty(this, "state", { steps: [], value: null }); _defineProperty(this, "onChange", params => { this.setState({ value: params.value[0] }); if (params.value.length === 0) { if (this.props.nullable) { this.props.onChange && this.props.onChange(null); } return; } const seconds = typeof params.value[0].id === 'string' ? parseInt(params.value[0].id, 10) : params.value[0].id || 0; this.handleChange(seconds); }); _defineProperty(this, "secondsToLabel", (seconds, format) => { let [hours, minutes] = this.dateHelpers.secondsToHourMinute(seconds); // @ts-ignore const zeroPrefix = n => n < 10 ? `0${n}` : n; if (format === '12') { const isAfterNoon = seconds >= NOON; if (isAfterNoon) { hours -= 12; } if (hours === 0) { hours = 12; } return `${hours}:${zeroPrefix(minutes)} ${isAfterNoon ? 'PM' : 'AM'}`; } return `${zeroPrefix(hours)}:${zeroPrefix(minutes)}`; }); /** * Converts a time string, e.g. 10:00, to one or more possible TimePicker * options representing that time. */ _defineProperty(this, "stringToOptions", (str, format = '12') => { // leading zero is optional, AM/PM is optional const twelveHourRegex = /^(1[0-2]|0?[1-9]):([0-5][0-9]) ?([AaPp][Mm]?)?$/; // leading zero is optional const twentyFourHourRegex = /^([0-9]|0[0-9]|1[0-9]|2[0-3]):([0-5][0-9])$/; const regex = format === '12' ? twelveHourRegex : twentyFourHourRegex; const match = str.match(regex); if (!match) { return []; } const hours = Number(match[1]); const minutes = Number(match[2]); let hoursMinutes = []; switch (format) { case '24': { // @ts-ignore hoursMinutes = [{ hours, minutes }]; break; } case '12': default: { const twelveHours = hours % 12; const meridiem = match[3]; // if there's no AM/PM, add both AM and PM options if (!meridiem) { hoursMinutes = [ // @ts-ignore { hours: twelveHours, minutes }, // @ts-ignore { hours: twelveHours + 12, minutes }]; } else { const twentyFourHours = meridiem.toLowerCase()[0] === 'a' ? twelveHours : twelveHours + 12; // @ts-ignore hoursMinutes = [{ hours: twentyFourHours, minutes }]; } break; } } return hoursMinutes.map(({ hours, minutes }) => { const secs = hours * 3600 + minutes * 60; return { id: secs, label: this.secondsToLabel(secs, format) }; }); }); _defineProperty(this, "handleChange", seconds => { const [hours, minutes] = this.dateHelpers.secondsToHourMinute(seconds); const updatedDate = this.setTime(this.props.value, hours, minutes, 0); this.props.onChange && this.props.onChange(updatedDate); }); _defineProperty(this, "setTime", (val, hours, minutes, seconds) => { const { setSeconds, setMinutes, setHours } = this.dateHelpers; const date = this.props.adapter.startOfDay(this.props.adapter.date(val || undefined)); return setSeconds(setMinutes(setHours(date, hours), minutes), seconds); }); _defineProperty(this, "getTimeWindowInSeconds", step => { let { minTime: min, maxTime: max, ignoreMinMaxDateComponent } = this.props; const dayStart = this.setTime(this.props.value, 0, 0, 0); const dayEnd = this.setTime(this.props.value, 24, 0, 0); if (!min || this.props.adapter.isBefore(min, dayStart) && !ignoreMinMaxDateComponent) { min = dayStart; } else { min = this.setTime(this.props.value, this.props.adapter.getHours(min), this.props.adapter.getMinutes(min), this.props.adapter.getSeconds(min)); } if (!max || this.props.adapter.isAfter(max, dayEnd) && !ignoreMinMaxDateComponent) { max = dayEnd; } else { max = this.setTime(this.props.value, this.props.adapter.getHours(max), this.props.adapter.getMinutes(max), // maxTime (if provided) should be inclusive, so add an extra second here this.props.adapter.getSeconds(max) + 1); } const minDate = this.props.adapter.toJsDate(min); const maxDate = this.props.adapter.toJsDate(max); const midnightDate = this.props.adapter.toJsDate(dayStart); return { // @ts-expect-error todo(flow->ts) start: (minDate - midnightDate) / 1000, // @ts-expect-error todo(flow->ts) end: (maxDate - midnightDate) / 1000 }; }); _defineProperty(this, "buildSteps", () => { const { step = 900 } = this.props; const timeWindow = this.getTimeWindowInSeconds(step); let stepCount = (timeWindow.end - timeWindow.start) / step; if (process.env.NODE_ENV !== "production" && stepCount > 500) { // eslint-disable-next-line no-console console.warn(`Provided step value (${step}) results in ${stepCount} steps. Performance may suffer when more than 500 elements are rendered.`); } if (!Number.isInteger(stepCount)) { const previousStepCount = stepCount; stepCount = Math.round(stepCount); if (process.env.NODE_ENV !== "production") { // eslint-disable-next-line no-console console.warn(`Provided step value (${step}) does not spread evenly across a day. Rounding from ${previousStepCount} total steps to ${stepCount}.`); } } const options = []; for (let i = timeWindow.start; i < timeWindow.end; i += step) { // @ts-ignore options.push(i); } return options; }); _defineProperty(this, "creatableFilterOptions", (options, filterValue, excludeOptions, newProps) => { const result = this.stringToOptions(filterValue, this.props.format); if (result.length) { return result; } return (0, _select.filterOptions)(options, filterValue, excludeOptions, newProps); }); _defineProperty(this, "buildSelectedOption", (value, format = '12') => { const secs = this.dateHelpers.dateToSeconds(value); return { id: secs, label: this.secondsToLabel(secs, format || '12') }; }); this.dateHelpers = new _dateHelpers.default(props.adapter); } componentDidMount() { const steps = this.buildSteps(); if (this.props.value && this.props.adapter.isValid(this.props.value)) { this.setState({ steps: steps, value: this.buildSelectedOption(this.props.value, this.props.format) }); } else { const seconds = this.dateHelpers.dateToSeconds(this.props.adapter.date()); let closestStep = NOON; steps.forEach(step => { if (Math.abs(step - seconds) < Math.abs(closestStep - seconds)) { closestStep = step; } }); this.setState({ steps: steps, value: this.props.nullable ? undefined : { id: closestStep, label: this.secondsToLabel(closestStep, this.props.format) } }); if (this.props.value || !this.props.nullable && !this.props.value) { this.handleChange(closestStep); } } } componentDidUpdate(prevProps) { const formatChanged = prevProps.format !== this.props.format; const stepChanged = prevProps.step !== this.props.step; const adapterChanged = prevProps.adapter !== this.props.adapter; const minTimeChange = prevProps.minTime !== this.props.minTime; const maxTimeChange = prevProps.maxTime !== this.props.maxTime; if (adapterChanged) { this.dateHelpers = new _dateHelpers.default(this.props.adapter); } if (formatChanged || stepChanged || minTimeChange || maxTimeChange) { const steps = this.buildSteps(); this.setState({ steps }); } if (prevProps.value && !this.props.value) { this.setState({ value: null }); } } render() { const { format, overrides = {}, adapter } = this.props; const [OverriddenSelect, selectProps] = (0, _overrides.getOverrides)(overrides.Select, _select.Select); selectProps.overrides = (0, _overrides.mergeOverrides)({ Dropdown: { style: { maxHeight: '126px' } } }, selectProps.overrides); const value = this.props.value && adapter.isValid(this.props.value) ? this.buildSelectedOption(this.props.value, this.props.format) : this.state.value; return /*#__PURE__*/React.createElement(_locale.LocaleContext.Consumer, null, locale => { const ariaLabel = format === '12' ? locale.datepicker.timePickerAriaLabel12Hour : locale.datepicker.timePickerAriaLabel24Hour; return /*#__PURE__*/React.createElement(OverriddenSelect, _extends({ "aria-label": ariaLabel, disabled: this.props.disabled, error: this.props.error, positive: this.props.positive, size: this.props.size, placeholder: this.props.placeholder || 'HH:mm', options: this.state.steps.map(n => ({ id: n, label: this.secondsToLabel(n, this.props.format) })), filterOptions: this.props.creatable ? this.creatableFilterOptions : undefined, onChange: this.onChange // if value is defined, it should be an array type , value: value ? [value] : value, clearable: false, backspaceRemoves: false, valueKey: "label" }, selectProps)); }); } } _defineProperty(TimePicker, "defaultProps", { format: '12', step: 900, creatable: false, adapter: _dateFnsAdapter.default, ignoreMinMaxDateComponent: false }); var _default = exports.default = TimePicker;