diff --git a/packages/core/dev-test/config.yml b/packages/core/dev-test/config.yml index ff020199..c5ba97d5 100644 --- a/packages/core/dev-test/config.yml +++ b/packages/core/dev-test/config.yml @@ -525,6 +525,42 @@ collections: widget: markdown media_library: folder_support: true + - name: customized_buttons + label: Customized Buttons + widget: markdown + toolbar_buttons: + main: + - bold + - italic + - font + - shortcode + - label: Insert + groups: + - items: ['image', 'file-link'] + - items: ['insert-table'] + empty: + - bold + - italic + - font + - label: Insert + groups: + - items: ['image', 'file-link'] + - items: ['blockquote', 'code-block'] + selection: + - bold + - italic + - font + - file-link + table_empty: + - insert-row + - insert-column + - delete-row + - delete-column + - delete-table + table_selection: + - bold + - italic + - font - name: number label: Number file: _widgets/number.json diff --git a/packages/core/src/__mocks__/@udecode/plate-list.ts b/packages/core/src/__mocks__/@udecode/plate-list.ts new file mode 100644 index 00000000..f3936b41 --- /dev/null +++ b/packages/core/src/__mocks__/@udecode/plate-list.ts @@ -0,0 +1,2 @@ +export const getListItemEntry = jest.fn(); +export const toggleList = jest.fn(); diff --git a/packages/core/src/constants/toolbar_buttons.ts b/packages/core/src/constants/toolbar_buttons.ts new file mode 100644 index 00000000..db2d6829 --- /dev/null +++ b/packages/core/src/constants/toolbar_buttons.ts @@ -0,0 +1,20 @@ +export const FONT_TOOLBAR_BUTTON = 'font'; +export const SHORTCODE_TOOLBAR_BUTTON = 'shortcode'; +export const BLOCKQUOTE_TOOLBAR_BUTTON = 'blockquote'; +export const BOLD_TOOLBAR_BUTTON = 'bold'; +export const CODE_BLOCK_TOOLBAR_BUTTON = 'code-block'; +export const CODE_TOOLBAR_BUTTON = 'code'; +export const DECREASE_IDENT_TOOLBAR_BUTTON = 'decrease-indent'; +export const DELETE_COLUMN_TOOLBAR_BUTTON = 'delete-column'; +export const DELETE_ROW_TOOLBAR_BUTTON = 'delete-row'; +export const DELETE_TABLE_TOOLBAR_BUTTON = 'delete-table'; +export const INCRASE_IDENT_TOOLBAR_BUTTON = 'increase-indent'; +export const INSERT_COLUMN_TOOLBAR_BUTTON = 'insert-column'; +export const IMAGE_TOOLBAR_BUTTON = 'image'; +export const FILE_LINK_TOOLBAR_BUTTON = 'file-link'; +export const INSERT_ROW_TOOLBAR_BUTTON = 'insert-row'; +export const INSERT_TABLE_TOOLBAR_BUTTON = 'insert-table'; +export const ITALIC_TOOLBAR_BUTTON = 'italic'; +export const ORDERED_LIST_TOOLBAR_BUTTON = 'ordered-list'; +export const STRIKETHROUGH_TOOLBAR_BUTTON = 'strikethrough'; +export const UNORDERED_LIST_TOOLBAR_BUTTON = 'unordered-list'; diff --git a/packages/core/src/interface.ts b/packages/core/src/interface.ts index 4972876c..43a32636 100644 --- a/packages/core/src/interface.ts +++ b/packages/core/src/interface.ts @@ -15,6 +15,28 @@ import type { SORT_DIRECTION_DESCENDING, SORT_DIRECTION_NONE, } from './constants'; +import type { + BLOCKQUOTE_TOOLBAR_BUTTON, + BOLD_TOOLBAR_BUTTON, + CODE_BLOCK_TOOLBAR_BUTTON, + CODE_TOOLBAR_BUTTON, + DECREASE_IDENT_TOOLBAR_BUTTON, + DELETE_COLUMN_TOOLBAR_BUTTON, + DELETE_ROW_TOOLBAR_BUTTON, + DELETE_TABLE_TOOLBAR_BUTTON, + FILE_LINK_TOOLBAR_BUTTON, + FONT_TOOLBAR_BUTTON, + IMAGE_TOOLBAR_BUTTON, + INCRASE_IDENT_TOOLBAR_BUTTON, + INSERT_COLUMN_TOOLBAR_BUTTON, + INSERT_ROW_TOOLBAR_BUTTON, + INSERT_TABLE_TOOLBAR_BUTTON, + ITALIC_TOOLBAR_BUTTON, + ORDERED_LIST_TOOLBAR_BUTTON, + SHORTCODE_TOOLBAR_BUTTON, + STRIKETHROUGH_TOOLBAR_BUTTON, + UNORDERED_LIST_TOOLBAR_BUTTON, +} from './constants/toolbar_buttons'; import type { formatExtensions } from './formats/formats'; import type { I18N_FIELD_DUPLICATE, @@ -642,8 +664,52 @@ export interface MapField extends BaseField { height?: string; } +export type MarkdownToolbarButtonType = + | LowLevelMarkdownToolbarButtonType + | typeof FONT_TOOLBAR_BUTTON + | typeof SHORTCODE_TOOLBAR_BUTTON; + +export type LowLevelMarkdownToolbarButtonType = + | typeof BLOCKQUOTE_TOOLBAR_BUTTON + | typeof BOLD_TOOLBAR_BUTTON + | typeof CODE_BLOCK_TOOLBAR_BUTTON + | typeof CODE_TOOLBAR_BUTTON + | typeof DECREASE_IDENT_TOOLBAR_BUTTON + | typeof DELETE_COLUMN_TOOLBAR_BUTTON + | typeof DELETE_ROW_TOOLBAR_BUTTON + | typeof DELETE_TABLE_TOOLBAR_BUTTON + | typeof INCRASE_IDENT_TOOLBAR_BUTTON + | typeof INSERT_COLUMN_TOOLBAR_BUTTON + | typeof IMAGE_TOOLBAR_BUTTON + | typeof FILE_LINK_TOOLBAR_BUTTON + | typeof INSERT_ROW_TOOLBAR_BUTTON + | typeof INSERT_TABLE_TOOLBAR_BUTTON + | typeof ITALIC_TOOLBAR_BUTTON + | typeof ORDERED_LIST_TOOLBAR_BUTTON + | typeof STRIKETHROUGH_TOOLBAR_BUTTON + | typeof UNORDERED_LIST_TOOLBAR_BUTTON; + +export type MarkdownToolbarItem = + | MarkdownToolbarButtonType + | { + label: string; + icon?: string; + groups: { + items: LowLevelMarkdownToolbarButtonType[]; + }[]; + }; + +export interface MarkdownFieldToolbarButtons { + main?: MarkdownToolbarItem[]; + empty?: MarkdownToolbarItem[]; + selection?: MarkdownToolbarItem[]; + table_empty?: MarkdownToolbarItem[]; + table_selection?: MarkdownToolbarItem[]; +} + export interface MarkdownField extends MediaField { widget: 'markdown'; + toolbar_buttons?: MarkdownFieldToolbarButtons; default?: string; } diff --git a/packages/core/src/widgets/markdown/plate/components/balloon-toolbar/BalloonToolbar.tsx b/packages/core/src/widgets/markdown/plate/components/balloon-toolbar/BalloonToolbar.tsx index 97a7f825..755736a7 100644 --- a/packages/core/src/widgets/markdown/plate/components/balloon-toolbar/BalloonToolbar.tsx +++ b/packages/core/src/widgets/markdown/plate/components/balloon-toolbar/BalloonToolbar.tsx @@ -17,17 +17,73 @@ import { useFocused } from 'slate-react'; import useDebounce from '@staticcms/core/lib/hooks/useDebounce'; import { isEmpty } from '@staticcms/core/lib/util/string.util'; +import { selectVisible } from '@staticcms/core/reducers/selectors/mediaLibrary'; +import { useAppSelector } from '@staticcms/core/store/hooks'; import { useMdPlateEditorState } from '@staticcms/markdown/plate/plateTypes'; -import BasicElementToolbarButtons from '../buttons/BasicElementToolbarButtons'; -import BasicMarkToolbarButtons from '../buttons/BasicMarkToolbarButtons'; -import MediaToolbarButtons from '../buttons/MediaToolbarButtons'; -import ShortcodeToolbarButton from '../buttons/ShortcodeToolbarButton'; -import TableToolbarButtons from '../buttons/TableToolbarButtons'; +import { getToolbarButtons } from '../../hooks/useToolbarButtons'; +import { + BOLD_TOOLBAR_BUTTON, + CODE_TOOLBAR_BUTTON, + DELETE_COLUMN_TOOLBAR_BUTTON, + DELETE_ROW_TOOLBAR_BUTTON, + DELETE_TABLE_TOOLBAR_BUTTON, + FILE_LINK_TOOLBAR_BUTTON, + FONT_TOOLBAR_BUTTON, + IMAGE_TOOLBAR_BUTTON, + INSERT_COLUMN_TOOLBAR_BUTTON, + INSERT_ROW_TOOLBAR_BUTTON, + ITALIC_TOOLBAR_BUTTON, + SHORTCODE_TOOLBAR_BUTTON, + STRIKETHROUGH_TOOLBAR_BUTTON, +} from '@staticcms/core/constants/toolbar_buttons'; -import type { Collection, MarkdownField } from '@staticcms/core/interface'; +import type { + Collection, + MarkdownField, + MarkdownToolbarButtonType, +} from '@staticcms/core/interface'; import type { ClientRectObject } from '@udecode/plate'; import type { FC, ReactNode } from 'react'; +const DEFAULT_EMPTY_BUTTONS: MarkdownToolbarButtonType[] = []; + +const DEFAULT_SELECTION_BUTTONS: MarkdownToolbarButtonType[] = [ + BOLD_TOOLBAR_BUTTON, + ITALIC_TOOLBAR_BUTTON, + STRIKETHROUGH_TOOLBAR_BUTTON, + CODE_TOOLBAR_BUTTON, + FONT_TOOLBAR_BUTTON, + FILE_LINK_TOOLBAR_BUTTON, +]; + +const DEFAULT_TABLE_EMPTY_BUTTONS: MarkdownToolbarButtonType[] = [ + BOLD_TOOLBAR_BUTTON, + ITALIC_TOOLBAR_BUTTON, + STRIKETHROUGH_TOOLBAR_BUTTON, + CODE_TOOLBAR_BUTTON, + INSERT_ROW_TOOLBAR_BUTTON, + DELETE_ROW_TOOLBAR_BUTTON, + INSERT_COLUMN_TOOLBAR_BUTTON, + DELETE_COLUMN_TOOLBAR_BUTTON, + DELETE_TABLE_TOOLBAR_BUTTON, + FILE_LINK_TOOLBAR_BUTTON, + IMAGE_TOOLBAR_BUTTON, + SHORTCODE_TOOLBAR_BUTTON, +]; + +const DEFAULT_TABLE_SELECTION_BUTTONS: MarkdownToolbarButtonType[] = [ + BOLD_TOOLBAR_BUTTON, + ITALIC_TOOLBAR_BUTTON, + STRIKETHROUGH_TOOLBAR_BUTTON, + CODE_TOOLBAR_BUTTON, + INSERT_ROW_TOOLBAR_BUTTON, + DELETE_ROW_TOOLBAR_BUTTON, + INSERT_COLUMN_TOOLBAR_BUTTON, + DELETE_COLUMN_TOOLBAR_BUTTON, + DELETE_TABLE_TOOLBAR_BUTTON, + FILE_LINK_TOOLBAR_BUTTON, +]; + export interface BalloonToolbarProps { useMdx: boolean; containerRef: HTMLElement | null; @@ -49,6 +105,8 @@ const BalloonToolbar: FC = ({ const [hasFocus, setHasFocus] = useState(false); const debouncedHasFocus = useDebounce(hasFocus, 150); + const isMediaLibraryOpen = useAppSelector(selectVisible); + const handleFocus = useCallback(() => { setHasFocus(true); }, []); @@ -91,79 +149,57 @@ const BalloonToolbar: FC = ({ const debouncedEditorFocus = useDebounce(hasEditorFocus, 150); - const groups: ReactNode[] = useMemo(() => { + const [groups, setGroups] = useState([]); + + useEffect(() => { + if (isMediaLibraryOpen) { + return; + } + if (!debouncedEditorFocus && !hasFocus && !debouncedHasFocus) { - return []; + setGroups([]); + return; } if (selection && someNode(editor, { match: { type: ELEMENT_LINK }, at: selection?.anchor })) { - return []; + setGroups([]); + return; } // Selected text buttons if (selectionText && selectionExpanded) { - return [ - , - , - isInTableCell && ( - + setGroups( + getToolbarButtons( + isInTableCell + ? field.toolbar_buttons?.table_selection ?? DEFAULT_TABLE_SELECTION_BUTTONS + : field.toolbar_buttons?.selection ?? DEFAULT_SELECTION_BUTTONS, + collection, + field, + disabled, ), - , - ].filter(Boolean); + ); + return; } - const allButtons = [ - , - , - , - , - !useMdx ? : null, - ].filter(Boolean); - // Empty table cell if ( - isInTableCell && - editor.children.length > 1 && node && ((isElement(node) && isElementEmpty(editor, node)) || (isText(node) && isEmpty(node.text))) ) { - return allButtons; + setGroups( + getToolbarButtons( + isInTableCell + ? field.toolbar_buttons?.table_empty ?? DEFAULT_TABLE_EMPTY_BUTTONS + : field.toolbar_buttons?.empty ?? DEFAULT_EMPTY_BUTTONS, + collection, + field, + disabled, + ), + ); + return; } - return []; + setGroups([]); // eslint-disable-next-line react-hooks/exhaustive-deps }, [ debouncedEditorFocus, @@ -179,6 +215,7 @@ const BalloonToolbar: FC = ({ containerRef, collection, field, + isMediaLibraryOpen, ]); const [prevSelectionBoundingClientRect, setPrevSelectionBoundingClientRect] = useState( @@ -190,8 +227,8 @@ const BalloonToolbar: FC = ({ prevSelectionBoundingClientRect !== selectionBoundingClientRect ? 0 : 150, ); const open = useMemo( - () => groups.length > 0 || debouncedGroups.length > 0, - [debouncedGroups.length, groups.length], + () => groups.length > 0 || debouncedGroups.length > 0 || isMediaLibraryOpen, + [debouncedGroups.length, groups.length, isMediaLibraryOpen], ); const debouncedOpen = useDebounce( open, @@ -239,10 +276,11 @@ const BalloonToolbar: FC = ({ " >
{groups.length > 0 ? groups : debouncedGroups}
diff --git a/packages/core/src/widgets/markdown/plate/components/balloon-toolbar/__tests__/BalloonToolbar.spec.tsx b/packages/core/src/widgets/markdown/plate/components/balloon-toolbar/__tests__/BalloonToolbar.spec.tsx index 089f43e1..12adb1d1 100644 --- a/packages/core/src/widgets/markdown/plate/components/balloon-toolbar/__tests__/BalloonToolbar.spec.tsx +++ b/packages/core/src/widgets/markdown/plate/components/balloon-toolbar/__tests__/BalloonToolbar.spec.tsx @@ -2,14 +2,16 @@ * @jest-environment jsdom */ import '@testing-library/jest-dom'; -import { screen } from '@testing-library/react'; +import { act, screen } from '@testing-library/react'; import { ELEMENT_LINK, findNodePath, getNode, getParentNode, + getSelectionText, isElement, isElementEmpty, + isSelectionExpanded, someNode, usePlateEditorState, usePlateSelection, @@ -67,8 +69,12 @@ describe(BalloonToolbar.name, () => { const mockUseFocused = useFocused as jest.Mock; const mockFindNodePath = findNodePath as jest.Mock; const mockGetParentNode = getParentNode as jest.Mock; + const mockGetSelectionText = getSelectionText as jest.Mock; + const mockIsSelectionExpanded = isSelectionExpanded as jest.Mock; beforeEach(() => { + jest.useFakeTimers(); + store.dispatch(configLoaded(config as unknown as Config)); mockEditor = { @@ -78,107 +84,158 @@ describe(BalloonToolbar.name, () => { mockUseEditor.mockReturnValue(mockEditor); }); + afterAll(() => { + jest.useRealTimers(); + }); + it('renders empty div by default', () => { renderWithProviders(); expect(screen.queryAllByRole('button').length).toBe(0); }); - describe('empty node toolbar inside table', () => { - interface EmptyNodeToolbarSetupOptions { - useMdx?: boolean; - } + interface BalloonToolbarSetupOptions { + inTable?: boolean; + selectedText?: string; + } - const emptyNodeToolbarSetup = ({ useMdx }: EmptyNodeToolbarSetupOptions = {}) => { - mockEditor = { - selection: { - anchor: { - path: [1, 0], - offset: 0, - }, - focus: { - path: [1, 0], - offset: 0, - }, - } as TRange, - children: [ - { - type: 'p', - children: [{ text: '' }], - }, - { - type: 'td', - children: [{ text: '' }], - }, - ], - } as unknown as MdEditor; - - mockUseEditor.mockReturnValue(mockEditor); - mockUsePlateSelection.mockReturnValue(mockEditor.selection); - - mockGetNode.mockReturnValue({ text: '' }); - mockIsElement.mockReturnValue(true); - mockIsElementEmpty.mockReturnValue(true); - mockSomeNode.mockImplementation((_editor, { match: { type } }) => type !== ELEMENT_LINK); - mockUseFocused.mockReturnValue(true); - - mockFindNodePath.mockReturnValue([1, 0]); - mockGetParentNode.mockReturnValue([ + const emptyNodeToolbarSetup = ({ inTable, selectedText }: BalloonToolbarSetupOptions = {}) => { + mockEditor = { + selection: { + anchor: { + path: [1, 0], + offset: 0, + }, + focus: { + path: [1, 0], + offset: 0, + }, + } as TRange, + children: [ + { + type: 'p', + children: [{ text: !inTable && selectedText ? selectedText : '' }], + }, { type: 'td', - children: [{ text: '' }], + children: [{ text: inTable && selectedText ? selectedText : '' }], }, - ]); + ], + } as unknown as MdEditor; - const result = renderWithProviders(); + mockUseEditor.mockReturnValue(mockEditor); + mockUsePlateSelection.mockReturnValue(mockEditor.selection); - result.rerender(); + mockGetNode.mockReturnValue({ text: '' }); + mockIsElement.mockReturnValue(true); + mockIsElementEmpty.mockReturnValue(true); + mockUseFocused.mockReturnValue(true); - return result; - }; + if (selectedText) { + mockIsSelectionExpanded.mockReturnValue(true); + mockGetSelectionText.mockReturnValue(selectedText); + } else { + mockIsSelectionExpanded.mockReturnValue(false); + mockGetSelectionText.mockReturnValue(''); + } - it('renders empty node toolbar for markdown', () => { - emptyNodeToolbarSetup(); + if (inTable) { + mockSomeNode.mockImplementation((_editor, { match: { type } }) => type !== ELEMENT_LINK); + mockFindNodePath.mockReturnValue([1, 0]); + } else { + mockSomeNode.mockReturnValue(false); + mockFindNodePath.mockReturnValue([0, 0]); + } - expect(screen.queryByTestId('toolbar-button-bold')).toBeInTheDocument(); - expect(screen.queryByTestId('toolbar-button-italic')).toBeInTheDocument(); - expect(screen.queryByTestId('toolbar-button-code')).toBeInTheDocument(); - expect(screen.queryByTestId('toolbar-button-strikethrough')).toBeInTheDocument(); + mockGetParentNode.mockReturnValue([ + { + type: 'td', + children: [{ text: '' }], + }, + ]); - expect(screen.queryByTestId('font-type-select')).not.toBeInTheDocument(); + const result = renderWithProviders(); - expect(screen.queryByTestId('toolbar-button-insert-row')).toBeInTheDocument(); - expect(screen.queryByTestId('toolbar-button-delete-row')).toBeInTheDocument(); - expect(screen.queryByTestId('toolbar-button-insert-column')).toBeInTheDocument(); - expect(screen.queryByTestId('toolbar-button-delete-column')).toBeInTheDocument(); - expect(screen.queryByTestId('toolbar-button-delete-table')).toBeInTheDocument(); - - expect(screen.queryByTestId('toolbar-button-insert-link')).toBeInTheDocument(); - expect(screen.queryByTestId('toolbar-button-insert-image')).toBeInTheDocument(); - - // MDX Only do not show for markdown version - expect(screen.queryByTestId('toolbar-button-underline')).not.toBeInTheDocument(); + act(() => { + jest.advanceTimersByTime(1000); }); - it('renders empty node toolbar for mdx', () => { - emptyNodeToolbarSetup({ useMdx: true }); + result.rerender(); - expect(screen.queryByTestId('toolbar-button-bold')).toBeInTheDocument(); - expect(screen.queryByTestId('toolbar-button-italic')).toBeInTheDocument(); - expect(screen.queryByTestId('toolbar-button-code')).toBeInTheDocument(); - expect(screen.queryByTestId('toolbar-button-strikethrough')).toBeInTheDocument(); + return result; + }; - expect(screen.queryByTestId('font-type-select')).not.toBeInTheDocument(); + it('does not render empty node toolbar', () => { + emptyNodeToolbarSetup(); - expect(screen.queryByTestId('toolbar-button-insert-row')).toBeInTheDocument(); - expect(screen.queryByTestId('toolbar-button-delete-row')).toBeInTheDocument(); - expect(screen.queryByTestId('toolbar-button-insert-column')).toBeInTheDocument(); - expect(screen.queryByTestId('toolbar-button-delete-column')).toBeInTheDocument(); - expect(screen.queryByTestId('toolbar-button-delete-table')).toBeInTheDocument(); + expect(screen.queryByTestId('balloon-toolbar')).not.toBeInTheDocument(); + }); - expect(screen.queryByTestId('toolbar-button-insert-link')).toBeInTheDocument(); - expect(screen.queryByTestId('toolbar-button-insert-image')).toBeInTheDocument(); + it('renders selected node toolbar when text is selected', () => { + emptyNodeToolbarSetup({ selectedText: 'Test Text' }); - expect(screen.queryByTestId('toolbar-button-underline')).toBeInTheDocument(); - }); + expect(screen.queryByTestId('balloon-toolbar')).toBeInTheDocument(); + + expect(screen.queryByTestId('toolbar-button-bold')).toBeInTheDocument(); + expect(screen.queryByTestId('toolbar-button-italic')).toBeInTheDocument(); + expect(screen.queryByTestId('toolbar-button-code')).toBeInTheDocument(); + expect(screen.queryByTestId('toolbar-button-strikethrough')).toBeInTheDocument(); + + expect(screen.queryByTestId('font-type-select')).toBeInTheDocument(); + + expect(screen.queryByTestId('toolbar-button-insert-row')).not.toBeInTheDocument(); + expect(screen.queryByTestId('toolbar-button-delete-row')).not.toBeInTheDocument(); + expect(screen.queryByTestId('toolbar-button-insert-column')).not.toBeInTheDocument(); + expect(screen.queryByTestId('toolbar-button-delete-column')).not.toBeInTheDocument(); + expect(screen.queryByTestId('toolbar-button-delete-table')).not.toBeInTheDocument(); + + expect(screen.queryByTestId('toolbar-button-link')).toBeInTheDocument(); + expect(screen.queryByTestId('toolbar-button-image')).not.toBeInTheDocument(); + expect(screen.queryByTestId('toolbar-button-shortcode')).not.toBeInTheDocument(); + }); + + it('renders empty table node toolbar when in table', () => { + emptyNodeToolbarSetup({ inTable: true }); + + expect(screen.queryByTestId('balloon-toolbar')).toBeInTheDocument(); + + expect(screen.queryByTestId('toolbar-button-bold')).toBeInTheDocument(); + expect(screen.queryByTestId('toolbar-button-italic')).toBeInTheDocument(); + expect(screen.queryByTestId('toolbar-button-code')).toBeInTheDocument(); + expect(screen.queryByTestId('toolbar-button-strikethrough')).toBeInTheDocument(); + + expect(screen.queryByTestId('font-type-select')).not.toBeInTheDocument(); + + expect(screen.queryByTestId('toolbar-button-insert-row')).toBeInTheDocument(); + expect(screen.queryByTestId('toolbar-button-delete-row')).toBeInTheDocument(); + expect(screen.queryByTestId('toolbar-button-insert-column')).toBeInTheDocument(); + expect(screen.queryByTestId('toolbar-button-delete-column')).toBeInTheDocument(); + expect(screen.queryByTestId('toolbar-button-delete-table')).toBeInTheDocument(); + + expect(screen.queryByTestId('toolbar-button-link')).toBeInTheDocument(); + expect(screen.queryByTestId('toolbar-button-image')).toBeInTheDocument(); + expect(screen.queryByTestId('toolbar-button-shortcode')).toBeInTheDocument(); + }); + + it('renders selected table node toolbar when text is selected in table', () => { + emptyNodeToolbarSetup({ inTable: true, selectedText: 'Test Text' }); + + expect(screen.queryByTestId('balloon-toolbar')).toBeInTheDocument(); + + expect(screen.queryByTestId('toolbar-button-bold')).toBeInTheDocument(); + expect(screen.queryByTestId('toolbar-button-italic')).toBeInTheDocument(); + expect(screen.queryByTestId('toolbar-button-code')).toBeInTheDocument(); + expect(screen.queryByTestId('toolbar-button-strikethrough')).toBeInTheDocument(); + + expect(screen.queryByTestId('font-type-select')).not.toBeInTheDocument(); + + expect(screen.queryByTestId('toolbar-button-insert-row')).toBeInTheDocument(); + expect(screen.queryByTestId('toolbar-button-delete-row')).toBeInTheDocument(); + expect(screen.queryByTestId('toolbar-button-insert-column')).toBeInTheDocument(); + expect(screen.queryByTestId('toolbar-button-delete-column')).toBeInTheDocument(); + expect(screen.queryByTestId('toolbar-button-delete-table')).toBeInTheDocument(); + + expect(screen.queryByTestId('toolbar-button-link')).toBeInTheDocument(); + expect(screen.queryByTestId('toolbar-button-image')).not.toBeInTheDocument(); + expect(screen.queryByTestId('toolbar-button-shortcode')).not.toBeInTheDocument(); }); }); diff --git a/packages/core/src/widgets/markdown/plate/components/buttons/AddButtons.tsx b/packages/core/src/widgets/markdown/plate/components/buttons/AddButtons.tsx deleted file mode 100644 index 0f719df0..00000000 --- a/packages/core/src/widgets/markdown/plate/components/buttons/AddButtons.tsx +++ /dev/null @@ -1,112 +0,0 @@ -import { TableAdd } from '@styled-icons/fluentui-system-regular/TableAdd'; -import { Add as AddIcon } from '@styled-icons/material/Add'; -import { Code as CodeIcon } from '@styled-icons/material/Code'; -import { FormatQuote as FormatQuoteIcon } from '@styled-icons/material/FormatQuote'; -import { - ELEMENT_BLOCKQUOTE, - ELEMENT_CODE_BLOCK, - ELEMENT_IMAGE, - ELEMENT_LINK, - ELEMENT_TABLE, - insertEmptyCodeBlock, - insertTable, - toggleNodeType, -} from '@udecode/plate'; -import React, { useCallback } from 'react'; - -import Menu from '@staticcms/core/components/common/menu/Menu'; -import MenuGroup from '@staticcms/core/components/common/menu/MenuGroup'; -import MenuItemButton from '@staticcms/core/components/common/menu/MenuItemButton'; -import { useMdPlateEditorState } from '../../plateTypes'; -import ImageToolbarButton from './common/ImageToolbarButton'; -import LinkToolbarButton from './common/LinkToolbarButton'; - -import type { Collection, MarkdownField } from '@staticcms/core/interface'; -import type { FC } from 'react'; - -interface AddButtonsProps { - collection: Collection; - field: MarkdownField; - disabled: boolean; -} - -const AddButtons: FC = ({ collection, field, disabled }) => { - const editor = useMdPlateEditorState(); - - const handleBlockOnClick = useCallback( - (type: string, inactiveType?: string) => () => { - toggleNodeType(editor, { activeType: type, inactiveType }); - }, - [editor], - ); - - const handleCodeBlockOnClick = useCallback(() => { - insertEmptyCodeBlock(editor, { - insertNodesOptions: { select: true }, - }); - }, [editor]); - - const handleTableAdd = useCallback(() => { - insertTable(editor, { - rowCount: 2, - colCount: 2, - }); - }, [editor]); - - return ( - - ); -}; - -export default AddButtons; diff --git a/packages/core/src/widgets/markdown/plate/components/buttons/AlignToolbarButtons.tsx b/packages/core/src/widgets/markdown/plate/components/buttons/AlignToolbarButtons.tsx deleted file mode 100644 index b4151764..00000000 --- a/packages/core/src/widgets/markdown/plate/components/buttons/AlignToolbarButtons.tsx +++ /dev/null @@ -1,42 +0,0 @@ -import { FormatAlignCenter as FormatAlignCenterIcon } from '@styled-icons/material/FormatAlignCenter'; -import { FormatAlignLeft as FormatAlignLeftIcon } from '@styled-icons/material/FormatAlignLeft'; -import { FormatAlignRight as FormatAlignRightIcon } from '@styled-icons/material/FormatAlignRight'; -import React from 'react'; - -import AlignToolbarButton from './common/AlignToolbarButton'; - -import type { FC } from 'react'; - -interface AlignToolbarButtonsProps { - disabled: boolean; -} - -const AlignToolbarButtons: FC = ({ disabled }) => { - return ( - <> - } - disabled={disabled} - /> - } - disabled={disabled} - /> - } - disabled={disabled} - /> - - ); -}; - -export default AlignToolbarButtons; diff --git a/packages/core/src/widgets/markdown/plate/components/buttons/BasicElementToolbarButtons.tsx b/packages/core/src/widgets/markdown/plate/components/buttons/BasicElementToolbarButtons.tsx deleted file mode 100644 index 38a663d0..00000000 --- a/packages/core/src/widgets/markdown/plate/components/buttons/BasicElementToolbarButtons.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import React from 'react'; - -import FontTypeSelect from './FontTypeSelect'; - -import type { FC } from 'react'; - -export interface BasicElementToolbarButtonsProps { - hideFontTypeSelect?: boolean; - disableFontTypeSelect?: boolean; - hideCodeBlock?: boolean; - disabled: boolean; -} - -const BasicElementToolbarButtons: FC = ({ - hideFontTypeSelect = false, - disableFontTypeSelect = false, - disabled, -}) => { - return !hideFontTypeSelect ? ( - - ) : null; -}; - -export default BasicElementToolbarButtons; diff --git a/packages/core/src/widgets/markdown/plate/components/buttons/BasicMarkToolbarButtons.tsx b/packages/core/src/widgets/markdown/plate/components/buttons/BasicMarkToolbarButtons.tsx deleted file mode 100644 index cf4471cf..00000000 --- a/packages/core/src/widgets/markdown/plate/components/buttons/BasicMarkToolbarButtons.tsx +++ /dev/null @@ -1,93 +0,0 @@ -import { Code as CodeIcon } from '@styled-icons/material/Code'; -import { FormatBold as FormatBoldIcon } from '@styled-icons/material/FormatBold'; -import { FormatItalic as FormatItalicIcon } from '@styled-icons/material/FormatItalic'; -import { FormatStrikethrough as FormatStrikethroughIcon } from '@styled-icons/material/FormatStrikethrough'; -import { FormatUnderlined as FormatUnderlinedIcon } from '@styled-icons/material/FormatUnderlined'; -import { Subscript as SubscriptIcon } from '@styled-icons/material/Subscript'; -import { Superscript as SuperscriptIcon } from '@styled-icons/material/Superscript'; -import { - MARK_BOLD, - MARK_CODE, - MARK_ITALIC, - MARK_STRIKETHROUGH, - MARK_SUBSCRIPT, - MARK_SUPERSCRIPT, - MARK_UNDERLINE, -} from '@udecode/plate'; -import React from 'react'; - -import MarkToolbarButton from './common/MarkToolbarButton'; - -import type { FC } from 'react'; - -export interface BasicMarkToolbarButtonsProps { - extended?: boolean; - useMdx: boolean; - disabled: boolean; -} - -const BasicMarkToolbarButtons: FC = ({ - extended = false, - useMdx, - disabled, -}) => { - return ( - <> - } - disabled={disabled} - /> - } - disabled={disabled} - /> - {useMdx ? ( - } - disabled={disabled} - /> - ) : null} - } - disabled={disabled} - /> - } - disabled={disabled} - /> - {useMdx && extended ? ( - <> - } - disabled={disabled} - /> - } - disabled={disabled} - /> - - ) : null} - - ); -}; - -export default BasicMarkToolbarButtons; diff --git a/packages/core/src/widgets/markdown/plate/components/buttons/BlockquoteToolbarButton.tsx b/packages/core/src/widgets/markdown/plate/components/buttons/BlockquoteToolbarButton.tsx new file mode 100644 index 00000000..9a66abe7 --- /dev/null +++ b/packages/core/src/widgets/markdown/plate/components/buttons/BlockquoteToolbarButton.tsx @@ -0,0 +1,27 @@ +import { FormatQuote as FormatQuoteIcon } from '@styled-icons/material/FormatQuote'; +import { ELEMENT_BLOCKQUOTE } from '@udecode/plate'; +import React from 'react'; + +import BlockToolbarButton from './common/BlockToolbarButton'; + +import type { FC } from 'react'; + +export interface BlockquoteToolbarButtonProps { + disabled: boolean; + variant: 'button' | 'menu'; +} + +const BlockquoteToolbarButton: FC = ({ disabled, variant }) => { + return ( + + ); +}; + +export default BlockquoteToolbarButton; diff --git a/packages/core/src/widgets/markdown/plate/components/buttons/BoldToolbarButton.tsx b/packages/core/src/widgets/markdown/plate/components/buttons/BoldToolbarButton.tsx new file mode 100644 index 00000000..b4d76bcf --- /dev/null +++ b/packages/core/src/widgets/markdown/plate/components/buttons/BoldToolbarButton.tsx @@ -0,0 +1,26 @@ +import { FormatBold as FormatBoldIcon } from '@styled-icons/material/FormatBold'; +import { MARK_BOLD } from '@udecode/plate'; +import React from 'react'; + +import MarkToolbarButton from './common/MarkToolbarButton'; + +import type { FC } from 'react'; + +export interface BoldToolbarButtonProps { + disabled: boolean; + variant: 'button' | 'menu'; +} + +const BoldToolbarButton: FC = ({ disabled, variant }) => { + return ( + + ); +}; + +export default BoldToolbarButton; diff --git a/packages/core/src/widgets/markdown/plate/components/buttons/CodeBlockToolbarButtons.tsx b/packages/core/src/widgets/markdown/plate/components/buttons/CodeBlockToolbarButtons.tsx new file mode 100644 index 00000000..00bd0a00 --- /dev/null +++ b/packages/core/src/widgets/markdown/plate/components/buttons/CodeBlockToolbarButtons.tsx @@ -0,0 +1,36 @@ +import { Code as CodeIcon } from '@styled-icons/material/Code'; +import { insertEmptyCodeBlock } from '@udecode/plate'; +import React, { useCallback } from 'react'; + +import { useMdPlateEditorState } from '../../plateTypes'; +import ToolbarButton from './common/ToolbarButton'; + +import type { FC } from 'react'; + +export interface CodeBlockToolbarButtonsProps { + disabled: boolean; + variant: 'button' | 'menu'; +} + +const CodeBlockToolbarButtons: FC = ({ disabled, variant }) => { + const editor = useMdPlateEditorState(); + + const handleCodeBlockOnClick = useCallback(() => { + insertEmptyCodeBlock(editor, { + insertNodesOptions: { select: true }, + }); + }, [editor]); + + return ( + + ); +}; + +export default CodeBlockToolbarButtons; diff --git a/packages/core/src/widgets/markdown/plate/components/buttons/CodeToolbarButton.tsx b/packages/core/src/widgets/markdown/plate/components/buttons/CodeToolbarButton.tsx new file mode 100644 index 00000000..ff92d6ec --- /dev/null +++ b/packages/core/src/widgets/markdown/plate/components/buttons/CodeToolbarButton.tsx @@ -0,0 +1,26 @@ +import { Code as CodeIcon } from '@styled-icons/material/Code'; +import { MARK_CODE } from '@udecode/plate'; +import React from 'react'; + +import MarkToolbarButton from './common/MarkToolbarButton'; + +import type { FC } from 'react'; + +export interface CodeToolbarButtonProps { + disabled: boolean; + variant: 'button' | 'menu'; +} + +const CodeToolbarButton: FC = ({ disabled, variant }) => { + return ( + + ); +}; + +export default CodeToolbarButton; diff --git a/packages/core/src/widgets/markdown/plate/components/buttons/ColorToolbarButtons.tsx b/packages/core/src/widgets/markdown/plate/components/buttons/ColorToolbarButtons.tsx deleted file mode 100644 index a3d7ea5d..00000000 --- a/packages/core/src/widgets/markdown/plate/components/buttons/ColorToolbarButtons.tsx +++ /dev/null @@ -1,35 +0,0 @@ -import { FontDownload as FontDownloadIcon } from '@styled-icons/material/FontDownload'; -import { FormatColorText as FormatColorTextIcon } from '@styled-icons/material/FormatColorText'; -import { MARK_BG_COLOR, MARK_COLOR } from '@udecode/plate'; -import React from 'react'; - -import ColorPickerToolbarDropdown from './common/ColorPickerToolbarDropdown'; - -import type { FC } from 'react'; - -interface ColorToolbarButtonsProps { - disabled: boolean; -} - -const ColorToolbarButtons: FC = ({ disabled }) => { - return ( - <> - } - tooltip="Color" - disabled={disabled} - /> - } - tooltip="Background Color" - disabled={disabled} - /> - - ); -}; - -export default ColorToolbarButtons; diff --git a/packages/core/src/widgets/markdown/plate/components/buttons/DecreaseIndentToolbarButton.tsx b/packages/core/src/widgets/markdown/plate/components/buttons/DecreaseIndentToolbarButton.tsx new file mode 100644 index 00000000..93dffa87 --- /dev/null +++ b/packages/core/src/widgets/markdown/plate/components/buttons/DecreaseIndentToolbarButton.tsx @@ -0,0 +1,36 @@ +import { FormatIndentDecrease as FormatIndentDecreaseIcon } from '@styled-icons/material/FormatIndentDecrease'; +import { outdent } from '@udecode/plate'; +import React, { useCallback } from 'react'; + +import { useMdPlateEditorState } from '../../plateTypes'; +import ToolbarButton from './common/ToolbarButton'; + +import type { FC } from 'react'; + +export interface DecreaseIndentToolbarButtonProps { + disabled: boolean; + variant: 'button' | 'menu'; +} + +const DecreaseIndentToolbarButton: FC = ({ + disabled, + variant, +}) => { + const editor = useMdPlateEditorState(); + + const handleOutdent = useCallback(() => { + outdent(editor); + }, [editor]); + + return ( + + ); +}; + +export default DecreaseIndentToolbarButton; diff --git a/packages/core/src/widgets/markdown/plate/components/buttons/DeleteColumnToolbarButton.tsx b/packages/core/src/widgets/markdown/plate/components/buttons/DeleteColumnToolbarButton.tsx new file mode 100644 index 00000000..d049acef --- /dev/null +++ b/packages/core/src/widgets/markdown/plate/components/buttons/DeleteColumnToolbarButton.tsx @@ -0,0 +1,33 @@ +import { TableDeleteColumn } from '@styled-icons/fluentui-system-regular/TableDeleteColumn'; +import { deleteColumn } from '@udecode/plate'; +import React, { useCallback } from 'react'; + +import { useMdPlateEditorState } from '../../plateTypes'; +import ToolbarButton from './common/ToolbarButton'; + +import type { FC } from 'react'; + +export interface DeleteColumnToolbarButtonProps { + disabled: boolean; + variant: 'button' | 'menu'; +} + +const DeleteColumnToolbarButton: FC = ({ disabled, variant }) => { + const editor = useMdPlateEditorState(); + + const handleDeleteColumn = useCallback(() => { + deleteColumn(editor); + }, [editor]); + + return ( + + ); +}; + +export default DeleteColumnToolbarButton; diff --git a/packages/core/src/widgets/markdown/plate/components/buttons/DeleteRowToolbarButton.tsx b/packages/core/src/widgets/markdown/plate/components/buttons/DeleteRowToolbarButton.tsx new file mode 100644 index 00000000..f2ce0a75 --- /dev/null +++ b/packages/core/src/widgets/markdown/plate/components/buttons/DeleteRowToolbarButton.tsx @@ -0,0 +1,33 @@ +import { TableDeleteRow } from '@styled-icons/fluentui-system-regular/TableDeleteRow'; +import { deleteRow } from '@udecode/plate'; +import React, { useCallback } from 'react'; + +import { useMdPlateEditorState } from '../../plateTypes'; +import ToolbarButton from './common/ToolbarButton'; + +import type { FC } from 'react'; + +export interface DeleteRowToolbarButtonProps { + disabled: boolean; + variant: 'button' | 'menu'; +} + +const DeleteRowToolbarButton: FC = ({ disabled, variant }) => { + const editor = useMdPlateEditorState(); + + const handleDeleteRow = useCallback(() => { + deleteRow(editor); + }, [editor]); + + return ( + + ); +}; + +export default DeleteRowToolbarButton; diff --git a/packages/core/src/widgets/markdown/plate/components/buttons/DeleteTableToolbarButton.tsx b/packages/core/src/widgets/markdown/plate/components/buttons/DeleteTableToolbarButton.tsx new file mode 100644 index 00000000..623abeb5 --- /dev/null +++ b/packages/core/src/widgets/markdown/plate/components/buttons/DeleteTableToolbarButton.tsx @@ -0,0 +1,33 @@ +import { TableDismiss } from '@styled-icons/fluentui-system-regular/TableDismiss'; +import { deleteTable } from '@udecode/plate'; +import React, { useCallback } from 'react'; + +import { useMdPlateEditorState } from '../../plateTypes'; +import ToolbarButton from './common/ToolbarButton'; + +import type { FC } from 'react'; + +export interface DeleteTableToolbarButtonProps { + disabled: boolean; + variant: 'button' | 'menu'; +} + +const DeleteTableToolbarButton: FC = ({ disabled, variant }) => { + const editor = useMdPlateEditorState(); + + const handleDeleteTable = useCallback(() => { + deleteTable(editor); + }, [editor]); + + return ( + + ); +}; + +export default DeleteTableToolbarButton; diff --git a/packages/core/src/widgets/markdown/plate/components/buttons/IncreaseIndentToolbarButton.tsx b/packages/core/src/widgets/markdown/plate/components/buttons/IncreaseIndentToolbarButton.tsx new file mode 100644 index 00000000..f9630338 --- /dev/null +++ b/packages/core/src/widgets/markdown/plate/components/buttons/IncreaseIndentToolbarButton.tsx @@ -0,0 +1,36 @@ +import { FormatIndentIncrease as FormatIndentIncreaseIcon } from '@styled-icons/material/FormatIndentIncrease'; +import { indent } from '@udecode/plate'; +import React, { useCallback } from 'react'; + +import { useMdPlateEditorState } from '../../plateTypes'; +import ToolbarButton from './common/ToolbarButton'; + +import type { FC } from 'react'; + +export interface IncreaseIndentToolbarButtonProps { + disabled: boolean; + variant: 'button' | 'menu'; +} + +const IncreaseIndentToolbarButton: FC = ({ + disabled, + variant, +}) => { + const editor = useMdPlateEditorState(); + + const handleIndent = useCallback(() => { + indent(editor); + }, [editor]); + + return ( + + ); +}; + +export default IncreaseIndentToolbarButton; diff --git a/packages/core/src/widgets/markdown/plate/components/buttons/InsertColumnToolbarButton.tsx b/packages/core/src/widgets/markdown/plate/components/buttons/InsertColumnToolbarButton.tsx new file mode 100644 index 00000000..564ab408 --- /dev/null +++ b/packages/core/src/widgets/markdown/plate/components/buttons/InsertColumnToolbarButton.tsx @@ -0,0 +1,33 @@ +import { TableInsertColumn } from '@styled-icons/fluentui-system-regular/TableInsertColumn'; +import { insertTableColumn } from '@udecode/plate'; +import React, { useCallback } from 'react'; + +import { useMdPlateEditorState } from '../../plateTypes'; +import ToolbarButton from './common/ToolbarButton'; + +import type { FC } from 'react'; + +export interface InsertColumnToolbarButtonProps { + disabled: boolean; + variant: 'button' | 'menu'; +} + +const InsertColumnToolbarButton: FC = ({ disabled, variant }) => { + const editor = useMdPlateEditorState(); + + const handleInsertTableColumn = useCallback(() => { + insertTableColumn(editor); + }, [editor]); + + return ( + + ); +}; + +export default InsertColumnToolbarButton; diff --git a/packages/core/src/widgets/markdown/plate/components/buttons/common/ImageToolbarButton.tsx b/packages/core/src/widgets/markdown/plate/components/buttons/InsertImageToolbarButton.tsx similarity index 64% rename from packages/core/src/widgets/markdown/plate/components/buttons/common/ImageToolbarButton.tsx rename to packages/core/src/widgets/markdown/plate/components/buttons/InsertImageToolbarButton.tsx index 90dafea3..d116a474 100644 --- a/packages/core/src/widgets/markdown/plate/components/buttons/common/ImageToolbarButton.tsx +++ b/packages/core/src/widgets/markdown/plate/components/buttons/InsertImageToolbarButton.tsx @@ -1,26 +1,25 @@ import { Image as ImageIcon } from '@styled-icons/material/Image'; -import { ELEMENT_IMAGE, insertImage } from '@udecode/plate'; +import { insertImage } from '@udecode/plate'; import React, { useCallback, useMemo } from 'react'; -import MenuItemButton from '@staticcms/core/components/common/menu/MenuItemButton'; import useMediaInsert from '@staticcms/core/lib/hooks/useMediaInsert'; import { isNotEmpty } from '@staticcms/core/lib/util/string.util'; import { useMdPlateEditorState } from '@staticcms/markdown/plate/plateTypes'; -import ToolbarButton from './ToolbarButton'; +import ToolbarButton from './common/ToolbarButton'; import type { Collection, MarkdownField, MediaPath } from '@staticcms/core/interface'; import type { FC } from 'react'; -interface ImageToolbarButtonProps { - variant?: 'button' | 'menu'; +export interface InsertImageToolbarButtonProps { + variant: 'button' | 'menu'; currentValue?: { url: string; alt?: string }; collection: Collection; field: MarkdownField; disabled: boolean; } -const ImageToolbarButton: FC = ({ - variant = 'button', +const InsertImageToolbarButton: FC = ({ + variant, field, collection, currentValue, @@ -47,23 +46,16 @@ const ImageToolbarButton: FC = ({ handleInsert, ); - if (variant === 'menu') { - return ( - - Image - - ); - } - return ( } - onClick={(_editor, event) => openMediaLibrary(event)} + label="Image" + tooltip="Insert image" + icon={ImageIcon} + onClick={openMediaLibrary} disabled={disabled} + variant={variant} /> ); }; -export default ImageToolbarButton; +export default InsertImageToolbarButton; diff --git a/packages/core/src/widgets/markdown/plate/components/buttons/common/LinkToolbarButton.tsx b/packages/core/src/widgets/markdown/plate/components/buttons/InsertLinkToolbarButton.tsx similarity index 68% rename from packages/core/src/widgets/markdown/plate/components/buttons/common/LinkToolbarButton.tsx rename to packages/core/src/widgets/markdown/plate/components/buttons/InsertLinkToolbarButton.tsx index abb0792b..3138e777 100644 --- a/packages/core/src/widgets/markdown/plate/components/buttons/common/LinkToolbarButton.tsx +++ b/packages/core/src/widgets/markdown/plate/components/buttons/InsertLinkToolbarButton.tsx @@ -1,27 +1,26 @@ import { Link as LinkIcon } from '@styled-icons/material/Link'; -import { ELEMENT_LINK, insertLink, someNode } from '@udecode/plate'; +import { ELEMENT_LINK, getSelectionText, insertLink, someNode } from '@udecode/plate'; import React, { useCallback, useMemo } from 'react'; -import MenuItemButton from '@staticcms/core/components/common/menu/MenuItemButton'; import useMediaInsert from '@staticcms/core/lib/hooks/useMediaInsert'; import useUUID from '@staticcms/core/lib/hooks/useUUID'; import { isNotEmpty } from '@staticcms/core/lib/util/string.util'; import { useMdPlateEditorState } from '@staticcms/markdown/plate/plateTypes'; -import ToolbarButton from './ToolbarButton'; +import ToolbarButton from './common/ToolbarButton'; import type { Collection, MarkdownField, MediaPath } from '@staticcms/core/interface'; import type { FC } from 'react'; -interface LinkToolbarButtonProps { - variant?: 'button' | 'menu'; +export interface InsertLinkToolbarButtonProps { + variant: 'button' | 'menu'; currentValue?: { url: string; alt?: string }; collection: Collection; field: MarkdownField; disabled: boolean; } -const LinkToolbarButton: FC = ({ - variant = 'button', +const InsertLinkToolbarButton: FC = ({ + variant, field, collection, currentValue, @@ -45,34 +44,35 @@ const LinkToolbarButton: FC = ({ const isLink = !!editor?.selection && someNode(editor, { match: { type: ELEMENT_LINK } }); + const selectedText: string = useMemo(() => { + if (!editor.selection) { + return ''; + } + + return getSelectionText(editor); + }, [editor]); + const controlID = useUUID(); const openMediaLibrary = useMediaInsert( { path: currentValue?.url ?? '', - alt: currentValue?.alt, + alt: currentValue?.alt ?? selectedText, }, { collection, field, controlID, forImage: false, insertOptions: { chooseUrl, showAlt: true } }, handleInsert, ); - if (variant === 'menu') { - return ( - - File / Link - - ); - } - return ( } - onClick={(_editor, event) => openMediaLibrary(event)} + label="Link" + tooltip="Insert link" + icon={LinkIcon} + onClick={openMediaLibrary} active={isLink} disabled={disabled} + variant={variant} /> ); }; -export default LinkToolbarButton; +export default InsertLinkToolbarButton; diff --git a/packages/core/src/widgets/markdown/plate/components/buttons/InsertRowToolbarButton.tsx b/packages/core/src/widgets/markdown/plate/components/buttons/InsertRowToolbarButton.tsx new file mode 100644 index 00000000..92f02688 --- /dev/null +++ b/packages/core/src/widgets/markdown/plate/components/buttons/InsertRowToolbarButton.tsx @@ -0,0 +1,33 @@ +import { TableInsertRow } from '@styled-icons/fluentui-system-regular/TableInsertRow'; +import { insertTableRow } from '@udecode/plate'; +import React, { useCallback } from 'react'; + +import { useMdPlateEditorState } from '../../plateTypes'; +import ToolbarButton from './common/ToolbarButton'; + +import type { FC } from 'react'; + +export interface InsertRowToolbarButtonProps { + disabled: boolean; + variant: 'button' | 'menu'; +} + +const InsertRowToolbarButton: FC = ({ disabled, variant }) => { + const editor = useMdPlateEditorState(); + + const handleInsertTableRow = useCallback(() => { + insertTableRow(editor); + }, [editor]); + + return ( + + ); +}; + +export default InsertRowToolbarButton; diff --git a/packages/core/src/widgets/markdown/plate/components/buttons/InsertTableToolbarButton.tsx b/packages/core/src/widgets/markdown/plate/components/buttons/InsertTableToolbarButton.tsx new file mode 100644 index 00000000..f49532ba --- /dev/null +++ b/packages/core/src/widgets/markdown/plate/components/buttons/InsertTableToolbarButton.tsx @@ -0,0 +1,40 @@ +import { TableAdd } from '@styled-icons/fluentui-system-regular/TableAdd'; +import { insertTable } from '@udecode/plate'; +import React, { useCallback } from 'react'; + +import { useMdPlateEditorState } from '../../plateTypes'; +import ToolbarButton from './common/ToolbarButton'; + +import type { FC } from 'react'; + +export interface InsertTableToolbarButtonProps { + disabled: boolean; + variant: 'button' | 'menu'; +} + +const InsertTableToolbarButton: FC = ({ + disabled, + variant = 'button', +}) => { + const editor = useMdPlateEditorState(); + + const handleTableAdd = useCallback(() => { + insertTable(editor, { + rowCount: 2, + colCount: 2, + }); + }, [editor]); + + return ( + + ); +}; + +export default InsertTableToolbarButton; diff --git a/packages/core/src/widgets/markdown/plate/components/buttons/ItalicToolbarButton.tsx b/packages/core/src/widgets/markdown/plate/components/buttons/ItalicToolbarButton.tsx new file mode 100644 index 00000000..6fd6c993 --- /dev/null +++ b/packages/core/src/widgets/markdown/plate/components/buttons/ItalicToolbarButton.tsx @@ -0,0 +1,26 @@ +import { FormatItalic as FormatItalicIcon } from '@styled-icons/material/FormatItalic'; +import { MARK_ITALIC } from '@udecode/plate'; +import React from 'react'; + +import MarkToolbarButton from './common/MarkToolbarButton'; + +import type { FC } from 'react'; + +export interface ItalicToolbarButtonsProp { + disabled: boolean; + variant: 'button' | 'menu'; +} + +const ItalicToolbarButton: FC = ({ disabled, variant }) => { + return ( + + ); +}; + +export default ItalicToolbarButton; diff --git a/packages/core/src/widgets/markdown/plate/components/buttons/ListToolbarButtons.tsx b/packages/core/src/widgets/markdown/plate/components/buttons/ListToolbarButtons.tsx deleted file mode 100644 index f02d956c..00000000 --- a/packages/core/src/widgets/markdown/plate/components/buttons/ListToolbarButtons.tsx +++ /dev/null @@ -1,60 +0,0 @@ -import { FormatIndentDecrease as FormatIndentDecreaseIcon } from '@styled-icons/material/FormatIndentDecrease'; -import { FormatIndentIncrease as FormatIndentIncreaseIcon } from '@styled-icons/material/FormatIndentIncrease'; -import { FormatListBulleted as FormatListBulletedIcon } from '@styled-icons/material/FormatListBulleted'; -import { FormatListNumbered as FormatListNumberedIcon } from '@styled-icons/material/FormatListNumbered'; -import { ELEMENT_OL, ELEMENT_UL, getPluginType, indent, outdent } from '@udecode/plate'; -import React, { useCallback } from 'react'; - -import { useMdPlateEditorRef } from '@staticcms/markdown'; -import ListToolbarButton from './common/ListToolbarButton'; -import ToolbarButton from './common/ToolbarButton'; - -import type { FC } from 'react'; -import type { MdEditor } from '@staticcms/markdown'; - -interface ListToolbarButtonsProps { - disabled: boolean; -} - -const ListToolbarButtons: FC = ({ disabled }) => { - const editor = useMdPlateEditorRef(); - - const handleOutdent = useCallback((editor: MdEditor) => { - outdent(editor); - }, []); - - const handleIndent = useCallback((editor: MdEditor) => { - indent(editor); - }, []); - - return ( - <> - } - disabled={disabled} - /> - } - disabled={disabled} - /> - } - disabled={disabled} - /> - } - disabled={disabled} - /> - - ); -}; - -export default ListToolbarButtons; diff --git a/packages/core/src/widgets/markdown/plate/components/buttons/MediaToolbarButtons.tsx b/packages/core/src/widgets/markdown/plate/components/buttons/MediaToolbarButtons.tsx deleted file mode 100644 index 951bd7a2..00000000 --- a/packages/core/src/widgets/markdown/plate/components/buttons/MediaToolbarButtons.tsx +++ /dev/null @@ -1,42 +0,0 @@ -import React from 'react'; - -import ImageToolbarButton from './common/ImageToolbarButton'; -import LinkToolbarButton from './common/LinkToolbarButton'; - -import type { Collection, MarkdownField } from '@staticcms/core/interface'; -import type { FC } from 'react'; - -export interface MediaToolbarButtonsProps { - collection: Collection; - field: MarkdownField; - hideImages?: boolean; - disabled: boolean; -} - -const MediaToolbarButtons: FC = ({ - collection, - field, - hideImages = false, - disabled, -}) => { - return ( - <> - - {!hideImages ? ( - - ) : null} - - ); -}; - -export default MediaToolbarButtons; diff --git a/packages/core/src/widgets/markdown/plate/components/buttons/OrderedListToolbarButton.tsx b/packages/core/src/widgets/markdown/plate/components/buttons/OrderedListToolbarButton.tsx new file mode 100644 index 00000000..f6b346ae --- /dev/null +++ b/packages/core/src/widgets/markdown/plate/components/buttons/OrderedListToolbarButton.tsx @@ -0,0 +1,26 @@ +import { FormatListNumbered as FormatListNumberedIcon } from '@styled-icons/material/FormatListNumbered'; +import { ELEMENT_OL } from '@udecode/plate'; +import React from 'react'; + +import ListToolbarButton from './common/ListToolbarButton'; + +import type { FC } from 'react'; + +export interface OrderedListToolbarButtonProps { + disabled: boolean; + variant: 'button' | 'menu'; +} + +const OrderedListToolbarButton: FC = ({ disabled, variant }) => { + return ( + + ); +}; + +export default OrderedListToolbarButton; diff --git a/packages/core/src/widgets/markdown/plate/components/buttons/ShortcodeToolbarButton.tsx b/packages/core/src/widgets/markdown/plate/components/buttons/ShortcodeToolbarButton.tsx index 073d80a9..18fe15c6 100644 --- a/packages/core/src/widgets/markdown/plate/components/buttons/ShortcodeToolbarButton.tsx +++ b/packages/core/src/widgets/markdown/plate/components/buttons/ShortcodeToolbarButton.tsx @@ -11,7 +11,7 @@ import { ELEMENT_SHORTCODE, useMdPlateEditorState } from '@staticcms/markdown/pl import type { FC } from 'react'; -interface ShortcodeToolbarButtonProps { +export interface ShortcodeToolbarButtonProps { disabled: boolean; } @@ -36,7 +36,7 @@ const ShortcodeToolbarButton: FC = ({ disabled }) = return (