Horizontal auto scroll to a React Component - reactjs

I have a React app in which I render a horizontal list draft picks. At any point in time, one draft pick is active, but at a certain point that pick is made, and then the next one becomes active. When the next one becomes active, I'd like for the list to auto-scroll horizontally so that the active pick is always in view.
I'm sure this has been asked before, but I don't really know how to cause an auto-scroll event to a React component.
Here's the code I have:
DraftOrder.js:
class DraftOrder extends React.Component {
render() {
return (
<React.Fragment>
<div className="d-flex flex-row auto-scroll-x">
{this.props.draftPicks.map(aDraftPick => {
return <ADraftPickInOrder key={v4()} draftPick={aDraftPick} team={this.props.team} />
})}
</div>
</React.Fragment>
);
}
}
ADraftPickInOrder.js:
class ADraftPickInOrder extends React.Component {
constructor(props) {
super(props);
this.state = {
active: this.props.draftPick.active
}
this.renderUserName = this.renderUserName.bind(this);
this.renderDraftedPlayer = this.renderDraftedPlayer.bind(this);
}
renderUserName() {
...
}
renderDraftedPlayer() {
...
}
render() {
return (
<div className="text-center px-3">
<div className={classnames("font-weight-bold no-wrap", { "text-success" : this.props.draftPick.team.id === this.props.team.id } )}>{this.props.draftPick.team.name}</div>
{this.renderUserName()}
{this.renderDraftedPlayer()}
<div><small>{formatDraftPickNumber(this.props.draftPick)}</small></div>
</div>
);
}
}
Any help would be much appreciated!

Use ScrollView and the scrollTo method of it:
import { ScrollView } from 'react-native';
...
setScrollViewRef = ref => {
this.scrollView = ref;
};
handlePress = ({nativeEvent}) => {
const { pageX, pageY } = nativeEvent;
// find out x and y somehow
this.scrollView.scrollTo({x, y, animated: true})
}
render() {
<ScrollView
horizontal
ref={this.setScrollViewRef}
showsHorizontalScrollIndicator={false}>
<DraftPick onPress={this.handlePress} />
<DraftPick onPress={this.handlePress} />
...
</ScrollView>
}

Related

Change width on click using React

I am trying to implement a side navigation bar changing the width to show and hide the side navbar. Anyhow, I cannot make the openNav function to work. If I set the initial state to 250 px it shows the navbar and the close function works (it's setting the width to 0). Why the Menu button is not working? What am I missing?
class SideNav extends Component {
constructor(props){
super(props);
this.state = {
navwidth: 0
};
this.openNav = this.openNav.bind(this)
this.closeNav = this.closeNav.bind(this)
}
openNav() {
this.setState({
navwidth: '250 px'
});
}
closeNav() {
this.setState({
navwidth: 0
});
}
render(){
return(
<>
<button onClick={this.openNav}>Menu</button>
<div id="mySidenav" className='sidenav' style={{width: `${this.state.navwidth}`}}>
<a className="closebtn" onClick={this.closeNav}>×</a>
Find data
Visualize Data
About
</div>
</>
)
}
}
class Header extends Component {
render(){
return(
<SideNav/>
)
}
}
export default Header;
Modified your code,
class SideNav extends Component {
constructor(props) {
super(props);
this.state = {
navwidth: 0
};
}
openNav = () => {
this.setState({
navwidth: '250px'
});
}
closeNav = () => {
this.setState({
navwidth: '0px'
});
}
render = () =>
<>
<button onClick={this.openNav}>Menu</button>
<div id="mySidenav" className='sidenav' style={{ width: this.state.navwidth }}>
<a className="closebtn" onClick={this.closeNav}>×</a>
Find data
Visualize Data
About
</div>
</>
}
250 px should be 250px. You also don't need to use a templated string here:
`${this.state.navwidth}`
The simpler:
this.state.navwidth
is valid.

React show/hide content

I'm trying to make a toggle content button with React. But I can only get it to open, not to close when I click on it again. Can someone please take a look and let me know what I need to change within the code to accomplish it?
Here's what I have so far:
class Test extends React.Component {
constructor(props) {
super(props)
this.state = {
activeLocation: 0,
}
}
changeActiveLocation = (activeLocation) => {
this.setState({
activeLocation: activeLocation,
});
}
render() {
const activeLocation = company.locations[this.state.activeLocation];
return (
{company.locations.map((location, index) => (
<div className="test-item">
<div className="test-item-container" onClick={() => {this.changeActiveLocation(index)}}>
<div className="test-item-header">
<h3>Text goes here!</h3>
<a><FontAwesomeIcon icon={(this.state.activeLocation === index) ? 'times' : 'chevron-right'} /></a>
</div>
</div>
</div>
))}
)
}
}
Thank you!
You're setting the active location to be the same location that you've clicked already so the this.state.activeLocation === index is always true. I would refactor the locations to their own component with an isOpen state value that gets updated when the location is clicked. So like the following:
// test class
class Test extends React.Component {
constructor(props) {
super(props)
this.state = {
activeLocation: 0,
}
}
changeActiveLocation = (activeLocation) => {
this.setState({
activeLocation: activeLocation,
});
}
render() {
const activeLocation = company.locations[this.state.activeLocation];
return (
{company.locations.map((location, index) => (
<LocationItem location={location} onClick={() => this.changeActiveLocation(index)} />
))}
)
}
}
// LocationItem
class LocationItem extends React.Component {
state = { isOpen: false };
handleClick = () => {
this.setState(prevState => { isOpen: !prevState.isOpen});
// call parent click to set new active location if that's still needed
if(this.props.onClick) this.props.onClick;
}
render() {
return <div className="test-item">
<div className="test-item-container" onClick={this.handleClick}>
<div className="test-item-header">
<h3>Text goes here!</h3>
<a><FontAwesomeIcon icon={(this.state.isOpen ? 'times' : 'chevron-right'} /></a>
</div>
</div>
</div>
}
}

Why does clicking expand button closes the side panel in react?

i have a side panel with items listed. when the list item content overflows expand button appears and clicking that expand btn would show the entire content of list item
For this i have created a expandable component. this will show arrow_down when list item content overflows and clicking arrow_down shows up arrow_up.
However with the below code, clicking button 1 just makes the sidpanel disappear instead of arrow_up appearing. could some one help me solve this. thanks.
export default class Expandable extends React.PureComponent{
constructor(props) {
super(props);
this.expandable_ref = React.createRef();
this.state = {
expanded: false,
overflow: false,
};
}
componentDidMount () {
if (this.expandable_ref.current.offsetHeight <
this.expandable_ref.current.scrollHeight) {
this.setState({overflow: true});
}
}
on_expand = () => {
this.setState({expanded: true});
console.log("in expnad");
};
on_collapse = () => {
this.setState({expanded: false});
};
render () {
return (
<div className={(this.state.overflow ?
this.props.container_classname : '')}>
<div className={(this.state.overflow ?
this.props.classname : '')} style={{overflow: 'hidden',
display: 'flex', height: (this.state.expanded ? null :
this.props.base_height)}}
ref={this.expandable_ref}>
{this.props.children}
</div>
{this.state.overflow && this.state.expanded &&
<div className={this.props.expand}>
<button onClick={this.on_collapse}>
{this.props.arrow_up}</button>
</div>}
{this.state.overflow && !this.state.expanded &&
<div className={this.props.expand}>
<button onClick={this.on_expand}>
{this.props.arrow_down}</button>
</div>}
</div>
);
}
}
In the above code i pass the base_height to be 42px.
Edit:
i have realised for the side panel component i add eventlistener click to close the side panel if user clicks anywhere outside sidepanel. When i remove that eventlistener it works fine....
class sidepanel extends React.PureComponent {
constructor(props) {
super(props);
this.sidepanel_ref = React.createRef();
}
handle_click = (event) => {
if (this.sidepanel_ref.current.contains(event.target)) {
return;
} else {
this.props.on_close();
}
};
componentDidMount() {
document.addEventListener('click', this.handle_click, false);
}
componentWillUnmount() {
document.removeEventListener('click', this.handle_click, false);
}
render() {
return (
<div>
<div className="sidepanel" ref=
{this.sidepanel_ref}>
{this.props.children}
</div>
</div>
);
}
}
when i log the event.target and sidepanel_ref.current i see the button element in both of them but svg seems different in both of them.
How can i fix this?
Probably it is because click events bubble up the component tree as they do in the DOM too. If you have an element with an onClick handler inside an element with another onClick handler it will trigger both. Use event.stopPropagation() in the handler of the inner element to stop the event from bubbling up:
export default class Expandable extends React.PureComponent{
constructor(props) {
super(props);
this.expandable_ref = React.createRef();
this.state = {
expanded: false,
overflow: false,
};
}
componentDidMount () {
if (this.expandable_ref.current.offsetHeight <
this.expandable_ref.current.scrollHeight) {
this.setState({overflow: true});
}
}
toggleCollapse = event => {
// use preventDefault here to stop the event from bubbling up
event.stopPropagation();
this.setState(({expanded}) => ({expanded: !expanded}));
};
render () {
const {className, container_classname, base_height, expand, arrow_up, arrow_down} = this.props;
const {overflow, expanded} = this.state;
return (
<div className={overflow ? container_classname : ''}>
<div
className={overflow ? classname : ''}
style={{
overflow: 'hidden',
display: 'flex',
height: expanded ? null : base_height
}}
ref={this.expandable_ref}
>
{this.props.children}
</div>
{overflow && (
<div className={expand}>
<button onClick={this.toggleCollapse}>
{expanded ? arrow_up : arrow_down}
</button>
</div>
)}
</div>
);
}
}

Bootstrap dropdowns in ReactJS, only one open at a time

I have a page contains multiple Bootstrap Cards and each card is a component and each card footer is also a component. Card Footer contains buttons. When you click on a button, drop down will be opened like below
At any point of time when I click on a button, other drop downs should be in closed state. But its happening like this...
Requirement: One more thing is when I click on the same button, the respective drop down should be closed.
Requirement: When I click on any item inside drop down the respective drop down should be closed
My Architecture is like below
HOME PAGE COMPONENT CODE -START
class HomePage extends React.Component {
constructor(props) {
super(props);
this.state = {
items: [],
activatedIdStoredInParent: ""
};
}
toggleCountersMenu = (name) => {
var name1 = name;
this.setState((prevState) => {
return {
activatedIdStoredInParent: name1
}
});
}
render() {
const products = this.state.items.map((item, index) => {
return <div>
<Card
product={item}
activatedIdStoredInParent={this.state.activatedIdStoredInParent}
toggleCountersMenu={this.toggleCountersMenu}
>
</Card>;
</div>
});
return (
<div>
<div className="card-columns">
{products}
</div>
</div >
);
}
}
export default HomePage;
HOME PAGE COMPONENT CODE - END
CARD COMPONENT CODE - START
class Card extends React.Component {
handleActionClick = (name) => {
this.props.toggleCountersMenu(name);
}
render() {
return (
<div key={this.props.product.name}>
<CardHeader product={this.props.product} />
<CardBody product={this.props.product} />
<CardFooter
product={this.props.product}
onActionItemClick={this.handleActionClick}
activatedIdStoredInParent={this.props.activatedIdStoredInParent}
/>
</div>
);
}
}
export default Card;
CARD FOOTER COMPONENT CODE - START
class CardFooter extends React.Component {
handleActionItemClick = (name) => {
this.props.onActionItemClick(name);
}
render() {
console.log('Card Footer Drop Down comp rendered');
return (
<div className=" card-footer text-center">
<ButtonDropdown text="F" className="danger"
product={this.props.product}
onActionItemClick={this.handleActionItemClick}
activatedIdStoredInParent={this.props.activatedIdStoredInParent}
></ButtonDropdown>
</div>
);
}
}
export default CardFooter;
ButtonDropdown COMPONENT CODE - START
class ButtonDropdown extends React.Component {
constructor(props) {
super(props);
this.state = {
open: false,
show: ' none',
localActivatedId: 'none'
}
}
toggleOpen = (e) => {
var name = e.target.name;
this.setState((prevState, props) => {
var item = {
localActivatedId: name
}
if (props.activatedIdStoredInParent === name) {
if (prevState.show === ' show') {
item.show = ' none';
}
else {
item.show = ' show';
}
}
return item;
});
this.props.onActionItemClick(name);
}
numberClick = (e) => {
var qty = e.target.innerText;
this.setState((prevState, props) => {
var item = {
show: ' none'
}
return item;
});
}
render() {
return (
<div className="btn-group" >
<button type="button" className={`btn btn-${this.props.className} mr-1`} name={this.props.product.name + '$$' + this.props.text} onClick={this.toggleOpen}>
{this.props.text} (classAdded={this.state.show})
</button>
<div className={`dropdown-menu ${this.state.show}`}>
<span className="dropdown-item cursor-pointer " onClick={this.numberClick}>
-1
</span>
<span className="dropdown-item cursor-pointer" onClick={this.numberClick}>
-2
</span>
</div>
</div>
);
}
}
export default ButtonDropdown;
When I add multiple buttonDropdown components in Card Footer the end product is like this. How can I close other dropdowns.
I would like to know is my architecture is correct.. I am not using Redux/Flux etc..
You can use the componentDidUpdate lifecycle, in order to update your state's property that is opening the dropdown.
I don't know if it's the open or show property that displays the content of the dropdown but here's my logic.
class ButtonDropdown extends React.Component {
constructor(props) {
super(props);
this.state = {
//
};
}
componentDidUpdate(prevProps) {
const name = this.props.product.name + '$$' + this.props.text;
if (prevProps.activatedIdStoredInParent !== this.props.activatedIdStoredInParent && this.props.activatedIdStoredInParent !== name) {
this.closeDropDown();
}
}
closeDropDown = () => this.setState({ isOpen: false });
toggleOpen = (e) => {
//
}
numberClick = (e) => {
//
}
render() {
//
}
}
export default ButtonDropdown;

How to use react-virtualized with dynamic list height

I am trying to implement react-virtualized into my project. I have a side panel with a list of subdivisions. I would like to have an accordion-like functionality when the user selects an item. When the page first loads it looks like it is working correctly.
However when I start to scroll down the list looks like this
and here is code
const mapStateToProps = state => ({
windowHeight: state.dimensions.windowHeight,
sbds: new Immutable.List(state.sbds.data),
sbd: state.sbd.data
})
#connect(mapStateToProps)
export default class SBD extends Component {
static propTypes = {
windowHeight: PropTypes.number,
sbds: PropTypes.instanceOf(Immutable.List),
sbd: PropTypes.object
}
constructor(props) {
super(props)
this.state = {
listHeight: props.windowHeight - 250,
listRowHeight: 60,
overscanRowCount: 10,
rowCount: props.sbds.size,
scrollToIndex: undefined,
collapse: true
}
}
componentWillUnmount() {
}
shouldComponentUpdate(nextProps, nextState) {
const {sbds} = this.props
if (shallowCompare(this, nextProps, nextState))
return true
else return stringify(sbds) !== stringify(nextProps.sbds)
}
_handleSelectRow = selected => {
sbdRequest(selected)
const obj = {[selected.id]: true}
this.setState(obj)
}
render() {
const {
listHeight,
listRowHeight,
overscanRowCount,
rowCount,
scrollToIndex
} = this.state
return (
<div>
<SearchGroup />
<Card className='border-0 mt-10 mb-0'>
<CardBlock className='p-0'>
<AutoSizer disableHeight>
{({width}) => (
<List
ref='List'
className=''
height={listHeight}
overscanRowCount={overscanRowCount}
rowCount={rowCount}
rowHeight={listRowHeight}
rowRenderer={this._rowRenderer}
scrollToIndex={scrollToIndex}
width={width}
/>
)}
</AutoSizer>
</CardBlock>
</Card>
</div>
)
}
_getDatum = index => {
const {sbds} = this.props
return sbds.get(index % sbds.size)
}
_rowRenderer = ({index}) => {
const {sbd} = this.props
const datum = this._getDatum(index)
return (
<span key={datum.id}>
<Button
type='button'
color='link'
block
onClick={() => this._handleSelectRow(datum)}>
{datum.name}
</Button>
<Collapse isOpen={this.state[datum.id]}>
<Card>
<CardBlock>
FOO BAR
</CardBlock>
</Card>
</Collapse>
</span>
)
}
}
You're not setting the style parameter (passed to your rowRenderer). This is the thing that absolutely positions rows. Without it, they'll all stack up in the top/left as you scroll.
https://bvaughn.github.io/forward-js-2017/#/32/1

Resources