@carbon/react
Version:
React components for the Carbon Design System
195 lines (193 loc) • 8.25 kB
JavaScript
/**
* Copyright IBM Corp. 2016, 2026
*
* This source code is licensed under the Apache-2.0 license found in the
* LICENSE file in the root directory of this source tree.
*/
const require_runtime = require("../../_virtual/_rolldown/runtime.js");
const require_usePrefix = require("../../internal/usePrefix.js");
const require_keys = require("../../internal/keyboard/keys.js");
const require_match = require("../../internal/keyboard/match.js");
const require_useId = require("../../internal/useId.js");
const require_noopFn = require("../../internal/noopFn.js");
const require_deprecate = require("../../prop-types/deprecate.js");
const require_useMergedRefs = require("../../internal/useMergedRefs.js");
const require_Tooltip = require("../Tooltip/Tooltip.js");
const require_events = require("../../tools/events.js");
const require_FormContext = require("../FluidForm/FormContext.js");
const require_utils = require("./utils.js");
let classnames = require("classnames");
classnames = require_runtime.__toESM(classnames);
let react = require("react");
react = require_runtime.__toESM(react);
let prop_types = require("prop-types");
prop_types = require_runtime.__toESM(prop_types);
let react_jsx_runtime = require("react/jsx-runtime");
let _carbon_icons_react = require("@carbon/icons-react");
//#region src/components/Search/Search.tsx
/**
* Copyright IBM Corp. 2016, 2026
*
* This source code is licensed under the Apache-2.0 license found in the
* LICENSE file in the root directory of this source tree.
*/
const Search = react.default.forwardRef(({ autoComplete = "off", className, closeButtonLabelText = "Clear search input", defaultValue, disabled, isExpanded = true, id, labelText, light, onChange = () => {}, onClear = () => {}, onKeyDown, onExpand, placeholder = "Search", renderIcon, role, size = "md", type = "search", value, ...rest }, forwardRef) => {
const hasPropValue = require_utils.isSearchValuePresent(value) || require_utils.isSearchValuePresent(defaultValue);
const prefix = require_usePrefix.usePrefix();
const { isFluid } = (0, react.useContext)(require_FormContext.FormContext);
const inputRef = (0, react.useRef)(null);
const ref = require_useMergedRefs.useMergedRefs([forwardRef, inputRef]);
const expandButtonRef = (0, react.useRef)(null);
const inputId = require_useId.useId("search-input");
const uniqueId = id || inputId;
const searchId = `${uniqueId}-search`;
const [hasContent, setHasContent] = (0, react.useState)(hasPropValue || false);
const searchClasses = (0, classnames.default)({
[`${prefix}--search`]: true,
[`${prefix}--search--sm`]: size === "sm",
[`${prefix}--search--md`]: size === "md",
[`${prefix}--search--lg`]: size === "lg",
[`${prefix}--search--light`]: light,
[`${prefix}--search--disabled`]: disabled,
[`${prefix}--search--fluid`]: isFluid
}, className);
const clearClasses = (0, classnames.default)({
[`${prefix}--search-close`]: true,
[`${prefix}--search-close--hidden`]: !hasContent || !isExpanded
});
(0, react.useEffect)(() => {
if (typeof value !== "undefined") setHasContent(require_utils.isSearchValuePresent(value));
}, [value]);
function clearInput() {
if (!value && inputRef.current) inputRef.current.value = "";
if (inputRef.current) {
const inputTarget = Object.assign({}, inputRef.current, { value: "" });
onChange({
bubbles: false,
cancelable: false,
currentTarget: inputRef.current,
defaultPrevented: false,
eventPhase: 0,
isDefaultPrevented: () => false,
isPropagationStopped: () => false,
isTrusted: false,
nativeEvent: new Event("change"),
persist: require_noopFn.noopFn,
preventDefault: require_noopFn.noopFn,
stopPropagation: require_noopFn.noopFn,
target: inputTarget,
timeStamp: 0,
type: "change"
});
}
onClear();
setHasContent(false);
inputRef.current?.focus();
}
function handleChange(event) {
setHasContent(event.target.value !== "");
}
function handleKeyDown(event) {
if (require_match.match(event, require_keys.Escape)) {
event.stopPropagation();
if (inputRef.current?.value) clearInput();
else if (onExpand && isExpanded) expandButtonRef.current?.focus();
}
}
function handleExpandButtonKeyDown(event) {
if (require_match.match(event, require_keys.Enter) || require_match.match(event, require_keys.Space)) {
event.stopPropagation();
if (onExpand) onExpand(event);
}
}
const magnifierButton = /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
"aria-labelledby": onExpand ? searchId : void 0,
role: onExpand ? "button" : void 0,
className: `${prefix}--search-magnifier`,
onClick: onExpand,
onKeyDown: handleExpandButtonKeyDown,
tabIndex: onExpand && !isExpanded ? 0 : -1,
ref: expandButtonRef,
"aria-expanded": onExpand && isExpanded ? true : onExpand && !isExpanded ? false : void 0,
"aria-controls": onExpand ? uniqueId : void 0,
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CustomSearchIcon, { icon: renderIcon })
});
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
role: "search",
"aria-label": placeholder,
className: searchClasses,
children: [
onExpand && !isExpanded ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_Tooltip.Tooltip, {
className: `${prefix}--search-tooltip ${prefix}--search-magnifier-tooltip ${prefix}--icon-tooltip`,
align: "top",
label: "Search",
children: magnifierButton
}) : magnifierButton,
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
id: searchId,
htmlFor: uniqueId,
className: `${prefix}--label`,
children: labelText
}),
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
autoComplete,
className: `${prefix}--search-input`,
defaultValue,
disabled,
role,
ref,
id: uniqueId,
onChange: require_events.composeEventHandlers([onChange, handleChange]),
onKeyDown: require_events.composeEventHandlers([onKeyDown, handleKeyDown]),
placeholder,
type,
value,
tabIndex: onExpand && !isExpanded ? -1 : void 0,
...rest
}),
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
"aria-label": closeButtonLabelText,
className: clearClasses,
disabled,
onClick: clearInput,
title: closeButtonLabelText,
type: "button",
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_carbon_icons_react.Close, {})
})
]
});
});
Search.displayName = "Search";
Search.propTypes = {
autoComplete: prop_types.default.string,
className: prop_types.default.string,
closeButtonLabelText: prop_types.default.string,
defaultValue: prop_types.default.oneOfType([prop_types.default.string, prop_types.default.number]),
disabled: prop_types.default.bool,
id: prop_types.default.string,
isExpanded: prop_types.default.bool,
labelText: prop_types.default.node.isRequired,
light: require_deprecate.deprecate(prop_types.default.bool, "The `light` prop for `Search` is no longer needed and has been deprecated in v11 in favor of the new `Layer` component. It will be moved in the next major release."),
onChange: prop_types.default.func,
onClear: prop_types.default.func,
onExpand: prop_types.default.func,
onKeyDown: prop_types.default.func,
placeholder: prop_types.default.string,
renderIcon: prop_types.default.oneOfType([prop_types.default.func, prop_types.default.object]),
role: require_deprecate.deprecate(prop_types.default.string, "The `role` prop has been deprecated since <input type=\"search\"> already provides correct semantics. It will be removed in the next major release of Carbon."),
size: prop_types.default.oneOf([
"sm",
"md",
"lg"
]),
type: prop_types.default.string,
value: prop_types.default.oneOfType([prop_types.default.string, prop_types.default.number])
};
function CustomSearchIcon({ icon: Icon }) {
const prefix = require_usePrefix.usePrefix();
if (Icon) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Icon, { className: `${prefix}--search-magnifier-icon` });
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_carbon_icons_react.Search, { className: `${prefix}--search-magnifier-icon` });
}
CustomSearchIcon.propTypes = { icon: prop_types.default.oneOfType([prop_types.default.func, prop_types.default.object]) };
//#endregion
exports.default = Search;