Add Advanced Search toggle button
It shows all of the suggestion options! I feel like the state stuff on this isn't super robust, I hope it works okay!
This commit is contained in:
parent
5799c725ff
commit
add7fc0cb6
1 changed files with 99 additions and 29 deletions
|
@ -9,9 +9,15 @@ import {
|
||||||
InputLeftAddon,
|
InputLeftAddon,
|
||||||
InputLeftElement,
|
InputLeftElement,
|
||||||
InputRightElement,
|
InputRightElement,
|
||||||
|
Tooltip,
|
||||||
useColorModeValue,
|
useColorModeValue,
|
||||||
} from "@chakra-ui/react";
|
} from "@chakra-ui/react";
|
||||||
import { CloseIcon, SearchIcon } from "@chakra-ui/icons";
|
import {
|
||||||
|
ChevronDownIcon,
|
||||||
|
ChevronUpIcon,
|
||||||
|
CloseIcon,
|
||||||
|
SearchIcon,
|
||||||
|
} from "@chakra-ui/icons";
|
||||||
import { ClassNames } from "@emotion/react";
|
import { ClassNames } from "@emotion/react";
|
||||||
import Autosuggest from "react-autosuggest";
|
import Autosuggest from "react-autosuggest";
|
||||||
|
|
||||||
|
@ -48,6 +54,7 @@ function SearchToolbar({
|
||||||
boxShadow = null,
|
boxShadow = null,
|
||||||
}) {
|
}) {
|
||||||
const [suggestions, setSuggestions] = React.useState([]);
|
const [suggestions, setSuggestions] = React.useState([]);
|
||||||
|
const [advancedSearchIsOpen, setAdvancedSearchIsOpen] = React.useState(false);
|
||||||
const { isLoggedIn } = useCurrentUser();
|
const { isLoggedIn } = useCurrentUser();
|
||||||
|
|
||||||
// NOTE: This query should always load ~instantly, from the client cache.
|
// NOTE: This query should always load ~instantly, from the client cache.
|
||||||
|
@ -103,8 +110,9 @@ function SearchToolbar({
|
||||||
{...otherContainerProps}
|
{...otherContainerProps}
|
||||||
borderBottomRadius="md"
|
borderBottomRadius="md"
|
||||||
boxShadow="md"
|
boxShadow="md"
|
||||||
overflow="hidden"
|
overflow="auto"
|
||||||
transition="all 0.4s"
|
transition="all 0.4s"
|
||||||
|
maxHeight="48"
|
||||||
className={cx(
|
className={cx(
|
||||||
className,
|
className,
|
||||||
css`
|
css`
|
||||||
|
@ -115,12 +123,22 @@ function SearchToolbar({
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
|
{!children && advancedSearchIsOpen && (
|
||||||
|
<Box
|
||||||
|
padding="4"
|
||||||
|
fontSize="sm"
|
||||||
|
fontStyle="italic"
|
||||||
|
textAlign="center"
|
||||||
|
>
|
||||||
|
No more filters available!
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
</ClassNames>
|
</ClassNames>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
[]
|
[advancedSearchIsOpen]
|
||||||
);
|
);
|
||||||
|
|
||||||
// When we change the query filters, clear out the suggestions.
|
// When we change the query filters, clear out the suggestions.
|
||||||
|
@ -148,11 +166,23 @@ function SearchToolbar({
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const allSuggestions = getSuggestions(null, query, zoneLabels, isLoggedIn, {
|
||||||
|
showAll: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Once you remove the final suggestion available, close Advanced Search. We
|
||||||
|
// have placeholder text available, sure, but this feels more natural!
|
||||||
|
React.useEffect(() => {
|
||||||
|
if (allSuggestions.length === 0) {
|
||||||
|
setAdvancedSearchIsOpen(false);
|
||||||
|
}
|
||||||
|
}, [allSuggestions.length]);
|
||||||
|
|
||||||
const focusBorderColor = useColorModeValue("green.600", "green.400");
|
const focusBorderColor = useColorModeValue("green.600", "green.400");
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Autosuggest
|
<Autosuggest
|
||||||
suggestions={suggestions}
|
suggestions={advancedSearchIsOpen ? allSuggestions : suggestions}
|
||||||
onSuggestionsFetchRequested={({ value }) => {
|
onSuggestionsFetchRequested={({ value }) => {
|
||||||
// HACK: I'm not sure why, but apparently this gets called with value
|
// HACK: I'm not sure why, but apparently this gets called with value
|
||||||
// set to the _chosen suggestion_ after choosing it? Has that
|
// set to the _chosen suggestion_ after choosing it? Has that
|
||||||
|
@ -162,7 +192,8 @@ function SearchToolbar({
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
onSuggestionSelected={(e, { suggestion }) => {
|
onSuggestionSelected={(e, { suggestion }) => {
|
||||||
const valueWithoutLastWord = query.value.match(/^(.*?)\s*\S+$/)[1];
|
const valueWithoutLastWord =
|
||||||
|
query.value.match(/^(.*?)\s*\S+$/)?.[1] || query.value;
|
||||||
onChange({
|
onChange({
|
||||||
...query,
|
...query,
|
||||||
value: valueWithoutLastWord,
|
value: valueWithoutLastWord,
|
||||||
|
@ -194,24 +225,45 @@ function SearchToolbar({
|
||||||
autoFocus={autoFocus}
|
autoFocus={autoFocus}
|
||||||
{...inputProps}
|
{...inputProps}
|
||||||
/>
|
/>
|
||||||
{!searchQueryIsEmpty(query) && (
|
<InputRightElement
|
||||||
<InputRightElement>
|
width="auto"
|
||||||
|
justifyContent="flex-end"
|
||||||
|
paddingRight="2px"
|
||||||
|
paddingY="2px"
|
||||||
|
>
|
||||||
|
{!searchQueryIsEmpty(query) && (
|
||||||
|
<Tooltip label="Clear">
|
||||||
|
<IconButton
|
||||||
|
icon={<CloseIcon fontSize="0.6em" />}
|
||||||
|
color="gray.400"
|
||||||
|
variant="ghost"
|
||||||
|
height="100%"
|
||||||
|
marginLeft="1"
|
||||||
|
aria-label="Clear search"
|
||||||
|
onClick={() => {
|
||||||
|
setSuggestions([]);
|
||||||
|
onChange(emptySearchQuery);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
|
)}
|
||||||
|
<Tooltip label="Advanced search">
|
||||||
<IconButton
|
<IconButton
|
||||||
icon={<CloseIcon />}
|
icon={
|
||||||
|
advancedSearchIsOpen ? (
|
||||||
|
<ChevronUpIcon fontSize="1.5em" />
|
||||||
|
) : (
|
||||||
|
<ChevronDownIcon fontSize="1.5em" />
|
||||||
|
)
|
||||||
|
}
|
||||||
color="gray.400"
|
color="gray.400"
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
colorScheme="green"
|
height="100%"
|
||||||
aria-label="Clear search"
|
aria-label="Open advanced search"
|
||||||
onClick={() => {
|
onClick={() => setAdvancedSearchIsOpen((isOpen) => !isOpen)}
|
||||||
setSuggestions([]);
|
|
||||||
onChange(emptySearchQuery);
|
|
||||||
}}
|
|
||||||
// Big style hacks here!
|
|
||||||
height="calc(100% - 2px)"
|
|
||||||
marginRight="2px"
|
|
||||||
/>
|
/>
|
||||||
</InputRightElement>
|
</Tooltip>
|
||||||
)}
|
</InputRightElement>
|
||||||
</InputGroup>
|
</InputGroup>
|
||||||
)}
|
)}
|
||||||
inputProps={{
|
inputProps={{
|
||||||
|
@ -256,46 +308,64 @@ function SearchToolbar({
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getSuggestions(value, query, zoneLabels, isLoggedIn) {
|
function getSuggestions(
|
||||||
if (!value) {
|
value,
|
||||||
|
query,
|
||||||
|
zoneLabels,
|
||||||
|
isLoggedIn,
|
||||||
|
{ showAll = false } = {}
|
||||||
|
) {
|
||||||
|
if (!value && !showAll) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
const words = value.split(/\s+/);
|
const words = (value || "").split(/\s+/);
|
||||||
const lastWord = words[words.length - 1];
|
const lastWord = words[words.length - 1];
|
||||||
if (lastWord.length < 2) {
|
if (lastWord.length < 2 && !showAll) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
const suggestions = [];
|
const suggestions = [];
|
||||||
|
|
||||||
if (query.filterToItemKind == null) {
|
if (query.filterToItemKind == null) {
|
||||||
if (wordMatches("NC", lastWord) || wordMatches("Neocash", lastWord)) {
|
if (
|
||||||
|
wordMatches("NC", lastWord) ||
|
||||||
|
wordMatches("Neocash", lastWord) ||
|
||||||
|
showAll
|
||||||
|
) {
|
||||||
suggestions.push({ itemKind: "NC", text: "Neocash items" });
|
suggestions.push({ itemKind: "NC", text: "Neocash items" });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (wordMatches("NP", lastWord) || wordMatches("Neopoints", lastWord)) {
|
if (
|
||||||
|
wordMatches("NP", lastWord) ||
|
||||||
|
wordMatches("Neopoints", lastWord) ||
|
||||||
|
showAll
|
||||||
|
) {
|
||||||
suggestions.push({ itemKind: "NP", text: "Neopoint items" });
|
suggestions.push({ itemKind: "NP", text: "Neopoint items" });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (wordMatches("PB", lastWord) || wordMatches("Paintbrush", lastWord)) {
|
if (
|
||||||
|
wordMatches("PB", lastWord) ||
|
||||||
|
wordMatches("Paintbrush", lastWord) ||
|
||||||
|
showAll
|
||||||
|
) {
|
||||||
suggestions.push({ itemKind: "PB", text: "Paintbrush items" });
|
suggestions.push({ itemKind: "PB", text: "Paintbrush items" });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isLoggedIn && query.filterToCurrentUserOwnsOrWants == null) {
|
if (isLoggedIn && query.filterToCurrentUserOwnsOrWants == null) {
|
||||||
if (wordMatches("Items you own", lastWord)) {
|
if (wordMatches("Items you own", lastWord) || showAll) {
|
||||||
suggestions.push({ userOwnsOrWants: "OWNS", text: "Items you own" });
|
suggestions.push({ userOwnsOrWants: "OWNS", text: "Items you own" });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (wordMatches("Items you want", lastWord)) {
|
if (wordMatches("Items you want", lastWord) || showAll) {
|
||||||
suggestions.push({ userOwnsOrWants: "WANTS", text: "Items you want" });
|
suggestions.push({ userOwnsOrWants: "WANTS", text: "Items you want" });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (query.filterToZoneLabel == null) {
|
if (query.filterToZoneLabel == null) {
|
||||||
for (const zoneLabel of zoneLabels) {
|
for (const zoneLabel of zoneLabels) {
|
||||||
if (wordMatches(zoneLabel, lastWord)) {
|
if (wordMatches(zoneLabel, lastWord) || showAll) {
|
||||||
suggestions.push({ zoneLabel, text: `Zone: ${zoneLabel}` });
|
suggestions.push({ zoneLabel, text: `Zone: ${zoneLabel}` });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue