diff --git a/src/components/prompt/prompt.jsx b/src/components/prompt/prompt.jsx index 1d648448140..2fd19b58570 100644 --- a/src/components/prompt/prompt.jsx +++ b/src/components/prompt/prompt.jsx @@ -40,7 +40,41 @@ const messages = defineMessages({ } }); -const PromptComponent = props => ( +const PromptComponent = props => props.isCustom ? ( + + + + + + + + + + +) : ( ( ); PromptComponent.propTypes = { - isAddingCloudVariableScratchSafe: PropTypes.bool.isRequired, - canAddCloudVariable: PropTypes.bool.isRequired, - cloudSelected: PropTypes.bool.isRequired, - defaultValue: PropTypes.string, - globalSelected: PropTypes.bool.isRequired, - isStage: PropTypes.bool.isRequired, - showListMessage: PropTypes.bool.isRequired, - label: PropTypes.string.isRequired, + title: PropTypes.string.isRequired, onCancel: PropTypes.func.isRequired, - onChange: PropTypes.func.isRequired, - onCloudVarOptionChange: PropTypes.func, - onFocus: PropTypes.func.isRequired, onKeyPress: PropTypes.func.isRequired, onOk: PropTypes.func.isRequired, - onScopeOptionSelection: PropTypes.func.isRequired, - showCloudOption: PropTypes.bool.isRequired, - showVariableOptions: PropTypes.bool.isRequired, - title: PropTypes.string.isRequired + isAddingCloudVariableScratchSafe: PropTypes.bool, + canAddCloudVariable: PropTypes.bool, + cloudSelected: PropTypes.bool, + defaultValue: PropTypes.string, + globalSelected: PropTypes.bool, + isStage: PropTypes.bool, + showListMessage: PropTypes.bool, + label: PropTypes.string, + onChange: PropTypes.func, + onCloudVarOptionChange: PropTypes.func, + onFocus: PropTypes.func, + onScopeOptionSelection: PropTypes.func, + showCloudOption: PropTypes.bool, + showVariableOptions: PropTypes.bool, + + /* custom modals */ + isCustom: PropTypes.bool, + enterTitle: PropTypes.string, + closeTitle: PropTypes.string }; export default PromptComponent; diff --git a/src/containers/blocks.jsx b/src/containers/blocks.jsx index 807fa98977f..43c2eeebdca 100644 --- a/src/containers/blocks.jsx +++ b/src/containers/blocks.jsx @@ -137,6 +137,7 @@ class Blocks extends React.Component { 'handleEnableProcedureReturns' ]); this.ScratchBlocks.prompt = this.handlePromptStart; + this.ScratchBlocks.customPrompt = this.handleCustomPrompt; this.ScratchBlocks.statusButtonCallback = this.handleConnectionModalStart; this.ScratchBlocks.recordSoundCallback = this.handleOpenSoundRecorder; @@ -609,6 +610,47 @@ class Blocks extends React.Component { p.prompt.showCloudOption = (optVarType === this.ScratchBlocks.SCALAR_VARIABLE_TYPE) && this.props.canUseCloud; this.setState(p); } + handleCustomPrompt (title, scale, enterInfo, closeInfo) { + const isObject = (value) => typeof value === 'object' && !Array.isArray(value); + let needsExit = false; + const exitFunc = (message) => { + needsExit = true; + console.error(message); + }; + + /* validate arguments */ + if (isObject(scale)) { + if (!scale.width || !scale.height) exitFunc("Custom Modal -- Missing width/height number property in Param 2"); + } else { + exitFunc("Custom Modal -- Param 2 must be a object with 'width' and 'height' number properties"); + } + if (isObject(enterInfo)) { + if (!scale.name || !scale.callback) exitFunc("Custom Modal -- Missing width/height number property in Param 3"); + if (scale.callback && typeof scale.callback !== 'function') exitFunc("Custom Modal -- callback property in Param 3 must be a function"); + } else { + exitFunc("Custom Modal -- Param 3 must be a object with properties: 'name' (string) and 'callback' (function)"); + } + if (isObject(closeInfo)) { + if (!scale.name || !scale.callback) exitFunc("Custom Modal -- Missing width/height number property in Param 4"); + if (scale.callback && typeof scale.callback !== 'function') exitFunc("Custom Modal -- callback property in Param 4 must be a function"); + } else { + exitFunc("Custom Modal -- Param 4 must be a object with properties: 'name' (string) and 'callback' (function)"); + } + if (needsExit) return; + + this.setState({prompt: { + isCustom: true, + title, enterInfo, closeInfo + }}); + + const modal = document.querySelector(`div[class="ReactModalPortal"]`); + if (modal) { + const inner = modal.firstChild.firstChild; + inner.style.width = `${scale.width}px`; + inner.style.height = `${scale.height}px`; + return inner.querySelector(`div[class*="prompt_body_"] div`); + } + } handleConnectionModalStart (extensionId) { this.props.onOpenConnectionModal(extensionId); } @@ -625,6 +667,11 @@ class Blocks extends React.Component { * to the variable validation prompt callback used in scratch-blocks. */ handlePromptCallback (input, variableOptions) { + if (this.state.prompt.isCustom) { + this.state.prompt.enterInfo.callback(); + this.setState({prompt: null}); + return; + } this.state.prompt.callback( input, this.props.vm.runtime.getAllVarNamesOfType(this.state.prompt.varType), @@ -632,6 +679,7 @@ class Blocks extends React.Component { this.handlePromptClose(); } handlePromptClose () { + if (this.state.prompt.isCustom) this.state.prompt.closeInfo.callback(); this.setState({prompt: null}); } handleCustomProceduresClose (data) { @@ -687,7 +735,17 @@ class Blocks extends React.Component { onDrop={this.handleDrop} {...props} /> - {this.state.prompt ? ( + {this.state.prompt ? this.state.prompt.isCustom ? ( + + ) : ( + ) + else return (