react-blockly-component
Advanced tools
+26
| module.exports = { | ||
| env: { | ||
| amd: true, | ||
| browser: true, | ||
| es6: true, | ||
| jquery: false, | ||
| node: true, | ||
| }, | ||
| globals: { | ||
| 'Blockly': true, | ||
| }, | ||
| extends: [ | ||
| "airbnb", | ||
| ], | ||
| parser: "babel-eslint", | ||
| parserOptions: { | ||
| sourceType: 'module', | ||
| ecmaVersion: 6, | ||
| ecmaFeatures: { | ||
| jsx: true, | ||
| }, | ||
| }, | ||
| }; |
| /* eslint-disable import/no-extraneous-dependencies */ | ||
| import React from 'react'; | ||
| import ReactDOM from 'react-dom'; | ||
| import ReactBlocklyComponent from './index'; | ||
| window.addEventListener('load', () => { | ||
| const editor = React.createElement(ReactBlocklyComponent.BlocklyEditor, { | ||
| workspaceConfiguration: { | ||
| grid: { | ||
| spacing: 20, | ||
| length: 3, | ||
| colour: '#ccc', | ||
| snap: true | ||
| } | ||
| }, | ||
| toolboxCategories: [{ | ||
| name: 'Controls', | ||
| blocks: [{ type: 'controls_if' }, { | ||
| type: 'controls_repeat_ext', | ||
| values: { | ||
| TIMES: { | ||
| type: 'math_number', | ||
| shadow: true, | ||
| fields: { | ||
| NUM: 10 | ||
| } | ||
| } | ||
| }, | ||
| statements: { | ||
| DO: { | ||
| type: 'text_print', | ||
| shadow: true, | ||
| values: { | ||
| TEXT: { | ||
| type: 'text', | ||
| shadow: true, | ||
| fields: { | ||
| TEXT: 'abc' | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
| }] | ||
| }, { | ||
| name: 'Text', | ||
| blocks: [{ type: 'text' }, { | ||
| type: 'text_print', | ||
| values: { | ||
| TEXT: { | ||
| type: 'text', | ||
| shadow: true, | ||
| fields: { | ||
| TEXT: 'abc' | ||
| } | ||
| } | ||
| } | ||
| }] | ||
| }], | ||
| initialXml: '<xml xmlns="http://www.w3.org/1999/xhtml"><block type="text" x="70" y="30"><field name="TEXT"></field></block></xml>', | ||
| wrapperDivClassName: 'fill-height', | ||
| workspaceDidChange: workspace => { | ||
| const newXml = Blockly.Xml.domToText(Blockly.Xml.workspaceToDom(workspace)); | ||
| document.getElementById('generated-xml').innerText = newXml; | ||
| const code = Blockly.JavaScript.workspaceToCode(workspace); | ||
| document.getElementById('code').value = code; | ||
| } | ||
| }); | ||
| ReactDOM.render(editor, document.getElementById('blockly')); | ||
| }); |
| /* eslint-disable import/no-extraneous-dependencies */ | ||
| import React from 'react'; | ||
| import ReactDOM from 'react-dom'; | ||
| import ReactBlocklyComponent from './index'; | ||
| window.addEventListener('load', () => { | ||
| const editor = React.createElement(ReactBlocklyComponent.BlocklyEditor, { | ||
| workspaceConfiguration: { | ||
| grid: { | ||
| spacing: 20, | ||
| length: 3, | ||
| colour: '#ccc', | ||
| snap: true, | ||
| }, | ||
| }, | ||
| toolboxCategories: [ | ||
| { | ||
| name: 'Controls', | ||
| blocks: [ | ||
| { type: 'controls_if' }, | ||
| { | ||
| type: 'controls_repeat_ext', | ||
| values: { | ||
| TIMES: { | ||
| type: 'math_number', | ||
| shadow: true, | ||
| fields: { | ||
| NUM: 10, | ||
| }, | ||
| }, | ||
| }, | ||
| statements: { | ||
| DO: { | ||
| type: 'text_print', | ||
| shadow: true, | ||
| values: { | ||
| TEXT: { | ||
| type: 'text', | ||
| shadow: true, | ||
| fields: { | ||
| TEXT: 'abc', | ||
| }, | ||
| }, | ||
| }, | ||
| }, | ||
| }, | ||
| }, | ||
| ], | ||
| }, | ||
| { | ||
| name: 'Text', | ||
| blocks: [ | ||
| { type: 'text' }, | ||
| { | ||
| type: 'text_print', | ||
| values: { | ||
| TEXT: { | ||
| type: 'text', | ||
| shadow: true, | ||
| fields: { | ||
| TEXT: 'abc', | ||
| }, | ||
| }, | ||
| }, | ||
| }, | ||
| ], | ||
| }, | ||
| ], | ||
| initialXml: '<xml xmlns="http://www.w3.org/1999/xhtml"><block type="text" x="70" y="30"><field name="TEXT"></field></block></xml>', | ||
| wrapperDivClassName: 'fill-height', | ||
| workspaceDidChange: (workspace) => { | ||
| const newXml = Blockly.Xml.domToText(Blockly.Xml.workspaceToDom(workspace)); | ||
| document.getElementById('generated-xml').innerText = newXml; | ||
| const code = Blockly.JavaScript.workspaceToCode(workspace); | ||
| document.getElementById('code').value = code; | ||
| }, | ||
| }); | ||
| ReactDOM.render(editor, document.getElementById('blockly')); | ||
| }); |
+1
-4
| { | ||
| "presets": [ | ||
| "stage-0", | ||
| "react", | ||
| "es2015" | ||
| ], | ||
| "plugins": [ | ||
| "transform-object-assign" | ||
| ] | ||
| } |
+7
-0
@@ -0,1 +1,8 @@ | ||
| # Version 3.0.0 - December 29, 2017 | ||
| * Support React 16. Drop support for React 0.14.x. | ||
| * Deprecate `xmlDidChange` in favor of `workspaceDidChange`, which gives you access to the workspace object itself to allow generating code for any language including XML (thanks @Xaptor!) | ||
| * Stop depending on ReactDOM in favor of callback refs. | ||
| * Major internal cleanups to pass AirBnB's eslint configuration. | ||
| # Version 2.0.1 - November 22, 2016 | ||
@@ -2,0 +9,0 @@ |
@@ -1,96 +0,123 @@ | ||
| 'use strict'; | ||
| var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; | ||
| Object.defineProperty(exports, "__esModule", { | ||
| value: true | ||
| }); | ||
| import React from 'react'; | ||
| import PropTypes from 'prop-types'; | ||
| import Immutable from 'immutable'; | ||
| var _react = require('react'); | ||
| import BlocklyToolbox from './BlocklyToolbox'; | ||
| import BlocklyWorkspace from './BlocklyWorkspace'; | ||
| var _react2 = _interopRequireDefault(_react); | ||
| const BlockPropType = PropTypes.shape({ | ||
| type: PropTypes.string.isRequired, | ||
| shadow: PropTypes.bool, | ||
| fields: PropTypes.object, | ||
| values: PropTypes.object, | ||
| statements: PropTypes.object, | ||
| next: PropTypes.object, | ||
| mutation: PropTypes.shape({ | ||
| attributes: PropTypes.object, | ||
| innerContent: PropTypes.string | ||
| }) | ||
| }); | ||
| var _reactDom = require('react-dom'); | ||
| const categoryPropsNonRecursive = { | ||
| type: PropTypes.string.isRequired, | ||
| name: PropTypes.string, | ||
| custom: PropTypes.string, | ||
| colour: PropTypes.string, | ||
| blocks: PropTypes.arrayOf(BlockPropType) | ||
| }; | ||
| var _reactDom2 = _interopRequireDefault(_reactDom); | ||
| const CategoryPropType = PropTypes.shape(_extends({}, categoryPropsNonRecursive, { | ||
| categories: PropTypes.arrayOf(PropTypes.shape(categoryPropsNonRecursive)) | ||
| })); | ||
| var _immutable = require('immutable'); | ||
| class BlocklyEditor extends React.Component { | ||
| constructor(...args) { | ||
| var _temp; | ||
| var _immutable2 = _interopRequireDefault(_immutable); | ||
| return _temp = super(...args), this.componentDidMount = () => { | ||
| this.toolboxDidUpdate(); | ||
| var _BlocklyToolbox = require('./BlocklyToolbox'); | ||
| if (this.props.xmlDidChange) { | ||
| if (typeof console !== 'undefined') { | ||
| // eslint-disable-next-line no-console | ||
| console.error('Warning: xmlDidChange is deprecated and will be removed in future versions! Please use workspaceDidChange instead.'); | ||
| } | ||
| } | ||
| }, this.toolboxDidUpdate = () => { | ||
| const workspaceConfiguration = this.props.workspaceConfiguration || {}; | ||
| if (this.workspace && !workspaceConfiguration.readOnly) { | ||
| this.workspace.toolboxDidUpdate(this.toolbox.getRootNode()); | ||
| } | ||
| }, this.xmlDidChange = newXml => { | ||
| if (this.props.xmlDidChange) { | ||
| this.props.xmlDidChange(newXml); | ||
| } | ||
| }, this.workspaceDidChange = workspace => { | ||
| if (this.props.workspaceDidChange) { | ||
| this.props.workspaceDidChange(workspace); | ||
| } | ||
| }, this.importFromXml = xml => this.workspace.importFromXml(xml), this.resize = () => { | ||
| this.workspace.resize(); | ||
| }, this.render = () => { | ||
| let toolboxMode; | ||
| if (this.props.toolboxCategories) { | ||
| toolboxMode = 'CATEGORIES'; | ||
| } else if (this.props.toolboxBlocks) { | ||
| toolboxMode = 'BLOCKS'; | ||
| } | ||
| var _BlocklyToolbox2 = _interopRequireDefault(_BlocklyToolbox); | ||
| return React.createElement( | ||
| 'div', | ||
| { className: this.props.wrapperDivClassName }, | ||
| React.createElement(BlocklyToolbox, { | ||
| categories: Immutable.fromJS(this.props.toolboxCategories), | ||
| blocks: Immutable.fromJS(this.props.toolboxBlocks), | ||
| didUpdate: this.toolboxDidUpdate, | ||
| processCategory: this.props.processToolboxCategory, | ||
| ref: toolbox => { | ||
| this.toolbox = toolbox; | ||
| } | ||
| }), | ||
| React.createElement(BlocklyWorkspace, { | ||
| ref: workspace => { | ||
| this.workspace = workspace; | ||
| }, | ||
| initialXml: this.props.initialXml, | ||
| onImportXmlError: this.props.onImportXmlError, | ||
| toolboxMode: toolboxMode, | ||
| xmlDidChange: this.xmlDidChange, | ||
| workspaceDidChange: this.workspaceDidChange, | ||
| wrapperDivClassName: this.props.wrapperDivClassName, | ||
| workspaceConfiguration: this.props.workspaceConfiguration | ||
| }) | ||
| ); | ||
| }, _temp; | ||
| } | ||
| var _BlocklyWorkspace = require('./BlocklyWorkspace'); | ||
| } | ||
| var _BlocklyWorkspace2 = _interopRequireDefault(_BlocklyWorkspace); | ||
| function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
| var BlocklyEditor = _react2.default.createClass({ | ||
| displayName: 'BlocklyEditor', | ||
| propTypes: { | ||
| initialXml: _react2.default.PropTypes.string, | ||
| workspaceConfiguration: _react2.default.PropTypes.object, | ||
| wrapperDivClassName: _react2.default.PropTypes.string, | ||
| toolboxCategories: _react2.default.PropTypes.array, | ||
| toolboxBlocks: _react2.default.PropTypes.array, | ||
| xmlDidChange: _react2.default.PropTypes.func, | ||
| onImportXmlError: _react2.default.PropTypes.func, | ||
| processToolboxCategory: _react2.default.PropTypes.func | ||
| }, | ||
| toolboxDidUpdate: function toolboxDidUpdate() { | ||
| var workspaceConfiguration = this.props.workspaceConfiguration || {}; | ||
| if (this.refs.workspace && !workspaceConfiguration.readOnly) { | ||
| this.refs.workspace.toolboxDidUpdate(_reactDom2.default.findDOMNode(this.refs.toolbox)); | ||
| } | ||
| }, | ||
| componentDidMount: function componentDidMount() { | ||
| this.toolboxDidUpdate(); | ||
| }, | ||
| xmlDidChange: function xmlDidChange(newXml) { | ||
| if (this.props.xmlDidChange) { | ||
| this.props.xmlDidChange(newXml); | ||
| } | ||
| }, | ||
| importFromXml: function importFromXml(xml) { | ||
| return this.refs.workspace.importFromXml(xml); | ||
| }, | ||
| resize: function resize() { | ||
| this.refs.workspace.resize(); | ||
| }, | ||
| render: function render() { | ||
| var toolboxMode; | ||
| if (this.props.toolboxCategories) { | ||
| toolboxMode = "CATEGORIES"; | ||
| } else if (this.props.toolboxBlocks) { | ||
| toolboxMode = "BLOCKS"; | ||
| } | ||
| return _react2.default.createElement( | ||
| 'div', | ||
| { className: this.props.wrapperDivClassName }, | ||
| _react2.default.createElement(_BlocklyToolbox2.default, { | ||
| categories: _immutable2.default.fromJS(this.props.toolboxCategories), | ||
| blocks: _immutable2.default.fromJS(this.props.toolboxBlocks), | ||
| didUpdate: this.toolboxDidUpdate, | ||
| processCategory: this.props.processToolboxCategory, | ||
| ref: 'toolbox' }), | ||
| _react2.default.createElement(_BlocklyWorkspace2.default, { ref: 'workspace', | ||
| initialXml: this.props.initialXml, | ||
| onImportXmlError: this.props.onImportXmlError, | ||
| toolboxMode: toolboxMode, | ||
| xmlDidChange: this.xmlDidChange, | ||
| wrapperDivClassName: this.props.wrapperDivClassName, | ||
| workspaceConfiguration: this.props.workspaceConfiguration }) | ||
| ); | ||
| } | ||
| }); | ||
| exports.default = BlocklyEditor; | ||
| BlocklyEditor.propTypes = { | ||
| initialXml: PropTypes.string, | ||
| workspaceConfiguration: PropTypes.object, // eslint-disable-line react/forbid-prop-types | ||
| wrapperDivClassName: PropTypes.string, | ||
| toolboxCategories: PropTypes.arrayOf(CategoryPropType.isRequired), | ||
| toolboxBlocks: PropTypes.arrayOf(BlockPropType.isRequired), | ||
| xmlDidChange: PropTypes.func, | ||
| workspaceDidChange: PropTypes.func, | ||
| onImportXmlError: PropTypes.func, | ||
| processToolboxCategory: PropTypes.func | ||
| }; | ||
| BlocklyEditor.defaultProps = { | ||
| initialXml: null, | ||
| workspaceConfiguration: null, | ||
| wrapperDivClassName: null, | ||
| toolboxCategories: null, | ||
| toolboxBlocks: null, | ||
| xmlDidChange: null, | ||
| workspaceDidChange: null, | ||
| onImportXmlError: null, | ||
| processToolboxCategory: null | ||
| }; | ||
| export default BlocklyEditor; |
@@ -1,100 +0,79 @@ | ||
| 'use strict'; | ||
| /* eslint-disable react/no-array-index-key */ | ||
| Object.defineProperty(exports, "__esModule", { | ||
| value: true | ||
| }); | ||
| import React from 'react'; | ||
| import PropTypes from 'prop-types'; | ||
| import { is } from 'immutable'; | ||
| import ImmutablePropTypes from 'react-immutable-proptypes'; | ||
| var _react = require('react'); | ||
| import BlocklyToolboxCategory from './BlocklyToolboxCategory'; | ||
| import BlocklyToolboxBlock from './BlocklyToolboxBlock'; | ||
| var _react2 = _interopRequireDefault(_react); | ||
| class BlocklyToolbox extends React.Component { | ||
| constructor(...args) { | ||
| var _temp; | ||
| var _immutable = require('immutable'); | ||
| return _temp = super(...args), this.componentDidMount = () => { | ||
| this.props.didUpdate(); | ||
| }, this.shouldComponentUpdate = nextProps => !(is(nextProps.categories, this.props.categories) && is(nextProps.blocks, this.props.blocks)), this.componentDidUpdate = () => { | ||
| this.props.didUpdate(); | ||
| }, this.getRootNode = () => this.rootNode, this.processCategory = category => { | ||
| let processedCategory = category; | ||
| var _reactImmutableProptypes = require('react-immutable-proptypes'); | ||
| if (processedCategory.has('categories')) { | ||
| processedCategory = category.update('categories', subcategories => subcategories.map(this.processCategory)); | ||
| } | ||
| var _reactImmutableProptypes2 = _interopRequireDefault(_reactImmutableProptypes); | ||
| if (this.props.processCategory) { | ||
| return this.props.processCategory(processedCategory); | ||
| } | ||
| var _BlocklyToolboxCategory = require('./BlocklyToolboxCategory'); | ||
| var _BlocklyToolboxCategory2 = _interopRequireDefault(_BlocklyToolboxCategory); | ||
| var _BlocklyToolboxBlock = require('./BlocklyToolboxBlock'); | ||
| var _BlocklyToolboxBlock2 = _interopRequireDefault(_BlocklyToolboxBlock); | ||
| function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
| var BlocklyToolbox = _react2.default.createClass({ | ||
| displayName: 'BlocklyToolbox', | ||
| propTypes: { | ||
| categories: _reactImmutableProptypes2.default.list, | ||
| blocks: _reactImmutableProptypes2.default.list, | ||
| processCategory: _react2.default.PropTypes.func, | ||
| didUpdate: _react2.default.PropTypes.func | ||
| }, | ||
| renderCategories: function renderCategories(categories) { | ||
| return categories.map(function (category, i) { | ||
| return processedCategory; | ||
| }, this.renderCategories = categories => categories.map((category, i) => { | ||
| if (category.get('type') === 'sep') { | ||
| return _react2.default.createElement('sep', { key: "sep_" + i }); | ||
| return React.createElement('sep', { key: `sep_${i}` }); | ||
| } else if (category.get('type') === 'search') { | ||
| return _react2.default.createElement('search', { key: "search_" + i }); | ||
| } else { | ||
| return _react2.default.createElement(_BlocklyToolboxCategory2.default, { | ||
| name: category.get('name'), | ||
| custom: category.get('custom'), | ||
| colour: category.get('colour'), | ||
| key: "category_" + category.get('name') + "_" + i, | ||
| blocks: category.get('blocks'), | ||
| categories: category.get('categories') }); | ||
| return React.createElement('search', { key: `search_${i}` }); | ||
| } | ||
| }.bind(this)); | ||
| }, | ||
| shouldComponentUpdate: function shouldComponentUpdate(nextProps, nextState) { | ||
| return !((0, _immutable.is)(nextProps.categories, this.props.categories) && (0, _immutable.is)(nextProps.blocks, this.props.blocks)); | ||
| }, | ||
| componentDidMount: function componentDidMount() { | ||
| this.props.didUpdate(); | ||
| }, | ||
| componentDidUpdate: function componentDidUpdate(prevProps, prevState) { | ||
| this.props.didUpdate(); | ||
| }, | ||
| processCategory: function processCategory(category) { | ||
| var processedCategory = category; | ||
| if (processedCategory.has('categories')) { | ||
| processedCategory = category.update('categories', function (subcategories) { | ||
| return subcategories.map(this.processCategory); | ||
| }.bind(this)); | ||
| } | ||
| if (this.props.processCategory) { | ||
| return this.props.processCategory(processedCategory); | ||
| } | ||
| return processedCategory; | ||
| }, | ||
| render: function render() { | ||
| if (this.props.categories) { | ||
| return _react2.default.createElement( | ||
| return React.createElement(BlocklyToolboxCategory, { | ||
| name: category.get('name'), | ||
| custom: category.get('custom'), | ||
| colour: category.get('colour'), | ||
| key: `category_${category.get('name')}_${i}`, | ||
| blocks: category.get('blocks'), | ||
| categories: category.get('categories') | ||
| }); | ||
| }), this.render = () => { | ||
| if (this.props.categories) { | ||
| return React.createElement( | ||
| 'xml', | ||
| { style: { display: 'none' }, ref: node => { | ||
| this.rootNode = node; | ||
| } }, | ||
| this.renderCategories(this.props.categories.map(this.processCategory)) | ||
| ); | ||
| } | ||
| return React.createElement( | ||
| 'xml', | ||
| { style: { display: "none" } }, | ||
| this.renderCategories(this.props.categories.map(this.processCategory)) | ||
| { style: { display: 'none' }, ref: node => { | ||
| this.rootNode = node; | ||
| } }, | ||
| this.props.blocks.map(BlocklyToolboxBlock.renderBlock) | ||
| ); | ||
| } else { | ||
| return _react2.default.createElement( | ||
| 'xml', | ||
| { style: { display: "none" } }, | ||
| this.props.blocks.map(_BlocklyToolboxBlock2.default.renderBlock) | ||
| ); | ||
| } | ||
| }, _temp; | ||
| } | ||
| }); | ||
| exports.default = BlocklyToolbox; | ||
| } | ||
| BlocklyToolbox.propTypes = { | ||
| categories: ImmutablePropTypes.list, | ||
| blocks: ImmutablePropTypes.list, | ||
| processCategory: PropTypes.func, | ||
| didUpdate: PropTypes.func | ||
| }; | ||
| BlocklyToolbox.defaultProps = { | ||
| categories: null, | ||
| blocks: null, | ||
| processCategory: null, | ||
| didUpdate: null | ||
| }; | ||
| export default BlocklyToolbox; |
@@ -1,129 +0,80 @@ | ||
| 'use strict'; | ||
| /* eslint-disable react/no-array-index-key */ | ||
| Object.defineProperty(exports, "__esModule", { | ||
| value: true | ||
| }); | ||
| import React from 'react'; | ||
| import PropTypes from 'prop-types'; | ||
| import ImmutablePropTypes from 'react-immutable-proptypes'; | ||
| var _react = require('react'); | ||
| class BlocklyToolboxBlock extends React.PureComponent { | ||
| constructor(...args) { | ||
| var _temp; | ||
| var _react2 = _interopRequireDefault(_react); | ||
| return _temp = super(...args), this.componentDidMount = () => { | ||
| if (this.props.mutation) { | ||
| this.props.mutation.get('attributes').forEach((value, attributeName) => { | ||
| this.mutationElement.setAttribute(attributeName, value); | ||
| return true; | ||
| }); | ||
| } | ||
| }, this.render = () => { | ||
| let fields = []; | ||
| let values = []; | ||
| let statements = []; | ||
| let mutation = null; | ||
| let nextBlock = null; | ||
| var _reactDom = require('react-dom'); | ||
| var _reactDom2 = _interopRequireDefault(_reactDom); | ||
| var _reactImmutableProptypes = require('react-immutable-proptypes'); | ||
| var _reactImmutableProptypes2 = _interopRequireDefault(_reactImmutableProptypes); | ||
| var _reactImmutableRenderMixin = require('react-immutable-render-mixin'); | ||
| var _reactImmutableRenderMixin2 = _interopRequireDefault(_reactImmutableRenderMixin); | ||
| function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
| var BlocklyToolboxBlock = _react2.default.createClass({ | ||
| displayName: 'BlocklyToolboxBlock', | ||
| mixins: [_reactImmutableRenderMixin2.default], | ||
| propTypes: { | ||
| type: _react2.default.PropTypes.string.isRequired, | ||
| shadow: _react2.default.PropTypes.bool, | ||
| fields: _reactImmutableProptypes2.default.map, | ||
| values: _reactImmutableProptypes2.default.map, | ||
| statements: _reactImmutableProptypes2.default.map, | ||
| next: _reactImmutableProptypes2.default.map, | ||
| mutation: _reactImmutableProptypes2.default.mapContains({ | ||
| attributes: _reactImmutableProptypes2.default.map, | ||
| innerContent: _react2.default.PropTypes.string | ||
| }) | ||
| }, | ||
| statics: { | ||
| renderBlock: function renderBlock(block, key) { | ||
| return _react2.default.createElement(BlocklyToolboxBlock, { | ||
| type: block.get('type'), | ||
| key: key, | ||
| fields: block.get('fields'), | ||
| values: block.get('values'), | ||
| statements: block.get('statements'), | ||
| mutation: block.get('mutation'), | ||
| shadow: block.get('shadow'), | ||
| next: block.get('next') }); | ||
| } | ||
| }, | ||
| componentDidMount: function componentDidMount() { | ||
| if (this.props.mutation) { | ||
| var mutation = _reactDom2.default.findDOMNode(this.refs.mutation); | ||
| this.props.mutation.get('attributes').forEach(function (value, attributeName) { | ||
| mutation.setAttribute(attributeName, value); | ||
| return true; | ||
| }); | ||
| } | ||
| }, | ||
| render: function render() { | ||
| var fields = []; | ||
| var values = []; | ||
| var statements = []; | ||
| var mutation = null; | ||
| var nextBlock = null; | ||
| if (this.props.fields) { | ||
| fields = this.props.fields.map(function (fieldValue, fieldName, i) { | ||
| return _react2.default.createElement( | ||
| if (this.props.fields) { | ||
| fields = this.props.fields.map((fieldValue, fieldName, i) => React.createElement( | ||
| 'field', | ||
| { name: fieldName, key: "field_" + fieldName + "_" + i }, | ||
| { name: fieldName, key: `field_${fieldName}_${i}` }, | ||
| fieldValue | ||
| ); | ||
| }.bind(this)).valueSeq(); | ||
| } | ||
| )).valueSeq(); | ||
| } | ||
| if (this.props.values) { | ||
| values = this.props.values.map(function (valueBlock, valueName, i) { | ||
| return _react2.default.createElement( | ||
| if (this.props.values) { | ||
| values = this.props.values.map((valueBlock, valueName, i) => React.createElement( | ||
| 'value', | ||
| { name: valueName, key: "value_" + valueName + "_" + i }, | ||
| { name: valueName, key: `value_${valueName}_${i}` }, | ||
| BlocklyToolboxBlock.renderBlock(valueBlock) | ||
| ); | ||
| }.bind(this)).valueSeq(); | ||
| } | ||
| )).valueSeq(); | ||
| } | ||
| if (this.props.statements) { | ||
| statements = this.props.statements.map(function (statementBlock, statementName, i) { | ||
| return _react2.default.createElement( | ||
| if (this.props.statements) { | ||
| statements = this.props.statements.map((statementBlock, statementName, i) => React.createElement( | ||
| 'statement', | ||
| { name: statementName, key: "statement_" + statementName + "_" + i }, | ||
| { name: statementName, key: `statement_${statementName}_${i}` }, | ||
| BlocklyToolboxBlock.renderBlock(statementBlock) | ||
| )).valueSeq(); | ||
| } | ||
| if (this.props.mutation) { | ||
| mutation = React.createElement('mutation', { | ||
| // eslint-disable-next-line react/no-danger | ||
| dangerouslySetInnerHTML: { __html: this.props.mutation.get('innerContent') }, | ||
| ref: mutationElement => { | ||
| this.mutationElement = mutationElement; | ||
| } | ||
| }); | ||
| } | ||
| if (this.props.next) { | ||
| nextBlock = React.createElement( | ||
| 'next', | ||
| null, | ||
| BlocklyToolboxBlock.renderBlock(this.props.next) | ||
| ); | ||
| }.bind(this)).valueSeq(); | ||
| } | ||
| } | ||
| if (this.props.mutation) { | ||
| mutation = _react2.default.createElement('mutation', { dangerouslySetInnerHTML: { __html: this.props.mutation.get('innerContent') }, ref: 'mutation' }); | ||
| } | ||
| if (this.props.shadow) { | ||
| return React.createElement( | ||
| 'shadow', | ||
| { type: this.props.type }, | ||
| mutation, | ||
| fields, | ||
| values, | ||
| statements, | ||
| nextBlock | ||
| ); | ||
| } | ||
| if (this.props.next) { | ||
| nextBlock = _react2.default.createElement( | ||
| 'next', | ||
| null, | ||
| BlocklyToolboxBlock.renderBlock(this.props.next) | ||
| ); | ||
| } | ||
| if (this.props.shadow) { | ||
| return _react2.default.createElement( | ||
| 'shadow', | ||
| { type: this.props.type }, | ||
| mutation, | ||
| fields, | ||
| values, | ||
| statements, | ||
| nextBlock | ||
| ); | ||
| } else { | ||
| return _react2.default.createElement( | ||
| return React.createElement( | ||
| 'block', | ||
@@ -137,6 +88,39 @@ { type: this.props.type }, | ||
| ); | ||
| } | ||
| }, _temp; | ||
| } | ||
| } | ||
| BlocklyToolboxBlock.propTypes = { | ||
| type: PropTypes.string.isRequired, | ||
| shadow: PropTypes.bool, | ||
| fields: ImmutablePropTypes.map, | ||
| values: ImmutablePropTypes.map, | ||
| statements: ImmutablePropTypes.map, | ||
| next: ImmutablePropTypes.map, | ||
| mutation: ImmutablePropTypes.mapContains({ | ||
| attributes: ImmutablePropTypes.map, | ||
| innerContent: PropTypes.string | ||
| }) | ||
| }; | ||
| BlocklyToolboxBlock.defaultProps = { | ||
| shadow: false, | ||
| fields: null, | ||
| values: null, | ||
| statements: null, | ||
| next: null, | ||
| mutation: null | ||
| }; | ||
| BlocklyToolboxBlock.renderBlock = (block, key) => React.createElement(BlocklyToolboxBlock, { | ||
| type: block.get('type'), | ||
| key: key, | ||
| fields: block.get('fields'), | ||
| values: block.get('values'), | ||
| statements: block.get('statements'), | ||
| mutation: block.get('mutation'), | ||
| shadow: block.get('shadow'), | ||
| next: block.get('next') | ||
| }); | ||
| exports.default = BlocklyToolboxBlock; | ||
| export default BlocklyToolboxBlock; |
@@ -1,69 +0,57 @@ | ||
| 'use strict'; | ||
| import React from 'react'; | ||
| import PropTypes from 'prop-types'; | ||
| import ImmutablePropTypes from 'react-immutable-proptypes'; | ||
| Object.defineProperty(exports, "__esModule", { | ||
| value: true | ||
| }); | ||
| import BlocklyToolboxBlock from './BlocklyToolboxBlock'; | ||
| var _react = require('react'); | ||
| class BlocklyToolboxCategory extends React.PureComponent { | ||
| constructor(...args) { | ||
| var _temp; | ||
| var _react2 = _interopRequireDefault(_react); | ||
| return _temp = super(...args), this.render = () => { | ||
| const subcategories = (this.props.categories || []).map(BlocklyToolboxCategory.renderCategory); | ||
| const blocks = (this.props.blocks || []).map(BlocklyToolboxBlock.renderBlock); | ||
| var _reactImmutableProptypes = require('react-immutable-proptypes'); | ||
| return React.createElement( | ||
| 'category', | ||
| { name: this.props.name, custom: this.props.custom, colour: this.props.colour }, | ||
| blocks, | ||
| subcategories | ||
| ); | ||
| }, _temp; | ||
| } | ||
| var _reactImmutableProptypes2 = _interopRequireDefault(_reactImmutableProptypes); | ||
| } | ||
| var _reactImmutableRenderMixin = require('react-immutable-render-mixin'); | ||
| BlocklyToolboxCategory.propTypes = { | ||
| name: PropTypes.string, | ||
| custom: PropTypes.string, | ||
| colour: PropTypes.string, | ||
| categories: ImmutablePropTypes.list, | ||
| blocks: ImmutablePropTypes.list | ||
| }; | ||
| BlocklyToolboxCategory.defaultProps = { | ||
| name: null, | ||
| custom: null, | ||
| colour: null, | ||
| categories: null, | ||
| blocks: null | ||
| }; | ||
| var _reactImmutableRenderMixin2 = _interopRequireDefault(_reactImmutableRenderMixin); | ||
| var _BlocklyToolboxBlock = require('./BlocklyToolboxBlock'); | ||
| var _BlocklyToolboxBlock2 = _interopRequireDefault(_BlocklyToolboxBlock); | ||
| function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
| var BlocklyToolboxCategory = _react2.default.createClass({ | ||
| displayName: 'BlocklyToolboxCategory', | ||
| mixins: [_reactImmutableRenderMixin2.default], | ||
| propTypes: { | ||
| name: _react2.default.PropTypes.string, | ||
| custom: _react2.default.PropTypes.string, | ||
| colour: _react2.default.PropTypes.string, | ||
| categories: _reactImmutableProptypes2.default.list, | ||
| blocks: _reactImmutableProptypes2.default.list | ||
| }, | ||
| statics: { | ||
| renderCategory: function renderCategory(category, key) { | ||
| if (category.get('type') === 'sep') { | ||
| return _react2.default.createElement('sep', { key: key }); | ||
| } else if (category.get('type') === 'search') { | ||
| return _react2.default.createElement('search', { key: key }); | ||
| } else { | ||
| return _react2.default.createElement(BlocklyToolboxCategory, { | ||
| name: category.get('name'), | ||
| custom: category.get('custom'), | ||
| colour: category.get('colour'), | ||
| key: key, | ||
| blocks: category.get('blocks'), | ||
| categories: category.get('categories') }); | ||
| } | ||
| } | ||
| }, | ||
| render: function render() { | ||
| var subcategories = (this.props.categories || []).map(BlocklyToolboxCategory.renderCategory); | ||
| var blocks = (this.props.blocks || []).map(_BlocklyToolboxBlock2.default.renderBlock); | ||
| return _react2.default.createElement( | ||
| 'category', | ||
| { is: true, name: this.props.name, custom: this.props.custom, colour: this.props.colour, ref: 'category' }, | ||
| blocks, | ||
| subcategories | ||
| ); | ||
| BlocklyToolboxCategory.renderCategory = (category, key) => { | ||
| if (category.get('type') === 'sep') { | ||
| return React.createElement('sep', { key: key }); | ||
| } else if (category.get('type') === 'search') { | ||
| return React.createElement('search', { key: key }); | ||
| } | ||
| }); | ||
| return React.createElement(BlocklyToolboxCategory, { | ||
| name: category.get('name'), | ||
| custom: category.get('custom'), | ||
| colour: category.get('colour'), | ||
| key: key, | ||
| blocks: category.get('blocks'), | ||
| categories: category.get('categories') | ||
| }); | ||
| }; | ||
| exports.default = BlocklyToolboxCategory; | ||
| export default BlocklyToolboxCategory; |
+122
-111
@@ -1,25 +0,12 @@ | ||
| 'use strict'; | ||
| Object.defineProperty(exports, "__esModule", { | ||
| value: true | ||
| }); | ||
| var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; | ||
| var _react = require('react'); | ||
| import React from 'react'; | ||
| import PropTypes from 'prop-types'; | ||
| var _react2 = _interopRequireDefault(_react); | ||
| function debounce(func, wait) { | ||
| let timeout; | ||
| var _reactDom = require('react-dom'); | ||
| var _reactDom2 = _interopRequireDefault(_reactDom); | ||
| function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
| var debounce = function debounce(func, wait) { | ||
| var timeout; | ||
| return function () { | ||
| var context = this, | ||
| args = arguments; | ||
| var later = function later() { | ||
| return function debouncedFunction(...args) { | ||
| const context = this; | ||
| const later = function later() { | ||
| timeout = null; | ||
@@ -31,112 +18,136 @@ func.apply(context, args); | ||
| }; | ||
| }; | ||
| } | ||
| var BlocklyWorkspace = _react2.default.createClass({ | ||
| displayName: 'BlocklyWorkspace', | ||
| class BlocklyWorkspace extends React.Component { | ||
| propTypes: { | ||
| initialXml: _react2.default.PropTypes.string, | ||
| workspaceConfiguration: _react2.default.PropTypes.object, | ||
| wrapperDivClassName: _react2.default.PropTypes.string, | ||
| xmlDidChange: _react2.default.PropTypes.func, | ||
| onImportXmlError: _react2.default.PropTypes.func, | ||
| toolboxMode: _react2.default.PropTypes.oneOf(['CATEGORIES', 'BLOCKS']) | ||
| }, | ||
| constructor(props) { | ||
| super(props); | ||
| getInitialState: function getInitialState() { | ||
| return { | ||
| workspace: null, | ||
| xml: this.props.initialXml | ||
| }; | ||
| }, | ||
| this.componentDidMount = () => { | ||
| // TODO figure out how to use setState here without breaking the toolbox when switching tabs | ||
| this.state.workspace = Blockly.inject(this.editorDiv, _extends({}, this.props.workspaceConfiguration, { | ||
| toolbox: this.dummyToolbox | ||
| })); | ||
| componentDidMount: function componentDidMount() { | ||
| // TODO figure out how to use setState here without breaking the toolbox when switching tabs | ||
| this.state.workspace = Blockly.inject(this.refs.editorDiv, _extends({}, this.props.workspaceConfiguration || {}, { | ||
| toolbox: _reactDom2.default.findDOMNode(this.refs.dummyToolbox) | ||
| })); | ||
| if (this.state.xml) { | ||
| if (this.importFromXml(this.state.xml)) { | ||
| this.xmlDidChange(); | ||
| } else { | ||
| this.setState({ xml: null }, this.xmlDidChange); | ||
| if (this.state.xml) { | ||
| if (this.importFromXml(this.state.xml)) { | ||
| this.xmlDidChange(); | ||
| } else { | ||
| this.setState({ xml: null }, this.xmlDidChange); | ||
| } | ||
| } | ||
| } | ||
| this.state.workspace.addChangeListener(debounce(function () { | ||
| var newXml = Blockly.Xml.domToText(Blockly.Xml.workspaceToDom(this.state.workspace)); | ||
| if (newXml == this.state.xml) { | ||
| return; | ||
| this.state.workspace.addChangeListener(this.workspaceDidChange); | ||
| this.state.workspace.addChangeListener(debounce(() => { | ||
| const newXml = Blockly.Xml.domToText(Blockly.Xml.workspaceToDom(this.state.workspace)); | ||
| if (newXml === this.state.xml) { | ||
| return; | ||
| } | ||
| this.setState({ xml: newXml }, this.xmlDidChange); | ||
| }, 200)); | ||
| }; | ||
| this.componentWillReceiveProps = newProps => { | ||
| if (this.props.initialXml !== newProps.initialXml) { | ||
| this.setState({ xml: newProps.initialXml }); | ||
| } | ||
| }; | ||
| this.setState({ xml: newXml }, this.xmlDidChange); | ||
| }.bind(this), 200)); | ||
| }, | ||
| this.shouldComponentUpdate = () => false; | ||
| importFromXml: function importFromXml(xml) { | ||
| try { | ||
| Blockly.Xml.domToWorkspace(Blockly.Xml.textToDom(xml), this.state.workspace); | ||
| return true; | ||
| } catch (e) { | ||
| if (this.props.onImportXmlError) { | ||
| this.props.onImportXmlError(e); | ||
| this.componentWillUnmount = () => { | ||
| if (this.state.workspace) { | ||
| this.state.workspace.dispose(); | ||
| } | ||
| return false; | ||
| } | ||
| }, | ||
| }; | ||
| componentWillReceiveProps: function componentWillReceiveProps(newProps) { | ||
| if (this.props.initialXml != newProps.initialXml) { | ||
| this.setState({ xml: newProps.initialXml }); | ||
| } | ||
| }, | ||
| this.importFromXml = xml => { | ||
| try { | ||
| Blockly.Xml.domToWorkspace(Blockly.Xml.textToDom(xml), this.state.workspace); | ||
| return true; | ||
| } catch (e) { | ||
| if (this.props.onImportXmlError) { | ||
| this.props.onImportXmlError(e); | ||
| } | ||
| return false; | ||
| } | ||
| }; | ||
| componentWillUnmount: function componentWillUnmount() { | ||
| if (this.state.workspace) { | ||
| this.state.workspace.dispose(); | ||
| } | ||
| }, | ||
| this.xmlDidChange = () => { | ||
| if (this.props.xmlDidChange) { | ||
| this.props.xmlDidChange(this.state.xml); | ||
| } | ||
| }; | ||
| shouldComponentUpdate: function shouldComponentUpdate() { | ||
| return false; | ||
| }, | ||
| this.workspaceDidChange = () => { | ||
| if (this.props.workspaceDidChange) { | ||
| this.props.workspaceDidChange(this.state.workspace); | ||
| } | ||
| }; | ||
| xmlDidChange: function xmlDidChange() { | ||
| if (this.props.xmlDidChange) { | ||
| this.props.xmlDidChange(this.state.xml); | ||
| } | ||
| }, | ||
| this.toolboxDidUpdate = toolboxNode => { | ||
| if (toolboxNode && this.state.workspace) { | ||
| this.state.workspace.updateToolbox(toolboxNode); | ||
| } | ||
| }; | ||
| toolboxDidUpdate: function toolboxDidUpdate(toolboxNode) { | ||
| if (toolboxNode && this.state.workspace) { | ||
| this.state.workspace.updateToolbox(toolboxNode); | ||
| } | ||
| }, | ||
| this.resize = () => { | ||
| Blockly.svgResize(this.state.workspace); | ||
| }; | ||
| resize: function resize() { | ||
| Blockly.svgResize(this.state.workspace); | ||
| }, | ||
| this.render = () => { | ||
| // We have to fool Blockly into setting up a toolbox with categories initially; | ||
| // otherwise it will refuse to do so after we inject the real categories into it. | ||
| let dummyToolboxContent; | ||
| if (this.props.toolboxMode === 'CATEGORIES') { | ||
| dummyToolboxContent = React.createElement('category', { name: 'Dummy toolbox' }); | ||
| } | ||
| render: function render() { | ||
| // We have to fool Blockly into setting up a toolbox with categories initially; | ||
| // otherwise it will refuse to do so after we inject the real categories into it. | ||
| var dummyToolboxContent; | ||
| if (this.props.toolboxMode === "CATEGORIES") { | ||
| dummyToolboxContent = _react2.default.createElement('category', { name: 'Dummy toolbox' }); | ||
| } | ||
| return React.createElement( | ||
| 'div', | ||
| { className: this.props.wrapperDivClassName }, | ||
| React.createElement( | ||
| 'xml', | ||
| { style: { display: 'none' }, ref: dummyToolbox => { | ||
| this.dummyToolbox = dummyToolbox; | ||
| } }, | ||
| dummyToolboxContent | ||
| ), | ||
| React.createElement('div', { | ||
| className: this.props.wrapperDivClassName, | ||
| ref: editorDiv => { | ||
| this.editorDiv = editorDiv; | ||
| } | ||
| }) | ||
| ); | ||
| }; | ||
| return _react2.default.createElement( | ||
| 'div', | ||
| { className: this.props.wrapperDivClassName }, | ||
| _react2.default.createElement( | ||
| 'xml', | ||
| { style: { display: "none" }, ref: 'dummyToolbox' }, | ||
| dummyToolboxContent | ||
| ), | ||
| _react2.default.createElement('div', { ref: 'editorDiv', className: this.props.wrapperDivClassName }) | ||
| ); | ||
| this.state = { | ||
| workspace: null, | ||
| xml: this.props.initialXml | ||
| }; | ||
| } | ||
| }); | ||
| exports.default = BlocklyWorkspace; | ||
| } | ||
| BlocklyWorkspace.propTypes = { | ||
| initialXml: PropTypes.string, | ||
| workspaceConfiguration: PropTypes.object, // eslint-disable-line react/forbid-prop-types | ||
| wrapperDivClassName: PropTypes.string, | ||
| xmlDidChange: PropTypes.func, | ||
| workspaceDidChange: PropTypes.func, | ||
| onImportXmlError: PropTypes.func, | ||
| toolboxMode: PropTypes.oneOf(['CATEGORIES', 'BLOCKS']) | ||
| }; | ||
| BlocklyWorkspace.defaultProps = { | ||
| initialXml: null, | ||
| workspaceConfiguration: null, | ||
| wrapperDivClassName: null, | ||
| xmlDidChange: null, | ||
| workspaceDidChange: null, | ||
| onImportXmlError: null, | ||
| toolboxMode: 'BLOCKS' | ||
| }; | ||
| export default BlocklyWorkspace; |
+12
-36
@@ -1,39 +0,15 @@ | ||
| 'use strict'; | ||
| import BlocklyEditor from './BlocklyEditor'; | ||
| import BlocklyToolbox from './BlocklyToolbox'; | ||
| import BlocklyToolboxBlock from './BlocklyToolboxBlock'; | ||
| import BlocklyToolboxCategory from './BlocklyToolboxCategory'; | ||
| import BlocklyWorkspace from './BlocklyWorkspace'; | ||
| Object.defineProperty(exports, "__esModule", { | ||
| value: true | ||
| }); | ||
| var _BlocklyEditor = require('./BlocklyEditor'); | ||
| var _BlocklyEditor2 = _interopRequireDefault(_BlocklyEditor); | ||
| var _BlocklyToolbox = require('./BlocklyToolbox'); | ||
| var _BlocklyToolbox2 = _interopRequireDefault(_BlocklyToolbox); | ||
| var _BlocklyToolboxBlock = require('./BlocklyToolboxBlock'); | ||
| var _BlocklyToolboxBlock2 = _interopRequireDefault(_BlocklyToolboxBlock); | ||
| var _BlocklyToolboxCategory = require('./BlocklyToolboxCategory'); | ||
| var _BlocklyToolboxCategory2 = _interopRequireDefault(_BlocklyToolboxCategory); | ||
| var _BlocklyWorkspace = require('./BlocklyWorkspace'); | ||
| var _BlocklyWorkspace2 = _interopRequireDefault(_BlocklyWorkspace); | ||
| function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
| var ReactBlocklyComponent = { | ||
| BlocklyEditor: _BlocklyEditor2.default, | ||
| BlocklyToolbox: _BlocklyToolbox2.default, | ||
| BlocklyToolboxBlock: _BlocklyToolboxBlock2.default, | ||
| BlocklyToolboxCategory: _BlocklyToolboxCategory2.default, | ||
| BlocklyWorkspace: _BlocklyWorkspace2.default | ||
| const ReactBlocklyComponent = { | ||
| BlocklyEditor, | ||
| BlocklyToolbox, | ||
| BlocklyToolboxBlock, | ||
| BlocklyToolboxCategory, | ||
| BlocklyWorkspace | ||
| }; | ||
| module.exports = ReactBlocklyComponent; | ||
| exports.default = ReactBlocklyComponent; | ||
| export default ReactBlocklyComponent; |
+27
-17
| { | ||
| "name": "react-blockly-component", | ||
| "version": "2.0.1", | ||
| "version": "3.0.0", | ||
| "description": "A React wrapper for the Blockly visual programming editor", | ||
@@ -8,6 +8,8 @@ "main": "dist-modules", | ||
| "build": "webpack --config webpack.config.js", | ||
| "lint": "eslint --ext .js,.jsx src", | ||
| "lint:fix": "eslint --fix --ext .js,.jsx src", | ||
| "start": "webpack-dev-server --config webpack.config.js", | ||
| "test": "echo \"Error: no test specified\" && exit 1", | ||
| "prepublish": "babel ./src --out-dir ./dist-modules", | ||
| "prepublish-watch": "babel ./src --watch --out-dir ./dist-modules" | ||
| "prepublishOnly": "babel ./src --out-dir ./dist-modules", | ||
| "prepublish:watch": "babel ./src --watch --out-dir ./dist-modules" | ||
| }, | ||
@@ -17,20 +19,28 @@ "author": "Nat Budin <nbudin@patientslikeme.com>", | ||
| "dependencies": { | ||
| "immutable": "^3.7.6", | ||
| "react-immutable-proptypes": "^2.1.0", | ||
| "react-immutable-render-mixin": "^0.9.5" | ||
| "immutable": "^3.8.2", | ||
| "prop-types": "^15.6.0", | ||
| "react-immutable-proptypes": "^2.1.0" | ||
| }, | ||
| "peerDependencies": { | ||
| "react": "^0.14 || ^15.0", | ||
| "react-dom": "^0.14 || ^15.0" | ||
| "react": "^15.0 || ^16.0" | ||
| }, | ||
| "devDependencies": { | ||
| "babel-cli": "~6.10.1", | ||
| "babel-core": "^6.3.26", | ||
| "babel-loader": "^6.2.0", | ||
| "babel-plugin-transform-object-assign": "^6.3.13", | ||
| "babel-preset-es2015": "^6.3.13", | ||
| "babel-preset-react": "^6.3.13", | ||
| "expose-loader": "^0.7.1", | ||
| "webpack": "^1.12.9", | ||
| "webpack-dev-server": "^1.14.0" | ||
| "babel-cli": "^6.26", | ||
| "babel-core": "^6.26.0", | ||
| "babel-eslint": "^8.1.2", | ||
| "babel-loader": "^7.1", | ||
| "babel-plugin-transform-object-assign": "^6.22.0", | ||
| "babel-preset-es2015": "^6.24.1", | ||
| "babel-preset-react": "^6.24.1", | ||
| "babel-preset-stage-0": "^6.24.1", | ||
| "eslint": "^4.14.0", | ||
| "eslint-config-airbnb": "^16.1.0", | ||
| "eslint-plugin-import": "^2.8.0", | ||
| "eslint-plugin-jsx-a11y": "^6.0.3", | ||
| "eslint-plugin-react": "^7.5.1", | ||
| "expose-loader": "^0.7.3", | ||
| "react": "^16.0.0", | ||
| "react-dom": "^16.0.0", | ||
| "webpack": "^3.0", | ||
| "webpack-dev-server": "^2.9" | ||
| }, | ||
@@ -37,0 +47,0 @@ "repository": { |
+1
-1
@@ -44,3 +44,3 @@ # react-blockly-component | ||
| * `toolboxBlocks`: An array of top-level blocks for the toolbox. Each block is an object following the format of the objects in the `toolboxCategories` `blocks` array (specified above). If this property is defined, `toolboxCategories` should not be defined, since Blockly's toolbox can either contain blocks or categories, but not both. | ||
| * `xmlDidChange`: A callback function that will be called whenever the XML content generated by the Blockly editor changes. The new XML content will be passed as an argument to the function. | ||
| * `workspaceDidChange`: A callback function that will be fired for every workspace event. Take a look at https://developers.google.com/blockly/guides/configure/web/events for a full list of events. For example you can handle your clientside code generation in this function. The new workspace object will be passed as an argument to the function. | ||
| * `processToolboxCategory`: A callback function that can be used to pre-process the content of a toolbox category. This function is passed a single object (an [Immutable.js](https://facebook.github.io/immutable-js/) Map) from the `toolboxCategories` array and is expected to return an object of the same format. This is useful if another React component is embedding `BlocklyEditor` and wants to add dynamic content to the toolbox. | ||
@@ -47,0 +47,0 @@ |
+91
-37
| import React from 'react'; | ||
| import ReactDOM from 'react-dom'; | ||
| import PropTypes from 'prop-types'; | ||
| import Immutable from 'immutable'; | ||
@@ -8,45 +8,95 @@ | ||
| var BlocklyEditor = React.createClass({ | ||
| propTypes: { | ||
| initialXml: React.PropTypes.string, | ||
| workspaceConfiguration: React.PropTypes.object, | ||
| wrapperDivClassName: React.PropTypes.string, | ||
| toolboxCategories: React.PropTypes.array, | ||
| toolboxBlocks: React.PropTypes.array, | ||
| xmlDidChange: React.PropTypes.func, | ||
| onImportXmlError: React.PropTypes.func, | ||
| processToolboxCategory: React.PropTypes.func | ||
| }, | ||
| const BlockPropType = PropTypes.shape({ | ||
| type: PropTypes.string.isRequired, | ||
| shadow: PropTypes.bool, | ||
| fields: PropTypes.object, | ||
| values: PropTypes.object, | ||
| statements: PropTypes.object, | ||
| next: PropTypes.object, | ||
| mutation: PropTypes.shape({ | ||
| attributes: PropTypes.object, | ||
| innerContent: PropTypes.string, | ||
| }), | ||
| }); | ||
| toolboxDidUpdate: function() { | ||
| var workspaceConfiguration = this.props.workspaceConfiguration || {}; | ||
| if (this.refs.workspace && !workspaceConfiguration.readOnly) { | ||
| this.refs.workspace.toolboxDidUpdate(ReactDOM.findDOMNode(this.refs.toolbox)); | ||
| } | ||
| }, | ||
| const categoryPropsNonRecursive = { | ||
| type: PropTypes.string.isRequired, | ||
| name: PropTypes.string, | ||
| custom: PropTypes.string, | ||
| colour: PropTypes.string, | ||
| blocks: PropTypes.arrayOf(BlockPropType), | ||
| }; | ||
| componentDidMount: function() { | ||
| const CategoryPropType = PropTypes.shape({ | ||
| ...categoryPropsNonRecursive, | ||
| categories: PropTypes.arrayOf(PropTypes.shape(categoryPropsNonRecursive)), | ||
| }); | ||
| class BlocklyEditor extends React.Component { | ||
| static propTypes = { | ||
| initialXml: PropTypes.string, | ||
| workspaceConfiguration: PropTypes.object, // eslint-disable-line react/forbid-prop-types | ||
| wrapperDivClassName: PropTypes.string, | ||
| toolboxCategories: PropTypes.arrayOf(CategoryPropType.isRequired), | ||
| toolboxBlocks: PropTypes.arrayOf(BlockPropType.isRequired), | ||
| xmlDidChange: PropTypes.func, | ||
| workspaceDidChange: PropTypes.func, | ||
| onImportXmlError: PropTypes.func, | ||
| processToolboxCategory: PropTypes.func, | ||
| }; | ||
| static defaultProps = { | ||
| initialXml: null, | ||
| workspaceConfiguration: null, | ||
| wrapperDivClassName: null, | ||
| toolboxCategories: null, | ||
| toolboxBlocks: null, | ||
| xmlDidChange: null, | ||
| workspaceDidChange: null, | ||
| onImportXmlError: null, | ||
| processToolboxCategory: null, | ||
| }; | ||
| componentDidMount = () => { | ||
| this.toolboxDidUpdate(); | ||
| }, | ||
| xmlDidChange: function(newXml) { | ||
| if (this.props.xmlDidChange) { | ||
| if (typeof console !== 'undefined') { | ||
| // eslint-disable-next-line no-console | ||
| console.error('Warning: xmlDidChange is deprecated and will be removed in future versions! Please use workspaceDidChange instead.'); | ||
| } | ||
| } | ||
| } | ||
| toolboxDidUpdate = () => { | ||
| const workspaceConfiguration = this.props.workspaceConfiguration || {}; | ||
| if (this.workspace && !workspaceConfiguration.readOnly) { | ||
| this.workspace.toolboxDidUpdate(this.toolbox.getRootNode()); | ||
| } | ||
| } | ||
| xmlDidChange = (newXml) => { | ||
| if (this.props.xmlDidChange) { | ||
| this.props.xmlDidChange(newXml); | ||
| } | ||
| }, | ||
| } | ||
| importFromXml: function(xml) { | ||
| return this.refs.workspace.importFromXml(xml); | ||
| }, | ||
| workspaceDidChange = (workspace) => { | ||
| if (this.props.workspaceDidChange) { | ||
| this.props.workspaceDidChange(workspace); | ||
| } | ||
| } | ||
| resize: function() { | ||
| this.refs.workspace.resize(); | ||
| }, | ||
| importFromXml = xml => this.workspace.importFromXml(xml) | ||
| render: function() { | ||
| var toolboxMode; | ||
| resize = () => { | ||
| this.workspace.resize(); | ||
| } | ||
| render = () => { | ||
| let toolboxMode; | ||
| if (this.props.toolboxCategories) { | ||
| toolboxMode = "CATEGORIES"; | ||
| toolboxMode = 'CATEGORIES'; | ||
| } else if (this.props.toolboxBlocks) { | ||
| toolboxMode = "BLOCKS"; | ||
| toolboxMode = 'BLOCKS'; | ||
| } | ||
@@ -61,4 +111,6 @@ | ||
| processCategory={this.props.processToolboxCategory} | ||
| ref="toolbox" /> | ||
| <BlocklyWorkspace ref="workspace" | ||
| ref={(toolbox) => { this.toolbox = toolbox; }} | ||
| /> | ||
| <BlocklyWorkspace | ||
| ref={(workspace) => { this.workspace = workspace; }} | ||
| initialXml={this.props.initialXml} | ||
@@ -68,9 +120,11 @@ onImportXmlError={this.props.onImportXmlError} | ||
| xmlDidChange={this.xmlDidChange} | ||
| workspaceDidChange={this.workspaceDidChange} | ||
| wrapperDivClassName={this.props.wrapperDivClassName} | ||
| workspaceConfiguration={this.props.workspaceConfiguration} /> | ||
| workspaceConfiguration={this.props.workspaceConfiguration} | ||
| /> | ||
| </div> | ||
| ); | ||
| } | ||
| }); | ||
| } | ||
| export default BlocklyEditor; | ||
| export default BlocklyEditor; |
+53
-46
@@ -0,2 +1,5 @@ | ||
| /* eslint-disable react/no-array-index-key */ | ||
| import React from 'react'; | ||
| import PropTypes from 'prop-types'; | ||
| import { is } from 'immutable'; | ||
@@ -8,47 +11,36 @@ import ImmutablePropTypes from 'react-immutable-proptypes'; | ||
| var BlocklyToolbox = React.createClass({ | ||
| propTypes: { | ||
| class BlocklyToolbox extends React.Component { | ||
| static propTypes = { | ||
| categories: ImmutablePropTypes.list, | ||
| blocks: ImmutablePropTypes.list, | ||
| processCategory: React.PropTypes.func, | ||
| didUpdate: React.PropTypes.func | ||
| }, | ||
| processCategory: PropTypes.func, | ||
| didUpdate: PropTypes.func, | ||
| }; | ||
| renderCategories: function(categories) { | ||
| return categories.map(function(category, i) { | ||
| if (category.get('type') === 'sep') { | ||
| return <sep key={"sep_" + i}></sep>; | ||
| } else if (category.get('type') === 'search') { | ||
| return <search key={"search_" + i}/>; | ||
| } else { | ||
| return <BlocklyToolboxCategory | ||
| name={category.get('name')} | ||
| custom={category.get('custom')} | ||
| colour={category.get('colour')} | ||
| key={"category_" + category.get('name') + "_" + i} | ||
| blocks={category.get('blocks')} | ||
| categories={category.get('categories')} />; | ||
| } | ||
| }.bind(this)); | ||
| }, | ||
| static defaultProps = { | ||
| categories: null, | ||
| blocks: null, | ||
| processCategory: null, | ||
| didUpdate: null, | ||
| }; | ||
| shouldComponentUpdate: function(nextProps, nextState) { | ||
| return !(is(nextProps.categories, this.props.categories) && is(nextProps.blocks, this.props.blocks)); | ||
| }, | ||
| componentDidMount: function() { | ||
| componentDidMount = () => { | ||
| this.props.didUpdate(); | ||
| }, | ||
| } | ||
| componentDidUpdate: function(prevProps, prevState) { | ||
| shouldComponentUpdate = nextProps => !( | ||
| is(nextProps.categories, this.props.categories) && is(nextProps.blocks, this.props.blocks) | ||
| ) | ||
| componentDidUpdate = () => { | ||
| this.props.didUpdate(); | ||
| }, | ||
| } | ||
| processCategory: function(category) { | ||
| var processedCategory = category; | ||
| getRootNode = () => this.rootNode | ||
| processCategory = (category) => { | ||
| let processedCategory = category; | ||
| if (processedCategory.has('categories')) { | ||
| processedCategory = category.update('categories', function(subcategories) { | ||
| return subcategories.map(this.processCategory); | ||
| }.bind(this)); | ||
| processedCategory = category.update('categories', subcategories => subcategories.map(this.processCategory)); | ||
| } | ||
@@ -61,21 +53,36 @@ | ||
| return processedCategory; | ||
| }, | ||
| } | ||
| render: function() { | ||
| renderCategories = categories => categories.map((category, i) => { | ||
| if (category.get('type') === 'sep') { | ||
| return <sep key={`sep_${i}`} />; | ||
| } else if (category.get('type') === 'search') { | ||
| return <search key={`search_${i}`} />; | ||
| } | ||
| return (<BlocklyToolboxCategory | ||
| name={category.get('name')} | ||
| custom={category.get('custom')} | ||
| colour={category.get('colour')} | ||
| key={`category_${category.get('name')}_${i}`} | ||
| blocks={category.get('blocks')} | ||
| categories={category.get('categories')} | ||
| />); | ||
| }) | ||
| render = () => { | ||
| if (this.props.categories) { | ||
| return ( | ||
| <xml style={{display: "none"}}> | ||
| <xml style={{ display: 'none' }} ref={(node) => { this.rootNode = node; }}> | ||
| {this.renderCategories(this.props.categories.map(this.processCategory))} | ||
| </xml> | ||
| ); | ||
| } else { | ||
| return ( | ||
| <xml style={{display: "none"}}> | ||
| {this.props.blocks.map(BlocklyToolboxBlock.renderBlock)} | ||
| </xml> | ||
| ); | ||
| } | ||
| return ( | ||
| <xml style={{ display: 'none' }} ref={(node) => { this.rootNode = node; }}> | ||
| {this.props.blocks.map(BlocklyToolboxBlock.renderBlock)} | ||
| </xml> | ||
| ); | ||
| } | ||
| }); | ||
| } | ||
| export default BlocklyToolbox; | ||
| export default BlocklyToolbox; |
@@ -0,12 +1,11 @@ | ||
| /* eslint-disable react/no-array-index-key */ | ||
| import React from 'react'; | ||
| import ReactDOM from 'react-dom'; | ||
| import PropTypes from 'prop-types'; | ||
| import ImmutablePropTypes from 'react-immutable-proptypes'; | ||
| import ImmutableRenderMixin from 'react-immutable-render-mixin'; | ||
| var BlocklyToolboxBlock = React.createClass({ | ||
| mixins: [ImmutableRenderMixin], | ||
| propTypes: { | ||
| type: React.PropTypes.string.isRequired, | ||
| shadow: React.PropTypes.bool, | ||
| class BlocklyToolboxBlock extends React.PureComponent { | ||
| static propTypes = { | ||
| type: PropTypes.string.isRequired, | ||
| shadow: PropTypes.bool, | ||
| fields: ImmutablePropTypes.map, | ||
@@ -18,79 +17,84 @@ values: ImmutablePropTypes.map, | ||
| attributes: ImmutablePropTypes.map, | ||
| innerContent: React.PropTypes.string | ||
| }) | ||
| }, | ||
| innerContent: PropTypes.string, | ||
| }), | ||
| }; | ||
| statics: { | ||
| renderBlock: function(block, key) { | ||
| return ( | ||
| <BlocklyToolboxBlock | ||
| type={block.get('type')} | ||
| key={key} | ||
| fields={block.get('fields')} | ||
| values={block.get('values')} | ||
| statements={block.get('statements')} | ||
| mutation={block.get('mutation')} | ||
| shadow={block.get('shadow')} | ||
| next={block.get('next')} /> | ||
| ); | ||
| } | ||
| }, | ||
| static defaultProps = { | ||
| shadow: false, | ||
| fields: null, | ||
| values: null, | ||
| statements: null, | ||
| next: null, | ||
| mutation: null, | ||
| }; | ||
| componentDidMount: function() { | ||
| static renderBlock = (block, key) => ( | ||
| <BlocklyToolboxBlock | ||
| type={block.get('type')} | ||
| key={key} | ||
| fields={block.get('fields')} | ||
| values={block.get('values')} | ||
| statements={block.get('statements')} | ||
| mutation={block.get('mutation')} | ||
| shadow={block.get('shadow')} | ||
| next={block.get('next')} | ||
| /> | ||
| ); | ||
| componentDidMount = () => { | ||
| if (this.props.mutation) { | ||
| var mutation = ReactDOM.findDOMNode(this.refs.mutation); | ||
| this.props.mutation.get('attributes').forEach(function(value, attributeName) { | ||
| mutation.setAttribute(attributeName, value); | ||
| this.props.mutation.get('attributes').forEach((value, attributeName) => { | ||
| this.mutationElement.setAttribute(attributeName, value); | ||
| return true; | ||
| }); | ||
| } | ||
| }, | ||
| } | ||
| render: function() { | ||
| var fields = []; | ||
| var values = []; | ||
| var statements = []; | ||
| var mutation = null; | ||
| var nextBlock = null; | ||
| render = () => { | ||
| let fields = []; | ||
| let values = []; | ||
| let statements = []; | ||
| let mutation = null; | ||
| let nextBlock = null; | ||
| if (this.props.fields) { | ||
| fields = this.props.fields.map(function(fieldValue, fieldName, i) { | ||
| return ( | ||
| <field name={fieldName} key={"field_" + fieldName + "_" + i}> | ||
| {fieldValue} | ||
| </field> | ||
| ); | ||
| }.bind(this)).valueSeq(); | ||
| fields = this.props.fields.map((fieldValue, fieldName, i) => ( | ||
| <field name={fieldName} key={`field_${fieldName}_${i}`}> | ||
| {fieldValue} | ||
| </field> | ||
| )).valueSeq(); | ||
| } | ||
| if (this.props.values) { | ||
| values = this.props.values.map(function(valueBlock, valueName, i) { | ||
| return ( | ||
| <value name={valueName} key={"value_" + valueName + "_" + i}> | ||
| {BlocklyToolboxBlock.renderBlock(valueBlock)} | ||
| </value> | ||
| ); | ||
| }.bind(this)).valueSeq(); | ||
| values = this.props.values.map((valueBlock, valueName, i) => ( | ||
| <value name={valueName} key={`value_${valueName}_${i}`}> | ||
| {BlocklyToolboxBlock.renderBlock(valueBlock)} | ||
| </value> | ||
| )).valueSeq(); | ||
| } | ||
| if (this.props.statements) { | ||
| statements = this.props.statements.map(function(statementBlock, statementName, i) { | ||
| return ( | ||
| <statement name={statementName} key={"statement_" + statementName + "_" + i}> | ||
| {BlocklyToolboxBlock.renderBlock(statementBlock)} | ||
| </statement> | ||
| ); | ||
| }.bind(this)).valueSeq(); | ||
| statements = this.props.statements.map((statementBlock, statementName, i) => ( | ||
| <statement name={statementName} key={`statement_${statementName}_${i}`}> | ||
| {BlocklyToolboxBlock.renderBlock(statementBlock)} | ||
| </statement> | ||
| )).valueSeq(); | ||
| } | ||
| if (this.props.mutation) { | ||
| mutation = <mutation dangerouslySetInnerHTML={{__html: this.props.mutation.get('innerContent')}} ref="mutation" />; | ||
| mutation = (( | ||
| <mutation | ||
| // eslint-disable-next-line react/no-danger | ||
| dangerouslySetInnerHTML={{ __html: this.props.mutation.get('innerContent') }} | ||
| ref={(mutationElement) => { this.mutationElement = mutationElement; }} | ||
| /> | ||
| )); | ||
| } | ||
| if (this.props.next) { | ||
| nextBlock = <next> | ||
| {BlocklyToolboxBlock.renderBlock(this.props.next)} | ||
| </next>; | ||
| nextBlock = (( | ||
| <next> | ||
| {BlocklyToolboxBlock.renderBlock(this.props.next)} | ||
| </next> | ||
| )); | ||
| } | ||
@@ -108,16 +112,16 @@ | ||
| ); | ||
| } else { | ||
| return ( | ||
| <block type={this.props.type}> | ||
| {mutation} | ||
| {fields} | ||
| {values} | ||
| {statements} | ||
| {nextBlock} | ||
| </block> | ||
| ); | ||
| } | ||
| return ( | ||
| <block type={this.props.type}> | ||
| {mutation} | ||
| {fields} | ||
| {values} | ||
| {statements} | ||
| {nextBlock} | ||
| </block> | ||
| ); | ||
| } | ||
| }); | ||
| } | ||
| export default BlocklyToolboxBlock; | ||
| export default BlocklyToolboxBlock; |
| import React from 'react'; | ||
| import PropTypes from 'prop-types'; | ||
| import ImmutablePropTypes from 'react-immutable-proptypes'; | ||
| import ImmutableRenderMixin from 'react-immutable-render-mixin'; | ||
| import BlocklyToolboxBlock from './BlocklyToolboxBlock'; | ||
| var BlocklyToolboxCategory = React.createClass({ | ||
| mixins: [ImmutableRenderMixin], | ||
| propTypes: { | ||
| name: React.PropTypes.string, | ||
| custom: React.PropTypes.string, | ||
| colour: React.PropTypes.string, | ||
| class BlocklyToolboxCategory extends React.PureComponent { | ||
| static propTypes = { | ||
| name: PropTypes.string, | ||
| custom: PropTypes.string, | ||
| colour: PropTypes.string, | ||
| categories: ImmutablePropTypes.list, | ||
| blocks: ImmutablePropTypes.list | ||
| }, | ||
| blocks: ImmutablePropTypes.list, | ||
| }; | ||
| statics: { | ||
| renderCategory: function(category, key) { | ||
| if (category.get('type') === 'sep') { | ||
| return <sep key={key}></sep>; | ||
| } else if (category.get('type') === 'search') { | ||
| return <search key={key}/>; | ||
| } else { | ||
| return <BlocklyToolboxCategory | ||
| name={category.get('name')} | ||
| custom={category.get('custom')} | ||
| colour={category.get('colour')} | ||
| key={key} | ||
| blocks={category.get('blocks')} | ||
| categories={category.get('categories')} />; | ||
| } | ||
| static defaultProps = { | ||
| name: null, | ||
| custom: null, | ||
| colour: null, | ||
| categories: null, | ||
| blocks: null, | ||
| }; | ||
| static renderCategory = (category, key) => { | ||
| if (category.get('type') === 'sep') { | ||
| return <sep key={key} />; | ||
| } else if (category.get('type') === 'search') { | ||
| return <search key={key} />; | ||
| } | ||
| }, | ||
| return (<BlocklyToolboxCategory | ||
| name={category.get('name')} | ||
| custom={category.get('custom')} | ||
| colour={category.get('colour')} | ||
| key={key} | ||
| blocks={category.get('blocks')} | ||
| categories={category.get('categories')} | ||
| />); | ||
| }; | ||
| render: function() { | ||
| var subcategories = (this.props.categories || []).map(BlocklyToolboxCategory.renderCategory); | ||
| var blocks = (this.props.blocks || []).map(BlocklyToolboxBlock.renderBlock); | ||
| render = () => { | ||
| const subcategories = (this.props.categories || []).map(BlocklyToolboxCategory.renderCategory); | ||
| const blocks = (this.props.blocks || []).map(BlocklyToolboxBlock.renderBlock); | ||
| return ( | ||
| <category is name={this.props.name} custom={this.props.custom} colour={this.props.colour} ref="category"> | ||
| <category name={this.props.name} custom={this.props.custom} colour={this.props.colour}> | ||
| {blocks} | ||
@@ -47,4 +51,4 @@ {subcategories} | ||
| } | ||
| }); | ||
| } | ||
| export default BlocklyToolboxCategory; | ||
| export default BlocklyToolboxCategory; |
+92
-68
| import React from 'react'; | ||
| import ReactDOM from 'react-dom'; | ||
| import PropTypes from 'prop-types'; | ||
| var debounce = function(func, wait) { | ||
| var timeout; | ||
| return function() { | ||
| var context = this, args = arguments; | ||
| var later = function() { | ||
| timeout = null; | ||
| func.apply(context, args); | ||
| }; | ||
| clearTimeout(timeout); | ||
| timeout = setTimeout(later, wait); | ||
| }; | ||
| }; | ||
| function debounce(func, wait) { | ||
| let timeout; | ||
| var BlocklyWorkspace = React.createClass({ | ||
| propTypes: { | ||
| initialXml: React.PropTypes.string, | ||
| workspaceConfiguration: React.PropTypes.object, | ||
| wrapperDivClassName: React.PropTypes.string, | ||
| xmlDidChange: React.PropTypes.func, | ||
| onImportXmlError: React.PropTypes.func, | ||
| toolboxMode: React.PropTypes.oneOf(['CATEGORIES', 'BLOCKS']) | ||
| }, | ||
| return function debouncedFunction(...args) { | ||
| const context = this; | ||
| const later = function later() { | ||
| timeout = null; | ||
| func.apply(context, args); | ||
| }; | ||
| clearTimeout(timeout); | ||
| timeout = setTimeout(later, wait); | ||
| }; | ||
| } | ||
| getInitialState: function() { | ||
| return { | ||
| class BlocklyWorkspace extends React.Component { | ||
| static propTypes = { | ||
| initialXml: PropTypes.string, | ||
| workspaceConfiguration: PropTypes.object, // eslint-disable-line react/forbid-prop-types | ||
| wrapperDivClassName: PropTypes.string, | ||
| xmlDidChange: PropTypes.func, | ||
| workspaceDidChange: PropTypes.func, | ||
| onImportXmlError: PropTypes.func, | ||
| toolboxMode: PropTypes.oneOf(['CATEGORIES', 'BLOCKS']), | ||
| }; | ||
| static defaultProps = { | ||
| initialXml: null, | ||
| workspaceConfiguration: null, | ||
| wrapperDivClassName: null, | ||
| xmlDidChange: null, | ||
| workspaceDidChange: null, | ||
| onImportXmlError: null, | ||
| toolboxMode: 'BLOCKS', | ||
| }; | ||
| constructor(props) { | ||
| super(props); | ||
| this.state = { | ||
| workspace: null, | ||
| xml: this.props.initialXml | ||
| xml: this.props.initialXml, | ||
| }; | ||
| }, | ||
| } | ||
| componentDidMount: function() { | ||
| componentDidMount = () => { | ||
| // TODO figure out how to use setState here without breaking the toolbox when switching tabs | ||
| this.state.workspace = Blockly.inject( | ||
| this.refs.editorDiv, | ||
| Object.assign({}, (this.props.workspaceConfiguration || {}), { | ||
| toolbox: ReactDOM.findDOMNode(this.refs.dummyToolbox) | ||
| }) | ||
| this.editorDiv, | ||
| { | ||
| ...this.props.workspaceConfiguration, | ||
| toolbox: this.dummyToolbox, | ||
| }, | ||
| ); | ||
@@ -47,17 +62,33 @@ | ||
| } else { | ||
| this.setState({xml: null}, this.xmlDidChange); | ||
| this.setState({ xml: null }, this.xmlDidChange); | ||
| } | ||
| } | ||
| this.state.workspace.addChangeListener(debounce(function() { | ||
| var newXml = Blockly.Xml.domToText(Blockly.Xml.workspaceToDom(this.state.workspace)); | ||
| if (newXml == this.state.xml) { | ||
| this.state.workspace.addChangeListener(this.workspaceDidChange); | ||
| this.state.workspace.addChangeListener(debounce(() => { | ||
| const newXml = Blockly.Xml.domToText(Blockly.Xml.workspaceToDom(this.state.workspace)); | ||
| if (newXml === this.state.xml) { | ||
| return; | ||
| } | ||
| this.setState({xml: newXml}, this.xmlDidChange); | ||
| }.bind(this), 200)); | ||
| }, | ||
| this.setState({ xml: newXml }, this.xmlDidChange); | ||
| }, 200)); | ||
| } | ||
| importFromXml: function(xml) { | ||
| componentWillReceiveProps = (newProps) => { | ||
| if (this.props.initialXml !== newProps.initialXml) { | ||
| this.setState({ xml: newProps.initialXml }); | ||
| } | ||
| } | ||
| shouldComponentUpdate = () => false | ||
| componentWillUnmount = () => { | ||
| if (this.state.workspace) { | ||
| this.state.workspace.dispose(); | ||
| } | ||
| } | ||
| importFromXml = (xml) => { | ||
| try { | ||
@@ -72,41 +103,31 @@ Blockly.Xml.domToWorkspace(Blockly.Xml.textToDom(xml), this.state.workspace); | ||
| } | ||
| }, | ||
| } | ||
| componentWillReceiveProps: function(newProps) { | ||
| if (this.props.initialXml != newProps.initialXml) { | ||
| this.setState({xml: newProps.initialXml}); | ||
| xmlDidChange = () => { | ||
| if (this.props.xmlDidChange) { | ||
| this.props.xmlDidChange(this.state.xml); | ||
| } | ||
| }, | ||
| } | ||
| componentWillUnmount: function() { | ||
| if (this.state.workspace) { | ||
| this.state.workspace.dispose(); | ||
| workspaceDidChange = () => { | ||
| if (this.props.workspaceDidChange) { | ||
| this.props.workspaceDidChange(this.state.workspace); | ||
| } | ||
| }, | ||
| } | ||
| shouldComponentUpdate: function() { | ||
| return false; | ||
| }, | ||
| xmlDidChange: function() { | ||
| if (this.props.xmlDidChange) { | ||
| this.props.xmlDidChange(this.state.xml); | ||
| } | ||
| }, | ||
| toolboxDidUpdate: function(toolboxNode) { | ||
| toolboxDidUpdate = (toolboxNode) => { | ||
| if (toolboxNode && this.state.workspace) { | ||
| this.state.workspace.updateToolbox(toolboxNode); | ||
| } | ||
| }, | ||
| } | ||
| resize: function() { | ||
| resize = () => { | ||
| Blockly.svgResize(this.state.workspace); | ||
| }, | ||
| } | ||
| render: function() { | ||
| render = () => { | ||
| // We have to fool Blockly into setting up a toolbox with categories initially; | ||
| // otherwise it will refuse to do so after we inject the real categories into it. | ||
| var dummyToolboxContent; | ||
| if (this.props.toolboxMode === "CATEGORIES") { | ||
| let dummyToolboxContent; | ||
| if (this.props.toolboxMode === 'CATEGORIES') { | ||
| dummyToolboxContent = ( | ||
@@ -119,11 +140,14 @@ <category name="Dummy toolbox" /> | ||
| <div className={this.props.wrapperDivClassName}> | ||
| <xml style={{display: "none"}} ref="dummyToolbox"> | ||
| <xml style={{ display: 'none' }} ref={(dummyToolbox) => { this.dummyToolbox = dummyToolbox; }}> | ||
| {dummyToolboxContent} | ||
| </xml> | ||
| <div ref="editorDiv" className={this.props.wrapperDivClassName} /> | ||
| <div | ||
| className={this.props.wrapperDivClassName} | ||
| ref={(editorDiv) => { this.editorDiv = editorDiv; }} | ||
| /> | ||
| </div> | ||
| ); | ||
| } | ||
| }); | ||
| } | ||
| export default BlocklyWorkspace; |
+7
-9
@@ -7,12 +7,10 @@ import BlocklyEditor from './BlocklyEditor'; | ||
| var ReactBlocklyComponent = { | ||
| BlocklyEditor: BlocklyEditor, | ||
| BlocklyToolbox: BlocklyToolbox, | ||
| BlocklyToolboxBlock: BlocklyToolboxBlock, | ||
| BlocklyToolboxCategory: BlocklyToolboxCategory, | ||
| BlocklyWorkspace: BlocklyWorkspace | ||
| const ReactBlocklyComponent = { | ||
| BlocklyEditor, | ||
| BlocklyToolbox, | ||
| BlocklyToolboxBlock, | ||
| BlocklyToolboxCategory, | ||
| BlocklyWorkspace, | ||
| }; | ||
| module.exports = ReactBlocklyComponent; | ||
| export default ReactBlocklyComponent; | ||
| export default ReactBlocklyComponent; |
+13
-10
@@ -1,9 +0,10 @@ | ||
| var webpack = require('webpack'); | ||
| const webpack = require('webpack'); | ||
| const path = require('path'); | ||
| module.exports = { | ||
| entry: [ | ||
| './src/index.js' | ||
| './src/dev-index.js' | ||
| ], | ||
| output: { | ||
| path: './dist/', | ||
| path: path.resolve(__dirname, 'dist'), | ||
| filename: "react-blockly-component.js", | ||
@@ -19,12 +20,14 @@ libraryTarget: "var", | ||
| exclude: /(node_modules|bower_components)/, | ||
| loader: 'babel' | ||
| loader: 'babel-loader' | ||
| } | ||
| ] | ||
| }, | ||
| externals: { | ||
| 'react': 'React', | ||
| 'react-dom': 'ReactDOM', | ||
| }, | ||
| plugins: [ | ||
| new webpack.ProvidePlugin({ | ||
| React: 'react', | ||
| ReactDOM: 'react-dom', | ||
| }), | ||
| ], | ||
| resolve: { | ||
| extensions: ['', '.js', '.jsx'] | ||
| extensions: ['.js', '.jsx'] | ||
| }, | ||
@@ -35,2 +38,2 @@ devServer: { | ||
| } | ||
| }; | ||
| }; |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 2 instances in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Found 1 instance in 1 package
2276824
449.65%4
-20%25299
351.36%18
100%19
Infinity%1
Infinity%+ Added
+ Added
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
Updated