diff --git a/frontend/locales/en.json b/frontend/locales/en.json index cc098f68a..f3cb28abe 100644 --- a/frontend/locales/en.json +++ b/frontend/locales/en.json @@ -234,35 +234,6 @@ "untitledNote": "Untitled", "placeholder": "← Start by entering a title here\n===\nVisit {{host}}features if you don't know what to do.\nHappy hacking :)", "infoToc": "Structure your note with headings to see a table-of-contents here.", - "help": { - "shortcuts": { - "title": "Shortcuts", - "bold": "Make selection bold", - "italic": "Make selection italic", - "underline": "Underline selection", - "strikethrough": "Strike selection through", - "mark": "Mark selection", - "link": "Add link around selection", - "view": "Show only View", - "both": "Show View and Edit", - "edit": "Show only Edit" - }, - "links": { - "title": "Links" - }, - "contacts": { - "title": "Contacts", - "community": "Join the community", - "meetUsOn": "Meet us on {{service}}", - "helpTranslating": "Help us translating", - "reportIssue": "Report an issue" - }, - "documents": { - "title": "Documents", - "yamlMetadata": "YAML Metadata", - "slideExample": "Slide Example" - } - }, "onlineStatus": { "online": "Online", "you": "(You)" @@ -513,6 +484,15 @@ "editNote": "Edit this note" } }, + "appbar": { + "help": { + "help": { + "header": "Help", + "shortcuts": "Shortcuts", + "cheatsheet": "Cheatsheet" + } + } + }, "common": { "yes": "Yes", "no": "No", @@ -643,6 +623,24 @@ } } }, + "shortcuts": { + "title": "Shortcuts", + "editor": { + "header": "Editor", + "bold": "Make selection bold", + "italic": "Make selection italic", + "underline": "Underline selection", + "strikethrough": "Strike selection through", + "mark": "Mark selection", + "link": "Add link around selection" + }, + "viewMode": { + "header": "View Mode", + "view": "Show only View", + "both": "Show View and Edit", + "edit": "Show only Edit" + } + }, "cheatsheet": { "button": "Open Cheatsheet", "search": "Search for Cheatsheets", diff --git a/frontend/src/components/editor-page/app-bar/help-button/help-button.tsx b/frontend/src/components/editor-page/app-bar/help-button/help-button.tsx index 5fd4ecacf..2c00ee0ee 100644 --- a/frontend/src/components/editor-page/app-bar/help-button/help-button.tsx +++ b/frontend/src/components/editor-page/app-bar/help-button/help-button.tsx @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file) + * SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file) * * SPDX-License-Identifier: AGPL-3.0-only */ @@ -8,13 +8,13 @@ import { useTranslatedText } from '../../../../hooks/common/use-translated-text' import { useOutlineButtonVariant } from '../../../../hooks/dark-mode/use-outline-button-variant' import { cypressId } from '../../../../utils/cypress-attribute' import { IconButton } from '../../../common/icon-button/icon-button' -import { HelpModal } from './help-modal' +import { ShortcutsModal } from '../../../global-dialogs/shortcuts-modal/shortcuts-modal' import React, { Fragment } from 'react' import { QuestionCircle as IconQuestionCircle } from 'react-bootstrap-icons' import { Trans } from 'react-i18next' /** - * Renders the button to open the {@link HelpModal}. + * Renders the button to open the shortcuts modal. */ export const HelpButton: React.FC = () => { const [modalVisibility, showModal, closeModal] = useBooleanState() @@ -33,7 +33,7 @@ export const HelpButton: React.FC = () => { onClick={showModal}> - + ) } diff --git a/frontend/src/components/editor-page/app-bar/help-button/help-modal.tsx b/frontend/src/components/editor-page/app-bar/help-button/help-modal.tsx deleted file mode 100644 index 76b401ee2..000000000 --- a/frontend/src/components/editor-page/app-bar/help-button/help-modal.tsx +++ /dev/null @@ -1,67 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file) - * - * SPDX-License-Identifier: AGPL-3.0-only - */ -import type { ModalVisibilityProps } from '../../../common/modals/common-modal' -import { CommonModal } from '../../../common/modals/common-modal' -import { LinksTabContent } from './links-tab-content' -import { ShortcutTabContent } from './shortcuts-tab-content' -import React, { useMemo, useState } from 'react' -import { Button, Modal } from 'react-bootstrap' -import { QuestionCircle as IconQuestionCircle } from 'react-bootstrap-icons' -import { Trans, useTranslation } from 'react-i18next' - -export enum HelpTabStatus { - Shortcuts = 'shortcuts.title', - Links = 'links.title' -} - -/** - * Renders the help modal. - * This modal shows the user the markdown cheatsheet, shortcuts and different links with further help. - * - * @see CheatsheetTabContent - * @see ShortcutTabContent - * @see LinksTabContent - * - * @param show If the modal should be shown - * @param onHide A callback when the modal should be closed again - */ -export const HelpModal: React.FC = ({ show, onHide }) => { - const [tab, setTab] = useState(HelpTabStatus.Shortcuts) - const { t } = useTranslation() - - const tabContent = useMemo(() => { - switch (tab) { - case HelpTabStatus.Shortcuts: - return - case HelpTabStatus.Links: - return - } - }, [tab]) - - const modalTitle = useMemo(() => t('editor.documentBar.help') + ' - ' + t(`editor.help.${tab}`), [t, tab]) - - return ( - - - - {tabContent} - - - ) -} diff --git a/frontend/src/components/editor-page/app-bar/help-button/links-tab-content.tsx b/frontend/src/components/editor-page/app-bar/help-button/links-tab-content.tsx deleted file mode 100644 index 136e3a07d..000000000 --- a/frontend/src/components/editor-page/app-bar/help-button/links-tab-content.tsx +++ /dev/null @@ -1,92 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file) - * - * SPDX-License-Identifier: AGPL-3.0-only - */ -import links from '../../../../links.json' -import { IconMatrixOrg } from '../../../common/icons/additional/icon-matrix-org' -import { TranslatedExternalLink } from '../../../common/links/translated-external-link' -import { TranslatedInternalLink } from '../../../common/links/translated-internal-link' -import React from 'react' -import { Col, Row } from 'react-bootstrap' -import { Dot as IconDot, Flag as IconFlag, PeopleFill as IconPeopleFill, Tag as IconTag } from 'react-bootstrap-icons' -import { Trans, useTranslation } from 'react-i18next' - -/** - * Renders a bunch of links, where further help can be requested. - */ -export const LinksTabContent: React.FC = () => { - useTranslation() - - return ( - - -

- -

-
-
    -
  • - -
  • -
  • - -
  • -
  • - -
  • -
  • - -
  • -
-
- - -

- -

-
-
    -
  • - -
  • -
  • - -
  • -
-
- -
- ) -} diff --git a/frontend/src/components/editor-page/app-bar/help-button/shortcuts-tab-content.tsx b/frontend/src/components/editor-page/app-bar/help-button/shortcuts-tab-content.tsx deleted file mode 100644 index 4d259dda1..000000000 --- a/frontend/src/components/editor-page/app-bar/help-button/shortcuts-tab-content.tsx +++ /dev/null @@ -1,60 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file) - * - * SPDX-License-Identifier: AGPL-3.0-only - */ -import { isMac } from '../../utils' -import React, { Fragment, useMemo } from 'react' -import { Card, ListGroup, Row } from 'react-bootstrap' -import { Trans } from 'react-i18next' - -/** - * Renders a list of shortcuts usable in HedgeDoc. - */ -export const ShortcutTabContent: React.FC = () => { - const modifierKey = useMemo(() => (isMac() ? : Ctrl), []) - const altKey = useMemo(() => (isMac() ? : Alt), []) - - const shortcutMap: { [category: string]: { [functionName: string]: JSX.Element[] } } = { - 'View Mode': { - 'editor.help.shortcuts.view': [Ctrl, <> + , altKey, <> + , V], - 'editor.help.shortcuts.both': [Ctrl, <> + , altKey, <> + , B], - 'editor.help.shortcuts.edit': [Ctrl, <> + , altKey, <> + , E] - }, - Editor: { - 'editor.help.shortcuts.bold': [modifierKey, <> + , B], - 'editor.help.shortcuts.italic': [modifierKey, <> + , I], - 'editor.help.shortcuts.underline': [modifierKey, <> + , U], - 'editor.help.shortcuts.strikethrough': [modifierKey, <> + , D], - 'editor.help.shortcuts.mark': [modifierKey, <> + , M], - 'editor.help.shortcuts.link': [modifierKey, <> + , K] - } - } - return ( - - {Object.keys(shortcutMap).map((category) => { - return ( - - {category} - - {Object.entries(shortcutMap[category]).map(([functionName, shortcuts]) => { - return ( - - - - - - {shortcuts.map((shortcut, shortcutIndex) => ( - {shortcut} - ))} - - - ) - })} - - - ) - })} - - ) -} diff --git a/frontend/src/components/global-dialogs/shortcuts-modal/alt-key.tsx b/frontend/src/components/global-dialogs/shortcuts-modal/alt-key.tsx new file mode 100644 index 000000000..1a107b45d --- /dev/null +++ b/frontend/src/components/global-dialogs/shortcuts-modal/alt-key.tsx @@ -0,0 +1,14 @@ +/* + * SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file) + * + * SPDX-License-Identifier: AGPL-3.0-only + */ +import { isMac } from '../../editor-page/utils' +import React from 'react' + +/** + * Renders a keyboard alt/option key hint depending on if the browser is running on macOS or not. + */ +export const AltKey: React.FC = () => { + return isMac() ? : Alt +} diff --git a/frontend/src/components/global-dialogs/shortcuts-modal/category-card.tsx b/frontend/src/components/global-dialogs/shortcuts-modal/category-card.tsx new file mode 100644 index 000000000..58f4a330f --- /dev/null +++ b/frontend/src/components/global-dialogs/shortcuts-modal/category-card.tsx @@ -0,0 +1,32 @@ +/* + * SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file) + * + * SPDX-License-Identifier: AGPL-3.0-only + */ +import type { PropsWithChildren } from 'react' +import React from 'react' +import { Card, ListGroup } from 'react-bootstrap' +import { Trans, useTranslation } from 'react-i18next' + +interface CategoryProps extends PropsWithChildren { + headerI18nKey: string +} + +/** + * Renders a group of shortcut lines + * + * @param headerI18nKey The i18n key of the header + * @param children The lines to include + */ +export const CategoryCard: React.FC = ({ headerI18nKey, children }) => { + useTranslation() + + return ( + + + + + {children} + + ) +} diff --git a/frontend/src/components/global-dialogs/shortcuts-modal/modifier-key.tsx b/frontend/src/components/global-dialogs/shortcuts-modal/modifier-key.tsx new file mode 100644 index 000000000..3cb58f781 --- /dev/null +++ b/frontend/src/components/global-dialogs/shortcuts-modal/modifier-key.tsx @@ -0,0 +1,14 @@ +/* + * SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file) + * + * SPDX-License-Identifier: AGPL-3.0-only + */ +import { isMac } from '../../editor-page/utils' +import React from 'react' + +/** + * Renders a keyboard control/command key hint depending on if the browser is running on macOS or not. + */ +export const ModifierKey: React.FC = () => { + return isMac() ? : Ctrl +} diff --git a/frontend/src/components/global-dialogs/shortcuts-modal/shortcut-line.tsx b/frontend/src/components/global-dialogs/shortcuts-modal/shortcut-line.tsx new file mode 100644 index 000000000..3ae02c853 --- /dev/null +++ b/frontend/src/components/global-dialogs/shortcuts-modal/shortcut-line.tsx @@ -0,0 +1,56 @@ +/* + * SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file) + * + * SPDX-License-Identifier: AGPL-3.0-only + */ +import { ShowIf } from '../../common/show-if/show-if' +import { AltKey } from './alt-key' +import { ModifierKey } from './modifier-key' +import React from 'react' +import { ListGroup } from 'react-bootstrap' +import { Trans, useTranslation } from 'react-i18next' + +interface ShortcutLineProps { + functionNameI18nKey: string + showModifierKey: boolean + showAltKey: boolean + functionKeyCode: string +} + +/** + * Renders one shortcut hint for the modal + * + * @param functionNameI18nKey The i18n key of the function name that is associated to the shortcut + * @param showAltKey Defines if the shortcut requires the alt/option key + * @param showModifierKey Defines if the shortcut requires the control/command key + * @param functionKeyCode The actual key of the shortcut + */ +export const ShortcutLine: React.FC = ({ + functionNameI18nKey, + showAltKey, + showModifierKey, + functionKeyCode +}) => { + useTranslation() + + return ( + + + + + + + + + + + + + + + + + + {functionKeyCode.toUpperCase()} + + + ) +} diff --git a/frontend/src/components/global-dialogs/shortcuts-modal/shortcuts-content.tsx b/frontend/src/components/global-dialogs/shortcuts-modal/shortcuts-content.tsx new file mode 100644 index 000000000..14a1250b0 --- /dev/null +++ b/frontend/src/components/global-dialogs/shortcuts-modal/shortcuts-content.tsx @@ -0,0 +1,78 @@ +/* + * SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file) + * + * SPDX-License-Identifier: AGPL-3.0-only + */ +import { CategoryCard } from './category-card' +import { ShortcutLine } from './shortcut-line' +import React from 'react' +import { Container } from 'react-bootstrap' + +/** + * Renders a list of shortcuts usable in HedgeDoc. + */ +export const ShortcutsContent: React.FC = () => { + return ( + + + + + + + + + + + + + + + + + ) +} diff --git a/frontend/src/components/global-dialogs/shortcuts-modal/shortcuts-modal.tsx b/frontend/src/components/global-dialogs/shortcuts-modal/shortcuts-modal.tsx new file mode 100644 index 000000000..000c7135a --- /dev/null +++ b/frontend/src/components/global-dialogs/shortcuts-modal/shortcuts-modal.tsx @@ -0,0 +1,29 @@ +/* + * SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file) + * + * SPDX-License-Identifier: AGPL-3.0-only + */ +import { useTranslatedText } from '../../../hooks/common/use-translated-text' +import type { ModalVisibilityProps } from '../../common/modals/common-modal' +import { CommonModal } from '../../common/modals/common-modal' +import { ShortcutsContent } from './shortcuts-content' +import React from 'react' +import { Modal } from 'react-bootstrap' +import { QuestionCircle as IconQuestionCircle } from 'react-bootstrap-icons' + +/** + * Renders the keyboard shortcuts overview modal. + * @param show True when the modal should be shown + * @param onHide Callback when the modal should be hidden + */ +export const ShortcutsModal: React.FC = ({ show, onHide }) => { + const title = useTranslatedText('shortcuts.title') + + return ( + + + + + + ) +}