mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-28 22:03:16 -05:00
Create settings menu react elements in editor left menu for editor left menu react migration
GitOrigin-RevId: e75581a43a8694398b20bee3dc5ec69b60db86ab
This commit is contained in:
parent
044d6fde65
commit
5eff354971
23 changed files with 661 additions and 0 deletions
|
@ -26,6 +26,11 @@ meta(name="ol-galileoEnabled" data-type="string" content=galileoEnabled)
|
||||||
meta(name="ol-galileoPromptWords" data-type="string" content=galileoPromptWords)
|
meta(name="ol-galileoPromptWords" data-type="string" content=galileoPromptWords)
|
||||||
meta(name="ol-galileoFeatures" data-type="json" content=galileoFeatures)
|
meta(name="ol-galileoFeatures" data-type="json" content=galileoFeatures)
|
||||||
meta(name="ol-detachRole" data-type="string" content=detachRole)
|
meta(name="ol-detachRole" data-type="string" content=detachRole)
|
||||||
|
meta(name="ol-allowedImageNames" data-type="json" content=allowedImageNames)
|
||||||
|
meta(name="ol-languages" data-type="json" content=languages)
|
||||||
|
meta(name="ol-dictionaryEditorEnabled" data-type="boolean" content=dictionaryEditorEnabled)
|
||||||
|
meta(name="ol-editorThemes" data-type="json" content=editorThemes)
|
||||||
|
meta(name="ol-legacyEditorThemes" data-type="json" content=legacyEditorThemes)
|
||||||
meta(name="ol-showUpgradePrompt" data-type="boolean" content=showUpgradePrompt)
|
meta(name="ol-showUpgradePrompt" data-type="boolean" content=showUpgradePrompt)
|
||||||
meta(name="ol-useOpenTelemetry" data-type="boolean" content=useOpenTelemetry)
|
meta(name="ol-useOpenTelemetry" data-type="boolean" content=useOpenTelemetry)
|
||||||
meta(name="ol-showSupport", data-type="boolean" content=showSupport)
|
meta(name="ol-showSupport", data-type="boolean" content=showSupport)
|
||||||
|
|
|
@ -36,7 +36,9 @@
|
||||||
"ask_proj_owner_to_upgrade_for_git_bridge": "",
|
"ask_proj_owner_to_upgrade_for_git_bridge": "",
|
||||||
"ask_proj_owner_to_upgrade_for_longer_compiles": "",
|
"ask_proj_owner_to_upgrade_for_longer_compiles": "",
|
||||||
"ask_proj_owner_to_upgrade_for_references_search": "",
|
"ask_proj_owner_to_upgrade_for_references_search": "",
|
||||||
|
"auto_close_brackets": "",
|
||||||
"auto_compile": "",
|
"auto_compile": "",
|
||||||
|
"auto_complete": "",
|
||||||
"autocompile_disabled": "",
|
"autocompile_disabled": "",
|
||||||
"autocompile_disabled_reason": "",
|
"autocompile_disabled_reason": "",
|
||||||
"autocomplete": "",
|
"autocomplete": "",
|
||||||
|
@ -47,6 +49,7 @@
|
||||||
"beta_program_not_participating": "",
|
"beta_program_not_participating": "",
|
||||||
"blank_project": "",
|
"blank_project": "",
|
||||||
"blocked_filename": "",
|
"blocked_filename": "",
|
||||||
|
"browser": "",
|
||||||
"can_edit": "",
|
"can_edit": "",
|
||||||
"can_link_institution_email_acct_to_institution_acct": "",
|
"can_link_institution_email_acct_to_institution_acct": "",
|
||||||
"can_link_your_institution_acct_2": "",
|
"can_link_your_institution_acct_2": "",
|
||||||
|
@ -88,11 +91,13 @@
|
||||||
"commit": "",
|
"commit": "",
|
||||||
"common": "",
|
"common": "",
|
||||||
"commons_plan_tooltip": "",
|
"commons_plan_tooltip": "",
|
||||||
|
"compact": "",
|
||||||
"compile_error_entry_description": "",
|
"compile_error_entry_description": "",
|
||||||
"compile_error_handling": "",
|
"compile_error_handling": "",
|
||||||
"compile_larger_projects": "",
|
"compile_larger_projects": "",
|
||||||
"compile_mode": "",
|
"compile_mode": "",
|
||||||
"compile_terminated_by_user": "",
|
"compile_terminated_by_user": "",
|
||||||
|
"compiler": "",
|
||||||
"compiling": "",
|
"compiling": "",
|
||||||
"confirm": "",
|
"confirm": "",
|
||||||
"confirm_affiliation": "",
|
"confirm_affiliation": "",
|
||||||
|
@ -131,6 +136,7 @@
|
||||||
"department": "",
|
"department": "",
|
||||||
"descending": "",
|
"descending": "",
|
||||||
"description": "",
|
"description": "",
|
||||||
|
"dictionary": "",
|
||||||
"did_you_know_institution_providing_professional": "",
|
"did_you_know_institution_providing_professional": "",
|
||||||
"disable_stop_on_first_error": "",
|
"disable_stop_on_first_error": "",
|
||||||
"dismiss": "",
|
"dismiss": "",
|
||||||
|
@ -162,6 +168,7 @@
|
||||||
"duplicate_file": "",
|
"duplicate_file": "",
|
||||||
"duplicate_projects": "",
|
"duplicate_projects": "",
|
||||||
"easily_manage_your_project_files_everywhere": "",
|
"easily_manage_your_project_files_everywhere": "",
|
||||||
|
"edit": "",
|
||||||
"edit_dictionary": "",
|
"edit_dictionary": "",
|
||||||
"edit_dictionary_empty": "",
|
"edit_dictionary_empty": "",
|
||||||
"edit_dictionary_remove": "",
|
"edit_dictionary_remove": "",
|
||||||
|
@ -169,6 +176,7 @@
|
||||||
"editing": "",
|
"editing": "",
|
||||||
"editor_and_pdf": "",
|
"editor_and_pdf": "",
|
||||||
"editor_only_hide_pdf": "",
|
"editor_only_hide_pdf": "",
|
||||||
|
"editor_theme": "",
|
||||||
"email": "",
|
"email": "",
|
||||||
"email_or_password_wrong_try_again": "",
|
"email_or_password_wrong_try_again": "",
|
||||||
"emails_and_affiliations_explanation": "",
|
"emails_and_affiliations_explanation": "",
|
||||||
|
@ -198,6 +206,8 @@
|
||||||
"first_name": "",
|
"first_name": "",
|
||||||
"fold_line": "",
|
"fold_line": "",
|
||||||
"following_paths_conflict": "",
|
"following_paths_conflict": "",
|
||||||
|
"font_family": "",
|
||||||
|
"font_size": "",
|
||||||
"free_accounts_have_timeout_upgrade_to_increase": "",
|
"free_accounts_have_timeout_upgrade_to_increase": "",
|
||||||
"free_plan_label": "",
|
"free_plan_label": "",
|
||||||
"free_plan_tooltip": "",
|
"free_plan_tooltip": "",
|
||||||
|
@ -323,6 +333,7 @@
|
||||||
"is_email_affiliated": "",
|
"is_email_affiliated": "",
|
||||||
"join_project": "",
|
"join_project": "",
|
||||||
"joining": "",
|
"joining": "",
|
||||||
|
"keybindings": "",
|
||||||
"labs_program_already_participating": "",
|
"labs_program_already_participating": "",
|
||||||
"labs_program_benefits": "<0></0>",
|
"labs_program_benefits": "<0></0>",
|
||||||
"labs_program_not_participating": "",
|
"labs_program_not_participating": "",
|
||||||
|
@ -339,6 +350,7 @@
|
||||||
"leave_projects": "",
|
"leave_projects": "",
|
||||||
"let_us_know": "",
|
"let_us_know": "",
|
||||||
"limited_offer": "",
|
"limited_offer": "",
|
||||||
|
"line_height": "",
|
||||||
"link": "",
|
"link": "",
|
||||||
"link_account": "",
|
"link_account": "",
|
||||||
"link_accounts": "",
|
"link_accounts": "",
|
||||||
|
@ -368,6 +380,7 @@
|
||||||
"login_with_service": "",
|
"login_with_service": "",
|
||||||
"logs_and_output_files": "",
|
"logs_and_output_files": "",
|
||||||
"looks_like_youre_at": "",
|
"looks_like_youre_at": "",
|
||||||
|
"main_document": "",
|
||||||
"main_file_not_found": "",
|
"main_file_not_found": "",
|
||||||
"make_a_copy": "",
|
"make_a_copy": "",
|
||||||
"make_email_primary_description": "",
|
"make_email_primary_description": "",
|
||||||
|
@ -433,6 +446,8 @@
|
||||||
"or": "",
|
"or": "",
|
||||||
"other_logs_and_files": "",
|
"other_logs_and_files": "",
|
||||||
"other_output_files": "",
|
"other_output_files": "",
|
||||||
|
"overall_theme": "",
|
||||||
|
"overleaf": "",
|
||||||
"overleaf_labs": "",
|
"overleaf_labs": "",
|
||||||
"owned_by_x": "",
|
"owned_by_x": "",
|
||||||
"owner": "",
|
"owner": "",
|
||||||
|
@ -448,6 +463,7 @@
|
||||||
"pdf_only_hide_editor": "",
|
"pdf_only_hide_editor": "",
|
||||||
"pdf_preview_error": "",
|
"pdf_preview_error": "",
|
||||||
"pdf_rendering_error": "",
|
"pdf_rendering_error": "",
|
||||||
|
"pdf_viewer": "",
|
||||||
"pdf_viewer_error": "",
|
"pdf_viewer_error": "",
|
||||||
"plan_tooltip": "",
|
"plan_tooltip": "",
|
||||||
"please_change_primary_to_remove": "",
|
"please_change_primary_to_remove": "",
|
||||||
|
@ -579,6 +595,7 @@
|
||||||
"session_error": "",
|
"session_error": "",
|
||||||
"session_expired_redirecting_to_login": "",
|
"session_expired_redirecting_to_login": "",
|
||||||
"sessions": "",
|
"sessions": "",
|
||||||
|
"settings": "",
|
||||||
"share": "",
|
"share": "",
|
||||||
"share_project": "",
|
"share_project": "",
|
||||||
"share_with_your_collabs": "",
|
"share_with_your_collabs": "",
|
||||||
|
@ -605,6 +622,7 @@
|
||||||
"sort_by": "",
|
"sort_by": "",
|
||||||
"sort_by_x": "",
|
"sort_by_x": "",
|
||||||
"source": "",
|
"source": "",
|
||||||
|
"spell_check": "",
|
||||||
"sso_link_error": "",
|
"sso_link_error": "",
|
||||||
"start_by_adding_your_email": "",
|
"start_by_adding_your_email": "",
|
||||||
"start_free_trial": "",
|
"start_free_trial": "",
|
||||||
|
@ -624,6 +642,7 @@
|
||||||
"sync_project_to_github_explanation": "",
|
"sync_project_to_github_explanation": "",
|
||||||
"sync_to_dropbox": "",
|
"sync_to_dropbox": "",
|
||||||
"sync_to_github": "",
|
"sync_to_github": "",
|
||||||
|
"syntax_validation": "",
|
||||||
"tab_connecting": "",
|
"tab_connecting": "",
|
||||||
"tab_no_longer_connected": "",
|
"tab_no_longer_connected": "",
|
||||||
"tag_name_cannot_exceed_characters": "",
|
"tag_name_cannot_exceed_characters": "",
|
||||||
|
@ -634,6 +653,7 @@
|
||||||
"template_approved_by_publisher": "",
|
"template_approved_by_publisher": "",
|
||||||
"templates": "",
|
"templates": "",
|
||||||
"terminated": "",
|
"terminated": "",
|
||||||
|
"tex_live_version": "",
|
||||||
"thank_you_exclamation": "",
|
"thank_you_exclamation": "",
|
||||||
"thanks_settings_updated": "",
|
"thanks_settings_updated": "",
|
||||||
"this_action_cannot_be_undone": "",
|
"this_action_cannot_be_undone": "",
|
||||||
|
@ -709,6 +729,7 @@
|
||||||
"we_cant_find_any_sections_or_subsections_in_this_file": "",
|
"we_cant_find_any_sections_or_subsections_in_this_file": "",
|
||||||
"we_logged_you_in": "",
|
"we_logged_you_in": "",
|
||||||
"welcome_to_sl": "",
|
"welcome_to_sl": "",
|
||||||
|
"wide": "",
|
||||||
"with_premium_subscription_you_also_get": "",
|
"with_premium_subscription_you_also_get": "",
|
||||||
"word_count": "",
|
"word_count": "",
|
||||||
"work_offline": "",
|
"work_offline": "",
|
||||||
|
|
|
@ -4,6 +4,7 @@ import HelpMenu from './help-menu'
|
||||||
import { useLayoutContext } from '../../../shared/context/layout-context'
|
import { useLayoutContext } from '../../../shared/context/layout-context'
|
||||||
import classNames from 'classnames'
|
import classNames from 'classnames'
|
||||||
import SyncMenu from './sync-menu'
|
import SyncMenu from './sync-menu'
|
||||||
|
import SettingsMenu from './settings-menu'
|
||||||
|
|
||||||
export default function EditorLeftMenu() {
|
export default function EditorLeftMenu() {
|
||||||
const { leftMenuShown, setLeftMenuShown } = useLayoutContext()
|
const { leftMenuShown, setLeftMenuShown } = useLayoutContext()
|
||||||
|
@ -17,6 +18,7 @@ export default function EditorLeftMenu() {
|
||||||
<DownloadMenu />
|
<DownloadMenu />
|
||||||
<ActionsMenu />
|
<ActionsMenu />
|
||||||
<SyncMenu />
|
<SyncMenu />
|
||||||
|
<SettingsMenu />
|
||||||
<HelpMenu />
|
<HelpMenu />
|
||||||
</aside>
|
</aside>
|
||||||
{leftMenuShown ? (
|
{leftMenuShown ? (
|
||||||
|
|
|
@ -0,0 +1,50 @@
|
||||||
|
import { Form } from 'react-bootstrap'
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import getMeta from '../../../utils/meta'
|
||||||
|
import SettingsAutoCloseBrackets from './settings/settings-auto-close-brackets'
|
||||||
|
import SettingsAutoComplete from './settings/settings-auto-complete'
|
||||||
|
import SettingsCompiler from './settings/settings-compiler'
|
||||||
|
import SettingsDictionary from './settings/settings-dictionary'
|
||||||
|
import SettingsDocument from './settings/settings-document'
|
||||||
|
import SettingsEditorTheme from './settings/settings-editor-theme'
|
||||||
|
import SettingsFontFamily from './settings/settings-font-family'
|
||||||
|
import SettingsFontSize from './settings/settings-font-size'
|
||||||
|
import SettingsImageName from './settings/settings-image-name'
|
||||||
|
import SettingsKeybindings from './settings/settings-keybindings'
|
||||||
|
import SettingsLineHeight from './settings/settings-line-height'
|
||||||
|
import SettingsOverallTheme from './settings/settings-overall-theme'
|
||||||
|
import SettingsPdfViewer from './settings/settings-pdf-viewer'
|
||||||
|
import SettingsSpellCheckLanguage from './settings/settings-spell-check-language'
|
||||||
|
import SettingsSyntaxValidation from './settings/settings-syntax-validation'
|
||||||
|
|
||||||
|
export default function SettingsMenu() {
|
||||||
|
const { t } = useTranslation()
|
||||||
|
const anonymous = getMeta('ol-anonymous') as boolean | undefined
|
||||||
|
|
||||||
|
if (anonymous === true || anonymous === undefined) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<h4>{t('settings')}</h4>
|
||||||
|
<Form className="settings">
|
||||||
|
<SettingsCompiler />
|
||||||
|
<SettingsImageName />
|
||||||
|
<SettingsDocument />
|
||||||
|
<SettingsSpellCheckLanguage />
|
||||||
|
<SettingsDictionary />
|
||||||
|
<SettingsAutoComplete />
|
||||||
|
<SettingsAutoCloseBrackets />
|
||||||
|
<SettingsSyntaxValidation />
|
||||||
|
<SettingsEditorTheme />
|
||||||
|
<SettingsOverallTheme />
|
||||||
|
<SettingsKeybindings />
|
||||||
|
<SettingsFontSize />
|
||||||
|
<SettingsFontFamily />
|
||||||
|
<SettingsLineHeight />
|
||||||
|
<SettingsPdfViewer />
|
||||||
|
</Form>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import SettingsMenuSelect from './settings-menu-select'
|
||||||
|
|
||||||
|
export default function SettingsAutoCloseBrackets() {
|
||||||
|
const { t } = useTranslation()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<SettingsMenuSelect
|
||||||
|
options={[
|
||||||
|
{
|
||||||
|
value: 'true',
|
||||||
|
label: t('on'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'false',
|
||||||
|
label: t('off'),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
label={t('auto_close_brackets')}
|
||||||
|
name="autoPairDelimiters"
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import SettingsMenuSelect from './settings-menu-select'
|
||||||
|
|
||||||
|
export default function SettingsAutoComplete() {
|
||||||
|
const { t } = useTranslation()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<SettingsMenuSelect
|
||||||
|
options={[
|
||||||
|
{
|
||||||
|
value: 'true',
|
||||||
|
label: t('on'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'false',
|
||||||
|
label: t('off'),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
label={t('auto_complete')}
|
||||||
|
name="autoComplete"
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
|
@ -0,0 +1,37 @@
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import { useEditorContext } from '../../../../shared/context/editor-context'
|
||||||
|
import SettingsMenuSelect from './settings-menu-select'
|
||||||
|
|
||||||
|
export default function SettingsCompiler() {
|
||||||
|
const { t } = useTranslation()
|
||||||
|
const { permissionsLevel } = useEditorContext()
|
||||||
|
|
||||||
|
if (permissionsLevel === 'readOnly') {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<SettingsMenuSelect
|
||||||
|
options={[
|
||||||
|
{
|
||||||
|
value: 'pdflatex',
|
||||||
|
label: 'pdfLaTeX',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'latex',
|
||||||
|
label: 'LaTeX',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'xelatex',
|
||||||
|
label: 'XeLaTeX',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'lualatex',
|
||||||
|
label: 'LuaLaTeX',
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
label={t('compiler')}
|
||||||
|
name="compiler"
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
import { useState } from 'react'
|
||||||
|
import { Button } from 'react-bootstrap'
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import getMeta from '../../../../utils/meta'
|
||||||
|
import DictionaryModal from '../../../dictionary/components/dictionary-modal'
|
||||||
|
|
||||||
|
export default function SettingsDictionary() {
|
||||||
|
const { t } = useTranslation()
|
||||||
|
const [showModal, setShowModal] = useState(false)
|
||||||
|
const dictionaryEditorEnabled = getMeta(
|
||||||
|
'ol-dictionaryEditorEnabled'
|
||||||
|
) as boolean
|
||||||
|
|
||||||
|
if (!dictionaryEditorEnabled) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="form-group left-menu-setting">
|
||||||
|
<label htmlFor="dictionary">{t('dictionary')}</label>
|
||||||
|
<Button bsSize="xs" bsStyle="default" onClick={() => setShowModal(true)}>
|
||||||
|
{t('edit')}
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<DictionaryModal
|
||||||
|
show={showModal}
|
||||||
|
handleHide={() => setShowModal(false)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
|
@ -0,0 +1,53 @@
|
||||||
|
import { useMemo } from 'react'
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import isValidTeXFile from '../../../../main/is-valid-tex-file'
|
||||||
|
import { useEditorContext } from '../../../../shared/context/editor-context'
|
||||||
|
import { useProjectContext } from '../../../../shared/context/project-context'
|
||||||
|
import useScopeValue from '../../../../shared/hooks/use-scope-value'
|
||||||
|
import SettingsMenuSelect from './settings-menu-select'
|
||||||
|
import type { Option } from './settings-menu-select'
|
||||||
|
|
||||||
|
type Doc = {
|
||||||
|
doc: {
|
||||||
|
name: string
|
||||||
|
id: string
|
||||||
|
type: string
|
||||||
|
selected: boolean
|
||||||
|
}
|
||||||
|
path: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function SettingsDocument() {
|
||||||
|
const { t } = useTranslation()
|
||||||
|
|
||||||
|
const { permissionsLevel } = useEditorContext()
|
||||||
|
|
||||||
|
const { rootDocId } = useProjectContext()
|
||||||
|
const [docs] = useScopeValue<Doc[] | undefined>('docs')
|
||||||
|
|
||||||
|
const validDocsOptions = useMemo(() => {
|
||||||
|
const filteredDocs =
|
||||||
|
docs?.filter(
|
||||||
|
doc => isValidTeXFile(doc.doc.name) || rootDocId === doc.doc.id
|
||||||
|
) ?? []
|
||||||
|
|
||||||
|
const mappedDocs: Array<Option> = filteredDocs.map(doc => ({
|
||||||
|
value: doc.doc.id,
|
||||||
|
label: doc.doc.name,
|
||||||
|
}))
|
||||||
|
|
||||||
|
return mappedDocs
|
||||||
|
}, [docs, rootDocId])
|
||||||
|
|
||||||
|
if (permissionsLevel === 'readOnly') {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<SettingsMenuSelect
|
||||||
|
options={validDocsOptions}
|
||||||
|
label={t('main_document')}
|
||||||
|
name="rootDoc_id"
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
|
@ -0,0 +1,44 @@
|
||||||
|
import { useMemo } from 'react'
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import getMeta from '../../../../utils/meta'
|
||||||
|
import SettingsMenuSelect from './settings-menu-select'
|
||||||
|
import type { Option } from './settings-menu-select'
|
||||||
|
|
||||||
|
export default function SettingsEditorTheme() {
|
||||||
|
const { t } = useTranslation()
|
||||||
|
const editorThemes = getMeta('ol-editorThemes') as string[] | undefined
|
||||||
|
const legacyEditorThemes = getMeta('ol-legacyEditorThemes') as
|
||||||
|
| string[]
|
||||||
|
| undefined
|
||||||
|
|
||||||
|
const options = useMemo(() => {
|
||||||
|
const editorThemeOptions: Array<Option> =
|
||||||
|
editorThemes?.map(theme => ({
|
||||||
|
value: theme,
|
||||||
|
label: theme.replace(/_/g, ' '),
|
||||||
|
})) ?? []
|
||||||
|
|
||||||
|
const dividerOption: Option = {
|
||||||
|
value: '-',
|
||||||
|
label: '—————————————————',
|
||||||
|
ariaHidden: 'true',
|
||||||
|
disabled: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
const legacyEditorThemeOptions: Array<Option> =
|
||||||
|
legacyEditorThemes?.map(theme => ({
|
||||||
|
value: theme,
|
||||||
|
label: theme.replace(/_/g, ' '),
|
||||||
|
})) ?? []
|
||||||
|
|
||||||
|
return [...editorThemeOptions, dividerOption, ...legacyEditorThemeOptions]
|
||||||
|
}, [editorThemes, legacyEditorThemes])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<SettingsMenuSelect
|
||||||
|
label={t('editor_theme')}
|
||||||
|
name="editorTheme"
|
||||||
|
options={options}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import SettingsMenuSelect from './settings-menu-select'
|
||||||
|
|
||||||
|
export default function SettingsFontFamily() {
|
||||||
|
const { t } = useTranslation()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<SettingsMenuSelect
|
||||||
|
options={[
|
||||||
|
{
|
||||||
|
value: 'monaco',
|
||||||
|
label: 'Monaco / Menlo / Consolas',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'lucida',
|
||||||
|
label: 'Lucida / Source Code Pro',
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
label={t('font_family')}
|
||||||
|
name="fontFamily"
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import SettingsMenuSelect from './settings-menu-select'
|
||||||
|
import type { Option } from './settings-menu-select'
|
||||||
|
|
||||||
|
const sizes = ['10', '11', '12', '13', '14', '16', '18', '20', '22', '24']
|
||||||
|
const options: Array<Option> = sizes.map(size => ({
|
||||||
|
value: size,
|
||||||
|
label: `${size}px`,
|
||||||
|
}))
|
||||||
|
|
||||||
|
export default function SettingsFontSize() {
|
||||||
|
const { t } = useTranslation()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<SettingsMenuSelect
|
||||||
|
options={options}
|
||||||
|
label={t('font_size')}
|
||||||
|
name="fontSize"
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
|
@ -0,0 +1,40 @@
|
||||||
|
import { useMemo } from 'react'
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import getMeta from '../../../../utils/meta'
|
||||||
|
import SettingsMenuSelect from './settings-menu-select'
|
||||||
|
import type { Option } from './settings-menu-select'
|
||||||
|
|
||||||
|
/* eslint-disable react/no-unused-prop-types */
|
||||||
|
type AllowedImageName = {
|
||||||
|
imageDesc: string
|
||||||
|
imageName: string
|
||||||
|
}
|
||||||
|
/* eslint-enable */
|
||||||
|
|
||||||
|
export default function SettingsImageName() {
|
||||||
|
const { t } = useTranslation()
|
||||||
|
const allowedImageNames = getMeta('ol-allowedImageNames') as
|
||||||
|
| AllowedImageName[]
|
||||||
|
| undefined
|
||||||
|
|
||||||
|
const options: Array<Option> = useMemo(
|
||||||
|
() =>
|
||||||
|
allowedImageNames?.map(({ imageName, imageDesc }) => ({
|
||||||
|
value: imageName,
|
||||||
|
label: imageDesc,
|
||||||
|
})) ?? [],
|
||||||
|
[allowedImageNames]
|
||||||
|
)
|
||||||
|
|
||||||
|
if ((allowedImageNames?.length ?? 0) === 0) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<SettingsMenuSelect
|
||||||
|
options={options}
|
||||||
|
label={t('tex_live_version')}
|
||||||
|
name="imageName"
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import SettingsMenuSelect from './settings-menu-select'
|
||||||
|
|
||||||
|
export default function SettingsKeybindings() {
|
||||||
|
const { t } = useTranslation()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<SettingsMenuSelect
|
||||||
|
options={[
|
||||||
|
{
|
||||||
|
value: 'default',
|
||||||
|
label: 'None',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'vim',
|
||||||
|
label: 'Vim',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'emacs',
|
||||||
|
label: 'Emacs',
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
label={t('keybindings')}
|
||||||
|
name="mode"
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import SettingsMenuSelect from './settings-menu-select'
|
||||||
|
|
||||||
|
export default function SettingsLineHeight() {
|
||||||
|
const { t } = useTranslation()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<SettingsMenuSelect
|
||||||
|
options={[
|
||||||
|
{
|
||||||
|
value: 'compact',
|
||||||
|
label: t('compact'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'normal',
|
||||||
|
label: t('normal'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'wide',
|
||||||
|
label: t('wide'),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
label={t('line_height')}
|
||||||
|
name="lineHeight"
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
|
@ -0,0 +1,60 @@
|
||||||
|
export type Option = {
|
||||||
|
value: string
|
||||||
|
label: string
|
||||||
|
ariaHidden?: 'true' | 'false'
|
||||||
|
disabled?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export type Optgroup = {
|
||||||
|
label: string
|
||||||
|
options: Array<Option>
|
||||||
|
}
|
||||||
|
|
||||||
|
type SettingsMenuSelectProps = {
|
||||||
|
label: string
|
||||||
|
name: string
|
||||||
|
options: Array<Option>
|
||||||
|
optgroup?: Optgroup
|
||||||
|
loading?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function SettingsMenuSelect({
|
||||||
|
label,
|
||||||
|
name,
|
||||||
|
options,
|
||||||
|
optgroup,
|
||||||
|
loading,
|
||||||
|
}: SettingsMenuSelectProps) {
|
||||||
|
return (
|
||||||
|
<div className="form-group left-menu-setting">
|
||||||
|
<label htmlFor={name}>{label}</label>
|
||||||
|
{loading ? (
|
||||||
|
<p className="loading pull-right">
|
||||||
|
<i className="fa fa-fw fa-spin fa-refresh" />
|
||||||
|
</p>
|
||||||
|
) : (
|
||||||
|
<select name={name} className="form-control">
|
||||||
|
{options.map(option => (
|
||||||
|
<option
|
||||||
|
key={`${name}-${option.value}`}
|
||||||
|
value={option.value}
|
||||||
|
aria-hidden={option.ariaHidden}
|
||||||
|
disabled={option.disabled}
|
||||||
|
>
|
||||||
|
{option.label}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
{optgroup ? (
|
||||||
|
<optgroup label={optgroup.label}>
|
||||||
|
{optgroup.options.map(option => (
|
||||||
|
<option value={option.value} key={option.value}>
|
||||||
|
{option.label}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</optgroup>
|
||||||
|
) : null}
|
||||||
|
</select>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
|
@ -0,0 +1,47 @@
|
||||||
|
import { useMemo } from 'react'
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import { useLayoutContext } from '../../../../shared/context/layout-context'
|
||||||
|
import getMeta from '../../../../utils/meta'
|
||||||
|
import SettingsMenuSelect from './settings-menu-select'
|
||||||
|
import type { Option } from './settings-menu-select'
|
||||||
|
|
||||||
|
type OverallTheme = {
|
||||||
|
name: string
|
||||||
|
path: string
|
||||||
|
val: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function SettingsOverallTheme() {
|
||||||
|
const { t } = useTranslation()
|
||||||
|
const overallThemes = getMeta('ol-overallThemes') as
|
||||||
|
| OverallTheme[]
|
||||||
|
| undefined
|
||||||
|
const { loadingStyleSheet } = useLayoutContext() as {
|
||||||
|
loadingStyleSheet: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
const options: Array<Option> = useMemo(
|
||||||
|
() =>
|
||||||
|
overallThemes?.map(({ name, val }) => ({
|
||||||
|
value: val,
|
||||||
|
label: name,
|
||||||
|
})) ?? [],
|
||||||
|
[overallThemes]
|
||||||
|
)
|
||||||
|
|
||||||
|
// TODO: check for IEEE brand by:
|
||||||
|
// - const brandVariation = getMeta('ol-brandVariation') as any[]
|
||||||
|
// - settings.overleaf != null && !isIEEE(brandVariation)
|
||||||
|
if (!overallThemes) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<SettingsMenuSelect
|
||||||
|
options={options}
|
||||||
|
loading={loadingStyleSheet}
|
||||||
|
label={t('overall_theme')}
|
||||||
|
name="overallTheme"
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import SettingsMenuSelect from './settings-menu-select'
|
||||||
|
|
||||||
|
export default function SettingsPdfViewer() {
|
||||||
|
const { t } = useTranslation()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<SettingsMenuSelect
|
||||||
|
options={[
|
||||||
|
{
|
||||||
|
value: 'pdfjs',
|
||||||
|
label: t('overleaf'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'native',
|
||||||
|
label: t('browser'),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
label={t('pdf_viewer')}
|
||||||
|
name="pdfViewer"
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
import { useMemo } from 'react'
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import getMeta from '../../../../utils/meta'
|
||||||
|
import SettingsMenuSelect from './settings-menu-select'
|
||||||
|
import type { Optgroup } from './settings-menu-select'
|
||||||
|
|
||||||
|
type Language = {
|
||||||
|
name: string
|
||||||
|
code: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function SettingsSpellCheckLanguage() {
|
||||||
|
const { t } = useTranslation()
|
||||||
|
const languages = getMeta('ol-languages') as Language[] | undefined
|
||||||
|
|
||||||
|
const optgroup: Optgroup = useMemo(
|
||||||
|
() => ({
|
||||||
|
label: 'Language',
|
||||||
|
options:
|
||||||
|
languages?.map(language => ({
|
||||||
|
value: language.code,
|
||||||
|
label: language.name,
|
||||||
|
})) ?? [],
|
||||||
|
}),
|
||||||
|
[languages]
|
||||||
|
)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<SettingsMenuSelect
|
||||||
|
options={[{ value: '', label: t('off') }]}
|
||||||
|
optgroup={optgroup}
|
||||||
|
label={t('spell_check')}
|
||||||
|
name="spellCheckLanguage"
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import SettingsMenuSelect from './settings-menu-select'
|
||||||
|
|
||||||
|
export default function SettingsSyntaxValidation() {
|
||||||
|
const { t } = useTranslation()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<SettingsMenuSelect
|
||||||
|
options={[
|
||||||
|
{
|
||||||
|
value: 'true',
|
||||||
|
label: t('on'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'false',
|
||||||
|
label: t('off'),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
label={t('syntax_validation')}
|
||||||
|
name="syntaxValidation"
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
|
@ -84,6 +84,11 @@ export function LayoutProvider({ children }) {
|
||||||
// whether to display the editor and preview side-by-side or full-width ("flat")
|
// whether to display the editor and preview side-by-side or full-width ("flat")
|
||||||
const [pdfLayout, setPdfLayout] = useScopeValue('ui.pdfLayout')
|
const [pdfLayout, setPdfLayout] = useScopeValue('ui.pdfLayout')
|
||||||
|
|
||||||
|
// whether stylesheet on theme is loading
|
||||||
|
const [loadingStyleSheet, setLoadingStyleSheet] = useScopeValue(
|
||||||
|
'ui.loadingStyleSheet'
|
||||||
|
)
|
||||||
|
|
||||||
// switch to either side-by-side or flat (full-width) layout
|
// switch to either side-by-side or flat (full-width) layout
|
||||||
const switchLayout = useCallback(() => {
|
const switchLayout = useCallback(() => {
|
||||||
setPdfLayout(layout => {
|
setPdfLayout(layout => {
|
||||||
|
@ -157,10 +162,12 @@ export function LayoutProvider({ children }) {
|
||||||
pdfLayout,
|
pdfLayout,
|
||||||
pdfPreviewOpen,
|
pdfPreviewOpen,
|
||||||
reviewPanelOpen,
|
reviewPanelOpen,
|
||||||
|
loadingStyleSheet,
|
||||||
setChatIsOpen,
|
setChatIsOpen,
|
||||||
setLeftMenuShown,
|
setLeftMenuShown,
|
||||||
setPdfLayout,
|
setPdfLayout,
|
||||||
setReviewPanelOpen,
|
setReviewPanelOpen,
|
||||||
|
setLoadingStyleSheet,
|
||||||
setView,
|
setView,
|
||||||
switchLayout,
|
switchLayout,
|
||||||
view,
|
view,
|
||||||
|
@ -176,10 +183,12 @@ export function LayoutProvider({ children }) {
|
||||||
pdfLayout,
|
pdfLayout,
|
||||||
pdfPreviewOpen,
|
pdfPreviewOpen,
|
||||||
reviewPanelOpen,
|
reviewPanelOpen,
|
||||||
|
loadingStyleSheet,
|
||||||
setChatIsOpen,
|
setChatIsOpen,
|
||||||
setLeftMenuShown,
|
setLeftMenuShown,
|
||||||
setPdfLayout,
|
setPdfLayout,
|
||||||
setReviewPanelOpen,
|
setReviewPanelOpen,
|
||||||
|
setLoadingStyleSheet,
|
||||||
setView,
|
setView,
|
||||||
switchLayout,
|
switchLayout,
|
||||||
view,
|
view,
|
||||||
|
|
|
@ -145,6 +145,42 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.left-menu-setting {
|
||||||
|
padding: 0 9px;
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
align-items: baseline;
|
||||||
|
justify-content: flex-end;
|
||||||
|
border-bottom: solid 1px rgba(0, 0, 0, 0.07);
|
||||||
|
margin-bottom: 0;
|
||||||
|
|
||||||
|
&:first-child {
|
||||||
|
margin-top: -9px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
border-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: @link-color;
|
||||||
|
|
||||||
|
label {
|
||||||
|
color: @white;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
select.form-control {
|
||||||
|
height: 23px;
|
||||||
|
padding: 1px @padding-xs;
|
||||||
|
font-size: inherit;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: @ol-blue-gray-1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#left-menu-mask {
|
#left-menu-mask {
|
||||||
|
|
Loading…
Reference in a new issue