mirror of
https://github.com/yu-i-i/overleaf-cep.git
synced 2026-05-28 19:41:33 +02:00
Move symbol palette to module and add overlay (#5396)
GitOrigin-RevId: 649dd56aaecd60662bd2bf534bda323ee541874f
This commit is contained in:
@@ -88,7 +88,7 @@ block content
|
||||
ng-class="{ 'ide-history-open' : (ui.view == 'history' && history.isV2) }",
|
||||
layout="main",
|
||||
ng-hide="state.loading",
|
||||
resize-on="layout:chat:resize,history:toggle,layout:flat-screen:toggle",
|
||||
resize-on="layout:chat:resize,history:toggle,layout:flat-screen:toggle,symbol-palette-toggled",
|
||||
minimum-restore-size-west="130"
|
||||
custom-toggler-pane=hasFeature('custom-togglers') ? "west" : false
|
||||
custom-toggler-msg-when-open=hasFeature('custom-togglers') ? translate("tooltip_hide_filetree") : false
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
.ui-layout-center(
|
||||
ng-controller="ReviewPanelController",
|
||||
ng-class="{\
|
||||
'rp-unsupported': editor.showRichText,\
|
||||
'rp-state-current-file': (reviewPanel.subView === SubViews.CUR_FILE),\
|
||||
'rp-state-current-file-expanded': (reviewPanel.subView === SubViews.CUR_FILE && ui.reviewPanelOpen),\
|
||||
'rp-state-current-file-mini': (reviewPanel.subView === SubViews.CUR_FILE && !ui.reviewPanelOpen),\
|
||||
'rp-state-overview': (reviewPanel.subView === SubViews.OVERVIEW),\
|
||||
'rp-size-mini': ui.miniReviewPanelVisible,\
|
||||
'rp-size-expanded': ui.reviewPanelOpen,\
|
||||
'rp-layout-left': reviewPanel.layoutToLeft,\
|
||||
'rp-loading-threads': reviewPanel.loadingThreads,\
|
||||
}"
|
||||
)
|
||||
.loading-panel(
|
||||
ng-show="(!editor.sharejs_doc || editor.opening) && !editor.error_state",
|
||||
style=showRichText ? "top: 32px" : "",
|
||||
)
|
||||
span(ng-show="editor.open_doc_id")
|
||||
i.fa.fa-spin.fa-refresh
|
||||
| #{translate("loading")}…
|
||||
span(ng-show="!editor.open_doc_id")
|
||||
i.fa.fa-arrow-left
|
||||
| #{translate("open_a_file_on_the_left")}
|
||||
|
||||
if moduleIncludesAvailable('editor:main')
|
||||
!= moduleIncludes('editor:main', locals)
|
||||
else
|
||||
.toolbar.toolbar-editor
|
||||
|
||||
.multi-selection-ongoing(
|
||||
ng-show="multiSelectedCount > 0"
|
||||
)
|
||||
.multi-selection-message
|
||||
h4 {{ multiSelectedCount }} #{translate('files_selected')}
|
||||
|
||||
if showNewSourceEditor
|
||||
if moduleIncludesAvailable('editor:source-editor')
|
||||
!= moduleIncludes('editor:source-editor', locals)
|
||||
else
|
||||
include ./source-editor
|
||||
|
||||
if !isRestrictedTokenMember
|
||||
include ./review-panel
|
||||
@@ -17,10 +17,10 @@
|
||||
vertical-resizable-panes="symbol-palette-resizer"
|
||||
vertical-resizable-panes-hidden-externally-on="symbol-palette-toggled"
|
||||
vertical-resizable-panes-hidden-initially="true"
|
||||
vertical-resizable-panes-default-size="196"
|
||||
vertical-resizable-panes-min-size="144"
|
||||
vertical-resizable-panes-default-size="250"
|
||||
vertical-resizable-panes-min-size="250"
|
||||
vertical-resizable-panes-max-size="336"
|
||||
vertical-resizable-panes-resize-on="layout:flat-screen:toggle"
|
||||
vertical-resizable-panes-resize-on="layout:flat-screen:toggle,symbol-palette-toggled"
|
||||
)
|
||||
.div(vertical-resizable-top)
|
||||
|
||||
@@ -55,5 +55,7 @@
|
||||
if !isRestrictedTokenMember
|
||||
include ./review-panel
|
||||
|
||||
.div(vertical-resizable-bottom)
|
||||
include ./symbol-palette
|
||||
if moduleIncludesAvailable('editor:symbol-palette')
|
||||
.div(vertical-resizable-bottom)
|
||||
!= moduleIncludes('editor:symbol-palette', locals)
|
||||
|
||||
@@ -12,10 +12,7 @@ div.full-size(
|
||||
custom-toggler-msg-when-open=hasFeature('custom-togglers') ? translate("tooltip_hide_pdf") : false
|
||||
custom-toggler-msg-when-closed=hasFeature('custom-togglers') ? translate("tooltip_show_pdf") : false
|
||||
)
|
||||
if showSymbolPalette
|
||||
include ./editor-with-symbol-palette
|
||||
else
|
||||
include ./editor-no-symbol-palette
|
||||
include ./editor-pane
|
||||
|
||||
.ui-layout-east
|
||||
div(ng-if="ui.pdfLayout == 'sideBySide'")
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
if showSymbolPalette
|
||||
symbol-palette(show="editor.showSymbolPalette" handle-select="editor.insertSymbol")
|
||||
@@ -737,6 +737,7 @@ module.exports = {
|
||||
tprLinkedFileInfo: [],
|
||||
tprLinkedFileRefreshError: [],
|
||||
contactUsModal: [],
|
||||
editorToolbarButtons: [],
|
||||
},
|
||||
|
||||
moduleImportSequence: ['launchpad', 'server-ce-scripts', 'user-activate'],
|
||||
|
||||
@@ -90,8 +90,8 @@
|
||||
"file_name_in_this_project": "",
|
||||
"file_outline": "",
|
||||
"files_cannot_include_invalid_characters": "",
|
||||
"find_out_more_about_latex_symbols": "",
|
||||
"find_out_more_about_the_file_outline": "",
|
||||
"find_the_symbols_you_need_with_premium": "",
|
||||
"first_error_popup_label": "",
|
||||
"following_paths_conflict": "",
|
||||
"free_accounts_have_timeout_upgrade_to_increase": "",
|
||||
@@ -170,6 +170,7 @@
|
||||
"layout": "",
|
||||
"learn_how_to_make_documents_compile_quickly": "",
|
||||
"learn_more_about_link_sharing": "",
|
||||
"learn_more_about_the_symbol_palette": "",
|
||||
"link_sharing_is_off": "",
|
||||
"link_sharing_is_on": "",
|
||||
"link_to_github": "",
|
||||
@@ -345,6 +346,7 @@
|
||||
"too_recently_compiled": "",
|
||||
"total_words": "",
|
||||
"try_it_for_free": "",
|
||||
"try_premium_for_free": "",
|
||||
"try_recompile_project": "",
|
||||
"try_refresh_page": "",
|
||||
"turn_off_link_sharing": "",
|
||||
@@ -367,6 +369,7 @@
|
||||
"view_warning": "",
|
||||
"view_warning_plural": "",
|
||||
"we_cant_find_any_sections_or_subsections_in_this_file": "",
|
||||
"with_premium_subscription_you_also_get": "",
|
||||
"word_count": "",
|
||||
"work_offline": "",
|
||||
"work_with_non_overleaf_users": "",
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useEditorContext } from '../../../shared/context/editor-context'
|
||||
import Icon from '../../../shared/components/icon'
|
||||
import StartFreeTrialButton from '../../../shared/components/start-free-trial-button'
|
||||
import { memo } from 'react'
|
||||
import PdfLogEntry from './pdf-log-entry'
|
||||
import UpgradeBenefits from '../../../shared/components/upgrade-benefits'
|
||||
|
||||
function TimeoutUpgradePrompt() {
|
||||
const { t } = useTranslation()
|
||||
@@ -26,38 +26,7 @@ function TimeoutUpgradePrompt() {
|
||||
<p>{t('free_accounts_have_timeout_upgrade_to_increase')}</p>
|
||||
<p>{t('plus_upgraded_accounts_receive')}:</p>
|
||||
<div>
|
||||
<ul className="list-unstyled">
|
||||
<li>
|
||||
<Icon type="check" />
|
||||
|
||||
{t('unlimited_projects')}
|
||||
</li>
|
||||
<li>
|
||||
<Icon type="check" />
|
||||
|
||||
{t('collabs_per_proj', { collabcount: 'Multiple' })}
|
||||
</li>
|
||||
<li>
|
||||
<Icon type="check" />
|
||||
|
||||
{t('full_doc_history')}
|
||||
</li>
|
||||
<li>
|
||||
<Icon type="check" />
|
||||
|
||||
{t('sync_to_dropbox')}
|
||||
</li>
|
||||
<li>
|
||||
<Icon type="check" />
|
||||
|
||||
{t('sync_to_github')}
|
||||
</li>
|
||||
<li>
|
||||
<Icon type="check" />
|
||||
|
||||
{t('compile_larger_projects')}
|
||||
</li>
|
||||
</ul>
|
||||
<UpgradeBenefits />
|
||||
</div>
|
||||
{isProjectOwner && (
|
||||
<p className="text-center">
|
||||
|
||||
@@ -1,61 +0,0 @@
|
||||
import { TabPanels, TabPanel } from '@reach/tabs'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import PropTypes from 'prop-types'
|
||||
import SymbolPaletteItems from './symbol-palette-items'
|
||||
|
||||
export default function SymbolPaletteBody({
|
||||
categories,
|
||||
categorisedSymbols,
|
||||
filteredSymbols,
|
||||
handleSelect,
|
||||
focusInput,
|
||||
}) {
|
||||
const { t } = useTranslation()
|
||||
|
||||
// searching with matches: show the matched symbols
|
||||
// searching with no matches: show a message
|
||||
// note: include empty tab panels so that aria-controls on tabs can still reference the panel ids
|
||||
if (filteredSymbols) {
|
||||
return (
|
||||
<>
|
||||
{filteredSymbols.length ? (
|
||||
<SymbolPaletteItems
|
||||
items={filteredSymbols}
|
||||
handleSelect={handleSelect}
|
||||
focusInput={focusInput}
|
||||
/>
|
||||
) : (
|
||||
<div className="symbol-palette-empty">{t('no_symbols_found')}</div>
|
||||
)}
|
||||
|
||||
<TabPanels>
|
||||
{categories.map(category => (
|
||||
<TabPanel key={category.id} tabIndex={-1} />
|
||||
))}
|
||||
</TabPanels>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
// not searching: show the symbols grouped by category
|
||||
return (
|
||||
<TabPanels>
|
||||
{categories.map(category => (
|
||||
<TabPanel key={category.id} tabIndex={-1}>
|
||||
<SymbolPaletteItems
|
||||
items={categorisedSymbols[category.id]}
|
||||
handleSelect={handleSelect}
|
||||
focusInput={focusInput}
|
||||
/>
|
||||
</TabPanel>
|
||||
))}
|
||||
</TabPanels>
|
||||
)
|
||||
}
|
||||
SymbolPaletteBody.propTypes = {
|
||||
categories: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||
categorisedSymbols: PropTypes.object,
|
||||
filteredSymbols: PropTypes.arrayOf(PropTypes.object),
|
||||
handleSelect: PropTypes.func.isRequired,
|
||||
focusInput: PropTypes.func.isRequired,
|
||||
}
|
||||
@@ -1,103 +0,0 @@
|
||||
import { Tabs } from '@reach/tabs'
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import PropTypes from 'prop-types'
|
||||
import { matchSorter } from 'match-sorter'
|
||||
|
||||
import symbols from '../data/symbols.json'
|
||||
import { buildCategorisedSymbols, createCategories } from '../utils/categories'
|
||||
import SymbolPaletteSearch from './symbol-palette-search'
|
||||
import SymbolPaletteBody from './symbol-palette-body'
|
||||
import SymbolPaletteTabs from './symbol-palette-tabs'
|
||||
// import SymbolPaletteInfoLink from './symbol-palette-info-link'
|
||||
import BetaBadge from '../../../shared/components/beta-badge'
|
||||
|
||||
import '@reach/tabs/styles.css'
|
||||
|
||||
export default function SymbolPaletteContent({ handleSelect }) {
|
||||
const [input, setInput] = useState('')
|
||||
|
||||
const { t } = useTranslation()
|
||||
|
||||
// build the list of categories with translated labels
|
||||
const categories = useMemo(() => createCategories(t), [t])
|
||||
|
||||
// group the symbols by category
|
||||
const categorisedSymbols = useMemo(
|
||||
() => buildCategorisedSymbols(categories),
|
||||
[categories]
|
||||
)
|
||||
|
||||
// select symbols which match the input
|
||||
const filteredSymbols = useMemo(() => {
|
||||
if (input === '') {
|
||||
return null
|
||||
}
|
||||
|
||||
const words = input.trim().split(/\s+/)
|
||||
|
||||
return words.reduceRight(
|
||||
(symbols, word) =>
|
||||
matchSorter(symbols, word, {
|
||||
keys: ['command', 'description', 'character', 'aliases'],
|
||||
threshold: matchSorter.rankings.CONTAINS,
|
||||
}),
|
||||
symbols
|
||||
)
|
||||
}, [input])
|
||||
|
||||
const inputRef = useRef(null)
|
||||
|
||||
// allow the input to be focused
|
||||
const focusInput = useCallback(() => {
|
||||
if (inputRef.current) {
|
||||
inputRef.current.focus()
|
||||
}
|
||||
}, [])
|
||||
|
||||
// focus the input when the symbol palette is opened
|
||||
useEffect(() => {
|
||||
if (inputRef.current) {
|
||||
inputRef.current.focus()
|
||||
}
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<Tabs className="symbol-palette-container">
|
||||
<div className="symbol-palette">
|
||||
<div className="symbol-palette-header">
|
||||
<SymbolPaletteTabs
|
||||
categories={categories}
|
||||
disabled={input.length > 0}
|
||||
/>
|
||||
<div className="symbol-palette-header-group">
|
||||
<BetaBadge
|
||||
tooltip={{
|
||||
id: 'tooltip-symbol-palette-beta',
|
||||
text:
|
||||
'The Symbol Palette is a beta feature. Click here to give feedback.',
|
||||
placement: 'top',
|
||||
}}
|
||||
url="https://forms.gle/BybHV5svGE8rJ6Ki9"
|
||||
/>
|
||||
{/* NOTE: replace the beta badge with this info link when rolling out to all users */}
|
||||
{/* <SymbolPaletteInfoLink /> */}
|
||||
<SymbolPaletteSearch setInput={setInput} inputRef={inputRef} />
|
||||
</div>
|
||||
</div>
|
||||
<div className="symbol-palette-body">
|
||||
<SymbolPaletteBody
|
||||
categories={categories}
|
||||
categorisedSymbols={categorisedSymbols}
|
||||
filteredSymbols={filteredSymbols}
|
||||
handleSelect={handleSelect}
|
||||
focusInput={focusInput}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</Tabs>
|
||||
)
|
||||
}
|
||||
SymbolPaletteContent.propTypes = {
|
||||
handleSelect: PropTypes.func.isRequired,
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
import { Button, OverlayTrigger, Tooltip } from 'react-bootstrap'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
export default function SymbolPaletteInfoLink() {
|
||||
const { t } = useTranslation()
|
||||
|
||||
return (
|
||||
<OverlayTrigger
|
||||
placement="top"
|
||||
trigger={['hover', 'focus']}
|
||||
overlay={
|
||||
<Tooltip id="tooltip-symbol-palette-info">
|
||||
{t('find_out_more_about_latex_symbols')}
|
||||
</Tooltip>
|
||||
}
|
||||
>
|
||||
<Button
|
||||
bsStyle="link"
|
||||
bsSize="small"
|
||||
className="symbol-palette-info-link"
|
||||
href="https://www.overleaf.com/learn/latex/List_of_Greek_letters_and_math_symbols"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<span className="info-badge" />
|
||||
</Button>
|
||||
</OverlayTrigger>
|
||||
)
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
import { useEffect, useRef } from 'react'
|
||||
import { OverlayTrigger, Tooltip } from 'react-bootstrap'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
export default function SymbolPaletteItem({
|
||||
focused,
|
||||
handleSelect,
|
||||
handleKeyDown,
|
||||
symbol,
|
||||
}) {
|
||||
const buttonRef = useRef(null)
|
||||
|
||||
// call focus() on this item when appropriate
|
||||
useEffect(() => {
|
||||
if (
|
||||
focused &&
|
||||
buttonRef.current &&
|
||||
document.activeElement?.closest('.symbol-palette-items')
|
||||
) {
|
||||
buttonRef.current.focus()
|
||||
}
|
||||
}, [focused])
|
||||
|
||||
return (
|
||||
<OverlayTrigger
|
||||
placement="top"
|
||||
trigger={['hover', 'focus']}
|
||||
overlay={
|
||||
<Tooltip id={`tooltip-symbol-${symbol.codepoint}`}>
|
||||
<div className="symbol-palette-item-description">
|
||||
{symbol.description}
|
||||
</div>
|
||||
<div className="symbol-palette-item-command">{symbol.command}</div>
|
||||
{symbol.notes && (
|
||||
<div className="symbol-palette-item-notes">{symbol.notes}</div>
|
||||
)}
|
||||
</Tooltip>
|
||||
}
|
||||
>
|
||||
<button
|
||||
key={symbol.codepoint}
|
||||
className="symbol-palette-item"
|
||||
onClick={() => handleSelect(symbol)}
|
||||
onKeyDown={handleKeyDown}
|
||||
tabIndex={focused ? 0 : -1}
|
||||
ref={buttonRef}
|
||||
role="option"
|
||||
aria-label={symbol.description}
|
||||
aria-selected={focused ? 'true' : 'false'}
|
||||
>
|
||||
{symbol.character}
|
||||
</button>
|
||||
</OverlayTrigger>
|
||||
)
|
||||
}
|
||||
SymbolPaletteItem.propTypes = {
|
||||
symbol: PropTypes.shape({
|
||||
codepoint: PropTypes.string.isRequired,
|
||||
description: PropTypes.string.isRequired,
|
||||
command: PropTypes.string.isRequired,
|
||||
character: PropTypes.string.isRequired,
|
||||
notes: PropTypes.string,
|
||||
}),
|
||||
handleKeyDown: PropTypes.func.isRequired,
|
||||
handleSelect: PropTypes.func.isRequired,
|
||||
focused: PropTypes.bool,
|
||||
}
|
||||
@@ -1,86 +0,0 @@
|
||||
import { useCallback, useEffect, useState } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import SymbolPaletteItem from './symbol-palette-item'
|
||||
|
||||
export default function SymbolPaletteItems({
|
||||
items,
|
||||
handleSelect,
|
||||
focusInput,
|
||||
}) {
|
||||
const [focusedIndex, setFocusedIndex] = useState(0)
|
||||
|
||||
// reset the focused item when the list of items changes
|
||||
useEffect(() => {
|
||||
setFocusedIndex(0)
|
||||
}, [items])
|
||||
|
||||
// navigate through items with left and right arrows
|
||||
const handleKeyDown = useCallback(
|
||||
event => {
|
||||
if (event.metaKey || event.altKey || event.ctrlKey || event.shiftKey) {
|
||||
return
|
||||
}
|
||||
|
||||
switch (event.key) {
|
||||
// focus previous item
|
||||
case 'ArrowLeft':
|
||||
case 'ArrowUp':
|
||||
setFocusedIndex(index => (index > 0 ? index - 1 : items.length - 1))
|
||||
break
|
||||
|
||||
// focus next item
|
||||
case 'ArrowRight':
|
||||
case 'ArrowDown':
|
||||
setFocusedIndex(index => (index < items.length - 1 ? index + 1 : 0))
|
||||
break
|
||||
|
||||
// focus first item
|
||||
case 'Home':
|
||||
setFocusedIndex(0)
|
||||
break
|
||||
|
||||
// focus last item
|
||||
case 'End':
|
||||
setFocusedIndex(items.length - 1)
|
||||
break
|
||||
|
||||
// allow the default action
|
||||
case 'Enter':
|
||||
case ' ':
|
||||
break
|
||||
|
||||
// any other key returns focus to the input
|
||||
default:
|
||||
focusInput()
|
||||
break
|
||||
}
|
||||
},
|
||||
[focusInput, items.length]
|
||||
)
|
||||
|
||||
return (
|
||||
<div className="symbol-palette-items" role="listbox" aria-label="Symbols">
|
||||
{items.map((symbol, index) => (
|
||||
<SymbolPaletteItem
|
||||
key={symbol.codepoint}
|
||||
symbol={symbol}
|
||||
handleSelect={symbol => {
|
||||
handleSelect(symbol)
|
||||
setFocusedIndex(index)
|
||||
}}
|
||||
handleKeyDown={handleKeyDown}
|
||||
focused={index === focusedIndex}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
SymbolPaletteItems.propTypes = {
|
||||
items: PropTypes.arrayOf(
|
||||
PropTypes.shape({
|
||||
codepoint: PropTypes.string.isRequired,
|
||||
})
|
||||
).isRequired,
|
||||
handleSelect: PropTypes.func.isRequired,
|
||||
focusInput: PropTypes.func.isRequired,
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
import { useCallback, useEffect, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import PropTypes from 'prop-types'
|
||||
import { FormControl } from 'react-bootstrap'
|
||||
import useDebounce from '../../../shared/hooks/use-debounce'
|
||||
|
||||
export default function SymbolPaletteSearch({ setInput, inputRef }) {
|
||||
const [localInput, setLocalInput] = useState('')
|
||||
|
||||
// debounce the search input until a typing delay
|
||||
const debouncedLocalInput = useDebounce(localInput, 250)
|
||||
|
||||
useEffect(() => {
|
||||
setInput(debouncedLocalInput)
|
||||
}, [debouncedLocalInput, setInput])
|
||||
|
||||
const { t } = useTranslation()
|
||||
|
||||
const inputRefCallback = useCallback(
|
||||
element => {
|
||||
inputRef.current = element
|
||||
},
|
||||
[inputRef]
|
||||
)
|
||||
|
||||
return (
|
||||
<FormControl
|
||||
className="symbol-palette-search"
|
||||
type="search"
|
||||
inputRef={inputRefCallback}
|
||||
id="symbol-palette-input"
|
||||
aria-label="Search"
|
||||
value={localInput}
|
||||
placeholder={t('search') + '…'}
|
||||
onChange={event => {
|
||||
setLocalInput(event.target.value)
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}
|
||||
SymbolPaletteSearch.propTypes = {
|
||||
setInput: PropTypes.func.isRequired,
|
||||
inputRef: PropTypes.object.isRequired,
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
import { TabList, Tab } from '@reach/tabs'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
export default function SymbolPaletteTabs({ categories, disabled }) {
|
||||
return (
|
||||
<TabList aria-label="Symbol Categories" className="symbol-palette-tab-list">
|
||||
{categories.map(category => (
|
||||
<Tab
|
||||
key={category.id}
|
||||
disabled={disabled}
|
||||
className="symbol-palette-tab"
|
||||
>
|
||||
{category.label}
|
||||
</Tab>
|
||||
))}
|
||||
</TabList>
|
||||
)
|
||||
}
|
||||
SymbolPaletteTabs.propTypes = {
|
||||
categories: PropTypes.arrayOf(
|
||||
PropTypes.shape({
|
||||
id: PropTypes.string.isRequired,
|
||||
label: PropTypes.string.isRequired,
|
||||
})
|
||||
).isRequired,
|
||||
disabled: PropTypes.bool,
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
import PropTypes from 'prop-types'
|
||||
import SymbolPaletteContent from './symbol-palette-content'
|
||||
|
||||
export default function SymbolPalette({ show, handleSelect }) {
|
||||
if (!show) {
|
||||
return null
|
||||
}
|
||||
|
||||
return <SymbolPaletteContent handleSelect={handleSelect} />
|
||||
}
|
||||
SymbolPalette.propTypes = {
|
||||
show: PropTypes.bool,
|
||||
handleSelect: PropTypes.func.isRequired,
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
import App from '../../../base'
|
||||
import { react2angular } from 'react2angular'
|
||||
|
||||
import SymbolPalette from '../components/symbol-palette'
|
||||
|
||||
App.component('symbolPalette', react2angular(SymbolPalette))
|
||||
@@ -1,872 +0,0 @@
|
||||
[
|
||||
{
|
||||
"category": "Greek",
|
||||
"command": "\\alpha",
|
||||
"codepoint": "U+1D6FC",
|
||||
"description": "Lowercase Greek letter alpha",
|
||||
"aliases": ["a", "α"],
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"category": "Greek",
|
||||
"command": "\\beta",
|
||||
"codepoint": "U+1D6FD",
|
||||
"description": "Lowercase Greek letter beta",
|
||||
"aliases": ["b", "β"],
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"category": "Greek",
|
||||
"command": "\\gamma",
|
||||
"codepoint": "U+1D6FE",
|
||||
"description": "Lowercase Greek letter gamma",
|
||||
"aliases": ["γ"],
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"category": "Greek",
|
||||
"command": "\\delta",
|
||||
"codepoint": "U+1D6FF",
|
||||
"description": "Lowercase Greek letter delta",
|
||||
"aliases": ["δ"],
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"category": "Greek",
|
||||
"command": "\\varepsilon",
|
||||
"codepoint": "U+1D700",
|
||||
"description": "Lowercase Greek letter epsilon, varepsilon",
|
||||
"aliases": ["ε"],
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"category": "Greek",
|
||||
"command": "\\epsilon",
|
||||
"codepoint": "U+1D716",
|
||||
"description": "Lowercase Greek letter epsilon lunate",
|
||||
"aliases": ["ε"],
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"category": "Greek",
|
||||
"command": "\\zeta",
|
||||
"codepoint": "U+1D701",
|
||||
"description": "Lowercase Greek letter zeta",
|
||||
"aliases": ["ζ"],
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"category": "Greek",
|
||||
"command": "\\eta",
|
||||
"codepoint": "U+1D702",
|
||||
"description": "Lowercase Greek letter eta",
|
||||
"aliases": ["η"],
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"category": "Greek",
|
||||
"command": "\\vartheta",
|
||||
"codepoint": "U+1D717",
|
||||
"description": "Lowercase Greek letter curly theta, vartheta",
|
||||
"aliases": ["θ"],
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"category": "Greek",
|
||||
"command": "\\theta",
|
||||
"codepoint": "U+1D703",
|
||||
"description": "Lowercase Greek letter theta",
|
||||
"aliases": ["θ"],
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"category": "Greek",
|
||||
"command": "\\iota",
|
||||
"codepoint": "U+1D704",
|
||||
"description": "Lowercase Greek letter iota",
|
||||
"aliases": ["ι"],
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"category": "Greek",
|
||||
"command": "\\kappa",
|
||||
"codepoint": "U+1D705",
|
||||
"description": "Lowercase Greek letter kappa",
|
||||
"aliases": ["κ"],
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"category": "Greek",
|
||||
"command": "\\lambda",
|
||||
"codepoint": "U+1D706",
|
||||
"description": "Lowercase Greek letter lambda",
|
||||
"aliases": ["λ"],
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"category": "Greek",
|
||||
"command": "\\mu",
|
||||
"codepoint": "U+1D707",
|
||||
"description": "Lowercase Greek letter mu",
|
||||
"aliases": ["μ"],
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"category": "Greek",
|
||||
"command": "\\nu",
|
||||
"codepoint": "U+1D708",
|
||||
"description": "Lowercase Greek letter nu",
|
||||
"aliases": ["ν"],
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"category": "Greek",
|
||||
"command": "\\xi",
|
||||
"codepoint": "U+1D709",
|
||||
"description": "Lowercase Greek letter xi",
|
||||
"aliases": ["ξ"],
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"category": "Greek",
|
||||
"command": "\\pi",
|
||||
"codepoint": "U+1D70B",
|
||||
"description": "Lowercase Greek letter pi",
|
||||
"aliases": ["π"],
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"category": "Greek",
|
||||
"command": "\\varrho",
|
||||
"codepoint": "U+1D71A",
|
||||
"description": "Lowercase Greek letter rho, varrho",
|
||||
"aliases": ["ρ"],
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"category": "Greek",
|
||||
"command": "\\rho",
|
||||
"codepoint": "U+1D70C",
|
||||
"description": "Lowercase Greek letter rho",
|
||||
"aliases": ["ρ"],
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"category": "Greek",
|
||||
"command": "\\sigma",
|
||||
"codepoint": "U+1D70E",
|
||||
"description": "Lowercase Greek letter sigma",
|
||||
"aliases": ["σ"],
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"category": "Greek",
|
||||
"command": "\\varsigma",
|
||||
"codepoint": "U+1D70D",
|
||||
"description": "Lowercase Greek letter final sigma, varsigma",
|
||||
"aliases": ["ς"],
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"category": "Greek",
|
||||
"command": "\\tau",
|
||||
"codepoint": "U+1D70F",
|
||||
"description": "Lowercase Greek letter tau",
|
||||
"aliases": ["τ"],
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"category": "Greek",
|
||||
"command": "\\upsilon",
|
||||
"codepoint": "U+1D710",
|
||||
"description": "Lowercase Greek letter upsilon",
|
||||
"aliases": ["υ"],
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"category": "Greek",
|
||||
"command": "\\phi",
|
||||
"codepoint": "U+1D719",
|
||||
"description": "Lowercase Greek letter phi",
|
||||
"aliases": ["φ"],
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"category": "Greek",
|
||||
"command": "\\varphi",
|
||||
"codepoint": "U+1D711",
|
||||
"description": "Lowercase Greek letter phi, varphi",
|
||||
"aliases": ["φ"],
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"category": "Greek",
|
||||
"command": "\\chi",
|
||||
"codepoint": "U+1D712",
|
||||
"description": "Lowercase Greek letter chi",
|
||||
"aliases": ["χ"],
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"category": "Greek",
|
||||
"command": "\\psi",
|
||||
"codepoint": "U+1D713",
|
||||
"description": "Lowercase Greek letter psi",
|
||||
"aliases": ["ψ"],
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"category": "Greek",
|
||||
"command": "\\omega",
|
||||
"codepoint": "U+1D714",
|
||||
"description": "Lowercase Greek letter omega",
|
||||
"aliases": ["ω"],
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"category": "Greek",
|
||||
"command": "\\Gamma",
|
||||
"codepoint": "U+00393",
|
||||
"description": "Uppercase Greek letter Gamma",
|
||||
"aliases": ["Γ"],
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"category": "Greek",
|
||||
"command": "\\Delta",
|
||||
"codepoint": "U+00394",
|
||||
"description": "Uppercase Greek letter Delta",
|
||||
"aliases": ["Δ"],
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"category": "Greek",
|
||||
"command": "\\Theta",
|
||||
"codepoint": "U+00398",
|
||||
"description": "Uppercase Greek letter Theta",
|
||||
"aliases": ["Θ"],
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"category": "Greek",
|
||||
"command": "\\Lambda",
|
||||
"codepoint": "U+0039B",
|
||||
"description": "Uppercase Greek letter Lambda",
|
||||
"aliases": ["Λ"],
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"category": "Greek",
|
||||
"command": "\\Xi",
|
||||
"codepoint": "U+0039E",
|
||||
"description": "Uppercase Greek letter Xi",
|
||||
"aliases": ["Ξ"],
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"category": "Greek",
|
||||
"command": "\\Pi",
|
||||
"codepoint": "U+003A0",
|
||||
"description": "Uppercase Greek letter Pi",
|
||||
"aliases": ["Π"],
|
||||
"notes": "Use \\prod for the product."
|
||||
},
|
||||
{
|
||||
"category": "Greek",
|
||||
"command": "\\Sigma",
|
||||
"codepoint": "U+003A3",
|
||||
"description": "Uppercase Greek letter Sigma",
|
||||
"aliases": ["Σ"],
|
||||
"notes": "Use \\sum for the sum."
|
||||
},
|
||||
{
|
||||
"category": "Greek",
|
||||
"command": "\\Upsilon",
|
||||
"codepoint": "U+003A5",
|
||||
"description": "Uppercase Greek letter Upsilon",
|
||||
"aliases": ["Υ"],
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"category": "Greek",
|
||||
"command": "\\Phi",
|
||||
"codepoint": "U+003A6",
|
||||
"description": "Uppercase Greek letter Phi",
|
||||
"aliases": ["Φ"],
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"category": "Greek",
|
||||
"command": "\\Psi",
|
||||
"codepoint": "U+003A8",
|
||||
"description": "Uppercase Greek letter Psi",
|
||||
"aliases": ["Ψ"],
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"category": "Greek",
|
||||
"command": "\\Omega",
|
||||
"codepoint": "U+003A9",
|
||||
"description": "Uppercase Greek letter Omega",
|
||||
"aliases": ["Ω"],
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"category": "Relations",
|
||||
"command": "\\neq",
|
||||
"codepoint": "U+02260",
|
||||
"description": "Not equal",
|
||||
"aliases": ["!="],
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"category": "Relations",
|
||||
"command": "\\leq",
|
||||
"codepoint": "U+02264",
|
||||
"description": "Less than or equal",
|
||||
"aliases": ["<="],
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"category": "Relations",
|
||||
"command": "\\geq",
|
||||
"codepoint": "U+02265",
|
||||
"description": "Greater than or equal",
|
||||
"aliases": [">="],
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"category": "Relations",
|
||||
"command": "\\ll",
|
||||
"codepoint": "U+0226A",
|
||||
"description": "Much less than",
|
||||
"aliases": ["<<"],
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"category": "Relations",
|
||||
"command": "\\gg",
|
||||
"codepoint": "U+0226B",
|
||||
"description": "Much greater than",
|
||||
"aliases": [">>"],
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"category": "Relations",
|
||||
"command": "\\prec",
|
||||
"codepoint": "U+0227A",
|
||||
"description": "Precedes",
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"category": "Relations",
|
||||
"command": "\\succ",
|
||||
"codepoint": "U+0227B",
|
||||
"description": "Succeeds",
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"category": "Relations",
|
||||
"command": "\\in",
|
||||
"codepoint": "U+02208",
|
||||
"description": "Set membership",
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"category": "Relations",
|
||||
"command": "\\notin",
|
||||
"codepoint": "U+02209",
|
||||
"description": "Negated set membership",
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"category": "Relations",
|
||||
"command": "\\ni",
|
||||
"codepoint": "U+0220B",
|
||||
"description": "Contains",
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"category": "Relations",
|
||||
"command": "\\subset",
|
||||
"codepoint": "U+02282",
|
||||
"description": "Subset",
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"category": "Relations",
|
||||
"command": "\\subseteq",
|
||||
"codepoint": "U+02286",
|
||||
"description": "Subset or equals",
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"category": "Relations",
|
||||
"command": "\\supset",
|
||||
"codepoint": "U+02283",
|
||||
"description": "Superset",
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"category": "Relations",
|
||||
"command": "\\simeq",
|
||||
"codepoint": "U+02243",
|
||||
"description": "Similar",
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"category": "Relations",
|
||||
"command": "\\approx",
|
||||
"codepoint": "U+02248",
|
||||
"description": "Approximate",
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"category": "Relations",
|
||||
"command": "\\equiv",
|
||||
"codepoint": "U+02261",
|
||||
"description": "Identical with",
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"category": "Relations",
|
||||
"command": "\\cong",
|
||||
"codepoint": "U+02245",
|
||||
"description": "Congruent with",
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"category": "Relations",
|
||||
"command": "\\mid",
|
||||
"codepoint": "U+02223",
|
||||
"description": "Mid, divides, vertical bar, modulus, absolute value",
|
||||
"notes": "Use \\lvert...\\rvert for the absolute value."
|
||||
},
|
||||
{
|
||||
"category": "Relations",
|
||||
"command": "\\nmid",
|
||||
"codepoint": "U+02224",
|
||||
"description": "Negated mid, not divides",
|
||||
"notes": "Requires \\usepackage{amssymb}."
|
||||
},
|
||||
{
|
||||
"category": "Relations",
|
||||
"command": "\\parallel",
|
||||
"codepoint": "U+02225",
|
||||
"description": "Parallel, double vertical bar, norm",
|
||||
"notes": "Use \\lVert...\\rVert for the norm."
|
||||
},
|
||||
{
|
||||
"category": "Relations",
|
||||
"command": "\\perp",
|
||||
"codepoint": "U+027C2",
|
||||
"description": "Perpendicular",
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"category": "Operators",
|
||||
"command": "\\times",
|
||||
"codepoint": "U+000D7",
|
||||
"description": "Cross product, multiplication",
|
||||
"aliases": ["x"],
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"category": "Operators",
|
||||
"command": "\\div",
|
||||
"codepoint": "U+000F7",
|
||||
"description": "Division",
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"category": "Operators",
|
||||
"command": "\\cap",
|
||||
"codepoint": "U+02229",
|
||||
"description": "Intersection",
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"category": "Operators",
|
||||
"command": "\\cup",
|
||||
"codepoint": "U+0222A",
|
||||
"description": "Union",
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"category": "Operators",
|
||||
"command": "\\cdot",
|
||||
"codepoint": "U+022C5",
|
||||
"description": "Dot product, multiplication",
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"category": "Operators",
|
||||
"command": "\\cdots",
|
||||
"codepoint": "U+022EF",
|
||||
"description": "Centered dots",
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"category": "Operators",
|
||||
"command": "\\bullet",
|
||||
"codepoint": "U+02219",
|
||||
"description": "Bullet",
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"category": "Operators",
|
||||
"command": "\\circ",
|
||||
"codepoint": "U+025E6",
|
||||
"description": "Circle",
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"category": "Operators",
|
||||
"command": "\\wedge",
|
||||
"codepoint": "U+02227",
|
||||
"description": "Wedge, logical and",
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"category": "Operators",
|
||||
"command": "\\vee",
|
||||
"codepoint": "U+02228",
|
||||
"description": "Vee, logical or",
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"category": "Operators",
|
||||
"command": "\\setminus",
|
||||
"codepoint": "U+0005C",
|
||||
"description": "Set minus, backslash",
|
||||
"notes": "Use \\backslash for a backslash."
|
||||
},
|
||||
{
|
||||
"category": "Operators",
|
||||
"command": "\\oplus",
|
||||
"codepoint": "U+02295",
|
||||
"description": "Plus sign in circle",
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"category": "Operators",
|
||||
"command": "\\otimes",
|
||||
"codepoint": "U+02297",
|
||||
"description": "Multiply sign in circle",
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"category": "Operators",
|
||||
"command": "\\sum",
|
||||
"codepoint": "U+02211",
|
||||
"description": "Summation operator",
|
||||
"notes": "Use \\Sigma for the letter Sigma."
|
||||
},
|
||||
{
|
||||
"category": "Operators",
|
||||
"command": "\\prod",
|
||||
"codepoint": "U+0220F",
|
||||
"description": "Product operator",
|
||||
"notes": "Use \\Pi for the letter Pi."
|
||||
},
|
||||
{
|
||||
"category": "Operators",
|
||||
"command": "\\bigcap",
|
||||
"codepoint": "U+022C2",
|
||||
"description": "Intersection operator",
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"category": "Operators",
|
||||
"command": "\\bigcup",
|
||||
"codepoint": "U+022C3",
|
||||
"description": "Union operator",
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"category": "Operators",
|
||||
"command": "\\int",
|
||||
"codepoint": "U+0222B",
|
||||
"description": "Integral operator",
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"category": "Operators",
|
||||
"command": "\\iint",
|
||||
"codepoint": "U+0222C",
|
||||
"description": "Double integral operator",
|
||||
"notes": "Requires \\usepackage{amsmath}."
|
||||
},
|
||||
{
|
||||
"category": "Operators",
|
||||
"command": "\\iiint",
|
||||
"codepoint": "U+0222D",
|
||||
"description": "Triple integral operator",
|
||||
"notes": "Requires \\usepackage{amsmath}."
|
||||
},
|
||||
{
|
||||
"category": "Arrows",
|
||||
"command": "\\leftarrow",
|
||||
"codepoint": "U+02190",
|
||||
"description": "Leftward arrow",
|
||||
"aliases": ["<-"],
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"category": "Arrows",
|
||||
"command": "\\rightarrow",
|
||||
"codepoint": "U+02192",
|
||||
"description": "Rightward arrow",
|
||||
"aliases": ["->"],
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"category": "Arrows",
|
||||
"command": "\\leftrightarrow",
|
||||
"codepoint": "U+02194",
|
||||
"description": "Left and right arrow",
|
||||
"aliases": ["<->"],
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"category": "Arrows",
|
||||
"command": "\\uparrow",
|
||||
"codepoint": "U+02191",
|
||||
"description": "Upward arrow",
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"category": "Arrows",
|
||||
"command": "\\downarrow",
|
||||
"codepoint": "U+02193",
|
||||
"description": "Downward arrow",
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"category": "Arrows",
|
||||
"command": "\\Leftarrow",
|
||||
"codepoint": "U+021D0",
|
||||
"description": "Is implied by",
|
||||
"aliases": ["<="],
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"category": "Arrows",
|
||||
"command": "\\Rightarrow",
|
||||
"codepoint": "U+021D2",
|
||||
"description": "Implies",
|
||||
"aliases": ["=>"],
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"category": "Arrows",
|
||||
"command": "\\Leftrightarrow",
|
||||
"codepoint": "U+021D4",
|
||||
"description": "Left and right double arrow",
|
||||
"aliases": ["<=>"],
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"category": "Arrows",
|
||||
"command": "\\mapsto",
|
||||
"codepoint": "U+021A6",
|
||||
"description": "Maps to, rightward",
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"category": "Arrows",
|
||||
"command": "\\nearrow",
|
||||
"codepoint": "U+02197",
|
||||
"description": "NE pointing arrow",
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"category": "Arrows",
|
||||
"command": "\\searrow",
|
||||
"codepoint": "U+02198",
|
||||
"description": "SE pointing arrow",
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"category": "Arrows",
|
||||
"command": "\\rightleftharpoons",
|
||||
"codepoint": "U+021CC",
|
||||
"description": "Right harpoon over left",
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"category": "Arrows",
|
||||
"command": "\\leftharpoonup",
|
||||
"codepoint": "U+021BC",
|
||||
"description": "Left harpoon up",
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"category": "Arrows",
|
||||
"command": "\\rightharpoonup",
|
||||
"codepoint": "U+021C0",
|
||||
"description": "Right harpoon up",
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"category": "Arrows",
|
||||
"command": "\\leftharpoondown",
|
||||
"codepoint": "U+021BD",
|
||||
"description": "Left harpoon down",
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"category": "Arrows",
|
||||
"command": "\\rightharpoondown",
|
||||
"codepoint": "U+021C1",
|
||||
"description": "Right harpoon down",
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"category": "Misc",
|
||||
"command": "\\infty",
|
||||
"codepoint": "U+0221E",
|
||||
"description": "Infinity",
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"category": "Misc",
|
||||
"command": "\\partial",
|
||||
"codepoint": "U+1D715",
|
||||
"description": "Partial differential",
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"category": "Misc",
|
||||
"command": "\\nabla",
|
||||
"codepoint": "U+02207",
|
||||
"description": "Nabla, del, hamilton operator",
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"category": "Misc",
|
||||
"command": "\\varnothing",
|
||||
"codepoint": "U+02300",
|
||||
"description": "Empty set",
|
||||
"notes": "Requires \\usepackage{amssymb}."
|
||||
},
|
||||
{
|
||||
"category": "Misc",
|
||||
"command": "\\forall",
|
||||
"codepoint": "U+02200",
|
||||
"description": "For all",
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"category": "Misc",
|
||||
"command": "\\exists",
|
||||
"codepoint": "U+02203",
|
||||
"description": "There exists",
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"category": "Misc",
|
||||
"command": "\\neg",
|
||||
"codepoint": "U+000AC",
|
||||
"description": "Not sign",
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"category": "Misc",
|
||||
"command": "\\Re",
|
||||
"codepoint": "U+0211C",
|
||||
"description": "Real part",
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"category": "Misc",
|
||||
"command": "\\Im",
|
||||
"codepoint": "U+02111",
|
||||
"description": "Imaginary part",
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"category": "Misc",
|
||||
"command": "\\Box",
|
||||
"codepoint": "U+025A1",
|
||||
"description": "Square",
|
||||
"notes": "Requires \\usepackage{amssymb}."
|
||||
},
|
||||
{
|
||||
"category": "Misc",
|
||||
"command": "\\triangle",
|
||||
"codepoint": "U+025B3",
|
||||
"description": "Triangle",
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"category": "Misc",
|
||||
"command": "\\aleph",
|
||||
"codepoint": "U+02135",
|
||||
"description": "Hebrew letter aleph",
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"category": "Misc",
|
||||
"command": "\\wp",
|
||||
"codepoint": "U+02118",
|
||||
"description": "Weierstrass letter p",
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"category": "Misc",
|
||||
"command": "\\#",
|
||||
"codepoint": "U+00023",
|
||||
"description": "Number sign, hashtag",
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"category": "Misc",
|
||||
"command": "\\$",
|
||||
"codepoint": "U+00024",
|
||||
"description": "Dollar sign",
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"category": "Misc",
|
||||
"command": "\\%",
|
||||
"codepoint": "U+00025",
|
||||
"description": "Percent sign",
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"category": "Misc",
|
||||
"command": "\\&",
|
||||
"codepoint": "U+00026",
|
||||
"description": "Et sign, and, ampersand",
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"category": "Misc",
|
||||
"command": "\\{",
|
||||
"codepoint": "U+0007B",
|
||||
"description": "Left curly brace",
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"category": "Misc",
|
||||
"command": "\\}",
|
||||
"codepoint": "U+0007D",
|
||||
"description": "Right curly brace",
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"category": "Misc",
|
||||
"command": "\\langle",
|
||||
"codepoint": "U+027E8",
|
||||
"description": "Left angle bracket, bra",
|
||||
"notes": ""
|
||||
},
|
||||
{
|
||||
"category": "Misc",
|
||||
"command": "\\rangle",
|
||||
"codepoint": "U+027E9",
|
||||
"description": "Right angle bracket, ket",
|
||||
"notes": ""
|
||||
}
|
||||
]
|
||||
@@ -1,45 +0,0 @@
|
||||
import symbols from '../data/symbols.json'
|
||||
|
||||
export function createCategories(t) {
|
||||
return [
|
||||
{
|
||||
id: 'Greek',
|
||||
label: t('category_greek'),
|
||||
},
|
||||
{
|
||||
id: 'Arrows',
|
||||
label: t('category_arrows'),
|
||||
},
|
||||
{
|
||||
id: 'Operators',
|
||||
label: t('category_operators'),
|
||||
},
|
||||
{
|
||||
id: 'Relations',
|
||||
label: t('category_relations'),
|
||||
},
|
||||
{
|
||||
id: 'Misc',
|
||||
label: t('category_misc'),
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
export function buildCategorisedSymbols(categories) {
|
||||
const output = {}
|
||||
|
||||
for (const category of categories) {
|
||||
output[category.id] = []
|
||||
}
|
||||
|
||||
for (const item of symbols) {
|
||||
if (item.category in output) {
|
||||
item.character = String.fromCodePoint(
|
||||
parseInt(item.codepoint.replace(/^U\+0*/, ''), 16)
|
||||
)
|
||||
output[item.category].push(item)
|
||||
}
|
||||
}
|
||||
|
||||
return output
|
||||
}
|
||||
@@ -19,7 +19,6 @@ import './components/spellMenu'
|
||||
import './directives/aceEditor'
|
||||
import './directives/toggleSwitch'
|
||||
import './controllers/SavingNotificationController'
|
||||
import '../../features/symbol-palette/controllers/symbol-palette-controller'
|
||||
let EditorManager
|
||||
|
||||
export default EditorManager = (function () {
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
import Icon from './icon'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { memo } from 'react'
|
||||
|
||||
function UpgradeBenefits() {
|
||||
const { t } = useTranslation()
|
||||
|
||||
return (
|
||||
<ul className="list-unstyled upgrade-benefits">
|
||||
<li>
|
||||
<Icon type="check" />
|
||||
|
||||
{t('unlimited_projects')}
|
||||
</li>
|
||||
<li>
|
||||
<Icon type="check" />
|
||||
|
||||
{t('collabs_per_proj', { collabcount: 'Multiple' })}
|
||||
</li>
|
||||
<li>
|
||||
<Icon type="check" />
|
||||
|
||||
{t('full_doc_history')}
|
||||
</li>
|
||||
<li>
|
||||
<Icon type="check" />
|
||||
|
||||
{t('sync_to_dropbox')}
|
||||
</li>
|
||||
<li>
|
||||
<Icon type="check" />
|
||||
|
||||
{t('sync_to_github')}
|
||||
</li>
|
||||
<li>
|
||||
<Icon type="check" />
|
||||
|
||||
{t('compile_larger_projects')}
|
||||
</li>
|
||||
</ul>
|
||||
)
|
||||
}
|
||||
|
||||
export default memo(UpgradeBenefits)
|
||||
@@ -29,6 +29,9 @@ EditorContext.Provider.propTypes = {
|
||||
hasPremiumCompile: PropTypes.bool,
|
||||
loading: PropTypes.bool,
|
||||
renameProject: PropTypes.func.isRequired,
|
||||
showSymbolPalette: PropTypes.bool,
|
||||
toggleSymbolPalette: PropTypes.func,
|
||||
insertSymbol: PropTypes.func,
|
||||
isProjectOwner: PropTypes.bool,
|
||||
isRestrictedTokenMember: PropTypes.bool,
|
||||
rootFolder: PropTypes.shape({
|
||||
@@ -72,6 +75,9 @@ export function EditorProvider({ children, settings }) {
|
||||
const [projectName, setProjectName] = useScopeValue('project.name')
|
||||
const [rootFolder] = useScopeValue('rootFolder')
|
||||
const [permissionsLevel] = useScopeValue('permissionsLevel')
|
||||
const [showSymbolPalette] = useScopeValue('editor.showSymbolPalette')
|
||||
const [toggleSymbolPalette] = useScopeValue('editor.toggleSymbolPalette')
|
||||
const [insertSymbol] = useScopeValue('editor.insertSymbol')
|
||||
|
||||
useEffect(() => {
|
||||
if (ide?.socket) {
|
||||
@@ -123,6 +129,9 @@ export function EditorProvider({ children, settings }) {
|
||||
isProjectOwner: owner?._id === window.user.id,
|
||||
isRestrictedTokenMember: window.isRestrictedTokenMember,
|
||||
rootFolder,
|
||||
showSymbolPalette,
|
||||
toggleSymbolPalette,
|
||||
insertSymbol,
|
||||
}),
|
||||
[
|
||||
cobranding,
|
||||
@@ -132,6 +141,9 @@ export function EditorProvider({ children, settings }) {
|
||||
permissionsLevel,
|
||||
owner?._id,
|
||||
rootFolder,
|
||||
showSymbolPalette,
|
||||
toggleSymbolPalette,
|
||||
insertSymbol,
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
import SymbolPalette from '../js/features/symbol-palette/components/symbol-palette'
|
||||
|
||||
export const Interactive = args => {
|
||||
return (
|
||||
<div style={{ maxWidth: 700, marginTop: 120 }}>
|
||||
<SymbolPalette {...args} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default {
|
||||
title: 'Symbol Palette',
|
||||
component: SymbolPalette,
|
||||
args: {
|
||||
show: true,
|
||||
},
|
||||
argTypes: {
|
||||
handleSelect: { action: 'handleSelect' },
|
||||
},
|
||||
}
|
||||
@@ -99,3 +99,7 @@
|
||||
@import 'app/editor/history-v2.less';
|
||||
@import 'app/metrics.less';
|
||||
@import 'app/open-in-overleaf.less';
|
||||
|
||||
// module styles
|
||||
// TODO: find a way for modules to add styles dynamically
|
||||
@import 'modules/symbol-palette.less';
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
@import './editor/publish-modal.less';
|
||||
@import './editor/outline.less';
|
||||
@import './editor/logs.less';
|
||||
@import './editor/symbol-palette.less';
|
||||
|
||||
@ui-layout-toggler-def-height: 50px;
|
||||
@ui-resizer-size: 7px;
|
||||
|
||||
@@ -330,6 +330,14 @@
|
||||
color: @toolbar-btn-active-color;
|
||||
background-color: @toolbar-btn-active-bg-color;
|
||||
box-shadow: @toolbar-btn-active-shadow;
|
||||
|
||||
&:focus {
|
||||
color: @toolbar-btn-active-color;
|
||||
|
||||
&:not(:focus-visible) {
|
||||
outline: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&:focus {
|
||||
|
||||
@@ -136,3 +136,4 @@
|
||||
@symbol-palette-item-color: @ol-blue-gray-3;
|
||||
@symbol-palette-selected-tab-bg: #fff;
|
||||
@symbol-palette-selected-tab-color: @ol-blue;
|
||||
@symbol-palette-text-shadow-color: @ol-blue-gray-1;
|
||||
|
||||
@@ -1121,3 +1121,4 @@
|
||||
@symbol-palette-item-color: #fff;
|
||||
@symbol-palette-selected-tab-bg: @ol-blue-gray-4;
|
||||
@symbol-palette-selected-tab-color: #fff;
|
||||
@symbol-palette-text-shadow-color: @ol-blue-gray-6;
|
||||
|
||||
155
services/web/frontend/stylesheets/modules/symbol-palette.less
Normal file
155
services/web/frontend/stylesheets/modules/symbol-palette.less
Normal file
@@ -0,0 +1,155 @@
|
||||
.symbol-palette-container {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
position: relative;
|
||||
|
||||
.symbol-palette {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background: @symbol-palette-bg;
|
||||
color: @symbol-palette-color;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
min-height: 220px; // allow space for the overlay contents
|
||||
}
|
||||
|
||||
.symbol-palette-header-outer {
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
flex-wrap: nowrap;
|
||||
justify-content: space-between;
|
||||
align-items: flex-start;
|
||||
font-family: @font-family-sans-serif;
|
||||
font-size: 16px;
|
||||
background: @symbol-palette-header-background;
|
||||
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
.symbol-palette-header {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.symbol-palette-tab-list[data-reach-tab-list] {
|
||||
background: none;
|
||||
border-bottom: none;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.symbol-palette-tab[data-reach-tab][data-selected] {
|
||||
background: @symbol-palette-selected-tab-bg;
|
||||
color: @symbol-palette-selected-tab-color;
|
||||
border-bottom-color: transparent;
|
||||
}
|
||||
|
||||
.symbol-palette-body {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.symbol-palette-items {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
padding: @padding-xs;
|
||||
}
|
||||
|
||||
.symbol-palette-item {
|
||||
font-family: 'Stix Two Math', serif;
|
||||
font-size: 24px;
|
||||
line-height: 42px;
|
||||
height: 42px;
|
||||
width: 42px;
|
||||
margin: @margin-xs;
|
||||
color: @symbol-palette-item-color;
|
||||
background: @symbol-palette-item-bg;
|
||||
border: 1px solid transparent;
|
||||
border-radius: @border-radius-base;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.symbol-palette-item-command {
|
||||
font-family: monospace;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.symbol-palette-item-notes {
|
||||
margin-top: @margin-xs;
|
||||
}
|
||||
|
||||
.symbol-palette-empty {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: @padding-sm;
|
||||
}
|
||||
|
||||
.symbol-palette-search {
|
||||
padding: 2px @padding-sm;
|
||||
margin: @margin-xs;
|
||||
line-height: 1;
|
||||
height: auto;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.symbol-palette-header-group {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
white-space: nowrap;
|
||||
margin-left: @margin-xs;
|
||||
}
|
||||
|
||||
.symbol-palette-info-link,
|
||||
.symbol-palette-info-link:focus,
|
||||
.symbol-palette-info-link:hover {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.symbol-palette-close-button {
|
||||
background: transparent;
|
||||
color: @symbol-palette-color;
|
||||
padding-left: @padding-sm;
|
||||
padding-right: @padding-sm;
|
||||
margin-left: @margin-xs;
|
||||
font-size: 24px;
|
||||
font-weight: bold;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.symbol-palette-overlay {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
background: fade(@symbol-palette-bg, 75%);
|
||||
color: @symbol-palette-color;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 0 @padding-lg @padding-sm;
|
||||
align-items: center;
|
||||
text-shadow: 0 0 8px @symbol-palette-text-shadow-color;
|
||||
min-height: 200px;
|
||||
overflow: auto;
|
||||
|
||||
h4 {
|
||||
font-weight: bold;
|
||||
color: @symbol-palette-color;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.symbol-palette-close-button {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.upgrade-benefits {
|
||||
column-count: 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -30,7 +30,6 @@
|
||||
"log_entry_maximum_entries": "Maximum log entries limit hit",
|
||||
"log_entry_maximum_entries_title": "__total__ issues total. Showing the first __displayed__",
|
||||
"log_entry_maximum_entries_message": "<0>Tip</0>: Try to fix the first error and recompile. Often one error causes many later error messages",
|
||||
"log_entry_description": "Log entry with level: __level__",
|
||||
"navigate_log_source": "Navigate to log position in source code: __location__",
|
||||
"other_output_files": "Download other output files",
|
||||
"refresh": "Refresh",
|
||||
@@ -1484,7 +1483,10 @@
|
||||
"category_relations": "Relations",
|
||||
"category_misc": "Misc",
|
||||
"no_symbols_found": "No symbols found",
|
||||
"find_out_more_about_latex_symbols": "Find out more about LaTeX symbols",
|
||||
"learn_more_about_the_symbol_palette": "Learn more about the Symbol Palette and how to use it",
|
||||
"find_the_symbols_you_need_with_premium": "Find the symbols you need faster with Overleaf Premium",
|
||||
"with_premium_subscription_you_also_get": "With an Overleaf Premium subscription you also get",
|
||||
"try_premium_for_free": "Try Premium for free",
|
||||
"search": "Search",
|
||||
"also": "Also",
|
||||
"add_email": "Add Email",
|
||||
|
||||
@@ -1,104 +0,0 @@
|
||||
import { expect } from 'chai'
|
||||
import sinon from 'sinon'
|
||||
import { screen, render, fireEvent, waitFor } from '@testing-library/react'
|
||||
import SymbolPalette from '../../../../../frontend/js/features/symbol-palette/components/symbol-palette'
|
||||
|
||||
describe('symbol palette', function () {
|
||||
let clock
|
||||
|
||||
before(function () {
|
||||
clock = sinon.useFakeTimers({
|
||||
toFake: ['setTimeout', 'clearTimeout', 'setInterval', 'clearInterval'],
|
||||
})
|
||||
})
|
||||
|
||||
after(function () {
|
||||
clock.runAll()
|
||||
clock.restore()
|
||||
})
|
||||
|
||||
it('handles keyboard interaction', async function () {
|
||||
this.timeout(10000)
|
||||
|
||||
const handleSelect = sinon.stub()
|
||||
|
||||
const { container } = render(
|
||||
<SymbolPalette show handleSelect={handleSelect} />
|
||||
)
|
||||
|
||||
// check the number of tabs
|
||||
const tabs = await screen.findAllByRole('tab')
|
||||
expect(tabs).to.have.length(5)
|
||||
|
||||
let selectedTab
|
||||
let symbols
|
||||
|
||||
// the first tab should be selected
|
||||
selectedTab = await screen.getByRole('tab', { selected: true })
|
||||
expect(selectedTab.textContent).to.equal('Greek')
|
||||
symbols = await screen.findAllByRole('option')
|
||||
expect(symbols).to.have.length(39)
|
||||
|
||||
// click to select the third tab
|
||||
tabs[2].click()
|
||||
selectedTab = await screen.getByRole('tab', { selected: true })
|
||||
expect(selectedTab.textContent).to.equal('Operators')
|
||||
symbols = await screen.findAllByRole('option')
|
||||
expect(symbols).to.have.length(20)
|
||||
|
||||
// press the left arrow to select the second tab
|
||||
fireEvent.keyDown(selectedTab, { key: 'ArrowLeft' })
|
||||
selectedTab = await screen.getByRole('tab', { selected: true })
|
||||
expect(selectedTab.textContent).to.equal('Arrows')
|
||||
symbols = await screen.findAllByRole('option')
|
||||
expect(symbols).to.have.length(16)
|
||||
|
||||
// select the search input
|
||||
const input = await screen.getByRole('searchbox')
|
||||
input.click()
|
||||
|
||||
// type in the search input
|
||||
fireEvent.change(input, { target: { value: 'pi' } })
|
||||
|
||||
// make sure all scheduled microtasks have executed
|
||||
clock.runAll()
|
||||
|
||||
// wait for the symbols to be filtered
|
||||
await waitFor(async () => {
|
||||
symbols = await screen.findAllByRole('option')
|
||||
expect(symbols).to.have.length(2)
|
||||
})
|
||||
|
||||
// check the tabs are disabled
|
||||
expect(selectedTab.disabled).to.be.true
|
||||
|
||||
// press Tab to select the symbols
|
||||
fireEvent.keyDown(container, { key: 'Tab' })
|
||||
|
||||
// get the selected symbol
|
||||
let selectedSymbol
|
||||
|
||||
selectedSymbol = await screen.getByRole('option', { selected: true })
|
||||
expect(selectedSymbol.textContent).to.equal('𝜋')
|
||||
|
||||
// move to the next symbol
|
||||
fireEvent.keyDown(selectedSymbol, { key: 'ArrowRight' })
|
||||
|
||||
// wait for the symbol to be selected
|
||||
selectedSymbol = await screen.getByRole('option', { selected: true })
|
||||
expect(selectedSymbol.textContent).to.equal('Π')
|
||||
|
||||
// click on the selected symbol
|
||||
selectedSymbol.click()
|
||||
|
||||
expect(handleSelect).to.have.been.calledWith({
|
||||
aliases: ['Π'],
|
||||
category: 'Greek',
|
||||
character: 'Π',
|
||||
codepoint: 'U+003A0',
|
||||
command: '\\Pi',
|
||||
description: 'Uppercase Greek letter Pi',
|
||||
notes: 'Use \\prod for the product.',
|
||||
})
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user