UNPKG

terriajs

Version:

Geospatial data visualization platform.

87 lines 4.03 kB
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