fix: Add icons to menu items (#2373)
Co-authored-by: Tom Moor <tom.moor@gmail.com>
This commit is contained in:
@ -4,6 +4,7 @@ import * as React from "react";
|
|||||||
import { MenuItem as BaseMenuItem } from "reakit/Menu";
|
import { MenuItem as BaseMenuItem } from "reakit/Menu";
|
||||||
import styled from "styled-components";
|
import styled from "styled-components";
|
||||||
import breakpoint from "styled-components-breakpoint";
|
import breakpoint from "styled-components-breakpoint";
|
||||||
|
import MenuIconWrapper from "../MenuIconWrapper";
|
||||||
|
|
||||||
type Props = {|
|
type Props = {|
|
||||||
onClick?: (SyntheticEvent<>) => void | Promise<void>,
|
onClick?: (SyntheticEvent<>) => void | Promise<void>,
|
||||||
@ -16,6 +17,7 @@ type Props = {|
|
|||||||
as?: string | React.ComponentType<*>,
|
as?: string | React.ComponentType<*>,
|
||||||
hide?: () => void,
|
hide?: () => void,
|
||||||
level?: number,
|
level?: number,
|
||||||
|
icon?: React.Node,
|
||||||
|};
|
|};
|
||||||
|
|
||||||
const MenuItem = ({
|
const MenuItem = ({
|
||||||
@ -25,6 +27,7 @@ const MenuItem = ({
|
|||||||
disabled,
|
disabled,
|
||||||
as,
|
as,
|
||||||
hide,
|
hide,
|
||||||
|
icon,
|
||||||
...rest
|
...rest
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
const handleClick = React.useCallback(
|
const handleClick = React.useCallback(
|
||||||
@ -71,6 +74,7 @@ const MenuItem = ({
|
|||||||
|
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
{icon && <MenuIconWrapper>{icon}</MenuIconWrapper>}
|
||||||
{children}
|
{children}
|
||||||
</MenuAnchor>
|
</MenuAnchor>
|
||||||
)}
|
)}
|
||||||
@ -130,8 +134,8 @@ export const MenuAnchor = styled.a`
|
|||||||
`};
|
`};
|
||||||
|
|
||||||
${breakpoint("tablet")`
|
${breakpoint("tablet")`
|
||||||
padding: ${(props) => (props.$toggleable ? "4px 12px" : "6px 12px")};
|
padding: 4px 12px;
|
||||||
font-size: 15px;
|
font-size: 14px;
|
||||||
`};
|
`};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
@ -9,6 +9,8 @@ import {
|
|||||||
MenuItem as BaseMenuItem,
|
MenuItem as BaseMenuItem,
|
||||||
} from "reakit/Menu";
|
} from "reakit/Menu";
|
||||||
import styled from "styled-components";
|
import styled from "styled-components";
|
||||||
|
import Flex from "components/Flex";
|
||||||
|
import MenuIconWrapper from "components/MenuIconWrapper";
|
||||||
import Header from "./Header";
|
import Header from "./Header";
|
||||||
import MenuItem, { MenuAnchor } from "./MenuItem";
|
import MenuItem, { MenuAnchor } from "./MenuItem";
|
||||||
import Separator from "./Separator";
|
import Separator from "./Separator";
|
||||||
@ -67,7 +69,15 @@ export function filterTemplateItems(items: TMenuItem[]): TMenuItem[] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function Template({ items, ...menu }: Props): React.Node {
|
function Template({ items, ...menu }: Props): React.Node {
|
||||||
return filterTemplateItems(items).map((item, index) => {
|
const filteredTemplates = filterTemplateItems(items);
|
||||||
|
const iconIsPresentInAnyMenuItem = filteredTemplates.find(
|
||||||
|
(item) => !!item.icon
|
||||||
|
);
|
||||||
|
|
||||||
|
return filteredTemplates.map((item, index) => {
|
||||||
|
if (iconIsPresentInAnyMenuItem)
|
||||||
|
item.icon = item.icon ? item.icon : <MenuIconWrapper />;
|
||||||
|
|
||||||
if (item.to) {
|
if (item.to) {
|
||||||
return (
|
return (
|
||||||
<MenuItem
|
<MenuItem
|
||||||
@ -76,6 +86,7 @@ function Template({ items, ...menu }: Props): React.Node {
|
|||||||
key={index}
|
key={index}
|
||||||
disabled={item.disabled}
|
disabled={item.disabled}
|
||||||
selected={item.selected}
|
selected={item.selected}
|
||||||
|
icon={item.icon}
|
||||||
{...menu}
|
{...menu}
|
||||||
>
|
>
|
||||||
{item.title}
|
{item.title}
|
||||||
@ -92,6 +103,7 @@ function Template({ items, ...menu }: Props): React.Node {
|
|||||||
selected={item.selected}
|
selected={item.selected}
|
||||||
level={item.level}
|
level={item.level}
|
||||||
target={item.href.startsWith("#") ? undefined : "_blank"}
|
target={item.href.startsWith("#") ? undefined : "_blank"}
|
||||||
|
icon={item.icon}
|
||||||
{...menu}
|
{...menu}
|
||||||
>
|
>
|
||||||
{item.title}
|
{item.title}
|
||||||
@ -107,6 +119,7 @@ function Template({ items, ...menu }: Props): React.Node {
|
|||||||
disabled={item.disabled}
|
disabled={item.disabled}
|
||||||
selected={item.selected}
|
selected={item.selected}
|
||||||
key={index}
|
key={index}
|
||||||
|
icon={item.icon}
|
||||||
{...menu}
|
{...menu}
|
||||||
>
|
>
|
||||||
{item.title}
|
{item.title}
|
||||||
@ -120,7 +133,7 @@ function Template({ items, ...menu }: Props): React.Node {
|
|||||||
key={index}
|
key={index}
|
||||||
as={Submenu}
|
as={Submenu}
|
||||||
templateItems={item.items}
|
templateItems={item.items}
|
||||||
title={item.title}
|
title={<Title title={item.title} icon={item.icon} />}
|
||||||
{...menu}
|
{...menu}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
@ -139,4 +152,13 @@ function Template({ items, ...menu }: Props): React.Node {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function Title({ title, icon }) {
|
||||||
|
return (
|
||||||
|
<Flex align="center">
|
||||||
|
{icon && <MenuIconWrapper>{icon}</MenuIconWrapper>}
|
||||||
|
{title}
|
||||||
|
</Flex>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
export default React.memo<Props>(Template);
|
export default React.memo<Props>(Template);
|
||||||
|
11
app/components/MenuIconWrapper.js
Normal file
11
app/components/MenuIconWrapper.js
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
// @flow
|
||||||
|
import styled from "styled-components";
|
||||||
|
|
||||||
|
const MenuIconWrapper = styled.span`
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
margin-right: 6px;
|
||||||
|
margin-left: -4px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
export default MenuIconWrapper;
|
@ -1,23 +1,19 @@
|
|||||||
// @flow
|
// @flow
|
||||||
import { observer } from "mobx-react";
|
import { observer } from "mobx-react";
|
||||||
import { SunIcon, MoonIcon } from "outline-icons";
|
import { MoonIcon, SunIcon } from "outline-icons";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { Link } from "react-router-dom";
|
import { MenuButton, useMenuState } from "reakit/Menu";
|
||||||
import { useMenuState, MenuButton } from "reakit/Menu";
|
|
||||||
import styled from "styled-components";
|
|
||||||
import {
|
import {
|
||||||
developers,
|
|
||||||
changelog,
|
changelog,
|
||||||
|
developers,
|
||||||
githubIssuesUrl,
|
githubIssuesUrl,
|
||||||
mailToUrl,
|
mailToUrl,
|
||||||
settings,
|
settings,
|
||||||
} from "shared/utils/routeHelpers";
|
} from "shared/utils/routeHelpers";
|
||||||
import KeyboardShortcuts from "scenes/KeyboardShortcuts";
|
import KeyboardShortcuts from "scenes/KeyboardShortcuts";
|
||||||
import ContextMenu from "components/ContextMenu";
|
import ContextMenu from "components/ContextMenu";
|
||||||
import MenuItem, { MenuAnchor } from "components/ContextMenu/MenuItem";
|
import Template from "components/ContextMenu/Template";
|
||||||
import Separator from "components/ContextMenu/Separator";
|
|
||||||
import Flex from "components/Flex";
|
|
||||||
import Guide from "components/Guide";
|
import Guide from "components/Guide";
|
||||||
import useBoolean from "hooks/useBoolean";
|
import useBoolean from "hooks/useBoolean";
|
||||||
import usePrevious from "hooks/usePrevious";
|
import usePrevious from "hooks/usePrevious";
|
||||||
@ -27,50 +23,6 @@ type Props = {|
|
|||||||
children: (props: any) => React.Node,
|
children: (props: any) => React.Node,
|
||||||
|};
|
|};
|
||||||
|
|
||||||
const AppearanceMenu = React.forwardRef((props, ref) => {
|
|
||||||
const { ui } = useStores();
|
|
||||||
const { t } = useTranslation();
|
|
||||||
const menu = useMenuState();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<MenuButton ref={ref} {...menu} {...props} onClick={menu.show}>
|
|
||||||
{(props) => (
|
|
||||||
<MenuAnchor {...props}>
|
|
||||||
<ChangeTheme justify="space-between">
|
|
||||||
{t("Appearance")}
|
|
||||||
{ui.resolvedTheme === "light" ? <SunIcon /> : <MoonIcon />}
|
|
||||||
</ChangeTheme>
|
|
||||||
</MenuAnchor>
|
|
||||||
)}
|
|
||||||
</MenuButton>
|
|
||||||
<ContextMenu {...menu} aria-label={t("Appearance")}>
|
|
||||||
<MenuItem
|
|
||||||
{...menu}
|
|
||||||
onClick={() => ui.setTheme("system")}
|
|
||||||
selected={ui.theme === "system"}
|
|
||||||
>
|
|
||||||
{t("System")}
|
|
||||||
</MenuItem>
|
|
||||||
<MenuItem
|
|
||||||
{...menu}
|
|
||||||
onClick={() => ui.setTheme("light")}
|
|
||||||
selected={ui.theme === "light"}
|
|
||||||
>
|
|
||||||
{t("Light")}
|
|
||||||
</MenuItem>
|
|
||||||
<MenuItem
|
|
||||||
{...menu}
|
|
||||||
onClick={() => ui.setTheme("dark")}
|
|
||||||
selected={ui.theme === "dark"}
|
|
||||||
>
|
|
||||||
{t("Dark")}
|
|
||||||
</MenuItem>
|
|
||||||
</ContextMenu>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
function AccountMenu(props: Props) {
|
function AccountMenu(props: Props) {
|
||||||
const menu = useMenuState({
|
const menu = useMenuState({
|
||||||
unstable_offset: [8, 0],
|
unstable_offset: [8, 0],
|
||||||
@ -92,6 +44,67 @@ function AccountMenu(props: Props) {
|
|||||||
}
|
}
|
||||||
}, [menu, ui.theme, previousTheme]);
|
}, [menu, ui.theme, previousTheme]);
|
||||||
|
|
||||||
|
const items = React.useMemo(
|
||||||
|
() => [
|
||||||
|
{
|
||||||
|
title: t("Settings"),
|
||||||
|
to: settings(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t("Keyboard shortcuts"),
|
||||||
|
onClick: handleKeyboardShortcutsOpen,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t("API documentation"),
|
||||||
|
href: developers(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "separator",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t("Changelog"),
|
||||||
|
href: changelog(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t("Send us feedback"),
|
||||||
|
href: mailToUrl(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t("Report a bug"),
|
||||||
|
href: githubIssuesUrl(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t("Appearance"),
|
||||||
|
icon: ui.resolvedTheme === "light" ? <SunIcon /> : <MoonIcon />,
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
title: t("System"),
|
||||||
|
onClick: () => ui.setTheme("system"),
|
||||||
|
selected: ui.theme === "system",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t("Light"),
|
||||||
|
onClick: () => ui.setTheme("light"),
|
||||||
|
selected: ui.theme === "light",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t("Dark"),
|
||||||
|
onClick: () => ui.setTheme("dark"),
|
||||||
|
selected: ui.theme === "dark",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "separator",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t("Log out"),
|
||||||
|
onClick: auth.logout,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[auth.logout, handleKeyboardShortcutsOpen, t, ui]
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Guide
|
<Guide
|
||||||
@ -103,38 +116,10 @@ function AccountMenu(props: Props) {
|
|||||||
</Guide>
|
</Guide>
|
||||||
<MenuButton {...menu}>{props.children}</MenuButton>
|
<MenuButton {...menu}>{props.children}</MenuButton>
|
||||||
<ContextMenu {...menu} aria-label={t("Account")}>
|
<ContextMenu {...menu} aria-label={t("Account")}>
|
||||||
<MenuItem {...menu} as={Link} to={settings()}>
|
<Template {...menu} items={items} />
|
||||||
{t("Settings")}
|
|
||||||
</MenuItem>
|
|
||||||
<MenuItem {...menu} onClick={handleKeyboardShortcutsOpen}>
|
|
||||||
{t("Keyboard shortcuts")}
|
|
||||||
</MenuItem>
|
|
||||||
<MenuItem {...menu} href={developers()} target="_blank">
|
|
||||||
{t("API documentation")}
|
|
||||||
</MenuItem>
|
|
||||||
<Separator {...menu} />
|
|
||||||
<MenuItem {...menu} href={changelog()} target="_blank">
|
|
||||||
{t("Changelog")}
|
|
||||||
</MenuItem>
|
|
||||||
<MenuItem {...menu} href={mailToUrl()} target="_blank">
|
|
||||||
{t("Send us feedback")}
|
|
||||||
</MenuItem>
|
|
||||||
<MenuItem {...menu} href={githubIssuesUrl()} target="_blank">
|
|
||||||
{t("Report a bug")}
|
|
||||||
</MenuItem>
|
|
||||||
<Separator {...menu} />
|
|
||||||
<MenuItem {...menu} as={AppearanceMenu} />
|
|
||||||
<Separator {...menu} />
|
|
||||||
<MenuItem {...menu} onClick={auth.logout}>
|
|
||||||
{t("Log out")}
|
|
||||||
</MenuItem>
|
|
||||||
</ContextMenu>
|
</ContextMenu>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const ChangeTheme = styled(Flex)`
|
|
||||||
width: 100%;
|
|
||||||
`;
|
|
||||||
|
|
||||||
export default observer(AccountMenu);
|
export default observer(AccountMenu);
|
||||||
|
@ -1,5 +1,13 @@
|
|||||||
// @flow
|
// @flow
|
||||||
import { observer } from "mobx-react";
|
import { observer } from "mobx-react";
|
||||||
|
import {
|
||||||
|
NewDocumentIcon,
|
||||||
|
EditIcon,
|
||||||
|
TrashIcon,
|
||||||
|
ImportIcon,
|
||||||
|
ExportIcon,
|
||||||
|
PadlockIcon,
|
||||||
|
} from "outline-icons";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { useHistory } from "react-router-dom";
|
import { useHistory } from "react-router-dom";
|
||||||
@ -12,7 +20,7 @@ import CollectionExport from "scenes/CollectionExport";
|
|||||||
import CollectionPermissions from "scenes/CollectionPermissions";
|
import CollectionPermissions from "scenes/CollectionPermissions";
|
||||||
import ContextMenu from "components/ContextMenu";
|
import ContextMenu from "components/ContextMenu";
|
||||||
import OverflowMenuButton from "components/ContextMenu/OverflowMenuButton";
|
import OverflowMenuButton from "components/ContextMenu/OverflowMenuButton";
|
||||||
import Template, { filterTemplateItems } from "components/ContextMenu/Template";
|
import Template from "components/ContextMenu/Template";
|
||||||
import Modal from "components/Modal";
|
import Modal from "components/Modal";
|
||||||
import useStores from "hooks/useStores";
|
import useStores from "hooks/useStores";
|
||||||
import useToasts from "hooks/useToasts";
|
import useToasts from "hooks/useToasts";
|
||||||
@ -113,17 +121,18 @@ function CollectionMenu({
|
|||||||
|
|
||||||
const can = policies.abilities(collection.id);
|
const can = policies.abilities(collection.id);
|
||||||
const items = React.useMemo(
|
const items = React.useMemo(
|
||||||
() =>
|
() => [
|
||||||
filterTemplateItems([
|
|
||||||
{
|
{
|
||||||
title: t("New document"),
|
title: t("New document"),
|
||||||
visible: can.update,
|
visible: can.update,
|
||||||
onClick: handleNewDocument,
|
onClick: handleNewDocument,
|
||||||
|
icon: <NewDocumentIcon />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t("Import document"),
|
title: t("Import document"),
|
||||||
visible: can.update,
|
visible: can.update,
|
||||||
onClick: handleImportDocument,
|
onClick: handleImportDocument,
|
||||||
|
icon: <ImportIcon />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: "separator",
|
type: "separator",
|
||||||
@ -132,16 +141,19 @@ function CollectionMenu({
|
|||||||
title: `${t("Edit")}…`,
|
title: `${t("Edit")}…`,
|
||||||
visible: can.update,
|
visible: can.update,
|
||||||
onClick: () => setShowCollectionEdit(true),
|
onClick: () => setShowCollectionEdit(true),
|
||||||
|
icon: <EditIcon />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: `${t("Permissions")}…`,
|
title: `${t("Permissions")}…`,
|
||||||
visible: can.update,
|
visible: can.update,
|
||||||
onClick: () => setShowCollectionPermissions(true),
|
onClick: () => setShowCollectionPermissions(true),
|
||||||
|
icon: <PadlockIcon />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: `${t("Export")}…`,
|
title: `${t("Export")}…`,
|
||||||
visible: !!(collection && can.export),
|
visible: !!(collection && can.export),
|
||||||
onClick: () => setShowCollectionExport(true),
|
onClick: () => setShowCollectionExport(true),
|
||||||
|
icon: <ExportIcon />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: "separator",
|
type: "separator",
|
||||||
@ -150,8 +162,9 @@ function CollectionMenu({
|
|||||||
title: `${t("Delete")}…`,
|
title: `${t("Delete")}…`,
|
||||||
visible: !!(collection && can.delete),
|
visible: !!(collection && can.delete),
|
||||||
onClick: () => setShowCollectionDelete(true),
|
onClick: () => setShowCollectionDelete(true),
|
||||||
|
icon: <TrashIcon />,
|
||||||
},
|
},
|
||||||
]),
|
],
|
||||||
[can, collection, handleNewDocument, handleImportDocument, t]
|
[can, collection, handleNewDocument, handleImportDocument, t]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -1,5 +1,25 @@
|
|||||||
// @flow
|
// @flow
|
||||||
import { observer } from "mobx-react";
|
import { observer } from "mobx-react";
|
||||||
|
import {
|
||||||
|
EditIcon,
|
||||||
|
PinIcon,
|
||||||
|
StarredIcon,
|
||||||
|
UnstarredIcon,
|
||||||
|
DuplicateIcon,
|
||||||
|
ArchiveIcon,
|
||||||
|
TrashIcon,
|
||||||
|
MoveIcon,
|
||||||
|
HistoryIcon,
|
||||||
|
UnpublishIcon,
|
||||||
|
ShapesIcon,
|
||||||
|
PrintIcon,
|
||||||
|
ImportIcon,
|
||||||
|
NewDocumentIcon,
|
||||||
|
DownloadIcon,
|
||||||
|
BuildingBlocksIcon,
|
||||||
|
RestoreIcon,
|
||||||
|
CrossIcon,
|
||||||
|
} from "outline-icons";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { useHistory } from "react-router-dom";
|
import { useHistory } from "react-router-dom";
|
||||||
@ -248,6 +268,7 @@ function DocumentMenu({
|
|||||||
title: t("Restore"),
|
title: t("Restore"),
|
||||||
visible: (!!collection && can.restore) || can.unarchive,
|
visible: (!!collection && can.restore) || can.unarchive,
|
||||||
onClick: handleRestore,
|
onClick: handleRestore,
|
||||||
|
icon: <RestoreIcon />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t("Restore"),
|
title: t("Restore"),
|
||||||
@ -258,6 +279,7 @@ function DocumentMenu({
|
|||||||
position: "relative",
|
position: "relative",
|
||||||
top: -40,
|
top: -40,
|
||||||
},
|
},
|
||||||
|
icon: <RestoreIcon />,
|
||||||
hover: true,
|
hover: true,
|
||||||
items: [
|
items: [
|
||||||
{
|
{
|
||||||
@ -271,86 +293,102 @@ function DocumentMenu({
|
|||||||
title: t("Unpin"),
|
title: t("Unpin"),
|
||||||
onClick: document.unpin,
|
onClick: document.unpin,
|
||||||
visible: !!(showPin && document.pinned && can.unpin),
|
visible: !!(showPin && document.pinned && can.unpin),
|
||||||
|
icon: <PinIcon />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t("Pin to collection"),
|
title: t("Pin to collection"),
|
||||||
onClick: document.pin,
|
onClick: document.pin,
|
||||||
visible: !!(showPin && !document.pinned && can.pin),
|
visible: !!(showPin && !document.pinned && can.pin),
|
||||||
|
icon: <PinIcon />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t("Unstar"),
|
title: t("Unstar"),
|
||||||
onClick: handleUnstar,
|
onClick: handleUnstar,
|
||||||
visible: document.isStarred && !!can.unstar,
|
visible: document.isStarred && !!can.unstar,
|
||||||
|
icon: <UnstarredIcon />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t("Star"),
|
title: t("Star"),
|
||||||
onClick: handleStar,
|
onClick: handleStar,
|
||||||
visible: !document.isStarred && !!can.star,
|
visible: !document.isStarred && !!can.star,
|
||||||
},
|
icon: <StarredIcon />,
|
||||||
{
|
|
||||||
title: t("Enable embeds"),
|
|
||||||
onClick: document.enableEmbeds,
|
|
||||||
visible: !!showToggleEmbeds && document.embedsDisabled,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: t("Disable embeds"),
|
|
||||||
onClick: document.disableEmbeds,
|
|
||||||
visible: !!showToggleEmbeds && !document.embedsDisabled,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: "separator",
|
type: "separator",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: t("Edit"),
|
||||||
|
to: editDocumentUrl(document),
|
||||||
|
visible: !!can.update,
|
||||||
|
icon: <EditIcon />,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: t("New nested document"),
|
title: t("New nested document"),
|
||||||
to: newDocumentUrl(document.collectionId, {
|
to: newDocumentUrl(document.collectionId, {
|
||||||
parentDocumentId: document.id,
|
parentDocumentId: document.id,
|
||||||
}),
|
}),
|
||||||
visible: !!can.createChildDocument,
|
visible: !!can.createChildDocument,
|
||||||
|
icon: <NewDocumentIcon />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t("Import document"),
|
title: t("Import document"),
|
||||||
visible: can.createChildDocument,
|
visible: can.createChildDocument,
|
||||||
onClick: handleImportDocument,
|
onClick: handleImportDocument,
|
||||||
|
icon: <ImportIcon />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: `${t("Create template")}…`,
|
title: `${t("Create template")}…`,
|
||||||
onClick: () => setShowTemplateModal(true),
|
onClick: () => setShowTemplateModal(true),
|
||||||
visible: !!can.update && !document.isTemplate,
|
visible: !!can.update && !document.isTemplate,
|
||||||
},
|
icon: <ShapesIcon />,
|
||||||
{
|
|
||||||
title: t("Edit"),
|
|
||||||
to: editDocumentUrl(document),
|
|
||||||
visible: !!can.update,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t("Duplicate"),
|
title: t("Duplicate"),
|
||||||
onClick: handleDuplicate,
|
onClick: handleDuplicate,
|
||||||
visible: !!can.update,
|
visible: !!can.update,
|
||||||
|
icon: <DuplicateIcon />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t("Unpublish"),
|
title: t("Unpublish"),
|
||||||
onClick: handleUnpublish,
|
onClick: handleUnpublish,
|
||||||
visible: !!can.unpublish,
|
visible: !!can.unpublish,
|
||||||
|
icon: <UnpublishIcon />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t("Archive"),
|
title: t("Archive"),
|
||||||
onClick: handleArchive,
|
onClick: handleArchive,
|
||||||
visible: !!can.archive,
|
visible: !!can.archive,
|
||||||
|
icon: <ArchiveIcon />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: `${t("Delete")}…`,
|
title: `${t("Delete")}…`,
|
||||||
onClick: () => setShowDeleteModal(true),
|
onClick: () => setShowDeleteModal(true),
|
||||||
visible: !!can.delete,
|
visible: !!can.delete,
|
||||||
|
icon: <TrashIcon />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: `${t("Permanently delete")}…`,
|
title: `${t("Permanently delete")}…`,
|
||||||
onClick: () => setShowPermanentDeleteModal(true),
|
onClick: () => setShowPermanentDeleteModal(true),
|
||||||
visible: can.permanentDelete,
|
visible: can.permanentDelete,
|
||||||
|
icon: <CrossIcon />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: `${t("Move")}…`,
|
title: `${t("Move")}…`,
|
||||||
onClick: () => setShowMoveModal(true),
|
onClick: () => setShowMoveModal(true),
|
||||||
visible: !!can.move,
|
visible: !!can.move,
|
||||||
|
icon: <MoveIcon />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t("Enable embeds"),
|
||||||
|
onClick: document.enableEmbeds,
|
||||||
|
visible: !!showToggleEmbeds && document.embedsDisabled,
|
||||||
|
icon: <BuildingBlocksIcon />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t("Disable embeds"),
|
||||||
|
onClick: document.disableEmbeds,
|
||||||
|
visible: !!showToggleEmbeds && !document.embedsDisabled,
|
||||||
|
icon: <BuildingBlocksIcon />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: "separator",
|
type: "separator",
|
||||||
@ -361,16 +399,19 @@ function DocumentMenu({
|
|||||||
? documentUrl(document)
|
? documentUrl(document)
|
||||||
: documentHistoryUrl(document),
|
: documentHistoryUrl(document),
|
||||||
visible: canViewHistory,
|
visible: canViewHistory,
|
||||||
|
icon: <HistoryIcon />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t("Download"),
|
title: t("Download"),
|
||||||
onClick: document.download,
|
onClick: document.download,
|
||||||
visible: !!can.download,
|
visible: !!can.download,
|
||||||
|
icon: <DownloadIcon />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t("Print"),
|
title: t("Print"),
|
||||||
onClick: handlePrint,
|
onClick: handlePrint,
|
||||||
visible: !!showPrint,
|
visible: !!showPrint,
|
||||||
|
icon: <PrintIcon />,
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
|
@ -11,7 +11,6 @@ import CollectionIcon from "components/CollectionIcon";
|
|||||||
import ContextMenu from "components/ContextMenu";
|
import ContextMenu from "components/ContextMenu";
|
||||||
import Header from "components/ContextMenu/Header";
|
import Header from "components/ContextMenu/Header";
|
||||||
import Template from "components/ContextMenu/Template";
|
import Template from "components/ContextMenu/Template";
|
||||||
import Flex from "components/Flex";
|
|
||||||
import useCurrentTeam from "hooks/useCurrentTeam";
|
import useCurrentTeam from "hooks/useCurrentTeam";
|
||||||
import useStores from "hooks/useStores";
|
import useStores from "hooks/useStores";
|
||||||
import { newDocumentUrl } from "utils/routeHelpers";
|
import { newDocumentUrl } from "utils/routeHelpers";
|
||||||
@ -31,12 +30,8 @@ function NewDocumentMenu() {
|
|||||||
if (can.update) {
|
if (can.update) {
|
||||||
filtered.push({
|
filtered.push({
|
||||||
to: newDocumentUrl(collection.id),
|
to: newDocumentUrl(collection.id),
|
||||||
title: (
|
title: <CollectionName>{collection.name}</CollectionName>,
|
||||||
<Flex align="center">
|
icon: <CollectionIcon collection={collection} />,
|
||||||
<CollectionIcon collection={collection} />
|
|
||||||
<CollectionName>{collection.name}</CollectionName>
|
|
||||||
</Flex>
|
|
||||||
),
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return filtered;
|
return filtered;
|
||||||
|
@ -3,14 +3,13 @@ import { observer } from "mobx-react";
|
|||||||
import { PlusIcon } from "outline-icons";
|
import { PlusIcon } from "outline-icons";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { useMenuState, MenuButton } from "reakit/Menu";
|
import { MenuButton, useMenuState } from "reakit/Menu";
|
||||||
import styled from "styled-components";
|
import styled from "styled-components";
|
||||||
import Button from "components/Button";
|
import Button from "components/Button";
|
||||||
import CollectionIcon from "components/CollectionIcon";
|
import CollectionIcon from "components/CollectionIcon";
|
||||||
import ContextMenu from "components/ContextMenu";
|
import ContextMenu from "components/ContextMenu";
|
||||||
import Header from "components/ContextMenu/Header";
|
import Header from "components/ContextMenu/Header";
|
||||||
import Template from "components/ContextMenu/Template";
|
import Template from "components/ContextMenu/Template";
|
||||||
import Flex from "components/Flex";
|
|
||||||
import useCurrentTeam from "hooks/useCurrentTeam";
|
import useCurrentTeam from "hooks/useCurrentTeam";
|
||||||
import useStores from "hooks/useStores";
|
import useStores from "hooks/useStores";
|
||||||
import { newDocumentUrl } from "utils/routeHelpers";
|
import { newDocumentUrl } from "utils/routeHelpers";
|
||||||
@ -29,12 +28,8 @@ function NewTemplateMenu() {
|
|||||||
if (can.update) {
|
if (can.update) {
|
||||||
filtered.push({
|
filtered.push({
|
||||||
to: newDocumentUrl(collection.id, { template: true }),
|
to: newDocumentUrl(collection.id, { template: true }),
|
||||||
title: (
|
title: <CollectionName>{collection.name}</CollectionName>,
|
||||||
<Flex align="center">
|
icon: <CollectionIcon collection={collection} />,
|
||||||
<CollectionIcon collection={collection} />
|
|
||||||
<CollectionName>{collection.name}</CollectionName>
|
|
||||||
</Flex>
|
|
||||||
),
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return filtered;
|
return filtered;
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
// @flow
|
// @flow
|
||||||
import { observer } from "mobx-react";
|
import { observer } from "mobx-react";
|
||||||
|
import { RestoreIcon, LinkIcon } from "outline-icons";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { useHistory } from "react-router-dom";
|
import { useHistory } from "react-router-dom";
|
||||||
@ -10,6 +11,7 @@ import MenuItem from "components/ContextMenu/MenuItem";
|
|||||||
import OverflowMenuButton from "components/ContextMenu/OverflowMenuButton";
|
import OverflowMenuButton from "components/ContextMenu/OverflowMenuButton";
|
||||||
import Separator from "components/ContextMenu/Separator";
|
import Separator from "components/ContextMenu/Separator";
|
||||||
import CopyToClipboard from "components/CopyToClipboard";
|
import CopyToClipboard from "components/CopyToClipboard";
|
||||||
|
import MenuIconWrapper from "components/MenuIconWrapper";
|
||||||
import useToasts from "hooks/useToasts";
|
import useToasts from "hooks/useToasts";
|
||||||
import { documentHistoryUrl } from "utils/routeHelpers";
|
import { documentHistoryUrl } from "utils/routeHelpers";
|
||||||
|
|
||||||
@ -54,11 +56,19 @@ function RevisionMenu({ document, revisionId, className }: Props) {
|
|||||||
/>
|
/>
|
||||||
<ContextMenu {...menu} aria-label={t("Revision options")}>
|
<ContextMenu {...menu} aria-label={t("Revision options")}>
|
||||||
<MenuItem {...menu} onClick={handleRestore}>
|
<MenuItem {...menu} onClick={handleRestore}>
|
||||||
|
<MenuIconWrapper>
|
||||||
|
<RestoreIcon />
|
||||||
|
</MenuIconWrapper>
|
||||||
{t("Restore version")}
|
{t("Restore version")}
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
<Separator />
|
<Separator />
|
||||||
<CopyToClipboard text={url} onCopy={handleCopy}>
|
<CopyToClipboard text={url} onCopy={handleCopy}>
|
||||||
<MenuItem {...menu}>{t("Copy link")}</MenuItem>
|
<MenuItem {...menu}>
|
||||||
|
<MenuIconWrapper>
|
||||||
|
<LinkIcon />
|
||||||
|
</MenuIconWrapper>
|
||||||
|
{t("Copy link")}
|
||||||
|
</MenuItem>
|
||||||
</CopyToClipboard>
|
</CopyToClipboard>
|
||||||
</ContextMenu>
|
</ContextMenu>
|
||||||
</>
|
</>
|
||||||
|
@ -37,9 +37,9 @@ function TemplatesMenu({ document }: Props) {
|
|||||||
<MenuItem
|
<MenuItem
|
||||||
key={template.id}
|
key={template.id}
|
||||||
onClick={() => document.updateFromTemplate(template)}
|
onClick={() => document.updateFromTemplate(template)}
|
||||||
|
icon={<DocumentIcon />}
|
||||||
{...menu}
|
{...menu}
|
||||||
>
|
>
|
||||||
<DocumentIcon />
|
|
||||||
<TemplateItem>
|
<TemplateItem>
|
||||||
<strong>{template.titleWithDefault}</strong>
|
<strong>{template.titleWithDefault}</strong>
|
||||||
<br />
|
<br />
|
||||||
|
@ -66,6 +66,7 @@ export type MenuItem =
|
|||||||
visible?: boolean,
|
visible?: boolean,
|
||||||
selected?: boolean,
|
selected?: boolean,
|
||||||
disabled?: boolean,
|
disabled?: boolean,
|
||||||
|
icon?: React.Node,
|
||||||
|}
|
|}
|
||||||
| {|
|
| {|
|
||||||
title: React.Node,
|
title: React.Node,
|
||||||
@ -73,6 +74,7 @@ export type MenuItem =
|
|||||||
visible?: boolean,
|
visible?: boolean,
|
||||||
selected?: boolean,
|
selected?: boolean,
|
||||||
disabled?: boolean,
|
disabled?: boolean,
|
||||||
|
icon?: React.Node,
|
||||||
|}
|
|}
|
||||||
| {|
|
| {|
|
||||||
title: React.Node,
|
title: React.Node,
|
||||||
@ -81,6 +83,7 @@ export type MenuItem =
|
|||||||
selected?: boolean,
|
selected?: boolean,
|
||||||
disabled?: boolean,
|
disabled?: boolean,
|
||||||
level?: number,
|
level?: number,
|
||||||
|
icon?: React.Node,
|
||||||
|}
|
|}
|
||||||
| {|
|
| {|
|
||||||
title: React.Node,
|
title: React.Node,
|
||||||
@ -89,6 +92,7 @@ export type MenuItem =
|
|||||||
style?: Object,
|
style?: Object,
|
||||||
hover?: boolean,
|
hover?: boolean,
|
||||||
items: MenuItem[],
|
items: MenuItem[],
|
||||||
|
icon?: React.Node,
|
||||||
|}
|
|}
|
||||||
| {|
|
| {|
|
||||||
type: "separator",
|
type: "separator",
|
||||||
|
@ -167,14 +167,14 @@
|
|||||||
"Previous page": "Previous page",
|
"Previous page": "Previous page",
|
||||||
"Next page": "Next page",
|
"Next page": "Next page",
|
||||||
"Could not import file": "Could not import file",
|
"Could not import file": "Could not import file",
|
||||||
"Appearance": "Appearance",
|
|
||||||
"System": "System",
|
|
||||||
"Light": "Light",
|
|
||||||
"Dark": "Dark",
|
|
||||||
"API documentation": "API documentation",
|
"API documentation": "API documentation",
|
||||||
"Changelog": "Changelog",
|
"Changelog": "Changelog",
|
||||||
"Send us feedback": "Send us feedback",
|
"Send us feedback": "Send us feedback",
|
||||||
"Report a bug": "Report a bug",
|
"Report a bug": "Report a bug",
|
||||||
|
"Appearance": "Appearance",
|
||||||
|
"System": "System",
|
||||||
|
"Light": "Light",
|
||||||
|
"Dark": "Dark",
|
||||||
"Log out": "Log out",
|
"Log out": "Log out",
|
||||||
"Show path to document": "Show path to document",
|
"Show path to document": "Show path to document",
|
||||||
"Path to document": "Path to document",
|
"Path to document": "Path to document",
|
||||||
|
@ -10,7 +10,7 @@ const colors = {
|
|||||||
|
|
||||||
slate: "#9BA6B2",
|
slate: "#9BA6B2",
|
||||||
slateLight: "#DAE1E9",
|
slateLight: "#DAE1E9",
|
||||||
slateDark: "#4E5C6E",
|
slateDark: "#394351",
|
||||||
|
|
||||||
smoke: "#F4F7FA",
|
smoke: "#F4F7FA",
|
||||||
smokeLight: "#F9FBFC",
|
smokeLight: "#F9FBFC",
|
||||||
|
Reference in New Issue
Block a user