import React, { useState, useRef } from 'react'
import PropTypes from 'prop-types'
import classNames from 'classnames'
import { useTranslation } from 'react-i18next'
import { OverlayTrigger, Tooltip } from 'react-bootstrap'
import useExpandCollapse from '../../../shared/hooks/use-expand-collapse'
import useResizeObserver from '../../../shared/hooks/use-resize-observer'
import Icon from '../../../shared/components/icon'
function PreviewLogsPaneEntry({
headerTitle,
headerIcon,
rawContent,
logType,
formattedContent,
extraInfoURL,
level,
sourceLocation,
showSourceLocationLink = true,
showCloseButton = false,
entryAriaLabel = null,
customClass,
onSourceLocationClick,
onClose,
}) {
const logEntryClasses = classNames('log-entry', customClass)
function handleLogEntryLinkClick(e) {
e.preventDefault()
onSourceLocationClick(sourceLocation)
}
return (
{rawContent || formattedContent ? (
) : null}
)
}
function PreviewLogEntryHeader({
sourceLocation,
level,
headerTitle,
headerIcon,
logType,
showSourceLocationLink = true,
showCloseButton = false,
onSourceLocationClick,
onClose,
}) {
const { t } = useTranslation()
const logLocationSpanRef = useRef()
const [locationSpanOverflown, setLocationSpanOverflown] = useState(false)
useResizeObserver(
logLocationSpanRef,
locationSpanOverflown,
checkLocationSpanOverflow
)
const file = sourceLocation ? sourceLocation.file : null
const line = sourceLocation ? sourceLocation.line : null
const logEntryHeaderClasses = classNames('log-entry-header', {
'log-entry-header-error': level === 'error',
'log-entry-header-warning': level === 'warning',
'log-entry-header-typesetting': level === 'typesetting',
'log-entry-header-raw': level === 'raw',
'log-entry-header-success': level === 'success',
})
const logEntryLocationBtnClasses = classNames('log-entry-header-link', {
'log-entry-header-link-error': level === 'error',
'log-entry-header-link-warning': level === 'warning',
'log-entry-header-link-typesetting': level === 'typesetting',
'log-entry-header-link-raw': level === 'raw',
'log-entry-header-link-success': level === 'success',
})
const headerLogLocationTitle = t('navigate_log_source', {
location: file + (line ? `, ${line}` : ''),
})
function checkLocationSpanOverflow(observedElement) {
const spanEl = observedElement.target
const isOverflowing = spanEl.scrollWidth > spanEl.clientWidth
setLocationSpanOverflown(isOverflowing)
}
const locationLinkText =
showSourceLocationLink && file ? `${file}${line ? `, ${line}` : ''}` : null
// Because we want an ellipsis on the left-hand side (e.g. "...longfilename.tex"), the
// `log-entry-header-link-location` class has text laid out from right-to-left using the CSS
// rule `direction: rtl;`.
// This works most of the times, except when the first character of the filename is considered
// a punctuation mark, like `/` (e.g. `/foo/bar/baz.sty`). In this case, because of
// right-to-left writing rules, the punctuation mark is moved to the right-side of the string,
// resulting in `...bar/baz.sty/` instead of `...bar/baz.sty`.
// To avoid this edge-case, we wrap the `logLocationLinkText` in two directional formatting
// characters:
// * \u202A LEFT-TO-RIGHT EMBEDDING Treat the following text as embedded left-to-right.
// * \u202C POP DIRECTIONAL FORMATTING End the scope of the last LRE, RLE, RLO, or LRO.
// This essentially tells the browser that, althought the text is laid out from right-to-left,
// the wrapped portion of text should follow left-to-right writing rules.
const locationLink = locationLinkText ? (
) : null
const locationTooltip =
locationSpanOverflown && locationLinkText ? (
{locationLinkText}
) : null
var headerTitleText = logType ? `${logType} ${headerTitle}` : headerTitle
return (
{headerIcon ? (