sonner-native
Version:
An opinionated toast component for React Native. A port of @emilkowalski's sonner.
173 lines (172 loc) • 6.49 kB
JavaScript
;
import * as React from 'react';
import { Platform } from 'react-native';
import { FullWindowOverlay } from 'react-native-screens';
import { toastDefaultValues } from "./constants.js";
import { DynamicToastContext, ToastContext } from "./context.js";
import { getOrderedToastIds } from "./position-utils.js";
import { Positioner } from "./positioner.js";
import { Toast } from "./toast.js";
import { toastStore } from "./toast-store.js";
import { jsx as _jsx } from "react/jsx-runtime";
import { createElement as _createElement } from "react";
const allPositions = ['top-center', 'bottom-center', 'center'];
const EMPTY_TOAST_OPTIONS = {};
const EMPTY_ICONS = {};
const EMPTY_ANIMATION = {};
function orderToastsFromPosition(currentToasts, position) {
return position === 'top-center' ? currentToasts.slice().reverse() : currentToasts;
}
export const Toaster = ({
ToasterOverlayWrapper,
...toasterProps
}) => {
const storeState = React.useSyncExternalStore(toastStore.subscribe, toastStore.getSnapshot, toastStore.getSnapshot);
const {
toasts,
shouldShowOverlay,
toastHeights,
isExpanded,
toastHeightsVersion
} = storeState;
const uiProps = {
...toasterProps,
toasts,
toastHeights,
isExpanded,
toastHeightsVersion
};
if (!shouldShowOverlay) {
return /*#__PURE__*/_jsx(ToasterUI, {
...uiProps
});
}
if (ToasterOverlayWrapper) {
return /*#__PURE__*/_jsx(ToasterOverlayWrapper, {
children: /*#__PURE__*/_jsx(ToasterUI, {
...uiProps
})
});
}
if (Platform.OS === 'ios') {
return /*#__PURE__*/_jsx(FullWindowOverlay, {
children: /*#__PURE__*/_jsx(ToasterUI, {
...uiProps
})
});
}
return /*#__PURE__*/_jsx(ToasterUI, {
...uiProps
});
};
const ToasterUI = ({
toasts,
toastHeights,
isExpanded,
toastHeightsVersion,
duration = toastDefaultValues.duration,
position = toastDefaultValues.position,
offset = toastDefaultValues.offset,
visibleToasts = toastDefaultValues.visibleToasts,
swipeToDismissDirection = toastDefaultValues.swipeToDismissDirection,
closeButton,
invert,
allowFontScaling,
maxFontSizeMultiplier,
toastOptions = EMPTY_TOAST_OPTIONS,
icons,
pauseWhenPageIsHidden,
gap,
theme,
autoWiggleOnUpdate,
richColors,
enableStacking = toastDefaultValues.enableStacking,
animation,
ToastWrapper,
positionerStyle,
...props
}) => {
React.useEffect(() => {
toastStore.setConfig({
autoWiggleOnUpdate,
visibleToasts,
duration,
pauseWhenPageIsHidden
});
}, [autoWiggleOnUpdate, visibleToasts, duration, pauseWhenPageIsHidden]);
const value = React.useMemo(() => ({
duration: duration ?? toastDefaultValues.duration,
position: position ?? toastDefaultValues.position,
offset: offset ?? toastDefaultValues.offset,
swipeToDismissDirection: swipeToDismissDirection ?? toastDefaultValues.swipeToDismissDirection,
closeButton: closeButton ?? toastDefaultValues.closeButton,
unstyled: toastOptions.unstyled ?? toastDefaultValues.unstyled,
addToast: toastStore.addToast,
invert: invert ?? toastDefaultValues.invert,
allowFontScaling: allowFontScaling ?? toastDefaultValues.allowFontScaling,
maxFontSizeMultiplier,
icons: icons ?? EMPTY_ICONS,
pauseWhenPageIsHidden: pauseWhenPageIsHidden ?? toastDefaultValues.pauseWhenPageIsHidden,
gap: gap ?? toastDefaultValues.gap,
theme: theme ?? toastDefaultValues.theme,
toastOptions,
autoWiggleOnUpdate: autoWiggleOnUpdate ?? toastDefaultValues.autoWiggleOnUpdate,
richColors: richColors ?? toastDefaultValues.richColors,
enableStacking: enableStacking ?? toastDefaultValues.enableStacking,
visibleToasts: visibleToasts ?? toastDefaultValues.visibleToasts,
animation: animation ?? EMPTY_ANIMATION
}), [duration, position, offset, swipeToDismissDirection, closeButton, toastOptions, invert, allowFontScaling, maxFontSizeMultiplier, icons, pauseWhenPageIsHidden, gap, theme, autoWiggleOnUpdate, richColors, enableStacking, visibleToasts, animation]);
const dynamicValue = React.useMemo(() => ({
toastHeights,
toastHeightsVersion,
isExpanded,
expand: toastStore.expand,
collapse: toastStore.collapse,
toggleExpand: toastStore.toggleExpand
}), [toastHeights, toastHeightsVersion, isExpanded]);
const orderedToasts = React.useMemo(() => orderToastsFromPosition(toasts, position), [toasts, position]);
const onDismiss = React.useCallback(id => {
toastStore.dismissToast(id, 'onDismiss');
}, []);
const onAutoClose = React.useCallback(id => {
toastStore.dismissToast(id, 'onAutoClose');
}, []);
const possiblePositions = React.useMemo(() => allPositions.filter(possiblePosition => toasts.find(positionedToast => positionedToast.position === possiblePosition) || position === possiblePosition), [toasts, position]);
return /*#__PURE__*/_jsx(ToastContext.Provider, {
value: value,
children: /*#__PURE__*/_jsx(DynamicToastContext.Provider, {
value: dynamicValue,
children: possiblePositions.map((currentPosition, positionIndex) => {
const toastsForPosition = orderedToasts.filter(possibleToast => !possibleToast.position && positionIndex === 0 || possibleToast.position === currentPosition);
const orderedToastIds = getOrderedToastIds(toastsForPosition, currentPosition, enableStacking);
return /*#__PURE__*/_jsx(Positioner, {
style: positionerStyle,
position: currentPosition,
children: toastsForPosition.map((toastToRender, index) => {
const ToastToRender = /*#__PURE__*/_createElement(Toast, {
...props,
...toastToRender,
parentStyle: props.style,
parentStyles: props.styles,
onDismiss: onDismiss,
onAutoClose: onAutoClose,
index: index,
ref: toastStore.getToastRef(toastToRender.id),
key: toastToRender.id,
numberOfToasts: toastsForPosition.length,
orderedToastIds: orderedToastIds
});
if (ToastWrapper) {
return /*#__PURE__*/_jsx(ToastWrapper, {
toastId: toastToRender.id,
children: ToastToRender
}, toastToRender.id);
}
return ToastToRender;
})
}, currentPosition);
})
})
});
};
//# sourceMappingURL=toaster.js.map