Minor refactoring

This commit is contained in:
Josh Johnson 2019-12-29 12:39:54 +00:00
parent 9e3d1cd519
commit f6f63bc994
7 changed files with 288 additions and 277 deletions

View file

@ -228,6 +228,7 @@ exports.TEXT_TYPE = 'text';
exports.SELECT_ONE_TYPE = 'select-one'; exports.SELECT_ONE_TYPE = 'select-one';
exports.SELECT_MULTIPLE_TYPE = 'select-multiple'; exports.SELECT_MULTIPLE_TYPE = 'select-multiple';
exports.SCROLLING_SPEED = 4; exports.SCROLLING_SPEED = 4;
exports.DEFAULT_ID = -1;
/***/ }), /***/ }),
/* 1 */ /* 1 */
@ -1412,8 +1413,8 @@ function () {
} }
if (userConfig.addItemFilter && typeof userConfig.addItemFilter !== 'function') { if (userConfig.addItemFilter && typeof userConfig.addItemFilter !== 'function') {
var re = userConfig.addItemFilter instanceof RegExp ? userConfig.addItemFilter : new RegExp(userConfig.addItemFilter); var exp = userConfig.addItemFilter instanceof RegExp ? userConfig.addItemFilter : new RegExp(userConfig.addItemFilter);
this.config.addItemFilter = re.test.bind(re); this.config.addItemFilter = exp.test.bind(exp);
} }
if (this._isTextElement) { if (this._isTextElement) {
@ -1434,9 +1435,11 @@ function () {
this.initialised = false; this.initialised = false;
this._store = new store_1.default(); this._store = new store_1.default();
this._initialState = reducers_1.defaultState; this._state = {
this._currentState = reducers_1.defaultState; initial: reducers_1.defaultState,
this._prevState = reducers_1.defaultState; current: reducers_1.defaultState,
previous: reducers_1.defaultState
};
this._currentValue = ''; this._currentValue = '';
this._canSearch = !!this.config.searchEnabled; this._canSearch = !!this.config.searchEnabled;
this._isScrollingOnIe = false; this._isScrollingOnIe = false;
@ -1444,6 +1447,9 @@ function () {
this._wasTap = true; this._wasTap = true;
this._placeholderValue = this._generatePlaceholderValue(); this._placeholderValue = this._generatePlaceholderValue();
this._baseId = utils_1.generateId(this.passedElement.element, 'choices-'); this._baseId = utils_1.generateId(this.passedElement.element, 'choices-');
this._idNames = {
itemChoice: 'item-choice'
};
/** /**
* setting direction in cases where it's explicitly set on passedElement * setting direction in cases where it's explicitly set on passedElement
* or when calculated direction is different from the document * or when calculated direction is different from the document
@ -1460,31 +1466,23 @@ function () {
} }
} }
this._idNames = { this._presetData = {
itemChoice: 'item-choice' choices: this.config.choices,
}; items: this.config.items,
// Assign preset option groups/options from passed element
if (this._isSelectElement) { groups: this._isSelectElement ? this.passedElement.optionGroups : [],
// Assign preset groups from passed element options: this._isSelectElement ? this.passedElement.options : []
this._presetGroups = this.passedElement.optionGroups; // Assign preset options from passed element }; // Add any values passed from attribute
this._presetOptions = this.passedElement.options;
} // Assign preset choices from passed object
this._presetChoices = this.config.choices; // Assign preset items from passed object first
this._presetItems = this.config.items; // Add any values passed from attribute
if (this.passedElement.value && this._isTextElement) { if (this.passedElement.value && this._isTextElement) {
var splitValues = this.passedElement.value.split(this.config.delimiter); var splitValues = this.passedElement.value.split(this.config.delimiter);
this._presetItems = this._presetItems.concat(splitValues); this._presetData.items = this._presetData.items.concat(splitValues);
} // Create array of choices from option elements } // Create array of choices from option elements
if (this.passedElement.options) { if (this.passedElement.options) {
this.passedElement.options.forEach(function (option) { this.passedElement.options.forEach(function (option) {
_this._presetChoices.push({ _this._presetData.choices.push({
value: option.value, value: option.value,
label: option.innerHTML, label: option.innerHTML,
selected: !!option.selected, selected: !!option.selected,
@ -1543,6 +1541,7 @@ function () {
enumerable: true, enumerable: true,
configurable: true configurable: true
}); });
;
Choices.prototype.init = function () { Choices.prototype.init = function () {
if (this.initialised) { if (this.initialised) {
@ -1587,7 +1586,7 @@ function () {
this.clearStore(); this.clearStore();
if (this._isSelectElement) { if (this._isSelectElement) {
this.passedElement.options = this._presetOptions; this.passedElement.options = this._presetData.options;
} }
this._templates = templates_1.default; this._templates = templates_1.default;
@ -1635,7 +1634,7 @@ function () {
var id = item.id, var id = item.id,
_a = item.groupId, _a = item.groupId,
groupId = _a === void 0 ? -1 : _a, groupId = _a === void 0 ? constants_1.DEFAULT_ID : _a,
_b = item.value, _b = item.value,
value = _b === void 0 ? '' : _b, value = _b === void 0 ? '' : _b,
_c = item.label, _c = item.label,
@ -1663,7 +1662,7 @@ function () {
var id = item.id, var id = item.id,
_a = item.groupId, _a = item.groupId,
groupId = _a === void 0 ? -1 : _a, groupId = _a === void 0 ? constants_1.DEFAULT_ID : _a,
_b = item.value, _b = item.value,
value = _b === void 0 ? '' : _b, value = _b === void 0 ? '' : _b,
_c = item.label, _c = item.label,
@ -2034,10 +2033,10 @@ function () {
return; return;
} }
this._currentState = this._store.state; this._state.current = this._store.state;
var stateChanged = this._currentState.choices !== this._prevState.choices || this._currentState.groups !== this._prevState.groups || this._currentState.items !== this._prevState.items; var stateChanged = this._state.current.choices !== this._state.previous.choices || this._state.current.groups !== this._state.previous.groups || this._state.current.items !== this._state.previous.items;
var shouldRenderChoices = this._isSelectElement; var shouldRenderChoices = this._isSelectElement;
var shouldRenderItems = this._currentState.items !== this._prevState.items; var shouldRenderItems = this._state.current.items !== this._state.previous.items;
if (!stateChanged) { if (!stateChanged) {
return; return;
@ -2051,7 +2050,7 @@ function () {
this._renderItems(); this._renderItems();
} }
this._prevState = this._currentState; this._state.previous = this._state.current;
}; };
Choices.prototype._renderChoices = function () { Choices.prototype._renderChoices = function () {
@ -2073,7 +2072,7 @@ function () {
if (activeGroups.length >= 1 && !this._isSearching) { if (activeGroups.length >= 1 && !this._isSearching) {
// If we have a placeholder choice along with groups // If we have a placeholder choice along with groups
var activePlaceholders = activeChoices.filter(function (activeChoice) { var activePlaceholders = activeChoices.filter(function (activeChoice) {
return activeChoice.placeholder === true && activeChoice.groupId === -1; return activeChoice.placeholder === true && activeChoice.groupId === constants_1.DEFAULT_ID;
}); });
if (activePlaceholders.length >= 1) { if (activePlaceholders.length >= 1) {
@ -2784,7 +2783,6 @@ function () {
var highlightedChoice = this.dropdown.getChild("." + this.config.classNames.highlightedState); var highlightedChoice = this.dropdown.getChild("." + this.config.classNames.highlightedState);
if (highlightedChoice) { if (highlightedChoice) {
// add enter keyCode value
if (activeItems[0]) { if (activeItems[0]) {
activeItems[0].keyCode = enterKey; // eslint-disable-line no-param-reassign activeItems[0].keyCode = enterKey; // eslint-disable-line no-param-reassign
} }
@ -3060,7 +3058,7 @@ function () {
}; };
Choices.prototype._onFormReset = function () { Choices.prototype._onFormReset = function () {
this._store.dispatch(misc_1.resetTo(this._initialState)); this._store.dispatch(misc_1.resetTo(this._state.initial));
}; };
Choices.prototype._highlightChoice = function (el) { Choices.prototype._highlightChoice = function (el) {
@ -3120,9 +3118,9 @@ function () {
_b = _a.label, _b = _a.label,
label = _b === void 0 ? null : _b, label = _b === void 0 ? null : _b,
_c = _a.choiceId, _c = _a.choiceId,
choiceId = _c === void 0 ? -1 : _c, choiceId = _c === void 0 ? constants_1.DEFAULT_ID : _c,
_d = _a.groupId, _d = _a.groupId,
groupId = _d === void 0 ? -1 : _d, groupId = _d === void 0 ? constants_1.DEFAULT_ID : _d,
_e = _a.customProperties, _e = _a.customProperties,
customProperties = _e === void 0 ? {} : _e, customProperties = _e === void 0 ? {} : _e,
_f = _a.placeholder, _f = _a.placeholder,
@ -3132,7 +3130,6 @@ function () {
var passedValue = typeof value === 'string' ? value.trim() : value; var passedValue = typeof value === 'string' ? value.trim() : value;
var items = this._store.items; var items = this._store.items;
var passedLabel = label || passedValue; var passedLabel = label || passedValue;
var passedOptionId = choiceId || -1;
var group = groupId >= 0 ? this._store.getGroupById(groupId) : null; var group = groupId >= 0 ? this._store.getGroupById(groupId) : null;
var id = items ? items.length + 1 : 1; // If a prepended value has been passed, prepend it var id = items ? items.length + 1 : 1; // If a prepended value has been passed, prepend it
@ -3149,7 +3146,7 @@ function () {
value: passedValue, value: passedValue,
label: passedLabel, label: passedLabel,
id: id, id: id,
choiceId: passedOptionId, choiceId: choiceId,
groupId: groupId, groupId: groupId,
customProperties: customProperties, customProperties: customProperties,
placeholder: placeholder, placeholder: placeholder,
@ -3204,7 +3201,7 @@ function () {
_d = _a.isDisabled, _d = _a.isDisabled,
isDisabled = _d === void 0 ? false : _d, isDisabled = _d === void 0 ? false : _d,
_e = _a.groupId, _e = _a.groupId,
groupId = _e === void 0 ? -1 : _e, groupId = _e === void 0 ? constants_1.DEFAULT_ID : _e,
_f = _a.customProperties, _f = _a.customProperties,
customProperties = _f === void 0 ? {} : _f, customProperties = _f === void 0 ? {} : _f,
_g = _a.placeholder, _g = _a.placeholder,
@ -3214,8 +3211,7 @@ function () {
if (typeof value === 'undefined' || value === null) { if (typeof value === 'undefined' || value === null) {
return; return;
} // Generate unique id }
var choices = this._store.choices; var choices = this._store.choices;
var choiceLabel = label || value; var choiceLabel = label || value;
@ -3259,37 +3255,37 @@ function () {
var groupId = id || Math.floor(new Date().valueOf() * Math.random()); var groupId = id || Math.floor(new Date().valueOf() * Math.random());
var isDisabled = group.disabled ? group.disabled : false; var isDisabled = group.disabled ? group.disabled : false;
if (groupChoices) { if (!groupChoices) {
this._store.dispatch(groups_1.addGroup({ return this._store.dispatch(groups_1.addGroup({
value: group.label,
id: groupId,
active: true,
disabled: isDisabled
}));
var addGroupChoices = function addGroupChoices(choice) {
var isOptDisabled = choice.disabled || choice.parentNode && choice.parentNode.disabled;
_this._addChoice({
value: choice[valueKey],
label: utils_1.isType('Object', choice) ? choice[labelKey] : choice.innerHTML,
isSelected: choice.selected,
isDisabled: isOptDisabled,
groupId: groupId,
customProperties: choice.customProperties,
placeholder: choice.placeholder
});
};
groupChoices.forEach(addGroupChoices);
} else {
this._store.dispatch(groups_1.addGroup({
value: group.label, value: group.label,
id: group.id, id: group.id,
active: false, active: false,
disabled: group.disabled disabled: group.disabled
})); }));
} }
this._store.dispatch(groups_1.addGroup({
value: group.label,
id: groupId,
active: true,
disabled: isDisabled
}));
var addGroupChoices = function addGroupChoices(choice) {
var isOptDisabled = choice.disabled || choice.parentNode && choice.parentNode.disabled;
_this._addChoice({
value: choice[valueKey],
label: utils_1.isType('Object', choice) ? choice[labelKey] : choice.innerHTML,
isSelected: choice.selected,
isDisabled: isOptDisabled,
groupId: groupId,
customProperties: choice.customProperties,
placeholder: choice.placeholder
});
};
groupChoices.forEach(addGroupChoices);
}; };
Choices.prototype._getTemplate = function (template) { Choices.prototype._getTemplate = function (template) {
@ -3383,17 +3379,17 @@ function () {
this._startLoading(); this._startLoading();
if (this._presetGroups.length) { if (this._presetData.groups.length) {
this._addPredefinedGroups(this._presetGroups); this._addPredefinedGroups(this._presetData.groups);
} else { } else {
this._addPredefinedChoices(this._presetChoices); this._addPredefinedChoices(this._presetData.choices);
} }
this._stopLoading(); this._stopLoading();
} }
if (this._isTextElement) { if (this._isTextElement) {
this._addPredefinedItems(this._presetItems); this._addPredefinedItems(this._presetData.items);
} }
}; };
@ -3442,36 +3438,8 @@ function () {
customProperties = choice.customProperties, customProperties = choice.customProperties,
placeholder = choice.placeholder; placeholder = choice.placeholder;
if (_this._isSelectElement) { if (!_this._isSelectElement) {
// If the choice is actually a group return _this._addChoice({
if (choice.choices) {
_this._addGroup({
group: choice,
id: choice.id || null
});
} else {
/**
* If there is a selected choice already or the choice is not the first in
* the array, add each choice normally.
*
* Otherwise we pre-select the first enabled choice in the array ("select-one" only)
*/
var shouldPreselect = _this._isSelectOneElement && !hasSelectedChoice && index === firstEnabledChoiceIndex;
var isSelected = shouldPreselect ? true : choice.selected;
var isDisabled = choice.disabled;
console.log(isDisabled, choice);
_this._addChoice({
value: value,
label: label,
isSelected: !!isSelected,
isDisabled: !!isDisabled,
placeholder: !!placeholder,
customProperties: customProperties
});
}
} else {
_this._addChoice({
value: value, value: value,
label: label, label: label,
isSelected: !!choice.selected, isSelected: !!choice.selected,
@ -3479,7 +3447,35 @@ function () {
placeholder: !!choice.placeholder, placeholder: !!choice.placeholder,
customProperties: customProperties customProperties: customProperties
}); });
} // If the choice is actually a group
if (choice.choices) {
return _this._addGroup({
group: choice,
id: choice.id || null
});
} }
/**
* If there is a selected choice already or the choice is not the first in
* the array, add each choice normally.
*
* Otherwise we pre-select the first enabled choice in the array ("select-one" only)
*/
var shouldPreselect = _this._isSelectOneElement && !hasSelectedChoice && index === firstEnabledChoiceIndex;
var isSelected = shouldPreselect ? true : choice.selected;
var isDisabled = choice.disabled;
_this._addChoice({
value: value,
label: label,
isSelected: !!isSelected,
isDisabled: !!isDisabled,
placeholder: !!placeholder,
customProperties: customProperties
});
}); });
}; };
@ -3518,7 +3514,7 @@ function () {
if (!_this._isTextElement) { if (!_this._isTextElement) {
_this._addChoice({ return _this._addChoice({
value: item.value, value: item.value,
label: item.label, label: item.label,
isSelected: true, isSelected: true,
@ -3526,29 +3522,29 @@ function () {
customProperties: item.customProperties, customProperties: item.customProperties,
placeholder: item.placeholder placeholder: item.placeholder
}); });
} else {
_this._addItem({
value: item.value,
label: item.label,
choiceId: item.id,
customProperties: item.customProperties,
placeholder: item.placeholder
});
} }
_this._addItem({
value: item.value,
label: item.label,
choiceId: item.id,
customProperties: item.customProperties,
placeholder: item.placeholder
});
}, },
string: function string() { string: function string() {
if (!_this._isTextElement) { if (!_this._isTextElement) {
_this._addChoice({ return _this._addChoice({
value: item, value: item,
label: item, label: item,
isSelected: true, isSelected: true,
isDisabled: false isDisabled: false
}); });
} else {
_this._addItem({
value: item
});
} }
_this._addItem({
value: item
});
} }
}; };
handleType[itemType](); handleType[itemType]();
@ -3577,7 +3573,7 @@ function () {
}; };
Choices.prototype._generatePlaceholderValue = function () { Choices.prototype._generatePlaceholderValue = function () {
if (this._isSelectElement) { if (this._isSelectElement && this.passedElement.placeholderOption) {
var placeholderOption = this.passedElement.placeholderOption; var placeholderOption = this.passedElement.placeholderOption;
return placeholderOption ? placeholderOption.text : null; return placeholderOption ? placeholderOption.text : null;
} }

File diff suppressed because one or more lines are too long

View file

@ -2085,15 +2085,15 @@ describe('choices', () => {
hasFocussedInput = instance.input.isFocussed; hasFocussedInput = instance.input.isFocussed;
}); });
describe('direction key', () => { describe('direction keys', () => {
const keyCodes = [ const directionKeyCodes = [
KEY_CODES.UP_KEY, KEY_CODES.upKey,
KEY_CODES.DOWN_KEY, KEY_CODES.downKey,
KEY_CODES.PAGE_UP_KEY, KEY_CODES.pageUpKey,
KEY_CODES.PAGE_DOWN_KEY, KEY_CODES.pageDownKey,
]; ];
keyCodes.forEach(keyCode => { directionKeyCodes.forEach(keyCode => {
it(`calls _onDirectionKey with the expected arguments`, () => { it(`calls _onDirectionKey with the expected arguments`, () => {
const event = { const event = {
keyCode, keyCode,
@ -2112,7 +2112,7 @@ describe('choices', () => {
describe('select key', () => { describe('select key', () => {
it(`calls _onSelectKey with the expected arguments`, () => { it(`calls _onSelectKey with the expected arguments`, () => {
const event = { const event = {
keyCode: KEY_CODES.A_KEY, keyCode: KEY_CODES.aKey,
}; };
instance._onKeyDown(event); instance._onKeyDown(event);
@ -2127,7 +2127,7 @@ describe('choices', () => {
describe('enter key', () => { describe('enter key', () => {
it(`calls _onEnterKey with the expected arguments`, () => { it(`calls _onEnterKey with the expected arguments`, () => {
const event = { const event = {
keyCode: KEY_CODES.ENTER_KEY, keyCode: KEY_CODES.enterKey,
}; };
instance._onKeyDown(event); instance._onKeyDown(event);
@ -2141,9 +2141,9 @@ describe('choices', () => {
}); });
describe('delete key', () => { describe('delete key', () => {
const keyCodes = [KEY_CODES.DELETE_KEY, KEY_CODES.BACK_KEY]; const deleteKeyCodes = [KEY_CODES.deleteKey, KEY_CODES.backKey];
keyCodes.forEach(keyCode => { deleteKeyCodes.forEach(keyCode => {
it(`calls _onDeleteKey with the expected arguments`, () => { it(`calls _onDeleteKey with the expected arguments`, () => {
const event = { const event = {
keyCode, keyCode,

View file

@ -94,9 +94,12 @@ class Choices {
_isSelectMultipleElement: boolean; _isSelectMultipleElement: boolean;
_isSelectElement: boolean; _isSelectElement: boolean;
_store: Store; _store: Store;
_state: {
initial: State;
previous: State;
current: State;
};
_templates: typeof templates; _templates: typeof templates;
_initialState: State;
_currentState: State;
_prevState: State; _prevState: State;
_currentValue: string; _currentValue: string;
_canSearch: boolean; _canSearch: boolean;
@ -110,10 +113,12 @@ class Choices {
_idNames: { _idNames: {
itemChoice: string; itemChoice: string;
}; };
_presetGroups: Group[] | HTMLOptGroupElement[] | Element[]; _presetData: {
_presetOptions: Item[] | HTMLOptionElement[]; groups: Group[] | HTMLOptGroupElement[] | Element[];
_presetChoices: Partial<Choice>[]; options: Item[] | HTMLOptionElement[];
_presetItems: Item[] | string[]; choices: Partial<Choice>[];
items: Item[] | string[];
};
constructor( constructor(
element: element:
@ -195,9 +200,11 @@ class Choices {
this.initialised = false; this.initialised = false;
this._store = new Store(); this._store = new Store();
this._initialState = defaultState; this._state = {
this._currentState = defaultState; initial: defaultState,
this._prevState = defaultState; current: defaultState,
previous: defaultState,
};
this._currentValue = ''; this._currentValue = '';
this._canSearch = !!this.config.searchEnabled; this._canSearch = !!this.config.searchEnabled;
this._isScrollingOnIe = false; this._isScrollingOnIe = false;
@ -226,28 +233,30 @@ class Choices {
} }
} }
if (this._isSelectElement) { this._presetData = {
// Assign preset groups from passed element choices: this.config.choices,
this._presetGroups = (this.passedElement as WrappedSelect).optionGroups; items: this.config.items,
// Assign preset options from passed element // Assign preset option groups/options from passed element
this._presetOptions = (this.passedElement as WrappedSelect).options; groups: this._isSelectElement
} ? (this.passedElement as WrappedSelect).optionGroups
: [],
// Assign preset choices from passed object options: this._isSelectElement
this._presetChoices = this.config.choices; ? (this.passedElement as WrappedSelect).options
// Assign preset items from passed object first : [],
this._presetItems = this.config.items; };
// Add any values passed from attribute // Add any values passed from attribute
if (this.passedElement.value && this._isTextElement) { if (this.passedElement.value && this._isTextElement) {
const splitValues: string[] = this.passedElement.value.split( const splitValues: string[] = this.passedElement.value.split(
this.config.delimiter, this.config.delimiter,
); );
this._presetItems = (this._presetItems as string[]).concat(splitValues); this._presetData.items = (this._presetData.items as string[]).concat(
splitValues,
);
} }
// Create array of choices from option elements // Create array of choices from option elements
if ((this.passedElement as WrappedSelect).options) { if ((this.passedElement as WrappedSelect).options) {
(this.passedElement as WrappedSelect).options.forEach(option => { (this.passedElement as WrappedSelect).options.forEach(option => {
this._presetChoices.push({ this._presetData.choices.push({
value: option.value, value: option.value,
label: option.innerHTML, label: option.innerHTML,
selected: !!option.selected, selected: !!option.selected,
@ -337,7 +346,7 @@ class Choices {
this.clearStore(); this.clearStore();
if (this._isSelectElement) { if (this._isSelectElement) {
(this.passedElement as WrappedSelect).options = this._presetOptions; (this.passedElement as WrappedSelect).options = this._presetData.options;
} }
this._templates = templates; this._templates = templates;
@ -628,8 +637,6 @@ class Choices {
const fetcher = choicesArrayOrFetcher(this); const fetcher = choicesArrayOrFetcher(this);
if (typeof Promise === 'function' && fetcher instanceof Promise) { if (typeof Promise === 'function' && fetcher instanceof Promise) {
// that's a promise
// eslint-disable-next-line compat/compat
return new Promise(resolve => requestAnimationFrame(resolve)) // eslint-disable-line compat/compat return new Promise(resolve => requestAnimationFrame(resolve)) // eslint-disable-line compat/compat
.then(() => this._handleLoadingState(true)) .then(() => this._handleLoadingState(true))
.then(() => fetcher) .then(() => fetcher)
@ -658,7 +665,7 @@ class Choices {
if (!Array.isArray(choicesArrayOrFetcher)) { if (!Array.isArray(choicesArrayOrFetcher)) {
throw new TypeError( throw new TypeError(
`.setChoices must be called either with array of choices with a function resulting into Promise of array of choices`, `setChoices must be called either with array of choices with a function resulting into Promise of array of choices`,
); );
} }
@ -726,15 +733,15 @@ class Choices {
return; return;
} }
this._currentState = this._store.state; this._state.current = this._store.state;
const stateChanged = const stateChanged =
this._currentState.choices !== this._prevState.choices || this._state.current.choices !== this._state.previous.choices ||
this._currentState.groups !== this._prevState.groups || this._state.current.groups !== this._state.previous.groups ||
this._currentState.items !== this._prevState.items; this._state.current.items !== this._state.previous.items;
const shouldRenderChoices = this._isSelectElement; const shouldRenderChoices = this._isSelectElement;
const shouldRenderItems = const shouldRenderItems =
this._currentState.items !== this._prevState.items; this._state.current.items !== this._state.previous.items;
if (!stateChanged) { if (!stateChanged) {
return; return;
@ -748,12 +755,11 @@ class Choices {
this._renderItems(); this._renderItems();
} }
this._prevState = this._currentState; this._state.previous = this._state.current;
} }
_renderChoices(): void { _renderChoices(): void {
const { activeGroups, activeChoices } = this._store; const { activeGroups, activeChoices } = this._store;
let choiceListFragment = document.createDocumentFragment();
this.choiceList.clear(); this.choiceList.clear();
@ -761,31 +767,10 @@ class Choices {
requestAnimationFrame(() => this.choiceList.scrollToTop()); requestAnimationFrame(() => this.choiceList.scrollToTop());
} }
// If we have grouped options const choiceListFragment = this._createChoiceListFragment(
if (activeGroups.length >= 1 && !this._isSearching) { activeGroups,
// If we have a placeholder choice along with groups activeChoices,
const activePlaceholders = activeChoices.filter( );
activeChoice =>
activeChoice.placeholder === true &&
activeChoice.groupId === DEFAULT_ID,
);
if (activePlaceholders.length >= 1) {
choiceListFragment = this._createChoicesFragment(
activePlaceholders,
choiceListFragment,
);
}
choiceListFragment = this._createGroupsFragment(
activeGroups,
activeChoices,
choiceListFragment,
);
} else if (activeChoices.length >= 1) {
choiceListFragment = this._createChoicesFragment(
activeChoices,
choiceListFragment,
);
}
// If we have choices to show // If we have choices to show
if ( if (
@ -805,30 +790,32 @@ class Choices {
this.choiceList.append(notice); this.choiceList.append(notice);
} }
} else { } else {
// Otherwise show a notice this.choiceList.append(
let dropdownItem; this._isSearching
let notice; ? this._getNoResultsTemplate()
: this._getNoChoicesTemplate(),
if (this._isSearching) { );
notice =
typeof this.config.noResultsText === 'function'
? this.config.noResultsText()
: this.config.noResultsText;
dropdownItem = this._getTemplate('notice', notice, 'no-results');
} else {
notice =
typeof this.config.noChoicesText === 'function'
? this.config.noChoicesText()
: this.config.noChoicesText;
dropdownItem = this._getTemplate('notice', notice, 'no-choices');
}
this.choiceList.append(dropdownItem);
} }
} }
_getNoChoicesTemplate(): HTMLElement {
const textToDisplay =
typeof this.config.noChoicesText === 'function'
? this.config.noChoicesText()
: this.config.noChoicesText;
return this._getTemplate('notice', textToDisplay, 'no-choices');
}
_getNoResultsTemplate(): HTMLElement {
const textToDisplay =
typeof this.config.noResultsText === 'function'
? this.config.noResultsText()
: this.config.noResultsText;
return this._getTemplate('notice', textToDisplay, 'no-results');
}
_renderItems(): void { _renderItems(): void {
const activeItems = this._store.activeItems || []; const activeItems = this._store.activeItems || [];
this.itemList.clear(); this.itemList.clear();
@ -992,6 +979,40 @@ class Choices {
return fragment; return fragment;
} }
_createChoiceListFragment(
groups: Group[],
choices: Choice[],
): DocumentFragment {
let choiceListFragment = document.createDocumentFragment();
// If we have grouped options
if (groups.length >= 1 && !this._isSearching) {
// If we have a placeholder choice along with groups
const activePlaceholders = choices.filter(
activeChoice =>
activeChoice.placeholder === true &&
activeChoice.groupId === DEFAULT_ID,
);
if (activePlaceholders.length >= 1) {
choiceListFragment = this._createChoicesFragment(
activePlaceholders,
choiceListFragment,
);
}
choiceListFragment = this._createGroupsFragment(
groups,
choices,
choiceListFragment,
);
} else if (choices.length >= 1) {
choiceListFragment = this._createChoicesFragment(
choices,
choiceListFragment,
);
}
return choiceListFragment;
}
_triggerChange(value): void { _triggerChange(value): void {
if (value === undefined || value === null) { if (value === undefined || value === null) {
return; return;
@ -1043,16 +1064,11 @@ class Choices {
} }
_handleItemAction( _handleItemAction(
activeItems?: Item[], activeItems: Item[],
element?: HTMLElement, element: HTMLElement,
hasShiftKey = false, hasShiftKey = false,
): void { ): void {
if ( if (!this.config.removeItems || this._isSelectOneElement) {
!activeItems ||
!element ||
!this.config.removeItems ||
this._isSelectOneElement
) {
return; return;
} }
@ -1074,14 +1090,11 @@ class Choices {
this.input.focus(); this.input.focus();
} }
_handleChoiceAction(activeItems?: Item[], element?: HTMLElement): void { _handleChoiceAction(activeItems: Item[], element: HTMLElement): void {
if (!activeItems || !element) {
return;
}
// If we are clicking on an option // If we are clicking on an option
const { id } = element.dataset; const { id } = element.dataset;
const choice = id && this._store.getChoiceById(id); const choice = id && this._store.getChoiceById(id);
if (!choice) { if (!choice) {
return; return;
} }
@ -1126,8 +1139,8 @@ class Choices {
} }
} }
_handleBackspace(activeItems?: Item[]): void { _handleBackspace(activeItems: Item[]): void {
if (!this.config.removeItems || !activeItems) { if (!this.config.removeItems) {
return; return;
} }
@ -1146,6 +1159,8 @@ class Choices {
// Highlight last item if none already highlighted // Highlight last item if none already highlighted
this.highlightItem(lastItem, false); this.highlightItem(lastItem, false);
} }
// then remove all highlighted items
this.removeHighlightedItems(true); this.removeHighlightedItems(true);
} }
} }
@ -1158,12 +1173,12 @@ class Choices {
this._store.dispatch(setIsLoading(false)); this._store.dispatch(setIsLoading(false));
} }
_handleLoadingState(setLoading = true): void { _handleLoadingState(isLoading = true): void {
let placeholderItem = this.itemList.getChild( let placeholderItem = this.itemList.getChild(
`.${this.config.classNames.placeholder}`, `.${this.config.classNames.placeholder}`,
); );
if (setLoading) { if (isLoading) {
this.disable(); this.disable();
this.containerOuter.addLoadingState(); this.containerOuter.addLoadingState();
@ -1406,15 +1421,15 @@ class Choices {
const wasAlphaNumericChar = /[a-zA-Z0-9-_ ]/.test(keyString); const wasAlphaNumericChar = /[a-zA-Z0-9-_ ]/.test(keyString);
const { const {
BACK_KEY, backKey,
DELETE_KEY, deleteKey,
ENTER_KEY, enterKey,
A_KEY, aKey,
ESC_KEY, escKey,
UP_KEY, upKey,
DOWN_KEY, downKey,
PAGE_UP_KEY, pageUpKey,
PAGE_DOWN_KEY, pageDownKey,
} = KEY_CODES; } = KEY_CODES;
if (!this._isTextElement && !hasActiveDropdown && wasAlphaNumericChar) { if (!this._isTextElement && !hasActiveDropdown && wasAlphaNumericChar) {
@ -1431,19 +1446,19 @@ class Choices {
} }
switch (keyCode) { switch (keyCode) {
case A_KEY: case aKey:
return this._onSelectKey(event, hasItems); return this._onSelectKey(event, hasItems);
case ENTER_KEY: case enterKey:
return this._onEnterKey(event, activeItems, hasActiveDropdown); return this._onEnterKey(event, activeItems, hasActiveDropdown);
case ESC_KEY: case escKey:
return this._onEscapeKey(hasActiveDropdown); return this._onEscapeKey(hasActiveDropdown);
case UP_KEY: case upKey:
case PAGE_UP_KEY: case pageUpKey:
case DOWN_KEY: case downKey:
case PAGE_DOWN_KEY: case pageDownKey:
return this._onDirectionKey(event, hasActiveDropdown); return this._onDirectionKey(event, hasActiveDropdown);
case DELETE_KEY: case deleteKey:
case BACK_KEY: case backKey:
return this._onDeleteKey(event, activeItems, hasFocusedInput); return this._onDeleteKey(event, activeItems, hasFocusedInput);
default: default:
} }
@ -1456,7 +1471,7 @@ class Choices {
const { value } = this.input; const { value } = this.input;
const { activeItems } = this._store; const { activeItems } = this._store;
const canAddItem = this._canAddItem(activeItems, value); const canAddItem = this._canAddItem(activeItems, value);
const { BACK_KEY: backKey, DELETE_KEY: deleteKey } = KEY_CODES; const { backKey, deleteKey } = KEY_CODES;
// We are typing into a text input and have a value, we want to show a dropdown // We are typing into a text input and have a value, we want to show a dropdown
// notice. Otherwise hide the dropdown // notice. Otherwise hide the dropdown
@ -1513,7 +1528,7 @@ class Choices {
hasActiveDropdown: boolean, hasActiveDropdown: boolean,
): void { ): void {
const { target } = event; const { target } = event;
const { ENTER_KEY: enterKey } = KEY_CODES; const { enterKey: enterKey } = KEY_CODES;
const targetWasButton = const targetWasButton =
target && (target as HTMLElement).hasAttribute('data-button'); target && (target as HTMLElement).hasAttribute('data-button');
@ -1563,9 +1578,9 @@ class Choices {
_onDirectionKey(event: KeyboardEvent, hasActiveDropdown: boolean): void { _onDirectionKey(event: KeyboardEvent, hasActiveDropdown: boolean): void {
const { keyCode, metaKey } = event; const { keyCode, metaKey } = event;
const { const {
DOWN_KEY: downKey, downKey: downKey,
PAGE_UP_KEY: pageUpKey, pageUpKey: pageUpKey,
PAGE_DOWN_KEY: pageDownKey, pageDownKey: pageDownKey,
} = KEY_CODES; } = KEY_CODES;
// If up or down key is pressed, traverse through options // If up or down key is pressed, traverse through options
@ -1841,7 +1856,7 @@ class Choices {
} }
_onFormReset(): void { _onFormReset(): void {
this._store.dispatch(resetTo(this._initialState)); this._store.dispatch(resetTo(this._state.initial));
} }
_highlightChoice(el: HTMLElement | null = null): void { _highlightChoice(el: HTMLElement | null = null): void {
@ -2176,17 +2191,17 @@ class Choices {
this._isSearching = false; this._isSearching = false;
this._startLoading(); this._startLoading();
if (this._presetGroups.length) { if (this._presetData.groups.length) {
this._addPredefinedGroups(this._presetGroups); this._addPredefinedGroups(this._presetData.groups);
} else { } else {
this._addPredefinedChoices(this._presetChoices); this._addPredefinedChoices(this._presetData.choices);
} }
this._stopLoading(); this._stopLoading();
} }
if (this._isTextElement) { if (this._isTextElement) {
this._addPredefinedItems(this._presetItems); this._addPredefinedItems(this._presetData.items);
} }
} }

View file

@ -132,15 +132,15 @@ describe('constants', () => {
it('exports as an object with expected keys', () => { it('exports as an object with expected keys', () => {
expect(KEY_CODES).to.be.an('object'); expect(KEY_CODES).to.be.an('object');
expect(Object.keys(KEY_CODES)).to.eql([ expect(Object.keys(KEY_CODES)).to.eql([
'BACK_KEY', 'backKey',
'DELETE_KEY', 'deleteKey',
'ENTER_KEY', 'enterKey',
'A_KEY', 'aKey',
'ESC_KEY', 'escKey',
'UP_KEY', 'upKey',
'DOWN_KEY', 'downKey',
'PAGE_UP_KEY', 'pageUpKey',
'PAGE_DOWN_KEY', 'pageDownKey',
]); ]);
}); });

View file

@ -111,15 +111,15 @@ export const ACTION_TYPES: Record<ActionType, ActionType> = {
}; };
export const KEY_CODES: KeyCodeMap = { export const KEY_CODES: KeyCodeMap = {
BACK_KEY: 46, backKey: 46,
DELETE_KEY: 8, deleteKey: 8,
ENTER_KEY: 13, enterKey: 13,
A_KEY: 65, aKey: 65,
ESC_KEY: 27, escKey: 27,
UP_KEY: 38, upKey: 38,
DOWN_KEY: 40, downKey: 40,
PAGE_UP_KEY: 33, pageUpKey: 33,
PAGE_DOWN_KEY: 34, pageDownKey: 34,
}; };
export const TEXT_TYPE: HTMLInputElement['type'] = 'text'; export const TEXT_TYPE: HTMLInputElement['type'] = 'text';

View file

@ -159,15 +159,15 @@ export interface EventMap {
} }
export interface KeyCodeMap { export interface KeyCodeMap {
BACK_KEY: 46; backKey: 46;
DELETE_KEY: 8; deleteKey: 8;
ENTER_KEY: 13; enterKey: 13;
A_KEY: 65; aKey: 65;
ESC_KEY: 27; escKey: 27;
UP_KEY: 38; upKey: 38;
DOWN_KEY: 40; downKey: 40;
PAGE_UP_KEY: 33; pageUpKey: 33;
PAGE_DOWN_KEY: 34; pageDownKey: 34;
} }
export type ActionType = export type ActionType =