Skip to content

Commit

Permalink
Merge pull request #7369 from code-dot-org/icon-color-picker
Browse files Browse the repository at this point in the history
Add color picker for Design Mode icons
  • Loading branch information
Josh Lory committed Mar 18, 2016
2 parents 53cfb6e + f4526fa commit 26954f8
Show file tree
Hide file tree
Showing 6 changed files with 98 additions and 18 deletions.
2 changes: 2 additions & 0 deletions apps/src/applab/constants.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
module.exports = {
FOOTER_HEIGHT: 30,
DESIGN_ELEMENT_ID_PREFIX: 'design_',
ICON_PREFIX: 'icon://',
ICON_PREFIX_REGEX: new RegExp('^icon://'),
NEW_SCREEN: "New screen...",
ApplabInterfaceMode: {
CODE: 'CODE',
Expand Down
38 changes: 27 additions & 11 deletions apps/src/applab/designElements/ColorPickerPropertyRow.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,29 @@ var ColorPickerPropertyRow = React.createClass({
getInitialState: function () {
return {
value: this.props.initialValue,
displayColorPicker: false,
displayColorPicker: false
};
},

handleChangeInternal: function(event) {
componentDidMount: function () {
window.addEventListener('mousedown', this.handlePageClick);
},

componentWillUnmount: function () {
window.removeEventListener('mousedown', this.handlePageClick);
},

handlePageClick: function (e) {
if (e.target === ReactDOM.findDOMNode(this.refs.button)) {
return;
}
var ref = this.refs.colorPicker;
if (ref && !ReactDOM.findDOMNode(ref).contains(e.target)) {
this.setState({displayColorPicker: false});
}
},

handleChangeInternal: function (event) {
this.changeColor(event.target.value);
},

Expand All @@ -33,23 +51,20 @@ var ColorPickerPropertyRow = React.createClass({
this.setState({value: color});
},

toggleColorPicker: function() {
toggleColorPicker: function () {
this.setState({displayColorPicker: !this.state.displayColorPicker});
},

render: function() {
render: function () {
var buttonStyle = {
backgroundColor: this.state.value,
verticalAlign: 'top'
};
let colorPicker = this.state.displayColorPicker ? (
<div style={{position: 'absolute'}}>
<div style={{position: 'fixed', top: '0', right: '0', bottom: '0', left: '0'}}
onClick={this.toggleColorPicker}></div>
<ColorPicker type="sketch"
color={this.state.value}
onChangeComplete={this.handleColorChange}/>
</div>
<ColorPicker
ref="colorPicker"
color={this.state.value}
onChangeComplete={this.handleColorChange}/>
) : null;
return (
<div style={rowStyle.container}>
Expand All @@ -60,6 +75,7 @@ var ColorPickerPropertyRow = React.createClass({
onChange={this.handleChangeInternal}
style={rowStyle.input} />
<button
ref="button"
className={this.state.value === '' ? 'rainbow-gradient' : undefined}
style={buttonStyle}
onClick={this.toggleColorPicker}>
Expand Down
19 changes: 19 additions & 0 deletions apps/src/applab/designElements/button.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ var EventHeaderRow = require('./EventHeaderRow.jsx');
var EventRow = require('./EventRow.jsx');
var EnumPropertyRow = require('./EnumPropertyRow.jsx');
var color = require('../../color');
var ICON_PREFIX_REGEX = require('../constants').ICON_PREFIX_REGEX;

var elementUtils = require('./elementUtils');

Expand All @@ -20,9 +21,26 @@ var ButtonProperties = React.createClass({
onDepthChange: React.PropTypes.func.isRequired
},

handleIconColorChange: function (value) {
this.props.handleChange('icon-color', value);
this.props.handleChange('image',
this.props.element.getAttribute('data-canonical-image-url'));
},

render: function () {
var element = this.props.element;

var iconColorPicker;
var canonicalImage = element.getAttribute('data-canonical-image-url');
if (ICON_PREFIX_REGEX.test(canonicalImage)) {
iconColorPicker = (
<ColorPickerPropertyRow
desc={'icon color'}
initialValue={elementUtils.rgb2hex(element.getAttribute('data-icon-color') || '#000000')}
handleChange={this.handleIconColorChange} />
);
}

return (
<div id='propertyRowContainer'>
<PropertyRow
Expand Down Expand Up @@ -76,6 +94,7 @@ var ButtonProperties = React.createClass({
desc={'image'}
initialValue={element.getAttribute('data-canonical-image-url') || ''}
handleChange={this.props.handleChange.bind(this, 'image')} />
{iconColorPicker}
<BooleanPropertyRow
desc={'hidden'}
initialValue={$(element).hasClass('design-mode-hidden')}
Expand Down
21 changes: 20 additions & 1 deletion apps/src/applab/designElements/image.jsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
/* global $ */


var PropertyRow = require('./PropertyRow.jsx');
var ColorPickerPropertyRow = require('./ColorPickerPropertyRow.jsx');
var BooleanPropertyRow = require('./BooleanPropertyRow.jsx');
var ImagePickerPropertyRow = require('./ImagePickerPropertyRow.jsx');
var ZOrderRow = require('./ZOrderRow.jsx');
var EventHeaderRow = require('./EventHeaderRow.jsx');
var EventRow = require('./EventRow.jsx');
var ICON_PREFIX_REGEX = require('../constants').ICON_PREFIX_REGEX;

var elementUtils = require('./elementUtils');

Expand All @@ -17,9 +18,26 @@ var ImageProperties = React.createClass({
onDepthChange: React.PropTypes.func.isRequired
},

handleIconColorChange: function (value) {
this.props.handleChange('icon-color', value);
this.props.handleChange('picture',
this.props.element.getAttribute('data-canonical-image-url'));
},

render: function () {
var element = this.props.element;

var iconColorPicker;
var canonicalImage = element.getAttribute('data-canonical-image-url');
if (ICON_PREFIX_REGEX.test(canonicalImage)) {
iconColorPicker = (
<ColorPickerPropertyRow
desc={'icon color'}
initialValue={elementUtils.rgb2hex(element.getAttribute('data-icon-color') || '#000000')}
handleChange={this.handleIconColorChange} />
);
}

return (
<div id='propertyRowContainer'>
<PropertyRow
Expand Down Expand Up @@ -51,6 +69,7 @@ var ImageProperties = React.createClass({
desc={'picture'}
initialValue={element.getAttribute('data-canonical-image-url') || ''}
handleChange={this.props.handleChange.bind(this, 'picture')} />
{iconColorPicker}
<BooleanPropertyRow
desc={'hidden'}
initialValue={$(element).hasClass('design-mode-hidden')}
Expand Down
19 changes: 19 additions & 0 deletions apps/src/applab/designElements/screen.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ var BooleanPropertyRow = require('./BooleanPropertyRow.jsx');
var EventHeaderRow = require('./EventHeaderRow.jsx');
var EventRow = require('./EventRow.jsx');
var DefaultScreenButtonPropertyRow = require('./DefaultScreenButtonPropertyRow.jsx');
var ICON_PREFIX_REGEX = require('../constants').ICON_PREFIX_REGEX;

var elementUtils = require('./elementUtils');

Expand All @@ -15,9 +16,26 @@ var ScreenProperties = React.createClass({
handleChange: React.PropTypes.func.isRequired
},

handleIconColorChange: function (value) {
this.props.handleChange('icon-color', value);
this.props.handleChange('screen-image',
this.props.element.getAttribute('data-canonical-image-url'));
},

render: function () {
var element = this.props.element;

var iconColorPicker;
var canonicalImage = element.getAttribute('data-canonical-image-url');
if (ICON_PREFIX_REGEX.test(canonicalImage)) {
iconColorPicker = (
<ColorPickerPropertyRow
desc={'icon color'}
initialValue={elementUtils.rgb2hex(element.getAttribute('data-icon-color') || '#000000')}
handleChange={this.handleIconColorChange} />
);
}

return (
<div id='propertyRowContainer'>
<PropertyRow
Expand All @@ -33,6 +51,7 @@ var ScreenProperties = React.createClass({
desc={'image'}
initialValue={element.getAttribute('data-canonical-image-url') || ''}
handleChange={this.props.handleChange.bind(this, 'screen-image')} />
{iconColorPicker}
<DefaultScreenButtonPropertyRow
screenId={elementUtils.getId(element)}
handleChange={this.props.handleChange.bind(this, 'is-default')}/>
Expand Down
17 changes: 11 additions & 6 deletions apps/src/applab/designMode.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ var logToCloud = require('../logToCloud');
var actions = require('./actions');
var icons = require('../assetManagement/icons');

var ICON_PREFIX = 'icon://';
var ICON_PREFIX_REGEX = new RegExp('^' + ICON_PREFIX);
var ICON_PREFIX = applabConstants.ICON_PREFIX;
var ICON_PREFIX_REGEX = applabConstants.ICON_PREFIX_REGEX;

var currentlyEditedElement = null;
var ApplabInterfaceMode = applabConstants.ApplabInterfaceMode;
Expand Down Expand Up @@ -178,15 +178,17 @@ designMode.onPropertyChange = function(element, name, value) {
/**
* Create a data-URI with the image data of the given icon glyph.
* @param value {string} An icon identifier of the format "icon://fa-icon-name".
* @param element {Element}
* @return {string}
*/
function renderIconToString(value) {
function renderIconToString(value, element) {
var canvas = document.createElement('canvas');
canvas.width = canvas.height = 400;
var ctx = canvas.getContext('2d');
ctx.font = '300px FontAwesome, serif';
ctx.textBaseline = 'middle';
ctx.textAlign = 'center';
ctx.fillStyle = element.getAttribute('data-icon-color');
var regex = new RegExp('^' + ICON_PREFIX + 'fa-');
var unicode = '0x' + icons.unicode[value.replace(regex, '')];
ctx.fillText(String.fromCharCode(unicode), 200, 200);
Expand Down Expand Up @@ -261,6 +263,9 @@ designMode.updateProperty = function(element, name, value) {
case 'textAlign':
element.style.textAlign = value;
break;
case 'icon-color':
element.setAttribute('data-icon-color', value);
break;
case 'image':
var originalValue = element.getAttribute('data-canonical-image-url');
element.setAttribute('data-canonical-image-url', value);
Expand All @@ -278,7 +283,7 @@ designMode.updateProperty = function(element, name, value) {
};

if (ICON_PREFIX_REGEX.test(value)) {
element.style.backgroundImage = 'url(' + renderIconToString(value) + ')';
element.style.backgroundImage = 'url(' + renderIconToString(value, element) + ')';
fitImage();
break;
}
Expand All @@ -301,7 +306,7 @@ designMode.updateProperty = function(element, name, value) {
var height = parseInt(element.style.height, 10);
element.style.backgroundSize = width + 'px ' + height + 'px';

var url = ICON_PREFIX_REGEX.test(value) ? renderIconToString(value) : assetPrefix.fixPath(value);
var url = ICON_PREFIX_REGEX.test(value) ? renderIconToString(value, element) : assetPrefix.fixPath(value);
element.style.backgroundImage = 'url(' + url + ')';

break;
Expand All @@ -311,7 +316,7 @@ designMode.updateProperty = function(element, name, value) {
element.setAttribute('data-canonical-image-url', value);

if (ICON_PREFIX_REGEX.test(value)) {
element.src = renderIconToString(value);
element.src = renderIconToString(value, element);
break;
}

Expand Down

0 comments on commit 26954f8

Please sign in to comment.