parent
66126a4d15
commit
eb11ba7842
@ -0,0 +1,30 @@ |
||||
import { AppModal } from '../interface' |
||||
|
||||
export type ActionMap<M extends { [index: string]: any }> = { |
||||
[Key in keyof M]: M[Key] extends undefined |
||||
? { |
||||
type: Key; |
||||
} |
||||
: { |
||||
type: Key; |
||||
payload: M[Key]; |
||||
} |
||||
} |
||||
|
||||
export const enum actionTypes { |
||||
setModal = 'SET_MODAL', |
||||
setToast = 'SET_TOAST', |
||||
handleHideModal = 'HANDLE_HIDE_MODAL', |
||||
handleToaster = 'HANDLE_HIDE_TOAST' |
||||
} |
||||
|
||||
type ModalPayload = { |
||||
[actionTypes.setModal]: AppModal |
||||
[actionTypes.handleHideModal]: any |
||||
[actionTypes.setToast]: string |
||||
[actionTypes.handleToaster]: any |
||||
} |
||||
|
||||
export type ModalAction = ActionMap<ModalPayload>[keyof ActionMap< |
||||
ModalPayload |
||||
>] |
@ -0,0 +1,27 @@ |
||||
import React, { useEffect } from 'react' |
||||
import { useDialogDispatchers, useDialogs } from '../../context/provider' |
||||
import { Toaster } from '@remix-ui/toaster' |
||||
import ModalWrapper from './modal-wrapper' |
||||
import { ModalTypes } from '../../types' |
||||
|
||||
const AppDialogs = () => { |
||||
const { modal, toast, alert, handleHideModal, handleToaster } = useDialogDispatchers() |
||||
const { focusModal, focusToaster } = useDialogs() |
||||
|
||||
const ok = (returnedValue) => { |
||||
console.log('ok', returnedValue) |
||||
} |
||||
|
||||
useEffect(() => { |
||||
modal('test', 'Enter gist url', 'yes', ok, 'cancel', () => { }, ModalTypes.prompt, 'wss://remix.ethereum.org:8545') |
||||
toast('test toast') |
||||
// alert('fat error')
|
||||
}, []) |
||||
|
||||
return ( |
||||
<> |
||||
<ModalWrapper {...focusModal } handleHide={handleHideModal}></ModalWrapper> |
||||
<Toaster message={focusToaster} handleHide={handleToaster} /> |
||||
</>) |
||||
} |
||||
export default AppDialogs |
@ -1,6 +1,6 @@ |
||||
import React, { useContext, useEffect, useState } from 'react' |
||||
import { ModalDialog } from '@remix-ui/modal-dialog' |
||||
import AppContext from '../context/context' |
||||
import { AppContext } from '../../context/context' |
||||
const _paq = window._paq = window._paq || [] |
||||
|
||||
const MatomoDialog = (props) => { |
@ -0,0 +1,54 @@ |
||||
import React, { useEffect, useRef, useState } from 'react' |
||||
import { ModalDialog } from '@remix-ui/modal-dialog' |
||||
import { ModalDialogProps } from 'libs/remix-ui/modal-dialog/src/lib/types' |
||||
import { ModalTypes } from '../../types' |
||||
|
||||
interface ModalWrapperProps extends ModalDialogProps { |
||||
modalType?: ModalTypes |
||||
defaultValue?: string |
||||
} |
||||
|
||||
const ModalWrapper = (props: ModalWrapperProps) => { |
||||
const [state, setState] = useState<ModalDialogProps>() |
||||
const ref = useRef() |
||||
|
||||
const onFinishPrompt = async () => { |
||||
if (ref.current === undefined) { |
||||
props.okFn() |
||||
} else { |
||||
// @ts-ignore: Object is possibly 'null'.
|
||||
props.okFn(ref.current.value) |
||||
} |
||||
} |
||||
|
||||
const createModalMessage = () => { |
||||
return ( |
||||
<> |
||||
{props.message} |
||||
<input type={props.modalType === ModalTypes.password ? 'password' : 'text'} value={props.defaultValue} data-id="modalDialogCustomPromp" ref={ref} className="form-control" /></> |
||||
) |
||||
} |
||||
|
||||
useEffect(() => { |
||||
console.log(props) |
||||
if (props.modalType) { |
||||
switch (props.modalType) { |
||||
case ModalTypes.prompt: |
||||
case ModalTypes.password: |
||||
setState({ |
||||
...props, |
||||
okFn: onFinishPrompt, |
||||
message: createModalMessage() |
||||
}) |
||||
break |
||||
default: |
||||
setState({ ...props }) |
||||
break |
||||
} |
||||
} |
||||
}, [props]) |
||||
|
||||
return ( |
||||
<ModalDialog id='appDialog' {...state} handleHide={props.handleHide} />) |
||||
} |
||||
export default ModalWrapper |
@ -1,5 +1,15 @@ |
||||
import React from 'react' |
||||
import { ModalInitialState } from '../state/modals' |
||||
import { ModalTypes } from '../types' |
||||
|
||||
const AppContext = React.createContext(null) |
||||
export const AppContext = React.createContext(null) |
||||
|
||||
export default AppContext |
||||
export const dispatchModalContext = React.createContext({ |
||||
modal: (title: string, message: string | JSX.Element, okLabel: string, okFn: (value?:any) => void, cancelLabel?: string, cancelFn?: () => void, modalType?: ModalTypes, defaultValue?: string) => { }, |
||||
toast: (message: string) => {}, |
||||
alert: (title: string, message?: string | JSX.Element) => {}, |
||||
handleHideModal: () => { }, |
||||
handleToaster: () => {} |
||||
}) |
||||
|
||||
export const modalContext = React.createContext(ModalInitialState) |
||||
|
@ -0,0 +1,62 @@ |
||||
import React, { useReducer } from 'react' |
||||
import { actionTypes } from '../actions/modals' |
||||
import { modalReducer } from '../reducer/modals' |
||||
import { ModalInitialState } from '../state/modals' |
||||
import { ModalTypes } from '../types' |
||||
import { AppContext, dispatchModalContext, modalContext } from './context' |
||||
|
||||
export const ModalProvider = ({ children = [], reducer = modalReducer, initialState = ModalInitialState } = {}) => { |
||||
const [{ modals, toasters, focusModal, focusToaster }, dispatch] = useReducer(reducer, initialState) |
||||
|
||||
const modal = (title: string, message: string | JSX.Element, okLabel: string, okFn: (value?:any) => void, cancelLabel?: string, cancelFn?: () => void, modalType?: ModalTypes, defaultValue?: string) => { |
||||
dispatch({ |
||||
type: actionTypes.setModal, |
||||
payload: { title, message, okLabel, okFn, cancelLabel, cancelFn, modalType: modalType || ModalTypes.default, defaultValue: defaultValue } |
||||
}) |
||||
} |
||||
|
||||
const alert = (title: string, message?: string | JSX.Element) => { |
||||
modal(message ? title : 'Alert', message || title, 'OK', null, null, null) |
||||
} |
||||
|
||||
const handleHideModal = () => { |
||||
dispatch({ |
||||
type: actionTypes.handleHideModal, |
||||
payload: null |
||||
}) |
||||
} |
||||
|
||||
const toast = (message: string) => { |
||||
dispatch({ |
||||
type: actionTypes.setToast, |
||||
payload: message |
||||
}) |
||||
} |
||||
|
||||
const handleToaster = () => { |
||||
dispatch({ |
||||
type: actionTypes.handleToaster, |
||||
payload: null |
||||
}) |
||||
} |
||||
|
||||
return (<dispatchModalContext.Provider value={{ modal, toast, alert, handleHideModal, handleToaster }}> |
||||
<modalContext.Provider value={{ modals, toasters, focusModal, focusToaster }}> |
||||
{children} |
||||
</modalContext.Provider> |
||||
</dispatchModalContext.Provider>) |
||||
} |
||||
|
||||
export const AppProvider = ({ children = [], value = {} } = {}) => { |
||||
return <AppContext.Provider value={value}> |
||||
<ModalProvider>{children}</ModalProvider> |
||||
</AppContext.Provider> |
||||
} |
||||
|
||||
export const useDialogs = () => { |
||||
return React.useContext(modalContext) |
||||
} |
||||
|
||||
export const useDialogDispatchers = () => { |
||||
return React.useContext(dispatchModalContext) |
||||
} |
@ -0,0 +1,21 @@ |
||||
import { ModalTypes } from '../types' |
||||
|
||||
export interface AppModal { |
||||
hide?: boolean |
||||
title: string |
||||
// eslint-disable-next-line no-undef
|
||||
message: string | JSX.Element |
||||
okLabel: string |
||||
okFn: (value?:any) => void |
||||
cancelLabel: string |
||||
cancelFn: () => void, |
||||
modalType?: ModalTypes, |
||||
defaultValue?: string |
||||
} |
||||
|
||||
export interface ModalState { |
||||
modals: AppModal[], |
||||
toasters: string[], |
||||
focusModal: AppModal, |
||||
focusToaster: string |
||||
} |
@ -1,43 +1,48 @@ |
||||
import { actionTypes } from '../state/modals' |
||||
import { Modal } from '../types' |
||||
import { actionTypes, ModalAction } from '../actions/modals' |
||||
import { ModalInitialState } from '../state/modals' |
||||
import { AppModal, ModalState } from '../interface' |
||||
|
||||
interface Action { |
||||
type: string |
||||
payload: any |
||||
} |
||||
|
||||
export interface ModalState { |
||||
modals: Modal[], |
||||
toasters: string[], |
||||
focusModal: Modal, |
||||
focusToaster: string |
||||
} |
||||
export const modalReducer = (state: ModalState = ModalInitialState, action: ModalAction) => { |
||||
console.log(action) |
||||
switch (action.type) { |
||||
case actionTypes.setModal: { |
||||
console.log('set modal', action, Date.now()) |
||||
let modalList:AppModal[] = state.modals |
||||
modalList.push(action.payload) |
||||
if (state.modals.length === 1 && state.focusModal.hide === true) { // if it's the first one show it
|
||||
const focusModal: AppModal = { |
||||
hide: false, |
||||
title: modalList[0].title, |
||||
message: modalList[0].message, |
||||
okLabel: modalList[0].okLabel, |
||||
okFn: modalList[0].okFn, |
||||
cancelLabel: modalList[0].cancelLabel, |
||||
cancelFn: modalList[0].cancelFn, |
||||
modalType: modalList[0].modalType, |
||||
defaultValue: modalList[0].defaultValue |
||||
} |
||||
|
||||
const ModalInitialState: ModalState = { |
||||
modals: [], |
||||
toasters: [], |
||||
focusModal: { |
||||
hide: true, |
||||
title: '', |
||||
message: '', |
||||
okLabel: '', |
||||
okFn: () => {}, |
||||
cancelLabel: '', |
||||
cancelFn: () => {} |
||||
}, |
||||
focusToaster: '' |
||||
} |
||||
modalList = modalList.slice() |
||||
modalList.shift() |
||||
return { ...state, modals: modalList, focusModal: focusModal } |
||||
} |
||||
return { ...state, modals: modalList } |
||||
} |
||||
case actionTypes.handleHideModal: |
||||
console.log('handle hid', JSON.stringify(state.modals)) |
||||
state.focusModal = { ...state.focusModal, hide: true, message: null } |
||||
return { ...state } |
||||
|
||||
export const modalReducer = (state: ModalState = ModalInitialState, action: Action) => { |
||||
switch (action.type) { |
||||
case actionTypes.setModal: |
||||
state.modals.push(action.payload) |
||||
case actionTypes.setToast: |
||||
state.toasters.push(action.payload) |
||||
if (state.toasters.length > 0) { |
||||
const focus = state.toasters[0] |
||||
state.toasters.shift() |
||||
return { ...state, focusToaster: focus } |
||||
} |
||||
return { ...state } |
||||
} |
||||
return { |
||||
modals: state.modals, |
||||
toasters: state.toasters, |
||||
focusModal: state.focusModal, |
||||
focusToaster: state.focusToaster |
||||
|
||||
case actionTypes.handleToaster: |
||||
return { ...state, focusToaster: '' } |
||||
} |
||||
} |
||||
|
@ -1,48 +1,16 @@ |
||||
import { useReducer } from 'react' |
||||
import { modalReducer } from '../reducer/modals' |
||||
import { ModalState } from '../interface' |
||||
|
||||
export const actionTypes = { |
||||
setModal: 'SET_MODAL', |
||||
setToast: 'SET_TOAST' |
||||
} |
||||
|
||||
export const setModal = (title: string, message: string | JSX.Element, okLabel: string, okFn: () => void, cancelLabel?: string, cancelFn?: () => void) => { |
||||
return { |
||||
type: actionTypes.setModal, |
||||
payload: { message, title, okLabel, okFn, cancelLabel, cancelFn } |
||||
} |
||||
} |
||||
|
||||
export const setToast = (toasterMsg: string) => { |
||||
return { |
||||
type: actionTypes.setToast, |
||||
payload: toasterMsg |
||||
} |
||||
} |
||||
|
||||
function useModals ({ reducer = modalReducer } = {}) { |
||||
const [{ modals, toasters, focusModal, focusToaster }, dispatch] = useReducer(reducer, { |
||||
modals: [], |
||||
toasters: [], |
||||
focusModal: { |
||||
hide: true, |
||||
title: '', |
||||
message: '', |
||||
okLabel: '', |
||||
okFn: () => {}, |
||||
cancelLabel: '', |
||||
cancelFn: () => {} |
||||
}, |
||||
focusToaster: '' |
||||
}) |
||||
|
||||
const modal = (title: string, message: string | JSX.Element, okLabel: string, okFn: () => void, cancelLabel?: string, cancelFn?: () => void) => { |
||||
dispatch(setModal(title, message, okLabel, okFn, cancelLabel, cancelFn)) |
||||
} |
||||
|
||||
const toast = (toasterMsg: string) => { |
||||
dispatch(setToast(toasterMsg)) |
||||
} |
||||
|
||||
return { focusModal, modals, focusToaster, toasters, modal, toast } |
||||
export const ModalInitialState: ModalState = { |
||||
modals: [], |
||||
toasters: [], |
||||
focusModal: { |
||||
hide: true, |
||||
title: '', |
||||
message: '', |
||||
okLabel: '', |
||||
okFn: () => { }, |
||||
cancelLabel: '', |
||||
cancelFn: () => { } |
||||
}, |
||||
focusToaster: '' |
||||
} |
||||
|
@ -1,11 +1,7 @@ |
||||
|
||||
export interface Modal { |
||||
hide?: boolean |
||||
title: string |
||||
// eslint-disable-next-line no-undef
|
||||
message: string | JSX.Element |
||||
okLabel: string |
||||
okFn: () => void |
||||
cancelLabel: string |
||||
cancelFn: () => void |
||||
} |
||||
export const enum ModalTypes { |
||||
alert = 'alert', |
||||
confirm = 'confirm', |
||||
prompt = 'prompt', |
||||
password = 'password', |
||||
default = 'default', |
||||
} |
||||
|
Loading…
Reference in new issue