Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

✨ Add insertParagraph buttons #67

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions lang/de/lang.php
Original file line number Diff line number Diff line change
Expand Up @@ -100,4 +100,6 @@
$lang['js']['label:rss details'] = 'Zeige die Beschreibung des Eintrages';
$lang['js']['label:rss refresh'] = 'Abfragerhythmus';

$lang['js']['button:insert paragraph'] = 'Absatz einfügen';

$lang['js']['code_block_hint'] = '💡 Zum Verlassen drücken Sie STRG+Enter';
2 changes: 2 additions & 0 deletions lang/en/lang.php
Original file line number Diff line number Diff line change
Expand Up @@ -100,4 +100,6 @@
$lang['js']['label:rss details'] = 'Show the item description';
$lang['js']['label:rss refresh'] = 'Refresh period';

$lang['js']['button:insert paragraph'] = 'Insert paragraph';

$lang['js']['code_block_hint'] = '💡 Press CTRL+Enter to exit';
49 changes: 49 additions & 0 deletions script/custom/paragraphButtons.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { insertParagraphAtPos } from '../customCommands';

/**
* Attach event handler to buttons for inserting a paragraph
*
* @return {void}
*/
export function initializeButtons() {
if (window.Prosemirror.paragraphHandlingIsInitialized) {
return;
}
window.Prosemirror.paragraphHandlingIsInitialized = true;
jQuery(document).on('click', '.ProseMirror button.addParagraphButton', function insertParagraph(event) {
event.stopPropagation();
event.preventDefault();
const $button = jQuery(this);
const viewID = $button.data('viewid');
const direction = $button.data('direction');
const view = window.Prosemirror.views[viewID];

const pos = view.posAtDOM(event.target.parentNode);
const command = insertParagraphAtPos(pos, direction);
command(view.state, view.dispatch);
});
}

/**
* Produce the spec for a button to insert a paragraph before or after the respective element
*
* @param viewID
* @param direction
* @return {array}
*/
export function getButtonSpec(viewID, direction) {
if (typeof LANG === 'undefined') {
// early return during js schema testing
return [];
}
return [
'button',
{
class: 'addParagraphButton',
type: 'button',
'data-direction': direction,
'data-viewid': viewID,
},
LANG.plugins.prosemirror['button:insert paragraph'],
];
}
30 changes: 30 additions & 0 deletions script/customCommands.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { Selection } from 'prosemirror-state';

/**
* Returns a command that tries to set the selected textblocks to the given node type with the given attributes.
*
Expand All @@ -20,3 +22,31 @@ export function setBlockTypeNoAttrCheck(nodeType, attrs) { // eslint-disable-lin
return true;
};
}

/**
* Returns a command that inserts a new paragraph before or after the node at the given position
*
* @param {int} pos position near which the paragraph should be inserted
* @param {string} direction should be 'before' or 'after'
* @return {function}
*/
export function insertParagraphAtPos(pos, direction = 'after') {
return function insertParagraphAtPosCommand(state, dispatch) {
const $pos = state.doc.resolve(pos);
const above = $pos.node(-1);
const beforeOrAfter = direction !== 'after' ? $pos.index(-1) : $pos.indexAfter(-1);
const type = above.contentMatchAt(beforeOrAfter).defaultType;

if (!above.canReplaceWith(beforeOrAfter, beforeOrAfter, type)) {
return false;
}

if (dispatch) {
const insertPos = direction !== 'after' ? $pos.before() : $pos.after();
const tr = state.tr.replaceWith(insertPos, insertPos, type.createAndFill());
tr.setSelection(Selection.near(tr.doc.resolve(insertPos), 1));
dispatch(tr.scrollIntoView());
}
return true;
};
}
10 changes: 7 additions & 3 deletions script/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,13 @@ import getKeymapPlugin from './plugins/Keymap/keymap';
import initializePublicAPI from './initializePublicAPI';
import MenuInitializer from './plugins/Menu/MenuInitializer';
import getNodeViews from './nodeviews';
import { initializeButtons } from './custom/paragraphButtons';

initializePublicAPI();

window.Prosemirror.enableProsemirror = function enableProsemirror() {
const schema = new Schema(getSpec());
initializeButtons();

const mi = new MenuInitializer(schema);

Expand Down Expand Up @@ -40,7 +42,9 @@ window.Prosemirror.enableProsemirror = function enableProsemirror() {
},
nodeViews: getNodeViews(),
});
window.view = view;
window.Prosemirror.views = {
main: view,
};
jQuery(window).on('scroll.prosemirror_menu', () => {
const $container = jQuery('#prosemirror__editor');
const $menuBar = $container.find('.menubar');
Expand All @@ -56,8 +60,8 @@ window.Prosemirror.enableProsemirror = function enableProsemirror() {
};

window.Prosemirror.destroyProsemirror = function destroyProsemirror() {
if (window.view && typeof window.view.destroy === 'function') {
window.view.destroy();
if (window.Prosemirror.views.main && typeof window.Prosemirror.views.main.destroy === 'function') {
window.Prosemirror.views.main.destroy();
}
jQuery(window).off('scroll.prosemirror_menu');
};
4 changes: 2 additions & 2 deletions script/nodeviews/Footnote/footnoteSchema.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import getSpec from '../../schema';

export default function getFootnoteSpec() {
const baseSpec = getSpec();
export default function getFootnoteSpec(viewID) {
const baseSpec = getSpec(viewID);
let footnoteSchemaNodes = baseSpec.nodes.remove('footnote').remove('heading');
const doc = { ...footnoteSchemaNodes.get('doc') };
const { notoc: ommitted, nocache: ommitted2, ...newDocAttrs } = doc.attrs;
Expand Down
5 changes: 4 additions & 1 deletion script/nodeviews/FootnoteView.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,10 @@ class FootnoteView extends AbstractNodeView {
appendTo: '.dokuwiki',
close: this.dispatchOuter.bind(this),
});
const viewID = 'footnoteView';

// And put a sub-ProseMirror into that
const footnoteSchema = new Schema(getFootnoteSpec());
const footnoteSchema = new Schema(getFootnoteSpec(viewID));
const mi = new MenuInitializer(footnoteSchema);
this.innerView = new EditorView(this.tooltip, {
// You can use any node as an editor document
Expand All @@ -72,6 +74,7 @@ class FootnoteView extends AbstractNodeView {
},
nodeViews: getNodeViews(),
});
window.Prosemirror.views[viewID] = this.innerView;
}

close() {
Expand Down
17 changes: 15 additions & 2 deletions script/schema.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,17 @@
import { schema as schemaBasic } from 'prosemirror-schema-basic';
import { tableNodes } from 'prosemirror-tables';
import { bulletList, listItem, orderedList } from 'prosemirror-schema-list';
import { getButtonSpec } from './custom/paragraphButtons';

export default function getSpec() {
/**
* @param {string} viewID key that defines the view for this schema in window.Prosemirror.views
*
* @return {{nodes: OrderedMap, marks: OrderedMap }}
*/
export default function getSpec(viewID = 'main') {
let { nodes, marks } = schemaBasic.spec;
const buttonParagraphBeforeSpec = getButtonSpec(viewID, 'before');
const buttonParagraphAfterSpec = getButtonSpec(viewID, 'after');

const doc = nodes.get('doc');
doc.content = '(block | baseonly | container | protected_block | substitution_block)+';
Expand Down Expand Up @@ -89,7 +97,12 @@ export default function getSpec() {
code: true,
defining: true,
toDOM(node) {
return ['pre', node.attrs, 0];
return [
'div',
buttonParagraphBeforeSpec,
['pre', node.attrs, 0],
buttonParagraphAfterSpec,
];
},
});

Expand Down