react-calendar-timeline
Advanced tools
Comparing version 0.15.11 to 0.16.0-beta.2
@@ -10,9 +10,13 @@ # Change Log | ||
When you submit a PR, add your changes here! | ||
### Added | ||
## 0.15.11 | ||
* added `stickyHeader` to disable/enable timeline header sticking on scroll. | ||
* removed `fullUpdate` prop and functionality. Labels rely on `position: sticky` to show for items that start before `visibleTimeStart`. This (should) greatly improve scroll performance. | ||
* removed extraneous css such as `text-align: center` on `.rct-item`, `.rct-item-overflow` to simplify the dom structure of `Item.js` | ||
* added `headerRef` callback to receive a reference to the header element. Due to the change in how the header positioning is implemented (i.e. using `position: sticky`), there is a need to use a polyfill in [certain browsers](https://caniuse.com/#feat=css-sticky) that don't support `position: sticky`. With a reference to the header dom element, you can use a polyfill to apply sticky behavior. | ||
* `minimumWidthForItemContentVisibility` prop to control at what width inner item content is rendered. | ||
### Fixed | ||
### Breaking | ||
* removed `preventDefault` call in item double click handler - #277 | ||
* removed `fixedHeader` prop in favor of using `position: sticky` by default | ||
@@ -19,0 +23,0 @@ ## 0.15.10 |
@@ -74,2 +74,3 @@ 'use strict'; | ||
_this.handleDoubleClick = function (e) { | ||
e.preventDefault(); | ||
e.stopPropagation(); | ||
@@ -115,3 +116,3 @@ if (_this.props.onItemDoubleClick) { | ||
value: function shouldComponentUpdate(nextProps, nextState) { | ||
var shouldUpdate = nextState.dragging !== this.state.dragging || nextState.dragTime !== this.state.dragTime || nextState.dragGroupDelta !== this.state.dragGroupDelta || nextState.resizing !== this.state.resizing || nextState.resizeTime !== this.state.resizeTime || nextProps.keys !== this.props.keys || !(0, _generic.deepObjectCompare)(nextProps.itemProps, this.props.itemProps) || nextProps.selected !== this.props.selected || nextProps.item !== this.props.item || nextProps.canvasTimeStart !== this.props.canvasTimeStart || nextProps.canvasTimeEnd !== this.props.canvasTimeEnd || nextProps.canvasWidth !== this.props.canvasWidth || nextProps.order !== this.props.order || nextProps.dragSnap !== this.props.dragSnap || nextProps.minResizeWidth !== this.props.minResizeWidth || nextProps.canChangeGroup !== this.props.canChangeGroup || nextProps.canSelect !== this.props.canSelect || nextProps.topOffset !== this.props.topOffset || nextProps.canMove !== this.props.canMove || nextProps.canResizeLeft !== this.props.canResizeLeft || nextProps.canResizeRight !== this.props.canResizeRight || nextProps.dimensions !== this.props.dimensions; | ||
var shouldUpdate = nextState.dragging !== this.state.dragging || nextState.dragTime !== this.state.dragTime || nextState.dragGroupDelta !== this.state.dragGroupDelta || nextState.resizing !== this.state.resizing || nextState.resizeTime !== this.state.resizeTime || nextProps.keys !== this.props.keys || !(0, _generic.deepObjectCompare)(nextProps.itemProps, this.props.itemProps) || nextProps.selected !== this.props.selected || nextProps.item !== this.props.item || nextProps.canvasTimeStart !== this.props.canvasTimeStart || nextProps.canvasTimeEnd !== this.props.canvasTimeEnd || nextProps.canvasWidth !== this.props.canvasWidth || nextProps.order !== this.props.order || nextProps.dragSnap !== this.props.dragSnap || nextProps.minResizeWidth !== this.props.minResizeWidth || nextProps.canChangeGroup !== this.props.canChangeGroup || nextProps.canSelect !== this.props.canSelect || nextProps.topOffset !== this.props.topOffset || nextProps.canMove !== this.props.canMove || nextProps.canResizeLeft !== this.props.canResizeLeft || nextProps.canResizeRight !== this.props.canResizeRight || nextProps.dimensions !== this.props.dimensions || nextProps.minimumWidthForItemContentVisibility !== this.props.minimumWidthForItemContentVisibility; | ||
return shouldUpdate; | ||
@@ -382,3 +383,3 @@ } | ||
if (!props.canResizeLeft || props.dimensions.clippedLeft) { | ||
if (!props.canResizeLeft) { | ||
return false; | ||
@@ -394,3 +395,3 @@ } | ||
if (!props.canResizeRight || props.dimensions.clippedRight) { | ||
if (!props.canResizeRight) { | ||
return false; | ||
@@ -473,3 +474,3 @@ } | ||
var classNames = 'rct-item' + (this.props.selected ? ' selected' : '') + (this.canMove(this.props) ? ' can-move' : '') + (this.canResizeLeft(this.props) || this.canResizeRight(this.props) ? ' can-resize' : '') + (this.canResizeLeft(this.props) ? ' can-resize-left' : '') + (this.canResizeRight(this.props) ? ' can-resize-right' : '') + (this.props.item.className ? ' ' + this.props.item.className : '') + (dimensions.clippedLeft ? ' clipped-left' : '') + (dimensions.clippedRight ? ' clipped-right' : ''); | ||
var classNames = 'rct-item' + (this.props.selected ? ' selected' : '') + (this.canMove(this.props) ? ' can-move' : '') + (this.canResizeLeft(this.props) || this.canResizeRight(this.props) ? ' can-resize' : '') + (this.canResizeLeft(this.props) ? ' can-resize-left' : '') + (this.canResizeRight(this.props) ? ' can-resize-right' : '') + (this.props.item.className ? ' ' + this.props.item.className : ''); | ||
@@ -484,2 +485,5 @@ var style = { | ||
var showInnerContents = dimensions.width > this.props.minimumWidthForItemContentVisibility; | ||
// TODO: conditionals are really ugly. could use Fragment if supporting React 16+ but for now, it'll | ||
// be ugly | ||
return _react2.default.createElement( | ||
@@ -502,15 +506,16 @@ 'div', | ||
}), | ||
this.props.useResizeHandle ? _react2.default.createElement('div', { ref: function ref(el) { | ||
this.props.useResizeHandle && showInnerContents ? _react2.default.createElement('div', { ref: function ref(el) { | ||
return _this3.dragLeft = el; | ||
}, className: 'rct-drag-left' }) : '', | ||
_react2.default.createElement( | ||
showInnerContents ? _react2.default.createElement( | ||
'div', | ||
{ className: 'rct-item-overflow' }, | ||
_react2.default.createElement( | ||
'div', | ||
{ className: 'rct-item-content' }, | ||
this.renderContent() | ||
) | ||
), | ||
this.props.useResizeHandle ? _react2.default.createElement('div', { ref: function ref(el) { | ||
{ | ||
className: 'rct-item-content', | ||
style: { | ||
maxWidth: dimensions.width + 'px' | ||
} | ||
}, | ||
this.renderContent() | ||
) : '', | ||
this.props.useResizeHandle && showInnerContents ? _react2.default.createElement('div', { ref: function ref(el) { | ||
return _this3.dragRight = el; | ||
@@ -530,2 +535,3 @@ }, className: 'rct-drag-right' }) : '' | ||
order: _propTypes2.default.number, | ||
minimumWidthForItemContentVisibility: _propTypes2.default.number.isRequired, | ||
@@ -532,0 +538,0 @@ dragSnap: _propTypes2.default.number, |
@@ -54,3 +54,3 @@ 'use strict'; | ||
value: function shouldComponentUpdate(nextProps) { | ||
return !((0, _generic.arraysEqual)(nextProps.groups, this.props.groups) && (0, _generic.arraysEqual)(nextProps.items, this.props.items) && nextProps.keys === this.props.keys && nextProps.canvasTimeStart === this.props.canvasTimeStart && nextProps.canvasTimeEnd === this.props.canvasTimeEnd && nextProps.canvasWidth === this.props.canvasWidth && nextProps.selectedItem === this.props.selectedItem && nextProps.selected === this.props.selected && nextProps.dragSnap === this.props.dragSnap && nextProps.minResizeWidth === this.props.minResizeWidth && nextProps.canChangeGroup === this.props.canChangeGroup && nextProps.canMove === this.props.canMove && nextProps.canResize === this.props.canResize && nextProps.canSelect === this.props.canSelect && nextProps.dimensionItems === this.props.dimensionItems && nextProps.topOffset === this.props.topOffset); | ||
return !((0, _generic.arraysEqual)(nextProps.groups, this.props.groups) && (0, _generic.arraysEqual)(nextProps.items, this.props.items) && nextProps.keys === this.props.keys && nextProps.canvasTimeStart === this.props.canvasTimeStart && nextProps.canvasTimeEnd === this.props.canvasTimeEnd && nextProps.canvasWidth === this.props.canvasWidth && nextProps.selectedItem === this.props.selectedItem && nextProps.selected === this.props.selected && nextProps.dragSnap === this.props.dragSnap && nextProps.minResizeWidth === this.props.minResizeWidth && nextProps.canChangeGroup === this.props.canChangeGroup && nextProps.canMove === this.props.canMove && nextProps.canResize === this.props.canResize && nextProps.canSelect === this.props.canSelect && nextProps.dimensionItems === this.props.dimensionItems && nextProps.topOffset === this.props.topOffset && nextProps.minimumWidthForItemContentVisibility === this.props.minimumWidthForItemContentVisibility); | ||
} | ||
@@ -107,3 +107,4 @@ | ||
canvasTimeEnd = _props.canvasTimeEnd, | ||
dimensionItems = _props.dimensionItems; | ||
dimensionItems = _props.dimensionItems, | ||
minimumWidthForItemContentVisibility = _props.minimumWidthForItemContentVisibility; | ||
var _props$keys2 = this.props.keys, | ||
@@ -152,3 +153,4 @@ itemIdKey = _props$keys2.itemIdKey, | ||
onSelect: _this2.props.itemSelect, | ||
itemRenderer: _this2.props.itemRenderer | ||
itemRenderer: _this2.props.itemRenderer, | ||
minimumWidthForItemContentVisibility: minimumWidthForItemContentVisibility | ||
}); | ||
@@ -170,2 +172,3 @@ }) | ||
canvasWidth: _propTypes2.default.number.isRequired, | ||
minimumWidthForItemContentVisibility: _propTypes2.default.number.isRequired, | ||
@@ -172,0 +175,0 @@ dragSnap: _propTypes2.default.number, |
@@ -9,6 +9,2 @@ 'use strict'; | ||
var _propTypes = require('prop-types'); | ||
var _propTypes2 = _interopRequireDefault(_propTypes); | ||
var _react = require('react'); | ||
@@ -18,8 +14,10 @@ | ||
var _moment = require('moment'); | ||
var _propTypes = require('prop-types'); | ||
var _moment2 = _interopRequireDefault(_moment); | ||
var _propTypes2 = _interopRequireDefault(_propTypes); | ||
var _calendar = require('../utility/calendar'); | ||
var _TimelineElementsHeader = require('./TimelineElementsHeader'); | ||
var _TimelineElementsHeader2 = _interopRequireDefault(_TimelineElementsHeader); | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
@@ -36,207 +34,25 @@ | ||
function Header(props) { | ||
function Header() { | ||
_classCallCheck(this, Header); | ||
var _this = _possibleConstructorReturn(this, (Header.__proto__ || Object.getPrototypeOf(Header)).call(this, props)); | ||
_this.periodClick = function (e) { | ||
var _e$target$dataset = e.target.dataset, | ||
time = _e$target$dataset.time, | ||
unit = _e$target$dataset.unit; | ||
if (time && unit) { | ||
_this.props.showPeriod((0, _moment2.default)(time - 0), unit); | ||
} | ||
}; | ||
_this.touchStart = function (e) { | ||
if (e.touches.length === 1) { | ||
_this.setState({ | ||
touchTarget: e.target || e.touchTarget, | ||
touchActive: true | ||
}); | ||
} | ||
}; | ||
_this.touchEnd = function (e) { | ||
if (!_this.state.touchActive) { | ||
return _this.resetTouchState(); | ||
} | ||
var changedTouches = e.changedTouches[0]; | ||
if (changedTouches) { | ||
var elem = document.elementFromPoint(changedTouches.pageX, changedTouches.pageY); | ||
if (elem !== _this.state.touchTarget) { | ||
return _this.resetTouchState(); | ||
} | ||
} | ||
_this.resetTouchState(); | ||
_this.periodClick(e); | ||
}; | ||
_this.state = { | ||
touchTarget: null, | ||
touchActive: false | ||
}; | ||
return _this; | ||
return _possibleConstructorReturn(this, (Header.__proto__ || Object.getPrototypeOf(Header)).apply(this, arguments)); | ||
} | ||
_createClass(Header, [{ | ||
key: 'headerLabel', | ||
value: function headerLabel(time, unit, width) { | ||
var f = this.props.headerLabelFormats; | ||
if (unit === 'year') { | ||
return time.format(width < 46 ? f.yearShort : f.yearLong); | ||
} else if (unit === 'month') { | ||
return time.format(width < 65 ? f.monthShort : width < 75 ? f.monthMedium : width < 120 ? f.monthMediumLong : f.monthLong); | ||
} else if (unit === 'day') { | ||
return time.format(width < 150 ? f.dayShort : f.dayLong); | ||
} else if (unit === 'hour') { | ||
return time.format(width < 50 ? f.hourShort : width < 130 ? f.hourMedium : width < 150 ? f.hourMediumLong : f.hourLong); | ||
} else { | ||
return time.format(f.time); | ||
} | ||
} | ||
}, { | ||
key: 'subHeaderLabel', | ||
value: function subHeaderLabel(time, unit, width) { | ||
var f = this.props.subHeaderLabelFormats; | ||
if (unit === 'year') { | ||
return time.format(width < 46 ? f.yearShort : f.yearLong); | ||
} else if (unit === 'month') { | ||
return time.format(width < 37 ? f.monthShort : width < 85 ? f.monthMedium : f.monthLong); | ||
} else if (unit === 'day') { | ||
return time.format(width < 47 ? f.dayShort : width < 80 ? f.dayMedium : width < 120 ? f.dayMediumLong : f.dayLong); | ||
} else if (unit === 'hour') { | ||
return time.format(width < 50 ? f.hourShort : f.hourLong); | ||
} else if (unit === 'minute') { | ||
return time.format(width < 60 ? f.minuteShort : f.minuteLong); | ||
} else { | ||
return time.get(unit); | ||
} | ||
} | ||
}, { | ||
key: 'resetTouchState', | ||
value: function resetTouchState() { | ||
this.setState({ | ||
touchTarget: null, | ||
touchActive: false | ||
}); | ||
} | ||
}, { | ||
key: 'render', | ||
value: function render() { | ||
var _this2 = this; | ||
var timeLabels = []; | ||
var _props = this.props, | ||
canvasTimeStart = _props.canvasTimeStart, | ||
canvasTimeEnd = _props.canvasTimeEnd, | ||
canvasWidth = _props.canvasWidth, | ||
lineHeight = _props.lineHeight, | ||
visibleTimeStart = _props.visibleTimeStart, | ||
visibleTimeEnd = _props.visibleTimeEnd, | ||
minUnit = _props.minUnit, | ||
timeSteps = _props.timeSteps, | ||
fixedHeader = _props.fixedHeader, | ||
leftSidebarHeader = _props.leftSidebarHeader, | ||
rightSidebarHeader = _props.rightSidebarHeader, | ||
width = _props.width, | ||
stickyOffset = _props.stickyOffset, | ||
headerPosition = _props.headerPosition, | ||
headerLabelGroupHeight = _props.headerLabelGroupHeight, | ||
headerLabelHeight = _props.headerLabelHeight, | ||
hasRightSidebar = _props.hasRightSidebar, | ||
width = _props.width; | ||
stickyHeader = _props.stickyHeader, | ||
headerRef = _props.headerRef; | ||
var ratio = canvasWidth / (canvasTimeEnd - canvasTimeStart); | ||
var twoHeaders = minUnit !== 'year'; | ||
var correctLeftPositions = fixedHeader === 'fixed' || fixedHeader === 'sticky' && headerPosition === 'fixed'; | ||
// add the top header | ||
if (twoHeaders) { | ||
var nextUnit = (0, _calendar.getNextUnit)(minUnit); | ||
(0, _calendar.iterateTimes)(visibleTimeStart, visibleTimeEnd, nextUnit, timeSteps, function (time, nextTime) { | ||
var startTime = Math.max(visibleTimeStart, time.valueOf()); | ||
var endTime = Math.min(visibleTimeEnd, nextTime.valueOf()); | ||
var left = Math.round((startTime.valueOf() - canvasTimeStart) * ratio, -2); | ||
var right = Math.round((endTime.valueOf() - canvasTimeStart) * ratio, -2); | ||
var labelWidth = right - left; | ||
var leftCorrect = correctLeftPositions ? Math.round((canvasTimeStart - visibleTimeStart) * ratio) - 1 : 0; | ||
timeLabels.push(_react2.default.createElement( | ||
'div', | ||
{ | ||
key: 'top-label-' + time.valueOf(), | ||
className: 'rct-label-group' + (hasRightSidebar ? ' rct-has-right-sidebar' : ''), | ||
'data-time': time, | ||
'data-unit': nextUnit, | ||
style: { | ||
left: left + leftCorrect + 'px', | ||
width: labelWidth + 'px', | ||
height: headerLabelGroupHeight + 'px', | ||
lineHeight: headerLabelGroupHeight + 'px', | ||
cursor: 'pointer' | ||
} | ||
}, | ||
_this2.headerLabel(time, nextUnit, labelWidth) | ||
)); | ||
}); | ||
} | ||
(0, _calendar.iterateTimes)(canvasTimeStart, canvasTimeEnd, minUnit, timeSteps, function (time, nextTime) { | ||
var left = Math.round((time.valueOf() - canvasTimeStart) * ratio, -2); | ||
var minUnitValue = time.get(minUnit === 'day' ? 'date' : minUnit); | ||
var firstOfType = minUnitValue === (minUnit === 'day' ? 1 : 0); | ||
var labelWidth = Math.round((nextTime.valueOf() - time.valueOf()) * ratio, -2); | ||
var borderWidth = firstOfType ? 2 : 1; | ||
var leftCorrect = correctLeftPositions ? Math.round((canvasTimeStart - visibleTimeStart) * ratio) - borderWidth + 1 : 0; | ||
timeLabels.push(_react2.default.createElement( | ||
'div', | ||
{ | ||
key: 'label-' + time.valueOf(), | ||
className: 'rct-label ' + (twoHeaders ? '' : 'rct-label-only') + ' ' + (firstOfType ? 'rct-first-of-type' : '') + ' ' + (minUnit !== 'month' ? 'rct-day-' + time.day() : '') + ' ', | ||
'data-time': time, | ||
'data-unit': minUnit, | ||
style: { | ||
top: (minUnit === 'year' ? 0 : headerLabelGroupHeight) + 'px', | ||
left: left + leftCorrect + 'px', | ||
width: labelWidth + 'px', | ||
height: (minUnit === 'year' ? headerLabelGroupHeight + headerLabelHeight : headerLabelHeight) + 'px', | ||
lineHeight: (minUnit === 'year' ? headerLabelGroupHeight + headerLabelHeight : headerLabelHeight) + 'px', | ||
fontSize: (labelWidth > 30 ? '14' : labelWidth > 20 ? '12' : '10') + 'px', | ||
cursor: 'pointer' | ||
} | ||
}, | ||
_this2.subHeaderLabel(time, minUnit, labelWidth) | ||
)); | ||
}); | ||
var headerStyle = { | ||
height: headerLabelGroupHeight + headerLabelHeight + 'px', | ||
lineHeight: lineHeight + 'px' | ||
top: stickyHeader ? stickyOffset || 0 : 0 | ||
}; | ||
if (fixedHeader === 'fixed') { | ||
headerStyle.position = 'fixed'; | ||
headerStyle.width = width + 'px'; | ||
} else if (fixedHeader === 'sticky') { | ||
if (headerPosition === 'top') { | ||
// do nothing, keep at the top | ||
} else if (headerPosition === 'fixed') { | ||
headerStyle.position = 'fixed'; | ||
headerStyle.top = stickyOffset; | ||
headerStyle.width = width + 'px'; | ||
} else if (headerPosition === 'bottom') { | ||
headerStyle.position = 'absolute'; | ||
headerStyle.bottom = 0; | ||
headerStyle.width = canvasWidth + 'px'; | ||
} | ||
} | ||
var headerClass = stickyHeader ? 'header-sticky' : ''; | ||
@@ -246,10 +62,16 @@ return _react2.default.createElement( | ||
{ | ||
key: 'header', | ||
className: 'rct-header', | ||
onTouchStart: this.touchStart, | ||
onTouchEnd: this.touchEnd, | ||
onClick: this.periodClick, | ||
className: 'rct-header-container ' + headerClass, | ||
ref: headerRef, | ||
style: headerStyle | ||
}, | ||
timeLabels | ||
leftSidebarHeader, | ||
_react2.default.createElement( | ||
'div', | ||
{ | ||
style: { width: width } | ||
}, | ||
_react2.default.createElement(_TimelineElementsHeader2.default, this.props) | ||
), | ||
rightSidebarHeader | ||
); | ||
@@ -269,4 +91,2 @@ } | ||
lineHeight: _propTypes2.default.number.isRequired, | ||
visibleTimeStart: _propTypes2.default.number.isRequired, | ||
visibleTimeEnd: _propTypes2.default.number.isRequired, | ||
minUnit: _propTypes2.default.string.isRequired, | ||
@@ -277,13 +97,11 @@ timeSteps: _propTypes2.default.object.isRequired, | ||
subHeaderLabelFormats: _propTypes2.default.object.isRequired, | ||
fixedHeader: _propTypes2.default.oneOf(['fixed', 'sticky', 'none']), | ||
stickyOffset: _propTypes2.default.number.isRequired, | ||
headerPosition: _propTypes2.default.oneOf(['top', 'bottom', 'fixed']), | ||
stickyOffset: _propTypes2.default.number, | ||
stickyHeader: _propTypes2.default.bool.isRequired, | ||
headerLabelGroupHeight: _propTypes2.default.number.isRequired, | ||
headerLabelHeight: _propTypes2.default.number.isRequired | ||
headerLabelHeight: _propTypes2.default.number.isRequired, | ||
registerScroll: _propTypes2.default.func.isRequired, | ||
leftSidebarHeader: _propTypes2.default.node, | ||
rightSidebarHeader: _propTypes2.default.node, | ||
headerRef: _propTypes2.default.func.isRequired | ||
}; | ||
Header.defaultProps = { | ||
fixedHeader: 'sticky', | ||
stickyOffset: 0, | ||
headerPosition: 'top' | ||
}; | ||
exports.default = Header; |
@@ -39,3 +39,3 @@ 'use strict'; | ||
value: function shouldComponentUpdate(nextProps) { | ||
return !((0, _generic.arraysEqual)(nextProps.groups, this.props.groups) && nextProps.keys === this.props.keys && nextProps.width === this.props.width && nextProps.lineHeight === this.props.lineHeight && nextProps.fixedHeader === this.props.fixedHeader && nextProps.stickyOffset === this.props.stickyOffset && nextProps.headerPosition === this.props.headerPosition && nextProps.groupHeights === this.props.groupHeights && nextProps.height === this.props.height); | ||
return !((0, _generic.arraysEqual)(nextProps.groups, this.props.groups) && nextProps.keys === this.props.keys && nextProps.width === this.props.width && nextProps.groupHeights === this.props.groupHeights && nextProps.height === this.props.height); | ||
} | ||
@@ -60,11 +60,6 @@ }, { | ||
var _props = this.props, | ||
fixedHeader = _props.fixedHeader, | ||
stickyOffset = _props.stickyOffset, | ||
width = _props.width, | ||
lineHeight = _props.lineHeight, | ||
groupHeights = _props.groupHeights, | ||
height = _props.height, | ||
headerHeight = _props.headerHeight, | ||
isRightSidebar = _props.isRightSidebar, | ||
headerPosition = _props.headerPosition; | ||
isRightSidebar = _props.isRightSidebar; | ||
var _props$keys = this.props.keys, | ||
@@ -81,8 +76,2 @@ groupIdKey = _props$keys.groupIdKey, | ||
var headerStyle = { | ||
height: headerHeight + 'px', | ||
lineHeight: lineHeight + 'px', | ||
width: width + 'px' | ||
}; | ||
var groupsStyle = { | ||
@@ -92,25 +81,2 @@ width: width + 'px' | ||
if (fixedHeader === 'fixed') { | ||
headerStyle.position = 'fixed'; | ||
groupsStyle.paddingTop = headerStyle.height; | ||
} else if (fixedHeader === 'sticky') { | ||
if (headerPosition === 'top') { | ||
// do nothing - keep at the top | ||
} else if (headerPosition === 'fixed') { | ||
headerStyle.position = 'fixed'; | ||
headerStyle.top = stickyOffset; | ||
groupsStyle.paddingTop = headerStyle.height; | ||
} else if (headerPosition === 'bottom') { | ||
headerStyle.position = 'absolute'; | ||
headerStyle.bottom = 0; | ||
groupsStyle.paddingTop = headerStyle.height; | ||
} | ||
} | ||
var header = _react2.default.createElement( | ||
'div', | ||
{ className: 'rct-sidebar-header', style: headerStyle }, | ||
this.props.children | ||
); | ||
var groupLines = []; | ||
@@ -143,3 +109,2 @@ var i = 0; | ||
}, | ||
header, | ||
_react2.default.createElement( | ||
@@ -161,20 +126,7 @@ 'div', | ||
height: _propTypes2.default.number.isRequired, | ||
lineHeight: _propTypes2.default.number.isRequired, | ||
groupHeights: _propTypes2.default.array.isRequired, | ||
fixedHeader: _propTypes2.default.oneOf(['fixed', 'sticky', 'none']), | ||
stickyOffset: _propTypes2.default.number.isRequired, | ||
headerPosition: _propTypes2.default.oneOf(['top', 'bottom', 'fixed']), | ||
keys: _propTypes2.default.object.isRequired, | ||
groupRenderer: _propTypes2.default.func, | ||
children: _propTypes2.default.node, | ||
isRightSidebar: _propTypes2.default.bool, | ||
headerHeight: _propTypes2.default.number.isRequired | ||
isRightSidebar: _propTypes2.default.bool | ||
}; | ||
Sidebar.defaultProps = { | ||
fixedHeader: 'sticky', | ||
stickyOffset: 0, | ||
headerPosition: 'top', | ||
children: null, | ||
isRightSidebar: false | ||
}; | ||
exports.default = Sidebar; |
@@ -45,28 +45,2 @@ 'use strict'; | ||
_this.touchStart = function (e) { | ||
if (e.touches.length === 1) { | ||
_this.setState({ | ||
touchTarget: e.target || e.touchTarget, | ||
touchActive: true | ||
}); | ||
} | ||
}; | ||
_this.touchEnd = function (e) { | ||
if (!_this.state.touchActive) { | ||
return _this.resetTouchState(); | ||
} | ||
var changedTouches = e.changedTouches[0]; | ||
if (changedTouches) { | ||
var elem = document.elementFromPoint(changedTouches.pageX, changedTouches.pageY); | ||
if (elem !== _this.state.touchTarget) { | ||
return _this.resetTouchState(); | ||
} | ||
} | ||
_this.resetTouchState(); | ||
_this.periodClick(e); | ||
}; | ||
props.registerScroll(function (scrollX) { | ||
@@ -129,15 +103,3 @@ if (scrollX != null) { | ||
} | ||
// TODO: this is broken on touch devices as period click expects | ||
// time and unit. | ||
}, { | ||
key: 'resetTouchState', | ||
value: function resetTouchState() { | ||
this.setState({ | ||
touchTarget: null, | ||
touchActive: false | ||
}); | ||
} | ||
}, { | ||
key: 'shouldComponentUpdate', | ||
@@ -144,0 +106,0 @@ value: function shouldComponentUpdate(nextProps) { |
@@ -43,6 +43,5 @@ 'use strict'; | ||
var left = Math.round((cursorTime - this.props.canvasTimeStart) * ratio); | ||
var top = this.props.headerHeight; | ||
var height = this.props.height - this.props.headerHeight; | ||
var height = this.props.height; | ||
var styles = { | ||
top: top + 'px', | ||
top: '0px', | ||
left: left + 'px', | ||
@@ -69,5 +68,4 @@ height: height + 'px' | ||
cursorTime: _propTypes2.default.number, | ||
headerHeight: _propTypes2.default.number.isRequired, | ||
height: _propTypes2.default.number.isRequired | ||
}; | ||
exports.default = CursorLine; |
@@ -37,3 +37,3 @@ 'use strict'; | ||
value: function shouldComponentUpdate(nextProps) { | ||
return !(nextProps.canvasWidth === this.props.canvasWidth && nextProps.headerHeight === this.props.headerHeight && nextProps.lineCount === this.props.lineCount && nextProps.groupHeights === this.props.groupHeights); | ||
return !(nextProps.canvasWidth === this.props.canvasWidth && nextProps.lineCount === this.props.lineCount && nextProps.groupHeights === this.props.groupHeights); | ||
} | ||
@@ -45,3 +45,2 @@ }, { | ||
canvasWidth = _props.canvasWidth, | ||
headerHeight = _props.headerHeight, | ||
lineCount = _props.lineCount, | ||
@@ -52,3 +51,3 @@ groupHeights = _props.groupHeights; | ||
var totalHeight = headerHeight; | ||
var totalHeight = 0; | ||
for (var i = 0; i < lineCount; i++) { | ||
@@ -81,3 +80,2 @@ lines.push(_react2.default.createElement('div', { | ||
canvasWidth: _propTypes2.default.number.isRequired, | ||
headerHeight: _propTypes2.default.number.isRequired, | ||
lineCount: _propTypes2.default.number.isRequired, | ||
@@ -84,0 +82,0 @@ groupHeights: _propTypes2.default.array.isRequired |
@@ -45,6 +45,5 @@ 'use strict'; | ||
var left = Math.round((currentTime - this.props.canvasTimeStart) * ratio); | ||
var top = this.props.headerHeight; | ||
var height = this.props.height - this.props.headerHeight; | ||
var height = this.props.height; | ||
var styles = { | ||
top: top + 'px', | ||
top: '0px', | ||
left: left + 'px', | ||
@@ -70,3 +69,2 @@ height: height + 'px' | ||
lineCount: _propTypes2.default.number.isRequired, | ||
headerHeight: _propTypes2.default.number.isRequired, | ||
height: _propTypes2.default.number.isRequired | ||
@@ -73,0 +71,0 @@ }; |
@@ -39,3 +39,3 @@ 'use strict'; | ||
value: function shouldComponentUpdate(nextProps) { | ||
return !(nextProps.canvasTimeStart === this.props.canvasTimeStart && nextProps.canvasTimeEnd === this.props.canvasTimeEnd && nextProps.canvasWidth === this.props.canvasWidth && nextProps.lineHeight === this.props.lineHeight && nextProps.lineCount === this.props.lineCount && nextProps.minUnit === this.props.minUnit && nextProps.timeSteps === this.props.timeSteps && nextProps.fixedHeader === this.props.fixedHeader && nextProps.height === this.props.height && nextProps.headerHeight === this.props.headerHeight); | ||
return !(nextProps.canvasTimeStart === this.props.canvasTimeStart && nextProps.canvasTimeEnd === this.props.canvasTimeEnd && nextProps.canvasWidth === this.props.canvasWidth && nextProps.lineHeight === this.props.lineHeight && nextProps.lineCount === this.props.lineCount && nextProps.minUnit === this.props.minUnit && nextProps.timeSteps === this.props.timeSteps && nextProps.height === this.props.height); | ||
} | ||
@@ -45,4 +45,2 @@ }, { | ||
value: function render() { | ||
var _this2 = this; | ||
var _props = this.props, | ||
@@ -54,4 +52,3 @@ canvasTimeStart = _props.canvasTimeStart, | ||
timeSteps = _props.timeSteps, | ||
height = _props.height, | ||
headerHeight = _props.headerHeight; | ||
height = _props.height; | ||
@@ -68,3 +65,3 @@ var ratio = canvasWidth / (canvasTimeEnd - canvasTimeStart); | ||
var labelWidth = Math.ceil((nextTime.valueOf() - time.valueOf()) * ratio) - lineWidth; | ||
var leftPush = _this2.props.fixedHeader !== 'none' && firstOfType ? -1 : 0; | ||
var leftPush = firstOfType ? -1 : 0; | ||
@@ -77,6 +74,6 @@ var classNames = 'rct-vl' + (firstOfType ? ' rct-vl-first' : '') + (minUnit === 'day' || minUnit === 'hour' || minUnit === 'minute' ? ' rct-day-' + time.day() : ''); | ||
style: { | ||
top: headerHeight + 'px', | ||
top: '0px', | ||
left: left + leftPush + 'px', | ||
width: labelWidth + 'px', | ||
height: height - headerHeight + 'px' | ||
height: height + 'px' | ||
} | ||
@@ -105,10 +102,4 @@ })); | ||
timeSteps: _propTypes2.default.object.isRequired, | ||
fixedHeader: _propTypes2.default.string.isRequired, | ||
height: _propTypes2.default.number.isRequired, | ||
headerHeight: _propTypes2.default.number.isRequired | ||
height: _propTypes2.default.number.isRequired | ||
}; | ||
VerticalLines.defaultProps = { | ||
fixedHeader: 'sticky', | ||
dayBackground: null | ||
}; | ||
exports.default = VerticalLines; |
@@ -7,2 +7,4 @@ 'use strict'; | ||
exports.buildDom = buildDom; | ||
exports.sel = sel; | ||
exports.noop = noop; | ||
@@ -13,2 +15,8 @@ var _jsdom = require('jsdom'); | ||
return new _jsdom.JSDOM('<!DOCTYPE html><body>' + domString + '></body>'); | ||
} | ||
} | ||
function sel(selectorString) { | ||
return '[data-test-id="' + selectorString + '"]'; | ||
} | ||
function noop() {} |
@@ -6,3 +6,2 @@ 'use strict'; | ||
}); | ||
exports.defaultSubHeaderLabelFormats = exports.defaultHeaderLabelFormats = exports.defaultTimeSteps = exports.defaultKeys = undefined; | ||
@@ -59,2 +58,6 @@ var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_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"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }(); | ||
var _ScrollElement = require('./scroll/ScrollElement'); | ||
var _ScrollElement2 = _interopRequireDefault(_ScrollElement); | ||
var _window = require('../resize-detector/window'); | ||
@@ -70,2 +73,4 @@ | ||
var _defaultConfig = require('./default-config'); | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
@@ -81,55 +86,2 @@ | ||
var defaultKeys = exports.defaultKeys = { | ||
groupIdKey: 'id', | ||
groupTitleKey: 'title', | ||
groupRightTitleKey: 'rightTitle', | ||
itemIdKey: 'id', | ||
itemTitleKey: 'title', | ||
itemDivTitleKey: 'title', | ||
itemGroupKey: 'group', | ||
itemTimeStartKey: 'start_time', | ||
itemTimeEndKey: 'end_time' | ||
}; | ||
var defaultTimeSteps = exports.defaultTimeSteps = { | ||
second: 1, | ||
minute: 1, | ||
hour: 1, | ||
day: 1, | ||
month: 1, | ||
year: 1 | ||
}; | ||
var defaultHeaderLabelFormats = exports.defaultHeaderLabelFormats = { | ||
yearShort: 'YY', | ||
yearLong: 'YYYY', | ||
monthShort: 'MM/YY', | ||
monthMedium: 'MM/YYYY', | ||
monthMediumLong: 'MMM YYYY', | ||
monthLong: 'MMMM YYYY', | ||
dayShort: 'L', | ||
dayLong: 'dddd, LL', | ||
hourShort: 'HH', | ||
hourMedium: 'HH:00', | ||
hourMediumLong: 'L, HH:00', | ||
hourLong: 'dddd, LL, HH:00', | ||
time: 'LLL' | ||
}; | ||
var defaultSubHeaderLabelFormats = exports.defaultSubHeaderLabelFormats = { | ||
yearShort: 'YY', | ||
yearLong: 'YYYY', | ||
monthShort: 'MM', | ||
monthMedium: 'MMM', | ||
monthLong: 'MMMM', | ||
dayShort: 'D', | ||
dayMedium: 'dd D', | ||
dayMediumLong: 'ddd, Do', | ||
dayLong: 'dddd, Do', | ||
hourShort: 'HH', | ||
hourLong: 'HH:00', | ||
minuteShort: 'mm', | ||
minuteLong: 'HH:mm' | ||
}; | ||
var ReactCalendarTimeline = function (_Component) { | ||
@@ -192,4 +144,2 @@ _inherits(ReactCalendarTimeline, _Component); | ||
headerPosition: 'top', | ||
selectedItem: null, | ||
@@ -199,3 +149,2 @@ dragTime: null, | ||
resizeTime: null, | ||
isDragging: false, | ||
topOffset: 0, | ||
@@ -236,8 +185,2 @@ resizingItem: null, | ||
this.lastTouchDistance = null; | ||
window.addEventListener('scroll', this.scrollEventListener); | ||
this.scrollComponent.addEventListener('touchstart', this.touchStart); | ||
this.scrollComponent.addEventListener('touchmove', this.touchMove); | ||
this.scrollComponent.addEventListener('touchend', this.touchEnd); | ||
} | ||
@@ -252,12 +195,3 @@ }, { | ||
_window2.default.removeListener(this); | ||
window.removeEventListener('scroll', this.scrollEventListener); | ||
this.scrollComponent.removeEventListener('touchstart', this.touchStart); | ||
this.scrollComponent.removeEventListener('touchmove', this.touchMove); | ||
this.scrollComponent.removeEventListener('touchend', this.touchEnd); | ||
} | ||
// called on window scroll. it's job is to figure out if we should fix or float the header | ||
}, { | ||
@@ -305,16 +239,2 @@ key: 'componentWillReceiveProps', | ||
}, { | ||
key: 'zoomIn', | ||
value: function zoomIn(e) { | ||
e.preventDefault(); | ||
this.changeZoom(0.75); | ||
} | ||
}, { | ||
key: 'zoomOut', | ||
value: function zoomOut(e) { | ||
e.preventDefault(); | ||
this.changeZoom(1.25); | ||
} | ||
}, { | ||
key: 'changeZoom', | ||
@@ -376,3 +296,2 @@ value: function changeZoom(scale) { | ||
timeSteps: timeSteps, | ||
fixedHeader: this.props.fixedHeader, | ||
height: height, | ||
@@ -425,3 +344,4 @@ headerHeight: headerHeight | ||
itemRenderer: this.props.itemRenderer, | ||
selected: this.props.selected | ||
selected: this.props.selected, | ||
minimumWidthForItemContentVisibility: this.props.minimumWidthForItemContentVisibility | ||
}); | ||
@@ -445,2 +365,24 @@ } | ||
value: function header(canvasTimeStart, zoom, canvasTimeEnd, canvasWidth, minUnit, timeSteps, headerLabelGroupHeight, headerLabelHeight) { | ||
var _props2 = this.props, | ||
sidebarWidth = _props2.sidebarWidth, | ||
rightSidebarWidth = _props2.rightSidebarWidth; | ||
var leftSidebar = sidebarWidth != null && sidebarWidth > 0 && _react2.default.createElement( | ||
'div', | ||
{ | ||
className: 'rct-sidebar-header', | ||
style: { width: this.props.sidebarWidth } | ||
}, | ||
this.props.sidebarContent | ||
); | ||
var rightSidebar = rightSidebarWidth != null && rightSidebarWidth > 0 && _react2.default.createElement( | ||
'div', | ||
{ | ||
className: 'rct-sidebar-header rct-sidebar-right', | ||
style: { width: this.props.rightSidebarWidth } | ||
}, | ||
this.props.rightSidebarContent | ||
); | ||
return _react2.default.createElement(_Header2.default, { | ||
@@ -460,51 +402,48 @@ canvasTimeStart: canvasTimeStart, | ||
visibleTimeEnd: this.state.visibleTimeEnd, | ||
headerPosition: this.state.headerPosition, | ||
fixedHeader: this.props.fixedHeader, | ||
stickyOffset: this.props.stickyOffset, | ||
stickyHeader: this.props.stickyHeader, | ||
showPeriod: this.showPeriod, | ||
headerLabelFormats: this.props.headerLabelFormats, | ||
subHeaderLabelFormats: this.props.subHeaderLabelFormats | ||
subHeaderLabelFormats: this.props.subHeaderLabelFormats, | ||
registerScroll: this.registerScrollListener, | ||
leftSidebarHeader: leftSidebar, | ||
rightSidebarHeader: rightSidebar, | ||
headerRef: this.props.headerRef | ||
}); | ||
} | ||
}, { | ||
key: 'componentDidUpdate', | ||
value: function componentDidUpdate() { | ||
this.headerScrollListener(this.state.currentScrollLeft); | ||
} | ||
}, { | ||
key: 'sidebar', | ||
value: function sidebar(height, groupHeights, headerHeight) { | ||
return _react2.default.createElement( | ||
_Sidebar2.default, | ||
{ | ||
groups: this.props.groups, | ||
groupRenderer: this.props.groupRenderer, | ||
keys: this.props.keys, | ||
width: this.props.sidebarWidth, | ||
lineHeight: this.props.lineHeight, | ||
groupHeights: groupHeights, | ||
height: height, | ||
headerHeight: headerHeight, | ||
headerPosition: this.state.headerPosition, | ||
stickyOffset: this.props.stickyOffset, | ||
fixedHeader: this.props.fixedHeader | ||
}, | ||
this.props.sidebarContent | ||
); | ||
value: function sidebar(height, groupHeights) { | ||
var sidebarWidth = this.props.sidebarWidth; | ||
return sidebarWidth != null && sidebarWidth > 0 && _react2.default.createElement(_Sidebar2.default, { | ||
groups: this.props.groups, | ||
groupRenderer: this.props.groupRenderer, | ||
keys: this.props.keys, | ||
width: this.props.sidebarWidth, | ||
lineHeight: this.props.lineHeight, | ||
groupHeights: groupHeights, | ||
height: height | ||
}); | ||
} | ||
}, { | ||
key: 'rightSidebar', | ||
value: function rightSidebar(height, groupHeights, headerHeight) { | ||
return _react2.default.createElement( | ||
_Sidebar2.default, | ||
{ | ||
groups: this.props.groups, | ||
keys: this.props.keys, | ||
isRightSidebar: true, | ||
width: this.props.rightSidebarWidth, | ||
lineHeight: this.props.lineHeight, | ||
groupHeights: groupHeights, | ||
height: height, | ||
headerHeight: headerHeight, | ||
headerPosition: this.state.headerPosition, | ||
stickyOffset: this.props.stickyOffset, | ||
fixedHeader: this.props.fixedHeader | ||
}, | ||
this.props.rightSidebarContent | ||
); | ||
value: function rightSidebar(height, groupHeights) { | ||
var rightSidebarWidth = this.props.rightSidebarWidth; | ||
return rightSidebarWidth != null && rightSidebarWidth > 0 && _react2.default.createElement(_Sidebar2.default, { | ||
groups: this.props.groups, | ||
keys: this.props.keys, | ||
isRightSidebar: true, | ||
width: this.props.rightSidebarWidth, | ||
lineHeight: this.props.lineHeight, | ||
groupHeights: groupHeights, | ||
height: height | ||
}); | ||
} | ||
@@ -524,11 +463,10 @@ }, { | ||
var _props2 = this.props, | ||
keys = _props2.keys, | ||
dragSnap = _props2.dragSnap, | ||
lineHeight = _props2.lineHeight, | ||
headerLabelGroupHeight = _props2.headerLabelGroupHeight, | ||
headerLabelHeight = _props2.headerLabelHeight, | ||
stackItems = _props2.stackItems, | ||
fullUpdate = _props2.fullUpdate, | ||
itemHeightRatio = _props2.itemHeightRatio; | ||
var _props3 = this.props, | ||
keys = _props3.keys, | ||
dragSnap = _props3.dragSnap, | ||
lineHeight = _props3.lineHeight, | ||
headerLabelGroupHeight = _props3.headerLabelGroupHeight, | ||
headerLabelHeight = _props3.headerLabelHeight, | ||
stackItems = _props3.stackItems, | ||
itemHeightRatio = _props3.itemHeightRatio; | ||
var _state2 = this.state, | ||
@@ -566,6 +504,3 @@ draggingItem = _state2.draggingItem, | ||
resizingEdge: resizingEdge, | ||
resizeTime: resizeTime, | ||
fullUpdate: fullUpdate, | ||
visibleTimeStart: visibleTimeStart, | ||
visibleTimeEnd: visibleTimeEnd | ||
resizeTime: resizeTime | ||
}); | ||
@@ -639,15 +574,16 @@ | ||
var _props3 = this.props, | ||
items = _props3.items, | ||
groups = _props3.groups, | ||
headerLabelGroupHeight = _props3.headerLabelGroupHeight, | ||
headerLabelHeight = _props3.headerLabelHeight, | ||
sidebarWidth = _props3.sidebarWidth, | ||
rightSidebarWidth = _props3.rightSidebarWidth, | ||
timeSteps = _props3.timeSteps, | ||
showCursorLine = _props3.showCursorLine; | ||
var _props4 = this.props, | ||
items = _props4.items, | ||
groups = _props4.groups, | ||
headerLabelGroupHeight = _props4.headerLabelGroupHeight, | ||
headerLabelHeight = _props4.headerLabelHeight, | ||
sidebarWidth = _props4.sidebarWidth, | ||
rightSidebarWidth = _props4.rightSidebarWidth, | ||
timeSteps = _props4.timeSteps, | ||
showCursorLine = _props4.showCursorLine, | ||
clickTolerance = _props4.clickTolerance, | ||
traditionalZoom = _props4.traditionalZoom; | ||
var _state3 = this.state, | ||
draggingItem = _state3.draggingItem, | ||
resizingItem = _state3.resizingItem, | ||
isDragging = _state3.isDragging, | ||
width = _state3.width, | ||
@@ -672,3 +608,5 @@ visibleTimeStart = _state3.visibleTimeStart, | ||
if (draggingItem || resizingItem) { | ||
var isInteractingWithItem = !!draggingItem || !!resizingItem; | ||
if (isInteractingWithItem) { | ||
var stackResults = this.stackItems(items, groups, canvasTimeStart, visibleTimeStart, visibleTimeEnd, width); | ||
@@ -685,8 +623,2 @@ dimensionItems = stackResults.dimensionItems; | ||
var scrollComponentStyle = { | ||
width: width + 'px', | ||
height: height + 20 + 'px', | ||
cursor: isDragging ? 'move' : 'default' | ||
}; | ||
var canvasComponentStyle = { | ||
@@ -706,20 +638,26 @@ width: canvasWidth + 'px', | ||
}, | ||
this.header(canvasTimeStart, zoom, canvasTimeEnd, canvasWidth, minUnit, timeSteps, headerLabelGroupHeight, headerLabelHeight), | ||
_react2.default.createElement( | ||
'div', | ||
{ style: outerComponentStyle, className: 'rct-outer' }, | ||
sidebarWidth > 0 ? this.sidebar(height, groupHeights, headerHeight) : null, | ||
sidebarWidth > 0 ? this.sidebar(height, groupHeights) : null, | ||
_react2.default.createElement( | ||
'div', | ||
_ScrollElement2.default, | ||
{ | ||
ref: function ref(el) { | ||
scrollRef: function scrollRef(el) { | ||
return _this3.scrollComponent = el; | ||
}, | ||
className: 'rct-scroll', | ||
style: scrollComponentStyle, | ||
width: width, | ||
height: height, | ||
clickTolerance: clickTolerance, | ||
onWheelZoom: this.handleWheelZoom, | ||
traditionalZoom: traditionalZoom, | ||
onClick: this.scrollAreaClick, | ||
onScroll: this.onScroll, | ||
onWheel: this.onWheel, | ||
onMouseDown: this.handleMouseDown, | ||
onMouseMove: this.handleMouseMove, | ||
onMouseUp: this.handleMouseUp, | ||
onMouseLeave: this.handleMouseLeave | ||
isInteractingWithItem: isInteractingWithItem, | ||
onDoubleClick: this.handleScrollDoubleClick, | ||
onMouseEnter: this.handleScrollMouseEnter, | ||
onMouseLeave: this.handleScrollMouseLeave, | ||
onMouseMove: this.handleScrollMouseMove, | ||
onContextMenu: this.handleScrollContextMenu | ||
}, | ||
@@ -733,8 +671,3 @@ _react2.default.createElement( | ||
className: 'rct-canvas', | ||
style: canvasComponentStyle, | ||
onDoubleClick: this.handleDoubleClick, | ||
onMouseEnter: this.handleCanvasMouseEnter, | ||
onMouseLeave: this.handleCanvasMouseLeave, | ||
onMouseMove: this.handleCanvasMouseMove, | ||
onContextMenu: this.handleCanvasContextMenu | ||
style: canvasComponentStyle | ||
}, | ||
@@ -745,3 +678,2 @@ this.items(canvasTimeStart, zoom, canvasTimeEnd, canvasWidth, minUnit, dimensionItems, groupHeights, groupTops), | ||
this.todayLine(canvasTimeStart, zoom, canvasTimeEnd, canvasWidth, minUnit, height, headerHeight), | ||
this.header(canvasTimeStart, zoom, canvasTimeEnd, canvasWidth, minUnit, timeSteps, headerLabelGroupHeight, headerLabelHeight), | ||
mouseOverCanvas && showCursorLine ? this.cursorLine(cursorTime, canvasTimeStart, zoom, canvasTimeEnd, canvasWidth, minUnit, height, headerHeight) : null, | ||
@@ -752,3 +684,3 @@ this.infoLabel(), | ||
), | ||
rightSidebarWidth > 0 ? this.rightSidebar(height, groupHeights, headerHeight) : null | ||
rightSidebarWidth > 0 ? this.rightSidebar(height, groupHeights) : null | ||
) | ||
@@ -771,5 +703,4 @@ ); | ||
minResizeWidth: _propTypes2.default.number, | ||
fixedHeader: _propTypes2.default.oneOf(['fixed', 'sticky', 'none']), | ||
stickyOffset: _propTypes2.default.number, | ||
fullUpdate: _propTypes2.default.bool, | ||
stickyHeader: _propTypes2.default.bool, | ||
lineHeight: _propTypes2.default.number, | ||
@@ -779,2 +710,3 @@ headerLabelGroupHeight: _propTypes2.default.number, | ||
itemHeightRatio: _propTypes2.default.number, | ||
minimumWidthForItemContentVisibility: _propTypes2.default.number, | ||
@@ -834,2 +766,3 @@ minZoom: _propTypes2.default.number, | ||
}), | ||
headerRef: _propTypes2.default.func, | ||
@@ -899,5 +832,4 @@ timeSteps: _propTypes2.default.shape({ | ||
minResizeWidth: 20, | ||
fixedHeader: 'sticky', // fixed or sticky or none | ||
stickyOffset: 0, | ||
fullUpdate: true, | ||
stickyHeader: true, | ||
lineHeight: 30, | ||
@@ -907,2 +839,3 @@ headerLabelGroupHeight: 30, | ||
itemHeightRatio: 0.65, | ||
minimumWidthForItemContentVisibility: 25, | ||
@@ -948,4 +881,5 @@ minZoom: 60 * 60 * 1000, // 1 hour | ||
style: {}, | ||
keys: defaultKeys, | ||
timeSteps: defaultTimeSteps, | ||
keys: _defaultConfig.defaultKeys, | ||
timeSteps: _defaultConfig.defaultTimeSteps, | ||
headerRef: function headerRef() {}, | ||
@@ -965,4 +899,4 @@ // if you pass in visibleTimeStart and visibleTimeEnd, you must also pass onTimeChange(visibleTimeStart, visibleTimeEnd), | ||
headerLabelFormats: defaultHeaderLabelFormats, | ||
subHeaderLabelFormats: defaultSubHeaderLabelFormats, | ||
headerLabelFormats: _defaultConfig.defaultHeaderLabelFormats, | ||
subHeaderLabelFormats: _defaultConfig.defaultSubHeaderLabelFormats, | ||
@@ -995,96 +929,2 @@ selected: null | ||
this.scrollEventListener = function () { | ||
var _props4 = _this4.props, | ||
headerLabelGroupHeight = _props4.headerLabelGroupHeight, | ||
headerLabelHeight = _props4.headerLabelHeight; | ||
var headerHeight = headerLabelGroupHeight + headerLabelHeight; | ||
var rect = _this4.container.getBoundingClientRect(); | ||
if (rect.top > _this4.props.stickyOffset) { | ||
_this4.setState({ headerPosition: 'top' }); | ||
} else if (rect.bottom < headerHeight + _this4.props.stickyOffset) { | ||
_this4.setState({ headerPosition: 'bottom' }); | ||
} else { | ||
_this4.setState({ headerPosition: 'fixed' }); | ||
} | ||
}; | ||
this.touchStart = function (e) { | ||
if (e.touches.length === 2) { | ||
e.preventDefault(); | ||
_this4.lastTouchDistance = Math.abs(e.touches[0].screenX - e.touches[1].screenX); | ||
_this4.singleTouchStart = null; | ||
_this4.lastSingleTouch = null; | ||
} else if (e.touches.length === 1) { | ||
e.preventDefault(); | ||
var x = e.touches[0].clientX; | ||
var y = e.touches[0].clientY; | ||
_this4.lastTouchDistance = null; | ||
_this4.singleTouchStart = { x: x, y: y, screenY: window.pageYOffset }; | ||
_this4.lastSingleTouch = { x: x, y: y, screenY: window.pageYOffset }; | ||
} | ||
}; | ||
this.touchMove = function (e) { | ||
if (_this4.state.dragTime || _this4.state.resizeTime) { | ||
e.preventDefault(); | ||
return; | ||
} | ||
if (_this4.lastTouchDistance && e.touches.length === 2) { | ||
e.preventDefault(); | ||
var touchDistance = Math.abs(e.touches[0].screenX - e.touches[1].screenX); | ||
var parentPosition = (0, _domHelpers.getParentPosition)(e.currentTarget); | ||
var xPosition = (e.touches[0].screenX + e.touches[1].screenX) / 2 - parentPosition.x; | ||
if (touchDistance !== 0 && _this4.lastTouchDistance !== 0) { | ||
_this4.changeZoom(_this4.lastTouchDistance / touchDistance, xPosition / _this4.state.width); | ||
_this4.lastTouchDistance = touchDistance; | ||
} | ||
} else if (_this4.lastSingleTouch && e.touches.length === 1) { | ||
e.preventDefault(); | ||
var x = e.touches[0].clientX; | ||
var y = e.touches[0].clientY; | ||
var deltaX = x - _this4.lastSingleTouch.x; | ||
// let deltaY = y - this.lastSingleTouch.y | ||
var deltaX0 = x - _this4.singleTouchStart.x; | ||
var deltaY0 = y - _this4.singleTouchStart.y; | ||
_this4.lastSingleTouch = { x: x, y: y }; | ||
var moveX = Math.abs(deltaX0) * 3 > Math.abs(deltaY0); | ||
var moveY = Math.abs(deltaY0) * 3 > Math.abs(deltaX0); | ||
if (deltaX !== 0 && moveX) { | ||
_this4.scrollComponent.scrollLeft -= deltaX; | ||
} | ||
if (moveY) { | ||
window.scrollTo(window.pageXOffset, _this4.singleTouchStart.screenY - deltaY0); | ||
} | ||
} | ||
}; | ||
this.touchEnd = function (e) { | ||
if (_this4.lastTouchDistance) { | ||
e.preventDefault(); | ||
_this4.lastTouchDistance = null; | ||
} | ||
if (_this4.lastSingleTouch) { | ||
e.preventDefault(); | ||
_this4.lastSingleTouch = null; | ||
_this4.singleTouchStart = null; | ||
} | ||
}; | ||
this.resize = function () { | ||
@@ -1116,6 +956,5 @@ var props = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : _this4.props; | ||
this.onScroll = function () { | ||
var scrollComponent = _this4.scrollComponent; | ||
this.onScroll = function (scrollX) { | ||
var canvasTimeStart = _this4.state.canvasTimeStart; | ||
var scrollX = scrollComponent.scrollLeft; | ||
var zoom = _this4.state.visibleTimeEnd - _this4.state.visibleTimeStart; | ||
@@ -1125,3 +964,2 @@ var width = _this4.state.width; | ||
// move the virtual canvas if needed | ||
if (scrollX < _this4.state.width * 0.5) { | ||
@@ -1131,3 +969,2 @@ _this4.setState({ | ||
}); | ||
scrollComponent.scrollLeft += _this4.state.width; | ||
} | ||
@@ -1138,3 +975,2 @@ if (scrollX > _this4.state.width * 1.5) { | ||
}); | ||
scrollComponent.scrollLeft -= _this4.state.width; | ||
} | ||
@@ -1145,2 +981,6 @@ | ||
} | ||
_this4.setState({ | ||
currentScrollLeft: scrollX | ||
}); | ||
}; | ||
@@ -1154,5 +994,3 @@ | ||
var groups = updatedGroups || _this4.props.groups; | ||
var fullUpdate = _this4.props.fullUpdate; | ||
var newState = { | ||
@@ -1188,6 +1026,6 @@ visibleTimeStart: visibleTimeStart, | ||
if (resetCanvas || forceUpdateDimensions || fullUpdate) { | ||
if (resetCanvas || forceUpdateDimensions) { | ||
var canvasTimeStart = newState.canvasTimeStart ? newState.canvasTimeStart : oldCanvasTimeStart; | ||
var _stackItems3 = _this4.stackItems(items, groups, canvasTimeStart, visibleTimeStart, visibleTimeEnd, _this4.state.width, fullUpdate), | ||
var _stackItems3 = _this4.stackItems(items, groups, canvasTimeStart, visibleTimeStart, visibleTimeEnd, _this4.state.width), | ||
dimensionItems = _stackItems3.dimensionItems, | ||
@@ -1214,45 +1052,6 @@ height = _stackItems3.height, | ||
this.zoomWithWheel = function (speed, xPosition, deltaY) { | ||
this.handleWheelZoom = function (speed, xPosition, deltaY) { | ||
_this4.changeZoom(1.0 + speed * deltaY / 500, xPosition / _this4.state.width); | ||
}; | ||
this.onWheel = function (e) { | ||
var traditionalZoom = _this4.props.traditionalZoom; | ||
e.preventDefault(); | ||
// zoom in the time dimension | ||
if (e.ctrlKey || e.metaKey || e.altKey) { | ||
var parentPosition = (0, _domHelpers.getParentPosition)(e.currentTarget); | ||
var xPosition = e.clientX - parentPosition.x; | ||
var speed = e.ctrlKey ? 10 : e.metaKey ? 3 : 1; | ||
_this4.zoomWithWheel(speed, xPosition, e.deltaY); | ||
// convert vertical zoom to horiziontal | ||
} else if (e.shiftKey) { | ||
var scrollComponent = _this4.scrollComponent; | ||
scrollComponent.scrollLeft += e.deltaY; | ||
// no modifier pressed? we prevented the default event, so scroll or zoom as needed | ||
} else { | ||
if (e.deltaX !== 0) { | ||
if (!traditionalZoom) { | ||
_this4.scrollComponent.scrollLeft += e.deltaX; | ||
} | ||
} | ||
if (e.deltaY !== 0) { | ||
window.scrollTo(window.pageXOffset, window.pageYOffset + e.deltaY); | ||
if (traditionalZoom) { | ||
var _parentPosition = (0, _domHelpers.getParentPosition)(e.currentTarget); | ||
var _xPosition = e.clientX - _parentPosition.x; | ||
_this4.zoomWithWheel(10, _xPosition, e.deltaY); | ||
} | ||
} | ||
} | ||
}; | ||
this.showPeriod = function (from, unit) { | ||
@@ -1430,51 +1229,3 @@ var visibleTimeStart = from.valueOf(); | ||
this.handleMouseDown = function (e) { | ||
var topOffset = _this4.state.topOffset; | ||
var pageY = e.pageY; | ||
var _props6 = _this4.props, | ||
headerLabelGroupHeight = _props6.headerLabelGroupHeight, | ||
headerLabelHeight = _props6.headerLabelHeight; | ||
var headerHeight = headerLabelGroupHeight + headerLabelHeight; | ||
if (pageY - topOffset > headerHeight && e.button === 0) { | ||
_this4.setState({ | ||
isDragging: true, | ||
dragStartPosition: e.pageX, | ||
dragLastPosition: e.pageX | ||
}); | ||
} | ||
}; | ||
this.handleMouseMove = function (e) { | ||
if (_this4.state.isDragging && !_this4.state.draggingItem && !_this4.state.resizingItem) { | ||
_this4.scrollComponent.scrollLeft += _this4.state.dragLastPosition - e.pageX; | ||
_this4.setState({ dragLastPosition: e.pageX }); | ||
} | ||
}; | ||
this.handleMouseUp = function (e) { | ||
var dragStartPosition = _this4.state.dragStartPosition; | ||
if (Math.abs(dragStartPosition - e.pageX) <= _this4.props.clickTolerance) { | ||
_this4.scrollAreaClick(e); | ||
} | ||
_this4.setState({ | ||
isDragging: false, | ||
dragStartPosition: null, | ||
dragLastPosition: null | ||
}); | ||
}; | ||
this.handleMouseLeave = function () { | ||
_this4.setState({ | ||
isDragging: false, | ||
dragStartPosition: null, | ||
dragLastPosition: null | ||
}); | ||
}; | ||
this.handleCanvasMouseEnter = function (e) { | ||
this.handleScrollMouseEnter = function (e) { | ||
var showCursorLine = _this4.props.showCursorLine; | ||
@@ -1491,3 +1242,3 @@ | ||
this.handleCanvasMouseLeave = function (e) { | ||
this.handleScrollMouseLeave = function (e) { | ||
var showCursorLine = _this4.props.showCursorLine; | ||
@@ -1504,3 +1255,3 @@ | ||
this.handleCanvasMouseMove = function (e) { | ||
this.handleScrollMouseMove = function (e) { | ||
var showCursorLine = _this4.props.showCursorLine; | ||
@@ -1536,3 +1287,7 @@ var _state8 = _this4.state, | ||
this.handleDoubleClick = function (e) { | ||
this.registerScrollListener = function (listener) { | ||
_this4.headerScrollListener = listener; | ||
}; | ||
this.handleScrollDoubleClick = function (e) { | ||
var _state9 = _this4.state, | ||
@@ -1602,3 +1357,3 @@ canvasTimeStart = _state9.canvasTimeStart, | ||
this.handleCanvasContextMenu = function (e) { | ||
this.handleScrollContextMenu = function (e) { | ||
var _state10 = _this4.state, | ||
@@ -1605,0 +1360,0 @@ canvasTimeStart = _state10.canvasTimeStart, |
@@ -143,6 +143,3 @@ 'use strict'; | ||
resizingEdge = _ref.resizingEdge, | ||
resizeTime = _ref.resizeTime, | ||
fullUpdate = _ref.fullUpdate, | ||
visibleTimeStart = _ref.visibleTimeStart, | ||
visibleTimeEnd = _ref.visibleTimeEnd; | ||
resizeTime = _ref.resizeTime; | ||
@@ -168,25 +165,2 @@ var itemStart = isResizing && resizingEdge === 'left' ? resizeTime : itemTimeStart; | ||
var clippedLeft = false; | ||
var clippedRight = false; | ||
if (fullUpdate) { | ||
if (!isDragging && (visibleTimeStart > x + w || visibleTimeEnd < x)) { | ||
return null; | ||
} | ||
if (visibleTimeStart > x) { | ||
w -= visibleTimeStart - x; | ||
x = visibleTimeStart; | ||
if (isDragging && w < 0) { | ||
x += w; | ||
w = 0; | ||
} | ||
clippedLeft = true; | ||
} | ||
if (x + w > visibleTimeEnd) { | ||
w -= x + w - visibleTimeEnd; | ||
clippedRight = true; | ||
} | ||
} | ||
var ratio = 1 / coordinateToTimeRatio(canvasTimeStart, canvasTimeEnd, canvasWidth); | ||
@@ -199,5 +173,3 @@ | ||
originalLeft: itemTimeStart, | ||
collisionWidth: collisionW, | ||
clippedLeft: clippedLeft, | ||
clippedRight: clippedRight | ||
collisionWidth: collisionW | ||
}; | ||
@@ -259,5 +231,5 @@ | ||
function stack(items, groupOrders, lineHeight, headerHeight, force) { | ||
function stack(items, groupOrders, lineHeight, force) { | ||
var i, iMax; | ||
var totalHeight = headerHeight; | ||
var totalHeight = 0; | ||
@@ -320,6 +292,6 @@ var groupHeights = []; | ||
function nostack(items, groupOrders, lineHeight, headerHeight, force) { | ||
function nostack(items, groupOrders, lineHeight, force) { | ||
var i, iMax; | ||
var totalHeight = headerHeight; | ||
var totalHeight = 0; | ||
@@ -326,0 +298,0 @@ var groupHeights = []; |
{ | ||
"name": "react-calendar-timeline", | ||
"version": "0.15.11", | ||
"version": "0.16.0-beta.2", | ||
"description": "react calendar timeline", | ||
@@ -75,2 +75,3 @@ "main": "lib/index.js", | ||
"babel-plugin-add-module-exports": "^0.2.1", | ||
"babel-plugin-react-remove-properties": "^0.2.5", | ||
"babel-plugin-transform-class-properties": "^6.24.1", | ||
@@ -77,0 +78,0 @@ "babel-plugin-transform-object-rest-spread": "^6.26.0", |
252
README.md
@@ -41,11 +41,26 @@ # React Calendar Timeline | ||
const groups = [ | ||
{id: 1, title: 'group 1'}, | ||
{id: 2, title: 'group 2'} | ||
] | ||
const groups = [{ id: 1, title: 'group 1' }, { id: 2, title: 'group 2' }] | ||
const items = [ | ||
{id: 1, group: 1, title: 'item 1', start_time: moment(), end_time: moment().add(1, 'hour')}, | ||
{id: 2, group: 2, title: 'item 2', start_time: moment().add(-0.5, 'hour'), end_time: moment().add(0.5, 'hour')}, | ||
{id: 3, group: 1, title: 'item 3', start_time: moment().add(2, 'hour'), end_time: moment().add(3, 'hour')} | ||
{ | ||
id: 1, | ||
group: 1, | ||
title: 'item 1', | ||
start_time: moment(), | ||
end_time: moment().add(1, 'hour') | ||
}, | ||
{ | ||
id: 2, | ||
group: 2, | ||
title: 'item 2', | ||
start_time: moment().add(-0.5, 'hour'), | ||
end_time: moment().add(0.5, 'hour') | ||
}, | ||
{ | ||
id: 3, | ||
group: 1, | ||
title: 'item 3', | ||
start_time: moment().add(2, 'hour'), | ||
end_time: moment().add(3, 'hour') | ||
} | ||
] | ||
@@ -56,19 +71,23 @@ | ||
Rendered by react! | ||
<Timeline groups={groups} | ||
items={items} | ||
defaultTimeStart={moment().add(-12, 'hour')} | ||
defaultTimeEnd={moment().add(12, 'hour')} | ||
/> | ||
<Timeline | ||
groups={groups} | ||
items={items} | ||
defaultTimeStart={moment().add(-12, 'hour')} | ||
defaultTimeEnd={moment().add(12, 'hour')} | ||
/> | ||
</div>, | ||
document.getElementById('root') | ||
); | ||
) | ||
``` | ||
## API | ||
*NB!* All props need to be immutable. For example, this means if you wish to change the title of one of your items, please pass in a whole new items array instead of changing the title in the old array. [Here's more info.](http://reactkungfu.com/2015/08/pros-and-cons-of-using-immutability-with-react-js/) | ||
_NB!_ All props need to be immutable. For example, this means if you wish to change the title of one of your items, please pass in a whole new items array instead of changing the title in the old array. [Here's more info.](http://reactkungfu.com/2015/08/pros-and-cons-of-using-immutability-with-react-js/) | ||
The component can take many props: | ||
### groups | ||
Expects either a vanilla JS array or an immutableJS array, consisting of objects with the following attributes: | ||
```js | ||
@@ -85,3 +104,5 @@ { | ||
### items | ||
Expects either a vanilla JS array or an immutableJS array, consisting of objects with the following attributes: | ||
```js | ||
@@ -110,6 +131,9 @@ { | ||
### selected | ||
An array with id's corresponding to id's in items (`item.id`). If this prop is set you have to manage the selected items yourself within the `onItemSelect` handler to update the property with new id's. This overwrites the default behaviour of selecting one item on click. | ||
### keys | ||
An array specifying keys in the `items` and `groups` objects. Defaults to | ||
```js | ||
@@ -130,89 +154,104 @@ { | ||
### sidebarWidth | ||
Width of the sidebar in pixels. If set to `0`, the sidebar is not rendered. Defaults to `150`. | ||
### sidebarContent | ||
Everything passed here will be displayed above the left sidebar. Use this to display small filters or so. Defaults to `null`. | ||
### rightSidebarWidth | ||
Width of the right sidebar in pixels. If set to `0`, the right sidebar is not rendered. Defaults to `0`. | ||
### rightSidebarContent | ||
Everything passed here will be displayed above the right sidebar. Use this to display small filters or so. Defaults to `null`. | ||
### dragSnap | ||
Snapping unit when dragging items. Defaults to `15 * 60 * 1000` or 15min. When so, the items will snap to 15min intervals when dragging. | ||
### minResizeWidth | ||
The minimum width, in pixels, of a timeline entry when it's possible to resize. If not reached, you must zoom in to resize more. Default to `20`. | ||
### fixedHeader | ||
How does the header (the scrolling part with dates) behave if not all of the groups fit on the page, resulting in a vertical scrollbar. | ||
### stickyOffset | ||
* `fixed` - the header is always fixed to its initial position | ||
* `sticky` (default) - the header follows the scroll of the page to be always visible | ||
* `none` - the header is always at the top of the component and doesn't stick with scrolling | ||
At what height from the top of the screen should we start "sticking" the header (i.e. position: sticky)? This is useful if for example you already have | ||
a sticky navbar and want to push the timeline header down further. Defaults `0`. | ||
### stickyOffset | ||
If fixedHeader is `sticky`, at what height from the top of the screen should we start floating it? This is useful if for example you already have | ||
a sticky navbar. Defaults `0`. | ||
### stickyHeader | ||
### fullUpdate | ||
If your calendar has large items compared to the zoom level (e.g. multi week events when viewing one day at a time), set this to `true` (default). | ||
Specify whether you want the timeline header to be "sticky". Pass `false` if you want the header to fix at top of element and not fix when you scroll down the page. Defaults to `true` | ||
If you have many small events compared to the zoom level (e.g. hundreds of 30min events and viewing one week at a time), set this to `false`. | ||
### headerRef | ||
When set to `true` we update the dimensions of the items on every scroll event. This looks nicer, as 1) item labels | ||
are always fully on the screen, even if the start or end of the items is off screen, 2) item stacking also reflects what's on the screen. | ||
Ref callback that gets a DOM reference to the header element. See [FAQ below](#the-timeline-header-doesnt-fix-to-the-top-of-the-container-when-i-scroll-down). | ||
When set to `false`, we update the dimensions of the items only when the [scrolling canvas](https://github.com/namespace-ee/react-calendar-timeline#behind-the-scenes) updates. This makes scrolling much faster, but labels can go off screen. | ||
### lineHeight | ||
### lineHeight | ||
Height of one line in the calendar in pixels. Default `30` | ||
### headerLabelGroupHeight | ||
Height of the top header line. Default `30` | ||
### headerLabelHeight | ||
Height of the bottom header line. Default `30` | ||
### itemHeightRatio | ||
What percentage of the height of the line is taken by the item? Default `0.65` | ||
### minZoom | ||
Smallest time the calendar can zoom to in milliseconds. Default `60 * 60 * 1000` (1 hour) | ||
### maxZoom | ||
Largest time the calendar can zoom to in milliseconds. Default `5 * 365.24 * 86400 * 1000` (5 years) | ||
### clickTolerance | ||
How many pixels we can drag the background for it to be counted as a click on the background. Defualt: `3` | ||
### canMove | ||
Can items be dragged around? Can be overridden in the `items` array. Defaults to `true` | ||
### canChangeGroup | ||
Can items be moved between groups? Can be overridden in the `items` array. Defaults to `true` | ||
### canResize | ||
Can items be resized? Can be overridden in the `items` array. Accepted values: `false`, `"left"`, `"right"`, `"both"`. Defaults to `"right"`. If you pass `true`, it will be treated as `"right"` to not break compatibility with versions 0.9 and below. | ||
### useResizeHandle | ||
Append a special `.rct-drag-right` handle to the elements and only resize if dragged from there. Defaults to `false` | ||
### showCursorLine | ||
Show a vertical line at the snap point when you mouse over the calendar | ||
### stackItems | ||
Stack items under each other, so there is no visual overlap when times collide. Defaults to `false`. | ||
### traditionalZoom | ||
Zoom in when scrolling the mouse up/down. Defaults to `false` | ||
### itemTouchSendsClick | ||
Normally tapping (touching) an item selects it. If this is set to true, a tap will have the same effect, as selecting with the first click and then clicking again to open and send the onItemClick event. Defaults to `false`. | ||
### timeSteps | ||
With what step to display different units. E.g. `15` for `minute` means only minutes 0, 15, 30 and 45 will be shown. | ||
Default: | ||
```js | ||
@@ -230,32 +269,43 @@ { | ||
### onItemMove(itemId, dragTime, newGroupOrder) | ||
Callback when an item is moved. Returns 1) the item's ID, 2) the new start time and 3) the index of the new group in the `groups` array. | ||
### onItemResize(itemId, time, edge) | ||
Callback when an item is resized. Returns 1) the item's ID, 2) the new start or end time of the item 3) The edge that was dragged (`left` or `right`) | ||
### onItemSelect(itemId, e, time) | ||
Called when an item is selected. This is sent on the first click on an item. `time` is the time that corresponds to where you click/select on the item in the timeline. | ||
### onItemClick(itemId, e, time) | ||
Called when an item is clicked. Note: the item must be selected before it's clicked... except if it's a touch event and `itemTouchSendsClick` is enabled. `time` is the time that corresponds to where you click on the item in the timeline. | ||
### onItemDoubleClick(itemId, e, time) | ||
Called when an item was double clicked. `time` is the time that corresponds to where you double click on the item in the timeline. | ||
### onItemContextMenu(itemId, e, time) | ||
Called when the item is clicked by the right button of the mouse. `time` is the time that corresponds to where you context click on the item in the timeline. Note: If this property is set the default context menu doesn't appear. | ||
### onCanvasClick(groupId, time, e) | ||
Called when an empty spot on the canvas was clicked. Get the group ID and the time as arguments. For example open a "new item" window after this. | ||
### onCanvasDoubleClick(group, time, e) | ||
Called when an empty spot on the canvas was double clicked. Get the group and the time as arguments. | ||
### onCanvasContextMenu(group, time, e) | ||
Called when the canvas is clicked by the right button of the mouse. Note: If this property is set the default context menu doesn't appear | ||
### onZoom(timelineContext) | ||
Called when the timeline is zoomed, either via mouse/pinch zoom or clicking header to change timeline units | ||
### moveResizeValidator(action, itemId, time, resizeEdge) | ||
This function is called when an item is being moved or resized. It's up to this function to return a new version of `change`, when the proposed move would violate business logic. | ||
@@ -285,44 +335,52 @@ | ||
### defaultTimeStart and defaultTimeEnd | ||
Unless overridden by `visibleTimeStart` and `visibleTimeEnd`, specify where the calendar begins and where it ends. This parameter expects a Date or moment object. | ||
### visibleTimeStart and visibleTimeEnd | ||
The exact viewport of the calendar. When these are specified, scrolling in the calendar must be orchestrated by the `onTimeChange` function. This parameter expects a unix timestamp in milliseconds. | ||
The exact viewport of the calendar. When these are specified, scrolling in the calendar must be orchestrated by the `onTimeChange` function. This parameter expects a unix timestamp in milliseconds. | ||
### headerLabelFormats and subHeaderLabelFormats | ||
The formats passed to moment to render times in the header and subheader. Defaults to these: | ||
```js | ||
import { defaultHeaderLabelFormats, defaultSubHeaderLabelFormats } from 'react-calendar-timeline' | ||
import { | ||
defaultHeaderLabelFormats, | ||
defaultSubHeaderLabelFormats | ||
} from 'react-calendar-timeline' | ||
defaultHeaderLabelFormats == { | ||
yearShort: 'YY', | ||
yearLong: 'YYYY', | ||
monthShort: 'MM/YY', | ||
monthMedium: 'MM/YYYY', | ||
monthMediumLong: 'MMM YYYY', | ||
monthLong: 'MMMM YYYY', | ||
dayShort: 'L', | ||
dayLong: 'dddd, LL', | ||
hourShort: 'HH', | ||
hourMedium: 'HH:00', | ||
hourMediumLong: 'L, HH:00', | ||
hourLong: 'dddd, LL, HH:00', | ||
time: 'LLL' | ||
} | ||
defaultHeaderLabelFormats == | ||
{ | ||
yearShort: 'YY', | ||
yearLong: 'YYYY', | ||
monthShort: 'MM/YY', | ||
monthMedium: 'MM/YYYY', | ||
monthMediumLong: 'MMM YYYY', | ||
monthLong: 'MMMM YYYY', | ||
dayShort: 'L', | ||
dayLong: 'dddd, LL', | ||
hourShort: 'HH', | ||
hourMedium: 'HH:00', | ||
hourMediumLong: 'L, HH:00', | ||
hourLong: 'dddd, LL, HH:00', | ||
time: 'LLL' | ||
} | ||
defaultSubHeaderLabelFormats == { | ||
yearShort: 'YY', | ||
yearLong: 'YYYY', | ||
monthShort: 'MM', | ||
monthMedium: 'MMM', | ||
monthLong: 'MMMM', | ||
dayShort: 'D', | ||
dayMedium: 'dd D', | ||
dayMediumLong: 'ddd, Do', | ||
dayLong: 'dddd, Do', | ||
hourShort: 'HH', | ||
hourLong: 'HH:00', | ||
minuteShort: 'mm', | ||
minuteLong: 'HH:mm' | ||
} | ||
defaultSubHeaderLabelFormats == | ||
{ | ||
yearShort: 'YY', | ||
yearLong: 'YYYY', | ||
monthShort: 'MM', | ||
monthMedium: 'MMM', | ||
monthLong: 'MMMM', | ||
dayShort: 'D', | ||
dayMedium: 'dd D', | ||
dayMediumLong: 'ddd, Do', | ||
dayLong: 'dddd, Do', | ||
hourShort: 'HH', | ||
hourLong: 'HH:00', | ||
minuteShort: 'mm', | ||
minuteLong: 'HH:mm' | ||
} | ||
``` | ||
@@ -333,3 +391,6 @@ | ||
```js | ||
import { defaultHeaderLabelFormats, defaultSubHeaderLabelFormats } from 'react-calendar-timeline' | ||
import { | ||
defaultHeaderLabelFormats, | ||
defaultSubHeaderLabelFormats | ||
} from 'react-calendar-timeline' | ||
@@ -340,10 +401,14 @@ const usHeaderLabelFormats = Object.assign({}, defaultSubHeaderLabelFormats, { | ||
hourMediumLong: 'L, h A', | ||
hourLong: 'dddd, LL, h A', | ||
hourLong: 'dddd, LL, h A' | ||
}) | ||
const usSubHeaderLabelFormats = Object.assign({}, defaultSubHeaderLabelFormats, { | ||
hourShort: 'h A', | ||
hourLong: 'h A', | ||
minuteLong: 'h:mm A' | ||
}) | ||
const usSubHeaderLabelFormats = Object.assign( | ||
{}, | ||
defaultSubHeaderLabelFormats, | ||
{ | ||
hourShort: 'h A', | ||
hourLong: 'h A', | ||
minuteLong: 'h:mm A' | ||
} | ||
) | ||
``` | ||
@@ -354,2 +419,3 @@ | ||
### onTimeChange(visibleTimeStart, visibleTimeEnd, updateScrollCanvas) | ||
A function that's called when the user tries to scroll. Call the passed `updateScrollCanvas(start, end)` with the updated visibleTimeStart and visibleTimeEnd (as unix timestamps in milliseconds) to change the scroll behavior, for example to limit scrolling. | ||
@@ -378,9 +444,12 @@ | ||
### onTimeInit(visibleTimeStart, visibleTimeEnd) | ||
Called when the calendar is first initialised. `visibleTimeStart` and `visibleTimeEnd` are unix timestamps in milliseconds. | ||
### onBoundsChange(canvasTimeStart, canvasTimeEnd) | ||
Called when the bounds in the calendar's canvas change. Use it for example to load new data to display. (see "Behind the scenes" below). `canvasTimeStart` and `canvasTimeEnd` are unix timestamps in milliseconds. | ||
### itemRenderer | ||
React component that will be used to render the item content. Will be | ||
React component that will be used to render the item content. Will be | ||
passed the `item` as a prop. | ||
@@ -442,2 +511,3 @@ | ||
### groupRenderer | ||
React component that will be used to render the content of groups in the | ||
@@ -457,5 +527,5 @@ sidebar. Will be passed the `group` and `isRightSidebar` as props. | ||
return ( | ||
<div className='custom-group'> | ||
<span className='title'>{group.title}</span> | ||
<p className='tip'>{group.tip}</p> | ||
<div className="custom-group"> | ||
<span className="title">{group.title}</span> | ||
<p className="tip">{group.tip}</p> | ||
</div> | ||
@@ -466,3 +536,8 @@ ) | ||
### minimumWidthForItemContentVisibility | ||
Number of pixels to render inner content of an Item. To improve performance of the timeline, this prop dictates whether the inner contents of an Item are rendered based on the item width. This setting is useful if you have a dataset which results in a large number of small items to be rendered on the timeline. Default is 25. | ||
### resizeDetector | ||
The component automatically detects when the window has been resized. Optionally you can also detect when the component's DOM element has been resized. | ||
@@ -478,2 +553,3 @@ To do this, pass a `resizeDetector`. Since bundling it by default would add ~18kb of minimized JS, you need to opt in to this like so: | ||
### children (plugins) | ||
If you give the component any children, they will be passed some extra props. Use this to render anything on the timeline (custom backgrounds, arrows, etc). | ||
@@ -517,2 +593,3 @@ | ||
To use it, you need to add two props to the `<Timeline />` component: | ||
```jsx | ||
@@ -524,2 +601,3 @@ rightSidebarWidth={150} | ||
And add `right_sidebar` prop to the groups objects: | ||
```js | ||
@@ -533,2 +611,31 @@ { | ||
### The timeline header doesn't fix to the top of the container when I scroll down. | ||
There are two causes of this: | ||
* you are passing `stickyHeader={false}` to the timeline component. The header by default has sticky behavior unless you tell it not to using this prop. | ||
* the browser you are viewing the timeline in doesn't support `position: sticky`. In this scenario, you will need to polyfill this behavior using the `headerRef`. | ||
In this example, we use [stickyfill](https://github.com/wilddeer/stickyfill) as our sticky polyfill | ||
```jsx | ||
// add a handler in your parent component that accepts a DOM element | ||
// with this element, pass the element into a polyfill library | ||
handleHeaderRef = (el) => { | ||
// polyfill dom element with stickyfill | ||
Stickyfill.addOne(el) | ||
} | ||
// in render, pass this handler to the `headerRef` prop: | ||
render() { | ||
<Timeline | ||
//other props | ||
headerRef={this.handleHeaderRef} | ||
/> | ||
} | ||
``` | ||
### I'm using Babel with Rollup or Webpack 2+ and I'm getting strange bugs with click events | ||
@@ -605,3 +712,2 @@ | ||
<!-- | ||
@@ -608,0 +714,0 @@ |
@@ -18,3 +18,2 @@ import { calculateDimensions } from '../../../utility/calendar' | ||
resizeTime: false, // we are not resizing right now | ||
fullUpdate: true, | ||
visibleTimeStart: 0, | ||
@@ -25,4 +24,2 @@ visibleTimeEnd: 500 | ||
expect(dimension).toMatchObject({ | ||
clippedLeft: false, | ||
clippedRight: false, | ||
collisionLeft: 200, | ||
@@ -50,3 +47,2 @@ collisionWidth: 100, | ||
resizeTime: false, // we are not resizing right now | ||
fullUpdate: true, | ||
visibleTimeStart: 0, | ||
@@ -57,4 +53,2 @@ visibleTimeEnd: 500 | ||
expect(dimension).toMatchObject({ | ||
clippedLeft: false, | ||
clippedRight: false, | ||
collisionLeft: 192, | ||
@@ -82,23 +76,9 @@ collisionWidth: 108, | ||
resizeTime: false, // we are not resizing right now | ||
fullUpdate: true, | ||
visibleTimeStart: 100, | ||
visibleTimeEnd: 500 | ||
} | ||
expect(calculateDimensions(example)).toMatchObject({ | ||
clippedLeft: true, | ||
clippedRight: false, | ||
collisionLeft: 0, | ||
collisionWidth: 300, | ||
left: 0, | ||
originalLeft: 0, | ||
width: 200 | ||
}) | ||
// if we don't do the fullUpdate we don't get correct | ||
// clipping informations | ||
example.fullUpdate = false | ||
expect(calculateDimensions(example)).toMatchObject({ | ||
clippedLeft: false, | ||
clippedRight: false, | ||
collisionLeft: 0, | ||
collisionWidth: 300, | ||
left: -100, | ||
@@ -124,9 +104,7 @@ originalLeft: 0, | ||
resizeTime: false, // we are not resizing right now | ||
fullUpdate: true, | ||
visibleTimeStart: 500, | ||
visibleTimeEnd: 900 | ||
} | ||
expect(calculateDimensions(example)).toMatchObject({ | ||
clippedLeft: false, | ||
clippedRight: true, | ||
collisionLeft: 700, | ||
@@ -136,14 +114,2 @@ collisionWidth: 300, | ||
originalLeft: 700, | ||
width: 200 | ||
}) | ||
// if we don't do the fullUpdate we don't get correct | ||
// clipping informations | ||
example.fullUpdate = false | ||
expect(calculateDimensions(example)).toMatchObject({ | ||
clippedLeft: false, | ||
clippedRight: false, | ||
collisionLeft: 700, | ||
collisionWidth: 300, | ||
left: 200, | ||
originalLeft: 700, | ||
width: 300 | ||
@@ -167,3 +133,2 @@ }) | ||
resizeTime: false, // we are not resizing right now | ||
fullUpdate: true, | ||
visibleTimeStart: 0, | ||
@@ -174,4 +139,2 @@ visibleTimeEnd: 500 | ||
expect(dimension).toMatchObject({ | ||
clippedLeft: false, | ||
clippedRight: false, | ||
collisionLeft: 200, | ||
@@ -202,4 +165,2 @@ collisionWidth: 200, | ||
expect(dimension).toMatchObject({ | ||
clippedLeft: false, | ||
clippedRight: false, | ||
collisionLeft: 200, | ||
@@ -227,3 +188,2 @@ collisionWidth: 50, | ||
resizeTime: 210, | ||
fullUpdate: true, | ||
visibleTimeStart: 0, | ||
@@ -234,4 +194,2 @@ visibleTimeEnd: 500 | ||
expect(dimension).toMatchObject({ | ||
clippedLeft: false, | ||
clippedRight: false, | ||
collisionLeft: 210, | ||
@@ -238,0 +196,0 @@ collisionWidth: 90, |
/* eslint-disable */ | ||
import { getMinUnit, minCellWidth } from '../../../utility/calendar' | ||
import moment from 'moment' | ||
import { defaultTimeSteps } from '../../../default-config' | ||
const defaultTimeSteps = { | ||
second: 1, | ||
minute: 1, | ||
hour: 1, | ||
day: 1, | ||
month: 1, | ||
year: 1 | ||
} | ||
describe('getMinUnit', () => { | ||
@@ -15,0 +7,0 @@ // this is the happy path and used as safety net if we make any refactorings |
@@ -17,2 +17,3 @@ import React, { Component } from 'react' | ||
order: PropTypes.number, | ||
minimumWidthForItemContentVisibility: PropTypes.number.isRequired, | ||
@@ -101,3 +102,5 @@ dragSnap: PropTypes.number, | ||
nextProps.canResizeRight !== this.props.canResizeRight || | ||
nextProps.dimensions !== this.props.dimensions | ||
nextProps.dimensions !== this.props.dimensions || | ||
nextProps.minimumWidthForItemContentVisibility !== | ||
this.props.minimumWidthForItemContentVisibility | ||
return shouldUpdate | ||
@@ -378,3 +381,3 @@ } | ||
canResizeLeft(props = this.props) { | ||
if (!props.canResizeLeft || props.dimensions.clippedLeft) { | ||
if (!props.canResizeLeft) { | ||
return false | ||
@@ -387,3 +390,3 @@ } | ||
canResizeRight(props = this.props) { | ||
if (!props.canResizeRight || props.dimensions.clippedRight) { | ||
if (!props.canResizeRight) { | ||
return false | ||
@@ -471,2 +474,3 @@ } | ||
handleDoubleClick = e => { | ||
e.preventDefault() | ||
e.stopPropagation() | ||
@@ -517,5 +521,3 @@ if (this.props.onItemDoubleClick) { | ||
(this.canResizeRight(this.props) ? ' can-resize-right' : '') + | ||
(this.props.item.className ? ` ${this.props.item.className}` : '') + | ||
(dimensions.clippedLeft ? ' clipped-left' : '') + | ||
(dimensions.clippedRight ? ' clipped-right' : '') | ||
(this.props.item.className ? ` ${this.props.item.className}` : '') | ||
@@ -530,2 +532,6 @@ const style = { | ||
const showInnerContents = | ||
dimensions.width > this.props.minimumWidthForItemContentVisibility | ||
// TODO: conditionals are really ugly. could use Fragment if supporting React 16+ but for now, it'll | ||
// be ugly | ||
return ( | ||
@@ -546,3 +552,3 @@ <div | ||
> | ||
{this.props.useResizeHandle ? ( | ||
{this.props.useResizeHandle && showInnerContents ? ( | ||
<div ref={el => (this.dragLeft = el)} className="rct-drag-left" /> | ||
@@ -552,6 +558,17 @@ ) : ( | ||
)} | ||
<div className="rct-item-overflow"> | ||
<div className="rct-item-content">{this.renderContent()}</div> | ||
</div> | ||
{this.props.useResizeHandle ? ( | ||
{showInnerContents ? ( | ||
<div | ||
className="rct-item-content" | ||
style={{ | ||
maxWidth: `${dimensions.width}px` | ||
}} | ||
> | ||
{this.renderContent()} | ||
</div> | ||
) : ( | ||
'' | ||
)} | ||
{this.props.useResizeHandle && showInnerContents ? ( | ||
<div ref={el => (this.dragRight = el)} className="rct-drag-right" /> | ||
@@ -558,0 +575,0 @@ ) : ( |
@@ -28,2 +28,3 @@ import PropTypes from 'prop-types' | ||
canvasWidth: PropTypes.number.isRequired, | ||
minimumWidthForItemContentVisibility: PropTypes.number.isRequired, | ||
@@ -81,3 +82,5 @@ dragSnap: PropTypes.number, | ||
nextProps.dimensionItems === this.props.dimensionItems && | ||
nextProps.topOffset === this.props.topOffset | ||
nextProps.topOffset === this.props.topOffset && | ||
nextProps.minimumWidthForItemContentVisibility === | ||
this.props.minimumWidthForItemContentVisibility | ||
) | ||
@@ -121,3 +124,8 @@ } | ||
render() { | ||
const { canvasTimeStart, canvasTimeEnd, dimensionItems } = this.props | ||
const { | ||
canvasTimeStart, | ||
canvasTimeEnd, | ||
dimensionItems, | ||
minimumWidthForItemContentVisibility | ||
} = this.props | ||
const { itemIdKey, itemGroupKey } = this.props.keys | ||
@@ -181,2 +189,5 @@ | ||
itemRenderer={this.props.itemRenderer} | ||
minimumWidthForItemContentVisibility={ | ||
minimumWidthForItemContentVisibility | ||
} | ||
/> | ||
@@ -183,0 +194,0 @@ ))} |
@@ -0,8 +1,6 @@ | ||
import React, { Component } from 'react' | ||
import PropTypes from 'prop-types' | ||
import React, { Component } from 'react' | ||
import moment from 'moment' | ||
import TimelineElementsHeader from './TimelineElementsHeader' | ||
import { iterateTimes, getNextUnit } from '../utility/calendar' | ||
export default class Header extends Component { | ||
class Header extends Component { | ||
static propTypes = { | ||
@@ -15,4 +13,2 @@ hasRightSidebar: PropTypes.bool.isRequired, | ||
lineHeight: PropTypes.number.isRequired, | ||
visibleTimeStart: PropTypes.number.isRequired, | ||
visibleTimeEnd: PropTypes.number.isRequired, | ||
minUnit: PropTypes.string.isRequired, | ||
@@ -23,279 +19,46 @@ timeSteps: PropTypes.object.isRequired, | ||
subHeaderLabelFormats: PropTypes.object.isRequired, | ||
fixedHeader: PropTypes.oneOf(['fixed', 'sticky', 'none']), | ||
stickyOffset: PropTypes.number.isRequired, | ||
headerPosition: PropTypes.oneOf(['top', 'bottom', 'fixed']), | ||
stickyOffset: PropTypes.number, | ||
stickyHeader: PropTypes.bool.isRequired, | ||
headerLabelGroupHeight: PropTypes.number.isRequired, | ||
headerLabelHeight: PropTypes.number.isRequired | ||
headerLabelHeight: PropTypes.number.isRequired, | ||
registerScroll: PropTypes.func.isRequired, | ||
leftSidebarHeader: PropTypes.node, | ||
rightSidebarHeader: PropTypes.node, | ||
headerRef: PropTypes.func.isRequired | ||
} | ||
static defaultProps = { | ||
fixedHeader: 'sticky', | ||
stickyOffset: 0, | ||
headerPosition: 'top' | ||
} | ||
constructor(props) { | ||
super(props) | ||
this.state = { | ||
touchTarget: null, | ||
touchActive: false | ||
} | ||
} | ||
headerLabel(time, unit, width) { | ||
const { headerLabelFormats: f } = this.props | ||
if (unit === 'year') { | ||
return time.format(width < 46 ? f.yearShort : f.yearLong) | ||
} else if (unit === 'month') { | ||
return time.format( | ||
width < 65 | ||
? f.monthShort | ||
: width < 75 | ||
? f.monthMedium | ||
: width < 120 ? f.monthMediumLong : f.monthLong | ||
) | ||
} else if (unit === 'day') { | ||
return time.format(width < 150 ? f.dayShort : f.dayLong) | ||
} else if (unit === 'hour') { | ||
return time.format( | ||
width < 50 | ||
? f.hourShort | ||
: width < 130 | ||
? f.hourMedium | ||
: width < 150 ? f.hourMediumLong : f.hourLong | ||
) | ||
} else { | ||
return time.format(f.time) | ||
} | ||
} | ||
subHeaderLabel(time, unit, width) { | ||
const { subHeaderLabelFormats: f } = this.props | ||
if (unit === 'year') { | ||
return time.format(width < 46 ? f.yearShort : f.yearLong) | ||
} else if (unit === 'month') { | ||
return time.format( | ||
width < 37 ? f.monthShort : width < 85 ? f.monthMedium : f.monthLong | ||
) | ||
} else if (unit === 'day') { | ||
return time.format( | ||
width < 47 | ||
? f.dayShort | ||
: width < 80 ? f.dayMedium : width < 120 ? f.dayMediumLong : f.dayLong | ||
) | ||
} else if (unit === 'hour') { | ||
return time.format(width < 50 ? f.hourShort : f.hourLong) | ||
} else if (unit === 'minute') { | ||
return time.format(width < 60 ? f.minuteShort : f.minuteLong) | ||
} else { | ||
return time.get(unit) | ||
} | ||
} | ||
periodClick = e => { | ||
const { time, unit } = e.target.dataset | ||
if (time && unit) { | ||
this.props.showPeriod(moment(time - 0), unit) | ||
} | ||
} | ||
touchStart = e => { | ||
if (e.touches.length === 1) { | ||
this.setState({ | ||
touchTarget: e.target || e.touchTarget, | ||
touchActive: true | ||
}) | ||
} | ||
} | ||
touchEnd = e => { | ||
if (!this.state.touchActive) { | ||
return this.resetTouchState() | ||
} | ||
var changedTouches = e.changedTouches[0] | ||
if (changedTouches) { | ||
var elem = document.elementFromPoint( | ||
changedTouches.pageX, | ||
changedTouches.pageY | ||
) | ||
if (elem !== this.state.touchTarget) { | ||
return this.resetTouchState() | ||
} | ||
} | ||
this.resetTouchState() | ||
this.periodClick(e) | ||
} | ||
resetTouchState() { | ||
this.setState({ | ||
touchTarget: null, | ||
touchActive: false | ||
}) | ||
} | ||
render() { | ||
let timeLabels = [] | ||
const { | ||
canvasTimeStart, | ||
canvasTimeEnd, | ||
canvasWidth, | ||
lineHeight, | ||
visibleTimeStart, | ||
visibleTimeEnd, | ||
minUnit, | ||
timeSteps, | ||
fixedHeader, | ||
leftSidebarHeader, | ||
rightSidebarHeader, | ||
width, | ||
stickyOffset, | ||
headerPosition, | ||
headerLabelGroupHeight, | ||
headerLabelHeight, | ||
hasRightSidebar, | ||
width | ||
stickyHeader, | ||
headerRef | ||
} = this.props | ||
const ratio = canvasWidth / (canvasTimeEnd - canvasTimeStart) | ||
const twoHeaders = minUnit !== 'year' | ||
const correctLeftPositions = | ||
fixedHeader === 'fixed' || | ||
(fixedHeader === 'sticky' && headerPosition === 'fixed') | ||
// add the top header | ||
if (twoHeaders) { | ||
const nextUnit = getNextUnit(minUnit) | ||
iterateTimes( | ||
visibleTimeStart, | ||
visibleTimeEnd, | ||
nextUnit, | ||
timeSteps, | ||
(time, nextTime) => { | ||
const startTime = Math.max(visibleTimeStart, time.valueOf()) | ||
const endTime = Math.min(visibleTimeEnd, nextTime.valueOf()) | ||
const left = Math.round( | ||
(startTime.valueOf() - canvasTimeStart) * ratio, | ||
-2 | ||
) | ||
const right = Math.round( | ||
(endTime.valueOf() - canvasTimeStart) * ratio, | ||
-2 | ||
) | ||
const labelWidth = right - left | ||
const leftCorrect = correctLeftPositions | ||
? Math.round((canvasTimeStart - visibleTimeStart) * ratio) - 1 | ||
: 0 | ||
timeLabels.push( | ||
<div | ||
key={`top-label-${time.valueOf()}`} | ||
className={`rct-label-group${ | ||
hasRightSidebar ? ' rct-has-right-sidebar' : '' | ||
}`} | ||
data-time={time} | ||
data-unit={nextUnit} | ||
style={{ | ||
left: `${left + leftCorrect}px`, | ||
width: `${labelWidth}px`, | ||
height: `${headerLabelGroupHeight}px`, | ||
lineHeight: `${headerLabelGroupHeight}px`, | ||
cursor: 'pointer' | ||
}} | ||
> | ||
{this.headerLabel(time, nextUnit, labelWidth)} | ||
</div> | ||
) | ||
} | ||
) | ||
const headerStyle = { | ||
top: stickyHeader ? stickyOffset || 0 : 0 | ||
} | ||
iterateTimes( | ||
canvasTimeStart, | ||
canvasTimeEnd, | ||
minUnit, | ||
timeSteps, | ||
(time, nextTime) => { | ||
const left = Math.round((time.valueOf() - canvasTimeStart) * ratio, -2) | ||
const minUnitValue = time.get(minUnit === 'day' ? 'date' : minUnit) | ||
const firstOfType = minUnitValue === (minUnit === 'day' ? 1 : 0) | ||
const labelWidth = Math.round( | ||
(nextTime.valueOf() - time.valueOf()) * ratio, | ||
-2 | ||
) | ||
const borderWidth = firstOfType ? 2 : 1 | ||
const leftCorrect = correctLeftPositions | ||
? Math.round((canvasTimeStart - visibleTimeStart) * ratio) - | ||
borderWidth + | ||
1 | ||
: 0 | ||
const headerClass = stickyHeader ? 'header-sticky' : '' | ||
timeLabels.push( | ||
<div | ||
key={`label-${time.valueOf()}`} | ||
className={`rct-label ${twoHeaders ? '' : 'rct-label-only'} ${ | ||
firstOfType ? 'rct-first-of-type' : '' | ||
} ${minUnit !== 'month' ? `rct-day-${time.day()}` : ''} `} | ||
data-time={time} | ||
data-unit={minUnit} | ||
style={{ | ||
top: `${minUnit === 'year' ? 0 : headerLabelGroupHeight}px`, | ||
left: `${left + leftCorrect}px`, | ||
width: `${labelWidth}px`, | ||
height: `${ | ||
minUnit === 'year' | ||
? headerLabelGroupHeight + headerLabelHeight | ||
: headerLabelHeight | ||
}px`, | ||
lineHeight: `${ | ||
minUnit === 'year' | ||
? headerLabelGroupHeight + headerLabelHeight | ||
: headerLabelHeight | ||
}px`, | ||
fontSize: `${ | ||
labelWidth > 30 ? '14' : labelWidth > 20 ? '12' : '10' | ||
}px`, | ||
cursor: 'pointer' | ||
}} | ||
> | ||
{this.subHeaderLabel(time, minUnit, labelWidth)} | ||
</div> | ||
) | ||
} | ||
) | ||
let headerStyle = { | ||
height: `${headerLabelGroupHeight + headerLabelHeight}px`, | ||
lineHeight: `${lineHeight}px` | ||
} | ||
if (fixedHeader === 'fixed') { | ||
headerStyle.position = 'fixed' | ||
headerStyle.width = `${width}px` | ||
} else if (fixedHeader === 'sticky') { | ||
if (headerPosition === 'top') { | ||
// do nothing, keep at the top | ||
} else if (headerPosition === 'fixed') { | ||
headerStyle.position = 'fixed' | ||
headerStyle.top = stickyOffset | ||
headerStyle.width = `${width}px` | ||
} else if (headerPosition === 'bottom') { | ||
headerStyle.position = 'absolute' | ||
headerStyle.bottom = 0 | ||
headerStyle.width = `${canvasWidth}px` | ||
} | ||
} | ||
return ( | ||
<div | ||
key="header" | ||
className="rct-header" | ||
onTouchStart={this.touchStart} | ||
onTouchEnd={this.touchEnd} | ||
onClick={this.periodClick} | ||
className={`rct-header-container ${headerClass}`} | ||
data-test-id="timeline-elements-container" | ||
ref={headerRef} | ||
style={headerStyle} | ||
> | ||
{timeLabels} | ||
{leftSidebarHeader} | ||
<div | ||
style={{ width }} | ||
data-test-id="timeline-elements-header-container" | ||
> | ||
<TimelineElementsHeader | ||
data-test-id="timeline-elements-header" | ||
{...this.props} | ||
/> | ||
</div> | ||
{rightSidebarHeader} | ||
</div> | ||
@@ -305,1 +68,3 @@ ) | ||
} | ||
export default Header |
@@ -11,22 +11,8 @@ import PropTypes from 'prop-types' | ||
height: PropTypes.number.isRequired, | ||
lineHeight: PropTypes.number.isRequired, | ||
groupHeights: PropTypes.array.isRequired, | ||
fixedHeader: PropTypes.oneOf(['fixed', 'sticky', 'none']), | ||
stickyOffset: PropTypes.number.isRequired, | ||
headerPosition: PropTypes.oneOf(['top', 'bottom', 'fixed']), | ||
keys: PropTypes.object.isRequired, | ||
groupRenderer: PropTypes.func, | ||
children: PropTypes.node, | ||
isRightSidebar: PropTypes.bool, | ||
headerHeight: PropTypes.number.isRequired | ||
isRightSidebar: PropTypes.bool | ||
} | ||
static defaultProps = { | ||
fixedHeader: 'sticky', | ||
stickyOffset: 0, | ||
headerPosition: 'top', | ||
children: null, | ||
isRightSidebar: false | ||
} | ||
shouldComponentUpdate(nextProps) { | ||
@@ -37,6 +23,2 @@ return !( | ||
nextProps.width === this.props.width && | ||
nextProps.lineHeight === this.props.lineHeight && | ||
nextProps.fixedHeader === this.props.fixedHeader && | ||
nextProps.stickyOffset === this.props.stickyOffset && | ||
nextProps.headerPosition === this.props.headerPosition && | ||
nextProps.groupHeights === this.props.groupHeights && | ||
@@ -59,13 +41,3 @@ nextProps.height === this.props.height | ||
render() { | ||
const { | ||
fixedHeader, | ||
stickyOffset, | ||
width, | ||
lineHeight, | ||
groupHeights, | ||
height, | ||
headerHeight, | ||
isRightSidebar, | ||
headerPosition | ||
} = this.props | ||
const { width, groupHeights, height, isRightSidebar } = this.props | ||
@@ -79,8 +51,2 @@ const { groupIdKey, groupTitleKey, groupRightTitleKey } = this.props.keys | ||
const headerStyle = { | ||
height: `${headerHeight}px`, | ||
lineHeight: `${lineHeight}px`, | ||
width: `${width}px` | ||
} | ||
const groupsStyle = { | ||
@@ -90,25 +56,2 @@ width: `${width}px` | ||
if (fixedHeader === 'fixed') { | ||
headerStyle.position = 'fixed' | ||
groupsStyle.paddingTop = headerStyle.height | ||
} else if (fixedHeader === 'sticky') { | ||
if (headerPosition === 'top') { | ||
// do nothing - keep at the top | ||
} else if (headerPosition === 'fixed') { | ||
headerStyle.position = 'fixed' | ||
headerStyle.top = stickyOffset | ||
groupsStyle.paddingTop = headerStyle.height | ||
} else if (headerPosition === 'bottom') { | ||
headerStyle.position = 'absolute' | ||
headerStyle.bottom = 0 | ||
groupsStyle.paddingTop = headerStyle.height | ||
} | ||
} | ||
const header = ( | ||
<div className="rct-sidebar-header" style={headerStyle}> | ||
{this.props.children} | ||
</div> | ||
) | ||
let groupLines = [] | ||
@@ -148,3 +91,2 @@ let i = 0 | ||
> | ||
{header} | ||
<div style={groupsStyle}>{groupLines}</div> | ||
@@ -151,0 +93,0 @@ </div> |
@@ -12,3 +12,2 @@ import PropTypes from 'prop-types' | ||
cursorTime: PropTypes.number, | ||
headerHeight: PropTypes.number.isRequired, | ||
height: PropTypes.number.isRequired | ||
@@ -28,6 +27,5 @@ } | ||
const left = Math.round((cursorTime - this.props.canvasTimeStart) * ratio) | ||
const top = this.props.headerHeight | ||
const height = this.props.height - this.props.headerHeight | ||
const height = this.props.height | ||
const styles = { | ||
top: `${top}px`, | ||
top: '0px', | ||
left: `${left}px`, | ||
@@ -34,0 +32,0 @@ height: `${height}px` |
@@ -7,3 +7,2 @@ import PropTypes from 'prop-types' | ||
canvasWidth: PropTypes.number.isRequired, | ||
headerHeight: PropTypes.number.isRequired, | ||
lineCount: PropTypes.number.isRequired, | ||
@@ -16,3 +15,2 @@ groupHeights: PropTypes.array.isRequired | ||
nextProps.canvasWidth === this.props.canvasWidth && | ||
nextProps.headerHeight === this.props.headerHeight && | ||
nextProps.lineCount === this.props.lineCount && | ||
@@ -24,6 +22,6 @@ nextProps.groupHeights === this.props.groupHeights | ||
render() { | ||
const { canvasWidth, headerHeight, lineCount, groupHeights } = this.props | ||
const { canvasWidth, lineCount, groupHeights } = this.props | ||
let lines = [] | ||
var totalHeight = headerHeight | ||
var totalHeight = 0 | ||
for (let i = 0; i < lineCount; i++) { | ||
@@ -30,0 +28,0 @@ lines.push( |
@@ -11,3 +11,2 @@ import PropTypes from 'prop-types' | ||
lineCount: PropTypes.number.isRequired, | ||
headerHeight: PropTypes.number.isRequired, | ||
height: PropTypes.number.isRequired | ||
@@ -32,6 +31,5 @@ } | ||
) | ||
const top = this.props.headerHeight | ||
const height = this.props.height - this.props.headerHeight | ||
const height = this.props.height | ||
const styles = { | ||
top: `${top}px`, | ||
top: '0px', | ||
left: `${left}px`, | ||
@@ -38,0 +36,0 @@ height: `${height}px` |
@@ -15,12 +15,5 @@ import PropTypes from 'prop-types' | ||
timeSteps: PropTypes.object.isRequired, | ||
fixedHeader: PropTypes.string.isRequired, | ||
height: PropTypes.number.isRequired, | ||
headerHeight: PropTypes.number.isRequired | ||
height: PropTypes.number.isRequired | ||
} | ||
static defaultProps = { | ||
fixedHeader: 'sticky', | ||
dayBackground: null | ||
} | ||
shouldComponentUpdate(nextProps) { | ||
@@ -35,5 +28,3 @@ return !( | ||
nextProps.timeSteps === this.props.timeSteps && | ||
nextProps.fixedHeader === this.props.fixedHeader && | ||
nextProps.height === this.props.height && | ||
nextProps.headerHeight === this.props.headerHeight | ||
nextProps.height === this.props.height | ||
) | ||
@@ -49,4 +40,3 @@ } | ||
timeSteps, | ||
height, | ||
headerHeight | ||
height | ||
} = this.props | ||
@@ -69,4 +59,3 @@ const ratio = canvasWidth / (canvasTimeEnd - canvasTimeStart) | ||
Math.ceil((nextTime.valueOf() - time.valueOf()) * ratio) - lineWidth | ||
const leftPush = | ||
this.props.fixedHeader !== 'none' && firstOfType ? -1 : 0 | ||
const leftPush = firstOfType ? -1 : 0 | ||
@@ -85,6 +74,6 @@ const classNames = | ||
style={{ | ||
top: `${headerHeight}px`, | ||
top: '0px', | ||
left: `${left + leftPush}px`, | ||
width: `${labelWidth}px`, | ||
height: `${height - headerHeight}px` | ||
height: `${height}px` | ||
}} | ||
@@ -91,0 +80,0 @@ /> |
@@ -6,1 +6,7 @@ import { JSDOM } from 'jsdom' | ||
} | ||
export function sel(selectorString) { | ||
return `[data-test-id="${selectorString}"]` | ||
} | ||
export function noop() {} |
@@ -14,2 +14,3 @@ import PropTypes from 'prop-types' | ||
import CursorLine from './lines/CursorLine' | ||
import ScrollElement from './scroll/ScrollElement' | ||
@@ -29,56 +30,9 @@ import windowResizeDetector from '../resize-detector/window' | ||
import { _get, _length } from './utility/generic' | ||
import { | ||
defaultKeys, | ||
defaultTimeSteps, | ||
defaultHeaderLabelFormats, | ||
defaultSubHeaderLabelFormats | ||
} from './default-config' | ||
export const defaultKeys = { | ||
groupIdKey: 'id', | ||
groupTitleKey: 'title', | ||
groupRightTitleKey: 'rightTitle', | ||
itemIdKey: 'id', | ||
itemTitleKey: 'title', | ||
itemDivTitleKey: 'title', | ||
itemGroupKey: 'group', | ||
itemTimeStartKey: 'start_time', | ||
itemTimeEndKey: 'end_time' | ||
} | ||
export const defaultTimeSteps = { | ||
second: 1, | ||
minute: 1, | ||
hour: 1, | ||
day: 1, | ||
month: 1, | ||
year: 1 | ||
} | ||
export const defaultHeaderLabelFormats = { | ||
yearShort: 'YY', | ||
yearLong: 'YYYY', | ||
monthShort: 'MM/YY', | ||
monthMedium: 'MM/YYYY', | ||
monthMediumLong: 'MMM YYYY', | ||
monthLong: 'MMMM YYYY', | ||
dayShort: 'L', | ||
dayLong: 'dddd, LL', | ||
hourShort: 'HH', | ||
hourMedium: 'HH:00', | ||
hourMediumLong: 'L, HH:00', | ||
hourLong: 'dddd, LL, HH:00', | ||
time: 'LLL' | ||
} | ||
export const defaultSubHeaderLabelFormats = { | ||
yearShort: 'YY', | ||
yearLong: 'YYYY', | ||
monthShort: 'MM', | ||
monthMedium: 'MMM', | ||
monthLong: 'MMMM', | ||
dayShort: 'D', | ||
dayMedium: 'dd D', | ||
dayMediumLong: 'ddd, Do', | ||
dayLong: 'dddd, Do', | ||
hourShort: 'HH', | ||
hourLong: 'HH:00', | ||
minuteShort: 'mm', | ||
minuteLong: 'HH:mm' | ||
} | ||
export default class ReactCalendarTimeline extends Component { | ||
@@ -94,5 +48,4 @@ static propTypes = { | ||
minResizeWidth: PropTypes.number, | ||
fixedHeader: PropTypes.oneOf(['fixed', 'sticky', 'none']), | ||
stickyOffset: PropTypes.number, | ||
fullUpdate: PropTypes.bool, | ||
stickyHeader: PropTypes.bool, | ||
lineHeight: PropTypes.number, | ||
@@ -102,2 +55,3 @@ headerLabelGroupHeight: PropTypes.number, | ||
itemHeightRatio: PropTypes.number, | ||
minimumWidthForItemContentVisibility: PropTypes.number, | ||
@@ -157,2 +111,3 @@ minZoom: PropTypes.number, | ||
}), | ||
headerRef: PropTypes.func, | ||
@@ -223,5 +178,4 @@ timeSteps: PropTypes.shape({ | ||
minResizeWidth: 20, | ||
fixedHeader: 'sticky', // fixed or sticky or none | ||
stickyOffset: 0, | ||
fullUpdate: true, | ||
stickyHeader: true, | ||
lineHeight: 30, | ||
@@ -231,2 +185,3 @@ headerLabelGroupHeight: 30, | ||
itemHeightRatio: 0.65, | ||
minimumWidthForItemContentVisibility: 25, | ||
@@ -274,2 +229,3 @@ minZoom: 60 * 60 * 1000, // 1 hour | ||
timeSteps: defaultTimeSteps, | ||
headerRef: () => {}, | ||
@@ -361,4 +317,2 @@ // if you pass in visibleTimeStart and visibleTimeEnd, you must also pass onTimeChange(visibleTimeStart, visibleTimeEnd), | ||
headerPosition: 'top', | ||
selectedItem: null, | ||
@@ -368,3 +322,2 @@ dragTime: null, | ||
resizeTime: null, | ||
isDragging: false, | ||
topOffset: 0, | ||
@@ -403,8 +356,2 @@ resizingItem: null, | ||
this.lastTouchDistance = null | ||
window.addEventListener('scroll', this.scrollEventListener) | ||
this.scrollComponent.addEventListener('touchstart', this.touchStart) | ||
this.scrollComponent.addEventListener('touchmove', this.touchMove) | ||
this.scrollComponent.addEventListener('touchend', this.touchEnd) | ||
} | ||
@@ -418,111 +365,4 @@ | ||
windowResizeDetector.removeListener(this) | ||
window.removeEventListener('scroll', this.scrollEventListener) | ||
this.scrollComponent.removeEventListener('touchstart', this.touchStart) | ||
this.scrollComponent.removeEventListener('touchmove', this.touchMove) | ||
this.scrollComponent.removeEventListener('touchend', this.touchEnd) | ||
} | ||
// called on window scroll. it's job is to figure out if we should fix or float the header | ||
scrollEventListener = () => { | ||
const { headerLabelGroupHeight, headerLabelHeight } = this.props | ||
const headerHeight = headerLabelGroupHeight + headerLabelHeight | ||
const rect = this.container.getBoundingClientRect() | ||
if (rect.top > this.props.stickyOffset) { | ||
this.setState({ headerPosition: 'top' }) | ||
} else if (rect.bottom < headerHeight + this.props.stickyOffset) { | ||
this.setState({ headerPosition: 'bottom' }) | ||
} else { | ||
this.setState({ headerPosition: 'fixed' }) | ||
} | ||
} | ||
touchStart = e => { | ||
if (e.touches.length === 2) { | ||
e.preventDefault() | ||
this.lastTouchDistance = Math.abs( | ||
e.touches[0].screenX - e.touches[1].screenX | ||
) | ||
this.singleTouchStart = null | ||
this.lastSingleTouch = null | ||
} else if (e.touches.length === 1) { | ||
e.preventDefault() | ||
let x = e.touches[0].clientX | ||
let y = e.touches[0].clientY | ||
this.lastTouchDistance = null | ||
this.singleTouchStart = { x: x, y: y, screenY: window.pageYOffset } | ||
this.lastSingleTouch = { x: x, y: y, screenY: window.pageYOffset } | ||
} | ||
} | ||
touchMove = e => { | ||
if (this.state.dragTime || this.state.resizeTime) { | ||
e.preventDefault() | ||
return | ||
} | ||
if (this.lastTouchDistance && e.touches.length === 2) { | ||
e.preventDefault() | ||
let touchDistance = Math.abs(e.touches[0].screenX - e.touches[1].screenX) | ||
let parentPosition = getParentPosition(e.currentTarget) | ||
let xPosition = | ||
(e.touches[0].screenX + e.touches[1].screenX) / 2 - parentPosition.x | ||
if (touchDistance !== 0 && this.lastTouchDistance !== 0) { | ||
this.changeZoom( | ||
this.lastTouchDistance / touchDistance, | ||
xPosition / this.state.width | ||
) | ||
this.lastTouchDistance = touchDistance | ||
} | ||
} else if (this.lastSingleTouch && e.touches.length === 1) { | ||
e.preventDefault() | ||
let x = e.touches[0].clientX | ||
let y = e.touches[0].clientY | ||
let deltaX = x - this.lastSingleTouch.x | ||
// let deltaY = y - this.lastSingleTouch.y | ||
let deltaX0 = x - this.singleTouchStart.x | ||
let deltaY0 = y - this.singleTouchStart.y | ||
this.lastSingleTouch = { x: x, y: y } | ||
let moveX = Math.abs(deltaX0) * 3 > Math.abs(deltaY0) | ||
let moveY = Math.abs(deltaY0) * 3 > Math.abs(deltaX0) | ||
if (deltaX !== 0 && moveX) { | ||
this.scrollComponent.scrollLeft -= deltaX | ||
} | ||
if (moveY) { | ||
window.scrollTo( | ||
window.pageXOffset, | ||
this.singleTouchStart.screenY - deltaY0 | ||
) | ||
} | ||
} | ||
} | ||
touchEnd = e => { | ||
if (this.lastTouchDistance) { | ||
e.preventDefault() | ||
this.lastTouchDistance = null | ||
} | ||
if (this.lastSingleTouch) { | ||
e.preventDefault() | ||
this.lastSingleTouch = null | ||
this.singleTouchStart = null | ||
} | ||
} | ||
resize = (props = this.props) => { | ||
@@ -555,6 +395,5 @@ const { | ||
onScroll = () => { | ||
const scrollComponent = this.scrollComponent | ||
onScroll = scrollX => { | ||
const canvasTimeStart = this.state.canvasTimeStart | ||
const scrollX = scrollComponent.scrollLeft | ||
const zoom = this.state.visibleTimeEnd - this.state.visibleTimeStart | ||
@@ -564,3 +403,2 @@ const width = this.state.width | ||
// move the virtual canvas if needed | ||
if (scrollX < this.state.width * 0.5) { | ||
@@ -570,3 +408,2 @@ this.setState({ | ||
}) | ||
scrollComponent.scrollLeft += this.state.width | ||
} | ||
@@ -577,3 +414,2 @@ if (scrollX > this.state.width * 1.5) { | ||
}) | ||
scrollComponent.scrollLeft -= this.state.width | ||
} | ||
@@ -591,2 +427,6 @@ | ||
} | ||
this.setState({ | ||
currentScrollLeft: scrollX | ||
}) | ||
} | ||
@@ -653,3 +493,2 @@ | ||
const groups = updatedGroups || this.props.groups | ||
const { fullUpdate } = this.props | ||
@@ -695,3 +534,3 @@ let newState = { | ||
if (resetCanvas || forceUpdateDimensions || fullUpdate) { | ||
if (resetCanvas || forceUpdateDimensions) { | ||
const canvasTimeStart = newState.canvasTimeStart | ||
@@ -711,4 +550,3 @@ ? newState.canvasTimeStart | ||
visibleTimeEnd, | ||
this.state.width, | ||
fullUpdate | ||
this.state.width | ||
) | ||
@@ -731,56 +569,6 @@ newState.dimensionItems = dimensionItems | ||
zoomWithWheel = (speed, xPosition, deltaY) => { | ||
handleWheelZoom = (speed, xPosition, deltaY) => { | ||
this.changeZoom(1.0 + speed * deltaY / 500, xPosition / this.state.width) | ||
} | ||
onWheel = e => { | ||
const { traditionalZoom } = this.props | ||
e.preventDefault() | ||
// zoom in the time dimension | ||
if (e.ctrlKey || e.metaKey || e.altKey) { | ||
const parentPosition = getParentPosition(e.currentTarget) | ||
const xPosition = e.clientX - parentPosition.x | ||
const speed = e.ctrlKey ? 10 : e.metaKey ? 3 : 1 | ||
this.zoomWithWheel(speed, xPosition, e.deltaY) | ||
// convert vertical zoom to horiziontal | ||
} else if (e.shiftKey) { | ||
const scrollComponent = this.scrollComponent | ||
scrollComponent.scrollLeft += e.deltaY | ||
// no modifier pressed? we prevented the default event, so scroll or zoom as needed | ||
} else { | ||
if (e.deltaX !== 0) { | ||
if (!traditionalZoom) { | ||
this.scrollComponent.scrollLeft += e.deltaX | ||
} | ||
} | ||
if (e.deltaY !== 0) { | ||
window.scrollTo(window.pageXOffset, window.pageYOffset + e.deltaY) | ||
if (traditionalZoom) { | ||
const parentPosition = getParentPosition(e.currentTarget) | ||
const xPosition = e.clientX - parentPosition.x | ||
this.zoomWithWheel(10, xPosition, e.deltaY) | ||
} | ||
} | ||
} | ||
} | ||
zoomIn(e) { | ||
e.preventDefault() | ||
this.changeZoom(0.75) | ||
} | ||
zoomOut(e) { | ||
e.preventDefault() | ||
this.changeZoom(1.25) | ||
} | ||
changeZoom(scale, offset = 0.5) { | ||
@@ -979,51 +767,3 @@ const { minZoom, maxZoom } = this.props | ||
handleMouseDown = e => { | ||
const { topOffset } = this.state | ||
const { pageY } = e | ||
const { headerLabelGroupHeight, headerLabelHeight } = this.props | ||
const headerHeight = headerLabelGroupHeight + headerLabelHeight | ||
if (pageY - topOffset > headerHeight && e.button === 0) { | ||
this.setState({ | ||
isDragging: true, | ||
dragStartPosition: e.pageX, | ||
dragLastPosition: e.pageX | ||
}) | ||
} | ||
} | ||
handleMouseMove = e => { | ||
if ( | ||
this.state.isDragging && | ||
!this.state.draggingItem && | ||
!this.state.resizingItem | ||
) { | ||
this.scrollComponent.scrollLeft += this.state.dragLastPosition - e.pageX | ||
this.setState({ dragLastPosition: e.pageX }) | ||
} | ||
} | ||
handleMouseUp = e => { | ||
const { dragStartPosition } = this.state | ||
if (Math.abs(dragStartPosition - e.pageX) <= this.props.clickTolerance) { | ||
this.scrollAreaClick(e) | ||
} | ||
this.setState({ | ||
isDragging: false, | ||
dragStartPosition: null, | ||
dragLastPosition: null | ||
}) | ||
} | ||
handleMouseLeave = () => { | ||
this.setState({ | ||
isDragging: false, | ||
dragStartPosition: null, | ||
dragLastPosition: null | ||
}) | ||
} | ||
handleCanvasMouseEnter = e => { | ||
handleScrollMouseEnter = e => { | ||
const { showCursorLine } = this.props | ||
@@ -1039,3 +779,3 @@ if (showCursorLine) { | ||
handleCanvasMouseLeave = e => { | ||
handleScrollMouseLeave = e => { | ||
const { showCursorLine } = this.props | ||
@@ -1051,3 +791,3 @@ if (showCursorLine) { | ||
handleCanvasMouseMove = e => { | ||
handleScrollMouseMove = e => { | ||
const { showCursorLine } = this.props | ||
@@ -1148,3 +888,2 @@ const { | ||
timeSteps={timeSteps} | ||
fixedHeader={this.props.fixedHeader} | ||
height={height} | ||
@@ -1209,2 +948,5 @@ headerHeight={headerHeight} | ||
selected={this.props.selected} | ||
minimumWidthForItemContentVisibility={ | ||
this.props.minimumWidthForItemContentVisibility | ||
} | ||
/> | ||
@@ -1238,2 +980,23 @@ ) | ||
) { | ||
const { sidebarWidth, rightSidebarWidth } = this.props | ||
const leftSidebar = sidebarWidth != null && | ||
sidebarWidth > 0 && ( | ||
<div | ||
className="rct-sidebar-header" | ||
style={{ width: this.props.sidebarWidth }} | ||
> | ||
{this.props.sidebarContent} | ||
</div> | ||
) | ||
const rightSidebar = rightSidebarWidth != null && | ||
rightSidebarWidth > 0 && ( | ||
<div | ||
className="rct-sidebar-header rct-sidebar-right" | ||
style={{ width: this.props.rightSidebarWidth }} | ||
> | ||
{this.props.rightSidebarContent} | ||
</div> | ||
) | ||
return ( | ||
@@ -1254,8 +1017,11 @@ <Header | ||
visibleTimeEnd={this.state.visibleTimeEnd} | ||
headerPosition={this.state.headerPosition} | ||
fixedHeader={this.props.fixedHeader} | ||
stickyOffset={this.props.stickyOffset} | ||
stickyHeader={this.props.stickyHeader} | ||
showPeriod={this.showPeriod} | ||
headerLabelFormats={this.props.headerLabelFormats} | ||
subHeaderLabelFormats={this.props.subHeaderLabelFormats} | ||
registerScroll={this.registerScrollListener} | ||
leftSidebarHeader={leftSidebar} | ||
rightSidebarHeader={rightSidebar} | ||
headerRef={this.props.headerRef} | ||
/> | ||
@@ -1265,39 +1031,44 @@ ) | ||
sidebar(height, groupHeights, headerHeight) { | ||
componentDidUpdate() { | ||
this.headerScrollListener(this.state.currentScrollLeft) | ||
} | ||
registerScrollListener = listener => { | ||
this.headerScrollListener = listener | ||
} | ||
sidebar(height, groupHeights) { | ||
const { sidebarWidth } = this.props | ||
return ( | ||
<Sidebar | ||
groups={this.props.groups} | ||
groupRenderer={this.props.groupRenderer} | ||
keys={this.props.keys} | ||
width={this.props.sidebarWidth} | ||
lineHeight={this.props.lineHeight} | ||
groupHeights={groupHeights} | ||
height={height} | ||
headerHeight={headerHeight} | ||
headerPosition={this.state.headerPosition} | ||
stickyOffset={this.props.stickyOffset} | ||
fixedHeader={this.props.fixedHeader} | ||
> | ||
{this.props.sidebarContent} | ||
</Sidebar> | ||
sidebarWidth != null && | ||
sidebarWidth > 0 && ( | ||
<Sidebar | ||
groups={this.props.groups} | ||
groupRenderer={this.props.groupRenderer} | ||
keys={this.props.keys} | ||
width={this.props.sidebarWidth} | ||
lineHeight={this.props.lineHeight} | ||
groupHeights={groupHeights} | ||
height={height} | ||
/> | ||
) | ||
) | ||
} | ||
rightSidebar(height, groupHeights, headerHeight) { | ||
rightSidebar(height, groupHeights) { | ||
const { rightSidebarWidth } = this.props | ||
return ( | ||
<Sidebar | ||
groups={this.props.groups} | ||
keys={this.props.keys} | ||
isRightSidebar | ||
width={this.props.rightSidebarWidth} | ||
lineHeight={this.props.lineHeight} | ||
groupHeights={groupHeights} | ||
height={height} | ||
headerHeight={headerHeight} | ||
headerPosition={this.state.headerPosition} | ||
stickyOffset={this.props.stickyOffset} | ||
fixedHeader={this.props.fixedHeader} | ||
> | ||
{this.props.rightSidebarContent} | ||
</Sidebar> | ||
rightSidebarWidth != null && | ||
rightSidebarWidth > 0 && ( | ||
<Sidebar | ||
groups={this.props.groups} | ||
keys={this.props.keys} | ||
isRightSidebar | ||
width={this.props.rightSidebarWidth} | ||
lineHeight={this.props.lineHeight} | ||
groupHeights={groupHeights} | ||
height={height} | ||
/> | ||
) | ||
) | ||
@@ -1331,3 +1102,2 @@ } | ||
stackItems, | ||
fullUpdate, | ||
itemHeightRatio | ||
@@ -1372,6 +1142,3 @@ } = this.props | ||
resizingEdge, | ||
resizeTime, | ||
fullUpdate, | ||
visibleTimeStart, | ||
visibleTimeEnd | ||
resizeTime | ||
}) | ||
@@ -1409,3 +1176,3 @@ | ||
handleDoubleClick = e => { | ||
handleScrollDoubleClick = e => { | ||
const { | ||
@@ -1460,3 +1227,3 @@ canvasTimeStart, | ||
handleCanvasContextMenu = e => { | ||
handleScrollContextMenu = e => { | ||
const { | ||
@@ -1562,3 +1329,5 @@ canvasTimeStart, | ||
timeSteps, | ||
showCursorLine | ||
showCursorLine, | ||
clickTolerance, | ||
traditionalZoom | ||
} = this.props | ||
@@ -1568,3 +1337,2 @@ const { | ||
resizingItem, | ||
isDragging, | ||
width, | ||
@@ -1585,3 +1353,5 @@ visibleTimeStart, | ||
if (draggingItem || resizingItem) { | ||
const isInteractingWithItem = !!draggingItem || !!resizingItem | ||
if (isInteractingWithItem) { | ||
const stackResults = this.stackItems( | ||
@@ -1605,8 +1375,2 @@ items, | ||
const scrollComponentStyle = { | ||
width: `${width}px`, | ||
height: `${height + 20}px`, | ||
cursor: isDragging ? 'move' : 'default' | ||
} | ||
const canvasComponentStyle = { | ||
@@ -1623,16 +1387,29 @@ width: `${canvasWidth}px`, | ||
> | ||
{this.header( | ||
canvasTimeStart, | ||
zoom, | ||
canvasTimeEnd, | ||
canvasWidth, | ||
minUnit, | ||
timeSteps, | ||
headerLabelGroupHeight, | ||
headerLabelHeight | ||
)} | ||
<div style={outerComponentStyle} className="rct-outer"> | ||
{sidebarWidth > 0 | ||
? this.sidebar(height, groupHeights, headerHeight) | ||
: null} | ||
<div | ||
ref={el => (this.scrollComponent = el)} | ||
className="rct-scroll" | ||
style={scrollComponentStyle} | ||
{sidebarWidth > 0 ? this.sidebar(height, groupHeights) : null} | ||
<ScrollElement | ||
scrollRef={el => (this.scrollComponent = el)} | ||
width={width} | ||
height={height} | ||
clickTolerance={clickTolerance} | ||
onWheelZoom={this.handleWheelZoom} | ||
traditionalZoom={traditionalZoom} | ||
onClick={this.scrollAreaClick} | ||
onScroll={this.onScroll} | ||
onWheel={this.onWheel} | ||
onMouseDown={this.handleMouseDown} | ||
onMouseMove={this.handleMouseMove} | ||
onMouseUp={this.handleMouseUp} | ||
onMouseLeave={this.handleMouseLeave} | ||
isInteractingWithItem={isInteractingWithItem} | ||
onDoubleClick={this.handleScrollDoubleClick} | ||
onMouseEnter={this.handleScrollMouseEnter} | ||
onMouseLeave={this.handleScrollMouseLeave} | ||
onMouseMove={this.handleScrollMouseMove} | ||
onContextMenu={this.handleScrollContextMenu} | ||
> | ||
@@ -1643,7 +1420,2 @@ <div | ||
style={canvasComponentStyle} | ||
onDoubleClick={this.handleDoubleClick} | ||
onMouseEnter={this.handleCanvasMouseEnter} | ||
onMouseLeave={this.handleCanvasMouseLeave} | ||
onMouseMove={this.handleCanvasMouseMove} | ||
onContextMenu={this.handleCanvasContextMenu} | ||
> | ||
@@ -1680,12 +1452,2 @@ {this.items( | ||
)} | ||
{this.header( | ||
canvasTimeStart, | ||
zoom, | ||
canvasTimeEnd, | ||
canvasWidth, | ||
minUnit, | ||
timeSteps, | ||
headerLabelGroupHeight, | ||
headerLabelHeight | ||
)} | ||
{mouseOverCanvas && showCursorLine | ||
@@ -1719,5 +1481,5 @@ ? this.cursorLine( | ||
</div> | ||
</div> | ||
</ScrollElement> | ||
{rightSidebarWidth > 0 | ||
? this.rightSidebar(height, groupHeights, headerHeight) | ||
? this.rightSidebar(height, groupHeights) | ||
: null} | ||
@@ -1724,0 +1486,0 @@ </div> |
@@ -130,6 +130,3 @@ import moment from 'moment' | ||
resizingEdge, | ||
resizeTime, | ||
fullUpdate, | ||
visibleTimeStart, | ||
visibleTimeEnd | ||
resizeTime | ||
}) { | ||
@@ -157,25 +154,2 @@ const itemStart = | ||
let clippedLeft = false | ||
let clippedRight = false | ||
if (fullUpdate) { | ||
if (!isDragging && (visibleTimeStart > x + w || visibleTimeEnd < x)) { | ||
return null | ||
} | ||
if (visibleTimeStart > x) { | ||
w -= visibleTimeStart - x | ||
x = visibleTimeStart | ||
if (isDragging && w < 0) { | ||
x += w | ||
w = 0 | ||
} | ||
clippedLeft = true | ||
} | ||
if (x + w > visibleTimeEnd) { | ||
w -= x + w - visibleTimeEnd | ||
clippedRight = true | ||
} | ||
} | ||
const ratio = | ||
@@ -189,5 +163,3 @@ 1 / coordinateToTimeRatio(canvasTimeStart, canvasTimeEnd, canvasWidth) | ||
originalLeft: itemTimeStart, | ||
collisionWidth: collisionW, | ||
clippedLeft, | ||
clippedRight | ||
collisionWidth: collisionW | ||
} | ||
@@ -252,5 +224,5 @@ | ||
export function stack(items, groupOrders, lineHeight, headerHeight, force) { | ||
export function stack(items, groupOrders, lineHeight, force) { | ||
var i, iMax | ||
var totalHeight = headerHeight | ||
var totalHeight = 0 | ||
@@ -321,6 +293,6 @@ var groupHeights = [] | ||
export function nostack(items, groupOrders, lineHeight, headerHeight, force) { | ||
export function nostack(items, groupOrders, lineHeight, force) { | ||
var i, iMax | ||
var totalHeight = headerHeight | ||
var totalHeight = 0 | ||
@@ -327,0 +299,0 @@ var groupHeights = [] |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
81
703
357196
45
8301