React style width based on length of props - css

I have a react component:
const TeamRow = props => {
const items = props.team.map((item, index) => {
const con_width = 100 / props.team.length;
const Style = {
team_img: {
width: "50px",
height: "auto"
},
member_container: {
float: "left",
width: `{con_width}%`
}
};
return (
<div style={Style.member_container}>
<img style={Style.team_img} src={logo} />
<p>{item.name}</p>
<p>{item.bio}</p>
</div>
);
});
return <div>{items}</div>;
};
The idea is to have a set of divs next to one another horizontally. The width of each div should equal 100% divided by the number of divs.
{con_width} is calculated correctly, but in the web inspector, there is no 'width' style at all. It just gets ignored by react. What am I doing wrong?

Change
width: `{con_width}%`
To
width: `${con_width}%`
You need to use ${} in template literals to print the value

Related

Can we animate a CSS change with a State?

I have a Sidebar on left of my App, and it is set on 300px wide. When I click on a button, the Sidebar goes from 300px to 50px.
I change this width with a state :
const [isMinimized, setIsMinimized] = useState(false);
[...]
<div className={`${isMinimized ? 'w-16' : '2xl:w-80 w-56'} relative bg-white`}>
I'd like to set an animation when the sidebar minimize. I tried to add an animation on my div, but does not work.
Is it possible ?
You can specify a className when isMinimized is true ( for Example isMinimzed ? 'animatedScrollbar w-16' : '2xl:w-80 w-56') and add your transition as css using the className of the minimized state .
You need to add transition-width so tailwind adds transition-property:width to the element's style properties.
You can do this all within JSX with tailwind classes. No need for other code.
Check below
function MyApp() {
const [isMinimized, setIsMinimized] = React.useState(false);
return (
<div >
<div
className = {
`${isMinimized ? 'w-16' : '2xl:w-80 w-56'} relative bg-black duration-1000 transition-width ease-in-out`
} >
MY DIV
</div>
<button
onClick = {() => setIsMinimized(!isMinimized)}
>
CLICK
</button>
</div>
)
}
ReactDOM.createRoot(
document.getElementById("root")
).render( <
MyApp / >
);
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.1.0/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.1.0/umd/react-dom.development.js"></script>
<script src="https://cdn.tailwindcss.com"></script>
Or see jsFiddle
If you want to add custom transition durations or behaviors you would need to change tailwind config. Read more here tailwind transition
Use Web Animation API
First create a ref object of the element you change its width.
const ref = useRef(null);
And use it:
<div ref={ref} className={relative bg-white`}>
Call this function on click.
const onClick = () => {
const { current } = ref;
if (current) {
const keyframe = [{ width: "300px" }, { width: "50px" }];
const options = {
duration: 1000,
fill: "forwards",
delay: 0,
easing: "ease-in",
};
const newKeyframeEffect = new KeyframeEffect(current, keyframe, options);
const animation = new Animation(newKeyframeEffect, document.timeline);
animation.play();
animation.onfinish = () => {
}
}
};
Do whatever you need in onfinish functoin.
If you need to change the width of the element back to 300px, do this:
Track if the element is expanded.
const [isExpended, setIsExpended] = useState(false)
If expanded, change its width from 300px to 50px. Else vise versa.
const keyframe = isExpended ? [{ width: "50px" }, { width: "300px" }]: [{ width: "300px" }, { width: "50px" }];
When the animation finishes, change the isExpended state.
setIsExpended(!isExpended)
Example code
const App = () => {
const ref = useRef(null)
const [isExpended, setIsExpended] = useState(false)
const onClick = () => {
const { current } = ref;
if (current) {
const keyframe = isExpended ? [{ width: "50px" }, { width: "300px" }]: [{ width: "300px" }, { width: "50px" }];
const options = {
duration: 1000,
fill: "forwards",
delay: 0,
easing: "ease-in",
};
const newKeyframeEffect = new KeyframeEffect(current, keyframe, options);
const animation = new Animation(newKeyframeEffect, document.timeline);
animation.play();
animation.onfinish = () => setIsExpended(!isExpended);
}
};
return <div ref={ref} style={{width: "300px", background: "red"}}>
<button onClick={onClick} type="button">Click</button>
</div>
}
Try this
https://stackblitz.com/edit/react-17rkkx?file=src%2FApp.js
Reference
useRef - https://reactjs.org/docs/hooks-reference.html#useref
Web Animation API - https://developer.mozilla.org/en-US/docs/Web/API/Web_Animations_API

React.js & styled-components - Styled Input with (the illusion of ) Element inside it

so what i'm trying to do is to create an styled input that seem to have a fixed text (div / children / etc...) inside it.
one image can be self explenatory so this is how it looks like right now:
it seems simple, and the basic idea i quite is:
i got some Wrapper component that has 2 children - the input itself & some element.
like so:
const Input = (props: InputProps) => {
return (
<Wrapper>
<InputStyled {...props} />
<InnerElement>cm</InnerElement>
</Wrapper>
);
};
export default Input;
So where is the problem?
The problem is when i'm trying to set width to this component.
It destroys everything.
is looks like so:
so the wrapper should get a width prop and keep the input and the text element inside of it.
here is a codesandbox i created:
https://codesandbox.io/s/reactjs-input-with-element-inside-forked-2kr6s?file=/src/App.tsx:0-1795
it'll be nice if someone understand what i'm doing wrong.
here are some files:
Input.tsx
import React, { InputHTMLAttributes, CSSProperties } from "react";
import styled from "styled-components";
export interface InputProps extends InputHTMLAttributes<HTMLInputElement> {
style?: CSSProperties;
label?: string;
value: string | number;
onChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
text?: string;
isDisabled?: boolean;
hasError?: boolean;
errorLabel?: string | Function;
placeholder?: string;
width?: string;
}
const defaultProps: InputProps = {
text: "",
onChange: () => null,
value: ""
};
const Wrapper = styled.div<Partial<InputProps>>`
outline: 1px dashed red;
box-sizing: border-box;
justify-self: flex-start; // This is what prevent the child item strech inside grid column
border: 2px solid lightblue;
background: lightblue;
border-radius: 8px;
display: inline-flex;
align-items: center;
max-width: 100%;
width: ${({ width }) => width};
`;
const InputStyled = styled.input<Partial<InputProps>>`
box-sizing: border-box;
flex: 1;
outline: 1px dashed blue;
padding: 8px;
border: none;
border-radius: 8px;
max-width: 100%;
:focus {
outline: none;
}
`;
const InnerElement = styled.div`
outline: 1px dashed green;
box-sizing: border-box;
${({ children }) =>
children &&
`
padding: 0 8px;
font-size: 13px;
`};
`;
const Input = (props: InputProps) => {
return (
<Wrapper width={props.width}>
<InputStyled {...props} />
<InnerElement>{props.text}</InnerElement>
</Wrapper>
);
};
Input.defaultProps = defaultProps;
export default Input;
App.tsx
import * as React from "react";
import "./styles.css";
import Input from "./Input";
import styled from "styled-components";
const ContainerGrid = styled.div`
display: grid;
grid-template-columns: 1fr 1fr;
grid-gap: 50px;
`;
export default function App() {
const [value, setValue] = React.useState("");
const handleChange = (e: any) => {
setValue(e.target.value);
};
const renderInputWithElement = () => {
return (
<ContainerGrid>
<Input
placeholder="input with element inside"
value={value}
onChange={handleChange}
width={"60px"} // Play with this
text="inch"
/>
<Input
placeholder="input with element inside"
value={value}
onChange={handleChange}
width={"110px"} // Play with this
text="cm"
/>
<Input
placeholder="input with element inside"
value={value}
onChange={handleChange}
width={"200px"} // Play with this
text="mm"
/>
<Input
placeholder="input with element inside"
value={value}
onChange={handleChange}
width={"100%"} // Play with this
text="El"
/>
<Input
placeholder="input with element inside"
value={value}
onChange={handleChange}
width={"100%"} // Play with this
/>
</ContainerGrid>
);
};
return (
<div className="App">
<h1>StyledCode</h1>
<h3>Input with an element inside</h3>
<p>
This is kind of an illusion since input cannot have child element inside
of it really.
</p>
<hr style={{ marginBottom: "32px" }} />
{renderInputWithElement()}
</div>
);
}
I Fixed it for you:
https://codesandbox.io/s/reactjs-input-with-element-inside-forked-h2het
First problem was that your were setting the same width to Wrapper and to InputStyled (by passing ...props) Then things get messy. Obviously, both parent and children which has some other element next to it can't have same width. InputStyled's won't be rendered with width you give it, but lesser, as it's being stretched to left by InnerElement.
If you need to pass it all the props, including the width, than you can just remove that width prop by setting width to unset
<InputStyled {...props} width="unset" />
However, there was another issue with padding interfering with width calculation. See answer to another question:
According to the CSS basic box model, an element's width and height
are applied to its content box. Padding falls outside of that content
box and increases the element's overall size.
As a result, if you set an element with padding to 100% width, its
padding will make it wider than 100% of its containing element. In
your context, inputs become wider than their parent.
You can go along with solution there. However, the one simple solution is to use width lesser than your padding, so width: calc(100% - 16px). This does the trick.
So i've found the answer, and its quite simple.
since the browser initializes an input with a width, it cause problems
with the flexbox behavor - the input element can't shrink below its default width and is > forced to overflow the flex container.
So i added min-width: 0 to InputStyled.
thats it.
#justdv thanx for your answer, you right about the width being on the wrong place, i fixed it but the sandbox wasnt updated. i missed that.
thanx anyway for your time :)
here is my fixed sandbox:
https://codesandbox.io/s/reactjs-input-with-element-inside-forked-44h8i?file=/src/Input.tsx
Thanx again for reading.

Get image height in React grid

I am making masonry grid gallery in React and I have trouble getting image height after load and before resizing it with grid. I have onLoad handler on img but there I get only already resized image height where it should be original, to use it to calculate number of row spans. I am aware it could be pure css problem, but I don't know how to solve it.
Here is my useEffect:
useEffect(() => {
if (imageRef.current && !loading) {
const height = imageRef.current.clientHeight;
console.log('height', height);
const spansCount = Math.ceil(height / 10 + 1);
setSpansCount(spansCount);
}
}, [imageRef.current, loading]);
And here is my render:
return (
<div
{...rest}
className={styles.gallery__item}
style={{ gridRowEnd: `span ${spansCount}` }}
>
<img
src={image.previewURL}
ref={imageRef}
className={styles.gallery__img}
alt={image.tags}
onLoad={handleLoad}
/>
<Placeholder
ref={loaderRef}
style={loading ? { display: 'block' } : { display: 'none' }}
/>
</div>
Here is my non working Codesandbox.
And here is working Codesandbox with react-load-image package, but I am not sure why this is working and the first one isn't.
I just needed to change this css on image:
.gallery__img {
width: 100%;
height: auto; // not 100%
}

styling material-table (specifically height)

I'm using material table from material UI react from here and I can't figure out how to style the table properly, especially the height of rows.
I've tried all the ways explained in doc, alternatively and altogether and nothing makes it.
But there seems to be something somewhere that takes precedence over my css. Some of these CSS prop do have effects (like textAlign and vertical align), but most don't.
What am I missing here ?
I also overrode some css directly, without any effect
tr {
height: 100px;
}
tr td {
height: auto !important;
overflow:scroll;
}
MuiTableRow-root.{
height:50px;
max-height:50px;
}
here's my code:
let columns = [];
//console.log(this.props.data.matrix[0][0])
for(var i=0;i<this.props.headers.length;i++){
let filtering = false;
let obj = {'title':this.props.headers[i].display,'field':this.props.headers[i].accessor,headerStyle: {
backgroundColor: '#315481',
color: '#FFF',
},cellStyle:{maxHeight:50,height:50,overflow:'scroll',padding:1}}
columns.push(obj)
}
return(
<div>
<a className="anchor" id={this.props.id}></a>
<div className="centeringDiv"><h3>{this.props.title}</h3></div>
{this.props.infoText && <SectionInfo infoText = {this.props.infoText} />}
<div style={{ maxWidth: '100%',marginTop:15}}>
<MaterialTable
columns={columns}
data={this.props.dataToDisplay}
title={''}
onRowClick={((evt, selectedRow) => this.setState({ selectedRow }))}
options={{
pageSize:pageSizeCalc,
maxBodyHeight:400,
paging:true,
filtering:this.props.filtering,
pageSizeOptions: pageSizeOptionsCalc,
//fixedColumns: {left: 1},
rowStyle: rowData => ({backgroundColor: (this.state.selectedRow && this.state.selectedRow.tableData.id === rowData.tableData.id) ? '#EEE' : '#FFF',maxHeight:100}),
headerStyle:{height:60,maxHeight:60,padding:0,overflow:'none',lineHeight:1,textAlign:'center',fontSize:'2.5vh'},
cellStyle:{height:100,overflowY:'scroll',verticalAlign:'top',fontSize:'2vh',overflow:'scroll',padding:1}
}}
/>
finally the computed css
Try to style cell with cellStyle property like:
const cellStyle = {
padding: '0 14px',
};
const [state, setState] = React.useState<TableState>({
columns: [
{ title: 'ID', field: 'id', cellStyle }...
]
...
})

How to removing the padding for card in and design?

I am using ant design to react UI components. I need to remove the padding given for the ant design card.
So I need to remove the padding given for the classes .ant-card-wider-padding and .ant-card-body.I am using JSS for styling the UI components.
cardStyle: {
marginTop: '30px',
boxShadow: '0px 1px 10px rgba(0,1,1,0.15)',
backgroundColor: '#ffffff',
borderStyle: 'solid',
outline: 'none',
width: '100%',
},
i am using cardStyle class to styling ant design card.Now i need to remove the padding in that card.
From the documentation of Ant Design
You need to override the style in bodyStyle not cardStyle
bodyStyle: Inline style to apply to the card content
<Card title="Card title" bodyStyle={{padding: "0"}}>Card content</Card>
use fullWidth props for removing padding..,
<Card.Section fullWidth>
<ResourceList
items={[
{
id: 341,
url: 'customers/341',
name: 'Mae Jemison',
location: 'Decatur, USA',
}
]}
renderItem={
(item) => {
const {id, url, name, location} = item;
const defaultImage = "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxMDAgMTAwIj48cGF0aCBkPSJNLS4wMi0uMDFoMTAwdjEwMGgtMTAweiIgZmlsbD0iI2ZmZTBjMyIvPjxwYXRoIGZpbGw9IiNmZjk2N2QiIGQ9Ik0wIDBoNjkuNDF2MTAwSDB6Ii8+PHBhdGggZD0iTTY5LjkyIDB2NDQuMzJMNTEuMzQgNTV2NDVIMTAwVjB6IiBmaWxsPSIjZmZlMGMzIi8+PHBhdGggZmlsbD0iIzMyY2FjNiIgZD0iTTM5LjMyIDc2YTExLjg1IDExLjg1IDAgMCAwIDEyIDExLjYyVjc2Ii8+PHBhdGggZmlsbD0iIzAwOTc5NiIgZD0iTTM5LjMyIDc2YTEyIDEyIDAgMCAxIDEyLTExLjgyVjc2Ii8+PHBhdGggZmlsbD0ibm9uZSIgc3Ryb2tlPSIjZmZmIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1taXRlcmxpbWl0PSIxMCIgc3Ryb2tlLXdpZHRoPSI1IiBkPSJNNDMuNzQgMTkuODNhMTIuODIgMTIuODIgMCAxIDEtMjUuNjQgMCIvPjxwYXRoIGZpbGw9Im5vbmUiIHN0cm9rZT0iI2ZmZiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbWl0ZXJsaW1pdD0iMTAiIHN0cm9rZS13aWR0aD0iNCIgZD0iTTI3LjM5IDMxLjZsLTEuNTggNS45Nm05LjM3LTUuNzJsMi41NSA1LjQ3bTQuMjYtOS44NWwzLjUzIDQuNW0tMjUuNDMtNC41bC0zLjUzIDQuNSIvPjwvc3ZnPgo=" ;
const media = <Thumbnail source={defaultImage} size="small" name={name} />;
return (
<ResourceList.Item id={id} url={url} media={media}>
<Stack alignment="center">
<Stack.Item fill>
<TextStyle>{name}</TextStyle>
</Stack.Item>
<Stack.Item>
<TextStyle>Last changed</TextStyle>
</Stack.Item>
<Stack.Item>
<Button>Edit Giffy</Button>
</Stack.Item>
</Stack>
</ResourceList.Item>
);
}
}
/>
</Card.Section>
very simple just add bodyStyle in Card Component
<Card bodyStyle={{ padding: "0"}}>
You can use this:
.cardStyle {
padding: 0;
}
If didn't work, use this:
.cardStyle {
padding: 0 !important;
}
I'm not too familiar with JSS but if your other styles are being applied then I assume the issue is not with that.
I was able to remove the padding from the card with the following code.
//style.less
.panelcard { ... }
.panelcard .ant-card-body {
padding: 0;
}
// panelCard.js
import { Card } from 'antd';
require('./style.less');
const PanelCard = ({ children }) => {
return (
<Card className='panelcard'>
{children} // <p>Some Child Component(s)</p>
</Card>
);
}
// invocation
<PanelCard label='Panel Title'>
<p>Some Child Component(s)</p>
</PanelCard>
This gave me the following output (card is the white box):
I am not sure if this is the preferred way of customizing antd's components but I didn't really find too much on antd's website about overriding styles, only on extending components.
Try using :global in you scss/less
div { // or any parent element/class
:global {
.ant-card-body {
passing: <number>px; // number can be 0 onwards
}
}
}

Resources