diff --git a/src/components/item/ItemSearch.js b/src/components/item/ItemSearch.js new file mode 100644 index 000000000..db6ac8188 --- /dev/null +++ b/src/components/item/ItemSearch.js @@ -0,0 +1,99 @@ +import OutlinedInput from '@material-ui/core/OutlinedInput'; +import { makeStyles } from '@material-ui/core/styles'; +import SearchIcon from '@material-ui/icons/Search'; +import PropTypes from 'prop-types'; +import React, { useState } from 'react'; + +const useSearchStyles = makeStyles((theme) => ({ + search: { + position: 'relative', + borderRadius: theme.shape.borderRadius, + marginLeft: 0, + width: '100%', + [theme.breakpoints.up('sm')]: { + marginLeft: theme.spacing(1), + width: 'auto', + }, + }, + searchIcon: { + padding: theme.spacing(0, 2), + height: '100%', + position: 'absolute', + pointerEvents: 'none', + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + }, + inputRoot: { + color: 'inherit', + }, + inputInput: { + padding: theme.spacing(1, 1, 1, 0), + // vertical padding + font size from searchIcon + paddingLeft: `calc(1em + ${theme.spacing(4)}px)`, + transition: theme.transitions.create('width'), + width: '100%', + [theme.breakpoints.up('sm')]: { + width: '12ch', + '&:focus': { + width: '20ch', + }, + }, + }, +})); + +const ItemSearchInput = (props) => { + const { searchInputHandler, searchTextState } = props; + const classes = useSearchStyles(); + + return ( +
+
+ +
+ +
+ ); +}; + +ItemSearchInput.propTypes = { + searchInputHandler: PropTypes.func, + searchTextState: PropTypes.string, +}; + +ItemSearchInput.defaultProps = { + searchInputHandler: () => {}, + searchTextState: '', +}; + +const useItemSearch = (items) => { + const [searchText, setSearchText] = useState(''); + + const handleSearchInput = (event) => { + const text = event.target.value; + setSearchText(text.toLowerCase()); + }; + + const searchResults = items.filter((it) => + it.name.toLowerCase().includes(searchText), + ); + + const itemSearchInput = ( + + ); + return { searchResults, itemSearchInput }; +}; + +export { useItemSearch, ItemSearchInput }; diff --git a/src/components/main/Items.js b/src/components/main/Items.js index fb2116fc6..e5598570c 100644 --- a/src/components/main/Items.js +++ b/src/components/main/Items.js @@ -1,20 +1,36 @@ -import React, { useContext } from 'react'; import { List } from 'immutable'; import PropTypes from 'prop-types'; +import React, { useContext } from 'react'; import { ITEM_LAYOUT_MODES } from '../../enums'; -import ItemsTable from './ItemsTable'; -import ItemsGrid from './ItemsGrid'; import { LayoutContext } from '../context/LayoutContext'; +import { useItemSearch } from '../item/ItemSearch'; +import ItemsGrid from './ItemsGrid'; +import ItemsTable from './ItemsTable'; const Items = ({ items, title, id }) => { const { mode } = useContext(LayoutContext); + const { searchResults, itemSearchInput } = useItemSearch(items); switch (mode) { case ITEM_LAYOUT_MODES.GRID: - return ; + return ( + + ); case ITEM_LAYOUT_MODES.LIST: default: - return ; + return ( + + ); } }; diff --git a/src/components/main/ItemsGrid.js b/src/components/main/ItemsGrid.js index b4cffc426..8936319fd 100644 --- a/src/components/main/ItemsGrid.js +++ b/src/components/main/ItemsGrid.js @@ -1,21 +1,23 @@ -import React, { Component } from 'react'; +import Grid from '@material-ui/core/Grid'; +import { withStyles } from '@material-ui/core/styles'; import { List } from 'immutable'; import PropTypes from 'prop-types'; +import React, { Component } from 'react'; import { withRouter } from 'react-router'; -import { withStyles } from '@material-ui/core/styles'; -import Typography from '@material-ui/core/Typography'; -import Grid from '@material-ui/core/Grid'; -import NewItemButton from './NewItemButton'; -import Item from './Item'; +import { ItemSearchInput } from '../item/ItemSearch'; import EmptyItem from './EmptyItem'; +import Item from './Item'; +import TableToolbar from './TableToolbar'; const styles = (theme) => ({ + empty: { padding: '5px 20px' }, title: { display: 'flex', alignItems: 'center', marginBottom: theme.spacing(1), }, }); + class ItemsGrid extends Component { static propTypes = { items: PropTypes.instanceOf(List).isRequired, @@ -24,15 +26,25 @@ class ItemsGrid extends Component { }).isRequired, classes: PropTypes.shape({ title: PropTypes.string.isRequired, + empty: PropTypes.string.isRequired, }).isRequired, title: PropTypes.string.isRequired, + searchInput: PropTypes.instanceOf(ItemSearchInput), + }; + + static defaultProps = { + searchInput: null, }; renderItems = () => { - const { items } = this.props; + const { classes, items } = this.props; if (!items || !items.size) { - return ; + return ( +
+ +
+ ); } return items.map((item) => ( @@ -43,17 +55,14 @@ class ItemsGrid extends Component { }; render() { - const { classes, title } = this.props; + const { title, searchInput } = this.props; return ( - <> - - {title} - - +
+ {this.renderItems()} - +
); } } diff --git a/src/components/main/ItemsTable.js b/src/components/main/ItemsTable.js index 0b3810f26..34c4342d3 100644 --- a/src/components/main/ItemsTable.js +++ b/src/components/main/ItemsTable.js @@ -1,41 +1,42 @@ -import React, { useEffect } from 'react'; -import PropTypes from 'prop-types'; -import { List } from 'immutable'; +import { MUTATION_KEYS } from '@graasp/query-client'; +import Checkbox from '@material-ui/core/Checkbox'; +import Paper from '@material-ui/core/Paper'; import { lighten, makeStyles } from '@material-ui/core/styles'; import Table from '@material-ui/core/Table'; -import { useHistory, useParams } from 'react-router'; -import { useTranslation } from 'react-i18next'; import TableBody from '@material-ui/core/TableBody'; import TableCell from '@material-ui/core/TableCell'; import TableContainer from '@material-ui/core/TableContainer'; import TablePagination from '@material-ui/core/TablePagination'; import TableRow from '@material-ui/core/TableRow'; -import Paper from '@material-ui/core/Paper'; -import Checkbox from '@material-ui/core/Checkbox'; -import { MUTATION_KEYS } from '@graasp/query-client'; -import ItemMenu from './ItemMenu'; +import { List } from 'immutable'; +import PropTypes from 'prop-types'; +import React, { useEffect } from 'react'; +import { useTranslation } from 'react-i18next'; +import { useHistory, useParams } from 'react-router'; +import { ROWS_PER_PAGE_OPTIONS, USER_ITEM_ORDER } from '../../config/constants'; import { buildItemPath } from '../../config/paths'; -import { ORDERING, ITEM_DATA_TYPES, ITEM_TYPES } from '../../enums'; -import { getComparator, stableSort, getRowsForPage } from '../../utils/table'; -import { formatDate } from '../../utils/date'; -import EditButton from '../common/EditButton'; -import ShareButton from '../common/ShareButton'; -import DeleteButton from '../common/DeleteButton'; +import { hooks, useMutation } from '../../config/queryClient'; import { buildItemsTableRowId, ITEMS_TABLE_BODY, ITEMS_TABLE_EMPTY_ROW_ID, ITEMS_TABLE_ROW_CHECKBOX_CLASS, } from '../../config/selectors'; -import TableToolbar from './TableToolbar'; -import TableHead from './TableHead'; -import ItemIcon from './ItemIcon'; +import { ITEM_DATA_TYPES, ITEM_TYPES, ORDERING } from '../../enums'; +import { formatDate } from '../../utils/date'; +import { getChildrenOrderFromFolderExtra } from '../../utils/item'; import { getShortcutTarget } from '../../utils/itemExtra'; -import { ROWS_PER_PAGE_OPTIONS, USER_ITEM_ORDER } from '../../config/constants'; -import DroppableTableBody from '../common/DroppableTableBody'; +import { getComparator, getRowsForPage, stableSort } from '../../utils/table'; +import DeleteButton from '../common/DeleteButton'; import DraggableTableRow from '../common/DraggableTableRow'; -import { hooks, useMutation } from '../../config/queryClient'; -import { getChildrenOrderFromFolderExtra } from '../../utils/item'; +import DroppableTableBody from '../common/DroppableTableBody'; +import EditButton from '../common/EditButton'; +import ShareButton from '../common/ShareButton'; +import { ItemSearchInput } from '../item/ItemSearch'; +import ItemIcon from './ItemIcon'; +import ItemMenu from './ItemMenu'; +import TableHead from './TableHead'; +import TableToolbar from './TableToolbar'; const { useItem } = hooks; @@ -81,7 +82,12 @@ const computeReorderedIdList = (list, startIndex, endIndex) => { return result.map((i) => i.id); }; -const ItemsTable = ({ items: rows, tableTitle, id: tableId }) => { +const ItemsTable = ({ + items: rows, + tableTitle, + id: tableId, + searchInput: itemSearchInput, +}) => { const { itemId } = useParams(); const { data: parentItem } = useItem(itemId); @@ -279,6 +285,7 @@ const ItemsTable = ({ items: rows, tableTitle, id: tableId }) => { tableTitle={tableTitle} numSelected={selected.length} selected={selected} + itemSearchInput={itemSearchInput} /> ({ root: { @@ -28,7 +29,7 @@ const useToolbarStyles = makeStyles((theme) => ({ const TableToolbar = (props) => { const classes = useToolbarStyles(); const { t } = useTranslation(); - const { numSelected, tableTitle, selected } = props; + const { numSelected, tableTitle, selected, itemSearchInput } = props; return ( { )} + {itemSearchInput} + {numSelected > 0 ? (