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 = ( {pdfExpandLabel} ) function handlePdfExpandBtnClick() { if (splitLayout) { onSetFullLayout() } else { onSetSplitLayout() } } useResizeObserver(toolbarRef, logsState, checkCanShowText) return (
) } PreviewToolbar.propTypes = { compilerState: PropTypes.shape({ autoCompileHasLintingError: PropTypes.bool, 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 }), 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