ReactJs - Horizontal Mobile Menu using ReactCSSTransitionGroup - reactjs

Mobile Menu UI: (PanelGroup shared with ancestor & descendant components)
Inital MenuPanel passes a PanelGroup (with an navItems array in props) to the DOM.
Some of the navItems passed have subNavItems.
Those with subNavItems also pass a PanelGroup with its children to the DOM.
What is expected to happen:
When the MenuPanel is triggered navItems are listed and those with subNavItems have a marker for the user to click to reveal the subNavItems.
When subNavItems are selected, all previous navItems are to disappear and only the subNavItems display.
Desired Result: Items up the chain disappear & selected child nodes appear after Family Law navItem is clicked
Actual Result: Items up the chain remain and selected child nodes write ontop of them after Family Law navItem is clicked
MobileNavButton: (the blue menu button above)
export default class MobileNavButton extends Component {
constructor(props) {
super(props);
this.state = {
showMobileMenuGroup: false
}
}
static propTypes = {
btn: PropTypes.object.isRequired,
viewDevice: PropTypes.object.isRequired
};
handleShowHide(e) {
e.preventDefault();
const doesShow = !this.state.showMobileMenuGroup;
this.setState({showMobileMenuGroup: doesShow});
}
render() {
const { btn, viewDevice } = this.props;
return (
<section>
.....
{ btn.clickEvent ?
<li ref="menuButton">
<a onClick={ this.handleShowHide.bind(this) }>
<icon className={ btn.icon }/>
<h2>{ btn.innerText }</h2>
</a>
<MobileMenuPanel childPages={ btn.childPages } showMobileMenuGroup={ this.state.showMobileMenuGroup } />
</li> : ''
}
</section>
);
}
}
MenuPanel:
export default class MobileMenuPanel extends Component {
static propTypes = {
childPages: PropTypes.array.isRequired,
showMobileMenuGroup: PropTypes.bool.isRequired
};
render() {
const { showMobileMenuGroup } = this.props;
const { childPages } = this.props;
return (
<div className='imenupanel' style={showMobileMenuGroup ? showSlide : hideSlide } >
<MobileMenuGroup childPages={ childPages } showMobileMenuGroup={showMobileMenuGroup ? true : false } />
</div>
);
}
}
MobileMenuGroup:
export default class MobileMenuGroup extends Component {
static propTypes = {
childPages: PropTypes.array.isRequired,
showMobileMenuGroup: PropTypes.bool.isRequired
};
render() {
const { childPages } = this.props;
const { showMobileMenuGroup } = this.props;
return (
<div className='imenu' style={ showMobileMenuGroup ? { display: 'block' } : { display: 'none' } }>
{
childPages.map((childPage, idx) => {
return (
<MobileNavMenuItem key={ idx } menuItem={ childPage }/>
)
})
}
</div>
);
}
}
MobileNavMenuItem:
export default class MobileNavMenuItem extends Component {
constructor(props) {
super(props);
this.state = {
showMobileMenuGroup: false
}
}
static propTypes = {
menuItem: PropTypes.object.isRequired
};
showChildren(e) {
e.preventDefault();
let showMobileMenuGroup = !this.state.showMobileMenuGroup;
this.setState({ showMobileMenuGroup: showMobileMenuGroup });
}
render() {
const { menuItem } = this.props;
const childPages = menuItem.childPages !== undefined ? menuItem.childPages.length : 0;
let menuItemsStyle = childPages > 0 ? 'imenuitem iright' : 'imenuitem'
return (
<div className={ menuItemsStyle } pageId={ menuItem.pageId } childPages={ childPages }
>
{ childPages > 0 ?
<span onClick={ this.showChildren.bind(this) } >
<div style={ { color: '#FFFFFF', padding: '12px 15px' } }>{ menuItem.title }</div>
<MenuPanelGroup childPages={ menuItem.childPages } showMobileMenuGroup={ this.state.showMobileMenuGroup } />
</span>
:
<a href={ menuItem.linkTo }>{ menuItem.title }</a>
}
</div>
);
}
}

I went with ReactCSSTransitionGroup
Here is sample code that achieved my goals:
Index.js
// "react": "^0.14.7",
// "react-addons-css-transition-group": "^0.14.7",
// "react-dom": "^0.14.7",
import React from 'react';
import { render } from 'react-dom';
const ReactCSSTransitionGroup = require('react-addons-css-transition-group')
const SlideTransition = React.createClass({
propTypes: {
depth: React.PropTypes.number.isRequired,
name: React.PropTypes.string
},
getDefaultProps() {
return {
name: 'slider'
};
},
getInitialState() {
return {direction: 'right'};
},
componentWillReceiveProps(newProps) {
const direction = newProps.depth > this.props.depth ? 'right' : 'left';
this.setState({direction});
},
render() {
const {name, depth} = this.props;
const outerProps = {
className: `${name}-outer-wrapper ${this.props.className}`
};
console.log(`name: ${name} depth: ${depth}`);
const transProps = {
component: 'div',
transitionName: `${name}-${this.state.direction}`,
className: `${name}-transition-group`
};
const innerProps = {
ref: 'inner',
key: depth,
className: `${name}-inner-wrapper` // imenupanel
};
return <div {...this.props} {...outerProps}>
<ReactCSSTransitionGroup transitionName="example" transitionEnterTimeout={500} transitionLeaveTimeout={300} {...transProps}>
<div {...innerProps}>
{this.props.children}
</div>
</ReactCSSTransitionGroup>
</div>;
}
});
const Browser = React.createClass({
getInitialState() {
return {
path: []
}
},
navUp() {
this.setState({path: this.state.path.slice(0, -1)})
},
navDown(index) {
this.setState({path: this.state.path.concat(index)})
},
render() {
const {path} = this.state;
const items = path.reduce(function(items, key) {
return items[key].children;
}, this.props.items);
console.log(path);
console.log(this.props.items);
return <div className="browser">
<SlideTransition depth={path.length} className="items-container">
<div className="menuItem">{path.length > 0 ? <a style={ { color: 'white' } } onClick={this.navUp}>Back</a> : 'Home'}</div>
{items.map(function(item, index) {
if (item.children) {
return <div key={item.name} style={ { clear: 'both' } } className="menuItem" >
<a style={ { display: 'inline!Important', color: 'white' } } href="#">{item.name}</a>
<a style={ { display: 'inline!Important', float: 'right', background: 'transparent' } } className="arrow" onClick={e => this.navDown(index)} key={item.name}> </a></div>;
} else {
return <div className="menuItem" key={item.name} key={item.name}><a href="#"style={ { color: 'white' } }>{item.name}</a></div>;
}
}.bind(this))}
</SlideTransition>
</div>;
}
});
const data = [
{name: 'Animal', children: [
{name: 'Land', children: [
{name: 'Cheetah'},
{name: 'Ant'},
]},
{name: 'Air', children: [
{name: 'Eagle'}
]},
{name: 'Water', children: [
{name: 'Water 1', children: [{name: 'Level'}]},
{name: 'Water 2', children: [
{name: 'Water 2', children: [
{name: 'Water 2 -1'},
{name: 'Water 2 -2', children: [
{name: 'Water 2-2-1'},
{name: 'Water 2-2 -2'},
{name: 'Water 2-2-3'}
]},
{name: 'Water 2 -3'}
]}
]}
]}
]},
{name: 'Vegetable', children: [
{name: 'Broccoli'},
{name: 'IE6'},
]},
{name: 'Mineral', children: [
{name: 'Granite'},
{name: 'Uraninite'},
]},
];
render(<Browser items={data} />, document.getElementById('MobileNav'));
index.html styles
<style>
a {
cursor: pointer;
display: block;
color:#fff;
}
.items-container {
max-width: 20rem;
border: 2px solid #000;
border-radius: .25rem;
}
.arrow {
padding: .25rem .5rem;
}
.menuItem {
color: #FFFFFF;
font-size: 15px;
border-bottom: solid 1px #5a5a5a;
padding: 10px 0 10px 10px;
}
a.arrow:after {
content: '\e0ea';
color: #fff;
font-family: 'icomoon-ult';
font-style: normal;
font-weight: normal;
text-transform: none;
}
a.item:hover {
background: #eee;
}
/* Generic slider styles */
.slider-outer-wrapper {
position: relative;
overflow: hidden;
transition: max-height .2s ease-in;
}
.slider-transition-group {
width: 200%;
overflow: hidden;
}
.slider-inner-wrapper {
background-color: #000;
width: 50%;
float: right;
transition: all .2s ease-in-out;
height: 400px;
}
.slider-inner-wrapper:first-child {
position: relative;
left: -50%;
}
.slider-right-enter {
transform: translate3d(100%, 0, 0);
}
.slider-left-enter {
transform: translate3d(-100%, 0, 0);
}
.slider-right-enter-active,
.slider-left-enter-active,
.slider-right-leave,
.slider-left-leave {
transform: translate3d(0, 0, 0);
}
.slider-right-leave-active {
transform: translate3d(-100%, 0, 0);
}
.slider-left-leave-active {
transform: translate3d(100%, 0, 0);
}
</style>

Related

Issue in removing Grandchild in a recursive component

What I have been trying to achieve?
Create a nested context menu that is driven by a config.
Where am I stuck:
Sub menus are rendering correctly, but if there is more than 2 level, the change in root level only affects its sub-menu and not its entire tree
Here is the sandbox link for you to check.
Steps to reproduce:
On load, a menu is displayed (say menu)
Click on File, it will open its sub menu (say sub-menu1).
Click on Open in the sub-menu1, again another sub menu (say sub-menu2) is open.
Now when you click on Edit in menu, sub-menu1 disappears but not sub-menu2
I think, I know the problem. sub-menu2 is not refreshing because props or state is not changed. To hide it, we will need to trickle down some prop but can't think of elegant way to do it without state management system.
You'll have a better time if the ContextMenu component is responsible for state management and recursion is flattened into iteration.
function ContextItem({ item, onClick }) {
return (
<div className="menu-item" onClick={() => onClick(item)}>
<p className="menu-title">{item.title}</p>
{item.children && item.children.length > 0 ? <i className="right-icon">{">"}</i> : null}
</div>
);
}
function MenuList({ list, onClick }) {
return (
<div className="menu-container">
{list.map((listItem) => (
<ContextItem item={listItem} key={listItem.title} onClick={onClick} />
))}
</div>
);
}
const ContextMenu = ({ list }) => {
const [openSubmenus, setOpenSubmenus] = React.useState([]);
const clickHandler = React.useCallback((item, level) => {
if (item.children && item.children.length) {
setOpenSubmenus((oldItems) => {
return [...oldItems.slice(0, level), item.children];
});
} else {
setOpenSubmenus([]); // item selected, clear submenus
alert(item.title);
}
}, []);
const menus = [list, ...openSubmenus];
return (
<div className="menu">
{menus.map((menu, level) => (
<MenuList
key={level}
list={menu}
level={level}
onClick={(item) => clickHandler(item, level)}
/>
))}
</div>
);
};
const menuList = [{
title: "File",
children: [{
title: "Close",
children: [],
action: "fileClose",
}, {
title: "Open",
children: [{
title: "A:\\",
children: [],
action: "",
}, {
title: "C:\\",
children: [],
action: "",
}, {
title: "\\",
children: [],
action: "",
}],
action: "",
}, {
title: "Find",
children: [{
title: "here",
children: [],
}, {
title: "elsewhere",
children: [],
}],
action: "",
}, {
title: "Backup",
children: [],
action: "backup",
}],
action: "",
}, {
title: "Edit",
children: [],
action: "edit",
}];
function App() {
return <ContextMenu list={menuList} />;
}
ReactDOM.render(<App />, document.getElementById("root"));
.menu {
display: flex;
flex-direction: row;
}
.menu-container {
display: flex;
flex-direction: column;
background-color: #eee;
border: 1px solid gray;
border-radius: 4px;
}
.menu-item {
display: flex;
flex-direction: row;
margin: 2px;
max-width: 200px;
line-height: 30px;
padding: 5px 10px;
}
.menu-title {
min-width: 80px;
height: 30px;
flex-grow: 1;
margin: 0;
vertical-align: middle;
}
.menu-title.active {
background-color: blue;
color: white;
}
.right-icon {
width: 25px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.0.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.0.0/umd/react-dom.production.min.js"></script>
<div id="root"></div>
You could use the label as the key to reset the ContextMenu state when selectedItem change (assuming the label is unique at a given depth, but it seems reasonable, otherwise you could add unique ids).
export const ContextMenu = ({list}) => {
const [selectedItem, setSelectedItem] = useState();
return (
<div className="menu">
<div className="menu-container">
{list.map((listItem) => {
return (
<ContextItem
item={listItem}
key={listItem.title}
onClick={setSelectedItem}
/>
);
})}
</div>
{selectedItem?.children.length > 0 && <ContextMenu
key={selectedItem.title}
list={selectedItem.children}/>}
</div>
);
};

Written a Flexi React Component that is handed a JSON object.buting having logic error

const flexiConfig = {
items: [
{
"name": "person_name",
"label": "Person's Name",
"type": 'TextField',
},
{
"name": "states",
"label": "Person's state",
"type": "DropDown",
"values": [
"Maharashtra",
"Kerala",
"Tamil Nadu"
]
}
]
};
class Flexi extends React.Component {
constructor(props) {
super(props)
this.state = {
}
}
onFlexSubmit(e) {
e.preventDefault();
console.log(props.config.items)
}
render() {
return (
<div>
<form action="">
<button type="submit">Submit</button>
</form>
</div>
)
}
}
ReactDOM.render(<Flexi onSubmit={this.onFlexiSubmit} config={flexiConfig}/>, document.querySelector("#app"))
body {
background: #20262E;
padding: 20px;
font-family: Helvetica;
}
#app {
background: #fff;
border-radius: 4px;
padding: 20px;
transition: all 0.2s;
}
li {
margin: 8px 0;
}
h2 {
font-weight: bold;
margin-bottom: 15px;
}
.done {
color: rgba(0, 0, 0, 0.3);
text-decoration: line-through;
}
input {
margin-right: 5px;
}
<div id="app"></div>
Tried to do like this,
the component creates the appropriate TextField and DropDown HTML elements to allow the user to enter the necessary information. The component also renders a submit button that calls the onSubmit function supplied in the props with a JSON object as an argument that contains the user entered values for each of the items in flexiConfig.
what changes i need to make in order to get above one.
Not sure, but my understanding from your questions is that
you're trying to render a form based on input from that json
pass values back to the function which does something? (I just printed those)
relevant flexi component:
import React, { useState, useEffect } from "react";
const Flexi = ({ onSubmitFn, config }) => {
const [inputVal, setInputVal] = useState("");
const [ddVal, setDdVal] = useState("");
useEffect(() => {
}, []);
const changeHandler = e => {
setInputVal(e.target.value);
};
const changeHandlerDD = e => {
setDdVal(e.target.value);
};
const formSubmitHandler = e => {
e.preventDefault();
onSubmitFn({ name: inputVal, dd: ddVal });
};
return (
<>
<h1>Flex-ish form</h1>
<form>
{(config.length > 0) ?config.map((val, index) => {
if (val.type === "TextField") {
return (
<div>
<label htmlFor={val.label}>{val.label}:</label>
<input type="text" id={val.label} onChange={changeHandler} />
</div>
);
}
if (val.type === "DropDown") {
return (
<div>
<label htmlFor={val.label}>{val.label}:</label>
<select type="text" id={val.label} onChange={changeHandlerDD}>
<option value="" />
{val.values.map(ddOption => {
return <option value={ddOption}>{ddOption}</option>;
})}
</select>
</div>
);
}
}): <p>No data received</p> }
<button type="submit" onClick={formSubmitHandler}>
Submit
</button>
</form>
</>
);
};
export default Flexi;
relevant section in the parent component:
constructor() {
super();
this.state = {
name: "React",
place: 'somewhere',
};
}
onFlexSubmit = (val) => {
this.setState({ name: val.name, place: val.dd });
}
render() {
return (
<div>
<Flexi onSubmitFn={this.onFlexSubmit} config={flexiConfig.items} />
<Hello name={this.state.name} place={this.state.place} />
</div>
);
}
}
complete working stackblitz here

React-select, open sub-menu when hover over an option

I'm trying to build a submenu inside a main menu with React-select, it should be something like this:
When hovering over an option from the main menu, it triggers the submenu to open at the side.
Is there a way to do this using react-select? I couldn't find any example or documentation on this, is there a function like ```optionOnMouseover`` for this? Thank you in advance!
const options = [
{ value: 'chocolate', label: 'Chocolate' },
{ value: 'strawberry', label: 'Strawberry' },
{ value: 'vanilla', label: 'Vanilla' },
];
...
<Select
value={...}
onChange={...}
options={options}
/>```
This is on click, but if you need on hover,
just modify it
import React, { useState } from "react";
import ReactDOM from "react-dom";
import Select, { components } from "react-select"
const CustomOption = (props) => {
const [submenu, setSubmenu] = useState(false)
const [height, setHeight] = useState(0)
const handleOption = (e) => {
if(submenu) {
setSubmenu(false)
} else {
setHeight(e.clientY)
setSubmenu(true)
}
}
const handleSubOption = (e) => {
console.log('clicked')
}
const { data } = props;
return data.custom ? (
<>
<div onClick={handleOption} className="customs">
{data.label} <span className="caret"/>
{
submenu && (
<div className="dropdown-submenu">
<div className="drops" onClick={handleSubOption}>
Test dropdown 1
</div>
<div className="drops" onClick={handleSubOption}>
Test dropdown 2
</div>
<div className="drops" onClick={handleSubOption}>
Test dropdown 3
</div>
</div>
)
}
</div>
<style jsx>{`
.customs {
height: 36px;
padding: 8px;
position: relative;
}
.drops {
height: 36px;
padding: 8px;
}
.customs:hover, .drops:hover {
background-color: #17cf76;
}
.dropdown-submenu {
position: fixed;
top: ${height - 10}px;
left: 410px;
min-height: 36px;
overflow: auto;
border: 1px solid hsl(0,0%,80%);
border-radius: 4px;
color: #212529;
}
`}</style>
</>
) : (
<components.Option {...props} />
);
};
const options = [
{ custom: true, label: "I'm a custom link", value: "cust" }
];
function App() {
return (
<>
<Select classNamePrefix="category-select" className="w-30" components={{ Option: CustomOption }} options={options} />
<style jsx global>{`
* {
font-family: sans-serif;
text-align: center;
}
.w-30 {
width: 30% !important;
}
`}</style>
</>
)
}
export default App

react map leaflet circleMarker doesn't render

I have a leaflet map, and I have one button. I want to display some circlesMarker when I click on a button and remove the circlesMarker when I click again (remove option is not implemented yet)
I don't want to render each time all map when I click the button, I want render only the circleMarker.
CircleMarker doesn't render on a map, but I can see the map
Here my code :
COMPONENT MAP
function Map1() {
const apartments = [
{ roomType: 'shared room', geometry: [41.402610, 2.204270] },
{ roomType: 'shared room', geometry: [41.411300, 2.217630] },
{ roomType: 'private room', geometry: [41.410220, 2.212520] },
{ roomType: 'apartament sencer', geometry: [41.410630, 2.216970] },
{ roomType: 'private room', geometry: [41.409190, 2.209030] },
]
let map = useRef(null);
useEffect(() => {
let url = 'https://api.tiles.mapbox.com/v4/{id}/{z}/{x}/{y}.png?access_token={accessToken}'
const access = 'pk.eyJ1Ijoiam9zZXQyMSIsImEiOiJjazF1bGZlcHowYjVlM2RwYW9ia2pwaWtlIn0.9n-6tKArfdSfd15Do6YxLA'
map.current = L.map("map");
const defaultCenter = [41.383, 2.173];
const defaultZoom = 13.10;
let layer = L.tileLayer(url, {
maxZoom: 18,
id: 'mapbox.streets',
accessToken: access,
});
map.current.setView(defaultCenter, defaultZoom);
layer.addTo(map.current);
}, [map])
const handleOnclik = (e) => {
e.preventDefault()
let color = (e.target.name)
if (color === 'pink') {
apartments.map(item => {
if (item.roomType === 'private room' || item.roomType === 'shared room') {
return (
<div>
<p>Carme</p>
<CircleMarker className="circle"
center={[41.409190, 2.209030]}
color={'#000080'}
width={.5}
fillColor={'blue'}
fillOpacity={0.5}
stroke={'black'}
>
</CircleMarker >
</div>
)
}
})
}
return (<>
<div className="container">
<div>
<div id="map" className="normalscreen"></div>
</div>
<div id="basemaps-wrapper" className="leaflet-bar">
<select id="basemaps">
<option>Visualització</option>
<option value="mapbox.streets">Streets</option>
<option value="mapbox.satellite">Satellite</option>
<option value="mapbox.outdoors">Outdoors</option>
<option value="mapbox.dark">Dark</option>
<option value="mapbox.light">Light</option>
<option value="mapbox.DarkGray">Dark Gray</option>
</select>
<button onClick={onChangeFullScreen}>Full Screen</button>
<div>
<img onClick={handleOnclik} name="pink" src="images/pink" alt="habitacio" />
<img onClick={handleOnclik} name="green" src="images/green" alt="apartament" />
</div>
</div>
</div>
</>)
}
export default Map1
MAP CSS
html,
body {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
}
.container {
display: flex;
flex-direction: column;
width: 100vw;
justify-content: center;
}
#map {
display: block;
}
.normalscreen {
width: 50%;
height: 50vh;
left: 49%;
position: relative;
}
.fullscreen {
width: 100%;
height: 80vh;
left: 0%;
text-align: center;
}
APP
import React from 'react'
import { withRouter } from 'react-router-dom'
import Navbar from './Navbar'
import Map from './Map'
function App() {
return (<>
<Navbar />
<Map />
</>);
}
export default withRouter(App)
There is no CircleMarker component on leaflet library exposed unless you use react-leaflet which you don't. Therefore here is the code you need to use in order to add to the map circle markers:
const handleOnclik = () => {
apartments.forEach(({ geometry }) => {
new L.CircleMarker(geometry, {
radius: 5,
fillColor: "blue",
width: 0.5,
stroke: "black",
color: "#000080",
fillOpacity: 0.5
}).addTo(map.current);
});
};
I removed the if statements for simplicity just to illustrate an example.
Demo

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>

Resources