refactor: repo (#1070)
This commit is contained in:
@@ -0,0 +1,78 @@
|
||||
import { forwardRef, Fragment, Ref } from 'react'
|
||||
import { DecoratedInputProps } from './DecoratedInputProps'
|
||||
|
||||
const getClassNames = (hasLeftDecorations: boolean, hasRightDecorations: boolean) => {
|
||||
return {
|
||||
container: `flex items-stretch position-relative bg-default border-1 border-solid border-main rounded focus-within:ring-info overflow-hidden ${
|
||||
!hasLeftDecorations && !hasRightDecorations ? 'px-2 py-1.5' : ''
|
||||
}`,
|
||||
input: `w-full border-0 focus:shadow-none bg-transparent color-text ${
|
||||
!hasLeftDecorations && hasRightDecorations ? 'pl-2' : ''
|
||||
} ${hasRightDecorations ? 'pr-2' : ''}`,
|
||||
disabled: 'bg-passive-5 cursor-not-allowed',
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Input that can be decorated on the left and right side
|
||||
*/
|
||||
const DecoratedInput = forwardRef(
|
||||
(
|
||||
{
|
||||
type = 'text',
|
||||
className = '',
|
||||
disabled = false,
|
||||
left,
|
||||
right,
|
||||
value,
|
||||
placeholder = '',
|
||||
onChange,
|
||||
onFocus,
|
||||
onKeyDown,
|
||||
autocomplete = false,
|
||||
}: DecoratedInputProps,
|
||||
ref: Ref<HTMLInputElement>,
|
||||
) => {
|
||||
const hasLeftDecorations = Boolean(left?.length)
|
||||
const hasRightDecorations = Boolean(right?.length)
|
||||
const classNames = getClassNames(hasLeftDecorations, hasRightDecorations)
|
||||
|
||||
return (
|
||||
<div className={`${classNames.container} ${disabled ? classNames.disabled : ''} ${className}`}>
|
||||
{left && (
|
||||
<div className="flex items-center px-2 py-1.5">
|
||||
{left.map((leftChild, index) => (
|
||||
<Fragment key={index}>{leftChild}</Fragment>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<input
|
||||
type={type}
|
||||
className={`${classNames.input} ${disabled ? classNames.disabled : ''}`}
|
||||
disabled={disabled}
|
||||
value={value}
|
||||
placeholder={placeholder}
|
||||
onChange={(e) => onChange && onChange((e.target as HTMLInputElement).value)}
|
||||
onFocus={onFocus}
|
||||
onKeyDown={onKeyDown}
|
||||
data-lpignore={type !== 'password' ? true : false}
|
||||
autoComplete={autocomplete ? 'on' : 'off'}
|
||||
ref={ref}
|
||||
/>
|
||||
|
||||
{right && (
|
||||
<div className="flex items-center px-2 py-1.5">
|
||||
{right.map((rightChild, index) => (
|
||||
<div className={index > 0 ? 'ml-3' : ''} key={index}>
|
||||
{rightChild}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
},
|
||||
)
|
||||
|
||||
export default DecoratedInput
|
||||
@@ -0,0 +1,15 @@
|
||||
import { FocusEventHandler, KeyboardEventHandler, ReactNode } from 'react'
|
||||
|
||||
export type DecoratedInputProps = {
|
||||
type?: 'text' | 'email' | 'password'
|
||||
className?: string
|
||||
disabled?: boolean
|
||||
left?: ReactNode[]
|
||||
right?: ReactNode[]
|
||||
value?: string
|
||||
placeholder?: string
|
||||
onChange?: (text: string) => void
|
||||
onFocus?: FocusEventHandler
|
||||
onKeyDown?: KeyboardEventHandler
|
||||
autocomplete?: boolean
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
import { Dispatch, FunctionComponent, Ref, SetStateAction, forwardRef, useState } from 'react'
|
||||
import DecoratedInput from './DecoratedInput'
|
||||
import IconButton from '@/Components/Button/IconButton'
|
||||
import { DecoratedInputProps } from './DecoratedInputProps'
|
||||
|
||||
const Toggle: FunctionComponent<{
|
||||
isToggled: boolean
|
||||
setIsToggled: Dispatch<SetStateAction<boolean>>
|
||||
}> = ({ isToggled, setIsToggled }) => (
|
||||
<IconButton
|
||||
className="w-5 h-5 p-0 justify-center sk-circle hover:bg-passive-4 color-neutral"
|
||||
icon={isToggled ? 'eye-off' : 'eye'}
|
||||
iconClassName="sn-icon--small"
|
||||
title="Show/hide password"
|
||||
onClick={() => setIsToggled((isToggled) => !isToggled)}
|
||||
focusable={true}
|
||||
/>
|
||||
)
|
||||
|
||||
/**
|
||||
* Password input that has a toggle to show/hide password and can be decorated on the left and right side
|
||||
*/
|
||||
const DecoratedPasswordInput = forwardRef((props: DecoratedInputProps, ref: Ref<HTMLInputElement>) => {
|
||||
const [isToggled, setIsToggled] = useState(false)
|
||||
|
||||
const rightSideDecorations = props.right ? [...props.right] : []
|
||||
|
||||
return (
|
||||
<DecoratedInput
|
||||
{...props}
|
||||
ref={ref}
|
||||
type={isToggled ? 'text' : 'password'}
|
||||
right={[...rightSideDecorations, <Toggle isToggled={isToggled} setIsToggled={setIsToggled} />]}
|
||||
/>
|
||||
)
|
||||
})
|
||||
|
||||
export default DecoratedPasswordInput
|
||||
@@ -0,0 +1,72 @@
|
||||
import { ChangeEventHandler, Ref, forwardRef, useState } from 'react'
|
||||
|
||||
type Props = {
|
||||
id: string
|
||||
type: 'text' | 'email' | 'password'
|
||||
label: string
|
||||
value: string
|
||||
onChange: ChangeEventHandler<HTMLInputElement>
|
||||
disabled?: boolean
|
||||
className?: string
|
||||
labelClassName?: string
|
||||
inputClassName?: string
|
||||
isInvalid?: boolean
|
||||
}
|
||||
|
||||
const FloatingLabelInput = forwardRef(
|
||||
(
|
||||
{
|
||||
id,
|
||||
type,
|
||||
label,
|
||||
disabled,
|
||||
value,
|
||||
isInvalid,
|
||||
onChange,
|
||||
className = '',
|
||||
labelClassName = '',
|
||||
inputClassName = '',
|
||||
}: Props,
|
||||
ref: Ref<HTMLInputElement>,
|
||||
) => {
|
||||
const [focused, setFocused] = useState(false)
|
||||
|
||||
const BASE_CLASSNAME = 'relative bg-default'
|
||||
|
||||
const LABEL_CLASSNAME = `hidden absolute ${!focused ? 'color-neutral' : 'color-info'} ${
|
||||
focused || value ? 'flex top-0 left-2 pt-1.5 px-1' : ''
|
||||
} ${isInvalid ? 'color-danger' : ''} ${labelClassName}`
|
||||
|
||||
const INPUT_CLASSNAME = `w-full h-full ${
|
||||
focused || value ? 'pt-6 pb-2' : 'py-2.5'
|
||||
} px-3 text-input border-1 border-solid border-main rounded placeholder-medium text-input focus:ring-info ${
|
||||
isInvalid ? 'border-danger placeholder-dark-red' : ''
|
||||
} ${inputClassName}`
|
||||
|
||||
const handleFocus = () => setFocused(true)
|
||||
|
||||
const handleBlur = () => setFocused(false)
|
||||
|
||||
return (
|
||||
<div className={`${BASE_CLASSNAME} ${className}`}>
|
||||
<label htmlFor={id} className={LABEL_CLASSNAME}>
|
||||
{label}
|
||||
</label>
|
||||
<input
|
||||
id={id}
|
||||
className={INPUT_CLASSNAME}
|
||||
placeholder={!focused ? label : ''}
|
||||
type={type}
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
onFocus={handleFocus}
|
||||
onBlur={handleBlur}
|
||||
ref={ref}
|
||||
disabled={disabled}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
},
|
||||
)
|
||||
|
||||
export default FloatingLabelInput
|
||||
16
packages/web/src/javascripts/Components/Input/Input.tsx
Normal file
16
packages/web/src/javascripts/Components/Input/Input.tsx
Normal file
@@ -0,0 +1,16 @@
|
||||
import { FunctionComponent } from 'react'
|
||||
|
||||
interface Props {
|
||||
text?: string
|
||||
disabled?: boolean
|
||||
className?: string
|
||||
}
|
||||
|
||||
const Input: FunctionComponent<Props> = ({ className = '', disabled = false, text }) => {
|
||||
const base = 'rounded py-1.5 px-3 text-input my-1 h-8 bg-contrast'
|
||||
const stateClasses = disabled ? 'no-border' : 'border-solid border-1 border-main'
|
||||
const classes = `${base} ${stateClasses} ${className}`
|
||||
return <input type="text" className={classes} disabled={disabled} value={text} />
|
||||
}
|
||||
|
||||
export default Input
|
||||
Reference in New Issue
Block a user