@@ -6,6 +6,10 @@ import { generateOrderedListIndex } from '@helpers/orderedListUtils.js';
66import { getListItemStyleDefinitions } from '@helpers/list-numbering-helpers.js' ;
77import { docxNumberigHelpers } from '@/core/super-converter/v2/importer/listImporter.js' ;
88
9+ const MARKER_PADDING = 6 ;
10+ const MARKER_OFFSET_RIGHT = 4 ;
11+ const MIN_MARKER_WIDTH = 20 ;
12+
913const IS_DEBUGGING = false ;
1014
1115/**
@@ -60,7 +64,7 @@ export class ListItemNodeView {
6064 }
6165
6266 const pos = this . getPos ( ) ;
63- const { fontSize, fontFamily } = getTextStyleMarksFromLinkedStyles ( {
67+ const { fontSize, fontFamily, lineHeight } = getTextStyleMarksFromLinkedStyles ( {
6468 node : this . node ,
6569 pos,
6670 editor : this . editor ,
@@ -71,6 +75,7 @@ export class ListItemNodeView {
7175 this . dom . className = 'sd-editor-list-item-node-view' ;
7276 this . dom . style . fontSize = fontSize ;
7377 this . dom . style . fontFamily = fontFamily ? fontFamily : 'inherit' ;
78+ this . dom . style . lineHeight = lineHeight || '' ;
7479 this . dom . setAttribute ( 'data-marker-type' , orderMarker ) ;
7580 this . dom . setAttribute ( 'data-num-id' , numId ) ;
7681 this . dom . setAttribute ( 'data-list-level' , JSON . stringify ( listLevel ) ) ;
@@ -99,27 +104,41 @@ export class ListItemNodeView {
99104 const { attrs } = this . node ;
100105 const { styleId, numId, level, indent : inlineIndent } = attrs ;
101106
102- // Gather visible indents
103107 const defs = getListItemStyleDefinitions ( { styleId, node : this . node , numId, level, editor : this . editor } ) ;
104108 const visibleIndent = getVisibleIndent ( defs . stylePpr , defs . numDefPpr , inlineIndent ) ;
105-
106- let absoluteLeft = visibleIndent . left - ( visibleIndent . hanging || 0 ) ;
107- if ( ! absoluteLeft && absoluteLeft !== 0 ) absoluteLeft = 0 ;
108-
109- // Place the content at the visible indent left
110- let contentLeft = visibleIndent . left ;
111- if ( IS_DEBUGGING ) {
112- console . debug ( '[ListItemNodeView] absoluteLeft' , absoluteLeft ) ;
113- console . debug ( '[ListItemNodeView] contentLeft' , contentLeft ) ;
114- }
115-
116- if ( visibleIndent . left === absoluteLeft ) {
117- absoluteLeft -= 24 ;
118- }
119-
120- // Apply the styling
121- this . contentDOM . style . marginLeft = `${ contentLeft } px` ;
122- this . numberingDOM . style . left = `${ absoluteLeft } px` ;
109+ const lvlJc = defs . numLvlJs ?. attributes ?. [ 'w:val' ] || 'left' ;
110+
111+ const contentLeft = visibleIndent . left || 0 ;
112+ const hanging = visibleIndent . hanging || 0 ;
113+
114+ const handlers = {
115+ right : ( ) => {
116+ const calculatedWidth = calculateMarkerWidth ( this . dom , this . numberingDOM ) ;
117+ const minMarkerWidth = Math . max ( calculatedWidth , MIN_MARKER_WIDTH ) ;
118+ const effectiveHanging = Math . max ( hanging , minMarkerWidth ) ;
119+ const markerLeft = contentLeft - effectiveHanging - MARKER_OFFSET_RIGHT ;
120+ this . contentDOM . style . marginLeft = `${ contentLeft } px` ;
121+ this . numberingDOM . style . left = `${ markerLeft } px` ;
122+ this . numberingDOM . style . width = `${ effectiveHanging } px` ;
123+ this . numberingDOM . style . textAlign = 'right' ;
124+ } ,
125+ left : ( ) => {
126+ let markerLeft = contentLeft - hanging ;
127+ if ( markerLeft === contentLeft ) {
128+ const calculatedWidth = calculateMarkerWidth ( this . dom , this . numberingDOM ) ;
129+ const fallbackWidth = Math . max ( calculatedWidth , MIN_MARKER_WIDTH ) ;
130+ markerLeft -= fallbackWidth ;
131+ }
132+ this . contentDOM . style . marginLeft = `${ contentLeft } px` ;
133+ this . numberingDOM . style . left = `${ markerLeft } px` ;
134+ this . numberingDOM . style . width = '' ;
135+ this . numberingDOM . style . textAlign = '' ;
136+ } ,
137+ } ;
138+
139+ const handleStyles = handlers [ lvlJc ] ?? handlers . left ;
140+
141+ handleStyles ( ) ;
123142 }
124143
125144 handleNumberingClick = ( ) => {
@@ -131,13 +150,14 @@ export class ListItemNodeView {
131150 this . node = node ;
132151 this . decorations = decorations ;
133152
134- const { fontSize, fontFamily } = getTextStyleMarksFromLinkedStyles ( {
153+ const { fontSize, fontFamily, lineHeight } = getTextStyleMarksFromLinkedStyles ( {
135154 node,
136155 pos : this . getPos ( ) ,
137156 editor : this . editor ,
138157 } ) ;
139158 this . dom . style . fontSize = fontSize ;
140159 this . dom . style . fontFamily = fontFamily || 'inherit' ;
160+ this . dom . style . lineHeight = lineHeight || '' ;
141161 }
142162
143163 destroy ( ) {
@@ -164,12 +184,14 @@ export function refreshAllListItemNodeViews() {
164184 * Get the text style marks from a list item
165185 * @param {Node } listItem - The list item node
166186 * @param {MarkType } markType - The mark type to look for
167- * @returns {Array } An array of text style marks
187+ * @returns {Object } An array of text style marks and attrs object
168188 */
169189function getListItemTextStyleMarks ( listItem , markType ) {
170190 let textStyleMarks = [ ] ;
191+ let attrs = { } ;
171192 listItem . forEach ( ( childNode ) => {
172193 if ( childNode . type . name !== 'paragraph' ) return ;
194+ attrs . lineHeight = childNode . attrs . lineHeight ;
173195 childNode . forEach ( ( textNode ) => {
174196 let isTextNode = textNode . type . name === 'text' ;
175197 let hasTextStyleMarks = markType . isInSet ( textNode . marks ) ;
@@ -179,7 +201,10 @@ function getListItemTextStyleMarks(listItem, markType) {
179201 }
180202 } ) ;
181203 } ) ;
182- return textStyleMarks ;
204+ return {
205+ marks : textStyleMarks ,
206+ attrs,
207+ } ;
183208}
184209
185210/**
@@ -199,12 +224,13 @@ function getTextStyleMarksFromLinkedStyles({ node, pos, editor }) {
199224
200225 // 2. Find all textStyle marks on this node
201226 const textStyleType = getMarkType ( 'textStyle' , editor . schema ) ;
202- const allMarks = getListItemTextStyleMarks ( node , textStyleType ) ;
227+ const { marks : allMarks , attrs : allAttrs } = getListItemTextStyleMarks ( node , textStyleType ) ;
203228 const styleMarks = allMarks . filter ( ( m ) => m . type === textStyleType ) ;
204229
205230 // 3. Helpers to find the first mark that has a fontSize / fontFamily attr
206231 const sizeMark = styleMarks . find ( ( m ) => m . attrs . fontSize ) ;
207232 const familyMark = styleMarks . find ( ( m ) => m . attrs . fontFamily ) ;
233+ const lineHeight = allAttrs . lineHeight ;
208234
209235 // 4. Compute final fontSize (parse it, fall back to default if invalid)
210236 let fontSize = sizeMark
@@ -233,7 +259,7 @@ function getTextStyleMarksFromLinkedStyles({ node, pos, editor }) {
233259 }
234260 }
235261
236- return { fontSize, fontFamily } ;
262+ return { fontSize, fontFamily, lineHeight } ;
237263}
238264
239265/**
@@ -252,7 +278,7 @@ const getStylesFromLinkedStyles = ({ node, pos, editor }) => {
252278 const decorationsInPlace = linkedStyles ?. find ( pos , pos + node . nodeSize ) ;
253279 // We are looking from the end as there may be several decorations
254280 // and we need to find the most specific one.
255- const styleDeco = decorationsInPlace ?. findLast ( ( dec ) => dec . type . attrs ?. style ) ;
281+ const styleDeco = decorationsInPlace ?. find ( ( dec ) => dec . type . attrs ?. style ) ;
256282 const style = styleDeco ?. type . attrs ?. style ;
257283
258284 const stylesArray = style ?. split ( ';' ) || [ ] ;
@@ -291,5 +317,32 @@ export const getVisibleIndent = (stylePpr, numDefPpr, inlineIndent) => {
291317 if ( IS_DEBUGGING ) console . debug ( '[getVisibleIndent] numDefIndent' , numDefIndent , numDefIndentTag , '\n\n' ) ;
292318
293319 const indent = combineIndents ( styleIndent , numDefIndent ) ;
294- return combineIndents ( indent , inlineIndent ) ;
320+ const result = combineIndents ( indent , inlineIndent ) ;
321+
322+ return result ;
295323} ;
324+
325+ function calculateMarkerWidth ( dom , numberingDOM , { withPadding = true } = { } ) {
326+ const markerText = numberingDOM . textContent || '' ;
327+ const fontSize = dom . style . fontSize || '10pt' ;
328+ const fontValue = dom . style . fontFamily ;
329+ const fontFamily = fontValue && fontValue !== 'inherit' ? fontValue : 'Arial' ;
330+
331+ if ( ! markerText . trim ( ) ) return 0 ;
332+
333+ try {
334+ const canvas = document . createElement ( 'canvas' ) ;
335+ const context = canvas . getContext ( '2d' ) ;
336+
337+ const fontSizePx = fontSize . includes ( 'pt' ) ? Number . parseFloat ( fontSize ) * 1.33 : Number . parseFloat ( fontSize ) ;
338+
339+ context . font = `${ fontSizePx } px ${ fontFamily } ` ;
340+
341+ const textWidth = context . measureText ( markerText ) . width ;
342+ const resultWidth = withPadding ? Math . ceil ( textWidth + MARKER_PADDING ) : Math . ceil ( textWidth ) ;
343+
344+ return resultWidth ;
345+ } catch ( err ) {
346+ return 0 ;
347+ }
348+ }
0 commit comments