mr-class-style-generator
Version:
A utility to apply inline styles from custom class names like mr-fs-[10vw]
194 lines (173 loc) • 8.13 kB
JavaScript
function ApplyMrStyles({ startWith = "mr", breakpoint = {
"xs": "425px",
"sm": "575px",
"md": "768px",
"lg": "992px",
"xl": "1200px",
"2xl": "1400px",
"3xl": "1600px",
"4xl": "1800px",
} }) {
const matchedClassNames = [];
function traverse(element) {
for (const child of element.children) {
try {
const classAttr = child.getAttribute("class")?.trim();
if (classAttr && classAttr.startsWith(startWith) && classAttr.length > 3) {
matchedClassNames.push(classAttr);
}
} catch (err) {
console.warn("Error reading class attribute:", err);
}
traverse(child);
}
}
function generateDynamicStyles(classNames) {
const cssObj = { defaultCssText: "", maxDeviceCssText: "", minDeviceCssText: "" };
const cssMapObj = new Map();
let cssText = "";
const tempArrData = [];
classNames
.map((className) => {
try {
const escapedClass = className.replace(/\[/g, "\\[").replace(/\]/g, "\\]");
const multiClassArr = className.split(" ");
const classList = multiClassArr.map((item, index) => {
return item.split("-").slice(1, item.split("-").length);
}
);
console.log('escapedClass', escapedClass)
console.log('multiClassArr', multiClassArr)
console.log('classList', classList);
const [prefix, key1, key2] = className.split("-");
const value = key2.replace(/\[|\]/g, "");
const newObj = new Map();
const temArr = [];
classList.forEach((element, index) => {
console.log('element', element, index)
let temObj = {
withType: "",
fontSize: "",
fontSizeNumber: 0,
breakPoint: "",
className: multiClassArr[index],
styleType: "",
breakPointNumber: 0
};
element.forEach(sub => {
console.log('sub', sub)
if (sub == "max") {
temObj.withType = "max";
temObj.styleType = "max-devices";
}
if (sub == "min") {
temObj.withType = "min";
temObj.styleType = "min-devices";
}
if (sub?.includes("[")) {
const value = sub.replace(/\[|\]/g, "");
console.log('fontSize', temObj);
temObj.fontSize = value;
temObj.fontSizeNumber = Number(value.replaceAll("px", ""));
}
if (breakpoint[sub]) {
temObj.breakPoint = breakpoint[sub];
temObj.breakPointNumber = Number((breakpoint[sub]).replaceAll("px", ""));
}
if (temObj.styleType == "") {
temObj.styleType = "default";
}
})
console.log('temObj', Object.values(temObj));
temArr.push(temObj);
temObj = {};
});
tempArrData.push(temArr);
return cssText;
} catch (err) {
console.warn(`Error parsing class "${className}":`, err);
return '';
}
})
.filter(Boolean)
.join("\n\n");
const dataMap = processStyleData(tempArrData);
dataMap.forEach((item, index) => (
cssText += item
))
console.log('dataMap', { cssText })
return { cssText };
}
function processStyleData(data) {
const dataMap = new Map();
if (data) {
const arrayData = data.flat();
const defaultStyleList = [...arrayData].filter((item, index) => (item.withType == ""));
const minStyleList = [...arrayData].filter((item, index) => (item.withType == "min"));
const maxStyleList = [...arrayData].filter((item, index) => (item.withType == "max"));
const sortDefaultStyleList = [...defaultStyleList].sort((a, b) => {
return a.fontSizeNumber - b.fontSizeNumber;
});
const sortMinStyleList = [...minStyleList].sort((a, b) => {
return a.breakPointNumber - b.breakPointNumber;
});
const sortMaxStyleList = [...maxStyleList].sort((a, b) => {
return b.breakPointNumber - a.breakPointNumber;
});
const mergeStyleList = [
...sortDefaultStyleList,
...sortMinStyleList,
...sortMaxStyleList,
]
console.log('defaultStyleList', { mergeStyleList });
mergeStyleList.forEach((item, index) => {
console.log('temArr-item', item);
if (item.styleType == "default") {
const currentClassName = item.className.replaceAll("[", "\\[").replaceAll("]", "\\]").replaceAll(startWith, `.${startWith}`);
console.log('currentClassName', currentClassName);
const cssStyle = `\n\n${currentClassName} {\n font-size: ${item.fontSize} !important;\n}`
dataMap.set(currentClassName, cssStyle);
}
if (item.styleType === "max-devices") {
if (item.withType) {
const currentClassName = item.className.replaceAll("[", "\\[").replaceAll("]", "\\]").replaceAll(startWith, `.${startWith}`);
const cssStyle = `\n@media screen and (${item.withType}-width:${item.breakPoint}){\n${currentClassName}{\n font-size: ${item.fontSize} !important;\n}}\n`
dataMap.set(`${item.withType}-width:${item.breakPoint}`, cssStyle);
}
}
if (item.styleType === "min-devices") {
if (item.withType) {
const currentClassName = item.className.replaceAll("[", "\\[").replaceAll("]", "\\]").replaceAll(startWith, `.${startWith}`);
const cssStyle = `\n@media screen and (${item.withType}-width:${item.breakPoint}){\n${currentClassName}{\n font-size: ${item.fontSize} !important;\n}}\n`
dataMap.set(`${item.withType}-width:${item.breakPoint}`, cssStyle);
}
}
})
console.log('dataMap', dataMap)
return dataMap;
}
}
function injectStyles(cssRules) {
try {
const style = document.createElement("style");
style.textContent = cssRules;
console.log('cssRules', cssRules)
style.setAttribute(`data-mr-style-sheets`, "");
document.head.appendChild(style);
} catch (err) {
console.error("Failed to inject styles:", err);
}
}
try {
traverse(document.body);
if (matchedClassNames.length > 0) {
const { cssText } = generateDynamicStyles(matchedClassNames);
console.log('generateDynamicStyles', cssText);
console.log('matchedClassNames', matchedClassNames);
injectStyles(cssText);
}
} catch (err) {
console.error(`Error applying ${startWith} styles:`, err);
}
}
export default ApplyMrStyles;