Lift and wrap

Given a selection, I need to lift it and then wrap it (to change alignment from for example center to right)

I tried:

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);

tr=liftTr(, r($from.start(depth)), r($from.end(depth)))
$from= r(
depth =$from.depth
tr = wrapTr(tr, r($from.start(depth)), r($to.end(depth)), schema.nodes.align, { alignment: alignment})

However this does not work and I get various errors

How can I achieve this ?


1 Like

Sounds like you just need to change the attributes, which you can do with setNodeType (pass null for the node type to preserve the existing type, but provide a new set of attributes). Lifting and then wrapping is very unlikely to work – the content of the node may not even be allowed as content of the parent node.

great, thx

the documentation for setNodeType says that type, attributes and marks are optional

What happens if they are omitted ?

The old values are used.

I don’t think that setNodeType solve the problem in this case.

I tried:

let {from, to, $from, $to} = state.selection
state.doc.nodesBetween(from, to, (node, pos, parent, index) => {
	if (node.type == schema.nodes.align) {
		tr=tr.setNodeType(pos, schema.nodes.align, { alignment: alignment })
	} else {
     	tr = wrapTr(tr, state.doc.resolve(pos), state.doc.resolve(pos+node.nodeSize), schema.nodes.align, { alignment: alignment})
	return false;

But this does not behave well when aligning a centered/righted block to the left.

The issue is that when aligning to the left (for example a centered block), I have to remove the alignment - that is lift

(using the code above, to left align a centered block, all lines in it will now behave as one block, for example centering one of those lines will center all of them)

I’m stuck here, any ideas welcomed …


Oh, I hadn’t seen that you are using wrapping nodes to do alignment. I’d have done it with an attribute. But if you do use a node, yes, you will have to lift to remove it.

do you mean use marks ?

No, not a mark, a node attribute

I am doing that, my schema is:

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 ["div", { class: node.attrs.alignment }, 0] }

The issue is that unlike center and right which wrap their content, there actually shouldn’t be any left aligned nodes - that is aligning to the left needs to be a lift out of the align attribute node.

The reason is that if you align to the left on any line of for example a centered block, it should move the entire centered block to the left, but if you align to the center on any line of a left aligned block it should only center that line.

Basically aligning to the left needs to removes alignment from the entire centered/righted block, where as aligning a left (unaligned) line to the center/right wraps just that line.

So unless I’m missing something I have to lift which I am not able to successfully achieve :frowning:

Not an attribute on an extra wrapper node, an attribute on the alignable textblock nodes themselves.

sorry I am confused. Do you mean adding the attributes to the schema for paragraph ? or else can you explain it further ? thanks

Yes, that’s what I mean.

ok this is all way too hard for such a basic editor function and I can’t get it to work :frowning: I hope someone will post a working sample at some point