mirror of
https://github.com/zhigang1992/react.git
synced 2026-04-24 04:15:54 +08:00
style(prettier): format code style
This commit is contained in:
@@ -8,7 +8,7 @@ describe('Input', () => {
|
||||
const wrapper = mount(<Input placeholder="placeholder" />)
|
||||
expect(() => wrapper.unmount()).not.toThrow()
|
||||
})
|
||||
|
||||
|
||||
it('should work with different sizes', () => {
|
||||
const wrapper = mount(
|
||||
<div>
|
||||
@@ -16,29 +16,31 @@ describe('Input', () => {
|
||||
<Input size="small" />
|
||||
<Input size="large" />
|
||||
<Input width="50%" />
|
||||
</div>
|
||||
</div>,
|
||||
)
|
||||
expect(wrapper.html()).toMatchSnapshot()
|
||||
})
|
||||
|
||||
|
||||
it('should work with different status', () => {
|
||||
const wrapper = mount(
|
||||
<div>
|
||||
<Input status="secondary" />
|
||||
<Input status="success" />
|
||||
<Input status="warning" />
|
||||
</div>
|
||||
</div>,
|
||||
)
|
||||
expect(wrapper.html()).toMatchSnapshot()
|
||||
})
|
||||
|
||||
|
||||
it('should be work with label', () => {
|
||||
const wrapper = mount(
|
||||
<div>
|
||||
<Input label="label" />
|
||||
<Input labelRight="label" />
|
||||
<Input><span>Block Label</span></Input>
|
||||
</div>
|
||||
<Input>
|
||||
<span>Block Label</span>
|
||||
</Input>
|
||||
</div>,
|
||||
)
|
||||
expect(wrapper.html()).toMatchSnapshot()
|
||||
})
|
||||
@@ -47,95 +49,104 @@ describe('Input', () => {
|
||||
<div>
|
||||
<Input icon={<span>test-icon</span>} />
|
||||
<Input iconRight={<span>test-icon</span>} />
|
||||
</div>
|
||||
</div>,
|
||||
)
|
||||
expect(wrapper.html()).toMatchSnapshot()
|
||||
})
|
||||
|
||||
|
||||
it('should set input from value', () => {
|
||||
let wrapper = mount(<Input initialValue="test" />)
|
||||
let input = wrapper.find('input').getDOMNode() as HTMLInputElement
|
||||
expect(input.value).toEqual('test')
|
||||
|
||||
|
||||
wrapper = mount(<Input value="test2" />)
|
||||
input = wrapper.find('input').getDOMNode() as HTMLInputElement
|
||||
expect(input.value).toEqual('test2')
|
||||
|
||||
|
||||
wrapper.setProps({ value: 'test3' })
|
||||
input = wrapper.find('input').getDOMNode() as HTMLInputElement
|
||||
expect(input.value).toEqual('test3')
|
||||
})
|
||||
|
||||
|
||||
it('should trigger event when input changed', () => {
|
||||
let value = ''
|
||||
const callback = jest.fn()
|
||||
.mockImplementation((e: React.ChangeEvent<HTMLInputElement>) => value = e.target.value)
|
||||
const callback = jest
|
||||
.fn()
|
||||
.mockImplementation((e: React.ChangeEvent<HTMLInputElement>) => (value = e.target.value))
|
||||
const wrapper = mount(<Input onChange={callback} />)
|
||||
wrapper.find('input').at(0)
|
||||
wrapper
|
||||
.find('input')
|
||||
.at(0)
|
||||
.simulate('change', { target: { value: 'test' } })
|
||||
expect(callback).toHaveBeenCalled()
|
||||
expect(value).toEqual('test')
|
||||
})
|
||||
|
||||
|
||||
it('should ignore event when input disabled', () => {
|
||||
const callback = jest.fn()
|
||||
const wrapper = mount(<Input onChange={callback} disabled />)
|
||||
wrapper.find('input').at(0)
|
||||
wrapper
|
||||
.find('input')
|
||||
.at(0)
|
||||
.simulate('change', { target: { value: 'test' } })
|
||||
expect(callback).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
|
||||
it('should ignore event when input readonly', () => {
|
||||
const callback = jest.fn()
|
||||
const wrapper = mount(<Input onChange={callback} readOnly />)
|
||||
wrapper.find('input').at(0)
|
||||
wrapper
|
||||
.find('input')
|
||||
.at(0)
|
||||
.simulate('change', { target: { value: 'test' } })
|
||||
expect(callback).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
|
||||
it('should clear text', () => {
|
||||
let value = ''
|
||||
const callback = jest.fn()
|
||||
.mockImplementation((e: React.ChangeEvent<HTMLInputElement>) => value = e.target.value)
|
||||
const callback = jest
|
||||
.fn()
|
||||
.mockImplementation((e: React.ChangeEvent<HTMLInputElement>) => (value = e.target.value))
|
||||
const clearHandler = jest.fn()
|
||||
const wrapper = mount(<Input onChange={callback} clearable onClearClick={clearHandler} />)
|
||||
|
||||
wrapper.find('input').at(0)
|
||||
|
||||
wrapper
|
||||
.find('input')
|
||||
.at(0)
|
||||
.simulate('change', { target: { value: 'test' } })
|
||||
expect(callback).toHaveBeenCalled()
|
||||
expect(value).toEqual('test')
|
||||
|
||||
wrapper.find('.clear-icon').at(0)
|
||||
.simulate('click', nativeEvent)
|
||||
|
||||
wrapper.find('.clear-icon').at(0).simulate('click', nativeEvent)
|
||||
expect(clearHandler).toHaveBeenCalled()
|
||||
expect(value).toEqual('')
|
||||
})
|
||||
|
||||
|
||||
it('should trigger focus correctly', () => {
|
||||
const focus = jest.fn()
|
||||
const blur = jest.fn()
|
||||
const wrapper = mount(<Input onFocus={focus} onBlur={blur} />)
|
||||
|
||||
|
||||
const input = wrapper.find('input').at(0)
|
||||
input.simulate('focus')
|
||||
expect(focus).toHaveBeenCalled()
|
||||
input.simulate('blur')
|
||||
expect(blur).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
|
||||
it('should trigger icon event', () => {
|
||||
const click = jest.fn()
|
||||
const wrapper = mount(
|
||||
<Input icon={<span id="test-icon">icon</span>} onIconClick={click} iconClickable />
|
||||
<Input icon={<span id="test-icon">icon</span>} onIconClick={click} iconClickable />,
|
||||
)
|
||||
wrapper.find('#test-icon').simulate('click', nativeEvent)
|
||||
expect(click).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
|
||||
it('should ignore icon event when input disabled', () => {
|
||||
const click = jest.fn()
|
||||
const wrapper = mount(
|
||||
<Input icon={<span id="test-icon">icon</span>} onIconClick={click} iconClickable disabled />
|
||||
<Input icon={<span id="test-icon">icon</span>} onIconClick={click} iconClickable disabled />,
|
||||
)
|
||||
wrapper.find('#test-icon').simulate('click', nativeEvent)
|
||||
expect(click).not.toHaveBeenCalled()
|
||||
|
||||
@@ -11,14 +11,14 @@ describe('InputPassword', () => {
|
||||
expect(wrapper.html()).toMatchSnapshot()
|
||||
expect(() => wrapper.unmount()).not.toThrow()
|
||||
})
|
||||
|
||||
|
||||
it('should toggle input type', () => {
|
||||
const wrapper = mount(<Input.Password />)
|
||||
wrapper.find('.input-icon').simulate('click', nativeEvent)
|
||||
const el = wrapper.find('input').getDOMNode() as HTMLInputElement
|
||||
expect(el.type).toEqual('text')
|
||||
})
|
||||
|
||||
|
||||
it('should hide toggle icon', () => {
|
||||
const wrapper = mount(<Input.Password hideToggle />)
|
||||
expect(wrapper.find('.input-icon').length).toBe(0)
|
||||
|
||||
@@ -5,8 +5,7 @@ import { Input, useInput } from 'components'
|
||||
describe('UseInput', () => {
|
||||
it('should follow change with use-input', () => {
|
||||
let log = ''
|
||||
const logSpy = jest.spyOn(console, 'log')
|
||||
.mockImplementation(msg => log = msg)
|
||||
const logSpy = jest.spyOn(console, 'log').mockImplementation((msg) => (log = msg))
|
||||
const MockInput: React.FC<{ value?: string }> = ({ value }) => {
|
||||
const { state, setState, bindings } = useInput('')
|
||||
useEffect(() => {
|
||||
@@ -17,23 +16,28 @@ describe('UseInput', () => {
|
||||
}, [state])
|
||||
return <Input {...bindings} />
|
||||
}
|
||||
|
||||
|
||||
const wrapper = mount(<MockInput />)
|
||||
wrapper.setProps({ value: 'test' })
|
||||
const input = wrapper.find('input').at(0).getDOMNode() as HTMLInputElement
|
||||
|
||||
|
||||
expect(input.value).toEqual('test')
|
||||
expect(log).toContain('test')
|
||||
|
||||
|
||||
log = ''
|
||||
wrapper.find('input').at(0)
|
||||
.simulate('change', { target: { value: 'test-change' }})
|
||||
wrapper
|
||||
.find('input')
|
||||
.at(0)
|
||||
.simulate('change', { target: { value: 'test-change' } })
|
||||
expect(log).toContain('test-change')
|
||||
logSpy.mockRestore()
|
||||
})
|
||||
|
||||
|
||||
it('should follow change with use-input', () => {
|
||||
const MockInput: React.FC<{ value?: string, resetValue?: boolean }> = ({ value, resetValue }) => {
|
||||
const MockInput: React.FC<{ value?: string; resetValue?: boolean }> = ({
|
||||
value,
|
||||
resetValue,
|
||||
}) => {
|
||||
const { reset, setState, bindings } = useInput('')
|
||||
useEffect(() => {
|
||||
if (value) setState(value)
|
||||
@@ -43,12 +47,12 @@ describe('UseInput', () => {
|
||||
}, [resetValue])
|
||||
return <Input {...bindings} />
|
||||
}
|
||||
|
||||
|
||||
const wrapper = mount(<MockInput />)
|
||||
wrapper.setProps({ value: 'test' })
|
||||
let input = wrapper.find('input').at(0).getDOMNode() as HTMLInputElement
|
||||
expect(input.value).toEqual('test')
|
||||
|
||||
|
||||
wrapper.setProps({ resetValue: true })
|
||||
input = wrapper.find('input').at(0).getDOMNode() as HTMLInputElement
|
||||
expect(input.value).toEqual('')
|
||||
|
||||
@@ -1,14 +1,11 @@
|
||||
import React from 'react'
|
||||
import useTheme from '../styles/use-theme'
|
||||
|
||||
export interface InputBlockLabelLabel {
|
||||
}
|
||||
export interface InputBlockLabelLabel {}
|
||||
|
||||
const InputBlockLabel: React.FC<React.PropsWithChildren<InputBlockLabelLabel>> = ({
|
||||
children,
|
||||
}) => {
|
||||
const InputBlockLabel: React.FC<React.PropsWithChildren<InputBlockLabelLabel>> = ({ children }) => {
|
||||
const theme = useTheme()
|
||||
|
||||
|
||||
return (
|
||||
<label>
|
||||
{children}
|
||||
@@ -22,11 +19,11 @@ const InputBlockLabel: React.FC<React.PropsWithChildren<InputBlockLabelLabel>> =
|
||||
font-size: 1rem;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
|
||||
label > :global(*:first-child) {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
|
||||
label > :global(*:last-child) {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
@@ -8,9 +8,7 @@ interface Props {
|
||||
disabled?: boolean
|
||||
}
|
||||
|
||||
const InputIconClear: React.FC<Props> = ({
|
||||
onClick, heightRatio, disabled, visibale,
|
||||
}) => {
|
||||
const InputIconClear: React.FC<Props> = ({ onClick, heightRatio, disabled, visibale }) => {
|
||||
const theme = useTheme()
|
||||
const width = useMemo(() => {
|
||||
return heightRatio ? `calc(10.66px * ${heightRatio})` : '18px'
|
||||
@@ -23,12 +21,18 @@ const InputIconClear: React.FC<Props> = ({
|
||||
}
|
||||
return (
|
||||
<div onClick={clickHandler} className={`clear-icon ${visibale ? 'visibale' : ''}`}>
|
||||
<svg viewBox="0 0 24 24" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round"
|
||||
strokeLinejoin="round" fill="none" shapeRendering="geometricPrecision">
|
||||
<svg
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
strokeWidth="1.5"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
fill="none"
|
||||
shapeRendering="geometricPrecision">
|
||||
<path d="M18 6L6 18" />
|
||||
<path d="M6 6l12 12" />
|
||||
</svg>
|
||||
|
||||
|
||||
<style jsx>{`
|
||||
.clear-icon {
|
||||
padding: 0 ${theme.layout.gapHalf};
|
||||
@@ -43,16 +47,16 @@ const InputIconClear: React.FC<Props> = ({
|
||||
visibility: hidden;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
|
||||
.visibale {
|
||||
visibility: visible;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
|
||||
.clear-icon:hover {
|
||||
color: ${disabled ? theme.palette.accents_3 : theme.palette.foreground};
|
||||
}
|
||||
|
||||
|
||||
svg {
|
||||
color: currentColor;
|
||||
width: ${width};
|
||||
|
||||
@@ -8,9 +8,7 @@ export interface InputIconProps {
|
||||
onClick: (e: React.MouseEvent<HTMLDivElement>) => void
|
||||
}
|
||||
|
||||
const InputIcon: React.FC<InputIconProps> = ({
|
||||
icon, ratio, clickable, onClick,
|
||||
}) => {
|
||||
const InputIcon: React.FC<InputIconProps> = ({ icon, ratio, clickable, onClick }) => {
|
||||
const theme = useTheme()
|
||||
const width = useMemo(() => {
|
||||
return `calc(${ratio} * ${theme.layout.gap} * .42)`
|
||||
|
||||
@@ -7,10 +7,12 @@ export interface InputLabel {
|
||||
}
|
||||
|
||||
const InputLabel: React.FC<React.PropsWithChildren<InputLabel>> = ({
|
||||
children, isRight, fontSize,
|
||||
children,
|
||||
isRight,
|
||||
fontSize,
|
||||
}) => {
|
||||
const theme = useTheme()
|
||||
|
||||
|
||||
return (
|
||||
<span className={isRight ? 'right' : ''}>
|
||||
{children}
|
||||
@@ -33,7 +35,7 @@ const InputLabel: React.FC<React.PropsWithChildren<InputLabel>> = ({
|
||||
font-size: ${fontSize};
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
|
||||
span.right {
|
||||
border-top-left-radius: 0;
|
||||
border-bottom-left-radius: 0;
|
||||
|
||||
@@ -14,7 +14,7 @@ export type InputProps = Props & typeof defaultProps & NativeAttrs
|
||||
|
||||
const simulateChangeEvent = (
|
||||
el: HTMLInputElement,
|
||||
event: React.MouseEvent<HTMLDivElement>
|
||||
event: React.MouseEvent<HTMLDivElement>,
|
||||
): React.ChangeEvent<HTMLInputElement> => {
|
||||
return {
|
||||
...event,
|
||||
@@ -23,196 +23,240 @@ const simulateChangeEvent = (
|
||||
}
|
||||
}
|
||||
|
||||
const Input = React.forwardRef<HTMLInputElement, React.PropsWithChildren<InputProps>>(({
|
||||
label, labelRight, size, status, icon, iconRight, iconClickable, onIconClick,
|
||||
initialValue, onChange, readOnly, value, onClearClick, clearable, width,
|
||||
className, onBlur, onFocus, autoComplete, placeholder, children, disabled,
|
||||
...props
|
||||
}, ref: React.Ref<HTMLInputElement | null>) => {
|
||||
const theme = useTheme()
|
||||
const inputRef = useRef<HTMLInputElement>(null)
|
||||
useImperativeHandle(ref, () => inputRef.current)
|
||||
const Input = React.forwardRef<HTMLInputElement, React.PropsWithChildren<InputProps>>(
|
||||
(
|
||||
{
|
||||
label,
|
||||
labelRight,
|
||||
size,
|
||||
status,
|
||||
icon,
|
||||
iconRight,
|
||||
iconClickable,
|
||||
onIconClick,
|
||||
initialValue,
|
||||
onChange,
|
||||
readOnly,
|
||||
value,
|
||||
onClearClick,
|
||||
clearable,
|
||||
width,
|
||||
className,
|
||||
onBlur,
|
||||
onFocus,
|
||||
autoComplete,
|
||||
placeholder,
|
||||
children,
|
||||
disabled,
|
||||
...props
|
||||
},
|
||||
ref: React.Ref<HTMLInputElement | null>,
|
||||
) => {
|
||||
const theme = useTheme()
|
||||
const inputRef = useRef<HTMLInputElement>(null)
|
||||
useImperativeHandle(ref, () => inputRef.current)
|
||||
|
||||
const [selfValue, setSelfValue] = useState<string>(initialValue)
|
||||
const [hover, setHover] = useState<boolean>(false)
|
||||
const { heightRatio, fontSize } = useMemo(() => getSizes(size),[size])
|
||||
const showClearIcon = useMemo(() => clearable && selfValue !== '', [selfValue, clearable])
|
||||
const labelClasses = useMemo(
|
||||
() => labelRight ? 'right-label' : (label ? 'left-label' : ''),
|
||||
[label, labelRight],
|
||||
)
|
||||
const iconClasses = useMemo(
|
||||
() => iconRight ? 'right-icon' : (icon ? 'left-icon' : ''),
|
||||
[icon, iconRight],
|
||||
)
|
||||
const { color, borderColor, hoverBorder } = useMemo(
|
||||
() => getColors(theme.palette, status),
|
||||
[theme.palette, status],
|
||||
)
|
||||
const changeHandler = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
if (disabled || readOnly) return
|
||||
setSelfValue(event.target.value)
|
||||
onChange && onChange(event)
|
||||
}
|
||||
|
||||
const clearHandler = (event: React.MouseEvent<HTMLDivElement>) => {
|
||||
setSelfValue('')
|
||||
onClearClick && onClearClick(event)
|
||||
/* istanbul ignore next */
|
||||
if (!inputRef.current) return
|
||||
|
||||
const changeEvent = simulateChangeEvent(inputRef.current, event)
|
||||
changeEvent.target.value = ''
|
||||
onChange && onChange(changeEvent)
|
||||
inputRef.current.focus()
|
||||
}
|
||||
const [selfValue, setSelfValue] = useState<string>(initialValue)
|
||||
const [hover, setHover] = useState<boolean>(false)
|
||||
const { heightRatio, fontSize } = useMemo(() => getSizes(size), [size])
|
||||
const showClearIcon = useMemo(() => clearable && selfValue !== '', [selfValue, clearable])
|
||||
const labelClasses = useMemo(() => (labelRight ? 'right-label' : label ? 'left-label' : ''), [
|
||||
label,
|
||||
labelRight,
|
||||
])
|
||||
const iconClasses = useMemo(() => (iconRight ? 'right-icon' : icon ? 'left-icon' : ''), [
|
||||
icon,
|
||||
iconRight,
|
||||
])
|
||||
const { color, borderColor, hoverBorder } = useMemo(() => getColors(theme.palette, status), [
|
||||
theme.palette,
|
||||
status,
|
||||
])
|
||||
const changeHandler = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
if (disabled || readOnly) return
|
||||
setSelfValue(event.target.value)
|
||||
onChange && onChange(event)
|
||||
}
|
||||
|
||||
const focusHandler = (e: React.FocusEvent<HTMLInputElement>) => {
|
||||
setHover(true)
|
||||
onFocus && onFocus(e)
|
||||
}
|
||||
const blurHandler = (e: React.FocusEvent<HTMLInputElement>) => {
|
||||
setHover(false)
|
||||
onBlur && onBlur(e)
|
||||
}
|
||||
|
||||
const iconClickHandler = (e: React.MouseEvent<HTMLDivElement>) => {
|
||||
if (disabled) return
|
||||
onIconClick && onIconClick(e)
|
||||
}
|
||||
const iconProps = useMemo(() => ({
|
||||
ratio: heightRatio,
|
||||
clickable: iconClickable,
|
||||
onClick: iconClickHandler,
|
||||
}), [heightRatio, iconClickable])
|
||||
|
||||
useEffect(() => {
|
||||
if (value === undefined) return
|
||||
setSelfValue(value)
|
||||
}, [value])
|
||||
|
||||
return (
|
||||
<div className="with-label">
|
||||
{children && <InputBlockLabel>{children}</InputBlockLabel>}
|
||||
<div className={`input-container ${className}`}>
|
||||
{label && <InputLabel fontSize={fontSize}>{label}</InputLabel>}
|
||||
<div className={`input-wrapper ${hover ? 'hover' : ''} ${disabled ? 'disabled' : ''} ${labelClasses}`}>
|
||||
{icon && <InputIcon icon={icon} {...iconProps} />}
|
||||
<input type="text" ref={inputRef}
|
||||
className={`${disabled ? 'disabled' : ''} ${iconClasses}`}
|
||||
value={selfValue}
|
||||
placeholder={placeholder}
|
||||
disabled={disabled}
|
||||
readOnly={readOnly}
|
||||
onFocus={focusHandler}
|
||||
onBlur={blurHandler}
|
||||
onChange={changeHandler}
|
||||
autoComplete={autoComplete}
|
||||
{...props}
|
||||
/>
|
||||
{clearable && <InputClearIcon
|
||||
visibale={showClearIcon}
|
||||
heightRatio={heightRatio}
|
||||
disabled={disabled || readOnly}
|
||||
onClick={clearHandler} />}
|
||||
{iconRight && <InputIcon icon={iconRight} {...iconProps} />}
|
||||
const clearHandler = (event: React.MouseEvent<HTMLDivElement>) => {
|
||||
setSelfValue('')
|
||||
onClearClick && onClearClick(event)
|
||||
/* istanbul ignore next */
|
||||
if (!inputRef.current) return
|
||||
|
||||
const changeEvent = simulateChangeEvent(inputRef.current, event)
|
||||
changeEvent.target.value = ''
|
||||
onChange && onChange(changeEvent)
|
||||
inputRef.current.focus()
|
||||
}
|
||||
|
||||
const focusHandler = (e: React.FocusEvent<HTMLInputElement>) => {
|
||||
setHover(true)
|
||||
onFocus && onFocus(e)
|
||||
}
|
||||
const blurHandler = (e: React.FocusEvent<HTMLInputElement>) => {
|
||||
setHover(false)
|
||||
onBlur && onBlur(e)
|
||||
}
|
||||
|
||||
const iconClickHandler = (e: React.MouseEvent<HTMLDivElement>) => {
|
||||
if (disabled) return
|
||||
onIconClick && onIconClick(e)
|
||||
}
|
||||
const iconProps = useMemo(
|
||||
() => ({
|
||||
ratio: heightRatio,
|
||||
clickable: iconClickable,
|
||||
onClick: iconClickHandler,
|
||||
}),
|
||||
[heightRatio, iconClickable],
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
if (value === undefined) return
|
||||
setSelfValue(value)
|
||||
}, [value])
|
||||
|
||||
return (
|
||||
<div className="with-label">
|
||||
{children && <InputBlockLabel>{children}</InputBlockLabel>}
|
||||
<div className={`input-container ${className}`}>
|
||||
{label && <InputLabel fontSize={fontSize}>{label}</InputLabel>}
|
||||
<div
|
||||
className={`input-wrapper ${hover ? 'hover' : ''} ${
|
||||
disabled ? 'disabled' : ''
|
||||
} ${labelClasses}`}>
|
||||
{icon && <InputIcon icon={icon} {...iconProps} />}
|
||||
<input
|
||||
type="text"
|
||||
ref={inputRef}
|
||||
className={`${disabled ? 'disabled' : ''} ${iconClasses}`}
|
||||
value={selfValue}
|
||||
placeholder={placeholder}
|
||||
disabled={disabled}
|
||||
readOnly={readOnly}
|
||||
onFocus={focusHandler}
|
||||
onBlur={blurHandler}
|
||||
onChange={changeHandler}
|
||||
autoComplete={autoComplete}
|
||||
{...props}
|
||||
/>
|
||||
{clearable && (
|
||||
<InputClearIcon
|
||||
visibale={showClearIcon}
|
||||
heightRatio={heightRatio}
|
||||
disabled={disabled || readOnly}
|
||||
onClick={clearHandler}
|
||||
/>
|
||||
)}
|
||||
{iconRight && <InputIcon icon={iconRight} {...iconProps} />}
|
||||
</div>
|
||||
{labelRight && (
|
||||
<InputLabel fontSize={fontSize} isRight={true}>
|
||||
{labelRight}
|
||||
</InputLabel>
|
||||
)}
|
||||
</div>
|
||||
{labelRight && <InputLabel fontSize={fontSize} isRight={true}>{labelRight}</InputLabel>}
|
||||
<style jsx>{`
|
||||
.with-label {
|
||||
display: inline-block;
|
||||
width: ${width};
|
||||
box-sizing: border-box;
|
||||
-webkit-box-align: center;
|
||||
}
|
||||
|
||||
.input-container {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
width: ${width};
|
||||
height: calc(${heightRatio} * ${theme.layout.gap});
|
||||
}
|
||||
|
||||
.input-wrapper {
|
||||
display: inline-flex;
|
||||
vertical-align: middle;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
flex: 1;
|
||||
user-select: none;
|
||||
border-radius: ${theme.layout.radius};
|
||||
border: 1px solid ${borderColor};
|
||||
transition: border 0.2s ease 0s, color 0.2s ease 0s;
|
||||
}
|
||||
|
||||
.input-wrapper.left-label {
|
||||
border-top-left-radius: 0;
|
||||
border-bottom-left-radius: 0;
|
||||
}
|
||||
|
||||
.input-wrapper.right-label {
|
||||
border-top-right-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
}
|
||||
|
||||
.input-wrapper.disabled {
|
||||
background-color: ${theme.palette.accents_1};
|
||||
border-color: ${theme.palette.accents_2};
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
input.disabled {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.input-wrapper.hover {
|
||||
border-color: ${hoverBorder};
|
||||
}
|
||||
|
||||
input {
|
||||
margin: 4px 10px;
|
||||
padding: 0;
|
||||
box-shadow: none;
|
||||
font-size: ${fontSize};
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
color: ${color};
|
||||
outline: none;
|
||||
border-radius: 0;
|
||||
width: 100%;
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
|
||||
input.left-icon {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
input.right-icon {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
::placeholder,
|
||||
::-moz-placeholder,
|
||||
:-ms-input-placeholder,
|
||||
::-webkit-input-placeholder {
|
||||
color: ${theme.palette.accents_3};
|
||||
}
|
||||
|
||||
input:-webkit-autofill,
|
||||
input:-webkit-autofill:hover,
|
||||
input:-webkit-autofill:active,
|
||||
input:-webkit-autofill:focus {
|
||||
-webkit-box-shadow: 0 0 0 30px ${theme.palette.background} inset !important;
|
||||
-webkit-text-fill-color: ${color} !important;
|
||||
}
|
||||
`}</style>
|
||||
</div>
|
||||
<style jsx>{`
|
||||
.with-label {
|
||||
display: inline-block;
|
||||
width: ${width};
|
||||
box-sizing: border-box;
|
||||
-webkit-box-align: center;
|
||||
}
|
||||
|
||||
.input-container {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
width: ${width};
|
||||
height: calc(${heightRatio} * ${theme.layout.gap});
|
||||
}
|
||||
|
||||
.input-wrapper {
|
||||
display: inline-flex;
|
||||
vertical-align: middle;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
flex: 1;
|
||||
user-select: none;
|
||||
border-radius: ${theme.layout.radius};
|
||||
border: 1px solid ${borderColor};
|
||||
transition: border 0.2s ease 0s, color 0.2s ease 0s;
|
||||
}
|
||||
|
||||
.input-wrapper.left-label {
|
||||
border-top-left-radius: 0;
|
||||
border-bottom-left-radius: 0;
|
||||
}
|
||||
|
||||
.input-wrapper.right-label {
|
||||
border-top-right-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
}
|
||||
|
||||
.input-wrapper.disabled {
|
||||
background-color: ${theme.palette.accents_1};
|
||||
border-color: ${theme.palette.accents_2};
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
input.disabled {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.input-wrapper.hover {
|
||||
border-color: ${hoverBorder};
|
||||
}
|
||||
|
||||
input {
|
||||
margin: 4px 10px;
|
||||
padding: 0;
|
||||
box-shadow: none;
|
||||
font-size: ${fontSize};
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
color: ${color};
|
||||
outline: none;
|
||||
border-radius: 0;
|
||||
width: 100%;
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
|
||||
input.left-icon {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
input.right-icon {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
::placeholder, ::-moz-placeholder, :-ms-input-placeholder, ::-webkit-input-placeholder {
|
||||
color: ${theme.palette.accents_3};
|
||||
}
|
||||
|
||||
input:-webkit-autofill,
|
||||
input:-webkit-autofill:hover,
|
||||
input:-webkit-autofill:active,
|
||||
input:-webkit-autofill:focus {
|
||||
-webkit-box-shadow: 0 0 0 30px ${theme.palette.background} inset !important;
|
||||
-webkit-text-fill-color: ${color} !important;
|
||||
}
|
||||
`}</style>
|
||||
</div>
|
||||
)
|
||||
})
|
||||
)
|
||||
},
|
||||
)
|
||||
|
||||
type InputComponent<P = {}> = React.ForwardRefExoticComponent<P> & {
|
||||
Textarea: typeof Textarea
|
||||
Password: typeof InputPassword
|
||||
}
|
||||
type ComponentProps = Partial<typeof defaultProps> & Omit<Props, keyof typeof defaultProps> & NativeAttrs
|
||||
type ComponentProps = Partial<typeof defaultProps> &
|
||||
Omit<Props, keyof typeof defaultProps> &
|
||||
NativeAttrs
|
||||
|
||||
Input.defaultProps = defaultProps
|
||||
|
||||
|
||||
@@ -6,18 +6,26 @@ interface Props {
|
||||
|
||||
const PasswordIcon: React.FC<Props> = ({ visible }) => {
|
||||
return (
|
||||
<svg viewBox="0 0 24 24" width="16" height="16" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round"
|
||||
strokeLinejoin="round" fill="none" shapeRendering="geometricPrecision" style={{ color: 'currentColor' }}>
|
||||
<svg
|
||||
viewBox="0 0 24 24"
|
||||
width="16"
|
||||
height="16"
|
||||
stroke="currentColor"
|
||||
strokeWidth="1.5"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
fill="none"
|
||||
shapeRendering="geometricPrecision"
|
||||
style={{ color: 'currentColor' }}>
|
||||
{!visible ? (
|
||||
<>
|
||||
<path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/>
|
||||
<circle cx="12" cy="12" r="3"/>
|
||||
<path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z" />
|
||||
<circle cx="12" cy="12" r="3" />
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<path
|
||||
d="M17.94 17.94A10.07 10.07 0 0112 20c-7 0-11-8-11-8a18.45 18.45 0 015.06-5.94M9.9 4.24A9.12 9.12 0 0112 4c7 0 11 8 11 8a18.5 18.5 0 01-2.16 3.19m-6.72-1.07a3 3 0 11-4.24-4.24"/>
|
||||
<path d="M1 1l22 22"/>
|
||||
<path d="M17.94 17.94A10.07 10.07 0 0112 20c-7 0-11-8-11-8a18.45 18.45 0 015.06-5.94M9.9 4.24A9.12 9.12 0 0112 4c7 0 11 8 11 8a18.5 18.5 0 01-2.16 3.19m-6.72-1.07a3 3 0 11-4.24-4.24" />
|
||||
<path d="M1 1l22 22" />
|
||||
</>
|
||||
)}
|
||||
</svg>
|
||||
|
||||
@@ -16,35 +16,41 @@ const passwordDefaultProps = {
|
||||
type NativeAttrs = Omit<React.InputHTMLAttributes<any>, keyof PasswordProps>
|
||||
export type InputPasswordProps = PasswordProps & typeof passwordDefaultProps & NativeAttrs
|
||||
|
||||
const InputPassword = React.forwardRef<HTMLInputElement, React.PropsWithChildren<InputPasswordProps>>(({
|
||||
hideToggle, children, ...props
|
||||
}, ref: React.Ref<HTMLInputElement | null>) => {
|
||||
const InputPassword = React.forwardRef<
|
||||
HTMLInputElement,
|
||||
React.PropsWithChildren<InputPasswordProps>
|
||||
>(({ hideToggle, children, ...props }, ref: React.Ref<HTMLInputElement | null>) => {
|
||||
const inputRef = useRef<HTMLInputElement>(null)
|
||||
const [visible, setVisible] = useState<boolean>(false)
|
||||
useImperativeHandle(ref, () => inputRef.current)
|
||||
|
||||
|
||||
const iconClickHandler = () => {
|
||||
setVisible(v => !v)
|
||||
setVisible((v) => !v)
|
||||
/* istanbul ignore next */
|
||||
if (inputRef && inputRef.current) {
|
||||
inputRef.current.focus()
|
||||
}
|
||||
}
|
||||
|
||||
const inputProps = useMemo(() => ({
|
||||
...props,
|
||||
ref: inputRef,
|
||||
iconClickable: true,
|
||||
onIconClick: iconClickHandler,
|
||||
type: visible ? 'text' : 'password',
|
||||
}), [props, iconClickHandler, visible, inputRef])
|
||||
|
||||
const inputProps = useMemo(
|
||||
() => ({
|
||||
...props,
|
||||
ref: inputRef,
|
||||
iconClickable: true,
|
||||
onIconClick: iconClickHandler,
|
||||
type: visible ? 'text' : 'password',
|
||||
}),
|
||||
[props, iconClickHandler, visible, inputRef],
|
||||
)
|
||||
const icon = useMemo(() => {
|
||||
if (hideToggle) return null
|
||||
return <PasswordIcon visible={visible} />
|
||||
}, [hideToggle, visible])
|
||||
|
||||
|
||||
return (
|
||||
<Input iconRight={icon} {...inputProps}>{children}</Input>
|
||||
<Input iconRight={icon} {...inputProps}>
|
||||
{children}
|
||||
</Input>
|
||||
)
|
||||
})
|
||||
|
||||
|
||||
@@ -63,7 +63,7 @@ export const getColors = (palette: ZeitUIThemesPalette, status?: NormalTypes): I
|
||||
hoverBorder: palette.errorDark,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
if (!status) return colors.default
|
||||
return colors[status]
|
||||
}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import React, { Dispatch, MutableRefObject, SetStateAction } from 'react'
|
||||
import useCurrentState from '../utils/use-current-state'
|
||||
|
||||
const useInput = (initialValue: string): {
|
||||
const useInput = (
|
||||
initialValue: string,
|
||||
): {
|
||||
state: string
|
||||
setState: Dispatch<SetStateAction<string>>
|
||||
currentRef: MutableRefObject<string>
|
||||
@@ -12,7 +14,7 @@ const useInput = (initialValue: string): {
|
||||
}
|
||||
} => {
|
||||
const [state, setState, currentRef] = useCurrentState<string>(initialValue)
|
||||
|
||||
|
||||
return {
|
||||
state,
|
||||
setState,
|
||||
|
||||
Reference in New Issue
Block a user