React-Material UI-D3 Promise.all does not show chart without double click on menu - react-d3

I am a newbie in React Router and State, i don't know why that my simple D3 Chart not show if i use condition/steps below
Load data on Apps using Promise.all and store in State (the reason i load data in here is because i try to avoid to load data on each chart page)
Call Dashboard page from App with Navigation
First time it will Load the Dashboard page with my BarChart
When i click on next menu to display the BarChart, it does not show
If i click again on the same menu, it will show again my BarChart
Is it because of Async function (Promise.all) ? How to resolve it?
alphabet.csv sample data
letter,frequency
A,0.08167
B,0.01492
C,0.02782
D,0.04253
E,0.12702
import React, { useState, useEffect } from 'react';
import * as d3 from 'd3';
import { Route, BrowserRouter as Router } from 'react-router-dom';
import Dashboard from './components/Dashboard';
import Dashboard1 from './components/Dashboard1';
export default function App() {
const [result, setResult] = useState([])
const mydata = () => {
const data = Promise.all([
d3.csv("alphabet.csv", d3.autoType),
]).then((result) => {
setResult([result]);
})
return data
}
useEffect(() => mydata(), []);
return (
<Router>
<Route path="/" exact render={(props) => <Dashboard {...props} data={result} />} />
<Route path="/Dashboard" render={(props) => <Dashboard {...props} data={result} />} />
<Route path="/Dashboard1" render={(props) => <Dashboard {...props} data={result} />} />
</Router>
)
}
import React from 'react';
import clsx from 'clsx';
import { makeStyles } from '#material-ui/core/styles';
import Container from '#material-ui/core/Container';
import Grid from '#material-ui/core/Grid';
import Paper from '#material-ui/core/Paper';
import Toolbar from '#material-ui/core/Toolbar';
import Navigation from './Navigation';
import BarChart from './BarChart';
const useStyles = makeStyles((theme) => ({
root: {
display: 'flex',
},
title: {
flexGrow: 1,
},
content: {
flexGrow: 1,
height: '100vh',
overflow: 'auto',
},
container: {
paddingLeft: theme.spacing(2),
paddingTop: theme.spacing(2),
paddingBottom: theme.spacing(2),
paddingRight: theme.spacing(2),
},
paper: {
padding: theme.spacing(2),
display: 'flex',
overflow: 'auto',
flexDirection: 'column',
},
fixedHeight: {
height: 350,
},
}));
export default function Dashboard(props) {
const classes = useStyles();
const fixedHeightPaper = clsx(classes.paper, classes.fixedHeight);
return (
<div className={classes.root}>
<Navigation />
<main className={classes.content}>
<Toolbar />
<div className={classes.appBarSpacer} />
<Container maxWidth="lg" className={classes.container}>
<Grid container spacing={3}>
<Grid item xs={12} md={8} lg={9}>
<Paper className={fixedHeightPaper}>
Dashboard<BarChart data={props.data} />
</Paper>
</Grid>
</Grid>
</Container>
</main>
</div>
)
}
import React from 'react';
import clsx from 'clsx';
import { Link } from 'react-router-dom';
import { makeStyles } from '#material-ui/core/styles';
import Drawer from '#material-ui/core/Drawer';
import AppBar from '#material-ui/core/AppBar';
import Toolbar from '#material-ui/core/Toolbar';
import List from '#material-ui/core/List';
import Typography from '#material-ui/core/Typography';
import Divider from '#material-ui/core/Divider';
import IconButton from '#material-ui/core/IconButton';
import ChevronLeftIcon from '#material-ui/icons/ChevronLeft';
import ListItem from '#material-ui/core/ListItem';
import ListItemIcon from '#material-ui/core/ListItemIcon';
import ListItemText from '#material-ui/core/ListItemText';
import DashboardIcon from '#material-ui/icons/Dashboard';
import BarChartIcon from '#material-ui/icons/BarChart';
const drawerWidth = 55;
const useStyles = makeStyles((theme) => ({
root: {
display: 'flex',
},
toolbar: {
paddingRight: 24, // keep right padding when drawer closed
},
toolbarIcon: {
display: 'flex',
alignItems: 'center',
justifyContent: 'flex-end',
padding: '0 8px',
...theme.mixins.toolbar,
},
appBar: {
zIndex: theme.zIndex.drawer + 1,
transition: theme.transitions.create(['width', 'margin'], {
easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.leavingScreen,
}),
},
menuButton: {
marginRight: 36,
},
menuButtonHidden: {
display: 'none',
},
title: {
flexGrow: 1,
},
drawerPaper: {
position: 'relative',
whiteSpace: 'nowrap',
width: drawerWidth,
transition: theme.transitions.create('width', {
easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.enteringScreen,
}),
},
drawerPaperClose: {
overflowX: 'hidden',
transition: theme.transitions.create('width', {
easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.leavingScreen,
}),
width: theme.spacing(7),
[theme.breakpoints.up('sm')]: {
width: theme.spacing(7),
},
},
appBarSpacer: theme.mixins.toolbar,
}));
export default function Navigation() {
const classes = useStyles();
return (
<div className={classes.root}>
<AppBar position="absolute" className={clsx(classes.appBar)}>
<Toolbar className={classes.toolbar}>
<Typography component="h1" variant="h6" color="inherit" noWrap className={classes.title}>
Testing
</Typography>
</Toolbar>
</AppBar>
<Drawer
variant="permanent"
classes={{
paper: clsx(classes.drawerPaper, classes.drawerPaperClose),
}}
>
<div className={classes.toolbarIcon}>
<IconButton>
<ChevronLeftIcon />
</IconButton>
</div>
<Divider />
<List>
<ListItem button>
<ListItemIcon>
<Link to='/Dashboard'><DashboardIcon /></Link>
</ListItemIcon>
<ListItemText primary="Dashboard" />
</ListItem>
<ListItem button>
<ListItemIcon>
<Link to='/Dashboard1'><BarChartIcon /></Link>
</ListItemIcon>
<ListItemText primary="Dashboard1" />
</ListItem>
</List>
</Drawer>
</div >
)
}
import React, { useRef } from 'react';
import * as d3 from 'd3';
export default function BarChart(props) {
const ref = useRef();
const pdata = props.data;
const margin = ({ top: 30, right: 30, bottom: 60, left: 100 });
const width = window.innerWidth - margin.right - margin.left;
const height = window.innerHeight - margin.top - margin.bottom + 100;
const color = "steelblue";
if (pdata.length > 0) {
const data = pdata[0];
const x = d3.scaleBand()
.domain(d3.range(data[0].length))
.range([margin.left, width - margin.right])
.padding(0.1);
const xAxis = g => g
.attr("transform", `translate(0,${height - margin.bottom})`)
.call(d3.axisBottom(x).tickFormat(i => data[0][i].letter).tickSizeOuter(0))
.attr('font-size', '18px')
.call(g => g.append("text")
.attr("x", width / 2)
.attr("y", margin.bottom)
.attr("fill", "currentColor")
.text("Letter"));
const y = d3.scaleLinear()
.domain([0, d3.max(data[0], d => d.frequency)]).nice()
.range([height - margin.bottom, margin.top]);
const yAxis = g => g
.attr("transform", `translate(${margin.left},0)`)
.call(d3.axisLeft(y).ticks(20, data.format))
.attr('font-size', '18px')
.call(g => g.append("text")
.attr("transform", "rotate(-90)")
.attr("x", -height / 2)
.attr("y", -margin.left)
.attr("fill", "currentColor")
.text("Frequency"));
const svg = d3.select(ref.current)
.attr("viewBox", [0, 0, width, height]);
svg.append("g")
.call(xAxis);
svg.append("g")
.call(yAxis);
svg.append("g")
.attr("fill", color)
.selectAll("rect")
.data(data[0])
.join("rect")
.attr("x", (d, i) => x(i))
.attr("y", d => y(d.frequency))
.attr("height", d => y(0) - y(d.frequency))
.attr("width", x.bandwidth());
}
return <svg ref={ref} />
}

Not sure if this was answered, but this line
setResult([result]);
could be the culprit. Is the result already an array? If yes, then just do
setResult(result);
because you might not be mapping over the correct data.

Related

RingBufferGeometry thetaLength animation with GSAP and react-three-fiber

I would like to animate my ringBufferGeometry like in example below, but I would like to use GSAP to animate this ringBufferGeometry for certain amount of time and I would like then to stop it when the thetaLength will equal to some value. Something like in example below but without the use of Math.sin().
three js RingBufferGeometry, update thetaLength
This is what I want to achieve(link below), but I want stop to animate when it reaches the full triangle form.
https://jsfiddle.net/02oyunbj/1/
My code
import React from 'react'
import ReactDOM from 'react-dom'
import App from './App'
function Container(props) {
return <div style={{ position: 'absolute', inset: 0 }} {...props} />
}
ReactDOM.render(
<Container>
<App />
</Container>,
document.getElementById('root')
)
import { useRef, useEffect } from 'react'
import { Canvas } from '#react-three/fiber'
import { OrbitControls } from '#react-three/drei'
import * as THREE from 'three'
import gsap from 'gsap'
export default function App() {
return (
<Canvas camera={{ position: [0, 0, 15] }}>
<color attach="background" args={['black']} />
<OrbitControls />
<spotLight position={[15, 15, 15]} angle={0.3} color={'white'} />
<TriangleRing />
</Canvas>
)
}
const TriangleRing = () => {
const triangleRingRef = useRef()
const parametersObj = {
innerRadius: 4,
outerRadius: 8,
thetaSegments: 1,
phiSegments: 30,
thetaStart: 1.55,
thetaLength: 0,
}
const parameters = [
parametersObj.innerRadius,
parametersObj.outerRadius,
parametersObj.thetaSegments,
parametersObj.phiSegments,
parametersObj.thetaStart,
parametersObj.thetaLength,
]
useEffect(() => {
console.log(triangleRingRef.current)
const triangleGeometry = triangleRingRef.current.geometry
gsap.set(triangleGeometry, {
parameters: { ...parametersObj, thetaLength: 0 },
})
const tl = gsap.timeline({ defaults: { ease: 'power3.inOut' } })
tl.to(triangleGeometry, {
parameters: { ...parametersObj, thetaLength: 6.3 },
duration: 4,
})
})
return (
<mesh ref={triangleRingRef}>
<ringBufferGeometry args={parameters} name="TriangleRing" />
<meshToonMaterial side={THREE.DoubleSide} color={0xffffff} />
</mesh>
)
}
Link to sandbox:
https://codesandbox.io/s/affectionate-lichterman-edpfr?file=/src/App.jsx:0-1517
By default GSAP works in terms of seconds, not velocity or anything like that. So you'll need to calculate how long the tween(s) should run (by using the difference in value and the rate at which you want it to move) to get it to result in the end value that you need.
Without a minimal, complete, and verifiable example it's hard for us to help more at this point.

Using Vue.js 3 component dynamically in the D3.js v7

im using the Vue.js 3 and D3.js v7 to making the flowchart.
My problem here is I can't dynamically append the component inside the D3.js.
My component is imported like shown below.
components: {
StoryPanel: defineAsyncComponent(() => import("#/Pages/Story/Partials/StoryPanel"))
},
let nodes = container.selectAll("g")
.data(root.descendants())
.join("g")
.append("foreignObject")
.attr("width", entityWidth - 10)
.attr("height", 150)
nodes.append("xhtml:div")
.attr("class", "border border-black")
.html(function (d) {
// return '<StoryPanel />' tried this but not working
})
.style("border", '1px solid black')
This is the generated html
<foreignObject width="190" height="150" transform="translate(-95,10)">
<div class="border border-black" style="border: 1px solid black;">
<storypanel></storypanel>
</div>
</foreignObject>
[Edit 1]
Tried this Rendering Vue 3 component into HTML string as #MichalLevĂ˝ suggested, but still not working
.html(function (d) {
const tempApp = createApp({
render() {
return h(StoryPanel,{step: d})
}
})
const el = document.createElement('div');
const mountedApp = tempApp.mount(el)
return mountedApp.$el.outerHTML
})
[Edit 2]
I found it works only when using const instead of a Component.vue
const CircleWithTextSvg = {
name: 'CircleWithTextSvg',
props: {
text: {
type: String,
default: "1"
},
fill: {
type: String,
default: "white"
}
},
template: `<div>template</div>`
}
Any help is appreciated. Thank you.
Use createApp
import {createApp} from 'vue';
and use this instead and return the outerHTML
const app = createApp(Component, {prop1: this.prop1})
.mount(document.createElement('div'))
return app.$el.outerHTML

Image not showing in React Native Web App. Appears when I build for Android or iOS but doesn't show when using react-scripts start

I am trying to display an image in a React Native Web App which is run using react-scripts start. When I build the App for iOS or Android, the image appears perfectly fine (using expo) but when I build it for the Web App, the image fails to load. Here is the code snippet for the Home component where the image is loaded
import React from "react";
import { ScrollView, ActivityIndicator, StyleSheet, Image, ImageBackground } from "react-native";
import UserList from "./user-list";
import Header from './header';
import sanityClient from './assets/client'
import BackButton from './back-button'
import User from './user'
// import {Asset} from 'expo-asset';
// const imageURI = Asset.fromModule(require('./arrow.png')).uri;
const image = require('./assets/aoeu.jpg');
class Home extends React.Component {
state = {
user: {},
loading: true
};
componentDidMount() {
// TODO: get users
this.getUser();
}
async getUser() {
sanityClient.fetch(`*[ _type == "user" && emailAddress.current == "dwight#viamaven.com"]`)
.then((data) => {
console.log(data);
this.setState({user: data[0], loading: false});
console.log(this.state.user);
})
.catch((err) => console.error(err))
// const res = await fetch("https://randomuser.me/api/?results=20");
// const { results} = await res.json();
// // console.log(results)
// this.setState({users: [...results], loading: false});
}
render() {
return (
<ScrollView
noSpacer={true}
noScroll={true}
style={styles.container}
showVerticalSCrollIndicator = {false}
showHorizontalScrollIndicator = {false}
>
{this.state.loading ? (
<ActivityIndicator
style={[styles.centering, styles.gray]}
color="#5d38aa"
size="large"
/>
) : (
<View>
<Header title={this.state.user.name} />
<View>
<Image
source={require('./arrow.png')}
style={styles.image}
/>
</View>
<User />
</View>
)}
</ScrollView>
);
}
}
var styles = StyleSheet.create({
container: {
backgroundColor: "white",
width: '375px',
height: '812px',
// top: '50px',
},
centering: {
alignItems: "center",
justifyContent: "center",
padding: 8,
height: '100vh'
},
image: {
width: '50px',
height: '50px',
marginRight: 20,
boxShadow: "0 1px 2px 0 rgba(0,0,0,0.1)"
}
});
export default Home;
Here is a link to the GitHub repo where the entire project is stored https://github.com/nimbusdin/stackreactnative
Try to import this way and use it like this
import image = './assets/aoeu.jpg';
<Image
source={image}
style={styles.image}
/>

D3, VueJS, axes of chart not rendering

I am trying to add D3 histogram to a VueJS component and I can't get the axes to appear correctly. The only thing I see rendered is the tip of the y-axis.
here is my js for the chart component:
export default {
name: "Histogram",
props: {
},
mounted() {
this.generateBars();
},
data: () => ({
title: 'Bar Chart'
}),
methods: {
generateBars() {
const sample = [
{
language: 'Rust',
value: 78.9,
color: '#000000'
},
...
{
language: 'Clojure',
value: 59.6,
color: '#507dca'
}
];
const margin = 60
const width = 1000 - 2 * margin;
const height = 600 - 2 * margin;
const svg = d3.select("svg");
const chart = svg.append('g')
.attr('transform', `translate(${margin}, ${margin})`);
const yScale = d3.scaleLinear()
.range([height, 0])
.domain([0, 100]);
chart.append('g')
.call(d3.axisLeft(yScale));
const xScale = d3.scaleBand()
.range([0, width])
.domain(this.sample.map((s) => s.language))
.padding(0.2)
chart.append('g')
.attr('transform', `translate(0, ${height})`)
.call(d3.axisBottom(xScale));
}
}
};
My template for the component is simply the chart itself:
<template>
<div id="container" class="svg-container" align="center">
<h3>{{ title }}</h3>
<svg />
</div>
</template>
Here is what I see:
After further examination, I think the footer is covering the bottom of the chart. Not sure why it does not move down to accommodate the additional content.
Looks like you forgot to set width and height for svg (via html or js).
<svg width="1000" height="600"></svg>
And remove "this" in line:
.domain(this.sample.map((s) => s.language))

How to get data from custom component?

I have an edit action inside my model file. I am using default react-admin components, unfortunately I had to created my custom component and after the form is submitted no data from this custom component were provided.
I have tried to wrap the whole component inside <FormControl> , it does not do the trick for me.
My component:
import React from 'react';
import PropTypes from 'prop-types';
import { withStyles } from '#material-ui/core/styles';
import Input from '#material-ui/core/Input';
import InputLabel from '#material-ui/core/InputLabel';
import MenuItem from '#material-ui/core/MenuItem';
import FormControl from '#material-ui/core/FormControl';
import Select from '#material-ui/core/Select';
import Chip from '#material-ui/core/Chip';
import { fetchUtils } from 'react-admin';
const styles = theme => ({
root: {
display: 'flex',
flexWrap: 'wrap',
},
formControl: {
margin: theme.spacing.unit,
minWidth: 120,
maxWidth: 300,
},
chips: {
display: 'flex',
flexWrap: 'wrap',
},
chip: {
margin: theme.spacing.unit / 4,
},
noLabel: {
marginTop: theme.spacing.unit * 3,
},
});
const ITEM_HEIGHT = 48;
const ITEM_PADDING_TOP = 8;
const MenuProps = {
PaperProps: {
style: {
maxHeight: ITEM_HEIGHT * 4.5 + ITEM_PADDING_TOP,
width: 250,
},
},
};
function getStyles(name, that) {
return {
fontWeight:
that.state.name.indexOf(name) === -1
? that.props.theme.typography.fontWeightRegular
: that.props.theme.typography.fontWeightMedium,
};
}
var names = [];
class MultipleSelect extends React.Component {
state = {
name: [
]
};
getRoles() {
const url = 'URLTOENDPOINT'
var data = [];
fetchUtils.fetchJson(url, {
method: "GET",
})
.then(response => {
Object.keys(response.json.value).forEach(function (key) {
var object = response.json.value[key];
data.push({
name: object.Name,
value: object.Id
});
})
this.setState({name: data});
});
}
getAllOptions() {
const url = 'URLTOENDPOINT'
var data = [];
fetchUtils.fetchJson(url, {
method: "GET",
})
.then(response => {
Object.keys(response.json.value).forEach(function (key) {
var object = response.json.value[key];
data.push({
name: object.Name,
value: object.Id
});
})
names = data;
});
}
componentDidMount() {
this.getRoles();
this.getAllOptions();
this.forceUpdate();
}
handleChange = event => {
console.log("y",event);
this.setState({ name: event.target.value });
};
render() {
const { classes } = this.props;
return (
<div>
<FormControl>
<InputLabel htmlFor="UserRoles">Chip</InputLabel>
<Select
multiple
value={this.state.name}
onChange={this.handleChange}
input={<Input id="UserRoles" />}
renderValue={selected => (
<div className={classes.chips}>
{selected.map(obj => (
<Chip key={obj.value} label={obj.name} className={classes.chip} />
))}
</div>
)}
MenuProps={MenuProps}
>
{names.map(obj => (
<MenuItem key={obj.value} value={obj.value} style={getStyles(obj.name, this)}>
{obj.name}
</MenuItem>
))}
</Select>
</FormControl>
</div>
);
}
}
MultipleSelect.propTypes = {
classes: PropTypes.object.isRequired,
};
export default withStyles(styles, { withTheme: true })(MultipleSelect);
EditAction:
export const UserEdit = props => (
<Edit {...props} title={<UserTitle/>} aside={<Aside />}>
<SimpleForm>
<DisabledInput source="Id" />
<TextInput source="Login" validate={required()} />
<TextInput source="Email" type="email" validate={required()} />
<ReferrenceSelectBox source="UserRoles" />
<NullableBooleanInput source="Active" />
<DateField source="CreatedDate" showTime
locales={process.env.REACT_APP_LOCALE}
disabled={true} />
</SimpleForm>
</Edit>
);
I need to show multiple select box with selected data from API, so i had wrote custom component, because default components did not help me, but it didnt change anything and also the data are not showing.

Resources