UNPKG

@primer/react

Version:

An implementation of GitHub's Primer Design System using React

152 lines (151 loc) • 7.22 kB
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 };