Blocklys Textblock input fields can not be edited when in a Material-UI Modal
I only found https://groups.google.com/g/blockly/c/SDUosMpAFAk to my problem, but it has no answers that could help me, so I created a Codesandbox to reproduce the behavior.
https://codesandbox.io/s/gallant-galois-bqjjb
The button in the Sandbox will open a modal with a Blockly Canvas in it. Trying to write something in the "text"- or "math_number"-Block does not work, and when you close the modal, with an outside click, some artifacts are staying.
I would be glad if someone can help me out with this.
EDIT: In case of the CodeSandbox link is not working.
Dependencies:
- @material-ui/core: 4.12.3
- @material-ui/styles: 4.11.4
- blockly: 6.20210701.0 (6.20210701.0)
- react: 17.0.2
- react-dom: 17.0.2
- react-scripts: 4.0.0
- react-use: 17.3.1
CODE:
index.js
import { StrictMode } from "react";
import ReactDOM from "react-dom";
import App from "./App";
const rootElement = document.getElementById("root");
ReactDOM.render(
<StrictMode>
<App />
</StrictMode>,
rootElement
);
App.js
import React from "react";
import "./styles.css";
import { Modal } from "@material-ui/core";
import BlocklyContainer from "./BlocklyContainer";
export default function App() {
const [blocklyModalOpenRule, setBlocklyModalOpenRule] = React.useState(false);
const handleOpenBlocklyModal = () => setBlocklyModalOpenRule(true);
const handleCloseBlocklyModal = () => setBlocklyModalOpenRule(false);
return (
<div className="App">
<p>The button will open a modal with a Blockly Canvas in it.</p>
<p>Trying to write something in the "text"- or "math_number"-Block</p>
<p>does not work, and when you close the modal, with an outside click,</p>
<p>some artefacts are staying</p>
<button onClick={handleOpenBlocklyModal}>open</button>
<Modal
open={!!blocklyModalOpenRule}
onClose={handleCloseBlocklyModal}
aria-labelledby="blockly-modal-label"
aria-describedby="blockly-modal-description"
disableEnforceFocus
>
<div
style={{
top: `50%`,
left: `50%`,
transform: `translate(-50%, -50%)`,
height: "90vh",
width: "90vw",
position: "absolute",
background: "white"
}}
>
<BlocklyContainer />
</div>
</Modal>
</div>
);
}
BlocklyContainer.js
import React, { useRef } from "react";
import BlocklyComponent, { Block, Category } from "./Blockly";
import { Grid } from "@material-ui/core";
import { useMeasure } from "react-use";
const initialXML = `
<xml xmlns="http://www.w3.org/1999/xhtml">
<block type="text" id=".mq~5Vo#Hz32wh/q98Sv" x="10" y="10">
</block>
<block type="math_number" id=".mq~5Vo#Hz32wh/q98Sv" x="10" y="40">
</block>
</xml>
`;
const BlocklyContainer = () => {
const simpleWorkspace = useRef();
const [ref, { height }] = useMeasure();
return (
<div
ref={ref}
style={{
flexGrow: 0,
maxWidth: "100%",
flexBasis: "100%",
margin: 0,
boxSizing: "border-box",
minHeight: 650,
height: "100%",
maxHeight: 700
}}
>
<Grid item md={12} style={{ height }}>
<div
style={{
position: "relative",
width: "100%",
height: "100%",
zIndex: 1400
}}
>
<BlocklyComponent
ref={(ref) => (simpleWorkspace.current = ref)}
readOnly={false}
trashcan={true}
media="media/"
move={{
scrollbars: true,
drag: true,
wheel: true
}}
initialXml={initialXML}
>
<Category name="Control" colour="210">
<Block type="controls_if" />
<Block type="controls_ifelse" />
<Block type="logic_compare" />
<Block type="logic_boolean" />
<Block type="logic_negate" />
<Block type="logic_ternary" />
<Block type="math_arithmetic" />
<Block type="text" />
<Block type="math_number" />
</Category>
</BlocklyComponent>
</div>
</Grid>
</div>
);
};
export default React.memo(BlocklyContainer);
Blockly/index.js
/**
* @license
*
* Copyright 2019 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @fileoverview XML wrappers for block, category, value, field and shadow.
* @author [email protected] (Sam El-Husseini)
*/
import React from "react";
import BlocklyComponent from "./BlocklyComponent";
export default BlocklyComponent;
const Block = (p) => {
const { children, ...props } = p;
props.is = "blockly";
return React.createElement("block", props, children);
};
const Category = (p) => {
const { children, ...props } = p;
props.is = "blockly";
return React.createElement("category", props, children);
};
const Value = (p) => {
const { children, ...props } = p;
props.is = "blockly";
return React.createElement("value", props, children);
};
const Field = (p) => {
const { children, ...props } = p;
props.is = "blockly";
return React.createElement("field", props, children);
};
const Shadow = (p) => {
const { children, ...props } = p;
props.is = "blockly";
return React.createElement("shadow", props, children);
};
export { Block, Category, Value, Field, Shadow };
Blocky/BlocklyComponent.js
/**
* @license
*
* Copyright 2019 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @fileoverview Blockly React Component.
* @author [email protected] (Sam El-Husseini)
*/
import React from "react";
import "./BlocklyComponent.css";
import Blockly from "blockly/core";
import locale from "blockly/msg/en";
import "blockly/blocks";
Blockly.setLocale(locale);
class BlocklyComponent extends React.Component {
blocklyDiv = React.createRef();
toolbox = React.createRef();
workspace;
setWorkSpace(xml) {
if (this.workspace) {
const dom = Blockly.Xml.textToDom(xml);
Blockly.Xml.domToWorkspace(dom, this.workspace);
}
}
componentDidMount() {
const { initialXml, children, ...rest } = this.props;
this.workspace = Blockly.inject(this.blocklyDiv.current, {
toolbox: this.toolbox.current,
...rest
});
if (initialXml) {
this.setWorkSpace(initialXml);
}
}
// setXml(xml: string) {
// this.setWorkSpace(xml)
// }
render() {
const { children } = this.props;
return (
<React.Fragment>
<div ref={this.blocklyDiv} id="blocklyDiv" />
<xml
xmlns="https://developers.google.com/blockly/xml"
is="blockly"
style={{ display: "none" }}
ref={this.toolbox}
>
{children}
</xml>
</React.Fragment>
);
}
}
export default BlocklyComponent;
styles.css
body {
background: greenyellow;
}
.App {
font-family: sans-serif;
text-align: center;
}
Blockly/BlocklyComponent.css
#blocklyDiv {
height: 100%;
width: 100%;
position: absolute;
bottom: 0;
}
.blocklySvg {
height: 100%;
}
.blocklyFlyout {
transform: translate(90px, 0px) !important;
height: 100%;
}
.blocklyFlyoutBackground {
height: 100%;
transform: scaleY(99999999999);
}
.blocklyDropDownDiv {
z-index: 9999999;
}
Solution 1:
You can set the property disableEnforceFocus
to true
, and that will solve the problem for the input text/number blocks. However the problem persists for blocks using selection elements (e.g. logic_compare
, math_arithmetic
).
<Modal
...
disableEnforceFocus
>
....
</Modal>
To solve the second problem I changed the z-index for the blocklyDropDownDiv and now everything works perfectly.
.blocklyDropDownDiv {
z-index: 5000;
}