| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287 |
- import React, { useRef, useState } from 'react'
- import PropTypes from 'prop-types'
- import { OverlayTrigger, Tooltip } from 'react-bootstrap'
- import { useTranslation } from 'react-i18next'
- import PreviewDownloadButton from './preview-download-button'
- import PreviewRecompileButton from './preview-recompile-button'
- import PreviewLogsToggleButton from './preview-logs-toggle-button'
- import useResizeObserver from '../../../shared/hooks/use-resize-observer'
- import Icon from '../../../shared/components/icon'
- function _getElementWidth(element) {
- if (!element) return 0
- return Math.ceil(element.getBoundingClientRect().width)
- }
- function PreviewToolbar({
- compilerState,
- logsState,
- onRecompileFromScratch,
- onRecompile,
- onRunSyntaxCheckNow,
- onSetAutoCompile,
- onSetDraftMode,
- onSetSyntaxCheck,
- onToggleLogs,
- onSetSplitLayout,
- onSetFullLayout,
- onStopCompilation,
- outputFiles,
- pdfDownloadUrl,
- showLogs,
- splitLayout
- }) {
- const showTextRef = useRef(true)
- const showToggleTextRef = useRef(true)
- const toolbarRef = useRef()
- const recompileWidthDifferenceRef = useRef()
- const recompileLongerTextRef = useRef()
- const [showText, setShowText] = useState(showTextRef.current)
- const [showToggleText, setShowToggleText] = useState(
- showToggleTextRef.current
- )
- const { t } = useTranslation()
- function checkCanShowText(observedElement) {
- // toolbar items can be in 3 states:
- // all text, only toggle logs w/text and icons on others, all icons
- // states depend on available space in the toolbar
- const toolbarWidth =
- observedElement &&
- observedElement.contentRect &&
- observedElement.contentRect.width
- if (!toolbarWidth) return
- _checkRecompileStateWidths()
- let textWidths = 0
- let itemsWidth = _getItemsWidth() // could be with or without text
- // get widths of text only
- // required for _checkToggleText, even if currently showing text
- const textElements = toolbarRef.current.querySelectorAll('.toolbar-text')
- textElements.forEach(item => {
- if (item.getAttribute('aria-hidden') !== 'true') {
- textWidths += _getElementWidth(item)
- }
- })
- const logsToggleText = toolbarRef.current.querySelector(
- '#logs-toggle .toolbar-text'
- )
- const logsToggleTextWidth = _getElementWidth(logsToggleText)
- if (!showTextRef.current && !showToggleTextRef.current) {
- // itemsWidth was calculated without any text shown
- itemsWidth += textWidths
- } else if (!showTextRef.current && showToggleTextRef.current) {
- // itemsWidth was calculated with toggle text but no other text
- // only add text width for other items and then
- // subtract toggle text width, since it is already in itemsWidth
- itemsWidth += parseInt(textWidths - logsToggleTextWidth, 10)
- }
- // only add extra if recompile button is in state with smaller length
- if (
- recompileWidthDifferenceRef.current &&
- recompileLongerTextRef.current &&
- ((!compilerState.isCompiling &&
- recompileLongerTextRef.current === 'compiling') ||
- (compilerState.isCompiling &&
- recompileLongerTextRef.current === 'recompile'))
- ) {
- itemsWidth += recompileWidthDifferenceRef.current
- }
- itemsWidth += 10 // add extra for some spacing between items
- let canShowText = itemsWidth < toolbarWidth
- if (!canShowText) {
- _checkToggleText(
- toolbarWidth,
- logsToggleTextWidth,
- itemsWidth,
- textWidths
- )
- } else if (showToggleTextRef.current !== true) {
- setShowToggleText(true)
- showToggleTextRef.current = true
- }
- setShowText(canShowText)
- showTextRef.current = canShowText
- }
- function _checkRecompileStateWidths() {
- // check recompile/compiling button text.
- // Do not want to hide and then show text when recompiling
- if (
- recompileWidthDifferenceRef.current ||
- recompileWidthDifferenceRef.current === 0
- )
- return
- const textCompiling = toolbarRef.current.querySelector('#text-compiling')
- const textRecompile = toolbarRef.current.querySelector('#text-recompile')
- const textCompilingWidth = _getElementWidth(textCompiling)
- const textRecompileWidth = _getElementWidth(textRecompile)
- const textLengthDifference = Math.abs(
- parseInt(textCompilingWidth - textRecompileWidth, 10)
- )
- recompileWidthDifferenceRef.current = textLengthDifference
- // ignore if equal
- if (textRecompileWidth > textCompilingWidth) {
- recompileLongerTextRef.current = 'recompile'
- } else if (textRecompileWidth < textCompilingWidth) {
- recompileLongerTextRef.current = 'compiling'
- }
- }
- function _checkToggleText(
- toolbarWidth,
- logsToggleTextWidth,
- itemsWithTextWidth,
- textWidths
- ) {
- // check to see if we can still show the toggle button text
- let toggleWithTextWidth = 0
- let toggleWithoutTextWidth = 0
- const itemsWithoutTextWidth = parseInt(itemsWithTextWidth - textWidths, 10)
- const logsToggle = toolbarRef.current.querySelector('#logs-toggle')
- const logsToggleWidth = _getElementWidth(logsToggle)
- // logsToggleWidth could be with or without text
- if (showToggleTextRef.current) {
- toggleWithTextWidth = logsToggleWidth
- toggleWithoutTextWidth = parseInt(
- logsToggleWidth - logsToggleTextWidth,
- 10
- )
- } else {
- toggleWithTextWidth = parseInt(logsToggleWidth + logsToggleTextWidth, 10)
- toggleWithoutTextWidth = logsToggleWidth
- }
- const itemsWithoutTextAndToggleWidth = parseInt(
- itemsWithoutTextWidth - toggleWithoutTextWidth,
- 10
- )
- const itemsWithIconsExceptToggleWidth = parseInt(
- itemsWithoutTextAndToggleWidth + toggleWithTextWidth,
- 10
- )
- const canShowToggleText = itemsWithIconsExceptToggleWidth < toolbarWidth
- if (canShowToggleText !== showToggleTextRef.current) {
- setShowToggleText(canShowToggleText)
- showToggleTextRef.current = canShowToggleText
- }
- }
- function _getItemsWidth() {
- const toolbarItems = toolbarRef.current.querySelectorAll('.toolbar-item')
- let itemWidth = 0
- toolbarItems.forEach(item => {
- itemWidth += _getElementWidth(item)
- })
- return itemWidth
- }
- const pdfExpandLabel = splitLayout ? t('full_screen') : t('split_screen')
- const pdfExpandIconType = splitLayout ? 'expand' : 'compress'
- const pdfExpandTooltip = (
- <Tooltip id="expand-pdf-btn">{pdfExpandLabel}</Tooltip>
- )
- function handlePdfExpandBtnClick() {
- if (splitLayout) {
- onSetFullLayout()
- } else {
- onSetSplitLayout()
- }
- }
- useResizeObserver(toolbarRef, logsState, checkCanShowText)
- return (
- <div
- className="toolbar toolbar-pdf"
- id="toolbar-preview"
- data-testid="toolbar-preview"
- ref={toolbarRef}
- >
- <div className="toolbar-pdf-left">
- <PreviewRecompileButton
- compilerState={compilerState}
- onRecompile={onRecompile}
- onRecompileFromScratch={onRecompileFromScratch}
- onRunSyntaxCheckNow={onRunSyntaxCheckNow}
- onSetAutoCompile={onSetAutoCompile}
- onSetDraftMode={onSetDraftMode}
- onSetSyntaxCheck={onSetSyntaxCheck}
- onStopCompilation={onStopCompilation}
- showText={showText}
- />
- <PreviewDownloadButton
- isCompiling={compilerState.isCompiling}
- outputFiles={outputFiles}
- pdfDownloadUrl={pdfDownloadUrl}
- showText={showText}
- />
- </div>
- <div className="toolbar-pdf-right">
- <PreviewLogsToggleButton
- logsState={logsState}
- showLogs={showLogs}
- compileFailed={compilerState.compileFailed}
- onToggle={onToggleLogs}
- showText={showToggleText}
- />
- <OverlayTrigger placement="left" overlay={pdfExpandTooltip}>
- <button
- onClick={handlePdfExpandBtnClick}
- className="toolbar-pdf-expand-btn toolbar-item"
- aria-label={pdfExpandLabel}
- >
- <Icon type={pdfExpandIconType} />
- </button>
- </OverlayTrigger>
- </div>
- </div>
- )
- }
- PreviewToolbar.propTypes = {
- compilerState: PropTypes.shape({
- isAutoCompileOn: PropTypes.bool.isRequired,
- isCompiling: PropTypes.bool.isRequired,
- isDraftModeOn: PropTypes.bool.isRequired,
- isSyntaxCheckOn: PropTypes.bool.isRequired,
- compileFailed: PropTypes.bool,
- logEntries: PropTypes.object.isRequired
- }),
- logsState: PropTypes.shape({
- nErrors: PropTypes.number.isRequired,
- nWarnings: PropTypes.number.isRequired,
- nLogEntries: PropTypes.number.isRequired
- }),
- showLogs: PropTypes.bool.isRequired,
- splitLayout: PropTypes.bool.isRequired,
- onRecompile: PropTypes.func.isRequired,
- onRecompileFromScratch: PropTypes.func.isRequired,
- onRunSyntaxCheckNow: PropTypes.func.isRequired,
- onSetAutoCompile: PropTypes.func.isRequired,
- onSetDraftMode: PropTypes.func.isRequired,
- onSetSyntaxCheck: PropTypes.func.isRequired,
- onToggleLogs: PropTypes.func.isRequired,
- onSetSplitLayout: PropTypes.func.isRequired,
- onSetFullLayout: PropTypes.func.isRequired,
- onStopCompilation: PropTypes.func.isRequired,
- pdfDownloadUrl: PropTypes.string,
- outputFiles: PropTypes.array
- }
- export default PreviewToolbar
|