mirror of
https://github.com/Choices-js/Choices.git
synced 2024-06-13 03:12:41 +02:00
Use DOM querying for highlighting options - less rendering
This commit is contained in:
parent
9dc23e86bf
commit
cbfe38e937
4
assets/scripts/dist/bundle.js
vendored
4
assets/scripts/dist/bundle.js
vendored
File diff suppressed because one or more lines are too long
|
@ -24,33 +24,17 @@ export const selectItem = (id, selected) => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const addOption = (value, label, id, groupId, highlighted, disabled) => {
|
export const addOption = (value, label, id, groupId, disabled) => {
|
||||||
return {
|
return {
|
||||||
type: 'ADD_OPTION',
|
type: 'ADD_OPTION',
|
||||||
value,
|
value,
|
||||||
label,
|
label,
|
||||||
id,
|
id,
|
||||||
groupId,
|
groupId,
|
||||||
highlighted,
|
|
||||||
disabled,
|
disabled,
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const selectOption = (id, selected) => {
|
|
||||||
return {
|
|
||||||
type: 'SELECT_OPTION',
|
|
||||||
id,
|
|
||||||
selected,
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const highlightOption = (id) => {
|
|
||||||
return {
|
|
||||||
type: 'HIGHLIGHT_OPTION',
|
|
||||||
id,
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const filterOptions = (results) => {
|
export const filterOptions = (results) => {
|
||||||
return {
|
return {
|
||||||
type: 'FILTER_OPTIONS',
|
type: 'FILTER_OPTIONS',
|
||||||
|
|
|
@ -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, selectOption, highlightOption, filterOptions, activateOptions, addGroup } from './actions/index';
|
import { addItem, removeItem, selectItem, addOption, filterOptions, activateOptions, addGroup } from './actions/index';
|
||||||
import { hasClass, wrap, getSiblings, isType, strToEl, extend, getWidthOfInput, debounce } from './lib/utils.js';
|
import { hasClass, wrap, getSiblings, isType, strToEl, extend, getWidthOfInput, debounce } from './lib/utils.js';
|
||||||
import Sifter from 'sifter';
|
import Sifter from 'sifter';
|
||||||
|
|
||||||
|
@ -249,13 +249,14 @@ export class Choices {
|
||||||
const upKey = 38;
|
const upKey = 38;
|
||||||
const downKey = 40;
|
const downKey = 40;
|
||||||
const hasActiveDropDown = this.dropdown && this.dropdown.classList.contains(this.options.classNames.activeState);
|
const hasActiveDropDown = this.dropdown && this.dropdown.classList.contains(this.options.classNames.activeState);
|
||||||
|
const hasItems = this.list && this.list.children;
|
||||||
|
|
||||||
// If we are typing in the input
|
// If we are typing in the input
|
||||||
if(e.target === this.input) {
|
if(e.target === this.input) {
|
||||||
// this.input.style.width = getWidthOfInput(this.input);
|
// this.input.style.width = getWidthOfInput(this.input);
|
||||||
|
|
||||||
// If CTRL + A or CMD + A have been pressed and there are items to select
|
// If CTRL + A or CMD + A have been pressed and there are items to select
|
||||||
if (ctrlDownKey && e.keyCode === aKey && this.list && this.list.children) {
|
if (ctrlDownKey && e.keyCode === aKey && hasItems) {
|
||||||
this.handleSelectAll();
|
this.handleSelectAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -267,22 +268,55 @@ export class Choices {
|
||||||
}
|
}
|
||||||
|
|
||||||
if(this.passedElement.type === 'select-multiple' && hasActiveDropDown) {
|
if(this.passedElement.type === 'select-multiple' && hasActiveDropDown) {
|
||||||
const highlighted = this.getOptionsFilteredByHighlighted();
|
const highlighted = this.dropdown.querySelector(`.${this.options.classNames.highlightedState}`);
|
||||||
|
const value = highlighted.getAttribute('data-choice-value');
|
||||||
|
const label = highlighted.innerHTML;
|
||||||
|
const id = highlighted.getAttribute('data-choice-id');
|
||||||
|
|
||||||
if(highlighted) {
|
if(highlighted) {
|
||||||
this.addItem(highlighted.value, highlighted.label, highlighted.id);
|
this.addItem(value, label, id);
|
||||||
this.input.value = "";
|
this.input.value = "";
|
||||||
|
// this.highlightPosition()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(e.keyCode === escapeKey && hasActiveDropDown) {
|
if(e.keyCode === escapeKey && hasActiveDropDown) {
|
||||||
|
if(this.passedElement.type === 'select-multiple' && hasActiveDropDown) {
|
||||||
this.toggleDropdown();
|
this.toggleDropdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
if((e.keyCode === downKey || e.keyCode === upKey) && hasActiveDropDown) {
|
|
||||||
let option = activeOptions[0];
|
|
||||||
this.highlightOption(option.id);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(e.keyCode === downKey || e.keyCode === upKey) {
|
||||||
|
if(this.passedElement.type === 'select-multiple' && hasActiveDropDown) {
|
||||||
|
const selectableOptions = activeOptions.filter((option) => {
|
||||||
|
return !option.selected;
|
||||||
|
});
|
||||||
|
|
||||||
|
let canHighlight = true;
|
||||||
|
|
||||||
|
if(e.keyCode === downKey) {
|
||||||
|
this.highlightPosition < (selectableOptions.length - 1) ? this.highlightPosition++ : canHighlight = false;
|
||||||
|
} else if(e.keyCode === upKey) {
|
||||||
|
this.highlightPosition > 0 ? this.highlightPosition-- : canHighlight = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(canHighlight) {
|
||||||
|
const option = selectableOptions[this.highlightPosition];
|
||||||
|
const previousElement = this.dropdown.querySelector(`.${this.options.classNames.highlightedState}`);
|
||||||
|
const currentElement = this.dropdown.querySelector(`[data-choice-id="${option.id}"]`);
|
||||||
|
|
||||||
|
if(previousElement) {
|
||||||
|
previousElement.classList.remove(this.options.classNames.highlightedState);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(currentElement) {
|
||||||
|
currentElement.classList.add(this.options.classNames.highlightedState);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(inputIsFocussed) {
|
if(inputIsFocussed) {
|
||||||
|
@ -297,6 +331,8 @@ export class Choices {
|
||||||
onKeyUp(e) {
|
onKeyUp(e) {
|
||||||
if(e.target === this.input) {
|
if(e.target === this.input) {
|
||||||
if(this.passedElement.type === 'select-multiple' && this.options.allowSearch) {
|
if(this.passedElement.type === 'select-multiple' && this.options.allowSearch) {
|
||||||
|
const charStr = String.fromCharCode(e.keyCode);
|
||||||
|
if(this.input === document.activeElement && /[a-z0-9]/i.test(charStr)) {
|
||||||
if(this.input.value) {
|
if(this.input.value) {
|
||||||
// If we have a value, filter options based on it
|
// If we have a value, filter options based on it
|
||||||
const handleFilter = debounce(() => {
|
const handleFilter = debounce(() => {
|
||||||
|
@ -316,7 +352,7 @@ export class Choices {
|
||||||
this.store.dispatch(activateOptions());
|
this.store.dispatch(activateOptions());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -355,7 +391,6 @@ export class Choices {
|
||||||
});
|
});
|
||||||
|
|
||||||
if(!option.selected) {
|
if(!option.selected) {
|
||||||
this.selectOption(id, true);
|
|
||||||
this.addItem(option.value, option.label, option.id);
|
this.addItem(option.value, option.label, option.id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -471,16 +506,6 @@ export class Choices {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
selectOption(id, value = true) {
|
|
||||||
if(!id) return;
|
|
||||||
this.input.value = '';
|
|
||||||
this.store.dispatch(selectOption(id, value));
|
|
||||||
}
|
|
||||||
|
|
||||||
highlightOption(id) {
|
|
||||||
if(!id) return;
|
|
||||||
this.store.dispatch(highlightOption(id));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add item to store with correct value
|
* Add item to store with correct value
|
||||||
|
@ -621,7 +646,7 @@ export class Choices {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
addOption(option, groupId = -1, highlighted = false, disabled = false) {
|
addOption(option, groupId = -1, disabled = false) {
|
||||||
// Generate unique id
|
// Generate unique id
|
||||||
const state = this.store.getState();
|
const state = this.store.getState();
|
||||||
const id = state.options.length + 1;
|
const id = state.options.length + 1;
|
||||||
|
@ -629,10 +654,9 @@ export class Choices {
|
||||||
const label = option.innerHTML;
|
const label = option.innerHTML;
|
||||||
const isSelected = option.selected;
|
const isSelected = option.selected;
|
||||||
|
|
||||||
this.store.dispatch(addOption(value, label, id, groupId, highlighted, disabled));
|
this.store.dispatch(addOption(value, label, id, groupId, disabled));
|
||||||
|
|
||||||
if(isSelected) {
|
if(isSelected) {
|
||||||
this.selectOption(id);
|
|
||||||
this.addItem(value, label, id);
|
this.addItem(value, label, id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -693,20 +717,12 @@ export class Choices {
|
||||||
getOptionsFilteredByActive() {
|
getOptionsFilteredByActive() {
|
||||||
const options = this.getOptions();
|
const options = this.getOptions();
|
||||||
const valueArray = options.filter((option) => {
|
const valueArray = options.filter((option) => {
|
||||||
return option.active === true && option.disabled === false;
|
return option.active === true && option.disabled === false && option.selected !== true;
|
||||||
},[]);
|
},[]);
|
||||||
|
|
||||||
return valueArray;
|
return valueArray;
|
||||||
}
|
}
|
||||||
|
|
||||||
getOptionsFilteredByHighlighted() {
|
|
||||||
const options = this.getOptions();
|
|
||||||
const value = options.find((option) => {
|
|
||||||
return option.highlighted === true;
|
|
||||||
});
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
getOptionsFiltedBySelectable() {
|
getOptionsFiltedBySelectable() {
|
||||||
const options = this.getOptions();
|
const options = this.getOptions();
|
||||||
const valueArray = options.filter((option) => {
|
const valueArray = options.filter((option) => {
|
||||||
|
@ -835,6 +851,7 @@ export class Choices {
|
||||||
this.input = input;
|
this.input = input;
|
||||||
this.list = list;
|
this.list = list;
|
||||||
this.dropdown = dropdown;
|
this.dropdown = dropdown;
|
||||||
|
this.highlightPosition = 0;
|
||||||
|
|
||||||
const passedGroups = Array.from(this.passedElement.getElementsByTagName('OPTGROUP'));
|
const passedGroups = Array.from(this.passedElement.getElementsByTagName('OPTGROUP'));
|
||||||
|
|
||||||
|
@ -851,9 +868,9 @@ export class Choices {
|
||||||
|
|
||||||
// If group is disabled, disable all of its children
|
// If group is disabled, disable all of its children
|
||||||
if(group.disabled) {
|
if(group.disabled) {
|
||||||
this.addOption(option, groupId, highlighted, true);
|
this.addOption(option, groupId, true);
|
||||||
} else {
|
} else {
|
||||||
this.addOption(option, groupId, highlighted);
|
this.addOption(option, groupId);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
@ -901,13 +918,13 @@ export class Choices {
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
render(callback = this.options.callbackOnRender) {
|
render(callback = this.options.callbackOnRender) {
|
||||||
|
console.log('Rendering');
|
||||||
const classNames = this.options.classNames;
|
const classNames = this.options.classNames;
|
||||||
const activeItems = this.getItemsFilteredByActive();
|
const activeItems = this.getItemsFilteredByActive();
|
||||||
|
|
||||||
|
|
||||||
// OPTIONS
|
// OPTIONS
|
||||||
if(this.passedElement.type === 'select-multiple') {
|
if(this.passedElement.type === 'select-multiple') {
|
||||||
|
|
||||||
const activeOptions = this.getOptionsFilteredByActive();
|
const activeOptions = this.getOptionsFilteredByActive();
|
||||||
const activeGroups = this.getGroupsFilteredByActive();
|
const activeGroups = this.getGroupsFilteredByActive();
|
||||||
|
|
||||||
|
@ -919,7 +936,6 @@ export class Choices {
|
||||||
|
|
||||||
// If we have grouped options
|
// If we have grouped options
|
||||||
if(activeGroups.length >= 1) {
|
if(activeGroups.length >= 1) {
|
||||||
|
|
||||||
activeGroups.forEach((group, index) => {
|
activeGroups.forEach((group, index) => {
|
||||||
// Grab options that are children of this group
|
// Grab options that are children of this group
|
||||||
const groupOptions = activeOptions.filter((option) => {
|
const groupOptions = activeOptions.filter((option) => {
|
||||||
|
@ -934,7 +950,7 @@ export class Choices {
|
||||||
|
|
||||||
groupOptions.forEach((option) => {
|
groupOptions.forEach((option) => {
|
||||||
const dropdownItem = strToEl(`
|
const dropdownItem = strToEl(`
|
||||||
<div class="${ classNames.item } ${ classNames.itemOption } ${ option.selected ? classNames.selectedState + ' ' + classNames.itemDisabled : classNames.itemSelectable } ${ option.highlighted ? classNames.highlightedState : '' }" data-choice-option data-choice-id="${ option.id }" data-choice-value="${ option.value }">
|
<div class="${ classNames.item } ${ classNames.itemOption } ${ option.selected ? classNames.selectedState + ' ' + classNames.itemDisabled : classNames.itemSelectable }" data-choice-option data-choice-id="${ option.id }" data-choice-value="${ option.value }">
|
||||||
${ option.label }
|
${ option.label }
|
||||||
</div>
|
</div>
|
||||||
`);
|
`);
|
||||||
|
@ -948,7 +964,7 @@ export class Choices {
|
||||||
} else if(activeOptions.length >= 1) {
|
} else if(activeOptions.length >= 1) {
|
||||||
activeOptions.forEach((option) => {
|
activeOptions.forEach((option) => {
|
||||||
const dropdownItem = strToEl(`
|
const dropdownItem = strToEl(`
|
||||||
<div class="${ classNames.item } ${ classNames.itemOption } ${ option.selected ? classNames.selectedState + ' ' + classNames.itemDisabled : classNames.itemSelectable } ${ option.highlighted ? classNames.highlightedState : '' }" data-choice-option data-choice-id="${ option.id }" data-choice-value="${ option.value }">
|
<div class="${ classNames.item } ${ classNames.itemOption } ${ option.selected ? classNames.selectedState + ' ' + classNames.itemDisabled : classNames.itemSelectable }" data-choice-option data-choice-id="${ option.id }" data-choice-value="${ option.value }">
|
||||||
${ option.label }
|
${ option.label }
|
||||||
</div>
|
</div>
|
||||||
`);
|
`);
|
||||||
|
@ -960,7 +976,6 @@ export class Choices {
|
||||||
optionListFragment.appendChild(dropdownItem);
|
optionListFragment.appendChild(dropdownItem);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
this.dropdown.appendChild(optionListFragment);
|
this.dropdown.appendChild(optionListFragment);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,39 +6,32 @@ const options = (state = [], action) => {
|
||||||
groupId: action.groupId,
|
groupId: action.groupId,
|
||||||
value: action.value,
|
value: action.value,
|
||||||
label: action.label,
|
label: action.label,
|
||||||
highlighted: action.highlighted,
|
|
||||||
disabled: action.disabled,
|
disabled: action.disabled,
|
||||||
selected: false,
|
selected: false,
|
||||||
active: true,
|
active: true,
|
||||||
}];
|
}];
|
||||||
|
|
||||||
case 'HIGHLIGHT_OPTION':
|
case 'ADD_ITEM':
|
||||||
|
// When an item is added and it has an associated option,
|
||||||
|
// we want to disable it so it can't be chosen again
|
||||||
|
if(action.optionId > -1) {
|
||||||
return state.map((option) => {
|
return state.map((option) => {
|
||||||
if(option.id === parseInt(action.id)) {
|
if(option.id === parseInt(action.optionId)) {
|
||||||
option.highlighted = true;
|
option.selected = true;
|
||||||
|
}
|
||||||
|
return option;
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
option.highlighted = false;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
||||||
return option;
|
|
||||||
});
|
|
||||||
|
|
||||||
case 'SELECT_OPTION':
|
|
||||||
return state.map((option) => {
|
|
||||||
if(option.id === parseInt(action.id)) {
|
|
||||||
option.selected = action.selected;
|
|
||||||
}
|
|
||||||
|
|
||||||
return option;
|
|
||||||
});
|
|
||||||
|
|
||||||
case 'REMOVE_ITEM':
|
case 'REMOVE_ITEM':
|
||||||
// When an item is removed and it has an associated option,
|
// When an item is removed and it has an associated option,
|
||||||
// we want to re-enable it so it can be chosen again
|
// we want to re-enable it so it can be chosen again
|
||||||
if(action.optionId > -1) {
|
if(action.optionId > -1) {
|
||||||
return state.map((option) => {
|
return state.map((option) => {
|
||||||
if(option.id === parseInt(action.optionId)) {
|
if(option.id === parseInt(action.optionId)) {
|
||||||
option.selected = action.selected;
|
option.selected = false;
|
||||||
}
|
}
|
||||||
return option;
|
return option;
|
||||||
});
|
});
|
||||||
|
@ -56,15 +49,6 @@ const options = (state = [], action) => {
|
||||||
return result.id === index;
|
return result.id === index;
|
||||||
});
|
});
|
||||||
|
|
||||||
// Highlight option if it is active and is the first
|
|
||||||
// active option in state
|
|
||||||
if(option.active && firstActive === false) {
|
|
||||||
option.highlighted = true;
|
|
||||||
firstActive = true;
|
|
||||||
} else {
|
|
||||||
option.highlighted = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return option;
|
return option;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue