React = require 'react' {Actions} = require("nylas-exports") CalendarBarItem = require("./calendar-bar-item") CalendarBarEventStore = require ("./calendar-bar-event-store") class CalendarBarRow constructor: (initialItem = null) -> @items = [] @last = 0 if initialItem @last = initialItem.event.end @items.push(initialItem) canHoldItem: (item) => item.event.start > @last addItem: (item) => @last = item.event.end @items.push(item) class CalendarBarMarker extends React.Component @displayName: "CalendarBarMarker" render: => classname = "marker" classname += " now" if @props.marker.now
class CalendarBar extends React.Component @displayName: "CalendarBar" constructor: (@props) -> @state = @_getStateFromStores() componentDidMount: => @unsubscribe = CalendarBarEventStore.listen @_onStoreChange # It's important that every React class explicitly stops listening to # atom events before it unmounts. Thank you event-kit # This can be fixed via a Reflux mixin componentWillUnmount: => @unsubscribe() if @unsubscribe render: => markers = @_getMarkers().map (marker) -> items = @_getItemsForEvents(@state.events) items = items.map (item) ->
{markers} {items}
_onStoreChange: => @setState @_getStateFromStores() _getStateFromStores: => events: CalendarBarEventStore.events() range: CalendarBarEventStore.range() _getMarkers: => range = @state.range now = (new Date).getTime()/1000 - range.start markers = [] for hour in [0..24] time = 60*60*hour markers.push xPercent: (time * 100) / (range.end - range.start) + "%" markers.push now: true xPercent: (now * 100) / (range.end - range.start) + "%" markers _getItemsForEvents: (events) => # Create an array of items with additional metadata needed for our view. # We compute the X and width of elements using their durations as a fraction # of the displayed range range = @state.range items = events.map (event) -> { event: event, z: event.start - range.start xPercent: (event.start - range.start) * 100 / (range.end - range.start) + "%", wPercent: (event.end - event.start) * 100 / (range.end - range.start) + "%" } # Compute the number of rows we need by assigning events to rows. This works by # creating virtual "row" objects which hold a series of non-overlapping events and # have a "last" timestamp. For each item, we iterate through the rows: # # - If the event fits in more than one row, we delete all but one of the rows. # This ensures that if we have two overlapping events, the next event that # does not overlap goes back to taking all of the available height. (Rows no # longer necessary) # # - If the event does not fit in any rows, we create a new row, and tell all of # the items in existing rows that they're now sharing space with a new row. rows = [new CalendarBarRow] for item in items for x in [rows.length-1..0] by -1 if rows[x].canHoldItem(item) rows.splice(item.rowIndex, 1) unless item.rowIndex is undefined rows[x].addItem(item) item.rowIndex = x if item.rowIndex is undefined rows.push(new CalendarBarRow(item)) item.rowIndex = rows.length - 1 for row in rows for item in row.items item.rowCount += 1 item.rowCount = rows.length # Now that each item knows what row it's in and how many rows are being displayed # alongside it, we can assign fractional positions to them. for item in items item.yPercent = (item.rowIndex / item.rowCount) * 100 + "%" item.hPercent = (100.0 / item.rowCount) + "%" items module.exports = CalendarBar