From 0f04d21ab733cfa3995b097c546527fabcf0cd63 Mon Sep 17 00:00:00 2001 From: Ira Date: Fri, 23 Apr 2021 14:57:16 +0300 Subject: [PATCH 1/2] feat: add radio-button --- src/lib/box-styles.tsx | 6 +- src/ui/atoms/input/index.tsx | 4 +- src/ui/atoms/input/usage.mdx | 61 ++++--- .../elements/quarks/input-container/index.tsx | 36 ++-- .../elements/quarks/input-element/index.tsx | 3 +- src/ui/molecules/checkbox/usage.mdx | 4 +- src/ui/molecules/field/usage.mdx | 12 +- src/ui/molecules/index.ts | 1 + src/ui/molecules/input-password/index.tsx | 18 +- src/ui/molecules/input-password/usage.mdx | 155 +++++++++--------- src/ui/molecules/radio-button/index.tsx | 140 ++++++++++++++++ src/ui/molecules/radio-button/usage.mdx | 44 +++++ src/ui/molecules/switch/index.tsx | 1 - 13 files changed, 324 insertions(+), 161 deletions(-) create mode 100644 src/ui/molecules/radio-button/index.tsx create mode 100644 src/ui/molecules/radio-button/usage.mdx diff --git a/src/lib/box-styles.tsx b/src/lib/box-styles.tsx index 8fd87ce2..dabb7e29 100644 --- a/src/lib/box-styles.tsx +++ b/src/lib/box-styles.tsx @@ -10,7 +10,7 @@ export const Global = styled.div` /* should be rewritten to formulas */ --woly-line-height: 24px; - --woly-border-width: 1.5px; + --woly-border-width: 2px; --woly-rounding: 4px; --woly-font-size: 15px; @@ -25,7 +25,7 @@ export const Global = styled.div` --woly-shape-default: #b0a3f4; --woly-shape-disabled: #e5e5e5; --woly-shape-hover: #c9c0f8; - --woly-shape-active: #b0a3f4; + --woly-shape-active: var(--palette-lavender-500); --woly-shape-text-default: var(--palette-snow-0); --woly-shape-text-disabled: var(--palette-snow-0); @@ -35,7 +35,7 @@ export const Global = styled.div` --woly-canvas-default: transparent; --woly-canvas-disabled: var(--palette-snow-100); --woly-canvas-hover: var(--palette-snow-500); - --woly-canvas-active: var(--palette-snow-500); + --woly-canvas-active: var(--palette-lavender-500); --woly-canvas-text-default: var(--palette-snow-1000); --woly-canvas-text-disabled: var(--palette-snow-500); diff --git a/src/ui/atoms/input/index.tsx b/src/ui/atoms/input/index.tsx index 2180ffa9..bc33bf15 100644 --- a/src/ui/atoms/input/index.tsx +++ b/src/ui/atoms/input/index.tsx @@ -1,6 +1,7 @@ import * as React from 'react'; import styled, { StyledComponent } from 'styled-components'; import { Variant } from 'lib/types'; + import { InputContainer, InputElement } from '../../elements/quarks'; interface InputProps extends React.InputHTMLAttributes { @@ -52,12 +53,11 @@ export const Input = styled(InputBase)` --local-horizontal: calc( var(--woly-const-m) + (1px * var(--woly-main-level)) + var(--local-vertical) ); - + box-sizing: border-box; width: 100%; &[data-disabled='true'] { pointer-events: none; } - ` as StyledComponent<'div', Record, InputProps & Variant>; diff --git a/src/ui/atoms/input/usage.mdx b/src/ui/atoms/input/usage.mdx index bb94cee3..b63368cf 100644 --- a/src/ui/atoms/input/usage.mdx +++ b/src/ui/atoms/input/usage.mdx @@ -14,14 +14,14 @@ import { ButtonIcon } from '../../atoms'; `Input` is a field to accept data from the user. Input is used to create form fields that accept user input. They typically appear in forms and dialogs. -Inputs requiring certain data formats such as numbers, e-mail addresses or passwords are declared accordingly. +Inputs requiring certain data formats such as numbers, e-mail addresses or passwords are declared accordingly. This makes it easier for the user to enter information using a virtual keyboard. When a input is active or contains an error, the input’s border color and thickness vary. ## Placeholder text (Hint text) -Placeholder text rests in the input field until the user starts entering text. +Placeholder text rests in the input field until the user starts entering text. It may contain an action or an example, such as a phone number or email address. ## Helper text @@ -35,10 +35,9 @@ Specs: - Left justified - On a single line if possible, or with text wrapping (if multiple lines) - ## Error message -When input isn’t accepted, text fields can display an error message below the input line, with instructions on how to fix the error. +When input isn’t accepted, text fields can display an error message below the input line, with instructions on how to fix the error. Until the error is fixed, the error replaces the helper text. An error message should appear on a single line, if possible. @@ -52,7 +51,6 @@ Specs: - Right justified - Displayed as a ratio of characters used and the character limit (formatted as: characters used / character limit) - ### Example @@ -69,7 +67,7 @@ Specs: Disabled text fields are uneditable. They have less opacity so that they appear less tappable. - + -### Icons +### Icons Inputs can be combined with an icon on the left side or the right. Use icons on the both sides is not recommended. @@ -121,10 +119,10 @@ Inputs can be combined with an icon on the left side or the right. Use icons on /> -Icons can also be touch targets for nested components. +Icons can also be touch targets for nested components. - console.info('On input change')} variant="primary" /> - } type="text" name="name" @@ -162,9 +160,10 @@ Icons can also be touch targets for nested components. ### Variants Primary and secondary variants are should be used to focus user attention. + - + } name="name" @@ -185,7 +184,6 @@ Primary and secondary variants are should be used to focus user attention. /> - Error variant can be used to focus user attention on error. @@ -214,7 +212,7 @@ Size controlled by the `component-level` block property not from the props. placeholder="Enter your name here" type="text" variant="primary" - /> + /> + /> + /> + /> + /> @@ -268,10 +266,10 @@ Inputs can be placed inside the container. Input width is equal to container siz placeholder="Enter your name here" variant="primary" onChange={(event) => console.info('On input change')} - /> + /> - } type="text" name="name" @@ -291,7 +289,6 @@ Inputs can be placed inside the container. Input width is equal to container siz - ### Kinds | Name | Description | @@ -300,15 +297,15 @@ 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` | `'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` | +| 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` | `'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 f62b3523..b9a5b176 100644 --- a/src/ui/elements/quarks/input-container/index.tsx +++ b/src/ui/elements/quarks/input-container/index.tsx @@ -2,8 +2,7 @@ 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 { +interface InputContainerProps extends React.InputHTMLAttributes { className?: string; disabled?: boolean; leftIcon?: React.ReactNode; @@ -20,12 +19,10 @@ const InputContainerBase: React.FC = ({ rightIcon, variant = 'default', }) => { - const tabIndex = disabled ? -1 : 0; const onKeyDown = React.useCallback( (event: React.KeyboardEvent) => { - if (event.key === 'Enter') { event.preventDefault(); } @@ -56,8 +53,7 @@ const InputContainerBase: React.FC = ({ {rightIcon && {rightIcon}} ); -} - +}; export const InputContainer = styled(InputContainerBase)` --local-vertical: calc(1px * var(--woly-component-level) * var(--woly-main-level)); @@ -88,13 +84,13 @@ export const InputContainer = styled(InputContainerBase)` border: var(--woly-border-width) solid var(--local-border-color); border-radius: var(--woly-rounding); - [data-input="input"] { + [data-input='input'] { flex: 1; color: var(--local-value-color); - + padding: 0 var(--local-horizontal); - input{ + input { padding: 0; } @@ -105,7 +101,7 @@ export const InputContainer = styled(InputContainerBase)` [data-icon] { --local-icon-size: var(--woly-line-height); - + display: flex; align-items: center; justify-content: center; @@ -113,9 +109,9 @@ export const InputContainer = styled(InputContainerBase)` width: var(--local-icon-size); height: var(--local-icon-size); - svg > path { - fill: var(--local-icon-fill); - } + svg > path { + fill: var(--local-icon-fill); + } } [data-icon='left'] { @@ -126,8 +122,8 @@ export const InputContainer = styled(InputContainerBase)` padding: 0 calc(var(--local-horizontal) - var(--local-compensate)) 0 0; } - [data-icon='left'] ~ [data-input="input"], - [data-input="input"] ~ [data-icon='right'] { + [data-icon='left'] ~ [data-input='input'], + [data-input='input'] ~ [data-icon='right'] { padding-left: var(--local-gap); } @@ -137,7 +133,7 @@ export const InputContainer = styled(InputContainerBase)` outline: none; [data-icon] { - --local-icon-fill: var(--woly-canvas-text-default); + --local-icon-fill: var(--woly-canvas-text-default); } } @@ -147,7 +143,7 @@ export const InputContainer = styled(InputContainerBase)` &:active { --local-border-color: var(--woly-focus); - + [data-icon] { --local-icon-fill: var(--woly-canvas-text-default); } @@ -160,8 +156,4 @@ export const InputContainer = styled(InputContainerBase)` --local-border-color: var(--woly-shape-disabled); --local-value-color: var(--woly-canvas-text-disabled); } -` as StyledComponent< - 'div', - Record, - InputContainerProps & Variant ->; +` as StyledComponent<'div', Record, InputContainerProps & Variant>; diff --git a/src/ui/elements/quarks/input-element/index.tsx b/src/ui/elements/quarks/input-element/index.tsx index ae2527cb..4ea6fca0 100644 --- a/src/ui/elements/quarks/input-element/index.tsx +++ b/src/ui/elements/quarks/input-element/index.tsx @@ -1,8 +1,7 @@ import * as React from 'react'; import styled, { StyledComponent } from 'styled-components'; -interface InputElementProps - extends React.InputHTMLAttributes { +interface InputElementProps extends React.InputHTMLAttributes { className?: string; name: string; onChange: React.EventHandler; diff --git a/src/ui/molecules/checkbox/usage.mdx b/src/ui/molecules/checkbox/usage.mdx index f927eb8c..2d11be3d 100644 --- a/src/ui/molecules/checkbox/usage.mdx +++ b/src/ui/molecules/checkbox/usage.mdx @@ -31,9 +31,9 @@ Use a checkbox when the user has to stop before performing an action. Checkbox w Error and primary/secondary variants are should be used to focus user attention. - + - + ### Kinds diff --git a/src/ui/molecules/field/usage.mdx b/src/ui/molecules/field/usage.mdx index e71c9b31..7d418f4d 100644 --- a/src/ui/molecules/field/usage.mdx +++ b/src/ui/molecules/field/usage.mdx @@ -81,11 +81,7 @@ Labels and children component can be replaced in row !i}> {(value, change) => ( - + )} @@ -102,11 +98,7 @@ or in column variants !i}> {(value, change) => ( - + )} diff --git a/src/ui/molecules/index.ts b/src/ui/molecules/index.ts index 844d44cb..16ccf839 100644 --- a/src/ui/molecules/index.ts +++ b/src/ui/molecules/index.ts @@ -1,6 +1,7 @@ export { Checkbox } from './checkbox'; export { Field } from './field'; export { InputPassword } from './input-password'; +export { RadioButton } from './radio-button'; export { Popover } from './popover'; export { Select } from './select'; export { Switch } from './switch'; diff --git a/src/ui/molecules/input-password/index.tsx b/src/ui/molecules/input-password/index.tsx index fa9f7b36..e1a940e3 100644 --- a/src/ui/molecules/input-password/index.tsx +++ b/src/ui/molecules/input-password/index.tsx @@ -1,10 +1,10 @@ import * as React from 'react'; import styled, { StyledComponent } from 'styled-components'; -import { Variant } from 'lib/types'; - import { ButtonIcon, Input } from 'ui'; +import { ClosedEyeIcon, InfoIcon, OpenedEyeIcon } from 'icons'; +import { Variant } from 'lib/types'; import { block } from 'box-styles'; -import { ClosedEyeIcon, OpenedEyeIcon, InfoIcon } from 'icons'; + interface InputPasswordProps { className?: string; disabled?: boolean; @@ -53,19 +53,17 @@ export const InputPasswordBase: React.FC = ({ /> ); -} +}; -export const InputPassword = styled(InputPasswordBase)` +export const InputPassword = (styled(InputPasswordBase)` --local-gap: calc( - (1px * var(--woly-main-level)) + - (1px * var(--woly-main-level) * var(--woly-component-level)) + (1px * var(--woly-main-level)) + (1px * var(--woly-main-level) * var(--woly-component-level)) ); - + box-sizing: border-box; width: 100%; & > *:not(:first-child) { margin-left: var(--woly-gap); } - -` as unknown as StyledComponent<'div', Record, InputPasswordProps & Variant>; +` as unknown) as StyledComponent<'div', Record, InputPasswordProps & Variant>; diff --git a/src/ui/molecules/input-password/usage.mdx b/src/ui/molecules/input-password/usage.mdx index 02751196..861e9ec8 100644 --- a/src/ui/molecules/input-password/usage.mdx +++ b/src/ui/molecules/input-password/usage.mdx @@ -16,7 +16,7 @@ InputPassword can be used in an authorization form or to confirm user action. ### Example - !i}> + !i}> {(value, change) => ( @@ -99,73 +100,73 @@ Size controlled by the `component-level` block property not from the props. !i}> - {(value, change) => ( - console.info('On input change')} - variant="primary" - /> - )} - + {(value, change) => ( + console.info('On input change')} + variant="primary" + /> + )} + !i}> - {(value, change) => ( - console.info('On input change')} - variant="primary" - /> - )} - + {(value, change) => ( + console.info('On input change')} + variant="primary" + /> + )} + !i}> - {(value, change) => ( - console.info('On input change')} - variant="primary" - /> - )} - + {(value, change) => ( + console.info('On input change')} + variant="primary" + /> + )} + !i}> - {(value, change) => ( - console.info('On input change')} - variant="primary" - /> - )} - + {(value, change) => ( + console.info('On input change')} + variant="primary" + /> + )} + !i}> - {(value, change) => ( - console.info('On input change')} - variant="primary" - /> - )} - + {(value, change) => ( + console.info('On input change')} + variant="primary" + /> + )} + @@ -176,17 +177,17 @@ Inputs can be placed inside the container. Input width is equal to container siz !i}> - {(value, change) => ( - console.info('On input change')} - variant="primary" - /> - )} - + {(value, change) => ( + console.info('On input change')} + variant="primary" + /> + )} + @@ -198,13 +199,13 @@ Inputs can be placed inside the container. Input width is equal to container siz ### Props -| Name | Type | Default | Description | -| -------------- | -------------------------------------------- | ----------- | ----------------------------------------------------------- | -| `disabled` | `boolean` | `null` | HTML disabled attribute | -| `name` | `string` | | HTML name attribute | -| `onChange` | `React.ChangeEventHandler` | | Callback when input is changed | -| `value` | `HTMLInputElement['value']` | `null` | HTML value attribute | -| `variant` | `string` | `'default'` | Variant prop to style InputPassword component | -| `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 | +| ------------- | -------------------------------------------- | ----------- | ----------------------------------------------------------- | +| `disabled` | `boolean` | `null` | HTML disabled attribute | +| `name` | `string` | | HTML name attribute | +| `onChange` | `React.ChangeEventHandler` | | Callback when input is changed | +| `value` | `HTMLInputElement['value']` | `null` | HTML value attribute | +| `variant` | `string` | `'default'` | Variant prop to style InputPassword component | +| `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/molecules/radio-button/index.tsx b/src/ui/molecules/radio-button/index.tsx new file mode 100644 index 00000000..aea20265 --- /dev/null +++ b/src/ui/molecules/radio-button/index.tsx @@ -0,0 +1,140 @@ +import * as React from 'react'; +import styled, { StyledComponent } from 'styled-components'; +import { CheckIcon, UnCheckIcon } from 'icons'; +import { Variant } from 'lib/types'; + +interface RadioButtonProps { + className?: string; + disabled?: boolean; + id: string; + checked: boolean; + onChange: React.ChangeEventHandler; + text?: string; +} + +const RadioButtonBase: React.FC = ({ + className, + disabled, + id, + checked, + onChange, + text, + variant = 'default', + ...p +}) => ( + +); + +export const RadioButton = styled(RadioButtonBase)` + --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) + ); + --local-gap: var(--woly-const-m); + + --local-size: 18px; + + --local-color-text: var(--woly-canvas-text-default); + --local-background: var(--woly-canvas-default); + + --local-icon-fill: var(--woly-shape-default); + + --local-border-color: var(--woly-canvas-hover); + --local-border-rounding: 50%; + + width: 100%; + user-select: none; + outline: none; + + input { + position: absolute; + opacity: 0; + height: 0; + width: 0; + } + + [data-container='container'] { + display: flex; + align-items: center; + + [data-input='radio-input'] { + display: flex; + align-items: center; + justify-content: center; + + margin-right: var(--local-vertical); + + [data-radio='radio-control'] { + display: flex; + align-items: center; + justify-content: center; + + width: var(--local-size); + height: var(--local-size); + + padding: var(--local-gap); + + border: var(--woly-border-width) solid var(--local-border-color); + border-radius: var(--local-border-rounding); + + background: var(--local-background); + + &:hover { + --local-border-color: var(--woly-shape-hover); + } + + &:active { + --local-border-color: var(--woly-focus); + } + } + } + } + [data-icon] { + align-items: center; + justify-content: center; + + width: 10px; + height: 10px; + + border-radius: var(--local-border-rounding); + background: var(--local-icon-fill); + padding: calc(var(--local-gap) - var(--woly-border-width)); + } + + input:not(:checked) ~ [data-radio='radio-control'] > [data-icon] { + display: none; + } + + input:checked ~ [data-radio='radio-control'] { + --local-border-color: var(--woly-shape-default); + + [data-icon] { + display: flex; + + &:hover { + --local-icon-fill: var(--woly-shape-hover); + } + + &:active { + --local-icon-fill: var(--woly-focus); + } + } + } + + [data-block='text'] { + font-size: var(--woly-font-size, 12px); + line-height: var(--woly-line-height, 24px); + + color: var(--local-color-text); + } +` as StyledComponent<'div', Record, RadioButtonProps & Variant>; diff --git a/src/ui/molecules/radio-button/usage.mdx b/src/ui/molecules/radio-button/usage.mdx new file mode 100644 index 00000000..94f5748e --- /dev/null +++ b/src/ui/molecules/radio-button/usage.mdx @@ -0,0 +1,44 @@ +--- +name: radio-button +category: molecules +package: 'woly' +--- + +import { RadioButton } from './index'; +import { Playground, State } from 'box-styles'; + +The `RadioButton` component lets you add a radio button and assign it to a radio group. +Users can select only one radio button at a time within a radio group. + +### Radio button group. + +Radio buttons are used for mutually exclusive choices, not for multiple choices. +Only one radio button can be selected at a time. +When a user chooses a new item, the previous choice is automatically deselected. + +### Example + + + !i}> + {(value, change) => ( + + )} + + + +### Kinds + +| Name | Description | +| ------------- | ------------------------------------------------------------------ | +| `RadioButton` | Base radio-button. Useful for creating a new kind of radio-buttons | + +### Props + +| Name | Type | Default | Description | +| ---------- | -------------------------------------------- | ----------- | ------------------------------------------------- | +| `checked` | `boolean` | | Whether radio-button is checked or not | +| `disabled` | `boolean` | | Disable radio-button | +| `id` | `string` | | HTML id attribute | +| `onChange` | `React.ChangeEventHandler` | | Callback when radio-button is clicked | +| `variant` | `string` | `'default'` | Variant prop to style RadioButton component | +| `...` | `HTMLInputElement` | `{}` | Other props are inherited from `HTMLInputElement` | diff --git a/src/ui/molecules/switch/index.tsx b/src/ui/molecules/switch/index.tsx index f81a7f0e..940254d2 100644 --- a/src/ui/molecules/switch/index.tsx +++ b/src/ui/molecules/switch/index.tsx @@ -24,7 +24,6 @@ const SwitchBase: React.FC = ({ const onKeyDown = React.useCallback( (event: React.KeyboardEvent) => { - if (event.key === 'Enter') { event.preventDefault(); } From 80e5418e2be36925d5694f5f785a9949fe496bdb Mon Sep 17 00:00:00 2001 From: Ira Date: Tue, 27 Apr 2021 14:24:29 +0300 Subject: [PATCH 2/2] implement radio-button --- src/ui/molecules/radio-button/index.tsx | 209 ++++++++++++++---------- src/ui/molecules/radio-button/usage.mdx | 111 ++++++++++++- 2 files changed, 232 insertions(+), 88 deletions(-) diff --git a/src/ui/molecules/radio-button/index.tsx b/src/ui/molecules/radio-button/index.tsx index aea20265..a8c0863c 100644 --- a/src/ui/molecules/radio-button/index.tsx +++ b/src/ui/molecules/radio-button/index.tsx @@ -1,39 +1,67 @@ import * as React from 'react'; import styled, { StyledComponent } from 'styled-components'; -import { CheckIcon, UnCheckIcon } from 'icons'; import { Variant } from 'lib/types'; +import { keyboardEventHandle } from 'lib'; interface RadioButtonProps { - className?: string; - disabled?: boolean; - id: string; - checked: boolean; - onChange: React.ChangeEventHandler; - text?: string; + className?: string; + disabled?: boolean; + id: string; + checked: boolean; + onChange: React.EventHandler; + text?: string; + name: string; } const RadioButtonBase: React.FC = ({ - className, - disabled, - id, - checked, - onChange, - text, - variant = 'default', - ...p -}) => ( - -); + className, + disabled = false, + id, + checked, + onChange, + text, + name, + variant = 'primary', + ...p +}) => { + 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 ( +
+ +
+ ); +} + export const RadioButton = styled(RadioButtonBase)` --local-vertical: calc(1px * var(--woly-component-level) * var(--woly-main-level)); @@ -42,99 +70,106 @@ export const RadioButton = styled(RadioButtonBase)` ); --local-gap: var(--woly-const-m); - --local-size: 18px; + --local-radio-size: 18px; + --local-ellipse-size: 10px; --local-color-text: var(--woly-canvas-text-default); - --local-background: var(--woly-canvas-default); + --local-background: var(--woly-shape-text-default); --local-icon-fill: var(--woly-shape-default); --local-border-color: var(--woly-canvas-hover); --local-border-rounding: 50%; - - width: 100%; - user-select: none; + + display: flex; + align-items: center; outline: none; + border-radius: var(--local-border-rounding); - input { - position: absolute; - opacity: 0; - height: 0; - width: 0; - } + margin-right: var(--local-vertical); - [data-container='container'] { + label { display: flex; align-items: center; - [data-input='radio-input'] { - display: flex; - align-items: center; - justify-content: center; + cursor: pointer; - margin-right: var(--local-vertical); + input { + display: none; + outline: none; + } + } - [data-radio='radio-control'] { - display: flex; - align-items: center; - justify-content: center; + [data-checkbox] { + display: flex; + align-items: center; + justify-content: center; + + width: var(--local-radio-size); + height: var(--local-radio-size); - width: var(--local-size); - height: var(--local-size); + background: var(--local-background); + border-radius: var(--local-border-rounding); + border: var(--woly-border-width) solid var(--local-border-color); - padding: var(--local-gap); + margin-right: var(--local-gap); + } - border: var(--woly-border-width) solid var(--local-border-color); - border-radius: var(--local-border-rounding); + input:checked + [data-checkbox] { + --local-border-color: var(--woly-shape-default); + &:before { + content: ''; - background: var(--local-background); + width: var(--local-ellipse-size); + height: var(--local-ellipse-size); - &:hover { - --local-border-color: var(--woly-shape-hover); - } + background-color: var(--local-icon-fill); + border-radius: var(--local-border-rounding); + } - &:active { - --local-border-color: var(--woly-focus); - } - } + &:hover { + --local-border-color: var(--woly-shape-hover); + --local-icon-fill: var(--woly-shape-hover); } - } - [data-icon] { - align-items: center; - justify-content: center; - width: 10px; - height: 10px; + &:active { + --local-border-color: var(--woly-shape-active); + --local-icon-fill: var(--woly-shape-active); + } + } - border-radius: var(--local-border-rounding); - background: var(--local-icon-fill); - padding: calc(var(--local-gap) - var(--woly-border-width)); + &:hover { + --local-border-color: var(--woly-shape-hover); } - input:not(:checked) ~ [data-radio='radio-control'] > [data-icon] { - display: none; + &:active > label > [data-checkbox]{ + --local-border-color: var(--woly-shape-active); } - input:checked ~ [data-radio='radio-control'] { - --local-border-color: var(--woly-shape-default); + &:focus > label > [data-checkbox]{ + box-shadow: 0 0 0 var(--woly-border-width) var(--woly-focus); + } + + &[data-disabled='true'] { + pointer-events: none; - [data-icon] { - display: flex; + [data-checkbox] { + --local-border-color: var(--woly-shape-disabled); + } - &:hover { - --local-icon-fill: var(--woly-shape-hover); - } + input:checked + [data-checkbox] { + --local-border-color: var(--woly-shape-disabled); + --local-icon-fill: var(--woly-shape-disabled); + } - &:active { - --local-icon-fill: var(--woly-focus); - } + [data-text] { + --local-color-text: var(--woly-canvas-text-disabled); } } - [data-block='text'] { - font-size: var(--woly-font-size, 12px); - line-height: var(--woly-line-height, 24px); - + [data-text] { + font-size: var(--woly-font-size); + line-height: var(--woly-line-height); color: var(--local-color-text); } ` as StyledComponent<'div', Record, RadioButtonProps & Variant>; diff --git a/src/ui/molecules/radio-button/usage.mdx b/src/ui/molecules/radio-button/usage.mdx index 94f5748e..f8155885 100644 --- a/src/ui/molecules/radio-button/usage.mdx +++ b/src/ui/molecules/radio-button/usage.mdx @@ -18,14 +18,123 @@ When a user chooses a new item, the previous choice is automatically deselected. ### Example + + !i}> + {(value, change) => ( + <> + + + + )} + + + +### Disabled + +Disabled radio button are uneditable. They have less opacity so that they appear less tappable. + !i}> {(value, change) => ( - + + )} + + + +### Variants + +Primary and danger variants are should be used to focus user attention. + + + !i}> + {(value, change) => ( + <> + + + + )} + + + + + !i}> + {(value, change) => ( + <> + + + )} +Secondary variant should be used as default variant + + + !i}> + {(value, change) => ( + <> + + + + )} + + ### Kinds | Name | Description |