From 14ba7ea9cedc5846e4ac7ccaa24ca58c8841d89a Mon Sep 17 00:00:00 2001 From: Tilman Vatteroth Date: Wed, 31 May 2023 20:14:37 +0200 Subject: [PATCH] refactor: extract visual part of the toolbar-button component and use it in all buttons components Signed-off-by: Tilman Vatteroth --- frontend/cypress/e2e/fileUpload.spec.ts | 8 ++-- .../tool-bar/buttons/bold-button.tsx | 4 +- .../tool-bar/buttons/check-list-button.tsx | 4 +- .../tool-bar/buttons/code-fence-button.tsx | 4 +- .../buttons/collapsible-block-button.tsx | 4 +- .../tool-bar/buttons/comment-button.tsx | 4 +- .../tool-bar/buttons/header-level-button.tsx | 4 +- .../tool-bar/buttons/highlight-button.tsx | 4 +- .../buttons/horizontal-line-button.tsx | 4 +- .../tool-bar/buttons/image-link-button.tsx | 4 +- .../tool-bar/buttons/italic-button.tsx | 4 +- .../tool-bar/buttons/link-button.tsx | 4 +- .../tool-bar/buttons/ordered-list-button.tsx | 4 +- .../tool-bar/buttons/quotes-button.tsx | 4 +- .../tool-bar/buttons/strikethrough-button.tsx | 4 +- .../tool-bar/buttons/subscript-button.tsx | 4 +- .../tool-bar/buttons/superscript-button.tsx | 4 +- .../tool-bar/buttons/underline-button.tsx | 4 +- .../buttons/unordered-list-button.tsx | 4 +- .../emoji-picker/emoji-picker-button.tsx | 19 ++-------- .../tool-bar/formatter-toolbar-button.tsx | 31 ++++++++++++++++ .../table-picker/table-picker-button.tsx | 23 ++++-------- .../table-picker/table-picker.module.scss | 10 ++++- .../table-size-picker-popover.tsx | 5 ++- .../editor-pane/tool-bar/toolbar-button.tsx | 37 +++++++++++-------- .../upload-image-button.tsx | 31 ++++++---------- 26 files changed, 127 insertions(+), 109 deletions(-) create mode 100644 frontend/src/components/editor-page/editor-pane/tool-bar/formatter-toolbar-button.tsx diff --git a/frontend/cypress/e2e/fileUpload.spec.ts b/frontend/cypress/e2e/fileUpload.spec.ts index ffe6738fd..9dfb469bc 100644 --- a/frontend/cypress/e2e/fileUpload.spec.ts +++ b/frontend/cypress/e2e/fileUpload.spec.ts @@ -29,8 +29,8 @@ describe('File upload', () => { }) it('via button', () => { cy.getByCypressId('editor-pane').should('have.attr', 'data-cypress-editor-ready', 'true') - cy.getByCypressId('editor-toolbar-upload-image-button').should('be.visible') - cy.getByCypressId('editor-toolbar-upload-image-input').selectFile( + cy.getByCypressId('toolbar.uploadImage').should('be.visible') + cy.getByCypressId('toolbar.uploadImage.input').selectFile( { contents: '@demoImage', fileName: 'demo.png', @@ -80,8 +80,8 @@ describe('File upload', () => { statusCode: 400 } ) - cy.getByCypressId('editor-toolbar-upload-image-button').should('be.visible') - cy.getByCypressId('editor-toolbar-upload-image-input').selectFile( + cy.getByCypressId('toolbar.uploadImage').should('be.visible') + cy.getByCypressId('toolbar.uploadImage.input').selectFile( { contents: '@demoImage', fileName: 'demo.png', diff --git a/frontend/src/components/editor-page/editor-pane/tool-bar/buttons/bold-button.tsx b/frontend/src/components/editor-page/editor-pane/tool-bar/buttons/bold-button.tsx index 72e49095e..279fd73b5 100644 --- a/frontend/src/components/editor-page/editor-pane/tool-bar/buttons/bold-button.tsx +++ b/frontend/src/components/editor-page/editor-pane/tool-bar/buttons/bold-button.tsx @@ -4,8 +4,8 @@ * SPDX-License-Identifier: AGPL-3.0-only */ import type { ContentFormatter } from '../../../change-content-context/use-change-editor-content-callback' +import { FormatterToolbarButton } from '../formatter-toolbar-button' import { wrapSelection } from '../formatters/wrap-selection' -import { ToolbarButton } from '../toolbar-button' import React, { useCallback } from 'react' import { TypeBold as IconTypeBold } from 'react-bootstrap-icons' @@ -16,5 +16,5 @@ export const BoldButton: React.FC = () => { const formatter: ContentFormatter = useCallback(({ currentSelection }) => { return wrapSelection(currentSelection, '**', '**') }, []) - return + return } diff --git a/frontend/src/components/editor-page/editor-pane/tool-bar/buttons/check-list-button.tsx b/frontend/src/components/editor-page/editor-pane/tool-bar/buttons/check-list-button.tsx index 4d992b001..62ac39e28 100644 --- a/frontend/src/components/editor-page/editor-pane/tool-bar/buttons/check-list-button.tsx +++ b/frontend/src/components/editor-page/editor-pane/tool-bar/buttons/check-list-button.tsx @@ -4,8 +4,8 @@ * SPDX-License-Identifier: AGPL-3.0-only */ import type { ContentFormatter } from '../../../change-content-context/use-change-editor-content-callback' +import { FormatterToolbarButton } from '../formatter-toolbar-button' import { prependLinesOfSelection } from '../formatters/prepend-lines-of-selection' -import { ToolbarButton } from '../toolbar-button' import React, { useCallback } from 'react' import { CheckSquare as IconCheckSquare } from 'react-bootstrap-icons' @@ -16,5 +16,5 @@ export const CheckListButton: React.FC = () => { const formatter: ContentFormatter = useCallback(({ currentSelection, markdownContent }) => { return prependLinesOfSelection(markdownContent, currentSelection, () => `- [ ] `) }, []) - return + return } diff --git a/frontend/src/components/editor-page/editor-pane/tool-bar/buttons/code-fence-button.tsx b/frontend/src/components/editor-page/editor-pane/tool-bar/buttons/code-fence-button.tsx index 49654d0e9..ed3e269d3 100644 --- a/frontend/src/components/editor-page/editor-pane/tool-bar/buttons/code-fence-button.tsx +++ b/frontend/src/components/editor-page/editor-pane/tool-bar/buttons/code-fence-button.tsx @@ -4,9 +4,9 @@ * SPDX-License-Identifier: AGPL-3.0-only */ import type { ContentFormatter } from '../../../change-content-context/use-change-editor-content-callback' +import { FormatterToolbarButton } from '../formatter-toolbar-button' import { changeCursorsToWholeLineIfNoToCursor } from '../formatters/utils/change-cursors-to-whole-line-if-no-to-cursor' import { wrapSelection } from '../formatters/wrap-selection' -import { ToolbarButton } from '../toolbar-button' import React, { useCallback } from 'react' import { Code as IconCode } from 'react-bootstrap-icons' @@ -17,5 +17,5 @@ export const CodeFenceButton: React.FC = () => { const formatter: ContentFormatter = useCallback(({ currentSelection, markdownContent }) => { return wrapSelection(changeCursorsToWholeLineIfNoToCursor(markdownContent, currentSelection), '```\n', '\n```') }, []) - return + return } diff --git a/frontend/src/components/editor-page/editor-pane/tool-bar/buttons/collapsible-block-button.tsx b/frontend/src/components/editor-page/editor-pane/tool-bar/buttons/collapsible-block-button.tsx index 2a322eadd..d1712b05e 100644 --- a/frontend/src/components/editor-page/editor-pane/tool-bar/buttons/collapsible-block-button.tsx +++ b/frontend/src/components/editor-page/editor-pane/tool-bar/buttons/collapsible-block-button.tsx @@ -4,9 +4,9 @@ * SPDX-License-Identifier: AGPL-3.0-only */ import type { ContentFormatter } from '../../../change-content-context/use-change-editor-content-callback' +import { FormatterToolbarButton } from '../formatter-toolbar-button' import { changeCursorsToWholeLineIfNoToCursor } from '../formatters/utils/change-cursors-to-whole-line-if-no-to-cursor' import { wrapSelection } from '../formatters/wrap-selection' -import { ToolbarButton } from '../toolbar-button' import React, { useCallback } from 'react' import { ArrowsCollapse as IconArrowsCollapse } from 'react-bootstrap-icons' @@ -21,5 +21,5 @@ export const CollapsibleBlockButton: React.FC = () => { '\n:::\n' ) }, []) - return + return } diff --git a/frontend/src/components/editor-page/editor-pane/tool-bar/buttons/comment-button.tsx b/frontend/src/components/editor-page/editor-pane/tool-bar/buttons/comment-button.tsx index 654e365b9..073956e6d 100644 --- a/frontend/src/components/editor-page/editor-pane/tool-bar/buttons/comment-button.tsx +++ b/frontend/src/components/editor-page/editor-pane/tool-bar/buttons/comment-button.tsx @@ -4,8 +4,8 @@ * SPDX-License-Identifier: AGPL-3.0-only */ import type { ContentFormatter } from '../../../change-content-context/use-change-editor-content-callback' +import { FormatterToolbarButton } from '../formatter-toolbar-button' import { replaceSelection } from '../formatters/replace-selection' -import { ToolbarButton } from '../toolbar-button' import React, { useCallback } from 'react' import { ChatDots as IconChatDots } from 'react-bootstrap-icons' @@ -16,5 +16,5 @@ export const CommentButton: React.FC = () => { const formatter: ContentFormatter = useCallback(({ currentSelection }) => { return replaceSelection({ from: currentSelection.to ?? currentSelection.from }, '> []', true) }, []) - return + return } diff --git a/frontend/src/components/editor-page/editor-pane/tool-bar/buttons/header-level-button.tsx b/frontend/src/components/editor-page/editor-pane/tool-bar/buttons/header-level-button.tsx index 3e385d28b..600be58fb 100644 --- a/frontend/src/components/editor-page/editor-pane/tool-bar/buttons/header-level-button.tsx +++ b/frontend/src/components/editor-page/editor-pane/tool-bar/buttons/header-level-button.tsx @@ -4,8 +4,8 @@ * SPDX-License-Identifier: AGPL-3.0-only */ import type { ContentFormatter } from '../../../change-content-context/use-change-editor-content-callback' +import { FormatterToolbarButton } from '../formatter-toolbar-button' import { prependLinesOfSelection } from '../formatters/prepend-lines-of-selection' -import { ToolbarButton } from '../toolbar-button' import React, { useCallback } from 'react' import { TypeH1 as IconTypeH1 } from 'react-bootstrap-icons' @@ -16,5 +16,5 @@ export const HeaderLevelButton: React.FC = () => { const formatter: ContentFormatter = useCallback(({ currentSelection, markdownContent }) => { return prependLinesOfSelection(markdownContent, currentSelection, (line) => (line.startsWith('#') ? `#` : `# `)) }, []) - return + return } diff --git a/frontend/src/components/editor-page/editor-pane/tool-bar/buttons/highlight-button.tsx b/frontend/src/components/editor-page/editor-pane/tool-bar/buttons/highlight-button.tsx index f5e0271ed..dff64fa2f 100644 --- a/frontend/src/components/editor-page/editor-pane/tool-bar/buttons/highlight-button.tsx +++ b/frontend/src/components/editor-page/editor-pane/tool-bar/buttons/highlight-button.tsx @@ -4,8 +4,8 @@ * SPDX-License-Identifier: AGPL-3.0-only */ import type { ContentFormatter } from '../../../change-content-context/use-change-editor-content-callback' +import { FormatterToolbarButton } from '../formatter-toolbar-button' import { wrapSelection } from '../formatters/wrap-selection' -import { ToolbarButton } from '../toolbar-button' import React, { useCallback } from 'react' import { Eraser as IconEraser } from 'react-bootstrap-icons' @@ -16,5 +16,5 @@ export const HighlightButton: React.FC = () => { const formatter: ContentFormatter = useCallback(({ currentSelection }) => { return wrapSelection(currentSelection, '==', '==') }, []) - return + return } diff --git a/frontend/src/components/editor-page/editor-pane/tool-bar/buttons/horizontal-line-button.tsx b/frontend/src/components/editor-page/editor-pane/tool-bar/buttons/horizontal-line-button.tsx index 0bf22019f..03f9b4efe 100644 --- a/frontend/src/components/editor-page/editor-pane/tool-bar/buttons/horizontal-line-button.tsx +++ b/frontend/src/components/editor-page/editor-pane/tool-bar/buttons/horizontal-line-button.tsx @@ -4,8 +4,8 @@ * SPDX-License-Identifier: AGPL-3.0-only */ import type { ContentFormatter } from '../../../change-content-context/use-change-editor-content-callback' +import { FormatterToolbarButton } from '../formatter-toolbar-button' import { replaceSelection } from '../formatters/replace-selection' -import { ToolbarButton } from '../toolbar-button' import React, { useCallback } from 'react' import { DashLg as IconDashLg } from 'react-bootstrap-icons' @@ -16,5 +16,5 @@ export const HorizontalLineButton: React.FC = () => { const formatter: ContentFormatter = useCallback(({ currentSelection }) => { return replaceSelection({ from: currentSelection.to ?? currentSelection.from }, '----\n', true) }, []) - return + return } diff --git a/frontend/src/components/editor-page/editor-pane/tool-bar/buttons/image-link-button.tsx b/frontend/src/components/editor-page/editor-pane/tool-bar/buttons/image-link-button.tsx index 0b7231d41..67a4eacdd 100644 --- a/frontend/src/components/editor-page/editor-pane/tool-bar/buttons/image-link-button.tsx +++ b/frontend/src/components/editor-page/editor-pane/tool-bar/buttons/image-link-button.tsx @@ -4,8 +4,8 @@ * SPDX-License-Identifier: AGPL-3.0-only */ import type { ContentFormatter } from '../../../change-content-context/use-change-editor-content-callback' +import { FormatterToolbarButton } from '../formatter-toolbar-button' import { addLink } from '../formatters/add-link' -import { ToolbarButton } from '../toolbar-button' import React, { useCallback } from 'react' import { Image as IconImage } from 'react-bootstrap-icons' @@ -16,5 +16,5 @@ export const ImageLinkButton: React.FC = () => { const formatter: ContentFormatter = useCallback(({ currentSelection, markdownContent }) => { return addLink(markdownContent, currentSelection, '!') }, []) - return + return } diff --git a/frontend/src/components/editor-page/editor-pane/tool-bar/buttons/italic-button.tsx b/frontend/src/components/editor-page/editor-pane/tool-bar/buttons/italic-button.tsx index aed31c36f..11b2842b9 100644 --- a/frontend/src/components/editor-page/editor-pane/tool-bar/buttons/italic-button.tsx +++ b/frontend/src/components/editor-page/editor-pane/tool-bar/buttons/italic-button.tsx @@ -4,8 +4,8 @@ * SPDX-License-Identifier: AGPL-3.0-only */ import type { ContentFormatter } from '../../../change-content-context/use-change-editor-content-callback' +import { FormatterToolbarButton } from '../formatter-toolbar-button' import { wrapSelection } from '../formatters/wrap-selection' -import { ToolbarButton } from '../toolbar-button' import React, { useCallback } from 'react' import { TypeItalic as IconTypeItalic } from 'react-bootstrap-icons' @@ -16,5 +16,5 @@ export const ItalicButton: React.FC = () => { const formatter: ContentFormatter = useCallback(({ currentSelection }) => { return wrapSelection(currentSelection, '*', '*') }, []) - return + return } diff --git a/frontend/src/components/editor-page/editor-pane/tool-bar/buttons/link-button.tsx b/frontend/src/components/editor-page/editor-pane/tool-bar/buttons/link-button.tsx index d0e650bcd..6c546c7d3 100644 --- a/frontend/src/components/editor-page/editor-pane/tool-bar/buttons/link-button.tsx +++ b/frontend/src/components/editor-page/editor-pane/tool-bar/buttons/link-button.tsx @@ -4,8 +4,8 @@ * SPDX-License-Identifier: AGPL-3.0-only */ import type { ContentFormatter } from '../../../change-content-context/use-change-editor-content-callback' +import { FormatterToolbarButton } from '../formatter-toolbar-button' import { addLink } from '../formatters/add-link' -import { ToolbarButton } from '../toolbar-button' import React, { useCallback } from 'react' import { Link as IconLink } from 'react-bootstrap-icons' @@ -16,5 +16,5 @@ export const LinkButton: React.FC = () => { const formatter: ContentFormatter = useCallback(({ currentSelection, markdownContent }) => { return addLink(markdownContent, currentSelection) }, []) - return + return } diff --git a/frontend/src/components/editor-page/editor-pane/tool-bar/buttons/ordered-list-button.tsx b/frontend/src/components/editor-page/editor-pane/tool-bar/buttons/ordered-list-button.tsx index 600622f70..335247f35 100644 --- a/frontend/src/components/editor-page/editor-pane/tool-bar/buttons/ordered-list-button.tsx +++ b/frontend/src/components/editor-page/editor-pane/tool-bar/buttons/ordered-list-button.tsx @@ -4,8 +4,8 @@ * SPDX-License-Identifier: AGPL-3.0-only */ import type { ContentFormatter } from '../../../change-content-context/use-change-editor-content-callback' +import { FormatterToolbarButton } from '../formatter-toolbar-button' import { prependLinesOfSelection } from '../formatters/prepend-lines-of-selection' -import { ToolbarButton } from '../toolbar-button' import React, { useCallback } from 'react' import { ListOl as IconListOl } from 'react-bootstrap-icons' @@ -20,5 +20,5 @@ export const OrderedListButton: React.FC = () => { (line, lineIndexInBlock) => `${lineIndexInBlock + 1}. ` ) }, []) - return + return } diff --git a/frontend/src/components/editor-page/editor-pane/tool-bar/buttons/quotes-button.tsx b/frontend/src/components/editor-page/editor-pane/tool-bar/buttons/quotes-button.tsx index 9ae9265e6..75ee303c9 100644 --- a/frontend/src/components/editor-page/editor-pane/tool-bar/buttons/quotes-button.tsx +++ b/frontend/src/components/editor-page/editor-pane/tool-bar/buttons/quotes-button.tsx @@ -4,8 +4,8 @@ * SPDX-License-Identifier: AGPL-3.0-only */ import type { ContentFormatter } from '../../../change-content-context/use-change-editor-content-callback' +import { FormatterToolbarButton } from '../formatter-toolbar-button' import { prependLinesOfSelection } from '../formatters/prepend-lines-of-selection' -import { ToolbarButton } from '../toolbar-button' import React, { useCallback } from 'react' import { Quote as IconQuote } from 'react-bootstrap-icons' @@ -16,5 +16,5 @@ export const QuotesButton: React.FC = () => { const formatter: ContentFormatter = useCallback(({ currentSelection, markdownContent }) => { return prependLinesOfSelection(markdownContent, currentSelection, () => `> `) }, []) - return + return } diff --git a/frontend/src/components/editor-page/editor-pane/tool-bar/buttons/strikethrough-button.tsx b/frontend/src/components/editor-page/editor-pane/tool-bar/buttons/strikethrough-button.tsx index 739523c98..a89e41c59 100644 --- a/frontend/src/components/editor-page/editor-pane/tool-bar/buttons/strikethrough-button.tsx +++ b/frontend/src/components/editor-page/editor-pane/tool-bar/buttons/strikethrough-button.tsx @@ -4,8 +4,8 @@ * SPDX-License-Identifier: AGPL-3.0-only */ import type { ContentFormatter } from '../../../change-content-context/use-change-editor-content-callback' +import { FormatterToolbarButton } from '../formatter-toolbar-button' import { wrapSelection } from '../formatters/wrap-selection' -import { ToolbarButton } from '../toolbar-button' import React, { useCallback } from 'react' import { TypeStrikethrough as IconTypeStrikethrough } from 'react-bootstrap-icons' @@ -16,5 +16,5 @@ export const StrikethroughButton: React.FC = () => { const formatter: ContentFormatter = useCallback(({ currentSelection }) => { return wrapSelection(currentSelection, '~~', '~~') }, []) - return + return } diff --git a/frontend/src/components/editor-page/editor-pane/tool-bar/buttons/subscript-button.tsx b/frontend/src/components/editor-page/editor-pane/tool-bar/buttons/subscript-button.tsx index ea8781508..88f0f9a93 100644 --- a/frontend/src/components/editor-page/editor-pane/tool-bar/buttons/subscript-button.tsx +++ b/frontend/src/components/editor-page/editor-pane/tool-bar/buttons/subscript-button.tsx @@ -4,8 +4,8 @@ * SPDX-License-Identifier: AGPL-3.0-only */ import type { ContentFormatter } from '../../../change-content-context/use-change-editor-content-callback' +import { FormatterToolbarButton } from '../formatter-toolbar-button' import { wrapSelection } from '../formatters/wrap-selection' -import { ToolbarButton } from '../toolbar-button' import React, { useCallback } from 'react' import { Subscript as IconSubscript } from 'react-bootstrap-icons' @@ -16,5 +16,5 @@ export const SubscriptButton: React.FC = () => { const formatter: ContentFormatter = useCallback(({ currentSelection }) => { return wrapSelection(currentSelection, '~', '~') }, []) - return + return } diff --git a/frontend/src/components/editor-page/editor-pane/tool-bar/buttons/superscript-button.tsx b/frontend/src/components/editor-page/editor-pane/tool-bar/buttons/superscript-button.tsx index 9195634e5..469518910 100644 --- a/frontend/src/components/editor-page/editor-pane/tool-bar/buttons/superscript-button.tsx +++ b/frontend/src/components/editor-page/editor-pane/tool-bar/buttons/superscript-button.tsx @@ -4,8 +4,8 @@ * SPDX-License-Identifier: AGPL-3.0-only */ import type { ContentFormatter } from '../../../change-content-context/use-change-editor-content-callback' +import { FormatterToolbarButton } from '../formatter-toolbar-button' import { wrapSelection } from '../formatters/wrap-selection' -import { ToolbarButton } from '../toolbar-button' import React, { useCallback } from 'react' import { Superscript as IconSuperscript } from 'react-bootstrap-icons' @@ -16,5 +16,5 @@ export const SuperscriptButton: React.FC = () => { const formatter: ContentFormatter = useCallback(({ currentSelection }) => { return wrapSelection(currentSelection, '^', '^') }, []) - return + return } diff --git a/frontend/src/components/editor-page/editor-pane/tool-bar/buttons/underline-button.tsx b/frontend/src/components/editor-page/editor-pane/tool-bar/buttons/underline-button.tsx index 468aa4250..3b9d1c8e3 100644 --- a/frontend/src/components/editor-page/editor-pane/tool-bar/buttons/underline-button.tsx +++ b/frontend/src/components/editor-page/editor-pane/tool-bar/buttons/underline-button.tsx @@ -4,8 +4,8 @@ * SPDX-License-Identifier: AGPL-3.0-only */ import type { ContentFormatter } from '../../../change-content-context/use-change-editor-content-callback' +import { FormatterToolbarButton } from '../formatter-toolbar-button' import { wrapSelection } from '../formatters/wrap-selection' -import { ToolbarButton } from '../toolbar-button' import React, { useCallback } from 'react' import { TypeUnderline as IconTypeUnderline } from 'react-bootstrap-icons' @@ -16,5 +16,5 @@ export const UnderlineButton: React.FC = () => { const formatter: ContentFormatter = useCallback(({ currentSelection }) => { return wrapSelection(currentSelection, '++', '++') }, []) - return + return } diff --git a/frontend/src/components/editor-page/editor-pane/tool-bar/buttons/unordered-list-button.tsx b/frontend/src/components/editor-page/editor-pane/tool-bar/buttons/unordered-list-button.tsx index 00e0588ff..e4a6d5ec1 100644 --- a/frontend/src/components/editor-page/editor-pane/tool-bar/buttons/unordered-list-button.tsx +++ b/frontend/src/components/editor-page/editor-pane/tool-bar/buttons/unordered-list-button.tsx @@ -4,8 +4,8 @@ * SPDX-License-Identifier: AGPL-3.0-only */ import type { ContentFormatter } from '../../../change-content-context/use-change-editor-content-callback' +import { FormatterToolbarButton } from '../formatter-toolbar-button' import { prependLinesOfSelection } from '../formatters/prepend-lines-of-selection' -import { ToolbarButton } from '../toolbar-button' import React, { useCallback } from 'react' import { List as IconList } from 'react-bootstrap-icons' @@ -16,5 +16,5 @@ export const UnorderedListButton: React.FC = () => { const formatter: ContentFormatter = useCallback(({ currentSelection, markdownContent }) => { return prependLinesOfSelection(markdownContent, currentSelection, () => `- `) }, []) - return + return } diff --git a/frontend/src/components/editor-page/editor-pane/tool-bar/emoji-picker/emoji-picker-button.tsx b/frontend/src/components/editor-page/editor-pane/tool-bar/emoji-picker/emoji-picker-button.tsx index fc2bc753a..a497490d6 100644 --- a/frontend/src/components/editor-page/editor-pane/tool-bar/emoji-picker/emoji-picker-button.tsx +++ b/frontend/src/components/editor-page/editor-pane/tool-bar/emoji-picker/emoji-picker-button.tsx @@ -3,29 +3,26 @@ * * SPDX-License-Identifier: AGPL-3.0-only */ -import { cypressId } from '../../../../../utils/cypress-attribute' -import { UiIcon } from '../../../../common/icons/ui-icon' import { useChangeEditorContentCallback } from '../../../change-content-context/use-change-editor-content-callback' import { replaceSelection } from '../formatters/replace-selection' +import { ToolbarButton } from '../toolbar-button' import { EmojiPickerPopover } from './emoji-picker-popover' import styles from './emoji-picker.module.scss' import { extractEmojiShortCode } from './extract-emoji-short-code' import type { EmojiClickEventDetail } from 'emoji-picker-element/shared' import React, { Fragment, useCallback, useRef, useState } from 'react' -import { Button, Overlay } from 'react-bootstrap' +import { Overlay } from 'react-bootstrap' import { EmojiSmile as IconEmojiSmile } from 'react-bootstrap-icons' import type { OverlayInjectedProps } from 'react-bootstrap/Overlay' -import { useTranslation } from 'react-i18next' /** * Renders a button to open the emoji picker. * @see EmojiPickerPopover */ export const EmojiPickerButton: React.FC = () => { - const { t } = useTranslation() const [showEmojiPicker, setShowEmojiPicker] = useState(false) const changeEditorContent = useChangeEditorContentCallback() - const buttonRef = useRef(null) + const buttonRef = useRef(null) const onEmojiSelected = useCallback( (emojiClickEvent: EmojiClickEventDetail) => { @@ -57,15 +54,7 @@ export const EmojiPickerButton: React.FC = () => { offset={[0, 0]}> {createPopoverElement} - + ) } diff --git a/frontend/src/components/editor-page/editor-pane/tool-bar/formatter-toolbar-button.tsx b/frontend/src/components/editor-page/editor-pane/tool-bar/formatter-toolbar-button.tsx new file mode 100644 index 000000000..e8470c6ae --- /dev/null +++ b/frontend/src/components/editor-page/editor-pane/tool-bar/formatter-toolbar-button.tsx @@ -0,0 +1,31 @@ +/* + * SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file) + * + * SPDX-License-Identifier: AGPL-3.0-only + */ +import type { ContentFormatter } from '../../change-content-context/use-change-editor-content-callback' +import { useChangeEditorContentCallback } from '../../change-content-context/use-change-editor-content-callback' +import type { ToolbarButtonProps } from './toolbar-button' +import { ToolbarButton } from './toolbar-button' +import React, { useCallback } from 'react' + +export interface FormatterToolbarButtonProps extends Omit { + formatter: ContentFormatter +} + +/** + * Renders a button for the editor toolbar that formats the content using a given formatter function. + * + * @param i18nKey Used to generate a title for the button by interpreting it as translation key in the i18n-namespace `editor.editorToolbar`- + * @param iconName A fork awesome icon name that is shown in the button + * @param formatter The formatter function changes the editor content on click + */ +export const FormatterToolbarButton: React.FC = ({ i18nKey, icon, formatter }) => { + const changeEditorContent = useChangeEditorContentCallback() + + const onClick = useCallback(() => { + changeEditorContent?.(formatter) + }, [formatter, changeEditorContent]) + + return +} diff --git a/frontend/src/components/editor-page/editor-pane/tool-bar/table-picker/table-picker-button.tsx b/frontend/src/components/editor-page/editor-pane/tool-bar/table-picker/table-picker-button.tsx index f4a243fc9..25a5a701c 100644 --- a/frontend/src/components/editor-page/editor-pane/tool-bar/table-picker/table-picker-button.tsx +++ b/frontend/src/components/editor-page/editor-pane/tool-bar/table-picker/table-picker-button.tsx @@ -3,19 +3,17 @@ * * SPDX-License-Identifier: AGPL-3.0-only */ -import { cypressId } from '../../../../../utils/cypress-attribute' -import { UiIcon } from '../../../../common/icons/ui-icon' import { useChangeEditorContentCallback } from '../../../change-content-context/use-change-editor-content-callback' import { replaceSelection } from '../formatters/replace-selection' +import { ToolbarButton } from '../toolbar-button' import { createMarkdownTable } from './create-markdown-table' import { CustomTableSizeModal } from './custom-table-size-modal' import './table-picker.module.scss' import { TableSizePickerPopover } from './table-size-picker-popover' -import React, { Fragment, useCallback, useMemo, useRef, useState } from 'react' -import { Button, Overlay } from 'react-bootstrap' +import React, { Fragment, useCallback, useRef, useState } from 'react' +import { Overlay } from 'react-bootstrap' import { Table as IconTable } from 'react-bootstrap-icons' import type { OverlayInjectedProps } from 'react-bootstrap/Overlay' -import { useTranslation } from 'react-i18next' enum PickerMode { INVISIBLE, @@ -27,7 +25,6 @@ enum PickerMode { * Toggles the visibility of a table size picker overlay and inserts the result into the editor. */ export const TablePickerButton: React.FC = () => { - const { t } = useTranslation() const [pickerMode, setPickerMode] = useState(PickerMode.INVISIBLE) const onDismiss = useCallback(() => setPickerMode(PickerMode.INVISIBLE), []) const onShowModal = useCallback(() => setPickerMode(PickerMode.CUSTOM), []) @@ -42,7 +39,6 @@ export const TablePickerButton: React.FC = () => { [changeEditorContent] ) - const tableTitle = useMemo(() => t('editor.editorToolbar.table.titleWithoutSize'), [t]) const button = useRef(null) const toggleOverlayVisibility = useCallback(() => { setPickerMode((oldPickerMode) => (oldPickerMode === PickerMode.INVISIBLE ? PickerMode.GRID : PickerMode.INVISIBLE)) @@ -71,15 +67,12 @@ export const TablePickerButton: React.FC = () => { return ( - + buttonRef={button} + /> + diff --git a/frontend/src/components/editor-page/editor-pane/tool-bar/toolbar-button.tsx b/frontend/src/components/editor-page/editor-pane/tool-bar/toolbar-button.tsx index bf00e4b5b..4f908c51f 100644 --- a/frontend/src/components/editor-page/editor-pane/tool-bar/toolbar-button.tsx +++ b/frontend/src/components/editor-page/editor-pane/tool-bar/toolbar-button.tsx @@ -5,9 +5,8 @@ */ import { cypressId } from '../../../../utils/cypress-attribute' import { UiIcon } from '../../../common/icons/ui-icon' -import type { ContentFormatter } from '../../change-content-context/use-change-editor-content-callback' -import { useChangeEditorContentCallback } from '../../change-content-context/use-change-editor-content-callback' -import React, { useCallback, useMemo } from 'react' +import type { PropsWithChildren, RefObject } from 'react' +import React, { useMemo } from 'react' import { Button } from 'react-bootstrap' import type { Icon } from 'react-bootstrap-icons' import { useTranslation } from 'react-i18next' @@ -15,33 +14,41 @@ import { useTranslation } from 'react-i18next' export interface ToolbarButtonProps { i18nKey: string icon: Icon - formatter: ContentFormatter + onClick: () => void + disabled?: boolean + buttonRef?: RefObject } /** - * Renders a button for the editor toolbar that formats the content using a given formatter function. + * Renders a button for the editor toolbar. * * @param i18nKey Used to generate a title for the button by interpreting it as translation key in the i18n-namespace `editor.editorToolbar`- - * @param iconName A fork awesome icon name that is shown in the button - * @param formatter The formatter function changes the editor content on click + * @param iconName An icon that is shown in the button + * @param onClick A callback that is executed on click + * @param disabled Defines if the button is disabled + * @param buttonRef A reference to the button element */ -export const ToolbarButton: React.FC = ({ i18nKey, icon, formatter }) => { +export const ToolbarButton: React.FC> = ({ + i18nKey, + icon, + onClick, + disabled = false, + buttonRef, + children +}) => { const { t } = useTranslation('', { keyPrefix: 'editor.editorToolbar' }) - const changeEditorContent = useChangeEditorContentCallback() - - const onClick = useCallback(() => { - changeEditorContent?.(formatter) - }, [formatter, changeEditorContent]) const title = useMemo(() => t(i18nKey), [i18nKey, t]) return ( ) } diff --git a/frontend/src/components/editor-page/editor-pane/tool-bar/upload-image-button/upload-image-button.tsx b/frontend/src/components/editor-page/editor-pane/tool-bar/upload-image-button/upload-image-button.tsx index 056d577e9..afe1a4936 100644 --- a/frontend/src/components/editor-page/editor-pane/tool-bar/upload-image-button/upload-image-button.tsx +++ b/frontend/src/components/editor-page/editor-pane/tool-bar/upload-image-button/upload-image-button.tsx @@ -5,18 +5,16 @@ */ import { cypressId } from '../../../../../utils/cypress-attribute' import { Logger } from '../../../../../utils/logger' -import { UiIcon } from '../../../../common/icons/ui-icon' import { ShowIf } from '../../../../common/show-if/show-if' import { acceptedMimeTypes } from '../../../../common/upload-image-mimetypes' import { UploadInput } from '../../../../common/upload-input' import { useCodemirrorReferenceContext } from '../../../change-content-context/codemirror-reference-context' import { useHandleUpload } from '../../hooks/use-handle-upload' +import { ToolbarButton } from '../toolbar-button' import { extractSelectedText } from './extract-selected-text' import { Optional } from '@mrdrogdrog/optional' import React, { Fragment, useCallback, useRef } from 'react' -import { Button } from 'react-bootstrap' import { Upload as IconUpload } from 'react-bootstrap-icons' -import { useTranslation } from 'react-i18next' const logger = new Logger('Upload image button') @@ -24,7 +22,6 @@ const logger = new Logger('Upload image button') * Shows a button that uploads a chosen file to the backend and adds the link to the note. */ export const UploadImageButton: React.FC = () => { - const { t } = useTranslation() const clickRef = useRef<() => void>() const buttonClick = useCallback(() => { clickRef.current?.() @@ -49,22 +46,16 @@ export const UploadImageButton: React.FC = () => { return ( - - - - + + + + + ) }