imnapo / react-native-cn-richtext-editor Goto Github PK
View Code? Open in Web Editor NEWRichtext editor for react native
License: MIT License
Richtext editor for react native
License: MIT License
Hi, is it possible to move the image in the editor, e.g. move the image from second line to third line?
I am using react-native-cn-richtext-editor in my React Native app for Android. I am able to change the value of text input programmatically, but after value is updated on input field the value keeps repeating if i do any changes from UI(means even if i don't press submit button, pressSubmit is executing). I am pretty sure that i haven't done any mistake in calling the pressSubmit function. I also have tried the same using Modal(instead of Dialog), but that too didn't worked. I am sharing my code and versions i am using. Please tell me if my approach is correct or not. If not, then how can i make it work?
RN: 0.58.6
React: 16.6.3
pressSubmit = () => {
console.log('pressSubmit reached');
if(this.state.visible) {
this.editor.textInputs[0].props.items[0].text = this.editor.textInputs[0].props.items[0].text + ' : ' + this.state.modalValue;
} else {
this.editor.textInputs[0].props.items[0].text = this.editor.textInputs[0].props.items[0].text ;
}
this.setState({
visible : false,
});
}
render() {
return (
<Dialog.Container visible={this.state.visible}>
<Dialog.Title>Add Concept</Dialog.Title>
<Dialog.Input
value={this.state.modalValue}
onChangeText={(text) => this.setState({modalValue : text})}>
</Dialog.Input>
<Dialog.Button label="CANCEL" onPress={()=>{this.onCancelPress()}}/>
<Dialog.Button label="SUBMIT" onPress={()=>{this.pressSubmit()}}/>
</Dialog.Container>
)
}
Thanks in advance.
I'm trying to use <CNRichTextView text={text} />
but it doesn't seem to work.
I set text as simple html (<div><h1><span style="font-weight: bold;">Heading 1</span></h1></div>
for example - that is produced by convertToHtmlString()
) but it just shows nothing at all.
If I convertToObject()
and set it into CNRichTextEditor
it works properly and shows up good.
I'm using a simple rendering without anything else in the screen.
Any idea what I'm doing wrong?
P.S.
It would be very useful to have a doc about CNRichTextView, since I discovered it exploring the src directory.
How add placeholder to text editor?
I've set the body style inside styleList to { fontSize:12 }.
But when I open the view which contains the editor,
the default fontSize of the input field is much larger than 12.
I checked into the source code, it seems that the initial fontSize of the editor is hardcoded to 20.
Will there be any enhancement on this?
convertToHtmlString() function produce extra '\n' for content following an image
First of all ; thanks so much for this amazing work!
If you're interested in migrating this repo to Typescript, I would be glad to participate.
I was also thinking about using a linter, as the source code is sometimes missing style consistency and a bit difficult to read.
operate
1 insert image
2. wirte text Over screen
I'd like to save to database (mongodb) the content of the text editor with images.
Using the convertToHtmlString
method it converts the whole document in html, using file:// for the images.
Do you have any idea on how to save the images in order to retrieve them later and convert it back to Object (using the convertToObject
method) and let the user edit the document again?
Should I have to save the images separately, edit the html keeping track of the new images url?
Hi,
Thank you for this amazing library, is very useful.
Sometimes I have a long string of text as the initial value of the editor. But when the text surpasses the height of the screen, the editor goes right to the end of the text rather than the start. I would like the editor to initially show the top or start of the text to give the user a clear idea of where he is at. This is an iOS app.
I found a related post in stackoverflow but still doesn't work. Is there a way to do that?
Thanks for this helpful lib. It already working fine with me on RN 0.57.3 but after I update version, this bug is appear. I already debug many times, maybe relate to TextInput on CNTextInput. The onSelectionChange call twice time every type any text input and return different selection value.
When using line break on iOS I have the following behavior :
Scroll position is not updating except if I type something from keyboard.
I removed ScrollView component in the CNRichTextEditor.js
file and I also removed the scrollEnabled
TextInput prop in CNTextInput.js
file and I have the following behavior :
Do you think the ScrollView component can be removed to keep the default scroll behavior of TextInput ?
Note : on Android everything works fine for both cases
If I edit the state.value, (changing it from getInitialObject()), the editor will no longer work
I need to highlight a particular text on the Editor. Suppose text on the Editor is "This is Blue Colored". Out of the text, only "Blue Colored" should be highlighted by blue color. I am able to highlight the whole text of the Editor using this.editor.applyToolbar() , but i want to highlight particular text. Please help.
Is it possible to gain compatibility with react-native-web?
At the moment it just renders a textarea with [object Object] as value, the problem seems to be rendering children in textarea. Or do you have a recommendation for a web editor who works nice with the html output?
Thanks for the great work
I want to add input accessory button, refer https://facebook.github.io/react-native/docs/inputaccessoryview
Is it possible to support inputAccessoryViewID for CNRichTextEditor component?
Thanks for the great work!
Hi,
I want to upload videos from this text editor in react native expo, every other thing is working fine, but I am unable to work on how to add/embed videos.
Can anyone help me who has modified this library?
Hello, thank you for this awsome library first of all,
I saw 3 properties of CNRichTextView (Text, style, styleList). I want to make preview in a list like this
But unfortunately I can't limit the number of lines to make it good. Is it possible to limit the amount of text / container height to make the preview not too big?
Thanks
Does this package works without expo?
P.s. Sorry for second question, I’m really interested in this package, good job 👏
Can I somehow catch onFocus/onBlur events?
Implement the ability to insert a quote into the editor
When I click on the new line of the android keyboard, it gets rid of bold, italic, etc. that I selected.
Implement the ability to insert a link into the editor
First I have to thank you all for the great work.
The example app works just fine. but when I copied the example as is to my app (which is expo tabs app) I only change "app" to "EditorScreen" issues emerged like:
1- The toolbar not showing when keyboard is active
2- Unable to select text
What have I missed?
code and screenshots:
import React, { Component } from 'react';
import { View, StyleSheet, Keyboard
, TouchableWithoutFeedback, Text, Dimensions
, KeyboardAvoidingView, Platform } from 'react-native';
import { Permissions, ImagePicker } from 'expo';
import { MaterialCommunityIcons } from '@expo/vector-icons';
import CNRichTextEditor , { CNToolbar, getInitialObject , getDefaultStyles } from "react-native-cn-richtext-editor";
import {
Menu,
MenuOptions,
MenuOption,
MenuTrigger,
MenuContext,
MenuProvider,
renderers
} from 'react-native-popup-menu';
const { SlideInMenu } = renderers;
const IS_IOS = Platform.OS === 'ios';
const { width, height } = Dimensions.get('window');
const defaultStyles = getDefaultStyles();
class EditorScreen extends React.Component {
constructor(props) {
super(props);
this.state = {
selectedTag : 'body',
selectedColor : 'default',
selectedHighlight: 'default',
colors : ['red', 'green', 'blue'],
highlights:['yellow_hl','pink_hl', 'orange_hl', 'green_hl','purple_hl','blue_hl'],
selectedStyles : [],
value: [getInitialObject]
};
this.state.value = [getInitialObject()];
this.editor = null;
}
onStyleKeyPress = (toolType) => {
if (toolType == 'image') {
return;
}
else {
this.editor.applyToolbar(toolType);
}
}
onSelectedTagChanged = (tag) => {
this.setState({
selectedTag: tag
})
}
onSelectedStyleChanged = (styles) => {
const colors = this.state.colors;
const highlights = this.state.highlights;
let sel = styles.filter(x=> colors.indexOf(x) >= 0);
let hl = styles.filter(x=> highlights.indexOf(x) >= 0);
this.setState({
selectedStyles: styles,
selectedColor : (sel.length > 0) ? sel[sel.length - 1] : 'default',
selectedHighlight : (hl.length > 0) ? hl[hl.length - 1] : 'default',
})
}
onValueChanged = (value) => {
this.setState({
value: value
});
}
insertImage(url) {
this.editor.insertImage(url);
}
askPermissionsAsync = async () => {
const camera = await Permissions.askAsync(Permissions.CAMERA);
const cameraRoll = await Permissions.askAsync(Permissions.CAMERA_ROLL);
this.setState({
hasCameraPermission: camera.status === 'granted',
hasCameraRollPermission: cameraRoll.status === 'granted'
});
};
useLibraryHandler = async () => {
await this.askPermissionsAsync();
let result = await ImagePicker.launchImageLibraryAsync({
allowsEditing: true,
aspect: [4, 4],
base64: false,
});
this.insertImage(result.uri);
};
useCameraHandler = async () => {
await this.askPermissionsAsync();
let result = await ImagePicker.launchCameraAsync({
allowsEditing: true,
aspect: [4, 4],
base64: false,
});
console.log(result);
this.insertImage(result.uri);
};
onImageSelectorClicked = (value) => {
if(value == 1) {
this.useCameraHandler();
}
else if(value == 2) {
this.useLibraryHandler();
}
}
onColorSelectorClicked = (value) => {
if(value === 'default') {
this.editor.applyToolbar(this.state.selectedColor);
}
else {
this.editor.applyToolbar(value);
}
this.setState({
selectedColor: value
});
}
onHighlightSelectorClicked = (value) => {
if(value === 'default') {
this.editor.applyToolbar(this.state.selectedHighlight);
}
else {
this.editor.applyToolbar(value);
}
this.setState({
selectedHighlight: value
});
}
renderImageSelector() {
return (
<Menu renderer={SlideInMenu} onSelect={this.onImageSelectorClicked}>
<MenuTrigger>
<MaterialCommunityIcons name="image" size={28} color="#737373" />
</MenuTrigger>
<MenuOptions>
<MenuOption value={1}>
<Text style={styles.menuOptionText}>
Take Photo
</Text>
</MenuOption>
<View style={styles.divider}/>
<MenuOption value={2} >
<Text style={styles.menuOptionText}>
Photo Library
</Text>
</MenuOption>
<View style={styles.divider}/>
<MenuOption value={3}>
<Text style={styles.menuOptionText}>
Cancel
</Text>
</MenuOption>
</MenuOptions>
</Menu>
);
}
renderColorMenuOptions = () => {
let lst = [];
if(defaultStyles[this.state.selectedColor]) {
lst = this.state.colors.filter(x => x !== this.state.selectedColor);
lst.push('default');
lst.push(this.state.selectedColor);
}
else {
lst = this.state.colors.filter(x=> true);
lst.push('default');
}
return (
lst.map( (item) => {
let color = defaultStyles[item] ? defaultStyles[item].color : 'black';
return (
<MenuOption value={item} key={item}>
<MaterialCommunityIcons name="format-color-text" color={color}
size={28} />
</MenuOption>
);
})
);
}
renderHighlightMenuOptions = () => {
let lst = [];
if(defaultStyles[this.state.selectedHighlight]) {
lst = this.state.highlights.filter(x => x !== this.state.selectedHighlight);
lst.push('default');
lst.push(this.state.selectedHighlight);
}
else {
lst = this.state.highlights.filter(x=> true);
lst.push('default');
}
return (
lst.map( (item) => {
let bgColor = defaultStyles[item] ? defaultStyles[item].backgroundColor : 'black';
return (
<MenuOption value={item} key={item}>
<MaterialCommunityIcons name="marker" color={bgColor}
size={26} />
</MenuOption>
);
})
);
}
renderColorSelector() {
let selectedColor = '#737373';
if(defaultStyles[this.state.selectedColor])
{
selectedColor = defaultStyles[this.state.selectedColor].color;
}
return (
<Menu renderer={SlideInMenu} onSelect={this.onColorSelectorClicked}>
<MenuTrigger>
<MaterialCommunityIcons name="format-color-text" color={selectedColor}
size={28} style={{
top:2
}} />
</MenuTrigger>
<MenuOptions customStyles={optionsStyles}>
{this.renderColorMenuOptions()}
</MenuOptions>
</Menu>
);
}
renderHighlight() {
let selectedColor = '#737373';
if(defaultStyles[this.state.selectedHighlight])
{
selectedColor = defaultStyles[this.state.selectedHighlight].backgroundColor;
}
return (
<Menu renderer={SlideInMenu} onSelect={this.onHighlightSelectorClicked}>
<MenuTrigger>
<MaterialCommunityIcons name="marker" color={selectedColor}
size={24} style={{
}} />
</MenuTrigger>
<MenuOptions customStyles={highlightOptionsStyles}>
{this.renderHighlightMenuOptions()}
</MenuOptions>
</Menu>
);
}
render() {
return (
<KeyboardAvoidingView
behavior="padding"
enabled
keyboardVerticalOffset={IS_IOS ? 0 : 0}
style={{
flex: 1,
paddingTop: 20,
backgroundColor:'#eee',
flexDirection: 'column',
justifyContent: 'flex-end',
}}
>
<MenuProvider style={{flex: 1}}>
<TouchableWithoutFeedback onPress={Keyboard.dismiss} >
<View style={styles.main}>
<CNRichTextEditor
ref={input => this.editor = input}
onSelectedTagChanged={this.onSelectedTagChanged}
onSelectedStyleChanged={this.onSelectedStyleChanged}
value={this.state.value}
style={{ backgroundColor : '#fff'}}
styleList={defaultStyles}
//foreColor=''
onValueChanged={this.onValueChanged}
//onRemoveImage={this.onRemoveImage}
/>
</View>
</TouchableWithoutFeedback>
<View style={{
minHeight: 35
}}>
<CNToolbar
size={28}
bold={<MaterialCommunityIcons name="format-bold" />}
italic={<MaterialCommunityIcons name="format-italic" />}
underline={<MaterialCommunityIcons name="format-underline" />}
lineThrough={<MaterialCommunityIcons name="format-strikethrough-variant" />}
body={<MaterialCommunityIcons name="format-text" />}
title={<MaterialCommunityIcons name="format-header-1" />}
heading={<MaterialCommunityIcons name="format-header-3" />}
ul={<MaterialCommunityIcons name="format-list-bulleted" />}
ol={<MaterialCommunityIcons name="format-list-numbers" />}
image={this.renderImageSelector()}
foreColor={this.renderColorSelector()}
highlight={this.renderHighlight()}
selectedTag={this.state.selectedTag}
selectedStyles={this.state.selectedStyles}
onStyleKeyPress={this.onStyleKeyPress} />
</View>
</MenuProvider>
</KeyboardAvoidingView>
);
}
}
var styles = StyleSheet.create({
main: {
flex: 1,
marginTop: 10,
paddingLeft: 30,
paddingRight: 30,
paddingBottom: 1,
alignItems: 'stretch',
},
menuOptionText: {
textAlign: 'center',
paddingTop: 5,
paddingBottom: 5
},
divider: {
marginVertical: 0,
marginHorizontal: 0,
borderBottomWidth: 1,
borderColor: '#eee'
}
});
const optionsStyles = {
optionsContainer: {
backgroundColor: 'yellow',
padding: 0,
width: 40,
marginLeft: width - 40 - 30,
alignItems: 'flex-end',
},
optionsWrapper: {
//width: 40,
backgroundColor: 'white',
},
optionWrapper: {
//backgroundColor: 'yellow',
margin: 2,
},
optionTouchable: {
underlayColor: 'gold',
activeOpacity: 70,
},
// optionText: {
// color: 'brown',
// },
};
const highlightOptionsStyles = {
optionsContainer: {
backgroundColor: 'transparent',
padding: 0,
width: 40,
marginLeft: width - 40,
alignItems: 'flex-end',
},
optionsWrapper: {
//width: 40,
backgroundColor: 'white',
},
optionWrapper: {
//backgroundColor: 'yellow',
margin: 2,
},
optionTouchable: {
underlayColor: 'gold',
activeOpacity: 70,
},
// optionText: {
// color: 'brown',
// },
};
export default EditorScreen;
Hello there.
When i copy text to clipboard and paste it in the text editor, the editor records the length of text as 0 and text as nothing. I initially wanted to validate if the text editor is empty and so if you can help me with this on how i can validate it as empty or if i can override the copy paste method to add text to the value
Is this repo still on going? well I want to know if you handle HTML tags. And I see there's convertToHtmlString and convertToObject i don'at know if this two handle html tags. Could you help me or show me how to use it ?
Does it supports android?
I play around the basic logic that convert editor value to html by convertToHtmlString
(assuming to save it in db), and directly convert the result back to object by convertToObject
, render the result through CNRichTextView. There is error that stop it from rendering anything.
Here are some snapshots and piece of demo code.
{this.state.back && <CNRichTextView text={this.state.back} />}
demo = () => {
const res = convertToHtmlString(this.state.value);
console.log(res);
const back = convertToObject(res);
console.log(back);
this.setState({ back });
};
Hi I just copy paste the USAGE
import React, { Component } from 'react';
import {
View, StyleSheet, Keyboard
, TouchableWithoutFeedback
, KeyboardAvoidingView, Platform
} from 'react-native';
import { MaterialCommunityIcons } from '@expo/vector-icons'
import CNRichTextEditor, { CNToolbar, getInitialObject } from "react-native-cn-richtext-editor";
export default class SendEmail extends Component {
constructor(props) {
super(props);
this.state = {
selectedTag: 'body',
selectedStyles: [],
value: [getInitialObject()]
};
this.editor = null;
}
onStyleKeyPress = (toolType) => {
if (toolType !== 'image') {
this.editor.applyToolbar(toolType);
}
else {
// Handling image ...
}
}
onSelectedTagChanged = (tag) => {
this.setState({
selectedTag: tag
})
}
onSelectedStyleChanged = (styles) => {
this.setState({
selectedStyles: styles
})
}
onValueChanged = (value) => {
this.setState({
value: value
});
}
render() {
return (
<KeyboardAvoidingView
behavior="padding"
enabled
keyboardVerticalOffset={0}
style={{
flex: 1,
paddingTop: Platform.OS == 'ios' ? 20 : 0,
backgroundColor: '#eee',
flexDirection: 'column',
justifyContent: 'flex-end'
}}>
<TouchableWithoutFeedback onPress={Keyboard.dismiss} >
<View style={styles.main}>
<CNRichTextEditor
ref={input => this.editor = input}
onSelectedTagChanged={this.onSelectedTagChanged}
onSelectedStyleChanged={this.onSelectedStyleChanged}
value={this.state.value}
onValueChanged={this.onValueChanged}
style={{ backgroundColor: '#fff', padding: 10 }}
/>
</View>
</TouchableWithoutFeedback>
<View style={{
minHeight: 35
}}>
<CNToolbar
selectedTag={this.state.selectedTag}
selectedStyles={this.state.selectedStyles}
onStyleKeyPress={this.onStyleKeyPress}
size={28}
bold={<MaterialCommunityIcons name="format-bold" />}
italic={<MaterialCommunityIcons name="format-italic" />}
underline={<MaterialCommunityIcons name="format-underline" />}
lineThrough={<MaterialCommunityIcons name="format-strikethrough-variant" />}
body={<MaterialCommunityIcons name="format-text" />}
title={<MaterialCommunityIcons name="format-header-1" />}
heading={<MaterialCommunityIcons name="format-header-3" />}
ul={<MaterialCommunityIcons name="format-list-bulleted" />}
ol={<MaterialCommunityIcons name="format-list-numbers" />}
image={this.renderImageSelector()}
foreColor={this.renderColorSelector()}
highlight={this.renderHighlight()}
/>
</View>
</KeyboardAvoidingView>
);
}
}
var styles = StyleSheet.create({
main: {
flex: 1,
paddingTop: 0,
paddingLeft: 30,
paddingRight: 30,
paddingBottom: 1,
alignItems: 'stretch',
},
});
Image -- this is the error
it saying is undefiend bold, when im trying to use it
Hi, is there a way to insert hyper link to an url?
Thanks
Provide a onDeletedImage
prop which allows to delete the local resource and save space on storage.
Is there a way to play with text alignment (align left, align right, center and justify) in your library ?
Hello, I'd like to build a todo list with something like this wysiwyg rich text editor.
Could you provide pointers on where to add this? Or if this would even be a welcome PR?
Also when we add support for checkbox lists how might one get an id for this list item? I need to associate task details to this list item as well.
First of all, this project is awesome and enlighten me with new idea about how to handle rich text editor in react native.
In social app, we may like to add tag( linking to different topic such as #fancy movie, #beauty), or @Someone to notify someone whoever user like to.
The basic logic is:
After all, the key is an API to attach a tag to content, and support to delete it with only one backspace.
Appreciate it!
Is there a component which can serve as a place to put a title?
Hi, I can not make styles work
this.state.value = convertToObject(html)
this not works, editor does not getting styles from html
however, if user edits text than styles are working fine,
but cant make initial styles work
html
is like
<div><p><span style="font-weight: bold">This should be a bold text</span></p></div>
and convertToObject(html)
not attaching styles to it :(
When there is a lot of information in the editor, trying to scroll to the bottom and then place the cursor there brings up the keyboard right away. Is there a way to not trigger the keyboard onScroll, and only on the onPress?
The CNRichTextEditor props onSelectedTagChanged
and onSelectedStyleChanged
are listed in the README as optional. However, in CNRichTextEditor
, it assumes these props exist.
e.g.
onSelectedStyleChanged = (styles) => {
this.props.onSelectedStyleChanged(styles);
}
In CNTextInput
, the props are checked:
if (this.props.onSelectedStyleChanged) {
this.props.onSelectedStyleChanged(styles);
}
But if the props don't exist, it fails before it gets here, in CNRichTextEditor
<CNToolbar
size={28}
bold={<MaterialCommunityIcons
name="format-bold"
size={12}
color="#0120c1" />}
italic={<MaterialCommunityIcons
name="format-italic"
color="#ffffff" />}
underline={
<MaterialCommunityIcons
name="format-underline"
color="#ffffff" />
}
lineThrough={
<MaterialCommunityIcons
name="format-strikethrough-variant"
color="#ffffff" />
}
body={<MaterialCommunityIcons name="format-text" />}
title={
<MaterialCommunityIcons name="format-header-1" />
}
heading={
<MaterialCommunityIcons name="format-header-3" />
}
ul={
<MaterialCommunityIcons name="format-list-bulleted" />
}
ol={
<MaterialCommunityIcons name="format-list-numbers" />
}
indent={
<MaterialCommunityIcons name="format-list-numbers" />
}
foreColor={this.renderColorSelector()}
highlight={this.renderHighlight()}
selectedTag={this.state.selectedTag}
selectedStyles={this.state.selectedStyles}
onStyleKeyPress={this.onStyleKeyPress}
/>
This code should change the color of the first 4 icons (and the size of the first) but they stay the same size and color, regardless of what i make the values
I could offer a PR for a MIT license for example.
Implement the ability to insert attachments into the editor.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.