diff --git a/src/lib/box-styles.tsx b/src/lib/box-styles.tsx index 2311685f..e19b9260 100644 --- a/src/lib/box-styles.tsx +++ b/src/lib/box-styles.tsx @@ -269,3 +269,10 @@ export const ColumnContent = styled.div` export const TableContent = styled.div` padding: 10px; `; + +export const Form = styled.form` + width: 100%; + & > div { + margin-bottom: 10px; + } +`; diff --git a/src/ui/atoms/input/index.tsx b/src/ui/atoms/input/index.tsx index b8f32c98..f8877175 100644 --- a/src/ui/atoms/input/index.tsx +++ b/src/ui/atoms/input/index.tsx @@ -5,6 +5,7 @@ import { Variant } from 'lib/types'; import { InputContainer, InputElement } from '../../elements/quarks'; interface InputProps extends React.InputHTMLAttributes { + autoComplete?: 'on' | 'off'; className?: string; disabled?: boolean; leftIcon?: React.ReactNode; @@ -17,6 +18,7 @@ interface InputProps extends React.InputHTMLAttributes { } const InputBase: React.FC = ({ + autoComplete = 'off', className, disabled = false, leftIcon, @@ -37,6 +39,7 @@ const InputBase: React.FC = ({ variant={variant} > = ({ ); export const Input = styled(InputBase)` - --local-vertical: calc(1px * var(--woly-component-level) * var(--woly-main-level)); - --local-horizontal: calc( - var(--woly-const-m) + (1px * var(--woly-main-level)) + var(--local-vertical) - ); - box-sizing: border-box; width: 100%; diff --git a/src/ui/atoms/input/usage.mdx b/src/ui/atoms/input/usage.mdx index 9f1c6ecf..cb795bc3 100644 --- a/src/ui/atoms/input/usage.mdx +++ b/src/ui/atoms/input/usage.mdx @@ -7,7 +7,7 @@ package: 'woly' import { Input } from './index'; import { InfoIcon } from 'icons'; -import { Playground, block, State } from 'box-styles' +import { Playground, block, State, Form } from 'box-styles' import { ButtonIcon } from '../../atoms'; @@ -54,13 +54,15 @@ Specs: ### Example - console.info('On input change')} - placeholder="Enter your name here" - type="text" - variant="primary" - /> +
+ console.info('On input change')} + placeholder="Enter your name here" + type="text" + variant="primary" + /> +
### Disabled @@ -68,23 +70,25 @@ Specs: Disabled text fields are uneditable. They have less opacity so that they appear less tappable. - console.info('On input change')} - placeholder="Enter your name here" - type="text" - variant="primary" - /> - } - name="name" - onChange={(event) => console.info('On input change')} - placeholder="Enter your name here" - type="text" - variant="primary" - /> +
+ console.info('On input change')} + placeholder="Enter your name here" + type="text" + variant="primary" + /> + } + name="name" + onChange={(event) => console.info('On input change')} + placeholder="Enter your name here" + type="text" + variant="primary" + /> +
### Icons @@ -92,107 +96,117 @@ Disabled text fields are uneditable. They have less opacity so that they appear Inputs can be combined with an icon on the left side or the right. Use icons on the both sides is not recommended. - } - type="text" - name="name" - placeholder="Enter your name here" - variant="primary" - onChange={(event) => console.info('On input change')} - /> - console.info('On input change')} - rightIcon={} - /> - } - type="text" - name="name" - placeholder="Enter your name here" - variant="primary" - onChange={(event) => console.info('On input change')} - rightIcon={} - /> +
+ } + type="text" + name="name" + placeholder="Enter your name here" + variant="primary" + onChange={(event) => console.info('On input change')} + /> + console.info('On input change')} + rightIcon={} + /> + } + type="text" + name="name" + placeholder="Enter your name here" + variant="primary" + onChange={(event) => console.info('On input change')} + rightIcon={} + /> +
Icons can also be touch targets for nested components. - - } - onClick={() => console.info('ButtonIcon clicked')} - variant="primary" - /> - - } - type="text" - name="name" - placeholder="Enter your name here" - onChange={(event) => console.info('On input change')} - variant="primary" - /> - } - type="text" - name="name" - placeholder="Enter your name here" - onChange={(event) => console.info('On input change')} - variant="primary" - rightIcon={ - - } - onClick={() => console.info('ButtonIcon clicked')} - variant="primary" - /> - - } - /> +
+ + } + onClick={() => console.info('ButtonIcon clicked')} + variant="primary" + /> + + } + type="text" + name="name" + placeholder="Enter your name here" + onChange={(event) => console.info('On input change')} + variant="primary" + /> + } + type="text" + name="name" + placeholder="Enter your name here" + onChange={(event) => console.info('On input change')} + variant="primary" + rightIcon={ + + } + onClick={() => console.info('ButtonIcon clicked')} + variant="primary" + /> + + } + /> +
### Variants -Primary and danger variants are should be used to focus user attention. +Primary and secondary variants are should be used to focus user attention. + + - } - name="name" - onChange={(event) => console.info('On input change')} - placeholder="Primary input" - type="text" - value="Primary" - variant="primary" - /> - } - type="text" - name="name" - placeholder="Error input" - value="Error input" - variant="danger" - onChange={(event) => console.info('On input change')} - /> +
+ } + name="name" + onChange={(event) => console.info('On input change')} + placeholder="Primary input" + type="text" + value="Primary" + variant="primary" + /> + } + type="text" + name="name" + value="Secondary" + placeholder="Secondary input" + variant="secondary" + onChange={(event) => console.info('On input change')} + /> +
-Secondary variant should be used as a default variant. +Error variant can be used to focus user attention on error. - } - name="name" - onChange={(event) => console.info('On input change')} - placeholder="Secondary input" - variant="secondary" - type="text" - /> +
+ } + name="name" + onChange={(event) => console.info('On input change')} + placeholder="Error input" + type="text" + variant="danger" + /> +
### Sizes @@ -200,70 +214,81 @@ Secondary variant should be used as a default variant. Size controlled by the `component-level` block property not from the props. - +
console.info('On input change')} placeholder="Enter your name here" type="text" variant="primary" /> - - } name="name" onChange={(event) => console.info('On input change')} placeholder="Enter your name here" type="text" variant="primary" /> - - + +
+ +### Icons + +Inputs can be combined with an icon on the left side or the right. Use icons on the both sides is not recommended. + + +
} + type="text" name="name" - onChange={(event) => console.info('On input change')} placeholder="Enter your name here" - type="text" variant="primary" + onChange={(event) => console.info('On input change')} /> - - console.info('On input change')} placeholder="Enter your name here" - type="text" variant="primary" + onChange={(event) => console.info('On input change')} + rightIcon={} /> - - } + type="text" name="name" - onChange={(event) => console.info('On input change')} placeholder="Enter your name here" - type="text" variant="primary" + onChange={(event) => console.info('On input change')} + rightIcon={} /> - +
-### Container - -Inputs can be placed inside the container. Input width is equal to container size. +Icons can also be touch targets for nested components. - - + +
} + rightIcon={ + + } + onClick={() => console.info('ButtonIcon clicked')} + variant="primary" + /> + + } type="text" name="name" - value="Primary" placeholder="Enter your name here" - variant="primary" onChange={(event) => console.info('On input change')} + variant="primary" /> - - } type="text" @@ -281,7 +306,147 @@ Inputs can be placed inside the container. Input width is equal to container siz } /> - +
+
+ +### Variants + +Primary and secondary variants are should be used to focus user attention. + + + + +
+ } + name="name" + onChange={(event) => console.info('On input change')} + placeholder="Primary input" + type="text" + value="Primary" + variant="primary" + /> + } + type="text" + name="name" + value="Secondary" + placeholder="Secondary input" + variant="secondary" + onChange={(event) => console.info('On input change')} + /> +
+
+ +Error variant can be used to focus user attention on error. + + + + +
+ } + name="name" + onChange={(event) => console.info('On input change')} + placeholder="Error input" + type="text" + value="Error" + variant="error" + /> +
+
+ +### Sizes + +Size controlled by the `component-level` block property not from the props. + + +
+ + console.info('On input change')} + placeholder="Enter your name here" + type="text" + variant="primary" + /> + + + console.info('On input change')} + placeholder="Enter your name here" + type="text" + variant="primary" + /> + + + console.info('On input change')} + placeholder="Enter your name here" + type="text" + variant="primary" + /> + + + console.info('On input change')} + placeholder="Enter your name here" + type="text" + variant="primary" + /> + + + console.info('On input change')} + placeholder="Enter your name here" + type="text" + variant="primary" + /> + +
+
+ +### Container + +Inputs can be placed inside the container. Input width is equal to container size. + + +
+ + } + type="text" + name="name" + value="Primary" + placeholder="Enter your name here" + variant="primary" + onChange={(event) => console.info('On input change')} + /> + + + } + type="text" + name="name" + placeholder="Enter your name here" + onChange={(event) => console.info('On input change')} + variant="primary" + rightIcon={ + + } + onClick={() => console.info('ButtonIcon clicked')} + variant="primary" + /> + + } + /> + +
### Kinds @@ -292,15 +457,16 @@ Inputs can be placed inside the container. Input width is equal to container siz ### Props -| Name | Type | Default | Description | -| -------------- | ----------------------------------------------------- | ------------- | ----------------------------------------------------------- | -| `type` | `"text" ӏ "password" ӏ "email"` | `text` | HTML type of the input | -| `name` | `string` | | Name attribute specifies a name of the input | -| `value` | `HTMLInputElement['value']` | `null` | Value of input field | -| `onChange` | `React.EventHandler;` | | On change event handler. | -| `variant` | `string` | `'secondary'` | Variant prop to style Input component | -| `leftIcon` | `React.ReactNode` | | Component to show on the left side of the text (ex.: Icon) | -| `disabled` | `boolean` | | HTML disabled attribute | -| `placeholder` | `string` | | CSS pseudo-element represents the placeholder text | -|`rightIcon` | `React.ReactNode` | | Component to show on the right side of the text (ex.: Icon) | -| `...` | `HTMLInputElement` | `{}` | Other props are inherited from `HTMLInputElement` | +| Name | Type | Default | Description | +| -------------- | ------------------------------------------- | ----------- | ----------------------------------------------------------- | +| `autocomplete` | `"on" ӏ "off"` | `"off"` | Autocomplete attribute | +| `type` | `"text" ӏ "password" ӏ "email"` | `text` | HTML type of the input | +| `name` | `string` | | Name attribute specifies a name of the input | +| `value` | `HTMLInputElement['value']` | `null` | Value of input field | +| `onChange` | `React.EventHandler;` | | On change event handler. | +| `variant` | `string` | `'default'` | Variant prop to style Input component | +| `leftIcon` | `React.ReactNode` | | Component to show on the left side of the text (ex.: Icon) | +| `disabled` | `boolean` | | HTML disabled attribute | +| `placeholder` | `string` | | CSS pseudo-element represents the placeholder text | +| `rightIcon` | `React.ReactNode` | | Component to show on the right side of the text (ex.: Icon) | +| `...` | `HTMLInputElement` | `{}` | Other props are inherited from `HTMLInputElement` | diff --git a/src/ui/elements/quarks/input-container/index.tsx b/src/ui/elements/quarks/input-container/index.tsx index 27075188..b82cb0f0 100644 --- a/src/ui/elements/quarks/input-container/index.tsx +++ b/src/ui/elements/quarks/input-container/index.tsx @@ -1,64 +1,34 @@ import * as React from 'react'; import styled, { StyledComponent } from 'styled-components'; import { Variant } from 'lib/types'; -import { keyboardEventHandle } from 'lib'; + interface InputContainerProps extends React.InputHTMLAttributes { className?: string; disabled?: boolean; leftIcon?: React.ReactNode; - onChange: React.EventHandler; rightIcon?: React.ReactNode; } const InputContainerBase: React.FC = ({ children, className, - disabled, + disabled = 'false', leftIcon, - onChange, rightIcon, - variant = 'secondary', -}) => { - const tabIndex = disabled ? -1 : 0; - - const onKeyDown = React.useCallback( - (event: React.KeyboardEvent) => { - if (event.key === 'Enter') { - event.preventDefault(); - } - const keyHandler = { - enter: (event: React.SyntheticEvent) => { - onChange(event); - }, - }; - - keyboardEventHandle({ - event, - keyHandler, - }); - }, - [onChange], - ); - - return ( -
- {leftIcon && {leftIcon}} -
{children}
- {rightIcon && {rightIcon}} -
- ); -}; + variant = 'default', +}) => ( +
+ {leftIcon && {leftIcon}} +
{children}
+ {rightIcon && {rightIcon}} +
+); export const InputContainer = styled(InputContainerBase)` --local-vertical: calc(1px * var(--woly-component-level) * var(--woly-main-level)); --local-horizontal: calc( - var(--woly-const-m) + (1px * var(--woly-main-level)) + var(--local-vertical) + var(--woly-const-m) + (1px * var(--woly-main-level)) + var(--local-vertical) - + var(--woly-border-width) ); --local-gap: var(--local-vertical); @@ -72,7 +42,7 @@ export const InputContainer = styled(InputContainerBase)` width: 100%; outline: none; - padding: var(--local-vertical) 0; + padding: calc(var(--local-vertical) - var(--woly-border-width)) 0; box-sizing: border-box; @@ -100,15 +70,10 @@ export const InputContainer = styled(InputContainerBase)` } [data-icon] { - --local-icon-size: var(--woly-line-height); - display: flex; align-items: center; justify-content: center; - width: var(--local-icon-size); - height: var(--local-icon-size); - svg > path { fill: var(--local-icon-fill); } @@ -127,9 +92,8 @@ export const InputContainer = styled(InputContainerBase)` padding-left: var(--local-gap); } - &:focus { + &:focus-within { box-shadow: 0 0 0 var(--woly-border-width) var(--woly-focus); - outline: none; [data-icon] { --local-icon-fill: var(--woly-canvas-text-default); @@ -142,10 +106,15 @@ export const InputContainer = styled(InputContainerBase)` &:active { --local-border-color: var(--woly-focus); + --local-icon-fill: var(--woly-canvas-text-default); + } - [data-icon] { - --local-icon-fill: var(--woly-canvas-text-default); - } + &:focus-within { + --local-border-color: var(--woly-focus); + --local-icon-fill: var(--woly-canvas-text-default); + --local-border-color: var(--woly-focus); + + outline: none; } &[data-disabled='true'] { diff --git a/src/ui/elements/quarks/input-element/index.tsx b/src/ui/elements/quarks/input-element/index.tsx index 4ea6fca0..3f71f7c2 100644 --- a/src/ui/elements/quarks/input-element/index.tsx +++ b/src/ui/elements/quarks/input-element/index.tsx @@ -36,8 +36,8 @@ export const InputElement = styled(InputElementBase)` width: 100%; - font-size: var(--woly-font-size, 15px); - line-height: var(--woly-line-height, 24px); + font-size: var(--woly-font-size); + line-height: var(--woly-line-height); color: var(--local-value-color); background: var(--local-background);