import {createContext, Dispatch, ReactNode, SetStateAction, useCallback, useContext, useState} from 'react' import {Button, Dialog, DialogBody, DialogFooter, FormGroup, InputGroup} from '@blueprintjs/core' import {useLoadingState} from './loadingState' import {Intent} from '@blueprintjs/core/src/common/intent' export type DialogStateType = { isOpen: boolean, title: string, content: ReactNode, actions: ReactNode, state: any, canClose: boolean // if true, dialog can be closed by clicking outside of it } export const defaultDialogContext = { isOpen: false, title: '', content: null as ReactNode, actions: null as ReactNode, state: {} as any, canClose: true, } export function setupDialog(){ const [dialog, setDialog] = useState(defaultDialogContext) const open = useCallback((d: Partial)=>{ setDialog((d1)=>({...d1, ...d, isOpen: true})) }, []) const close = useCallback(()=>setDialog((d1)=>({...d1, isOpen: false})), []) const setDialogState = useCallback((state: any)=>{ setDialog((d1)=>({...d1, state})) }, []) return {dialog, open, close, setDialog, state: dialog.state, setState: setDialogState} } export const DialogContext = createContext<{ dialog: DialogStateType, open: (d: Partial)=>void, close: ()=>void, setDialog: Dispatch>, setState: Dispatch>, state: any }>( {dialog: defaultDialogContext, open: ()=>{}, close: ()=>{}, setDialog: ()=>{}, setState: ()=>{}, state: {}} ) export const useDialog = () => useContext(DialogContext) export function DialogProvider({children}: {children: ReactNode}){ const dialog = setupDialog() return {children} } export function DialogComponent(){ const {dialog, close} = useDialog() return ( {dialog.canClose && close()}}> {dialog.content} ) } interface DialogPromptState { value: string, intent: Intent, helperText: string, key: string, } interface DialogPromptProps extends Partial, Partial{ closeButtonText?: string, submitButtonText?: string, message?: string, placeholder?: string, showInput?: boolean, onClose?: (value: string)=>boolean|undefined|Promise, // does not close if false onSubmit?: (value: string)=>boolean|undefined|any|Promise, // does not close if false } /** * Usage: * ```tsx * const {prompt, close} = useDialogPrompt() * const text = await prompt({ * title: 'Enter some text', * message: 'Enter some text: ', * placeholder: 'Enter some text', * value: 'Default Value', * onClose: ()=>{console.log('close'); return true}, * onSubmit: ()=>{console.log('submit'); return true}, * closeButtonText: 'Close', * submitButtonText: 'Okay', * }) * ``` */ export function useDialogPrompt(){ const {open, close} = useDialog() const prompt = useCallback(({ closeButtonText = 'Close', submitButtonText = 'Okay', message = 'Enter some text: ', placeholder = '', value = '', showInput = true, onClose, onSubmit, helperText = '', intent = Intent.NONE, ...props}: DialogPromptProps) => { return new Promise((resolve)=>{ open({ canClose: false, state: { value, intent, helperText, key: Math.random().toString(36).slice(2, 10), } as DialogPromptState, content: ( ), actions: ( ), ...props, }) }) }, [open, close]) return {prompt, close, open} } function DialogPromptButtons({ closeButtonText = 'Close', submitButtonText = 'Okay', onClose, onSubmit, resolve }:{ closeButtonText?: string, submitButtonText?: string, onClose?: (value: string)=>boolean|undefined|Promise, // does not close if false onSubmit?: (value: string)=>boolean|undefined|{error: string}|Promise, // does not close if false resolve: (value: string|null)=>void, }){ const {loadingState, updateLoading} = useLoadingState() const {state, setState, close} = useDialog() as {state: DialogPromptState, setState: Dispatch>, close: ()=>void} const doClose = async()=> { if (onClose && (await onClose(state.value)) !== true) return close() resolve(null) } const doSubmit = async()=> { if (onSubmit) { const res = await onSubmit(state.value) if(res && (res as any).error){ setState({...state, intent: Intent.DANGER, helperText: (res as any).error}) } if(res !== true) return } close() resolve(state.value) } return <>