Skip to content

Commit

Permalink
Merge pull request #394 from sugarlabs/gsoc-2023/niloy
Browse files Browse the repository at this point in the history
GSoC 2023: Niloy
  • Loading branch information
meganindya authored Mar 20, 2024
2 parents d76bc37 + 12d2b23 commit 551e939
Show file tree
Hide file tree
Showing 26 changed files with 2,946 additions and 356 deletions.
4 changes: 3 additions & 1 deletion modules/code-builder/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
"react-dom": "~18.x"
},
"dependencies": {
"@sugarlabs/musicblocks-v4-lib": "^0.2.0"
"@sugarlabs/musicblocks-v4-lib": "^0.2.0",
"react-aria": "^3.26.0",
"zustand": "^4.3.9"
}
}
5 changes: 5 additions & 0 deletions modules/code-builder/playground/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,17 @@ import { createRoot } from 'react-dom/client';
import { createBrowserRouter, Navigate, RouterProvider } from 'react-router-dom';

import PageCollision from './pages/Collision';
import WorkSpace from './pages/WorkSpace';

const router = createBrowserRouter([
{
path: '/collision',
element: <PageCollision />,
},
{
path: '/workspace',
element: <WorkSpace />,
},
{
path: '/',
element: <Navigate to="/collision" />,
Expand Down
185 changes: 185 additions & 0 deletions modules/code-builder/playground/pages/WorkSpace/BrickFactory.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
import { useState } from 'react';
import { useMove } from 'react-aria';
import { BrickBlock, BrickData, BrickExpression, BrickStatement } from '@/brick';
import { useBricksCoords } from './BricksCoordsStore';
import { WORKSPACES_DATA } from './data';
import type { Brick } from './data';
import { getBelowBricksIds } from './utils';

const BrickFactory = ({ brickData }: { brickData: Brick }) => {
const CONTAINER_SIZE_X = 800;
const CONTAINER_SIZE_Y = 700;
const BRICK_HEIGHT = brickData.instance.bBoxBrick.extent.height;
const BRICK_WIDTH = brickData.instance.bBoxBrick.extent.width;
const { getCoords, setCoords } = useBricksCoords();
const brickCoords = getCoords(brickData.id)!;
const [color, setColor] = useState(brickData.instance.colorBg as string);

const clampX = (pos: number) => Math.min(Math.max(pos, 0), CONTAINER_SIZE_X - BRICK_WIDTH * 2);
const clampY = (pos: number) => Math.min(Math.max(pos, 0), CONTAINER_SIZE_Y - BRICK_HEIGHT * 2);

const { moveProps } = useMove({
onMoveStart(e) {
console.log(`move start with pointerType = ${e.pointerType}`);
setColor('white');
},
onMove(e) {
const newX = brickCoords.x + e.deltaX;
const newY = brickCoords.y + e.deltaY;
setCoords(brickData.id, { x: clampX(newX), y: clampY(newY) });

brickData.childBricks.forEach((childBrick) => {
const childBrickCoords = getCoords(childBrick)!;
setCoords(childBrick, {
x: childBrickCoords.x + e.deltaX,
y: childBrickCoords.y + e.deltaY,
});
});

const belowBrickIds = getBelowBricksIds(WORKSPACES_DATA[0].data, brickData.id);
belowBrickIds.forEach((belowBrickId) => {
const belowBrickCoords = getCoords(belowBrickId)!;
setCoords(belowBrickId, {
x: belowBrickCoords.x + e.deltaX,
y: belowBrickCoords.y + e.deltaY,
});
});

// Normally, we want to allow the user to continue
// dragging outside the box such that they need to
// drag back over the ball again before it moves.
// This is handled below by clamping during render.
// If using the keyboard, however, we need to clamp
// here so that dragging outside the container and
// then using the arrow keys works as expected.
// if (e.pointerType === 'keyboard') {
// x = clamp(x);
// y = clamp(y);
// }

// setEvents((events) => [
// `move with pointerType = ${e.pointerType}, deltaX = ${e.deltaX}, deltaY = ${e.deltaY}`,
// ...events,
// ]);
},
onMoveEnd(e) {
console.log(`move end with pointerType = ${e.pointerType}`);
setColor(brickData.instance.colorBg as string);
},
});

const VisualIndicators = () => (
<>
{/* Right args bounding box */}
{'bBoxArgs' in brickData.instance && (
<>
{Object.keys(brickData.instance.bBoxArgs).map((name, i) => {
if ('bBoxArgs' in brickData.instance) {
const arg = brickData.instance.bBoxArgs[name];

return (
<rect
key={i}
x={brickCoords.x + arg?.coords.x}
y={brickCoords.y + arg?.coords.y}
height={arg?.extent.height}
width={arg?.extent.width}
fill="green"
opacity={0.8}
/>
);
}
})}
</>
)}

{/* Top instruction notch bounding box */}
{'bBoxNotchInsTop' in brickData.instance && (
<rect
x={brickCoords.x + brickData.instance.bBoxNotchInsTop?.coords.x}
y={brickCoords.y + brickData.instance.bBoxNotchInsTop?.coords.y}
height={brickData.instance.bBoxNotchInsTop?.extent.height}
width={brickData.instance.bBoxNotchInsTop?.extent.width}
fill="green"
opacity={0.9}
/>
)}

{/* Bottom instruction notch bounding box */}
{'bBoxNotchInsBot' in brickData.instance && (
<rect
x={brickCoords.x + brickData.instance.bBoxNotchInsBot?.coords.x}
y={brickCoords.y + brickData.instance.bBoxNotchInsBot?.coords.y}
height={brickData.instance.bBoxNotchInsBot?.extent.height}
width={brickData.instance.bBoxNotchInsBot?.extent.width}
fill="green"
opacity={0.9}
/>
)}

{/* Top instruction notch inside nesting bounding box */}
{'bBoxNotchInsNestTop' in brickData.instance && (
<rect
x={brickCoords.x + brickData.instance.bBoxNotchInsNestTop?.coords.x}
y={brickCoords.y + brickData.instance.bBoxNotchInsNestTop?.coords.y}
height={brickData.instance.bBoxNotchInsNestTop?.extent.height}
width={brickData.instance.bBoxNotchInsNestTop?.extent.width}
fill="green"
opacity={0.9}
/>
)}
</>
);

const getBrick = () => {
switch (brickData.type) {
case 'data':
return (
<BrickData
brickData={brickData}
moveProps={moveProps}
coords={brickCoords}
color={color}
/>
);
case 'expression':
return (
<BrickExpression
brickData={brickData}
moveProps={moveProps}
coords={brickCoords}
color={color}
/>
);
case 'statement':
return (
<BrickStatement
brickData={brickData}
moveProps={moveProps}
coords={brickCoords}
color={color}
/>
);
case 'block':
return (
<BrickBlock
brickData={brickData}
moveProps={moveProps}
coords={brickCoords}
color={color}
/>
);
default:
return <></>;
}
};

return (
<>
<VisualIndicators />
{/* {getBrick()} */}
</>
);
};

export default BrickFactory;
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { create } from 'zustand';

type CoordsState = {
allCoords: {
brickId: string;
coords: {
x: number;
y: number;
};
}[];
setCoords: (brickId: string, coords: { x: number; y: number }) => void;
getCoords: (brickId: string) => { x: number; y: number } | undefined;
};

const useBricksCoordsStore = create<CoordsState>((set, get) => ({
allCoords: [
{ brickId: '1', coords: { x: 50, y: 50 } },
{ brickId: '2', coords: { x: 68, y: 92 } },
{ brickId: '3', coords: { x: 68, y: 134 } },
{ brickId: '4', coords: { x: 68, y: 176 } },
{ brickId: '5', coords: { x: 86, y: 218 } },
{ brickId: '6', coords: { x: 68, y: 302 } },
],
setCoords: (brickId: string, coords: { x: number; y: number }) =>
set(
(state: {
allCoords: {
brickId: string;
coords: {
x: number;
y: number;
};
}[];
}) => ({
allCoords: state.allCoords.map((item) =>
item.brickId === brickId ? { brickId, coords } : item,
),
}),
),
getCoords: (brickId: string) =>
get().allCoords.find((item) => item.brickId === brickId)?.coords,
}));

export const useBricksCoords = () => {
const allCoords = useBricksCoordsStore((state) => state.allCoords);
const setCoords = useBricksCoordsStore((state) => state.setCoords);
const getCoords = useBricksCoordsStore((state) => state.getCoords);

return { allCoords, setCoords, getCoords };
};
Loading

0 comments on commit 551e939

Please sign in to comment.