fix: Add access to document TOC on mobile (#2279)

* Add TOC button for mobile

* Undo NewDocumentMenu changes

* Place the toc button in the correct position.

* Pass menu props to menuitem

* Update app/menus/TableOfContentsMenu.js

Co-authored-by: Tom Moor <tom.moor@gmail.com>

* Update app/menus/TableOfContentsMenu.js

Co-authored-by: Tom Moor <tom.moor@gmail.com>

* Use the existing prop type

* Write menu inside actions prop

* Prevent blank webpage behaviour for toc

* Use href instead of level to determine target

* Update app/scenes/Document/components/Header.js

Co-authored-by: Tom Moor <tom.moor@gmail.com>

* Add heading to menu items

* Use existing Heading component

Co-authored-by: Tom Moor <tom.moor@gmail.com>
This commit is contained in:
Saumya Pandey
2021-07-09 17:20:27 +05:30
committed by GitHub
parent 056f89fcfd
commit 7d5fbeb7b0
6 changed files with 91 additions and 2 deletions

View File

@ -15,6 +15,7 @@ type Props = {|
target?: "_blank",
as?: string | React.ComponentType<*>,
hide?: () => void,
level?: number,
|};
const MenuItem = ({
@ -86,6 +87,7 @@ const Spacer = styled.svg`
export const MenuAnchor = styled.a`
display: flex;
margin: 0;
margin-left: ${(props) => props.level * 10}px;
border: 0;
padding: 12px;
width: 100%;

View File

@ -9,6 +9,7 @@ import {
MenuItem as BaseMenuItem,
} from "reakit/Menu";
import styled from "styled-components";
import Header from "./Header";
import MenuItem, { MenuAnchor } from "./MenuItem";
import Separator from "./Separator";
import ContextMenu from ".";
@ -34,6 +35,7 @@ type TMenuItem =
visible?: boolean,
selected?: boolean,
disabled?: boolean,
level?: number,
|}
| {|
title: React.Node,
@ -128,7 +130,8 @@ function Template({ items, ...menu }: Props): React.Node {
key={index}
disabled={item.disabled}
selected={item.selected}
target="_blank"
level={item.level}
target={item.href.startsWith("#") ? undefined : "_blank"}
{...menu}
>
{item.title}
@ -167,6 +170,10 @@ function Template({ items, ...menu }: Props): React.Node {
return <Separator key={index} />;
}
if (item.type === "heading") {
return <Header>{item.title}</Header>;
}
return null;
});
}

View File

@ -0,0 +1,66 @@
// @flow
import { observer } from "mobx-react";
import { TableOfContentsIcon } from "outline-icons";
import * as React from "react";
import { useTranslation } from "react-i18next";
import { MenuButton, useMenuState } from "reakit/Menu";
import Button from "components/Button";
import ContextMenu from "components/ContextMenu";
import Template from "components/ContextMenu/Template";
type Props = {|
headings: { title: string, level: number, id: string }[],
|};
function TableOfContentsMenu({ headings }: Props) {
const menu = useMenuState({
modal: true,
unstable_preventOverflow: true,
unstable_fixed: true,
unstable_flip: true,
});
const { t } = useTranslation();
const minHeading = headings.reduce(
(memo, heading) => (heading.level < memo ? heading.level : memo),
Infinity
);
return (
<>
<MenuButton {...menu}>
{(props) => (
<Button
{...props}
icon={<TableOfContentsIcon />}
iconColor="currentColor"
borderOnHover
neutral
/>
)}
</MenuButton>
<ContextMenu {...menu} aria-label={t("Table of contents")}>
<Template
{...menu}
items={[
{
type: "heading",
visible: true,
title: t("Contents"),
},
...headings.map((heading) => {
return {
href: `#${heading.id}`,
title: t(heading.title),
level: heading.level - minHeading,
};
}),
]}
/>
</ContextMenu>
</>
);
}
export default observer(TableOfContentsMenu);

View File

@ -374,6 +374,7 @@ class DocumentScene extends React.Component<Props> {
sharedTree={this.props.sharedTree}
goBack={this.goBack}
onSave={this.onSave}
headings={headings}
/>
<MaxWidth
archived={document.isArchived}

View File

@ -24,6 +24,7 @@ import useMobile from "hooks/useMobile";
import useStores from "hooks/useStores";
import DocumentMenu from "menus/DocumentMenu";
import NewChildDocumentMenu from "menus/NewChildDocumentMenu";
import TableOfContentsMenu from "menus/TableOfContentsMenu";
import TemplatesMenu from "menus/TemplatesMenu";
import { type NavigationNode } from "types";
import { metaDisplay } from "utils/keyboard";
@ -46,6 +47,7 @@ type Props = {|
publish?: boolean,
autosave?: boolean,
}) => void,
headings: { title: string, level: number, id: string }[],
|};
function DocumentHeader({
@ -60,6 +62,7 @@ function DocumentHeader({
publishingIsDisabled,
sharedTree,
onSave,
headings,
}: Props) {
const { t } = useTranslation();
const { auth, ui, policies } = useStores();
@ -153,6 +156,11 @@ function DocumentHeader({
}
actions={
<>
{isMobile && (
<TocWrapper>
<TableOfContentsMenu headings={headings} />
</TocWrapper>
)}
{!isPublishing && isSaving && <Status>{t("Saving")}</Status>}
<Collaborators
document={document}
@ -274,4 +282,9 @@ const Status = styled(Action)`
color: ${(props) => props.theme.slate};
`;
const TocWrapper = styled(Action)`
position: absolute;
left: 42px;
`;
export default observer(DocumentHeader);

View File

@ -206,6 +206,7 @@
"Share options": "Share options",
"Go to document": "Go to document",
"Revoke link": "Revoke link",
"Table of contents": "Table of contents",
"By {{ author }}": "By {{ author }}",
"Are you sure you want to make {{ userName }} an admin? Admins can modify team and billing information.": "Are you sure you want to make {{ userName }} an admin? Admins can modify team and billing information.",
"Are you sure you want to make {{ userName }} a member?": "Are you sure you want to make {{ userName }} a member?",
@ -337,7 +338,6 @@
"Move current document": "Move current document",
"Jump to search": "Jump to search",
"Jump to home": "Jump to home",
"Table of contents": "Table of contents",
"Toggle navigation": "Toggle navigation",
"Focus search input": "Focus search input",
"Open this guide": "Open this guide",