From 15e4d57574f41470cfcb5cb6619b81df781d35eb Mon Sep 17 00:00:00 2001 From: Sofia Pohjalainen Date: Mon, 25 May 2020 18:16:35 +0100 Subject: [PATCH] finished search --- src/panel/pages/request/components/Schema.tsx | 114 +++++----- src/panel/pages/request/components/Search.tsx | 201 +++++++++++------- 2 files changed, 187 insertions(+), 128 deletions(-) diff --git a/src/panel/pages/request/components/Schema.tsx b/src/panel/pages/request/components/Schema.tsx index a5d4c005..16d2db7d 100644 --- a/src/panel/pages/request/components/Schema.tsx +++ b/src/panel/pages/request/components/Schema.tsx @@ -5,6 +5,7 @@ import { RequestContext } from "../../../context"; import { Arrow } from "../../../components"; import { Stack } from "./Stack"; import { Fields } from "./Fields"; +import { Search } from "./Search"; type ActiveIds = 1 | 2 | 3; @@ -52,59 +53,72 @@ export const Schema = () => { }; return ( - <> - {stack.length > 0 ? ( - - ) : ( - - {schemaTypes.Query ? ( - <> - handleHeaderClick(1)} - aria-expanded={isActiveId(1)} - > - - Query - - {isActiveId(1) && ( - - )} - - ) : null} - {schemaTypes.Mutation ? ( - <> - handleHeaderClick(2)} - aria-expanded={isActiveId(2)} - > - - Mutation - - {isActiveId(2) && ( - - )} - - ) : null} - {schemaTypes.Subscription ? ( - <> - handleHeaderClick(3)} - aria-expanded={isActiveId(3)} - > - - Subscription - - {isActiveId(3) && ( - - )} - - ) : null} - - )} - + + + + {stack.length > 0 ? ( + + ) : ( + + {schemaTypes.Query ? ( + <> + handleHeaderClick(1)} + aria-expanded={isActiveId(1)} + > + + Query + + {isActiveId(1) && ( + + )} + + ) : null} + {schemaTypes.Mutation ? ( + <> + handleHeaderClick(2)} + aria-expanded={isActiveId(2)} + > + + Mutation + + {isActiveId(2) && ( + + )} + + ) : null} + {schemaTypes.Subscription ? ( + <> + handleHeaderClick(3)} + aria-expanded={isActiveId(3)} + > + + Subscription + + {isActiveId(3) && ( + + )} + + ) : null} + + )} + + ); }; +const FlexContainer = styled.div` + display: flex; + flex-direction: column; + width: 100%; +`; + +const Container = styled.div` + position: relative; +`; + const CollapsibleHeader = styled.button` display: flex; align-items: center; diff --git a/src/panel/pages/request/components/Search.tsx b/src/panel/pages/request/components/Search.tsx index 44bafda4..5d1cc787 100644 --- a/src/panel/pages/request/components/Search.tsx +++ b/src/panel/pages/request/components/Search.tsx @@ -1,88 +1,163 @@ -import React, { FC, useMemo, useCallback } from "react"; +import React, { + FC, + useMemo, + useCallback, + ChangeEvent, + useState, + useEffect, + useRef, +} from "react"; import { GraphQLNamedType } from "graphql"; import styled from "styled-components"; import { TypeMap } from "graphql/type/schema"; +import { faSearch } from "@fortawesome/free-solid-svg-icons"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; interface SearchProps { typeMap: TypeMap; setType: (type: GraphQLNamedType) => void; } -export const Search: FC = (props) => { - const [results, setResults] = useState([]); - const [searchValue, setSearchValue] = useState(""); - const typeKeys = useMemo(() => Object.keys(props.typeMap)); +export const Search: FC = ({ typeMap, setType }) => { + const containerRef = useRef(undefined as any); + const [searchValue, setSearchValue] = useState(""); + const [listOpen, setListOpen] = useState(false); + let typeKeys = useMemo(() => Object.keys(typeMap), [typeMap]); + const results = useMemo( + () => + (typeKeys = typeKeys.filter((key) => + key.toLowerCase().startsWith(searchValue.toLowerCase()) + )), + [searchValue] + ); + + useEffect(() => { + if (searchValue && results.length) { + setListOpen(true); + } else { + setListOpen(false); + } + }, [searchValue, results]); + + useEffect(() => { + const onOutsideClick = (e: MouseEvent) => { + if (!containerRef?.current?.contains(e.target as Node)) { + setListOpen(false); + } else if (searchValue && results.length) { + setListOpen(true); + } + }; + + window.addEventListener("click", onOutsideClick); + + return () => { + window.removeEventListener("click", onOutsideClick); + }; + }, [searchValue, results]); + + const handleOnChange = useCallback( + (e: ChangeEvent) => { + setSearchValue(e.target.value || ""); + }, + [setSearchValue] + ); + + const handleTypeSelect = useCallback( + (type) => { + setType(type); + setListOpen(false); + }, + [setListOpen, setType] + ); - const handleOnChange = () => {}; return ( -
- -
    - {results.map((res) => ( -
  • {res.name}
  • - ))} -
-
+ + + + + + {listOpen ? ( + + {results.map((res, i) => ( + + handleTypeSelect(typeMap[res])}> + {typeMap[res].name} + + + ))} + + ) : null} + ); }; -const StackHeading = styled.div` +const Container = styled.div` + background-color: ${(p) => p.theme.dark["+5"]}; +`; + +const Icon = styled(FontAwesomeIcon)` + font-size: 13px; + margin-right: 6px; + color: ${(p) => p.theme.light["-9"]}; +`; + +const InputWrapper = styled.div` + position: relative; display: flex; align-items: center; justify-content: space-between; + max-width: 250px; color: ${(p) => p.theme.light["-5"]}; - background-color: ${(p) => p.theme.dark["+3"]}; - border-top: 1px solid ${(p) => p.theme.dark["+7"]}; - border-bottom: 1px solid ${(p) => p.theme.dark["+7"]}; font-size: 13px; padding: 6px 12px; - &:first-of-type { - background-color: ${(p) => p.theme.dark["+5"]}; - border-bottom: none; + &::after { + content: "|"; + color: ${(p) => p.theme.grey["-2"]}; } `; -const StackWrapper = styled.div` - box-sizing: border-box; - position: absolute; - right: 0; - top: 0; - height: 100%; +const Input = styled.input` + background-color: transparent; + border: none; width: 100%; - display: flex; - flex-direction: column; - background-color: ${(p) => p.theme.dark["+1"]}; + color: ${(p) => p.theme.light["-5"]}; `; -export const Box = styled.div` +const List = styled.ul` + position: absolute; + right: 0; + left: 0; display: flex; + width: 250px; + max-height: 400px; + margin-top: 0; + padding: 12px 6px; + list-style: none; flex-direction: column; -`; + background-color: ${(p) => p.theme.dark["+2"]}; + border: 1px solid ${(p) => p.theme.dark["+4"]}; -const TypeKind = styled.code` - color: ${(p) => p.theme.yellow["+3"]}; - margin-right: 6px; - - &[data-kind="interface"] { - color: ${(p) => p.theme.red["+3"]}; - } - - &[data-kind="enum"] { - color: ${(p) => p.theme.purple["+3"]}; - } + z-index: 2; + overflow: auto; +`; - &[data-kind="union"] { - color: ${(p) => p.theme.blue["+3"]}; - } +const ListItem = styled.li` + padding: 6px; - &[data-kind="scalar"] { - color: ${(p) => p.theme.orange["+3"]}; + &:hover { + background-color: ${(p) => p.theme.dark["+3"]}; } `; const TextButton = styled.button` display: inline-block; + width: 100%; background: transparent; outline: none; border: none; @@ -97,33 +172,3 @@ const TextButton = styled.button` text-decoration: underline; } `; - -const BackButton = styled(TextButton)` - width: max-content; - color: ${(p) => p.theme.light["-9"]}; - font-size: 12px; - border-radius: 3px; - margin-right: 6px; - padding: 4px 6px; - - &:last-child { - margin-right: 0; - } - - &:hover { - background-color: ${(p) => p.theme.grey["-9"]}; - text-decoration: none; - } -`; - -const Title = styled.span` - color: ${(p) => p.theme.light["-9"]}; - display: inline-block; - padding: 4px 6px; -`; - -const Description = styled.p` - font-size: 13px; - color: ${(p) => p.theme.light["-9"]}; - margin: 12px; -`;