braid-design-system
Version:
Themeable design system for the SEEK Group
197 lines (196 loc) • 6.57 kB
JavaScript
"use strict";
const react = require("react");
const entries_css_cjs = require("../../../css.cjs");
const lib_hooks_useIsomorphicLayoutEffect_cjs = require("../../hooks/useIsomorphicLayoutEffect.cjs");
const lib_utils_px_cjs = require("../../utils/px.cjs");
const lib_components_useToast_Toast_css_cjs = require("./Toast.css.cjs");
const animationTimeout = 200;
const baseTransition = "opacity 0.2s ease, transform 0.2s ease, height 0.2s ease";
const exitTransition = "opacity 0.2s ease, height 0.2s ease";
const visibleStackedToasts = 3;
const animate = (element, transforms, transition, done) => {
const fallbackTimeout = setTimeout(() => {
if (done) {
done();
}
}, animationTimeout);
transforms.forEach(({ property, from = "" }) => {
if (property === "className") {
if (from) {
element.classList.add(from);
}
} else {
element.style.setProperty(property, from);
}
});
element.style.setProperty("transition", "");
const transitionEndHandler = (ev) => {
if (ev.target !== element) {
return;
}
element.style.setProperty("transition", "");
if (done) {
done();
}
element.removeEventListener("transitionend", transitionEndHandler);
clearTimeout(fallbackTimeout);
};
element.addEventListener("transitionend", transitionEndHandler);
window.requestAnimationFrame(() => {
window.requestAnimationFrame(() => {
element.style.setProperty("transition", transition);
transforms.forEach(({ property, from = "", to = "" }) => {
if (property === "className") {
if (from) {
element.classList.remove(from);
}
if (to) {
element.classList.add(to);
}
} else {
element.style.setProperty(property, to);
}
});
});
});
};
const useFlipList = (expanded) => {
const refs = react.useMemo(() => /* @__PURE__ */ new Map(), []);
const toastStates = react.useMemo(() => /* @__PURE__ */ new Map(), []);
lib_hooks_useIsomorphicLayoutEffect_cjs.useIsomorphicLayoutEffect(() => {
const animations = [];
const getCurrentAndFullHeight = (element) => {
const currentHeight = element.getBoundingClientRect().height;
element.style.height = "auto";
const fullHeight = element.getBoundingClientRect().height;
element.style.height = lib_utils_px_cjs.px(currentHeight);
return { currentHeight, fullHeight };
};
const activeToasts = [];
Array.from(refs.entries()).forEach(([toastKey, element]) => {
if (element !== null) {
if (toastStates.get(toastKey) === "exiting") {
refs.delete(toastKey);
} else {
activeToasts.push([toastKey, element]);
}
}
});
activeToasts.forEach(([toastKey, element], index) => {
const position = activeToasts.length - index - 1;
const { opacity, transform } = element.style;
const { currentHeight, fullHeight } = getCurrentAndFullHeight(element);
let animationState;
if (position > 0) {
animationState = expanded ? "expand" : "collapse";
} else if (toastStates.get(toastKey) !== "entered") {
animationState = "enter";
} else {
animationState = "become-first";
}
switch (animationState) {
case "expand":
animations.push({
element,
transition: baseTransition,
transforms: [
{
property: "height",
from: lib_utils_px_cjs.px(currentHeight),
to: lib_utils_px_cjs.px(fullHeight)
},
{ property: "transform", from: transform, to: void 0 },
{ property: "opacity", from: opacity, to: "1" },
{ property: "className", from: lib_components_useToast_Toast_css_cjs.collapsed, to: void 0 }
]
});
break;
case "collapse":
const visible = position < visibleStackedToasts;
const scale = position === 1 ? 0.9 : 0.8;
animations.push({
element,
transition: baseTransition,
transforms: [
{
property: "height",
from: lib_utils_px_cjs.px(currentHeight),
to: visible ? entries_css_cjs.vars.space.small : "0px"
},
{
property: "transform",
from: transform,
to: `scaleX(${scale})`
},
{ property: "opacity", from: opacity, to: visible ? "1" : "0" },
{
property: "className",
from: void 0,
to: position > 0 ? lib_components_useToast_Toast_css_cjs.collapsed : void 0
}
]
});
break;
case "enter":
animations.push({
element,
transition: baseTransition,
transforms: [
{ property: "opacity", from: "0" },
{ property: "height", from: "0px", to: lib_utils_px_cjs.px(fullHeight) }
]
});
break;
case "become-first":
animations.push({
element,
transition: baseTransition,
transforms: [
{
property: "height",
from: lib_utils_px_cjs.px(currentHeight),
to: lib_utils_px_cjs.px(fullHeight)
},
{ property: "transform", from: transform },
{
property: "className",
from: expanded ? lib_components_useToast_Toast_css_cjs.collapsed : void 0,
to: void 0
}
]
});
break;
}
toastStates.set(toastKey, "entered");
});
animations.forEach(({ element, transforms, transition }) => {
animate(element, transforms, transition);
});
});
const remove = react.useCallback(
(toastKey, cb) => {
const element = refs.get(toastKey);
if (element) {
toastStates.set(toastKey, "exiting");
animate(
element,
[
{ property: "opacity", to: "0" },
{ property: "height", from: element.style.height, to: "0px" }
],
exitTransition,
cb
);
}
},
[refs, toastStates]
);
const itemRef = react.useCallback(
(toastKey) => (ref) => {
refs.set(toastKey, ref);
},
[refs]
);
return { itemRef, remove };
};
exports.useFlipList = useFlipList;