React:Input label up in style component - html

I am using React-typescript in my app. For styling I am using Style-components. I am really new Style-components. Lots of syntax are new to me. I normally use css. I have decided I will convert my css into Style-component. So I can use it different components. I have created one Wrapper components where I put my Label with style components. I also created two components one is Input and another one is Textarea. I want to use Wrapper components both Input and Textarea components. I followed one youtube tutorial. Where I converted html and css into react-css. which works fine as expected. But I am struggling to convert react css into style-components. specially the focus and valid option. I really don't know how to use them. Also I am facing difficulties when label size vary. It takes sometimes too left or sometimes too right. Here is the Image:
.
I really don't know how to fix those issues.
This is my Youtube tutorial based on React component
import React from 'react'
import './new.css'
const Test = () => {
return (
<div className="main">
<div className="input-group">
<input type="text" id="firstname" className="inputField" autoComplete="off" required />
<label htmlFor="name" className="labels">First name</label>
</div>
</div>
)
}
export default Test;
This is the css
.main{
position: relative;
}
.inputField{
outline: none;
padding: 16px 22px;
border: 1px solid #dadce0;
font-size:18px;
border-radius:5px;
}
.inputField:focus{
border: 2px solid royalblue;
}
.labels{
color: #8d8d8d;
position: absolute;
top: 27px;
left: 55px;
background: #ffffff;
transition: 300ms;
transform: translate(-50%, -50%);
}
.inputField:valid + .labels{ // I don't know how to use this `Valid` in style-component
top: -1px;
padding: 0 3px;
font-size:14px;
color: #8d8d8d;
}
.inputField:focus + .labels { // I don't know how to use this `focus` in style-component
top: -1px;
padding: 0 3px;
font-size:14px;
color: royalblue;
transition: 300ms;
}
This is the wrapper component where I used Label styling with style-components and others requirements.
import React from 'react';
import styled from 'styled-components';
const Container = styled.div`
position: relative;
`
const Label = styled.label`
display: block;
color: #8d8d8d;
background: #ffffff;
position: absolute;
left: 50px;
top: 29px;
pointer-events: none;
transition: 300ms;
transform: translate(-50%, -50%);
`
const Error = styled.p`
color: red;
`
const Description = styled.p`
color: blue;
`
export interface IInputWrapperProps {
label?: string;
required?: boolean;
description?: string;
error?: string;
wrapperStyle?: React.CSSProperties;
children?: JSX.Element;
}
export default ({
label, error, description, children, required, wrapperStyle
}: IInputWrapperProps) => {
return (
<ErrorBoundary id="InputWrapperErrorBoundary">
<div style={wrapperStyle}>
<Container>
<Label>
{label} {required && <span style={{ color: 'red' }}> *</span>}
</Label>
{children}
</Container>
{description && <Description>{description}</Description>}
{error && <Error>{error}</Error>}
</div>
</ErrorBoundary>
);
}
This is my Input component styling with style-componenents
import React, { useState, memo } from "react";
import styled from "styled-components";
import InputWrapper, { IInputWrapperProps } from "../wrapper";
import { Label } from "../wrapper"
const Input = styled.input.attrs(() => ({
className: 'text-input'
}))`
border: 1px solid #dadce0;
background: #fdfdfd;
font-size:18px;
border-radius: 5px;
box-shadow: none;
padding: 16px 30px;
outline: none;
&:focus {
border: 2px solid royalblue;
}
${Label}:focus & {
top: -12px;
padding: 0 3px;
font-size:14px;
color: royalblue;
transition: 300ms;
}
`
export interface ITextInputProps extends IInputWrapperProps {
value: string | undefined;
onChange: (i: string) => void;
}
export default memo(({
value, onChange, ...wrapperProps
}: ITextInputProps) => {
return (
<InputWrapper
{...wrapperProps}
>
<div>
<Input
value={value}
onChange={e => onChange(e.target.value)}
/>
</div>
</InputWrapper>
)
});
This is the parent component where I am rendering those two input fields
<InputTesting
label="Frist Name"
value={undefined}
onChange={(i) => console.log(i)}
/>
<br></br>
<br></br>
<InputTesting
label="Hello"
value={undefined}
onChange={(i) => console.log(i)}
/>

Before I answer the question, I need to say two things: first, I couldn't understand so much about your styled-components code, so I'll just transform your react/CSS code to react-typescript/styled-component. Second, I didn't get what you mean by "difficulties when label size varies", so I'm not focusing on this in the answer. That been said, let's go to the code.
Input.tsx
import React from 'react';
import styled from 'styled-components';
interface InputProps extends React.InputHTMLAttributes<HTMLInputElement>{
id: string;
label: string;
}
const InputGroup = styled.div`
position: relative;
`;
const InputLabel = styled.label`
color: #8d8d8d;
position: absolute;
top: 27px;
left: 55px;
background: #ffffff;
transition: 300ms;
transform: translate(-50%, -50%);
`;
const InputField = styled.input`
outline: none;
padding: 16px 22px;
border: 1px solid #dadce0;
font-size: 18px;
border-radius: 5px;
&:focus
{
border: 2px solid royalblue;
}
&:valid + ${InputLabel}
{
top: -1px;
padding: 0 3px;
font-size:14px;
color: #8d8d8d;
}
&:focus + ${InputLabel}
{
top: -1px;
padding: 0 3px;
font-size:14px;
color: royalblue;
transition: 300ms;
}
`;
const Input: React.FC<InputProps> = ({ id, label, ...rest }) => {
return (
<InputGroup>
<InputField id={id} {...rest} />
<InputLabel htmlFor={id} >{label}</InputLabel>
</InputGroup>
);
}
export default Input;
The idea here is to create an input component that already has a label inside it. Initially, I create an interface called InputProps which extends all attributes from a common InputElement and forces the parent component to put an id and a label attribute.
Them I used styled-components to copy all styles from the youtube guy. To put pseudo-classes on styled-components you have to use a sass' feature called parent selector. Using the ampersand, you can refer to the parent selector. Another thing you have to use is styled-components reference. This feature will allow you to refer to the styled-label in the code.
Now that you've created the Input component, let's use it on the App.
App.tsx
import React from 'react';
import Input from './Input';
function App() {
return (
<>
<Input required type="text" label="First Name" id="first-name" />
<Input required type="text" label="Hello" id="hello" />
</>
);
}
export default App;
This is the final result.

Related

TextArea dont auto-resize correct when starting on one row

I having some trouble to get a textarea to auto expand from one row.
It works when I have 3 or more rows and one row, but the two first looks weird.
Can't find a good way of knowing when the text is clipped like that. Because the lenght of the input will be dynamic.
I have seen solutions that kind of work here on Stackoverflow, but I must have the total height of the input to start at 40px; And the solutions I have seen cant handle that case.
(Dont mind the black-border for the textarea, for some reason codeSandBox shows it but not for me.)
I have a codeSandbox that shows the issue:
https://codesandbox.io/s/affectionate-yalow-fnqnep?file=/src/App.js:241-296
Anyone had a similliar issue or could point me in the right direction?
Edit:
Working React component:
import React, { useState, useEffect } from "react";
import styled from "styled-components";
const InputWrapper = styled.div`
display: inline-flex;
justify-content: flex-start;
align-items: center;
width: 100%;
padding: 8px 16px;
border-radius: 6px;
background-color: white;
border: 1px solid black;
`;
const StyledTextArea = styled.textarea`
width: 100%;
max-height: 300px;
font-size: 16px;
background-color: white;
line-height: 18px;
resize: none;
transition: all 300ms;
border: none;
overflow: hidden;
`;
const TextArea = ({ value, placeholder, handleEvent }) => {
const [textAreaRef, setTextAreaRef] = useState(React.createRef());
const updateHeight = () => {
const updatedTextArea = { ...textAreaRef };
updatedTextArea.current.style.height = "";
updatedTextArea.current.style.height = `${updatedTextArea.current.scrollHeight}px`;
setTextAreaRef(updatedTextArea);
};
useEffect(() => updateHeight(), [value]);
return (
<InputWrapper>
<StyledTextArea
ref={textAreaRef}
value={value}
rows={1}
placeholder={placeholder}
onChange={handleEvent}
/>
</InputWrapper>
);
};
export default TextArea;

Why is Text After Image, Not Inline (React) + Why Won't Hover Line Appear?

Currently trying (and failing) to learn React for a project, and not understanding why the header links appear after the image, if they're in the same wrapper. I made different components for different parts of the navbar, and made a different file for the Logo and NavLinks (each in its own section). Here's the code.
App.js
// Importing NavBar
import NavBar from './components/navbar/NavBar';
// Actual App function, has our code
function App() {
return (
<div className="App">
{/* Navbar Declaration, with statement of what links to add */}
<NavBar />
</div>
);
}
export default App;
NavBar.js
// Importing react
import React from "react";
// Importing styled to be able to style the page
import styled from "styled-components";
// Importing the logo
import Logo from "../logo/Logo";
// Importing the links
import NavLinks from "./NavLinks.js";
// ---------------------------- Stylizing the navbar using styled-components
// Main Wrapper for Navbar
const Wrapper = styled.div`
width: 100%;
height: 10rem;
align-items: center;
padding: 0 1.5 rem;
transition: background-color .5s ease;
z-index: 9999;
border-bottom: 2px solid rgba(255,255,255,.05);
margin-left: 50px;
margin-right: 50px;
`;
// NavBar is separated into left, center and right
// Left side of Navbar
const LeftSide = styled.div`
display: flex;
`;
// Center of Navbar
// Flex is a way to define how much each portion is gonna take of the size given to it
const Center = styled.div`
display: flex;
`;
// Right side of Navbar
const RightSide = styled.div`
display: flex;
`;
// Declaration of navbar links
const navbarLinks = [
"Home Page",
"Illustrator Gallery",
"Art Gallery",
"Challenges"
];
/*const navbarLinks = [
{ title: `Home Page`, path: `/` },
{ title: `Illustrator Gallery`, path: `/illustrator-gallery` },
{ title: `Art Gallery`, path: `/art-gallery` },
{ title: `Challenges`, path: `/challenges` }
];*/
// ---------------------------- Creating the NavBar function/component
function NavBar(props) {
// Setting the return value, or the component
return(
<Wrapper>
{/* For the left side, we want to import the Logo component */}
<LeftSide>
<Logo />
</LeftSide>
<Center>
{/* For the middle, we want to add the different links */}
<NavLinks links={navbarLinks} />
</Center>
<RightSide></RightSide>
</Wrapper>
);
}
export default NavBar;
Logo.js
// Importing react
import React from 'react';
// Importing styled components
import styled from "styled-components";
// Importing the image
import USLogo from "../../assets/images/bunny.png"
// Styling the wrapper for the logo
const LogoWrapper = styled.div`
display: flex;
align-items: center;
`;
// Styling the actual logo, as well as its container
const LogoImg = styled.div`
width: 50px;
height: 50px;
img {
width: 100%;
height: 100%;
}
`;
// Styling the information next to the logo
const LogoText = styled.h2`
text-transform: uppercase;
font-size: 3rem;
font-weight: bold;
margin-left: 4px;
padding-top: 8px;
color: black;
`;
// Creating logo
function Logo(props) {
return(
// First we make the wrapper
<LogoWrapper>
{/* Inside the wrapper we'll have the image, then the text */}
<LogoImg><img src={USLogo} alt="US. logo"/></LogoImg>
<LogoText>US.</LogoText>
</LogoWrapper>
);
}
export default Logo;
NavLinks.js
// Importing react
import React from 'react';
// Importing styling
import styled from 'styled-components';
// Styling the container for the links
const LinksContainer = styled.div`
display: flex;
align-items: center;
`;
// Styling the ul components, or the menu
const LinksMenu = styled.ul`
display: inline-block;
text-transform: uppercase;
letter-spacing: 3px;
`;
// Styling each li
const LinksItem = styled.li`
display: inline-block;
vertical-align: top;
align-items: center;
justify-content: space-between;
margin-top: 15px;
padding: 0 1.1rem;
`;
// Styling each link
const Link = styled.a`
text-decoration: none;
color: black;
font-size: 1.6rem;
margin: 0 2rem;
position: relative;
&:hover{
color: rgb(24, 23, 23);
}
&::after {
content: '';
width: 100%;
height: 2px;
background-color: black;
left: 0;
bottom: -3px;
transform: scaleX(0);
transform-origin: left;
transition transform .5s ease;
}
&:hover::after{
transform: scaleX(1);
}
`;
// Creating the navigation links component
function NavLinks(props) {
return(
// First we do the container
<LinksContainer>
{/* Inside the container, we have the menu of links, then the li, and finally links */}
<LinksMenu>
{/* Each link Menu has a bunch of items, with each item having a link */}
{
props.links.map(
(label) => {
return(
<LinksItem><Link>{label}</Link></LinksItem>
)
}
)
}
</LinksMenu>
</LinksContainer>
);
}
export default NavLinks;
The result shows as follows:
Additionally, if I hover over the text, it doesn't change anything. In fact, it doesn't even add the little blue color indicating it's a link anymore. I'm not sure if this is a syntactical problem, since the same code works fine when using css.
I've checked other answers on here suggesting float: left and the like for the wrapper, but for some reason they don't work. Either I'm putting them in the wrong object, or they should be somewhere else.
Any help is appreciated!

How to add line break to custom tooltip directive using angular 7

I am new to angular 7 directives. I created a custom tooltip directive using angular 7. Now, I am unable to specify line breaks when i pass the tooltip text from my html. I want a line break after the Tootltip Title text I pass from html. Any idea how to achieve this?
Tried to pass
and 
 code for line break in my input string to the tooltip directive. Didn't work either.
Here is what I tried so far.
My directive : tooltip.directive.ts:
import { Directive, Input, ElementRef, HostListener, Renderer2 } from '#angular/core';
#Directive({
selector: '[tooltip]'
})
export class TooltipDirective {
#Input('tooltip') tooltipTitle: string;
#Input() placement: string;
#Input() delay: number;
tooltip: HTMLElement;
offset = 10;
constructor(private el: ElementRef, private renderer: Renderer2) { }
#HostListener('mouseenter') onMouseEnter() {
if (!this.tooltip) { this.show(); }
}
#HostListener('mouseleave') onMouseLeave() {
if (this.tooltip) { this.hide(); }
}
show() {
this.create();
this.setPosition();
this.renderer.addClass(this.tooltip, 'ng-tooltip-show');
}
hide() {
this.renderer.removeClass(this.tooltip, 'ng-tooltip-show');
window.setTimeout(() => {
this.renderer.removeChild(document.body, this.tooltip);
this.tooltip = null;
}, 0);
}
create() {
this.tooltip = this.renderer.createElement('span');
this.renderer.appendChild(
this.tooltip,
this.renderer.createText(this.tooltipTitle) // textNode
);
this.renderer.appendChild(document.body, this.tooltip);
this.renderer.addClass(this.tooltip, 'ng-tooltip');
this.renderer.addClass(this.tooltip, `ng-tooltip-${this.placement}`);
}
setPosition() {
const hostPos = this.el.nativeElement.getBoundingClientRect();
const tooltipPos = this.tooltip.getBoundingClientRect();
let top, left;
if (this.placement === 'top') {
top = hostPos.top - tooltipPos.height - this.offset;
left = hostPos.left + (hostPos.width - tooltipPos.width) / 2;
}
/* other positions to be added */
this.renderer.setStyle(this.tooltip, 'top', `${top}px`);
this.renderer.setStyle(this.tooltip, 'left', `${left}px`);
}
}
CSS :
/* Styles for tooltip */
.ng-tooltip {
position: absolute;
max-width: 150px;
font-size: 14px;
text-align: center;
color: #f8f8f2;
padding: 3px 8px;
border-radius: 2px;
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.5);
background-color: #38393b;
z-index: 1000;
opacity: 0;
}
.ng-tooltip:after {
content: "";
position: absolute;
border-style: solid;
}
.ng-tooltip-top:after {
top: 100%;
left: 50%;
margin-left: -5px;
border-width: 5px;
border-color: #38393b transparent transparent transparent;
}
.ng-tooltip-show {
opacity: 1;
}
From the html file, I invoke the tooltip directive by passing the text like this:
<div tooltip="Title: ( want a line break here) 
 {{someVariable}}" placement="top" class="remark">Hover Here</div>
This can be resolved by taking any of the approach,
Either create two different input such as #Input('Title'): string and #Input('Body'): string and pass into two different parameters.
OR
if you want to pass it in a single parameter make a use of interface such as
export interface IToolTip {
title?: string;
body: string;
footer?: string;
}
Assign this interface to your tooltip variable #Input('tooltip') tooltipTitle: ITooltip; Rest of the things can taken care under create() funciton.
Thanks.

Responsive input component margin

I have input component that takes full size based on maxWidth property and can shrink on screen size changes.
Problem is that margin is not consistent with maxWidth in pixels, but ok if input is 100%. Also centering input have it's own padding problems, but i think it's the same problem.
How to limit max-width of input with respect of paddings and save flexibility to shrink?
Codesandbox (you can fork it and change).
import * as React from "react";
import styled from "styled-components";
interface Props extends React.ComponentPropsWithoutRef<"input"> {
maxWidth?: string;
}
const Input = React.forwardRef<HTMLInputElement, Props>((props, ref) => {
return <StyledInput ref={ref} {...props} />;
});
const defPadding = 5;
const StyledInput = styled.input<Props>`
box-sizing: content-box;
margin: 5px;
vertical-align: middle;
padding: ${defPadding}px 0;
box-shadow: inset 0 0 0 1px blue;
border-radius: 5px;
border: none;
outline: none;
font: inherit;
max-width: calc(
${({ maxWidth }): string => maxWidth || "100%"} - ${defPadding * 2}px
);
width: 100%;
text-align: center;
`;
export default function App() {
return (
<div className="App">
<div>
<Input />
<Input maxWidth="1000px" />
</div>
<div
style={{
display: "flex",
flexDirection: "column",
alignItems: "center",
justifyContent: "center"
}}
>
<Input />
<Input maxWidth="1000px" />
</div>
</div>
);
}
If I understand the question right, the issue is not about paddings but those side 5-pixel-margins instead.
You can update the styles slightly so that
<Input maxWidth="1000px" /> extends as much as possible, but not beyond 1000px. The end CSS result should be:
width: 1000px;
max-width: calc(100% - 10px);
In your component styles try:
const StyledInput = styled.input<Props>`
box-sizing: content-box;
margin: 5px;
vertical-align: middle;
padding: ${defPadding}px 0;
box-shadow: inset 0 0 0 1px blue;
border-radius: 5px;
border: none;
outline: none;
font: inherit;
max-width: calc(100% - ${defPadding * 2}px);
width: ${({ maxWidth }): string => maxWidth || "auto"};
text-align: center;
`;
If this is your goal, maybe the margin: 5px should also use the defPadding variable.

Unable to add background image using semantic-ui-react in a component

I have tried to add background Image in my application using inline CSS styling but I'm unable to add in semantic-ui-react component and also the div.
Tried by backgroundImage:url(${https://wallpapercave.com/wp/wp2449777.png}) using this in my div and also the component called in semantic-ui-react
import React,{Component} from 'react';
import {Container,Image,Segment} from 'semantic-ui-react';
import Certificate from '../ethereum/certificate';
import web3 from '../ethereum/web3';
class certificateHere extends Component{
static async getInitialProps(props){
const numberofCertificates = await Certificate.methods.getCertificateCount().call();
const recentCertificate = await Certificate.methods.certificates(numberofCertificates-1).call();
return { numberofCertificates , recentCertificate};
}
render(){
const { numberofCertificates , recentCertificate}= this.props;
return (
<div className='main'
style={{
textAlign:'center',
backgroundImage:`url(${https://wallpapercave.com/wp/wp2449777.png})`
}}
>
<Segment >
<div className='sub'>
<h1><b>Blockchain Certification</b></h1>
<h3 >This is to certify that</h3><br/>
<p><b>{recentCertificate.CandidateName} has successfully completed his {recentCertificate.CourseName} developer Program which<br/>
is of 14 hrs online course: Build projects using solidity on {recentCertificate.DateOfCompletion}.</b></p>
</div>
<Image src='' size='small' style={{ marginLeft: 'auto', marginRight: 'auto'}} />
<div style={{ position: 'absolute',
bottom: '8px',
left: '16px'}}
>
<h4 className='issued'style={{textAlign:'left', textDecoration: 'underline'}}>Issued by:{recentCertificate.InstituteName}</h4>
<h4 className='location'style={{textAlign:'left',textDecoration: 'underline'}}>Location:{recentCertificate.Location}</h4>
</div>
<div style={{ position: 'absolute',
bottom: '8px',
right: '16px'}}>
<h4 className='issuer'style={{textAlign:'right',textDecoration: 'underline'}}>Issuer Name:{recentCertificate.IssuerName}</h4></div>
<style jsx>{`
h1 {
color: orange;
font-style: oblique;
font-size: 50px;
}
h3{
font-size: 40px;
color:orange;
padding-top: 25px;
}
p{
font-size: 20px;
color: orange;
padding:30px;
}
h4.issued{
color: orange;
padding-bottom: 25px;
}
h4.location{
padding-bottom: 100px;
}
h4.issuer{
padding-bottom: 100px;
}
.main{
backgroundColor:green;
}
`}</style>
</Segment>
</div>
);
}
}
export default certificateHere;
I just want the background image for this component covering the entire page.
Try using backgroundImage: ‘url(https://wallpapercave.com/wp/wp2449777.png)’