This commit is contained in:
dap
2024-10-14 17:19:16 +08:00
71 changed files with 970 additions and 1071 deletions

View File

@@ -23,7 +23,6 @@ export * from './sheet';
export * from './switch';
export * from './tabs';
export * from './textarea';
export * from './toast';
export * from './toggle';
export * from './toggle-group';
export * from './tooltip';

View File

@@ -1,35 +0,0 @@
<script setup lang="ts">
import { computed } from 'vue';
import { cn } from '@vben-core/shared/utils';
import {
ToastRoot,
type ToastRootEmits,
useForwardPropsEmits,
} from 'radix-vue';
import { type ToastProps, toastVariants } from './toast';
const props = defineProps<ToastProps>();
const emits = defineEmits<ToastRootEmits>();
const delegatedProps = computed(() => {
const { class: _, ...delegated } = props;
return delegated;
});
const forwarded = useForwardPropsEmits(delegatedProps, emits);
</script>
<template>
<ToastRoot
v-bind="forwarded"
:class="cn(toastVariants({ variant }), props.class)"
@update:open="onOpenChange"
>
<slot></slot>
</ToastRoot>
</template>

View File

@@ -1,29 +0,0 @@
<script setup lang="ts">
import { computed } from 'vue';
import { cn } from '@vben-core/shared/utils';
import { ToastAction, type ToastActionProps } from 'radix-vue';
const props = defineProps<{ class?: any } & ToastActionProps>();
const delegatedProps = computed(() => {
const { class: _, ...delegated } = props;
return delegated;
});
</script>
<template>
<ToastAction
v-bind="delegatedProps"
:class="
cn(
'hover:bg-secondary focus:ring-ring group-[.destructive]:border-muted/40 group-[.destructive]:hover:border-destructive/30 group-[.destructive]:hover:bg-destructive group-[.destructive]:hover:text-destructive-foreground group-[.destructive]:focus:ring-destructive border-border inline-flex h-8 shrink-0 items-center justify-center rounded-md border bg-transparent px-3 text-sm font-medium transition-colors focus:outline-none focus:ring-1 disabled:pointer-events-none disabled:opacity-50',
props.class,
)
"
>
<slot></slot>
</ToastAction>
</template>

View File

@@ -1,34 +0,0 @@
<script setup lang="ts">
import { computed } from 'vue';
import { cn } from '@vben-core/shared/utils';
import { X } from 'lucide-vue-next';
import { ToastClose, type ToastCloseProps } from 'radix-vue';
const props = defineProps<
{
class?: any;
} & ToastCloseProps
>();
const delegatedProps = computed(() => {
const { class: _, ...delegated } = props;
return delegated;
});
</script>
<template>
<ToastClose
v-bind="delegatedProps"
:class="
cn(
'text-foreground/50 hover:text-foreground absolute right-1 top-1 rounded-md p-1 opacity-0 transition-opacity focus:opacity-100 focus:outline-none focus:ring-1 group-hover:opacity-100 group-[.destructive]:text-red-300 group-[.destructive]:hover:text-red-50 group-[.destructive]:focus:ring-red-400 group-[.destructive]:focus:ring-offset-red-600',
props.class,
)
"
>
<X class="size-4" />
</ToastClose>
</template>

View File

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

View File

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

View File

@@ -1,24 +0,0 @@
<script setup lang="ts">
import { computed } from 'vue';
import { cn } from '@vben-core/shared/utils';
import { ToastTitle, type ToastTitleProps } from 'radix-vue';
const props = defineProps<{ class?: any } & ToastTitleProps>();
const delegatedProps = computed(() => {
const { class: _, ...delegated } = props;
return delegated;
});
</script>
<template>
<ToastTitle
v-bind="delegatedProps"
:class="cn('text-sm font-semibold [&+div]:text-xs', props.class)"
>
<slot></slot>
</ToastTitle>
</template>

View File

@@ -1,27 +0,0 @@
<script setup lang="ts">
import { computed } from 'vue';
import { cn } from '@vben-core/shared/utils';
import { ToastViewport, type ToastViewportProps } from 'radix-vue';
const props = defineProps<{ class?: any } & ToastViewportProps>();
const delegatedProps = computed(() => {
const { class: _, ...delegated } = props;
return delegated;
});
</script>
<template>
<ToastViewport
v-bind="delegatedProps"
:class="
cn(
'fixed top-0 z-[1200] flex max-h-screen w-full flex-col-reverse p-4 sm:bottom-0 sm:right-0 sm:top-auto sm:flex-col md:max-w-[420px]',
props.class,
)
"
/>
</template>

View File

@@ -1,36 +0,0 @@
<script setup lang="ts">
import { isVNode } from 'vue';
import Toast from './Toast.vue';
import ToastClose from './ToastClose.vue';
import ToastDescription from './ToastDescription.vue';
import ToastProvider from './ToastProvider.vue';
import ToastTitle from './ToastTitle.vue';
import ToastViewport from './ToastViewport.vue';
import { useToast } from './use-toast';
const { toasts } = useToast();
</script>
<template>
<ToastProvider swipe-direction="down">
<Toast v-for="toast in toasts" :key="toast.id" v-bind="toast">
<div class="grid gap-1">
<ToastTitle v-if="toast.title">
{{ toast.title }}
</ToastTitle>
<template v-if="toast.description">
<ToastDescription v-if="isVNode(toast.description)">
<component :is="toast.description" />
</ToastDescription>
<ToastDescription v-else>
{{ toast.description }}
</ToastDescription>
</template>
<ToastClose />
</div>
<component :is="toast.action" />
</Toast>
<ToastViewport />
</ToastProvider>
</template>

View File

@@ -1,11 +0,0 @@
export * from './toast';
export { default as Toast } from './Toast.vue';
export { default as ToastAction } from './ToastAction.vue';
export { default as ToastClose } from './ToastClose.vue';
export { default as ToastDescription } from './ToastDescription.vue';
export { default as Toaster } from './Toaster.vue';
export { default as ToastProvider } from './ToastProvider.vue';
export { default as ToastTitle } from './ToastTitle.vue';
export { default as ToastViewport } from './ToastViewport.vue';
export { toast, useToast } from './use-toast';

View File

@@ -1,27 +0,0 @@
import type { ToastRootProps } from 'radix-vue';
import { cva, type VariantProps } from 'class-variance-authority';
export const toastVariants = cva(
'group pointer-events-auto relative flex w-full items-center justify-between space-x-2 overflow-hidden rounded-md border border-border p-4 pr-6 shadow-lg transition-all data-[swipe=cancel]:translate-x-0 data-[swipe=end]:translate-x-[var(--radix-toast-swipe-end-x)] data-[swipe=move]:translate-x-[var(--radix-toast-swipe-move-x)] data-[swipe=move]:transition-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[swipe=end]:animate-out data-[state=closed]:fade-out-80 data-[state=closed]:slide-out-to-right-full data-[state=open]:slide-in-from-top-full data-[state=open]:sm:slide-in-from-bottom-full',
{
defaultVariants: {
variant: 'default',
},
variants: {
variant: {
default: 'border bg-background border-border text-foreground',
destructive:
'destructive group border-destructive bg-destructive text-destructive-foreground',
},
},
},
);
type ToastVariants = VariantProps<typeof toastVariants>;
export interface ToastProps extends ToastRootProps {
class?: any;
onOpenChange?: ((value: boolean) => void) | undefined;
variant?: ToastVariants['variant'];
}

View File

@@ -1,168 +0,0 @@
import type { ToastProps } from './toast';
import { computed, ref } from 'vue';
import type { Component, VNode } from 'vue';
const TOAST_LIMIT = 1;
const TOAST_REMOVE_DELAY = 1_000_000;
export type StringOrVNode = (() => VNode) | string | VNode;
type ToasterToast = {
action?: Component;
description?: StringOrVNode;
id: string;
title?: string;
} & ToastProps;
const actionTypes = {
ADD_TOAST: 'ADD_TOAST',
DISMISS_TOAST: 'DISMISS_TOAST',
REMOVE_TOAST: 'REMOVE_TOAST',
UPDATE_TOAST: 'UPDATE_TOAST',
} as const;
let count = 0;
function genId() {
count = (count + 1) % Number.MAX_VALUE;
return count.toString();
}
type ActionType = typeof actionTypes;
type Action =
| {
toast: Partial<ToasterToast>;
type: ActionType['UPDATE_TOAST'];
}
| {
toast: ToasterToast;
type: ActionType['ADD_TOAST'];
}
| {
toastId?: ToasterToast['id'];
type: ActionType['DISMISS_TOAST'];
}
| {
toastId?: ToasterToast['id'];
type: ActionType['REMOVE_TOAST'];
};
interface State {
toasts: ToasterToast[];
}
const toastTimeouts = new Map<string, ReturnType<typeof setTimeout>>();
function addToRemoveQueue(toastId: string) {
if (toastTimeouts.has(toastId)) return;
const timeout = setTimeout(() => {
toastTimeouts.delete(toastId);
dispatch({
toastId,
type: actionTypes.REMOVE_TOAST,
});
}, TOAST_REMOVE_DELAY);
toastTimeouts.set(toastId, timeout);
}
const state = ref<State>({
toasts: [],
});
function dispatch(action: Action) {
switch (action.type) {
case actionTypes.ADD_TOAST: {
state.value.toasts = [action.toast, ...state.value.toasts].slice(
0,
TOAST_LIMIT,
);
break;
}
case actionTypes.UPDATE_TOAST: {
state.value.toasts = state.value.toasts.map((t) =>
t.id === action.toast.id ? { ...t, ...action.toast } : t,
);
break;
}
case actionTypes.DISMISS_TOAST: {
const { toastId } = action;
if (toastId) {
addToRemoveQueue(toastId);
} else {
state.value.toasts.forEach((toast) => {
addToRemoveQueue(toast.id);
});
}
state.value.toasts = state.value.toasts.map((t) =>
t.id === toastId || toastId === undefined
? {
...t,
open: false,
}
: t,
);
break;
}
case actionTypes.REMOVE_TOAST: {
state.value.toasts =
action.toastId === undefined
? []
: state.value.toasts.filter((t) => t.id !== action.toastId);
break;
}
}
}
function useToast() {
return {
dismiss: (toastId?: string) =>
dispatch({ toastId, type: actionTypes.DISMISS_TOAST }),
toast,
toasts: computed(() => state.value.toasts),
};
}
type Toast = Omit<ToasterToast, 'id'>;
function toast(props: Toast) {
const id = genId();
const update = (props: ToasterToast) =>
dispatch({
toast: { ...props, id },
type: actionTypes.UPDATE_TOAST,
});
const dismiss = () =>
dispatch({ toastId: id, type: actionTypes.DISMISS_TOAST });
dispatch({
toast: {
...props,
id,
onOpenChange: (open: boolean) => {
if (!open) dismiss();
},
open: true,
},
type: actionTypes.ADD_TOAST,
});
return {
dismiss,
id,
update,
};
}
export { toast, useToast };