diff --git a/web/vtadmin/src/components/Button.module.scss b/web/vtadmin/src/components/Button.module.scss index 7b9c50ab33e..064667d8719 100644 --- a/web/vtadmin/src/components/Button.module.scss +++ b/web/vtadmin/src/components/Button.module.scss @@ -2,14 +2,16 @@ background: var(--colorPrimary); border: solid 2px var(--colorPrimary); border-radius: 6px; + box-sizing: border-box; color: var(--textColorInverted); cursor: pointer; font-family: var(--fontFamilyPrimary); font-size: var(--fontSizeDefault); font-weight: 500; - line-height: var(--lineHeightBody); + height: var(--inputHeightMedium); + line-height: 1; outline: none; - padding: 8px 11px; + padding: 0 12px; user-select: none; transition: all 0.1s ease-in-out; white-space: nowrap; @@ -68,11 +70,13 @@ .button.sizeLarge { font-size: var(--fontSizeLarge); - padding: 13px 15px; + height: var(--inputHeightLarge); + padding: 0 16px; } .button.sizeSmall { border-width: 1px; font-size: var(--fontSizeSmall); - padding: 4px 6px; + height: var(--inputHeightSmall); + padding: 0 6px; } diff --git a/web/vtadmin/src/components/TextInput.module.scss b/web/vtadmin/src/components/TextInput.module.scss new file mode 100644 index 00000000000..5c3e67b4448 --- /dev/null +++ b/web/vtadmin/src/components/TextInput.module.scss @@ -0,0 +1,106 @@ +.inputContainer { + display: inline-block; + position: relative; +} + +$iconSizeMedium: 1.6rem; +$iconSizeLarge: 2.2rem; +$iconOffsetHorizontal: 3.2rem; +$iconOffsetHorizontalLarge: 4.2rem; +$iconPositionHorizontal: 1.2rem; +$iconPositionHorizontalLarge: 1.6rem; + +.input { + background: var(--backgroundPrimary); + border: solid 2px var(--colorDisabled); + border-radius: 6px; + box-sizing: border-box; + color: var(--textColorPrimary); + display: block; + font-family: var(--fontFamilyPrimary); + font-size: var(--fontSizeBody); + height: var(--inputHeightMedium); + line-height: var(--inputHeightMedium); + padding: 0 12px; + transition: all 0.1s ease-in-out; + width: 100%; + + &:disabled { + background: var(--backgroundSecondary); + border-color: var(--backgroundSecondaryHighlight); + cursor: not-allowed; + } + + &.large { + font-size: var(--fontSizeLarge); + height: var(--inputHeightLarge); + line-height: var(--inputHeightLarge); + padding: 0 16px; + } + + &.withIconLeft { + padding-left: $iconOffsetHorizontal; + + &.large { + padding-left: $iconOffsetHorizontalLarge; + } + } + + &.withIconRight { + padding-right: $iconOffsetHorizontal; + + &.large { + padding-right: $iconOffsetHorizontalLarge; + } + } +} + +.icon { + fill: var(--grey600); + height: $iconSizeMedium; + margin-top: -($iconSizeMedium / 2); + position: absolute; + top: 50%; + width: $iconSizeMedium; +} + +.iconLeft { + /* stylelint-disable-next-line */ + @extend .icon; + + left: $iconPositionHorizontal; +} + +.iconRight { + /* stylelint-disable-next-line */ + @extend .icon; + + right: $iconPositionHorizontal; +} + +.large ~ .icon { + height: $iconSizeLarge; + margin-top: -($iconSizeLarge / 2); + width: $iconSizeLarge; +} + +.large ~ .iconLeft { + left: $iconPositionHorizontalLarge; +} + +.large ~ .iconRight { + right: $iconPositionHorizontalLarge; +} + +.input:focus { + border-color: var(--colorPrimary); + outline: none; +} + +.input:focus ~ .icon { + fill: var(--colorPrimary); +} + +.input:disabled ~ .icon { + fill: var(--colorDisabled); +} diff --git a/web/vtadmin/src/components/TextInput.tsx b/web/vtadmin/src/components/TextInput.tsx new file mode 100644 index 00000000000..5b567c79819 --- /dev/null +++ b/web/vtadmin/src/components/TextInput.tsx @@ -0,0 +1,37 @@ +import * as React from 'react'; +import cx from 'classnames'; + +import { Icon, Icons } from './Icon'; + +import style from './TextInput.module.scss'; + +type NativeInputProps = Omit< + React.DetailedHTMLProps, HTMLInputElement>, + // Omit the props that we explicitly want to overwrite + 'size' +>; +interface Props extends NativeInputProps { + // className is applied to the , not the parent container. + className?: string; + iconLeft?: Icons; + iconRight?: Icons; + size?: 'large' | 'medium'; // We have no need for small inputs right now. +} + +export const TextInput = ({ className, iconLeft, iconRight, size = 'medium', ...props }: Props) => { + const inputClass = cx(style.input, { + [style.large]: size === 'large', + [style.withIconLeft]: !!iconLeft, + [style.withIconRight]: !!iconRight, + }); + + // Order of elements matters: the comes before the icons so that + // we can use CSS adjacency selectors like `input:focus ~ .icon`. + return ( +
+ + {iconLeft && } + {iconRight && } +
+ ); +}; diff --git a/web/vtadmin/src/components/routes/Debug.module.scss b/web/vtadmin/src/components/routes/Debug.module.scss index 46bced42ee3..2f4aa14f44c 100644 --- a/web/vtadmin/src/components/routes/Debug.module.scss +++ b/web/vtadmin/src/components/routes/Debug.module.scss @@ -22,3 +22,17 @@ fill: var(--colorPrimary200); } } + +.inputContainer { + display: grid; + grid-template-columns: 100%; + row-gap: 16px; + max-width: 640px; +} + +.inputRow { + display: grid; + grid-template-columns: 1fr min-content min-content; + column-gap: 8px; + max-width: 100%; +} diff --git a/web/vtadmin/src/components/routes/Debug.tsx b/web/vtadmin/src/components/routes/Debug.tsx index 9d8a6e4dde5..c6505eaaa28 100644 --- a/web/vtadmin/src/components/routes/Debug.tsx +++ b/web/vtadmin/src/components/routes/Debug.tsx @@ -2,6 +2,7 @@ import * as React from 'react'; import { Theme, useTheme } from '../../hooks/useTheme'; import { Button } from '../Button'; import { Icon, Icons } from '../Icon'; +import { TextInput } from '../TextInput'; import style from './Debug.module.scss'; export const Debug = () => { @@ -41,6 +42,38 @@ export const Debug = () => { ))} +

Text Inputs

+
+ + + + + + +
+ + + +
+
+ + + +
+
+

Buttons

{/* Large */} diff --git a/web/vtadmin/src/index.css b/web/vtadmin/src/index.css index 9bf96c53914..70fb44ed009 100644 --- a/web/vtadmin/src/index.css +++ b/web/vtadmin/src/index.css @@ -63,6 +63,11 @@ --fontSizeHeading3: 2rem; --lineHeightHeading: 1.36; + /* Inputs + other form controls */ + --inputHeightSmall: 2.4rem; + --inputHeightMedium: 3.6rem; + --inputHeightLarge: 4.8rem; + /* Layout variables, set to light theme by default */ --backgroundPrimary: #fff; --backgroundPrimaryHighlight: rgba(61, 90, 254, 0.1);