From 55cae0962d5fbb4b6ae384563006c49bc9c8fafd Mon Sep 17 00:00:00 2001 From: Tom Hacohen Date: Thu, 15 Oct 2020 18:37:38 +0300 Subject: [PATCH] Contacts: redesign the address book view and add filtering by group. This is the first step towards fixing #136. --- src/Contacts/AddressBook.tsx | 2 +- src/Contacts/SearchableAddressBook.tsx | 77 +++++++++++++++++++------- src/Contacts/Sidebar.tsx | 66 ++++++++++++++++++++++ src/Contacts/Toolbar.tsx | 64 +++++++++++++++++++++ 4 files changed, 187 insertions(+), 22 deletions(-) create mode 100644 src/Contacts/Sidebar.tsx create mode 100644 src/Contacts/Toolbar.tsx diff --git a/src/Contacts/AddressBook.tsx b/src/Contacts/AddressBook.tsx index f7f3b8c..1c27af6 100644 --- a/src/Contacts/AddressBook.tsx +++ b/src/Contacts/AddressBook.tsx @@ -96,7 +96,7 @@ class AddressBook extends React.PureComponent { : sortedEntries; return ( - + {({ height, width }) => ( ({ + topBar: { + backgroundColor: theme.palette.primary[500], + }, +})); + interface PropsType { entries: ContactType[]; onItemClick: (contact: ContactType) => void; @@ -20,25 +26,54 @@ interface PropsType { export default function SearchableAddressBook(props: PropsType) { const [searchQuery, setSearchQuery] = React.useState(""); + const [filterByGroup, setFilterByGroup] = React.useState(); + const theme = useTheme(); + const classes = useStyles(); + + const groups = React.useMemo( + (() => props.entries.filter((x) => x.group)), + [props.entries] + ); + const group = React.useMemo( + (() => groups.find((x) => x.uid === filterByGroup)), + [groups, filterByGroup] + ); + + function filterFunc(ent: ContactType) { + return ( + (!group || (group.members.includes(ent.uid))) && + ent.fn?.match(reg) + ); + } const reg = new RegExp(searchQuery, "i"); return ( - - setSearchQuery(event.target.value)} - /> - {searchQuery && - setSearchQuery("")}> - - - } - - ent.fn?.match(reg)} {...props} /> - + + + {/* spacer */} + + + + + + + + + + + + + + + + ); } diff --git a/src/Contacts/Sidebar.tsx b/src/Contacts/Sidebar.tsx new file mode 100644 index 0000000..8737c78 --- /dev/null +++ b/src/Contacts/Sidebar.tsx @@ -0,0 +1,66 @@ +import * as React from "react"; + +import InboxIcon from "@material-ui/icons/Inbox"; +import LabelIcon from "@material-ui/icons/LabelOutlined"; + +import { List, ListItem, ListSubheader } from "../widgets/List"; +import { ContactType } from "../pim-types"; + +interface ListItemPropsType { + name: string | undefined; + icon?: React.ReactElement; + primaryText: string; + filterByGroup: string | undefined; + setFilterByGroup: (group: string | undefined) => void; +} + +function SidebarListItem(props: ListItemPropsType) { + const { name, icon, primaryText, filterByGroup } = props; + + const handleClick = () => props.setFilterByGroup(name); + + return ( + + ); +} + +interface PropsType { + groups: ContactType[]; + filterByGroup: string | undefined; + setFilterByGroup: (group: string | undefined) => void; +} + +export default React.memo(function Sidebar(props: PropsType) { + const { groups, filterByGroup, setFilterByGroup } = props; + + const groupList = [...groups].sort((a, b) => a.fn.localeCompare(b.fn)).map((group) => ( + } + filterByGroup={filterByGroup} + setFilterByGroup={setFilterByGroup} + /> + )); + + return ( + + } + filterByGroup={filterByGroup} + setFilterByGroup={setFilterByGroup} + /> + + Groups + {groupList} + + ); +}); diff --git a/src/Contacts/Toolbar.tsx b/src/Contacts/Toolbar.tsx new file mode 100644 index 0000000..dab0db4 --- /dev/null +++ b/src/Contacts/Toolbar.tsx @@ -0,0 +1,64 @@ +import * as React from "react"; + +import SearchIcon from "@material-ui/icons/Search"; +import TextField from "@material-ui/core/TextField"; +import { makeStyles } from "@material-ui/core/styles"; +import { Transition } from "react-transition-group"; +import InputAdornment from "@material-ui/core/InputAdornment"; + +const transitionTimeout = 300; + +const transitionStyles = { + entering: { visibility: "visible", width: "100%", overflow: "hidden" }, + entered: { visibility: "visible", width: "100%" }, + exiting: { visibility: "visible", width: "0%", overflow: "hidden" }, + exited: { visibility: "hidden", width: "0%" }, +}; + +const useStyles = makeStyles((theme) => ({ + button: { + marginRight: theme.spacing(1), + }, + textField: { + transition: `width ${transitionTimeout}ms`, + marginRight: theme.spacing(1), + }, +})); + +interface PropsType { + searchTerm: string; + setSearchTerm: (term: string) => void; +} + +export default function Toolbar(props: PropsType) { + const { searchTerm, setSearchTerm } = props; + + const showSearchField = true; + const classes = useStyles(); + + return ( +
+ + {(state) => ( + setSearchTerm(e.target.value)} + InputProps={{ + startAdornment: ( + + + + ), + }} + /> + )} + +
+ ); +}