feature: add button to open cheatsheet in a new tab

Signed-off-by: Tilman Vatteroth <git@tilmanvatteroth.de>
This commit is contained in:
Tilman Vatteroth 2023-03-27 14:56:50 +02:00
parent 438a5466e0
commit 9b9eafc948
7 changed files with 108 additions and 39 deletions

View file

@ -645,6 +645,7 @@
"cheatsheet": {
"button": "Open Cheatsheet",
"modal":{
"popup": "Open in new tab",
"title": "Cheatsheet",
"headlines": {
"description": "Description",

View file

@ -15,7 +15,6 @@ import type { Icon } from 'react-bootstrap-icons'
export interface IconButtonProps extends ButtonProps, PropsWithDataTestId {
icon: Icon
onClick?: () => void
border?: boolean
iconSize?: number | string
}

View file

@ -8,8 +8,8 @@ import { cypressId } from '../../../utils/cypress-attribute'
import { testId } from '../../../utils/test-id'
import { UiIcon } from '../icons/ui-icon'
import { ShowIf } from '../show-if/show-if'
import type { PropsWithChildren } from 'react'
import React, { useMemo } from 'react'
import type { PropsWithChildren, ReactElement } from 'react'
import React, { Fragment, useMemo } from 'react'
import { Modal } from 'react-bootstrap'
import type { Icon } from 'react-bootstrap-icons'
import { Trans, useTranslation } from 'react-i18next'
@ -26,6 +26,7 @@ export interface ModalContentProps {
titleIcon?: Icon
modalSize?: 'lg' | 'sm' | 'xl'
additionalClasses?: string
additionalTitleElement?: ReactElement
}
export type CommonModalProps = PropsWithDataCypressId & ModalVisibilityProps & ModalContentProps
@ -42,6 +43,7 @@ export type CommonModalProps = PropsWithDataCypressId & ModalVisibilityProps & M
* @param modalSize The modal size
* @param children The children to render into the modal.
* @param titleIsI18nKey If the title is a i18n key and should be used as such
* @param additionalTitleElement additional optional element that should be shown in the header
* @param props Additional props directly given to the modal
*/
export const CommonModal: React.FC<PropsWithChildren<CommonModalProps>> = ({
@ -54,6 +56,7 @@ export const CommonModal: React.FC<PropsWithChildren<CommonModalProps>> = ({
additionalClasses,
modalSize,
children,
additionalTitleElement,
...props
}) => {
useTranslation()
@ -77,6 +80,7 @@ export const CommonModal: React.FC<PropsWithChildren<CommonModalProps>> = ({
<UiIcon icon={titleIcon} nbsp={true} />
{titleElement}
</Modal.Title>
{additionalTitleElement ?? <Fragment />}
</Modal.Header>
{children}
</Modal>

View file

@ -6,10 +6,10 @@
import { useBooleanState } from '../../../../hooks/common/use-boolean-state'
import { cypressId } from '../../../../utils/cypress-attribute'
import { CommonModal } from '../../../common/modals/common-modal'
import { CheatsheetModalBody } from './cheatsheet-modal-body'
import { CheatsheetContent } from './cheatsheet-content'
import { CheatsheetInNewTabButton } from './cheatsheet-in-new-tab-button'
import React, { Fragment } from 'react'
import { Button } from 'react-bootstrap'
import { QuestionCircle as IconQuestionCircle } from 'react-bootstrap-icons'
import { Button, Modal } from 'react-bootstrap'
import { Trans, useTranslation } from 'react-i18next'
/**
@ -32,12 +32,18 @@ export const CheatsheetButton: React.FC = () => {
</Button>
<CommonModal
modalSize={'xl'}
titleIcon={IconQuestionCircle}
show={modalVisibility}
onHide={closeModal}
showCloseButton={true}
titleI18nKey={'cheatsheet.modal.title'}>
<CheatsheetModalBody />
titleI18nKey={'cheatsheet.modal.title'}
additionalTitleElement={
<div className={'d-flex flex-row-reverse w-100 mx-2'}>
<CheatsheetInNewTabButton onClick={closeModal} />
</div>
}>
<Modal.Body>
<CheatsheetContent />
</Modal.Body>
</CommonModal>
</Fragment>
)

View file

@ -10,13 +10,13 @@ import { CategoryAccordion } from './category-accordion'
import { CheatsheetEntryPane } from './cheatsheet-entry-pane'
import { TopicSelection } from './topic-selection'
import React, { useCallback, useMemo, useState } from 'react'
import { Col, ListGroup, Modal, Row } from 'react-bootstrap'
import { Col, ListGroup, Row } from 'react-bootstrap'
import { Trans } from 'react-i18next'
/**
* Renders the tab content for the cheatsheet.
*/
export const CheatsheetModalBody: React.FC = () => {
export const CheatsheetContent: React.FC = () => {
const [selectedExtension, setSelectedExtension] = useState<CheatsheetExtension>()
const [selectedEntry, setSelectedEntry] = useState<CheatsheetEntry>()
@ -31,35 +31,29 @@ export const CheatsheetModalBody: React.FC = () => {
)
return (
<Modal.Body>
<Row className={`mt-2`}>
<Col xs={3}>
<CategoryAccordion
extensions={extensions}
selectedEntry={selectedExtension}
onStateChange={changeExtension}
<Row className={`mt-2`}>
<Col xs={3}>
<CategoryAccordion extensions={extensions} selectedEntry={selectedExtension} onStateChange={changeExtension} />
</Col>
<Col xs={9}>
<ListGroup>
<TopicSelection
extension={selectedExtension}
selectedEntry={selectedEntry}
setSelectedEntry={setSelectedEntry}
/>
</Col>
<Col xs={9}>
<ListGroup>
<TopicSelection
extension={selectedExtension}
selectedEntry={selectedEntry}
setSelectedEntry={setSelectedEntry}
{selectedEntry !== undefined ? (
<CheatsheetEntryPane
rootI18nKey={isCheatsheetGroup(selectedExtension) ? selectedExtension.i18nKey : undefined}
extension={selectedEntry}
/>
{selectedEntry !== undefined ? (
<CheatsheetEntryPane
rootI18nKey={isCheatsheetGroup(selectedExtension) ? selectedExtension.i18nKey : undefined}
extension={selectedEntry}
/>
) : (
<span>
<Trans i18nKey={'cheatsheet.modal.noSelection'}></Trans>
</span>
)}
</ListGroup>
</Col>
</Row>
</Modal.Body>
) : (
<span>
<Trans i18nKey={'cheatsheet.modal.noSelection'}></Trans>
</span>
)}
</ListGroup>
</Col>
</Row>
)
}

View file

@ -0,0 +1,44 @@
/*
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { IconButton } from '../../../common/icon-button/icon-button'
import type { MouseEvent } from 'react'
import React, { useCallback } from 'react'
import { BoxArrowUpRight } from 'react-bootstrap-icons'
import { useTranslation } from 'react-i18next'
export interface CheatsheetInNewTabButtonProps {
onClick?: () => void
}
/**
* Renders a button that opens the cheatsheet in a new tab.
*/
export const CheatsheetInNewTabButton: React.FC<CheatsheetInNewTabButtonProps> = ({ onClick }) => {
const openPopUp = useCallback(
(event: MouseEvent<HTMLButtonElement>) => {
event.preventDefault()
window.open('/cheatsheet', '_blank', 'menubar=no,width=1000,height=600,location=no,toolbar=no')
onClick?.()
},
[onClick]
)
const { t } = useTranslation()
return (
<IconButton
size={'sm'}
iconSize={1.2}
href={'/cheatsheet'}
onClick={openPopUp}
icon={BoxArrowUpRight}
className={'p-2 border-0'}
variant={'outline-dark'}
target={'_blank'}
title={t('cheatsheet.modal.popup') ?? ''}
/>
)
}

View file

@ -0,0 +1,21 @@
/*
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { CheatsheetContent } from '../components/editor-page/app-bar/cheatsheet/cheatsheet-content'
import { useApplyDarkMode } from '../hooks/common/use-apply-dark-mode'
import type { NextPage } from 'next'
import { Container } from 'react-bootstrap'
const CheatsheetPage: NextPage = () => {
useApplyDarkMode()
return (
<Container>
<CheatsheetContent></CheatsheetContent>
</Container>
)
}
export default CheatsheetPage