sonner-native
Version:
An opinionated toast component for React Native. A port of @emilkowalski's sonner.
244 lines (242 loc) • 9.6 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.getToastContext = exports.ToasterUI = exports.Toaster = void 0;
var _react = _interopRequireWildcard(require("react"));
var React = _react;
var _reactNative = require("react-native");
var _reactNativeScreens = require("react-native-screens");
var _constants = require("./constants.js");
var _context = require("./context.js");
var _positioner = require("./positioner.js");
var _toast = require("./toast.js");
var _toastComparator = require("./toast-comparator.js");
var _animations = require("./animations.js");
var _jsxRuntime = require("react/jsx-runtime");
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
let addToastHandler;
let dismissToastHandler;
let wiggleHandler;
const Toaster = ({
ToasterOverlayWrapper,
...toasterProps
}) => {
const toastsCounter = React.useRef(1);
const toastRefs = React.useRef({});
const [toasts, setToasts] = React.useState([]);
const [toastsVisible, setToastsVisible] = React.useState(false);
React.useLayoutEffect(() => {
if (toasts.length > 0) {
setToastsVisible(true);
return;
}
// let the animation finish
const timeout = setTimeout(() => {
setToastsVisible(false);
}, _animations.ANIMATION_DURATION);
return () => clearTimeout(timeout);
}, [toasts.length]);
const props = React.useMemo(() => {
return {
...toasterProps,
toasts,
setToasts,
toastsCounter,
toastRefs
};
}, [toasterProps, toasts]);
if (!toastsVisible) {
return /*#__PURE__*/(0, _jsxRuntime.jsx)(ToasterUI, {
...props
});
}
if (ToasterOverlayWrapper) {
return /*#__PURE__*/(0, _jsxRuntime.jsx)(ToasterOverlayWrapper, {
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(ToasterUI, {
...props
})
});
}
if (_reactNative.Platform.OS === 'ios') {
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNativeScreens.FullWindowOverlay, {
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(ToasterUI, {
...props
})
});
}
return /*#__PURE__*/(0, _jsxRuntime.jsx)(ToasterUI, {
...props
});
};
exports.Toaster = Toaster;
const ToasterUI = ({
duration = _constants.toastDefaultValues.duration,
position = _constants.toastDefaultValues.position,
offset = _constants.toastDefaultValues.offset,
visibleToasts = _constants.toastDefaultValues.visibleToasts,
swipeToDismissDirection = _constants.toastDefaultValues.swipeToDismissDirection,
closeButton,
invert,
toastOptions = {},
icons,
pauseWhenPageIsHidden,
gap,
theme,
autoWiggleOnUpdate,
richColors,
toasts,
setToasts,
toastsCounter,
toastRefs,
ToastWrapper,
...props
}) => {
addToastHandler = React.useCallback(options => {
const id = typeof options?.id === 'number' || options.id && options.id?.length > 0 ? options.id : toastsCounter.current++;
setToasts(currentToasts => {
const newToast = {
...options,
id: options?.id ?? id,
variant: options.variant ?? _constants.toastDefaultValues.variant
};
const existingToast = currentToasts.find(currentToast => currentToast.id === newToast.id);
const shouldUpdate = existingToast && options?.id;
if (shouldUpdate) {
const shouldWiggle = autoWiggleOnUpdate === 'always' || autoWiggleOnUpdate === 'toast-change' && !(0, _toastComparator.areToastsEqual)(newToast, existingToast);
if (shouldWiggle && options.id) {
wiggleHandler(options.id);
}
return currentToasts.map(currentToast => {
if (currentToast.id === options.id) {
return {
...currentToast,
...newToast,
duration: options.duration ?? duration,
id: options.id
};
}
return currentToast;
});
} else {
const newToasts = [...currentToasts, newToast];
if (!(newToast.id in toastRefs.current)) {
toastRefs.current[newToast.id] = /*#__PURE__*/React.createRef();
}
if (newToasts.length > visibleToasts) {
newToasts.shift();
}
return newToasts;
}
});
return id;
}, [toastsCounter, toastRefs, visibleToasts, duration, autoWiggleOnUpdate, setToasts]);
const dismissToast = React.useCallback((id, origin) => {
if (!id) {
toasts.forEach(currentToast => {
if (origin === 'onDismiss') {
currentToast.onDismiss?.(currentToast.id);
} else {
currentToast.onAutoClose?.(currentToast.id);
}
});
setToasts([]);
toastsCounter.current = 1;
return;
}
setToasts(currentToasts => currentToasts.filter(currentToast => currentToast.id !== id));
const toastForCallback = toasts.find(currentToast => currentToast.id === id);
if (origin === 'onDismiss') {
toastForCallback?.onDismiss?.(id);
} else {
toastForCallback?.onAutoClose?.(id);
}
return id;
}, [setToasts, toasts, toastsCounter]);
dismissToastHandler = React.useCallback(id => {
return dismissToast(id);
}, [dismissToast]);
wiggleHandler = React.useCallback(id => {
const toastRef = toastRefs.current[id];
if (toastRef && toastRef.current) {
toastRef.current.wiggle();
}
}, [toastRefs]);
const {
unstyled
} = toastOptions;
const value = React.useMemo(() => ({
duration: duration ?? _constants.toastDefaultValues.duration,
position: position ?? _constants.toastDefaultValues.position,
offset: offset ?? _constants.toastDefaultValues.offset,
swipeToDismissDirection: swipeToDismissDirection ?? _constants.toastDefaultValues.swipeToDismissDirection,
closeButton: closeButton ?? _constants.toastDefaultValues.closeButton,
unstyled: unstyled ?? _constants.toastDefaultValues.unstyled,
addToast: addToastHandler,
invert: invert ?? _constants.toastDefaultValues.invert,
icons: icons ?? {},
pauseWhenPageIsHidden: pauseWhenPageIsHidden ?? _constants.toastDefaultValues.pauseWhenPageIsHidden,
gap: gap ?? _constants.toastDefaultValues.gap,
theme: theme ?? _constants.toastDefaultValues.theme,
toastOptions,
autoWiggleOnUpdate: autoWiggleOnUpdate ?? _constants.toastDefaultValues.autoWiggleOnUpdate,
richColors: richColors ?? _constants.toastDefaultValues.richColors
}), [duration, position, offset, swipeToDismissDirection, closeButton, unstyled, invert, icons, pauseWhenPageIsHidden, gap, theme, toastOptions, autoWiggleOnUpdate, richColors]);
const orderToastsFromPosition = React.useCallback(currentToasts => {
return position === 'bottom-center' ? currentToasts : currentToasts.slice().reverse();
}, [position]);
const onDismiss = React.useCallback(id => {
dismissToast(id, 'onDismiss');
}, [dismissToast]);
const onAutoClose = React.useCallback(id => {
dismissToast(id, 'onAutoClose');
}, [dismissToast]);
const allPositions = React.useMemo(() => {
return ['top-center', 'bottom-center', 'center'];
}, []);
const possiblePositions = React.useMemo(() => {
return allPositions.filter(possiblePossition => {
return toasts.find(positionedToast => positionedToast.position === possiblePossition) || value.position === possiblePossition;
});
}, [allPositions, toasts, value.position]);
const orderedToasts = React.useMemo(() => {
return orderToastsFromPosition(toasts);
}, [toasts, orderToastsFromPosition]);
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_context.ToastContext.Provider, {
value: value,
children: possiblePositions.map((currentPosition, positionIndex) => /*#__PURE__*/(0, _jsxRuntime.jsx)(_positioner.Positioner, {
position: currentPosition,
children: orderedToasts.filter(possibleToast => !possibleToast.position && positionIndex === 0 || possibleToast.position === currentPosition).map(toastToRender => {
const ToastToRender = /*#__PURE__*/(0, _react.createElement)(_toast.Toast, {
...toastToRender,
onDismiss: onDismiss,
onAutoClose: onAutoClose,
ref: toastRefs.current[toastToRender.id],
key: toastToRender.id,
...props
});
if (ToastWrapper) {
return /*#__PURE__*/(0, _jsxRuntime.jsx)(ToastWrapper, {
toastId: toastToRender.id,
children: ToastToRender
}, toastToRender.id);
}
return ToastToRender;
})
}, currentPosition))
});
};
exports.ToasterUI = ToasterUI;
const getToastContext = () => {
if (!addToastHandler || !dismissToastHandler || !wiggleHandler) {
throw new Error('ToastContext is not initialized');
}
return {
addToast: addToastHandler,
dismissToast: dismissToastHandler,
wiggleToast: wiggleHandler
};
};
exports.getToastContext = getToastContext;
//# sourceMappingURL=toaster.js.map