Skip to content
Closed
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
Original file line number Diff line number Diff line change
Expand Up @@ -526,10 +526,12 @@ export const getListItemStyleDefinitions = ({ styleId, numId, level, editor, tri

const numDefinition = getDefinitionForLevel(abstractDefinition, level);
const numDefPpr = numDefinition?.elements.find((el) => el.name === 'w:pPr');
const numLvlJs = numDefinition?.elements.find((el) => el.name === 'w:lvlJc');

return {
stylePpr,
numDefPpr,
numLvlJs,
};
};

Expand Down
109 changes: 82 additions & 27 deletions packages/super-editor/src/extensions/list-item/ListItemNodeView.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@
import { getListItemStyleDefinitions } from '@helpers/list-numbering-helpers.js';
import { docxNumberigHelpers } from '@/core/super-converter/v2/importer/listImporter.js';

const MARKER_PADDING = 6;
const MARKER_OFFSET_RIGHT = 4;
const MIN_MARKER_WIDTH = 20;

const IS_DEBUGGING = false;

/**
Expand Down Expand Up @@ -60,7 +64,7 @@
}

const pos = this.getPos();
const { fontSize, fontFamily } = getTextStyleMarksFromLinkedStyles({
const { fontSize, fontFamily, lineHeight } = getTextStyleMarksFromLinkedStyles({
node: this.node,
pos,
editor: this.editor,
Expand All @@ -71,6 +75,7 @@
this.dom.className = 'sd-editor-list-item-node-view';
this.dom.style.fontSize = fontSize;
this.dom.style.fontFamily = fontFamily ? fontFamily : 'inherit';
this.dom.style.lineHeight = lineHeight || '';
this.dom.setAttribute('data-marker-type', orderMarker);
this.dom.setAttribute('data-num-id', numId);
this.dom.setAttribute('data-list-level', JSON.stringify(listLevel));
Expand Down Expand Up @@ -99,27 +104,44 @@
const { attrs } = this.node;
const { styleId, numId, level, indent: inlineIndent } = attrs;

// Gather visible indents
const defs = getListItemStyleDefinitions({ styleId, node: this.node, numId, level, editor: this.editor });
const visibleIndent = getVisibleIndent(defs.stylePpr, defs.numDefPpr, inlineIndent);

let absoluteLeft = visibleIndent.left - (visibleIndent.hanging || 0);
if (!absoluteLeft && absoluteLeft !== 0) absoluteLeft = 0;

// Place the content at the visible indent left
let contentLeft = visibleIndent.left;
if (IS_DEBUGGING) {
console.debug('[ListItemNodeView] absoluteLeft', absoluteLeft);
console.debug('[ListItemNodeView] contentLeft', contentLeft);
}

if (visibleIndent.left === absoluteLeft) {
absoluteLeft -= 24;
}

// Apply the styling
this.contentDOM.style.marginLeft = `${contentLeft}px`;
this.numberingDOM.style.left = `${absoluteLeft}px`;
const lvlJc = defs.numLvlJs?.attributes?.['w:val'] || 'left';

const contentLeft = visibleIndent.left || 0;
const hanging = visibleIndent.hanging || 0;

const handlers = {
right: () => {
const calculatedWidth = calculateMarkerWidth(this.dom, this.numberingDOM);
const minMarkerWidth = Math.max(calculatedWidth, MIN_MARKER_WIDTH);
const effectiveHanging = Math.max(hanging, minMarkerWidth);
const markerLeft = contentLeft - effectiveHanging - MARKER_OFFSET_RIGHT;
this.contentDOM.style.marginLeft = `${contentLeft}px`;
this.numberingDOM.style.left = `${markerLeft}px`;
this.numberingDOM.style.width = `${effectiveHanging}px`;
this.numberingDOM.style.textAlign = 'right';
},
left: () => {
const calculatedWidth = calculateMarkerWidth(this.dom, this.numberingDOM);
const minMarkerWidth = Math.max(calculatedWidth, MIN_MARKER_WIDTH);
let markerLeft = contentLeft - hanging;
if (markerLeft === contentLeft) {
markerLeft -= minMarkerWidth;
} else if (minMarkerWidth > hanging) {
const diff = minMarkerWidth - hanging;
markerLeft -= diff;
}
this.contentDOM.style.marginLeft = `${contentLeft}px`;
this.numberingDOM.style.left = `${markerLeft}px`;
this.numberingDOM.style.width = '';
this.numberingDOM.style.textAlign = '';
},
};

const handleStyles = handlers[lvlJc] ?? handlers.left;

handleStyles();
}

handleNumberingClick = () => {
Expand All @@ -131,13 +153,14 @@
this.node = node;
this.decorations = decorations;

const { fontSize, fontFamily } = getTextStyleMarksFromLinkedStyles({
const { fontSize, fontFamily, lineHeight } = getTextStyleMarksFromLinkedStyles({
node,
pos: this.getPos(),
editor: this.editor,
});
this.dom.style.fontSize = fontSize;
this.dom.style.fontFamily = fontFamily || 'inherit';
this.dom.style.lineHeight = lineHeight || '';
}

destroy() {
Expand All @@ -164,12 +187,14 @@
* Get the text style marks from a list item
* @param {Node} listItem - The list item node
* @param {MarkType} markType - The mark type to look for
* @returns {Array} An array of text style marks
* @returns {Object} An array of text style marks and attrs object
*/
function getListItemTextStyleMarks(listItem, markType) {
let textStyleMarks = [];
let attrs = {};
listItem.forEach((childNode) => {
if (childNode.type.name !== 'paragraph') return;
attrs.lineHeight = childNode.attrs.lineHeight;
childNode.forEach((textNode) => {
let isTextNode = textNode.type.name === 'text';
let hasTextStyleMarks = markType.isInSet(textNode.marks);
Expand All @@ -179,7 +204,10 @@
}
});
});
return textStyleMarks;
return {
marks: textStyleMarks,
attrs,
};
}

/**
Expand All @@ -199,12 +227,13 @@

// 2. Find all textStyle marks on this node
const textStyleType = getMarkType('textStyle', editor.schema);
const allMarks = getListItemTextStyleMarks(node, textStyleType);
const { marks: allMarks, attrs: allAttrs } = getListItemTextStyleMarks(node, textStyleType);
const styleMarks = allMarks.filter((m) => m.type === textStyleType);

// 3. Helpers to find the first mark that has a fontSize / fontFamily attr
const sizeMark = styleMarks.find((m) => m.attrs.fontSize);
const familyMark = styleMarks.find((m) => m.attrs.fontFamily);
const lineHeight = allAttrs.lineHeight;

// 4. Compute final fontSize (parse it, fall back to default if invalid)
let fontSize = sizeMark
Expand Down Expand Up @@ -233,7 +262,7 @@
}
}

return { fontSize, fontFamily };
return { fontSize, fontFamily, lineHeight };
}

/**
Expand All @@ -252,7 +281,7 @@
const decorationsInPlace = linkedStyles?.find(pos, pos + node.nodeSize);
// We are looking from the end as there may be several decorations
// and we need to find the most specific one.
const styleDeco = decorationsInPlace?.findLast((dec) => dec.type.attrs?.style);
const styleDeco = decorationsInPlace?.find((dec) => dec.type.attrs?.style);
const style = styleDeco?.type.attrs?.style;

const stylesArray = style?.split(';') || [];
Expand Down Expand Up @@ -291,5 +320,31 @@
if (IS_DEBUGGING) console.debug('[getVisibleIndent] numDefIndent', numDefIndent, numDefIndentTag, '\n\n');

const indent = combineIndents(styleIndent, numDefIndent);
return combineIndents(indent, inlineIndent);
const result = combineIndents(indent, inlineIndent);
return result;
};

function calculateMarkerWidth(dom, numberingDOM, { withPadding = true } = {}) {
const markerText = numberingDOM.textContent || '';
const fontSize = dom.style.fontSize || '10pt';
const fontValue = dom.style.fontFamily;
const fontFamily = fontValue && fontValue !== 'inherit' ? fontValue : 'Arial';

if (!markerText.trim()) return 0;

try {
const canvas = document.createElement('canvas');
const context = canvas.getContext('2d');

const fontSizePx = fontSize.includes('pt') ? Number.parseFloat(fontSize) * 1.33 : Number.parseFloat(fontSize);

context.font = `${fontSizePx}px ${fontFamily}`;

const textWidth = context.measureText(markerText).width;
const resultWidth = withPadding ? Math.ceil(textWidth + MARKER_PADDING) : Math.ceil(textWidth);

return resultWidth;
} catch (err) {

Check warning on line 347 in packages/super-editor/src/extensions/list-item/ListItemNodeView.js

View workflow job for this annotation

GitHub Actions / Lint & Format Check

'err' is defined but never used
return 0;
}
}
Loading