Toggle Button Group in React - reactjs

New to react and trying to use in a new electron learning project I have. I'm trying to build a really basic drawing app.
I created a ToolbarButton class. That would represent each button and a Toolbar that would manage the button group. Example If you pick a 'primary' tool then it should turn off all other primary tools and leave only your current selection.
In jQuery I'd just do something like
let button = $(this);
let toolbar = $(this).parent();
toolbar.find('button.toolbar-button').removeClass('active');
button.addClass('active');
How would I do the same in react? I can toggle what I want to with setState from within the ToggleButton but separating it out into a prop seems to be an answer, but then I need to have the Toolbar manage the button 'active' states and I'm not sure how to do that. Think the answer is in ReactDOM, super newbie to react so apologize if the answer is overly obvious.
import React from 'react';
import FontAwesome from 'react-fontawesome';
import { ButtonGroup } from 'react-bootstrap';
import { ChromePicker} from 'react-color';
class ToolbarButton extends React.Component
{
state =
{
active: false
}
handleClick = ()=> {
if(this.props.onClick)
{
this.props.onClick();
}
this.setState({ active: !this.state.active});
}
render(){
return <div className={`btn btn-primary${this.state.active ? ' active' : ''}`} onClick={this.handleClick}>{this.props.children}</div>
}
}
class ColorPickerButton extends React.Component
{
constructor(props)
{
super(props);
this.state = {
displayColorPicker: false,
color: { r: 255, g: 255, b: 255, a:1 }
}
}
state = {
flyout: 'bottom',
displayColorPicker: false,
color: { r: 255, g: 255, b: 255, a:1 }
}
/* This button should have an option to display how the colorpicker flys out */
static flyoutStyles =
{
normal: { },
top: {top: '0px' },
bottom: { top: '100%' },
left: { right: '100%' },
right: { left: '100%' }
}
handleClick = (e) => {
this.setState({ displayColorPicker: !this.state.displayColorPicker});
}
handleClose = () => {
this.setState({ displayColorPicker: false });
}
handleChange = (color) => {
this.setState({ color: color.rgb });
}
stopPropagation = (e) => {
e.stopPropagation();
}
render()
{
const swatchStyle = {
backgroundColor: `rgba(${this.state.color.r},
${this.state.color.g},
${this.state.color.b},
${this.state.color.a})`,
height: '16px',
width: '16px',
border: '1px solid white'
};
const popup = {
position: 'absolute',
zIndex: 2,
top: 'calc(100% + 2px)'
};
const cover = {
position: 'fixed',
top: '0px',
right: '0px',
left: '0px',
bottom: '0px',
};
return (
<ToolbarButton onClick={this.handleClick} active={this.state.displayColorPicker}>
<div style={swatchStyle} />
{
this.state.displayColorPicker ?
<div style={popup} onClick={this.stopPropagation}>
<div style={ cover } onClick={ this.handleClose }/>
<ChromePicker color={this.state.color} onChange={this.handleChange} />
</div>
: null
}
</ToolbarButton>
);
}
}
export class CanvasToolbar extends React.Component
{
handleClick = (e) => {
}
render(){
return (<div className="CanvasToolbar">
<ButtonGroup vertical>
<ToolbarButton>
<FontAwesome name='paint-brush' />
</ToolbarButton>
<ToolbarButton>
<FontAwesome name='eraser' />
</ToolbarButton>
<ToolbarButton>
<FontAwesome name='magic' />
</ToolbarButton>
<ColorPickerButton />
</ButtonGroup>
</div>);
}
}

Related

How to delay a component render in React?

I have an instagram widget thas uses iframe, but when I switch between routes, the widget loads too slow and does'nt have time to render properly.
Can You tell me, how to set delay rendering of the component, jr another solution to this problem?
Here is the component:
import React, { Component } from 'react';
const divStyle = [
{
border: 'none',
overflow: 'hidden',
width: '100%'
},
{
font: "10px/14px 'Roboto','Helvetica Neue',Arial,Helvetica,sans-serif",
fontWeight: '400',
width: '100%',
textAlign: 'right'
},
{
color: '#777',
textDecoration: 'none'
}
];
class Instagram extends Component {
render() {
return (
<div id="instagram">
<iframe src="https://snapwidget.com/embed/711808" className="snapwidget-widget" allowtransparency="true" frameborder="0" scrolling="no" style={divStyle[0]}></iframe>
</div>
);
}
}
export default Instagram;
Also the code is located in the CodeSandbox.
Thanks for any help!
This can be possible solution from your code sandbox.
NOTE: Please Replace your loader with loading div.
CodeSandbox: https://codesandbox.io/s/damp-platform-950yw
import React, { Component } from "react";
const divStyle = [
{
border: "none",
overflow: "hidden",
width: "100%"
},
{
font: "10px/14px 'Roboto','Helvetica Neue',Arial,Helvetica,sans-serif",
fontWeight: "400",
width: "100%",
textAlign: "right"
},
{
color: "#777",
textDecoration: "none"
}
];
class Instagram extends Component {
state = {
loading: true
}
handelOnLoad = () => {
this.setState({
loading: false
})
}
render() {
return (
<>
{this.state.loading && <div style={{
position: "fixed",
background: "rgba(0,0,0,0.7)",
top: 0,
bottom: 0,
right: 0,
left: 0,
color: "#fff"
}}>Loading</div>}
<div id="instagram">
<iframe
src="https://snapwidget.com/embed/711808"
className="snapwidget-widget"
allowtransparency="true"
frameborder="0"
scrolling="no"
style={divStyle[0]}
onLoad={this.handelOnLoad}
/>
</div>
</>
);
}
}
export default Instagram;
You can make use of state to render,
class Instagram extends Component {
state={
show: false
}
componentDidMount(){
setTimeout(()=>{
this.setState({show: true})
},5000) //runs after 5sec
}
render() {
return (
<div id="instagram">
{ this.state.show && <iframe src="https://snapwidget.com/embed/711808" className="snapwidget-widget" allowtransparency="true" frameborder="0" scrolling="no" style={divStyle[0]}></iframe> }
</div>
);
}
}

How do I transition from one image to another by changing state in React-Native

I have two images with little difference between them and each corresponding to a particular state. I need to smoothly transition from one to the other when I change the state so that the effect feels like only the part which is different in the two images has undergone animation,the rest of the image staying as it is.
I want it to work so that when I render the second image on stateChange, only the rod like part in the second image appears to fade in,the rest remaining still.
I think this can be achieved without using any animation libraries like react-transition-group probably by using some of the life cycle methods in React and obviously, the AnimatedAPI. The major issue that I am facing is that when I update the state I have no control over the previous image that was rendered. I somehow want the previously rendered image to stay until newly rendered Component appears and does its animation. Here is what I tried to do. I have this ImageLoader Component which renders an image while providing a fading-in animation to it.
class ImageLoader extends React.Component {
constructor(){
super()
this.opacity= new Animated.Value(0)
}
componentDidUpdate(){
{this.onLoad()}
}
onLoad = () => {
this.opacity.setValue(0);
Animated.timing(this.opacity, {
toValue: 1,
duration: 500,
useNativeDriver: true,
}).start();
}
render() {
return (
<Animated.Image onLoad={this.onLoad}{...this.props}style={[
{opacity: this.opacity,}, this.props.style,
]}
/>
);
}
}
export default class App extends React.Component {
state={
no:1,
}
render() {
let Dun=()=>{return this.state.no==1?
<ImageLoader source={require('./assets/img1.PNG')}/>: <ImageLoader
source={require('./assets/img2.PNG')}/>
}
const calc=()=>{
this.setState((state)=>({no:Math.abs(state.no-1)}));
}
return (
<View style={styles.container}>
<View style={{height:100,marginLeft:50}}>
{Dun()}
<Button onPress={()=>{calc()}}> Press</Button>
</View>
</View>
);
}
}
You could use 2 animated images to give the impression that one is fading into the other. Here is a solution based on your example:
import React from 'react';
import { Animated, StyleSheet, Text, TouchableOpacity, View } from 'react-native';
import images from 'src/images';
const styles = StyleSheet.create({
image: {
position: 'absolute',
top: 0,
right: 0,
bottom: 0,
left: 0
}
});
class ImageSwitcher extends React.Component {
fadeInOpacity = new Animated.Value(0);
fadeOutOpacity = new Animated.Value(1);
state = {
prevSource: null
};
componentDidMount() {
this.onLoad();
}
componentDidUpdate() {
this.onLoad();
}
componentWillReceiveProps({ source: newSource }) {
const { source } = this.props;
if (newSource !== source) {
this.setState({ prevSource: source });
}
}
onLoad = () => {
this.fadeInOpacity.setValue(0);
this.fadeOutOpacity.setValue(1);
Animated.timing(this.fadeInOpacity, {
toValue: 1,
duration: 500,
useNativeDriver: true
}).start();
Animated.timing(this.fadeOutOpacity, {
toValue: 0,
duration: 500,
useNativeDriver: true
}).start();
};
render() {
const { prevSource } = this.state;
return (
<View
style={{
width: 200,
height: 200
}}
>
<Animated.Image {...this.props} style={[styles.image, { opacity: this.fadeInOpacity }]} resizeMode="cover" />
{prevSource && (
<Animated.Image {...this.props} style={[styles.image, { opacity: this.fadeOutOpacity }]} resizeMode="cover" source={prevSource} />
)}
</View>
);
}
}
export default class App extends React.Component {
state = {
source: images.first
};
handleToggle = () => this.setState(({ source }) => ({ source: source === images.first ? images.second : images.first }));
render() {
const { source } = this.state;
return (
<View style={{ flex: 1 }}>
<ImageSwitcher source={source} />
<TouchableOpacity onPress={this.handleToggle}>
<Text>Toggle Image</Text>
</TouchableOpacity>
</View>
);
}
}
const SwitchImage = (props) => {
const [previousImage, setPreviousImage] = useState('');
const opacity = useRef(new Animated.Value(0)).current;
useEffect(()=>{
Animated.timing(opacity, {
toValue: 1,
duration: 300,
easing: Easing.spring,
useNativeDriver: true
}).start(()=>{
setPreviousImage(props.source);
opacity.setValue(0);
})
}, [props.source])
return(
<View style={{width: props.style.width, height: props.style.height}}>
<FastImage
resizeMode={props.resizeMode}
source={previousImage}
style={{width: props.style.width, height: props.style.height, position: 'absolute'}}
/>
<Animated.View style={{opacity: opacity}}>
<FastImage
resizeMode={props.resizeMode}
source={props.source}
style={{width: props.style.width, height: props.style.height, position: 'absolute'}}
/>
</Animated.View>
</View>
)}

React menu item hover

I am trying React MobX on hover over a list item. Here is my code
export default observer(
class SidebarContent extends React.Component {
constructor(props) {
super(props);
}
componentWillMount() {
this.props.store.fetchSources()
}
toggleHover(){
this.props.store.hover = !this.props.store.hover;
}
getArticles(src) {
this.props.store.fetchArticles(src);
}
render() {
const style1 = this.props.style ? {...styles.sidebar, ...this.props.style} : styles.sidebar;
const { sources, hover } = this.props.store
var linkStyle;
if (this.props.store.hover) {
linkStyle = {backgroundColor: 'black'}
} else {
linkStyle = {backgroundColor: 'white'}
}
const links = sources.map( (src,index) => (
<a key={index} href='javascript:;' style={styles.sidebarLink} onClick={this.getArticles.bind(this, src.id)} >
<span style={linkStyle} onMouseEnter={this.toggleHover.bind(this)} onMouseLeave={this.toggleHover.bind(this)} >
<img className="ui-avaatar image" src='{ src.urlsToLogos.small }' />
<span className="side-news-item"> {src.name} </span>
</span>
</a>
))
return (
<MaterialTitlePanel title="Menu" style={style1}>
<div style={styles.content}>
<div style={styles.divider} />
{links}
</div>
</MaterialTitlePanel>
);
}
}
);
const styles = {
sidebar: {
width: 256,
height: '100%',
},
sidebarLink: {
display: 'block',
padding: '16px 0px',
color: '#757575',
textDecoration: 'none',
},
divider: {
margin: '8px 0',
height: 1,
backgroundColor: '#757575',
},
content: {
padding: '16px',
height: '100%',
backgroundColor: 'white',
},
};
this.props.store.hover is a observable.
The problem is that when mouse over one item, all of the items get the hover effect. What did I do wrong?
Do not set Component's props directly, set it on upper Component.
or you could use state feature, and always use setState() to change state.
write an subcomponent to control the Button State
code below maybe help
class SidebarContent extends React.Component {
constructor(props) {
super(props);
}
componentWillMount() {
this.props.store.fetchSources()
}
getArticles(src) {
this.props.store.fetchArticles(src);
}
render() {
const links = sources.map( (src,index) => <Button />);
return (
<MaterialTitlePanel title="Menu" style={style1}>
<div style={styles.content}>
<div style={styles.divider} />
{links}
</div>
</MaterialTitlePanel>
);
}
}
class Button extends React.Component {
toggleHover(){
this.setState({
hover: !this.state.hover,
});
}
render() {
const style1 = this.props.style ? {...styles.sidebar, ...this.props.style} : styles.sidebar;
const { sources } = this.props.store
const { hover } = this.state;
var linkStyle;
if (hover) {
linkStyle = {backgroundColor: 'black'}
} else {
linkStyle = {backgroundColor: 'white'}
}
return (
<a key={index} href='javascript:;' style={styles.sidebarLink} onClick={this.getArticles.bind(this, src.id)} >
<span style={linkStyle} onMouseEnter={this.toggleHover.bind(this)} onMouseLeave={this.toggleHover.bind(this)} >
<img className="ui-avaatar image" src='{ src.urlsToLogos.small }' />
<span className="side-news-item"> {src.name} </span>
</span>
</a>
);
}
}
const styles = {
sidebar: {
width: 256,
height: '100%',
},
sidebarLink: {
display: 'block',
padding: '16px 0px',
color: '#757575',
textDecoration: 'none',
},
divider: {
margin: '8px 0',
height: 1,
backgroundColor: '#757575',
},
content: {
padding: '16px',
height: '100%',
backgroundColor: 'white',
},
};
<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>

How do I add an image to the DOM after another image has loaded?

I want to make sure images are loaded in the right order: first the primary image, then the secondary image. My plan is to inject the secondaryImage once the primary image is done.
class HoverImage extends Component {
constructor (props) {
super(props)
this.state = { secondaryImage: null }
}
primaryImageLoaded () {
//here I would like inject <img className='img-responsive' src={stripUrl(secondaryImage)} /> before primaryImage
}
render () {
const primaryImage = this.props.primaryImage
const secondaryImage = this.props.secondaryImage
if (secondaryImage) {
return (
<div style={{position: 'relative'}}>
<img
className='img-responsive'
src={stripUrl(primaryImage)}
onLoad={this.primaryImageLoaded.bind(this)}
style={{
':hover': {
opacity: 0
},
position: 'absolute',
top: 0}}
/>
</div>
)
}
}
other solutions that create the same effect are fine too!
Try this:
class HoverImage extends Component {
constructor (props) {
super(props)
this.state = {
secondaryImage: null,
showSecondaryImage: false,
}
}
primaryImageLoaded () {
this.setState({showSecondaryImage: true});
}
render () {
const primaryImage = this.props.primaryImage;
const secondaryImage = this.props.secondaryImage;
secondaryImage ?
return (
<div style={{position: 'relative'}}>
{this.state.showSecondaryImage ?
<img className='img-responsive' src={stripUrl(secondaryImage)} />
: <div/>}
<img
className='img-responsive'
src={stripUrl(primaryImage)}
onLoad={this.primaryImageLoaded.bind(this)}
style={{
':hover': {
opacity: 0
},
position: 'absolute',
top: 0
}}
/>
</div>
)
: return <div/>
}
}
jsfiddle link: http://jsfiddle.net/d7hwzapc/
class HoverImage extends Component {
constructor (props) {
super(props)
this.state = {
firstImageLoaded: false,
};
}
componentDidMount() {
this.setState({ firstImageLoaded: true });
}
loadSecondImage() {
if(this.state.firstImageLoaded) {
return (<img
className='img-responsive'
src={stripUrl(this.props.secondaryImage)}
/>);
}
}
render () {
return (
<div style={{position: 'relative'}}>
<img
className='img-responsive'
src={stripUrl(this.props.primaryImage)}
onLoad={this.setState({ firstImageLoaded: true })}
style={{
':hover': {
opacity: 0
},
position: 'absolute',
top: 0}}
/>
{this.loadSecondImage()}
</div>
)
}
When the initial mount is done, it will set a flag in the state which will trigger a re-render.
Hope that helps!
ps: this answer is in no way perfect but should get what you wanted done.

react-burger-menu css does not work

I'm using burger-menu and i can't set CSS for the burger menu like the author guide: https://github.com/negomi/react-burger-menu
Here's my burger menu component:
import React from 'react';
import BurgerMenu from 'react-burger-menu';
import { List, ListItem, ListItemContent } from 'react-mdl';
var MenuWrap = React.createClass({
getInitialState() {
return {hidden : false};
},
toggle() {
this.setState({hidden: !this.state.hidden});
},
render() {
let style;
if (this.state.hidden) {
style = {display: 'none'};
}
return (
<div style={style} className={this.props.side}>
{this.props.children}
</div>
);
}
});
export default class LeftSidebar extends React.Component {
constructor(props) {
super(props);
this.state = {
currentMenu: 'push',
side: 'left',
hidden: true,
};
};
render() {
const Menu = BurgerMenu[this.state.currentMenu];
var styles = {
bmBurgerButton: {
position: 'fixed',
width: '36px',
height: '30px',
left: '36px',
top: '36px'
},
bmBurgerBars: {
background: '#373a47'
},
bmCrossButton: {
height: '24px',
width: '24px'
},
bmCross: {
background: '#bdc3c7'
},
bmMenu: {
background: '#373a47',
padding: '2.5em 1.5em 0',
fontSize: '1.15em'
},
bmMorphShape: {
fill: '#373a47'
},
bmItemList: {
color: '#b8b7ad',
padding: '0.8em'
},
bmOverlay: {
background: 'rgba(0, 0, 0, 0.3)'
}
};
return (
<MenuWrap wait={20}>
<Menu
style={styles}
noOverlay id={this.state.currentMenu}
pageWrapId={'page-wrap'}
outerContainerId={'outer-container'}
>
{console.log(Menu)}
<List>
<ListItem>
<ListItemContent icon="person">Dashboard</ListItemContent>
</ListItem>
<ListItem>
<ListItemContent icon="person">Community</ListItemContent>
</ListItem>
<ListItem>
<ListItemContent icon="person">About</ListItemContent>
</ListItem>
</List>
</Menu>
</MenuWrap>
);
}
};
And here is my main component:
import React from 'react';
import styles from './Main.scss';
import LeftSidebar from '../LeftSidebar/LeftSidebar'
export default class Program extends React.Component {
render() {
return (
<div id="outer-container" style={{height: '100%'}}>
<LeftSidebar />
<div id="page-wrap">
<p>Content</p>
</div>
</div>
);
}
}
All of the css from variable styles does not work.
EDIT: the problem above is solved by change style ={styles} to styles = {styles}. The other problem is: when i click close, the sidebar menu shift down about 10 or 20px before moving back to the left. How to remove that shift down effect?
Seems like you have a typo. It should be styles={styles} instead of style={styles}.

Resources