preline
Version:
Preline UI is an open-source set of prebuilt UI components based on the utility-first Tailwind CSS framework.
278 lines (228 loc) • 6.18 kB
text/typescript
/*
* @version: 3.2.2
* @author: Preline Labs Ltd.
* @license: Licensed under MIT and Preline UI Fair Use License (https://preline.co/docs/license.html)
* Copyright 2024 Preline Labs Ltd.
*/
const stringToBoolean = (string: string): boolean => {
return string === "true" ? true : false;
};
const getClassProperty = (el: HTMLElement, prop: string, val = "") => {
return (window.getComputedStyle(el).getPropertyValue(prop) || val).replace(
" ",
"",
);
};
const getClassPropertyAlt = (
el: HTMLElement,
prop?: string,
val: string = "",
) => {
let targetClass = "";
el.classList.forEach((c) => {
if (c.includes(prop)) {
targetClass = c;
}
});
return targetClass.match(/:(.*)]/) ? targetClass.match(/:(.*)]/)[1] : val;
};
const getZIndex = (el: HTMLElement) => {
const computedStyle = window.getComputedStyle(el);
const zIndex = computedStyle.getPropertyValue("z-index");
return zIndex;
};
const getHighestZIndex = (arr: HTMLElement[]) => {
let highestZIndex = Number.NEGATIVE_INFINITY;
arr.forEach((el) => {
let zIndex: string | number = getZIndex(el);
if (zIndex !== "auto") {
zIndex = parseInt(zIndex, 10);
if (zIndex > highestZIndex) highestZIndex = zIndex;
}
});
return highestZIndex;
};
const isDirectChild = (parent: Element, child: HTMLElement) => {
const children = parent.children;
for (let i = 0; i < children.length; i++) {
if (children[i] === child) return true;
}
return false;
};
const isEnoughSpace = (
el: HTMLElement,
toggle: HTMLElement,
preferredPosition: "top" | "bottom" | "auto" = "auto",
space = 10,
wrapper: HTMLElement | null = null,
) => {
const referenceRect = toggle.getBoundingClientRect();
const wrapperRect = wrapper ? wrapper.getBoundingClientRect() : null;
const viewportHeight = window.innerHeight;
const spaceAbove = wrapperRect
? referenceRect.top - wrapperRect.top
: referenceRect.top;
const spaceBelow = (wrapper ? wrapperRect.bottom : viewportHeight) -
referenceRect.bottom;
const minimumSpaceRequired = el.clientHeight + space;
if (preferredPosition === "bottom") {
return spaceBelow >= minimumSpaceRequired;
} else if (preferredPosition === "top") {
return spaceAbove >= minimumSpaceRequired;
} else {
return (
spaceAbove >= minimumSpaceRequired || spaceBelow >= minimumSpaceRequired
);
}
};
const isFocused = (target: HTMLElement) => {
return document.activeElement === target;
};
const isFormElement = (target: HTMLElement) => {
return (
target instanceof HTMLInputElement ||
target instanceof HTMLTextAreaElement ||
target instanceof HTMLSelectElement
);
};
const isIOS = () => {
if (/iPad|iPhone|iPod/.test(navigator.platform)) {
return true;
} else {
return (
navigator.maxTouchPoints &&
navigator.maxTouchPoints > 2 &&
/MacIntel/.test(navigator.platform)
);
}
};
const isIpadOS = () => {
return (
navigator.maxTouchPoints &&
navigator.maxTouchPoints > 2 &&
/MacIntel/.test(navigator.platform)
);
};
const isJson = (str: string) => {
if (typeof str !== "string") return false;
const firstChar = str.trim()[0];
const lastChar = str.trim().slice(-1);
if (
(firstChar === "{" && lastChar === "}") ||
(firstChar === "[" && lastChar === "]")
) {
try {
JSON.parse(str);
return true;
} catch {
return false;
}
}
return false;
};
const isParentOrElementHidden = (element: any): any => {
if (!element) return false;
const computedStyle = window.getComputedStyle(element);
if (computedStyle.display === "none") return true;
return isParentOrElementHidden(element.parentElement);
};
const isScrollable = (el: HTMLElement) => {
const style = window.getComputedStyle(el);
const overflowY = style.overflowY;
const overflowX = style.overflowX;
const canScrollVertically =
(overflowY === "scroll" || overflowY === "auto") &&
el.scrollHeight > el.clientHeight;
const canScrollHorizontally =
(overflowX === "scroll" || overflowX === "auto") &&
el.scrollWidth > el.clientWidth;
return canScrollVertically || canScrollHorizontally;
};
const debounce = (func: Function, timeout = 200) => {
let timer: any;
return (...args: any[]) => {
clearTimeout(timer);
timer = setTimeout(() => {
func.apply(this, args);
}, timeout);
};
};
const dispatch = (evt: string, element: any, payload: any = null) => {
const event = new CustomEvent(evt, {
detail: { payload },
bubbles: true,
cancelable: true,
composed: false,
});
element.dispatchEvent(event);
};
const afterTransition = (el: HTMLElement, callback: Function) => {
const handleEvent = () => {
callback();
el.removeEventListener("transitionend", handleEvent, true);
};
const computedStyle = window.getComputedStyle(el);
const transitionDuration = computedStyle.getPropertyValue(
"transition-duration",
);
const transitionProperty = computedStyle.getPropertyValue(
"transition-property",
);
const hasTransition = transitionProperty !== "none" &&
parseFloat(transitionDuration) > 0;
if (hasTransition) el.addEventListener("transitionend", handleEvent, true);
else callback();
};
const htmlToElement = (html: string): HTMLElement => {
const template = document.createElement("template");
html = html.trim();
template.innerHTML = html;
return template.content.firstChild as HTMLElement;
};
const classToClassList = (
classes: string,
target: HTMLElement,
splitter = " ",
action: "add" | "remove" = "add",
) => {
const classesToArray = classes.split(splitter);
classesToArray.forEach((cl) => {
if (cl.trim()) {
action === "add" ? target.classList.add(cl) : target.classList.remove(cl);
}
});
};
const menuSearchHistory = {
historyIndex: -1,
addHistory(index: number) {
this.historyIndex = index;
},
existsInHistory(index: number) {
return index > this.historyIndex;
},
clearHistory() {
this.historyIndex = -1;
},
};
export {
afterTransition,
classToClassList,
debounce,
dispatch,
getClassProperty,
getClassPropertyAlt,
getHighestZIndex,
getZIndex,
htmlToElement,
isDirectChild,
isEnoughSpace,
isFocused,
isFormElement,
isIOS,
isIpadOS,
isJson,
isParentOrElementHidden,
isScrollable,
menuSearchHistory,
stringToBoolean,
};