Skip to content

Commit

Permalink
option to fully update the calendar at every scroll event
Browse files Browse the repository at this point in the history
  • Loading branch information
mariusandra committed Nov 20, 2016
1 parent 15cffed commit d2e3454
Show file tree
Hide file tree
Showing 7 changed files with 80 additions and 20 deletions.
10 changes: 10 additions & 0 deletions README.md
Expand Up @@ -127,6 +127,16 @@ How does the header (the scrolling part with dates) behave if not all of the gro
* `fixed` - the header is always on the screen
* `none` (default) - the header is always at the top of the component

### 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).

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`.

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.

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.

### zIndexStart
What z-index value do the header and the sidebar have. Default `10`

Expand Down
1 change: 1 addition & 0 deletions index.html
Expand Up @@ -65,6 +65,7 @@
groups: groups,
items: items,
fixedHeader: 'fixed',
fullUpdate: true,

canMove: true, // defaults
canResize: 'right',
Expand Down
23 changes: 15 additions & 8 deletions src/lib/Timeline.js
Expand Up @@ -40,6 +40,7 @@ export default class ReactCalendarTimeline extends Component {
dragSnap: PropTypes.number,
minResizeWidth: PropTypes.number,
fixedHeader: PropTypes.oneOf(['fixed', 'absolute', 'none']),
fullUpdate: PropTypes.bool,
zIndexStart: PropTypes.number,
lineHeight: PropTypes.number,
headerLabelGroupHeight: PropTypes.number,
Expand Down Expand Up @@ -98,6 +99,7 @@ export default class ReactCalendarTimeline extends Component {
dragSnap: 1000 * 60 * 15, // 15min
minResizeWidth: 20,
fixedHeader: 'none', // fixed or absolute or none
fullUpdate: true,
zIndexStart: 10,
lineHeight: 30,
headerLabelGroupHeight: 30,
Expand Down Expand Up @@ -376,12 +378,14 @@ export default class ReactCalendarTimeline extends Component {
this.setState({ dimensionItems, height, groupHeights, groupTops })
}

// called when the visible time changes
updateScrollCanvas = (visibleTimeStart, visibleTimeEnd, forceUpdateDimensions, updatedItems, updatedGroups) => {
const oldCanvasTimeStart = this.state.canvasTimeStart
const oldZoom = this.state.visibleTimeEnd - this.state.visibleTimeStart
const newZoom = visibleTimeEnd - visibleTimeStart
const items = updatedItems || this.props.items
const groups = updatedGroups || this.props.groups
const { fullUpdate } = this.props

let newState = {
visibleTimeStart: visibleTimeStart,
Expand Down Expand Up @@ -416,11 +420,11 @@ export default class ReactCalendarTimeline extends Component {
}
}

if (resetCanvas || forceUpdateDimensions) {
if (resetCanvas || forceUpdateDimensions || fullUpdate) {
const canvasTimeStart = newState.canvasTimeStart ? newState.canvasTimeStart : oldCanvasTimeStart
const {
dimensionItems, height, groupHeights, groupTops
} = this.stackItems(items, groups, canvasTimeStart, visibleTimeStart, visibleTimeEnd, this.state.width)
} = this.stackItems(items, groups, canvasTimeStart, visibleTimeStart, visibleTimeEnd, this.state.width, fullUpdate)
newState.dimensionItems = dimensionItems
newState.height = height
newState.groupHeights = groupHeights
Expand Down Expand Up @@ -740,7 +744,7 @@ export default class ReactCalendarTimeline extends Component {
}

stackItems (items, groups, canvasTimeStart, visibleTimeStart, visibleTimeEnd, width) {
const { keys, dragSnap, lineHeight, headerLabelGroupHeight, headerLabelHeight, stackItems, itemHeightRatio } = this.props
const { keys, dragSnap, lineHeight, headerLabelGroupHeight, headerLabelHeight, stackItems, fullUpdate, itemHeightRatio } = this.props
const { draggingItem, dragTime, resizingItem, resizingEdge, resizeTime, newGroupOrder } = this.state
const zoom = visibleTimeEnd - visibleTimeStart
const canvasTimeEnd = canvasTimeStart + zoom * 3
Expand All @@ -753,9 +757,9 @@ export default class ReactCalendarTimeline extends Component {
let dimensionItems = visibleItems.map(item => {
return {
id: _get(item, keys.itemIdKey),
dimensions: calculateDimensions(
dimensions: calculateDimensions({
item,
groupOrders[_get(item, keys.itemGroupKey)],
order: groupOrders[_get(item, keys.itemGroupKey)],
keys,
canvasTimeStart,
canvasTimeEnd,
Expand All @@ -768,10 +772,13 @@ export default class ReactCalendarTimeline extends Component {
resizingEdge,
resizeTime,
newGroupOrder,
itemHeightRatio
)
itemHeightRatio,
fullUpdate,
visibleTimeStart,
visibleTimeEnd
})
}
})
}).filter(i => i.dimensions)

const stackingMethod = stackItems ? stack : nostack

Expand Down
14 changes: 14 additions & 0 deletions src/lib/Timeline.scss
Expand Up @@ -57,11 +57,25 @@ $weekend: rgba(250, 246, 225, 0.5);
border: $item-border;
z-index: 1;

&.clipped-left {
border-left: 0;
}

&.clipped-right {
border-right: 0;
}

&.selected {
background: $item-selected-background;
border: $item-selected-border;
z-index: 2;

&.clipped-left {
border-left: 0;
}
&.clipped-right {
border-right: 0;
}
&.can-move {
cursor: move;
}
Expand Down
12 changes: 7 additions & 5 deletions src/lib/items/Item.js
Expand Up @@ -315,18 +315,18 @@ export default class Item extends Component {
}

canResizeLeft (props = this.props) {
if (!props.canResizeLeft) {
if (!props.canResizeLeft || props.dimensions.clippedLeft) {
return false
}
let width = parseInt(this.props.dimensions.width, 10)
let width = parseInt(props.dimensions.width, 10)
return width >= props.minResizeWidth
}

canResizeRight (props = this.props) {
if (!props.canResizeRight) {
if (!props.canResizeRight || props.dimensions.clippedRight) {
return false
}
let width = parseInt(this.props.dimensions.width, 10)
let width = parseInt(props.dimensions.width, 10)
return width >= props.minResizeWidth
}

Expand Down Expand Up @@ -433,7 +433,9 @@ export default class Item extends Component {
(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}` : '')
(this.props.item.className ? ` ${this.props.item.className}` : '') +
(dimensions.clippedLeft ? ' clipped-left' : '') +
(dimensions.clippedRight ? ' clipped-right' : '')

const style = {
left: `${dimensions.left}px`,
Expand Down
3 changes: 2 additions & 1 deletion src/lib/items/Items.js
Expand Up @@ -99,7 +99,8 @@ export default class Items extends Component {

return (
<div className='rct-items'>
{visibleItems.map(item => <Item key={_get(item, itemIdKey)}
{visibleItems.filter(item => sortedDimensionItems[_get(item, itemIdKey)])
.map(item => <Item key={_get(item, itemIdKey)}
item={item}
keys={this.props.keys}
order={groupOrders[_get(item, itemGroupKey)]}
Expand Down
37 changes: 31 additions & 6 deletions src/lib/utils.js
Expand Up @@ -90,7 +90,7 @@ export function coordinateToTimeRatio (canvasTimeStart, canvasTimeEnd, canvasWid
return (canvasTimeEnd - canvasTimeStart) / canvasWidth
}

export function calculateDimensions (item, order, keys, canvasTimeStart, canvasTimeEnd, canvasWidth, dragSnap, lineHeight, draggingItem, dragTime, resizingItem, resizingEdge, resizeTime, newGroupOrder, itemHeightRatio) {
export function calculateDimensions ({ item, order, keys, canvasTimeStart, canvasTimeEnd, canvasWidth, dragSnap, lineHeight, draggingItem, dragTime, resizingItem, resizingEdge, resizeTime, newGroupOrder, itemHeightRatio, fullUpdate, visibleTimeStart, visibleTimeEnd }) {
var itemId = _get(item, keys.itemIdKey)
var itemTimeStart = _get(item, keys.itemTimeStartKey)
var itemTimeEnd = _get(item, keys.itemTimeEndKey)
Expand All @@ -101,9 +101,9 @@ export function calculateDimensions (item, order, keys, canvasTimeStart, canvasT
const itemStart = (isResizing && resizingEdge === 'left' ? resizeTime : itemTimeStart)
const itemEnd = (isResizing && resizingEdge === 'right' ? resizeTime : itemTimeEnd)

const x = isDragging ? dragTime : itemStart
let x = isDragging ? dragTime : itemStart

const w = Math.max(itemEnd - itemStart, dragSnap)
let w = Math.max(itemEnd - itemStart, dragSnap)

let collisionX = itemStart
let collisionW = w
Expand All @@ -117,8 +117,31 @@ export function calculateDimensions (item, order, keys, canvasTimeStart, canvasT
}
}

const h = lineHeight * itemHeightRatio
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 = 1 / coordinateToTimeRatio(canvasTimeStart, canvasTimeEnd, canvasWidth)
const h = lineHeight * itemHeightRatio

const dimensions = {
left: (x - canvasTimeStart) * ratio,
Expand All @@ -127,11 +150,13 @@ export function calculateDimensions (item, order, keys, canvasTimeStart, canvasT
height: h,
order: isDragging ? newGroupOrder : order,
stack: true,
lineHeight: lineHeight,
collisionLeft: collisionX,
originalLeft: itemTimeStart,
collisionWidth: collisionW,
isDragging: isDragging
lineHeight,
isDragging,
clippedLeft,
clippedRight
}

return dimensions
Expand Down

0 comments on commit d2e3454

Please sign in to comment.