feat: Add classnames minifier transformer and update component styles

This commit is contained in:
2026-01-06 18:42:14 +07:00
parent 5fc4bef5be
commit 800c8fd033
7 changed files with 134 additions and 24 deletions

View File

@@ -0,0 +1,108 @@
// import type { SourceCodeTransformer } from '@unocss/core'
// import { escapeRegExp, expandVariantGroup } from '@unocss/core'
import { SourceCodeTransformer, escapeRegExp, expandVariantGroup } from 'unocss'
export const defaultChars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
export function charCombinations(chars: string = defaultChars) {
const combination = [-1]
const charsLastIdx = chars.length - 1
const resetFromIndex = (idx: number) => {
for (let i = idx; i < combination.length; i++)
combination[i] = 0
}
return () => {
for (let i = combination.length - 1; i >= 0; i--) {
if (combination[i] !== charsLastIdx) {
combination[i] += 1
resetFromIndex(i + 1)
break
}
if (i === 0) {
resetFromIndex(0)
combination.push(0)
break
}
}
return "_"+combination.map(i => chars[i]).join('')
}
}
export interface CompileClassOptions {
/**
* Special prefix to avoid UnoCSS transforming your code.
* @default ':uno:'
*/
trigger?: string
/**
* Hash function
*/
hashFn?: () => string
/**
* The layer name of generated rules
*/
layer?: string
}
export default function transformerClassnamesMinifier(options: CompileClassOptions = {}): SourceCodeTransformer {
const {
trigger = ':uno:',
hashFn = charCombinations(),
} = options
const compiledClass = new Map()
const regexp = RegExp(`(["'\`])${escapeRegExp(trigger)}${trigger ? '\\s' : ''}(.*?)\\1`, 'g')
return {
name: 'name',
enforce: 'pre',
async transform(s, _id, { uno }) {
if(s.original.includes('p-button') || s.original.includes('p-component') || s.original.includes('p-button-secondary')) {
console.log("transforming:", _id);
}
const matches = [...s.original.matchAll(regexp)]
if (!matches.length)
return
// console.log("s.original", s.original)
for (const match of matches) {
const body = match.length ? expandVariantGroup(match[2].trim()) : ''
const start = match.index!
const replacements = []
const result = await Promise.all(body.split(/\s+/).filter(Boolean).map(async i => [i, !!await uno.parseToken(i)] as const))
const known = result.filter(([, matched]) => matched).map(([i]) => i)
const unknown = result.filter(([, matched]) => !matched).map(([i]) => i)
replacements.push(...unknown)
known.forEach((i) => {
const compiled = compiledClass.get(i)
if (compiled)
return replacements.push(compiled)
const className = hashFn()
compiledClass.set(i, className)
if (options.layer)
uno.config.shortcuts.push([className, i, { layer: options.layer }])
else
uno.config.shortcuts.push([className, i])
replacements.push(className)
})
s.overwrite(start + 1, start + match[0].length - 1, replacements.join(' '))
}
},
}
}

View File

@@ -29,15 +29,15 @@ const links = [
:class="cn(className, $route.path === i.href && 'bg-primary/15')">
<component :is="i.icon" :filled="$route.path === i.href" />
</component>
<div class="w-12 h-12 rounded-2xl hover:bg-primary/15 flex">
<button class="h-[38px] w-[38px] rounded-full m-a ring-2 ring flex press-animated" @click="auth.logout()">
<img class="h-8 w-8 rounded-full m-a ring-1 ring-white"
<div class=":m: w-12 h-12 rounded-2xl hover:bg-primary/15 flex">
<button class=":m: h-[38px] w-[38px] rounded-full m-a ring-2 ring flex press-animated" @click="auth.logout()">
<img class=":m: h-8 w-8 rounded-full m-a ring-1 ring-white"
src="https://picsum.photos/seed/user123/40/40.jpg" alt="User avatar" />
</button>
</div>
</header>
<main class="flex flex-1 overflow-hidden md:ps-18">
<div class="flex-1 overflow-auto p-4 bg-white rounded-lg md:(mr-2 mb-2) min-h-[calc(100vh-8rem)]">
<div class=":m: flex-1 overflow-auto p-4 bg-white rounded-lg md:(mr-2 mb-2) min-h-[calc(100vh-8rem)]">
<router-view v-slot="{ Component }">
<Transition enter-active-class="transition-all duration-300 ease-in-out"
enter-from-class="opacity-0 transform translate-y-4"

View File

@@ -1,6 +1,6 @@
<template>
<vue-head :input="{title: '404 - Page Not Found'}"/>
<div class="mx-auto text-center mt-20 flex flex-col items-center gap-4">
<div class=":m: mx-auto text-center mt-20 flex flex-col items-center gap-4">
<h1>404 - Page Not Found</h1>
<p>The page you are looking for does not exist.</p>
<router-link class="btn btn-primary" to="/">Go back to Home</router-link>

View File

@@ -1,7 +1,7 @@
<template>
<div class="w-full max-w-md bg-white p-8 rounded-xl border border-gray-200 m-auto overflow-hidden">
<div class=":m: w-full max-w-md bg-white p-8 rounded-xl border border-gray-200 m-auto overflow-hidden">
<div class="text-center mb-8">
<router-link to="/" class="inline-flex items-center justify-center w-12 h-12 mb-4">
<router-link to="/" class=":m: inline-flex items-center justify-center w-12 h-12 mb-4">
<img class="w-12 h-12" src="/apple-touch-icon.png" alt="Logo" />
</router-link>
<h2 class="text-2xl font-bold text-gray-900">

View File

@@ -19,10 +19,10 @@
$form.password.error?.message }}</Message>
</div>
<div class="flex items-center justify-between">
<div class="flex items-center gap-2">
<div class=":m: flex items-center justify-between">
<div class=":m: flex items-center gap-2">
<Checkbox inputId="remember-me" name="rememberMe" binary :disabled="auth.loading" />
<label for="remember-me" class="text-sm text-gray-900">Remember me</label>
<label for="remember-me" class=":m: text-sm text-gray-900">Remember me</label>
</div>
<div class="text-sm">
<router-link to="/forgot"

View File

@@ -1,11 +1,11 @@
<template>
<section class="relative pt-32 pb-20 lg:pt-48 lg:pb-32 overflow-hidden min-h-svh flex">
<section class=":m: relative pt-32 pb-20 lg:pt-48 lg:pb-32 overflow-hidden min-h-svh flex">
<!-- <div class="absolute inset-0 bg-grid-pattern opacity-[0.4] -z-10"></div> -->
<div
class="absolute top-0 right-0 -translate-y-1/2 translate-x-1/2 w-[800px] h-[800px] bg-primary-light/40 rounded-full blur-3xl -z-10 mix-blend-multiply animate-pulse duration-1000">
class=":m: absolute top-0 right-0 -translate-y-1/2 translate-x-1/2 w-[800px] h-[800px] bg-primary-light/40 rounded-full blur-3xl -z-10 mix-blend-multiply animate-pulse duration-1000">
</div>
<div
class="absolute bottom-0 left-0 translate-y-1/2 -translate-x-1/2 w-[600px] h-[600px] bg-teal-100/50 rounded-full blur-3xl -z-10 mix-blend-multiply">
class=":m: absolute bottom-0 left-0 translate-y-1/2 -translate-x-1/2 w-[600px] h-[600px] bg-teal-100/50 rounded-full blur-3xl -z-10 mix-blend-multiply">
</div>
@@ -52,7 +52,7 @@
<div class="grid grid-cols-1 md:grid-cols-3 gap-6">
<div
class="md:col-span-2 bg-slate-50 rounded-2xl p-8 border border-slate-100 hover:border-primary/60 transition-all group overflow-hidden relative">
class=":m: md:col-span-2 bg-slate-50 rounded-2xl p-8 border border-slate-100 hover:border-primary/60 transition-all group overflow-hidden relative">
<div class="relative z-10">
<div
class="w-12 h-12 bg-white rounded-xl flex items-center justify-center mb-6 border border-slate-100">
@@ -75,11 +75,11 @@
</div>
</div>
<div class="md:row-span-2 bg-slate-900 rounded-2xl p-8 text-white relative overflow-hidden group">
<div class="absolute inset-0 bg-gradient-to-b from-slate-800/50 to-transparent"></div>
<div class=":m: md:row-span-2 bg-slate-900 rounded-2xl p-8 text-white relative overflow-hidden group">
<div class=":m: absolute inset-0 bg-gradient-to-b from-slate-800/50 to-transparent"></div>
<div class="relative z-10">
<div
class="w-12 h-12 bg-white/10 rounded-xl flex items-center justify-center mb-6 backdrop-blur-sm border border-white/10">
class=":m: w-12 h-12 bg-white/10 rounded-xl flex items-center justify-center mb-6 backdrop-blur-sm border border-white/10">
<svg xmlns="http://www.w3.org/2000/svg" width="24" viewBox="-10 -146 468 384">
<path
d="M392-136c-31 0-56 25-56 56v280c0 16 13 28 28 28h28c31 0 56-25 56-56V-80c0-31-25-56-56-56zM168 4c0-31 25-56 56-56h28c16 0 28 13 28 28v224c0 16-12 28-28 28h-56c-15 0-28-12-28-28V4zM0 88c0-31 25-56 56-56h28c16 0 28 13 28 28v140c0 16-12 28-28 28H56c-31 0-56-25-56-56V88z"
@@ -96,7 +96,7 @@
<div class="flex justify-between items-center mb-3 border-b border-white/5 pb-2">
<span class="text-slate-500">Live Status</span>
<span
class="flex items-center gap-1.5 text-red-500 text-[10px] uppercase font-bold tracking-wider animate-pulse"><span
class=":m: flex items-center gap-1.5 text-red-500 text-[10px] uppercase font-bold tracking-wider animate-pulse"><span
class="w-1.5 h-1.5 rounded-full bg-red-500 animate-pulse"></span> On Air</span>
</div>
<div class="space-y-1">
@@ -113,9 +113,9 @@
<!-- Standard Feature -->
<div
class="bg-slate-50 rounded-2xl p-8 border border-slate-100 hover:border-brand-200 transition-all group hover:shadow-lg hover:shadow-brand-500/5">
class=":m: bg-slate-50 rounded-2xl p-8 border border-slate-100 transition-all group hover:(border-brand-200 shadow-lg shadow-brand-500/5)">
<div
class="w-12 h-12 bg-white rounded-xl shadow-sm flex items-center justify-center mb-6 text-purple-600 border border-slate-100">
class=":m: w-12 h-12 bg-white rounded-xl shadow-sm flex items-center justify-center mb-6 text-purple-600 border border-slate-100">
<svg xmlns="http://www.w3.org/2000/svg" width="24" viewBox="0 0 570 570">
<path
d="M50 428c-5 5-5 14 0 19s14 5 19 0l237-237c5-5 5-14 0-19s-14-5-19 0L50 428zm16-224c-5 5-5 13 0 19 5 5 14 5 19 0l12-12c5-5 5-14 0-19-6-5-14-5-20 0l-11 12zM174 60c-5 5-5 13 0 19 5 5 14 5 19 0l12-12c5-5 5-14 0-19-6-5-14-5-20 0l-11 12zm215 29c-5 5-5 14 0 19s14 5 19 0l39-39c5-5 5-14 0-19s-14-5-19 0l-39 39zm21 357c-5 5-5 14 0 19s14 5 19 0l18-18c5-5 5-14 0-19s-14-5-19 0l-18 18z"
@@ -132,9 +132,9 @@
<!-- Standard Feature -->
<div
class="bg-slate-50 rounded-2xl p-8 border border-slate-100 hover:border-brand-200 transition-all group hover:shadow-lg hover:shadow-brand-500/5">
class=":m: bg-slate-50 rounded-2xl p-8 border border-slate-100 transition-all group hover:(border-brand-200 shadow-lg shadow-brand-500/5)">
<div
class="w-12 h-12 bg-white rounded-xl shadow-sm flex items-center justify-center mb-6 text-orange-600 border border-slate-100">
class=":m: w-12 h-12 bg-white rounded-xl shadow-sm flex items-center justify-center mb-6 text-orange-600 border border-slate-100">
<svg xmlns="http://www.w3.org/2000/svg" width="24" viewBox="-10 -226 532 468">
<path
d="M32-216c18 0 32 14 32 32v336c0 9 7 16 16 16h400c18 0 32 14 32 32s-14 32-32 32H80c-44 0-80-36-80-80v-336c0-18 14-32 32-32zM144-24c18 0 32 14 32 32v64c0 18-14 32-32 32s-32-14-32-32V8c0-18 14-32 32-32zm144-64V72c0 18-14 32-32 32s-32-14-32-32V-88c0-18 14-32 32-32s32 14 32 32zm80 32c18 0 32 14 32 32v96c0 18-14 32-32 32s-32-14-32-32v-96c0-18 14-32 32-32zm144-96V72c0 18-14 32-32 32s-32-14-32-32v-224c0-18 14-32 32-32s32 14 32 32z"
@@ -161,7 +161,7 @@
:class="cn(':uno: p-8 rounded-2xl relative overflow-hidden hover:border-primary transition-colors flex flex-col justify-between', pack.tag == 'POPULAR' ? 'border-primary/80 border-2' : 'border-slate-200 border')"
:style="{ background: pack.bg }">
<div v-if="pack.tag"
class="absolute top-0 right-0 bg-primary/80 text-white text-xs font-bold px-3 py-1 rounded-bl-lg uppercase">
class=":m: absolute top-0 right-0 bg-primary/80 text-white text-xs font-bold px-3 py-1 rounded-bl-lg uppercase">
{{ pack.tag }}</div>
<div>
<h3 class="font-semibold text-slate-900 text-xl mb-2">{{ pack.name }}</h3>

View File

@@ -1,6 +1,6 @@
import { defineConfig, presetAttributify, presetTypography, presetWind4, transformerCompileClass, transformerVariantGroup } from 'unocss'
import { presetBootstrapBtn } from "./bootstrap_btn";
import transformerClassnamesMinifier from './plugins/encodeClassTransformer'
export default defineConfig({
presets: [
presetWind4() as any,
@@ -106,6 +106,8 @@ export default defineConfig({
],
transformers: [transformerVariantGroup(), transformerCompileClass({
classPrefix: "_",
}),transformerClassnamesMinifier({
trigger: ':m:',
})],
preflights: [
{