I want to apply an option where user can switch between dark mode and light mode in the application.
<link rel="stylesheet" href="../src/cssf/light.css">
<link rel="stylesheet" href="../src/cssf/dark.css">
I have two sheets for the whole website.
<label class="form-check-label" id="dark">
<input type="radio" class="form-check-input" checked name="theme"><label>Dark</label>
</label>
</div>
<div class="form-check-inline">
<label class="form-check-label" id="light">
<input type="radio" class="form-check-input" name="theme"><label>Light</label>
</label>
I have given the option but what do i have to do to switch between the two css files?
import React, { useEffect, useState } from "react";
import "./cssf/style.css";
import logo from "./cssf/logo-sm.png";
function App() {
const [ stylePath, setStylePath ] = useState("./cssf/dark-theme.css");
const handleButtonClick = () => {
setStylePath("./cssf/light-theme.css");
}
useEffect(() => {
var head = document.head;
var link = document.createElement("link");
link.type = "text/css";
link.rel = "stylesheet";
link.href = stylePath;
head.appendChild(link);
return () => { head.removeChild(link); }
}, [stylePath]);
I used this method and it is updating the link tag perfectly in the head but without importing it to my app using import "../cssf/sheername.css" it is of no use.How can i solve it?
that’s quite an interesting issue.
For dynamically importing css files into react I’d check this thread: here
However I don’t think this is the best solution, as it is potentially very hard to maintain and not very DRY.
I would rather have 1 css file that looks at the class on body and changes css colors based on that (assuming you don’t change layout, only colors)
Related
I am making a react application where I am in the need to generate a pdf from specific div.
Current scenario:
-> Made an app with a printable div and upon click button, the pdf is generated and downloaded.
Code:
<div id="printable-div">
<h1>Generate PDF</h1>
<p>Create a screenshot from this div, and make it as a PDF file.</p>
<p style={{ color: "red" }}>
*Then do not download instead attach to contact us form as attachment.
</p>
</div>
<button id="print" onClick={printPDF}>
Contact Us
</button>
printPDF Function:
const printPDF = () => {
setIsShowContact(true);
const domElement = document.getElementById("printable-div");
html2canvas(domElement).then((canvas) => {
const doc = new jsPdf();
doc.save(`attachment.pdf`);
});
};
On click of the Contact Us button, two actions happen.
-> Pdf file is downloaded.
-> A form with inputs Name, Email and Attachment will be shown.
Working Codesandbox:
Requirement:
Here the requirement is onclick of the Contact Us button, the pdf should be generated but not downloadable instead the generate pdf needs to be attached to the input type="file" in the contact form.
We need to send information of the data user have right now in the specific div id="printable-div" to backend api as pdf attachment on click of the contact button.
In real application, this is like an enquiry of a product, where user
selects a product with some config's and finally that product info
will be shown to user based on the config they choosen. Then the user will
click Contact Us button, so the printable-div will have that product
information which user searched, so we need to capture it as pdf and
attach to input and send to backend on form submission.
Kindly help me with the inputs on achieving this scenario of making the generated pdf to attach as attachment to the input field.
The problem
1. You need to convert the PDF correctly, although the intention is to attach the PDF in the input field, you are downloading a blank PDF.
The first step would be to download the PDF correctly, referring to the DIV element whose id is printable-div, and after that, instead of downloading, attach it to the input field.
The invalid code is here:
const printPDF = () => {
setIsShowContact(true);
const domElement = document.getElementById("printable-div");
html2canvas(domElement).then((canvas) => {
const doc = new jsPdf(); <<- YOU`RE CREATING AN EMPTY PDF AND
doc.save('attachment.pdf'); <<- DOWNLOADING THIS EMPTY PDF
});
};
The solution is very simple, just use the argument
canvas passed to the callback function instead of generating a new PDF
2. You need to append the .files property and not add
instead the generate pdf needs to be attached to the input type="file" in the contact form.
It is impossible to add new items to the .files of input[type="file"] field that belongs to the FileList class, on the other hand, it is possible to change it, that is, remove the old FileList and attach a new one with the necessary file(s).
Which in this example would just be a single file.
The solution
1. You need to convert the canvas that was passed as a callback from the html2canvas function to a file.
You can do this in the following way:
const canvas2file = (canvas, fileName = 't.jpg') =>
new Promise(resolve => {
canvas.toBlob((blob) => {
resolve(new File([blob], fileName, { type: "image/jpeg" }))
}, 'image/jpeg');
})
2. You need to use this function in the promise that is expected by the html2canvas function, that is:
html2canvas(domElement)
.then(canvas2file)
3. You will need to get a reference (or document.querySelector / document.getElementXXX) to the input field whose type is file, and also a state variable for the file itself that was converted previously (by the canvas2file function), that is:
function App() {
...
const fileRef = useRef(); //Reference to input[type="file"]
const [file, setFile] = useState(); //State variable that contains the File
...
}
4. Modify the printPDF function to save the File to the state variable
const printPDF = () => {
setIsShowContact(true);
const domElement = document.getElementById("printable-div");
html2canvas(domElement)
.then(canvas2file)
.then(setFile);
};
5. Use the useEffect hook to detect the change of the File in the state variable, that is, every time the user clicks on "Contact Us", a new File will be generated through the canvas2file function, and this file will be stored in the file state variable.
After detecting this change, we remove the .files (of type FileList) from the input[type="file"] and we will re-attach a new FileList to the input, example:
useEffect(() => {
if(!fileRef.current) return;
let list = new DataTransfer();
list.items.add(file);
fileRef.current.files = list.files;
console.log(fileRef.current)
}, [file])
The code
const { useEffect, useRef, useState } = React;
const canvas2file = (canvas, fileName = 't.jpg') =>
new Promise(resolve => {
canvas.toBlob((blob) => {
resolve(new File([blob], fileName, { type: "image/jpeg" }))
}, 'image/jpeg');
})
function App() {
const [isShowContact, setIsShowContact] = useState(false);
const fileRef = useRef();
const [file, setFile] = useState();
useEffect(() => {
if(!fileRef.current) return;
let list = new DataTransfer();
list.items.add(file);
fileRef.current.files = list.files;
console.log(fileRef.current)
}, [file])
const printPDF = () => {
setIsShowContact(true);
const domElement = document.getElementById("printable-div");
html2canvas(domElement)
.then(canvas2file)
.then(setFile);
};
return (
<div className="App">
<div id="printable-div">
<h1>Generate PDF</h1>
<p>Create a screenshot from this div, and make it as a PDF file.</p>
<p style={{ color: "red" }}>
*Then do not download instead attach to contact us form as attachment.
</p>
</div>
<br />
<button id="print" onClick={printPDF}>
Contact Us
</button>
<br />
<br />
<br />
{isShowContact && (
<form>
<div id="contact">
<div className="block">
<label>Name:</label>
<input type="text" defaultValue="John Doe" />
</div>
<br />
<div className="block">
<label>Email:</label>
<input type="email" defaultValue="xyz#example.com" />
</div>
<br />
<div className="block">
<label>Table pdf as attachment:</label>
<input ref={fileRef} type="file" />
</div>
</div>
</form>
)}
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
label {
display: inline-block;
width: 75x;
text-align: right;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="theme-color" content="#000000">
<script src="https://unpkg.com/react#18/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom#18/umd/react-dom.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js"></script>
<title>React App</title>
</head>
<body>
<noscript>
You need to enable JavaScript to run this app.
</noscript>
<div id="root"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
</body>
</html>
Try this
Due to Stackoverflow Sandbox policies, the code above will probably not run, for this reason I am hosting the working code on Codesandbox.
Demo URL: https://codesandbox.io/s/html2canvas-jspdf-forked-5ennvt?file=/src/index.js
You cannot set the value of a file input unfortunately, even if you give it a "file" yourself. You will have to intercept the form submission and construct your own request while adding the attachment. FormData API is useful for that.
Here's the link to the updated sandbox: https://codesandbox.io/s/html2canvas-jspdf-forked-eun59q?file=/src/index.js
And here's the used jsPDF function docs: https://artskydj.github.io/jsPDF/docs/jsPDF.html#output
From what I understand, you're essentially trying to take a screenshot of an area and send that in the form data.
You can use a library called use-react-screenshot. It's really easy to implement and you'll need to set the reference area using the createRef hook from react.
Once you've got the screenshot, you can silently upload that somewhere to a database, alongside that form data, so that you can reference the screenshot when looking at the form data.
With the current method, I don't think there is a way to set the file input programmatically.
I hope this helps.
I'm trying to import this Widget designed for JQuery in my React projet :
<script type="text/javascript" src="https://widget.mondialrelay.com/parcelshop-picker/jquery.plugin.mondialrelay.parcelshoppicker.min.js">
</script>
This widget needs JQuery to work, so i also load JQuery using Google CDN:
<script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/jquery/2.2.4/jquery.min.js"></script>
The documentation is written for JQuery, and shows this example to use the widget :
HTML
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="fr" lang="fr" dir="ltr">
<head>
<title>Mondial Relay Parcelshop Picker with Map</title>
<!-- JQuery required (>1.6.4) -->
<script src="//ajax.googleapis.com/ajax/libs/jquery/2.2.4/jquery.min.js"></script>
<!-- Leaflet dependency is not required since it is loaded by the plugin -->
<script src="//unpkg.com/leaflet/dist/leaflet.js"></script>
<link rel="stylesheet" type="text/css" href="//unpkg.com/leaflet/dist/leaflet.css" />
<!-- JS plugin for the Parcelshop Picker Widget MR using JQuery -->
<script src="//widget.mondialrelay.com/parcelshop-picker/jquery.plugin.mondialrelay.parcelshoppicker.min.js"></script>
</head>
<body>
<!-- HTML Element in which the Parcelshop Picker Widget is loaded -->
<div id="Zone_Widget"></div>
<!-- HTML element to display the parcelshop selected, for demo only. Should use hidden instead. -->
<input type="text" id="Target_Widget" />
</body>
</html>
JAVASCRIPT :
```
$(document).ready(function () {
$("#Zone_Widget").MR_ParcelShopPicker({
Target: "#Retour_Widget",
Brand: "BDTEST ",
Country: "FR",
EnableGmap: true
});
});
```
I really don't know how to do it. I've tried for 3 days but i'm still blocked.
Thank you a lot for your help and i'm very sorry for my bad english.
Thanks again.
(UPDATE)
My code is :
import React, { useEffect } from 'react';
const MondialRelay = () => {
useEffect(() => {
const jQuery = document.createElement('script');
jQuery.src = "//ajax.googleapis.com/ajax/libs/jquery/2.2.4/jquery.min.js";
jQuery.async = "true";
document.body.appendChild(jQuery);
return () => {
document.body.removeChild(jQuery);
}
})
useEffect(() => {
const scriptMr = document.createElement('script');
scriptMr.src = "https://widget.mondialrelay.com/parcelshop-picker/jquery.plugin.mondialrelay.parcelshoppicker.min.js";
scriptMr.async = "true";
document.body.appendChild(scriptMr);
return () => {
document.body.removeChild(scriptMr);
}
});
!function(){
// Parameterized the widget
$(document).ready(function () {
$("#Zone_Widget").MR_ParcelShopPicker({
Target: "#ParcelShopCode",
Brand: "BDTEST ",
Country: "FR"
});
});
}();
return(
<div>
<div id="Zone_Widget"></div>
</div>
)
};
export default MondialRelay;
And the error i encounter is "$ is not defined" but this is because i try to use JQuery syntax in React and $ is never defined in my code.
I dont know how to create this div with #Zone_Widget id.
There's a typo in your script tag, you missed https:
change the URL to https://ajax.googleapis.com/ajax/libs/jquery/2.2.4/jquery.min.js and it should work.
As a suggestion, since you're using nodejs/npm already, you can install jquery using npm as well.
npm install jquery
I have some html (JSX) in my react app that looks like this:
<div><h3>Title</h3><p> some message...</p></div>
I am then assigning this to a variable called msg using innerHTML like so:
let msg
msg.innerHTML = "<div><h3>Title</h3><p> some message...</p></div>"
//then i append it to my app like this
document.body.appendChild(msg)
Now i would like to surround the html above with a native React Link element like so
msg.innerHTML = "<Link to"/"><div><h3>Title</h3><p> some message...</p></div></Link>"
of course it doesn't work and it is compiled as regular html when it is rendered to the page as and doesn't act as a react link
How can i achieve this, is it even possible or is there a totally different way of approaching this issue?
I tried doing this and many different variations but to no cigar:
msg.innerHTML = `${<Link to"/">}<div><h3>Title</h3><p> some message...</p></div>${</Link}`
There is ReactDOMServer.renderToString that will render a React element to its initial HTML.
...
import ReactDOMServer from "react-dom/server";
export default function App() {
useEffect(() => {
const msg = document.createElement("div");
msg.innerHTML = ReactDOMServer.renderToString(
<Link to="/">
<div>
<h3>Title</h3>
<p> some message...</p>
</div>
</Link>
);
document.body.appendChild(msg);
}, []);
return null;
}
I am new to React JS and written some React JS code directly to HTML and it is working fine.
Now when I converted this inline code to JS using babel converter using Online Babel Converter and link the Converted JS to HTML, it is showng the blank the UI with no error or warning on browser console window.
I've written the inline babel script in <script type="text/babel> ... </script> tag
Note: I converted the inline code with default selected options in Online Babel Converter, Evaluate in Settings, stage-2 & react in Presets
Edit: Added some portion of code
<!DOCTYPE HTML>
<html>
<body>
<script type="text/babel>
class App extends React.Component {
createCircles = () => {
let circles = [];
for(let i = 1; i <= this.props.count; i++){
circles.push(<div className = "smallCircle" id={'circle'+i} key={i}><code className="circles" id={'id'+i}>{i}</code></div>);
}
return circles;
}
render(){
return (
<div id="circles">
<div className = "bigCircle" id="bigCircle">
<img id="bigCircleImage" src="http://localhost" />
</div>
<div className = "smallCircles">
{this.createCircles()}
</div>
</div>
);
}
}
function AppLoader(){
return (
<App />
);
}
ReactDOM.render(<AppLoader />, document.getElementById('root'));
</script>
<div id="root"></div>
</body>
</html>
Agreed with #JoeWarner answer to not to use extra AppLoader function if you are not returning more than one component.
Coming to the question, I saw that you written the script before the div tag of id root. After converting your script, import the script below the tag to see the changes
I am developing an application using Bokeh and Flask.
Using server-side python code, it produces a plot embedded in a web page which contains a variety of user input elements aiming at configuring the plot.
I know that starting from Bokeh v0.12.x, there is an API allowing to create and manipulate plots directly from javascript.
The bit I am missing here is, starting from the Bokeh javascript object, how can I list and access the already instantiated figure objects (figure, line, ColumnDataSource, ...)? Using the BokehJS API, I would then be able to write javascript code translating webpage user events (checkbox, button click, text input, ...) into actions on the plot (change line color, hide lines, update data point values, ...).
Consider this very rudimentary example. I hope it can get you started.
Two sliders change x and y coordinates of the middle dot.
from bokeh.plotting import figure
from bokeh.resources import CDN
from bokeh.embed import file_html
from bokeh.models import ColumnDataSource
from jinja2 import Template
source = ColumnDataSource(data=dict(x=[1, 2, 3],
y=[3, 2, 1]),
name='my-data-source')
p = figure()
l1 = p.line("x", "y", source=source)
# copied and modified default file.html template used for e.g. `file_html`
html_template = Template("""
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>{{ title|e if title else "Bokeh Plot" }}</title>
{{ bokeh_css }}
{{ bokeh_js }}
<style>
html {
width: 100%;
height: 100%;
}
body {
width: 90%;
height: 100%;
margin: auto;
}
</style>
<script>
function change_ds_value(name, idx, value) {
var ds = Bokeh.documents[0].get_model_by_name('my-data-source');
ds.data[name][idx] = value;
ds.change.emit();
}
</script>
</head>
<body>
<div>
{{ plot_div|indent(8) }}
{{ plot_script|indent(8) }}
<input type='range' min='-5' max='5'
onchange='change_ds_value("x", 1, this.value)'/>
<input type='range' min='-5' max='5'
onchange='change_ds_value("y", 1, this.value)'/>
</div>
</body>
</html>
""")
html = file_html(p, CDN, template=html_template)
with open('test.html', 'wt') as f:
f.write(html)