mirror of
https://github.com/outline/outline.git
synced 2026-06-13 11:25:03 +03:00
Add option to choose default TOC visibility on public shares (#10283)
* Add show TOC option * Revert copy change
This commit is contained in:
@@ -71,6 +71,19 @@ function InnerPublicAccess({ collection, share }: Props) {
|
||||
[share]
|
||||
);
|
||||
|
||||
const handleShowTOCChanged = useCallback(
|
||||
async (checked: boolean) => {
|
||||
try {
|
||||
await share?.save({
|
||||
showTOC: checked,
|
||||
});
|
||||
} catch (err) {
|
||||
toast.error(err.message);
|
||||
}
|
||||
},
|
||||
[share]
|
||||
);
|
||||
|
||||
const handlePublishedChange = useCallback(
|
||||
async (checked: boolean) => {
|
||||
try {
|
||||
@@ -204,6 +217,31 @@ function InnerPublicAccess({ collection, share }: Props) {
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<ListItem
|
||||
title={
|
||||
<Text type="tertiary" as={Flex}>
|
||||
{t("Show table of contents")}
|
||||
<Tooltip
|
||||
content={t(
|
||||
"Display the table of contents on documents by default"
|
||||
)}
|
||||
>
|
||||
<NudeButton size={18}>
|
||||
<QuestionMarkIcon size={18} />
|
||||
</NudeButton>
|
||||
</Tooltip>
|
||||
</Text>
|
||||
}
|
||||
actions={
|
||||
<Switch
|
||||
aria-label={t("Show table of contents")}
|
||||
checked={share?.showTOC ?? false}
|
||||
onChange={handleShowTOCChanged}
|
||||
width={26}
|
||||
height={14}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<ShareLinkInput
|
||||
type="text"
|
||||
ref={inputRef}
|
||||
|
||||
@@ -77,6 +77,19 @@ function PublicAccess({ document, share, sharedParent }: Props) {
|
||||
[share]
|
||||
);
|
||||
|
||||
const handleShowTOCChanged = React.useCallback(
|
||||
async (checked: boolean) => {
|
||||
try {
|
||||
await share?.save({
|
||||
showTOC: checked,
|
||||
});
|
||||
} catch (err) {
|
||||
toast.error(err.message);
|
||||
}
|
||||
},
|
||||
[share]
|
||||
);
|
||||
|
||||
const handlePublishedChange = React.useCallback(
|
||||
async (checked: boolean) => {
|
||||
try {
|
||||
@@ -241,6 +254,31 @@ function PublicAccess({ document, share, sharedParent }: Props) {
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<ListItem
|
||||
title={
|
||||
<Text type="tertiary" as={Flex}>
|
||||
{t("Show table of contents")}
|
||||
<Tooltip
|
||||
content={t(
|
||||
"Display the table of contents on documents by default"
|
||||
)}
|
||||
>
|
||||
<NudeButton size={18}>
|
||||
<QuestionMarkIcon size={18} />
|
||||
</NudeButton>
|
||||
</Tooltip>
|
||||
</Text>
|
||||
}
|
||||
actions={
|
||||
<Switch
|
||||
aria-label={t("Show table of contents")}
|
||||
checked={share?.showTOC ?? false}
|
||||
onChange={handleShowTOCChanged}
|
||||
width={26}
|
||||
height={14}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ import { SharedCollectionLink } from "./components/SharedCollectionLink";
|
||||
import { SharedDocumentLink } from "./components/SharedDocumentLink";
|
||||
import SidebarButton from "./components/SidebarButton";
|
||||
import ToggleButton from "./components/ToggleButton";
|
||||
import { useEffect } from "react";
|
||||
|
||||
type Props = {
|
||||
share: Share;
|
||||
@@ -37,6 +38,10 @@ function SharedSidebar({ share }: Props) {
|
||||
const rootNode = share.tree;
|
||||
const shareId = share.urlId || share.id;
|
||||
|
||||
useEffect(() => {
|
||||
ui.tocVisible = share.showTOC;
|
||||
}, []);
|
||||
|
||||
if (!rootNode?.children.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -75,6 +75,10 @@ class Share extends Model implements Searchable {
|
||||
@observable
|
||||
showLastUpdated: boolean;
|
||||
|
||||
@Field
|
||||
@observable
|
||||
showTOC: boolean;
|
||||
|
||||
@observable
|
||||
views: number;
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import { useTranslation } from "react-i18next";
|
||||
import styled from "styled-components";
|
||||
import breakpoint from "styled-components-breakpoint";
|
||||
import { EditorStyleHelper } from "@shared/editor/styles/EditorStyleHelper";
|
||||
import { depths, s } from "@shared/styles";
|
||||
import { depths, hideScrollbars, s } from "@shared/styles";
|
||||
import { useDocumentContext } from "~/components/DocumentContext";
|
||||
import useWindowScrollPosition from "~/hooks/useWindowScrollPosition";
|
||||
import { decodeURIComponentSafe } from "~/utils/urls";
|
||||
@@ -78,16 +78,16 @@ function Contents() {
|
||||
|
||||
const StickyWrapper = styled.div`
|
||||
display: none;
|
||||
|
||||
position: sticky;
|
||||
top: 90px;
|
||||
max-height: calc(100vh - 90px);
|
||||
width: ${EditorStyleHelper.tocWidth}px;
|
||||
|
||||
${hideScrollbars()}
|
||||
|
||||
padding: 0 16px;
|
||||
overflow-y: auto;
|
||||
border-radius: 8px;
|
||||
|
||||
background: ${s("background")};
|
||||
|
||||
@supports (backdrop-filter: blur(20px)) {
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
"use strict";
|
||||
|
||||
module.exports = {
|
||||
async up(queryInterface, Sequelize) {
|
||||
await queryInterface.addColumn("shares", "showTOC", {
|
||||
type: Sequelize.BOOLEAN,
|
||||
allowNull: false,
|
||||
defaultValue: false,
|
||||
});
|
||||
},
|
||||
|
||||
async down(queryInterface) {
|
||||
await queryInterface.removeColumn("shares", "showTOC");
|
||||
},
|
||||
};
|
||||
@@ -150,6 +150,10 @@ class Share extends IdModel<
|
||||
@Column
|
||||
showLastUpdated: boolean;
|
||||
|
||||
@Default(false)
|
||||
@Column
|
||||
showTOC: boolean;
|
||||
|
||||
// hooks
|
||||
|
||||
@BeforeUpdate
|
||||
|
||||
@@ -17,6 +17,7 @@ export default function presentShare(share: Share, isAdmin = false) {
|
||||
includeChildDocuments: share.includeChildDocuments,
|
||||
allowIndexing: share.allowIndexing,
|
||||
showLastUpdated: share.showLastUpdated,
|
||||
showTOC: share.showTOC,
|
||||
lastAccessedAt: share.lastAccessedAt || undefined,
|
||||
views: share.views || 0,
|
||||
domain: share.domain,
|
||||
|
||||
@@ -54,6 +54,7 @@ export const SharesUpdateSchema = BaseSchema.extend({
|
||||
published: z.boolean().optional(),
|
||||
allowIndexing: z.boolean().optional(),
|
||||
showLastUpdated: z.boolean().optional(),
|
||||
showTOC: z.boolean().optional(),
|
||||
urlId: z
|
||||
.string()
|
||||
.regex(UrlHelper.SHARE_URL_SLUG_REGEX, {
|
||||
@@ -73,6 +74,7 @@ export const SharesCreateSchema = BaseSchema.extend({
|
||||
published: z.boolean().default(false),
|
||||
allowIndexing: z.boolean().optional(),
|
||||
showLastUpdated: z.boolean().optional(),
|
||||
showTOC: z.boolean().optional(),
|
||||
urlId: z
|
||||
.string()
|
||||
.regex(UrlHelper.SHARE_URL_SLUG_REGEX, {
|
||||
|
||||
@@ -238,6 +238,7 @@ router.post(
|
||||
includeChildDocuments,
|
||||
allowIndexing,
|
||||
showLastUpdated,
|
||||
showTOC,
|
||||
} = ctx.input.body;
|
||||
const { user } = ctx.state.auth;
|
||||
authorize(user, "createShare", user.team);
|
||||
@@ -274,6 +275,7 @@ router.post(
|
||||
includeChildDocuments,
|
||||
allowIndexing,
|
||||
showLastUpdated,
|
||||
showTOC,
|
||||
urlId,
|
||||
},
|
||||
});
|
||||
@@ -303,6 +305,7 @@ router.post(
|
||||
urlId,
|
||||
allowIndexing,
|
||||
showLastUpdated,
|
||||
showTOC,
|
||||
} = ctx.input.body;
|
||||
|
||||
const { user } = ctx.state.auth;
|
||||
@@ -333,10 +336,12 @@ router.post(
|
||||
if (allowIndexing !== undefined) {
|
||||
share.allowIndexing = allowIndexing;
|
||||
}
|
||||
|
||||
if (showLastUpdated !== undefined) {
|
||||
share.showLastUpdated = showLastUpdated;
|
||||
}
|
||||
if (showTOC !== undefined) {
|
||||
share.showTOC = showTOC;
|
||||
}
|
||||
|
||||
await share.saveWithCtx(ctx);
|
||||
|
||||
|
||||
@@ -365,6 +365,8 @@
|
||||
"Disable this setting to discourage search engines from indexing the page": "Disable this setting to discourage search engines from indexing the page",
|
||||
"Show last modified": "Show last modified",
|
||||
"Display the last modified timestamp on the shared page": "Display the last modified timestamp on the shared page",
|
||||
"Show table of contents": "Show table of contents",
|
||||
"Display the table of contents on documents by default": "Display the table of contents on documents by default",
|
||||
"All documents in this collection will be shared on the web, including any new documents added later": "All documents in this collection will be shared on the web, including any new documents added later",
|
||||
"Invite": "Invite",
|
||||
"{{ userName }} was added to the collection": "{{ userName }} was added to the collection",
|
||||
|
||||
Reference in New Issue
Block a user