Skip to content
This repository has been archived by the owner on Apr 25, 2023. It is now read-only.

Commit

Permalink
feat: add plugin api modal & popup (#328)
Browse files Browse the repository at this point in the history
* feat: plugin api modal

* refactor: replace state with ref

* feat: add plugin popup

* refactor: correct type

* refactor: define common props for plugin

* feat: support extension type block

* refactor: use rest spread opreator

* refactor: update plugin common props

* refactor: update type defination for styles

* fix: typo error

* refactor: simplify useContainer

* refactor: rename functionality

* refactor: remove unnessary function definition

* chore: use certain library version

* refactor: shorten as `modalVisible` `popupVisible`

* refactor: rename functionality
  • Loading branch information
airslice committed Oct 24, 2022
1 parent 1f5296d commit 27cd7a7
Show file tree
Hide file tree
Showing 25 changed files with 640 additions and 90 deletions.
4 changes: 3 additions & 1 deletion package.json
Expand Up @@ -92,10 +92,12 @@
"@auth0/auth0-react": "1.10.1",
"@emotion/react": "11.9.3",
"@emotion/styled": "11.9.3",
"@floating-ui/react-dom": "1.0.0",
"@monaco-editor/react": "4.4.5",
"@popperjs/core": "2.11.5",
"@rot1024/use-transition": "1.0.0",
"@sentry/browser": "6.19.7",
"@seznam/compose-react-refs": "1.0.6",
"@types/apollo-upload-client": "17.0.0",
"@types/gapi.auth2": "0.0.56",
"@types/gapi.client": "1.0.4",
Expand Down Expand Up @@ -155,4 +157,4 @@
"use-custom-compare": "1.2.0",
"use-file-input": "1.0.0"
}
}
}
13 changes: 8 additions & 5 deletions src/components/atoms/Plugin/IFrame/hooks.ts
Expand Up @@ -29,6 +29,7 @@ export default function useHook({
onLoad,
onMessage,
onClick,
onAutoResized,
}: {
width?: number | string;
height?: number | string;
Expand All @@ -41,6 +42,7 @@ export default function useHook({
onLoad?: () => void;
onMessage?: (message: any) => void;
onClick?: () => void;
onAutoResized?: () => void;
} = {}): {
ref: RefObject<HTMLIFrameElement>;
props: IframeHTMLAttributes<HTMLIFrameElement>;
Expand Down Expand Up @@ -77,6 +79,7 @@ export default function useHook({
const { width, height } = ev.data[autoResizeMessageKey];
if (typeof width !== "number" || typeof height !== "number") return;
setIFrameSize([width + "px", height + "px"]);
onAutoResized?.();
} else {
onMessage?.(ev.data);
}
Expand All @@ -85,7 +88,7 @@ export default function useHook({
return () => {
window.removeEventListener("message", cb);
};
}, [autoResize, autoResizeMessageKey, onMessage]);
}, [autoResize, autoResizeMessageKey, onMessage, onAutoResized]);

const onIframeLoad = useCallback(() => {
const win = iFrameRef.current?.contentWindow;
Expand All @@ -109,15 +112,15 @@ export default function useHook({
const width = ${
width
? typeof width === "number"
? width
: width + "px"
? `"${width}px"`
: `"${width}"`
: "html.offsetWidth + horizontalMargin"
};
const height = ${
height
? typeof height === "number"
? height + "px"
: height
? `"${height}px"`
: `"${height}"`
: "html.offsetHeight + verticalMargin"
};
parent.postMessage({
Expand Down
22 changes: 20 additions & 2 deletions src/components/atoms/Plugin/IFrame/index.tsx
@@ -1,4 +1,6 @@
import composeRefs from "@seznam/compose-react-refs";
import React, { IframeHTMLAttributes } from "react";
import type { RefObject } from "react";

import useHook, { RefType, AutoResize as AutoResizeType } from "./hooks";

Expand All @@ -14,13 +16,28 @@ export type Props = {
iFrameProps?: IframeHTMLAttributes<HTMLIFrameElement>;
width?: string | number;
height?: string | number;
externalRef?: RefObject<HTMLIFrameElement>;
onLoad?: () => void;
onMessage?: (message: any) => void;
onClick?: () => void;
onAutoResized?: () => void;
};

const IFrame: React.ForwardRefRenderFunction<Ref, Props> = (
{ autoResize, className, html, visible, iFrameProps, width, height, onLoad, onMessage, onClick },
{
autoResize,
className,
html,
visible,
iFrameProps,
width,
height,
externalRef,
onLoad,
onMessage,
onClick,
onAutoResized,
},
ref,
) => {
const {
Expand All @@ -38,6 +55,7 @@ const IFrame: React.ForwardRefRenderFunction<Ref, Props> = (
onLoad,
onMessage,
onClick,
onAutoResized,
});

return html ? (
Expand All @@ -47,7 +65,7 @@ const IFrame: React.ForwardRefRenderFunction<Ref, Props> = (
data-testid="iframe"
srcDoc=""
key={html}
ref={iFrameRef}
ref={composeRefs(iFrameRef, externalRef)}
className={className}
onLoad={onIFrameLoad}
{...props}
Expand Down
18 changes: 7 additions & 11 deletions src/components/atoms/Plugin/PluginIFrame/index.tsx
@@ -1,4 +1,5 @@
import { forwardRef, ForwardRefRenderFunction, IframeHTMLAttributes, ReactNode } from "react";
import { forwardRef, ForwardRefRenderFunction, IframeHTMLAttributes, ReactNode, memo } from "react";
import type { RefObject } from "react";
import { createPortal } from "react-dom";

import IFrame, { AutoResize } from "../IFrame";
Expand All @@ -17,8 +18,8 @@ export type Props = {
autoResize?: AutoResize;
iFrameProps?: IframeHTMLAttributes<HTMLIFrameElement>;
renderPlaceholder?: ReactNode;
useContainer?: boolean;
container?: HTMLElement | DocumentFragment;
externalRef?: RefObject<HTMLIFrameElement>;
onRender?: (type: string) => void;
onClick?: () => void;
onMessage?: (message: any) => void;
Expand All @@ -34,8 +35,8 @@ const PluginIFrame: ForwardRefRenderFunction<Ref, Props> = (
autoResize,
iFrameProps,
renderPlaceholder,
useContainer,
container,
externalRef,
onRender,
onClick,
onMessage,
Expand All @@ -58,6 +59,7 @@ const PluginIFrame: ForwardRefRenderFunction<Ref, Props> = (
iFrameProps={iFrameProps}
html={html}
autoResize={autoResize}
externalRef={externalRef}
onMessage={onMessage}
onClick={onClick}
onLoad={handleLoad}
Expand All @@ -69,13 +71,7 @@ const PluginIFrame: ForwardRefRenderFunction<Ref, Props> = (
</>
);

return enabled
? container
? useContainer
? createPortal(children, container)
: null
: children
: null;
return enabled ? (container ? createPortal(children, container) : children) : null;
};

export default forwardRef(PluginIFrame);
export default memo(forwardRef(PluginIFrame));
20 changes: 18 additions & 2 deletions src/components/atoms/Plugin/PluginIFrame/useIFrame.ts
Expand Up @@ -7,7 +7,12 @@ import { usePostMessage } from "./usePostMessage";
export type IFrameAPI = {
render: (
html: string,
options?: { visible?: boolean; width?: number | string; height?: number | string },
options?: {
visible?: boolean;
width?: number | string;
height?: number | string;
onAutoResized?: () => void;
},
) => void;
resize: (width: string | number | undefined, height: string | number | undefined) => void;
postMessage: (message: any) => void;
Expand All @@ -25,7 +30,18 @@ export default function useIFrame({
const ref = useRef<IFrameRef>(null);
const [iFrameLoaded, setIFrameLoaded] = useState(false);
const [[html, options], setIFrameState] = useState<
[string, { visible?: boolean; width?: number | string; height?: number | string } | undefined]
[
string,
(
| {
visible?: boolean;
width?: number | string;
height?: number | string;
onAutoResized?: () => void;
}
| undefined
),
]
>(["", undefined]);
const postMessage = usePostMessage(ref, !ready || !iFrameLoaded);
const handleLoad = useCallback(() => setIFrameLoaded(true), []);
Expand Down
3 changes: 3 additions & 0 deletions src/components/atoms/Plugin/hooks.ts
@@ -1,5 +1,6 @@
import { getQuickJS } from "quickjs-emscripten";
import { Arena } from "quickjs-emscripten-sync";
import type { RefObject } from "react";
import {
ForwardedRef,
useCallback,
Expand All @@ -18,6 +19,7 @@ export type Options = {
skip?: boolean;
isMarshalable?: boolean | "json" | ((obj: any) => boolean | "json");
ref?: ForwardedRef<Ref>;
mainIFrameRef?: RefObject<IFrameRef>;
exposed?: ((api: API) => { [key: string]: any }) | { [key: string]: any };
onError?: (err: any) => void;
onPreInit?: () => void;
Expand Down Expand Up @@ -220,6 +222,7 @@ export default function useHook({
onMessage,
offMessage,
onceMessage,
mainIFrameRef,
messageEvents,
messageOnceEvents,
]);
Expand Down
18 changes: 10 additions & 8 deletions src/components/atoms/Plugin/index.tsx
@@ -1,4 +1,5 @@
import { forwardRef, ForwardRefRenderFunction, IframeHTMLAttributes, ReactNode } from "react";
import type { RefObject } from "react";

import useHook, { defaultIsMarshalable, IFrameType, API, Ref } from "./hooks";
import PluginIFrame, { AutoResize } from "./PluginIFrame";
Expand All @@ -19,8 +20,9 @@ export type Props = {
iFrameProps?: IframeHTMLAttributes<HTMLIFrameElement>;
modalContainer?: HTMLElement | DocumentFragment;
popupContainer?: HTMLElement | DocumentFragment;
modalCanBeVisible?: boolean;
popupCanBeVisible?: boolean;
modalVisible?: boolean;
popupVisible?: boolean;
externalRef?: RefObject<HTMLIFrameElement>;
isMarshalable?: boolean | "json" | ((target: any) => boolean | "json");
exposed?: ((api: API) => { [key: string]: any }) | { [key: string]: any };
onMessage?: (message: any) => void;
Expand All @@ -35,8 +37,8 @@ const Plugin: ForwardRefRenderFunction<Ref, Props> = (
{
className,
canBeVisible,
modalCanBeVisible,
popupCanBeVisible,
modalVisible,
popupVisible,
skip,
src,
sourceCode,
Expand All @@ -46,6 +48,7 @@ const Plugin: ForwardRefRenderFunction<Ref, Props> = (
isMarshalable,
modalContainer,
popupContainer,
externalRef,
exposed,
onPreInit,
onError,
Expand Down Expand Up @@ -81,6 +84,7 @@ const Plugin: ForwardRefRenderFunction<Ref, Props> = (
iFrameProps={iFrameProps}
autoResize={autoResize}
renderPlaceholder={renderPlaceholder}
externalRef={externalRef}
onClick={onClick}
onRender={onRender as (type: string) => void}
onMessage={handleMessage}
Expand All @@ -89,9 +93,8 @@ const Plugin: ForwardRefRenderFunction<Ref, Props> = (
type="modal"
ref={modalIFrameRef}
container={modalContainer}
useContainer
visible
enabled={modalCanBeVisible}
enabled={modalVisible}
ready={loaded}
autoResize="both"
onRender={onRender as (type: string) => void}
Expand All @@ -101,9 +104,8 @@ const Plugin: ForwardRefRenderFunction<Ref, Props> = (
type="popup"
ref={popupIFrameRef}
container={popupContainer}
useContainer
visible
enabled={popupCanBeVisible}
enabled={popupVisible}
ready={loaded}
autoResize="both"
onRender={onRender as (type: string) => void}
Expand Down
20 changes: 12 additions & 8 deletions src/components/molecules/Visualizer/Block/index.tsx
Expand Up @@ -4,36 +4,34 @@ import { styled } from "@reearth/theme";
import { ValueType, ValueTypes } from "@reearth/util/value";

import Plugin from "../Plugin";
import type { Block, Layer, InfoboxProperty } from "../Plugin";
import type { Block, Layer, InfoboxProperty, CommonProps as PluginCommonProps } from "../Plugin";

import builtin from "./builtin";

export type { Block, Layer } from "../Plugin";

export type Props<BP = any, PP = any> = {
export type Props<BP = any> = {
isEditable?: boolean;
isBuilt?: boolean;
isSelected?: boolean;
layer?: Layer;
block?: Block<BP>;
infoboxProperty?: InfoboxProperty;
pluginProperty?: PP;
pluginBaseUrl?: string;
onClick?: () => void;
onChange?: <T extends ValueType>(
schemaItemId: string,
fieldId: string,
value: ValueTypes[T],
type: T,
) => void;
};
} & PluginCommonProps;

export type Component<BP = any, PP = any> = ComponentType<Props<BP, PP>>;
export type Component<BP = any> = ComponentType<Props<BP>>;

export default function BlockComponent<P = any, PP = any>({
export default function BlockComponent<P = any>({
pluginBaseUrl,
...props
}: Props<P, PP>): JSX.Element | null {
}: Props<P>): JSX.Element | null {
const Builtin =
props.block?.pluginId && props.block.extensionId
? builtin[`${props.block.pluginId}/${props.block.extensionId}`]
Expand All @@ -56,6 +54,12 @@ export default function BlockComponent<P = any, PP = any>({
layer={props.layer}
block={props.block}
onClick={props.onClick}
pluginModalContainer={props.pluginModalContainer}
shownPluginModalInfo={props.shownPluginModalInfo}
onPluginModalShow={props.onPluginModalShow}
pluginPopupContainer={props.pluginPopupContainer}
shownPluginPopupInfo={props.shownPluginPopupInfo}
onPluginPopupShow={props.onPluginPopupShow}
/>
</Wrapper>
);
Expand Down
7 changes: 4 additions & 3 deletions src/components/molecules/Visualizer/Infobox/index.tsx
Expand Up @@ -9,6 +9,7 @@ import { ValueTypes, ValueType } from "@reearth/util/value";

import PluginBlock, { Layer, Block } from "../Block";
import type { SceneProperty } from "../Engine";
import type { CommonProps as PluginCommonProps } from "../Plugin";

import Field from "./Field";
import Frame from "./Frame";
Expand All @@ -29,8 +30,6 @@ export type Props = {
isBuilt?: boolean;
selectedBlockId?: string;
visible?: boolean;
pluginBaseUrl?: string;
pluginProperty?: { [key: string]: any };
onMaskClick?: () => void;
onBlockSelect?: (id?: string) => void;
onBlockChange?: <T extends ValueType>(
Expand All @@ -44,7 +43,7 @@ export type Props = {
onBlockDelete?: (id: string) => void;
onBlockInsert?: (bi: number, i: number, pos?: "top" | "bottom") => void;
renderInsertionPopUp?: (onSelect: (bi: number) => void, onClose: () => void) => React.ReactNode;
};
} & PluginCommonProps;

const Infobox: React.FC<Props> = ({
className,
Expand All @@ -65,6 +64,7 @@ const Infobox: React.FC<Props> = ({
onBlockMove,
renderInsertionPopUp,
onBlockInsert,
...props
}) => {
const {
insertionPopUpPosition,
Expand Down Expand Up @@ -136,6 +136,7 @@ const Infobox: React.FC<Props> = ({
}}
layer={layer}
pluginBaseUrl={pluginBaseUrl}
{...props}
/>
</Field>
))}
Expand Down

0 comments on commit 27cd7a7

Please sign in to comment.