mirror of
https://github.com/overleaf/overleaf.git
synced 2024-10-24 21:12:38 -04:00
92eade7502
[web] BS5 pdf toolbar GitOrigin-RevId: a04091c9e936e52f47c3977b3149ffe613d43bb9
366 lines
11 KiB
TypeScript
366 lines
11 KiB
TypeScript
import { useTranslation } from 'react-i18next'
|
|
import { memo } from 'react'
|
|
import classNames from 'classnames'
|
|
import { useDetachCompileContext as useCompileContext } from '../../../shared/context/detach-compile-context'
|
|
import { useStopOnFirstError } from '../../../shared/hooks/use-stop-on-first-error'
|
|
import SplitMenu from '../../../shared/components/split-menu'
|
|
import Icon from '../../../shared/components/icon'
|
|
import * as eventTracking from '../../../infrastructure/event-tracking'
|
|
import BootstrapVersionSwitcher from '@/features/ui/components/bootstrap-5/bootstrap-version-switcher'
|
|
import OLTooltip from '@/features/ui/components/ol/ol-tooltip'
|
|
import { Spinner } from 'react-bootstrap-5'
|
|
import {
|
|
Dropdown,
|
|
DropdownDivider,
|
|
DropdownHeader,
|
|
DropdownItem,
|
|
DropdownMenu,
|
|
DropdownToggle,
|
|
} from '@/features/ui/components/bootstrap-5/dropdown-menu'
|
|
import OLButton from '@/features/ui/components/ol/ol-button'
|
|
import OLButtonGroup from '@/features/ui/components/ol/ol-button-group'
|
|
import { bsVersion } from '@/features/utils/bootstrap-5'
|
|
|
|
const modifierKey = /Mac/i.test(navigator.platform) ? 'Cmd' : 'Ctrl'
|
|
|
|
function sendEventAndSet<T extends boolean>(
|
|
value: T,
|
|
setter: (value: T) => void,
|
|
settingName: string
|
|
) {
|
|
eventTracking.sendMB('recompile-setting-changed', {
|
|
setting: settingName,
|
|
settingVal: value,
|
|
})
|
|
setter(value)
|
|
}
|
|
|
|
function PdfCompileButton() {
|
|
const {
|
|
animateCompileDropdownArrow,
|
|
autoCompile,
|
|
compiling,
|
|
draft,
|
|
hasChanges,
|
|
setAnimateCompileDropdownArrow,
|
|
setAutoCompile,
|
|
setDraft,
|
|
setStopOnValidationError,
|
|
stopOnFirstError,
|
|
stopOnValidationError,
|
|
startCompile,
|
|
stopCompile,
|
|
recompileFromScratch,
|
|
} = useCompileContext()
|
|
const { enableStopOnFirstError, disableStopOnFirstError } =
|
|
useStopOnFirstError({ eventSource: 'dropdown' })
|
|
|
|
const { t } = useTranslation()
|
|
|
|
const fromScratchWithEvent = () => {
|
|
eventTracking.sendMB('recompile-setting-changed', {
|
|
setting: 'from-scratch',
|
|
})
|
|
recompileFromScratch()
|
|
}
|
|
|
|
const compileButtonLabel = compiling ? `${t('compiling')}…` : t('recompile')
|
|
const tooltipElement = (
|
|
<>
|
|
{t('recompile_pdf')}{' '}
|
|
<span className="keyboard-shortcut">({modifierKey} + Enter)</span>
|
|
</>
|
|
)
|
|
|
|
const dropdownToggleClassName = classNames(
|
|
{
|
|
'detach-compile-button-animate': animateCompileDropdownArrow,
|
|
'btn-striped-animated': hasChanges,
|
|
'no-left-border': true,
|
|
},
|
|
bsVersion({ bs5: 'dropdown-button-toggle' })
|
|
)
|
|
|
|
const buttonClassName = classNames({
|
|
'btn-striped-animated': hasChanges,
|
|
'no-left-radius': true,
|
|
})
|
|
|
|
return (
|
|
<BootstrapVersionSwitcher
|
|
bs3={
|
|
<SplitMenu
|
|
bsStyle="primary"
|
|
bsSize="xs"
|
|
disabled={compiling}
|
|
button={{
|
|
tooltip: {
|
|
description: tooltipElement,
|
|
id: 'compile',
|
|
tooltipProps: { className: 'keyboard-tooltip' },
|
|
overlayProps: { delayShow: 500 },
|
|
},
|
|
icon: { type: 'refresh', spin: compiling },
|
|
onClick: () => startCompile(),
|
|
text: compileButtonLabel,
|
|
className: buttonClassName,
|
|
}}
|
|
dropdownToggle={{
|
|
'aria-label': t('toggle_compile_options_menu'),
|
|
handleAnimationEnd: () => setAnimateCompileDropdownArrow(false),
|
|
className: dropdownToggleClassName,
|
|
}}
|
|
dropdown={{
|
|
id: 'pdf-recompile-dropdown',
|
|
}}
|
|
>
|
|
<SplitMenu.Item header>{t('auto_compile')}</SplitMenu.Item>
|
|
|
|
<SplitMenu.Item
|
|
onSelect={() =>
|
|
sendEventAndSet(true, setAutoCompile, 'auto-compile')
|
|
}
|
|
>
|
|
<Icon type={autoCompile ? 'check' : ''} fw />
|
|
{t('on')}
|
|
</SplitMenu.Item>
|
|
|
|
<SplitMenu.Item
|
|
onSelect={() =>
|
|
sendEventAndSet(false, setAutoCompile, 'auto-compile')
|
|
}
|
|
>
|
|
<Icon type={!autoCompile ? 'check' : ''} fw />
|
|
{t('off')}
|
|
</SplitMenu.Item>
|
|
|
|
<SplitMenu.Item header>{t('compile_mode')}</SplitMenu.Item>
|
|
|
|
<SplitMenu.Item
|
|
onSelect={() => sendEventAndSet(false, setDraft, 'compile-mode')}
|
|
>
|
|
<Icon type={!draft ? 'check' : ''} fw />
|
|
{t('normal')}
|
|
</SplitMenu.Item>
|
|
|
|
<SplitMenu.Item
|
|
onSelect={() => sendEventAndSet(true, setDraft, 'compile-mode')}
|
|
>
|
|
<Icon type={draft ? 'check' : ''} fw />
|
|
{t('fast')} <span className="subdued">[draft]</span>
|
|
</SplitMenu.Item>
|
|
|
|
<SplitMenu.Item header>Syntax Checks</SplitMenu.Item>
|
|
|
|
<SplitMenu.Item
|
|
onSelect={() =>
|
|
sendEventAndSet(true, setStopOnValidationError, 'syntax-check')
|
|
}
|
|
>
|
|
<Icon type={stopOnValidationError ? 'check' : ''} fw />
|
|
{t('stop_on_validation_error')}
|
|
</SplitMenu.Item>
|
|
|
|
<SplitMenu.Item
|
|
onSelect={() =>
|
|
sendEventAndSet(false, setStopOnValidationError, 'syntax-check')
|
|
}
|
|
>
|
|
<Icon type={!stopOnValidationError ? 'check' : ''} fw />
|
|
{t('ignore_validation_errors')}
|
|
</SplitMenu.Item>
|
|
|
|
<SplitMenu.Item header>{t('compile_error_handling')}</SplitMenu.Item>
|
|
|
|
<SplitMenu.Item onSelect={enableStopOnFirstError}>
|
|
<Icon type={stopOnFirstError ? 'check' : ''} fw />
|
|
{t('stop_on_first_error')}
|
|
</SplitMenu.Item>
|
|
|
|
<SplitMenu.Item onSelect={disableStopOnFirstError}>
|
|
<Icon type={!stopOnFirstError ? 'check' : ''} fw />
|
|
{t('try_to_compile_despite_errors')}
|
|
</SplitMenu.Item>
|
|
|
|
<SplitMenu.Item divider />
|
|
|
|
<SplitMenu.Item
|
|
onSelect={() => stopCompile()}
|
|
disabled={!compiling}
|
|
aria-disabled={!compiling}
|
|
>
|
|
{t('stop_compile')}
|
|
</SplitMenu.Item>
|
|
|
|
<SplitMenu.Item
|
|
onSelect={fromScratchWithEvent}
|
|
disabled={compiling}
|
|
aria-disabled={compiling}
|
|
>
|
|
{t('recompile_from_scratch')}
|
|
</SplitMenu.Item>
|
|
</SplitMenu>
|
|
}
|
|
bs5={
|
|
<Dropdown as={OLButtonGroup} autoClose="outside">
|
|
<OLTooltip
|
|
description={tooltipElement}
|
|
id="compile"
|
|
tooltipProps={{ className: 'keyboard-tooltip' }}
|
|
overlayProps={{ delay: { show: 500, hide: 0 } }}
|
|
>
|
|
<OLButton
|
|
variant="primary"
|
|
size="sm"
|
|
disabled={compiling}
|
|
onClick={() => startCompile()}
|
|
leadingIcon={
|
|
compiling ? (
|
|
<Spinner
|
|
animation="border"
|
|
aria-hidden="true"
|
|
size="sm"
|
|
role="status"
|
|
/>
|
|
) : null
|
|
}
|
|
className={buttonClassName}
|
|
>
|
|
{compileButtonLabel}
|
|
</OLButton>
|
|
</OLTooltip>
|
|
|
|
<DropdownToggle
|
|
split
|
|
variant="primary"
|
|
id="pdf-recompile-dropdown"
|
|
size="sm"
|
|
aria-label={t('toggle_compile_options_menu')}
|
|
className={dropdownToggleClassName}
|
|
/>
|
|
|
|
<DropdownMenu>
|
|
<DropdownHeader>{t('auto_compile')}</DropdownHeader>
|
|
<li role="none">
|
|
<DropdownItem
|
|
as="button"
|
|
onClick={() =>
|
|
sendEventAndSet(true, setAutoCompile, 'auto-compile')
|
|
}
|
|
trailingIcon={autoCompile ? 'check' : null}
|
|
>
|
|
{t('on')}
|
|
</DropdownItem>
|
|
</li>
|
|
<li role="none">
|
|
<DropdownItem
|
|
as="button"
|
|
onClick={() =>
|
|
sendEventAndSet(false, setAutoCompile, 'auto-compile')
|
|
}
|
|
trailingIcon={!autoCompile ? 'check' : null}
|
|
>
|
|
{t('off')}
|
|
</DropdownItem>
|
|
</li>
|
|
<DropdownDivider />
|
|
<DropdownHeader>{t('compile_mode')}</DropdownHeader>
|
|
<li role="none">
|
|
<DropdownItem
|
|
as="button"
|
|
onClick={() => sendEventAndSet(false, setDraft, 'compile-mode')}
|
|
trailingIcon={!draft ? 'check' : null}
|
|
>
|
|
{t('normal')}
|
|
</DropdownItem>
|
|
</li>
|
|
<li role="none">
|
|
<DropdownItem
|
|
as="button"
|
|
onClick={() => sendEventAndSet(true, setDraft, 'compile-mode')}
|
|
trailingIcon={draft ? 'check' : null}
|
|
>
|
|
{t('fast')} <span className="subdued">[draft]</span>
|
|
</DropdownItem>
|
|
</li>
|
|
<DropdownDivider />
|
|
<DropdownHeader>Syntax Checks</DropdownHeader>
|
|
<li role="none">
|
|
<DropdownItem
|
|
as="button"
|
|
onClick={() =>
|
|
sendEventAndSet(
|
|
true,
|
|
setStopOnValidationError,
|
|
'syntax-check'
|
|
)
|
|
}
|
|
trailingIcon={stopOnValidationError ? 'check' : null}
|
|
>
|
|
{t('stop_on_validation_error')}
|
|
</DropdownItem>
|
|
</li>
|
|
<li role="none">
|
|
<DropdownItem
|
|
as="button"
|
|
onClick={() =>
|
|
sendEventAndSet(
|
|
false,
|
|
setStopOnValidationError,
|
|
'syntax-check'
|
|
)
|
|
}
|
|
trailingIcon={!stopOnValidationError ? 'check' : null}
|
|
>
|
|
{t('ignore_validation_errors')}
|
|
</DropdownItem>
|
|
</li>
|
|
<DropdownDivider />
|
|
<DropdownHeader>{t('compile_error_handling')}</DropdownHeader>
|
|
<li role="none">
|
|
<DropdownItem
|
|
as="button"
|
|
onClick={enableStopOnFirstError}
|
|
trailingIcon={stopOnFirstError ? 'check' : null}
|
|
>
|
|
{t('stop_on_first_error')}
|
|
</DropdownItem>
|
|
</li>
|
|
<li role="none">
|
|
<DropdownItem
|
|
as="button"
|
|
onClick={disableStopOnFirstError}
|
|
trailingIcon={!stopOnFirstError ? 'check' : null}
|
|
>
|
|
{t('try_to_compile_despite_errors')}
|
|
</DropdownItem>
|
|
</li>
|
|
<DropdownDivider />
|
|
<li role="none">
|
|
<DropdownItem
|
|
as="button"
|
|
onClick={() => stopCompile()}
|
|
disabled={!compiling}
|
|
aria-disabled={!compiling}
|
|
>
|
|
{t('stop_compile')}
|
|
</DropdownItem>
|
|
</li>
|
|
<li role="none">
|
|
<DropdownItem
|
|
as="button"
|
|
onClick={fromScratchWithEvent}
|
|
disabled={compiling}
|
|
aria-disabled={compiling}
|
|
>
|
|
{t('recompile_from_scratch')}
|
|
</DropdownItem>
|
|
</li>
|
|
</DropdownMenu>
|
|
</Dropdown>
|
|
}
|
|
/>
|
|
)
|
|
}
|
|
|
|
export default memo(PdfCompileButton)
|