UNPKG

@react-spectrum/s2

Version:
550 lines (524 loc) 25.2 kB
require("./Toast.css"); var $6e265ff388155b91$exports = require("./ActionButton.cjs"); var $7e7cdbd2b8ae2467$exports = require("../icons/AlertTriangle.cjs"); var $25d06cf8d4e72761$exports = require("./Button.cjs"); var $e991cbcdf82ced71$exports = require("./CenterBaseline.cjs"); var $6391be254c189366$exports = require("../icons/CheckmarkCircle.cjs"); var $d88edd0de6bc65f6$exports = require("../icons/ChevronDown.cjs"); var $2f907cb84c6e9e75$exports = require("./CloseButton.cjs"); var $0ed6e07b499b9797$exports = require("../icons/InfoCircle.cjs"); var $4526404114e78c80$exports = require("./intlStrings.cjs"); var $6367bc87eb7d24ad$exports = require("./Content.cjs"); require("./Toast_module.css"); var $aa16d99d2c9699f6$exports = require("./Toast_module.cjs"); var $fI0QS$reactjsxruntime = require("react/jsx-runtime"); var $fI0QS$react = require("react"); var $fI0QS$reactariautils = require("@react-aria/utils"); var $fI0QS$reactdom = require("react-dom"); var $fI0QS$reactaria = require("react-aria"); var $fI0QS$reactariacomponents = require("react-aria-components"); var $fI0QS$reactariai18n = require("@react-aria/i18n"); var $fI0QS$reactspectrumutils = require("@react-spectrum/utils"); var $fI0QS$reactstately = require("react-stately"); function $parcel$interopDefault(a) { return a && a.__esModule ? a.default : a; } function $parcel$export(e, n, v, s) { Object.defineProperty(e, n, {get: v, set: s, enumerable: true, configurable: true}); } $parcel$export(module.exports, "ToastQueue", () => $f1992060af7549d9$export$f1f8569633bbbec4); $parcel$export(module.exports, "ToastContainer", () => $f1992060af7549d9$export$f2815235e76a62b9); /* * 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 $f1992060af7549d9$var$globalReduceMotion = false; function $f1992060af7549d9$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($aa16d99d2c9699f6$exports)))[type]); if ($f1992060af7549d9$var$globalReduceMotion) document.documentElement.classList.add((0, ($parcel$interopDefault($aa16d99d2c9699f6$exports))).reduceMotion); let viewTransition = document.startViewTransition(()=>(0, $fI0QS$reactdom.flushSync)(fn)); viewTransition.ready.catch(()=>{}); viewTransition.finished.then(()=>{ document.documentElement.classList.remove((0, ($parcel$interopDefault($aa16d99d2c9699f6$exports)))[type], (0, ($parcel$interopDefault($aa16d99d2c9699f6$exports))).reduceMotion); }); } else fn(); } // There is a single global toast queue instance for the whole app, initialized lazily. let $f1992060af7549d9$var$globalToastQueue = null; function $f1992060af7549d9$var$getGlobalToastQueue() { if (!$f1992060af7549d9$var$globalToastQueue) $f1992060af7549d9$var$globalToastQueue = new (0, $fI0QS$reactariacomponents.UNSTABLE_ToastQueue)({ maxVisibleToasts: Infinity, wrapUpdate (fn, action) { $f1992060af7549d9$var$startViewTransition(fn, `toast-${action}`); } }); return $f1992060af7549d9$var$globalToastQueue; } function $f1992060af7549d9$var$addToast(children, variant, options = {}) { let value = { children: children, variant: variant, actionLabel: options.actionLabel, onAction: options.onAction, shouldCloseOnAction: options.shouldCloseOnAction, ...(0, $fI0QS$reactariautils.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 = $f1992060af7549d9$var$getGlobalToastQueue(); let key = queue.add(value, { timeout: timeout, onClose: options.onClose }); return ()=>queue.close(key); } const $f1992060af7549d9$export$f1f8569633bbbec4 = { /** Queues a neutral toast. */ neutral (children, options = {}) { return $f1992060af7549d9$var$addToast(children, 'neutral', options); }, /** Queues a positive toast. */ positive (children, options = {}) { return $f1992060af7549d9$var$addToast(children, 'positive', options); }, /** Queues a negative toast. */ negative (children, options = {}) { return $f1992060af7549d9$var$addToast(children, 'negative', options); }, /** Queues an informational toast. */ info (children, options = {}) { return $f1992060af7549d9$var$addToast(children, 'info', options); } }; const $f1992060af7549d9$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 $f1992060af7549d9$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 $f1992060af7549d9$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 $f1992060af7549d9$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 $f1992060af7549d9$var$toastContent = " sd1 Ue1 qe1 ea1 zk52g2d1 yk52g2d1 Bk52g2d1 Ak52g2d1 ri1 ZJ1 -nTSCV-LTAEPe1 __ZLTAEPe1 Na1"; const $f1992060af7549d9$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 $f1992060af7549d9$var$ICONS = { info: (0, $0ed6e07b499b9797$exports.default), negative: (0, $7e7cdbd2b8ae2467$exports.default), positive: (0, $6391be254c189366$exports.default) }; const $f1992060af7549d9$var$ToastContainerContext = /*#__PURE__*/ (0, $fI0QS$react.createContext)(null); function $f1992060af7549d9$export$f2815235e76a62b9(props) { let { placement: placement = 'bottom' } = props; let queue = $f1992060af7549d9$var$getGlobalToastQueue(); let align = 'center'; [placement, align = 'center'] = placement.split(' '); let stringFormatter = (0, $fI0QS$reactariai18n.useLocalizedStringFormatter)((0, ($parcel$interopDefault($4526404114e78c80$exports))), '@react-spectrum/s2'); let regionRef = (0, $fI0QS$react.useRef)(null); let state = (0, $fI0QS$reactstately.useOverlayTriggerState)({}); let { isOpen: isExpanded, close: close, toggle: toggle } = state; let ctx = (0, $fI0QS$react.useMemo)(()=>({ isExpanded: isExpanded, toggleExpanded () { if (!isExpanded && queue.visibleToasts.length <= 1) return; $f1992060af7549d9$var$startViewTransition(()=>toggle(), isExpanded ? 'toast-collapse' : 'toast-expand'); } }), [ isExpanded, toggle, queue ]); // Set the state to collapsed whenever the queue is emptied. (0, $fI0QS$react.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, $fI0QS$reactaria.useModalOverlay)({}, state, regionRef); (0, $fI0QS$reactariautils.useEvent)(regionRef, 'keydown', isExpanded ? (e)=>{ if (e.key === 'Escape') collapse(); } : undefined); let prefersReducedMotion = (0, $fI0QS$reactspectrumutils.useMediaQuery)('(prefers-reduced-motion)'); let reduceMotion = props['PRIVATE_forceReducedMotion'] ?? prefersReducedMotion; (0, $fI0QS$react.useEffect)(()=>{ let oldGlobalReduceMotion = $f1992060af7549d9$var$globalReduceMotion; $f1992060af7549d9$var$globalReduceMotion = reduceMotion; return ()=>{ $f1992060af7549d9$var$globalReduceMotion = oldGlobalReduceMotion; }; }, [ reduceMotion ]); return /*#__PURE__*/ (0, $fI0QS$reactjsxruntime.jsx)((0, $fI0QS$reactariacomponents.UNSTABLE_ToastRegion), { ...props, ref: regionRef, queue: queue, className: (renderProps)=>$f1992060af7549d9$var$toastRegion({ ...renderProps, placement: placement, align: align, isExpanded: isExpanded }), children: /*#__PURE__*/ (0, $fI0QS$reactjsxruntime.jsx)((0, $fI0QS$reactaria.FocusScope), { contain: isExpanded, children: /*#__PURE__*/ (0, $fI0QS$reactjsxruntime.jsxs)($f1992060af7549d9$var$ToastContainerContext.Provider, { value: ctx, children: [ isExpanded && // eslint-disable-next-line /*#__PURE__*/ (0, $fI0QS$reactjsxruntime.jsx)("div", { className: (0, ($parcel$interopDefault($aa16d99d2c9699f6$exports)))['toast-background'] + " _Pb1 Wr1 _lr1 _Ar1 _zr1 g81", onClick: collapse }), /*#__PURE__*/ (0, $fI0QS$reactjsxruntime.jsx)($f1992060af7549d9$var$SpectrumToastList, { placement: placement, align: align, reduceMotion: reduceMotion }), /*#__PURE__*/ (0, $fI0QS$reactjsxruntime.jsxs)("div", { className: (0, ($parcel$interopDefault($aa16d99d2c9699f6$exports)))['toast-controls'] + $f1992060af7549d9$var$controls({ isExpanded: isExpanded }), children: [ /*#__PURE__*/ (0, $fI0QS$reactjsxruntime.jsx)((0, $6e265ff388155b91$exports.ActionButton), { 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, $fI0QS$reactjsxruntime.jsx)((0, $6e265ff388155b91$exports.ActionButton), { size: "S", onPress: collapse, UNSAFE_style: { outlineColor: 'white' }, children: stringFormatter.format('toast.collapse') }) ] }) ] }) }) }); } function $f1992060af7549d9$var$SpectrumToastList({ placement: placement, align: align, reduceMotion: reduceMotion }) { let { isExpanded: isExpanded, toggleExpanded: toggleExpanded } = (0, $fI0QS$react.useContext)($f1992060af7549d9$var$ToastContainerContext); // Attach click handler to ref since ToastList doesn't pass through onClick/onPress. let toastListRef = (0, $fI0QS$react.useRef)(null); (0, $fI0QS$reactariautils.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, $fI0QS$reactjsxruntime.jsx)((0, $fI0QS$reactariacomponents.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($aa16d99d2c9699f6$exports)))[isExpanded ? 'toast-list-expanded' : 'toast-list-collapsed'] + $f1992060af7549d9$var$toastList({ placement: placement, align: align, isExpanded: isExpanded }), children: ({ toast: toast })=>/*#__PURE__*/ (0, $fI0QS$reactjsxruntime.jsx)($f1992060af7549d9$export$f9b1e1c88454fa08, { toast: toast, placement: placement, align: align, reduceMotion: reduceMotion }) }); } function $f1992060af7549d9$export$f9b1e1c88454fa08(props) { let { toast: toast, placement: placement = 'bottom', align: align = 'center' } = props; let variant = toast.content.variant || 'info'; let Icon = $f1992060af7549d9$var$ICONS[variant]; let state = (0, $fI0QS$react.useContext)((0, $fI0QS$reactariacomponents.UNSTABLE_ToastStateContext)); let visibleToasts = state.visibleToasts; let index = visibleToasts.indexOf(toast); let isMain = index <= 0; let ctx = (0, $fI0QS$react.useContext)($f1992060af7549d9$var$ToastContainerContext); let isExpanded = ctx?.isExpanded || false; let toastRef = (0, $fI0QS$react.useRef)(null); let stringFormatter = (0, $fI0QS$reactariai18n.useLocalizedStringFormatter)((0, ($parcel$interopDefault($4526404114e78c80$exports))), '@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, $fI0QS$reactjsxruntime.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($aa16d99d2c9699f6$exports))).toast, (0, ($parcel$interopDefault($aa16d99d2c9699f6$exports)))['background-toast'] ].map((c)=>CSS.escape(c)).join(' ') }, className: (0, ($parcel$interopDefault($aa16d99d2c9699f6$exports))).toast + $f1992060af7549d9$var$toastStyle({ variant: toast.content.variant || 'info', index: index, isExpanded: isExpanded }), ref: $f1992060af7549d9$var$fixSafariTransform }); return /*#__PURE__*/ (0, $fI0QS$reactjsxruntime.jsxs)((0, $fI0QS$reactariacomponents.UNSTABLE_Toast), { ref: toastRef, toast: toast, style: { zIndex: visibleToasts.length - index - 1, viewTransitionName: toast.key, viewTransitionClass: [ (0, ($parcel$interopDefault($aa16d99d2c9699f6$exports))).toast, !isMain ? (0, ($parcel$interopDefault($aa16d99d2c9699f6$exports)))['background-toast'] : '', (0, ($parcel$interopDefault($aa16d99d2c9699f6$exports)))[placement], (0, ($parcel$interopDefault($aa16d99d2c9699f6$exports)))[align] ].filter(Boolean).map((c)=>CSS.escape(c)).join(' ') }, className: (renderProps)=>(0, ($parcel$interopDefault($aa16d99d2c9699f6$exports))).toast + $f1992060af7549d9$var$toastStyle({ ...renderProps, variant: toast.content.variant || 'info', index: index, isExpanded: isExpanded }), children: [ /*#__PURE__*/ (0, $fI0QS$reactjsxruntime.jsxs)("div", { role: "presentation", className: $f1992060af7549d9$var$toastBody({ isSingle: !isMain || visibleToasts.length <= 1 || isExpanded }), children: [ /*#__PURE__*/ (0, $fI0QS$reactjsxruntime.jsxs)((0, $fI0QS$reactariacomponents.UNSTABLE_ToastContent), { className: $f1992060af7549d9$var$toastContent + (ctx && isMain ? ` ${(0, ($parcel$interopDefault($aa16d99d2c9699f6$exports)))['toast-content']}` : null), children: [ Icon && /*#__PURE__*/ (0, $fI0QS$reactjsxruntime.jsx)((0, $e991cbcdf82ced71$exports.CenterBaseline), { children: /*#__PURE__*/ (0, $fI0QS$reactjsxruntime.jsx)(Icon, {}) }), /*#__PURE__*/ (0, $fI0QS$reactjsxruntime.jsx)((0, $6367bc87eb7d24ad$exports.Text), { slot: "title", children: toast.content.children }) ] }), !isExpanded && visibleToasts.length > 1 && /*#__PURE__*/ (0, $fI0QS$reactjsxruntime.jsxs)((0, $6e265ff388155b91$exports.ActionButton), { 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($aa16d99d2c9699f6$exports)))['toast-expand'] : undefined, onPress: ()=>{ // This button disappears when clicked, so move focus to the toast. toastRef.current?.focus(); ctx?.toggleExpanded(); }, children: [ /*#__PURE__*/ (0, $fI0QS$reactjsxruntime.jsx)((0, $6367bc87eb7d24ad$exports.Text), { children: stringFormatter.format('toast.showAll') }), /*#__PURE__*/ (0, $fI0QS$reactjsxruntime.jsx)((0, $d88edd0de6bc65f6$exports.default), { UNSAFE_style: { rotate: placement === 'bottom' ? '180deg' : undefined } }) ] }), toast.content.actionLabel && /*#__PURE__*/ (0, $fI0QS$reactjsxruntime.jsx)((0, $25d06cf8d4e72761$exports.Button), { 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($aa16d99d2c9699f6$exports)))['toast-action'] : undefined, styles: " IM1 zzz3eAe1 yzz3eAe1 Bzz3eAe1 Azz3eAe1", children: toast.content.actionLabel }) ] }), /*#__PURE__*/ (0, $fI0QS$reactjsxruntime.jsx)((0, $2f907cb84c6e9e75$exports.CloseButton), { staticColor: "white", UNSAFE_className: ctx && isMain ? (0, ($parcel$interopDefault($aa16d99d2c9699f6$exports)))['toast-close'] : undefined }) ] }); } function $f1992060af7549d9$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, $fI0QS$reactariautils.isWebKit)()) { let translate = el.style.translate; el.style.translate = ''; el.offsetHeight; el.style.translate = translate; } } //# sourceMappingURL=Toast.cjs.map