diff options
author | Marcin Zelent <marcin@zelent.net> | 2022-11-16 15:16:38 +0100 |
---|---|---|
committer | Marcin Zelent <marcin@zelent.net> | 2022-11-16 15:16:38 +0100 |
commit | f2ecc1803f3ea294a0c6b7915b61348ed0395b26 (patch) | |
tree | e8c6fb1350ae4f659b3f9ef8d17157158b974b16 /src/components/Sidebar/Sidebar.tsx | |
parent | efb64f24d6200a39870c0e8966ab4f87e07c93a9 (diff) |
Remade and extended the app using React
Diffstat (limited to 'src/components/Sidebar/Sidebar.tsx')
-rw-r--r-- | src/components/Sidebar/Sidebar.tsx | 152 |
1 files changed, 152 insertions, 0 deletions
diff --git a/src/components/Sidebar/Sidebar.tsx b/src/components/Sidebar/Sidebar.tsx new file mode 100644 index 0000000..b4ddaa9 --- /dev/null +++ b/src/components/Sidebar/Sidebar.tsx @@ -0,0 +1,152 @@ +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>
+ );
+}
|