Less querying of the DOM - use state instead + deselect all items if click outside

This commit is contained in:
Josh Johnson 2016-04-12 20:16:36 +01:00
parent 4947da2b43
commit cd43799258
5 changed files with 75 additions and 31 deletions

File diff suppressed because one or more lines are too long

View file

@ -2,22 +2,22 @@ export const addItem = (value, id) => {
return { return {
type: 'ADD_ITEM', type: 'ADD_ITEM',
value: value, value: value,
id: id id: id,
} }
}; };
export const removeItem = (id) => { export const removeItem = (id) => {
return { return {
type: 'REMOVE_ITEM', type: 'REMOVE_ITEM',
id: id id: id,
} }
}; };
export const selectItem = (id, value) => { export const selectItem = (id, selected) => {
return { return {
type: 'SELECT_ITEM', type: 'SELECT_ITEM',
id: id, id: id,
value: value selected: selected,
} }
}; };
@ -25,6 +25,14 @@ export const addOption = (value, id) => {
return { return {
type: 'ADD_OPTION', type: 'ADD_OPTION',
value: value, value: value,
id: id id: id,
}
};
export const selectOption = (id, selected) => {
return {
type: 'SELECT_OPTION',
id: id,
selected: selected,
} }
}; };

View file

@ -2,7 +2,7 @@
import { createStore } from 'redux'; import { createStore } from 'redux';
import rootReducer from './reducers/index.js'; import rootReducer from './reducers/index.js';
import { addItem, removeItem, selectItem, addOption } from './actions/index'; import { addItem, removeItem, selectItem, addOption, selectOption } from './actions/index';
import { hasClass, wrap, getSiblings, isType, strToEl, extend } from './lib/utils.js'; import { hasClass, wrap, getSiblings, isType, strToEl, extend } from './lib/utils.js';
export class Choices { export class Choices {
@ -191,13 +191,12 @@ export class Choices {
let handleBackspaceKey = () => { let handleBackspaceKey = () => {
if(this.options.removeItems) { if(this.options.removeItems) {
let inputIsFocussed = this.input === document.activeElement;
let lastItem = items[items.length - 1]; let lastItem = items[items.length - 1];
let selectedItems = items.filter((item) => { let selectedItems = items.filter((item) => {
return item.selected === true; return item.selected === true;
}); });
let inputIsFocussed = this.input === document.activeElement;
if(items && lastItem && !this.options.editItems && inputIsFocussed && this.options.removeItems) { if(items && lastItem && !this.options.editItems && inputIsFocussed && this.options.removeItems) {
this.selectItem(lastItem); this.selectItem(lastItem);
} }
@ -229,6 +228,10 @@ export class Choices {
onClick(e) { onClick(e) {
// If click is affecting a child node of our element // If click is affecting a child node of our element
if(this.containerOuter.contains(e.target)) { if(this.containerOuter.contains(e.target)) {
const state = this.store.getState();
const items = state.items;
const options = state.options;
if(this.input !== document.activeElement) { if(this.input !== document.activeElement) {
this.input.focus(); this.input.focus();
} }
@ -239,20 +242,16 @@ export class Choices {
let handleClick = (item) => { let handleClick = (item) => {
if(this.options.removeItems) { if(this.options.removeItems) {
let passedId = item.getAttribute('data-choice-id'); let passedId = item.getAttribute('data-choice-id');
let items = this.list.children;
// We only want to select one item with a click // We only want to select one item with a click
// so we deselect any items that aren't the target // so we deselect any items that aren't the target
for (var i = 0; i < items.length; i++) { items.forEach((item) => {
let singleItem = items[i]; if(item.id === parseInt(passedId) && !item.selected) {
let id = singleItem.getAttribute('data-choice-id');; this.selectItem(item);
if(id === passedId && !singleItem.classList.contains(this.options.classNames.selectedState)) {
this.selectItem(singleItem);
} else { } else {
this.deselectItem(singleItem); this.deselectItem(item);
} }
} });
} }
} }
@ -260,13 +259,24 @@ export class Choices {
} }
if(e.target.hasAttribute('data-choice-selectable')) { if(e.target.hasAttribute('data-choice-selectable')) {
let item = e.target; let id = e.target.getAttribute('data-choice-id');
let value = e.target.getAttribute('data-choice-value');
this.addItem(value); let option = options.find((option) => {
return option.id === parseInt(id);
});
if(option) {
this.selectOption(id);
this.addItem(option.value);
}
} }
} else if(this.dropdown && this.dropdown.classList.contains(this.options.classNames.activeState)) { } else {
this.toggleDropdown(); // Click is outside of our element so close dropdown and de-select items
this.deselectAll();
if(this.dropdown && this.dropdown.classList.contains(this.options.classNames.activeState)) {
this.toggleDropdown();
}
} }
} }
@ -328,7 +338,8 @@ export class Choices {
* @return * @return
*/ */
selectItem(item) { selectItem(item) {
let id = item.getAttribute('data-choice-id'); if(!item) return;
let id = item.id;
this.store.dispatch(selectItem(id, true)); this.store.dispatch(selectItem(id, true));
} }
@ -338,7 +349,8 @@ export class Choices {
* @return * @return
*/ */
deselectItem(item) { deselectItem(item) {
let id = item.getAttribute('data-choice-id'); if(!item) return;
let id = item.id;
this.store.dispatch(selectItem(id, false)); this.store.dispatch(selectItem(id, false));
} }
@ -347,11 +359,25 @@ export class Choices {
* @param {Array} items Array of items to select * @param {Array} items Array of items to select
* @return * @return
*/ */
selectAll(items) { selectAll() {
for (let i = 0; i < items.length; i++) { const state = this.store.getState();
let item = items[i]; const items = state.items;
items.forEach((item) => {
this.selectItem(item); this.selectItem(item);
}; });
}
deselectAll() {
const state = this.store.getState();
const items = state.items;
items.forEach((item) => {
this.deselectItem(item);
});
}
selectOption(id) {
if(!id) return;
this.store.dispatch(selectOption(id));
} }
/** /**
@ -632,7 +658,7 @@ export class Choices {
// Add each option to dropdown // Add each option to dropdown
options.forEach((option) => { options.forEach((option) => {
const dropdownItem = strToEl(`<li class="${ this.options.classNames.item } ${ this.options.classNames.itemSelectable }" data-choice-selectable data-choice-value="${ option.value }">${ option.value }</li>`); const dropdownItem = strToEl(`<li class="${ this.options.classNames.item } ${ this.options.classNames.itemSelectable }" data-choice-selectable data-choice-id="${ option.id }" data-choice-value="${ option.value }">${ option.value }</li>`);
this.dropdown.appendChild(dropdownItem); this.dropdown.appendChild(dropdownItem);
}); });
} }

View file

@ -28,7 +28,7 @@ const items = (state = [], action) => {
case 'SELECT_ITEM': case 'SELECT_ITEM':
return state.map((item) => { return state.map((item) => {
if(item.id === parseInt(action.id)) { if(item.id === parseInt(action.id)) {
item.selected = action.value; item.selected = action.selected;
} }
return item; return item;

View file

@ -10,6 +10,16 @@ const options = (state = [], action) => {
}]; }];
return newState; return newState;
case 'SELECT_OPTION':
return state.map((option) => {
if(option.id === parseInt(action.id)) {
option.selected = action.value;
}
return option;
});
default: default:
return state; return state;
} }