mirror of
https://github.com/Choices-js/Choices.git
synced 2024-06-08 08:52:19 +02:00
Make render method private
This commit is contained in:
parent
49c6a648c0
commit
4824958e4d
|
@ -32,11 +32,11 @@ Or include Choices directly:
|
|||
|
||||
```html
|
||||
<!-- 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 -->
|
||||
<link rel="stylesheet" href="public/styles/choices.min.css">
|
||||
<link rel="stylesheet" href="public/assets/styles/choices.min.css">
|
||||
<!-- Include Choices JavaScript -->
|
||||
<script src="/public/scripts/choices.min.js"></script>
|
||||
<script src="/public/assets/scripts/choices.min.js"></script>
|
||||
```
|
||||
## Setup
|
||||
|
||||
|
|
|
@ -131,7 +131,8 @@ class Choices {
|
|||
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._onBlur = this._onBlur.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
|
||||
// modify the inner objects properties in the state) 🤢
|
||||
this._initialState = cloneObject(this._store.state);
|
||||
this._store.subscribe(this.render);
|
||||
this.render();
|
||||
this._store.subscribe(this._render);
|
||||
this._render();
|
||||
this._addEventListeners();
|
||||
this.initialised = true;
|
||||
|
||||
|
@ -232,32 +233,6 @@ class Choices {
|
|||
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) {
|
||||
if (!item) {
|
||||
return this;
|
||||
|
@ -485,6 +460,123 @@ class Choices {
|
|||
= 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) {
|
||||
const groupFragment = fragment || document.createDocumentFragment();
|
||||
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) {
|
||||
let canAddItem = true;
|
||||
let notice = isType('Function', this.config.addItemText)
|
||||
|
@ -916,30 +1032,6 @@ class Choices {
|
|||
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() {
|
||||
document.addEventListener('keyup', this._onKeyUp);
|
||||
document.addEventListener('keydown', this._onKeyDown);
|
||||
|
@ -1960,97 +2052,6 @@ class Choices {
|
|||
: 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 ====== */
|
||||
}
|
||||
|
||||
|
|
|
@ -61,7 +61,7 @@ describe('choices', () => {
|
|||
createTemplatesSpy = spy(instance, '_createTemplates');
|
||||
createInputSpy = spy(instance, '_createStructure');
|
||||
storeSubscribeSpy = spy(instance._store, 'subscribe');
|
||||
renderSpy = spy(instance, 'render');
|
||||
renderSpy = spy(instance, '_render');
|
||||
addEventListenersSpy = spy(instance, '_addEventListeners');
|
||||
|
||||
instance.initialised = false;
|
||||
|
@ -90,7 +90,7 @@ describe('choices', () => {
|
|||
|
||||
it('subscribes to store with render method', () => {
|
||||
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', () => {
|
||||
|
@ -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