@primer/react
Version:
An implementation of GitHub's Primer Design System using React
152 lines (151 loc) • 7.22 kB
JavaScript
import { useProvidedRefOrCreate } from "../hooks/useProvidedRefOrCreate.js";
import { AriaStatus } from "../live-region/AriaStatus.js";
import Text_default from "../Text/Text.js";
import _VisuallyHidden_module_css_default from "../_VisuallyHidden.module.css.js";
import VisuallyHidden from "../_VisuallyHidden.js";
import TextInput_module_css_default from "./TextInput.module.css.js";
import TextInputInnerVisualSlot from "../internal/components/TextInputInnerVisualSlot.js";
import { TextInputWrapper } from "../internal/components/TextInputWrapper.js";
import TextInputAction from "../internal/components/TextInputInnerAction.js";
import UnstyledTextInput from "../internal/components/UnstyledTextInput.js";
import { getCharacterCountState } from "../utils/character-counter.js";
import { clsx } from "clsx";
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
import React, { useCallback, useId, useState } from "react";
import { isValidElementType } from "react-is";
import { AlertFillIcon } from "@primer/octicons-react";
//#region src/TextInput/TextInput.tsx
const TextInput = /*#__PURE__*/ React.forwardRef(({ icon: IconComponent, leadingVisual: LeadingVisual, trailingVisual: TrailingVisual, trailingAction, block, className, contrast, disabled, loading, loaderPosition = "auto", loaderText = "Loading", monospace, validationStatus, size: sizeProp, onFocus, onBlur, variant: variantProp, width: widthProp, minWidth: minWidthProp, maxWidth: maxWidthProp, type = "text", required, characterLimit, onChange, value, defaultValue, "data-component": dataComponent, ...inputProps }, ref) => {
var _counter$isOverLimit;
const [isInputFocused, setIsInputFocused] = useState(false);
const inputRef = useProvidedRefOrCreate(ref);
const isControlled = value !== void 0;
const [uncontrolledLength, setUncontrolledLength] = useState(() => defaultValue !== void 0 ? String(defaultValue).length : 0);
const currentLength = isControlled ? String(value).length : uncontrolledLength;
const counter = characterLimit ? getCharacterCountState(currentLength, characterLimit) : void 0;
const isOverLimit = (_counter$isOverLimit = counter === null || counter === void 0 ? void 0 : counter.isOverLimit) !== null && _counter$isOverLimit !== void 0 ? _counter$isOverLimit : false;
const wrapperClasses = clsx(className, "TextInput-wrapper");
const showLeadingLoadingIndicator = loading && (loaderPosition === "leading" || Boolean(LeadingVisual && loaderPosition !== "trailing"));
const showTrailingLoadingIndicator = loading && (loaderPosition === "trailing" || Boolean(loaderPosition === "auto" && !LeadingVisual));
const isSegmentedInputType = type === "date" || type === "time" || type === "datetime-local";
const focusInput = (e) => {
if (e.target !== inputRef.current || !isSegmentedInputType) {
var _inputRef$current;
(_inputRef$current = inputRef.current) === null || _inputRef$current === void 0 || _inputRef$current.focus();
}
};
const leadingVisualId = useId();
const trailingVisualId = useId();
const loadingId = useId();
const inputDescribedBy = clsx(inputProps["aria-describedby"], LeadingVisual && leadingVisualId, TrailingVisual && trailingVisualId, loading && loadingId) || void 0;
const handleInputFocus = useCallback((e_0) => {
setIsInputFocused(true);
onFocus && onFocus(e_0);
}, [onFocus]);
const handleInputBlur = useCallback((e_1) => {
setIsInputFocused(false);
onBlur && onBlur(e_1);
}, [onBlur]);
const handleInputChange = useCallback((e_2) => {
if (characterLimit && !isControlled) setUncontrolledLength(e_2.target.value.length);
onChange === null || onChange === void 0 || onChange(e_2);
}, [
onChange,
characterLimit,
isControlled
]);
const characterCountId = useId();
const characterCountStaticMessageId = useId();
const isValid = isOverLimit ? "error" : validationStatus;
return /*#__PURE__*/ jsxs(Fragment, { children: [/*#__PURE__*/ jsxs(TextInputWrapper, {
block,
className: wrapperClasses,
validationStatus: isValid,
contrast,
disabled,
monospace,
size: sizeProp,
width: widthProp,
minWidth: minWidthProp,
maxWidth: maxWidthProp,
variant: variantProp,
hasLeadingVisual: Boolean(LeadingVisual || showLeadingLoadingIndicator),
hasTrailingVisual: Boolean(TrailingVisual || showTrailingLoadingIndicator),
hasTrailingAction: Boolean(trailingAction),
isInputFocused,
onClick: focusInput,
"aria-busy": Boolean(loading),
children: [
IconComponent && /*#__PURE__*/ jsx(IconComponent, {
className: "TextInput-icon",
"data-component": "TextInput.Icon"
}),
/*#__PURE__*/ jsx(TextInputInnerVisualSlot, {
visualPosition: "leading",
showLoadingIndicator: showLeadingLoadingIndicator,
hasLoadingIndicator: typeof loading === "boolean",
id: leadingVisualId,
children: typeof LeadingVisual !== "string" && isValidElementType(LeadingVisual) ? /*#__PURE__*/ jsx(LeadingVisual, {}) : LeadingVisual
}),
/*#__PURE__*/ jsx(UnstyledTextInput, {
ref: inputRef,
disabled,
onFocus: handleInputFocus,
onBlur: handleInputBlur,
onChange: handleInputChange,
type,
"aria-required": required,
"aria-invalid": isValid === "error" ? "true" : void 0,
value,
defaultValue,
...inputProps,
"aria-describedby": characterLimit ? [characterCountStaticMessageId, inputDescribedBy].filter(Boolean).join(" ") || void 0 : inputDescribedBy,
"data-component": dataComponent !== null && dataComponent !== void 0 ? dataComponent : "input"
}),
loading && /*#__PURE__*/ jsx(VisuallyHidden, {
id: loadingId,
children: loaderText
}),
/*#__PURE__*/ jsx(TextInputInnerVisualSlot, {
visualPosition: "trailing",
showLoadingIndicator: showTrailingLoadingIndicator,
hasLoadingIndicator: typeof loading === "boolean",
id: trailingVisualId,
"data-testid": "text-input-trailing-visual",
children: typeof TrailingVisual !== "string" && isValidElementType(TrailingVisual) ? /*#__PURE__*/ jsx(TrailingVisual, {}) : TrailingVisual
}),
trailingAction
]
}), characterLimit && /*#__PURE__*/ jsxs(Fragment, { children: [
/*#__PURE__*/ jsx(AriaStatus, {
announceOnShow: false,
delayMs: 500,
className: _VisuallyHidden_module_css_default.InternalVisuallyHidden,
children: counter === null || counter === void 0 ? void 0 : counter.message
}),
/*#__PURE__*/ jsxs(VisuallyHidden, {
id: characterCountStaticMessageId,
children: [
"You can enter up to ",
characterLimit,
" ",
characterLimit === 1 ? "character" : "characters"
]
}),
/*#__PURE__*/ jsxs(Text_default, {
"aria-hidden": "true",
id: characterCountId,
size: "small",
className: clsx(TextInput_module_css_default.CharacterCounter, isOverLimit && TextInput_module_css_default["CharacterCounter--error"]),
"data-component": "TextInput.CharacterCounter",
children: [isOverLimit && /*#__PURE__*/ jsx(AlertFillIcon, { size: 16 }), counter === null || counter === void 0 ? void 0 : counter.message]
})
] })] });
});
TextInput.displayName = "TextInput";
var TextInput_default = Object.assign(TextInput, {
__SLOT__: Symbol("TextInput"),
Action: TextInputAction
});
//#endregion
export { TextInput_default as default };