It is working only on window resize when on div(container) resize the chart is getting resized
From the below code you can check the component of reactgridlayout for draggable but not the chart in the paper. On window resize it's working fine i need with div resize(on draf of paper component).
onLayoutChange(layout) {
console.log(layout);
//chart.setSize(null, null);
}
_loadSavedChart(){
return (
<ResponsiveReactGridLayout layouts={this.state.layout}
onLayoutChange={this.onLayoutChange}
className={this.state.className}
rowHeight={this.state.rowHeight}
cols={this.state.cols}
initialLayout= {this.state.layout}
initialWidth={this.state.initialWidth}
width={this.state.width}
verticalCompact={false}
listenToWindowResize={false}
currentBreakpoint= 'sm'
>
{this.state.savedChartinDashboard.map((data, i) =>
<Paper key={i} className="c3chart" style={style.chartPaperStyle} zDepth={1}>{this._renderChart(data)}
</Paper>
)}
</ResponsiveReactGridLayout> )
}
I need to make use of this in react component instead of in jquery. I have a layout change how to make use of this in that.
$('#resizer').resizable({
// On resize, set the chart size to that of the
// resizer minus padding. If your chart has a lot of data or other
// content, the redrawing might be slow. In that case, we recommend
// that you use the 'stop' event instead of 'resize'.
resize: function () {
chart.setSize(
this.offsetWidth - 20,
this.offsetHeight - 20,
false
);
}
});
Related
I'm working on a commercial project in React where customer wants to display used and unused devices on one screen of the website. On the left, there is one div with used devices which takes up max 70% of the screen width and on the right is div with unused devices, which takes up min 30% of the screen width (or rather 550px). The number of used devices on the right may vary, so if there is not enough to fill the 70% of the screen, unused devices on the right will expand to fill the gap (display: flex). However, if there is more used devices than can fit in the 70% of the screen width, I want to display a custom overflow indicator on the left side of unused devices. The reason for this, is that the used devices will go under the used devices (z-index 0 and 1) and I want to show that there is overflow of used devices, that there are more than user can see. For this overflow indicator I wanted to use a gradient on the left side of unused devices. I did manage to achieve this, but with some problems.
I used a state boolean variable showMore to indicate when should I display the overflow indicator. The value change of this variable is happening in custom function checkOverflow:
function checkOverflow() {
if (ref.current && refUnused.current) {
if (
window.innerWidth - refUnused.current?.clientWidth <
ref.current?.clientWidth
) {
setShowMore(true);
} else {
setShowMore(false);
}
}
}
ref is representing used devices and refUnused unused devices. I use this function in useEffect and useLayoutEffect as I need to check this on load and on window resize.
useLayoutEffect(
() => {
if (reload()) {
checkOverflow();
}
window.addEventListener("resize", checkOverflow);
return function cleanupListener() {
window.removeEventListener("resize", checkOverflow);
};
}, // eslint-disable-next-line react-hooks/exhaustive-deps
[ref]
);
useEffect(
() => {
checkOverflow();
}, // eslint-disable-next-line react-hooks/exhaustive-deps
[ref]
);
However, the problem I ran into is flickering in the situation where the values differ just slightly. It's flickering between showing the overflow indicator and not showing it. I also get console error message
Warning: Maximum update depth exceeded. This can happen when a component calls setState inside useEffect, but useEffect either doesn't have a dependency array, or one of the dependencies changes on every render.
Does anyone know how to prevent this flickering and React error/warning? Or any idea on how to achieve this differently?
Here is the code:
<div className="vehicle-main-panel">
<div className="vehicle-standorten" ref={ref}> //used devices
{(() => {
const array = [];
items.stos.map((item, index) => {
return array.push(
<Veh
standort={item}
vehicles={item.vehicles}
key={index}
isVehicle={true}
></Veh>
);
});
return array;
})()}
</div>
<div className="vehicle-right-panel"> //unused vehicles and gradient
<div className="vehicle-overflow-hor-1">
<div className="vehicle-card-overflow">
<div className="vehicle-card-overflow-box gradient-90">
<p style={{ fontWeight: "bold", fontSize: "40px" }}>
... {/* {t("vehicles.more-items")} */}
</p>
</div>
</div>
</div>
<div
className={
showMore
? "vehicle-nonfunctional-absolute"
: "vehicle-nonfunctional-flex"
}
ref={refUnused}
>
<Veh value={items.notReady} isVehicle={false}></Veh>
</div>
</div>
</div>
I have a React application that is utilizing Material UI. The application has slider components implemented in a lot of places (https://material.io/components/sliders). When using a touch screen device, I am unintentionally impacting slider components (the value is getting changed) while trying to scroll up or down the page. This does not happen with other page components, I suspect this is because sliders do (and should) respond to swipe events.
Material UI has documentation that implies a way to discern between a "scroll" and a "swipe" (https://material.io/design/interaction/gestures.html#types-of-gestures). Is there a way for me to indicate to my slider components that a "scroll" should be ignored. Or, can I discern between a vertical or horizontal swipe, telling the slider to ignore vertical swipes?
I have come up with a fairly elegant solution, I believe, which allows the user to scroll if their scroll position begins on the track but not on the thumbs. This replicates the native HTML range input so I feel that this is the best solution.
There's two parts to this
Step 1, allow touch-action on the slider root element as it is disabled by default and prevents the user from starting a scroll on the slider
const useStyles = makeStyles({
sliderRoot: {
touchAction: "auto"
}
});
return (
<Slider
classes={{
root: classes.sliderRoot
}}
...
/>
Step 2, stop propagation on the root element with a ref
const ref = useRef(null);
useEffect(() => {
if (ref.current) {
ref.current.addEventListener(
"touchstart",
(e) => {
const isThumb = e.target?.dataset.index;
if (!isThumb) {
e.stopPropagation();
}
},
{ capture: true }
);
}
});
return (
<Slider
ref={ref}
...
/>
And here is a fully working demo on Codesandbox.
There is not going to be a completely clean solution to this other than telling your users to watch their finger placement when scrolling on mobile devices.
Using a controlled Slider, one approach would be to listen to the touchstart event and record the current pageY on the first changedTouches object. Then compare that coordinate to the pageY on the onChangeCommited event handler for the corresponding touchmove event. If the difference between the two coordinates is larger than some predefined range, then do not update the Slider value.
Inside your component using the Slider:
const delta = 50
const sliderRef = useRef(null)
const [value, setValue] = useState(0) // Or from some prop
const [touchStart, setTouchStart] = useState(0)
const debouncedHandler = useMemo(() => {
// Using lodash.debounce
return debounce((evt, value) => {
// If it is a mouse event then just update value as usual
if (evt instanceof MouseEvent) {
setValue(value)
}
}, 25)
}, [])
useLayoutEffect(() => {
if (sliderRef.current) {
sliderRef.current.addEventListener('touchstart', evt => {
setTouchStart(evt.changedTouches[0].pageY)
})
}
}, [])
return (
<Slider
value={value}
ref={sliderRef}
onChange={debouncedHandler}
onChangeCommitted={(evt, value) => {
if (evt instanceof TouchEvent) {
if (Math.abs(touchStart - evt.changedTouches[0].pageY) < delta) {
setValue(value)
}
} else {
setValue(value)
}
}}
/>
)
This will prevent the Slider from changing value on TouchEvent when the difference between the starting y-coordinate and the ending y-coordinate is larger than delta. Adjust delta to whatever value you like. The tradeoff is that you will not get as smooth of a transition when adjusting the Slider with a normal MouseEvent (or TouchEvent within the predefined range).
See the jsFiddle.
Or npm i mui-scrollable-slider-hook and use it like
import Slider from '#mui/material/Slider'
import { useMuiScrollableSlider } from 'mui-scrollable-slider-hook'
const { ref, value, onChange, onChangeCommitted } = useMuiScrollableSlider()
return <Slider ref={ref} value={value} onChange={onChange} onChangeCommitted={onChangeCommitted} />
An example of using mui-scrollable-slider-hook on codesandbox.
I am working on a dashboard part of a site and i am very new to reactjs.
I am trying fit everything on a screen so that the user does not have to scroll expect for the table, i hope the code below defines the situation better.
Here is my code:
componentDidMount() {
this.tableHeightOffset();
}
tableHeightOffset() {
var getOffset = this.containerLine.offsetTop;
this.setState({ getOffset });
}
render() {
var findTableHeight = this.state.getOffset;
const tableHeight = {
height: 'calc(100vh - ' + findTableHeight + 'px' + ')'
}
return (
<div className="table-responsive" style={tableHeight} ref={el => this.containerLine = el}>
)
}
How do i get the offset to change when the browser resize or there is a update on the site ?
Also I get the value on findTableHeight but it is not getting the offset from to the top of the window. I was suppose to get 161px of offsetTop but i am only getting 46px.
You could define a resize listener to the window, in order to calculate the new hight. This could be done in componentDidMount():
componentDidMount() {
window.addEventListener('resize', this.tableHeightOffset);
}
Don't forget to remove it before unmounting the component:
componentWillUnmount() {
window.removeEventListener('resize', this.tableHeightOffset);
}
Also offsetTop returns the offset from the parent. In order to find the offset relative to the document, check this article: Finding element's position relative to the document
Here you have api to listen for window size change https://developer.mozilla.org/en-US/docs/Web/API/Window/resize_event use it to dynamically change your offset.
I have been searching for a discussion about if it's possible to mimic the html tag textarea's resizing when using Monaco Editor's field all over the Internet but I couldn't find one answering my question.
I'm using the monaco-editor npm package in a React application. Do you have any idea if this is easy to implement?
Thank you in advance!
SOLUTION
With pure css I selected the target html element and just added these properties:
div {
resize: vertical;
overflow: auto;
}
TL;DR: add automaticLayout: true to your editor's configuration.
NL;PR:
Monaco has a built-in auto resize to parent container functionality:
createEditorWithAutoResize(){
this.editor = monaco.editor.create(
this.editorDiv.current, {
value: "var x = 0;",
language: 'javascript',
automaticLayout: true // <<== the important part
}
);
}
componentDidMount(){this.createEditorWithAutoResize();}
constructor(props){super(props); this.editorDiv = React.createRef();}
render(){return <div ref={this.editorDiv} className="editor" ></div>}
And the CSS for the editor (it avoids rendering the editor for the first time with like 10px height):
.editor{
height: 100%;
}
First tested: v0.10.1, Last tested: v0.32.1
Note:
< v0.20.0: The mechanism does not listen to its container size changes, it polls them.
#nrayburn-tech (Monaco Editor's contributor): Version 0.20 uses MutationObserver for all browsers. Version 0.21 and later uses ResizeObserver on supported browsers, otherwise, it uses polling as a fallback.
if you have a reference to the editor you can just call
editor.layout()
on some resize event.
For example, on window resize:
window.onresize = function (){
editor.layout();
};
For anyone coming here having this issue in a basic web app (html, css, javascript) I've found a solution for the resizing issue I'm experiencing.
I have the monaco editor in a resizable flex container. It will only grow the width, not shrink it, and vertical resizing doesn't seem to work out of the box.
If you use the monaco config "automaticLayout: true" and the following CSS it seems to resize as expected:
.monaco-editor { position: absolute !important; }
I tried the max-width 99% trick but it causes a laggy delayed effect when increasing the width near edge of page.
For posterity, the solution I arrived on was to set automaticLayout: false so that I could perform all the layout in a resize event listener.
const placeholder = document.getElementById('placeholder')
const editor = monaco.editor.create(placeholder, {
value: '// hello world',
language: 'javascript',
automaticLayout: false // or remove, it defaults to false
})
// we need the parent of the editor
const parent = placeholder.parentElement
window.addEventListener('resize', () => {
// make editor as small as possible
editor.layout({ width: 0, height: 0 })
// wait for next frame to ensure last layout finished
window.requestAnimationFrame(() => {
// get the parent dimensions and re-layout the editor
const rect = parent.getBoundingClientRect()
editor.layout({ width: rect.width, height: rect.height })
})
})
By first reducing the editor layout to 0 we can safely query the dimensions of the parent element without the child (editor) contributing to its size. We can then match the editor to the new parent dimensions. Since this takes place over a single frame, there should be no flickering or lag.
this is old question but get the problem to and solved it with react-resize-detector
based on ResizeObserver it feet perfectly to the need (check browser compatibility)
Exemple of component :
import React, { Component } from 'react';
import ReactResizeDetector from 'react-resize-detector';
import * as monaco from 'monaco-editor';
class Editor extends Component {
constructor(props) {
super(props)
this.state = {
width: 0,
height: 0,
}
this.editor_div = React.createRef()
this.handle_rezise = this.handle_rezise.bind(this);
}
componentDidMount() {
const editor_model = monaco.editor.createModel('', 'sql');
this.monaco_editor = monaco.editor.create(this.editor_div.current, this.props.editorOptions);
this.monaco_editor.setModel(editor_model);
}
componentWillUnmount() {
this.monaco_editor && this.monaco_editor.dispose();
}
handle_rezise(width, height) {
this.monaco_editor.layout({ height, width });
}
render() {
return(
<div
className="editor-container"
style={{ height: '100%' }}>
<ReactResizeDetector
handleWidth
handleHeight
onResize={ this.handle_rezise }
refreshMode="debounce"
refreshRate={100} />
<div
className="editor"
ref={ this.editor_div }
style={{ height: '100%' }} />
</div>
)
}
}
export default Editor;
Hope it's help
In my case I'm using that exact CSS but although automaticLayout: true works, I found out overkill (seems to pooling the DOM 100ms interval and I have several editors opened in the document. SO I ended up implementing it manually :
just in case , my needs are different: I want the user to resize it the container - in a standard way and cheap (both on code and performance) on libraries and performance. This is what I did:
css container : resize: vertical; overflow: auto
and this js :
function installResizeWatcher(el, fn, interval){
let offset = {width: el.offsetWidth, height: el.offsetHeight}
setInterval(()=>{
let newOffset = {width: el.offsetWidth, height: el.offsetHeight}
if(offset.height!=newOffset.height||offset.width!=newOffset.width){
offset = newOffset
fn()
}
}, interval)
}
const typeScriptCodeContainer = document.getElementById('typeScriptCodeContainer')
typeScriptCodeEditor = monaco.editor.create(typeScriptCodeContainer, Object.assign(editorOptions, {value: example.codeValue}))
installResizeWatcher(typeScriptCodeContainer, typeScriptCodeEditor.layout.bind(typeScriptCodeEditor), 2000)
yes, 2 seconds interval and make sure it registers only once. I see there is / was a resize interval on 100ms for the automatic relayout in monaco - IMHO that's too much.
See it in action: https://typescript-api-playground.glitch.me/?example=2
How to I scale a react component already created into the browser that already has a height and width predefined???
componentDidMount(x,y,z){
this.setState({height:window.innerHeight+'px'});
}
Not sure if this is the right way to go, I am not sure if I have to get th viewport size first and then later try to scale the on the browser later. How to can be accomplished?
You can assign the styles directly to the DOM element. For example:
render() {
return <div style={{ height: window.innerHeight }} />;
}
If you need to calculate the style after initial render, perhaps after some user interaction, you could calculate and store the styles in state:
// ... calculate and set state logic somewhere
// eg: someFunction() { this.setState({ height: window.innerHeight }); }
render() {
let styles = {};
if (this.state.height) {
styles.height = this.state.height;
}
return <div style={styles} />;
}