react-select how to edit created entries - reactjs

I'm using the Select.AsyncCreatable:
<Select.AsyncCreatable
clearableValue={true}
multi={true}
scrollMenuIntoView={true}
name="field-name"
value={values}
loadOptions={this.getOptions}
onChange={value => {
this.handleOnChange(value...)
}}
placeholder={this.defaultPlaceholder}
/>
How can I edit options I did create. I can remove created options and reenter them, but is there an option to edit selected values? It would be more comfortable.

Not sure how to do it with react-select, but I think what you are looking for is editable-creatable-multiselect
Here's a demo:

I really can't find any solution for this so i just modified what i found online and made it editable:
https://codesandbox.io/s/n4nqrjw564
hope it helps.
editing to add the code:
import React from "react";
const initialValues = [
{ label: "Test1", value: "Test1" },
{ label: "Test2", value: "Test2" },
{ label: "Test3", value: "Test3" }
];
const createOption = label => ({
label,
value: label
});
export default class TagsInput extends React.Component {
constructor(props) {
super(props);
this.state = {
//value: [],
value: initialValues,
focused: false,
inputValue: ""
};
this.handleInputChange = this.handleInputChange.bind(this);
this.handleInputKeyDown = this.handleInputKeyDown.bind(this);
this.handleEditItem = this.handleEditItem.bind(this);
this.handleBlur = this.handleBlur.bind(this);
}
render() {
const styles = {
container: {
border: "1px solid #ddd",
padding: "5px",
borderRadius: "5px"
},
items: {
display: "inline-block",
padding: "2px",
border: "1px solid black",
fontFamily: "Helvetica, sans-serif",
borderRadius: "5px",
marginRight: "5px",
cursor: "pointer"
},
input: {
outline: "none",
border: "none",
fontSize: "14px",
fontFamily: "Helvetica, sans-serif"
}
};
return (
<label>
<ul style={styles.container}>
{this.state.value.map((item, i) => (
<li key={i} style={styles.items} onClick={this.handleEditItem(i)}>
{item.label}
</li>
))}
<input
style={styles.input}
value={this.state.inputValue}
onChange={this.handleInputChange}
onKeyDown={this.handleInputKeyDown}
onBlur={this.handleBlur}
/>
</ul>
</label>
);
}
handleBlur(evt) {
const { value } = evt.target;
if (value !== "") {
this.setState(state => ({
value: [...state.value, createOption(value)],
inputValue: ""
}));
}
console.log(this.state.value);
}
handleInputChange(evt) {
this.setState({ inputValue: evt.target.value });
}
handleInputKeyDown(evt) {
if (evt.keyCode === 13) {
const { value } = evt.target;
if (value !== "") {
this.setState(state => ({
value: [...state.value, createOption(value)],
inputValue: ""
}));
}
}
if (evt.keyCode === 9) {
const { value } = evt.target;
if (value !== "") {
this.setState(state => ({
value: [...state.value, createOption(value)],
inputValue: ""
}));
}
}
if (
this.state.value.length &&
evt.keyCode === 8 &&
!this.state.inputValue.length
) {
this.setState(state => ({
value: state.value.slice(0, state.value.length - 1)
}));
}
console.log(this.state.value);
}
handleEditItem(index) {
return () => {
let it = this.state.value.filter((item, i) => i === index);
console.log(it);
this.setState(state => ({
value: state.value.filter((item, i) => i !== index),
inputValue: it[0].label
}));
//need to change to text box or open a new textbox
};
}
}

Related

React - Uncaught TypeError: Cannot read property 'toLowerCase' of undefined

After entering the color name in the input field, when I submit the form, an error occurs :
TypeError: Cannot read property 'toLowerCase' of undefined
(anonymous function)
C:/Users/HP/Documents/WDB/React/Practice/colors-app/src/NewPaletteForm.js:117
114 | //to check -> is 'palette name' unique
115 | ValidatorForm.addValidationRule("isPaletteNameUnique", value => {
116 | return palettes.every(
> 117 | ({ paletteName }) => paletteName.toLowerCase() !== value.toLowerCase()
118 | ^ );
119 | });
120 | })
App.js : (Class-based component)
class App extends Component {
constructor(props) {
super(props);
this.state = { palettes: seedColors };
this.findPalette = this.findPalette.bind(this);
this.savePalette = this.savePalette.bind(this);
}
savePalette(newPalette) {
this.setState({ palettes: [...this.state.palettes, newPalette] });
}
render() {
return (
<Switch>
<Route
exact
path='/palette/new'
render={(routeProps) =>
<NewPaletteForm
savePalette={this.savePalette}
palettes={this.state.palettes}
{...routeProps}
/>}
/>
NewPaletteForm.js : (Functional component and uses react hooks)
function NewPaletteForm(props) {
const classes = useStyles();
const [open, setOpen] = useState(false);
const [currentColor, setCurrentColor] = useState('teal');
const [colors, setColors] = useState([{ color: 'pink', name: 'pink' }]);
const [fields, setFields] = useState({
newColorName: '',
newPaletteName: ''
})
useEffect(() => {
ValidatorForm.addValidationRule('isColorNameUnique', (value) => {
return colors.every(
({ name }) => name.toLowerCase() !== value.toLowerCase()
);
});
ValidatorForm.addValidationRule('isColorUnique', (value) => {
return colors.every(
({ color }) => color !== currentColor
);
});
ValidatorForm.addValidationRule("isPaletteNameUnique", value => {
return props.palettes.every(
({ paletteName }) => paletteName.toLowerCase() !== value.toLowerCase()
);
});
})
function addNewColor() {
const newColor = {
color: currentColor,
name: fields.newColorName
}
setColors(oldColors => [...oldColors, newColor]);
setFields({ newColorName: '' });
};
function handleChange(evt) {
setFields({ ...fields, [evt.target.name]: evt.target.value });
}
function handleSubmit() {
let newName = fields.newPaletteName;
const newPalette = {
paletteName: newName,
id: newName.toLowerCase().replace(/ /g, '-'),
colors: colors
}
props.savePalette(newPalette);
props.history.push('/');
}
Validator form components for colors and palettes :
<ValidatorForm onSubmit={handleSubmit}>
<TextValidator
label='Palette Name'
value={fields.newPaletteName}
name='newPaletteName'
onChange={handleChange}
validators={['required', 'isPaletteNameUnique']}
errorMessages={['Enter Palette Name', 'Name already used']} />
<Button variant='contained' color='primary' type='submit'>
Save Palette
</Button>
</ValidatorForm>
<ValidatorForm onSubmit={addNewColor}>
<TextValidator
value={fields.newColorName}
name='newColorName'
onChange={handleChange}
validators={['required', 'isColorNameUnique', 'isColorUnique']}
errorMessages={['Enter a color name', 'Color name must be unique', 'Color already used!']}
/>
<Button
variant='contained'
type='submit'
color='primary'
style={{
backgroundColor: currentColor
}}
>
Add Color
</Button>
</ValidatorForm>
seedColors.js:
export default [
{
paletteName: "Material UI Colors",
id: "material-ui-colors",
emoji: "🎨",
colors: [
{ name: "red", color: "#F44336" },
{ name: "pink", color: "#E91E63" },
{ name: "purple", color: "#9C27B0" },
{ name: "deeppurple", color: "#673AB7" },
{ name: "indigo", color: "#3F51B5" },
{ name: "blue", color: "#2196F3" },
{ name: "lightblue", color: "#03A9F4" },
{ name: "cyan", color: "#00BCD4" },
{ name: "teal", color: "#009688" },
{ name: "green", color: "#4CAF50" },
{ name: "lightgreen", color: "#8BC34A" },
{ name: "lime", color: "#CDDC39" },
{ name: "yellow", color: "#FFEB3B" },
{ name: "amber", color: "#FFC107" },
{ name: "orange", color: "#FF9800" },
{ name: "deeporange", color: "#FF5722" },
{ name: "brown", color: "#795548" },
{ name: "grey", color: "#9E9E9E" },
{ name: "bluegrey", color: "#607D8B" }
]
},
{
paletteName: "Flat UI Colors v1",
id: "flat-ui-colors-v1",
emoji: "🤙",
colors: [
{ name: "Turquoise", color: "#1abc9c" },
{ name: "Emerald", color: "#2ecc71" },
{ name: "PeterRiver", color: "#3498db" },
{ name: "Amethyst", color: "#9b59b6" },
{ name: "WetAsphalt", color: "#34495e" },
{ name: "GreenSea", color: "#16a085" },
{ name: "Nephritis", color: "#27ae60" },
{ name: "BelizeHole", color: "#2980b9" },
{ name: "Wisteria", color: "#8e44ad" },
{ name: "MidnightBlue", color: "#2c3e50" },
{ name: "SunFlower", color: "#f1c40f" },
{ name: "Carrot", color: "#e67e22" },
{ name: "Alizarin", color: "#e74c3c" },
{ name: "Clouds", color: "#ecf0f1" },
{ name: "Concrete", color: "#95a5a6" },
{ name: "Orange", color: "#f39c12" },
{ name: "Pumpkin", color: "#d35400" },
{ name: "Pomegranate", color: "#c0392b" },
{ name: "Silver", color: "#bdc3c7" },
{ name: "Asbestos", color: "#7f8c8d" }
]
}
]
What you can do is check to see if the value exists before calling toLowerCase.
Try using ?., like this
Instead of using value.toLowerCase() use value?.toLowerCase().
That way if the value is undefined or null, it won't call toLowerCase()
If paletteName is the one failing you can use paletteName?.toLowerCase()
If you want to go completely safe you do
paletteName?.toLowerCase() !== value?.toLowerCase()

Huge State Array with number of Inputs, reduce performance when state changes [React-native]

I'm working on an app which the users will enter large number of records continuously. App functionalities are completed but the performance is slow . As i'm new to react native I've not much idea on this. But when I googled regarding this issue it has been noticed that when the state changes the whole app get rerenders. So as per some blogs they suggested to split components into pieces i.e., parent and child. I've done that but also the performance is low. Also they suggested to memorize the state and components. But I didn't understand what they meant.
This was my previous code before i split into components Previous code and this is my existing code after i splitted this into components.
import React, { useState, useEffect, useRef } from 'react'
import { StyleSheet, View, ScrollView, Text, RefreshControl, Alert } from 'react-native'
import Background from '../components/Background'
import Header from '../components/Header'
import { Appbar, Button, DataTable, ActivityIndicator, Menu, Divider, IconButton } from 'react-native-paper'
import{ showMessage } from "react-native-flash-message";
import { theme } from '../core/theme'
import Icon from 'react-native-vector-icons/Ionicons';
import { numberValidator } from '../helpers/numberValidator'
import AsyncStorage from '#react-native-async-storage/async-storage';
import * as constants from "../core/constants";
import axios from "axios";
import NumberInput from '../components/NumberInput'
import moment from 'moment';
const Componentinput = ({data,setListData,scrollref}) => {
const ref = React.useRef(View.prototype);
const firstref = React.useRef(View.prototype);
const [digit, setDigit] = useState({ value: '', error: '' })
const [count, setCount] = useState({ value: '', error: '' })
const onSubmitPress = async () => {
const digitError = numberValidator(digit.value)
const countError = numberValidator(count.value)
const usertoken = await AsyncStorage.getItem("#userToken")
if (digitError || countError) {
setDigit({ ...digit, error: digitError })
setCount({ ...count, error: countError })
return
}else if(digit.value.length < 3 || digit.value.length > 3){
setDigit({ ...digit, error: 'Enter 3 digits' })
return
}
let date = moment().format('YYYY-MM-DD HH:mm:ss');
axios
.post(
constants.BASE_URL+'savedata',
{
digit: digit.value,
count:count.value,
created_at:date,
token: usertoken
},
{
headers: {
"Content-Type": "application/json"
}
}
)
.then(setListData([...data, {digit:digit.value, count:count.value, countsum:'', created_at:date}]))
.then(firstref.current.focus(),scrollref.current.scrollToEnd({animated: true}))
// .then(firstref.current.focus())
.then(setDigit({ value: '', error: '' }),setCount({ value: '', error: '' }))
.then(response => {
if(response.data.status == 1){
setListData(response.data.data);
}
})
.catch(error => {
// setLoading(false)
showMessage({
message: "Error Occured",
description: "Oops!! Please try again",
type: "danger",
color: "#fff",
icon: "danger",
floating: true,
});
});
}
return (
<View style={styles.fixedform}>
<View style={styles.textinputViewleft}>
<NumberInput
style={styles.textinput}
ref={firstref}
label="Digit"
returnKeyType="next"
value={digit.value}
onChangeText={(text) => { setDigit({ value: text, error: '' }); if (text.length === 3) { ref.current.focus(); } }}
error={!!digit.error}
errorText={digit.error}
keyboardType="numeric"
maxLength={3}
minLength={3}/>
</View>
<View style={styles.textinputView}>
<NumberInput
style={styles.textinput}
ref={ref}
label="Count"
value={count.value}
onChangeText={(text) => setCount({ value: text, error: '' })}
error={!!count.error}
errorText={count.error}
keyboardType="numeric"
maxLength={3}/>
</View>
<View style={styles.textinputView}>
<Button style={styles.buttonView} mode="contained" onPress={onSubmitPress} >Submit</Button>
</View>
</View>
)
}
const Dashboard = ({ navigation }) => {
const [listdata, setListData] = useState([])
const [menuvisible, setMenuVisible] = useState(false);
const [refreshing, setRefreshing] = useState(false);
const scrollViewRef = useRef();
const [exceedCnt, setexceedCnt] = useState(null)
useEffect(() => {
async function fetchData() {
setexceedCnt(await AsyncStorage.getItem("#exceedCount"))
}
fetchData();
getListData();
}, []);
const openMenu = () => setMenuVisible(true);
const closeMenu = () => setMenuVisible(false);
const onRefresh = () => {
setRefreshing(true);
getListData();
};
const logOut = async () => {
setMenuVisible(false);
try {
await AsyncStorage.clear()
navigation.reset({
index: 0,
routes: [{ name: 'LoginScreen' }],
})
console.log('Storage successfully cleared!')
} catch (e) {
console.log('Failed to clear the async storage.')
}
}
const getListData = async () => {
const token = await AsyncStorage.getItem("#userToken")
try {
axios
.get(constants.BASE_URL + "getlist?token=" +token)
.then(response => {
setRefreshing(false)
setListData(response.data)
})
.catch(error => {
console.log(error);
});
} catch(error) {
console.log(error);
}
}
const deleteConfirmation = (index,id) => {
Alert.alert(
"Warning",
"Do you want to delete this record?",
[
{
text: "Cancel",
onPress: () => console.log("Cancel Pressed"),
style: "cancel"
},
{ text: "OK", onPress: () => onDeletePress(index,id) }
],
{ cancelable: true }
);
}
const onDeletePress = async (index,id) => {
const usertoken = await AsyncStorage.getItem("#userToken")
try {
axios.post(constants.BASE_URL+'deletedata',
{
id: id,
token: usertoken
},
{
headers: {
"Content-Type": "application/json"
}
}
)
.then(response => {
if(response.data.status == 1){
showMessage({
message: "Success",
description: "Data Deleted successfully",
type: "success",
color: "#fff",
icon: "success",
floating: true
});
var array = [...listdata]; // make a separate copy of the array
if (index !== -1) {
array.splice(index, 1);
setListData(response.data.data);
}
}else{
showMessage({
message: "Error Occured",
description: "Oops!! Please try again",
type: "danger",
color: "#fff",
icon: "danger",
floating: true,
});
}
})
.catch(error => {
showMessage({
message: "Error Occured",
description: "Oops!! Please try again",
type: "danger",
color: "#fff",
icon: "danger",
floating: true,
});
});
} catch(error) {
console.log(error);
}
}
const onDeleteoldrecordsPress = async () => {
setMenuVisible(false)
const usertoken = await AsyncStorage.getItem("#userToken")
try {
axios.post(constants.BASE_URL+'deletepreviousdata',
{
token: usertoken
},
{
headers: {
"Content-Type": "application/json"
}
}
)
.then(response => {
if(response.data.status == 1){
// setUploadData([]);
showMessage({
message: "Success",
description: "Data Deleted successfully",
type: "success",
color: "#fff",
icon: "success",
floating: true
});
getListData();
}else{
showMessage({
message: "Error Occured",
description: "Oops!! Please try again",
type: "danger",
color: "#fff",
icon: "danger",
floating: true,
});
}
})
.catch(error => {
showMessage({
message: "Error Occured",
description: "Oops!! Please try again",
type: "danger",
color: "#fff",
icon: "danger",
floating: true,
});
});
} catch(error) {
console.log(error);
}
}
const setexceedCount = () => {
setMenuVisible(false);
navigation.navigate('SetCountScreen');
}
const onDetailsPress = async () => {
navigation.reset({
routes: [{ name: 'DetailsScreen' }],
})
}
return (
<Background>
<Appbar style={styles.top}>
<Menu style={styles.menu}
visible={menuvisible}
onDismiss={closeMenu}
anchor={<Button onPress={openMenu}><Icon style={styles.appbariconfloat} name="ellipsis-vertical-outline" type="ionicon"/></Button>}>
<Menu.Item icon="trash-can-outline" onPress={onDeleteoldrecordsPress} title="Delete old records" />
<Divider />
<Menu.Item icon="plus" onPress={setexceedCount} title="Set Count" />
<Divider />
<Menu.Item icon="power" onPress={logOut} title="Logout" />
</Menu>
<Appbar.Content title='Add records' />
<Appbar.Action icon="table" onPress={onDetailsPress} />
</Appbar>
<Header style={styles.headermargin}></Header>
<View style={styles.datatable}>
{/* <ActivityIndicator style={styles.loadercenter} animating={loading} color="white" /> */}
<DataTable>
<DataTable.Header>
<DataTable.Title>Digit</DataTable.Title>
<DataTable.Title>Count</DataTable.Title>
<DataTable.Title>Total</DataTable.Title>
<DataTable.Title numeric>Action</DataTable.Title>
</DataTable.Header>
{listdata.length > 0 ?
<ScrollView ref={scrollViewRef} style={{marginBottom:150}} refreshControl={
<RefreshControl refreshing={refreshing} onRefresh={onRefresh} />}>
{listdata.map((item, index) => {
return (
<DataTable.Row key={index}>
<DataTable.Cell > {item.digit} </DataTable.Cell>
<DataTable.Cell > {item.count} </DataTable.Cell>
<DataTable.Cell > <Text style={ (parseInt(item.countsum) > exceedCnt) ? styles.countstyleexceed : styles.countstylenotexceed}>{item.countsum}</Text> </DataTable.Cell>
<DataTable.Cell numeric>
<IconButton icon="delete" color="red" size={20} onPress={() => deleteConfirmation(index,item.id)} />
</DataTable.Cell>
</DataTable.Row>
)
})}
<DataTable.Row>
</DataTable.Row>
</ScrollView>
: console.log('no records found')}
{listdata.length == 0
?<DataTable.Row style={styles.norecords}><Text style={{color:"white"}}>No Records found</Text></DataTable.Row>
: console.log("records found")
}
</DataTable>
</View>
<Componentinput
data={listdata}
setListData={setListData}
scrollref={scrollViewRef}
/>
</Background>
)
}
export default Dashboard
const styles = StyleSheet.create({
top: {
position: 'absolute',
left: 0,
right: 0,
top: 0,
},
headermargin: {
marginTop: 40,
},
customView: {
width: '100%',
marginTop: 37
},
appbariconfloat:{
// marginLeft: 0,
// left:0
color:"white",
fontSize:20
},
datatable:{
backgroundColor:'white',
width:'100%',
// marginBottom: 200,
// minHeight:10
},
textinputView:{
flex: 1,
marginRight: 10
},
textinputViewleft:{
flex: 1,
marginRight: 10,
marginLeft: 10
},
textinput:{
height: 55,
margin: 0,
backgroundColor:"#272727",
borderWidth:0.3,
borderColor:"#dfdfdf",
borderRadius: 4
},
buttonView:{
height: 55,
justifyContent: 'center',
marginTop: 12
},
fixedform:{
flexDirection: 'row',
width:'auto',
position: 'absolute',
bottom: 0,
left: 0,
right: 0,
backgroundColor: '#272727',
},
datatable:{
backgroundColor:'#272727',
width:'100%',
marginBottom: 82
},
iconstyle:{
color: 'red',
},
loader:{
color:"white",
},
loadercenter:{
position:"absolute",
left:0,
right:0,
top:0,
bottom:0
},
menu:{
paddingTop: 20,
flexDirection: 'row',
// justifyContent: 'left',
left:8
},
norecords:{
marginTop:25,
},
countstyleexceed:{
color:"red",
fontWeight:"bold"
},
countstylenotexceed:{
color:'#00ff69',
fontWeight:'bold'
}
})

How to configure multiple bars on same chart using react-charts

I have a react wherein I am able to display the. For this i am using react-charts library.
To populate chart data I am making a API call and thereafter updating the state. I want to display multiple values for YAxis.
My sample API response is:
{
"result": 1,
"data": [
{
"data1": "1272.00",
"data2": "1183.00",
"price": "131.00"
},
{
"data1": "1328.00",
"data2": "1468.00",
"price": "132.00"
},
{
"data1": "1829.00",
"data2": "1445.00",
"price": "133.00"
},
]
}
I want data1 and data2 values forYAxis and price for XAxis.
React code:
import React, { Component } from "react";
import Sidebar from "./Sidebar";
import { Chart } from "react-charts";
import axios from "axios";
const qs = require("qs");
class Home extends Component {
state = {
datelist: [],
chart_data: []
};
componentDidMount() {
this.getDatesList();
axios
.post(
`http://127.0.0.1:8000/pricedata/`,
qs.stringify({ date: "2019-01-11" })
)
.then(res => {
if (res.data.result === 1) {
this.setState({
chart_data: [
{
label: "Strike",
data: res.data.data.map(Object.values)
}
]
});
} else {
this.setState({ chart_data: [] });
}
});
}
getDatesList() {
axios.get("http://127.0.0.1:8000/dateslist/").then(res => {
if (res.data.result === 1) {
this.setState({ datelist: res.data.data });
} else {
this.setState({ datelist: [] });
}
});
}
handleChange = event => {
var dateval = event.target.value;
axios
.post(`http://127.0.0.1:8000/pricedata/`, qs.stringify({ date: dateval }))
.then(res => {
if (res.data.result === 1) {
this.setState({
chart_data: [
{
label: "Strike",
data: res.data.data.map(Object.values)
}
]
});
} else {
this.setState({ chart_data: [] });
}
});
};
render() {
return (
<div className="container container_padding">
<div className="row">
<Sidebar />
<div className="col-md-9 col-sm-9 col-xs-12">
<select
className="form-control"
style={{ width: "120px", marginBottom: "10px" }}
onChange={this.handleChange}
>
{this.state.datelist.map((date, i) => (
<option value={date} key={i}>
{date}
</option>
))}
</select>
<div
style={{
width: "400px",
height: "300px"
}}
>
<Chart
data={this.state.chart_data}
series={{ type: "bar" }}
axes={[
{ primary: true, type: "ordinal", position: "bottom" },
{ type: "linear", position: "left", stacked: true }
]}
primaryCursor
tooltip
/>
</div>
</div>
</div>
</div>
);
}
}
export default Home;
How can I achieve this?
Thanks in advance.
Modify following lines:
handleChange = event => {
var dateval = event.target.value;
axios
.post(`http://127.0.0.1:8000/pricedata/`, qs.stringify({ date: dateval }))
.then(res => {
if (res.data.result === 1) {
this.setState({
chart_data: [
{
label: "bar1",
data: res.data.data.map((d) => ({ x: d.price, y: d.data1 })
},
{
label: "bar2",
data: res.data.data.map((d) => ({ x: d.price, y: d.data2 })
}
]
});
} else {
this.setState({ chart_data: [] });
}
});
};
and inside render:
<Chart
data={this.state.chart_data}
series={{ type: 'bar' }}
axes={[
{ primary: true, position: 'bottom', type: 'ordinal' },
{ position: 'left', type: 'linear', min: 0 },
]}
primaryCursor
secondaryCursor
tooltip
/>

Antd UI Table: Dynamically add/delete columns

Based on ANTD's Table example: https://ant.design/components/table/#components-table-demo-edit-cell, I would like to replicate this, with the addition of having the ability to add/delete new columns. The sample from the link above only illustrates how to add new rows.
Here's the code from the sample:
import { Table, Input, Button, Popconfirm, Form } from 'antd';
const FormItem = Form.Item;
const EditableContext = React.createContext();
const EditableRow = ({ form, index, ...props }) => (
<EditableContext.Provider value={form}>
<tr {...props} />
</EditableContext.Provider>
);
const EditableFormRow = Form.create()(EditableRow);
class EditableCell extends React.Component {
state = {
editing: false,
}
componentDidMount() {
if (this.props.editable) {
document.addEventListener('click', this.handleClickOutside, true);
}
}
componentWillUnmount() {
if (this.props.editable) {
document.removeEventListener('click', this.handleClickOutside, true);
}
}
toggleEdit = () => {
const editing = !this.state.editing;
this.setState({ editing }, () => {
if (editing) {
this.input.focus();
}
});
}
handleClickOutside = (e) => {
const { editing } = this.state;
if (editing && this.cell !== e.target && !this.cell.contains(e.target)) {
this.save();
}
}
save = () => {
const { record, handleSave } = this.props;
this.form.validateFields((error, values) => {
if (error) {
return;
}
this.toggleEdit();
handleSave({ ...record, ...values });
});
}
render() {
const { editing } = this.state;
const {
editable,
dataIndex,
title,
record,
index,
handleSave,
...restProps
} = this.props;
return (
<td ref={node => (this.cell = node)} {...restProps}>
{editable ? (
<EditableContext.Consumer>
{(form) => {
this.form = form;
return (
editing ? (
<FormItem style={{ margin: 0 }}>
{form.getFieldDecorator(dataIndex, {
rules: [{
required: true,
message: `${title} is required.`,
}],
initialValue: record[dataIndex],
})(
<Input
ref={node => (this.input = node)}
onPressEnter={this.save}
/>
)}
</FormItem>
) : (
<div
className="editable-cell-value-wrap"
style={{ paddingRight: 24 }}
onClick={this.toggleEdit}
>
{restProps.children}
</div>
)
);
}}
</EditableContext.Consumer>
) : restProps.children}
</td>
);
}
}
class EditableTable extends React.Component {
constructor(props) {
super(props);
this.columns = [{
title: 'name',
dataIndex: 'name',
width: '30%',
editable: true,
}, {
title: 'age',
dataIndex: 'age',
}, {
title: 'address',
dataIndex: 'address',
}, {
title: 'operation',
dataIndex: 'operation',
render: (text, record) => (
this.state.dataSource.length >= 1
? (
<Popconfirm title="Sure to delete?" onConfirm={() => this.handleDelete(record.key)}>
Delete
</Popconfirm>
) : null
),
}];
this.state = {
dataSource: [{
key: '0',
name: 'Edward King 0',
age: '32',
address: 'London, Park Lane no. 0',
}, {
key: '1',
name: 'Edward King 1',
age: '32',
address: 'London, Park Lane no. 1',
}],
count: 2,
};
}
handleDelete = (key) => {
const dataSource = [...this.state.dataSource];
this.setState({ dataSource: dataSource.filter(item => item.key !== key) });
}
handleAdd = () => {
const { count, dataSource } = this.state;
const newData = {
key: count,
name: `Edward King ${count}`,
age: 32,
address: `London, Park Lane no. ${count}`,
};
this.setState({
dataSource: [...dataSource, newData],
count: count + 1,
});
}
handleSave = (row) => {
const newData = [...this.state.dataSource];
const index = newData.findIndex(item => row.key === item.key);
const item = newData[index];
newData.splice(index, 1, {
...item,
...row,
});
this.setState({ dataSource: newData });
}
render() {
const { dataSource } = this.state;
const components = {
body: {
row: EditableFormRow,
cell: EditableCell,
},
};
const columns = this.columns.map((col) => {
if (!col.editable) {
return col;
}
return {
...col,
onCell: record => ({
record,
editable: col.editable,
dataIndex: col.dataIndex,
title: col.title,
handleSave: this.handleSave,
}),
};
});
return (
<div>
<Button onClick={this.handleAdd} type="primary" style={{ marginBottom: 16 }}>
Add a row
</Button>
<Table
components={components}
rowClassName={() => 'editable-row'}
bordered
dataSource={dataSource}
columns={columns}
/>
</div>
);
}
}
ReactDOM.render(<EditableTable />, mountNode);
You can make your columns array a part of the state and update it through setState.
Here is a working codepen: https://codepen.io/gges5110/pen/GLPjYr?editors=0010
// State
this.state = {
dataSource: [{
key: '0',
name: 'Edward King 0',
age: '32',
address: 'London, Park Lane no. 0',
}, {
key: '1',
name: 'Edward King 1',
age: '32',
address: 'London, Park Lane no. 1',
}],
columns: [{
title: 'name',
dataIndex: 'name',
width: '30%',
editable: true,
}, {
title: 'age',
dataIndex: 'age',
}]
};
// Event to add new column
handleAddColumn = () => {
const { columns } = this.state;
const newColumn = {
title: 'age',
dataIndex: 'age',
};
this.setState({
columns: [...columns, newColumn]
});
}
// Render method
render() {
const { dataSource, columns } = this.state;
return (
<Table
dataSource={dataSource}
columns={columns}
/>
);
}

Material ui v1 autocomplete - how to style it/ pass props to it?

Looking forward for any hint how to style new material ui v1 autocomplete or how to pass props to it.
Here's a codesandbox code (working example):
https://codesandbox.io/s/xrzq940854
In my particular case - I would like to style the label (which goes up after entering some value into input) and that horizontal line (underline under the input value).
Thank u for any help. (dropping code also in the snippet)
P.S. I got also a question how to pass props to the styles function. If anyone knows, please let me know :)
import React from 'react';
import PropTypes from 'prop-types';
import Autosuggest from 'react-autosuggest';
import match from 'autosuggest-highlight/match';
import parse from 'autosuggest-highlight/parse';
import TextField from 'material-ui/TextField';
import Paper from 'material-ui/Paper';
import { MenuItem } from 'material-ui/Menu';
import { withStyles } from 'material-ui/styles';
const suggestions = [
{ label: 'Afghanistan' },
{ label: 'Aland Islands' },
{ label: 'Albania' },
{ label: 'Algeria' },
{ label: 'American Samoa' },
{ label: 'Andorra' },
{ label: 'Angola' },
{ label: 'Anguilla' },
{ label: 'Antarctica' },
{ label: 'Antigua and Barbuda' },
{ label: 'Argentina' },
{ label: 'Armenia' },
{ label: 'Aruba' },
{ label: 'Australia' },
{ label: 'Austria' },
{ label: 'Azerbaijan' },
{ label: 'Bahamas' },
{ label: 'Bahrain' },
{ label: 'Bangladesh' },
{ label: 'Barbados' },
{ label: 'Belarus' },
{ label: 'Belgium' },
{ label: 'Belize' },
{ label: 'Benin' },
{ label: 'Bermuda' },
{ label: 'Bhutan' },
{ label: 'Bolivia, Plurinational State of' },
{ label: 'Bonaire, Sint Eustatius and Saba' },
{ label: 'Bosnia and Herzegovina' },
{ label: 'Botswana' },
{ label: 'Bouvet Island' },
{ label: 'Brazil' },
{ label: 'British Indian Ocean Territory' },
{ label: 'Brunei Darussalam' },
];
function renderInput(inputProps) {
const { classes, autoFocus, value, ref, ...other } = inputProps;
return (
<TextField
autoFocus={autoFocus}
className={classes.textField}
value={value}
inputRef={ref}
label="Country"
InputProps={{
classes: {
input: classes.input,
},
...other,
}}
/>
);
}
function renderSuggestion(suggestion, { query, isHighlighted }) {
const matches = match(suggestion.label, query);
const parts = parse(suggestion.label, matches);
return (
<MenuItem selected={isHighlighted} component="div">
<div>
{parts.map((part, index) => {
return part.highlight ? (
<span key={String(index)} style={{ fontWeight: 300 }}>
{part.text}
</span>
) : (
<strong key={String(index)} style={{ fontWeight: 500 }}>
{part.text}
</strong>
);
})}
</div>
</MenuItem>
);
}
function renderSuggestionsContainer(options) {
const { containerProps, children } = options;
return (
<Paper {...containerProps} square>
{children}
</Paper>
);
}
function getSuggestionValue(suggestion) {
return suggestion.label;
}
function getSuggestions(value) {
const inputValue = value.trim().toLowerCase();
const inputLength = inputValue.length;
let count = 0;
return inputLength === 0
? []
: suggestions.filter(suggestion => {
const keep =
count < 5 && suggestion.label.toLowerCase().slice(0, inputLength) === inputValue;
if (keep) {
count += 1;
}
return keep;
});
}
const styles = theme => ({
container: {
flexGrow: 1,
position: 'relative',
height: 200,
},
suggestionsContainerOpen: {
position: 'absolute',
marginTop: theme.spacing.unit,
marginBottom: theme.spacing.unit * 3,
left: 0,
right: 0,
},
suggestion: {
display: 'block',
},
suggestionsList: {
margin: 0,
padding: 0,
listStyleType: 'none',
},
textField: {
width: '100%',
},
label: {
color: 'yellow',
}
});
class IntegrationAutosuggest extends React.Component {
state = {
value: '',
suggestions: [],
};
handleSuggestionsFetchRequested = ({ value }) => {
this.setState({
suggestions: getSuggestions(value),
});
};
handleSuggestionsClearRequested = () => {
this.setState({
suggestions: [],
});
};
handleChange = (event, { newValue }) => {
this.setState({
value: newValue,
});
};
render() {
const { classes } = this.props;
return (
<Autosuggest
theme={{
container: classes.container,
suggestionsContainerOpen: classes.suggestionsContainerOpen,
suggestionsList: classes.suggestionsList,
suggestion: classes.suggestion,
}}
renderInputComponent={renderInput}
suggestions={this.state.suggestions}
onSuggestionsFetchRequested={this.handleSuggestionsFetchRequested}
onSuggestionsClearRequested={this.handleSuggestionsClearRequested}
renderSuggestionsContainer={renderSuggestionsContainer}
getSuggestionValue={getSuggestionValue}
renderSuggestion={renderSuggestion}
inputProps={{
autoFocus: true,
classes,
placeholder: 'Search a country (start with a)',
value: this.state.value,
onChange: this.handleChange,
}}
/>
);
}
}
IntegrationAutosuggest.propTypes = {
classes: PropTypes.object.isRequired,
};
export default withStyles(styles)(IntegrationAutosuggest);
Material-UI v1 uses React-autosuggest module.
Check the below link
https://github.com/moroshko/react-autosuggest/blob/master/src/Autosuggest.js

Resources