Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/#66 z index properties #87

Merged
merged 2 commits into from
Jul 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions src/common/components/icons/bring-forward-icon.component.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
export const BringForwardIcon = () => {
return (
<svg
id="icon"
xmlns="http://www.w3.org/2000/svg"
width="1.2em"
height="1.2em"
viewBox="0 0 32 32"
>
<path d="M12,15H10V12a2.0023,2.0023,0,0,1,2-2h3v2H12Z" />
<path d="M15,30H12a2.0023,2.0023,0,0,1-2-2V25h2v3h3Z" />
<rect x="18" y="28" width="4" height="2" />
<path d="M28,30H25V28h3V25h2v3A2.0023,2.0023,0,0,1,28,30Z" />
<rect x="10" y="18" width="2" height="4" />
<rect x="28" y="18" width="2" height="4" />
<path d="M30,15H28V12H25V10h3a2.0023,2.0023,0,0,1,2,2Z" />
<rect x="18" y="10" width="4" height="2" />
<path d="M8,22H4a2.0023,2.0023,0,0,1-2-2V4A2.0023,2.0023,0,0,1,4,2H20a2.0023,2.0023,0,0,1,2,2V8H20V4H4V20H8Z" />
<rect
id="_Transparent_Rectangle_"
data-name="&lt;Transparent Rectangle&gt;"
width="1.2em"
height="1.2em"
fill="none"
/>
</svg>
);
};
20 changes: 20 additions & 0 deletions src/common/components/icons/bring-to-front-icon.component.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
export const BringToFrontIcon = () => {
return (
<svg
id="icon"
xmlns="http://www.w3.org/2000/svg"
width="1.2em"
height="1.2em"
viewBox="0 0 32 32"
>
<path d="M28,10H22V4a2.0023,2.0023,0,0,0-2-2H4A2.0023,2.0023,0,0,0,2,4V20a2.0023,2.0023,0,0,0,2,2h6v6a2,2,0,0,0,2,2H28a2,2,0,0,0,2-2V12A2,2,0,0,0,28,10ZM4,20,3.9985,4H20v6H12a2,2,0,0,0-2,2v8Z" />
<rect
id="_Transparent_Rectangle_"
data-name="&lt;Transparent Rectangle&gt;"
width="1.2em"
height="1.2em"
fill="none"
/>
</svg>
);
};
4 changes: 4 additions & 0 deletions src/common/components/icons/index.ts
Original file line number Diff line number Diff line change
@@ -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';
25 changes: 25 additions & 0 deletions src/common/components/icons/send-backward-icon.component.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
export const SendBackwardIcon = () => {
return (
<svg
id="icon"
xmlns="http://www.w3.org/2000/svg"
width="1.2em"
height="1.2em"
viewBox="0 0 32 32"
>
<path d="M4,7H2V4A2.0023,2.0023,0,0,1,4,2H7V4H4Z" />
<path d="M7,22H4a2.0023,2.0023,0,0,1-2-2V17H4v3H7Z" />
<rect x="2" y="10" width="2" height="4" />
<path d="M22,7H20V4H17V2h3a2.0023,2.0023,0,0,1,2,2Z" />
<rect x="10" y="2" width="4" height="2" />
<path d="M28,30H12a2.0023,2.0023,0,0,1-2-2V12a2.0023,2.0023,0,0,1,2-2H28a2.0023,2.0023,0,0,1,2,2V28A2.0023,2.0023,0,0,1,28,30ZM12,12V28H28V12Z" />
<rect
id="_Transparent_Rectangle_"
data-name="&lt;Transparent Rectangle&gt;"
width="1.2em"
height="1.2em"
fill="none"
/>
</svg>
);
};
20 changes: 20 additions & 0 deletions src/common/components/icons/send-to-back-icon.component.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
export const SendToBackIcon = () => {
return (
<svg
id="icon"
xmlns="http://www.w3.org/2000/svg"
width="1.2em"
height="1.2em"
viewBox="0 0 32 32"
>
<path d="M28,10H22V4a2,2,0,0,0-2-2H4A2,2,0,0,0,2,4V20a2,2,0,0,0,2,2h6v6a2.0023,2.0023,0,0,0,2,2H28a2.0023,2.0023,0,0,0,2-2V12A2.0023,2.0023,0,0,0,28,10ZM12,28V12H28l.0015,16Z" />
<rect
id="_Transparent_Rectangle_"
data-name="&lt;Transparent Rectangle&gt;"
width="1.2em"
height="1.2em"
fill="none"
/>
</svg>
);
};
3 changes: 3 additions & 0 deletions src/core/providers/canvas/canvas.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<Konva.Transformer>;
shapeRefs: React.MutableRefObject<ShapeRefs>;
Expand All @@ -14,6 +16,7 @@ export interface SelectionInfo {
selectedShapeRef: React.MutableRefObject<Node<NodeConfig> | null>;
selectedShapeId: string;
selectedShapeType: ShapeType | null;
setZIndexOnSelected: (action: ZIndexAction) => void;
}

export interface CanvasContextModel {
Expand Down
3 changes: 2 additions & 1 deletion src/core/providers/canvas/canvas.provider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -12,7 +13,7 @@ export const CanvasProvider: React.FC<Props> = props => {
const [shapes, setShapes] = React.useState<ShapeModel[]>([]);
const [scale, setScale] = React.useState(1);

const selectionInfo = useSelection(shapes);
const selectionInfo = useSelection(shapes, setShapes);

return (
<CanvasContext.Provider
Expand Down
15 changes: 13 additions & 2 deletions src/core/providers/canvas/use-selection.hook.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
import { useEffect, useRef, useState } from 'react';
import Konva from 'konva';
import { ShapeModel, ShapeRefs, ShapeType } from '@/core/model';
import { SelectionInfo } from './canvas.model';
import { SelectionInfo, ZIndexAction } from './canvas.model';
import { performZIndexAction } from './zindex.util';

export const useSelection = (shapes: ShapeModel[]): SelectionInfo => {
export const useSelection = (
shapes: ShapeModel[],
setShapes: React.Dispatch<React.SetStateAction<ShapeModel[]>>
): SelectionInfo => {
const transformerRef = useRef<Konva.Transformer>(null);
const shapeRefs = useRef<ShapeRefs>({});
const selectedShapeRef = useRef<Konva.Node | null>(null);
Expand Down Expand Up @@ -41,6 +45,12 @@ export const useSelection = (shapes: ShapeModel[]): SelectionInfo => {
}
};

const setZIndexOnSelected = (action: ZIndexAction) => {
setShapes(prevShapes =>
performZIndexAction(selectedShapeId, action, prevShapes)
);
};

return {
transformerRef,
shapeRefs,
Expand All @@ -49,5 +59,6 @@ export const useSelection = (shapes: ShapeModel[]): SelectionInfo => {
selectedShapeRef,
selectedShapeId,
selectedShapeType,
setZIndexOnSelected,
};
};
Original file line number Diff line number Diff line change
@@ -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[] => {
Expand All @@ -16,7 +17,7 @@ export const moveZIndexToTop = (
: shapeCollection;
};

export const moveZIndexToBottom = (
const moveZIndexToBottom = (
selectedShapeId: string,
shapeCollection: ShapeModel[]
): ShapeModel[] => {
Expand All @@ -31,7 +32,7 @@ export const moveZIndexToBottom = (
: shapeCollection;
};

export const moveZIndexDownOneLevel = (
const moveZIndexDownOneLevel = (
selectedShapeId: string,
shapeCollection: ShapeModel[]
): ShapeModel[] => {
Expand All @@ -54,7 +55,7 @@ export const moveZIndexDownOneLevel = (
: shapeCollection;
};

export const moveZIndexTopOneLevel = (
const moveZIndexTopOneLevel = (
selectedShapeId: string,
shapeCollection: ShapeModel[]
): ShapeModel[] => {
Expand All @@ -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);
}
};
40 changes: 0 additions & 40 deletions src/pods/canvas/canvas.pod.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 = () => {
Expand Down Expand Up @@ -48,10 +42,6 @@ export const CanvasPod = () => {
);
};

const handleZIndexChange = (shapeCollection: ShapeModel[]) => {
setShapes(shapeCollection);
};

{
/* TODO: add other animation for isDraggerOver */
}
Expand All @@ -61,36 +51,6 @@ export const CanvasPod = () => {
ref={dropRef}
style={{ opacity: isDraggedOver ? 0.5 : 1 }}
>
{/*TODO: move buttons to app props panel*/}
<button
onClick={() =>
handleZIndexChange(moveZIndexToBottom(selectedShapeId, shapes))
}
>
Move to Bottom
</button>
<button
onClick={() =>
handleZIndexChange(moveZIndexToTop(selectedShapeId, shapes))
}
>
Move to Top
</button>
<button
onClick={() =>
handleZIndexChange(moveZIndexDownOneLevel(selectedShapeId, shapes))
}
>
Move to Bottom One Level
</button>
<button
onClick={() =>
handleZIndexChange(moveZIndexTopOneLevel(selectedShapeId, shapes))
}
>
Move to Top One Level
</button>

{/*TODO: move size to canvas provider?*/}
<Stage
width={3000}
Expand Down
15 changes: 15 additions & 0 deletions src/pods/properties/components/zindex/zindex-button.component.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import classes from './zindex-option.module.css';

interface ButtonProps {
onClick: () => void;
Icon: React.ComponentType;
}

export const ZIndexButton: React.FC<ButtonProps> = props => {
const { onClick, Icon } = props;
return (
<button onClick={onClick} className={classes.button}>
<Icon />
</button>
);
};
49 changes: 49 additions & 0 deletions src/pods/properties/components/zindex/zindex-option.component.tsx
Original file line number Diff line number Diff line change
@@ -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> = props => {
const { selectionInfo } = props;

const handleZIndexChange = (position: ZIndexAction) => {
selectionInfo?.setZIndexOnSelected(position);
};

return (
<div className={classes.container}>
<p>Layering</p>
{layersOptions.map(({ position, Icon }) => (
<ZIndexButton
key={position}
onClick={() => handleZIndexChange(position)}
Icon={Icon}
/>
))}
</div>
);
};
31 changes: 31 additions & 0 deletions src/pods/properties/components/zindex/zindex-option.module.css
Original file line number Diff line number Diff line change
@@ -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);
}
1 change: 1 addition & 0 deletions src/pods/properties/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './properties.pod';
Loading
Loading