Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
ca093f8
Initial commit with CRA
40thieves Feb 19, 2022
8b02e7f
Install packages for blockly support
40thieves Feb 19, 2022
c325edd
Initial set up for components
40thieves Feb 19, 2022
7fdd0ca
Add blockly hook
40thieves Feb 19, 2022
fa46dfe
Copy/paste custom blocks code into react
40thieves Feb 19, 2022
c8cac0b
Switch to toolbox from first exercise
40thieves Feb 19, 2022
5352e9e
Get text shadow working
40thieves Feb 20, 2022
15df03e
Start on loading exercises
40thieves Feb 24, 2022
7185d82
this was inevitable wasn't it
SallyMcGrath Mar 4, 2022
50c676e
link to scss
SallyMcGrath Mar 4, 2022
6dadd98
install tokenised prism and draft output component
SallyMcGrath Mar 4, 2022
8d093d2
fiddle with links
SallyMcGrath Mar 4, 2022
17deb9a
ignore DS_Store
SallyMcGrath Mar 4, 2022
df3b4c4
clean up base
SallyMcGrath Mar 4, 2022
7b05ad5
throw in a bunch of layout components
SallyMcGrath Mar 4, 2022
4a09ef9
ignore DS_Store
SallyMcGrath Mar 4, 2022
30a9a5b
add Blockly sass partial
SallyMcGrath Mar 4, 2022
6068907
throwing in some chrome, just drafting
SallyMcGrath Mar 4, 2022
e56e9fa
move mixins to a library
SallyMcGrath Mar 5, 2022
77ca16d
sketching out some fun buttons
SallyMcGrath Mar 5, 2022
c6a6792
throwing the button component in the mix
SallyMcGrath Mar 5, 2022
fd1fdbd
edit class to className
SallyMcGrath Mar 6, 2022
97e6dce
pointless fiddling with css
SallyMcGrath Mar 7, 2022
742999f
output, text panel
SallyMcGrath Mar 7, 2022
9c03acc
componentise all the things?
SallyMcGrath Mar 7, 2022
3016649
set up basic layout to look at
SallyMcGrath Mar 7, 2022
f906a81
tweak handle size
SallyMcGrath Mar 7, 2022
0ed10dd
rewrite split component as render thingy
SallyMcGrath Mar 7, 2022
6971f23
SVG resources for backgrounds etc
SallyMcGrath Mar 8, 2022
cf96e82
fiddling with css to think about layouts
SallyMcGrath Mar 8, 2022
c46b60a
Merge pull request #43 from CodeYourFuture/feature/sass
SallyMcGrath Mar 17, 2022
d3cf012
Merge pull request #46 from CodeYourFuture/feature/layout
SallyMcGrath Mar 17, 2022
c7eca3d
Install prettier
40thieves Apr 9, 2022
2239b68
Prettify
40thieves Apr 9, 2022
346f3d5
Add format command
40thieves Apr 9, 2022
d07bfcc
Add actions config
40thieves Apr 9, 2022
046f96d
Improve prettier config
40thieves Apr 9, 2022
13d165c
Install before running prettier
40thieves Apr 9, 2022
a6e85ee
Remove Actions for now
40thieves Apr 12, 2022
bfdaec9
Merge pull request #69 from CodeYourFuture/prettier-react
40thieves Apr 28, 2022
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
node_modules
package-lock.json
.DS_Store
1 change: 1 addition & 0 deletions client/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/build
1 change: 1 addition & 0 deletions client/.prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/build
52 changes: 52 additions & 0 deletions client/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
{
"name": "cyf-blocks-client",
"version": "0.1.0",
"private": true,
"dependencies": {
"@blockly/theme-highcontrast": "^2.0.3",
"@blockly/theme-modern": "^2.1.28",
"@testing-library/jest-dom": "^5.16.2",
"@testing-library/react": "^12.1.3",
"@testing-library/user-event": "^13.5.0",
"blockly": "^7.20211209.4",
"lodash": "^4.17.21",
"prism-react-renderer": "^1.3.1",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-markdown": "^8.0.0",
"react-scripts": "5.0.0",
"react-split-grid": "^1.0.4",
"sass": "^1.45.1",
"use-deep-compare-effect": "^1.8.1",
"web-vitals": "^2.1.4"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject",
"format": "prettier . --check",
"format:fix": "prettier . --write"
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
]
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
},
"devDependencies": {
"prettier": "^2.6.2"
}
}
12 changes: 12 additions & 0 deletions client/public/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>CYF Blocks</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
</body>
</html>
124 changes: 124 additions & 0 deletions client/src/App.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
import { useState } from "react";
import * as Blockly from "blockly/core";
import locale from "blockly/msg/en";
import "blockly/blocks";

import "./Blocks/dom";
import "./Blocks/cyf";
import useBlockly from "./Blockly/useBlockly";

import * as Exercise1 from "./Exercises/01-stuff";
import * as Exercise2 from "./Exercises/02-more-stuff";

import Split from "react-split-grid";
import TextPanel from "./TextPanel/TextPanel";
import Output from "./Output/Output";
import Header from "./Layout/Header/Header";
import Menu from "./Layout/Menu/Menu";
import Footer from "./Layout/Footer/Footer";
import Button from "./Button/Button";
import "./App.scss";

import { ReactComponent as Background } from "../src/svgs/Humaaans-Phone.svg";

Blockly.setLocale(locale);

// just left all this and presumed you will pass whatever you decide to do into the text panel
const exercises = [Exercise1, Exercise2];

function useExercise() {
const [exerciseIndex, setExerciseIndex] = useState(0);

function nextExercise() {
setExerciseIndex(exerciseIndex + 1);
}
function prevExercise() {
setExerciseIndex(exerciseIndex - 1);
}

return {
exercise: exercises[exerciseIndex],
hasNextExercise: exerciseIndex + 1 < exercises.length,
nextExercise,
hasPrevExercise: exerciseIndex - 1 >= 0,
prevExercise,
};
}

export default function App() {
const {
exercise,
hasNextExercise,
nextExercise,
hasPrevExercise,
prevExercise,
} = useExercise();

const { BlocklyComponent, generate } = useBlockly({
toolbox: exercise.toolbox,
});

const [generated, setGenerated] = useState("");

function handleGenerate() {
setGenerated(generate());
}
// these are resizable panels -- a possible solution to the problem of making all three things available
// so the user can compare across text, blocks, and output
// not married to this

return (
<div className="c-layout">
<Background
style={{
position: "absolute",
height: "100vh",
width: "auto",
mixBlendMode: "multiply",
zIndex: "-1",
left: "33vw",
}}
/>
<Header />
<Menu />
<Split
minSize={100}
cursor="col-resize"
sizes={[50, 50, 200]}
gutterSize={48}
gutterAlign="center"
dragInterval={1}
direction="horizontal"
render={({ getGridProps, getGutterProps }) => (
<main {...getGridProps()} className="c-layout__panels">
<TextPanel
exercise={exercise}
navigation={{
nextExercise,
prevExercise,
hasNextExercise,
hasPrevExercise,
}}
/>
<div
{...getGutterProps("column", 1)}
className="c-button c-button--handle"
role="button"
></div>
<Output
generatedCode={generated}
generateCodeButton={handleGenerate}
/>
<div
{...getGutterProps("column", 3)}
className="c-button c-button--handle"
></div>
<BlocklyComponent />
</main>
)}
/>

<Footer />
</div>
);
}
20 changes: 20 additions & 0 deletions client/src/App.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
@import "theme/utilities.scss";

.c-layout {
@include grid-assign(header, panels, footer);
grid-template-columns: 0 1fr 0;
grid-template-areas:
". ...... ."
". header . "
". panels ."
". footer .";
gap: var(--theme-spacing--4);
place-content: center;
max-width: 100vw;
overflow-y: hidden;

&__panels {
display: grid;
grid-template-columns: 2fr 48px 1fr 48px 6fr;
}
}
5 changes: 5 additions & 0 deletions client/src/Blockly/Blockly.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
@import "../theme/utilities.scss";

.c-blockly {
height: 100%;
}
95 changes: 95 additions & 0 deletions client/src/Blockly/CYFTheme.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
/**
* @fileoverview Higher contrast, brandable theme.
*
*/

import Blockly from "blockly/core";

const defaultBlockStyles = {
colour_blocks: {
colourPrimary: "var(--theme-color--brand)",
colourSecondary: "var(--theme-color--accent)",
colourTertiary: "var(--theme-color--pop)",
},
list_blocks: {
colourPrimary: "var(--theme-color--brand)",
colourSecondary: "#AD7BE9",
colourTertiary: "#CDB6E9",
},
logic_blocks: {
colourPrimary: "var(--theme-color--brand)",
colourSecondary: "#64C7FF",
colourTertiary: "#C5EAFF",
},
loop_blocks: {
colourPrimary: "var(--theme-color--brand)",
colourSecondary: "#9AFF78",
colourTertiary: "#E1FFD7",
},
math_blocks: {
colourPrimary: "var(--theme-color--brand)",
colourSecondary: "#8A9EFF",
colourTertiary: "#DCE2FF",
},
procedure_blocks: {
colourPrimary: "var(--theme-color--brand)",
colourSecondary: "#77E6EE",
colourTertiary: "#CFECEE",
},
text_blocks: {
colourPrimary: "var(--theme-color--brand)",
colourSecondary: "#5ae27c",
colourTertiary: "#D2FFDD",
},
variable_blocks: {
colourPrimary: "var(--theme-color--brand)",
colourSecondary: "#FF73BE",
colourTertiary: "#FFD4EB",
},
variable_dynamic_blocks: {
colourPrimary: "var(--theme-color--brand)",
colourSecondary: "#FF73BE",
colourTertiary: "#FFD4EB",
},
hat_blocks: {
colourPrimary: "#880e4f",
colourSecondary: "#FF73BE",
colourTertiary: "#FFD4EB",
hat: "cap",
},
};

const categoryStyles = {
colour_category: { colour: "var(--theme-color--brand)" },
list_category: { colour: "var(--theme-color--brand)" },
logic_category: { colour: "var(--theme-color--brand)" },
loop_category: { colour: "var(--theme-color--brand)" },
math_category: { colour: "var(--theme-color--brand)" },
procedure_category: { colour: "var(--theme-color--brand)" },
text_category: { colour: "var(--theme-color--brand)" },
variable_category: { colour: "var(--theme-color--brand)" },
variable_dynamic_category: { colour: "var(--theme-color--brand)" },
};

// Temporarily required to ensure there's no conflict with
// Blockly.Themes.HighContrast
// Blockly.registry.unregister("theme", "highcontrast");

/**
* High contrast theme.
*/
export default Blockly.Theme.defineTheme("highcontrast", {
blockStyles: defaultBlockStyles,
categoryStyles: categoryStyles,
componentStyles: {
selectedGlowColour: "#000000",
selectedGlowSize: 1,
replacementGlowColour: "#000000",
},
fontStyle: {
family: "var(--theme-font-display)", // Use default font-family.
weight: null, // Use default font-weight.
size: 16,
},
startHats: null,
});
56 changes: 56 additions & 0 deletions client/src/Blockly/useBlockly.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { useMemo, useRef } from "react";
import useDeepCompareEffect from "use-deep-compare-effect";
import _ from "lodash";
import * as Blockly from "blockly/core";
import BlocklyJS from "blockly/javascript";
import "./Blockly.scss";
import * as Theme from "./CYFTheme";

export default function useBlockly({ initialBlock, toolbox, theme }) {
const wrapperRef = useRef();
const workspaceRef = useRef();

// Since the deps are objects, we need to deep compare them
useDeepCompareEffect(() => {
// Inject the workspace
workspaceRef.current = Blockly.inject(wrapperRef.current, {
// Unfortuntely Blockly mutates the toolbox object when initialising. This
// means that the dep changes between renders, which in turn means that
// the workspace is re-injected
toolbox: _.cloneDeep(toolbox),
theme: Theme,
// does this mean it cannot be https://developers.google.com/blockly/guides/configure/web/resizable ?
});

// Set the initial block in the workspace
if (initialBlock) {
let block = workspaceRef.current.newBlock(initialBlock.kind);
block.moveBy(initialBlock.x, initialBlock.y);
block.initSvg();

workspaceRef.current.render();
}

return () => {
workspaceRef.current.dispose();
};
}, [toolbox, initialBlock, theme]);

return useMemo(
() => ({
// Return a component to inject the workspace
BlocklyComponent: () => (
<section
ref={wrapperRef}
className="c-blockly"
aria-label="Visual Block Editor."
/>
),
// Generate code from the workspace
generate: () => {
return BlocklyJS.workspaceToCode(workspaceRef.current);
},
}),
[]
);
}
Loading