I've made a covid related travel map here: Where Can We Go Now
It's a static react JS website, billy basic with create-react-app. It uses amongst other things the excellent React Simple Maps (kudos to the creator) and react-tooltip.
Expected behaviour:
Locally the tooltip works fine when tested with npm run start
I move my mouse over a country and the tooltip displays some relevant information approximately where my mouse is on the map.
The Problem:
. . . once published with npm run build sort of works but strange things happen.
Please follow link to website to observe, but in short, if the sea (i.e. an area that isn't a country - so not defined by an svg in the topojson) is clicked then the tooltip is place at the top centre of the whole map itself.
Some bits of relevant code:
Index.js (snippet):
const [content, setContent] = useState("");
return (
<div><h1> Where can we go on holiday ?* </h1>
<MapChart setTooltipContent={setContent}/>
<ReactTooltip html={true}>{content}</ReactTooltip>
</div>
);
}
MapChart.js (snippet):
return (
<div>
<p><small>*Right now in the UK: {data.headlineAdviceNow} Last updated at {updateString}</small></p>
<ComposableMap
projectionConfig={{
rotate: [-40, -30, 10],
scale: 1600
}}
width={mapWidth}
height={mapHeight}
style={{ maxWidth: "100%", height: "80vh", background: "#b3b3b3", borderStyle: "solid", borderColor: "white", margin: 0.5,
borderRadius: 20 }}
>
<ZoomableGroup zoom={zoom} center={center} translateExtent={[
[0, 0],
[mapWidth, mapHeight]
]} maxZoom={15} data-tip="">
<Sphere stroke="#E4E5E6" strokeWidth={0.5} />
<Graticule stroke="#E4E5E6" strokeWidth={0.2} />
{!loading && (<Geographies geography={geoUrl}>
{({ geographies }) =>
geographies.map((geo) => {
const d = data.countries.find((s) => s.country.iso_a3 === geo.properties.ISO_A3);
return (
<Geography
key={geo.rsmKey}
geography={geo}
style={{
default: {
outline: "none",
stroke: "grey",
strokeWidth: "1",
strokeLinecap: "butt",
fill: d ? colorScale(d[attr]) : "#F5F4F6"
},
hover: {
outline: "none",
stroke: "black",
strokeWidth: "3",
strokeLinecap: "butt",
fill: d ? colorScale(d[attr]) : "#F5F4F6"
},
pressed: {
outline: "none"
}
}}
onMouseEnter={() => {
const NAME = geo.properties.NAME;
const travelIndex = d ? d["indexDescription"] : "Unknown";
//const border = d ? d["borderStatusString"] : "Unknown";
const returnStatus = d ? (fromCountry==="GBR" ? d["ukCategory"] : "TBC") : "Unknown";
const vaccinePc = d ? d.vaccineData["total_vaccinations_per_hundred"] + "%" : "NK";
const arrival = (() => {try{ return d["restrictionsFrom"][data["iomKeys"].indexOf(fromCountry)].split("-") } catch {return [4,""]}});
const arrivalLevel = rstrctLkup[arrival()[0]]["short"]
//const arvlRtns = arrival()[1].length===0 ? arrival()[1] : arrival()[1].split(",")
//var text =""
//var i;
//for (i = 0; i < arvlRtns.length; i++) {
// if (data["restrictionDefs"][arvlRtns[i]]) {
// if (i===0) {text="<br />Arrival Details:<br />"}
// text += data["restrictionDefs"][arvlRtns[i]] + "<br>";
//}}
setTooltipContent(`<b>${NAME.toUpperCase()}:</b> ${travelIndex}<br /><p style="text-align: left"><b>RISK FACTORS</b><br />Vaccinated: ${vaccinePc}<br /><br /><b>CURRENT RESTRICTIONS</b><br />Entry: ${arrivalLevel}<br />Return: ${returnStatus}</p>`);
}}
onMouseLeave={() => {
setTooltipContent("");
}}
/>
);
})
}
</Geographies>
)}
</ZoomableGroup>
</ComposableMap>
</div>
);
};
export default memo(MapChart);
What I've tried:
Variations of setting tooltip position, and moving where and what data-tip="" is. Also, banging head on wall.
I was inaccurate in my question, so didn't provide relevant details that was the source of the problem.
I publish with github actions and this was the fix:
yarn install --frozen-lockfile
The frozen lockfile was the key to make sure I was using the exact versions of relevant packages as I had been testing locally. I'd erroneously suggested I was using npm.
Related
Learning react by coding, here i'm using 'react-zoom-pan-pinch' for mouse scroll wheel, so when i zoomIn using mouse scroll and its scale gets bigger than '1.311' then it should make circle smaller ("10"), here i have created demo of what i want to achieve.
https://codesandbox.io/s/react-zoom-pan-pinch-forked-svx5v6?file=/src/index.js
you can check from console when value gets bigger that circle becomes smaller,
but in my real code i'm using 'viewGenerator' (react-d3-graph) and customNode where i have that svg and circle
import { Graph } from "react-d3-graph";
const [circleR, setCircleR] = useState("50");
const zoomChange = (event: any) => {
if (event.state.scale < 1.311) {
setCircleR("50");
}
if (event.state.scale > 1.311) {
setCircleR("10");
}
console.log("event state scale ", event.state.scale);
console.log("event circleR ", circleR);
};
function CustomNode(props) {
return (
<div>
<svg viewBox="-1 -1 2 2">
<circle
cx="0"
cy="0"
r={circleR}
strokeWidth="0.1"
stroke= "black"
fill= "orange"
/>
</svg>
</div>
);
}
const default_config = {
d3_config: {
nodeHighlightBehavior: false,
staticGraphWithDragAndDrop: true,
automaticRearrangeAfterDropNode: false,
d3: { disableLinkForce: true },
height: 512,
// width: 576,
minZoom: 1,
maxZoom: 1,
node: {
color: "lightblue",
size: 500,
highlightStrokeColor: "blue",
fontSize: 20,
highlightFontSize: 22,
labelProperty: (node: default_config) =>
node.name ? node.name : node.id,
viewGenerator: (node) => (
<CustomNode node={node} focusId={null} />
),
}
};
const [config, setConfig] = useState(default_config);
<Graph
config={config.d3_config}
/>
Problem is 'r={circleR}' is just taking default value from state, and its not updating it when i'm zooming in or zooming out.
Inside my 'zoomChange ' function i have two console.logs and i can check that it gives me right values and changes between '10' and '50' (zoomChange function works fine), then what is the reason i'm not getting updated value to 'r={circleR}' ?
writing values to it like 'r="50" or r="10"' will work but i want updated values according to setState.
english is not my mother language so could be mistakes !
I have to do a single stacked bar graph getting the data from an API, but I am finding some issues.
1- On one hand, the data I get is not rounded, even if I use %.
2- On the other hand, the total I get is not always 100%, then the bar is sometimes a few pixels shorter and sometimes a few pixels longer than expected.
import styled from 'styled-components'
import { Data } from '../../Types'
const Colors: Record<string, string> = {
warning: 'yellow',
good: 'green',
danger: 'red',
}
const StackedBar = (props: {
title?: string
Data: Data | null
}) => {
return (
<div>
<div
style={{
display: 'flex',
flex: '1 1 auto',
alignSelf: 'auto',
}}
>
{props && props.Data ? (
props.Data.items.map((item) => {
const percentage =
(item.count / items_total) * 100
return (
<Rectangle
percentage={percentage}
color={
itemColors[item.items_total]
}
/>
)
})
) : (
<Rectangle percentage={100} color="grey" />
)}
</div>
</div>
)
}
export default StackedBar
const Rectangle = styled.div<{ percentage: number; color: string }>`
height: 20px;
width: ${(props) => props.percentage}%;
background-color: ${(props) => props.color};
`
const NormalBold = styled.p`
font-weight: 700;
font-size: var(--font-size-normal);
`
percentage =(item.count / items_total) * 100, here items_ total is not defined, check the code again. I think it should be item.items_total.
for rounding use Math.round(percentage) to round off values.
I think this should resolve your issues.
How can I change the css styles using JavaScript on React ?
For example I would make this:
document.querySelector('.container').style.backGroundColor='purple';
}
Is it right ? Or should i make it with another way ?
You can use the style attribute.
<SomeComponent style={{
backgroundColor: someCondition ? 'purple' : null
}} />
Considering paragraph element
document.getElementsByClassName("container").style.color = "blue";
The simple way to change css in Reactjs is Inline styling. Others way you can see at: https://codeburst.io/4-four-ways-to-style-react-components-ac6f323da822
Let example. If your want change color of user status. Active will be green or deactive will be red.
const Example = (props) => {
let isActive = Math.floor(Math.random() * 2) % 2 === 0;
const color = isActive ? 'green' : 'red';
return <div style={{backgroundColor: color}}> {isActive ? 'Active' : 'Deactive'} </div>;
}
OR:
const Example = (props) => {
let isActive = Math.floor(Math.random() * 2) % 2 === 0;
return <div style={{backgroundColor: isActive ? 'green' : 'red'}}> {isActive ? 'Active' : 'Deactive'} </div>;
}
OR all styling:
const Example = (props) => {
let isActive = Math.floor(Math.random() * 2) % 2 === 0;
let styleStatus = isActive ?
{
backgroundColor: 'green',
fontSize: '20',
} :
{
backgroundColor: 'red',
fontSize: '15',
};
return <div style={styleStatus}> {isActive ? 'Active' : 'Deactive'} </div>;
}
make a const type obj like this(must create inside render method)
const mystyle = {
color: "white",
backgroundColor: "DodgerBlue",
padding: "10px",
fontFamily: "Arial"
};
and assign this into your element like this
h1 style={mystyle}>Hello Style!</h1>
Problem
I don't understand how to properly zoom to a position of a click, .center(center) on projection doesn't do anything.
Code
import React, { Fragment, useState } from 'react'
import { ComposableMap, ZoomableGroup, Geographies, Geography } from 'react-simple-maps'
import { geoConicEqualArea } from 'd3-geo'
import map from '../public/Russia.json'
const VectorMap = ({ onClick, width = 990, height = 505, onBlur }) => {
const [zoom, setZoom] = useState(1)
const [center, setCenter] = useState([100, 100])
// Zoom event handlers
const onWheel = e => (e.deltaY > 0 ? setZoom(prev => prev - 0.3) : setZoom(prev => prev + 0.3))
return (
<Fragment>
<ComposableMap
projection={() =>
geoConicEqualArea()
.scale(690)
// projection doesn't change anyway
.center(center)
.parallels([40, 80])
.rotate([265])
.translate([130, 5])
}
{...{ width, height }}
style={{
width: '85vw',
height: '100vh'
}}
>
<ZoomableGroup {...{ zoom }}>
<Geographies geography={map}>
{(geographies, projection) =>
geographies.map((geography, i) => (
<Geography
key={i}
{...{ geography, onWheel, projection, onBlur }}
onClick={(obj, e) => {
onClick(obj)
setCenter(e.clientX, e.clientY)
// setZoom(1.5)
}}
style={{
default: {
fill: '#ECEFF1',
stroke: '#607D8B',
strokeWidth: 0.75,
outline: 'none'
},
hover: {
fill: '#607D8B',
stroke: '#607D8B',
strokeWidth: 0.75,
outline: 'none'
},
pressed: {
fill: '#FF5722',
stroke: '#607D8B',
strokeWidth: 0.75,
outline: 'none'
}
}}
/>
))
}
</Geographies>
</ZoomableGroup>
</ComposableMap>
<button className="ZoomBtn" onClick={() => setZoom(prev => prev + 0.1)}>
+
</button>
<button className="ZoomBtn" onClick={() => setZoom(prev => prev - 0.1)}>
-
</button>
</Fragment>
)
}
export default VectorMap
I also noticed there's a center property for ZoomableGroup but it also doesn't seem to be working properly. It zooms to one place and map becomes no draggable anymore.
Here are the warnings in console:
Warning: componentWillReceiveProps has been renamed, and is not recommended for use.
* Move data fetching code or side effects to componentDidUpdate.
* If you're updating state whenever props change, refactor your code to use memoization techniques or move it to static getDerivedStateFromProps.
* Rename componentWillReceiveProps to UNSAFE_componentWillReceiveProps to suppress this warning in non-strict mode. In React 17.x, only the UNSAFE_ name will work. To rename all deprecated lifecycles to their new names, you can run `npx react-codemod rename-unsafe-lifecycles` in your project source folder.
Please update the following components: Geographies, ZoomableGroup dll_d6a88dbe3071bd165157.js:12608:15
Source map error: Error: Invalid URL: webpack://[name]_[chunkhash]/webpack/bootstrap
Resource URL: http://localhost/_next/static/development/dll/dll_d6a88dbe3071bd165157.js?ts=1577625154482
Source Map URL: dll_d6a88dbe3071bd165157.js.map
Unexpected value
translate(
NaN
NaN
)
scale(1)
translate(-495 -250)
parsing transform attribute.
Additional info
React version: 16.12
react-simple-maps version: 0.12.1
Help would be appreciated.
You can modify the value of center in ComposableMap.
Suppose if you want center to be at (20, 20), you must write:
center:[20, 20]
I need to make a calendar with events and I decided to use react-big-calendar. But I need to make events of different colors. So each event will have some category and each category has corresponding color. How can I change the color of the event with react?
Result should look something like this
Sorry, I haven't read the documentation really well. It can be done with the help of eventPropGetter attribute. I've made it like this:
eventStyleGetter: function(event, start, end, isSelected) {
console.log(event);
var backgroundColor = '#' + event.hexColor;
var style = {
backgroundColor: backgroundColor,
borderRadius: '0px',
opacity: 0.8,
color: 'black',
border: '0px',
display: 'block'
};
return {
style: style
};
},
render: function () {
return (
<Layout active="plan" title="Planning">
<div className="content-app fixed-header">
<div className="app-body">
<div className="box">
<BigCalendar
events={this.events}
defaultDate={new Date()}
defaultView='week'
views={[]}
onSelectSlot={(this.slotSelected)}
onSelectEvent={(this.eventSelected)}
eventPropGetter={(this.eventStyleGetter)}
/>
</div>
</div>
</div>
</Layout>
);
}
Additional tip on how to style different kinds of events: In the myEvents array of event objects, I gave each object a boolean property isMine, then I defined:
<BigCalendar
// other props here
eventPropGetter={
(event, start, end, isSelected) => {
let newStyle = {
backgroundColor: "lightgrey",
color: 'black',
borderRadius: "0px",
border: "none"
};
if (event.isMine){
newStyle.backgroundColor = "lightgreen"
}
return {
className: "",
style: newStyle
};
}
}
/>
This solution is simple !
eventPropGetter={(event) => {
const backgroundColor = event.allday ? 'yellow' : 'blue';
return { style: { backgroundColor } }
}}
change the condition according to your need and it is done.
Siva Surya's solution is the fastest, and I have added the color property as well. Thanks...
import React, {useEffect, useLayoutEffect} from 'react';
import { Calendar, momentLocalizer,globalizeLocalizer } from 'react-big-calendar'
import moment from 'moment';
import { connect } from 'frontity';
import BackgroundWrapper from 'react-big-calendar/lib/BackgroundWrapper';
const MyCalendar = ({ actions, state, objetoBloque, formato }) => {
const localizer = momentLocalizer(moment);
const myEventsList = [
{
title: 'My Event',
start: '2022-06-21T13:45:00-05:00',
end: '2022-06-25T14:00:00-05:00',
// elcolor:'red'
colorEvento:'red'
},
{
title: 'Otro',
start: '2022-06-15T13:45:00-05:00',
end: '2022-06-23T14:00:00-05:00',
colorEvento:'green',
color:'white'
}
];
return(
<div>
<Calendar
// defaultDate = {defaultDate}
localizer={localizer}
events={myEventsList}
startAccessor="start"
endAccessor="end"
style={{ height: 500 }}
BackgroundWrapper = "red"
eventPropGetter={(myEventsList) => {
const backgroundColor = myEventsList.colorEvento ? myEventsList.colorEvento : 'blue';
const color = myEventsList.color ? myEventsList.color : 'blue';
return { style: { backgroundColor ,color} }
}}
/>
</div>
)
}
export default connect(MyCalendar);
Searching for how to change the border colour of an event also lead me here, and I couldn't find the answer anywhere else, but found that adding the following done the trick:
border: "black",
borderStyle: "solid"