mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-02-20 14:54:12 +00:00
Component: Button migration 1: TextButton (#7537)
## Summary Setup the variants and migrate existing uses of TextButton/TextIconButton/IconButton to a single Button component. Still a work in progress. ## Changes - **What**: Add a new Button - **What**: Migrate old buttons - **What**: Delete old buttons - **Dependencies**: CVA, upgrade Storybook ## Review Focus <!-- Critical design decisions or edge cases that need attention --> <!-- If this PR fixes an issue, uncomment and update the line below --> <!-- Fixes #ISSUE_NUMBER --> ## Screenshots (if applicable) <!-- Add screenshots or video recording to help explain your changes --> ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-7537-WIP-Component-Button-migration-2cb6d73d36508156a81bfc7bbddb36e9) by [Unito](https://www.unito.io) --------- Co-authored-by: GitHub Action <action@github.com>
This commit is contained in:
68
src/components/ui/button/Button.stories.ts
Normal file
68
src/components/ui/button/Button.stories.ts
Normal file
@@ -0,0 +1,68 @@
|
||||
import type { Meta, StoryObj } from '@storybook/vue3-vite'
|
||||
|
||||
import Button from './Button.vue'
|
||||
import { FOR_STORIES } from '@/components/ui/button/button.variants'
|
||||
|
||||
const { variants, sizes } = FOR_STORIES
|
||||
const meta: Meta<typeof Button> = {
|
||||
title: 'Components/Button/Button',
|
||||
component: Button,
|
||||
tags: ['autodocs'],
|
||||
argTypes: {
|
||||
size: {
|
||||
control: { type: 'select' },
|
||||
options: sizes,
|
||||
defaultValue: 'md'
|
||||
},
|
||||
variant: {
|
||||
control: { type: 'select' },
|
||||
options: variants,
|
||||
defaultValue: 'primary'
|
||||
},
|
||||
as: { defaultValue: 'button' },
|
||||
asChild: { defaultValue: false },
|
||||
default: {
|
||||
defaultValue: 'Button'
|
||||
}
|
||||
},
|
||||
args: {
|
||||
variant: 'secondary',
|
||||
size: 'md',
|
||||
default: 'Button'
|
||||
}
|
||||
}
|
||||
|
||||
export default meta
|
||||
type Story = StoryObj<typeof meta>
|
||||
|
||||
export const SingleButton: Story = {
|
||||
args: {
|
||||
variant: 'primary',
|
||||
size: 'lg'
|
||||
}
|
||||
}
|
||||
|
||||
function generateVariants() {
|
||||
const variantButtons: string[] = []
|
||||
for (const variant of variants) {
|
||||
for (const size of sizes) {
|
||||
variantButtons.push(
|
||||
`<Button variant="${variant}" size="${size}">${size === 'icon' ? `<i class="icon-[lucide--settings]" />` : variant}</Button>`
|
||||
)
|
||||
}
|
||||
}
|
||||
return variantButtons
|
||||
}
|
||||
|
||||
// Note: Keep the number of columns here aligned with the number of sizes above.
|
||||
export const AllVariants: Story = {
|
||||
render: () => ({
|
||||
components: { Button },
|
||||
template: `
|
||||
<div class="grid grid-cols-4 gap-4 place-items-center-safe">
|
||||
${generateVariants().join('\n')}
|
||||
|
||||
</div>
|
||||
`
|
||||
})
|
||||
}
|
||||
28
src/components/ui/button/Button.vue
Normal file
28
src/components/ui/button/Button.vue
Normal file
@@ -0,0 +1,28 @@
|
||||
<script setup lang="ts">
|
||||
import type { PrimitiveProps } from 'reka-ui'
|
||||
import { Primitive } from 'reka-ui'
|
||||
import type { HTMLAttributes } from 'vue'
|
||||
|
||||
import { cn } from '@/utils/tailwindUtil'
|
||||
|
||||
import type { ButtonVariants } from './button.variants'
|
||||
import { buttonVariants } from './button.variants'
|
||||
|
||||
interface Props extends PrimitiveProps {
|
||||
variant?: ButtonVariants['variant']
|
||||
size?: ButtonVariants['size']
|
||||
class?: HTMLAttributes['class']
|
||||
}
|
||||
|
||||
const { as = 'button', class: customClass = '' } = defineProps<Props>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Primitive
|
||||
:as
|
||||
:as-child
|
||||
:class="cn(buttonVariants({ variant, size }), customClass)"
|
||||
>
|
||||
<slot />
|
||||
</Primitive>
|
||||
</template>
|
||||
49
src/components/ui/button/button.variants.ts
Normal file
49
src/components/ui/button/button.variants.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
import type { VariantProps } from 'cva'
|
||||
import { cva } from 'cva'
|
||||
|
||||
export const buttonVariants = cva({
|
||||
base: 'inline-flex items-center justify-center gap-2 cursor-pointer whitespace-nowrap appearance-none border-none rounded-md text-sm font-medium font-inter transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0',
|
||||
variants: {
|
||||
variant: {
|
||||
secondary:
|
||||
'bg-secondary-background text-secondary-foreground hover:bg-secondary-background-hover',
|
||||
primary:
|
||||
'bg-primary-background text-base-foreground hover:bg-primary-background-hover',
|
||||
inverted:
|
||||
'bg-base-foreground text-base-background hover:bg-base-foreground/80',
|
||||
destructive:
|
||||
'bg-destructive-background text-base-foreground hover:bg-destructive-background-hover',
|
||||
textonly:
|
||||
'text-base-foreground bg-transparent hover:bg-secondary-background-hover',
|
||||
'muted-textonly':
|
||||
'text-muted-foreground bg-transparent hover:bg-secondary-background-hover'
|
||||
},
|
||||
size: {
|
||||
sm: 'h-6 rounded-sm px-2 py-1 text-xs',
|
||||
md: 'h-8 rounded-lg p-2 text-xs',
|
||||
lg: 'h-10 rounded-lg px-4 py-2 text-sm',
|
||||
icon: 'size-9'
|
||||
}
|
||||
},
|
||||
|
||||
defaultVariants: {
|
||||
variant: 'secondary',
|
||||
size: 'md'
|
||||
}
|
||||
})
|
||||
|
||||
export type ButtonVariants = VariantProps<typeof buttonVariants>
|
||||
|
||||
const variants = [
|
||||
'secondary',
|
||||
'primary',
|
||||
'inverted',
|
||||
'destructive',
|
||||
'textonly',
|
||||
'muted-textonly'
|
||||
] as const satisfies Array<ButtonVariants['variant']>
|
||||
const sizes = ['sm', 'md', 'lg', 'icon'] as const satisfies Array<
|
||||
ButtonVariants['size']
|
||||
>
|
||||
|
||||
export const FOR_STORIES = { variants, sizes } as const
|
||||
Reference in New Issue
Block a user