@intility/bifrost-react
Version:
React library for Intility's design system, Bifrost.
158 lines (157 loc) • 6.85 kB
JavaScript
"use client";
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
import { useRef, forwardRef, useEffect, useState } from "react";
import { faTriangleExclamation } from "@fortawesome/free-solid-svg-icons/faTriangleExclamation";
import { faCheck } from "@fortawesome/free-solid-svg-icons/faCheck";
import { faXmark } from "@fortawesome/free-solid-svg-icons/faXmark";
import classNames from "classnames";
import Icon from "../Icon/Icon.js";
import setRef from "../../utils/setRef.js";
import triggerOnChange from "../../utils/triggerOnChange.js";
import bfSpinner from "../../assets/bfSpinner.js";
import useUniqueId from "../../hooks/useUniqueId.js";
import Label from "../Label/Label.js";
import Description from "../Description/Description.js";
import Feedback from "../Feedback/Feedback.js";
/**
* Text input field
*/ const Input = /*#__PURE__*/ forwardRef(({ label, hideLabel = false, icon, iconProps, rightIcon = false, className, style, id, disabled = false, readOnly = false, state = "default", feedback, required = false, description, requiredNoLabel = false, optional = false, clearable = false, loading = false, onIconClick, iconButton, inputClassName, inputStyle, small = false, ...props }, ref)=>{
const inputId = useUniqueId(id);
let hasState = !!state && state !== "default";
const iconProp = icon || iconProps?.icon;
const inputRef = useRef(null);
const handleClear = ()=>{
if (disabled) return;
if (inputRef.current) {
triggerOnChange(inputRef.current, "");
inputRef.current.focus();
}
};
const isControlled = "value" in props;
const [uncontrolledHasClearableValue, setUncontrolledHasClearableValue] = useState(!!props.defaultValue);
// detect uncontrolled value for clearable inputs
useEffect(()=>{
const inputElement = inputRef.current;
// only detect if clearable and uncontrolled
if (!inputElement || !clearable || isControlled) return;
const uncontrolledInputHandler = ()=>{
setUncontrolledHasClearableValue(!!inputElement.value);
};
inputElement.addEventListener("input", uncontrolledInputHandler);
return ()=>{
inputElement.removeEventListener("input", uncontrolledInputHandler);
};
}, [
isControlled,
clearable
]);
const showClearButton = clearable && (isControlled ? !!props.value : uncontrolledHasClearableValue);
let inputIcon = null;
if (iconProp && !(hasState && rightIcon)) {
inputIcon = /*#__PURE__*/ _jsx(Icon, {
icon: loading ? bfSpinner : iconProp,
spin: loading,
"data-testid": "bf-input-icon",
...iconProps,
className: classNames(iconProps?.className, {
"bf-input-loading-icon": loading
})
});
inputIcon = onIconClick ? /*#__PURE__*/ _jsx("button", {
type: "button",
className: "bf-input-icon bf-input-icon-button",
onClick: onIconClick,
disabled: disabled,
children: inputIcon
}) : /*#__PURE__*/ _jsx("span", {
className: classNames(iconProps?.className, "bf-input-icon"),
children: inputIcon
});
} else if (loading) {
inputIcon = /*#__PURE__*/ _jsx("span", {
className: "bf-input-icon",
children: /*#__PURE__*/ _jsx(Icon, {
icon: bfSpinner,
spin: true,
className: "bf-input-loading-icon"
})
});
rightIcon = true;
hasState = false;
}
return /*#__PURE__*/ _jsxs("div", {
className: classNames(className, "bf-input-container", {
"bf-input-disabled": disabled,
"bf-input-success": state === "success",
"bf-input-warning": state === "warning",
"bf-input-alert": state === "alert",
"bf-input-small": small
}),
style: style,
"data-testid": "bf-input-container",
children: [
!hideLabel && /*#__PURE__*/ _jsx(Label, {
htmlFor: inputId,
required: !disabled && required && !requiredNoLabel,
optional: optional,
disabled: disabled,
readOnly: readOnly,
children: label
}),
/*#__PURE__*/ _jsx(Description, {
children: description
}),
/*#__PURE__*/ _jsxs("div", {
className: classNames("bf-input-icon-container", {
"bf-input-icon-right": !hasState && inputIcon && rightIcon,
"bf-input-icon-left": inputIcon && !rightIcon,
"bf-input-icon-state": hasState,
"bf-input-clearable": showClearButton,
"bf-input-icon-button-filled": iconButton
}),
"data-testid": "bf-input-icon-container",
children: [
/*#__PURE__*/ _jsx("input", {
id: inputId,
disabled: disabled,
readOnly: readOnly,
required: !disabled && (required || requiredNoLabel),
...hideLabel && typeof label === "string" && {
"aria-label": label
},
autoComplete: "off",
...props,
style: inputStyle,
ref: (r)=>{
setRef(ref, r);
setRef(inputRef, r);
},
className: classNames("bf-input", inputClassName, {
"bf-input-state-only": hasState && !inputIcon
})
}),
inputIcon,
showClearButton && /*#__PURE__*/ _jsx("span", {
className: "bf-input-clear-icon",
onClick: ()=>handleClear(),
"data-testid": "bf-input-clear-icon",
children: /*#__PURE__*/ _jsx(Icon, {
icon: faXmark
})
}),
hasState && /*#__PURE__*/ _jsx("span", {
className: "bf-state-icon",
children: /*#__PURE__*/ _jsx(Icon, {
icon: state === "alert" || state === "warning" ? faTriangleExclamation : faCheck
})
})
]
}),
/*#__PURE__*/ _jsx(Feedback, {
children: feedback
})
]
});
});
Input.displayName = "Input";
export default Input;