UNPKG

fbz

Version:

Fork of the OpenBazaar 2.0 browser-based client.

185 lines (164 loc) 5.77 kB
import uuidv4 from 'uuid/v4'; import { singletonModals } from 'reducers/modals'; export const MODAL_OPEN = 'OPEN_MODAL'; export const MODAL_CLOSE = 'CLOSE_MODAL'; export const MODAL_BRING_TO_TOP = 'MODAL_BRING_TO_TOP'; /* * Will open a modal with the given props. * * @param {object} props - The props the modal will be created with or set with if * it's an already open singleton modal. * @param {object} props.Component - The component you want to render inside the * modal. * @param {object} props.Component.modalProps - This should be implemented as a * static getter if its a class, otherwise a property directly on the function if * it's a functional component. * @param {string} props.Component.modalProps.path - The path to the Component * on disk which ModalRoot will dynamically import. Since you're passing in the * Component, it will already be in cache. * @param {string} props.Component.modalProps.modalProps - Default props that will * be applied to all instances of your Component. They will be merged with any * props passed into this action creator. * @param {string} props.Component.modalProps.rootClass - The class that will be * applied to the ModalRoot element. * @param {string} [props.Component.modalProps.closeable=true] - Determines whether * the modal is closeable via the user (close button and esc press). This setting * overrides the closeableViaCloseButton and closeableViaEsc props. * @param {string} [props.Component.modalProps.closeableViaCloseButton=true] - * Determines whether the modal is closeable via the close button. The close button * will not be rendered if this value is false. * @param {string} [props.Component.modalProps.closeableViaEsc=true] - Determines * whether the modal can be close via an Esc key press. * * @returns {string} - The ID of the opened modal. This will be necessary if you want to * close a non-singleton modal. */ export const open = (props = {}) => (dispatch, getState) => { if (typeof props.Component !== 'function') { throw new Error('Please provide a Component.'); } if ( typeof props.Component.modalProps !== 'object' || (typeof props.Component.modalProps.path !== 'string' || !props.Component.modalProps.path) ) { throw new Error( 'The component must implement a modalProps object as a static getter or ' + 'a function property. At a minimum it must contain a path property ' + 'as a string.' ); } // todo: a more robust path validator..? if (props.Component.modalProps.path.includes(' ')) { throw new Error('The path should not contain any spaces.'); } const modalProps = { rootClass: '', closeable: true, closeableViaCloseButton: true, closeableViaEsc: true, ...props.Component.modalProps, ...props }; delete modalProps.Component; const curModal = getState().modals.openModals.find( modal => singletonModals.includes(modalProps.path) && modal.path === modalProps.path ); const id = curModal ? curModal.id : uuidv4(); dispatch({ type: MODAL_OPEN, ...modalProps, id }); return id; }; /* * When you have a method that needs to target a modal, it will need either an * id (for non-singleton modals) or a path (for singleton modals). This function * will check that at least one is provided and in the correct format. * * @param {object} options - You must provide either the id or path. * @param {string} [options.id] - The id of the modal to close. This should be used * for non-singleton modals. * @param {string} [options.path] - The path of the modal to close. This should be * used for singleton modals. */ const checkOptsTargettingModal = (options = {}) => { if ( (!options.path && !options.id) || (typeof options.path !== 'string' && typeof options.id !== 'string') ) { throw new Error( 'One of options.id or options.path must be provided ' + 'and the variable must be of type string.' ); } }; /* * Will close a modal. * * @param {object} options - You must provide either the id or path. * @param {string} [options.id] - The id of the modal to close. This should be used * for non-singleton modals. * @param {string} [options.path] - The path of the modal to close. This should be * used for singleton modals. */ export const close = (options = {}) => (dispatch, getState) => { const action = { type: MODAL_CLOSE }; checkOptsTargettingModal(options); if (options.id) { dispatch({ ...action, id: options.id }); } else { if (!singletonModals.includes(options.path)) { throw new Error( 'Only singleton modals should be closed via the path. Use the id ' + 'instead.' ); } const modal = getState().modals.openModals.find( modal => modal.path === options.path ); if (modal) { dispatch({ ...action, id: modal.id }); } } }; /* * Will bring a modal to the top of the stack. * * @param {object} options - You must provide either the id or path. * @param {string} [options.id] - The id of the modal to close. This should be used * for non-singleton modals. * @param {string} [options.path] - The path of the modal to close. This should be * used for singleton modals. */ export const bringToTop = (options = {}) => (dispatch, getState) => { const action = { type: MODAL_BRING_TO_TOP }; checkOptsTargettingModal(options); if (options.id) { dispatch({ ...action, id: options.id }); } else { const id = getState().openModals.find(modal => (modal.path = options.path)); if (id) { dispatch({ ...action, id }); } } };