123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136 |
- import {useCallback, useRef, useState} from 'react';
- import isPropValid from '@emotion/is-prop-valid';
- import styled from '@emotion/styled';
- import Button from 'sentry/components/button';
- import Input, {InputProps} from 'sentry/components/input';
- import {IconSearch} from 'sentry/icons';
- import {IconClose} from 'sentry/icons/iconClose';
- import {t} from 'sentry/locale';
- interface SearchBarProps extends Omit<InputProps, 'onChange'> {
- defaultQuery?: string;
- onChange?: (query: string) => void;
- onSearch?: (query: string) => void;
- query?: string;
- width?: string;
- }
- function SearchBar({
- query: queryProp,
- defaultQuery = '',
- onChange,
- onSearch,
- width,
- size,
- className,
- ...inputProps
- }: SearchBarProps) {
- const inputRef = useRef<HTMLInputElement>(null);
- const [query, setQuery] = useState(queryProp ?? defaultQuery);
- const onQueryChange = useCallback(
- (e: React.ChangeEvent<HTMLInputElement>) => {
- const {value} = e.target;
- setQuery(value);
- onChange?.(value);
- },
- [onChange]
- );
- const onSubmit = useCallback(
- (e: React.FormEvent<HTMLFormElement>) => {
- e.preventDefault();
- inputRef.current?.blur();
- onSearch?.(query);
- },
- [onSearch, query]
- );
- const clearSearch = useCallback(() => {
- setQuery('');
- onChange?.('');
- onSearch?.('');
- }, [onChange, onSearch]);
- return (
- <FormWrap onSubmit={onSubmit} className={className}>
- <StyledInput
- {...inputProps}
- ref={inputRef}
- type="text"
- name="query"
- autoComplete="off"
- value={query}
- onChange={onQueryChange}
- width={width}
- size={size}
- showClearButton={!!query}
- />
- <StyledIconSearch
- color="subText"
- size={size === 'xs' ? 'xs' : 'sm'}
- inputSize={size}
- />
- {!!query && (
- <SearchClearButton
- type="button"
- priority="link"
- onClick={clearSearch}
- size="xs"
- icon={<IconClose size="xs" />}
- aria-label={t('Clear')}
- inputSize={size}
- />
- )}
- </FormWrap>
- );
- }
- const FormWrap = styled('form')`
- display: block;
- position: relative;
- `;
- const StyledInput = styled(Input)<{showClearButton: boolean}>`
- width: ${p => (p.width ? p.width : undefined)};
- padding-left: ${p => `calc(
- ${p.theme.formPadding[p.size ?? 'md'].paddingLeft}px * 1.5 +
- ${p.theme.iconSizes.sm}
- )`};
- ${p =>
- p.showClearButton &&
- `
- padding-right: calc(
- ${p.theme.formPadding[p.size ?? 'md'].paddingRight}px * 1.5 +
- ${p.theme.iconSizes.xs}
- );
- `}
- `;
- const StyledIconSearch = styled(IconSearch, {
- shouldForwardProp: prop => typeof prop === 'string' && isPropValid(prop),
- })<{inputSize: InputProps['size']}>`
- position: absolute;
- top: 50%;
- left: ${p => p.theme.formPadding[p.inputSize ?? 'md'].paddingLeft}px;
- transform: translateY(-50%);
- pointer-events: none;
- `;
- const SearchClearButton = styled(Button)<{inputSize: InputProps['size']}>`
- position: absolute;
- top: 50%;
- transform: translateY(-50%);
- right: ${p => p.theme.formPadding[p.inputSize ?? 'md'].paddingRight}px;
- font-size: ${p => p.theme.fontSizeLarge};
- color: ${p => p.theme.subText};
- &:hover {
- color: ${p => p.theme.textColor};
- }
- `;
- export default SearchBar;
|