Set z-index of OverlayView in react-google-maps - reactjs

I am using react-google-maps and I want to accomplish something similar to Airbnb where when you hover over a search result and then a "marker" (aka OverlayView) is highlighted on the map. But the issue I am trying to solve is if an OverlayView is underneath another OverlayView, I want to bring that "active" OverlayView to the top (like by adjusting the z-index). The map on Airbnb does this very thing, which is when you hover over the search result and inspect the marker the z-index is altered to be 9001 to ensure it is brought to the front of all other markers.
There are a few issues related to this from years ago that have not received any movement:
tomchentw/react-google-maps#199
tomchentw/react-google-maps#93
In issue #199 it was mentioned you could hack around it with this.refs.childDiv.parentNode and setting z-index on that, but in React v16 this.refs is no longer a thing from what I can tell (it was deprecated). The original commenter attached a jsfiddle with the code sample which was:
class OverlayViewExample extends React.Component {
render () {
const pos = {lat: -34.397, lng: 150.644};
const mapPane = OverlayView.OVERLAY_MOUSE_TARGET;
const getOffset = this.getPixelPositionOffset.bind(this);
return (
<GoogleMap
defaultZoom={8}
defaultCenter={pos}
>
<OverlayView
position={pos}
mapPaneName={mapPane}
getPixelPositionOffset={getOffset}
>
<div style={{zIndex: 2}} className="overlay" ref="childDiv">This should be in front</div>
</OverlayView>
<OverlayView
position={pos}
mapPaneName={mapPane}
getPixelPositionOffset={getOffset}
>
<div style={{zIndex: 1}} className="overlay">Another overlay should be in front of me</div>
</OverlayView>
</GoogleMap>
);
}
getPixelPositionOffset (width, height) {
// this.refs is an empty object when I tried this :(
if (this.refs.childDiv) {
this.refs.childDiv.parentNode.style.zIndex = 2;
}
return { x: -(width / 2), y: -(height / 2) };
}
}
When I tried this workaround, this.refs was an empty object and therefore I could not alter the underlying zIndex of that node.
Is there another way to ensure when two OverlayViews are near each other that I can bring the one in the background to the foreground?

The answer for this was actually in my question (I should've tested the example code from Github)...
On this line: <div style={{zIndex: 2}} className="overlay" ref="childDiv">This should be in front</div> the zIndex was actually doing what I wanted (it would bring forward whichever element I wanted in the foreground).
So within each OverlayView, just apply a z-index to the first child element and that will effect the ordering of each OverlayView.

Related

Office UI Fabric: ScrollablePane's Position is not being reset to the initial position when details list is updated

Replication Steps:
https://developer.microsoft.com/en-us/fluentui#/controls/web/scrollablepane
In the 'DetailsList Locked Header' scroll down to a non zero position exceeding the first page and enter 'sed' in the 'filter by name'. This updates the detailsList and the scrollbar does not go to the initial position.
Bug/Ask:
As we scroll down to let's say a position of 50 and now if there is an update to the details list with say suppose 350. The scrollbar is not returning to the starting position and it rerenders to a random position close to the top(in my case).
Approach/Temporary Hack:
Going through the code, I noticed that the initial scroll position of the scrollablepane is being refreshed only when the 'initialScrollPosition' props change in the implementation.
I tried changing the 'initialScrollPosition' prop in my component whenever the detailList updates by setting it to either 0 or 1.
Debugging this, the props for the initialScrollPosition did not change inside the ScrollablePane component did not change.
This did not work and the scrollable pane is still not set to the start.
Has anyone found a workaround or a solution for this?
I Had the same issue
I'm also using infinitive scroll(react-infinite-scroller) to load the data.
what I finally done , was replace scrollablepane by css.
(Function handleGetNext just loading new data, and variable dataToRender I keep on the state to make concatenation between old state and new)
<div>
{handleSelect()}
<div style={{ overflowY: "scroll", height: 500 }} id="containers">
<InfiniteScroll
pageStart={0}
loadMore={() => {
handleGetNext(overviewData.page);
handleSelect();
}}
hasMore={!loadingNext && !!overviewData && overviewData.page < overviewData.totalPages}
initialLoad={false}
useWindow={false}
>
<ShimmeredDetailsList items={dataToRender} enableShimmer={overviewLoading} .........../>
</InfiniteScroll>
</div>
</div>
And later what is tricky you need to set scroll position, depends where you want to scroll.
const handleSelect = () => {
const elemToScrollTo = document.getElementById("containers");
if (!!elemToScrollTo) {
elemToScrollTo.scrollTop = 2100;//example
}
};

Intersection Observer loses entries

I am trying to use IntersectionObserver in my react app. I want to achieve lazy loading with it. Therefore I observe several elements and if they appear on screen I load content inside them.
Here is a very simplified code:
class Table extends React.Component {
constructor() {
super()
this.state = {
entries: 0
}
}
componentWillUnmount() {
console.log('componentWillUnmount')
if (this.observer) this.observer.disconnect()
}
observe (c) {
if (!this.observer) {
this.observer = new IntersectionObserver(
entries => {
this.setState({entries: entries.length})
},
{ threshold: [0, 0.25, 0.5, 0.75, 1] }
)
}
if (!c) return
this.observer.observe(c)
}
render() {
const {entries} = this.state
return (
<div>
<h1>Number of observer entries: {entries}</h1>
<div
ref={this.observe.bind(this)}
style={{height: '1000px', display: 'block', width: '500px'}}
/>
<div
ref={this.observe.bind(this)}
style={{height: '1000px', display: 'block', width: '500px'}}
/>
</div>
)
}
}
ReactDOM.render(<Table />, document.querySelector("#app"))
When a component is mounted it shows two elements are observed but as soon as I scroll down it changes to only one element. I have not idea what I am missing.
JSON fiddle - https://jsfiddle.net/w1zn49q6/12/
The divs are stacked one after the other vertically. In the initial render, as they are laid out, the intersection observer gets triggered for both, as they enter the viewport together (the first div enters, the second div exits). However, once they are rendered, they will enter/exit one at a time on a normal course of vertical scrolling, hence the entries will only ever contain one div, which intersected the x-axis most recently.
The intersection entry only reports a transition to/from 0 (not in view) from/to 1 (fully in view). So when one div has fully exited/entered the view, it will no longer be present in an entry update.
You can still get 2 entries however :). If you manage to scroll really fast! Try it using an accelerated mouse wheel. So basically, between two intersection calculations, if both the divs moved too far, both will raise the intersection event, but if they move slowly, the intersection will be gradual because they are stacked one by one.
If you would stack them in the same row, you will continuously get two entries, as both will be intersecting at the same moment with the x-axis.

change cursor to pointer google-maps-react

I am using google-maps-react. By default, when I put the mouse on map, it shows a hand cursor. I would like to set a normal pointer when person hover on map, and only when start dragging, the cursor become again a hand cursor.
I was trying to set draggableCursos prop in many ways, but I didnt get what I wanted.
Thank You for help in advance.
It appears the current version (2.0.2) of google-maps-react package published in npm does not support to specify a few properties including MapOptions.draggableCursor and MapOptions.draggingCursor via Map component. In such a cases those properties could be specified via native map object. The following example demonstrates it
class MapContainer extends React.Component {
constructor(props) {
super(props);
this.handleMapReady = this.handleMapReady.bind(this);
}
handleMapReady(mapProps,map) {
map.setOptions({
draggableCursor: "default",
draggingCursor: "pointer"
});
}
render() {
return (
<div className="map-container">
<Map
google={this.props.google}
className={"map"}
zoom={this.props.zoom}
initialCenter={this.props.center}
onReady={this.handleMapReady}
/>
</div>
);
}
}
Here is a demo
You can add a custom className to the map and attach an event that will change the pointer-events CSS property on the map.

React - scrolling to the bottom of a flex container without scrolling the whole page

I'm using react and trying to build a facebook like chat - where the right column scrolls down and has a list of threads, and the middle column had the thread content and scrolls bottom up.
I have it all setup using flex, but am stuck on getting the middle column to scroll to the bottom by default (since I want to see the latest chat message). Here is the snippet for the actual container (I'm using React bootstrap):
<Row >
<Col ref={ (node) => { this.cont = node }}>
{this._buildThread()};
</Col>
</Row>
and here is the snippet for my ComponentDidMount:
componentDidMount() {
MessageStore.addChangeListener(this._onChange);
MessageService.getThread(this.props.id, 1000, 1, false);
this.cont.scrollTop = this.cont.scrollHeight;
}
Where cont is the ref for the container div that holds my messages. Yet nothing happens - it doesn't scroll, and if I look at node.scrollTop after setting it, it remains at 0 - seems immutable.
Any help would be much appreciated. Thanks!
Not sure about the details of your CSS, but I just had a similar-looking issue. The solution in my case was to make sure that the element itself would scroll and not its container:
.containerIWantToScroll {
overflow-y: auto;
}
If the element's container expands to fit the element, then that element will not scroll, thus its scrollTop value is always zero.
When are you triggering the code you included? To scroll to the bottom on render, I would write something like the following, calling triggerScroll in componentDidMount:
class App extends React.Component {
componentDidMount () {
this.triggerScroll();
}
triggerScroll () {
this.cont.scrollTop = this.cont.scrollHeight;
}
render () {
return (
<div>
<div className='containerIWantToScroll' ref={ (node) => { this.cont = node } }>
Content
</div>
</div>
)
}
}

Is it possible to combine 'px' and 'vh' into the height of 1 component in react js

I have a few components and some of them are sized with px. The others I want to be variable size. However, the ones that are variable size are not a constant percentage of the page because of the components with a fixed px height. So I want a component to be about 80% of the screen height('80vh') minus the height of the other component. I was hoping to use something like
style={{height:'80vh-40px'}}
but that does not work.
I found this page which gets close but that my program does not recognize that calc function. Do I need to require it from some library maybe?
Any ideas on how to make this work?
Thanks!
I use syntax like this in my React.js components:
<div style={{height: 'calc(100vh - 400px)'}}></div>
it works for me.
Inside CSS code with LESS preprocessor I use the same syntax, but not equal:
.right_col {
height: calc(~"100vh - 400px");
}
In my experiments, I found that symbol "~" doesn't work in React.js components.
A way to solve it is by using style={} in your component.
const styles = {targetDiv: { height: 'calc(100vh - Xpx)'}}
then...
<div style={styles.targetDiv}>
...
</div>
Note - you can get the window.innerHeight (see http://ryanve.com/lab/dimensions/ for height and width options in javascript) and just calculate this yourself in react and set the width or height in pixels.
If you do this with height, you'd need to recalculate if it changes
state = {
windowHeight: window.innerHeight
}
componentWillMount () {
window.addEventListener('resize', this.handleResize)
}
componentWillUnmount () {
window.removeEventListener('resize', this.handleResize)
}
handleResize = () => {
this.setState({ windowHeight: window.innerHeight })
}
in your div
<div style={{height: this.state.windowHeight}} > ... </div>
You wouldn't want to do this everywhere, but it's very handy for some situations. For example - setting 100% height on Android mobile web, where 100vh does not work and device buttons cover up part of the screen.

Resources