Below is a simplified version of my app where there is a template rendered on the page and it is filled with the help of two text inputs.
It's a "template generator" type app and I want to be able to copy the completed template as rendered on the page so that it can be pasted elsewhere. To do this normally you would click + drag to highlight the rendered html then right click + copy or ctrl + c.
How can I convert my HTML to a rendered version so that I can copy to clipboard with the help of my react-clipboard button? Or is there another way I can achieve this?
If anything isn't clear please let me know - thanks in advance
TL;DR: Currently when you click on "copy to clipboard" the app is copying html. I want it to copy the contents as it would be rendered on a webpage. For example hello world converted and copied should copy a red h1 that says hello world.
import React, { Component } from 'react';
import { TextField } from 'material-ui';
import Clipboard from 'react-clipboard.js';
import './App.css';
const logo = 'https://www.google.co.uk/images/branding/googlelogo/2x/googlelogo_color_120x44dp.png';
class App extends Component {
constructor() {
super();
this.state = {
name: 'Default Name',
title: 'Default Title'
};
}
componentDidMount() {
this.setState({template: this.createTemplate(this.state.name, this.state.title) });
}
createTemplate(name, title) {
const template = `<!DOCTYPE html>
<html>
<head>
<link href="https://fonts.googleapis.com/css?family=Montserrat:400,700" rel="stylesheet">
</head>
<body>
<h1>${name}</h1>
<h2>${title}</h2>
</body>
</html>`
this.setState({template});
}
updateValue(event, type) {
if(type === 'name') {
this.setState({name: event.target.value});
this.createTemplate(event.target.value, this.state.title);
} else {
this.setState({title: event.target.value});
this.createTemplate(this.state.name, event.target.value);
}
}
render() {
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<h1 className="App-title">Signature Builder</h1>
</header>
<div>
<br/>
<TextField hintText="Name" onChange={(e) => this.updateValue(e, 'name')} />
<TextField hintText="Title" onChange={(e) => this.updateValue(e, 'title')} />
<div style={{textAlign: 'left', margin: 10}} dangerouslySetInnerHTML={{ __html: this.state.template }} />
<Clipboard data-clipboard-text={this.state.template}>
copy to clipboard
</Clipboard>
</div>
</div>
);
}
}
export default App;
The problem is that react-clipboard doesn't support copying HTML text; you'll just have to do it using normal JS. Unfortunately, clipboard APIs are fairly finicky.
You can get around some of the issues by just not using a button and capturing the onCopy event to change the content copied by the clipboard.
const copyAction = e => {
e.preventDefault()
e.clipboardData.setData('text/plain', 'This is plaintext; try pasting into a program that supports rich text')
e.clipboardData.setData('text/html', '<h1>Hello</h1><h2>World</h2>')
console.log('Copy event captured!')
}
const App = () => (
<div onCopy={copyAction}>
Copy me!
</div>)
ReactDOM.render(<App />,
document.getElementById('root'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id='root' />
Of course, the content of the HTML text itself can be created as you please whether it's through your template literals or through using ReactDOMServer like coreyward suggested.
More info:
How do I copy to the clipboard in JavaScript?
https://developer.mozilla.org/en-US/docs/Web/Events/copy
https://reactjs.org/docs/events.html#clipboard-events
You can use ReactDOMServer.renderToStaticMarkup to convert your React component into a string without any of the React-specific tags. If you expect the HTML to be hydrated later you can use renderToString instead.
https://reactjs.org/docs/react-dom-server.html
Related
thank you for reading this. I am attempting to learn React by making a dummy website, however I've run into a roadblock.
I want the "display-page" div to only show the Send element initially (which is easy) but when someone clicks one of the 4 options from the content_bar div I want remove the current element and only show the newly clicked element (in this case it is 'Transactions')
I've read about useState and routing but I'm not sure how to implement
Thanks! Please let me know if I didnt give enough details
import React, { Component } from 'react';
import './Data.css';
import Transactions from './Transactions';
import Send from './Send';
class Data extends Component {
constructor(props) {
super(props);
this.state = {
content: <Send />
}
}
transactionpage = () => {
this.setState({content: <Transactions/>});
}
render() {
return(
<div className="content">
<div className="content_bar">
<h5>Send</h5>
<h5 onClick={this.transactionpage}>Transactions</h5>
<h5>Friends</h5>
<h5>Professional</h5>
</div>
<div className="display-page">
{this.state.content}
</div>
</div>
);
}
}
export default Data;
Looking at You can't press an <h5> tag and React code without state feels strange.
You need to learn more to achieve your goal, these are the topics:
JSX expresssion
Conditional rendering
State management
Let me show you my solution, it is one of many ways.
class Data extends Component {
constructor(props) {
super(props);
this.state = {
toDisplay: ''
};
this.changeToDisplay = this.changeToDisplay.bind(this);
}
changeToDisplay(e) {
this.setState({ toDisplay: e.target.textContent.toString() });
}
render() {
return (
<div className="content">
<div className="content_bar">
<button onClick={e => changeToDisplay(e)}>Send</button> <br />
<button onClick={e => changeToDisplay(e)}>Transactions</button> <br />
<button>Friends</button> <br />
<button>Professional</button> <br />
</div>
<div className="display-page">
{this.state.toDisplay === 'Send' ? <Send /> : null}
{this.state.toDisplay === 'Transactions' ? <Transactions /> : null}
</div>
</div>
);
}
}
So I have an already written heapsort animation [here][1] with JS/Jquery + HTML+ CSS. We are currently rebuilding +expanding the project in React. I don't know it (or web technologies in general), and so far I've managed by working on the DS while my partner does the react specific things+ animations. I've been given the green light to just use the heap animation since he liked it too. I've been banging my head trying to integrate the HTML file in to react. Trying iframe, window.location, and the other SO things just haven't worked for me. This is why I'm going with the approach of trying to set the HTML code to my HTML file when I click it.
The directory structure is:
heap.js
heapsort (dir) (copied from project):
a)index.html
b)static (dir):
i)styles.css <br>
ii)style.css <br>
iii)heaper.js (does the actual animation work) <br>
I'd like to do something where a button click in heap.js will load up the index.html. If that can load up correctly, everything should work smoothly.
function create(){
//set HTML->./heapsort/index.html
window.location="./heapsort/index.html" //does not work despite going to the correct path
}
Edit: added code
Heap.js (everything except the create function works as is supposed to)
import React, { useState } from 'react';
import { CSSTransition } from 'react-transition-group';
// import { Component } from 'react';
// import Page from './heapsort/index.html';
import VisualPage, {
About,
Complexity,
Controls,
ControlGroup,
Visualization,
} from '../VisualPage.js';
import './Styles/queue.css';
export function Heap(props) {
return (
<div className="heap">
{props.children}
</div>
);
}
export function QueueNode(props) {
return (
<CSSTransition
appear
in={props.show}
onExited={props.onExited}
timeout={200}
unmountOnExit
classNames="queue-node"
>
<div className={"queue-node" + (props.highlight ? " highlight" : "")}>
{props.children}
</div>
</CSSTransition>
);
}
function Demo() {
const [list, setList] = useState([]);
const [length, setLength] = useState(0);
function onExited() {
setList(list.slice(1));
}
function create(){
//set HTML->./heapsort/index.html
// window.location="./heapsort/index.html" //does not work despite going to the correct path
window.location.replace("./heapsort/index.html")
}
return (
<>
<Controls>
<ControlGroup>
<label htmlFor="create">Len of randomized array</label>
<input name="add" type="text" onChange={e => setLength(e.target.value)}></input>
<button onClick={create}>Create Array</button>
</ControlGroup>
</Controls>
<Visualization>
<Heap>
{list.map((node, i) => {
return (
<QueueNode
key={i}
index={i}
show={node.show}
highlight={node.highlight}
onExited={onExited}
>
{node.value}
</QueueNode>
);
})}
</Heap>
</Visualization>
</>
);
}
export default function QueuePage(props) {
return (
<VisualPage title="Array">
<About>
<h4>What is a Heap?</h4>
The bane of my existance.
</About>
<Complexity complexity={[
{
"name": "Indexing",
"complexity": "Θ(1)"
},
{
"name": "Set Element at Index",
"complexity": "Θ(1)"
},
{
"name": "Average wasted space",
"complexity": "Θ(1)",
},
]} />
<Demo />
</VisualPage>
);
}
```<br>
Index.html
-->
<input type="number" id="len" placeholder="Insert length of randomized array"></input>
<button id="click">Generate Arr of Length</button>
<button id="Refresh" onclick="refresh()">Refresh</button>
var myLink = document.getElementById('click');
function refresh(){
location.reload();
}
myLink.onclick = function () {
var script = document.createElement("script");
script.type = "text/javascript";
script.src = "./static/heap.js";
document.getElementsByTagName("head")[0].appendChild(script);
return false;
}
```
[1]: https://dl1683.github.io/DataStructuresInJavaScript/ds/heapsort/index.html
window.location doesn't redirect to another page. You must use
window.location.replace()
function create(){
window.location.replace("./heapsort/index.html")
}
I have a translation json file with the following translation:
"pageNotFound": {
"description": "The page could not be found. Click {{link}} to return to the home page"
},
The link variable I am wanting to be replaced by a ReactRouter <Link>
I have the following code in my render method which outputs the below picture.
public render() {
const { t } = this.props;
const message = t('pageNotFound.description', { link: <Link to="/">here</Link> });
return (
<div className="body-content">
<div>
{message}
</div>
</div>
);
}
I have played with the <Trans> component and I think this may be a way but it seems like you have to type the full text including <> tags which for my use case is not what i'm after as I want all text to be in the translation json if possible.
Any recommendations are welcome
You should use Trans component for this.
"pageNotFound": {
"description": "The page could not be found. Click <0>here</0> to return to the home page"
},
public render() {
const { t } = this.props;
return (
<div className="body-content">
<div>
<Trans
t={t}
i18nKey="pageNotFound.description"
components={[
<Link key={0} to="/">
here
</Link>,
]}
/>
</div>
</div>
);
}
I'm implementing this library : https://github.com/felixrieseberg/React-Dropzone-Component
To trigger another component or element programmatically I can use refbut I got an error of photoUploadDropAreaElement is not a function using below code.
triggerUploadDialog(){
this.photoUploadDropAreaElement.click(); // doesn't work?
console.log(this.photoUploadDropAreaElement);
}
render() {
return(
<div onClick={this.triggerUploadDialog.bind(this)}>
<DropzoneComponent ref={dropArea => this.photoUploadDropAreaElement = dropArea} />
</div>
);
The result of DropzoneComponent look like this
What's wrong here? I just want to trigger a click to open the file dialog for user to select file to upload.
I'm using import * as Dropzone from 'react-dropzone'; via npm install react-dropzone --save-dev. Go here for the details.
This dropzone package allows you to, by default, click on the UI's dropzone to open the file dialog for user to select a file to upload.
Here is the code I used, which includes a 'Choose File' button as well as a 'Delete' button. *Note: multiple={false} disables the user's ability to choose multiple files. You can simply change it to true and the multiple file selection will be enabled.
import * as React from 'react';
import * as Dropzone from 'react-dropzone';
export interface FileUploadState { file: Array<File> }
export class FileUpload extends React.Component<{}, FileUploadState> {
constructor(props: any) {
super(props);
this.state = {
file: []
}
}
onDrop(droppedFile: any) {
if (droppedFile && droppedFile.preview) {
window.URL.revokeObjectURL(droppedFile.preview);
}
this.setState({
file: droppedFile
});
console.log(droppedFile);
}
onDelete() {
this.setState({
file: []
});
}
render() {
let dropzoneRef: any;
return (
<div>
<div>
<Dropzone ref={(node) => { dropzoneRef = node; }} onDrop={this.onDrop.bind(this)} multiple={false}>
<div className="text-center">Drag and drop your file here, or click here to select file to upload.</div>
</Dropzone>
<button type="button" className="button primary" onClick={(e) => {
e.preventDefault(); // --> without this onClick fires 3 times
dropzoneRef.open();
}}>
Choose File(s)
</button>
<button type="button" className="button alert" onClick={this.onDelete.bind(this)}>
Delete
</button>
</div>
<hr />
<div>
<h2>File(s) to be uploaded: {this.state.file.length} </h2>
<ul>
{
this.state.file.map(f => <li><code>{f.name}</code></li>)
}
</ul>
</div>
</div>
)
}
}
For anyone still looking, it looks like the library was updated to expose an open function.
https://github.com/react-dropzone/react-dropzone/commit/773eb660c7848dd1d67e6e13c7f7261eaaa9fd4d
You can use it this way via refs:
dropzoneRef: any;
onClickPickImage = () => {
if (this.dropzoneRef) {
this.dropzoneRef.open();
}
};
// When rendering your component, save a ref
<Dropzone
ref={(ref: any) => {
this.dropzoneRef = ref;
}}
onDrop={this.onDrop}
>
<div onClick={this.onClickPickImage}>Trigger manually</div>
</Dropzone>
Try like this. It's work for me
triggerUploadDialog () {
this.photoUploadDropAreaElement.dropzone.element.click()
}
Component
<div onClick={this.triggerUploadDialog.bind(this)}>
<DropzoneComponent ref={dropArea => this.photoUploadDropAreaElement = dropArea} />
</div>
My problem was NOT including the input element. When I did, it worked.
I managed to get my Quill working, but now I wanted to make a nice splitscreen as we have on this forum but one thing I haven't been able to figure out is how to convert the input of Quill to nice text on the preview side.
I'm able to display the text but it still has all the html tags which of course I don't want.
So this is my Quill setup so far:
export default class AddSpark extends Component {
constructor(props) {
super(props);
this.onChange = this.onChange.bind(this);
this.state ={
content: '',
};
}
onChange(html) {
this.setState ({ content: html });
console.log(html)
}
render() {
return (
<div>
<Col xs={12} md={6}>
<form ref={(input) => this.sparkForm = input} onSubmit={(e) => this.createSpark(e)}>
<ControlLabel>Select your city</ControlLabel>
<select id="formControlsCity" placeholder="Choose your city" onChange={this.onChange} className="form-control" onClick={ moreOptions } ref={(input) => this.city = input}>
<option value="select">Choose your city</option>
<option value="Beijing">Beijing</option>
<option value="Shanghai">Shanghai</option>
<option value="Chengdu & Chongqing">Chengdu & Chongqing</option>
</select>
<ControlLabel>Select your person</ControlLabel>
<select id="formControlsPerson" placeholder="Choose your person" className="form-control" ref={(input) => this.person = input}>
<option value="select">First select your city</option>
</select>
<ControlLabel>Select your location</ControlLabel>
<select id="formControlsLocation" placeholder="Choose your location" className="form-control" ref={(input) => this.location = input}>
<option value="select">First select your city</option>
</select>
<ControlLabel>Title</ControlLabel>
<input type="text" label="Title" placeholder="Enter your title" className="form-control" ref={(input) => this.title = input}/>
<ControlLabel>Content</ControlLabel>
<div className='_quill'>
<ReactQuill
ref='editor'
onChange={this.onChange}
/>
</div>
<br />
<Button type="submit">Submit</Button>
</form>
</Col>
<Col xs={12} md={6}>
<h3>Preview</h3>
{this.state.content}
</Col>
</div>
)}
}
At the moment I get this:
Any help is highly appreciated!
The simplest way is to use the same react-quill component with editing disabled. This method doesn't need you to install any extra package. You can do it by passing a prop readOnly whose value is set to true (By default it is false)
Here is the code for the component which you can use to preview side:
<ReactQuill
value={this.state.content}
readOnly={true}
theme={"bubble"}
/>
This is how it will look like:
Note: ReactQuill comes with two inbuilt themes (bubble and snow).
Here is how it looks in the snow theme. It has a toolbar at the top:
And here is how it looks in bubble theme.
You should use the "bubble" theme by passing the string "bubble" in the theme prop to display your editor content. This is because it does not have a toolbar at the top like the "snow" theme.
In order to use the bubble theme, you'll also have to import a CSS stylesheet for this specific theme as follow:*
import 'react-quill/dist/quill.bubble.css'
After doing some research I was able to find the answer:
To display the content of Quill in the preview section without the html tags I used this code:
<div dangerouslySetInnerHTML={{__html: this.state.content}}></div>
onChange(content, delta, source, editor) {
const text = editor.getText(content);
this.setState ({ content: text });
console.log(text)
}
You need html-react-parser
import Parser from 'html-react-parser';
... // in your render
<ReactQuill
...
value={code}
onChange={this.previewCode}
/>
...
{Parser(code)}
i used it like this in React.js..
To display quil content in a div i use class "ql-editor" because through this our div can use the quil default classes...works..
import ReactQuill from "react-quill";
import "react-quill/dist/quill.snow.css";
const ReactMarkdown = require("react-markdown/with-html"); //for displaying html
function editorContent() {
<div className="ql-editor" style={{ padding: 0 }}>
<ReactMarkdown escapeHtml={false} source={quilGeneratedHtml} />
</div>
}
Here's the docs link: https://github.com/zenoamaro/react-quill#the-unprivileged-editor
simple to implement and getText out of quill editor
// define function to get text,
// here inpText is defined in your constructor, or any global variable
onChangeText = () => {
const editor = this.reactQuillRef.getEditor();
const unprivilegedEditor = this.reactQuillRef.makeUnprivilegedEditor(editor);
// You may now use the unprivilegedEditor proxy methods
this.inpText = unprivilegedEditor.getText();
console.log("unprivilegedEditor.getText()", unprivilegedEditor.getText());
}
// on submit, you can set state with that text binded in this.inpText
this.setState({text: this.inputText})
}
// define react quill component
<ReactQuill value={this.state.text} onChange={this.onChangeText} ref={(el) => { this.reactQuillRef = el }} />
Good Luck...
Using 'html-react-parser' worked for me
Save the html output from quill editor. Then render it as an innerHTML on a div. Give class="ql-editor" to the div and the html will be formatted as we see it in the editor. No need to instantiate editor context. Just import quill's css file.
Vue example:
import '#vueup/vue-quill/dist/vue-quill.snow.css';
<div class="ql-editor" v-html="result"></div>
To preview HTML in your web page in REACT then there are 2 options you have,
using dependency html-react-parser
Follow below steps,
Step1:
Install dependency using below command,
npm i html-react-parser
If you are using yarn then use below command,
yarn add html-react-parser
Step 2:
const parse = require('html-react-parser');
parse(`<div>${this.state.content}</div>`);
Use below code to set the editor contents using below code,
<div
dangerouslySetInnerHTML={{
__html: this.state.content,
}}
I tried using 2 options:-
1> Using **dangerouslySetInnerHTML**, but this I will not recommend.
2> WE can try using a npm package for react such as
**html-react-parser**. Try it out, as its easy to use.