Customizing react-leaflet marker icons by using font awesome - leaflet

It is more theoretical question, rather than a problem.
How to use font awesome icons as react-leaflet map marker icons?
I would like to have such an icon selector control to assign(customize) each marker icon I have got on my map. By the way I am using JSX components of Map and Marker.
Is it possible to achieve this?
Anybody have a sample pen about this? I have really googled it strongly but couldn't find any plugin but a fontawesome plugin that is working only with Leaflet 1.0.
So any idea appreciated.
Thanks in advance.

For some reasons code is not getting formatted. See code on code sandbox
Here is how you can use font-awesome icons as markers.
Add font-awesome CDN to your index.html
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.3.1/css/all.css" integrity="sha384-mzrmE5qonljUremFsqc01SB46JvROS7bZs3IO2EmfFsd15uHvIt+Y8vEf7N7fWAU"
crossorigin="anonymous">
Use divIcon along with renderToStaticMarkup from react-dom/server to generate icon for marker. And pass this divIcon to Marker as a icon prop.
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import { renderToStaticMarkup } from 'react-dom/server';
import { divIcon } from 'leaflet';
import { Map, TileLayer, Marker, Popup } from 'react-leaflet';
import './styles.css';
class App extends Component {
state = {
lat: 51.505,
lng: -0.091,
zoom: 13,
};
render() {
const position = [this.state.lat, this.state.lng];
const iconMarkup = renderToStaticMarkup(<i className=" fa fa-map-marker-alt fa-3x" />);
const customMarkerIcon = divIcon({
html: iconMarkup,
});
return (
<Map center={position} zoom={this.state.zoom}>
<TileLayer
attribution="&copy <a href="http://osm.org/copyright">OpenStreetMap</a> contributors"
url="https://{s}.tile.osm.org/{z}/{x}/{y}.png"
/>
<Marker position={position} icon={customMarkerIcon}>
<Popup>
A pretty CSS3 popup. <br /> Easily customizable.
</Popup>
</Marker>
</Map>
);
}
}
const rootElement = document.getElementById('root');
ReactDOM.render(<App />, rootElement);
Override divIcon default style by adding the following class to your css file
.leaflet-div-icon {
background: transparent;
border: none;
}
Here is a working example of the same:
https://codesandbox.io/s/4ry180jl34

For those who already use the React components of Fontawesome (FontAwesomeIcon), there is a solution that does not require importing via CDN again. It uses the same principles of Murli's answers, but instead of adding <i className=" fa fa-map-marker-alt fa-3x" />, you can convert the FontAwesomeIcon component to html and pass that into the html attribute of the divIcon. It would look like this (adapted the example of the accepted answer):
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import ReactDOMServer from 'react-dom/server';
import Leaflet from 'leaflet'
import { Map, TileLayer, Marker, Popup } from 'react-leaflet';
import { FontAwesomeIcon } from '#fortawesome/react-fontawesome';
import './styles.css';
// FontAwesome requirements
import { faUserAstronaut } from '#fortawesome/free-solid-svg-icons'
library.add(faUserAstronaut)
class App extends Component {
state = {
lat: 51.505,
lng: -0.091,
zoom: 13,
};
render() {
const position = [this.state.lat, this.state.lng];
const iconHTML = ReactDOMServer.renderToString(<FontAwesomeIcon icon='user-astronaut' />)
const customMarkerIcon = new Leaflet.DivIcon({
html: iconHTML,
});
return (
<Map center={position} zoom={this.state.zoom}>
<TileLayer
attribution="&copy <a href="http://osm.org/copyright">OpenStreetMap</a> contributors"
url="https://{s}.tile.osm.org/{z}/{x}/{y}.png"
/>
<Marker position={position} icon={customMarkerIcon}>
<Popup>
A pretty CSS3 popup. <br /> Easily customizable.
</Popup>
</Marker>
</Map>
);
}
}
const rootElement = document.getElementById('root');
ReactDOM.render(<App />, rootElement);

Create divIcon and insert it into icon property:
// marker-icons.js
import L from 'leaflet';
const factory = new L.divIcon({
className: '',
iconAnchor: [12, 25],
labelAnchor: [-6, 0],
popupAnchor: [0, -15],
iconSize: [25, 41],
html: `<span class="fa fa-industry"></span>`
});
export default { factory };
Use icon in component file:
// component.js
import { factory } from './marker-icons';
<MapContainer center={[12.23432, 87.234]} zoom={6} scrollWheelZoom={false}>
<TileLayer attribution='© OpenStreetMap contributors' url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"/>
<Marker position={45.4534, 23.43]} icon={factory}>
<Popup>Help text</Popup>
</Marker>
</MapContainer>

Related

React Leaflet Map not displaying

I have the basic example from React-leaflet, but for the life of me I cannot get anything to display. I am using this fix from Jeremy Monson for the common babel-loader issue. I was having the same problems of no display when I went through the manual fixes. Right now I am using styled-components, but I was having the same issue with regular CSS. My map component page looks like this.
import React from 'react'
import styled from '#emotion/styled'
import { MapContainer, TileLayer, Marker, Popup } from '#monsonjeremy/react-leaflet'
const Leaflet = styled.div`
height:500px;
width:500px;
`
const Map = () => {
return (
<Leaflet>
<MapContainer center={[51.505, -0.09]} zoom={13} scrollWheelZoom={false}>
<TileLayer
attribution='© OpenStreetMap contributors'
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
/>
<Marker position={[51.505, -0.09]}>
<Popup>
A pretty CSS3 popup. <br /> Easily customizable.
</Popup>
</Marker>
</MapContainer>
</Leaflet>
)
}
export default Map
I am loading the leaflet css in the of head my index.html page. I am importing my map component in App.js and rendering it there.
Any thoughts or pointing in the right direction would be much appreciated.
Add height to your map container using style prop:
<MapContainer
center={[51.505, -0.09]}
zoom={13}
scrollWheelZoom={false}
style={{height: '100%'}} // Add a height
>
...

How to align the helperText to the far left in the TextField variant="outlined" in Material-UI for React?

By default the helperText "Some important text" is not aligned to the far left. How can it be done?
You can use makeStyles for your css and then apply your generated styles to the className prop inside of the FormHelperTextProps prop from TextField.
import React from "react";
import { TextField } from "#material-ui/core";
import { makeStyles } from "#material-ui/core/styles";
const useStyles = makeStyles((theme) => ({
helperText: {
marginLeft: 0
}
}));
export default function App() {
const classes = useStyles();
return (
<div className="App">
<TextField
FormHelperTextProps={{
className: classes.helperText
}}
label="Test"
helperText="Helper text..."
variant="outlined"
/>
</div>
);
}
Live demo:

How should a set width nav be made into a sticky in Material UI?

New to Material UI I'm trying to create a sticky div with a set width similar to how Material UI has their side nav on the right of their docs. For some reason I'm unable to get the div to stick. Per research and the docs I've read through several questions and here are my attempts:
Attempt 1
I tried utilizing Box but it scrolls with the page:
import React from 'react'
import { Hidden, Box, List,ListItem } from '#material-ui/core/'
import { makeStyles } from '#material-ui/core/styles'
const useStyles = makeStyles(theme => ({
box: {
background: 'red',
position: 'sticky',
top: 70,
bottom: 20,
paddingTop: 100,
marginTop: theme.spacing(2),
width: 300,
},
sub: {
display: 'block',
overflow: 'auto',
},
}))
const SideToc = ({ nav }) => {
const { box, sub } = useStyles()
return (
<>
<Hidden smDown>
<Box className={box}>
Contents
<List className={sub} component="nav" aria-label="main mailbox folders">
<ListItem button>Monday</ListItem>
<ListItem button>Tuesday</ListItem>
<ListItem button>Wednesday</ListItem>
</List>
</Box>
</Hidden>
</>
)
}
export default SideToc
Attempt 2
after research I've found a few mentions of Drawer but when I deploy the below component my green drawerPaper is fixed to the left and my blue Drawer is to the right a correct width.
import React from 'react'
import Drawer from '#material-ui/core/Drawer'
import Hidden from '#material-ui/core/Hidden'
import List from '#material-ui/core/List'
import ListItem from '#material-ui/core/ListItem'
import { makeStyles } from '#material-ui/core/styles'
const SideToc = ({ nav }) => {
const useStyles = makeStyles(theme => ({
toolbar: theme.mixins.toolbar, // necessary for content to be below app bar
drawer: {
background: 'blue',
width: 300,
flexShrink: 0,
},
drawerPaper: {
marginTop: 120,
background: 'green',
},
}))
const { toolbar, drawer, drawerPaper } = useStyles()
return (
<>
<div className={toolbar} />
<Hidden smDown>
<nav className={drawer} aria-label="mailbox folders">
<Drawer
classes={{
paper: drawerPaper,
}}
variant="permanent"
>
Contents
<List component="nav" aria-label="main mailbox folders">
<ListItem button>Monday</ListItem>
<ListItem button>Tuesday</ListItem>
<ListItem button>Wednesday</ListItem>
</List>
</Drawer>
</nav>
</Hidden>
</>
)
}
export default SideToc
Here is my parent component that has the content with the set width nav:
import React from 'react'
import { makeStyles } from '#material-ui/core/styles'
// Components
import SideToc from './SideToc'
const useStyles = makeStyles(theme => ({
toolbar: theme.mixins.toolbar, // necessary for content to be below app bar
content: {
flexGrow: 1,
padding: theme.spacing(3),
overflow: 'hidden',
background: 'orange',
},
}))
const Parent = ({ nav, children }) => {
const { content, toolbar } = useStyles()
return (
<>
<main className={content}>
<div className={toolbar} />
{children}
</main>
<SideToc className={toolbar} nav={nav} />
</>
)
}
export default Parent
Research
How to make a Material UI grid element sticky?
Reactjs Material-ui hide appBar sticky or static
Make a React Material-UI component to stick to the bottom-right of the screen
React Material-Ui Sticky Table Header with Dynamic Height
In Material UI if I want a div to be sticky that will not scroll and have a fixed width how should that be done? Is my issue due to my parent? Should I be using Material's Grid in this approach?
Edit
After further testing I am able to get the side nav to stick but the content, if outside the viewport, will not allow scrolling. The parent component has been modified with just a div surrounding the SideToc component:
<>
<main className={content}>
<div className={toolbar} />
{children}
</main>
<div>
<SideToc toc={toc} />
</div>
</>
SideToc Component:
import React from 'react'
import { Hidden, Box, List, ListItem } from '#material-ui/core/'
import { makeStyles } from '#material-ui/core/styles'
const useStyles = makeStyles(theme => ({
box: {
marginTop: theme.spacing(8),
top: theme.spacing(8),
width: 250,
position: 'sticky',
right: 0,
overflowY: 'auto',
flexShrink: 0,
},
}))
const SideToc = ({ nav }) => {
const { box } = useStyles()
return (
<>
<Hidden smDown>
<Box className={box} component="nav">
Contents
<List component="nav">
<ListItem button>Monday</ListItem>
<ListItem button>Tuesday</ListItem>
<ListItem button>Wednesday</ListItem>
</List>
</Box>
</Hidden>
</>
)
}
export default SideToc
Per request:

how to use material-ui Dialog PaperProps

I'm using v1.1.0 of material-ui in React 16.3.2. I'm trying to create a landing page similar to Showcase - Local Insights
where the dialog has opacity (Find foreclosures). I'm trying to use PaperProps for Dialog component described here Dialog doc
Here's a component I've created to try to do this.
import React from 'react';
import PropTypes from 'prop-types';
import { withStyles } from '#material-ui/core/styles';
import Button from '#material-ui/core/Button';
import Dialog from '#material-ui/core/Dialog';
import DialogActions from '#material-ui/core/DialogActions';
import DialogContent from '#material-ui/core/DialogContent';
import DialogTitle from '#material-ui/core/DialogTitle';
import ForwardIcon from '#material-ui/icons/Forward';
import Input from '#material-ui/core/Input';
import FormControl from '#material-ui/core/FormControl';
import Slide from '#material-ui/core/Slide';
const styles = theme => ({
dialogPaper: {
opacity: 0.5,
border: '#FF0000 1px solid',
},
button: {
margin: '30px'
}
});
function Transition(props) {
return <Slide direction="up" {...props} />;
}
class SignInDialog extends React.Component {
state = {
open: false,
username: ''
};
handleClickOpen = () => {
this.setState({ open: true });
};
handleClose = () => {
this.setState({ open: false });
};
handleChange = name => event => {
this.setState({
[name]: event.target.value,
});
};
render() {
const { classes } = this.props;
return (
<div>
<Button variant="fab" color="primary" aria-label="add" className={classes.button} onClick={this.handleClickOpen}>
<ForwardIcon />
</Button>
<Dialog
PaperProps={styles.dialogPaper}
open={this.state.open}
TransitionComponent={Transition}
onClose={this.handleClose}
aria-labelledby="form-dialog-title"
>
<DialogTitle id="form-dialog-title">WELCOME</DialogTitle>
<DialogContent>
<p>SIGN IN</p>
<FormControl className={classes.formControl}>
<Input
value={this.state.searchString}
onChange={this.handleChange('search')}
id="siginin-input"
placeholder="Enter your username"
/>
</FormControl>
</DialogContent>
<DialogActions>
<Button onClick={this.handleClose} color="primary">
Cancel
</Button>
<Button onClick={this.handleClose} color="primary">
Continue
</Button>
</DialogActions>
</Dialog>
</div>
);
}
}
SignInDialog.propTypes = {
classes: PropTypes.object.isRequired,
};
export default withStyles(styles)(SignInDialog);
I haven't been able to figure out how to get the Dialog to take the styles. What is needed to get PaperProps to work?
If you want to use PaperProps you have to specify the props of the Paperfor which you are applying style.
<Dialog
PaperProps={{ classes: {root: classes.dialogPaper } }}
/>
You can also use classes property and override the style
<Dialog
classes={{paper:classes.dialogPaper}}
/>
The correct way to overide paper props is by using classNames
<Dialog
PaperProps={{ className: classNames(classes.dialogPaper) }}/>
<Dialog
PaperProps={{ classes: {root: classes.dialogPaper } }}
/>

How to render Map in nested div View

I'm trying to work out how to render a map inside a nested div. I have my component Home.js:
import React, {Component} from "react";
import {Map, TileLayer} from "react-leaflet"
class Home extends Component {
constructor( )
{
super();
this.state = {
latlng: {
lat: 51.5074,
lng: 0.1277,
},
}
}
render()
{
return (
<div id="main-wrap">
<div id="sidebar">
...
</div>
<div id="content-wrap" style={{height:'700px'}}>
<Map
center={this.state.latlng}
length={4}
zoom={13}>
<TileLayer
attribution="&copy <a href="http://osm.org/copyright">OpenStreetMap</a> contributors"
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
/>
</Map>
</div>
</div>
);
}
}
export default Home;
But that doesn't render anything although I can see the component in the page if I use the React developer tools.
If I just have one div containing the map then it works fine. e.g.
import React, {Component} from "react";
import {Map, TileLayer} from "react-leaflet"
class Home extends Component {
constructor()
{
super();
this.state = {
latlng: {
lat: 51.5074,
lng: 0.1277,
},
}
}
render()
{
return (
<div id="main-wrap">
<Map
center={this.state.latlng}
length={4}
zoom={13}>
<TileLayer
attribution="&copy <a href="http://osm.org/copyright">OpenStreetMap</a> contributors"
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
/>
</Map>
</div>
);
}
}
export default Home;
But I'd quite like to include other stuff on the page as well, hence the multiple divs.
Thanks in advance if you can help.
Mark
The problem was that I hadn't set this CSS:
.leaflet-container {
height: 100%;
}
All works now!