UNPKG

@react-spectrum/s2

Version:
544 lines (520 loc) 25.3 kB
import "./Toast.css"; import {ActionButton as $da878a05ab4a403e$export$cfc7921d29ef7b80} from "./ActionButton.mjs"; import $e17a4836f4b6cc28$export$2e2bcd8739ae039 from "../icons/AlertTriangle.mjs"; import {Button as $067ea9f64ccd4e8e$export$353f5b6fc5456de1} from "./Button.mjs"; import {CenterBaseline as $1f4b04be3f24aae3$export$768dac55bb57081d} from "./CenterBaseline.mjs"; import $22a7d72fc05e138d$export$2e2bcd8739ae039 from "../icons/CheckmarkCircle.mjs"; import $e59ae2a33cca2ebf$export$2e2bcd8739ae039 from "../icons/ChevronDown.mjs"; import {CloseButton as $a9cda54c4f47ce52$export$de65de8213222d10} from "./CloseButton.mjs"; import $4c8f17dac3ecefd9$export$2e2bcd8739ae039 from "../icons/InfoCircle.mjs"; import $jeXmD$intlStringsmjs from "./intlStrings.mjs"; import {Text as $8e847109a6ab556d$export$5f1af8db9871e1d6} from "./Content.mjs"; import "./Toast_module.css"; import $jeXmD$Toast_modulemjs from "./Toast_module.mjs"; import {jsx as $jeXmD$jsx, jsxs as $jeXmD$jsxs} from "react/jsx-runtime"; import {createContext as $jeXmD$createContext, useRef as $jeXmD$useRef, useMemo as $jeXmD$useMemo, useEffect as $jeXmD$useEffect, useContext as $jeXmD$useContext} from "react"; import {filterDOMProps as $jeXmD$filterDOMProps, useEvent as $jeXmD$useEvent, isWebKit as $jeXmD$isWebKit} from "@react-aria/utils"; import {flushSync as $jeXmD$flushSync} from "react-dom"; import {useModalOverlay as $jeXmD$useModalOverlay, FocusScope as $jeXmD$FocusScope} from "react-aria"; import {UNSTABLE_ToastQueue as $jeXmD$UNSTABLE_ToastQueue, UNSTABLE_ToastRegion as $jeXmD$UNSTABLE_ToastRegion, UNSTABLE_ToastList as $jeXmD$UNSTABLE_ToastList, UNSTABLE_ToastStateContext as $jeXmD$UNSTABLE_ToastStateContext, UNSTABLE_Toast as $jeXmD$UNSTABLE_Toast, UNSTABLE_ToastContent as $jeXmD$UNSTABLE_ToastContent} from "react-aria-components"; import {useLocalizedStringFormatter as $jeXmD$useLocalizedStringFormatter} from "@react-aria/i18n"; import {useMediaQuery as $jeXmD$useMediaQuery} from "@react-spectrum/utils"; import {useOverlayTriggerState as $jeXmD$useOverlayTriggerState} from "react-stately"; function $parcel$interopDefault(a) { return a && a.__esModule ? a.default : a; } /* * Copyright 2025 Adobe. All rights reserved. * This file is licensed to you under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. You may obtain a copy * of the License at http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software distributed under * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS * OF ANY KIND, either express or implied. See the License for the specific language * governing permissions and limitations under the License. */ let $6e462db325878d1a$var$globalReduceMotion = false; function $6e462db325878d1a$var$startViewTransition(fn, type) { if ('startViewTransition' in document) { // Safari doesn't support :active-view-transition-type() yet, so we fall back to a class on the html element. document.documentElement.classList.add((0, ($parcel$interopDefault($jeXmD$Toast_modulemjs)))[type]); if ($6e462db325878d1a$var$globalReduceMotion) document.documentElement.classList.add((0, ($parcel$interopDefault($jeXmD$Toast_modulemjs))).reduceMotion); let viewTransition = document.startViewTransition(()=>(0, $jeXmD$flushSync)(fn)); viewTransition.ready.catch(()=>{}); viewTransition.finished.then(()=>{ document.documentElement.classList.remove((0, ($parcel$interopDefault($jeXmD$Toast_modulemjs)))[type], (0, ($parcel$interopDefault($jeXmD$Toast_modulemjs))).reduceMotion); }); } else fn(); } // There is a single global toast queue instance for the whole app, initialized lazily. let $6e462db325878d1a$var$globalToastQueue = null; function $6e462db325878d1a$var$getGlobalToastQueue() { if (!$6e462db325878d1a$var$globalToastQueue) $6e462db325878d1a$var$globalToastQueue = new (0, $jeXmD$UNSTABLE_ToastQueue)({ maxVisibleToasts: Infinity, wrapUpdate (fn, action) { $6e462db325878d1a$var$startViewTransition(fn, `toast-${action}`); } }); return $6e462db325878d1a$var$globalToastQueue; } function $6e462db325878d1a$var$addToast(children, variant, options = {}) { let value = { children: children, variant: variant, actionLabel: options.actionLabel, onAction: options.onAction, shouldCloseOnAction: options.shouldCloseOnAction, ...(0, $jeXmD$filterDOMProps)(options) }; // Minimum time of 5s from https://spectrum.adobe.com/page/toast/#Auto-dismissible // Actionable toasts cannot be auto dismissed. That would fail WCAG SC 2.2.1. // It is debatable whether non-actionable toasts would also fail. let timeout = options.timeout && !options.actionLabel ? Math.max(options.timeout, 5000) : undefined; let queue = $6e462db325878d1a$var$getGlobalToastQueue(); let key = queue.add(value, { timeout: timeout, onClose: options.onClose }); return ()=>queue.close(key); } const $6e462db325878d1a$export$f1f8569633bbbec4 = { /** Queues a neutral toast. */ neutral (children, options = {}) { return $6e462db325878d1a$var$addToast(children, 'neutral', options); }, /** Queues a positive toast. */ positive (children, options = {}) { return $6e462db325878d1a$var$addToast(children, 'positive', options); }, /** Queues a negative toast. */ negative (children, options = {}) { return $6e462db325878d1a$var$addToast(children, 'negative', options); }, /** Queues an informational toast. */ info (children, options = {}) { return $6e462db325878d1a$var$addToast(children, 'info', options); } }; const $6e462db325878d1a$var$toastRegion = function anonymous(props) { let rules = " "; if (props.isFocusVisible) rules += ' _Lf1'; else rules += ' _Le1'; rules += ' Oh1'; rules += ' Olc1'; rules += ' _Mc1'; rules += ' _Kd1'; rules += ' sd1'; if (props.placement === "bottom") rules += ' _tb1'; else if (props.placement === "top") rules += ' _ta1'; rules += ' _Pb1'; rules += ' _Ar1'; rules += ' _zr1'; rules += ' ZJ1'; if (props.placement === "top") { if (props.isExpanded) rules += ' Wr1'; else rules += ' Wu1'; } if (props.placement === "bottom") { if (props.isExpanded) rules += ' _lr1'; else rules += ' _lu1'; } if (props.align === "end") rules += ' IM1'; else if (props.align === "center") rules += ' IM1'; else if (props.align === "start") rules += ' IC1'; if (props.align === "end") rules += ' HC1'; else if (props.align === "center") rules += ' HM1'; else if (props.align === "start") rules += ' HM1'; rules += ' _oa1'; rules += ' Ka1'; rules += ' oc1'; rules += ' nc1'; rules += ' kc1'; rules += ' jc1'; return rules; }; const $6e462db325878d1a$var$toastList = function anonymous(props) { let rules = " "; rules += ' _Pc1'; rules += ' _ub1'; rules += ' sd1'; rules += ' Ue1'; rules += ' qe1'; if (props.placement === "bottom") rules += ' _tb1'; else if (props.placement === "top") rules += ' _ta1'; rules += ' _oa1'; rules += ' Jy1'; rules += ' Gy1'; if (props.isExpanded) { if (props.placement === "bottom") rules += ' Tt1'; else if (props.placement === "top") rules += ' Tf1'; } if (props.isExpanded) { if (props.placement === "bottom") rules += ' Qf1'; else if (props.placement === "top") rules += ' Qt1'; } if (props.isExpanded) rules += ' St1'; else rules += ' Sd1'; if (props.isExpanded) rules += ' Rt1'; else rules += ' Rd1'; if (props.isExpanded) rules += ' Ic1'; else rules += ' Iy1'; if (props.isExpanded) rules += ' Hc1'; else rules += ' Hy1'; if (props.isExpanded) rules += ' _Na1'; if (props.isExpanded) rules += ' Pa1'; return rules; }; const $6e462db325878d1a$var$toastStyle = function anonymous(props) { let rules = " "; if (props.isFocusVisible) rules += ' _Lf1'; else rules += ' _Le1'; if (props.isExpanded) rules += ' Ok1'; else rules += ' Oh1'; rules += ' _Mc1'; rules += ' _Kd1'; rules += ' sd1'; rules += ' Ul1'; rules += ' ql1'; rules += ' Sf1'; rules += ' Rt1'; rules += ' Te1'; rules += ' Qe1'; rules += ' oc1'; rules += ' nc1'; rules += ' kc1'; rules += ' jc1'; rules += ' Mj1'; rules += ' -Zn3gsb-L0uB6Qb1'; rules += ' LksArhc1'; rules += ' _oa1'; rules += ' _va1'; rules += ' uk1'; rules += ' ucJ9TBTb1'; rules += ' ud3Euai1'; rules += ' uea1'; rules += ' ugb1'; rules += ' uhd1'; rules += ' uje1'; rules += ' u2NhKxcl1'; rules += ' uic1'; rules += ' -_6BNtrc-c1'; rules += ' vx1'; rules += ' xb1'; rules += ' _xa1'; rules += ' _Fd1'; rules += ' _FnuYUweb1'; rules += ' px1'; if (props.variant === "negative") rules += ' gB1'; else if (props.variant === "positive") rules += ' g11'; else if (props.variant === "info") rules += ' g21'; else if (props.variant === "neutral") rules += ' g61'; rules += ' -_8sjo0b-t5ZbAob1'; if (props.isExpanded) rules += ' _nd1'; else rules += ' _nLeasBb1'; rules += ' _8d1'; return rules; }; const $6e462db325878d1a$var$toastBody = function anonymous(props) { let rules = " "; if (props.isSingle) rules += ' sd1'; else rules += ' se1'; rules += ' D4w1sLc1'; rules += ' CWLL0Xb1'; rules += ' _ub1'; rules += ' _wb1'; rules += ' eb1'; rules += ' qj1'; rules += ' Ue1'; return rules; }; const $6e462db325878d1a$var$toastContent = " sd1 Ue1 qe1 ea1 zk52g2d1 yk52g2d1 Bk52g2d1 Ak52g2d1 ri1 ZJ1 -nTSCV-LTAEPe1 __ZLTAEPe1 Na1"; const $6e462db325878d1a$var$controls = function anonymous(props) { let rules = " "; rules += ' _pb1'; if (props.isExpanded) rules += ' sd1'; else rules += ' sk1'; rules += ' _Cb1'; rules += ' Ue1'; rules += ' qe1'; if (props.isExpanded) rules += ' _Ib1'; else rules += ' _Ia1'; return rules; }; const $6e462db325878d1a$var$ICONS = { info: (0, $4c8f17dac3ecefd9$export$2e2bcd8739ae039), negative: (0, $e17a4836f4b6cc28$export$2e2bcd8739ae039), positive: (0, $22a7d72fc05e138d$export$2e2bcd8739ae039) }; const $6e462db325878d1a$var$ToastContainerContext = /*#__PURE__*/ (0, $jeXmD$createContext)(null); function $6e462db325878d1a$export$f2815235e76a62b9(props) { let { placement: placement = 'bottom' } = props; let queue = $6e462db325878d1a$var$getGlobalToastQueue(); let align = 'center'; [placement, align = 'center'] = placement.split(' '); let stringFormatter = (0, $jeXmD$useLocalizedStringFormatter)((0, ($parcel$interopDefault($jeXmD$intlStringsmjs))), '@react-spectrum/s2'); let regionRef = (0, $jeXmD$useRef)(null); let state = (0, $jeXmD$useOverlayTriggerState)({}); let { isOpen: isExpanded, close: close, toggle: toggle } = state; let ctx = (0, $jeXmD$useMemo)(()=>({ isExpanded: isExpanded, toggleExpanded () { if (!isExpanded && queue.visibleToasts.length <= 1) return; $6e462db325878d1a$var$startViewTransition(()=>toggle(), isExpanded ? 'toast-collapse' : 'toast-expand'); } }), [ isExpanded, toggle, queue ]); // Set the state to collapsed whenever the queue is emptied. (0, $jeXmD$useEffect)(()=>{ return queue.subscribe(()=>{ if (queue.visibleToasts.length === 0 && isExpanded) close(); }); }, [ queue, isExpanded, close ]); let collapse = ()=>{ regionRef.current?.focus(); ctx.toggleExpanded(); }; // Prevent scroll, aria hide outside, and contain focus when expanded, since we take over the whole screen. // Attach event handler to the ref since ToastRegion doesn't pass through onKeyDown. (0, $jeXmD$useModalOverlay)({}, state, regionRef); (0, $jeXmD$useEvent)(regionRef, 'keydown', isExpanded ? (e)=>{ if (e.key === 'Escape') collapse(); } : undefined); let prefersReducedMotion = (0, $jeXmD$useMediaQuery)('(prefers-reduced-motion)'); let reduceMotion = props['PRIVATE_forceReducedMotion'] ?? prefersReducedMotion; (0, $jeXmD$useEffect)(()=>{ let oldGlobalReduceMotion = $6e462db325878d1a$var$globalReduceMotion; $6e462db325878d1a$var$globalReduceMotion = reduceMotion; return ()=>{ $6e462db325878d1a$var$globalReduceMotion = oldGlobalReduceMotion; }; }, [ reduceMotion ]); return /*#__PURE__*/ (0, $jeXmD$jsx)((0, $jeXmD$UNSTABLE_ToastRegion), { ...props, ref: regionRef, queue: queue, className: (renderProps)=>$6e462db325878d1a$var$toastRegion({ ...renderProps, placement: placement, align: align, isExpanded: isExpanded }), children: /*#__PURE__*/ (0, $jeXmD$jsx)((0, $jeXmD$FocusScope), { contain: isExpanded, children: /*#__PURE__*/ (0, $jeXmD$jsxs)($6e462db325878d1a$var$ToastContainerContext.Provider, { value: ctx, children: [ isExpanded && // eslint-disable-next-line /*#__PURE__*/ (0, $jeXmD$jsx)("div", { className: (0, ($parcel$interopDefault($jeXmD$Toast_modulemjs)))['toast-background'] + " _Pb1 Wr1 _lr1 _Ar1 _zr1 g81", onClick: collapse }), /*#__PURE__*/ (0, $jeXmD$jsx)($6e462db325878d1a$var$SpectrumToastList, { placement: placement, align: align, reduceMotion: reduceMotion }), /*#__PURE__*/ (0, $jeXmD$jsxs)("div", { className: (0, ($parcel$interopDefault($jeXmD$Toast_modulemjs)))['toast-controls'] + $6e462db325878d1a$var$controls({ isExpanded: isExpanded }), children: [ /*#__PURE__*/ (0, $jeXmD$jsx)((0, $da878a05ab4a403e$export$cfc7921d29ef7b80), { size: "S", onPress: ()=>queue.clear(), // Default focus ring does not have enough contrast against gray background. UNSAFE_style: { outlineColor: 'white' }, children: stringFormatter.format('toast.clearAll') }), /*#__PURE__*/ (0, $jeXmD$jsx)((0, $da878a05ab4a403e$export$cfc7921d29ef7b80), { size: "S", onPress: collapse, UNSAFE_style: { outlineColor: 'white' }, children: stringFormatter.format('toast.collapse') }) ] }) ] }) }) }); } function $6e462db325878d1a$var$SpectrumToastList({ placement: placement, align: align, reduceMotion: reduceMotion }) { let { isExpanded: isExpanded, toggleExpanded: toggleExpanded } = (0, $jeXmD$useContext)($6e462db325878d1a$var$ToastContainerContext); // Attach click handler to ref since ToastList doesn't pass through onClick/onPress. let toastListRef = (0, $jeXmD$useRef)(null); (0, $jeXmD$useEvent)(toastListRef, 'click', (e)=>{ // Have to check if this is a button because stopPropagation in react events doesn't affect native events. if (!isExpanded && !e.target?.closest('button')) toggleExpanded(); }); return /*#__PURE__*/ (0, $jeXmD$jsx)((0, $jeXmD$UNSTABLE_ToastList), { ref: toastListRef, style: ({ isHovered: isHovered })=>{ let origin = isHovered ? 95 : 55; return { perspective: 80, perspectiveOrigin: 'center ' + (placement === 'top' ? `calc(100% + ${origin}px)` : `${-origin}px`), transition: 'perspective-origin 400ms' }; }, className: (0, ($parcel$interopDefault($jeXmD$Toast_modulemjs)))[isExpanded ? 'toast-list-expanded' : 'toast-list-collapsed'] + $6e462db325878d1a$var$toastList({ placement: placement, align: align, isExpanded: isExpanded }), children: ({ toast: toast })=>/*#__PURE__*/ (0, $jeXmD$jsx)($6e462db325878d1a$export$f9b1e1c88454fa08, { toast: toast, placement: placement, align: align, reduceMotion: reduceMotion }) }); } function $6e462db325878d1a$export$f9b1e1c88454fa08(props) { let { toast: toast, placement: placement = 'bottom', align: align = 'center' } = props; let variant = toast.content.variant || 'info'; let Icon = $6e462db325878d1a$var$ICONS[variant]; let state = (0, $jeXmD$useContext)((0, $jeXmD$UNSTABLE_ToastStateContext)); let visibleToasts = state.visibleToasts; let index = visibleToasts.indexOf(toast); let isMain = index <= 0; let ctx = (0, $jeXmD$useContext)($6e462db325878d1a$var$ToastContainerContext); let isExpanded = ctx?.isExpanded || false; let toastRef = (0, $jeXmD$useRef)(null); let stringFormatter = (0, $jeXmD$useLocalizedStringFormatter)((0, ($parcel$interopDefault($jeXmD$intlStringsmjs))), '@react-spectrum/s2'); // When not expanded, use a presentational div for the toasts behind the top one. // The content is invisible, all we show is the background color. if (!isMain && ctx && !ctx.isExpanded) return /*#__PURE__*/ (0, $jeXmD$jsx)("div", { role: "presentation", style: { position: 'absolute', [placement === 'top' ? 'bottom' : 'top']: 0, left: 0, width: '100%', translate: `0 0 ${-12 * index / 16}rem`, // Only 3 toasts are visible in the stack at once, but all toasts are in the DOM. // This allows view transitions to smoothly animate them from where they would be // in the collapsed stack to their final position in the expanded list. opacity: index >= 3 ? 0 : 1, zIndex: visibleToasts.length - index - 1, // When reduced motion is enabled, use append index to view-transition-name // so that adding/removing a toast cross fades instead of transitioning the position. // This works because the toasts are seen as separate elements instead of the same one when their index changes. viewTransitionName: toast.key + (props.reduceMotion ? '-' + index : ''), viewTransitionClass: [ (0, ($parcel$interopDefault($jeXmD$Toast_modulemjs))).toast, (0, ($parcel$interopDefault($jeXmD$Toast_modulemjs)))['background-toast'] ].map((c)=>CSS.escape(c)).join(' ') }, className: (0, ($parcel$interopDefault($jeXmD$Toast_modulemjs))).toast + $6e462db325878d1a$var$toastStyle({ variant: toast.content.variant || 'info', index: index, isExpanded: isExpanded }), ref: $6e462db325878d1a$var$fixSafariTransform }); return /*#__PURE__*/ (0, $jeXmD$jsxs)((0, $jeXmD$UNSTABLE_Toast), { ref: toastRef, toast: toast, style: { zIndex: visibleToasts.length - index - 1, viewTransitionName: toast.key, viewTransitionClass: [ (0, ($parcel$interopDefault($jeXmD$Toast_modulemjs))).toast, !isMain ? (0, ($parcel$interopDefault($jeXmD$Toast_modulemjs)))['background-toast'] : '', (0, ($parcel$interopDefault($jeXmD$Toast_modulemjs)))[placement], (0, ($parcel$interopDefault($jeXmD$Toast_modulemjs)))[align] ].filter(Boolean).map((c)=>CSS.escape(c)).join(' ') }, className: (renderProps)=>(0, ($parcel$interopDefault($jeXmD$Toast_modulemjs))).toast + $6e462db325878d1a$var$toastStyle({ ...renderProps, variant: toast.content.variant || 'info', index: index, isExpanded: isExpanded }), children: [ /*#__PURE__*/ (0, $jeXmD$jsxs)("div", { role: "presentation", className: $6e462db325878d1a$var$toastBody({ isSingle: !isMain || visibleToasts.length <= 1 || isExpanded }), children: [ /*#__PURE__*/ (0, $jeXmD$jsxs)((0, $jeXmD$UNSTABLE_ToastContent), { className: $6e462db325878d1a$var$toastContent + (ctx && isMain ? ` ${(0, ($parcel$interopDefault($jeXmD$Toast_modulemjs)))['toast-content']}` : null), children: [ Icon && /*#__PURE__*/ (0, $jeXmD$jsx)((0, $1f4b04be3f24aae3$export$768dac55bb57081d), { children: /*#__PURE__*/ (0, $jeXmD$jsx)(Icon, {}) }), /*#__PURE__*/ (0, $jeXmD$jsx)((0, $8e847109a6ab556d$export$5f1af8db9871e1d6), { slot: "title", children: toast.content.children }) ] }), !isExpanded && visibleToasts.length > 1 && /*#__PURE__*/ (0, $jeXmD$jsxs)((0, $da878a05ab4a403e$export$cfc7921d29ef7b80), { isQuiet: true, staticColor: "white", styles: " zPgKvMe1 yPgKvMe1 BPgKvMe1 APgKvMe1", // Make the chevron line up with the toast text, even though there is padding within the button. UNSAFE_style: { marginInlineStart: variant === 'neutral' ? -10 : 14 }, UNSAFE_className: ctx && isMain ? (0, ($parcel$interopDefault($jeXmD$Toast_modulemjs)))['toast-expand'] : undefined, onPress: ()=>{ // This button disappears when clicked, so move focus to the toast. toastRef.current?.focus(); ctx?.toggleExpanded(); }, children: [ /*#__PURE__*/ (0, $jeXmD$jsx)((0, $8e847109a6ab556d$export$5f1af8db9871e1d6), { children: stringFormatter.format('toast.showAll') }), /*#__PURE__*/ (0, $jeXmD$jsx)((0, $e59ae2a33cca2ebf$export$2e2bcd8739ae039), { UNSAFE_style: { rotate: placement === 'bottom' ? '180deg' : undefined } }) ] }), toast.content.actionLabel && /*#__PURE__*/ (0, $jeXmD$jsx)((0, $067ea9f64ccd4e8e$export$353f5b6fc5456de1), { variant: "secondary", fillStyle: "outline", staticColor: "white", onPress: ()=>{ toast.content.onAction?.(); if (toast.content.shouldCloseOnAction) state.close(toast.key); }, UNSAFE_className: ctx && isMain ? (0, ($parcel$interopDefault($jeXmD$Toast_modulemjs)))['toast-action'] : undefined, styles: " IM1 zzz3eAe1 yzz3eAe1 Bzz3eAe1 Azz3eAe1", children: toast.content.actionLabel }) ] }), /*#__PURE__*/ (0, $jeXmD$jsx)((0, $a9cda54c4f47ce52$export$de65de8213222d10), { staticColor: "white", UNSAFE_className: ctx && isMain ? (0, ($parcel$interopDefault($jeXmD$Toast_modulemjs)))['toast-close'] : undefined }) ] }); } function $6e462db325878d1a$var$fixSafariTransform(el) { // Safari has a bug where the toasts display in the wrong position (CSS transform is not applied correctly). // Work around this by removing it, forcing a reflow, and re-applying. if (el && (0, $jeXmD$isWebKit)()) { let translate = el.style.translate; el.style.translate = ''; el.offsetHeight; el.style.translate = translate; } } export {$6e462db325878d1a$export$f1f8569633bbbec4 as ToastQueue, $6e462db325878d1a$export$f2815235e76a62b9 as ToastContainer, $6e462db325878d1a$export$f9b1e1c88454fa08 as SpectrumToast}; //# sourceMappingURL=Toast.mjs.map