diff --git a/src/components/Navigator/Navigator.tsx b/src/components/Navigator/Navigator.tsx index e4fa3d6c4..3a2752f60 100644 --- a/src/components/Navigator/Navigator.tsx +++ b/src/components/Navigator/Navigator.tsx @@ -1,4 +1,8 @@ -import Dialog from "@material-ui/core/Dialog"; +import Fade from "@material-ui/core/Fade"; +import Modal from "@material-ui/core/Modal"; +import Paper from "@material-ui/core/Paper"; +import makeStyles from "@material-ui/core/styles/makeStyles"; +import useTheme from "@material-ui/core/styles/useTheme"; import Downshift from "downshift"; import hotkeys from "hotkeys-js"; import React from "react"; @@ -33,6 +37,29 @@ function getItemOffset( return cbs.reduce((acc, cb) => cb(actions).length + acc, 0); } +const useStyles = makeStyles( + theme => ({ + modal: { + alignItems: "center", + display: "flex", + justifyContent: "center", + padding: theme.spacing(3) + }, + paper: { + overflow: "hidden" + }, + root: { + height: 500, + maxWidth: 600, + outline: 0, + width: "100%" + } + }), + { + name: "Navigator" + } +); + const Navigator: React.FC = () => { const [visible, setVisible] = React.useState(false); const input = React.useRef(null); @@ -43,6 +70,8 @@ const Navigator: React.FC = () => { navigatorNotificationStorageKey, false ); + const classes = useStyles({}); + const theme = useTheme(); React.useEffect(() => { hotkeys(navigatorHotkey, event => { @@ -78,90 +107,99 @@ const Navigator: React.FC = () => { }, []); return ( - setVisible(false)} - fullWidth - maxWidth="sm" > - (item ? item.label : "")} - onSelect={(item: QuickSearchAction) => { - setVisible(false); - item.onClick(); - }} - onInputValueChange={value => - change({ - target: { - name: "query", - value - } - }) - } - defaultHighlightedIndex={0} - > - {({ getInputProps, getItemProps, highlightedIndex }) => ( -
- - {hasViews(actions) && ( - - )} - {hasActions(actions) && ( - - )} - {hasCustomers(actions) && ( - - )} - {hasCatalog(actions) && ( - - )} -
- )} -
-
+ +
+ + + item ? item.label : "" + } + onSelect={(item: QuickSearchAction) => { + const shouldRemainVisible = item.onClick(); + if (!shouldRemainVisible) { + setVisible(false); + } + }} + onInputValueChange={value => + change({ + target: { + name: "query", + value + } + }) + } + defaultHighlightedIndex={0} + > + {({ getInputProps, getItemProps, highlightedIndex }) => ( +
+ + {hasViews(actions) && ( + + )} + {hasActions(actions) && ( + + )} + {hasCustomers(actions) && ( + + )} + {hasCatalog(actions) && ( + + )} +
+ )} +
+
+
+
+ ); }; diff --git a/src/components/Navigator/NavigatorInput.tsx b/src/components/Navigator/NavigatorInput.tsx index 74961502b..cfc0619cd 100644 --- a/src/components/Navigator/NavigatorInput.tsx +++ b/src/components/Navigator/NavigatorInput.tsx @@ -58,6 +58,8 @@ const NavigatorInput = React.forwardRef( ? "@" : mode === "catalog" ? "$" + : mode === "help" + ? "?" : ">"} )} diff --git a/src/components/Navigator/NavigatorSection.tsx b/src/components/Navigator/NavigatorSection.tsx index 69f7eb603..40cb73a67 100644 --- a/src/components/Navigator/NavigatorSection.tsx +++ b/src/components/Navigator/NavigatorSection.tsx @@ -33,14 +33,19 @@ const useStyles = makeStyles( textTransform: "uppercase" }, root: { - "&:not(:last-child)": { - marginBottom: theme.spacing(3) + "&:last-child": { + marginBottom: 0 }, margin: theme.spacing(2, 0), padding: theme.spacing(0, 1) }, spacer: { flex: 1 + }, + symbol: { + display: "inline-block", + fontWeight: 600, + width: theme.spacing(4) } }), { @@ -78,6 +83,9 @@ const NavigatorSection: React.FC = props => { key={[item.label, item.type].join(":")} > + {item.symbol && ( + {item.symbol} + )} {item.label} {item.caption && ( {item.caption} diff --git a/src/components/Navigator/modes/catalog.ts b/src/components/Navigator/modes/catalog.ts index 41e018389..08be62dee 100644 --- a/src/components/Navigator/modes/catalog.ts +++ b/src/components/Navigator/modes/catalog.ts @@ -26,7 +26,10 @@ export function searchInCatalog( .map(category => ({ caption: intl.formatMessage(messages.category), label: category.name, - onClick: () => navigate(categoryUrl(category.id)), + onClick: () => { + navigate(categoryUrl(category.id)); + return false; + }, score: score(category.name, search), text: category.name, type: "catalog" @@ -45,7 +48,10 @@ export function searchInCatalog( : messages.collectionUnpublished ), label: collection.name, - onClick: () => navigate(collectionUrl(collection.id)), + onClick: () => { + navigate(collectionUrl(collection.id)); + return false; + }, score: score(collection.name, search), text: collection.name, type: "catalog" @@ -60,7 +66,10 @@ export function searchInCatalog( caption: intl.formatMessage(messages.product), extraInfo: product.category.name, label: product.name, - onClick: () => navigate(productUrl(product.id)), + onClick: () => { + navigate(productUrl(product.id)); + return false; + }, score: score(product.name, search), text: product.name, type: "catalog" diff --git a/src/components/Navigator/modes/commands/actions.ts b/src/components/Navigator/modes/commands/actions.ts index fe5e428eb..66aa226ee 100644 --- a/src/components/Navigator/modes/commands/actions.ts +++ b/src/components/Navigator/modes/commands/actions.ts @@ -9,7 +9,7 @@ import { UseNavigatorResult } from "@saleor/hooks/useNavigator"; import { OrderDraftCreate } from "@saleor/orders/types/OrderDraftCreate"; import { productAddUrl } from "@saleor/products/urls"; import { MutationFunction } from "react-apollo"; -import { QuickSearchActionInput } from "../../types"; +import { QuickSearchActionInput, QuickSearchMode } from "../../types"; import messages from "../messages"; import { sortScores } from "../utils"; @@ -18,38 +18,64 @@ const maxActions = 5; interface Command { label: string; - onClick: () => void; + onClick: () => boolean; } export function searchInCommands( search: string, intl: IntlShape, navigate: UseNavigatorResult, - createOrder: MutationFunction + createOrder: MutationFunction, + setMode: (mode: QuickSearchMode) => void ): QuickSearchActionInput[] { const actions: Command[] = [ { label: intl.formatMessage(messages.addCategory), - onClick: () => navigate(categoryAddUrl()) + onClick: () => { + navigate(categoryAddUrl()); + return false; + } }, { label: intl.formatMessage(messages.addCollection), - onClick: () => navigate(collectionAddUrl) + onClick: () => { + navigate(collectionAddUrl); + return false; + } }, { label: intl.formatMessage(messages.addProduct), - onClick: () => navigate(productAddUrl) + onClick: () => { + navigate(productAddUrl); + return false; + } }, { label: intl.formatMessage(messages.addCustomer), - onClick: () => navigate(customerAddUrl) + onClick: () => { + navigate(customerAddUrl); + return false; + } }, { label: intl.formatMessage(messages.addVoucher), - onClick: () => navigate(voucherAddUrl) + onClick: () => { + navigate(voucherAddUrl); + return false; + } }, { label: intl.formatMessage(messages.createOrder), - onClick: createOrder + onClick: () => { + createOrder(); + return false; + } + }, + { + label: intl.formatMessage(messages.helpMode), + onClick: () => { + setMode("help"); + return true; + } } ]; @@ -66,9 +92,10 @@ function getCommandModeActions( query: string, intl: IntlShape, navigate: UseNavigatorResult, - createOrder: MutationFunction + createOrder: MutationFunction, + setMode: (mode: QuickSearchMode) => void ): QuickSearchActionInput[] { - return [...searchInCommands(query, intl, navigate, createOrder)] + return [...searchInCommands(query, intl, navigate, createOrder, setMode)] .filter(action => action.score >= threshold) .sort(sortScores) .slice(0, maxActions); diff --git a/src/components/Navigator/modes/customers.ts b/src/components/Navigator/modes/customers.ts index 4a7e8d7c0..32c1fe187 100644 --- a/src/components/Navigator/modes/customers.ts +++ b/src/components/Navigator/modes/customers.ts @@ -20,7 +20,10 @@ export function searchInCustomers( lastName: customer.lastName }) : customer.email, - onClick: () => navigate(customerUrl(customer.id)), + onClick: () => { + navigate(customerUrl(customer.id)); + return false; + }, score: 1, type: "customer" })); diff --git a/src/components/Navigator/modes/default/default.ts b/src/components/Navigator/modes/default/default.ts index 09557e4ed..d25691fff 100644 --- a/src/components/Navigator/modes/default/default.ts +++ b/src/components/Navigator/modes/default/default.ts @@ -3,7 +3,7 @@ import { IntlShape } from "react-intl"; import { UseNavigatorResult } from "@saleor/hooks/useNavigator"; import { OrderDraftCreate } from "@saleor/orders/types/OrderDraftCreate"; -import { QuickSearchAction } from "../../types"; +import { QuickSearchAction, QuickSearchMode } from "../../types"; import { searchInCommands } from "../commands"; import { sortScores } from "../utils"; import searchInViews from "./views"; @@ -15,11 +15,12 @@ function getDefaultModeActions( query: string, intl: IntlShape, navigate: UseNavigatorResult, - createOrder: MutationFunction + createOrder: MutationFunction, + setMode: (mode: QuickSearchMode) => void ): QuickSearchAction[] { return [ ...searchInViews(query, intl, navigate), - ...searchInCommands(query, intl, navigate, createOrder) + ...searchInCommands(query, intl, navigate, createOrder, setMode) ] .filter(action => action.score >= threshold) .sort(sortScores) diff --git a/src/components/Navigator/modes/default/views.ts b/src/components/Navigator/modes/default/views.ts index dede14220..639f1852e 100644 --- a/src/components/Navigator/modes/default/views.ts +++ b/src/components/Navigator/modes/default/views.ts @@ -121,7 +121,10 @@ function searchInViews( return views.map(view => ({ label: view.label, - onClick: () => navigate(view.url), + onClick: () => { + navigate(view.url); + return false; + }, score: score(view.label, search), text: view.label, type: "view" diff --git a/src/components/Navigator/modes/help.ts b/src/components/Navigator/modes/help.ts new file mode 100644 index 000000000..92cd110c2 --- /dev/null +++ b/src/components/Navigator/modes/help.ts @@ -0,0 +1,79 @@ +import { IntlShape } from "react-intl"; + +import { QuickSearchAction, QuickSearchMode } from "../types"; +import messages from "./messages"; + +function getHelpModeActions( + query: string, + intl: IntlShape, + setMode: (mode: QuickSearchMode) => void +): QuickSearchAction[] { + if (query !== "") { + return [ + { + label: intl.formatMessage(messages.noResults), + onClick: () => true, + type: "action" + } + ]; + } + + return [ + { + label: intl.formatMessage(messages.helpDefaultMode), + onClick: () => { + setMode("default"); + return true; + }, + symbol: "...", + type: "action" + }, + { + label: intl.formatMessage(messages.helpCommandsMode), + onClick: () => { + setMode("commands"); + return true; + }, + symbol: ">", + type: "action" + }, + { + label: intl.formatMessage(messages.helpOrdersMode), + onClick: () => { + setMode("orders"); + return true; + }, + symbol: "#", + type: "action" + }, + { + label: intl.formatMessage(messages.helpCustomersMode), + onClick: () => { + setMode("customers"); + return true; + }, + symbol: "@", + type: "action" + }, + { + label: intl.formatMessage(messages.helpCatalogMode), + onClick: () => { + setMode("catalog"); + return true; + }, + symbol: "$", + type: "action" + }, + { + label: intl.formatMessage(messages.helpMode), + onClick: () => { + setMode("help"); + return true; + }, + symbol: "?", + type: "action" + } + ]; +} + +export default getHelpModeActions; diff --git a/src/components/Navigator/modes/index.ts b/src/components/Navigator/modes/index.ts index 4333883e9..4dd2d487f 100644 --- a/src/components/Navigator/modes/index.ts +++ b/src/components/Navigator/modes/index.ts @@ -8,6 +8,7 @@ import getCatalogModeActions from "./catalog"; import getCommandModeActions from "./commands"; import getCustomersModeActions from "./customers"; import getDefaultModeActions from "./default"; +import getHelpModeActions from "./help"; import getOrdersModeActions from "./orders"; import { ActionQueries } from "./types"; @@ -17,21 +18,36 @@ function getModeActions( intl: IntlShape, queries: ActionQueries, cbs: { - navigate: UseNavigatorResult; createOrder: MutationFunction; + navigate: UseNavigatorResult; + setMode: (mode: QuickSearchMode) => void; } ): QuickSearchAction[] { switch (mode) { case "catalog": return getCatalogModeActions(query, intl, cbs.navigate, queries.catalog); case "commands": - return getCommandModeActions(query, intl, cbs.navigate, cbs.createOrder); + return getCommandModeActions( + query, + intl, + cbs.navigate, + cbs.createOrder, + cbs.setMode + ); case "customers": return getCustomersModeActions(intl, cbs.navigate, queries.customers); + case "help": + return getHelpModeActions(query, intl, cbs.setMode); case "orders": return getOrdersModeActions(query, intl, cbs.navigate, queries.order); default: - return getDefaultModeActions(query, intl, cbs.navigate, cbs.createOrder); + return getDefaultModeActions( + query, + intl, + cbs.navigate, + cbs.createOrder, + cbs.setMode + ); } } diff --git a/src/components/Navigator/modes/messages.ts b/src/components/Navigator/modes/messages.ts index ef909af71..d17174278 100644 --- a/src/components/Navigator/modes/messages.ts +++ b/src/components/Navigator/modes/messages.ts @@ -48,6 +48,33 @@ const messages = defineMessages({ defaultMessage: "Go to order #{orderNumber}", description: "navigator action" }, + helpCatalogMode: { + defaultMessage: "Search in Catalog", + description: "navigator catalog mode description" + }, + helpCommandsMode: { + defaultMessage: "Search Command", + description: "navigator command mode description" + }, + helpCustomersMode: { + defaultMessage: "Search Customers", + description: "navigator customer mode description" + }, + helpDefaultMode: { + defaultMessage: "Search Views and Actions", + description: "navigator default mode description" + }, + helpMode: { + defaultMessage: "Display Help", + description: "navigator help mode description" + }, + helpOrdersMode: { + defaultMessage: "Search Orders", + description: "navigator order mode description" + }, + noResults: { + defaultMessage: "No Results" + }, product: { defaultMessage: "Product", description: "catalog item type" diff --git a/src/components/Navigator/modes/orders.ts b/src/components/Navigator/modes/orders.ts index d4e3d7da2..d5d44b244 100644 --- a/src/components/Navigator/modes/orders.ts +++ b/src/components/Navigator/modes/orders.ts @@ -30,7 +30,10 @@ function getOrdersModeActions( label: intl.formatMessage(messages.goToOrder, { orderNumber: query }), - onClick: () => navigate(orderUrl(gqlId)), + onClick: () => { + navigate(orderUrl(gqlId)); + return false; + }, type: "action" } ]; diff --git a/src/components/Navigator/modes/utils.ts b/src/components/Navigator/modes/utils.ts index 17ac2b1ba..2c0f599e1 100644 --- a/src/components/Navigator/modes/utils.ts +++ b/src/components/Navigator/modes/utils.ts @@ -1,4 +1,8 @@ -import { QuickSearchAction, QuickSearchActionInput } from "../types"; +import { + QuickSearchAction, + QuickSearchActionInput, + QuickSearchMode +} from "../types"; export function getActions(actions: QuickSearchAction[]): QuickSearchAction[] { return actions.filter(action => action.type === "action"); @@ -36,3 +40,21 @@ export function sortScores( ) { return a.score <= b.score ? 1 : -1; } + +export function getMode(command: string): QuickSearchMode { + switch (command) { + case ">": + return "commands"; + case "@": + return "customers"; + case "#": + return "orders"; + case "$": + return "catalog"; + case "?": + return "help"; + + default: + return null; + } +} diff --git a/src/components/Navigator/types.ts b/src/components/Navigator/types.ts index 1f7421860..a333e29a3 100644 --- a/src/components/Navigator/types.ts +++ b/src/components/Navigator/types.ts @@ -5,8 +5,9 @@ export interface QuickSearchAction { extraInfo?: string; label: string; price?: number; + symbol?: string; type: QuickSearchActionType; - onClick: () => void; + onClick: () => boolean; } export interface QuickSearchActionInput extends QuickSearchAction { @@ -18,5 +19,6 @@ export type QuickSearchMode = | "default" | "catalog" | "commands" - | "orders" - | "customers"; + | "customers" + | "help" + | "orders"; diff --git a/src/components/Navigator/useQuickSearch.ts b/src/components/Navigator/useQuickSearch.ts index 0eac8ce8c..edf3ddde8 100644 --- a/src/components/Navigator/useQuickSearch.ts +++ b/src/components/Navigator/useQuickSearch.ts @@ -11,6 +11,7 @@ import { orderUrl } from "@saleor/orders/urls"; import useCustomerSearch from "@saleor/searches/useCustomerSearch"; import getModeActions from "./modes"; import { getGqlOrderId, isQueryValidOrderNumber } from "./modes/orders"; +import { getMode } from "./modes/utils"; import useSearchCatalog from "./queries/useCatalogSearch"; import useCheckIfOrderExists from "./queries/useCheckIfOrderExists"; import { QuickSearchAction, QuickSearchMode } from "./types"; @@ -77,35 +78,23 @@ function useQuickSearch( const change = (event: ChangeEvent) => { const value = event.target.value; - if (mode === "default") { - switch (value) { - case "> ": - setMode("commands"); - break; - case "@ ": - setMode("customers"); - break; - case "# ": - setMode("orders"); - break; - case "$ ": - setMode("catalog"); - break; - default: - setQuery(value); + if (mode === "default" || mode === "help") { + const newMode = getMode(value); + if (newMode) { + setMode(newMode); } - } else { - if (mode === "orders" && isQueryValidOrderNumber(value)) { - getOrderData(getGqlOrderId(value)); - } - if (mode === "catalog") { - searchCatalog(value); - } - if (mode === "customers") { - searchCustomers(value); - } - setQuery(value); } + if (mode === "orders" && isQueryValidOrderNumber(value)) { + getOrderData(getGqlOrderId(value)); + } + if (mode === "catalog") { + searchCatalog(value); + } + if (mode === "customers") { + searchCustomers(value); + } + + setQuery(value); }; return [ @@ -126,7 +115,8 @@ function useQuickSearch( }, { createOrder, - navigate + navigate, + setMode } ) ];