draft.js: how to preserve paragraph breaks when pasting content? - reactjs

Is there documentation that explains how to preserve paragraph breaks when content is pasted into draft.js? Other formating looks reasonable but all the paragraph blocks are collapsed into a single paragraph block when pasting.

You can handle this using a prop for Editor.
handlePastedText = (text: string, html?: string, editorState: EditorState) => {
const selection = editorState.getSelection();
const pastedBlocks = ContentState.createFromText(text).blockMap;
const newState = Modifier.replaceWithFragment(
editorState.getCurrentContent(),
editorState.getSelection(),
pastedBlocks,
);
const newEditorState = EditorState.push(editorState, newState, "insert-fragment");
this.handleChange(newEditorState);
return "handled";
};
And then pass this as props in Editor. This will solve your problem.

Unfortunately, there is no public documentation of processing of pasted content. But since draft-js is open-sourced, reading the sources comes to the rescue.
Draft-js 0.9.1 and below
Just specify p as aliased element for unstyled block using blockRenderMap:
import { Map } from 'immutable';
import Editor from 'draft-js';
const customRenderMap = Map({
unstyled: {
element: 'div',
// will be used in convertFromHTMLtoContentBlocks
aliasedElements: ['p'],
},
});
<Editor
editorState={this.state.editorState}
blockRenderMap={customRenderMap}
/>
Explanation:
When you paste content to draft-js, editOnPaste function gets invoked. Inside it draft determines that content you pasted is html (yes, when you copy-paste any text from text processors like MS Word, Google Docs or Apple Pages, it's actually html), and calls convertFromHTMLToContentBlocks().
convertFromHTMLToContentBlocks() in its turn uses blockRenderMap to determine how to split html to blocks.
Draft-js 0.10.0
div is aliased with p by default in draft-js#0.10.0

Related

How to access current value from writable?

I want to create a custom wrapper for i18n to translate content of the site by clicking the lang button.
Currently, I have something like this.
<script>
import { localization } from './localiztion.ts';
</script>
<p>{localization.t("hello")}</p>
<button on:click={localization.toggleLocale}></button>
p which holds a text (which should be translated) and button which triggers translation.
To split logic from UI I moved localization logic into a different file. It looks like this
const resources = {
"en": {
"hello": "Hello",
},
"uk": {
"hello": "Привіт"
}
}
export function createLocalization() {
let store = writable("en");
return {
unsubscribe: store.unsubscribe,
toggleLocale: () => {
store.update((previousLocale) => {
let nextLocale = previousLocale === "en" ? "uk" : "en";
return nextLocale;
});
},
t: (key: string): string => {
// How to get access to the current store value and return it back to UI?
// I need to do something like this
return resources[store][key]
}
}
}
export const localization = createLocalization();
The problem I have I need to access the current local from within a t function. How can I do this?
I could pass it from UI like
// cut
<p>{localization.t("hello", $localization)}</p>
// cut
by doing this I achieve what I want, but the solution is too cumbersome.
Any advice on how I can do this?
You could get the store value via get, but this is be a bad idea, as it would lose reactivity. I.e. a language change would not update your text on the page.
A better approach is defining it as a store. Since stores currently have to be at the top level to be used with $ syntax, it is more ergonomic to split it into a separate derived store:
export let locale = writable("en"); // Wrap it to restrict it more
export let translate = derived(
locale,
$locale => key => resources[$locale][key],
);
This way you can import this store, which contains a function for translating keys:
import { translate } from '...';
// ...
$translate('hello')
REPL
(The stores can of course also be created differently and e.g. injected via a context instead of importing them.)

Export image node to markdown with Lexical

I create a new node: 'ImageNode', similar to this: https://lexical.dev/docs/concepts/nodes#extending-decoratornode
This is working, but I need to export markdown content, for this, I'm using $convertToMarkdownString.
My problem is images inserted in the editor, aren't being exported as markdown. My console log show just basic transforms.
How can I export the image node to markdown?
I need to create a new transform to markdown?
Thanks!
(Copy-pasting from discussions) ImagePlugin (just like the toolbar) is part of the playground only, so the transformer is not exposed to NPM. We expect to make ImagePlugin as part an individual #lexical/image package in the future but only once we it's generic enough to cater most use cases and guarantee no major breaking cases in the immediate future.
For now, you may want to copy-paste this bit from the playground:
export const IMAGE: TextMatchTransformer = {
export: (node, exportChildren, exportFormat) => {
if (!$isImageNode(node)) {
return null;
}
return `![${node.getAltText()}](${node.getSrc()})`;
},
importRegExp: /!(?:\[([^[]*)\])(?:\(([^(]+)\))/,
regExp: /!(?:\[([^[]*)\])(?:\(([^(]+)\))$/,
replace: (textNode, match) => {
const [, altText, src] = match;
const imageNode = $createImageNode(src, altText, 800);
textNode.replace(imageNode);
},
trigger: ')',
type: 'text-match',
};

React-Intl pass translation as string variable and not object

I'm in the process of adding react-intl to a payment app I'm building but hitting a snag. I apologize if this has been addressed somewhere. I scoured the issues and documentation and couldn't find a direct answer on this (probably just overlooking it).
Use Case: Once a payment is processed I'd like to give the user the option to tweet a translated message indicating they've donated.
Problem: Twitter uses an iframe to "share tweets", and requires a text field as a string variable. When I pass my translation I get [object Object] in the tweet instead of the translated text. This makes sense based on my understanding of the translation engine. But I cant seem to find a way to pass a string rather than a translation object.
what I get when I use {translate('example_tweet')}
const translationText = object
what I need
const translationText = 'this is the translated text'
Question
How do I get the translated text as a string variable rather than an object to be rendered on a page?
Code
button
import { Share } from 'react-twitter-widgets'
import translate from '../i18n/translate'
export default function TwitterButton () {
return (
<Share
url='https://www.sampleSite.org' options={{
text: {translate('example_tweet')},
size: 'large'
}}
/>
)
}
translate
import React from 'react'
import { FormattedMessage } from 'react-intl'
const translate = (id, value = {}) => <FormattedMessage id={id} values={{ ...value }} />
export default translate
I was able to solve it without messing with react-intl. I built a function that scrapes the text I need from the page itself. So it really doesnt matter what the language is. I was hoping to figure out how to snag the translations as variables, but this gets the job done.
function makeTweetableUrl (text, pageUrl) {
const tweetableText = 'https://twitter.com/intent/tweet?url=' + pageUrl + '&text=' + encodeURIComponent(text)
return tweetableText
}
function onClickToTweet (e) {
e.preventDefault()
window.open(
makeTweetableUrl(document.querySelector('#tweetText').innerText, pageUrl),
'twitterwindow',
'height=450, width=550, toolbar=0, location=0, menubar=0, directories=0, scrollbars=0'
)
}
function TwitterButton ({ text, onClick }) {
return (
<StyledButton onClick={onClick}>{text}</StyledButton>
)
}

Quill - Make Editor wrap text in Div instead of P tag

I have built a Quill text editor in ReactJS with React-Quill. I would like to wrap the generated text in the text editor with a Div tag instead of a P tag. Is this possible?
Overview of my use: When text is generated/edited in the Quill editor it is duplicated in another div on another part of the page. Our use of this editor goes back a few years, and thus was built on an older version of Quill, when the text was generated in Div's. We recently upgraded to Quill 1.0 and when the text is generated in P tags it creates unwanted styles that we can't remove. The simplest and least hacky solution would be to generate div's instead of p tags, but I have no idea if that's even possible.
Does anyone know more about this?
Thank you.
You can just change the tagName of the default Block:
var Block = Quill.import('blots/block');
Block.tagName = 'div';
Quill.register(Block);
Working example: https://codepen.io/anon/pen/brgvPR
This worked for me. I am using next.js
import dynamic from 'next/dynamic';
const ReactQuill = dynamic(async () => {
const ReactQuill = await import('react-quill');
const { Quill } = ReactQuill.default;
const Block = Quill.import('blots/block');
Block.tagName = 'div';
Quill.register(Block);
return ReactQuill;
}, { ssr: false });

How to get the selected text from text area in react?

I am trying to make a text editor in react.Does anyone knows how to get the selected text from the textarea so that styles can be applied on the selected text.I know we can use window.getSelection in javascript but I am curious to know If any other methods are available for this functionality?
Yes there is a method to do this, specially in React. The way you should go to achieve this is as follow.
step 1:- use ref in your textarea ui element. like
`<textarea
className='html-editor'
ref='myTextarea'
value = {this.state.textareaVal}
onChange={(event)=>{
this.setState({
textareaVal:event.target.value;
});
}}
>
</textarea>`
step 2:- now you can access the DOM element,using react refs.
let textVal = this.refs.myTextarea;
step 3:- use selectionStart and selectionEnd :- using selectionStart and
selectionEnd you can get to know your start and end pointer
of selected text.which can be done as below;
let cursorStart = textVal.selectionStart;
let cursorEnd = textVal.selectionEnd;
now you have start and end index of your selected text.
step 4 :- use javascript substring function to get the selected text.
this.state.textareaVal.substring(cursorStart,cursorEnd)
The best way to make a Text Editor in React is to use DraftJS.
If you are using React, DraftJS is the way to go about it. It abstracts away many of the challenges you would face while trying to create your own text editor from scratch. This includes managing the state of your editor (similarly to how you would manage a component's state), managing text selection, applying different attributes and so on.
You can get started by checking out the docs, and I would suggest watching the introduction video on that page, which goes through the difficulties DraftJS aims to solve.
I hope that helps.
How to do it in functional component? Expanding on the answer given by Sanjeev.
function MyEditor() {
const [state,setValue] = useState({value: ""});
//1
const myRef = React.createRef()
const inputsHandler = (e) =>{
var taxt = e.target.innerHTML
let textArray = taxt.split(/\n/gm)
console.log(textArray)
setValue( {value: e.target.value} )
}
const onDone = () => {
console.log("on done", stateNew)
dispatch(updateEditorVisibility())
// dispatch(props.reducer(state.value))
dispatch(stateNew.editorReducerAction(state.value))
}
return (
<div>
<textarea
type="text"
ref={myRef}
name="first_name"
onChange={inputsHandler}
value={state.value}/>
<button onClick={() => {
let textVal = myRef.current;
let cursorStart = textVal.selectionStart;
let cursorEnd = textVal.selectionEnd;
let selectedText = state.value.substring(cursorStart,cursorEnd)
console.log(selectedText)
}}>Log</button>
</div>
)}
Create a ref using create ref
const myRef = React.createRef();
set the ref in your textarea
ref={myRef}
To access use
let cursorStart = textVal.selectionStart;
let cursorEnd = textVal.selectionEnd;
let selectedText = state.value.substring(cursorStart,cursorEnd)
console.log(selectedText)````

Resources