In the following example, I am trying to change the size the UI chip in dynamically in order to respond to the font size of its parents using the em css unit.
My goal: I want to do something like this
style={{size:'1em'}}
My problem: the chip element in material-ui is not resizable like most of the material-UI components.
I tried:
style={{transform:'scale(1em)'}} but it did not work at all. I don't know how to change the anchor point of transform.
I also tried to create my own chip with <img style={{float: 'left', width: '1em', borderRadius: '50%',}}/> but it does not look original as the material UI chip.
import Avatar from '#material-ui/core/Avatar'
import Chip from '#material-ui/core/Chip'
function Chips() {
const classes = useStyles()
const handleDelete = () => {
console.info('You clicked the delete icon.')
}
const handleClick = () => {
console.info('You clicked the Chip.')
}
return (
<div className={classes.root}>
<h1>
<Chip
//style={{size:'1em'}}
avatar={<Avatar alt="Natacha" src="/static/images/avatar/1.jpg" />}
label="Deletable"
onDelete={handleDelete}
/>
</h1>
<h4>
<Chip
//style={{size:'1em'}}
avatar={<Avatar alt="Natacha" src="/static/images/avatar/1.jpg" />}
label="Deletable"
onDelete={handleDelete}
/>
</h4>
</div>
)
}
currently, Chip doesn't support the prop for size (Hope they support it in the future 🤞).
For this, you've to make your own custom component. I've created one name CustomChip.
In this, pass a prop named size, and the rest of the sizes of the chip scales accordingly.
CustomChip.js
function CustomChip(props) {
const { size = 1, ...restProps } = props;
const classes = useStyles({ size });
return (
<Chip
className={classes.root}
classes={{ avatar: classes.avatar, deleteIcon: classes.deleteIcon }}
{...restProps}
/>
);
}
const useStyles = makeStyles((theme) => ({
root: {
fontSize: (props) => `${props.size * 0.8125}rem`,
height: (props) => `${props.size * 32}px`,
borderRadius: "9999px"
},
avatar: {
"&&": {
height: (props) => `${props.size * 24}px`,
width: (props) => `${props.size * 24}px`,
fontSize: (props) => `${props.size * 0.75}rem`
}
},
deleteIcon: {
height: (props) => `${props.size * 22}px`,
width: (props) => `${props.size * 22}px`,
color: "green"
}
}));
Here the provided size gets multiplied by the default sizes of the parts.
use:-
<CustomChip
size={2}
avatar={<Avatar alt="Natacha" src="/static/images/avatar/1.jpg" />}
label="Deletable"
onDelete={handleDelete}
/>
Working sandbox link:-
Related
What do I want to do:
I want to make React Konva Text editable. That is, if I double click on the Text... (I can move it to anywhere in the Konva Stage) ...I can show a text area to get the edits from the user, any changes he/she would like to do to the default text.
Conditions:
Enter key (keyboard) should produce a new line.
Double click on Text should show this text area on the same offset X and Y of this Text.
Double click on div should take back to Text. Now if there was any change done to the text, then it should get reflected on the Text component's label
I tried to implement this HTML Text demo with React Konva, but in vain, because of limitations
The html Konva demo that I want to reproduce in React Konva
Things I did:
Since HTML 5.0 is incompatible if used in conjugation with React Konva components, like Text, Image, RegularPolygon ... etc.
I used { Html } from 'react-konva-utils' so that I could move that content along with the Text component like in the demo in the link above.
Things that I observed:
The HTML can take padding and margin (we can use normal html within tag.), but not top, left.
I did try to put X property of Text in the margin top of the root div and value of Text's Y property in the margin left attribute of the same div, but had to revert as it was not close to the demo above.
Code:
import React, { useState, useRef, useEffect, Fragment } from "react";
import { Html } from 'react-konva-utils';
import { Text, Transformer } from "react-konva";
/*
Konva warning: tr.setNode(shape), tr.node(shape) and tr.attachTo(shape) methods are deprecated. Please use tr.nodes(nodesArray) instead.
*/
const KText = ({ stage, id, properties, isSelected, onSelect, onChange, setActiveText }) => {
const shapeRef = useRef();
const trRef = useRef();
const [toggleEdit, setToggleEdit] = useState(false)
useEffect(() => {
if (isSelected) {
trRef.current.nodes([shapeRef.current]);
trRef.current.getLayer().batchDraw();
}
}, [isSelected]);
// console.log("KText", `properties: ${JSON.stringify(properties)}`)
// console.log("KText", ` properties.text: ${properties.text}`)
const EditTextField = () => {
var textProps
const updateText = (data) => {
textProps = data
// console.log("EditTextField", `textProps: ${JSON.stringify(textProps)}`)
}
// var mAreaPos = areaPosition()
const areaPosition = () => {
let stage1 = stage.current.getStage()
return ({
x: stage1.container().offsetLeft + properties.x,
y: stage1.container().offsetTop + properties.y,
})
};
return (
<Html >
<div style={{
margin: "200px", padding: "20px", background: "lavender",
borderRadius: 20, borderStyle: "solid", borderColor: "green",
top: areaPosition().x, left: areaPosition().y
}}
onDoubleClick={() => setToggleEdit(!toggleEdit)}>
<label htmlFor="inputText">Please enter some text below:</label><p>
<textarea onChange={(evt) => (updateText({ text: evt.target.value, id: id }))}
id="inputText" name="inputText" rows="4" cols="50" placeholder="Please enter here" />
<br />
<button type="text" onClick={() => {
setToggleEdit(!toggleEdit)
setActiveText(textProps)
}}>Close</button>
</p>
</div>{/* */}
</Html >
)
}
const MainText = () => {
return (
<>
<Fragment>
<Text
stroke={"black"}
strokeWidth={1}
onTap={onSelect}
onClick={onSelect}
onDblClick={() => setToggleEdit(!toggleEdit)}
ref={shapeRef}
// {...shapeProps}
name="text"
x={properties.x}
y={properties.y}
text={properties.text}
fontFamily={properties.fontFamily}//"Serif"
fontSize={properties.fontSize}//50
fontWeight={properties.fontWeight} //"bold"
fillLinearGradientStartPoint={{ x: 0, y: 0 }}
fillLinearGradientEndPoint={{ x: 100, y: 100 }}
fillLinearGradientColorStops={[
0,
"rgba(0,0,0,0.7)",
1,
"rgba(255,155,255,0.5)"
]}
fillPriority={"linear-gradient"}
draggable
onDragEnd={e => {
/* onChange({
...shapeProps,
x: e.target.x(),
y: e.target.y(),
});*/
}}
onTransformEnd={e => {
// transformer is changing scale
/* const node = shapeRef.current;
const scaleX = node.scaleX();
const scaleY = node.scaleY();
node.scaleX(1);
node.scaleY(1);
onChange({
...shapeProps,
x: node.x(),
y: node.y(),
width: node.width() * scaleX,
height: node.height() * scaleY,
}); */
}}
/>
{isSelected && <Transformer ref={trRef} />}
</Fragment>
</>
)
}
const RenderThis = () => {
let inText = "" + properties.text
if (inText.trim().length === 0 || toggleEdit) {
return (
<EditTextField />
)
} else return (
<MainText />
)
}
// rendering function
return (
<RenderThis />
);
};
export default KText;
I have created a table using MUI Data Grid. When I hide the columns and refresh the page the hidden columns are visible again.
I want to able to maintain the state of the columns so the hidden columns shouldn't be visible on re render.I want to be able to manage and save the state of of data grid columns and filter.
import { MrfColumns } from "./MrfTableColumns";
import { getMrfTabledata } from "./MrfTableServices";
const MrfTable = () => {
const [loading, setLoading] = React.useState(true);
const [pageSize, setPageSize] = React.useState<number>(20);
const [mrfTableData, setMrfTableData] = React.useState([]);
useEffect(() => {
getMrfTabledata().then((resp: any) => {
setMrfTableData(resp.data);
setLoading(false);
});
}, []);
console.log(ctx.mrfId);
return (
<Box
sx={{
display: "flex",
justifyContent: "center",
alignItems: "center",
px: 4,
height: "77vh",
}}
>
<DataGrid
rows={mrfTableData}
columns={MrfColumns}
components={{
Toolbar: MrfCustomToolbar,
NoRowsOverlay: CustomeNoRowsOverlay,
LoadingOverlay: LinearProgress,
}}
loading={loading}
rowsPerPageOptions={[20, 30, 40]}
pageSize={pageSize}
onPageSizeChange={(newPageSize) => setPageSize(newPageSize)}
pagination
checkboxSelection
disableSelectionOnClick
/>
</Box>
);
};
export default MrfTable;
I am currently experimenting with React and I am trying to change the background colour of a div according to what the user enters. I have created an Input-component with the input-element with a button, apart from the App-component, however I am unable to type in the input-element without the onChange-event, which I expected. I am unsure of how to change the state, of the state variable ('color' in App.js) with a button click instead of the onChange-event.
My App.js
const AppDiv = styled.div
`margin: 0;
box-sizing: border-box;
display: flex;
width: 100%;
`
class App extends Component {
state = {
color: ' ',
name: null
}
colorchange = (event) => {
event.preventDefault();
this.setState({
color: event.target.value
})
}
render(){
return (
<AppDiv>
<Input
name = {this.state.name}
colour = {this.state.color}
colourChange = {this.colorchange}
Changecolour = {this.changecolour}
/>
</AppDiv>
)
}
}
Input.js
const ColorButton = styled.button
`
width: 100px;
height: 50px;
border-radius: 24px/50%;
background-color: green;
margin: 10px;
`
const ColorDiv = styled.div
`
height: 100vh;
flex-basis: 300px;
background-color: ${props => props.colour}; //Changing background with 'colour' prop
`
const input = (props) => {
return (
<ColorDiv>
<h2>What is your name</h2>
<input type = "text" value = {props.name}/>
<h2>Choose your colour</h2>
<input type = "text" value = {props.colour} />
<ColorButton onClick = {props.colourChange}> Change Colour </ColorButton>
</ColorDiv>
)
}
I am using styled-components to apply styling. Any suggestions or help will be appreciated. Thank you
The best way to achieve what you are doing is to have 2 pieces of state. One for the text you have inputted in the <input /> and one for the color you want the background to be.
In the App
state = {
colorText: null,
backgroundColor: null,
}
onTextChange = (e) => {
this.setState({
colorText: event.target.value
})
}
onUpdateBackgroundColor = () => {
this.setState({
backgroundColor: this.state.colorText
})
}
.......
<Input
colour = {this.state.colorText}
backgroundColor={this.state.backgroundColor}
onTextChange = {this.onTextChange}
updateBackgroundColor = {this.onUpdateBackgroundColor}
/>
then in Input.tsx
background-color: ${props => props.backgroundColor};
<input type="text" value={props.colour} onChange={props.onTextChange} />
<ColorButton onClick={props.updateBackgroundColor}> Change Colour </ColorButton>
Note: To make this better, you should move the state into Input as keeping state in a parent component and updating it on every input change will cause a lot of renders
You can either user a ref for you input and not having it controlled
export default function App() {
const [color, setColor] = React.useState("");
const inputRef = React.useRef();
return (
<div className="App">
<h1>Your Color: {color}</h1>
<input ref={inputRef} />
<button
onClick={() => {
setColor(inputRef.current.value);
}}
>
Update Color
</button>
</div>
);
}
Or having separated state for your input:
export default function App() {
const [color, setColor] = React.useState("");
const [inputValue, setInputValue] = React.useState("");
return (
<div className="App">
<h1>Your Color: {color}</h1>
<input
value={inputValue}
onChange={({ target: { value } }) => {
setInputValue(value);
}}
/>
<button
onClick={() => {
setColor(inputValue);
}}
>
Update Color
</button>
</div>
);
}
I am having problems calling a function in React Native. I simply want to change the value of 'Context'. Here is some code, first the script for 'context':
//LogContext.js
import React, { useState } from 'react'
export const LogContext = React.createContext({
set: "en",
login: "false"
})
export const LogContextProvider = (props) => {
const setLog = (login) => {
setState({set: "jp", login: login})
}
const initState = {
set: "en",
login: "false"
}
const [state, setState] = useState(initState)
return (
<LogContext.Provider value={state}>
{props.children}
</LogContext.Provider>
)
}
and the 'app.js' code:
//app.js
import React, { useState, useContext } from 'react';
import { Button, Text, TextInput, View } from 'react-native';
import { NavigationContainer } from '#react-navigation/native';
import { createStackNavigator } from '#react-navigation/stack';
import { LogContextProvider, LogContext } from './LogContext'
function HomeScreen({ navigation }) {
const state = useContext(LogContext);
return (
<>
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Passed config: {JSON.stringify({state})}</Text>
<Text>Home Screen</Text>
</View>
{state.login === 'false' ? (
<Button
title="Go to Login"
onPress={() => navigation.navigate('Login')}
/>
) : (
<Button title="Stuff" onPress={() => navigation.navigate('DoStuff')} />
)}
</>
);
}
function LoginScreen({ navigation }) {
const state = useContext(LogContext);
//do stuff to login here...
state.setLog('true'); //not functional...
return (
<LogContext.Provider value={'true'}> //value={'true'} also not functional...
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Login Screen</Text>
<Button title="Go to Home" onPress={() => navigation.navigate('Home')} />
</View>
</LogContext.Provider>
);
}
function StuffScreen({ navigation }) {
//do other stuff here...
}
const Stack = createStackNavigator();
function App() {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen name="Login" component={LoginScreen} />
<Stack.Screen name="DoStuff" component={StuffScreen} />
</Stack.Navigator>
</NavigationContainer>
);
}
export default App;
Obviously I am not too familiar with React Native. Any advice on how to call the "setLog()" function as to enable an update of the value for the 'Context' global variable would be greatly appreciated. I thank you in advance.
I am trying to modify my "App()" function to wrap the Navigator within the provider as suggested by another user...however this following is completely non-functional...suggestions appreciated:
const Stack = createStackNavigator();
function App() {
const [data, setData] = useState({
set: 'en',
login: 'false',
});
const state = { data, setData };
return (
<LogContext.Provider value={state}>
<NavigationContainer>
{state.data.login === 'true' ? (
<Stack.Navigator>
<Stack.Screen name="BroadCast" component={VideoScreen} />
<Stack.Screen name="Logout" component={LogoutScreen} />
</Stack.Navigator>
) : (
<Stack.Navigator>
<Stack.Screen name="Login" component={LoginScreen} />
<Stack.Screen name="Details" component={DetailsScreen} />
<Stack.Screen name="Home" component={HomeScreen} />
</Stack.Navigator>
)}
</NavigationContainer>
</LogContext.Provider>
);
}
The issue you are having is not having a set function in your context and i dont see a need for a separate LogContext provider function.
You can simply do that part in your app.js or whatever the root function. The below example does that. You can see how a state value is passed along with a function to set the values and this can be modified from teh Login component which is inside the provider. If you use a separate provider its a bit confusing. The below is a working example without the navigation part to give you an idea.
const LogContext = createContext({
data: {
set: 'en',
login: 'false',
},
});
export default function App() {
const [data, setData] = useState({
set: 'en',
login: 'false',
});
const state = { data, setData };
return (
<LogContext.Provider value={state}>
<View style={{ flex: 1 }}>
<Text>{JSON.stringify(state.data)}</Text>
<Login />
</View>
</LogContext.Provider>
);
}
const Login = () => {
const state = React.useContext(LogContext);
return (
<View>
<Button
onPress={() => state.setData({ set: 'bb', login: 'true' })}
title="Update"
/>
</View>
);
};
To modify your code, you should wrap the main navigator inside the LogContext.Provider and maintain the state there which will help you do the rest.
Feel free to ask any further clarification :)
I’m writing a small web application, hopefully it looks aesthetic on phone. On a mobile phone, the font showing the selected clinics are center justified, I want to know how to insert a line break into the code, so that the options appear consistently justified left in the mobile version, regardless of the name of the options.
Update: I still maintain a spaced-out view on the computer version, after the change.
The problem can be reproduced if 670150 is keyed as postal code, age 22, Singapore, eligible, CHAS Orange, and just select the first option of GP and polyclinics.
Phone view
Computer view
Can someone help me out here? Here's the relevant code.
ResultsTab.js
import React from "react";
import PropTypes from "prop-types";
import SwipeableViews from "react-swipeable-views";
import { makeStyles, useTheme } from "#material-ui/core/styles";
import AppBar from "#material-ui/core/AppBar";
import Tabs from "#material-ui/core/Tabs";
import Tab from "#material-ui/core/Tab";
import Typography from "#material-ui/core/Typography";
// import MyMap from "./myMap";
import TestMap from "./TestMap";
import PcDialog from "./PcDialog";
import GpDialog from "./GpDialog";
// import { display } from "#material-ui/system";
import CompareDialog from "./CompareDialog";
import Grid from "#material-ui/core/Grid";
import Paper from "#material-ui/core/Paper";
function TabContainer({ children, dir }) {
return (
<Typography component="div" dir={dir} style={{ padding: 8 * 3 }}>
{children}
</Typography>
);
}
TabContainer.propTypes = {
children: PropTypes.node.isRequired,
dir: PropTypes.string.isRequired
};
const useStyles = makeStyles(theme => ({
root: {
backgroundColor: theme.palette.background.paper,
width: "100%"
}
}));
const ResultTabs = props => {
const classes = useStyles();
const theme = useTheme();
const [value, setValue] = React.useState(0);
const [state, setState] = React.useState({
sortByLoc: true
});
function handleChange(event, newValue) {
setValue(newValue);
}
function handleChangeIndex(index) {
setValue(index);
}
const [open, setOpen] = React.useState(false);
const [selectedGP, setSelectedGP] = React.useState({
properties: { HCI_NAME: "Please Choose a GP" },
distance: "x",
price: "x",
rating: "x"
});
const [selectedPC, setSelectedPC] = React.useState({
Name: "Please choose a Polyclinic",
distance: "x",
price: "x",
rating: "x"
});
const [GPName, setGPName] = React.useState("none");
const [PCName, setPCName] = React.useState("none");
function handleClickOpen() {
setOpen(true);
}
const handleGPClose = (clinic, name) => {
setOpen(false);
clinic.price = "$$";
clinic.rating = "4.3";
setSelectedGP(clinic);
setGPName(name);
};
const handlePCClose = (clinic, name) => {
setOpen(false);
clinic.price = "$";
clinic.rating = "4.0";
setSelectedPC(clinic);
setPCName(name);
};
return (
<div className={classes.root}>
<Grid style={{ flexGrow: 1 }} direction="row">
<Grid container justify="space-evenly">
<Grid item>Selected GP: {GPName}</Grid>
<Grid item>
<p style={{ fontSize: "1em" }}>Selected PolyClinic: {PCName}</p>
{/* {console.log(selectedGP)} */}
</Grid>
</Grid>
</Grid>
<Grid style={{ flexGrow: 1 }} direction="row">
<Grid container justify="center">
<CompareDialog
GP={selectedGP}
PC={selectedPC}
formData={props.formData}
/>
</Grid>
</Grid>
<hr />
<AppBar position="static" color="default">
<Tabs
value={value}
onChange={handleChange}
indicatorColor="primary"
textColor="primary"
variant="fullWidth"
>
<Tab label="GP" />
<Tab label="Polyclinic" />
<Tab label="Map View" />
</Tabs>
</AppBar>
<SwipeableViews
axis={theme.direction === "rtl" ? "x-reverse" : "x"}
index={value}
onChangeIndex={handleChangeIndex}
>
<TabContainer dir={theme.direction}>
{props.GP.map(clinic => {
return (
<div key={clinic.properties.id}>
<GpDialog
clinic={clinic}
selectedGP={selectedGP}
open={open}
onClose={handleGPClose}
/>
<hr />
</div>
);
})}
</TabContainer>
<TabContainer dir={theme.direction}>
{props.PC.map(clinic => {
return (
<div key={clinic.id}>
<PcDialog
clinic={clinic}
selectedPC={selectedGP}
open={open}
onClose={handlePCClose}
/>
<hr />
</div>
);
})}
</TabContainer>
<TabContainer dir={theme.direction}>
{props.currentLoc[0] !== 0 && (
<TestMap coord={props.currentLoc} GP={props.GP} PC={props.PC} />
)}
</TabContainer>
</SwipeableViews>
</div>
);
};
export default ResultTabs;
FilteredResults.js
import React from "react";
import GP from "./chas.json";
import * as turf from "#turf/turf";
import ResultTabs from "./ResultTabs.js";
import PC from "./polyclinics.json";
import Button from "#material-ui/core/Button";
import Switch from "#material-ui/core/Switch";
import Grid from "#material-ui/core/Grid";
const API_KEY = "";
// this component aims to display the filtered clinic after they fill in the form
//try not to abuse the API call, im using some kind of free credits from google for this
//api to be able to consistenly make the api call
//api in use here are: google geocode & turf
//everything works except for styling, but the content from the json file abit lacking,
// no opening hrs etc
class FilteredResult extends React.Component {
constructor(props) {
super(props);
this.state = {
formData: this.props.location.state, //this gets the info from react router from Form.js
userLng: 0,
userLat: 0,
sortByLoc: true
};
this.goBack = this.goBack.bind(this);
}
componentDidMount() {
fetch(
`https://maps.googleapis.com/maps/api/geocode/json?address=${
this.state.formData.postalCode
}®ion=sg&key=${API_KEY}`
)
.then(res => res.json())
.then(json => {
this.setState({
userLng: json.results[0].geometry.location.lng,
userLat: json.results[0].geometry.location.lat
});
});
}
goBack() {
this.props.history.goBack();
}
render(props) {
const { userLat, userLng, formData } = this.state;
const filteredGP = GP.features.filter(clinic => {
const from = turf.point([userLng, userLat]);
const to = turf.point([
clinic.geometry.coordinates[0],
clinic.geometry.coordinates[1]
]);
const options = { units: "kilometers" };
const dist = turf.distance(from, to, options);
clinic.distance = dist;
if (formData.hasSubsidy === "Yes") {
return (
dist <= 3
// && clinic.properties.CLINIC_PROGRAMME_CODE.includes(formData.subsidyType)
);
}
return dist <= 3;
});
const filteredPC = PC.clinics.filter(clinic => {
const from = turf.point([userLng, userLat]);
const to = turf.point([clinic.coord[0], clinic.coord[1]]);
const options = { units: "kilometers" };
const dist = turf.distance(from, to, options);
clinic.distance = dist;
return dist <= 100;
});
function sortDist(a, b) {
if (a.distance < b.distance) {
return -1;
} else {
return 1;
}
}
const handleSwitch = name => event => {
this.setState({ [name]: event.target.checked });
};
const sortedGP = filteredGP.sort(sortDist);
const sortedPC = filteredPC.sort(sortDist);
//note: dangerouslySetInnerHTML cos the json is in string, but its actually HTML
return (
<div>
<Grid container justify="center">
<h2>
Filtered clinics for{" "}
<span style={{ fongWeight: "bold", textDecoration: "underline" }}>
S{formData.postalCode}
</span> {" "}
{formData.subsidyType === ""
? ""
: `with ${formData.subsidyType} subsidy`}
</h2>
</Grid>
{/* <Switch
checked={this.state.sortByLoc}
onChange={handleSwitch('sortByLoc')}
value="sortByLoc"
inputProps={{ 'aria-label': 'secondary checkbox' }}
/> */}
<div>
<hr />
<ResultTabs
GP={sortedGP}
PC={sortedPC}
formData={formData}
currentLoc={[this.state.userLng, this.state.userLat]}
/>
<Button onClick={this.goBack}>Go Back</Button>
</div>
</div>
);
}
}
export default FilteredResult;
In the ResultTab.js inside the return give justify="left" instead of "space-evenly" for the Grid and I hope you will achieve what you wanted for phone application
<Grid container justify="left">
<Grid item>Selected GP: {GPName}</Grid>
<Grid item>
<p style={{ fontSize: "1em" }}>Selected PolyClinic: {PCName}</p>
{/* {console.log(selectedGP)} */}
</Grid>
</Grid>
</Grid>