Merge pull request #28503 from overleaf/mj-split-theme-list-dark-light-legacy

[web] Split dark and light options in theme selector

GitOrigin-RevId: 507c5a71269b360d23b246516487a0a072ee7d18
This commit is contained in:
Mathias Jakobsen
2025-12-09 09:06:07 +00:00
committed by Copybot
parent da72a45e86
commit 0b51972da1
10 changed files with 79 additions and 74 deletions
@@ -1,43 +1,19 @@
import { useMemo } from 'react'
import { useTranslation } from 'react-i18next'
import getMeta from '../../../../utils/meta'
import { useProjectSettingsContext } from '../../context/project-settings-context'
import SettingsMenuSelect from './settings-menu-select'
import type { Option } from './settings-menu-select'
import { useEditorThemesOptionGroups } from '../../hooks/use-editor-theme-option-groups'
export default function SettingsEditorTheme() {
const { t } = useTranslation()
const editorThemes = getMeta('ol-editorThemes')
const legacyEditorThemes = getMeta('ol-legacyEditorThemes')
const { editorTheme, setEditorTheme } = useProjectSettingsContext()
const options = useMemo(() => {
const editorThemeOptions: Array<Option> =
editorThemes?.map(theme => ({
value: theme.name,
label: theme.name.replace(/_/g, ' '),
})) ?? []
const dividerOption: Option = {
value: '-',
label: '—————————————————',
disabled: true,
}
const legacyEditorThemeOptions: Array<Option> =
legacyEditorThemes?.map(theme => ({
value: theme.name,
label: theme.name.replace(/_/g, ' ') + ' (Legacy)',
})) ?? []
return [...editorThemeOptions, dividerOption, ...legacyEditorThemeOptions]
}, [editorThemes, legacyEditorThemes])
const optGroups = useEditorThemesOptionGroups()
return (
<SettingsMenuSelect
onChange={setEditorTheme}
value={editorTheme}
options={options}
optgroups={optGroups}
label={t('editor_theme')}
name="editorTheme"
translateOptions="no"
@@ -22,8 +22,8 @@ export type Optgroup<T extends PossibleValue = string> = {
type SettingsMenuSelectProps<T extends PossibleValue = string> = {
label: string
name: string
options: Array<Option<T>>
optgroup?: Optgroup<T>
options?: Array<Option<T>>
optgroups?: Array<Optgroup<T>>
loading?: boolean
onChange: (val: T) => void
value?: T
@@ -35,7 +35,7 @@ export default function SettingsMenuSelect<T extends PossibleValue = string>({
label,
name,
options,
optgroup,
optgroups,
loading,
onChange,
value,
@@ -92,7 +92,7 @@ export default function SettingsMenuSelect<T extends PossibleValue = string>({
ref={selectRef}
translate={translateOptions}
>
{options.map(option => (
{options?.map(option => (
<option
key={`${name}-${option.value}`}
value={option.value.toString()}
@@ -102,8 +102,8 @@ export default function SettingsMenuSelect<T extends PossibleValue = string>({
{option.label}
</option>
))}
{optgroup ? (
<optgroup label={optgroup.label}>
{optgroups?.map(optgroup => (
<optgroup label={optgroup.label} key={optgroup.label}>
{optgroup.options.map(option => (
<option
value={option.value.toString()}
@@ -113,7 +113,7 @@ export default function SettingsMenuSelect<T extends PossibleValue = string>({
</option>
))}
</optgroup>
) : null}
))}
</OLFormSelect>
)}
</OLFormGroup>
@@ -33,7 +33,7 @@ export default function SettingsSpellCheckLanguage() {
onChange={setSpellCheckLanguage}
value={supportsWebAssembly() ? spellCheckLanguage : ''}
options={[{ value: '', label: t('off') }]}
optgroup={optgroup}
optgroups={[optgroup]}
label={t('spell_check')}
name="spellCheckLanguage"
disabled={permissionsLevel === 'readOnly' || !supportsWebAssembly()}
@@ -0,0 +1,51 @@
import getMeta from '@/utils/meta'
import { useMemo } from 'react'
import { Option } from '../components/settings/settings-menu-select'
import { useTranslation } from 'react-i18next'
const overrides = new Map([['overleaf', 'overleaf light']])
function getThemeName(theme: string): string {
return (overrides.get(theme) ?? theme).replace(/_/g, ' ')
}
export function useEditorThemesOptionGroups() {
const editorThemes = getMeta('ol-editorThemes')
const legacyEditorThemes = getMeta('ol-legacyEditorThemes')
const { t } = useTranslation()
const optgroups = useMemo(() => {
const lightThemes: Array<Option> = []
const darkThemes: Array<Option> = []
editorThemes?.forEach(({ name: theme, dark }) => {
const target = dark ? darkThemes : lightThemes
target.push({
value: theme,
label: getThemeName(theme),
})
})
const legacyEditorThemeOptions: Array<Option> =
legacyEditorThemes?.map(({ name: theme }) => ({
value: theme,
label: getThemeName(theme) + ' (Legacy)',
})) ?? []
const groups = []
if (lightThemes.length > 0) {
groups.push({ label: t('light_themes'), options: lightThemes })
}
if (darkThemes.length > 0) {
groups.push({ label: t('dark_themes'), options: darkThemes })
}
if (legacyEditorThemeOptions.length > 0) {
groups.push({
label: t('legacy_themes'),
options: legacyEditorThemeOptions,
})
}
return groups
}, [editorThemes, legacyEditorThemes, t])
return optgroups
}
@@ -1,44 +1,20 @@
import { useProjectSettingsContext } from '@/features/editor-left-menu/context/project-settings-context'
import DropdownSetting from '../dropdown-setting'
import getMeta from '@/utils/meta'
import { useMemo } from 'react'
import type { Option } from '../dropdown-setting'
import { useTranslation } from 'react-i18next'
import { useEditorThemesOptionGroups } from '@/features/editor-left-menu/hooks/use-editor-theme-option-groups'
export default function EditorThemeSetting() {
const editorThemes = getMeta('ol-editorThemes')
const legacyEditorThemes = getMeta('ol-legacyEditorThemes')
const { editorTheme, setEditorTheme } = useProjectSettingsContext()
const { t } = useTranslation()
const options = useMemo(() => {
const editorThemeOptions: Array<Option> =
editorThemes?.map(theme => ({
value: theme.name,
label: theme.name.replace(/_/g, ' '),
})) ?? []
const dividerOption: Option = {
value: '-',
label: '—————————————————',
disabled: true,
}
const legacyEditorThemeOptions: Array<Option> =
legacyEditorThemes?.map(theme => ({
value: theme.name,
label: theme.name.replace(/_/g, ' ') + ' (Legacy)',
})) ?? []
return [...editorThemeOptions, dividerOption, ...legacyEditorThemeOptions]
}, [editorThemes, legacyEditorThemes])
const optGroups = useEditorThemesOptionGroups()
return (
<DropdownSetting
id="editorTheme"
label={t('editor_theme')}
description={t('the_code_editor_color_scheme')}
options={options}
optgroups={optGroups}
onChange={setEditorTheme}
value={editorTheme}
translateOptions="no"
@@ -21,12 +21,10 @@ export type Optgroup<T extends PossibleValue = string> = {
type SettingsMenuSelectProps<T extends PossibleValue = string> = {
id: string
label: string
options: Array<Option<T>>
options?: Array<Option<T>>
onChange: (val: T) => void
description?: string
// TODO: We can remove optgroup when the spellcheck setting is
// split into 2 and no longer uses it.
optgroup?: Optgroup<T>
optgroups?: Array<Optgroup<T>>
value?: T
disabled?: boolean
width?: 'default' | 'wide'
@@ -40,7 +38,7 @@ export default function DropdownSetting<T extends PossibleValue = string>({
options,
onChange,
value,
optgroup,
optgroups,
description = undefined,
disabled = false,
width = 'default',
@@ -77,7 +75,7 @@ export default function DropdownSetting<T extends PossibleValue = string>({
disabled={disabled}
translate={translateOptions}
>
{options.map(option => (
{options?.map(option => (
<option
key={`${id}-${option.value}`}
value={option.value.toString()}
@@ -87,8 +85,8 @@ export default function DropdownSetting<T extends PossibleValue = string>({
{option.label}
</option>
))}
{optgroup ? (
<optgroup label={optgroup.label}>
{optgroups?.map(optgroup => (
<optgroup label={optgroup.label} key={optgroup.label}>
{optgroup.options.map(option => (
<option
value={option.value.toString()}
@@ -98,7 +96,7 @@ export default function DropdownSetting<T extends PossibleValue = string>({
</option>
))}
</optgroup>
) : null}
))}
</OLFormSelect>
)}
</Setting>
@@ -31,7 +31,7 @@ export default function SpellCheckSetting() {
id="spellCheckLanguage"
label={t('spellcheck_language')}
options={[{ value: '', label: t('off') }]}
optgroup={optgroup}
optgroups={[optgroup]}
onChange={setSpellCheckLanguage}
value={supportsWebAssembly() ? spellCheckLanguage : ''}
width="wide"