Merge pull request #29292 from overleaf/dp-dashboard-theme-toggle

Add theme toggle to project dashboard

GitOrigin-RevId: 4c76bcc36f77d7fd883798f8ccfcb5d1cf1a54b0
This commit is contained in:
David
2025-10-29 09:40:20 +00:00
committed by Copybot
parent fea346f4a6
commit 38edeca871
6 changed files with 130 additions and 0 deletions

View File

@@ -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'

View File

@@ -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": "",

View File

@@ -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 (
<div
className="project-list-sidebar-wrapper-react d-none d-md-flex"
@@ -159,6 +162,7 @@ function SidebarDsNav() {
<AccountMenuItems
sessionUser={sessionUser}
showSubscriptionLink={showSubscriptionLink}
showThemeToggle={themedDsNav}
/>
</Dropdown.Menu>
</Dropdown>

View File

@@ -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 (
<fieldset className="dropdown-item theme-toggle">
<legend>{t('theme')}</legend>
<div className="theme-toggle-radios">
{overallThemes.map(theme => (
<OLTooltip
key={theme.val}
description={theme.name}
id={`theme-switch-${theme.name}-tooltip`}
>
<div className="theme-toggle-radio">
<input
id={`theme-switch-${theme.name}`}
type="radio"
value={theme.val}
checked={overallTheme === theme.val}
onChange={() => setOverallTheme(theme.val)}
/>
<label htmlFor={`theme-switch-${theme.name}`}>
<MaterialIcon type={getIcon(theme)} />
</label>
</div>
</OLTooltip>
))}
</div>
</fieldset>
)
}

View File

@@ -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 (
<>
<Dropdown.Item as="li" disabled role="menuitem">
@@ -32,6 +36,12 @@ export function AccountMenuItems({
{t('subscription')}
</NavDropdownLinkItem>
) : null}
{showThemeToggle && (
<DropdownListItem>
<ThemeToggle />
</DropdownListItem>
)}
<NavDropdownDivider />
<DropdownListItem>
{

View File

@@ -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);
}
}