UNPKG

@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
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 };