terriajs
Version:
Geospatial data visualization platform.
87 lines • 4.03 kB
JavaScript
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
import debounce from "lodash-es/debounce";
import { useCallback, useEffect, useRef } from "react";
import { forwardRef } from "react";
import styled, { useTheme } from "styled-components";
import Box, { BoxSpan } from "../../Styled/Box";
import { RawButton } from "../../Styled/Button";
import Icon, { StyledIcon } from "../../Styled/Icon";
import Text from "../../Styled/Text";
const SearchInput = styled.input `
box-sizing: border-box;
margin-top: 0;
margin-bottom: 0;
border: none;
border-radius: 4px;
height: 40px;
width: 100%;
display: block;
padding: 0.5rem 40px;
vertical-align: middle;
-webkit-appearance: none;
`;
export const DEBOUNCE_INTERVAL = 1000;
/**
* Simple dumb search box component that leaves the actual execution of searches to the component that renders it. Note
* that just like an input, this calls onSearchTextChanged when the value is changed, and expects that its parent
* component will listen for this and update searchText with the new value.
*/
export const SearchBox = ({ onSearchTextChanged, onDoSearch, searchText, onFocus, placeholder = "Search", onClear, alwaysShowClear = false, debounceDuration, autoFocus = false, supportsAutocomplete = true, inputBoxRef }) => {
const theme = useTheme();
const debouncedOnDoSearch = useRef();
useEffect(() => {
const debounced = debounce(() => onDoSearch(), debounceDuration ?? DEBOUNCE_INTERVAL);
debouncedOnDoSearch.current = debounced;
return () => {
debounced.flush();
};
}, [onDoSearch, debounceDuration]);
useEffect(() => {
return () => debouncedOnDoSearch.current?.cancel();
}, []);
const search = useCallback(() => {
debouncedOnDoSearch.current?.cancel();
onDoSearch();
}, [onDoSearch]);
const handleChange = (event) => {
const value = event.target.value;
// immediately bypass debounce if we started with no value
onSearchTextChanged(value);
if (!supportsAutocomplete)
return;
if (searchText.length === 0) {
search();
}
else {
debouncedOnDoSearch.current?.();
}
};
const clearSearch = () => {
onSearchTextChanged("");
search();
if (onClear) {
onClear();
}
};
const onKeyDown = (event) => {
if (event.key === "Enter") {
search();
}
};
const hasValue = searchText.length > 0;
return (_jsxs("form", { autoComplete: "off", onSubmit: (event) => {
event.preventDefault();
event.stopPropagation();
search();
}, css: `
position: relative;
width: 100%;
`, children: [_jsx("label", { htmlFor: "search", css: `
position: absolute;
`, children: _jsx(Box, { paddedRatio: 2, children: _jsx(StyledIcon, { glyph: Icon.GLYPHS.search, styledWidth: "20px", fillColor: theme.charcoalGrey, opacity: 0.5, css: `
position: absolute;
` }) }) }), _jsx(Text, { large: true, semiBold: true, children: _jsx(SearchInput, { ref: inputBoxRef, id: "search", type: "text", name: "search", value: searchText, onChange: handleChange, onFocus: onFocus, onKeyDown: onKeyDown, placeholder: placeholder, autoComplete: "off", autoFocus: autoFocus, rounded: true }) }), (alwaysShowClear || hasValue) && (_jsx(Box, { position: "absolute", topRight: true, fullHeight: true, styledWidth: "40px", children: _jsx(RawButton, { type: "button", onClick: clearSearch, fullWidth: true, fullHeight: true, "aria-label": "Clear search", children: _jsx(BoxSpan, { centered: true, children: _jsx(StyledIcon, { glyph: Icon.GLYPHS.close, styledWidth: "15px", fillColor: theme.charcoalGrey, opacity: 0.5 }) }) }) }))] }));
};
const SearchBoxWithRef = (props, ref) => _jsx(SearchBox, { ...props, inputBoxRef: ref });
export default forwardRef(SearchBoxWithRef);
//# sourceMappingURL=SearchBox.js.map