Skip to content

Instantly share code, notes, and snippets.

@sglanzer-deprecated
Created January 9, 2017 20:56
Show Gist options
  • Save sglanzer-deprecated/8622da8bc3ffeaca1d369494b08a7c66 to your computer and use it in GitHub Desktop.
Save sglanzer-deprecated/8622da8bc3ffeaca1d369494b08a7c66 to your computer and use it in GitHub Desktop.
click (event) {
// Acceptable event modifiers for range selection
const isSimpleClick = Ember.ViewUtils.isSimpleClick(event)
const isSpecificSelect = (new window.UAParser()).getOS() === 'Mac OS' ? event.ctrlKey : event.metaKey // TODO Move instance to a service
const isRangeSelect = event.shiftKey
// Only process simple clicks or clicks with the acceptable modifiers
if (isSimpleClick || isSpecificSelect || isRangeSelect) {
event.preventDefault()
event.stopPropagation()
// Each list item provides a checkbox which behaves as a specific select
const isCheckboxSelect = $(event.target).hasClass('frost-list-selection-indicator')
this.onSelect({
isRangeSelect,
isSpecificSelect: isSpecificSelect || isCheckboxSelect,
item: this.get('model')
})
}
}
this.set('selectedItems', []) // TODO prefix
this.set('rangeState', {}) // TODO prefix
this.set('actions.select', function ({isRangeSelect, isSpecificSelect, item}) { // TODO does this need a prefix?
const items = this.get('items') // TODO prefix
const selectedItems = this.get('selectedItems') // TODO prefix
const rangeState = this.get('rangeState') // TODO prefix
// Selects are proccessed in order of precedence: specific, range, basic
if (isSpecificSelect) {
selection.specific(selectedItems, item, rangeState)
} else if (isRangeSelect) {
selection.range(items, selectedItems, item, rangeState)
} else {
selection.basic(selectedItems, item, rangeState)
}
})
import Ember from 'ember'
const {isNone} = Ember
export default {
/**
* Basic selection acts conditionally based on the presence of additional selections.
*
* If no other selections are present the selection simply toggles the given item's selection state.
*
* If other selections are present the selection clears the other selections, but positively selects
* the given item.
*
* @param {Object[]} selectedItems - currently selected items
* @param {Object} item - selection event target
* @param {Object} rangeState - tracking the anchor and endpoint
*/
basic (selectedItems, item, rangeState) {
// If a previous set of selections is present
if (selectedItems.get('length') > 1) {
// Clear the other selections and select the item
selectedItems.setObjects([item])
// Set the range anchor
rangeState['anchor'] = item
// New anchor, clear any previous endpoint
rangeState['endpoint'] = null
} else {
// Toggle the item selection
const isCurrentlySelected = selectedItems.indexOf(item) >= 0
const isSelected = !isCurrentlySelected
if (isSelected) {
selectedItems.pushObject(item)
} else {
selectedItems.removeObject(item)
}
// Set the range anchor if selected, otherwise clear the anchor
rangeState['anchor'] = isSelected ? item : null
// New or no anchor, clear any previous endpoint
rangeState['endpoint'] = null
}
},
/**
* Range selection requires an anchor and an endpoint; items between the
* anchor and endpoint are added to the selected items (inclusive).
* This means that a range selection event is either setting an anchor
* or selecting items between the anchor and a new endpoint
*
* @param {Object[]} items - all items available
* @param {Object[]} selectedItems - currently selected items
* @param {Object} item - selection event target
* @param {Object} rangeState - tracking the anchor and endpoint
*/
/* eslint-disable complexity */
range (items, selectedItems, item, rangeState) {
// If an anchor isn't set, then set the anchor and exit
const rangeAnchor = rangeState['anchor']
if (isNone(rangeAnchor)) {
// Range select is always a positive selection (no deselect)
rangeState['anchor'] = item
// New anchor, clear any previous endpoint
rangeState['endpoint'] = null
// Add the anchor to the selected items
selectedItems.pushObject(item)
return
}
// Find the indicies of the anchor and endpoint
const anchor = items.indexOf(rangeState['anchor'])
const endpoint = items.indexOf(item)
// Select all of the items between the anchor and the item (inclusive)
if (anchor < endpoint) {
selectedItems.pushObjects(items.slice(anchor, endpoint + 1))
} else {
selectedItems.pushObjects(items.slice(endpoint, anchor + 1))
}
// If an endpoint was already selected remove selected items that were
// in the previous range but aren't in the new range
const previousEndpoint = items.indexOf(rangeState['endpoint'])
if (previousEndpoint >= 0) {
// If both endpoints are above the anchor
if (anchor < endpoint && anchor < previousEndpoint) {
// and the new range encompasses less
if (endpoint < previousEndpoint) {
selectedItems.removeObjects(items.slice(endpoint + 1, previousEndpoint + 1))
}
// If both endpoints are below the anchor
} else if (anchor > endpoint && anchor > previousEndpoint) {
// and the new range encompasses less
if (endpoint > previousEndpoint) {
selectedItems.removeObjects(items.slice(previousEndpoint, endpoint))
}
// Pivoted over the anchor, deselect all items in the previous range minus the anchor
} else if (anchor > previousEndpoint) {
selectedItems.removeObjects(items.slice(previousEndpoint, anchor))
} else {
selectedItems.removeObjects(items.slice(anchor + 1, previousEndpoint + 1))
}
}
// Store the new endpoint
rangeState['endpoint'] = item
},
/* eslint-enable complexity */
/**
* Specific selection toggles the current selection state for a given
* item without impacting the selection state for any other items
*
* @param {Object[]} selectedItems - currently selected items
* @param {Object} item - selection event target
* @param {Object} rangeState - tracking the anchor and endpoint
*/
specific (selectedItems, item, rangeState) {
const isCurrentlySelected = selectedItems.indexOf(item) >= 0
const isSelected = !isCurrentlySelected
// Set the range anchor if selected, otherwise clear the anchor
rangeState['anchor'] = isSelected ? item : null
// New or no anchor, clear any previous endpoint
rangeState['endpoint'] = null
// Store the selection
if (isSelected) {
selectedItems.pushObject(item)
} else {
selectedItems.removeObject(item)
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment