diff options
Diffstat (limited to 'packages/taler-wallet-webextension/src/mui/Portal.tsx')
-rw-r--r-- | packages/taler-wallet-webextension/src/mui/Portal.tsx | 128 |
1 files changed, 128 insertions, 0 deletions
diff --git a/packages/taler-wallet-webextension/src/mui/Portal.tsx b/packages/taler-wallet-webextension/src/mui/Portal.tsx new file mode 100644 index 000000000..1d835abac --- /dev/null +++ b/packages/taler-wallet-webextension/src/mui/Portal.tsx @@ -0,0 +1,128 @@ +/* + This file is part of GNU Taler + (C) 2022 Taler Systems S.A. + + GNU Taler is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + */ + +import { css } from "@linaria/core"; +import { createPortal, forwardRef } from "preact/compat"; +import { + h, + JSX, + VNode, + ComponentChildren, + RefObject, + isValidElement, + cloneElement, + Fragment, +} from "preact"; +import { Ref, useEffect, useState } from "preact/hooks"; + +import { theme } from "./style.js"; + +const baseStyle = css` + position: fixed; + z-index: ${theme.zIndex.modal}; + right: 0px; + bottom: 0px; + top: 0px; + left: 0px; +`; + +interface Props { + // class: string; + children: ComponentChildren; + disablePortal?: boolean; + container?: VNode; +} + +export const Portal = forwardRef(function Portal( + { container, disablePortal, children }: Props, + ref: Ref<any>, +): VNode { + const [mountNode, setMountNode] = useState<HTMLElement | undefined>( + undefined, + ); + const handleRef = null; + // useForkRef( + // isValidElement(children) ? children.ref : null, + // ref, + // ); + + useEffect(() => { + if (!disablePortal) { + setMountNode(getContainer(container) || document.body); + } + }, [container, disablePortal]); + + useEffect(() => { + if (mountNode && !disablePortal) { + setRef(ref, mountNode); + return () => { + setRef(ref, null); + }; + } + + return undefined; + }, [ref, mountNode, disablePortal]); + + if (disablePortal) { + if (isValidElement(children)) { + return cloneElement(children, { + ref: handleRef, + }); + } + return <Fragment>{children}</Fragment>; + } + + return mountNode ? ( + createPortal(<Fragment>{children}</Fragment>, mountNode) + ) : ( + <Fragment /> + ); +} as any); + +function getContainer(container: any): any { + return typeof container === "function" ? container() : container; +} + +// function useForkRef<Instance>( +// refA: React.Ref<Instance> | null | undefined, +// refB: React.Ref<Instance> | null | undefined, +// ): React.Ref<Instance> | null { +// /** +// * This will create a new function if the ref props change and are defined. +// * This means react will call the old forkRef with `null` and the new forkRef +// * with the ref. Cleanup naturally emerges from this behavior. +// */ +// return useMemo(() => { +// if (refA == null && refB == null) { +// return null; +// } +// return (refValue) => { +// setRef(refA, refValue); +// setRef(refB, refValue); +// }; +// }, [refA, refB]); +// } + +function setRef<T>( + ref: RefObject<T | null> | ((instance: T | null) => void) | null | undefined, + value: T | null, +): void { + if (typeof ref === "function") { + ref(value); + } else if (ref) { + ref.current = value; + } +} |