1
0
mirror of https://github.com/laurent22/joplin.git synced 2025-12-20 23:30:05 +02:00

Mobile: Rich Text Editor: Add button for creating tables (#13645)

This commit is contained in:
Henry Heino
2025-11-17 14:06:42 -08:00
committed by GitHub
parent ad4a8aa76d
commit bd569b9d8d
9 changed files with 93 additions and 15 deletions

View File

@@ -1145,6 +1145,7 @@ packages/editor/ProseMirror/utils/dom/showModal.js
packages/editor/ProseMirror/utils/extractSelectedLinesTo.test.js
packages/editor/ProseMirror/utils/extractSelectedLinesTo.js
packages/editor/ProseMirror/utils/forEachHeading.js
packages/editor/ProseMirror/utils/insertRenderedMarkdown.js
packages/editor/ProseMirror/utils/jumpToHash.js
packages/editor/ProseMirror/utils/makeLinksClickableInElement.js
packages/editor/ProseMirror/utils/postprocessEditorOutput.test.js

1
.gitignore vendored
View File

@@ -1117,6 +1117,7 @@ packages/editor/ProseMirror/utils/dom/showModal.js
packages/editor/ProseMirror/utils/extractSelectedLinesTo.test.js
packages/editor/ProseMirror/utils/extractSelectedLinesTo.js
packages/editor/ProseMirror/utils/forEachHeading.js
packages/editor/ProseMirror/utils/insertRenderedMarkdown.js
packages/editor/ProseMirror/utils/jumpToHash.js
packages/editor/ProseMirror/utils/makeLinksClickableInElement.js
packages/editor/ProseMirror/utils/postprocessEditorOutput.test.js

View File

@@ -20,6 +20,8 @@ const builtInCommandNames = [
EditorCommandType.ToggleBulletedList,
EditorCommandType.ToggleCheckList,
'-',
`editor.${EditorCommandType.InsertTable}`,
'-',
EditorCommandType.IndentLess,
EditorCommandType.IndentMore,
`editor.${EditorCommandType.SwapLineDown}`,

View File

@@ -9,6 +9,12 @@ const markdownEditorOnlyCommands = [
EditorCommandType.SwapLineDown,
].map(command => `editor.${command}`);
const richTextEditorOnlyCommands = [
EditorCommandType.InsertTable,
].map(command => `editor.${command}`);
export const visibleCondition = (commandName: string) => {
const output = [];
@@ -16,6 +22,10 @@ export const visibleCondition = (commandName: string) => {
output.push('!richTextEditorVisible');
}
if (richTextEditorOnlyCommands.includes(commandName)) {
output.push('!markdownEditorPaneVisible');
}
return output.join(' && ');
};
@@ -102,6 +112,11 @@ const declarations: CommandDeclaration[] = [
label: () => _('Task list'),
iconName: 'material format-list-checks',
},
{
name: `editor.${EditorCommandType.InsertTable}`,
label: () => _('Table'),
iconName: 'material table',
},
{
name: EditorCommandType.IndentLess,
label: () => _('Decrease indent level'),

View File

@@ -45,6 +45,15 @@ const editorCommands: Record<EditorCommandType, EditorCommandFunction> = {
[EditorCommandType.ToggleHeading4]: toggleHeaderLevel(4),
[EditorCommandType.ToggleHeading5]: toggleHeaderLevel(5),
[EditorCommandType.InsertHorizontalRule]: insertHorizontalRule,
[EditorCommandType.InsertTable]: editor => {
replaceSelectionCommand(editor, [
'',
'| | |',
'|----|----|',
'| | |',
'',
].join('\n'));
},
[EditorCommandType.ScrollSelectionIntoView]: editor => {
editor.dispatch(editor.state.update({

View File

@@ -77,4 +77,20 @@ describe('ProseMirror/commands', () => {
expect(jumpToHash('test-heading-2')).toBe(true);
expect(editor.state.selection.$anchor.parent.textContent).toBe('Test heading 2');
});
test('textTable should insert a table', () => {
const editor = createTestEditor({ html: '<p></p>' });
commands[EditorCommandType.InsertTable](editor.state, editor.dispatch, editor);
expect(editor.state.doc.toJSON()).toMatchObject({
content: [{
content: [
{ type: 'table_row' },
{ type: 'table_row' },
],
type: 'table',
}],
});
});
});

View File

@@ -12,8 +12,9 @@ import { EditorEventType } from '../../events';
import extractSelectedLinesTo from '../utils/extractSelectedLinesTo';
import { EditorView } from 'prosemirror-view';
import jumpToHash from '../utils/jumpToHash';
import canReplaceSelectionWith from '../utils/canReplaceSelectionWith';
import focusEditor from './focusEditor';
import insertRenderedMarkdown from '../utils/insertRenderedMarkdown';
import canReplaceSelectionWith from '../utils/canReplaceSelectionWith';
type Dispatch = (tr: Transaction)=> void;
type ExtendedCommand = (state: EditorState, dispatch: Dispatch, view?: EditorView, options?: string[])=> boolean;
@@ -74,7 +75,6 @@ const toggleCode: Command = (state, dispatch, view) => {
return toggleMark(schema.marks.code)(state, dispatch, view) || setBlockType(schema.nodes.paragraph)(state, dispatch, view);
};
const listItemTypes = [schema.nodes.list_item, schema.nodes.task_list_item];
const commands: Record<EditorCommandType, ExtendedCommand|null> = {
@@ -86,24 +86,17 @@ const commands: Record<EditorCommandType, ExtendedCommand|null> = {
[EditorCommandType.ToggleItalicized]: toggleMark(schema.marks.emphasis),
[EditorCommandType.ToggleCode]: toggleCode,
[EditorCommandType.ToggleMath]: (state, _dispatch, view) => {
const renderer = getEditorApi(state).renderer;
const selectedText = state.doc.textBetween(state.selection.from, state.selection.to);
const block = selectedText.includes('\n');
const nodeType = block ? schema.nodes.joplinEditableBlock : schema.nodes.joplinEditableInline;
if (canReplaceSelectionWith(state.selection, nodeType)) {
void (async () => {
const separator = block ? '$$' : '$';
const rendered = await renderer.renderMarkupToHtml(`${separator}${selectedText}${separator}`, {
forceMarkdown: true,
isFullPageRender: false,
});
if (view) {
view.pasteHTML(rendered.html);
const separator = block ? '$$' : '$';
void insertRenderedMarkdown(view,
`${separator}${selectedText}${separator}`,
);
}
})();
return true;
}
return false;
@@ -121,6 +114,29 @@ const commands: Record<EditorCommandType, ExtendedCommand|null> = {
[EditorCommandType.ToggleHeading4]: toggleHeading(4),
[EditorCommandType.ToggleHeading5]: toggleHeading(5),
[EditorCommandType.InsertHorizontalRule]: null,
[EditorCommandType.InsertTable]: (state, dispatch, view) => {
if (view) {
// See https://github.com/ProseMirror/prosemirror-tables/issues/91
const tr = state.tr.replaceSelectionWith(
schema.nodes.table.create(null, [
schema.nodes.table_row.create(null, [
schema.nodes.table_header.createAndFill(),
schema.nodes.table_header.createAndFill(),
]),
schema.nodes.table_row.create(null, [
schema.nodes.table_cell.createAndFill(),
schema.nodes.table_cell.createAndFill(),
]),
]),
);
if (dispatch) {
dispatch(tr);
}
}
return true;
},
[EditorCommandType.ToggleSearch]: (state, dispatch, view) => {
const command = setSearchVisible(!getSearchVisible(state));
return command(state, dispatch, view);

View File

@@ -0,0 +1,17 @@
import { EditorView } from 'prosemirror-view';
import { getEditorApi } from '../plugins/joplinEditorApiPlugin';
const insertRenderedMarkdown = async (
view: EditorView,
markdown: string,
) => {
const renderer = getEditorApi(view.state).renderer;
const rendered = await renderer.renderMarkupToHtml(markdown, {
forceMarkdown: true,
isFullPageRender: false,
});
view.pasteHTML(rendered.html);
};
export default insertRenderedMarkdown;

View File

@@ -30,6 +30,7 @@ export enum EditorCommandType {
ToggleHeading5 = 'textHeading5',
InsertHorizontalRule = 'textHorizontalRule',
InsertTable = 'textTable',
// Find commands
ToggleSearch = 'textSearch',