Skip to content

Latest commit

 

History

History
399 lines (322 loc) · 11.7 KB

README.md

File metadata and controls

399 lines (322 loc) · 11.7 KB

TableSchedule.js

Vanilla JS table schedule with DnD support.

Demo

In this document, event or event item stands for event item in the schedule, while Event stands for JavaScript Event object.

Contents

Features

What TableSchedule.js provides:

  • Drag-and-Drop to trigger aed (i.e. add / edit / delete) Events.
  • Quantization or snap-to-grid by default when DnD, switchable by holding SHIFT key.
  • APIs to aed event items programmatically.
  • Manages events data and makes them accessible through custom Events as well as an instance property.

What it doesn't provide:

  • UI components for filling forms / confirmation / changing dates or whatever. It only provides an interface for displaying and manipulating event items. Say if you're using Bootstrap, you might need to initialize a form modal when add / edit Events fire.

Getting Started

Installing

npm

npm install table-schedule

then

import TableSchedule from 'table-schedule'

or

var TableSchedule = require('table-schedule')

or

<script src="/path/to/table-schedule.js"></script>

and don't forget css

<link rel="stylesheet" href="/path/to/table-schedule.css">

Usage

Prepare a <TABLE> element, wrapped in an overflow:auto element.

<div style="overflow:auto">
  <table class="table-schedule">
  </table>
</div>

Initiate the instance.

var ts = new TableSchedule('.table-schedule')

The constructor takes two params:

  • element
    • Type: CSS selector or HTMLElement
    • The container for the widget, must be a <TABLE> element or equivalent selector
  • options (optional)

Then add Event handlers:

var $ts = document.querySelector('.table-schedule')
$ts.addEventListener('create', function(e) {
    const { item } = e.detail
    // Assume you have a very convenient prompt dialog like this
    fantasyDialog({
      fields: [
        // you can put date time values from e.detail into the form
        {label: 'Date', type: 'date', initValue: item.date},
        {label: 'Start Time', type: 'time', initValue: item.start},
        {label: 'End Time', type: 'time', initValue: item.end},
        // you might need other data
        {label: 'Title', type: 'text'},
        {label: 'Content', type: 'textarea'},
      ],
      onConfirm: function(load) { // load contains all field values
        ts.addEvent({
          date: load[0],
          start: load[1],
          end: load[2],
          title: load[3],
          content: load[4]
        })
      }
    })
})
$ts.addEventListener('modify', function(e) {
    const { item, coords, mod } = e.detail
    // if you don't need a form to modify other information
    var modified = Object.assign({}, item, mod)
    ts.updateEvent(coords, modified)
    // else you still need to provide a form for input
})
$ts.addEventListener('remove', function(e) {
    const { coords } = e.detail
    ts.deleteEvent(coords)
})

All events' data are stored in the instance's events property, which is a 2-dimensional array.

instance.events[i][j] is the j-th (starts from 0) added event on i-th (starts from 0) date among the current period.

These two indices together bind the event item's UI element and its data, and I'll call them a pair of coordinates for the event item in the rest of the document.

Options

num

  • Type: Number
  • Default: 7

Length of the period.

startDate

  • Type: Date
  • Default: new Date()

Initial start date of the period.

dayStart

  • Type: Number
  • Default: 6

Start hour of the day.

dayEnd

  • Type: Number
  • Default: 22

End hour of the day.

gap

  • Type: Number
  • Default: 10

Equivalent minutes of cell height.

quantizing

  • Type: Boolean
  • Default: true

Use quantization without(true) or with(false) holding SHIFT key.

createThreshold

  • Type: Number
  • Default: 10

In minutes. When a new event is drawn, create Event only fires if the event item's duration equals or is longer than this. When change event item's duration (by dragging the handle), delete Event will fire when duration is shorter than this.

stretchThreshold

  • Type: Number
  • Default: 5

In minutes. When dragging an event item's handle, the item's duration (height) won't start to change until mouse / finger moved equivalent pixels (default is half of cell height) in Y axis.

moveYThreshold

  • Type: Number
  • Default: 5

In minutes. When dragging an event item, the item won't start to move until mouse / finger moved equivalent pixels (default is half of cell height) in Y axis.

dateFormat

  • Type: function
  • Default: date => {format(date, 'MM/dd')},

The date texts displayed in the table header take what this function returns.

Params:

  • date - Date object

directChange

  • Type: Boolean|Array
  • Default: false

Whether to automatically apply aed changes to the event items when corresponding Event fires.

When it is true, all changes will be applied.

Or it can be an array of any of:

  • create: apply add changes
  • modify: apply all edit changes
  • start/end/date/datetime: apply specific edit changes. See modify
  • remove: apply delete changes

extraDataset

  • Type: Object
  • Default: null

Add extra dataset entries to event elements. For example, {id: 'ID'} will add a data-id to the element with the value from event item's ID property.

labelGroups

  • Type: Boolean
  • Default: false

Whether to show group headers below the top table header.

groupHeaderText

  • Type: Object|String|function
  • Default: null

What text to show in group headers.

  • null - eventItem.group
  • String - a property value from eventItem
  • function - a function which takes eventItem.group, e.g. group => ('Group - ' + group)

Custom Events

These 3 CustomEvents are fired on mouseup or touchend, which is after a drag-and-drop action on the schedule. All of them come with a detail property which contains useful information.

create

Fired when a new event item is drawn. Event.detail:

  • item Object: new event item
    • date String: date string formatted in yyyy/MM/dd
    • start String: start time, formatted in HH:mm
    • end String: end time, formatted in HH:mm

modify

Fired when change either date / start / end of an event item. Event.detail:

  • item Object: reference to the original event item, which is untouched since added through instance.addEvent
  • coords Array: the coordinates for the event item
  • mod Object: the modification made to the event item
    • type String: type of the modification, can be either of:
      • 'start'- when DnD an event item within its date and the start time is changed
      • 'end' - when DnD the handle bar at the event item's bottom and the end time is changed
      • 'date' - when DnD an event item to another date with start time not changed
      • 'datetime' - when drag-n-drap an event item to another date and start time
    • date String: the event item's date after modifying
    • start String: the event item's start time after modifying
    • end String: the event item's end time after modifying

remove

Fired when drag an event item's handle and change the event item's duration to a value smaller than options.createThreshold. Event.detail:

  • item Object: reference to the original event item
  • coords Array: the coordinates for the event item

APIs

addEvent(eventItem)

  • eventItem
    • Type: Object
  • (return value)
    • Type: this

Add new event item to the schedule.

eventItem MUST include these properties:

  • date Sting|Object: a date string that can be parsed by Date.parse or a Date object
  • start String: start time, in HH:mm format
  • end String: end time, in HH:mm format

may include these properties:

  • title String: event item's title
  • content String: event item's content, which will be rendered using innerHTML
  • style Object: additional styles you want to apply to the event item.
  • className String: additional classnames you want to apply to the event item.
  • group String|Number: event items with a same group key will be grouped together, while event items with different group keys will never be in a same column.

Besides all above which will take effect on display, you can put any properties you want in it, like an ID for the event or whatever. eventItem is stored in the instance untouched and is available in Event.detail.

updateEvent(coords, modified)

  • coords
    • Type: Array
    • coordinates
  • modified
    • Type: Object
    • modified eventItem
  • (return value)
    • Type: this

Update the event item instance.events[coords[0]][coords[1]] to modified.

deleteEvent(coords)

  • coords
    • Type: Array
  • (return value)
    • Type: this

Delete event item instance.events[coords[0]][coords[1]]

clear(which)

  • which
    • Type: any
    • clear event items on which date(s)
  • (return value)
    • Type: this

which can be:

  • Number: Date index, value constrained in [0, options.num]
  • String: Date string
  • Date: Date object
  • Array: array of values of any types above
  • undefined: to clear all

changeStartDate(date)

  • date
    • Type: Object
    • Target start date
  • (return value)
    • Type: this

Change the start date to target date, will also alter instance.events accordingly.

rerender()

  • (return value)
    • Type: this

Rerender all event items in the schedule.

updateRect()

  • (return value)
    • Type: this

Update the container's size and position. You'll need to call this after any changes that affect the container's size and position, e.g. the container is switched from hidden to shown, a toolbar appears above the widget, etc.

destroy()

Destroy the instance.

getEvent(elem)

  • elem
    • Type: Object
    • the event item element
  • (return value)
    • Type: Object
    • a reference to the corresponding event item stored in instance.events or null.

getElem(eventItem)

  • eventItem
    • Type: Object
    • the event item
  • (return value)
    • Type: Object
    • a reference to the corresponding event item element in the schedule or null

events

  • Type: Array

All event items.

Browser Support

Not tested yet but supposed to and should work in all latest modern browsers.

Contribute

If you find this widget useful and are willing to help, any issue or PR is welcomed.

License

MIT.