Browse Source

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
Mathias Jakobsen 10 months ago
parent
commit
0b51972da1

+ 3 - 0
services/web/frontend/extracted-translations.json

@@ -381,6 +381,7 @@
   "customizing_figures": "",
   "customizing_tables": "",
   "dark_mode_pdf_preview": "",
+  "dark_themes": "",
   "date_and_owner": "",
   "date_and_time": "",
   "dealing_with_errors": "",
@@ -977,6 +978,7 @@
   "leave_project": "",
   "leave_projects": "",
   "left": "",
+  "legacy_themes": "",
   "length_unit": "",
   "let_us_know": "",
   "let_us_know_how_we_can_help": "",
@@ -985,6 +987,7 @@
   "lets_get_you_set_up": "",
   "library": "",
   "licenses": "",
+  "light_themes": "",
   "limited_document_history": "",
   "limited_to_n_collaborators_per_project": "",
   "limited_to_n_collaborators_per_project_plural": "",

+ 3 - 27
services/web/frontend/js/features/editor-left-menu/components/settings/settings-editor-theme.tsx

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

+ 7 - 7
services/web/frontend/js/features/editor-left-menu/components/settings/settings-menu-select.tsx

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

+ 1 - 1
services/web/frontend/js/features/editor-left-menu/components/settings/settings-spell-check-language.tsx

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

+ 51 - 0
services/web/frontend/js/features/editor-left-menu/hooks/use-editor-theme-option-groups.tsx

@@ -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
+}

+ 3 - 27
services/web/frontend/js/features/ide-redesign/components/settings/appearance-settings/editor-theme-setting.tsx

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

+ 7 - 9
services/web/frontend/js/features/ide-redesign/components/settings/dropdown-setting.tsx

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

+ 1 - 1
services/web/frontend/js/features/ide-redesign/components/settings/editor-settings/spell-check-setting.tsx

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

+ 3 - 0
services/web/locales/en.json

@@ -502,6 +502,7 @@
   "da": "Danish",
   "dark_mode": "Dark mode",
   "dark_mode_pdf_preview": "Dark mode PDF preview",
+  "dark_themes": "Dark themes",
   "date": "Date",
   "date_and_owner": "Date and owner",
   "date_and_time": "Date and time",
@@ -1263,6 +1264,7 @@
   "leave_project": "Leave Project",
   "leave_projects": "Leave Projects",
   "left": "Left",
+  "legacy_themes": "Legacy themes",
   "length_unit": "Length unit",
   "let_us_know": "Let us know",
   "let_us_know_how_we_can_help": "Let us know how we can help",
@@ -1273,6 +1275,7 @@
   "library": "Library",
   "license": "License",
   "licenses": "Licenses",
+  "light_themes": "Light themes",
   "limited_document_history": "Limited document history",
   "limited_to_n_collaborators_per_project": "Limited to __count__ collaborator per project",
   "limited_to_n_collaborators_per_project_plural": "Limited to __count__ collaborators per project",

+ 0 - 2
services/web/test/frontend/components/editor-left-menu/editor-left-menu.spec.tsx

@@ -607,7 +607,6 @@ describe('<EditorLeftMenu />', function () {
               'editortheme-1',
               'editortheme-2',
               'editortheme-3',
-              '-',
               'legacytheme-1',
               'legacytheme-2',
               'legacytheme-3',
@@ -618,7 +617,6 @@ describe('<EditorLeftMenu />', function () {
               'editortheme-1',
               'editortheme-2',
               'editortheme-3',
-              '—————————————————',
               'legacytheme-1 (Legacy)',
               'legacytheme-2 (Legacy)',
               'legacytheme-3 (Legacy)',