Skip to content

Commit 332eb1a

Browse files
authored
Custom Modal API [GUI]
1 parent 5647336 commit 332eb1a

File tree

3 files changed

+141
-27
lines changed

3 files changed

+141
-27
lines changed

src/components/prompt/prompt.jsx

Lines changed: 55 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,41 @@ const messages = defineMessages({
4040
}
4141
});
4242

43-
const PromptComponent = props => (
43+
const PromptComponent = props => props.isCustom ? (
44+
<Modal
45+
className={styles.modalContent}
46+
contentLabel={props.title}
47+
id="customModal"
48+
onRequestClose={props.onCancel}
49+
>
50+
<Box className={styles.body}>
51+
<Box>
52+
</Box>
53+
<Box className={styles.buttonRow}>
54+
<button
55+
className={styles.cancelButton}
56+
onClick={props.onCancel}
57+
>
58+
<FormattedMessage
59+
defaultMessage={props.closeTitle}
60+
description="Button in prompt for cancelling the dialog"
61+
id="gui.prompt.cancel"
62+
/>
63+
</button>
64+
<button
65+
className={styles.okButton}
66+
onClick={props.onOk}
67+
>
68+
<FormattedMessage
69+
defaultMessage={props.enterTitle}
70+
description="Button in prompt for confirming the dialog"
71+
id="gui.prompt.ok"
72+
/>
73+
</button>
74+
</Box>
75+
</Box>
76+
</Modal>
77+
) : (
4478
<Modal
4579
className={styles.modalContent}
4680
contentLabel={props.title}
@@ -188,24 +222,29 @@ const PromptComponent = props => (
188222
);
189223

190224
PromptComponent.propTypes = {
191-
isAddingCloudVariableScratchSafe: PropTypes.bool.isRequired,
192-
canAddCloudVariable: PropTypes.bool.isRequired,
193-
cloudSelected: PropTypes.bool.isRequired,
194-
defaultValue: PropTypes.string,
195-
globalSelected: PropTypes.bool.isRequired,
196-
isStage: PropTypes.bool.isRequired,
197-
showListMessage: PropTypes.bool.isRequired,
198-
label: PropTypes.string.isRequired,
225+
title: PropTypes.string.isRequired,
199226
onCancel: PropTypes.func.isRequired,
200-
onChange: PropTypes.func.isRequired,
201-
onCloudVarOptionChange: PropTypes.func,
202-
onFocus: PropTypes.func.isRequired,
203227
onKeyPress: PropTypes.func.isRequired,
204228
onOk: PropTypes.func.isRequired,
205-
onScopeOptionSelection: PropTypes.func.isRequired,
206-
showCloudOption: PropTypes.bool.isRequired,
207-
showVariableOptions: PropTypes.bool.isRequired,
208-
title: PropTypes.string.isRequired
229+
isAddingCloudVariableScratchSafe: PropTypes.bool,
230+
canAddCloudVariable: PropTypes.bool,
231+
cloudSelected: PropTypes.bool,
232+
defaultValue: PropTypes.string,
233+
globalSelected: PropTypes.bool,
234+
isStage: PropTypes.bool,
235+
showListMessage: PropTypes.bool,
236+
label: PropTypes.string,
237+
onChange: PropTypes.func,
238+
onCloudVarOptionChange: PropTypes.func,
239+
onFocus: PropTypes.func,
240+
onScopeOptionSelection: PropTypes.func,
241+
showCloudOption: PropTypes.bool,
242+
showVariableOptions: PropTypes.bool,
243+
244+
/* custom modals */
245+
isCustom: PropTypes.bool,
246+
enterTitle: PropTypes.string,
247+
closeTitle: PropTypes.string
209248
};
210249

211250
export default PromptComponent;

src/containers/blocks.jsx

Lines changed: 59 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@ class Blocks extends React.Component {
137137
'handleEnableProcedureReturns'
138138
]);
139139
this.ScratchBlocks.prompt = this.handlePromptStart;
140+
this.ScratchBlocks.customPrompt = this.handleCustomPrompt;
140141
this.ScratchBlocks.statusButtonCallback = this.handleConnectionModalStart;
141142
this.ScratchBlocks.recordSoundCallback = this.handleOpenSoundRecorder;
142143

@@ -609,6 +610,47 @@ class Blocks extends React.Component {
609610
p.prompt.showCloudOption = (optVarType === this.ScratchBlocks.SCALAR_VARIABLE_TYPE) && this.props.canUseCloud;
610611
this.setState(p);
611612
}
613+
handleCustomPrompt (title, scale, enterInfo, closeInfo) {
614+
const isObject = (value) => typeof value === 'object' && !Array.isArray(value);
615+
let needsExit = false;
616+
const exitFunc = (message) => {
617+
needsExit = true;
618+
console.error(message);
619+
};
620+
621+
/* validate arguments */
622+
if (isObject(scale)) {
623+
if (!scale.width || !scale.height) exitFunc("Custom Modal -- Missing width/height number property in Param 2");
624+
} else {
625+
exitFunc("Custom Modal -- Param 2 must be a object with 'width' and 'height' number properties");
626+
}
627+
if (isObject(enterInfo)) {
628+
if (!scale.name || !scale.callback) exitFunc("Custom Modal -- Missing width/height number property in Param 3");
629+
if (scale.callback && typeof scale.callback !== 'function') exitFunc("Custom Modal -- callback property in Param 3 must be a function");
630+
} else {
631+
exitFunc("Custom Modal -- Param 3 must be a object with properties: 'name' (string) and 'callback' (function)");
632+
}
633+
if (isObject(closeInfo)) {
634+
if (!scale.name || !scale.callback) exitFunc("Custom Modal -- Missing width/height number property in Param 4");
635+
if (scale.callback && typeof scale.callback !== 'function') exitFunc("Custom Modal -- callback property in Param 4 must be a function");
636+
} else {
637+
exitFunc("Custom Modal -- Param 4 must be a object with properties: 'name' (string) and 'callback' (function)");
638+
}
639+
if (needsExit) return;
640+
641+
this.setState({prompt: {
642+
isCustom: true,
643+
title, enterInfo, closeInfo
644+
}});
645+
646+
const modal = document.querySelector(`div[class="ReactModalPortal"]`);
647+
if (modal) {
648+
const inner = modal.firstChild.firstChild;
649+
inner.style.width = `${scale.width}px`;
650+
inner.style.height = `${scale.height}px`;
651+
return inner.querySelector(`div[class*="prompt_body_"] div`);
652+
}
653+
}
612654
handleConnectionModalStart (extensionId) {
613655
this.props.onOpenConnectionModal(extensionId);
614656
}
@@ -625,13 +667,19 @@ class Blocks extends React.Component {
625667
* to the variable validation prompt callback used in scratch-blocks.
626668
*/
627669
handlePromptCallback (input, variableOptions) {
670+
if (this.state.prompt.isCustom) {
671+
this.state.prompt.enterInfo.callback();
672+
this.setState({prompt: null});
673+
return;
674+
}
628675
this.state.prompt.callback(
629676
input,
630677
this.props.vm.runtime.getAllVarNamesOfType(this.state.prompt.varType),
631678
variableOptions);
632679
this.handlePromptClose();
633680
}
634681
handlePromptClose () {
682+
if (this.state.prompt.isCustom) this.state.prompt.closeInfo.callback();
635683
this.setState({prompt: null});
636684
}
637685
handleCustomProceduresClose (data) {
@@ -687,7 +735,17 @@ class Blocks extends React.Component {
687735
onDrop={this.handleDrop}
688736
{...props}
689737
/>
690-
{this.state.prompt ? (
738+
{this.state.prompt ? this.state.prompt.isCustom ? (
739+
<Prompt
740+
isCustom={this.state.prompt.isCustom}
741+
title={this.state.prompt.title}
742+
enterTitle={this.state.prompt.enterInfo.name}
743+
closeTitle={this.state.prompt.closeInfo.name}
744+
vm={vm}
745+
onCancel={this.handlePromptClose}
746+
onOk={this.handlePromptCallback}
747+
/>
748+
) : (
691749
<Prompt
692750
defaultValue={this.state.prompt.defaultValue}
693751
isStage={vm.runtime.getEditingTarget().isStage}

src/containers/prompt.jsx

Lines changed: 27 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,8 @@ class Prompt extends React.Component {
3434
event.target.select();
3535
}
3636
handleOk () {
37-
this.props.onOk(this.state.inputValue, {
37+
if (this.props.isCustom) this.props.onOk();
38+
else this.props.onOk(this.state.inputValue, {
3839
scope: this.state.globalSelected ? 'global' : 'local',
3940
isCloud: this.state.cloudSelected
4041
});
@@ -58,7 +59,18 @@ class Prompt extends React.Component {
5859
}
5960
}
6061
render () {
61-
return (
62+
if (this.props.isCustom) return (
63+
<PromptComponent
64+
isCustom={this.props.isCustom}
65+
title={this.props.title}
66+
enterTitle={this.props.enterTitle}
67+
closeTitle={this.props.closeTitle}
68+
onOk={this.handleOk}
69+
onCancel={this.handleCancel}
70+
onKeyPress={this.handleKeyPress}
71+
/>
72+
)
73+
else return (
6274
<PromptComponent
6375
isAddingCloudVariableScratchSafe={this.state.isAddingCloudVariableScratchSafe}
6476
canAddCloudVariable={this.state.canAddCloudVariable}
@@ -84,16 +96,21 @@ class Prompt extends React.Component {
8496
}
8597

8698
Prompt.propTypes = {
87-
defaultValue: PropTypes.string,
88-
isStage: PropTypes.bool.isRequired,
89-
showListMessage: PropTypes.bool.isRequired,
90-
label: PropTypes.string.isRequired,
99+
title: PropTypes.string.isRequired,
91100
onCancel: PropTypes.func.isRequired,
92101
onOk: PropTypes.func.isRequired,
93-
showCloudOption: PropTypes.bool.isRequired,
94-
showVariableOptions: PropTypes.bool.isRequired,
95-
title: PropTypes.string.isRequired,
96-
vm: PropTypes.instanceOf(VM)
102+
defaultValue: PropTypes.string,
103+
isStage: PropTypes.bool,
104+
showListMessage: PropTypes.bool,
105+
label: PropTypes.string,
106+
showCloudOption: PropTypes.bool,
107+
showVariableOptions: PropTypes.bool,
108+
vm: PropTypes.instanceOf(VM),
109+
110+
/* custom modals */
111+
isCustom: PropTypes.bool,
112+
enterTitle: PropTypes.string,
113+
closeTitle: PropTypes.string
97114
};
98115

99116
export default Prompt;

0 commit comments

Comments
 (0)