Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: allow user to set TOC display preference #6786

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
9 changes: 5 additions & 4 deletions app/scenes/Document/components/Contents.tsx
Expand Up @@ -18,9 +18,10 @@ type Props = {
level: number;
id: string;
}[];
onLeftSide: boolean;
};

export default function Contents({ headings, isFullWidth }: Props) {
export default function Contents({ headings, isFullWidth, onLeftSide }: Props) {
const [activeSlug, setActiveSlug] = React.useState<string>();
const position = useWindowScrollPosition({
throttle: 100,
Expand Down Expand Up @@ -56,7 +57,7 @@ export default function Contents({ headings, isFullWidth }: Props) {
const { t } = useTranslation();

return (
<Wrapper isFullWidth={isFullWidth}>
<Wrapper isFullWidth={isFullWidth} onLeftSide={onLeftSide}>
<Sticky>
<Heading>{t("Contents")}</Heading>
{headings.length ? (
Expand All @@ -83,7 +84,7 @@ export default function Contents({ headings, isFullWidth }: Props) {
);
}

const Wrapper = styled.div<{ isFullWidth: boolean }>`
const Wrapper = styled.div<{ isFullWidth: boolean; onLeftSide: boolean }>`
width: 256px;
display: none;

Expand All @@ -94,7 +95,7 @@ const Wrapper = styled.div<{ isFullWidth: boolean }>`
${(props) =>
!props.isFullWidth &&
breakpoint("desktopLarge")`
transform: translateX(-256px);
transform: ${props.onLeftSide && "translateX(-256px)"};
width: 0;
`}
`;
Expand Down
7 changes: 5 additions & 2 deletions app/scenes/Document/components/Document.tsx
Expand Up @@ -15,7 +15,7 @@ import { toast } from "sonner";
import styled from "styled-components";
import breakpoint from "styled-components-breakpoint";
import { s } from "@shared/styles";
import { NavigationNode } from "@shared/types";
import { NavigationNode, TOCPosition, TeamPreference } from "@shared/types";
import { Heading } from "@shared/utils/ProsemirrorHelper";
import { parseDomain } from "@shared/utils/domains";
import getTasks from "@shared/utils/getTasks";
Expand Down Expand Up @@ -401,6 +401,8 @@ class DocumentScene extends React.Component<Props> {
const hasHeadings = this.headings.length > 0;
const showContents =
ui.tocVisible && ((readOnly && hasHeadings) || !readOnly);
const tocOnLeftSide =
team?.getPreference(TeamPreference.TocPosition) === TOCPosition.Left;
const multiplayerEditor =
!document.isArchived && !document.isDeleted && !revision && !isShare;

Expand Down Expand Up @@ -480,7 +482,7 @@ class DocumentScene extends React.Component<Props> {
>
<Notices document={document} readOnly={readOnly} />
<React.Suspense fallback={<PlaceholderDocument />}>
<Flex auto={!readOnly} reverse>
<Flex auto={!readOnly} reverse={tocOnLeftSide}>
{revision ? (
<RevisionViewer
document={document}
Expand Down Expand Up @@ -540,6 +542,7 @@ class DocumentScene extends React.Component<Props> {
<Contents
headings={this.headings}
isFullWidth={document.fullWidth}
onLeftSide={tocOnLeftSide}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
onLeftSide={tocOnLeftSide}
position={position}

Lets continue to pass position down

/>
)}
</>
Expand Down
33 changes: 32 additions & 1 deletion app/scenes/Settings/Details.tsx
Expand Up @@ -8,14 +8,15 @@ import { useTranslation, Trans } from "react-i18next";
import { toast } from "sonner";
import { ThemeProvider, useTheme } from "styled-components";
import { buildDarkTheme, buildLightTheme } from "@shared/styles/theme";
import { CustomTheme, TeamPreference } from "@shared/types";
import { CustomTheme, TOCPosition, TeamPreference } from "@shared/types";
import { getBaseDomain } from "@shared/utils/domains";
import Button from "~/components/Button";
import ButtonLink from "~/components/ButtonLink";
import DefaultCollectionInputSelect from "~/components/DefaultCollectionInputSelect";
import Heading from "~/components/Heading";
import Input from "~/components/Input";
import InputColor from "~/components/InputColor";
import InputSelect from "~/components/InputSelect";
import Scene from "~/components/Scene";
import Switch from "~/components/Switch";
import Text from "~/components/Text";
Expand Down Expand Up @@ -58,6 +59,10 @@ function Details() {
isHexColor
);

const [tocPosition, setTocPosition] = useState(
team.getPreference(TeamPreference.TocPosition) as TOCPosition
);

const handleSubmit = React.useCallback(
async (event?: React.SyntheticEvent) => {
if (event) {
Expand All @@ -73,6 +78,7 @@ function Details() {
...team.preferences,
publicBranding,
customTheme,
tocPosition,
},
});
toast.success(t("Settings saved"));
Expand Down Expand Up @@ -276,6 +282,31 @@ function Details() {
/>
</SettingRow>

<SettingRow
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lets move this under the "Display" header, it's currently under "Behavior"

border={false}
label={t("Table of Contents Position")}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
label={t("Table of Contents Position")}
label={t("Table of Contents position")}

name="tocPosition"
description={t(
"The side to display the table of contents in relation to the main content."
)}
>
<InputSelect
ariaLabel={t("TOC position")}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
ariaLabel={t("TOC position")}
ariaLabel={t("Table of Contents position")}

options={[
{
label: "Left",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
label: "Left",
label: t("Left"),

value: TOCPosition.Left,
},
{
label: "Right",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
label: "Right",
label: t("Right"),

value: TOCPosition.Right,
},
]}
value={tocPosition}
onChange={(p: TOCPosition) => setTocPosition(p)}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
onChange={(p: TOCPosition) => setTocPosition(p)}
onChange={setTocPosition}

Should work, no? Or is typescript forcing the intermediate function

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, Typescript 😅 .

/>
</SettingRow>

<Button type="submit" disabled={team.isSaving || !isValid}>
{team.isSaving ? `${t("Saving")}…` : t("Save")}
</Button>
Expand Down
4 changes: 3 additions & 1 deletion server/routes/api/teams/schema.ts
@@ -1,5 +1,5 @@
import { z } from "zod";
import { UserRole } from "@shared/types";
import { TOCPosition, UserRole } from "@shared/types";
import { BaseSchema } from "@server/routes/api/schema";

export const TeamsUpdateSchema = BaseSchema.extend({
Expand Down Expand Up @@ -46,6 +46,8 @@ export const TeamsUpdateSchema = BaseSchema.extend({
accentText: z.string().min(4).max(7).regex(/^#/).optional(),
})
.optional(),
/** Side to display the document's table of contents in relation to the main content. */
tocPosition: z.nativeEnum(TOCPosition).optional(),
})
.optional(),
}),
Expand Down
2 changes: 2 additions & 0 deletions shared/constants.ts
@@ -1,4 +1,5 @@
import {
TOCPosition,
TeamPreference,
TeamPreferences,
UserPreference,
Expand All @@ -21,6 +22,7 @@ export const TeamPreferenceDefaults: TeamPreferences = {
[TeamPreference.PublicBranding]: false,
[TeamPreference.Commenting]: true,
[TeamPreference.CustomTheme]: undefined,
[TeamPreference.TocPosition]: TOCPosition.Left,
};

export const UserPreferenceDefaults: UserPreferences = {
Expand Down
3 changes: 3 additions & 0 deletions shared/i18n/locales/en_US/translation.json
Expand Up @@ -834,6 +834,9 @@
"Choose a subdomain to enable a login page just for your team.": "Choose a subdomain to enable a login page just for your team.",
"Start view": "Start view",
"This is the screen that workspace members will first see when they sign in.": "This is the screen that workspace members will first see when they sign in.",
"Table of Contents Position": "Table of Contents Position",
"The side to display the table of contents in relation to the main content.": "The side to display the table of contents in relation to the main content.",
"TOC position": "TOC position",
"Danger": "Danger",
"You can delete this entire workspace including collections, documents, and users.": "You can delete this entire workspace including collections, documents, and users.",
"Export data": "Export data",
Expand Down
8 changes: 8 additions & 0 deletions shared/types.ts
Expand Up @@ -180,6 +180,11 @@ export type PublicTeam = {
customTheme: Partial<CustomTheme>;
};

export enum TOCPosition {
Left = "left",
Right = "right",
}

export enum TeamPreference {
/** Whether documents have a separate edit mode instead of always editing. */
SeamlessEdit = "seamlessEdit",
Expand All @@ -193,6 +198,8 @@ export enum TeamPreference {
Commenting = "commenting",
/** The custom theme for the team. */
CustomTheme = "customTheme",
/** Side to display the document's table of contents in relation to the main content. */
TocPosition = "tocPosition",
}

export type TeamPreferences = {
Expand All @@ -202,6 +209,7 @@ export type TeamPreferences = {
[TeamPreference.MembersCanInvite]?: boolean;
[TeamPreference.Commenting]?: boolean;
[TeamPreference.CustomTheme]?: Partial<CustomTheme>;
[TeamPreference.TocPosition]?: TOCPosition;
};

export enum NavigationNodeType {
Expand Down