feat: apply color theme to extension categories and blocks
This commit is contained in:
1 parent
d9baf4ca95
commit
63d43fbc78
7 files changed
+125
-7
No files matched your search
@@ -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 => ({
|
||||
|
||||
@@ -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'
|
||||
};
|
||||
|
||||
@@ -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
|
||||
};
|
||||
@@ -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
|
||||
|
||||
@@ -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) => {
|
||||
|
||||
@@ -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'
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user