Skip to content

Commit 551e939

Browse files
authored
Merge pull request #394 from sugarlabs/gsoc-2023/niloy
GSoC 2023: Niloy
2 parents d76bc37 + 12d2b23 commit 551e939

File tree

26 files changed

+2946
-356
lines changed

26 files changed

+2946
-356
lines changed

modules/code-builder/package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
"react-dom": "~18.x"
1717
},
1818
"dependencies": {
19-
"@sugarlabs/musicblocks-v4-lib": "^0.2.0"
19+
"@sugarlabs/musicblocks-v4-lib": "^0.2.0",
20+
"react-aria": "^3.26.0",
21+
"zustand": "^4.3.9"
2022
}
2123
}

modules/code-builder/playground/index.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,17 @@ import { createRoot } from 'react-dom/client';
22
import { createBrowserRouter, Navigate, RouterProvider } from 'react-router-dom';
33

44
import PageCollision from './pages/Collision';
5+
import WorkSpace from './pages/WorkSpace';
56

67
const router = createBrowserRouter([
78
{
89
path: '/collision',
910
element: <PageCollision />,
1011
},
12+
{
13+
path: '/workspace',
14+
element: <WorkSpace />,
15+
},
1116
{
1217
path: '/',
1318
element: <Navigate to="/collision" />,
Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
import { useState } from 'react';
2+
import { useMove } from 'react-aria';
3+
import { BrickBlock, BrickData, BrickExpression, BrickStatement } from '@/brick';
4+
import { useBricksCoords } from './BricksCoordsStore';
5+
import { WORKSPACES_DATA } from './data';
6+
import type { Brick } from './data';
7+
import { getBelowBricksIds } from './utils';
8+
9+
const BrickFactory = ({ brickData }: { brickData: Brick }) => {
10+
const CONTAINER_SIZE_X = 800;
11+
const CONTAINER_SIZE_Y = 700;
12+
const BRICK_HEIGHT = brickData.instance.bBoxBrick.extent.height;
13+
const BRICK_WIDTH = brickData.instance.bBoxBrick.extent.width;
14+
const { getCoords, setCoords } = useBricksCoords();
15+
const brickCoords = getCoords(brickData.id)!;
16+
const [color, setColor] = useState(brickData.instance.colorBg as string);
17+
18+
const clampX = (pos: number) => Math.min(Math.max(pos, 0), CONTAINER_SIZE_X - BRICK_WIDTH * 2);
19+
const clampY = (pos: number) => Math.min(Math.max(pos, 0), CONTAINER_SIZE_Y - BRICK_HEIGHT * 2);
20+
21+
const { moveProps } = useMove({
22+
onMoveStart(e) {
23+
console.log(`move start with pointerType = ${e.pointerType}`);
24+
setColor('white');
25+
},
26+
onMove(e) {
27+
const newX = brickCoords.x + e.deltaX;
28+
const newY = brickCoords.y + e.deltaY;
29+
setCoords(brickData.id, { x: clampX(newX), y: clampY(newY) });
30+
31+
brickData.childBricks.forEach((childBrick) => {
32+
const childBrickCoords = getCoords(childBrick)!;
33+
setCoords(childBrick, {
34+
x: childBrickCoords.x + e.deltaX,
35+
y: childBrickCoords.y + e.deltaY,
36+
});
37+
});
38+
39+
const belowBrickIds = getBelowBricksIds(WORKSPACES_DATA[0].data, brickData.id);
40+
belowBrickIds.forEach((belowBrickId) => {
41+
const belowBrickCoords = getCoords(belowBrickId)!;
42+
setCoords(belowBrickId, {
43+
x: belowBrickCoords.x + e.deltaX,
44+
y: belowBrickCoords.y + e.deltaY,
45+
});
46+
});
47+
48+
// Normally, we want to allow the user to continue
49+
// dragging outside the box such that they need to
50+
// drag back over the ball again before it moves.
51+
// This is handled below by clamping during render.
52+
// If using the keyboard, however, we need to clamp
53+
// here so that dragging outside the container and
54+
// then using the arrow keys works as expected.
55+
// if (e.pointerType === 'keyboard') {
56+
// x = clamp(x);
57+
// y = clamp(y);
58+
// }
59+
60+
// setEvents((events) => [
61+
// `move with pointerType = ${e.pointerType}, deltaX = ${e.deltaX}, deltaY = ${e.deltaY}`,
62+
// ...events,
63+
// ]);
64+
},
65+
onMoveEnd(e) {
66+
console.log(`move end with pointerType = ${e.pointerType}`);
67+
setColor(brickData.instance.colorBg as string);
68+
},
69+
});
70+
71+
const VisualIndicators = () => (
72+
<>
73+
{/* Right args bounding box */}
74+
{'bBoxArgs' in brickData.instance && (
75+
<>
76+
{Object.keys(brickData.instance.bBoxArgs).map((name, i) => {
77+
if ('bBoxArgs' in brickData.instance) {
78+
const arg = brickData.instance.bBoxArgs[name];
79+
80+
return (
81+
<rect
82+
key={i}
83+
x={brickCoords.x + arg?.coords.x}
84+
y={brickCoords.y + arg?.coords.y}
85+
height={arg?.extent.height}
86+
width={arg?.extent.width}
87+
fill="green"
88+
opacity={0.8}
89+
/>
90+
);
91+
}
92+
})}
93+
</>
94+
)}
95+
96+
{/* Top instruction notch bounding box */}
97+
{'bBoxNotchInsTop' in brickData.instance && (
98+
<rect
99+
x={brickCoords.x + brickData.instance.bBoxNotchInsTop?.coords.x}
100+
y={brickCoords.y + brickData.instance.bBoxNotchInsTop?.coords.y}
101+
height={brickData.instance.bBoxNotchInsTop?.extent.height}
102+
width={brickData.instance.bBoxNotchInsTop?.extent.width}
103+
fill="green"
104+
opacity={0.9}
105+
/>
106+
)}
107+
108+
{/* Bottom instruction notch bounding box */}
109+
{'bBoxNotchInsBot' in brickData.instance && (
110+
<rect
111+
x={brickCoords.x + brickData.instance.bBoxNotchInsBot?.coords.x}
112+
y={brickCoords.y + brickData.instance.bBoxNotchInsBot?.coords.y}
113+
height={brickData.instance.bBoxNotchInsBot?.extent.height}
114+
width={brickData.instance.bBoxNotchInsBot?.extent.width}
115+
fill="green"
116+
opacity={0.9}
117+
/>
118+
)}
119+
120+
{/* Top instruction notch inside nesting bounding box */}
121+
{'bBoxNotchInsNestTop' in brickData.instance && (
122+
<rect
123+
x={brickCoords.x + brickData.instance.bBoxNotchInsNestTop?.coords.x}
124+
y={brickCoords.y + brickData.instance.bBoxNotchInsNestTop?.coords.y}
125+
height={brickData.instance.bBoxNotchInsNestTop?.extent.height}
126+
width={brickData.instance.bBoxNotchInsNestTop?.extent.width}
127+
fill="green"
128+
opacity={0.9}
129+
/>
130+
)}
131+
</>
132+
);
133+
134+
const getBrick = () => {
135+
switch (brickData.type) {
136+
case 'data':
137+
return (
138+
<BrickData
139+
brickData={brickData}
140+
moveProps={moveProps}
141+
coords={brickCoords}
142+
color={color}
143+
/>
144+
);
145+
case 'expression':
146+
return (
147+
<BrickExpression
148+
brickData={brickData}
149+
moveProps={moveProps}
150+
coords={brickCoords}
151+
color={color}
152+
/>
153+
);
154+
case 'statement':
155+
return (
156+
<BrickStatement
157+
brickData={brickData}
158+
moveProps={moveProps}
159+
coords={brickCoords}
160+
color={color}
161+
/>
162+
);
163+
case 'block':
164+
return (
165+
<BrickBlock
166+
brickData={brickData}
167+
moveProps={moveProps}
168+
coords={brickCoords}
169+
color={color}
170+
/>
171+
);
172+
default:
173+
return <></>;
174+
}
175+
};
176+
177+
return (
178+
<>
179+
<VisualIndicators />
180+
{/* {getBrick()} */}
181+
</>
182+
);
183+
};
184+
185+
export default BrickFactory;
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import { create } from 'zustand';
2+
3+
type CoordsState = {
4+
allCoords: {
5+
brickId: string;
6+
coords: {
7+
x: number;
8+
y: number;
9+
};
10+
}[];
11+
setCoords: (brickId: string, coords: { x: number; y: number }) => void;
12+
getCoords: (brickId: string) => { x: number; y: number } | undefined;
13+
};
14+
15+
const useBricksCoordsStore = create<CoordsState>((set, get) => ({
16+
allCoords: [
17+
{ brickId: '1', coords: { x: 50, y: 50 } },
18+
{ brickId: '2', coords: { x: 68, y: 92 } },
19+
{ brickId: '3', coords: { x: 68, y: 134 } },
20+
{ brickId: '4', coords: { x: 68, y: 176 } },
21+
{ brickId: '5', coords: { x: 86, y: 218 } },
22+
{ brickId: '6', coords: { x: 68, y: 302 } },
23+
],
24+
setCoords: (brickId: string, coords: { x: number; y: number }) =>
25+
set(
26+
(state: {
27+
allCoords: {
28+
brickId: string;
29+
coords: {
30+
x: number;
31+
y: number;
32+
};
33+
}[];
34+
}) => ({
35+
allCoords: state.allCoords.map((item) =>
36+
item.brickId === brickId ? { brickId, coords } : item,
37+
),
38+
}),
39+
),
40+
getCoords: (brickId: string) =>
41+
get().allCoords.find((item) => item.brickId === brickId)?.coords,
42+
}));
43+
44+
export const useBricksCoords = () => {
45+
const allCoords = useBricksCoordsStore((state) => state.allCoords);
46+
const setCoords = useBricksCoordsStore((state) => state.setCoords);
47+
const getCoords = useBricksCoordsStore((state) => state.getCoords);
48+
49+
return { allCoords, setCoords, getCoords };
50+
};

0 commit comments

Comments
 (0)