From 38edeca871a5f8bbb20170eeba4923699f14ca2d Mon Sep 17 00:00:00 2001 From: David <33458145+davidmcpowell@users.noreply.github.com> Date: Wed, 29 Oct 2025 09:40:20 +0000 Subject: [PATCH] Merge pull request #29292 from overleaf/dp-dashboard-theme-toggle Add theme toggle to project dashboard GitOrigin-RevId: 4c76bcc36f77d7fd883798f8ccfcb5d1cf1a54b0 --- services/web/app/views/project/list-react.pug | 1 + .../web/frontend/extracted-translations.json | 1 + .../components/sidebar/sidebar-ds-nav.tsx | 4 ++ .../components/sidebar/theme-toggle.tsx | 55 +++++++++++++++++ .../components/navbar/account-menu-items.tsx | 10 ++++ .../pages/project-list-ds-nav.scss | 59 +++++++++++++++++++ 6 files changed, 130 insertions(+) create mode 100644 services/web/frontend/js/features/project-list/components/sidebar/theme-toggle.tsx diff --git a/services/web/app/views/project/list-react.pug b/services/web/app/views/project/list-react.pug index ba29486146..13382f1d13 100644 --- a/services/web/app/views/project/list-react.pug +++ b/services/web/app/views/project/list-react.pug @@ -34,6 +34,7 @@ block append meta meta(name='ol-tags' data-type='json' content=tags) meta(name='ol-portalTemplates' data-type='json' content=portalTemplates) meta(name='ol-userSettings' data-type='json' content=userSettings) + meta(name='ol-overallThemes' data-type='json' content=overallThemes) meta( name='ol-prefetchedProjectsBlob' data-type='json' diff --git a/services/web/frontend/extracted-translations.json b/services/web/frontend/extracted-translations.json index b57e69c976..5ee6b2522b 100644 --- a/services/web/frontend/extracted-translations.json +++ b/services/web/frontend/extracted-translations.json @@ -1857,6 +1857,7 @@ "the_visual_editor_cant_preview_this_type_of_image_file": "", "the_width_you_choose_here_is_based_on_the_width_of_the_text_in_your_document": "", "their_projects_will_be_transferred_to_another_user": "", + "theme": "", "then_x_price_per_month": "", "then_x_price_per_year": "", "there_are_lots_of_options_to_edit_and_customize_your_figures": "", diff --git a/services/web/frontend/js/features/project-list/components/sidebar/sidebar-ds-nav.tsx b/services/web/frontend/js/features/project-list/components/sidebar/sidebar-ds-nav.tsx index e68fa9e6ff..41c169134c 100644 --- a/services/web/frontend/js/features/project-list/components/sidebar/sidebar-ds-nav.tsx +++ b/services/web/frontend/js/features/project-list/components/sidebar/sidebar-ds-nav.tsx @@ -17,6 +17,7 @@ import { AccountMenuItems } from '@/shared/components/navbar/account-menu-items' import { useScrolled } from '@/features/project-list/components/sidebar/use-scroll' import { useSendProjectListMB } from '@/features/project-list/components/project-list-events' import { SurveyWidgetDsNav } from '@/features/project-list/components/survey-widget-ds-nav' +import { useFeatureFlag } from '@/shared/context/split-test-context' function SidebarDsNav() { const { t } = useTranslation() @@ -36,6 +37,8 @@ function SidebarDsNav() { item => item.text === 'help_and_resources' ) as NavbarDropdownItemData const { containerRef, scrolledUp, scrolledDown } = useScrolled() + const themedDsNav = useFeatureFlag('themed-project-dashboard') + return (
diff --git a/services/web/frontend/js/features/project-list/components/sidebar/theme-toggle.tsx b/services/web/frontend/js/features/project-list/components/sidebar/theme-toggle.tsx new file mode 100644 index 0000000000..37c4a3b121 --- /dev/null +++ b/services/web/frontend/js/features/project-list/components/sidebar/theme-toggle.tsx @@ -0,0 +1,55 @@ +import useSetOverallTheme from '@/features/editor-left-menu/hooks/use-set-overall-theme' +import MaterialIcon from '@/shared/components/material-icon' +import OLTooltip from '@/shared/components/ol/ol-tooltip' +import { useUserSettingsContext } from '@/shared/context/user-settings-context' +import getMeta from '@/utils/meta' +import { OverallThemeMeta } from '@ol-types/project-settings' +import { useTranslation } from 'react-i18next' + +const getIcon = (theme: OverallThemeMeta) => { + switch (theme.val) { + case 'light-': + return 'light_mode' + case 'system': + return 'computer' + default: + return 'dark_mode' + } +} + +export default function ThemeToggle() { + const { + userSettings: { overallTheme }, + } = useUserSettingsContext() + const setOverallTheme = useSetOverallTheme() + const overallThemes = getMeta('ol-overallThemes') + const { t } = useTranslation() + + return ( +
+ {t('theme')} +
+ {overallThemes.map(theme => ( + +
+ setOverallTheme(theme.val)} + /> + +
+
+ ))} +
+
+ ) +} diff --git a/services/web/frontend/js/shared/components/navbar/account-menu-items.tsx b/services/web/frontend/js/shared/components/navbar/account-menu-items.tsx index 5ca88f3683..93a2e89643 100644 --- a/services/web/frontend/js/shared/components/navbar/account-menu-items.tsx +++ b/services/web/frontend/js/shared/components/navbar/account-menu-items.tsx @@ -7,17 +7,21 @@ import NavDropdownDivider from './nav-dropdown-divider' import NavDropdownLinkItem from './nav-dropdown-link-item' import { useDsNavStyle } from '@/features/project-list/components/use-is-ds-nav' import { SignOut } from '@phosphor-icons/react' +import ThemeToggle from '@/features/project-list/components/sidebar/theme-toggle' export function AccountMenuItems({ sessionUser, showSubscriptionLink, + showThemeToggle = false, }: { sessionUser: NavbarSessionUser showSubscriptionLink: boolean + showThemeToggle?: boolean }) { const { t } = useTranslation() const logOutFormId = 'logOutForm' const dsNavStyle = useDsNavStyle() + return ( <> @@ -32,6 +36,12 @@ export function AccountMenuItems({ {t('subscription')} ) : null} + {showThemeToggle && ( + + + + )} + { diff --git a/services/web/frontend/stylesheets/pages/project-list-ds-nav.scss b/services/web/frontend/stylesheets/pages/project-list-ds-nav.scss index 7848f1e7f7..472ec1d2cb 100644 --- a/services/web/frontend/stylesheets/pages/project-list-ds-nav.scss +++ b/services/web/frontend/stylesheets/pages/project-list-ds-nav.scss @@ -337,3 +337,62 @@ } } } + +.theme-toggle { + display: flex; + justify-content: space-between; + cursor: default !important; + align-items: center; + + &:hover { + background-color: transparent !important; + color: var(--content-primary-themed); + } + + legend { + font-size: var(--font-size-02); + line-height: var(--line-height-02); + margin-bottom: 0; + } +} + +.theme-toggle-radios { + display: flex; + border-radius: var(--border-radius-full); + background-color: var(--bg-secondary-themed); + padding: var(--spacing-01); + gap: var(--spacing-01); +} + +:root { + --theme-toggle-selected-background: var(--green-70); +} + +@include theme('light') { + --theme-toggle-selected-background: var(--green-20); +} + +.theme-toggle-radio { + display: flex; + + input { + all: unset; + } + + label { + display: flex; + padding: var(--spacing-03); + margin-bottom: 0; + border-radius: var(--border-radius-full); + cursor: pointer; + color: var(--content-primary-themed); + } + + .material-symbols { + font-size: var(--font-size-03); + } + + input:checked + label { + background-color: var(--theme-toggle-selected-background); + } +}