import React, { useState, useEffect } from 'react'; interface ModalState { active: boolean; setActive: (value: any) => any; forceAction: boolean; setForceAction: (value: any) => any; } export interface ModalContextProviderProps { /** * Dash-assigned callback that should be called whenever any of the * properties change */ setProps?: (value: any) => any; /** * The current or default state of the modal. If true, the "is-active" class is added to . * This value can be watched and changed from outside the component (e.g. via dash callback). */ active?: boolean; /** * Prevent modal from being closed without completion of a specific action. * If set, there must be a button within the modal that updates the "active" * state using the ModalContext or a dash callback on the active prop. */ forceAction?: boolean; } const ModalContext = React.createContext(undefined); /** * Wrap a `ModalTrigger` component and a `Modal` component inside a `ModalContextProvider` to render an element (trigger) that * will open up a modal. Apply props to the `ModalContextProvider`. */ export const ModalContextProvider: React.FC = (props) => { const [active, setActive] = useState(() => props.active || false); const [forceAction, setForceAction] = useState(() => props.forceAction || false); /** * Dynamically update the isActive prop so that the * active tab index can be accessed via dash callbacks. * * Prevent document scrolling when active */ useEffect(() => { if (active) { document.documentElement.classList.add('is-clipped'); } else { document.documentElement.classList.remove('is-clipped'); } props.setProps!({ active: active }); }, [active]); /** * Allow changes to active state from outside the component * (e.g. to change modal state via dash callback). */ useEffect(() => { setActive(props.active || false); }, [props.active]); return ( {props.children} ); }; ModalContextProvider.defaultProps = { setProps: () => null, active: false }; /** * Custom hook for consuming the ModalContext * Must only be used by child components of ModalContextProvider */ export const useModalContext = () => { const context = React.useContext(ModalContext); if (context === undefined) { throw new Error('useModalContext must be used within a ModalContextProvider'); } return context; };