init project

This commit is contained in:
caiyuchao
2025-05-16 14:52:30 +08:00
commit 1d6f7521c4
1496 changed files with 134863 additions and 0 deletions

View File

@@ -0,0 +1,16 @@
<script setup lang="ts">
import type { AccordionRootEmits, AccordionRootProps } from 'radix-vue';
import { AccordionRoot, useForwardPropsEmits } from 'radix-vue';
const props = defineProps<AccordionRootProps>();
const emits = defineEmits<AccordionRootEmits>();
const forwarded = useForwardPropsEmits(props, emits);
</script>
<template>
<AccordionRoot v-bind="forwarded">
<slot></slot>
</AccordionRoot>
</template>

View File

@@ -0,0 +1,28 @@
<script setup lang="ts">
import type { AccordionContentProps } from 'radix-vue';
import { computed } from 'vue';
import { cn } from '@vben-core/shared/utils';
import { AccordionContent } from 'radix-vue';
const props = defineProps<AccordionContentProps & { class?: any }>();
const delegatedProps = computed(() => {
const { class: _, ...delegated } = props;
return delegated;
});
</script>
<template>
<AccordionContent
v-bind="delegatedProps"
class="data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down overflow-hidden text-sm"
>
<div :class="cn('pb-4 pt-0', props.class)">
<slot></slot>
</div>
</AccordionContent>
</template>

View File

@@ -0,0 +1,25 @@
<script setup lang="ts">
import type { AccordionItemProps } from 'radix-vue';
import { computed } from 'vue';
import { cn } from '@vben-core/shared/utils';
import { AccordionItem, useForwardProps } from 'radix-vue';
const props = defineProps<AccordionItemProps & { class?: any }>();
const delegatedProps = computed(() => {
const { class: _, ...delegated } = props;
return delegated;
});
const forwardedProps = useForwardProps(delegatedProps);
</script>
<template>
<AccordionItem v-bind="forwardedProps" :class="cn('border-b', props.class)">
<slot></slot>
</AccordionItem>
</template>

View File

@@ -0,0 +1,39 @@
<script setup lang="ts">
import type { AccordionTriggerProps } from 'radix-vue';
import { computed } from 'vue';
import { cn } from '@vben-core/shared/utils';
import { ChevronDown } from 'lucide-vue-next';
import { AccordionHeader, AccordionTrigger } from 'radix-vue';
const props = defineProps<AccordionTriggerProps & { class?: any }>();
const delegatedProps = computed(() => {
const { class: _, ...delegated } = props;
return delegated;
});
</script>
<template>
<AccordionHeader class="flex">
<AccordionTrigger
v-bind="delegatedProps"
:class="
cn(
'flex flex-1 items-center justify-between py-4 text-sm font-medium transition-all hover:underline [&[data-state=open]>svg]:rotate-180',
props.class,
)
"
>
<slot></slot>
<slot name="icon">
<ChevronDown
class="text-muted-foreground h-4 w-4 shrink-0 transition-transform duration-200"
/>
</slot>
</AccordionTrigger>
</AccordionHeader>
</template>

View File

@@ -0,0 +1,4 @@
export { default as Accordion } from './Accordion.vue';
export { default as AccordionContent } from './AccordionContent.vue';
export { default as AccordionItem } from './AccordionItem.vue';
export { default as AccordionTrigger } from './AccordionTrigger.vue';

View File

@@ -0,0 +1,16 @@
<script setup lang="ts">
import type { AlertDialogEmits, AlertDialogProps } from 'radix-vue';
import { AlertDialogRoot, useForwardPropsEmits } from 'radix-vue';
const props = defineProps<AlertDialogProps>();
const emits = defineEmits<AlertDialogEmits>();
const forwarded = useForwardPropsEmits(props, emits);
</script>
<template>
<AlertDialogRoot v-bind="forwarded">
<slot></slot>
</AlertDialogRoot>
</template>

View File

@@ -0,0 +1,13 @@
<script setup lang="ts">
import type { AlertDialogActionProps } from 'radix-vue';
import { AlertDialogAction } from 'radix-vue';
const props = defineProps<AlertDialogActionProps>();
</script>
<template>
<AlertDialogAction v-bind="props">
<slot></slot>
</AlertDialogAction>
</template>

View File

@@ -0,0 +1,13 @@
<script setup lang="ts">
import type { AlertDialogCancelProps } from 'radix-vue';
import { AlertDialogCancel } from 'radix-vue';
const props = defineProps<AlertDialogCancelProps>();
</script>
<template>
<AlertDialogCancel v-bind="props">
<slot></slot>
</AlertDialogCancel>
</template>

View File

@@ -0,0 +1,101 @@
<script setup lang="ts">
import type {
AlertDialogContentEmits,
AlertDialogContentProps,
} from 'radix-vue';
import type { ClassType } from '@vben-core/typings';
import { computed, ref } from 'vue';
import { cn } from '@vben-core/shared/utils';
import {
AlertDialogContent,
AlertDialogPortal,
useForwardPropsEmits,
} from 'radix-vue';
import AlertDialogOverlay from './AlertDialogOverlay.vue';
const props = withDefaults(
defineProps<
AlertDialogContentProps & {
centered?: boolean;
class?: ClassType;
modal?: boolean;
open?: boolean;
overlayBlur?: number;
zIndex?: number;
}
>(),
{ modal: true },
);
const emits = defineEmits<
AlertDialogContentEmits & { close: []; closed: []; opened: [] }
>();
const delegatedProps = computed(() => {
const { class: _, modal: _modal, open: _open, ...delegated } = props;
return delegated;
});
const forwarded = useForwardPropsEmits(delegatedProps, emits);
const contentRef = ref<InstanceType<typeof AlertDialogContent> | null>(null);
function onAnimationEnd(event: AnimationEvent) {
// 只有在 contentRef 的动画结束时才触发 opened/closed 事件
if (event.target === contentRef.value?.$el) {
if (props.open) {
emits('opened');
} else {
emits('closed');
}
}
}
defineExpose({
getContentRef: () => contentRef.value,
});
</script>
<template>
<AlertDialogPortal>
<Transition name="fade" appear>
<AlertDialogOverlay
v-if="open && modal"
:style="{
...(zIndex ? { zIndex } : {}),
position: 'fixed',
backdropFilter:
overlayBlur && overlayBlur > 0 ? `blur(${overlayBlur}px)` : 'none',
}"
@click="() => emits('close')"
/>
</Transition>
<AlertDialogContent
ref="contentRef"
:style="{ ...(zIndex ? { zIndex } : {}), position: 'fixed' }"
@animationend="onAnimationEnd"
v-bind="forwarded"
:class="
cn(
'z-popup bg-background w-full p-6 shadow-lg outline-none sm:rounded-xl',
'data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95',
'data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95',
{
'data-[state=open]:slide-in-from-top-[48%] data-[state=closed]:slide-out-to-top-[48%]':
!centered,
'data-[state=open]:slide-in-from-top-[98%] data-[state=closed]:slide-out-to-top-[148%]':
centered,
'top-[10vh]': !centered,
'top-1/2 -translate-y-1/2': centered,
},
props.class,
)
"
>
<slot></slot>
</AlertDialogContent>
</AlertDialogPortal>
</template>

View File

@@ -0,0 +1,28 @@
<script lang="ts" setup>
import type { AlertDialogDescriptionProps } from 'radix-vue';
import { computed } from 'vue';
import { cn } from '@vben-core/shared/utils';
import { AlertDialogDescription, useForwardProps } from 'radix-vue';
const props = defineProps<AlertDialogDescriptionProps & { class?: any }>();
const delegatedProps = computed(() => {
const { class: _, ...delegated } = props;
return delegated;
});
const forwardedProps = useForwardProps(delegatedProps);
</script>
<template>
<AlertDialogDescription
v-bind="forwardedProps"
:class="cn('text-muted-foreground text-sm', props.class)"
>
<slot></slot>
</AlertDialogDescription>
</template>

View File

@@ -0,0 +1,8 @@
<script setup lang="ts">
import { useScrollLock } from '@vben-core/composables';
useScrollLock();
</script>
<template>
<div class="bg-overlay z-popup inset-0"></div>
</template>

View File

@@ -0,0 +1,30 @@
<script setup lang="ts">
import type { AlertDialogTitleProps } from 'radix-vue';
import { computed } from 'vue';
import { cn } from '@vben-core/shared/utils';
import { AlertDialogTitle, useForwardProps } from 'radix-vue';
const props = defineProps<AlertDialogTitleProps & { class?: any }>();
const delegatedProps = computed(() => {
const { class: _, ...delegated } = props;
return delegated;
});
const forwardedProps = useForwardProps(delegatedProps);
</script>
<template>
<AlertDialogTitle
v-bind="forwardedProps"
:class="
cn('text-lg font-semibold leading-none tracking-tight', props.class)
"
>
<slot></slot>
</AlertDialogTitle>
</template>

View File

@@ -0,0 +1,6 @@
export { default as AlertDialog } from './AlertDialog.vue';
export { default as AlertDialogAction } from './AlertDialogAction.vue';
export { default as AlertDialogCancel } from './AlertDialogCancel.vue';
export { default as AlertDialogContent } from './AlertDialogContent.vue';
export { default as AlertDialogDescription } from './AlertDialogDescription.vue';
export { default as AlertDialogTitle } from './AlertDialogTitle.vue';

View File

@@ -0,0 +1,27 @@
<script setup lang="ts">
import type { AvatarVariants } from './avatar';
import { cn } from '@vben-core/shared/utils';
import { AvatarRoot } from 'radix-vue';
import { avatarVariant } from './avatar';
const props = withDefaults(
defineProps<{
class?: any;
shape?: AvatarVariants['shape'];
size?: AvatarVariants['size'];
}>(),
{
shape: 'circle',
size: 'sm',
},
);
</script>
<template>
<AvatarRoot :class="cn(avatarVariant({ size, shape }), props.class)">
<slot></slot>
</AvatarRoot>
</template>

View File

@@ -0,0 +1,13 @@
<script setup lang="ts">
import type { AvatarFallbackProps } from 'radix-vue';
import { AvatarFallback } from 'radix-vue';
const props = defineProps<AvatarFallbackProps>();
</script>
<template>
<AvatarFallback v-bind="props">
<slot></slot>
</AvatarFallback>
</template>

View File

@@ -0,0 +1,11 @@
<script setup lang="ts">
import type { AvatarImageProps } from 'radix-vue';
import { AvatarImage } from 'radix-vue';
const props = defineProps<AvatarImageProps>();
</script>
<template>
<AvatarImage v-bind="props" class="h-full w-full object-cover" />
</template>

View File

@@ -0,0 +1,22 @@
import type { VariantProps } from 'class-variance-authority';
import { cva } from 'class-variance-authority';
export const avatarVariant = cva(
'inline-flex items-center justify-center font-normal text-foreground select-none shrink-0 bg-secondary overflow-hidden',
{
variants: {
shape: {
circle: 'rounded-full',
square: 'rounded-md',
},
size: {
base: 'h-16 w-16 text-2xl',
lg: 'h-32 w-32 text-5xl',
sm: 'h-10 w-10 text-xs',
},
},
},
);
export type AvatarVariants = VariantProps<typeof avatarVariant>;

View File

@@ -0,0 +1,4 @@
export * from './avatar';
export { default as Avatar } from './Avatar.vue';
export { default as AvatarFallback } from './AvatarFallback.vue';
export { default as AvatarImage } from './AvatarImage.vue';

View File

@@ -0,0 +1,18 @@
<script setup lang="ts">
import type { BadgeVariants } from './badge';
import { cn } from '@vben-core/shared/utils';
import { badgeVariants } from './badge';
const props = defineProps<{
class?: any;
variant?: BadgeVariants['variant'];
}>();
</script>
<template>
<div :class="cn(badgeVariants({ variant }), props.class)">
<slot></slot>
</div>
</template>

View File

@@ -0,0 +1,25 @@
import type { VariantProps } from 'class-variance-authority';
import { cva } from 'class-variance-authority';
export const badgeVariants = cva(
'inline-flex items-center rounded-md border border-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',
{
defaultVariants: {
variant: 'default',
},
variants: {
variant: {
default:
'border-transparent bg-accent hover:bg-accent text-primary-foreground shadow',
destructive:
'border-transparent bg-destructive text-destructive-foreground shadow hover:bg-destructive-hover',
outline: 'text-foreground',
secondary:
'border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80',
},
},
},
);
export type BadgeVariants = VariantProps<typeof badgeVariants>;

View File

@@ -0,0 +1,3 @@
export * from './badge';
export { default as Badge } from './Badge.vue';

View File

@@ -0,0 +1,11 @@
<script lang="ts" setup>
const props = defineProps<{
class?: any;
}>();
</script>
<template>
<nav :class="props.class" aria-label="breadcrumb" role="navigation">
<slot></slot>
</nav>
</template>

View File

@@ -0,0 +1,22 @@
<script lang="ts" setup>
import { cn } from '@vben-core/shared/utils';
import { MoreHorizontal } from 'lucide-vue-next';
const props = defineProps<{
class?: any;
}>();
</script>
<template>
<span
:class="cn('flex h-9 w-9 items-center justify-center', props.class)"
aria-hidden="true"
role="presentation"
>
<slot>
<MoreHorizontal class="h-4 w-4" />
</slot>
<span class="sr-only">More</span>
</span>
</template>

View File

@@ -0,0 +1,17 @@
<script lang="ts" setup>
import { cn } from '@vben-core/shared/utils';
const props = defineProps<{
class?: any;
}>();
</script>
<template>
<li
:class="
cn('hover:text-foreground inline-flex items-center gap-1.5', props.class)
"
>
<slot></slot>
</li>
</template>

View File

@@ -0,0 +1,21 @@
<script lang="ts" setup>
import type { PrimitiveProps } from 'radix-vue';
import { cn } from '@vben-core/shared/utils';
import { Primitive } from 'radix-vue';
const props = withDefaults(defineProps<PrimitiveProps & { class?: any }>(), {
as: 'a',
});
</script>
<template>
<Primitive
:as="as"
:as-child="asChild"
:class="cn('hover:text-foreground transition-colors', props.class)"
>
<slot></slot>
</Primitive>
</template>

View File

@@ -0,0 +1,20 @@
<script lang="ts" setup>
import { cn } from '@vben-core/shared/utils';
const props = defineProps<{
class?: any;
}>();
</script>
<template>
<ol
:class="
cn(
'text-muted-foreground flex flex-wrap items-center gap-1.5 break-words text-sm sm:gap-2.5',
props.class,
)
"
>
<slot></slot>
</ol>
</template>

View File

@@ -0,0 +1,18 @@
<script lang="ts" setup>
import { cn } from '@vben-core/shared/utils';
const props = defineProps<{
class?: any;
}>();
</script>
<template>
<span
:class="cn('text-foreground font-normal', props.class)"
aria-current="page"
aria-disabled="true"
role="link"
>
<slot></slot>
</span>
</template>

View File

@@ -0,0 +1,21 @@
<script lang="ts" setup>
import { cn } from '@vben-core/shared/utils';
import { ChevronRight } from 'lucide-vue-next';
const props = defineProps<{
class?: any;
}>();
</script>
<template>
<li
:class="cn('[&>svg]:size-3.5', props.class)"
aria-hidden="true"
role="presentation"
>
<slot>
<ChevronRight />
</slot>
</li>
</template>

View File

@@ -0,0 +1,7 @@
export { default as Breadcrumb } from './Breadcrumb.vue';
export { default as BreadcrumbEllipsis } from './BreadcrumbEllipsis.vue';
export { default as BreadcrumbItem } from './BreadcrumbItem.vue';
export { default as BreadcrumbLink } from './BreadcrumbLink.vue';
export { default as BreadcrumbList } from './BreadcrumbList.vue';
export { default as BreadcrumbPage } from './BreadcrumbPage.vue';
export { default as BreadcrumbSeparator } from './BreadcrumbSeparator.vue';

View File

@@ -0,0 +1,32 @@
<script setup lang="ts">
import type { PrimitiveProps } from 'radix-vue';
import type { ButtonVariants, ButtonVariantSize } from './types';
import { cn } from '@vben-core/shared/utils';
import { Primitive } from 'radix-vue';
import { buttonVariants } from './button';
interface Props extends PrimitiveProps {
class?: any;
size?: ButtonVariantSize;
variant?: ButtonVariants;
}
const props = withDefaults(defineProps<Props>(), {
as: 'button',
class: '',
});
</script>
<template>
<Primitive
:as="as"
:as-child="asChild"
:class="cn(buttonVariants({ variant, size }), props.class)"
>
<slot></slot>
</Primitive>
</template>

View File

@@ -0,0 +1,34 @@
import { cva } from 'class-variance-authority';
export const buttonVariants = cva(
'inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50',
{
defaultVariants: {
size: 'default',
variant: 'default',
},
variants: {
size: {
default: 'h-9 px-4 py-2',
icon: 'h-8 w-8 rounded-sm px-1 text-lg',
lg: 'h-10 rounded-md px-4',
sm: 'h-8 rounded-md px-2 text-xs',
xs: 'h-8 w-8 rounded-sm px-1 text-xs',
},
variant: {
default:
'bg-primary text-primary-foreground shadow hover:bg-primary/90',
destructive:
'bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive-hover',
ghost: 'hover:bg-accent hover:text-accent-foreground',
heavy: 'hover:bg-heavy hover:text-heavy-foreground',
icon: 'hover:bg-accent hover:text-accent-foreground text-foreground/80',
link: 'text-primary underline-offset-4 hover:underline',
outline:
'border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground',
secondary:
'bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80',
},
},
},
);

View File

@@ -0,0 +1,5 @@
export * from './button';
export { default as Button } from './Button.vue';
export type * from './types';

View File

@@ -0,0 +1,20 @@
export type ButtonVariantSize =
| 'default'
| 'icon'
| 'lg'
| 'sm'
| 'xs'
| null
| undefined;
export type ButtonVariants =
| 'default'
| 'destructive'
| 'ghost'
| 'heavy'
| 'icon'
| 'link'
| 'outline'
| 'secondary'
| null
| undefined;

View File

@@ -0,0 +1,20 @@
<script setup lang="ts">
import { cn } from '@vben-core/shared/utils';
const props = defineProps<{
class?: any;
}>();
</script>
<template>
<div
:class="
cn(
'bg-card text-card-foreground border-border rounded-xl border',
props.class,
)
"
>
<slot></slot>
</div>
</template>

View File

@@ -0,0 +1,13 @@
<script setup lang="ts">
import { cn } from '@vben-core/shared/utils';
const props = defineProps<{
class?: any;
}>();
</script>
<template>
<div :class="cn('p-6 pt-0', props.class)">
<slot></slot>
</div>
</template>

View File

@@ -0,0 +1,13 @@
<script setup lang="ts">
import { cn } from '@vben-core/shared/utils';
const props = defineProps<{
class?: any;
}>();
</script>
<template>
<p :class="cn('text-muted-foreground text-sm', props.class)">
<slot></slot>
</p>
</template>

View File

@@ -0,0 +1,13 @@
<script setup lang="ts">
import { cn } from '@vben-core/shared/utils';
const props = defineProps<{
class?: any;
}>();
</script>
<template>
<div :class="cn('flex items-center p-6 pt-0', props.class)">
<slot></slot>
</div>
</template>

View File

@@ -0,0 +1,13 @@
<script setup lang="ts">
import { cn } from '@vben-core/shared/utils';
const props = defineProps<{
class?: any;
}>();
</script>
<template>
<div :class="cn('flex flex-col gap-y-1.5 p-5', props.class)">
<slot></slot>
</div>
</template>

View File

@@ -0,0 +1,13 @@
<script setup lang="ts">
import { cn } from '@vben-core/shared/utils';
const props = defineProps<{
class?: any;
}>();
</script>
<template>
<h3 :class="cn('font-semibold leading-none tracking-tight', props.class)">
<slot></slot>
</h3>
</template>

View File

@@ -0,0 +1,6 @@
export { default as Card } from './Card.vue';
export { default as CardContent } from './CardContent.vue';
export { default as CardDescription } from './CardDescription.vue';
export { default as CardFooter } from './CardFooter.vue';
export { default as CardHeader } from './CardHeader.vue';
export { default as CardTitle } from './CardTitle.vue';

View File

@@ -0,0 +1,47 @@
<script setup lang="ts">
import type { CheckboxRootEmits, CheckboxRootProps } from 'radix-vue';
import { computed } from 'vue';
import { cn } from '@vben-core/shared/utils';
import { Check, Minus } from 'lucide-vue-next';
import {
CheckboxIndicator,
CheckboxRoot,
useForwardPropsEmits,
} from 'radix-vue';
const props = defineProps<
CheckboxRootProps & { class?: any; indeterminate?: boolean }
>();
const emits = defineEmits<CheckboxRootEmits>();
const delegatedProps = computed(() => {
const { class: _, ...delegated } = props;
return delegated;
});
const forwarded = useForwardPropsEmits(delegatedProps, emits);
</script>
<template>
<CheckboxRoot
v-bind="forwarded"
:class="
cn(
'focus-visible:ring-ring data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground border-border peer h-4 w-4 shrink-0 rounded-sm border transition focus-visible:outline-none focus-visible:ring-1 disabled:cursor-not-allowed disabled:opacity-50',
props.class,
)
"
>
<CheckboxIndicator
class="flex h-full w-full items-center justify-center text-current"
>
<slot>
<component :is="indeterminate ? Minus : Check" class="h-4 w-4" />
</slot>
</CheckboxIndicator>
</CheckboxRoot>
</template>

View File

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

View File

@@ -0,0 +1,18 @@
<script setup lang="ts">
import type { ContextMenuRootEmits, ContextMenuRootProps } from 'radix-vue';
import { ContextMenuRoot, useForwardPropsEmits } from 'radix-vue';
const props = withDefaults(defineProps<ContextMenuRootProps>(), {
modal: false,
});
const emits = defineEmits<ContextMenuRootEmits>();
const forwarded = useForwardPropsEmits(props, emits);
</script>
<template>
<ContextMenuRoot v-bind="forwarded">
<slot></slot>
</ContextMenuRoot>
</template>

View File

@@ -0,0 +1,47 @@
<script setup lang="ts">
import type {
ContextMenuCheckboxItemEmits,
ContextMenuCheckboxItemProps,
} from 'radix-vue';
import { computed } from 'vue';
import { cn } from '@vben-core/shared/utils';
import { Check } from 'lucide-vue-next';
import {
ContextMenuCheckboxItem,
ContextMenuItemIndicator,
useForwardPropsEmits,
} from 'radix-vue';
const props = defineProps<ContextMenuCheckboxItemProps & { class?: any }>();
const emits = defineEmits<ContextMenuCheckboxItemEmits>();
const delegatedProps = computed(() => {
const { class: _, ...delegated } = props;
return delegated;
});
const forwarded = useForwardPropsEmits(delegatedProps, emits);
</script>
<template>
<ContextMenuCheckboxItem
v-bind="forwarded"
:class="
cn(
'focus:bg-accent focus:text-accent-foreground relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none 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">
<ContextMenuItemIndicator>
<Check class="h-4 w-4" />
</ContextMenuItemIndicator>
</span>
<slot></slot>
</ContextMenuCheckboxItem>
</template>

View File

@@ -0,0 +1,43 @@
<script setup lang="ts">
import type {
ContextMenuContentEmits,
ContextMenuContentProps,
} from 'radix-vue';
import { computed } from 'vue';
import { cn } from '@vben-core/shared/utils';
import {
ContextMenuContent,
ContextMenuPortal,
useForwardPropsEmits,
} from 'radix-vue';
const props = defineProps<ContextMenuContentProps & { class?: any }>();
const emits = defineEmits<ContextMenuContentEmits>();
const delegatedProps = computed(() => {
const { class: _, ...delegated } = props;
return delegated;
});
const forwarded = useForwardPropsEmits(delegatedProps, emits);
</script>
<template>
<ContextMenuPortal>
<ContextMenuContent
v-bind="forwarded"
: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 border-border z-popup min-w-32 overflow-hidden rounded-md border p-1 shadow-md',
props.class,
)
"
>
<slot></slot>
</ContextMenuContent>
</ContextMenuPortal>
</template>

View File

@@ -0,0 +1,13 @@
<script setup lang="ts">
import type { ContextMenuGroupProps } from 'radix-vue';
import { ContextMenuGroup } from 'radix-vue';
const props = defineProps<ContextMenuGroupProps>();
</script>
<template>
<ContextMenuGroup v-bind="props">
<slot></slot>
</ContextMenuGroup>
</template>

View File

@@ -0,0 +1,37 @@
<script setup lang="ts">
import type { ContextMenuItemEmits, ContextMenuItemProps } from 'radix-vue';
import { computed } from 'vue';
import { cn } from '@vben-core/shared/utils';
import { ContextMenuItem, useForwardPropsEmits } from 'radix-vue';
const props = defineProps<
ContextMenuItemProps & { class?: any; inset?: boolean }
>();
const emits = defineEmits<ContextMenuItemEmits>();
const delegatedProps = computed(() => {
const { class: _, ...delegated } = props;
return delegated;
});
const forwarded = useForwardPropsEmits(delegatedProps, emits);
</script>
<template>
<ContextMenuItem
v-bind="forwarded"
:class="
cn(
'focus:bg-accent focus:text-accent-foreground relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
inset && 'pl-8',
props.class,
)
"
>
<slot></slot>
</ContextMenuItem>
</template>

View File

@@ -0,0 +1,34 @@
<script setup lang="ts">
import type { ContextMenuLabelProps } from 'radix-vue';
import { computed } from 'vue';
import { cn } from '@vben-core/shared/utils';
import { ContextMenuLabel } from 'radix-vue';
const props = defineProps<
ContextMenuLabelProps & { class?: any; inset?: boolean }
>();
const delegatedProps = computed(() => {
const { class: _, ...delegated } = props;
return delegated;
});
</script>
<template>
<ContextMenuLabel
v-bind="delegatedProps"
:class="
cn(
'text-foreground px-2 py-1.5 text-sm font-semibold',
inset && 'pl-8',
props.class,
)
"
>
<slot></slot>
</ContextMenuLabel>
</template>

View File

@@ -0,0 +1,13 @@
<script setup lang="ts">
import type { ContextMenuPortalProps } from 'radix-vue';
import { ContextMenuPortal } from 'radix-vue';
const props = defineProps<ContextMenuPortalProps>();
</script>
<template>
<ContextMenuPortal v-bind="props">
<slot></slot>
</ContextMenuPortal>
</template>

View File

@@ -0,0 +1,19 @@
<script setup lang="ts">
import type {
ContextMenuRadioGroupEmits,
ContextMenuRadioGroupProps,
} from 'radix-vue';
import { ContextMenuRadioGroup, useForwardPropsEmits } from 'radix-vue';
const props = defineProps<ContextMenuRadioGroupProps>();
const emits = defineEmits<ContextMenuRadioGroupEmits>();
const forwarded = useForwardPropsEmits(props, emits);
</script>
<template>
<ContextMenuRadioGroup v-bind="forwarded">
<slot></slot>
</ContextMenuRadioGroup>
</template>

View File

@@ -0,0 +1,47 @@
<script setup lang="ts">
import type {
ContextMenuRadioItemEmits,
ContextMenuRadioItemProps,
} from 'radix-vue';
import { computed } from 'vue';
import { cn } from '@vben-core/shared/utils';
import { Circle } from 'lucide-vue-next';
import {
ContextMenuItemIndicator,
ContextMenuRadioItem,
useForwardPropsEmits,
} from 'radix-vue';
const props = defineProps<ContextMenuRadioItemProps & { class?: any }>();
const emits = defineEmits<ContextMenuRadioItemEmits>();
const delegatedProps = computed(() => {
const { class: _, ...delegated } = props;
return delegated;
});
const forwarded = useForwardPropsEmits(delegatedProps, emits);
</script>
<template>
<ContextMenuRadioItem
v-bind="forwarded"
:class="
cn(
'focus:bg-accent focus:text-accent-foreground relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none 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">
<ContextMenuItemIndicator>
<Circle class="h-2 w-2 fill-current" />
</ContextMenuItemIndicator>
</span>
<slot></slot>
</ContextMenuRadioItem>
</template>

View File

@@ -0,0 +1,24 @@
<script setup lang="ts">
import type { ContextMenuSeparatorProps } from 'radix-vue';
import { computed } from 'vue';
import { cn } from '@vben-core/shared/utils';
import { ContextMenuSeparator } from 'radix-vue';
const props = defineProps<ContextMenuSeparatorProps & { class?: any }>();
const delegatedProps = computed(() => {
const { class: _, ...delegated } = props;
return delegated;
});
</script>
<template>
<ContextMenuSeparator
v-bind="delegatedProps"
:class="cn('bg-border -mx-1 my-1 h-px', props.class)"
/>
</template>

View File

@@ -0,0 +1,17 @@
<script setup lang="ts">
import { cn } from '@vben-core/shared/utils';
const props = defineProps<{
class?: any;
}>();
</script>
<template>
<span
:class="
cn('text-muted-foreground ml-auto text-xs tracking-widest', props.class)
"
>
<slot></slot>
</span>
</template>

View File

@@ -0,0 +1,16 @@
<script setup lang="ts">
import type { ContextMenuSubEmits, ContextMenuSubProps } from 'radix-vue';
import { ContextMenuSub, useForwardPropsEmits } from 'radix-vue';
const props = defineProps<ContextMenuSubProps>();
const emits = defineEmits<ContextMenuSubEmits>();
const forwarded = useForwardPropsEmits(props, emits);
</script>
<template>
<ContextMenuSub v-bind="forwarded">
<slot></slot>
</ContextMenuSub>
</template>

View File

@@ -0,0 +1,37 @@
<script setup lang="ts">
import type {
DropdownMenuSubContentEmits,
DropdownMenuSubContentProps,
} from 'radix-vue';
import { computed } from 'vue';
import { cn } from '@vben-core/shared/utils';
import { ContextMenuSubContent, useForwardPropsEmits } from 'radix-vue';
const props = defineProps<DropdownMenuSubContentProps & { class?: any }>();
const emits = defineEmits<DropdownMenuSubContentEmits>();
const delegatedProps = computed(() => {
const { class: _, ...delegated } = props;
return delegated;
});
const forwarded = useForwardPropsEmits(delegatedProps, emits);
</script>
<template>
<ContextMenuSubContent
v-bind="forwarded"
: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 border-border z-50 min-w-32 overflow-hidden rounded-md border p-1 shadow-lg',
props.class,
)
"
>
<slot></slot>
</ContextMenuSubContent>
</template>

View File

@@ -0,0 +1,41 @@
<script setup lang="ts">
import type { ContextMenuSubTriggerProps } from 'radix-vue';
import { computed } from 'vue';
import { cn } from '@vben-core/shared/utils';
import { ChevronRight } from 'lucide-vue-next';
import { ContextMenuSubTrigger, useForwardProps } from 'radix-vue';
const props = defineProps<
ContextMenuSubTriggerProps & {
class?: any;
inset?: boolean;
}
>();
const delegatedProps = computed(() => {
const { class: _, ...delegated } = props;
return delegated;
});
const forwardedProps = useForwardProps(delegatedProps);
</script>
<template>
<ContextMenuSubTrigger
v-bind="forwardedProps"
:class="
cn(
'focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none',
inset && 'pl-8',
props.class,
)
"
>
<slot></slot>
<ChevronRight class="ml-auto h-4 w-4" />
</ContextMenuSubTrigger>
</template>

View File

@@ -0,0 +1,15 @@
<script setup lang="ts">
import type { ContextMenuTriggerProps } from 'radix-vue';
import { ContextMenuTrigger, useForwardProps } from 'radix-vue';
const props = defineProps<ContextMenuTriggerProps>();
const forwardedProps = useForwardProps(props);
</script>
<template>
<ContextMenuTrigger v-bind="forwardedProps">
<slot></slot>
</ContextMenuTrigger>
</template>

View File

@@ -0,0 +1,14 @@
export { default as ContextMenu } from './ContextMenu.vue';
export { default as ContextMenuCheckboxItem } from './ContextMenuCheckboxItem.vue';
export { default as ContextMenuContent } from './ContextMenuContent.vue';
export { default as ContextMenuGroup } from './ContextMenuGroup.vue';
export { default as ContextMenuItem } from './ContextMenuItem.vue';
export { default as ContextMenuLabel } from './ContextMenuLabel.vue';
export { default as ContextMenuRadioGroup } from './ContextMenuRadioGroup.vue';
export { default as ContextMenuRadioItem } from './ContextMenuRadioItem.vue';
export { default as ContextMenuSeparator } from './ContextMenuSeparator.vue';
export { default as ContextMenuShortcut } from './ContextMenuShortcut.vue';
export { default as ContextMenuSub } from './ContextMenuSub.vue';
export { default as ContextMenuSubContent } from './ContextMenuSubContent.vue';
export { default as ContextMenuSubTrigger } from './ContextMenuSubTrigger.vue';
export { default as ContextMenuTrigger } from './ContextMenuTrigger.vue';

View File

@@ -0,0 +1,16 @@
<script setup lang="ts">
import type { DialogRootEmits, DialogRootProps } from 'radix-vue';
import { DialogRoot, useForwardPropsEmits } from 'radix-vue';
const props = defineProps<DialogRootProps>();
const emits = defineEmits<DialogRootEmits>();
const forwarded = useForwardPropsEmits(props, emits);
</script>
<template>
<DialogRoot v-bind="forwarded">
<slot></slot>
</DialogRoot>
</template>

View File

@@ -0,0 +1,13 @@
<script setup lang="ts">
import type { DialogCloseProps } from 'radix-vue';
import { DialogClose } from 'radix-vue';
const props = defineProps<DialogCloseProps>();
</script>
<template>
<DialogClose v-bind="props">
<slot></slot>
</DialogClose>
</template>

View File

@@ -0,0 +1,125 @@
<script setup lang="ts">
import type { DialogContentEmits, DialogContentProps } from 'radix-vue';
import type { ClassType } from '@vben-core/typings';
import { computed, ref } from 'vue';
import { cn } from '@vben-core/shared/utils';
import { X } from 'lucide-vue-next';
import {
DialogClose,
DialogContent,
DialogPortal,
useForwardPropsEmits,
} from 'radix-vue';
import DialogOverlay from './DialogOverlay.vue';
const props = withDefaults(
defineProps<
DialogContentProps & {
appendTo?: HTMLElement | string;
class?: ClassType;
closeClass?: ClassType;
closeDisabled?: boolean;
modal?: boolean;
open?: boolean;
overlayBlur?: number;
showClose?: boolean;
zIndex?: number;
}
>(),
{ appendTo: 'body', closeDisabled: false, showClose: true },
);
const emits = defineEmits<
DialogContentEmits & { close: []; closed: []; opened: [] }
>();
const delegatedProps = computed(() => {
const {
class: _,
modal: _modal,
open: _open,
showClose: __,
...delegated
} = props;
return delegated;
});
function isAppendToBody() {
return (
props.appendTo === 'body' ||
props.appendTo === document.body ||
!props.appendTo
);
}
const position = computed(() => {
return isAppendToBody() ? 'fixed' : 'absolute';
});
const forwarded = useForwardPropsEmits(delegatedProps, emits);
const contentRef = ref<InstanceType<typeof DialogContent> | null>(null);
function onAnimationEnd(event: AnimationEvent) {
// 只有在 contentRef 的动画结束时才触发 opened/closed 事件
if (event.target === contentRef.value?.$el) {
if (props.open) {
emits('opened');
} else {
emits('closed');
}
}
}
defineExpose({
getContentRef: () => contentRef.value,
});
</script>
<template>
<DialogPortal :to="appendTo">
<Transition name="fade">
<DialogOverlay
v-if="open && modal"
:style="{
...(zIndex ? { zIndex } : {}),
position,
backdropFilter:
overlayBlur && overlayBlur > 0 ? `blur(${overlayBlur}px)` : 'none',
}"
@click="() => emits('close')"
/>
</Transition>
<DialogContent
ref="contentRef"
:style="{ ...(zIndex ? { zIndex } : {}), position }"
@animationend="onAnimationEnd"
v-bind="forwarded"
:class="
cn(
'z-popup 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 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-top-[48%] w-full p-6 shadow-lg outline-none sm:rounded-xl',
props.class,
)
"
>
<slot></slot>
<DialogClose
v-if="showClose"
:disabled="closeDisabled"
:class="
cn(
'data-[state=open]:bg-accent data-[state=open]:text-muted-foreground hover:bg-accent hover:text-accent-foreground text-foreground/80 flex-center absolute right-3 top-3 h-6 w-6 rounded-full px-1 text-lg opacity-70 transition-opacity hover:opacity-100 focus:outline-none disabled:pointer-events-none',
props.closeClass,
)
"
@click="() => emits('close')"
>
<X class="h-4 w-4" />
</DialogClose>
</DialogContent>
</DialogPortal>
</template>

View File

@@ -0,0 +1,28 @@
<script setup lang="ts">
import type { DialogDescriptionProps } from 'radix-vue';
import { computed } from 'vue';
import { cn } from '@vben-core/shared/utils';
import { DialogDescription, useForwardProps } from 'radix-vue';
const props = defineProps<DialogDescriptionProps & { class?: any }>();
const delegatedProps = computed(() => {
const { class: _, ...delegated } = props;
return delegated;
});
const forwardedProps = useForwardProps(delegatedProps);
</script>
<template>
<DialogDescription
v-bind="forwardedProps"
:class="cn('text-muted-foreground text-sm', props.class)"
>
<slot></slot>
</DialogDescription>
</template>

View File

@@ -0,0 +1,15 @@
<script setup lang="ts">
import { cn } from '@vben-core/shared/utils';
const props = defineProps<{ class?: any }>();
</script>
<template>
<div
:class="
cn('flex flex-row flex-col-reverse justify-end gap-x-2', props.class)
"
>
<slot></slot>
</div>
</template>

View File

@@ -0,0 +1,15 @@
<script setup lang="ts">
import { cn } from '@vben-core/shared/utils';
const props = defineProps<{
class?: any;
}>();
</script>
<template>
<div
:class="cn('flex flex-col gap-y-1.5 text-center sm:text-left', props.class)"
>
<slot></slot>
</div>
</template>

View File

@@ -0,0 +1,11 @@
<script setup lang="ts">
import { inject } from 'vue';
import { useScrollLock } from '@vben-core/composables';
useScrollLock();
const id = inject('DISMISSABLE_MODAL_ID');
</script>
<template>
<div :data-dismissable-modal="id" class="bg-overlay z-popup inset-0"></div>
</template>

View File

@@ -0,0 +1,71 @@
<script setup lang="ts">
import type { DialogContentEmits, DialogContentProps } from 'radix-vue';
import { computed } from 'vue';
import { cn } from '@vben-core/shared/utils';
import { X } from 'lucide-vue-next';
import {
DialogClose,
DialogContent,
DialogOverlay,
DialogPortal,
useForwardPropsEmits,
} from 'radix-vue';
const props = withDefaults(
defineProps<DialogContentProps & { class?: any; zIndex?: number }>(),
{ zIndex: 1000 },
);
const emits = defineEmits<DialogContentEmits>();
const delegatedProps = computed(() => {
const { class: _, ...delegated } = props;
return delegated;
});
const forwarded = useForwardPropsEmits(delegatedProps, emits);
</script>
<template>
<DialogPortal>
<DialogOverlay
:style="{ zIndex }"
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 absolute inset-0 grid place-items-center overflow-y-auto border bg-black/80"
>
<DialogContent
:class="
cn(
'border-border bg-background relative z-50 my-8 grid w-full max-w-lg gap-4 border p-6 shadow-lg duration-200 sm:rounded-lg md:w-full',
props.class,
)
"
:style="{ zIndex }"
v-bind="forwarded"
@pointer-down-outside="
(event) => {
const originalEvent = event.detail.originalEvent;
const target = originalEvent.target as HTMLElement;
if (
originalEvent.offsetX > target.clientWidth ||
originalEvent.offsetY > target.clientHeight
) {
event.preventDefault();
}
}
"
>
<slot></slot>
<DialogClose
class="hover:bg-secondary absolute right-4 top-4 rounded-md p-0.5 transition-colors"
>
<X class="h-4 w-4" />
<span class="sr-only">Close</span>
</DialogClose>
</DialogContent>
</DialogOverlay>
</DialogPortal>
</template>

View File

@@ -0,0 +1,30 @@
<script setup lang="ts">
import type { DialogTitleProps } from 'radix-vue';
import { computed } from 'vue';
import { cn } from '@vben-core/shared/utils';
import { DialogTitle, useForwardProps } from 'radix-vue';
const props = defineProps<DialogTitleProps & { class?: any }>();
const delegatedProps = computed(() => {
const { class: _, ...delegated } = props;
return delegated;
});
const forwardedProps = useForwardProps(delegatedProps);
</script>
<template>
<DialogTitle
v-bind="forwardedProps"
:class="
cn('text-lg font-semibold leading-none tracking-tight', props.class)
"
>
<slot></slot>
</DialogTitle>
</template>

View File

@@ -0,0 +1,13 @@
<script setup lang="ts">
import type { DialogTriggerProps } from 'radix-vue';
import { DialogTrigger } from 'radix-vue';
const props = defineProps<DialogTriggerProps>();
</script>
<template>
<DialogTrigger v-bind="props">
<slot></slot>
</DialogTrigger>
</template>

View File

@@ -0,0 +1,9 @@
export { default as Dialog } from './Dialog.vue';
export { default as DialogClose } from './DialogClose.vue';
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 DialogScrollContent } from './DialogScrollContent.vue';
export { default as DialogTitle } from './DialogTitle.vue';
export { default as DialogTrigger } from './DialogTrigger.vue';

View File

@@ -0,0 +1,18 @@
<script setup lang="ts">
import type { DropdownMenuRootEmits, DropdownMenuRootProps } from 'radix-vue';
import { DropdownMenuRoot, useForwardPropsEmits } from 'radix-vue';
const props = withDefaults(defineProps<DropdownMenuRootProps>(), {
modal: false,
});
const emits = defineEmits<DropdownMenuRootEmits>();
const forwarded = useForwardPropsEmits(props, emits);
</script>
<template>
<DropdownMenuRoot v-bind="forwarded">
<slot></slot>
</DropdownMenuRoot>
</template>

View File

@@ -0,0 +1,47 @@
<script setup lang="ts">
import type {
DropdownMenuCheckboxItemEmits,
DropdownMenuCheckboxItemProps,
} from 'radix-vue';
import { computed } from 'vue';
import { cn } from '@vben-core/shared/utils';
import { Check } from 'lucide-vue-next';
import {
DropdownMenuCheckboxItem,
DropdownMenuItemIndicator,
useForwardPropsEmits,
} from 'radix-vue';
const props = defineProps<DropdownMenuCheckboxItemProps & { class?: any }>();
const emits = defineEmits<DropdownMenuCheckboxItemEmits>();
const delegatedProps = computed(() => {
const { class: _, ...delegated } = props;
return delegated;
});
const forwarded = useForwardPropsEmits(delegatedProps, emits);
</script>
<template>
<DropdownMenuCheckboxItem
v-bind="forwarded"
:class="
cn(
'focus:bg-accent focus:text-accent-foreground relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors 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">
<DropdownMenuItemIndicator>
<Check class="h-4 w-4" />
</DropdownMenuItemIndicator>
</span>
<slot></slot>
</DropdownMenuCheckboxItem>
</template>

View File

@@ -0,0 +1,48 @@
<script setup lang="ts">
import type {
DropdownMenuContentEmits,
DropdownMenuContentProps,
} from 'radix-vue';
import { computed } from 'vue';
import { cn } from '@vben-core/shared/utils';
import {
DropdownMenuContent,
DropdownMenuPortal,
useForwardPropsEmits,
} from 'radix-vue';
const props = withDefaults(
defineProps<DropdownMenuContentProps & { class?: any }>(),
{
sideOffset: 4,
},
);
const emits = defineEmits<DropdownMenuContentEmits>();
const delegatedProps = computed(() => {
const { class: _, ...delegated } = props;
return delegated;
});
const forwarded = useForwardPropsEmits(delegatedProps, emits);
</script>
<template>
<DropdownMenuPortal>
<DropdownMenuContent
v-bind="forwarded"
: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 border-border z-popup min-w-32 overflow-hidden rounded-md border p-1 shadow-md',
props.class,
)
"
>
<slot></slot>
</DropdownMenuContent>
</DropdownMenuPortal>
</template>

View File

@@ -0,0 +1,13 @@
<script setup lang="ts">
import type { DropdownMenuGroupProps } from 'radix-vue';
import { DropdownMenuGroup } from 'radix-vue';
const props = defineProps<DropdownMenuGroupProps>();
</script>
<template>
<DropdownMenuGroup v-bind="props">
<slot></slot>
</DropdownMenuGroup>
</template>

View File

@@ -0,0 +1,36 @@
<script setup lang="ts">
import type { DropdownMenuItemProps } from 'radix-vue';
import { computed } from 'vue';
import { cn } from '@vben-core/shared/utils';
import { DropdownMenuItem, useForwardProps } from 'radix-vue';
const props = defineProps<
DropdownMenuItemProps & { class?: any; inset?: boolean }
>();
const delegatedProps = computed(() => {
const { class: _, ...delegated } = props;
return delegated;
});
const forwardedProps = useForwardProps(delegatedProps);
</script>
<template>
<DropdownMenuItem
v-bind="forwardedProps"
:class="
cn(
'focus:bg-accent focus:text-accent-foreground relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none transition-colors data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
inset && 'pl-8',
props.class,
)
"
>
<slot></slot>
</DropdownMenuItem>
</template>

View File

@@ -0,0 +1,32 @@
<script setup lang="ts">
import type { DropdownMenuLabelProps } from 'radix-vue';
import { computed } from 'vue';
import { cn } from '@vben-core/shared/utils';
import { DropdownMenuLabel, useForwardProps } from 'radix-vue';
const props = defineProps<
DropdownMenuLabelProps & { class?: any; inset?: boolean }
>();
const delegatedProps = computed(() => {
const { class: _, ...delegated } = props;
return delegated;
});
const forwardedProps = useForwardProps(delegatedProps);
</script>
<template>
<DropdownMenuLabel
v-bind="forwardedProps"
:class="
cn('px-2 py-1.5 text-sm font-semibold', inset && 'pl-8', props.class)
"
>
<slot></slot>
</DropdownMenuLabel>
</template>

View File

@@ -0,0 +1,19 @@
<script setup lang="ts">
import type {
DropdownMenuRadioGroupEmits,
DropdownMenuRadioGroupProps,
} from 'radix-vue';
import { DropdownMenuRadioGroup, useForwardPropsEmits } from 'radix-vue';
const props = defineProps<DropdownMenuRadioGroupProps>();
const emits = defineEmits<DropdownMenuRadioGroupEmits>();
const forwarded = useForwardPropsEmits(props, emits);
</script>
<template>
<DropdownMenuRadioGroup v-bind="forwarded">
<slot></slot>
</DropdownMenuRadioGroup>
</template>

View File

@@ -0,0 +1,48 @@
<script setup lang="ts">
import type {
DropdownMenuRadioItemEmits,
DropdownMenuRadioItemProps,
} from 'radix-vue';
import { computed } from 'vue';
import { cn } from '@vben-core/shared/utils';
import { Circle } from 'lucide-vue-next';
import {
DropdownMenuItemIndicator,
DropdownMenuRadioItem,
useForwardPropsEmits,
} from 'radix-vue';
const props = defineProps<DropdownMenuRadioItemProps & { class?: any }>();
const emits = defineEmits<DropdownMenuRadioItemEmits>();
const delegatedProps = computed(() => {
const { class: _, ...delegated } = props;
return delegated;
});
const forwarded = useForwardPropsEmits(delegatedProps, emits);
</script>
<template>
<DropdownMenuRadioItem
v-bind="forwarded"
:class="
cn(
'focus:bg-accent focus:text-accent-foreground relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors 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">
<DropdownMenuItemIndicator>
<Circle class="h-2 w-2 fill-current" />
</DropdownMenuItemIndicator>
</span>
<slot></slot>
</DropdownMenuRadioItem>
</template>

View File

@@ -0,0 +1,28 @@
<script setup lang="ts">
import type { DropdownMenuSeparatorProps } from 'radix-vue';
import { computed } from 'vue';
import { cn } from '@vben-core/shared/utils';
import { DropdownMenuSeparator } from 'radix-vue';
const props = defineProps<
DropdownMenuSeparatorProps & {
class?: any;
}
>();
const delegatedProps = computed(() => {
const { class: _, ...delegated } = props;
return delegated;
});
</script>
<template>
<DropdownMenuSeparator
v-bind="delegatedProps"
:class="cn('bg-border -mx-1 my-1 h-px', props.class)"
/>
</template>

View File

@@ -0,0 +1,13 @@
<script setup lang="ts">
import { cn } from '@vben-core/shared/utils';
const props = defineProps<{
class?: any;
}>();
</script>
<template>
<span :class="cn('ml-auto text-xs tracking-widest opacity-60', props.class)">
<slot></slot>
</span>
</template>

View File

@@ -0,0 +1,16 @@
<script setup lang="ts">
import type { DropdownMenuSubEmits, DropdownMenuSubProps } from 'radix-vue';
import { DropdownMenuSub, useForwardPropsEmits } from 'radix-vue';
const props = defineProps<DropdownMenuSubProps>();
const emits = defineEmits<DropdownMenuSubEmits>();
const forwarded = useForwardPropsEmits(props, emits);
</script>
<template>
<DropdownMenuSub v-bind="forwarded">
<slot></slot>
</DropdownMenuSub>
</template>

View File

@@ -0,0 +1,37 @@
<script setup lang="ts">
import type {
DropdownMenuSubContentEmits,
DropdownMenuSubContentProps,
} from 'radix-vue';
import { computed } from 'vue';
import { cn } from '@vben-core/shared/utils';
import { DropdownMenuSubContent, useForwardPropsEmits } from 'radix-vue';
const props = defineProps<DropdownMenuSubContentProps & { class?: any }>();
const emits = defineEmits<DropdownMenuSubContentEmits>();
const delegatedProps = computed(() => {
const { class: _, ...delegated } = props;
return delegated;
});
const forwarded = useForwardPropsEmits(delegatedProps, emits);
</script>
<template>
<DropdownMenuSubContent
v-bind="forwarded"
: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 border-border z-50 min-w-32 overflow-hidden rounded-md border p-1 shadow-lg',
props.class,
)
"
>
<slot></slot>
</DropdownMenuSubContent>
</template>

View File

@@ -0,0 +1,35 @@
<script setup lang="ts">
import type { DropdownMenuSubTriggerProps } from 'radix-vue';
import { computed } from 'vue';
import { cn } from '@vben-core/shared/utils';
import { ChevronRight } from 'lucide-vue-next';
import { DropdownMenuSubTrigger, useForwardProps } from 'radix-vue';
const props = defineProps<DropdownMenuSubTriggerProps & { class?: any }>();
const delegatedProps = computed(() => {
const { class: _, ...delegated } = props;
return delegated;
});
const forwardedProps = useForwardProps(delegatedProps);
</script>
<template>
<DropdownMenuSubTrigger
v-bind="forwardedProps"
:class="
cn(
'focus:bg-accent data-[state=open]:bg-accent flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none',
props.class,
)
"
>
<slot></slot>
<ChevronRight class="ml-auto h-4 w-4" />
</DropdownMenuSubTrigger>
</template>

View File

@@ -0,0 +1,15 @@
<script setup lang="ts">
import type { DropdownMenuTriggerProps } from 'radix-vue';
import { DropdownMenuTrigger, useForwardProps } from 'radix-vue';
const props = defineProps<DropdownMenuTriggerProps>();
const forwardedProps = useForwardProps(props);
</script>
<template>
<DropdownMenuTrigger class="outline-none" v-bind="forwardedProps">
<slot></slot>
</DropdownMenuTrigger>
</template>

View File

@@ -0,0 +1,16 @@
export { default as DropdownMenu } from './DropdownMenu.vue';
export { default as DropdownMenuCheckboxItem } from './DropdownMenuCheckboxItem.vue';
export { default as DropdownMenuContent } from './DropdownMenuContent.vue';
export { default as DropdownMenuGroup } from './DropdownMenuGroup.vue';
export { default as DropdownMenuItem } from './DropdownMenuItem.vue';
export { default as DropdownMenuLabel } from './DropdownMenuLabel.vue';
export { default as DropdownMenuRadioGroup } from './DropdownMenuRadioGroup.vue';
export { default as DropdownMenuRadioItem } from './DropdownMenuRadioItem.vue';
export { default as DropdownMenuSeparator } from './DropdownMenuSeparator.vue';
export { default as DropdownMenuShortcut } from './DropdownMenuShortcut.vue';
export { default as DropdownMenuSub } from './DropdownMenuSub.vue';
export { default as DropdownMenuSubContent } from './DropdownMenuSubContent.vue';
export { default as DropdownMenuSubTrigger } from './DropdownMenuSubTrigger.vue';
export { default as DropdownMenuTrigger } from './DropdownMenuTrigger.vue';
export { DropdownMenuPortal } from 'radix-vue';

View File

@@ -0,0 +1,19 @@
<script lang="ts" setup>
import { Slot } from 'radix-vue';
import { useFormField } from './useFormField';
const { error, formDescriptionId, formItemId, formMessageId } = useFormField();
</script>
<template>
<Slot
:id="formItemId"
:aria-describedby="
!error ? `${formDescriptionId}` : `${formDescriptionId} ${formMessageId}`
"
:aria-invalid="!!error"
>
<slot></slot>
</Slot>
</template>

View File

@@ -0,0 +1,20 @@
<script lang="ts" setup>
import { cn } from '@vben-core/shared/utils';
import { useFormField } from './useFormField';
const props = defineProps<{
class?: any;
}>();
const { formDescriptionId } = useFormField();
</script>
<template>
<p
:id="formDescriptionId"
:class="cn('text-muted-foreground text-sm', props.class)"
>
<slot></slot>
</p>
</template>

View File

@@ -0,0 +1,20 @@
<script lang="ts" setup>
import { provide, useId } from 'vue';
import { cn } from '@vben-core/shared/utils';
import { FORM_ITEM_INJECTION_KEY } from './injectionKeys';
const props = defineProps<{
class?: any;
}>();
const id = useId() as string;
provide(FORM_ITEM_INJECTION_KEY, id);
</script>
<template>
<div :class="cn(props.class)">
<slot></slot>
</div>
</template>

View File

@@ -0,0 +1,18 @@
<script lang="ts" setup>
import type { LabelProps } from 'radix-vue';
import { cn } from '@vben-core/shared/utils';
import { Label } from '../label';
import { useFormField } from './useFormField';
const props = defineProps<LabelProps & { class?: any }>();
const { formItemId } = useFormField();
</script>
<template>
<Label :class="cn(props.class)" :for="formItemId">
<slot></slot>
</Label>
</template>

View File

@@ -0,0 +1,18 @@
<script lang="ts" setup>
import { toValue } from 'vue';
import { ErrorMessage } from 'vee-validate';
import { useFormField } from './useFormField';
const { formMessageId, name } = useFormField();
</script>
<template>
<ErrorMessage
:id="formMessageId"
:name="toValue(name)"
as="p"
class="text-destructive text-[0.8rem]"
/>
</template>

View File

@@ -0,0 +1,11 @@
export { default as FormControl } from './FormControl.vue';
export { default as FormDescription } from './FormDescription.vue';
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,
FieldArray as FormFieldArray,
} from 'vee-validate';

View File

@@ -0,0 +1,4 @@
import type { InjectionKey } from 'vue';
// eslint-disable-next-line symbol-description
export const FORM_ITEM_INJECTION_KEY = Symbol() as InjectionKey<string>;

View File

@@ -0,0 +1,38 @@
import { inject } from 'vue';
import {
FieldContextKey,
useFieldError,
useIsFieldDirty,
useIsFieldTouched,
useIsFieldValid,
} from 'vee-validate';
import { FORM_ITEM_INJECTION_KEY } from './injectionKeys';
export function useFormField() {
const fieldContext = inject(FieldContextKey);
const fieldItemContext = inject(FORM_ITEM_INJECTION_KEY);
if (!fieldContext)
throw new Error('useFormField should be used within <FormField>');
const { name } = fieldContext;
const id = fieldItemContext;
const fieldState = {
error: useFieldError(name),
isDirty: useIsFieldDirty(name),
isTouched: useIsFieldTouched(name),
valid: useIsFieldValid(name),
};
return {
formDescriptionId: `${id}-form-item-description`,
formItemId: `${id}-form-item`,
formMessageId: `${id}-form-item-message`,
id,
name,
...fieldState,
};
}

View File

@@ -0,0 +1,16 @@
<script setup lang="ts">
import type { HoverCardRootEmits, HoverCardRootProps } from 'radix-vue';
import { HoverCardRoot, useForwardPropsEmits } from 'radix-vue';
const props = defineProps<HoverCardRootProps>();
const emits = defineEmits<HoverCardRootEmits>();
const forwarded = useForwardPropsEmits(props, emits);
</script>
<template>
<HoverCardRoot v-bind="forwarded">
<slot></slot>
</HoverCardRoot>
</template>

View File

@@ -0,0 +1,40 @@
<script setup lang="ts">
import type { HoverCardContentProps } from 'radix-vue';
import { computed } from 'vue';
import { cn } from '@vben-core/shared/utils';
import { HoverCardContent, HoverCardPortal, useForwardProps } from 'radix-vue';
const props = withDefaults(
defineProps<HoverCardContentProps & { class?: any }>(),
{
sideOffset: 4,
},
);
const delegatedProps = computed(() => {
const { class: _, ...delegated } = props;
return delegated;
});
const forwardedProps = useForwardProps(delegatedProps);
</script>
<template>
<HoverCardPortal>
<HoverCardContent
v-bind="forwardedProps"
: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 border-border z-popup w-64 rounded-md border p-4 shadow-md outline-none',
props.class,
)
"
>
<slot></slot>
</HoverCardContent>
</HoverCardPortal>
</template>

View File

@@ -0,0 +1,13 @@
<script setup lang="ts">
import type { HoverCardTriggerProps } from 'radix-vue';
import { HoverCardTrigger } from 'radix-vue';
const props = defineProps<HoverCardTriggerProps>();
</script>
<template>
<HoverCardTrigger v-bind="props">
<slot></slot>
</HoverCardTrigger>
</template>

View File

@@ -0,0 +1,3 @@
export { default as HoverCard } from './HoverCard.vue';
export { default as HoverCardContent } from './HoverCardContent.vue';
export { default as HoverCardTrigger } from './HoverCardTrigger.vue';

View File

@@ -0,0 +1,31 @@
export * from './accordion';
export * from './alert-dialog';
export * from './avatar';
export * from './badge';
export * from './breadcrumb';
export * from './button';
export * from './card';
export * from './checkbox';
export * from './dialog';
export * from './dropdown-menu';
export * from './form';
export * from './hover-card';
export * from './input';
export * from './label';
export * from './number-field';
export * from './pagination';
export * from './pin-input';
export * from './popover';
export * from './radio-group';
export * from './resizable';
export * from './scroll-area';
export * from './select';
export * from './separator';
export * from './sheet';
export * from './switch';
export * from './tabs';
export * from './textarea';
export * from './toggle';
export * from './toggle-group';
export * from './tooltip';
export * from './tree';

View File

@@ -0,0 +1,37 @@
<script setup lang="ts">
import { cn } from '@vben-core/shared/utils';
import { useVModel } from '@vueuse/core';
const props = defineProps<{
class?: any;
defaultValue?: number | string;
modelValue?: number | string;
}>();
const emits = defineEmits<{
(e: 'update:modelValue', payload: number | string): void;
}>();
const modelValue = useVModel(props, 'modelValue', emits, {
defaultValue: props.defaultValue,
passive: true,
});
</script>
<template>
<input
v-model="modelValue"
:class="
cn(
'border-input bg-background ring-offset-background placeholder:text-muted-foreground focus-visible:ring-ring flex h-10 w-full rounded-md border px-3 py-2 text-sm file:border-0 file:bg-transparent file:text-sm file:font-medium focus-visible:outline-none focus-visible:ring-1 disabled:cursor-not-allowed disabled:opacity-50',
props.class,
)
"
/>
</template>
<style lang="scss" scoped>
input {
--ring: var(--primary);
}
</style>

View File

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

View File

@@ -0,0 +1,31 @@
<script setup lang="ts">
import type { LabelProps } from 'radix-vue';
import { computed } from 'vue';
import { cn } from '@vben-core/shared/utils';
import { Label } from 'radix-vue';
const props = defineProps<LabelProps & { class?: any }>();
const delegatedProps = computed(() => {
const { class: _, ...delegated } = props;
return delegated;
});
</script>
<template>
<Label
v-bind="delegatedProps"
:class="
cn(
'text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70',
props.class,
)
"
>
<slot></slot>
</Label>
</template>

Some files were not shown because too many files have changed in this diff Show More