mirror of
https://github.com/Choices-js/Choices.git
synced 2024-06-03 14:32:11 +02:00
Make render method private
This commit is contained in:
parent
49c6a648c0
commit
4824958e4d
|
@ -32,11 +32,11 @@ Or include Choices directly:
|
||||||
|
|
||||||
```html
|
```html
|
||||||
<!-- Include base CSS (optional) -->
|
<!-- Include base CSS (optional) -->
|
||||||
<link rel="stylesheet" href="public/styles/base.min.css">
|
<link rel="stylesheet" href="public/assets/styles/base.min.css">
|
||||||
<!-- Include Choices CSS -->
|
<!-- Include Choices CSS -->
|
||||||
<link rel="stylesheet" href="public/styles/choices.min.css">
|
<link rel="stylesheet" href="public/assets/styles/choices.min.css">
|
||||||
<!-- Include Choices JavaScript -->
|
<!-- Include Choices JavaScript -->
|
||||||
<script src="/public/scripts/choices.min.js"></script>
|
<script src="/public/assets/scripts/choices.min.js"></script>
|
||||||
```
|
```
|
||||||
## Setup
|
## Setup
|
||||||
|
|
||||||
|
|
|
@ -131,7 +131,8 @@ class Choices {
|
||||||
this.passedElement.value.split(this.config.delimiter),
|
this.passedElement.value.split(this.config.delimiter),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
this.render = this.render.bind(this);
|
|
||||||
|
this._render = this._render.bind(this);
|
||||||
this._onFocus = this._onFocus.bind(this);
|
this._onFocus = this._onFocus.bind(this);
|
||||||
this._onBlur = this._onBlur.bind(this);
|
this._onBlur = this._onBlur.bind(this);
|
||||||
this._onKeyUp = this._onKeyUp.bind(this);
|
this._onKeyUp = this._onKeyUp.bind(this);
|
||||||
|
@ -169,8 +170,8 @@ class Choices {
|
||||||
// Set initial state (We need to clone the state because some reducers
|
// Set initial state (We need to clone the state because some reducers
|
||||||
// modify the inner objects properties in the state) 🤢
|
// modify the inner objects properties in the state) 🤢
|
||||||
this._initialState = cloneObject(this._store.state);
|
this._initialState = cloneObject(this._store.state);
|
||||||
this._store.subscribe(this.render);
|
this._store.subscribe(this._render);
|
||||||
this.render();
|
this._render();
|
||||||
this._addEventListeners();
|
this._addEventListeners();
|
||||||
this.initialised = true;
|
this.initialised = true;
|
||||||
|
|
||||||
|
@ -232,32 +233,6 @@ class Choices {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
|
||||||
this._currentState = this._store.state;
|
|
||||||
|
|
||||||
const stateChanged =
|
|
||||||
this._currentState.choices !== this._prevState.choices ||
|
|
||||||
this._currentState.groups !== this._prevState.groups ||
|
|
||||||
this._currentState.items !== this._prevState.items;
|
|
||||||
const shouldRenderChoices = this._isSelectElement;
|
|
||||||
const shouldRenderItems =
|
|
||||||
this._currentState.items !== this._prevState.items;
|
|
||||||
|
|
||||||
if (!stateChanged) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (shouldRenderChoices) {
|
|
||||||
this._renderChoices();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (shouldRenderItems) {
|
|
||||||
this._renderItems();
|
|
||||||
}
|
|
||||||
|
|
||||||
this._prevState = this._currentState;
|
|
||||||
}
|
|
||||||
|
|
||||||
highlightItem(item, runEvent = true) {
|
highlightItem(item, runEvent = true) {
|
||||||
if (!item) {
|
if (!item) {
|
||||||
return this;
|
return this;
|
||||||
|
@ -485,6 +460,123 @@ class Choices {
|
||||||
= Private functions =
|
= Private functions =
|
||||||
============================================= */
|
============================================= */
|
||||||
|
|
||||||
|
_render() {
|
||||||
|
this._currentState = this._store.state;
|
||||||
|
|
||||||
|
const stateChanged =
|
||||||
|
this._currentState.choices !== this._prevState.choices ||
|
||||||
|
this._currentState.groups !== this._prevState.groups ||
|
||||||
|
this._currentState.items !== this._prevState.items;
|
||||||
|
const shouldRenderChoices = this._isSelectElement;
|
||||||
|
const shouldRenderItems =
|
||||||
|
this._currentState.items !== this._prevState.items;
|
||||||
|
|
||||||
|
if (!stateChanged) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shouldRenderChoices) {
|
||||||
|
this._renderChoices();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shouldRenderItems) {
|
||||||
|
this._renderItems();
|
||||||
|
}
|
||||||
|
|
||||||
|
this._prevState = this._currentState;
|
||||||
|
}
|
||||||
|
|
||||||
|
_renderChoices() {
|
||||||
|
const { activeGroups, activeChoices } = this._store;
|
||||||
|
let choiceListFragment = document.createDocumentFragment();
|
||||||
|
|
||||||
|
this.choiceList.clear();
|
||||||
|
|
||||||
|
if (this.config.resetScrollPosition) {
|
||||||
|
requestAnimationFrame(() => this.choiceList.scrollToTop());
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we have grouped options
|
||||||
|
if (activeGroups.length >= 1 && !this._isSearching) {
|
||||||
|
// If we have a placeholder choice along with groups
|
||||||
|
const activePlaceholders = activeChoices.filter(
|
||||||
|
activeChoice =>
|
||||||
|
activeChoice.placeholder === true && activeChoice.groupId === -1,
|
||||||
|
);
|
||||||
|
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 (
|
||||||
|
choiceListFragment.childNodes &&
|
||||||
|
choiceListFragment.childNodes.length > 0
|
||||||
|
) {
|
||||||
|
const activeItems = this._store.activeItems;
|
||||||
|
const canAddItem = this._canAddItem(activeItems, this.input.value);
|
||||||
|
|
||||||
|
// ...and we can select them
|
||||||
|
if (canAddItem.response) {
|
||||||
|
// ...append them and highlight the first choice
|
||||||
|
this.choiceList.append(choiceListFragment);
|
||||||
|
this._highlightChoice();
|
||||||
|
} else {
|
||||||
|
// ...otherwise show a notice
|
||||||
|
this.choiceList.append(this._getTemplate('notice', canAddItem.notice));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Otherwise show a notice
|
||||||
|
let dropdownItem;
|
||||||
|
let notice;
|
||||||
|
|
||||||
|
if (this._isSearching) {
|
||||||
|
notice = isType('Function', this.config.noResultsText)
|
||||||
|
? this.config.noResultsText()
|
||||||
|
: this.config.noResultsText;
|
||||||
|
|
||||||
|
dropdownItem = this._getTemplate('notice', notice, 'no-results');
|
||||||
|
} else {
|
||||||
|
notice = isType('Function', this.config.noChoicesText)
|
||||||
|
? this.config.noChoicesText()
|
||||||
|
: this.config.noChoicesText;
|
||||||
|
|
||||||
|
dropdownItem = this._getTemplate('notice', notice, 'no-choices');
|
||||||
|
}
|
||||||
|
|
||||||
|
this.choiceList.append(dropdownItem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_renderItems() {
|
||||||
|
const activeItems = this._store.activeItems || [];
|
||||||
|
this.itemList.clear();
|
||||||
|
|
||||||
|
if (activeItems.length) {
|
||||||
|
// Create a fragment to store our list items
|
||||||
|
// (so we don't have to update the DOM for each item)
|
||||||
|
const itemListFragment = this._createItemsFragment(activeItems);
|
||||||
|
|
||||||
|
// If we have items to add, append them
|
||||||
|
if (itemListFragment.childNodes) {
|
||||||
|
this.itemList.append(itemListFragment);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
_createGroupsFragment(groups, choices, fragment) {
|
_createGroupsFragment(groups, choices, fragment) {
|
||||||
const groupFragment = fragment || document.createDocumentFragment();
|
const groupFragment = fragment || document.createDocumentFragment();
|
||||||
const getGroupChoices = group =>
|
const getGroupChoices = group =>
|
||||||
|
@ -793,6 +885,30 @@ class Choices {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_handleSearch(value) {
|
||||||
|
if (!value || !this.input.isFocussed) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const choices = this._store.choices;
|
||||||
|
const { searchFloor, searchChoices } = this.config;
|
||||||
|
const hasUnactiveChoices = choices.some(option => !option.active);
|
||||||
|
|
||||||
|
// Check that we have a value to search and the input was an alphanumeric character
|
||||||
|
if (value && value.length >= searchFloor) {
|
||||||
|
const resultCount = searchChoices ? this._searchChoices(value) : 0;
|
||||||
|
// Trigger search event
|
||||||
|
this.passedElement.triggerEvent(EVENTS.search, {
|
||||||
|
value,
|
||||||
|
resultCount,
|
||||||
|
});
|
||||||
|
} else if (hasUnactiveChoices) {
|
||||||
|
// Otherwise reset choices to active
|
||||||
|
this._isSearching = false;
|
||||||
|
this._store.dispatch(activateChoices(true));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
_canAddItem(activeItems, value) {
|
_canAddItem(activeItems, value) {
|
||||||
let canAddItem = true;
|
let canAddItem = true;
|
||||||
let notice = isType('Function', this.config.addItemText)
|
let notice = isType('Function', this.config.addItemText)
|
||||||
|
@ -916,30 +1032,6 @@ class Choices {
|
||||||
return results.length;
|
return results.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
_handleSearch(value) {
|
|
||||||
if (!value || !this.input.isFocussed) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const choices = this._store.choices;
|
|
||||||
const { searchFloor, searchChoices } = this.config;
|
|
||||||
const hasUnactiveChoices = choices.some(option => !option.active);
|
|
||||||
|
|
||||||
// Check that we have a value to search and the input was an alphanumeric character
|
|
||||||
if (value && value.length >= searchFloor) {
|
|
||||||
const resultCount = searchChoices ? this._searchChoices(value) : 0;
|
|
||||||
// Trigger search event
|
|
||||||
this.passedElement.triggerEvent(EVENTS.search, {
|
|
||||||
value,
|
|
||||||
resultCount,
|
|
||||||
});
|
|
||||||
} else if (hasUnactiveChoices) {
|
|
||||||
// Otherwise reset choices to active
|
|
||||||
this._isSearching = false;
|
|
||||||
this._store.dispatch(activateChoices(true));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_addEventListeners() {
|
_addEventListeners() {
|
||||||
document.addEventListener('keyup', this._onKeyUp);
|
document.addEventListener('keyup', this._onKeyUp);
|
||||||
document.addEventListener('keydown', this._onKeyDown);
|
document.addEventListener('keydown', this._onKeyDown);
|
||||||
|
@ -1960,97 +2052,6 @@ class Choices {
|
||||||
: false;
|
: false;
|
||||||
}
|
}
|
||||||
|
|
||||||
_renderChoices() {
|
|
||||||
const { activeGroups, activeChoices } = this._store;
|
|
||||||
let choiceListFragment = document.createDocumentFragment();
|
|
||||||
|
|
||||||
this.choiceList.clear();
|
|
||||||
|
|
||||||
if (this.config.resetScrollPosition) {
|
|
||||||
requestAnimationFrame(() => this.choiceList.scrollToTop());
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we have grouped options
|
|
||||||
if (activeGroups.length >= 1 && !this._isSearching) {
|
|
||||||
// If we have a placeholder choice along with groups
|
|
||||||
const activePlaceholders = activeChoices.filter(
|
|
||||||
activeChoice =>
|
|
||||||
activeChoice.placeholder === true && activeChoice.groupId === -1,
|
|
||||||
);
|
|
||||||
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 (
|
|
||||||
choiceListFragment.childNodes &&
|
|
||||||
choiceListFragment.childNodes.length > 0
|
|
||||||
) {
|
|
||||||
const activeItems = this._store.activeItems;
|
|
||||||
const canAddItem = this._canAddItem(activeItems, this.input.value);
|
|
||||||
|
|
||||||
// ...and we can select them
|
|
||||||
if (canAddItem.response) {
|
|
||||||
// ...append them and highlight the first choice
|
|
||||||
this.choiceList.append(choiceListFragment);
|
|
||||||
this._highlightChoice();
|
|
||||||
} else {
|
|
||||||
// ...otherwise show a notice
|
|
||||||
this.choiceList.append(this._getTemplate('notice', canAddItem.notice));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Otherwise show a notice
|
|
||||||
let dropdownItem;
|
|
||||||
let notice;
|
|
||||||
|
|
||||||
if (this._isSearching) {
|
|
||||||
notice = isType('Function', this.config.noResultsText)
|
|
||||||
? this.config.noResultsText()
|
|
||||||
: this.config.noResultsText;
|
|
||||||
|
|
||||||
dropdownItem = this._getTemplate('notice', notice, 'no-results');
|
|
||||||
} else {
|
|
||||||
notice = isType('Function', this.config.noChoicesText)
|
|
||||||
? this.config.noChoicesText()
|
|
||||||
: this.config.noChoicesText;
|
|
||||||
|
|
||||||
dropdownItem = this._getTemplate('notice', notice, 'no-choices');
|
|
||||||
}
|
|
||||||
|
|
||||||
this.choiceList.append(dropdownItem);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_renderItems() {
|
|
||||||
const activeItems = this._store.activeItems || [];
|
|
||||||
this.itemList.clear();
|
|
||||||
|
|
||||||
if (activeItems.length) {
|
|
||||||
// Create a fragment to store our list items
|
|
||||||
// (so we don't have to update the DOM for each item)
|
|
||||||
const itemListFragment = this._createItemsFragment(activeItems);
|
|
||||||
|
|
||||||
// If we have items to add, append them
|
|
||||||
if (itemListFragment.childNodes) {
|
|
||||||
this.itemList.append(itemListFragment);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ===== End of Private functions ====== */
|
/* ===== End of Private functions ====== */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -61,7 +61,7 @@ describe('choices', () => {
|
||||||
createTemplatesSpy = spy(instance, '_createTemplates');
|
createTemplatesSpy = spy(instance, '_createTemplates');
|
||||||
createInputSpy = spy(instance, '_createStructure');
|
createInputSpy = spy(instance, '_createStructure');
|
||||||
storeSubscribeSpy = spy(instance._store, 'subscribe');
|
storeSubscribeSpy = spy(instance._store, 'subscribe');
|
||||||
renderSpy = spy(instance, 'render');
|
renderSpy = spy(instance, '_render');
|
||||||
addEventListenersSpy = spy(instance, '_addEventListeners');
|
addEventListenersSpy = spy(instance, '_addEventListeners');
|
||||||
|
|
||||||
instance.initialised = false;
|
instance.initialised = false;
|
||||||
|
@ -90,7 +90,7 @@ describe('choices', () => {
|
||||||
|
|
||||||
it('subscribes to store with render method', () => {
|
it('subscribes to store with render method', () => {
|
||||||
expect(storeSubscribeSpy.called).to.equal(true);
|
expect(storeSubscribeSpy.called).to.equal(true);
|
||||||
expect(storeSubscribeSpy.lastCall.args[0]).to.equal(instance.render);
|
expect(storeSubscribeSpy.lastCall.args[0]).to.equal(instance._render);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('fires initial render', () => {
|
it('fires initial render', () => {
|
||||||
|
@ -1748,32 +1748,5 @@ describe('choices', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// describe('render', () => {
|
|
||||||
// beforeEach(() => {});
|
|
||||||
|
|
||||||
// describe('no change to state', () => {
|
|
||||||
// it('returns early', () => {});
|
|
||||||
// });
|
|
||||||
|
|
||||||
// describe('change to state', () => {
|
|
||||||
// it('updates previous state to current state', () => {});
|
|
||||||
|
|
||||||
// describe('select element', () => {
|
|
||||||
// it('clears choice list', () => {});
|
|
||||||
|
|
||||||
// describe('when resetScrollPosition config option is set to true', () => {
|
|
||||||
// it('scrolls to top of choice list', () => {});
|
|
||||||
// });
|
|
||||||
// });
|
|
||||||
|
|
||||||
// describe('text element', () => {
|
|
||||||
// describe('active items in store', () => {
|
|
||||||
// it('clears item list', () => {});
|
|
||||||
// it('renders active items inside item list', () => {});
|
|
||||||
// });
|
|
||||||
// });
|
|
||||||
// });
|
|
||||||
// });
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in a new issue