And more...

This commit is contained in:
Josh Johnson 2019-12-14 16:40:15 +00:00
parent 585ee6457f
commit 3f850f61d6
4 changed files with 78 additions and 107 deletions

View file

@ -1503,7 +1503,8 @@ class Choices {
}: Pick<KeyDownAction, 'event' | 'activeItems' | 'hasActiveDropdown'>): void {
const { target } = event;
const { ENTER_KEY: enterKey } = KEY_CODES;
const targetWasButton = target && target.hasAttribute('data-button');
const targetWasButton =
target && (target as HTMLElement).hasAttribute('data-button');
if (this._isTextElement && target && target.value) {
const { value } = this.input;
@ -1518,7 +1519,7 @@ class Choices {
}
if (targetWasButton) {
this._handleButtonAction(activeItems, target);
this._handleButtonAction(activeItems, target as HTMLElement);
event.preventDefault();
}
@ -1618,6 +1619,10 @@ class Choices {
hasFocusedInput,
activeItems,
}: Partial<KeyDownAction>): void {
if (!event || event.type !== 'KeyboardEvent' || !event.target) {
return;
}
const { target } = event;
// If backspace or delete key is pressed and the input has no value
if (hasFocusedInput && !target.value && !this._isSelectOneElement) {
@ -1635,7 +1640,7 @@ class Choices {
_onTouchEnd(event: TouchEvent): void {
const { target } = event || event.touches[0];
const touchWasWithinContainer =
this._wasTap && this.containerOuter.element.contains(target);
this._wasTap && this.containerOuter.element.contains(target as Node);
if (touchWasWithinContainer) {
const containerWasExactTarget =
@ -1829,7 +1834,7 @@ class Choices {
}
_highlightChoice(el: HTMLElement | null = null): void {
const choices = Array.from(
const choices: HTMLElement[] = Array.from(
this.dropdown.element.querySelectorAll('[data-choice-selectable]'),
);
@ -2059,7 +2064,10 @@ class Choices {
}
}
_getTemplate<K extends keyof Templates>(template: K, ...args): Templates[K] {
_getTemplate<K extends keyof Templates>(
template: K,
...args: any
): HTMLElement | HTMLOptionElement {
const { classNames } = this.config;
return this._templates[template].call(this, classNames, ...args);

View file

@ -4,7 +4,7 @@ import { ClassNames, Item, Choice } from '../interfaces';
export default class WrappedSelect extends WrappedElement {
element: HTMLSelectElement;
classNames: ClassNames;
template: () => HTMLElement;
template: (data: object) => HTMLOptionElement;
constructor({
element,
@ -13,7 +13,7 @@ export default class WrappedSelect extends WrappedElement {
}: {
element: HTMLSelectElement;
classNames: ClassNames;
template: () => HTMLElement;
template: (data: object) => HTMLOptionElement;
}) {
super({ element, classNames });
this.template = template;
@ -37,7 +37,7 @@ export default class WrappedSelect extends WrappedElement {
set options(options: Item[] | Choice[]): void {
const fragment = document.createDocumentFragment();
const addOptionToFragment = data => {
const addOptionToFragment = (data): void => {
// Create a standard select option
const option = this.template(data);
// Append it to fragment

View file

@ -1,3 +1,4 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import Choices from './choices';
export namespace Types {

View file

@ -1,24 +1,17 @@
/**
* @param {number} min
* @param {number} max
* @returns {number}
*/
export const getRandomNumber = (min, max) =>
import { EventMap } from '../interfaces';
/* eslint-disable @typescript-eslint/no-explicit-any */
export const getRandomNumber = (min: number, max: number): number =>
Math.floor(Math.random() * (max - min) + min);
/**
* @param {number} length
* @returns {string}
*/
export const generateChars = length =>
export const generateChars = (length: number): string =>
Array.from({ length }, () => getRandomNumber(0, 36).toString(36)).join('');
/**
* @param {HTMLInputElement | HTMLSelectElement} element
* @param {string} prefix
* @returns {string}
*/
export const generateId = (element, prefix) => {
export const generateId = (
element: HTMLInputElement | HTMLSelectElement,
prefix: string,
): string => {
let id =
element.id ||
(element.name && `${element.name}-${generateChars(2)}`) ||
@ -29,46 +22,31 @@ export const generateId = (element, prefix) => {
return id;
};
/**
* @param {any} obj
* @returns {string}
*/
export const getType = obj => Object.prototype.toString.call(obj).slice(8, -1);
export const getType = (obj: any): string =>
Object.prototype.toString.call(obj).slice(8, -1);
/**
* @param {string} type
* @param {any} obj
* @returns {boolean}
*/
export const isType = (type, obj) =>
export const isType = (type: string, obj: any): boolean =>
obj !== undefined && obj !== null && getType(obj) === type;
/**
* @param {HTMLElement} element
* @param {HTMLElement} [wrapper={HTMLDivElement}]
* @returns {HTMLElement}
*/
export const wrap = (element, wrapper = document.createElement('div')) => {
export const wrap = (
element: HTMLElement,
wrapper: HTMLElement = document.createElement('div'),
): HTMLElement => {
if (element.nextSibling) {
element.parentNode.insertBefore(wrapper, element.nextSibling);
element.parentNode &&
element.parentNode.insertBefore(wrapper, element.nextSibling);
} else {
element.parentNode.appendChild(wrapper);
element.parentNode && element.parentNode.appendChild(wrapper);
}
return wrapper.appendChild(element);
};
/**
* @param {Element} startEl
* @param {string} selector
* @param {1 | -1} direction
* @returns {Element | undefined}
*/
export const getAdjacentEl = (startEl, selector, direction = 1) => {
if (!(startEl instanceof Element) || typeof selector !== 'string') {
return undefined;
}
export const getAdjacentEl = (
startEl: Element,
selector: string,
direction = 1,
): Element => {
const prop = `${direction > 0 ? 'next' : 'previous'}ElementSibling`;
let sibling = startEl[prop];
@ -82,13 +60,11 @@ export const getAdjacentEl = (startEl, selector, direction = 1) => {
return sibling;
};
/**
* @param {Element} element
* @param {Element} parent
* @param {-1 | 1} direction
* @returns {boolean}
*/
export const isScrolledIntoView = (element, parent, direction = 1) => {
export const isScrolledIntoView = (
element: HTMLElement,
parent: HTMLElement,
direction = 1,
): boolean => {
if (!element) {
return false;
}
@ -108,11 +84,7 @@ export const isScrolledIntoView = (element, parent, direction = 1) => {
return isVisible;
};
/**
* @param {any} value
* @returns {any}
*/
export const sanitise = value => {
export const sanitise = <T>(value: T | string): T | string => {
if (typeof value !== 'string') {
return value;
}
@ -124,13 +96,10 @@ export const sanitise = value => {
.replace(/"/g, '&quot;');
};
/**
* @returns {() => (str: string) => Element}
*/
export const strToEl = (() => {
export const strToEl = ((): ((str: string) => Element) => {
const tmpEl = document.createElement('div');
return str => {
return (str): Element => {
const cleanedInput = str.trim();
tmpEl.innerHTML = cleanedInput;
const firldChild = tmpEl.children[0];
@ -143,33 +112,31 @@ export const strToEl = (() => {
};
})();
/**
* @param {{ label?: string, value: string }} a
* @param {{ label?: string, value: string }} b
* @returns {number}
*/
interface RecordToCompare {
value: string;
label?: string;
}
export const sortByAlpha = (
{ value, label = value },
{ value: value2, label: label2 = value2 },
) =>
{ value, label = value }: RecordToCompare,
{ value: value2, label: label2 = value2 }: RecordToCompare,
): number =>
label.localeCompare(label2, [], {
sensitivity: 'base',
ignorePunctuation: true,
numeric: true,
});
/**
* @param {{ score: number }} a
* @param {{ score: number }} b
*/
export const sortByScore = (a, b) => a.score - b.score;
interface RecordToSort {
score: number;
}
export const sortByScore = (a: RecordToSort, b: RecordToSort): number =>
a.score - b.score;
/**
* @param {HTMLElement} element
* @param {string} type
* @param {object} customArgs
*/
export const dispatchEvent = (element, type, customArgs = null) => {
export const dispatchEvent = (
element: HTMLElement,
type: keyof EventMap,
customArgs: object | null = null,
): boolean => {
const event = new CustomEvent(type, {
detail: customArgs,
bubbles: true,
@ -179,13 +146,11 @@ export const dispatchEvent = (element, type, customArgs = null) => {
return element.dispatchEvent(event);
};
/**
* @param {array} array
* @param {any} value
* @param {string} [key="value"]
* @returns {boolean}
*/
export const existsInArray = (array, value, key = 'value') =>
export const existsInArray = (
array: any[],
value: string,
key = 'value',
): boolean =>
array.some(item => {
if (typeof value === 'string') {
return item[key] === value.trim();
@ -194,19 +159,16 @@ export const existsInArray = (array, value, key = 'value') =>
return item[key] === value;
});
/**
* @param {any} obj
* @returns {any}
*/
export const cloneObject = obj => JSON.parse(JSON.stringify(obj));
export const cloneObject = (obj: object): object =>
JSON.parse(JSON.stringify(obj));
/**
* Returns an array of keys present on the first but missing on the second object
* @param {object} a
* @param {object} b
* @returns {string[]}
*/
export const diff = (a, b) => {
export const diff = (
a: Record<string, any>,
b: Record<string, any>,
): string[] => {
const aKeys = Object.keys(a).sort();
const bKeys = Object.keys(b).sort();