solid-sonner
Advanced tools
+666
-387
@@ -1,3 +0,3 @@ | ||
| import { delegateEvents, createComponent, insert, use, setAttribute, effect, className, style, template, addEventListener, memo } from 'solid-js/web'; | ||
| import { mergeProps, createSignal, onMount, onCleanup, createEffect, on, Show, For } from 'solid-js'; | ||
| import { delegateEvents, insert, createComponent, use, setAttribute, effect, className, style, memo, template } from 'solid-js/web'; | ||
| import { createSignal, onMount, onCleanup, mergeProps, createMemo, createEffect, For, Show, on } from 'solid-js'; | ||
| import { createStore, produce, reconcile } from 'solid-js/store'; | ||
@@ -30,4 +30,4 @@ | ||
| // src/styles.css | ||
| styleInject(':where(html[dir=ltr]),\n:where([data-sonner-toaster][dir=ltr]) {\n --toast-icon-margin-start: -3px;\n --toast-icon-margin-end: 4px;\n --toast-svg-margin-start: -1px;\n --toast-svg-margin-end: 0px;\n --toast-button-margin-start: auto;\n --toast-button-margin-end: 0;\n --toast-close-button-start: 0;\n --toast-close-button-end: unset;\n --toast-close-button-transform: translate(-35%, -35%);\n}\n:where(html[dir=rtl]),\n:where([data-sonner-toaster][dir=rtl]) {\n --toast-icon-margin-start: 4px;\n --toast-icon-margin-end: -3px;\n --toast-svg-margin-start: 0px;\n --toast-svg-margin-end: -1px;\n --toast-button-margin-start: 0;\n --toast-button-margin-end: auto;\n --toast-close-button-start: unset;\n --toast-close-button-end: 0;\n --toast-close-button-transform: translate(35%, -35%);\n}\n:where([data-sonner-toaster]) {\n position: fixed;\n width: var(--width);\n font-family:\n ui-sans-serif,\n system-ui,\n -apple-system,\n BlinkMacSystemFont,\n Segoe UI,\n Roboto,\n Helvetica Neue,\n Arial,\n Noto Sans,\n sans-serif,\n Apple Color Emoji,\n Segoe UI Emoji,\n Segoe UI Symbol,\n Noto Color Emoji;\n --gray1: hsl(0, 0%, 99%);\n --gray2: hsl(0, 0%, 97.3%);\n --gray3: hsl(0, 0%, 95.1%);\n --gray4: hsl(0, 0%, 93%);\n --gray5: hsl(0, 0%, 90.9%);\n --gray6: hsl(0, 0%, 88.7%);\n --gray7: hsl(0, 0%, 85.8%);\n --gray8: hsl(0, 0%, 78%);\n --gray9: hsl(0, 0%, 56.1%);\n --gray10: hsl(0, 0%, 52.3%);\n --gray11: hsl(0, 0%, 43.5%);\n --gray12: hsl(0, 0%, 9%);\n --border-radius: 8px;\n box-sizing: border-box;\n padding: 0;\n margin: 0;\n list-style: none;\n outline: none;\n z-index: 999999999;\n}\n:where([data-sonner-toaster][data-x-position=right]) {\n right: max(var(--offset), env(safe-area-inset-right));\n}\n:where([data-sonner-toaster][data-x-position=left]) {\n left: max(var(--offset), env(safe-area-inset-left));\n}\n:where([data-sonner-toaster][data-x-position=center]) {\n left: 50%;\n transform: translateX(-50%);\n}\n:where([data-sonner-toaster][data-y-position=top]) {\n top: max(var(--offset), env(safe-area-inset-top));\n}\n:where([data-sonner-toaster][data-y-position=bottom]) {\n bottom: max(var(--offset), env(safe-area-inset-bottom));\n}\n:where([data-sonner-toast]) {\n --y: translateY(100%);\n --lift-amount: calc(var(--lift) * var(--gap));\n z-index: var(--z-index);\n position: absolute;\n opacity: 0;\n transform: var(--y);\n filter: blur(0);\n touch-action: none;\n transition:\n transform 400ms,\n opacity 400ms,\n height 400ms,\n box-shadow 200ms;\n box-sizing: border-box;\n outline: none;\n overflow-wrap: anywhere;\n}\n:where([data-sonner-toast][data-styled=true]) {\n padding: 16px;\n background: var(--normal-bg);\n border: 1px solid var(--normal-border);\n color: var(--normal-text);\n border-radius: var(--border-radius);\n box-shadow: 0px 4px 12px rgba(0, 0, 0, 0.1);\n width: var(--width);\n font-size: 13px;\n display: flex;\n align-items: center;\n gap: 6px;\n}\n:where([data-sonner-toast]:focus-visible) {\n box-shadow: 0px 4px 12px rgba(0, 0, 0, 0.1), 0 0 0 2px rgba(0, 0, 0, 0.2);\n}\n:where([data-sonner-toast][data-y-position=top]) {\n top: 0;\n --y: translateY(-100%);\n --lift: 1;\n --lift-amount: calc(1 * var(--gap));\n}\n:where([data-sonner-toast][data-y-position=bottom]) {\n bottom: 0;\n --y: translateY(100%);\n --lift: -1;\n --lift-amount: calc(var(--lift) * var(--gap));\n}\n:where([data-sonner-toast]) :where([data-description]) {\n font-weight: 400;\n line-height: 1.4;\n color: inherit;\n}\n:where([data-sonner-toast]) :where([data-title]) {\n font-weight: 500;\n line-height: 1.5;\n color: inherit;\n}\n:where([data-sonner-toast]) :where([data-icon]) {\n display: flex;\n height: 16px;\n width: 16px;\n position: relative;\n justify-content: flex-start;\n align-items: center;\n flex-shrink: 0;\n margin-left: var(--toast-icon-margin-start);\n margin-right: var(--toast-icon-margin-end);\n}\n:where([data-sonner-toast][data-promise=true]) :where([data-icon]) > svg {\n opacity: 0;\n transform: scale(0.8);\n transform-origin: center;\n animation: sonner-fade-in 300ms ease forwards;\n}\n:where([data-sonner-toast]) :where([data-icon]) > * {\n flex-shrink: 0;\n}\n:where([data-sonner-toast]) :where([data-icon]) svg {\n margin-left: var(--toast-svg-margin-start);\n margin-right: var(--toast-svg-margin-end);\n}\n:where([data-sonner-toast]) :where([data-content]) {\n display: flex;\n flex-direction: column;\n gap: 2px;\n}\n[data-sonner-toast][data-styled=true] [data-button] {\n border-radius: 4px;\n padding-left: 8px;\n padding-right: 8px;\n height: 24px;\n font-size: 12px;\n color: var(--normal-bg);\n background: var(--normal-text);\n margin-left: var(--toast-button-margin-start);\n margin-right: var(--toast-button-margin-end);\n border: none;\n cursor: pointer;\n outline: none;\n display: flex;\n align-items: center;\n flex-shrink: 0;\n transition: opacity 400ms, box-shadow 200ms;\n}\n:where([data-sonner-toast]) :where([data-button]):focus-visible {\n box-shadow: 0 0 0 2px rgba(0, 0, 0, 0.4);\n}\n:where([data-sonner-toast]) :where([data-button]):first-of-type {\n margin-left: var(--toast-button-margin-start);\n margin-right: var(--toast-button-margin-end);\n}\n:where([data-sonner-toast]) :where([data-cancel]) {\n color: var(--normal-text);\n background: rgba(0, 0, 0, 0.08);\n}\n:where([data-sonner-toast][data-theme=dark]) :where([data-cancel]) {\n background: rgba(255, 255, 255, 0.3);\n}\n:where([data-sonner-toast]) :where([data-close-button]) {\n position: absolute;\n left: var(--toast-close-button-start);\n right: var(--toast-close-button-end);\n top: 0;\n height: 20px;\n width: 20px;\n display: flex;\n justify-content: center;\n align-items: center;\n padding: 0;\n background: var(--gray1);\n color: var(--gray12);\n border: 1px solid var(--gray4);\n transform: var(--toast-close-button-transform);\n border-radius: 50%;\n cursor: pointer;\n z-index: 1;\n transition:\n opacity 100ms,\n background 200ms,\n border-color 200ms;\n}\n:where([data-sonner-toast]) :where([data-close-button]):focus-visible {\n box-shadow: 0px 4px 12px rgba(0, 0, 0, 0.1), 0 0 0 2px rgba(0, 0, 0, 0.2);\n}\n:where([data-sonner-toast]) :where([data-disabled=true]) {\n cursor: not-allowed;\n}\n:where([data-sonner-toast]):hover :where([data-close-button]):hover {\n background: var(--gray2);\n border-color: var(--gray5);\n}\n:where([data-sonner-toast][data-swiping=true])::before {\n content: "";\n position: absolute;\n left: 0;\n right: 0;\n height: 100%;\n z-index: -1;\n}\n:where([data-sonner-toast][data-y-position=top][data-swiping=true])::before {\n bottom: 50%;\n transform: scaleY(3) translateY(50%);\n}\n:where([data-sonner-toast][data-y-position=bottom][data-swiping=true])::before {\n top: 50%;\n transform: scaleY(3) translateY(-50%);\n}\n:where([data-sonner-toast][data-swiping=false][data-removed=true])::before {\n content: "";\n position: absolute;\n inset: 0;\n transform: scaleY(2);\n}\n:where([data-sonner-toast])::after {\n content: "";\n position: absolute;\n left: 0;\n height: calc(var(--gap) + 1px);\n bottom: 100%;\n width: 100%;\n}\n:where([data-sonner-toast][data-mounted=true]) {\n --y: translateY(0);\n opacity: 1;\n}\n:where([data-sonner-toast][data-expanded=false][data-front=false]) {\n --scale: var(--toasts-before) * 0.05 + 1;\n --y: translateY(calc(var(--lift-amount) * var(--toasts-before))) scale(calc(-1 * var(--scale)));\n height: var(--front-toast-height);\n}\n:where([data-sonner-toast]) > * {\n transition: opacity 400ms;\n}\n:where([data-sonner-toast][data-expanded=false][data-front=false][data-styled=true]) > * {\n opacity: 0;\n}\n:where([data-sonner-toast][data-visible=false]) {\n opacity: 0;\n pointer-events: none;\n}\n:where([data-sonner-toast][data-mounted=true][data-expanded=true]) {\n --y: translateY(calc(var(--lift) * var(--offset)));\n height: var(--initial-height);\n}\n:where([data-sonner-toast][data-removed=true][data-front=true][data-swipe-out=false]) {\n --y: translateY(calc(var(--lift) * -100%));\n opacity: 0;\n}\n:where([data-sonner-toast][data-removed=true][data-front=false][data-swipe-out=false][data-expanded=true]) {\n --y: translateY(calc(var(--lift) * var(--offset) + var(--lift) * -100%));\n opacity: 0;\n}\n:where([data-sonner-toast][data-removed=true][data-front=false][data-swipe-out=false][data-expanded=false]) {\n --y: translateY(40%);\n opacity: 0;\n transition: transform 500ms, opacity 200ms;\n}\n:where([data-sonner-toast][data-removed=true][data-front=false])::before {\n height: calc(var(--initial-height) + 20%);\n}\n[data-sonner-toast][data-swiping=true] {\n transform: var(--y) translateY(var(--swipe-amount, 0px));\n transition: none;\n}\n[data-sonner-toast][data-swipe-out=true][data-y-position=bottom],\n[data-sonner-toast][data-swipe-out=true][data-y-position=top] {\n animation: swipe-out 200ms ease-out forwards;\n}\n@keyframes swipe-out {\n from {\n transform: translateY(calc(var(--lift) * var(--offset) + var(--swipe-amount)));\n opacity: 1;\n }\n to {\n transform: translateY(calc(var(--lift) * var(--offset) + var(--swipe-amount) + var(--lift) * -100%));\n opacity: 0;\n }\n}\n@media (max-width: 600px) {\n [data-sonner-toaster] {\n position: fixed;\n --mobile-offset: 16px;\n right: var(--mobile-offset);\n left: var(--mobile-offset);\n width: 100%;\n }\n [data-sonner-toaster] [data-sonner-toast] {\n left: 0;\n right: 0;\n width: calc(100% - var(--mobile-offset) * 2);\n }\n [data-sonner-toaster][data-x-position=left] {\n left: var(--mobile-offset);\n }\n [data-sonner-toaster][data-y-position=bottom] {\n bottom: 20px;\n }\n [data-sonner-toaster][data-y-position=top] {\n top: 20px;\n }\n [data-sonner-toaster][data-x-position=center] {\n left: var(--mobile-offset);\n right: var(--mobile-offset);\n transform: none;\n }\n}\n[data-sonner-toaster][data-theme=light] {\n --normal-bg: #fff;\n --normal-border: var(--gray4);\n --normal-text: var(--gray12);\n --success-bg: hsl(143, 85%, 96%);\n --success-border: hsl(145, 92%, 91%);\n --success-text: hsl(140, 100%, 27%);\n --info-bg: hsl(208, 100%, 97%);\n --info-border: hsl(221, 91%, 91%);\n --info-text: hsl(210, 92%, 45%);\n --warning-bg: hsl(49, 100%, 97%);\n --warning-border: hsl(49, 91%, 91%);\n --warning-text: hsl(31, 92%, 45%);\n --error-bg: hsl(359, 100%, 97%);\n --error-border: hsl(359, 100%, 94%);\n --error-text: hsl(360, 100%, 45%);\n}\n[data-sonner-toaster][data-theme=light] [data-sonner-toast][data-invert=true] {\n --normal-bg: #000;\n --normal-border: hsl(0, 0%, 20%);\n --normal-text: var(--gray1);\n}\n[data-sonner-toaster][data-theme=dark] [data-sonner-toast][data-invert=true] {\n --normal-bg: #fff;\n --normal-border: var(--gray3);\n --normal-text: var(--gray12);\n}\n[data-sonner-toaster][data-theme=dark] {\n --normal-bg: #000;\n --normal-border: hsl(0, 0%, 20%);\n --normal-text: var(--gray1);\n --success-bg: hsl(150, 100%, 6%);\n --success-border: hsl(147, 100%, 12%);\n --success-text: hsl(150, 86%, 65%);\n --info-bg: hsl(215, 100%, 6%);\n --info-border: hsl(223, 100%, 12%);\n --info-text: hsl(216, 87%, 65%);\n --warning-bg: hsl(64, 100%, 6%);\n --warning-border: hsl(60, 100%, 12%);\n --warning-text: hsl(46, 87%, 65%);\n --error-bg: hsl(358, 76%, 10%);\n --error-border: hsl(357, 89%, 16%);\n --error-text: hsl(358, 100%, 81%);\n}\n[data-rich-colors=true] [data-sonner-toast][data-type=success] {\n background: var(--success-bg);\n border-color: var(--success-border);\n color: var(--success-text);\n}\n[data-rich-colors=true] [data-sonner-toast][data-type=success] [data-close-button] {\n background: var(--success-bg);\n border-color: var(--success-border);\n color: var(--success-text);\n}\n[data-rich-colors=true] [data-sonner-toast][data-type=info] {\n background: var(--info-bg);\n border-color: var(--info-border);\n color: var(--info-text);\n}\n[data-rich-colors=true] [data-sonner-toast][data-type=info] [data-close-button] {\n background: var(--info-bg);\n border-color: var(--info-border);\n color: var(--info-text);\n}\n[data-rich-colors=true] [data-sonner-toast][data-type=warning] {\n background: var(--warning-bg);\n border-color: var(--warning-border);\n color: var(--warning-text);\n}\n[data-rich-colors=true] [data-sonner-toast][data-type=warning] [data-close-button] {\n background: var(--warning-bg);\n border-color: var(--warning-border);\n color: var(--warning-text);\n}\n[data-rich-colors=true] [data-sonner-toast][data-type=error] {\n background: var(--error-bg);\n border-color: var(--error-border);\n color: var(--error-text);\n}\n[data-rich-colors=true] [data-sonner-toast][data-type=error] [data-close-button] {\n background: var(--error-bg);\n border-color: var(--error-border);\n color: var(--error-text);\n}\n.sonner-loading-wrapper {\n --size: 16px;\n height: var(--size);\n width: var(--size);\n position: absolute;\n inset: 0;\n z-index: 10;\n}\n.sonner-loading-wrapper[data-visible=false] {\n transform-origin: center;\n animation: sonner-fade-out 0.2s ease forwards;\n}\n.sonner-spinner {\n position: relative;\n top: 50%;\n left: 50%;\n height: var(--size);\n width: var(--size);\n}\n.sonner-loading-bar {\n animation: sonner-spin 1.2s linear infinite;\n background: var(--gray11);\n border-radius: 6px;\n height: 8%;\n left: -10%;\n position: absolute;\n top: -3.9%;\n width: 24%;\n}\n.sonner-loading-bar:nth-child(1) {\n animation-delay: -1.2s;\n transform: rotate(0.0001deg) translate(146%);\n}\n.sonner-loading-bar:nth-child(2) {\n animation-delay: -1.1s;\n transform: rotate(30deg) translate(146%);\n}\n.sonner-loading-bar:nth-child(3) {\n animation-delay: -1s;\n transform: rotate(60deg) translate(146%);\n}\n.sonner-loading-bar:nth-child(4) {\n animation-delay: -0.9s;\n transform: rotate(90deg) translate(146%);\n}\n.sonner-loading-bar:nth-child(5) {\n animation-delay: -0.8s;\n transform: rotate(120deg) translate(146%);\n}\n.sonner-loading-bar:nth-child(6) {\n animation-delay: -0.7s;\n transform: rotate(150deg) translate(146%);\n}\n.sonner-loading-bar:nth-child(7) {\n animation-delay: -0.6s;\n transform: rotate(180deg) translate(146%);\n}\n.sonner-loading-bar:nth-child(8) {\n animation-delay: -0.5s;\n transform: rotate(210deg) translate(146%);\n}\n.sonner-loading-bar:nth-child(9) {\n animation-delay: -0.4s;\n transform: rotate(240deg) translate(146%);\n}\n.sonner-loading-bar:nth-child(10) {\n animation-delay: -0.3s;\n transform: rotate(270deg) translate(146%);\n}\n.sonner-loading-bar:nth-child(11) {\n animation-delay: -0.2s;\n transform: rotate(300deg) translate(146%);\n}\n.sonner-loading-bar:nth-child(12) {\n animation-delay: -0.1s;\n transform: rotate(330deg) translate(146%);\n}\n@keyframes sonner-fade-in {\n 0% {\n opacity: 0;\n transform: scale(0.8);\n }\n 100% {\n opacity: 1;\n transform: scale(1);\n }\n}\n@keyframes sonner-fade-out {\n 0% {\n opacity: 1;\n transform: scale(1);\n }\n 100% {\n opacity: 0;\n transform: scale(0.8);\n }\n}\n@keyframes sonner-spin {\n 0% {\n opacity: 1;\n }\n 100% {\n opacity: 0.15;\n }\n}\n@media (prefers-reduced-motion) {\n [data-sonner-toast],\n [data-sonner-toast] > *,\n .sonner-loading-bar {\n transition: none !important;\n animation: none !important;\n }\n}\n.sonner-loader {\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n transform-origin: center;\n transition: opacity 200ms, transform 200ms;\n}\n.sonner-loader[data-visible=false] {\n opacity: 0;\n transform: scale(0.8) translate(-50%, -50%);\n}\n'); | ||
| var _tmpl$ = /* @__PURE__ */ template(`<div class=sonner-loading-wrapper><div class=sonner-spinner>`); | ||
| styleInject('html[dir=ltr],\n[data-sonner-toaster][dir=ltr] {\n --toast-icon-margin-start: -3px;\n --toast-icon-margin-end: 4px;\n --toast-svg-margin-start: -1px;\n --toast-svg-margin-end: 0px;\n --toast-button-margin-start: auto;\n --toast-button-margin-end: 0;\n --toast-close-button-start: 0;\n --toast-close-button-end: unset;\n --toast-close-button-transform: translate(-35%, -35%);\n}\nhtml[dir=rtl],\n[data-sonner-toaster][dir=rtl] {\n --toast-icon-margin-start: 4px;\n --toast-icon-margin-end: -3px;\n --toast-svg-margin-start: 0px;\n --toast-svg-margin-end: -1px;\n --toast-button-margin-start: 0;\n --toast-button-margin-end: auto;\n --toast-close-button-start: unset;\n --toast-close-button-end: 0;\n --toast-close-button-transform: translate(35%, -35%);\n}\n[data-sonner-toaster] {\n position: fixed;\n width: var(--width);\n font-family:\n ui-sans-serif,\n system-ui,\n -apple-system,\n BlinkMacSystemFont,\n Segoe UI,\n Roboto,\n Helvetica Neue,\n Arial,\n Noto Sans,\n sans-serif,\n Apple Color Emoji,\n Segoe UI Emoji,\n Segoe UI Symbol,\n Noto Color Emoji;\n --gray1: hsl(0, 0%, 99%);\n --gray2: hsl(0, 0%, 97.3%);\n --gray3: hsl(0, 0%, 95.1%);\n --gray4: hsl(0, 0%, 93%);\n --gray5: hsl(0, 0%, 90.9%);\n --gray6: hsl(0, 0%, 88.7%);\n --gray7: hsl(0, 0%, 85.8%);\n --gray8: hsl(0, 0%, 78%);\n --gray9: hsl(0, 0%, 56.1%);\n --gray10: hsl(0, 0%, 52.3%);\n --gray11: hsl(0, 0%, 43.5%);\n --gray12: hsl(0, 0%, 9%);\n --border-radius: 8px;\n box-sizing: border-box;\n padding: 0;\n margin: 0;\n list-style: none;\n outline: none;\n z-index: 999999999;\n transition: transform 400ms ease;\n}\n@media (hover: none) and (pointer: coarse) {\n [data-sonner-toaster][data-lifted=true] {\n transform: none;\n }\n}\n[data-sonner-toaster][data-x-position=right] {\n right: var(--offset-right);\n}\n[data-sonner-toaster][data-x-position=left] {\n left: var(--offset-left);\n}\n[data-sonner-toaster][data-x-position=center] {\n left: 50%;\n transform: translateX(-50%);\n}\n[data-sonner-toaster][data-y-position=top] {\n top: var(--offset-top);\n}\n[data-sonner-toaster][data-y-position=bottom] {\n bottom: var(--offset-bottom);\n}\n[data-sonner-toast] {\n --y: translateY(100%);\n --lift-amount: calc(var(--lift) * var(--gap));\n z-index: var(--z-index);\n position: absolute;\n opacity: 0;\n transform: var(--y);\n touch-action: none;\n transition:\n transform 400ms,\n opacity 400ms,\n height 400ms,\n box-shadow 200ms;\n box-sizing: border-box;\n outline: none;\n overflow-wrap: anywhere;\n}\n[data-sonner-toast][data-styled=true] {\n padding: 16px;\n background: var(--normal-bg);\n border: 1px solid var(--normal-border);\n color: var(--normal-text);\n border-radius: var(--border-radius);\n box-shadow: 0px 4px 12px rgba(0, 0, 0, 0.1);\n width: var(--width);\n font-size: 13px;\n display: flex;\n align-items: center;\n gap: 6px;\n}\n[data-sonner-toast]:focus-visible {\n box-shadow: 0px 4px 12px rgba(0, 0, 0, 0.1), 0 0 0 2px rgba(0, 0, 0, 0.2);\n}\n[data-sonner-toast][data-y-position=top] {\n top: 0;\n --y: translateY(-100%);\n --lift: 1;\n --lift-amount: calc(1 * var(--gap));\n}\n[data-sonner-toast][data-y-position=bottom] {\n bottom: 0;\n --y: translateY(100%);\n --lift: -1;\n --lift-amount: calc(var(--lift) * var(--gap));\n}\n[data-sonner-toast][data-styled=true] [data-description] {\n font-weight: 400;\n line-height: 1.4;\n color: #3f3f3f;\n}\n[data-rich-colors=true][data-sonner-toast][data-styled=true] [data-description] {\n color: inherit;\n}\n[data-sonner-toaster][data-sonner-theme=dark] [data-description] {\n color: hsl(0, 0%, 91%);\n}\n[data-sonner-toast][data-styled=true] [data-title] {\n font-weight: 500;\n line-height: 1.5;\n color: inherit;\n}\n[data-sonner-toast][data-styled=true] [data-icon] {\n display: flex;\n height: 16px;\n width: 16px;\n position: relative;\n justify-content: flex-start;\n align-items: center;\n flex-shrink: 0;\n margin-left: var(--toast-icon-margin-start);\n margin-right: var(--toast-icon-margin-end);\n}\n[data-sonner-toast][data-promise=true] [data-icon] > svg {\n opacity: 0;\n transform: scale(0.8);\n transform-origin: center;\n animation: sonner-fade-in 300ms ease forwards;\n}\n[data-sonner-toast][data-styled=true] [data-icon] > * {\n flex-shrink: 0;\n}\n[data-sonner-toast][data-styled=true] [data-icon] svg {\n margin-left: var(--toast-svg-margin-start);\n margin-right: var(--toast-svg-margin-end);\n}\n[data-sonner-toast][data-styled=true] [data-content] {\n display: flex;\n flex-direction: column;\n gap: 2px;\n}\n[data-sonner-toast][data-styled=true] [data-button] {\n border-radius: 4px;\n padding-left: 8px;\n padding-right: 8px;\n height: 24px;\n font-size: 12px;\n color: var(--normal-bg);\n background: var(--normal-text);\n margin-left: var(--toast-button-margin-start);\n margin-right: var(--toast-button-margin-end);\n border: none;\n font-weight: 500;\n cursor: pointer;\n outline: none;\n display: flex;\n align-items: center;\n flex-shrink: 0;\n transition: opacity 400ms, box-shadow 200ms;\n}\n[data-sonner-toast][data-styled=true] [data-button]:focus-visible {\n box-shadow: 0 0 0 2px rgba(0, 0, 0, 0.4);\n}\n[data-sonner-toast][data-styled=true] [data-button]:first-of-type {\n margin-left: var(--toast-button-margin-start);\n margin-right: var(--toast-button-margin-end);\n}\n[data-sonner-toast][data-styled=true] [data-cancel] {\n color: var(--normal-text);\n background: rgba(0, 0, 0, 0.08);\n}\n[data-sonner-toaster][data-sonner-theme=dark] [data-sonner-toast][data-styled=true] [data-cancel] {\n background: rgba(255, 255, 255, 0.3);\n}\n[data-sonner-toast][data-styled=true] [data-close-button] {\n position: absolute;\n left: var(--toast-close-button-start);\n right: var(--toast-close-button-end);\n top: 0;\n height: 20px;\n width: 20px;\n display: flex;\n justify-content: center;\n align-items: center;\n padding: 0;\n color: var(--normal-text);\n background: var(--normal-bg);\n border: 1px solid var(--normal-border);\n transform: var(--toast-close-button-transform);\n border-radius: 50%;\n cursor: pointer;\n z-index: 1;\n transition:\n opacity 100ms,\n background 200ms,\n border-color 200ms;\n}\n[data-sonner-toast][data-styled=true] [data-close-button]:focus-visible {\n box-shadow: 0px 4px 12px rgba(0, 0, 0, 0.1), 0 0 0 2px rgba(0, 0, 0, 0.2);\n}\n[data-sonner-toast][data-styled=true] [data-disabled=true] {\n cursor: not-allowed;\n}\n[data-sonner-toast][data-styled=true]:hover [data-close-button]:hover {\n background: var(--gray2);\n border-color: var(--gray5);\n}\n[data-sonner-toast][data-swiping=true]::before {\n content: "";\n position: absolute;\n left: -100%;\n right: -100%;\n height: 100%;\n z-index: -1;\n}\n[data-sonner-toast][data-y-position=top][data-swiping=true]::before {\n bottom: 50%;\n transform: scaleY(3) translateY(50%);\n}\n[data-sonner-toast][data-y-position=bottom][data-swiping=true]::before {\n top: 50%;\n transform: scaleY(3) translateY(-50%);\n}\n[data-sonner-toast][data-swiping=false][data-removed=true]::before {\n content: "";\n position: absolute;\n inset: 0;\n transform: scaleY(2);\n}\n[data-sonner-toast][data-expanded=true]::after {\n content: "";\n position: absolute;\n left: 0;\n height: calc(var(--gap) + 1px);\n bottom: 100%;\n width: 100%;\n}\n[data-sonner-toast][data-mounted=true] {\n --y: translateY(0);\n opacity: 1;\n}\n[data-sonner-toast][data-expanded=false][data-front=false] {\n --scale: var(--toasts-before) * 0.05 + 1;\n --y: translateY(calc(var(--lift-amount) * var(--toasts-before))) scale(calc(-1 * var(--scale)));\n height: var(--front-toast-height);\n}\n[data-sonner-toast] > * {\n transition: opacity 400ms;\n}\n[data-sonner-toast][data-x-position=right] {\n right: 0;\n}\n[data-sonner-toast][data-x-position=left] {\n left: 0;\n}\n[data-sonner-toast][data-expanded=false][data-front=false][data-styled=true] > * {\n opacity: 0;\n}\n[data-sonner-toast][data-visible=false] {\n opacity: 0;\n pointer-events: none;\n}\n[data-sonner-toast][data-mounted=true][data-expanded=true] {\n --y: translateY(calc(var(--lift) * var(--offset)));\n height: var(--initial-height);\n}\n[data-sonner-toast][data-removed=true][data-front=true][data-swipe-out=false] {\n --y: translateY(calc(var(--lift) * -100%));\n opacity: 0;\n}\n[data-sonner-toast][data-removed=true][data-front=false][data-swipe-out=false][data-expanded=true] {\n --y: translateY(calc(var(--lift) * var(--offset) + var(--lift) * -100%));\n opacity: 0;\n}\n[data-sonner-toast][data-removed=true][data-front=false][data-swipe-out=false][data-expanded=false] {\n --y: translateY(40%);\n opacity: 0;\n transition: transform 500ms, opacity 200ms;\n}\n[data-sonner-toast][data-removed=true][data-front=false]::before {\n height: calc(var(--initial-height) + 20%);\n}\n[data-sonner-toast][data-swiping=true] {\n transform: var(--y) translateY(var(--swipe-amount-y, 0px)) translateX(var(--swipe-amount-x, 0px));\n transition: none;\n}\n[data-sonner-toast][data-swiped=true] {\n -webkit-user-select: none;\n user-select: none;\n}\n[data-sonner-toast][data-swipe-out=true][data-y-position=bottom],\n[data-sonner-toast][data-swipe-out=true][data-y-position=top] {\n animation-duration: 200ms;\n animation-timing-function: ease-out;\n animation-fill-mode: forwards;\n}\n[data-sonner-toast][data-swipe-out=true][data-swipe-direction=left] {\n animation-name: swipe-out-left;\n}\n[data-sonner-toast][data-swipe-out=true][data-swipe-direction=right] {\n animation-name: swipe-out-right;\n}\n[data-sonner-toast][data-swipe-out=true][data-swipe-direction=up] {\n animation-name: swipe-out-up;\n}\n[data-sonner-toast][data-swipe-out=true][data-swipe-direction=down] {\n animation-name: swipe-out-down;\n}\n@keyframes swipe-out-left {\n from {\n transform: var(--y) translateX(var(--swipe-amount-x));\n opacity: 1;\n }\n to {\n transform: var(--y) translateX(calc(var(--swipe-amount-x) - 100%));\n opacity: 0;\n }\n}\n@keyframes swipe-out-right {\n from {\n transform: var(--y) translateX(var(--swipe-amount-x));\n opacity: 1;\n }\n to {\n transform: var(--y) translateX(calc(var(--swipe-amount-x) + 100%));\n opacity: 0;\n }\n}\n@keyframes swipe-out-up {\n from {\n transform: var(--y) translateY(var(--swipe-amount-y));\n opacity: 1;\n }\n to {\n transform: var(--y) translateY(calc(var(--swipe-amount-y) - 100%));\n opacity: 0;\n }\n}\n@keyframes swipe-out-down {\n from {\n transform: var(--y) translateY(var(--swipe-amount-y));\n opacity: 1;\n }\n to {\n transform: var(--y) translateY(calc(var(--swipe-amount-y) + 100%));\n opacity: 0;\n }\n}\n@media (max-width: 600px) {\n [data-sonner-toaster] {\n position: fixed;\n right: var(--mobile-offset-right);\n left: var(--mobile-offset-left);\n width: 100%;\n }\n [data-sonner-toaster][dir=rtl] {\n left: calc(var(--mobile-offset-left) * -1);\n }\n [data-sonner-toaster] [data-sonner-toast] {\n left: 0;\n right: 0;\n width: calc(100% - var(--mobile-offset-left) * 2);\n }\n [data-sonner-toaster][data-x-position=left] {\n left: var(--mobile-offset-left);\n }\n [data-sonner-toaster][data-y-position=bottom] {\n bottom: var(--mobile-offset-bottom);\n }\n [data-sonner-toaster][data-y-position=top] {\n top: var(--mobile-offset-top);\n }\n [data-sonner-toaster][data-x-position=center] {\n left: var(--mobile-offset-left);\n right: var(--mobile-offset-right);\n transform: none;\n }\n}\n[data-sonner-toaster][data-sonner-theme=light] {\n --normal-bg: #fff;\n --normal-border: var(--gray4);\n --normal-text: var(--gray12);\n --success-bg: hsl(143, 85%, 96%);\n --success-border: hsl(145, 92%, 87%);\n --success-text: hsl(140, 100%, 27%);\n --info-bg: hsl(208, 100%, 97%);\n --info-border: hsl(221, 91%, 93%);\n --info-text: hsl(210, 92%, 45%);\n --warning-bg: hsl(49, 100%, 97%);\n --warning-border: hsl(49, 91%, 84%);\n --warning-text: hsl(31, 92%, 45%);\n --error-bg: hsl(359, 100%, 97%);\n --error-border: hsl(359, 100%, 94%);\n --error-text: hsl(360, 100%, 45%);\n}\n[data-sonner-toaster][data-sonner-theme=light] [data-sonner-toast][data-invert=true] {\n --normal-bg: #000;\n --normal-border: hsl(0, 0%, 20%);\n --normal-text: var(--gray1);\n}\n[data-sonner-toaster][data-sonner-theme=dark] [data-sonner-toast][data-invert=true] {\n --normal-bg: #fff;\n --normal-border: var(--gray3);\n --normal-text: var(--gray12);\n}\n[data-sonner-toaster][data-sonner-theme=dark] {\n --normal-bg: #000;\n --normal-bg-hover: hsl(0, 0%, 12%);\n --normal-border: hsl(0, 0%, 20%);\n --normal-border-hover: hsl(0, 0%, 25%);\n --normal-text: var(--gray1);\n --success-bg: hsl(150, 100%, 6%);\n --success-border: hsl(147, 100%, 12%);\n --success-text: hsl(150, 86%, 65%);\n --info-bg: hsl(215, 100%, 6%);\n --info-border: hsl(223, 43%, 17%);\n --info-text: hsl(216, 87%, 65%);\n --warning-bg: hsl(64, 100%, 6%);\n --warning-border: hsl(60, 100%, 9%);\n --warning-text: hsl(46, 87%, 65%);\n --error-bg: hsl(358, 76%, 10%);\n --error-border: hsl(357, 89%, 16%);\n --error-text: hsl(358, 100%, 81%);\n}\n[data-sonner-toaster][data-sonner-theme=dark] [data-sonner-toast] [data-close-button] {\n background: var(--normal-bg);\n border-color: var(--normal-border);\n color: var(--normal-text);\n}\n[data-sonner-toaster][data-sonner-theme=dark] [data-sonner-toast] [data-close-button]:hover {\n background: var(--normal-bg-hover);\n border-color: var(--normal-border-hover);\n}\n[data-rich-colors=true][data-sonner-toast][data-type=success] {\n background: var(--success-bg);\n border-color: var(--success-border);\n color: var(--success-text);\n}\n[data-rich-colors=true][data-sonner-toast][data-type=success] [data-close-button] {\n background: var(--success-bg);\n border-color: var(--success-border);\n color: var(--success-text);\n}\n[data-rich-colors=true][data-sonner-toast][data-type=info] {\n background: var(--info-bg);\n border-color: var(--info-border);\n color: var(--info-text);\n}\n[data-rich-colors=true][data-sonner-toast][data-type=info] [data-close-button] {\n background: var(--info-bg);\n border-color: var(--info-border);\n color: var(--info-text);\n}\n[data-rich-colors=true][data-sonner-toast][data-type=warning] {\n background: var(--warning-bg);\n border-color: var(--warning-border);\n color: var(--warning-text);\n}\n[data-rich-colors=true][data-sonner-toast][data-type=warning] [data-close-button] {\n background: var(--warning-bg);\n border-color: var(--warning-border);\n color: var(--warning-text);\n}\n[data-rich-colors=true][data-sonner-toast][data-type=error] {\n background: var(--error-bg);\n border-color: var(--error-border);\n color: var(--error-text);\n}\n[data-rich-colors=true][data-sonner-toast][data-type=error] [data-close-button] {\n background: var(--error-bg);\n border-color: var(--error-border);\n color: var(--error-text);\n}\n.sonner-loading-wrapper {\n --size: 16px;\n height: var(--size);\n width: var(--size);\n position: absolute;\n inset: 0;\n z-index: 10;\n}\n.sonner-loading-wrapper[data-visible=false] {\n transform-origin: center;\n animation: sonner-fade-out 0.2s ease forwards;\n}\n.sonner-spinner {\n position: relative;\n top: 50%;\n left: 50%;\n height: var(--size);\n width: var(--size);\n}\n.sonner-loading-bar {\n animation: sonner-spin 1.2s linear infinite;\n background: var(--gray11);\n border-radius: 6px;\n height: 8%;\n left: -10%;\n position: absolute;\n top: -3.9%;\n width: 24%;\n}\n.sonner-loading-bar:nth-child(1) {\n animation-delay: -1.2s;\n transform: rotate(0.0001deg) translate(146%);\n}\n.sonner-loading-bar:nth-child(2) {\n animation-delay: -1.1s;\n transform: rotate(30deg) translate(146%);\n}\n.sonner-loading-bar:nth-child(3) {\n animation-delay: -1s;\n transform: rotate(60deg) translate(146%);\n}\n.sonner-loading-bar:nth-child(4) {\n animation-delay: -0.9s;\n transform: rotate(90deg) translate(146%);\n}\n.sonner-loading-bar:nth-child(5) {\n animation-delay: -0.8s;\n transform: rotate(120deg) translate(146%);\n}\n.sonner-loading-bar:nth-child(6) {\n animation-delay: -0.7s;\n transform: rotate(150deg) translate(146%);\n}\n.sonner-loading-bar:nth-child(7) {\n animation-delay: -0.6s;\n transform: rotate(180deg) translate(146%);\n}\n.sonner-loading-bar:nth-child(8) {\n animation-delay: -0.5s;\n transform: rotate(210deg) translate(146%);\n}\n.sonner-loading-bar:nth-child(9) {\n animation-delay: -0.4s;\n transform: rotate(240deg) translate(146%);\n}\n.sonner-loading-bar:nth-child(10) {\n animation-delay: -0.3s;\n transform: rotate(270deg) translate(146%);\n}\n.sonner-loading-bar:nth-child(11) {\n animation-delay: -0.2s;\n transform: rotate(300deg) translate(146%);\n}\n.sonner-loading-bar:nth-child(12) {\n animation-delay: -0.1s;\n transform: rotate(330deg) translate(146%);\n}\n@keyframes sonner-fade-in {\n 0% {\n opacity: 0;\n transform: scale(0.8);\n }\n 100% {\n opacity: 1;\n transform: scale(1);\n }\n}\n@keyframes sonner-fade-out {\n 0% {\n opacity: 1;\n transform: scale(1);\n }\n 100% {\n opacity: 0;\n transform: scale(0.8);\n }\n}\n@keyframes sonner-spin {\n 0% {\n opacity: 1;\n }\n 100% {\n opacity: 0.15;\n }\n}\n@media (prefers-reduced-motion) {\n [data-sonner-toast],\n [data-sonner-toast] > *,\n .sonner-loading-bar {\n transition: none !important;\n animation: none !important;\n }\n}\n.sonner-loader {\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n transform-origin: center;\n transition: opacity 200ms, transform 200ms;\n}\n.sonner-loader[data-visible=false] {\n opacity: 0;\n transform: scale(0.8) translate(-50%, -50%);\n}\n'); | ||
| var _tmpl$ = /* @__PURE__ */ template(`<div><div class=sonner-spinner>`); | ||
| var _tmpl$2 = /* @__PURE__ */ template(`<div class=sonner-loading-bar>`); | ||
@@ -38,2 +38,3 @@ var _tmpl$3 = /* @__PURE__ */ template(`<svg xmlns=http://www.w3.org/2000/svg viewBox="0 0 20 20"fill=currentColor height=20 width=20><path fill-rule=evenodd d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.857-9.809a.75.75 0 00-1.214-.882l-3.483 4.79-1.88-1.88a.75.75 0 10-1.06 1.061l2.5 2.5a.75.75 0 001.137-.089l4-5.5z"clip-rule=evenodd>`); | ||
| var _tmpl$6 = /* @__PURE__ */ template(`<svg xmlns=http://www.w3.org/2000/svg viewBox="0 0 20 20"fill=currentColor height=20 width=20><path fill-rule=evenodd d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-8-5a.75.75 0 01.75.75v4.5a.75.75 0 01-1.5 0v-4.5A.75.75 0 0110 5zm0 10a1 1 0 100-2 1 1 0 000 2z"clip-rule=evenodd>`); | ||
| var _tmpl$7 = /* @__PURE__ */ template(`<svg xmlns=http://www.w3.org/2000/svg width=12 height=12 viewBox="0 0 24 24"fill=none stroke=currentColor stroke-width=1.5 stroke-linecap=round stroke-linejoin=round><line x1=18 y1=6 x2=6 y2=18></line><line x1=6 y1=6 x2=18 y2=18>`); | ||
| var bars = Array(12).fill(0); | ||
@@ -45,5 +46,17 @@ function Loader(props) { | ||
| each: bars, | ||
| children: () => _tmpl$2() | ||
| children: (_, i) => (() => { | ||
| var _el$3 = _tmpl$2(); | ||
| effect(() => setAttribute(_el$3, "data-index", i())); | ||
| return _el$3; | ||
| })() | ||
| })); | ||
| effect(() => setAttribute(_el$, "data-visible", props.visible)); | ||
| effect((_p$) => { | ||
| var _v$ = ["sonner-loading-wrapper", props.class ?? props.className].filter(Boolean).join(" "), _v$2 = props.visible; | ||
| _v$ !== _p$.e && className(_el$, _p$.e = _v$); | ||
| _v$2 !== _p$.t && setAttribute(_el$, "data-visible", _p$.t = _v$2); | ||
| return _p$; | ||
| }, { | ||
| e: void 0, | ||
| t: void 0 | ||
| }); | ||
| return _el$; | ||
@@ -67,9 +80,9 @@ })(); | ||
| case "success": | ||
| return SuccessIcon; | ||
| return createComponent(SuccessIcon, {}); | ||
| case "info": | ||
| return InfoIcon; | ||
| return createComponent(InfoIcon, {}); | ||
| case "warning": | ||
| return WarningIcon; | ||
| return createComponent(WarningIcon, {}); | ||
| case "error": | ||
| return ErrorIcon; | ||
| return createComponent(ErrorIcon, {}); | ||
| default: | ||
@@ -79,13 +92,30 @@ return null; | ||
| } | ||
| function CloseIcon() { | ||
| return _tmpl$7(); | ||
| } | ||
| function useIsDocumentHidden() { | ||
| const [isDocumentHidden, setIsDocumentHidden] = createSignal(typeof document !== "undefined" ? document.hidden : false); | ||
| onMount(() => { | ||
| const callback = () => { | ||
| setIsDocumentHidden(document.hidden); | ||
| }; | ||
| document.addEventListener("visibilitychange", callback); | ||
| onCleanup(() => { | ||
| document.removeEventListener("visibilitychange", callback); | ||
| }); | ||
| }); | ||
| return isDocumentHidden; | ||
| } | ||
| // src/state.ts | ||
| var toastsCounter = 0; | ||
| var toastsCounter = 1; | ||
| var Observer = class { | ||
| subscribers; | ||
| toasts; | ||
| dismissedToasts; | ||
| constructor() { | ||
| this.subscribers = []; | ||
| this.toasts = []; | ||
| this.dismissedToasts = /* @__PURE__ */ new Set(); | ||
| } | ||
| // We use arrow functions to maintain the correct `this` reference | ||
| subscribe = (subscriber) => { | ||
@@ -107,11 +137,21 @@ this.subscribers.push(subscriber); | ||
| const { message, ...rest } = data; | ||
| const id = typeof data?.id === "number" || data.id && data.id?.length > 0 ? data.id : toastsCounter++; | ||
| const hasStringId = typeof data.id === "string" && data.id.length > 0; | ||
| const id = typeof data.id === "number" || hasStringId ? data.id : toastsCounter++; | ||
| const alreadyExists = this.toasts.find((toast2) => { | ||
| return toast2.id === id; | ||
| }); | ||
| const dismissible = data.dismissible === void 0 ? true : data.dismissible; | ||
| if (this.dismissedToasts.has(id)) | ||
| this.dismissedToasts.delete(id); | ||
| if (alreadyExists) { | ||
| this.toasts = this.toasts.map((toast2) => { | ||
| if (toast2.id === id) { | ||
| this.publish({ ...toast2, ...data, id, title: message }); | ||
| return { ...toast2, ...data, id, title: message }; | ||
| this.publish({ ...toast2, ...data, id, title: message, dismissible }); | ||
| return { | ||
| ...toast2, | ||
| ...data, | ||
| id, | ||
| dismissible, | ||
| title: message | ||
| }; | ||
| } | ||
@@ -121,3 +161,3 @@ return toast2; | ||
| } else { | ||
| this.addToast({ title: message, ...rest, id }); | ||
| this.addToast({ title: message, ...rest, dismissible, id }); | ||
| } | ||
@@ -127,8 +167,13 @@ return id; | ||
| dismiss = (id) => { | ||
| if (!id) { | ||
| if (id) { | ||
| this.dismissedToasts.add(id); | ||
| requestAnimationFrame(() => { | ||
| this.subscribers.forEach((subscriber) => subscriber({ id, dismiss: true })); | ||
| }); | ||
| } else { | ||
| this.toasts.forEach((toast2) => { | ||
| this.dismissedToasts.add(toast2.id); | ||
| this.subscribers.forEach((subscriber) => subscriber({ id: toast2.id, dismiss: true })); | ||
| }); | ||
| } | ||
| this.subscribers.forEach((subscriber) => subscriber({ id, dismiss: true })); | ||
| return id; | ||
@@ -151,6 +196,8 @@ }; | ||
| }; | ||
| loading = (message, data) => { | ||
| return this.create({ ...data, type: "loading", message }); | ||
| }; | ||
| promise = (promise, data) => { | ||
| if (!data) { | ||
| if (!data) | ||
| return; | ||
| } | ||
| let id; | ||
@@ -162,22 +209,41 @@ if (data.loading !== void 0) { | ||
| type: "loading", | ||
| message: data.loading | ||
| message: data.loading, | ||
| description: typeof data.description !== "function" ? data.description : void 0 | ||
| }); | ||
| } | ||
| const p = promise instanceof Promise ? promise : promise(); | ||
| const p = Promise.resolve(typeof promise === "function" ? promise() : promise); | ||
| let shouldDismiss = id !== void 0; | ||
| p.then((response) => { | ||
| if (response && typeof response.ok === "boolean" && !response.ok) { | ||
| let result; | ||
| const originalPromise = p.then(async (response) => { | ||
| result = ["resolve", response]; | ||
| if (isValidElement(response)) { | ||
| shouldDismiss = false; | ||
| const message = typeof data.error === "function" ? data.error(`HTTP error! status: ${response.status}`) : data.error; | ||
| this.create({ id, type: "error", message }); | ||
| this.create({ id, type: "default", message: response }); | ||
| } else if (isHttpResponse(response) && !response.ok) { | ||
| shouldDismiss = false; | ||
| const promiseData = typeof data.error === "function" ? await data.error(`HTTP error! status: ${response.status}`) : data.error; | ||
| const description = typeof data.description === "function" ? await data.description(`HTTP error! status: ${response.status}`) : data.description; | ||
| const toastSettings = isExtendedResult(promiseData) ? promiseData : { message: promiseData }; | ||
| this.create({ id, type: "error", description, ...toastSettings }); | ||
| } else if (response instanceof Error) { | ||
| shouldDismiss = false; | ||
| const promiseData = typeof data.error === "function" ? await data.error(response) : data.error; | ||
| const description = typeof data.description === "function" ? await data.description(response) : data.description; | ||
| const toastSettings = isExtendedResult(promiseData) ? promiseData : { message: promiseData }; | ||
| this.create({ id, type: "error", description, ...toastSettings }); | ||
| } else if (data.success !== void 0) { | ||
| shouldDismiss = false; | ||
| const message = typeof data.success === "function" ? data.success(response) : data.success; | ||
| this.create({ id, type: "success", message }); | ||
| const promiseData = typeof data.success === "function" ? await data.success(response) : data.success; | ||
| const description = typeof data.description === "function" ? await data.description(response) : data.description; | ||
| const toastSettings = isExtendedResult(promiseData) ? promiseData : { message: promiseData }; | ||
| this.create({ id, type: "success", description, ...toastSettings }); | ||
| } | ||
| }).catch((error) => { | ||
| }).catch(async (error) => { | ||
| result = ["reject", error]; | ||
| if (data.error !== void 0) { | ||
| shouldDismiss = false; | ||
| const message = typeof data.error === "function" ? data.error(error) : data.error; | ||
| this.create({ id, type: "error", message }); | ||
| const promiseData = typeof data.error === "function" ? await data.error(error) : data.error; | ||
| const description = typeof data.description === "function" ? await data.description(error) : data.description; | ||
| const toastSettings = isExtendedResult(promiseData) ? promiseData : { message: promiseData }; | ||
| this.create({ id, type: "error", description, ...toastSettings }); | ||
| } | ||
@@ -191,14 +257,32 @@ }).finally(() => { | ||
| }); | ||
| return id; | ||
| const unwrap = () => new Promise((resolve, reject) => { | ||
| originalPromise.then(() => { | ||
| if (result[0] === "reject") | ||
| reject(result[1]); | ||
| else | ||
| resolve(result[1]); | ||
| }).catch(reject); | ||
| }); | ||
| if (typeof id !== "string" && typeof id !== "number") | ||
| return { unwrap }; | ||
| return Object.assign(id, { unwrap }); | ||
| }; | ||
| loading = (message, data) => { | ||
| return this.create({ ...data, type: "loading", message }); | ||
| }; | ||
| // We can't provide the toast we just created as a prop as we didn't create it yet, so we can create a default toast object, I just don't know how to use function in argument when calling()? | ||
| custom = (jsx, data) => { | ||
| const id = data?.id || toastsCounter++; | ||
| this.publish({ jsx: jsx(id), id, ...data }); | ||
| this.create({ jsx: jsx(id), ...data, id }); | ||
| return id; | ||
| }; | ||
| getActiveToasts = () => { | ||
| return this.toasts.filter((toast2) => !this.dismissedToasts.has(toast2.id)); | ||
| }; | ||
| }; | ||
| function isHttpResponse(data) { | ||
| return data && typeof data === "object" && "ok" in data && typeof data.ok === "boolean" && "status" in data && typeof data.status === "number"; | ||
| } | ||
| function isExtendedResult(value) { | ||
| return typeof value === "object" && value !== null && "message" in value; | ||
| } | ||
| function isValidElement(value) { | ||
| return typeof Node !== "undefined" && value instanceof Node; | ||
| } | ||
| var ToastState = new Observer(); | ||
@@ -215,49 +299,129 @@ function toastFunction(message, data) { | ||
| var basicToast = toastFunction; | ||
| var toast = Object.assign(basicToast, { | ||
| success: ToastState.success, | ||
| info: ToastState.info, | ||
| warning: ToastState.warning, | ||
| error: ToastState.error, | ||
| custom: ToastState.custom, | ||
| message: ToastState.message, | ||
| promise: ToastState.promise, | ||
| dismiss: ToastState.dismiss, | ||
| loading: ToastState.loading | ||
| }); | ||
| function useIsDocumentHidden() { | ||
| const [isDocumentHidden, setIsDocumentHidden] = createSignal(false); | ||
| onMount(() => { | ||
| const callback = () => { | ||
| setIsDocumentHidden(document.hidden); | ||
| }; | ||
| document.addEventListener("visibilitychange", callback); | ||
| onCleanup(() => { | ||
| window.removeEventListener("visibilitychange", callback); | ||
| }); | ||
| }); | ||
| return isDocumentHidden; | ||
| var getHistory = () => ToastState.toasts; | ||
| var getToasts = () => ToastState.getActiveToasts(); | ||
| var toast = Object.assign( | ||
| basicToast, | ||
| { | ||
| success: ToastState.success, | ||
| info: ToastState.info, | ||
| warning: ToastState.warning, | ||
| error: ToastState.error, | ||
| custom: ToastState.custom, | ||
| message: ToastState.message, | ||
| promise: ToastState.promise, | ||
| dismiss: ToastState.dismiss, | ||
| loading: ToastState.loading | ||
| }, | ||
| { | ||
| getHistory, | ||
| getToasts | ||
| } | ||
| ); | ||
| // src/types.ts | ||
| function isAction(action) { | ||
| return typeof action === "object" && action !== null && "label" in action; | ||
| } | ||
| // src/index.tsx | ||
| var _tmpl$7 = /* @__PURE__ */ template(`<div class=sonner-loader>`); | ||
| var _tmpl$22 = /* @__PURE__ */ template(`<button aria-label="Close toast"data-close-button><svg xmlns=http://www.w3.org/2000/svg width=12 height=12 viewBox="0 0 24 24"fill=none stroke=currentColor stroke-width=1.5 stroke-linecap=round stroke-linejoin=round><line x1=18 y1=6 x2=6 y2=18></line><line x1=6 y1=6 x2=18 y2=18>`); | ||
| var _tmpl$32 = /* @__PURE__ */ template(`<li aria-atomic=true role=status tabindex=0 data-sonner-toast="">`); | ||
| var _tmpl$42 = /* @__PURE__ */ template(`<div data-icon="">`); | ||
| var _tmpl$52 = /* @__PURE__ */ template(`<div data-description="">`); | ||
| var _tmpl$62 = /* @__PURE__ */ template(`<div data-content=""><div data-title="">`); | ||
| var _tmpl$72 = /* @__PURE__ */ template(`<button data-button data-cancel>`); | ||
| var _tmpl$8 = /* @__PURE__ */ template(`<button data-button="">`); | ||
| var _tmpl$9 = /* @__PURE__ */ template(`<section tabindex=-1>`); | ||
| var _tmpl$10 = /* @__PURE__ */ template(`<ol tabindex=-1 data-sonner-toaster>`); | ||
| var _tmpl$8 = /* @__PURE__ */ template(`<div>`); | ||
| var _tmpl$22 = /* @__PURE__ */ template(`<button data-close-button>`); | ||
| var _tmpl$32 = /* @__PURE__ */ template(`<div data-icon>`); | ||
| var _tmpl$42 = /* @__PURE__ */ template(`<div data-description>`); | ||
| var _tmpl$52 = /* @__PURE__ */ template(`<li tabindex=0 data-sonner-toast><div data-content><div data-title>`); | ||
| var _tmpl$62 = /* @__PURE__ */ template(`<button data-button data-cancel>`); | ||
| var _tmpl$72 = /* @__PURE__ */ template(`<button data-button data-action>`); | ||
| var _tmpl$82 = /* @__PURE__ */ template(`<section tabindex=-1 aria-live=polite aria-relevant="additions text"aria-atomic=false data-react-aria-top-layer>`); | ||
| var _tmpl$9 = /* @__PURE__ */ template(`<ol tabindex=-1 data-sonner-toaster style=--width:356px>`); | ||
| var VISIBLE_TOASTS_AMOUNT = 3; | ||
| var VIEWPORT_OFFSET = "32px"; | ||
| var VIEWPORT_OFFSET = "24px"; | ||
| var MOBILE_VIEWPORT_OFFSET = "16px"; | ||
| var TOAST_LIFETIME = 4e3; | ||
| var TOAST_WIDTH = 356; | ||
| var GAP = 14; | ||
| var SWIPE_TRESHOLD = 20; | ||
| var SWIPE_THRESHOLD = 45; | ||
| var TIME_BEFORE_UNMOUNT = 200; | ||
| function _cn(...classes) { | ||
| function cn(...classes) { | ||
| return classes.filter(Boolean).join(" "); | ||
| } | ||
| var Toast = (props) => { | ||
| function resolveContent(content) { | ||
| return typeof content === "function" ? content() : content; | ||
| } | ||
| function getDefaultSwipeDirections(position) { | ||
| const [y, x] = position.split("-"); | ||
| const directions = []; | ||
| if (y) directions.push(y); | ||
| if (x) directions.push(x); | ||
| return directions; | ||
| } | ||
| function getDocumentDirection() { | ||
| if (typeof window === "undefined" || typeof document === "undefined") return "ltr"; | ||
| const dirAttribute = document.documentElement.getAttribute("dir"); | ||
| if (dirAttribute === "auto" || !dirAttribute) return window.getComputedStyle(document.documentElement).direction; | ||
| return dirAttribute; | ||
| } | ||
| function assignOffset(defaultOffset, mobileOffset) { | ||
| const styles = {}; | ||
| [defaultOffset, mobileOffset].forEach((offset, index) => { | ||
| const isMobile = index === 1; | ||
| const prefix = isMobile ? "--mobile-offset" : "--offset"; | ||
| const defaultValue = isMobile ? MOBILE_VIEWPORT_OFFSET : VIEWPORT_OFFSET; | ||
| const assignAll = (value) => { | ||
| for (const key of ["top", "right", "bottom", "left"]) styles[`${prefix}-${key}`] = typeof value === "number" ? `${value}px` : value; | ||
| }; | ||
| if (typeof offset === "number" || typeof offset === "string") { | ||
| assignAll(offset); | ||
| return; | ||
| } | ||
| if (typeof offset === "object" && offset !== null) { | ||
| for (const key of ["top", "right", "bottom", "left"]) { | ||
| const value = offset[key]; | ||
| styles[`${prefix}-${key}`] = value === void 0 ? defaultValue : typeof value === "number" ? `${value}px` : value; | ||
| } | ||
| return; | ||
| } | ||
| assignAll(defaultValue); | ||
| }); | ||
| return styles; | ||
| } | ||
| function mergeClassNames(classNames, legacy) { | ||
| return classNames ?? legacy; | ||
| } | ||
| function mergeDescriptionClassName(descriptionClassName, legacy) { | ||
| return descriptionClassName ?? legacy ?? ""; | ||
| } | ||
| function mergeClassName(className, legacy) { | ||
| return className ?? legacy ?? ""; | ||
| } | ||
| function useSonner() { | ||
| const [activeToasts, setActiveToasts] = createSignal(toast.getToasts()); | ||
| onMount(() => { | ||
| const unsubscribe = ToastState.subscribe((toastItem) => { | ||
| if (toastItem.dismiss) { | ||
| requestAnimationFrame(() => { | ||
| setActiveToasts((toasts) => toasts.filter((t) => t.id !== toastItem.id)); | ||
| }); | ||
| return; | ||
| } | ||
| const nextToast = toastItem; | ||
| setActiveToasts((toasts) => { | ||
| const indexOfExistingToast = toasts.findIndex((t) => t.id === nextToast.id); | ||
| if (indexOfExistingToast !== -1) { | ||
| return [...toasts.slice(0, indexOfExistingToast), { | ||
| ...toasts[indexOfExistingToast], | ||
| ...nextToast | ||
| }, ...toasts.slice(indexOfExistingToast + 1)]; | ||
| } | ||
| return [nextToast, ...toasts]; | ||
| }); | ||
| }); | ||
| onCleanup(unsubscribe); | ||
| }); | ||
| return { | ||
| toasts: activeToasts | ||
| }; | ||
| } | ||
| function createToastTheme(theme) { | ||
| return theme !== "system" ? theme : typeof window !== "undefined" && window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light"; | ||
| } | ||
| function Toast(props) { | ||
| const [mounted, setMounted] = createSignal(false); | ||
@@ -267,20 +431,32 @@ const [removed, setRemoved] = createSignal(false); | ||
| const [swipeOut, setSwipeOut] = createSignal(false); | ||
| const [isSwiped, setIsSwiped] = createSignal(false); | ||
| const [swipeDirection, setSwipeDirection] = createSignal(null); | ||
| const [swipeOutDirection, setSwipeOutDirection] = createSignal(null); | ||
| const [offsetBeforeRemove, setOffsetBeforeRemove] = createSignal(0); | ||
| const [initialHeight, setInitialHeight] = createSignal(0); | ||
| let toastRef; | ||
| let dragStartTime = null; | ||
| let closeTimerStartTimeRef = 0; | ||
| let lastCloseTimerStartTimeRef = 0; | ||
| let remainingTime = TOAST_LIFETIME; | ||
| const [pointerStartRef, setPointerStartRef] = createSignal(null); | ||
| const isFront = () => props.index === 0; | ||
| const isVisible = () => props.index + 1 <= props.visibleToasts; | ||
| const toastType = () => props.toast.type; | ||
| const toastClassname = () => props.toast.class || ""; | ||
| const toastDescriptionClassname = () => props.toast.descriptionClass || ""; | ||
| const propsWithDefaults = mergeProps({ | ||
| gap: GAP | ||
| }, props); | ||
| const heightIndex = () => props.heights.findIndex((height) => height.toastId === props.toast.id) || 0; | ||
| const duration = () => props.toast.duration || props.duration || TOAST_LIFETIME; | ||
| let closeTimerStartTimeRef = 0; | ||
| let lastCloseTimerStartTimeRef = 0; | ||
| const [pointerStartRef, setPointerStartRef] = createSignal(null); | ||
| const coords = () => props.position.split("-"); | ||
| const toastsHeightBefore = () => { | ||
| const dismissible = () => props.toast.dismissible !== false; | ||
| const classNames = () => mergeClassNames(props.classNames, props.classes); | ||
| const className$1 = () => mergeClassName(props.className, props.class); | ||
| const descriptionClassName = () => mergeDescriptionClassName(props.descriptionClassName, props.descriptionClass); | ||
| const toastClassName = () => mergeClassName(props.toast.className, props.toast.class); | ||
| const toastDescriptionClassName = () => mergeDescriptionClassName(props.toast.descriptionClassName, props.toast.descriptionClass); | ||
| const toastClassNames = () => mergeClassNames(props.toast.classNames, props.toast.classes); | ||
| const closeButton = () => props.toast.closeButton ?? props.closeButton; | ||
| const duration = () => props.toast.duration ?? props.duration ?? TOAST_LIFETIME; | ||
| const invert = () => props.toast.invert ?? props.invert; | ||
| const disabled = () => toastType() === "loading"; | ||
| const swipeDirections = () => props.swipeDirections ?? getDefaultSwipeDirections(props.position); | ||
| const y = createMemo(() => props.position.split("-")[0]); | ||
| const x = createMemo(() => props.position.split("-")[1]); | ||
| const heightIndex = createMemo(() => Math.max(0, props.heights.findIndex((height) => height.toastId === props.toast.id))); | ||
| const toastsHeightBefore = createMemo(() => { | ||
| return props.heights.reduce((prev, curr, reducerIndex) => { | ||
@@ -290,13 +466,30 @@ if (reducerIndex >= heightIndex()) return prev; | ||
| }, 0); | ||
| }; | ||
| }); | ||
| const offset = createMemo(() => heightIndex() * (props.gap ?? GAP) + toastsHeightBefore()); | ||
| const isDocumentHidden = useIsDocumentHidden(); | ||
| const invert = () => props.toast.invert || props.invert; | ||
| const disabled = () => toastType() === "loading"; | ||
| const offset = () => heightIndex() * propsWithDefaults.gap + toastsHeightBefore(); | ||
| createEffect(() => { | ||
| remainingTime = duration(); | ||
| }); | ||
| function deleteToast() { | ||
| setRemoved(true); | ||
| setOffsetBeforeRemove(offset()); | ||
| props.setHeights((heights) => heights.filter((height) => height.toastId !== props.toast.id)); | ||
| window.setTimeout(() => { | ||
| props.removeToast(props.toast); | ||
| }, TIME_BEFORE_UNMOUNT); | ||
| } | ||
| function getLoadingIcon() { | ||
| if (props.icons?.loading) { | ||
| return (() => { | ||
| var _el$ = _tmpl$7(); | ||
| var _el$ = _tmpl$8(); | ||
| insert(_el$, () => props.icons.loading); | ||
| effect(() => setAttribute(_el$, "data-visible", toastType() === "loading")); | ||
| effect((_p$) => { | ||
| var _v$ = cn(classNames()?.loader, toastClassNames()?.loader, "sonner-loader"), _v$2 = toastType() === "loading"; | ||
| _v$ !== _p$.e && className(_el$, _p$.e = _v$); | ||
| _v$2 !== _p$.t && setAttribute(_el$, "data-visible", _p$.t = _v$2); | ||
| return _p$; | ||
| }, { | ||
| e: void 0, | ||
| t: void 0 | ||
| }); | ||
| return _el$; | ||
@@ -306,2 +499,5 @@ })(); | ||
| return createComponent(Loader, { | ||
| get ["class"]() { | ||
| return cn(classNames()?.loader, toastClassNames()?.loader); | ||
| }, | ||
| get visible() { | ||
@@ -312,38 +508,43 @@ return toastType() === "loading"; | ||
| } | ||
| const icon = () => props.toast.icon ?? props.icons?.[toastType()] ?? getAsset(toastType()); | ||
| onMount(() => { | ||
| setMounted(true); | ||
| if (!toastRef) return; | ||
| const height = toastRef.getBoundingClientRect().height; | ||
| setInitialHeight(height); | ||
| props.setHeights((h) => [{ | ||
| toastId: props.toast.id, | ||
| toasterId: props.toast.toasterId, | ||
| height, | ||
| position: props.toast.position | ||
| }, ...h]); | ||
| onCleanup(() => { | ||
| props.setHeights((h) => h.filter((height2) => height2.toastId !== props.toast.id)); | ||
| }); | ||
| }); | ||
| onMount(() => { | ||
| const toastNode = toastRef; | ||
| const originalHeight = toastNode.style.height; | ||
| toastNode.style.height = "auto"; | ||
| const newHeight = toastNode.getBoundingClientRect().height; | ||
| toastNode.style.height = originalHeight; | ||
| setInitialHeight(newHeight); | ||
| createEffect(() => { | ||
| props.setHeights((heights) => { | ||
| const alreadyExists = heights.find((height) => height.toastId === props.toast.id); | ||
| if (!alreadyExists) return [{ | ||
| toastId: props.toast.id, | ||
| height: newHeight, | ||
| position: props.toast.position | ||
| }, ...heights]; | ||
| else return heights.map((height) => height.toastId === props.toast.id ? { | ||
| ...height, | ||
| height: newHeight | ||
| } : height); | ||
| }); | ||
| createEffect(() => { | ||
| if (!mounted() || !toastRef) return; | ||
| resolveContent(props.toast.title); | ||
| resolveContent(props.toast.description); | ||
| const originalHeight = toastRef.style.height; | ||
| toastRef.style.height = "auto"; | ||
| const nextHeight = toastRef.getBoundingClientRect().height; | ||
| toastRef.style.height = originalHeight; | ||
| setInitialHeight(nextHeight); | ||
| props.setHeights((heights) => { | ||
| const alreadyExists = heights.find((height) => height.toastId === props.toast.id); | ||
| if (!alreadyExists) return [{ | ||
| toastId: props.toast.id, | ||
| toasterId: props.toast.toasterId, | ||
| height: nextHeight, | ||
| position: props.toast.position | ||
| }, ...heights]; | ||
| return heights.map((height) => height.toastId === props.toast.id ? { | ||
| ...height, | ||
| height: nextHeight | ||
| } : height); | ||
| }); | ||
| }); | ||
| const deleteToast = () => { | ||
| setRemoved(true); | ||
| setOffsetBeforeRemove(offset()); | ||
| props.setHeights((h) => h.filter((height) => height.toastId !== props.toast.id)); | ||
| setTimeout(() => { | ||
| props.removeToast(props.toast); | ||
| }, TIME_BEFORE_UNMOUNT); | ||
| }; | ||
| let remainingTime = duration(); | ||
| createEffect(on(() => [props.expanded, props.interacting, props.toast, duration(), props.toast.promise, toastType(), props.pauseWhenPageIsHidden, isDocumentHidden()], ([expanded, interacting, toast2, duration2, promise, toastType2, pauseWhenPageIsHidden, isDocumentHidden2]) => { | ||
| if (promise && toastType2 === "loading" || duration2 === Number.POSITIVE_INFINITY) return; | ||
| createEffect(on(() => [props.expanded, props.interacting, props.toast, toastType(), isDocumentHidden(), duration()], ([expanded, interacting, currentToast, currentType]) => { | ||
| if (currentToast.promise && currentType === "loading" || currentToast.duration === Number.POSITIVE_INFINITY || currentType === "loading") return; | ||
| let timeoutId; | ||
@@ -353,3 +554,3 @@ const pauseTimer = () => { | ||
| const elapsedTime = (/* @__PURE__ */ new Date()).getTime() - closeTimerStartTimeRef; | ||
| remainingTime = remainingTime - elapsedTime; | ||
| remainingTime -= elapsedTime; | ||
| } | ||
@@ -359,55 +560,66 @@ lastCloseTimerStartTimeRef = (/* @__PURE__ */ new Date()).getTime(); | ||
| const startTimer = () => { | ||
| if (remainingTime === Number.POSITIVE_INFINITY) return; | ||
| closeTimerStartTimeRef = (/* @__PURE__ */ new Date()).getTime(); | ||
| timeoutId = setTimeout(() => { | ||
| toast2.onAutoClose?.(toast2); | ||
| currentToast.onAutoClose?.(currentToast); | ||
| deleteToast(); | ||
| }, remainingTime); | ||
| }; | ||
| if (expanded || interacting || pauseWhenPageIsHidden && isDocumentHidden2) pauseTimer(); | ||
| const shouldPause = expanded || interacting || (props.pauseWhenPageIsHidden ?? true) && isDocumentHidden(); | ||
| if (shouldPause) pauseTimer(); | ||
| else startTimer(); | ||
| onCleanup(() => { | ||
| clearTimeout(timeoutId); | ||
| if (timeoutId) clearTimeout(timeoutId); | ||
| }); | ||
| })); | ||
| createEffect(on(() => props.toast.id, (toastId) => { | ||
| const toastNode = toastRef; | ||
| if (toastNode) { | ||
| const height = toastNode.getBoundingClientRect().height; | ||
| setInitialHeight(height); | ||
| props.setHeights((h) => [{ | ||
| toastId, | ||
| height, | ||
| position: props.toast.position | ||
| }, ...h]); | ||
| onCleanup(() => { | ||
| props.setHeights((h) => h.filter((height2) => height2.toastId !== toastId)); | ||
| }); | ||
| createEffect(on(() => props.toast.delete, (shouldDelete) => { | ||
| if (shouldDelete) { | ||
| deleteToast(); | ||
| props.toast.onDismiss?.(props.toast); | ||
| } | ||
| })); | ||
| createEffect(on(() => props.toast.delete, (d) => { | ||
| if (d) deleteToast(); | ||
| })); | ||
| return (() => { | ||
| var _el$2 = _tmpl$32(); | ||
| var _el$2 = _tmpl$52(), _el$5 = _el$2.firstChild, _el$6 = _el$5.firstChild; | ||
| _el$2.$$pointermove = (event) => { | ||
| if (!pointerStartRef()) return; | ||
| const yPosition = event.clientY - pointerStartRef().y; | ||
| const xPosition = event.clientX - pointerStartRef().x; | ||
| const clamp = coords()[0] === "top" ? Math.min : Math.max; | ||
| const clampedY = clamp(0, yPosition); | ||
| const swipeStartThreshold = event.pointerType === "touch" ? 10 : 2; | ||
| const isAllowedToSwipe = Math.abs(clampedY) > swipeStartThreshold; | ||
| if (isAllowedToSwipe) { | ||
| toastRef?.style.setProperty("--swipe-amount", `${yPosition}px`); | ||
| } else if (Math.abs(xPosition) > swipeStartThreshold) { | ||
| setPointerStartRef(null); | ||
| if (!pointerStartRef() || !dismissible()) return; | ||
| if ((window.getSelection()?.toString().length ?? 0) > 0) return; | ||
| const yDelta = event.clientY - pointerStartRef().y; | ||
| const xDelta = event.clientX - pointerStartRef().x; | ||
| if (!swipeDirection() && (Math.abs(xDelta) > 1 || Math.abs(yDelta) > 1)) setSwipeDirection(Math.abs(xDelta) > Math.abs(yDelta) ? "x" : "y"); | ||
| const swipeAmount = { | ||
| x: 0, | ||
| y: 0 | ||
| }; | ||
| const getDampening = (delta) => { | ||
| const factor = Math.abs(delta) / 20; | ||
| return 1 / (1.5 + factor); | ||
| }; | ||
| if (swipeDirection() === "y") { | ||
| if (swipeDirections().includes("top") || swipeDirections().includes("bottom")) { | ||
| if (swipeDirections().includes("top") && yDelta < 0 || swipeDirections().includes("bottom") && yDelta > 0) swipeAmount.y = yDelta; | ||
| else swipeAmount.y = yDelta * getDampening(yDelta); | ||
| } | ||
| } else if (swipeDirection() === "x") { | ||
| if (swipeDirections().includes("left") || swipeDirections().includes("right")) { | ||
| if (swipeDirections().includes("left") && xDelta < 0 || swipeDirections().includes("right") && xDelta > 0) swipeAmount.x = xDelta; | ||
| else swipeAmount.x = xDelta * getDampening(xDelta); | ||
| } | ||
| } | ||
| if (Math.abs(swipeAmount.x) > 0 || Math.abs(swipeAmount.y) > 0) setIsSwiped(true); | ||
| toastRef?.style.setProperty("--swipe-amount-x", `${swipeAmount.x}px`); | ||
| toastRef?.style.setProperty("--swipe-amount-y", `${swipeAmount.y}px`); | ||
| }; | ||
| _el$2.$$pointerup = () => { | ||
| if (swipeOut()) return; | ||
| if (swipeOut() || !dismissible()) return; | ||
| setPointerStartRef(null); | ||
| const swipeAmount = Number(toastRef?.style.getPropertyValue("--swipe-amount").replace("px", "") || 0); | ||
| if (Math.abs(swipeAmount) >= SWIPE_TRESHOLD) { | ||
| const swipeAmountX = Number(toastRef?.style.getPropertyValue("--swipe-amount-x").replace("px", "") || 0); | ||
| const swipeAmountY = Number(toastRef?.style.getPropertyValue("--swipe-amount-y").replace("px", "") || 0); | ||
| const timeTaken = Math.max(1, (/* @__PURE__ */ new Date()).getTime() - (dragStartTime?.getTime() ?? 0)); | ||
| const swipeAmount = swipeDirection() === "x" ? swipeAmountX : swipeAmountY; | ||
| const velocity = Math.abs(swipeAmount) / timeTaken; | ||
| if (Math.abs(swipeAmount) >= SWIPE_THRESHOLD || velocity > 0.11) { | ||
| setOffsetBeforeRemove(offset()); | ||
| props.toast.onDismiss?.(props.toast); | ||
| if (swipeDirection() === "x") setSwipeOutDirection(swipeAmountX > 0 ? "right" : "left"); | ||
| else setSwipeOutDirection(swipeAmountY > 0 ? "down" : "up"); | ||
| deleteToast(); | ||
@@ -417,9 +629,14 @@ setSwipeOut(true); | ||
| } | ||
| toastRef?.style.setProperty("--swipe-amount", "0px"); | ||
| toastRef?.style.setProperty("--swipe-amount-x", "0px"); | ||
| toastRef?.style.setProperty("--swipe-amount-y", "0px"); | ||
| setIsSwiped(false); | ||
| setSwiping(false); | ||
| setSwipeDirection(null); | ||
| }; | ||
| _el$2.$$pointerdown = (event) => { | ||
| if (disabled()) return; | ||
| if (event.button === 2) return; | ||
| if (disabled() || !dismissible()) return; | ||
| dragStartTime = /* @__PURE__ */ new Date(); | ||
| setOffsetBeforeRemove(offset()); | ||
| event.target.setPointerCapture(event.pointerId); | ||
| event.currentTarget.setPointerCapture(event.pointerId); | ||
| if (event.target.tagName === "BUTTON") return; | ||
@@ -432,2 +649,7 @@ setSwiping(true); | ||
| }; | ||
| _el$2.addEventListener("dragend", () => { | ||
| setSwiping(false); | ||
| setSwipeDirection(null); | ||
| setPointerStartRef(null); | ||
| }); | ||
| var _ref$ = toastRef; | ||
@@ -437,74 +659,81 @@ typeof _ref$ === "function" ? use(_ref$, _el$2) : toastRef = _el$2; | ||
| get when() { | ||
| return props.closeButton && !props.toast.jsx; | ||
| return memo(() => !!(closeButton() && !props.toast.jsx))() && toastType() !== "loading"; | ||
| }, | ||
| get children() { | ||
| var _el$3 = _tmpl$22(); | ||
| addEventListener(_el$3, "click", disabled() ? void 0 : () => { | ||
| _el$3.$$click = () => { | ||
| if (disabled() || !dismissible()) return; | ||
| deleteToast(); | ||
| props.toast.onDismiss?.(props.toast); | ||
| }, true); | ||
| }; | ||
| insert(_el$3, () => props.icons?.close ?? createComponent(CloseIcon, {})); | ||
| effect((_p$) => { | ||
| var _v$ = disabled(), _v$2 = _cn(props.classes?.closeButton, props.toast?.classes?.closeButton); | ||
| _v$ !== _p$.e && setAttribute(_el$3, "data-disabled", _p$.e = _v$); | ||
| _v$2 !== _p$.t && className(_el$3, _p$.t = _v$2); | ||
| var _v$3 = props.closeButtonAriaLabel ?? "Close toast", _v$4 = disabled(), _v$5 = cn(classNames()?.closeButton, toastClassNames()?.closeButton); | ||
| _v$3 !== _p$.e && setAttribute(_el$3, "aria-label", _p$.e = _v$3); | ||
| _v$4 !== _p$.t && setAttribute(_el$3, "data-disabled", _p$.t = _v$4); | ||
| _v$5 !== _p$.a && className(_el$3, _p$.a = _v$5); | ||
| return _p$; | ||
| }, { | ||
| e: void 0, | ||
| t: void 0 | ||
| t: void 0, | ||
| a: void 0 | ||
| }); | ||
| return _el$3; | ||
| } | ||
| }), _el$5); | ||
| insert(_el$2, createComponent(Show, { | ||
| get when() { | ||
| return memo(() => !!((toastType() || props.toast.icon || props.toast.promise) && props.toast.icon !== null))() && (props.icons?.[toastType()] !== null || props.toast.icon); | ||
| }, | ||
| get children() { | ||
| var _el$4 = _tmpl$32(); | ||
| insert(_el$4, (() => { | ||
| var _c$ = memo(() => !!(props.toast.promise || props.toast.type === "loading" && !props.toast.icon)); | ||
| return () => _c$() ? props.toast.icon || getLoadingIcon() : null; | ||
| })(), null); | ||
| insert(_el$4, (() => { | ||
| var _c$2 = memo(() => props.toast.type !== "loading"); | ||
| return () => _c$2() ? icon() : null; | ||
| })(), null); | ||
| effect(() => className(_el$4, cn(classNames()?.icon, toastClassNames()?.icon))); | ||
| return _el$4; | ||
| } | ||
| }), _el$5); | ||
| insert(_el$6, (() => { | ||
| var _c$3 = memo(() => !!props.toast.jsx); | ||
| return () => _c$3() ? props.toast.jsx : resolveContent(props.toast.title); | ||
| })()); | ||
| insert(_el$5, createComponent(Show, { | ||
| get when() { | ||
| return props.toast.description; | ||
| }, | ||
| get children() { | ||
| var _el$7 = _tmpl$42(); | ||
| insert(_el$7, () => resolveContent(props.toast.description)); | ||
| effect(() => className(_el$7, cn(descriptionClassName(), toastDescriptionClassName(), classNames()?.description, toastClassNames()?.description))); | ||
| return _el$7; | ||
| } | ||
| }), null); | ||
| insert(_el$2, createComponent(Show, { | ||
| get when() { | ||
| return props.toast.jsx || props.toast.title instanceof Element; | ||
| return props.toast.cancel; | ||
| }, | ||
| get fallback() { | ||
| return [createComponent(Show, { | ||
| get when() { | ||
| return toastType() || props.toast.icon || props.toast.promise; | ||
| }, | ||
| get children() { | ||
| var _el$4 = _tmpl$42(); | ||
| insert(_el$4, (() => { | ||
| var _c$ = memo(() => !!(props.toast.promise || props.toast.type === "loading" && !props.toast.icon)); | ||
| return () => _c$() ? props.toast.icon || getLoadingIcon() : null; | ||
| })(), null); | ||
| insert(_el$4, (() => { | ||
| var _c$2 = memo(() => props.toast.type !== "loading"); | ||
| return () => _c$2() ? props.toast.icon || props.icons?.[toastType()] || getAsset(toastType())() : null; | ||
| })(), null); | ||
| return _el$4; | ||
| } | ||
| }), (() => { | ||
| var _el$5 = _tmpl$62(), _el$6 = _el$5.firstChild; | ||
| insert(_el$6, () => props.toast.title); | ||
| insert(_el$5, createComponent(Show, { | ||
| get when() { | ||
| return props.toast.description; | ||
| }, | ||
| get children() { | ||
| var _el$7 = _tmpl$52(); | ||
| insert(_el$7, () => props.toast.description); | ||
| effect(() => className(_el$7, _cn(props.descriptionClass, toastDescriptionClassname(), props.classes?.description, props.toast?.classes?.description))); | ||
| return _el$7; | ||
| } | ||
| }), null); | ||
| effect(() => className(_el$6, _cn(props.classes?.title, props.toast?.classes?.title))); | ||
| return _el$5; | ||
| })(), createComponent(Show, { | ||
| get when() { | ||
| return props.toast.cancel; | ||
| }, | ||
| get children() { | ||
| var _el$8 = _tmpl$72(); | ||
| _el$8.$$click = () => { | ||
| children: (cancel) => createComponent(Show, { | ||
| get when() { | ||
| return !isAction(cancel()); | ||
| }, | ||
| get fallback() { | ||
| return (() => { | ||
| var _el$8 = _tmpl$62(); | ||
| _el$8.$$click = (event) => { | ||
| const currentCancel = cancel(); | ||
| if (!dismissible()) return; | ||
| currentCancel.onClick?.(event); | ||
| deleteToast(); | ||
| if (props.toast.cancel?.onClick) props.toast.cancel.onClick(); | ||
| }; | ||
| insert(_el$8, () => props.toast.cancel.label); | ||
| insert(_el$8, () => cancel().label); | ||
| effect((_p$) => { | ||
| var _v$20 = props.toast.cancelButtonStyle || props.cancelButtonStyle, _v$21 = _cn(props.classes?.cancelButton, props.toast?.classes?.cancelButton); | ||
| _p$.e = style(_el$8, _v$20, _p$.e); | ||
| _v$21 !== _p$.t && className(_el$8, _p$.t = _v$21); | ||
| var _v$27 = props.toast.cancelButtonStyle ?? props.cancelButtonStyle, _v$28 = cn(classNames()?.cancelButton, toastClassNames()?.cancelButton); | ||
| _p$.e = style(_el$8, _v$27, _p$.e); | ||
| _v$28 !== _p$.t && className(_el$8, _p$.t = _v$28); | ||
| return _p$; | ||
@@ -516,19 +745,31 @@ }, { | ||
| return _el$8; | ||
| } | ||
| }), createComponent(Show, { | ||
| get when() { | ||
| return props.toast.action; | ||
| }, | ||
| get children() { | ||
| var _el$9 = _tmpl$8(); | ||
| })(); | ||
| }, | ||
| get children() { | ||
| return cancel(); | ||
| } | ||
| }) | ||
| }), null); | ||
| insert(_el$2, createComponent(Show, { | ||
| get when() { | ||
| return props.toast.action; | ||
| }, | ||
| children: (action) => createComponent(Show, { | ||
| get when() { | ||
| return !isAction(action()); | ||
| }, | ||
| get fallback() { | ||
| return (() => { | ||
| var _el$9 = _tmpl$72(); | ||
| _el$9.$$click = (event) => { | ||
| props.toast.action?.onClick(event); | ||
| const currentAction = action(); | ||
| currentAction.onClick?.(event); | ||
| if (event.defaultPrevented) return; | ||
| deleteToast(); | ||
| }; | ||
| insert(_el$9, () => props.toast.action.label); | ||
| insert(_el$9, () => action().label); | ||
| effect((_p$) => { | ||
| var _v$22 = props.toast.actionButtonStyle || props.actionButtonStyle, _v$23 = _cn(props.classes?.actionButton, props.toast?.classes?.actionButton); | ||
| _p$.e = style(_el$9, _v$22, _p$.e); | ||
| _v$23 !== _p$.t && className(_el$9, _p$.t = _v$23); | ||
| var _v$29 = props.toast.actionButtonStyle ?? props.actionButtonStyle, _v$30 = cn(classNames()?.actionButton, toastClassNames()?.actionButton); | ||
| _p$.e = style(_el$9, _v$29, _p$.e); | ||
| _v$30 !== _p$.t && className(_el$9, _p$.t = _v$30); | ||
| return _p$; | ||
@@ -540,11 +781,11 @@ }, { | ||
| return _el$9; | ||
| } | ||
| })]; | ||
| }, | ||
| get children() { | ||
| return props.toast.jsx || props.toast.title; | ||
| } | ||
| })(); | ||
| }, | ||
| get children() { | ||
| return action(); | ||
| } | ||
| }) | ||
| }), null); | ||
| effect((_p$) => { | ||
| var _v$3 = props.toast.important ? "assertive" : "polite", _v$4 = _cn(props.class, toastClassname(), props.classes?.toast, props.toast?.classes?.toast, props.classes?.default, props.classes?.[toastType()], props.toast?.classes?.[toastType()]), _v$5 = !(props.toast.jsx || props.toast.unstyled || props.unstyled), _v$6 = mounted(), _v$7 = Boolean(props.toast.promise), _v$8 = removed(), _v$9 = isVisible(), _v$10 = coords()[0], _v$11 = coords()[1], _v$12 = props.index, _v$13 = isFront(), _v$14 = swiping(), _v$15 = toastType(), _v$16 = invert(), _v$17 = swipeOut(), _v$18 = Boolean(props.expanded || props.expandByDefault && mounted()), _v$19 = { | ||
| var _v$6 = cn(className$1(), toastClassName(), classNames()?.toast, toastClassNames()?.toast, classNames()?.default, classNames()?.[toastType()], toastClassNames()?.[toastType()]), _v$7 = props.toast.richColors ?? props.defaultRichColors, _v$8 = !(props.toast.jsx || props.toast.unstyled || props.unstyled), _v$9 = mounted(), _v$0 = Boolean(props.toast.promise), _v$1 = isSwiped(), _v$10 = removed(), _v$11 = isVisible(), _v$12 = y(), _v$13 = x(), _v$14 = props.index, _v$15 = isFront(), _v$16 = swiping(), _v$17 = dismissible(), _v$18 = toastType(), _v$19 = invert(), _v$20 = swipeOut(), _v$21 = swipeOutDirection(), _v$22 = Boolean(props.expanded || props.expandByDefault && mounted()), _v$23 = props.toast.testId, _v$24 = { | ||
| "--index": props.index, | ||
@@ -557,20 +798,26 @@ "--toasts-before": props.index, | ||
| ...props.toast.style | ||
| }; | ||
| _v$3 !== _p$.e && setAttribute(_el$2, "aria-live", _p$.e = _v$3); | ||
| _v$4 !== _p$.t && className(_el$2, _p$.t = _v$4); | ||
| _v$5 !== _p$.a && setAttribute(_el$2, "data-styled", _p$.a = _v$5); | ||
| _v$6 !== _p$.o && setAttribute(_el$2, "data-mounted", _p$.o = _v$6); | ||
| _v$7 !== _p$.i && setAttribute(_el$2, "data-promise", _p$.i = _v$7); | ||
| _v$8 !== _p$.n && setAttribute(_el$2, "data-removed", _p$.n = _v$8); | ||
| _v$9 !== _p$.s && setAttribute(_el$2, "data-visible", _p$.s = _v$9); | ||
| _v$10 !== _p$.h && setAttribute(_el$2, "data-y-position", _p$.h = _v$10); | ||
| _v$11 !== _p$.r && setAttribute(_el$2, "data-x-position", _p$.r = _v$11); | ||
| _v$12 !== _p$.d && setAttribute(_el$2, "data-index", _p$.d = _v$12); | ||
| _v$13 !== _p$.l && setAttribute(_el$2, "data-front", _p$.l = _v$13); | ||
| _v$14 !== _p$.u && setAttribute(_el$2, "data-swiping", _p$.u = _v$14); | ||
| _v$15 !== _p$.c && setAttribute(_el$2, "data-type", _p$.c = _v$15); | ||
| _v$16 !== _p$.w && setAttribute(_el$2, "data-invert", _p$.w = _v$16); | ||
| _v$17 !== _p$.m && setAttribute(_el$2, "data-swipe-out", _p$.m = _v$17); | ||
| _v$18 !== _p$.f && setAttribute(_el$2, "data-expanded", _p$.f = _v$18); | ||
| _p$.y = style(_el$2, _v$19, _p$.y); | ||
| }, _v$25 = cn(classNames()?.content, toastClassNames()?.content), _v$26 = cn(classNames()?.title, toastClassNames()?.title); | ||
| _v$6 !== _p$.e && className(_el$2, _p$.e = _v$6); | ||
| _v$7 !== _p$.t && setAttribute(_el$2, "data-rich-colors", _p$.t = _v$7); | ||
| _v$8 !== _p$.a && setAttribute(_el$2, "data-styled", _p$.a = _v$8); | ||
| _v$9 !== _p$.o && setAttribute(_el$2, "data-mounted", _p$.o = _v$9); | ||
| _v$0 !== _p$.i && setAttribute(_el$2, "data-promise", _p$.i = _v$0); | ||
| _v$1 !== _p$.n && setAttribute(_el$2, "data-swiped", _p$.n = _v$1); | ||
| _v$10 !== _p$.s && setAttribute(_el$2, "data-removed", _p$.s = _v$10); | ||
| _v$11 !== _p$.h && setAttribute(_el$2, "data-visible", _p$.h = _v$11); | ||
| _v$12 !== _p$.r && setAttribute(_el$2, "data-y-position", _p$.r = _v$12); | ||
| _v$13 !== _p$.d && setAttribute(_el$2, "data-x-position", _p$.d = _v$13); | ||
| _v$14 !== _p$.l && setAttribute(_el$2, "data-index", _p$.l = _v$14); | ||
| _v$15 !== _p$.u && setAttribute(_el$2, "data-front", _p$.u = _v$15); | ||
| _v$16 !== _p$.c && setAttribute(_el$2, "data-swiping", _p$.c = _v$16); | ||
| _v$17 !== _p$.w && setAttribute(_el$2, "data-dismissible", _p$.w = _v$17); | ||
| _v$18 !== _p$.m && setAttribute(_el$2, "data-type", _p$.m = _v$18); | ||
| _v$19 !== _p$.f && setAttribute(_el$2, "data-invert", _p$.f = _v$19); | ||
| _v$20 !== _p$.y && setAttribute(_el$2, "data-swipe-out", _p$.y = _v$20); | ||
| _v$21 !== _p$.g && setAttribute(_el$2, "data-swipe-direction", _p$.g = _v$21); | ||
| _v$22 !== _p$.p && setAttribute(_el$2, "data-expanded", _p$.p = _v$22); | ||
| _v$23 !== _p$.b && setAttribute(_el$2, "data-testid", _p$.b = _v$23); | ||
| _p$.T = style(_el$2, _v$24, _p$.T); | ||
| _v$25 !== _p$.A && className(_el$5, _p$.A = _v$25); | ||
| _v$26 !== _p$.O && className(_el$6, _p$.O = _v$26); | ||
| return _p$; | ||
@@ -594,15 +841,14 @@ }, { | ||
| f: void 0, | ||
| y: void 0 | ||
| y: void 0, | ||
| g: void 0, | ||
| p: void 0, | ||
| b: void 0, | ||
| T: void 0, | ||
| A: void 0, | ||
| O: void 0 | ||
| }); | ||
| return _el$2; | ||
| })(); | ||
| }; | ||
| function getDocumentDirection() { | ||
| if (typeof window === "undefined") return "ltr"; | ||
| if (typeof document === "undefined") return "ltr"; | ||
| const dirAttribute = document.documentElement.getAttribute("dir"); | ||
| if (dirAttribute === "auto" || !dirAttribute) return window.getComputedStyle(document.documentElement).direction; | ||
| return dirAttribute; | ||
| } | ||
| var Toaster = (props) => { | ||
| function Toaster(props) { | ||
| const propsWithDefaults = mergeProps({ | ||
@@ -612,46 +858,66 @@ position: "bottom-right", | ||
| theme: "light", | ||
| gap: GAP, | ||
| visibleToasts: VISIBLE_TOASTS_AMOUNT, | ||
| dir: getDocumentDirection() | ||
| dir: getDocumentDirection(), | ||
| containerAriaLabel: "Notifications" | ||
| }, props); | ||
| const initialTheme = createToastTheme(propsWithDefaults.theme); | ||
| const [toastsStore, setToastsStore] = createStore({ | ||
| toasts: [] | ||
| }); | ||
| const possiblePositions = () => { | ||
| return Array.from(new Set([propsWithDefaults.position].concat(toastsStore.toasts.filter((toast2) => toast2.position).map((toast2) => toast2.position)))); | ||
| }; | ||
| const filteredToasts = createMemo(() => { | ||
| const toasts = toastsStore.toasts; | ||
| if (propsWithDefaults.id) return toasts.filter((toast2) => toast2.toasterId === propsWithDefaults.id); | ||
| return toasts.filter((toast2) => !toast2.toasterId); | ||
| }); | ||
| const possiblePositions = createMemo(() => { | ||
| return Array.from(/* @__PURE__ */ new Set([propsWithDefaults.position, ...filteredToasts().filter((toast2) => toast2.position).map((toast2) => toast2.position)])); | ||
| }); | ||
| const [heights, setHeights] = createSignal([]); | ||
| const [expanded, setExpanded] = createSignal(false); | ||
| const [interacting, setInteracting] = createSignal(false); | ||
| const [actualTheme, setActualTheme] = createSignal(initialTheme); | ||
| const [lastFocusedElementRef, setLastFocusedElementRef] = createSignal(null); | ||
| const [isFocusWithinRef, setIsFocusWithinRef] = createSignal(false); | ||
| let listRef; | ||
| const hotkeyLabel = () => propsWithDefaults.hotkey.join("+").replace(/Key/g, "").replace(/Digit/g, ""); | ||
| const [lastFocusedElementRef, setLastFocusedElementRef] = createSignal(null); | ||
| const [isFocusedWithinRef, setIsFocusedWithinRef] = createSignal(false); | ||
| const [actualTheme, setActualTheme] = createSignal(propsWithDefaults.theme !== "system" ? propsWithDefaults.theme : typeof window !== "undefined" ? window.matchMedia && window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light" : "light"); | ||
| const removeToast = (toast2) => setToastsStore("toasts", (toasts) => toasts.filter(({ | ||
| id | ||
| }) => id !== toast2.id)); | ||
| const className$1 = () => mergeClassName(propsWithDefaults.className, propsWithDefaults.class); | ||
| const toastOptions = () => propsWithDefaults.toastOptions; | ||
| const toastClassNames = () => mergeClassNames(toastOptions()?.classNames, toastOptions()?.classes); | ||
| const toastDescriptionClassName = () => mergeDescriptionClassName(toastOptions()?.descriptionClassName, toastOptions()?.descriptionClass); | ||
| const toastClassName = () => mergeClassName(toastOptions()?.className, toastOptions()?.class); | ||
| const removeToast = (toastToRemove) => { | ||
| setToastsStore("toasts", (toasts) => { | ||
| if (!toasts.find((toast2) => toast2.id === toastToRemove.id)?.delete) ToastState.dismiss(toastToRemove.id); | ||
| return toasts.filter(({ | ||
| id | ||
| }) => id !== toastToRemove.id); | ||
| }); | ||
| }; | ||
| onMount(() => { | ||
| const unsub = ToastState.subscribe((toast2) => { | ||
| if (toast2.dismiss) { | ||
| setToastsStore("toasts", produce((_toasts) => { | ||
| _toasts.forEach((t) => { | ||
| if (t.id === toast2.id) t.delete = true; | ||
| }); | ||
| })); | ||
| const unsubscribe = ToastState.subscribe((toastItem) => { | ||
| if (toastItem.dismiss) { | ||
| requestAnimationFrame(() => { | ||
| setToastsStore("toasts", produce((toasts) => { | ||
| toasts.forEach((toast2) => { | ||
| if (toast2.id === toastItem.id) toast2.delete = true; | ||
| }); | ||
| })); | ||
| }); | ||
| return; | ||
| } | ||
| const changedIndex = toastsStore.toasts.findIndex((t) => t.id === toast2.id); | ||
| if (changedIndex !== -1) { | ||
| setToastsStore("toasts", [changedIndex], reconcile(toast2)); | ||
| const nextToast = toastItem; | ||
| const indexOfExistingToast = toastsStore.toasts.findIndex((t) => t.id === nextToast.id); | ||
| if (indexOfExistingToast !== -1) { | ||
| setToastsStore("toasts", indexOfExistingToast, reconcile(nextToast)); | ||
| return; | ||
| } | ||
| setToastsStore("toasts", produce((_toasts) => { | ||
| _toasts.unshift(toast2); | ||
| setToastsStore("toasts", produce((toasts) => { | ||
| toasts.unshift(nextToast); | ||
| })); | ||
| }); | ||
| onCleanup(() => { | ||
| unsub(); | ||
| }); | ||
| onCleanup(unsubscribe); | ||
| }); | ||
| createEffect(on(() => propsWithDefaults.theme, (theme) => { | ||
| createEffect(() => { | ||
| const theme = propsWithDefaults.theme; | ||
| if (theme !== "system") { | ||
@@ -662,15 +928,23 @@ setActualTheme(theme); | ||
| if (typeof window === "undefined") return; | ||
| window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change", ({ | ||
| const darkMediaQuery = window.matchMedia("(prefers-color-scheme: dark)"); | ||
| const updateTheme = ({ | ||
| matches | ||
| }) => { | ||
| if (matches) setActualTheme("dark"); | ||
| else setActualTheme("light"); | ||
| }); | ||
| })); | ||
| setActualTheme(matches ? "dark" : "light"); | ||
| }; | ||
| updateTheme(darkMediaQuery); | ||
| try { | ||
| darkMediaQuery.addEventListener("change", updateTheme); | ||
| onCleanup(() => darkMediaQuery.removeEventListener("change", updateTheme)); | ||
| } catch { | ||
| darkMediaQuery.addListener(updateTheme); | ||
| onCleanup(() => darkMediaQuery.removeListener(updateTheme)); | ||
| } | ||
| }); | ||
| createEffect(() => { | ||
| if (toastsStore.toasts.length <= 1) setExpanded(false); | ||
| if (filteredToasts().length <= 1) setExpanded(false); | ||
| }); | ||
| onMount(() => { | ||
| const handleKeyDown = (event) => { | ||
| const isHotkeyPressed = propsWithDefaults.hotkey.every((key) => event[key] || event.code === key); | ||
| const isHotkeyPressed = propsWithDefaults.hotkey.length > 0 && propsWithDefaults.hotkey.every((key) => event[key] || event.code === key); | ||
| if (isHotkeyPressed) { | ||
@@ -683,51 +957,52 @@ setExpanded(true); | ||
| document.addEventListener("keydown", handleKeyDown); | ||
| onCleanup(() => { | ||
| document.removeEventListener("keydown", handleKeyDown); | ||
| }); | ||
| onCleanup(() => document.removeEventListener("keydown", handleKeyDown)); | ||
| }); | ||
| createEffect(on(() => listRef, (ref) => { | ||
| if (ref) { | ||
| onCleanup(() => { | ||
| if (lastFocusedElementRef()) { | ||
| lastFocusedElementRef()?.focus({ | ||
| preventScroll: true | ||
| }); | ||
| setLastFocusedElementRef(null); | ||
| setIsFocusedWithinRef(false); | ||
| } | ||
| onCleanup(() => { | ||
| if (lastFocusedElementRef()) { | ||
| lastFocusedElementRef()?.focus({ | ||
| preventScroll: true | ||
| }); | ||
| setLastFocusedElementRef(null); | ||
| setIsFocusWithinRef(false); | ||
| } | ||
| })); | ||
| return createComponent(Show, { | ||
| get when() { | ||
| return toastsStore.toasts.length > 0; | ||
| }, | ||
| get children() { | ||
| var _el$10 = _tmpl$9(); | ||
| insert(_el$10, createComponent(For, { | ||
| get each() { | ||
| return possiblePositions(); | ||
| }, | ||
| children: (position, index) => { | ||
| const [y, x] = position.split("-"); | ||
| return (() => { | ||
| var _el$11 = _tmpl$10(); | ||
| _el$11.$$pointerup = () => setInteracting(false); | ||
| _el$11.$$pointerdown = () => { | ||
| }); | ||
| return (() => { | ||
| var _el$0 = _tmpl$82(); | ||
| insert(_el$0, createComponent(For, { | ||
| get each() { | ||
| return possiblePositions(); | ||
| }, | ||
| children: (position, index) => { | ||
| const [y, x] = position.split("-"); | ||
| const toastsByPosition = createMemo(() => filteredToasts().filter((toast2) => !toast2.position && index() === 0 || toast2.position === position)); | ||
| const heightsByPosition = createMemo(() => heights().filter((height) => index() === 0 && !height.position || height.position === position)); | ||
| return createComponent(Show, { | ||
| get when() { | ||
| return filteredToasts().length > 0; | ||
| }, | ||
| get children() { | ||
| var _el$1 = _tmpl$9(); | ||
| _el$1.$$pointerup = () => setInteracting(false); | ||
| _el$1.$$pointerdown = (event) => { | ||
| const isNotDismissible = event.target instanceof HTMLElement && event.target.dataset.dismissible === "false"; | ||
| if (isNotDismissible) return; | ||
| setInteracting(true); | ||
| }; | ||
| _el$11.addEventListener("mouseleave", () => { | ||
| _el$1.addEventListener("dragend", () => setExpanded(false)); | ||
| _el$1.addEventListener("mouseleave", () => { | ||
| if (!interacting()) setExpanded(false); | ||
| }); | ||
| _el$11.$$mousemove = () => setExpanded(true); | ||
| _el$11.addEventListener("mouseenter", () => setExpanded(true)); | ||
| _el$11.addEventListener("focus", (event) => { | ||
| if (!isFocusedWithinRef()) { | ||
| setIsFocusedWithinRef(true); | ||
| _el$1.$$mousemove = () => setExpanded(true); | ||
| _el$1.addEventListener("mouseenter", () => setExpanded(true)); | ||
| _el$1.addEventListener("focus", (event) => { | ||
| const isNotDismissible = event.target instanceof HTMLElement && event.target.dataset.dismissible === "false"; | ||
| if (isNotDismissible) return; | ||
| if (!isFocusWithinRef()) { | ||
| setIsFocusWithinRef(true); | ||
| setLastFocusedElementRef(event.relatedTarget); | ||
| } | ||
| }); | ||
| _el$11.addEventListener("blur", (event) => { | ||
| if (isFocusedWithinRef() && !event.currentTarget.contains(event.relatedTarget)) { | ||
| setIsFocusedWithinRef(false); | ||
| _el$1.addEventListener("blur", (event) => { | ||
| if (isFocusWithinRef() && !event.currentTarget.contains(event.relatedTarget)) { | ||
| setIsFocusWithinRef(false); | ||
| if (lastFocusedElementRef()) { | ||
@@ -742,35 +1017,29 @@ lastFocusedElementRef()?.focus({ | ||
| var _ref$2 = listRef; | ||
| typeof _ref$2 === "function" ? use(_ref$2, _el$11) : listRef = _el$11; | ||
| setAttribute(_el$11, "data-y-position", y); | ||
| setAttribute(_el$11, "data-x-position", x); | ||
| insert(_el$11, createComponent(For, { | ||
| typeof _ref$2 === "function" ? use(_ref$2, _el$1) : listRef = _el$1; | ||
| setAttribute(_el$1, "data-y-position", y); | ||
| setAttribute(_el$1, "data-x-position", x); | ||
| insert(_el$1, createComponent(For, { | ||
| get each() { | ||
| return toastsStore.toasts.filter((toast2) => !toast2.position && index() === 0 || toast2.position === position); | ||
| return toastsByPosition(); | ||
| }, | ||
| children: (toast2, index2) => createComponent(Toast, { | ||
| get index() { | ||
| return index2(); | ||
| }, | ||
| children: (toastItem, toastIndex) => createComponent(Toast, { | ||
| get icons() { | ||
| return propsWithDefaults.icons; | ||
| }, | ||
| toast: toast2, | ||
| get index() { | ||
| return toastIndex(); | ||
| }, | ||
| toast: toastItem, | ||
| get defaultRichColors() { | ||
| return propsWithDefaults.richColors; | ||
| }, | ||
| get duration() { | ||
| return propsWithDefaults.toastOptions?.duration ?? props.duration; | ||
| return toastOptions()?.duration ?? propsWithDefaults.duration; | ||
| }, | ||
| get ["class"]() { | ||
| return propsWithDefaults.toastOptions?.class; | ||
| return toastClassName(); | ||
| }, | ||
| get classes() { | ||
| return propsWithDefaults.toastOptions?.classes; | ||
| get descriptionClassName() { | ||
| return toastDescriptionClassName(); | ||
| }, | ||
| get cancelButtonStyle() { | ||
| return propsWithDefaults.toastOptions?.cancelButtonStyle; | ||
| }, | ||
| get actionButtonStyle() { | ||
| return propsWithDefaults.toastOptions?.actionButtonStyle; | ||
| }, | ||
| get descriptionClass() { | ||
| return propsWithDefaults.toastOptions?.descriptionClass; | ||
| }, | ||
| get invert() { | ||
@@ -783,3 +1052,3 @@ return Boolean(propsWithDefaults.invert); | ||
| get closeButton() { | ||
| return Boolean(propsWithDefaults.closeButton); | ||
| return toastOptions()?.closeButton ?? propsWithDefaults.closeButton ?? false; | ||
| }, | ||
@@ -789,17 +1058,27 @@ get interacting() { | ||
| }, | ||
| get position() { | ||
| return propsWithDefaults.position; | ||
| }, | ||
| position, | ||
| get style() { | ||
| return propsWithDefaults.toastOptions?.style; | ||
| return toastOptions()?.style; | ||
| }, | ||
| get unstyled() { | ||
| return propsWithDefaults.toastOptions?.unstyled; | ||
| return toastOptions()?.unstyled; | ||
| }, | ||
| get classNames() { | ||
| return toastClassNames(); | ||
| }, | ||
| get cancelButtonStyle() { | ||
| return toastOptions()?.cancelButtonStyle; | ||
| }, | ||
| get actionButtonStyle() { | ||
| return toastOptions()?.actionButtonStyle; | ||
| }, | ||
| get closeButtonAriaLabel() { | ||
| return toastOptions()?.closeButtonAriaLabel; | ||
| }, | ||
| removeToast, | ||
| get toasts() { | ||
| return toastsStore.toasts; | ||
| return toastsByPosition(); | ||
| }, | ||
| get heights() { | ||
| return heights(); | ||
| return heightsByPosition(); | ||
| }, | ||
@@ -816,2 +1095,5 @@ setHeights, | ||
| }, | ||
| get swipeDirections() { | ||
| return propsWithDefaults.swipeDirections; | ||
| }, | ||
| get pauseWhenPageIsHidden() { | ||
@@ -823,14 +1105,12 @@ return propsWithDefaults.pauseWhenPageIsHidden; | ||
| effect((_p$) => { | ||
| var _v$24 = propsWithDefaults.dir === "auto" ? getDocumentDirection() : propsWithDefaults.dir, _v$25 = propsWithDefaults.class, _v$26 = actualTheme(), _v$27 = propsWithDefaults.richColors, _v$28 = { | ||
| "--front-toast-height": `${heights()[0]?.height}px`, | ||
| "--offset": typeof propsWithDefaults.offset === "number" ? `${propsWithDefaults.offset}px` : propsWithDefaults.offset || VIEWPORT_OFFSET, | ||
| "--width": `${TOAST_WIDTH}px`, | ||
| "--gap": `${GAP}px`, | ||
| ...propsWithDefaults.style | ||
| var _v$31 = propsWithDefaults.dir === "auto" ? getDocumentDirection() : propsWithDefaults.dir, _v$32 = className$1(), _v$33 = actualTheme(), _v$34 = { | ||
| "--front-toast-height": `${heightsByPosition()[0]?.height ?? 0}px`, | ||
| "--gap": `${propsWithDefaults.gap}px`, | ||
| ...propsWithDefaults.style, | ||
| ...assignOffset(propsWithDefaults.offset, propsWithDefaults.mobileOffset) | ||
| }; | ||
| _v$24 !== _p$.e && setAttribute(_el$11, "dir", _p$.e = _v$24); | ||
| _v$25 !== _p$.t && className(_el$11, _p$.t = _v$25); | ||
| _v$26 !== _p$.a && setAttribute(_el$11, "data-theme", _p$.a = _v$26); | ||
| _v$27 !== _p$.o && setAttribute(_el$11, "data-rich-colors", _p$.o = _v$27); | ||
| _p$.i = style(_el$11, _v$28, _p$.i); | ||
| _v$31 !== _p$.e && setAttribute(_el$1, "dir", _p$.e = _v$31); | ||
| _v$32 !== _p$.t && className(_el$1, _p$.t = _v$32); | ||
| _v$33 !== _p$.a && setAttribute(_el$1, "data-sonner-theme", _p$.a = _v$33); | ||
| _p$.o = style(_el$1, _v$34, _p$.o); | ||
| return _p$; | ||
@@ -841,14 +1121,13 @@ }, { | ||
| a: void 0, | ||
| o: void 0, | ||
| i: void 0 | ||
| o: void 0 | ||
| }); | ||
| return _el$11; | ||
| })(); | ||
| } | ||
| })); | ||
| effect(() => setAttribute(_el$10, "aria-label", `Notifications ${hotkeyLabel()}`)); | ||
| return _el$10; | ||
| } | ||
| }); | ||
| }; | ||
| return _el$1; | ||
| } | ||
| }); | ||
| } | ||
| })); | ||
| effect(() => setAttribute(_el$0, "aria-label", propsWithDefaults.customAriaLabel ?? `${propsWithDefaults.containerAriaLabel} ${hotkeyLabel()}`)); | ||
| return _el$0; | ||
| })(); | ||
| } | ||
| delegateEvents(["pointerdown", "pointerup", "pointermove", "click", "mousemove"]); | ||
@@ -863,2 +1142,2 @@ /*! | ||
| export { Toaster, toast }; | ||
| export { Toaster, toast, useSonner }; |
+623
-337
@@ -24,6 +24,6 @@ // #style-inject:#style-inject | ||
| // src/styles.css | ||
| styleInject(':where(html[dir=ltr]),\n:where([data-sonner-toaster][dir=ltr]) {\n --toast-icon-margin-start: -3px;\n --toast-icon-margin-end: 4px;\n --toast-svg-margin-start: -1px;\n --toast-svg-margin-end: 0px;\n --toast-button-margin-start: auto;\n --toast-button-margin-end: 0;\n --toast-close-button-start: 0;\n --toast-close-button-end: unset;\n --toast-close-button-transform: translate(-35%, -35%);\n}\n:where(html[dir=rtl]),\n:where([data-sonner-toaster][dir=rtl]) {\n --toast-icon-margin-start: 4px;\n --toast-icon-margin-end: -3px;\n --toast-svg-margin-start: 0px;\n --toast-svg-margin-end: -1px;\n --toast-button-margin-start: 0;\n --toast-button-margin-end: auto;\n --toast-close-button-start: unset;\n --toast-close-button-end: 0;\n --toast-close-button-transform: translate(35%, -35%);\n}\n:where([data-sonner-toaster]) {\n position: fixed;\n width: var(--width);\n font-family:\n ui-sans-serif,\n system-ui,\n -apple-system,\n BlinkMacSystemFont,\n Segoe UI,\n Roboto,\n Helvetica Neue,\n Arial,\n Noto Sans,\n sans-serif,\n Apple Color Emoji,\n Segoe UI Emoji,\n Segoe UI Symbol,\n Noto Color Emoji;\n --gray1: hsl(0, 0%, 99%);\n --gray2: hsl(0, 0%, 97.3%);\n --gray3: hsl(0, 0%, 95.1%);\n --gray4: hsl(0, 0%, 93%);\n --gray5: hsl(0, 0%, 90.9%);\n --gray6: hsl(0, 0%, 88.7%);\n --gray7: hsl(0, 0%, 85.8%);\n --gray8: hsl(0, 0%, 78%);\n --gray9: hsl(0, 0%, 56.1%);\n --gray10: hsl(0, 0%, 52.3%);\n --gray11: hsl(0, 0%, 43.5%);\n --gray12: hsl(0, 0%, 9%);\n --border-radius: 8px;\n box-sizing: border-box;\n padding: 0;\n margin: 0;\n list-style: none;\n outline: none;\n z-index: 999999999;\n}\n:where([data-sonner-toaster][data-x-position=right]) {\n right: max(var(--offset), env(safe-area-inset-right));\n}\n:where([data-sonner-toaster][data-x-position=left]) {\n left: max(var(--offset), env(safe-area-inset-left));\n}\n:where([data-sonner-toaster][data-x-position=center]) {\n left: 50%;\n transform: translateX(-50%);\n}\n:where([data-sonner-toaster][data-y-position=top]) {\n top: max(var(--offset), env(safe-area-inset-top));\n}\n:where([data-sonner-toaster][data-y-position=bottom]) {\n bottom: max(var(--offset), env(safe-area-inset-bottom));\n}\n:where([data-sonner-toast]) {\n --y: translateY(100%);\n --lift-amount: calc(var(--lift) * var(--gap));\n z-index: var(--z-index);\n position: absolute;\n opacity: 0;\n transform: var(--y);\n filter: blur(0);\n touch-action: none;\n transition:\n transform 400ms,\n opacity 400ms,\n height 400ms,\n box-shadow 200ms;\n box-sizing: border-box;\n outline: none;\n overflow-wrap: anywhere;\n}\n:where([data-sonner-toast][data-styled=true]) {\n padding: 16px;\n background: var(--normal-bg);\n border: 1px solid var(--normal-border);\n color: var(--normal-text);\n border-radius: var(--border-radius);\n box-shadow: 0px 4px 12px rgba(0, 0, 0, 0.1);\n width: var(--width);\n font-size: 13px;\n display: flex;\n align-items: center;\n gap: 6px;\n}\n:where([data-sonner-toast]:focus-visible) {\n box-shadow: 0px 4px 12px rgba(0, 0, 0, 0.1), 0 0 0 2px rgba(0, 0, 0, 0.2);\n}\n:where([data-sonner-toast][data-y-position=top]) {\n top: 0;\n --y: translateY(-100%);\n --lift: 1;\n --lift-amount: calc(1 * var(--gap));\n}\n:where([data-sonner-toast][data-y-position=bottom]) {\n bottom: 0;\n --y: translateY(100%);\n --lift: -1;\n --lift-amount: calc(var(--lift) * var(--gap));\n}\n:where([data-sonner-toast]) :where([data-description]) {\n font-weight: 400;\n line-height: 1.4;\n color: inherit;\n}\n:where([data-sonner-toast]) :where([data-title]) {\n font-weight: 500;\n line-height: 1.5;\n color: inherit;\n}\n:where([data-sonner-toast]) :where([data-icon]) {\n display: flex;\n height: 16px;\n width: 16px;\n position: relative;\n justify-content: flex-start;\n align-items: center;\n flex-shrink: 0;\n margin-left: var(--toast-icon-margin-start);\n margin-right: var(--toast-icon-margin-end);\n}\n:where([data-sonner-toast][data-promise=true]) :where([data-icon]) > svg {\n opacity: 0;\n transform: scale(0.8);\n transform-origin: center;\n animation: sonner-fade-in 300ms ease forwards;\n}\n:where([data-sonner-toast]) :where([data-icon]) > * {\n flex-shrink: 0;\n}\n:where([data-sonner-toast]) :where([data-icon]) svg {\n margin-left: var(--toast-svg-margin-start);\n margin-right: var(--toast-svg-margin-end);\n}\n:where([data-sonner-toast]) :where([data-content]) {\n display: flex;\n flex-direction: column;\n gap: 2px;\n}\n[data-sonner-toast][data-styled=true] [data-button] {\n border-radius: 4px;\n padding-left: 8px;\n padding-right: 8px;\n height: 24px;\n font-size: 12px;\n color: var(--normal-bg);\n background: var(--normal-text);\n margin-left: var(--toast-button-margin-start);\n margin-right: var(--toast-button-margin-end);\n border: none;\n cursor: pointer;\n outline: none;\n display: flex;\n align-items: center;\n flex-shrink: 0;\n transition: opacity 400ms, box-shadow 200ms;\n}\n:where([data-sonner-toast]) :where([data-button]):focus-visible {\n box-shadow: 0 0 0 2px rgba(0, 0, 0, 0.4);\n}\n:where([data-sonner-toast]) :where([data-button]):first-of-type {\n margin-left: var(--toast-button-margin-start);\n margin-right: var(--toast-button-margin-end);\n}\n:where([data-sonner-toast]) :where([data-cancel]) {\n color: var(--normal-text);\n background: rgba(0, 0, 0, 0.08);\n}\n:where([data-sonner-toast][data-theme=dark]) :where([data-cancel]) {\n background: rgba(255, 255, 255, 0.3);\n}\n:where([data-sonner-toast]) :where([data-close-button]) {\n position: absolute;\n left: var(--toast-close-button-start);\n right: var(--toast-close-button-end);\n top: 0;\n height: 20px;\n width: 20px;\n display: flex;\n justify-content: center;\n align-items: center;\n padding: 0;\n background: var(--gray1);\n color: var(--gray12);\n border: 1px solid var(--gray4);\n transform: var(--toast-close-button-transform);\n border-radius: 50%;\n cursor: pointer;\n z-index: 1;\n transition:\n opacity 100ms,\n background 200ms,\n border-color 200ms;\n}\n:where([data-sonner-toast]) :where([data-close-button]):focus-visible {\n box-shadow: 0px 4px 12px rgba(0, 0, 0, 0.1), 0 0 0 2px rgba(0, 0, 0, 0.2);\n}\n:where([data-sonner-toast]) :where([data-disabled=true]) {\n cursor: not-allowed;\n}\n:where([data-sonner-toast]):hover :where([data-close-button]):hover {\n background: var(--gray2);\n border-color: var(--gray5);\n}\n:where([data-sonner-toast][data-swiping=true])::before {\n content: "";\n position: absolute;\n left: 0;\n right: 0;\n height: 100%;\n z-index: -1;\n}\n:where([data-sonner-toast][data-y-position=top][data-swiping=true])::before {\n bottom: 50%;\n transform: scaleY(3) translateY(50%);\n}\n:where([data-sonner-toast][data-y-position=bottom][data-swiping=true])::before {\n top: 50%;\n transform: scaleY(3) translateY(-50%);\n}\n:where([data-sonner-toast][data-swiping=false][data-removed=true])::before {\n content: "";\n position: absolute;\n inset: 0;\n transform: scaleY(2);\n}\n:where([data-sonner-toast])::after {\n content: "";\n position: absolute;\n left: 0;\n height: calc(var(--gap) + 1px);\n bottom: 100%;\n width: 100%;\n}\n:where([data-sonner-toast][data-mounted=true]) {\n --y: translateY(0);\n opacity: 1;\n}\n:where([data-sonner-toast][data-expanded=false][data-front=false]) {\n --scale: var(--toasts-before) * 0.05 + 1;\n --y: translateY(calc(var(--lift-amount) * var(--toasts-before))) scale(calc(-1 * var(--scale)));\n height: var(--front-toast-height);\n}\n:where([data-sonner-toast]) > * {\n transition: opacity 400ms;\n}\n:where([data-sonner-toast][data-expanded=false][data-front=false][data-styled=true]) > * {\n opacity: 0;\n}\n:where([data-sonner-toast][data-visible=false]) {\n opacity: 0;\n pointer-events: none;\n}\n:where([data-sonner-toast][data-mounted=true][data-expanded=true]) {\n --y: translateY(calc(var(--lift) * var(--offset)));\n height: var(--initial-height);\n}\n:where([data-sonner-toast][data-removed=true][data-front=true][data-swipe-out=false]) {\n --y: translateY(calc(var(--lift) * -100%));\n opacity: 0;\n}\n:where([data-sonner-toast][data-removed=true][data-front=false][data-swipe-out=false][data-expanded=true]) {\n --y: translateY(calc(var(--lift) * var(--offset) + var(--lift) * -100%));\n opacity: 0;\n}\n:where([data-sonner-toast][data-removed=true][data-front=false][data-swipe-out=false][data-expanded=false]) {\n --y: translateY(40%);\n opacity: 0;\n transition: transform 500ms, opacity 200ms;\n}\n:where([data-sonner-toast][data-removed=true][data-front=false])::before {\n height: calc(var(--initial-height) + 20%);\n}\n[data-sonner-toast][data-swiping=true] {\n transform: var(--y) translateY(var(--swipe-amount, 0px));\n transition: none;\n}\n[data-sonner-toast][data-swipe-out=true][data-y-position=bottom],\n[data-sonner-toast][data-swipe-out=true][data-y-position=top] {\n animation: swipe-out 200ms ease-out forwards;\n}\n@keyframes swipe-out {\n from {\n transform: translateY(calc(var(--lift) * var(--offset) + var(--swipe-amount)));\n opacity: 1;\n }\n to {\n transform: translateY(calc(var(--lift) * var(--offset) + var(--swipe-amount) + var(--lift) * -100%));\n opacity: 0;\n }\n}\n@media (max-width: 600px) {\n [data-sonner-toaster] {\n position: fixed;\n --mobile-offset: 16px;\n right: var(--mobile-offset);\n left: var(--mobile-offset);\n width: 100%;\n }\n [data-sonner-toaster] [data-sonner-toast] {\n left: 0;\n right: 0;\n width: calc(100% - var(--mobile-offset) * 2);\n }\n [data-sonner-toaster][data-x-position=left] {\n left: var(--mobile-offset);\n }\n [data-sonner-toaster][data-y-position=bottom] {\n bottom: 20px;\n }\n [data-sonner-toaster][data-y-position=top] {\n top: 20px;\n }\n [data-sonner-toaster][data-x-position=center] {\n left: var(--mobile-offset);\n right: var(--mobile-offset);\n transform: none;\n }\n}\n[data-sonner-toaster][data-theme=light] {\n --normal-bg: #fff;\n --normal-border: var(--gray4);\n --normal-text: var(--gray12);\n --success-bg: hsl(143, 85%, 96%);\n --success-border: hsl(145, 92%, 91%);\n --success-text: hsl(140, 100%, 27%);\n --info-bg: hsl(208, 100%, 97%);\n --info-border: hsl(221, 91%, 91%);\n --info-text: hsl(210, 92%, 45%);\n --warning-bg: hsl(49, 100%, 97%);\n --warning-border: hsl(49, 91%, 91%);\n --warning-text: hsl(31, 92%, 45%);\n --error-bg: hsl(359, 100%, 97%);\n --error-border: hsl(359, 100%, 94%);\n --error-text: hsl(360, 100%, 45%);\n}\n[data-sonner-toaster][data-theme=light] [data-sonner-toast][data-invert=true] {\n --normal-bg: #000;\n --normal-border: hsl(0, 0%, 20%);\n --normal-text: var(--gray1);\n}\n[data-sonner-toaster][data-theme=dark] [data-sonner-toast][data-invert=true] {\n --normal-bg: #fff;\n --normal-border: var(--gray3);\n --normal-text: var(--gray12);\n}\n[data-sonner-toaster][data-theme=dark] {\n --normal-bg: #000;\n --normal-border: hsl(0, 0%, 20%);\n --normal-text: var(--gray1);\n --success-bg: hsl(150, 100%, 6%);\n --success-border: hsl(147, 100%, 12%);\n --success-text: hsl(150, 86%, 65%);\n --info-bg: hsl(215, 100%, 6%);\n --info-border: hsl(223, 100%, 12%);\n --info-text: hsl(216, 87%, 65%);\n --warning-bg: hsl(64, 100%, 6%);\n --warning-border: hsl(60, 100%, 12%);\n --warning-text: hsl(46, 87%, 65%);\n --error-bg: hsl(358, 76%, 10%);\n --error-border: hsl(357, 89%, 16%);\n --error-text: hsl(358, 100%, 81%);\n}\n[data-rich-colors=true] [data-sonner-toast][data-type=success] {\n background: var(--success-bg);\n border-color: var(--success-border);\n color: var(--success-text);\n}\n[data-rich-colors=true] [data-sonner-toast][data-type=success] [data-close-button] {\n background: var(--success-bg);\n border-color: var(--success-border);\n color: var(--success-text);\n}\n[data-rich-colors=true] [data-sonner-toast][data-type=info] {\n background: var(--info-bg);\n border-color: var(--info-border);\n color: var(--info-text);\n}\n[data-rich-colors=true] [data-sonner-toast][data-type=info] [data-close-button] {\n background: var(--info-bg);\n border-color: var(--info-border);\n color: var(--info-text);\n}\n[data-rich-colors=true] [data-sonner-toast][data-type=warning] {\n background: var(--warning-bg);\n border-color: var(--warning-border);\n color: var(--warning-text);\n}\n[data-rich-colors=true] [data-sonner-toast][data-type=warning] [data-close-button] {\n background: var(--warning-bg);\n border-color: var(--warning-border);\n color: var(--warning-text);\n}\n[data-rich-colors=true] [data-sonner-toast][data-type=error] {\n background: var(--error-bg);\n border-color: var(--error-border);\n color: var(--error-text);\n}\n[data-rich-colors=true] [data-sonner-toast][data-type=error] [data-close-button] {\n background: var(--error-bg);\n border-color: var(--error-border);\n color: var(--error-text);\n}\n.sonner-loading-wrapper {\n --size: 16px;\n height: var(--size);\n width: var(--size);\n position: absolute;\n inset: 0;\n z-index: 10;\n}\n.sonner-loading-wrapper[data-visible=false] {\n transform-origin: center;\n animation: sonner-fade-out 0.2s ease forwards;\n}\n.sonner-spinner {\n position: relative;\n top: 50%;\n left: 50%;\n height: var(--size);\n width: var(--size);\n}\n.sonner-loading-bar {\n animation: sonner-spin 1.2s linear infinite;\n background: var(--gray11);\n border-radius: 6px;\n height: 8%;\n left: -10%;\n position: absolute;\n top: -3.9%;\n width: 24%;\n}\n.sonner-loading-bar:nth-child(1) {\n animation-delay: -1.2s;\n transform: rotate(0.0001deg) translate(146%);\n}\n.sonner-loading-bar:nth-child(2) {\n animation-delay: -1.1s;\n transform: rotate(30deg) translate(146%);\n}\n.sonner-loading-bar:nth-child(3) {\n animation-delay: -1s;\n transform: rotate(60deg) translate(146%);\n}\n.sonner-loading-bar:nth-child(4) {\n animation-delay: -0.9s;\n transform: rotate(90deg) translate(146%);\n}\n.sonner-loading-bar:nth-child(5) {\n animation-delay: -0.8s;\n transform: rotate(120deg) translate(146%);\n}\n.sonner-loading-bar:nth-child(6) {\n animation-delay: -0.7s;\n transform: rotate(150deg) translate(146%);\n}\n.sonner-loading-bar:nth-child(7) {\n animation-delay: -0.6s;\n transform: rotate(180deg) translate(146%);\n}\n.sonner-loading-bar:nth-child(8) {\n animation-delay: -0.5s;\n transform: rotate(210deg) translate(146%);\n}\n.sonner-loading-bar:nth-child(9) {\n animation-delay: -0.4s;\n transform: rotate(240deg) translate(146%);\n}\n.sonner-loading-bar:nth-child(10) {\n animation-delay: -0.3s;\n transform: rotate(270deg) translate(146%);\n}\n.sonner-loading-bar:nth-child(11) {\n animation-delay: -0.2s;\n transform: rotate(300deg) translate(146%);\n}\n.sonner-loading-bar:nth-child(12) {\n animation-delay: -0.1s;\n transform: rotate(330deg) translate(146%);\n}\n@keyframes sonner-fade-in {\n 0% {\n opacity: 0;\n transform: scale(0.8);\n }\n 100% {\n opacity: 1;\n transform: scale(1);\n }\n}\n@keyframes sonner-fade-out {\n 0% {\n opacity: 1;\n transform: scale(1);\n }\n 100% {\n opacity: 0;\n transform: scale(0.8);\n }\n}\n@keyframes sonner-spin {\n 0% {\n opacity: 1;\n }\n 100% {\n opacity: 0.15;\n }\n}\n@media (prefers-reduced-motion) {\n [data-sonner-toast],\n [data-sonner-toast] > *,\n .sonner-loading-bar {\n transition: none !important;\n animation: none !important;\n }\n}\n.sonner-loader {\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n transform-origin: center;\n transition: opacity 200ms, transform 200ms;\n}\n.sonner-loader[data-visible=false] {\n opacity: 0;\n transform: scale(0.8) translate(-50%, -50%);\n}\n'); | ||
| styleInject('html[dir=ltr],\n[data-sonner-toaster][dir=ltr] {\n --toast-icon-margin-start: -3px;\n --toast-icon-margin-end: 4px;\n --toast-svg-margin-start: -1px;\n --toast-svg-margin-end: 0px;\n --toast-button-margin-start: auto;\n --toast-button-margin-end: 0;\n --toast-close-button-start: 0;\n --toast-close-button-end: unset;\n --toast-close-button-transform: translate(-35%, -35%);\n}\nhtml[dir=rtl],\n[data-sonner-toaster][dir=rtl] {\n --toast-icon-margin-start: 4px;\n --toast-icon-margin-end: -3px;\n --toast-svg-margin-start: 0px;\n --toast-svg-margin-end: -1px;\n --toast-button-margin-start: 0;\n --toast-button-margin-end: auto;\n --toast-close-button-start: unset;\n --toast-close-button-end: 0;\n --toast-close-button-transform: translate(35%, -35%);\n}\n[data-sonner-toaster] {\n position: fixed;\n width: var(--width);\n font-family:\n ui-sans-serif,\n system-ui,\n -apple-system,\n BlinkMacSystemFont,\n Segoe UI,\n Roboto,\n Helvetica Neue,\n Arial,\n Noto Sans,\n sans-serif,\n Apple Color Emoji,\n Segoe UI Emoji,\n Segoe UI Symbol,\n Noto Color Emoji;\n --gray1: hsl(0, 0%, 99%);\n --gray2: hsl(0, 0%, 97.3%);\n --gray3: hsl(0, 0%, 95.1%);\n --gray4: hsl(0, 0%, 93%);\n --gray5: hsl(0, 0%, 90.9%);\n --gray6: hsl(0, 0%, 88.7%);\n --gray7: hsl(0, 0%, 85.8%);\n --gray8: hsl(0, 0%, 78%);\n --gray9: hsl(0, 0%, 56.1%);\n --gray10: hsl(0, 0%, 52.3%);\n --gray11: hsl(0, 0%, 43.5%);\n --gray12: hsl(0, 0%, 9%);\n --border-radius: 8px;\n box-sizing: border-box;\n padding: 0;\n margin: 0;\n list-style: none;\n outline: none;\n z-index: 999999999;\n transition: transform 400ms ease;\n}\n@media (hover: none) and (pointer: coarse) {\n [data-sonner-toaster][data-lifted=true] {\n transform: none;\n }\n}\n[data-sonner-toaster][data-x-position=right] {\n right: var(--offset-right);\n}\n[data-sonner-toaster][data-x-position=left] {\n left: var(--offset-left);\n}\n[data-sonner-toaster][data-x-position=center] {\n left: 50%;\n transform: translateX(-50%);\n}\n[data-sonner-toaster][data-y-position=top] {\n top: var(--offset-top);\n}\n[data-sonner-toaster][data-y-position=bottom] {\n bottom: var(--offset-bottom);\n}\n[data-sonner-toast] {\n --y: translateY(100%);\n --lift-amount: calc(var(--lift) * var(--gap));\n z-index: var(--z-index);\n position: absolute;\n opacity: 0;\n transform: var(--y);\n touch-action: none;\n transition:\n transform 400ms,\n opacity 400ms,\n height 400ms,\n box-shadow 200ms;\n box-sizing: border-box;\n outline: none;\n overflow-wrap: anywhere;\n}\n[data-sonner-toast][data-styled=true] {\n padding: 16px;\n background: var(--normal-bg);\n border: 1px solid var(--normal-border);\n color: var(--normal-text);\n border-radius: var(--border-radius);\n box-shadow: 0px 4px 12px rgba(0, 0, 0, 0.1);\n width: var(--width);\n font-size: 13px;\n display: flex;\n align-items: center;\n gap: 6px;\n}\n[data-sonner-toast]:focus-visible {\n box-shadow: 0px 4px 12px rgba(0, 0, 0, 0.1), 0 0 0 2px rgba(0, 0, 0, 0.2);\n}\n[data-sonner-toast][data-y-position=top] {\n top: 0;\n --y: translateY(-100%);\n --lift: 1;\n --lift-amount: calc(1 * var(--gap));\n}\n[data-sonner-toast][data-y-position=bottom] {\n bottom: 0;\n --y: translateY(100%);\n --lift: -1;\n --lift-amount: calc(var(--lift) * var(--gap));\n}\n[data-sonner-toast][data-styled=true] [data-description] {\n font-weight: 400;\n line-height: 1.4;\n color: #3f3f3f;\n}\n[data-rich-colors=true][data-sonner-toast][data-styled=true] [data-description] {\n color: inherit;\n}\n[data-sonner-toaster][data-sonner-theme=dark] [data-description] {\n color: hsl(0, 0%, 91%);\n}\n[data-sonner-toast][data-styled=true] [data-title] {\n font-weight: 500;\n line-height: 1.5;\n color: inherit;\n}\n[data-sonner-toast][data-styled=true] [data-icon] {\n display: flex;\n height: 16px;\n width: 16px;\n position: relative;\n justify-content: flex-start;\n align-items: center;\n flex-shrink: 0;\n margin-left: var(--toast-icon-margin-start);\n margin-right: var(--toast-icon-margin-end);\n}\n[data-sonner-toast][data-promise=true] [data-icon] > svg {\n opacity: 0;\n transform: scale(0.8);\n transform-origin: center;\n animation: sonner-fade-in 300ms ease forwards;\n}\n[data-sonner-toast][data-styled=true] [data-icon] > * {\n flex-shrink: 0;\n}\n[data-sonner-toast][data-styled=true] [data-icon] svg {\n margin-left: var(--toast-svg-margin-start);\n margin-right: var(--toast-svg-margin-end);\n}\n[data-sonner-toast][data-styled=true] [data-content] {\n display: flex;\n flex-direction: column;\n gap: 2px;\n}\n[data-sonner-toast][data-styled=true] [data-button] {\n border-radius: 4px;\n padding-left: 8px;\n padding-right: 8px;\n height: 24px;\n font-size: 12px;\n color: var(--normal-bg);\n background: var(--normal-text);\n margin-left: var(--toast-button-margin-start);\n margin-right: var(--toast-button-margin-end);\n border: none;\n font-weight: 500;\n cursor: pointer;\n outline: none;\n display: flex;\n align-items: center;\n flex-shrink: 0;\n transition: opacity 400ms, box-shadow 200ms;\n}\n[data-sonner-toast][data-styled=true] [data-button]:focus-visible {\n box-shadow: 0 0 0 2px rgba(0, 0, 0, 0.4);\n}\n[data-sonner-toast][data-styled=true] [data-button]:first-of-type {\n margin-left: var(--toast-button-margin-start);\n margin-right: var(--toast-button-margin-end);\n}\n[data-sonner-toast][data-styled=true] [data-cancel] {\n color: var(--normal-text);\n background: rgba(0, 0, 0, 0.08);\n}\n[data-sonner-toaster][data-sonner-theme=dark] [data-sonner-toast][data-styled=true] [data-cancel] {\n background: rgba(255, 255, 255, 0.3);\n}\n[data-sonner-toast][data-styled=true] [data-close-button] {\n position: absolute;\n left: var(--toast-close-button-start);\n right: var(--toast-close-button-end);\n top: 0;\n height: 20px;\n width: 20px;\n display: flex;\n justify-content: center;\n align-items: center;\n padding: 0;\n color: var(--normal-text);\n background: var(--normal-bg);\n border: 1px solid var(--normal-border);\n transform: var(--toast-close-button-transform);\n border-radius: 50%;\n cursor: pointer;\n z-index: 1;\n transition:\n opacity 100ms,\n background 200ms,\n border-color 200ms;\n}\n[data-sonner-toast][data-styled=true] [data-close-button]:focus-visible {\n box-shadow: 0px 4px 12px rgba(0, 0, 0, 0.1), 0 0 0 2px rgba(0, 0, 0, 0.2);\n}\n[data-sonner-toast][data-styled=true] [data-disabled=true] {\n cursor: not-allowed;\n}\n[data-sonner-toast][data-styled=true]:hover [data-close-button]:hover {\n background: var(--gray2);\n border-color: var(--gray5);\n}\n[data-sonner-toast][data-swiping=true]::before {\n content: "";\n position: absolute;\n left: -100%;\n right: -100%;\n height: 100%;\n z-index: -1;\n}\n[data-sonner-toast][data-y-position=top][data-swiping=true]::before {\n bottom: 50%;\n transform: scaleY(3) translateY(50%);\n}\n[data-sonner-toast][data-y-position=bottom][data-swiping=true]::before {\n top: 50%;\n transform: scaleY(3) translateY(-50%);\n}\n[data-sonner-toast][data-swiping=false][data-removed=true]::before {\n content: "";\n position: absolute;\n inset: 0;\n transform: scaleY(2);\n}\n[data-sonner-toast][data-expanded=true]::after {\n content: "";\n position: absolute;\n left: 0;\n height: calc(var(--gap) + 1px);\n bottom: 100%;\n width: 100%;\n}\n[data-sonner-toast][data-mounted=true] {\n --y: translateY(0);\n opacity: 1;\n}\n[data-sonner-toast][data-expanded=false][data-front=false] {\n --scale: var(--toasts-before) * 0.05 + 1;\n --y: translateY(calc(var(--lift-amount) * var(--toasts-before))) scale(calc(-1 * var(--scale)));\n height: var(--front-toast-height);\n}\n[data-sonner-toast] > * {\n transition: opacity 400ms;\n}\n[data-sonner-toast][data-x-position=right] {\n right: 0;\n}\n[data-sonner-toast][data-x-position=left] {\n left: 0;\n}\n[data-sonner-toast][data-expanded=false][data-front=false][data-styled=true] > * {\n opacity: 0;\n}\n[data-sonner-toast][data-visible=false] {\n opacity: 0;\n pointer-events: none;\n}\n[data-sonner-toast][data-mounted=true][data-expanded=true] {\n --y: translateY(calc(var(--lift) * var(--offset)));\n height: var(--initial-height);\n}\n[data-sonner-toast][data-removed=true][data-front=true][data-swipe-out=false] {\n --y: translateY(calc(var(--lift) * -100%));\n opacity: 0;\n}\n[data-sonner-toast][data-removed=true][data-front=false][data-swipe-out=false][data-expanded=true] {\n --y: translateY(calc(var(--lift) * var(--offset) + var(--lift) * -100%));\n opacity: 0;\n}\n[data-sonner-toast][data-removed=true][data-front=false][data-swipe-out=false][data-expanded=false] {\n --y: translateY(40%);\n opacity: 0;\n transition: transform 500ms, opacity 200ms;\n}\n[data-sonner-toast][data-removed=true][data-front=false]::before {\n height: calc(var(--initial-height) + 20%);\n}\n[data-sonner-toast][data-swiping=true] {\n transform: var(--y) translateY(var(--swipe-amount-y, 0px)) translateX(var(--swipe-amount-x, 0px));\n transition: none;\n}\n[data-sonner-toast][data-swiped=true] {\n -webkit-user-select: none;\n user-select: none;\n}\n[data-sonner-toast][data-swipe-out=true][data-y-position=bottom],\n[data-sonner-toast][data-swipe-out=true][data-y-position=top] {\n animation-duration: 200ms;\n animation-timing-function: ease-out;\n animation-fill-mode: forwards;\n}\n[data-sonner-toast][data-swipe-out=true][data-swipe-direction=left] {\n animation-name: swipe-out-left;\n}\n[data-sonner-toast][data-swipe-out=true][data-swipe-direction=right] {\n animation-name: swipe-out-right;\n}\n[data-sonner-toast][data-swipe-out=true][data-swipe-direction=up] {\n animation-name: swipe-out-up;\n}\n[data-sonner-toast][data-swipe-out=true][data-swipe-direction=down] {\n animation-name: swipe-out-down;\n}\n@keyframes swipe-out-left {\n from {\n transform: var(--y) translateX(var(--swipe-amount-x));\n opacity: 1;\n }\n to {\n transform: var(--y) translateX(calc(var(--swipe-amount-x) - 100%));\n opacity: 0;\n }\n}\n@keyframes swipe-out-right {\n from {\n transform: var(--y) translateX(var(--swipe-amount-x));\n opacity: 1;\n }\n to {\n transform: var(--y) translateX(calc(var(--swipe-amount-x) + 100%));\n opacity: 0;\n }\n}\n@keyframes swipe-out-up {\n from {\n transform: var(--y) translateY(var(--swipe-amount-y));\n opacity: 1;\n }\n to {\n transform: var(--y) translateY(calc(var(--swipe-amount-y) - 100%));\n opacity: 0;\n }\n}\n@keyframes swipe-out-down {\n from {\n transform: var(--y) translateY(var(--swipe-amount-y));\n opacity: 1;\n }\n to {\n transform: var(--y) translateY(calc(var(--swipe-amount-y) + 100%));\n opacity: 0;\n }\n}\n@media (max-width: 600px) {\n [data-sonner-toaster] {\n position: fixed;\n right: var(--mobile-offset-right);\n left: var(--mobile-offset-left);\n width: 100%;\n }\n [data-sonner-toaster][dir=rtl] {\n left: calc(var(--mobile-offset-left) * -1);\n }\n [data-sonner-toaster] [data-sonner-toast] {\n left: 0;\n right: 0;\n width: calc(100% - var(--mobile-offset-left) * 2);\n }\n [data-sonner-toaster][data-x-position=left] {\n left: var(--mobile-offset-left);\n }\n [data-sonner-toaster][data-y-position=bottom] {\n bottom: var(--mobile-offset-bottom);\n }\n [data-sonner-toaster][data-y-position=top] {\n top: var(--mobile-offset-top);\n }\n [data-sonner-toaster][data-x-position=center] {\n left: var(--mobile-offset-left);\n right: var(--mobile-offset-right);\n transform: none;\n }\n}\n[data-sonner-toaster][data-sonner-theme=light] {\n --normal-bg: #fff;\n --normal-border: var(--gray4);\n --normal-text: var(--gray12);\n --success-bg: hsl(143, 85%, 96%);\n --success-border: hsl(145, 92%, 87%);\n --success-text: hsl(140, 100%, 27%);\n --info-bg: hsl(208, 100%, 97%);\n --info-border: hsl(221, 91%, 93%);\n --info-text: hsl(210, 92%, 45%);\n --warning-bg: hsl(49, 100%, 97%);\n --warning-border: hsl(49, 91%, 84%);\n --warning-text: hsl(31, 92%, 45%);\n --error-bg: hsl(359, 100%, 97%);\n --error-border: hsl(359, 100%, 94%);\n --error-text: hsl(360, 100%, 45%);\n}\n[data-sonner-toaster][data-sonner-theme=light] [data-sonner-toast][data-invert=true] {\n --normal-bg: #000;\n --normal-border: hsl(0, 0%, 20%);\n --normal-text: var(--gray1);\n}\n[data-sonner-toaster][data-sonner-theme=dark] [data-sonner-toast][data-invert=true] {\n --normal-bg: #fff;\n --normal-border: var(--gray3);\n --normal-text: var(--gray12);\n}\n[data-sonner-toaster][data-sonner-theme=dark] {\n --normal-bg: #000;\n --normal-bg-hover: hsl(0, 0%, 12%);\n --normal-border: hsl(0, 0%, 20%);\n --normal-border-hover: hsl(0, 0%, 25%);\n --normal-text: var(--gray1);\n --success-bg: hsl(150, 100%, 6%);\n --success-border: hsl(147, 100%, 12%);\n --success-text: hsl(150, 86%, 65%);\n --info-bg: hsl(215, 100%, 6%);\n --info-border: hsl(223, 43%, 17%);\n --info-text: hsl(216, 87%, 65%);\n --warning-bg: hsl(64, 100%, 6%);\n --warning-border: hsl(60, 100%, 9%);\n --warning-text: hsl(46, 87%, 65%);\n --error-bg: hsl(358, 76%, 10%);\n --error-border: hsl(357, 89%, 16%);\n --error-text: hsl(358, 100%, 81%);\n}\n[data-sonner-toaster][data-sonner-theme=dark] [data-sonner-toast] [data-close-button] {\n background: var(--normal-bg);\n border-color: var(--normal-border);\n color: var(--normal-text);\n}\n[data-sonner-toaster][data-sonner-theme=dark] [data-sonner-toast] [data-close-button]:hover {\n background: var(--normal-bg-hover);\n border-color: var(--normal-border-hover);\n}\n[data-rich-colors=true][data-sonner-toast][data-type=success] {\n background: var(--success-bg);\n border-color: var(--success-border);\n color: var(--success-text);\n}\n[data-rich-colors=true][data-sonner-toast][data-type=success] [data-close-button] {\n background: var(--success-bg);\n border-color: var(--success-border);\n color: var(--success-text);\n}\n[data-rich-colors=true][data-sonner-toast][data-type=info] {\n background: var(--info-bg);\n border-color: var(--info-border);\n color: var(--info-text);\n}\n[data-rich-colors=true][data-sonner-toast][data-type=info] [data-close-button] {\n background: var(--info-bg);\n border-color: var(--info-border);\n color: var(--info-text);\n}\n[data-rich-colors=true][data-sonner-toast][data-type=warning] {\n background: var(--warning-bg);\n border-color: var(--warning-border);\n color: var(--warning-text);\n}\n[data-rich-colors=true][data-sonner-toast][data-type=warning] [data-close-button] {\n background: var(--warning-bg);\n border-color: var(--warning-border);\n color: var(--warning-text);\n}\n[data-rich-colors=true][data-sonner-toast][data-type=error] {\n background: var(--error-bg);\n border-color: var(--error-border);\n color: var(--error-text);\n}\n[data-rich-colors=true][data-sonner-toast][data-type=error] [data-close-button] {\n background: var(--error-bg);\n border-color: var(--error-border);\n color: var(--error-text);\n}\n.sonner-loading-wrapper {\n --size: 16px;\n height: var(--size);\n width: var(--size);\n position: absolute;\n inset: 0;\n z-index: 10;\n}\n.sonner-loading-wrapper[data-visible=false] {\n transform-origin: center;\n animation: sonner-fade-out 0.2s ease forwards;\n}\n.sonner-spinner {\n position: relative;\n top: 50%;\n left: 50%;\n height: var(--size);\n width: var(--size);\n}\n.sonner-loading-bar {\n animation: sonner-spin 1.2s linear infinite;\n background: var(--gray11);\n border-radius: 6px;\n height: 8%;\n left: -10%;\n position: absolute;\n top: -3.9%;\n width: 24%;\n}\n.sonner-loading-bar:nth-child(1) {\n animation-delay: -1.2s;\n transform: rotate(0.0001deg) translate(146%);\n}\n.sonner-loading-bar:nth-child(2) {\n animation-delay: -1.1s;\n transform: rotate(30deg) translate(146%);\n}\n.sonner-loading-bar:nth-child(3) {\n animation-delay: -1s;\n transform: rotate(60deg) translate(146%);\n}\n.sonner-loading-bar:nth-child(4) {\n animation-delay: -0.9s;\n transform: rotate(90deg) translate(146%);\n}\n.sonner-loading-bar:nth-child(5) {\n animation-delay: -0.8s;\n transform: rotate(120deg) translate(146%);\n}\n.sonner-loading-bar:nth-child(6) {\n animation-delay: -0.7s;\n transform: rotate(150deg) translate(146%);\n}\n.sonner-loading-bar:nth-child(7) {\n animation-delay: -0.6s;\n transform: rotate(180deg) translate(146%);\n}\n.sonner-loading-bar:nth-child(8) {\n animation-delay: -0.5s;\n transform: rotate(210deg) translate(146%);\n}\n.sonner-loading-bar:nth-child(9) {\n animation-delay: -0.4s;\n transform: rotate(240deg) translate(146%);\n}\n.sonner-loading-bar:nth-child(10) {\n animation-delay: -0.3s;\n transform: rotate(270deg) translate(146%);\n}\n.sonner-loading-bar:nth-child(11) {\n animation-delay: -0.2s;\n transform: rotate(300deg) translate(146%);\n}\n.sonner-loading-bar:nth-child(12) {\n animation-delay: -0.1s;\n transform: rotate(330deg) translate(146%);\n}\n@keyframes sonner-fade-in {\n 0% {\n opacity: 0;\n transform: scale(0.8);\n }\n 100% {\n opacity: 1;\n transform: scale(1);\n }\n}\n@keyframes sonner-fade-out {\n 0% {\n opacity: 1;\n transform: scale(1);\n }\n 100% {\n opacity: 0;\n transform: scale(0.8);\n }\n}\n@keyframes sonner-spin {\n 0% {\n opacity: 1;\n }\n 100% {\n opacity: 0.15;\n }\n}\n@media (prefers-reduced-motion) {\n [data-sonner-toast],\n [data-sonner-toast] > *,\n .sonner-loading-bar {\n transition: none !important;\n animation: none !important;\n }\n}\n.sonner-loader {\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n transform-origin: center;\n transition: opacity 200ms, transform 200ms;\n}\n.sonner-loader[data-visible=false] {\n opacity: 0;\n transform: scale(0.8) translate(-50%, -50%);\n}\n'); | ||
| // src/index.tsx | ||
| import { For as For2, Show, createEffect, createSignal as createSignal2, mergeProps, on, onCleanup as onCleanup2, onMount as onMount2 } from "solid-js"; | ||
| import { For as For2, Show, createEffect, createMemo, createSignal as createSignal2, mergeProps, on, onCleanup as onCleanup2, onMount as onMount2 } from "solid-js"; | ||
| import { createStore, produce, reconcile } from "solid-js/store"; | ||
@@ -35,31 +35,43 @@ | ||
| function Loader(props) { | ||
| return <div class="sonner-loading-wrapper" data-visible={props.visible}><div class="sonner-spinner"><For each={bars}>{() => <div class="sonner-loading-bar" />}</For></div></div>; | ||
| return <div class={["sonner-loading-wrapper", props.class ?? props.className].filter(Boolean).join(" ")} data-visible={props.visible}> | ||
| <div class="sonner-spinner"> | ||
| <For each={bars}>{(_, i) => <div class="sonner-loading-bar" data-index={i()} />}</For> | ||
| </div> | ||
| </div>; | ||
| } | ||
| function SuccessIcon() { | ||
| return <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" height="20" width="20"><path | ||
| return <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" height="20" width="20"> | ||
| <path | ||
| fill-rule="evenodd" | ||
| d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.857-9.809a.75.75 0 00-1.214-.882l-3.483 4.79-1.88-1.88a.75.75 0 10-1.06 1.061l2.5 2.5a.75.75 0 001.137-.089l4-5.5z" | ||
| clip-rule="evenodd" | ||
| /></svg>; | ||
| /> | ||
| </svg>; | ||
| } | ||
| function WarningIcon() { | ||
| return <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" height="20" width="20"><path | ||
| return <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" height="20" width="20"> | ||
| <path | ||
| fill-rule="evenodd" | ||
| d="M9.401 3.003c1.155-2 4.043-2 5.197 0l7.355 12.748c1.154 2-.29 4.5-2.599 4.5H4.645c-2.309 0-3.752-2.5-2.598-4.5L9.4 3.003zM12 8.25a.75.75 0 01.75.75v3.75a.75.75 0 01-1.5 0V9a.75.75 0 01.75-.75zm0 8.25a.75.75 0 100-1.5.75.75 0 000 1.5z" | ||
| clip-rule="evenodd" | ||
| /></svg>; | ||
| /> | ||
| </svg>; | ||
| } | ||
| function InfoIcon() { | ||
| return <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" height="20" width="20"><path | ||
| return <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" height="20" width="20"> | ||
| <path | ||
| fill-rule="evenodd" | ||
| d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a.75.75 0 000 1.5h.253a.25.25 0 01.244.304l-.459 2.066A1.75 1.75 0 0010.747 15H11a.75.75 0 000-1.5h-.253a.25.25 0 01-.244-.304l.459-2.066A1.75 1.75 0 009.253 9H9z" | ||
| clip-rule="evenodd" | ||
| /></svg>; | ||
| /> | ||
| </svg>; | ||
| } | ||
| function ErrorIcon() { | ||
| return <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" height="20" width="20"><path | ||
| return <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" height="20" width="20"> | ||
| <path | ||
| fill-rule="evenodd" | ||
| d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-8-5a.75.75 0 01.75.75v4.5a.75.75 0 01-1.5 0v-4.5A.75.75 0 0110 5zm0 10a1 1 0 100-2 1 1 0 000 2z" | ||
| clip-rule="evenodd" | ||
| /></svg>; | ||
| /> | ||
| </svg>; | ||
| } | ||
@@ -69,9 +81,9 @@ function getAsset(type) { | ||
| case "success": | ||
| return SuccessIcon; | ||
| return <SuccessIcon />; | ||
| case "info": | ||
| return InfoIcon; | ||
| return <InfoIcon />; | ||
| case "warning": | ||
| return WarningIcon; | ||
| return <WarningIcon />; | ||
| case "error": | ||
| return ErrorIcon; | ||
| return <ErrorIcon />; | ||
| default: | ||
@@ -81,13 +93,46 @@ return null; | ||
| } | ||
| function CloseIcon() { | ||
| return <svg | ||
| xmlns="http://www.w3.org/2000/svg" | ||
| width="12" | ||
| height="12" | ||
| viewBox="0 0 24 24" | ||
| fill="none" | ||
| stroke="currentColor" | ||
| stroke-width="1.5" | ||
| stroke-linecap="round" | ||
| stroke-linejoin="round" | ||
| > | ||
| <line x1="18" y1="6" x2="6" y2="18" /> | ||
| <line x1="6" y1="6" x2="18" y2="18" /> | ||
| </svg>; | ||
| } | ||
| // src/primitives.ts | ||
| import { createSignal, onCleanup, onMount } from "solid-js"; | ||
| function useIsDocumentHidden() { | ||
| const [isDocumentHidden, setIsDocumentHidden] = createSignal(typeof document !== "undefined" ? document.hidden : false); | ||
| onMount(() => { | ||
| const callback = () => { | ||
| setIsDocumentHidden(document.hidden); | ||
| }; | ||
| document.addEventListener("visibilitychange", callback); | ||
| onCleanup(() => { | ||
| document.removeEventListener("visibilitychange", callback); | ||
| }); | ||
| }); | ||
| return isDocumentHidden; | ||
| } | ||
| // src/state.ts | ||
| var toastsCounter = 0; | ||
| var toastsCounter = 1; | ||
| var Observer = class { | ||
| subscribers; | ||
| toasts; | ||
| dismissedToasts; | ||
| constructor() { | ||
| this.subscribers = []; | ||
| this.toasts = []; | ||
| this.dismissedToasts = /* @__PURE__ */ new Set(); | ||
| } | ||
| // We use arrow functions to maintain the correct `this` reference | ||
| subscribe = (subscriber) => { | ||
@@ -109,11 +154,21 @@ this.subscribers.push(subscriber); | ||
| const { message, ...rest } = data; | ||
| const id = typeof data?.id === "number" || data.id && data.id?.length > 0 ? data.id : toastsCounter++; | ||
| const hasStringId = typeof data.id === "string" && data.id.length > 0; | ||
| const id = typeof data.id === "number" || hasStringId ? data.id : toastsCounter++; | ||
| const alreadyExists = this.toasts.find((toast2) => { | ||
| return toast2.id === id; | ||
| }); | ||
| const dismissible = data.dismissible === void 0 ? true : data.dismissible; | ||
| if (this.dismissedToasts.has(id)) | ||
| this.dismissedToasts.delete(id); | ||
| if (alreadyExists) { | ||
| this.toasts = this.toasts.map((toast2) => { | ||
| if (toast2.id === id) { | ||
| this.publish({ ...toast2, ...data, id, title: message }); | ||
| return { ...toast2, ...data, id, title: message }; | ||
| this.publish({ ...toast2, ...data, id, title: message, dismissible }); | ||
| return { | ||
| ...toast2, | ||
| ...data, | ||
| id, | ||
| dismissible, | ||
| title: message | ||
| }; | ||
| } | ||
@@ -123,3 +178,3 @@ return toast2; | ||
| } else { | ||
| this.addToast({ title: message, ...rest, id }); | ||
| this.addToast({ title: message, ...rest, dismissible, id }); | ||
| } | ||
@@ -129,8 +184,13 @@ return id; | ||
| dismiss = (id) => { | ||
| if (!id) { | ||
| if (id) { | ||
| this.dismissedToasts.add(id); | ||
| requestAnimationFrame(() => { | ||
| this.subscribers.forEach((subscriber) => subscriber({ id, dismiss: true })); | ||
| }); | ||
| } else { | ||
| this.toasts.forEach((toast2) => { | ||
| this.dismissedToasts.add(toast2.id); | ||
| this.subscribers.forEach((subscriber) => subscriber({ id: toast2.id, dismiss: true })); | ||
| }); | ||
| } | ||
| this.subscribers.forEach((subscriber) => subscriber({ id, dismiss: true })); | ||
| return id; | ||
@@ -153,6 +213,8 @@ }; | ||
| }; | ||
| loading = (message, data) => { | ||
| return this.create({ ...data, type: "loading", message }); | ||
| }; | ||
| promise = (promise, data) => { | ||
| if (!data) { | ||
| if (!data) | ||
| return; | ||
| } | ||
| let id; | ||
@@ -164,22 +226,41 @@ if (data.loading !== void 0) { | ||
| type: "loading", | ||
| message: data.loading | ||
| message: data.loading, | ||
| description: typeof data.description !== "function" ? data.description : void 0 | ||
| }); | ||
| } | ||
| const p = promise instanceof Promise ? promise : promise(); | ||
| const p = Promise.resolve(typeof promise === "function" ? promise() : promise); | ||
| let shouldDismiss = id !== void 0; | ||
| p.then((response) => { | ||
| if (response && typeof response.ok === "boolean" && !response.ok) { | ||
| let result; | ||
| const originalPromise = p.then(async (response) => { | ||
| result = ["resolve", response]; | ||
| if (isValidElement(response)) { | ||
| shouldDismiss = false; | ||
| const message = typeof data.error === "function" ? data.error(`HTTP error! status: ${response.status}`) : data.error; | ||
| this.create({ id, type: "error", message }); | ||
| this.create({ id, type: "default", message: response }); | ||
| } else if (isHttpResponse(response) && !response.ok) { | ||
| shouldDismiss = false; | ||
| const promiseData = typeof data.error === "function" ? await data.error(`HTTP error! status: ${response.status}`) : data.error; | ||
| const description = typeof data.description === "function" ? await data.description(`HTTP error! status: ${response.status}`) : data.description; | ||
| const toastSettings = isExtendedResult(promiseData) ? promiseData : { message: promiseData }; | ||
| this.create({ id, type: "error", description, ...toastSettings }); | ||
| } else if (response instanceof Error) { | ||
| shouldDismiss = false; | ||
| const promiseData = typeof data.error === "function" ? await data.error(response) : data.error; | ||
| const description = typeof data.description === "function" ? await data.description(response) : data.description; | ||
| const toastSettings = isExtendedResult(promiseData) ? promiseData : { message: promiseData }; | ||
| this.create({ id, type: "error", description, ...toastSettings }); | ||
| } else if (data.success !== void 0) { | ||
| shouldDismiss = false; | ||
| const message = typeof data.success === "function" ? data.success(response) : data.success; | ||
| this.create({ id, type: "success", message }); | ||
| const promiseData = typeof data.success === "function" ? await data.success(response) : data.success; | ||
| const description = typeof data.description === "function" ? await data.description(response) : data.description; | ||
| const toastSettings = isExtendedResult(promiseData) ? promiseData : { message: promiseData }; | ||
| this.create({ id, type: "success", description, ...toastSettings }); | ||
| } | ||
| }).catch((error) => { | ||
| }).catch(async (error) => { | ||
| result = ["reject", error]; | ||
| if (data.error !== void 0) { | ||
| shouldDismiss = false; | ||
| const message = typeof data.error === "function" ? data.error(error) : data.error; | ||
| this.create({ id, type: "error", message }); | ||
| const promiseData = typeof data.error === "function" ? await data.error(error) : data.error; | ||
| const description = typeof data.description === "function" ? await data.description(error) : data.description; | ||
| const toastSettings = isExtendedResult(promiseData) ? promiseData : { message: promiseData }; | ||
| this.create({ id, type: "error", description, ...toastSettings }); | ||
| } | ||
@@ -193,14 +274,32 @@ }).finally(() => { | ||
| }); | ||
| return id; | ||
| const unwrap = () => new Promise((resolve, reject) => { | ||
| originalPromise.then(() => { | ||
| if (result[0] === "reject") | ||
| reject(result[1]); | ||
| else | ||
| resolve(result[1]); | ||
| }).catch(reject); | ||
| }); | ||
| if (typeof id !== "string" && typeof id !== "number") | ||
| return { unwrap }; | ||
| return Object.assign(id, { unwrap }); | ||
| }; | ||
| loading = (message, data) => { | ||
| return this.create({ ...data, type: "loading", message }); | ||
| }; | ||
| // We can't provide the toast we just created as a prop as we didn't create it yet, so we can create a default toast object, I just don't know how to use function in argument when calling()? | ||
| custom = (jsx, data) => { | ||
| const id = data?.id || toastsCounter++; | ||
| this.publish({ jsx: jsx(id), id, ...data }); | ||
| this.create({ jsx: jsx(id), ...data, id }); | ||
| return id; | ||
| }; | ||
| getActiveToasts = () => { | ||
| return this.toasts.filter((toast2) => !this.dismissedToasts.has(toast2.id)); | ||
| }; | ||
| }; | ||
| function isHttpResponse(data) { | ||
| return data && typeof data === "object" && "ok" in data && typeof data.ok === "boolean" && "status" in data && typeof data.status === "number"; | ||
| } | ||
| function isExtendedResult(value) { | ||
| return typeof value === "object" && value !== null && "message" in value; | ||
| } | ||
| function isValidElement(value) { | ||
| return typeof Node !== "undefined" && value instanceof Node; | ||
| } | ||
| var ToastState = new Observer(); | ||
@@ -217,28 +316,26 @@ function toastFunction(message, data) { | ||
| var basicToast = toastFunction; | ||
| var toast = Object.assign(basicToast, { | ||
| success: ToastState.success, | ||
| info: ToastState.info, | ||
| warning: ToastState.warning, | ||
| error: ToastState.error, | ||
| custom: ToastState.custom, | ||
| message: ToastState.message, | ||
| promise: ToastState.promise, | ||
| dismiss: ToastState.dismiss, | ||
| loading: ToastState.loading | ||
| }); | ||
| var getHistory = () => ToastState.toasts; | ||
| var getToasts = () => ToastState.getActiveToasts(); | ||
| var toast = Object.assign( | ||
| basicToast, | ||
| { | ||
| success: ToastState.success, | ||
| info: ToastState.info, | ||
| warning: ToastState.warning, | ||
| error: ToastState.error, | ||
| custom: ToastState.custom, | ||
| message: ToastState.message, | ||
| promise: ToastState.promise, | ||
| dismiss: ToastState.dismiss, | ||
| loading: ToastState.loading | ||
| }, | ||
| { | ||
| getHistory, | ||
| getToasts | ||
| } | ||
| ); | ||
| // src/primitives.ts | ||
| import { createSignal, onCleanup, onMount } from "solid-js"; | ||
| function useIsDocumentHidden() { | ||
| const [isDocumentHidden, setIsDocumentHidden] = createSignal(false); | ||
| onMount(() => { | ||
| const callback = () => { | ||
| setIsDocumentHidden(document.hidden); | ||
| }; | ||
| document.addEventListener("visibilitychange", callback); | ||
| onCleanup(() => { | ||
| window.removeEventListener("visibilitychange", callback); | ||
| }); | ||
| }); | ||
| return isDocumentHidden; | ||
| // src/types.ts | ||
| function isAction(action) { | ||
| return typeof action === "object" && action !== null && "label" in action; | ||
| } | ||
@@ -248,12 +345,99 @@ | ||
| var VISIBLE_TOASTS_AMOUNT = 3; | ||
| var VIEWPORT_OFFSET = "32px"; | ||
| var VIEWPORT_OFFSET = "24px"; | ||
| var MOBILE_VIEWPORT_OFFSET = "16px"; | ||
| var TOAST_LIFETIME = 4e3; | ||
| var TOAST_WIDTH = 356; | ||
| var GAP = 14; | ||
| var SWIPE_TRESHOLD = 20; | ||
| var SWIPE_THRESHOLD = 45; | ||
| var TIME_BEFORE_UNMOUNT = 200; | ||
| function _cn(...classes) { | ||
| function cn(...classes) { | ||
| return classes.filter(Boolean).join(" "); | ||
| } | ||
| var Toast = (props) => { | ||
| function resolveContent(content) { | ||
| return typeof content === "function" ? content() : content; | ||
| } | ||
| function getDefaultSwipeDirections(position) { | ||
| const [y, x] = position.split("-"); | ||
| const directions = []; | ||
| if (y) | ||
| directions.push(y); | ||
| if (x) | ||
| directions.push(x); | ||
| return directions; | ||
| } | ||
| function getDocumentDirection() { | ||
| if (typeof window === "undefined" || typeof document === "undefined") | ||
| return "ltr"; | ||
| const dirAttribute = document.documentElement.getAttribute("dir"); | ||
| if (dirAttribute === "auto" || !dirAttribute) | ||
| return window.getComputedStyle(document.documentElement).direction; | ||
| return dirAttribute; | ||
| } | ||
| function assignOffset(defaultOffset, mobileOffset) { | ||
| const styles = {}; | ||
| [defaultOffset, mobileOffset].forEach((offset, index) => { | ||
| const isMobile = index === 1; | ||
| const prefix = isMobile ? "--mobile-offset" : "--offset"; | ||
| const defaultValue = isMobile ? MOBILE_VIEWPORT_OFFSET : VIEWPORT_OFFSET; | ||
| const assignAll = (value) => { | ||
| for (const key of ["top", "right", "bottom", "left"]) | ||
| styles[`${prefix}-${key}`] = typeof value === "number" ? `${value}px` : value; | ||
| }; | ||
| if (typeof offset === "number" || typeof offset === "string") { | ||
| assignAll(offset); | ||
| return; | ||
| } | ||
| if (typeof offset === "object" && offset !== null) { | ||
| for (const key of ["top", "right", "bottom", "left"]) { | ||
| const value = offset[key]; | ||
| styles[`${prefix}-${key}`] = value === void 0 ? defaultValue : typeof value === "number" ? `${value}px` : value; | ||
| } | ||
| return; | ||
| } | ||
| assignAll(defaultValue); | ||
| }); | ||
| return styles; | ||
| } | ||
| function mergeClassNames(classNames, legacy) { | ||
| return classNames ?? legacy; | ||
| } | ||
| function mergeDescriptionClassName(descriptionClassName, legacy) { | ||
| return descriptionClassName ?? legacy ?? ""; | ||
| } | ||
| function mergeClassName(className, legacy) { | ||
| return className ?? legacy ?? ""; | ||
| } | ||
| function useSonner() { | ||
| const [activeToasts, setActiveToasts] = createSignal2(toast.getToasts()); | ||
| onMount2(() => { | ||
| const unsubscribe = ToastState.subscribe((toastItem) => { | ||
| if (toastItem.dismiss) { | ||
| requestAnimationFrame(() => { | ||
| setActiveToasts((toasts) => toasts.filter((t) => t.id !== toastItem.id)); | ||
| }); | ||
| return; | ||
| } | ||
| const nextToast = toastItem; | ||
| setActiveToasts((toasts) => { | ||
| const indexOfExistingToast = toasts.findIndex((t) => t.id === nextToast.id); | ||
| if (indexOfExistingToast !== -1) { | ||
| return [ | ||
| ...toasts.slice(0, indexOfExistingToast), | ||
| { ...toasts[indexOfExistingToast], ...nextToast }, | ||
| ...toasts.slice(indexOfExistingToast + 1) | ||
| ]; | ||
| } | ||
| return [nextToast, ...toasts]; | ||
| }); | ||
| }); | ||
| onCleanup2(unsubscribe); | ||
| }); | ||
| return { | ||
| toasts: activeToasts | ||
| }; | ||
| } | ||
| function createToastTheme(theme) { | ||
| return theme !== "system" ? theme : typeof window !== "undefined" && window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light"; | ||
| } | ||
| function Toast(props) { | ||
| const [mounted, setMounted] = createSignal2(false); | ||
@@ -263,20 +447,32 @@ const [removed, setRemoved] = createSignal2(false); | ||
| const [swipeOut, setSwipeOut] = createSignal2(false); | ||
| const [isSwiped, setIsSwiped] = createSignal2(false); | ||
| const [swipeDirection, setSwipeDirection] = createSignal2(null); | ||
| const [swipeOutDirection, setSwipeOutDirection] = createSignal2(null); | ||
| const [offsetBeforeRemove, setOffsetBeforeRemove] = createSignal2(0); | ||
| const [initialHeight, setInitialHeight] = createSignal2(0); | ||
| let toastRef; | ||
| let dragStartTime = null; | ||
| let closeTimerStartTimeRef = 0; | ||
| let lastCloseTimerStartTimeRef = 0; | ||
| let remainingTime = TOAST_LIFETIME; | ||
| const [pointerStartRef, setPointerStartRef] = createSignal2(null); | ||
| const isFront = () => props.index === 0; | ||
| const isVisible = () => props.index + 1 <= props.visibleToasts; | ||
| const toastType = () => props.toast.type; | ||
| const toastClassname = () => props.toast.class || ""; | ||
| const toastDescriptionClassname = () => props.toast.descriptionClass || ""; | ||
| const propsWithDefaults = mergeProps({ | ||
| gap: GAP | ||
| }, props); | ||
| const heightIndex = () => props.heights.findIndex((height) => height.toastId === props.toast.id) || 0; | ||
| const duration = () => props.toast.duration || props.duration || TOAST_LIFETIME; | ||
| let closeTimerStartTimeRef = 0; | ||
| let lastCloseTimerStartTimeRef = 0; | ||
| const [pointerStartRef, setPointerStartRef] = createSignal2(null); | ||
| const coords = () => props.position.split("-"); | ||
| const toastsHeightBefore = () => { | ||
| const dismissible = () => props.toast.dismissible !== false; | ||
| const classNames = () => mergeClassNames(props.classNames, props.classes); | ||
| const className = () => mergeClassName(props.className, props.class); | ||
| const descriptionClassName = () => mergeDescriptionClassName(props.descriptionClassName, props.descriptionClass); | ||
| const toastClassName = () => mergeClassName(props.toast.className, props.toast.class); | ||
| const toastDescriptionClassName = () => mergeDescriptionClassName(props.toast.descriptionClassName, props.toast.descriptionClass); | ||
| const toastClassNames = () => mergeClassNames(props.toast.classNames, props.toast.classes); | ||
| const closeButton = () => props.toast.closeButton ?? props.closeButton; | ||
| const duration = () => props.toast.duration ?? props.duration ?? TOAST_LIFETIME; | ||
| const invert = () => props.toast.invert ?? props.invert; | ||
| const disabled = () => toastType() === "loading"; | ||
| const swipeDirections = () => props.swipeDirections ?? getDefaultSwipeDirections(props.position); | ||
| const y = createMemo(() => props.position.split("-")[0]); | ||
| const x = createMemo(() => props.position.split("-")[1]); | ||
| const heightIndex = createMemo(() => Math.max(0, props.heights.findIndex((height) => height.toastId === props.toast.id))); | ||
| const toastsHeightBefore = createMemo(() => { | ||
| return props.heights.reduce((prev, curr, reducerIndex) => { | ||
@@ -287,56 +483,58 @@ if (reducerIndex >= heightIndex()) | ||
| }, 0); | ||
| }; | ||
| }); | ||
| const offset = createMemo(() => heightIndex() * (props.gap ?? GAP) + toastsHeightBefore()); | ||
| const isDocumentHidden = useIsDocumentHidden(); | ||
| const invert = () => props.toast.invert || props.invert; | ||
| const disabled = () => toastType() === "loading"; | ||
| const offset = () => heightIndex() * propsWithDefaults.gap + toastsHeightBefore(); | ||
| createEffect(() => { | ||
| remainingTime = duration(); | ||
| }); | ||
| function deleteToast() { | ||
| setRemoved(true); | ||
| setOffsetBeforeRemove(offset()); | ||
| props.setHeights((heights) => heights.filter((height) => height.toastId !== props.toast.id)); | ||
| window.setTimeout(() => { | ||
| props.removeToast(props.toast); | ||
| }, TIME_BEFORE_UNMOUNT); | ||
| } | ||
| function getLoadingIcon() { | ||
| if (props.icons?.loading) { | ||
| return <div class="sonner-loader" data-visible={toastType() === "loading"}>{props.icons.loading}</div>; | ||
| return <div class={cn(classNames()?.loader, toastClassNames()?.loader, "sonner-loader")} data-visible={toastType() === "loading"}> | ||
| {props.icons.loading} | ||
| </div>; | ||
| } | ||
| return <Loader visible={toastType() === "loading"} />; | ||
| return <Loader class={cn(classNames()?.loader, toastClassNames()?.loader)} visible={toastType() === "loading"} />; | ||
| } | ||
| const icon = () => props.toast.icon ?? props.icons?.[toastType()] ?? getAsset(toastType()); | ||
| onMount2(() => { | ||
| setMounted(true); | ||
| if (!toastRef) | ||
| return; | ||
| const height = toastRef.getBoundingClientRect().height; | ||
| setInitialHeight(height); | ||
| props.setHeights((h) => [{ toastId: props.toast.id, toasterId: props.toast.toasterId, height, position: props.toast.position }, ...h]); | ||
| onCleanup2(() => { | ||
| props.setHeights((h) => h.filter((height2) => height2.toastId !== props.toast.id)); | ||
| }); | ||
| }); | ||
| onMount2(() => { | ||
| const toastNode = toastRef; | ||
| const originalHeight = toastNode.style.height; | ||
| toastNode.style.height = "auto"; | ||
| const newHeight = toastNode.getBoundingClientRect().height; | ||
| toastNode.style.height = originalHeight; | ||
| setInitialHeight(newHeight); | ||
| createEffect(() => { | ||
| props.setHeights((heights) => { | ||
| const alreadyExists = heights.find((height) => height.toastId === props.toast.id); | ||
| if (!alreadyExists) | ||
| return [{ toastId: props.toast.id, height: newHeight, position: props.toast.position }, ...heights]; | ||
| else | ||
| return heights.map((height) => height.toastId === props.toast.id ? { ...height, height: newHeight } : height); | ||
| }); | ||
| createEffect(() => { | ||
| if (!mounted() || !toastRef) | ||
| return; | ||
| resolveContent(props.toast.title); | ||
| resolveContent(props.toast.description); | ||
| const originalHeight = toastRef.style.height; | ||
| toastRef.style.height = "auto"; | ||
| const nextHeight = toastRef.getBoundingClientRect().height; | ||
| toastRef.style.height = originalHeight; | ||
| setInitialHeight(nextHeight); | ||
| props.setHeights((heights) => { | ||
| const alreadyExists = heights.find((height) => height.toastId === props.toast.id); | ||
| if (!alreadyExists) | ||
| return [{ toastId: props.toast.id, toasterId: props.toast.toasterId, height: nextHeight, position: props.toast.position }, ...heights]; | ||
| return heights.map((height) => height.toastId === props.toast.id ? { ...height, height: nextHeight } : height); | ||
| }); | ||
| }); | ||
| const deleteToast = () => { | ||
| setRemoved(true); | ||
| setOffsetBeforeRemove(offset()); | ||
| props.setHeights((h) => h.filter((height) => height.toastId !== props.toast.id)); | ||
| setTimeout(() => { | ||
| props.removeToast(props.toast); | ||
| }, TIME_BEFORE_UNMOUNT); | ||
| }; | ||
| let remainingTime = duration(); | ||
| createEffect( | ||
| on( | ||
| () => [ | ||
| props.expanded, | ||
| props.interacting, | ||
| props.toast, | ||
| duration(), | ||
| props.toast.promise, | ||
| toastType(), | ||
| props.pauseWhenPageIsHidden, | ||
| isDocumentHidden() | ||
| ], | ||
| ([expanded, interacting, toast2, duration2, promise, toastType2, pauseWhenPageIsHidden, isDocumentHidden2]) => { | ||
| if (promise && toastType2 === "loading" || duration2 === Number.POSITIVE_INFINITY) | ||
| () => [props.expanded, props.interacting, props.toast, toastType(), isDocumentHidden(), duration()], | ||
| ([expanded, interacting, currentToast, currentType]) => { | ||
| if (currentToast.promise && currentType === "loading" || currentToast.duration === Number.POSITIVE_INFINITY || currentType === "loading") | ||
| return; | ||
@@ -347,3 +545,3 @@ let timeoutId; | ||
| const elapsedTime = (/* @__PURE__ */ new Date()).getTime() - closeTimerStartTimeRef; | ||
| remainingTime = remainingTime - elapsedTime; | ||
| remainingTime -= elapsedTime; | ||
| } | ||
@@ -353,9 +551,12 @@ lastCloseTimerStartTimeRef = (/* @__PURE__ */ new Date()).getTime(); | ||
| const startTimer = () => { | ||
| if (remainingTime === Number.POSITIVE_INFINITY) | ||
| return; | ||
| closeTimerStartTimeRef = (/* @__PURE__ */ new Date()).getTime(); | ||
| timeoutId = setTimeout(() => { | ||
| toast2.onAutoClose?.(toast2); | ||
| currentToast.onAutoClose?.(currentToast); | ||
| deleteToast(); | ||
| }, remainingTime); | ||
| }; | ||
| if (expanded || interacting || pauseWhenPageIsHidden && isDocumentHidden2) | ||
| const shouldPause = expanded || interacting || (props.pauseWhenPageIsHidden ?? true) && isDocumentHidden(); | ||
| if (shouldPause) | ||
| pauseTimer(); | ||
@@ -365,3 +566,4 @@ else | ||
| onCleanup2(() => { | ||
| clearTimeout(timeoutId); | ||
| if (timeoutId) | ||
| clearTimeout(timeoutId); | ||
| }); | ||
@@ -373,22 +575,8 @@ } | ||
| on( | ||
| () => props.toast.id, | ||
| (toastId) => { | ||
| const toastNode = toastRef; | ||
| if (toastNode) { | ||
| const height = toastNode.getBoundingClientRect().height; | ||
| setInitialHeight(height); | ||
| props.setHeights((h) => [{ toastId, height, position: props.toast.position }, ...h]); | ||
| onCleanup2(() => { | ||
| props.setHeights((h) => h.filter((height2) => height2.toastId !== toastId)); | ||
| }); | ||
| } | ||
| } | ||
| ) | ||
| ); | ||
| createEffect( | ||
| on( | ||
| () => props.toast.delete, | ||
| (d) => { | ||
| if (d) | ||
| (shouldDelete) => { | ||
| if (shouldDelete) { | ||
| deleteToast(); | ||
| props.toast.onDismiss?.(props.toast); | ||
| } | ||
| } | ||
@@ -398,31 +586,33 @@ ) | ||
| return <li | ||
| aria-live={props.toast.important ? "assertive" : "polite"} | ||
| aria-atomic="true" | ||
| role="status" | ||
| tabIndex={0} | ||
| ref={toastRef} | ||
| class={_cn( | ||
| props.class, | ||
| toastClassname(), | ||
| props.classes?.toast, | ||
| props.toast?.classes?.toast, | ||
| props.classes?.default, | ||
| props.classes?.[toastType()], | ||
| props.toast?.classes?.[toastType()] | ||
| class={cn( | ||
| className(), | ||
| toastClassName(), | ||
| classNames()?.toast, | ||
| toastClassNames()?.toast, | ||
| classNames()?.default, | ||
| classNames()?.[toastType()], | ||
| toastClassNames()?.[toastType()] | ||
| )} | ||
| data-sonner-toast="" | ||
| data-rich-colors={props.toast.richColors ?? props.defaultRichColors} | ||
| data-styled={!(props.toast.jsx || props.toast.unstyled || props.unstyled)} | ||
| data-mounted={mounted()} | ||
| data-promise={Boolean(props.toast.promise)} | ||
| data-swiped={isSwiped()} | ||
| data-removed={removed()} | ||
| data-visible={isVisible()} | ||
| data-y-position={coords()[0]} | ||
| data-x-position={coords()[1]} | ||
| data-y-position={y()} | ||
| data-x-position={x()} | ||
| data-index={props.index} | ||
| data-front={isFront()} | ||
| data-swiping={swiping()} | ||
| data-dismissible={dismissible()} | ||
| data-type={toastType()} | ||
| data-invert={invert()} | ||
| data-swipe-out={swipeOut()} | ||
| data-swipe-direction={swipeOutDirection()} | ||
| data-expanded={Boolean(props.expanded || props.expandByDefault && mounted())} | ||
| data-testid={props.toast.testId} | ||
| style={{ | ||
@@ -437,7 +627,15 @@ "--index": props.index, | ||
| }} | ||
| onDragEnd={() => { | ||
| setSwiping(false); | ||
| setSwipeDirection(null); | ||
| setPointerStartRef(null); | ||
| }} | ||
| onPointerDown={(event) => { | ||
| if (disabled()) | ||
| if (event.button === 2) | ||
| return; | ||
| if (disabled() || !dismissible()) | ||
| return; | ||
| dragStartTime = /* @__PURE__ */ new Date(); | ||
| setOffsetBeforeRemove(offset()); | ||
| event.target.setPointerCapture(event.pointerId); | ||
| event.currentTarget.setPointerCapture(event.pointerId); | ||
| if (event.target.tagName === "BUTTON") | ||
@@ -449,9 +647,17 @@ return; | ||
| onPointerUp={() => { | ||
| if (swipeOut()) | ||
| if (swipeOut() || !dismissible()) | ||
| return; | ||
| setPointerStartRef(null); | ||
| const swipeAmount = Number(toastRef?.style.getPropertyValue("--swipe-amount").replace("px", "") || 0); | ||
| if (Math.abs(swipeAmount) >= SWIPE_TRESHOLD) { | ||
| const swipeAmountX = Number(toastRef?.style.getPropertyValue("--swipe-amount-x").replace("px", "") || 0); | ||
| const swipeAmountY = Number(toastRef?.style.getPropertyValue("--swipe-amount-y").replace("px", "") || 0); | ||
| const timeTaken = Math.max(1, (/* @__PURE__ */ new Date()).getTime() - (dragStartTime?.getTime() ?? 0)); | ||
| const swipeAmount = swipeDirection() === "x" ? swipeAmountX : swipeAmountY; | ||
| const velocity = Math.abs(swipeAmount) / timeTaken; | ||
| if (Math.abs(swipeAmount) >= SWIPE_THRESHOLD || velocity > 0.11) { | ||
| setOffsetBeforeRemove(offset()); | ||
| props.toast.onDismiss?.(props.toast); | ||
| if (swipeDirection() === "x") | ||
| setSwipeOutDirection(swipeAmountX > 0 ? "right" : "left"); | ||
| else | ||
| setSwipeOutDirection(swipeAmountY > 0 ? "down" : "up"); | ||
| deleteToast(); | ||
@@ -461,83 +667,128 @@ setSwipeOut(true); | ||
| } | ||
| toastRef?.style.setProperty("--swipe-amount", "0px"); | ||
| toastRef?.style.setProperty("--swipe-amount-x", "0px"); | ||
| toastRef?.style.setProperty("--swipe-amount-y", "0px"); | ||
| setIsSwiped(false); | ||
| setSwiping(false); | ||
| setSwipeDirection(null); | ||
| }} | ||
| onPointerMove={(event) => { | ||
| if (!pointerStartRef()) | ||
| if (!pointerStartRef() || !dismissible()) | ||
| return; | ||
| const yPosition = event.clientY - pointerStartRef().y; | ||
| const xPosition = event.clientX - pointerStartRef().x; | ||
| const clamp = coords()[0] === "top" ? Math.min : Math.max; | ||
| const clampedY = clamp(0, yPosition); | ||
| const swipeStartThreshold = event.pointerType === "touch" ? 10 : 2; | ||
| const isAllowedToSwipe = Math.abs(clampedY) > swipeStartThreshold; | ||
| if (isAllowedToSwipe) { | ||
| toastRef?.style.setProperty("--swipe-amount", `${yPosition}px`); | ||
| } else if (Math.abs(xPosition) > swipeStartThreshold) { | ||
| setPointerStartRef(null); | ||
| if ((window.getSelection()?.toString().length ?? 0) > 0) | ||
| return; | ||
| const yDelta = event.clientY - pointerStartRef().y; | ||
| const xDelta = event.clientX - pointerStartRef().x; | ||
| if (!swipeDirection() && (Math.abs(xDelta) > 1 || Math.abs(yDelta) > 1)) | ||
| setSwipeDirection(Math.abs(xDelta) > Math.abs(yDelta) ? "x" : "y"); | ||
| const swipeAmount = { x: 0, y: 0 }; | ||
| const getDampening = (delta) => { | ||
| const factor = Math.abs(delta) / 20; | ||
| return 1 / (1.5 + factor); | ||
| }; | ||
| if (swipeDirection() === "y") { | ||
| if (swipeDirections().includes("top") || swipeDirections().includes("bottom")) { | ||
| if (swipeDirections().includes("top") && yDelta < 0 || swipeDirections().includes("bottom") && yDelta > 0) | ||
| swipeAmount.y = yDelta; | ||
| else | ||
| swipeAmount.y = yDelta * getDampening(yDelta); | ||
| } | ||
| } else if (swipeDirection() === "x") { | ||
| if (swipeDirections().includes("left") || swipeDirections().includes("right")) { | ||
| if (swipeDirections().includes("left") && xDelta < 0 || swipeDirections().includes("right") && xDelta > 0) | ||
| swipeAmount.x = xDelta; | ||
| else | ||
| swipeAmount.x = xDelta * getDampening(xDelta); | ||
| } | ||
| } | ||
| if (Math.abs(swipeAmount.x) > 0 || Math.abs(swipeAmount.y) > 0) | ||
| setIsSwiped(true); | ||
| toastRef?.style.setProperty("--swipe-amount-x", `${swipeAmount.x}px`); | ||
| toastRef?.style.setProperty("--swipe-amount-y", `${swipeAmount.y}px`); | ||
| }} | ||
| ><Show when={props.closeButton && !props.toast.jsx}><button | ||
| aria-label="Close toast" | ||
| > | ||
| <Show when={closeButton() && !props.toast.jsx && toastType() !== "loading"}> | ||
| <button | ||
| aria-label={props.closeButtonAriaLabel ?? "Close toast"} | ||
| data-disabled={disabled()} | ||
| data-close-button | ||
| onClick={disabled() ? void 0 : () => { | ||
| onClick={() => { | ||
| if (disabled() || !dismissible()) | ||
| return; | ||
| deleteToast(); | ||
| props.toast.onDismiss?.(props.toast); | ||
| }} | ||
| class={_cn(props.classes?.closeButton, props.toast?.classes?.closeButton)} | ||
| ><svg | ||
| xmlns="http://www.w3.org/2000/svg" | ||
| width="12" | ||
| height="12" | ||
| viewBox="0 0 24 24" | ||
| fill="none" | ||
| stroke="currentColor" | ||
| stroke-width="1.5" | ||
| stroke-linecap="round" | ||
| stroke-linejoin="round" | ||
| ><line x1="18" y1="6" x2="6" y2="18" /><line x1="6" y1="6" x2="18" y2="18" /></svg></button></Show><Show | ||
| when={props.toast.jsx || props.toast.title instanceof Element} | ||
| fallback={<><Show when={toastType() || props.toast.icon || props.toast.promise}><div data-icon="">{props.toast.promise || props.toast.type === "loading" && !props.toast.icon ? props.toast.icon || getLoadingIcon() : null}{props.toast.type !== "loading" ? props.toast.icon || props.icons?.[toastType()] || getAsset(toastType())() : null}</div></Show><div data-content=""><div data-title="" class={_cn(props.classes?.title, props.toast?.classes?.title)}>{props.toast.title}</div><Show when={props.toast.description}><div | ||
| data-description="" | ||
| class={_cn( | ||
| props.descriptionClass, | ||
| toastDescriptionClassname(), | ||
| props.classes?.description, | ||
| props.toast?.classes?.description | ||
| )} | ||
| >{props.toast.description}</div></Show></div><Show when={props.toast.cancel}><button | ||
| data-button | ||
| data-cancel | ||
| style={props.toast.cancelButtonStyle || props.cancelButtonStyle} | ||
| onClick={() => { | ||
| deleteToast(); | ||
| if (props.toast.cancel?.onClick) | ||
| props.toast.cancel.onClick(); | ||
| }} | ||
| class={_cn(props.classes?.cancelButton, props.toast?.classes?.cancelButton)} | ||
| >{props.toast.cancel.label}</button></Show><Show when={props.toast.action}><button | ||
| data-button="" | ||
| style={props.toast.actionButtonStyle || props.actionButtonStyle} | ||
| onClick={(event) => { | ||
| props.toast.action?.onClick(event); | ||
| if (event.defaultPrevented) | ||
| return; | ||
| deleteToast(); | ||
| }} | ||
| class={_cn(props.classes?.actionButton, props.toast?.classes?.actionButton)} | ||
| >{props.toast.action.label}</button></Show></>} | ||
| >{props.toast.jsx || props.toast.title}</Show></li>; | ||
| }; | ||
| function getDocumentDirection() { | ||
| if (typeof window === "undefined") | ||
| return "ltr"; | ||
| if (typeof document === "undefined") | ||
| return "ltr"; | ||
| const dirAttribute = document.documentElement.getAttribute("dir"); | ||
| if (dirAttribute === "auto" || !dirAttribute) | ||
| return window.getComputedStyle(document.documentElement).direction; | ||
| return dirAttribute; | ||
| class={cn(classNames()?.closeButton, toastClassNames()?.closeButton)} | ||
| > | ||
| {props.icons?.close ?? <CloseIcon />} | ||
| </button> | ||
| </Show> | ||
| <Show when={(toastType() || props.toast.icon || props.toast.promise) && props.toast.icon !== null && (props.icons?.[toastType()] !== null || props.toast.icon)}> | ||
| <div data-icon="" class={cn(classNames()?.icon, toastClassNames()?.icon)}> | ||
| {props.toast.promise || props.toast.type === "loading" && !props.toast.icon ? props.toast.icon || getLoadingIcon() : null} | ||
| {props.toast.type !== "loading" ? icon() : null} | ||
| </div> | ||
| </Show> | ||
| <div data-content="" class={cn(classNames()?.content, toastClassNames()?.content)}> | ||
| <div data-title="" class={cn(classNames()?.title, toastClassNames()?.title)}> | ||
| {props.toast.jsx ? props.toast.jsx : resolveContent(props.toast.title)} | ||
| </div> | ||
| <Show when={props.toast.description}> | ||
| <div | ||
| data-description="" | ||
| class={cn( | ||
| descriptionClassName(), | ||
| toastDescriptionClassName(), | ||
| classNames()?.description, | ||
| toastClassNames()?.description | ||
| )} | ||
| > | ||
| {resolveContent(props.toast.description)} | ||
| </div> | ||
| </Show> | ||
| </div> | ||
| <Show when={props.toast.cancel}> | ||
| {(cancel) => <Show when={!isAction(cancel())} fallback={<button | ||
| data-button | ||
| data-cancel | ||
| style={props.toast.cancelButtonStyle ?? props.cancelButtonStyle} | ||
| onClick={(event) => { | ||
| const currentCancel = cancel(); | ||
| if (!dismissible()) | ||
| return; | ||
| currentCancel.onClick?.(event); | ||
| deleteToast(); | ||
| }} | ||
| class={cn(classNames()?.cancelButton, toastClassNames()?.cancelButton)} | ||
| > | ||
| {cancel().label} | ||
| </button>}> | ||
| {cancel()} | ||
| </Show>} | ||
| </Show> | ||
| <Show when={props.toast.action}> | ||
| {(action) => <Show when={!isAction(action())} fallback={<button | ||
| data-button | ||
| data-action | ||
| style={props.toast.actionButtonStyle ?? props.actionButtonStyle} | ||
| onClick={(event) => { | ||
| const currentAction = action(); | ||
| currentAction.onClick?.(event); | ||
| if (event.defaultPrevented) | ||
| return; | ||
| deleteToast(); | ||
| }} | ||
| class={cn(classNames()?.actionButton, toastClassNames()?.actionButton)} | ||
| > | ||
| {action().label} | ||
| </button>}> | ||
| {action()} | ||
| </Show>} | ||
| </Show> | ||
| </li>; | ||
| } | ||
| var Toaster = (props) => { | ||
| function Toaster(props) { | ||
| const propsWithDefaults = mergeProps({ | ||
@@ -547,67 +798,86 @@ position: "bottom-right", | ||
| theme: "light", | ||
| gap: GAP, | ||
| visibleToasts: VISIBLE_TOASTS_AMOUNT, | ||
| dir: getDocumentDirection() | ||
| dir: getDocumentDirection(), | ||
| containerAriaLabel: "Notifications" | ||
| }, props); | ||
| const initialTheme = createToastTheme(propsWithDefaults.theme); | ||
| const [toastsStore, setToastsStore] = createStore({ toasts: [] }); | ||
| const possiblePositions = () => { | ||
| return Array.from( | ||
| new Set([propsWithDefaults.position].concat(toastsStore.toasts.filter((toast2) => toast2.position).map((toast2) => toast2.position))) | ||
| ); | ||
| }; | ||
| const filteredToasts = createMemo(() => { | ||
| const toasts = toastsStore.toasts; | ||
| if (propsWithDefaults.id) | ||
| return toasts.filter((toast2) => toast2.toasterId === propsWithDefaults.id); | ||
| return toasts.filter((toast2) => !toast2.toasterId); | ||
| }); | ||
| const possiblePositions = createMemo(() => { | ||
| return Array.from(/* @__PURE__ */ new Set([propsWithDefaults.position, ...filteredToasts().filter((toast2) => toast2.position).map((toast2) => toast2.position)])); | ||
| }); | ||
| const [heights, setHeights] = createSignal2([]); | ||
| const [expanded, setExpanded] = createSignal2(false); | ||
| const [interacting, setInteracting] = createSignal2(false); | ||
| const [actualTheme, setActualTheme] = createSignal2(initialTheme); | ||
| const [lastFocusedElementRef, setLastFocusedElementRef] = createSignal2(null); | ||
| const [isFocusWithinRef, setIsFocusWithinRef] = createSignal2(false); | ||
| let listRef; | ||
| const hotkeyLabel = () => propsWithDefaults.hotkey.join("+").replace(/Key/g, "").replace(/Digit/g, ""); | ||
| const [lastFocusedElementRef, setLastFocusedElementRef] = createSignal2(null); | ||
| const [isFocusedWithinRef, setIsFocusedWithinRef] = createSignal2(false); | ||
| const [actualTheme, setActualTheme] = createSignal2( | ||
| propsWithDefaults.theme !== "system" ? propsWithDefaults.theme : typeof window !== "undefined" ? window.matchMedia && window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light" : "light" | ||
| ); | ||
| const removeToast = (toast2) => setToastsStore("toasts", (toasts) => toasts.filter(({ id }) => id !== toast2.id)); | ||
| const className = () => mergeClassName(propsWithDefaults.className, propsWithDefaults.class); | ||
| const toastOptions = () => propsWithDefaults.toastOptions; | ||
| const toastClassNames = () => mergeClassNames(toastOptions()?.classNames, toastOptions()?.classes); | ||
| const toastDescriptionClassName = () => mergeDescriptionClassName(toastOptions()?.descriptionClassName, toastOptions()?.descriptionClass); | ||
| const toastClassName = () => mergeClassName(toastOptions()?.className, toastOptions()?.class); | ||
| const removeToast = (toastToRemove) => { | ||
| setToastsStore("toasts", (toasts) => { | ||
| if (!toasts.find((toast2) => toast2.id === toastToRemove.id)?.delete) | ||
| ToastState.dismiss(toastToRemove.id); | ||
| return toasts.filter(({ id }) => id !== toastToRemove.id); | ||
| }); | ||
| }; | ||
| onMount2(() => { | ||
| const unsub = ToastState.subscribe((toast2) => { | ||
| if (toast2.dismiss) { | ||
| setToastsStore("toasts", produce((_toasts) => { | ||
| _toasts.forEach((t) => { | ||
| if (t.id === toast2.id) | ||
| t.delete = true; | ||
| }); | ||
| })); | ||
| const unsubscribe = ToastState.subscribe((toastItem) => { | ||
| if (toastItem.dismiss) { | ||
| requestAnimationFrame(() => { | ||
| setToastsStore("toasts", produce((toasts) => { | ||
| toasts.forEach((toast2) => { | ||
| if (toast2.id === toastItem.id) | ||
| toast2.delete = true; | ||
| }); | ||
| })); | ||
| }); | ||
| return; | ||
| } | ||
| const changedIndex = toastsStore.toasts.findIndex((t) => t.id === toast2.id); | ||
| if (changedIndex !== -1) { | ||
| setToastsStore("toasts", [changedIndex], reconcile(toast2)); | ||
| const nextToast = toastItem; | ||
| const indexOfExistingToast = toastsStore.toasts.findIndex((t) => t.id === nextToast.id); | ||
| if (indexOfExistingToast !== -1) { | ||
| setToastsStore("toasts", indexOfExistingToast, reconcile(nextToast)); | ||
| return; | ||
| } | ||
| setToastsStore("toasts", produce((_toasts) => { | ||
| _toasts.unshift(toast2); | ||
| setToastsStore("toasts", produce((toasts) => { | ||
| toasts.unshift(nextToast); | ||
| })); | ||
| }); | ||
| onCleanup2(() => { | ||
| unsub(); | ||
| }); | ||
| onCleanup2(unsubscribe); | ||
| }); | ||
| createEffect( | ||
| on( | ||
| () => propsWithDefaults.theme, | ||
| (theme) => { | ||
| if (theme !== "system") { | ||
| setActualTheme(theme); | ||
| return; | ||
| } | ||
| if (typeof window === "undefined") | ||
| return; | ||
| window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change", ({ matches }) => { | ||
| if (matches) | ||
| setActualTheme("dark"); | ||
| else | ||
| setActualTheme("light"); | ||
| }); | ||
| } | ||
| ) | ||
| ); | ||
| createEffect(() => { | ||
| if (toastsStore.toasts.length <= 1) | ||
| const theme = propsWithDefaults.theme; | ||
| if (theme !== "system") { | ||
| setActualTheme(theme); | ||
| return; | ||
| } | ||
| if (typeof window === "undefined") | ||
| return; | ||
| const darkMediaQuery = window.matchMedia("(prefers-color-scheme: dark)"); | ||
| const updateTheme = ({ matches }) => { | ||
| setActualTheme(matches ? "dark" : "light"); | ||
| }; | ||
| updateTheme(darkMediaQuery); | ||
| try { | ||
| darkMediaQuery.addEventListener("change", updateTheme); | ||
| onCleanup2(() => darkMediaQuery.removeEventListener("change", updateTheme)); | ||
| } catch { | ||
| darkMediaQuery.addListener(updateTheme); | ||
| onCleanup2(() => darkMediaQuery.removeListener(updateTheme)); | ||
| } | ||
| }); | ||
| createEffect(() => { | ||
| if (filteredToasts().length <= 1) | ||
| setExpanded(false); | ||
@@ -617,3 +887,3 @@ }); | ||
| const handleKeyDown = (event) => { | ||
| const isHotkeyPressed = propsWithDefaults.hotkey.every((key) => event[key] || event.code === key); | ||
| const isHotkeyPressed = propsWithDefaults.hotkey.length > 0 && propsWithDefaults.hotkey.every((key) => event[key] || event.code === key); | ||
| if (isHotkeyPressed) { | ||
@@ -627,46 +897,44 @@ setExpanded(true); | ||
| document.addEventListener("keydown", handleKeyDown); | ||
| onCleanup2(() => { | ||
| document.removeEventListener("keydown", handleKeyDown); | ||
| }); | ||
| onCleanup2(() => document.removeEventListener("keydown", handleKeyDown)); | ||
| }); | ||
| createEffect( | ||
| on( | ||
| () => listRef, | ||
| (ref) => { | ||
| if (ref) { | ||
| onCleanup2(() => { | ||
| if (lastFocusedElementRef()) { | ||
| lastFocusedElementRef()?.focus({ preventScroll: true }); | ||
| setLastFocusedElementRef(null); | ||
| setIsFocusedWithinRef(false); | ||
| } | ||
| }); | ||
| } | ||
| } | ||
| ) | ||
| ); | ||
| return <Show when={toastsStore.toasts.length > 0}>{ | ||
| /* Remove item from normal navigation flow, only available via hotkey */ | ||
| }<section aria-label={`Notifications ${hotkeyLabel()}`} tabIndex={-1}><For2 each={possiblePositions()}>{(position, index) => { | ||
| onCleanup2(() => { | ||
| if (lastFocusedElementRef()) { | ||
| lastFocusedElementRef()?.focus({ preventScroll: true }); | ||
| setLastFocusedElementRef(null); | ||
| setIsFocusWithinRef(false); | ||
| } | ||
| }); | ||
| return <section | ||
| aria-label={propsWithDefaults.customAriaLabel ?? `${propsWithDefaults.containerAriaLabel} ${hotkeyLabel()}`} | ||
| tabIndex={-1} | ||
| aria-live="polite" | ||
| aria-relevant="additions text" | ||
| aria-atomic="false" | ||
| data-react-aria-top-layer | ||
| > | ||
| <For2 each={possiblePositions()}> | ||
| {(position, index) => { | ||
| const [y, x] = position.split("-"); | ||
| return <ol | ||
| const toastsByPosition = createMemo(() => filteredToasts().filter((toast2) => !toast2.position && index() === 0 || toast2.position === position)); | ||
| const heightsByPosition = createMemo(() => heights().filter((height) => index() === 0 && !height.position || height.position === position)); | ||
| return <Show when={filteredToasts().length > 0}> | ||
| <ol | ||
| tabIndex={-1} | ||
| ref={listRef} | ||
| dir={propsWithDefaults.dir === "auto" ? getDocumentDirection() : propsWithDefaults.dir} | ||
| class={propsWithDefaults.class} | ||
| class={className()} | ||
| data-sonner-toaster | ||
| data-theme={actualTheme()} | ||
| data-rich-colors={propsWithDefaults.richColors} | ||
| data-sonner-theme={actualTheme()} | ||
| data-y-position={y} | ||
| data-x-position={x} | ||
| style={{ | ||
| "--front-toast-height": `${heights()[0]?.height}px`, | ||
| "--offset": typeof propsWithDefaults.offset === "number" ? `${propsWithDefaults.offset}px` : propsWithDefaults.offset || VIEWPORT_OFFSET, | ||
| "--front-toast-height": `${heightsByPosition()[0]?.height ?? 0}px`, | ||
| "--width": `${TOAST_WIDTH}px`, | ||
| "--gap": `${GAP}px`, | ||
| ...propsWithDefaults.style | ||
| "--gap": `${propsWithDefaults.gap}px`, | ||
| ...propsWithDefaults.style, | ||
| ...assignOffset(propsWithDefaults.offset, propsWithDefaults.mobileOffset) | ||
| }} | ||
| onBlur={(event) => { | ||
| if (isFocusedWithinRef() && !event.currentTarget.contains(event.relatedTarget)) { | ||
| setIsFocusedWithinRef(false); | ||
| if (isFocusWithinRef() && !event.currentTarget.contains(event.relatedTarget)) { | ||
| setIsFocusWithinRef(false); | ||
| if (lastFocusedElementRef()) { | ||
@@ -679,4 +947,7 @@ lastFocusedElementRef()?.focus({ preventScroll: true }); | ||
| onFocus={(event) => { | ||
| if (!isFocusedWithinRef()) { | ||
| setIsFocusedWithinRef(true); | ||
| const isNotDismissible = event.target instanceof HTMLElement && event.target.dataset.dismissible === "false"; | ||
| if (isNotDismissible) | ||
| return; | ||
| if (!isFocusWithinRef()) { | ||
| setIsFocusWithinRef(true); | ||
| setLastFocusedElementRef(event.relatedTarget); | ||
@@ -691,26 +962,34 @@ } | ||
| }} | ||
| onPointerDown={() => { | ||
| onDragEnd={() => setExpanded(false)} | ||
| onPointerDown={(event) => { | ||
| const isNotDismissible = event.target instanceof HTMLElement && event.target.dataset.dismissible === "false"; | ||
| if (isNotDismissible) | ||
| return; | ||
| setInteracting(true); | ||
| }} | ||
| onPointerUp={() => setInteracting(false)} | ||
| ><For2 each={toastsStore.toasts.filter((toast2) => !toast2.position && index() === 0 || toast2.position === position)}>{(toast2, index2) => <Toast | ||
| index={index2()} | ||
| > | ||
| <For2 each={toastsByPosition()}> | ||
| {(toastItem, toastIndex) => <Toast | ||
| icons={propsWithDefaults.icons} | ||
| toast={toast2} | ||
| duration={propsWithDefaults.toastOptions?.duration ?? props.duration} | ||
| class={propsWithDefaults.toastOptions?.class} | ||
| classes={propsWithDefaults.toastOptions?.classes} | ||
| cancelButtonStyle={propsWithDefaults.toastOptions?.cancelButtonStyle} | ||
| actionButtonStyle={propsWithDefaults.toastOptions?.actionButtonStyle} | ||
| descriptionClass={propsWithDefaults.toastOptions?.descriptionClass} | ||
| index={toastIndex()} | ||
| toast={toastItem} | ||
| defaultRichColors={propsWithDefaults.richColors} | ||
| duration={toastOptions()?.duration ?? propsWithDefaults.duration} | ||
| class={toastClassName()} | ||
| descriptionClassName={toastDescriptionClassName()} | ||
| invert={Boolean(propsWithDefaults.invert)} | ||
| visibleToasts={propsWithDefaults.visibleToasts} | ||
| closeButton={Boolean(propsWithDefaults.closeButton)} | ||
| closeButton={toastOptions()?.closeButton ?? propsWithDefaults.closeButton ?? false} | ||
| interacting={interacting()} | ||
| position={propsWithDefaults.position} | ||
| style={propsWithDefaults.toastOptions?.style} | ||
| unstyled={propsWithDefaults.toastOptions?.unstyled} | ||
| position={position} | ||
| style={toastOptions()?.style} | ||
| unstyled={toastOptions()?.unstyled} | ||
| classNames={toastClassNames()} | ||
| cancelButtonStyle={toastOptions()?.cancelButtonStyle} | ||
| actionButtonStyle={toastOptions()?.actionButtonStyle} | ||
| closeButtonAriaLabel={toastOptions()?.closeButtonAriaLabel} | ||
| removeToast={removeToast} | ||
| toasts={toastsStore.toasts} | ||
| heights={heights()} | ||
| toasts={toastsByPosition()} | ||
| heights={heightsByPosition()} | ||
| setHeights={setHeights} | ||
@@ -720,9 +999,16 @@ expandByDefault={Boolean(propsWithDefaults.expand)} | ||
| expanded={expanded()} | ||
| swipeDirections={propsWithDefaults.swipeDirections} | ||
| pauseWhenPageIsHidden={propsWithDefaults.pauseWhenPageIsHidden} | ||
| />}</For2></ol>; | ||
| }}</For2></section></Show>; | ||
| }; | ||
| />} | ||
| </For2> | ||
| </ol> | ||
| </Show>; | ||
| }} | ||
| </For2> | ||
| </section>; | ||
| } | ||
| export { | ||
| Toaster, | ||
| toast | ||
| toast, | ||
| useSonner | ||
| }; | ||
@@ -729,0 +1015,0 @@ /*! |
+90
-50
@@ -1,12 +0,21 @@ | ||
| import { JSX, Component } from 'solid-js'; | ||
| import * as solid_js from 'solid-js'; | ||
| import { JSX } from 'solid-js'; | ||
| type ToastTypes = 'normal' | 'action' | 'success' | 'info' | 'warning' | 'error' | 'loading' | 'default'; | ||
| type PromiseT<Data = any> = Promise<Data> | (() => Promise<Data>); | ||
| type PromiseData<ToastData = any> = ExternalToast & { | ||
| loading: string | JSX.Element; | ||
| success: string | JSX.Element | ((data: ToastData) => JSX.Element | string); | ||
| error: string | JSX.Element | ((error: any) => JSX.Element | string); | ||
| type ToastContent = (() => JSX.Element) | JSX.Element; | ||
| interface PromiseIExtendedResult extends ExternalToast { | ||
| message: JSX.Element | string; | ||
| } | ||
| type PromiseTExtendedResult<Data = any> = PromiseIExtendedResult | ((data: Data) => PromiseIExtendedResult | Promise<PromiseIExtendedResult>); | ||
| type PromiseTResult<Data = any> = string | JSX.Element | ((data: Data) => JSX.Element | string | Promise<JSX.Element | string>); | ||
| type PromiseExternalToast = Omit<ExternalToast, 'description'>; | ||
| type PromiseData<ToastData = any> = PromiseExternalToast & { | ||
| loading?: string | JSX.Element; | ||
| success?: PromiseTResult<ToastData> | PromiseTExtendedResult<ToastData>; | ||
| error?: PromiseTResult | PromiseTExtendedResult; | ||
| description?: PromiseTResult; | ||
| finally?: () => void | Promise<void>; | ||
| }; | ||
| interface ToastClasses { | ||
| interface ToastClassnames { | ||
| toast?: string; | ||
@@ -25,30 +34,34 @@ title?: string; | ||
| default?: string; | ||
| content?: string; | ||
| icon?: string; | ||
| } | ||
| interface ToastIcons { | ||
| success?: JSX.Element; | ||
| info?: JSX.Element; | ||
| warning?: JSX.Element; | ||
| error?: JSX.Element; | ||
| loading?: JSX.Element; | ||
| success?: JSX.Element | null; | ||
| info?: JSX.Element | null; | ||
| warning?: JSX.Element | null; | ||
| error?: JSX.Element | null; | ||
| loading?: JSX.Element | null; | ||
| close?: JSX.Element | null; | ||
| } | ||
| interface Action { | ||
| label: JSX.Element; | ||
| onClick: (event: MouseEvent) => void; | ||
| actionButtonStyle?: JSX.CSSProperties; | ||
| } | ||
| interface ToastT { | ||
| id: number | string; | ||
| title?: string | JSX.Element; | ||
| toasterId?: string; | ||
| title?: ToastContent; | ||
| type?: ToastTypes; | ||
| icon?: JSX.Element; | ||
| icon?: JSX.Element | null; | ||
| jsx?: JSX.Element; | ||
| richColors?: boolean; | ||
| invert?: boolean; | ||
| closeButton?: boolean; | ||
| description?: JSX.Element; | ||
| dismissible?: boolean; | ||
| description?: ToastContent; | ||
| duration?: number; | ||
| delete?: boolean; | ||
| important?: boolean; | ||
| action?: { | ||
| label: string; | ||
| onClick: (event: MouseEvent) => void; | ||
| }; | ||
| cancel?: { | ||
| label: string; | ||
| onClick?: () => void; | ||
| }; | ||
| action?: Action | JSX.Element; | ||
| cancel?: Action | JSX.Element; | ||
| onDismiss?: (toast: ToastT) => void; | ||
@@ -61,12 +74,16 @@ onAutoClose?: (toast: ToastT) => void; | ||
| unstyled?: boolean; | ||
| className?: string; | ||
| classNames?: ToastClassnames; | ||
| descriptionClassName?: string; | ||
| position?: Position; | ||
| testId?: string; | ||
| class?: string; | ||
| classes?: ToastClasses; | ||
| classes?: ToastClassnames; | ||
| descriptionClass?: string; | ||
| position?: Position; | ||
| } | ||
| type Position = 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right' | 'top-center' | 'bottom-center'; | ||
| interface ToastOptions { | ||
| class?: string; | ||
| className?: string; | ||
| closeButton?: boolean; | ||
| descriptionClass?: string; | ||
| descriptionClassName?: string; | ||
| style?: JSX.CSSProperties; | ||
@@ -77,5 +94,18 @@ cancelButtonStyle?: JSX.CSSProperties; | ||
| unstyled?: boolean; | ||
| classes?: ToastClasses; | ||
| classNames?: ToastClassnames; | ||
| closeButtonAriaLabel?: string; | ||
| toasterId?: string; | ||
| class?: string; | ||
| classes?: ToastClassnames; | ||
| descriptionClass?: string; | ||
| } | ||
| type Offset = { | ||
| top?: string | number; | ||
| right?: string | number; | ||
| bottom?: string | number; | ||
| left?: string | number; | ||
| } | string | number; | ||
| type SwipeDirection = 'top' | 'right' | 'bottom' | 'left'; | ||
| interface ToasterProps { | ||
| id?: string; | ||
| invert?: boolean; | ||
@@ -92,36 +122,46 @@ theme?: 'light' | 'dark' | 'system'; | ||
| toastOptions?: ToastOptions; | ||
| class?: string; | ||
| className?: string; | ||
| style?: JSX.CSSProperties; | ||
| offset?: string | number; | ||
| offset?: Offset; | ||
| mobileOffset?: Offset; | ||
| dir?: 'rtl' | 'ltr' | 'auto'; | ||
| swipeDirections?: SwipeDirection[]; | ||
| icons?: ToastIcons; | ||
| dir?: 'rtl' | 'ltr' | 'auto'; | ||
| customAriaLabel?: string; | ||
| containerAriaLabel?: string; | ||
| pauseWhenPageIsHidden?: boolean; | ||
| class?: string; | ||
| } | ||
| type ExternalToast = Omit<ToastT, 'id' | 'type' | 'title' | 'delete' | 'promise'> & { | ||
| interface ToastToDismiss { | ||
| id: number | string; | ||
| dismiss: boolean; | ||
| } | ||
| type ExternalToast = Omit<ToastT, 'id' | 'type' | 'title' | 'jsx' | 'delete' | 'promise'> & { | ||
| id?: number | string; | ||
| toasterId?: string; | ||
| }; | ||
| declare function toastFunction(message: string | JSX.Element, data?: ExternalToast): string | number; | ||
| declare function toastFunction(message: ToastContent, data?: ExternalToast): string | number; | ||
| declare const toast: typeof toastFunction & { | ||
| success: (message: string | JSX.Element, data?: ExternalToast) => string | number; | ||
| info: (message: string | JSX.Element, data?: ExternalToast) => string | number; | ||
| warning: (message: string | JSX.Element, data?: ExternalToast) => string | number; | ||
| error: (message: string | JSX.Element, data?: ExternalToast) => string | number; | ||
| success: (message: ToastContent, data?: ExternalToast) => string | number; | ||
| info: (message: ToastContent, data?: ExternalToast) => string | number; | ||
| warning: (message: ToastContent, data?: ExternalToast) => string | number; | ||
| error: (message: ToastContent, data?: ExternalToast) => string | number; | ||
| custom: (jsx: (id: number | string) => JSX.Element, data?: ExternalToast) => string | number; | ||
| message: (message: string | JSX.Element, data?: ExternalToast) => string | number; | ||
| promise: <ToastData>(promise: PromiseT<ToastData>, data?: PromiseData<ToastData> | undefined) => string | number | undefined; | ||
| message: (message: ToastContent, data?: ExternalToast) => string | number; | ||
| promise: <ToastData>(promise: PromiseT<ToastData>, data?: PromiseData<ToastData> | undefined) => { | ||
| unwrap: () => Promise<ToastData>; | ||
| } | undefined; | ||
| dismiss: (id?: number | string) => string | number | undefined; | ||
| loading: (message: string | JSX.Element, data?: ExternalToast) => string | number; | ||
| loading: (message: ToastContent, data?: ExternalToast) => string | number; | ||
| } & { | ||
| getHistory: () => (ToastT | ToastToDismiss)[]; | ||
| getToasts: () => ToastT[]; | ||
| }; | ||
| /*! | ||
| * Original code by Emil Kowalski | ||
| * MIT Licensed, Copyright 2023 Emil Kowalski, see https://github.com/emilkowalski/sonner/blob/main/LICENSE.md for details | ||
| * | ||
| * Credits: | ||
| * https://github.com/emilkowalski/sonner/blob/main/src/index.tsx | ||
| */ | ||
| declare function useSonner(): { | ||
| toasts: solid_js.Accessor<ToastT[]>; | ||
| }; | ||
| declare function Toaster(props: ToasterProps): JSX.Element; | ||
| declare const Toaster: Component<ToasterProps>; | ||
| export { type ExternalToast, type ToastT, Toaster, toast }; | ||
| export { type Action, type ExternalToast, type ToastClassnames, type ToastT, type ToastToDismiss, Toaster, type ToasterProps, toast, useSonner }; |
+666
-387
@@ -1,3 +0,3 @@ | ||
| import { delegateEvents, createComponent, insert, use, setAttribute, effect, className, style, template, addEventListener, memo } from 'solid-js/web'; | ||
| import { mergeProps, createSignal, onMount, onCleanup, createEffect, on, Show, For } from 'solid-js'; | ||
| import { delegateEvents, insert, createComponent, use, setAttribute, effect, className, style, memo, template } from 'solid-js/web'; | ||
| import { createSignal, onMount, onCleanup, mergeProps, createMemo, createEffect, For, Show, on } from 'solid-js'; | ||
| import { createStore, produce, reconcile } from 'solid-js/store'; | ||
@@ -30,4 +30,4 @@ | ||
| // src/styles.css | ||
| styleInject(':where(html[dir=ltr]),\n:where([data-sonner-toaster][dir=ltr]) {\n --toast-icon-margin-start: -3px;\n --toast-icon-margin-end: 4px;\n --toast-svg-margin-start: -1px;\n --toast-svg-margin-end: 0px;\n --toast-button-margin-start: auto;\n --toast-button-margin-end: 0;\n --toast-close-button-start: 0;\n --toast-close-button-end: unset;\n --toast-close-button-transform: translate(-35%, -35%);\n}\n:where(html[dir=rtl]),\n:where([data-sonner-toaster][dir=rtl]) {\n --toast-icon-margin-start: 4px;\n --toast-icon-margin-end: -3px;\n --toast-svg-margin-start: 0px;\n --toast-svg-margin-end: -1px;\n --toast-button-margin-start: 0;\n --toast-button-margin-end: auto;\n --toast-close-button-start: unset;\n --toast-close-button-end: 0;\n --toast-close-button-transform: translate(35%, -35%);\n}\n:where([data-sonner-toaster]) {\n position: fixed;\n width: var(--width);\n font-family:\n ui-sans-serif,\n system-ui,\n -apple-system,\n BlinkMacSystemFont,\n Segoe UI,\n Roboto,\n Helvetica Neue,\n Arial,\n Noto Sans,\n sans-serif,\n Apple Color Emoji,\n Segoe UI Emoji,\n Segoe UI Symbol,\n Noto Color Emoji;\n --gray1: hsl(0, 0%, 99%);\n --gray2: hsl(0, 0%, 97.3%);\n --gray3: hsl(0, 0%, 95.1%);\n --gray4: hsl(0, 0%, 93%);\n --gray5: hsl(0, 0%, 90.9%);\n --gray6: hsl(0, 0%, 88.7%);\n --gray7: hsl(0, 0%, 85.8%);\n --gray8: hsl(0, 0%, 78%);\n --gray9: hsl(0, 0%, 56.1%);\n --gray10: hsl(0, 0%, 52.3%);\n --gray11: hsl(0, 0%, 43.5%);\n --gray12: hsl(0, 0%, 9%);\n --border-radius: 8px;\n box-sizing: border-box;\n padding: 0;\n margin: 0;\n list-style: none;\n outline: none;\n z-index: 999999999;\n}\n:where([data-sonner-toaster][data-x-position=right]) {\n right: max(var(--offset), env(safe-area-inset-right));\n}\n:where([data-sonner-toaster][data-x-position=left]) {\n left: max(var(--offset), env(safe-area-inset-left));\n}\n:where([data-sonner-toaster][data-x-position=center]) {\n left: 50%;\n transform: translateX(-50%);\n}\n:where([data-sonner-toaster][data-y-position=top]) {\n top: max(var(--offset), env(safe-area-inset-top));\n}\n:where([data-sonner-toaster][data-y-position=bottom]) {\n bottom: max(var(--offset), env(safe-area-inset-bottom));\n}\n:where([data-sonner-toast]) {\n --y: translateY(100%);\n --lift-amount: calc(var(--lift) * var(--gap));\n z-index: var(--z-index);\n position: absolute;\n opacity: 0;\n transform: var(--y);\n filter: blur(0);\n touch-action: none;\n transition:\n transform 400ms,\n opacity 400ms,\n height 400ms,\n box-shadow 200ms;\n box-sizing: border-box;\n outline: none;\n overflow-wrap: anywhere;\n}\n:where([data-sonner-toast][data-styled=true]) {\n padding: 16px;\n background: var(--normal-bg);\n border: 1px solid var(--normal-border);\n color: var(--normal-text);\n border-radius: var(--border-radius);\n box-shadow: 0px 4px 12px rgba(0, 0, 0, 0.1);\n width: var(--width);\n font-size: 13px;\n display: flex;\n align-items: center;\n gap: 6px;\n}\n:where([data-sonner-toast]:focus-visible) {\n box-shadow: 0px 4px 12px rgba(0, 0, 0, 0.1), 0 0 0 2px rgba(0, 0, 0, 0.2);\n}\n:where([data-sonner-toast][data-y-position=top]) {\n top: 0;\n --y: translateY(-100%);\n --lift: 1;\n --lift-amount: calc(1 * var(--gap));\n}\n:where([data-sonner-toast][data-y-position=bottom]) {\n bottom: 0;\n --y: translateY(100%);\n --lift: -1;\n --lift-amount: calc(var(--lift) * var(--gap));\n}\n:where([data-sonner-toast]) :where([data-description]) {\n font-weight: 400;\n line-height: 1.4;\n color: inherit;\n}\n:where([data-sonner-toast]) :where([data-title]) {\n font-weight: 500;\n line-height: 1.5;\n color: inherit;\n}\n:where([data-sonner-toast]) :where([data-icon]) {\n display: flex;\n height: 16px;\n width: 16px;\n position: relative;\n justify-content: flex-start;\n align-items: center;\n flex-shrink: 0;\n margin-left: var(--toast-icon-margin-start);\n margin-right: var(--toast-icon-margin-end);\n}\n:where([data-sonner-toast][data-promise=true]) :where([data-icon]) > svg {\n opacity: 0;\n transform: scale(0.8);\n transform-origin: center;\n animation: sonner-fade-in 300ms ease forwards;\n}\n:where([data-sonner-toast]) :where([data-icon]) > * {\n flex-shrink: 0;\n}\n:where([data-sonner-toast]) :where([data-icon]) svg {\n margin-left: var(--toast-svg-margin-start);\n margin-right: var(--toast-svg-margin-end);\n}\n:where([data-sonner-toast]) :where([data-content]) {\n display: flex;\n flex-direction: column;\n gap: 2px;\n}\n[data-sonner-toast][data-styled=true] [data-button] {\n border-radius: 4px;\n padding-left: 8px;\n padding-right: 8px;\n height: 24px;\n font-size: 12px;\n color: var(--normal-bg);\n background: var(--normal-text);\n margin-left: var(--toast-button-margin-start);\n margin-right: var(--toast-button-margin-end);\n border: none;\n cursor: pointer;\n outline: none;\n display: flex;\n align-items: center;\n flex-shrink: 0;\n transition: opacity 400ms, box-shadow 200ms;\n}\n:where([data-sonner-toast]) :where([data-button]):focus-visible {\n box-shadow: 0 0 0 2px rgba(0, 0, 0, 0.4);\n}\n:where([data-sonner-toast]) :where([data-button]):first-of-type {\n margin-left: var(--toast-button-margin-start);\n margin-right: var(--toast-button-margin-end);\n}\n:where([data-sonner-toast]) :where([data-cancel]) {\n color: var(--normal-text);\n background: rgba(0, 0, 0, 0.08);\n}\n:where([data-sonner-toast][data-theme=dark]) :where([data-cancel]) {\n background: rgba(255, 255, 255, 0.3);\n}\n:where([data-sonner-toast]) :where([data-close-button]) {\n position: absolute;\n left: var(--toast-close-button-start);\n right: var(--toast-close-button-end);\n top: 0;\n height: 20px;\n width: 20px;\n display: flex;\n justify-content: center;\n align-items: center;\n padding: 0;\n background: var(--gray1);\n color: var(--gray12);\n border: 1px solid var(--gray4);\n transform: var(--toast-close-button-transform);\n border-radius: 50%;\n cursor: pointer;\n z-index: 1;\n transition:\n opacity 100ms,\n background 200ms,\n border-color 200ms;\n}\n:where([data-sonner-toast]) :where([data-close-button]):focus-visible {\n box-shadow: 0px 4px 12px rgba(0, 0, 0, 0.1), 0 0 0 2px rgba(0, 0, 0, 0.2);\n}\n:where([data-sonner-toast]) :where([data-disabled=true]) {\n cursor: not-allowed;\n}\n:where([data-sonner-toast]):hover :where([data-close-button]):hover {\n background: var(--gray2);\n border-color: var(--gray5);\n}\n:where([data-sonner-toast][data-swiping=true])::before {\n content: "";\n position: absolute;\n left: 0;\n right: 0;\n height: 100%;\n z-index: -1;\n}\n:where([data-sonner-toast][data-y-position=top][data-swiping=true])::before {\n bottom: 50%;\n transform: scaleY(3) translateY(50%);\n}\n:where([data-sonner-toast][data-y-position=bottom][data-swiping=true])::before {\n top: 50%;\n transform: scaleY(3) translateY(-50%);\n}\n:where([data-sonner-toast][data-swiping=false][data-removed=true])::before {\n content: "";\n position: absolute;\n inset: 0;\n transform: scaleY(2);\n}\n:where([data-sonner-toast])::after {\n content: "";\n position: absolute;\n left: 0;\n height: calc(var(--gap) + 1px);\n bottom: 100%;\n width: 100%;\n}\n:where([data-sonner-toast][data-mounted=true]) {\n --y: translateY(0);\n opacity: 1;\n}\n:where([data-sonner-toast][data-expanded=false][data-front=false]) {\n --scale: var(--toasts-before) * 0.05 + 1;\n --y: translateY(calc(var(--lift-amount) * var(--toasts-before))) scale(calc(-1 * var(--scale)));\n height: var(--front-toast-height);\n}\n:where([data-sonner-toast]) > * {\n transition: opacity 400ms;\n}\n:where([data-sonner-toast][data-expanded=false][data-front=false][data-styled=true]) > * {\n opacity: 0;\n}\n:where([data-sonner-toast][data-visible=false]) {\n opacity: 0;\n pointer-events: none;\n}\n:where([data-sonner-toast][data-mounted=true][data-expanded=true]) {\n --y: translateY(calc(var(--lift) * var(--offset)));\n height: var(--initial-height);\n}\n:where([data-sonner-toast][data-removed=true][data-front=true][data-swipe-out=false]) {\n --y: translateY(calc(var(--lift) * -100%));\n opacity: 0;\n}\n:where([data-sonner-toast][data-removed=true][data-front=false][data-swipe-out=false][data-expanded=true]) {\n --y: translateY(calc(var(--lift) * var(--offset) + var(--lift) * -100%));\n opacity: 0;\n}\n:where([data-sonner-toast][data-removed=true][data-front=false][data-swipe-out=false][data-expanded=false]) {\n --y: translateY(40%);\n opacity: 0;\n transition: transform 500ms, opacity 200ms;\n}\n:where([data-sonner-toast][data-removed=true][data-front=false])::before {\n height: calc(var(--initial-height) + 20%);\n}\n[data-sonner-toast][data-swiping=true] {\n transform: var(--y) translateY(var(--swipe-amount, 0px));\n transition: none;\n}\n[data-sonner-toast][data-swipe-out=true][data-y-position=bottom],\n[data-sonner-toast][data-swipe-out=true][data-y-position=top] {\n animation: swipe-out 200ms ease-out forwards;\n}\n@keyframes swipe-out {\n from {\n transform: translateY(calc(var(--lift) * var(--offset) + var(--swipe-amount)));\n opacity: 1;\n }\n to {\n transform: translateY(calc(var(--lift) * var(--offset) + var(--swipe-amount) + var(--lift) * -100%));\n opacity: 0;\n }\n}\n@media (max-width: 600px) {\n [data-sonner-toaster] {\n position: fixed;\n --mobile-offset: 16px;\n right: var(--mobile-offset);\n left: var(--mobile-offset);\n width: 100%;\n }\n [data-sonner-toaster] [data-sonner-toast] {\n left: 0;\n right: 0;\n width: calc(100% - var(--mobile-offset) * 2);\n }\n [data-sonner-toaster][data-x-position=left] {\n left: var(--mobile-offset);\n }\n [data-sonner-toaster][data-y-position=bottom] {\n bottom: 20px;\n }\n [data-sonner-toaster][data-y-position=top] {\n top: 20px;\n }\n [data-sonner-toaster][data-x-position=center] {\n left: var(--mobile-offset);\n right: var(--mobile-offset);\n transform: none;\n }\n}\n[data-sonner-toaster][data-theme=light] {\n --normal-bg: #fff;\n --normal-border: var(--gray4);\n --normal-text: var(--gray12);\n --success-bg: hsl(143, 85%, 96%);\n --success-border: hsl(145, 92%, 91%);\n --success-text: hsl(140, 100%, 27%);\n --info-bg: hsl(208, 100%, 97%);\n --info-border: hsl(221, 91%, 91%);\n --info-text: hsl(210, 92%, 45%);\n --warning-bg: hsl(49, 100%, 97%);\n --warning-border: hsl(49, 91%, 91%);\n --warning-text: hsl(31, 92%, 45%);\n --error-bg: hsl(359, 100%, 97%);\n --error-border: hsl(359, 100%, 94%);\n --error-text: hsl(360, 100%, 45%);\n}\n[data-sonner-toaster][data-theme=light] [data-sonner-toast][data-invert=true] {\n --normal-bg: #000;\n --normal-border: hsl(0, 0%, 20%);\n --normal-text: var(--gray1);\n}\n[data-sonner-toaster][data-theme=dark] [data-sonner-toast][data-invert=true] {\n --normal-bg: #fff;\n --normal-border: var(--gray3);\n --normal-text: var(--gray12);\n}\n[data-sonner-toaster][data-theme=dark] {\n --normal-bg: #000;\n --normal-border: hsl(0, 0%, 20%);\n --normal-text: var(--gray1);\n --success-bg: hsl(150, 100%, 6%);\n --success-border: hsl(147, 100%, 12%);\n --success-text: hsl(150, 86%, 65%);\n --info-bg: hsl(215, 100%, 6%);\n --info-border: hsl(223, 100%, 12%);\n --info-text: hsl(216, 87%, 65%);\n --warning-bg: hsl(64, 100%, 6%);\n --warning-border: hsl(60, 100%, 12%);\n --warning-text: hsl(46, 87%, 65%);\n --error-bg: hsl(358, 76%, 10%);\n --error-border: hsl(357, 89%, 16%);\n --error-text: hsl(358, 100%, 81%);\n}\n[data-rich-colors=true] [data-sonner-toast][data-type=success] {\n background: var(--success-bg);\n border-color: var(--success-border);\n color: var(--success-text);\n}\n[data-rich-colors=true] [data-sonner-toast][data-type=success] [data-close-button] {\n background: var(--success-bg);\n border-color: var(--success-border);\n color: var(--success-text);\n}\n[data-rich-colors=true] [data-sonner-toast][data-type=info] {\n background: var(--info-bg);\n border-color: var(--info-border);\n color: var(--info-text);\n}\n[data-rich-colors=true] [data-sonner-toast][data-type=info] [data-close-button] {\n background: var(--info-bg);\n border-color: var(--info-border);\n color: var(--info-text);\n}\n[data-rich-colors=true] [data-sonner-toast][data-type=warning] {\n background: var(--warning-bg);\n border-color: var(--warning-border);\n color: var(--warning-text);\n}\n[data-rich-colors=true] [data-sonner-toast][data-type=warning] [data-close-button] {\n background: var(--warning-bg);\n border-color: var(--warning-border);\n color: var(--warning-text);\n}\n[data-rich-colors=true] [data-sonner-toast][data-type=error] {\n background: var(--error-bg);\n border-color: var(--error-border);\n color: var(--error-text);\n}\n[data-rich-colors=true] [data-sonner-toast][data-type=error] [data-close-button] {\n background: var(--error-bg);\n border-color: var(--error-border);\n color: var(--error-text);\n}\n.sonner-loading-wrapper {\n --size: 16px;\n height: var(--size);\n width: var(--size);\n position: absolute;\n inset: 0;\n z-index: 10;\n}\n.sonner-loading-wrapper[data-visible=false] {\n transform-origin: center;\n animation: sonner-fade-out 0.2s ease forwards;\n}\n.sonner-spinner {\n position: relative;\n top: 50%;\n left: 50%;\n height: var(--size);\n width: var(--size);\n}\n.sonner-loading-bar {\n animation: sonner-spin 1.2s linear infinite;\n background: var(--gray11);\n border-radius: 6px;\n height: 8%;\n left: -10%;\n position: absolute;\n top: -3.9%;\n width: 24%;\n}\n.sonner-loading-bar:nth-child(1) {\n animation-delay: -1.2s;\n transform: rotate(0.0001deg) translate(146%);\n}\n.sonner-loading-bar:nth-child(2) {\n animation-delay: -1.1s;\n transform: rotate(30deg) translate(146%);\n}\n.sonner-loading-bar:nth-child(3) {\n animation-delay: -1s;\n transform: rotate(60deg) translate(146%);\n}\n.sonner-loading-bar:nth-child(4) {\n animation-delay: -0.9s;\n transform: rotate(90deg) translate(146%);\n}\n.sonner-loading-bar:nth-child(5) {\n animation-delay: -0.8s;\n transform: rotate(120deg) translate(146%);\n}\n.sonner-loading-bar:nth-child(6) {\n animation-delay: -0.7s;\n transform: rotate(150deg) translate(146%);\n}\n.sonner-loading-bar:nth-child(7) {\n animation-delay: -0.6s;\n transform: rotate(180deg) translate(146%);\n}\n.sonner-loading-bar:nth-child(8) {\n animation-delay: -0.5s;\n transform: rotate(210deg) translate(146%);\n}\n.sonner-loading-bar:nth-child(9) {\n animation-delay: -0.4s;\n transform: rotate(240deg) translate(146%);\n}\n.sonner-loading-bar:nth-child(10) {\n animation-delay: -0.3s;\n transform: rotate(270deg) translate(146%);\n}\n.sonner-loading-bar:nth-child(11) {\n animation-delay: -0.2s;\n transform: rotate(300deg) translate(146%);\n}\n.sonner-loading-bar:nth-child(12) {\n animation-delay: -0.1s;\n transform: rotate(330deg) translate(146%);\n}\n@keyframes sonner-fade-in {\n 0% {\n opacity: 0;\n transform: scale(0.8);\n }\n 100% {\n opacity: 1;\n transform: scale(1);\n }\n}\n@keyframes sonner-fade-out {\n 0% {\n opacity: 1;\n transform: scale(1);\n }\n 100% {\n opacity: 0;\n transform: scale(0.8);\n }\n}\n@keyframes sonner-spin {\n 0% {\n opacity: 1;\n }\n 100% {\n opacity: 0.15;\n }\n}\n@media (prefers-reduced-motion) {\n [data-sonner-toast],\n [data-sonner-toast] > *,\n .sonner-loading-bar {\n transition: none !important;\n animation: none !important;\n }\n}\n.sonner-loader {\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n transform-origin: center;\n transition: opacity 200ms, transform 200ms;\n}\n.sonner-loader[data-visible=false] {\n opacity: 0;\n transform: scale(0.8) translate(-50%, -50%);\n}\n'); | ||
| var _tmpl$ = /* @__PURE__ */ template(`<div class=sonner-loading-wrapper><div class=sonner-spinner>`); | ||
| styleInject('html[dir=ltr],\n[data-sonner-toaster][dir=ltr] {\n --toast-icon-margin-start: -3px;\n --toast-icon-margin-end: 4px;\n --toast-svg-margin-start: -1px;\n --toast-svg-margin-end: 0px;\n --toast-button-margin-start: auto;\n --toast-button-margin-end: 0;\n --toast-close-button-start: 0;\n --toast-close-button-end: unset;\n --toast-close-button-transform: translate(-35%, -35%);\n}\nhtml[dir=rtl],\n[data-sonner-toaster][dir=rtl] {\n --toast-icon-margin-start: 4px;\n --toast-icon-margin-end: -3px;\n --toast-svg-margin-start: 0px;\n --toast-svg-margin-end: -1px;\n --toast-button-margin-start: 0;\n --toast-button-margin-end: auto;\n --toast-close-button-start: unset;\n --toast-close-button-end: 0;\n --toast-close-button-transform: translate(35%, -35%);\n}\n[data-sonner-toaster] {\n position: fixed;\n width: var(--width);\n font-family:\n ui-sans-serif,\n system-ui,\n -apple-system,\n BlinkMacSystemFont,\n Segoe UI,\n Roboto,\n Helvetica Neue,\n Arial,\n Noto Sans,\n sans-serif,\n Apple Color Emoji,\n Segoe UI Emoji,\n Segoe UI Symbol,\n Noto Color Emoji;\n --gray1: hsl(0, 0%, 99%);\n --gray2: hsl(0, 0%, 97.3%);\n --gray3: hsl(0, 0%, 95.1%);\n --gray4: hsl(0, 0%, 93%);\n --gray5: hsl(0, 0%, 90.9%);\n --gray6: hsl(0, 0%, 88.7%);\n --gray7: hsl(0, 0%, 85.8%);\n --gray8: hsl(0, 0%, 78%);\n --gray9: hsl(0, 0%, 56.1%);\n --gray10: hsl(0, 0%, 52.3%);\n --gray11: hsl(0, 0%, 43.5%);\n --gray12: hsl(0, 0%, 9%);\n --border-radius: 8px;\n box-sizing: border-box;\n padding: 0;\n margin: 0;\n list-style: none;\n outline: none;\n z-index: 999999999;\n transition: transform 400ms ease;\n}\n@media (hover: none) and (pointer: coarse) {\n [data-sonner-toaster][data-lifted=true] {\n transform: none;\n }\n}\n[data-sonner-toaster][data-x-position=right] {\n right: var(--offset-right);\n}\n[data-sonner-toaster][data-x-position=left] {\n left: var(--offset-left);\n}\n[data-sonner-toaster][data-x-position=center] {\n left: 50%;\n transform: translateX(-50%);\n}\n[data-sonner-toaster][data-y-position=top] {\n top: var(--offset-top);\n}\n[data-sonner-toaster][data-y-position=bottom] {\n bottom: var(--offset-bottom);\n}\n[data-sonner-toast] {\n --y: translateY(100%);\n --lift-amount: calc(var(--lift) * var(--gap));\n z-index: var(--z-index);\n position: absolute;\n opacity: 0;\n transform: var(--y);\n touch-action: none;\n transition:\n transform 400ms,\n opacity 400ms,\n height 400ms,\n box-shadow 200ms;\n box-sizing: border-box;\n outline: none;\n overflow-wrap: anywhere;\n}\n[data-sonner-toast][data-styled=true] {\n padding: 16px;\n background: var(--normal-bg);\n border: 1px solid var(--normal-border);\n color: var(--normal-text);\n border-radius: var(--border-radius);\n box-shadow: 0px 4px 12px rgba(0, 0, 0, 0.1);\n width: var(--width);\n font-size: 13px;\n display: flex;\n align-items: center;\n gap: 6px;\n}\n[data-sonner-toast]:focus-visible {\n box-shadow: 0px 4px 12px rgba(0, 0, 0, 0.1), 0 0 0 2px rgba(0, 0, 0, 0.2);\n}\n[data-sonner-toast][data-y-position=top] {\n top: 0;\n --y: translateY(-100%);\n --lift: 1;\n --lift-amount: calc(1 * var(--gap));\n}\n[data-sonner-toast][data-y-position=bottom] {\n bottom: 0;\n --y: translateY(100%);\n --lift: -1;\n --lift-amount: calc(var(--lift) * var(--gap));\n}\n[data-sonner-toast][data-styled=true] [data-description] {\n font-weight: 400;\n line-height: 1.4;\n color: #3f3f3f;\n}\n[data-rich-colors=true][data-sonner-toast][data-styled=true] [data-description] {\n color: inherit;\n}\n[data-sonner-toaster][data-sonner-theme=dark] [data-description] {\n color: hsl(0, 0%, 91%);\n}\n[data-sonner-toast][data-styled=true] [data-title] {\n font-weight: 500;\n line-height: 1.5;\n color: inherit;\n}\n[data-sonner-toast][data-styled=true] [data-icon] {\n display: flex;\n height: 16px;\n width: 16px;\n position: relative;\n justify-content: flex-start;\n align-items: center;\n flex-shrink: 0;\n margin-left: var(--toast-icon-margin-start);\n margin-right: var(--toast-icon-margin-end);\n}\n[data-sonner-toast][data-promise=true] [data-icon] > svg {\n opacity: 0;\n transform: scale(0.8);\n transform-origin: center;\n animation: sonner-fade-in 300ms ease forwards;\n}\n[data-sonner-toast][data-styled=true] [data-icon] > * {\n flex-shrink: 0;\n}\n[data-sonner-toast][data-styled=true] [data-icon] svg {\n margin-left: var(--toast-svg-margin-start);\n margin-right: var(--toast-svg-margin-end);\n}\n[data-sonner-toast][data-styled=true] [data-content] {\n display: flex;\n flex-direction: column;\n gap: 2px;\n}\n[data-sonner-toast][data-styled=true] [data-button] {\n border-radius: 4px;\n padding-left: 8px;\n padding-right: 8px;\n height: 24px;\n font-size: 12px;\n color: var(--normal-bg);\n background: var(--normal-text);\n margin-left: var(--toast-button-margin-start);\n margin-right: var(--toast-button-margin-end);\n border: none;\n font-weight: 500;\n cursor: pointer;\n outline: none;\n display: flex;\n align-items: center;\n flex-shrink: 0;\n transition: opacity 400ms, box-shadow 200ms;\n}\n[data-sonner-toast][data-styled=true] [data-button]:focus-visible {\n box-shadow: 0 0 0 2px rgba(0, 0, 0, 0.4);\n}\n[data-sonner-toast][data-styled=true] [data-button]:first-of-type {\n margin-left: var(--toast-button-margin-start);\n margin-right: var(--toast-button-margin-end);\n}\n[data-sonner-toast][data-styled=true] [data-cancel] {\n color: var(--normal-text);\n background: rgba(0, 0, 0, 0.08);\n}\n[data-sonner-toaster][data-sonner-theme=dark] [data-sonner-toast][data-styled=true] [data-cancel] {\n background: rgba(255, 255, 255, 0.3);\n}\n[data-sonner-toast][data-styled=true] [data-close-button] {\n position: absolute;\n left: var(--toast-close-button-start);\n right: var(--toast-close-button-end);\n top: 0;\n height: 20px;\n width: 20px;\n display: flex;\n justify-content: center;\n align-items: center;\n padding: 0;\n color: var(--normal-text);\n background: var(--normal-bg);\n border: 1px solid var(--normal-border);\n transform: var(--toast-close-button-transform);\n border-radius: 50%;\n cursor: pointer;\n z-index: 1;\n transition:\n opacity 100ms,\n background 200ms,\n border-color 200ms;\n}\n[data-sonner-toast][data-styled=true] [data-close-button]:focus-visible {\n box-shadow: 0px 4px 12px rgba(0, 0, 0, 0.1), 0 0 0 2px rgba(0, 0, 0, 0.2);\n}\n[data-sonner-toast][data-styled=true] [data-disabled=true] {\n cursor: not-allowed;\n}\n[data-sonner-toast][data-styled=true]:hover [data-close-button]:hover {\n background: var(--gray2);\n border-color: var(--gray5);\n}\n[data-sonner-toast][data-swiping=true]::before {\n content: "";\n position: absolute;\n left: -100%;\n right: -100%;\n height: 100%;\n z-index: -1;\n}\n[data-sonner-toast][data-y-position=top][data-swiping=true]::before {\n bottom: 50%;\n transform: scaleY(3) translateY(50%);\n}\n[data-sonner-toast][data-y-position=bottom][data-swiping=true]::before {\n top: 50%;\n transform: scaleY(3) translateY(-50%);\n}\n[data-sonner-toast][data-swiping=false][data-removed=true]::before {\n content: "";\n position: absolute;\n inset: 0;\n transform: scaleY(2);\n}\n[data-sonner-toast][data-expanded=true]::after {\n content: "";\n position: absolute;\n left: 0;\n height: calc(var(--gap) + 1px);\n bottom: 100%;\n width: 100%;\n}\n[data-sonner-toast][data-mounted=true] {\n --y: translateY(0);\n opacity: 1;\n}\n[data-sonner-toast][data-expanded=false][data-front=false] {\n --scale: var(--toasts-before) * 0.05 + 1;\n --y: translateY(calc(var(--lift-amount) * var(--toasts-before))) scale(calc(-1 * var(--scale)));\n height: var(--front-toast-height);\n}\n[data-sonner-toast] > * {\n transition: opacity 400ms;\n}\n[data-sonner-toast][data-x-position=right] {\n right: 0;\n}\n[data-sonner-toast][data-x-position=left] {\n left: 0;\n}\n[data-sonner-toast][data-expanded=false][data-front=false][data-styled=true] > * {\n opacity: 0;\n}\n[data-sonner-toast][data-visible=false] {\n opacity: 0;\n pointer-events: none;\n}\n[data-sonner-toast][data-mounted=true][data-expanded=true] {\n --y: translateY(calc(var(--lift) * var(--offset)));\n height: var(--initial-height);\n}\n[data-sonner-toast][data-removed=true][data-front=true][data-swipe-out=false] {\n --y: translateY(calc(var(--lift) * -100%));\n opacity: 0;\n}\n[data-sonner-toast][data-removed=true][data-front=false][data-swipe-out=false][data-expanded=true] {\n --y: translateY(calc(var(--lift) * var(--offset) + var(--lift) * -100%));\n opacity: 0;\n}\n[data-sonner-toast][data-removed=true][data-front=false][data-swipe-out=false][data-expanded=false] {\n --y: translateY(40%);\n opacity: 0;\n transition: transform 500ms, opacity 200ms;\n}\n[data-sonner-toast][data-removed=true][data-front=false]::before {\n height: calc(var(--initial-height) + 20%);\n}\n[data-sonner-toast][data-swiping=true] {\n transform: var(--y) translateY(var(--swipe-amount-y, 0px)) translateX(var(--swipe-amount-x, 0px));\n transition: none;\n}\n[data-sonner-toast][data-swiped=true] {\n -webkit-user-select: none;\n user-select: none;\n}\n[data-sonner-toast][data-swipe-out=true][data-y-position=bottom],\n[data-sonner-toast][data-swipe-out=true][data-y-position=top] {\n animation-duration: 200ms;\n animation-timing-function: ease-out;\n animation-fill-mode: forwards;\n}\n[data-sonner-toast][data-swipe-out=true][data-swipe-direction=left] {\n animation-name: swipe-out-left;\n}\n[data-sonner-toast][data-swipe-out=true][data-swipe-direction=right] {\n animation-name: swipe-out-right;\n}\n[data-sonner-toast][data-swipe-out=true][data-swipe-direction=up] {\n animation-name: swipe-out-up;\n}\n[data-sonner-toast][data-swipe-out=true][data-swipe-direction=down] {\n animation-name: swipe-out-down;\n}\n@keyframes swipe-out-left {\n from {\n transform: var(--y) translateX(var(--swipe-amount-x));\n opacity: 1;\n }\n to {\n transform: var(--y) translateX(calc(var(--swipe-amount-x) - 100%));\n opacity: 0;\n }\n}\n@keyframes swipe-out-right {\n from {\n transform: var(--y) translateX(var(--swipe-amount-x));\n opacity: 1;\n }\n to {\n transform: var(--y) translateX(calc(var(--swipe-amount-x) + 100%));\n opacity: 0;\n }\n}\n@keyframes swipe-out-up {\n from {\n transform: var(--y) translateY(var(--swipe-amount-y));\n opacity: 1;\n }\n to {\n transform: var(--y) translateY(calc(var(--swipe-amount-y) - 100%));\n opacity: 0;\n }\n}\n@keyframes swipe-out-down {\n from {\n transform: var(--y) translateY(var(--swipe-amount-y));\n opacity: 1;\n }\n to {\n transform: var(--y) translateY(calc(var(--swipe-amount-y) + 100%));\n opacity: 0;\n }\n}\n@media (max-width: 600px) {\n [data-sonner-toaster] {\n position: fixed;\n right: var(--mobile-offset-right);\n left: var(--mobile-offset-left);\n width: 100%;\n }\n [data-sonner-toaster][dir=rtl] {\n left: calc(var(--mobile-offset-left) * -1);\n }\n [data-sonner-toaster] [data-sonner-toast] {\n left: 0;\n right: 0;\n width: calc(100% - var(--mobile-offset-left) * 2);\n }\n [data-sonner-toaster][data-x-position=left] {\n left: var(--mobile-offset-left);\n }\n [data-sonner-toaster][data-y-position=bottom] {\n bottom: var(--mobile-offset-bottom);\n }\n [data-sonner-toaster][data-y-position=top] {\n top: var(--mobile-offset-top);\n }\n [data-sonner-toaster][data-x-position=center] {\n left: var(--mobile-offset-left);\n right: var(--mobile-offset-right);\n transform: none;\n }\n}\n[data-sonner-toaster][data-sonner-theme=light] {\n --normal-bg: #fff;\n --normal-border: var(--gray4);\n --normal-text: var(--gray12);\n --success-bg: hsl(143, 85%, 96%);\n --success-border: hsl(145, 92%, 87%);\n --success-text: hsl(140, 100%, 27%);\n --info-bg: hsl(208, 100%, 97%);\n --info-border: hsl(221, 91%, 93%);\n --info-text: hsl(210, 92%, 45%);\n --warning-bg: hsl(49, 100%, 97%);\n --warning-border: hsl(49, 91%, 84%);\n --warning-text: hsl(31, 92%, 45%);\n --error-bg: hsl(359, 100%, 97%);\n --error-border: hsl(359, 100%, 94%);\n --error-text: hsl(360, 100%, 45%);\n}\n[data-sonner-toaster][data-sonner-theme=light] [data-sonner-toast][data-invert=true] {\n --normal-bg: #000;\n --normal-border: hsl(0, 0%, 20%);\n --normal-text: var(--gray1);\n}\n[data-sonner-toaster][data-sonner-theme=dark] [data-sonner-toast][data-invert=true] {\n --normal-bg: #fff;\n --normal-border: var(--gray3);\n --normal-text: var(--gray12);\n}\n[data-sonner-toaster][data-sonner-theme=dark] {\n --normal-bg: #000;\n --normal-bg-hover: hsl(0, 0%, 12%);\n --normal-border: hsl(0, 0%, 20%);\n --normal-border-hover: hsl(0, 0%, 25%);\n --normal-text: var(--gray1);\n --success-bg: hsl(150, 100%, 6%);\n --success-border: hsl(147, 100%, 12%);\n --success-text: hsl(150, 86%, 65%);\n --info-bg: hsl(215, 100%, 6%);\n --info-border: hsl(223, 43%, 17%);\n --info-text: hsl(216, 87%, 65%);\n --warning-bg: hsl(64, 100%, 6%);\n --warning-border: hsl(60, 100%, 9%);\n --warning-text: hsl(46, 87%, 65%);\n --error-bg: hsl(358, 76%, 10%);\n --error-border: hsl(357, 89%, 16%);\n --error-text: hsl(358, 100%, 81%);\n}\n[data-sonner-toaster][data-sonner-theme=dark] [data-sonner-toast] [data-close-button] {\n background: var(--normal-bg);\n border-color: var(--normal-border);\n color: var(--normal-text);\n}\n[data-sonner-toaster][data-sonner-theme=dark] [data-sonner-toast] [data-close-button]:hover {\n background: var(--normal-bg-hover);\n border-color: var(--normal-border-hover);\n}\n[data-rich-colors=true][data-sonner-toast][data-type=success] {\n background: var(--success-bg);\n border-color: var(--success-border);\n color: var(--success-text);\n}\n[data-rich-colors=true][data-sonner-toast][data-type=success] [data-close-button] {\n background: var(--success-bg);\n border-color: var(--success-border);\n color: var(--success-text);\n}\n[data-rich-colors=true][data-sonner-toast][data-type=info] {\n background: var(--info-bg);\n border-color: var(--info-border);\n color: var(--info-text);\n}\n[data-rich-colors=true][data-sonner-toast][data-type=info] [data-close-button] {\n background: var(--info-bg);\n border-color: var(--info-border);\n color: var(--info-text);\n}\n[data-rich-colors=true][data-sonner-toast][data-type=warning] {\n background: var(--warning-bg);\n border-color: var(--warning-border);\n color: var(--warning-text);\n}\n[data-rich-colors=true][data-sonner-toast][data-type=warning] [data-close-button] {\n background: var(--warning-bg);\n border-color: var(--warning-border);\n color: var(--warning-text);\n}\n[data-rich-colors=true][data-sonner-toast][data-type=error] {\n background: var(--error-bg);\n border-color: var(--error-border);\n color: var(--error-text);\n}\n[data-rich-colors=true][data-sonner-toast][data-type=error] [data-close-button] {\n background: var(--error-bg);\n border-color: var(--error-border);\n color: var(--error-text);\n}\n.sonner-loading-wrapper {\n --size: 16px;\n height: var(--size);\n width: var(--size);\n position: absolute;\n inset: 0;\n z-index: 10;\n}\n.sonner-loading-wrapper[data-visible=false] {\n transform-origin: center;\n animation: sonner-fade-out 0.2s ease forwards;\n}\n.sonner-spinner {\n position: relative;\n top: 50%;\n left: 50%;\n height: var(--size);\n width: var(--size);\n}\n.sonner-loading-bar {\n animation: sonner-spin 1.2s linear infinite;\n background: var(--gray11);\n border-radius: 6px;\n height: 8%;\n left: -10%;\n position: absolute;\n top: -3.9%;\n width: 24%;\n}\n.sonner-loading-bar:nth-child(1) {\n animation-delay: -1.2s;\n transform: rotate(0.0001deg) translate(146%);\n}\n.sonner-loading-bar:nth-child(2) {\n animation-delay: -1.1s;\n transform: rotate(30deg) translate(146%);\n}\n.sonner-loading-bar:nth-child(3) {\n animation-delay: -1s;\n transform: rotate(60deg) translate(146%);\n}\n.sonner-loading-bar:nth-child(4) {\n animation-delay: -0.9s;\n transform: rotate(90deg) translate(146%);\n}\n.sonner-loading-bar:nth-child(5) {\n animation-delay: -0.8s;\n transform: rotate(120deg) translate(146%);\n}\n.sonner-loading-bar:nth-child(6) {\n animation-delay: -0.7s;\n transform: rotate(150deg) translate(146%);\n}\n.sonner-loading-bar:nth-child(7) {\n animation-delay: -0.6s;\n transform: rotate(180deg) translate(146%);\n}\n.sonner-loading-bar:nth-child(8) {\n animation-delay: -0.5s;\n transform: rotate(210deg) translate(146%);\n}\n.sonner-loading-bar:nth-child(9) {\n animation-delay: -0.4s;\n transform: rotate(240deg) translate(146%);\n}\n.sonner-loading-bar:nth-child(10) {\n animation-delay: -0.3s;\n transform: rotate(270deg) translate(146%);\n}\n.sonner-loading-bar:nth-child(11) {\n animation-delay: -0.2s;\n transform: rotate(300deg) translate(146%);\n}\n.sonner-loading-bar:nth-child(12) {\n animation-delay: -0.1s;\n transform: rotate(330deg) translate(146%);\n}\n@keyframes sonner-fade-in {\n 0% {\n opacity: 0;\n transform: scale(0.8);\n }\n 100% {\n opacity: 1;\n transform: scale(1);\n }\n}\n@keyframes sonner-fade-out {\n 0% {\n opacity: 1;\n transform: scale(1);\n }\n 100% {\n opacity: 0;\n transform: scale(0.8);\n }\n}\n@keyframes sonner-spin {\n 0% {\n opacity: 1;\n }\n 100% {\n opacity: 0.15;\n }\n}\n@media (prefers-reduced-motion) {\n [data-sonner-toast],\n [data-sonner-toast] > *,\n .sonner-loading-bar {\n transition: none !important;\n animation: none !important;\n }\n}\n.sonner-loader {\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n transform-origin: center;\n transition: opacity 200ms, transform 200ms;\n}\n.sonner-loader[data-visible=false] {\n opacity: 0;\n transform: scale(0.8) translate(-50%, -50%);\n}\n'); | ||
| var _tmpl$ = /* @__PURE__ */ template(`<div><div class=sonner-spinner>`); | ||
| var _tmpl$2 = /* @__PURE__ */ template(`<div class=sonner-loading-bar>`); | ||
@@ -38,2 +38,3 @@ var _tmpl$3 = /* @__PURE__ */ template(`<svg xmlns=http://www.w3.org/2000/svg viewBox="0 0 20 20"fill=currentColor height=20 width=20><path fill-rule=evenodd d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.857-9.809a.75.75 0 00-1.214-.882l-3.483 4.79-1.88-1.88a.75.75 0 10-1.06 1.061l2.5 2.5a.75.75 0 001.137-.089l4-5.5z"clip-rule=evenodd>`); | ||
| var _tmpl$6 = /* @__PURE__ */ template(`<svg xmlns=http://www.w3.org/2000/svg viewBox="0 0 20 20"fill=currentColor height=20 width=20><path fill-rule=evenodd d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-8-5a.75.75 0 01.75.75v4.5a.75.75 0 01-1.5 0v-4.5A.75.75 0 0110 5zm0 10a1 1 0 100-2 1 1 0 000 2z"clip-rule=evenodd>`); | ||
| var _tmpl$7 = /* @__PURE__ */ template(`<svg xmlns=http://www.w3.org/2000/svg width=12 height=12 viewBox="0 0 24 24"fill=none stroke=currentColor stroke-width=1.5 stroke-linecap=round stroke-linejoin=round><line x1=18 y1=6 x2=6 y2=18></line><line x1=6 y1=6 x2=18 y2=18>`); | ||
| var bars = Array(12).fill(0); | ||
@@ -45,5 +46,17 @@ function Loader(props) { | ||
| each: bars, | ||
| children: () => _tmpl$2() | ||
| children: (_, i) => (() => { | ||
| var _el$3 = _tmpl$2(); | ||
| effect(() => setAttribute(_el$3, "data-index", i())); | ||
| return _el$3; | ||
| })() | ||
| })); | ||
| effect(() => setAttribute(_el$, "data-visible", props.visible)); | ||
| effect((_p$) => { | ||
| var _v$ = ["sonner-loading-wrapper", props.class ?? props.className].filter(Boolean).join(" "), _v$2 = props.visible; | ||
| _v$ !== _p$.e && className(_el$, _p$.e = _v$); | ||
| _v$2 !== _p$.t && setAttribute(_el$, "data-visible", _p$.t = _v$2); | ||
| return _p$; | ||
| }, { | ||
| e: void 0, | ||
| t: void 0 | ||
| }); | ||
| return _el$; | ||
@@ -67,9 +80,9 @@ })(); | ||
| case "success": | ||
| return SuccessIcon; | ||
| return createComponent(SuccessIcon, {}); | ||
| case "info": | ||
| return InfoIcon; | ||
| return createComponent(InfoIcon, {}); | ||
| case "warning": | ||
| return WarningIcon; | ||
| return createComponent(WarningIcon, {}); | ||
| case "error": | ||
| return ErrorIcon; | ||
| return createComponent(ErrorIcon, {}); | ||
| default: | ||
@@ -79,13 +92,30 @@ return null; | ||
| } | ||
| function CloseIcon() { | ||
| return _tmpl$7(); | ||
| } | ||
| function useIsDocumentHidden() { | ||
| const [isDocumentHidden, setIsDocumentHidden] = createSignal(typeof document !== "undefined" ? document.hidden : false); | ||
| onMount(() => { | ||
| const callback = () => { | ||
| setIsDocumentHidden(document.hidden); | ||
| }; | ||
| document.addEventListener("visibilitychange", callback); | ||
| onCleanup(() => { | ||
| document.removeEventListener("visibilitychange", callback); | ||
| }); | ||
| }); | ||
| return isDocumentHidden; | ||
| } | ||
| // src/state.ts | ||
| var toastsCounter = 0; | ||
| var toastsCounter = 1; | ||
| var Observer = class { | ||
| subscribers; | ||
| toasts; | ||
| dismissedToasts; | ||
| constructor() { | ||
| this.subscribers = []; | ||
| this.toasts = []; | ||
| this.dismissedToasts = /* @__PURE__ */ new Set(); | ||
| } | ||
| // We use arrow functions to maintain the correct `this` reference | ||
| subscribe = (subscriber) => { | ||
@@ -107,11 +137,21 @@ this.subscribers.push(subscriber); | ||
| const { message, ...rest } = data; | ||
| const id = typeof data?.id === "number" || data.id && data.id?.length > 0 ? data.id : toastsCounter++; | ||
| const hasStringId = typeof data.id === "string" && data.id.length > 0; | ||
| const id = typeof data.id === "number" || hasStringId ? data.id : toastsCounter++; | ||
| const alreadyExists = this.toasts.find((toast2) => { | ||
| return toast2.id === id; | ||
| }); | ||
| const dismissible = data.dismissible === void 0 ? true : data.dismissible; | ||
| if (this.dismissedToasts.has(id)) | ||
| this.dismissedToasts.delete(id); | ||
| if (alreadyExists) { | ||
| this.toasts = this.toasts.map((toast2) => { | ||
| if (toast2.id === id) { | ||
| this.publish({ ...toast2, ...data, id, title: message }); | ||
| return { ...toast2, ...data, id, title: message }; | ||
| this.publish({ ...toast2, ...data, id, title: message, dismissible }); | ||
| return { | ||
| ...toast2, | ||
| ...data, | ||
| id, | ||
| dismissible, | ||
| title: message | ||
| }; | ||
| } | ||
@@ -121,3 +161,3 @@ return toast2; | ||
| } else { | ||
| this.addToast({ title: message, ...rest, id }); | ||
| this.addToast({ title: message, ...rest, dismissible, id }); | ||
| } | ||
@@ -127,8 +167,13 @@ return id; | ||
| dismiss = (id) => { | ||
| if (!id) { | ||
| if (id) { | ||
| this.dismissedToasts.add(id); | ||
| requestAnimationFrame(() => { | ||
| this.subscribers.forEach((subscriber) => subscriber({ id, dismiss: true })); | ||
| }); | ||
| } else { | ||
| this.toasts.forEach((toast2) => { | ||
| this.dismissedToasts.add(toast2.id); | ||
| this.subscribers.forEach((subscriber) => subscriber({ id: toast2.id, dismiss: true })); | ||
| }); | ||
| } | ||
| this.subscribers.forEach((subscriber) => subscriber({ id, dismiss: true })); | ||
| return id; | ||
@@ -151,6 +196,8 @@ }; | ||
| }; | ||
| loading = (message, data) => { | ||
| return this.create({ ...data, type: "loading", message }); | ||
| }; | ||
| promise = (promise, data) => { | ||
| if (!data) { | ||
| if (!data) | ||
| return; | ||
| } | ||
| let id; | ||
@@ -162,22 +209,41 @@ if (data.loading !== void 0) { | ||
| type: "loading", | ||
| message: data.loading | ||
| message: data.loading, | ||
| description: typeof data.description !== "function" ? data.description : void 0 | ||
| }); | ||
| } | ||
| const p = promise instanceof Promise ? promise : promise(); | ||
| const p = Promise.resolve(typeof promise === "function" ? promise() : promise); | ||
| let shouldDismiss = id !== void 0; | ||
| p.then((response) => { | ||
| if (response && typeof response.ok === "boolean" && !response.ok) { | ||
| let result; | ||
| const originalPromise = p.then(async (response) => { | ||
| result = ["resolve", response]; | ||
| if (isValidElement(response)) { | ||
| shouldDismiss = false; | ||
| const message = typeof data.error === "function" ? data.error(`HTTP error! status: ${response.status}`) : data.error; | ||
| this.create({ id, type: "error", message }); | ||
| this.create({ id, type: "default", message: response }); | ||
| } else if (isHttpResponse(response) && !response.ok) { | ||
| shouldDismiss = false; | ||
| const promiseData = typeof data.error === "function" ? await data.error(`HTTP error! status: ${response.status}`) : data.error; | ||
| const description = typeof data.description === "function" ? await data.description(`HTTP error! status: ${response.status}`) : data.description; | ||
| const toastSettings = isExtendedResult(promiseData) ? promiseData : { message: promiseData }; | ||
| this.create({ id, type: "error", description, ...toastSettings }); | ||
| } else if (response instanceof Error) { | ||
| shouldDismiss = false; | ||
| const promiseData = typeof data.error === "function" ? await data.error(response) : data.error; | ||
| const description = typeof data.description === "function" ? await data.description(response) : data.description; | ||
| const toastSettings = isExtendedResult(promiseData) ? promiseData : { message: promiseData }; | ||
| this.create({ id, type: "error", description, ...toastSettings }); | ||
| } else if (data.success !== void 0) { | ||
| shouldDismiss = false; | ||
| const message = typeof data.success === "function" ? data.success(response) : data.success; | ||
| this.create({ id, type: "success", message }); | ||
| const promiseData = typeof data.success === "function" ? await data.success(response) : data.success; | ||
| const description = typeof data.description === "function" ? await data.description(response) : data.description; | ||
| const toastSettings = isExtendedResult(promiseData) ? promiseData : { message: promiseData }; | ||
| this.create({ id, type: "success", description, ...toastSettings }); | ||
| } | ||
| }).catch((error) => { | ||
| }).catch(async (error) => { | ||
| result = ["reject", error]; | ||
| if (data.error !== void 0) { | ||
| shouldDismiss = false; | ||
| const message = typeof data.error === "function" ? data.error(error) : data.error; | ||
| this.create({ id, type: "error", message }); | ||
| const promiseData = typeof data.error === "function" ? await data.error(error) : data.error; | ||
| const description = typeof data.description === "function" ? await data.description(error) : data.description; | ||
| const toastSettings = isExtendedResult(promiseData) ? promiseData : { message: promiseData }; | ||
| this.create({ id, type: "error", description, ...toastSettings }); | ||
| } | ||
@@ -191,14 +257,32 @@ }).finally(() => { | ||
| }); | ||
| return id; | ||
| const unwrap = () => new Promise((resolve, reject) => { | ||
| originalPromise.then(() => { | ||
| if (result[0] === "reject") | ||
| reject(result[1]); | ||
| else | ||
| resolve(result[1]); | ||
| }).catch(reject); | ||
| }); | ||
| if (typeof id !== "string" && typeof id !== "number") | ||
| return { unwrap }; | ||
| return Object.assign(id, { unwrap }); | ||
| }; | ||
| loading = (message, data) => { | ||
| return this.create({ ...data, type: "loading", message }); | ||
| }; | ||
| // We can't provide the toast we just created as a prop as we didn't create it yet, so we can create a default toast object, I just don't know how to use function in argument when calling()? | ||
| custom = (jsx, data) => { | ||
| const id = data?.id || toastsCounter++; | ||
| this.publish({ jsx: jsx(id), id, ...data }); | ||
| this.create({ jsx: jsx(id), ...data, id }); | ||
| return id; | ||
| }; | ||
| getActiveToasts = () => { | ||
| return this.toasts.filter((toast2) => !this.dismissedToasts.has(toast2.id)); | ||
| }; | ||
| }; | ||
| function isHttpResponse(data) { | ||
| return data && typeof data === "object" && "ok" in data && typeof data.ok === "boolean" && "status" in data && typeof data.status === "number"; | ||
| } | ||
| function isExtendedResult(value) { | ||
| return typeof value === "object" && value !== null && "message" in value; | ||
| } | ||
| function isValidElement(value) { | ||
| return typeof Node !== "undefined" && value instanceof Node; | ||
| } | ||
| var ToastState = new Observer(); | ||
@@ -215,49 +299,129 @@ function toastFunction(message, data) { | ||
| var basicToast = toastFunction; | ||
| var toast = Object.assign(basicToast, { | ||
| success: ToastState.success, | ||
| info: ToastState.info, | ||
| warning: ToastState.warning, | ||
| error: ToastState.error, | ||
| custom: ToastState.custom, | ||
| message: ToastState.message, | ||
| promise: ToastState.promise, | ||
| dismiss: ToastState.dismiss, | ||
| loading: ToastState.loading | ||
| }); | ||
| function useIsDocumentHidden() { | ||
| const [isDocumentHidden, setIsDocumentHidden] = createSignal(false); | ||
| onMount(() => { | ||
| const callback = () => { | ||
| setIsDocumentHidden(document.hidden); | ||
| }; | ||
| document.addEventListener("visibilitychange", callback); | ||
| onCleanup(() => { | ||
| window.removeEventListener("visibilitychange", callback); | ||
| }); | ||
| }); | ||
| return isDocumentHidden; | ||
| var getHistory = () => ToastState.toasts; | ||
| var getToasts = () => ToastState.getActiveToasts(); | ||
| var toast = Object.assign( | ||
| basicToast, | ||
| { | ||
| success: ToastState.success, | ||
| info: ToastState.info, | ||
| warning: ToastState.warning, | ||
| error: ToastState.error, | ||
| custom: ToastState.custom, | ||
| message: ToastState.message, | ||
| promise: ToastState.promise, | ||
| dismiss: ToastState.dismiss, | ||
| loading: ToastState.loading | ||
| }, | ||
| { | ||
| getHistory, | ||
| getToasts | ||
| } | ||
| ); | ||
| // src/types.ts | ||
| function isAction(action) { | ||
| return typeof action === "object" && action !== null && "label" in action; | ||
| } | ||
| // src/index.tsx | ||
| var _tmpl$7 = /* @__PURE__ */ template(`<div class=sonner-loader>`); | ||
| var _tmpl$22 = /* @__PURE__ */ template(`<button aria-label="Close toast"data-close-button><svg xmlns=http://www.w3.org/2000/svg width=12 height=12 viewBox="0 0 24 24"fill=none stroke=currentColor stroke-width=1.5 stroke-linecap=round stroke-linejoin=round><line x1=18 y1=6 x2=6 y2=18></line><line x1=6 y1=6 x2=18 y2=18>`); | ||
| var _tmpl$32 = /* @__PURE__ */ template(`<li aria-atomic=true role=status tabindex=0 data-sonner-toast="">`); | ||
| var _tmpl$42 = /* @__PURE__ */ template(`<div data-icon="">`); | ||
| var _tmpl$52 = /* @__PURE__ */ template(`<div data-description="">`); | ||
| var _tmpl$62 = /* @__PURE__ */ template(`<div data-content=""><div data-title="">`); | ||
| var _tmpl$72 = /* @__PURE__ */ template(`<button data-button data-cancel>`); | ||
| var _tmpl$8 = /* @__PURE__ */ template(`<button data-button="">`); | ||
| var _tmpl$9 = /* @__PURE__ */ template(`<section tabindex=-1>`); | ||
| var _tmpl$10 = /* @__PURE__ */ template(`<ol tabindex=-1 data-sonner-toaster>`); | ||
| var _tmpl$8 = /* @__PURE__ */ template(`<div>`); | ||
| var _tmpl$22 = /* @__PURE__ */ template(`<button data-close-button>`); | ||
| var _tmpl$32 = /* @__PURE__ */ template(`<div data-icon>`); | ||
| var _tmpl$42 = /* @__PURE__ */ template(`<div data-description>`); | ||
| var _tmpl$52 = /* @__PURE__ */ template(`<li tabindex=0 data-sonner-toast><div data-content><div data-title>`); | ||
| var _tmpl$62 = /* @__PURE__ */ template(`<button data-button data-cancel>`); | ||
| var _tmpl$72 = /* @__PURE__ */ template(`<button data-button data-action>`); | ||
| var _tmpl$82 = /* @__PURE__ */ template(`<section tabindex=-1 aria-live=polite aria-relevant="additions text"aria-atomic=false data-react-aria-top-layer>`); | ||
| var _tmpl$9 = /* @__PURE__ */ template(`<ol tabindex=-1 data-sonner-toaster style=--width:356px>`); | ||
| var VISIBLE_TOASTS_AMOUNT = 3; | ||
| var VIEWPORT_OFFSET = "32px"; | ||
| var VIEWPORT_OFFSET = "24px"; | ||
| var MOBILE_VIEWPORT_OFFSET = "16px"; | ||
| var TOAST_LIFETIME = 4e3; | ||
| var TOAST_WIDTH = 356; | ||
| var GAP = 14; | ||
| var SWIPE_TRESHOLD = 20; | ||
| var SWIPE_THRESHOLD = 45; | ||
| var TIME_BEFORE_UNMOUNT = 200; | ||
| function _cn(...classes) { | ||
| function cn(...classes) { | ||
| return classes.filter(Boolean).join(" "); | ||
| } | ||
| var Toast = (props) => { | ||
| function resolveContent(content) { | ||
| return typeof content === "function" ? content() : content; | ||
| } | ||
| function getDefaultSwipeDirections(position) { | ||
| const [y, x] = position.split("-"); | ||
| const directions = []; | ||
| if (y) directions.push(y); | ||
| if (x) directions.push(x); | ||
| return directions; | ||
| } | ||
| function getDocumentDirection() { | ||
| if (typeof window === "undefined" || typeof document === "undefined") return "ltr"; | ||
| const dirAttribute = document.documentElement.getAttribute("dir"); | ||
| if (dirAttribute === "auto" || !dirAttribute) return window.getComputedStyle(document.documentElement).direction; | ||
| return dirAttribute; | ||
| } | ||
| function assignOffset(defaultOffset, mobileOffset) { | ||
| const styles = {}; | ||
| [defaultOffset, mobileOffset].forEach((offset, index) => { | ||
| const isMobile = index === 1; | ||
| const prefix = isMobile ? "--mobile-offset" : "--offset"; | ||
| const defaultValue = isMobile ? MOBILE_VIEWPORT_OFFSET : VIEWPORT_OFFSET; | ||
| const assignAll = (value) => { | ||
| for (const key of ["top", "right", "bottom", "left"]) styles[`${prefix}-${key}`] = typeof value === "number" ? `${value}px` : value; | ||
| }; | ||
| if (typeof offset === "number" || typeof offset === "string") { | ||
| assignAll(offset); | ||
| return; | ||
| } | ||
| if (typeof offset === "object" && offset !== null) { | ||
| for (const key of ["top", "right", "bottom", "left"]) { | ||
| const value = offset[key]; | ||
| styles[`${prefix}-${key}`] = value === void 0 ? defaultValue : typeof value === "number" ? `${value}px` : value; | ||
| } | ||
| return; | ||
| } | ||
| assignAll(defaultValue); | ||
| }); | ||
| return styles; | ||
| } | ||
| function mergeClassNames(classNames, legacy) { | ||
| return classNames ?? legacy; | ||
| } | ||
| function mergeDescriptionClassName(descriptionClassName, legacy) { | ||
| return descriptionClassName ?? legacy ?? ""; | ||
| } | ||
| function mergeClassName(className, legacy) { | ||
| return className ?? legacy ?? ""; | ||
| } | ||
| function useSonner() { | ||
| const [activeToasts, setActiveToasts] = createSignal(toast.getToasts()); | ||
| onMount(() => { | ||
| const unsubscribe = ToastState.subscribe((toastItem) => { | ||
| if (toastItem.dismiss) { | ||
| requestAnimationFrame(() => { | ||
| setActiveToasts((toasts) => toasts.filter((t) => t.id !== toastItem.id)); | ||
| }); | ||
| return; | ||
| } | ||
| const nextToast = toastItem; | ||
| setActiveToasts((toasts) => { | ||
| const indexOfExistingToast = toasts.findIndex((t) => t.id === nextToast.id); | ||
| if (indexOfExistingToast !== -1) { | ||
| return [...toasts.slice(0, indexOfExistingToast), { | ||
| ...toasts[indexOfExistingToast], | ||
| ...nextToast | ||
| }, ...toasts.slice(indexOfExistingToast + 1)]; | ||
| } | ||
| return [nextToast, ...toasts]; | ||
| }); | ||
| }); | ||
| onCleanup(unsubscribe); | ||
| }); | ||
| return { | ||
| toasts: activeToasts | ||
| }; | ||
| } | ||
| function createToastTheme(theme) { | ||
| return theme !== "system" ? theme : typeof window !== "undefined" && window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light"; | ||
| } | ||
| function Toast(props) { | ||
| const [mounted, setMounted] = createSignal(false); | ||
@@ -267,20 +431,32 @@ const [removed, setRemoved] = createSignal(false); | ||
| const [swipeOut, setSwipeOut] = createSignal(false); | ||
| const [isSwiped, setIsSwiped] = createSignal(false); | ||
| const [swipeDirection, setSwipeDirection] = createSignal(null); | ||
| const [swipeOutDirection, setSwipeOutDirection] = createSignal(null); | ||
| const [offsetBeforeRemove, setOffsetBeforeRemove] = createSignal(0); | ||
| const [initialHeight, setInitialHeight] = createSignal(0); | ||
| let toastRef; | ||
| let dragStartTime = null; | ||
| let closeTimerStartTimeRef = 0; | ||
| let lastCloseTimerStartTimeRef = 0; | ||
| let remainingTime = TOAST_LIFETIME; | ||
| const [pointerStartRef, setPointerStartRef] = createSignal(null); | ||
| const isFront = () => props.index === 0; | ||
| const isVisible = () => props.index + 1 <= props.visibleToasts; | ||
| const toastType = () => props.toast.type; | ||
| const toastClassname = () => props.toast.class || ""; | ||
| const toastDescriptionClassname = () => props.toast.descriptionClass || ""; | ||
| const propsWithDefaults = mergeProps({ | ||
| gap: GAP | ||
| }, props); | ||
| const heightIndex = () => props.heights.findIndex((height) => height.toastId === props.toast.id) || 0; | ||
| const duration = () => props.toast.duration || props.duration || TOAST_LIFETIME; | ||
| let closeTimerStartTimeRef = 0; | ||
| let lastCloseTimerStartTimeRef = 0; | ||
| const [pointerStartRef, setPointerStartRef] = createSignal(null); | ||
| const coords = () => props.position.split("-"); | ||
| const toastsHeightBefore = () => { | ||
| const dismissible = () => props.toast.dismissible !== false; | ||
| const classNames = () => mergeClassNames(props.classNames, props.classes); | ||
| const className$1 = () => mergeClassName(props.className, props.class); | ||
| const descriptionClassName = () => mergeDescriptionClassName(props.descriptionClassName, props.descriptionClass); | ||
| const toastClassName = () => mergeClassName(props.toast.className, props.toast.class); | ||
| const toastDescriptionClassName = () => mergeDescriptionClassName(props.toast.descriptionClassName, props.toast.descriptionClass); | ||
| const toastClassNames = () => mergeClassNames(props.toast.classNames, props.toast.classes); | ||
| const closeButton = () => props.toast.closeButton ?? props.closeButton; | ||
| const duration = () => props.toast.duration ?? props.duration ?? TOAST_LIFETIME; | ||
| const invert = () => props.toast.invert ?? props.invert; | ||
| const disabled = () => toastType() === "loading"; | ||
| const swipeDirections = () => props.swipeDirections ?? getDefaultSwipeDirections(props.position); | ||
| const y = createMemo(() => props.position.split("-")[0]); | ||
| const x = createMemo(() => props.position.split("-")[1]); | ||
| const heightIndex = createMemo(() => Math.max(0, props.heights.findIndex((height) => height.toastId === props.toast.id))); | ||
| const toastsHeightBefore = createMemo(() => { | ||
| return props.heights.reduce((prev, curr, reducerIndex) => { | ||
@@ -290,13 +466,30 @@ if (reducerIndex >= heightIndex()) return prev; | ||
| }, 0); | ||
| }; | ||
| }); | ||
| const offset = createMemo(() => heightIndex() * (props.gap ?? GAP) + toastsHeightBefore()); | ||
| const isDocumentHidden = useIsDocumentHidden(); | ||
| const invert = () => props.toast.invert || props.invert; | ||
| const disabled = () => toastType() === "loading"; | ||
| const offset = () => heightIndex() * propsWithDefaults.gap + toastsHeightBefore(); | ||
| createEffect(() => { | ||
| remainingTime = duration(); | ||
| }); | ||
| function deleteToast() { | ||
| setRemoved(true); | ||
| setOffsetBeforeRemove(offset()); | ||
| props.setHeights((heights) => heights.filter((height) => height.toastId !== props.toast.id)); | ||
| window.setTimeout(() => { | ||
| props.removeToast(props.toast); | ||
| }, TIME_BEFORE_UNMOUNT); | ||
| } | ||
| function getLoadingIcon() { | ||
| if (props.icons?.loading) { | ||
| return (() => { | ||
| var _el$ = _tmpl$7(); | ||
| var _el$ = _tmpl$8(); | ||
| insert(_el$, () => props.icons.loading); | ||
| effect(() => setAttribute(_el$, "data-visible", toastType() === "loading")); | ||
| effect((_p$) => { | ||
| var _v$ = cn(classNames()?.loader, toastClassNames()?.loader, "sonner-loader"), _v$2 = toastType() === "loading"; | ||
| _v$ !== _p$.e && className(_el$, _p$.e = _v$); | ||
| _v$2 !== _p$.t && setAttribute(_el$, "data-visible", _p$.t = _v$2); | ||
| return _p$; | ||
| }, { | ||
| e: void 0, | ||
| t: void 0 | ||
| }); | ||
| return _el$; | ||
@@ -306,2 +499,5 @@ })(); | ||
| return createComponent(Loader, { | ||
| get ["class"]() { | ||
| return cn(classNames()?.loader, toastClassNames()?.loader); | ||
| }, | ||
| get visible() { | ||
@@ -312,38 +508,43 @@ return toastType() === "loading"; | ||
| } | ||
| const icon = () => props.toast.icon ?? props.icons?.[toastType()] ?? getAsset(toastType()); | ||
| onMount(() => { | ||
| setMounted(true); | ||
| if (!toastRef) return; | ||
| const height = toastRef.getBoundingClientRect().height; | ||
| setInitialHeight(height); | ||
| props.setHeights((h) => [{ | ||
| toastId: props.toast.id, | ||
| toasterId: props.toast.toasterId, | ||
| height, | ||
| position: props.toast.position | ||
| }, ...h]); | ||
| onCleanup(() => { | ||
| props.setHeights((h) => h.filter((height2) => height2.toastId !== props.toast.id)); | ||
| }); | ||
| }); | ||
| onMount(() => { | ||
| const toastNode = toastRef; | ||
| const originalHeight = toastNode.style.height; | ||
| toastNode.style.height = "auto"; | ||
| const newHeight = toastNode.getBoundingClientRect().height; | ||
| toastNode.style.height = originalHeight; | ||
| setInitialHeight(newHeight); | ||
| createEffect(() => { | ||
| props.setHeights((heights) => { | ||
| const alreadyExists = heights.find((height) => height.toastId === props.toast.id); | ||
| if (!alreadyExists) return [{ | ||
| toastId: props.toast.id, | ||
| height: newHeight, | ||
| position: props.toast.position | ||
| }, ...heights]; | ||
| else return heights.map((height) => height.toastId === props.toast.id ? { | ||
| ...height, | ||
| height: newHeight | ||
| } : height); | ||
| }); | ||
| createEffect(() => { | ||
| if (!mounted() || !toastRef) return; | ||
| resolveContent(props.toast.title); | ||
| resolveContent(props.toast.description); | ||
| const originalHeight = toastRef.style.height; | ||
| toastRef.style.height = "auto"; | ||
| const nextHeight = toastRef.getBoundingClientRect().height; | ||
| toastRef.style.height = originalHeight; | ||
| setInitialHeight(nextHeight); | ||
| props.setHeights((heights) => { | ||
| const alreadyExists = heights.find((height) => height.toastId === props.toast.id); | ||
| if (!alreadyExists) return [{ | ||
| toastId: props.toast.id, | ||
| toasterId: props.toast.toasterId, | ||
| height: nextHeight, | ||
| position: props.toast.position | ||
| }, ...heights]; | ||
| return heights.map((height) => height.toastId === props.toast.id ? { | ||
| ...height, | ||
| height: nextHeight | ||
| } : height); | ||
| }); | ||
| }); | ||
| const deleteToast = () => { | ||
| setRemoved(true); | ||
| setOffsetBeforeRemove(offset()); | ||
| props.setHeights((h) => h.filter((height) => height.toastId !== props.toast.id)); | ||
| setTimeout(() => { | ||
| props.removeToast(props.toast); | ||
| }, TIME_BEFORE_UNMOUNT); | ||
| }; | ||
| let remainingTime = duration(); | ||
| createEffect(on(() => [props.expanded, props.interacting, props.toast, duration(), props.toast.promise, toastType(), props.pauseWhenPageIsHidden, isDocumentHidden()], ([expanded, interacting, toast2, duration2, promise, toastType2, pauseWhenPageIsHidden, isDocumentHidden2]) => { | ||
| if (promise && toastType2 === "loading" || duration2 === Number.POSITIVE_INFINITY) return; | ||
| createEffect(on(() => [props.expanded, props.interacting, props.toast, toastType(), isDocumentHidden(), duration()], ([expanded, interacting, currentToast, currentType]) => { | ||
| if (currentToast.promise && currentType === "loading" || currentToast.duration === Number.POSITIVE_INFINITY || currentType === "loading") return; | ||
| let timeoutId; | ||
@@ -353,3 +554,3 @@ const pauseTimer = () => { | ||
| const elapsedTime = (/* @__PURE__ */ new Date()).getTime() - closeTimerStartTimeRef; | ||
| remainingTime = remainingTime - elapsedTime; | ||
| remainingTime -= elapsedTime; | ||
| } | ||
@@ -359,55 +560,66 @@ lastCloseTimerStartTimeRef = (/* @__PURE__ */ new Date()).getTime(); | ||
| const startTimer = () => { | ||
| if (remainingTime === Number.POSITIVE_INFINITY) return; | ||
| closeTimerStartTimeRef = (/* @__PURE__ */ new Date()).getTime(); | ||
| timeoutId = setTimeout(() => { | ||
| toast2.onAutoClose?.(toast2); | ||
| currentToast.onAutoClose?.(currentToast); | ||
| deleteToast(); | ||
| }, remainingTime); | ||
| }; | ||
| if (expanded || interacting || pauseWhenPageIsHidden && isDocumentHidden2) pauseTimer(); | ||
| const shouldPause = expanded || interacting || (props.pauseWhenPageIsHidden ?? true) && isDocumentHidden(); | ||
| if (shouldPause) pauseTimer(); | ||
| else startTimer(); | ||
| onCleanup(() => { | ||
| clearTimeout(timeoutId); | ||
| if (timeoutId) clearTimeout(timeoutId); | ||
| }); | ||
| })); | ||
| createEffect(on(() => props.toast.id, (toastId) => { | ||
| const toastNode = toastRef; | ||
| if (toastNode) { | ||
| const height = toastNode.getBoundingClientRect().height; | ||
| setInitialHeight(height); | ||
| props.setHeights((h) => [{ | ||
| toastId, | ||
| height, | ||
| position: props.toast.position | ||
| }, ...h]); | ||
| onCleanup(() => { | ||
| props.setHeights((h) => h.filter((height2) => height2.toastId !== toastId)); | ||
| }); | ||
| createEffect(on(() => props.toast.delete, (shouldDelete) => { | ||
| if (shouldDelete) { | ||
| deleteToast(); | ||
| props.toast.onDismiss?.(props.toast); | ||
| } | ||
| })); | ||
| createEffect(on(() => props.toast.delete, (d) => { | ||
| if (d) deleteToast(); | ||
| })); | ||
| return (() => { | ||
| var _el$2 = _tmpl$32(); | ||
| var _el$2 = _tmpl$52(), _el$5 = _el$2.firstChild, _el$6 = _el$5.firstChild; | ||
| _el$2.$$pointermove = (event) => { | ||
| if (!pointerStartRef()) return; | ||
| const yPosition = event.clientY - pointerStartRef().y; | ||
| const xPosition = event.clientX - pointerStartRef().x; | ||
| const clamp = coords()[0] === "top" ? Math.min : Math.max; | ||
| const clampedY = clamp(0, yPosition); | ||
| const swipeStartThreshold = event.pointerType === "touch" ? 10 : 2; | ||
| const isAllowedToSwipe = Math.abs(clampedY) > swipeStartThreshold; | ||
| if (isAllowedToSwipe) { | ||
| toastRef?.style.setProperty("--swipe-amount", `${yPosition}px`); | ||
| } else if (Math.abs(xPosition) > swipeStartThreshold) { | ||
| setPointerStartRef(null); | ||
| if (!pointerStartRef() || !dismissible()) return; | ||
| if ((window.getSelection()?.toString().length ?? 0) > 0) return; | ||
| const yDelta = event.clientY - pointerStartRef().y; | ||
| const xDelta = event.clientX - pointerStartRef().x; | ||
| if (!swipeDirection() && (Math.abs(xDelta) > 1 || Math.abs(yDelta) > 1)) setSwipeDirection(Math.abs(xDelta) > Math.abs(yDelta) ? "x" : "y"); | ||
| const swipeAmount = { | ||
| x: 0, | ||
| y: 0 | ||
| }; | ||
| const getDampening = (delta) => { | ||
| const factor = Math.abs(delta) / 20; | ||
| return 1 / (1.5 + factor); | ||
| }; | ||
| if (swipeDirection() === "y") { | ||
| if (swipeDirections().includes("top") || swipeDirections().includes("bottom")) { | ||
| if (swipeDirections().includes("top") && yDelta < 0 || swipeDirections().includes("bottom") && yDelta > 0) swipeAmount.y = yDelta; | ||
| else swipeAmount.y = yDelta * getDampening(yDelta); | ||
| } | ||
| } else if (swipeDirection() === "x") { | ||
| if (swipeDirections().includes("left") || swipeDirections().includes("right")) { | ||
| if (swipeDirections().includes("left") && xDelta < 0 || swipeDirections().includes("right") && xDelta > 0) swipeAmount.x = xDelta; | ||
| else swipeAmount.x = xDelta * getDampening(xDelta); | ||
| } | ||
| } | ||
| if (Math.abs(swipeAmount.x) > 0 || Math.abs(swipeAmount.y) > 0) setIsSwiped(true); | ||
| toastRef?.style.setProperty("--swipe-amount-x", `${swipeAmount.x}px`); | ||
| toastRef?.style.setProperty("--swipe-amount-y", `${swipeAmount.y}px`); | ||
| }; | ||
| _el$2.$$pointerup = () => { | ||
| if (swipeOut()) return; | ||
| if (swipeOut() || !dismissible()) return; | ||
| setPointerStartRef(null); | ||
| const swipeAmount = Number(toastRef?.style.getPropertyValue("--swipe-amount").replace("px", "") || 0); | ||
| if (Math.abs(swipeAmount) >= SWIPE_TRESHOLD) { | ||
| const swipeAmountX = Number(toastRef?.style.getPropertyValue("--swipe-amount-x").replace("px", "") || 0); | ||
| const swipeAmountY = Number(toastRef?.style.getPropertyValue("--swipe-amount-y").replace("px", "") || 0); | ||
| const timeTaken = Math.max(1, (/* @__PURE__ */ new Date()).getTime() - (dragStartTime?.getTime() ?? 0)); | ||
| const swipeAmount = swipeDirection() === "x" ? swipeAmountX : swipeAmountY; | ||
| const velocity = Math.abs(swipeAmount) / timeTaken; | ||
| if (Math.abs(swipeAmount) >= SWIPE_THRESHOLD || velocity > 0.11) { | ||
| setOffsetBeforeRemove(offset()); | ||
| props.toast.onDismiss?.(props.toast); | ||
| if (swipeDirection() === "x") setSwipeOutDirection(swipeAmountX > 0 ? "right" : "left"); | ||
| else setSwipeOutDirection(swipeAmountY > 0 ? "down" : "up"); | ||
| deleteToast(); | ||
@@ -417,9 +629,14 @@ setSwipeOut(true); | ||
| } | ||
| toastRef?.style.setProperty("--swipe-amount", "0px"); | ||
| toastRef?.style.setProperty("--swipe-amount-x", "0px"); | ||
| toastRef?.style.setProperty("--swipe-amount-y", "0px"); | ||
| setIsSwiped(false); | ||
| setSwiping(false); | ||
| setSwipeDirection(null); | ||
| }; | ||
| _el$2.$$pointerdown = (event) => { | ||
| if (disabled()) return; | ||
| if (event.button === 2) return; | ||
| if (disabled() || !dismissible()) return; | ||
| dragStartTime = /* @__PURE__ */ new Date(); | ||
| setOffsetBeforeRemove(offset()); | ||
| event.target.setPointerCapture(event.pointerId); | ||
| event.currentTarget.setPointerCapture(event.pointerId); | ||
| if (event.target.tagName === "BUTTON") return; | ||
@@ -432,2 +649,7 @@ setSwiping(true); | ||
| }; | ||
| _el$2.addEventListener("dragend", () => { | ||
| setSwiping(false); | ||
| setSwipeDirection(null); | ||
| setPointerStartRef(null); | ||
| }); | ||
| var _ref$ = toastRef; | ||
@@ -437,74 +659,81 @@ typeof _ref$ === "function" ? use(_ref$, _el$2) : toastRef = _el$2; | ||
| get when() { | ||
| return props.closeButton && !props.toast.jsx; | ||
| return memo(() => !!(closeButton() && !props.toast.jsx))() && toastType() !== "loading"; | ||
| }, | ||
| get children() { | ||
| var _el$3 = _tmpl$22(); | ||
| addEventListener(_el$3, "click", disabled() ? void 0 : () => { | ||
| _el$3.$$click = () => { | ||
| if (disabled() || !dismissible()) return; | ||
| deleteToast(); | ||
| props.toast.onDismiss?.(props.toast); | ||
| }, true); | ||
| }; | ||
| insert(_el$3, () => props.icons?.close ?? createComponent(CloseIcon, {})); | ||
| effect((_p$) => { | ||
| var _v$ = disabled(), _v$2 = _cn(props.classes?.closeButton, props.toast?.classes?.closeButton); | ||
| _v$ !== _p$.e && setAttribute(_el$3, "data-disabled", _p$.e = _v$); | ||
| _v$2 !== _p$.t && className(_el$3, _p$.t = _v$2); | ||
| var _v$3 = props.closeButtonAriaLabel ?? "Close toast", _v$4 = disabled(), _v$5 = cn(classNames()?.closeButton, toastClassNames()?.closeButton); | ||
| _v$3 !== _p$.e && setAttribute(_el$3, "aria-label", _p$.e = _v$3); | ||
| _v$4 !== _p$.t && setAttribute(_el$3, "data-disabled", _p$.t = _v$4); | ||
| _v$5 !== _p$.a && className(_el$3, _p$.a = _v$5); | ||
| return _p$; | ||
| }, { | ||
| e: void 0, | ||
| t: void 0 | ||
| t: void 0, | ||
| a: void 0 | ||
| }); | ||
| return _el$3; | ||
| } | ||
| }), _el$5); | ||
| insert(_el$2, createComponent(Show, { | ||
| get when() { | ||
| return memo(() => !!((toastType() || props.toast.icon || props.toast.promise) && props.toast.icon !== null))() && (props.icons?.[toastType()] !== null || props.toast.icon); | ||
| }, | ||
| get children() { | ||
| var _el$4 = _tmpl$32(); | ||
| insert(_el$4, (() => { | ||
| var _c$ = memo(() => !!(props.toast.promise || props.toast.type === "loading" && !props.toast.icon)); | ||
| return () => _c$() ? props.toast.icon || getLoadingIcon() : null; | ||
| })(), null); | ||
| insert(_el$4, (() => { | ||
| var _c$2 = memo(() => props.toast.type !== "loading"); | ||
| return () => _c$2() ? icon() : null; | ||
| })(), null); | ||
| effect(() => className(_el$4, cn(classNames()?.icon, toastClassNames()?.icon))); | ||
| return _el$4; | ||
| } | ||
| }), _el$5); | ||
| insert(_el$6, (() => { | ||
| var _c$3 = memo(() => !!props.toast.jsx); | ||
| return () => _c$3() ? props.toast.jsx : resolveContent(props.toast.title); | ||
| })()); | ||
| insert(_el$5, createComponent(Show, { | ||
| get when() { | ||
| return props.toast.description; | ||
| }, | ||
| get children() { | ||
| var _el$7 = _tmpl$42(); | ||
| insert(_el$7, () => resolveContent(props.toast.description)); | ||
| effect(() => className(_el$7, cn(descriptionClassName(), toastDescriptionClassName(), classNames()?.description, toastClassNames()?.description))); | ||
| return _el$7; | ||
| } | ||
| }), null); | ||
| insert(_el$2, createComponent(Show, { | ||
| get when() { | ||
| return props.toast.jsx || props.toast.title instanceof Element; | ||
| return props.toast.cancel; | ||
| }, | ||
| get fallback() { | ||
| return [createComponent(Show, { | ||
| get when() { | ||
| return toastType() || props.toast.icon || props.toast.promise; | ||
| }, | ||
| get children() { | ||
| var _el$4 = _tmpl$42(); | ||
| insert(_el$4, (() => { | ||
| var _c$ = memo(() => !!(props.toast.promise || props.toast.type === "loading" && !props.toast.icon)); | ||
| return () => _c$() ? props.toast.icon || getLoadingIcon() : null; | ||
| })(), null); | ||
| insert(_el$4, (() => { | ||
| var _c$2 = memo(() => props.toast.type !== "loading"); | ||
| return () => _c$2() ? props.toast.icon || props.icons?.[toastType()] || getAsset(toastType())() : null; | ||
| })(), null); | ||
| return _el$4; | ||
| } | ||
| }), (() => { | ||
| var _el$5 = _tmpl$62(), _el$6 = _el$5.firstChild; | ||
| insert(_el$6, () => props.toast.title); | ||
| insert(_el$5, createComponent(Show, { | ||
| get when() { | ||
| return props.toast.description; | ||
| }, | ||
| get children() { | ||
| var _el$7 = _tmpl$52(); | ||
| insert(_el$7, () => props.toast.description); | ||
| effect(() => className(_el$7, _cn(props.descriptionClass, toastDescriptionClassname(), props.classes?.description, props.toast?.classes?.description))); | ||
| return _el$7; | ||
| } | ||
| }), null); | ||
| effect(() => className(_el$6, _cn(props.classes?.title, props.toast?.classes?.title))); | ||
| return _el$5; | ||
| })(), createComponent(Show, { | ||
| get when() { | ||
| return props.toast.cancel; | ||
| }, | ||
| get children() { | ||
| var _el$8 = _tmpl$72(); | ||
| _el$8.$$click = () => { | ||
| children: (cancel) => createComponent(Show, { | ||
| get when() { | ||
| return !isAction(cancel()); | ||
| }, | ||
| get fallback() { | ||
| return (() => { | ||
| var _el$8 = _tmpl$62(); | ||
| _el$8.$$click = (event) => { | ||
| const currentCancel = cancel(); | ||
| if (!dismissible()) return; | ||
| currentCancel.onClick?.(event); | ||
| deleteToast(); | ||
| if (props.toast.cancel?.onClick) props.toast.cancel.onClick(); | ||
| }; | ||
| insert(_el$8, () => props.toast.cancel.label); | ||
| insert(_el$8, () => cancel().label); | ||
| effect((_p$) => { | ||
| var _v$20 = props.toast.cancelButtonStyle || props.cancelButtonStyle, _v$21 = _cn(props.classes?.cancelButton, props.toast?.classes?.cancelButton); | ||
| _p$.e = style(_el$8, _v$20, _p$.e); | ||
| _v$21 !== _p$.t && className(_el$8, _p$.t = _v$21); | ||
| var _v$27 = props.toast.cancelButtonStyle ?? props.cancelButtonStyle, _v$28 = cn(classNames()?.cancelButton, toastClassNames()?.cancelButton); | ||
| _p$.e = style(_el$8, _v$27, _p$.e); | ||
| _v$28 !== _p$.t && className(_el$8, _p$.t = _v$28); | ||
| return _p$; | ||
@@ -516,19 +745,31 @@ }, { | ||
| return _el$8; | ||
| } | ||
| }), createComponent(Show, { | ||
| get when() { | ||
| return props.toast.action; | ||
| }, | ||
| get children() { | ||
| var _el$9 = _tmpl$8(); | ||
| })(); | ||
| }, | ||
| get children() { | ||
| return cancel(); | ||
| } | ||
| }) | ||
| }), null); | ||
| insert(_el$2, createComponent(Show, { | ||
| get when() { | ||
| return props.toast.action; | ||
| }, | ||
| children: (action) => createComponent(Show, { | ||
| get when() { | ||
| return !isAction(action()); | ||
| }, | ||
| get fallback() { | ||
| return (() => { | ||
| var _el$9 = _tmpl$72(); | ||
| _el$9.$$click = (event) => { | ||
| props.toast.action?.onClick(event); | ||
| const currentAction = action(); | ||
| currentAction.onClick?.(event); | ||
| if (event.defaultPrevented) return; | ||
| deleteToast(); | ||
| }; | ||
| insert(_el$9, () => props.toast.action.label); | ||
| insert(_el$9, () => action().label); | ||
| effect((_p$) => { | ||
| var _v$22 = props.toast.actionButtonStyle || props.actionButtonStyle, _v$23 = _cn(props.classes?.actionButton, props.toast?.classes?.actionButton); | ||
| _p$.e = style(_el$9, _v$22, _p$.e); | ||
| _v$23 !== _p$.t && className(_el$9, _p$.t = _v$23); | ||
| var _v$29 = props.toast.actionButtonStyle ?? props.actionButtonStyle, _v$30 = cn(classNames()?.actionButton, toastClassNames()?.actionButton); | ||
| _p$.e = style(_el$9, _v$29, _p$.e); | ||
| _v$30 !== _p$.t && className(_el$9, _p$.t = _v$30); | ||
| return _p$; | ||
@@ -540,11 +781,11 @@ }, { | ||
| return _el$9; | ||
| } | ||
| })]; | ||
| }, | ||
| get children() { | ||
| return props.toast.jsx || props.toast.title; | ||
| } | ||
| })(); | ||
| }, | ||
| get children() { | ||
| return action(); | ||
| } | ||
| }) | ||
| }), null); | ||
| effect((_p$) => { | ||
| var _v$3 = props.toast.important ? "assertive" : "polite", _v$4 = _cn(props.class, toastClassname(), props.classes?.toast, props.toast?.classes?.toast, props.classes?.default, props.classes?.[toastType()], props.toast?.classes?.[toastType()]), _v$5 = !(props.toast.jsx || props.toast.unstyled || props.unstyled), _v$6 = mounted(), _v$7 = Boolean(props.toast.promise), _v$8 = removed(), _v$9 = isVisible(), _v$10 = coords()[0], _v$11 = coords()[1], _v$12 = props.index, _v$13 = isFront(), _v$14 = swiping(), _v$15 = toastType(), _v$16 = invert(), _v$17 = swipeOut(), _v$18 = Boolean(props.expanded || props.expandByDefault && mounted()), _v$19 = { | ||
| var _v$6 = cn(className$1(), toastClassName(), classNames()?.toast, toastClassNames()?.toast, classNames()?.default, classNames()?.[toastType()], toastClassNames()?.[toastType()]), _v$7 = props.toast.richColors ?? props.defaultRichColors, _v$8 = !(props.toast.jsx || props.toast.unstyled || props.unstyled), _v$9 = mounted(), _v$0 = Boolean(props.toast.promise), _v$1 = isSwiped(), _v$10 = removed(), _v$11 = isVisible(), _v$12 = y(), _v$13 = x(), _v$14 = props.index, _v$15 = isFront(), _v$16 = swiping(), _v$17 = dismissible(), _v$18 = toastType(), _v$19 = invert(), _v$20 = swipeOut(), _v$21 = swipeOutDirection(), _v$22 = Boolean(props.expanded || props.expandByDefault && mounted()), _v$23 = props.toast.testId, _v$24 = { | ||
| "--index": props.index, | ||
@@ -557,20 +798,26 @@ "--toasts-before": props.index, | ||
| ...props.toast.style | ||
| }; | ||
| _v$3 !== _p$.e && setAttribute(_el$2, "aria-live", _p$.e = _v$3); | ||
| _v$4 !== _p$.t && className(_el$2, _p$.t = _v$4); | ||
| _v$5 !== _p$.a && setAttribute(_el$2, "data-styled", _p$.a = _v$5); | ||
| _v$6 !== _p$.o && setAttribute(_el$2, "data-mounted", _p$.o = _v$6); | ||
| _v$7 !== _p$.i && setAttribute(_el$2, "data-promise", _p$.i = _v$7); | ||
| _v$8 !== _p$.n && setAttribute(_el$2, "data-removed", _p$.n = _v$8); | ||
| _v$9 !== _p$.s && setAttribute(_el$2, "data-visible", _p$.s = _v$9); | ||
| _v$10 !== _p$.h && setAttribute(_el$2, "data-y-position", _p$.h = _v$10); | ||
| _v$11 !== _p$.r && setAttribute(_el$2, "data-x-position", _p$.r = _v$11); | ||
| _v$12 !== _p$.d && setAttribute(_el$2, "data-index", _p$.d = _v$12); | ||
| _v$13 !== _p$.l && setAttribute(_el$2, "data-front", _p$.l = _v$13); | ||
| _v$14 !== _p$.u && setAttribute(_el$2, "data-swiping", _p$.u = _v$14); | ||
| _v$15 !== _p$.c && setAttribute(_el$2, "data-type", _p$.c = _v$15); | ||
| _v$16 !== _p$.w && setAttribute(_el$2, "data-invert", _p$.w = _v$16); | ||
| _v$17 !== _p$.m && setAttribute(_el$2, "data-swipe-out", _p$.m = _v$17); | ||
| _v$18 !== _p$.f && setAttribute(_el$2, "data-expanded", _p$.f = _v$18); | ||
| _p$.y = style(_el$2, _v$19, _p$.y); | ||
| }, _v$25 = cn(classNames()?.content, toastClassNames()?.content), _v$26 = cn(classNames()?.title, toastClassNames()?.title); | ||
| _v$6 !== _p$.e && className(_el$2, _p$.e = _v$6); | ||
| _v$7 !== _p$.t && setAttribute(_el$2, "data-rich-colors", _p$.t = _v$7); | ||
| _v$8 !== _p$.a && setAttribute(_el$2, "data-styled", _p$.a = _v$8); | ||
| _v$9 !== _p$.o && setAttribute(_el$2, "data-mounted", _p$.o = _v$9); | ||
| _v$0 !== _p$.i && setAttribute(_el$2, "data-promise", _p$.i = _v$0); | ||
| _v$1 !== _p$.n && setAttribute(_el$2, "data-swiped", _p$.n = _v$1); | ||
| _v$10 !== _p$.s && setAttribute(_el$2, "data-removed", _p$.s = _v$10); | ||
| _v$11 !== _p$.h && setAttribute(_el$2, "data-visible", _p$.h = _v$11); | ||
| _v$12 !== _p$.r && setAttribute(_el$2, "data-y-position", _p$.r = _v$12); | ||
| _v$13 !== _p$.d && setAttribute(_el$2, "data-x-position", _p$.d = _v$13); | ||
| _v$14 !== _p$.l && setAttribute(_el$2, "data-index", _p$.l = _v$14); | ||
| _v$15 !== _p$.u && setAttribute(_el$2, "data-front", _p$.u = _v$15); | ||
| _v$16 !== _p$.c && setAttribute(_el$2, "data-swiping", _p$.c = _v$16); | ||
| _v$17 !== _p$.w && setAttribute(_el$2, "data-dismissible", _p$.w = _v$17); | ||
| _v$18 !== _p$.m && setAttribute(_el$2, "data-type", _p$.m = _v$18); | ||
| _v$19 !== _p$.f && setAttribute(_el$2, "data-invert", _p$.f = _v$19); | ||
| _v$20 !== _p$.y && setAttribute(_el$2, "data-swipe-out", _p$.y = _v$20); | ||
| _v$21 !== _p$.g && setAttribute(_el$2, "data-swipe-direction", _p$.g = _v$21); | ||
| _v$22 !== _p$.p && setAttribute(_el$2, "data-expanded", _p$.p = _v$22); | ||
| _v$23 !== _p$.b && setAttribute(_el$2, "data-testid", _p$.b = _v$23); | ||
| _p$.T = style(_el$2, _v$24, _p$.T); | ||
| _v$25 !== _p$.A && className(_el$5, _p$.A = _v$25); | ||
| _v$26 !== _p$.O && className(_el$6, _p$.O = _v$26); | ||
| return _p$; | ||
@@ -594,15 +841,14 @@ }, { | ||
| f: void 0, | ||
| y: void 0 | ||
| y: void 0, | ||
| g: void 0, | ||
| p: void 0, | ||
| b: void 0, | ||
| T: void 0, | ||
| A: void 0, | ||
| O: void 0 | ||
| }); | ||
| return _el$2; | ||
| })(); | ||
| }; | ||
| function getDocumentDirection() { | ||
| if (typeof window === "undefined") return "ltr"; | ||
| if (typeof document === "undefined") return "ltr"; | ||
| const dirAttribute = document.documentElement.getAttribute("dir"); | ||
| if (dirAttribute === "auto" || !dirAttribute) return window.getComputedStyle(document.documentElement).direction; | ||
| return dirAttribute; | ||
| } | ||
| var Toaster = (props) => { | ||
| function Toaster(props) { | ||
| const propsWithDefaults = mergeProps({ | ||
@@ -612,46 +858,66 @@ position: "bottom-right", | ||
| theme: "light", | ||
| gap: GAP, | ||
| visibleToasts: VISIBLE_TOASTS_AMOUNT, | ||
| dir: getDocumentDirection() | ||
| dir: getDocumentDirection(), | ||
| containerAriaLabel: "Notifications" | ||
| }, props); | ||
| const initialTheme = createToastTheme(propsWithDefaults.theme); | ||
| const [toastsStore, setToastsStore] = createStore({ | ||
| toasts: [] | ||
| }); | ||
| const possiblePositions = () => { | ||
| return Array.from(new Set([propsWithDefaults.position].concat(toastsStore.toasts.filter((toast2) => toast2.position).map((toast2) => toast2.position)))); | ||
| }; | ||
| const filteredToasts = createMemo(() => { | ||
| const toasts = toastsStore.toasts; | ||
| if (propsWithDefaults.id) return toasts.filter((toast2) => toast2.toasterId === propsWithDefaults.id); | ||
| return toasts.filter((toast2) => !toast2.toasterId); | ||
| }); | ||
| const possiblePositions = createMemo(() => { | ||
| return Array.from(/* @__PURE__ */ new Set([propsWithDefaults.position, ...filteredToasts().filter((toast2) => toast2.position).map((toast2) => toast2.position)])); | ||
| }); | ||
| const [heights, setHeights] = createSignal([]); | ||
| const [expanded, setExpanded] = createSignal(false); | ||
| const [interacting, setInteracting] = createSignal(false); | ||
| const [actualTheme, setActualTheme] = createSignal(initialTheme); | ||
| const [lastFocusedElementRef, setLastFocusedElementRef] = createSignal(null); | ||
| const [isFocusWithinRef, setIsFocusWithinRef] = createSignal(false); | ||
| let listRef; | ||
| const hotkeyLabel = () => propsWithDefaults.hotkey.join("+").replace(/Key/g, "").replace(/Digit/g, ""); | ||
| const [lastFocusedElementRef, setLastFocusedElementRef] = createSignal(null); | ||
| const [isFocusedWithinRef, setIsFocusedWithinRef] = createSignal(false); | ||
| const [actualTheme, setActualTheme] = createSignal(propsWithDefaults.theme !== "system" ? propsWithDefaults.theme : typeof window !== "undefined" ? window.matchMedia && window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light" : "light"); | ||
| const removeToast = (toast2) => setToastsStore("toasts", (toasts) => toasts.filter(({ | ||
| id | ||
| }) => id !== toast2.id)); | ||
| const className$1 = () => mergeClassName(propsWithDefaults.className, propsWithDefaults.class); | ||
| const toastOptions = () => propsWithDefaults.toastOptions; | ||
| const toastClassNames = () => mergeClassNames(toastOptions()?.classNames, toastOptions()?.classes); | ||
| const toastDescriptionClassName = () => mergeDescriptionClassName(toastOptions()?.descriptionClassName, toastOptions()?.descriptionClass); | ||
| const toastClassName = () => mergeClassName(toastOptions()?.className, toastOptions()?.class); | ||
| const removeToast = (toastToRemove) => { | ||
| setToastsStore("toasts", (toasts) => { | ||
| if (!toasts.find((toast2) => toast2.id === toastToRemove.id)?.delete) ToastState.dismiss(toastToRemove.id); | ||
| return toasts.filter(({ | ||
| id | ||
| }) => id !== toastToRemove.id); | ||
| }); | ||
| }; | ||
| onMount(() => { | ||
| const unsub = ToastState.subscribe((toast2) => { | ||
| if (toast2.dismiss) { | ||
| setToastsStore("toasts", produce((_toasts) => { | ||
| _toasts.forEach((t) => { | ||
| if (t.id === toast2.id) t.delete = true; | ||
| }); | ||
| })); | ||
| const unsubscribe = ToastState.subscribe((toastItem) => { | ||
| if (toastItem.dismiss) { | ||
| requestAnimationFrame(() => { | ||
| setToastsStore("toasts", produce((toasts) => { | ||
| toasts.forEach((toast2) => { | ||
| if (toast2.id === toastItem.id) toast2.delete = true; | ||
| }); | ||
| })); | ||
| }); | ||
| return; | ||
| } | ||
| const changedIndex = toastsStore.toasts.findIndex((t) => t.id === toast2.id); | ||
| if (changedIndex !== -1) { | ||
| setToastsStore("toasts", [changedIndex], reconcile(toast2)); | ||
| const nextToast = toastItem; | ||
| const indexOfExistingToast = toastsStore.toasts.findIndex((t) => t.id === nextToast.id); | ||
| if (indexOfExistingToast !== -1) { | ||
| setToastsStore("toasts", indexOfExistingToast, reconcile(nextToast)); | ||
| return; | ||
| } | ||
| setToastsStore("toasts", produce((_toasts) => { | ||
| _toasts.unshift(toast2); | ||
| setToastsStore("toasts", produce((toasts) => { | ||
| toasts.unshift(nextToast); | ||
| })); | ||
| }); | ||
| onCleanup(() => { | ||
| unsub(); | ||
| }); | ||
| onCleanup(unsubscribe); | ||
| }); | ||
| createEffect(on(() => propsWithDefaults.theme, (theme) => { | ||
| createEffect(() => { | ||
| const theme = propsWithDefaults.theme; | ||
| if (theme !== "system") { | ||
@@ -662,15 +928,23 @@ setActualTheme(theme); | ||
| if (typeof window === "undefined") return; | ||
| window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change", ({ | ||
| const darkMediaQuery = window.matchMedia("(prefers-color-scheme: dark)"); | ||
| const updateTheme = ({ | ||
| matches | ||
| }) => { | ||
| if (matches) setActualTheme("dark"); | ||
| else setActualTheme("light"); | ||
| }); | ||
| })); | ||
| setActualTheme(matches ? "dark" : "light"); | ||
| }; | ||
| updateTheme(darkMediaQuery); | ||
| try { | ||
| darkMediaQuery.addEventListener("change", updateTheme); | ||
| onCleanup(() => darkMediaQuery.removeEventListener("change", updateTheme)); | ||
| } catch { | ||
| darkMediaQuery.addListener(updateTheme); | ||
| onCleanup(() => darkMediaQuery.removeListener(updateTheme)); | ||
| } | ||
| }); | ||
| createEffect(() => { | ||
| if (toastsStore.toasts.length <= 1) setExpanded(false); | ||
| if (filteredToasts().length <= 1) setExpanded(false); | ||
| }); | ||
| onMount(() => { | ||
| const handleKeyDown = (event) => { | ||
| const isHotkeyPressed = propsWithDefaults.hotkey.every((key) => event[key] || event.code === key); | ||
| const isHotkeyPressed = propsWithDefaults.hotkey.length > 0 && propsWithDefaults.hotkey.every((key) => event[key] || event.code === key); | ||
| if (isHotkeyPressed) { | ||
@@ -683,51 +957,52 @@ setExpanded(true); | ||
| document.addEventListener("keydown", handleKeyDown); | ||
| onCleanup(() => { | ||
| document.removeEventListener("keydown", handleKeyDown); | ||
| }); | ||
| onCleanup(() => document.removeEventListener("keydown", handleKeyDown)); | ||
| }); | ||
| createEffect(on(() => listRef, (ref) => { | ||
| if (ref) { | ||
| onCleanup(() => { | ||
| if (lastFocusedElementRef()) { | ||
| lastFocusedElementRef()?.focus({ | ||
| preventScroll: true | ||
| }); | ||
| setLastFocusedElementRef(null); | ||
| setIsFocusedWithinRef(false); | ||
| } | ||
| onCleanup(() => { | ||
| if (lastFocusedElementRef()) { | ||
| lastFocusedElementRef()?.focus({ | ||
| preventScroll: true | ||
| }); | ||
| setLastFocusedElementRef(null); | ||
| setIsFocusWithinRef(false); | ||
| } | ||
| })); | ||
| return createComponent(Show, { | ||
| get when() { | ||
| return toastsStore.toasts.length > 0; | ||
| }, | ||
| get children() { | ||
| var _el$10 = _tmpl$9(); | ||
| insert(_el$10, createComponent(For, { | ||
| get each() { | ||
| return possiblePositions(); | ||
| }, | ||
| children: (position, index) => { | ||
| const [y, x] = position.split("-"); | ||
| return (() => { | ||
| var _el$11 = _tmpl$10(); | ||
| _el$11.$$pointerup = () => setInteracting(false); | ||
| _el$11.$$pointerdown = () => { | ||
| }); | ||
| return (() => { | ||
| var _el$0 = _tmpl$82(); | ||
| insert(_el$0, createComponent(For, { | ||
| get each() { | ||
| return possiblePositions(); | ||
| }, | ||
| children: (position, index) => { | ||
| const [y, x] = position.split("-"); | ||
| const toastsByPosition = createMemo(() => filteredToasts().filter((toast2) => !toast2.position && index() === 0 || toast2.position === position)); | ||
| const heightsByPosition = createMemo(() => heights().filter((height) => index() === 0 && !height.position || height.position === position)); | ||
| return createComponent(Show, { | ||
| get when() { | ||
| return filteredToasts().length > 0; | ||
| }, | ||
| get children() { | ||
| var _el$1 = _tmpl$9(); | ||
| _el$1.$$pointerup = () => setInteracting(false); | ||
| _el$1.$$pointerdown = (event) => { | ||
| const isNotDismissible = event.target instanceof HTMLElement && event.target.dataset.dismissible === "false"; | ||
| if (isNotDismissible) return; | ||
| setInteracting(true); | ||
| }; | ||
| _el$11.addEventListener("mouseleave", () => { | ||
| _el$1.addEventListener("dragend", () => setExpanded(false)); | ||
| _el$1.addEventListener("mouseleave", () => { | ||
| if (!interacting()) setExpanded(false); | ||
| }); | ||
| _el$11.$$mousemove = () => setExpanded(true); | ||
| _el$11.addEventListener("mouseenter", () => setExpanded(true)); | ||
| _el$11.addEventListener("focus", (event) => { | ||
| if (!isFocusedWithinRef()) { | ||
| setIsFocusedWithinRef(true); | ||
| _el$1.$$mousemove = () => setExpanded(true); | ||
| _el$1.addEventListener("mouseenter", () => setExpanded(true)); | ||
| _el$1.addEventListener("focus", (event) => { | ||
| const isNotDismissible = event.target instanceof HTMLElement && event.target.dataset.dismissible === "false"; | ||
| if (isNotDismissible) return; | ||
| if (!isFocusWithinRef()) { | ||
| setIsFocusWithinRef(true); | ||
| setLastFocusedElementRef(event.relatedTarget); | ||
| } | ||
| }); | ||
| _el$11.addEventListener("blur", (event) => { | ||
| if (isFocusedWithinRef() && !event.currentTarget.contains(event.relatedTarget)) { | ||
| setIsFocusedWithinRef(false); | ||
| _el$1.addEventListener("blur", (event) => { | ||
| if (isFocusWithinRef() && !event.currentTarget.contains(event.relatedTarget)) { | ||
| setIsFocusWithinRef(false); | ||
| if (lastFocusedElementRef()) { | ||
@@ -742,35 +1017,29 @@ lastFocusedElementRef()?.focus({ | ||
| var _ref$2 = listRef; | ||
| typeof _ref$2 === "function" ? use(_ref$2, _el$11) : listRef = _el$11; | ||
| setAttribute(_el$11, "data-y-position", y); | ||
| setAttribute(_el$11, "data-x-position", x); | ||
| insert(_el$11, createComponent(For, { | ||
| typeof _ref$2 === "function" ? use(_ref$2, _el$1) : listRef = _el$1; | ||
| setAttribute(_el$1, "data-y-position", y); | ||
| setAttribute(_el$1, "data-x-position", x); | ||
| insert(_el$1, createComponent(For, { | ||
| get each() { | ||
| return toastsStore.toasts.filter((toast2) => !toast2.position && index() === 0 || toast2.position === position); | ||
| return toastsByPosition(); | ||
| }, | ||
| children: (toast2, index2) => createComponent(Toast, { | ||
| get index() { | ||
| return index2(); | ||
| }, | ||
| children: (toastItem, toastIndex) => createComponent(Toast, { | ||
| get icons() { | ||
| return propsWithDefaults.icons; | ||
| }, | ||
| toast: toast2, | ||
| get index() { | ||
| return toastIndex(); | ||
| }, | ||
| toast: toastItem, | ||
| get defaultRichColors() { | ||
| return propsWithDefaults.richColors; | ||
| }, | ||
| get duration() { | ||
| return propsWithDefaults.toastOptions?.duration ?? props.duration; | ||
| return toastOptions()?.duration ?? propsWithDefaults.duration; | ||
| }, | ||
| get ["class"]() { | ||
| return propsWithDefaults.toastOptions?.class; | ||
| return toastClassName(); | ||
| }, | ||
| get classes() { | ||
| return propsWithDefaults.toastOptions?.classes; | ||
| get descriptionClassName() { | ||
| return toastDescriptionClassName(); | ||
| }, | ||
| get cancelButtonStyle() { | ||
| return propsWithDefaults.toastOptions?.cancelButtonStyle; | ||
| }, | ||
| get actionButtonStyle() { | ||
| return propsWithDefaults.toastOptions?.actionButtonStyle; | ||
| }, | ||
| get descriptionClass() { | ||
| return propsWithDefaults.toastOptions?.descriptionClass; | ||
| }, | ||
| get invert() { | ||
@@ -783,3 +1052,3 @@ return Boolean(propsWithDefaults.invert); | ||
| get closeButton() { | ||
| return Boolean(propsWithDefaults.closeButton); | ||
| return toastOptions()?.closeButton ?? propsWithDefaults.closeButton ?? false; | ||
| }, | ||
@@ -789,17 +1058,27 @@ get interacting() { | ||
| }, | ||
| get position() { | ||
| return propsWithDefaults.position; | ||
| }, | ||
| position, | ||
| get style() { | ||
| return propsWithDefaults.toastOptions?.style; | ||
| return toastOptions()?.style; | ||
| }, | ||
| get unstyled() { | ||
| return propsWithDefaults.toastOptions?.unstyled; | ||
| return toastOptions()?.unstyled; | ||
| }, | ||
| get classNames() { | ||
| return toastClassNames(); | ||
| }, | ||
| get cancelButtonStyle() { | ||
| return toastOptions()?.cancelButtonStyle; | ||
| }, | ||
| get actionButtonStyle() { | ||
| return toastOptions()?.actionButtonStyle; | ||
| }, | ||
| get closeButtonAriaLabel() { | ||
| return toastOptions()?.closeButtonAriaLabel; | ||
| }, | ||
| removeToast, | ||
| get toasts() { | ||
| return toastsStore.toasts; | ||
| return toastsByPosition(); | ||
| }, | ||
| get heights() { | ||
| return heights(); | ||
| return heightsByPosition(); | ||
| }, | ||
@@ -816,2 +1095,5 @@ setHeights, | ||
| }, | ||
| get swipeDirections() { | ||
| return propsWithDefaults.swipeDirections; | ||
| }, | ||
| get pauseWhenPageIsHidden() { | ||
@@ -823,14 +1105,12 @@ return propsWithDefaults.pauseWhenPageIsHidden; | ||
| effect((_p$) => { | ||
| var _v$24 = propsWithDefaults.dir === "auto" ? getDocumentDirection() : propsWithDefaults.dir, _v$25 = propsWithDefaults.class, _v$26 = actualTheme(), _v$27 = propsWithDefaults.richColors, _v$28 = { | ||
| "--front-toast-height": `${heights()[0]?.height}px`, | ||
| "--offset": typeof propsWithDefaults.offset === "number" ? `${propsWithDefaults.offset}px` : propsWithDefaults.offset || VIEWPORT_OFFSET, | ||
| "--width": `${TOAST_WIDTH}px`, | ||
| "--gap": `${GAP}px`, | ||
| ...propsWithDefaults.style | ||
| var _v$31 = propsWithDefaults.dir === "auto" ? getDocumentDirection() : propsWithDefaults.dir, _v$32 = className$1(), _v$33 = actualTheme(), _v$34 = { | ||
| "--front-toast-height": `${heightsByPosition()[0]?.height ?? 0}px`, | ||
| "--gap": `${propsWithDefaults.gap}px`, | ||
| ...propsWithDefaults.style, | ||
| ...assignOffset(propsWithDefaults.offset, propsWithDefaults.mobileOffset) | ||
| }; | ||
| _v$24 !== _p$.e && setAttribute(_el$11, "dir", _p$.e = _v$24); | ||
| _v$25 !== _p$.t && className(_el$11, _p$.t = _v$25); | ||
| _v$26 !== _p$.a && setAttribute(_el$11, "data-theme", _p$.a = _v$26); | ||
| _v$27 !== _p$.o && setAttribute(_el$11, "data-rich-colors", _p$.o = _v$27); | ||
| _p$.i = style(_el$11, _v$28, _p$.i); | ||
| _v$31 !== _p$.e && setAttribute(_el$1, "dir", _p$.e = _v$31); | ||
| _v$32 !== _p$.t && className(_el$1, _p$.t = _v$32); | ||
| _v$33 !== _p$.a && setAttribute(_el$1, "data-sonner-theme", _p$.a = _v$33); | ||
| _p$.o = style(_el$1, _v$34, _p$.o); | ||
| return _p$; | ||
@@ -841,14 +1121,13 @@ }, { | ||
| a: void 0, | ||
| o: void 0, | ||
| i: void 0 | ||
| o: void 0 | ||
| }); | ||
| return _el$11; | ||
| })(); | ||
| } | ||
| })); | ||
| effect(() => setAttribute(_el$10, "aria-label", `Notifications ${hotkeyLabel()}`)); | ||
| return _el$10; | ||
| } | ||
| }); | ||
| }; | ||
| return _el$1; | ||
| } | ||
| }); | ||
| } | ||
| })); | ||
| effect(() => setAttribute(_el$0, "aria-label", propsWithDefaults.customAriaLabel ?? `${propsWithDefaults.containerAriaLabel} ${hotkeyLabel()}`)); | ||
| return _el$0; | ||
| })(); | ||
| } | ||
| delegateEvents(["pointerdown", "pointerup", "pointermove", "click", "mousemove"]); | ||
@@ -863,2 +1142,2 @@ /*! | ||
| export { Toaster, toast }; | ||
| export { Toaster, toast, useSonner }; |
+623
-337
@@ -24,6 +24,6 @@ // #style-inject:#style-inject | ||
| // src/styles.css | ||
| styleInject(':where(html[dir=ltr]),\n:where([data-sonner-toaster][dir=ltr]) {\n --toast-icon-margin-start: -3px;\n --toast-icon-margin-end: 4px;\n --toast-svg-margin-start: -1px;\n --toast-svg-margin-end: 0px;\n --toast-button-margin-start: auto;\n --toast-button-margin-end: 0;\n --toast-close-button-start: 0;\n --toast-close-button-end: unset;\n --toast-close-button-transform: translate(-35%, -35%);\n}\n:where(html[dir=rtl]),\n:where([data-sonner-toaster][dir=rtl]) {\n --toast-icon-margin-start: 4px;\n --toast-icon-margin-end: -3px;\n --toast-svg-margin-start: 0px;\n --toast-svg-margin-end: -1px;\n --toast-button-margin-start: 0;\n --toast-button-margin-end: auto;\n --toast-close-button-start: unset;\n --toast-close-button-end: 0;\n --toast-close-button-transform: translate(35%, -35%);\n}\n:where([data-sonner-toaster]) {\n position: fixed;\n width: var(--width);\n font-family:\n ui-sans-serif,\n system-ui,\n -apple-system,\n BlinkMacSystemFont,\n Segoe UI,\n Roboto,\n Helvetica Neue,\n Arial,\n Noto Sans,\n sans-serif,\n Apple Color Emoji,\n Segoe UI Emoji,\n Segoe UI Symbol,\n Noto Color Emoji;\n --gray1: hsl(0, 0%, 99%);\n --gray2: hsl(0, 0%, 97.3%);\n --gray3: hsl(0, 0%, 95.1%);\n --gray4: hsl(0, 0%, 93%);\n --gray5: hsl(0, 0%, 90.9%);\n --gray6: hsl(0, 0%, 88.7%);\n --gray7: hsl(0, 0%, 85.8%);\n --gray8: hsl(0, 0%, 78%);\n --gray9: hsl(0, 0%, 56.1%);\n --gray10: hsl(0, 0%, 52.3%);\n --gray11: hsl(0, 0%, 43.5%);\n --gray12: hsl(0, 0%, 9%);\n --border-radius: 8px;\n box-sizing: border-box;\n padding: 0;\n margin: 0;\n list-style: none;\n outline: none;\n z-index: 999999999;\n}\n:where([data-sonner-toaster][data-x-position=right]) {\n right: max(var(--offset), env(safe-area-inset-right));\n}\n:where([data-sonner-toaster][data-x-position=left]) {\n left: max(var(--offset), env(safe-area-inset-left));\n}\n:where([data-sonner-toaster][data-x-position=center]) {\n left: 50%;\n transform: translateX(-50%);\n}\n:where([data-sonner-toaster][data-y-position=top]) {\n top: max(var(--offset), env(safe-area-inset-top));\n}\n:where([data-sonner-toaster][data-y-position=bottom]) {\n bottom: max(var(--offset), env(safe-area-inset-bottom));\n}\n:where([data-sonner-toast]) {\n --y: translateY(100%);\n --lift-amount: calc(var(--lift) * var(--gap));\n z-index: var(--z-index);\n position: absolute;\n opacity: 0;\n transform: var(--y);\n filter: blur(0);\n touch-action: none;\n transition:\n transform 400ms,\n opacity 400ms,\n height 400ms,\n box-shadow 200ms;\n box-sizing: border-box;\n outline: none;\n overflow-wrap: anywhere;\n}\n:where([data-sonner-toast][data-styled=true]) {\n padding: 16px;\n background: var(--normal-bg);\n border: 1px solid var(--normal-border);\n color: var(--normal-text);\n border-radius: var(--border-radius);\n box-shadow: 0px 4px 12px rgba(0, 0, 0, 0.1);\n width: var(--width);\n font-size: 13px;\n display: flex;\n align-items: center;\n gap: 6px;\n}\n:where([data-sonner-toast]:focus-visible) {\n box-shadow: 0px 4px 12px rgba(0, 0, 0, 0.1), 0 0 0 2px rgba(0, 0, 0, 0.2);\n}\n:where([data-sonner-toast][data-y-position=top]) {\n top: 0;\n --y: translateY(-100%);\n --lift: 1;\n --lift-amount: calc(1 * var(--gap));\n}\n:where([data-sonner-toast][data-y-position=bottom]) {\n bottom: 0;\n --y: translateY(100%);\n --lift: -1;\n --lift-amount: calc(var(--lift) * var(--gap));\n}\n:where([data-sonner-toast]) :where([data-description]) {\n font-weight: 400;\n line-height: 1.4;\n color: inherit;\n}\n:where([data-sonner-toast]) :where([data-title]) {\n font-weight: 500;\n line-height: 1.5;\n color: inherit;\n}\n:where([data-sonner-toast]) :where([data-icon]) {\n display: flex;\n height: 16px;\n width: 16px;\n position: relative;\n justify-content: flex-start;\n align-items: center;\n flex-shrink: 0;\n margin-left: var(--toast-icon-margin-start);\n margin-right: var(--toast-icon-margin-end);\n}\n:where([data-sonner-toast][data-promise=true]) :where([data-icon]) > svg {\n opacity: 0;\n transform: scale(0.8);\n transform-origin: center;\n animation: sonner-fade-in 300ms ease forwards;\n}\n:where([data-sonner-toast]) :where([data-icon]) > * {\n flex-shrink: 0;\n}\n:where([data-sonner-toast]) :where([data-icon]) svg {\n margin-left: var(--toast-svg-margin-start);\n margin-right: var(--toast-svg-margin-end);\n}\n:where([data-sonner-toast]) :where([data-content]) {\n display: flex;\n flex-direction: column;\n gap: 2px;\n}\n[data-sonner-toast][data-styled=true] [data-button] {\n border-radius: 4px;\n padding-left: 8px;\n padding-right: 8px;\n height: 24px;\n font-size: 12px;\n color: var(--normal-bg);\n background: var(--normal-text);\n margin-left: var(--toast-button-margin-start);\n margin-right: var(--toast-button-margin-end);\n border: none;\n cursor: pointer;\n outline: none;\n display: flex;\n align-items: center;\n flex-shrink: 0;\n transition: opacity 400ms, box-shadow 200ms;\n}\n:where([data-sonner-toast]) :where([data-button]):focus-visible {\n box-shadow: 0 0 0 2px rgba(0, 0, 0, 0.4);\n}\n:where([data-sonner-toast]) :where([data-button]):first-of-type {\n margin-left: var(--toast-button-margin-start);\n margin-right: var(--toast-button-margin-end);\n}\n:where([data-sonner-toast]) :where([data-cancel]) {\n color: var(--normal-text);\n background: rgba(0, 0, 0, 0.08);\n}\n:where([data-sonner-toast][data-theme=dark]) :where([data-cancel]) {\n background: rgba(255, 255, 255, 0.3);\n}\n:where([data-sonner-toast]) :where([data-close-button]) {\n position: absolute;\n left: var(--toast-close-button-start);\n right: var(--toast-close-button-end);\n top: 0;\n height: 20px;\n width: 20px;\n display: flex;\n justify-content: center;\n align-items: center;\n padding: 0;\n background: var(--gray1);\n color: var(--gray12);\n border: 1px solid var(--gray4);\n transform: var(--toast-close-button-transform);\n border-radius: 50%;\n cursor: pointer;\n z-index: 1;\n transition:\n opacity 100ms,\n background 200ms,\n border-color 200ms;\n}\n:where([data-sonner-toast]) :where([data-close-button]):focus-visible {\n box-shadow: 0px 4px 12px rgba(0, 0, 0, 0.1), 0 0 0 2px rgba(0, 0, 0, 0.2);\n}\n:where([data-sonner-toast]) :where([data-disabled=true]) {\n cursor: not-allowed;\n}\n:where([data-sonner-toast]):hover :where([data-close-button]):hover {\n background: var(--gray2);\n border-color: var(--gray5);\n}\n:where([data-sonner-toast][data-swiping=true])::before {\n content: "";\n position: absolute;\n left: 0;\n right: 0;\n height: 100%;\n z-index: -1;\n}\n:where([data-sonner-toast][data-y-position=top][data-swiping=true])::before {\n bottom: 50%;\n transform: scaleY(3) translateY(50%);\n}\n:where([data-sonner-toast][data-y-position=bottom][data-swiping=true])::before {\n top: 50%;\n transform: scaleY(3) translateY(-50%);\n}\n:where([data-sonner-toast][data-swiping=false][data-removed=true])::before {\n content: "";\n position: absolute;\n inset: 0;\n transform: scaleY(2);\n}\n:where([data-sonner-toast])::after {\n content: "";\n position: absolute;\n left: 0;\n height: calc(var(--gap) + 1px);\n bottom: 100%;\n width: 100%;\n}\n:where([data-sonner-toast][data-mounted=true]) {\n --y: translateY(0);\n opacity: 1;\n}\n:where([data-sonner-toast][data-expanded=false][data-front=false]) {\n --scale: var(--toasts-before) * 0.05 + 1;\n --y: translateY(calc(var(--lift-amount) * var(--toasts-before))) scale(calc(-1 * var(--scale)));\n height: var(--front-toast-height);\n}\n:where([data-sonner-toast]) > * {\n transition: opacity 400ms;\n}\n:where([data-sonner-toast][data-expanded=false][data-front=false][data-styled=true]) > * {\n opacity: 0;\n}\n:where([data-sonner-toast][data-visible=false]) {\n opacity: 0;\n pointer-events: none;\n}\n:where([data-sonner-toast][data-mounted=true][data-expanded=true]) {\n --y: translateY(calc(var(--lift) * var(--offset)));\n height: var(--initial-height);\n}\n:where([data-sonner-toast][data-removed=true][data-front=true][data-swipe-out=false]) {\n --y: translateY(calc(var(--lift) * -100%));\n opacity: 0;\n}\n:where([data-sonner-toast][data-removed=true][data-front=false][data-swipe-out=false][data-expanded=true]) {\n --y: translateY(calc(var(--lift) * var(--offset) + var(--lift) * -100%));\n opacity: 0;\n}\n:where([data-sonner-toast][data-removed=true][data-front=false][data-swipe-out=false][data-expanded=false]) {\n --y: translateY(40%);\n opacity: 0;\n transition: transform 500ms, opacity 200ms;\n}\n:where([data-sonner-toast][data-removed=true][data-front=false])::before {\n height: calc(var(--initial-height) + 20%);\n}\n[data-sonner-toast][data-swiping=true] {\n transform: var(--y) translateY(var(--swipe-amount, 0px));\n transition: none;\n}\n[data-sonner-toast][data-swipe-out=true][data-y-position=bottom],\n[data-sonner-toast][data-swipe-out=true][data-y-position=top] {\n animation: swipe-out 200ms ease-out forwards;\n}\n@keyframes swipe-out {\n from {\n transform: translateY(calc(var(--lift) * var(--offset) + var(--swipe-amount)));\n opacity: 1;\n }\n to {\n transform: translateY(calc(var(--lift) * var(--offset) + var(--swipe-amount) + var(--lift) * -100%));\n opacity: 0;\n }\n}\n@media (max-width: 600px) {\n [data-sonner-toaster] {\n position: fixed;\n --mobile-offset: 16px;\n right: var(--mobile-offset);\n left: var(--mobile-offset);\n width: 100%;\n }\n [data-sonner-toaster] [data-sonner-toast] {\n left: 0;\n right: 0;\n width: calc(100% - var(--mobile-offset) * 2);\n }\n [data-sonner-toaster][data-x-position=left] {\n left: var(--mobile-offset);\n }\n [data-sonner-toaster][data-y-position=bottom] {\n bottom: 20px;\n }\n [data-sonner-toaster][data-y-position=top] {\n top: 20px;\n }\n [data-sonner-toaster][data-x-position=center] {\n left: var(--mobile-offset);\n right: var(--mobile-offset);\n transform: none;\n }\n}\n[data-sonner-toaster][data-theme=light] {\n --normal-bg: #fff;\n --normal-border: var(--gray4);\n --normal-text: var(--gray12);\n --success-bg: hsl(143, 85%, 96%);\n --success-border: hsl(145, 92%, 91%);\n --success-text: hsl(140, 100%, 27%);\n --info-bg: hsl(208, 100%, 97%);\n --info-border: hsl(221, 91%, 91%);\n --info-text: hsl(210, 92%, 45%);\n --warning-bg: hsl(49, 100%, 97%);\n --warning-border: hsl(49, 91%, 91%);\n --warning-text: hsl(31, 92%, 45%);\n --error-bg: hsl(359, 100%, 97%);\n --error-border: hsl(359, 100%, 94%);\n --error-text: hsl(360, 100%, 45%);\n}\n[data-sonner-toaster][data-theme=light] [data-sonner-toast][data-invert=true] {\n --normal-bg: #000;\n --normal-border: hsl(0, 0%, 20%);\n --normal-text: var(--gray1);\n}\n[data-sonner-toaster][data-theme=dark] [data-sonner-toast][data-invert=true] {\n --normal-bg: #fff;\n --normal-border: var(--gray3);\n --normal-text: var(--gray12);\n}\n[data-sonner-toaster][data-theme=dark] {\n --normal-bg: #000;\n --normal-border: hsl(0, 0%, 20%);\n --normal-text: var(--gray1);\n --success-bg: hsl(150, 100%, 6%);\n --success-border: hsl(147, 100%, 12%);\n --success-text: hsl(150, 86%, 65%);\n --info-bg: hsl(215, 100%, 6%);\n --info-border: hsl(223, 100%, 12%);\n --info-text: hsl(216, 87%, 65%);\n --warning-bg: hsl(64, 100%, 6%);\n --warning-border: hsl(60, 100%, 12%);\n --warning-text: hsl(46, 87%, 65%);\n --error-bg: hsl(358, 76%, 10%);\n --error-border: hsl(357, 89%, 16%);\n --error-text: hsl(358, 100%, 81%);\n}\n[data-rich-colors=true] [data-sonner-toast][data-type=success] {\n background: var(--success-bg);\n border-color: var(--success-border);\n color: var(--success-text);\n}\n[data-rich-colors=true] [data-sonner-toast][data-type=success] [data-close-button] {\n background: var(--success-bg);\n border-color: var(--success-border);\n color: var(--success-text);\n}\n[data-rich-colors=true] [data-sonner-toast][data-type=info] {\n background: var(--info-bg);\n border-color: var(--info-border);\n color: var(--info-text);\n}\n[data-rich-colors=true] [data-sonner-toast][data-type=info] [data-close-button] {\n background: var(--info-bg);\n border-color: var(--info-border);\n color: var(--info-text);\n}\n[data-rich-colors=true] [data-sonner-toast][data-type=warning] {\n background: var(--warning-bg);\n border-color: var(--warning-border);\n color: var(--warning-text);\n}\n[data-rich-colors=true] [data-sonner-toast][data-type=warning] [data-close-button] {\n background: var(--warning-bg);\n border-color: var(--warning-border);\n color: var(--warning-text);\n}\n[data-rich-colors=true] [data-sonner-toast][data-type=error] {\n background: var(--error-bg);\n border-color: var(--error-border);\n color: var(--error-text);\n}\n[data-rich-colors=true] [data-sonner-toast][data-type=error] [data-close-button] {\n background: var(--error-bg);\n border-color: var(--error-border);\n color: var(--error-text);\n}\n.sonner-loading-wrapper {\n --size: 16px;\n height: var(--size);\n width: var(--size);\n position: absolute;\n inset: 0;\n z-index: 10;\n}\n.sonner-loading-wrapper[data-visible=false] {\n transform-origin: center;\n animation: sonner-fade-out 0.2s ease forwards;\n}\n.sonner-spinner {\n position: relative;\n top: 50%;\n left: 50%;\n height: var(--size);\n width: var(--size);\n}\n.sonner-loading-bar {\n animation: sonner-spin 1.2s linear infinite;\n background: var(--gray11);\n border-radius: 6px;\n height: 8%;\n left: -10%;\n position: absolute;\n top: -3.9%;\n width: 24%;\n}\n.sonner-loading-bar:nth-child(1) {\n animation-delay: -1.2s;\n transform: rotate(0.0001deg) translate(146%);\n}\n.sonner-loading-bar:nth-child(2) {\n animation-delay: -1.1s;\n transform: rotate(30deg) translate(146%);\n}\n.sonner-loading-bar:nth-child(3) {\n animation-delay: -1s;\n transform: rotate(60deg) translate(146%);\n}\n.sonner-loading-bar:nth-child(4) {\n animation-delay: -0.9s;\n transform: rotate(90deg) translate(146%);\n}\n.sonner-loading-bar:nth-child(5) {\n animation-delay: -0.8s;\n transform: rotate(120deg) translate(146%);\n}\n.sonner-loading-bar:nth-child(6) {\n animation-delay: -0.7s;\n transform: rotate(150deg) translate(146%);\n}\n.sonner-loading-bar:nth-child(7) {\n animation-delay: -0.6s;\n transform: rotate(180deg) translate(146%);\n}\n.sonner-loading-bar:nth-child(8) {\n animation-delay: -0.5s;\n transform: rotate(210deg) translate(146%);\n}\n.sonner-loading-bar:nth-child(9) {\n animation-delay: -0.4s;\n transform: rotate(240deg) translate(146%);\n}\n.sonner-loading-bar:nth-child(10) {\n animation-delay: -0.3s;\n transform: rotate(270deg) translate(146%);\n}\n.sonner-loading-bar:nth-child(11) {\n animation-delay: -0.2s;\n transform: rotate(300deg) translate(146%);\n}\n.sonner-loading-bar:nth-child(12) {\n animation-delay: -0.1s;\n transform: rotate(330deg) translate(146%);\n}\n@keyframes sonner-fade-in {\n 0% {\n opacity: 0;\n transform: scale(0.8);\n }\n 100% {\n opacity: 1;\n transform: scale(1);\n }\n}\n@keyframes sonner-fade-out {\n 0% {\n opacity: 1;\n transform: scale(1);\n }\n 100% {\n opacity: 0;\n transform: scale(0.8);\n }\n}\n@keyframes sonner-spin {\n 0% {\n opacity: 1;\n }\n 100% {\n opacity: 0.15;\n }\n}\n@media (prefers-reduced-motion) {\n [data-sonner-toast],\n [data-sonner-toast] > *,\n .sonner-loading-bar {\n transition: none !important;\n animation: none !important;\n }\n}\n.sonner-loader {\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n transform-origin: center;\n transition: opacity 200ms, transform 200ms;\n}\n.sonner-loader[data-visible=false] {\n opacity: 0;\n transform: scale(0.8) translate(-50%, -50%);\n}\n'); | ||
| styleInject('html[dir=ltr],\n[data-sonner-toaster][dir=ltr] {\n --toast-icon-margin-start: -3px;\n --toast-icon-margin-end: 4px;\n --toast-svg-margin-start: -1px;\n --toast-svg-margin-end: 0px;\n --toast-button-margin-start: auto;\n --toast-button-margin-end: 0;\n --toast-close-button-start: 0;\n --toast-close-button-end: unset;\n --toast-close-button-transform: translate(-35%, -35%);\n}\nhtml[dir=rtl],\n[data-sonner-toaster][dir=rtl] {\n --toast-icon-margin-start: 4px;\n --toast-icon-margin-end: -3px;\n --toast-svg-margin-start: 0px;\n --toast-svg-margin-end: -1px;\n --toast-button-margin-start: 0;\n --toast-button-margin-end: auto;\n --toast-close-button-start: unset;\n --toast-close-button-end: 0;\n --toast-close-button-transform: translate(35%, -35%);\n}\n[data-sonner-toaster] {\n position: fixed;\n width: var(--width);\n font-family:\n ui-sans-serif,\n system-ui,\n -apple-system,\n BlinkMacSystemFont,\n Segoe UI,\n Roboto,\n Helvetica Neue,\n Arial,\n Noto Sans,\n sans-serif,\n Apple Color Emoji,\n Segoe UI Emoji,\n Segoe UI Symbol,\n Noto Color Emoji;\n --gray1: hsl(0, 0%, 99%);\n --gray2: hsl(0, 0%, 97.3%);\n --gray3: hsl(0, 0%, 95.1%);\n --gray4: hsl(0, 0%, 93%);\n --gray5: hsl(0, 0%, 90.9%);\n --gray6: hsl(0, 0%, 88.7%);\n --gray7: hsl(0, 0%, 85.8%);\n --gray8: hsl(0, 0%, 78%);\n --gray9: hsl(0, 0%, 56.1%);\n --gray10: hsl(0, 0%, 52.3%);\n --gray11: hsl(0, 0%, 43.5%);\n --gray12: hsl(0, 0%, 9%);\n --border-radius: 8px;\n box-sizing: border-box;\n padding: 0;\n margin: 0;\n list-style: none;\n outline: none;\n z-index: 999999999;\n transition: transform 400ms ease;\n}\n@media (hover: none) and (pointer: coarse) {\n [data-sonner-toaster][data-lifted=true] {\n transform: none;\n }\n}\n[data-sonner-toaster][data-x-position=right] {\n right: var(--offset-right);\n}\n[data-sonner-toaster][data-x-position=left] {\n left: var(--offset-left);\n}\n[data-sonner-toaster][data-x-position=center] {\n left: 50%;\n transform: translateX(-50%);\n}\n[data-sonner-toaster][data-y-position=top] {\n top: var(--offset-top);\n}\n[data-sonner-toaster][data-y-position=bottom] {\n bottom: var(--offset-bottom);\n}\n[data-sonner-toast] {\n --y: translateY(100%);\n --lift-amount: calc(var(--lift) * var(--gap));\n z-index: var(--z-index);\n position: absolute;\n opacity: 0;\n transform: var(--y);\n touch-action: none;\n transition:\n transform 400ms,\n opacity 400ms,\n height 400ms,\n box-shadow 200ms;\n box-sizing: border-box;\n outline: none;\n overflow-wrap: anywhere;\n}\n[data-sonner-toast][data-styled=true] {\n padding: 16px;\n background: var(--normal-bg);\n border: 1px solid var(--normal-border);\n color: var(--normal-text);\n border-radius: var(--border-radius);\n box-shadow: 0px 4px 12px rgba(0, 0, 0, 0.1);\n width: var(--width);\n font-size: 13px;\n display: flex;\n align-items: center;\n gap: 6px;\n}\n[data-sonner-toast]:focus-visible {\n box-shadow: 0px 4px 12px rgba(0, 0, 0, 0.1), 0 0 0 2px rgba(0, 0, 0, 0.2);\n}\n[data-sonner-toast][data-y-position=top] {\n top: 0;\n --y: translateY(-100%);\n --lift: 1;\n --lift-amount: calc(1 * var(--gap));\n}\n[data-sonner-toast][data-y-position=bottom] {\n bottom: 0;\n --y: translateY(100%);\n --lift: -1;\n --lift-amount: calc(var(--lift) * var(--gap));\n}\n[data-sonner-toast][data-styled=true] [data-description] {\n font-weight: 400;\n line-height: 1.4;\n color: #3f3f3f;\n}\n[data-rich-colors=true][data-sonner-toast][data-styled=true] [data-description] {\n color: inherit;\n}\n[data-sonner-toaster][data-sonner-theme=dark] [data-description] {\n color: hsl(0, 0%, 91%);\n}\n[data-sonner-toast][data-styled=true] [data-title] {\n font-weight: 500;\n line-height: 1.5;\n color: inherit;\n}\n[data-sonner-toast][data-styled=true] [data-icon] {\n display: flex;\n height: 16px;\n width: 16px;\n position: relative;\n justify-content: flex-start;\n align-items: center;\n flex-shrink: 0;\n margin-left: var(--toast-icon-margin-start);\n margin-right: var(--toast-icon-margin-end);\n}\n[data-sonner-toast][data-promise=true] [data-icon] > svg {\n opacity: 0;\n transform: scale(0.8);\n transform-origin: center;\n animation: sonner-fade-in 300ms ease forwards;\n}\n[data-sonner-toast][data-styled=true] [data-icon] > * {\n flex-shrink: 0;\n}\n[data-sonner-toast][data-styled=true] [data-icon] svg {\n margin-left: var(--toast-svg-margin-start);\n margin-right: var(--toast-svg-margin-end);\n}\n[data-sonner-toast][data-styled=true] [data-content] {\n display: flex;\n flex-direction: column;\n gap: 2px;\n}\n[data-sonner-toast][data-styled=true] [data-button] {\n border-radius: 4px;\n padding-left: 8px;\n padding-right: 8px;\n height: 24px;\n font-size: 12px;\n color: var(--normal-bg);\n background: var(--normal-text);\n margin-left: var(--toast-button-margin-start);\n margin-right: var(--toast-button-margin-end);\n border: none;\n font-weight: 500;\n cursor: pointer;\n outline: none;\n display: flex;\n align-items: center;\n flex-shrink: 0;\n transition: opacity 400ms, box-shadow 200ms;\n}\n[data-sonner-toast][data-styled=true] [data-button]:focus-visible {\n box-shadow: 0 0 0 2px rgba(0, 0, 0, 0.4);\n}\n[data-sonner-toast][data-styled=true] [data-button]:first-of-type {\n margin-left: var(--toast-button-margin-start);\n margin-right: var(--toast-button-margin-end);\n}\n[data-sonner-toast][data-styled=true] [data-cancel] {\n color: var(--normal-text);\n background: rgba(0, 0, 0, 0.08);\n}\n[data-sonner-toaster][data-sonner-theme=dark] [data-sonner-toast][data-styled=true] [data-cancel] {\n background: rgba(255, 255, 255, 0.3);\n}\n[data-sonner-toast][data-styled=true] [data-close-button] {\n position: absolute;\n left: var(--toast-close-button-start);\n right: var(--toast-close-button-end);\n top: 0;\n height: 20px;\n width: 20px;\n display: flex;\n justify-content: center;\n align-items: center;\n padding: 0;\n color: var(--normal-text);\n background: var(--normal-bg);\n border: 1px solid var(--normal-border);\n transform: var(--toast-close-button-transform);\n border-radius: 50%;\n cursor: pointer;\n z-index: 1;\n transition:\n opacity 100ms,\n background 200ms,\n border-color 200ms;\n}\n[data-sonner-toast][data-styled=true] [data-close-button]:focus-visible {\n box-shadow: 0px 4px 12px rgba(0, 0, 0, 0.1), 0 0 0 2px rgba(0, 0, 0, 0.2);\n}\n[data-sonner-toast][data-styled=true] [data-disabled=true] {\n cursor: not-allowed;\n}\n[data-sonner-toast][data-styled=true]:hover [data-close-button]:hover {\n background: var(--gray2);\n border-color: var(--gray5);\n}\n[data-sonner-toast][data-swiping=true]::before {\n content: "";\n position: absolute;\n left: -100%;\n right: -100%;\n height: 100%;\n z-index: -1;\n}\n[data-sonner-toast][data-y-position=top][data-swiping=true]::before {\n bottom: 50%;\n transform: scaleY(3) translateY(50%);\n}\n[data-sonner-toast][data-y-position=bottom][data-swiping=true]::before {\n top: 50%;\n transform: scaleY(3) translateY(-50%);\n}\n[data-sonner-toast][data-swiping=false][data-removed=true]::before {\n content: "";\n position: absolute;\n inset: 0;\n transform: scaleY(2);\n}\n[data-sonner-toast][data-expanded=true]::after {\n content: "";\n position: absolute;\n left: 0;\n height: calc(var(--gap) + 1px);\n bottom: 100%;\n width: 100%;\n}\n[data-sonner-toast][data-mounted=true] {\n --y: translateY(0);\n opacity: 1;\n}\n[data-sonner-toast][data-expanded=false][data-front=false] {\n --scale: var(--toasts-before) * 0.05 + 1;\n --y: translateY(calc(var(--lift-amount) * var(--toasts-before))) scale(calc(-1 * var(--scale)));\n height: var(--front-toast-height);\n}\n[data-sonner-toast] > * {\n transition: opacity 400ms;\n}\n[data-sonner-toast][data-x-position=right] {\n right: 0;\n}\n[data-sonner-toast][data-x-position=left] {\n left: 0;\n}\n[data-sonner-toast][data-expanded=false][data-front=false][data-styled=true] > * {\n opacity: 0;\n}\n[data-sonner-toast][data-visible=false] {\n opacity: 0;\n pointer-events: none;\n}\n[data-sonner-toast][data-mounted=true][data-expanded=true] {\n --y: translateY(calc(var(--lift) * var(--offset)));\n height: var(--initial-height);\n}\n[data-sonner-toast][data-removed=true][data-front=true][data-swipe-out=false] {\n --y: translateY(calc(var(--lift) * -100%));\n opacity: 0;\n}\n[data-sonner-toast][data-removed=true][data-front=false][data-swipe-out=false][data-expanded=true] {\n --y: translateY(calc(var(--lift) * var(--offset) + var(--lift) * -100%));\n opacity: 0;\n}\n[data-sonner-toast][data-removed=true][data-front=false][data-swipe-out=false][data-expanded=false] {\n --y: translateY(40%);\n opacity: 0;\n transition: transform 500ms, opacity 200ms;\n}\n[data-sonner-toast][data-removed=true][data-front=false]::before {\n height: calc(var(--initial-height) + 20%);\n}\n[data-sonner-toast][data-swiping=true] {\n transform: var(--y) translateY(var(--swipe-amount-y, 0px)) translateX(var(--swipe-amount-x, 0px));\n transition: none;\n}\n[data-sonner-toast][data-swiped=true] {\n -webkit-user-select: none;\n user-select: none;\n}\n[data-sonner-toast][data-swipe-out=true][data-y-position=bottom],\n[data-sonner-toast][data-swipe-out=true][data-y-position=top] {\n animation-duration: 200ms;\n animation-timing-function: ease-out;\n animation-fill-mode: forwards;\n}\n[data-sonner-toast][data-swipe-out=true][data-swipe-direction=left] {\n animation-name: swipe-out-left;\n}\n[data-sonner-toast][data-swipe-out=true][data-swipe-direction=right] {\n animation-name: swipe-out-right;\n}\n[data-sonner-toast][data-swipe-out=true][data-swipe-direction=up] {\n animation-name: swipe-out-up;\n}\n[data-sonner-toast][data-swipe-out=true][data-swipe-direction=down] {\n animation-name: swipe-out-down;\n}\n@keyframes swipe-out-left {\n from {\n transform: var(--y) translateX(var(--swipe-amount-x));\n opacity: 1;\n }\n to {\n transform: var(--y) translateX(calc(var(--swipe-amount-x) - 100%));\n opacity: 0;\n }\n}\n@keyframes swipe-out-right {\n from {\n transform: var(--y) translateX(var(--swipe-amount-x));\n opacity: 1;\n }\n to {\n transform: var(--y) translateX(calc(var(--swipe-amount-x) + 100%));\n opacity: 0;\n }\n}\n@keyframes swipe-out-up {\n from {\n transform: var(--y) translateY(var(--swipe-amount-y));\n opacity: 1;\n }\n to {\n transform: var(--y) translateY(calc(var(--swipe-amount-y) - 100%));\n opacity: 0;\n }\n}\n@keyframes swipe-out-down {\n from {\n transform: var(--y) translateY(var(--swipe-amount-y));\n opacity: 1;\n }\n to {\n transform: var(--y) translateY(calc(var(--swipe-amount-y) + 100%));\n opacity: 0;\n }\n}\n@media (max-width: 600px) {\n [data-sonner-toaster] {\n position: fixed;\n right: var(--mobile-offset-right);\n left: var(--mobile-offset-left);\n width: 100%;\n }\n [data-sonner-toaster][dir=rtl] {\n left: calc(var(--mobile-offset-left) * -1);\n }\n [data-sonner-toaster] [data-sonner-toast] {\n left: 0;\n right: 0;\n width: calc(100% - var(--mobile-offset-left) * 2);\n }\n [data-sonner-toaster][data-x-position=left] {\n left: var(--mobile-offset-left);\n }\n [data-sonner-toaster][data-y-position=bottom] {\n bottom: var(--mobile-offset-bottom);\n }\n [data-sonner-toaster][data-y-position=top] {\n top: var(--mobile-offset-top);\n }\n [data-sonner-toaster][data-x-position=center] {\n left: var(--mobile-offset-left);\n right: var(--mobile-offset-right);\n transform: none;\n }\n}\n[data-sonner-toaster][data-sonner-theme=light] {\n --normal-bg: #fff;\n --normal-border: var(--gray4);\n --normal-text: var(--gray12);\n --success-bg: hsl(143, 85%, 96%);\n --success-border: hsl(145, 92%, 87%);\n --success-text: hsl(140, 100%, 27%);\n --info-bg: hsl(208, 100%, 97%);\n --info-border: hsl(221, 91%, 93%);\n --info-text: hsl(210, 92%, 45%);\n --warning-bg: hsl(49, 100%, 97%);\n --warning-border: hsl(49, 91%, 84%);\n --warning-text: hsl(31, 92%, 45%);\n --error-bg: hsl(359, 100%, 97%);\n --error-border: hsl(359, 100%, 94%);\n --error-text: hsl(360, 100%, 45%);\n}\n[data-sonner-toaster][data-sonner-theme=light] [data-sonner-toast][data-invert=true] {\n --normal-bg: #000;\n --normal-border: hsl(0, 0%, 20%);\n --normal-text: var(--gray1);\n}\n[data-sonner-toaster][data-sonner-theme=dark] [data-sonner-toast][data-invert=true] {\n --normal-bg: #fff;\n --normal-border: var(--gray3);\n --normal-text: var(--gray12);\n}\n[data-sonner-toaster][data-sonner-theme=dark] {\n --normal-bg: #000;\n --normal-bg-hover: hsl(0, 0%, 12%);\n --normal-border: hsl(0, 0%, 20%);\n --normal-border-hover: hsl(0, 0%, 25%);\n --normal-text: var(--gray1);\n --success-bg: hsl(150, 100%, 6%);\n --success-border: hsl(147, 100%, 12%);\n --success-text: hsl(150, 86%, 65%);\n --info-bg: hsl(215, 100%, 6%);\n --info-border: hsl(223, 43%, 17%);\n --info-text: hsl(216, 87%, 65%);\n --warning-bg: hsl(64, 100%, 6%);\n --warning-border: hsl(60, 100%, 9%);\n --warning-text: hsl(46, 87%, 65%);\n --error-bg: hsl(358, 76%, 10%);\n --error-border: hsl(357, 89%, 16%);\n --error-text: hsl(358, 100%, 81%);\n}\n[data-sonner-toaster][data-sonner-theme=dark] [data-sonner-toast] [data-close-button] {\n background: var(--normal-bg);\n border-color: var(--normal-border);\n color: var(--normal-text);\n}\n[data-sonner-toaster][data-sonner-theme=dark] [data-sonner-toast] [data-close-button]:hover {\n background: var(--normal-bg-hover);\n border-color: var(--normal-border-hover);\n}\n[data-rich-colors=true][data-sonner-toast][data-type=success] {\n background: var(--success-bg);\n border-color: var(--success-border);\n color: var(--success-text);\n}\n[data-rich-colors=true][data-sonner-toast][data-type=success] [data-close-button] {\n background: var(--success-bg);\n border-color: var(--success-border);\n color: var(--success-text);\n}\n[data-rich-colors=true][data-sonner-toast][data-type=info] {\n background: var(--info-bg);\n border-color: var(--info-border);\n color: var(--info-text);\n}\n[data-rich-colors=true][data-sonner-toast][data-type=info] [data-close-button] {\n background: var(--info-bg);\n border-color: var(--info-border);\n color: var(--info-text);\n}\n[data-rich-colors=true][data-sonner-toast][data-type=warning] {\n background: var(--warning-bg);\n border-color: var(--warning-border);\n color: var(--warning-text);\n}\n[data-rich-colors=true][data-sonner-toast][data-type=warning] [data-close-button] {\n background: var(--warning-bg);\n border-color: var(--warning-border);\n color: var(--warning-text);\n}\n[data-rich-colors=true][data-sonner-toast][data-type=error] {\n background: var(--error-bg);\n border-color: var(--error-border);\n color: var(--error-text);\n}\n[data-rich-colors=true][data-sonner-toast][data-type=error] [data-close-button] {\n background: var(--error-bg);\n border-color: var(--error-border);\n color: var(--error-text);\n}\n.sonner-loading-wrapper {\n --size: 16px;\n height: var(--size);\n width: var(--size);\n position: absolute;\n inset: 0;\n z-index: 10;\n}\n.sonner-loading-wrapper[data-visible=false] {\n transform-origin: center;\n animation: sonner-fade-out 0.2s ease forwards;\n}\n.sonner-spinner {\n position: relative;\n top: 50%;\n left: 50%;\n height: var(--size);\n width: var(--size);\n}\n.sonner-loading-bar {\n animation: sonner-spin 1.2s linear infinite;\n background: var(--gray11);\n border-radius: 6px;\n height: 8%;\n left: -10%;\n position: absolute;\n top: -3.9%;\n width: 24%;\n}\n.sonner-loading-bar:nth-child(1) {\n animation-delay: -1.2s;\n transform: rotate(0.0001deg) translate(146%);\n}\n.sonner-loading-bar:nth-child(2) {\n animation-delay: -1.1s;\n transform: rotate(30deg) translate(146%);\n}\n.sonner-loading-bar:nth-child(3) {\n animation-delay: -1s;\n transform: rotate(60deg) translate(146%);\n}\n.sonner-loading-bar:nth-child(4) {\n animation-delay: -0.9s;\n transform: rotate(90deg) translate(146%);\n}\n.sonner-loading-bar:nth-child(5) {\n animation-delay: -0.8s;\n transform: rotate(120deg) translate(146%);\n}\n.sonner-loading-bar:nth-child(6) {\n animation-delay: -0.7s;\n transform: rotate(150deg) translate(146%);\n}\n.sonner-loading-bar:nth-child(7) {\n animation-delay: -0.6s;\n transform: rotate(180deg) translate(146%);\n}\n.sonner-loading-bar:nth-child(8) {\n animation-delay: -0.5s;\n transform: rotate(210deg) translate(146%);\n}\n.sonner-loading-bar:nth-child(9) {\n animation-delay: -0.4s;\n transform: rotate(240deg) translate(146%);\n}\n.sonner-loading-bar:nth-child(10) {\n animation-delay: -0.3s;\n transform: rotate(270deg) translate(146%);\n}\n.sonner-loading-bar:nth-child(11) {\n animation-delay: -0.2s;\n transform: rotate(300deg) translate(146%);\n}\n.sonner-loading-bar:nth-child(12) {\n animation-delay: -0.1s;\n transform: rotate(330deg) translate(146%);\n}\n@keyframes sonner-fade-in {\n 0% {\n opacity: 0;\n transform: scale(0.8);\n }\n 100% {\n opacity: 1;\n transform: scale(1);\n }\n}\n@keyframes sonner-fade-out {\n 0% {\n opacity: 1;\n transform: scale(1);\n }\n 100% {\n opacity: 0;\n transform: scale(0.8);\n }\n}\n@keyframes sonner-spin {\n 0% {\n opacity: 1;\n }\n 100% {\n opacity: 0.15;\n }\n}\n@media (prefers-reduced-motion) {\n [data-sonner-toast],\n [data-sonner-toast] > *,\n .sonner-loading-bar {\n transition: none !important;\n animation: none !important;\n }\n}\n.sonner-loader {\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n transform-origin: center;\n transition: opacity 200ms, transform 200ms;\n}\n.sonner-loader[data-visible=false] {\n opacity: 0;\n transform: scale(0.8) translate(-50%, -50%);\n}\n'); | ||
| // src/index.tsx | ||
| import { For as For2, Show, createEffect, createSignal as createSignal2, mergeProps, on, onCleanup as onCleanup2, onMount as onMount2 } from "solid-js"; | ||
| import { For as For2, Show, createEffect, createMemo, createSignal as createSignal2, mergeProps, on, onCleanup as onCleanup2, onMount as onMount2 } from "solid-js"; | ||
| import { createStore, produce, reconcile } from "solid-js/store"; | ||
@@ -35,31 +35,43 @@ | ||
| function Loader(props) { | ||
| return <div class="sonner-loading-wrapper" data-visible={props.visible}><div class="sonner-spinner"><For each={bars}>{() => <div class="sonner-loading-bar" />}</For></div></div>; | ||
| return <div class={["sonner-loading-wrapper", props.class ?? props.className].filter(Boolean).join(" ")} data-visible={props.visible}> | ||
| <div class="sonner-spinner"> | ||
| <For each={bars}>{(_, i) => <div class="sonner-loading-bar" data-index={i()} />}</For> | ||
| </div> | ||
| </div>; | ||
| } | ||
| function SuccessIcon() { | ||
| return <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" height="20" width="20"><path | ||
| return <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" height="20" width="20"> | ||
| <path | ||
| fill-rule="evenodd" | ||
| d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.857-9.809a.75.75 0 00-1.214-.882l-3.483 4.79-1.88-1.88a.75.75 0 10-1.06 1.061l2.5 2.5a.75.75 0 001.137-.089l4-5.5z" | ||
| clip-rule="evenodd" | ||
| /></svg>; | ||
| /> | ||
| </svg>; | ||
| } | ||
| function WarningIcon() { | ||
| return <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" height="20" width="20"><path | ||
| return <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" height="20" width="20"> | ||
| <path | ||
| fill-rule="evenodd" | ||
| d="M9.401 3.003c1.155-2 4.043-2 5.197 0l7.355 12.748c1.154 2-.29 4.5-2.599 4.5H4.645c-2.309 0-3.752-2.5-2.598-4.5L9.4 3.003zM12 8.25a.75.75 0 01.75.75v3.75a.75.75 0 01-1.5 0V9a.75.75 0 01.75-.75zm0 8.25a.75.75 0 100-1.5.75.75 0 000 1.5z" | ||
| clip-rule="evenodd" | ||
| /></svg>; | ||
| /> | ||
| </svg>; | ||
| } | ||
| function InfoIcon() { | ||
| return <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" height="20" width="20"><path | ||
| return <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" height="20" width="20"> | ||
| <path | ||
| fill-rule="evenodd" | ||
| d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a.75.75 0 000 1.5h.253a.25.25 0 01.244.304l-.459 2.066A1.75 1.75 0 0010.747 15H11a.75.75 0 000-1.5h-.253a.25.25 0 01-.244-.304l.459-2.066A1.75 1.75 0 009.253 9H9z" | ||
| clip-rule="evenodd" | ||
| /></svg>; | ||
| /> | ||
| </svg>; | ||
| } | ||
| function ErrorIcon() { | ||
| return <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" height="20" width="20"><path | ||
| return <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" height="20" width="20"> | ||
| <path | ||
| fill-rule="evenodd" | ||
| d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-8-5a.75.75 0 01.75.75v4.5a.75.75 0 01-1.5 0v-4.5A.75.75 0 0110 5zm0 10a1 1 0 100-2 1 1 0 000 2z" | ||
| clip-rule="evenodd" | ||
| /></svg>; | ||
| /> | ||
| </svg>; | ||
| } | ||
@@ -69,9 +81,9 @@ function getAsset(type) { | ||
| case "success": | ||
| return SuccessIcon; | ||
| return <SuccessIcon />; | ||
| case "info": | ||
| return InfoIcon; | ||
| return <InfoIcon />; | ||
| case "warning": | ||
| return WarningIcon; | ||
| return <WarningIcon />; | ||
| case "error": | ||
| return ErrorIcon; | ||
| return <ErrorIcon />; | ||
| default: | ||
@@ -81,13 +93,46 @@ return null; | ||
| } | ||
| function CloseIcon() { | ||
| return <svg | ||
| xmlns="http://www.w3.org/2000/svg" | ||
| width="12" | ||
| height="12" | ||
| viewBox="0 0 24 24" | ||
| fill="none" | ||
| stroke="currentColor" | ||
| stroke-width="1.5" | ||
| stroke-linecap="round" | ||
| stroke-linejoin="round" | ||
| > | ||
| <line x1="18" y1="6" x2="6" y2="18" /> | ||
| <line x1="6" y1="6" x2="18" y2="18" /> | ||
| </svg>; | ||
| } | ||
| // src/primitives.ts | ||
| import { createSignal, onCleanup, onMount } from "solid-js"; | ||
| function useIsDocumentHidden() { | ||
| const [isDocumentHidden, setIsDocumentHidden] = createSignal(typeof document !== "undefined" ? document.hidden : false); | ||
| onMount(() => { | ||
| const callback = () => { | ||
| setIsDocumentHidden(document.hidden); | ||
| }; | ||
| document.addEventListener("visibilitychange", callback); | ||
| onCleanup(() => { | ||
| document.removeEventListener("visibilitychange", callback); | ||
| }); | ||
| }); | ||
| return isDocumentHidden; | ||
| } | ||
| // src/state.ts | ||
| var toastsCounter = 0; | ||
| var toastsCounter = 1; | ||
| var Observer = class { | ||
| subscribers; | ||
| toasts; | ||
| dismissedToasts; | ||
| constructor() { | ||
| this.subscribers = []; | ||
| this.toasts = []; | ||
| this.dismissedToasts = /* @__PURE__ */ new Set(); | ||
| } | ||
| // We use arrow functions to maintain the correct `this` reference | ||
| subscribe = (subscriber) => { | ||
@@ -109,11 +154,21 @@ this.subscribers.push(subscriber); | ||
| const { message, ...rest } = data; | ||
| const id = typeof data?.id === "number" || data.id && data.id?.length > 0 ? data.id : toastsCounter++; | ||
| const hasStringId = typeof data.id === "string" && data.id.length > 0; | ||
| const id = typeof data.id === "number" || hasStringId ? data.id : toastsCounter++; | ||
| const alreadyExists = this.toasts.find((toast2) => { | ||
| return toast2.id === id; | ||
| }); | ||
| const dismissible = data.dismissible === void 0 ? true : data.dismissible; | ||
| if (this.dismissedToasts.has(id)) | ||
| this.dismissedToasts.delete(id); | ||
| if (alreadyExists) { | ||
| this.toasts = this.toasts.map((toast2) => { | ||
| if (toast2.id === id) { | ||
| this.publish({ ...toast2, ...data, id, title: message }); | ||
| return { ...toast2, ...data, id, title: message }; | ||
| this.publish({ ...toast2, ...data, id, title: message, dismissible }); | ||
| return { | ||
| ...toast2, | ||
| ...data, | ||
| id, | ||
| dismissible, | ||
| title: message | ||
| }; | ||
| } | ||
@@ -123,3 +178,3 @@ return toast2; | ||
| } else { | ||
| this.addToast({ title: message, ...rest, id }); | ||
| this.addToast({ title: message, ...rest, dismissible, id }); | ||
| } | ||
@@ -129,8 +184,13 @@ return id; | ||
| dismiss = (id) => { | ||
| if (!id) { | ||
| if (id) { | ||
| this.dismissedToasts.add(id); | ||
| requestAnimationFrame(() => { | ||
| this.subscribers.forEach((subscriber) => subscriber({ id, dismiss: true })); | ||
| }); | ||
| } else { | ||
| this.toasts.forEach((toast2) => { | ||
| this.dismissedToasts.add(toast2.id); | ||
| this.subscribers.forEach((subscriber) => subscriber({ id: toast2.id, dismiss: true })); | ||
| }); | ||
| } | ||
| this.subscribers.forEach((subscriber) => subscriber({ id, dismiss: true })); | ||
| return id; | ||
@@ -153,6 +213,8 @@ }; | ||
| }; | ||
| loading = (message, data) => { | ||
| return this.create({ ...data, type: "loading", message }); | ||
| }; | ||
| promise = (promise, data) => { | ||
| if (!data) { | ||
| if (!data) | ||
| return; | ||
| } | ||
| let id; | ||
@@ -164,22 +226,41 @@ if (data.loading !== void 0) { | ||
| type: "loading", | ||
| message: data.loading | ||
| message: data.loading, | ||
| description: typeof data.description !== "function" ? data.description : void 0 | ||
| }); | ||
| } | ||
| const p = promise instanceof Promise ? promise : promise(); | ||
| const p = Promise.resolve(typeof promise === "function" ? promise() : promise); | ||
| let shouldDismiss = id !== void 0; | ||
| p.then((response) => { | ||
| if (response && typeof response.ok === "boolean" && !response.ok) { | ||
| let result; | ||
| const originalPromise = p.then(async (response) => { | ||
| result = ["resolve", response]; | ||
| if (isValidElement(response)) { | ||
| shouldDismiss = false; | ||
| const message = typeof data.error === "function" ? data.error(`HTTP error! status: ${response.status}`) : data.error; | ||
| this.create({ id, type: "error", message }); | ||
| this.create({ id, type: "default", message: response }); | ||
| } else if (isHttpResponse(response) && !response.ok) { | ||
| shouldDismiss = false; | ||
| const promiseData = typeof data.error === "function" ? await data.error(`HTTP error! status: ${response.status}`) : data.error; | ||
| const description = typeof data.description === "function" ? await data.description(`HTTP error! status: ${response.status}`) : data.description; | ||
| const toastSettings = isExtendedResult(promiseData) ? promiseData : { message: promiseData }; | ||
| this.create({ id, type: "error", description, ...toastSettings }); | ||
| } else if (response instanceof Error) { | ||
| shouldDismiss = false; | ||
| const promiseData = typeof data.error === "function" ? await data.error(response) : data.error; | ||
| const description = typeof data.description === "function" ? await data.description(response) : data.description; | ||
| const toastSettings = isExtendedResult(promiseData) ? promiseData : { message: promiseData }; | ||
| this.create({ id, type: "error", description, ...toastSettings }); | ||
| } else if (data.success !== void 0) { | ||
| shouldDismiss = false; | ||
| const message = typeof data.success === "function" ? data.success(response) : data.success; | ||
| this.create({ id, type: "success", message }); | ||
| const promiseData = typeof data.success === "function" ? await data.success(response) : data.success; | ||
| const description = typeof data.description === "function" ? await data.description(response) : data.description; | ||
| const toastSettings = isExtendedResult(promiseData) ? promiseData : { message: promiseData }; | ||
| this.create({ id, type: "success", description, ...toastSettings }); | ||
| } | ||
| }).catch((error) => { | ||
| }).catch(async (error) => { | ||
| result = ["reject", error]; | ||
| if (data.error !== void 0) { | ||
| shouldDismiss = false; | ||
| const message = typeof data.error === "function" ? data.error(error) : data.error; | ||
| this.create({ id, type: "error", message }); | ||
| const promiseData = typeof data.error === "function" ? await data.error(error) : data.error; | ||
| const description = typeof data.description === "function" ? await data.description(error) : data.description; | ||
| const toastSettings = isExtendedResult(promiseData) ? promiseData : { message: promiseData }; | ||
| this.create({ id, type: "error", description, ...toastSettings }); | ||
| } | ||
@@ -193,14 +274,32 @@ }).finally(() => { | ||
| }); | ||
| return id; | ||
| const unwrap = () => new Promise((resolve, reject) => { | ||
| originalPromise.then(() => { | ||
| if (result[0] === "reject") | ||
| reject(result[1]); | ||
| else | ||
| resolve(result[1]); | ||
| }).catch(reject); | ||
| }); | ||
| if (typeof id !== "string" && typeof id !== "number") | ||
| return { unwrap }; | ||
| return Object.assign(id, { unwrap }); | ||
| }; | ||
| loading = (message, data) => { | ||
| return this.create({ ...data, type: "loading", message }); | ||
| }; | ||
| // We can't provide the toast we just created as a prop as we didn't create it yet, so we can create a default toast object, I just don't know how to use function in argument when calling()? | ||
| custom = (jsx, data) => { | ||
| const id = data?.id || toastsCounter++; | ||
| this.publish({ jsx: jsx(id), id, ...data }); | ||
| this.create({ jsx: jsx(id), ...data, id }); | ||
| return id; | ||
| }; | ||
| getActiveToasts = () => { | ||
| return this.toasts.filter((toast2) => !this.dismissedToasts.has(toast2.id)); | ||
| }; | ||
| }; | ||
| function isHttpResponse(data) { | ||
| return data && typeof data === "object" && "ok" in data && typeof data.ok === "boolean" && "status" in data && typeof data.status === "number"; | ||
| } | ||
| function isExtendedResult(value) { | ||
| return typeof value === "object" && value !== null && "message" in value; | ||
| } | ||
| function isValidElement(value) { | ||
| return typeof Node !== "undefined" && value instanceof Node; | ||
| } | ||
| var ToastState = new Observer(); | ||
@@ -217,28 +316,26 @@ function toastFunction(message, data) { | ||
| var basicToast = toastFunction; | ||
| var toast = Object.assign(basicToast, { | ||
| success: ToastState.success, | ||
| info: ToastState.info, | ||
| warning: ToastState.warning, | ||
| error: ToastState.error, | ||
| custom: ToastState.custom, | ||
| message: ToastState.message, | ||
| promise: ToastState.promise, | ||
| dismiss: ToastState.dismiss, | ||
| loading: ToastState.loading | ||
| }); | ||
| var getHistory = () => ToastState.toasts; | ||
| var getToasts = () => ToastState.getActiveToasts(); | ||
| var toast = Object.assign( | ||
| basicToast, | ||
| { | ||
| success: ToastState.success, | ||
| info: ToastState.info, | ||
| warning: ToastState.warning, | ||
| error: ToastState.error, | ||
| custom: ToastState.custom, | ||
| message: ToastState.message, | ||
| promise: ToastState.promise, | ||
| dismiss: ToastState.dismiss, | ||
| loading: ToastState.loading | ||
| }, | ||
| { | ||
| getHistory, | ||
| getToasts | ||
| } | ||
| ); | ||
| // src/primitives.ts | ||
| import { createSignal, onCleanup, onMount } from "solid-js"; | ||
| function useIsDocumentHidden() { | ||
| const [isDocumentHidden, setIsDocumentHidden] = createSignal(false); | ||
| onMount(() => { | ||
| const callback = () => { | ||
| setIsDocumentHidden(document.hidden); | ||
| }; | ||
| document.addEventListener("visibilitychange", callback); | ||
| onCleanup(() => { | ||
| window.removeEventListener("visibilitychange", callback); | ||
| }); | ||
| }); | ||
| return isDocumentHidden; | ||
| // src/types.ts | ||
| function isAction(action) { | ||
| return typeof action === "object" && action !== null && "label" in action; | ||
| } | ||
@@ -248,12 +345,99 @@ | ||
| var VISIBLE_TOASTS_AMOUNT = 3; | ||
| var VIEWPORT_OFFSET = "32px"; | ||
| var VIEWPORT_OFFSET = "24px"; | ||
| var MOBILE_VIEWPORT_OFFSET = "16px"; | ||
| var TOAST_LIFETIME = 4e3; | ||
| var TOAST_WIDTH = 356; | ||
| var GAP = 14; | ||
| var SWIPE_TRESHOLD = 20; | ||
| var SWIPE_THRESHOLD = 45; | ||
| var TIME_BEFORE_UNMOUNT = 200; | ||
| function _cn(...classes) { | ||
| function cn(...classes) { | ||
| return classes.filter(Boolean).join(" "); | ||
| } | ||
| var Toast = (props) => { | ||
| function resolveContent(content) { | ||
| return typeof content === "function" ? content() : content; | ||
| } | ||
| function getDefaultSwipeDirections(position) { | ||
| const [y, x] = position.split("-"); | ||
| const directions = []; | ||
| if (y) | ||
| directions.push(y); | ||
| if (x) | ||
| directions.push(x); | ||
| return directions; | ||
| } | ||
| function getDocumentDirection() { | ||
| if (typeof window === "undefined" || typeof document === "undefined") | ||
| return "ltr"; | ||
| const dirAttribute = document.documentElement.getAttribute("dir"); | ||
| if (dirAttribute === "auto" || !dirAttribute) | ||
| return window.getComputedStyle(document.documentElement).direction; | ||
| return dirAttribute; | ||
| } | ||
| function assignOffset(defaultOffset, mobileOffset) { | ||
| const styles = {}; | ||
| [defaultOffset, mobileOffset].forEach((offset, index) => { | ||
| const isMobile = index === 1; | ||
| const prefix = isMobile ? "--mobile-offset" : "--offset"; | ||
| const defaultValue = isMobile ? MOBILE_VIEWPORT_OFFSET : VIEWPORT_OFFSET; | ||
| const assignAll = (value) => { | ||
| for (const key of ["top", "right", "bottom", "left"]) | ||
| styles[`${prefix}-${key}`] = typeof value === "number" ? `${value}px` : value; | ||
| }; | ||
| if (typeof offset === "number" || typeof offset === "string") { | ||
| assignAll(offset); | ||
| return; | ||
| } | ||
| if (typeof offset === "object" && offset !== null) { | ||
| for (const key of ["top", "right", "bottom", "left"]) { | ||
| const value = offset[key]; | ||
| styles[`${prefix}-${key}`] = value === void 0 ? defaultValue : typeof value === "number" ? `${value}px` : value; | ||
| } | ||
| return; | ||
| } | ||
| assignAll(defaultValue); | ||
| }); | ||
| return styles; | ||
| } | ||
| function mergeClassNames(classNames, legacy) { | ||
| return classNames ?? legacy; | ||
| } | ||
| function mergeDescriptionClassName(descriptionClassName, legacy) { | ||
| return descriptionClassName ?? legacy ?? ""; | ||
| } | ||
| function mergeClassName(className, legacy) { | ||
| return className ?? legacy ?? ""; | ||
| } | ||
| function useSonner() { | ||
| const [activeToasts, setActiveToasts] = createSignal2(toast.getToasts()); | ||
| onMount2(() => { | ||
| const unsubscribe = ToastState.subscribe((toastItem) => { | ||
| if (toastItem.dismiss) { | ||
| requestAnimationFrame(() => { | ||
| setActiveToasts((toasts) => toasts.filter((t) => t.id !== toastItem.id)); | ||
| }); | ||
| return; | ||
| } | ||
| const nextToast = toastItem; | ||
| setActiveToasts((toasts) => { | ||
| const indexOfExistingToast = toasts.findIndex((t) => t.id === nextToast.id); | ||
| if (indexOfExistingToast !== -1) { | ||
| return [ | ||
| ...toasts.slice(0, indexOfExistingToast), | ||
| { ...toasts[indexOfExistingToast], ...nextToast }, | ||
| ...toasts.slice(indexOfExistingToast + 1) | ||
| ]; | ||
| } | ||
| return [nextToast, ...toasts]; | ||
| }); | ||
| }); | ||
| onCleanup2(unsubscribe); | ||
| }); | ||
| return { | ||
| toasts: activeToasts | ||
| }; | ||
| } | ||
| function createToastTheme(theme) { | ||
| return theme !== "system" ? theme : typeof window !== "undefined" && window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light"; | ||
| } | ||
| function Toast(props) { | ||
| const [mounted, setMounted] = createSignal2(false); | ||
@@ -263,20 +447,32 @@ const [removed, setRemoved] = createSignal2(false); | ||
| const [swipeOut, setSwipeOut] = createSignal2(false); | ||
| const [isSwiped, setIsSwiped] = createSignal2(false); | ||
| const [swipeDirection, setSwipeDirection] = createSignal2(null); | ||
| const [swipeOutDirection, setSwipeOutDirection] = createSignal2(null); | ||
| const [offsetBeforeRemove, setOffsetBeforeRemove] = createSignal2(0); | ||
| const [initialHeight, setInitialHeight] = createSignal2(0); | ||
| let toastRef; | ||
| let dragStartTime = null; | ||
| let closeTimerStartTimeRef = 0; | ||
| let lastCloseTimerStartTimeRef = 0; | ||
| let remainingTime = TOAST_LIFETIME; | ||
| const [pointerStartRef, setPointerStartRef] = createSignal2(null); | ||
| const isFront = () => props.index === 0; | ||
| const isVisible = () => props.index + 1 <= props.visibleToasts; | ||
| const toastType = () => props.toast.type; | ||
| const toastClassname = () => props.toast.class || ""; | ||
| const toastDescriptionClassname = () => props.toast.descriptionClass || ""; | ||
| const propsWithDefaults = mergeProps({ | ||
| gap: GAP | ||
| }, props); | ||
| const heightIndex = () => props.heights.findIndex((height) => height.toastId === props.toast.id) || 0; | ||
| const duration = () => props.toast.duration || props.duration || TOAST_LIFETIME; | ||
| let closeTimerStartTimeRef = 0; | ||
| let lastCloseTimerStartTimeRef = 0; | ||
| const [pointerStartRef, setPointerStartRef] = createSignal2(null); | ||
| const coords = () => props.position.split("-"); | ||
| const toastsHeightBefore = () => { | ||
| const dismissible = () => props.toast.dismissible !== false; | ||
| const classNames = () => mergeClassNames(props.classNames, props.classes); | ||
| const className = () => mergeClassName(props.className, props.class); | ||
| const descriptionClassName = () => mergeDescriptionClassName(props.descriptionClassName, props.descriptionClass); | ||
| const toastClassName = () => mergeClassName(props.toast.className, props.toast.class); | ||
| const toastDescriptionClassName = () => mergeDescriptionClassName(props.toast.descriptionClassName, props.toast.descriptionClass); | ||
| const toastClassNames = () => mergeClassNames(props.toast.classNames, props.toast.classes); | ||
| const closeButton = () => props.toast.closeButton ?? props.closeButton; | ||
| const duration = () => props.toast.duration ?? props.duration ?? TOAST_LIFETIME; | ||
| const invert = () => props.toast.invert ?? props.invert; | ||
| const disabled = () => toastType() === "loading"; | ||
| const swipeDirections = () => props.swipeDirections ?? getDefaultSwipeDirections(props.position); | ||
| const y = createMemo(() => props.position.split("-")[0]); | ||
| const x = createMemo(() => props.position.split("-")[1]); | ||
| const heightIndex = createMemo(() => Math.max(0, props.heights.findIndex((height) => height.toastId === props.toast.id))); | ||
| const toastsHeightBefore = createMemo(() => { | ||
| return props.heights.reduce((prev, curr, reducerIndex) => { | ||
@@ -287,56 +483,58 @@ if (reducerIndex >= heightIndex()) | ||
| }, 0); | ||
| }; | ||
| }); | ||
| const offset = createMemo(() => heightIndex() * (props.gap ?? GAP) + toastsHeightBefore()); | ||
| const isDocumentHidden = useIsDocumentHidden(); | ||
| const invert = () => props.toast.invert || props.invert; | ||
| const disabled = () => toastType() === "loading"; | ||
| const offset = () => heightIndex() * propsWithDefaults.gap + toastsHeightBefore(); | ||
| createEffect(() => { | ||
| remainingTime = duration(); | ||
| }); | ||
| function deleteToast() { | ||
| setRemoved(true); | ||
| setOffsetBeforeRemove(offset()); | ||
| props.setHeights((heights) => heights.filter((height) => height.toastId !== props.toast.id)); | ||
| window.setTimeout(() => { | ||
| props.removeToast(props.toast); | ||
| }, TIME_BEFORE_UNMOUNT); | ||
| } | ||
| function getLoadingIcon() { | ||
| if (props.icons?.loading) { | ||
| return <div class="sonner-loader" data-visible={toastType() === "loading"}>{props.icons.loading}</div>; | ||
| return <div class={cn(classNames()?.loader, toastClassNames()?.loader, "sonner-loader")} data-visible={toastType() === "loading"}> | ||
| {props.icons.loading} | ||
| </div>; | ||
| } | ||
| return <Loader visible={toastType() === "loading"} />; | ||
| return <Loader class={cn(classNames()?.loader, toastClassNames()?.loader)} visible={toastType() === "loading"} />; | ||
| } | ||
| const icon = () => props.toast.icon ?? props.icons?.[toastType()] ?? getAsset(toastType()); | ||
| onMount2(() => { | ||
| setMounted(true); | ||
| if (!toastRef) | ||
| return; | ||
| const height = toastRef.getBoundingClientRect().height; | ||
| setInitialHeight(height); | ||
| props.setHeights((h) => [{ toastId: props.toast.id, toasterId: props.toast.toasterId, height, position: props.toast.position }, ...h]); | ||
| onCleanup2(() => { | ||
| props.setHeights((h) => h.filter((height2) => height2.toastId !== props.toast.id)); | ||
| }); | ||
| }); | ||
| onMount2(() => { | ||
| const toastNode = toastRef; | ||
| const originalHeight = toastNode.style.height; | ||
| toastNode.style.height = "auto"; | ||
| const newHeight = toastNode.getBoundingClientRect().height; | ||
| toastNode.style.height = originalHeight; | ||
| setInitialHeight(newHeight); | ||
| createEffect(() => { | ||
| props.setHeights((heights) => { | ||
| const alreadyExists = heights.find((height) => height.toastId === props.toast.id); | ||
| if (!alreadyExists) | ||
| return [{ toastId: props.toast.id, height: newHeight, position: props.toast.position }, ...heights]; | ||
| else | ||
| return heights.map((height) => height.toastId === props.toast.id ? { ...height, height: newHeight } : height); | ||
| }); | ||
| createEffect(() => { | ||
| if (!mounted() || !toastRef) | ||
| return; | ||
| resolveContent(props.toast.title); | ||
| resolveContent(props.toast.description); | ||
| const originalHeight = toastRef.style.height; | ||
| toastRef.style.height = "auto"; | ||
| const nextHeight = toastRef.getBoundingClientRect().height; | ||
| toastRef.style.height = originalHeight; | ||
| setInitialHeight(nextHeight); | ||
| props.setHeights((heights) => { | ||
| const alreadyExists = heights.find((height) => height.toastId === props.toast.id); | ||
| if (!alreadyExists) | ||
| return [{ toastId: props.toast.id, toasterId: props.toast.toasterId, height: nextHeight, position: props.toast.position }, ...heights]; | ||
| return heights.map((height) => height.toastId === props.toast.id ? { ...height, height: nextHeight } : height); | ||
| }); | ||
| }); | ||
| const deleteToast = () => { | ||
| setRemoved(true); | ||
| setOffsetBeforeRemove(offset()); | ||
| props.setHeights((h) => h.filter((height) => height.toastId !== props.toast.id)); | ||
| setTimeout(() => { | ||
| props.removeToast(props.toast); | ||
| }, TIME_BEFORE_UNMOUNT); | ||
| }; | ||
| let remainingTime = duration(); | ||
| createEffect( | ||
| on( | ||
| () => [ | ||
| props.expanded, | ||
| props.interacting, | ||
| props.toast, | ||
| duration(), | ||
| props.toast.promise, | ||
| toastType(), | ||
| props.pauseWhenPageIsHidden, | ||
| isDocumentHidden() | ||
| ], | ||
| ([expanded, interacting, toast2, duration2, promise, toastType2, pauseWhenPageIsHidden, isDocumentHidden2]) => { | ||
| if (promise && toastType2 === "loading" || duration2 === Number.POSITIVE_INFINITY) | ||
| () => [props.expanded, props.interacting, props.toast, toastType(), isDocumentHidden(), duration()], | ||
| ([expanded, interacting, currentToast, currentType]) => { | ||
| if (currentToast.promise && currentType === "loading" || currentToast.duration === Number.POSITIVE_INFINITY || currentType === "loading") | ||
| return; | ||
@@ -347,3 +545,3 @@ let timeoutId; | ||
| const elapsedTime = (/* @__PURE__ */ new Date()).getTime() - closeTimerStartTimeRef; | ||
| remainingTime = remainingTime - elapsedTime; | ||
| remainingTime -= elapsedTime; | ||
| } | ||
@@ -353,9 +551,12 @@ lastCloseTimerStartTimeRef = (/* @__PURE__ */ new Date()).getTime(); | ||
| const startTimer = () => { | ||
| if (remainingTime === Number.POSITIVE_INFINITY) | ||
| return; | ||
| closeTimerStartTimeRef = (/* @__PURE__ */ new Date()).getTime(); | ||
| timeoutId = setTimeout(() => { | ||
| toast2.onAutoClose?.(toast2); | ||
| currentToast.onAutoClose?.(currentToast); | ||
| deleteToast(); | ||
| }, remainingTime); | ||
| }; | ||
| if (expanded || interacting || pauseWhenPageIsHidden && isDocumentHidden2) | ||
| const shouldPause = expanded || interacting || (props.pauseWhenPageIsHidden ?? true) && isDocumentHidden(); | ||
| if (shouldPause) | ||
| pauseTimer(); | ||
@@ -365,3 +566,4 @@ else | ||
| onCleanup2(() => { | ||
| clearTimeout(timeoutId); | ||
| if (timeoutId) | ||
| clearTimeout(timeoutId); | ||
| }); | ||
@@ -373,22 +575,8 @@ } | ||
| on( | ||
| () => props.toast.id, | ||
| (toastId) => { | ||
| const toastNode = toastRef; | ||
| if (toastNode) { | ||
| const height = toastNode.getBoundingClientRect().height; | ||
| setInitialHeight(height); | ||
| props.setHeights((h) => [{ toastId, height, position: props.toast.position }, ...h]); | ||
| onCleanup2(() => { | ||
| props.setHeights((h) => h.filter((height2) => height2.toastId !== toastId)); | ||
| }); | ||
| } | ||
| } | ||
| ) | ||
| ); | ||
| createEffect( | ||
| on( | ||
| () => props.toast.delete, | ||
| (d) => { | ||
| if (d) | ||
| (shouldDelete) => { | ||
| if (shouldDelete) { | ||
| deleteToast(); | ||
| props.toast.onDismiss?.(props.toast); | ||
| } | ||
| } | ||
@@ -398,31 +586,33 @@ ) | ||
| return <li | ||
| aria-live={props.toast.important ? "assertive" : "polite"} | ||
| aria-atomic="true" | ||
| role="status" | ||
| tabIndex={0} | ||
| ref={toastRef} | ||
| class={_cn( | ||
| props.class, | ||
| toastClassname(), | ||
| props.classes?.toast, | ||
| props.toast?.classes?.toast, | ||
| props.classes?.default, | ||
| props.classes?.[toastType()], | ||
| props.toast?.classes?.[toastType()] | ||
| class={cn( | ||
| className(), | ||
| toastClassName(), | ||
| classNames()?.toast, | ||
| toastClassNames()?.toast, | ||
| classNames()?.default, | ||
| classNames()?.[toastType()], | ||
| toastClassNames()?.[toastType()] | ||
| )} | ||
| data-sonner-toast="" | ||
| data-rich-colors={props.toast.richColors ?? props.defaultRichColors} | ||
| data-styled={!(props.toast.jsx || props.toast.unstyled || props.unstyled)} | ||
| data-mounted={mounted()} | ||
| data-promise={Boolean(props.toast.promise)} | ||
| data-swiped={isSwiped()} | ||
| data-removed={removed()} | ||
| data-visible={isVisible()} | ||
| data-y-position={coords()[0]} | ||
| data-x-position={coords()[1]} | ||
| data-y-position={y()} | ||
| data-x-position={x()} | ||
| data-index={props.index} | ||
| data-front={isFront()} | ||
| data-swiping={swiping()} | ||
| data-dismissible={dismissible()} | ||
| data-type={toastType()} | ||
| data-invert={invert()} | ||
| data-swipe-out={swipeOut()} | ||
| data-swipe-direction={swipeOutDirection()} | ||
| data-expanded={Boolean(props.expanded || props.expandByDefault && mounted())} | ||
| data-testid={props.toast.testId} | ||
| style={{ | ||
@@ -437,7 +627,15 @@ "--index": props.index, | ||
| }} | ||
| onDragEnd={() => { | ||
| setSwiping(false); | ||
| setSwipeDirection(null); | ||
| setPointerStartRef(null); | ||
| }} | ||
| onPointerDown={(event) => { | ||
| if (disabled()) | ||
| if (event.button === 2) | ||
| return; | ||
| if (disabled() || !dismissible()) | ||
| return; | ||
| dragStartTime = /* @__PURE__ */ new Date(); | ||
| setOffsetBeforeRemove(offset()); | ||
| event.target.setPointerCapture(event.pointerId); | ||
| event.currentTarget.setPointerCapture(event.pointerId); | ||
| if (event.target.tagName === "BUTTON") | ||
@@ -449,9 +647,17 @@ return; | ||
| onPointerUp={() => { | ||
| if (swipeOut()) | ||
| if (swipeOut() || !dismissible()) | ||
| return; | ||
| setPointerStartRef(null); | ||
| const swipeAmount = Number(toastRef?.style.getPropertyValue("--swipe-amount").replace("px", "") || 0); | ||
| if (Math.abs(swipeAmount) >= SWIPE_TRESHOLD) { | ||
| const swipeAmountX = Number(toastRef?.style.getPropertyValue("--swipe-amount-x").replace("px", "") || 0); | ||
| const swipeAmountY = Number(toastRef?.style.getPropertyValue("--swipe-amount-y").replace("px", "") || 0); | ||
| const timeTaken = Math.max(1, (/* @__PURE__ */ new Date()).getTime() - (dragStartTime?.getTime() ?? 0)); | ||
| const swipeAmount = swipeDirection() === "x" ? swipeAmountX : swipeAmountY; | ||
| const velocity = Math.abs(swipeAmount) / timeTaken; | ||
| if (Math.abs(swipeAmount) >= SWIPE_THRESHOLD || velocity > 0.11) { | ||
| setOffsetBeforeRemove(offset()); | ||
| props.toast.onDismiss?.(props.toast); | ||
| if (swipeDirection() === "x") | ||
| setSwipeOutDirection(swipeAmountX > 0 ? "right" : "left"); | ||
| else | ||
| setSwipeOutDirection(swipeAmountY > 0 ? "down" : "up"); | ||
| deleteToast(); | ||
@@ -461,83 +667,128 @@ setSwipeOut(true); | ||
| } | ||
| toastRef?.style.setProperty("--swipe-amount", "0px"); | ||
| toastRef?.style.setProperty("--swipe-amount-x", "0px"); | ||
| toastRef?.style.setProperty("--swipe-amount-y", "0px"); | ||
| setIsSwiped(false); | ||
| setSwiping(false); | ||
| setSwipeDirection(null); | ||
| }} | ||
| onPointerMove={(event) => { | ||
| if (!pointerStartRef()) | ||
| if (!pointerStartRef() || !dismissible()) | ||
| return; | ||
| const yPosition = event.clientY - pointerStartRef().y; | ||
| const xPosition = event.clientX - pointerStartRef().x; | ||
| const clamp = coords()[0] === "top" ? Math.min : Math.max; | ||
| const clampedY = clamp(0, yPosition); | ||
| const swipeStartThreshold = event.pointerType === "touch" ? 10 : 2; | ||
| const isAllowedToSwipe = Math.abs(clampedY) > swipeStartThreshold; | ||
| if (isAllowedToSwipe) { | ||
| toastRef?.style.setProperty("--swipe-amount", `${yPosition}px`); | ||
| } else if (Math.abs(xPosition) > swipeStartThreshold) { | ||
| setPointerStartRef(null); | ||
| if ((window.getSelection()?.toString().length ?? 0) > 0) | ||
| return; | ||
| const yDelta = event.clientY - pointerStartRef().y; | ||
| const xDelta = event.clientX - pointerStartRef().x; | ||
| if (!swipeDirection() && (Math.abs(xDelta) > 1 || Math.abs(yDelta) > 1)) | ||
| setSwipeDirection(Math.abs(xDelta) > Math.abs(yDelta) ? "x" : "y"); | ||
| const swipeAmount = { x: 0, y: 0 }; | ||
| const getDampening = (delta) => { | ||
| const factor = Math.abs(delta) / 20; | ||
| return 1 / (1.5 + factor); | ||
| }; | ||
| if (swipeDirection() === "y") { | ||
| if (swipeDirections().includes("top") || swipeDirections().includes("bottom")) { | ||
| if (swipeDirections().includes("top") && yDelta < 0 || swipeDirections().includes("bottom") && yDelta > 0) | ||
| swipeAmount.y = yDelta; | ||
| else | ||
| swipeAmount.y = yDelta * getDampening(yDelta); | ||
| } | ||
| } else if (swipeDirection() === "x") { | ||
| if (swipeDirections().includes("left") || swipeDirections().includes("right")) { | ||
| if (swipeDirections().includes("left") && xDelta < 0 || swipeDirections().includes("right") && xDelta > 0) | ||
| swipeAmount.x = xDelta; | ||
| else | ||
| swipeAmount.x = xDelta * getDampening(xDelta); | ||
| } | ||
| } | ||
| if (Math.abs(swipeAmount.x) > 0 || Math.abs(swipeAmount.y) > 0) | ||
| setIsSwiped(true); | ||
| toastRef?.style.setProperty("--swipe-amount-x", `${swipeAmount.x}px`); | ||
| toastRef?.style.setProperty("--swipe-amount-y", `${swipeAmount.y}px`); | ||
| }} | ||
| ><Show when={props.closeButton && !props.toast.jsx}><button | ||
| aria-label="Close toast" | ||
| > | ||
| <Show when={closeButton() && !props.toast.jsx && toastType() !== "loading"}> | ||
| <button | ||
| aria-label={props.closeButtonAriaLabel ?? "Close toast"} | ||
| data-disabled={disabled()} | ||
| data-close-button | ||
| onClick={disabled() ? void 0 : () => { | ||
| onClick={() => { | ||
| if (disabled() || !dismissible()) | ||
| return; | ||
| deleteToast(); | ||
| props.toast.onDismiss?.(props.toast); | ||
| }} | ||
| class={_cn(props.classes?.closeButton, props.toast?.classes?.closeButton)} | ||
| ><svg | ||
| xmlns="http://www.w3.org/2000/svg" | ||
| width="12" | ||
| height="12" | ||
| viewBox="0 0 24 24" | ||
| fill="none" | ||
| stroke="currentColor" | ||
| stroke-width="1.5" | ||
| stroke-linecap="round" | ||
| stroke-linejoin="round" | ||
| ><line x1="18" y1="6" x2="6" y2="18" /><line x1="6" y1="6" x2="18" y2="18" /></svg></button></Show><Show | ||
| when={props.toast.jsx || props.toast.title instanceof Element} | ||
| fallback={<><Show when={toastType() || props.toast.icon || props.toast.promise}><div data-icon="">{props.toast.promise || props.toast.type === "loading" && !props.toast.icon ? props.toast.icon || getLoadingIcon() : null}{props.toast.type !== "loading" ? props.toast.icon || props.icons?.[toastType()] || getAsset(toastType())() : null}</div></Show><div data-content=""><div data-title="" class={_cn(props.classes?.title, props.toast?.classes?.title)}>{props.toast.title}</div><Show when={props.toast.description}><div | ||
| data-description="" | ||
| class={_cn( | ||
| props.descriptionClass, | ||
| toastDescriptionClassname(), | ||
| props.classes?.description, | ||
| props.toast?.classes?.description | ||
| )} | ||
| >{props.toast.description}</div></Show></div><Show when={props.toast.cancel}><button | ||
| data-button | ||
| data-cancel | ||
| style={props.toast.cancelButtonStyle || props.cancelButtonStyle} | ||
| onClick={() => { | ||
| deleteToast(); | ||
| if (props.toast.cancel?.onClick) | ||
| props.toast.cancel.onClick(); | ||
| }} | ||
| class={_cn(props.classes?.cancelButton, props.toast?.classes?.cancelButton)} | ||
| >{props.toast.cancel.label}</button></Show><Show when={props.toast.action}><button | ||
| data-button="" | ||
| style={props.toast.actionButtonStyle || props.actionButtonStyle} | ||
| onClick={(event) => { | ||
| props.toast.action?.onClick(event); | ||
| if (event.defaultPrevented) | ||
| return; | ||
| deleteToast(); | ||
| }} | ||
| class={_cn(props.classes?.actionButton, props.toast?.classes?.actionButton)} | ||
| >{props.toast.action.label}</button></Show></>} | ||
| >{props.toast.jsx || props.toast.title}</Show></li>; | ||
| }; | ||
| function getDocumentDirection() { | ||
| if (typeof window === "undefined") | ||
| return "ltr"; | ||
| if (typeof document === "undefined") | ||
| return "ltr"; | ||
| const dirAttribute = document.documentElement.getAttribute("dir"); | ||
| if (dirAttribute === "auto" || !dirAttribute) | ||
| return window.getComputedStyle(document.documentElement).direction; | ||
| return dirAttribute; | ||
| class={cn(classNames()?.closeButton, toastClassNames()?.closeButton)} | ||
| > | ||
| {props.icons?.close ?? <CloseIcon />} | ||
| </button> | ||
| </Show> | ||
| <Show when={(toastType() || props.toast.icon || props.toast.promise) && props.toast.icon !== null && (props.icons?.[toastType()] !== null || props.toast.icon)}> | ||
| <div data-icon="" class={cn(classNames()?.icon, toastClassNames()?.icon)}> | ||
| {props.toast.promise || props.toast.type === "loading" && !props.toast.icon ? props.toast.icon || getLoadingIcon() : null} | ||
| {props.toast.type !== "loading" ? icon() : null} | ||
| </div> | ||
| </Show> | ||
| <div data-content="" class={cn(classNames()?.content, toastClassNames()?.content)}> | ||
| <div data-title="" class={cn(classNames()?.title, toastClassNames()?.title)}> | ||
| {props.toast.jsx ? props.toast.jsx : resolveContent(props.toast.title)} | ||
| </div> | ||
| <Show when={props.toast.description}> | ||
| <div | ||
| data-description="" | ||
| class={cn( | ||
| descriptionClassName(), | ||
| toastDescriptionClassName(), | ||
| classNames()?.description, | ||
| toastClassNames()?.description | ||
| )} | ||
| > | ||
| {resolveContent(props.toast.description)} | ||
| </div> | ||
| </Show> | ||
| </div> | ||
| <Show when={props.toast.cancel}> | ||
| {(cancel) => <Show when={!isAction(cancel())} fallback={<button | ||
| data-button | ||
| data-cancel | ||
| style={props.toast.cancelButtonStyle ?? props.cancelButtonStyle} | ||
| onClick={(event) => { | ||
| const currentCancel = cancel(); | ||
| if (!dismissible()) | ||
| return; | ||
| currentCancel.onClick?.(event); | ||
| deleteToast(); | ||
| }} | ||
| class={cn(classNames()?.cancelButton, toastClassNames()?.cancelButton)} | ||
| > | ||
| {cancel().label} | ||
| </button>}> | ||
| {cancel()} | ||
| </Show>} | ||
| </Show> | ||
| <Show when={props.toast.action}> | ||
| {(action) => <Show when={!isAction(action())} fallback={<button | ||
| data-button | ||
| data-action | ||
| style={props.toast.actionButtonStyle ?? props.actionButtonStyle} | ||
| onClick={(event) => { | ||
| const currentAction = action(); | ||
| currentAction.onClick?.(event); | ||
| if (event.defaultPrevented) | ||
| return; | ||
| deleteToast(); | ||
| }} | ||
| class={cn(classNames()?.actionButton, toastClassNames()?.actionButton)} | ||
| > | ||
| {action().label} | ||
| </button>}> | ||
| {action()} | ||
| </Show>} | ||
| </Show> | ||
| </li>; | ||
| } | ||
| var Toaster = (props) => { | ||
| function Toaster(props) { | ||
| const propsWithDefaults = mergeProps({ | ||
@@ -547,67 +798,86 @@ position: "bottom-right", | ||
| theme: "light", | ||
| gap: GAP, | ||
| visibleToasts: VISIBLE_TOASTS_AMOUNT, | ||
| dir: getDocumentDirection() | ||
| dir: getDocumentDirection(), | ||
| containerAriaLabel: "Notifications" | ||
| }, props); | ||
| const initialTheme = createToastTheme(propsWithDefaults.theme); | ||
| const [toastsStore, setToastsStore] = createStore({ toasts: [] }); | ||
| const possiblePositions = () => { | ||
| return Array.from( | ||
| new Set([propsWithDefaults.position].concat(toastsStore.toasts.filter((toast2) => toast2.position).map((toast2) => toast2.position))) | ||
| ); | ||
| }; | ||
| const filteredToasts = createMemo(() => { | ||
| const toasts = toastsStore.toasts; | ||
| if (propsWithDefaults.id) | ||
| return toasts.filter((toast2) => toast2.toasterId === propsWithDefaults.id); | ||
| return toasts.filter((toast2) => !toast2.toasterId); | ||
| }); | ||
| const possiblePositions = createMemo(() => { | ||
| return Array.from(/* @__PURE__ */ new Set([propsWithDefaults.position, ...filteredToasts().filter((toast2) => toast2.position).map((toast2) => toast2.position)])); | ||
| }); | ||
| const [heights, setHeights] = createSignal2([]); | ||
| const [expanded, setExpanded] = createSignal2(false); | ||
| const [interacting, setInteracting] = createSignal2(false); | ||
| const [actualTheme, setActualTheme] = createSignal2(initialTheme); | ||
| const [lastFocusedElementRef, setLastFocusedElementRef] = createSignal2(null); | ||
| const [isFocusWithinRef, setIsFocusWithinRef] = createSignal2(false); | ||
| let listRef; | ||
| const hotkeyLabel = () => propsWithDefaults.hotkey.join("+").replace(/Key/g, "").replace(/Digit/g, ""); | ||
| const [lastFocusedElementRef, setLastFocusedElementRef] = createSignal2(null); | ||
| const [isFocusedWithinRef, setIsFocusedWithinRef] = createSignal2(false); | ||
| const [actualTheme, setActualTheme] = createSignal2( | ||
| propsWithDefaults.theme !== "system" ? propsWithDefaults.theme : typeof window !== "undefined" ? window.matchMedia && window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light" : "light" | ||
| ); | ||
| const removeToast = (toast2) => setToastsStore("toasts", (toasts) => toasts.filter(({ id }) => id !== toast2.id)); | ||
| const className = () => mergeClassName(propsWithDefaults.className, propsWithDefaults.class); | ||
| const toastOptions = () => propsWithDefaults.toastOptions; | ||
| const toastClassNames = () => mergeClassNames(toastOptions()?.classNames, toastOptions()?.classes); | ||
| const toastDescriptionClassName = () => mergeDescriptionClassName(toastOptions()?.descriptionClassName, toastOptions()?.descriptionClass); | ||
| const toastClassName = () => mergeClassName(toastOptions()?.className, toastOptions()?.class); | ||
| const removeToast = (toastToRemove) => { | ||
| setToastsStore("toasts", (toasts) => { | ||
| if (!toasts.find((toast2) => toast2.id === toastToRemove.id)?.delete) | ||
| ToastState.dismiss(toastToRemove.id); | ||
| return toasts.filter(({ id }) => id !== toastToRemove.id); | ||
| }); | ||
| }; | ||
| onMount2(() => { | ||
| const unsub = ToastState.subscribe((toast2) => { | ||
| if (toast2.dismiss) { | ||
| setToastsStore("toasts", produce((_toasts) => { | ||
| _toasts.forEach((t) => { | ||
| if (t.id === toast2.id) | ||
| t.delete = true; | ||
| }); | ||
| })); | ||
| const unsubscribe = ToastState.subscribe((toastItem) => { | ||
| if (toastItem.dismiss) { | ||
| requestAnimationFrame(() => { | ||
| setToastsStore("toasts", produce((toasts) => { | ||
| toasts.forEach((toast2) => { | ||
| if (toast2.id === toastItem.id) | ||
| toast2.delete = true; | ||
| }); | ||
| })); | ||
| }); | ||
| return; | ||
| } | ||
| const changedIndex = toastsStore.toasts.findIndex((t) => t.id === toast2.id); | ||
| if (changedIndex !== -1) { | ||
| setToastsStore("toasts", [changedIndex], reconcile(toast2)); | ||
| const nextToast = toastItem; | ||
| const indexOfExistingToast = toastsStore.toasts.findIndex((t) => t.id === nextToast.id); | ||
| if (indexOfExistingToast !== -1) { | ||
| setToastsStore("toasts", indexOfExistingToast, reconcile(nextToast)); | ||
| return; | ||
| } | ||
| setToastsStore("toasts", produce((_toasts) => { | ||
| _toasts.unshift(toast2); | ||
| setToastsStore("toasts", produce((toasts) => { | ||
| toasts.unshift(nextToast); | ||
| })); | ||
| }); | ||
| onCleanup2(() => { | ||
| unsub(); | ||
| }); | ||
| onCleanup2(unsubscribe); | ||
| }); | ||
| createEffect( | ||
| on( | ||
| () => propsWithDefaults.theme, | ||
| (theme) => { | ||
| if (theme !== "system") { | ||
| setActualTheme(theme); | ||
| return; | ||
| } | ||
| if (typeof window === "undefined") | ||
| return; | ||
| window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change", ({ matches }) => { | ||
| if (matches) | ||
| setActualTheme("dark"); | ||
| else | ||
| setActualTheme("light"); | ||
| }); | ||
| } | ||
| ) | ||
| ); | ||
| createEffect(() => { | ||
| if (toastsStore.toasts.length <= 1) | ||
| const theme = propsWithDefaults.theme; | ||
| if (theme !== "system") { | ||
| setActualTheme(theme); | ||
| return; | ||
| } | ||
| if (typeof window === "undefined") | ||
| return; | ||
| const darkMediaQuery = window.matchMedia("(prefers-color-scheme: dark)"); | ||
| const updateTheme = ({ matches }) => { | ||
| setActualTheme(matches ? "dark" : "light"); | ||
| }; | ||
| updateTheme(darkMediaQuery); | ||
| try { | ||
| darkMediaQuery.addEventListener("change", updateTheme); | ||
| onCleanup2(() => darkMediaQuery.removeEventListener("change", updateTheme)); | ||
| } catch { | ||
| darkMediaQuery.addListener(updateTheme); | ||
| onCleanup2(() => darkMediaQuery.removeListener(updateTheme)); | ||
| } | ||
| }); | ||
| createEffect(() => { | ||
| if (filteredToasts().length <= 1) | ||
| setExpanded(false); | ||
@@ -617,3 +887,3 @@ }); | ||
| const handleKeyDown = (event) => { | ||
| const isHotkeyPressed = propsWithDefaults.hotkey.every((key) => event[key] || event.code === key); | ||
| const isHotkeyPressed = propsWithDefaults.hotkey.length > 0 && propsWithDefaults.hotkey.every((key) => event[key] || event.code === key); | ||
| if (isHotkeyPressed) { | ||
@@ -627,46 +897,44 @@ setExpanded(true); | ||
| document.addEventListener("keydown", handleKeyDown); | ||
| onCleanup2(() => { | ||
| document.removeEventListener("keydown", handleKeyDown); | ||
| }); | ||
| onCleanup2(() => document.removeEventListener("keydown", handleKeyDown)); | ||
| }); | ||
| createEffect( | ||
| on( | ||
| () => listRef, | ||
| (ref) => { | ||
| if (ref) { | ||
| onCleanup2(() => { | ||
| if (lastFocusedElementRef()) { | ||
| lastFocusedElementRef()?.focus({ preventScroll: true }); | ||
| setLastFocusedElementRef(null); | ||
| setIsFocusedWithinRef(false); | ||
| } | ||
| }); | ||
| } | ||
| } | ||
| ) | ||
| ); | ||
| return <Show when={toastsStore.toasts.length > 0}>{ | ||
| /* Remove item from normal navigation flow, only available via hotkey */ | ||
| }<section aria-label={`Notifications ${hotkeyLabel()}`} tabIndex={-1}><For2 each={possiblePositions()}>{(position, index) => { | ||
| onCleanup2(() => { | ||
| if (lastFocusedElementRef()) { | ||
| lastFocusedElementRef()?.focus({ preventScroll: true }); | ||
| setLastFocusedElementRef(null); | ||
| setIsFocusWithinRef(false); | ||
| } | ||
| }); | ||
| return <section | ||
| aria-label={propsWithDefaults.customAriaLabel ?? `${propsWithDefaults.containerAriaLabel} ${hotkeyLabel()}`} | ||
| tabIndex={-1} | ||
| aria-live="polite" | ||
| aria-relevant="additions text" | ||
| aria-atomic="false" | ||
| data-react-aria-top-layer | ||
| > | ||
| <For2 each={possiblePositions()}> | ||
| {(position, index) => { | ||
| const [y, x] = position.split("-"); | ||
| return <ol | ||
| const toastsByPosition = createMemo(() => filteredToasts().filter((toast2) => !toast2.position && index() === 0 || toast2.position === position)); | ||
| const heightsByPosition = createMemo(() => heights().filter((height) => index() === 0 && !height.position || height.position === position)); | ||
| return <Show when={filteredToasts().length > 0}> | ||
| <ol | ||
| tabIndex={-1} | ||
| ref={listRef} | ||
| dir={propsWithDefaults.dir === "auto" ? getDocumentDirection() : propsWithDefaults.dir} | ||
| class={propsWithDefaults.class} | ||
| class={className()} | ||
| data-sonner-toaster | ||
| data-theme={actualTheme()} | ||
| data-rich-colors={propsWithDefaults.richColors} | ||
| data-sonner-theme={actualTheme()} | ||
| data-y-position={y} | ||
| data-x-position={x} | ||
| style={{ | ||
| "--front-toast-height": `${heights()[0]?.height}px`, | ||
| "--offset": typeof propsWithDefaults.offset === "number" ? `${propsWithDefaults.offset}px` : propsWithDefaults.offset || VIEWPORT_OFFSET, | ||
| "--front-toast-height": `${heightsByPosition()[0]?.height ?? 0}px`, | ||
| "--width": `${TOAST_WIDTH}px`, | ||
| "--gap": `${GAP}px`, | ||
| ...propsWithDefaults.style | ||
| "--gap": `${propsWithDefaults.gap}px`, | ||
| ...propsWithDefaults.style, | ||
| ...assignOffset(propsWithDefaults.offset, propsWithDefaults.mobileOffset) | ||
| }} | ||
| onBlur={(event) => { | ||
| if (isFocusedWithinRef() && !event.currentTarget.contains(event.relatedTarget)) { | ||
| setIsFocusedWithinRef(false); | ||
| if (isFocusWithinRef() && !event.currentTarget.contains(event.relatedTarget)) { | ||
| setIsFocusWithinRef(false); | ||
| if (lastFocusedElementRef()) { | ||
@@ -679,4 +947,7 @@ lastFocusedElementRef()?.focus({ preventScroll: true }); | ||
| onFocus={(event) => { | ||
| if (!isFocusedWithinRef()) { | ||
| setIsFocusedWithinRef(true); | ||
| const isNotDismissible = event.target instanceof HTMLElement && event.target.dataset.dismissible === "false"; | ||
| if (isNotDismissible) | ||
| return; | ||
| if (!isFocusWithinRef()) { | ||
| setIsFocusWithinRef(true); | ||
| setLastFocusedElementRef(event.relatedTarget); | ||
@@ -691,26 +962,34 @@ } | ||
| }} | ||
| onPointerDown={() => { | ||
| onDragEnd={() => setExpanded(false)} | ||
| onPointerDown={(event) => { | ||
| const isNotDismissible = event.target instanceof HTMLElement && event.target.dataset.dismissible === "false"; | ||
| if (isNotDismissible) | ||
| return; | ||
| setInteracting(true); | ||
| }} | ||
| onPointerUp={() => setInteracting(false)} | ||
| ><For2 each={toastsStore.toasts.filter((toast2) => !toast2.position && index() === 0 || toast2.position === position)}>{(toast2, index2) => <Toast | ||
| index={index2()} | ||
| > | ||
| <For2 each={toastsByPosition()}> | ||
| {(toastItem, toastIndex) => <Toast | ||
| icons={propsWithDefaults.icons} | ||
| toast={toast2} | ||
| duration={propsWithDefaults.toastOptions?.duration ?? props.duration} | ||
| class={propsWithDefaults.toastOptions?.class} | ||
| classes={propsWithDefaults.toastOptions?.classes} | ||
| cancelButtonStyle={propsWithDefaults.toastOptions?.cancelButtonStyle} | ||
| actionButtonStyle={propsWithDefaults.toastOptions?.actionButtonStyle} | ||
| descriptionClass={propsWithDefaults.toastOptions?.descriptionClass} | ||
| index={toastIndex()} | ||
| toast={toastItem} | ||
| defaultRichColors={propsWithDefaults.richColors} | ||
| duration={toastOptions()?.duration ?? propsWithDefaults.duration} | ||
| class={toastClassName()} | ||
| descriptionClassName={toastDescriptionClassName()} | ||
| invert={Boolean(propsWithDefaults.invert)} | ||
| visibleToasts={propsWithDefaults.visibleToasts} | ||
| closeButton={Boolean(propsWithDefaults.closeButton)} | ||
| closeButton={toastOptions()?.closeButton ?? propsWithDefaults.closeButton ?? false} | ||
| interacting={interacting()} | ||
| position={propsWithDefaults.position} | ||
| style={propsWithDefaults.toastOptions?.style} | ||
| unstyled={propsWithDefaults.toastOptions?.unstyled} | ||
| position={position} | ||
| style={toastOptions()?.style} | ||
| unstyled={toastOptions()?.unstyled} | ||
| classNames={toastClassNames()} | ||
| cancelButtonStyle={toastOptions()?.cancelButtonStyle} | ||
| actionButtonStyle={toastOptions()?.actionButtonStyle} | ||
| closeButtonAriaLabel={toastOptions()?.closeButtonAriaLabel} | ||
| removeToast={removeToast} | ||
| toasts={toastsStore.toasts} | ||
| heights={heights()} | ||
| toasts={toastsByPosition()} | ||
| heights={heightsByPosition()} | ||
| setHeights={setHeights} | ||
@@ -720,9 +999,16 @@ expandByDefault={Boolean(propsWithDefaults.expand)} | ||
| expanded={expanded()} | ||
| swipeDirections={propsWithDefaults.swipeDirections} | ||
| pauseWhenPageIsHidden={propsWithDefaults.pauseWhenPageIsHidden} | ||
| />}</For2></ol>; | ||
| }}</For2></section></Show>; | ||
| }; | ||
| />} | ||
| </For2> | ||
| </ol> | ||
| </Show>; | ||
| }} | ||
| </For2> | ||
| </section>; | ||
| } | ||
| export { | ||
| Toaster, | ||
| toast | ||
| toast, | ||
| useSonner | ||
| }; | ||
@@ -729,0 +1015,0 @@ /*! |
+25
-27
| { | ||
| "name": "solid-sonner", | ||
| "type": "module", | ||
| "version": "0.2.8", | ||
| "version": "0.3.0", | ||
| "private": false, | ||
| "packageManager": "pnpm@9.2.0", | ||
| "description": "An opinionated toast component for Solid.", | ||
@@ -46,14 +45,2 @@ "author": "Robert Soriano <sorianorobertc@gmail.com>", | ||
| "browser": {}, | ||
| "scripts": { | ||
| "dev": "vite serve dev", | ||
| "build": "tsup", | ||
| "dev:build": "vite build dev", | ||
| "test": "playwright test", | ||
| "prepublishOnly": "pnpm build", | ||
| "update-deps": "pnpm up -Li", | ||
| "typecheck": "tsc --noEmit", | ||
| "lint": "eslint .", | ||
| "release": "bumpp && npm publish", | ||
| "lint:fix": "eslint . --fix" | ||
| }, | ||
| "peerDependencies": { | ||
@@ -64,18 +51,18 @@ "solid-js": "^1.6.0" | ||
| "@antfu/eslint-config-ts": "^0.43.1", | ||
| "@playwright/test": "^1.44.1", | ||
| "@types/node": "^20.14.2", | ||
| "bumpp": "^9.4.1", | ||
| "@changesets/cli": "^2.30.0", | ||
| "@playwright/test": "^1.58.2", | ||
| "@types/node": "^20.19.37", | ||
| "copy-to-clipboard": "^3.3.3", | ||
| "esbuild": "^0.21.4", | ||
| "esbuild": "^0.21.5", | ||
| "esbuild-plugin-solid": "^0.6.0", | ||
| "eslint": "^8.56.0", | ||
| "eslint-plugin-solid": "^0.14.0", | ||
| "highlight.js": "^11.9.0", | ||
| "jsdom": "^24.1.0", | ||
| "solid-js": "^1.8.17", | ||
| "tsup": "^8.1.0", | ||
| "eslint-plugin-solid": "^0.14.5", | ||
| "highlight.js": "^11.11.1", | ||
| "jsdom": "^24.1.3", | ||
| "solid-js": "^1.9.11", | ||
| "tsup": "^8.5.1", | ||
| "tsup-preset-solid": "^2.2.0", | ||
| "typescript": "^5.4.5", | ||
| "vite": "^5.2.13", | ||
| "vite-plugin-solid": "^2.10.2" | ||
| "typescript": "^5.9.3", | ||
| "vite": "^5.4.21", | ||
| "vite-plugin-solid": "^2.11.10" | ||
| }, | ||
@@ -90,3 +77,14 @@ "eslintConfig": { | ||
| ] | ||
| }, | ||
| "scripts": { | ||
| "dev": "vite serve dev", | ||
| "build": "tsup", | ||
| "dev:build": "vite build dev", | ||
| "test": "playwright test", | ||
| "update-deps": "pnpm up -Li", | ||
| "typecheck": "tsc --noEmit", | ||
| "lint": "eslint .", | ||
| "release": "changeset publish", | ||
| "lint:fix": "eslint . --fix" | ||
| } | ||
| } | ||
| } |
+101
-252
@@ -11,8 +11,6 @@ <p> | ||
| Based on the React [implementation](https://sonner.emilkowal.ski/). | ||
| This package tracks the React [Sonner](https://sonner.emilkowal.ski/) API as closely as possible while keeping the implementation Solid-friendly. | ||
| ## Quick start | ||
| ## Install | ||
| Install it: | ||
| ```bash | ||
@@ -26,3 +24,3 @@ npm i solid-sonner | ||
| Add `<Toaster />` to your app, it will be the place where all your toasts will be rendered. After that you can use `toast()` from anywhere in your app. | ||
| ## Quick start | ||
@@ -32,5 +30,3 @@ ```tsx | ||
| // ... | ||
| function App() { | ||
| export default function App() { | ||
| return ( | ||
@@ -45,59 +41,28 @@ <div> | ||
| ## Types | ||
| ## API | ||
| ### Default | ||
| Exports: | ||
| Most basic toast. You can customize it (and any other type) by passing an options object as the second argument. | ||
| - `Toaster` | ||
| - `toast` | ||
| - `useSonner` | ||
| - types: `Action`, `ExternalToast`, `ToastClassnames`, `ToastT`, `ToastToDismiss`, `ToasterProps` | ||
| ```jsx | ||
| ### Toast types | ||
| ```tsx | ||
| toast('Event has been created') | ||
| ``` | ||
| With custom icon and description: | ||
| ```jsx | ||
| toast('Event has been created', { | ||
| description: 'Monday, January 3rd at 6:00pm', | ||
| icon: <MyIcon />, | ||
| }) | ||
| ``` | ||
| ### Success | ||
| Renders a checkmark icon in front of the message. | ||
| ```jsx | ||
| toast.success('Event has been created') | ||
| ``` | ||
| ### Info | ||
| Renders an error icon in front of the message. | ||
| ```jsx | ||
| toast.info('Event has new information') | ||
| ``` | ||
| ### Warning | ||
| Renders an error icon in front of the message. | ||
| ```jsx | ||
| toast.warning('Event has warning') | ||
| ``` | ||
| ### Error | ||
| Renders an error icon in front of the message. | ||
| ```jsx | ||
| toast.error('Event has not been created') | ||
| toast.loading('Loading data') | ||
| ``` | ||
| ### Action | ||
| With description, icon, and actions: | ||
| Renders a button. | ||
| ```jsx | ||
| ```tsx | ||
| toast('Event has been created', { | ||
| description: 'Monday, January 3rd at 6:00pm', | ||
| icon: <MyIcon />, | ||
| action: { | ||
@@ -107,13 +72,14 @@ label: 'Undo', | ||
| }, | ||
| cancel: { | ||
| label: 'Cancel', | ||
| }, | ||
| }) | ||
| ``` | ||
| ### Promise | ||
| ### Promise toasts | ||
| Starts in a loading state and will update automatically after the promise resolves or fails. | ||
| ```jsx | ||
| toast.promise(() => new Promise(resolve => setTimeout(resolve, 2000)), { | ||
| loading: 'Loading', | ||
| success: 'Success', | ||
| ```tsx | ||
| toast.promise(fetchData(), { | ||
| loading: 'Loading...', | ||
| success: data => `${data.name} has been added!`, | ||
| error: 'Error', | ||
@@ -123,52 +89,34 @@ }) | ||
| You can pass a function to the success/error messages to incorporate the result/error of the promise. | ||
| Extended results are supported too: | ||
| ```jsx | ||
| toast.promise(promise, { | ||
| loading: 'Loading...', | ||
| success: (data) => { | ||
| return `${data.name} has been added!` | ||
| }, | ||
| error: 'Error', | ||
| ```tsx | ||
| toast.promise(saveProject(), { | ||
| loading: 'Saving...', | ||
| success: result => ({ | ||
| message: 'Project saved', | ||
| description: result.id, | ||
| }), | ||
| error: error => ({ | ||
| message: 'Save failed', | ||
| description: String(error), | ||
| }), | ||
| }) | ||
| ``` | ||
| ### Loading | ||
| ### Updating and dismissing | ||
| Renders a toast with a loading spinner. Useful when you want to handle various states yourself instead of using a promise toast. | ||
| ```tsx | ||
| const id = toast('Uploading...', { duration: Number.POSITIVE_INFINITY }) | ||
| ```jsx | ||
| toast.loading('Loading data') | ||
| toast.success('Done', { id }) | ||
| toast.dismiss(id) | ||
| toast.dismiss() | ||
| ``` | ||
| ### Custom JSX | ||
| ### Headless custom toasts | ||
| You can pass jsx as the first argument instead of a string to render custom jsx while maintaining default styling. You can use the headless version below for a custom, unstyled toast. | ||
| ```jsx | ||
| toast(<div>A custom toast with default styling</div>) | ||
| ``` | ||
| ### Updating a toast | ||
| You can update a toast by using the `toast` function and passing it the id of the toast you want to update, the rest stays the same. | ||
| ```jsx | ||
| const toastId = toast('Sonner') | ||
| toast.success('Toast has been updated', { | ||
| id: toastId, | ||
| }) | ||
| ``` | ||
| ## Customization | ||
| ### Headless | ||
| You can use `toast.custom` to render an unstyled toast with custom jsx while maintaining the functionality. | ||
| ```jsx | ||
| toast.custom(t => ( | ||
| ```tsx | ||
| toast.custom(id => ( | ||
| <div> | ||
| This is a custom component <button onClick={() => toast.dismiss(t)}>close</button> | ||
| Custom toast <button onClick={() => toast.dismiss(id)}>close</button> | ||
| </div> | ||
@@ -178,66 +126,37 @@ )) | ||
| ### Theme | ||
| ### Read current state | ||
| You can change the theme using the `theme` prop. Default theme is light. | ||
| ```tsx | ||
| const { toasts } = useSonner() | ||
| ```jsx | ||
| <Toaster theme="dark" /> | ||
| toast.getToasts() | ||
| toast.getHistory() | ||
| ``` | ||
| ### Position | ||
| ## Toaster props | ||
| You can change the position through the `position` prop on the `<Toaster />` component. Default is `bottom-right`. | ||
| ```jsx | ||
| // Available positions | ||
| // top-left, top-center, top-right, bottom-left, bottom-center, bottom-right | ||
| <Toaster position="top-center" /> | ||
| ``` | ||
| ### Expanded | ||
| Toasts can also be expanded by default through the `expand` prop. You can also change the amount of visible toasts which is 3 by default. | ||
| ```jsx | ||
| <Toaster expand visibleToasts={9} /> | ||
| ``` | ||
| ### Styling | ||
| Styling can be done globally via `toastOptions`, this way every toast will have the same styling. | ||
| ```jsx | ||
| <Toaster | ||
| toastOptions={{ style: { background: 'red' }, class: 'my-toast', descriptionClass: 'my-toast-description' }} | ||
| /> | ||
| ``` | ||
| You can also use the same props when calling toast to style a specific toast. | ||
| ```jsx | ||
| toast('Event has been created', { | ||
| style: { | ||
| background: 'red', | ||
| }, | ||
| class: 'my-toast', | ||
| descriptionClass: 'my-toast-description', | ||
| }) | ||
| ``` | ||
| ### Tailwind CSS | ||
| The preferred way to style the toasts with tailwind is by using the `unstyled` prop. That will give you an unstyled toast which you can then style with tailwind. | ||
| ```tsx | ||
| <Toaster | ||
| theme="system" | ||
| position="top-right" | ||
| richColors | ||
| closeButton | ||
| expand | ||
| visibleToasts={5} | ||
| duration={5000} | ||
| gap={14} | ||
| offset={32} | ||
| mobileOffset={{ bottom: 24, left: 16, right: 16 }} | ||
| hotkey={['altKey', 'KeyT']} | ||
| dir="auto" | ||
| swipeDirections={['top', 'right']} | ||
| containerAriaLabel="Notifications" | ||
| toastOptions={{ | ||
| unstyled: true, | ||
| classes: { | ||
| toast: 'bg-blue-400', | ||
| title: 'text-red-400', | ||
| description: 'text-red-400', | ||
| actionButton: 'bg-zinc-400', | ||
| cancelButton: 'bg-orange-400', | ||
| closeButton: 'bg-lime-400', | ||
| className: 'my-toast', | ||
| descriptionClassName: 'my-toast-description', | ||
| closeButtonAriaLabel: 'Close notification', | ||
| classNames: { | ||
| toast: 'toast', | ||
| title: 'title', | ||
| description: 'description', | ||
| }, | ||
@@ -248,19 +167,21 @@ }} | ||
| You can do the same when calling `toast()`. | ||
| Legacy aliases from older `solid-sonner` versions still work for compatibility: | ||
| - `class` -> `className` | ||
| - `classes` -> `classNames` | ||
| - `descriptionClass` -> `descriptionClassName` | ||
| ## Multiple toasters | ||
| ```tsx | ||
| toast('Hello World', { | ||
| unstyled: true, | ||
| classes: { | ||
| toast: 'bg-blue-400', | ||
| title: 'text-red-400 text-2xl', | ||
| description: 'text-red-400', | ||
| actionButton: 'bg-zinc-400', | ||
| cancelButton: 'bg-orange-400', | ||
| closeButton: 'bg-lime-400', | ||
| }, | ||
| }) | ||
| <> | ||
| <Toaster /> | ||
| <Toaster id="sidebar" position="top-left" /> | ||
| </> | ||
| toast('Global toast') | ||
| toast('Sidebar toast', { toasterId: 'sidebar' }) | ||
| ``` | ||
| Styling per toast type is also possible. | ||
| ## Tailwind / unstyled mode | ||
@@ -271,7 +192,9 @@ ```tsx | ||
| unstyled: true, | ||
| classes: { | ||
| error: 'bg-red-400', | ||
| success: 'text-green-400', | ||
| warning: 'text-yellow-400', | ||
| info: 'bg-blue-400', | ||
| classNames: { | ||
| toast: 'bg-blue-500 text-white', | ||
| title: 'font-semibold', | ||
| description: 'text-blue-100', | ||
| actionButton: 'bg-white text-blue-700', | ||
| cancelButton: 'bg-blue-700 text-white', | ||
| closeButton: 'bg-white text-black', | ||
| }, | ||
@@ -282,84 +205,10 @@ }} | ||
| ### Close button | ||
| ## Notes | ||
| Add a close button to all toasts that shows on hover by adding the `closeButton` prop. | ||
| - `pauseWhenPageIsHidden` is available and defaults to Sonner-like hidden-page pausing behavior | ||
| - Per-toast `closeButton`, `dismissible`, `richColors`, `testId`, and `toasterId` are supported | ||
| - `action` respects `event.preventDefault()` and will keep the toast open | ||
| ```jsx | ||
| <Toaster closeButton /> | ||
| ``` | ||
| ### Rich colors | ||
| You can make error and success state more colorful by adding the `richColors` prop. | ||
| ```jsx | ||
| <Toaster richColors /> | ||
| ``` | ||
| ### Custom offset | ||
| Offset from the edges of the screen. | ||
| ```jsx | ||
| <Toaster offset="80px" /> | ||
| ``` | ||
| ### Programmatically remove toast | ||
| To remove a toast programmatically use `toast.dismiss(id)`. | ||
| ```jsx | ||
| const toastId = toast('Event has been created') | ||
| toast.dismiss(toastId) | ||
| ``` | ||
| You can also use the dismiss method without the id to dismiss all toasts. | ||
| ```jsx | ||
| // Removes all toasts | ||
| toast.dismiss() | ||
| ``` | ||
| ### Duration | ||
| You can change the duration of each toast by using the `duration` property, or change the duration of all toasts like this: | ||
| ```jsx | ||
| <Toaster duration={10000} /> | ||
| ``` | ||
| ```jsx | ||
| toast('Event has been created', { | ||
| duration: 10000, | ||
| }) | ||
| // Persisent toast | ||
| toast('Event has been created', { | ||
| duration: Number.POSITIVE_INFINITY, | ||
| }) | ||
| ``` | ||
| ### On Close Callback | ||
| You can pass `onDismiss` and `onAutoClose` callbacks. `onDismiss` gets fired when either the close button gets clicked or the toast is swiped. `onAutoClose` fires when the toast disappears automatically after it's timeout (`duration` prop). | ||
| ```jsx | ||
| toast('Event has been created', { | ||
| onDismiss: t => console.log(`Toast with id ${t.id} has been dismissed`), | ||
| onAutoClose: t => console.log(`Toast with id ${t.id} has been closed automatically`), | ||
| }) | ||
| ``` | ||
| ## Keyboard focus | ||
| You can focus on the toast area by pressing ⌥/alt + T. You can override it by providing an array of event.code values for each key. | ||
| ```jsx | ||
| <Toaster hotkey={['KeyC']} /> | ||
| ``` | ||
| ## License | ||
| MIT |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
250265
29.01%4585
34.69%205
-42.42%1
Infinity%