UNPKG

@react-spectrum/s2

Version:
529 lines (503 loc) 24.1 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. */ 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]); let viewTransition = document.startViewTransition({ update: ()=>(0, $fI0QS$reactdom.flushSync)(fn), types: [ (0, ($parcel$interopDefault($aa16d99d2c9699f6$exports)))[type] ] }); viewTransition.ready.catch(()=>{}); viewTransition.finished.then(()=>{ document.documentElement.classList.remove((0, ($parcel$interopDefault($aa16d99d2c9699f6$exports)))[type]); }); } 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 += ' _Lf91'; else rules += ' _Le91'; rules += ' Oh91'; rules += ' _Mc91'; rules += ' _Kd91'; rules += ' sd91'; if (props.placement === "bottom") rules += ' _tb91'; else if (props.placement === "top") rules += ' _ta91'; rules += ' _Pb91'; rules += ' _Ar91'; rules += ' _zr91'; rules += ' ZJ91'; if (props.placement === "top") { if (props.isExpanded) rules += ' Wr91'; else rules += ' Wu91'; } if (props.placement === "bottom") { if (props.isExpanded) rules += ' _lr91'; else rules += ' _lu91'; } if (props.align === "end") rules += ' IM91'; else if (props.align === "center") rules += ' IM91'; else if (props.align === "start") rules += ' IC91'; if (props.align === "end") rules += ' HC91'; else if (props.align === "center") rules += ' HM91'; else if (props.align === "start") rules += ' HM91'; rules += ' _oa91'; rules += ' Kb91'; rules += ' oc91'; rules += ' nc91'; rules += ' kc91'; rules += ' jc91'; return rules; }; const $f1992060af7549d9$var$toastList = function anonymous(props) { let rules = " "; rules += ' _Pc91'; rules += ' _ub91'; rules += ' sd91'; rules += ' Ue91'; rules += ' qe91'; if (props.placement === "bottom") rules += ' _tb91'; else if (props.placement === "top") rules += ' _ta91'; rules += ' _oa91'; rules += ' Jy91'; rules += ' Gy91'; if (props.isExpanded) { if (props.placement === "bottom") rules += ' Tt91'; else if (props.placement === "top") rules += ' Tf91'; } if (props.isExpanded) { if (props.placement === "bottom") rules += ' Qf91'; else if (props.placement === "top") rules += ' Qt91'; } if (props.isExpanded) rules += ' St91'; else rules += ' Sd91'; if (props.isExpanded) rules += ' Rt91'; else rules += ' Rd91'; if (props.isExpanded) rules += ' Ic91'; else rules += ' Iy91'; if (props.isExpanded) rules += ' Hc91'; else rules += ' Hy91'; if (props.isExpanded) rules += ' _Na91'; if (props.isExpanded) rules += ' Pa91'; return rules; }; const $f1992060af7549d9$var$toastStyle = function anonymous(props) { let rules = " "; if (props.isFocusVisible) rules += ' _Lf91'; else rules += ' _Le91'; if (props.isExpanded) rules += ' Ok91'; else rules += ' Oh91'; rules += ' _Mc91'; rules += ' _Kd91'; rules += ' sd91'; rules += ' Ul91'; rules += ' ql91'; rules += ' Sf91'; rules += ' Rt91'; rules += ' Te91'; rules += ' Qe91'; rules += ' oc91'; rules += ' nc91'; rules += ' kc91'; rules += ' jc91'; rules += ' Mj91'; rules += ' -Zn3gsb-L0uB6Qb91'; rules += ' LksArhc91'; rules += ' _oa91'; rules += ' _va91'; rules += ' ug91'; rules += ' uch91'; rules += ' udi91'; rules += ' uea91'; rules += ' ugb91'; rules += ' uhd91'; rules += ' uje91'; rules += ' uic91'; rules += ' vd91'; rules += ' vsf91'; rules += ' wb91'; rules += ' xb91'; rules += ' _xa91'; rules += ' _Fa91'; rules += ' _Ffb91'; rules += ' px91'; if (props.variant === "negative") rules += ' gB91'; else if (props.variant === "positive") rules += ' g191'; else if (props.variant === "info") rules += ' g291'; else if (props.variant === "neutral") rules += ' g691'; rules += ' -_8sjo0b-t5ZbAob91'; if (props.isExpanded) rules += ' _nd91'; else rules += ' _nb91'; return rules; }; const $f1992060af7549d9$var$toastBody = function anonymous(props) { let rules = " "; if (props.isSingle) rules += ' sd91'; else rules += ' se91'; rules += ' D4w1sLc91'; rules += ' CWLL0Xb91'; rules += ' _ub91'; rules += ' _wb91'; rules += ' eb91'; rules += ' qj91'; rules += ' Ue91'; return rules; }; const $f1992060af7549d9$var$toastContent = " sd91 Ue91 qe91 ea91 zk52g2d91 yk52g2d91 Bk52g2d91 Ak52g2d91 ri91 ZJ91"; const $f1992060af7549d9$var$controls = function anonymous(props) { let rules = " "; rules += ' _pb91'; if (props.isExpanded) rules += ' sd91'; else rules += ' sk91'; rules += ' _Cb91'; rules += ' Ue91'; rules += ' qe91'; if (props.isExpanded) rules += ' _Ib91'; else rules += ' _Ia91'; 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); 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'] + " _Pb91 Wr91 _lr91 _Ar91 _zr91 g891", onClick: collapse }), /*#__PURE__*/ (0, $fI0QS$reactjsxruntime.jsx)($f1992060af7549d9$var$SpectrumToastList, { placement: placement, align: align }), /*#__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 }) { 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(); }); let reduceMotion = (0, $fI0QS$reactspectrumutils.useMediaQuery)('(prefers-reduced-motion)'); 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 }) }); 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: " zPgKvMe91 yPgKvMe91 BPgKvMe91 APgKvMe91", // 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: " IM91 zzz3eAe91 yzz3eAe91 Bzz3eAe91 Azz3eAe91", 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 }) ] }); } //# sourceMappingURL=Toast.cjs.map