I am looking for a text editor component for React Native without WebView. I found only react-native-zss-rich-text-editor, but it uses WebView, which I think is terrible.
I hope to find something, that works with NSAttributedString and SpannableString for IOS and Android in a native way, like in Evernote, for example.
Evernote Android app text editor
Here is a TextArea component, that wraps TextInput and resizes it automatically, when you press "new line" button
import React, { PropTypes } from 'react'
import { TextInput } from 'react-native'
export default class TextArea extends React.Component {
static propTypes = {
text: PropTypes.string.isRequired,
onChangeText: PropTypes.func.isRequired,
initialHeight: PropTypes.number,
isEditing: PropTypes.bool,
scrollViewRef: PropTypes.object,
}
static defaultProps = {
initialHeight: 40,
isEditing: true,
scrollViewRef: null,
}
state = {
height: this.props.initialHeight,
}
componentWillUnmount(){
this._isUnmounted = false
}
focus(){
this.refs.textInput.focus()
}
blur(){
this.refs.textInput.blur()
}
_contentSizeChanged = e => {
this.setState({
height: e.nativeEvent.contentSize.height,
}, () => {
if (this.props.scrollViewRef) {
setTimeout(() => {
if (!this._isUnmounted) this.props.scrollViewRef.scrollToEnd()
}, 0)
}
})
}
_onChangeText = text => {
this.props.onChangeText(text)
}
render(){
return (
<TextInput
ref = "textInput"
multiline
value = {this.props.text}
style = {{ height: this.state.height, color: 'black', flex: 1 }}
onChangeText = {this._onChangeText}
onContentSizeChange = {this._contentSizeChanged}
editable = {this.props.isEditing}
blurOnSubmit = {false}
/>
)
}
}
Related
getting error while making the below page, give me solution for this!!
import React, { Component } from 'react';
import { Text,View ,StyleSheet, TextInput, Button} from 'react-native';
import ValidationComponent from '../index';
export default class Register extends ValidationComponent {
constructor(props) {
super(props);
this.state = {First_name : "First Name",Last_name : 'Last Name', email: "tibtib#gmail.com", password:"****", confirm_password : '****' };
}
I think you've just become a bit confused about passing the variables around. As per our convo in the comments, here's my attempt at what I think you're trying to do (with explanations):
import React, { Component } from 'react';
import { Text, View, StyleSheet, TextInput } from 'react-native';
export default class Register extends Component {
constructor(props) {
super(props);
this.state = {
firstName: { // I have split this so you can store the value and a valid flag conveniently
value: '', // removed initial value and used a placeholder in the element instead
isValid: false
},
lastName: '',
email: 'tibtib#gmail.com',
password: '****',
confirmPassword: '****'
};
}
checkIfValid = (type, value) => { // you can add more checks to this as you add more fields
if (type == 'firstName') return value.length >= 4;
}
_onChangeText = (type, value) => {
// console.log('value =>', value);
const isValid = this.checkIfValid(type, value) // check validity whenever new text is entered
this.setState({ // removed the switch statement and just setState with the isValid variable above
[type]: {
value,
isValid
}
})
};
firstInputStyle = () => { // so we can change the style dynamically depending on state
if (!this.state.firstName.isValid) return styles.firstNameTextInput;
return [styles.firstNameTextInput, styles.isValid]; // if valid, add the isValid style
// you could also generalise this method to work on all input fields
};
render() {
return (
<View style={styles.container}>
<TextInput
style={this.firstInputStyle()} // here we get the style dynamically with the method above
onChangeText={value => this._onChangeText('firstName', value)}
value={this.state.firstName.value} // need to tie the value to our state
placeholder='Enter first name...'
/>
<Text>First name valid = {this.state.firstName.isValid.toString()}</Text>
</View>
);
}
}
const styles = StyleSheet.create({ // some dummy styles I made up
container: {
padding: 20,
paddingTop: 30,
height: '100%'
},
firstNameTextInput: {
borderColor: 'black',
borderWidth: 1,
fontSize: 25,
padding: 10
},
isValid: {
backgroundColor: 'green'
}
});
I hope that make sense... let me know if you have questions!
Note: I've also updated some of your variable names to camelCase as this is more conventional
Goal: Be able to select 2 dates on a calendar using react-native-calendars using the onDayPress prop, and use the result in markedDates prop to form a period of days.
Component.js:
import React, { useState, useEffect } from 'react';
import { Image, View, Animated, StyleSheet, TouchableOpacity, Dimensions } from 'react-native';
import EStyleSheet from 'react-native-extended-stylesheet';
import { Calendar } from 'react-native-calendars';
const { width } = Dimensions.get('window');
const CalendarPicker = (props) => {
const [ markedDates, setMarkedDates ] = useState({});
const markDate = (dateString) => {
setMarkedDates(
(markedDates[dateString] = {
endingDay: true,
color: 'blue'
})
);
};
useEffect(() => {});
return (
<Calendar
style={{
width: width * 0.8
}}
theme={{
arrowColor: '#219F75'
}}
minDate={Date()}
onDayPress={({ dateString }) => markDate(dateString)}
hideArrows={false}
hideExtraDays={true}
hideDayNames={false}
markedDates={markedDates}
markingType={'period'}
/>
);
};
export default CalendarPicker;
Problem: Nothing happens. the date isn't "marked", the useState variable is assigned the data correctly though. Wondering if its a re-render issue? How can this be resolved to display the selected date as "marked"?
According to react-native-calendar when you want to highlight dates between start & end, you need to create markedDates as below,
<Calendar
markedDates={{
"2020-01-16": { startingDay: true, color: "green" },
"2020-01-17": { color: "green" },
"2020-01-18": { color: "green" },
"2020-01-19": { endingDay: true, color: "gray" }
}}
markingType={"period"}
/>
Check below example code
import * as React from 'react';
import { View, StyleSheet } from 'react-native';
import { Calendar } from 'react-native-calendars';
import moment from 'moment';
export default class CalendarExample extends React.Component {
state = {
markedDates: {},
isStartDatePicked: false,
isEndDatePicked: false,
startDate: ''
}
onDayPress = (day) => {
if (this.state.isStartDatePicked == false) {
let markedDates = {}
markedDates[day.dateString] = { startingDay: true, color: '#00B0BF', textColor: '#FFFFFF' };
this.setState({
markedDates: markedDates,
isStartDatePicked: true,
isEndDatePicked: false,
startDate: day.dateString,
});
} else {
let markedDates = this.state.markedDates
let startDate = moment(this.state.startDate);
let endDate = moment(day.dateString);
let range = endDate.diff(startDate, 'days')
if (range > 0) {
for (let i = 1; i <= range; i++) {
let tempDate = startDate.add(1, 'day');
tempDate = moment(tempDate).format('YYYY-MM-DD')
if (i < range) {
markedDates[tempDate] = { color: '#00B0BF', textColor: '#FFFFFF' };
} else {
markedDates[tempDate] = { endingDay: true, color: '#00B0BF', textColor: '#FFFFFF' };
}
}
this.setState({
markedDates: markedDates,
isStartDatePicked: false,
isEndDatePicked: true,
startDate: ''
});
} else {
alert('Select an upcomming date!');
}
}
}
render() {
return (
<View style={styles.container}>
<Calendar
minDate={Date()}
monthFormat={"MMMM yyyy"}
markedDates={this.state.markedDates}
markingType="period"
hideExtraDays={true}
hideDayNames={true}
onDayPress={this.onDayPress}
/>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: 'rgba(52, 52, 52, 0.8)',
padding: 20,
justifyContent: 'center'
}
});
Change this according to your requirements & if you have any doubts feel free to ask.
Hope this helps you.
I am trying to make a pulsing svg heart in ReactNative expo using an SVG image.
The only way I managed to make the heart to resize with an animated value is to change bind it to style: fontSize.
This seems to change size correctly, but the animation is really choppy.
Here is the code:
import React, { Component } from 'react';
import { Animated } from 'react-native';
import { SimpleLineIcons } from '#expo/vector-icons';
const AnimatedIcon = Animated.createAnimatedComponent(SimpleLineIcons);
const TARGET_FONT_SIZE = 16;
const GROWN_FONT_SIZE = 24;
class GrowingHeart extends Component<any, any> {
size = new Animated.Value(TARGET_FONT_SIZE);
constructor(props) {
super(props);
Animated.sequence([
Animated.timing(this.size, {
duration: 1000,
toValue: GROWN_FONT_SIZE
}),
Animated.timing(this.size, {
duration: 1000,
toValue: GROWN_FONT_SIZE
})
]).start();
}
render() {
return (
<AnimatedIcon
style={{ fontSize: this.size }}
size={20}
name="heart"
color="red"
/>
);
}
}
I tried also to bind width and height but they are also choppy + they change container size, rather than the icon.
Is there a better way of doing this? Thanks
Seems that Animated api is simply still rubbish (please correct me if I am getting this wrong)
I re-wrote this with reanimated and it works smooth. The code below is not complete but shows how heart is growing with no choppiness, but rather perfectly smooth.
import React, { Component } from 'react';
import { TouchableOpacity } from 'react-native-gesture-handler';
import Animated, { Easing } from 'react-native-reanimated';
import { SimpleLineIcons } from '#expo/vector-icons';
const {
createAnimatedComponent,
debug,
set,
get,
block,
eq,
Value,
cond,
greaterThan,
startClock,
timing,
Clock,
Code,
clockRunning,
stopClock
} = Animated;
const AnimatedIcon = createAnimatedComponent(SimpleLineIcons);
const TARGET_FONT_SIZE = 16;
const GROWN_FONT_SIZE = 20;
class GrowingHeart extends Component<any, any> {
size = new Value(TARGET_FONT_SIZE);
clock = new Clock();
updatingValue = new Value(0);
clockState = {
finished: new Value(0),
position: new Value(5),
time: new Value(0),
frameTime: new Value(0)
};
clockConfig = {
duration: new Value(500),
toValue: new Value(GROWN_FONT_SIZE),
easing: Easing.linear
};
constructor(props) {
super(props);
}
render() {
const { color } = this.props;
const { updatingValue, size, clock, clockConfig, clockState } = this;
return (
<>
<Code>
{() =>
block([
cond(
// animation not triggered
eq(0, updatingValue),
[],
[
cond(
clockRunning(clock),
[],
[
set(clockState.finished, 0),
set(clockState.time, 0),
set(clockState.position, TARGET_FONT_SIZE),
set(clockState.frameTime, 0),
set(clockConfig.toValue, GROWN_FONT_SIZE),
startClock(clock)
]
),
cond(
greaterThan(0, updatingValue),
// is decreasing
[debug('going down', updatingValue)],
// is growing
[
timing(clock, clockState, clockConfig),
set(size, clockState.position)
]
),
cond(clockState.finished, [stopClock(clock)])
]
)
])
}
</Code>
<TouchableOpacity
onPress={() => {
this.updatingValue.setValue(1);
}}
>
<AnimatedIcon
style={{ fontSize: this.size }}
name="heart"
color={color}
/>
</TouchableOpacity>
</>
);
}
}
For this component I have two nested states.
And I have a text input with a submit button as shown in the code below.
I'd like to save user's input in this.state.schoolForm.userInput whenever onChangeText fires, and then save this.state.schoolForm.userInput to this.userInputs.schoolName when commit button is clicked.
This would work when I just setState to this.state.userInputValue (A simple not nested state) However, it wouldn't work when I try to setState to the nested state: this.state.schoolForm.userInput
When I click submit button nothing happens but it's supposed to transit to the next state. It seems like the way I save to a nested state is causing the problem but I have followed this post React setState for nested state and I can't find anything wrong from my code.
import React from 'react';
import { StyleSheet, Button, StatusBar, Text, TextInput, View} from 'react-native';
const states = {
schoolForm: {
prompt: "Where did you go to school?",
userInput: "School name",
},
durationForm: {
prompt: "For how long?",
userInput: "Duration",
},
}
export default class NewEducation extends React.Component {
constructor(props) {
super(props);
this.state = {
//form: states.reviewForm,
form: states.schoolForm,
userInputs: {
schoolName: "",
duration: ""
},
//for testing
userInputValue: "",
}
}
_nextState = () => {
switch(this.state.form) {
case states.schoolForm:
this.setState({form:states.durationForm});
break;
case states.durationForm:
this.setState({form:states.degreeForm});
break;
default:
break;
}
}
_submitInfo = () => {
switch(this.state.form) {
case states.schoolForm:
//this.setState({ userInputs: { ...this.state.userInputs, schoolName: this.state.form.userInput} });
this.setState({ userInputs: { ...this.state.userInputs, schoolName: this.state.userInputValue}});
break;
case states.durationForm:
//this.setState({ userInputs: { ...this.state.userInputs, duration: this.state.form.userInput} });
this.setState({ userInputs: { ...this.state.userInputs, duration: this.state.userInputValue}});
break;
default:
break;
}
this._nextState();
}
render() {
return (
<View style={styles.container}>
<StatusBar barStyle="light-content"/>
<Text style={styles.textContentWhite}>{this.state.form.prompt}</Text>
<TextInput
style={styles.textContentWhite}
placeholder={this.state.form.userInput}
placeholderTextColor="#B7BEDE"
onChangeText={(userInputValue) =>
//this.setState({ form: {userInput: userInputValue} }
this.setState({form: { ...this.state.form, userInput: userInputValue }}
)}
/>
<Button
onPress={this._submitInfo}
title="Submit"
color="white"
/>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
backgroundColor: "black",
},
textContentWhite: {
fontSize: 20,
color: 'white',
},
});
Signed APK crash after launch, in logCat i got requiring unknown module 'React'
Debug application works fine, but in logCat i got >> Requiring module 'React' by name is only supported for debugging purposes and will BREAK IN PRODUCTION!
React v15.4.1, React native v0.39.2 ?
Sorry for my english
this is my index.android.js
import React from 'react';
import {AppRegistry} from 'react-native';
import myapp from './index_start.js';
AppRegistry.registerComponent('myapp', () => myapp);
and index_start.js
import React, { Component } from "react";
import {
StyleSheet,
AppRegistry,
Text,
Image,
View,
AsyncStorage,
NetInfo,
StatusBar,
Navigator,
Dimensions
} from 'react-native';
// Window dismensions
const { width, height } = Dimensions.get('window');
// Device infos
import DeviceInfo from 'react-native-device-info';
// Native SplashScreen
import SplashScreen from 'react-native-splash-screen';
// Spinner
import Spinner from 'react-native-spinkit';
// Models
import User from './model/UserModel';
// Json data for initial launch
var DB = require('./DB.json');
// Components
import Stage from './components/stage/stage.js'
import Player from './components/player/player.js'
import Settings from './components/settings/settings.js'
import House from './stages/house/house.js'
// LocalStorage key
var USER_KEY = 'user_key';
const routes = [
{name: 'loading'},
{name: 'stage', component: Stage},
{name: 'house', component: House},
{name: 'settings', component: Settings}
];
const _navigator = null;
export default class myapp extends Component {
constructor(props) {
super(props);
this.state = {
isConnected: false,
isLoading: true,
_navigator: null,
stages: null
}
}
componentWillMount() {
// check if connected
this._checkConnexionType();
}
componentDidMount() {
SplashScreen.hide();
this._loadInitialData();
}
componentDidUpdate() {
// console.log(this.state.stages)
if (!this.state.isLoading && this.state.stages !== null) {
_navigator.push({
name: 'stage',
passProps: {
data: this.state.stages
}
})
}
}
/**
* Load localStorage Data
*/
async _loadInitialData() {
// GET User LocalStorage
if (this.state.stages == null) {
var localData;
//AsyncStorage.removeItem(USER_KEY)
AsyncStorage.getItem(USER_KEY).then((data) => {
if (data !== null) {
var localData = JSON.parse(data);
// User.uuid = localData.uuid;
User.setStages(localData.stages)
this.setState({
'stages' : localData.stages
})
} else {
var storage = {};
storage.setUiid = DeviceInfo.getUniqueID();
storage.stages = DB.stages;
AsyncStorage.setItem(USER_KEY, JSON.stringify(storage));
this.setState({
'stages' : DB.stages
})
}
})
}
if (this.state.isConnected) {
// var rStages = this._loadRemoteStages();
// console.log(rStages);
}
// Change state
setTimeout((function() {
this.setState({
'isLoading': false
})
}).bind(this), 1500);
}
/**
* GET stages from remote DB
*/
async _loadRemoteStages() {
await fetch(API_URL)
.then((response) => response.json())
.then((responseJson) => {
console.log(responseJson)
return responseJson;
})
.catch((error) => {
console.error(error);
});
}
/**
* CHECK IF user is connected to Network
* SET bool to state isLoading
*/
_checkConnexionType() {
NetInfo.isConnected.fetch().then(response => {
this.setState({ isConnected: response})
})
}
_renderScene(route, navigator) {
_navigator = navigator;
if (route.name == 'loading') {
return (
<View style={styles.container}>
<StatusBar hidden={true} />
<Image
style={{width: width, height: height}}
source={require('./img/screen.jpg')}
/>
<View style={styles.loading}>
<Text style={styles.loadingText}>CHARGEMENT</Text>
<Spinner type="ThreeBounce" color={'#fff'}/>
</View>
</View>
)
} else if (route.name == 'stage') {
return (
<Stage navigator={_navigator} {...route.passProps}/>
)
} else if (route.name == 'player') {
return (
<House navigator={_navigator} {...route.passProps}}/>
)
} else if (route.name == 'settings') {
return (
<Settings navigator={_navigator} {...route.passProps}/>
)
}
}
render() {
return (
<Navigator
initialRoute={{name: 'loading'}}
configureScene={() => Navigator.SceneConfigs.FloatFromBottomAndroid}
renderScene={this._renderScene.bind(this)}
/>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
},
loading: {
flex: 1,
position: 'absolute',
bottom: 50,
left: 0,
right: 0,
alignItems: 'center',
},
loadingText:{
flex: 1,
fontFamily: 'CarterOne',
fontSize: 20,
color: '#fff'
}
});