How can I use an Openlayer 'overlay' inside React? - reactjs

I created a simple card with react and I want to display an overlay.
But the overlay is not showing and I have no errors in the console.
I declare the map in the constructor
// Declaration of the map
this.olmap = new Map({
target: null,
layers: [this.osm],
view: new View({
center: this.state.center,
zoom: this.state.zoom
})
})
// Déclaration of the Marker
this.marker = new Overlay({
position: fromLonLat([1.3529599, 44.0221252]),
positioning: "center-center",
element: document.getElementById("marker"),
stopEvent: false
});
//console.log(this.marker);
// Adding to the Map Object
this.olmap.addOverlay(this.marker);
and here is the rendering
render() {
return (
<>
<div id="map15" style={{ width: "100%", height: "360px" }} />
<div style={{ display: "none" }}>
{/* Marker */}
<div
id="marker"
title="Marker"
style={{
width: "20px",
height: "20px",
border: "1px solid #088",
borderRadius: "10px",
backgroundColor: "#0FF",
opacity: "0.5"
}}
/>
</div>
</>
);
}
}

I finally found the solution, I moved the declaration of the Marker in componentDidMount
componentDidMount() {
this.olmap.setTarget("map15");
// Déclaration of the Marker
this.marker = new Overlay({
position: fromLonLat([-43.3307, -22.9201]),
positioning: "center-center",
element: document.getElementById("marker"),
stopEvent: false
});
//console.log(this.marker);
// Adding to the Map Object
this.olmap.addOverlay(this.marker);
console.log(this.olmap);
}

Related

Draw2D alignement in react, or Draw2D boxing

I found a simple application with React and draw2D.
The dashbox is the div and the canvas.
The circle is a
draw2d.shape.basic.Circle({
x: 40,
y: 10,
stroke: 3
})
How to change the code to draw the circle inside the box dashed (the canvas)?
https://codesandbox.io/s/exciting-cray-97g6r?file=/src/App.js
thanks.
The first solution
<span>
<p>Avec canvas2</p>
<div ref={inputRef} id="canvas"
style={{ height: 200, width: 300, border: "dashed brown",position:"relative" }}/>
</span>
Only add position:"relative", to the div.
I improve this solution again.
componentDidMount() {
this.canvas = new draw2d.Canvas("canvas", 9000, 9000));
this.canvas.add(
new draw2d.shape.basic.Circle({
x: 40,
y: 10,
stroke: 3
})
);
}
render() {
return (
<span>
<p>Avec canvas2</p>
<div
id="canvas"
style={{ height: 600, width: 600, border: "dashed brown" }}
/>
</span>
);
}
Now, we have a big picture inside a window ..

is it possible to animate a strikethrough with React Spring?

I'm new to React Spring and I initially tried this
const strikeProps = useSpring({
textDecoration: "line-through",
from: { textDecoration: "none" },
});
But it's not working. I think there should be a way simulate the CSS solution for this.
The problem here is, that the original CSS solution is uses pseudo element for emulating the strike trough. We can only add react-spring properties for normal html elements. So the most compact way is to create a separate strike through component for this problem. For example:
const StrikeTroughtText = ({ children, weight = 1 }) => {
const props = useSpring({
from: { width: "0%" },
to: { width: "100%" }
});
return (
<div style={{ position: "relative", display: "inline-block" }}>
{children}
<animated.div
style={{
position: "absolute",
top: "50%",
left: 0,
width: props.width,
height: `${weight}px`,
background: "black"
}}
/>
</div>
);
};
We basically animate the width of the absolutely positioned div containing a black line over the text.
You can use it like a div component:
<StrikeTroughtText>text</StrikeTroughtText>
For bigger font size the default 1 px line weight is not enough, so I added a weight property also.
Here is my example: https://codesandbox.io/s/react-strike-trought-text-component-with-react-spring-animation-86cfd?file=/src/App.js

Auto Bottom Scroll in reactjs and jsx

I have integrated dialogflow in a react page and it is working now the issue is whenever I am writing a phrase in the bot is responding but the chat window is not getting auto scrolled to bottom. I want the bot window to be automatically scrolled to bottom every time.
class App extends Component {
render() {
const { feed, sendMessage } = this.props;// structure of the bot
return (
<div // which is the main div
style={{
backgroundColor: "green",
height: "70%",
width: "23%",
position: "fixed",
bottom: 0,
right: 5
}}
>
<div // inner div
style={{
height: "67%",
width: "22%",
position: "fixed",
bottom: "30px",
maxHeight: "65%",
right: "5px",
overflowY: "scroll",
overflowX: "hidden"
}}
>
<h1>CHATBOT!</h1>
{feed.map(entry => ( // the div where the user is typing the response
<div>{entry.text}</div> // inner- inner div
))}
</div>
<div
style={{
position: "fixed",
right: "23%",
bottom: "28px",
marginLeft: "-1300px"
}}
>
<input
style={{
position: "fixed",
width: "22%",
height: "3%"
}}
type="text" // the value by which the user is connected the bot
onKeyDown={e => // this is the box where the response is coming from the bot
e.keyCode === 13 ? sendMessage(e.target.value) : null
}// 13 is the ascii of ENTER
/>
</div>
</div>
);
}
}
const mapStateToProps = state => ({
feed: state
});
chat.js // intergration with dialogflow
const accessToken = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"; //you have to enter your key
const client = new ApiAiClient({ accessToken });
const ON_MESSAGE = "ON_MESSAGE";
export const sendMessage = (text, sender = "user") => ({ // bot text box
type: ON_MESSAGE,
payload: { text, sender }
});
const messageMiddleware = () => next => action => {
next(action);
if (action.type === ON_MESSAGE) {
const { text } = action.payload;
client.textRequest(text).then(onSuccess);
function onSuccess(response) {// response from dialgflow
const {
result: { fulfillment }
} = response;
next(sendMessage(fulfillment.speech, "bot"));
}
}
};
const initState = [{ text: "" }];
const messageReducer = (state = initState, action) => {
switch (action.type) {
case ON_MESSAGE:
return [...state, action.payload];
default:
return state;
}
};
app.js
class App extends Component {
render() {
const { feed, sendMessage } = this.props;// structure of the bot
return (
<div // which is the main div
style={{
backgroundColor: "green",
height: "70%",
width: "23%",
position: "fixed",
bottom: 0,
right: 5
}}
>
<div // inner div
style={{
height: "67%",
width: "22%",
position: "fixed",
bottom: "30px",
maxHeight: "65%",
right: "5px",
overflowY: "scroll",
overflowX: "hidden"
}}
>
<h1>CHATBOT!</h1>
{feed.map(entry => ( // the div where the user is typing the response
<div>{entry.text}</div> // inner- inner div
))}
</div>
<div
style={{
position: "fixed",
right: "23%",
bottom: "28px",
marginLeft: "-1300px"
}}
>
<input
style={{
position: "fixed",
width: "22%",
height: "3%"
}}
type="text" // the value by which the user is connected the bot
onKeyDown={e => // this is the box where the response is coming from the bot
e.keyCode === 13 ? sendMessage(e.target.value) : null
}// 13 is the ascii of ENTER
/>
</div>
</div>
);
}
}
const mapStateToProps = state => ({
feed: state
});
I want the bot window to be automatically scrolled whenever I type anything in the window.
In the App component, you can create a span or div and place it right below the chat-feed.
Then give that element a ref, which we will use to scroll to upon receiving any new message.
You can use React.createRef() to make that ref. Refs essentially give us access to methods you traditionally see in vanilla JavaScript.
It also looks like you're receiving updated messages via props from Redux. So we can use componentDidUpdate() to run some logic that will scroll to that ref element.
class App extends Component {
endOfFeed = React.createRef()
scrollToEnd = () => {
if(this.endOfFeed.current){
this.endOfFeed.current.scrollIntoView()
}
}
componentDidUpdate(prevProps){
if(prevProps.feed.length !== this.props.feed.length){
this.scrollToEnd()
}
}
render() {
const { feed, sendMessage } = this.props;// structure of the bot
return (
<div // which is the main div
style={{
backgroundColor: "green",
height: "70%",
width: "23%",
position: "fixed",
bottom: 0,
right: 5
}}
>
<div // inner div
style={{
height: "67%",
width: "22%",
position: "fixed",
bottom: "30px",
maxHeight: "65%",
right: "5px",
overflowY: "scroll",
overflowX: "hidden"
}}
>
<h1>CHATBOT!</h1>
{feed.map(entry => ( // the div where the user is typing the response
<div>{entry.text}</div> // inner- inner div
))}
<span ref={this.endOfFeed}></span>
</div>
<div
style={{
position: "fixed",
right: "23%",
bottom: "28px",
marginLeft: "-1300px"
}}
>
<input
style={{
position: "fixed",
width: "22%",
height: "3%"
}}
type="text" // the value by which the user is connected the bot
onKeyDown={e => // this is the box where the response is coming from the bot
e.keyCode === 13 ? sendMessage(e.target.value) : null
}// 13 is the ascii of ENTER
/>
</div>
</div>
);
}
}
const mapStateToProps = state => ({
feed: state
});
You can use window.scrollY property and give offset accordingly.
Check this https://developer.mozilla.org/en-US/docs/Web/API/Window/scrollY

Trigger Marker on external element hover without map re-rendering React

I have an airbnb-like app with cards on the left and a google map on the right (using the react-google-maps package)
I would like to highlight (in the code below through an animation) a marker when the user hovers on its corresponding card.
I actually managed to do so (see code below) but the problem is, the map component rerenders when another card is hovered on by the user.
Is there a way to do so without having the map to rerender everytime ?
My App.js (simplyfied for comprehension purposes):
import React from "react";
import { Meals } from "../api/meals.js";
import { Restaurants } from "../api/restaurants.js";
import MealCard from "./MealCard";
import MealsMap from "./MealsMap";
class App extends React.Component {
constructor() {
super();
this.state = {
highlightedMarker: ""
};
this.renderMeals = this.renderMeals.bind(this);
this.highlightMarker = this.highlightMarker.bind(this);
}
renderMeals() {
return this.props.meals.map(m => (
<div
className="col-sm-6 col-xs-12 "
key={m._id}
onMouseOver={() => this.highlightMarker(m.restaurant)}
>
<MealCard
name={m.name}
restaurant={
this.props.restaurants.find(r => r._id === m.restaurant).name
}
image={m.image}
address={
this.props.restaurants.find(r => r._id === m.restaurant).address
}
/>
</div>
));
}
renderMap() {
return (
<MealsMap
restaurants={this.props.restaurants}
highlightedMarker={this.state.highlightedMarker}
/>
);
}
highlightMarker(restaurantId) {
this.setState({ highlightedMarker: restaurantId });
}
render() {
return (
<div>
<div className="app-wrapper" style={{ display: "flex" }}>
<div className="container">
<div className="row">{this.renderMeals()}</div>
</div>
{this.renderMap()}
</div>
</div>
);
}
}
and my MealsMap.js:
import React from "react";
import { withGoogleMap, GoogleMap, Marker } from "react-google-maps";
class MealsMap extends React.Component {
render() {
const GoogleMapMeals = withGoogleMap(props => (
<GoogleMap
defaultCenter={{ lat: 50.6320134, lng: 3.0568584 }}
defaultZoom={13}
>
{this.props.restaurants.map(r => (
<Marker
key={r._id}
position={{ lat: Number(r.lat), lng: Number(r.lng) }}
animation={
this.props.highlightedMarker === r._id
? google.maps.Animation.BOUNCE
: ""
}
/>
))}
</GoogleMap>
));
return (
<GoogleMapMeals
containerElement={
<div
style={{
flex: "0 0 400px",
height: "100vh",
position: "sticky",
top: "0"
}}
/>
}
mapElement={
<div
style={{
height: "100%",
width: "100%",
position: "absolute",
top: "0px",
left: "0px",
backgroundColor: "rgb(229, 227, 223)"
}}
/>
}
/>
);
}
}
export default MealsMap;
You don't want to define the GoogleMapMeals component inside of the render method of MealsMap, since that will result in a new component each render which will make React unmount the previous one and create an entirely new one.
You could define GoogleMapMeals outside of the render method instead.
Example
const GoogleMapMeals = withGoogleMap(props => (
<GoogleMap
defaultCenter={{ lat: 50.6320134, lng: 3.0568584 }}
defaultZoom={13}
>
{props.markers.map(r => (
<Marker
key={r._id}
position={{ lat: Number(r.lat), lng: Number(r.lng) }}
animation={
props.highlightedMarker === r._id
? google.maps.Animation.BOUNCE
: ""
}
/>
))}
</GoogleMap>
));
class MealsMap extends React.Component {
render() {
return (
<GoogleMapMeals
markers={this.props.restaurants}
highlightedMarker={this.props.highlightedMarker}
containerElement={
<div
style={{
flex: "0 0 400px",
height: "100vh",
position: "sticky",
top: "0"
}}
/>
}
mapElement={
<div
style={{
height: "100%",
width: "100%",
position: "absolute",
top: "0px",
left: "0px",
backgroundColor: "rgb(229, 227, 223)"
}}
/>
}
/>
);
}
}

Update React state from outside render when using HOC?

I'm creating a React widget and using the react-jss HOC for styling. The widget is just a small part of a larger page, and I want to be able to signal it to open or close with a button on another part of the page outside of the React render. Originally I was doing it like this:
var modal = ReactDOM.render(<Modal />, document.getElementById('widget'))
// Inside an onClick function
modal.toggleModal()
That was before JSS, but now widget doesn't return the component, it returns the JSS HOC. I've tried passing <Widget /> a prop and updating that and then using widget.forceUpdate() but that did nothing. Not really sure what else to try at this point. I'm currently just toggling everything outside of React, but I want the component to be able to close itself as well.
import React, { Component } from 'react'
import injectSheet from 'react-jss'
const styles = {
show: {
display: 'block',
},
modal: {
display: 'none',
background: 'rgba(0, 0, 0, 0.3)',
position: 'fixed',
top: 0,
left: 0,
bottom: 0,
right: 0,
width: '100%',
height: '100%',
},
form: {
maxWidth: '440px',
margin: '15px',
padding: '30px',
background: '#fff',
position: 'fixed',
top: '50%',
left: '50%',
transform: 'translate(-50%, -50%)'
},
input: {
width: '100%',
marginBottom: '15px'
},
button: {
width: '100%'
}
}
class Modal extends Component {
constructor(props) {
super(props)
this.state = {
show: false
}
this.toggleModal = this.toggleModal.bind(this)
}
toggleModal() {
this.setState({show: !this.state.show})
}
render() {
const { classes } = this.props
return (
<div className={`${classes.modal} ${this.state.show ? classes.show : ''}`}>
<form className={classes.form}>
<label htmlFor="aatitle-field">Title</label>
<input className={classes.input} type="text" id="aatitle-field" name="title" value="" />
<button className={`btn ${classes.button}`}>Save</button>
</form>
</div>
)
}
}
export default injectSheet(styles)(Modal)
First of all, please use the classnames library to generate classNames, it's so more elegant.
Secondly, classes are applied in order they are parsed by the brower. So what you're doing is tricky. How can we know if the modal class is parsed before show? (This is a requirement in your current code). You can simply move the modal declaration before the show declaration in styles and surely this will work, but this is a fragile solution. Better is to use a show and hide class and apply them according the state. This removes the dependency of which style class is loaded first. So remove display:none from modal and introduce hide:
Something like:
import React, { Component } from 'react'
import injectSheet from 'react-jss'
const styles = {
show: {
display: 'block',
},
hide: {
display: 'none',
},
modal: {
background: 'rgba(0, 0, 0, 0.3)',
position: 'fixed',
top: 0,
left: 0,
bottom: 0,
right: 0,
width: '100%',
height: '100%',
},
form: {
maxWidth: '440px',
margin: '15px',
padding: '30px',
background: '#fff',
position: 'fixed',
top: '50%',
left: '50%',
transform: 'translate(-50%, -50%)'
},
input: {
width: '100%',
marginBottom: '15px'
},
button: {
width: '100%'
}
}
class Modal extends Component {
constructor(props) {
super(props)
this.state = {
show: false
}
this.toggleModal = this.toggleModal.bind(this)
}
toggleModal() {
this.setState({show: !this.state.show})
}
render() {
const { classes } = this.props
return (
<div className={`${classes.modal} ${this.state.show ? classes.show : classes.hide}`}>
<form className={classes.form}>
<label htmlFor="aatitle-field">Title</label>
<input className={classes.input} type="text" id="aatitle-field" name="title" value="" />
<button className={`btn ${classes.button}`}>Save</button>
</form>
</div>
)
}
}
export default injectSheet(styles)(Modal)
```

Resources