@navikt/ds-react
Version:
React components from the Norwegian Labour and Welfare Administration.
216 lines • 10.4 kB
JavaScript
;
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