import React, { useId, ReactElement, InputHTMLAttributes, KeyboardEvent, ChangeEvent, useEffect, useContext, Ref, useRef, ReactNode, } from 'react'; import classnames from 'classnames'; import keycoder from 'keycoder'; import { Icon } from './Icon'; import { FormElement, FormElementProps } from './FormElement'; import { Text } from './Text'; import { FieldSetColumnContext } from './FieldSet'; import { registerStyle } from './util'; import { useEventCallback } from './hooks'; import { createFC } from './common'; /** * */ function useInitComponentStyle() { useEffect(() => { registerStyle('input-icons', [ // fix styles of double-iconed input [ '.slds-input-has-icon_left-right .react-slds-icon.slds-input__icon_right', '{ left: auto; }', ], ]); }, []); } /** * */ const InputAddon = ({ content }: { content: string }) => ( {content} ); /** * */ const InputIcon = ({ icon, align, }: { icon: string | ReactElement; align: 'left' | 'right'; }) => { return React.isValidElement(icon) ? ( icon ) : typeof icon === 'string' ? ( ) : ( <> ); }; /** * */ export type InputProps = { label?: string; required?: boolean; error?: FormElementProps['error']; cols?: number; value?: string; defaultValue?: string; placeholder?: string; bare?: boolean; symbolPattern?: string; readOnly?: boolean; htmlReadOnly?: boolean; iconLeft?: string | JSX.Element; iconRight?: string | JSX.Element; addonLeft?: string; addonRight?: string; tooltip?: ReactNode; tooltipIcon?: string; elementRef?: Ref; inputRef?: Ref; onValueChange?: (value: string, prevValue?: string) => void; } & Omit, 'value' | 'defaultValue'>; /** * */ export const Input = createFC( (props) => { const { id, className, label, required, error, readOnly, cols, type, bare, value, defaultValue, htmlReadOnly, iconLeft, iconRight, addonLeft, addonRight, symbolPattern, tooltip, tooltipIcon, elementRef, inputRef, onKeyDown: onKeyDown_, onChange: onChange_, onValueChange, ...rprops } = props; useInitComponentStyle(); const prevValueRef = useRef(); const onKeyDown = useEventCallback((e: KeyboardEvent) => { if (symbolPattern) { const { keyCode, shiftKey } = e; const value = keycoder.toCharacter(keyCode, shiftKey); if (value && !value.match(new RegExp(symbolPattern))) { e.preventDefault(); return; } } onKeyDown_?.(e); }); const onChange = useEventCallback((e: ChangeEvent) => { onChange_?.(e); onValueChange?.(e.target.value, prevValueRef.current); prevValueRef.current = e.target.value; }); const prefix = useId(); const rawTextId = id ?? `${prefix}-raw-text-id`; const inputId = id ?? `${prefix}-input-id`; const labelForId = readOnly ? rawTextId : inputId; const errorId = `${prefix}-error-id`; const { isFieldSetColumn } = useContext(FieldSetColumnContext); const inputClassNames = classnames( className, bare ? 'slds-input_bare' : 'slds-input' ); const inputElem = readOnly ? ( {value} ) : ( ); let contentElem = inputElem; if (iconLeft || iconRight || addonLeft || addonRight) { const wrapperClassName = classnames( 'slds-form-element__control', { 'slds-input-has-icon': iconLeft || iconRight }, { 'slds-input-has-icon_left-right': iconLeft && iconRight }, { 'slds-input-has-icon_left': iconLeft }, { 'slds-input-has-icon_right': iconRight }, { 'slds-input-has-fixed-addon': addonLeft || addonRight } ); contentElem = (
{addonLeft ? : undefined} {iconLeft ? : undefined} {inputElem} {iconRight ? : undefined} {addonRight ? : undefined}
); } if (isFieldSetColumn || label || required || error || cols) { const formElemProps = { controlId: labelForId, label, required, error, errorId, readOnly, cols, tooltip, tooltipIcon, elementRef, }; return {contentElem}; } return contentElem; }, { isFormElement: true } );