baseui
Version:
A React Component library implementing the Base design language
315 lines (312 loc) • 13 kB
JavaScript
"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;