You've already forked joplin
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:
@@ -1145,6 +1145,7 @@ packages/editor/ProseMirror/utils/dom/showModal.js
|
|||||||
packages/editor/ProseMirror/utils/extractSelectedLinesTo.test.js
|
packages/editor/ProseMirror/utils/extractSelectedLinesTo.test.js
|
||||||
packages/editor/ProseMirror/utils/extractSelectedLinesTo.js
|
packages/editor/ProseMirror/utils/extractSelectedLinesTo.js
|
||||||
packages/editor/ProseMirror/utils/forEachHeading.js
|
packages/editor/ProseMirror/utils/forEachHeading.js
|
||||||
|
packages/editor/ProseMirror/utils/insertRenderedMarkdown.js
|
||||||
packages/editor/ProseMirror/utils/jumpToHash.js
|
packages/editor/ProseMirror/utils/jumpToHash.js
|
||||||
packages/editor/ProseMirror/utils/makeLinksClickableInElement.js
|
packages/editor/ProseMirror/utils/makeLinksClickableInElement.js
|
||||||
packages/editor/ProseMirror/utils/postprocessEditorOutput.test.js
|
packages/editor/ProseMirror/utils/postprocessEditorOutput.test.js
|
||||||
|
|||||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1117,6 +1117,7 @@ packages/editor/ProseMirror/utils/dom/showModal.js
|
|||||||
packages/editor/ProseMirror/utils/extractSelectedLinesTo.test.js
|
packages/editor/ProseMirror/utils/extractSelectedLinesTo.test.js
|
||||||
packages/editor/ProseMirror/utils/extractSelectedLinesTo.js
|
packages/editor/ProseMirror/utils/extractSelectedLinesTo.js
|
||||||
packages/editor/ProseMirror/utils/forEachHeading.js
|
packages/editor/ProseMirror/utils/forEachHeading.js
|
||||||
|
packages/editor/ProseMirror/utils/insertRenderedMarkdown.js
|
||||||
packages/editor/ProseMirror/utils/jumpToHash.js
|
packages/editor/ProseMirror/utils/jumpToHash.js
|
||||||
packages/editor/ProseMirror/utils/makeLinksClickableInElement.js
|
packages/editor/ProseMirror/utils/makeLinksClickableInElement.js
|
||||||
packages/editor/ProseMirror/utils/postprocessEditorOutput.test.js
|
packages/editor/ProseMirror/utils/postprocessEditorOutput.test.js
|
||||||
|
|||||||
@@ -20,6 +20,8 @@ const builtInCommandNames = [
|
|||||||
EditorCommandType.ToggleBulletedList,
|
EditorCommandType.ToggleBulletedList,
|
||||||
EditorCommandType.ToggleCheckList,
|
EditorCommandType.ToggleCheckList,
|
||||||
'-',
|
'-',
|
||||||
|
`editor.${EditorCommandType.InsertTable}`,
|
||||||
|
'-',
|
||||||
EditorCommandType.IndentLess,
|
EditorCommandType.IndentLess,
|
||||||
EditorCommandType.IndentMore,
|
EditorCommandType.IndentMore,
|
||||||
`editor.${EditorCommandType.SwapLineDown}`,
|
`editor.${EditorCommandType.SwapLineDown}`,
|
||||||
|
|||||||
@@ -9,6 +9,12 @@ const markdownEditorOnlyCommands = [
|
|||||||
EditorCommandType.SwapLineDown,
|
EditorCommandType.SwapLineDown,
|
||||||
].map(command => `editor.${command}`);
|
].map(command => `editor.${command}`);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const richTextEditorOnlyCommands = [
|
||||||
|
EditorCommandType.InsertTable,
|
||||||
|
].map(command => `editor.${command}`);
|
||||||
|
|
||||||
export const visibleCondition = (commandName: string) => {
|
export const visibleCondition = (commandName: string) => {
|
||||||
const output = [];
|
const output = [];
|
||||||
|
|
||||||
@@ -16,6 +22,10 @@ export const visibleCondition = (commandName: string) => {
|
|||||||
output.push('!richTextEditorVisible');
|
output.push('!richTextEditorVisible');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (richTextEditorOnlyCommands.includes(commandName)) {
|
||||||
|
output.push('!markdownEditorPaneVisible');
|
||||||
|
}
|
||||||
|
|
||||||
return output.join(' && ');
|
return output.join(' && ');
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -102,6 +112,11 @@ const declarations: CommandDeclaration[] = [
|
|||||||
label: () => _('Task list'),
|
label: () => _('Task list'),
|
||||||
iconName: 'material format-list-checks',
|
iconName: 'material format-list-checks',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: `editor.${EditorCommandType.InsertTable}`,
|
||||||
|
label: () => _('Table'),
|
||||||
|
iconName: 'material table',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: EditorCommandType.IndentLess,
|
name: EditorCommandType.IndentLess,
|
||||||
label: () => _('Decrease indent level'),
|
label: () => _('Decrease indent level'),
|
||||||
|
|||||||
@@ -45,6 +45,15 @@ const editorCommands: Record<EditorCommandType, EditorCommandFunction> = {
|
|||||||
[EditorCommandType.ToggleHeading4]: toggleHeaderLevel(4),
|
[EditorCommandType.ToggleHeading4]: toggleHeaderLevel(4),
|
||||||
[EditorCommandType.ToggleHeading5]: toggleHeaderLevel(5),
|
[EditorCommandType.ToggleHeading5]: toggleHeaderLevel(5),
|
||||||
[EditorCommandType.InsertHorizontalRule]: insertHorizontalRule,
|
[EditorCommandType.InsertHorizontalRule]: insertHorizontalRule,
|
||||||
|
[EditorCommandType.InsertTable]: editor => {
|
||||||
|
replaceSelectionCommand(editor, [
|
||||||
|
'',
|
||||||
|
'| | |',
|
||||||
|
'|----|----|',
|
||||||
|
'| | |',
|
||||||
|
'',
|
||||||
|
].join('\n'));
|
||||||
|
},
|
||||||
|
|
||||||
[EditorCommandType.ScrollSelectionIntoView]: editor => {
|
[EditorCommandType.ScrollSelectionIntoView]: editor => {
|
||||||
editor.dispatch(editor.state.update({
|
editor.dispatch(editor.state.update({
|
||||||
|
|||||||
@@ -77,4 +77,20 @@ describe('ProseMirror/commands', () => {
|
|||||||
expect(jumpToHash('test-heading-2')).toBe(true);
|
expect(jumpToHash('test-heading-2')).toBe(true);
|
||||||
expect(editor.state.selection.$anchor.parent.textContent).toBe('Test heading 2');
|
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',
|
||||||
|
}],
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -12,8 +12,9 @@ import { EditorEventType } from '../../events';
|
|||||||
import extractSelectedLinesTo from '../utils/extractSelectedLinesTo';
|
import extractSelectedLinesTo from '../utils/extractSelectedLinesTo';
|
||||||
import { EditorView } from 'prosemirror-view';
|
import { EditorView } from 'prosemirror-view';
|
||||||
import jumpToHash from '../utils/jumpToHash';
|
import jumpToHash from '../utils/jumpToHash';
|
||||||
import canReplaceSelectionWith from '../utils/canReplaceSelectionWith';
|
|
||||||
import focusEditor from './focusEditor';
|
import focusEditor from './focusEditor';
|
||||||
|
import insertRenderedMarkdown from '../utils/insertRenderedMarkdown';
|
||||||
|
import canReplaceSelectionWith from '../utils/canReplaceSelectionWith';
|
||||||
|
|
||||||
type Dispatch = (tr: Transaction)=> void;
|
type Dispatch = (tr: Transaction)=> void;
|
||||||
type ExtendedCommand = (state: EditorState, dispatch: Dispatch, view?: EditorView, options?: string[])=> boolean;
|
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);
|
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 listItemTypes = [schema.nodes.list_item, schema.nodes.task_list_item];
|
||||||
|
|
||||||
const commands: Record<EditorCommandType, ExtendedCommand|null> = {
|
const commands: Record<EditorCommandType, ExtendedCommand|null> = {
|
||||||
@@ -86,24 +86,17 @@ const commands: Record<EditorCommandType, ExtendedCommand|null> = {
|
|||||||
[EditorCommandType.ToggleItalicized]: toggleMark(schema.marks.emphasis),
|
[EditorCommandType.ToggleItalicized]: toggleMark(schema.marks.emphasis),
|
||||||
[EditorCommandType.ToggleCode]: toggleCode,
|
[EditorCommandType.ToggleCode]: toggleCode,
|
||||||
[EditorCommandType.ToggleMath]: (state, _dispatch, view) => {
|
[EditorCommandType.ToggleMath]: (state, _dispatch, view) => {
|
||||||
const renderer = getEditorApi(state).renderer;
|
|
||||||
const selectedText = state.doc.textBetween(state.selection.from, state.selection.to);
|
const selectedText = state.doc.textBetween(state.selection.from, state.selection.to);
|
||||||
|
|
||||||
const block = selectedText.includes('\n');
|
const block = selectedText.includes('\n');
|
||||||
const nodeType = block ? schema.nodes.joplinEditableBlock : schema.nodes.joplinEditableInline;
|
const nodeType = block ? schema.nodes.joplinEditableBlock : schema.nodes.joplinEditableInline;
|
||||||
|
|
||||||
if (canReplaceSelectionWith(state.selection, nodeType)) {
|
if (canReplaceSelectionWith(state.selection, nodeType)) {
|
||||||
void (async () => {
|
|
||||||
const separator = block ? '$$' : '$';
|
|
||||||
const rendered = await renderer.renderMarkupToHtml(`${separator}${selectedText}${separator}`, {
|
|
||||||
forceMarkdown: true,
|
|
||||||
isFullPageRender: false,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (view) {
|
if (view) {
|
||||||
view.pasteHTML(rendered.html);
|
const separator = block ? '$$' : '$';
|
||||||
|
void insertRenderedMarkdown(view,
|
||||||
|
`${separator}${selectedText}${separator}`,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
})();
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
@@ -121,6 +114,29 @@ const commands: Record<EditorCommandType, ExtendedCommand|null> = {
|
|||||||
[EditorCommandType.ToggleHeading4]: toggleHeading(4),
|
[EditorCommandType.ToggleHeading4]: toggleHeading(4),
|
||||||
[EditorCommandType.ToggleHeading5]: toggleHeading(5),
|
[EditorCommandType.ToggleHeading5]: toggleHeading(5),
|
||||||
[EditorCommandType.InsertHorizontalRule]: null,
|
[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) => {
|
[EditorCommandType.ToggleSearch]: (state, dispatch, view) => {
|
||||||
const command = setSearchVisible(!getSearchVisible(state));
|
const command = setSearchVisible(!getSearchVisible(state));
|
||||||
return command(state, dispatch, view);
|
return command(state, dispatch, view);
|
||||||
|
|||||||
17
packages/editor/ProseMirror/utils/insertRenderedMarkdown.ts
Normal file
17
packages/editor/ProseMirror/utils/insertRenderedMarkdown.ts
Normal 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;
|
||||||
@@ -30,6 +30,7 @@ export enum EditorCommandType {
|
|||||||
ToggleHeading5 = 'textHeading5',
|
ToggleHeading5 = 'textHeading5',
|
||||||
|
|
||||||
InsertHorizontalRule = 'textHorizontalRule',
|
InsertHorizontalRule = 'textHorizontalRule',
|
||||||
|
InsertTable = 'textTable',
|
||||||
|
|
||||||
// Find commands
|
// Find commands
|
||||||
ToggleSearch = 'textSearch',
|
ToggleSearch = 'textSearch',
|
||||||
|
|||||||
Reference in New Issue
Block a user