@varlet/ui
Version:
A Vue3 component library based on Material Design 2 and 3, supporting mobile and desktop.
238 lines (237 loc) • 7.09 kB
JavaScript
import {
assert,
getRect,
getScrollLeft,
getScrollTop,
getStyle,
inBrowser,
isNumber,
isNumeric,
isObject,
isString,
isWindow,
kebabCase,
toNumber
} from "@varlet/shared";
function getLeft(element) {
const { left } = getRect(element);
return left + (document.body.scrollLeft || document.documentElement.scrollLeft);
}
function getTop(element) {
const { top } = getRect(element);
return top + (document.body.scrollTop || document.documentElement.scrollTop);
}
function getTranslateY(el) {
const { transform } = getStyle(el);
return +transform.slice(transform.lastIndexOf(",") + 2, transform.length - 1);
}
function getParentScroller(el) {
let element = el;
while (element) {
if (!element.parentNode) {
break;
}
element = element.parentNode;
if (element === document.body || element === document.documentElement) {
break;
}
const scrollRE = /(scroll|auto)/;
const { overflowY, overflow } = getStyle(element);
if (scrollRE.test(overflowY) || scrollRE.test(overflow)) {
return element;
}
}
return window;
}
function getAllParentScroller(el) {
const allParentScroller = [];
let element = el;
while (!isWindow(element)) {
element = getParentScroller(element);
allParentScroller.push(element);
}
return allParentScroller;
}
function getTarget(target, componentName) {
if (isString(target)) {
const el = document.querySelector(target);
assert(!!el, componentName, "target element cannot found");
return el;
}
assert(isObject(target), componentName, 'type of prop "target" should be an element object');
return target;
}
function getViewportSize() {
const { width, height } = getRect(window);
return {
vw: width,
vh: height,
vMin: Math.min(width, height),
vMax: Math.max(width, height)
};
}
const isRem = (value) => isString(value) && value.endsWith("rem");
const isEm = (value) => isString(value) && value.endsWith("em") && !value.endsWith("rem");
const isPx = (value) => isString(value) && value.endsWith("px") || isNumber(value);
const isPercent = (value) => isString(value) && value.endsWith("%");
const isVw = (value) => isString(value) && value.endsWith("vw");
const isVh = (value) => isString(value) && value.endsWith("vh");
const isVMin = (value) => isString(value) && value.endsWith("vmin");
const isVMax = (value) => isString(value) && value.endsWith("vmax");
const isCalc = (value) => isString(value) && value.startsWith("calc(");
const isVar = (value) => isString(value) && value.startsWith("var(");
const toPxNum = (value) => {
if (isNumeric(value)) {
return Number(value);
}
if (isPx(value)) {
return +value.replace("px", "");
}
if (!inBrowser()) {
return 0;
}
const { vw, vh, vMin, vMax } = getViewportSize();
if (isVw(value)) {
return +value.replace("vw", "") * vw / 100;
}
if (isVh(value)) {
return +value.replace("vh", "") * vh / 100;
}
if (isVMin(value)) {
return +value.replace("vmin", "") * vMin / 100;
}
if (isVMax(value)) {
return +value.replace("vmax", "") * vMax / 100;
}
if (isRem(value)) {
const num = +value.replace("rem", "");
const rootFontSize = getStyle(document.documentElement).fontSize;
return num * parseFloat(rootFontSize);
}
if (isString(value)) {
return toNumber(value);
}
return 0;
};
const toSizeUnit = (value) => {
if (value == null) {
return void 0;
}
if (isNumeric(value)) {
return `${value}px`;
}
return String(value);
};
const multiplySizeUnit = (value, quantity = 1) => {
if (value == null) {
return void 0;
}
const legalSize = toSizeUnit(value);
const unit = legalSize.match(/(vh|%|r?em|px|vw|vmin|vmax)$/)[0];
return `${parseFloat(legalSize) * quantity}${unit}`;
};
function scrollTo(element, { top = 0, left = 0, duration = 300, animation }) {
const startTime = Date.now();
const scrollTop = getScrollTop(element);
const scrollLeft = getScrollLeft(element);
return new Promise((resolve) => {
const frame = () => {
const progress = (Date.now() - startTime) / duration;
if (progress < 1) {
const nextTop = scrollTop + (top - scrollTop) * animation(progress);
const nextLeft = scrollLeft + (left - scrollLeft) * animation(progress);
element.scrollTo(nextLeft, nextTop);
requestAnimationFrame(frame);
} else {
element.scrollTo(left, top);
resolve();
}
};
requestAnimationFrame(frame);
});
}
function formatStyleVars(styleVars) {
return Object.entries(styleVars != null ? styleVars : {}).reduce((styles, [key, value]) => {
const cssVar = key.startsWith("--") ? key : `--${kebabCase(key)}`;
styles[cssVar] = value;
return styles;
}, {});
}
function padStartFlex(style) {
return style === "start" || style === "end" ? `flex-${style}` : style;
}
function isDisplayNoneElement(element) {
let parent = element;
while (parent && parent !== document.documentElement) {
if (getStyle(parent).display === "none") {
return true;
}
parent = parent.parentNode;
}
return false;
}
const focusableSelector = ["button", "input", "select", "textarea", "[tabindex]", "[href]"].map((s) => `${s}:not([disabled])`).join(", ");
function focusChildElementByKey(referenceElement, parentElement, key, beforeFocus) {
const focusableElements = Array.from(parentElement.querySelectorAll(focusableSelector)).filter(
(element) => !isDisplayNoneElement(element)
);
if (!focusableElements.length) {
return;
}
const isActiveInReferenceElements = [referenceElement, ...Array.from(referenceElement.querySelectorAll(focusableSelector))].findIndex(
(el) => el === document.activeElement
) !== -1;
const activeElementIndex = Array.from(focusableElements).findIndex((el) => el === document.activeElement);
if (key === "ArrowDown") {
if (isActiveInReferenceElements && activeElementIndex === -1 || activeElementIndex === focusableElements.length - 1) {
focus(focusableElements[0]);
return;
}
if (activeElementIndex !== -1 && activeElementIndex < focusableElements.length - 1) {
focus(focusableElements[activeElementIndex + 1]);
return;
}
}
if (key === "ArrowUp") {
if (isActiveInReferenceElements && activeElementIndex === -1 || activeElementIndex === 0) {
focus(focusableElements[focusableElements.length - 1]);
return;
}
if (activeElementIndex > 0) {
focus(focusableElements[activeElementIndex - 1]);
}
}
function focus(nextActiveElement) {
if (beforeFocus && !beforeFocus(document.activeElement, nextActiveElement, isActiveInReferenceElements)) {
return;
}
nextActiveElement.focus();
}
}
export {
focusChildElementByKey,
formatStyleVars,
getAllParentScroller,
getLeft,
getParentScroller,
getTarget,
getTop,
getTranslateY,
getViewportSize,
isCalc,
isDisplayNoneElement,
isEm,
isPercent,
isPx,
isRem,
isVMax,
isVMin,
isVar,
isVh,
isVw,
multiplySizeUnit,
padStartFlex,
scrollTo,
toPxNum,
toSizeUnit
};