@vue3-oop/tailwind-preset
Version:
vue3-oop tailwindcss预设插件
279 lines (268 loc) • 7.72 kB
JavaScript
// src/index.ts
import "tailwindcss";
import { createPreset } from "tailwindcss-rem2px-preset";
import safeAreaPlugin from "tailwindcss-safe-area";
// src/color/index.ts
import plugin from "tailwindcss/plugin";
// src/utils.ts
function isArray(value) {
return Array.isArray(value);
}
function isObject(value) {
const type = typeof value;
return value != null && (type === "object" || type === "function") && !isArray(value);
}
function toKebabCase(value) {
return value.replace(/([a-z])([A-Z])/g, "$1-$2").replace(/\s+/g, "-").toLowerCase();
}
function flatten(target, separator, maxDepth = Infinity) {
if (!isObject(target) && !Array.isArray(target) || !maxDepth) {
return target;
}
return Object.entries(target).reduce((result, [key, value]) => {
if (isObject(value)) {
Object.entries(flatten(value, separator, maxDepth - 1)).forEach(([childKey, childValue]) => {
result[`${key}${separator}${childKey}`] = childValue;
});
} else {
result[key] = value;
}
return result;
}, {});
}
function removeDefaultSuffix(token) {
return token.replace("-DEFAULT", "");
}
function createVarsFn(cssVarPrefix) {
return (token) => {
return `var(--${cssVarPrefix}${toKebabCase(removeDefaultSuffix(token.replace(/\./g, "-")))})`;
};
}
function isHexColor(hex) {
const HEX_REGEXP = /^#?([0-9A-F]{3}){1,2}$/i;
return HEX_REGEXP.test(hex);
}
function rgbColorChannel(hexColor) {
let hexString = hexColor.replace("#", "");
if (hexString.length === 3) {
const shorthandHex = hexString.split("");
hexString = [
shorthandHex[0],
shorthandHex[0],
shorthandHex[1],
shorthandHex[1],
shorthandHex[2],
shorthandHex[2]
].join("");
}
const parsed = parseInt(hexString, 16);
const r = parsed >> 16 & 255;
const g = parsed >> 8 & 255;
const b = parsed & 255;
return `${r},${g},${b}`;
}
// src/color/index.ts
function getCssVarPrefix(options) {
if (!(options == null ? void 0 : options.cssVarPrefix)) return "";
return options.cssVarPrefix + "-";
}
var colorPlugin = (options) => {
if (!(options == null ? void 0 : options.colors) || !Reflect.ownKeys((options == null ? void 0 : options.colors) || {}).length) return plugin(() => {
});
const cssVarPrefix = getCssVarPrefix(options);
const vars = createVarsFn(cssVarPrefix);
const finalColors = flatten(options.colors, "-");
const rootCssVars = {};
const colors = Object.keys(finalColors).reduce((acc, key) => {
const formattedKey = toKebabCase(removeDefaultSuffix(key));
acc[cssVarPrefix + formattedKey] = `rgba(${vars(key)},<alpha-value>)`;
rootCssVars["--" + cssVarPrefix + formattedKey] = isHexColor(finalColors[key]) ? rgbColorChannel(finalColors[key]) : finalColors[key];
return acc;
}, {});
return plugin(
({ addBase }) => {
addBase({
body: rootCssVars
});
},
{
theme: {
extend: {
colors
}
}
}
);
};
// src/font/index.ts
import plugin2 from "tailwindcss/plugin";
// package.json
var name = "@vue3-oop/tailwind-preset";
// src/font/index.ts
var numberFont = {
"d-din-pro": [
{
path: `${name}/font/D-DIN-PRO/D-DIN-PRO-400-Regular.otf`,
weight: 400
},
{
path: `${name}/font/D-DIN-PRO/D-DIN-PRO-500-Medium.otf`,
weight: 500
},
{
path: `${name}/font/D-DIN-PRO/D-DIN-PRO-600-SemiBold.otf`,
weight: 600
},
{
path: `${name}/font/D-DIN-PRO/D-DIN-PRO-700-Bold.otf`,
weight: 700
},
{
path: `${name}/font/D-DIN-PRO/D-DIN-PRO-800-ExtraBold.otf`,
weight: 800
},
{
path: `${name}/font/D-DIN-PRO/D-DIN-PRO-900-Heavy.otf`,
weight: 900
}
]
};
function fontPlugin(options) {
if (!(options == null ? void 0 : options.useNumberFont) && !options.webFonts) return plugin2(() => {
});
const fontFamily = {};
if (options.useNumberFont) Object.assign(fontFamily, numberFont);
if (options.webFonts) Object.assign(fontFamily, options.webFonts);
const base = Object.entries(fontFamily).map(([key, val]) => {
return val.map((k) => {
const { path: path2, ...extra } = k;
const fontface = {
"font-family": key,
src: `url('${k.path}') format('opentype')`
};
Object.entries(extra).forEach((c) => fontface[`font-${c[0]}`] = c[1]);
return {
"@font-face": fontface
};
});
}).flat();
const family = Object.fromEntries(Object.keys(fontFamily).map((k) => [k, [k]]));
return plugin2(
({ addBase }) => {
addBase(base);
},
{
theme: {
extend: {
fontFamily: family
}
}
}
);
}
// src/icon/index.ts
import plugin3 from "tailwindcss/plugin";
// src/icon/icons.ts
import * as fs from "node:fs";
import * as path from "node:path";
import { optimize } from "svgo";
function readDirectoryRecursively(directory, rootDir) {
const result = [];
try {
const files = fs.readdirSync(directory);
files.forEach((file) => {
const filePath = path.join(directory, file);
const stat = fs.statSync(filePath);
if (stat.isDirectory()) {
result.push(...readDirectoryRecursively(filePath, rootDir));
} else {
try {
const source = fs.readFileSync(path.resolve(filePath), "utf-8");
const base64 = optimize(source, {
datauri: "base64",
multipass: true
}).data;
result.push({
base64,
name: filePath.replaceAll(rootDir + path.sep, "").replaceAll(".svg", "").replaceAll(path.sep, "/"),
isMask: source.includes("currentColor")
});
} catch (err) {
console.error(err);
console.warn("filepath:", filePath);
}
}
});
return result;
} catch (e) {
return result;
}
}
function getIcons(iconPath) {
const dir = path.resolve(iconPath);
const files = readDirectoryRecursively(dir, dir);
return Object.fromEntries(files.map((k) => [k.name, k]));
}
// src/icon/index.ts
function svgIconPlugin(options = {}) {
if ((options == null ? void 0 : options.useSvgIcon) === false) return plugin3(() => {
});
const { iconClassPrefix = "icon", iconPath = "src/icons" } = options;
const icons = getIcons(iconPath);
const defaultProps = {
display: "inline-block",
width: `1em`,
height: `1em`
};
return plugin3(({ matchComponents }) => {
matchComponents(
{
[iconClassPrefix]: (value) => {
if (typeof value !== "object") {
return {};
}
const isMask = value.isMask;
const props = {
"--icon": `url("${value.base64}")`,
"flex-shrink": 0,
...defaultProps,
...options.extraIconProps
};
if (isMask) {
return {
...props,
mask: "var(--icon) no-repeat",
"mask-size": "100% 100%",
"-webkit-mask": "var(--icon) no-repeat",
"-webkit-mask-size": "100% 100%",
"background-color": "currentColor"
};
}
return {
...props,
background: "var(--icon) no-repeat",
"background-size": "100% 100%",
"background-color": "transparent"
};
}
},
{
values: icons
}
);
});
}
// src/index.ts
function preset(options = {}) {
return {
corePlugins: {
// button的重置色会导致按钮失色
preflight: false
},
plugins: [colorPlugin(options), fontPlugin(options), svgIconPlugin(options), safeAreaPlugin],
presets: [createPreset()]
};
}
export {
preset
};