From ebc604a395d37e04c24bfe65768a43d5ba67b9b9 Mon Sep 17 00:00:00 2001 From: ABHINAV JHA <75615092+ABHINAV-JHA-27@users.noreply.github.com> Date: Wed, 16 Aug 2023 14:37:16 +0530 Subject: [PATCH 1/3] feat: Added Higher order component for Bricks and created a playground and canvas --- modules/code-builder/src/@types/pallete.d.ts | 7 ++ modules/code-builder/src/pallete/Canvas.tsx | 36 ++++++ modules/code-builder/src/pallete/DragHoc.tsx | 73 ++++++++++++ modules/code-builder/src/pallete/Pallete.tsx | 109 ++++++++++++++++++ .../code-builder/src/pallete/Playground.tsx | 78 +++++++++++++ modules/code-builder/src/pallete/pallete.scss | 53 +++++++++ .../src/pallete/pallete.stories.tsx | 24 ++++ .../src/pallete/playground.stories.tsx | 24 ++++ 8 files changed, 404 insertions(+) create mode 100644 modules/code-builder/src/@types/pallete.d.ts create mode 100644 modules/code-builder/src/pallete/Canvas.tsx create mode 100644 modules/code-builder/src/pallete/DragHoc.tsx create mode 100644 modules/code-builder/src/pallete/Pallete.tsx create mode 100644 modules/code-builder/src/pallete/Playground.tsx create mode 100644 modules/code-builder/src/pallete/pallete.scss create mode 100644 modules/code-builder/src/pallete/pallete.stories.tsx create mode 100644 modules/code-builder/src/pallete/playground.stories.tsx diff --git a/modules/code-builder/src/@types/pallete.d.ts b/modules/code-builder/src/@types/pallete.d.ts new file mode 100644 index 00000000..27bbaad2 --- /dev/null +++ b/modules/code-builder/src/@types/pallete.d.ts @@ -0,0 +1,7 @@ +export type PalleteProps = { + config: { + data: []; + }; +}; + +export type Tab = 'flow' | 'music' | 'graphic'; diff --git a/modules/code-builder/src/pallete/Canvas.tsx b/modules/code-builder/src/pallete/Canvas.tsx new file mode 100644 index 00000000..78974737 --- /dev/null +++ b/modules/code-builder/src/pallete/Canvas.tsx @@ -0,0 +1,36 @@ +interface CanvasProps { + onDrop: (type: string, x: number, y: number) => void; + children: React.ReactNode; +} + +const Canvas: React.FC = ({ onDrop, children }) => { + const handleMouseUp = (event: React.MouseEvent) => { + const type = event.currentTarget.getAttribute('data-type'); + if (type) { + const { clientX, clientY } = event; + const svgRect = event.currentTarget.getBoundingClientRect(); + const x = clientX - svgRect.left; + const y = clientY - svgRect.top; + onDrop(type, x, y); + } + }; + + const handleDragOver = (event: React.DragEvent) => { + event.preventDefault(); + }; + return ( + + {children} + + ); +}; + +export default Canvas; diff --git a/modules/code-builder/src/pallete/DragHoc.tsx b/modules/code-builder/src/pallete/DragHoc.tsx new file mode 100644 index 00000000..50a45774 --- /dev/null +++ b/modules/code-builder/src/pallete/DragHoc.tsx @@ -0,0 +1,73 @@ +import React, { useState, useRef, useEffect } from 'react'; + +const withDraggable = (Component: React.FC) => (props: any) => { + const [isDragging, setIsDragging] = useState(false); + const [dragStartPos, setDragStartPos] = useState<{ x: number; y: number }>({ x: 0, y: 0 }); + const [position, setPosition] = useState<{ x: number; y: number }>({ x: 0, y: 0 }); + const svgRef = useRef(null); + + const handleMouseDown = (event: React.MouseEvent) => { + event.preventDefault(); + const svgRect = svgRef.current?.getBoundingClientRect(); + if (svgRect) { + const offsetX = event.clientX - svgRect.left; + const offsetY = event.clientY - svgRect.top; + setIsDragging(true); + setDragStartPos({ x: offsetX, y: offsetY }); + } + }; + + const handleMouseMove = (event: MouseEvent) => { + if (isDragging && svgRef.current) { + const svgRect = svgRef.current.getBoundingClientRect(); + const offsetX = event.clientX - svgRect.left; + const offsetY = event.clientY - svgRect.top; + + const deltaX = offsetX - dragStartPos.x; + const deltaY = offsetY - dragStartPos.y; + setPosition((prevPosition) => ({ + x: prevPosition.x + deltaX, + y: prevPosition.y + deltaY, + })); + } + }; + + const handleMouseUp = () => { + if (isDragging) { + setIsDragging(false); + } + }; + + useEffect(() => { + if (isDragging) { + window.addEventListener('mousemove', handleMouseMove); + window.addEventListener('mouseup', handleMouseUp); + return () => { + window.removeEventListener('mousemove', handleMouseMove); + window.removeEventListener('mouseup', handleMouseUp); + }; + } + }, [isDragging]); + + return ( + + + + ); +}; + +export default withDraggable; diff --git a/modules/code-builder/src/pallete/Pallete.tsx b/modules/code-builder/src/pallete/Pallete.tsx new file mode 100644 index 00000000..1f7fa8f8 --- /dev/null +++ b/modules/code-builder/src/pallete/Pallete.tsx @@ -0,0 +1,109 @@ +import { TBrickArgDataType } from '@/@types/brick'; +import { PalleteProps, Tab } from '@/@types/pallete'; +import { BrickStatement, ModelBrickStatement } from '@/brick'; +import { useState } from 'react'; +import withDraggable from './DragHoc'; +import './pallete.scss'; + +const Pallete = (PalleteProps: PalleteProps) => { + const { config } = PalleteProps; + const [search, setSearch] = useState(''); + const [selectedTab, setSelectedTab] = useState('flow'); + const [data, setData] = useState(config.data); + const instance = new ModelBrickStatement({ + label: 'Statement', + args: Object.fromEntries( + [].map< + [ + string, + { + label: string; + dataType: TBrickArgDataType; + meta: unknown; + }, + ] + >((name) => [name, { label: name, dataType: 'any', meta: undefined }]), + ), + colorBg: 'lightgreen', + colorFg: 'black', + outline: 'green', + scale: 2, + glyph: '', + connectAbove: true, + connectBelow: true, + name: '', + }); + + const DragBrick = withDraggable(BrickStatement); + + const ResetPallete = () => { + setSearch(''); + setSelectedTab('flow'); + setData(config.data); + }; + + const searchBlocks = (search: string) => { + if (search === '') { + ResetPallete(); + return; + } + const newData = data.filter((block) => { + return block; + }); + console.log(newData); + setData([]); + }; + + return ( +
+ Pallete +
+ + + +
+
+ setSearch(e.target.value)} + className="search" + onSubmit={() => searchBlocks(search)} + /> +
+
+ +
+
+ ); +}; + +export default Pallete; diff --git a/modules/code-builder/src/pallete/Playground.tsx b/modules/code-builder/src/pallete/Playground.tsx new file mode 100644 index 00000000..52e039b3 --- /dev/null +++ b/modules/code-builder/src/pallete/Playground.tsx @@ -0,0 +1,78 @@ +import { useState } from 'react'; +import Canvas from './Canvas'; +import Pallete from './Pallete'; +import { BrickStatement, ModelBrickStatement } from '@/brick'; +import { TBrickArgDataType } from '@/@types/brick'; +import withDraggable from './DragHoc'; + +const Playground = () => { + const [elements, setElements] = useState([]); + const instance = new ModelBrickStatement({ + label: 'Statement', + args: Object.fromEntries( + [].map< + [ + string, + { + label: string; + dataType: TBrickArgDataType; + meta: unknown; + }, + ] + >((name) => [name, { label: name, dataType: 'any', meta: undefined }]), + ), + colorBg: 'lightgreen', + colorFg: 'black', + outline: 'green', + scale: 2, + glyph: '', + connectAbove: true, + connectBelow: true, + name: '', + }); + + const handleElementDrop = (type: string, x: number, y: number) => { + console.log(type, x, y); + const DragBrick = withDraggable(BrickStatement); + const newElement = ; + setElements([...elements, newElement]); + }; + + return ( +
+
+ +
+
+ {elements} +
+
+ ); +}; + +export default Playground; diff --git a/modules/code-builder/src/pallete/pallete.scss b/modules/code-builder/src/pallete/pallete.scss new file mode 100644 index 00000000..86db0ce5 --- /dev/null +++ b/modules/code-builder/src/pallete/pallete.scss @@ -0,0 +1,53 @@ +.tab { + border: solid 1px #ccc; + border-radius: 5px; + background-color: #fff; + padding: 6px 12px; + margin: 5px; +} + +.palleteContainer { + display: flex; + flex-direction: column; + border: 1px solid black; + border-radius: 5px; + padding: 7px; + margin: 5px; + background-color: #fff; + height: 100%; +} + +.search { + flex: 1; + margin: 5px; + padding: 5px; + border: solid 1px #ccc; + border-radius: 5px; + background-color: #fff; + color: '#686868'; +} + +.tabContainer { + display: flex; + flex-direction: row; + justify-content: space-between; + align-items: center; +} + +.searchContainer { + display: flex; + flex-direction: row; + justify-content: center; + align-items: center; +} + +.blocksContainer { + margin-top: 5px; + display: flex; + width: 100%; + height: 100%; + flex-direction: column; + align-items: center; + overflow-y: scroll; + row-gap: 10px; +} diff --git a/modules/code-builder/src/pallete/pallete.stories.tsx b/modules/code-builder/src/pallete/pallete.stories.tsx new file mode 100644 index 00000000..833620a3 --- /dev/null +++ b/modules/code-builder/src/pallete/pallete.stories.tsx @@ -0,0 +1,24 @@ +import type { Meta, StoryObj } from '@storybook/react'; +import Pallete from './Pallete'; + +const MetaData: Meta = { + component: Pallete, + parameters: { + layout: 'centered', + }, +}; + +type Story = StoryObj; + +export default { + title: 'Pallete', + ...MetaData, +}; + +export const PalleteStory: Story = { + args: { + config: { + data: [], + }, + }, +}; diff --git a/modules/code-builder/src/pallete/playground.stories.tsx b/modules/code-builder/src/pallete/playground.stories.tsx new file mode 100644 index 00000000..af50277f --- /dev/null +++ b/modules/code-builder/src/pallete/playground.stories.tsx @@ -0,0 +1,24 @@ +import type { Meta, StoryObj } from '@storybook/react'; +import Playground from './Playground'; + +const MetaData: Meta = { + component: Playground, + parameters: { + layout: 'centered', + }, +}; + +type Story = StoryObj; + +export default { + title: 'Pallete Playground', + ...MetaData, +}; + +export const PlayGroundStory: Story = { + args: { + config: { + data: [], + }, + }, +}; From bb0dbb0ac4ebeb330bceef7175eb70151431d2b3 Mon Sep 17 00:00:00 2001 From: ABHINAV JHA <75615092+ABHINAV-JHA-27@users.noreply.github.com> Date: Fri, 18 Aug 2023 15:10:07 +0530 Subject: [PATCH 2/3] Feat: Cleaned the component code for canvas and playground --- modules/code-builder/src/@types/pallete.d.ts | 1 + modules/code-builder/src/pallete/Canvas.tsx | 22 +++------------- modules/code-builder/src/pallete/DragHoc.tsx | 9 +++---- modules/code-builder/src/pallete/Pallete.tsx | 25 +++++++++++-------- .../code-builder/src/pallete/Playground.tsx | 14 ++++++----- 5 files changed, 31 insertions(+), 40 deletions(-) diff --git a/modules/code-builder/src/@types/pallete.d.ts b/modules/code-builder/src/@types/pallete.d.ts index 27bbaad2..90fb330e 100644 --- a/modules/code-builder/src/@types/pallete.d.ts +++ b/modules/code-builder/src/@types/pallete.d.ts @@ -2,6 +2,7 @@ export type PalleteProps = { config: { data: []; }; + reset: boolean; }; export type Tab = 'flow' | 'music' | 'graphic'; diff --git a/modules/code-builder/src/pallete/Canvas.tsx b/modules/code-builder/src/pallete/Canvas.tsx index 78974737..6b70cd1c 100644 --- a/modules/code-builder/src/pallete/Canvas.tsx +++ b/modules/code-builder/src/pallete/Canvas.tsx @@ -1,29 +1,15 @@ interface CanvasProps { - onDrop: (type: string, x: number, y: number) => void; children: React.ReactNode; } -const Canvas: React.FC = ({ onDrop, children }) => { - const handleMouseUp = (event: React.MouseEvent) => { - const type = event.currentTarget.getAttribute('data-type'); - if (type) { - const { clientX, clientY } = event; - const svgRect = event.currentTarget.getBoundingClientRect(); - const x = clientX - svgRect.left; - const y = clientY - svgRect.top; - onDrop(type, x, y); - } - }; - - const handleDragOver = (event: React.DragEvent) => { - event.preventDefault(); - }; +const Canvas: React.FC = ({ children }) => { return ( ) => (props: any) => { const [isDragging, setIsDragging] = useState(false); @@ -57,15 +57,14 @@ const withDraggable = (Component: React.FC) => (props: any) => { onMouseDown={handleMouseDown} transform={`translate(${position.x}, ${position.y})`} style={{ - // width: '100px', - // height: '100px', + width: svgRef.current?.children[0].getBoundingClientRect().width, + height: svgRef.current?.children[0].getBoundingClientRect().height, position: 'absolute', overflow: 'hidden', - border: '1px solid black', zIndex: 100, }} > - + ); }; diff --git a/modules/code-builder/src/pallete/Pallete.tsx b/modules/code-builder/src/pallete/Pallete.tsx index 1f7fa8f8..ebc4f1f8 100644 --- a/modules/code-builder/src/pallete/Pallete.tsx +++ b/modules/code-builder/src/pallete/Pallete.tsx @@ -1,12 +1,12 @@ import { TBrickArgDataType } from '@/@types/brick'; import { PalleteProps, Tab } from '@/@types/pallete'; import { BrickStatement, ModelBrickStatement } from '@/brick'; -import { useState } from 'react'; +import { useEffect, useState } from 'react'; import withDraggable from './DragHoc'; import './pallete.scss'; const Pallete = (PalleteProps: PalleteProps) => { - const { config } = PalleteProps; + const { config, reset } = PalleteProps; const [search, setSearch] = useState(''); const [selectedTab, setSelectedTab] = useState('flow'); const [data, setData] = useState(config.data); @@ -36,26 +36,29 @@ const Pallete = (PalleteProps: PalleteProps) => { const DragBrick = withDraggable(BrickStatement); - const ResetPallete = () => { - setSearch(''); - setSelectedTab('flow'); - setData(config.data); - }; - const searchBlocks = (search: string) => { if (search === '') { - ResetPallete(); + setSearch(''); + setSelectedTab('flow'); + setData(config.data); return; } const newData = data.filter((block) => { return block; }); - console.log(newData); setData([]); }; + useEffect(() => { + if (reset) { + setSearch(''); + setSelectedTab('flow'); + setData(config.data); + } + }, [reset]); + return ( -
+
Pallete
{ width: '65%', }} > - {elements} + {elements}
); From 75dd3a5724414465dfe33b3d946538d5f5247f7d Mon Sep 17 00:00:00 2001 From: ABHINAV JHA <75615092+ABHINAV-JHA-27@users.noreply.github.com> Date: Fri, 18 Aug 2023 16:36:54 +0530 Subject: [PATCH 3/3] Feat: Added Drag and Drop from pallete to canvas and reset pallete afterwards --- modules/code-builder/src/@types/pallete.d.ts | 1 + modules/code-builder/src/pallete/DragHoc.tsx | 54 ++++++++++++++++++- modules/code-builder/src/pallete/Pallete.tsx | 4 +- .../code-builder/src/pallete/Playground.tsx | 5 +- 4 files changed, 60 insertions(+), 4 deletions(-) diff --git a/modules/code-builder/src/@types/pallete.d.ts b/modules/code-builder/src/@types/pallete.d.ts index 90fb330e..5a689dbc 100644 --- a/modules/code-builder/src/@types/pallete.d.ts +++ b/modules/code-builder/src/@types/pallete.d.ts @@ -3,6 +3,7 @@ export type PalleteProps = { data: []; }; reset: boolean; + onBrickDrop: (brick: any) => void; }; export type Tab = 'flow' | 'music' | 'graphic'; diff --git a/modules/code-builder/src/pallete/DragHoc.tsx b/modules/code-builder/src/pallete/DragHoc.tsx index 3957f77c..6b84703a 100644 --- a/modules/code-builder/src/pallete/DragHoc.tsx +++ b/modules/code-builder/src/pallete/DragHoc.tsx @@ -1,7 +1,25 @@ import React, { useEffect, useRef, useState } from 'react'; +const getPalletteDimensions = () => { + const pallete = document.getElementById('Pallete'); + if (pallete) { + const dimensions = pallete.getBoundingClientRect(); + return dimensions; + } +}; + +const getCanvasDimensions = () => { + const canvas = document.getElementById('Canvas'); + if (canvas) { + const dimensions = canvas.getBoundingClientRect(); + return dimensions; + } +}; + const withDraggable = (Component: React.FC) => (props: any) => { + const threshold = 50; const [isDragging, setIsDragging] = useState(false); + const [originalPos, setOriginalPos] = useState<{ x: number; y: number }>({ x: 0, y: 0 }); const [dragStartPos, setDragStartPos] = useState<{ x: number; y: number }>({ x: 0, y: 0 }); const [position, setPosition] = useState<{ x: number; y: number }>({ x: 0, y: 0 }); const svgRef = useRef(null); @@ -14,9 +32,22 @@ const withDraggable = (Component: React.FC) => (props: any) => { const offsetY = event.clientY - svgRect.top; setIsDragging(true); setDragStartPos({ x: offsetX, y: offsetY }); + setOriginalPos({ x: svgRect.x, y: svgRect.y }); } }; + const returnToPallete = () => { + console.log('originalPos', originalPos); + setPosition(originalPos); + console.log('position', position); + }; + + const resetPositon = () => { + console.log('dragStartPos', dragStartPos); + setPosition(dragStartPos); + console.log('position', position); + }; + const handleMouseMove = (event: MouseEvent) => { if (isDragging && svgRef.current) { const svgRect = svgRef.current.getBoundingClientRect(); @@ -25,6 +56,7 @@ const withDraggable = (Component: React.FC) => (props: any) => { const deltaX = offsetX - dragStartPos.x; const deltaY = offsetY - dragStartPos.y; + setPosition((prevPosition) => ({ x: prevPosition.x + deltaX, y: prevPosition.y + deltaY, @@ -32,8 +64,28 @@ const withDraggable = (Component: React.FC) => (props: any) => { } }; - const handleMouseUp = () => { + const handleMouseUp = (event: MouseEvent) => { if (isDragging) { + const svgRect = svgRef.current?.getBoundingClientRect(); + const canvasDimensions = getCanvasDimensions(); + const palletteDimensions = getPalletteDimensions(); + if (svgRect && canvasDimensions && palletteDimensions) { + if ( + svgRect.right > canvasDimensions.right || + svgRect.top < canvasDimensions.top || + svgRect.bottom > canvasDimensions.bottom || + svgRect.left < palletteDimensions.left || + svgRect.top < palletteDimensions.top || + svgRect.bottom > palletteDimensions.bottom + ) { + returnToPallete(); + } else if (svgRect.left < canvasDimensions.left) { + resetPositon(); + } else { + props.onCanvasDrop(); + svgRef.current?.removeChild(svgRef.current?.children[0]); + } + } setIsDragging(false); } }; diff --git a/modules/code-builder/src/pallete/Pallete.tsx b/modules/code-builder/src/pallete/Pallete.tsx index ebc4f1f8..300b9e73 100644 --- a/modules/code-builder/src/pallete/Pallete.tsx +++ b/modules/code-builder/src/pallete/Pallete.tsx @@ -6,7 +6,7 @@ import withDraggable from './DragHoc'; import './pallete.scss'; const Pallete = (PalleteProps: PalleteProps) => { - const { config, reset } = PalleteProps; + const { config, reset, onBrickDrop } = PalleteProps; const [search, setSearch] = useState(''); const [selectedTab, setSelectedTab] = useState('flow'); const [data, setData] = useState(config.data); @@ -103,7 +103,7 @@ const Pallete = (PalleteProps: PalleteProps) => { />
- +
); diff --git a/modules/code-builder/src/pallete/Playground.tsx b/modules/code-builder/src/pallete/Playground.tsx index e3a948c6..c46cc146 100644 --- a/modules/code-builder/src/pallete/Playground.tsx +++ b/modules/code-builder/src/pallete/Playground.tsx @@ -33,10 +33,12 @@ const Playground = () => { name: '', }); - const handleElementDrop = () => { + const handleBrickDrop = () => { + console.log('handleBrickDrop'); const DragBrick = withDraggable(BrickStatement); const newElement = ; setElements([...elements, newElement]); + setReset(true); }; return ( @@ -61,6 +63,7 @@ const Playground = () => { config={{ data: [], }} + onBrickDrop={handleBrickDrop} reset={reset} />