Wondering if anyone can help here please...
I've written an extension that handles text-align and line-height. It'll manage to set the alignment or line height correctly, but only one of the other.
If I set line-height, it reverts the alignment back to whatever is set as default (left).
Any ideas? Code below. No doubt i'm missing something obvious.
import { Node } from "tiptap";
import { toggleBlockType } from "tiptap-commands";
export default class ParagraphStyle extends Node {
get name() {
return "paragraphStyle";
}
get schema() {
return {
attrs: {
lineSpacing: {
default: null,
},
align: {
default: "left",
},
},
content: "inline*",
group: "block",
parseDOM: [
{
tag: "p",
getAttrs(dom) {
const { lineHeight, textAlign } = dom.style;
let align = textAlign || "";
const lineSpacing = lineHeight ? lineHeight : null;
const id = dom.getAttribute("id") || "";
return { align, lineSpacing, id };
},
},
],
toDOM(node) {
const { align, lineSpacing, id } = node.attrs;
const attrs = {};
let style = "";
if (align && align !== "left") {
style += `text-align: ${align};`;
}
if (lineSpacing) {
style += `line-height: ${lineSpacing};`;
}
if (style) {
attrs.style = style;
}
if (id) {
attrs.id = id;
}
return ["p", attrs, 0];
},
};
}
commands({ type, schema }) {
return attrs => toggleBlockType(type, schema.nodes.paragraphStyle, attrs);;
}
}
And being set with:
commands.paragraphStyle({lineSpacing: $event.target.value})
or
@click="commands.paragraphStyle({align: 'left'})"
Ok, I sort of have a working fix for this.
A bit messy, so I'll definitely be cleaning this up.
Essentially, look into the EditorState, get the cursor/selection position, get the parent node, get it's attrs and then use that as a base to assign. I'm using lodash's clone which I don't like, but for now it works for my requirements.
commands({ type, schema }) {
return attrs => (state, dispatch, view) => {
const { selection } = state;
const position = selection.$cursor
? selection.$cursor.pos
: selection.$to.pos;
let $pos = state.doc.resolve(position);
let $parent = $pos.parent;
let updateAttrs = $parent ? clone($parent.attrs) : {};
Object.assign(updateAttrs, attrs);
const isActive = nodeIsActive(state, type, attrs);
if (isActive) {
return setBlockType(toggletype)(state, dispatch, view);
}
return setBlockType(type, updateAttrs)(state, dispatch, view);
};
}
Hacked from TipTaps toggleBlockType command, and some bits from ProseMirror's setBlockType command.
Next to figure out getting the status of all of these for the button active states!
@paulrose Neato! Regarding the alignment stuff, I have an open discussion about how to handle that for multiple node types (#180.) It sounds like you are taking a similar approach to the one discussed there. Would you be willing to contribute your 2cents to that thread? Ideally it would be great to have a way to package up alignment (or text color, line height, etc. as you have done here) as extensions of some kind (or at least examples in the docs) to share with others.
Absolutely! I've read that thread a few times, and came back to it a lot during my struggles!
I've got a bit more to figure out on it, but will absolutely share what I find when I have something more concrete.
here is my solution
font.js defined in marks
import { Mark } from 'tiptap'
import { updateMark, markInputRule, markPasteRule } from 'tiptap-commands'
export default class Font extends Mark {
get name () {
return 'font'
}
get schema () {
return {
attrs: {
color: {
default: ''
},
fontSize: {
default: ''
},
fontFamily: {
default: ''
},
backgroundColor: {
default: ''
},
lineHeight: {
default: ''
}
},
parseDOM: [
{
tag: 'span',
getAttrs: dom => {
return {
color: dom.style.color,
fontSize: dom.style.fontSize,
fontFamily: dom.style.fontFamily,
backgroundColor: dom.style.backgroundColor,
lineHeight: dom.style.lineHeight,
}
}
}
],
toDOM: (node) => {
console.log(node)
return ['span', {
style: `color:${node.attrs.color};font-size:${node.attrs.fontSize};font-family:${node.attrs.fontFamily};background-color:${node.attrs.backgroundColor};line-height:${node.attrs.lineHeight};`
}, 0]
}
}
}
keys ({ type }) {
return {
'Mod-f': updateMark(type)
}
}
commands ({ type }) {
return attrs => updateMark(type, attrs)
}
inputRules ({ type }) {
return [
markInputRule(/(?:\*\*|__)([^*_]+)(?:\*\*|__)$/, type)
]
}
pasteRules ({ type }) {
return [
markPasteRule(/(?:\*\*|__)([^*_]+)(?:\*\*|__)/g, type)
]
}
}
define a function to update line-height, actually i don't write like this,just as a reference
function handleLineHeight(lineHeight, command = commands.font, attrs = getMarkAttrs('font')){
attrs.lineHeight = lineHeight
console.log(attrs)
command({
...attrs
})
}
Most helpful comment
Absolutely! I've read that thread a few times, and came back to it a lot during my struggles!
I've got a bit more to figure out on it, but will absolutely share what I find when I have something more concrete.