Skip to content

Commit 77c4e2f

Browse files
gonfunkocwillisf
authored andcommitted
refactor: use block styles instead of directly specifying block colors (#23)
* chore: format monitor.jsx * chore: format define-dynamic-block.js * chore: format make-toolbox-xml.js * chore: format blockHelpers.js * chore: format theme definitions * chore: format custom-procedures.jsx * fix: use styles instead of colors for coloring dynamic blocks * refactor: define themes in the format expected by Blockly for BlockStyles * refactor: convert and inject Scratch themes into Blockly * chore: add comments clarifying the color/colour dichotomy
1 parent 217a3c0 commit 77c4e2f

File tree

8 files changed

+650
-435
lines changed

8 files changed

+650
-435
lines changed
Lines changed: 73 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -1,49 +1,56 @@
1-
import React from 'react';
2-
import ReactDOM from 'react-dom';
3-
import PropTypes from 'prop-types';
4-
import Draggable from 'react-draggable';
5-
import {FormattedMessage} from 'react-intl';
6-
import {ContextMenuTrigger} from 'react-contextmenu';
7-
import {BorderedMenuItem, ContextMenu, MenuItem} from '../context-menu/context-menu.jsx';
8-
import Box from '../box/box.jsx';
9-
import DefaultMonitor from './default-monitor.jsx';
10-
import LargeMonitor from './large-monitor.jsx';
11-
import SliderMonitor from '../../containers/slider-monitor.jsx';
12-
import ListMonitor from '../../containers/list-monitor.jsx';
13-
import {getColorsForTheme} from '../../lib/themes/index.js';
1+
import React from "react";
2+
import ReactDOM from "react-dom";
3+
import PropTypes from "prop-types";
4+
import Draggable from "react-draggable";
5+
import { FormattedMessage } from "react-intl";
6+
import { ContextMenuTrigger } from "react-contextmenu";
7+
import {
8+
BorderedMenuItem,
9+
ContextMenu,
10+
MenuItem,
11+
} from "../context-menu/context-menu.jsx";
12+
import Box from "../box/box.jsx";
13+
import DefaultMonitor from "./default-monitor.jsx";
14+
import LargeMonitor from "./large-monitor.jsx";
15+
import SliderMonitor from "../../containers/slider-monitor.jsx";
16+
import ListMonitor from "../../containers/list-monitor.jsx";
17+
import { getColorsForTheme } from "../../lib/themes/index.js";
1418

15-
import styles from './monitor.css';
19+
import styles from "./monitor.css";
1620

17-
// Map category name to color name used in scratch-blocks Blockly.Colours
21+
// Map category name to color name used in scratch-blocks Blockly.Colours. Note
22+
// that Blockly uses the UK spelling of "colour", so fields that interact
23+
// directly with Blockly follow that convention, while Scratch code uses the US
24+
// spelling of "color".
1825
const categoryColorMap = {
19-
data: 'data',
20-
sensing: 'sensing',
21-
sound: 'sounds',
22-
looks: 'looks',
23-
motion: 'motion',
24-
list: 'data_lists',
25-
extension: 'pen'
26+
data: "data",
27+
sensing: "sensing",
28+
sound: "sounds",
29+
looks: "looks",
30+
motion: "motion",
31+
list: "data_lists",
32+
extension: "pen",
2633
};
2734

2835
const modes = {
2936
default: DefaultMonitor,
3037
large: LargeMonitor,
3138
slider: SliderMonitor,
32-
list: ListMonitor
39+
list: ListMonitor,
3340
};
3441

3542
const getCategoryColor = (theme, category) => {
3643
const colors = getColorsForTheme(theme);
3744
return {
38-
background: colors[categoryColorMap[category]].primary,
39-
text: colors.text
45+
background: colors[categoryColorMap[category]].colourPrimary,
46+
text: colors.text,
4047
};
4148
};
4249

43-
const MonitorComponent = props => (
50+
const MonitorComponent = (props) => (
4451
<ContextMenuTrigger
4552
disable={!props.draggable}
46-
holdToDisplay={props.mode === 'slider' ? -1 : 1000}
53+
holdToDisplay={props.mode === "slider" ? -1 : 1000}
4754
id={`monitor-${props.label}`}
4855
>
4956
<Draggable
@@ -56,80 +63,94 @@ const MonitorComponent = props => (
5663
<Box
5764
className={styles.monitorContainer}
5865
componentRef={props.componentRef}
59-
onDoubleClick={props.mode === 'list' || !props.draggable ? null : props.onNextMode}
66+
onDoubleClick={
67+
props.mode === "list" || !props.draggable
68+
? null
69+
: props.onNextMode
70+
}
6071
>
6172
{React.createElement(modes[props.mode], {
62-
categoryColor: getCategoryColor(props.theme, props.category),
63-
...props
73+
categoryColor: getCategoryColor(
74+
props.theme,
75+
props.category
76+
),
77+
...props,
6478
})}
6579
</Box>
6680
</Draggable>
67-
{ReactDOM.createPortal((
81+
{ReactDOM.createPortal(
6882
// Use a portal to render the context menu outside the flow to avoid
6983
// positioning conflicts between the monitors `transform: scale` and
7084
// the context menus `position: fixed`. For more details, see
7185
// http://meyerweb.com/eric/thoughts/2011/09/12/un-fixing-fixed-elements-with-css-transforms/
7286
<ContextMenu id={`monitor-${props.label}`}>
73-
{props.onSetModeToDefault &&
87+
{props.onSetModeToDefault && (
7488
<MenuItem onClick={props.onSetModeToDefault}>
7589
<FormattedMessage
7690
defaultMessage="normal readout"
7791
description="Menu item to switch to the default monitor"
7892
id="gui.monitor.contextMenu.default"
7993
/>
80-
</MenuItem>}
81-
{props.onSetModeToLarge &&
94+
</MenuItem>
95+
)}
96+
{props.onSetModeToLarge && (
8297
<MenuItem onClick={props.onSetModeToLarge}>
8398
<FormattedMessage
8499
defaultMessage="large readout"
85100
description="Menu item to switch to the large monitor"
86101
id="gui.monitor.contextMenu.large"
87102
/>
88-
</MenuItem>}
89-
{props.onSetModeToSlider &&
103+
</MenuItem>
104+
)}
105+
{props.onSetModeToSlider && (
90106
<MenuItem onClick={props.onSetModeToSlider}>
91107
<FormattedMessage
92108
defaultMessage="slider"
93109
description="Menu item to switch to the slider monitor"
94110
id="gui.monitor.contextMenu.slider"
95111
/>
96-
</MenuItem>}
97-
{props.onSliderPromptOpen && props.mode === 'slider' &&
112+
</MenuItem>
113+
)}
114+
{props.onSliderPromptOpen && props.mode === "slider" && (
98115
<BorderedMenuItem onClick={props.onSliderPromptOpen}>
99116
<FormattedMessage
100117
defaultMessage="change slider range"
101118
description="Menu item to change the slider range"
102119
id="gui.monitor.contextMenu.sliderRange"
103120
/>
104-
</BorderedMenuItem>}
105-
{props.onImport &&
121+
</BorderedMenuItem>
122+
)}
123+
{props.onImport && (
106124
<MenuItem onClick={props.onImport}>
107125
<FormattedMessage
108126
defaultMessage="import"
109127
description="Menu item to import into list monitors"
110128
id="gui.monitor.contextMenu.import"
111129
/>
112-
</MenuItem>}
113-
{props.onExport &&
130+
</MenuItem>
131+
)}
132+
{props.onExport && (
114133
<MenuItem onClick={props.onExport}>
115134
<FormattedMessage
116135
defaultMessage="export"
117136
description="Menu item to export from list monitors"
118137
id="gui.monitor.contextMenu.export"
119138
/>
120-
</MenuItem>}
121-
{props.onHide &&
139+
</MenuItem>
140+
)}
141+
{props.onHide && (
122142
<BorderedMenuItem onClick={props.onHide}>
123143
<FormattedMessage
124144
defaultMessage="hide"
125145
description="Menu item to hide the monitor"
126146
id="gui.monitor.contextMenu.hide"
127147
/>
128-
</BorderedMenuItem>}
129-
</ContextMenu>
130-
), document.body)}
148+
</BorderedMenuItem>
149+
)}
150+
</ContextMenu>,
151+
document.body
152+
)}
131153
</ContextMenuTrigger>
132-
133154
);
134155

135156
const monitorModes = Object.keys(modes);
@@ -149,15 +170,12 @@ MonitorComponent.propTypes = {
149170
onSetModeToLarge: PropTypes.func,
150171
onSetModeToSlider: PropTypes.func,
151172
onSliderPromptOpen: PropTypes.func,
152-
theme: PropTypes.string.isRequired
173+
theme: PropTypes.string.isRequired,
153174
};
154175

155176
MonitorComponent.defaultProps = {
156-
category: 'extension',
157-
mode: 'default'
177+
category: "extension",
178+
mode: "default",
158179
};
159180

160-
export {
161-
MonitorComponent as default,
162-
monitorModes
163-
};
181+
export { MonitorComponent as default, monitorModes };

packages/scratch-gui/src/containers/blocks.jsx

Lines changed: 42 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,9 @@ import DragConstants from "../lib/drag-constants";
2323
import defineDynamicBlock from "../lib/define-dynamic-block";
2424
import { DEFAULT_THEME, getColorsForTheme, themeMap } from "../lib/themes";
2525
import {
26-
injectExtensionBlockTheme,
26+
injectExtensionBlockIcons,
2727
injectExtensionCategoryTheme,
28+
getExtensionColors,
2829
} from "../lib/themes/blockHelpers";
2930

3031
import { connect } from "react-redux";
@@ -126,7 +127,10 @@ class Blocks extends React.Component {
126127
{
127128
rtl: this.props.isRtl,
128129
toolbox: this.props.toolboxXML,
129-
colours: getColorsForTheme(this.props.theme),
130+
theme: new this.ScratchBlocks.Theme(
131+
this.props.theme,
132+
getColorsForTheme(this.props.theme)
133+
),
130134
}
131135
);
132136
this.workspace = this.ScratchBlocks.inject(
@@ -580,7 +584,7 @@ class Blocks extends React.Component {
580584
dynamicBlocksInfo.push(blockInfo);
581585
} else if (blockInfo.json) {
582586
staticBlocksJson.push(
583-
injectExtensionBlockTheme(
587+
injectExtensionBlockIcons(
584588
blockInfo.json,
585589
this.props.theme
586590
)
@@ -617,7 +621,39 @@ class Blocks extends React.Component {
617621
);
618622
defineBlocks(categoryInfo.menus);
619623
defineBlocks(categoryInfo.blocks);
620-
624+
// Note that Blockly uses the UK spelling of "colour", so fields that
625+
// interact directly with Blockly follow that convention, while Scratch
626+
// code uses the US spelling of "color".
627+
let colourPrimary = categoryInfo.color1;
628+
let colourSecondary = categoryInfo.color2;
629+
let colourTertiary = categoryInfo.color3;
630+
let colourQuaternary = categoryInfo.color3;
631+
if (this.props.theme !== DEFAULT_THEME) {
632+
const colors = getExtensionColors(this.props.theme);
633+
colourPrimary = colors.colourPrimary;
634+
colourSecondary = colors.colourSecondary;
635+
colourTertiary = colors.colourTertiary;
636+
colourQuaternary = colors.colourQuaternary;
637+
}
638+
this.ScratchBlocks.getMainWorkspace()
639+
.getTheme()
640+
.setBlockStyle(categoryInfo.id, {
641+
colourPrimary,
642+
colourSecondary,
643+
colourTertiary,
644+
colourQuaternary,
645+
});
646+
this.ScratchBlocks.getMainWorkspace()
647+
.getTheme()
648+
.setBlockStyle(`${categoryInfo.id}_selected`, {
649+
colourPrimary: colourQuaternary,
650+
colourSecondary: colourQuaternary,
651+
colourTertiary: colourQuaternary,
652+
colourQuaternary: colourQuaternary,
653+
});
654+
this.ScratchBlocks.getMainWorkspace().setTheme(
655+
this.ScratchBlocks.getMainWorkspace().getTheme()
656+
);
621657
// Update the toolbox with new blocks if possible
622658
const toolboxXML = this.getToolboxXML();
623659
if (toolboxXML) {
@@ -734,6 +770,7 @@ class Blocks extends React.Component {
734770
updateMetrics: updateMetricsProp,
735771
useCatBlocks,
736772
workspaceMetrics,
773+
theme,
737774
...props
738775
} = this.props;
739776
/* eslint-enable no-unused-vars */
@@ -776,6 +813,7 @@ class Blocks extends React.Component {
776813
media: options.media,
777814
}}
778815
onRequestClose={this.handleCustomProceduresClose}
816+
theme={theme}
779817
/>
780818
) : null}
781819
</React.Fragment>

0 commit comments

Comments
 (0)