Reducing Repetitive Code Within Styled-components - javascript

I'm using styled-components in a react project, and wondering if there's a way to reduce these 3 blocks that are repeating, with the only difference being the color of the border.
Initially, I'm thinking of creating an object that maps out the possible alert values (success, warning, danger), and add appropriate color as their keys, however I'm not sure where it to put that object.
const customStyles = {
${({ alert }) =>
alert === "success" &&
css`
border: 1px solid green;
`}
${({ alert }) =>
alert === "warning" &&
css`
border: 1px solid orange;
`}
${({ alert }) =>
alert === "danger" &&
css`
border: 1px solid red;
`}
}
const Input = styled.input`
${customStyles}
`;

How about a function you can reuse that returns the right color based on the value?
Something like this:
function getAlertColor(alert){
switch (alert) {
case "success":
return "green"
case "warning":
return "orange"
case "danger":
return "red"
}
}
const Input = styled.input`
border: ${props => `1px solid ${getAlertColor(props.alert)};`};
`;

Related

event.target in undefined in React <input> component

I am using React. I am trying to dynamically add and remove input fields. I am trying to make a list of objects like
[
{"item": 1, "key": "RAM", "value": "8GB"},
{"item": 1, "key": "Color", "value": "Black"},
{"item": 1, "key": "Used For", "value": "6 months"},
]
There can be any number of objects in the above list. I want to send a post request with the above data. The problem I am facing is creating the above pattern. I want to create an Add button which will show two input fields for the key and value side by side. Then I want to add them to the above list when the user enters the values. Don't worry about the item because it is always the same.Here is a CodeSandBox link. Please expand the browser tab in CodeSandBox to see the full page.
I have done the following:
App.js
import React, { useState } from "react";
import {
SpecificationFormContainer,
SpecificationFormCard,
SpecificationFormTitle,
SpecificationInputRow,
SpecificationValue,
AddMoreButton,
RemoveButton
} from "./Elements";
import "./styles.css";
export default function App() {
const [numberOfSpecFields, setNumberOfSpecFields] = useState(0);
const [specificationKeys, setSpecificationKeys] = useState("");
const [specificationValues, setSpecificationValues] = useState("");
const [specifications, setSpecifications] = useState([]);
const handleSpecKeyChange = (event) => {
setSpecificationKeys(event.target.value);
};
const handleSpecValueChange = (event) => {
setSpecificationValues(event.target.value);
};
const handleFocusOut = (event, index) => {
console.log(event);
if (event.target.value === "") {
console.log("String Empty");
return;
}
};
return (
<SpecificationFormContainer>
<SpecificationFormCard>
<SpecificationFormTitle>Add Specifications</SpecificationFormTitle>
<form>
{[...Array(numberOfSpecFields)].map((e, index) => {
return (
<SpecificationInputRow key={index}>
<SpecificationValue
placeholder="Key"
onChange={handleSpecKeyChange}
name="key"
onBlur={() => {
handleFocusOut(index);
}}
/>
<SpecificationValue
placeholder="Value"
onChange={handleSpecValueChange}
name="value"
onBlur={() => {
handleFocusOut(index);
}}
/>
</SpecificationInputRow>
);
})}
</form>
<AddMoreButton
onClick={() => {
setNumberOfSpecFields((prev) => prev + 1);
}}
>
Add
</AddMoreButton>
{numberOfSpecFields > 0 && (
<RemoveButton
onClick={() => {
setNumberOfSpecFields((prev) => prev - 1);
}}
>
Remove
</RemoveButton>
)}
</SpecificationFormCard>
</SpecificationFormContainer>
);
}
Elements.js
import styled from "styled-components";
export const SpecificationFormContainer = styled.div`
width: 100%;
border: 2px solid blue;
padding: 1em;
display: flex;
justify-content: center;
align-items: center;
`;
export const SpecificationFormCard = styled.div`
background-color: rgba(0, 0, 0, 0.1);
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19);
/* min-height: 15em; */
min-width: 50em;
`;
export const SpecificationFormTitle = styled.div`
width: 100%;
font-size: large;
text-align: center;
`;
export const SpecificationInputRow = styled.div`
width: 100%;
display: flex;
justify-content: center;
`;
export const SpecificationValue = styled.input`
width: 40%;
padding: 0.5em;
margin: 0.5em;
`;
export const AddMoreButton = styled.button`
width: 7em;
`;
export const RemoveButton = styled.button`
width: 7em;
`;
In the handleFocusOut function, the event is 0. I was thinking of making an object with the key value pair in the handleFocusOut function based on the index as the key and value input pair have the same index.
I want to know why this does not work and how to make it work. There is most likely a better way to do this as this approach probably is not the best since I am only just a beginner. So, if there is a better way to achieve this, I want to know that.
In the handleFocusOut function, the event is 0.
It's 0 because you are passing index as an argument and ignoring the event. You could use a curried function to accept the event and index as well.
const handleFocusOut = (index) => (event) => {
// do stuff
};
onBlur={handleFocusOut(index)}
or add a function directly in JSX:
onBlur={(event) => handleFocusOut(event, index)}

React Native - change color based on api value

i'm making a React Native project, and basically, i wanted to change a styled component background color based on the value of the api, but no idea how i'll make this
(i'm using: React Native, Expo, typescript and styled components)
i wanted the ContainerRating (which is a styled component) background turn to green if levelRiskMorse in API is equal to "NoRisk"
This is the api server.json:
"allTasks": [
{
"patientName": "PacientOne",
"taskName": "Walk",
"executors": "Person3 - Doctor",
"institutionName": "IntitutionName",
"generalObservations": "Observations Test",
"planType": "1588",
"time": "07:00",
"risk": true,
"levelRiskMorse": "NoRisk",
"levelRiskBarden": "Low"
},
this is a fake API !
Component itself and the styles.ts:
[...]
const [risk, setRisk] = useState('');
//fetching data
useEffect(() => {
async function getRiskLevel(){
const { data } = await api.get('allTasks');
const response = data.forEach((item: any | undefined) => {
return item.levelRiskMorse
})
setRisk(response);
}
getRiskLevel();
}, [])
return(
<View>
<ContainerRating> // if levelRiskMorse === "NoRISK" color turn to green
<Text>Sem Risco</Text>
<Text>0-24</Text>
</ContainerRating>
</View>
)
}
export const ContainerRating = styled.View`
border-width: 0.6px;
border-color: ${({theme}) => theme.colors.default_color};
flex-direction: row;
justify-content: space-around;
height: 50px;
align-items: center;
margin-top: 2px;
border-left: unset;
`;
sorry if i didn't make it clear, im not used to ask questions here
Simple do something like:
<ContainerRating style={{color: levelRiskMorse==="NoRISK" ? 'green' : defaultColor }}
<Text>Sem Risco</Text>
<Text>0-24</Text>
</ContainerRating>
What's happening? I'm applying a style object directly in the component (every component has this property). levelRiskMorse==="NoRISK" it's an if condition inline. If it is true, 'green' will be set, otherwise a default color variable you want to use.

React Styled Component doesn't show correct output

I've changed my style in StyledButton tag but it doesn't reflect on webpage. Can you help what is wrong in this code
import React, { Component } from 'react';
import './App.css';
//import Radium, {StyleRoot} from 'radium';
import Person from './Person/Person';
import { render } from 'react-dom';
import styled from 'styled-components'
const StyledButton = styled.button `
background-color: ${props => props.alt ? 'red' : 'green'}; //Here I have define these property which is not reflecting in output
color: white;
font: inherit;
border: 1px solid blue;
padding: 8px;
cursor:pointer;
&:hover:{
background-color:${props => props.alt ? 'salmon' : 'green'}; //Hover Function is also not working
color:black;
}`;
class App extends Component {
state ={
persons : [
{id:'12', name: 'Max', age: 28},
{id:'21', name: 'Manu', age:29},
{id:'33', name: 'Nikhil', age:23}
]};
nameChangeHandler = (event, id) => {
const personIndex = this.state.persons.findIndex(p=>{
return p.id===id;
});
const person = {
... this.state.persons[personIndex]
};
person.name = event.target.value;
const persons =[...this.state.persons];
persons[personIndex]=person;
this.setState({ persons: persons
});
}
deletePersonHandler = (personIndex) =>{
//const persons = this.state.persons.slice();
//const persons = this.state.persons
const persons = [... this.state.persons];
persons.splice(personIndex,1);
this.setState({persons: persons})
}
togglePersonsHandler = ()=> {
const doesShow = this.state.showPersons;
this.setState({showPersons: !doesShow});
}
render()
{
let persons = null;
if(this.state.showPersons)
{
persons = (
<div>
{ this.state.persons.map((person, index) =>{
return <Person
click = { () => this.deletePersonHandler(index) }
name={person.name}
age={person.age}
key= {person.id}
changed={(event)=> this.nameChangeHandler(event, person.id)}/>
})}
</div>
);
//StyledButton.backgroundColor= 'red';
}
let classes = []
if(this.state.persons.length<=2)
{
classes.push('red');
}
if(this.state.persons.length<=1)
{
classes.push('bold');
}
return (
<div className = "App">
<h1>Hi there this is my first react application</h1>
<p className= {classes.join(' ')}>Hi this is really working</p>
<StyledButton
alt ={ this.state.showPersons }
onClick = {this.togglePersonsHandler}>Toggle Persons</StyledButton>
{ persons }
</div>
);
}
}
export default App;
Code just toggle the names and ages when user click on button and delete the names when click on the paragraph and adding certain classes these are all works fine.
I'm using styled component package on toggle button and it is not working properly, I don't why Please let me know if you understand
You've defined alt to be a transient prop, i.e. $alt, but you don't pass that prop to the StyledButton.
You've also a typo in your hover selector, there's a trailing colon (:): &:hover: should be :hover (the leading parent node selector & is also unnecessary).
const StyledButton = styled.button `
background-color: ${props => props.$alt ? 'red' : 'green'};
color: white;
font: inherit;
border: 1px solid blue;
padding: 8px;
cursor: pointer;
:hover {
background-color:${props => props.$alt ? 'salmon' : 'green'};
color:black;
}
`;
...
<StyledButton
$alt={this.state.showPersons} // <-- pass transient prop
onClick={this.togglePersonsHandler}
>
Toggle Persons
</StyledButton>
This was introduced in v5.1. If you didn't intend to declare a transient prop or you aren't on v5.1 or newer, then simply remove the $ and use the alt prop.
const StyledButton = styled.button `
background-color: ${props => props.alt ? 'red' : 'green'};
color: white;
font: inherit;
border: 1px solid blue;
padding: 8px;
cursor: pointer;
:hover {
background-color:${props => props.alt ? 'salmon' : 'green'};
color:black;
}
}`;
...
<StyledButton
alt={this.state.showPersons} // <-- use alt prop
onClick={this.togglePersonsHandler}
>
Toggle Persons
</StyledButton>
Demo

how can i display round dot for active status in react

In older code of react it just display the text active or in active for an user. Now I want to replace it to red or green small dot how can I do it.
CSS
div .colored-circle {
display: inline-block;
margin-left: 5px;
margin-right: 5px;
margin-bottom: -2px;
border-radius: 50%;
border-style: solid;
border-width: 0.5px;
border-color: black;
height: 20px;
width: 20px;
}
Component:
const ColoredCircle = ({ color }) => {
const styles = { backgroundColor: color };
return color ? (
<Fragment>
<span className="colored-circle" style={styles} />
</Fragment>
) : null;
};
export default ColoredCircle;
Use the same logic you used to show 'active' or 'inactive' and instead of text add a div with css or img of the desired color.
If you happen to use material UI, you can do like this:
import React, { Fragment } from "react";
import { makeStyles } from "#material-ui/core";
const RADIUS_DOT = 1.5;
const useStyles = makeStyles((theme) => ({
circle: {
borderRadius: RADIUS_DOT,
height: RADIUS_DOT * 2,
width: RADIUS_DOT * 2,
padding: 0,
},
}));
const ColoredCircle = ({ color }) => {
const styles = { backgroundColor: color };
const classes = useStyles();
return color ? (
<Fragment>
<span className={classes.circle} style={styles} />
</Fragment>
) : null;
};
export default ColoredCircle;
you can use this package, can be helpful
npm i react-color-circle
import Circle from '#uiw/react-color-circle';
return (
<Circle
colors={[ '#F44E3B' ]}
/>);

Styled Components: nesting and referring to other components at the same time

<FooterLight transparent={true}/>
Is it possible to have a nested rule in definition of FooterLight for which the props value transparent is evaluated. Then it assigns 'color: white' to its children ChangelogVersion and CopyRight?
Next two questions:
Since color: white !important is the same for ChangelogVersion and CopyRight. Can these be merged together in one statement?
Does &&& work to not use !important?
export const FooterLight = styled(Navbar).attrs({fixed: 'bottom'})`
background-color: ${props => props.transparent
? 'transparent'
: 'white'};
${props => props.transparent && ChangelogVersion} {
color: white !important;
}
${props => props.transparent && CopyRight} {
color: white !important;
}
`
export const ChangelogVersion = styled(NavLink)`
&&& {
font-size: 14px !important;
padding-right: .5rem;
}
`
export const CopyRight = styled.div `
padding: .5rem 0;
color: '#777777';
font-size: 14px;
}
Sure can... You could do something like this:
export const FooterLight = styled(Navbar).attrs({fixed: 'bottom'})`
background-color: ${props => props.transparent
? 'transparent'
: 'white'};
${ChangelogVersion}, ${CopyRight} {
${props => props.transparent && "color: white !important;"}
}
`
As for your second statement, &&& might work but you're better off structuring the CSS better so it doesn't need to be !important in the first place... In you're example there's no reason for any of the importants to be there, so it's hard to offer a suggestion.

Categories