mirror of
https://github.com/rowyio/rowy.git
synced 2025-12-29 00:16:39 +01:00
searchbox list result select result item
This commit is contained in:
@@ -2,21 +2,8 @@ import React, { useState, useEffect } from "react";
|
||||
import SearchIcon from "@material-ui/icons/Search";
|
||||
import IconButton from "@material-ui/core/IconButton";
|
||||
import { createStyles, makeStyles, Theme } from "@material-ui/core/styles";
|
||||
import Popper from "@material-ui/core/Popper";
|
||||
import Paper from "@material-ui/core/Paper";
|
||||
import ClickAwayListener from "@material-ui/core/ClickAwayListener";
|
||||
import algoliasearch from "algoliasearch/lite";
|
||||
import { TextField } from "@material-ui/core";
|
||||
|
||||
const searchClient = algoliasearch(
|
||||
process.env.REACT_APP_ALGOLIA_APP_ID
|
||||
? process.env.REACT_APP_ALGOLIA_APP_ID
|
||||
: "",
|
||||
process.env.REACT_APP_ALGOLIA_SEARCH_KEY
|
||||
? process.env.REACT_APP_ALGOLIA_SEARCH_KEY
|
||||
: ""
|
||||
);
|
||||
|
||||
const useStyles = makeStyles((theme: Theme) =>
|
||||
createStyles({
|
||||
root: {
|
||||
@@ -41,80 +28,47 @@ interface Props {
|
||||
onSubmit: Function;
|
||||
collectionPath: string;
|
||||
isScrolling: boolean;
|
||||
setSearch: any;
|
||||
}
|
||||
|
||||
const DocSelect = (props: Props) => {
|
||||
const { value, row, onSubmit, collectionPath, isScrolling } = props;
|
||||
const {
|
||||
value,
|
||||
row,
|
||||
onSubmit,
|
||||
collectionPath,
|
||||
isScrolling,
|
||||
setSearch,
|
||||
} = props;
|
||||
const [query, setQuery] = useState(value ? value : "");
|
||||
const [hits, setHits] = useState<{}>([]);
|
||||
const algoliaIndex = searchClient.initIndex(collectionPath);
|
||||
const search = async (query: string) => {
|
||||
const resp = await algoliaIndex.search({ query });
|
||||
setHits(resp.hits);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
//search(query);
|
||||
setSearch((oldValues: any) => ({
|
||||
...oldValues,
|
||||
query,
|
||||
}));
|
||||
}, [query]);
|
||||
|
||||
const [anchorEl, setAnchorEl] = useState<HTMLButtonElement | null>(null);
|
||||
const classes = useStyles();
|
||||
|
||||
const handleClick = (
|
||||
event: React.MouseEvent<HTMLButtonElement, MouseEvent>
|
||||
) => {
|
||||
setAnchorEl(event.currentTarget);
|
||||
const handleClick = (event: React.MouseEvent<HTMLDivElement, MouseEvent>) => {
|
||||
setSearch((oldValues: any) => ({
|
||||
...oldValues,
|
||||
collection: collectionPath,
|
||||
anchorEl: event.currentTarget,
|
||||
onSubmit: onSubmit,
|
||||
}));
|
||||
};
|
||||
// if (isScrolling) return <div />;
|
||||
|
||||
const open = Boolean(anchorEl);
|
||||
const id = open ? "no-transition-popper" : undefined;
|
||||
const onClickAway = (event: any) => {
|
||||
if (event.target.id !== id) {
|
||||
// onSubmit();
|
||||
// setAnchorEl(null);
|
||||
}
|
||||
};
|
||||
return (
|
||||
<div className={classes.root}>
|
||||
<ClickAwayListener onClickAway={onClickAway}>
|
||||
<div>
|
||||
<IconButton onClick={handleClick}>
|
||||
<SearchIcon />
|
||||
</IconButton>
|
||||
{value}
|
||||
<Popper id={id} open={open} anchorEl={anchorEl}>
|
||||
<Paper>
|
||||
<TextField
|
||||
id={id}
|
||||
placeholder={`searching ${collectionPath}`}
|
||||
onChange={(e: any) => {
|
||||
setQuery(e.target.value);
|
||||
}}
|
||||
/>
|
||||
|
||||
<div>
|
||||
{/* <InstantSearch
|
||||
indexName={collectionPath}
|
||||
searchClient={searchClient}
|
||||
>
|
||||
|
||||
<SearchBox /> */}
|
||||
|
||||
{/* </InstantSearch> */}
|
||||
</div>
|
||||
</Paper>
|
||||
</Popper>
|
||||
</div>
|
||||
</ClickAwayListener>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const Hit = (props: any) => {
|
||||
return (
|
||||
<div>
|
||||
<h3>{props.hit.firstName}</h3>
|
||||
<p>{props.hit.email}</p>
|
||||
<div className={classes.root} onClick={handleClick}>
|
||||
<TextField
|
||||
placeholder={`searching ${collectionPath}`}
|
||||
onChange={(e: any) => {
|
||||
setQuery(e.target.value);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
105
src/components/SearchBox.tsx
Normal file
105
src/components/SearchBox.tsx
Normal file
@@ -0,0 +1,105 @@
|
||||
import React, { useState, useEffect, useContext } from "react";
|
||||
import { createStyles, makeStyles, Theme } from "@material-ui/core/styles";
|
||||
import Popper from "@material-ui/core/Popper";
|
||||
import List from "@material-ui/core/List";
|
||||
import ListItem from "@material-ui/core/ListItem";
|
||||
import ListItemText from "@material-ui/core/ListItemText";
|
||||
import ClickAwayListener from "@material-ui/core/ClickAwayListener";
|
||||
import algoliasearch from "algoliasearch/lite";
|
||||
import Paper from "@material-ui/core/Paper";
|
||||
|
||||
const searchClient = algoliasearch(
|
||||
process.env.REACT_APP_ALGOLIA_APP_ID
|
||||
? process.env.REACT_APP_ALGOLIA_APP_ID
|
||||
: "",
|
||||
process.env.REACT_APP_ALGOLIA_SEARCH_KEY
|
||||
? process.env.REACT_APP_ALGOLIA_SEARCH_KEY
|
||||
: ""
|
||||
);
|
||||
|
||||
const useStyles = makeStyles(() =>
|
||||
createStyles({
|
||||
root: {
|
||||
position: "relative",
|
||||
display: "flex",
|
||||
flexWrap: "wrap",
|
||||
minWidth: 200,
|
||||
},
|
||||
typography: {},
|
||||
textArea: {
|
||||
fontSize: 14,
|
||||
minWidth: 230,
|
||||
},
|
||||
list: { backgroundColor: "#fff", minWidth: 200 },
|
||||
})
|
||||
);
|
||||
interface Props {
|
||||
searchData: {
|
||||
query: string;
|
||||
collection: string;
|
||||
anchorEl: any;
|
||||
onSubmit: Function | undefined;
|
||||
};
|
||||
clearSearch: Function;
|
||||
}
|
||||
|
||||
const SearchBox = (props: Props) => {
|
||||
const { searchData, clearSearch } = props;
|
||||
const { query, collection, anchorEl, onSubmit } = searchData;
|
||||
const classes = useStyles();
|
||||
|
||||
const [hits, setHits] = useState<any[]>([]);
|
||||
const [algoliaIndex, setAlgoliaIndex] = useState<
|
||||
algoliasearch.Index | undefined
|
||||
>(undefined);
|
||||
useEffect(() => {
|
||||
if (collection) {
|
||||
setAlgoliaIndex(searchClient.initIndex(collection));
|
||||
}
|
||||
}, [collection]);
|
||||
|
||||
const search = async (query: string) => {
|
||||
if (algoliaIndex) {
|
||||
const resp = await algoliaIndex.search({ query });
|
||||
setHits(resp.hits);
|
||||
}
|
||||
};
|
||||
useEffect(() => {
|
||||
search(query);
|
||||
}, [query]);
|
||||
|
||||
const open = Boolean(anchorEl);
|
||||
const id = open ? "no-transition-popper" : undefined;
|
||||
const onClickAway = (event: any) => {};
|
||||
const Hit = (hit: any) => (
|
||||
<ListItem
|
||||
button
|
||||
onClick={() => {
|
||||
let snapshot = { ...hit };
|
||||
delete snapshot._highlightResult;
|
||||
if (onSubmit)
|
||||
onSubmit({ snapshot, docPath: `${collection}/${snapshot.objectID}` });
|
||||
clearSearch();
|
||||
}}
|
||||
>
|
||||
<ListItemText primary={hit.firstName} secondary={hit.lastName} />
|
||||
</ListItem>
|
||||
);
|
||||
|
||||
if (anchorEl)
|
||||
return (
|
||||
<div className={classes.root}>
|
||||
<ClickAwayListener onClickAway={onClickAway}>
|
||||
<Popper id={id} open={open} anchorEl={anchorEl}>
|
||||
<Paper>
|
||||
results for {query}
|
||||
<List>{hits.map((hit: any) => Hit(hit))}</List>
|
||||
</Paper>
|
||||
</Popper>
|
||||
</ClickAwayListener>
|
||||
</div>
|
||||
);
|
||||
else return <div />;
|
||||
};
|
||||
|
||||
export default SearchBox;
|
||||
@@ -120,16 +120,7 @@ export const cellFormatter = (column: any) => {
|
||||
return (props: any) => {
|
||||
return <LongText {...props} onSubmit={onSubmit(key, props.row)} />;
|
||||
};
|
||||
case FieldType.documentSelect:
|
||||
return (props: any) => {
|
||||
return (
|
||||
<DocSelect
|
||||
{...props}
|
||||
onSubmit={onSubmit(key, props.row)}
|
||||
collectionPath={column.collectionPath}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -15,10 +15,14 @@ import {
|
||||
onGridRowsUpdated,
|
||||
singleSelectEditor,
|
||||
editable,
|
||||
onSubmit,
|
||||
} from "./grid-fns";
|
||||
|
||||
import { CLOUD_FUNCTIONS } from "firebase/callables";
|
||||
import ImportCSV from "components/ImportCSV";
|
||||
import SearchBox from "../SearchBox";
|
||||
import DocSelect from "../Fields/DocSelect";
|
||||
|
||||
const deleteAlgoliaRecord = functions.httpsCallable(
|
||||
CLOUD_FUNCTIONS.deleteAlgoliaRecord
|
||||
);
|
||||
@@ -42,6 +46,12 @@ const useStyles = makeStyles(Theme =>
|
||||
function Table(props: any) {
|
||||
const { collection } = props;
|
||||
const { tableState, tableActions } = useFiretable(collection);
|
||||
const [search, setSearch] = useState({
|
||||
query: "",
|
||||
anchorEl: undefined,
|
||||
collection: "",
|
||||
onSubmit: undefined,
|
||||
});
|
||||
useEffect(() => {
|
||||
tableActions.set(collection);
|
||||
}, [collection]);
|
||||
@@ -54,6 +64,22 @@ function Table(props: any) {
|
||||
setHeader(null);
|
||||
setAnchorEl(null);
|
||||
};
|
||||
const clearSearch = () => {
|
||||
setSearch({
|
||||
query: "",
|
||||
anchorEl: undefined,
|
||||
collection: "",
|
||||
onSubmit: undefined,
|
||||
});
|
||||
};
|
||||
const docSelect = (column: any) => (props: any) => (
|
||||
<DocSelect
|
||||
{...props}
|
||||
onSubmit={onSubmit(column.key, props.row)}
|
||||
collectionPath={column.collectionPath}
|
||||
setSearch={setSearch}
|
||||
/>
|
||||
);
|
||||
const handleClick = (headerProps: any) => (
|
||||
event: React.MouseEvent<HTMLButtonElement, MouseEvent>
|
||||
) => {
|
||||
@@ -100,7 +126,10 @@ function Table(props: any) {
|
||||
editable: editable(column.type),
|
||||
resizable: true,
|
||||
headerRenderer: headerRenderer,
|
||||
formatter: cellFormatter(column),
|
||||
formatter:
|
||||
column.type === FieldType.documentSelect
|
||||
? docSelect(column)
|
||||
: cellFormatter(column),
|
||||
editor:
|
||||
column.type === FieldType.singleSelect
|
||||
? singleSelectEditor(column.options)
|
||||
@@ -171,6 +200,7 @@ function Table(props: any) {
|
||||
column={header && header.column}
|
||||
actions={tableActions.column}
|
||||
/>
|
||||
<SearchBox searchData={search} clearSearch={clearSearch} />
|
||||
</>
|
||||
);
|
||||
} else return <p>Loading</p>;
|
||||
|
||||
Reference in New Issue
Block a user