From 770edb9abe80d917876dcc4e08bb2f4df62a645a Mon Sep 17 00:00:00 2001 From: KaWaite <34051327+KaWaite@users.noreply.github.com> Date: Tue, 14 Nov 2023 14:10:03 +0900 Subject: [PATCH] chore(web): ui and ux fixes (#809) --- server/pkg/builtin/manifest.yml | 76 +++---- .../Icon/Icons/timelineStoryBlock.svg | 5 +- .../Icon/Icons/timelineStoryBlockSolid.svg | 3 + web/src/beta/components/Icon/icons.ts | 2 + .../SidePanelSectionField/index.tsx | 2 +- .../beta/features/Editor/SidePanel/index.tsx | 2 +- .../tabs/map/LeftPanel/GroupField/index.tsx | 2 +- .../tabs/map/LeftPanel/Layers/index.tsx | 2 +- .../tabs/story/LeftPanel/Action/index.tsx | 5 +- .../Block/builtin/Layer/Content.tsx | 134 +++++++++++ .../StoryPanel/Block/builtin/Layer/Editor.tsx | 210 ------------------ .../Block/builtin/Layer/Editor/hooks.ts | 120 ++++++++++ .../Block/builtin/Layer/Editor/index.tsx | 128 +++++++++++ .../StoryPanel/Block/builtin/Layer/index.tsx | 88 ++------ .../Block/builtin/Timeline/Editor.tsx | 2 +- web/src/services/i18n/translations/en.yml | 2 +- web/src/services/i18n/translations/ja.yml | 2 +- 17 files changed, 455 insertions(+), 330 deletions(-) create mode 100644 web/src/beta/components/Icon/Icons/timelineStoryBlockSolid.svg create mode 100644 web/src/beta/lib/core/StoryPanel/Block/builtin/Layer/Content.tsx delete mode 100644 web/src/beta/lib/core/StoryPanel/Block/builtin/Layer/Editor.tsx create mode 100644 web/src/beta/lib/core/StoryPanel/Block/builtin/Layer/Editor/hooks.ts create mode 100644 web/src/beta/lib/core/StoryPanel/Block/builtin/Layer/Editor/index.tsx diff --git a/server/pkg/builtin/manifest.yml b/server/pkg/builtin/manifest.yml index 8ed7647a31..4c76ba12ff 100644 --- a/server/pkg/builtin/manifest.yml +++ b/server/pkg/builtin/manifest.yml @@ -2340,7 +2340,7 @@ extensions: ui: datetime title: Time - id: textStoryBlock - name: Text Block + name: Text type: storyBlock description: Storytelling Text Block schema: @@ -2362,7 +2362,7 @@ extensions: title: Content ui: multiline - id: mdTextStoryBlock - name: MD Text Block + name: MD Text type: storyBlock description: Storytelling MD Text Block schema: @@ -2384,7 +2384,7 @@ extensions: title: Content ui: multiline - id: imageStoryBlock - name: Image Block + name: Image type: storyBlock description: Storytelling Image Block schema: @@ -2406,7 +2406,7 @@ extensions: title: Image ui: image - id: videoStoryBlock - name: Video Block + name: Video type: storyBlock description: Storytelling Video Block schema: @@ -2501,40 +2501,8 @@ extensions: - id: url type: url title: Link URL - - id: timelineStoryBlock - name: Timeline Block - type: storyBlock - description: Storytelling Timeline Block - schema: - groups: - - id: panel - title: Panel Setting - fields: - - id: padding - type: spacing - title: Padding - ui: padding - min: 0 - max: 100 - - id: default - title: Timeline Setting - fields: - - id: timelineSetting - type: timeline - ui: datetime - title: Timeline Setting - - id: playMode - type: string - title: Play Mode - description: Specify play mode. - defaultValue: once - choices: - - key: once - label: Once - - key: loop - label: Loop - id: showLayersStoryBlock - name: Show Layers + name: Show Layers Button type: storyBlock description: Storytelling Show Layers schema: @@ -2568,4 +2536,36 @@ extensions: type: array title: Show Layers ui: layer - choices: \ No newline at end of file + choices: + - id: timelineStoryBlock + name: Timeline + type: storyBlock + description: Storytelling Timeline Block + schema: + groups: + - id: panel + title: Panel Setting + fields: + - id: padding + type: spacing + title: Padding + ui: padding + min: 0 + max: 100 + - id: default + title: Timeline Setting + fields: + - id: timelineSetting + type: timeline + ui: datetime + title: Timeline Setting + - id: playMode + type: string + title: Play Mode + description: Specify play mode. + defaultValue: once + choices: + - key: once + label: Once + - key: loop + label: Loop \ No newline at end of file diff --git a/web/src/beta/components/Icon/Icons/timelineStoryBlock.svg b/web/src/beta/components/Icon/Icons/timelineStoryBlock.svg index e3d0fc806f..8b64acebce 100644 --- a/web/src/beta/components/Icon/Icons/timelineStoryBlock.svg +++ b/web/src/beta/components/Icon/Icons/timelineStoryBlock.svg @@ -1,3 +1,4 @@ - - + + + diff --git a/web/src/beta/components/Icon/Icons/timelineStoryBlockSolid.svg b/web/src/beta/components/Icon/Icons/timelineStoryBlockSolid.svg new file mode 100644 index 0000000000..e3d0fc806f --- /dev/null +++ b/web/src/beta/components/Icon/Icons/timelineStoryBlockSolid.svg @@ -0,0 +1,3 @@ + + + diff --git a/web/src/beta/components/Icon/icons.ts b/web/src/beta/components/Icon/icons.ts index 2cf763d03d..debb6b8320 100644 --- a/web/src/beta/components/Icon/icons.ts +++ b/web/src/beta/components/Icon/icons.ts @@ -93,6 +93,7 @@ import MdTextStoryBlock from "./Icons/mdTextStoryBlock.svg"; import CameraButtonStoryBlock from "./Icons/cameraButtonStoryBlock.svg"; import ShowLayersStoryBlock from "./Icons/showLayersStoryBlock.svg"; import TimelineStoryBlock from "./Icons/timelineStoryBlock.svg"; +import TimelineStoryBlockSolid from "./Icons/timelineStoryBlockSolid.svg"; // Widget tab import Desktop from "./Icons/desktop.svg"; @@ -192,6 +193,7 @@ export default { cameraButtonStoryBlock: CameraButtonStoryBlock, showLayersStoryBlock: ShowLayersStoryBlock, timelineStoryBlock: TimelineStoryBlock, + timelineStoryBlockSolid: TimelineStoryBlockSolid, widget: Widgets, widgets: Widgets, menu: WidgetMenu, diff --git a/web/src/beta/components/SidePanelSectionField/index.tsx b/web/src/beta/components/SidePanelSectionField/index.tsx index d0e99e56d0..06f57c61cf 100644 --- a/web/src/beta/components/SidePanelSectionField/index.tsx +++ b/web/src/beta/components/SidePanelSectionField/index.tsx @@ -56,7 +56,7 @@ const Content = styled.div` padding: 8px; display: flex; flex-direction: column; - gap: 4px; + gap: 16px; `; export default SidePanelSectionField; diff --git a/web/src/beta/features/Editor/SidePanel/index.tsx b/web/src/beta/features/Editor/SidePanel/index.tsx index e2372ba4fd..afb1bc5e01 100644 --- a/web/src/beta/features/Editor/SidePanel/index.tsx +++ b/web/src/beta/features/Editor/SidePanel/index.tsx @@ -72,7 +72,7 @@ const ActionArea = styled.div` `; const Content = styled.div<{ hasActions?: boolean }>` - padding: ${({ hasActions }) => (hasActions ? "0" : "8px 4px")}; + padding: ${({ hasActions }) => (hasActions ? "0" : "8px")}; border-bottom-right-radius: 4px; border-bottom-left-radius: 4px; overflow-y: auto; 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 e23efa0be0..14d9ca4dab 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 @@ -55,7 +55,7 @@ const GroupSectionField: React.FC = ({ ), )} - + = ({ const handleZoomToLayer = () => { if (selectedLayerId) { - onFlyTo?.(selectedLayerId); + onFlyTo?.(selectedLayerId, { duration: 0 }); } }; diff --git a/web/src/beta/features/Editor/tabs/story/LeftPanel/Action/index.tsx b/web/src/beta/features/Editor/tabs/story/LeftPanel/Action/index.tsx index 46ed239fab..57cb47f18b 100644 --- a/web/src/beta/features/Editor/tabs/story/LeftPanel/Action/index.tsx +++ b/web/src/beta/features/Editor/tabs/story/LeftPanel/Action/index.tsx @@ -39,10 +39,11 @@ const Wrapper = styled.button` min-height: 28px; transition: all 0.15s; - border: 1px solid ${({ theme }) => theme.outline.weakest}; + background: ${props => props.theme.bg[1]}; + border: 1px solid ${({ theme }) => theme.outline.weaker}; :hover { - background: ${props => props.theme.bg[3]}; + background: ${props => props.theme.bg[2]}; } user-select: none; cursor: pointer; diff --git a/web/src/beta/lib/core/StoryPanel/Block/builtin/Layer/Content.tsx b/web/src/beta/lib/core/StoryPanel/Block/builtin/Layer/Content.tsx new file mode 100644 index 0000000000..844aaa37b3 --- /dev/null +++ b/web/src/beta/lib/core/StoryPanel/Block/builtin/Layer/Content.tsx @@ -0,0 +1,134 @@ +import { useCallback, useContext, useState } from "react"; + +import Button from "@reearth/beta/components/Button"; +import { useVisualizer } from "@reearth/beta/lib/core/Visualizer"; +import { useT } from "@reearth/services/i18n"; +import { styled } from "@reearth/services/theme"; + +import { BlockContext } from "../common/Wrapper"; + +import LayerEditor, { type LayerBlock as LayerBlockType } from "./Editor"; + +type Props = { + propertyId?: string; + layerButtons: LayerBlockType[]; + isEditable?: boolean; + onPropertyUpdate?: ( + propertyId?: string, + schemaItemId?: string, + fieldId?: string, + itemId?: string, + vt?: any, + v?: any, + ) => Promise; + onPropertyItemAdd?: (propertyId?: string, schemaGroupId?: string) => Promise; + onPropertyItemMove?: ( + propertyId?: string, + schemaGroupId?: string, + itemId?: string, + index?: number, + ) => Promise; + onPropertyItemDelete?: ( + propertyId?: string, + schemaGroupId?: string, + itemId?: string, + ) => Promise; +}; + +const Content: React.FC = ({ + propertyId, + layerButtons, + isEditable, + onPropertyUpdate, + onPropertyItemAdd, + onPropertyItemDelete, + onPropertyItemMove, +}) => { + const t = useT(); + const context = useContext(BlockContext); + const visualizer = useVisualizer(); + const [selected, setSelected] = useState(layerButtons[0]?.id); + + const handleClick = useCallback( + (itemId: string) => { + if (isEditable) { + setSelected(itemId); + return; + } + const item = layerButtons.find(i => i.id === itemId); + + if (!item?.showLayers?.value) return; + + // Hide all layers + const layers = visualizer.current?.layers; + + // Show only selected layers + layers?.show(...item.showLayers.value); + const allLayers = layers?.layers() ?? []; + + // Hide the rest + layers?.hide( + ...allLayers.map(({ id }) => id).filter(id => !item.showLayers?.value?.includes(id)), + ); + }, + [isEditable, visualizer, layerButtons], + ); + + return ( + + + {layerButtons.map(({ title, color, bgColor, id }) => { + return ( + handleClick(id)} + /> + ); + })} + + {context?.editMode && ( + + )} + + ); +}; + +export default Content; + +const Wrapper = styled.div` + width: 100%; +`; + +const ButtonWrapper = styled.div` + width: 100%; + display: flex; + gap: 4px; + max-width: 400px; + flex-wrap: wrap; +`; + +const StyledButton = styled(Button)<{ color?: string; bgColor?: string }>` + color: ${({ color }) => color}; + background-color: ${({ bgColor }) => bgColor}; + border-color: ${({ color }) => color}; + + &:hover { + color: ${({ bgColor }) => bgColor}; + background-color: ${({ color }) => color}; + } +`; diff --git a/web/src/beta/lib/core/StoryPanel/Block/builtin/Layer/Editor.tsx b/web/src/beta/lib/core/StoryPanel/Block/builtin/Layer/Editor.tsx deleted file mode 100644 index 5099062a82..0000000000 --- a/web/src/beta/lib/core/StoryPanel/Block/builtin/Layer/Editor.tsx +++ /dev/null @@ -1,210 +0,0 @@ -import { debounce } from "lodash-es"; -import { useContext, useMemo, useState, useCallback, useEffect } from "react"; - -import Button from "@reearth/beta/components/Button"; -import ColorField from "@reearth/beta/components/fields/ColorField"; -import ListField from "@reearth/beta/components/fields/ListField"; -import SelectField from "@reearth/beta/components/fields/SelectField"; -import TextField from "@reearth/beta/components/fields/TextField"; -import { useVisualizer } from "@reearth/beta/lib/core/Visualizer/context"; -import { useT } from "@reearth/services/i18n"; -import { styled } from "@reearth/services/theme"; - -import type { Field } from "../../../types"; -import { BlockContext } from "../common/Wrapper"; - -export type LayerBlock = { - id: string; - title?: Field; - color?: Field; - bgColor?: Field; - showLayers?: Field; -}; - -export type Props = { - items: LayerBlock[]; - inEditor: boolean; - onUpdate: ( - id: string, - fieldId: keyof LayerBlock, - fieldType: "string" | "array", - value: string | string[] | undefined, - ) => void; - onItemRemove: (id: string) => void; - onItemAdd: () => void; - onItemMove: ({ id }: { id: string }, index: number) => void; -}; - -const LayerBlockEditor: React.FC = ({ - items, - inEditor, - onUpdate, - onItemRemove, - onItemAdd, - onItemMove, -}) => { - const t = useT(); - const context = useContext(BlockContext); - const [selected, setSelected] = useState(items[0]?.id); - - const visualizer = useVisualizer(); - - const layers = useMemo( - () => - visualizer.current?.layers?.layers()?.map(({ id, title }) => ({ - key: id, - label: title ?? `Layer: ${id}`, - })), - [visualizer], - ); - - const defaultTitle = useMemo(() => t("New Layer Button"), [t]); - - const editorProperties = useMemo(() => items.find(i => i.id === selected), [items, selected]); - - const handleClick = useCallback( - (itemId: string) => { - if (inEditor) { - setSelected(itemId); - return; - } - const item = items.find(i => i.id === itemId); - - if (!item?.showLayers?.value) return; - - // Hide all layers - const layers = visualizer.current?.layers; - - // Show only selected layers - layers?.show(...item.showLayers.value); - const allLayers = layers?.layers() ?? []; - - // Hide the rest - layers?.hide( - ...allLayers.map(({ id }) => id).filter(id => !item.showLayers?.value?.includes(id)), - ); - }, - [inEditor, visualizer, items], - ); - - const debounceOnUpdate = useMemo(() => debounce(onUpdate, 500), [onUpdate]); - - const listItems = useMemo( - () => items.map(({ id, title }) => ({ id, value: title?.value ?? defaultTitle })), - [items, defaultTitle], - ); - - useEffect(() => { - const currentVisualizer = visualizer?.current; - return () => { - const allLayers = currentVisualizer?.layers?.layers(); - if (!Array.isArray(allLayers)) return; - // Show all the layers after the component is unmounted - currentVisualizer?.layers.show(...allLayers.map(({ id }) => id)); - }; - }, [visualizer]); - - return ( - - - {items.map(({ title, color, bgColor, id }) => { - return ( - handleClick(id)} - /> - ); - })} - - {context?.editMode && ( - - - - debounceOnUpdate(selected, "title", "string", value)} - /> - debounceOnUpdate(selected, "color", "string", value)} - /> - debounceOnUpdate(selected, "bgColor", "string", value)} - /> - debounceOnUpdate(selected, "showLayers", "array", value)} - multiSelect - /> - - - )} - - ); -}; - -const Wrapper = styled.div` - width: 100%; -`; - -const ButtonWrapper = styled.div` - width: 100%; - display: flex; - gap: 4px; - max-width: 400px; - flex-wrap: wrap; -`; - -const StyledButton = styled(Button)<{ color?: string; bgColor?: string }>` - color: ${({ color }) => color}; - background-color: ${({ bgColor }) => bgColor}; - border-color: ${({ color }) => color}; - - &:hover { - color: ${({ bgColor }) => bgColor}; - background-color: ${({ color }) => color}; - } -`; - -const EditorWrapper = styled.div` - padding: 12px; - margin: 2px 0; - background: ${({ theme }) => theme.bg[1]}; -`; - -const FieldGroup = styled.div<{ disabled: boolean }>` - display: flex; - flex-direction: column; - gap: 8px; - margin-top: 10px; - opacity: ${({ disabled }) => (disabled ? 0.6 : 1)}; - cursor: ${({ disabled }) => (disabled ? "not-allowed" : "inherit")}; - pointer-events: ${({ disabled }) => (disabled ? "none" : "inherit")}; -`; - -export default LayerBlockEditor; diff --git a/web/src/beta/lib/core/StoryPanel/Block/builtin/Layer/Editor/hooks.ts b/web/src/beta/lib/core/StoryPanel/Block/builtin/Layer/Editor/hooks.ts new file mode 100644 index 0000000000..1faff4fa5b --- /dev/null +++ b/web/src/beta/lib/core/StoryPanel/Block/builtin/Layer/Editor/hooks.ts @@ -0,0 +1,120 @@ +import { debounce } from "lodash-es"; +import { useCallback, useMemo } from "react"; + +import { useVisualizer } from "@reearth/beta/lib/core/Visualizer"; +import { useT } from "@reearth/services/i18n"; + +import type { Field } from "../../../../types"; + +export type LayerBlock = { + id: string; + title?: Field; + color?: Field; + bgColor?: Field; + showLayers?: Field; +}; +export default ({ + items, + propertyId, + selected, + onPropertyUpdate, + onPropertyItemAdd, + onPropertyItemDelete, + onPropertyItemMove, +}: { + items: LayerBlock[]; + propertyId?: string; + selected?: string; + onPropertyUpdate?: ( + propertyId?: string, + schemaItemId?: string, + fieldId?: string, + itemId?: string, + vt?: any, + v?: any, + ) => Promise; + onPropertyItemAdd?: (propertyId?: string, schemaGroupId?: string) => Promise; + onPropertyItemMove?: ( + propertyId?: string, + schemaGroupId?: string, + itemId?: string, + index?: number, + ) => Promise; + onPropertyItemDelete?: ( + propertyId?: string, + schemaGroupId?: string, + itemId?: string, + ) => Promise; +}) => { + const visualizer = useVisualizer(); + const t = useT(); + + const layers = useMemo( + () => + visualizer.current?.layers?.layers()?.map(({ id, title }) => ({ + key: id, + label: title ?? `Layer: ${id}`, + })), + [visualizer], + ); + + const editorProperties = useMemo(() => items.find(i => i.id === selected), [items, selected]); + + const handlePropertyValueUpdate = useCallback( + (schemaGroupId: string, propertyId: string, fieldId: string, vt: any, itemId?: string) => { + return async (v?: any) => { + await onPropertyUpdate?.(propertyId, schemaGroupId, fieldId, itemId, vt, v); + }; + }, + [onPropertyUpdate], + ); + + const handleUpdate = useCallback( + (itemId: string, fieldId: string, fieldType: any, updatedValue: any) => { + if (!propertyId || !itemId) return; + + handlePropertyValueUpdate("default", propertyId, fieldId, fieldType, itemId)(updatedValue); + }, + [propertyId, handlePropertyValueUpdate], + ); + + const debounceOnUpdate = useMemo(() => debounce(handleUpdate, 500), [handleUpdate]); + + const listItems = useMemo( + () => items.map(({ id, title }) => ({ id, value: title?.value ?? t("New Layers Button") })), + [items, t], + ); + + const handleItemAdd = useCallback(() => { + if (!propertyId) return; + onPropertyItemAdd?.(propertyId, "default"); + }, [propertyId, onPropertyItemAdd]); + + const handleItemRemove = useCallback( + (itemId: string) => { + if (!propertyId || !itemId) return; + + onPropertyItemDelete?.(propertyId, "default", itemId); + }, + [propertyId, onPropertyItemDelete], + ); + + const handleItemMove = useCallback( + ({ id }: { id: string }, index: number) => { + if (!propertyId || !id) return; + + onPropertyItemMove?.(propertyId, "default", id, index); + }, + [propertyId, onPropertyItemMove], + ); + + return { + layers, + editorProperties, + debounceOnUpdate, + listItems, + handleItemAdd, + handleItemRemove, + handleItemMove, + }; +}; diff --git a/web/src/beta/lib/core/StoryPanel/Block/builtin/Layer/Editor/index.tsx b/web/src/beta/lib/core/StoryPanel/Block/builtin/Layer/Editor/index.tsx new file mode 100644 index 0000000000..0a13edfff6 --- /dev/null +++ b/web/src/beta/lib/core/StoryPanel/Block/builtin/Layer/Editor/index.tsx @@ -0,0 +1,128 @@ +import ColorField from "@reearth/beta/components/fields/ColorField"; +import ListField from "@reearth/beta/components/fields/ListField"; +import SelectField from "@reearth/beta/components/fields/SelectField"; +import TextField from "@reearth/beta/components/fields/TextField"; +import { useT } from "@reearth/services/i18n"; +import { styled } from "@reearth/services/theme"; + +import useHooks, { type LayerBlock } from "./hooks"; + +export { type LayerBlock } from "./hooks"; + +export type Props = { + items: LayerBlock[]; + selected: string; + propertyId?: string; + setSelected: (id: string) => void; + onPropertyUpdate?: ( + propertyId?: string, + schemaItemId?: string, + fieldId?: string, + itemId?: string, + vt?: any, + v?: any, + ) => Promise; + onPropertyItemAdd?: (propertyId?: string, schemaGroupId?: string) => Promise; + onPropertyItemMove?: ( + propertyId?: string, + schemaGroupId?: string, + itemId?: string, + index?: number, + ) => Promise; + onPropertyItemDelete?: ( + propertyId?: string, + schemaGroupId?: string, + itemId?: string, + ) => Promise; +}; + +const CameraBlockEditor: React.FC = ({ + items, + propertyId, + selected, + setSelected, + onPropertyUpdate, + onPropertyItemAdd, + onPropertyItemDelete, + onPropertyItemMove, +}) => { + const t = useT(); + const { + layers, + editorProperties, + debounceOnUpdate, + listItems, + handleItemAdd, + handleItemRemove, + handleItemMove, + } = useHooks({ + selected, + items, + propertyId, + onPropertyUpdate, + onPropertyItemAdd, + onPropertyItemDelete, + onPropertyItemMove, + }); + + return ( + + + + debounceOnUpdate(selected, "title", "string", value)} + /> + debounceOnUpdate(selected, "color", "string", value)} + /> + debounceOnUpdate(selected, "bgColor", "string", value)} + /> + debounceOnUpdate(selected, "showLayers", "array", value)} + multiSelect + /> + + + ); +}; + +const EditorWrapper = styled.div` + padding: 12px; + margin: 2px 0; + background: ${({ theme }) => theme.bg[1]}; +`; + +const FieldGroup = styled.div<{ disabled: boolean }>` + display: flex; + flex-direction: column; + gap: 8px; + margin-top: 10px; + opacity: ${({ disabled }) => (disabled ? 0.6 : 1)}; + cursor: ${({ disabled }) => (disabled ? "not-allowed" : "inherit")}; + pointer-events: ${({ disabled }) => (disabled ? "none" : "inherit")}; +`; + +export default CameraBlockEditor; diff --git a/web/src/beta/lib/core/StoryPanel/Block/builtin/Layer/index.tsx b/web/src/beta/lib/core/StoryPanel/Block/builtin/Layer/index.tsx index d33f443125..dd2e048031 100644 --- a/web/src/beta/lib/core/StoryPanel/Block/builtin/Layer/index.tsx +++ b/web/src/beta/lib/core/StoryPanel/Block/builtin/Layer/index.tsx @@ -1,82 +1,26 @@ -import { useCallback, useMemo, useEffect } from "react"; - -import { type ValueTypes } from "@reearth/beta/utils/value"; +import { useMemo, useEffect } from "react"; import type { CommonProps as BlockProps } from "../../types"; import BlockWrapper from "../common/Wrapper"; -import LayerBlockEditor, { type LayerBlock as LayerBlockType } from "./Editor"; +import Content from "./Content"; +import { type LayerBlock as LayerBlockType } from "./Editor"; export type Props = BlockProps; -const LayerBlock: React.FC = ({ block, isSelected, ...props }) => { - const showLayerButtons = useMemo( +const LayerBlock: React.FC = ({ block, isSelected, onPropertyItemAdd, ...props }) => { + const layerButtons = useMemo( () => (block?.property?.default ?? []) as LayerBlockType[], [block?.property?.default], ); - const handlePropertyValueUpdate = useCallback( - (schemaGroupId: string, propertyId: string, fieldId: string, vt: any, itemId?: string) => { - return async (v?: any) => { - await props.onPropertyUpdate?.(propertyId, schemaGroupId, fieldId, itemId, vt, v); - }; - }, - [props], - ); - - const handleUpdate = useCallback( - ( - itemId: string, - fieldId: string, - fieldType: keyof ValueTypes, - updatedValue: ValueTypes[keyof ValueTypes] | undefined, - ) => { - if (!block?.propertyId || !itemId) return; - - console.log(itemId, fieldId, fieldType, updatedValue); - - handlePropertyValueUpdate( - "default", - block?.propertyId, - fieldId, - fieldType, - itemId, - )(updatedValue); - }, - [block?.propertyId, handlePropertyValueUpdate], - ); - - const handleItemAdd = useCallback(() => { - if (!block?.propertyId) return; - props.onPropertyItemAdd?.(block.propertyId, "default"); - }, [block?.propertyId, props]); - - const handleItemRemove = useCallback( - (itemId: string) => { - if (!block?.propertyId || !itemId) return; - - props.onPropertyItemDelete?.(block.propertyId, "default", itemId); - }, - [block?.propertyId, props], - ); - - const handleItemMove = useCallback( - ({ id }: { id: string }, index: number) => { - if (!block?.propertyId || !id) return; - - props.onPropertyItemMove?.(block.propertyId, "default", id, index); - }, - [block?.propertyId, props], - ); - // if there's no item add 1 button. // TODO: Should be added to block creationAPI for generic blocks that require at least 1 item useEffect(() => { - if (!showLayerButtons || showLayerButtons.length === 0) { - handleItemAdd(); - return; + if (!block?.property?.default || block?.property?.default.length === 0) { + onPropertyItemAdd?.(block?.propertyId, "default"); } - }, [showLayerButtons, handleItemAdd, handleUpdate]); + }, [block?.propertyId, block?.property?.default, onPropertyItemAdd]); return ( = ({ block, isSelected, ...props }) => { propertyId={block?.propertyId} property={block?.property} settingsEnabled={false} + onPropertyItemAdd={onPropertyItemAdd} {...props}> - ); diff --git a/web/src/beta/lib/core/StoryPanel/Block/builtin/Timeline/Editor.tsx b/web/src/beta/lib/core/StoryPanel/Block/builtin/Timeline/Editor.tsx index bfdaadd507..0cc5df854f 100644 --- a/web/src/beta/lib/core/StoryPanel/Block/builtin/Timeline/Editor.tsx +++ b/web/src/beta/lib/core/StoryPanel/Block/builtin/Timeline/Editor.tsx @@ -81,7 +81,7 @@ const TimelineEditor = ({ - +