Skip to content

Commit 53f7ed2

Browse files
committed
#6: no more sonner. Custom toasts and fixed linting issues.
1 parent dc8407b commit 53f7ed2

29 files changed

+2402
-7679
lines changed

package.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,7 @@
3636
"tw-animate-css": "^1.3.7",
3737
"v-code-diff": "^1.13.1",
3838
"vaul-vue": "^0.4.1",
39-
"vue": "^3.5.18",
40-
"vue-sonner": "^2.0.8"
39+
"vue": "^3.5.18"
4140
},
4241
"devDependencies": {
4342
"@antfu/eslint-config": "^5.2.1",

pnpm-lock.yaml

Lines changed: 2158 additions & 7517 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/App.vue

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
<script setup lang="ts">
22
import YVHeader from '@/components/YvHeader.vue';
3-
43
import YvToasts from './components/toasts/YvToasts.vue';
54
// import Drawer from './components/ui/drawer/Drawer.vue';
65
import YvFooter from './components/YvFooter.vue';

src/components/toasts/YvToasts.vue

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
<script lang="ts" setup>
22
import { ref, watchEffect } from 'vue';
3-
import { toast, useVueSonner } from 'vue-sonner';
3+
import { useToastsStore } from './useToastsStore';
44
5-
const { activeToasts } = useVueSonner();
5+
const { activeToasts, toast } = useToastsStore();
66
77
// Store timeout data for each toast
8-
const toastTimers = ref(new Map<string | number, {
8+
const toastTimers = ref(new Map<string, {
99
timeoutId: number;
1010
remainingTime: number;
1111
startTime: number;
@@ -41,7 +41,7 @@ function getToastClasses(type: string) {
4141
return classes[type as keyof typeof classes] || classes.info;
4242
}
4343
44-
function handleToastClick(toastId: string | number) {
44+
function handleToastClick(toastId: string) {
4545
const timer = toastTimers.value.get(toastId);
4646
if (timer) {
4747
timer.stop();
@@ -50,7 +50,7 @@ function handleToastClick(toastId: string | number) {
5050
toast.dismiss(toastId);
5151
}
5252
53-
function createAutoDissmiss(toastId: string | number, type: string, duration?: number) {
53+
function createAutoDissmiss(toastId: string, type: string, duration?: number) {
5454
// Check if toast should auto-dismiss
5555
const shouldAutoDismiss = duration !== Infinity && duration !== 0;
5656
if (!shouldAutoDismiss)
@@ -80,7 +80,7 @@ function createAutoDissmiss(toastId: string | number, type: string, duration?: n
8080
return timer;
8181
}
8282
83-
function pauseAutoDissmiss(toastId: string | number) {
83+
function pauseAutoDissmiss(toastId: string) {
8484
const timer = toastTimers.value.get(toastId);
8585
if (timer) {
8686
// Clear the current timeout
@@ -94,7 +94,7 @@ function pauseAutoDissmiss(toastId: string | number) {
9494
}
9595
}
9696
97-
function resumeAutoDissmiss(toastId: string | number) {
97+
function resumeAutoDissmiss(toastId: string) {
9898
const timer = toastTimers.value.get(toastId);
9999
if (timer && timer.remainingTime > 0) {
100100
// Create new timeout with remaining time
@@ -115,10 +115,10 @@ function resumeAutoDissmiss(toastId: string | number) {
115115
116116
// Watch for changes in active toasts and manage timers
117117
watchEffect(() => {
118-
const activeToastIds = new Set(activeToasts.value.map(t => t.id));
118+
const activeToastIds = new Set(activeToasts.map(t => t.id));
119119
120120
// Set up timers for new toasts
121-
activeToasts.value.forEach((activeToast) => {
121+
activeToasts.forEach((activeToast) => {
122122
if (!toastTimers.value.has(activeToast.id)) {
123123
createAutoDissmiss(
124124
activeToast.id,
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
import type { Ref } from 'vue';
2+
3+
import { defineStore } from 'pinia';
4+
import { ref } from 'vue';
5+
6+
type ToastType = 'success' | 'error' | 'warning' | 'info';
7+
8+
type ToastsStore = {
9+
activeToasts: Ref<Toast[]>;
10+
11+
toast: {
12+
success: (title: string, toast?: Omit<Partial<Toast>, 'id'>) => string;
13+
error: (title: string, toast?: Omit<Partial<Toast>, 'id'>) => string;
14+
warning: (title: string, toast?: Omit<Partial<Toast>, 'id'>) => string;
15+
info: (title: string, toast?: Omit<Partial<Toast>, 'id'>) => string;
16+
17+
dismiss: (id: string) => void;
18+
};
19+
};
20+
21+
type Toast = {
22+
id: string;
23+
type: ToastType;
24+
title: string;
25+
description: string;
26+
duration?: number;
27+
};
28+
29+
const defaultToast: Omit<Toast, 'id'> = {
30+
type: 'info',
31+
title: '',
32+
description: '',
33+
duration: 4000,
34+
};
35+
36+
export const useToastsStore = defineStore<'toastStore', ToastsStore>('toastStore', (): ToastsStore => {
37+
const activeToasts = ref<Toast[]>([]);
38+
39+
function addToast(toast: Omit<Partial<Toast>, 'id'>): string {
40+
const id = crypto.randomUUID().slice(0, 8);
41+
activeToasts.value.push({ id, ...defaultToast, ...toast });
42+
return id;
43+
};
44+
45+
function dismiss(id: string): void {
46+
const index = activeToasts.value.findIndex(t => t.id === id);
47+
if (index > -1)
48+
activeToasts.value.splice(index, 1);
49+
};
50+
51+
function success(title: string, toast?: Omit<Partial<Toast>, 'id'>): string {
52+
return addToast({ ...toast, title, type: 'success' });
53+
};
54+
55+
function error(title: string, toast?: Omit<Partial<Toast>, 'id'>): string {
56+
return addToast({ ...toast, title, type: 'error' });
57+
};
58+
59+
function info(title: string, toast?: Omit<Partial<Toast>, 'id'>): string {
60+
return addToast({ ...toast, title, type: 'info' });
61+
};
62+
63+
function warning(title: string, toast?: Omit<Partial<Toast>, 'id'>): string {
64+
return addToast({ ...toast, title, type: 'warning' });
65+
};
66+
67+
return {
68+
activeToasts,
69+
70+
toast: {
71+
success,
72+
error,
73+
info,
74+
warning,
75+
dismiss,
76+
},
77+
};
78+
});

src/components/ui/button/Button.vue

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,20 @@
11
<script setup lang="ts">
2-
import type { PrimitiveProps } from "reka-ui"
3-
import type { HTMLAttributes } from "vue"
4-
import type { ButtonVariants } from "."
5-
import { Primitive } from "reka-ui"
6-
import { cn } from "@/lib/utils"
7-
import { buttonVariants } from "."
2+
import type { PrimitiveProps } from 'reka-ui';
3+
import type { HTMLAttributes } from 'vue';
4+
import type { ButtonVariants } from '.';
5+
import { Primitive } from 'reka-ui';
6+
import { cn } from '@/lib/utils';
7+
import { buttonVariants } from '.';
88
9-
interface Props extends PrimitiveProps {
10-
variant?: ButtonVariants["variant"]
11-
size?: ButtonVariants["size"]
12-
class?: HTMLAttributes["class"]
13-
}
9+
type Props = {
10+
variant?: ButtonVariants['variant'];
11+
size?: ButtonVariants['size'];
12+
class?: HTMLAttributes['class'];
13+
} & PrimitiveProps;
1414
1515
const props = withDefaults(defineProps<Props>(), {
16-
as: "button",
17-
})
16+
as: 'button',
17+
});
1818
</script>
1919

2020
<template>

src/components/ui/button/index.ts

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,37 @@
1-
import type { VariantProps } from "class-variance-authority"
2-
import { cva } from "class-variance-authority"
1+
import type { VariantProps } from 'class-variance-authority';
2+
import { cva } from 'class-variance-authority';
33

4-
export { default as Button } from "./Button.vue"
4+
export { default as Button } from './Button.vue';
55

66
export const buttonVariants = cva(
7-
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
7+
'inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*=\'size-\'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive',
88
{
99
variants: {
1010
variant: {
1111
default:
12-
"bg-primary text-primary-foreground shadow-xs hover:bg-primary/90",
12+
'bg-primary text-primary-foreground shadow-xs hover:bg-primary/90',
1313
destructive:
14-
"bg-destructive text-white shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
14+
'bg-destructive text-white shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60',
1515
outline:
16-
"border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50",
16+
'border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50',
1717
secondary:
18-
"bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80",
18+
'bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80',
1919
ghost:
20-
"hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
21-
link: "text-primary underline-offset-4 hover:underline",
20+
'hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50',
21+
link: 'text-primary underline-offset-4 hover:underline',
2222
},
2323
size: {
24-
default: "h-9 px-4 py-2 has-[>svg]:px-3",
25-
sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5",
26-
lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
27-
icon: "size-9",
24+
default: 'h-9 px-4 py-2 has-[>svg]:px-3',
25+
sm: 'h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5',
26+
lg: 'h-10 rounded-md px-6 has-[>svg]:px-4',
27+
icon: 'size-9',
2828
},
2929
},
3030
defaultVariants: {
31-
variant: "default",
32-
size: "default",
31+
variant: 'default',
32+
size: 'default',
3333
},
3434
},
35-
)
35+
);
3636

37-
export type ButtonVariants = VariantProps<typeof buttonVariants>
37+
export type ButtonVariants = VariantProps<typeof buttonVariants>;

src/components/ui/input/Input.vue

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,22 @@
11
<script setup lang="ts">
2-
import type { HTMLAttributes } from "vue"
3-
import { useVModel } from "@vueuse/core"
4-
import { cn } from "@/lib/utils"
2+
import type { HTMLAttributes } from 'vue';
3+
import { useVModel } from '@vueuse/core';
4+
import { cn } from '@/lib/utils';
55
66
const props = defineProps<{
7-
defaultValue?: string | number
8-
modelValue?: string | number
9-
class?: HTMLAttributes["class"]
10-
}>()
7+
defaultValue?: string | number;
8+
modelValue?: string | number;
9+
class?: HTMLAttributes['class'];
10+
}>();
1111
1212
const emits = defineEmits<{
13-
(e: "update:modelValue", payload: string | number): void
14-
}>()
13+
(e: 'update:modelValue', payload: string | number): void;
14+
}>();
1515
16-
const modelValue = useVModel(props, "modelValue", emits, {
16+
const modelValue = useVModel(props, 'modelValue', emits, {
1717
passive: true,
1818
defaultValue: props.defaultValue,
19-
})
19+
});
2020
</script>
2121

2222
<template>

src/components/ui/input/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
export { default as Input } from "./Input.vue"
1+
export { default as Input } from './Input.vue';

src/components/ui/separator/Separator.vue

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,18 @@
11
<script setup lang="ts">
2-
import type { SeparatorProps } from "reka-ui"
3-
import type { HTMLAttributes } from "vue"
4-
import { reactiveOmit } from "@vueuse/core"
5-
import { Separator } from "reka-ui"
6-
import { cn } from "@/lib/utils"
2+
import type { SeparatorProps } from 'reka-ui';
3+
import type { HTMLAttributes } from 'vue';
4+
import { reactiveOmit } from '@vueuse/core';
5+
import { Separator } from 'reka-ui';
6+
import { cn } from '@/lib/utils';
77
88
const props = withDefaults(defineProps<
9-
SeparatorProps & { class?: HTMLAttributes["class"] }
9+
SeparatorProps & { class?: HTMLAttributes['class'] }
1010
>(), {
11-
orientation: "horizontal",
11+
orientation: 'horizontal',
1212
decorative: true,
13-
})
13+
});
1414
15-
const delegatedProps = reactiveOmit(props, "class")
15+
const delegatedProps = reactiveOmit(props, 'class');
1616
</script>
1717

1818
<template>

0 commit comments

Comments
 (0)