2025-05-28 15:36:51 -07:00

175 lines
4.9 KiB
TypeScript

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<SidebarContextType>(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<number>(0);
const [paneHistory, setPaneHistory] = useState<Array<PaneComponentInfo>>([]);
const [activePane, setActivePane] = useState<PaneComponentInfo | null>(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 (
<SidebarContext.Provider
value={{
sidebar: {
isVisible,
setIsVisible,
isLocked,
setIsLocked,
placement,
setPlacement,
alignment,
setAlignment,
width,
},
pane: {
activePane,
showControls: paneHistory.length > 1,
isAtStart: paneIdx === 0,
isAtEnd: paneIdx === paneHistory.length - 1,
paneHistoryStart,
paneHistoryPush,
paneHistoryPrevious,
paneHistoryNext,
},
}}
>
{children}
</SidebarContext.Provider>
);
};
export const useSidebar = () => {
return useContext(SidebarContext);
};