mirror of
https://github.com/versia-pub/frontend.git
synced 2026-03-13 03:29:16 +01:00
refactor: ⬆️ Upgrade to Tailwind v4
This commit is contained in:
parent
14d283c7a8
commit
b6080eff60
160 changed files with 1187 additions and 1178 deletions
|
|
@ -72,6 +72,8 @@ onUnmounted(() => {
|
|||
</style>
|
||||
|
||||
<style>
|
||||
@reference "../../styles/index.css";
|
||||
|
||||
.tiptap p.is-editor-empty:first-child::before {
|
||||
color: hsl(var(--muted-foreground));
|
||||
content: attr(data-placeholder);
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ const forwarded = useForwardPropsEmits(props, emits);
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<AlertDialogRoot v-bind="forwarded">
|
||||
<AlertDialogRoot data-slot="alert-dialog" v-bind="forwarded">
|
||||
<slot />
|
||||
</AlertDialogRoot>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -27,13 +27,15 @@ const forwarded = useForwardPropsEmits(delegatedProps, emits);
|
|||
<template>
|
||||
<AlertDialogPortal>
|
||||
<AlertDialogOverlay
|
||||
class="fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0"
|
||||
data-slot="alert-dialog-overlay"
|
||||
class="data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/80"
|
||||
/>
|
||||
<AlertDialogContent
|
||||
data-slot="alert-dialog-content"
|
||||
v-bind="forwarded"
|
||||
:class="
|
||||
cn(
|
||||
'fixed left-1/2 top-1/2 z-50 grid w-full max-w-lg -translate-x-1/2 -translate-y-1/2 gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg',
|
||||
'bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border p-6 shadow-lg duration-200 sm:max-w-lg',
|
||||
props.class,
|
||||
)
|
||||
"
|
||||
|
|
|
|||
|
|
@ -19,8 +19,9 @@ const delegatedProps = computed(() => {
|
|||
|
||||
<template>
|
||||
<AlertDialogDescription
|
||||
data-slot="alert-dialog-description"
|
||||
v-bind="delegatedProps"
|
||||
:class="cn('text-sm text-muted-foreground', props.class)"
|
||||
:class="cn('text-muted-foreground text-sm', props.class)"
|
||||
>
|
||||
<slot />
|
||||
</AlertDialogDescription>
|
||||
|
|
|
|||
|
|
@ -9,9 +9,10 @@ const props = defineProps<{
|
|||
|
||||
<template>
|
||||
<div
|
||||
data-slot="alert-dialog-footer"
|
||||
:class="
|
||||
cn(
|
||||
'flex flex-col-reverse sm:flex-row sm:justify-end sm:gap-x-2',
|
||||
'flex flex-col-reverse gap-2 sm:flex-row sm:justify-end',
|
||||
props.class,
|
||||
)
|
||||
"
|
||||
|
|
|
|||
|
|
@ -9,7 +9,8 @@ const props = defineProps<{
|
|||
|
||||
<template>
|
||||
<div
|
||||
:class="cn('flex flex-col gap-y-2 text-center sm:text-left', props.class)"
|
||||
data-slot="alert-dialog-header"
|
||||
:class="cn('flex flex-col gap-2 text-center sm:text-left', props.class)"
|
||||
>
|
||||
<slot />
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ const delegatedProps = computed(() => {
|
|||
|
||||
<template>
|
||||
<AlertDialogTitle
|
||||
data-slot="alert-dialog-title"
|
||||
v-bind="delegatedProps"
|
||||
:class="cn('text-lg font-semibold', props.class)"
|
||||
>
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ const props = defineProps<AlertDialogTriggerProps>();
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<AlertDialogTrigger v-bind="props">
|
||||
<AlertDialogTrigger data-slot="alert-dialog-trigger" v-bind="props">
|
||||
<slot />
|
||||
</AlertDialogTrigger>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -10,7 +10,11 @@ const props = defineProps<{
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<div :class="cn(alertVariants({ variant }), props.class)" role="alert">
|
||||
<div
|
||||
data-slot="alert"
|
||||
:class="cn(alertVariants({ variant }), props.class)"
|
||||
role="alert"
|
||||
>
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -8,7 +8,10 @@ const props = defineProps<{
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<div :class="cn('text-sm [&_p]:leading-relaxed', props.class)">
|
||||
<div
|
||||
data-slot="alert-description"
|
||||
:class="cn('text-muted-foreground col-start-2 grid justify-items-start gap-1 text-sm [&_p]:leading-relaxed', props.class)"
|
||||
>
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -8,7 +8,10 @@ const props = defineProps<{
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<h5 :class="cn('mb-1 font-medium leading-none tracking-tight', props.class)">
|
||||
<div
|
||||
data-slot="alert-title"
|
||||
:class="cn('col-start-2 line-clamp-1 min-h-4 font-medium tracking-tight', props.class)"
|
||||
>
|
||||
<slot />
|
||||
</h5>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -5,15 +5,13 @@ export { default as AlertDescription } from "./AlertDescription.vue";
|
|||
export { default as AlertTitle } from "./AlertTitle.vue";
|
||||
|
||||
export const alertVariants = cva(
|
||||
"relative w-full rounded-lg border p-4 [&>svg~*]:pl-7 [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:text-foreground [&>svg]:size-4",
|
||||
"relative w-full rounded-lg border px-4 py-3 text-sm grid has-[>svg]:grid-cols-[calc(var(--spacing)*4)_1fr] grid-cols-[0_1fr] has-[>svg]:gap-x-3 gap-y-0.5 items-start [&>svg]:size-4 [&>svg]:translate-y-0.5 [&>svg]:text-current",
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
default: "bg-background text-foreground",
|
||||
default: "bg-card text-card-foreground",
|
||||
destructive:
|
||||
"border-destructive/50 text-destructive dark:border-destructive [&>svg]:text-destructive",
|
||||
warning:
|
||||
"border-warning/50 text-warning dark:border-warning [&>svg]:text-warning",
|
||||
"text-destructive bg-card [&>svg]:text-current *:data-[slot=alert-description]:text-destructive/90",
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
|
|
|
|||
|
|
@ -2,23 +2,17 @@
|
|||
import { cn } from "@/lib/utils";
|
||||
import { AvatarRoot } from "reka-ui";
|
||||
import type { HTMLAttributes } from "vue";
|
||||
import { type AvatarVariants, avatarVariant } from ".";
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
class?: HTMLAttributes["class"];
|
||||
size?: AvatarVariants["size"];
|
||||
shape?: AvatarVariants["shape"];
|
||||
}>(),
|
||||
{
|
||||
size: "sm",
|
||||
shape: "circle",
|
||||
},
|
||||
);
|
||||
const props = defineProps<{
|
||||
class?: HTMLAttributes["class"];
|
||||
}>();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<AvatarRoot :class="cn(avatarVariant({ size, shape }), props.class)">
|
||||
<AvatarRoot
|
||||
data-slot="avatar"
|
||||
:class="cn('relative flex size-8 shrink-0 overflow-hidden rounded-full', props.class)"
|
||||
>
|
||||
<slot />
|
||||
</AvatarRoot>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -1,11 +1,25 @@
|
|||
<script setup lang="ts">
|
||||
import { cn } from "@/lib/utils";
|
||||
import { AvatarFallback, type AvatarFallbackProps } from "reka-ui";
|
||||
import { type HTMLAttributes, computed } from "vue";
|
||||
|
||||
const props = defineProps<AvatarFallbackProps>();
|
||||
const props = defineProps<
|
||||
AvatarFallbackProps & { class?: HTMLAttributes["class"] }
|
||||
>();
|
||||
|
||||
const delegatedProps = computed(() => {
|
||||
const { class: _, ...delegated } = props;
|
||||
|
||||
return delegated;
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<AvatarFallback v-bind="props">
|
||||
<AvatarFallback
|
||||
data-slot="avatar-fallback"
|
||||
v-bind="delegatedProps"
|
||||
:class="cn('bg-muted flex size-full items-center justify-center rounded-full', props.class)"
|
||||
>
|
||||
<slot />
|
||||
</AvatarFallback>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -6,7 +6,11 @@ const props = defineProps<AvatarImageProps>();
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<AvatarImage v-bind="props" class="h-full w-full object-cover">
|
||||
<AvatarImage
|
||||
data-slot="avatar-image"
|
||||
v-bind="props"
|
||||
class="aspect-square size-full"
|
||||
>
|
||||
<slot />
|
||||
</AvatarImage>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -1,24 +1,3 @@
|
|||
import { type VariantProps, cva } from "class-variance-authority";
|
||||
|
||||
export { default as Avatar } from "./Avatar.vue";
|
||||
export { default as AvatarFallback } from "./AvatarFallback.vue";
|
||||
export { default as AvatarImage } from "./AvatarImage.vue";
|
||||
|
||||
export const avatarVariant = cva(
|
||||
"inline-flex items-center justify-center font-normal text-foreground select-none shrink-0 bg-secondary overflow-hidden",
|
||||
{
|
||||
variants: {
|
||||
size: {
|
||||
sm: "h-10 w-10 text-xs",
|
||||
base: "h-16 w-16 text-2xl",
|
||||
lg: "h-32 w-32 text-5xl",
|
||||
},
|
||||
shape: {
|
||||
circle: "rounded-full",
|
||||
square: "rounded-md",
|
||||
},
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
export type AvatarVariants = VariantProps<typeof avatarVariant>;
|
||||
|
|
|
|||
|
|
@ -1,16 +1,30 @@
|
|||
<script setup lang="ts">
|
||||
import { cn } from "@/lib/utils";
|
||||
import type { HTMLAttributes } from "vue";
|
||||
import type { PrimitiveProps } from "reka-ui";
|
||||
import { Primitive } from "reka-ui";
|
||||
import { type HTMLAttributes, computed } from "vue";
|
||||
import { type BadgeVariants, badgeVariants } from ".";
|
||||
|
||||
const props = defineProps<{
|
||||
variant?: BadgeVariants["variant"];
|
||||
class?: HTMLAttributes["class"];
|
||||
}>();
|
||||
const props = defineProps<
|
||||
PrimitiveProps & {
|
||||
variant?: BadgeVariants["variant"];
|
||||
class?: HTMLAttributes["class"];
|
||||
}
|
||||
>();
|
||||
|
||||
const delegatedProps = computed(() => {
|
||||
const { class: _, ...delegated } = props;
|
||||
|
||||
return delegated;
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div :class="cn(badgeVariants({ variant }), props.class)">
|
||||
<Primitive
|
||||
data-slot="badge"
|
||||
:class="cn(badgeVariants({ variant }), props.class)"
|
||||
v-bind="delegatedProps"
|
||||
>
|
||||
<slot />
|
||||
</div>
|
||||
</Primitive>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -3,17 +3,18 @@ import { type VariantProps, cva } from "class-variance-authority";
|
|||
export { default as Badge } from "./Badge.vue";
|
||||
|
||||
export const badgeVariants = cva(
|
||||
"inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
|
||||
"inline-flex items-center justify-center rounded-md border px-2 py-0.5 text-xs font-medium w-fit whitespace-nowrap shrink-0 [&>svg]:size-3 gap-1 [&>svg]:pointer-events-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive transition-[color,box-shadow] overflow-hidden",
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
default:
|
||||
"border-transparent bg-primary text-primary-foreground hover:bg-primary/80",
|
||||
"border-transparent bg-primary text-primary-foreground [a&]:hover:bg-primary/90",
|
||||
secondary:
|
||||
"border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
|
||||
"border-transparent bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90",
|
||||
destructive:
|
||||
"border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80",
|
||||
outline: "text-foreground",
|
||||
"border-transparent bg-destructive text-white [a&]:hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
|
||||
outline:
|
||||
"text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground",
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
|
|
@ -21,5 +22,4 @@ export const badgeVariants = cva(
|
|||
},
|
||||
},
|
||||
);
|
||||
|
||||
export type BadgeVariants = VariantProps<typeof badgeVariants>;
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ const props = withDefaults(defineProps<Props>(), {
|
|||
|
||||
<template>
|
||||
<Primitive
|
||||
data-slot="button"
|
||||
:as="as"
|
||||
:as-child="asChild"
|
||||
:class="cn(buttonVariants({ variant, size }), props.class)"
|
||||
|
|
|
|||
|
|
@ -1,29 +1,28 @@
|
|||
import type { VariantProps } from "class-variance-authority";
|
||||
import { cva } from "class-variance-authority";
|
||||
import { type VariantProps, cva } from "class-variance-authority";
|
||||
|
||||
export { default as Button } from "./Button.vue";
|
||||
|
||||
export const buttonVariants = cva(
|
||||
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
|
||||
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
default:
|
||||
"bg-primary text-primary-foreground hover:bg-primary/90",
|
||||
"bg-primary text-primary-foreground shadow-xs hover:bg-primary/90",
|
||||
destructive:
|
||||
"bg-destructive text-destructive-foreground hover:bg-destructive/90",
|
||||
"bg-destructive text-white shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
|
||||
outline:
|
||||
"border border-input bg-background hover:bg-accent hover:text-accent-foreground",
|
||||
"border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50",
|
||||
secondary:
|
||||
"bg-secondary text-secondary-foreground hover:bg-secondary/80",
|
||||
ghost: "hover:bg-accent hover:text-accent-foreground",
|
||||
"bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80",
|
||||
ghost: "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
|
||||
link: "text-primary underline-offset-4 hover:underline",
|
||||
},
|
||||
size: {
|
||||
default: "h-10 px-4 py-2",
|
||||
sm: "h-9 rounded-md px-3",
|
||||
lg: "h-11 rounded-md px-8",
|
||||
icon: "h-10 w-10",
|
||||
default: "h-9 px-4 py-2 has-[>svg]:px-3",
|
||||
sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5",
|
||||
lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
|
||||
icon: "size-9",
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
|
|
|
|||
|
|
@ -8,14 +8,11 @@ const props = defineProps<{
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
:class="
|
||||
cn(
|
||||
'rounded-lg border bg-card text-card-foreground shadow-sm flex flex-col gap-6 p-4 items-center justify-center',
|
||||
props.class
|
||||
)
|
||||
"
|
||||
>
|
||||
<div data-slot="card" :class="cn(
|
||||
'bg-card text-card-foreground flex flex-col gap-6 rounded-xl border py-6 shadow-sm',
|
||||
props.class,
|
||||
)
|
||||
">
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
|||
17
components/ui/card/CardAction.vue
Normal file
17
components/ui/card/CardAction.vue
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
<script setup lang="ts">
|
||||
import { cn } from "@/lib/utils";
|
||||
import type { HTMLAttributes } from "vue";
|
||||
|
||||
const props = defineProps<{
|
||||
class?: HTMLAttributes["class"];
|
||||
}>();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
data-slot="card-action"
|
||||
:class="cn('col-start-2 row-span-2 row-start-1 self-start justify-self-end', props.class)"
|
||||
>
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
|
|
@ -8,7 +8,10 @@ const props = defineProps<{
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<div :class="cn('flex flex-col gap-2', props.class)">
|
||||
<slot />
|
||||
</div>
|
||||
<div
|
||||
data-slot="card-content"
|
||||
:class="cn('px-6', props.class)"
|
||||
>
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -8,7 +8,10 @@ const props = defineProps<{
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<p :class="cn('text-sm text-muted-foreground', props.class)">
|
||||
<p
|
||||
data-slot="card-description"
|
||||
:class="cn('text-muted-foreground text-sm', props.class)"
|
||||
>
|
||||
<slot />
|
||||
</p>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -8,7 +8,10 @@ const props = defineProps<{
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<div :class="cn('flex items-center', props.class)">
|
||||
<slot />
|
||||
</div>
|
||||
<div
|
||||
data-slot="card-footer"
|
||||
:class="cn('flex items-center px-6 [.border-t]:pt-6', props.class)"
|
||||
>
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -8,7 +8,10 @@ const props = defineProps<{
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<div :class="cn('flex flex-col gap-y-1.5', props.class)">
|
||||
<slot />
|
||||
</div>
|
||||
<div
|
||||
data-slot="card-header"
|
||||
:class="cn('@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-start gap-1.5 px-6 has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6', props.class)"
|
||||
>
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -9,9 +9,8 @@ const props = defineProps<{
|
|||
|
||||
<template>
|
||||
<h3
|
||||
:class="
|
||||
cn('text-2xl font-semibold leading-none tracking-tight', props.class)
|
||||
"
|
||||
data-slot="card-title"
|
||||
:class="cn('leading-none font-semibold', props.class)"
|
||||
>
|
||||
<slot />
|
||||
</h3>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
export { default as Card } from "./Card.vue";
|
||||
export { default as CardAction } from "./CardAction.vue";
|
||||
export { default as CardContent } from "./CardContent.vue";
|
||||
export { default as CardDescription } from "./CardDescription.vue";
|
||||
export { default as CardFooter } from "./CardFooter.vue";
|
||||
|
|
|
|||
|
|
@ -21,14 +21,18 @@ const forwarded = useForwardPropsEmits(delegatedProps, emits);
|
|||
|
||||
<template>
|
||||
<CheckboxRoot
|
||||
data-slot="checkbox"
|
||||
v-bind="forwarded"
|
||||
:class="
|
||||
cn('peer h-4 w-4 shrink-0 rounded-sm border border-primary ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground',
|
||||
cn('peer border-input data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground data-[state=checked]:border-primary focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive size-4 shrink-0 rounded-[4px] border shadow-xs transition-shadow outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50',
|
||||
props.class)"
|
||||
>
|
||||
<CheckboxIndicator class="flex h-full w-full items-center justify-center text-current">
|
||||
<CheckboxIndicator
|
||||
data-slot="checkbox-indicator"
|
||||
class="flex items-center justify-center text-current transition-none"
|
||||
>
|
||||
<slot>
|
||||
<Check class="h-4 w-4" />
|
||||
<Check class="size-3.5" />
|
||||
</slot>
|
||||
</CheckboxIndicator>
|
||||
</CheckboxRoot>
|
||||
|
|
|
|||
|
|
@ -9,7 +9,11 @@ const forwarded = useForwardPropsEmits(props, emits);
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<CollapsibleRoot v-slot="{ open }" v-bind="forwarded">
|
||||
<CollapsibleRoot
|
||||
v-slot="{ open }"
|
||||
data-slot="collapsible"
|
||||
v-bind="forwarded"
|
||||
>
|
||||
<slot :open="open" />
|
||||
</CollapsibleRoot>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -5,7 +5,10 @@ const props = defineProps<CollapsibleContentProps>();
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<CollapsibleContent v-bind="props" class="overflow-hidden transition-all data-[state=closed]:animate-collapsible-up data-[state=open]:animate-collapsible-down">
|
||||
<CollapsibleContent
|
||||
data-slot="collapsible-content"
|
||||
v-bind="props"
|
||||
>
|
||||
<slot />
|
||||
</CollapsibleContent>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -5,7 +5,10 @@ const props = defineProps<CollapsibleTriggerProps>();
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<CollapsibleTrigger v-bind="props">
|
||||
<CollapsibleTrigger
|
||||
data-slot="collapsible-trigger"
|
||||
v-bind="props"
|
||||
>
|
||||
<slot />
|
||||
</CollapsibleTrigger>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ function filterItems() {
|
|||
// Check which groups have at least 1 item shown
|
||||
for (const [groupId, group] of allGroups.value) {
|
||||
for (const itemId of group) {
|
||||
if (filterState.filtered.items.get(itemId)) {
|
||||
if ((filterState.filtered.items.get(itemId) ?? 0) > 0) {
|
||||
filterState.filtered.groups.add(groupId);
|
||||
break;
|
||||
}
|
||||
|
|
@ -90,15 +90,11 @@ provideCommandContext({
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<ListboxRoot
|
||||
v-bind="forwarded"
|
||||
:class="
|
||||
cn(
|
||||
'flex h-full w-full flex-col overflow-hidden rounded-md bg-popover text-popover-foreground',
|
||||
props.class
|
||||
)
|
||||
"
|
||||
>
|
||||
<slot />
|
||||
</ListboxRoot>
|
||||
<ListboxRoot
|
||||
data-slot="command"
|
||||
v-bind="forwarded"
|
||||
:class="cn('bg-popover text-popover-foreground flex h-full w-full flex-col overflow-hidden rounded-md', props.class)"
|
||||
>
|
||||
<slot />
|
||||
</ListboxRoot>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -1,10 +1,27 @@
|
|||
<script setup lang="ts">
|
||||
import { Dialog, DialogContent } from "@/components/ui/dialog";
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
} from "@/components/ui/dialog";
|
||||
import type { DialogRootEmits, DialogRootProps } from "reka-ui";
|
||||
import { useForwardPropsEmits } from "reka-ui";
|
||||
import Command from "./Command.vue";
|
||||
|
||||
const props = defineProps<DialogRootProps>();
|
||||
const props = withDefaults(
|
||||
defineProps<
|
||||
DialogRootProps & {
|
||||
title?: string;
|
||||
description?: string;
|
||||
}
|
||||
>(),
|
||||
{
|
||||
title: "Command Palette",
|
||||
description: "Search for a command to run...",
|
||||
},
|
||||
);
|
||||
const emits = defineEmits<DialogRootEmits>();
|
||||
|
||||
const forwarded = useForwardPropsEmits(props, emits);
|
||||
|
|
@ -12,8 +29,12 @@ const forwarded = useForwardPropsEmits(props, emits);
|
|||
|
||||
<template>
|
||||
<Dialog v-bind="forwarded">
|
||||
<DialogContent class="overflow-hidden p-0 shadow-lg">
|
||||
<Command class="[&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground [&_[cmdk-group]:not([hidden])_~[cmdk-group]]:pt-0 [&_[cmdk-group]]:px-2 [&_[cmdk-input-wrapper]_svg]:h-5 [&_[cmdk-input-wrapper]_svg]:w-5 [&_[cmdk-input]]:h-12 [&_[cmdk-item]]:px-2 [&_[cmdk-item]]:py-3 [&_[cmdk-item]_svg]:h-5 [&_[cmdk-item]_svg]:w-5">
|
||||
<DialogHeader class="sr-only">
|
||||
<DialogTitle>{{ title }}</DialogTitle>
|
||||
<DialogDescription>{{ description }}</DialogDescription>
|
||||
</DialogHeader>
|
||||
<DialogContent class="overflow-hidden p-0 ">
|
||||
<Command>
|
||||
<slot />
|
||||
</Command>
|
||||
</DialogContent>
|
||||
|
|
|
|||
|
|
@ -22,7 +22,11 @@ const isRender = computed(
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<Primitive v-if="isRender" v-bind="delegatedProps" :class="cn('py-6 text-center text-sm', props.class)">
|
||||
<Primitive
|
||||
v-if="isRender"
|
||||
data-slot="command-empty"
|
||||
v-bind="delegatedProps" :class="cn('py-6 text-center text-sm', props.class)"
|
||||
>
|
||||
<slot />
|
||||
</Primitive>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -37,23 +37,16 @@ onUnmounted(() => {
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<ListboxGroup
|
||||
v-bind="delegatedProps"
|
||||
:id="id"
|
||||
:class="
|
||||
cn(
|
||||
'overflow-hidden p-1 text-foreground [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:py-1.5 [&_[cmdk-group-heading]]:text-xs [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground',
|
||||
props.class
|
||||
)
|
||||
"
|
||||
:hidden="isRender ? undefined : true"
|
||||
>
|
||||
<ListboxGroupLabel
|
||||
v-if="heading"
|
||||
class="px-2 py-1.5 text-xs font-medium text-muted-foreground"
|
||||
>
|
||||
{{ heading }}
|
||||
</ListboxGroupLabel>
|
||||
<slot />
|
||||
</ListboxGroup>
|
||||
<ListboxGroup
|
||||
v-bind="delegatedProps"
|
||||
:id="id"
|
||||
data-slot="command-group"
|
||||
:class="cn('text-foreground overflow-hidden p-1', props.class)"
|
||||
:hidden="isRender ? undefined : true"
|
||||
>
|
||||
<ListboxGroupLabel v-if="heading" class="px-2 py-1.5 text-xs font-medium text-muted-foreground">
|
||||
{{ heading }}
|
||||
</ListboxGroupLabel>
|
||||
<slot />
|
||||
</ListboxGroup>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -31,13 +31,17 @@ const { filterState } = useCommand();
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex items-center border-b px-3" cmdk-input-wrapper>
|
||||
<Search class="mr-2 h-4 w-4 shrink-0 opacity-50" />
|
||||
<div
|
||||
data-slot="command-input-wrapper"
|
||||
class="flex h-12 items-center gap-2 border-b px-3"
|
||||
>
|
||||
<Search class="size-4 shrink-0 opacity-50" />
|
||||
<ListboxFilter
|
||||
v-bind="{ ...forwardedProps, ...$attrs }"
|
||||
v-model="filterState.search"
|
||||
data-slot="command-input"
|
||||
auto-focus
|
||||
:class="cn('flex h-10 w-full rounded-md bg-transparent py-3 text-sm outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50', props.class)"
|
||||
:class="cn('placeholder:text-muted-foreground flex h-12 w-full rounded-md bg-transparent py-3 text-sm outline-hidden disabled:cursor-not-allowed disabled:opacity-50', props.class)"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -73,23 +73,17 @@ onUnmounted(() => {
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<ListboxItem
|
||||
v-if="isRender"
|
||||
v-bind="forwarded"
|
||||
:id="id"
|
||||
ref="itemRef"
|
||||
:class="
|
||||
cn(
|
||||
'relative flex cursor-default gap-2 select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:size-4 [&_svg]:shrink-0',
|
||||
props.class
|
||||
)
|
||||
"
|
||||
@select="
|
||||
() => {
|
||||
filterState.search = '';
|
||||
}
|
||||
"
|
||||
>
|
||||
<slot />
|
||||
</ListboxItem>
|
||||
<ListboxItem
|
||||
v-if="isRender"
|
||||
v-bind="forwarded"
|
||||
:id="id"
|
||||
ref="itemRef"
|
||||
data-slot="command-item"
|
||||
:class="cn(`data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground [&_svg:not([class*='text-'])]:text-muted-foreground relative flex cursor-default items-center gap-2 rounded-sm px-2 py-3 text-sm outline-hidden select-none data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4`, props.class)"
|
||||
@select="() => {
|
||||
filterState.search = ''
|
||||
}"
|
||||
>
|
||||
<slot />
|
||||
</ListboxItem>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -18,7 +18,11 @@ const forwarded = useForwardProps(delegatedProps);
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<ListboxContent v-bind="forwarded" :class="cn('max-h-[300px] overflow-y-auto overflow-x-hidden', props.class)">
|
||||
<ListboxContent
|
||||
data-slot="command-list"
|
||||
v-bind="forwarded"
|
||||
:class="cn('max-h-[300px] scroll-py-1 overflow-x-hidden overflow-y-auto', props.class)"
|
||||
>
|
||||
<div role="presentation">
|
||||
<slot />
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -17,8 +17,9 @@ const delegatedProps = computed(() => {
|
|||
|
||||
<template>
|
||||
<Separator
|
||||
data-slot="command-separator"
|
||||
v-bind="delegatedProps"
|
||||
:class="cn('-mx-1 h-px bg-border', props.class)"
|
||||
:class="cn('bg-border -mx-1 h-px', props.class)"
|
||||
>
|
||||
<slot />
|
||||
</Separator>
|
||||
|
|
|
|||
|
|
@ -8,7 +8,10 @@ const props = defineProps<{
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<span :class="cn('ml-auto text-xs tracking-widest text-muted-foreground', props.class)">
|
||||
<span
|
||||
data-slot="command-shortcut"
|
||||
:class="cn('text-muted-foreground ml-auto text-xs tracking-widest', props.class)"
|
||||
>
|
||||
<slot />
|
||||
</span>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -13,7 +13,10 @@ const forwarded = useForwardPropsEmits(props, emits);
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<DialogRoot v-bind="forwarded">
|
||||
<DialogRoot
|
||||
data-slot="dialog"
|
||||
v-bind="forwarded"
|
||||
>
|
||||
<slot />
|
||||
</DialogRoot>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -5,7 +5,10 @@ const props = defineProps<DialogCloseProps>();
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<DialogClose v-bind="props">
|
||||
<DialogClose
|
||||
data-slot="dialog-close"
|
||||
v-bind="props"
|
||||
>
|
||||
<slot />
|
||||
</DialogClose>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -6,17 +6,14 @@ import {
|
|||
DialogContent,
|
||||
type DialogContentEmits,
|
||||
type DialogContentProps,
|
||||
DialogOverlay,
|
||||
DialogPortal,
|
||||
useForwardPropsEmits,
|
||||
} from "reka-ui";
|
||||
import { type HTMLAttributes, computed } from "vue";
|
||||
import DialogOverlay from "./DialogOverlay.vue";
|
||||
|
||||
const props = defineProps<
|
||||
DialogContentProps & {
|
||||
class?: HTMLAttributes["class"];
|
||||
hideClose?: boolean;
|
||||
}
|
||||
DialogContentProps & { class?: HTMLAttributes["class"] }
|
||||
>();
|
||||
const emits = defineEmits<DialogContentEmits>();
|
||||
|
||||
|
|
@ -30,28 +27,25 @@ const forwarded = useForwardPropsEmits(delegatedProps, emits);
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<DialogPortal>
|
||||
<DialogOverlay
|
||||
class="fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0"
|
||||
/>
|
||||
<DialogContent
|
||||
v-bind="forwarded"
|
||||
:class="
|
||||
cn(
|
||||
'fixed left-1/2 top-1/2 z-50 grid w-full max-w-lg -translate-x-1/2 -translate-y-1/2 gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg',
|
||||
props.class
|
||||
)
|
||||
"
|
||||
>
|
||||
<slot />
|
||||
<DialogPortal>
|
||||
<DialogOverlay />
|
||||
<DialogContent
|
||||
data-slot="dialog-content"
|
||||
v-bind="forwarded"
|
||||
:class="
|
||||
cn(
|
||||
'bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border p-6 shadow-lg duration-200 sm:max-w-lg',
|
||||
props.class,
|
||||
)"
|
||||
>
|
||||
<slot />
|
||||
|
||||
<DialogClose
|
||||
v-if="!hideClose"
|
||||
class="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground"
|
||||
>
|
||||
<X class="w-4 h-4" />
|
||||
<span class="sr-only">Close</span>
|
||||
</DialogClose>
|
||||
</DialogContent>
|
||||
</DialogPortal>
|
||||
<DialogClose
|
||||
class="ring-offset-background focus:ring-ring data-[state=open]:bg-accent data-[state=open]:text-muted-foreground absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4"
|
||||
>
|
||||
<X />
|
||||
<span class="sr-only">Close</span>
|
||||
</DialogClose>
|
||||
</DialogContent>
|
||||
</DialogPortal>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -22,8 +22,9 @@ const forwardedProps = useForwardProps(delegatedProps);
|
|||
|
||||
<template>
|
||||
<DialogDescription
|
||||
data-slot="dialog-description"
|
||||
v-bind="forwardedProps"
|
||||
:class="cn('text-sm text-muted-foreground', props.class)"
|
||||
:class="cn('text-muted-foreground text-sm', props.class)"
|
||||
>
|
||||
<slot />
|
||||
</DialogDescription>
|
||||
|
|
|
|||
|
|
@ -7,12 +7,8 @@ const props = defineProps<{ class?: HTMLAttributes["class"] }>();
|
|||
|
||||
<template>
|
||||
<div
|
||||
:class="
|
||||
cn(
|
||||
'flex flex-col-reverse sm:flex-row sm:justify-end sm:gap-x-2',
|
||||
props.class,
|
||||
)
|
||||
"
|
||||
data-slot="dialog-footer"
|
||||
:class="cn('flex flex-col-reverse gap-2 sm:flex-row sm:justify-end', props.class)"
|
||||
>
|
||||
<slot />
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -9,7 +9,8 @@ const props = defineProps<{
|
|||
|
||||
<template>
|
||||
<div
|
||||
:class="cn('flex flex-col gap-y-1.5 text-center sm:text-left', props.class)"
|
||||
data-slot="dialog-header"
|
||||
:class="cn('flex flex-col gap-2 text-center sm:text-left', props.class)"
|
||||
>
|
||||
<slot />
|
||||
</div>
|
||||
|
|
|
|||
25
components/ui/dialog/DialogOverlay.vue
Normal file
25
components/ui/dialog/DialogOverlay.vue
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
<script setup lang="ts">
|
||||
import { cn } from "@/lib/utils";
|
||||
import { DialogOverlay, type DialogOverlayProps } from "reka-ui";
|
||||
import { type HTMLAttributes, computed } from "vue";
|
||||
|
||||
const props = defineProps<
|
||||
DialogOverlayProps & { class?: HTMLAttributes["class"] }
|
||||
>();
|
||||
|
||||
const delegatedProps = computed(() => {
|
||||
const { class: _, ...delegated } = props;
|
||||
|
||||
return delegated;
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<DialogOverlay
|
||||
data-slot="dialog-overlay"
|
||||
v-bind="delegatedProps"
|
||||
:class="cn('data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/80', props.class)"
|
||||
>
|
||||
<slot />
|
||||
</DialogOverlay>
|
||||
</template>
|
||||
|
|
@ -29,7 +29,7 @@ const forwarded = useForwardPropsEmits(delegatedProps, emits);
|
|||
<template>
|
||||
<DialogPortal>
|
||||
<DialogOverlay
|
||||
class="fixed inset-0 z-50 grid place-items-center overflow-y-auto bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0"
|
||||
class="fixed inset-0 z-50 grid place-items-center overflow-y-auto bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0"
|
||||
>
|
||||
<DialogContent
|
||||
:class="
|
||||
|
|
@ -50,7 +50,7 @@ const forwarded = useForwardPropsEmits(delegatedProps, emits);
|
|||
<slot />
|
||||
|
||||
<DialogClose
|
||||
class="absolute top-3 right-3 p-0.5 transition-colors rounded-md hover:bg-secondary"
|
||||
class="absolute top-4 right-4 p-0.5 transition-colors rounded-md hover:bg-secondary"
|
||||
>
|
||||
<X class="w-4 h-4" />
|
||||
<span class="sr-only">Close</span>
|
||||
|
|
|
|||
|
|
@ -18,13 +18,9 @@ const forwardedProps = useForwardProps(delegatedProps);
|
|||
|
||||
<template>
|
||||
<DialogTitle
|
||||
data-slot="dialog-title"
|
||||
v-bind="forwardedProps"
|
||||
:class="
|
||||
cn(
|
||||
'text-lg font-semibold leading-none tracking-tight',
|
||||
props.class,
|
||||
)
|
||||
"
|
||||
:class="cn('text-lg leading-none font-semibold', props.class)"
|
||||
>
|
||||
<slot />
|
||||
</DialogTitle>
|
||||
|
|
|
|||
|
|
@ -5,7 +5,10 @@ const props = defineProps<DialogTriggerProps>();
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<DialogTrigger v-bind="props">
|
||||
<DialogTrigger
|
||||
data-slot="dialog-trigger"
|
||||
v-bind="props"
|
||||
>
|
||||
<slot />
|
||||
</DialogTrigger>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ export { default as DialogContent } from "./DialogContent.vue";
|
|||
export { default as DialogDescription } from "./DialogDescription.vue";
|
||||
export { default as DialogFooter } from "./DialogFooter.vue";
|
||||
export { default as DialogHeader } from "./DialogHeader.vue";
|
||||
export { default as DialogOverlay } from "./DialogOverlay.vue";
|
||||
export { default as DialogScrollContent } from "./DialogScrollContent.vue";
|
||||
export { default as DialogTitle } from "./DialogTitle.vue";
|
||||
export { default as DialogTrigger } from "./DialogTrigger.vue";
|
||||
|
|
|
|||
|
|
@ -13,7 +13,10 @@ const forwarded = useForwardPropsEmits(props, emits);
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<DrawerRoot v-bind="forwarded">
|
||||
<DrawerRoot
|
||||
data-slot="drawer"
|
||||
v-bind="forwarded"
|
||||
>
|
||||
<slot />
|
||||
</DrawerRoot>
|
||||
</template>
|
||||
|
|
|
|||
15
components/ui/drawer/DrawerClose.vue
Normal file
15
components/ui/drawer/DrawerClose.vue
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
<script lang="ts" setup>
|
||||
import type { DrawerCloseProps } from "vaul-vue";
|
||||
import { DrawerClose } from "vaul-vue";
|
||||
|
||||
const props = defineProps<DrawerCloseProps>();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<DrawerClose
|
||||
data-slot="drawer-close"
|
||||
v-bind="props"
|
||||
>
|
||||
<slot />
|
||||
</DrawerClose>
|
||||
</template>
|
||||
|
|
@ -18,12 +18,18 @@ const forwarded = useForwardPropsEmits(props, emits);
|
|||
<DrawerPortal>
|
||||
<DrawerOverlay />
|
||||
<DrawerContent
|
||||
v-bind="forwarded" :class="cn(
|
||||
'fixed inset-x-0 bottom-0 z-50 mt-24 flex h-auto flex-col rounded-t-[10px] border bg-background',
|
||||
data-slot="drawer-content"
|
||||
v-bind="forwarded"
|
||||
:class="cn(
|
||||
`group/drawer-content bg-background fixed z-50 flex h-auto flex-col`,
|
||||
`data-[vaul-drawer-direction=top]:inset-x-0 data-[vaul-drawer-direction=top]:top-0 data-[vaul-drawer-direction=top]:mb-24 data-[vaul-drawer-direction=top]:max-h-[80vh] data-[vaul-drawer-direction=top]:rounded-b-lg`,
|
||||
`data-[vaul-drawer-direction=bottom]:inset-x-0 data-[vaul-drawer-direction=bottom]:bottom-0 data-[vaul-drawer-direction=bottom]:mt-24 data-[vaul-drawer-direction=bottom]:max-h-[80vh] data-[vaul-drawer-direction=bottom]:rounded-t-lg`,
|
||||
`data-[vaul-drawer-direction=right]:inset-y-0 data-[vaul-drawer-direction=right]:right-0 data-[vaul-drawer-direction=right]:w-3/4 data-[vaul-drawer-direction=right]:sm:max-w-sm`,
|
||||
`data-[vaul-drawer-direction=left]:inset-y-0 data-[vaul-drawer-direction=left]:left-0 data-[vaul-drawer-direction=left]:w-3/4 data-[vaul-drawer-direction=left]:sm:max-w-sm`,
|
||||
props.class,
|
||||
)"
|
||||
>
|
||||
<div class="mx-auto mt-4 h-2 w-[100px] rounded-full bg-muted" />
|
||||
<div class="bg-muted mx-auto mt-4 hidden h-2 w-[100px] shrink-0 rounded-full group-data-[vaul-drawer-direction=bottom]/drawer-content:block" />
|
||||
<slot />
|
||||
</DrawerContent>
|
||||
</DrawerPortal>
|
||||
|
|
|
|||
|
|
@ -16,7 +16,11 @@ const delegatedProps = computed(() => {
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<DrawerDescription v-bind="delegatedProps" :class="cn('text-sm text-muted-foreground', props.class)">
|
||||
<DrawerDescription
|
||||
data-slot="drawer-description"
|
||||
v-bind="delegatedProps"
|
||||
:class="cn('text-muted-foreground text-sm', props.class)"
|
||||
>
|
||||
<slot />
|
||||
</DrawerDescription>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -8,7 +8,10 @@ const props = defineProps<{
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<div :class="cn('mt-auto flex flex-col gap-2 p-4', props.class)">
|
||||
<div
|
||||
data-slot="drawer-footer"
|
||||
:class="cn('mt-auto flex flex-col gap-2 p-4', props.class)"
|
||||
>
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -8,7 +8,10 @@ const props = defineProps<{
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<div :class="cn('grid gap-1.5 p-4 text-center sm:text-left', props.class)">
|
||||
<div
|
||||
data-slot="drawer-header"
|
||||
:class="cn('flex flex-col gap-1.5 p-4', props.class)"
|
||||
>
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -16,5 +16,9 @@ const delegatedProps = computed(() => {
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<DrawerOverlay v-bind="delegatedProps" :class="cn('fixed inset-0 z-50 bg-black/80', props.class)" />
|
||||
<DrawerOverlay
|
||||
data-slot="drawer-overlay"
|
||||
v-bind="delegatedProps"
|
||||
:class="cn('data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/80', props.class)"
|
||||
/>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -16,7 +16,11 @@ const delegatedProps = computed(() => {
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<DrawerTitle v-bind="delegatedProps" :class="cn('text-lg font-semibold leading-none tracking-tight', props.class)">
|
||||
<DrawerTitle
|
||||
data-slot="drawer-title"
|
||||
v-bind="delegatedProps"
|
||||
:class="cn('text-foreground font-semibold', props.class)"
|
||||
>
|
||||
<slot />
|
||||
</DrawerTitle>
|
||||
</template>
|
||||
|
|
|
|||
15
components/ui/drawer/DrawerTrigger.vue
Normal file
15
components/ui/drawer/DrawerTrigger.vue
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
<script lang="ts" setup>
|
||||
import type { DrawerTriggerProps } from "vaul-vue";
|
||||
import { DrawerTrigger } from "vaul-vue";
|
||||
|
||||
const props = defineProps<DrawerTriggerProps>();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<DrawerTrigger
|
||||
data-slot="drawer-trigger"
|
||||
v-bind="props"
|
||||
>
|
||||
<slot />
|
||||
</DrawerTrigger>
|
||||
</template>
|
||||
|
|
@ -1,8 +1,9 @@
|
|||
export { default as Drawer } from "./Drawer.vue";
|
||||
export { default as DrawerClose } from "./DrawerClose.vue";
|
||||
export { default as DrawerContent } from "./DrawerContent.vue";
|
||||
export { default as DrawerDescription } from "./DrawerDescription.vue";
|
||||
export { default as DrawerFooter } from "./DrawerFooter.vue";
|
||||
export { default as DrawerHeader } from "./DrawerHeader.vue";
|
||||
export { default as DrawerOverlay } from "./DrawerOverlay.vue";
|
||||
export { default as DrawerTitle } from "./DrawerTitle.vue";
|
||||
export { DrawerClose, DrawerPortal, DrawerTrigger } from "vaul-vue";
|
||||
export { default as DrawerTrigger } from "./DrawerTrigger.vue";
|
||||
|
|
|
|||
|
|
@ -13,7 +13,10 @@ const forwarded = useForwardPropsEmits(props, emits);
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<DropdownMenuRoot v-bind="forwarded">
|
||||
<DropdownMenuRoot
|
||||
data-slot="dropdown-menu"
|
||||
v-bind="forwarded"
|
||||
>
|
||||
<slot />
|
||||
</DropdownMenuRoot>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -26,15 +26,16 @@ const forwarded = useForwardPropsEmits(delegatedProps, emits);
|
|||
|
||||
<template>
|
||||
<DropdownMenuCheckboxItem
|
||||
data-slot="dropdown-menu-checkbox-item"
|
||||
v-bind="forwarded"
|
||||
:class=" cn(
|
||||
'relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
|
||||
`focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4`,
|
||||
props.class,
|
||||
)"
|
||||
>
|
||||
<span class="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
|
||||
<span class="pointer-events-none absolute left-2 flex size-3.5 items-center justify-center">
|
||||
<DropdownMenuItemIndicator>
|
||||
<Check class="w-4 h-4" />
|
||||
<Check class="size-4" />
|
||||
</DropdownMenuItemIndicator>
|
||||
</span>
|
||||
<slot />
|
||||
|
|
|
|||
|
|
@ -31,8 +31,9 @@ const forwarded = useForwardPropsEmits(delegatedProps, emits);
|
|||
<template>
|
||||
<DropdownMenuPortal>
|
||||
<DropdownMenuContent
|
||||
data-slot="dropdown-menu-content"
|
||||
v-bind="forwarded"
|
||||
:class="cn('z-50 min-w-32 overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-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', props.class)"
|
||||
:class="cn('bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-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 z-50 max-h-(--reka-dropdown-menu-content-available-height) min-w-[8rem] origin-(--reka-dropdown-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border p-1 shadow-md', props.class)"
|
||||
>
|
||||
<slot />
|
||||
</DropdownMenuContent>
|
||||
|
|
|
|||
|
|
@ -5,7 +5,10 @@ const props = defineProps<DropdownMenuGroupProps>();
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<DropdownMenuGroup v-bind="props">
|
||||
<DropdownMenuGroup
|
||||
data-slot="dropdown-menu-group"
|
||||
v-bind="props"
|
||||
>
|
||||
<slot />
|
||||
</DropdownMenuGroup>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -1,36 +1,39 @@
|
|||
<script setup lang="ts">
|
||||
import { cn } from "@/lib/utils";
|
||||
import { reactiveOmit } from "@vueuse/core";
|
||||
import {
|
||||
DropdownMenuItem,
|
||||
type DropdownMenuItemProps,
|
||||
useForwardProps,
|
||||
} from "reka-ui";
|
||||
import { type HTMLAttributes, computed } from "vue";
|
||||
import type { HTMLAttributes } from "vue";
|
||||
|
||||
const props = defineProps<
|
||||
DropdownMenuItemProps & { class?: HTMLAttributes["class"]; inset?: boolean }
|
||||
>();
|
||||
const props = withDefaults(
|
||||
defineProps<
|
||||
DropdownMenuItemProps & {
|
||||
class?: HTMLAttributes["class"];
|
||||
inset?: boolean;
|
||||
variant?: "default" | "destructive";
|
||||
}
|
||||
>(),
|
||||
{
|
||||
variant: "default",
|
||||
},
|
||||
);
|
||||
|
||||
const delegatedProps = computed(() => {
|
||||
const { class: _, ...delegated } = props;
|
||||
|
||||
return delegated;
|
||||
});
|
||||
const delegatedProps = reactiveOmit(props, "inset", "variant");
|
||||
|
||||
const forwardedProps = useForwardProps(delegatedProps);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<DropdownMenuItem
|
||||
v-bind="forwardedProps"
|
||||
:class="
|
||||
cn(
|
||||
'relative flex w-full cursor-default select-none items-center rounded-sm gap-2 px-2 py-1.5 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&>svg]:size-4 [&>svg]:shrink-0',
|
||||
inset && 'pl-8',
|
||||
props.class
|
||||
)
|
||||
"
|
||||
>
|
||||
<slot />
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem
|
||||
data-slot="dropdown-menu-item"
|
||||
:data-inset="inset ? '' : undefined"
|
||||
:data-variant="variant"
|
||||
v-bind="forwardedProps"
|
||||
:class="cn(`focus:bg-accent focus:text-accent-foreground data-[variant=destructive]:text-destructive-foreground data-[variant=destructive]:focus:bg-destructive/10 dark:data-[variant=destructive]:focus:bg-destructive/40 data-[variant=destructive]:focus:text-destructive-foreground data-[variant=destructive]:*:[svg]:!text-destructive-foreground [&_svg:not([class*='text-'])]:text-muted-foreground relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4`, props.class)"
|
||||
>
|
||||
<slot />
|
||||
</DropdownMenuItem>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
<script setup lang="ts">
|
||||
import { cn } from "@/lib/utils";
|
||||
import { reactiveOmit } from "@vueuse/core";
|
||||
import {
|
||||
DropdownMenuLabel,
|
||||
type DropdownMenuLabelProps,
|
||||
useForwardProps,
|
||||
} from "reka-ui";
|
||||
import { type HTMLAttributes, computed } from "vue";
|
||||
import type { HTMLAttributes } from "vue";
|
||||
|
||||
const props = defineProps<
|
||||
DropdownMenuLabelProps & {
|
||||
|
|
@ -14,19 +15,16 @@ const props = defineProps<
|
|||
}
|
||||
>();
|
||||
|
||||
const delegatedProps = computed(() => {
|
||||
const { class: _, ...delegated } = props;
|
||||
|
||||
return delegated;
|
||||
});
|
||||
|
||||
const delegatedProps = reactiveOmit(props, "class", "inset");
|
||||
const forwardedProps = useForwardProps(delegatedProps);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<DropdownMenuLabel
|
||||
data-slot="dropdown-menu-label"
|
||||
:data-inset="inset ? '' : undefined"
|
||||
v-bind="forwardedProps"
|
||||
:class="cn('px-2 py-1.5 text-sm font-semibold', inset && 'pl-8', props.class)"
|
||||
:class="cn('px-2 py-1.5 text-sm font-medium data-[inset]:pl-8', props.class)"
|
||||
>
|
||||
<slot />
|
||||
</DropdownMenuLabel>
|
||||
|
|
|
|||
|
|
@ -13,7 +13,10 @@ const forwarded = useForwardPropsEmits(props, emits);
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<DropdownMenuRadioGroup v-bind="forwarded">
|
||||
<DropdownMenuRadioGroup
|
||||
data-slot="dropdown-menu-radio-group"
|
||||
v-bind="forwarded"
|
||||
>
|
||||
<slot />
|
||||
</DropdownMenuRadioGroup>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -27,15 +27,16 @@ const forwarded = useForwardPropsEmits(delegatedProps, emits);
|
|||
|
||||
<template>
|
||||
<DropdownMenuRadioItem
|
||||
data-slot="dropdown-menu-radio-item"
|
||||
v-bind="forwarded"
|
||||
:class="cn(
|
||||
'relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
|
||||
`focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4`,
|
||||
props.class,
|
||||
)"
|
||||
>
|
||||
<span class="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
|
||||
<span class="pointer-events-none absolute left-2 flex size-3.5 items-center justify-center">
|
||||
<DropdownMenuItemIndicator>
|
||||
<Circle class="h-2 w-2 fill-current" />
|
||||
<Circle class="size-2 fill-current" />
|
||||
</DropdownMenuItemIndicator>
|
||||
</span>
|
||||
<slot />
|
||||
|
|
|
|||
|
|
@ -20,5 +20,9 @@ const delegatedProps = computed(() => {
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<DropdownMenuSeparator v-bind="delegatedProps" :class="cn('-mx-1 my-1 h-px bg-muted', props.class)" />
|
||||
<DropdownMenuSeparator
|
||||
data-slot="dropdown-menu-separator"
|
||||
v-bind="delegatedProps"
|
||||
:class="cn('bg-border -mx-1 my-1 h-px', props.class)"
|
||||
/>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -8,7 +8,10 @@ const props = defineProps<{
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<span :class="cn('ml-auto text-xs tracking-widest opacity-60', props.class)">
|
||||
<span
|
||||
data-slot="dropdown-menu-shortcut"
|
||||
:class="cn('text-muted-foreground ml-auto text-xs tracking-widest', props.class)"
|
||||
>
|
||||
<slot />
|
||||
</span>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ const forwarded = useForwardPropsEmits(props, emits);
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<DropdownMenuSub v-bind="forwarded">
|
||||
<DropdownMenuSub data-slot="dropdown-menu-sub" v-bind="forwarded">
|
||||
<slot />
|
||||
</DropdownMenuSub>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -24,8 +24,9 @@ const forwarded = useForwardPropsEmits(delegatedProps, emits);
|
|||
|
||||
<template>
|
||||
<DropdownMenuSubContent
|
||||
data-slot="dropdown-menu-sub-content"
|
||||
v-bind="forwarded"
|
||||
:class="cn('z-50 min-w-32 overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-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', props.class)"
|
||||
:class="cn('bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-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 z-50 min-w-[8rem] origin-(--reka-dropdown-menu-content-transform-origin) overflow-hidden rounded-md border p-1 shadow-lg', props.class)"
|
||||
>
|
||||
<slot />
|
||||
</DropdownMenuSubContent>
|
||||
|
|
|
|||
|
|
@ -1,35 +1,35 @@
|
|||
<script setup lang="ts">
|
||||
import { cn } from "@/lib/utils";
|
||||
import { reactiveOmit } from "@vueuse/core";
|
||||
import { ChevronRight } from "lucide-vue-next";
|
||||
import {
|
||||
DropdownMenuSubTrigger,
|
||||
type DropdownMenuSubTriggerProps,
|
||||
useForwardProps,
|
||||
} from "reka-ui";
|
||||
import { type HTMLAttributes, computed } from "vue";
|
||||
import type { HTMLAttributes } from "vue";
|
||||
|
||||
const props = defineProps<
|
||||
DropdownMenuSubTriggerProps & { class?: HTMLAttributes["class"] }
|
||||
DropdownMenuSubTriggerProps & {
|
||||
class?: HTMLAttributes["class"];
|
||||
inset?: boolean;
|
||||
}
|
||||
>();
|
||||
|
||||
const delegatedProps = computed(() => {
|
||||
const { class: _, ...delegated } = props;
|
||||
|
||||
return delegated;
|
||||
});
|
||||
|
||||
const delegatedProps = reactiveOmit(props, "class", "inset");
|
||||
const forwardedProps = useForwardProps(delegatedProps);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<DropdownMenuSubTrigger
|
||||
data-slot="dropdown-menu-sub-trigger"
|
||||
v-bind="forwardedProps"
|
||||
:class="cn(
|
||||
'flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent data-[state=open]:bg-accent',
|
||||
'focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground flex cursor-default items-center rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[inset]:pl-8',
|
||||
props.class,
|
||||
)"
|
||||
>
|
||||
<slot />
|
||||
<ChevronRight class="ml-auto h-4 w-4" />
|
||||
<ChevronRight class="ml-auto size-4" />
|
||||
</DropdownMenuSubTrigger>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -11,7 +11,10 @@ const forwardedProps = useForwardProps(props);
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<DropdownMenuTrigger class="outline-none" v-bind="forwardedProps">
|
||||
<DropdownMenuTrigger
|
||||
data-slot="dropdown-menu-trigger"
|
||||
v-bind="forwardedProps"
|
||||
>
|
||||
<slot />
|
||||
</DropdownMenuTrigger>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ const { error, formItemId, formDescriptionId, formMessageId } = useFormField();
|
|||
<template>
|
||||
<Slot
|
||||
:id="formItemId"
|
||||
data-slot="form-control"
|
||||
:aria-describedby="!error ? `${formDescriptionId}` : `${formDescriptionId} ${formMessageId}`"
|
||||
:aria-invalid="!!error"
|
||||
>
|
||||
|
|
|
|||
|
|
@ -13,7 +13,8 @@ const { formDescriptionId } = useFormField();
|
|||
<template>
|
||||
<p
|
||||
:id="formDescriptionId"
|
||||
:class="cn('text-sm text-muted-foreground', props.class)"
|
||||
data-slot="form-description"
|
||||
:class="cn('text-muted-foreground text-sm', props.class)"
|
||||
>
|
||||
<slot />
|
||||
</p>
|
||||
|
|
|
|||
|
|
@ -13,7 +13,10 @@ provide(FORM_ITEM_INJECTION_KEY, id);
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<div :class="cn('space-y-2', props.class)">
|
||||
<div
|
||||
data-slot="form-item"
|
||||
:class="cn('grid gap-2', props.class)"
|
||||
>
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -12,8 +12,10 @@ const { error, formItemId } = useFormField();
|
|||
|
||||
<template>
|
||||
<Label
|
||||
data-slot="form-label"
|
||||
:data-error="!!error"
|
||||
:class="cn(
|
||||
error && 'text-destructive',
|
||||
'data-[error=true]:text-destructive-foreground',
|
||||
props.class,
|
||||
)"
|
||||
:for="formItemId"
|
||||
|
|
|
|||
|
|
@ -1,16 +1,22 @@
|
|||
<script lang="ts" setup>
|
||||
import { cn } from "@/lib/utils";
|
||||
import { ErrorMessage } from "vee-validate";
|
||||
import { toValue } from "vue";
|
||||
import { type HTMLAttributes, toValue } from "vue";
|
||||
import { useFormField } from "./useFormField";
|
||||
|
||||
const props = defineProps<{
|
||||
class?: HTMLAttributes["class"];
|
||||
}>();
|
||||
|
||||
const { name, formMessageId } = useFormField();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ErrorMessage
|
||||
:id="formMessageId"
|
||||
data-slot="form-message"
|
||||
as="p"
|
||||
:name="toValue(name)"
|
||||
class="text-sm font-medium text-destructive"
|
||||
:class="cn('text-destructive-foreground text-sm', props.class)"
|
||||
/>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -4,4 +4,8 @@ export { default as FormItem } from "./FormItem.vue";
|
|||
export { default as FormLabel } from "./FormLabel.vue";
|
||||
export { default as FormMessage } from "./FormMessage.vue";
|
||||
export { FORM_ITEM_INJECTION_KEY } from "./injectionKeys";
|
||||
export { Form, Field as FormField } from "vee-validate";
|
||||
export {
|
||||
Form,
|
||||
Field as FormField,
|
||||
FieldArray as FormFieldArray,
|
||||
} from "vee-validate";
|
||||
|
|
|
|||
|
|
@ -13,7 +13,10 @@ const forwarded = useForwardPropsEmits(props, emits);
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<HoverCardRoot v-bind="forwarded">
|
||||
<HoverCardRoot
|
||||
data-slot="hover-card"
|
||||
v-bind="forwarded"
|
||||
>
|
||||
<slot />
|
||||
</HoverCardRoot>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -27,10 +27,11 @@ const forwardedProps = useForwardProps(delegatedProps);
|
|||
<template>
|
||||
<HoverCardPortal>
|
||||
<HoverCardContent
|
||||
data-slot="hover-card-content"
|
||||
v-bind="forwardedProps"
|
||||
:class="
|
||||
cn(
|
||||
'z-50 w-64 rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-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',
|
||||
'bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-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 z-50 w-64 rounded-md border p-4 shadow-md outline-hidden',
|
||||
props.class,
|
||||
)
|
||||
"
|
||||
|
|
|
|||
|
|
@ -5,7 +5,10 @@ const props = defineProps<HoverCardTriggerProps>();
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<HoverCardTrigger v-bind="props">
|
||||
<HoverCardTrigger
|
||||
data-slot="hover-card-trigger"
|
||||
v-bind="props"
|
||||
>
|
||||
<slot />
|
||||
</HoverCardTrigger>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -19,5 +19,14 @@ const modelValue = useVModel(props, "modelValue", emits, {
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<input v-model="modelValue" :class="cn('flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50', props.class)">
|
||||
<input
|
||||
v-model="modelValue"
|
||||
data-slot="input"
|
||||
:class="cn(
|
||||
'file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 border-input flex h-9 w-full min-w-0 rounded-md border bg-transparent px-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm',
|
||||
'focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]',
|
||||
'aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive',
|
||||
props.class,
|
||||
)"
|
||||
>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -14,10 +14,11 @@ const delegatedProps = computed(() => {
|
|||
|
||||
<template>
|
||||
<Label
|
||||
data-slot="label"
|
||||
v-bind="delegatedProps"
|
||||
:class="
|
||||
cn(
|
||||
'text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70',
|
||||
'flex items-center gap-2 text-sm leading-none font-medium select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50',
|
||||
props.class,
|
||||
)
|
||||
"
|
||||
|
|
|
|||
|
|
@ -9,7 +9,10 @@ const forwarded = useForwardPropsEmits(props, emits);
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<PopoverRoot v-bind="forwarded">
|
||||
<PopoverRoot
|
||||
data-slot="popover"
|
||||
v-bind="forwarded"
|
||||
>
|
||||
<slot />
|
||||
</PopoverRoot>
|
||||
</template>
|
||||
|
|
|
|||
15
components/ui/popover/PopoverAnchor.vue
Normal file
15
components/ui/popover/PopoverAnchor.vue
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
<script setup lang="ts">
|
||||
import type { PopoverAnchorProps } from "reka-ui";
|
||||
import { PopoverAnchor } from "reka-ui";
|
||||
|
||||
const props = defineProps<PopoverAnchorProps>();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<PopoverAnchor
|
||||
data-slot="popover-anchor"
|
||||
v-bind="props"
|
||||
>
|
||||
<slot />
|
||||
</PopoverAnchor>
|
||||
</template>
|
||||
|
|
@ -34,10 +34,11 @@ const forwarded = useForwardPropsEmits(delegatedProps, emits);
|
|||
<template>
|
||||
<PopoverPortal>
|
||||
<PopoverContent
|
||||
data-slot="popover-content"
|
||||
v-bind="{ ...forwarded, ...$attrs }"
|
||||
:class="
|
||||
cn(
|
||||
'z-50 w-72 rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-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',
|
||||
'bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-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 z-50 w-72 rounded-md border p-4 shadow-md origin-(--reka-popover-content-transform-origin) outline-hidden',
|
||||
props.class,
|
||||
)
|
||||
"
|
||||
|
|
|
|||
|
|
@ -5,7 +5,10 @@ const props = defineProps<PopoverTriggerProps>();
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<PopoverTrigger v-bind="props">
|
||||
<PopoverTrigger
|
||||
data-slot="popover-trigger"
|
||||
v-bind="props"
|
||||
>
|
||||
<slot />
|
||||
</PopoverTrigger>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
export { default as Popover } from "./Popover.vue";
|
||||
export { default as PopoverAnchor } from "./PopoverAnchor.vue";
|
||||
export { default as PopoverContent } from "./PopoverContent.vue";
|
||||
export { default as PopoverTrigger } from "./PopoverTrigger.vue";
|
||||
|
|
|
|||
|
|
@ -9,7 +9,10 @@ const forwarded = useForwardPropsEmits(props, emits);
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<SelectRoot v-bind="forwarded">
|
||||
<slot />
|
||||
</SelectRoot>
|
||||
<SelectRoot
|
||||
data-slot="select"
|
||||
v-bind="forwarded"
|
||||
>
|
||||
<slot />
|
||||
</SelectRoot>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -35,8 +35,10 @@ const forwarded = useForwardPropsEmits(delegatedProps, emits);
|
|||
<template>
|
||||
<SelectPortal>
|
||||
<SelectContent
|
||||
v-bind="{ ...forwarded, ...$attrs }" :class="cn(
|
||||
'relative z-50 max-h-96 min-w-32 overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-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',
|
||||
data-slot="select-content"
|
||||
v-bind="{ ...forwarded, ...$attrs }"
|
||||
:class="cn(
|
||||
'bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-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 relative z-50 max-h-(--reka-select-content-available-height) min-w-[8rem] overflow-x-hidden overflow-y-auto rounded-md border shadow-md',
|
||||
position === 'popper'
|
||||
&& 'data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1',
|
||||
props.class,
|
||||
|
|
@ -44,7 +46,7 @@ const forwarded = useForwardPropsEmits(delegatedProps, emits);
|
|||
"
|
||||
>
|
||||
<SelectScrollUpButton />
|
||||
<SelectViewport :class="cn('p-1', position === 'popper' && 'h-[--reka-select-trigger-height] w-full min-w-[--reka-select-trigger-width]')">
|
||||
<SelectViewport :class="cn('p-1', position === 'popper' && 'h-[var(--reka-select-trigger-height)] w-full min-w-[var(--reka-select-trigger-width)] scroll-my-1')">
|
||||
<slot />
|
||||
</SelectViewport>
|
||||
<SelectScrollDownButton />
|
||||
|
|
|
|||
|
|
@ -1,21 +1,14 @@
|
|||
<script setup lang="ts">
|
||||
import { cn } from "@/lib/utils";
|
||||
import { SelectGroup, type SelectGroupProps } from "reka-ui";
|
||||
import { type HTMLAttributes, computed } from "vue";
|
||||
|
||||
const props = defineProps<
|
||||
SelectGroupProps & { class?: HTMLAttributes["class"] }
|
||||
>();
|
||||
|
||||
const delegatedProps = computed(() => {
|
||||
const { class: _, ...delegated } = props;
|
||||
|
||||
return delegated;
|
||||
});
|
||||
const props = defineProps<SelectGroupProps>();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<SelectGroup :class="cn('p-1 w-full', props.class)" v-bind="delegatedProps">
|
||||
<SelectGroup
|
||||
data-slot="select-group"
|
||||
v-bind="props"
|
||||
>
|
||||
<slot />
|
||||
</SelectGroup>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -24,25 +24,24 @@ const forwardedProps = useForwardProps(delegatedProps);
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<SelectItem
|
||||
v-bind="forwardedProps"
|
||||
:class="
|
||||
cn(
|
||||
'relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
|
||||
props.class
|
||||
)
|
||||
"
|
||||
>
|
||||
<span
|
||||
class="absolute left-2 flex h-3.5 w-3.5 items-center justify-center"
|
||||
>
|
||||
<SelectItemIndicator>
|
||||
<Check class="h-4 w-4" />
|
||||
</SelectItemIndicator>
|
||||
</span>
|
||||
<SelectItem
|
||||
data-slot="select-item"
|
||||
v-bind="forwardedProps"
|
||||
:class="
|
||||
cn(
|
||||
`focus:bg-accent focus:text-accent-foreground [&_svg:not([class*='text-'])]:text-muted-foreground relative flex w-full cursor-default items-center gap-2 rounded-sm py-1.5 pr-8 pl-2 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2`,
|
||||
props.class,
|
||||
)
|
||||
"
|
||||
>
|
||||
<span class="absolute right-2 flex size-3.5 items-center justify-center">
|
||||
<SelectItemIndicator>
|
||||
<Check class="size-4" />
|
||||
</SelectItemIndicator>
|
||||
</span>
|
||||
|
||||
<SelectItemText class="w-full">
|
||||
<slot />
|
||||
</SelectItemText>
|
||||
</SelectItem>
|
||||
<SelectItemText>
|
||||
<slot />
|
||||
</SelectItemText>
|
||||
</SelectItem>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -5,7 +5,10 @@ const props = defineProps<SelectItemTextProps>();
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<SelectItemText v-bind="props">
|
||||
<SelectItemText
|
||||
data-slot="select-item-text"
|
||||
v-bind="props"
|
||||
>
|
||||
<slot />
|
||||
</SelectItemText>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -9,7 +9,10 @@ const props = defineProps<
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<SelectLabel :class="cn('py-1.5 pl-8 pr-2 text-sm font-semibold', props.class)">
|
||||
<SelectLabel
|
||||
data-slot="select-label"
|
||||
:class="cn('px-2 py-1.5 text-sm font-medium', props.class)"
|
||||
>
|
||||
<slot />
|
||||
</SelectLabel>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -22,9 +22,13 @@ const forwardedProps = useForwardProps(delegatedProps);
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<SelectScrollDownButton v-bind="forwardedProps" :class="cn('flex cursor-default items-center justify-center py-1', props.class)">
|
||||
<SelectScrollDownButton
|
||||
data-slot="select-scroll-down-button"
|
||||
v-bind="forwardedProps"
|
||||
:class="cn('flex cursor-default items-center justify-center py-1', props.class)"
|
||||
>
|
||||
<slot>
|
||||
<ChevronDown class="h-4 w-4" />
|
||||
<ChevronDown class="size-4" />
|
||||
</slot>
|
||||
</SelectScrollDownButton>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -22,9 +22,13 @@ const forwardedProps = useForwardProps(delegatedProps);
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<SelectScrollUpButton v-bind="forwardedProps" :class="cn('flex cursor-default items-center justify-center py-1', props.class)">
|
||||
<SelectScrollUpButton
|
||||
data-slot="select-scroll-up-button"
|
||||
v-bind="forwardedProps"
|
||||
:class="cn('flex cursor-default items-center justify-center py-1', props.class)"
|
||||
>
|
||||
<slot>
|
||||
<ChevronUp class="h-4 w-4" />
|
||||
<ChevronUp class="size-4" />
|
||||
</slot>
|
||||
</SelectScrollUpButton>
|
||||
</template>
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue