@engie-group/fluid-design-system-react
Version:
Fluid Design System React
188 lines (185 loc) • 10.8 kB
JavaScript
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 };