UNPKG

@patternfly/react-core

Version:

This library provides a set of common React components for use with the PatternFly reference implementation.

174 lines • 14 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.SearchInput = void 0; const tslib_1 = require("tslib"); const jsx_runtime_1 = require("react/jsx-runtime"); const react_1 = require("react"); const react_styles_1 = require("@patternfly/react-styles"); const Button_1 = require("../Button"); const Badge_1 = require("../Badge"); const Icon_1 = require("../Icon"); const angle_down_icon_1 = tslib_1.__importDefault(require('@patternfly/react-icons/dist/js/icons/angle-down-icon')); const angle_up_icon_1 = tslib_1.__importDefault(require('@patternfly/react-icons/dist/js/icons/angle-up-icon')); const times_icon_1 = tslib_1.__importDefault(require('@patternfly/react-icons/dist/js/icons/times-icon')); const search_icon_1 = tslib_1.__importDefault(require('@patternfly/react-icons/dist/js/icons/search-icon')); const caret_down_icon_1 = tslib_1.__importDefault(require('@patternfly/react-icons/dist/js/icons/caret-down-icon')); const arrow_right_icon_1 = tslib_1.__importDefault(require('@patternfly/react-icons/dist/js/icons/arrow-right-icon')); const AdvancedSearchMenu_1 = require("./AdvancedSearchMenu"); const TextInputGroup_1 = require("../TextInputGroup"); const InputGroup_1 = require("../InputGroup"); const helpers_1 = require("../../helpers"); const text_input_group_1 = tslib_1.__importDefault(require("@patternfly/react-styles/css/components/TextInputGroup/text-input-group")); const SearchInputBase = (_a) => { var { className, searchInputId, value = '', attributes = [], formAdditionalItems, hasWordsAttrLabel = 'Has words', advancedSearchDelimiter, placeholder, hint, onChange, onSearch, onClear, onToggleAdvancedSearch, isAdvancedSearchOpen = false, resultsCount, onNextClick, onPreviousClick, innerRef, expandableInput, 'aria-label': ariaLabel = 'Search input', resetButtonLabel = 'Reset', openMenuButtonAriaLabel = 'Open advanced search', previousNavigationButtonAriaLabel = 'Previous', isPreviousNavigationButtonDisabled = false, isNextNavigationButtonDisabled = false, nextNavigationButtonAriaLabel = 'Next', submitSearchButtonLabel = 'Search', isDisabled = false, appendTo, zIndex = 9999, name, areUtilitiesDisplayed, inputProps } = _a, props = tslib_1.__rest(_a, ["className", "searchInputId", "value", "attributes", "formAdditionalItems", "hasWordsAttrLabel", "advancedSearchDelimiter", "placeholder", "hint", "onChange", "onSearch", "onClear", "onToggleAdvancedSearch", "isAdvancedSearchOpen", "resultsCount", "onNextClick", "onPreviousClick", "innerRef", "expandableInput", 'aria-label', "resetButtonLabel", "openMenuButtonAriaLabel", "previousNavigationButtonAriaLabel", "isPreviousNavigationButtonDisabled", "isNextNavigationButtonDisabled", "nextNavigationButtonAriaLabel", "submitSearchButtonLabel", "isDisabled", "appendTo", "zIndex", "name", "areUtilitiesDisplayed", "inputProps"]); const [isSearchMenuOpen, setIsSearchMenuOpen] = (0, react_1.useState)(false); const [searchValue, setSearchValue] = (0, react_1.useState)(value); const searchInputRef = (0, react_1.useRef)(null); const ref = (0, react_1.useRef)(null); const searchInputInputRef = innerRef || ref; const searchInputExpandableToggleRef = (0, react_1.useRef)(null); const triggerRef = (0, react_1.useRef)(null); const popperRef = (0, react_1.useRef)(null); const [focusAfterExpandChange, setFocusAfterExpandChange] = (0, react_1.useState)(false); const { isExpanded, onToggleExpand, toggleAriaLabel } = expandableInput || {}; (0, react_1.useEffect)(() => { var _a, _b; // this effect and the focusAfterExpandChange variable are needed to focus the input/toggle as needed when the // expansion toggle is fired without focusing on mount if (!focusAfterExpandChange) { return; } else if (isExpanded) { (_a = searchInputInputRef === null || searchInputInputRef === void 0 ? void 0 : searchInputInputRef.current) === null || _a === void 0 ? void 0 : _a.focus(); } else { (_b = searchInputExpandableToggleRef === null || searchInputExpandableToggleRef === void 0 ? void 0 : searchInputExpandableToggleRef.current) === null || _b === void 0 ? void 0 : _b.focus(); } setFocusAfterExpandChange(false); }, [focusAfterExpandChange, isExpanded, searchInputInputRef, searchInputExpandableToggleRef]); (0, react_1.useEffect)(() => { setSearchValue(value); }, [value]); (0, react_1.useEffect)(() => { if (attributes.length > 0 && !advancedSearchDelimiter) { // eslint-disable-next-line no-console console.error('An advancedSearchDelimiter prop is required when advanced search attributes are provided using the attributes prop'); } }); (0, react_1.useEffect)(() => { setIsSearchMenuOpen(isAdvancedSearchOpen); }, [isAdvancedSearchOpen]); const onChangeHandler = (event, value) => { if (onChange) { onChange(event, value); } setSearchValue(value); }; const onToggle = (e) => { const isOpen = !isSearchMenuOpen; setIsSearchMenuOpen(isOpen); if (onToggleAdvancedSearch) { onToggleAdvancedSearch(e, isOpen); } }; const onSearchHandler = (event) => { event.preventDefault(); if (onSearch) { onSearch(event, value, getAttrValueMap()); } setIsSearchMenuOpen(false); }; const splitStringExceptInQuotes = (str) => { let quoteType; return str.match(/\\?.|^$/g).reduce((p, c) => { if (c === "'" || c === '"') { if (!quoteType) { quoteType = c; } if (c === quoteType) { p.quote = !p.quote; } } else if (!p.quote && c === ' ') { p.a.push(''); } else { p.a[p.a.length - 1] += c.replace(/\\(.)/, '$1'); } return p; }, { a: [''] }).a; }; const getAttrValueMap = () => { const attrValue = {}; const pairs = splitStringExceptInQuotes(searchValue); pairs.map((pair) => { const splitPair = pair.split(advancedSearchDelimiter); if (splitPair.length === 2) { attrValue[splitPair[0]] = splitPair[1].replace(/(^'|'$)/g, ''); } else if (splitPair.length === 1) { attrValue.haswords = attrValue.hasOwnProperty('haswords') ? `${attrValue.haswords} ${splitPair[0]}` : splitPair[0]; } }); return attrValue; }; const onEnter = (event) => { if (event.key === 'Enter') { onSearchHandler(event); } }; const onClearInput = (e) => { if (onClear) { onClear(e); } if (searchInputInputRef && searchInputInputRef.current) { searchInputInputRef.current.focus(); } }; const onExpandHandler = (event) => { setSearchValue(''); onToggleExpand(event, isExpanded); setFocusAfterExpandChange(true); }; const renderUtilities = value && (resultsCount || (!!onNextClick && !!onPreviousClick) || (!!onClear && !expandableInput)); const buildTextInputGroup = (_a = {}) => { var searchInputProps = tslib_1.__rest(_a, []); return ((0, jsx_runtime_1.jsxs)(TextInputGroup_1.TextInputGroup, Object.assign({ isDisabled: isDisabled }, searchInputProps, { children: [(0, jsx_runtime_1.jsx)(TextInputGroup_1.TextInputGroupMain, { hint: hint, icon: (0, jsx_runtime_1.jsx)(search_icon_1.default, {}), innerRef: searchInputInputRef, value: searchValue, placeholder: placeholder, "aria-label": ariaLabel, onKeyDown: onEnter, onChange: onChangeHandler, name: name, inputId: searchInputId, inputProps: inputProps }), (renderUtilities || areUtilitiesDisplayed) && ((0, jsx_runtime_1.jsxs)(TextInputGroup_1.TextInputGroupUtilities, { children: [resultsCount && (0, jsx_runtime_1.jsx)(Badge_1.Badge, { isRead: true, children: resultsCount }), !!onNextClick && !!onPreviousClick && ((0, jsx_runtime_1.jsxs)("div", { className: text_input_group_1.default.textInputGroupGroup, children: [(0, jsx_runtime_1.jsx)(Button_1.Button, { variant: Button_1.ButtonVariant.plain, "aria-label": previousNavigationButtonAriaLabel, isDisabled: isDisabled || isPreviousNavigationButtonDisabled, onClick: onPreviousClick, icon: (0, jsx_runtime_1.jsx)(angle_up_icon_1.default, {}) }), (0, jsx_runtime_1.jsx)(Button_1.Button, { variant: Button_1.ButtonVariant.plain, "aria-label": nextNavigationButtonAriaLabel, isDisabled: isDisabled || isNextNavigationButtonDisabled, onClick: onNextClick, icon: (0, jsx_runtime_1.jsx)(angle_down_icon_1.default, {}) })] })), !!onClear && !expandableInput && ((0, jsx_runtime_1.jsx)(Button_1.Button, { variant: Button_1.ButtonVariant.plain, isDisabled: isDisabled, "aria-label": resetButtonLabel, onClick: onClearInput, icon: (0, jsx_runtime_1.jsx)(times_icon_1.default, {}) }))] }))] }))); }; const expandableToggle = ((0, jsx_runtime_1.jsx)(Button_1.Button, { variant: Button_1.ButtonVariant.plain, "aria-label": toggleAriaLabel, "aria-expanded": isExpanded, icon: isExpanded ? (0, jsx_runtime_1.jsx)(times_icon_1.default, {}) : (0, jsx_runtime_1.jsx)(search_icon_1.default, {}), onClick: onExpandHandler, ref: searchInputExpandableToggleRef })); const buildExpandableSearchInput = (_a = {}) => { var searchInputProps = tslib_1.__rest(_a, []); return ((0, jsx_runtime_1.jsxs)(InputGroup_1.InputGroup, Object.assign({}, searchInputProps, { children: [(0, jsx_runtime_1.jsxs)(InputGroup_1.InputGroupItem, { isFill: true, children: [buildTextInputGroup(), " "] }), (0, jsx_runtime_1.jsx)(InputGroup_1.InputGroupItem, { isPlain: true, children: expandableToggle })] }))); }; const buildSearchTextInputGroup = (_a = {}) => { var searchInputProps = tslib_1.__rest(_a, []); if (expandableInput) { return buildExpandableSearchInput(Object.assign({}, searchInputProps)); } return buildTextInputGroup(Object.assign({}, searchInputProps)); }; const buildSearchTextInputGroupWithExtraButtons = (_a = {}) => { var searchInputProps = tslib_1.__rest(_a, []); return ((0, jsx_runtime_1.jsxs)(InputGroup_1.InputGroup, Object.assign({ ref: triggerRef }, searchInputProps, { children: [(0, jsx_runtime_1.jsx)(InputGroup_1.InputGroupItem, { isFill: true, children: buildTextInputGroup() }), (attributes.length > 0 || onToggleAdvancedSearch) && ((0, jsx_runtime_1.jsx)(InputGroup_1.InputGroupItem, { isPlain: true, children: (0, jsx_runtime_1.jsx)(Button_1.Button, { className: isSearchMenuOpen && 'pf-m-expanded', variant: Button_1.ButtonVariant.control, "aria-label": openMenuButtonAriaLabel, onClick: onToggle, isDisabled: isDisabled, "aria-expanded": isSearchMenuOpen, icon: (0, jsx_runtime_1.jsx)(caret_down_icon_1.default, {}) }) })), !!onSearch && ((0, jsx_runtime_1.jsx)(InputGroup_1.InputGroupItem, { children: (0, jsx_runtime_1.jsx)(Button_1.Button, { type: "submit", variant: Button_1.ButtonVariant.control, "aria-label": submitSearchButtonLabel, onClick: onSearchHandler, isDisabled: isDisabled, icon: (0, jsx_runtime_1.jsx)(Icon_1.Icon, { shouldMirrorRTL: true, children: (0, jsx_runtime_1.jsx)(arrow_right_icon_1.default, {}) }) }) })), expandableInput && (0, jsx_runtime_1.jsx)(InputGroup_1.InputGroupItem, { children: expandableToggle })] }))); }; const searchInputProps = Object.assign(Object.assign({}, props), { className: className && (0, react_styles_1.css)(className), innerRef: searchInputRef }); if (!!expandableInput && !isExpanded) { return ((0, jsx_runtime_1.jsx)(InputGroup_1.InputGroup, Object.assign({}, searchInputProps, { children: (0, jsx_runtime_1.jsx)(InputGroup_1.InputGroupItem, { children: expandableToggle }) }))); } if (!!onSearch || attributes.length > 0 || !!onToggleAdvancedSearch) { if (attributes.length > 0) { const AdvancedSearch = ((0, jsx_runtime_1.jsx)("div", { ref: popperRef, children: (0, jsx_runtime_1.jsx)(AdvancedSearchMenu_1.AdvancedSearchMenu, { value: value, parentRef: searchInputRef, parentInputRef: searchInputInputRef, onSearch: onSearch, onClear: onClear, onChange: onChange, onToggleAdvancedMenu: onToggle, resetButtonLabel: resetButtonLabel, submitSearchButtonLabel: submitSearchButtonLabel, attributes: attributes, formAdditionalItems: formAdditionalItems, hasWordsAttrLabel: hasWordsAttrLabel, advancedSearchDelimiter: advancedSearchDelimiter, getAttrValueMap: getAttrValueMap, isSearchMenuOpen: isSearchMenuOpen }) })); const AdvancedSearchWithPopper = ((0, jsx_runtime_1.jsx)("div", Object.assign({ className: (0, react_styles_1.css)(className), ref: searchInputRef }, props, { children: (0, jsx_runtime_1.jsx)(helpers_1.Popper, { trigger: buildSearchTextInputGroupWithExtraButtons(), triggerRef: triggerRef, popper: AdvancedSearch, popperRef: popperRef, isVisible: isSearchMenuOpen, enableFlip: true, appendTo: () => appendTo || searchInputRef.current, zIndex: zIndex }) }))); const AdvancedSearchInline = ((0, jsx_runtime_1.jsxs)("div", Object.assign({ className: (0, react_styles_1.css)(className), ref: searchInputRef }, props, { children: [buildSearchTextInputGroupWithExtraButtons(), AdvancedSearch] }))); return appendTo !== 'inline' ? AdvancedSearchWithPopper : AdvancedSearchInline; } return buildSearchTextInputGroupWithExtraButtons(Object.assign({}, searchInputProps)); } return buildSearchTextInputGroup(searchInputProps); }; SearchInputBase.displayName = 'SearchInputBase'; exports.SearchInput = (0, react_1.forwardRef)((props, ref) => ((0, jsx_runtime_1.jsx)(SearchInputBase, Object.assign({}, props, { innerRef: ref })))); exports.SearchInput.displayName = 'SearchInput'; //# sourceMappingURL=SearchInput.js.map