From 394e50a4cc78f8e8ee86791051d9772166ddd694 Mon Sep 17 00:00:00 2001 From: TheoryOfNekomata Date: Sun, 15 Aug 2021 14:35:03 +0800 Subject: [PATCH] Fix multiline input styling Make styles similar to regular text input component. --- .../MultilineTextInput.stories.tsx | 125 +++++++++++ .../components/MultilineTextInput/index.tsx | 197 ++++++++---------- .../src/components/TextInput/index.tsx | 12 +- 3 files changed, 215 insertions(+), 119 deletions(-) create mode 100644 packages/react-common/src/components/MultilineTextInput/MultilineTextInput.stories.tsx diff --git a/packages/react-common/src/components/MultilineTextInput/MultilineTextInput.stories.tsx b/packages/react-common/src/components/MultilineTextInput/MultilineTextInput.stories.tsx new file mode 100644 index 0000000..71061b4 --- /dev/null +++ b/packages/react-common/src/components/MultilineTextInput/MultilineTextInput.stories.tsx @@ -0,0 +1,125 @@ +import * as React from 'react' +import { Props, MultilineTextInput, MultilineTextInputSize } from '.' +import { Meta, Story } from '@storybook/react' + +export default { + title: 'components/MultilineTextInput', + component: MultilineTextInput, + argTypes: { onChange: { action: 'changed' } }, +} as Meta + +const Template: Story = ({ ref, ...args }) => ( + <> + Text here + {' '} + + {' '} + Other text here + +) + +export const Default = Template.bind({}) +Default.args = { + border: true, + label: '', + hint: '', + indicator: '', + size: MultilineTextInputSize.MEDIUM, +} + +export const WithLabel = Template.bind({}) +WithLabel.args = { + border: true, + label: 'Foo', +} + +export const WithLabelAndHint = Template.bind({}) +WithLabelAndHint.args = { + border: true, + label: 'Foo', + hint: '(example value)', +} + +export const WithLabelAndHintAlternate = Template.bind({}) +WithLabelAndHintAlternate.args = { + border: true, + alternate: true, + label: 'Foo', + hint: '(example value)', +} + +export const WithLabelAndHintAlternateBorderless = Template.bind({}) +WithLabelAndHintAlternateBorderless.args = { + alternate: true, + label: 'Foo', + hint: '(example value)', +} + +export const WithLabelAndHintBorderless = Template.bind({}) +WithLabelAndHintBorderless.args = { + label: 'Foo', + hint: '(example value)', +} + +export const Block = Template.bind({}) +Block.args = { + border: true, + block: true, +} + +export const WithLabelHintAndIndicator = Template.bind({}) +WithLabelHintAndIndicator.args = { + border: true, + label: 'Foo', + hint: '(example value)', + indicator: 'A', +} + +export const WithLabelHintAndIndicatorAlternate = Template.bind({}) +WithLabelHintAndIndicatorAlternate.args = { + alternate: true, + border: true, + label: 'Foo', + hint: '(example value)', + indicator: 'A', +} + +export const WithLabelHintAndIndicatorAlternateSmall = Template.bind({}) +WithLabelHintAndIndicatorAlternateSmall.args = { + alternate: true, + border: true, + label: 'Foo', + hint: '(example value)', + indicator: 'A', + size: MultilineTextInputSize.SMALL, +} + +export const WithLabelHintAndIndicatorAlternateLarge = Template.bind({}) +WithLabelHintAndIndicatorAlternateLarge.args = { + alternate: true, + border: true, + label: 'Foo', + hint: '(example value)', + indicator: 'A', + size: MultilineTextInputSize.LARGE, +} + +export const WithLabelHintAndIndicatorSmall = Template.bind({}) +WithLabelHintAndIndicatorSmall.args = { + border: true, + label: 'Foo', + hint: '(example value)', + indicator: 'A', + size: MultilineTextInputSize.SMALL, +} + +export const WithLabelHintAndIndicatorLarge = Template.bind({}) +WithLabelHintAndIndicatorLarge.args = { + border: true, + label: 'Foo', + hint: '(example value)', + indicator: 'A', + size: MultilineTextInputSize.LARGE, +} diff --git a/packages/react-common/src/components/MultilineTextInput/index.tsx b/packages/react-common/src/components/MultilineTextInput/index.tsx index 586316b..386924f 100644 --- a/packages/react-common/src/components/MultilineTextInput/index.tsx +++ b/packages/react-common/src/components/MultilineTextInput/index.tsx @@ -1,8 +1,5 @@ import * as React from 'react' -import * as PropTypes from 'prop-types' import styled from 'styled-components' -import stringify from '../../utilities/stringify' -import { Size } from '../../utilities/utilities' // TODO implement web-client text inputs! @@ -21,7 +18,7 @@ const MIN_HEIGHTS: Record = { const LABEL_VERTICAL_PADDING_SIZES: Record = { [MultilineTextInputSize.SMALL]: '0.125rem', [MultilineTextInputSize.MEDIUM]: '0.25rem', - [MultilineTextInputSize.LARGE]: '0.5rem', + [MultilineTextInputSize.LARGE]: '0.375rem', } const VERTICAL_PADDING_SIZES: Record = { @@ -43,6 +40,7 @@ const SECONDARY_TEXT_SIZES: Record = { } const ComponentBase = styled('div')({ + verticalAlign: 'middle', position: 'relative', borderRadius: '0.25rem', fontFamily: 'var(--font-family-base, sans-serif)', @@ -58,6 +56,7 @@ const CaptureArea = styled('label')({ display: 'block', borderRadius: 'inherit', overflow: 'hidden', + boxSizing: 'border-box', }) CaptureArea.displayName = 'label' @@ -70,6 +69,7 @@ const LabelWrapper = styled('span')({ left: 0, fontSize: '0.85em', maxWidth: '100%', + width: '100%', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', @@ -95,7 +95,7 @@ const Border = styled('span')({ width: '100%', height: '100%', borderRadius: 'inherit', - zIndex: 2, + zIndex: 3, pointerEvents: 'none', transitionProperty: 'border-color', '::before': { @@ -150,22 +150,24 @@ const TextArea = styled('textarea')({ display: 'block', verticalAlign: 'top', width: '100%', + height: '100%', boxSizing: 'border-box', position: 'relative', border: 0, borderRadius: 'inherit', + paddingTop: 0, + paddingBottom: 0, margin: 0, font: 'inherit', - minHeight: '4rem', minWidth: '3rem', maxWidth: '100%', zIndex: 1, transitionProperty: 'background-color, color', + resize: 'none', ':focus': { outline: 0, }, '@media only screen': { - backgroundColor: 'var(--color-bg, white)', color: 'var(--color-fg, black)', }, }) @@ -177,15 +179,19 @@ const HintWrapper = styled('span')({ position: 'absolute', left: 0, fontSize: '0.85em', - opacity: 0.5, maxWidth: '100%', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', zIndex: 2, pointerEvents: 'none', - lineHeight: 1, userSelect: 'none', + width: '100%', +}) + +const HintContent = styled('span')({ + display: 'block', + opacity: 0.5, }) const IndicatorWrapper = styled('span')({ @@ -204,84 +210,43 @@ const IndicatorWrapper = styled('span')({ userSelect: 'none', }) -const propTypes = { +export type Props = Omit, 'as' | 'className' | 'style' | 'placeholder' | 'size'> & { /** * Short textual description indicating the nature of the component's value. */ - label: PropTypes.any, + label?: React.ReactNode, /** * Short textual description as guidelines for valid input values. */ - hint: PropTypes.any, + hint?: React.ReactNode, /** * Size of the component. */ - size: PropTypes.oneOf(['small', 'medium', 'large']), + size?: MultilineTextInputSize, /** * Additional description, usually graphical, indicating the nature of the component's value. */ - indicator: PropTypes.node, - /** - * Should the component accept multiple lines of input? - */ - multiline: PropTypes.bool, - /** - * Is the component active? - */ - disabled: PropTypes.bool, - /** - * Should the component resize itself to show all its value? - */ - autoResize: PropTypes.bool, - /** - * Placeholder of the component when there is no value. - */ - placeholder: PropTypes.string, - /** - * How many rows should the component display if it accepts multiline input? - */ - rows: PropTypes.number, - /** - * Does the button display a border? - */ - border: PropTypes.bool, - /** - * Event handler triggered when the component changes value. - */ - onChange: PropTypes.func, - /** - * Event handler triggered when the component receives focus. - */ - onFocus: PropTypes.func, + indicator?: React.ReactNode, /** - * Event handler triggered when the component loses focus. + * Should the component display a border? */ - onBlur: PropTypes.func, + border?: boolean, /** * Should the component be displayed with an alternate appearance? */ - alternate: PropTypes.bool, + alternate?: boolean, /** - * Default value of the component. + * Should the component occupy the whole width of its parent? */ - defaultValue: PropTypes.any, + block?: boolean, /** - * Value of the component. + * How many rows should the component display if it accepts multiline input? */ - value: PropTypes.any, + rows?: number, /** - * Name of the form field associated with this component. + * Should the component resize itself to show all its value? */ - name: PropTypes.string, -} - -type Props = { - label?: React.ReactNode, - hint?: React.ReactNode, - size?: MultilineTextInputSize, - indicator?: React.ReactNode, - border?: boolean, - alternate?: boolean, + autoResize?: boolean, } /** @@ -289,7 +254,7 @@ type Props = { * * This component supports multiline input and adjusts its layout accordingly. */ -const MultilineTextInput = React.forwardRef( +export const MultilineTextInput = React.forwardRef( ( { label = '', @@ -297,8 +262,6 @@ const MultilineTextInput = React.forwardRef( indicator = null, size = MultilineTextInputSize.MEDIUM, disabled = false, - autoResize = false, - placeholder = '', rows = 3, border = false, onChange, @@ -308,67 +271,79 @@ const MultilineTextInput = React.forwardRef( defaultValue, value, name, + block = false, + ...etcProps }, ref, ) => ( {border && } - - - {stringify(label)} - - {stringify(label).length > 0 && ' '} + + {label && ( + <> + + {label} + + {' '} + + )}