mirror of
https://github.com/hedgedoc/hedgedoc.git
synced 2024-11-21 09:16:30 -05:00
feat(frontend): add basic print functionality
Co-authored-by: Philip Molares <philip.molares@udo.edu> Signed-off-by: Erik Michelson <github@erik.michelson.eu>
This commit is contained in:
parent
d726e6d94e
commit
8a1b29fddc
14 changed files with 123 additions and 8 deletions
|
@ -971,5 +971,11 @@
|
|||
"example": "```markdown=12\nline1\n```\n```markdown=+\nline2\n```\n```markdown=\nline3\n```"
|
||||
}
|
||||
}
|
||||
},
|
||||
"print": {
|
||||
"warning": {
|
||||
"title": "Warning!",
|
||||
"text": "To print this note, please use the print button in the export menu in the sidebar. Printing this page directly will not work as expected."
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -177,11 +177,14 @@ export const RendererIframe: React.FC<RendererIframeProps> = ({
|
|||
<Fragment>
|
||||
{!rendererReady && showWaitSpinner && <WaitSpinner />}
|
||||
<iframe
|
||||
id={'editor-renderer-iframe'}
|
||||
style={{ height: `${frameHeight}px` }}
|
||||
{...cypressId('documentIframe')}
|
||||
onLoad={onIframeLoad}
|
||||
title='render'
|
||||
{...(isTestMode ? {} : { sandbox: 'allow-downloads allow-same-origin allow-scripts allow-popups' })}
|
||||
{...(isTestMode
|
||||
? {}
|
||||
: { sandbox: 'allow-downloads allow-same-origin allow-scripts allow-popups allow-modals' })}
|
||||
allowFullScreen={true}
|
||||
ref={frameReference}
|
||||
referrerPolicy={'no-referrer'}
|
||||
|
|
|
@ -7,3 +7,9 @@
|
|||
.frame {
|
||||
color-scheme: initial;
|
||||
}
|
||||
|
||||
@media print {
|
||||
.frame {
|
||||
height: auto !important;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,8 +15,11 @@ import { useUpdateLocalHistoryEntry } from './hooks/use-update-local-history-ent
|
|||
import { RendererPane } from './renderer-pane/renderer-pane'
|
||||
import { Sidebar } from './sidebar/sidebar'
|
||||
import { Splitter } from './splitter/splitter'
|
||||
import { PrintWarning } from './print-warning/print-warning'
|
||||
import React, { useMemo, useRef } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import './print.scss'
|
||||
import { usePrintKeyboardShortcut } from './hooks/use-print-keyboard-shortcut'
|
||||
|
||||
export enum ScrollSource {
|
||||
EDITOR = 'editor',
|
||||
|
@ -28,7 +31,7 @@ export enum ScrollSource {
|
|||
*/
|
||||
export const EditorPageContent: React.FC = () => {
|
||||
useTranslation()
|
||||
|
||||
usePrintKeyboardShortcut()
|
||||
useUpdateLocalHistoryEntry()
|
||||
|
||||
const scrollSource = useRef<ScrollSource>(ScrollSource.EDITOR)
|
||||
|
@ -68,6 +71,7 @@ export const EditorPageContent: React.FC = () => {
|
|||
<ExtensionEventEmitterProvider>
|
||||
{editorExtensionComponents}
|
||||
<CommunicatorImageLightbox />
|
||||
<PrintWarning />
|
||||
<div className={'flex-fill d-flex h-100 w-100 overflow-hidden flex-row'}>
|
||||
<Splitter
|
||||
left={leftPane}
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import { printIframe } from '../utils/print-iframe'
|
||||
import { useEffect } from 'react'
|
||||
|
||||
/**
|
||||
* Hook to listen for the print keyboard shortcut and print the content of the renderer iframe.
|
||||
*/
|
||||
export const usePrintKeyboardShortcut = (): void => {
|
||||
useEffect(() => {
|
||||
const handlePrint = (event: KeyboardEvent): void => {
|
||||
if (event.key === 'p' && (event.ctrlKey || event.metaKey)) {
|
||||
event.preventDefault()
|
||||
printIframe()
|
||||
}
|
||||
}
|
||||
|
||||
window.addEventListener('keydown', handlePrint)
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('keydown', handlePrint)
|
||||
}
|
||||
}, [])
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
import React from 'react'
|
||||
import { Alert } from 'react-bootstrap'
|
||||
import { Trans } from 'react-i18next'
|
||||
|
||||
export const PrintWarning: React.FC = () => {
|
||||
return (
|
||||
<div className={'d-none d-print-block'}>
|
||||
<Alert variant={'warning'}>
|
||||
<Alert.Heading>
|
||||
<Trans i18nKey={'print.warning.title'} />
|
||||
</Alert.Heading>
|
||||
<p>
|
||||
<Trans i18nKey={'print.warning.text'} />
|
||||
</p>
|
||||
</Alert>
|
||||
</div>
|
||||
)
|
||||
}
|
15
frontend/src/components/editor-page/print.scss
Normal file
15
frontend/src/components/editor-page/print.scss
Normal file
|
@ -0,0 +1,15 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
@media print {
|
||||
body {
|
||||
& > div.d-flex {
|
||||
nav, #editor-edit-pane, #editor-splitter, #editor-sidebar, #editor-view-pane {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -43,7 +43,7 @@ export const Sidebar: React.FC = () => {
|
|||
const selectionIsNotNone = selectedMenu !== DocumentSidebarMenuSelection.NONE
|
||||
|
||||
return (
|
||||
<div className={styles['slide-sidebar']}>
|
||||
<div className={styles['slide-sidebar']} id={'editor-sidebar'}>
|
||||
<div ref={sideBarRef} className={`${styles['sidebar-inner']} ${selectionIsNotNone ? styles['show'] : ''}`}>
|
||||
<UsersOnlineSidebarMenu
|
||||
menuId={DocumentSidebarMenuSelection.USERS_ONLINE}
|
||||
|
|
|
@ -8,13 +8,14 @@ import { SidebarButton } from '../../../sidebar-button/sidebar-button'
|
|||
import React, { useCallback } from 'react'
|
||||
import { PrinterFill as IconPrinterFill } from 'react-bootstrap-icons'
|
||||
import { Trans } from 'react-i18next'
|
||||
import { printIframe } from '../../../../utils/print-iframe'
|
||||
|
||||
/**
|
||||
* Editor sidebar entry for exporting the markdown content into a local file.
|
||||
*/
|
||||
export const ExportPrintSidebarEntry: React.FC = () => {
|
||||
const onClick = useCallback(() => {
|
||||
window.print()
|
||||
printIframe()
|
||||
}, [])
|
||||
|
||||
return (
|
||||
|
|
|
@ -60,7 +60,7 @@ export const SplitDivider: React.FC<SplitDividerProps> = ({
|
|||
}, [dividerButtonsShift, forceOpen])
|
||||
|
||||
return (
|
||||
<div className={styles.divider} {...testId('splitter-divider')}>
|
||||
<div className={styles.divider} {...testId('splitter-divider')} id={'editor-splitter'}>
|
||||
<div className={className}>
|
||||
<div className={styles.buttons}>
|
||||
<Button variant={focusLeft ? 'secondary' : 'light'} onClick={onLeftButtonClick}>
|
||||
|
|
|
@ -150,7 +150,10 @@ export const Splitter: React.FC<SplitterProps> = ({ additionalContainerClassName
|
|||
onTouchEnd={onStopResizing}
|
||||
onMouseUp={onStopResizing}></div>
|
||||
)}
|
||||
<div className={styles['left']} style={{ width: `calc(${adjustedRelativeSplitValue}% - 5px)` }}>
|
||||
<div
|
||||
id={'editor-edit-pane'}
|
||||
className={styles['left']}
|
||||
style={{ width: `calc(${adjustedRelativeSplitValue}% - 5px)` }}>
|
||||
<div className={styles['inner']}>{left}</div>
|
||||
</div>
|
||||
<SplitDivider
|
||||
|
@ -162,7 +165,10 @@ export const Splitter: React.FC<SplitterProps> = ({ additionalContainerClassName
|
|||
focusRight={relativeSplitValue > 100 - SNAP_PERCENTAGE}
|
||||
dividerButtonsShift={dividerButtonsShift}
|
||||
/>
|
||||
<div className={styles['right']} style={{ width: `calc(100% - ${adjustedRelativeSplitValue}%)` }}>
|
||||
<div
|
||||
id={'editor-view-pane'}
|
||||
className={styles['right']}
|
||||
style={{ width: `calc(100% - ${adjustedRelativeSplitValue}%)` }}>
|
||||
<div className={styles['inner']}>{right}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
16
frontend/src/components/editor-page/utils/print-iframe.ts
Normal file
16
frontend/src/components/editor-page/utils/print-iframe.ts
Normal file
|
@ -0,0 +1,16 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2024 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
/**
|
||||
* Prints the content of the renderer iframe.
|
||||
*/
|
||||
export const printIframe = (): void => {
|
||||
const iframe = document.getElementById('editor-renderer-iframe') as HTMLIFrameElement
|
||||
if (!iframe) {
|
||||
return
|
||||
}
|
||||
iframe.contentWindow.print()
|
||||
}
|
|
@ -76,7 +76,7 @@ export const DocumentMarkdownRenderer: React.FC<DocumentMarkdownRendererProps> =
|
|||
|
||||
return (
|
||||
<div
|
||||
className={`${styles.document} vh-100`}
|
||||
className={`vh-100 ${styles.document}`}
|
||||
ref={internalDocumentRenderPaneRef}
|
||||
onScroll={onUserScroll}
|
||||
data-scroll-element={true}
|
||||
|
|
|
@ -27,3 +27,10 @@
|
|||
width: 900px;
|
||||
}
|
||||
}
|
||||
|
||||
@media print {
|
||||
.document {
|
||||
height: auto !important;
|
||||
color: #000;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue