react-keyboard-time-input
Version:
A keyboard friendly react component for capturing time
267 lines (239 loc) • 7.96 kB
JavaScript
'use strict';
exports.__esModule = true;
var _react = require('react');
var _react2 = _interopRequireDefault(_react);
var _createReactClass = require('create-react-class');
var _createReactClass2 = _interopRequireDefault(_createReactClass);
var _propTypes = require('prop-types');
var _propTypes2 = _interopRequireDefault(_propTypes);
var _isTwelveHourTime = require('./lib/is-twelve-hour-time');
var _isTwelveHourTime2 = _interopRequireDefault(_isTwelveHourTime);
var _replaceCharAt = require('./lib/replace-char-at');
var _replaceCharAt2 = _interopRequireDefault(_replaceCharAt);
var _getGroupId = require('./lib/get-group-id');
var _getGroupId2 = _interopRequireDefault(_getGroupId);
var _getGroups = require('./lib/get-groups');
var _getGroups2 = _interopRequireDefault(_getGroups);
var _timeStringAdder = require('./lib/time-string-adder');
var _timeStringAdder2 = _interopRequireDefault(_timeStringAdder);
var _caret = require('./lib/caret');
var _caret2 = _interopRequireDefault(_caret);
var _validate = require('./lib/validate');
var _validate2 = _interopRequireDefault(_validate);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
var SILHOUETTE = '00:00:00:000 AM';
// isSeparator :: Char -> Bool
var isSeparator = function isSeparator(char) {
return (/[:\s]/.test(char)
);
};
var TimeInput = (0, _createReactClass2.default)({
getInitialState: function getInitialState() {
return {};
},
getDefaultProps: function getDefaultProps() {
return {
value: '12:00 AM'
};
},
propTypes: {
className: _propTypes2.default.string,
value: _propTypes2.default.string,
onChange: _propTypes2.default.func
},
render: function render() {
var _this = this;
var className = 'TimeInput';
if (this.props.className) {
className += ' ' + this.props.className;
}
return _react2.default.createElement(
'div',
{ className: className },
_react2.default.createElement('input', {
className: 'TimeInput-input',
ref: function ref(input) {
_this.input = input;
},
type: 'text',
value: this.format(this.props.value),
onChange: this.handleChange,
onBlur: this.handleBlur,
onKeyDown: this.handleKeyDown
})
);
},
format: function format(val) {
if ((0, _isTwelveHourTime2.default)(val)) val = val.replace(/^00/, '12');
return val.toUpperCase();
},
componentDidMount: function componentDidMount() {
this.mounted = true;
},
componentWillUnmount: function componentWillUnmount() {
this.mounted = false;
},
componentDidUpdate: function componentDidUpdate() {
var index = this.state.caretIndex;
if (index || index === 0) _caret2.default.set(this.input, index);
},
handleBlur: function handleBlur() {
if (this.mounted) this.setState({ caretIndex: null });
},
handleEscape: function handleEscape() {
if (this.mounted) this.input.blur();
},
handleTab: function handleTab(event) {
var start = _caret2.default.start(this.input);
var value = this.props.value;
var groups = (0, _getGroups2.default)(value);
var groupId = (0, _getGroupId2.default)(start);
if (event.shiftKey) {
if (!groupId) return;
groupId--;
} else {
if (groupId >= groups.length - 1) return;
groupId++;
}
event.preventDefault();
var index = groupId * 3;
if (this.props.value.charAt(index) === ' ') index++;
if (this.mounted) this.setState({ caretIndex: index });
},
handleArrows: function handleArrows(event) {
event.preventDefault();
var start = _caret2.default.start(this.input);
var value = this.props.value;
var amount = event.which === 38 ? 1 : -1;
if (event.shiftKey) {
amount *= 2;
if (event.metaKey) amount *= 2;
}
value = (0, _timeStringAdder2.default)(value, (0, _getGroupId2.default)(start), amount);
this.onChange(value, start);
},
silhouette: function silhouette() {
return this.props.value.replace(/\d/g, function (val, i) {
return SILHOUETTE.charAt(i);
});
},
handleBackspace: function handleBackspace(event) {
event.preventDefault();
var start = _caret2.default.start(this.input);
var value = this.props.value;
var end = _caret2.default.end(this.input);
if (!start && !end) return;
var diff = end - start;
var silhouette = this.silhouette();
if (!diff) {
if (value[start - 1] === ':') start--;
value = (0, _replaceCharAt2.default)(value, start - 1, silhouette.charAt(start - 1));
start--;
} else {
while (diff--) {
if (value[end - 1] !== ':') {
value = (0, _replaceCharAt2.default)(value, end - 1, silhouette.charAt(end - 1));
}
end--;
}
}
if (value.charAt(start - 1) === ':') start--;
this.onChange(value, start);
},
handleForwardSpace: function handleForwardSpace(event) {
event.preventDefault();
var start = _caret2.default.start(this.input);
var value = this.props.value;
var end = _caret2.default.end(this.input);
if (start === end === value.length - 1) return;
var diff = end - start;
var silhouette = this.silhouette();
if (!diff) {
if (value[start] === ':') start++;
value = (0, _replaceCharAt2.default)(value, start, silhouette.charAt(start));
start++;
} else {
while (diff--) {
if (value[end - 1] !== ':') {
value = (0, _replaceCharAt2.default)(value, start, silhouette.charAt(start));
}
start++;
}
}
if (value.charAt(start) === ':') start++;
this.onChange(value, start);
},
handleKeyDown: function handleKeyDown(event) {
switch (event.which) {
case 9:
// Tab
return this.handleTab(event);
case 8:
// Backspace
return this.handleBackspace(event);
case 46:
// Forward
return this.handleForwardSpace(event);
case 27:
// Esc
return this.handleEscape(event);
case 38: // Left
case 40:
// Right
return this.handleArrows(event);
default:
break;
}
},
handleChange: function handleChange(event) {
var value = this.props.value;
var newValue = this.input.value;
var diff = newValue.length - value.length;
var end = _caret2.default.start(this.input);
var insertion = void 0;
var start = end - Math.abs(diff);
event.preventDefault();
if (diff > 0) {
insertion = newValue.slice(end - diff, end);
while (diff--) {
var oldChar = value.charAt(start);
var newChar = insertion.charAt(0);
if (isSeparator(oldChar)) {
if (isSeparator(newChar)) {
insertion = insertion.slice(1);
start++;
} else {
start++;
diff++;
end++;
}
} else {
value = (0, _replaceCharAt2.default)(value, start, newChar);
insertion = insertion.slice(1);
start++;
}
}
newValue = value;
} else {
if (newValue.charAt(start) === ':') start++;
// apply default to selection
var result = value;
for (var i = start; i < end; i++) {
result = (0, _replaceCharAt2.default)(result, i, newValue.charAt(i));
}
newValue = result;
}
if ((0, _validate2.default)(newValue)) {
if (newValue.charAt(end) === ':') end++;
this.onChange(newValue, end);
} else {
var caretIndex = this.props.value.length - (newValue.length - end);
if (this.mounted) this.setState({ caretIndex: caretIndex });
}
},
onChange: function onChange(str, caretIndex) {
if (this.props.onChange) this.props.onChange(this.format(str));
if (this.mounted && typeof caretIndex === 'number') this.setState({ caretIndex: caretIndex });
}
});
exports.default = TimeInput;