scrollsheet
Advanced tools
Sorry, the diff of this file is too big to display
| "use client"; | ||
| import { c as prefersReducedMotion, h as warnOnce, n as useCloseWatcher, r as createStyleInjector, t as cn } from "./cn-CActSFFr.mjs"; | ||
| import * as React from "react"; | ||
| import { Fragment, jsx, jsxs } from "react/jsx-runtime"; | ||
| import { createPortal } from "react-dom"; | ||
| //#region packages/scrollsheet/src/toast/state.ts | ||
| let toasts = []; | ||
| const listeners = new Set(); | ||
| let uid = 0; | ||
| const MAX_HISTORY_SIZE = 100; | ||
| let history = []; | ||
| function trimHistory() { | ||
| let toRemove = history.length - MAX_HISTORY_SIZE; | ||
| if (toRemove <= 0) return; | ||
| const liveIds = new Set(toasts.map((t) => t.id)); | ||
| history = history.filter((record) => { | ||
| if (toRemove > 0 && !liveIds.has(record.id)) { | ||
| toRemove -= 1; | ||
| return false; | ||
| } | ||
| return true; | ||
| }); | ||
| } | ||
| function recordHistory(record) { | ||
| const index = history.findIndex((t) => t.id === record.id); | ||
| if (index === -1) { | ||
| history = [...history, record]; | ||
| trimHistory(); | ||
| return; | ||
| } | ||
| history = history.map((t, i) => i === index ? record : t); | ||
| } | ||
| function nextId() { | ||
| uid += 1; | ||
| return uid; | ||
| } | ||
| function publish() { | ||
| for (const listener of listeners) listener(); | ||
| } | ||
| function subscribe(listener) { | ||
| listeners.add(listener); | ||
| return () => listeners.delete(listener); | ||
| } | ||
| function getSnapshot() { | ||
| return toasts; | ||
| } | ||
| const EMPTY_TOASTS = []; | ||
| function getServerSnapshot() { | ||
| return EMPTY_TOASTS; | ||
| } | ||
| function upsert(id, patch, fallbackType) { | ||
| const resolvedId = id ?? nextId(); | ||
| const index = toasts.findIndex((t) => t.id === resolvedId); | ||
| const type = patch.type ?? fallbackType; | ||
| let record; | ||
| if (index === -1) { | ||
| record = { | ||
| dismissible: true, | ||
| ...patch, | ||
| id: resolvedId, | ||
| type, | ||
| createdAt: Date.now() | ||
| }; | ||
| toasts = [...toasts, record]; | ||
| } else { | ||
| record = { | ||
| ...toasts[index], | ||
| ...patch, | ||
| id: resolvedId, | ||
| type | ||
| }; | ||
| toasts = toasts.map((t, i) => i === index ? record : t); | ||
| } | ||
| recordHistory(record); | ||
| publish(); | ||
| return resolvedId; | ||
| } | ||
| function dismiss(id) { | ||
| if (id === void 0) { | ||
| const swept = new Set(toasts); | ||
| for (const t of swept) { | ||
| if (!toasts.includes(t)) continue; | ||
| t.onDismiss?.(t); | ||
| } | ||
| toasts = toasts.filter((t) => !swept.has(t)); | ||
| publish(); | ||
| return; | ||
| } | ||
| const existing = toasts.find((t) => t.id === id); | ||
| if (!existing) return id; | ||
| existing.onDismiss?.(existing); | ||
| toasts = toasts.filter((t) => t !== existing); | ||
| publish(); | ||
| return id; | ||
| } | ||
| function expire(id) { | ||
| const existing = toasts.find((t) => t.id === id); | ||
| if (!existing) return; | ||
| existing.onAutoClose?.(existing); | ||
| toasts = toasts.filter((t) => t !== existing); | ||
| publish(); | ||
| } | ||
| function baseToast(message, data) { | ||
| return upsert(data?.id, { | ||
| ...data, | ||
| title: message | ||
| }, "default"); | ||
| } | ||
| function withType(type) { | ||
| return (message, data) => upsert(data?.id, { | ||
| ...data, | ||
| title: message | ||
| }, type); | ||
| } | ||
| const toastImpl = baseToast; | ||
| toastImpl.success = withType("success"); | ||
| toastImpl.error = withType("error"); | ||
| toastImpl.info = withType("info"); | ||
| toastImpl.warning = withType("warning"); | ||
| toastImpl.loading = withType("loading"); | ||
| toastImpl.message = withType("default"); | ||
| toastImpl.custom = (jsx, data) => { | ||
| const id = data?.id ?? nextId(); | ||
| return upsert(id, { | ||
| ...data, | ||
| jsx: jsx(id) | ||
| }, "default"); | ||
| }; | ||
| function isHttpResponse(value) { | ||
| return typeof value === "object" && value !== null && "ok" in value && typeof value.ok === "boolean" && "status" in value && typeof value.status === "number"; | ||
| } | ||
| function isPromiseExtendedResult(value) { | ||
| return typeof value === "object" && value !== null && !React.isValidElement(value); | ||
| } | ||
| function applyPromiseSettlement(id, resolved, fallbackTitle, description, type) { | ||
| if (isPromiseExtendedResult(resolved)) { | ||
| const { message, ...rest } = resolved; | ||
| upsert(id, { | ||
| description, | ||
| ...rest, | ||
| title: message ?? fallbackTitle, | ||
| type | ||
| }, type); | ||
| return; | ||
| } | ||
| upsert(id, { | ||
| title: resolved ?? fallbackTitle, | ||
| description, | ||
| type | ||
| }, type); | ||
| } | ||
| function promiseImpl(promiseOrFn, data) { | ||
| const { loading, success, error, finally: onFinally, description, ...rest } = data; | ||
| const id = upsert(data.id, { | ||
| ...rest, | ||
| description: typeof description === "function" ? void 0 : description, | ||
| title: loading, | ||
| type: "loading" | ||
| }, "loading"); | ||
| const settle = typeof promiseOrFn === "function" ? promiseOrFn() : promiseOrFn; | ||
| let settled; | ||
| const chain = settle.then(async (result) => { | ||
| if (isHttpResponse(result) && !result.ok) { | ||
| settled = { | ||
| ok: false, | ||
| reason: result | ||
| }; | ||
| const statusMessage = `HTTP error! status: ${result.status}`; | ||
| const resolvedDescription = typeof description === "function" ? await description(statusMessage) : description; | ||
| const resolved = typeof error === "function" ? await error(statusMessage) : error; | ||
| applyPromiseSettlement(id, resolved, "Error", resolvedDescription, "error"); | ||
| return; | ||
| } | ||
| if (result instanceof Error) { | ||
| settled = { | ||
| ok: false, | ||
| reason: result | ||
| }; | ||
| const resolvedDescription = typeof description === "function" ? await description(result) : description; | ||
| const resolved = typeof error === "function" ? await error(result) : error; | ||
| applyPromiseSettlement(id, resolved, "Error", resolvedDescription, "error"); | ||
| return; | ||
| } | ||
| settled = { | ||
| ok: true, | ||
| value: result | ||
| }; | ||
| const resolvedDescription = typeof description === "function" ? await description(result) : description; | ||
| const resolved = typeof success === "function" ? await success(result) : success; | ||
| applyPromiseSettlement(id, resolved, "Success", resolvedDescription, "success"); | ||
| }).catch(async (err) => { | ||
| settled = { | ||
| ok: false, | ||
| reason: err | ||
| }; | ||
| const resolvedDescription = typeof description === "function" ? await description(err) : description; | ||
| const resolved = typeof error === "function" ? await error(err) : error; | ||
| applyPromiseSettlement(id, resolved, "Error", resolvedDescription, "error"); | ||
| }).finally(() => onFinally?.()); | ||
| const unwrap = () => chain.then(() => { | ||
| if (!settled) throw new Error("scrollsheet toast.promise: chain settled with no outcome"); | ||
| if (settled.ok) return settled.value; | ||
| throw settled.reason; | ||
| }); | ||
| return Object.assign(id, { unwrap }); | ||
| } | ||
| toastImpl.promise = promiseImpl; | ||
| toastImpl.dismiss = dismiss; | ||
| toastImpl.getToasts = () => toasts; | ||
| toastImpl.getHistory = () => history; | ||
| const toast = toastImpl; | ||
| function useSonner() { | ||
| return { toasts: React.useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot) }; | ||
| } | ||
| const useToasts = useSonner; | ||
| function selectToasterToasts(all, toasterId) { | ||
| return all.filter((t) => toasterId === void 0 ? t.toasterId === void 0 : t.toasterId === toasterId); | ||
| } | ||
| function selectPositionToasts(all, position, defaultPosition) { | ||
| return all.filter((t) => t.position === void 0 ? position === defaultPosition : t.position === position); | ||
| } | ||
| function selectToastWindow(toasts, visibleToasts) { | ||
| const newestFirst = [...toasts].reverse(); | ||
| return { | ||
| visible: newestFirst.slice(0, visibleToasts), | ||
| hidden: newestFirst.slice(visibleToasts) | ||
| }; | ||
| } | ||
| //#endregion | ||
| //#region packages/scrollsheet/src/toast/toast-styles.ts | ||
| const { injectDocument, injectShadowRoot } = createStyleInjector(`.scrollsheet-toast{border-radius:var(--scrollsheet-toast-radius,14px);color:#18181b;background:#fff;border:1px solid #00000014;align-items:flex-start;gap:10px;padding:16px;font-family:ui-sans-serif,system-ui,-apple-system,Segoe UI,sans-serif;font-size:13px;display:flex;position:relative;box-shadow:0 4px 12px #0000001a}.scrollsheet-toast[data-scrollsheet-custom]{box-shadow:none;background:0 0;border:none;padding:0}.scrollsheet-toast-icon{color:#fff;background:#6b7280;border-radius:50%;flex:none;place-items:center;width:18px;height:18px;margin-top:1px;font-size:11px;line-height:1;display:grid}.scrollsheet-toast-icon[data-type=success]{background:#22c55e}.scrollsheet-toast-icon[data-type=error]{background:#ef4444}.scrollsheet-toast-icon[data-type=warning]{background:#f59e0b}.scrollsheet-toast-icon[data-type=info]{background:#3b82f6}.scrollsheet-toast-icon[data-type=loading]{background:0 0}.scrollsheet-toast-spinner{border:2px solid #00000026;border-top-color:#0000008c;border-radius:50%;width:14px;height:14px;animation:.6s linear infinite scrollsheet-toast-spin}@keyframes scrollsheet-toast-spin{to{transform:rotate(360deg)}}@media (prefers-reduced-motion:reduce){.scrollsheet-toast-spinner{animation-duration:1.6s}}.scrollsheet-toast-body{flex-direction:column;flex:1;gap:4px;min-width:0;display:flex}.scrollsheet-toast-title{font-size:13px;font-weight:500;line-height:1.35}.scrollsheet-toast-description{color:#0009;font-size:13px;line-height:1.4}.scrollsheet-toast-actions{flex:none;align-items:center;gap:6px;display:flex}.scrollsheet-toast-action,.scrollsheet-toast-cancel{cursor:pointer;border:none;border-radius:4px;height:24px;padding:0 8px;font-size:12px;font-weight:500}.scrollsheet-toast-action{color:#fff;background:#18181b}.scrollsheet-toast-cancel{color:#18181b;background:#0000000f}.scrollsheet-toast-close{color:#00000080;cursor:pointer;background:0 0;border:none;border-radius:50%;flex:none;place-items:center;width:20px;height:20px;padding:0;font-size:14px;line-height:1;display:grid}.scrollsheet-toast-close:hover{background:#0000000f}[data-scrollsheet-toaster]{z-index:2147483647;width:min(356px, calc(100vw - 2 * var(--scrollsheet-toast-offset,var(--sonner-offset,24px))));outline:none;margin:0;padding:0;list-style:none;transition:transform .4s;position:fixed}[data-scrollsheet-toaster][data-x-position=right]{right:var(--scrollsheet-toast-offset,var(--sonner-offset,24px))}[data-scrollsheet-toaster][data-x-position=left]{left:var(--scrollsheet-toast-offset,var(--sonner-offset,24px))}[data-scrollsheet-toaster][data-x-position=center]{left:50%;transform:translate(-50%)}[data-scrollsheet-toaster][data-y-position=top]{top:var(--scrollsheet-toast-offset,var(--sonner-offset,24px))}[data-scrollsheet-toaster][data-y-position=bottom]{bottom:var(--scrollsheet-toast-offset,var(--sonner-offset,24px))}.scrollsheet-toast{--scrollsheet-toast-lift:-1;--scrollsheet-toast-y:translateY(100%);opacity:0;transform:var(--scrollsheet-toast-y);touch-action:none;box-sizing:border-box;overflow-wrap:anywhere;transition:transform .25s,opacity .25s,height .25s;position:absolute;left:0;right:0}.scrollsheet-toast[data-y-position=top]{--scrollsheet-toast-lift:1;--scrollsheet-toast-y:translateY(-100%);top:0}.scrollsheet-toast[data-y-position=bottom]{--scrollsheet-toast-lift:-1;--scrollsheet-toast-y:translateY(100%);bottom:0}.scrollsheet-toast[data-mounted]{--scrollsheet-toast-y:translateY(0);opacity:1}.scrollsheet-toast[data-expanded=false][data-front=false]{--scrollsheet-toast-y:translateY(calc(var(--scrollsheet-toast-lift) * var(--scrollsheet-toast-gap,var(--sonner-gap,14px)) * var(--scrollsheet-toast-toasts-before,var(--sonner-toasts-before,0)))) scale(calc(1 - var(--scrollsheet-toast-toasts-before,var(--sonner-toasts-before,0)) * .05));height:var(--scrollsheet-toast-front-height,var(--sonner-front-height,auto));overflow:hidden}.scrollsheet-toast[data-expanded=false][data-front=false]>*{opacity:0}.scrollsheet-toast[data-mounted][data-expanded=true]{--scrollsheet-toast-y:translateY(calc(var(--scrollsheet-toast-lift) * var(--scrollsheet-toast-stack-offset,var(--sonner-stack-offset,0px))));height:var(--scrollsheet-toast-initial-height,var(--sonner-initial-height,auto))}.scrollsheet-toast[data-visible=false]{opacity:0;pointer-events:none}.scrollsheet-toast[data-removed]{pointer-events:none}.scrollsheet-toast[data-removed][data-front=true]{--scrollsheet-toast-y:translateY(calc(var(--scrollsheet-toast-lift) * -100%));opacity:0}.scrollsheet-toast[data-removed][data-front=false][data-expanded=true]{--scrollsheet-toast-y:translateY(calc(var(--scrollsheet-toast-lift) * var(--scrollsheet-toast-stack-offset,var(--sonner-stack-offset,0px)) + var(--scrollsheet-toast-lift) * -100%));opacity:0}.scrollsheet-toast[data-removed][data-front=false][data-expanded=false]{--scrollsheet-toast-y:translateY(40%);opacity:0}.scrollsheet-toast[data-swiping=true]{transform:var(--scrollsheet-toast-y) translateY(var(--scrollsheet-toast-swipe-y,var(--sonner-swipe-y,0px))) translateX(var(--scrollsheet-toast-swipe-x,var(--sonner-swipe-x,0px)));transition:none}.scrollsheet-toast[data-swiped=true]{-webkit-user-select:none;user-select:none}.scrollsheet-toast[data-swipe-out=true]{animation:.2s ease-out forwards scrollsheet-toast-swipe-out}.scrollsheet-toast[data-swipe-direction=left]{--scrollsheet-toast-swipe-out-x:-1}.scrollsheet-toast[data-swipe-direction=right]{--scrollsheet-toast-swipe-out-x:1}.scrollsheet-toast[data-swipe-direction=up]{--scrollsheet-toast-swipe-out-y:-1}.scrollsheet-toast[data-swipe-direction=down]{--scrollsheet-toast-swipe-out-y:1}@keyframes scrollsheet-toast-swipe-out{0%{transform:var(--scrollsheet-toast-y) translateY(var(--scrollsheet-toast-swipe-y,var(--sonner-swipe-y,0px))) translateX(var(--scrollsheet-toast-swipe-x,var(--sonner-swipe-x,0px)));opacity:1}to{transform:var(--scrollsheet-toast-y) translateY(calc(var(--scrollsheet-toast-swipe-y,var(--sonner-swipe-y,0px)) + var(--scrollsheet-toast-swipe-out-y,var(--sonner-swipe-out-y,0)) * 100%)) translateX(calc(var(--scrollsheet-toast-swipe-x,var(--sonner-swipe-x,0px)) + var(--scrollsheet-toast-swipe-out-x,var(--sonner-swipe-out-x,0)) * 100%));opacity:0}}@media (prefers-reduced-motion:reduce){[data-scrollsheet-toaster],.scrollsheet-toast{transition:none!important;animation:none!important}}@media (max-width:600px){[data-scrollsheet-toaster]{right:var(--scrollsheet-toast-mobile-offset,var(--sonner-mobile-offset,16px));left:var(--scrollsheet-toast-mobile-offset,var(--sonner-mobile-offset,16px));width:100%}[data-scrollsheet-toaster] .scrollsheet-toast{width:calc(100% - var(--scrollsheet-toast-mobile-offset,var(--sonner-mobile-offset,16px)) * 2);left:0;right:0}[data-scrollsheet-toaster][data-x-position=left]{left:var(--scrollsheet-toast-mobile-offset,var(--sonner-mobile-offset,16px))}[data-scrollsheet-toaster][data-y-position=bottom]{bottom:var(--scrollsheet-toast-mobile-offset,var(--sonner-mobile-offset,16px))}[data-scrollsheet-toaster][data-y-position=top]{top:var(--scrollsheet-toast-mobile-offset,var(--sonner-mobile-offset,16px))}[data-scrollsheet-toaster][data-x-position=center]{left:var(--scrollsheet-toast-mobile-offset,var(--sonner-mobile-offset,16px));right:var(--scrollsheet-toast-mobile-offset,var(--sonner-mobile-offset,16px));transform:none}}`, "data-sonner-toast-styles"); | ||
| function injectToastStyles(nonce) { | ||
| injectDocument(nonce); | ||
| } | ||
| function injectToastStylesInto(root, nonce) { | ||
| injectShadowRoot(root, nonce); | ||
| } | ||
| //#endregion | ||
| //#region packages/scrollsheet/src/toast/toaster.tsx | ||
| function resolveVisibleToasts(count) { | ||
| if (count === void 0) return 3; | ||
| return Math.max(1, Math.floor(count)); | ||
| } | ||
| function useMountedFlag() { | ||
| const [mounted, setMounted] = React.useState(false); | ||
| React.useEffect(() => { | ||
| if (prefersReducedMotion()) { | ||
| setMounted(true); | ||
| return; | ||
| } | ||
| let raf2 = 0; | ||
| const raf1 = requestAnimationFrame(() => { | ||
| raf2 = requestAnimationFrame(() => setMounted(true)); | ||
| }); | ||
| return () => { | ||
| cancelAnimationFrame(raf1); | ||
| cancelAnimationFrame(raf2); | ||
| }; | ||
| }, []); | ||
| return mounted; | ||
| } | ||
| function useIsDocumentHidden() { | ||
| const [hidden, setHidden] = React.useState(() => typeof document !== "undefined" && document.hidden); | ||
| React.useEffect(() => { | ||
| const onVisibilityChange = () => setHidden(document.hidden); | ||
| document.addEventListener("visibilitychange", onVisibilityChange); | ||
| return () => document.removeEventListener("visibilitychange", onVisibilityChange); | ||
| }, []); | ||
| return hidden; | ||
| } | ||
| const EMPTY_RECORDS = []; | ||
| const EMPTY_MAP = new Map(); | ||
| function toIdMap(records) { | ||
| return new Map(records.map((r) => [r.id, r])); | ||
| } | ||
| function sameIds(a, b) { | ||
| if (a.size !== b.size) return false; | ||
| for (const id of a.keys()) if (!b.has(id)) return false; | ||
| return true; | ||
| } | ||
| function useToastExit(live, queued = EMPTY_RECORDS, exitMs = 220) { | ||
| const [exiting, setExiting] = React.useState(() => EMPTY_MAP); | ||
| const [prevLive, setPrevLive] = React.useState(() => toIdMap(live)); | ||
| const timersRef = React.useRef(new Map()); | ||
| const liveIds = new Set(live.map((r) => r.id)); | ||
| let renderExiting = exiting; | ||
| if (!sameIds(prevLive, liveIds)) { | ||
| const queuedIds = new Set(queued.map((r) => r.id)); | ||
| const justRemoved = []; | ||
| for (const [id, record] of prevLive) { | ||
| if (liveIds.has(id)) continue; | ||
| if (queuedIds.has(id)) continue; | ||
| justRemoved.push(record); | ||
| } | ||
| setPrevLive(toIdMap(live)); | ||
| if (justRemoved.length > 0) { | ||
| const next = new Map(exiting); | ||
| for (const record of justRemoved) next.set(record.id, record); | ||
| renderExiting = next; | ||
| setExiting(next); | ||
| } | ||
| } | ||
| React.useEffect(() => { | ||
| const reduced = prefersReducedMotion(); | ||
| for (const [id] of exiting) { | ||
| if (timersRef.current.has(id)) continue; | ||
| const timer = setTimeout(() => { | ||
| timersRef.current.delete(id); | ||
| setExiting((prev) => { | ||
| if (!prev.has(id)) return prev; | ||
| const next = new Map(prev); | ||
| next.delete(id); | ||
| return next; | ||
| }); | ||
| }, reduced ? 0 : exitMs); | ||
| timersRef.current.set(id, timer); | ||
| } | ||
| for (const [id, timer] of timersRef.current) if (!exiting.has(id)) { | ||
| clearTimeout(timer); | ||
| timersRef.current.delete(id); | ||
| } | ||
| }, [exiting, exitMs]); | ||
| React.useEffect(() => { | ||
| const timers = timersRef.current; | ||
| return () => { | ||
| for (const timer of timers.values()) clearTimeout(timer); | ||
| timers.clear(); | ||
| }; | ||
| }, []); | ||
| const rows = live.map((record) => ({ | ||
| record, | ||
| exiting: false | ||
| })); | ||
| for (const [id, record] of renderExiting) if (!liveIds.has(id)) rows.push({ | ||
| record, | ||
| exiting: true | ||
| }); | ||
| return rows; | ||
| } | ||
| //#endregion | ||
| //#region packages/scrollsheet/src/toast/shell/shell-selectors.ts | ||
| function splitPosition(position) { | ||
| const [y, x] = position.split("-"); | ||
| return { | ||
| y, | ||
| x | ||
| }; | ||
| } | ||
| function computePossiblePositions(defaultPosition, toasts) { | ||
| const seen = new Set([defaultPosition]); | ||
| for (const t of toasts) if (t.position) seen.add(t.position); | ||
| return [...seen]; | ||
| } | ||
| function computeRowOffsets(newestFirst, heights, gap) { | ||
| let cumulativeHeight = 0; | ||
| return newestFirst.map((record, index) => { | ||
| const stackOffset = index * gap + cumulativeHeight; | ||
| cumulativeHeight += heights.get(record.id) ?? 0; | ||
| return { | ||
| id: record.id, | ||
| index, | ||
| toastsBefore: index, | ||
| stackOffset | ||
| }; | ||
| }); | ||
| } | ||
| function findNewestDismissible(toasts) { | ||
| for (let i = toasts.length - 1; i >= 0; i -= 1) { | ||
| const t = toasts[i]; | ||
| if (t && t.dismissible !== false) return t; | ||
| } | ||
| } | ||
| const SWIPE_VELOCITY_THRESHOLD = .11; | ||
| function getDefaultSwipeDirections(position) { | ||
| const { y, x } = splitPosition(position); | ||
| const directions = [y]; | ||
| if (x === "left" || x === "right") directions.push(x); | ||
| return directions; | ||
| } | ||
| function lockSwipeAxis(dx, dy) { | ||
| if (Math.abs(dx) <= 1 && Math.abs(dy) <= 1) return null; | ||
| return Math.abs(dx) > Math.abs(dy) ? "x" : "y"; | ||
| } | ||
| function dampenSwipeDelta(delta) { | ||
| const dampened = delta * (1 / (1.5 + Math.abs(delta) / 20)); | ||
| return Math.abs(dampened) < Math.abs(delta) ? dampened : delta; | ||
| } | ||
| function computeSwipeAxisAmount(axis, delta, directions) { | ||
| const negativeDir = axis === "x" ? "left" : "top"; | ||
| const positiveDir = axis === "x" ? "right" : "bottom"; | ||
| if (!(directions.includes(negativeDir) || directions.includes(positiveDir))) return 0; | ||
| return directions.includes(negativeDir) && delta < 0 || directions.includes(positiveDir) && delta > 0 ? delta : dampenSwipeDelta(delta); | ||
| } | ||
| function isSwipeReleaseAllowed(axis, amount, directions) { | ||
| if (axis === "x") return directions.includes(amount > 0 ? "right" : "left"); | ||
| return directions.includes(amount > 0 ? "bottom" : "top"); | ||
| } | ||
| function shouldDismissOnSwipeRelease(amount, velocity, thresholdPx = 45, velocityThreshold = SWIPE_VELOCITY_THRESHOLD) { | ||
| return Math.abs(amount) >= thresholdPx || velocity > velocityThreshold; | ||
| } | ||
| function resolveSwipeOutDirection(axis, amount) { | ||
| if (axis === "x") return amount > 0 ? "right" : "left"; | ||
| return amount > 0 ? "down" : "up"; | ||
| } | ||
| //#endregion | ||
| //#region packages/scrollsheet/src/toast/shell/use-toast-swipe.ts | ||
| const ZERO_AMOUNT = { | ||
| x: 0, | ||
| y: 0 | ||
| }; | ||
| function useToastSwipe(elRef, enabled, directions, onSwipeDismiss) { | ||
| const draggingRef = React.useRef(false); | ||
| const axisRef = React.useRef(null); | ||
| const startRef = React.useRef(ZERO_AMOUNT); | ||
| const amountRef = React.useRef(ZERO_AMOUNT); | ||
| const dragStartRef = React.useRef(0); | ||
| const onSwipeDismissRef = React.useRef(onSwipeDismiss); | ||
| onSwipeDismissRef.current = onSwipeDismiss; | ||
| const onPointerDown = React.useCallback((event) => { | ||
| if (!enabled || event.button !== 0) return; | ||
| if (event.target.tagName === "BUTTON") return; | ||
| const el = elRef.current; | ||
| if (!el) return; | ||
| draggingRef.current = true; | ||
| axisRef.current = null; | ||
| amountRef.current = ZERO_AMOUNT; | ||
| startRef.current = { | ||
| x: event.clientX, | ||
| y: event.clientY | ||
| }; | ||
| dragStartRef.current = Date.now(); | ||
| el.setPointerCapture(event.pointerId); | ||
| el.setAttribute("data-swiping", "true"); | ||
| }, [enabled, elRef]); | ||
| const onPointerMove = React.useCallback((event) => { | ||
| if (!draggingRef.current) return; | ||
| const el = elRef.current; | ||
| if (!el) return; | ||
| if ((window.getSelection?.()?.toString().length ?? 0) > 0) return; | ||
| const start = startRef.current; | ||
| const dx = event.clientX - start.x; | ||
| const dy = event.clientY - start.y; | ||
| if (axisRef.current === null) axisRef.current = lockSwipeAxis(dx, dy); | ||
| const axis = axisRef.current; | ||
| if (axis === null) return; | ||
| const resolved = computeSwipeAxisAmount(axis, axis === "x" ? dx : dy, directions); | ||
| const amount = axis === "x" ? { | ||
| x: resolved, | ||
| y: 0 | ||
| } : { | ||
| x: 0, | ||
| y: resolved | ||
| }; | ||
| amountRef.current = amount; | ||
| if (resolved !== 0) el.setAttribute("data-swiped", "true"); | ||
| el.style.setProperty("--scrollsheet-toast-swipe-x", `${amount.x}px`); | ||
| el.style.setProperty("--scrollsheet-toast-swipe-y", `${amount.y}px`); | ||
| }, [elRef, directions]); | ||
| const release = React.useCallback(() => { | ||
| if (!draggingRef.current) return; | ||
| draggingRef.current = false; | ||
| const el = elRef.current; | ||
| const axis = axisRef.current; | ||
| axisRef.current = null; | ||
| if (!el) return; | ||
| if (axis === null) { | ||
| el.setAttribute("data-swiping", "false"); | ||
| return; | ||
| } | ||
| const amount = axis === "x" ? amountRef.current.x : amountRef.current.y; | ||
| const elapsed = Math.max(1, Date.now() - dragStartRef.current); | ||
| const velocity = Math.abs(amount) / elapsed; | ||
| if (isSwipeReleaseAllowed(axis, amount, directions) && shouldDismissOnSwipeRelease(amount, velocity)) { | ||
| const direction = resolveSwipeOutDirection(axis, amount); | ||
| el.setAttribute("data-swipe-direction", direction); | ||
| if (prefersReducedMotion()) { | ||
| el.setAttribute("data-swipe-out", "true"); | ||
| onSwipeDismissRef.current(); | ||
| return; | ||
| } | ||
| const onAnimEnd = () => { | ||
| el.removeEventListener("animationend", onAnimEnd); | ||
| onSwipeDismissRef.current(); | ||
| }; | ||
| el.addEventListener("animationend", onAnimEnd); | ||
| el.setAttribute("data-swipe-out", "true"); | ||
| return; | ||
| } | ||
| el.setAttribute("data-swiping", "false"); | ||
| el.setAttribute("data-swiped", "false"); | ||
| el.style.setProperty("--scrollsheet-toast-swipe-x", "0px"); | ||
| el.style.setProperty("--scrollsheet-toast-swipe-y", "0px"); | ||
| }, [elRef, directions]); | ||
| return { | ||
| onPointerDown, | ||
| onPointerMove, | ||
| onPointerUp: React.useCallback((event) => { | ||
| if (elRef.current?.hasPointerCapture(event.pointerId)) elRef.current.releasePointerCapture(event.pointerId); | ||
| release(); | ||
| }, [elRef, release]), | ||
| onPointerCancel: React.useCallback(() => release(), [release]) | ||
| }; | ||
| } | ||
| //#endregion | ||
| //#region packages/scrollsheet/src/toast/shell/toast-row.tsx | ||
| function ToastIcon({ type, icons, spinnerClassName }) { | ||
| if (type === "loading") { | ||
| if (icons?.loading) return jsx(Fragment, { children: icons.loading }); | ||
| return jsx("span", { className: spinnerClassName }); | ||
| } | ||
| switch (type) { | ||
| case "success": return jsx(Fragment, { children: icons?.success ?? "✓" }); | ||
| case "error": return jsx(Fragment, { children: icons?.error ?? "✕" }); | ||
| case "warning": return jsx(Fragment, { children: icons?.warning ?? "!" }); | ||
| case "info": return jsx(Fragment, { children: icons?.info ?? "i" }); | ||
| default: return null; | ||
| } | ||
| } | ||
| function ToastRow({ record, index, total, toastsBefore, stackOffset, height, frontHeight, visible, expanded, removed, yPosition, xPosition, closeButton, directions, onDismiss, observe, toasterClassNames, icons, toasterCloseButtonAriaLabel }) { | ||
| const role = record.type === "error" ? "alert" : "status"; | ||
| const mounted = useMountedFlag(); | ||
| const elRef = React.useRef(null); | ||
| const dismissible = record.dismissible !== false; | ||
| const isFront = index === 0; | ||
| const setRefs = React.useCallback((el) => { | ||
| elRef.current = el; | ||
| observe(record.id, el); | ||
| }, [observe, record.id]); | ||
| const swipeHandlers = useToastSwipe(elRef, !removed && dismissible && record.type !== "loading", directions, () => onDismiss(record)); | ||
| React.useEffect(() => { | ||
| const el = elRef.current; | ||
| if (el) el.inert = removed; | ||
| }, [removed]); | ||
| const style = { | ||
| "--scrollsheet-toast-toasts-before": toastsBefore, | ||
| "--scrollsheet-toast-stack-offset": `${stackOffset}px`, | ||
| "--scrollsheet-toast-front-height": frontHeight !== void 0 ? `${frontHeight}px` : "0px", | ||
| "--scrollsheet-toast-initial-height": height !== void 0 ? `${height}px` : "auto", | ||
| zIndex: Math.max(0, total - index) | ||
| }; | ||
| const rootClassName = cn("scrollsheet-toast", "sonner-toast", record.className, toasterClassNames?.toast, record.classNames?.toast, toasterClassNames?.default, toasterClassNames?.[record.type], record.classNames?.[record.type]); | ||
| const motionAttrs = { | ||
| "data-mounted": mounted ? "" : void 0, | ||
| "data-removed": removed ? "" : void 0, | ||
| "data-visible": visible ? "true" : "false", | ||
| "data-front": isFront ? "true" : "false", | ||
| "data-expanded": expanded ? "true" : "false", | ||
| "data-y-position": yPosition, | ||
| "data-x-position": xPosition, | ||
| "data-index": index, | ||
| "data-dismissible": dismissible ? "true" : "false", | ||
| "data-swiping": "false", | ||
| "data-swiped": "false", | ||
| "aria-hidden": removed ? "true" : void 0 | ||
| }; | ||
| if (record.jsx !== void 0) return jsx("div", { | ||
| ref: setRefs, | ||
| className: rootClassName, | ||
| "data-scrollsheet-toast": "", | ||
| "data-sonner-toast": "", | ||
| "data-scrollsheet-custom": "", | ||
| "data-sonner-custom": "", | ||
| "data-testid": record.testId, | ||
| role, | ||
| style, | ||
| ...motionAttrs, | ||
| ...swipeHandlers, | ||
| children: record.jsx | ||
| }); | ||
| const showCloseButton = closeButton && dismissible && record.type !== "loading"; | ||
| const hasActions = Boolean(record.action || record.cancel || showCloseButton); | ||
| const closeButtonAriaLabel = record.closeButtonAriaLabel ?? toasterCloseButtonAriaLabel ?? "Close toast"; | ||
| return jsxs("div", { | ||
| ref: setRefs, | ||
| className: rootClassName, | ||
| "data-scrollsheet-toast": "", | ||
| "data-sonner-toast": "", | ||
| "data-type": record.type, | ||
| "data-testid": record.testId, | ||
| role, | ||
| style, | ||
| ...motionAttrs, | ||
| ...swipeHandlers, | ||
| children: [ | ||
| jsx("span", { | ||
| className: cn("scrollsheet-toast-icon", "sonner-toast-icon", toasterClassNames?.icon, record.classNames?.icon), | ||
| "data-type": record.type, | ||
| "aria-hidden": "true", | ||
| children: record.icon ?? jsx(ToastIcon, { | ||
| type: record.type, | ||
| icons, | ||
| spinnerClassName: cn("scrollsheet-toast-spinner", "sonner-toast-spinner", toasterClassNames?.loader, record.classNames?.loader) | ||
| }) | ||
| }), | ||
| jsxs("div", { | ||
| className: cn("scrollsheet-toast-body", "sonner-toast-body", toasterClassNames?.content, record.classNames?.content), | ||
| children: [record.title !== void 0 && jsx("div", { | ||
| className: cn("scrollsheet-toast-title", "sonner-toast-title", toasterClassNames?.title, record.classNames?.title), | ||
| children: record.title | ||
| }), record.description !== void 0 && jsx("div", { | ||
| className: cn("scrollsheet-toast-description", "sonner-toast-description", toasterClassNames?.description, record.classNames?.description), | ||
| children: record.description | ||
| })] | ||
| }), | ||
| hasActions && jsxs("div", { | ||
| className: "scrollsheet-toast-actions sonner-toast-actions", | ||
| children: [ | ||
| record.cancel && jsx("button", { | ||
| type: "button", | ||
| className: cn("scrollsheet-toast-cancel", "sonner-toast-cancel", toasterClassNames?.cancelButton, record.classNames?.cancelButton), | ||
| onClick: (event) => { | ||
| if (!dismissible) return; | ||
| record.cancel?.onClick(event); | ||
| onDismiss(record); | ||
| }, | ||
| children: record.cancel.label | ||
| }), | ||
| record.action && jsx("button", { | ||
| type: "button", | ||
| className: cn("scrollsheet-toast-action", "sonner-toast-action", toasterClassNames?.actionButton, record.classNames?.actionButton), | ||
| onClick: (event) => { | ||
| record.action?.onClick(event); | ||
| if (event.defaultPrevented) return; | ||
| onDismiss(record); | ||
| }, | ||
| children: record.action.label | ||
| }), | ||
| showCloseButton && jsx("button", { | ||
| type: "button", | ||
| className: cn("scrollsheet-toast-close", "sonner-toast-close", toasterClassNames?.closeButton, record.classNames?.closeButton), | ||
| "aria-label": closeButtonAriaLabel, | ||
| onClick: () => onDismiss(record), | ||
| children: icons?.close ?? "×" | ||
| }) | ||
| ] | ||
| }) | ||
| ] | ||
| }); | ||
| } | ||
| //#endregion | ||
| //#region packages/scrollsheet/src/toast/shell/use-toast-heights.ts | ||
| function useToastHeights() { | ||
| const [heights, setHeights] = React.useState(() => new Map()); | ||
| const observersRef = React.useRef(new Map()); | ||
| const setHeight = React.useCallback((id, height) => { | ||
| setHeights((prev) => prev.get(id) === height ? prev : new Map(prev).set(id, height)); | ||
| }, []); | ||
| const forget = React.useCallback((id) => { | ||
| observersRef.current.get(id)?.disconnect(); | ||
| observersRef.current.delete(id); | ||
| setHeights((prev) => { | ||
| if (!prev.has(id)) return prev; | ||
| const next = new Map(prev); | ||
| next.delete(id); | ||
| return next; | ||
| }); | ||
| }, []); | ||
| const observe = React.useCallback((id, el) => { | ||
| const observers = observersRef.current; | ||
| observers.get(id)?.disconnect(); | ||
| observers.delete(id); | ||
| if (!el || typeof ResizeObserver === "undefined") return; | ||
| const ro = new ResizeObserver(() => setHeight(id, el.getBoundingClientRect().height)); | ||
| ro.observe(el); | ||
| observers.set(id, ro); | ||
| setHeight(id, el.getBoundingClientRect().height); | ||
| }, [setHeight]); | ||
| React.useEffect(() => { | ||
| const observers = observersRef.current; | ||
| return () => { | ||
| for (const ro of observers.values()) ro.disconnect(); | ||
| observers.clear(); | ||
| }; | ||
| }, []); | ||
| return { | ||
| heights, | ||
| observe, | ||
| forget | ||
| }; | ||
| } | ||
| //#endregion | ||
| //#region packages/scrollsheet/src/toast/shell/toaster-shell.tsx | ||
| const DEFAULT_HOTKEY = ["altKey", "KeyT"]; | ||
| function PositionGroup({ position, defaultPosition, toasts, visibleToasts, gap, expanded, closeButton, swipeDirections, toastOptions, icons, className, baseStyle, registerListEl, dismissRow, onHoverEnter, onHoverLeave, onInteractingChange }) { | ||
| const directions = React.useMemo(() => swipeDirections ?? getDefaultSwipeDirections(position), [swipeDirections, position]); | ||
| const positionToasts = React.useMemo(() => selectPositionToasts(toasts, position, defaultPosition), [ | ||
| toasts, | ||
| position, | ||
| defaultPosition | ||
| ]); | ||
| const newestFirst = React.useMemo(() => [...positionToasts].reverse(), [positionToasts]); | ||
| const window_ = React.useMemo(() => selectToastWindow(positionToasts, visibleToasts), [positionToasts, visibleToasts]); | ||
| const visibleIds = React.useMemo(() => new Set(window_.visible.map((t) => t.id)), [window_]); | ||
| const { heights, observe, forget } = useToastHeights(); | ||
| const exitRows = useToastExit(newestFirst); | ||
| const exitingIds = React.useMemo(() => new Set(exitRows.filter((r) => r.exiting).map((r) => r.record.id)), [exitRows]); | ||
| React.useEffect(() => { | ||
| for (const id of exitingIds) forget(id); | ||
| }, [exitingIds, forget]); | ||
| const offsets = React.useMemo(() => computeRowOffsets(newestFirst, heights, gap), [ | ||
| newestFirst, | ||
| heights, | ||
| gap | ||
| ]); | ||
| const offsetById = React.useMemo(() => new Map(offsets.map((o) => [o.id, o])), [offsets]); | ||
| const lastOffsetRef = React.useRef(new Map()); | ||
| for (const o of offsets) lastOffsetRef.current.set(o.id, o); | ||
| if (lastOffsetRef.current.size > offsets.length) { | ||
| const keep = new Set([...offsets.map((o) => o.id), ...exitingIds]); | ||
| for (const id of [...lastOffsetRef.current.keys()]) if (!keep.has(id)) lastOffsetRef.current.delete(id); | ||
| } | ||
| const frontHeight = heights.get(newestFirst[0]?.id ?? ""); | ||
| const { y, x } = splitPosition(position); | ||
| const listRef = React.useCallback((el) => registerListEl(position, el), [registerListEl, position]); | ||
| if (exitRows.length === 0) return null; | ||
| return jsx("ol", { | ||
| ref: listRef, | ||
| "data-scrollsheet-toaster": "", | ||
| "data-sonner-toaster": "", | ||
| "data-scrollsheet-theme": "light", | ||
| "data-sonner-theme": "light", | ||
| "data-y-position": y, | ||
| "data-x-position": x, | ||
| tabIndex: -1, | ||
| className, | ||
| style: { | ||
| ...baseStyle, | ||
| "--scrollsheet-toast-gap": `${gap}px`, | ||
| "--scrollsheet-toast-front-height": frontHeight !== void 0 ? `${frontHeight}px` : void 0 | ||
| }, | ||
| onMouseEnter: onHoverEnter, | ||
| onMouseMove: onHoverEnter, | ||
| onMouseLeave: onHoverLeave, | ||
| onPointerDown: (event) => { | ||
| if (event.target.dataset.dismissible === "false") return; | ||
| onInteractingChange(true); | ||
| }, | ||
| onPointerUp: () => onInteractingChange(false), | ||
| children: exitRows.map(({ record, exiting }) => { | ||
| const offset = offsetById.get(record.id) ?? lastOffsetRef.current.get(record.id); | ||
| const index = offset?.index ?? newestFirst.length; | ||
| return jsx(ToastRow, { | ||
| record, | ||
| index, | ||
| total: newestFirst.length, | ||
| toastsBefore: offset?.toastsBefore ?? index, | ||
| stackOffset: offset?.stackOffset ?? 0, | ||
| height: heights.get(record.id), | ||
| frontHeight, | ||
| visible: visibleIds.has(record.id), | ||
| expanded, | ||
| removed: exiting, | ||
| yPosition: y, | ||
| xPosition: x, | ||
| closeButton, | ||
| directions, | ||
| onDismiss: dismissRow, | ||
| observe, | ||
| toasterClassNames: toastOptions?.classNames, | ||
| icons, | ||
| toasterCloseButtonAriaLabel: toastOptions?.closeButtonAriaLabel | ||
| }, record.id); | ||
| }) | ||
| }); | ||
| } | ||
| function resolveShellPosition(position) { | ||
| return position ?? "bottom-right"; | ||
| } | ||
| function ToasterShell({ id, toasterId, position, theme, richColors, expand: forceExpand, visibleToasts: visibleToastsProp, closeButton = false, duration: durationProp, gap = 14, offset, swipeDirections, toastOptions, icons, className, style, containerAriaLabel = "Notifications", hotkey = DEFAULT_HOTKEY, nonce }) { | ||
| const resolvedId = id ?? toasterId; | ||
| if (toasterId !== void 0) warnOnce("toaster-id-deprecated", "[scrollsheet Toaster] Toaster's \"toasterId\" prop is deprecated — use \"id\" instead."); | ||
| const { toasts: allToasts } = useSonner(); | ||
| const toasts = React.useMemo(() => selectToasterToasts(allToasts, resolvedId), [allToasts, resolvedId]); | ||
| const defaultPosition = resolveShellPosition(position); | ||
| const possiblePositions = React.useMemo(() => computePossiblePositions(defaultPosition, toasts), [defaultPosition, toasts]); | ||
| const visibleToasts = resolveVisibleToasts(visibleToastsProp); | ||
| const defaultDuration = durationProp ?? toastOptions?.duration ?? 4e3; | ||
| if (theme !== void 0 && theme !== "light") warnOnce("shell-theme", `[scrollsheet Toaster] theme="${theme}" isn't implemented yet — v1 always renders the static light card real Sonner ships by default.`); | ||
| if (richColors) warnOnce("shell-rich-colors", "[scrollsheet Toaster] richColors isn't implemented yet — toasts render with their default icon color only."); | ||
| React.useEffect(() => { | ||
| injectToastStyles(nonce); | ||
| }, [nonce]); | ||
| const [mounted, setMounted] = React.useState(false); | ||
| React.useEffect(() => setMounted(true), []); | ||
| const [hovered, setHovered] = React.useState(false); | ||
| const [hotkeyExpanded, setHotkeyExpanded] = React.useState(false); | ||
| const [interacting, setInteracting] = React.useState(false); | ||
| const expanded = Boolean(forceExpand) || hovered || hotkeyExpanded; | ||
| const isDocumentHidden = useIsDocumentHidden(); | ||
| const handleHoverEnter = React.useCallback(() => setHovered(true), []); | ||
| const handleHoverLeave = React.useCallback(() => { | ||
| if (!interacting) setHovered(false); | ||
| }, [interacting]); | ||
| const dismissRow = React.useCallback((record) => { | ||
| toast.dismiss(record.id); | ||
| }, []); | ||
| const listElsRef = React.useRef(new Map()); | ||
| const registerListEl = React.useCallback((pos, el) => { | ||
| if (el) listElsRef.current.set(pos, el); | ||
| else listElsRef.current.delete(pos); | ||
| }, []); | ||
| React.useEffect(() => { | ||
| if (hotkey.length === 0) return; | ||
| const handleKeyDown = (event) => { | ||
| if (hotkey.every((key) => event[key] || event.code === key)) { | ||
| setHotkeyExpanded(true); | ||
| const [firstList] = listElsRef.current.values(); | ||
| firstList?.focus({ preventScroll: true }); | ||
| } | ||
| if (event.code !== "Escape") return; | ||
| const active = document.activeElement; | ||
| const focusedEntry = [...listElsRef.current.entries()].find(([, el]) => active === el || el.contains(active)); | ||
| if (!focusedEntry) return; | ||
| if (hotkeyExpanded) { | ||
| setHotkeyExpanded(false); | ||
| return; | ||
| } | ||
| const [focusedPosition] = focusedEntry; | ||
| const positionToasts = selectPositionToasts(toasts, focusedPosition, defaultPosition); | ||
| const front = positionToasts[positionToasts.length - 1]; | ||
| if (front) dismissRow(front); | ||
| }; | ||
| document.addEventListener("keydown", handleKeyDown); | ||
| return () => document.removeEventListener("keydown", handleKeyDown); | ||
| }, [ | ||
| hotkey, | ||
| hotkeyExpanded, | ||
| toasts, | ||
| defaultPosition, | ||
| dismissRow | ||
| ]); | ||
| const timersRef = React.useRef(new Map()); | ||
| React.useEffect(() => { | ||
| const timers = timersRef.current; | ||
| const liveIds = new Set(toasts.map((t) => t.id)); | ||
| for (const [id, entry] of timers) { | ||
| if (liveIds.has(id)) continue; | ||
| if (entry.timer) clearTimeout(entry.timer); | ||
| timers.delete(id); | ||
| } | ||
| const paused = expanded || interacting || isDocumentHidden; | ||
| for (const t of toasts) { | ||
| if (t.type === "loading") { | ||
| const stale = timers.get(t.id); | ||
| if (stale) { | ||
| if (stale.timer) clearTimeout(stale.timer); | ||
| timers.delete(t.id); | ||
| } | ||
| continue; | ||
| } | ||
| const ms = t.duration ?? toastOptions?.duration ?? defaultDuration; | ||
| const existing = timers.get(t.id); | ||
| if (!existing || existing.record !== t) { | ||
| if (existing?.timer) clearTimeout(existing.timer); | ||
| const entry = { | ||
| timer: null, | ||
| startedAt: 0, | ||
| remaining: ms, | ||
| record: t | ||
| }; | ||
| timers.set(t.id, entry); | ||
| if (Number.isFinite(ms) && !paused) { | ||
| entry.startedAt = Date.now(); | ||
| entry.timer = setTimeout(() => { | ||
| timersRef.current.delete(t.id); | ||
| expire(t.id); | ||
| }, ms); | ||
| } | ||
| continue; | ||
| } | ||
| if (paused) { | ||
| if (existing.timer) { | ||
| clearTimeout(existing.timer); | ||
| const elapsed = Date.now() - existing.startedAt; | ||
| existing.remaining = Math.max(0, existing.remaining - elapsed); | ||
| existing.timer = null; | ||
| } | ||
| continue; | ||
| } | ||
| if (existing.timer || !Number.isFinite(existing.remaining)) continue; | ||
| existing.startedAt = Date.now(); | ||
| existing.timer = setTimeout(() => { | ||
| timersRef.current.delete(t.id); | ||
| expire(t.id); | ||
| }, existing.remaining); | ||
| } | ||
| }, [ | ||
| toasts, | ||
| expanded, | ||
| interacting, | ||
| isDocumentHidden, | ||
| defaultDuration, | ||
| toastOptions?.duration | ||
| ]); | ||
| React.useEffect(() => { | ||
| const timers = timersRef.current; | ||
| return () => { | ||
| for (const entry of timers.values()) if (entry.timer) clearTimeout(entry.timer); | ||
| timers.clear(); | ||
| }; | ||
| }, []); | ||
| useCloseWatcher({ | ||
| present: toasts.length > 0, | ||
| nonModal: true, | ||
| escapeDismissible: true, | ||
| onClose: () => { | ||
| const target = findNewestDismissible(toasts); | ||
| if (target) dismissRow(target); | ||
| } | ||
| }); | ||
| if (!mounted || typeof document === "undefined") return null; | ||
| const offsetValue = offset !== void 0 ? typeof offset === "number" ? `${offset}px` : offset : void 0; | ||
| const baseStyle = { | ||
| ...style, | ||
| "--scrollsheet-toast-offset": offsetValue | ||
| }; | ||
| return createPortal(jsx("section", { | ||
| "aria-label": containerAriaLabel, | ||
| tabIndex: -1, | ||
| "aria-live": "polite", | ||
| "aria-relevant": "additions text", | ||
| "aria-atomic": "false", | ||
| suppressHydrationWarning: true, | ||
| "data-react-aria-top-layer": "", | ||
| children: possiblePositions.map((groupPosition) => jsx(PositionGroup, { | ||
| position: groupPosition, | ||
| defaultPosition, | ||
| toasts, | ||
| visibleToasts, | ||
| gap, | ||
| expanded, | ||
| closeButton, | ||
| swipeDirections, | ||
| toastOptions, | ||
| icons, | ||
| className, | ||
| baseStyle, | ||
| registerListEl, | ||
| dismissRow, | ||
| onHoverEnter: handleHoverEnter, | ||
| onHoverLeave: handleHoverLeave, | ||
| onInteractingChange: setInteracting | ||
| }, groupPosition)) | ||
| }), document.body); | ||
| } | ||
| //#endregion | ||
| export { useSonner as a, toast as i, resolveVisibleToasts as n, useToasts as o, injectToastStylesInto as r, ToasterShell as t }; |
Sorry, the diff of this file is too big to display
| "use client"; | ||
| "use client"; | ||
| import { a as NestedRoot, c as Root, d as hasMultipleSnapPoints, f as resolveCloseThreshold, g as Title, h as Description, i as Handle, l as composeHandleClick, n as Drawer, o as Overlay, p as resolveFadeFromIndex, r as DrawerClose, s as Portal, t as Content, u as composeOpenChange, x as Trigger } from "./drawer-DjGdqpHe.mjs"; | ||
| import { a as NestedRoot, c as Root, d as hasMultipleSnapPoints, f as resolveCloseThreshold, g as Title, h as Description, i as Handle, l as composeHandleClick, n as Drawer, o as Overlay, p as resolveFadeFromIndex, r as DrawerClose, s as Portal, t as Content, u as composeOpenChange, x as Trigger } from "./drawer-HVSMFDtk.mjs"; | ||
| export { DrawerClose as Close, Content, Description, Drawer, Handle, NestedRoot, Overlay, Portal, Root, Title, Trigger, composeHandleClick, composeOpenChange, hasMultipleSnapPoints, resolveCloseThreshold, resolveFadeFromIndex }; |
| "use client"; | ||
| import { S as Root, _ as Handle, b as spring, g as Title, h as Description, m as Close, n as Drawer, v as Content, x as Trigger, y as injectStylesInto } from "./drawer-DjGdqpHe.mjs"; | ||
| import { S as Root, _ as Handle, b as spring, g as Title, h as Description, m as Close, n as Drawer, v as Content, x as Trigger, y as injectStylesInto } from "./drawer-HVSMFDtk.mjs"; | ||
| import { o as hasDialogSupport } from "./cn-CActSFFr.mjs"; | ||
| import { a as useSonner, i as toast, o as useToasts, r as injectToastStylesInto, t as ToasterShell } from "./toast-DKgHZqNc.mjs"; | ||
| import { a as useSonner, i as toast, o as useToasts, r as injectToastStylesInto, t as ToasterShell } from "./toast-BByAtKPq.mjs"; | ||
| //#region packages/scrollsheet/src/index.ts | ||
@@ -6,0 +6,0 @@ function isSupported() { |
| "use client"; | ||
| "use client"; | ||
| import { a as useSonner, i as toast, n as resolveVisibleToasts, o as useToasts, r as injectToastStylesInto, t as ToasterShell } from "./toast-DKgHZqNc.mjs"; | ||
| import { a as useSonner, i as toast, n as resolveVisibleToasts, o as useToasts, r as injectToastStylesInto, t as ToasterShell } from "./toast-BByAtKPq.mjs"; | ||
| export { ToasterShell as Toaster, injectToastStylesInto, resolveVisibleToasts, toast, useSonner, useToasts }; |
+1
-1
| "use client"; | ||
| "use client"; | ||
| import { a as NestedRoot, b as Trigger, c as Root, d as hasMultipleSnapPoints, f as resolveCloseThreshold, g as Title, h as Description, i as Handle, l as composeHandleClick, n as Drawer, o as Overlay, p as resolveFadeFromIndex, r as DrawerClose, s as Portal, t as Content, u as composeOpenChange } from "./drawer-FytErbvx.mjs"; | ||
| import { a as NestedRoot, b as Trigger, c as Root, d as hasMultipleSnapPoints, f as resolveCloseThreshold, g as Title, h as Description, i as Handle, l as composeHandleClick, n as Drawer, o as Overlay, p as resolveFadeFromIndex, r as DrawerClose, s as Portal, t as Content, u as composeOpenChange } from "./drawer-BXqx0bTN.mjs"; | ||
| export { DrawerClose as Close, Content, Description, Drawer, Handle, NestedRoot, Overlay, Portal, Root, Title, Trigger, composeHandleClick, composeOpenChange, hasMultipleSnapPoints, resolveCloseThreshold, resolveFadeFromIndex }; |
+1
-1
| "use client"; | ||
| import { _ as Handle, b as Trigger, g as Title, h as Description, m as Close, n as Drawer, v as Content, x as Root, y as injectStylesInto } from "./drawer-FytErbvx.mjs"; | ||
| import { _ as Handle, b as Trigger, g as Title, h as Description, m as Close, n as Drawer, v as Content, x as Root, y as injectStylesInto } from "./drawer-BXqx0bTN.mjs"; | ||
| import { o as hasDialogSupport } from "./cn-CActSFFr.mjs"; | ||
@@ -4,0 +4,0 @@ import { a as spring } from "./animate-D5TGKlub.mjs"; |
+1
-1
@@ -1,1 +0,1 @@ | ||
| .scrollsheet-toast{color:#18181b;background:#fff;border:1px solid #00000014;border-radius:8px;align-items:flex-start;gap:10px;padding:16px;font-family:ui-sans-serif,system-ui,-apple-system,Segoe UI,sans-serif;font-size:13px;display:flex;position:relative;box-shadow:0 4px 12px #0000001a}.scrollsheet-toast[data-scrollsheet-custom]{box-shadow:none;background:0 0;border:none;padding:0}.scrollsheet-toast-icon{color:#fff;background:#6b7280;border-radius:50%;flex:none;place-items:center;width:18px;height:18px;margin-top:1px;font-size:11px;line-height:1;display:grid}.scrollsheet-toast-icon[data-type=success]{background:#22c55e}.scrollsheet-toast-icon[data-type=error]{background:#ef4444}.scrollsheet-toast-icon[data-type=warning]{background:#f59e0b}.scrollsheet-toast-icon[data-type=info]{background:#3b82f6}.scrollsheet-toast-icon[data-type=loading]{background:0 0}.scrollsheet-toast-spinner{border:2px solid #00000026;border-top-color:#0000008c;border-radius:50%;width:14px;height:14px;animation:.6s linear infinite scrollsheet-toast-spin}@keyframes scrollsheet-toast-spin{to{transform:rotate(360deg)}}@media (prefers-reduced-motion:reduce){.scrollsheet-toast-spinner{animation-duration:1.6s}}.scrollsheet-toast-body{flex-direction:column;flex:1;gap:4px;min-width:0;display:flex}.scrollsheet-toast-title{font-size:13px;font-weight:500;line-height:1.35}.scrollsheet-toast-description{color:#0009;font-size:13px;line-height:1.4}.scrollsheet-toast-actions{flex:none;align-items:center;gap:6px;display:flex}.scrollsheet-toast-action,.scrollsheet-toast-cancel{cursor:pointer;border:none;border-radius:4px;height:24px;padding:0 8px;font-size:12px;font-weight:500}.scrollsheet-toast-action{color:#fff;background:#18181b}.scrollsheet-toast-cancel{color:#18181b;background:#0000000f}.scrollsheet-toast-close{color:#00000080;cursor:pointer;background:0 0;border:none;border-radius:50%;flex:none;place-items:center;width:20px;height:20px;padding:0;font-size:14px;line-height:1;display:grid}.scrollsheet-toast-close:hover{background:#0000000f}[data-scrollsheet-toaster]{z-index:2147483647;width:min(356px, calc(100vw - 2 * var(--scrollsheet-toast-offset,var(--sonner-offset,24px))));outline:none;margin:0;padding:0;list-style:none;transition:transform .4s;position:fixed}[data-scrollsheet-toaster][data-x-position=right]{right:var(--scrollsheet-toast-offset,var(--sonner-offset,24px))}[data-scrollsheet-toaster][data-x-position=left]{left:var(--scrollsheet-toast-offset,var(--sonner-offset,24px))}[data-scrollsheet-toaster][data-x-position=center]{left:50%;transform:translate(-50%)}[data-scrollsheet-toaster][data-y-position=top]{top:var(--scrollsheet-toast-offset,var(--sonner-offset,24px))}[data-scrollsheet-toaster][data-y-position=bottom]{bottom:var(--scrollsheet-toast-offset,var(--sonner-offset,24px))}.scrollsheet-toast{--scrollsheet-toast-lift:-1;--scrollsheet-toast-y:translateY(100%);opacity:0;transform:var(--scrollsheet-toast-y);touch-action:none;box-sizing:border-box;overflow-wrap:anywhere;transition:transform .25s,opacity .25s,height .25s;position:absolute;left:0;right:0}.scrollsheet-toast[data-y-position=top]{--scrollsheet-toast-lift:1;--scrollsheet-toast-y:translateY(-100%);top:0}.scrollsheet-toast[data-y-position=bottom]{--scrollsheet-toast-lift:-1;--scrollsheet-toast-y:translateY(100%);bottom:0}.scrollsheet-toast[data-mounted]{--scrollsheet-toast-y:translateY(0);opacity:1}.scrollsheet-toast[data-expanded=false][data-front=false]{--scrollsheet-toast-y:translateY(calc(var(--scrollsheet-toast-lift) * var(--scrollsheet-toast-gap,var(--sonner-gap,14px)) * var(--scrollsheet-toast-toasts-before,var(--sonner-toasts-before,0)))) scale(calc(-1 * (1 + var(--scrollsheet-toast-toasts-before,var(--sonner-toasts-before,0)) * .05)));height:var(--scrollsheet-toast-front-height,var(--sonner-front-height,auto));overflow:hidden}.scrollsheet-toast[data-expanded=false][data-front=false]>*{opacity:0}.scrollsheet-toast[data-mounted][data-expanded=true]{--scrollsheet-toast-y:translateY(calc(var(--scrollsheet-toast-lift) * var(--scrollsheet-toast-stack-offset,var(--sonner-stack-offset,0px))));height:var(--scrollsheet-toast-initial-height,var(--sonner-initial-height,auto))}.scrollsheet-toast[data-visible=false]{opacity:0;pointer-events:none}.scrollsheet-toast[data-removed]{pointer-events:none}.scrollsheet-toast[data-removed][data-front=true]{--scrollsheet-toast-y:translateY(calc(var(--scrollsheet-toast-lift) * -100%));opacity:0}.scrollsheet-toast[data-removed][data-front=false][data-expanded=true]{--scrollsheet-toast-y:translateY(calc(var(--scrollsheet-toast-lift) * var(--scrollsheet-toast-stack-offset,var(--sonner-stack-offset,0px)) + var(--scrollsheet-toast-lift) * -100%));opacity:0}.scrollsheet-toast[data-removed][data-front=false][data-expanded=false]{--scrollsheet-toast-y:translateY(40%);opacity:0}.scrollsheet-toast[data-swiping=true]{transform:var(--scrollsheet-toast-y) translateY(var(--scrollsheet-toast-swipe-y,var(--sonner-swipe-y,0px))) translateX(var(--scrollsheet-toast-swipe-x,var(--sonner-swipe-x,0px)));transition:none}.scrollsheet-toast[data-swiped=true]{-webkit-user-select:none;user-select:none}.scrollsheet-toast[data-swipe-out=true]{animation:.2s ease-out forwards scrollsheet-toast-swipe-out}.scrollsheet-toast[data-swipe-direction=left]{--scrollsheet-toast-swipe-out-x:-1}.scrollsheet-toast[data-swipe-direction=right]{--scrollsheet-toast-swipe-out-x:1}.scrollsheet-toast[data-swipe-direction=up]{--scrollsheet-toast-swipe-out-y:-1}.scrollsheet-toast[data-swipe-direction=down]{--scrollsheet-toast-swipe-out-y:1}@keyframes scrollsheet-toast-swipe-out{0%{transform:var(--scrollsheet-toast-y) translateY(var(--scrollsheet-toast-swipe-y,var(--sonner-swipe-y,0px))) translateX(var(--scrollsheet-toast-swipe-x,var(--sonner-swipe-x,0px)));opacity:1}to{transform:var(--scrollsheet-toast-y) translateY(calc(var(--scrollsheet-toast-swipe-y,var(--sonner-swipe-y,0px)) + var(--scrollsheet-toast-swipe-out-y,var(--sonner-swipe-out-y,0)) * 100%)) translateX(calc(var(--scrollsheet-toast-swipe-x,var(--sonner-swipe-x,0px)) + var(--scrollsheet-toast-swipe-out-x,var(--sonner-swipe-out-x,0)) * 100%));opacity:0}}@media (prefers-reduced-motion:reduce){[data-scrollsheet-toaster],.scrollsheet-toast{transition:none!important;animation:none!important}}@media (max-width:600px){[data-scrollsheet-toaster]{right:var(--scrollsheet-toast-mobile-offset,var(--sonner-mobile-offset,16px));left:var(--scrollsheet-toast-mobile-offset,var(--sonner-mobile-offset,16px));width:100%}[data-scrollsheet-toaster] .scrollsheet-toast{width:calc(100% - var(--scrollsheet-toast-mobile-offset,var(--sonner-mobile-offset,16px)) * 2);left:0;right:0}[data-scrollsheet-toaster][data-x-position=left]{left:var(--scrollsheet-toast-mobile-offset,var(--sonner-mobile-offset,16px))}[data-scrollsheet-toaster][data-y-position=bottom]{bottom:var(--scrollsheet-toast-mobile-offset,var(--sonner-mobile-offset,16px))}[data-scrollsheet-toaster][data-y-position=top]{top:var(--scrollsheet-toast-mobile-offset,var(--sonner-mobile-offset,16px))}[data-scrollsheet-toaster][data-x-position=center]{left:var(--scrollsheet-toast-mobile-offset,var(--sonner-mobile-offset,16px));right:var(--scrollsheet-toast-mobile-offset,var(--sonner-mobile-offset,16px));transform:none}} | ||
| .scrollsheet-toast{border-radius:var(--scrollsheet-toast-radius,14px);color:#18181b;background:#fff;border:1px solid #00000014;align-items:flex-start;gap:10px;padding:16px;font-family:ui-sans-serif,system-ui,-apple-system,Segoe UI,sans-serif;font-size:13px;display:flex;position:relative;box-shadow:0 4px 12px #0000001a}.scrollsheet-toast[data-scrollsheet-custom]{box-shadow:none;background:0 0;border:none;padding:0}.scrollsheet-toast-icon{color:#fff;background:#6b7280;border-radius:50%;flex:none;place-items:center;width:18px;height:18px;margin-top:1px;font-size:11px;line-height:1;display:grid}.scrollsheet-toast-icon[data-type=success]{background:#22c55e}.scrollsheet-toast-icon[data-type=error]{background:#ef4444}.scrollsheet-toast-icon[data-type=warning]{background:#f59e0b}.scrollsheet-toast-icon[data-type=info]{background:#3b82f6}.scrollsheet-toast-icon[data-type=loading]{background:0 0}.scrollsheet-toast-spinner{border:2px solid #00000026;border-top-color:#0000008c;border-radius:50%;width:14px;height:14px;animation:.6s linear infinite scrollsheet-toast-spin}@keyframes scrollsheet-toast-spin{to{transform:rotate(360deg)}}@media (prefers-reduced-motion:reduce){.scrollsheet-toast-spinner{animation-duration:1.6s}}.scrollsheet-toast-body{flex-direction:column;flex:1;gap:4px;min-width:0;display:flex}.scrollsheet-toast-title{font-size:13px;font-weight:500;line-height:1.35}.scrollsheet-toast-description{color:#0009;font-size:13px;line-height:1.4}.scrollsheet-toast-actions{flex:none;align-items:center;gap:6px;display:flex}.scrollsheet-toast-action,.scrollsheet-toast-cancel{cursor:pointer;border:none;border-radius:4px;height:24px;padding:0 8px;font-size:12px;font-weight:500}.scrollsheet-toast-action{color:#fff;background:#18181b}.scrollsheet-toast-cancel{color:#18181b;background:#0000000f}.scrollsheet-toast-close{color:#00000080;cursor:pointer;background:0 0;border:none;border-radius:50%;flex:none;place-items:center;width:20px;height:20px;padding:0;font-size:14px;line-height:1;display:grid}.scrollsheet-toast-close:hover{background:#0000000f}[data-scrollsheet-toaster]{z-index:2147483647;width:min(356px, calc(100vw - 2 * var(--scrollsheet-toast-offset,var(--sonner-offset,24px))));outline:none;margin:0;padding:0;list-style:none;transition:transform .4s;position:fixed}[data-scrollsheet-toaster][data-x-position=right]{right:var(--scrollsheet-toast-offset,var(--sonner-offset,24px))}[data-scrollsheet-toaster][data-x-position=left]{left:var(--scrollsheet-toast-offset,var(--sonner-offset,24px))}[data-scrollsheet-toaster][data-x-position=center]{left:50%;transform:translate(-50%)}[data-scrollsheet-toaster][data-y-position=top]{top:var(--scrollsheet-toast-offset,var(--sonner-offset,24px))}[data-scrollsheet-toaster][data-y-position=bottom]{bottom:var(--scrollsheet-toast-offset,var(--sonner-offset,24px))}.scrollsheet-toast{--scrollsheet-toast-lift:-1;--scrollsheet-toast-y:translateY(100%);opacity:0;transform:var(--scrollsheet-toast-y);touch-action:none;box-sizing:border-box;overflow-wrap:anywhere;transition:transform .25s,opacity .25s,height .25s;position:absolute;left:0;right:0}.scrollsheet-toast[data-y-position=top]{--scrollsheet-toast-lift:1;--scrollsheet-toast-y:translateY(-100%);top:0}.scrollsheet-toast[data-y-position=bottom]{--scrollsheet-toast-lift:-1;--scrollsheet-toast-y:translateY(100%);bottom:0}.scrollsheet-toast[data-mounted]{--scrollsheet-toast-y:translateY(0);opacity:1}.scrollsheet-toast[data-expanded=false][data-front=false]{--scrollsheet-toast-y:translateY(calc(var(--scrollsheet-toast-lift) * var(--scrollsheet-toast-gap,var(--sonner-gap,14px)) * var(--scrollsheet-toast-toasts-before,var(--sonner-toasts-before,0)))) scale(calc(1 - var(--scrollsheet-toast-toasts-before,var(--sonner-toasts-before,0)) * .05));height:var(--scrollsheet-toast-front-height,var(--sonner-front-height,auto));overflow:hidden}.scrollsheet-toast[data-expanded=false][data-front=false]>*{opacity:0}.scrollsheet-toast[data-mounted][data-expanded=true]{--scrollsheet-toast-y:translateY(calc(var(--scrollsheet-toast-lift) * var(--scrollsheet-toast-stack-offset,var(--sonner-stack-offset,0px))));height:var(--scrollsheet-toast-initial-height,var(--sonner-initial-height,auto))}.scrollsheet-toast[data-visible=false]{opacity:0;pointer-events:none}.scrollsheet-toast[data-removed]{pointer-events:none}.scrollsheet-toast[data-removed][data-front=true]{--scrollsheet-toast-y:translateY(calc(var(--scrollsheet-toast-lift) * -100%));opacity:0}.scrollsheet-toast[data-removed][data-front=false][data-expanded=true]{--scrollsheet-toast-y:translateY(calc(var(--scrollsheet-toast-lift) * var(--scrollsheet-toast-stack-offset,var(--sonner-stack-offset,0px)) + var(--scrollsheet-toast-lift) * -100%));opacity:0}.scrollsheet-toast[data-removed][data-front=false][data-expanded=false]{--scrollsheet-toast-y:translateY(40%);opacity:0}.scrollsheet-toast[data-swiping=true]{transform:var(--scrollsheet-toast-y) translateY(var(--scrollsheet-toast-swipe-y,var(--sonner-swipe-y,0px))) translateX(var(--scrollsheet-toast-swipe-x,var(--sonner-swipe-x,0px)));transition:none}.scrollsheet-toast[data-swiped=true]{-webkit-user-select:none;user-select:none}.scrollsheet-toast[data-swipe-out=true]{animation:.2s ease-out forwards scrollsheet-toast-swipe-out}.scrollsheet-toast[data-swipe-direction=left]{--scrollsheet-toast-swipe-out-x:-1}.scrollsheet-toast[data-swipe-direction=right]{--scrollsheet-toast-swipe-out-x:1}.scrollsheet-toast[data-swipe-direction=up]{--scrollsheet-toast-swipe-out-y:-1}.scrollsheet-toast[data-swipe-direction=down]{--scrollsheet-toast-swipe-out-y:1}@keyframes scrollsheet-toast-swipe-out{0%{transform:var(--scrollsheet-toast-y) translateY(var(--scrollsheet-toast-swipe-y,var(--sonner-swipe-y,0px))) translateX(var(--scrollsheet-toast-swipe-x,var(--sonner-swipe-x,0px)));opacity:1}to{transform:var(--scrollsheet-toast-y) translateY(calc(var(--scrollsheet-toast-swipe-y,var(--sonner-swipe-y,0px)) + var(--scrollsheet-toast-swipe-out-y,var(--sonner-swipe-out-y,0)) * 100%)) translateX(calc(var(--scrollsheet-toast-swipe-x,var(--sonner-swipe-x,0px)) + var(--scrollsheet-toast-swipe-out-x,var(--sonner-swipe-out-x,0)) * 100%));opacity:0}}@media (prefers-reduced-motion:reduce){[data-scrollsheet-toaster],.scrollsheet-toast{transition:none!important;animation:none!important}}@media (max-width:600px){[data-scrollsheet-toaster]{right:var(--scrollsheet-toast-mobile-offset,var(--sonner-mobile-offset,16px));left:var(--scrollsheet-toast-mobile-offset,var(--sonner-mobile-offset,16px));width:100%}[data-scrollsheet-toaster] .scrollsheet-toast{width:calc(100% - var(--scrollsheet-toast-mobile-offset,var(--sonner-mobile-offset,16px)) * 2);left:0;right:0}[data-scrollsheet-toaster][data-x-position=left]{left:var(--scrollsheet-toast-mobile-offset,var(--sonner-mobile-offset,16px))}[data-scrollsheet-toaster][data-y-position=bottom]{bottom:var(--scrollsheet-toast-mobile-offset,var(--sonner-mobile-offset,16px))}[data-scrollsheet-toaster][data-y-position=top]{top:var(--scrollsheet-toast-mobile-offset,var(--sonner-mobile-offset,16px))}[data-scrollsheet-toaster][data-x-position=center]{left:var(--scrollsheet-toast-mobile-offset,var(--sonner-mobile-offset,16px));right:var(--scrollsheet-toast-mobile-offset,var(--sonner-mobile-offset,16px));transform:none}} |
+1
-1
| { | ||
| "name": "scrollsheet", | ||
| "version": "1.0.0-beta.1", | ||
| "version": "1.0.0-beta.2", | ||
| "publishConfig": { | ||
@@ -5,0 +5,0 @@ "tag": "beta" |
Sorry, the diff of this file is too big to display
| "use client"; | ||
| import { c as prefersReducedMotion, h as warnOnce, n as useCloseWatcher, r as createStyleInjector, t as cn } from "./cn-CActSFFr.mjs"; | ||
| import * as React from "react"; | ||
| import { Fragment, jsx, jsxs } from "react/jsx-runtime"; | ||
| import { createPortal } from "react-dom"; | ||
| //#region packages/scrollsheet/src/toast/state.ts | ||
| let toasts = []; | ||
| const listeners = new Set(); | ||
| let uid = 0; | ||
| const MAX_HISTORY_SIZE = 100; | ||
| let history = []; | ||
| function trimHistory() { | ||
| let toRemove = history.length - MAX_HISTORY_SIZE; | ||
| if (toRemove <= 0) return; | ||
| const liveIds = new Set(toasts.map((t) => t.id)); | ||
| history = history.filter((record) => { | ||
| if (toRemove > 0 && !liveIds.has(record.id)) { | ||
| toRemove -= 1; | ||
| return false; | ||
| } | ||
| return true; | ||
| }); | ||
| } | ||
| function recordHistory(record) { | ||
| const index = history.findIndex((t) => t.id === record.id); | ||
| if (index === -1) { | ||
| history = [...history, record]; | ||
| trimHistory(); | ||
| return; | ||
| } | ||
| history = history.map((t, i) => i === index ? record : t); | ||
| } | ||
| function nextId() { | ||
| uid += 1; | ||
| return uid; | ||
| } | ||
| function publish() { | ||
| for (const listener of listeners) listener(); | ||
| } | ||
| function subscribe(listener) { | ||
| listeners.add(listener); | ||
| return () => listeners.delete(listener); | ||
| } | ||
| function getSnapshot() { | ||
| return toasts; | ||
| } | ||
| const EMPTY_TOASTS = []; | ||
| function getServerSnapshot() { | ||
| return EMPTY_TOASTS; | ||
| } | ||
| function upsert(id, patch, fallbackType) { | ||
| const resolvedId = id ?? nextId(); | ||
| const index = toasts.findIndex((t) => t.id === resolvedId); | ||
| const type = patch.type ?? fallbackType; | ||
| let record; | ||
| if (index === -1) { | ||
| record = { | ||
| dismissible: true, | ||
| ...patch, | ||
| id: resolvedId, | ||
| type, | ||
| createdAt: Date.now() | ||
| }; | ||
| toasts = [...toasts, record]; | ||
| } else { | ||
| record = { | ||
| ...toasts[index], | ||
| ...patch, | ||
| id: resolvedId, | ||
| type | ||
| }; | ||
| toasts = toasts.map((t, i) => i === index ? record : t); | ||
| } | ||
| recordHistory(record); | ||
| publish(); | ||
| return resolvedId; | ||
| } | ||
| function dismiss(id) { | ||
| if (id === void 0) { | ||
| const swept = new Set(toasts); | ||
| for (const t of swept) { | ||
| if (!toasts.includes(t)) continue; | ||
| t.onDismiss?.(t); | ||
| } | ||
| toasts = toasts.filter((t) => !swept.has(t)); | ||
| publish(); | ||
| return; | ||
| } | ||
| const existing = toasts.find((t) => t.id === id); | ||
| if (!existing) return id; | ||
| existing.onDismiss?.(existing); | ||
| toasts = toasts.filter((t) => t !== existing); | ||
| publish(); | ||
| return id; | ||
| } | ||
| function expire(id) { | ||
| const existing = toasts.find((t) => t.id === id); | ||
| if (!existing) return; | ||
| existing.onAutoClose?.(existing); | ||
| toasts = toasts.filter((t) => t !== existing); | ||
| publish(); | ||
| } | ||
| function baseToast(message, data) { | ||
| return upsert(data?.id, { | ||
| ...data, | ||
| title: message | ||
| }, "default"); | ||
| } | ||
| function withType(type) { | ||
| return (message, data) => upsert(data?.id, { | ||
| ...data, | ||
| title: message | ||
| }, type); | ||
| } | ||
| const toastImpl = baseToast; | ||
| toastImpl.success = withType("success"); | ||
| toastImpl.error = withType("error"); | ||
| toastImpl.info = withType("info"); | ||
| toastImpl.warning = withType("warning"); | ||
| toastImpl.loading = withType("loading"); | ||
| toastImpl.message = withType("default"); | ||
| toastImpl.custom = (jsx, data) => { | ||
| const id = data?.id ?? nextId(); | ||
| return upsert(id, { | ||
| ...data, | ||
| jsx: jsx(id) | ||
| }, "default"); | ||
| }; | ||
| function isHttpResponse(value) { | ||
| return typeof value === "object" && value !== null && "ok" in value && typeof value.ok === "boolean" && "status" in value && typeof value.status === "number"; | ||
| } | ||
| function isPromiseExtendedResult(value) { | ||
| return typeof value === "object" && value !== null && !React.isValidElement(value); | ||
| } | ||
| function applyPromiseSettlement(id, resolved, fallbackTitle, description, type) { | ||
| if (isPromiseExtendedResult(resolved)) { | ||
| const { message, ...rest } = resolved; | ||
| upsert(id, { | ||
| description, | ||
| ...rest, | ||
| title: message ?? fallbackTitle, | ||
| type | ||
| }, type); | ||
| return; | ||
| } | ||
| upsert(id, { | ||
| title: resolved ?? fallbackTitle, | ||
| description, | ||
| type | ||
| }, type); | ||
| } | ||
| function promiseImpl(promiseOrFn, data) { | ||
| const { loading, success, error, finally: onFinally, description, ...rest } = data; | ||
| const id = upsert(data.id, { | ||
| ...rest, | ||
| description: typeof description === "function" ? void 0 : description, | ||
| title: loading, | ||
| type: "loading" | ||
| }, "loading"); | ||
| const settle = typeof promiseOrFn === "function" ? promiseOrFn() : promiseOrFn; | ||
| let settled; | ||
| const chain = settle.then(async (result) => { | ||
| if (isHttpResponse(result) && !result.ok) { | ||
| settled = { | ||
| ok: false, | ||
| reason: result | ||
| }; | ||
| const statusMessage = `HTTP error! status: ${result.status}`; | ||
| const resolvedDescription = typeof description === "function" ? await description(statusMessage) : description; | ||
| const resolved = typeof error === "function" ? await error(statusMessage) : error; | ||
| applyPromiseSettlement(id, resolved, "Error", resolvedDescription, "error"); | ||
| return; | ||
| } | ||
| if (result instanceof Error) { | ||
| settled = { | ||
| ok: false, | ||
| reason: result | ||
| }; | ||
| const resolvedDescription = typeof description === "function" ? await description(result) : description; | ||
| const resolved = typeof error === "function" ? await error(result) : error; | ||
| applyPromiseSettlement(id, resolved, "Error", resolvedDescription, "error"); | ||
| return; | ||
| } | ||
| settled = { | ||
| ok: true, | ||
| value: result | ||
| }; | ||
| const resolvedDescription = typeof description === "function" ? await description(result) : description; | ||
| const resolved = typeof success === "function" ? await success(result) : success; | ||
| applyPromiseSettlement(id, resolved, "Success", resolvedDescription, "success"); | ||
| }).catch(async (err) => { | ||
| settled = { | ||
| ok: false, | ||
| reason: err | ||
| }; | ||
| const resolvedDescription = typeof description === "function" ? await description(err) : description; | ||
| const resolved = typeof error === "function" ? await error(err) : error; | ||
| applyPromiseSettlement(id, resolved, "Error", resolvedDescription, "error"); | ||
| }).finally(() => onFinally?.()); | ||
| const unwrap = () => chain.then(() => { | ||
| if (!settled) throw new Error("scrollsheet toast.promise: chain settled with no outcome"); | ||
| if (settled.ok) return settled.value; | ||
| throw settled.reason; | ||
| }); | ||
| return Object.assign(id, { unwrap }); | ||
| } | ||
| toastImpl.promise = promiseImpl; | ||
| toastImpl.dismiss = dismiss; | ||
| toastImpl.getToasts = () => toasts; | ||
| toastImpl.getHistory = () => history; | ||
| const toast = toastImpl; | ||
| function useSonner() { | ||
| return { toasts: React.useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot) }; | ||
| } | ||
| const useToasts = useSonner; | ||
| function selectToasterToasts(all, toasterId) { | ||
| return all.filter((t) => toasterId === void 0 ? t.toasterId === void 0 : t.toasterId === toasterId); | ||
| } | ||
| function selectPositionToasts(all, position, defaultPosition) { | ||
| return all.filter((t) => t.position === void 0 ? position === defaultPosition : t.position === position); | ||
| } | ||
| function selectToastWindow(toasts, visibleToasts) { | ||
| const newestFirst = [...toasts].reverse(); | ||
| return { | ||
| visible: newestFirst.slice(0, visibleToasts), | ||
| hidden: newestFirst.slice(visibleToasts) | ||
| }; | ||
| } | ||
| //#endregion | ||
| //#region packages/scrollsheet/src/toast/toast-styles.ts | ||
| const { injectDocument, injectShadowRoot } = createStyleInjector(`.scrollsheet-toast{color:#18181b;background:#fff;border:1px solid #00000014;border-radius:8px;align-items:flex-start;gap:10px;padding:16px;font-family:ui-sans-serif,system-ui,-apple-system,Segoe UI,sans-serif;font-size:13px;display:flex;position:relative;box-shadow:0 4px 12px #0000001a}.scrollsheet-toast[data-scrollsheet-custom]{box-shadow:none;background:0 0;border:none;padding:0}.scrollsheet-toast-icon{color:#fff;background:#6b7280;border-radius:50%;flex:none;place-items:center;width:18px;height:18px;margin-top:1px;font-size:11px;line-height:1;display:grid}.scrollsheet-toast-icon[data-type=success]{background:#22c55e}.scrollsheet-toast-icon[data-type=error]{background:#ef4444}.scrollsheet-toast-icon[data-type=warning]{background:#f59e0b}.scrollsheet-toast-icon[data-type=info]{background:#3b82f6}.scrollsheet-toast-icon[data-type=loading]{background:0 0}.scrollsheet-toast-spinner{border:2px solid #00000026;border-top-color:#0000008c;border-radius:50%;width:14px;height:14px;animation:.6s linear infinite scrollsheet-toast-spin}@keyframes scrollsheet-toast-spin{to{transform:rotate(360deg)}}@media (prefers-reduced-motion:reduce){.scrollsheet-toast-spinner{animation-duration:1.6s}}.scrollsheet-toast-body{flex-direction:column;flex:1;gap:4px;min-width:0;display:flex}.scrollsheet-toast-title{font-size:13px;font-weight:500;line-height:1.35}.scrollsheet-toast-description{color:#0009;font-size:13px;line-height:1.4}.scrollsheet-toast-actions{flex:none;align-items:center;gap:6px;display:flex}.scrollsheet-toast-action,.scrollsheet-toast-cancel{cursor:pointer;border:none;border-radius:4px;height:24px;padding:0 8px;font-size:12px;font-weight:500}.scrollsheet-toast-action{color:#fff;background:#18181b}.scrollsheet-toast-cancel{color:#18181b;background:#0000000f}.scrollsheet-toast-close{color:#00000080;cursor:pointer;background:0 0;border:none;border-radius:50%;flex:none;place-items:center;width:20px;height:20px;padding:0;font-size:14px;line-height:1;display:grid}.scrollsheet-toast-close:hover{background:#0000000f}[data-scrollsheet-toaster]{z-index:2147483647;width:min(356px, calc(100vw - 2 * var(--scrollsheet-toast-offset,var(--sonner-offset,24px))));outline:none;margin:0;padding:0;list-style:none;transition:transform .4s;position:fixed}[data-scrollsheet-toaster][data-x-position=right]{right:var(--scrollsheet-toast-offset,var(--sonner-offset,24px))}[data-scrollsheet-toaster][data-x-position=left]{left:var(--scrollsheet-toast-offset,var(--sonner-offset,24px))}[data-scrollsheet-toaster][data-x-position=center]{left:50%;transform:translate(-50%)}[data-scrollsheet-toaster][data-y-position=top]{top:var(--scrollsheet-toast-offset,var(--sonner-offset,24px))}[data-scrollsheet-toaster][data-y-position=bottom]{bottom:var(--scrollsheet-toast-offset,var(--sonner-offset,24px))}.scrollsheet-toast{--scrollsheet-toast-lift:-1;--scrollsheet-toast-y:translateY(100%);opacity:0;transform:var(--scrollsheet-toast-y);touch-action:none;box-sizing:border-box;overflow-wrap:anywhere;transition:transform .25s,opacity .25s,height .25s;position:absolute;left:0;right:0}.scrollsheet-toast[data-y-position=top]{--scrollsheet-toast-lift:1;--scrollsheet-toast-y:translateY(-100%);top:0}.scrollsheet-toast[data-y-position=bottom]{--scrollsheet-toast-lift:-1;--scrollsheet-toast-y:translateY(100%);bottom:0}.scrollsheet-toast[data-mounted]{--scrollsheet-toast-y:translateY(0);opacity:1}.scrollsheet-toast[data-expanded=false][data-front=false]{--scrollsheet-toast-y:translateY(calc(var(--scrollsheet-toast-lift) * var(--scrollsheet-toast-gap,var(--sonner-gap,14px)) * var(--scrollsheet-toast-toasts-before,var(--sonner-toasts-before,0)))) scale(calc(-1 * (1 + var(--scrollsheet-toast-toasts-before,var(--sonner-toasts-before,0)) * .05)));height:var(--scrollsheet-toast-front-height,var(--sonner-front-height,auto));overflow:hidden}.scrollsheet-toast[data-expanded=false][data-front=false]>*{opacity:0}.scrollsheet-toast[data-mounted][data-expanded=true]{--scrollsheet-toast-y:translateY(calc(var(--scrollsheet-toast-lift) * var(--scrollsheet-toast-stack-offset,var(--sonner-stack-offset,0px))));height:var(--scrollsheet-toast-initial-height,var(--sonner-initial-height,auto))}.scrollsheet-toast[data-visible=false]{opacity:0;pointer-events:none}.scrollsheet-toast[data-removed]{pointer-events:none}.scrollsheet-toast[data-removed][data-front=true]{--scrollsheet-toast-y:translateY(calc(var(--scrollsheet-toast-lift) * -100%));opacity:0}.scrollsheet-toast[data-removed][data-front=false][data-expanded=true]{--scrollsheet-toast-y:translateY(calc(var(--scrollsheet-toast-lift) * var(--scrollsheet-toast-stack-offset,var(--sonner-stack-offset,0px)) + var(--scrollsheet-toast-lift) * -100%));opacity:0}.scrollsheet-toast[data-removed][data-front=false][data-expanded=false]{--scrollsheet-toast-y:translateY(40%);opacity:0}.scrollsheet-toast[data-swiping=true]{transform:var(--scrollsheet-toast-y) translateY(var(--scrollsheet-toast-swipe-y,var(--sonner-swipe-y,0px))) translateX(var(--scrollsheet-toast-swipe-x,var(--sonner-swipe-x,0px)));transition:none}.scrollsheet-toast[data-swiped=true]{-webkit-user-select:none;user-select:none}.scrollsheet-toast[data-swipe-out=true]{animation:.2s ease-out forwards scrollsheet-toast-swipe-out}.scrollsheet-toast[data-swipe-direction=left]{--scrollsheet-toast-swipe-out-x:-1}.scrollsheet-toast[data-swipe-direction=right]{--scrollsheet-toast-swipe-out-x:1}.scrollsheet-toast[data-swipe-direction=up]{--scrollsheet-toast-swipe-out-y:-1}.scrollsheet-toast[data-swipe-direction=down]{--scrollsheet-toast-swipe-out-y:1}@keyframes scrollsheet-toast-swipe-out{0%{transform:var(--scrollsheet-toast-y) translateY(var(--scrollsheet-toast-swipe-y,var(--sonner-swipe-y,0px))) translateX(var(--scrollsheet-toast-swipe-x,var(--sonner-swipe-x,0px)));opacity:1}to{transform:var(--scrollsheet-toast-y) translateY(calc(var(--scrollsheet-toast-swipe-y,var(--sonner-swipe-y,0px)) + var(--scrollsheet-toast-swipe-out-y,var(--sonner-swipe-out-y,0)) * 100%)) translateX(calc(var(--scrollsheet-toast-swipe-x,var(--sonner-swipe-x,0px)) + var(--scrollsheet-toast-swipe-out-x,var(--sonner-swipe-out-x,0)) * 100%));opacity:0}}@media (prefers-reduced-motion:reduce){[data-scrollsheet-toaster],.scrollsheet-toast{transition:none!important;animation:none!important}}@media (max-width:600px){[data-scrollsheet-toaster]{right:var(--scrollsheet-toast-mobile-offset,var(--sonner-mobile-offset,16px));left:var(--scrollsheet-toast-mobile-offset,var(--sonner-mobile-offset,16px));width:100%}[data-scrollsheet-toaster] .scrollsheet-toast{width:calc(100% - var(--scrollsheet-toast-mobile-offset,var(--sonner-mobile-offset,16px)) * 2);left:0;right:0}[data-scrollsheet-toaster][data-x-position=left]{left:var(--scrollsheet-toast-mobile-offset,var(--sonner-mobile-offset,16px))}[data-scrollsheet-toaster][data-y-position=bottom]{bottom:var(--scrollsheet-toast-mobile-offset,var(--sonner-mobile-offset,16px))}[data-scrollsheet-toaster][data-y-position=top]{top:var(--scrollsheet-toast-mobile-offset,var(--sonner-mobile-offset,16px))}[data-scrollsheet-toaster][data-x-position=center]{left:var(--scrollsheet-toast-mobile-offset,var(--sonner-mobile-offset,16px));right:var(--scrollsheet-toast-mobile-offset,var(--sonner-mobile-offset,16px));transform:none}}`, "data-sonner-toast-styles"); | ||
| function injectToastStyles(nonce) { | ||
| injectDocument(nonce); | ||
| } | ||
| function injectToastStylesInto(root, nonce) { | ||
| injectShadowRoot(root, nonce); | ||
| } | ||
| //#endregion | ||
| //#region packages/scrollsheet/src/toast/toaster.tsx | ||
| function resolveVisibleToasts(count) { | ||
| if (count === void 0) return 3; | ||
| return Math.max(1, Math.floor(count)); | ||
| } | ||
| function useMountedFlag() { | ||
| const [mounted, setMounted] = React.useState(false); | ||
| React.useEffect(() => { | ||
| if (prefersReducedMotion()) { | ||
| setMounted(true); | ||
| return; | ||
| } | ||
| let raf2 = 0; | ||
| const raf1 = requestAnimationFrame(() => { | ||
| raf2 = requestAnimationFrame(() => setMounted(true)); | ||
| }); | ||
| return () => { | ||
| cancelAnimationFrame(raf1); | ||
| cancelAnimationFrame(raf2); | ||
| }; | ||
| }, []); | ||
| return mounted; | ||
| } | ||
| function useIsDocumentHidden() { | ||
| const [hidden, setHidden] = React.useState(() => typeof document !== "undefined" && document.hidden); | ||
| React.useEffect(() => { | ||
| const onVisibilityChange = () => setHidden(document.hidden); | ||
| document.addEventListener("visibilitychange", onVisibilityChange); | ||
| return () => document.removeEventListener("visibilitychange", onVisibilityChange); | ||
| }, []); | ||
| return hidden; | ||
| } | ||
| const EMPTY_RECORDS = []; | ||
| const EMPTY_MAP = new Map(); | ||
| function toIdMap(records) { | ||
| return new Map(records.map((r) => [r.id, r])); | ||
| } | ||
| function sameIds(a, b) { | ||
| if (a.size !== b.size) return false; | ||
| for (const id of a.keys()) if (!b.has(id)) return false; | ||
| return true; | ||
| } | ||
| function useToastExit(live, queued = EMPTY_RECORDS, exitMs = 220) { | ||
| const [exiting, setExiting] = React.useState(() => EMPTY_MAP); | ||
| const [prevLive, setPrevLive] = React.useState(() => toIdMap(live)); | ||
| const timersRef = React.useRef(new Map()); | ||
| const liveIds = new Set(live.map((r) => r.id)); | ||
| let renderExiting = exiting; | ||
| if (!sameIds(prevLive, liveIds)) { | ||
| const queuedIds = new Set(queued.map((r) => r.id)); | ||
| const justRemoved = []; | ||
| for (const [id, record] of prevLive) { | ||
| if (liveIds.has(id)) continue; | ||
| if (queuedIds.has(id)) continue; | ||
| justRemoved.push(record); | ||
| } | ||
| setPrevLive(toIdMap(live)); | ||
| if (justRemoved.length > 0) { | ||
| const next = new Map(exiting); | ||
| for (const record of justRemoved) next.set(record.id, record); | ||
| renderExiting = next; | ||
| setExiting(next); | ||
| } | ||
| } | ||
| React.useEffect(() => { | ||
| const reduced = prefersReducedMotion(); | ||
| for (const [id] of exiting) { | ||
| if (timersRef.current.has(id)) continue; | ||
| const timer = setTimeout(() => { | ||
| timersRef.current.delete(id); | ||
| setExiting((prev) => { | ||
| if (!prev.has(id)) return prev; | ||
| const next = new Map(prev); | ||
| next.delete(id); | ||
| return next; | ||
| }); | ||
| }, reduced ? 0 : exitMs); | ||
| timersRef.current.set(id, timer); | ||
| } | ||
| for (const [id, timer] of timersRef.current) if (!exiting.has(id)) { | ||
| clearTimeout(timer); | ||
| timersRef.current.delete(id); | ||
| } | ||
| }, [exiting, exitMs]); | ||
| React.useEffect(() => { | ||
| const timers = timersRef.current; | ||
| return () => { | ||
| for (const timer of timers.values()) clearTimeout(timer); | ||
| timers.clear(); | ||
| }; | ||
| }, []); | ||
| const rows = live.map((record) => ({ | ||
| record, | ||
| exiting: false | ||
| })); | ||
| for (const [id, record] of renderExiting) if (!liveIds.has(id)) rows.push({ | ||
| record, | ||
| exiting: true | ||
| }); | ||
| return rows; | ||
| } | ||
| //#endregion | ||
| //#region packages/scrollsheet/src/toast/shell/shell-selectors.ts | ||
| function splitPosition(position) { | ||
| const [y, x] = position.split("-"); | ||
| return { | ||
| y, | ||
| x | ||
| }; | ||
| } | ||
| function computePossiblePositions(defaultPosition, toasts) { | ||
| const seen = new Set([defaultPosition]); | ||
| for (const t of toasts) if (t.position) seen.add(t.position); | ||
| return [...seen]; | ||
| } | ||
| function computeRowOffsets(newestFirst, heights, gap) { | ||
| let cumulativeHeight = 0; | ||
| return newestFirst.map((record, index) => { | ||
| const stackOffset = index * gap + cumulativeHeight; | ||
| cumulativeHeight += heights.get(record.id) ?? 0; | ||
| return { | ||
| id: record.id, | ||
| index, | ||
| toastsBefore: index, | ||
| stackOffset | ||
| }; | ||
| }); | ||
| } | ||
| function findNewestDismissible(toasts) { | ||
| for (let i = toasts.length - 1; i >= 0; i -= 1) { | ||
| const t = toasts[i]; | ||
| if (t && t.dismissible !== false) return t; | ||
| } | ||
| } | ||
| const SWIPE_VELOCITY_THRESHOLD = .11; | ||
| function getDefaultSwipeDirections(position) { | ||
| const { y, x } = splitPosition(position); | ||
| const directions = [y]; | ||
| if (x === "left" || x === "right") directions.push(x); | ||
| return directions; | ||
| } | ||
| function lockSwipeAxis(dx, dy) { | ||
| if (Math.abs(dx) <= 1 && Math.abs(dy) <= 1) return null; | ||
| return Math.abs(dx) > Math.abs(dy) ? "x" : "y"; | ||
| } | ||
| function dampenSwipeDelta(delta) { | ||
| const dampened = delta * (1 / (1.5 + Math.abs(delta) / 20)); | ||
| return Math.abs(dampened) < Math.abs(delta) ? dampened : delta; | ||
| } | ||
| function computeSwipeAxisAmount(axis, delta, directions) { | ||
| const negativeDir = axis === "x" ? "left" : "top"; | ||
| const positiveDir = axis === "x" ? "right" : "bottom"; | ||
| if (!(directions.includes(negativeDir) || directions.includes(positiveDir))) return 0; | ||
| return directions.includes(negativeDir) && delta < 0 || directions.includes(positiveDir) && delta > 0 ? delta : dampenSwipeDelta(delta); | ||
| } | ||
| function isSwipeReleaseAllowed(axis, amount, directions) { | ||
| if (axis === "x") return directions.includes(amount > 0 ? "right" : "left"); | ||
| return directions.includes(amount > 0 ? "bottom" : "top"); | ||
| } | ||
| function shouldDismissOnSwipeRelease(amount, velocity, thresholdPx = 45, velocityThreshold = SWIPE_VELOCITY_THRESHOLD) { | ||
| return Math.abs(amount) >= thresholdPx || velocity > velocityThreshold; | ||
| } | ||
| function resolveSwipeOutDirection(axis, amount) { | ||
| if (axis === "x") return amount > 0 ? "right" : "left"; | ||
| return amount > 0 ? "down" : "up"; | ||
| } | ||
| //#endregion | ||
| //#region packages/scrollsheet/src/toast/shell/use-toast-swipe.ts | ||
| const ZERO_AMOUNT = { | ||
| x: 0, | ||
| y: 0 | ||
| }; | ||
| function useToastSwipe(elRef, enabled, directions, onSwipeDismiss) { | ||
| const draggingRef = React.useRef(false); | ||
| const axisRef = React.useRef(null); | ||
| const startRef = React.useRef(ZERO_AMOUNT); | ||
| const amountRef = React.useRef(ZERO_AMOUNT); | ||
| const dragStartRef = React.useRef(0); | ||
| const onSwipeDismissRef = React.useRef(onSwipeDismiss); | ||
| onSwipeDismissRef.current = onSwipeDismiss; | ||
| const onPointerDown = React.useCallback((event) => { | ||
| if (!enabled || event.button !== 0) return; | ||
| if (event.target.tagName === "BUTTON") return; | ||
| const el = elRef.current; | ||
| if (!el) return; | ||
| draggingRef.current = true; | ||
| axisRef.current = null; | ||
| amountRef.current = ZERO_AMOUNT; | ||
| startRef.current = { | ||
| x: event.clientX, | ||
| y: event.clientY | ||
| }; | ||
| dragStartRef.current = Date.now(); | ||
| el.setPointerCapture(event.pointerId); | ||
| el.setAttribute("data-swiping", "true"); | ||
| }, [enabled, elRef]); | ||
| const onPointerMove = React.useCallback((event) => { | ||
| if (!draggingRef.current) return; | ||
| const el = elRef.current; | ||
| if (!el) return; | ||
| if ((window.getSelection?.()?.toString().length ?? 0) > 0) return; | ||
| const start = startRef.current; | ||
| const dx = event.clientX - start.x; | ||
| const dy = event.clientY - start.y; | ||
| if (axisRef.current === null) axisRef.current = lockSwipeAxis(dx, dy); | ||
| const axis = axisRef.current; | ||
| if (axis === null) return; | ||
| const resolved = computeSwipeAxisAmount(axis, axis === "x" ? dx : dy, directions); | ||
| const amount = axis === "x" ? { | ||
| x: resolved, | ||
| y: 0 | ||
| } : { | ||
| x: 0, | ||
| y: resolved | ||
| }; | ||
| amountRef.current = amount; | ||
| if (resolved !== 0) el.setAttribute("data-swiped", "true"); | ||
| el.style.setProperty("--scrollsheet-toast-swipe-x", `${amount.x}px`); | ||
| el.style.setProperty("--scrollsheet-toast-swipe-y", `${amount.y}px`); | ||
| }, [elRef, directions]); | ||
| const release = React.useCallback(() => { | ||
| if (!draggingRef.current) return; | ||
| draggingRef.current = false; | ||
| const el = elRef.current; | ||
| const axis = axisRef.current; | ||
| axisRef.current = null; | ||
| if (!el) return; | ||
| if (axis === null) { | ||
| el.setAttribute("data-swiping", "false"); | ||
| return; | ||
| } | ||
| const amount = axis === "x" ? amountRef.current.x : amountRef.current.y; | ||
| const elapsed = Math.max(1, Date.now() - dragStartRef.current); | ||
| const velocity = Math.abs(amount) / elapsed; | ||
| if (isSwipeReleaseAllowed(axis, amount, directions) && shouldDismissOnSwipeRelease(amount, velocity)) { | ||
| const direction = resolveSwipeOutDirection(axis, amount); | ||
| el.setAttribute("data-swipe-direction", direction); | ||
| if (prefersReducedMotion()) { | ||
| el.setAttribute("data-swipe-out", "true"); | ||
| onSwipeDismissRef.current(); | ||
| return; | ||
| } | ||
| const onAnimEnd = () => { | ||
| el.removeEventListener("animationend", onAnimEnd); | ||
| onSwipeDismissRef.current(); | ||
| }; | ||
| el.addEventListener("animationend", onAnimEnd); | ||
| el.setAttribute("data-swipe-out", "true"); | ||
| return; | ||
| } | ||
| el.setAttribute("data-swiping", "false"); | ||
| el.setAttribute("data-swiped", "false"); | ||
| el.style.setProperty("--scrollsheet-toast-swipe-x", "0px"); | ||
| el.style.setProperty("--scrollsheet-toast-swipe-y", "0px"); | ||
| }, [elRef, directions]); | ||
| return { | ||
| onPointerDown, | ||
| onPointerMove, | ||
| onPointerUp: React.useCallback((event) => { | ||
| if (elRef.current?.hasPointerCapture(event.pointerId)) elRef.current.releasePointerCapture(event.pointerId); | ||
| release(); | ||
| }, [elRef, release]), | ||
| onPointerCancel: React.useCallback(() => release(), [release]) | ||
| }; | ||
| } | ||
| //#endregion | ||
| //#region packages/scrollsheet/src/toast/shell/toast-row.tsx | ||
| function ToastIcon({ type, icons, spinnerClassName }) { | ||
| if (type === "loading") { | ||
| if (icons?.loading) return jsx(Fragment, { children: icons.loading }); | ||
| return jsx("span", { className: spinnerClassName }); | ||
| } | ||
| switch (type) { | ||
| case "success": return jsx(Fragment, { children: icons?.success ?? "✓" }); | ||
| case "error": return jsx(Fragment, { children: icons?.error ?? "✕" }); | ||
| case "warning": return jsx(Fragment, { children: icons?.warning ?? "!" }); | ||
| case "info": return jsx(Fragment, { children: icons?.info ?? "i" }); | ||
| default: return null; | ||
| } | ||
| } | ||
| function ToastRow({ record, index, total, toastsBefore, stackOffset, height, frontHeight, visible, expanded, removed, yPosition, xPosition, closeButton, directions, onDismiss, observe, toasterClassNames, icons, toasterCloseButtonAriaLabel }) { | ||
| const role = record.type === "error" ? "alert" : "status"; | ||
| const mounted = useMountedFlag(); | ||
| const elRef = React.useRef(null); | ||
| const dismissible = record.dismissible !== false; | ||
| const isFront = index === 0; | ||
| const setRefs = React.useCallback((el) => { | ||
| elRef.current = el; | ||
| observe(record.id, el); | ||
| }, [observe, record.id]); | ||
| const swipeHandlers = useToastSwipe(elRef, !removed && dismissible && record.type !== "loading", directions, () => onDismiss(record)); | ||
| React.useEffect(() => { | ||
| const el = elRef.current; | ||
| if (el) el.inert = removed; | ||
| }, [removed]); | ||
| const style = { | ||
| "--scrollsheet-toast-toasts-before": toastsBefore, | ||
| "--scrollsheet-toast-stack-offset": `${stackOffset}px`, | ||
| "--scrollsheet-toast-front-height": frontHeight !== void 0 ? `${frontHeight}px` : "0px", | ||
| "--scrollsheet-toast-initial-height": height !== void 0 ? `${height}px` : "auto", | ||
| zIndex: Math.max(0, total - index) | ||
| }; | ||
| const rootClassName = cn("scrollsheet-toast", "sonner-toast", record.className, toasterClassNames?.toast, record.classNames?.toast, toasterClassNames?.default, toasterClassNames?.[record.type], record.classNames?.[record.type]); | ||
| const motionAttrs = { | ||
| "data-mounted": mounted ? "" : void 0, | ||
| "data-removed": removed ? "" : void 0, | ||
| "data-visible": visible ? "true" : "false", | ||
| "data-front": isFront ? "true" : "false", | ||
| "data-expanded": expanded ? "true" : "false", | ||
| "data-y-position": yPosition, | ||
| "data-x-position": xPosition, | ||
| "data-index": index, | ||
| "data-dismissible": dismissible ? "true" : "false", | ||
| "data-swiping": "false", | ||
| "data-swiped": "false", | ||
| "aria-hidden": removed ? "true" : void 0 | ||
| }; | ||
| if (record.jsx !== void 0) return jsx("div", { | ||
| ref: setRefs, | ||
| className: rootClassName, | ||
| "data-scrollsheet-toast": "", | ||
| "data-sonner-toast": "", | ||
| "data-scrollsheet-custom": "", | ||
| "data-sonner-custom": "", | ||
| "data-testid": record.testId, | ||
| role, | ||
| style, | ||
| ...motionAttrs, | ||
| ...swipeHandlers, | ||
| children: record.jsx | ||
| }); | ||
| const showCloseButton = closeButton && dismissible && record.type !== "loading"; | ||
| const hasActions = Boolean(record.action || record.cancel || showCloseButton); | ||
| const closeButtonAriaLabel = record.closeButtonAriaLabel ?? toasterCloseButtonAriaLabel ?? "Close toast"; | ||
| return jsxs("div", { | ||
| ref: setRefs, | ||
| className: rootClassName, | ||
| "data-scrollsheet-toast": "", | ||
| "data-sonner-toast": "", | ||
| "data-type": record.type, | ||
| "data-testid": record.testId, | ||
| role, | ||
| style, | ||
| ...motionAttrs, | ||
| ...swipeHandlers, | ||
| children: [ | ||
| jsx("span", { | ||
| className: cn("scrollsheet-toast-icon", "sonner-toast-icon", toasterClassNames?.icon, record.classNames?.icon), | ||
| "data-type": record.type, | ||
| "aria-hidden": "true", | ||
| children: record.icon ?? jsx(ToastIcon, { | ||
| type: record.type, | ||
| icons, | ||
| spinnerClassName: cn("scrollsheet-toast-spinner", "sonner-toast-spinner", toasterClassNames?.loader, record.classNames?.loader) | ||
| }) | ||
| }), | ||
| jsxs("div", { | ||
| className: cn("scrollsheet-toast-body", "sonner-toast-body", toasterClassNames?.content, record.classNames?.content), | ||
| children: [record.title !== void 0 && jsx("div", { | ||
| className: cn("scrollsheet-toast-title", "sonner-toast-title", toasterClassNames?.title, record.classNames?.title), | ||
| children: record.title | ||
| }), record.description !== void 0 && jsx("div", { | ||
| className: cn("scrollsheet-toast-description", "sonner-toast-description", toasterClassNames?.description, record.classNames?.description), | ||
| children: record.description | ||
| })] | ||
| }), | ||
| hasActions && jsxs("div", { | ||
| className: "scrollsheet-toast-actions sonner-toast-actions", | ||
| children: [ | ||
| record.cancel && jsx("button", { | ||
| type: "button", | ||
| className: cn("scrollsheet-toast-cancel", "sonner-toast-cancel", toasterClassNames?.cancelButton, record.classNames?.cancelButton), | ||
| onClick: (event) => { | ||
| if (!dismissible) return; | ||
| record.cancel?.onClick(event); | ||
| onDismiss(record); | ||
| }, | ||
| children: record.cancel.label | ||
| }), | ||
| record.action && jsx("button", { | ||
| type: "button", | ||
| className: cn("scrollsheet-toast-action", "sonner-toast-action", toasterClassNames?.actionButton, record.classNames?.actionButton), | ||
| onClick: (event) => { | ||
| record.action?.onClick(event); | ||
| if (event.defaultPrevented) return; | ||
| onDismiss(record); | ||
| }, | ||
| children: record.action.label | ||
| }), | ||
| showCloseButton && jsx("button", { | ||
| type: "button", | ||
| className: cn("scrollsheet-toast-close", "sonner-toast-close", toasterClassNames?.closeButton, record.classNames?.closeButton), | ||
| "aria-label": closeButtonAriaLabel, | ||
| onClick: () => onDismiss(record), | ||
| children: icons?.close ?? "×" | ||
| }) | ||
| ] | ||
| }) | ||
| ] | ||
| }); | ||
| } | ||
| //#endregion | ||
| //#region packages/scrollsheet/src/toast/shell/use-toast-heights.ts | ||
| function useToastHeights() { | ||
| const [heights, setHeights] = React.useState(() => new Map()); | ||
| const observersRef = React.useRef(new Map()); | ||
| const setHeight = React.useCallback((id, height) => { | ||
| setHeights((prev) => prev.get(id) === height ? prev : new Map(prev).set(id, height)); | ||
| }, []); | ||
| const forget = React.useCallback((id) => { | ||
| observersRef.current.get(id)?.disconnect(); | ||
| observersRef.current.delete(id); | ||
| setHeights((prev) => { | ||
| if (!prev.has(id)) return prev; | ||
| const next = new Map(prev); | ||
| next.delete(id); | ||
| return next; | ||
| }); | ||
| }, []); | ||
| const observe = React.useCallback((id, el) => { | ||
| const observers = observersRef.current; | ||
| observers.get(id)?.disconnect(); | ||
| observers.delete(id); | ||
| if (!el || typeof ResizeObserver === "undefined") return; | ||
| const ro = new ResizeObserver(() => setHeight(id, el.getBoundingClientRect().height)); | ||
| ro.observe(el); | ||
| observers.set(id, ro); | ||
| setHeight(id, el.getBoundingClientRect().height); | ||
| }, [setHeight]); | ||
| React.useEffect(() => { | ||
| const observers = observersRef.current; | ||
| return () => { | ||
| for (const ro of observers.values()) ro.disconnect(); | ||
| observers.clear(); | ||
| }; | ||
| }, []); | ||
| return { | ||
| heights, | ||
| observe, | ||
| forget | ||
| }; | ||
| } | ||
| //#endregion | ||
| //#region packages/scrollsheet/src/toast/shell/toaster-shell.tsx | ||
| const DEFAULT_HOTKEY = ["altKey", "KeyT"]; | ||
| function PositionGroup({ position, defaultPosition, toasts, visibleToasts, gap, expanded, closeButton, swipeDirections, toastOptions, icons, className, baseStyle, registerListEl, dismissRow, onHoverEnter, onHoverLeave, onInteractingChange }) { | ||
| const directions = React.useMemo(() => swipeDirections ?? getDefaultSwipeDirections(position), [swipeDirections, position]); | ||
| const positionToasts = React.useMemo(() => selectPositionToasts(toasts, position, defaultPosition), [ | ||
| toasts, | ||
| position, | ||
| defaultPosition | ||
| ]); | ||
| const newestFirst = React.useMemo(() => [...positionToasts].reverse(), [positionToasts]); | ||
| const window_ = React.useMemo(() => selectToastWindow(positionToasts, visibleToasts), [positionToasts, visibleToasts]); | ||
| const visibleIds = React.useMemo(() => new Set(window_.visible.map((t) => t.id)), [window_]); | ||
| const { heights, observe, forget } = useToastHeights(); | ||
| const exitRows = useToastExit(newestFirst); | ||
| const exitingIds = React.useMemo(() => new Set(exitRows.filter((r) => r.exiting).map((r) => r.record.id)), [exitRows]); | ||
| React.useEffect(() => { | ||
| for (const id of exitingIds) forget(id); | ||
| }, [exitingIds, forget]); | ||
| const offsets = React.useMemo(() => computeRowOffsets(newestFirst, heights, gap), [ | ||
| newestFirst, | ||
| heights, | ||
| gap | ||
| ]); | ||
| const offsetById = React.useMemo(() => new Map(offsets.map((o) => [o.id, o])), [offsets]); | ||
| const lastOffsetRef = React.useRef(new Map()); | ||
| for (const o of offsets) lastOffsetRef.current.set(o.id, o); | ||
| if (lastOffsetRef.current.size > offsets.length) { | ||
| const keep = new Set([...offsets.map((o) => o.id), ...exitingIds]); | ||
| for (const id of [...lastOffsetRef.current.keys()]) if (!keep.has(id)) lastOffsetRef.current.delete(id); | ||
| } | ||
| const frontHeight = heights.get(newestFirst[0]?.id ?? ""); | ||
| const { y, x } = splitPosition(position); | ||
| const listRef = React.useCallback((el) => registerListEl(position, el), [registerListEl, position]); | ||
| if (exitRows.length === 0) return null; | ||
| return jsx("ol", { | ||
| ref: listRef, | ||
| "data-scrollsheet-toaster": "", | ||
| "data-sonner-toaster": "", | ||
| "data-scrollsheet-theme": "light", | ||
| "data-sonner-theme": "light", | ||
| "data-y-position": y, | ||
| "data-x-position": x, | ||
| tabIndex: -1, | ||
| className, | ||
| style: { | ||
| ...baseStyle, | ||
| "--scrollsheet-toast-gap": `${gap}px`, | ||
| "--scrollsheet-toast-front-height": frontHeight !== void 0 ? `${frontHeight}px` : void 0 | ||
| }, | ||
| onMouseEnter: onHoverEnter, | ||
| onMouseMove: onHoverEnter, | ||
| onMouseLeave: onHoverLeave, | ||
| onPointerDown: (event) => { | ||
| if (event.target.dataset.dismissible === "false") return; | ||
| onInteractingChange(true); | ||
| }, | ||
| onPointerUp: () => onInteractingChange(false), | ||
| children: exitRows.map(({ record, exiting }) => { | ||
| const offset = offsetById.get(record.id) ?? lastOffsetRef.current.get(record.id); | ||
| const index = offset?.index ?? newestFirst.length; | ||
| return jsx(ToastRow, { | ||
| record, | ||
| index, | ||
| total: newestFirst.length, | ||
| toastsBefore: offset?.toastsBefore ?? index, | ||
| stackOffset: offset?.stackOffset ?? 0, | ||
| height: heights.get(record.id), | ||
| frontHeight, | ||
| visible: visibleIds.has(record.id), | ||
| expanded, | ||
| removed: exiting, | ||
| yPosition: y, | ||
| xPosition: x, | ||
| closeButton, | ||
| directions, | ||
| onDismiss: dismissRow, | ||
| observe, | ||
| toasterClassNames: toastOptions?.classNames, | ||
| icons, | ||
| toasterCloseButtonAriaLabel: toastOptions?.closeButtonAriaLabel | ||
| }, record.id); | ||
| }) | ||
| }); | ||
| } | ||
| function resolveShellPosition(position) { | ||
| return position ?? "bottom-right"; | ||
| } | ||
| function ToasterShell({ id, toasterId, position, theme, richColors, expand: forceExpand, visibleToasts: visibleToastsProp, closeButton = false, duration: durationProp, gap = 14, offset, swipeDirections, toastOptions, icons, className, style, containerAriaLabel = "Notifications", hotkey = DEFAULT_HOTKEY, nonce }) { | ||
| const resolvedId = id ?? toasterId; | ||
| if (toasterId !== void 0) warnOnce("toaster-id-deprecated", "[scrollsheet Toaster] Toaster's \"toasterId\" prop is deprecated — use \"id\" instead."); | ||
| const { toasts: allToasts } = useSonner(); | ||
| const toasts = React.useMemo(() => selectToasterToasts(allToasts, resolvedId), [allToasts, resolvedId]); | ||
| const defaultPosition = resolveShellPosition(position); | ||
| const possiblePositions = React.useMemo(() => computePossiblePositions(defaultPosition, toasts), [defaultPosition, toasts]); | ||
| const visibleToasts = resolveVisibleToasts(visibleToastsProp); | ||
| const defaultDuration = durationProp ?? toastOptions?.duration ?? 4e3; | ||
| if (theme !== void 0 && theme !== "light") warnOnce("shell-theme", `[scrollsheet Toaster] theme="${theme}" isn't implemented yet — v1 always renders the static light card real Sonner ships by default.`); | ||
| if (richColors) warnOnce("shell-rich-colors", "[scrollsheet Toaster] richColors isn't implemented yet — toasts render with their default icon color only."); | ||
| React.useEffect(() => { | ||
| injectToastStyles(nonce); | ||
| }, [nonce]); | ||
| const [mounted, setMounted] = React.useState(false); | ||
| React.useEffect(() => setMounted(true), []); | ||
| const [hovered, setHovered] = React.useState(false); | ||
| const [hotkeyExpanded, setHotkeyExpanded] = React.useState(false); | ||
| const [interacting, setInteracting] = React.useState(false); | ||
| const expanded = Boolean(forceExpand) || hovered || hotkeyExpanded; | ||
| const isDocumentHidden = useIsDocumentHidden(); | ||
| const handleHoverEnter = React.useCallback(() => setHovered(true), []); | ||
| const handleHoverLeave = React.useCallback(() => { | ||
| if (!interacting) setHovered(false); | ||
| }, [interacting]); | ||
| const dismissRow = React.useCallback((record) => { | ||
| toast.dismiss(record.id); | ||
| }, []); | ||
| const listElsRef = React.useRef(new Map()); | ||
| const registerListEl = React.useCallback((pos, el) => { | ||
| if (el) listElsRef.current.set(pos, el); | ||
| else listElsRef.current.delete(pos); | ||
| }, []); | ||
| React.useEffect(() => { | ||
| if (hotkey.length === 0) return; | ||
| const handleKeyDown = (event) => { | ||
| if (hotkey.every((key) => event[key] || event.code === key)) { | ||
| setHotkeyExpanded(true); | ||
| const [firstList] = listElsRef.current.values(); | ||
| firstList?.focus({ preventScroll: true }); | ||
| } | ||
| if (event.code !== "Escape") return; | ||
| const active = document.activeElement; | ||
| const focusedEntry = [...listElsRef.current.entries()].find(([, el]) => active === el || el.contains(active)); | ||
| if (!focusedEntry) return; | ||
| if (hotkeyExpanded) { | ||
| setHotkeyExpanded(false); | ||
| return; | ||
| } | ||
| const [focusedPosition] = focusedEntry; | ||
| const positionToasts = selectPositionToasts(toasts, focusedPosition, defaultPosition); | ||
| const front = positionToasts[positionToasts.length - 1]; | ||
| if (front) dismissRow(front); | ||
| }; | ||
| document.addEventListener("keydown", handleKeyDown); | ||
| return () => document.removeEventListener("keydown", handleKeyDown); | ||
| }, [ | ||
| hotkey, | ||
| hotkeyExpanded, | ||
| toasts, | ||
| defaultPosition, | ||
| dismissRow | ||
| ]); | ||
| const timersRef = React.useRef(new Map()); | ||
| React.useEffect(() => { | ||
| const timers = timersRef.current; | ||
| const liveIds = new Set(toasts.map((t) => t.id)); | ||
| for (const [id, entry] of timers) { | ||
| if (liveIds.has(id)) continue; | ||
| if (entry.timer) clearTimeout(entry.timer); | ||
| timers.delete(id); | ||
| } | ||
| const paused = expanded || interacting || isDocumentHidden; | ||
| for (const t of toasts) { | ||
| if (t.type === "loading") { | ||
| const stale = timers.get(t.id); | ||
| if (stale) { | ||
| if (stale.timer) clearTimeout(stale.timer); | ||
| timers.delete(t.id); | ||
| } | ||
| continue; | ||
| } | ||
| const ms = t.duration ?? toastOptions?.duration ?? defaultDuration; | ||
| const existing = timers.get(t.id); | ||
| if (!existing || existing.record !== t) { | ||
| if (existing?.timer) clearTimeout(existing.timer); | ||
| const entry = { | ||
| timer: null, | ||
| startedAt: 0, | ||
| remaining: ms, | ||
| record: t | ||
| }; | ||
| timers.set(t.id, entry); | ||
| if (Number.isFinite(ms) && !paused) { | ||
| entry.startedAt = Date.now(); | ||
| entry.timer = setTimeout(() => { | ||
| timersRef.current.delete(t.id); | ||
| expire(t.id); | ||
| }, ms); | ||
| } | ||
| continue; | ||
| } | ||
| if (paused) { | ||
| if (existing.timer) { | ||
| clearTimeout(existing.timer); | ||
| const elapsed = Date.now() - existing.startedAt; | ||
| existing.remaining = Math.max(0, existing.remaining - elapsed); | ||
| existing.timer = null; | ||
| } | ||
| continue; | ||
| } | ||
| if (existing.timer || !Number.isFinite(existing.remaining)) continue; | ||
| existing.startedAt = Date.now(); | ||
| existing.timer = setTimeout(() => { | ||
| timersRef.current.delete(t.id); | ||
| expire(t.id); | ||
| }, existing.remaining); | ||
| } | ||
| }, [ | ||
| toasts, | ||
| expanded, | ||
| interacting, | ||
| isDocumentHidden, | ||
| defaultDuration, | ||
| toastOptions?.duration | ||
| ]); | ||
| React.useEffect(() => { | ||
| const timers = timersRef.current; | ||
| return () => { | ||
| for (const entry of timers.values()) if (entry.timer) clearTimeout(entry.timer); | ||
| timers.clear(); | ||
| }; | ||
| }, []); | ||
| useCloseWatcher({ | ||
| present: toasts.length > 0, | ||
| nonModal: true, | ||
| escapeDismissible: true, | ||
| onClose: () => { | ||
| const target = findNewestDismissible(toasts); | ||
| if (target) dismissRow(target); | ||
| } | ||
| }); | ||
| if (!mounted || typeof document === "undefined") return null; | ||
| const offsetValue = offset !== void 0 ? typeof offset === "number" ? `${offset}px` : offset : void 0; | ||
| const baseStyle = { | ||
| ...style, | ||
| "--scrollsheet-toast-offset": offsetValue | ||
| }; | ||
| return createPortal(jsx("section", { | ||
| "aria-label": containerAriaLabel, | ||
| tabIndex: -1, | ||
| "aria-live": "polite", | ||
| "aria-relevant": "additions text", | ||
| "aria-atomic": "false", | ||
| suppressHydrationWarning: true, | ||
| "data-react-aria-top-layer": "", | ||
| children: possiblePositions.map((groupPosition) => jsx(PositionGroup, { | ||
| position: groupPosition, | ||
| defaultPosition, | ||
| toasts, | ||
| visibleToasts, | ||
| gap, | ||
| expanded, | ||
| closeButton, | ||
| swipeDirections, | ||
| toastOptions, | ||
| icons, | ||
| className, | ||
| baseStyle, | ||
| registerListEl, | ||
| dismissRow, | ||
| onHoverEnter: handleHoverEnter, | ||
| onHoverLeave: handleHoverLeave, | ||
| onInteractingChange: setInteracting | ||
| }, groupPosition)) | ||
| }), document.body); | ||
| } | ||
| //#endregion | ||
| export { useSonner as a, toast as i, resolveVisibleToasts as n, useToasts as o, injectToastStylesInto as r, ToasterShell as t }; |
Sorry, the diff of this file is too big to display
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.
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
481632
0.09%8561
0.09%1
Infinity%