From 522cb3bbefb4f261f1090b291eebd156f02617ce Mon Sep 17 00:00:00 2001 From: Braulio Date: Tue, 30 Jul 2024 13:47:17 +0200 Subject: [PATCH 1/2] initial plumbing --- src/core/providers/canvas/canvas.model.ts | 3 ++ src/core/providers/canvas/canvas.provider.tsx | 3 +- .../providers/canvas/use-selection.hook.ts | 15 ++++++- .../providers}/canvas/zindex.util.ts | 29 ++++++++++++-- src/pods/canvas/canvas.pod.tsx | 40 ------------------- src/pods/properties/index.ts | 1 + src/pods/properties/properties.pod.tsx | 21 ++++++++++ src/scenes/main.scene.tsx | 18 ++++++--- 8 files changed, 77 insertions(+), 53 deletions(-) rename src/{pods => core/providers}/canvas/zindex.util.ts (74%) create mode 100644 src/pods/properties/index.ts create mode 100644 src/pods/properties/properties.pod.tsx diff --git a/src/core/providers/canvas/canvas.model.ts b/src/core/providers/canvas/canvas.model.ts index 5400eddf..79e036f5 100644 --- a/src/core/providers/canvas/canvas.model.ts +++ b/src/core/providers/canvas/canvas.model.ts @@ -2,6 +2,8 @@ import { ShapeModel, ShapeRefs, ShapeType } from '@/core/model'; import Konva from 'konva'; import { Node, NodeConfig } from 'konva/lib/Node'; +export type ZIndexAction = 'top' | 'bottom' | 'up' | 'down'; + export interface SelectionInfo { transformerRef: React.RefObject; shapeRefs: React.MutableRefObject; @@ -14,6 +16,7 @@ export interface SelectionInfo { selectedShapeRef: React.MutableRefObject | null>; selectedShapeId: string; selectedShapeType: ShapeType | null; + setZIndexOnSelected: (action: ZIndexAction) => void; } export interface CanvasContextModel { diff --git a/src/core/providers/canvas/canvas.provider.tsx b/src/core/providers/canvas/canvas.provider.tsx index c1071658..854785dc 100644 --- a/src/core/providers/canvas/canvas.provider.tsx +++ b/src/core/providers/canvas/canvas.provider.tsx @@ -2,6 +2,7 @@ import React from 'react'; import { ShapeModel } from '@/core/model'; import { CanvasContext } from './canvas.context'; import { useSelection } from './use-selection.hook'; +import { ZIndexAction } from './canvas.model'; interface Props { children: React.ReactNode; @@ -12,7 +13,7 @@ export const CanvasProvider: React.FC = props => { const [shapes, setShapes] = React.useState([]); const [scale, setScale] = React.useState(1); - const selectionInfo = useSelection(shapes); + const selectionInfo = useSelection(shapes, setShapes); return ( { +export const useSelection = ( + shapes: ShapeModel[], + setShapes: React.Dispatch> +): SelectionInfo => { const transformerRef = useRef(null); const shapeRefs = useRef({}); const selectedShapeRef = useRef(null); @@ -41,6 +45,12 @@ export const useSelection = (shapes: ShapeModel[]): SelectionInfo => { } }; + const setZIndexOnSelected = (action: ZIndexAction) => { + setShapes(prevShapes => + performZIndexAction(selectedShapeId, action, prevShapes) + ); + }; + return { transformerRef, shapeRefs, @@ -49,5 +59,6 @@ export const useSelection = (shapes: ShapeModel[]): SelectionInfo => { selectedShapeRef, selectedShapeId, selectedShapeType, + setZIndexOnSelected, }; }; diff --git a/src/pods/canvas/zindex.util.ts b/src/core/providers/canvas/zindex.util.ts similarity index 74% rename from src/pods/canvas/zindex.util.ts rename to src/core/providers/canvas/zindex.util.ts index 7f378c5d..46b51993 100644 --- a/src/pods/canvas/zindex.util.ts +++ b/src/core/providers/canvas/zindex.util.ts @@ -1,7 +1,8 @@ import { ShapeModel } from '@/core/model'; +import { ZIndexAction } from './canvas.model'; // TOO Add Unit tests to all these methods: #65 -export const moveZIndexToTop = ( +const moveZIndexToTop = ( selectedShapeId: string, shapeCollection: ShapeModel[] ): ShapeModel[] => { @@ -16,7 +17,7 @@ export const moveZIndexToTop = ( : shapeCollection; }; -export const moveZIndexToBottom = ( +const moveZIndexToBottom = ( selectedShapeId: string, shapeCollection: ShapeModel[] ): ShapeModel[] => { @@ -31,7 +32,7 @@ export const moveZIndexToBottom = ( : shapeCollection; }; -export const moveZIndexDownOneLevel = ( +const moveZIndexDownOneLevel = ( selectedShapeId: string, shapeCollection: ShapeModel[] ): ShapeModel[] => { @@ -54,7 +55,7 @@ export const moveZIndexDownOneLevel = ( : shapeCollection; }; -export const moveZIndexTopOneLevel = ( +const moveZIndexTopOneLevel = ( selectedShapeId: string, shapeCollection: ShapeModel[] ): ShapeModel[] => { @@ -76,3 +77,23 @@ export const moveZIndexTopOneLevel = ( ] : shapeCollection; }; + +export const performZIndexAction = ( + selectedShapeId: string, + action: ZIndexAction, + shapes: ShapeModel[] +): ShapeModel[] => { + switch (action) { + case 'top': + return moveZIndexToTop(selectedShapeId, shapes); + break; + case 'bottom': + return moveZIndexToBottom(selectedShapeId, shapes); + break; + case 'up': + return moveZIndexTopOneLevel(selectedShapeId, shapes); + break; + case 'down': + return moveZIndexDownOneLevel(selectedShapeId, shapes); + } +}; diff --git a/src/pods/canvas/canvas.pod.tsx b/src/pods/canvas/canvas.pod.tsx index a0af0265..05eedee1 100644 --- a/src/pods/canvas/canvas.pod.tsx +++ b/src/pods/canvas/canvas.pod.tsx @@ -7,12 +7,6 @@ import { renderShapeComponent } from './shape-renderer'; import { useDropShape } from './use-drop-shape.hook'; import { useMonitorShape } from './use-monitor-shape.hook'; import classes from './canvas.pod.module.css'; -import { - moveZIndexDownOneLevel, - moveZIndexToBottom, - moveZIndexTopOneLevel, - moveZIndexToTop, -} from './zindex.util'; import { ShapeModel } from '@/core/model'; export const CanvasPod = () => { @@ -48,10 +42,6 @@ export const CanvasPod = () => { ); }; - const handleZIndexChange = (shapeCollection: ShapeModel[]) => { - setShapes(shapeCollection); - }; - { /* TODO: add other animation for isDraggerOver */ } @@ -61,36 +51,6 @@ export const CanvasPod = () => { ref={dropRef} style={{ opacity: isDraggedOver ? 0.5 : 1 }} > - {/*TODO: move buttons to app props panel*/} - - - - - {/*TODO: move size to canvas provider?*/} { + const { selectionInfo } = useCanvasContext(); + + const selectedShapeID = selectionInfo?.selectedShapeRef.current ?? null; + + const handleZIndexTop = () => { + selectionInfo?.setZIndexOnSelected('top'); + }; + + if (!selectedShapeID) { + return null; + } + + return ( +
+ +
+ ); +}; diff --git a/src/scenes/main.scene.tsx b/src/scenes/main.scene.tsx index 722ea371..04dae8fb 100644 --- a/src/scenes/main.scene.tsx +++ b/src/scenes/main.scene.tsx @@ -1,19 +1,25 @@ -import { MainLayout } from "@/layout/main.layout"; -import classes from "./main.module.css"; +import { MainLayout } from '@/layout/main.layout'; +import classes from './main.module.css'; import { CanvasPod, ToolbarPod, ContainerGalleryPod, ComponentGalleryPod, -} from "@/pods"; +} from '@/pods'; +import { PropertiesPod } from '@/pods/properties'; export const MainScene = () => { return ( - + -
-
+
+ + +
+
+ +
); From 7387052d4a74f7bd0ca14e1687b8309eff7d838a Mon Sep 17 00:00:00 2001 From: oleojake Date: Wed, 31 Jul 2024 10:06:53 +0200 Subject: [PATCH 2/2] #66-zindex-implemented-in-properties-toolbar --- .../icons/bring-forward-icon.component.tsx | 28 +++++++++++ .../icons/bring-to-front-icon.component.tsx | 20 ++++++++ src/common/components/icons/index.ts | 4 ++ .../icons/send-backward-icon.component.tsx | 25 ++++++++++ .../icons/send-to-back-icon.component.tsx | 20 ++++++++ .../zindex/zindex-button.component.tsx | 15 ++++++ .../zindex/zindex-option.component.tsx | 49 +++++++++++++++++++ .../zindex/zindex-option.module.css | 31 ++++++++++++ src/pods/properties/properties.pod.module.css | 5 ++ src/pods/properties/properties.pod.tsx | 11 +++-- 10 files changed, 203 insertions(+), 5 deletions(-) create mode 100644 src/common/components/icons/bring-forward-icon.component.tsx create mode 100644 src/common/components/icons/bring-to-front-icon.component.tsx create mode 100644 src/common/components/icons/send-backward-icon.component.tsx create mode 100644 src/common/components/icons/send-to-back-icon.component.tsx create mode 100644 src/pods/properties/components/zindex/zindex-button.component.tsx create mode 100644 src/pods/properties/components/zindex/zindex-option.component.tsx create mode 100644 src/pods/properties/components/zindex/zindex-option.module.css create mode 100644 src/pods/properties/properties.pod.module.css diff --git a/src/common/components/icons/bring-forward-icon.component.tsx b/src/common/components/icons/bring-forward-icon.component.tsx new file mode 100644 index 00000000..4b6f954a --- /dev/null +++ b/src/common/components/icons/bring-forward-icon.component.tsx @@ -0,0 +1,28 @@ +export const BringForwardIcon = () => { + return ( + + + + + + + + + + + + + ); +}; diff --git a/src/common/components/icons/bring-to-front-icon.component.tsx b/src/common/components/icons/bring-to-front-icon.component.tsx new file mode 100644 index 00000000..bbda6c0b --- /dev/null +++ b/src/common/components/icons/bring-to-front-icon.component.tsx @@ -0,0 +1,20 @@ +export const BringToFrontIcon = () => { + return ( + + + + + ); +}; diff --git a/src/common/components/icons/index.ts b/src/common/components/icons/index.ts index 1cf65ea1..8c8711cd 100644 --- a/src/common/components/icons/index.ts +++ b/src/common/components/icons/index.ts @@ -1 +1,5 @@ export * from './about-icon.component'; +export * from './bring-forward-icon.component'; +export * from './bring-to-front-icon.component'; +export * from './send-backward-icon.component'; +export * from './send-to-back-icon.component'; diff --git a/src/common/components/icons/send-backward-icon.component.tsx b/src/common/components/icons/send-backward-icon.component.tsx new file mode 100644 index 00000000..ee4fcfb6 --- /dev/null +++ b/src/common/components/icons/send-backward-icon.component.tsx @@ -0,0 +1,25 @@ +export const SendBackwardIcon = () => { + return ( + + + + + + + + + + ); +}; diff --git a/src/common/components/icons/send-to-back-icon.component.tsx b/src/common/components/icons/send-to-back-icon.component.tsx new file mode 100644 index 00000000..3217c1fa --- /dev/null +++ b/src/common/components/icons/send-to-back-icon.component.tsx @@ -0,0 +1,20 @@ +export const SendToBackIcon = () => { + return ( + + + + + ); +}; diff --git a/src/pods/properties/components/zindex/zindex-button.component.tsx b/src/pods/properties/components/zindex/zindex-button.component.tsx new file mode 100644 index 00000000..5ae0770c --- /dev/null +++ b/src/pods/properties/components/zindex/zindex-button.component.tsx @@ -0,0 +1,15 @@ +import classes from './zindex-option.module.css'; + +interface ButtonProps { + onClick: () => void; + Icon: React.ComponentType; +} + +export const ZIndexButton: React.FC = props => { + const { onClick, Icon } = props; + return ( + + ); +}; diff --git a/src/pods/properties/components/zindex/zindex-option.component.tsx b/src/pods/properties/components/zindex/zindex-option.component.tsx new file mode 100644 index 00000000..186bb2a2 --- /dev/null +++ b/src/pods/properties/components/zindex/zindex-option.component.tsx @@ -0,0 +1,49 @@ +import { + SelectionInfo, + ZIndexAction, +} from '@/core/providers/canvas/canvas.model'; +import classes from './zindex-option.module.css'; +import { + BringForwardIcon, + BringToFrontIcon, + SendBackwardIcon, + SendToBackIcon, +} from '@/common/components/icons'; +import { ZIndexButton } from './zindex-button.component'; + +interface LayerOption { + position: ZIndexAction; + Icon: React.ComponentType; +} + +const layersOptions: LayerOption[] = [ + { position: 'top', Icon: BringToFrontIcon }, + { position: 'up', Icon: BringForwardIcon }, + { position: 'down', Icon: SendBackwardIcon }, + { position: 'bottom', Icon: SendToBackIcon }, +]; + +interface Props { + selectionInfo: SelectionInfo; +} + +export const ZIndexOptions: React.FC = props => { + const { selectionInfo } = props; + + const handleZIndexChange = (position: ZIndexAction) => { + selectionInfo?.setZIndexOnSelected(position); + }; + + return ( +
+

Layering

+ {layersOptions.map(({ position, Icon }) => ( + handleZIndexChange(position)} + Icon={Icon} + /> + ))} +
+ ); +}; diff --git a/src/pods/properties/components/zindex/zindex-option.module.css b/src/pods/properties/components/zindex/zindex-option.module.css new file mode 100644 index 00000000..b41220db --- /dev/null +++ b/src/pods/properties/components/zindex/zindex-option.module.css @@ -0,0 +1,31 @@ +.container { + display: flex; + gap: 0.5em; + align-items: center; + padding: var(--space-xs) var(--space-md); + border-bottom: 1px solid var(--primary-300); +} + +.container :first-child { + flex: 1; +} + +.button { + border: none; + color: var(--text-color); + background-color: inherit; + padding: var(--space-xs); + border-radius: var(--border-radius-s); + font-size: var(--fs-xs); + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: var(--space-s); + transition: all 0.3s ease-in-out; + cursor: pointer; +} + +.button:hover { + background-color: var(--primary-100); +} diff --git a/src/pods/properties/properties.pod.module.css b/src/pods/properties/properties.pod.module.css new file mode 100644 index 00000000..ed158c52 --- /dev/null +++ b/src/pods/properties/properties.pod.module.css @@ -0,0 +1,5 @@ +.title { + background-color: var(--primary-200); + padding: var(--space-xs) var(--space-md); + border-bottom: 1px solid var(--primary-900); +} diff --git a/src/pods/properties/properties.pod.tsx b/src/pods/properties/properties.pod.tsx index e193d774..162402b9 100644 --- a/src/pods/properties/properties.pod.tsx +++ b/src/pods/properties/properties.pod.tsx @@ -1,21 +1,22 @@ import { useCanvasContext } from '@/core/providers'; +import classes from './properties.pod.module.css'; +import { ZIndexOptions } from './components/zindex/zindex-option.component'; export const PropertiesPod = () => { const { selectionInfo } = useCanvasContext(); const selectedShapeID = selectionInfo?.selectedShapeRef.current ?? null; - const handleZIndexTop = () => { - selectionInfo?.setZIndexOnSelected('top'); - }; - if (!selectedShapeID) { return null; } return (
- +
+

Properties

+
+
); };