UNPKG

scaler-js

Version:

Your best ally for easily building responsive web applications

199 lines (186 loc) 8.52 kB
function isValidJsonString(value) { try { JSON.parse(value); } catch (e) { return false; } return true; } const htmlTagBaseFontSize = 16; const bypassScalerTransformationClassName = 'bypass-scaler-js-transformation'; const browserFontSizeDiffVarName = '--browser-font-size-diff'; function getPxToRemValue(value) { return (value / htmlTagBaseFontSize).toFixed(4).replace(/[.,]0+$/, ""); } var transformCss = (shouldTransformPixels, options, css) => { const selector = css.selectorText; if (!selector) return ''; const isExcludedSelector = options.excludeSelectors.some(i => selector.includes(i)); const transformations = new Map(); // @ts-ignore Array.from(css.styleMap).forEach((prop) => { const propName = prop[0]; const cssUnitValueArr = prop[1]; if (cssUnitValueArr && Array.isArray(cssUnitValueArr) && cssUnitValueArr[0] instanceof CSSUnitValue) { const cssUnitValue = cssUnitValueArr[0]; const isPixelUnit = cssUnitValue.unit === 'px'; const isExcludedAttr = options.excludeAttributes.some(i => propName.includes(i)); if (propName === 'font-size') { const fontSizeValue = isPixelUnit ? `${getPxToRemValue(cssUnitValue.value)}rem` : `${cssUnitValue.value}${cssUnitValue.unit}`; transformations.set(propName, `calc(${fontSizeValue} + var(${browserFontSizeDiffVarName}))`); } else if (shouldTransformPixels && isPixelUnit && !isExcludedSelector && !isExcludedAttr) { transformations.set(propName, `${getPxToRemValue(cssUnitValue.value)}rem`); } } }); if (transformations.size) { let transformedCss = `${selector}:not(${bypassScalerTransformationClassName}) {\n`; transformations.forEach((value, key) => { transformedCss += `${key}: ${value};\n`; }); transformedCss += '}'; return transformedCss; } return ''; }; const transformPixelsDefault = { excludeAttributes: [], excludeSelectors: [] }; var scalerScript = () => { return ` if (typeof window !== 'undefined') { const baseFontSize = ${htmlTagBaseFontSize} const segments = { width: 80, height: 45 } const preciseBreakpoints = { width: 1320, height: 720 } function getVirtualRemFontSize(width, height) { const isLandscape = width > height const widthSegment = isLandscape ? segments.width : segments.height const heightSegment = isLandscape ? segments.height : segments.width const preciseWidthBreakpoint = isLandscape ? preciseBreakpoints.width : preciseBreakpoints.height const preciseHeightBreakpoint = isLandscape ? preciseBreakpoints.height : preciseBreakpoints.width let X = width > preciseWidthBreakpoint ? widthSegment : widthSegment - Math.floor((preciseWidthBreakpoint - width) / (widthSegment / 2)) let Y = height > preciseHeightBreakpoint ? heightSegment : heightSegment - Math.floor((preciseHeightBreakpoint - height) / (heightSegment / 2)) return Math.round(((width / X) + (height / Y)) / 2) } const setBrowserFontSizeDiff = function(htmlElement) { htmlElement.style.removeProperty('font-size'); const browserFontSize = window.getComputedStyle(htmlElement).getPropertyValue('font-size'); const browserDifference = Number(browserFontSize.replace('px', '')) - baseFontSize; document.documentElement.style.setProperty('${browserFontSizeDiffVarName}', browserDifference + 'px') } const setVirtualRemFontSize = function(htmlElement) { const vRem = getVirtualRemFontSize(window.innerWidth, window.innerHeight) htmlElement.style.setProperty('font-size', vRem + 'px') } const updateHtmlFontSize = function() { const htmlElement = document.querySelector('html'); setBrowserFontSizeDiff(htmlElement) setVirtualRemFontSize(htmlElement) } const initHtmlFontSizeWatcher = function() { window.addEventListener('resize', updateHtmlFontSize) updateHtmlFontSize() } if (window.document.readyState !== 'loading') { initHtmlFontSizeWatcher(); } else { window.document.addEventListener('DOMContentLoaded', function() { initHtmlFontSizeWatcher(); }); } }`; }; function transformExistingStyles(shouldTransformPixels, options) { let transformations = ''; Array.from(document.styleSheets).forEach((styleSheet) => { try { const cssRules = styleSheet.cssRules; Array.from(cssRules).filter((i) => i instanceof CSSStyleRule).forEach((rule) => { const transformedRules = transformCss(shouldTransformPixels, options, rule); if (transformedRules) transformations += '\n' + transformedRules; }); } catch (error) { console.warn('Scaler-js: Could not access a stylesheet rule. This might or might not affect your page responsiveness', error); } }); const style = document.createElement('style'); style.setAttribute('type', 'text/css'); style.setAttribute('data-scaler-js-transformations', 'true'); style.textContent = transformations.replace(/\n/g, ''); document.head.appendChild(style); } function observeNewlyAddedStyles(shouldTransformPixels, options) { const cssObserver = new MutationObserver((mutations) => { for (const mutation of mutations) { if (mutation.type === 'childList') { mutation.addedNodes.forEach((node) => { if (node.nodeType === Node.ELEMENT_NODE && node.tagName.toLowerCase() === 'style') { const styleEl = node; if (styleEl.sheet) { let transformations = ''; Array.from(styleEl.sheet.cssRules).filter((i) => i instanceof CSSStyleRule).forEach((rule) => { const transformedRules = transformCss(shouldTransformPixels, options, rule); if (transformedRules) transformations += '\n' + transformedRules; }); if (transformations) { const css = styleEl.textContent ?? ''; styleEl.textContent = css + transformations; } } } }); } } }); cssObserver.observe(document.head, { childList: true, subtree: true, }); } function index (transformParams) { function scaleUI() { const htmlElem = document.querySelector('html'); const transformPixelsAttr = htmlElem?.getAttribute('data-scaler-js-transform-pixels'); const hasRuntimeOption = transformParams === 'runtime' && transformPixelsAttr != 'false'; const hasCustomOptions = typeof transformParams === 'object'; const shouldTransformPixels = hasRuntimeOption || hasCustomOptions || transformParams === true; let transformPixelsOptions; if (hasRuntimeOption && transformPixelsAttr && isValidJsonString(transformPixelsAttr)) { transformPixelsOptions = Object.assign(transformPixelsDefault, JSON.parse(transformPixelsAttr)); } else if (hasCustomOptions) { transformPixelsOptions = Object.assign(transformPixelsDefault, { ...transformParams }); } else { transformPixelsOptions = transformPixelsDefault; } setTimeout(() => { transformExistingStyles(shouldTransformPixels, transformPixelsOptions); observeNewlyAddedStyles(shouldTransformPixels, transformPixelsOptions); }); const script = scalerScript(); const scriptTag = document.createElement('script'); scriptTag.setAttribute('data-scaler-js-html-font-size-watcher', 'true'); scriptTag.textContent = script; document.head.appendChild(scriptTag); } if (window.document.readyState !== 'loading') { scaleUI(); } else { window.document.addEventListener('DOMContentLoaded', function () { scaleUI(); }); } } export { index as default }; //# sourceMappingURL=index.esm.mjs.map