I'm pretty new to D3.js library. I've been trying to implement it in React JS project.
I'm just using d3 functions for calculations and rendering part will be done by React JS.
I successfully created a tree but I'm getting links with weird style (Not as expected)
This is what I expected (As per D3 Documentation)
But I got the below tree :(
Also I can't able to collapse or expand...
You can find the code below.
componentDidMount() {
const width = 800, height = 800;
const tree = d3.tree().size([height, width - 160]);
const stratify = d3.stratify().id((d) => {
return d.name;
}).parentId((d) => {
return d.parent;
});
const root = stratify(this.state.data)
.sort((a, b) => {
return (a.height - b.height) || a.id.localeCompare(b.id);
});
this.setState({ paths: tree(root).links() });
this.setState({ nodes: root.descendants() })
}
render() {
let paths = this.state.paths && this.state.paths.map(item => {
let d = d3
.linkHorizontal()
.x((d) => {
return d.y;
})
.y((d) => {
return d.x;
});
return <path className='link' d={d(item)} />
})
let nodes = this.state.nodes && this.state.nodes.map((node, i) => {
return <g key={node.id} className={"node" + node.children ? " node--internal" : " node--leaf"}
transform={`translate(${node.y}, ${node.x})`}>
<circle r="10" style={{ 'fill': node.children ? 'lightsteelblue' : 'black' }} />
<text y="0" dy="0" textAnchor="middle"
style={{ 'fillOpacity': 1 }}>{node.name}</text>
</g>
})
return (
<svg className="tree-chart-basic" ref={(r) => this.chartRf = r} style={{ width: '800px', height: '800px' }}>
<g transform='translate(20,20)'>
{nodes}
{paths}
</g>
</svg>
);
}
this.state.data will be having the array as follows
[
{ "name": "ProjectA", "parent": "" },
{ "name": "ApplicationA", "parent": "ProjectA" },
{ "name": "EnvironmentB", "parent": "ProjectA" },
{ "name": "TierC", "parent": "ApplicationA" },
{ "name": "TierD", "parent": "ApplicationA" },
{ "name": "TierE", "parent": "ApplicationA" },
{ "name": "ServiceF", "parent": "EnvironmentB" },
{ "name": "ContainerG", "parent": "EnvironmentB" },
{ "name": "ContainerH", "parent": "TierE" },
{ "name": "ContainerH", "parent": "TierE" },
{ "name": "ContainerH", "parent": "TierE" },
{ "name": "ContainerH", "parent": "TierE" },
{ "name": "ContainerH", "parent": "TierE" },
{ "name": "ContainerH", "parent": "TierE" }
]
How can I get the expected Tree in my code?
How can I get collapse/expand functionality?
Please tell me what I'm doing wrong. Thanks for the time. (:
I finally got the solution to my answer.
I need to specify the style to <path> tag as follows.
<path
fill="none"
stroke="#97a6ff"
strokeWidth="2px"
/>
I had the same issue when using react-tree-graph which uses d3 under the hood. Luckily you can add custom classes to customize everything. I use css-modules so I had to escape the global classnames.
import { Tree } from 'react-tree-graph';
import style from './tree.module.scss';
// ...
<Tree svgProps={{ className: style.acmeProductTree }} />
// tree.module.scss
svg.acmeProductTree path:global(.link) {
fill: none;
stroke: #2593b8;
stroke-width: 1.5px;
}
As I found out just now you can alternatively apply a custom class directly to the path element:
<Tree pathProps={{ className: style.acmeProductTreeLink }} />
.acmeProductTreeLink {
fill: none;
stroke: #2593b8;
stroke-width: 1.5px;
}
Related
So I'm trying to populate a in-memory MirageJS' database with a post request of the data in useEffect, making a get request after. Then I save the result to columnsNames state and use it to map this array and read the information as a label of Tab MaterialUI component. However, I have some questions:
When I put a console.log after useEffect or inside get request, both response.data and columnsNames state have 2 identical arrays. Why 2 and not 1?
At runtime the console prints
Uncaught TypeError: columnsNames.map is not a function
The above error occurred in the <ScrollableTabsButtonAuto> component
and nothing is showed in the screen. Why?
Anyone that could help me, thanks in advance!
Data.ts
export const data = [
{
"id": 1,
"title": "Test 1",
"steps": [
"Read a starting book",
"Keep calm and drink coffee",
"Don't Panic so much",
]
},
{
"id": 2,
"title": "Test 2",
"steps": [
"Get some helpful help",
"Don't loose yourself",
]
},
{
"id": 3,
"title": "Test 3",
"steps": [
]
},
{
"id": 4,
"title": "Test 4",
"steps": [
]
},
{
"id": 5,
"title": "Test 5",
"steps": [
]
},
{
"id": 6,
"title": "Test 6",
"steps": [
]
}
]
App.tsx
import { createServer, Model } from 'miragejs';
createServer({
models: {
columns: Model,
},
routes() {
this.namespace = 'api'
this.get('columns', () => {
return this.schema.all('columns')
})
this.post('columns', (schema, request) => {
const data = JSON.parse(request.requestBody)
return schema.create('columns', data)
})
}
})
const root = ReactDOM.createRoot(
document.getElementById('root') as HTMLElement
);
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);
Tab.tsx
interface ColumnTypes {
id: number,
title: string,
steps: string[]
}
export default function ScrollableTabsButtonAuto() {
const [value, setValue] = React.useState(0);
const [columnsNames, setColumnsNames] = React.useState<ColumnTypes[]>([])
const handleChange = (event: React.SyntheticEvent, newValue: number) => {
setValue(newValue);
};
React.useEffect(() => {
api.post('columns', data)
.then( () => {
api.get<ColumnTypes[]>('columns')
.then(response => setColumnsNames(response.data))
})
}, [])
return (
<Box sx={{ maxWidth: { xs: 320, sm: 1280 }, bgcolor: 'background.paper' }}>
<Tabs
value={value}
onChange={handleChange}
variant="scrollable"
scrollButtons="auto"
aria-label="scrollable auto tabs example"
>
{columnsNames.map(({ id, title }) => (
<Tab
label={title}
key={id}
/>)
)}
</Tabs>
</Box>
);
}
In my use case I need to create interactive farm maps where I can select fields.
I'm trying to do this using the react-simple-maps component.
what was done
I created the farm Shapfile map in QGIS.
Converted to TopoJSON using Mapshaper as per this tutorial.
However the map does not render correctly, see CodeSandbox.
What can I be missing?
Is this the best component to use in this case?
Map error
TopoJSON Map
{
"type": "Topology",
"arcs": [
[
[0, 62],
[51, 60],
[60, -46],
[-85, -76],
[-26, 62]
],
[
[112, 77],
[-60, 44],
[92, 110],
[57, -40],
[0, -2],
[-66, -60],
[14, -19],
[-37, -33]
]
],
"transform": {
"scale": [15.721852200470671, 19.17233904106825],
"translate": [-65942.30731638917, 8482615.288037943]
},
"objects": {
"Contorno_UTM": {
"type": "GeometryCollection",
"geometries": [
{
"arcs": [[0]],
"type": "Polygon",
"properties": { "id": 1, "area_ha": 197.4585 }
},
{
"arcs": [[1]],
"type": "Polygon",
"properties": { "id": 2, "area_ha": 299.0857 }
}
]
}
}
}
React Simple Map component
import React, { memo } from "react";
import {
ZoomableGroup,
ComposableMap,
Geographies,
Geography
} from "react-simple-maps";
import map from "./map.json";
const geoUrl = map;
const rounded = (num) => {
if (num > 1000000000) {
return Math.round(num / 100000000) / 10 + "Bn";
} else if (num > 1000000) {
return Math.round(num / 100000) / 10 + "M";
} else {
return Math.round(num / 100) / 10 + "K";
}
};
const MapChart = ({ setTooltipContent }) => {
return (
<>
<ComposableMap data-tip="" projectionConfig={{ scale: 200 }}>
<ZoomableGroup>
<Geographies geography={geoUrl}>
{({ geographies }) =>
geographies.map((geo) => (
<Geography
key={geo.rsmKey}
geography={geo}
onMouseEnter={() => {
const { id, area_ha } = geo.properties;
setTooltipContent(`${id} — ${rounded(area_ha)}`);
}}
onMouseLeave={() => {
setTooltipContent("");
}}
style={{
default: {
fill: "#D6D6DA",
outline: "none"
},
hover: {
fill: "#F53",
outline: "none"
},
pressed: {
fill: "#E42",
outline: "none"
}
}}
/>
))
}
</Geographies>
</ZoomableGroup>
</ComposableMap>
</>
);
};
export default memo(MapChart);
I managed to solve it, the problem was in the project's SRC configuration, it was UTM and the correct is WGS 84, I changed, exported in GeoJSON format and converted to TopoJSON using Mapshaper, changed the projections and rotation and now everything is ok as you can see in CodeSandbox.
projection="geoAzimuthalEqualArea"
projectionConfig={{
rotate: [56.22, 13.66, 0],
scale: 360000
}}
I have a JSON array like below. I want to generate dynamic components using the array. I have an array inside the array and need to generate a checkbox dynamically inside a card.
export const DemoProductSpec =
{
"productSpecCharacteristic": [
{
"configurable": false,
"name": "testing",
"productSpecCharacteristicValue": [
{
"value": 2000
},
{
"value": 2002
},
{
"value": 2003
}
],
},
{
"configurable": false,
"name": "testing2",
"productSpecCharacteristicValue": [
{
"value": 20003
},
{
"value": 200233
},
{
"value": 200333
},
{
"value": 20034
}
],
},
]
}
My react code is as below. but this code is not working. first map function is working. but the second map is not working.
{DemoProductSpec.productSpecCharacteristic.map((Characteristic) => (
<Card
title={Characteristic.name}
bordered={true}
size="small"
extra={
<Radio.Group onChange={onChange} value={value}>
<Radio value={1}>Deafult</Radio>
<Radio value={2}>Overriden</Radio>
</Radio.Group>
}
>
{" "}
<span>Values: </span>
{Characteristic.productSpecCharacteristicValue.map((Values) => (<Checkbox>{Values.value}</Checkbox>))}
</Card>
))
}
I have to load previous chat messages first and i am getting response of messages like this
{
"id": "8102f902-d54b-457a-823e-13d3sssssds",
"buyerID": "4b358200-4da0-46a0-ad8b-838888888848",
"sellerID": "cf82474c-277b-483e-88ce-77777777777",
"text": "Hello, I would like to learn how to contour my cheeks.",
"messageType": "TEXT",
"createdAt": "2021-01-02T22:51:11.220Z",
"senderType": "BUYER",
"updatedAt": "2021-01-02T22:51:11.220Z"
},
{
"id": "08a8e684-2279-4cc1-9d2d-d5e4ebd9210a",
"buyerID": "4b358200-4da0-46a0-ad8b-838888888848",
"sellerID": "cf82474c-277b-483e-88ce-77777777777",
"text": "Sure how can I help contour?",
"messageType": "TEXT",
"createdAt": "2021-01-02T22:56:49.019Z",
"senderType": "SELLER",
"updatedAt": "2021-01-02T22:56:49.019Z"
},
{
"id": "67d75630-245a-46d4-9d33-8812d1e48b68",
"buyerID": "4b358200-4da0-46a0-ad8b-838888888848",
"sellerID": "cf82474c-277b-483e-88ce-77777777777",
"text": "Yo tell me how I can help",
"messageType": "TEXT",
"createdAt": "2021-01-02T23:04:39.893Z",
"senderType": "SELLER",
"updatedAt": "2021-01-02T23:04:39.893Z"
},
but all the messages are displaying on the left side of screen but i want buyer messages on the right side
I have including giftedChat component like this
<GiftedChat
messages={messages}
onSend={messages => onSend(messages)}
/>
because response doesn't have users avatar i also want to display avatar from custom url but that is also not working i am first time using gifted chat
can anybody help me how to do it ?
or any suggestions
Thanks in advance
I found solution for it
I changed the format of messages into required format of gifted chat it will not work fine until we modify our response into the gifted chat required format
Here it is what i did
let filteredChatMessages = yourMessages.map((chatMessage) => {
let modifiedMessages = {
_id: chatMessage.user_id,
text: chatMessage.text,// or chatMessage.message
createdAt: chatMessage.createdAt,
user: {
_id: chatMessage.id
avatar: chatMesssage.user_img_url
}
};
return modifiedMessages;
});
console.log(filteredChatMessage);
Then render bubble
// Render bubble
const renderBubble = props => {
const message_sender_id = props.currentMessage.user._id;
return (
<Bubble
{...props}
position={message_sender_id == currentLoggeduser ? 'right' : 'left'}
textStyle={{
right: {
color: COLORS.white,
fontSize: SIZES.s3
},
left: {
fontSize: SIZES.s3,
},
}}
wrapperStyle={{
right: {
backgroundColor: COLORS.appViolet,
marginRight: 5,
marginVertical: 5
},
left: {
marginVertical: 5
},
}}
/>
);
};
and finally render your gifted chat component
<GiftedChat
messages={messages}
onSend={messages => onSend(messages)}
renderBubble={renderBubble}
showUserAvatar={true}
scrollToBottom={true}
scrollToBottomComponent={renderScrollToBottom}
text={textMessage}
onInputTextChanged={text => setTextMessage(text)}
inverted={false}
/>
I have autocompleted features. when I search for something we will get the data accordingly. But I want to add one more feature like when we search data will come as well as whatever I typed in the search box that character should be highlighted in yellow color.
Here is the piece of code I have written.
import logo from './logo.svg';
import './App.css';
import React, { useState } from "react";
function App() {
const [names, setnames] = useState([{
"name": "Barbara-anne"
}, {
"name": "Debi"
}, {
"name": "Cara"
}, {
"name": "Cristin"
}, {
"name": "Jocelyne"
}, {
"name": "Joellyn"
}, {
"name": "Elmo"
}, {
"name": "Ivette"
}, {
"name": "Lea"
}, {
"name": "Michel"
}, {
"name": "Leigha"
}, {
"name": "Titus"
}, {
"name": "Nollie"
}, {
"name": "Celle"
}, {
"name": "Thea"
}, {
"name": "Brynn"
}, {
"name": "Sloane"
}, {
"name": "Margalo"
}, {
"name": "Genevieve"
}, {
"name": "Niel"
}, {
"name": "Heddi"
}, {
"name": "Gregg"
}, {
"name": "Eduard"
}, {
"name": "Kizzee"
}, {
"name": "Truman"
}, {
"name": "Merill"
}, {
"name": "Lindie"
}, {
"name": "Vasily"
}, {
"name": "Averil"
}, {
"name": "Golda"
}, {
"name": "Zorine"
}, {
"name": "Odele"
}, {
"name": "Amalie"
}, {
"name": "Ilsa"
}, {
"name": "Pepillo"
}, {
"name": "Hewe"
}, {
"name": "Byrann"
}, {
"name": "Alford"
}, {
"name": "Lanny"
}, {
"name": "Kristina"
}, {
"name": "Mar"
}, {
"name": "Vittoria"
}, {
"name": "Winslow"
}, {
"name": "Ashlan"
}, {
"name": "Gayelord"
}])
const [searchTerm, setSearchTerm] = useState('')
const filteredName=names.filter((val)=>{
if(searchTerm ===""){
return val;
}else if(val.name.toLowerCase().includes(searchTerm.toLowerCase())){
return val;
}
});
const renderStatementResult = searchTerm && searchTerm.length > 0;
return (
<>
<div className="srchField">
<label for="statement">Statement Name</label>
<div className="valueField">
<input type="text" name="fileName" id="statement" data-validate="true" placeholder="Type Name" onChange={event => {setSearchTerm(event.target.value)}}/>
{
renderStatementResult ? <ul className="lookup-results">
{filteredName.map((value)=>(<li key={value.name}>{value.name}</li>))}
</ul> : null
}
</div>
</div>
</>
);
}
export default App;
refer this image
Can anyone have an idea of how to match the highlighted text. I want to highlight all s in yellow color
Replace your map function with
filteredName.map((value) => {
let string = value.name.substr(
0,
value.name.toLowerCase().indexOf(searchTerm.toLowerCase())
);
let endString = value.name.substr(
value.name.toLowerCase().indexOf(searchTerm.toLowerCase()) +
searchTerm.length
);
let highlightedText = value.name.substr(
value.name.toLowerCase().indexOf(searchTerm.toLowerCase()),
searchTerm.length
);
return (
<li key={value.name}>
{string}
<span style={{ "background-color": "#FFFF00" }}>
{highlightedText}
</span>
{endString}
</li>
);
})
The first line( let string =...) extracts the part of string which comes before the part that should be highlighted, and the next line extracts the part after the highlight. The search term itself is kept in a span tag which is styled to highlight.
The above snippet only highlights the first occurrence of the search term, so if the search term is 's' and one of the names is 'Samson', only the first 's' would be highlighted. If you want to highlight all occurrences, then you can use regular expressions to find out the indices of all occurrences and loop across the indices, constructing your li tag along the way.
A working sandbox can be found at https://codesandbox.io/embed/elated-chandrasekhar-ggoi1?fontsize=14&hidenavigation=1&theme=dark
Try this
{filteredName.map((value)=>(<li key={value.name}>{
value.name.split('').map((char) => {
if (searchTerm.toLowerCase().split('').includes(char.toLowerCase())) {
return <span style={{ color: 'yellow' }}>{char}</span>;
} else {
return <span>{char}</span>;
}
})
}</li>))}