diff --git a/frontend/locales/en.json b/frontend/locales/en.json index 0d4b8814e..c6c216c0a 100644 --- a/frontend/locales/en.json +++ b/frontend/locales/en.json @@ -645,6 +645,7 @@ "cheatsheet": { "button": "Open Cheatsheet", "modal":{ + "popup": "Open in new tab", "title": "Cheatsheet", "headlines": { "description": "Description", diff --git a/frontend/src/components/common/icon-button/icon-button.tsx b/frontend/src/components/common/icon-button/icon-button.tsx index 65051f48b..88cd5cc20 100644 --- a/frontend/src/components/common/icon-button/icon-button.tsx +++ b/frontend/src/components/common/icon-button/icon-button.tsx @@ -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 } diff --git a/frontend/src/components/common/modals/common-modal.tsx b/frontend/src/components/common/modals/common-modal.tsx index e992b59d0..e4ef5670d 100644 --- a/frontend/src/components/common/modals/common-modal.tsx +++ b/frontend/src/components/common/modals/common-modal.tsx @@ -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> = ({ @@ -54,6 +56,7 @@ export const CommonModal: React.FC> = ({ additionalClasses, modalSize, children, + additionalTitleElement, ...props }) => { useTranslation() @@ -77,6 +80,7 @@ export const CommonModal: React.FC> = ({ {titleElement} + {additionalTitleElement ?? } {children} diff --git a/frontend/src/components/editor-page/app-bar/cheatsheet/cheatsheet-button.tsx b/frontend/src/components/editor-page/app-bar/cheatsheet/cheatsheet-button.tsx index d705bdaf4..7e46d00ef 100644 --- a/frontend/src/components/editor-page/app-bar/cheatsheet/cheatsheet-button.tsx +++ b/frontend/src/components/editor-page/app-bar/cheatsheet/cheatsheet-button.tsx @@ -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 = () => { - + titleI18nKey={'cheatsheet.modal.title'} + additionalTitleElement={ +
+ +
+ }> + + +
) diff --git a/frontend/src/components/editor-page/app-bar/cheatsheet/cheatsheet-modal-body.tsx b/frontend/src/components/editor-page/app-bar/cheatsheet/cheatsheet-content.tsx similarity index 55% rename from frontend/src/components/editor-page/app-bar/cheatsheet/cheatsheet-modal-body.tsx rename to frontend/src/components/editor-page/app-bar/cheatsheet/cheatsheet-content.tsx index f0c019d7f..6cb8e22e1 100644 --- a/frontend/src/components/editor-page/app-bar/cheatsheet/cheatsheet-modal-body.tsx +++ b/frontend/src/components/editor-page/app-bar/cheatsheet/cheatsheet-content.tsx @@ -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() const [selectedEntry, setSelectedEntry] = useState() @@ -31,35 +31,29 @@ export const CheatsheetModalBody: React.FC = () => { ) return ( - - - - + + + + + + - - - - - {selectedEntry !== undefined ? ( - - ) : ( - - - - )} - - - - + ) : ( + + + + )} + + + ) } diff --git a/frontend/src/components/editor-page/app-bar/cheatsheet/cheatsheet-in-new-tab-button.tsx b/frontend/src/components/editor-page/app-bar/cheatsheet/cheatsheet-in-new-tab-button.tsx new file mode 100644 index 000000000..073150b5f --- /dev/null +++ b/frontend/src/components/editor-page/app-bar/cheatsheet/cheatsheet-in-new-tab-button.tsx @@ -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 = ({ onClick }) => { + const openPopUp = useCallback( + (event: MouseEvent) => { + event.preventDefault() + window.open('/cheatsheet', '_blank', 'menubar=no,width=1000,height=600,location=no,toolbar=no') + onClick?.() + }, + [onClick] + ) + + const { t } = useTranslation() + + return ( + + ) +} diff --git a/frontend/src/pages/cheatsheet.tsx b/frontend/src/pages/cheatsheet.tsx new file mode 100644 index 000000000..be49c923b --- /dev/null +++ b/frontend/src/pages/cheatsheet.tsx @@ -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 ( + + + + ) +} + +export default CheatsheetPage