/* * Copyright (c) 2015 Nordic Semiconductor ASA * * SPDX-License-Identifier: LicenseRef-Nordic-4-Clause */ import React, { useRef, useState } from 'react'; import classNames from '../utils/classNames'; import { getPersistedGroupCollapseState, persistGroupCollapseState, } from '../utils/persistentStore'; const collapseSection = (element: HTMLDivElement) => { const sectionHeight = element.scrollHeight; element.style.overflow = 'hidden'; element.addEventListener( 'transitionstart', () => { element.setAttribute('data-animating', 'true'); element.style.pointerEvents = 'none'; }, { once: true }, ); // on the next frame (as soon as the previous style change has taken effect), // explicitly set the element's height to its current pixel height, so we // aren't transitioning out of 'auto' requestAnimationFrame(() => { element.style.height = `${sectionHeight}px`; // on the next frame (as soon as the previous style change has taken effect), // have the element transition to height: 0 requestAnimationFrame(() => { element.style.height = `${0}px`; element.addEventListener( 'transitionend', () => { element.style.pointerEvents = ''; element.setAttribute('data-animating', 'false'); }, { once: true }, ); }); }); }; const expandSection = (element: HTMLDivElement) => { const sectionHeight = element.scrollHeight; const clientHeight = element.clientHeight; if (sectionHeight === clientHeight) return; element.style.height = `${sectionHeight}px`; element.addEventListener( 'transitionstart', () => { element.style.pointerEvents = 'none'; element.setAttribute('data-animating', 'true'); }, { once: true }, ); element.addEventListener( 'transitionend', () => { element.style.height = ''; element.style.pointerEvents = ''; element.style.overflow = ''; element.setAttribute('data-animating', 'false'); }, { once: true }, ); }; export const Group = ({ className = '', heading, title, children, gap = 2, headingFullWidth = true, collapsible = false, defaultCollapsed = !!collapsible, onToggled, collapseStatePersistenceId, }: { className?: string; heading: React.ReactNode; headingFullWidth?: boolean; title?: string; children?: React.ReactNode; gap?: 0.5 | 1 | 2 | 4 | 8; collapsible?: boolean; defaultCollapsed?: boolean; onToggled?: (isNowExpanded: boolean) => void; /** Unique identifier for persisting the group's collapse state across sessions. When provided, enables state persistence. */ collapseStatePersistenceId?: string; }) => { const getInitialCollapseState = () => { if (collapseStatePersistenceId) { const persistedState = getPersistedGroupCollapseState( collapseStatePersistenceId, ); if (persistedState !== undefined) { return persistedState; } } return defaultCollapsed; }; const [collapsed, setCollapsed] = useState(getInitialCollapseState); const collapsibleDivRef = useRef(null); const initStateSet = useRef(false); return (
{ if (!initStateSet.current && ref) { if (collapsed) { collapseSection(ref); } else { expandSection(ref); } initStateSet.current = true; } collapsibleDivRef.current = ref; }} className={classNames( 'tw-transition-all', !initStateSet.current && collapsed && 'tw-h-0', !initStateSet.current && !collapsed && 'tw-h-full', )} >
{children}
); };