Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: multiple actions support and api improvements #2

Merged
merged 7 commits into from
May 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions components.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ export {}

declare module 'vue' {
export interface GlobalComponents {
'Carbon:cafe': typeof import('~icons/carbon/cafe')['default']
'Carbon:logoTwitter': typeof import('~icons/carbon/logo-twitter')['default']
CheckIcon: typeof import('./src/components/icons/CheckIcon.vue')['default']
CopyIcon: typeof import('./src/components/icons/CopyIcon.vue')['default']
Expand: typeof import('./src/components/Expand.vue')['default']
Expand All @@ -15,6 +17,8 @@ declare module 'vue' {
HeadlessToastWithProps: typeof import('./src/components/HeadlessToastWithProps.vue')['default']
Hero: typeof import('./src/components/Hero.vue')['default']
Installation: typeof import('./src/components/Installation.vue')['default']
'Mdi:heart': typeof import('~icons/mdi/heart')['default']
New: typeof import('./src/components/examples/New.vue')['default']
Others: typeof import('./src/components/Others.vue')['default']
Position: typeof import('./src/components/Position.vue')['default']
Styling: typeof import('./src/components/Styling.vue')['default']
Expand Down
105 changes: 63 additions & 42 deletions packages/Toast.vue
Original file line number Diff line number Diff line change
Expand Up @@ -109,37 +109,51 @@
</div>
</template>
</div>
<template v-if="toast.cancel">
<button
:class="cn(classes?.cancelButton, toast.classes?.cancelButton)"
data-button
data-cancel
@click="
() => {
deleteToast()
if (toast.cancel?.onClick) {
toast.cancel.onClick()
}
}
"
>
{{ toast.cancel.label }}
</button>
</template>
<template v-if="toast.action">
<button
:class="cn(classes?.actionButton, toast.classes?.actionButton)"
data-button
@click="
(event) => {
toast.action?.onClick(event)
if (event.defaultPrevented) return
deleteToast()
}
"
>
{{ toast.action.label }}
</button>

<template v-if="toast.cancel || toast.action">
<div data-buttons="">
anwarulislam marked this conversation as resolved.
Show resolved Hide resolved
<template v-if="toast.cancel">
<button
:class="cn(classes?.cancelButton, toast.classes?.cancelButton)"
data-button
data-cancel
@click="
() => {
deleteToast()
if (toast.cancel?.onClick) {
toast.cancel.onClick()
}
}
"
>
{{ toast.cancel.label }}
</button>
</template>

<template v-if="toast.action">
<template
v-for="(action, index) in Array.isArray(toast.action)
? toast.action
: [toast.action]"
:key="index"
>
<button
:class="cn(classes?.actionButton, action?.classes)"
data-button
@click="
(event) => {
action.onClick(event, {
dismiss: deleteToast
})
if (event.defaultPrevented) return
}
"
>
{{ action.label }}
</button>
</template>
</template>
</div>
</template>
</template>
</li>
Expand Down Expand Up @@ -265,18 +279,24 @@ onMounted(() => {
emit('update:heights', newHeightArr as HeightT[])
})

const deleteToast = () => {
// Save the offset for the exit swipe animation
removed.value = true
offsetBeforeRemove.value = offset.value
const newHeights = props.heights.filter(
(height) => height.toastId !== props.toast.id
)
emit('update:heights', newHeights)
let timeoutId: ReturnType<typeof setTimeout>

const deleteToast = (delay = 0) => {
timeoutId = setTimeout(() => {
// Save the offset for the exit swipe animation
removed.value = true
offsetBeforeRemove.value = offset.value
const newHeights = props.heights.filter(
(height) => height.toastId !== props.toast.id
)
emit('update:heights', newHeights)

setTimeout(() => {
emit('removeToast', props.toast)
}, TIME_BEFORE_UNMOUNT)
Comment on lines +294 to +296
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It'll be good to obtain a reference to the timeout ID returned by the inner setTimeout call and clear it during the unmount phase.

}, delay)

setTimeout(() => {
emit('removeToast', props.toast)
}, TIME_BEFORE_UNMOUNT)
return timeoutId
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need not return the timeoutID since it's available in the global scope and consumed in the unmount life cycle hook.

}

const handleCloseToast = () => {
Expand Down Expand Up @@ -422,5 +442,6 @@ onUnmounted(() => {
)
emit('update:heights', newHeights)
}
clearTimeout(timeoutId)
})
</script>
7 changes: 7 additions & 0 deletions packages/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,13 @@ html[dir='rtl'],
box-shadow: 0px 4px 12px rgba(0, 0, 0, 0.1), 0 0 0 2px rgba(0, 0, 0, 0.2);
}

[data-sonner-toast] [data-buttons] {
display: flex;
gap: 8px;
margin-left: var(--toast-button-margin-start);
margin-right: var(--toast-button-margin-end);
}

[data-sonner-toast][data-y-position='top'] {
top: 0;
--y: translateY(-100%);
Expand Down
16 changes: 12 additions & 4 deletions packages/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,17 @@ export interface ToastIcons {
loading?: Component
}

export type ToastAction = {
label: string | Component
onClick: (
event: MouseEvent,
toast: {
dismiss: (delay?: number) => void
}
) => void
classes?: string
anwarulislam marked this conversation as resolved.
Show resolved Hide resolved
}

export type ToastT<T extends Component = Component> = {
id: number | string
title?: string | Component
Expand All @@ -62,10 +73,7 @@ export type ToastT<T extends Component = Component> = {
duration?: number
delete?: boolean
important?: boolean
action?: {
label: string | Component
onClick: (event: MouseEvent) => void
}
action?: ToastAction | ToastAction[]
cancel?: {
label: string | Component
onClick?: () => void
Expand Down
Loading