UNPKG

@navikt/ds-react

Version:

React components from the Norwegian Labour and Welfare Administration.

216 lines 10.4 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); var __rest = (this && this.__rest) || function (s, e) { var t = {}; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p]; if (s != null && typeof Object.getOwnPropertySymbols === "function") for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) t[p[i]] = s[p[i]]; } return t; }; Object.defineProperty(exports, "__esModule", { value: true }); /* https://github.com/Stanko/react-animate-height/blob/v3/src/index.tsx */ const react_1 = __importStar(require("react")); const Theme_1 = require("../theme/Theme"); // ------------------ Helpers const prefersReducedMotion = (globalThis === null || globalThis === void 0 ? void 0 : globalThis.matchMedia) ? globalThis.matchMedia("(prefers-reduced-motion: reduce)").matches : false; function isNumber(n) { const number = parseFloat(n); return !Number.isNaN(number) && Number.isFinite(number); } function isPercentage(height) { // Percentage height return (typeof height === "string" && height[height.length - 1] === "%" && isNumber(height.substring(0, height.length - 1))); } function hideContent(element, height) { // Check for element?.style is added cause this would fail in tests (react-test-renderer) // Read more here: https://github.com/Stanko/react-animate-height/issues/17 if (height === 0 && (element === null || element === void 0 ? void 0 : element.style)) { element.style.display = "none"; } } function showContent(element, height) { // Check for element?.style is added cause this would fail in tests (react-test-renderer) // Read more here: https://github.com/Stanko/react-animate-height/issues/17 if (height === 0 && (element === null || element === void 0 ? void 0 : element.style)) { element.style.display = ""; } } const AnimateHeight = (_a) => { var { children, className, innerClassName, duration: userDuration = 250, easing = "ease", height } = _a, props = __rest(_a, ["children", "className", "innerClassName", "duration", "easing", "height"]); const { cn } = (0, Theme_1.useRenameCSS)(); // ------------------ Initialization const prevHeight = (0, react_1.useRef)(height); const contentElement = (0, react_1.useRef)(null); const animationClassesTimeoutID = (0, react_1.useRef)(); const timeoutID = (0, react_1.useRef)(); const initialHeight = (0, react_1.useRef)(height); const initialOverflow = (0, react_1.useRef)("visible"); const duration = prefersReducedMotion ? 0 : userDuration; if (typeof initialHeight.current === "number") { // Reset negative height to 0 if (typeof height !== "string") { initialHeight.current = height < 0 ? 0 : height; } initialOverflow.current = "hidden"; } else if (isPercentage(initialHeight.current)) { // If value is string "0%" make sure we convert it to number 0 initialHeight.current = height === "0%" ? 0 : height; initialOverflow.current = "hidden"; } const [currentHeight, setCurrentHeight] = (0, react_1.useState)(initialHeight.current); const [overflow, setOverflow] = (0, react_1.useState)(initialOverflow.current); const [useTransitions, setUseTransitions] = (0, react_1.useState)(false); (0, react_1.useEffect)(() => { // Hide content if height is 0 (to prevent tabbing into it) hideContent(contentElement.current, initialHeight.current); }, []); // ------------------ Height update // biome-ignore lint/correctness/useExhaustiveDependencies: This should be explicitly run only on height change (0, react_1.useEffect)(() => { if (height !== prevHeight.current && contentElement.current) { showContent(contentElement.current, prevHeight.current); // Cache content height contentElement.current.style.overflow = "hidden"; const contentHeight = contentElement.current.offsetHeight; contentElement.current.style.overflow = ""; // set total animation time const totalDuration = duration; let newHeight; let timeoutHeight; let timeoutOverflow = "hidden"; let timeoutUseTransitions; const isCurrentHeightAuto = prevHeight.current === "auto"; if (typeof height === "number") { // Reset negative height to 0 newHeight = height < 0 ? 0 : height; timeoutHeight = newHeight; } else if (isPercentage(height)) { // If value is string "0%" make sure we convert it to number 0 newHeight = height === "0%" ? 0 : height; timeoutHeight = newHeight; } else { // If not, animate to content height // and then reset to auto newHeight = contentHeight; // TODO solve contentHeight = 0 timeoutHeight = "auto"; timeoutOverflow = undefined; } if (isCurrentHeightAuto) { // This is the height to be animated to timeoutHeight = newHeight; // If previous height was 'auto' // set starting height explicitly to be able to use transition newHeight = contentHeight; } // Set starting height and animating classes // When animating from 'auto' we first need to set fixed height // that change should be animated setCurrentHeight(newHeight); setOverflow("hidden"); setUseTransitions(!isCurrentHeightAuto); // Clear timeouts clearTimeout(timeoutID.current); clearTimeout(animationClassesTimeoutID.current); if (isCurrentHeightAuto) { // When animating from 'auto' we use a short timeout to start animation // after setting fixed height above timeoutUseTransitions = true; // Short timeout to allow rendering of the initial animation state first timeoutID.current = setTimeout(() => { setCurrentHeight(timeoutHeight); setOverflow(timeoutOverflow); setUseTransitions(timeoutUseTransitions); }, 50); // Set static classes and remove transitions when animation ends animationClassesTimeoutID.current = setTimeout(() => { setUseTransitions(false); // ANIMATION ENDS // Hide content if height is 0 (to prevent tabbing into it) hideContent(contentElement.current, timeoutHeight); }, totalDuration); } else { // Set end height, classes and remove transitions when animation is complete timeoutID.current = setTimeout(() => { setCurrentHeight(timeoutHeight); setOverflow(timeoutOverflow); setUseTransitions(false); // ANIMATION ENDS // If height is auto, don't hide the content // (case when element is empty, therefore height is 0) if (height !== "auto") { // Hide content if height is 0 (to prevent tabbing into it) hideContent(contentElement.current, newHeight); // TODO solve newHeight = 0 } }, totalDuration); } } prevHeight.current = height; return () => { clearTimeout(timeoutID.current); clearTimeout(animationClassesTimeoutID.current); }; // This should be explicitly run only on height change // eslint-disable-next-line react-hooks/exhaustive-deps }, [height]); // ------------------ Render const componentStyle = { height: currentHeight, overflow, }; if (useTransitions) { componentStyle.transition = `height ${duration}ms ${easing} 0ms`; // Add webkit vendor prefix still used by opera, blackberry... componentStyle.WebkitTransition = componentStyle.transition; } // Check if user passed aria-hidden prop const hasAriaHiddenProp = typeof props["aria-hidden"] !== "undefined"; const ariaHidden = hasAriaHiddenProp ? props["aria-hidden"] : height === 0; return (react_1.default.createElement("div", Object.assign({}, props, { className: cn(className), style: componentStyle }), react_1.default.createElement("div", { "aria-hidden": ariaHidden, className: cn(innerClassName), ref: contentElement }, children))); }; exports.default = AnimateHeight; //# sourceMappingURL=AnimateHeight.js.map