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.
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>
);
}