import React, { useEffect, useRef, useState } from 'react'; import { Group, Trip } from '../../models'; import styles from './Sidebar.module.css'; interface Props { trips: Trip[]; currentTrip: Trip; groups: Group[]; currentGroup: Group | undefined; headline?: string; asideOpen: boolean; handleClose: () => void; handleGroupChange: (groupIndex: number, openGallery?: boolean) => void; handleTripChange: (tripIndex: number) => void; } export default function Sidebar({ trips, currentTrip, groups, currentGroup, asideOpen, handleClose, handleGroupChange, handleTripChange, }: Props): JSX.Element { const [tripListOpen, setTripListOpen] = useState(false); const wrapperRef = useRef<null | HTMLElement>(null); useEffect(() => { function handleClickOutside(e: Event): void { if ( wrapperRef.current !== null && e.target !== null && !wrapperRef.current.contains(e.target as Node) ) { handleClose(); } } document.addEventListener('mousedown', handleClickOutside); return () => { document.removeEventListener('mousedown', handleClickOutside); }; }, [wrapperRef]); function groupChangeHandler(groupIndex: number, openGallery?: boolean): void { handleGroupChange(groupIndex, openGallery); handleClose(); } function tripChangeHandler(tripIndex: number): void { handleTripChange(tripIndex); setTripListOpen(false); } function buildGroupDescription( template: string | undefined, tokens: { [key: string]: string | number } | undefined, ): string { if (template === undefined) { return ''; } let description = template; if (tokens === undefined) { return description; } Object.keys(tokens).forEach((key) => { description = description.replaceAll(`{${key}}`, `${tokens[key]}`); }); return description; } const asideStyle = asideOpen ? `${styles.aside} ${styles.asideOpen}` : styles.aside; return ( <aside ref={wrapperRef} className={asideStyle}> {currentTrip.name !== undefined && ( <div className={styles.headline}> <h2>{currentTrip.name}</h2> {trips.length > 1 && ( <button type="button" title="Show list of trips" onClick={() => setTripListOpen(!tripListOpen)} > <div></div> </button> )} {tripListOpen && ( <div className={styles.tripList}> <ul> {trips.map((t, i) => ( <li key={i}> <a onClick={() => tripChangeHandler(i)}>{t.name}</a> </li> ))} </ul> </div> )} </div> )} <ul className={styles.list}> {groups.map((group, index) => { const listItemStyle = group.id === currentGroup?.id ? `${styles.listItem} ${styles.listItemActive}` : styles.listItem; return ( <li key={index}> <a onClick={() => groupChangeHandler(index)} onKeyPress={() => groupChangeHandler(index)} tabIndex={0} role="menuitem" className={listItemStyle} > <div className={styles.listItemContent}> <b>{group.name}</b> <br /> <div className={styles.preformatted}> {buildGroupDescription(group.description, group.metadata)} </div> </div> <button type="button" title="Show gallery" className={styles.listItemButton} onClick={(e) => { e.stopPropagation(); groupChangeHandler(index, true); }} > <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"> <path d="M512 32H160c-35.35 0-64 28.65-64 64v224c0 35.35 28.65 64 64 64H512c35.35 0 64-28.65 64-64V96C576 60.65 547.3 32 512 32zM528 320c0 8.822-7.178 16-16 16h-16l-109.3-160.9C383.7 170.7 378.7 168 373.3 168c-5.352 0-10.35 2.672-13.31 7.125l-62.74 94.11L274.9 238.6C271.9 234.4 267.1 232 262 232c-5.109 0-9.914 2.441-12.93 6.574L176 336H160c-8.822 0-16-7.178-16-16V96c0-8.822 7.178-16 16-16H512c8.822 0 16 7.178 16 16V320zM224 112c-17.67 0-32 14.33-32 32s14.33 32 32 32c17.68 0 32-14.33 32-32S241.7 112 224 112zM456 480H120C53.83 480 0 426.2 0 360v-240C0 106.8 10.75 96 24 96S48 106.8 48 120v240c0 39.7 32.3 72 72 72h336c13.25 0 24 10.75 24 24S469.3 480 456 480z" /> </svg> </button> </a> </li> ); })} </ul> </aside> ); }