import { isEqual } from "lodash"; import { createContext, FC, useContext, useEffect, useState } from "react"; import { useSelector } from "react-redux"; import { PaneComponentEnum, PaneComponentInfo, PaneIdentifiers, SidebarAlignmentEnum, SidebarPlacementEnum, } from "~/subApps/sheet/components/Sidebar/types"; import { SIDEBAR_FIXED_POSITION_START_WIDTH } from "~/tools/js/CharacterSheet/config"; import { appEnvSelectors } from "~/tools/js/Shared/selectors"; export interface SidebarInfo { isVisible: boolean; setIsVisible: (isVisible: boolean) => void; isLocked: boolean; setIsLocked: (isLocked: boolean) => void; placement: SidebarPlacementEnum; setPlacement: (placement: SidebarPlacementEnum) => void; alignment: SidebarAlignmentEnum; setAlignment: (alignment: SidebarAlignmentEnum) => void; width: number; } export interface PaneInfo { activePane: PaneComponentInfo | null; showControls: boolean; isAtStart: boolean; isAtEnd: boolean; paneHistoryStart: ( componentType: PaneComponentEnum, componentIdentifiers?: PaneIdentifiers | null ) => void; paneHistoryPush: ( componentType: PaneComponentEnum, componentIdentifiers?: PaneIdentifiers | null ) => void; paneHistoryPrevious: () => void; paneHistoryNext: () => void; } export interface SidebarContextType { sidebar: SidebarInfo; pane: PaneInfo; } export const SidebarContext = createContext(null!); export const SidebarProvider: FC = ({ children }) => { const isMobile = useSelector(appEnvSelectors.getIsMobile); const sheetDimensions = useSelector(appEnvSelectors.getDimensions); const [isVisible, setIsVisible] = useState(false); const [isLocked, setIsLocked] = useState(false); const [placement, setPlacement] = useState(SidebarPlacementEnum.OVERLAY); const [alignment, setAlignment] = useState(SidebarAlignmentEnum.RIGHT); const [paneIdx, setPaneIdx] = useState(0); const [paneHistory, setPaneHistory] = useState>([]); const [activePane, setActivePane] = useState(null); const width: number = 340; //Create a new pane history const paneHistoryStart = ( type: PaneComponentEnum, identifiers: PaneIdentifiers | null = null ) => { setIsVisible(true); setPaneIdx(0); setPaneHistory([ { type, identifiers, }, ]); }; //Add a new pane to the history const paneHistoryPush = ( type: PaneComponentEnum, identifiers: PaneIdentifiers | null = null ) => { if (paneIdx === null) { paneHistoryStart(type, identifiers); } else { // get current active pane (last in history) const activeHistoryElement = { identifiers: activePane?.identifiers, type: activePane?.type, }; // create a new node for the history const newHistoryElement = { identifiers, type }; // compare the current active pane with the new one (last element in history with new one) if (!isEqual(activeHistoryElement, newHistoryElement)) { // Check if new history element is different than current - otherwise skip to avoid duplicates let newHistory = [ ...paneHistory.slice(0, paneIdx + 1), newHistoryElement, ]; setPaneHistory(newHistory); setPaneIdx(newHistory.length - 1); } } setIsVisible(true); }; //Go back to the previous pane in the history const paneHistoryPrevious = () => { setPaneIdx(Math.max(0, paneIdx - 1)); }; //Go to the next pane in the history const paneHistoryNext = () => { setPaneIdx(Math.min(paneHistory.length - 1, paneIdx + 1)); }; useEffect(() => { //If the window less than the fixed position start width (1600px), set the placement to overlay if ( isMobile || sheetDimensions.window.width < SIDEBAR_FIXED_POSITION_START_WIDTH ) { setPlacement(SidebarPlacementEnum.OVERLAY); } //set Right alignment if mobile setAlignment(isMobile ? SidebarAlignmentEnum.RIGHT : alignment); }, [isMobile, sheetDimensions]); useEffect(() => { const activePane = paneHistory[paneIdx]; setActivePane(activePane); }, [paneIdx, paneHistory]); return ( 1, isAtStart: paneIdx === 0, isAtEnd: paneIdx === paneHistory.length - 1, paneHistoryStart, paneHistoryPush, paneHistoryPrevious, paneHistoryNext, }, }} > {children} ); }; export const useSidebar = () => { return useContext(SidebarContext); };