How to overlay content on react leaflet (z-index problem) - reactjs

Really tried to not post this as I've seen similar questions but I'm not able to display anything over the react leaflet library after reading things online.
The following code snippets show the markup for the map and the overlay I'm trying to produce.
Many thanks, any help appreciated.
Looking at other questions online people refer to making the Map container position: absolute and setting the z-index to 400+ which doesn't seem to make a difference for me with the markup below.
Map markup
<Map
zoomControl={false}
doubleClickZoom= {false}
closePopupOnClick= {false}
dragging= {false}
zoomSnap= {false}
zoomDelta= {false}
trackResize= {false}
scrollWheelZoom= {false}
touchZoom={false}
className="map"
worldCopyJump={true}
center={position}
zoom={this.state.zoom}>
<TileLayer
attribution="&copy <a href="http://osm.org/copyright">OpenStreetMap</a> contributors and Chat location by Iconika from the Noun Project"
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
/>
{this.state.pins.map(pin => (
<Marker
onClick={() => this.showPanel(pin)}
key={pin._id}
position={[pin.latitude, pin.longitude]}
icon={pinIcon}>
</Marker>
))}
</Map>
Overlay markup
<div ref={ref => this.el = ref}>
<button onClick={() => this.setState({ isPaneOpen: true })}>Click me to open right pane!</button>
<SlidingPane
className='some-custom-class'
overlayClassName='some-custom-overlay-class'
isOpen={ this.state.isPaneOpen }
title='Hey, it is optional pane title. I can be React component too.'
subtitle='Optional subtitle.'
onRequestClose={ () => {
// triggered on "<" on left top click or on outside click
this.setState({ isPaneOpen: false });
} }>
<div className="container-fluid padding">
</div>
</SlidingPane>
Update
I have now moved the overlay into a function, shown below
function MyOverLay(props) {
return <SlidingPane
className='some-custom-class'
overlayClassName='some-custom-overlay-class'
isOpen={ props.isPaneOpen }
title='Book here
</SlidingPane>
}
The change in the markup is shown below
New markup
<div>
<MyOverLay isPaneOpen={this.state.isPaneOpen} />
<div className="container-fluid padding">
<div className="row padding">
<div className="col-lg-8">
<Map zoomControl={false}
doubleClickZoom= {false}
closePopupOnClick= {false}
dragging= {false}
zoomSnap= {false}
zoomDelta= {false}
trackResize= {false}
scrollWheelZoom= {false}
touchZoom={false}
className="map"
worldCopyJump={true}
center={position}
zoom="13">
<TileLayer url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png" />
<Marker
position={[10, -10]}
icon={messageIcon}>
</Marker>
</Map>
</div>

this is the perfect answer
.leaflet-control { z-index: 0 !important}
.leaflet-pane { z-index: 0 !important}
.leaflet-top, .leaflet-bottom {z-index: 0 !important}

The solution I can only think is the use of React Portal. It's a feature of React that can append a component outside the "root" component inside the body element. Considering that element children in the lower part of the parent has higher stacking index than those in the higher part, it can "overlay" any component inside the root component. Try wrapping your component like this:
import React from 'react';
import ReactDOM from 'react-dom';
const OverlayComponent = () => {
return ReactDOM.createPortal(
<MyComponent />,
document.body
);
};

just had the same Problem. For anyone still looking for an answer - The highest z-index on leaftlet-react is for the zoom buttons and is
.leaflet-top, .leaflet-bottom
{
position: absolute;
z-index: 1000;
pointer-events: none;
}
so if you go above you can show the element in front of the map.
Cheers

Managed to get it working by controlling the z-index values within the CSS for all the panels the map renders on the page. The zIndex option available on the TileLayer doesn't seem to work for me but the following resolved the issue. Noticed - have tested this in Chrome only.
div.map.leaflet-container.leaflet-touch.leaflet-retina.leaflet-fade-anim {
/*border: 5px solid red;*/
z-index: 1;
}
div.slide-pane__content {
/*border: 5px solid black;*/
z-index: 100;
}
div.ReactModal__Content.ReactModal__Content--after-open.slide-pane.slide-pane_from_right.some-custom-class {
/*border: 5px solid orange;*/
z-index: 100;
}
div.slide-pane__header {
/*border: 5px solid orange;*/
z-index: 100;
}
div.ReactModal__Overlay.ReactModal__Overlay--after-open.slide-pane__overlay.some-custom-overlay-class {
/*border: 5px solid blue;*/
z-index: 100;
}

Related

React - Modal expands div in footer div not on main screen

New to coding, trying to get a modal to work. It is not opening the way I expected it to.
The component is currently loaded within the footer div of my site. And when the button is toggled it simply hides or shows a div within the footer. I want it to appear in the centre of the webpage as a modal.
import React, {useState} from 'react';
import './policy.css'
export default function Policy() {
const [modal, setModal] = useState(false);
const toggleModal = () => {
setModal(!modal);
};
if(modal) {
document.body.classList.add('active-modal')
} else {
document.body.classList.remove('active-modal')
}
return (
<>
<div>
<button onClick={toggleModal} className='btn-modal'>
<p>Privacy Policy</p>
</button>
</div>
{modal && (
<div className="modal">
<div onClick={toggleModal} className="overlay"></div>
<div className="modal-content">
<h2>Privacy Policy</h2>
<p>This is the privacy policy</p>
<button className='close-modal' onClick={toggleModal}>Close</button>
</div>
</div>
)}
</>
);
}
The simplest way to achieve a modal effect is to use some CSS.
You could try a very crude style like (from MUI Modal docs):
const style = {
position: "absolute",
top: "50%",
left: "50%",
transform: "translate(-50%, -50%)",
width: 400,
bgcolor: "lightgrey",
border: "2px solid #000",
boxShadow: 24,
padding: 4,
};
Then apply it to your modal:
<div className="modal" style={style}>

How to make the Swiper.js and React Player carousel responsive

I have been looking for a way to make my Swiper.js carousel responsive when it's nesting the Youtube React Player. I finally found a workaround and hope this helps somebody. This also works if the slidesPerView is more than 2 or even float number like slidesPerView={1.3}.
Remove the default css mySwiper class, and apply player-wrapper and react-player css as below.
The point is that the player-wrapper div is self close and does not wrap the <ReactPlayer> component. Also have the width and height props to be 100% in the <ReactPlayer> component.
<Swiper
slidesPerView={1.3}
grabCursor={false}
loop={false}
watchSlidesProgress={true}
centeredSlides={true}
modules={[Pagination, Navigation]}
// className='mySwiper' <=remove
>
<SwiperSlide>
<div className='player-wrapper' /> //<= self close div
<ReactPlayer
url='youtube.com/1234'
controls={true}
width='100%'
height='100%'
playing={isPlaying === data.id}
config={{
youtube: {
playerVars: { showinfo: 1 },
},
}}
className='react-player'
/>
</SwiperSlide>
CSS
.player-wrapper {
position: relative;
padding-top: 56.25%
}
.react-player {
position: absolute;
top: 0;
left: 0;
}

simple list in react beautiful dnd not working

I've been following the official course on egghead (https://egghead.io/lessons/react-reorder-a-list-with-react-beautiful-dnd and sample working code: https://codesandbox.io/s/52p60qqlpp) and wrote this:
https://codesandbox.io/s/00k3rwq3qn
However, it doesn't actually drag and drop. I've looked through several examples, and I can't spot the issue in my code. I would really appreciate someone feedback on what my mistake is.
Thanks
I don't think using inline styles work that well with Draggable in react-beautiful-dnd. If you want to apply styling to the Task component you should use styled-components, this sample shows it working if you remove the inline style configuration https://codesandbox.io/s/6lq0854m6r.
Rather use the styled-components
import React from "react";
import styled from "styled-components";
import { Draggable } from "react-beautiful-dnd";
const Container = styled.div`
margin: 10px;
border: 1px solid black;
`;
export default class Task extends React.Component {
render() {
return (
<Draggable draggableId={this.props.task.id} index={this.props.index}>
{(provided, snapshot) => (
<Container
{...provided.draggableProps}
{...provided.dragHandleProps}
ref={provided.innerRef}
>
{this.props.task.content}
</Container>
)}
</Draggable>
);
}
}
EDIT:
It seems you can apply inline styles to the draggable, but then you should extend the DraggableProps.styles within the child function of the Draggable component, see here.
{(provided, snapshot) => (
const style = {
margin: "10px",
border: "1px solid black",
...provided.draggableProps.style,
};
return <div
{...provided.draggableProps}
{...provided.dragHandleProps}
ref={provided.innerRef}
style={style}
>
{this.props.task.content}
</div>
)}
#Ian 's answer did not work well for me.So I checked the react-beautiful-dnd's documents and came up with another workaround to solve the issue:
const styles = {
backgroundImage: `url(${background.MEDIUM})`,
backgroundSize: 'cover',
};
...
{(provided) => {
const otherProps = {
...provided.draggableProps,
style: {
...provided.draggableProps.style,
...styles,
},
};
return (<div
{...otherProps}
{...provided.dragHandleProps}
ref={provided.innerRef}
>
{this.props.task.content}
</div>)
}}
If you are trying to build draggable list with React functional component using official document and not using styled-components library then it want work. It will give you some error. So apply style using className attribute and then error will go away and also code will start working.

Why are Styled Component props displaying as HTML attributes?

I'm passing down boolean props to Styled Components to change styling based on the props that are passed into the Component, like so:
<Button href="#contact" small elevate={false} display="primary">
CONTACT
</Button>
The output of this JSX is invalid HTML that looks like this:
<a href="#contact" class="Button__ButtonWrapper-fvVzGy gOcROU" display="primary" fluid="0" elevate="0" small="1">
CONTACT
</a>
Any idea how to ensure props won't be displayed as HTML attrs?
Full Button component:
const ButtonWrapper = styled.button`
padding: ${props =>
props.small
? `${rem(6)} ${props.theme.sizes.sm}`
: `${rem(12)} ${props.theme.sizes.med}`};
box-shadow: ${props =>
props.elevate
? `0 10px 15px 0 rgba(0,0,0,0.10)`
: `0 2px 8px 0 rgba(0,0,0,0.10)`};
color: ${props => {
if (props.display === 'primary') return props.theme.buttons.primary.color;
if (props.display === 'secondary')
return props.theme.buttons.secondary.color;
}};
`;
const Button = ({
display,
fluid,
children,
cta,
elevate,
small,
...other
}) => {
<ButtonWrapper
display={display}
fluid={fluid ? 1 : 0}
elevate={elevate ? 1 : 0}
small={small ? 1 : 0}
{...other}
>
{children}
{cta && (
<div className="icon" dangerouslySetInnerHTML={{ __html: CtaIcon }} />
)}
</ButtonWrapper>
};
export default Button;
This documentation page might be helpful: https://www.styled-components.com/docs/basics#passed-props
Basically if you use a non-camelcase prop to do styling, it'll get passed through to the DOM node. We're looking into better alternatives to this pattern.
I have the exact same problem, when I pass width or height props in to a component they are visible on the html, I think that both words (and maybe others) are actually keywords because with the rest of props this doesn't happen.
Example:
<div class="Container-sc-10mm8fh-0 cyDjRF">
<div class="Line-sc-10mm8fh-1 oPRx"></div>
<div height="3rem" class="VSpace-sc-18gziyv-1 kkKpPd"></div>
<div class="Line-sc-10mm8fh-1 oPRx"></div>
<div height="0.4rem" class="VSpace-sc-18gziyv-1 kkKpPd"></div>
<div class="Line-sc-10mm8fh-1 oPRx"></div>
</div>

How to disable click events for props.children in React?

How can any click events be disabled for props.children?
const Example = props => (
<div>
<div>This can be clicked</div>
{props.children} /* These can't be clicked */
</div>
)
I am rendering a PDF page using react-pdf and want the user to be able to drag a custom selection marquee (like in Photoshop...). As it is, the PDF page under or inside the marquee element still registers mouse events upon dragging, like text selection.
There is an easy, but not robust way to do this:
const Example = props => (
<div style={{pointerEvents:'none'}}>
<div style={{pointerEvents:'auto'}}>This can be clicked</div>
{props.children}
</div>
)
It is not robust, because if any of the children have pointer-events set to auto, they will be clickable too. See if it fits your needs. Also, it will kill hover and other mouse events.
Use CSS Grid to put a div on top!
A transparent div rendered on top of another div will intercept click events.
CSS Grid can be used (abused?) to make a single grid area (using grid-template-areas) and assign multiple elements to it (using grid-area).
JSX
const ClickGuard = ({allow, block}) => (
<div className='click-guard-area'>
<div className='click-guard-allowed'>{props.allow}</div>
<div className='click-guard-block' />
<div className='click-guard-blocked'>{props.block}</div>
</div>
)
CSS
.click-guard-area {
display: grid;
grid-template-areas: 'click-guard';
height: 100%;
width: 100%;
}
.click-guard-allowed {
grid-area: click-guard;
height: 100%;
width: 100%;
z-index: 2;
}
.click-guard-block {
grid-area: click-guard;
height: 100%;
width: 100%;
z-index: 1;
}
.click-guard-blocked {
grid-area: click-guard;
height: 100%;
width: 100%;
}
Note that ClickGuard expects two props: allow and block. These should be JSX. The React docs explain passing React elements here.
<ClickGuard
allow={<div>I can be clicked!</div>}
block={<div>No clicks for me. 😞</div>}
/>
You cannot change the props within an element thus its children props.
An alternative solution may be possible with React.cloneElement,
Here is a simple peace of code for you:
const Example = props => (
<div>
<div>This can be clicked</div>
{props.children.map((child)=>
React.cloneElement(child, {
disabled: true
})
)}
</div>
)

Resources