react-widgets
Version:
An à la carte set of polished, extensible, and accessible inputs built for React
482 lines (385 loc) • 15.8 kB
JavaScript
'use strict';
var babelHelpers = require('./util/babelHelpers.js');
exports.__esModule = true;
var _VIEW, _OPPOSITE_DIRECTION, _MULTIPLIER;
var _react = require('react');
var _react2 = babelHelpers.interopRequireDefault(_react);
var _classnames = require('classnames');
var _classnames2 = babelHelpers.interopRequireDefault(_classnames);
var _utilCompat = require('./util/compat');
var _utilCompat2 = babelHelpers.interopRequireDefault(_utilCompat);
var _Header = require('./Header');
var _Header2 = babelHelpers.interopRequireDefault(_Header);
var _Footer = require('./Footer');
var _Footer2 = babelHelpers.interopRequireDefault(_Footer);
var _Month = require('./Month');
var _Month2 = babelHelpers.interopRequireDefault(_Month);
var _Year = require('./Year');
var _Year2 = babelHelpers.interopRequireDefault(_Year);
var _Decade = require('./Decade');
var _Decade2 = babelHelpers.interopRequireDefault(_Decade);
var _Century = require('./Century');
var _Century2 = babelHelpers.interopRequireDefault(_Century);
var _utilLocalizers = require('./util/localizers');
var _utilPropTypes = require('./util/propTypes');
var _utilPropTypes2 = babelHelpers.interopRequireDefault(_utilPropTypes);
var _uncontrollable = require('uncontrollable');
var _uncontrollable2 = babelHelpers.interopRequireDefault(_uncontrollable);
var _SlideTransition = require('./SlideTransition');
var _SlideTransition2 = babelHelpers.interopRequireDefault(_SlideTransition);
var _utilDates = require('./util/dates');
var _utilDates2 = babelHelpers.interopRequireDefault(_utilDates);
var _utilConstants = require('./util/constants');
var _utilConstants2 = babelHelpers.interopRequireDefault(_utilConstants);
var _util_ = require('./util/_');
var _util_2 = babelHelpers.interopRequireDefault(_util_);
//values, omit
var _utilWidgetHelpers = require('./util/widgetHelpers');
var _utilInteraction = require('./util/interaction');
var dir = _utilConstants2['default'].directions,
values = function values(obj) {
return Object.keys(obj).map(function (k) {
return obj[k];
});
},
invert = function invert(obj) {
return _util_2['default'].transform(obj, function (o, val, key) {
o[val] = key;
}, {});
};
var views = _utilConstants2['default'].calendarViews,
VIEW_OPTIONS = values(views),
ALT_VIEW = invert(_utilConstants2['default'].calendarViewHierarchy),
NEXT_VIEW = _utilConstants2['default'].calendarViewHierarchy,
VIEW_UNIT = _utilConstants2['default'].calendarViewUnits,
VIEW = (_VIEW = {}, _VIEW[views.MONTH] = _Month2['default'], _VIEW[views.YEAR] = _Year2['default'], _VIEW[views.DECADE] = _Decade2['default'], _VIEW[views.CENTURY] = _Century2['default'], _VIEW);
var ARROWS_TO_DIRECTION = {
ArrowDown: dir.DOWN,
ArrowUp: dir.UP,
ArrowRight: dir.RIGHT,
ArrowLeft: dir.LEFT
};
var OPPOSITE_DIRECTION = (_OPPOSITE_DIRECTION = {}, _OPPOSITE_DIRECTION[dir.LEFT] = dir.RIGHT, _OPPOSITE_DIRECTION[dir.RIGHT] = dir.LEFT, _OPPOSITE_DIRECTION);
var MULTIPLIER = (_MULTIPLIER = {}, _MULTIPLIER[views.YEAR] = 1, _MULTIPLIER[views.DECADE] = 10, _MULTIPLIER[views.CENTURY] = 100, _MULTIPLIER);
var format = function format(props, f) {
return _utilLocalizers.date.getFormat(f, props[f + 'Format']);
};
var propTypes = {
disabled: _utilPropTypes2['default'].disabled,
readOnly: _utilPropTypes2['default'].readOnly,
onChange: _react2['default'].PropTypes.func,
value: _react2['default'].PropTypes.instanceOf(Date),
min: _react2['default'].PropTypes.instanceOf(Date),
max: _react2['default'].PropTypes.instanceOf(Date),
initialView: _react2['default'].PropTypes.oneOf(VIEW_OPTIONS),
finalView: function finalView(props, propname, componentName) {
var err = _react2['default'].PropTypes.oneOf(VIEW_OPTIONS)(props, propname, componentName);
if (err) return err;
if (VIEW_OPTIONS.indexOf(props[propname]) < VIEW_OPTIONS.indexOf(props.initialView)) return new Error(('The `' + propname + '` prop: `' + props[propname] + '` cannot be \'lower\' than the `initialView`\n prop. This creates a range that cannot be rendered.').replace(/\n\t/g, ''));
},
culture: _react2['default'].PropTypes.string,
footer: _react2['default'].PropTypes.bool,
dayComponent: _utilPropTypes2['default'].elementType,
headerFormat: _utilPropTypes2['default'].dateFormat,
footerFormat: _utilPropTypes2['default'].dateFormat,
dayFormat: _utilPropTypes2['default'].dateFormat,
dateFormat: _utilPropTypes2['default'].dateFormat,
monthFormat: _utilPropTypes2['default'].dateFormat,
yearFormat: _utilPropTypes2['default'].dateFormat,
decadeFormat: _utilPropTypes2['default'].dateFormat,
centuryFormat: _utilPropTypes2['default'].dateFormat,
messages: _react2['default'].PropTypes.shape({
moveBack: _react2['default'].PropTypes.string,
moveForward: _react2['default'].PropTypes.string
})
};
var Calendar = _react2['default'].createClass(babelHelpers.createDecoratedObject([{
key: 'displayName',
initializer: function initializer() {
return 'Calendar';
}
}, {
key: 'mixins',
initializer: function initializer() {
return [require('./mixins/TimeoutMixin'), require('./mixins/PureRenderMixin'), require('./mixins/RtlParentContextMixin'), require('./mixins/AriaDescendantMixin')()];
}
}, {
key: 'propTypes',
initializer: function initializer() {
return propTypes;
}
}, {
key: 'getInitialState',
value: function getInitialState() {
var value = this.inRangeValue(this.props.value);
return {
selectedIndex: 0,
view: this.props.initialView || 'month',
currentDate: value ? new Date(value) : this.inRangeValue(new Date())
};
}
}, {
key: 'getDefaultProps',
value: function getDefaultProps() {
return {
value: null,
min: new Date(1900, 0, 1),
max: new Date(2099, 11, 31),
initialView: 'month',
finalView: 'century',
tabIndex: '0',
footer: false,
ariaActiveDescendantKey: 'calendar',
messages: msgs({})
};
}
}, {
key: 'componentWillReceiveProps',
value: function componentWillReceiveProps(nextProps) {
var bottom = VIEW_OPTIONS.indexOf(nextProps.initialView),
top = VIEW_OPTIONS.indexOf(nextProps.finalView),
current = VIEW_OPTIONS.indexOf(this.state.view),
view = this.state.view,
val = this.inRangeValue(nextProps.value);
if (current < bottom) this.setState({ view: view = nextProps.initialView });else if (current > top) this.setState({ view: view = nextProps.finalView });
//if the value changes reset views to the new one
if (!_utilDates2['default'].eq(val, dateOrNull(this.props.value), VIEW_UNIT[view])) this.setState({
currentDate: val ? new Date(val) : new Date()
});
}
}, {
key: 'render',
value: function render() {
var _this = this;
var _props = this.props;
var className = _props.className;
var value = _props.value;
var footerFormat = _props.footerFormat;
var disabled = _props.disabled;
var readOnly = _props.readOnly;
var finalView = _props.finalView;
var footer = _props.footer;
var messages = _props.messages;
var min = _props.min;
var max = _props.max;
var culture = _props.culture;
var duration = _props.duration;
var _state = this.state;
var view = _state.view;
var currentDate = _state.currentDate;
var slideDirection = _state.slideDirection;
var focused = _state.focused;
var View = VIEW[view],
unit = VIEW_UNIT[view],
todaysDate = new Date(),
todayNotInRange = !_utilDates2['default'].inRange(todaysDate, min, max, view);
unit = unit === 'day' ? 'date' : unit;
var viewID = _utilWidgetHelpers.instanceId(this, '_calendar'),
labelID = _utilWidgetHelpers.instanceId(this, '_calendar_label'),
key = view + '_' + _utilDates2['default'][view](currentDate);
var elementProps = _util_2['default'].omit(this.props, Object.keys(propTypes)),
viewProps = _util_2['default'].pick(this.props, Object.keys(View.propTypes));
var isDisabled = disabled || readOnly;
messages = msgs(this.props.messages);
return _react2['default'].createElement(
'div',
babelHelpers._extends({}, elementProps, {
role: 'group',
onKeyDown: this._keyDown,
onFocus: this._focus.bind(null, true),
onBlur: this._focus.bind(null, false),
className: _classnames2['default'](className, 'rw-calendar', 'rw-widget', {
'rw-state-focus': focused,
'rw-state-disabled': disabled,
'rw-state-readonly': readOnly,
'rw-rtl': this.isRtl()
})
}),
_react2['default'].createElement(_Header2['default'], {
label: this._label(),
labelId: labelID,
messages: messages,
upDisabled: isDisabled || view === finalView,
prevDisabled: isDisabled || !_utilDates2['default'].inRange(this.nextDate(dir.LEFT), min, max, view),
nextDisabled: isDisabled || !_utilDates2['default'].inRange(this.nextDate(dir.RIGHT), min, max, view),
onViewChange: this.navigate.bind(null, dir.UP, null),
onMoveLeft: this.navigate.bind(null, dir.LEFT, null),
onMoveRight: this.navigate.bind(null, dir.RIGHT, null)
}),
_react2['default'].createElement(
_SlideTransition2['default'],
{
ref: 'animation',
duration: duration,
direction: slideDirection,
onAnimate: function () {
return focused && _this.focus();
}
},
_react2['default'].createElement(View, babelHelpers._extends({}, viewProps, {
tabIndex: '-1',
key: key,
id: viewID,
className: 'rw-calendar-grid',
'aria-labelledby': labelID,
today: todaysDate,
value: value,
focused: currentDate,
onChange: this.change,
onKeyDown: this._keyDown,
ariaActiveDescendantKey: 'calendarView'
}))
),
footer && _react2['default'].createElement(_Footer2['default'], {
value: todaysDate,
format: footerFormat,
culture: culture,
disabled: disabled || todayNotInRange,
readOnly: readOnly,
onClick: this.select
})
);
}
}, {
key: 'navigate',
decorators: [_utilInteraction.widgetEditable],
value: function navigate(direction, date) {
var view = this.state.view,
slideDir = direction === dir.LEFT || direction === dir.UP ? 'right' : 'left';
if (!date) date = [dir.LEFT, dir.RIGHT].indexOf(direction) !== -1 ? this.nextDate(direction) : this.state.currentDate;
if (direction === dir.DOWN) view = ALT_VIEW[view] || view;
if (direction === dir.UP) view = NEXT_VIEW[view] || view;
if (this.isValidView(view) && _utilDates2['default'].inRange(date, this.props.min, this.props.max, view)) {
_utilWidgetHelpers.notify(this.props.onNavigate, [date, slideDir, view]);
this.focus(true);
this.setState({
currentDate: date,
slideDirection: slideDir,
view: view
});
}
}
}, {
key: 'focus',
value: function focus() {
if (+this.props.tabIndex > -1) _utilCompat2['default'].findDOMNode(this).focus();
//console.log(document.activeElement)
}
}, {
key: '_focus',
decorators: [_utilInteraction.widgetEnabled],
value: function _focus(focused, e) {
var _this2 = this;
if (+this.props.tabIndex === -1) return;
this.setTimeout('focus', function () {
if (focused !== _this2.state.focused) {
_utilWidgetHelpers.notify(_this2.props[focused ? 'onFocus' : 'onBlur'], e);
_this2.setState({ focused: focused });
}
});
}
}, {
key: 'change',
decorators: [_utilInteraction.widgetEditable],
value: function change(date) {
if (this.state.view === this.props.initialView) {
_utilWidgetHelpers.notify(this.props.onChange, date);
this.focus();
return;
}
this.navigate(dir.DOWN, date);
}
}, {
key: 'select',
decorators: [_utilInteraction.widgetEditable],
value: function select(date) {
var view = this.props.initialView,
slideDir = view !== this.state.view || _utilDates2['default'].gt(date, this.state.currentDate) ? 'left' // move down to a the view
: 'right';
_utilWidgetHelpers.notify(this.props.onChange, date);
if (this.isValidView(view) && _utilDates2['default'].inRange(date, this.props.min, this.props.max, view)) {
this.focus();
this.setState({
currentDate: date,
slideDirection: slideDir,
view: view
});
}
}
}, {
key: 'nextDate',
value: function nextDate(direction) {
var method = direction === dir.LEFT ? 'subtract' : 'add',
view = this.state.view,
unit = view === views.MONTH ? view : views.YEAR,
multi = MULTIPLIER[view] || 1;
return _utilDates2['default'][method](this.state.currentDate, 1 * multi, unit);
}
}, {
key: '_keyDown',
decorators: [_utilInteraction.widgetEditable],
value: function _keyDown(e) {
var ctrl = e.ctrlKey,
key = e.key,
direction = ARROWS_TO_DIRECTION[key],
current = this.state.currentDate,
view = this.state.view,
unit = VIEW_UNIT[view],
currentDate = current;
if (key === 'Enter') {
e.preventDefault();
return this.change(current);
}
if (direction) {
if (ctrl) {
e.preventDefault();
this.navigate(direction);
} else {
if (this.isRtl() && OPPOSITE_DIRECTION[direction]) direction = OPPOSITE_DIRECTION[direction];
currentDate = _utilDates2['default'].move(currentDate, this.props.min, this.props.max, view, direction);
if (!_utilDates2['default'].eq(current, currentDate, unit)) {
e.preventDefault();
if (_utilDates2['default'].gt(currentDate, current, view)) this.navigate(dir.RIGHT, currentDate);else if (_utilDates2['default'].lt(currentDate, current, view)) this.navigate(dir.LEFT, currentDate);else this.setState({ currentDate: currentDate });
}
}
}
_utilWidgetHelpers.notify(this.props.onKeyDown, [e]);
}
}, {
key: '_label',
value: function _label() {
var _props2 = this.props;
var culture = _props2.culture;
var props = babelHelpers.objectWithoutProperties(_props2, ['culture']);
var view = this.state.view;
var dt = this.state.currentDate;
if (view === 'month') return _utilLocalizers.date.format(dt, format(props, 'header'), culture);else if (view === 'year') return _utilLocalizers.date.format(dt, format(props, 'year'), culture);else if (view === 'decade') return _utilLocalizers.date.format(_utilDates2['default'].startOf(dt, 'decade'), format(props, 'decade'), culture);else if (view === 'century') return _utilLocalizers.date.format(_utilDates2['default'].startOf(dt, 'century'), format(props, 'century'), culture);
}
}, {
key: 'inRangeValue',
value: function inRangeValue(_value) {
var value = dateOrNull(_value);
if (value === null) return value;
return _utilDates2['default'].max(_utilDates2['default'].min(value, this.props.max), this.props.min);
}
}, {
key: 'isValidView',
value: function isValidView(next) {
var bottom = VIEW_OPTIONS.indexOf(this.props.initialView),
top = VIEW_OPTIONS.indexOf(this.props.finalView),
current = VIEW_OPTIONS.indexOf(next);
return current >= bottom && current <= top;
}
}]));
function dateOrNull(dt) {
if (dt && !isNaN(dt.getTime())) return dt;
return null;
}
function msgs(msgs) {
return babelHelpers._extends({
moveBack: 'navigate back',
moveForward: 'navigate forward'
}, msgs);
}
exports['default'] = _uncontrollable2['default'](Calendar, { value: 'onChange' });
module.exports = exports['default'];