Choices/src/scripts/lib/utils.ts
Josh Johnson 68313da412
Convert to typescript (#795)
* Typescript config setup

* Add type annotations to components

* Further type additions

* And more...

* Add types to actions

* Add types to templates

* Further type checks

* Further type additons

* Install fuse latest

* Housekeeping

* Remove old type definitions

* Fix remaning type issues

* Fix some failing tests

* Remove types workflow

* Fix failing unit tests

* Resolve back space event regression

* Convert cypress files to .ts

* Fix eslint issues

* Remove cachebusting urls

* Resolve delete button bug

* Resolve regression bugs

* Fix lint script

* Fix lint workflow

* Pass args instead of object to keyboard handlers

* Flatten misc reducer

* Resolve keyboad action test failures

* Use Pick instead of Partial

* Use interfaces in action tests

* Update firefox image

* Incorporate #791

* Incorporate #788
2019-12-23 18:22:54 +00:00

181 lines
4.1 KiB
TypeScript

import { EventMap, Choice } from '../interfaces';
/* eslint-disable @typescript-eslint/no-explicit-any */
export const getRandomNumber = (min: number, max: number): number =>
Math.floor(Math.random() * (max - min) + min);
export const generateChars = (length: number): string =>
Array.from({ length }, () => getRandomNumber(0, 36).toString(36)).join('');
export const generateId = (
element: HTMLInputElement | HTMLSelectElement,
prefix: string,
): string => {
let id =
element.id ||
(element.name && `${element.name}-${generateChars(2)}`) ||
generateChars(4);
id = id.replace(/(:|\.|\[|\]|,)/g, '');
id = `${prefix}-${id}`;
return id;
};
export const getType = (obj: any): string =>
Object.prototype.toString.call(obj).slice(8, -1);
export const isType = (type: string, obj: any): boolean =>
obj !== undefined && obj !== null && getType(obj) === type;
export const wrap = (
element: HTMLElement,
wrapper: HTMLElement = document.createElement('div'),
): HTMLElement => {
if (element.nextSibling) {
element.parentNode &&
element.parentNode.insertBefore(wrapper, element.nextSibling);
} else {
element.parentNode && element.parentNode.appendChild(wrapper);
}
return wrapper.appendChild(element);
};
export const getAdjacentEl = (
startEl: Element,
selector: string,
direction = 1,
): Element => {
const prop = `${direction > 0 ? 'next' : 'previous'}ElementSibling`;
let sibling = startEl[prop];
while (sibling) {
if (sibling.matches(selector)) {
return sibling;
}
sibling = sibling[prop];
}
return sibling;
};
export const isScrolledIntoView = (
element: HTMLElement,
parent: HTMLElement,
direction = 1,
): boolean => {
if (!element) {
return false;
}
let isVisible;
if (direction > 0) {
// In view from bottom
isVisible =
parent.scrollTop + parent.offsetHeight >=
element.offsetTop + element.offsetHeight;
} else {
// In view from top
isVisible = element.offsetTop >= parent.scrollTop;
}
return isVisible;
};
export const sanitise = <T>(value: T | string): T | string => {
if (typeof value !== 'string') {
return value;
}
return value
.replace(/&/g, '&amp;')
.replace(/>/g, '&rt;')
.replace(/</g, '&lt;')
.replace(/"/g, '&quot;');
};
export const strToEl = ((): ((str: string) => Element) => {
const tmpEl = document.createElement('div');
return (str): Element => {
const cleanedInput = str.trim();
tmpEl.innerHTML = cleanedInput;
const firldChild = tmpEl.children[0];
while (tmpEl.firstChild) {
tmpEl.removeChild(tmpEl.firstChild);
}
return firldChild;
};
})();
interface RecordToCompare {
value: string;
label?: string;
}
export const sortByAlpha = (
{ value, label = value }: RecordToCompare,
{ value: value2, label: label2 = value2 }: RecordToCompare,
): number =>
label.localeCompare(label2, [], {
sensitivity: 'base',
ignorePunctuation: true,
numeric: true,
});
export const sortByScore = (
a: Pick<Choice, 'score'>,
b: Pick<Choice, 'score'>,
): number => {
const { score: scoreA = 0 } = a;
const { score: scoreB = 0 } = b;
return scoreA - scoreB;
};
export const dispatchEvent = (
element: HTMLElement,
type: keyof EventMap,
customArgs: object | null = null,
): boolean => {
const event = new CustomEvent(type, {
detail: customArgs,
bubbles: true,
cancelable: true,
});
return element.dispatchEvent(event);
};
export const existsInArray = (
array: any[],
value: string,
key = 'value',
): boolean =>
array.some(item => {
if (typeof value === 'string') {
return item[key] === value.trim();
}
return item[key] === value;
});
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
*/
export const diff = (
a: Record<string, any>,
b: Record<string, any>,
): string[] => {
const aKeys = Object.keys(a).sort();
const bKeys = Object.keys(b).sort();
return aKeys.filter(i => bKeys.indexOf(i) < 0);
};