diff --git a/frontend/src/shared/components/ControlledInput/Input.stories.tsx b/frontend/src/shared/components/ControlledInput/Input.stories.tsx
new file mode 100644
index 0000000..32cd832
--- /dev/null
+++ b/frontend/src/shared/components/ControlledInput/Input.stories.tsx
@@ -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 (
+ <>
+
+
+
+
+
+
+ } width="100%" placeholder="Placeholder" />
+
+
+ >
+ );
+};
diff --git a/frontend/src/shared/components/ControlledInput/index.tsx b/frontend/src/shared/components/ControlledInput/index.tsx
new file mode 100644
index 0000000..342e602
--- /dev/null
+++ b/frontend/src/shared/components/ControlledInput/index.tsx
@@ -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) => void;
+ value?: string;
+ onClick?: (e: React.MouseEvent) => 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(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 (
+
+ {
+ 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 && {label}}
+ {icon && icon}
+
+ );
+};
+
+export default ControlledInput;
diff --git a/frontend/src/shared/components/Input/index.tsx b/frontend/src/shared/components/Input/index.tsx
index e997b5d..1857348 100644
--- a/frontend/src/shared/components/Input/index.tsx
+++ b/frontend/src/shared/components/Input/index.tsx
@@ -83,6 +83,7 @@ type InputProps = {
floatingLabel?: boolean;
placeholder?: string;
icon?: JSX.Element;
+ type?: string;
autocomplete?: boolean;
id?: string;
name?: string;
@@ -96,6 +97,7 @@ const Input = React.forwardRef(
{
width = 'auto',
variant = 'normal',
+ type = 'text',
autocomplete,
label,
placeholder,
@@ -125,6 +127,7 @@ const Input = React.forwardRef(
hasValue={hasValue}
ref={$ref}
id={id}
+ type={type}
name={name}
onClick={onClick}
autoComplete={autocomplete ? 'on' : 'off'}
diff --git a/frontend/src/shared/components/PopupMenu/LabelManager.tsx b/frontend/src/shared/components/PopupMenu/LabelManager.tsx
index 89f7ed5..edd5df9 100644
--- a/frontend/src/shared/components/PopupMenu/LabelManager.tsx
+++ b/frontend/src/shared/components/PopupMenu/LabelManager.tsx
@@ -22,24 +22,18 @@ type Props = {
};
const LabelManager: React.FC = ({ labels, taskLabels, onLabelToggle, onLabelEdit, onLabelCreate }) => {
- const $fieldName = useRef(null);
const [currentLabel, setCurrentLabel] = useState('');
const [currentSearch, setCurrentSearch] = useState('');
- useEffect(() => {
- if ($fieldName.current) {
- $fieldName.current.focus();
- }
- }, []);
return (
<>
{
- setCurrentSearch(e.currentTarget.value);
- }}
+ autoFocus
value={currentSearch}
+ variant="alternate"
+ width="100%"
+ onChange={e => setCurrentSearch(e.currentTarget.value)}
+ type="text"
+ placeholder="search labels..."
/>
Labels
diff --git a/frontend/src/shared/components/PopupMenu/Styles.ts b/frontend/src/shared/components/PopupMenu/Styles.ts
index 10febb5..4f797cd 100644
--- a/frontend/src/shared/components/PopupMenu/Styles.ts
+++ b/frontend/src/shared/components/PopupMenu/Styles.ts
@@ -1,5 +1,7 @@
import styled, { css } from 'styled-components';
import { mixin } from 'shared/utils/styles';
+import Input from '../Input';
+import ControlledInput from 'shared/components/ControlledInput';
export const Container = styled.div<{
invertY: boolean;
@@ -78,35 +80,13 @@ export const Content = styled.div`
max-height: 632px;
`;
-export const LabelSearch = styled.input`
- box-sizing: border-box;
- 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 LabelSearch = styled(ControlledInput)`
+ margin: 12px 12px 0 12px;
`;
export const Section = styled.div`
margin-top: 12px;
+ margin: 12px 12px 0 12px;
`;
export const SectionTitle = styled.h4`