feature: fix label manager popup padding & switch input to ControlledInput
This commit is contained in:
parent
2cf6be082c
commit
8c610b11bf
@ -0,0 +1,43 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import BaseStyles from 'App/BaseStyles';
|
||||||
|
import NormalizeStyles from 'App/NormalizeStyles';
|
||||||
|
import { theme } from 'App/ThemeStyles';
|
||||||
|
import styled, { ThemeProvider } from 'styled-components';
|
||||||
|
|
||||||
|
import Input from '.';
|
||||||
|
import { User } from 'shared/icons';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
component: Input,
|
||||||
|
title: 'Input',
|
||||||
|
parameters: {
|
||||||
|
backgrounds: [
|
||||||
|
{ name: 'white', value: '#ffffff', default: true },
|
||||||
|
{ name: 'gray', value: '#f8f8f8' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const Wrapper = styled.div`
|
||||||
|
background: rgba(${props => props.theme.colors.bg.primary});
|
||||||
|
padding: 45px;
|
||||||
|
margin: 25px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const Default = () => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<NormalizeStyles />
|
||||||
|
<BaseStyles />
|
||||||
|
<ThemeProvider theme={theme}>
|
||||||
|
<Wrapper>
|
||||||
|
<Input label="Label placeholder" />
|
||||||
|
<Input width="100%" placeholder="Placeholder" />
|
||||||
|
<Input icon={<User size={20} />} width="100%" placeholder="Placeholder" />
|
||||||
|
</Wrapper>
|
||||||
|
</ThemeProvider>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
155
frontend/src/shared/components/ControlledInput/index.tsx
Normal file
155
frontend/src/shared/components/ControlledInput/index.tsx
Normal file
@ -0,0 +1,155 @@
|
|||||||
|
import React, { useState, useEffect, useRef } from 'react';
|
||||||
|
import styled, { css } from 'styled-components/macro';
|
||||||
|
|
||||||
|
const InputWrapper = styled.div<{ width: string }>`
|
||||||
|
position: relative;
|
||||||
|
width: auto;
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
flex-direction: column;
|
||||||
|
position: relative;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
margin-bottom: 2.2rem;
|
||||||
|
margin-top: 24px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const InputLabel = styled.span<{ width: string }>`
|
||||||
|
width: ${props => props.width};
|
||||||
|
padding: 0.7rem !important;
|
||||||
|
color: #c2c6dc;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
position: absolute;
|
||||||
|
border-radius: 5px;
|
||||||
|
overflow: hidden;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
cursor: text;
|
||||||
|
font-size: 12px;
|
||||||
|
user-select: none;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const InputInput = styled.input<{
|
||||||
|
hasValue: boolean;
|
||||||
|
hasIcon: boolean;
|
||||||
|
width: string;
|
||||||
|
focusBg: string;
|
||||||
|
borderColor: string;
|
||||||
|
}>`
|
||||||
|
width: ${props => props.width};
|
||||||
|
font-size: 14px;
|
||||||
|
border: 1px solid rgba(0, 0, 0, 0.2);
|
||||||
|
border-color: ${props => props.borderColor};
|
||||||
|
background: #262c49;
|
||||||
|
box-shadow: 0 0 0 0 rgba(0, 0, 0, 0.15);
|
||||||
|
${props => (props.hasIcon ? 'padding: 0.7rem 1rem 0.7rem 3rem;' : 'padding: 0.7rem;')}
|
||||||
|
line-height: 16px;
|
||||||
|
color: #c2c6dc;
|
||||||
|
position: relative;
|
||||||
|
border-radius: 5px;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
&:focus {
|
||||||
|
box-shadow: 0 3px 10px 0 rgba(0, 0, 0, 0.15);
|
||||||
|
border: 1px solid rgba(115, 103, 240);
|
||||||
|
background: ${props => props.focusBg};
|
||||||
|
}
|
||||||
|
&:focus ~ ${InputLabel} {
|
||||||
|
color: rgba(115, 103, 240);
|
||||||
|
transform: translate(-3px, -90%);
|
||||||
|
}
|
||||||
|
${props =>
|
||||||
|
props.hasValue &&
|
||||||
|
css`
|
||||||
|
& ~ ${InputLabel} {
|
||||||
|
color: rgba(115, 103, 240);
|
||||||
|
transform: translate(-3px, -90%);
|
||||||
|
}
|
||||||
|
`}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const Icon = styled.div`
|
||||||
|
display: flex;
|
||||||
|
left: 16px;
|
||||||
|
position: absolute;
|
||||||
|
`;
|
||||||
|
|
||||||
|
type ControlledInputProps = {
|
||||||
|
variant?: 'normal' | 'alternate';
|
||||||
|
label?: string;
|
||||||
|
width?: string;
|
||||||
|
floatingLabel?: boolean;
|
||||||
|
placeholder?: string;
|
||||||
|
icon?: JSX.Element;
|
||||||
|
type?: string;
|
||||||
|
autocomplete?: boolean;
|
||||||
|
autoFocus?: boolean;
|
||||||
|
id?: string;
|
||||||
|
name?: string;
|
||||||
|
className?: string;
|
||||||
|
defaultValue?: string;
|
||||||
|
onChange?: (e: React.ChangeEvent<HTMLInputElement>) => void;
|
||||||
|
value?: string;
|
||||||
|
onClick?: (e: React.MouseEvent<HTMLInputElement>) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
const ControlledInput = ({
|
||||||
|
width = 'auto',
|
||||||
|
variant = 'normal',
|
||||||
|
type = 'text',
|
||||||
|
autocomplete,
|
||||||
|
autoFocus = false,
|
||||||
|
label,
|
||||||
|
placeholder,
|
||||||
|
icon,
|
||||||
|
name,
|
||||||
|
className,
|
||||||
|
onChange,
|
||||||
|
value,
|
||||||
|
onClick,
|
||||||
|
floatingLabel = false,
|
||||||
|
defaultValue,
|
||||||
|
id,
|
||||||
|
}: ControlledInputProps) => {
|
||||||
|
const $input = useRef<HTMLInputElement>(null);
|
||||||
|
const [hasValue, setHasValue] = useState(false);
|
||||||
|
const borderColor = variant === 'normal' ? 'rgba(0, 0, 0, 0.2)' : '#414561';
|
||||||
|
const focusBg = variant === 'normal' ? 'rgba(38, 44, 73, )' : 'rgba(16, 22, 58, 1)';
|
||||||
|
useEffect(() => {
|
||||||
|
if (autoFocus && $input && $input.current) {
|
||||||
|
$input.current.focus();
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
return (
|
||||||
|
<InputWrapper className={className} width={width}>
|
||||||
|
<InputInput
|
||||||
|
hasValue={hasValue}
|
||||||
|
onChange={e => {
|
||||||
|
if (onChange) {
|
||||||
|
setHasValue(e.currentTarget.value !== '' || floatingLabel);
|
||||||
|
onChange(e);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
value={value}
|
||||||
|
id={id}
|
||||||
|
type={type}
|
||||||
|
name={name}
|
||||||
|
ref={$input}
|
||||||
|
onClick={onClick}
|
||||||
|
autoComplete={autocomplete ? 'on' : 'off'}
|
||||||
|
defaultValue={defaultValue}
|
||||||
|
hasIcon={typeof icon !== 'undefined'}
|
||||||
|
width={width}
|
||||||
|
placeholder={placeholder}
|
||||||
|
focusBg={focusBg}
|
||||||
|
borderColor={borderColor}
|
||||||
|
/>
|
||||||
|
{label && <InputLabel width={width}>{label}</InputLabel>}
|
||||||
|
<Icon>{icon && icon}</Icon>
|
||||||
|
</InputWrapper>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ControlledInput;
|
@ -83,6 +83,7 @@ type InputProps = {
|
|||||||
floatingLabel?: boolean;
|
floatingLabel?: boolean;
|
||||||
placeholder?: string;
|
placeholder?: string;
|
||||||
icon?: JSX.Element;
|
icon?: JSX.Element;
|
||||||
|
type?: string;
|
||||||
autocomplete?: boolean;
|
autocomplete?: boolean;
|
||||||
id?: string;
|
id?: string;
|
||||||
name?: string;
|
name?: string;
|
||||||
@ -96,6 +97,7 @@ const Input = React.forwardRef(
|
|||||||
{
|
{
|
||||||
width = 'auto',
|
width = 'auto',
|
||||||
variant = 'normal',
|
variant = 'normal',
|
||||||
|
type = 'text',
|
||||||
autocomplete,
|
autocomplete,
|
||||||
label,
|
label,
|
||||||
placeholder,
|
placeholder,
|
||||||
@ -125,6 +127,7 @@ const Input = React.forwardRef(
|
|||||||
hasValue={hasValue}
|
hasValue={hasValue}
|
||||||
ref={$ref}
|
ref={$ref}
|
||||||
id={id}
|
id={id}
|
||||||
|
type={type}
|
||||||
name={name}
|
name={name}
|
||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
autoComplete={autocomplete ? 'on' : 'off'}
|
autoComplete={autocomplete ? 'on' : 'off'}
|
||||||
|
@ -22,24 +22,18 @@ type Props = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const LabelManager: React.FC<Props> = ({ labels, taskLabels, onLabelToggle, onLabelEdit, onLabelCreate }) => {
|
const LabelManager: React.FC<Props> = ({ labels, taskLabels, onLabelToggle, onLabelEdit, onLabelCreate }) => {
|
||||||
const $fieldName = useRef<HTMLInputElement>(null);
|
|
||||||
const [currentLabel, setCurrentLabel] = useState('');
|
const [currentLabel, setCurrentLabel] = useState('');
|
||||||
const [currentSearch, setCurrentSearch] = useState('');
|
const [currentSearch, setCurrentSearch] = useState('');
|
||||||
useEffect(() => {
|
|
||||||
if ($fieldName.current) {
|
|
||||||
$fieldName.current.focus();
|
|
||||||
}
|
|
||||||
}, []);
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<LabelSearch
|
<LabelSearch
|
||||||
type="text"
|
autoFocus
|
||||||
ref={$fieldName}
|
|
||||||
placeholder="search labels..."
|
|
||||||
onChange={e => {
|
|
||||||
setCurrentSearch(e.currentTarget.value);
|
|
||||||
}}
|
|
||||||
value={currentSearch}
|
value={currentSearch}
|
||||||
|
variant="alternate"
|
||||||
|
width="100%"
|
||||||
|
onChange={e => setCurrentSearch(e.currentTarget.value)}
|
||||||
|
type="text"
|
||||||
|
placeholder="search labels..."
|
||||||
/>
|
/>
|
||||||
<Section>
|
<Section>
|
||||||
<SectionTitle>Labels</SectionTitle>
|
<SectionTitle>Labels</SectionTitle>
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
import styled, { css } from 'styled-components';
|
import styled, { css } from 'styled-components';
|
||||||
import { mixin } from 'shared/utils/styles';
|
import { mixin } from 'shared/utils/styles';
|
||||||
|
import Input from '../Input';
|
||||||
|
import ControlledInput from 'shared/components/ControlledInput';
|
||||||
|
|
||||||
export const Container = styled.div<{
|
export const Container = styled.div<{
|
||||||
invertY: boolean;
|
invertY: boolean;
|
||||||
@ -78,35 +80,13 @@ export const Content = styled.div`
|
|||||||
max-height: 632px;
|
max-height: 632px;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const LabelSearch = styled.input`
|
export const LabelSearch = styled(ControlledInput)`
|
||||||
box-sizing: border-box;
|
margin: 12px 12px 0 12px;
|
||||||
display: block;
|
|
||||||
transition-property: background-color, border-color, box-shadow;
|
|
||||||
transition-duration: 85ms;
|
|
||||||
transition-timing-function: ease;
|
|
||||||
margin: 4px 0 12px;
|
|
||||||
width: 100%;
|
|
||||||
border: 1px solid rgba(0, 0, 0, 0.1);
|
|
||||||
border-radius: 3px;
|
|
||||||
line-height: 20px;
|
|
||||||
padding: 8px 12px;
|
|
||||||
font-size: 14px;
|
|
||||||
font-family: 'Droid Sans';
|
|
||||||
font-weight: 400;
|
|
||||||
|
|
||||||
background: #262c49;
|
|
||||||
outline: none;
|
|
||||||
color: #c2c6dc;
|
|
||||||
border-color: #414561;
|
|
||||||
|
|
||||||
&:focus {
|
|
||||||
box-shadow: rgb(115, 103, 240) 0px 0px 0px 1px;
|
|
||||||
background: ${mixin.darken('#262c49', 0.15)};
|
|
||||||
}
|
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const Section = styled.div`
|
export const Section = styled.div`
|
||||||
margin-top: 12px;
|
margin-top: 12px;
|
||||||
|
margin: 12px 12px 0 12px;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const SectionTitle = styled.h4`
|
export const SectionTitle = styled.h4`
|
||||||
|
Loading…
Reference in New Issue
Block a user