Forward Parent onWheel to Scroll Table - reactjs

I have an outer parent <div /> container that contains a <Table /> element. I'd like to be able to scroll the <Table /> on the onWheel event of the parent <div /> element, even if the mouse is not currently over the <Table />.
I have a ref to the table element and an event handler listening for the onWheel event but I haven't been able to figure out how to forward that event down to the table.

Because I guess that you want to scroll the table body, you can try with this.
class Table extends React.Component {
constructor() {
super();
this.callback = this.callback.bind(this);
this.body = null;
}
callback(ev) {
this.body.scrollTop += ev.deltaY;
}
render() {
return <div onWheel={this.callback}>
<table>
<tbody ref={c => this.body = c}>
{[1, 2, 3, 4, 5, 6,].map(i => <tr>
<td>{i}</td>
</tr>)}
</tbody>
</table>
</div>;
}
}
ReactDOM.render(<Table />, document.getElementById('root'));
tbody {
display: block;
height: 3rem;
overflow: scroll;
}
tr:nth-child(2n+1) {
background-color: #ddf;
}
tr:nth-child(2n) {
background-color: #eef;
}
table {
margin: auto;
border-collapse: collapse;
}
td {
width: 5rem;
text-align: center;
font-family: sans-serif;
color: #00f;
}
div {
width: 100%;
display: block;
text-align: center;
background-color: #99f;
}
<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 made a codePen illustrating a scroll redirection
This will listen wheelEvent on a parent <div>(the one with a red background), disable the default scrolling behavior (evt.preventDefault()) then set the scrollTop position of another <div>.
Here's the component code :
class RedirectScroll extends React.Component {
parentOnScroll = (evt) => {
evt.preventDefault();
const scrollTo= (evt.deltaY) + this.box.scrollTop;
this.box.scrollTop = scrollTo;
}
render() {
return (
<div className="parent" onWheel={this.parentOnScroll}> // Listen scrolls on this div
<div className="scrollablebox" ref={(box) => this.box = box}>
// Some content
</div>
</div>
);
}
}
I hope this is what you're looking for.

Related

Make overlay in react load only when input is clicked

I used the code by #WitVault to display an overlay when my button 'add data' is clicked; It works fine but the overlay loads whenever the page is loaded. I have to close the overlay to see my main content.
I want the overlay to appear only when the button is clicked and not when the page loads. Here's my code:
class registration extends Component{
constructor(props) {
super(props);
this.state = {
style : {
width : "100%"
}
};
this.openNav = this.openNav.bind(this);
this.closeNav = this.closeNav.bind(this);
}
componentDidMount() {
document.addEventListener("click", this.closeNav);
}
componentWillUnmount() {
document.removeEventListener("click", this.closeNav);
}
openNav() {
const style = { width : "100%" };
this.setState({ style });
document.body.style.backgroundColor = "rgba(0,0,0,0.4)";
document.addEventListener("click", this.closeNav);
}
closeNav() {
document.removeEventListener("click", this.closeNav);
const style = { width : 0 };
this.setState({ style });
document.body.style.backgroundColor = "#F3F3F3";
}
render(){
return(
<div class="encloser" id="test1">
<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>
<div class="addbuttondiv">
<button class="addbutton" onClick={this.openNav}>Add Data</button>
<div ref="snav" className = "overlay" style={this.state.style}>
<div className = "sidenav-container">
<div className = "text-center">
<h2>Form</h2>
<p>This is a sample input form</p>
</div>
<a href = "javascript:void(0)"
className = "closebtn"
onClick = {this.closeNav}
>
×
</a>
</div>
</div>
</div>
</div>
//some html content
</div>
);
}
}
export default registration;
CSS:
/* The Overlay (background) */
.overlay {
/* Height & width depends on how you want to reveal the overlay (see JS below) */
height: 100%;
width: 0;
position: fixed; /* Stay in place */
z-index: 1; /* Sit on top */
left: 0;
top: 0;
background-color: rgb(0,0,0); /* Black fallback color */
background-color: rgba(0,0,0, 0.9); /* Black w/opacity */
overflow-x: hidden; /* Disable horizontal scroll */
transition: 0.5s; /* 0.5 second transition effect to slide in or slide down the overlay (height or width, depending on reveal) */
}
.overlay-content {
position: relative;
top: 25%; /* 25% from the top */
width: 100%; /* 100% width */
text-align: center; /* Centered text/links */
margin-top: 30px; /* 30px top margin to avoid conflict with the close button on smaller screens */
}
.overlay .closebtn {
position: absolute;
top: 20px;
right: 45px;
font-size: 60px;
}
/* When the height of the screen is less than 450 pixels,
change the font-size of the links and position the close button again, so they don't overlap */
#media screen and (max-height: 450px) {
.overlay a {font-size: 20px}
.overlay .closebtn {
font-size: 40px;
top: 15px;
right: 35px;
}
}
You have to set width as "0" in constructor like below:
class registration extends Component{
constructor(props) {
super(props);
this.state = {
style : {
width : "0"
}
};
this.openNav = this.openNav.bind(this);
this.closeNav = this.closeNav.bind(this);
}
componentDidMount() {
document.addEventListener("click", this.closeNav);
}
componentWillUnmount() {
document.removeEventListener("click", this.closeNav);
}
openNav() {
const style = { width : "100%" };
this.setState({ style });
document.body.style.backgroundColor = "rgba(0,0,0,0.4)";
document.addEventListener("click", this.closeNav);
}
closeNav() {
document.removeEventListener("click", this.closeNav);
const style = { width : 0 };
this.setState({ style });
document.body.style.backgroundColor = "#F3F3F3";
}
render(){
return(
<div class="encloser" id="test1">
<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>
<div class="addbuttondiv">
<button class="addbutton" onClick={this.openNav}>Add Data</button>
<div ref="snav" className = "overlay" style={this.state.style}>
<div className = "sidenav-container">
<div className = "text-center">
<h2>Form</h2>
<p>This is a sample input form</p>
</div>
<a href = "javascript:void(0)"
className = "closebtn"
onClick = {this.closeNav}
>
×
</a>
</div>
</div>
</div>
</div>
//some html content
</div>
);
}
}
export default registration;

list jsx not re-rendered on state change

The below code snipped includes a clear explanation of the issues.
The explanation will appear if you run the code snipped by clicking the button
The code triggering the problem is
[0,1].map((i) => <Star src={this.state.src[i]} changeIcon={this.changeIcon} key={i} id={i} />)
{this.state.src[i]} does not trigger the re-rendering of the jsx when the state is updated
const host = 'https://s3.eu-central-1.amazonaws.com/moviedatabase1/'
const noStarIcon = host + 'no_star.png'
const StarIcon = host + 'star.png'
class Feedback extends React.Component {
constructor(props){
super(props);
this.state = { src: [noStarIcon, noStarIcon, noStarIcon], text: "" }
this.changeIcon = this.changeIcon.bind(this)
this.list = [0,1].map((i) => <Star src={this.state.src[i]} changeIcon={this.changeIcon} key={i} id={i} />)
}
changeIcon(){
this.setState({text: this.state.text + " state updated!"})
this.setState({src: [StarIcon, StarIcon, StarIcon]})
}
render() {
return (
<React.Fragment>
<p id="console">{this.state.text}</p>
<div class="box">{this.list}</div>
<div class="box box-blue flex-box">
<p>If I use the normal <b>jsx</b> tag, <b>setState</b> will trigger re-rendering of the <b>jsx</b> element</p> and the star will become yellow!
</div>
<div class="box">
<Star changeIcon={this.changeIcon} src={this.state.src[2]} key={2} id={2}/>
</div>
</React.Fragment>
);
}
}
class Star extends React.Component {
render() {
return <img src={this.props.src} onClick={this.props.changeIcon} style={this.props.style} id={this.props.id} />;
}
}
ReactDOM.render(
<Feedback />,
document.getElementById("react")
);
.box {
margin: 2vh 2vw;
color: white;
}
.box-blue {
border: solid 2px red;
background-color: blue;
border: solid 1px red;
margin: 2vh 2vw;
padding: 2vh 2vw;
color: white;
}
.flex-box {
display: flex;
flex-direction: column;
justify-content: space-around;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div class="box box-blue flex-box">
<p>The two stars below are multiple <b>Components</b> rendered using the Javascript <b>.map</b> function, which saves an array of two <b>Star</b> components.</p>
<p>If you click on this two stars, the function <b>changeIcon</b> is called and updates the <b>state</b>, but the <b>jsx</b> is not re-rendered</p>
</div>
<div id="react"></div>
<hr>
<p>Credits for the icons</p>
<div>Icons made by Smashicons from www.flaticon.com is licensed by CC 3.0 BY</div>
It is because you are setting up your this.list in your constructor. Calling this.setState will not call your constructor again so it won't update it. It only triggers lifecycle events (such as render). See below.
const host = 'https://s3.eu-central-1.amazonaws.com/moviedatabase1/'
const noStarIcon = host + 'no_star.png'
const StarIcon = host + 'star.png'
class Feedback extends React.Component {
constructor(props){
super(props);
this.state = { src: [noStarIcon, noStarIcon, noStarIcon], text: "" }
this.changeIcon = this.changeIcon.bind(this)
}
changeIcon(){
this.setState({text: this.state.text + " state updated!"})
this.setState({src: [StarIcon, StarIcon, StarIcon]})
}
render() {
return (
<React.Fragment>
<p id="console">{this.state.text}</p>
<div class="box">{[0,1].map((i) => <Star src={this.state.src[i]} changeIcon={this.changeIcon} key={i} id={i} />) }</div>
<div class="box box-blue flex-box">
<p>If I use the normal <b>jsx</b> tag, <b>setState</b> will trigger re-rendering of the <b>jsx</b> element</p> and the star will become yellow!
</div>
<div class="box">
<Star changeIcon={this.changeIcon} src={this.state.src[2]} key={2} id={2}/>
</div>
</React.Fragment>
);
}
}
class Star extends React.Component {
render() {
return <img src={this.props.src} onClick={this.props.changeIcon} style={this.props.style} id={this.props.id} />;
}
}
ReactDOM.render(
<Feedback />,
document.getElementById("react")
);
.box {
margin: 2vh 2vw;
color: white;
}
.box-blue {
border: solid 2px red;
background-color: blue;
border: solid 1px red;
margin: 2vh 2vw;
padding: 2vh 2vw;
color: white;
}
.flex-box {
display: flex;
flex-direction: column;
justify-content: space-around;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div class="box box-blue flex-box">
<p>The two stars below are multiple <b>Components</b> rendered using the Javascript <b>.map</b> function, which saves an array of two <b>Star</b> components.</p>
<p>If you click on this two stars, the function <b>changeIcon</b> is called and updates the <b>state</b>, but the <b>jsx</b> is not re-rendered</p>
</div>
<div id="react"></div>
<hr>
<p>Credits for the icons</p>
<div>Icons made by Smashicons from www.flaticon.com is licensed by CC 3.0 BY</div>

How to open a particular accordion based click event in reactjs and autoclose remaining accordion?

I want to open a particular Section on clicking on that. How to do that by using click event? And also auto close the remaining accordion if I click on another accordion. Here is my code.
Accordion Component
import React, { Component } from 'react';
import Section from './section';
class Accordion extends Component {
constructor(props) {
super(props);
this.handleClick = this.handleClick.bind(this);
this.state = {
open: false,
headingClassName: 'accordion-heading',
className: 'accordion-content accordion-close',
Label: 'label-close',
icon: "+",
selectedItem: null,
};
}
handleClick = () => {
const open = this.state.open;
if (open) {
this.setState({
open: false,
className: "accordion-content accordion-close",
headingClassName: "accordion-heading",
Label: 'label-close',
icon: "+",
})
} else {
this.setState({
open: true,
className: "accordion-content accordion-open",
headingClassName: "accordion-heading clicked",
Label: 'label-open',
icon: "-",
})
}
}
render() {
return (
<div className="accordion-container">
<h1>Accordian Component</h1>
How to pass id as parameter in each section in onClick event to open
particular accordion and to autoclose remaining.
<Section>
<div className={this.state.headingClassName} onClick={this.handleClick} id="1">
<h3>One</h3> <label className={this.state.Label}>{this.state.icon}</label>
</div>
<div className={this.state.className}>
<p>This is paragraph</p>
</div>
</Section>
<Section>
<div className={this.state.headingClassName} onClick={this.handleClick} id="2">
<h3>Two</h3> <label className={this.state.Label}>{this.state.icon}</label>
</div>
<div className={this.state.className}>
<p>This is paragraph</p>
</div>
</Section>
<Section>
<div className={this.state.headingClassName} onClick={this.handleClick} id="3">
<h3>Three</h3> <label className={this.state.Label}>{this.state.icon}</label>
</div>
<div className={this.state.className}>
<p>This is paragraph</p>
</div>
</Section>
</div>
);
}
}
export default Accordion;
Section Component
import React, { Component } from 'react';
class Section extends Component {
render() {
return (
<div className="parent-accordion">
{this.props.children}
</div>
);
}
}
export default Section;
CSS
body {
margin: 0;
padding: 0;
font-family: sans-serif;
}
.accordion-container {
margin: auto;
width: 700px;
}
.accordion-container h1 {
color: #0000007a;
text-align: center;
font-family: sans-serif;
}
.parent-accordion {
width: 100%;
border: 1.5px solid #00000017;
}
.accordion-heading {
padding: 5px 5px;
background-color: #e2e2e254;
cursor: pointer;
text-transform: capitalize;
/* font-size: 17px; */
/* font-weight: 600; */
color: #2b2b41;
/* font-family: sans-serif; */
transition: background-color 1s;
}
.accordion-heading:hover {
background-color: #000000c7;
color: white;
First of all i've encompased a minimal (no css) example of how an accordion would behave on codesandbox, here.
This can be done multiple ways. In the example above the body of the tab is hidden with display:none if the tab is not active.Basically you iterate over your data in the render function and that's where you set your classes based on whatever flags you want (in your case isActive). You could render a Section for each tab and pass props to it.
The click handler updates your state with the id of the active tab.

react Semantic-UI - multi-select checkbox drop-down

i want to built an multi select checkbox dropdown in react with es6
my requirement is as below specified in image
I tried doing this click here but it is not working.
You can use one parent component that will keep values in its state and toggle list items. Then you can create component for each list item that will keep active property in state that you can toggle on click.
class ListItem extends React.Component {
constructor(props) {
super(props);
this.state = {active: false}
}
render() {
return (
<a
onClick={() => {
this.setState(prevState => {
let newState = !prevState.active;
this.props.handleClick(newState, this.props.value);
return {active: newState}
})
}}
className={!this.state.active ? '' : 'selected'}
href="#">
{this.props.value}</a>
)
}
}
class Select extends React.Component {
constructor(props) {
super(props);
this.state = {
showList: false,
value: []
}
this.handleItemClick = this.handleItemClick.bind(this)
}
componentDidMount() {
document.addEventListener('mousedown', (e) => {
if(!this.node.contains(e.target)) {
this.setState({showList: false})
}
})
}
componentWillUnmount() {
document.removeEventListener('mousedown');
}
renderValue() {
let {value} = this.state;
if(!value.length) return "Select..."
else return value.join(', ')
}
toggleList() {
this.setState(prevState => ({showList: !prevState.showList}))
}
handleItemClick(active, val) {
let {value} = this.state;
if(active) value = [...value, val]
else value = value.filter(e => e != val);
this.setState({value})
}
render() {
return (
<div
ref={node => this.node = node}
className="select">
<button onClick={this.toggleList.bind(this)}>
<span className="select_value">
{this.renderValue()}
</span>
</button>
<div
className={"select_list " + (!this.state.showList && 'hide')}>
<ListItem handleClick={this.handleItemClick} value="Lorem" />
<ListItem handleClick={this.handleItemClick} value="Ipsum" />
<ListItem handleClick={this.handleItemClick} value="Dolor" />
</div>
</div>
)
}
}
ReactDOM.render(
<Select />,
document.getElementById('container')
);
button {
background: white;
width: 100%;
padding: 10px 15px;
border: 1px solid rgba(0, 0, 0, .1);
border-radius: 5px;
cursor: pointer;
text-align: left;
}
.select_list {
width: 100%;
background: white;
border: 1px solid rgba(0, 0, 0, .1);
border-radius: 5px;
}
.select_list a {
padding: 10px 15px;
display: flex;
color: black;
text-decoration: none;
position: relative;
align-items: center;
}
.select_list a:before {
width: 15px;
height: 15px;
content: '';
border: 1px solid rgba(0, 0, 0, .1);
border-radius: 5px;
margin-right: 10px;
display: block;
}
.select_list a.selected:before {
background: #0493D1;
content: '✓';
color: white;
font-size: 11px;
text-align: center;
line-height: 15px;
}
.hide {
display: none;
}
<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="container"></div>
Semantic-UI React Approach
After much digging, I found an old conversation between eugenetumachov and Semantic-UI developers(?). One of the users provided incredibly helpful code that answers this question using Semantic-UI's Dropdown component.
This is done by making use of Dropdown's Dropdown.Menu and Dropdown.Item. Then looping through your options via map to create checkboxes. The only downside is that the workaround does not seem to allow scrolling and will require more CSS. Additionally, based on CSS the checkbox items' background color may turn transparent if you double-click on the dropdown, and the dropdown will collapse on mouse hover. You can bypass the transparency issue by using a class or style property for your Dropdown.Menu and Dropdown.Item.
Semantic-UI developer's response to this type of question appears to be a flat "no" or a
Active items are automatically removed from the Dropdown menu. So you cannot show a "checked" state for an item in the menu.
You could create a similar component out of an Input as a trigger for
a Popup containing a Menu or List of Checkboxes.
Are dropdowns with checkboxes possible? #2417
eugenetumachov's workaround

React sharable panel url

Consider my following snippet. Right now on button click it opens a div-three that loads AnotherComponent.The url is simply 'http://localhost:3000/de' i.e. Indexroot
What I want to achieve is: If I hit 'http://localhost:3000/de/?open' then I want the panel i.e. div-three already open.
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
showThird: false
}
this.showDivThree = this.showDivThree.bind(this)
/*if(props.location.search=="?open"){
this.showDivThree()
}*/
}
showDivThree() {
this.setState(prevState => ({ showSecond: false, showThird: !prevState.showThird}))
console.log(this.state)
}
render() {
return (
<div className={'wrapper' + ( this.state.showThird ? ' show' : '')}>
<div className="one">one
{/* Show third */}
<div>
<button onClick={this.showDivThree}>{this.state.showThird ? 'hideThird' : 'showThird'}</button>
</div>
</div>
<div className="three">three
<div>
<button onClick={this.showDivThree}>{this.state.showThird ? 'hideThird' : 'showThird'}</button>
<AnotherComponent />
</div>
</div>
</div>
)
}
}
class AnotherComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
}
}
render() {
return (
<div>
<h4>Another component</h4>
</div>
)
}
}
ReactDOM.render(
<App />,
document.getElementById('root')
);
.wrapper {
overflow: hidden;
white-space: nowrap;
}
.one, .two, .three {
background: #333;
border: 2px solid #787567;
box-sizing: border-box;
color: #fff;
display: inline-block;
font-family: arial;
overflow: hidden;
padding: 20px;
text-align: center;
transition: border 0.2s, padding 0.2s, width 0.2s;
min-height: 50vh;
}
.one {
width: 100%;
}
.two {
border-width: 2px 0;
padding: 20px 0;
width: 0;
}
.three {
border-width: 2px 0;
padding: 20px 0;
width: 0;
}
.show .one, .show .two, .show .three {
border-width: 2px;
padding: 20px;
width: 50%;
}
<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>
<script src="https://unpkg.com/react-router/umd/react-router.min.js"></script>
<div id="root"></div>
I have commented a code where I read search string from props.location, if it is present then I simply call the function that opens the div-three. But as I have mixed conditions to open divs it somehow is not working.
How can I fix this?
You can't call setState (showDivThree method calls setState) in contructor since when constructor is called component hasn't been mounted yet. Please check this SO answer for more details.
You should move if statement checking URL search string from constructor to componentDidMount method which is called immediately after a component is mounted and in which you can safely use setState:
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
showThird: false
};
this.showDivThree = this.showDivThree.bind(this)
}
componentDidMount() {
if (props.location.search == "?open") {
this.showDivThree();
}
}
...
}
Besides, I think that your URL should be without slash before search query. So it should be http://localhost:3000/de?open instead of http://localhost:3000/de/?open.

Resources