Skip to content

Commit

Permalink
feat: finish TextArea interface and UI and Stories
Browse files Browse the repository at this point in the history
  • Loading branch information
mumiao committed Dec 3, 2020
1 parent a7be36f commit 052b16f
Show file tree
Hide file tree
Showing 5 changed files with 139 additions and 42 deletions.
22 changes: 8 additions & 14 deletions src/components/input/TextArea.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,17 @@ import * as React from 'react';
import RcTextArea, { TextAreaProps as RcTextAreaProps } from 'rc-textarea';
import { useEffect, useRef } from 'react';

import { classNames, prefixClaName } from 'mo/common/className';
import { classNames, getBEMElement, getBEMModifier } from 'mo/common/className';
import useMergedState from 'rc-util/lib/hooks/useMergedState';
import { fixControlledValue, resolveOnChange } from './input';
import { fixControlledValue, inputClassName, resolveOnChange } from './input';

export interface TextAreaProps extends RcTextAreaProps {
bordered?: boolean;
showCount?: boolean;
maxLength?: number;
onChange?: (e) => void;
}

const TextArea = ({
bordered = true,
showCount = false,
maxLength,
className,
Expand All @@ -37,10 +35,9 @@ const TextArea = ({
}
}, [props.value, prevValue.current]);

const handleSetValue = (val: string, callback?: () => void) => {
const handleSetValue = (val: string,) => {
if (props.value === undefined) {
setValue(val);
callback?.();
}
};

Expand All @@ -49,18 +46,19 @@ const TextArea = ({
resolveOnChange(innerRef.current!, e, onChange);
};

const prefixCls = prefixClaName('input');
const textAreaClassName = getBEMElement(inputClassName, 'textarea');
const showCountClassName = getBEMModifier(textAreaClassName, 'show-count');

const textArea = (
<RcTextArea
{...props}
value={value}
maxLength={maxLength}
className={classNames(
!bordered ? [`${prefixCls}--borderless`] : '',
className && !showCount ? [className!] : ''
)}
style={showCount ? {} : style}
prefixCls={prefixCls}
prefixCls={inputClassName}
onChange={handleChange}
ref={innerRef}
/>
Expand All @@ -79,11 +77,7 @@ const TextArea = ({

return (
<div
className={classNames(
`${prefixCls}-textarea`,
`${prefixCls}-textarea--show-count`,
className
)}
className={classNames(className, textAreaClassName, showCountClassName)}
style={style}
data-count={dataCount}
>
Expand Down
26 changes: 5 additions & 21 deletions src/components/input/input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export interface InputProps {
readonly onKeyDown?: (e: React.KeyboardEvent<HTMLInputElement>) => void;
readonly onChange?: (e: any) => void;
}
export const inputClassName = prefixClaName('input');

export function fixControlledValue<T>(value: T) {
if (typeof value === 'undefined' || value === null) return '';
Expand All @@ -39,22 +40,6 @@ export function resolveOnChange(
) {
if (onChange) {
let event = e;
if (e.type === 'click') {
event = Object.create(e);
event.target = target;
event.currentTarget = target;
const originalInputValue = target.value;
// change target ref value cause e.target.value should be '' when clear input
target.value = '';
onChange(
event as React.ChangeEvent<
HTMLInputElement | HTMLTextAreaElement
>
);
// reset target ref value
target.value = originalInputValue;
return;
}
onChange(
event as React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>
);
Expand Down Expand Up @@ -116,11 +101,9 @@ class Input extends React.Component<InputProps, InputState> {
this.input = input;
};

setValue(value: string, callback?: () => void) {
setValue(value: string) {
if (this.props.value === undefined) {
this.setState({ value }, callback);
} else {
callback?.();
this.setState({ value });
}
}

Expand All @@ -131,6 +114,7 @@ class Input extends React.Component<InputProps, InputState> {

handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
const { onPressEnter, onKeyDown } = this.props;
// todo
if (e.keyCode === 13 && onPressEnter) {
onPressEnter(e);
}
Expand All @@ -155,7 +139,7 @@ class Input extends React.Component<InputProps, InputState> {
onKeyDown={this.handleKeyDown}
className={classNames(
className,
getInputClassName(prefixClaName('input'), size, disabled)
getInputClassName(inputClassName, size, disabled)
)}
ref={this.saveInput}
/>
Expand Down
2 changes: 1 addition & 1 deletion src/components/input/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ $Input: 'input';
transition: all 0.3s;
width: calc(100% - 16px);

&-textarea {
&__textarea {
&--show-count::after {
color: rgba(0, 0, 0, 0.45);
content: attr(data-count);
Expand Down
1 change: 1 addition & 0 deletions src/style/theme.scss
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,7 @@
}
// =============== Input =============== //
#{prefix($Input)} {
background-color: #1e1e1e;
border: 1px solid transparent;
color: rgb(204, 204, 204);

Expand Down
130 changes: 124 additions & 6 deletions stories/components/15-Input.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,26 +1,144 @@
import * as React from 'react';
import Input from 'mo/components/input';
import { useCallback, useState } from 'react';
import { storiesOf } from '@storybook/react';
import { withKnobs } from '@storybook/addon-knobs';

import Input from 'mo/components/input';
import { propsTable } from '../common/propsTable';

const TextArea = Input.TextArea;
const stories = storiesOf('InpuxBox', module);
const stories = storiesOf('Input', module);
stories.addDecorator(withKnobs);

const propDefinitions = [
{
property: 'autoSize',
propType: 'boolean | object',
required: false,
description: '自适应内容高度,可设置为 true | false 或对象:{ minRows: 2, maxRows: 6 }',
defaultValue: 'false'
},
{
property: 'defaultValue',
propType: 'string',
required: false,
description: '输入框默认内容',
defaultValue: '--'
},
{
property: 'value',
propType: 'string',
required: false,
description: '输入框内容',
defaultValue: '--'
},
{
property: 'maxLength',
propType: 'number',
required: false,
description: '内容最大长度',
defaultValue: '--'
},
{
property: 'showCount',
propType: 'boolean',
required: false,
description: '是否展示字数',
defaultValue: 'false'
},
{
property: 'onPressEnter',
propType: 'function(e) ',
required: false,
description: '按下回车的回调',
defaultValue: '--'
},
{
property: 'onResize',
propType: 'function({ width, height })',
required: false,
description: 'resize 回调',
defaultValue: '--'
}
]
stories.add('Basic Usage', () => {
const renderMultipeTextArea = () => {
const [value, setValue] = useState(10)
const [ size, setSize] = useState({ width: 0, height: 0 })
const onChange = useCallback((e) => setValue(e.target.value), [value])
const onPressEnter = (e) => console.log(`enter key is pressed`);
const onResize = useCallback(({ width, height }) => {
console.log(`size is changed, width:${width} height:${height}`)
setSize((resize) => ({ ...size, width, height }))
}, [size.width, size.height]);
return (
<>
<TextArea placeholder="Autosize height based on content lines" autoSize />
<div style={{ margin: '10px 0' }} />
<TextArea
placeholder="Autosize height with minimum and maximum number of lines"
autoSize={{ minRows: 2, maxRows: 6 }}
/>
<div style={{ margin: '10px 0' }} />
<TextArea
value={value}
onChange={onChange}
onResize={onResize}
onPressEnter={onPressEnter}
placeholder="Controlled autosize"
autoSize={{ minRows: 3, maxRows: 5 }}
/>
</>
)
}
return (
<>
<h2>简述</h2>
<p>Inputbox</p>
<p>通过鼠标或键盘输入内容,是最基础的表单域的包装。</p>
<h3>使用示例 1 - Input</h3>
<Input placeholder="Search" />
<h3>使用示例 2 - TextArea</h3>
<Input placeholder="search" />
<h3>使用示例 2 - 带字数提示的文本域</h3>
<TextArea
placeholder="replace"
maxLength={100}
showCount={true}
style={{ marginTop: 10 }}
/>
<h3>使用示例 3 - 用于多行输入</h3>
<TextArea rows={4} placeholder="multipe line"/>
<h3>使用示例 4 - autoSize 属性适用于 textarea 节点,并且只有高度会自动变化。另外 autoSize 可以设定为一个对象,指定最小行数和最大行数。</h3>
{renderMultipeTextArea()}
</>
);
});

}, {
info: {
text: `
TextTrea代码示例:
~~~js
const [value, setValue] = useState(10)
const onChange = useCallback((e) => setValue(e.target.value), [value])
const onPressEnter = e => ('enter key is pressed')
return (
<>
<TextArea placeholder="Autosize height based on content lines" autoSize />
<div style={{ margin: '10px 0' }} />
<TextArea
placeholder="Autosize height with minimum and maximum number of lines"
autoSize={{ minRows: 2, maxRows: 6 }}
/>
<div style={{ margin: '10px 0' }} />
<TextArea
value={value}
onChange={onChange}
onPressEnter={onPressEnter}
placeholder="Controlled autosize"
autoSize={{ minRows: 3, maxRows: 5 }}
/>
</>
);
~~~
`,
TableComponent: () => propsTable({ propDefinitions })
}
})

0 comments on commit 052b16f

Please sign in to comment.