diff --git a/components/Timeline/TimelineOverview.tsx b/components/Timeline/TimelineOverview.tsx index 6bad52e..4974754 100644 --- a/components/Timeline/TimelineOverview.tsx +++ b/components/Timeline/TimelineOverview.tsx @@ -18,6 +18,8 @@ type TimelineProps = { const TimelineOverview = ({ timelineData }: TimelineProps) => { const [isMounted, setIsMounted] = useState(false); const [expandedUuid, setExpandedUuid] = useState(null); + const [expandedRowIndex, setExpandedRowIndex] = useState(null); + const [focusedTabIndex, setFocusedTabIndex] = useState(0); // Ensure the component is mounted before running media queries useEffect(() => { @@ -39,8 +41,55 @@ const TimelineOverview = ({ timelineData }: TimelineProps) => { return acc; }, []); - const handleToggle = (uuid: string) => { - setExpandedUuid((currentUuid) => (currentUuid === uuid ? null : uuid)); + const handleToggle = (uuid: string, rowIndex: number) => { + setExpandedUuid((currentUuid) => { + const isSameRow = rowIndex === expandedRowIndex; + const isSameItem = uuid === currentUuid; + + if (isSameRow) return isSameItem ? null : uuid; + + setExpandedRowIndex(rowIndex); + return uuid; + }); + }; + + const handleKeyDown = ( + event: React.KeyboardEvent, + itemUuid: string, + rowIndex: number, + ) => { + switch (event.key) { + case "Enter": + case " ": + event.preventDefault(); + handleToggle(itemUuid, rowIndex); + break; + case "ArrowRight": + case "ArrowDown": + event.preventDefault(); + setFocusedTabIndex( + (prevIndex) => (prevIndex + 1) % timelineData.length, + ); + break; + case "ArrowLeft": + case "ArrowUp": + event.preventDefault(); + setFocusedTabIndex( + (prevIndex) => + (prevIndex - 1 + timelineData.length) % timelineData.length, + ); + break; + case "Home": + event.preventDefault(); + setFocusedTabIndex(0); + break; + case "End": + event.preventDefault(); + setFocusedTabIndex(timelineData.length - 1); + break; + default: + break; + } }; if (!isMounted) { @@ -48,7 +97,13 @@ const TimelineOverview = ({ timelineData }: TimelineProps) => { } return ( - +
{rows.map((row, rowIndex) => (
{ >
{row.map((item, itemIndex) => { const sizePattern: SizeType[] = ["large", "medium", "small"]; const size = sizePattern[itemIndex % sizePattern.length]; const trapezoid = itemIndex % 2 === 0 ? "left" : "right"; + const globalIndex = rowIndex * itemsPerRow + itemIndex; return ( { isExpanded={expandedUuid === item.uuid} size={size} trapezoid={trapezoid} - onClick={() => handleToggle(item.uuid)} + onClick={() => handleToggle(item.uuid, rowIndex)} + tabIndex={focusedTabIndex === globalIndex ? 0 : -1} + onKeyDown={(e) => handleKeyDown(e, item.uuid, rowIndex)} + ref={(el) => { + if (focusedTabIndex === globalIndex) { + el?.focus(); + } + }} /> ); })} @@ -83,6 +147,7 @@ const TimelineOverview = ({ timelineData }: TimelineProps) => { {expandedUuid && + expandedRowIndex === rowIndex && row.some((item) => item.uuid === expandedUuid) && ( { {...timelineData.find( (item) => item.uuid === expandedUuid, )!} - onClose={() => setExpandedUuid(null)} + onClose={() => { + setExpandedUuid(null); + setExpandedRowIndex(null); + }} /> )}