Add help mode
This commit is contained in:
parent
19021040f0
commit
6faffa24ff
15 changed files with 369 additions and 139 deletions
|
@ -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,17 +107,23 @@ const Navigator: React.FC = () => {
|
|||
}, []);
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
<Modal
|
||||
className={classes.modal}
|
||||
open={visible}
|
||||
onClose={() => setVisible(false)}
|
||||
fullWidth
|
||||
maxWidth="sm"
|
||||
>
|
||||
<Fade appear in={visible} timeout={theme.transitions.duration.short}>
|
||||
<div className={classes.root}>
|
||||
<Paper className={classes.paper}>
|
||||
<Downshift
|
||||
itemToString={(item: QuickSearchAction) => (item ? item.label : "")}
|
||||
itemToString={(item: QuickSearchAction) =>
|
||||
item ? item.label : ""
|
||||
}
|
||||
onSelect={(item: QuickSearchAction) => {
|
||||
const shouldRemainVisible = item.onClick();
|
||||
if (!shouldRemainVisible) {
|
||||
setVisible(false);
|
||||
item.onClick();
|
||||
}
|
||||
}}
|
||||
onInputValueChange={value =>
|
||||
change({
|
||||
|
@ -161,7 +196,10 @@ const Navigator: React.FC = () => {
|
|||
</div>
|
||||
)}
|
||||
</Downshift>
|
||||
</Dialog>
|
||||
</Paper>
|
||||
</div>
|
||||
</Fade>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
@ -58,6 +58,8 @@ const NavigatorInput = React.forwardRef<HTMLInputElement, NavigatorInputProps>(
|
|||
? "@"
|
||||
: mode === "catalog"
|
||||
? "$"
|
||||
: mode === "help"
|
||||
? "?"
|
||||
: ">"}
|
||||
</span>
|
||||
)}
|
||||
|
|
|
@ -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<NavigatorSectionProps> = props => {
|
|||
key={[item.label, item.type].join(":")}
|
||||
>
|
||||
<span className={classes.itemLabel}>
|
||||
{item.symbol && (
|
||||
<span className={classes.symbol}>{item.symbol}</span>
|
||||
)}
|
||||
<span>{item.label}</span>
|
||||
{item.caption && (
|
||||
<Typography variant="caption">{item.caption}</Typography>
|
||||
|
|
|
@ -26,7 +26,10 @@ export function searchInCatalog(
|
|||
.map<QuickSearchActionInput>(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"
|
||||
|
|
|
@ -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<OrderDraftCreate, {}>
|
||||
createOrder: MutationFunction<OrderDraftCreate, {}>,
|
||||
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<OrderDraftCreate, {}>
|
||||
createOrder: MutationFunction<OrderDraftCreate, {}>,
|
||||
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);
|
||||
|
|
|
@ -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"
|
||||
}));
|
||||
|
|
|
@ -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<OrderDraftCreate, {}>
|
||||
createOrder: MutationFunction<OrderDraftCreate, {}>,
|
||||
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)
|
||||
|
|
|
@ -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"
|
||||
|
|
79
src/components/Navigator/modes/help.ts
Normal file
79
src/components/Navigator/modes/help.ts
Normal file
|
@ -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;
|
|
@ -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<OrderDraftCreate, {}>;
|
||||
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
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
];
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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,24 +78,12 @@ 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));
|
||||
}
|
||||
|
@ -104,8 +93,8 @@ function useQuickSearch(
|
|||
if (mode === "customers") {
|
||||
searchCustomers(value);
|
||||
}
|
||||
|
||||
setQuery(value);
|
||||
}
|
||||
};
|
||||
|
||||
return [
|
||||
|
@ -126,7 +115,8 @@ function useQuickSearch(
|
|||
},
|
||||
{
|
||||
createOrder,
|
||||
navigate
|
||||
navigate,
|
||||
setMode
|
||||
}
|
||||
)
|
||||
];
|
||||
|
|
Loading…
Reference in a new issue