feat: add swap component (#4149)

This commit is contained in:
Vben
2024-08-14 20:37:21 +08:00
committed by GitHub
parent b28740042b
commit 83fcdec37c
27 changed files with 260 additions and 146 deletions

View File

@@ -22,6 +22,7 @@ export * from './scrollbar';
export * from './segmented';
export * from './sheet';
export * from './spinner';
export * from './swap';
export * from './tooltip';
export * from './ui/alert-dialog';
export * from './ui/avatar';

View File

@@ -1 +1 @@
export { default as Spinner } from './spinner.vue';
export { default as VbenSpinner } from './spinner.vue';

View File

@@ -0,0 +1 @@
export { default as VbenSwap } from './swap.vue';

View File

@@ -0,0 +1,126 @@
<script lang="ts" setup>
interface Props {
/**
* @zh_CN 交换模式
*/
mode?: 'flip' | 'rotate';
/**
* @zh_CN 开启时的样式
*/
offClass?: string;
/**
* @zh_CN 关闭时的样式
*/
onClass?: string;
}
defineOptions({
name: 'Swap',
});
withDefaults(defineProps<Props>(), {
mode: 'rotate',
onClass: '',
});
</script>
<template>
<label
:class="{
'swap-flip': mode === 'flip',
'swap-rotate': mode === 'rotate',
}"
class="swap"
>
<input class="hidden" type="checkbox" />
<div :class="onClass" class="swap-on">
<slot name="swap-on"></slot>
</div>
<div :class="offClass" class="swap-off">
<slot name="swap-off"></slot>
</div>
</label>
</template>
<style scoped>
.swap {
@apply relative inline-grid cursor-pointer select-none place-content-center;
}
.swap > * {
@apply col-start-1 row-start-1 duration-300 ease-out;
transition-property: transform, opacity;
}
.swap-rotate .swap-on,
.swap-rotate .swap-indeterminate,
.swap-rotate input:indeterminate ~ .swap-on {
@apply rotate-45;
}
.swap-rotate input:checked ~ .swap-off,
.swap-active:where(.swap-rotate) .swap-off,
.swap-rotate input:indeterminate ~ .swap-off {
@apply -rotate-45;
}
.swap-rotate input:checked ~ .swap-on,
.swap-active:where(.swap-rotate) .swap-on,
.swap-rotate input:indeterminate ~ .swap-indeterminate {
@apply rotate-0;
}
.swap-flip {
transform-style: preserve-3d;
perspective: 16em;
}
.swap-flip .swap-on,
.swap-flip .swap-indeterminate,
.swap-flip input:indeterminate ~ .swap-on {
@apply opacity-100;
transform: rotateY(180deg);
backface-visibility: hidden;
}
.swap-flip input:checked ~ .swap-off,
.swap-active:where(.swap-flip) .swap-off,
.swap-flip input:indeterminate ~ .swap-off {
@apply opacity-100;
transform: rotateY(-180deg);
backface-visibility: hidden;
}
.swap-flip input:checked ~ .swap-on,
.swap-active:where(.swap-flip) .swap-on,
.swap-flip input:indeterminate ~ .swap-indeterminate {
transform: rotateY(0deg);
}
.swap input {
@apply appearance-none;
}
.swap .swap-on,
.swap .swap-indeterminate,
.swap input:indeterminate ~ .swap-on {
@apply opacity-0;
}
.swap input:checked ~ .swap-off,
.swap-active .swap-off,
.swap input:indeterminate ~ .swap-off {
@apply opacity-0;
}
.swap input:checked ~ .swap-on,
.swap-active .swap-on,
.swap input:indeterminate ~ .swap-indeterminate {
@apply opacity-100;
}
</style>

View File

@@ -30,7 +30,7 @@ const forwarded = useForwardPropsEmits(delegatedProps, emits);
<template>
<DialogPortal>
<DialogOverlay
class="data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 border-border fixed inset-0 z-[1000] grid place-items-center overflow-y-auto bg-black/80"
class="data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 border-border fixed inset-0 z-[1000] grid place-items-center overflow-y-auto border bg-black/80"
>
<DialogContent
:class="

View File

@@ -9,10 +9,10 @@ export const sheetVariants = cva(
variants: {
side: {
bottom:
'inset-x-0 bottom-0 border-t data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom',
'inset-x-0 bottom-0 border-t border-border data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom',
left: 'inset-y-0 left-0 h-full w-3/4 border-r data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left sm:max-w-sm',
right:
'inset-y-0 right-0 h-full w-3/4 border-l data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right sm:max-w-sm',
'inset-y-0 right-0 h-full w-3/4 border-l data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right sm:max-w-sm',
top: 'inset-x-0 top-0 border-b data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top',
},
},

View File

@@ -5,7 +5,7 @@ import type { HTMLAttributes } from 'vue';
import { cva, type VariantProps } from 'class-variance-authority';
export const toastVariants = cva(
'group pointer-events-auto relative flex w-full items-center justify-between space-x-2 overflow-hidden rounded-md border p-4 pr-6 shadow-lg transition-all data-[swipe=cancel]:translate-x-0 data-[swipe=end]:translate-x-[var(--radix-toast-swipe-end-x)] data-[swipe=move]:translate-x-[var(--radix-toast-swipe-move-x)] data-[swipe=move]:transition-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[swipe=end]:animate-out data-[state=closed]:fade-out-80 data-[state=closed]:slide-out-to-right-full data-[state=open]:slide-in-from-top-full data-[state=open]:sm:slide-in-from-bottom-full',
'group pointer-events-auto relative flex w-full items-center justify-between space-x-2 overflow-hidden rounded-md border border-border p-4 pr-6 shadow-lg transition-all data-[swipe=cancel]:translate-x-0 data-[swipe=end]:translate-x-[var(--radix-toast-swipe-end-x)] data-[swipe=move]:translate-x-[var(--radix-toast-swipe-move-x)] data-[swipe=move]:transition-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[swipe=end]:animate-out data-[state=closed]:fade-out-80 data-[state=closed]:slide-out-to-right-full data-[state=open]:slide-in-from-top-full data-[state=open]:sm:slide-in-from-bottom-full',
{
defaultVariants: {
variant: 'default',

View File

@@ -41,7 +41,7 @@ const forwarded = useForwardPropsEmits(delegatedProps, emits);
v-bind="{ ...forwarded, ...$attrs }"
:class="
cn(
'bg-accent text-accent-foreground animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 border-border z-[1000] overflow-hidden rounded-sm px-4 py-2 text-xs shadow-md',
'bg-accent text-accent-foreground animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 border-border shadow-float z-[1000] overflow-hidden rounded-sm border px-4 py-2 text-xs',
props.class,
)
"