Custom sorting in config + resolve setChoice bug when searching

This commit is contained in:
Josh Johnson 2016-08-03 14:23:23 +01:00
parent 6d9656bed2
commit 5f91165d4a
8 changed files with 49 additions and 37 deletions

File diff suppressed because one or more lines are too long

View file

@ -2,7 +2,7 @@
import './lib/polyfills.js';
import { addItem, removeItem, highlightItem, addChoice, filterChoices, activateChoices, addGroup, clearAll } from './actions/index';
import { isScrolledIntoView, getAdjacentEl, findAncestor, wrap, isType, isElement, strToEl, extend, getWidthOfInput, debounce } from './lib/utils.js';
import { isScrolledIntoView, getAdjacentEl, findAncestor, wrap, isType, isElement, strToEl, extend, getWidthOfInput, debounce, sortByAlpha, sortByScore } from './lib/utils.js';
import Fuse from 'fuse.js';
import Store from './store/index.js';
@ -38,6 +38,7 @@ export class Choices {
search: true,
flip: true,
regexFilter: null,
sortFilter: sortByAlpha,
sortFields: ['label', 'value'],
placeholder: true,
placeholderValue: null,
@ -488,17 +489,11 @@ export class Choices {
if(choices && choices.length) {
this.containerOuter.classList.remove(this.config.classNames.loadingState);
choices.forEach((result, index) => {
if(isType('Object', result)) {
if(result.choices) {
const isFirst = index === 0 ? true : false;
this._addGroup(result, index, isFirst);
this.setChoices(result.choices, value, label);
} else {
// Select first choice in list if single select input
if(index === 0 && this.passedElement.type === 'select-one') {
this._addChoice(true, result.disabled ? result.disabled : false, result[value], result[label]);
} else {
this._addChoice(result.selected ? result.selected : false, result.disabled ? result.disabled : false, result[value], result[label]);
}
this._addChoice(result.selected ? result.selected : false, result.disabled ? result.disabled : false, result[value], result[label]);
}
});
}
@ -885,6 +880,7 @@ export class Choices {
if(newValue.length >= 1 && newValue !== currentValue + ' ') {
const haystack = this.store.getChoicesFiltedBySelectable();
const needle = newValue;
const keys = isType('Array', this.config.sortFields) ? this.config.sortFields : [this.config.sortFields];
const fuse = new Fuse(haystack, {
keys: keys,
@ -1315,12 +1311,25 @@ export class Choices {
_addGroup(group, id, isFirst) {
const groupChoices = isType('Object', group) ? group.choices : Array.from(group.getElementsByTagName('OPTION'));
const groupId = id;
const isDisabled = group.disabled ? group.disabled : false;
if(groupChoices) {
this.store.dispatch(addGroup(group.label, groupId, true, group.disabled));
groupChoices.forEach((option) => {
this.store.dispatch(addGroup(group.label, groupId, true, isDisabled));
groupChoices.forEach((option, index) => {
const isDisabled = (option.disabled || option.parentNode && option.parentNode.disabled) || false;
this._addChoice(option.selected, isDisabled, option.value, option.innerHTML, groupId);
const isSelected = option.selected ? option.selected : false;
let label;
if(isType('Object', option)) {
label = option.label || option.value;
} else {
label = option.innerHTML;
}
this._addChoice(isSelected, isDisabled, option.value, label, groupId);
});
} else {
this.store.dispatch(addGroup(group.label, group.id, false, group.disabled));
@ -1539,8 +1548,11 @@ export class Choices {
*/
renderGroups(groups, choices, fragment) {
const groupFragment = fragment || document.createDocumentFragment();
const filter = this.config.sortFilter;
groups.forEach((group, i) => {
groups
.sort(filter)
.forEach((group, i) => {
// Grab options that are children of this group
const groupChoices = choices.filter((choice) => {
if(this.passedElement.type === 'select-one') {
@ -1572,16 +1584,19 @@ export class Choices {
renderChoices(choices, fragment) {
// Create a fragment to store our list items (so we don't have to update the DOM for each item)
const choicesFragment = fragment || document.createDocumentFragment();
const filter = this.isSearching ? sortByScore : this.config.sortFilter;
choices.forEach((choice, i) => {
const dropdownItem = this._getTemplate('choice', choice);
choices
.sort(filter)
.forEach((choice, i) => {
const dropdownItem = this._getTemplate('choice', choice);
if(this.passedElement.type === 'select-one') {
choicesFragment.appendChild(dropdownItem);
} else if(!choice.selected) {
choicesFragment.appendChild(dropdownItem);
}
});
if(this.passedElement.type === 'select-one') {
choicesFragment.appendChild(dropdownItem);
} else if(!choice.selected) {
choicesFragment.appendChild(dropdownItem);
}
});
return choicesFragment;
}

View file

@ -472,4 +472,8 @@ export const sortByAlpha = (a, b) => {
if (labelA < labelB) return -1;
if (labelA > labelB) return 1;
return 0;
};
export const sortByScore = (a, b) => {
return a.score - b.score;
};

View file

@ -1,5 +1,3 @@
import { sortByAlpha } from './../lib/utils.js';
const choices = (state = [], action) => {
switch (action.type) {
case 'ADD_CHOICE':
@ -12,7 +10,7 @@ const choices = (state = [], action) => {
selected: false, // A selected choice has been added to the passed input's value (added as an item)
active: true, // An active choice appears within the choice dropdown
score: 9999,
}].sort(sortByAlpha);
}];
case 'ADD_ITEM':
let newState = state;
@ -22,7 +20,7 @@ const choices = (state = [], action) => {
newState = state.map((choice) => {
choice.active = action.active;
return choice;
}).sort(sortByAlpha);
});
}
// When an item is added and it has an associated choice,
// we want to disable it so it can't be chosen again
@ -65,8 +63,6 @@ const choices = (state = [], action) => {
});
return choice;
}).sort((prev, next) => {
return prev.score - next.score;
});
return filteredState;
@ -75,8 +71,7 @@ const choices = (state = [], action) => {
return state.map((choice) => {
choice.active = action.active;
return choice;
}).sort(sortByAlpha);
});
default:
return state;

View file

@ -8,7 +8,7 @@ const groups = (state = [], action) => {
value: action.value,
active: action.active,
disabled: action.disabled,
}].sort(sortByAlpha);;
}];
default:
return state;

View file

@ -32,7 +32,6 @@ const items = (state = [], action) => {
if(item.id === action.id) {
item.highlighted = action.highlighted;
}
return item;
});

View file

@ -80,6 +80,7 @@ export class Store {
*/
getChoices() {
const state = this.store.getState();
return state.choices;
}
@ -104,7 +105,7 @@ export class Store {
getChoicesFiltedBySelectable() {
const choices = this.getChoices();
const values = choices.filter((choice) => {
return choice.selected === false && choice.disabled !== true;
return choice.disabled !== true;
},[]);
return values;

View file

@ -225,9 +225,7 @@
});
});
var example10 = new Choices('#choices-14', {
sortFields: 'value'
}).ajax(function(callback) {
var example10 = new Choices('#choices-14').ajax(function(callback) {
var request = new XMLHttpRequest();
request.open('get', 'https://restcountries.eu/rest/v1/all', true);
request.onreadystatechange = function() {