mirror of
https://github.com/Choices-js/Choices.git
synced 2024-05-22 23:42:16 +02:00
Add jsdoc comments to components
This commit is contained in:
parent
15a1e9c173
commit
31a86cc4b6
|
@ -1,42 +1,47 @@
|
|||
import { wrap } from '../lib/utils';
|
||||
|
||||
/**
|
||||
* @typedef {import('../../../types/index').Choices.passedElement} passedElement
|
||||
* @typedef {import('../../../types/index').Choices.ClassNames} ClassNames
|
||||
*/
|
||||
export default class Container {
|
||||
/**
|
||||
* @param {{
|
||||
* element: HTMLElement,
|
||||
* type: passedElement['type'],
|
||||
* classNames: ClassNames,
|
||||
* position
|
||||
* }} args
|
||||
*/
|
||||
constructor({ element, type, classNames, position }) {
|
||||
Object.assign(this, { element, classNames, type, position });
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add event listeners
|
||||
*/
|
||||
addEventListeners() {
|
||||
this.element.addEventListener('focus', this._onFocus);
|
||||
this.element.addEventListener('blur', this._onBlur);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove event listeners
|
||||
*/
|
||||
|
||||
/** */
|
||||
removeEventListeners() {
|
||||
this.element.removeEventListener('focus', this._onFocus);
|
||||
this.element.removeEventListener('blur', this._onBlur);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether container should be flipped
|
||||
* based on passed dropdown position
|
||||
* @param {Number} dropdownPos
|
||||
* @returns
|
||||
* Determine whether container should be flipped based on passed
|
||||
* dropdown position
|
||||
* @param {number} dropdownPos
|
||||
* @returns {boolean}
|
||||
*/
|
||||
shouldFlip(dropdownPos) {
|
||||
if (typeof dropdownPos !== 'number') {
|
||||
|
@ -57,20 +62,19 @@ export default class Container {
|
|||
}
|
||||
|
||||
/**
|
||||
* Set active descendant attribute
|
||||
* @param {Number} activeDescendant ID of active descendant
|
||||
* @param {number} activeDescendantID
|
||||
*/
|
||||
setActiveDescendant(activeDescendantID) {
|
||||
this.element.setAttribute('aria-activedescendant', activeDescendantID);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove active descendant attribute
|
||||
*/
|
||||
removeActiveDescendant() {
|
||||
this.element.removeAttribute('aria-activedescendant');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {number} dropdownPos
|
||||
*/
|
||||
open(dropdownPos) {
|
||||
this.element.classList.add(this.classNames.openState);
|
||||
this.element.setAttribute('aria-expanded', 'true');
|
||||
|
@ -109,9 +113,6 @@ export default class Container {
|
|||
this.element.classList.remove(this.classNames.focusState);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove disabled state
|
||||
*/
|
||||
enable() {
|
||||
this.element.classList.remove(this.classNames.disabledState);
|
||||
this.element.removeAttribute('aria-disabled');
|
||||
|
@ -121,9 +122,6 @@ export default class Container {
|
|||
this.isDisabled = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set disabled state
|
||||
*/
|
||||
disable() {
|
||||
this.element.classList.add(this.classNames.disabledState);
|
||||
this.element.setAttribute('aria-disabled', 'true');
|
||||
|
@ -133,10 +131,16 @@ export default class Container {
|
|||
this.isDisabled = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Element} element
|
||||
*/
|
||||
wrap(element) {
|
||||
wrap(element, this.element);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Element} element
|
||||
*/
|
||||
unwrap(element) {
|
||||
// Move passed element outside this element
|
||||
this.element.parentNode.insertBefore(element, this.element);
|
||||
|
@ -144,34 +148,22 @@ export default class Container {
|
|||
this.element.parentNode.removeChild(this.element);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add loading state to element
|
||||
*/
|
||||
addLoadingState() {
|
||||
this.element.classList.add(this.classNames.loadingState);
|
||||
this.element.setAttribute('aria-busy', 'true');
|
||||
this.isLoading = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove loading state from element
|
||||
*/
|
||||
removeLoadingState() {
|
||||
this.element.classList.remove(this.classNames.loadingState);
|
||||
this.element.removeAttribute('aria-busy');
|
||||
this.isLoading = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set focussed state
|
||||
*/
|
||||
_onFocus() {
|
||||
this.isFocussed = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove blurred state
|
||||
*/
|
||||
_onBlur() {
|
||||
this.isFocussed = false;
|
||||
}
|
||||
|
|
|
@ -1,13 +1,26 @@
|
|||
export default class Dropdown {
|
||||
constructor({ element, type, classNames }) {
|
||||
Object.assign(this, { element, type, classNames });
|
||||
/**
|
||||
* @typedef {import('../../../types/index').Choices.passedElement} passedElement
|
||||
* @typedef {import('../../../types/index').Choices.ClassNames} ClassNames
|
||||
*/
|
||||
|
||||
export default class Dropdown {
|
||||
/**
|
||||
* @param {{
|
||||
* element: HTMLElement,
|
||||
* type: passedElement['type'],
|
||||
* classNames: ClassNames,
|
||||
* }} args
|
||||
*/
|
||||
constructor({ element, type, classNames }) {
|
||||
this.element = element;
|
||||
this.classNames = classNames;
|
||||
this.type = type;
|
||||
this.isActive = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Bottom position of dropdown in viewport coordinates
|
||||
* @type {number} Vertical position
|
||||
* @returns {number} Vertical position
|
||||
*/
|
||||
get distanceFromTopWindow() {
|
||||
return this.element.getBoundingClientRect().bottom;
|
||||
|
@ -15,7 +28,8 @@ export default class Dropdown {
|
|||
|
||||
/**
|
||||
* Find element that matches passed selector
|
||||
* @return {HTMLElement}
|
||||
* @param {string} selector
|
||||
* @returns {HTMLElement}
|
||||
*/
|
||||
getChild(selector) {
|
||||
return this.element.querySelector(selector);
|
||||
|
@ -23,8 +37,7 @@ export default class Dropdown {
|
|||
|
||||
/**
|
||||
* Show dropdown to user by adding active state class
|
||||
* @return {Object} Class instance
|
||||
* @public
|
||||
* @returns {this}
|
||||
*/
|
||||
show() {
|
||||
this.element.classList.add(this.classNames.activeState);
|
||||
|
@ -36,8 +49,7 @@ export default class Dropdown {
|
|||
|
||||
/**
|
||||
* Hide dropdown from user
|
||||
* @return {Object} Class instance
|
||||
* @public
|
||||
* @returns {this}
|
||||
*/
|
||||
hide() {
|
||||
this.element.classList.remove(this.classNames.activeState);
|
||||
|
|
|
@ -1,11 +1,18 @@
|
|||
import { sanitise } from '../lib/utils';
|
||||
|
||||
/**
|
||||
* @typedef {import('../../../types/index').Choices.passedElement} passedElement
|
||||
* @typedef {import('../../../types/index').Choices.ClassNames} ClassNames
|
||||
*/
|
||||
|
||||
export default class Input {
|
||||
/**
|
||||
*
|
||||
* @typedef {import('../../../types/index').Choices.passedElement} passedElement
|
||||
* @typedef {import('../../../types/index').Choices.ClassNames} ClassNames
|
||||
* @param {{element: HTMLInputElement, type: passedElement['type'], classNames: ClassNames, preventPaste: boolean }} p
|
||||
* @param {{
|
||||
* element: HTMLInputElement,
|
||||
* type: passedElement['type'],
|
||||
* classNames: ClassNames,
|
||||
* preventPaste: boolean
|
||||
* }} args
|
||||
*/
|
||||
constructor({ element, type, classNames, preventPaste }) {
|
||||
this.element = element;
|
||||
|
@ -21,14 +28,23 @@ export default class Input {
|
|||
this._onBlur = this._onBlur.bind(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} placeholder
|
||||
*/
|
||||
set placeholder(placeholder) {
|
||||
this.element.placeholder = placeholder;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {string}
|
||||
*/
|
||||
get value() {
|
||||
return sanitise(this.element.value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} value
|
||||
*/
|
||||
set value(value) {
|
||||
this.element.value = value;
|
||||
}
|
||||
|
@ -83,8 +99,8 @@ export default class Input {
|
|||
|
||||
/**
|
||||
* Set value of input to blank
|
||||
* @return {Object} Class instance
|
||||
* @public
|
||||
* @param {boolean} setWidth
|
||||
* @returns {this}
|
||||
*/
|
||||
clear(setWidth = true) {
|
||||
if (this.element.value) {
|
||||
|
@ -109,6 +125,9 @@ export default class Input {
|
|||
style.width = `${value.length + 1}ch`;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} activeDescendantID
|
||||
*/
|
||||
setActiveDescendant(activeDescendantID) {
|
||||
this.element.setAttribute('aria-activedescendant', activeDescendantID);
|
||||
}
|
||||
|
@ -123,6 +142,9 @@ export default class Input {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Event} event
|
||||
*/
|
||||
_onPaste(event) {
|
||||
if (this.preventPaste) {
|
||||
event.preventDefault();
|
||||
|
|
|
@ -1,9 +1,14 @@
|
|||
import { SCROLLING_SPEED } from '../constants';
|
||||
|
||||
/**
|
||||
* @typedef {import('../../../types/index').Choices.Choice} Choice
|
||||
*/
|
||||
export default class List {
|
||||
/**
|
||||
* @param {{ element: HTMLElement }} args
|
||||
*/
|
||||
constructor({ element }) {
|
||||
Object.assign(this, { element });
|
||||
|
||||
this.element = element;
|
||||
this.scrollPos = this.element.scrollTop;
|
||||
this.height = this.element.offsetHeight;
|
||||
}
|
||||
|
@ -12,14 +17,24 @@ export default class List {
|
|||
this.element.innerHTML = '';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Element} node
|
||||
*/
|
||||
append(node) {
|
||||
this.element.appendChild(node);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} selector
|
||||
* @returns {Element}
|
||||
*/
|
||||
getChild(selector) {
|
||||
return this.element.querySelector(selector);
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {boolean}
|
||||
*/
|
||||
hasChildren() {
|
||||
return this.element.hasChildNodes();
|
||||
}
|
||||
|
@ -28,28 +43,37 @@ export default class List {
|
|||
this.element.scrollTop = 0;
|
||||
}
|
||||
|
||||
scrollToChoice(choice, direction) {
|
||||
if (!choice) {
|
||||
/**
|
||||
* @param {HTMLElement} element
|
||||
* @param {1 | -1} direction
|
||||
*/
|
||||
scrollToChoice(element, direction) {
|
||||
if (!element) {
|
||||
return;
|
||||
}
|
||||
|
||||
const dropdownHeight = this.element.offsetHeight;
|
||||
const choiceHeight = choice.offsetHeight;
|
||||
const elementHeight = element.offsetHeight;
|
||||
// Distance from bottom of element to top of parent
|
||||
const choicePos = choice.offsetTop + choiceHeight;
|
||||
const elementPos = element.offsetTop + elementHeight;
|
||||
// Scroll position of dropdown
|
||||
const containerScrollPos = this.element.scrollTop + dropdownHeight;
|
||||
// Difference between the choice and scroll position
|
||||
// Difference between the element and scroll position
|
||||
const destination =
|
||||
direction > 0
|
||||
? this.element.scrollTop + choicePos - containerScrollPos
|
||||
: choice.offsetTop;
|
||||
? this.element.scrollTop + elementPos - containerScrollPos
|
||||
: element.offsetTop;
|
||||
|
||||
requestAnimationFrame(time => {
|
||||
this._animateScroll(time, destination, direction);
|
||||
requestAnimationFrame(() => {
|
||||
this._animateScroll(destination, direction);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {number} scrollPos
|
||||
* @param {number} strength
|
||||
* @param {number} destination
|
||||
*/
|
||||
_scrollDown(scrollPos, strength, destination) {
|
||||
const easing = (destination - scrollPos) / strength;
|
||||
const distance = easing > 1 ? easing : 1;
|
||||
|
@ -57,6 +81,11 @@ export default class List {
|
|||
this.element.scrollTop = scrollPos + distance;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {number} scrollPos
|
||||
* @param {number} strength
|
||||
* @param {number} destination
|
||||
*/
|
||||
_scrollUp(scrollPos, strength, destination) {
|
||||
const easing = (scrollPos - destination) / strength;
|
||||
const distance = easing > 1 ? easing : 1;
|
||||
|
@ -64,7 +93,11 @@ export default class List {
|
|||
this.element.scrollTop = scrollPos - distance;
|
||||
}
|
||||
|
||||
_animateScroll(time, destination, direction) {
|
||||
/**
|
||||
* @param {*} destination
|
||||
* @param {*} direction
|
||||
*/
|
||||
_animateScroll(destination, direction) {
|
||||
const strength = SCROLLING_SPEED;
|
||||
const choiceListScrollTop = this.element.scrollTop;
|
||||
let continueAnimation = false;
|
||||
|
@ -85,7 +118,7 @@ export default class List {
|
|||
|
||||
if (continueAnimation) {
|
||||
requestAnimationFrame(() => {
|
||||
this._animateScroll(time, destination, direction);
|
||||
this._animateScroll(destination, direction);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,20 @@
|
|||
import { dispatchEvent } from '../lib/utils';
|
||||
|
||||
/**
|
||||
* @typedef {import('../../../types/index').Choices.passedElement} passedElement
|
||||
* @typedef {import('../../../types/index').Choices.ClassNames} ClassNames
|
||||
*/
|
||||
|
||||
export default class WrappedElement {
|
||||
/**
|
||||
* @param {{
|
||||
* element: HTMLElement,
|
||||
* classNames: ClassNames,
|
||||
* }} args
|
||||
*/
|
||||
constructor({ element, classNames }) {
|
||||
Object.assign(this, { element, classNames });
|
||||
this.element = element;
|
||||
this.classNames = classNames;
|
||||
|
||||
if (!(element instanceof Element)) {
|
||||
throw new TypeError('Invalid element passed');
|
||||
|
|
|
@ -1,6 +1,17 @@
|
|||
import WrappedElement from './wrapped-element';
|
||||
|
||||
/**
|
||||
* @typedef {import('../../../types/index').Choices.ClassNames} ClassNames
|
||||
*/
|
||||
|
||||
export default class WrappedInput extends WrappedElement {
|
||||
/**
|
||||
* @param {{
|
||||
* element: HTMLInputElement,
|
||||
* classNames: ClassNames,
|
||||
* delimiter: string
|
||||
* }} args
|
||||
*/
|
||||
constructor({ element, classNames, delimiter }) {
|
||||
super({ element, classNames });
|
||||
this.delimiter = delimiter;
|
||||
|
|
|
@ -1,6 +1,18 @@
|
|||
import WrappedElement from './wrapped-element';
|
||||
|
||||
/**
|
||||
* @typedef {import('../../../types/index').Choices.ClassNames} ClassNames
|
||||
*/
|
||||
|
||||
export default class WrappedSelect extends WrappedElement {
|
||||
/**
|
||||
* @param {{
|
||||
* element: HTMLSelectElement,
|
||||
* classNames: ClassNames,
|
||||
* delimiter: string
|
||||
* template: function
|
||||
* }} args
|
||||
*/
|
||||
constructor({ element, classNames, template }) {
|
||||
super({ element, classNames });
|
||||
this.template = template;
|
||||
|
@ -14,14 +26,23 @@ export default class WrappedSelect extends WrappedElement {
|
|||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {Element[]}
|
||||
*/
|
||||
get optionGroups() {
|
||||
return Array.from(this.element.getElementsByTagName('OPTGROUP'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {object[]}
|
||||
*/
|
||||
get options() {
|
||||
return Array.from(this.element.options);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {object[]} options
|
||||
*/
|
||||
set options(options) {
|
||||
const fragment = document.createDocumentFragment();
|
||||
const addOptionToFragment = data => {
|
||||
|
@ -37,6 +58,9 @@ export default class WrappedSelect extends WrappedElement {
|
|||
this.appendDocFragment(fragment);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {DocumentFragment} fragment
|
||||
*/
|
||||
appendDocFragment(fragment) {
|
||||
this.element.innerHTML = '';
|
||||
this.element.appendChild(fragment);
|
||||
|
|
|
@ -36,7 +36,7 @@ export default class Store {
|
|||
|
||||
/**
|
||||
* Get store object (wrapping Redux method)
|
||||
* @return {object} State
|
||||
* @returns {object} State
|
||||
*/
|
||||
get state() {
|
||||
return this._store.getState();
|
||||
|
@ -44,7 +44,7 @@ export default class Store {
|
|||
|
||||
/**
|
||||
* Get items from store
|
||||
* @return {Item[]} Item objects
|
||||
* @returns {Item[]} Item objects
|
||||
*/
|
||||
get items() {
|
||||
return this.state.items;
|
||||
|
@ -52,7 +52,7 @@ export default class Store {
|
|||
|
||||
/**
|
||||
* Get active items from store
|
||||
* @return {Item[]} Item objects
|
||||
* @returns {Item[]} Item objects
|
||||
*/
|
||||
get activeItems() {
|
||||
return this.items.filter(item => item.active === true);
|
||||
|
@ -60,7 +60,7 @@ export default class Store {
|
|||
|
||||
/**
|
||||
* Get highlighted items from store
|
||||
* @return {Item[]} Item objects
|
||||
* @returns {Item[]} Item objects
|
||||
*/
|
||||
get highlightedActiveItems() {
|
||||
return this.items.filter(item => item.active && item.highlighted);
|
||||
|
@ -68,7 +68,7 @@ export default class Store {
|
|||
|
||||
/**
|
||||
* Get choices from store
|
||||
* @return {Choice[]} Option objects
|
||||
* @returns {Choice[]} Option objects
|
||||
*/
|
||||
get choices() {
|
||||
return this.state.choices;
|
||||
|
@ -76,7 +76,7 @@ export default class Store {
|
|||
|
||||
/**
|
||||
* Get active choices from store
|
||||
* @return {Choice[]} Option objects
|
||||
* @returns {Choice[]} Option objects
|
||||
*/
|
||||
get activeChoices() {
|
||||
return this.choices.filter(choice => choice.active === true);
|
||||
|
@ -84,7 +84,7 @@ export default class Store {
|
|||
|
||||
/**
|
||||
* Get selectable choices from store
|
||||
* @return {Choice[]} Option objects
|
||||
* @returns {Choice[]} Option objects
|
||||
*/
|
||||
get selectableChoices() {
|
||||
return this.choices.filter(choice => choice.disabled !== true);
|
||||
|
@ -92,7 +92,7 @@ export default class Store {
|
|||
|
||||
/**
|
||||
* Get choices that can be searched (excluding placeholders)
|
||||
* @return {Choice[]} Option objects
|
||||
* @returns {Choice[]} Option objects
|
||||
*/
|
||||
get searchableChoices() {
|
||||
return this.selectableChoices.filter(choice => choice.placeholder !== true);
|
||||
|
@ -100,7 +100,7 @@ export default class Store {
|
|||
|
||||
/**
|
||||
* Get placeholder choice from store
|
||||
* @return {Choice | undefined} Found placeholder
|
||||
* @returns {Choice | undefined} Found placeholder
|
||||
*/
|
||||
get placeholderChoice() {
|
||||
return [...this.choices]
|
||||
|
@ -110,7 +110,7 @@ export default class Store {
|
|||
|
||||
/**
|
||||
* Get groups from store
|
||||
* @return {Group[]} Group objects
|
||||
* @returns {Group[]} Group objects
|
||||
*/
|
||||
get groups() {
|
||||
return this.state.groups;
|
||||
|
@ -118,7 +118,7 @@ export default class Store {
|
|||
|
||||
/**
|
||||
* Get active groups from store
|
||||
* @return {Group[]} Group objects
|
||||
* @returns {Group[]} Group objects
|
||||
*/
|
||||
get activeGroups() {
|
||||
const { groups, choices } = this;
|
||||
|
@ -135,7 +135,7 @@ export default class Store {
|
|||
|
||||
/**
|
||||
* Get loading state from store
|
||||
* @return {boolean} Loading State
|
||||
* @returns {boolean} Loading State
|
||||
*/
|
||||
isLoading() {
|
||||
return this.state.general.loading;
|
||||
|
@ -144,7 +144,7 @@ export default class Store {
|
|||
/**
|
||||
* Get single choice by it's ID
|
||||
* @param {string} id
|
||||
* @return {Choice | undefined} Found choice
|
||||
* @returns {Choice | undefined} Found choice
|
||||
*/
|
||||
getChoiceById(id) {
|
||||
return this.activeChoices.find(choice => choice.id === parseInt(id, 10));
|
||||
|
@ -153,7 +153,7 @@ export default class Store {
|
|||
/**
|
||||
* Get group by group id
|
||||
* @param {string} id Group ID
|
||||
* @return {Group | undefined} Group data
|
||||
* @returns {Group | undefined} Group data
|
||||
*/
|
||||
getGroupById(id) {
|
||||
return this.groups.find(group => group.id === parseInt(id, 10));
|
||||
|
|
Loading…
Reference in a new issue