mirror of
https://github.com/Choices-js/Choices.git
synced 2024-06-15 12:16:11 +02:00
Integrate dropdown opening with container class
This commit is contained in:
parent
1e40b60c3f
commit
b70f062acd
|
@ -747,36 +747,9 @@ class Choices {
|
||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
showDropdown(focusInput = false) {
|
showDropdown(focusInput = false) {
|
||||||
const body = document.body;
|
this.containerOuter.open(this.dropdown.getPosition());
|
||||||
const html = document.documentElement;
|
|
||||||
const winHeight = Math.max(
|
|
||||||
body.scrollHeight,
|
|
||||||
body.offsetHeight,
|
|
||||||
html.clientHeight,
|
|
||||||
html.scrollHeight,
|
|
||||||
html.offsetHeight,
|
|
||||||
);
|
|
||||||
|
|
||||||
this.containerOuter.element.classList.add(this.config.classNames.openState);
|
|
||||||
this.containerOuter.element.setAttribute('aria-expanded', 'true');
|
|
||||||
this.dropdown.show();
|
this.dropdown.show();
|
||||||
|
|
||||||
const dimensions = this.dropdown.element.getBoundingClientRect();
|
|
||||||
const dropdownPos = Math.ceil(dimensions.top + window.scrollY + this.dropdown.offsetHeight);
|
|
||||||
|
|
||||||
// If flip is enabled and the dropdown bottom position is
|
|
||||||
// greater than the window height flip the dropdown.
|
|
||||||
let shouldFlip = false;
|
|
||||||
if (this.config.position === 'auto') {
|
|
||||||
shouldFlip = dropdownPos >= winHeight;
|
|
||||||
} else if (this.config.position === 'top') {
|
|
||||||
shouldFlip = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (shouldFlip) {
|
|
||||||
this.containerOuter.element.classList.add(this.config.classNames.flippedState);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Optionally focus the input if we have a search input
|
// Optionally focus the input if we have a search input
|
||||||
if (focusInput && this.canSearch && document.activeElement !== this.input) {
|
if (focusInput && this.canSearch && document.activeElement !== this.input) {
|
||||||
this.input.focus();
|
this.input.focus();
|
||||||
|
@ -793,26 +766,15 @@ class Choices {
|
||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
hideDropdown(blurInput = false) {
|
hideDropdown(blurInput = false) {
|
||||||
// A dropdown flips if it does not have space within the page
|
this.containerOuter.close();
|
||||||
const isFlipped = this.containerOuter.element.classList.contains(
|
|
||||||
this.config.classNames.flippedState,
|
|
||||||
);
|
|
||||||
|
|
||||||
this.containerOuter.element.classList.remove(this.config.classNames.openState);
|
|
||||||
this.containerOuter.element.setAttribute('aria-expanded', 'false');
|
|
||||||
this.dropdown.hide();
|
this.dropdown.hide();
|
||||||
|
|
||||||
if (isFlipped) {
|
|
||||||
this.containerOuter.element.classList.remove(this.config.classNames.flippedState);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Optionally blur the input if we have a search input
|
// Optionally blur the input if we have a search input
|
||||||
if (blurInput && this.canSearch && document.activeElement === this.input) {
|
if (blurInput && this.canSearch && document.activeElement === this.input) {
|
||||||
this.input.blur();
|
this.input.blur();
|
||||||
}
|
}
|
||||||
|
|
||||||
triggerEvent(this.passedElement, 'hideDropdown', {});
|
triggerEvent(this.passedElement, 'hideDropdown', {});
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -822,7 +784,12 @@ class Choices {
|
||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
toggleDropdown() {
|
toggleDropdown() {
|
||||||
this.dropdown.toggle();
|
if (this.dropdown.isActive) {
|
||||||
|
this.hideDropdown();
|
||||||
|
} else {
|
||||||
|
this.showDropdown(true);
|
||||||
|
}
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1945,7 +1912,7 @@ class Choices {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove focus state
|
// Remove focus state
|
||||||
this.containerOuter.element.classList.remove(this.config.classNames.focusState);
|
this.containerOuter.blur()
|
||||||
|
|
||||||
// Close all other dropdowns
|
// Close all other dropdowns
|
||||||
if (hasActiveDropdown) {
|
if (hasActiveDropdown) {
|
||||||
|
@ -1962,8 +1929,11 @@ class Choices {
|
||||||
*/
|
*/
|
||||||
_onMouseOver(e) {
|
_onMouseOver(e) {
|
||||||
// If the dropdown is either the target or one of its children is the target
|
// If the dropdown is either the target or one of its children is the target
|
||||||
if (e.target === this.dropdown || this.dropdown.element.contains(e.target)) {
|
if (
|
||||||
if (e.target.hasAttribute('data-choice')) this._highlightChoice(e.target);
|
(e.target === this.dropdown || this.dropdown.element.contains(e.target)) &&
|
||||||
|
e.target.hasAttribute('data-choice')
|
||||||
|
) {
|
||||||
|
this._highlightChoice(e.target);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1994,11 +1964,11 @@ class Choices {
|
||||||
const focusActions = {
|
const focusActions = {
|
||||||
text: () => {
|
text: () => {
|
||||||
if (target === this.input) {
|
if (target === this.input) {
|
||||||
this.containerOuter.element.classList.add(this.config.classNames.focusState);
|
this.containerOuter.focus();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
'select-one': () => {
|
'select-one': () => {
|
||||||
this.containerOuter.element.classList.add(this.config.classNames.focusState);
|
this.containerOuter.focus();
|
||||||
if (target === this.input) {
|
if (target === this.input) {
|
||||||
// Show dropdown if it isn't already showing
|
// Show dropdown if it isn't already showing
|
||||||
if (!hasActiveDropdown) {
|
if (!hasActiveDropdown) {
|
||||||
|
@ -2010,7 +1980,7 @@ class Choices {
|
||||||
if (target === this.input) {
|
if (target === this.input) {
|
||||||
// If element is a select box, the focused element is the container and the dropdown
|
// If element is a select box, the focused element is the container and the dropdown
|
||||||
// isn't already open, focus and show dropdown
|
// isn't already open, focus and show dropdown
|
||||||
this.containerOuter.element.classList.add(this.config.classNames.focusState);
|
this.containerOuter.focus();
|
||||||
|
|
||||||
if (!hasActiveDropdown) {
|
if (!hasActiveDropdown) {
|
||||||
this.showDropdown(true);
|
this.showDropdown(true);
|
||||||
|
@ -2040,7 +2010,7 @@ class Choices {
|
||||||
text: () => {
|
text: () => {
|
||||||
if (target === this.input) {
|
if (target === this.input) {
|
||||||
// Remove the focus state
|
// Remove the focus state
|
||||||
this.containerOuter.element.classList.remove(this.config.classNames.focusState);
|
this.containerOuter.blur();
|
||||||
// De-select any highlighted items
|
// De-select any highlighted items
|
||||||
if (hasHighlightedItems) {
|
if (hasHighlightedItems) {
|
||||||
this.unhighlightAll();
|
this.unhighlightAll();
|
||||||
|
@ -2052,7 +2022,7 @@ class Choices {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
'select-one': () => {
|
'select-one': () => {
|
||||||
this.containerOuter.element.classList.remove(this.config.classNames.focusState);
|
this.containerOuter.blur();
|
||||||
if (target === this.containerOuter.element) {
|
if (target === this.containerOuter.element) {
|
||||||
// Hide dropdown if it is showing
|
// Hide dropdown if it is showing
|
||||||
if (hasActiveDropdown && !this.canSearch) {
|
if (hasActiveDropdown && !this.canSearch) {
|
||||||
|
@ -2067,7 +2037,7 @@ class Choices {
|
||||||
'select-multiple': () => {
|
'select-multiple': () => {
|
||||||
if (target === this.input) {
|
if (target === this.input) {
|
||||||
// Remove the focus state
|
// Remove the focus state
|
||||||
this.containerOuter.element.classList.remove(this.config.classNames.focusState);
|
this.containerOuter.blur();
|
||||||
// Hide dropdown if it is showing
|
// Hide dropdown if it is showing
|
||||||
if (hasActiveDropdown) {
|
if (hasActiveDropdown) {
|
||||||
this.hideDropdown();
|
this.hideDropdown();
|
||||||
|
@ -2695,8 +2665,8 @@ class Choices {
|
||||||
const input = this._getTemplate('input');
|
const input = this._getTemplate('input');
|
||||||
const dropdown = this._getTemplate('dropdown');
|
const dropdown = this._getTemplate('dropdown');
|
||||||
|
|
||||||
this.containerOuter = new Container(this, containerOuter, this.config.classNames);
|
this.containerOuter = new Container(this, containerOuter);
|
||||||
this.containerInner = new Container(this, containerInner, this.config.classNames);
|
this.containerInner = new Container(this, containerInner);
|
||||||
this.input = input;
|
this.input = input;
|
||||||
this.choiceList = choiceList;
|
this.choiceList = choiceList;
|
||||||
this.itemList = itemList;
|
this.itemList = itemList;
|
||||||
|
|
|
@ -2,9 +2,73 @@
|
||||||
* Container
|
* Container
|
||||||
*/
|
*/
|
||||||
export default class Container {
|
export default class Container {
|
||||||
constructor(instance, element, classNames) {
|
constructor(instance, element) {
|
||||||
this.instance = instance;
|
this.instance = instance;
|
||||||
this.element = element;
|
this.element = element;
|
||||||
this.classNames = classNames;
|
this.config = instance.config;
|
||||||
|
this.classNames = instance.config.classNames;
|
||||||
|
this.isOpen = false;
|
||||||
|
this.isFlipped = false;
|
||||||
|
this.isFocussed = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
shouldFlip(dropdownPos) {
|
||||||
|
if (!dropdownPos) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const body = document.body;
|
||||||
|
const html = document.documentElement;
|
||||||
|
const winHeight = Math.max(
|
||||||
|
body.scrollHeight,
|
||||||
|
body.offsetHeight,
|
||||||
|
html.clientHeight,
|
||||||
|
html.scrollHeight,
|
||||||
|
html.offsetHeight,
|
||||||
|
);
|
||||||
|
|
||||||
|
// If flip is enabled and the dropdown bottom position is
|
||||||
|
// greater than the window height flip the dropdown.
|
||||||
|
let shouldFlip = false;
|
||||||
|
if (this.config.position === 'auto') {
|
||||||
|
shouldFlip = dropdownPos >= winHeight;
|
||||||
|
} else if (this.config.position === 'top') {
|
||||||
|
shouldFlip = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return shouldFlip;
|
||||||
|
}
|
||||||
|
|
||||||
|
open(dropdownPos) {
|
||||||
|
this.element.classList.add(this.classNames.openState);
|
||||||
|
this.element.setAttribute('aria-expanded', 'true');
|
||||||
|
this.isOpen = true;
|
||||||
|
|
||||||
|
if (this.shouldFlip(dropdownPos)) {
|
||||||
|
this.element.classList.add(this.classNames.flippedState);
|
||||||
|
this.isFlipped = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
close() {
|
||||||
|
this.element.classList.remove(this.classNames.openState);
|
||||||
|
this.element.setAttribute('aria-expanded', 'false');
|
||||||
|
this.isOpen = false;
|
||||||
|
|
||||||
|
// A dropdown flips if it does not have space within the page
|
||||||
|
if (this.isFlipped) {
|
||||||
|
this.element.classList.remove(this.classNames.flippedState);
|
||||||
|
this.isFlipped = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
focus() {
|
||||||
|
this.element.classList.add(this.classNames.focusState);
|
||||||
|
this.isFocussed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
blur() {
|
||||||
|
this.element.classList.remove(this.classNames.focusState);
|
||||||
|
this.isFocussed = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,9 +6,15 @@ export default class Dropdown {
|
||||||
this.instance = instance;
|
this.instance = instance;
|
||||||
this.element = element;
|
this.element = element;
|
||||||
this.classNames = classNames;
|
this.classNames = classNames;
|
||||||
|
this.dimensions = null;
|
||||||
|
this.position = null;
|
||||||
|
this.isActive = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
getPosition() {
|
||||||
this.dimensions = this.element.getBoundingClientRect();
|
this.dimensions = this.element.getBoundingClientRect();
|
||||||
this.position = Math.ceil(this.dimensions.top + window.scrollY + this.element.offsetHeight);
|
this.position = Math.ceil(this.dimensions.top + window.scrollY + this.element.offsetHeight);
|
||||||
this.isActive = false;
|
return this.position;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
Loading…
Reference in a new issue