更新最新代码
This commit is contained in:
27
packages/@core/ui-kit/shadcn-ui/build.config.ts
Normal file
27
packages/@core/ui-kit/shadcn-ui/build.config.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import { defineBuildConfig } from 'unbuild';
|
||||
|
||||
export default defineBuildConfig({
|
||||
clean: true,
|
||||
declaration: true,
|
||||
entries: [
|
||||
{
|
||||
builder: 'mkdist',
|
||||
input: './src',
|
||||
|
||||
pattern: ['**/*'],
|
||||
},
|
||||
{
|
||||
builder: 'mkdist',
|
||||
input: './src',
|
||||
loaders: ['vue'],
|
||||
pattern: ['**/*.vue'],
|
||||
},
|
||||
{
|
||||
builder: 'mkdist',
|
||||
format: 'esm',
|
||||
input: './src',
|
||||
loaders: ['js'],
|
||||
pattern: ['**/*.ts'],
|
||||
},
|
||||
],
|
||||
});
|
||||
16
packages/@core/ui-kit/shadcn-ui/components.json
Normal file
16
packages/@core/ui-kit/shadcn-ui/components.json
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"$schema": "https://shadcn-vue.com/schema.json",
|
||||
"style": "new-york",
|
||||
"typescript": true,
|
||||
"tailwind": {
|
||||
"config": "tailwind.config.mjs",
|
||||
"css": "src/assets/index.css",
|
||||
"baseColor": "slate",
|
||||
"cssVariables": true
|
||||
},
|
||||
"framework": "vite",
|
||||
"aliases": {
|
||||
"components": "@vben-core/shadcn-ui/components",
|
||||
"utils": "@vben-core/shared/utils"
|
||||
}
|
||||
}
|
||||
1
packages/@core/ui-kit/shadcn-ui/postcss.config.mjs
Normal file
1
packages/@core/ui-kit/shadcn-ui/postcss.config.mjs
Normal file
@@ -0,0 +1 @@
|
||||
export { default } from '@vben/tailwind-config/postcss';
|
||||
@@ -0,0 +1 @@
|
||||
export { default as VbenAvatar } from './avatar.vue';
|
||||
@@ -0,0 +1,38 @@
|
||||
export const backtopProps = {
|
||||
/**
|
||||
* @zh_CN bottom distance.
|
||||
*/
|
||||
bottom: {
|
||||
default: 40,
|
||||
type: Number,
|
||||
},
|
||||
/**
|
||||
* @zh_CN right distance.
|
||||
*/
|
||||
right: {
|
||||
default: 40,
|
||||
type: Number,
|
||||
},
|
||||
/**
|
||||
* @zh_CN the target to trigger scroll.
|
||||
*/
|
||||
target: {
|
||||
default: '',
|
||||
type: String,
|
||||
},
|
||||
/**
|
||||
* @zh_CN the button will not show until the scroll height reaches this value.
|
||||
*/
|
||||
visibilityHeight: {
|
||||
default: 200,
|
||||
type: Number,
|
||||
},
|
||||
} as const;
|
||||
|
||||
export interface BacktopProps {
|
||||
bottom?: number;
|
||||
isGroup?: boolean;
|
||||
right?: number;
|
||||
target?: string;
|
||||
visibilityHeight?: number;
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export { default as VbenBackTop } from './back-top.vue';
|
||||
@@ -0,0 +1,45 @@
|
||||
import type { BacktopProps } from './backtop';
|
||||
|
||||
import { onMounted, ref, shallowRef } from 'vue';
|
||||
|
||||
import { useEventListener, useThrottleFn } from '@vueuse/core';
|
||||
|
||||
export const useBackTop = (props: BacktopProps) => {
|
||||
const el = shallowRef<HTMLElement>();
|
||||
const container = shallowRef<Document | HTMLElement>();
|
||||
const visible = ref(false);
|
||||
|
||||
const handleScroll = () => {
|
||||
if (el.value) {
|
||||
visible.value = el.value.scrollTop >= (props?.visibilityHeight ?? 0);
|
||||
}
|
||||
};
|
||||
|
||||
const handleClick = () => {
|
||||
el.value?.scrollTo({ behavior: 'smooth', top: 0 });
|
||||
};
|
||||
|
||||
const handleScrollThrottled = useThrottleFn(handleScroll, 300, true);
|
||||
|
||||
useEventListener(container, 'scroll', handleScrollThrottled);
|
||||
onMounted(() => {
|
||||
container.value = document;
|
||||
el.value = document.documentElement;
|
||||
|
||||
if (props.target) {
|
||||
el.value = document.querySelector<HTMLElement>(props.target) ?? undefined;
|
||||
|
||||
if (!el.value) {
|
||||
throw new Error(`target does not exist: ${props.target}`);
|
||||
}
|
||||
container.value = el.value;
|
||||
}
|
||||
// Give visible an initial value, fix #13066
|
||||
handleScroll();
|
||||
});
|
||||
|
||||
return {
|
||||
handleClick,
|
||||
visible,
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,98 @@
|
||||
<script lang="ts" setup>
|
||||
import type { BreadcrumbProps } from './types';
|
||||
|
||||
import { ChevronDown } from '@vben-core/icons';
|
||||
|
||||
import {
|
||||
Breadcrumb,
|
||||
BreadcrumbItem,
|
||||
BreadcrumbLink,
|
||||
BreadcrumbList,
|
||||
BreadcrumbPage,
|
||||
BreadcrumbSeparator,
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuTrigger,
|
||||
} from '../../ui';
|
||||
import { VbenIcon } from '../icon';
|
||||
|
||||
interface Props extends BreadcrumbProps {}
|
||||
|
||||
defineOptions({ name: 'Breadcrumb' });
|
||||
withDefaults(defineProps<Props>(), {
|
||||
showIcon: false,
|
||||
});
|
||||
|
||||
const emit = defineEmits<{ select: [string] }>();
|
||||
|
||||
function handleClick(path?: string) {
|
||||
if (!path) {
|
||||
return;
|
||||
}
|
||||
emit('select', path);
|
||||
}
|
||||
</script>
|
||||
<template>
|
||||
<Breadcrumb>
|
||||
<BreadcrumbList>
|
||||
<TransitionGroup name="breadcrumb-transition">
|
||||
<template
|
||||
v-for="(item, index) in breadcrumbs"
|
||||
:key="`${item.path}-${item.title}-${index}`"
|
||||
>
|
||||
<BreadcrumbItem>
|
||||
<div v-if="item.items?.length ?? 0 > 0">
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger class="flex items-center gap-1">
|
||||
<VbenIcon v-if="showIcon" :icon="item.icon" class="size-5" />
|
||||
{{ item.title }}
|
||||
<ChevronDown class="size-4" />
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="start">
|
||||
<template
|
||||
v-for="menuItem in item.items"
|
||||
:key="`sub-${menuItem.path}`"
|
||||
>
|
||||
<DropdownMenuItem @click.stop="handleClick(menuItem.path)">
|
||||
{{ menuItem.title }}
|
||||
</DropdownMenuItem>
|
||||
</template>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</div>
|
||||
<BreadcrumbLink
|
||||
v-else-if="index !== breadcrumbs.length - 1"
|
||||
href="javascript:void 0"
|
||||
@click.stop="handleClick(item.path)"
|
||||
>
|
||||
<div class="flex-center">
|
||||
<VbenIcon
|
||||
v-if="showIcon"
|
||||
:class="{ 'size-5': item.isHome }"
|
||||
:icon="item.icon"
|
||||
class="mr-1 size-4"
|
||||
/>
|
||||
{{ item.title }}
|
||||
</div>
|
||||
</BreadcrumbLink>
|
||||
<BreadcrumbPage v-else>
|
||||
<div class="flex-center">
|
||||
<VbenIcon
|
||||
v-if="showIcon"
|
||||
:class="{ 'size-5': item.isHome }"
|
||||
:icon="item.icon"
|
||||
class="mr-1 size-4"
|
||||
/>
|
||||
{{ item.title }}
|
||||
</div>
|
||||
</BreadcrumbPage>
|
||||
<BreadcrumbSeparator
|
||||
v-if="index < breadcrumbs.length - 1 && !item.isHome"
|
||||
/>
|
||||
</BreadcrumbItem>
|
||||
</template>
|
||||
</TransitionGroup>
|
||||
</BreadcrumbList>
|
||||
</Breadcrumb>
|
||||
</template>
|
||||
@@ -0,0 +1,3 @@
|
||||
export { default as VbenBreadcrumbView } from './breadcrumb-view.vue';
|
||||
|
||||
export type * from './types';
|
||||
@@ -0,0 +1,17 @@
|
||||
import type { Component } from 'vue';
|
||||
|
||||
import type { BreadcrumbStyleType } from '@vben-core/typings';
|
||||
|
||||
export interface IBreadcrumb {
|
||||
icon?: Component | string;
|
||||
isHome?: boolean;
|
||||
items?: IBreadcrumb[];
|
||||
path?: string;
|
||||
title?: string;
|
||||
}
|
||||
|
||||
export interface BreadcrumbProps {
|
||||
breadcrumbs: IBreadcrumb[];
|
||||
showIcon?: boolean;
|
||||
styleType?: BreadcrumbStyleType;
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
<script lang="ts" setup>
|
||||
import { cn } from '@vben-core/shared/utils';
|
||||
|
||||
defineOptions({ name: 'VbenButtonGroup' });
|
||||
|
||||
withDefaults(
|
||||
defineProps<{
|
||||
border?: boolean;
|
||||
gap?: number;
|
||||
size?: 'large' | 'middle' | 'small';
|
||||
}>(),
|
||||
{ border: false, gap: 0, size: 'middle' },
|
||||
);
|
||||
</script>
|
||||
<template>
|
||||
<div
|
||||
:class="
|
||||
cn(
|
||||
'vben-button-group rounded-md',
|
||||
`size-${size}`,
|
||||
gap ? 'with-gap' : 'no-gap',
|
||||
$attrs.class as string,
|
||||
)
|
||||
"
|
||||
:style="{ gap: gap ? `${gap}px` : '0px' }"
|
||||
>
|
||||
<slot></slot>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.vben-button-group {
|
||||
display: inline-flex;
|
||||
|
||||
&.size-large :deep(button) {
|
||||
height: 2.25rem;
|
||||
padding: 0.5rem 0.75rem;
|
||||
font-size: 0.875rem;
|
||||
line-height: 1.25rem;
|
||||
|
||||
.icon-wrapper {
|
||||
margin-right: 0.4rem;
|
||||
|
||||
svg {
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.size-middle :deep(button) {
|
||||
height: 2rem;
|
||||
padding: 0.25rem 0.5rem;
|
||||
font-size: 0.75rem;
|
||||
line-height: 1rem;
|
||||
|
||||
.icon-wrapper {
|
||||
margin-right: 0.2rem;
|
||||
|
||||
svg {
|
||||
width: 0.75rem;
|
||||
height: 0.75rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.size-small :deep(button) {
|
||||
height: 1.75rem;
|
||||
padding: 0.2rem 0.4rem;
|
||||
font-size: 0.65rem;
|
||||
line-height: 0.75rem;
|
||||
|
||||
.icon-wrapper {
|
||||
margin-right: 0.1rem;
|
||||
|
||||
svg {
|
||||
width: 0.65rem;
|
||||
height: 0.65rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.no-gap > :deep(button):nth-of-type(1) {
|
||||
border-radius: calc(var(--radius) - 2px) 0 0 calc(var(--radius) - 2px);
|
||||
}
|
||||
|
||||
&.no-gap > :deep(button):last-of-type {
|
||||
border-radius: 0 calc(var(--radius) - 2px) calc(var(--radius) - 2px) 0;
|
||||
}
|
||||
|
||||
&.no-gap {
|
||||
:deep(button + button) {
|
||||
border-left-width: 0;
|
||||
border-radius: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,196 @@
|
||||
<script lang="ts" setup>
|
||||
import type { Arrayable } from '@vueuse/core';
|
||||
|
||||
import type { ValueType, VbenButtonGroupProps } from './button';
|
||||
|
||||
import { computed, ref, watch } from 'vue';
|
||||
|
||||
import { Circle, CircleCheckBig, LoaderCircle } from '@vben-core/icons';
|
||||
import { cn, isFunction } from '@vben-core/shared/utils';
|
||||
|
||||
import { objectOmit } from '@vueuse/core';
|
||||
|
||||
import { VbenRenderContent } from '../render-content';
|
||||
import VbenButtonGroup from './button-group.vue';
|
||||
import Button from './button.vue';
|
||||
|
||||
const props = withDefaults(defineProps<VbenButtonGroupProps>(), {
|
||||
gap: 0,
|
||||
multiple: false,
|
||||
showIcon: true,
|
||||
size: 'middle',
|
||||
allowClear: false,
|
||||
maxCount: 0,
|
||||
});
|
||||
const emit = defineEmits(['btnClick']);
|
||||
const btnDefaultProps = computed(() => {
|
||||
return {
|
||||
...objectOmit(props, ['options', 'btnClass', 'size', 'disabled']),
|
||||
class: cn(props.btnClass),
|
||||
};
|
||||
});
|
||||
const modelValue = defineModel<Arrayable<ValueType> | undefined>();
|
||||
|
||||
const innerValue = ref<Array<ValueType>>([]);
|
||||
const loadingValues = ref<Array<ValueType>>([]);
|
||||
watch(
|
||||
() => props.multiple,
|
||||
(val) => {
|
||||
if (val) {
|
||||
modelValue.value = innerValue.value;
|
||||
} else {
|
||||
modelValue.value =
|
||||
innerValue.value.length > 0 ? innerValue.value[0] : undefined;
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
watch(
|
||||
() => modelValue.value,
|
||||
(val) => {
|
||||
if (Array.isArray(val)) {
|
||||
const arrVal = val.filter((v) => v !== undefined);
|
||||
if (arrVal.length > 0) {
|
||||
innerValue.value = props.multiple
|
||||
? [...arrVal]
|
||||
: [arrVal[0] as ValueType];
|
||||
} else {
|
||||
innerValue.value = [];
|
||||
}
|
||||
} else {
|
||||
innerValue.value = val === undefined ? [] : [val as ValueType];
|
||||
}
|
||||
},
|
||||
{ deep: true, immediate: true },
|
||||
);
|
||||
|
||||
async function onBtnClick(value: ValueType) {
|
||||
if (props.beforeChange && isFunction(props.beforeChange)) {
|
||||
try {
|
||||
loadingValues.value.push(value);
|
||||
const canChange = await props.beforeChange(
|
||||
value,
|
||||
!innerValue.value.includes(value),
|
||||
);
|
||||
if (canChange === false) {
|
||||
return;
|
||||
}
|
||||
} finally {
|
||||
loadingValues.value.splice(loadingValues.value.indexOf(value), 1);
|
||||
}
|
||||
}
|
||||
|
||||
if (props.multiple) {
|
||||
if (innerValue.value.includes(value)) {
|
||||
innerValue.value = innerValue.value.filter((item) => item !== value);
|
||||
} else {
|
||||
if (props.maxCount > 0 && innerValue.value.length >= props.maxCount) {
|
||||
innerValue.value = innerValue.value.slice(0, props.maxCount - 1);
|
||||
}
|
||||
innerValue.value.push(value);
|
||||
}
|
||||
modelValue.value = innerValue.value;
|
||||
} else {
|
||||
if (props.allowClear && innerValue.value.includes(value)) {
|
||||
innerValue.value = [];
|
||||
modelValue.value = undefined;
|
||||
emit('btnClick', undefined);
|
||||
return;
|
||||
} else {
|
||||
innerValue.value = [value];
|
||||
modelValue.value = value;
|
||||
}
|
||||
}
|
||||
emit('btnClick', value);
|
||||
}
|
||||
</script>
|
||||
<template>
|
||||
<VbenButtonGroup
|
||||
:size="props.size"
|
||||
:gap="props.gap"
|
||||
class="vben-check-button-group"
|
||||
>
|
||||
<Button
|
||||
v-for="(btn, index) in props.options"
|
||||
:key="index"
|
||||
:class="cn('border', props.btnClass)"
|
||||
:disabled="
|
||||
props.disabled ||
|
||||
loadingValues.includes(btn.value) ||
|
||||
(!props.multiple && loadingValues.length > 0)
|
||||
"
|
||||
v-bind="btnDefaultProps"
|
||||
:variant="innerValue.includes(btn.value) ? 'default' : 'outline'"
|
||||
@click="onBtnClick(btn.value)"
|
||||
type="button"
|
||||
>
|
||||
<div class="icon-wrapper" v-if="props.showIcon">
|
||||
<slot
|
||||
name="icon"
|
||||
:loading="loadingValues.includes(btn.value)"
|
||||
:checked="innerValue.includes(btn.value)"
|
||||
>
|
||||
<LoaderCircle
|
||||
class="animate-spin"
|
||||
v-if="loadingValues.includes(btn.value)"
|
||||
/>
|
||||
<CircleCheckBig v-else-if="innerValue.includes(btn.value)" />
|
||||
<Circle v-else />
|
||||
</slot>
|
||||
</div>
|
||||
<slot name="option" :label="btn.label" :value="btn.value" :data="btn">
|
||||
<VbenRenderContent :content="btn.label" />
|
||||
</slot>
|
||||
</Button>
|
||||
</VbenButtonGroup>
|
||||
</template>
|
||||
<style lang="scss" scoped>
|
||||
.vben-check-button-group {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
|
||||
&:deep(.size-large) button {
|
||||
.icon-wrapper {
|
||||
margin-right: 0.3rem;
|
||||
|
||||
svg {
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&:deep(.size-middle) button {
|
||||
.icon-wrapper {
|
||||
margin-right: 0.2rem;
|
||||
|
||||
svg {
|
||||
width: 0.75rem;
|
||||
height: 0.75rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&:deep(.size-small) button {
|
||||
.icon-wrapper {
|
||||
margin-right: 0.1rem;
|
||||
|
||||
svg {
|
||||
width: 0.65rem;
|
||||
height: 0.65rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.no-gap > :deep(button):nth-of-type(1) {
|
||||
border-right-width: 0;
|
||||
}
|
||||
|
||||
&.no-gap {
|
||||
:deep(button + button) {
|
||||
margin-right: -1px;
|
||||
border-left-width: 1px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,68 @@
|
||||
<script setup lang="ts">
|
||||
import type { ButtonVariants } from '../../ui';
|
||||
import type { VbenButtonProps } from './button';
|
||||
|
||||
import { computed, useSlots } from 'vue';
|
||||
|
||||
import { cn } from '@vben-core/shared/utils';
|
||||
|
||||
import { VbenTooltip } from '../tooltip';
|
||||
import VbenButton from './button.vue';
|
||||
|
||||
interface Props extends VbenButtonProps {
|
||||
class?: any;
|
||||
disabled?: boolean;
|
||||
onClick?: () => void;
|
||||
tooltip?: string;
|
||||
tooltipDelayDuration?: number;
|
||||
tooltipSide?: 'bottom' | 'left' | 'right' | 'top';
|
||||
variant?: ButtonVariants;
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
disabled: false,
|
||||
onClick: () => {},
|
||||
tooltipDelayDuration: 200,
|
||||
tooltipSide: 'bottom',
|
||||
variant: 'icon',
|
||||
});
|
||||
|
||||
const slots = useSlots();
|
||||
|
||||
const showTooltip = computed(() => !!slots.tooltip || !!props.tooltip);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<VbenButton
|
||||
v-if="!showTooltip"
|
||||
:class="cn('rounded-full', props.class)"
|
||||
:disabled="disabled"
|
||||
:variant="variant"
|
||||
size="icon"
|
||||
@click="onClick"
|
||||
>
|
||||
<slot></slot>
|
||||
</VbenButton>
|
||||
|
||||
<VbenTooltip
|
||||
v-else
|
||||
:delay-duration="tooltipDelayDuration"
|
||||
:side="tooltipSide"
|
||||
>
|
||||
<template #trigger>
|
||||
<VbenButton
|
||||
:class="cn('rounded-full', props.class)"
|
||||
:disabled="disabled"
|
||||
:variant="variant"
|
||||
size="icon"
|
||||
@click="onClick"
|
||||
>
|
||||
<slot></slot>
|
||||
</VbenButton>
|
||||
</template>
|
||||
<slot v-if="slots.tooltip" name="tooltip"> </slot>
|
||||
<template v-else>
|
||||
{{ tooltip }}
|
||||
</template>
|
||||
</VbenTooltip>
|
||||
</template>
|
||||
@@ -0,0 +1,5 @@
|
||||
export type * from './button';
|
||||
export { default as VbenButtonGroup } from './button-group.vue';
|
||||
export { default as VbenButton } from './button.vue';
|
||||
export { default as VbenCheckButtonGroup } from './check-button-group.vue';
|
||||
export { default as VbenIconButton } from './icon-button.vue';
|
||||
@@ -0,0 +1 @@
|
||||
export { default as VbenCheckbox } from './checkbox.vue';
|
||||
@@ -0,0 +1,3 @@
|
||||
export { default as VbenContextMenu } from './context-menu.vue';
|
||||
|
||||
export type * from './interface';
|
||||
@@ -0,0 +1,38 @@
|
||||
import type { Component } from 'vue';
|
||||
|
||||
interface IContextMenuItem {
|
||||
/**
|
||||
* @zh_CN 是否禁用
|
||||
*/
|
||||
disabled?: boolean;
|
||||
/**
|
||||
* @zh_CN 点击事件处理
|
||||
* @param data
|
||||
*/
|
||||
handler?: (data: any) => void;
|
||||
/**
|
||||
* @zh_CN 图标
|
||||
*/
|
||||
icon?: Component;
|
||||
/**
|
||||
* @zh_CN 是否显示图标
|
||||
*/
|
||||
inset?: boolean;
|
||||
/**
|
||||
* @zh_CN 唯一标识
|
||||
*/
|
||||
key: string;
|
||||
/**
|
||||
* @zh_CN 是否是分割线
|
||||
*/
|
||||
separator?: boolean;
|
||||
/**
|
||||
* @zh_CN 快捷键
|
||||
*/
|
||||
shortcut?: string;
|
||||
/**
|
||||
* @zh_CN 标题
|
||||
*/
|
||||
text: string;
|
||||
}
|
||||
export type { IContextMenuItem };
|
||||
@@ -0,0 +1,128 @@
|
||||
<script lang="ts" setup>
|
||||
import { computed, onMounted, ref, unref, watch, watchEffect } from 'vue';
|
||||
|
||||
import { isNumber } from '@vben-core/shared/utils';
|
||||
|
||||
import { TransitionPresets, useTransition } from '@vueuse/core';
|
||||
|
||||
interface Props {
|
||||
autoplay?: boolean;
|
||||
color?: string;
|
||||
decimal?: string;
|
||||
decimals?: number;
|
||||
duration?: number;
|
||||
endVal?: number;
|
||||
prefix?: string;
|
||||
separator?: string;
|
||||
startVal?: number;
|
||||
suffix?: string;
|
||||
transition?: keyof typeof TransitionPresets;
|
||||
useEasing?: boolean;
|
||||
}
|
||||
|
||||
defineOptions({ name: 'CountToAnimator' });
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
autoplay: true,
|
||||
color: '',
|
||||
decimal: '.',
|
||||
decimals: 0,
|
||||
duration: 1500,
|
||||
endVal: 2021,
|
||||
prefix: '',
|
||||
separator: ',',
|
||||
startVal: 0,
|
||||
suffix: '',
|
||||
transition: 'linear',
|
||||
useEasing: true,
|
||||
});
|
||||
|
||||
const emit = defineEmits<{
|
||||
finished: [];
|
||||
/**
|
||||
* @deprecated 请使用{@link finished}事件
|
||||
*/
|
||||
onFinished: [];
|
||||
/**
|
||||
* @deprecated 请使用{@link started}事件
|
||||
*/
|
||||
onStarted: [];
|
||||
started: [];
|
||||
}>();
|
||||
|
||||
const source = ref(props.startVal);
|
||||
const disabled = ref(false);
|
||||
let outputValue = useTransition(source);
|
||||
|
||||
const value = computed(() => formatNumber(unref(outputValue)));
|
||||
|
||||
watchEffect(() => {
|
||||
source.value = props.startVal;
|
||||
});
|
||||
|
||||
watch([() => props.startVal, () => props.endVal], () => {
|
||||
if (props.autoplay) {
|
||||
start();
|
||||
}
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
props.autoplay && start();
|
||||
});
|
||||
|
||||
function start() {
|
||||
run();
|
||||
source.value = props.endVal;
|
||||
}
|
||||
|
||||
function reset() {
|
||||
source.value = props.startVal;
|
||||
run();
|
||||
}
|
||||
|
||||
function run() {
|
||||
outputValue = useTransition(source, {
|
||||
disabled,
|
||||
duration: props.duration,
|
||||
onFinished: () => {
|
||||
emit('finished');
|
||||
emit('onFinished');
|
||||
},
|
||||
onStarted: () => {
|
||||
emit('started');
|
||||
emit('onStarted');
|
||||
},
|
||||
...(props.useEasing
|
||||
? { transition: TransitionPresets[props.transition] }
|
||||
: {}),
|
||||
});
|
||||
}
|
||||
|
||||
function formatNumber(num: number | string) {
|
||||
if (!num && num !== 0) {
|
||||
return '';
|
||||
}
|
||||
const { decimal, decimals, prefix, separator, suffix } = props;
|
||||
num = Number(num).toFixed(decimals);
|
||||
num += '';
|
||||
|
||||
const x = num.split('.');
|
||||
let x1 = x[0];
|
||||
const x2 = x.length > 1 ? decimal + x[1] : '';
|
||||
|
||||
const rgx = /(\d+)(\d{3})/;
|
||||
if (separator && !isNumber(separator) && x1) {
|
||||
while (rgx.test(x1)) {
|
||||
x1 = x1.replace(rgx, `$1${separator}$2`);
|
||||
}
|
||||
}
|
||||
return prefix + x1 + x2 + suffix;
|
||||
}
|
||||
|
||||
defineExpose({ reset });
|
||||
</script>
|
||||
<template>
|
||||
<span :style="{ color }">
|
||||
{{ value }}
|
||||
</span>
|
||||
</template>
|
||||
@@ -0,0 +1 @@
|
||||
export { default as VbenCountToAnimator } from './count-to-animator.vue';
|
||||
@@ -0,0 +1,4 @@
|
||||
export { default as VbenDropdownMenu } from './dropdown-menu.vue';
|
||||
export { default as VbenDropdownRadioMenu } from './dropdown-radio-menu.vue';
|
||||
|
||||
export type * from './interface';
|
||||
@@ -0,0 +1,32 @@
|
||||
import type { Component } from 'vue';
|
||||
|
||||
interface VbenDropdownMenuItem {
|
||||
disabled?: boolean;
|
||||
/**
|
||||
* @zh_CN 点击事件处理
|
||||
* @param data
|
||||
*/
|
||||
handler?: (data: any) => void;
|
||||
/**
|
||||
* @zh_CN 图标
|
||||
*/
|
||||
icon?: Component;
|
||||
/**
|
||||
* @zh_CN 标题
|
||||
*/
|
||||
label: string;
|
||||
/**
|
||||
* @zh_CN 是否是分割线
|
||||
*/
|
||||
separator?: boolean;
|
||||
/**
|
||||
* @zh_CN 唯一标识
|
||||
*/
|
||||
value: string;
|
||||
}
|
||||
|
||||
interface DropdownMenuProps {
|
||||
menus: VbenDropdownMenuItem[];
|
||||
}
|
||||
|
||||
export type { DropdownMenuProps, VbenDropdownMenuItem };
|
||||
@@ -0,0 +1,31 @@
|
||||
<script lang="ts" setup>
|
||||
import { ChevronDown } from '@vben-core/icons';
|
||||
import { cn } from '@vben-core/shared/utils';
|
||||
|
||||
const props = defineProps<{
|
||||
class?: string;
|
||||
}>();
|
||||
|
||||
// 控制箭头展开/收起状态
|
||||
const collapsed = defineModel({ default: false });
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
:class="cn('vben-link inline-flex items-center', props.class)"
|
||||
@click="collapsed = !collapsed"
|
||||
>
|
||||
<slot :is-expanded="collapsed">
|
||||
{{ collapsed }}
|
||||
<!-- <span>{{ isExpanded ? '收起' : '展开' }}</span> -->
|
||||
</slot>
|
||||
<div
|
||||
:class="{ 'rotate-180': !collapsed }"
|
||||
class="transition-transform duration-300"
|
||||
>
|
||||
<slot name="icon">
|
||||
<ChevronDown class="size-4" />
|
||||
</slot>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -0,0 +1 @@
|
||||
export { default as VbenExpandableArrow } from './expandable-arrow.vue';
|
||||
@@ -0,0 +1 @@
|
||||
export { default as VbenFullScreen } from './full-screen.vue';
|
||||
35
packages/@core/ui-kit/shadcn-ui/src/components/icon/icon.vue
Normal file
35
packages/@core/ui-kit/shadcn-ui/src/components/icon/icon.vue
Normal file
@@ -0,0 +1,35 @@
|
||||
<script setup lang="ts">
|
||||
import type { Component } from 'vue';
|
||||
|
||||
import { computed } from 'vue';
|
||||
|
||||
import { IconDefault, IconifyIcon } from '@vben-core/icons';
|
||||
import {
|
||||
isFunction,
|
||||
isHttpUrl,
|
||||
isObject,
|
||||
isString,
|
||||
} from '@vben-core/shared/utils';
|
||||
|
||||
const props = defineProps<{
|
||||
// 没有是否显示默认图标
|
||||
fallback?: boolean;
|
||||
icon?: Component | Function | string;
|
||||
}>();
|
||||
|
||||
const isRemoteIcon = computed(() => {
|
||||
return isString(props.icon) && isHttpUrl(props.icon);
|
||||
});
|
||||
|
||||
const isComponent = computed(() => {
|
||||
const { icon } = props;
|
||||
return !isString(icon) && (isObject(icon) || isFunction(icon));
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<component :is="icon as Component" v-if="isComponent" v-bind="$attrs" />
|
||||
<img v-else-if="isRemoteIcon" :src="icon as string" v-bind="$attrs" />
|
||||
<IconifyIcon v-else-if="icon" v-bind="$attrs" :icon="icon as string" />
|
||||
<IconDefault v-else-if="fallback" v-bind="$attrs" />
|
||||
</template>
|
||||
@@ -0,0 +1 @@
|
||||
export { default as VbenIcon } from './icon.vue';
|
||||
23
packages/@core/ui-kit/shadcn-ui/src/components/index.ts
Normal file
23
packages/@core/ui-kit/shadcn-ui/src/components/index.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
export * from './avatar';
|
||||
export * from './back-top';
|
||||
export * from './breadcrumb';
|
||||
export * from './button';
|
||||
export * from './checkbox';
|
||||
export * from './context-menu';
|
||||
export * from './count-to-animator';
|
||||
export * from './dropdown-menu';
|
||||
export * from './expandable-arrow';
|
||||
export * from './full-screen';
|
||||
export * from './hover-card';
|
||||
export * from './icon';
|
||||
export * from './input-password';
|
||||
export * from './logo';
|
||||
export * from './pin-input';
|
||||
export * from './popover';
|
||||
export * from './render-content';
|
||||
export * from './scrollbar';
|
||||
export * from './segmented';
|
||||
export * from './select';
|
||||
export * from './spine-text';
|
||||
export * from './spinner';
|
||||
export * from './tooltip';
|
||||
@@ -0,0 +1 @@
|
||||
export { default as VbenInputPassword } from './input-password.vue';
|
||||
@@ -0,0 +1 @@
|
||||
export { default as VbenLogo } from './logo.vue';
|
||||
@@ -0,0 +1,3 @@
|
||||
export { default as VbenPinInput } from './input.vue';
|
||||
|
||||
export type * from './types';
|
||||
@@ -0,0 +1,30 @@
|
||||
interface PinInputProps {
|
||||
class?: any;
|
||||
/**
|
||||
* 验证码长度
|
||||
*/
|
||||
codeLength?: number;
|
||||
/**
|
||||
* 发送验证码按钮文本
|
||||
*/
|
||||
createText?: (countdown: number) => string;
|
||||
/**
|
||||
* 是否禁用
|
||||
*/
|
||||
disabled?: boolean;
|
||||
/**
|
||||
* 自定义验证码发送逻辑
|
||||
* @returns
|
||||
*/
|
||||
handleSendCode?: () => Promise<void>;
|
||||
/**
|
||||
* 发送验证码按钮loading
|
||||
*/
|
||||
loading?: boolean;
|
||||
/**
|
||||
* 最大重试时间
|
||||
*/
|
||||
maxTime?: number;
|
||||
}
|
||||
|
||||
export type { PinInputProps };
|
||||
@@ -0,0 +1 @@
|
||||
export { default as VbenPopover } from './popover.vue';
|
||||
@@ -0,0 +1 @@
|
||||
export { default as VbenRenderContent } from './render-content.vue';
|
||||
@@ -0,0 +1,56 @@
|
||||
<script lang="ts">
|
||||
import type { Component, PropType } from 'vue';
|
||||
|
||||
import { defineComponent, h } from 'vue';
|
||||
|
||||
import { isFunction, isObject, isString } from '@vben-core/shared/utils';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'RenderContent',
|
||||
props: {
|
||||
content: {
|
||||
default: undefined as
|
||||
| PropType<(() => any) | Component | string>
|
||||
| undefined,
|
||||
type: [Object, String, Function],
|
||||
},
|
||||
renderBr: {
|
||||
default: false,
|
||||
type: Boolean,
|
||||
},
|
||||
},
|
||||
setup(props, { attrs, slots }) {
|
||||
return () => {
|
||||
if (!props.content) {
|
||||
return null;
|
||||
}
|
||||
const isComponent =
|
||||
(isObject(props.content) || isFunction(props.content)) &&
|
||||
props.content !== null;
|
||||
if (!isComponent) {
|
||||
if (props.renderBr && isString(props.content)) {
|
||||
const lines = props.content.split('\n');
|
||||
const result = [];
|
||||
for (const [i, line] of lines.entries()) {
|
||||
result.push(h('p', { key: i }, line));
|
||||
// if (i < lines.length - 1) {
|
||||
// result.push(h('br'));
|
||||
// }
|
||||
}
|
||||
return result;
|
||||
} else {
|
||||
return props.content;
|
||||
}
|
||||
}
|
||||
return h(props.content as never, {
|
||||
...attrs,
|
||||
props: {
|
||||
...props,
|
||||
...attrs,
|
||||
},
|
||||
slots,
|
||||
});
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
@@ -0,0 +1 @@
|
||||
export { default as VbenScrollbar } from './scrollbar.vue';
|
||||
@@ -0,0 +1,3 @@
|
||||
export { default as VbenSegmented } from './segmented.vue';
|
||||
|
||||
export type * from './types';
|
||||
@@ -0,0 +1,6 @@
|
||||
interface SegmentedItem {
|
||||
label: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
export type { SegmentedItem };
|
||||
@@ -0,0 +1 @@
|
||||
export { default as VbenSelect } from './select.vue';
|
||||
@@ -0,0 +1,57 @@
|
||||
<script lang="ts" setup>
|
||||
import { CircleX } from '@vben-core/icons';
|
||||
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from '../../ui';
|
||||
|
||||
interface Props {
|
||||
allowClear?: boolean;
|
||||
class?: any;
|
||||
options?: Array<{ label: string; value: string }>;
|
||||
placeholder?: string;
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
allowClear: false,
|
||||
});
|
||||
|
||||
const modelValue = defineModel<string>();
|
||||
|
||||
function handleClear() {
|
||||
modelValue.value = undefined;
|
||||
}
|
||||
</script>
|
||||
<template>
|
||||
<Select v-model="modelValue">
|
||||
<SelectTrigger :class="props.class" class="flex w-full items-center">
|
||||
<SelectValue class="flex-auto text-left" :placeholder="placeholder" />
|
||||
<CircleX
|
||||
@pointerdown.stop
|
||||
@click.stop.prevent="handleClear"
|
||||
v-if="allowClear && modelValue"
|
||||
data-clear-button
|
||||
class="mr-1 size-4 cursor-pointer opacity-50 hover:opacity-100"
|
||||
/>
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<template v-for="item in options" :key="item.value">
|
||||
<SelectItem :value="item.value"> {{ item.label }} </SelectItem>
|
||||
</template>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
button[role='combobox'][data-placeholder] {
|
||||
color: hsl(var(--muted-foreground));
|
||||
}
|
||||
|
||||
button {
|
||||
--ring: var(--primary);
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1 @@
|
||||
export { default as VbenSpineText } from './spine-text.vue';
|
||||
@@ -0,0 +1,49 @@
|
||||
<script lang="ts" setup>
|
||||
import { computed } from 'vue';
|
||||
|
||||
const { animationDuration = 2, animationIterationCount = 'infinite' } =
|
||||
defineProps<{
|
||||
// 动画持续时间,单位秒
|
||||
animationDuration?: number;
|
||||
// 动画是否只执行一次
|
||||
animationIterationCount?: 'infinite' | number;
|
||||
}>();
|
||||
|
||||
const style = computed(() => {
|
||||
return {
|
||||
animation: `shine ${animationDuration}s linear ${animationIterationCount}`,
|
||||
};
|
||||
});
|
||||
</script>
|
||||
<template>
|
||||
<div :style="style" class="vben-spine-text !bg-clip-text text-transparent">
|
||||
<slot></slot>
|
||||
</div>
|
||||
</template>
|
||||
<style>
|
||||
.vben-spine-text {
|
||||
background:
|
||||
radial-gradient(circle at center, rgb(255 255 255 / 80%), #f000) -200% 50% /
|
||||
200% 100% no-repeat,
|
||||
#000;
|
||||
|
||||
/* animation: shine 3s linear infinite; */
|
||||
}
|
||||
|
||||
.dark .vben-spine-text {
|
||||
background:
|
||||
radial-gradient(circle at center, rgb(24 24 26 / 80%), transparent) -200%
|
||||
50% / 200% 100% no-repeat,
|
||||
#f4f4f4;
|
||||
}
|
||||
|
||||
@keyframes shine {
|
||||
0% {
|
||||
background-position: 200% 0%;
|
||||
}
|
||||
|
||||
100% {
|
||||
background-position: -200% 0%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,2 @@
|
||||
export { default as VbenLoading } from './loading.vue';
|
||||
export { default as VbenSpinner } from './spinner.vue';
|
||||
@@ -0,0 +1,2 @@
|
||||
export { default as VbenHelpTooltip } from './help-tooltip.vue';
|
||||
export { default as VbenTooltip } from './tooltip.vue';
|
||||
@@ -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';
|
||||
@@ -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';
|
||||
22
packages/@core/ui-kit/shadcn-ui/src/ui/avatar/avatar.ts
Normal file
22
packages/@core/ui-kit/shadcn-ui/src/ui/avatar/avatar.ts
Normal 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>;
|
||||
4
packages/@core/ui-kit/shadcn-ui/src/ui/avatar/index.ts
Normal file
4
packages/@core/ui-kit/shadcn-ui/src/ui/avatar/index.ts
Normal 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';
|
||||
18
packages/@core/ui-kit/shadcn-ui/src/ui/badge/Badge.vue
Normal file
18
packages/@core/ui-kit/shadcn-ui/src/ui/badge/Badge.vue
Normal 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>
|
||||
25
packages/@core/ui-kit/shadcn-ui/src/ui/badge/badge.ts
Normal file
25
packages/@core/ui-kit/shadcn-ui/src/ui/badge/badge.ts
Normal 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>;
|
||||
3
packages/@core/ui-kit/shadcn-ui/src/ui/badge/index.ts
Normal file
3
packages/@core/ui-kit/shadcn-ui/src/ui/badge/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export * from './badge';
|
||||
|
||||
export { default as Badge } from './Badge.vue';
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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';
|
||||
34
packages/@core/ui-kit/shadcn-ui/src/ui/button/button.ts
Normal file
34
packages/@core/ui-kit/shadcn-ui/src/ui/button/button.ts
Normal 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',
|
||||
},
|
||||
},
|
||||
},
|
||||
);
|
||||
5
packages/@core/ui-kit/shadcn-ui/src/ui/button/index.ts
Normal file
5
packages/@core/ui-kit/shadcn-ui/src/ui/button/index.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
export * from './button';
|
||||
|
||||
export { default as Button } from './Button.vue';
|
||||
|
||||
export type * from './types';
|
||||
20
packages/@core/ui-kit/shadcn-ui/src/ui/button/types.ts
Normal file
20
packages/@core/ui-kit/shadcn-ui/src/ui/button/types.ts
Normal 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;
|
||||
13
packages/@core/ui-kit/shadcn-ui/src/ui/card/CardContent.vue
Normal file
13
packages/@core/ui-kit/shadcn-ui/src/ui/card/CardContent.vue
Normal 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>
|
||||
13
packages/@core/ui-kit/shadcn-ui/src/ui/card/CardFooter.vue
Normal file
13
packages/@core/ui-kit/shadcn-ui/src/ui/card/CardFooter.vue
Normal 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>
|
||||
13
packages/@core/ui-kit/shadcn-ui/src/ui/card/CardHeader.vue
Normal file
13
packages/@core/ui-kit/shadcn-ui/src/ui/card/CardHeader.vue
Normal 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>
|
||||
13
packages/@core/ui-kit/shadcn-ui/src/ui/card/CardTitle.vue
Normal file
13
packages/@core/ui-kit/shadcn-ui/src/ui/card/CardTitle.vue
Normal 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>
|
||||
6
packages/@core/ui-kit/shadcn-ui/src/ui/card/index.ts
Normal file
6
packages/@core/ui-kit/shadcn-ui/src/ui/card/index.ts
Normal 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';
|
||||
1
packages/@core/ui-kit/shadcn-ui/src/ui/checkbox/index.ts
Normal file
1
packages/@core/ui-kit/shadcn-ui/src/ui/checkbox/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export { default as Checkbox } from './Checkbox.vue';
|
||||
14
packages/@core/ui-kit/shadcn-ui/src/ui/context-menu/index.ts
Normal file
14
packages/@core/ui-kit/shadcn-ui/src/ui/context-menu/index.ts
Normal 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';
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
9
packages/@core/ui-kit/shadcn-ui/src/ui/dialog/index.ts
Normal file
9
packages/@core/ui-kit/shadcn-ui/src/ui/dialog/index.ts
Normal 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';
|
||||
@@ -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>
|
||||
20
packages/@core/ui-kit/shadcn-ui/src/ui/form/FormItem.vue
Normal file
20
packages/@core/ui-kit/shadcn-ui/src/ui/form/FormItem.vue
Normal 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>
|
||||
11
packages/@core/ui-kit/shadcn-ui/src/ui/form/index.ts
Normal file
11
packages/@core/ui-kit/shadcn-ui/src/ui/form/index.ts
Normal 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';
|
||||
@@ -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>;
|
||||
38
packages/@core/ui-kit/shadcn-ui/src/ui/form/useFormField.ts
Normal file
38
packages/@core/ui-kit/shadcn-ui/src/ui/form/useFormField.ts
Normal 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,
|
||||
};
|
||||
}
|
||||
@@ -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';
|
||||
31
packages/@core/ui-kit/shadcn-ui/src/ui/index.ts
Normal file
31
packages/@core/ui-kit/shadcn-ui/src/ui/index.ts
Normal 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';
|
||||
1
packages/@core/ui-kit/shadcn-ui/src/ui/input/index.ts
Normal file
1
packages/@core/ui-kit/shadcn-ui/src/ui/input/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export { default as Input } from './Input.vue';
|
||||
1
packages/@core/ui-kit/shadcn-ui/src/ui/label/index.ts
Normal file
1
packages/@core/ui-kit/shadcn-ui/src/ui/label/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export { default as Label } from './Label.vue';
|
||||
@@ -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(
|
||||
'relative [&>[data-slot=input]]:has-[[data-slot=decrement]]:pl-5 [&>[data-slot=input]]:has-[[data-slot=increment]]:pr-5',
|
||||
props.class,
|
||||
)
|
||||
"
|
||||
>
|
||||
<slot></slot>
|
||||
</div>
|
||||
</template>
|
||||
@@ -0,0 +1,5 @@
|
||||
export { default as NumberField } from './NumberField.vue';
|
||||
export { default as NumberFieldContent } from './NumberFieldContent.vue';
|
||||
export { default as NumberFieldDecrement } from './NumberFieldDecrement.vue';
|
||||
export { default as NumberFieldIncrement } from './NumberFieldIncrement.vue';
|
||||
export { default as NumberFieldInput } from './NumberFieldInput.vue';
|
||||
@@ -0,0 +1,4 @@
|
||||
export { default as PinInput } from './PinInput.vue';
|
||||
export { default as PinInputGroup } from './PinInputGroup.vue';
|
||||
export { default as PinInputInput } from './PinInputInput.vue';
|
||||
export { default as PinInputSeparator } from './PinInputSeparator.vue';
|
||||
@@ -0,0 +1,2 @@
|
||||
export { default as RadioGroup } from './RadioGroup.vue';
|
||||
export { default as RadioGroupItem } from './RadioGroupItem.vue';
|
||||
@@ -0,0 +1,2 @@
|
||||
export { default as ScrollArea } from './ScrollArea.vue';
|
||||
export { default as ScrollBar } from './ScrollBar.vue';
|
||||
11
packages/@core/ui-kit/shadcn-ui/src/ui/select/index.ts
Normal file
11
packages/@core/ui-kit/shadcn-ui/src/ui/select/index.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
export { default as Select } from './Select.vue';
|
||||
export { default as SelectContent } from './SelectContent.vue';
|
||||
export { default as SelectGroup } from './SelectGroup.vue';
|
||||
export { default as SelectItem } from './SelectItem.vue';
|
||||
export { default as SelectItemText } from './SelectItemText.vue';
|
||||
export { default as SelectLabel } from './SelectLabel.vue';
|
||||
export { default as SelectScrollDownButton } from './SelectScrollDownButton.vue';
|
||||
export { default as SelectScrollUpButton } from './SelectScrollUpButton.vue';
|
||||
export { default as SelectSeparator } from './SelectSeparator.vue';
|
||||
export { default as SelectTrigger } from './SelectTrigger.vue';
|
||||
export { default as SelectValue } from './SelectValue.vue';
|
||||
@@ -0,0 +1 @@
|
||||
export { default as Separator } from './Separator.vue';
|
||||
15
packages/@core/ui-kit/shadcn-ui/src/ui/sheet/SheetFooter.vue
Normal file
15
packages/@core/ui-kit/shadcn-ui/src/ui/sheet/SheetFooter.vue
Normal 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>
|
||||
11
packages/@core/ui-kit/shadcn-ui/src/ui/sheet/SheetHeader.vue
Normal file
11
packages/@core/ui-kit/shadcn-ui/src/ui/sheet/SheetHeader.vue
Normal file
@@ -0,0 +1,11 @@
|
||||
<script setup lang="ts">
|
||||
import { cn } from '@vben-core/shared/utils';
|
||||
|
||||
const props = defineProps<{ class?: any }>();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div :class="cn('flex flex-col text-center sm:text-left', props.class)">
|
||||
<slot></slot>
|
||||
</div>
|
||||
</template>
|
||||
10
packages/@core/ui-kit/shadcn-ui/src/ui/sheet/index.ts
Normal file
10
packages/@core/ui-kit/shadcn-ui/src/ui/sheet/index.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
export * from './sheet';
|
||||
export { default as Sheet } from './Sheet.vue';
|
||||
export { default as SheetClose } from './SheetClose.vue';
|
||||
export { default as SheetContent } from './SheetContent.vue';
|
||||
export { default as SheetDescription } from './SheetDescription.vue';
|
||||
export { default as SheetFooter } from './SheetFooter.vue';
|
||||
export { default as SheetHeader } from './SheetHeader.vue';
|
||||
export { default as SheetTitle } from './SheetTitle.vue';
|
||||
|
||||
export { default as SheetTrigger } from './SheetTrigger.vue';
|
||||
24
packages/@core/ui-kit/shadcn-ui/src/ui/sheet/sheet.ts
Normal file
24
packages/@core/ui-kit/shadcn-ui/src/ui/sheet/sheet.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import type { VariantProps } from 'class-variance-authority';
|
||||
|
||||
import { cva } from 'class-variance-authority';
|
||||
|
||||
export const sheetVariants = cva(
|
||||
'bg-background shadow-lg transition ease-in-out data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:duration-300 data-[state=open]:duration-500 border-border',
|
||||
{
|
||||
defaultVariants: {
|
||||
side: 'right',
|
||||
},
|
||||
variants: {
|
||||
side: {
|
||||
bottom:
|
||||
'inset-x-0 bottom-0 border-t border-border data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom',
|
||||
left: 'inset-y-0 left-0 h-full w-3/4 border-r data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left ',
|
||||
right:
|
||||
'inset-y-0 right-0 w-3/4 border-l data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right',
|
||||
top: 'inset-x-0 top-0 border-b data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top',
|
||||
},
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
export type SheetVariants = VariantProps<typeof sheetVariants>;
|
||||
1
packages/@core/ui-kit/shadcn-ui/src/ui/switch/index.ts
Normal file
1
packages/@core/ui-kit/shadcn-ui/src/ui/switch/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export { default as Switch } from './Switch.vue';
|
||||
1
packages/@core/ui-kit/shadcn-ui/src/ui/textarea/index.ts
Normal file
1
packages/@core/ui-kit/shadcn-ui/src/ui/textarea/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export { default as Textarea } from './Textarea.vue';
|
||||
@@ -0,0 +1,2 @@
|
||||
export { default as ToggleGroup } from './ToggleGroup.vue';
|
||||
export { default as ToggleGroupItem } from './ToggleGroupItem.vue';
|
||||
2
packages/@core/ui-kit/shadcn-ui/src/ui/toggle/index.ts
Normal file
2
packages/@core/ui-kit/shadcn-ui/src/ui/toggle/index.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export * from './toggle';
|
||||
export { default as Toggle } from './Toggle.vue';
|
||||
27
packages/@core/ui-kit/shadcn-ui/src/ui/toggle/toggle.ts
Normal file
27
packages/@core/ui-kit/shadcn-ui/src/ui/toggle/toggle.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import type { VariantProps } from 'class-variance-authority';
|
||||
|
||||
import { cva } from 'class-variance-authority';
|
||||
|
||||
export const toggleVariants = cva(
|
||||
'inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors hover:bg-muted hover:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 data-[state=on]:bg-accent data-[state=on]:text-accent-foreground',
|
||||
{
|
||||
defaultVariants: {
|
||||
size: 'default',
|
||||
variant: 'default',
|
||||
},
|
||||
variants: {
|
||||
size: {
|
||||
default: 'h-9 px-3',
|
||||
lg: 'h-10 px-3',
|
||||
sm: 'h-8 px-2',
|
||||
},
|
||||
variant: {
|
||||
default: 'bg-transparent',
|
||||
outline:
|
||||
'border border-input bg-transparent shadow-sm hover:bg-accent hover:text-accent-foreground',
|
||||
},
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
export type ToggleVariants = VariantProps<typeof toggleVariants>;
|
||||
4
packages/@core/ui-kit/shadcn-ui/src/ui/tooltip/index.ts
Normal file
4
packages/@core/ui-kit/shadcn-ui/src/ui/tooltip/index.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export { default as Tooltip } from './Tooltip.vue';
|
||||
export { default as TooltipContent } from './TooltipContent.vue';
|
||||
export { default as TooltipProvider } from './TooltipProvider.vue';
|
||||
export { default as TooltipTrigger } from './TooltipTrigger.vue';
|
||||
1
packages/@core/ui-kit/shadcn-ui/tailwind.config.mjs
Normal file
1
packages/@core/ui-kit/shadcn-ui/tailwind.config.mjs
Normal file
@@ -0,0 +1 @@
|
||||
export { default } from '@vben/tailwind-config';
|
||||
12
packages/@core/ui-kit/shadcn-ui/tsconfig.json
Normal file
12
packages/@core/ui-kit/shadcn-ui/tsconfig.json
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/tsconfig",
|
||||
"extends": "@vben/tsconfig/web.json",
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@vben-core/shadcn-ui/*": ["./src/*"]
|
||||
}
|
||||
},
|
||||
"include": ["src"],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
Reference in New Issue
Block a user