Skip to content

Instantly share code, notes, and snippets.

@median-man
Created January 17, 2018 14:27
Show Gist options
  • Save median-man/5b5ff8de285531511cca6c39b791b4de to your computer and use it in GitHub Desktop.
Save median-man/5b5ff8de285531511cca6c39b791b4de to your computer and use it in GitHub Desktop.
/*
Usage:
Create an instance of the class passing a selector string, callback for handling shift + click
on element completing a range, and a callback for handling a click on a single element.
The selector is a string passed to document.querySelector to set the container for the shiftGroup.
The onGroupClick callback is passed the original event object and an array containing the range
of selected elements.
The onItemClick callback is passed the original event object and the item containing the target
element of the event.
Example:
function handleSelection() {}
function handleItemClick() {}
const shiftGroup = new ShiftGroup('#group', handleSelection, handleItemClick);
*/
/**
* Class representing an element which contains a group of elements with shift + click handling
* on a range of elements from the group.
*/
class ShiftGroup {
/**
* Create a shiftGroup.
* @param {string} selector - Query selector for shift group container.
* @param {function} onGroupClick - Callback to handle group selection shift + click.
* @param {function} onItemClick - Callback to handle click on a single item in the group.
*/
constructor(selector, onGroupClick, onItemClick) {
this.lastItem = false;
this.container = document.querySelector(selector);
this.container.addEventListener('click', this.handleContainerClick.bind(this));
this.onGroupClick = onGroupClick;
this.onItemClick = onItemClick;
}
/**
* Gets array of item elements from the previously selected item to the target item (inclusive)
* and calls this.onGroupItem(event, selectedItems) where selectedItems is the array.
* @param {MouseEvent} event - MouseEvent triggered from descendant of this.container.
* @param {HTMLElement} targetItem - Group item from which the event was triggered.
*/
handleGroupClick(event, targetItem) {
const siblings = [...this.container.children];
const fromIndex = siblings.indexOf(targetItem);
const toIndex = siblings.indexOf(this.lastItem);
let selectedItems;
// get array of all items from the last selected item to the target item inclusive
if (fromIndex > toIndex) {
selectedItems = siblings.slice(toIndex, fromIndex + 1);
} else {
selectedItems = siblings.slice(fromIndex, toIndex + 1);
}
this.onGroupClick(event, selectedItems);
this.lastItem = false;
}
/**
* Assigns targetItem to lastItem property and passes the event and targetItem to callback.
* @param {MouseEvent} event - MouseEvent triggered from descendant of this.container.
* @param {HTMLElement} targetItem - Group item from which the event was triggered.
*/
handleItemClick(event, targetItem) {
this.lastItem = targetItem;
this.onItemClick(event, targetItem);
}
/**
* Determines if event is a range selection or single item and calls the corresponding handler.
* @param {MouseEvent} event
*/
handleContainerClick(event) {
// get the item of the group containing the event.target element
let targetItem = event.target;
while (targetItem.parentNode !== this.container) {
targetItem = targetItem.parentNode;
}
// if shift key was pressed and lastItem was set then proceed to group event handler
if (this.handleGroupClick && event.shiftKey && this.lastItem) {
return this.handleGroupClick(event, targetItem);
}
return this.handleItemClick ? this.handleItemClick(event, targetItem) : null;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment