UNPKG

@carbon/react

Version:

React components for the Carbon Design System

197 lines (192 loc) 6.09 kB
/** * Copyright IBM Corp. 2016, 2023 * * This source code is licensed under the Apache-2.0 license found in the * LICENSE file in the root directory of this source tree. */ import { extends as _extends } from '../../_virtual/_rollupPluginBabelHelpers.js'; import cx from 'classnames'; import PropTypes from 'prop-types'; import React, { useRef, useState, useEffect } from 'react'; import Search from '../Search/Search.js'; import '../Search/Search.Skeleton.js'; import { useId } from '../../internal/useId.js'; import { usePrefix } from '../../internal/usePrefix.js'; import { noopFn } from '../../internal/noopFn.js'; /** * Message ids that will be passed to translateWithId(). */ const translationKeys = { 'carbon.table.toolbar.search.label': 'Filter table', 'carbon.table.toolbar.search.placeholder': 'Filter table' }; const translateWithId = id => { return translationKeys[id]; }; const TableToolbarSearch = ({ className, searchContainerClass, onChange: onChangeProp, onClear = noopFn, translateWithId: t = translateWithId, placeholder, labelText, expanded: expandedProp, defaultExpanded, defaultValue, disabled, onExpand, persistent = false, id, onBlur, onFocus, size = 'lg', tabIndex = '0', ...rest }) => { const { current: controlled } = useRef(expandedProp !== undefined); const [expandedState, setExpandedState] = useState(Boolean(defaultExpanded || defaultValue)); const expanded = controlled ? expandedProp : expandedState; const [value, setValue] = useState(defaultValue || ''); const uniqueId = useId('table-toolbar-search'); const [focusTarget, setFocusTarget] = useState(null); const prefix = usePrefix(); useEffect(() => { if (focusTarget) { focusTarget.current?.querySelector?.('input')?.focus(); setFocusTarget(null); } }, [focusTarget]); useEffect(() => { if (defaultValue) { onChangeProp?.('', defaultValue); } }, //eslint-disable-next-line react-hooks/exhaustive-deps []); const searchClasses = cx(className, { // eslint-disable-next-line @typescript-eslint/no-non-null-assertion [searchContainerClass]: searchContainerClass, [`${prefix}--toolbar-search-container-active`]: expanded, [`${prefix}--toolbar-search-container-disabled`]: disabled, [`${prefix}--toolbar-search-container-expandable`]: !persistent, [`${prefix}--toolbar-search-container-persistent`]: persistent }); const handleExpand = (event, value = !expanded) => { if (!disabled) { if (!controlled && !persistent) { setExpandedState(value); } if (onExpand) { onExpand(event, value); } } }; const onChange = e => { setValue(e.target.value); if (onChangeProp) { onChangeProp(e, e.target.value); } }; const handleOnFocus = event => handleExpand(event, true); const handleOnBlur = event => !value && handleExpand(event, false); return /*#__PURE__*/React.createElement(Search, _extends({ disabled: disabled, className: searchClasses, value: value, id: typeof id !== 'undefined' ? id : uniqueId, labelText: labelText || t('carbon.table.toolbar.search.label'), placeholder: placeholder || t('carbon.table.toolbar.search.placeholder'), onChange: onChange, onClear: onClear, onFocus: onFocus ? event => onFocus(event, handleExpand) : handleOnFocus, onBlur: onBlur ? event => onBlur(event, handleExpand) : handleOnBlur, size: size // HTMLAttributes defines tabIndex as number | undefined but in reality it // also accepts a string, so we cast here to convince Typescript this is okay. , tabIndex: tabIndex }, rest)); }; TableToolbarSearch.propTypes = { children: PropTypes.node, /** * Provide an optional class name for the search container */ className: PropTypes.string, /** * Specifies if the search should initially render in an expanded state */ defaultExpanded: PropTypes.bool, /** * Provide an optional default value for the Search component */ defaultValue: PropTypes.string, /** * Specifies if the search should be disabled */ disabled: PropTypes.bool, /** * Specifies if the search should expand */ expanded: PropTypes.bool, /** * Provide an optional id for the search container */ id: PropTypes.string, /** * Provide an optional label text for the Search component icon */ labelText: PropTypes.string, /** * Provide an optional function to be called when the search input loses focus, this will be * passed the event as the first parameter and a function to handle the expanding of the search * input as the second */ onBlur: PropTypes.func, /** * Provide an optional hook that is called each time the input is updated */ onChange: PropTypes.func, /** * Optional callback called when the search value is cleared. */ onClear: PropTypes.func, /** * Provide an optional hook that is called each time the input is expanded */ onExpand: PropTypes.func, /** * Provide an optional function to be called when the search input gains focus, this will be * passed the event as the first parameter and a function to handle the expanding of the search * input as the second. */ onFocus: PropTypes.func, /** * Whether the search should be allowed to expand */ persistent: PropTypes.bool, /** * Provide an optional placeholder text for the Search component */ placeholder: PropTypes.string, /** * Provide an optional className for the overall container of the Search */ searchContainerClass: PropTypes.string, /** * Specify the size of the Search */ size: PropTypes.oneOf(['sm', 'md', 'lg']), /** * Optional prop to specify the tabIndex of the <Search> (in expanded state) or the container (in collapsed state) */ tabIndex: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), /** * Provide custom text for the component for each translation id */ translateWithId: PropTypes.func }; export { TableToolbarSearch as default };