feat: apply color theme to extension categories and blocks

This commit is contained in:
Andy O'Neill committed 2023-02-24 09:37:26 -05:00
1 parent d9baf4ca95
commit 63d43fbc78
7 files changed
+125 -7

No files matched your search

+8 -4
View File
@@ -18,7 +18,8 @@ import {BLOCKS_DEFAULT_SCALE, STAGE_DISPLAY_SIZES} from '../lib/layout-constants
import DropAreaHOC from '../lib/drop-area-hoc.jsx';
import DragConstants from '../lib/drag-constants';
import defineDynamicBlock from '../lib/define-dynamic-block';
import {getColorsForTheme, themes} from '../lib/themes';
import {DEFAULT_THEME, getColorsForTheme, themes} from '../lib/themes';
import {injectExtensionBlockColors, injectExtensionCategoryColors} from '../lib/themes/blockHelpers';
import {connect} from 'react-redux';
import {updateToolbox} from '../reducers/toolbox';
@@ -348,7 +349,10 @@ class Blocks extends React.Component {
const stageCostumes = stage.getCostumes();
const targetCostumes = target.getCostumes();
const targetSounds = target.getSounds();
const dynamicBlocksXML = this.props.vm.runtime.getBlocksXML(target);
const dynamicBlocksXML = injectExtensionCategoryColors(
this.props.vm.runtime.getBlocksXML(target),
this.props.theme
);
return makeToolboxXML(false, target.isStage, target.id, dynamicBlocksXML,
targetCostumes[targetCostumes.length - 1].name,
stageCostumes[stageCostumes.length - 1].name,
@@ -432,7 +436,7 @@ class Blocks extends React.Component {
if (blockInfo.info && blockInfo.info.isDynamic) {
dynamicBlocksInfo.push(blockInfo);
} else if (blockInfo.json) {
staticBlocksJson.push(blockInfo.json);
staticBlocksJson.push(injectExtensionBlockColors(blockInfo.json, this.props.theme));
}
// otherwise it's a non-block entry such as '---'
});
@@ -655,7 +659,7 @@ Blocks.defaultOptions = {
Blocks.defaultProps = {
isVisible: true,
options: Blocks.defaultOptions,
theme: 'standard'
theme: DEFAULT_THEME
};
const mapStateToProps = state => ({
+5
View File
@@ -2,6 +2,11 @@ const blockColors = {
motion: {
primary: '#AAAAAA'
},
pen: {
primary: '#FFFFFF',
secondary: '#EEEEEE',
tertiary: '#DDDDDD'
},
text: '#BBBBBB'
};
@@ -4,6 +4,11 @@ const blockColors = {
secondary: '#222222',
tertiary: '#333333'
},
pen: {
primary: '#121212',
secondary: '#232323',
tertiary: '#343434'
},
text: '#444444',
workspace: '#555555'
};
+61
View File
@@ -0,0 +1,61 @@
import {DEFAULT_THEME, getColorsForTheme} from '.';
// scratch-blocks colours has a pen property that scratch-gui uses for all extensions
const getExtensionColors = theme => getColorsForTheme(theme).pen;
/**
* Applies extension color theme to categories.
* No changes are applied if called with the default theme, allowing extensions to provide their own colors.
* These colors are not seen if the category provides a blockIconURI.
* @param {Array.<object>} dynamicBlockXML - XML for each category of extension blocks, returned from getBlocksXML
* in the vm runtime.
* @param {string} theme - Theme name
* @returns {Array.<object>} Dynamic block XML updated with colors.
*/
const injectExtensionCategoryColors = (dynamicBlockXML, theme) => {
// Don't do any manipulation for the default theme
if (theme === DEFAULT_THEME) return dynamicBlockXML;
const extensionColors = getExtensionColors(theme);
const parser = new DOMParser();
const serializer = new XMLSerializer();
return dynamicBlockXML.map(extension => {
const dom = parser.parseFromString(extension.xml, 'text/xml');
dom.documentElement.setAttribute('colour', extensionColors.primary);
// Note: the category's secondaryColour matches up with the blocks' tertiary color, both used for border color.
dom.documentElement.setAttribute('secondaryColour', extensionColors.tertiary);
return {
...extension,
xml: serializer.serializeToString(dom)
};
});
};
/**
* Applies extension color theme to static block json.
* No changes are applied if called with the default theme, allowing extensions to provide their own colors.
* @param {object} blockInfoJson - Static block json
* @param {string} theme - Theme name
* @returns {object} Block info json with updated colors. The original blockInfoJson is not modified.
*/
const injectExtensionBlockColors = (blockInfoJson, theme) => {
// Don't do any manipulation for the default theme
if (theme === DEFAULT_THEME) return blockInfoJson;
const extensionColors = getExtensionColors(theme);
return {
...blockInfoJson,
colour: extensionColors.primary,
colourSecondary: extensionColors.secondary,
colourTertiary: extensionColors.tertiary
};
};
export {
injectExtensionBlockColors,
injectExtensionCategoryColors
};
+3 -1
View File
@@ -4,12 +4,13 @@ import darkMode from './dark-mode';
import highContrast from './high-contrast';
import defaultColors from './default-colors';
const DEFAULT_THEME = 'standard';
const mergeWithDefaults = colors => defaultsDeep({}, colors, defaultColors);
const themeMap = {
'dark-mode': mergeWithDefaults(darkMode),
'high-contrast': mergeWithDefaults(highContrast),
'standard': defaultColors
[DEFAULT_THEME]: defaultColors
};
const getColorsForTheme = theme => {
@@ -25,6 +26,7 @@ const getColorsForTheme = theme => {
const themes = Object.keys(themeMap);
export {
DEFAULT_THEME,
defaultColors,
getColorsForTheme,
themes
+3 -1
View File
@@ -1,7 +1,9 @@
import {DEFAULT_THEME} from '../lib/themes';
const SET_THEME = 'scratch-gui/theme/SET_THEME';
const initialState = {
theme: 'standard'
theme: DEFAULT_THEME
};
const reducer = (state = initialState, action) => {
+40 -1
View File
@@ -1,4 +1,5 @@
import {defaultColors, getColorsForTheme} from '../../../src/lib/themes';
import {defaultColors, DEFAULT_THEME, getColorsForTheme} from '../../../src/lib/themes';
import {injectExtensionBlockColors} from '../../../src/lib/themes/blockHelpers';
jest.mock('../../../src/lib/themes/default-colors');
jest.mock('../../../src/lib/themes/dark-mode');
@@ -19,4 +20,42 @@ describe('themes', () => {
expect(colors.motion.secondary).toEqual('#222222');
});
test('updates extension blocks based on theme', () => {
const blockInfoJson = {
type: 'dummy_block',
colour: '#0FBD8C',
colourSecondary: '#0DA57A',
colourTertiary: '#0B8E69'
};
const updated = injectExtensionBlockColors(blockInfoJson, 'dark-mode');
expect(updated).toEqual({
type: 'dummy_block',
colour: '#FFFFFF',
colourSecondary: '#EEEEEE',
colourTertiary: '#DDDDDD'
});
// The original value was not modified
expect(blockInfoJson.colour).toBe('#0FBD8C');
});
test('bypasses updates if using the default theme', () => {
const blockInfoJson = {
type: 'dummy_block',
colour: '#0FBD8C',
colourSecondary: '#0DA57A',
colourTertiary: '#0B8E69'
};
const updated = injectExtensionBlockColors(blockInfoJson, DEFAULT_THEME);
expect(updated).toEqual({
type: 'dummy_block',
colour: '#0FBD8C',
colourSecondary: '#0DA57A',
colourTertiary: '#0B8E69'
});
});
});