braid-design-system
Version:
Themeable design system for the SEEK Group
159 lines (158 loc) • 6.11 kB
JavaScript
import { jsxs, jsx } from "react/jsx-runtime";
import assert from "assert";
import { forwardRef, useCallback, useEffect, cloneElement } from "react";
import { Box } from "../Box/Box.mjs";
import { ButtonIcon } from "../ButtonIcon/ButtonIcon.mjs";
import { Column } from "../Column/Column.mjs";
import { Columns } from "../Columns/Columns.mjs";
import { Inline } from "../Inline/Inline.mjs";
import { Stack } from "../Stack/Stack.mjs";
import { Text } from "../Text/Text.mjs";
import { TextLinkButton } from "../TextLinkButton/TextLinkButton.mjs";
import { Overlay } from "../private/Overlay/Overlay.mjs";
import { buildDataAttributes } from "../private/buildDataAttributes.mjs";
import { toastGap } from "./consts.mjs";
import { useTimeout } from "./useTimeout.mjs";
import { toast, collapsedToastContent } from "./Toast.css.mjs";
import { lineHeightContainer } from "../../css/lineHeightContainer.css.mjs";
import { IconClear } from "../icons/IconClear/IconClear.mjs";
import { IconPositive } from "../icons/IconPositive/IconPositive.mjs";
import { IconCritical } from "../icons/IconCritical/IconCritical.mjs";
const toneToIcon = {
critical: IconCritical,
positive: IconPositive
};
const toastDuration = 10 * 1e3;
const borderRadius = "large";
const Action = ({ label, onClick, removeToast }) => {
const handleClick = useCallback(
(event) => {
event.stopPropagation();
removeToast();
onClick();
},
[removeToast, onClick]
);
return /* @__PURE__ */ jsx(Text, { baseline: false, children: /* @__PURE__ */ jsx(Box, { component: "span", "aria-hidden": true, children: /* @__PURE__ */ jsx(TextLinkButton, { onClick: handleClick, hitArea: "large", children: label }) }) });
};
const ToastIcon = ({ tone, icon }) => {
if (tone !== "neutral") {
const Icon = toneToIcon[tone];
return /* @__PURE__ */ jsx(Icon, { tone });
}
if (icon) {
return cloneElement(icon, { tone });
}
return null;
};
const Toast = forwardRef(
({
toastKey,
dedupeKey,
message,
description,
tone,
icon,
onClose,
closeLabel = "Close",
action,
shouldRemove,
data,
expanded = true,
...restProps
}, ref) => {
const remove = useCallback(
() => onClose(dedupeKey, toastKey),
[onClose, dedupeKey, toastKey]
);
const { stopTimeout, startTimeout } = useTimeout({
duration: toastDuration,
onTimeout: remove
});
useEffect(() => {
if (shouldRemove) {
stopTimeout();
remove();
}
}, [shouldRemove, remove, stopTimeout]);
useEffect(
() => expanded ? stopTimeout() : startTimeout(),
[expanded, startTimeout, stopTimeout]
);
assert(
!icon || icon.props.size === void 0 && icon.props.tone === void 0,
"Icons cannot set the 'size' or 'tone' prop when passed to a Toast component"
);
assert(
!icon || icon && tone === "neutral",
`Icons cannot be customised on a Toast component using '${tone}' tone`
);
const content = description ? /* @__PURE__ */ jsxs(Stack, { space: "xxsmall", children: [
/* @__PURE__ */ jsx(Text, { weight: "medium", tone, baseline: false, children: message }),
description ? /* @__PURE__ */ jsx(Text, { baseline: false, tone: "secondary", children: description }) : null,
action ? /* @__PURE__ */ jsx(Action, { removeToast: remove, ...action }, action.label) : null
] }) : /* @__PURE__ */ jsxs(Inline, { space: "xxsmall", children: [
/* @__PURE__ */ jsx(Box, { paddingRight: "medium", children: /* @__PURE__ */ jsx(Text, { weight: "medium", tone, baseline: false, children: message }) }),
action ? /* @__PURE__ */ jsx(Action, { removeToast: remove, ...action }, action.label) : null
] });
return /* @__PURE__ */ jsx(Box, { position: "relative", width: "full", display: "flex", ref, children: /* @__PURE__ */ jsxs(
Box,
{
role: "alert",
textAlign: "left",
background: { lightMode: "surfaceDark", darkMode: "surface" },
boxShadow: {
lightMode: "borderNeutral",
darkMode: "borderNeutralLight"
},
borderRadius,
paddingY: "medium",
paddingX: "gutter",
marginTop: toastGap,
width: "full",
position: "relative",
className: toast,
tabIndex: 0,
outline: "focus",
onClick: (event) => {
event.stopPropagation();
},
...buildDataAttributes({ data, validateRestProps: restProps }),
children: [
/* @__PURE__ */ jsx(Box, { transition: "fast", className: collapsedToastContent, children: /* @__PURE__ */ jsxs(Columns, { space: "none", children: [
tone !== "neutral" || tone === "neutral" && icon ? /* @__PURE__ */ jsx(Column, { width: "content", children: /* @__PURE__ */ jsx(Box, { paddingRight: "small", children: /* @__PURE__ */ jsx(ToastIcon, { tone, icon }) }) }) : null,
/* @__PURE__ */ jsx(Column, { children: content }),
/* @__PURE__ */ jsx(Column, { width: "content", children: /* @__PURE__ */ jsx(
Box,
{
width: "touchable",
display: "flex",
justifyContent: "flexEnd",
alignItems: "center",
className: lineHeightContainer.standard,
"aria-hidden": true,
children: /* @__PURE__ */ jsx(
ButtonIcon,
{
icon: /* @__PURE__ */ jsx(IconClear, { tone: "secondary" }),
variant: "transparent",
onClick: (event) => {
event.stopPropagation();
remove();
},
label: closeLabel,
data: process.env.NODE_ENV !== "production" ? { testid: "clearToast" } : {}
}
)
}
) })
] }) }),
/* @__PURE__ */ jsx(Overlay, { visible: true, borderRadius, boxShadow: "large" })
]
}
) });
}
);
export {
Toast
};