mirror of
https://github.com/Choices-js/Choices.git
synced 2024-04-26 19:30:31 +02:00
179 lines
4.5 KiB
TypeScript
179 lines
4.5 KiB
TypeScript
import { wrap } from '../lib/utils';
|
|
import { SELECT_ONE_TYPE } from '../constants';
|
|
import { ClassNames } from '../interfaces/class-names';
|
|
import { PositionOptionsType } from '../interfaces/position-options-type';
|
|
import { PassedElementType } from '../interfaces/passed-element-type';
|
|
|
|
export default class Container {
|
|
element: HTMLElement;
|
|
|
|
type: PassedElementType;
|
|
|
|
classNames: ClassNames;
|
|
|
|
position: PositionOptionsType;
|
|
|
|
isOpen: boolean;
|
|
|
|
isFlipped: boolean;
|
|
|
|
isFocussed: boolean;
|
|
|
|
isDisabled: boolean;
|
|
|
|
isLoading: boolean;
|
|
|
|
constructor({
|
|
element,
|
|
type,
|
|
classNames,
|
|
position,
|
|
}: {
|
|
element: HTMLElement;
|
|
type: PassedElementType;
|
|
classNames: ClassNames;
|
|
position: PositionOptionsType;
|
|
}) {
|
|
this.element = element;
|
|
this.classNames = classNames;
|
|
this.type = type;
|
|
this.position = position;
|
|
this.isOpen = false;
|
|
this.isFlipped = false;
|
|
this.isFocussed = false;
|
|
this.isDisabled = false;
|
|
this.isLoading = false;
|
|
this._onFocus = this._onFocus.bind(this);
|
|
this._onBlur = this._onBlur.bind(this);
|
|
}
|
|
|
|
addEventListeners(): void {
|
|
this.element.addEventListener('focus', this._onFocus);
|
|
this.element.addEventListener('blur', this._onBlur);
|
|
}
|
|
|
|
removeEventListeners(): void {
|
|
this.element.removeEventListener('focus', this._onFocus);
|
|
this.element.removeEventListener('blur', this._onBlur);
|
|
}
|
|
|
|
/**
|
|
* Determine whether container should be flipped based on passed
|
|
* dropdown position
|
|
*/
|
|
shouldFlip(dropdownPos: number): boolean {
|
|
if (typeof dropdownPos !== 'number') {
|
|
return false;
|
|
}
|
|
|
|
// If flip is enabled and the dropdown bottom position is
|
|
// greater than the window height flip the dropdown.
|
|
let shouldFlip = false;
|
|
if (this.position === 'auto') {
|
|
shouldFlip = !window.matchMedia(`(min-height: ${dropdownPos + 1}px)`)
|
|
.matches;
|
|
} else if (this.position === 'top') {
|
|
shouldFlip = true;
|
|
}
|
|
|
|
return shouldFlip;
|
|
}
|
|
|
|
setActiveDescendant(activeDescendantID: string): void {
|
|
this.element.setAttribute('aria-activedescendant', activeDescendantID);
|
|
}
|
|
|
|
removeActiveDescendant(): void {
|
|
this.element.removeAttribute('aria-activedescendant');
|
|
}
|
|
|
|
open(dropdownPos: number): void {
|
|
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(): void {
|
|
this.element.classList.remove(this.classNames.openState);
|
|
this.element.setAttribute('aria-expanded', 'false');
|
|
this.removeActiveDescendant();
|
|
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(): void {
|
|
if (!this.isFocussed) {
|
|
this.element.focus();
|
|
}
|
|
}
|
|
|
|
addFocusState(): void {
|
|
this.element.classList.add(this.classNames.focusState);
|
|
}
|
|
|
|
removeFocusState(): void {
|
|
this.element.classList.remove(this.classNames.focusState);
|
|
}
|
|
|
|
enable(): void {
|
|
this.element.classList.remove(this.classNames.disabledState);
|
|
this.element.removeAttribute('aria-disabled');
|
|
if (this.type === SELECT_ONE_TYPE) {
|
|
this.element.setAttribute('tabindex', '0');
|
|
}
|
|
this.isDisabled = false;
|
|
}
|
|
|
|
disable(): void {
|
|
this.element.classList.add(this.classNames.disabledState);
|
|
this.element.setAttribute('aria-disabled', 'true');
|
|
if (this.type === SELECT_ONE_TYPE) {
|
|
this.element.setAttribute('tabindex', '-1');
|
|
}
|
|
this.isDisabled = true;
|
|
}
|
|
|
|
wrap(element: HTMLSelectElement | HTMLInputElement | HTMLElement): void {
|
|
wrap(element, this.element);
|
|
}
|
|
|
|
unwrap(element: HTMLElement): void {
|
|
if (this.element.parentNode) {
|
|
// Move passed element outside this element
|
|
this.element.parentNode.insertBefore(element, this.element);
|
|
// Remove this element
|
|
this.element.parentNode.removeChild(this.element);
|
|
}
|
|
}
|
|
|
|
addLoadingState(): void {
|
|
this.element.classList.add(this.classNames.loadingState);
|
|
this.element.setAttribute('aria-busy', 'true');
|
|
this.isLoading = true;
|
|
}
|
|
|
|
removeLoadingState(): void {
|
|
this.element.classList.remove(this.classNames.loadingState);
|
|
this.element.removeAttribute('aria-busy');
|
|
this.isLoading = false;
|
|
}
|
|
|
|
_onFocus(): void {
|
|
this.isFocussed = true;
|
|
}
|
|
|
|
_onBlur(): void {
|
|
this.isFocussed = false;
|
|
}
|
|
}
|