diff --git a/packages/css/src/index.ts b/packages/css/src/index.ts index 1bdf840..606b735 100644 --- a/packages/css/src/index.ts +++ b/packages/css/src/index.ts @@ -1,4 +1,4 @@ -import './index.css' -export * from './utils/cpm.ts'; -export * from './utils/cvr.ts'; -export * from './utils/prefix-list.ts'; \ No newline at end of file +import "./index.css"; +export * from "./utils/cpm.ts"; +export * from "./utils/cvr.ts"; +export * from "./utils/prefix-list.ts"; diff --git a/packages/css/src/utils/cpm.ts b/packages/css/src/utils/cpm.ts index d4e86e7..c4f05ab 100644 --- a/packages/css/src/utils/cpm.ts +++ b/packages/css/src/utils/cpm.ts @@ -1,42 +1,62 @@ import { defaultPrefixList } from "./prefix-list"; -function prefixParce(cls: string): { prefix: string; rest: string } { - const index = cls.indexOf("-"); - if (index === -1) return { prefix: "", rest: cls }; - const prefix = cls.slice(0, index); - const rest = cls.slice(index + 1); - return { prefix, rest }; +function matchLongestPrefix(cls: string, prefixList: string[]): string { + let bestPrefix: string | undefined; + + for (const prefix of prefixList) { + const reg = new RegExp(`^${prefix}(?:-|$)`); + if (reg.test(cls) && (!bestPrefix || prefix.length > bestPrefix.length)) { + bestPrefix = prefix; + } + } + + return bestPrefix ?? cls; } +// 重载 1 +export function cpm(...classes: Array): string; + +// 重载 2 export function cpm( - options?: { externalPrefixList?: string[] }, - ...classes: (string | string[])[] + options: { extendedPrefixList?: string[] }, + ...classes: Array +): string; + +export function cpm( + arg1: { extendedPrefixList?: string[] } | string | readonly string[], + ...rest: Array ): string { const map = new Map(); + + // ✅ 明确拆分 options / classes + let options: { extendedPrefixList?: string[] } = {}; + let classes: Array; + + if (typeof arg1 === "object" && !Array.isArray(arg1)) { + options = arg1 as { extendedPrefixList?: string[] }; + classes = rest; + } else { + classes = [arg1, ...rest]; + } + const mergedPrefixList: string[] = [ - ...new Set([...(options?.externalPrefixList ?? []), ...defaultPrefixList]), + ...new Set([...(options.extendedPrefixList ?? []), ...defaultPrefixList]), ]; classes.forEach((item) => { if (Array.isArray(item)) { item.forEach((i) => { - const { prefix, rest } = prefixParce(i); - if (mergedPrefixList.includes(prefix)) { - map.set(prefix, rest); - } + const bestPrefix = matchLongestPrefix(i, mergedPrefixList); + map.set(bestPrefix, i); }); return; } if (typeof item === "string") { - const { prefix, rest } = prefixParce(item); - if (mergedPrefixList.includes(prefix)) { - map.set(prefix, rest); - } + const bestPrefix = matchLongestPrefix(item, mergedPrefixList); + map.set(bestPrefix, item); } }); - return Array.from(map.entries()) - .map(([prefix, rest]) => `${prefix}-${rest}`) - .join(" "); + return Object.values(map).join(" "); } diff --git a/packages/css/src/utils/cvr.ts b/packages/css/src/utils/cvr.ts index 3cea8b9..a325cc8 100644 --- a/packages/css/src/utils/cvr.ts +++ b/packages/css/src/utils/cvr.ts @@ -1 +1,46 @@ -export function cvr() {} +import { cpm } from "./cpm"; + +type VariantsConfig>> = { + base?: string; + variants?: V; + compoundVariants?: Array< + Partial<{ [K in keyof V]: keyof V[K] }> & { class: string } + >; +}; + +type VariantProps>> = Partial<{ + [K in keyof V]: keyof V[K]; +}>; + +export function cvr>>( + config: VariantsConfig, +) { + return function (props: VariantProps = {}): string { + const classes: string[] = []; + + if (config.base) classes.push(config.base); + + if (config.variants) { + for (const [key, map] of Object.entries(config.variants) as any) { + const value = props[key]; + if (value && map[value]) { + classes.push(map[value]); + } + } + } + + if (config.compoundVariants) { + for (const cv of config.compoundVariants) { + const match = Object.entries(cv) + .filter(([k]) => k !== "class") + .every(([k, v]) => props[k] === v); + + if (match && cv.class) { + classes.push(cv.class); + } + } + } + + return cpm(...classes); + }; +} diff --git a/packages/css/src/utils/prefix-list.ts b/packages/css/src/utils/prefix-list.ts index 8efb393..64ecfc5 100644 --- a/packages/css/src/utils/prefix-list.ts +++ b/packages/css/src/utils/prefix-list.ts @@ -36,9 +36,3 @@ export const defaultPrefixList = [ "drop-shadow", "select", ]; - -// map 里面 key 是 prefix,value 是完整 class。 -// prefix 用正则来获取,先检测是否包含前缀,再检测这个前缀是否在开头,再检测前缀后面是否跟着“-”(就怕匹配到首字母) -// 用正则套壳,花括号注入前缀。正则要求1,在开头,在尾部跟着“-”或者“没有其他字符” -// const reg = new RegExp(`^${prefix}(?:-|$)`); -// class如果一次匹配2个prefix,那么就采用较长的那个prefix,因为这种情况肯定是“较短子字符串”的副作用