UNPKG

@react-spectrum/s2

Version:
523 lines (499 loc) 24.1 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} 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. */ 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]); let viewTransition = document.startViewTransition({ update: ()=>(0, $jeXmD$flushSync)(fn), types: [ (0, ($parcel$interopDefault($jeXmD$Toast_modulemjs)))[type] ] }); viewTransition.ready.catch(()=>{}); viewTransition.finished.then(()=>{ document.documentElement.classList.remove((0, ($parcel$interopDefault($jeXmD$Toast_modulemjs)))[type]); }); } 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 += ' _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 $6e462db325878d1a$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 $6e462db325878d1a$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 $6e462db325878d1a$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 $6e462db325878d1a$var$toastContent = " sd91 Ue91 qe91 ea91 zk52g2d91 yk52g2d91 Bk52g2d91 Ak52g2d91 ri91 ZJ91"; const $6e462db325878d1a$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 $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); 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'] + " _Pb91 Wr91 _lr91 _Ar91 _zr91 g891", onClick: collapse }), /*#__PURE__*/ (0, $jeXmD$jsx)($6e462db325878d1a$var$SpectrumToastList, { placement: placement, align: align }), /*#__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 }) { 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(); }); let reduceMotion = (0, $jeXmD$useMediaQuery)('(prefers-reduced-motion)'); 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 }) }); 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: " 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($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: " IM91 zzz3eAe91 yzz3eAe91 Bzz3eAe91 Azz3eAe91", 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 }) ] }); } export {$6e462db325878d1a$export$f1f8569633bbbec4 as ToastQueue, $6e462db325878d1a$export$f2815235e76a62b9 as ToastContainer, $6e462db325878d1a$export$f9b1e1c88454fa08 as SpectrumToast}; //# sourceMappingURL=Toast.mjs.map