diff --git a/services/web/app/views/project/ide-react-detached.pug b/services/web/app/views/project/ide-react-detached.pug index 88b48da5b7..13732b55ec 100644 --- a/services/web/app/views/project/ide-react-detached.pug +++ b/services/web/app/views/project/ide-react-detached.pug @@ -1,4 +1,4 @@ -extends ../layout-marketing +extends ../layout-react block entrypointVar - entrypoint = 'ide-detached' diff --git a/services/web/app/views/project/ide-react.pug b/services/web/app/views/project/ide-react.pug index 292874e01a..634a3714f7 100644 --- a/services/web/app/views/project/ide-react.pug +++ b/services/web/app/views/project/ide-react.pug @@ -1,4 +1,4 @@ -extends ../layout-marketing +extends ../layout-react block vars - var suppressNavbar = true diff --git a/services/web/frontend/js/features/editor-navigation-toolbar/components/back-to-editor-button.tsx b/services/web/frontend/js/features/editor-navigation-toolbar/components/back-to-editor-button.tsx index 537f8aa2b2..7c242941dd 100644 --- a/services/web/frontend/js/features/editor-navigation-toolbar/components/back-to-editor-button.tsx +++ b/services/web/frontend/js/features/editor-navigation-toolbar/components/back-to-editor-button.tsx @@ -8,7 +8,7 @@ function BackToEditorButton({ onClick }: { onClick: () => void }) { return ( diff --git a/services/web/frontend/js/features/editor-navigation-toolbar/components/upgrade-prompt.jsx b/services/web/frontend/js/features/editor-navigation-toolbar/components/upgrade-prompt.jsx index a214b704f9..45ed7d76be 100644 --- a/services/web/frontend/js/features/editor-navigation-toolbar/components/upgrade-prompt.jsx +++ b/services/web/frontend/js/features/editor-navigation-toolbar/components/upgrade-prompt.jsx @@ -15,7 +15,7 @@ function UpgradePrompt() { return ( - + - - + + {compileButtonLabel} + + } + bs5={compileButtonLabel} + /> + + ) } diff --git a/services/web/frontend/js/features/pdf-preview/components/pdf-compile-button.jsx b/services/web/frontend/js/features/pdf-preview/components/pdf-compile-button.jsx deleted file mode 100644 index f703deeb47..0000000000 --- a/services/web/frontend/js/features/pdf-preview/components/pdf-compile-button.jsx +++ /dev/null @@ -1,179 +0,0 @@ -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' - -const modifierKey = /Mac/i.test(navigator.platform) ? 'Cmd' : 'Ctrl' - -function sendEventAndSet(value, setter, settingName) { - 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')}{' '} - ({modifierKey} + Enter) - - ) - - const dropdownToggleClassName = classNames({ - 'detach-compile-button-animate': animateCompileDropdownArrow, - 'btn-striped-animated': hasChanges, - 'no-left-border': true, - }) - - const buttonClassName = classNames({ - 'btn-striped-animated': hasChanges, - 'no-left-radius': true, - }) - - return ( - startCompile(), - text: compileButtonLabel, - className: buttonClassName, - }} - dropdownToggle={{ - 'aria-label': t('toggle_compile_options_menu'), - handleAnimationEnd: () => setAnimateCompileDropdownArrow(false), - className: dropdownToggleClassName, - }} - dropdown={{ - id: 'pdf-recompile-dropdown', - }} - > - {t('auto_compile')} - - sendEventAndSet(true, setAutoCompile, 'auto-compile')} - > - - {t('on')} - - - sendEventAndSet(false, setAutoCompile, 'auto-compile')} - > - - {t('off')} - - - {t('compile_mode')} - - sendEventAndSet(false, setDraft, 'compile-mode')} - > - - {t('normal')} - - - sendEventAndSet(true, setDraft, 'compile-mode')} - > - - {t('fast')} [draft] - - - Syntax Checks - - - sendEventAndSet(true, setStopOnValidationError, 'syntax-check') - } - > - - {t('stop_on_validation_error')} - - - - sendEventAndSet(false, setStopOnValidationError, 'syntax-check') - } - > - - {t('ignore_validation_errors')} - - - {t('compile_error_handling')} - - - - {t('stop_on_first_error')} - - - - - {t('try_to_compile_despite_errors')} - - - - - stopCompile()} - disabled={!compiling} - aria-disabled={!compiling} - > - {t('stop_compile')} - - - - {t('recompile_from_scratch')} - - - ) -} - -export default memo(PdfCompileButton) diff --git a/services/web/frontend/js/features/pdf-preview/components/pdf-compile-button.tsx b/services/web/frontend/js/features/pdf-preview/components/pdf-compile-button.tsx new file mode 100644 index 0000000000..82ceb19785 --- /dev/null +++ b/services/web/frontend/js/features/pdf-preview/components/pdf-compile-button.tsx @@ -0,0 +1,366 @@ +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( + 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')}{' '} + ({modifierKey} + Enter) + + ) + + 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 ( + startCompile(), + text: compileButtonLabel, + className: buttonClassName, + }} + dropdownToggle={{ + 'aria-label': t('toggle_compile_options_menu'), + handleAnimationEnd: () => setAnimateCompileDropdownArrow(false), + className: dropdownToggleClassName, + }} + dropdown={{ + id: 'pdf-recompile-dropdown', + }} + > + {t('auto_compile')} + + + sendEventAndSet(true, setAutoCompile, 'auto-compile') + } + > + + {t('on')} + + + + sendEventAndSet(false, setAutoCompile, 'auto-compile') + } + > + + {t('off')} + + + {t('compile_mode')} + + sendEventAndSet(false, setDraft, 'compile-mode')} + > + + {t('normal')} + + + sendEventAndSet(true, setDraft, 'compile-mode')} + > + + {t('fast')} [draft] + + + Syntax Checks + + + sendEventAndSet(true, setStopOnValidationError, 'syntax-check') + } + > + + {t('stop_on_validation_error')} + + + + sendEventAndSet(false, setStopOnValidationError, 'syntax-check') + } + > + + {t('ignore_validation_errors')} + + + {t('compile_error_handling')} + + + + {t('stop_on_first_error')} + + + + + {t('try_to_compile_despite_errors')} + + + + + stopCompile()} + disabled={!compiling} + aria-disabled={!compiling} + > + {t('stop_compile')} + + + + {t('recompile_from_scratch')} + + + } + bs5={ + + + startCompile()} + leadingIcon={ + compiling ? ( + + + + + + + {t('auto_compile')} +
  • + + sendEventAndSet(true, setAutoCompile, 'auto-compile') + } + trailingIcon={autoCompile ? 'check' : null} + > + {t('on')} + +
  • +
  • + + sendEventAndSet(false, setAutoCompile, 'auto-compile') + } + trailingIcon={!autoCompile ? 'check' : null} + > + {t('off')} + +
  • + + {t('compile_mode')} +
  • + sendEventAndSet(false, setDraft, 'compile-mode')} + trailingIcon={!draft ? 'check' : null} + > + {t('normal')} + +
  • +
  • + sendEventAndSet(true, setDraft, 'compile-mode')} + trailingIcon={draft ? 'check' : null} + > + {t('fast')} [draft] + +
  • + + Syntax Checks +
  • + + sendEventAndSet( + true, + setStopOnValidationError, + 'syntax-check' + ) + } + trailingIcon={stopOnValidationError ? 'check' : null} + > + {t('stop_on_validation_error')} + +
  • +
  • + + sendEventAndSet( + false, + setStopOnValidationError, + 'syntax-check' + ) + } + trailingIcon={!stopOnValidationError ? 'check' : null} + > + {t('ignore_validation_errors')} + +
  • + + {t('compile_error_handling')} +
  • + + {t('stop_on_first_error')} + +
  • +
  • + + {t('try_to_compile_despite_errors')} + +
  • + +
  • + stopCompile()} + disabled={!compiling} + aria-disabled={!compiling} + > + {t('stop_compile')} + +
  • +
  • + + {t('recompile_from_scratch')} + +
  • +
    +
    + } + /> + ) +} + +export default memo(PdfCompileButton) diff --git a/services/web/frontend/js/features/pdf-preview/components/pdf-hybrid-code-check-button.jsx b/services/web/frontend/js/features/pdf-preview/components/pdf-hybrid-code-check-button.jsx index 5e2cd805a0..8f83f54c26 100644 --- a/services/web/frontend/js/features/pdf-preview/components/pdf-hybrid-code-check-button.jsx +++ b/services/web/frontend/js/features/pdf-preview/components/pdf-hybrid-code-check-button.jsx @@ -1,8 +1,11 @@ import { memo, useCallback } from 'react' -import { Button } from 'react-bootstrap' import Icon from '../../../shared/components/icon' import { useTranslation } from 'react-i18next' import { useDetachCompileContext as useCompileContext } from '../../../shared/context/detach-compile-context' +import OLButton from '@/features/ui/components/ol/ol-button' +import BootstrapVersionSwitcher from '@/features/ui/components/bootstrap-5/bootstrap-version-switcher' +import MaterialIcon from '@/shared/components/material-icon' +import { bsVersion } from '@/features/utils/bootstrap-5' function PdfHybridCodeCheckButton() { const { codeCheckFailed, error, toggleLogs } = useCompileContext() @@ -18,18 +21,24 @@ function PdfHybridCodeCheckButton() { } return ( - +
    ) } diff --git a/services/web/frontend/js/features/pdf-preview/components/pdf-hybrid-download-button.tsx b/services/web/frontend/js/features/pdf-preview/components/pdf-hybrid-download-button.tsx index f2ee8c40ae..c32ad063da 100644 --- a/services/web/frontend/js/features/pdf-preview/components/pdf-hybrid-download-button.tsx +++ b/services/web/frontend/js/features/pdf-preview/components/pdf-hybrid-download-button.tsx @@ -1,11 +1,12 @@ import { useTranslation } from 'react-i18next' -import { Button } from 'react-bootstrap' -import Tooltip from '../../../shared/components/tooltip' -import Icon from '../../../shared/components/icon' import { useDetachCompileContext as useCompileContext } from '../../../shared/context/detach-compile-context' -import { useProjectContext } from '../../../shared/context/project-context' -import * as eventTracking from '../../../infrastructure/event-tracking' -import { isSmallDevice } from '../../../infrastructure/event-tracking' +import { useProjectContext } from '@/shared/context/project-context' +import { sendMB, isSmallDevice } from '@/infrastructure/event-tracking' +import Icon from '@/shared/components/icon' +import OLTooltip from '@/features/ui/components/ol/ol-tooltip' +import OLButton from '@/features/ui/components/ol/ol-button' +import MaterialIcon from '@/shared/components/material-icon' +import BootstrapVersionSwitcher from '@/features/ui/components/bootstrap-5/bootstrap-version-switcher' function PdfHybridDownloadButton() { const { pdfDownloadUrl } = useCompileContext() @@ -17,8 +18,14 @@ function PdfHybridDownloadButton() { ? t('download_pdf') : t('please_compile_pdf_before_download') - function handleOnClick() { - eventTracking.sendMB('download-pdf-button-click', { + function handleOnClick(e: React.MouseEvent) { + const event = e as React.MouseEvent + if (event.currentTarget.dataset.disabled === 'true') { + event.preventDefault() + return + } + + sendMB('download-pdf-button-click', { projectId, location: 'pdf-preview', isSmallDevice, @@ -26,16 +33,17 @@ function PdfHybridDownloadButton() { } return ( - - - + } + bs5={} + /> + + ) } diff --git a/services/web/frontend/js/features/pdf-preview/components/pdf-hybrid-logs-button.tsx b/services/web/frontend/js/features/pdf-preview/components/pdf-hybrid-logs-button.tsx index d5dd4262aa..854c526105 100644 --- a/services/web/frontend/js/features/pdf-preview/components/pdf-hybrid-logs-button.tsx +++ b/services/web/frontend/js/features/pdf-preview/components/pdf-hybrid-logs-button.tsx @@ -1,10 +1,13 @@ import { memo, useCallback } from 'react' import { useTranslation } from 'react-i18next' -import { Button, Label } from 'react-bootstrap' -import Tooltip from '../../../shared/components/tooltip' -import Icon from '../../../shared/components/icon' -import { useDetachCompileContext as useCompileContext } from '../../../shared/context/detach-compile-context' -import * as eventTracking from '../../../infrastructure/event-tracking' +import Icon from '@/shared/components/icon' +import MaterialIcon from '@/shared/components/material-icon' +import { useDetachCompileContext as useCompileContext } from '@/shared/context/detach-compile-context' +import * as eventTracking from '@/infrastructure/event-tracking' +import OLTooltip from '@/features/ui/components/ol/ol-tooltip' +import OLButton from '@/features/ui/components/ol/ol-button' +import OLBadge from '@/features/ui/components/ol/ol-badge' +import BootstrapVersionSwitcher from '@/features/ui/components/bootstrap-5/bootstrap-version-switcher' function PdfHybridLogsButton() { const { error, logEntries, toggleLogs, showLogs, stoppedOnFirstError } = @@ -25,13 +28,13 @@ function PdfHybridLogsButton() { const totalCount = errorCount + warningCount return ( - - - + + ) } diff --git a/services/web/frontend/js/features/pdf-preview/components/pdf-orphan-refresh-button.jsx b/services/web/frontend/js/features/pdf-preview/components/pdf-orphan-refresh-button.jsx index 12dd467e1a..9dd48ac622 100644 --- a/services/web/frontend/js/features/pdf-preview/components/pdf-orphan-refresh-button.jsx +++ b/services/web/frontend/js/features/pdf-preview/components/pdf-orphan-refresh-button.jsx @@ -1,8 +1,8 @@ -import { Button } from 'react-bootstrap' import { useTranslation } from 'react-i18next' import { memo, useCallback } from 'react' -import { buildUrlWithDetachRole } from '../../../shared/utils/url-helper' -import { useLocation } from '../../../shared/hooks/use-location' +import { buildUrlWithDetachRole } from '@/shared/utils/url-helper' +import { useLocation } from '@/shared/hooks/use-location' +import OLButton from '@/features/ui/components/ol/ol-button' function PdfOrphanRefreshButton() { const { t } = useTranslation() @@ -13,14 +13,9 @@ function PdfOrphanRefreshButton() { }, [location]) return ( - + ) } diff --git a/services/web/frontend/js/features/pdf-preview/components/pdf-page-number-control.tsx b/services/web/frontend/js/features/pdf-preview/components/pdf-page-number-control.tsx index 0770d84cc1..80e2c8e66a 100644 --- a/services/web/frontend/js/features/pdf-preview/components/pdf-page-number-control.tsx +++ b/services/web/frontend/js/features/pdf-preview/components/pdf-page-number-control.tsx @@ -1,7 +1,7 @@ -import { ButtonGroup } from 'react-bootstrap' import PDFToolbarButton from './pdf-toolbar-button' import { useTranslation } from 'react-i18next' import { useState, useEffect } from 'react' +import OLButtonGroup from '@/features/ui/components/ol/ol-button-group' type PdfPageNumberControlProps = { setPage: (page: number) => void @@ -38,7 +38,7 @@ function PdfPageNumberControl({ return ( <> - + setPage(page + 1)} /> - +
    + {ToolbarInner} - + ) } @@ -88,7 +90,18 @@ function PdfPreviewHybridToolbarConnectingInner() { return ( <>
    - + } + bs5={ +
    diff --git a/services/web/frontend/js/features/pdf-preview/components/pdf-synctex-controls.jsx b/services/web/frontend/js/features/pdf-preview/components/pdf-synctex-controls.jsx index bbde33dd10..214ca0f4f8 100644 --- a/services/web/frontend/js/features/pdf-preview/components/pdf-synctex-controls.jsx +++ b/services/web/frontend/js/features/pdf-preview/components/pdf-synctex-controls.jsx @@ -7,8 +7,6 @@ import { getJSON } from '../../../infrastructure/fetch-json' import { useDetachCompileContext as useCompileContext } from '../../../shared/context/detach-compile-context' import { useLayoutContext } from '../../../shared/context/layout-context' import useScopeValue from '../../../shared/hooks/use-scope-value' -import { Button } from 'react-bootstrap' -import Tooltip from '../../../shared/components/tooltip' import Icon from '../../../shared/components/icon' import { useTranslation } from 'react-i18next' import useIsMounted from '../../../shared/hooks/use-is-mounted' @@ -21,6 +19,12 @@ import useScopeEventListener from '../../../shared/hooks/use-scope-event-listene import * as eventTracking from '../../../infrastructure/event-tracking' import { debugConsole } from '@/utils/debugging' import { useFileTreePathContext } from '@/features/file-tree/contexts/file-tree-path' +import OLTooltip from '@/features/ui/components/ol/ol-tooltip' +import OLButton from '@/features/ui/components/ol/ol-button' +import BootstrapVersionSwitcher from '@/features/ui/components/bootstrap-5/bootstrap-version-switcher' +import MaterialIcon from '@/shared/components/material-icon' +import { Spinner } from 'react-bootstrap-5' +import { bsVersion } from '@/features/utils/bootstrap-5' function GoToCodeButton({ position, @@ -30,15 +34,32 @@ function GoToCodeButton({ }) { const { t } = useTranslation() const tooltipPlacement = isDetachLayout ? 'bottom' : 'right' - const buttonClasses = classNames('synctex-control', 'btn-secondary', { + const buttonClasses = classNames('synctex-control', { 'detach-synctex-control': !!isDetachLayout, }) let buttonIcon = null if (syncToCodeInFlight) { - buttonIcon = + buttonIcon = ( + } + bs5={ +
    - - + + ) } diff --git a/services/web/frontend/js/features/pdf-preview/components/pdf-zoom-buttons.tsx b/services/web/frontend/js/features/pdf-preview/components/pdf-zoom-buttons.tsx index 9e5b941829..8f1f408cc7 100644 --- a/services/web/frontend/js/features/pdf-preview/components/pdf-zoom-buttons.tsx +++ b/services/web/frontend/js/features/pdf-preview/components/pdf-zoom-buttons.tsx @@ -1,6 +1,6 @@ -import { ButtonGroup } from 'react-bootstrap' import PDFToolbarButton from './pdf-toolbar-button' import { useTranslation } from 'react-i18next' +import OLButtonGroup from '@/features/ui/components/ol/ol-button-group' const isMac = /Mac/.test(window.navigator?.platform) @@ -15,7 +15,7 @@ function PdfZoomButtons({ setZoom }: PdfZoomButtonsProps) { const zoomOutShortcut = isMac ? '⌘-' : 'Ctrl+-' return ( - + setZoom('zoom-in')} shortcut={zoomInShortcut} /> - + ) } diff --git a/services/web/frontend/js/features/pdf-preview/components/pdf-zoom-dropdown.tsx b/services/web/frontend/js/features/pdf-preview/components/pdf-zoom-dropdown.tsx index 8c54142da5..7e6dd5c5f8 100644 --- a/services/web/frontend/js/features/pdf-preview/components/pdf-zoom-dropdown.tsx +++ b/services/web/frontend/js/features/pdf-preview/components/pdf-zoom-dropdown.tsx @@ -1,9 +1,19 @@ -import { Dropdown, MenuItem } from 'react-bootstrap' +import { Dropdown as BS3Dropdown, MenuItem } from 'react-bootstrap' import { useEffect, useState } from 'react' import { useTranslation } from 'react-i18next' import ControlledDropdown from '@/shared/components/controlled-dropdown' import classNames from 'classnames' import { useFeatureFlag } from '@/shared/context/split-test-context' +import BootstrapVersionSwitcher from '@/features/ui/components/bootstrap-5/bootstrap-version-switcher' +import { + Dropdown, + DropdownDivider, + DropdownHeader, + DropdownItem, + DropdownMenu, + DropdownToggle, +} from '@/features/ui/components/bootstrap-5/dropdown-menu' +import FormControl from '@/features/ui/components/bootstrap-5/form/form-control' const isMac = /Mac/.test(window.navigator?.platform) @@ -53,93 +63,215 @@ function PdfZoomDropdown({ const showPresentOption = enablePresentationMode && document.fullscreenEnabled return ( - { - if (eventKey === 'custom-zoom') { - return - } + { + if (eventKey === 'custom-zoom') { + return + } - if (eventKey === 'present') { - requestPresentationMode() - return - } + if (eventKey === 'present') { + requestPresentationMode() + return + } - setZoom(eventKey) - }} - pullRight - > - - - - event.target.select()} - value={customZoomValue} - onKeyDown={event => { - if (event.key === 'Enter') { - const zoom = Number(customZoomValue.replace('%', '')) / 100 - - // Only allow zoom values between 10% and 999% - if (zoom < 0.1) { - setZoom('0.1') - } else if (zoom > 9.99) { - setZoom('9.99') - } else { - setZoom(`${zoom}`) - } - } - }} - onChange={event => { - const rawValue = event.target.value - const parsedValue = rawValue.replace(/[^0-9%]/g, '') - setCustomZoomValue(parsedValue) - }} + - - - - {t('zoom_in')} - - - - {t('zoom_out')} - - - - {t('fit_to_width')} - - - - {t('fit_to_height')} - - - {showPresentOption && } - {showPresentOption && ( - - {t('presentation_mode')} - - )} - - {t('zoom_to')} - {zoomValues.map(value => ( - - {rawScaleToPercentage(Number(value))} - - ))} - - + + + event.target.select()} + value={customZoomValue} + onKeyDown={event => { + if (event.key === 'Enter') { + const zoom = Number(customZoomValue.replace('%', '')) / 100 + + // Only allow zoom values between 10% and 999% + if (zoom < 0.1) { + setZoom('0.1') + } else if (zoom > 9.99) { + setZoom('9.99') + } else { + setZoom(`${zoom}`) + } + } + }} + onChange={event => { + const rawValue = event.target.value + const parsedValue = rawValue.replace(/[^0-9%]/g, '') + setCustomZoomValue(parsedValue) + }} + /> + + + + {t('zoom_in')} + + + + {t('zoom_out')} + + + + {t('fit_to_width')} + + + + {t('fit_to_height')} + + + {showPresentOption && } + {showPresentOption && ( + + {t('presentation_mode')} + + )} + + {t('zoom_to')} + {zoomValues.map(value => ( + + {rawScaleToPercentage(Number(value))} + + ))} + + + } + bs5={ + { + if (eventKey === 'custom-zoom') { + return + } + + if (eventKey === 'present') { + requestPresentationMode() + return + } + + setZoom(eventKey) + }} + align="end" + > + + {rawScaleToPercentage(rawScale)} + + +
  • + + event.target.select()} + value={customZoomValue} + onKeyDown={event => { + if (event.key === 'Enter') { + const zoom = + Number(customZoomValue.replace('%', '')) / 100 + + // Only allow zoom values between 10% and 999% + if (zoom < 0.1) { + setZoom('0.1') + } else if (zoom > 9.99) { + setZoom('9.99') + } else { + setZoom(`${zoom}`) + } + } + }} + onChange={event => { + const rawValue = event.target.value + const parsedValue = rawValue.replace(/[^0-9%]/g, '') + setCustomZoomValue(parsedValue) + }} + /> + +
  • + +
  • + } + > + {t('zoom_in')} + +
  • +
  • + } + > + {t('zoom_out')} + +
  • +
  • + } + > + {t('fit_to_width')} + +
  • +
  • + } + > + {t('fit_to_height')} + +
  • + {showPresentOption && } + {showPresentOption && ( +
  • + + {t('presentation_mode')} + +
  • + )} + + + {zoomValues.map(value => ( +
  • + + {rawScaleToPercentage(Number(value))} + +
  • + ))} +
    +
    + } + /> ) } diff --git a/services/web/frontend/js/features/pdf-preview/components/switch-to-editor-button.tsx b/services/web/frontend/js/features/pdf-preview/components/switch-to-editor-button.tsx index ea5d55b3ad..b3fd477373 100644 --- a/services/web/frontend/js/features/pdf-preview/components/switch-to-editor-button.tsx +++ b/services/web/frontend/js/features/pdf-preview/components/switch-to-editor-button.tsx @@ -25,7 +25,7 @@ function SwitchToEditorButton() { return ( - {filter !== 'archived' && } {filter !== 'trashed' && } - + {(filter === 'trashed' || filter === 'archived') && ( - + {filter === 'trashed' && } {filter === 'archived' && } - + )} {filter === 'trashed' && ( - + - + )} {!['archived', 'trashed'].includes(filter) && } diff --git a/services/web/frontend/js/features/settings/components/emails/actions/make-primary/primary-button.tsx b/services/web/frontend/js/features/settings/components/emails/actions/make-primary/primary-button.tsx index f68dfb633e..228ad7f32e 100644 --- a/services/web/frontend/js/features/settings/components/emails/actions/make-primary/primary-button.tsx +++ b/services/web/frontend/js/features/settings/components/emails/actions/make-primary/primary-button.tsx @@ -8,7 +8,7 @@ function PrimaryButton({ }: OLButtonProps) { return ( diff --git a/services/web/frontend/js/features/settings/components/emails/row.tsx b/services/web/frontend/js/features/settings/components/emails/row.tsx index ba510d16c5..845fa24c80 100644 --- a/services/web/frontend/js/features/settings/components/emails/row.tsx +++ b/services/web/frontend/js/features/settings/components/emails/row.tsx @@ -152,7 +152,7 @@ function SSOAffiliationInfo({ userEmailData }: SSOAffiliationInfoProps) { className="btn-link-accounts" disabled={linkAccountsButtonDisabled} onClick={handleLinkAccountsButtonClick} - size="small" + size="sm" > {t('link_accounts')} diff --git a/services/web/frontend/js/features/share-project-modal/components/restricted-link-sharing/access-levels-changed.tsx b/services/web/frontend/js/features/share-project-modal/components/restricted-link-sharing/access-levels-changed.tsx index b4d220cde3..b1e4bd1883 100644 --- a/services/web/frontend/js/features/share-project-modal/components/restricted-link-sharing/access-levels-changed.tsx +++ b/services/web/frontend/js/features/share-project-modal/components/restricted-link-sharing/access-levels-changed.tsx @@ -43,7 +43,7 @@ export default function AccessLevelsChanged({
    {user.allowedFreeTrial ? ( diff --git a/services/web/frontend/js/features/share-project-modal/components/restricted-link-sharing/add-collaborators-upgrade.tsx b/services/web/frontend/js/features/share-project-modal/components/restricted-link-sharing/add-collaborators-upgrade.tsx index ace42c327a..4ef7427803 100644 --- a/services/web/frontend/js/features/share-project-modal/components/restricted-link-sharing/add-collaborators-upgrade.tsx +++ b/services/web/frontend/js/features/share-project-modal/components/restricted-link-sharing/add-collaborators-upgrade.tsx @@ -33,7 +33,7 @@ export default function AddCollaboratorsUpgrade() {
    {user.allowedFreeTrial ? ( diff --git a/services/web/frontend/js/features/share-project-modal/components/restricted-link-sharing/collaborators-limit-upgrade.tsx b/services/web/frontend/js/features/share-project-modal/components/restricted-link-sharing/collaborators-limit-upgrade.tsx index 53812066dc..a551005e6f 100644 --- a/services/web/frontend/js/features/share-project-modal/components/restricted-link-sharing/collaborators-limit-upgrade.tsx +++ b/services/web/frontend/js/features/share-project-modal/components/restricted-link-sharing/collaborators-limit-upgrade.tsx @@ -37,7 +37,7 @@ export default function CollaboratorsLimitUpgrade() { action={ user.allowedFreeTrial ? ( @@ -71,7 +71,7 @@ export default function CollaboratorsLimitUpgrade() { action={ user.allowedFreeTrial ? ( diff --git a/services/web/frontend/js/features/source-editor/components/switch-to-pdf-button.jsx b/services/web/frontend/js/features/source-editor/components/switch-to-pdf-button.jsx index 16d2b1e81e..93b8d2461b 100644 --- a/services/web/frontend/js/features/source-editor/components/switch-to-pdf-button.jsx +++ b/services/web/frontend/js/features/source-editor/components/switch-to-pdf-button.jsx @@ -25,7 +25,7 @@ function SwitchToPDFButton() { return ( , + BSBadgeProps, { prepend?: React.ReactNode } > -function Badge({ prepend, children, closeBtnProps, ...rest }: BadgeProps) { +function Badge({ prepend, children, ...rest }: BadgeProps) { return ( {prepend && {prepend}} diff --git a/services/web/frontend/js/features/ui/components/bootstrap-5/button.tsx b/services/web/frontend/js/features/ui/components/bootstrap-5/button.tsx index 0d80f8aab8..1e21058d28 100644 --- a/services/web/frontend/js/features/ui/components/bootstrap-5/button.tsx +++ b/services/web/frontend/js/features/ui/components/bootstrap-5/button.tsx @@ -5,12 +5,6 @@ import classNames from 'classnames' import { useTranslation } from 'react-i18next' import MaterialIcon from '@/shared/components/material-icon' -const sizeClasses = new Map([ - ['small', 'btn-sm'], - ['default', ''], - ['large', 'btn-lg'], -]) - const Button = forwardRef( ( { @@ -19,7 +13,6 @@ const Button = forwardRef( leadingIcon, isLoading = false, loadingLabel, - size = 'default', trailingIcon, variant = 'primary', ...props @@ -28,13 +21,28 @@ const Button = forwardRef( ) => { const { t } = useTranslation() - const sizeClass = sizeClasses.get(size) - const buttonClassName = classNames('d-inline-grid', sizeClass, className, { + const buttonClassName = classNames('d-inline-grid', className, { 'button-loading': isLoading, }) + const loadingSpinnerClassName = - size === 'large' ? 'loading-spinner-large' : 'loading-spinner-small' - const materialIconClassName = size === 'large' ? 'icon-large' : 'icon-small' + props.size === 'lg' ? 'loading-spinner-large' : 'loading-spinner-small' + const materialIconClassName = + props.size === 'lg' ? 'icon-large' : 'icon-small' + + const leadingIconComponent = + leadingIcon && typeof leadingIcon === 'string' ? ( + + ) : ( + leadingIcon + ) + + const trailingIconComponent = + trailingIcon && typeof trailingIcon === 'string' ? ( + + ) : ( + trailingIcon + ) return ( ( )} - {leadingIcon && ( - - )} + {leadingIconComponent} {children} - {trailingIcon && ( - - )} + {trailingIconComponent} ) diff --git a/services/web/frontend/js/features/ui/components/bootstrap-5/split-button.tsx b/services/web/frontend/js/features/ui/components/bootstrap-5/split-button.tsx deleted file mode 100644 index 8c80aafd44..0000000000 --- a/services/web/frontend/js/features/ui/components/bootstrap-5/split-button.tsx +++ /dev/null @@ -1,51 +0,0 @@ -import classNames from 'classnames' -import Button from './button' -import { - Dropdown, - DropdownItem, - DropdownMenu, - DropdownToggle, -} from './dropdown-menu' -import MaterialIcon from '@/shared/components/material-icon' -import type { SplitButtonProps } from '@/features/ui/components/types/split-button-props' - -export function SplitButton({ - accessibilityLabel, - align, - id, - items, - text, - variant, - ...props -}: SplitButtonProps) { - const buttonClassName = classNames('split-button') - - return ( -
    - - - - - - - {items.map((item, index) => ( -
  • - {item.label} -
  • - ))} -
    -
    -
    - ) -} diff --git a/services/web/frontend/js/features/ui/components/ol/ol-button-group.tsx b/services/web/frontend/js/features/ui/components/ol/ol-button-group.tsx index f4f0ede217..c9a9d1188b 100644 --- a/services/web/frontend/js/features/ui/components/ol/ol-button-group.tsx +++ b/services/web/frontend/js/features/ui/components/ol/ol-button-group.tsx @@ -10,7 +10,7 @@ type OLButtonGroupProps = ButtonGroupProps & { bs3Props?: Record } -function OlButtonGroup({ bs3Props, as, ...rest }: OLButtonGroupProps) { +function OLButtonGroup({ bs3Props, as, ...rest }: OLButtonGroupProps) { const bs3ButtonGroupProps: BS3ButtonGroupProps = { children: rest.children, className: rest.className, @@ -27,4 +27,4 @@ function OlButtonGroup({ bs3Props, as, ...rest }: OLButtonGroupProps) { ) } -export default OlButtonGroup +export default OLButtonGroup diff --git a/services/web/frontend/js/features/ui/components/ol/ol-button.tsx b/services/web/frontend/js/features/ui/components/ol/ol-button.tsx index 1e377ee3c0..524b3e3353 100644 --- a/services/web/frontend/js/features/ui/components/ol/ol-button.tsx +++ b/services/web/frontend/js/features/ui/components/ol/ol-button.tsx @@ -1,3 +1,4 @@ +import { forwardRef } from 'react' import BootstrapVersionSwitcher from '../bootstrap-5/bootstrap-version-switcher' import { Button as BS3Button } from 'react-bootstrap' import type { ButtonProps } from '@/features/ui/components/types/button-props' @@ -5,7 +6,7 @@ import type { ButtonProps as BS3ButtonPropsBase } from 'react-bootstrap' import Button from '../bootstrap-5/button' import classnames from 'classnames' import { getAriaAndDataProps } from '@/features/utils/bootstrap-5' - +import { callFnsInSequence } from '@/utils/functions' export type BS3ButtonSize = 'xsmall' | 'sm' | 'medium' | 'lg' export type OLButtonProps = ButtonProps & { @@ -14,6 +15,10 @@ export type OLButtonProps = ButtonProps & { bsSize?: BS3ButtonSize block?: boolean className?: string + onMouseOver?: React.MouseEventHandler + onMouseOut?: React.MouseEventHandler + onFocus?: React.FocusEventHandler + onBlur?: React.FocusEventHandler } } @@ -25,7 +30,7 @@ export type BS3ButtonProps = Omit & { export function bs3ButtonProps(props: ButtonProps) { const bs3ButtonProps: BS3ButtonProps = { bsStyle: null, - bsSize: mapBsButtonSizes(props.size), + bsSize: props.size, className: classnames(`btn-${props.variant || 'primary'}`, props.className), disabled: props.isLoading || props.disabled, form: props.form, @@ -34,38 +39,51 @@ export function bs3ButtonProps(props: ButtonProps) { rel: props.rel, onClick: props.onClick, type: props.type, + draggable: props.draggable, + download: props.download, + style: props.style, + active: props.active, } return bs3ButtonProps } -// maps Bootstrap 5 sizes to Bootstrap 3 sizes -export const mapBsButtonSizes = ( - size: ButtonProps['size'] -): 'sm' | 'lg' | undefined => - size === 'small' ? 'sm' : size === 'large' ? 'lg' : undefined +const OLButton = forwardRef( + ({ bs3Props = {}, ...rest }, ref) => { + const { className: _, ...restBs3Props } = bs3Props -export default function OLButton({ bs3Props = {}, ...rest }: OLButtonProps) { - const { className: _, ...restBs3Props } = bs3Props + // BS3 OverlayTrigger automatically provides 'onMouseOver', 'onMouseOut', 'onFocus', 'onBlur' event handlers + const bs3FinalProps = { + ...restBs3Props, + onMouseOver: callFnsInSequence(bs3Props?.onMouseOver, rest.onMouseOver), + onMouseOut: callFnsInSequence(bs3Props?.onMouseOut, rest.onMouseOut), + onFocus: callFnsInSequence(bs3Props?.onFocus, rest.onFocus), + onBlur: callFnsInSequence(bs3Props?.onBlur, rest.onBlur), + } - // Get all `aria-*` and `data-*` attributes - const extraProps = getAriaAndDataProps(rest) + // Get all `aria-*` and `data-*` attributes + const extraProps = getAriaAndDataProps(rest) - return ( - - {bs3Props?.loading || rest.children} - - } - bs5={ + + + Header + Action 1 + Action 2 + Action 3 + + + ))} +
    + + )) } -const meta: Meta = { +const meta: Meta = { title: 'Shared/Components/Bootstrap 5/SplitButton', - component: SplitButton, + component: Dropdown, args: { align: { sm: 'start' }, - id: 'split-button', - items: [ - { eventKey: '1', label: 'Action 1' }, - { eventKey: '2', label: 'Action 2' }, - { eventKey: '3', label: 'Action 3' }, - ], - text: 'Split Button', - }, - argTypes: { - id: { - table: { - disable: true, - }, - }, }, parameters: { bootstrap5: true, diff --git a/services/web/frontend/stylesheets/app/editor/pdf.less b/services/web/frontend/stylesheets/app/editor/pdf.less index a63766279b..cd4eeb8288 100644 --- a/services/web/frontend/stylesheets/app/editor/pdf.less +++ b/services/web/frontend/stylesheets/app/editor/pdf.less @@ -53,9 +53,6 @@ outline: none; } } -.btn-toggle-logs-label { - padding-left: @line-height-computed / 4; -} .pdf-toolbar-btn { display: inline-block; @@ -64,7 +61,6 @@ padding: 4px 2px; line-height: 1; height: 24px; - border-radius: 2px; border-radius: @border-radius-base; &:hover, diff --git a/services/web/frontend/stylesheets/bootstrap-5/abstracts/mixins.scss b/services/web/frontend/stylesheets/bootstrap-5/abstracts/mixins.scss index 2110f6541a..9747dd7027 100644 --- a/services/web/frontend/stylesheets/bootstrap-5/abstracts/mixins.scss +++ b/services/web/frontend/stylesheets/bootstrap-5/abstracts/mixins.scss @@ -67,11 +67,11 @@ // Toolbar @mixin toolbar-sm-height { - height: 32px; + height: var(--toolbar-small-height); } @mixin toolbar-alt-bg() { - background-color: var(--bg-dark-secondary); + background-color: var(--toolbar-alt-bg-color); } @mixin theme($name) { @@ -87,3 +87,20 @@ @mixin box-shadow-button-input { box-shadow: 0 0 0 2px var(--blue-30); } + +@mixin animation($animation) { + animation: $animation; +} + +@mixin striped($color: rgba(255, 255, 255, 0.15), $angle: 45deg) { + background-image: linear-gradient( + $angle, + $color 25%, + transparent 25%, + transparent 50%, + $color 50%, + $color 75%, + transparent 75%, + transparent + ); +} diff --git a/services/web/frontend/stylesheets/bootstrap-5/components/all.scss b/services/web/frontend/stylesheets/bootstrap-5/components/all.scss index f2a738a566..7f6b184cf8 100644 --- a/services/web/frontend/stylesheets/bootstrap-5/components/all.scss +++ b/services/web/frontend/stylesheets/bootstrap-5/components/all.scss @@ -2,7 +2,6 @@ @import 'button-group'; @import 'dropdown-menu'; @import 'image'; -@import 'split-button'; @import 'notifications'; @import 'system-messages'; @import 'tooltip'; diff --git a/services/web/frontend/stylesheets/bootstrap-5/components/button-group.scss b/services/web/frontend/stylesheets/bootstrap-5/components/button-group.scss index e0de77102f..5825a57ca6 100644 --- a/services/web/frontend/stylesheets/bootstrap-5/components/button-group.scss +++ b/services/web/frontend/stylesheets/bootstrap-5/components/button-group.scss @@ -1,10 +1,10 @@ .btn-group { > .btn { - &:first-child { + &:first-of-type { padding-left: var(--spacing-05); } - &:last-child { + &:last-of-type { padding-right: var(--spacing-05); } } diff --git a/services/web/frontend/stylesheets/bootstrap-5/components/dropdown-menu.scss b/services/web/frontend/stylesheets/bootstrap-5/components/dropdown-menu.scss index f07165a1db..d8b30248de 100644 --- a/services/web/frontend/stylesheets/bootstrap-5/components/dropdown-menu.scss +++ b/services/web/frontend/stylesheets/bootstrap-5/components/dropdown-menu.scss @@ -138,6 +138,7 @@ border-top-left-radius: 0; padding-right: var(--spacing-05); padding-left: var(--spacing-05); + margin-left: 0; &.btn-primary, &.btn-danger { diff --git a/services/web/frontend/stylesheets/bootstrap-5/components/split-button.scss b/services/web/frontend/stylesheets/bootstrap-5/components/split-button.scss deleted file mode 100644 index 544a4a4b34..0000000000 --- a/services/web/frontend/stylesheets/bootstrap-5/components/split-button.scss +++ /dev/null @@ -1,9 +0,0 @@ -.split-button { - border-bottom-right-radius: 0; - border-top-right-radius: 0; - border-right: none; -} - -.split-button-caret { - vertical-align: middle; -} diff --git a/services/web/frontend/stylesheets/bootstrap-5/pages/all.scss b/services/web/frontend/stylesheets/bootstrap-5/pages/all.scss index ea5e4c10c2..f9ac9aba51 100644 --- a/services/web/frontend/stylesheets/bootstrap-5/pages/all.scss +++ b/services/web/frontend/stylesheets/bootstrap-5/pages/all.scss @@ -12,5 +12,7 @@ @import 'editor/file-tree'; @import 'editor/figure-modal'; @import 'subscription'; +@import 'editor/pdf'; +@import 'editor/compile-button'; @import 'website-redesign'; @import 'group-settings'; diff --git a/services/web/frontend/stylesheets/bootstrap-5/pages/editor/compile-button.scss b/services/web/frontend/stylesheets/bootstrap-5/pages/editor/compile-button.scss new file mode 100644 index 0000000000..fd29d70acf --- /dev/null +++ b/services/web/frontend/stylesheets/bootstrap-5/pages/editor/compile-button.scss @@ -0,0 +1,31 @@ +$stripe-width: 20px; + +@keyframes pdf-toolbar-stripes { + from { + background-position: 0 0; + } + + to { + background-position: $stripe-width 0; + } +} + +.btn-striped-animated { + @include striped; + + background-size: $stripe-width $stripe-width; + background-origin: content-box; + + @include animation(pdf-toolbar-stripes 2s linear infinite); +} + +.detach-compile-button { + &[disabled], + &[disabled]:active { + background-color: var(--bs-btn-bg); + color: var(--bs-btn-color); + opacity: 1; + pointer-events: auto; + cursor: not-allowed; + } +} diff --git a/services/web/frontend/stylesheets/bootstrap-5/pages/editor/pdf.scss b/services/web/frontend/stylesheets/bootstrap-5/pages/editor/pdf.scss new file mode 100644 index 0000000000..dd41048115 --- /dev/null +++ b/services/web/frontend/stylesheets/bootstrap-5/pages/editor/pdf.scss @@ -0,0 +1,357 @@ +:root { + --pdf-bg: var(--neutral-10); + --pdf-toolbar-btn-hover-color: rgb(125 125 125 / 20%); +} + +@include theme('light') { + --pdf-toolbar-btn-hover-color: var(--neutral-10); +} + +.pdf .toolbar.toolbar-pdf { + @include toolbar-sm-height; + @include toolbar-alt-bg; + + padding-right: var(--spacing-03); + margin-left: 0; + + .btn.disabled, + .btn[disabled] { + pointer-events: auto; + cursor: not-allowed; + opacity: 1; + } +} + +.toolbar-pdf-left { + gap: var(--spacing-02); + + .dropdown > .btn { + border-top-left-radius: 0; + border-bottom-left-radius: 0; + + &[disabled], + &[disabled]:active { + background-color: var(--bs-btn-bg); + color: var(--bs-btn-color); + opacity: 1; + pointer-events: auto; + cursor: not-allowed; + } + } +} + +.toolbar-pdf-orphan, +.toolbar-pdf-left, +.toolbar-pdf-right, +.toolbar-pdf-controls { + display: flex; + align-items: center; + align-self: stretch; +} + +.toolbar-pdf-orphan, +.toolbar-pdf-controls { + flex: 1 1 100%; +} + +.toolbar-pdf-controls { + margin-right: var(--spacing-02); + justify-content: flex-end; +} + +.toolbar-pdf-right { + flex: 1; + justify-content: flex-end; +} + +.toolbar-pdf-orphan { + justify-content: center; + color: var(--toolbar-btn-color); + + .btn { + margin-left: var(--spacing-03); + } +} + +.btn.pdf-toolbar-btn { + display: inline-block; + color: var(--toolbar-btn-color); + background-color: transparent; + padding: 0 var(--spacing-01); + line-height: 1; + height: 24px; + border-radius: var(--border-radius-base); + text-decoration: none; + + &:hover, + &:active, + &:focus { + color: var(--toolbar-btn-color); + } + + &:hover { + &:not(:disabled) { + background-color: var(--pdf-toolbar-btn-hover-color); + } + } + + &:active { + background-color: transparent; + } + + .button-content { + align-self: center; + } + + .badge { + font-size: 60%; + } + + &.log-btn { + border: none; + + &.active { + color: var(--white); + background-color: var(--link-color); + box-shadow: none; + opacity: 0.65; + + &:hover { + &:not(:disabled) { + background-color: transparent; + color: var(--toolbar-btn-color); + } + } + } + + &:focus { + outline: none; + } + } +} + +.pdf { + background-color: var(--pdf-bg); +} + +.pdf-viewer, +.pdf-logs, +.pdf-errors, +.pdf-uncompiled { + @extend .full-size; + + top: var(--toolbar-small-height); +} + +.pdf-viewer { + iframe { + width: 100%; + height: 100%; + border: none; + } + + .pdfjs-viewer { + @extend .full-size; + + background-color: transparent; + overflow: scroll; + + /* stylelint-disable-next-line selector-class-pattern */ + .canvasWrapper > canvas, + div.pdf-canvas { + background: white; + box-shadow: 0 0 10px rgb(0 0 0 / 50%); + } + + div.pdf-canvas.pdfng-empty { + background-color: var(--white); + } + + div.pdf-canvas.pdfng-loading { + background-color: var(--white); + } + + .page-container { + margin: var(--spacing-05) auto; + padding: 0 var(--spacing-05); + box-sizing: content-box; + user-select: none; + } + + .page { + box-sizing: content-box; + margin: var(--spacing-05) auto; + box-shadow: 0 0 8px #bbb; + border: none; + } + + .pdfjs-viewer-inner { + position: absolute; + overflow-y: scroll; + width: 100%; + height: 100%; + -webkit-font-smoothing: initial; + -moz-osx-font-smoothing: initial; + + /* fix review-panel overflow issue, see: https://github.com/overleaf/internal/issues/6781#issuecomment-1112708638 */ + /* stylelint-disable-next-line selector-class-pattern */ + .pdfViewer { + min-height: 100%; + } + } + + &:focus-within { + outline: none; + } + + /* Avoids https://github.com/mozilla/pdf.js/issues/13840 in Chrome */ + /* stylelint-disable-next-line selector-class-pattern */ + .textLayer br::selection { + background: transparent; + } + } + + .progress-thin { + position: absolute; + top: -2px; + height: 3px; + left: 0; + right: 0; + + .progress-bar { + height: 100%; + background-color: var(--link-color); + } + } +} + +.pdfjs-viewer-controls { + display: flex; + align-items: center; + justify-content: flex-end; + width: 100%; +} + +.pdfjs-zoom-controls { + display: inline-flex; + border-left: 1px solid rgb(125 125 125 / 30%); +} + +.pdfjs-toolbar-buttons { + display: flex; + gap: var(--spacing-04); + margin-left: var(--spacing-04); + margin-right: var(--spacing-04); +} + +.pdfjs-toolbar-button { + padding: var(--spacing-01) !important; + display: flex; + align-items: center; +} + +.pdfjs-zoom-dropdown-button { + width: 60px; + text-align: right; + font-weight: normal; +} + +.pdfjs-zoom-dropdown-mac-shortcut-char { + display: inline-block; + width: 1em; + text-align: center; +} + +.pdfjs-custom-zoom-menu-item { + display: block; + pointer-events: initial !important; + + &:hover { + background-color: initial !important; + color: initial !important; + cursor: initial !important; + } +} + +.pdfjs-page-number-input { + color: var(--toolbar-btn-color); + font-size: var(--font-size-02); + padding-right: var(--spacing-04); + display: flex; + align-items: center; + gap: var(--spacing-02); + + input { + color: initial; + border: 1px solid var(--neutral-60); + width: 32px; + height: 24px; + border-radius: var(--border-radius-base); + text-align: center; + } +} + +.pdfjs-viewer-controls-small { + display: flex; + align-items: center; + gap: var(--spacing-04); +} + +.pdfjs-toolbar-popover-button { + padding: var(--spacing-01) !important; +} + +.pdfjs-toolbar-popover { + background-color: var(--editor-toolbar-bg); + border-radius: var(--border-radius-base); + + .popover-arrow { + display: none; + } + + button { + background-color: transparent; + color: var(--toolbar-btn-color); + } + + .popover-body { + display: flex; + align-items: center; + padding: var(--spacing-04) 0; + } +} + +// The new viewer UI has overflow on the inner element, +// so disable the overflow on the outer element +.pdf-viewer .pdfjs-viewer.pdfjs-viewer-outer { + overflow: hidden; +} + +:fullscreen { + .pdfjs-viewer-inner { + overflow-y: hidden !important; + } +} + +.synctex-control { + > .synctex-control-icon { + display: inline-block; + font: + normal normal normal 14px/1 FontAwesome, + sans-serif; + font-size: inherit; + text-rendering: auto; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + } + + > .synctex-spin-icon { + margin-top: var(--spacing-01); + } +} + +.keyboard-tooltip { + .tooltip-inner { + max-width: none; + } +} diff --git a/services/web/frontend/stylesheets/bootstrap-5/pages/editor/toolbar.scss b/services/web/frontend/stylesheets/bootstrap-5/pages/editor/toolbar.scss index 4c03c0147d..f0ed2e720c 100644 --- a/services/web/frontend/stylesheets/bootstrap-5/pages/editor/toolbar.scss +++ b/services/web/frontend/stylesheets/bootstrap-5/pages/editor/toolbar.scss @@ -7,6 +7,8 @@ --toolbar-btn-hover-color: var(--white); --toolbar-btn-active-color: var(--white); --toolbar-btn-active-bg-color: var(--green-50); + --toolbar-small-height: 32px; + --toolbar-alt-bg-color: var(--neutral-80); --formatting-btn-color: var(--white); --formatting-btn-bg: var(--neutral-80); --formatting-btn-border: var(--neutral-70); @@ -27,6 +29,7 @@ --toolbar-btn-hover-bg-color: var(--neutral-10); --toolbar-btn-hover-color: var(--neutral-70); --toolbar-btn-active-bg-color: var(--green-50); + --toolbar-alt-bg-color: var(--white); --formatting-btn-color: var(--neutral-70); --formatting-btn-bg: transparent; --formatting-btn-border: var(--neutral-20); @@ -43,7 +46,6 @@ display: flex; align-items: center; height: var(--toolbar-height); - min-height: var(--toolbar-height); border-bottom: 1px solid var(--toolbar-border-color); button { diff --git a/services/web/test/frontend/components/shared/start-free-trial-button.spec.tsx b/services/web/test/frontend/components/shared/start-free-trial-button.spec.tsx index 5d494fbbd5..e7cf681034 100644 --- a/services/web/test/frontend/components/shared/start-free-trial-button.spec.tsx +++ b/services/web/test/frontend/components/shared/start-free-trial-button.spec.tsx @@ -44,7 +44,7 @@ describe('start free trial button', function () { source="cypress-test" buttonProps={{ variant: 'danger', - size: 'large', + size: 'lg', }} /> )