@compeon/timepicker
Version:
Simple timepicker using react
605 lines (500 loc) • 19 kB
JavaScript
import * as React from 'react';
import React__default from 'react';
import PropTypes from 'prop-types';
import styled from 'styled-components';
function _classCallCheck(instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
}
function _defineProperties(target, props) {
for (var i = 0; i < props.length; i++) {
var descriptor = props[i];
descriptor.enumerable = descriptor.enumerable || false;
descriptor.configurable = true;
if ("value" in descriptor) descriptor.writable = true;
Object.defineProperty(target, descriptor.key, descriptor);
}
}
function _createClass(Constructor, protoProps, staticProps) {
if (protoProps) _defineProperties(Constructor.prototype, protoProps);
if (staticProps) _defineProperties(Constructor, staticProps);
return Constructor;
}
function _defineProperty(obj, key, value) {
if (key in obj) {
Object.defineProperty(obj, key, {
value: value,
enumerable: true,
configurable: true,
writable: true
});
} else {
obj[key] = value;
}
return obj;
}
function _extends() {
_extends = Object.assign || 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 _inherits(subClass, superClass) {
if (typeof superClass !== "function" && superClass !== null) {
throw new TypeError("Super expression must either be null or a function");
}
subClass.prototype = Object.create(superClass && superClass.prototype, {
constructor: {
value: subClass,
writable: true,
configurable: true
}
});
if (superClass) _setPrototypeOf(subClass, superClass);
}
function _getPrototypeOf(o) {
_getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) {
return o.__proto__ || Object.getPrototypeOf(o);
};
return _getPrototypeOf(o);
}
function _setPrototypeOf(o, p) {
_setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) {
o.__proto__ = p;
return o;
};
return _setPrototypeOf(o, p);
}
function _isNativeReflectConstruct() {
if (typeof Reflect === "undefined" || !Reflect.construct) return false;
if (Reflect.construct.sham) return false;
if (typeof Proxy === "function") return true;
try {
Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {}));
return true;
} catch (e) {
return false;
}
}
function _assertThisInitialized(self) {
if (self === void 0) {
throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
}
return self;
}
function _possibleConstructorReturn(self, call) {
if (call && (typeof call === "object" || typeof call === "function")) {
return call;
} else if (call !== void 0) {
throw new TypeError("Derived constructors may only return object or undefined");
}
return _assertThisInitialized(self);
}
function _createSuper(Derived) {
var hasNativeReflectConstruct = _isNativeReflectConstruct();
return function _createSuperInternal() {
var Super = _getPrototypeOf(Derived),
result;
if (hasNativeReflectConstruct) {
var NewTarget = _getPrototypeOf(this).constructor;
result = Reflect.construct(Super, arguments, NewTarget);
} else {
result = Super.apply(this, arguments);
}
return _possibleConstructorReturn(this, result);
};
}
function _taggedTemplateLiteral(strings, raw) {
if (!raw) {
raw = strings.slice(0);
}
return Object.freeze(Object.defineProperties(strings, {
raw: {
value: Object.freeze(raw)
}
}));
}
function _slicedToArray(arr, i) {
return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest();
}
function _arrayWithHoles(arr) {
if (Array.isArray(arr)) return arr;
}
function _iterableToArrayLimit(arr, i) {
var _i = arr == null ? null : typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"];
if (_i == null) return;
var _arr = [];
var _n = true;
var _d = false;
var _s, _e;
try {
for (_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true) {
_arr.push(_s.value);
if (i && _arr.length === i) break;
}
} catch (err) {
_d = true;
_e = err;
} finally {
try {
if (!_n && _i["return"] != null) _i["return"]();
} finally {
if (_d) throw _e;
}
}
return _arr;
}
function _unsupportedIterableToArray(o, minLen) {
if (!o) return;
if (typeof o === "string") return _arrayLikeToArray(o, minLen);
var n = Object.prototype.toString.call(o).slice(8, -1);
if (n === "Object" && o.constructor) n = o.constructor.name;
if (n === "Map" || n === "Set") return Array.from(o);
if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen);
}
function _arrayLikeToArray(arr, len) {
if (len == null || len > arr.length) len = arr.length;
for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i];
return arr2;
}
function _nonIterableRest() {
throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
}
var theme = {
primaryColor: '#27718c'
};
var _templateObject$2;
var ArrowDown = function ArrowDown(props) {
return /*#__PURE__*/React__default.createElement("svg", _extends({
xmlns: "http://www.w3.org/2000/svg",
width: "24",
height: "24",
viewBox: "0 0 24 24"
}, props), /*#__PURE__*/React__default.createElement("path", {
d: "M7.41,8.59L12,13.17l4.59-4.58L18,10l-6,6l-6-6L7.41,8.59z"
}), /*#__PURE__*/React__default.createElement("path", {
fill: "none",
d: "M0,0h24v24H0V0z"
}));
};
var StyledArrowDown = styled(ArrowDown)(_templateObject$2 || (_templateObject$2 = _taggedTemplateLiteral(["\n cursor: pointer;\n fill: ", ";\n"])), function (props) {
return props.theme.primaryColor;
});
StyledArrowDown.defaultProps = {
theme: theme
};
var _templateObject$1;
var ArrowUp = function ArrowUp(props) {
return /*#__PURE__*/React__default.createElement("svg", _extends({
xmlns: "http://www.w3.org/2000/svg",
width: "24",
height: "24",
viewBox: "0 0 24 24"
}, props), /*#__PURE__*/React__default.createElement("path", {
d: "M7.41 15.41L12 10.83l4.59 4.58L18 14l-6-6-6 6z"
}), /*#__PURE__*/React__default.createElement("path", {
d: "M0 0h24v24H0z",
fill: "none"
}));
};
var StyledArrowUp = styled(ArrowUp)(_templateObject$1 || (_templateObject$1 = _taggedTemplateLiteral(["\n cursor: pointer;\n fill: ", ";\n"])), function (props) {
return props.theme.primaryColor;
});
StyledArrowUp.defaultProps = {
theme: theme
};
var MINUTES_PER_DAY = 24 * 60;
var inRange = function inRange(from, to, totalMinutes) {
var normalizedMinutes = normalizeTotalMinutes(totalMinutes);
if (from < to) return normalizedMinutes >= from && normalizedMinutes <= to;
if (from > to) return normalizedMinutes >= from || normalizedMinutes <= to;
return from === to === normalizedMinutes;
};
var normalizeHours = function normalizeHours(hours) {
if (hours < 0) return 0;
if (hours > 23) return 23;
return hours;
};
var normalizeMinutes = function normalizeMinutes(minutes) {
if (minutes < 0) return 0;
if (minutes > 59) return 59;
return minutes;
};
var normalizeTotalMinutes = function normalizeTotalMinutes(totalMinutes) {
return totalMinutes < 0 ? MINUTES_PER_DAY + totalMinutes % MINUTES_PER_DAY : totalMinutes % MINUTES_PER_DAY;
};
var formatNumber = function formatNumber(number) {
var parsedNumber = Math.abs(parseInt(number) || 0);
return parsedNumber > 9 ? String(parsedNumber) : "0".concat(String(parsedNumber));
};
var formatTime = function formatTime(_ref) {
var hours = _ref.hours,
minutes = _ref.minutes;
return "".concat(formatNumber(hours), ":").concat(formatNumber(minutes));
};
var parseTime = function parseTime(time) {
var _time$split = time.split(':'),
_time$split2 = _slicedToArray(_time$split, 2),
hours = _time$split2[0],
minutes = _time$split2[1];
return {
hours: normalizeHours(parseInt(hours) || 0),
minutes: normalizeMinutes(parseInt(minutes) || 0)
};
};
var minutesToHoursAndMinutes = function minutesToHoursAndMinutes(minutes) {
var normalizedMinutes = normalizeTotalMinutes(minutes);
var hours = Math.floor(normalizedMinutes / 60);
return {
hours: hours,
minutes: normalizedMinutes - hours * 60
};
};
var hoursAndMinutesToMinutes = function hoursAndMinutesToMinutes(hours, minutes) {
return 60 * hours + minutes;
};
var totalMinutesFromRange = function totalMinutesFromRange(from, to) {
var fromTime = parseTime(from);
var fromMinutes = hoursAndMinutesToMinutes(fromTime.hours, fromTime.minutes);
var toTime = parseTime(to);
var toMinutes = hoursAndMinutesToMinutes(toTime.hours, toTime.minutes);
var rangeInMinutes = fromMinutes <= toMinutes ? toMinutes - fromMinutes : MINUTES_PER_DAY - fromMinutes + toMinutes;
return {
fromMinutes: fromMinutes,
rangeInMinutes: rangeInMinutes,
toMinutes: toMinutes
};
};
var addOrSubtractMinutesWithRange = function addOrSubtractMinutesWithRange(value, delta, from, to) {
var _totalMinutesFromRang = totalMinutesFromRange(from, to),
fromMinutes = _totalMinutesFromRang.fromMinutes,
rangeInMinutes = _totalMinutesFromRang.rangeInMinutes,
toMinutes = _totalMinutesFromRang.toMinutes;
var _parseTime = parseTime(value),
hours = _parseTime.hours,
minutes = _parseTime.minutes;
var nextMinutes = hoursAndMinutesToMinutes(hours, minutes) + delta;
if (rangeInMinutes === 0) {
return minutesToHoursAndMinutes(fromMinutes);
} else if (inRange(fromMinutes, toMinutes, nextMinutes)) {
return minutesToHoursAndMinutes(nextMinutes);
} else {
return delta > 0 ? minutesToHoursAndMinutes(toMinutes) : minutesToHoursAndMinutes(fromMinutes);
}
};
var addOrSubtractMinutes = function addOrSubtractMinutes(value, delta) {
var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
var from = options.from,
to = options.to;
if (from && !to) throw new Error('`from` range parameter is specified while `to` is missing.');
if (!from && to) throw new Error('`to` range parameter is specified while `from` is missing.');
if (from && to) {
return formatTime(addOrSubtractMinutesWithRange(value, delta, from, to));
}
var _parseTime2 = parseTime(value),
hours = _parseTime2.hours,
minutes = _parseTime2.minutes;
var nextMinutes = hoursAndMinutesToMinutes(hours, minutes) + delta;
return formatTime(minutesToHoursAndMinutes(nextMinutes));
};
var _templateObject, _templateObject2;
var TimepickerDialog = styled.div(_templateObject || (_templateObject = _taggedTemplateLiteral(["\n align-items: center;\n background: white;\n border-radius: 5px;\n border: 1px solid ", ";\n box-shadow: 0 1px 3px #d3d3d380, 0 1px 3px #d3d3d380;\n color: ", ";\n display: flex;\n font-size: 22px;\n justify-content: center;\n position: absolute;\n user-select: none;\n width: 190px;\n z-index: 999;\n"])), function (props) {
return props.theme.primaryColor;
}, function (props) {
return props.theme.primaryColor;
});
TimepickerDialog.defaultProps = {
theme: theme
};
var TimepickerPart = styled.div(_templateObject2 || (_templateObject2 = _taggedTemplateLiteral(["\n align-items: center;\n display: flex;\n flex-direction: column;\n margin: auto 12px;\n"])));
var TimepickerBlock = function TimepickerBlock(_ref) {
var number = _ref.number,
onDownClick = _ref.onDownClick,
onUpClick = _ref.onUpClick;
return /*#__PURE__*/React__default.createElement(TimepickerPart, null, /*#__PURE__*/React__default.createElement(StyledArrowUp, {
onClick: onUpClick,
size: 36
}), formatNumber(number), /*#__PURE__*/React__default.createElement(StyledArrowDown, {
onClick: onDownClick,
size: 36
}));
};
var Timepicker = /*#__PURE__*/function (_React$Component) {
_inherits(Timepicker, _React$Component);
var _super = _createSuper(Timepicker);
function Timepicker() {
var _this;
_classCallCheck(this, Timepicker);
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
_this = _super.call.apply(_super, [this].concat(args));
_defineProperty(_assertThisInitialized(_this), "state", {
dialogOpen: false
});
_defineProperty(_assertThisInitialized(_this), "addDOMEvents", function () {
document.addEventListener('click', _this.handleDocumentClick);
document.addEventListener('keydown', _this.handleDocumentKeyPress);
if (_this.inputElement) _this.inputElement.addEventListener('keydown', _this.handleDocumentKeyPress);
});
_defineProperty(_assertThisInitialized(_this), "removeDOMEvents", function () {
document.removeEventListener('click', _this.handleDocumentClick);
document.removeEventListener('keydown', _this.handleDocumentKeyPress);
if (_this.inputElement) _this.inputElement.removeEventListener('keydown', _this.handleDocumentKeyPress);
});
_defineProperty(_assertThisInitialized(_this), "ensureValueInRange", function () {
return _this.handleMinutesChange(0);
});
_defineProperty(_assertThisInitialized(_this), "closeDialog", function () {
if (!_this.state.dialogOpen) return;
var nextValue = _this.ensureValueInRange();
_this.setState({
dialogOpen: false
});
_this.props.onBlur(nextValue);
});
_defineProperty(_assertThisInitialized(_this), "openDialog", function () {
if (_this.state.dialogOpen) return;
_this.setState({
dialogOpen: true
});
});
_defineProperty(_assertThisInitialized(_this), "handleDocumentKeyPress", function (event) {
if (event.key !== 'Tab') return;
_this.closeDialog();
});
_defineProperty(_assertThisInitialized(_this), "handleDocumentClick", function (event) {
var element = event.target;
if (!_this.dialogElement || !_this.inputElement || !(element instanceof Node)) return;
if (_this.dialogElement.contains(element) || _this.inputElement.contains(element)) return;
_this.closeDialog();
});
_defineProperty(_assertThisInitialized(_this), "handleDialogRef", function (element) {
_this.dialogElement = element;
});
_defineProperty(_assertThisInitialized(_this), "handleInputRef", function (element) {
_this.inputElement = element;
});
_defineProperty(_assertThisInitialized(_this), "handleChange", function (event) {
return _this.props.onChange(event.target.value || '');
});
_defineProperty(_assertThisInitialized(_this), "handleFocus", function (event) {
_this.props.onFocus(event);
_this.openDialog();
});
_defineProperty(_assertThisInitialized(_this), "handleHoursChange", function (delta) {
return _this.handleMinutesChange(60 * delta);
});
_defineProperty(_assertThisInitialized(_this), "handleMinutesChange", function (delta) {
var _this$props = _this.props,
from = _this$props.from,
onChange = _this$props.onChange,
to = _this$props.to,
value = _this$props.value;
var nextValue = addOrSubtractMinutes(value, delta, {
from: from,
to: to
});
if (nextValue !== value) onChange(nextValue);
return nextValue;
});
_defineProperty(_assertThisInitialized(_this), "handleWheel", function (event) {
var minutesPerStep = _this.props.minutesPerStep;
var delta = event.deltaY > 0 ? +minutesPerStep : -minutesPerStep;
event.preventDefault();
_this.handleMinutesChange(delta);
});
return _this;
}
_createClass(Timepicker, [{
key: "componentDidMount",
value: function componentDidMount() {
this.addDOMEvents();
this.ensureValueInRange();
}
}, {
key: "componentWillUnmount",
value: function componentWillUnmount() {
this.removeDOMEvents();
}
}, {
key: "render",
value: function render() {
var _this2 = this;
var dialogOpen = this.state.dialogOpen;
var _this$props2 = this.props,
children = _this$props2.children,
className = _this$props2.className,
dialogClassName = _this$props2.dialogClassName,
inputClassName = _this$props2.inputClassName,
minutesPerStep = _this$props2.minutesPerStep,
value = _this$props2.value;
var _parseTime = parseTime(value),
hours = _parseTime.hours,
minutes = _parseTime.minutes;
return /*#__PURE__*/React.createElement("div", {
className: className
}, /*#__PURE__*/React.cloneElement(React.Children.only(children), {
className: inputClassName,
ref: this.handleInputRef,
onBlur: function onBlur() {},
onChange: this.handleChange,
onFocus: this.handleFocus,
onTouchMove: this.handleWheel,
onWheel: this.handleWheel,
value: value
}), dialogOpen && /*#__PURE__*/React.createElement(TimepickerDialog, {
className: dialogClassName,
innerRef: this.handleDialogRef,
onTouchMove: this.handleWheel,
onWheel: this.handleWheel
}, /*#__PURE__*/React.createElement(TimepickerBlock, {
number: hours,
onDownClick: function onDownClick() {
return _this2.handleHoursChange(-1);
},
onUpClick: function onUpClick() {
return _this2.handleHoursChange(1);
}
}), ":", /*#__PURE__*/React.createElement(TimepickerBlock, {
number: minutes,
onDownClick: function onDownClick() {
return _this2.handleMinutesChange(-minutesPerStep);
},
onUpClick: function onUpClick() {
return _this2.handleMinutesChange(+minutesPerStep);
}
})));
}
}]);
return Timepicker;
}(React.Component);
_defineProperty(Timepicker, "defaultProps", {
minutesPerStep: 15,
onBlur: function onBlur() {},
onChange: function onChange() {},
onFocus: function onFocus() {},
value: '00:00'
});
_defineProperty(Timepicker, "propTypes", {
className: PropTypes.string,
dialogClassName: PropTypes.string,
from: PropTypes.string,
inputClassName: PropTypes.string,
minutesPerStep: PropTypes.number,
onBlur: PropTypes.func,
onChange: PropTypes.func,
onFocus: PropTypes.func,
to: PropTypes.string,
value: PropTypes.string.isRequired
});
export { Timepicker as default };