UNPKG

@vue3-oop/tailwind-preset

Version:
279 lines (268 loc) 7.72 kB
// 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 };