Making two Component same height when one is a array map - javascript

I am learning React and Redux and now I have this problem.
Here's a codesandbox
I want this two Component to be side by side and always have the same height even if Component2 grow larger since it's a map.
Try like this:
Search for book title "dep"
Watch the log grow pushing down the screen
Here's an image showing the Component2 getting larger then Component1 and I don't want that I have added style={{overflowY:"scroll"} to Component2 but don't understand why it pushes down anyway.
I tried using FlexBox but it's not working:
.row {
display: flex; /* equal height of the children */
}
.col {
flex: 1; /* additionally, equal width */
padding: 1em;
border: solid;
}
I don't want to set a fixed height.

looks like you need to do more CSS here.
Try to add a max-height: 300px in that component that have the overflow-y.

I've managed to solve this without setting height manually.
So, I've added a ref for the form.
this.formRef = React.createRef();
Used ref into the form tag.
<form ref={this.formRef}
And added an extra property to the state:
formHeight: "200px"
At componentDidMount hook I've assigned the height of the form into the state.
componentDidMount() {
this.setState(
(prevState) => ({
localBook: {
...prevState.localBook
},
formHeight:
this.formRef.current.parentElement.clientHeight.toString() + "px"
}),
() => {}
);
}
At your logger component I've added another style from props.
<div style={{ overflowY: "scroll", maxHeight: props.maxHeight }}>
And passed the prop from the parent:
<EnhancedTable maxHeight={this.state.formHeight} />
Demo at CodeSandbox.
Result:

Related

(Resolved )Best way to remove Draggable React component from flow without jumping?

I have Note components that are rendered using notes.map(), each with position: static. The notes are draggable using react-draggable npm module.
The functionality I'm trying to achieve is when a note is deleted, to not affect the position of notes that have been dragged by the user. I've attempted to set position: absolute for notes that have been dragged. However, this causes the note to 'jump' in position once (happens when removed from flow).
-- Initial State:
-- After first drag attempt, test note jumps on top of other note:
-- Able to drag normally after first attempt:
I've included relevant code for Notes.jsx component:
function Note(props) {
const [dragDisabled, setDragDisabled] = useState(true);
const [beenDragged, setBeenDragged] = useState(props.beenDragged);
const [position, setPosition] = useState({ xPos: props.xPos, yPos: props.yPos });
// Drag Note Functions
function handleClick() {
setDragDisabled(prevValue => {
return !prevValue;
})
}
function firstDrag(event) {
if (!beenDragged) {
axios.post("api/note/beenDragged", {id: props.id})
.then(setBeenDragged(true));
}
}
function finishDrag(event, data) {
setPosition({ xPos: data.x, yPos: data.y });
}
useEffect(() => {
axios.post("/api/note/updateposition", {position, id: props.id });
}, [position]);
return <Draggable
disabled={dragDisabled}
onStart={firstDrag}
onStop={finishDrag}
defaultPosition={{ x: props.xPos, y: props.yPos }}
// position={location}
>
<div className='note' style={{position: beenDragged ? 'absolute' : 'static'}}>
<h1>{props.title}</h1>
<p>{props.content}</p>
<button onClick={handleClick}>
{dragDisabled ? <LockIcon /> : <LockOpenIcon />}
</button>
<EditPopup title={props.title} content={props.content} editNote={editNote} />
<DeletePopup deleteNote={deleteNote} />
</div>
</Draggable>
}
and for my CSS styling
.note {
background: #fff;
/* background-image: url("https://www.transparenttextures.com/patterns/notebook-dark.png"); */
background-image: url("https://www.transparenttextures.com/patterns/lined-paper-2.png");
border-radius: 7px;
box-shadow: 0 2px 5px rgb(120, 150, 179);
padding: 10px;
width: 240px;
margin: 16px;
float: left;
}
Relevant code for how App.jsx renders the notes:
function App() {
const [notes, setNotes] = useState([]);
return (
<div id="bootstrap-override">
{notes.map((note) => {
return <Note
key={note._id}
id={note._id}
title={note.title}
content={note.content}
xPos={note.xPos}
yPos={note.yPos}
beenDragged={note.beenDragged}
deleteNote={deleteNote}
editNote={editNote}
/>
})}
</div>);
}
Any help is much appreciated, thank you!
I solved the issue not directly, but rather using a different approach to get the result I was looking for.
To summarize, I used position: absolute for all notes, and created them at random x,y coordinates within a boundary. This way deleted notes would not affect the position of existing notes.
Hope this helps anyone with similar issues!
When your draggables are position: static, you'll experience that screen jank when removing an element because the order of the elements in the document help define where your draggables live, regardless of their dragged state (which boils down to something like transform: translate(100px, 150px);.
The translate is like saying: "go over 100px and up 100px from wherever you live. When we delete the first element, everybody shifts. They can still translate(100px, 150px); but the point from which they originate will have changed.
Someone mentioned setting the draggable's to position: absolute and the parent as position: relative. That works, but might introduce headaches of it's own.
I will offer an innocent, perhaps naive, solution: hiding the element. A quick and dirty way: instead of removing the element from the DOM, you can set the element's visibility: none. You can even delete the item in the backend and offer a "restore note" feature in the UI since the draggable will still contain content.
And whenever you're hiding things, make sure you do so with accessibility in mind. Here's a good article on that.
I also made a derpy screencast: https://www.youtube.com/watch?v=Fr0PfT3Frzk&ab_channel=SamKendrick. It goes a little fast, but I begin by deleting the first element to illustrate your problem. Then I undo the delete and instead attach a visibility: hidden style to the element I want to delete.

CSS Flex + fixed size container height + disabling partially drawn items due to overflow:hidden

I'll try to simplify my question:
I have a div container with a fixed height of 200px. Inside, I have a flexbox that shows vertical items. Each item has a FIXED height which should not change. I use overflow: hidden to prevent the last item to break out of that 200px. This works well.
This is what I have with overflow: hidden
This is what I have without overflow: hidden
But now, I'd like to take one step forward and prevent the rendering of the last item, if it's about to be cut and not displayed fully due to the container fixed height limitations and overflow: hidden
This is what I really want, show only those items which are not cut fully or partially by the overflow: hidden;
What's the best practice of achieving that? a kind of "make sure all items fit in their fixed height inside the fixed height component and if one doesn't fit, don't show it at all".
Using the lastest React. Probably doesn't matter but still.
I've made a small example here.
https://jsfiddle.net/hfw1t2p0/
Basically, I want to keep enforcing the 200px max height of the flexbox, but have some kind of automation that kills all elements which are partially or fully invisible, like items "4" and "5" in the example.
Please note the 200px flexbox height, and 50px item height are just examples. In reality, I need a flexbox that can weed out any item that doesn't fit fully in it... the max height of the flexbox or minimum height of elements is unknown until runtime.
First Thing : you should get benefits from using react:
To make Content Dynamically I'll add gridItem to state so that they're rendered dynamically.
state = {
items: [
"1",
"2",
"3",
" 4 I want this hidden, its partially visible",
"5 I want this hidden, its partially visible"
]
};
And For render:
render() {
return (
<div className="test">
<div className="gridContainer">
{this.state.items.map(el => {
return <div className="gridItem">{el}</div>;
})}
</div>
</div>
);
}
.
First Demo
Here is the Cool Part:
Based on:
Each item has a FIXED height which should not change
So that all items should have same height. The solution is to add:
1- ItemHeight
2- ContainerHeight
3-BorderWidth
to the state. Now with Some calculations + inline Styling You can achieve Your Goal:
first Your state will be:
state = {
containerHeight: 200, // referring to Container Height
gridHeight: 50, // referring to grid item Height
border: 1, // referring to border width
items: [
"1",
"2",
"3",
" 4 I want this hidden, its partially visible",
"5 I want this hidden, its partially visible"
]
};
in your render() method before return add this:
let ContHeight = this.state.containerHeight + "px";
let gridHeight = this.state.gridHeight + "px";
let border = this.state.border + "px solid green";
let gridStyle = {
maxHeight: ContHeight,
};
These are the same styles used in css but They're removed now from css and applied with inline styling.
Container will take it's max height property as:
<div className="gridContainer" style={gridStyle}> //gridStyle defined above.
let's see How gridItems will b e renderd:
//el for element, index for index of the element
{this.state.items.map((el, index) => {
// i now will start from 1 instead of 0
let i = index + 1,
// current height is calculating the height of the current item
// first item will be like: 1*50 + 1*1*2 = 52
// second item will be like: 2*50 + 2*1*2 = 104
// and so on
CurrentHeight =
i * this.state.gridHeight + i * this.state.border * 2,
// now we should determine if current height is larger than container height
// if yes: new Class "hidden" will be added.
// and in css we'll style it.
itemStyle =
CurrentHeight <= this.state.containerHeight
? "gridItem"
: "gridItem hidden";
return (
// YOU'RE A GOOD READER IF YOU REACHED HERE!
// now styleclass will be added to show-hide the item
// inline style will be added to make sure that the item will have same height, border-width as in state.
<div
className={itemStyle}
style={{ height: gridHeight, border: border }}
>
{el}
</div>
);
})}
Finally! in css add this:
.gridItem.hidden {
display: none;
}
Final Demo 1
Final Demo 2 with 40px gridItem height
Final Demo 3 with 300px container height
You could use the flexbox columns to send the overflowing items to another column and set the width to the width of the container to hide the items if you want to achieve this by plain flexbox css properties like you mentioned in the question. https://jsfiddle.net/kna61edz/
.gridContainer {
display: flex;
flex-direction: column;
background: yellow;
max-height: 200px;
overflow: hidden;
flex-wrap: wrap;
width: 52px;
}
The only difference is that you won't be seeing the yellow background of the gridContainer but you can set the background in the enclosing div like in the picture below but it will adapt to the height of the child divs.
why not test elements with document.getElementById('element').clientHeight?
check the container size and find which element is visible or not.

Monaco editor dynamically resizable

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

Can't change element's height

I'm trying to resize element by dragging (like so). I wrote a simple directive (will handle ghostbar later):
#Directive({ selector: '[drag-resize]' })
export class DragResizeDirective {
private dragging: boolean;
constructor(el: ElementRef, renderer: Renderer2) {
renderer.listen('window', 'mouseup', (e) => {
if (this.dragging) {
el.nativeElement.style.backgroundColor = 'red'; // Works fine.
el.nativeElement.style.height = `${e.pageY + 2}px`; // Not so much.
this.dragging = false;
}
});
}
#HostListener('mousedown') onMouseDown() {
this.dragging = true;
}
}
The problem is I can't change height of the element. Other styles work fine. I'm using Angular 4.0.3.
Computed attributes:
display: block;
height: 244.781px;
left: 0px;
overflow-x: hidden;
overflow-y: scroll;
position: absolute;
top: 655.422px;
width: 1793px;
*renderer.setStyle() doesn't work either.
** Element I'm trying to resize is a grid tile (md-grid-tile from Angular Material).
*** Directive works on other elements.
Edit 2:
I've dug into the md-grid-list implementation. The row height is recalculated everytime that ngAfterContentChecked is triggered (code). This happens after every mouse event and is probably the reason why setting the height has no effect.
From the md-grid-list documentation I can see that you can also pass a rowHeight (e.g. 200px) input parameter to the `md-grid-list. This seems to be cleanest way to set the row height, but will scale all rows to the same size.
If this is not the effect you want to achieve, you can try setting the height in the ngAfterViewChecked lifecycle hook.
Edit:
In case your code is trying to resize a display: inline element, you first have to apply a e.g. display: inline-block to it. Otherwise it will ignore the height value.
The style.height attribute expects numeric values to have a unit (e.g. %, px, rem, em, vh).
This should work:
el.nativeElement.style.height = `${e.pageX + 2}px`;

How to scale a reactjs component into the browser

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} />;
}

Categories

Resources