Elements (React Components) with position: relative overlapping - javascript

tl;dr: I'm finding that consecutive elements on my page with position: relative are overlapping, and really don't understand why.
Details: I'm trying to write a re-usable React component SlidePair that allows you to toggle between two other components with a slide animation, passing data between them. I'm using react-reveal for the animation, and positioning the elements at the same place in the page, so the exit of one accompanies the entry of the other. To make the divs overlap I followed the approach of this SO question.
My code, in the context of an App.js file from create-react-app, is below. You see that in the main App component I want to stack two SlidePair elements, which turn out to overlap instead. Everything else are just some example components for me to plug in; I've only included them for the sake of anyone who just wants to copy and paste the whole thing to get it running.
import React, {Component} from 'react';
import Slide from 'react-reveal/Slide';
class RedBox extends Component {
constructor(props){
super(props);
this.handleChange = this.handleChange.bind(this);
this.state = {
text: props.text
}
}
handleChange(event) {
this.setState({text: event.target.value});
}
render(){
const { toggleState, text, style} = this.props;
return(
<div style={style}
onClick={()=>{console.log('red clicked'); toggleState({text: this.state.text})}}>
<input onChange={this.handleChange}
type="text" value={this.state.text}
onClick={(event)=>{event.stopPropagation()}}
style={{zIndex: '999'}}
/>
{ text }
</div>
);
}
}
const BlueBox = ({toggleState, passedProps, style })=> {
return (
<div onClick={toggleState} style={style}>
{ passedProps.text }
</div>
);
};
class MouseTracker extends React.Component {
constructor(props) {
super(props);
this.handleClick = this.handleClick.bind(this);
}
handleClick(event) {
const coords = {
x: event.clientX,
y: event.clientY
};
this.props.toggleState(coords);
}
render() {
return (
<div style={{ height: '100px' }} onClick={this.handleClick}>
<h1>Click me!</h1>
</div>
);
}
}
const MouseInformer = ({toggleState, passedProps}) => (
<div>
You clicked {passedProps.x}, {passedProps.y}!
<button onClick={toggleState}>Go Back</button>
</div>
);
class SlidePair extends Component {
constructor(props){
super(props);
this.state = { left: true, passedProps: {}};
this.toggleState = this.toggleState.bind(this);
}
toggleState(passedProps){
const left = !this.state.left;
console.log(`Toggling left to ${left}`);
this.setState({ left, passedProps });
}
render(){
const {left, passedProps } = this.state;
return(
<div style={{position: 'relative'}}>
<Slide left when={left} >
<div style={ {position: 'absolute', top: '0px', right: '0px', width: '100%', zIndex: left ? '998' : -1 }}>
{this.props.renderLeft(this.toggleState, passedProps)}
</div>
</Slide>
<Slide right when={!left}>
<div style={{position: 'absolute', top: '0px', right: '0px', width: '100%', zIndex: left ? -1 : 1}}>
{ this.props.renderRight(this.toggleState, passedProps) }
</div>
</Slide>
</div>
)
}
}
class App extends Component {
render(){
const redBox = (toggleState, passedProps)=>(
<RedBox toggleState={toggleState}
style={{width: '100%', border: '5px solid red', height: '100px'}}/>
);
const blueBox = (toggleState, passedProps) => (
<BlueBox
toggleState={toggleState}
passedProps={passedProps}
style={{width: '100%', border: '5px solid blue', height: '100px'}}
/>
);
const mouseTracker = (toggleState, passedProps) => (
<MouseTracker toggleState={toggleState} passedProps={passedProps} style={{top: '300px'}}/>
);
const mouseInformer = (toggleState, passedProps) => (
<MouseInformer toggleState={toggleState} passedProps={passedProps} style={{top: '300px'}}/>
);
return (
<div className="App">
<SlidePair renderLeft={redBox} renderRight={blueBox}/>
<br/>
<SlidePair renderLeft={mouseTracker} renderRight={mouseInformer} />
</div>
);
}
}
export default App;
Thanks in advance for any help.

Related

How to change css code by pressing a button

Goal:
When you press on the button 'ok', the id element named test2 should be display non and id element named test1 should be display block with support of css code.
And also please take account to the color of the text that is located in the css code.
Problem:
I don't know how to solve it.
What is needed to be changed in the source code in order to achieve the goal?
Stackblitz:
https://stackblitz.com/edit/react-modal-gdh4hp?
Info:
*I'm newbie in Reactjs
Thank you!
index.js
import React, { Component } from 'react';
import { render } from 'react-dom';
import { Modal } from './modal';
import './style.css';
class App extends Component {
constructor() {
super();
this.state = {
modal: true
};
}
handleCloseModal = () => {
alert('ddd');
};
render() {
const { modal } = this.state;
const non = {
display: 'none',
color: 'yellow'
};
const block = {
display: 'block',
color: 'yellow'
};
return (
<div>
{modal ? (
<Modal
onClose={() => {
this.setState({ modal: false });
}}
>
<div id="test1" style={non}>Awesome1</div>
<div id="test2">Awesome2</div>
<button onClick={() => this.handleCloseModal()}>ok</button>
</Modal>
) : (
<button
onClick={() => {
this.setState({ modal: true });
}}
>
Show modal
</button>
)}
</div>
);
}
}
render(<App />, document.getElementById('root'));
modal.js
import React from 'react';
export class Modal extends React.Component {
render() {
const { children, onClose } = this.props;
return (
<div style={{position: "absolute", top: 0, left: 0, width: "100%", height: "100%", background: "gray"}} onClick={ev => onClose()}>
<div
style={{margin: "auto", background: "white", border: "red", width: "500px", height: "300px"}}
onClick={ev => ev.stopPropagation()}>
{ children }
</div>
</div>
);
}
}
You can simply do that :
ok() => {
document.getElementById('test1').style.display = 'block'
document.getElementById('test2').style.display = 'none'
}
You can use state
class App extends Component {
constructor() {
super();
this.state = {
modal: true,
showBlock: "",
showNon: "",
color: ""
};
}
handleCloseModal = () => {
this.setState({showBlock: "block"});
this.setState({showNon: "none"});
this.setState({color: "yellow"});
alert('ddd');
};
render() {
const { modal } = this.state;
return (
<div>
{modal ? (
<Modal
onClose={() => {
this.setState({ modal: false });
}}
>
<div id="test1" style={{display: this.state.showBlock, color: this.state.color}}>Awesome1</div>
<div id="test2" style={{display: this.state.showNon, color: this.state.color}}>Awesome2</div>
<button onClick={() => this.handleCloseModal()}>ok</button>
</Modal>
) : (
<button
onClick={() => {
this.setState({ modal: true });
}}
>
Show modal
</button>
)}
</div>
);
}
}
render(<App />, document.getElementById('root'));
React way to achieve that is to use useRef hook (or createRef for class approach):
Class approach:
constructor(props) {
this.testRef = React.createRef()
}
const toggleBlock = () => {
testRef.current.style.display = 'block'
testRef.current.style.color = 'yellow'
}
render() {
return (
<>
<div id="test1" ref={testRef}>Awesome1</div>
<button onclick={this.toggleBlock}>Ok</button>
</>
)
}
Hooks approach:
const testRef = useRef(null)
const toggleBlock = () => {
testRef.current.style.display = 'block'
testRef.current.style.color = 'yellow'
}
return (
<>
<div id="test1" ref={testRef}>Awesome1</div>
<button onclick={this.toggleBlock}>Ok</button>
</>
)

Problem with array of refs to reference a particular column

I can't get refs working. I want to render several columns and then render items that are placed over the right column. For example, declared items: const items = [0, 2, 5] should be rendered over the grid like that (assuming that first column has index 0):
I want to use refs on columns for getting column positions (using getBoundingClientRect() just for the idea) but my columnsRefs[index].current is null even though refs are assigned in componentDidMount as it's recommended. Here is my code:
import * as React from 'react';
export const GridColumn = (props: {forwardedRef?: any }) => (
<div style={{borderRight: "1px solid #ababab", width: "20px", height: "200px"}} ref={props.forwardedRef}></div>
);
interface GridProps {
}
export class Grid extends React.Component<GridProps, any> {
public constructor(props: GridProps) {
super(props);
this.state = {
columns: []
}
}
private columnRefs = new Array<React.RefObject<HTMLDivElement>>();
public componentDidMount(){
// setup the columns with refs
const columnIds = [0, 1, 2, 3, 4, 5, 6, 7];
const columns = columnIds.map(id => {
const ref = React.createRef<HTMLDivElement>();
this.columnRefs.push(ref);
return (<GridColumn forwardedRef={ref}/>)
});
this.setState({columns: columns});
}
public render() {
// setup the items
const items = [
0, 2, 5
]
let elements = items.map(item => {
if(this.columnRefs[item] && this.columnRefs[item].current) {
return (
<div style={{backgroundColor: "blue",
opacity: 0.5,
position: "absolute",
width: "10px",
height: "20px",
left: `${this.columnRefs[item].current.getBoundingClientRect().left}px`,
top: "0"}}>
</div>);
}
else {
return undefined;
}
});
return (
<div style={{width: "500px", height: "200px", position: "relative"}}>
{elements}
<div style={{display: "flex"}}>
{this.state.columns}
</div>
</div>);
}
}
What do I do wrong so that the condition in render if(this.columnRefs[item] && this.columnRefs[item].current) is all the time false?
You should not store your rendered components in state. State is for data you want to transform into markup. Here is modified code with annotations:
// Now component properly forwards ref
const GridColumn = React.forwardRef((props, ref) => (
<div
style={{ borderRight: "1px solid #ababab", width: "20px", height: "200px" }}
ref={ref}
{...props}
></div>
));
class Grid extends React.Component {
constructor(props) {
super(props);
this.state = {
columns: [1,2,3,4,5,6,7,8,9],
rendered: false
};
}
// When you want to use collection of refs, you can set up your own storage for them
// I use Set because it will prevent duplicates
// It is important that when component will be removed it's ref will turn into `null`, so you will have to check if your div still exists
columnRefs = new Set();
componentDidUpdate() {
// you will see your refs set in console on each render
console.debug(this.columnRefs);
}
componentDidMount() {
// You need to rerender to render something from refs
this.setState({rendered: true});
}
render() {
const items = [0, 2, 5];
let elements = items.map((item) => {
// turn set into array and filter out null
let refArray = [...this.columnRefs].filter(Boolean);
if (refArray[item]) {
return (
<div
key={item}
style={{
backgroundColor: "blue",
opacity: 0.5,
position: "absolute",
width: "10px",
height: "20px",
left: `${refArray[item].getBoundingClientRect().left}px`,
top: "0",
}}
></div>
);
} else {
return undefined;
}
});
return (
<div style={{ width: "500px", height: "200px", position: "relative" }}>
{elements}
<div style={{ display: "flex" }}>
{/* We use here ref callback to store forwarded ref into out set */}
{this.state.columns.map((item) => (
<GridColumn key={item} ref={(ref) => this.columnRefs.add(ref)} />
))}
</div>
</div>
);
}
}
What to read:
callback refs
forwarding refs
Set
Example
Reproducible and partially fixed example: https://jsfiddle.net/moL1sbzj/
I'm not sure if refs are needed at all
Use left:
left: item * 20 + 'px',
it's cheaper than .current.getBoundingClientRect().left
result:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
</head>
<body>
<div id="root"></div>
<script src="https://unpkg.com/react#17/umd/react.development.js" crossorigin ></script>
<script src="https://unpkg.com/react-dom#17/umd/react-dom.development.js" crossorigin ></script>
<script src="https://unpkg.com/babel-standalone#6/babel.min.js"></script>
<script type="text/babel">
const {useEffect, Fragment, useState} = React;
const GridColumn = (props) => (
<div
style={{ borderRight: "1px solid #ababab", width: "20px", height: "200px" }}
{...props}
></div>
);
class Grid extends React.Component {
constructor(props) {
super(props);
this.state = {
columns: [1, 2, 3, 4, 5, 6, 7, 8, 9],
rendered: false
};
}
componentDidUpdate() {
// you will see your refs set in console on each render
// console.debug(this.columnRefs);
}
componentDidMount() {
// You need to rerender to render something from refs
this.setState({ rendered: true });
}
render() {
const items = [0, 2, 5];
return (
<div
style={{
width: "500px",
height: "200px",
position: "relative"
}}
>
<div style={{ display: "flex" }}>
{this.state.columns.map((item, index) => (
<GridColumn key={item}>
{items.includes(index) && (
<div
style={{
backgroundColor: "blue",
opacity: 0.5,
width: "10px",
height: "20px"
}}
></div>
)}
</GridColumn>
))}
</div>
</div>
);
}
}
ReactDOM.render(<Grid />, document.getElementById("root"));
</script>
</body>
</html>
Multicolumn example:
getWidth function was added for calculate fill (width):
getWidth = (arr, index) => {
return arr.includes(index + 1) ? 20 : 10;
};
using this:
width: this.getWidth(items, index) + "px",
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
</head>
<body>
<div id="root"></div>
<script src="https://unpkg.com/react#17/umd/react.development.js" crossorigin ></script>
<script src="https://unpkg.com/react-dom#17/umd/react-dom.development.js" crossorigin ></script>
<script src="https://unpkg.com/babel-standalone#6/babel.min.js"></script>
<script type="text/babel">
const {useEffect, Fragment, useState} = React;
const GridColumn = (props) => (
<div
style={{ borderRight: "1px solid #ababab", width: "20px", height: "200px" }}
{...props}
></div>
);
class Grid extends React.Component {
constructor(props) {
super(props);
this.state = {
columns: [1, 2, 3, 4, 5, 6, 7, 8, 9],
rendered: false
};
}
componentDidUpdate() {
// you will see your refs set in console on each render
// console.debug(this.columnRefs);
}
componentDidMount() {
// You need to rerender to render something from refs
this.setState({ rendered: true });
}
getWidth = (arr, index) => {
return arr.includes(index + 1) ? 20 : 10;
};
render() {
const items = [0,1, 2, 5];
return (
<div
style={{
width: "500px",
height: "200px",
position: "relative"
}}
>
<div style={{ display: "flex" }}>
{this.state.columns.map((item, index) => (
<GridColumn key={item}>
{items.includes(index) && (
<div
style={{
backgroundColor: "blue",
opacity: 0.5,
width: this.getWidth(items, index) + "px",
height: "20px"
}}
></div>
)}
</GridColumn>
))}
</div>
</div>
);
}
}
ReactDOM.render(<Grid />, document.getElementById("root"));
</script>
</body>
</html>
for achieve this effect (no vertical lines) use style:
.afterFull::after {
content: "";
display: block;
position: relative;
left: 1px;
height: 20px;
border-right: 1px solid;
border-color: var(--color);
}
in code:
<div
className="afterFull"
style={{
backgroundColor: "blue",
opacity: 0.5,
width: this.getWidth(items, index) + 0 + "px",
height: "20px",
"--color": "rgba(0, 0, 255, 0.5)"
}}
/>

React-images carousel

I am trying to achieve an image/video carousel using https://jossmac.github.io/react-images/
and it should be like this including modal :
I following the code snippet given there but it's not working and I don't see any step by step guide to making that carousel.
class Gall extends Component {
state = { modalIsOpen: false }
toggleModal = () => {
this.setState(state => ({ modalIsOpen: !state.modalIsOpen }));
}
render() {
const { modalIsOpen } = this.state;
return (
<ModalGateway>
{modalIsOpen ? (
<Modal onClose={this.toggleModal}>
<Carousel views={images} />
</Modal>
) : null}
</ModalGateway>
);
}
}
export default Gall;
can anyone please help with a codesandbox?
Also is it possible to trigger the modal with the current active image?
Thanks in advance.
There is a link in their docs to the source
// #flow
// #jsx glam
import glam from 'glam';
import React, { Component, Fragment } from 'react';
import { type ProviderProps } from '../../ImageProvider';
import Carousel, { Modal, ModalGateway } from '../../../src/components';
import { FooterCaption } from '../components';
import { getAltText } from '../formatters';
type State = {
selectedIndex?: number,
lightboxIsOpen: boolean,
};
export default class Home extends Component<ProviderProps, State> {
state = {
selectedIndex: 0,
lightboxIsOpen: false,
};
toggleLightbox = (selectedIndex: number) => {
this.setState(state => ({
lightboxIsOpen: !state.lightboxIsOpen,
selectedIndex,
}));
};
render() {
const { images, isLoading } = this.props;
const { selectedIndex, lightboxIsOpen } = this.state;
return (
<Fragment>
{!isLoading ? (
<Gallery>
{images.map(({ author, caption, source }, j) => (
<Image onClick={() => this.toggleLightbox(j)} key={source.thumbnail}>
<img
alt={caption}
src={source.thumbnail}
css={{
cursor: 'pointer',
position: 'absolute',
maxWidth: '100%',
}}
/>
</Image>
))}
</Gallery>
) : null}
<ModalGateway>
{lightboxIsOpen && !isLoading ? (
<Modal onClose={this.toggleLightbox}>
<Carousel
components={{ FooterCaption }}
currentIndex={selectedIndex}
formatters={{ getAltText }}
frameProps={{ autoSize: 'height' }}
views={images}
/>
</Modal>
) : null}
</ModalGateway>
</Fragment>
);
}
}
const gutter = 2;
const Gallery = (props: any) => (
<div
css={{
overflow: 'hidden',
marginLeft: -gutter,
marginRight: -gutter,
}}
{...props}
/>
);
const Image = (props: any) => (
<div
css={{
backgroundColor: '#eee',
boxSizing: 'border-box',
float: 'left',
margin: gutter,
overflow: 'hidden',
paddingBottom: '16%',
position: 'relative',
width: `calc(25% - ${gutter * 2}px)`,
':hover': {
opacity: 0.9,
},
}}
{...props}
/>
);

How to pass a props from parent to child css properties?

I have a parent and child component there is a button in parent component on Clicking that button it should send a Props -"red" so the child should receive the red and change its background color as red. Below my code is there
jsfiddle.net/t1ru6oyz/3/
Child
import React from "react";
import ReactDOM from "react-dom";
const childContainer = {
border: "0px solid #000",
width: "300px",
height: "300px",
textAlign: "middle",
margin: "0 auto",
marginTop: "60px"
};
const Child = props => {
return (
<div
style={{
backgroundColor: props.color
}}
/>
);
};
export default Child;
Parent
import React, { Component } from "react";
import "./App.css";
import Child from "./child";
const parentContainer = {
border: "10px solid #000",
width: "500px",
height: "500px",
margin: "20px",
textAlign: "middle"
};
class App extends Component {
constructor() {
super();
this.state = {
currentColor: "red"
};
}
componentDidMount() {
this.setState({
currectColor: "green"
});
}
changeColor = () => {
this.setState({
currectColor: "blue"
});
};
render() {
console.log("in render", this.state);
return (
<div style={parentContainer}>
<button onClick={this.changeColor}>Blue</button>
<p>{this.state.name}</p>
<Child color={this.state.currentColor} />
</div>
);
}
}
export default App;
You have a few typos there in your parent component like currectColor, just fix those first. Then, you can add the backgroundColor property to your childContainer if there is prop by checking it.
const parentContainer = {
border: "10px solid #000",
width: "500px",
height: "500px",
margin: "20px",
textAlign: "middle"
};
class App extends React.Component {
constructor() {
super();
this.state = {
currentColor: "red"
};
}
componentDidMount() {
this.setState({
currentColor: "green"
});
}
changeColor = () => {
this.setState({
currentColor: "blue"
});
};
render() {
return (
<div style={parentContainer}>
<button onClick={this.changeColor}>Blue</button>
<p>{this.state.name}</p>
<Child color={this.state.currentColor} />
</div>
);
}
}
const Child = props => {
let childContainer = {
border: "0px solid #000",
width: "300px",
height: "300px",
textAlign: "middle",
margin: "0 auto",
marginTop: "60px"
};
if ( props.color ) {
childContainer = { ...childContainer, backgroundColor: props.color }
}
return (
<div
style={childContainer}
/>
);
};
ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>
I have created my own components, but you can apply this logic.
class Child extends React.Component {
constructor(props) {
super(props);
}
render() {
return ( <
h1 style = {
{
backgroundColor: this.props.color
}
} > hello < /h1>
)
}
}
class Parent extends React.Component {
constructor(props) {
super(props);
this.state = {
color: ''
}
}
handleClick = () => {
this.setState({
color: 'red'
})
}
render() {
return ( <
div >
<
Child color = {
this.state.color
}
/> <
button onClick = {
this.handleClick
} > change Child color < /button> < /
div >
)
}
}
ReactDOM.render( <
Parent / > ,
document.getElementById("root")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>
You need change in App component. Keep currentColor to blue and change it to red in changeColor function but you are doing it in reverse
Try with below code
const parentContainer = {
border: "10px solid #000",
width: "500px",
height: "500px",
margin: "20px",
textAlign: "middle"
};
class App extends Component {
constructor() {
super();
this.state = {
currentColor: "blue"
};
}
componentDidMount() {
this.setState({
currentColor: "green"
});
}
changeColor = () => {
this.setState({
currentColor: "red"
});
};
render() {
console.log("in render", this.state);
return (
<div style={parentContainer}>
<button onClick={this.changeColor}>Blue</button>
<p>{this.state.name}</p>
<Child color={this.state.currentColor} />
</div>
);
}
}
export default App;

get padding, margin and width of react-native elements

I am trying to make a component appear between other components. For that, I would like to create a generic wrapper able to calculate the size of its child component and create the correct animation.
So far, I succeeded (with a lot of trouble) to somehow make the component appear as expected when the size of the child element is hard coded. But It doesn't work as soon as a padding or margin is set...
See my test case here:
import React from "react";
import ReactDOM from "react-dom";
import { View, Text, TouchableOpacity } from "react-native-web";
import styled from "styled-components";
const Container = styled(View)`
flex-direction: row;
`;
//The button that will make the component appear
class Toggle extends React.PureComponent {
render() {
return (
<TouchableOpacity {...this.props}>
<Text>Press Me</Text>
</TouchableOpacity>
);
}
}
//This wrapper will carry the appearing animation
const Wrapper = styled(View)`
transition: all ${props => props.delay}s ease;
transform: scale(${props => (props.mount ? 1 : 0)});
width: ${props => (props.mount ? props.width : 0)}px;
`;
//This is the component in charge of the appearing effect
class Appearing extends React.PureComponent {
constructor(props) {
super(props);
this.state = {
mounted: false,
render: false,
width: 0
};
}
//This will start the animation
componentDidMount() {
this.setState({ mounted: true, render: true });
}
componentDidUpdate() {
//this will start the disappearing animation
if (this.state.mounted && !this.props.mount) {
this.setState({ mounted: false });
setTimeout(
() => this.setState({ render: false }),
this.props.delay * 1000
);
//this will restart the appearing animation
} else if (!this.state.mounted && this.props.mount) {
this.setState({ mounted: true, render: true });
}
//We read the expected this of the child component
this.setState({ width: this.getWidth ? this.getWidth() : 0 });
}
render() {
return (
<Wrapper {...this.props} width={this.state.width}>
{React.cloneElement(this.props.children, {
//We read the child size with the 'onLayout' method
onLayout: event =>
(this.getWidth = () => event.nativeEvent.layout.width)
})}
</Wrapper>
);
}
}
//Carry the test case
class App extends React.PureComponent {
state = { toggle: false };
render() {
return (
<View>
{/* with only the width set */}
<Container>
<Appearing delay={0.5} mount={this.state.toggle}>
<Text style={{ width: "9em" }}>Tadaaaaaaaa !</Text>
</Appearing>
<Toggle
onPress={() => this.setState({ toggle: !this.state.toggle })}
/>
</Container>
{/* with the width and padding set */}
<Container>
<Appearing delay={0.5} mount={this.state.toggle}>
<Text style={{ width: "9em", paddingLeft: "10em" }}>
Tadaaaaaaaa !
</Text>
</Appearing>
<Toggle
onPress={() => this.setState({ toggle: !this.state.toggle })}
/>
</Container>
</View>
);
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
Do you know the best way to achieve that?

Categories