feat: enhance Button component with new variants, sizes, and styles

feat/vapor
tangjinzhou 2025-07-29 19:53:16 +08:00
parent 136543398d
commit 2cf758d770
8 changed files with 248 additions and 23 deletions

View File

@ -1,9 +1,56 @@
<template>
<div class="flex flex-col gap-2">
<a-button class="btn btn-primary">Primary Button</a-button>
<a-button class="btn btn-secondary">Default Button</a-button>
<a-button class="btn btn-error">Dashed Button</a-button>
<a-button class="btn btn-link">Text Button</a-button>
<a-button class="btn btn-link">Link Button</a-button>
<div class="flex flex-wrap gap-2">
<a-button variant="solid" size="lg">Solid Button</a-button>
<a-button variant="solid" size="lg" disabled>Solid Button</a-button>
<a-button variant="solid" size="lg" loading>Solid Button</a-button>
<a-button variant="solid" size="lg" danger>Danger Solid Button</a-button>
<a-button variant="solid" size="lg" danger disabled>Disabled Danger Solid Button</a-button>
<br />
<a-button variant="outlined" size="sm">Outlined Button</a-button>
<a-button variant="outlined" size="md">Outlined Button</a-button>
<a-button variant="outlined" size="lg">Outlined Button</a-button>
<a-button variant="outlined" size="lg" disabled>Disabled Outlined Button</a-button>
<a-button variant="outlined" size="lg" loading>Loading Outlined Button</a-button>
<a-button variant="outlined" size="lg" danger>Danger Outlined Button</a-button>
<a-button variant="outlined" size="lg" danger disabled>
Disabled Danger Outlined Button
</a-button>
<br />
<a-button variant="text" size="sm">Text Button</a-button>
<a-button variant="text" size="md">Text Button</a-button>
<a-button variant="text" size="lg">Text Button</a-button>
<a-button variant="text" size="lg" disabled>Disabled Text Button</a-button>
<a-button variant="text" size="lg" loading>Loading Text Button</a-button>
<a-button variant="text" size="lg" danger>Danger Text Button</a-button>
<a-button variant="text" size="lg" danger disabled>Disabled Danger Text Button</a-button>
<br />
<a-button variant="link" size="sm">Link Button</a-button>
<a-button variant="link" size="md">Link Button</a-button>
<a-button variant="link" size="lg">Link Button</a-button>
<a-button variant="link" size="lg" disabled>Disabled Link Button</a-button>
<a-button variant="link" size="lg" loading>Loading Link Button</a-button>
<a-button variant="link" size="lg" danger>Danger Link Button</a-button>
<a-button variant="link" size="lg" danger disabled>Disabled Danger Link Button</a-button>
<br />
<a-button variant="dashed" size="sm">Dashed Button</a-button>
<a-button variant="dashed" size="md">Dashed Button</a-button>
<a-button variant="dashed" size="lg">Dashed Button</a-button>
<a-button variant="dashed" size="lg" disabled>Disabled Dashed Button</a-button>
<a-button variant="dashed" size="lg" loading>Loading Dashed Button</a-button>
<a-button variant="dashed" size="lg" danger>Danger Dashed Button</a-button>
<a-button variant="dashed" size="lg" danger disabled>Disabled Danger Dashed Button</a-button>
<br />
<a-button variant="filled" size="sm">Filled Button</a-button>
<a-button variant="filled" size="md">Filled Button</a-button>
<a-button variant="filled" size="lg">Filled Button</a-button>
<a-button variant="filled" size="lg" disabled>Disabled Filled Button</a-button>
<a-button variant="filled" size="lg" loading>Loading Filled Button</a-button>
<a-button variant="filled" size="lg" danger>Danger Filled Button</a-button>
<a-button variant="filled" size="lg" danger disabled>Disabled Danger Filled Button</a-button>
<a-button color="purple">Purple Button</a-button>
<a-button color="blue">Blue Button</a-button>
<a-button color="green">Green Button</a-button>
<a-button color="red">Red Button</a-button>
</div>
</template>

View File

@ -1,5 +1,4 @@
@import 'tailwindcss' source(none);
@plugin '@tailwindcss/typography';
@utility text-tint-* {
color: color-mix(in srgb, --value(--color-*, [*]), white calc(--modifier(integer) * 1%));

View File

@ -53,7 +53,7 @@
"license": "MIT",
"main": "./dist/lib.cjs",
"module": "./dist/lib.mjs",
"types": "./dist/types/index.d.ts",
"types": "./dist/index.d.ts",
"files": [
"dist",
"src/style",
@ -72,6 +72,7 @@
"@floating-ui/dom": "^1.6.13",
"@floating-ui/vue": "^1.1.6",
"lodash-es": "^4.17.21",
"@ctrl/tinycolor": "^4.0.0",
"resize-observer-polyfill": "^1.5.1"
},
"devDependencies": {

View File

@ -1,5 +1,7 @@
<template>
<button class="ant-btn" :class="classes" @click="$emit('click', $event)">
<button :class="rootClass" @click="$emit('click', $event)" :disabled="disabled" :style="cssVars">
<slot name="loading"></slot>
<slot name="icon"></slot>
<slot></slot>
</button>
</template>
@ -7,20 +9,42 @@
<script setup lang="ts">
import { computed } from 'vue'
import { buttonProps, buttonEmits, ButtonSlots } from './meta'
import { getCssVarColor } from '@/utils/colorAlgorithm'
const props = defineProps(buttonProps)
defineEmits(buttonEmits)
defineSlots<ButtonSlots>()
const classes = computed(() => {
return [
'rounded-md px-4 py-2 cursor-pointer',
props.type,
{
'bg-primary text-primary-content': props.type === 'primary',
'bg-secondary text-secondary-content': props.type === 'secondary',
},
]
// todo: color value should from theme provider
const color = computed(() => {
if (props.disabled) {
return 'rgba(0,0,0,0.25)'
}
if (props.color) {
return props.color
}
if (props.danger) {
return '#ff4d4f'
}
if (props.variant === 'text') {
return '#000000'
}
return '#1677ff'
})
const rootClass = computed(() => {
return {
'ant-btn': true,
[`ant-btn-${props.variant}`]: true,
[`ant-btn-${props.size}`]: true,
'ant-btn-danger': props.danger,
'ant-btn-loading': props.loading,
'ant-btn-disabled': props.disabled,
}
})
const cssVars = computed(() => {
return getCssVarColor(color.value)
})
</script>

View File

@ -2,7 +2,6 @@ import { App, Plugin } from 'vue'
import Button from './Button.vue'
import './style/index.css'
// 导出组件
export { default as Button } from './Button.vue'
export * from './meta'

View File

@ -6,9 +6,61 @@ export const buttonProps = {
* Specifies the visual style variant of the button
* @default 'primary'
*/
type: {
type: String as PropType<'primary' | 'secondary'>,
default: 'primary',
variant: {
type: String as PropType<'solid' | 'outlined' | 'text' | 'link' | 'dashed' | 'filled'>,
default: 'solid',
},
/**
* Specifies the size of the button
* @default 'md'
*/
size: {
type: String as PropType<'sm' | 'md' | 'lg'>,
default: 'md',
},
/**
* Specifies the shape of the button
* @default 'default'
*/
shape: {
type: String as PropType<'default' | 'circle' | 'round'>,
default: 'default',
},
/**
* Specifies the loading state of the button
* @default false
*/
loading: {
type: Boolean,
default: false,
},
/**
* Specifies the disabled state of the button
* @default false
*/
disabled: {
type: Boolean,
default: false,
},
/**
* Specifies the danger state of the button
* @default false
*/
danger: {
type: Boolean,
default: false,
},
/**
* Specifies the color of the button
*/
color: {
type: String,
},
} as const
@ -31,6 +83,14 @@ export const buttonSlots = {
* Main content slot for the button text or custom content
*/
default: (_: any) => null as any,
/**
* Slot for the button icon
*/
icon: (_: any) => null as any,
/**
* Slot for the button loading indicator
*/
loading: (_: any) => null as any,
} as const
export type ButtonSlots = typeof buttonSlots

View File

@ -1,5 +1,50 @@
@reference '../../../style/tailwind.css';
.ant-btn {
@apply text-primary-content;
@apply relative;
@apply inline-flex shrink-0 cursor-pointer items-center justify-center gap-1 whitespace-nowrap;
@apply border-1 text-sm;
@apply box-border rounded-md px-4 transition-all duration-200 select-none;
&:where(.ant-btn-disabled) {
@apply cursor-not-allowed;
}
&:where(.ant-btn-loading) {
@apply cursor-default opacity-50;
}
&:where(.ant-btn-solid) {
@apply border-none bg-[var(--bg-color)] text-[var(--bg-color-content)];
@apply not-disabled:hover:bg-[var(--bg-color-hover)] not-disabled:active:bg-[var(--bg-color-active)];
}
&:where(.ant-btn-outlined),
&:where(.ant-btn-dashed) {
@apply border-[var(--border-color-tint-30)] bg-transparent text-[var(--text-color)];
@apply not-disabled:hover:border-[var(--border-color-hover)] not-disabled:hover:text-[var(--text-color-hover)] not-disabled:active:border-[var(--border-color-active)] not-disabled:active:text-[var(--text-color-active)];
@apply disabled:border-[var(--border-color-tint-80)];
}
&:where(.ant-btn-text) {
@apply border-none bg-transparent text-[var(--text-color)];
@apply not-disabled:hover:bg-[var(--bg-color-tint-90)] not-disabled:hover:text-[var(--text-color-hover)];
}
&:where(.ant-btn-link) {
@apply border-none bg-transparent text-[var(--text-color)] not-disabled:hover:text-[var(--text-color-hover)];
}
&:where(.ant-btn-dashed) {
@apply border-dashed;
}
&:where(.ant-btn-filled) {
@apply border-none bg-[var(--bg-color-tint-90)] text-[var(--text-color)] not-disabled:hover:text-[var(--text-color-hover)];
@apply not-disabled:hover:bg-[var(--bg-color-tint-80)] not-disabled:active:bg-[var(--bg-color-tint-80)];
}
&:where(.ant-btn-sm) {
@apply h-6 text-xs;
}
&:where(.ant-btn-md) {
@apply h-8 text-sm;
}
&:where(.ant-btn-lg) {
@apply h-10 text-base;
}
}

View File

@ -0,0 +1,50 @@
import { TinyColor } from '@ctrl/tinycolor'
export const getAlphaColor = (baseColor: string, alpha: number) =>
new TinyColor(baseColor).setAlpha(alpha).toRgbString()
export const getSolidColor = (baseColor: string, brightness: number) => {
const instance = new TinyColor(baseColor)
return instance.darken(brightness).toHexString()
}
export const getTintColor = (baseColor: string, tintNumber: number) => {
return new TinyColor(baseColor).tint(tintNumber).toString()
}
export const getShadeColor = (baseColor: string, shadeNumber: number) => {
return new TinyColor(baseColor).shade(shadeNumber).toString()
}
export const getCssVarColor = (baseColor: string) => {
return {
'--bg-color': baseColor,
'--bg-color-hover': getTintColor(baseColor, 10),
'--bg-color-active': getTintColor(baseColor, 20),
'--bg-color-content': '#ffffff',
'--border-color': baseColor,
'--border-color-hover': getTintColor(baseColor, 10),
'--border-color-active': getTintColor(baseColor, 20),
'--border-color-tint-10': getTintColor(baseColor, 10),
'--border-color-tint-20': getTintColor(baseColor, 20),
'--border-color-tint-30': getTintColor(baseColor, 30),
'--border-color-tint-40': getTintColor(baseColor, 40),
'--border-color-tint-50': getTintColor(baseColor, 50),
'--border-color-tint-60': getTintColor(baseColor, 60),
'--border-color-tint-70': getTintColor(baseColor, 70),
'--border-color-tint-80': getTintColor(baseColor, 80),
'--border-color-tint-90': getTintColor(baseColor, 90),
'--bg-color-tint-10': getTintColor(baseColor, 10),
'--bg-color-tint-20': getTintColor(baseColor, 20),
'--bg-color-tint-30': getTintColor(baseColor, 30),
'--bg-color-tint-40': getTintColor(baseColor, 40),
'--bg-color-tint-50': getTintColor(baseColor, 50),
'--bg-color-tint-60': getTintColor(baseColor, 60),
'--bg-color-tint-70': getTintColor(baseColor, 70),
'--bg-color-tint-80': getTintColor(baseColor, 80),
'--bg-color-tint-90': getTintColor(baseColor, 90),
'--text-color': baseColor,
'--text-color-hover': getTintColor(baseColor, 10),
'--text-color-active': getTintColor(baseColor, 20),
}
}