UNPKG

@engie-group/fluid-design-system-react

Version:

Fluid Design System React

188 lines (185 loc) 10.8 kB
import { jsx, jsxs } from 'react/jsx-runtime'; import React__default, { useState, useId, useEffect } from 'react'; import { Utils } from '../../utils/util.js'; import { NJIcon } from '../icon/NJIcon.js'; import { NJIconButton } from '../icon-button/NJIconButton.js'; const NOT_HTML_ATTR = [ 'value', 'label', 'labelKind', 'className', 'wrapperClick', 'passwordButtonLabelShow', 'passwordButtonLabelHide', 'passwordNoticeIsVisible', 'passwordNoticeIsHidden', 'type', 'isDisabled', 'hasError', 'isRequired', 'subscriptMessage', 'iconName', 'iconClick', 'iconKeyPress', 'iconTitle', 'onChange', 'isMultiline', 'children', 'isSelect', 'clearable', 'clearButtonAriaLabel', 'onClear', 'wrapperKeyPress', 'wrapperKeyDown', 'iconClassName', 'labelClassName', 'size' ]; const NJFormItem = React__default.forwardRef((props, ref) => { const { id, name, value, label, labelKind = 'floating', className, onClick, onKeyPress, wrapperClick, title, ariaLabel, ariaLabelledBy, passwordButtonLabelShow, passwordButtonLabelHide, passwordNoticeIsVisible, passwordNoticeIsHidden, type = 'text', isDisabled, hasError, hasSuccess, autocomplete, isRequired, placeholder = ' ', // Placeholder must be " " because of webkit browser behavior with floating labels subscriptMessage, iconName, iconClick, iconKeyPress, iconTitle, onChange, maxLength, minLength, readOnly, form, tabIndex, onFocus, onBlur, isMultiline, rows, cols, wrap, children, isSelect, clearable, clearButtonAriaLabel = 'Clear input', onClear, wrapperKeyPress, wrapperKeyDown, inputRef, iconClassName, labelClassName, size } = props; let inputField, additionalContent; const [inputValue, setInputValue] = useState(value || ''); const [isVisible, setIsVisible] = useState(false); const [passwordNotice, setPasswordNotice] = useState(''); const [passwordButtonLabel, setPasswordButtonLabel] = useState('Afficher le mot de passe'); const [finalType, setFinalType] = useState(type); const isPassword = type === 'password'; const currentId = id || useId(); React__default.Children.forEach(children, (child) => { if (!child) { return; } if (React__default.isValidElement(child)) { const typedChild = child; if ('data-child-name' in typedChild.props && typedChild.props['data-child-name'] === 'inputField') { inputField = typedChild; } if (('data-child-name' in typedChild.props && typedChild.props['data-child-name'] === 'additionalContent') || typedChild.key === 'additionalContent') { additionalContent = typedChild; } } }); const getActualInputValue = () => { if (inputField) { const field = inputField; return field.props.value || ''; } return inputValue || value || ''; }; const actualInputValue = getActualInputValue(); const onChangeValue = (e) => { setInputValue(e.currentTarget.value); if (onChange && typeof onChange === 'function') onChange(e); }; const onIconClick = (e) => { if (iconClick && typeof iconClick === 'function') iconClick(e); }; const onIconKeyPress = (e) => { e.preventDefault(); if (iconKeyPress && typeof iconKeyPress === 'function') iconKeyPress(e); }; const togglePasswordVisibility = () => { const typeToSet = isVisible ? 'password' : 'text'; setFinalType(typeToSet); setIsVisible(!isVisible); if (isVisible) { if (passwordNoticeIsHidden) setPasswordNotice(passwordNoticeIsHidden); if (passwordButtonLabelShow) setPasswordButtonLabel(passwordButtonLabelShow); } else { if (passwordNoticeIsVisible) setPasswordNotice(passwordNoticeIsVisible); if (passwordButtonLabelHide) setPasswordButtonLabel(passwordButtonLabelHide); } }; const onClearButtonClick = (e) => { e.preventDefault(); if (isDisabled || readOnly) return; setInputValue(''); if (onClear && typeof onClear === 'function') { onClear(); } if (inputRef && 'current' in inputRef && inputRef.current) { inputRef.current.focus({ preventScroll: true }); } }; const onClearButtonMouseDown = (e) => { e.preventDefault(); }; useEffect(() => { if (isPassword && (!passwordNoticeIsHidden || !passwordNoticeIsVisible || !passwordButtonLabelShow || !passwordButtonLabelHide)) { console.warn('Some of these required props are missing: [passwordButtonLabelShow], [passwordButtonLabelHide], [passwordNoticeIsVisible], [passwordNoticeIsHidden]'); } }, []); useEffect(() => { setInputValue(value || ''); }, [value]); const formItemClass = Utils.classNames('nj-form-item', { ['nj-form-item--static']: labelKind === 'static', [`nj-form-item--${size}`]: size && size !== 'md', ['nj-form-item--disabled']: isDisabled, ['nj-form-item--error']: hasError, ['nj-form-item--success']: hasSuccess, ['nj-form-item--textarea']: isMultiline, ['nj-form-item--password']: type === 'password', ['nj-form-item--visible']: type === 'password' && isVisible, ['nj-form-item--select']: isSelect }, className); const getFieldWithAttributes = () => { if (!inputField) { return; } const className = Utils.classNames(inputField.props.className, 'nj-form-item__field'); // Apply class, id and needed aria-* attributes return React__default.cloneElement(inputField, { className, id: currentId, ...(subscriptMessage ? { 'aria-describedby': `${currentId}-subscript` } : {}), ...(hasError ? { 'aria-invalid': 'true' } : {}) }); }; function isCustomSelect() { if (!inputField) { return; } return isSelect && inputField.props.type === 'text'; } let field; if (!isTextarea(props)) { const otherProps = Utils.omit(props, ...NOT_HTML_ATTR); field = (jsx("input", { ...otherProps, size: undefined, type: finalType, id: currentId, name: name, value: inputValue ?? '', onClick: onClick, onChange: onChangeValue, onBlur: onBlur, onFocus: onFocus, title: title, "aria-label": otherProps['aria-label'] ?? ariaLabel, "aria-labelledby": otherProps['aria-labelledby'] ?? ariaLabelledBy, "aria-invalid": otherProps['aria-invalid'] ?? (hasError ? 'true' : undefined), "aria-describedby": otherProps['aria-describedby'] ?? (subscriptMessage ? `${currentId}-subscript` : undefined), disabled: otherProps.disabled ?? isDisabled, autoComplete: otherProps.autoComplete ?? autocomplete, required: otherProps.required ?? isRequired, placeholder: placeholder, className: "nj-form-item__field", maxLength: maxLength, minLength: minLength, readOnly: readOnly, form: form, tabIndex: tabIndex, onKeyPress: onKeyPress, ref: inputRef })); } else { const otherProps = Utils.omit(props, ...NOT_HTML_ATTR); field = (jsx("textarea", { ...otherProps, id: currentId, name: name, value: inputValue ?? '', rows: rows, cols: cols, wrap: wrap, onClick: onClick, onChange: onChangeValue, onBlur: onBlur, onFocus: onFocus, title: title, "aria-label": otherProps['aria-label'] ?? ariaLabel, "aria-labelledby": otherProps['aria-labelledby'] ?? ariaLabelledBy, "aria-invalid": otherProps['aria-invalid'] ?? (hasError ? 'true' : undefined), "aria-describedby": otherProps['aria-describedby'] ?? (subscriptMessage ? `${currentId}-subscript` : undefined), disabled: otherProps.disabled ?? isDisabled, autoComplete: otherProps.autoComplete ?? autocomplete, required: otherProps.required ?? isRequired, placeholder: placeholder, className: "nj-form-item__field", maxLength: maxLength, minLength: minLength, readOnly: readOnly, form: form, tabIndex: tabIndex, onKeyPress: onKeyPress, ref: inputRef })); } const ClearButton = (jsx(NJIconButton, { icon: "cancel", scale: "sm", variant: "tertiary", "aria-label": clearButtonAriaLabel, onClick: onClearButtonClick, onMouseDown: onClearButtonMouseDown, className: "nj-form-item__clear-button" })); const showClearButton = clearable && actualInputValue && !isDisabled && !readOnly; const hasIcon = !isMultiline && iconName; const hasTrailing = isPassword || showClearButton || hasIcon; return (jsxs("div", { className: formItemClass, ref: ref, children: [jsxs("div", { className: "nj-form-item__field-wrapper", role: wrapperClick ? 'button' : undefined, onClick: wrapperClick, onKeyPress: wrapperKeyPress, onKeyDown: wrapperKeyDown, children: [inputField ? getFieldWithAttributes() : field, jsxs("label", { htmlFor: currentId, className: Utils.classNames('nj-form-item__label', labelClassName), "aria-hidden": isCustomSelect() ? 'true' : undefined, children: [label, isRequired && jsx("span", { className: "nj-form-item__required-asterisk", children: "*" })] }), hasTrailing && (jsxs("div", { className: "nj-form-item__trailing", children: [showClearButton && ClearButton, isPassword && (jsx(NJIconButton, { "aria-label": passwordButtonLabel, scale: "sm", icon: iconName || 'visibility', "aria-pressed": isVisible, variant: "tertiary", className: "nj-form-item__password-button", onClick: togglePasswordVisibility })), !isPassword && iconName && (jsx(NJIcon, { name: iconName, onClick: onIconClick, onKeyDown: onIconKeyPress, "aria-label": iconTitle, className: Utils.classNames('nj-form-item__icon', iconClassName, { 'nj-form-item__icon--clickable': isSelect }), role: "button", tabIndex: -1 }))] })), additionalContent ? additionalContent : null, isPassword && (jsx("p", { className: "nj-sr-only", "aria-live": "polite", "aria-atomic": "true", children: passwordNotice }))] }), subscriptMessage && (jsxs("p", { className: "nj-form-item__subscript", id: `${currentId}-subscript`, children: [hasError && (jsx(NJIcon, { className: "nj-form-item__subscript-icon", name: "warning", variant: "red" })), hasSuccess && !hasError && (jsx(NJIcon, { className: "nj-form-item__subscript-icon", name: "check", variant: "green" })), subscriptMessage] }))] })); }); NJFormItem.displayName = 'NJFormItem'; const isTextarea = (props) => { return !!props.isMultiline; }; export { NJFormItem };