Files
kan/apps/web/src/providers/modal.tsx

144 lines
3.5 KiB
TypeScript

import { createContext, useCallback, useContext, useState } from "react";
interface ModalState {
contentType: string;
entityId?: string;
entityLabel?: string;
closeOnClickOutside?: boolean;
}
interface Props {
children: React.ReactNode;
}
interface ModalContextType {
isOpen: boolean;
openModal: (
contentType: string,
entityId?: string,
entityLabel?: string,
closeOnClickOutside?: boolean,
) => void;
closeModal: () => void;
closeModals: (count: number) => void;
clearAllModals: () => void;
modalContentType: string;
entityId: string;
entityLabel: string;
closeOnClickOutside: boolean;
modalStates: Record<string, any>;
setModalState: (modalType: string, state: any) => void;
getModalState: (modalType: string) => any;
clearModalState: (modalType: string) => void;
clearAllModalStates: () => void;
}
const ModalContext = createContext<ModalContextType | undefined>(undefined);
export const ModalProvider: React.FC<Props> = ({ children }) => {
const [modalStack, setModalStack] = useState<ModalState[]>([]);
const [modalStates, setModalStates] = useState<Record<string, any>>({});
const isOpen = modalStack.length > 0;
const currentModal = modalStack[modalStack.length - 1];
const modalContentType = currentModal?.contentType || "";
const entityId = currentModal?.entityId || "";
const entityLabel = currentModal?.entityLabel || "";
const closeOnClickOutside = currentModal?.closeOnClickOutside ?? true;
const openModal = useCallback(
(
contentType: string,
entityId?: string,
entityLabel?: string,
closeOnClickOutside?: boolean,
) => {
const newModal: ModalState = {
contentType,
entityId,
entityLabel,
closeOnClickOutside,
};
setModalStack((prev) => [...prev, newModal]);
},
[],
);
const closeModal = useCallback(() => {
setModalStack((prev) => {
if (prev.length <= 1) {
return [];
}
return prev.slice(0, -1);
});
}, []);
const closeModals = useCallback((count: number) => {
setModalStack((prev) => {
const newLength = Math.max(0, prev.length - count);
return prev.slice(0, newLength);
});
}, []);
const clearAllModals = useCallback(() => {
setModalStack([]);
}, []);
const setModalState = useCallback((modalType: string, state: any) => {
setModalStates((prev) => ({
...prev,
[modalType]: state,
}));
}, []);
const getModalState = useCallback(
(modalType: string) => {
return modalStates[modalType];
},
[modalStates],
);
const clearModalState = useCallback((modalType: string) => {
setModalStates((prev) => {
const newStates = { ...prev };
delete newStates[modalType];
return newStates;
});
}, []);
const clearAllModalStates = useCallback(() => {
setModalStates({});
}, []);
return (
<ModalContext.Provider
value={{
isOpen,
openModal,
closeModal,
closeModals,
clearAllModals,
modalContentType,
entityId,
entityLabel,
closeOnClickOutside,
modalStates,
setModalState,
getModalState,
clearModalState,
clearAllModalStates,
}}
>
{children}
</ModalContext.Provider>
);
};
export const useModal = () => {
const context = useContext(ModalContext);
if (context === undefined) {
throw new Error("useModal must be used within a ModalProvider");
}
return context;
};