diff --git a/web/src/beta/features/Editor/Visualizer/convert-infobox.ts b/web/src/beta/features/Editor/Visualizer/convert-infobox.ts index 6dd1c759c2..da9301886c 100644 --- a/web/src/beta/features/Editor/Visualizer/convert-infobox.ts +++ b/web/src/beta/features/Editor/Visualizer/convert-infobox.ts @@ -1,6 +1,7 @@ import { InfoboxBlock } from "@reearth/beta/features/Visualizer/Crust/Infobox/types"; import type { Layer } from "@reearth/core"; import { NLSInfobox } from "@reearth/services/api/layersApi/utils"; +import { convert } from "@reearth/services/api/propertyApi/utils"; import { processProperty as processNewProperty } from "./processNewProperty"; @@ -22,6 +23,7 @@ export default ( extensionId: b.extensionId, property: processNewProperty(undefined, b.property), propertyId: b.propertyId, // required by onBlockChange + pluginBlockPropertyItems: convert(b.property), })), }; }; diff --git a/web/src/beta/features/Editor/Visualizer/convert-story.ts b/web/src/beta/features/Editor/Visualizer/convert-story.ts index add1464d1b..9dd29e6534 100644 --- a/web/src/beta/features/Editor/Visualizer/convert-story.ts +++ b/web/src/beta/features/Editor/Visualizer/convert-story.ts @@ -3,6 +3,7 @@ import { StoryBlock, StoryPage, } from "@reearth/beta/features/Visualizer/Crust/StoryPanel/types"; +import { convert } from "@reearth/services/api/propertyApi/utils"; import { Scene } from "@reearth/services/api/sceneApi"; import { StoryPage as GqlStoryPage, StoryBlock as GqlStoryBlock } from "@reearth/services/gql"; @@ -38,6 +39,7 @@ export const convertStory = (scene?: Scene, storyId?: string): Story | undefined name: installedBlockNames?.[b.extensionId] ?? "Story Block", propertyId: b.property?.id, property: processProperty(undefined, b.property), + pluginBlockPropertyItems: convert(b.property), })); return { diff --git a/web/src/beta/features/Visualizer/Crust/Infobox/Block/index.tsx b/web/src/beta/features/Visualizer/Crust/Infobox/Block/index.tsx index a56c6aabb1..233edc48e0 100644 --- a/web/src/beta/features/Visualizer/Crust/Infobox/Block/index.tsx +++ b/web/src/beta/features/Visualizer/Crust/Infobox/Block/index.tsx @@ -28,7 +28,17 @@ const InfoboxBlockComponent = ({ renderBlock, onRemove, ...props }: Props): JSX. return Builtin ? ( ) : props.block ? ( - + {renderBlock?.({ block: props.block, layer: props.layer, onClick: props.onClick })} ) : null; diff --git a/web/src/beta/features/Visualizer/Crust/Infobox/types.ts b/web/src/beta/features/Visualizer/Crust/Infobox/types.ts index 2d69230518..8d8d914437 100644 --- a/web/src/beta/features/Visualizer/Crust/Infobox/types.ts +++ b/web/src/beta/features/Visualizer/Crust/Infobox/types.ts @@ -1,4 +1,5 @@ import type { Layer, Spacing } from "@reearth/core"; +import { Item } from "@reearth/services/api/propertyApi/utils"; export type Infobox = { featureId?: string; @@ -24,6 +25,7 @@ export type InfoboxBlock

= { extensionId?: string; property?: P; propertyId?: string; + pluginBlockPropertyItems?: Item[]; }; export type InfoboxBlockProps

= { diff --git a/web/src/beta/features/Visualizer/Crust/Plugins/plugin_types.ts b/web/src/beta/features/Visualizer/Crust/Plugins/plugin_types.ts index f92bc7667a..cd1656c073 100644 --- a/web/src/beta/features/Visualizer/Crust/Plugins/plugin_types.ts +++ b/web/src/beta/features/Visualizer/Crust/Plugins/plugin_types.ts @@ -33,7 +33,6 @@ import { ScreenSpaceCameraControllerOptions, } from "@reearth/core"; -import { InfoboxBlock as Block } from "../Infobox/types"; import { InteractionModeType } from "../types"; import { Widget } from "../Widgets"; @@ -112,7 +111,7 @@ export type Reearth = { >; readonly layer?: LazyLayer; readonly widget?: Widget; - readonly block?: Block; + readonly block?: CommonBlock; readonly scene: Undefinable; readonly viewport?: Viewport; readonly clientStorage: ClientStorage; @@ -131,6 +130,15 @@ export type Reearth = { ) => void; }; +export type CommonBlock = { + id: string; + name?: string | null; + pluginId: string; + extensionId: string; + propertyId?: string; + property?: any; +}; + export type Scene = { readonly inEditor: boolean; readonly built: boolean; diff --git a/web/src/beta/features/Visualizer/Crust/StoryPanel/Block/index.tsx b/web/src/beta/features/Visualizer/Crust/StoryPanel/Block/index.tsx index c8a5cd9858..052a74bc70 100644 --- a/web/src/beta/features/Visualizer/Crust/StoryPanel/Block/index.tsx +++ b/web/src/beta/features/Visualizer/Crust/StoryPanel/Block/index.tsx @@ -2,8 +2,8 @@ import { useCallback, type ComponentType, type ReactNode } from "react"; import type { CommonBlockProps, BlockProps } from "@reearth/beta/features/Visualizer/shared/types"; import type { Layer } from "@reearth/core"; -import { styled } from "@reearth/services/theme"; +import BlockWrapper from "../../../shared/components/BlockWrapper"; import { StoryBlock } from "../types"; import builtin, { isBuiltinStoryBlock } from "./builtin"; @@ -31,17 +31,17 @@ export default function StoryBlockComponent({ return Builtin ? ( ) : props.block ? ( - + {renderBlock?.({ block: props.block, layer: props.layer, onClick: props.onClick })} - + ) : null; } - -const Wrapper = styled.div<{ editable?: boolean; selected?: boolean }>` - border: 1px solid - ${({ selected, editable, theme }) => (editable && selected ? theme.select.main : "transparent")}; - - :hover { - ${({ editable, theme }) => editable && `border-color: ${theme.outline.main}`}; - } -`; diff --git a/web/src/beta/features/Visualizer/Crust/StoryPanel/types.ts b/web/src/beta/features/Visualizer/Crust/StoryPanel/types.ts index a8ad315f36..19b0d3cd66 100644 --- a/web/src/beta/features/Visualizer/Crust/StoryPanel/types.ts +++ b/web/src/beta/features/Visualizer/Crust/StoryPanel/types.ts @@ -1,3 +1,5 @@ +import { Item } from "@reearth/services/api/propertyApi/utils"; + export type Position = "left" | "right"; export type Story = { @@ -25,6 +27,7 @@ export type StoryBlock = { extensionId: string; propertyId?: string; property?: any; + pluginBlockPropertyItems?: Item[]; }; export type Field = { diff --git a/web/src/beta/features/Visualizer/shared/components/ActionPanel/index.tsx b/web/src/beta/features/Visualizer/shared/components/ActionPanel/index.tsx index 1cd46899a7..5c4fa7f845 100644 --- a/web/src/beta/features/Visualizer/shared/components/ActionPanel/index.tsx +++ b/web/src/beta/features/Visualizer/shared/components/ActionPanel/index.tsx @@ -168,6 +168,7 @@ const Wrapper = styled.div<{ isSelected?: boolean; position?: ActionPosition }>` gap: 4px; height: 24px; position: absolute; + max-width: 100%; ${({ position }) => position === "left-top" ? ` @@ -210,6 +211,10 @@ const OptionWrapper = styled.div<{ showPointer?: boolean }>` const OptionText = styled(Text)` padding-right: 4px; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + max-width: 150px; `; const OptionIcon = styled(Icon)<{ border?: boolean }>` diff --git a/web/src/beta/features/Visualizer/shared/components/BlockWrapper/hooks.ts b/web/src/beta/features/Visualizer/shared/components/BlockWrapper/hooks.ts index 811a82d12f..91495280f1 100644 --- a/web/src/beta/features/Visualizer/shared/components/BlockWrapper/hooks.ts +++ b/web/src/beta/features/Visualizer/shared/components/BlockWrapper/hooks.ts @@ -104,6 +104,10 @@ export default ({ }; }, [property?.panel, isEditable]); + const pluginBlockSettings = useMemo(() => { + return property; + }, [property]); + return { title, groupId, @@ -111,6 +115,7 @@ export default ({ showSettings, defaultSettings, generalBlockSettings, + pluginBlockSettings, disableSelection, handleEditModeToggle, handleSettingsToggle, diff --git a/web/src/beta/features/Visualizer/shared/components/BlockWrapper/index.tsx b/web/src/beta/features/Visualizer/shared/components/BlockWrapper/index.tsx index d25e0b706b..eac918cdb2 100644 --- a/web/src/beta/features/Visualizer/shared/components/BlockWrapper/index.tsx +++ b/web/src/beta/features/Visualizer/shared/components/BlockWrapper/index.tsx @@ -1,6 +1,10 @@ -import { ReactNode, createContext, memo } from "react"; +import { ReactNode, createContext, memo, useCallback } from "react"; +import PropertyItem from "@reearth/beta/components/fields/Property/PropertyItem"; +import SidePanelSectionField from "@reearth/beta/components/SidePanelSectionField"; import { stopClickPropagation } from "@reearth/beta/utils/events"; +import { FlyTo, useVisualizer } from "@reearth/core"; +import { Item } from "@reearth/services/api/propertyApi/utils"; import { styled } from "@reearth/services/theme"; import Template from "../../../Crust/StoryPanel/Block/Template"; @@ -26,9 +30,11 @@ type Props = { children?: ReactNode; propertyId?: string; property?: any; + pluginBlockPropertyItems?: Item[]; dndEnabled?: boolean; settingsEnabled?: boolean; minHeight?: number; + isPluginBlock?: boolean; onClick?: () => void; onClickAway?: () => void; onRemove?: () => void; @@ -63,9 +69,11 @@ const BlockWrapper: React.FC = ({ children, propertyId, property, + pluginBlockPropertyItems, dndEnabled = true, settingsEnabled = true, minHeight, + isPluginBlock, onClick, onBlockDoubleClick, onClickAway, @@ -82,6 +90,7 @@ const BlockWrapper: React.FC = ({ showSettings, defaultSettings, generalBlockSettings, + pluginBlockSettings, disableSelection, handleEditModeToggle, handleSettingsToggle, @@ -96,6 +105,14 @@ const BlockWrapper: React.FC = ({ onBlockDoubleClick, }); + const visualizerRef = useVisualizer(); + const handleFlyTo: FlyTo = useCallback( + (target, options) => { + visualizerRef.current?.engine.flyTo(target, options); + }, + [visualizerRef], + ); + return ( = ({ propertyId={propertyId} dndEnabled={dndEnabled} showSettings={showSettings} - contentSettings={generalBlockSettings} + contentSettings={isPluginBlock ? pluginBlockSettings : generalBlockSettings} + isPluginBlock={isPluginBlock} editMode={editMode} isEditable={isEditable} hideHoverUI={disableSelection} @@ -130,7 +148,7 @@ const BlockWrapper: React.FC = ({ {children ?? (isEditable &&