diff --git a/libs/ui-toolkit/src/components/input/input.stories.tsx b/libs/ui-toolkit/src/components/input/input.stories.tsx index 85fd255e8..4a6057d34 100644 --- a/libs/ui-toolkit/src/components/input/input.stories.tsx +++ b/libs/ui-toolkit/src/components/input/input.stories.tsx @@ -7,6 +7,18 @@ export default { const Template: Story = (args) => ; +const customElementPlaceholder = ( + + Ω + +); + export const Default = Template.bind({}); Default.args = {}; @@ -37,3 +49,11 @@ export const IconPrepend: Story = () => ( export const IconAppend: Story = () => ( ); + +export const ElementPrepend: Story = () => ( + +); + +export const ElementAppend: Story = () => ( + +); diff --git a/libs/ui-toolkit/src/components/input/input.tsx b/libs/ui-toolkit/src/components/input/input.tsx index ebacc005e..8875cbc3a 100644 --- a/libs/ui-toolkit/src/components/input/input.tsx +++ b/libs/ui-toolkit/src/components/input/input.tsx @@ -1,4 +1,4 @@ -import type { InputHTMLAttributes } from 'react'; +import type { InputHTMLAttributes, ReactNode } from 'react'; import { forwardRef } from 'react'; import classNames from 'classnames'; import type { IconName } from '../icon'; @@ -8,13 +8,58 @@ import { includesRightPadding, } from '../../utils/class-names'; -interface InputProps extends InputHTMLAttributes { +type InputRootProps = InputHTMLAttributes & { hasError?: boolean; disabled?: boolean; className?: string; - prependIconName?: IconName; - appendIconName?: IconName; -} +}; + +type NoPrepend = { + prependIconName?: never; + prependIconDescription?: string; + prependElement?: never; +}; + +type NoAppend = { + appendIconName?: never; + appendIconDescription?: string; + appendElement?: never; +}; + +type InputPrepend = NoAppend & + ( + | NoPrepend + | { + prependIconName: IconName; + prependIconDescription?: string; + prependElement?: never; + } + | { + prependIconName?: never; + prependIconDescription?: never; + prependElement: ReactNode; + } + ); + +type InputAppend = NoPrepend & + ( + | NoAppend + | { + appendIconName: IconName; + appendIconDescription?: string; + appendElement?: never; + } + | { + appendIconName?: never; + appendIconDescription?: never; + appendElement: ReactNode; + } + ); + +type AffixProps = InputPrepend | InputAppend; + +type InputProps = InputRootProps & AffixProps; + export const inputClassNames = ({ hasError, className, @@ -61,41 +106,98 @@ export const inputStyle = ({ } : style; +const getAffixElement = ({ + prependElement, + prependIconName, + prependIconDescription, + appendElement, + appendIconName, + appendIconDescription, +}: Pick) => { + const position = prependIconName || prependElement ? 'pre' : 'post'; + + const className = classNames( + ['fill-black-60 dark:fill-white-60', 'absolute', 'z-10'], + { + 'left-8': position === 'pre', + 'right-8': position === 'post', + } + ); + + const element = prependElement || appendElement; + const iconName = prependIconName || appendIconName; + const iconDescription = prependIconDescription || appendIconDescription; + + if (element) { + return
{element}
; + } + + if (iconName) { + return ( + + ); + } + + return null; +}; + export const Input = forwardRef( - ({ prependIconName, appendIconName, className, hasError, ...props }, ref) => { - className = `h-28 ${className}`; - if (prependIconName) { - className += ' pl-28'; - } - if (appendIconName) { - className += ' pr-28'; - } + ( + { + prependIconName, + prependIconDescription, + appendIconName, + appendIconDescription, + prependElement, + appendElement, + className, + hasError, + ...props + }, + ref + ) => { + const hasPrepended = !!(prependIconName || prependElement); + const hasAppended = !!(appendIconName || appendElement); + + const inputClassName = classNames('h-28', className, { + 'pl-28': hasPrepended ?? hasAppended, + }); const input = ( ); - const iconName = prependIconName || appendIconName; - if (iconName !== undefined) { - const iconClassName = classNames( - ['fill-black-60 dark:fill-white-60', 'absolute', 'z-10'], - { - 'left-8': prependIconName, - 'right-8': appendIconName, - } - ); - const icon = ; + + const element = getAffixElement({ + prependIconName, + prependIconDescription, + appendIconName, + appendIconDescription, + prependElement, + appendElement, + }); + + if (element) { return (
- {prependIconName && icon} + {hasPrepended && element} {input} - {appendIconName && icon} + {hasAppended && element}
); } + return input; } );