diff --git a/web/src/beta/features/Editor/tabs/map/LeftPanel/GroupField/index.tsx b/web/src/beta/features/Editor/tabs/map/LeftPanel/GroupField/index.tsx index 44eda968df..46c7efe727 100644 --- a/web/src/beta/features/Editor/tabs/map/LeftPanel/GroupField/index.tsx +++ b/web/src/beta/features/Editor/tabs/map/LeftPanel/GroupField/index.tsx @@ -1,4 +1,4 @@ -import ListItem from "@reearth/beta/components/ListItem"; +// import ListItem from "@reearth/beta/components/ListItem"; import SidePanelSectionField from "@reearth/beta/components/SidePanelSectionField"; import type { LayerNameUpdateProps } from "@reearth/beta/features/Editor/useLayers"; import type { NLSLayer } from "@reearth/services/api/layersApi/utils"; @@ -21,27 +21,27 @@ type GroupSectionFieldProps = { const GroupSectionField: React.FC = ({ layers, selectedLayerId, - selectedSceneSetting, + // selectedSceneSetting, onLayerDelete, onLayerNameUpdate, onLayerSelect, - onSceneSettingSelect, + // onSceneSettingSelect, onDataSourceManagerOpen, }) => { const t = useT(); return ( <> - - {/* {groups.map(({ schemaGroupId, title }) => ( + {/* */} + {/* {groups.map(({ schemaGroupId, title }) => ( {title} ))} */} - + {/* {t("Main")} - + */} = ({ onLayerSelect, onDataSourceManagerOpen, }) => { + const t = useT(); const [isAddMenuOpen, setAddMenuOpen] = useState(false); const toggleAddMenu = useCallback(() => { @@ -46,18 +48,18 @@ const Layers: React.FC = ({ size="md" items={[ { - name: "Add Layer from Resource", + name: t("Add Layer from Resource"), icon: "file", onClick: () => { onDataSourceManagerOpen(); toggleAddMenu(); }, }, - { - name: "Add Sketch Layer", - icon: "pencilSimple", - onClick: () => {}, - }, + // { + // name: t("Add Sketch Layer"), + // icon: "pencilSimple", + // onClick: () => {}, + // }, ]} /> diff --git a/web/src/beta/features/Editor/tabs/map/LeftPanel/index.tsx b/web/src/beta/features/Editor/tabs/map/LeftPanel/index.tsx index 831c9ee1f6..c9046dd1dd 100644 --- a/web/src/beta/features/Editor/tabs/map/LeftPanel/index.tsx +++ b/web/src/beta/features/Editor/tabs/map/LeftPanel/index.tsx @@ -1,9 +1,7 @@ -// import Loading from "@reearth/beta/components/Loading"; import SidePanelCommon from "@reearth/beta/features/Editor/SidePanel"; import GroupSectionField from "@reearth/beta/features/Editor/tabs/map/LeftPanel/GroupField"; import type { NLSLayer } from "@reearth/services/api/layersApi/utils"; import { useT } from "@reearth/services/i18n"; -// import { useTheme } from "@reearth/services/theme"; import type { LayerNameUpdateProps } from "../../../useLayers"; @@ -29,17 +27,7 @@ const MapSidePanel: React.FC = ({ onDataSourceManagerOpen, }) => { const t = useT(); - // const theme = useTheme(); - // const { useSceneQuery } = useSceneFetcher(); - - // const { scene } = useSceneQuery({ sceneId }); - - // const groups = scene?.property?.schema?.groups; - - // return !groups ? ( - // - // ) : ( return ( void; -}; - -export default function MenuButton({ - button: b, - menuItems, - pos, - theme, - onFlyTo: flyTo, -}: Props): JSX.Element { - const [visibleMenuButton, setVisibleMenuButton] = useState(); - - const referenceElement = useRef(null); - const popperElement = useRef(null); - const { styles, attributes } = usePopper(referenceElement.current, popperElement.current, { - placement: - pos === "topleft" - ? "bottom-start" - : pos === "topright" - ? "bottom-end" - : pos === "bottomleft" - ? "top-start" - : "top-end", - strategy: "fixed", - modifiers: [ - { - name: "eventListeners", - enabled: !visibleMenuButton, - options: { - scroll: false, - resize: false, - }, - }, - ], - }); - - const handleClick = useCallback( - (b: Button | MenuItem) => () => { - const t = "buttonType" in b ? b.buttonType : "menuType" in b ? b.menuType : undefined; - if (t === "menu") { - setVisibleMenuButton(v => (v === b.id ? undefined : b.id)); - return; - } - setVisibleMenuButton(undefined); - - if (t === "camera") { - const camera = - "buttonCamera" in b ? b.buttonCamera : "menuCamera" in b ? b.menuCamera : undefined; - if (camera) { - flyTo?.(camera, { duration: 2 }); - } - return; - } - - let link = "buttonLink" in b ? b.buttonLink : "menuLink" in b ? b.menuLink : undefined; - if (!link) return; - - const splitLink = link?.split("/"); - if (splitLink?.[0] !== "http:" && splitLink?.[0] !== "https:") { - link = "https://" + link; - } - window.open(link, "_blank", "noopener"); - }, - [flyTo], - ); - - const wrappperRef = useRef(null); - useClickAway(wrappperRef, () => setVisibleMenuButton(undefined)); - - return ( - - - setVisibleMenuButton(undefined)} - /> - - -
- {visibleMenuButton && ( - - {menuItems?.map(i => ( - - {i.menuType !== "border" && i.menuTitle} - - ))} - - )} -
-
- ); -} - -const Wrapper = styled.div` - position: relative; - padding: 2.5px; - &:first-of-type { - margin-left: 0; - } -`; - -const StyledIcon = styled(Icon)<{ margin: boolean }>` - vertical-align: middle; - margin-right: ${({ margin }) => (margin ? "5px" : null)}; -`; - -const MenuWrapper = styled.div<{ background?: string }>` - width: 100%; - position: absolute; - top: 0; - left: 0; - background-color: ${({ background }) => background}; - border-radius: 3px; - overflow-wrap: break-word; - hyphens: auto; -`; - -const MenuItem = styled.a<{ item?: MenuItem; color?: string }>` - display: block; - font-size: ${fonts.sizes["footnote"]}px; - margin: ${({ item }) => (item?.menuType === "border" ? "0 5px" : null)}; - padding: ${({ item }) => (item?.menuType === "border" ? null : "5px 20px")}; - cursor: ${({ item }) => (item?.menuType === "border" ? null : "pointer")}; - border-top: ${({ item }) => (item?.menuType === "border" ? "1px solid #fff" : null)}; - opacity: ${({ item }) => (item?.menuType === "border" ? "0.5" : null)}; - color: ${({ color }) => color}; -`; - -const Button = styled.div<{ button?: Button; publishedTheme?: Theme }>` - display: flex; - border-radius: 3px; - min-width: 32px; - height: 32px; - padding: 0 10px; - font-size: ${fonts.sizes["xFootnote"]}px; - line-height: 32px; - box-sizing: border-box; - background-color: ${({ button, publishedTheme }) => - button?.buttonBgcolor || publishedTheme?.background}; - color: ${({ button, publishedTheme }) => button?.buttonColor || publishedTheme?.mainText}; - cursor: pointer; - align-items: center; - user-select: none; -`; diff --git a/web/src/beta/lib/core/Crust/Widgets/Widget/builtin/LegacyMenu/index.stories.tsx b/web/src/beta/lib/core/Crust/Widgets/Widget/builtin/LegacyMenu/index.stories.tsx deleted file mode 100644 index df6c539508..0000000000 --- a/web/src/beta/lib/core/Crust/Widgets/Widget/builtin/LegacyMenu/index.stories.tsx +++ /dev/null @@ -1,107 +0,0 @@ -import { Meta, Story } from "@storybook/react"; -import { Math as CesiumMath } from "cesium"; - -import { contextEvents } from "../../storybook"; - -import Menu, { Props } from "."; - -export default { - component: Menu, - parameters: { actions: { argTypesRegex: "^on.*" } }, -} as Meta; - -export const Default: Story = args => ; - -Default.args = { - widget: { - id: "", - property: { - buttons: [ - { - id: "menu", - buttonType: "menu", - buttonTitle: "Menu", - buttonPosition: "topleft", - buttonStyle: "text", - }, - { - id: "google", - buttonType: "link", - buttonLink: "https://google.com", - buttonTitle: "Google", - buttonPosition: "topleft", - buttonStyle: "text", - buttonBgcolor: "red", - }, - { - id: "twitter", - buttonType: "link", - buttonLink: "https://twitter.com", - buttonTitle: "Twitter", - buttonPosition: "bottomright", - buttonStyle: "text", - buttonBgcolor: "#0ff", - }, - { - id: "hoge", - buttonType: "camera", - buttonPosition: "bottomright", - buttonCamera: { - lat: 0, - lng: 0, - height: 1000, - fov: CesiumMath.toRadians(60), - heading: 0, - pitch: 0, - roll: 0, - }, - buttonTitle: "hoge", - }, - { - id: "menu2", - buttonType: "menu", - buttonIcon: "/sample.svg", - buttonPosition: "bottomleft", - buttonStyle: "icon", - }, - { - id: "menu3", - buttonType: "menu", - buttonTitle: "Menu", - buttonIcon: "/sample.svg", - buttonPosition: "bottomleft", - buttonStyle: "texticon", - }, - ], - menu: [ - { - id: "hoge", - menuTitle: "Hoge", - menuType: "camera", - menuCamera: { - lat: 0, - lng: 0, - height: 1000, - fov: CesiumMath.toRadians(60), - heading: 0, - pitch: 0, - roll: 0, - }, - }, - { - id: "hoge", - menuType: "border", - }, - { - id: "GitHub", - menuType: "link", - menuTitle: "GitHub", - menuLink: "https://github.com", - }, - ], - }, - }, - context: { ...contextEvents }, - isBuilt: false, - isEditable: false, -}; diff --git a/web/src/beta/lib/core/Crust/Widgets/Widget/builtin/LegacyMenu/index.tsx b/web/src/beta/lib/core/Crust/Widgets/Widget/builtin/LegacyMenu/index.tsx deleted file mode 100644 index 891dd8ad6c..0000000000 --- a/web/src/beta/lib/core/Crust/Widgets/Widget/builtin/LegacyMenu/index.tsx +++ /dev/null @@ -1,66 +0,0 @@ -import { groupBy } from "lodash-es"; -import { useMemo } from "react"; - -import Flex from "@reearth/beta/components/Flex"; -import { styled } from "@reearth/services/theme"; - -import type { ComponentProps as WidgetProps } from "../.."; - -import MenuButton, { - Button as ButtonType, - Position as PositionType, - MenuItem as MenuItemType, -} from "./MenuButton"; - -export type Props = WidgetProps; -export type Position = PositionType; -export type Button = ButtonType; -export type MenuItem = MenuItemType; -export type Property = { - buttons?: Button[]; - menu?: MenuItem[]; -}; - -const Menu = ({ widget, theme, context: { onFlyTo } = {} }: Props): JSX.Element => { - const { buttons, menu: menuItems } = (widget?.property as Property | undefined) ?? {}; - const buttonsByPosition = useMemo( - () => groupBy(buttons, v => v.buttonPosition || "topleft"), - [buttons], - ); - - return ( - <> - {Object.entries(buttonsByPosition).map(([p, buttons]) => - buttons?.length ? ( - - {buttons.map(b => - !b.buttonInvisible ? ( - - ) : null, - )} - - ) : null, - )} - - ); -}; - -const Wrapper = styled(Flex)<{ position?: "topleft" | "topright" | "bottomleft" | "bottomright" }>` - position: absolute; - max-width: 100vw; - padding: 2.5px; - top: ${({ position }) => (position === "topleft" || position === "topright" ? "0" : null)}; - bottom: ${({ position }) => - position === "bottomleft" || position === "bottomright" ? "0" : null}; - left: ${({ position }) => (position === "topleft" || position === "bottomleft" ? "0" : null)}; - right: ${({ position }) => (position === "topright" || position === "bottomright" ? "0" : null)}; -`; - -export default Menu; diff --git a/web/src/beta/lib/core/Crust/Widgets/Widget/builtin/SplashScreen/index.stories.tsx b/web/src/beta/lib/core/Crust/Widgets/Widget/builtin/SplashScreen/index.stories.tsx deleted file mode 100644 index 220c10c5bd..0000000000 --- a/web/src/beta/lib/core/Crust/Widgets/Widget/builtin/SplashScreen/index.stories.tsx +++ /dev/null @@ -1,62 +0,0 @@ -import { Meta, Story } from "@storybook/react"; -import { Math as CesiumMath } from "cesium"; - -import { contextEvents } from "../../storybook"; - -import SplashScreen, { Props } from "."; - -export default { - component: SplashScreen, - parameters: { actions: { argTypesRegex: "^on.*" } }, -} as Meta; - -export const Default: Story = args => ; - -Default.args = { - widget: { - id: "", - property: { - overlay: { - overlayEnabled: true, - overlayDuration: 2, - overlayTransitionDuration: 1, - overlayDelay: 0.5, - overlayImage: `/sample.svg`, - overlayImageW: 648, - overlayImageH: 432, - overlayBgcolor: "#fff8", - }, - camera: [ - { - cameraPosition: { - lat: 0, - lng: 0, - height: 1000, - fov: CesiumMath.toRadians(60), - heading: 0, - pitch: 0, - roll: 0, - }, - cameraDelay: 3, - cameraDuration: 3, - }, - { - cameraPosition: { - lat: 0, - lng: 0, - height: 1000, - fov: CesiumMath.toRadians(60), - heading: 90, - pitch: 0, - roll: 0, - }, - cameraDelay: 3, - cameraDuration: 3, - }, - ], - }, - }, - context: { ...contextEvents }, - isBuilt: true, - isEditable: false, -}; diff --git a/web/src/beta/lib/core/Crust/Widgets/Widget/builtin/SplashScreen/index.tsx b/web/src/beta/lib/core/Crust/Widgets/Widget/builtin/SplashScreen/index.tsx deleted file mode 100644 index bb7d277162..0000000000 --- a/web/src/beta/lib/core/Crust/Widgets/Widget/builtin/SplashScreen/index.tsx +++ /dev/null @@ -1,138 +0,0 @@ -import { useTransition, TransitionStatus } from "@rot1024/use-transition"; -import { useState, useEffect } from "react"; -import { useTimeoutFn } from "react-use"; - -import { styled } from "@reearth/services/theme"; - -import type { ComponentProps as WidgetProps } from "../.."; -import type { Camera } from "../../types"; -import { useVisible, Visible } from "../../useVisible"; - -export type Props = WidgetProps; - -export type Property = { - overlay: { - overlayEnabled?: boolean; - overlayDelay?: number; - overlayDuration?: number; - overlayTransitionDuration?: number; - overlayBgcolor?: string; - overlayImage?: string; - overlayImageW?: number; - overlayImageH?: number; - overlayTitle?: string; - visible?: Visible; - }; - camera?: { - cameraPosition?: Camera; - cameraDuration?: number; - cameraDelay?: number; - }[]; -}; - -const SplashScreen = ({ - widget, - inEditor, - isMobile, - onVisibilityChange, - context: { onFlyTo } = {}, -}: Props): JSX.Element | null => { - const { property } = widget ?? {}; - const { - overlayEnabled: enabled, - overlayDelay: delay = 0, - overlayDuration: duration = 3, - overlayTransitionDuration: transitionDuration = 1, - overlayBgcolor: bgcolor, - overlayImage: image, - overlayImageW: imageW, - overlayImageH: imageH, - overlayTitle: title, - } = property?.overlay ?? {}; - const camera = property?.camera?.filter(c => !!c.cameraPosition); - const visible = useVisible({ - widgetId: widget.id, - visible: widget.property?.overlay.visible, - isMobile, - onVisibilityChange, - }); - - const [cameraSequence, setCameraSequence] = useState(0); - const [delayedCameraSequence, setDelayedCameraSequence] = useState(-1); - const currentCamera = camera?.[cameraSequence]; - const delayedCurrentCamera = camera?.[delayedCameraSequence]; - - useEffect(() => { - if (!onFlyTo) return; - const { cameraPosition, cameraDuration, cameraDelay } = delayedCurrentCamera ?? {}; - if (!cameraPosition) return; - const to = window.setTimeout(() => { - onFlyTo(cameraPosition, { - duration: cameraDuration ?? 0, - }); - }, (cameraDelay ?? 0) * 1000); - return () => clearTimeout(to); - }, [delayedCurrentCamera, onFlyTo]); - - const [isActive, setActive] = useState(false); - const state = useTransition(isActive, transitionDuration * 1000, { - mountOnEnter: true, - unmountOnExit: true, - }); - - useTimeoutFn(() => { - if (!inEditor && enabled) { - setActive(true); - } - }, delay * 1000); - - useTimeoutFn(() => { - setActive(false); - }, (delay + transitionDuration + duration) * 1000); - - useEffect(() => { - if (inEditor || !currentCamera) return; - const t = setTimeout(() => { - setDelayedCameraSequence(i => i + 1); - }, (currentCamera?.cameraDelay ?? 0) * 1000); - return () => clearTimeout(t); - }, [currentCamera, inEditor]); - - useEffect(() => { - if (inEditor || !delayedCurrentCamera) return; - const t = setTimeout(() => { - setCameraSequence(i => i + 1); - }, (delayedCurrentCamera?.cameraDuration ?? 0) * 1000); - return () => clearTimeout(t); - }, [delayedCurrentCamera, inEditor]); - - return !visible || state === "unmounted" ? null : ( - - {title} - - ); -}; - -const Wrapper = styled.div<{ state: TransitionStatus; bgcolor?: string; duration: number }>` - position: absolute; - top: 0; - right: 0; - left: 0; - bottom: 0; - transition: ${({ state, duration }) => - state === "entering" || state === "exiting" ? `all ${duration}s ease` : ""}; - opacity: ${({ state }) => (state === "entering" || state === "entered" ? 1 : 0)}; - background-color: ${({ bgcolor }) => bgcolor || "rgba(0, 0, 0, 0.7)"}; - display: flex; - align-items: center; - justify-content: center; - color: #fff; - padding: 16px; - z-index: ${props => props.theme.zIndexes.splashScreen}; -`; - -const Image = styled.img` - max-width: 100%; -`; - -export default SplashScreen; diff --git a/web/src/beta/lib/core/Crust/Widgets/Widget/builtin/Storytelling/hooks.ts b/web/src/beta/lib/core/Crust/Widgets/Widget/builtin/Storytelling/hooks.ts deleted file mode 100644 index c46a7cec0f..0000000000 --- a/web/src/beta/lib/core/Crust/Widgets/Widget/builtin/Storytelling/hooks.ts +++ /dev/null @@ -1,192 +0,0 @@ -import { Math as CesiumMath } from "cesium"; -import { useState, useCallback, useEffect, useMemo, useRef } from "react"; - -import { Camera, FlyToDestination, LookAtDestination } from "../../types"; - -export type Story = { - title: string; - layer?: string; - layerDuration?: number; - layerRange?: number; - layerCamera?: Camera; -}; - -const defaultRange = 50000; -const defaultDuration = 3; -const defaultCamera: Camera = { - lat: 0, - lng: 0, - height: 0, - heading: CesiumMath.toRadians(0), - pitch: CesiumMath.toRadians(-30), - roll: 0, - fov: CesiumMath.toRadians(60), -}; - -export default function ({ - duration = defaultDuration, - range = defaultRange, - camera = defaultCamera, - autoStart, - stories: storiesData, - selectedLayerId, - onFlyTo: flyTo, - onLookAt: lookAt, - onLayerSelect: selectLayer, - findPhotooverlayLayer, -}: { - duration?: number; - camera?: Camera; - range?: number; - autoStart?: boolean; - stories?: Story[]; - selectedLayerId?: { - layerId?: string; - featureId?: string; - }; - onFlyTo?: (target: string | FlyToDestination, options?: { duration?: number }) => void; - onLookAt?: (camera: LookAtDestination, options?: { duration?: number }) => void; - onLayerSelect?: ( - layerId: string | undefined, - featureId: string | undefined, - options?: { reason?: string }, - ) => void; - findPhotooverlayLayer?: ( - id: string, - ) => { title?: string; lat: number; lng: number; height: number } | undefined; -}) { - const [menuOpen, openMenu] = useState(false); - const toggleMenu = useCallback(() => openMenu(o => !o), []); - - const [selected, select] = useState<{ - index: number; - story: Story; - duration: number; - camera: Camera; - range: number; - noCameraFlight?: boolean; - }>(); - - const selectedLayer = useMemo( - () => (selectedLayerId?.layerId ? findPhotooverlayLayer?.(selectedLayerId.layerId) : undefined), - [findPhotooverlayLayer, selectedLayerId], - ); - - const timeout = useRef(); - - useEffect( - () => () => { - window.clearTimeout(timeout.current); - }, - [], - ); - - const stories = useMemo(() => { - if (!storiesData || !findPhotooverlayLayer) return []; - return storiesData.map(story => ({ - ...story, - title: story.title || (story.layer && findPhotooverlayLayer?.(story.layer)?.title) || "", - })); - }, [findPhotooverlayLayer, storiesData]); - - const selectAt = useCallback( - (index: number) => { - const story = stories?.[index]; - if (!story) { - select(undefined); - return; - } - - const id = story?.layer; - select({ - index, - story, - duration, - camera, - range, - }); - selectLayer?.(id, undefined, { reason: "storytelling" }); - }, - [camera, duration, range, selectLayer, stories], - ); - - const handleNext = useCallback(() => { - selectAt(typeof selected?.index === "undefined" ? 0 : selected.index + 1); - }, [selectAt, selected?.index]); - - const handlePrev = useCallback(() => { - selectAt(typeof selected?.index === "undefined" ? 0 : selected.index - 1); - }, [selectAt, selected?.index]); - - useEffect(() => { - openMenu(false); - }, [selectedLayer]); - - useEffect(() => { - const id = selectedLayerId?.layerId; - const index = id ? stories?.findIndex(l => l.layer === id) : undefined; - select( - typeof index === "number" && index >= 0 - ? { - index, - story: stories[index], - duration, - camera, - range, - noCameraFlight: true, - } - : undefined, - ); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [selectedLayer]); // ignore camera, duration, range, stories - - useEffect(() => { - if (!selected || !selectedLayer || selected.noCameraFlight) { - return; - } - - if (selected.story.layerCamera) { - flyTo?.(selected.story.layerCamera, { - duration: selected.story.layerDuration ?? selected.duration, - }); - return; - } - - const position = { - lat: selectedLayer?.lat, - lng: selectedLayer?.lng, - height: selectedLayer?.height ?? 0, - }; - - if (typeof position.lat !== "number" && typeof position.lng !== "number") return; - - lookAt?.( - { - ...position, - heading: selected.camera.heading, - pitch: selected.camera.pitch, - range: selected.story.layerRange ?? selected.range, - }, - { - duration: selected.story.layerDuration ?? selected.duration, - }, - ); - }, [flyTo, lookAt, selected, selectedLayer]); - - useEffect(() => { - if (!autoStart) return; - selectAt(0); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [autoStart]); // ignore selectAt - - return { - stories, - menuOpen, - selected, - handleNext, - handlePrev, - selectAt, - openMenu, - toggleMenu, - }; -} diff --git a/web/src/beta/lib/core/Crust/Widgets/Widget/builtin/Storytelling/index.stories.tsx b/web/src/beta/lib/core/Crust/Widgets/Widget/builtin/Storytelling/index.stories.tsx deleted file mode 100644 index 86a224355f..0000000000 --- a/web/src/beta/lib/core/Crust/Widgets/Widget/builtin/Storytelling/index.stories.tsx +++ /dev/null @@ -1,49 +0,0 @@ -import { Meta, Story } from "@storybook/react"; - -import { contextEvents } from "../../storybook"; - -import Component, { Props } from "."; - -export default { - component: Component, - parameters: { actions: { argTypesRegex: "^on.*" } }, -} as Meta; - -const Template: Story = args => ; - -export const Default = Template.bind({}); - -Default.args = { - widget: { - id: "", - property: { - stories: [ - { layer: "a", title: "a" }, - { layer: "b", title: "b" }, - { layer: "c", title: "c" }, - ], - }, - }, - context: { ...contextEvents }, - isBuilt: false, - isEditable: false, -}; - -export const AutoStart = Template.bind({}); - -AutoStart.args = { - widget: { - id: "", - property: { - stories: [ - { layer: "a", title: "a" }, - { layer: "b", title: "b" }, - { layer: "c", title: "c" }, - ], - default: { autoStart: true }, - }, - }, - context: { ...contextEvents }, - isBuilt: false, - isEditable: false, -}; diff --git a/web/src/beta/lib/core/Crust/Widgets/Widget/builtin/Storytelling/index.tsx b/web/src/beta/lib/core/Crust/Widgets/Widget/builtin/Storytelling/index.tsx deleted file mode 100644 index 69fe21c111..0000000000 --- a/web/src/beta/lib/core/Crust/Widgets/Widget/builtin/Storytelling/index.tsx +++ /dev/null @@ -1,262 +0,0 @@ -import { useRef } from "react"; -import { useClickAway, useMedia } from "react-use"; - -import Flex from "@reearth/beta/components/Flex"; -import Icon from "@reearth/beta/components/Icon"; -import Text from "@reearth/beta/components/Text"; -import { styled, css, metricsSizes } from "@reearth/services/theme"; - -import type { ComponentProps as WidgetProps } from "../.."; -import type { Camera, Theme } from "../../types"; - -import useHooks, { Story as StoryType } from "./hooks"; - -export type { Story } from "./hooks"; -export type Props = WidgetProps; - -export type Property = { - default?: { - duration?: number; - range?: number; - camera?: Camera; - autoStart?: boolean; - }; - stories?: StoryType[]; -}; - -const Storytelling = ({ - widget, - theme, - context: { selectedLayerId, onFlyTo, onLayerSelect, onLookAt, findPhotooverlayLayer } = {}, -}: Props): JSX.Element | null => { - const storiesData = widget?.property?.stories; - const { camera, duration, autoStart, range } = widget?.property?.default ?? {}; - const isExtraSmallWindow = useMedia("(max-width: 420px)"); - - const { stories, menuOpen, selected, handleNext, handlePrev, selectAt, openMenu, toggleMenu } = - useHooks({ - camera, - autoStart, - range, - duration, - stories: storiesData, - selectedLayerId, - onFlyTo: onFlyTo, - onLayerSelect, - onLookAt, - findPhotooverlayLayer, - }); - - const wrapperRef = useRef(null); - useClickAway(wrapperRef, () => { - openMenu(false); - }); - - return ( - <> - - {stories.map((story, i) => ( - - - - {story.title} - - - ))} - - - - - - - - - {selected?.story.title} - - - {typeof selected === "undefined" ? "-" : selected.index + 1} /{" "} - {stories.length > 0 ? stories.length : "-"} - - - - - - - - ); -}; - -const Widget = styled.div<{ - publishedTheme?: Theme; - extended?: boolean; - floating?: boolean; -}>` - background-color: ${({ publishedTheme }) => publishedTheme?.background}; - color: ${({ publishedTheme }) => publishedTheme?.mainText}; - display: flex; - align-items: stretch; - border-radius: ${metricsSizes["s"]}px; - overflow: hidden; - height: 80px; - width: ${({ extended }) => (extended ? "100%" : "500px")}; - box-shadow: 0 4px 4px rgba(0, 0, 0, 0.25); - - ${({ floating }) => - floating - ? css` - position: absolute; - bottom: 80px; - left: 80px; - ` - : null} - - @media (max-width: 560px) { - display: flex; - width: ${({ extended }) => (extended ? "100%" : "90vw")}; - margin: 0 auto; - height: 56px; - } -`; - -const ArrowButton = styled.button<{ publishedTheme?: Theme }>` - background-color: ${({ publishedTheme }) => publishedTheme?.mask}; - display: flex; - flex-flow: column; - justify-content: center; - text-align: center; - border: none; - padding: ${metricsSizes["s"]}px; - cursor: pointer; - color: ${({ publishedTheme }) => publishedTheme?.mainIcon}; - - &:disabled { - color: ${({ publishedTheme }) => publishedTheme?.weakIcon}; - cursor: auto; - } - @media (max-width: 420px) { - padding: ${metricsSizes["2xs"]}px; - } -`; - -const Current = styled(Flex)` - width: 100%; - padding: ${metricsSizes["2xl"]}px; - @media (max-width: 420px) { - padding: ${metricsSizes["s"]}px; - } -`; - -const Title = styled(Text)<{ color?: string }>` - color: ${({ color }) => color}; - text-overflow: ellipsis; - overflow: hidden; - white-space: nowrap; - box-sizing: border-box; - margin: 0 auto; - max-width: 250px; - text-align: center; - @media (max-width: 420px) { - max-width: 190px; - } -`; - -const StyledIcon = styled(Icon)<{ iconColor?: string }>` - color: ${({ color }) => color}; - margin-right: ${metricsSizes["l"]}px; - flex-shrink: 0; -`; - -const MenuIcon = styled(Icon)<{ menuOpen?: boolean; publishedTheme?: Theme }>` - background: ${props => - props.menuOpen && props.publishedTheme ? props.publishedTheme.select : "unset"}; - border-radius: 25px; - padding: ${metricsSizes["xs"]}px; - margin-right: ${metricsSizes["xs"]}px; - cursor: pointer; - user-select: none; - color: ${({ publishedTheme: theme }) => theme?.mainIcon}; -`; - -const Menu = styled.div<{ - menuOpen?: boolean; - publishedTheme?: Theme; - extended?: boolean; - area?: string; - align?: string; -}>` - background-color: ${({ publishedTheme: theme }) => theme?.background}; - z-index: ${props => props.theme.zIndexes.dropDown}; - position: absolute; - ${({ area, align }) => - area === "top" || (area === "middle" && align === "start") ? "top: 90px" : "bottom: 90px"}; - width: 324px; - max-height: ${({ area, align }) => - area === "middle" && align === "centered" ? "200px" : "500px"}; - overflow: auto; - -webkit-overflow-scrolling: touch; - border-radius: ${metricsSizes["s"]}px; - display: ${({ menuOpen }) => (!menuOpen ? "none" : "")}; - padding: ${metricsSizes["m"]}px ${metricsSizes["s"]}px; - @media (max-width: 560px) { - width: ${({ extended }) => (extended ? `calc(100% - 18px)` : "65vw")}; - max-height: ${({ area, align }) => - area === "middle" && align === "centered" ? "30vh" : "70vh"}; - border: 1px solid ${props => props.theme.content.main}; - top: ${({ area, align }) => - area === "top" || (area === "middle" && align === "start") ? "60px" : null}; - bottom: ${({ area, align }) => - (area === "middle" && align !== "start") || area !== "top" ? "60px" : null}; - } -`; - -const MenuItem = styled(Flex)<{ selected?: boolean; publishedTheme?: Theme }>` - border-radius: ${metricsSizes["m"]}px; - padding: ${metricsSizes["m"]}px ${metricsSizes["s"]}px; - background: ${({ publishedTheme: theme, selected }) => - selected && theme ? theme.select : "inherit"}; - cursor: pointer; - user-select: none; - &:hover { - background: ${props => !props.selected && props.publishedTheme?.mask}; - } -`; - -export default Storytelling; diff --git a/web/src/beta/lib/core/Crust/Widgets/Widget/builtin/index.ts b/web/src/beta/lib/core/Crust/Widgets/Widget/builtin/index.ts index 47cb1599c8..67517d20d5 100644 --- a/web/src/beta/lib/core/Crust/Widgets/Widget/builtin/index.ts +++ b/web/src/beta/lib/core/Crust/Widgets/Widget/builtin/index.ts @@ -1,26 +1,18 @@ import { merge } from "lodash-es"; +import { + BUTTON_BUILTIN_WIDGET_ID, + NAVIGATOR_BUILTIN_WIDGET_ID, +} from "@reearth/services/api/widgetsApi/utils"; + import Button from "./Button"; -import Menu from "./LegacyMenu"; import Navigator from "./Navigator"; -import SplashScreen from "./SplashScreen"; -import Storytelling from "./Storytelling"; -import Timeline from "./Timeline"; +// import Timeline from "./Timeline"; import { Component, unsafeBuiltinWidgets, type UnsafeBuiltinWidgets } from "./unsafeWidgets"; -export const MENU_BUILTIN_WIDGET_ID = "reearth/menu"; // legacy -export const BUTTON_BUILTIN_WIDGET_ID = "reearth/button"; -export const SPLASHSCREEN_BUILTIN_WIDGET_ID = "reearth/splashscreen"; -export const STORYTELLING_BUILTIN_WIDGET_ID = "reearth/storytelling"; -export const TIMELINE_BUILTIN_WIDGET_ID = "reearth/timeline"; -export const NAVIGATOR_BUILTIN_WIDGET_ID = "reearth/navigator"; - export type ReEarthBuiltinWidgets = Record< - | typeof MENU_BUILTIN_WIDGET_ID | typeof BUTTON_BUILTIN_WIDGET_ID - | typeof SPLASHSCREEN_BUILTIN_WIDGET_ID - | typeof STORYTELLING_BUILTIN_WIDGET_ID - | typeof TIMELINE_BUILTIN_WIDGET_ID + // | typeof TIMELINE_BUILTIN_WIDGET_ID | typeof NAVIGATOR_BUILTIN_WIDGET_ID, T >; @@ -28,13 +20,10 @@ export type ReEarthBuiltinWidgets = Record< export type BuiltinWidgets = ReEarthBuiltinWidgets & UnsafeBuiltinWidgets; const REEARTH_BUILTIN_WIDGET_OPTIONS: BuiltinWidgets<{ animation?: boolean }> = { - [MENU_BUILTIN_WIDGET_ID]: {}, [BUTTON_BUILTIN_WIDGET_ID]: {}, - [SPLASHSCREEN_BUILTIN_WIDGET_ID]: {}, - [STORYTELLING_BUILTIN_WIDGET_ID]: {}, - [TIMELINE_BUILTIN_WIDGET_ID]: { - animation: true, - }, + // [TIMELINE_BUILTIN_WIDGET_ID]: { + // animation: true, + // }, [NAVIGATOR_BUILTIN_WIDGET_ID]: { animation: true, }, @@ -48,11 +37,8 @@ Object.keys(unsafeBuiltinWidgets ?? {}).map(uw => { }); const reearthBuiltin: BuiltinWidgets = { - [MENU_BUILTIN_WIDGET_ID]: Menu, [BUTTON_BUILTIN_WIDGET_ID]: Button, - [SPLASHSCREEN_BUILTIN_WIDGET_ID]: SplashScreen, - [STORYTELLING_BUILTIN_WIDGET_ID]: Storytelling, - [TIMELINE_BUILTIN_WIDGET_ID]: Timeline, + // [TIMELINE_BUILTIN_WIDGET_ID]: Timeline, [NAVIGATOR_BUILTIN_WIDGET_ID]: Navigator, }; diff --git a/web/src/beta/lib/core/StoryPanel/ActionPanel/index.tsx b/web/src/beta/lib/core/StoryPanel/ActionPanel/index.tsx index 2ba9046c73..44dc211baf 100644 --- a/web/src/beta/lib/core/StoryPanel/ActionPanel/index.tsx +++ b/web/src/beta/lib/core/StoryPanel/ActionPanel/index.tsx @@ -1,4 +1,4 @@ -import { Dispatch, Fragment, MouseEvent, SetStateAction, useMemo } from "react"; +import { Dispatch, Fragment, MouseEvent, SetStateAction, useCallback, useMemo } from "react"; import { useItemContext } from "@reearth/beta/components/DragAndDropList/Item"; import FieldComponents from "@reearth/beta/components/fields/PropertyFields"; @@ -51,6 +51,11 @@ const ActionPanel: React.FC = ({ const t = useT(); const ref = useItemContext(); + const handleRemove = useCallback(() => { + onRemove?.(); + onSettingsToggle?.(); + }, [onRemove, onSettingsToggle]); + const popoverContent = useMemo(() => { const menuItems: { name: string; icon: Icons; onClick: () => void }[] = [ { @@ -63,11 +68,11 @@ const ActionPanel: React.FC = ({ menuItems.push({ name: t("Remove"), icon: "trash", - onClick: onRemove, + onClick: handleRemove, }); } return menuItems; - }, [t, setShowPadding, onRemove]); + }, [t, setShowPadding, onRemove, handleRemove]); return ( @@ -77,7 +82,7 @@ const ActionPanel: React.FC = ({ )} onSettingsToggle?.()} placement="bottom-start"> @@ -128,6 +133,8 @@ const ActionPanel: React.FC = ({ export default ActionPanel; const Wrapper = styled.div<{ isSelected?: boolean; position?: ActionPosition }>` + ${({ isSelected }) => !isSelected && "background: #f1f1f1;"} + z-index: 1; color: ${({ theme }) => theme.select.main}; display: flex; align-items: center; @@ -154,16 +161,14 @@ const Wrapper = styled.div<{ isSelected?: boolean; position?: ActionPosition }>` right: -1px; top: -25px; `} - transition: all 0.2s; `; const BlockOptions = styled.div<{ isSelected?: boolean }>` - background: ${({ isSelected, theme }) => (isSelected ? theme.select.main : "transparent")}; + background: ${({ isSelected, theme }) => (isSelected ? theme.select.main : "#f1f1f1")}; color: ${({ isSelected, theme }) => (isSelected ? theme.content.main : theme.select.main)}; display: flex; align-items: center; height: 24px; - transition: all 0.2s; `; const OptionWrapper = styled.div<{ showPointer?: boolean }>` @@ -177,8 +182,9 @@ const OptionText = styled(Text)` `; const OptionIcon = styled(Icon)<{ border?: boolean }>` - padding: 4px; ${({ border }) => border && "border-left: 1px solid #f1f1f1;"} + padding: 4px; + transition: none; `; const SettingsDropdown = styled.div` @@ -209,5 +215,8 @@ const CancelIcon = styled(Icon)` `; const DndHandle = styled.div` + height: 100%; + display: flex; + align-items: center; cursor: move; `; diff --git a/web/src/beta/lib/core/StoryPanel/Block/builtin/index.ts b/web/src/beta/lib/core/StoryPanel/Block/builtin/index.ts index 9784fc13b3..b0738098c5 100644 --- a/web/src/beta/lib/core/StoryPanel/Block/builtin/index.ts +++ b/web/src/beta/lib/core/StoryPanel/Block/builtin/index.ts @@ -1,5 +1,12 @@ import { merge } from "lodash-es"; +import { + IMAGE_BUILTIN_STORY_BLOCK_ID, + TEXT_BUILTIN_STORY_BLOCK_ID, + TITLE_BUILTIN_STORY_BLOCK_ID, + VIDEO_BUILTIN_STORY_BLOCK_ID, +} from "@reearth/services/api/storytellingApi/blocks"; + import { Component } from ".."; import ImageBlock from "./Image"; @@ -7,18 +14,6 @@ import TextBlock from "./Text"; import TitleBlock from "./Title"; import VideoBlock from "./Video"; -export const TITLE_BUILTIN_STORY_BLOCK_ID = "reearth/titleStoryBlock"; // pseudo storyblock - -export const IMAGE_BUILTIN_STORY_BLOCK_ID = "reearth/imageStoryBlock"; -export const TEXT_BUILTIN_STORY_BLOCK_ID = "reearth/textStoryBlock"; -export const VIDEO_BUILTIN_STORY_BLOCK_ID = "reearth/videoStoryBlock"; - -export const AVAILABLE_STORY_BLOCK_IDS = [ - IMAGE_BUILTIN_STORY_BLOCK_ID, - TEXT_BUILTIN_STORY_BLOCK_ID, - VIDEO_BUILTIN_STORY_BLOCK_ID, -]; - export type ReEarthBuiltinStoryBlocks = Record< | typeof TITLE_BUILTIN_STORY_BLOCK_ID | typeof IMAGE_BUILTIN_STORY_BLOCK_ID diff --git a/web/src/beta/lib/core/StoryPanel/Block/index.tsx b/web/src/beta/lib/core/StoryPanel/Block/index.tsx index 6586551d48..efa212af5f 100644 --- a/web/src/beta/lib/core/StoryPanel/Block/index.tsx +++ b/web/src/beta/lib/core/StoryPanel/Block/index.tsx @@ -7,7 +7,6 @@ import builtin, { isBuiltinStoryBlock } from "./builtin"; import type { CommonProps, BlockProps } from "./types"; export type Props = { - pageId?: string; renderBlock?: (block: BlockProps) => ReactNode; layer?: Layer; } & CommonProps; @@ -15,7 +14,6 @@ export type Props = { export type Component = ComponentType; export default function StoryBlockComponent({ - pageId, renderBlock, onRemove, ...props @@ -23,10 +21,7 @@ export default function StoryBlockComponent({ const builtinBlockId = `${props.block?.pluginId}/${props.block?.extensionId}`; const Builtin = isBuiltinStoryBlock(builtinBlockId) ? builtin[builtinBlockId] : undefined; - const handleRemove = useCallback( - () => onRemove?.(pageId, props.block?.id), - [pageId, props.block?.id, onRemove], - ); + const handleRemove = useCallback(() => onRemove?.(props.block?.id), [props.block?.id, onRemove]); return Builtin ? ( diff --git a/web/src/beta/lib/core/StoryPanel/Page/BlockAddBar.tsx b/web/src/beta/lib/core/StoryPanel/Page/BlockAddBar.tsx index 91a9396db2..d94db69c43 100644 --- a/web/src/beta/lib/core/StoryPanel/Page/BlockAddBar.tsx +++ b/web/src/beta/lib/core/StoryPanel/Page/BlockAddBar.tsx @@ -7,8 +7,9 @@ import type { InstallableStoryBlock } from "@reearth/services/api/storytellingAp import { styled } from "@reearth/services/theme"; type Props = { - openBlocks: boolean; installableStoryBlocks?: InstallableStoryBlock[]; + openBlocks: boolean; + alwaysShow?: boolean; onBlockOpen: () => void; onBlockAdd?: (extensionId?: string, pluginId?: string) => void; }; @@ -16,6 +17,7 @@ type Props = { const BlockAddBar: React.FC = ({ installableStoryBlocks, openBlocks, + alwaysShow, onBlockOpen, onBlockAdd, }) => { @@ -46,7 +48,7 @@ const BlockAddBar: React.FC = ({ - + diff --git a/web/src/beta/lib/core/StoryPanel/Page/hooks.ts b/web/src/beta/lib/core/StoryPanel/Page/hooks.ts index 91a1c28d98..b8e1c1f7e7 100644 --- a/web/src/beta/lib/core/StoryPanel/Page/hooks.ts +++ b/web/src/beta/lib/core/StoryPanel/Page/hooks.ts @@ -1,6 +1,9 @@ import { useCallback, useEffect, useMemo, useState } from "react"; +import { ValueTypes } from "@reearth/beta/utils/value"; + import type { Page } from "../hooks"; +import { getFieldValue } from "../utils"; export type { Page } from "../hooks"; @@ -22,6 +25,21 @@ export default ({ const propertyItems = useMemo(() => page?.property.items, [page?.property]); + const padding = useMemo( + () => getFieldValue(propertyItems ?? [], "padding", "panel") as ValueTypes["spacing"], + [propertyItems], + ); + + const gap = useMemo( + () => getFieldValue(propertyItems ?? [], "gap", "panel") as ValueTypes["number"], + [propertyItems], + ); + + const titleProperty = useMemo( + () => propertyItems?.find(i => i.schemaGroup === "title"), + [propertyItems], + ); + const handleBlockOpen = useCallback( (index: number) => { if (openBlocksIndex === index) { @@ -33,11 +51,6 @@ export default ({ [openBlocksIndex], ); - const titleProperty = useMemo( - () => propertyItems?.find(i => i.schemaGroup === "title"), - [propertyItems], - ); - const titleId = useMemo(() => `${page?.id}/title`, [page?.id]); const handleBlockCreate = useCallback( @@ -55,6 +68,8 @@ export default ({ titleId, titleProperty, propertyItems, + padding, + gap, storyBlocks, items, setItems, diff --git a/web/src/beta/lib/core/StoryPanel/Page/index.tsx b/web/src/beta/lib/core/StoryPanel/Page/index.tsx index bba30a5908..563bc73662 100644 --- a/web/src/beta/lib/core/StoryPanel/Page/index.tsx +++ b/web/src/beta/lib/core/StoryPanel/Page/index.tsx @@ -26,7 +26,7 @@ type Props = { pluginId?: string | undefined, index?: number | undefined, ) => Promise | undefined; - onBlockDelete?: (pageId?: string, blockId?: string) => Promise; + onBlockDelete?: (blockId?: string | undefined) => Promise | undefined; onBlockSelect?: (blockId?: string) => void; onPropertyUpdate?: ( propertyId?: string, @@ -61,6 +61,8 @@ const StoryPage: React.FC = ({ titleId, titleProperty, propertyItems, + padding, + gap, storyBlocks, items, setItems, @@ -85,7 +87,7 @@ const StoryPage: React.FC = ({ onClick={() => onPageSelect?.(page?.id)} onClickAway={onPageSelect} onSettingsToggle={onPageSettingsToggle}> - + {titleProperty && ( = ({ )} {isEditable && ( handleBlockOpen(-1)} @@ -116,7 +119,7 @@ const StoryPage: React.FC = ({ {storyBlocks && storyBlocks.length > 0 && ( item.id} onItemDrop={async (item, index) => { @@ -163,16 +166,23 @@ const StoryPage: React.FC = ({ export default StoryPage; -const Wrapper = styled.div<{ padding?: Spacing }>` +const Wrapper = styled.div<{ padding?: Spacing; gap?: number; isEditable?: boolean }>` display: flex; flex-direction: column; color: ${({ theme }) => theme.content.weaker}; + ${({ gap }) => gap && `gap: ${gap}px;`} - padding: 20px; - padding-top: ${({ padding }) => padding?.top + "px" ?? 0}; - padding-bottom: ${({ padding }) => padding?.bottom + "px" ?? 0}; - padding-left: ${({ padding }) => padding?.left + "px" ?? 0}; - padding-right: ${({ padding }) => padding?.right + "px" ?? 0}; + padding-top: ${({ padding, isEditable }) => calculatePadding(padding?.top, isEditable)}; + padding-bottom: ${({ padding, isEditable }) => calculatePadding(padding?.bottom, isEditable)}; + padding-left: ${({ padding, isEditable }) => calculatePadding(padding?.left, isEditable)}; + padding-right: ${({ padding, isEditable }) => calculatePadding(padding?.right, isEditable)}; box-sizing: border-box; `; + +const calculatePadding = (value?: number, editorMode?: boolean) => { + if (!value) { + return editorMode ? "4px" : "0px"; + } + return editorMode && value < 4 ? "4px" : value + "px"; +}; diff --git a/web/src/beta/lib/core/StoryPanel/PanelContent/hooks.ts b/web/src/beta/lib/core/StoryPanel/PanelContent/hooks.ts index 1b3623b581..3abb2e53dd 100644 --- a/web/src/beta/lib/core/StoryPanel/PanelContent/hooks.ts +++ b/web/src/beta/lib/core/StoryPanel/PanelContent/hooks.ts @@ -12,6 +12,7 @@ export default ({ isAutoScrolling, onAutoScrollingChange, onBlockCreate, + onBlockDelete, onCurrentPageChange, }: { pages?: Page[]; @@ -24,6 +25,7 @@ export default ({ pluginId?: string | undefined, index?: number | undefined, ) => Promise; + onBlockDelete?: (pageId?: string | undefined, blockId?: string | undefined) => Promise; onCurrentPageChange?: (pageId: string) => void; }) => { const scrollRef = useRef(undefined); @@ -42,6 +44,11 @@ export default ({ [onBlockCreate], ); + const handleBlockDelete = useCallback( + (pageId: string) => (blockId?: string) => onBlockDelete?.(pageId, blockId), + [onBlockDelete], + ); + useLayoutEffect(() => { const pageWrapperElement = document.getElementById(PAGES_ELEMENT_ID); if (pageWrapperElement) setPageGap(pageWrapperElement.clientHeight - 40); // 40px is the height of the page title block @@ -119,5 +126,6 @@ export default ({ return { pageGap, handleBlockCreate, + handleBlockDelete, }; }; diff --git a/web/src/beta/lib/core/StoryPanel/PanelContent/index.tsx b/web/src/beta/lib/core/StoryPanel/PanelContent/index.tsx index 8aca052393..043d564266 100644 --- a/web/src/beta/lib/core/StoryPanel/PanelContent/index.tsx +++ b/web/src/beta/lib/core/StoryPanel/PanelContent/index.tsx @@ -59,12 +59,13 @@ const StoryContent: React.FC = ({ onCurrentPageChange, onStoryBlockMove, }) => { - const { pageGap, handleBlockCreate } = useHooks({ + const { pageGap, handleBlockCreate, handleBlockDelete } = useHooks({ pages, selectedPageId, isAutoScrolling, onAutoScrollingChange, onBlockCreate, + onBlockDelete, onCurrentPageChange, }); @@ -82,7 +83,7 @@ const StoryContent: React.FC = ({ onPageSettingsToggle={onPageSettingsToggle} onPageSelect={onPageSelect} onBlockCreate={handleBlockCreate(p.id)} - onBlockDelete={onBlockDelete} + onBlockDelete={handleBlockDelete(p.id)} onBlockSelect={onBlockSelect} onStoryBlockMove={onStoryBlockMove} onPropertyUpdate={onPropertyUpdate} diff --git a/web/src/beta/lib/core/StoryPanel/SelectableArea/hooks.ts b/web/src/beta/lib/core/StoryPanel/SelectableArea/hooks.ts index 340e16ccc8..de0a8b50c3 100644 --- a/web/src/beta/lib/core/StoryPanel/SelectableArea/hooks.ts +++ b/web/src/beta/lib/core/StoryPanel/SelectableArea/hooks.ts @@ -15,12 +15,18 @@ type Props = { isSelected?: boolean; propertyItems?: Item[]; setEditMode?: Dispatch>; + onClickAway?: () => void; }; -export default ({ editMode, isSelected, propertyItems, setEditMode }: Props) => { +export default ({ editMode, isSelected, propertyItems, setEditMode, onClickAway }: Props) => { const [isHovered, setHover] = useState(false); const [showPadding, setShowPadding] = useState(false); + const panelSettings: Item | undefined = useMemo( + () => propertyItems?.find(i => i.schemaGroup === "panel"), + [propertyItems], + ); + useEffect(() => { if (!isSelected && editMode) { setEditMode?.(false); @@ -34,10 +40,10 @@ export default ({ editMode, isSelected, propertyItems, setEditMode }: Props) => const handleMouseOut = useCallback(() => setHover(false), []); - const panelSettings: Item | undefined = useMemo( - () => propertyItems?.find(i => i.schemaGroup === "panel"), - [propertyItems], - ); + const handleClickAway = useCallback(() => { + setShowPadding(false); + onClickAway?.(); + }, [onClickAway]); return { isHovered, @@ -46,5 +52,6 @@ export default ({ editMode, isSelected, propertyItems, setEditMode }: Props) => setShowPadding, handleMouseOver, handleMouseOut, + handleClickAway, }; }; diff --git a/web/src/beta/lib/core/StoryPanel/SelectableArea/index.tsx b/web/src/beta/lib/core/StoryPanel/SelectableArea/index.tsx index 735b4aa271..b8b99048b3 100644 --- a/web/src/beta/lib/core/StoryPanel/SelectableArea/index.tsx +++ b/web/src/beta/lib/core/StoryPanel/SelectableArea/index.tsx @@ -49,18 +49,26 @@ const SelectableArea: React.FC = ({ onClickAway, onRemove, }) => { - const { showPadding, panelSettings, setShowPadding, isHovered, handleMouseOver, handleMouseOut } = - useHooks({ - editMode, - isSelected, - propertyItems, - setEditMode, - }); + const { + isHovered, + showPadding, + panelSettings, + setShowPadding, + handleMouseOver, + handleMouseOut, + handleClickAway, + } = useHooks({ + editMode, + isSelected, + propertyItems, + setEditMode, + onClickAway, + }); return !isEditable ? ( <>{children} ) : ( - + { ?.map(p => { const plugin = p.plugin; return plugin?.extensions - .filter(e => e.type === PluginExtensionType.Widget) + .filter( + e => + e.type === PluginExtensionType.Widget && + AVAILABLE_WIDGET_IDS.includes(`reearth/${e.extensionId}`), + ) .map((e): InstallableWidget => { return { pluginId: plugin.id, diff --git a/web/src/services/i18n/translations/en.yml b/web/src/services/i18n/translations/en.yml index 6bcedb2f10..b5f5d6450b 100644 --- a/web/src/services/i18n/translations/en.yml +++ b/web/src/services/i18n/translations/en.yml @@ -21,10 +21,16 @@ You have passed the minimum value.: You have passed the minimum value. Remove: Remove Add Item: '' Please choose an option: '' +Wrong file format: Wrong file format +Choose: Choose +Upload: Upload Not found: Not found -Date: Date -File size: File size -Alphabetical: Alphabetical +Last Uploaded: Last Uploaded +First Uploaded: First Uploaded +A To Z: A To Z +Z To A: Z To A +Size Small to Large: Size Small to Large +Size Large to Small: Size Large to Small File Format: '' File format of the data source you want to add.: '' From URL: '' @@ -43,13 +49,13 @@ Choose layer to add: Choose layer to add Layer of the data source you want to add.: Layer of the data source you want to add layer name: Layer name Layer: Layer -Scene: Scene -Main: '' Layers: Layers Outline: Outline +Add Layer from Resource: '' Inspector: Inspector Published: Published Unpublished: Unpublished +Scene: Scene Story: Story Unpublish: Unpublish Update: Update @@ -88,6 +94,13 @@ Container Settings: Container Settings Angle: Angle Narrow: Narrow Wide: Wide +Please select an asset before clicking Select.: Please select an asset before clicking Select. +Select Image: Select Image +Select: Select +No assets match your search.: No assets match your search. +You haven't uploaded any assets yet. Click the upload button above and select a compatible file from your computer.: >- + You haven't uploaded any assets yet. Click the upload button above and select + a compatible file from your computer. Create New Workspace: Create New Workspace Create: Create Workspace Name: Workspace Name @@ -113,10 +126,6 @@ Something went wrong. Please try again later.: Something went wrong. Please try Project Settings: '' Upload file: Upload file Delete: Delete -No assets match your search.: No assets match your search. -You haven't uploaded any assets yet. Click the upload button above and select a compatible file from your computer.: >- - You haven't uploaded any assets yet. Click the upload button above and select - a compatible file from your computer. You are about to delete one or more assets from the current workspace.: You are about to delete one or more assets from the current workspace. Please make sure no selected assets are in use. This cannot be undone.: Please make sure no selected assets are in use. This cannot be undone. Most project settings are hidden when the project is archived. Please unarchive the project to view and edit these settings.: >- @@ -248,12 +257,14 @@ Reset password: Reset password Create your Account: Create your Account Already have an account?: Already have an account? Log in: Log in +Date: Date +File size: File size +Alphabetical: Alphabetical Assets Library: Assets Library Use URL: Use URL Add video URL: Add video URL Save: Save Select Asset: Select Asset -Select: Select Your browser is too small: Your browser is too small Re:Earth needs at least 900px width to be used effectively. Please resize your browser window.: >- Re:Earth needs at least 900px width to be used effectively. Please resize your @@ -516,14 +527,3 @@ Failed to update widget.: Failed to update widget. Failed to remove widget.: Failed to remove widget. Failed to update widget alignment.: Failed to update widget alignment. Failed to update the widget align system.: Failed to update the widget align system. -Choose: Choose -Upload: Upload -Last Uploaded: Last Uploaded -First Uploaded: First Uploaded -A To Z: A To Z -Z To A: Z To A -Size Small to Large: Size Small to Large -Size Large to Small: Size Large to Small -Please select an asset before clicking Select.: Please select an asset before clicking Select. -Select Image: Select Image -Wrong file format: Wrong file format diff --git a/web/src/services/i18n/translations/ja.yml b/web/src/services/i18n/translations/ja.yml index 56467cb940..28a2d74353 100644 --- a/web/src/services/i18n/translations/ja.yml +++ b/web/src/services/i18n/translations/ja.yml @@ -21,10 +21,16 @@ You have passed the minimum value.: You have passed the minimum value. Remove: 削除 Add Item: '' Please choose an option: '' +Wrong file format: Wrong file format +Choose: 選択 +Upload: アップロード Not found: ページが見つかりません -Date: 作成日時順 -File size: サイズ順 -Alphabetical: 名前順 +Last Uploaded: Last Uploaded +First Uploaded: First Uploaded +A To Z: A To Z +Z To A: Z To A +Size Small to Large: Size Small to Large +Size Large to Small: Size Large to Small File Format: ファイルフォーマット File format of the data source you want to add.: '' From URL: '' @@ -43,13 +49,13 @@ Choose layer to add: '' Layer of the data source you want to add.: '' layer name: '' Layer: レイヤー -Scene: シーン -Main: '' Layers: レイヤー Outline: アウトライン +Add Layer from Resource: '' Inspector: インスペクター Published: 一般公開 Unpublished: 非公開 +Scene: シーン Story: ストーリー Unpublish: 非公開にする Update: 更新する @@ -84,6 +90,11 @@ Container Settings: Container Settings Angle: 角度 Narrow: 狭い Wide: 広い +Please select an asset before clicking Select.: Please select an asset before clicking Select. +Select Image: イメージ選択 +Select: 選択 +No assets match your search.: 検索条件に該当するアセットは見つかりませんでした。 +You haven't uploaded any assets yet. Click the upload button above and select a compatible file from your computer.: アセットライブラリ Create New Workspace: 新規ワークスペース作成 Create: 作成 Workspace Name: ワークスペース名 @@ -109,8 +120,6 @@ Something went wrong. Please try again later.: 何らかの問題が発生しま Project Settings: プロジェクト設定 Upload file: ファイルアップロード Delete: 削除 -No assets match your search.: 検索条件に該当するアセットは見つかりませんでした。 -You haven't uploaded any assets yet. Click the upload button above and select a compatible file from your computer.: アセットライブラリ You are about to delete one or more assets from the current workspace.: 1つまたは複数のアセットを削除しようとしています。 Please make sure no selected assets are in use. This cannot be undone.: 選択されたアセットが使用されていないことを確認してください。この操作は取り消せません。 Most project settings are hidden when the project is archived. Please unarchive the project to view and edit these settings.: プロジェクトをアーカイブ化すると、削除とアーカイブ化解除以外の編集は行えません。再度編集可能な状態にするには、プロジェクトのアーカイブ化を解除してください。 @@ -227,12 +236,14 @@ Reset password: パスワードをリセット Create your Account: アカウントを登録する Already have an account?: 既にアカウントをお持ちの場合 Log in: ログイン +Date: 作成日時順 +File size: サイズ順 +Alphabetical: 名前順 Assets Library: アセット Use URL: URL Add video URL: 映像URLを追加 Save: 保存 Select Asset: 選択 -Select: 選択 Your browser is too small: ブラウザ幅が小さすぎます Re:Earth needs at least 900px width to be used effectively. Please resize your browser window.: 横幅が900px以上になるようブラウザ幅を調節してください。 OK: 確認 @@ -477,14 +488,3 @@ Failed to update widget.: ウィジェットのアップデートに失敗しま Failed to remove widget.: ウィジェットの削除に失敗しました。 Failed to update widget alignment.: ウィジェットのアラインメントのアップデートに失敗しました。 Failed to update the widget align system.: ウィジェットのアラインシステムのアップデートに失敗しました。 -Choose: 選択 -Upload: アップロード -Last Uploaded: Last Uploaded -First Uploaded: First Uploaded -A To Z: A To Z -Z To A: Z To A -Size Small to Large: Size Small to Large -Size Large to Small: Size Large to Small -Please select an asset before clicking Select.: Please select an asset before clicking Select. -Select Image: イメージ選択 -Wrong file format: Wrong file format \ No newline at end of file