feat: markdown toolbar customization (#776)
This commit is contained in:
parent
a7ab1a7c0d
commit
cd13f3d193
@ -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
|
||||
|
2
packages/core/src/__mocks__/@udecode/plate-list.ts
Normal file
2
packages/core/src/__mocks__/@udecode/plate-list.ts
Normal file
@ -0,0 +1,2 @@
|
||||
export const getListItemEntry = jest.fn();
|
||||
export const toggleList = jest.fn();
|
20
packages/core/src/constants/toolbar_buttons.ts
Normal file
20
packages/core/src/constants/toolbar_buttons.ts
Normal file
@ -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';
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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<BalloonToolbarProps> = ({
|
||||
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<BalloonToolbarProps> = ({
|
||||
|
||||
const debouncedEditorFocus = useDebounce(hasEditorFocus, 150);
|
||||
|
||||
const groups: ReactNode[] = useMemo(() => {
|
||||
const [groups, setGroups] = useState<ReactNode[]>([]);
|
||||
|
||||
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 [
|
||||
<BasicMarkToolbarButtons
|
||||
key="selection-basic-mark-buttons"
|
||||
useMdx={useMdx}
|
||||
disabled={disabled}
|
||||
/>,
|
||||
<BasicElementToolbarButtons
|
||||
key="selection-basic-element-buttons"
|
||||
hideFontTypeSelect={isInTableCell}
|
||||
hideCodeBlock
|
||||
disabled={disabled}
|
||||
/>,
|
||||
isInTableCell && (
|
||||
<TableToolbarButtons key="selection-table-toolbar-buttons" disabled={disabled} />
|
||||
setGroups(
|
||||
getToolbarButtons(
|
||||
isInTableCell
|
||||
? field.toolbar_buttons?.table_selection ?? DEFAULT_TABLE_SELECTION_BUTTONS
|
||||
: field.toolbar_buttons?.selection ?? DEFAULT_SELECTION_BUTTONS,
|
||||
collection,
|
||||
field,
|
||||
disabled,
|
||||
),
|
||||
<MediaToolbarButtons
|
||||
key="selection-media-buttons"
|
||||
collection={collection}
|
||||
field={field}
|
||||
hideImages
|
||||
disabled={disabled}
|
||||
/>,
|
||||
].filter(Boolean);
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const allButtons = [
|
||||
<BasicMarkToolbarButtons
|
||||
key="empty-basic-mark-buttons"
|
||||
useMdx={useMdx}
|
||||
disabled={disabled}
|
||||
/>,
|
||||
<BasicElementToolbarButtons
|
||||
key="empty-basic-element-buttons"
|
||||
hideFontTypeSelect={isInTableCell}
|
||||
hideCodeBlock
|
||||
disabled={disabled}
|
||||
/>,
|
||||
<TableToolbarButtons
|
||||
key="empty-table-toolbar-buttons"
|
||||
isInTable={isInTableCell}
|
||||
disabled={disabled}
|
||||
/>,
|
||||
<MediaToolbarButtons
|
||||
key="empty-media-buttons"
|
||||
collection={collection}
|
||||
field={field}
|
||||
disabled={disabled}
|
||||
/>,
|
||||
!useMdx ? <ShortcodeToolbarButton key="shortcode-button" disabled={disabled} /> : 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<BalloonToolbarProps> = ({
|
||||
containerRef,
|
||||
collection,
|
||||
field,
|
||||
isMediaLibraryOpen,
|
||||
]);
|
||||
|
||||
const [prevSelectionBoundingClientRect, setPrevSelectionBoundingClientRect] = useState(
|
||||
@ -190,8 +227,8 @@ const BalloonToolbar: FC<BalloonToolbarProps> = ({
|
||||
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<BalloonToolbarProps> = ({
|
||||
"
|
||||
>
|
||||
<div
|
||||
data-testid="balloon-toolbar"
|
||||
className="
|
||||
flex
|
||||
gap-0.5
|
||||
"
|
||||
flex
|
||||
gap-0.5
|
||||
"
|
||||
>
|
||||
{groups.length > 0 ? groups : debouncedGroups}
|
||||
</div>
|
||||
|
@ -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(<BalloonToolbarWrapper />);
|
||||
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(<BalloonToolbarWrapper />);
|
||||
mockUseEditor.mockReturnValue(mockEditor);
|
||||
mockUsePlateSelection.mockReturnValue(mockEditor.selection);
|
||||
|
||||
result.rerender(<BalloonToolbarWrapper useMdx={useMdx} />);
|
||||
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(<BalloonToolbarWrapper />);
|
||||
|
||||
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(<BalloonToolbarWrapper />);
|
||||
|
||||
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();
|
||||
});
|
||||
});
|
||||
|
@ -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<MarkdownField>;
|
||||
field: MarkdownField;
|
||||
disabled: boolean;
|
||||
}
|
||||
|
||||
const AddButtons: FC<AddButtonsProps> = ({ 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 (
|
||||
<Menu
|
||||
label={<AddIcon className="h-5 w-5" aria-hidden="true" />}
|
||||
data-testid="toolbar-add-buttons"
|
||||
keepMounted
|
||||
hideDropdownIcon
|
||||
variant="text"
|
||||
className="
|
||||
py-0.5
|
||||
px-0.5
|
||||
h-7
|
||||
w-7
|
||||
"
|
||||
disabled={disabled}
|
||||
>
|
||||
<MenuGroup>
|
||||
<MenuItemButton
|
||||
key={ELEMENT_BLOCKQUOTE}
|
||||
onClick={handleBlockOnClick(ELEMENT_BLOCKQUOTE)}
|
||||
startIcon={FormatQuoteIcon}
|
||||
>
|
||||
Blockquote
|
||||
</MenuItemButton>
|
||||
<MenuItemButton
|
||||
key={ELEMENT_CODE_BLOCK}
|
||||
onClick={handleCodeBlockOnClick}
|
||||
startIcon={CodeIcon}
|
||||
>
|
||||
Code Block
|
||||
</MenuItemButton>
|
||||
</MenuGroup>
|
||||
<MenuGroup>
|
||||
<MenuItemButton key={ELEMENT_TABLE} onClick={handleTableAdd} startIcon={TableAdd}>
|
||||
Table
|
||||
</MenuItemButton>
|
||||
</MenuGroup>
|
||||
<MenuGroup>
|
||||
<ImageToolbarButton
|
||||
key={ELEMENT_IMAGE}
|
||||
collection={collection}
|
||||
field={field}
|
||||
variant="menu"
|
||||
disabled={disabled}
|
||||
/>
|
||||
<LinkToolbarButton
|
||||
key={ELEMENT_LINK}
|
||||
collection={collection}
|
||||
field={field}
|
||||
variant="menu"
|
||||
disabled={disabled}
|
||||
/>
|
||||
</MenuGroup>
|
||||
</Menu>
|
||||
);
|
||||
};
|
||||
|
||||
export default AddButtons;
|
@ -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<AlignToolbarButtonsProps> = ({ disabled }) => {
|
||||
return (
|
||||
<>
|
||||
<AlignToolbarButton
|
||||
key="algin-button-left"
|
||||
tooltip="Align Left"
|
||||
value="left"
|
||||
icon={<FormatAlignLeftIcon className="h-5 w-5" />}
|
||||
disabled={disabled}
|
||||
/>
|
||||
<AlignToolbarButton
|
||||
key="algin-button-center"
|
||||
tooltip="Align Center"
|
||||
value="center"
|
||||
icon={<FormatAlignCenterIcon className="h-5 w-5" />}
|
||||
disabled={disabled}
|
||||
/>
|
||||
<AlignToolbarButton
|
||||
key="algin-button-right"
|
||||
tooltip="Align Right"
|
||||
value="right"
|
||||
icon={<FormatAlignRightIcon className="h-5 w-5" />}
|
||||
disabled={disabled}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default AlignToolbarButtons;
|
@ -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<BasicElementToolbarButtonsProps> = ({
|
||||
hideFontTypeSelect = false,
|
||||
disableFontTypeSelect = false,
|
||||
disabled,
|
||||
}) => {
|
||||
return !hideFontTypeSelect ? (
|
||||
<FontTypeSelect disabled={disableFontTypeSelect || disabled} />
|
||||
) : null;
|
||||
};
|
||||
|
||||
export default BasicElementToolbarButtons;
|
@ -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<BasicMarkToolbarButtonsProps> = ({
|
||||
extended = false,
|
||||
useMdx,
|
||||
disabled,
|
||||
}) => {
|
||||
return (
|
||||
<>
|
||||
<MarkToolbarButton
|
||||
tooltip="Bold"
|
||||
type={MARK_BOLD}
|
||||
icon={<FormatBoldIcon className="h-5 w-5" />}
|
||||
disabled={disabled}
|
||||
/>
|
||||
<MarkToolbarButton
|
||||
tooltip="Italic"
|
||||
type={MARK_ITALIC}
|
||||
icon={<FormatItalicIcon className="h-5 w-5" />}
|
||||
disabled={disabled}
|
||||
/>
|
||||
{useMdx ? (
|
||||
<MarkToolbarButton
|
||||
key="underline-button"
|
||||
tooltip="Underline"
|
||||
type={MARK_UNDERLINE}
|
||||
icon={<FormatUnderlinedIcon className="h-5 w-5" />}
|
||||
disabled={disabled}
|
||||
/>
|
||||
) : null}
|
||||
<MarkToolbarButton
|
||||
tooltip="Strikethrough"
|
||||
type={MARK_STRIKETHROUGH}
|
||||
icon={<FormatStrikethroughIcon className="h-5 w-5" />}
|
||||
disabled={disabled}
|
||||
/>
|
||||
<MarkToolbarButton
|
||||
tooltip="Code"
|
||||
type={MARK_CODE}
|
||||
icon={<CodeIcon className="h-5 w-5" />}
|
||||
disabled={disabled}
|
||||
/>
|
||||
{useMdx && extended ? (
|
||||
<>
|
||||
<MarkToolbarButton
|
||||
key="superscript-button"
|
||||
tooltip="Superscript"
|
||||
type={MARK_SUPERSCRIPT}
|
||||
clear={MARK_SUBSCRIPT}
|
||||
icon={<SuperscriptIcon className="h-5 w-5" />}
|
||||
disabled={disabled}
|
||||
/>
|
||||
<MarkToolbarButton
|
||||
key="subscript-button"
|
||||
tooltip="Subscript"
|
||||
type={MARK_SUBSCRIPT}
|
||||
clear={MARK_SUPERSCRIPT}
|
||||
icon={<SubscriptIcon className="h-5 w-5" />}
|
||||
disabled={disabled}
|
||||
/>
|
||||
</>
|
||||
) : null}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default BasicMarkToolbarButtons;
|
@ -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<BlockquoteToolbarButtonProps> = ({ disabled, variant }) => {
|
||||
return (
|
||||
<BlockToolbarButton
|
||||
label="Blockquote"
|
||||
tooltip="Insert blockquote"
|
||||
icon={FormatQuoteIcon}
|
||||
type={ELEMENT_BLOCKQUOTE}
|
||||
disabled={disabled}
|
||||
variant={variant}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default BlockquoteToolbarButton;
|
@ -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<BoldToolbarButtonProps> = ({ disabled, variant }) => {
|
||||
return (
|
||||
<MarkToolbarButton
|
||||
tooltip="Bold"
|
||||
type={MARK_BOLD}
|
||||
variant={variant}
|
||||
icon={FormatBoldIcon}
|
||||
disabled={disabled}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default BoldToolbarButton;
|
@ -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<CodeBlockToolbarButtonsProps> = ({ disabled, variant }) => {
|
||||
const editor = useMdPlateEditorState();
|
||||
|
||||
const handleCodeBlockOnClick = useCallback(() => {
|
||||
insertEmptyCodeBlock(editor, {
|
||||
insertNodesOptions: { select: true },
|
||||
});
|
||||
}, [editor]);
|
||||
|
||||
return (
|
||||
<ToolbarButton
|
||||
label="Code block"
|
||||
tooltip="Insert code block"
|
||||
icon={CodeIcon}
|
||||
onClick={handleCodeBlockOnClick}
|
||||
disabled={disabled}
|
||||
variant={variant}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default CodeBlockToolbarButtons;
|
@ -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<CodeToolbarButtonProps> = ({ disabled, variant }) => {
|
||||
return (
|
||||
<MarkToolbarButton
|
||||
tooltip="Code"
|
||||
type={MARK_CODE}
|
||||
icon={CodeIcon}
|
||||
disabled={disabled}
|
||||
variant={variant}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default CodeToolbarButton;
|
@ -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<ColorToolbarButtonsProps> = ({ disabled }) => {
|
||||
return (
|
||||
<>
|
||||
<ColorPickerToolbarDropdown
|
||||
key="color-picker-button"
|
||||
pluginKey={MARK_COLOR}
|
||||
icon={<FormatColorTextIcon className="h-5 w-5" />}
|
||||
tooltip="Color"
|
||||
disabled={disabled}
|
||||
/>
|
||||
<ColorPickerToolbarDropdown
|
||||
key="background-color-picker-button"
|
||||
pluginKey={MARK_BG_COLOR}
|
||||
icon={<FontDownloadIcon className="h-5 w-5" />}
|
||||
tooltip="Background Color"
|
||||
disabled={disabled}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default ColorToolbarButtons;
|
@ -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<DecreaseIndentToolbarButtonProps> = ({
|
||||
disabled,
|
||||
variant,
|
||||
}) => {
|
||||
const editor = useMdPlateEditorState();
|
||||
|
||||
const handleOutdent = useCallback(() => {
|
||||
outdent(editor);
|
||||
}, [editor]);
|
||||
|
||||
return (
|
||||
<ToolbarButton
|
||||
tooltip="Decrease indent"
|
||||
onClick={handleOutdent}
|
||||
icon={FormatIndentDecreaseIcon}
|
||||
disabled={disabled}
|
||||
variant={variant}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default DecreaseIndentToolbarButton;
|
@ -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<DeleteColumnToolbarButtonProps> = ({ disabled, variant }) => {
|
||||
const editor = useMdPlateEditorState();
|
||||
|
||||
const handleDeleteColumn = useCallback(() => {
|
||||
deleteColumn(editor);
|
||||
}, [editor]);
|
||||
|
||||
return (
|
||||
<ToolbarButton
|
||||
tooltip="Delete column"
|
||||
icon={TableDeleteColumn}
|
||||
onClick={handleDeleteColumn}
|
||||
disabled={disabled}
|
||||
variant={variant}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default DeleteColumnToolbarButton;
|
@ -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<DeleteRowToolbarButtonProps> = ({ disabled, variant }) => {
|
||||
const editor = useMdPlateEditorState();
|
||||
|
||||
const handleDeleteRow = useCallback(() => {
|
||||
deleteRow(editor);
|
||||
}, [editor]);
|
||||
|
||||
return (
|
||||
<ToolbarButton
|
||||
tooltip="Delete row"
|
||||
icon={TableDeleteRow}
|
||||
onClick={handleDeleteRow}
|
||||
disabled={disabled}
|
||||
variant={variant}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default DeleteRowToolbarButton;
|
@ -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<DeleteTableToolbarButtonProps> = ({ disabled, variant }) => {
|
||||
const editor = useMdPlateEditorState();
|
||||
|
||||
const handleDeleteTable = useCallback(() => {
|
||||
deleteTable(editor);
|
||||
}, [editor]);
|
||||
|
||||
return (
|
||||
<ToolbarButton
|
||||
tooltip="Delete table"
|
||||
icon={TableDismiss}
|
||||
onClick={handleDeleteTable}
|
||||
disabled={disabled}
|
||||
variant={variant}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default DeleteTableToolbarButton;
|
@ -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<IncreaseIndentToolbarButtonProps> = ({
|
||||
disabled,
|
||||
variant,
|
||||
}) => {
|
||||
const editor = useMdPlateEditorState();
|
||||
|
||||
const handleIndent = useCallback(() => {
|
||||
indent(editor);
|
||||
}, [editor]);
|
||||
|
||||
return (
|
||||
<ToolbarButton
|
||||
tooltip="Increase indent"
|
||||
onClick={handleIndent}
|
||||
icon={FormatIndentIncreaseIcon}
|
||||
disabled={disabled}
|
||||
variant={variant}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default IncreaseIndentToolbarButton;
|
@ -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<InsertColumnToolbarButtonProps> = ({ disabled, variant }) => {
|
||||
const editor = useMdPlateEditorState();
|
||||
|
||||
const handleInsertTableColumn = useCallback(() => {
|
||||
insertTableColumn(editor);
|
||||
}, [editor]);
|
||||
|
||||
return (
|
||||
<ToolbarButton
|
||||
tooltip="Insert column"
|
||||
icon={TableInsertColumn}
|
||||
onClick={handleInsertTableColumn}
|
||||
disabled={disabled}
|
||||
variant={variant}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default InsertColumnToolbarButton;
|
@ -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<MarkdownField>;
|
||||
field: MarkdownField;
|
||||
disabled: boolean;
|
||||
}
|
||||
|
||||
const ImageToolbarButton: FC<ImageToolbarButtonProps> = ({
|
||||
variant = 'button',
|
||||
const InsertImageToolbarButton: FC<InsertImageToolbarButtonProps> = ({
|
||||
variant,
|
||||
field,
|
||||
collection,
|
||||
currentValue,
|
||||
@ -47,23 +46,16 @@ const ImageToolbarButton: FC<ImageToolbarButtonProps> = ({
|
||||
handleInsert,
|
||||
);
|
||||
|
||||
if (variant === 'menu') {
|
||||
return (
|
||||
<MenuItemButton key={ELEMENT_IMAGE} onClick={openMediaLibrary} startIcon={ImageIcon}>
|
||||
Image
|
||||
</MenuItemButton>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<ToolbarButton
|
||||
key="insertImage"
|
||||
tooltip="Insert Image"
|
||||
icon={<ImageIcon className="w-5 h-5" />}
|
||||
onClick={(_editor, event) => openMediaLibrary(event)}
|
||||
label="Image"
|
||||
tooltip="Insert image"
|
||||
icon={ImageIcon}
|
||||
onClick={openMediaLibrary}
|
||||
disabled={disabled}
|
||||
variant={variant}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default ImageToolbarButton;
|
||||
export default InsertImageToolbarButton;
|
@ -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<MarkdownField>;
|
||||
field: MarkdownField;
|
||||
disabled: boolean;
|
||||
}
|
||||
|
||||
const LinkToolbarButton: FC<LinkToolbarButtonProps> = ({
|
||||
variant = 'button',
|
||||
const InsertLinkToolbarButton: FC<InsertLinkToolbarButtonProps> = ({
|
||||
variant,
|
||||
field,
|
||||
collection,
|
||||
currentValue,
|
||||
@ -45,34 +44,35 @@ const LinkToolbarButton: FC<LinkToolbarButtonProps> = ({
|
||||
|
||||
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 (
|
||||
<MenuItemButton key={ELEMENT_LINK} onClick={openMediaLibrary} startIcon={LinkIcon}>
|
||||
File / Link
|
||||
</MenuItemButton>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<ToolbarButton
|
||||
key="insertLink"
|
||||
tooltip="Insert Link"
|
||||
icon={<LinkIcon className="w-5 h-5" />}
|
||||
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;
|
@ -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<InsertRowToolbarButtonProps> = ({ disabled, variant }) => {
|
||||
const editor = useMdPlateEditorState();
|
||||
|
||||
const handleInsertTableRow = useCallback(() => {
|
||||
insertTableRow(editor);
|
||||
}, [editor]);
|
||||
|
||||
return (
|
||||
<ToolbarButton
|
||||
tooltip="Insert row"
|
||||
icon={TableInsertRow}
|
||||
onClick={handleInsertTableRow}
|
||||
disabled={disabled}
|
||||
variant={variant}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default InsertRowToolbarButton;
|
@ -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<InsertTableToolbarButtonProps> = ({
|
||||
disabled,
|
||||
variant = 'button',
|
||||
}) => {
|
||||
const editor = useMdPlateEditorState();
|
||||
|
||||
const handleTableAdd = useCallback(() => {
|
||||
insertTable(editor, {
|
||||
rowCount: 2,
|
||||
colCount: 2,
|
||||
});
|
||||
}, [editor]);
|
||||
|
||||
return (
|
||||
<ToolbarButton
|
||||
label="Table"
|
||||
tooltip="Insert table"
|
||||
icon={TableAdd}
|
||||
onClick={handleTableAdd}
|
||||
disabled={disabled}
|
||||
variant={variant}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default InsertTableToolbarButton;
|
@ -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<ItalicToolbarButtonsProp> = ({ disabled, variant }) => {
|
||||
return (
|
||||
<MarkToolbarButton
|
||||
tooltip="Italic"
|
||||
type={MARK_ITALIC}
|
||||
variant={variant}
|
||||
icon={FormatItalicIcon}
|
||||
disabled={disabled}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default ItalicToolbarButton;
|
@ -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<ListToolbarButtonsProps> = ({ disabled }) => {
|
||||
const editor = useMdPlateEditorRef();
|
||||
|
||||
const handleOutdent = useCallback((editor: MdEditor) => {
|
||||
outdent(editor);
|
||||
}, []);
|
||||
|
||||
const handleIndent = useCallback((editor: MdEditor) => {
|
||||
indent(editor);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
<ListToolbarButton
|
||||
tooltip="List"
|
||||
type={ELEMENT_UL}
|
||||
icon={<FormatListBulletedIcon className="h-5 w-5" />}
|
||||
disabled={disabled}
|
||||
/>
|
||||
<ListToolbarButton
|
||||
tooltip="Numbered List"
|
||||
type={getPluginType(editor, ELEMENT_OL)}
|
||||
icon={<FormatListNumberedIcon className="h-5 w-5" />}
|
||||
disabled={disabled}
|
||||
/>
|
||||
<ToolbarButton
|
||||
tooltip="Outdent"
|
||||
onClick={handleOutdent}
|
||||
icon={<FormatIndentDecreaseIcon className="h-5 w-5" />}
|
||||
disabled={disabled}
|
||||
/>
|
||||
<ToolbarButton
|
||||
tooltip="Indent"
|
||||
onClick={handleIndent}
|
||||
icon={<FormatIndentIncreaseIcon className="h-5 w-5" />}
|
||||
disabled={disabled}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default ListToolbarButtons;
|
@ -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<MarkdownField>;
|
||||
field: MarkdownField;
|
||||
hideImages?: boolean;
|
||||
disabled: boolean;
|
||||
}
|
||||
|
||||
const MediaToolbarButtons: FC<MediaToolbarButtonsProps> = ({
|
||||
collection,
|
||||
field,
|
||||
hideImages = false,
|
||||
disabled,
|
||||
}) => {
|
||||
return (
|
||||
<>
|
||||
<LinkToolbarButton
|
||||
key="link-button"
|
||||
collection={collection}
|
||||
field={field}
|
||||
disabled={disabled}
|
||||
/>
|
||||
{!hideImages ? (
|
||||
<ImageToolbarButton
|
||||
key="image-button"
|
||||
collection={collection}
|
||||
field={field}
|
||||
disabled={disabled}
|
||||
/>
|
||||
) : null}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default MediaToolbarButtons;
|
@ -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<OrderedListToolbarButtonProps> = ({ disabled, variant }) => {
|
||||
return (
|
||||
<ListToolbarButton
|
||||
tooltip="Numbered list"
|
||||
type={ELEMENT_OL}
|
||||
icon={FormatListNumberedIcon}
|
||||
disabled={disabled}
|
||||
variant={variant}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default OrderedListToolbarButton;
|
@ -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<ShortcodeToolbarButtonProps> = ({ disabled }) =
|
||||
return (
|
||||
<Menu
|
||||
label={<DataArrayIcon className="h-5 w-5" aria-hidden="true" />}
|
||||
data-testid="add-buttons"
|
||||
data-testid="toolbar-button-shortcode"
|
||||
keepMounted
|
||||
hideDropdownIcon
|
||||
variant="text"
|
||||
|
@ -0,0 +1,26 @@
|
||||
import { FormatStrikethrough as FormatStrikethroughIcon } from '@styled-icons/material/FormatStrikethrough';
|
||||
import { MARK_STRIKETHROUGH } from '@udecode/plate';
|
||||
import React from 'react';
|
||||
|
||||
import MarkToolbarButton from './common/MarkToolbarButton';
|
||||
|
||||
import type { FC } from 'react';
|
||||
|
||||
export interface StrikethroughToolbarButtonProps {
|
||||
disabled: boolean;
|
||||
variant: 'button' | 'menu';
|
||||
}
|
||||
|
||||
const StrikethroughToolbarButton: FC<StrikethroughToolbarButtonProps> = ({ disabled, variant }) => {
|
||||
return (
|
||||
<MarkToolbarButton
|
||||
tooltip="Strikethrough"
|
||||
type={MARK_STRIKETHROUGH}
|
||||
variant={variant}
|
||||
icon={FormatStrikethroughIcon}
|
||||
disabled={disabled}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default StrikethroughToolbarButton;
|
@ -1,104 +0,0 @@
|
||||
import { TableAdd } from '@styled-icons/fluentui-system-regular/TableAdd';
|
||||
import { TableDeleteColumn } from '@styled-icons/fluentui-system-regular/TableDeleteColumn';
|
||||
import { TableDeleteRow } from '@styled-icons/fluentui-system-regular/TableDeleteRow';
|
||||
import { TableDismiss } from '@styled-icons/fluentui-system-regular/TableDismiss';
|
||||
import { TableInsertColumn } from '@styled-icons/fluentui-system-regular/TableInsertColumn';
|
||||
import { TableInsertRow } from '@styled-icons/fluentui-system-regular/TableInsertRow';
|
||||
import {
|
||||
deleteColumn,
|
||||
deleteRow,
|
||||
deleteTable,
|
||||
insertTable,
|
||||
insertTableColumn,
|
||||
insertTableRow,
|
||||
} from '@udecode/plate';
|
||||
import React, { useCallback } from 'react';
|
||||
|
||||
import ToolbarButton from './common/ToolbarButton';
|
||||
|
||||
import type { FC } from 'react';
|
||||
import type { MdEditor } from '@staticcms/markdown';
|
||||
|
||||
export interface TableToolbarButtonsProps {
|
||||
isInTable?: boolean;
|
||||
disabled: boolean;
|
||||
}
|
||||
|
||||
const TableToolbarButtons: FC<TableToolbarButtonsProps> = ({ isInTable = true, disabled }) => {
|
||||
const handleTableAdd = useCallback((editor: MdEditor) => {
|
||||
insertTable(editor, {
|
||||
rowCount: 2,
|
||||
colCount: 2,
|
||||
});
|
||||
}, []);
|
||||
|
||||
const handleInsertTableRow = useCallback((editor: MdEditor) => {
|
||||
insertTableRow(editor);
|
||||
}, []);
|
||||
|
||||
const handleDeleteRow = useCallback((editor: MdEditor) => {
|
||||
deleteRow(editor);
|
||||
}, []);
|
||||
|
||||
const handleInsertTableColumn = useCallback((editor: MdEditor) => {
|
||||
insertTableColumn(editor);
|
||||
}, []);
|
||||
|
||||
const handleDeleteColumn = useCallback((editor: MdEditor) => {
|
||||
deleteColumn(editor);
|
||||
}, []);
|
||||
|
||||
const handleDeleteTable = useCallback((editor: MdEditor) => {
|
||||
deleteTable(editor);
|
||||
}, []);
|
||||
|
||||
return isInTable ? (
|
||||
<>
|
||||
<ToolbarButton
|
||||
key="insertRow"
|
||||
tooltip="Insert Row"
|
||||
icon={<TableInsertRow className="w-5 h-5" />}
|
||||
onClick={handleInsertTableRow}
|
||||
disabled={disabled}
|
||||
/>
|
||||
<ToolbarButton
|
||||
key="deleteRow"
|
||||
tooltip="Delete Row"
|
||||
icon={<TableDeleteRow className="w-5 h-5" />}
|
||||
onClick={handleDeleteRow}
|
||||
disabled={disabled}
|
||||
/>
|
||||
<ToolbarButton
|
||||
key="insertColumn"
|
||||
tooltip="Insert Column"
|
||||
icon={<TableInsertColumn className="w-5 h-5" />}
|
||||
onClick={handleInsertTableColumn}
|
||||
disabled={disabled}
|
||||
/>
|
||||
<ToolbarButton
|
||||
key="deleteColumn"
|
||||
tooltip="Delete Column"
|
||||
icon={<TableDeleteColumn className="w-5 h-5" />}
|
||||
onClick={handleDeleteColumn}
|
||||
disabled={disabled}
|
||||
/>
|
||||
<ToolbarButton
|
||||
key="deleteTable"
|
||||
tooltip="Delete Table"
|
||||
icon={<TableDismiss className="w-5 h-5" />}
|
||||
onClick={handleDeleteTable}
|
||||
disabled={disabled}
|
||||
/>
|
||||
</>
|
||||
) : (
|
||||
<ToolbarButton
|
||||
key="insertRow"
|
||||
tooltip="Add Table"
|
||||
icon={<TableAdd className="w-5 h-5" />}
|
||||
onClick={handleTableAdd}
|
||||
disabled={disabled}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default TableToolbarButtons;
|
@ -0,0 +1,26 @@
|
||||
import { FormatListBulleted as FormatListBulletedIcon } from '@styled-icons/material/FormatListBulleted';
|
||||
import { ELEMENT_UL } from '@udecode/plate';
|
||||
import React from 'react';
|
||||
|
||||
import ListToolbarButton from './common/ListToolbarButton';
|
||||
|
||||
import type { FC } from 'react';
|
||||
|
||||
export interface UnorderedListToolbarButtonProps {
|
||||
disabled: boolean;
|
||||
variant: 'button' | 'menu';
|
||||
}
|
||||
|
||||
const UnorderedListToolbarButton: FC<UnorderedListToolbarButtonProps> = ({ disabled, variant }) => {
|
||||
return (
|
||||
<ListToolbarButton
|
||||
tooltip="List"
|
||||
type={ELEMENT_UL}
|
||||
icon={FormatListBulletedIcon}
|
||||
disabled={disabled}
|
||||
variant={variant}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default UnorderedListToolbarButton;
|
@ -4,7 +4,6 @@ import React, { useCallback } from 'react';
|
||||
import { useMdPlateEditorState } from '@staticcms/markdown/plate/plateTypes';
|
||||
import ToolbarButton from './ToolbarButton';
|
||||
|
||||
import type { MdEditor } from '@staticcms/markdown';
|
||||
import type { Alignment } from '@udecode/plate';
|
||||
import type { FC } from 'react';
|
||||
import type { ToolbarButtonProps } from './ToolbarButton';
|
||||
@ -12,6 +11,7 @@ import type { ToolbarButtonProps } from './ToolbarButton';
|
||||
export interface AlignToolbarButtonProps extends Omit<ToolbarButtonProps, 'active' | 'onClick'> {
|
||||
value: Alignment;
|
||||
pluginKey?: string;
|
||||
variant: 'button' | 'menu';
|
||||
}
|
||||
|
||||
const AlignToolbarButton: FC<AlignToolbarButtonProps> = ({
|
||||
@ -21,15 +21,12 @@ const AlignToolbarButton: FC<AlignToolbarButtonProps> = ({
|
||||
}) => {
|
||||
const editor = useMdPlateEditorState();
|
||||
|
||||
const handleOnClick = useCallback(
|
||||
(editor: MdEditor) => {
|
||||
setAlign(editor, {
|
||||
value,
|
||||
key: pluginKey,
|
||||
});
|
||||
},
|
||||
[pluginKey, value],
|
||||
);
|
||||
const handleOnClick = useCallback(() => {
|
||||
setAlign(editor, {
|
||||
value,
|
||||
key: pluginKey,
|
||||
});
|
||||
}, [editor, pluginKey, value]);
|
||||
|
||||
return (
|
||||
<ToolbarButton
|
||||
|
@ -4,35 +4,33 @@ import React, { useCallback } from 'react';
|
||||
import { useMdPlateEditorState } from '@staticcms/markdown/plate/plateTypes';
|
||||
import ToolbarButton from './ToolbarButton';
|
||||
|
||||
import type { MdEditor } from '@staticcms/markdown';
|
||||
import type { FC } from 'react';
|
||||
import type { ToolbarButtonProps } from './ToolbarButton';
|
||||
|
||||
export interface BlockToolbarButtonProps extends Omit<ToolbarButtonProps, 'active' | 'onClick'> {
|
||||
type: string;
|
||||
inactiveType?: string;
|
||||
onClick?: (editor: MdEditor) => void;
|
||||
variant: 'button' | 'menu';
|
||||
}
|
||||
|
||||
const BlockToolbarButton: FC<BlockToolbarButtonProps> = ({
|
||||
type,
|
||||
inactiveType,
|
||||
onClick,
|
||||
icon,
|
||||
...props
|
||||
}) => {
|
||||
const editor = useMdPlateEditorState();
|
||||
|
||||
const handleOnClick = useCallback(
|
||||
(editor: MdEditor) => {
|
||||
toggleNodeType(editor, { activeType: type, inactiveType });
|
||||
},
|
||||
[inactiveType, type],
|
||||
);
|
||||
const handleOnClick = useCallback(() => {
|
||||
toggleNodeType(editor, { activeType: type, inactiveType });
|
||||
}, [editor, inactiveType, type]);
|
||||
|
||||
return (
|
||||
<ToolbarButton
|
||||
key={type}
|
||||
active={!!editor?.selection && someNode(editor, { match: { type } })}
|
||||
onClick={onClick ?? handleOnClick}
|
||||
onClick={handleOnClick}
|
||||
icon={icon}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
|
@ -4,30 +4,32 @@ import React, { useCallback } from 'react';
|
||||
import { useMdPlateEditorState } from '@staticcms/markdown/plate/plateTypes';
|
||||
import ToolbarButton from './ToolbarButton';
|
||||
|
||||
import type { MdEditor } from '@staticcms/markdown';
|
||||
import type { FC } from 'react';
|
||||
import type { ToolbarButtonProps } from './ToolbarButton';
|
||||
|
||||
export interface ListToolbarButtonProps extends Omit<ToolbarButtonProps, 'active' | 'onClick'> {
|
||||
type: string;
|
||||
variant: 'button' | 'menu';
|
||||
}
|
||||
|
||||
const ListToolbarButton: FC<ListToolbarButtonProps> = ({ type, ...props }) => {
|
||||
const ListToolbarButton: FC<ListToolbarButtonProps> = ({ type, icon, ...props }) => {
|
||||
const editor = useMdPlateEditorState();
|
||||
|
||||
const handleOnClick = useCallback(
|
||||
(editor: MdEditor) => {
|
||||
toggleList(editor, {
|
||||
type,
|
||||
});
|
||||
},
|
||||
[type],
|
||||
);
|
||||
const handleOnClick = useCallback(() => {
|
||||
toggleList(editor, {
|
||||
type,
|
||||
});
|
||||
}, [editor, type]);
|
||||
|
||||
const res = !!editor?.selection && getListItemEntry(editor);
|
||||
|
||||
return (
|
||||
<ToolbarButton active={!!res && res.list[0].type === type} onClick={handleOnClick} {...props} />
|
||||
<ToolbarButton
|
||||
active={!!res && res.list[0].type === type}
|
||||
onClick={handleOnClick}
|
||||
icon={icon}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -4,29 +4,29 @@ import React, { useCallback } from 'react';
|
||||
import { useMdPlateEditorState } from '@staticcms/markdown/plate/plateTypes';
|
||||
import ToolbarButton from './ToolbarButton';
|
||||
|
||||
import type { MdEditor } from '@staticcms/markdown';
|
||||
import type { FC } from 'react';
|
||||
import type { ToolbarButtonProps } from './ToolbarButton';
|
||||
|
||||
export interface MarkToolbarButtonProps extends Omit<ToolbarButtonProps, 'active' | 'onClick'> {
|
||||
export interface MarkToolbarButtonProps
|
||||
extends Omit<ToolbarButtonProps, 'active' | 'onClick' | 'icon'> {
|
||||
type: string;
|
||||
clear?: string | string[];
|
||||
variant: 'button' | 'menu';
|
||||
icon: FC<{ className?: string }>;
|
||||
}
|
||||
|
||||
const MarkToolbarButton: FC<MarkToolbarButtonProps> = ({ type, clear, ...props }) => {
|
||||
const MarkToolbarButton: FC<MarkToolbarButtonProps> = ({ type, clear, icon, ...props }) => {
|
||||
const editor = useMdPlateEditorState();
|
||||
|
||||
const handleOnClick = useCallback(
|
||||
(editor: MdEditor) => {
|
||||
toggleMark(editor, { key: type, clear });
|
||||
},
|
||||
[clear, type],
|
||||
);
|
||||
const handleOnClick = useCallback(() => {
|
||||
toggleMark(editor, { key: type, clear });
|
||||
}, [clear, editor, type]);
|
||||
|
||||
return (
|
||||
<ToolbarButton
|
||||
active={!!editor?.selection && isMarkActive(editor, type)}
|
||||
onClick={handleOnClick}
|
||||
icon={icon}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
|
@ -2,44 +2,46 @@ import { focusEditor } from '@udecode/plate';
|
||||
import React, { useCallback } from 'react';
|
||||
|
||||
import Button from '@staticcms/core/components/common/button/Button';
|
||||
import MenuItemButton from '@staticcms/core/components/common/menu/MenuItemButton';
|
||||
import classNames from '@staticcms/core/lib/util/classNames.util';
|
||||
import { useMdPlateEditorState } from '@staticcms/markdown/plate/plateTypes';
|
||||
|
||||
import type { MdEditor } from '@staticcms/markdown';
|
||||
import type { CSSProperties, FC, MouseEvent, ReactNode } from 'react';
|
||||
import type { CSSProperties, FC, MouseEvent } from 'react';
|
||||
|
||||
export interface ToolbarButtonProps {
|
||||
label?: string;
|
||||
tooltip: string;
|
||||
active?: boolean;
|
||||
activeColor?: string;
|
||||
icon: ReactNode;
|
||||
icon: FC<{ className?: string }>;
|
||||
disableFocusAfterClick?: boolean;
|
||||
disabled: boolean;
|
||||
onClick: (editor: MdEditor, event: MouseEvent<HTMLButtonElement>) => void;
|
||||
variant: 'button' | 'menu';
|
||||
onClick: (event: MouseEvent) => void;
|
||||
}
|
||||
|
||||
const ToolbarButton: FC<ToolbarButtonProps> = ({
|
||||
icon,
|
||||
icon: Icon,
|
||||
tooltip,
|
||||
label,
|
||||
active = false,
|
||||
activeColor,
|
||||
disableFocusAfterClick = false,
|
||||
disabled,
|
||||
variant,
|
||||
onClick,
|
||||
}) => {
|
||||
const editor = useMdPlateEditorState();
|
||||
|
||||
const handleOnClick = useCallback(
|
||||
(event: MouseEvent<HTMLButtonElement>) => {
|
||||
(event: MouseEvent) => {
|
||||
event.preventDefault();
|
||||
|
||||
if (!editor) {
|
||||
return;
|
||||
}
|
||||
|
||||
onClick(editor, event);
|
||||
onClick(event);
|
||||
|
||||
if (!disableFocusAfterClick) {
|
||||
setTimeout(() => {
|
||||
@ -55,8 +57,17 @@ const ToolbarButton: FC<ToolbarButtonProps> = ({
|
||||
style.color = activeColor;
|
||||
}
|
||||
|
||||
if (variant === 'menu') {
|
||||
return (
|
||||
<MenuItemButton key="menu-item" onClick={handleOnClick} startIcon={Icon}>
|
||||
{label ?? tooltip}
|
||||
</MenuItemButton>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Button
|
||||
key="button"
|
||||
aria-label={label ?? tooltip}
|
||||
variant="text"
|
||||
data-testid={`toolbar-button-${label ?? tooltip}`.replace(' ', '-').toLowerCase()}
|
||||
@ -78,7 +89,7 @@ const ToolbarButton: FC<ToolbarButtonProps> = ({
|
||||
style={style}
|
||||
disabled={disabled}
|
||||
>
|
||||
{icon}
|
||||
{<Icon className="w-5 h-5" />}
|
||||
</Button>
|
||||
);
|
||||
};
|
||||
|
@ -5,8 +5,6 @@ export { default as BlockToolbarButton } from './BlockToolbarButton';
|
||||
export * from './ColorPickerToolbarDropdown';
|
||||
export { default as ColorPickerToolbarDropdown } from './ColorPickerToolbarDropdown';
|
||||
export * from './dropdown';
|
||||
export { default as ImageToolbarButton } from './ImageToolbarButton';
|
||||
export { default as LinkToolbarButton } from './LinkToolbarButton';
|
||||
export * from './ListToolbarButton';
|
||||
export { default as ListToolbarButton } from './ListToolbarButton';
|
||||
export * from './MarkToolbarButton';
|
||||
|
@ -1,14 +1,32 @@
|
||||
export { default as AlignToolbarButtons } from './AlignToolbarButtons';
|
||||
export * from './BasicElementToolbarButtons';
|
||||
export { default as BasicElementToolbarButtons } from './BasicElementToolbarButtons';
|
||||
export * from './BasicMarkToolbarButtons';
|
||||
export { default as BasicMarkToolbarButtons } from './BasicMarkToolbarButtons';
|
||||
export { default as ColorToolbarButtons } from './ColorToolbarButtons';
|
||||
export * from './common';
|
||||
export * from './BoldToolbarButton';
|
||||
export { default as BoldToolbarButton } from './BoldToolbarButton';
|
||||
export * from './DecreaseIndentToolbarButton';
|
||||
export { default as IncreaseIndentToolbarButton } from './DecreaseIndentToolbarButton';
|
||||
export * from './DeleteColumnToolbarButton';
|
||||
export { default as DeleteColumnToolbarButton } from './DeleteColumnToolbarButton';
|
||||
export * from './DeleteRowToolbarButton';
|
||||
export { default as DeleteRowToolbarButton } from './DeleteRowToolbarButton';
|
||||
export * from './DeleteTableToolbarButton';
|
||||
export { default as DeleteTableToolbarButton } from './DeleteTableToolbarButton';
|
||||
export * from './FontTypeSelect';
|
||||
export { default as FontTypeSelect } from './FontTypeSelect';
|
||||
export { default as ListToolbarButtons } from './ListToolbarButtons';
|
||||
export * from './MediaToolbarButtons';
|
||||
export { default as MediaToolbarButtons } from './MediaToolbarButtons';
|
||||
export * from './TableToolbarButtons';
|
||||
export { default as TableToolbarButtons } from './TableToolbarButtons';
|
||||
export * from './InsertImageToolbarButton';
|
||||
export { default as ImageToolbarButton } from './InsertImageToolbarButton';
|
||||
export * from './IncreaseIndentToolbarButton';
|
||||
export { default as DecreaseIndentToolbarButton } from './IncreaseIndentToolbarButton';
|
||||
export * from './InsertColumnToolbarButton';
|
||||
export { default as InsertColumnToolbarButton } from './InsertColumnToolbarButton';
|
||||
export * from './InsertRowToolbarButton';
|
||||
export { default as InsertRowToolbarButton } from './InsertRowToolbarButton';
|
||||
export * from './ItalicToolbarButton';
|
||||
export { default as ItalicToolbarButton } from './ItalicToolbarButton';
|
||||
export * from './InsertLinkToolbarButton';
|
||||
export { default as LinkToolbarButton } from './InsertLinkToolbarButton';
|
||||
export * from './OrderedListToolbarButton';
|
||||
export { default as OrderedListToolbarButton } from './OrderedListToolbarButton';
|
||||
export * from './ShortcodeToolbarButton';
|
||||
export { default as ShortcodeToolbarButton } from './ShortcodeToolbarButton';
|
||||
export * from './StrikethroughToolbarButton';
|
||||
export { default as StrikethroughToolbarButton } from './StrikethroughToolbarButton';
|
||||
export * from './UnorderedListToolbarButton';
|
||||
export { default as UnorderedListToolbarButton } from './UnorderedListToolbarButton';
|
||||
|
@ -1,16 +1,54 @@
|
||||
import React from 'react';
|
||||
|
||||
import AddButtons from '../buttons/AddButtons';
|
||||
import AlignToolbarButtons from '../buttons/AlignToolbarButtons';
|
||||
import BasicElementToolbarButtons from '../buttons/BasicElementToolbarButtons';
|
||||
import BasicMarkToolbarButtons from '../buttons/BasicMarkToolbarButtons';
|
||||
import ColorToolbarButtons from '../buttons/ColorToolbarButtons';
|
||||
import ListToolbarButtons from '../buttons/ListToolbarButtons';
|
||||
import ShortcodeToolbarButton from '../buttons/ShortcodeToolbarButton';
|
||||
import useToolbarButtons from '../../hooks/useToolbarButtons';
|
||||
import {
|
||||
BOLD_TOOLBAR_BUTTON,
|
||||
ITALIC_TOOLBAR_BUTTON,
|
||||
STRIKETHROUGH_TOOLBAR_BUTTON,
|
||||
CODE_TOOLBAR_BUTTON,
|
||||
FONT_TOOLBAR_BUTTON,
|
||||
IMAGE_TOOLBAR_BUTTON,
|
||||
FILE_LINK_TOOLBAR_BUTTON,
|
||||
INSERT_TABLE_TOOLBAR_BUTTON,
|
||||
BLOCKQUOTE_TOOLBAR_BUTTON,
|
||||
SHORTCODE_TOOLBAR_BUTTON,
|
||||
INCRASE_IDENT_TOOLBAR_BUTTON,
|
||||
DECREASE_IDENT_TOOLBAR_BUTTON,
|
||||
ORDERED_LIST_TOOLBAR_BUTTON,
|
||||
UNORDERED_LIST_TOOLBAR_BUTTON,
|
||||
CODE_BLOCK_TOOLBAR_BUTTON,
|
||||
} from '@staticcms/core/constants/toolbar_buttons';
|
||||
|
||||
import type { Collection, MarkdownField } from '@staticcms/core/interface';
|
||||
import type { Collection, MarkdownField, MarkdownToolbarItem } from '@staticcms/core/interface';
|
||||
import type { FC } from 'react';
|
||||
|
||||
const DEFAULT_TOOLBAR_BUTTONS: MarkdownToolbarItem[] = [
|
||||
BOLD_TOOLBAR_BUTTON,
|
||||
ITALIC_TOOLBAR_BUTTON,
|
||||
STRIKETHROUGH_TOOLBAR_BUTTON,
|
||||
CODE_TOOLBAR_BUTTON,
|
||||
FONT_TOOLBAR_BUTTON,
|
||||
UNORDERED_LIST_TOOLBAR_BUTTON,
|
||||
ORDERED_LIST_TOOLBAR_BUTTON,
|
||||
DECREASE_IDENT_TOOLBAR_BUTTON,
|
||||
INCRASE_IDENT_TOOLBAR_BUTTON,
|
||||
SHORTCODE_TOOLBAR_BUTTON,
|
||||
{
|
||||
label: 'Insert',
|
||||
groups: [
|
||||
{
|
||||
items: [BLOCKQUOTE_TOOLBAR_BUTTON, CODE_BLOCK_TOOLBAR_BUTTON],
|
||||
},
|
||||
{
|
||||
items: [INSERT_TABLE_TOOLBAR_BUTTON],
|
||||
},
|
||||
{
|
||||
items: [IMAGE_TOOLBAR_BUTTON, FILE_LINK_TOOLBAR_BUTTON],
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
export interface ToolbarProps {
|
||||
useMdx: boolean;
|
||||
collection: Collection<MarkdownField>;
|
||||
@ -18,21 +56,13 @@ export interface ToolbarProps {
|
||||
disabled: boolean;
|
||||
}
|
||||
|
||||
const Toolbar: FC<ToolbarProps> = ({ useMdx, collection, field, disabled }) => {
|
||||
const groups = [
|
||||
<BasicMarkToolbarButtons
|
||||
key="basic-mark-buttons"
|
||||
useMdx={useMdx}
|
||||
extended
|
||||
disabled={disabled}
|
||||
/>,
|
||||
<BasicElementToolbarButtons key="basic-element-buttons" disabled={disabled} />,
|
||||
<ListToolbarButtons key="list-buttons" disabled={disabled} />,
|
||||
useMdx ? <ColorToolbarButtons key="color-buttons" disabled={disabled} /> : null,
|
||||
useMdx ? <AlignToolbarButtons key="align-mark-buttons" disabled={disabled} /> : null,
|
||||
!useMdx ? <ShortcodeToolbarButton key="shortcode-button" disabled={disabled} /> : null,
|
||||
<AddButtons key="add-buttons" collection={collection} field={field} disabled={disabled} />,
|
||||
].filter(Boolean);
|
||||
const Toolbar: FC<ToolbarProps> = ({ collection, field, disabled }) => {
|
||||
const buttons = useToolbarButtons(
|
||||
field.toolbar_buttons?.main ?? DEFAULT_TOOLBAR_BUTTONS,
|
||||
collection,
|
||||
field,
|
||||
disabled,
|
||||
);
|
||||
|
||||
return (
|
||||
<div
|
||||
@ -59,7 +89,7 @@ const Toolbar: FC<ToolbarProps> = ({ useMdx, collection, field, disabled }) => {
|
||||
z-10
|
||||
"
|
||||
>
|
||||
{groups}
|
||||
{buttons}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
@ -0,0 +1,206 @@
|
||||
import { Add as AddIcon } from '@styled-icons/material/Add';
|
||||
import React, { useMemo } from 'react';
|
||||
|
||||
import Menu from '@staticcms/core/components/common/menu/Menu';
|
||||
import MenuGroup from '@staticcms/core/components/common/menu/MenuGroup';
|
||||
import BlockquoteToolbarButton from '../components/buttons/BlockquoteToolbarButton';
|
||||
import BoldToolbarButton from '../components/buttons/BoldToolbarButton';
|
||||
import CodeBlockToolbarButtons from '../components/buttons/CodeBlockToolbarButtons';
|
||||
import CodeToolbarButton from '../components/buttons/CodeToolbarButton';
|
||||
import IncreaseIndentButton from '../components/buttons/DecreaseIndentToolbarButton';
|
||||
import DeleteColumnToolbarButton from '../components/buttons/DeleteColumnToolbarButton';
|
||||
import DeleteRowToolbarButton from '../components/buttons/DeleteRowToolbarButton';
|
||||
import DeleteTableToolbarButton from '../components/buttons/DeleteTableToolbarButton';
|
||||
import FontTypeSelect from '../components/buttons/FontTypeSelect';
|
||||
import DecreaseIndentButton from '../components/buttons/IncreaseIndentToolbarButton';
|
||||
import InsertColumnToolbarButton from '../components/buttons/InsertColumnToolbarButton';
|
||||
import InsertImageToolbarButton from '../components/buttons/InsertImageToolbarButton';
|
||||
import InsertLinkToolbarButton from '../components/buttons/InsertLinkToolbarButton';
|
||||
import InsertRowToolbarButton from '../components/buttons/InsertRowToolbarButton';
|
||||
import InsertTableToolbarButton from '../components/buttons/InsertTableToolbarButton';
|
||||
import ItalicToolbarButton from '../components/buttons/ItalicToolbarButton';
|
||||
import OrderedListButton from '../components/buttons/OrderedListToolbarButton';
|
||||
import ShortcodeToolbarButton from '../components/buttons/ShortcodeToolbarButton';
|
||||
import StrikethroughToolbarButton from '../components/buttons/StrikethroughToolbarButton';
|
||||
import UnorderedListButton from '../components/buttons/UnorderedListToolbarButton';
|
||||
|
||||
import type {
|
||||
Collection,
|
||||
LowLevelMarkdownToolbarButtonType,
|
||||
MarkdownField,
|
||||
MarkdownToolbarButtonType,
|
||||
MarkdownToolbarItem,
|
||||
} from '@staticcms/core/interface';
|
||||
import type { ReactNode } from 'react';
|
||||
|
||||
export default function useToolbarButtons(
|
||||
toolbarButtons: MarkdownToolbarItem[],
|
||||
collection: Collection<MarkdownField>,
|
||||
field: MarkdownField,
|
||||
disabled: boolean,
|
||||
): ReactNode[] {
|
||||
return useMemo(
|
||||
() => getToolbarButtons(toolbarButtons, collection, field, disabled),
|
||||
[collection, disabled, field, toolbarButtons],
|
||||
);
|
||||
}
|
||||
|
||||
export function getToolbarButtons(
|
||||
toolbarButtons: MarkdownToolbarItem[] | MarkdownToolbarButtonType[],
|
||||
collection: Collection<MarkdownField>,
|
||||
field: MarkdownField,
|
||||
disabled: boolean,
|
||||
): ReactNode[] {
|
||||
return toolbarButtons.map(button => {
|
||||
if (typeof button === 'string') {
|
||||
return getToolbarButton(button, collection, field, disabled, 'button');
|
||||
}
|
||||
|
||||
return (
|
||||
<Menu
|
||||
key={`menu-${button.label}`}
|
||||
label={<AddIcon className="h-5 w-5" aria-hidden="true" />}
|
||||
data-testid={`toolbar-menu-${button.label.toLowerCase().replace(' ', '-')}`}
|
||||
keepMounted
|
||||
hideDropdownIcon
|
||||
variant="text"
|
||||
className="
|
||||
py-0.5
|
||||
px-0.5
|
||||
h-6
|
||||
w-6
|
||||
"
|
||||
disabled={disabled}
|
||||
>
|
||||
{button.groups.map((group, index) => {
|
||||
if (group.items.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<MenuGroup key={`group-${index}`}>
|
||||
{group.items.map(item => getToolbarButton(item, collection, field, disabled, 'menu'))}
|
||||
</MenuGroup>
|
||||
);
|
||||
})}
|
||||
</Menu>
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
function getToolbarButton(
|
||||
name: MarkdownToolbarButtonType,
|
||||
collection: Collection<MarkdownField>,
|
||||
field: MarkdownField,
|
||||
disabled: boolean,
|
||||
variant: 'button',
|
||||
): ReactNode;
|
||||
function getToolbarButton(
|
||||
name: LowLevelMarkdownToolbarButtonType,
|
||||
collection: Collection<MarkdownField>,
|
||||
field: MarkdownField,
|
||||
disabled: boolean,
|
||||
variant: 'menu',
|
||||
): ReactNode;
|
||||
function getToolbarButton(
|
||||
name: MarkdownToolbarButtonType | LowLevelMarkdownToolbarButtonType,
|
||||
collection: Collection<MarkdownField>,
|
||||
field: MarkdownField,
|
||||
disabled: boolean,
|
||||
variant: 'button' | 'menu',
|
||||
): ReactNode {
|
||||
switch (name) {
|
||||
case 'blockquote':
|
||||
return <BlockquoteToolbarButton key="bold" disabled={disabled} variant={variant} />;
|
||||
|
||||
case 'bold':
|
||||
return <BoldToolbarButton key="bold" disabled={disabled} variant={variant} />;
|
||||
|
||||
case 'code':
|
||||
return <CodeToolbarButton key="code" disabled={disabled} variant={variant} />;
|
||||
|
||||
case 'code-block':
|
||||
return <CodeBlockToolbarButtons key="code" disabled={disabled} variant={variant} />;
|
||||
|
||||
case 'decrease-indent':
|
||||
return <DecreaseIndentButton key="decrease-indent" disabled={disabled} variant={variant} />;
|
||||
|
||||
case 'delete-column':
|
||||
return (
|
||||
<DeleteColumnToolbarButton key="delete-column" disabled={disabled} variant={variant} />
|
||||
);
|
||||
|
||||
case 'delete-row':
|
||||
return <DeleteRowToolbarButton key="delete-row" disabled={disabled} variant={variant} />;
|
||||
|
||||
case 'delete-table':
|
||||
return <DeleteTableToolbarButton key="delete-table" disabled={disabled} variant={variant} />;
|
||||
|
||||
case 'font':
|
||||
if (variant === 'menu') {
|
||||
return null;
|
||||
}
|
||||
|
||||
return <FontTypeSelect key="font" disabled={disabled} />;
|
||||
|
||||
case 'increase-indent':
|
||||
return <IncreaseIndentButton key="increase-indent" disabled={disabled} variant={variant} />;
|
||||
|
||||
case 'insert-column':
|
||||
return (
|
||||
<InsertColumnToolbarButton key="insert-column" disabled={disabled} variant={variant} />
|
||||
);
|
||||
|
||||
case 'image':
|
||||
return (
|
||||
<InsertImageToolbarButton
|
||||
key="image"
|
||||
disabled={disabled}
|
||||
variant={variant}
|
||||
collection={collection}
|
||||
field={field}
|
||||
/>
|
||||
);
|
||||
|
||||
case 'file-link':
|
||||
return (
|
||||
<InsertLinkToolbarButton
|
||||
key="file-link"
|
||||
disabled={disabled}
|
||||
variant={variant}
|
||||
collection={collection}
|
||||
field={field}
|
||||
/>
|
||||
);
|
||||
|
||||
case 'insert-row':
|
||||
return <InsertRowToolbarButton key="insert-row" disabled={disabled} variant={variant} />;
|
||||
|
||||
case 'insert-table':
|
||||
return <InsertTableToolbarButton key="insert-table" disabled={disabled} variant={variant} />;
|
||||
|
||||
case 'italic':
|
||||
return <ItalicToolbarButton key="italic" disabled={disabled} variant={variant} />;
|
||||
|
||||
case 'ordered-list':
|
||||
return <OrderedListButton key="ordered-list" disabled={disabled} variant={variant} />;
|
||||
|
||||
case 'shortcode':
|
||||
if (variant === 'menu') {
|
||||
return null;
|
||||
}
|
||||
|
||||
return <ShortcodeToolbarButton key="shortcode" disabled={disabled} />;
|
||||
|
||||
case 'strikethrough':
|
||||
return (
|
||||
<StrikethroughToolbarButton key="strikethrough" disabled={disabled} variant={variant} />
|
||||
);
|
||||
|
||||
case 'unordered-list':
|
||||
return <UnorderedListButton key="unordered-list" disabled={disabled} variant={variant} />;
|
||||
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
@ -1,3 +1,104 @@
|
||||
import {
|
||||
FONT_TOOLBAR_BUTTON,
|
||||
SHORTCODE_TOOLBAR_BUTTON,
|
||||
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,
|
||||
INCRASE_IDENT_TOOLBAR_BUTTON,
|
||||
INSERT_COLUMN_TOOLBAR_BUTTON,
|
||||
IMAGE_TOOLBAR_BUTTON,
|
||||
FILE_LINK_TOOLBAR_BUTTON,
|
||||
INSERT_ROW_TOOLBAR_BUTTON,
|
||||
INSERT_TABLE_TOOLBAR_BUTTON,
|
||||
ITALIC_TOOLBAR_BUTTON,
|
||||
ORDERED_LIST_TOOLBAR_BUTTON,
|
||||
STRIKETHROUGH_TOOLBAR_BUTTON,
|
||||
UNORDERED_LIST_TOOLBAR_BUTTON,
|
||||
} from '@staticcms/core/constants/toolbar_buttons';
|
||||
|
||||
const LowLevelButtonsArrayField = {
|
||||
type: 'array',
|
||||
items: {
|
||||
type: 'string',
|
||||
enum: [
|
||||
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,
|
||||
INCRASE_IDENT_TOOLBAR_BUTTON,
|
||||
INSERT_COLUMN_TOOLBAR_BUTTON,
|
||||
IMAGE_TOOLBAR_BUTTON,
|
||||
FILE_LINK_TOOLBAR_BUTTON,
|
||||
INSERT_ROW_TOOLBAR_BUTTON,
|
||||
INSERT_TABLE_TOOLBAR_BUTTON,
|
||||
ITALIC_TOOLBAR_BUTTON,
|
||||
ORDERED_LIST_TOOLBAR_BUTTON,
|
||||
STRIKETHROUGH_TOOLBAR_BUTTON,
|
||||
UNORDERED_LIST_TOOLBAR_BUTTON,
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
const MarkdownToolbarItemField = {
|
||||
type: 'array',
|
||||
items: {
|
||||
anyOf: [
|
||||
{
|
||||
type: 'string',
|
||||
enum: [
|
||||
FONT_TOOLBAR_BUTTON,
|
||||
SHORTCODE_TOOLBAR_BUTTON,
|
||||
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,
|
||||
INCRASE_IDENT_TOOLBAR_BUTTON,
|
||||
INSERT_COLUMN_TOOLBAR_BUTTON,
|
||||
IMAGE_TOOLBAR_BUTTON,
|
||||
FILE_LINK_TOOLBAR_BUTTON,
|
||||
INSERT_ROW_TOOLBAR_BUTTON,
|
||||
INSERT_TABLE_TOOLBAR_BUTTON,
|
||||
ITALIC_TOOLBAR_BUTTON,
|
||||
ORDERED_LIST_TOOLBAR_BUTTON,
|
||||
STRIKETHROUGH_TOOLBAR_BUTTON,
|
||||
UNORDERED_LIST_TOOLBAR_BUTTON,
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'object',
|
||||
properties: {
|
||||
label: { type: 'string' },
|
||||
icon: { type: 'string' },
|
||||
groups: {
|
||||
type: 'array',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
items: LowLevelButtonsArrayField,
|
||||
},
|
||||
required: ['items'],
|
||||
},
|
||||
},
|
||||
},
|
||||
required: ['label', 'groups'],
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
export default {
|
||||
properties: {
|
||||
default: { type: 'string' },
|
||||
@ -5,6 +106,16 @@ export default {
|
||||
public_folder: { type: 'string' },
|
||||
choose_url: { type: 'boolean' },
|
||||
multiple: { type: 'boolean' },
|
||||
toolbar_buttons: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
main: MarkdownToolbarItemField,
|
||||
empty: MarkdownToolbarItemField,
|
||||
selection: MarkdownToolbarItemField,
|
||||
table_empty: MarkdownToolbarItemField,
|
||||
table_select: MarkdownToolbarItemField,
|
||||
},
|
||||
},
|
||||
media_library: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
|
@ -16,13 +16,14 @@ _Please note:_ If you want to use your markdown editor to fill a markdown file c
|
||||
|
||||
For common options, see [Common widget options](/docs/widgets#common-widget-options).
|
||||
|
||||
| Name | Type | Default | Description |
|
||||
| ------------- | --------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| default | string | `''` | _Optional_. The default value for the field. Accepts markdown content |
|
||||
| media_folder | string | | _Optional_. Specifies the folder path where uploaded files should be saved, relative to the base of the repo |
|
||||
| public_folder | string | | _Optional_. Specifies the folder path where the files uploaded by the media library will be accessed, relative to the base of the built site |
|
||||
| media_library | Media Library Options | `{}` | _Optional_. Media library settings to apply when the media library is opened by the current widget. See [Media Library](/docs/configuration-options#media-library) |
|
||||
| choose_url | boolean | `true` | _Optional_. When set to `false`, the "Insert from URL" button will be hidden |
|
||||
| Name | Type | Default | Description |
|
||||
| --------------- | --------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| default | string | `''` | _Optional_. The default value for the field. Accepts markdown content |
|
||||
| media_folder | string | | _Optional_. Specifies the folder path where uploaded files should be saved, relative to the base of the repo |
|
||||
| public_folder | string | | _Optional_. Specifies the folder path where the files uploaded by the media library will be accessed, relative to the base of the built site |
|
||||
| media_library | Media Library Options | `{}` | _Optional_. Media library settings to apply when the media library is opened by the current widget. See [Media Library](/docs/configuration-options#media-library) |
|
||||
| choose_url | boolean | `true` | _Optional_. When set to `false`, the "Insert from URL" button will be hidden |
|
||||
| toolbar_buttons | object | [] | _Optional_. Specifies which toolbar items to show for the markdown widget. See [Toolbar Customization](#toolbar-customization) |
|
||||
|
||||
## Example
|
||||
|
||||
@ -47,6 +48,234 @@ This would render as:
|
||||
|
||||
_Please note:_ The markdown widget outputs a raw markdown string. Your static site generator may or may not render the markdown to HTML automatically. Consult with your static site generator's documentation for more information about rendering markdown.
|
||||
|
||||
## Toolbar Customization
|
||||
|
||||
| Name | Type | Default | Description |
|
||||
| --------------- | ------ | --------------------------------------------------------- | --------------------------------------------------------------------------------------------------------- |
|
||||
| main | object | See [Main Toolbar](#main-toolbar) | _Optional_. A list of buttons and menus for the top level toolbar |
|
||||
| empty | object | See [Empty Toolbar](#empty-toolbar) | _Optional_. A list of buttons and menus for the popup toolbar when in an empty paragraph |
|
||||
| selection | object | See [Selection Toolbar](#selection-toolbar) | _Optional_. A list of buttons and menus for the popup toolbar when selecting text outside of a table cell |
|
||||
| table_empty | object | See [Empty Table Cell Toolbar](#empty-table-cell-toolbar) | _Optional_. A list of buttons and menus for the popup toolbar when in an empty table cell |
|
||||
| table_selection | object | See [Table Selection Toolbar](#table-selection-toolbar) | _Optional_. A list of buttons and menus for the popup toolbar when selecting text in a table cell |
|
||||
|
||||
### Options
|
||||
|
||||
All toolbars can be customized with a list consisting of buttons and dropdown menus.
|
||||
|
||||
#### Buttons
|
||||
|
||||
Buttons can be configured simply with their name. The following options are available:
|
||||
|
||||
- `blockquote`
|
||||
- `bold`
|
||||
- `code`
|
||||
- `code-block`
|
||||
- `decrease-indent`
|
||||
- `delete-column`
|
||||
- `delete-row`
|
||||
- `delete-table`
|
||||
- `file-link`
|
||||
- `font`
|
||||
- `image`
|
||||
- `increase-indent`
|
||||
- `insert-column`
|
||||
- `insert-row`
|
||||
- `insert-table`
|
||||
- `italic`
|
||||
- `ordered-list`
|
||||
- `shortcode`
|
||||
- `strikethrough`
|
||||
- `unordered-list`
|
||||
|
||||
#### Menus
|
||||
|
||||
The following options are available for menus:
|
||||
|
||||
| Name | Type | Description |
|
||||
| ------ | -------------- | ----------------------------------------------------------------------------------------- |
|
||||
| label | string | The name and tooltip label for the menu |
|
||||
| icon | string | _Optional_. The icon to use for the menu. If not supplied a default add icon will be used |
|
||||
| groups | list of groups | A list groups of menu items. Each group is separated by a divider |
|
||||
|
||||
##### Menu Groups
|
||||
|
||||
The following options are available for menu groups:
|
||||
|
||||
| Name | Type | Description |
|
||||
| ----- | --------------- | -------------------------------------------------------------------------------------------------------------------------- |
|
||||
| items | list of strings | The name of the toolbar buttons in the group. All [buttons](#buttons) are available in menus except `font` and `shortcode` |
|
||||
|
||||
### Default Values
|
||||
|
||||
Below are the default values for the various toolbars:
|
||||
|
||||
#### Main Toolbar
|
||||
|
||||
<CodeTabs>
|
||||
|
||||
```yaml
|
||||
main:
|
||||
- bold
|
||||
- italic
|
||||
- strikethrough
|
||||
- code
|
||||
- font
|
||||
- unordered-list
|
||||
- ordered-list
|
||||
- decrease-indent
|
||||
- increase-indent
|
||||
- shortcode
|
||||
- label: Insert
|
||||
groups:
|
||||
- items:
|
||||
- blockquote
|
||||
- code-block
|
||||
- items:
|
||||
- insert-table
|
||||
- items:
|
||||
- image
|
||||
- file-link
|
||||
```
|
||||
|
||||
```js
|
||||
main: [
|
||||
'bold',
|
||||
'italic',
|
||||
'strikethrough',
|
||||
'code',
|
||||
'font',
|
||||
'unordered-list',
|
||||
'ordered-list',
|
||||
'decrease-indent',
|
||||
'increase-indent',
|
||||
'shortcode',
|
||||
{
|
||||
label: 'Insert',
|
||||
groups: [
|
||||
{
|
||||
items: ['blockquote', 'code-block'],
|
||||
},
|
||||
{
|
||||
items: ['insert-table'],
|
||||
},
|
||||
{
|
||||
items: ['image', 'file-link'],
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
```
|
||||
|
||||
</CodeTabs>
|
||||
|
||||
#### Empty Toolbar
|
||||
|
||||
<CodeTabs>
|
||||
|
||||
```yaml
|
||||
empty: []
|
||||
```
|
||||
|
||||
```js
|
||||
empty: [];
|
||||
```
|
||||
|
||||
</CodeTabs>
|
||||
|
||||
#### Selection Toolbar
|
||||
|
||||
<CodeTabs>
|
||||
|
||||
```yaml
|
||||
selection:
|
||||
- bold
|
||||
- italic
|
||||
- strikethrough
|
||||
- code
|
||||
- font
|
||||
- file-link
|
||||
```
|
||||
|
||||
```js
|
||||
selection: ['bold', 'italic', 'strikethrough', 'code', 'font', 'file-link'];
|
||||
```
|
||||
|
||||
</CodeTabs>
|
||||
|
||||
#### Empty Table Cell Toolbar
|
||||
|
||||
<CodeTabs>
|
||||
|
||||
```yaml
|
||||
table_empty:
|
||||
- bold
|
||||
- italic
|
||||
- strikethrough
|
||||
- code
|
||||
- insert-row
|
||||
- delete-row
|
||||
- insert-column
|
||||
- delete-column
|
||||
- delete-table
|
||||
- file-link
|
||||
- image
|
||||
- shortcode
|
||||
```
|
||||
|
||||
```js
|
||||
table_empty: [
|
||||
'bold',
|
||||
'italic',
|
||||
'strikethrough',
|
||||
'code',
|
||||
'insert-row',
|
||||
'delete-row',
|
||||
'insert-column',
|
||||
'delete-column',
|
||||
'delete-table',
|
||||
'file-link',
|
||||
'image',
|
||||
'shortcode',
|
||||
];
|
||||
```
|
||||
|
||||
</CodeTabs>
|
||||
|
||||
#### Table Selection Toolbar
|
||||
|
||||
<CodeTabs>
|
||||
|
||||
```yaml
|
||||
table_selection:
|
||||
- bold
|
||||
- italic
|
||||
- strikethrough
|
||||
- code
|
||||
- insert-row
|
||||
- delete-row
|
||||
- insert-column
|
||||
- delete-column
|
||||
- delete-table
|
||||
- file-link
|
||||
```
|
||||
|
||||
```js
|
||||
table_selection: [
|
||||
'bold',
|
||||
'italic',
|
||||
'strikethrough',
|
||||
'code',
|
||||
'insert-row',
|
||||
'delete-row',
|
||||
'insert-column',
|
||||
'delete-column',
|
||||
'delete-table',
|
||||
'file-link',
|
||||
];
|
||||
```
|
||||
|
||||
</CodeTabs>
|
||||
|
||||
## Shortcodes
|
||||
|
||||
Shortcodes can be added to customize the Markdown editor via `registerShortcode`.
|
||||
|
@ -36,6 +36,7 @@ const Anchor = ({ href = '', children = '' }: AnchorProps) => {
|
||||
document.querySelector(href)?.scrollIntoView({
|
||||
behavior: 'smooth',
|
||||
});
|
||||
history.pushState(null, '', href);
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
|
@ -39,6 +39,7 @@ const Header3 = ({ variant, children = '' }: Header3Props) => {
|
||||
const hasText = useMemo(() => isNotEmpty(textContent), [textContent]);
|
||||
return (
|
||||
<Typography
|
||||
id={anchor}
|
||||
variant={variant}
|
||||
component={variant}
|
||||
sx={{
|
||||
|
@ -8,7 +8,7 @@ const StyledList = styled('ul')(
|
||||
flex-direction: column;
|
||||
list-style-type: none;
|
||||
padding: 0;
|
||||
|
||||
|
||||
${theme.breakpoints.down('lg')} {
|
||||
margin-top: 0;
|
||||
}
|
||||
@ -64,6 +64,7 @@ const DocsHeadings = ({ headings, activeId }: DocsHeadingsProps) => (
|
||||
document.querySelector(`#${heading.id}`)?.scrollIntoView({
|
||||
behavior: 'smooth',
|
||||
});
|
||||
history.pushState(null, '', `#${heading.id}`);
|
||||
}}
|
||||
>
|
||||
{heading.title}
|
||||
@ -79,6 +80,7 @@ const DocsHeadings = ({ headings, activeId }: DocsHeadingsProps) => (
|
||||
document.querySelector(`#${child.id}`)?.scrollIntoView({
|
||||
behavior: 'smooth',
|
||||
});
|
||||
history.pushState(null, '', `#${child.id}`);
|
||||
}}
|
||||
>
|
||||
{child.title}
|
||||
|
Loading…
x
Reference in New Issue
Block a user