ui-nyla/src/components/Layout/Panel.tsx
ValueOn AG 27abfde833
All checks were successful
Deploy Nyla Frontend to Integration / deploy (push) Successful in 1m26s
panel fixes 4
2026-06-11 23:29:01 +02:00

90 lines
2.7 KiB
TypeScript

// Copyright (c) 2026 PowerOn AG
// All rights reserved.
import { type FC, useState, useEffect, useCallback } from 'react';
import { useLocation } from 'react-router-dom';
import { FaChevronDown, FaChevronRight } from 'react-icons/fa';
import type { PanelProps } from './types';
import styles from './Panel.module.css';
function _loadCollapsed(key: string, fallback: boolean): boolean {
try {
const stored = localStorage.getItem(`panel-collapse:${key}`);
if (stored !== null) return stored === '1';
} catch { /* noop */ }
return fallback;
}
function _saveCollapsed(key: string, value: boolean): void {
try {
localStorage.setItem(`panel-collapse:${key}`, value ? '1' : '0');
} catch { /* noop */ }
}
export const Panel: FC<PanelProps> = ({
variant = 'card',
title,
id,
subtitle,
actions,
collapsible = true,
defaultCollapsed = false,
collapseKey,
className = '',
style,
fill = false,
children,
}) => {
const { pathname } = useLocation();
const persistKey = collapseKey ?? `${pathname}:${id}`;
const [collapsed, setCollapsed] = useState(() => _loadCollapsed(persistKey, defaultCollapsed));
useEffect(() => {
_saveCollapsed(persistKey, collapsed);
}, [persistKey, collapsed]);
const _toggleCollapsed = useCallback(() => {
if (collapsible) setCollapsed((prev) => !prev);
}, [collapsible]);
return (
<div
className={`${styles.panel} ${collapsed ? styles.panelCollapsed : ''} ${className}`}
data-variant={variant}
data-fill={fill ? 'true' : undefined}
data-panel-id={id}
style={style}
>
<div
className={`${styles.header} ${collapsible ? styles.headerCollapsible : ''}`}
role={collapsible ? 'button' : undefined}
tabIndex={collapsible ? 0 : undefined}
aria-expanded={collapsible ? !collapsed : undefined}
onClick={_toggleCollapsed}
onKeyDown={
collapsible
? (e) => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
_toggleCollapsed();
}
}
: undefined
}
>
<div className={styles.titles}>
<span className={styles.title}>{title}</span>
{subtitle && <span className={styles.subtitle}>{subtitle}</span>}
</div>
{collapsible && (
<span className={styles.chevron} aria-hidden>
{collapsed ? <FaChevronRight /> : <FaChevronDown />}
</span>
)}
{actions && <div className={styles.actions} onClick={(e) => e.stopPropagation()}>{actions}</div>}
</div>
<div className={`${styles.body} ${collapsed ? styles.bodyHidden : ''}`}>
{children}
</div>
</div>
);
};