mirror of
https://github.com/hedgedoc/hedgedoc.git
synced 2024-11-21 09:16:30 -05:00
feature: add button to open cheatsheet in a new tab
Signed-off-by: Tilman Vatteroth <git@tilmanvatteroth.de>
This commit is contained in:
parent
438a5466e0
commit
9b9eafc948
7 changed files with 108 additions and 39 deletions
|
@ -645,6 +645,7 @@
|
|||
"cheatsheet": {
|
||||
"button": "Open Cheatsheet",
|
||||
"modal":{
|
||||
"popup": "Open in new tab",
|
||||
"title": "Cheatsheet",
|
||||
"headlines": {
|
||||
"description": "Description",
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
)
|
||||
|
|
|
@ -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>
|
||||
)
|
||||
}
|
|
@ -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') ?? ''}
|
||||
/>
|
||||
)
|
||||
}
|
21
frontend/src/pages/cheatsheet.tsx
Normal file
21
frontend/src/pages/cheatsheet.tsx
Normal 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
|
Loading…
Reference in a new issue