Skip to content

Commit 70f19b1

Browse files
committed
2 parents afaa65f + 9f67f6f commit 70f19b1

File tree

8 files changed

+155
-30
lines changed

8 files changed

+155
-30
lines changed

app/App.vue

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
<Usage />
1313
<Types />
1414
<Position v-model:position="position" />
15+
<ClosePosition v-model:closeButtonPosition="closeButtonPosition" />
1516
<Expand v-model:expand="expand" />
1617
<Theming @setTheme="(newTheme: Theme) => (theme = newTheme)" />
1718
<Styling />
@@ -28,6 +29,7 @@
2829
:expand="expand"
2930
:rich-colors="richColors"
3031
:close-button="closeButton"
32+
:close-button-position="closeButtonPosition"
3133
:theme="theme"
3234
/>
3335
</div>
@@ -37,13 +39,14 @@
3739
import { ref } from 'vue'
3840
// import { toggleDarkmode, isDark } from '~/composables/useDarkmode'
3941
import { Toaster } from '@/packages'
40-
import type { Position, Theme } from '@/packages/types'
42+
import type { CloseButtonPosition, Position, Theme } from '@/packages/types'
4143
import { useSEOHeader } from '~/composables/useSEOHeader'
4244
4345
useSEOHeader()
4446
4547
const expand = ref(false)
4648
const position = ref<Position>('bottom-right')
49+
const closeButtonPosition = ref<CloseButtonPosition>('top-left')
4750
const richColors = ref(false)
4851
const closeButton = ref(false)
4952
const theme = ref<Theme>('light')

app/components/ClosePosition.vue

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
<template>
2+
<div class="types">
3+
<h1 class="text-lg font-semibold my-2">Close Button Position</h1>
4+
<p class="text-base my-3">
5+
You can customize the position of the close button, If you haven't
6+
set the position, it defaults to
7+
<code class="text-xs !bg-neutral-200/66 p-1 mx-1 rounded-md">
8+
top-left
9+
</code>
10+
.
11+
</p>
12+
<div class="mb-4 flex gap-3 overflow-auto">
13+
<button
14+
v-for="position in positions"
15+
:key="position"
16+
class="btn-default"
17+
:class="{
18+
'bg-neutral-200/50 border-neutral-400/50': props.closeButtonPosition === position
19+
}"
20+
@click="() => handleChangePosition(position)"
21+
>
22+
{{ position }}
23+
</button>
24+
</div>
25+
<div class="code-block relative group">
26+
<Highlight
27+
language="javascript"
28+
className="rounded-md text-xs"
29+
:autodetect="false"
30+
:code="renderedCode"
31+
/>
32+
<button
33+
aria-label="Copy code"
34+
title="Copy code"
35+
class="absolute right-2 top-2 btn-border p-1 hidden group-hover:block"
36+
@click="handleCopyCode"
37+
>
38+
<CheckIcon v-if="showCheckIcon" />
39+
<CopyIcon v-else />
40+
</button>
41+
</div>
42+
</div>
43+
</template>
44+
45+
<script lang="ts" setup>
46+
import type { PropType } from 'vue'
47+
import { ref, computed, watch } from 'vue'
48+
49+
import { toast, useVueSonner } from '@/packages'
50+
import type { CloseButtonPosition } from '@/packages/types'
51+
import { useCopyCode } from '~/composables/useCopyCode'
52+
import CopyIcon from '~/components/icons/CopyIcon.vue'
53+
import CheckIcon from '~/components/icons/CheckIcon.vue'
54+
55+
const props = defineProps({
56+
closeButtonPosition: String as PropType<CloseButtonPosition>
57+
})
58+
59+
const emit = defineEmits<{
60+
(e: 'update:closeButtonPosition', position: CloseButtonPosition): void
61+
}>()
62+
63+
const positions = [
64+
'top-left',
65+
'top-right',
66+
'bottom-left',
67+
'bottom-right'
68+
] as const
69+
70+
const renderedCode = computed(() => {
71+
return `<Toaster closeButton="true" closeButtonPosition="${props.closeButtonPosition}" />`
72+
})
73+
const showCheckIcon = ref(false)
74+
75+
const handleChangePosition = (activePosition: CloseButtonPosition) => {
76+
toast.dismiss()
77+
emit('update:closeButtonPosition', activePosition)
78+
79+
toast('Event has been created', {
80+
description: 'Monday, January 3rd at 6:00pm',
81+
closeButton: true,
82+
closeButtonPosition: 'bottom-right'
83+
})
84+
}
85+
86+
const handleCopyCode = async () => {
87+
await useCopyCode({ code: renderedCode.value, checkIconRef: showCheckIcon })
88+
toast('Copied to your clipboard!!!')
89+
}
90+
</script>

app/components/Others.vue

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -105,23 +105,6 @@ const allTypes = [
105105
emit('setRichColors', true)
106106
}
107107
},
108-
{
109-
name: 'Close Button',
110-
snippet: `toast('Event has been created', {
111-
description: 'Monday, January 3rd at 6:00pm',
112-
})
113-
114-
// ...
115-
116-
<Toaster closeButton />
117-
`,
118-
action: () => {
119-
toast('Event has been created', {
120-
description: 'Monday, January 3rd at 6:00pm'
121-
})
122-
emit('setCloseButton')
123-
}
124-
},
125108
{
126109
name: 'Headless',
127110
snippet: `import { markRaw } from 'vue'

components.d.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ declare module 'vue' {
1111
'Carbon:cafe': typeof import('~icons/carbon/cafe')['default']
1212
'Carbon:logoTwitter': typeof import('~icons/carbon/logo-twitter')['default']
1313
CheckIcon: typeof import('./app/components/icons/CheckIcon.vue')['default']
14+
ClosePosition: typeof import('./app/components/ClosePosition.vue')['default']
15+
copy: typeof import('./app/components/Position copy.vue')['default']
1416
CopyIcon: typeof import('./app/components/icons/CopyIcon.vue')['default']
1517
Expand: typeof import('./app/components/Expand.vue')['default']
1618
Footer: typeof import('./app/components/Footer.vue')['default']

package.json

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,9 +79,20 @@
7979
"vue-sonner": "link:",
8080
"vue-tsc": "^2.2.12"
8181
},
82-
"dependencies": {
82+
"peerDependencies": {
8383
"@nuxt/kit": "^4.0.3",
8484
"@nuxt/schema": "^4.0.3",
8585
"nuxt": "^4.0.3"
86+
},
87+
"peerDependenciesMeta": {
88+
"@nuxt/kit": {
89+
"optional": true
90+
},
91+
"@nuxt/schema": {
92+
"optional": true
93+
},
94+
"nuxt": {
95+
"optional": true
96+
}
8697
}
8798
}

src/packages/Toast.vue

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
:aria-label="closeButtonAriaLabel || 'Close toast'"
5454
:data-disabled="disabled"
5555
data-close-button="true"
56+
:data-close-button-position="closeButtonPosition"
5657
:class="cn(classes?.closeButton, toast?.classes?.closeButton)"
5758
@click="handleCloseToast"
5859
>
@@ -484,4 +485,4 @@ function handleDragEnd() {
484485
swipeDirection.value = null;
485486
pointerStartRef.value = null;
486487
}
487-
</script>
488+
</script>

src/packages/Toaster.vue

Lines changed: 39 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
:closeButton="toastOptions?.closeButton ?? closeButton"
5353
:interacting="interacting"
5454
:position="pos"
55+
:closeButtonPosition="toastOptions?.closeButtonPosition ?? closeButtonPosition"
5556
:style="toastOptions?.style"
5657
:unstyled="toastOptions?.unstyled"
5758
:classes="toastOptions?.classes"
@@ -163,6 +164,7 @@ defineOptions({
163164
const props = withDefaults(defineProps<ToasterProps>(), {
164165
invert: false,
165166
position: 'bottom-right',
167+
closeButtonPosition: 'top-left',
166168
hotkey: () => ['altKey', 'KeyT'],
167169
expand: false,
168170
closeButton: false,
@@ -489,9 +491,6 @@ html[dir='ltr'],
489491
--toast-svg-margin-end: 0px;
490492
--toast-button-margin-start: auto;
491493
--toast-button-margin-end: 0;
492-
--toast-close-button-start: 0;
493-
--toast-close-button-end: unset;
494-
--toast-close-button-transform: translate(-35%, -35%);
495494
}
496495
497496
html[dir='rtl'],
@@ -502,9 +501,6 @@ html[dir='rtl'],
502501
--toast-svg-margin-end: -1px;
503502
--toast-button-margin-start: 0;
504503
--toast-button-margin-end: auto;
505-
--toast-close-button-start: unset;
506-
--toast-close-button-end: 0;
507-
--toast-close-button-transform: translate(35%, -35%);
508504
}
509505
510506
[data-sonner-toaster] {
@@ -699,11 +695,44 @@ html[dir='rtl'],
699695
background: rgba(255, 255, 255, 0.3);
700696
}
701697
698+
[data-sonner-toaster] [data-close-button-position='top-left'] {
699+
--toast-close-button-left: 0;
700+
--toast-close-button-right: unset;
701+
--toast-close-button-top: 0;
702+
--toast-close-button-bottom: unset;
703+
--toast-close-button-transform: translate(-35%, -35%);
704+
}
705+
706+
[data-sonner-toaster] [data-close-button-position='top-right'] {
707+
--toast-close-button-left: unset;
708+
--toast-close-button-right: 0;
709+
--toast-close-button-top: 0;
710+
--toast-close-button-bottom: unset;
711+
--toast-close-button-transform: translate(35%, -35%);
712+
}
713+
714+
[data-sonner-toaster] [data-close-button-position='bottom-left'] {
715+
--toast-close-button-left: 0;
716+
--toast-close-button-right: unset;
717+
--toast-close-button-top: unset;
718+
--toast-close-button-bottom: 0;
719+
--toast-close-button-transform: translate(-35%, 35%);
720+
}
721+
722+
[data-sonner-toaster] [data-close-button-position='bottom-right'] {
723+
--toast-close-button-left: unset;
724+
--toast-close-button-right: 0;
725+
--toast-close-button-top: unset;
726+
--toast-close-button-bottom: 0;
727+
--toast-close-button-transform: translate(35%, 35%);
728+
}
729+
702730
[data-sonner-toast][data-styled='true'] [data-close-button] {
703731
position: absolute;
704-
left: var(--toast-close-button-start);
705-
right: var(--toast-close-button-end);
706-
top: 0;
732+
left: var(--toast-close-button-left);
733+
right: var(--toast-close-button-right);
734+
top: var(--toast-close-button-top);
735+
bottom: var(--toast-close-button-bottom);
707736
height: 20px;
708737
width: 20px;
709738
display: flex;
@@ -1205,4 +1234,4 @@ html[dir='rtl'],
12051234
opacity: 0;
12061235
transform: scale(0.8) translate(-50%, -50%);
12071236
}
1208-
</style>
1237+
</style>

src/packages/types.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ export interface ToastT<T extends Component = Component> {
9797
classes?: ToastClasses
9898
descriptionClass?: string
9999
position?: Position
100+
closeButtonPosition?: CloseButtonPosition
100101
testId?: string
101102
}
102103

@@ -112,6 +113,8 @@ export type Position =
112113
| 'top-center'
113114
| 'bottom-center'
114115

116+
export type CloseButtonPosition = Exclude<Position, 'top-center' | 'bottom-center'>
117+
115118
export interface HeightT {
116119
height: number
117120
toastId: number | string
@@ -130,6 +133,7 @@ export interface ToastOptions {
130133
classes?: ToastClasses
131134
closeButtonAriaLabel?: string
132135
toasterId?: string
136+
closeButtonPosition?: CloseButtonPosition
133137
}
134138

135139
type Offset =
@@ -147,6 +151,7 @@ export interface ToasterProps {
147151
invert?: boolean
148152
theme?: Theme
149153
position?: Position
154+
closeButtonPosition?: CloseButtonPosition
150155
hotkey?: string[]
151156
richColors?: boolean
152157
expand?: boolean
@@ -177,6 +182,7 @@ export interface ToastProps {
177182
heights: HeightT[]
178183
gap?: number
179184
position: Position
185+
closeButtonPosition?: CloseButtonPosition
180186
visibleToasts: number
181187
expandByDefault: boolean
182188
closeButton: boolean

0 commit comments

Comments
 (0)