stream-chat-react
Version:
React components to create chat conversations or livestream style chat
68 lines (67 loc) • 3.96 kB
JavaScript
import React, { useCallback, useEffect, useRef, useState } from 'react';
import clsx from 'clsx';
import { MenuIcon as DefaultMenuIcon, SearchIcon as DefaultSearchInputIcon, ReturnIcon, XIcon, } from './icons';
import { SearchInput as DefaultSearchInput } from './SearchInput';
const SearchBarButton = ({ children, className, onClick, }) => (React.createElement("button", { className: clsx('str-chat__channel-search-bar-button', className), "data-testid": 'search-bar-button', onClick: onClick }, children));
// todo: add context menu control logic
export const SearchBar = (props) => {
const { activateSearch, AppMenu, ClearInputIcon = XIcon, exitSearch, ExitSearchIcon = ReturnIcon, inputIsFocused, MenuIcon = DefaultMenuIcon, searchBarRef, SearchInput = DefaultSearchInput, SearchInputIcon = DefaultSearchInputIcon, ...inputProps } = props;
const [menuIsOpen, setMenuIsOpen] = useState(false);
const appMenuRef = useRef(null);
useEffect(() => {
if (!appMenuRef.current)
return;
const handleKeyDown = (event) => {
if (menuIsOpen && event.key === 'Escape') {
setMenuIsOpen(false);
}
};
const clickListener = (e) => {
if (!(e.target instanceof HTMLElement) ||
!menuIsOpen ||
appMenuRef.current?.contains(e.target))
return;
setMenuIsOpen(false);
};
document.addEventListener('keydown', handleKeyDown);
document.addEventListener('click', clickListener);
return () => {
document.removeEventListener('keydown', handleKeyDown);
document.removeEventListener('click', clickListener);
};
}, [menuIsOpen]);
useEffect(() => {
if (!props.inputRef.current)
return;
const input = props.inputRef.current;
const handleFocus = () => {
activateSearch();
};
const handleBlur = (e) => {
e.stopPropagation(); // handle blur/focus state with React state
};
input.addEventListener('focus', handleFocus);
input.addEventListener('blur', handleBlur);
return () => {
input.removeEventListener('focus', handleFocus);
input.removeEventListener('blur', handleBlur);
};
}, [activateSearch, props.inputRef]);
const handleClearClick = useCallback(() => {
exitSearch();
inputProps.inputRef.current?.focus();
}, [exitSearch, inputProps.inputRef]);
const closeAppMenu = useCallback(() => setMenuIsOpen(false), []);
return (React.createElement("div", { className: 'str-chat__channel-search-bar', "data-testid": 'search-bar', ref: searchBarRef },
inputIsFocused ? (React.createElement(SearchBarButton, { className: 'str-chat__channel-search-bar-button--exit-search', onClick: exitSearch },
React.createElement(ExitSearchIcon, null))) : AppMenu ? (React.createElement(SearchBarButton, { className: 'str-chat__channel-search-bar-button--menu', onClick: () => setMenuIsOpen((prev) => !prev) },
React.createElement(MenuIcon, null))) : null,
React.createElement("div", { className: clsx('str-chat__channel-search-input--wrapper', inputProps.query && 'str-chat__channel-search-input--wrapper-active') },
React.createElement("div", { className: 'str-chat__channel-search-input--icon' },
React.createElement(SearchInputIcon, null)),
React.createElement(SearchInput, { ...inputProps }),
React.createElement("button", { className: 'str-chat__channel-search-input--clear-button', "data-testid": 'clear-input-button', disabled: !inputProps.query, onClick: handleClearClick },
React.createElement(ClearInputIcon, null))),
menuIsOpen && AppMenu && (React.createElement("div", { ref: appMenuRef },
React.createElement(AppMenu, { close: closeAppMenu })))));
};