I am trying to implement text-alignment - left/right/center
I’ve done the following:
css:
.ProseMirror .left { text-align: left; }
.ProseMirror .right { text-align: right; }
.ProseMirror .center { text-align: center; }
schema:
align: {
attrs: { alignment: { default: "left" } },
content: "block+",
group: "block",
defining: true,
parseDOM: [{
tag: "p",
getAttrs(dom) {
return {
alignment: dom.getAttribute("class")==="center" ? "center" : (dom.getAttribute("class")==="right" ? "right" : "left"),
}
}
}],
toDOM(node) { return ["p", { class: node.attrs.alignment }, 0] }
},
code: (adapted from http://pboysen.github.io/)
function alignWrap(state, dispatch, alignment) {
function wrapTr(tr, $from, $to, nodeType, attrs) {
let range = $from.blockRange($to), wrapping = range && findWrapping(range, nodeType, attrs)
return wrapping? tr.wrap(range, wrapping) : false
}
function liftTr(tr, $from, $to) {
let range = $from.blockRange($to), target = range && liftTarget(range)
return target == null ? false : tr.lift(range, target)
}
function r(p) {
return state.doc.resolve(p);
}
let {from, to, $from, $to} = state.selection, isLeft = alignment == "left"
let depth =$from.depth, tr
if (depth > 0 && $from.node(depth-1).type == schema.nodes.align) {
tr=liftTr(state.tr, r($from.start(depth)), r($from.end(depth)))
if (!isLeft) {
state=state.apply(tr)
tr = wrapTr(state.tr, r($from.start(depth)), r($from.end(depth)), schema.nodes.align, { alignment: alignment})
}
} else if (!isLeft) { //left is default and doesn't need wrapper
let $end = from == to ? $from: $to
tr = wrapTr(state.tr, r($from.start(depth)), r($end.end(depth)), schema.nodes.align, { alignment: alignment})
}
if (tr) {
tr=tr.scrollIntoView()
if (dispatch) dispatch(tr);
}
return !!tr;
}
function alignItem(options) {
let passedOptions = {
run(state, dispatch) {
return alignWrap(state, dispatch, options.attrs.alignment)
},
select(state) {
return alignWrap(state, undefined, options.attrs.alignment)
}
}
for (let prop in options) passedOptions[prop] = options[prop]
return new MenuItem(passedOptions)
}
let alignMenuItems=[];
['left', 'center', 'right'].forEach(alignment => {
alignMenuItems.push(alignItem({
title: "Align "+alignment,
icon: icons[alignment],
attrs: { alignment: alignment }
}))
})
All and all it’s mostly working but I still have issues
-
is the use of classes really necessary, I tried to set text-align directly in the schema but was then not sure how to implement parseDOM ?
-
when I create for example a centerd block and then type enter twice I’m back to left … how can I achieve including newlines in the aligned block ?
-
if I change the alignment of one of the centered lines to left, only that lines is now left aligned … how can I get the entire aligned block to change alignment ? I tried to use div instead of p but got the same effect
-
is this in general a good solution ? is there a better way to implement this or any other comments on the code ? (this is a basic editor feature and would be great if there was API support or an official solution example/gist)
thx!