UNPKG

grommet

Version:

The most advanced UX framework for enterprise applications.

648 lines (555 loc) 21.5 kB
'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); var _defineProperty2 = require('babel-runtime/helpers/defineProperty'); var _defineProperty3 = _interopRequireDefault(_defineProperty2); var _getPrototypeOf = require('babel-runtime/core-js/object/get-prototype-of'); var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf); var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck'); var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); var _createClass2 = require('babel-runtime/helpers/createClass'); var _createClass3 = _interopRequireDefault(_createClass2); var _possibleConstructorReturn2 = require('babel-runtime/helpers/possibleConstructorReturn'); var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2); var _inherits2 = require('babel-runtime/helpers/inherits'); var _inherits3 = _interopRequireDefault(_inherits2); var _react = require('react'); var _react2 = _interopRequireDefault(_react); var _classnames3 = require('classnames'); var _classnames4 = _interopRequireDefault(_classnames3); var _moment = require('moment'); var _moment2 = _interopRequireDefault(_moment); var _Box = require('./Box'); var _Box2 = _interopRequireDefault(_Box); var _Header = require('./Header'); var _Header2 = _interopRequireDefault(_Header); var _Title = require('./Title'); var _Title2 = _interopRequireDefault(_Title); var _Button = require('./Button'); var _Button2 = _interopRequireDefault(_Button); var _LinkPrevious = require('./icons/base/LinkPrevious'); var _LinkPrevious2 = _interopRequireDefault(_LinkPrevious); var _LinkNext = require('./icons/base/LinkNext'); var _LinkNext2 = _interopRequireDefault(_LinkNext); var _Add = require('./icons/base/Add'); var _Add2 = _interopRequireDefault(_Add); var _Subtract = require('./icons/base/Subtract'); var _Subtract2 = _interopRequireDefault(_Subtract); var _CSSClassnames = require('../utils/CSSClassnames'); var _CSSClassnames2 = _interopRequireDefault(_CSSClassnames); var _Announcer = require('../utils/Announcer'); var _Intl = require('../utils/Intl'); var _Intl2 = _interopRequireDefault(_Intl); var _KeyboardAccelerators = require('../utils/KeyboardAccelerators'); var _KeyboardAccelerators2 = _interopRequireDefault(_KeyboardAccelerators); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } var CLASS_ROOT = _CSSClassnames2.default.DATE_TIME_DROP; // (C) Copyright 2014-2016 Hewlett Packard Enterprise Development LP var WEEK_DAYS = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']; // const DATE_REGEXP = new RegExp('[DMY]'); var DAY_REGEXP = new RegExp('[D]'); var MONTHYEAR_REGEXP = new RegExp('[MY]'); var TIME_REGEXP = new RegExp('[hHmsa]'); var UNITS = { M: 'month', D: 'day', Y: 'year', h: 'hour', H: 'hour', m: 'minute', s: 'second', a: 'ampm' }; var DateTimeDrop = function (_Component) { (0, _inherits3.default)(DateTimeDrop, _Component); function DateTimeDrop(props, context) { (0, _classCallCheck3.default)(this, DateTimeDrop); var _this = (0, _possibleConstructorReturn3.default)(this, (DateTimeDrop.__proto__ || (0, _getPrototypeOf2.default)(DateTimeDrop)).call(this, props, context)); _this._announceActiveCell = _this._announceActiveCell.bind(_this); _this._buildDateRows = _this._buildDateRows.bind(_this); _this._onDay = _this._onDay.bind(_this); _this._onToday = _this._onToday.bind(_this); _this._onPrevious = _this._onPrevious.bind(_this); _this._onPreviousDay = _this._onPreviousDay.bind(_this); _this._onPreviousRow = _this._onPreviousRow.bind(_this); _this._onNext = _this._onNext.bind(_this); _this._onNextDay = _this._onNextDay.bind(_this); _this._onNextRow = _this._onNextRow.bind(_this); _this._onSelectDay = _this._onSelectDay.bind(_this); _this.state = _this._stateFromProps(props); _this.state.mouseActive = false; _this._buildDateRows(_this.state); return _this; } (0, _createClass3.default)(DateTimeDrop, [{ key: 'componentDidMount', value: function componentDidMount() { this._keyboardHandlers = { up: this._onPreviousRow, left: this._onPreviousDay, down: this._onNextRow, right: this._onNextDay, enter: this._onSelectDay }; _KeyboardAccelerators2.default.startListeningToKeyboard(this, this._keyboardHandlers); } }, { key: 'componentWillReceiveProps', value: function componentWillReceiveProps(nextProps) { var state = this._stateFromProps(nextProps); this._buildDateRows(state); this.setState(state); } }, { key: 'componentWillUnmount', value: function componentWillUnmount() { _KeyboardAccelerators2.default.stopListeningToKeyboard(this, this._keyboardHandlers); } }, { key: '_buildDateRows', value: function _buildDateRows(state) { var timeOfDay = state.timeOfDay, value = state.value; var start = (0, _moment2.default)(value).startOf('month').startOf('week').add(timeOfDay); var end = (0, _moment2.default)(value).endOf('month').endOf('week').add(timeOfDay); var date = (0, _moment2.default)(start); var dateRows = []; var activeCell = void 0; var rowIndex = 0; while (date.valueOf() <= end.valueOf()) { var days = []; for (var i = 0; i < 7; i += 1) { if (date.isSame(value, 'day')) { activeCell = [rowIndex, i]; } days.push((0, _moment2.default)(date)); date = date.add(1, 'days'); } dateRows.push(days); rowIndex++; } state.dateRows = dateRows; state.activeCell = activeCell; state.originalActiveCell = activeCell.slice(); } }, { key: '_stateFromProps', value: function _stateFromProps(props) { var format = props.format; var result = {}; var value = (0, _moment2.default)(props.value); if (value.isValid()) { result.value = value; result.timeOfDay = { hours: value.hours(), minutes: value.minutes(), seconds: value.seconds() }; } else { result.value = (0, _moment2.default)(); } // figure out which scope the step should apply to if (format.indexOf('s') !== -1) { result.stepScope = 'second'; } else if (format.indexOf('m') !== -1) { result.stepScope = 'minute'; } else if (format.indexOf('h') !== -1) { result.stepScope = 'hour'; } return result; } }, { key: '_announceActiveCell', value: function _announceActiveCell() { var _state = this.state, activeCell = _state.activeCell, dateRows = _state.dateRows; var intl = this.context.intl; var weekDay = WEEK_DAYS[activeCell[1]]; var day = dateRows[activeCell[0]][activeCell[1]].date(); var enterSelectMessage = _Intl2.default.getMessage(intl, 'Enter Select'); (0, _Announcer.announce)(weekDay + ', ' + day + ' (' + enterSelectMessage + ')'); } }, { key: '_onPreviousRow', value: function _onPreviousRow(event) { event.preventDefault(); var activeCell = this.state.activeCell; if (this.tableRef.contains(document.activeElement)) { if (activeCell[0] - 1 >= 0) { activeCell[0] = activeCell[0] - 1; this.setState({ activeCell: activeCell }, this._announceActiveCell); } } } }, { key: '_onPreviousDay', value: function _onPreviousDay(event) { event.preventDefault(); var activeCell = this.state.activeCell; if (this.tableRef.contains(document.activeElement)) { if (activeCell[1] - 1 >= 0) { activeCell[1] = activeCell[1] - 1; this.setState({ activeCell: activeCell }, this._announceActiveCell); } } } }, { key: '_onNextRow', value: function _onNextRow(event) { event.preventDefault(); var _state2 = this.state, dateRows = _state2.dateRows, activeCell = _state2.activeCell; if (this.tableRef.contains(document.activeElement)) { if (activeCell[0] + 1 <= dateRows.length - 1) { activeCell[0] = activeCell[0] + 1; this.setState({ activeCell: activeCell }, this._announceActiveCell); } } } }, { key: '_onNextDay', value: function _onNextDay(event) { event.preventDefault(); var activeCell = this.state.activeCell; if (this.tableRef.contains(document.activeElement)) { if (activeCell[1] + 1 <= WEEK_DAYS.length - 1) { activeCell[1] = activeCell[1] + 1; this.setState({ activeCell: activeCell }, this._announceActiveCell); } } } }, { key: '_onSelectDay', value: function _onSelectDay() { var _state3 = this.state, activeCell = _state3.activeCell, dateRows = _state3.dateRows; if (this.tableRef.contains(document.activeElement)) { var date = dateRows[activeCell[0]][activeCell[1]]; this._onDay(date); } } }, { key: '_onDay', value: function _onDay(date, event) { if (event) { event.stopPropagation(); // using native event to avoid document click in DateTime to be invoked event.nativeEvent.stopImmediatePropagation(); } var _props = this.props, format = _props.format, onChange = _props.onChange; var intl = this.context.intl; this.setState({ value: (0, _moment2.default)(date) }, function () { var dateFormatted = date.format(format); onChange(dateFormatted, true); var selectedMessage = _Intl2.default.getMessage(intl, 'Selected'); (0, _Announcer.announce)(dateFormatted + ' ' + selectedMessage); }); } }, { key: '_onToday', value: function _onToday() { var _props2 = this.props, format = _props2.format, onChange = _props2.onChange; var timeOfDay = this.state.timeOfDay; var intl = this.context.intl; var today = (0, _moment2.default)().startOf('day').add(timeOfDay); this.setState({ value: today }, function () { var dateFormatted = today.format(format); onChange(dateFormatted, true); var selectedMessage = _Intl2.default.getMessage(intl, 'Selected'); (0, _Announcer.announce)(dateFormatted + ' ' + selectedMessage); }); } }, { key: '_onPrevious', value: function _onPrevious(scope) { var notify = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true; var _props3 = this.props, format = _props3.format, step = _props3.step, onChange = _props3.onChange; var _state4 = this.state, stepScope = _state4.stepScope, timeOfDay = _state4.timeOfDay, value = _state4.value; var delta = scope === stepScope ? step : 1; if (scope === 'ampm') { delta = 12; scope = 'hours'; } var newValue = (0, _moment2.default)(value).subtract(delta, scope); this.setState({ value: newValue }, function () { if (scope === 'month') { (0, _Announcer.announce)(newValue.format('MMMM YYYY')); } else { (0, _Announcer.announce)(newValue.format(format)); } }); if (notify) { onChange(newValue.format(format)); } else { // rebuild grid var state = { timeOfDay: timeOfDay, value: newValue }; this._buildDateRows(state); this.setState(state); } } }, { key: '_onNext', value: function _onNext(scope) { var notify = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true; var _props4 = this.props, format = _props4.format, step = _props4.step, onChange = _props4.onChange; var _state5 = this.state, stepScope = _state5.stepScope, timeOfDay = _state5.timeOfDay, value = _state5.value; var delta = scope === stepScope ? step : 1; if (scope === 'ampm') { delta = 12; scope = 'hours'; } var newValue = (0, _moment2.default)(value).add(delta, scope); this.setState({ value: newValue }, function () { if (scope === 'month') { (0, _Announcer.announce)(newValue.format('MMMM YYYY')); } else { (0, _Announcer.announce)(newValue.format(format)); } }); if (notify) { onChange(newValue.format(format)); } else { // rebuild grid var state = { timeOfDay: timeOfDay, value: newValue }; this._buildDateRows(state); this.setState(state); } } }, { key: '_renderGrid', value: function _renderGrid() { var _this2 = this; var propsValue = this.props.value; var _state6 = this.state, activeCell = _state6.activeCell, dateRows = _state6.dateRows, focus = _state6.focus, mouseActive = _state6.mouseActive, value = _state6.value; var intl = this.context.intl; var dateSelectorMessage = _Intl2.default.getMessage(intl, 'Date Selector'); var navigationHelpMessage = _Intl2.default.getMessage(intl, 'Navigation Help'); var headerCells = WEEK_DAYS.map(function (day) { return _react2.default.createElement( 'th', { key: day }, day ); }); var rows = dateRows.map(function (row, rowIndex) { var days = row.map(function (date, columnIndex) { var _classnames; var classes = (0, _classnames4.default)(CLASS_ROOT + '__day', (_classnames = {}, (0, _defineProperty3.default)(_classnames, CLASS_ROOT + '__day--active', date.isSame(propsValue, 'day')), (0, _defineProperty3.default)(_classnames, CLASS_ROOT + '__day--hover', !date.isSame(value, 'day') && [rowIndex, columnIndex].toString() === activeCell.toString()), (0, _defineProperty3.default)(_classnames, CLASS_ROOT + '__day--other-month', !date.isSame(value, 'month')), _classnames)); var weekDay = WEEK_DAYS[columnIndex]; var day = dateRows[rowIndex][columnIndex].date(); return _react2.default.createElement( 'td', { key: date.valueOf() }, _react2.default.createElement( 'div', { className: classes, tabIndex: '-1', onClick: _this2._onDay.bind(_this2, (0, _moment2.default)(date)), 'aria-label': weekDay + ' ' + day, role: 'button', onFocus: function onFocus() { return _this2.setState({ activeCell: [rowIndex, columnIndex] }); }, onBlur: function onBlur() { return _this2.setState({ activeCell: _this2.state.originalActiveCell }); } }, date.date() ) ); }); return _react2.default.createElement( 'tr', { key: 'date_row_' + rowIndex }, days ); }); var gridClasses = (0, _classnames4.default)(CLASS_ROOT + '__grid', (0, _defineProperty3.default)({}, CLASS_ROOT + '__grid--focus', focus)); return _react2.default.createElement( 'div', { key: 'grid', className: gridClasses }, _react2.default.createElement( 'table', { ref: function ref(_ref) { return _this2.tableRef = _ref; }, tabIndex: '0', 'aria-label': dateSelectorMessage + ' (' + navigationHelpMessage + ')', onMouseDown: function onMouseDown() { return _this2.setState({ mouseActive: true }); }, onMouseUp: function onMouseUp() { return _this2.setState({ mouseActive: false }); }, onFocus: function onFocus() { if (mouseActive === false) { _this2.setState({ focus: true }); } }, onBlur: function onBlur() { return _this2.setState({ activeCell: _this2.state.originalActiveCell, focus: false }); } }, _react2.default.createElement( 'thead', null, _react2.default.createElement( 'tr', null, headerCells ) ), _react2.default.createElement( 'tbody', null, rows ) ) ); } }, { key: '_renderCalendar', value: function _renderCalendar() { var format = this.props.format; var value = this.state.value; var intl = this.context.intl; var previousMonthMessage = _Intl2.default.getMessage(intl, 'Previous Month'); var nextMonthMessage = _Intl2.default.getMessage(intl, 'Next Month'); var todayMessage = _Intl2.default.getMessage(intl, 'Today'); var grid = format.match(/D/) ? this._renderGrid() : _react2.default.createElement('span', { key: 'grid' }); return [_react2.default.createElement( _Header2.default, { key: 'header', justify: 'between', colorIndex: 'neutral-1' }, _react2.default.createElement(_Button2.default, { className: CLASS_ROOT + '__previous', icon: _react2.default.createElement(_LinkPrevious2.default, null), a11yTitle: previousMonthMessage, onClick: this._onPrevious.bind(this, 'month', false) }), _react2.default.createElement( _Title2.default, { className: CLASS_ROOT + '__title', responsive: false }, value.format('MMMM YYYY') ), _react2.default.createElement(_Button2.default, { className: CLASS_ROOT + '__next', icon: _react2.default.createElement(_LinkNext2.default, null), a11yTitle: nextMonthMessage, onClick: this._onNext.bind(this, 'month', false) }) ), grid, _react2.default.createElement( _Box2.default, { key: 'today', alignSelf: 'center', pad: { vertical: 'small' } }, _react2.default.createElement(_Button2.default, { className: CLASS_ROOT + '__today', label: todayMessage, onClick: this._onToday }) )]; } }, { key: '_renderCounters', value: function _renderCounters(includeDate) { var _this3 = this; var format = this.props.format; var value = this.state.value; var intl = this.context.intl; // break the format up into chunks var chunks = []; var index = 0; while (index < format.length) { var chunk = format[index]; index += 1; while (format[index] === chunk[0]) { chunk += format[index]; index += 1; } chunks.push(chunk); } var addMessage = _Intl2.default.getMessage(intl, 'Add'); var subtractMessage = _Intl2.default.getMessage(intl, 'Subtract'); var elements = chunks.map(function (chunk, index) { var unit = UNITS[chunk[0]]; if (unit) { var unitMessage = _Intl2.default.getMessage(intl, unit); return _react2.default.createElement( _Box2.default, { key: index, align: 'center' }, _react2.default.createElement(_Button2.default, { icon: _react2.default.createElement(_Subtract2.default, null), a11yTitle: subtractMessage + ' ' + unitMessage, onClick: _this3._onPrevious.bind(_this3, unit) }), value.format('M' === chunk ? 'MMM' : chunk), _react2.default.createElement(_Button2.default, { icon: _react2.default.createElement(_Add2.default, null), a11yTitle: addMessage + ' ' + unitMessage, onClick: _this3._onNext.bind(_this3, unit) }) ); } else { return _react2.default.createElement( _Box2.default, { key: index, align: 'center', justify: 'center', className: 'secondary' }, chunk ); } }); return _react2.default.createElement( _Box2.default, { className: CLASS_ROOT + '__time', direction: 'row', alignSelf: 'center', responsive: false }, elements ); } }, { key: 'render', value: function render() { var format = this.props.format; var calendar = void 0, counters = void 0; if (DAY_REGEXP.test(format)) { calendar = this._renderCalendar(); } if (TIME_REGEXP.test(format) || MONTHYEAR_REGEXP.test(format) && !DAY_REGEXP.test(format)) { counters = this._renderCounters(!DAY_REGEXP.test(format)); } return _react2.default.createElement( _Box2.default, { className: CLASS_ROOT }, calendar, counters ); } }]); return DateTimeDrop; }(_react.Component); DateTimeDrop.displayName = 'DateTimeDrop'; exports.default = DateTimeDrop; DateTimeDrop.contextTypes = { intl: _react.PropTypes.object }; DateTimeDrop.propTypes = { format: _react.PropTypes.string, onChange: _react.PropTypes.func.isRequired, step: _react.PropTypes.number.isRequired, value: _react.PropTypes.object.isRequired }; module.exports = exports['default'];