Kendo React save chart as image - reactjs

I'm having a problem about the download of the kendo react chart as an image,
currently the download works but only for my latest chart (I have six of them)
I've recreated the error in stackblitz
As you can see whenever I try to download one of the 2 charts the downloaded one is always the latest one
Is there any way for fixing this?

The problem is that refContainer is being set twice inside your App component in the example you linked. One time for each of your charts. The reference will always refer to the second chart, because the second chart overwrites the value of refContainer last.
What you can do instead is to create a CustomChart component that holds its own ref (refContainer). This way you can render multiple instances of this component, without the refs clashing. This also allows us to get rid of some duplicate code for creating the chart.
So you can do something like this:
import * as React from "react";
import {
Chart,
ChartSeries,
ChartSeriesItem,
ChartCategoryAxis,
ChartCategoryAxisItem,
exportVisual,
} from "#progress/kendo-react-charts";
import { exportImage } from "#progress/kendo-drawing";
import { saveAs } from "#progress/kendo-file-saver";
const CustomChart = ({ categories, data }) => {
let refContainer = React.useRef(null);
const onExportVisual = () => {
const chartVisual = exportVisual(refContainer);
if (chartVisual) {
exportImage(chartVisual).then((dataURI) => saveAs(dataURI, "chart.png"));
}
};
return (
<>
<button onClick={() => onExportVisual()}>Export as visual element</button>
<Chart ref={(chart) => (refContainer = chart)}>
<ChartCategoryAxis>
<ChartCategoryAxisItem categories={categories} />
</ChartCategoryAxis>
<ChartSeries>
<ChartSeriesItem data={data} />
</ChartSeries>
</Chart>
</>
);
};
const App = () => {
return (
<div>
<CustomChart
categories={[2015, 2016, 2017, 2018]}
data={[10, 100, 100, 10]}
/>
<CustomChart
categories={[2015, 2016, 2017, 2018]}
data={[100, 10, 10, 100]}
/>
</div>
);
};
export default App;

Related

Change Material of a babylonJs model with react-babylonjs

I load a model coming from Blender (exported with babylon js exporter). The model comes with materials. and has 5 meshes (very simple test model).
I would like to change albedo (color under natural light) of some materials, but don't get how to do, as there is no component related to the material (because imported) and in react, there is usually a function to call to update internal values (then a refresh is triggered).
const onModelLoaded = model => {
model.meshes.forEach(mesh => {
console.log(`mesh... `, mesh.material.albedoColor);
// It shows well albedo of each material
});
};
export const SceneWithLoad = () => {
return (
<div>
<Engine antialias adaptToDeviceRatio canvasId="babylonJS">
<Scene>
<Suspense>
<Model
rootUrl="assets/firstLoco.babylon"
sceneFileName=""
onModelLoaded={onModelLoaded}
/>
</Suspense>
<hemisphericLight ... />
<arcRotateCamera ... />
</Scene>
</Engine>
</div>
);
};
When mesh is loaded, I can see albedo of each material with onModelLoaded (that's great), now I would like to update albedo on a regular basis (setInterval(() => {changeAlbedo()}, 1000)), but ref to Material objects change on refresh, and I need to call a function for react to know code updated the material albedo.
Cant find the trick here, Thanks for advices !
Following numerous tests
Here the ids of the materials are known. They are used to set albedo on them through the lodash filter function. In this case, it alternates red / white lights in front / back of a locomotive.
boolBal is a value set to true or false every sec. SwapMaterial doesn't display anything and is simply an entry point for the scene modification code.
What is not really "react way of working" is that scene are mutable (you can update a scene item without generating a new reference), react in principle is not (state change = new ref to the state object)
Any better suggestion please comment.
import _ from "lodash";
import { Engine, Scene, Model, useScene } from "react-babylonjs";
import { Vector3, Color3 } from "#babylonjs/core";
import "#babylonjs/loaders";
const colorWhite = new Color3(1, 1, 1);
const colorRed = new Color3(1, 0, 0);
const SwapMaterial = ({ boolVal }) => {
const scene = useScene();
_.filter(
scene.materials,
r => ["FrontLeft-mat", "FrontRight-mat"].indexOf(r.id) >= 0
).forEach(m => {
m.albedoColor = boolVal ? colorRed : colorWhite;
});
_.filter(
scene.materials,
r => ["RearLeft-mat", "RearRight-mat"].indexOf(r.id) >= 0
).forEach(m => {
m.albedoColor = !boolVal ? colorRed : colorWhite;
});
return null;
};
export const SceneWithLoad = () => {
const [boolVal, boolValSet] = useState(false);
const ref = useRef({});
ref.current.boolVal = boolVal;
ref.current.boolValSet = boolValSet;
useEffect(() => {
const intId = setInterval(() => {
ref.current.boolValSet(!ref.current.boolVal);
}, 1000);
return () => {
clearInterval(intId);
};
}, []);
return (
<div>
<Engine antialias adaptToDeviceRatio canvasId="babylonJS">
<Scene>
<Suspense>
<Model rootUrl="assets/firstLoco.babylon" sceneFileName="" />
</Suspense>
<hemisphericLight ... />
<hemisphericLight ... />
<arcRotateCamera ... />
<SwapMaterial {...{ boolVal }} />
</Scene>
</Engine>
</div>
);
};

How can I display the newly updated variable currentPaginationData from the updateRowsPerPage function?

import BlogPost from "./BlogPost";
import Pagination from "./Pagination";
import React from "react";
import blogs from "../data/blogs.json";
const PAGE_SIZES = [15, 25, 50, 100];
function BlogList() {
let currentPaginationData = blogs.posts.slice(0, 15);
const updateRowsPerPage = (numPerPage) => {
currentPaginationData = blogs.posts.slice(0, numPerPage);
console.log(currentPaginationData);
};
const updatePage = () => {};
return (
<div>
<Pagination
currentPage={1}
totalCount={blogs.posts.length}
pageSize={15}
pageSizeOptions={PAGE_SIZES}
onPageChange={updatePage}
onPageSizeOptionChange={updateRowsPerPage}
/>
<ul
// Do not remove the aria-label below, it is used for Hatchways automation.
aria-label="blog list"
>
{currentPaginationData.map((blog) => (
<BlogPost
key={blog.id}
author={blog.author}
title={blog.title}
excerpt={blog.excerpt}
featureImage={blog.image}
/>
))}
</ul>
</div>
);
}
export default BlogList;
This jsx file will display the first 15 users blogs on start up and my updateRowsPerPage function updates the currentPaginationData successfully. I checked with the console.log statement. However, my DOM doesn't seem to be updating and showing the new updated value assigned to currentPaginationData and I can't figure out why.
Can anyone guide me in the right direction? Thanks in advance.
You Need To Store the Current Pagination Data in the State Variable and Update it with the SetState in the Update Rows Per Page..
const [currentPaginationData,setCurrentPaginationData] = useState(blogs.posts.slice(0, 15))
const updateRowsPerPage = (numPerPage) => {
let data = blogs.posts.slice(0, numPerPage);
setCurrentPaginationData(data)
};

How to export a component to a PDF file, that is not visible on UI, but has to be in PDF document (html-to-image, jsPDF, React)

Like the title says, I want to export a component to a PDF file, that I want to be invisible in the app or should I say on UI, but I want it to be inside a PDF document.
To make this PDF exporting functionality I have used the combination of html-to-image library, jsPDF library and everything is made using React.
This is my code:
function App() {
const [exporting, setExporting] = useState(false);
async function createPdf({ doc, element }) {
const imgData = await toPng(element);
const imgProps = doc.getImageProperties(imgData);
const pdfWidth = doc.internal.pageSize.getWidth();
const pdfHeight = (imgProps.height * pdfWidth) / imgProps.width;
doc.addImage(imgData, "PNG", 10, 0, pdfWidth, pdfHeight, "", "FAST");
}
async function handleDownloadPdf() {
const element = document.getElementsByClassName("container")[0];
const doc = new jsPDF(
"p",
"px",
[element.clientWidth, element.clientHeight],
true
);
setExporting(true);
await createPdf({ doc, element });
doc.save(`charts.pdf`);
}
return (
<pdfContext.Provider value={{ exporting, setExporting }}>
<div className="App">
<button onClick={handleDownloadPdf}>Test</button>
<div className="container">
<Hidden />
<Foo />
</div>
</div>
</pdfContext.Provider>
);
}
export default App;
The component that I want to be hidden is <Hidden />, this is a simple component but let me show the code anyways:
const Hidden = () => {
const { exporting, setExporting } = useContext(pdfContext);
return (
<div
className="elementOne"
style={{ visibility: exporting ? "visible" : "hidden" }}
>
</div>
);
};
export default Hidden;
As you can see I want to use the context called pdfContext that sets the visibility of a component to hidden when the component is not being exported, and to visible when it's being exported, but this way is not really a good solution, as the component gets visible for a split second before exporting and in my opinion it's not a good design.
So if anyone has any solution or a workaround on how to export a component to a PDF using these libraries, but without showing it on a UI, that would be great.
I know that the way these components are being exported to a PDF is by converting the container to an image, and probably the way I am asking to do this is maybe impossible but then again it does not hurt to ask.

Match background with users current weather conditions

I am new to React, trying to learn and I have this unsolvable problem. I have developed a weather app, I'm still working on it, but at this moment I am stuck for 3 days trying to have a background image that changes depending on the users weather conditions. I have tried something using the icon, from openweather API. I used the same method to get the icon (image from my folder) to match users weather conditions.
import React from "react";
export default function Background(props) {
const codeMapping = {
"01d": "clear-sky-day",
"01n": "clear-sky-night",
"02d": "cloudy-day",
"02n": "cloudy-night",
"03d": "cloudy-day",
"03n": "cloudy-night",
"04d": "cloudy-day",
"04n": "cloudy-night",
"09d": "shower-rain-day",
"09n": "shower-rain-night",
"10d": "rain-day",
"10n": "rain-night",
"11d": "thunderstorm-day",
"11n": "thunderstorm-night",
"13d": "snow-day",
"13n": "snow-night",
"50d": "fog-day",
"50n": "fog-night",
};
let name = codeMapping[props.code];
return (
<img
className="background"
src={`background/${name}.jpg`}
alt={props.alt}
size="cover"
/>
);
}
So... in order to get "icon" of the input city by the user I have to call "<Background cod={weatherData.icon} alt={weatherData.description} />" from the function "Search" which is the function handling the submit form and running api call for input city. But the image is not showing(img1), but to have the img as a background I would call <Background> from my App function(img2), but in this case I will not have access to the real icon value from the input city. I should mention I have a folder in "src" called background and the images names match the codes name from the mapping.
Thank you in advance!
current preview of my app
how I see in other documentation I should set a background
You can pass the code from Search.js as the state.
App.js
const codeMapping = {
"01d": "clear-sky-day",
"01n": "clear-sky-night",
};
export const App = () => {
const [code, setCode] = useState(null) // <-- We'll update this from Search.js
const [backgroundImage, setBackgroundImage] = useState("")
useEffect(() => {
// Set background value based on the code
setBackgroundImage(codeMapping[`${code}`])
}, [code]); // <-- useEffect will run everytime the code changes
return (
<div style={{
height: '100px',
width: '100px',
backgroundImage: `${backgroundImage || "defaultBackgroundImage"}`
}}>
<Search setCode={setCode} />
</div>
)
}
Search.js
import { WeatherContext } from './App';
export const Search = ({ setCode }) => {
const handleClick = (apiResponse) => {
// Some API call returning the actual code value here //
setCode(apiResponse)
}
return (
<input
onClick={() => handleClick("01n")}
type="button"
value="Change city"
/>
)
}

Inject Props to React Component

For security reasons, I have to update ant design in my codebase from version 3 to 4.
Previously, this is how I use the icon:
import { Icon } from 'antd';
const Demo = () => (
<div>
<Icon type="smile" />
</div>
);
Since my codebase is relatively big and every single page use Icon, I made a global function getIcon(type) that returns <Icon type={type}>, and I just have to call it whenever I need an Icon.
But starting from antd 4, we have to import Icon we want to use like this:
import { SmileOutlined } from '#ant-design/icons';
const Demo = () => (
<div>
<SmileOutlined />
</div>
);
And yes! Now my getIcon() is not working, I can't pass the type parameter directly.
I tried to import every icon I need and put them inside an object, and call them when I need them. Here's the code:
import {
QuestionCircleTwoTone,
DeleteOutlined,
EditTwoTone
} from '#ant-design/icons';
let icons = {
'notFound': <QuestionCircleTwoTone/>,
'edit': <EditTwoTone/>,
'delete': <DeleteOutlined/>,
}
export const getIcon = (
someParam: any
) => {
let icon = icons[type] !== undefined ? icons[type] : icons['notFound'];
return (
icon
);
};
My problem is: I want to put someParam to the Icon Component, how can I do that?
Or, is there any proper solution to solve my problem?
Thanks~
You can pass props as follows in the icons Object:
let icons = {
'notFound':(props:any)=> <QuestionCircleTwoTone {...props}/>,
'edit': (props:any)=><EditTwoTone {...props}/>,
'delete':(props:any)=> <DeleteOutlined {...props}/>,
}
And then if you will pass any prop to the Icon component then it will pass the prop to the specific icon component
let Icon = icons[type] !== undefined ? icons[type] : icons['notFound'];
return (<Icon someParam={'c'}/>)

Resources