You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
(Question) Change numbering style of nested ordered lists #587
I'm just a user of this library, but implementing this to the core list block would make lists much more complex than they need to be for most users (I can only speak for myself). Writing a custom block using the core Blocknote ListBlock as a basis would be what I would do in the short term.
You can also just use the slash menu in any nested block too:
Just to offer some alternatives for you in the meantime.
I think this is a good suggestion! While customizing the numbering style for each nesting level is probably overkill, I agree that nested numbered lists should have distinct numbering styles.
Having this in the base NumberedListItemBlock seems very useful. Unless I'm missing something, I don't think it's super easy to implement this even in a custom block today. If there's a path towards implementing it, I'd love to know so I can take a stab at it.
Yeah agreed it's not a super easy change, but definitely take a look at the NumberedListIndexingPlugin. I think in theory you would just need to check the depth from blockInfo and have a function which converts the regular index to/from the other formats, like roman numerals. Also had a quick look and the TipTap node doesn't care what the index attribute is since it just stores, renders, and parses it, so doing e.g. index: "iii" shouldn't cause any issues.
@matthewlipski I know a lot more about BlockNote now, so was more comfortable taking this on.
I'm not sure how to actually work in the dev version of the project. I was hoping to be able to write some tests or examples to test this feature, but I think they're based on published version of blocknote and not local.
I'm more than happy to work through putting this into BlockNote proper, or if you want to use the code that is also good with me.
A few notes on the code:
I wasn't sure how to best pass the function into the plugin. Because I am overriding the plugin itself, I decided to put it into the closure. I don't think this would work in the main project.
The plugin actually becomes simpler because we calculate the list index. This means that the requirement of firstInDoc + firstOfType is not really needed.
getListCharacter could be default-included in this project. I kept it dependency free
I could not use the existing index attribute to calculate "previous index", because it's no longer guaranteed to be a digit. I did not want to extend the existing types at all as part of this, and think the re-calculation of depth/index is probably good overall.
BUG: I realized that depth is not calculate properly here. It should be the depth of the list entry and NOT the depth relative to the document. I think that calculation would need to change to be accurate. Impact: a numeric list started at depth will not necessarily start at 1
// NumberListIndexingPlugin.tsimport{Plugin,PluginKey}from"prosemirror-state"import{getBlockInfoFromPos}from"../helpers/getBlockInfoFromPos"// ProseMirror Plugin which automatically assigns indices to ordered list items per nesting level.constPLUGIN_KEY=newPluginKey(`numbered-list-indexing`)interfaceOptions{getListCharacter?: (positionDetails: {depth: number;index: number})=>string}constdefaultGetListCharacter=(position: {index: number})=>position.index.toString()exportconstNumberedListIndexingPlugin=(opts: Options={})=>{constgetListCharacter=opts.getListCharacter||defaultGetListCharacterreturnnewPlugin({key: PLUGIN_KEY,appendTransaction: (_transactions,_oldState,newState)=>{consttr=newState.trtr.setMeta("numberedListIndexing",true)letmodified=false// Traverses each node the doc using DFS, so blocks which are on the same nesting level will be traversed in the// same order they appear. This means the index of each list item block can be calculated by incrementing the// index of the previous list item block.newState.doc.descendants((node,pos)=>{if(node.type.name==="blockContainer"&&node.firstChild!.type.name==="numberedListItem"){constblockInfo=getBlockInfoFromPos(tr.doc,pos+1)!if(blockInfo===undefined){return}// Divide by 2 because each block has a nesting around itconstdepth=blockInfo.depth/2letfirstListBlock=blockInfoletblockIndex=1while(firstListBlock){constprevBlockInfo=getBlockInfoFromPos(tr.doc,firstListBlock.startPos-2)!if(prevBlockInfo&&prevBlockInfo.id!==firstListBlock.id&&prevBlockInfo.depth===firstListBlock.depth&&prevBlockInfo.contentType===firstListBlock.contentType){blockIndex++firstListBlock=prevBlockInfo}else{break}}constnewIndex=getListCharacter({ depth,index: blockIndex})constcontentNode=blockInfo.contentNodeconstindex=contentNode.attrs["index"]if(index!==newIndex){modified=truetr.setNodeMarkup(pos+1,undefined,{index: newIndex})}}})returnmodified ? tr : null}})}
// getListCharacter.tsexportfunctiongetListCharacter(positionDetails: {depth: number;index: number}){conststyle=(positionDetails.depth-1)%3if(style===1){returngetColumnLetters(positionDetails.index)}elseif(style===2){returnromanize(positionDetails.index).toLowerCase()}else{return`${positionDetails.index}`}}// from https://dev.to/all_stacks_developer/how-to-convert-column-index-of-a-spreadsheet-into-letters-31k0// prettier-ignoreconstALPHABET=['a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z']functiongetColumnLetters(columnIndexStartFromOne: number): string{if(columnIndexStartFromOne<27){returnALPHABET[columnIndexStartFromOne-1]}else{varres=columnIndexStartFromOne%26vardiv=Math.floor(columnIndexStartFromOne/26)if(res===0){div=div-1res=26}returngetColumnLetters(div)+ALPHABET[res-1]}}// from https://stackoverflow.com/questions/9083037/convert-a-number-into-a-roman-numeral-in-javascriptfunctionromanize(num: number): string{constdigits=`${num}`.split("")// prettier-ignoreconstkey=["","C","CC","CCC","CD","D","DC","DCC","DCCC","CM","","X","XX","XXX","XL","L","LX","LXX","LXXX","XC","","I","II","III","IV","V","VI","VII","VIII","IX"]letroman=""leti=3while(i--)roman=(key[+digits.pop()!+i*10]||"")+romanreturnArray(+digits.join("")+1).join("M")+roman}
Activity
nicholasdavidbrown commentedon Feb 29, 2024
I'm just a user of this library, but implementing this to the core list block would make lists much more complex than they need to be for most users (I can only speak for myself). Writing a custom block using the core Blocknote ListBlock as a basis would be what I would do in the short term.
You can also just use the slash menu in any nested block too:
Just to offer some alternatives for you in the meantime.
matthewlipski commentedon Jun 11, 2024
I think this is a good suggestion! While customizing the numbering style for each nesting level is probably overkill, I agree that nested numbered lists should have distinct numbering styles.
sb8244 commentedon Jul 14, 2024
Having this in the base
NumberedListItemBlockseems very useful. Unless I'm missing something, I don't think it's super easy to implement this even in a custom block today. If there's a path towards implementing it, I'd love to know so I can take a stab at it.matthewlipski commentedon Jul 16, 2024
Yeah agreed it's not a super easy change, but definitely take a look at the
NumberedListIndexingPlugin. I think in theory you would just need to check the depth fromblockInfoand have a function which converts the regular index to/from the other formats, like roman numerals. Also had a quick look and the TipTap node doesn't care what theindexattribute is since it just stores, renders, and parses it, so doing e.g.index: "iii"shouldn't cause any issues.sb8244 commentedon Aug 3, 2024
@matthewlipski I know a lot more about BlockNote now, so was more comfortable taking this on.
I'm not sure how to actually work in the dev version of the project. I was hoping to be able to write some tests or examples to test this feature, but I think they're based on published version of blocknote and not local.
I'm more than happy to work through putting this into BlockNote proper, or if you want to use the code that is also good with me.
A few notes on the code:
depthis not calculate properly here. It should be the depth of the list entry and NOT the depth relative to the document. I think that calculation would need to change to be accurate. Impact: a numeric list started at depth will not necessarily start at 1sb8244 commentedon Aug 5, 2024
This updated version handles broken lists properly, by finding the closest parent of the same type: https://gist.github.com/sb8244/c59acf0836eda8c79852e623afe936d1
serpent213 commentedon Nov 12, 2024
Just solved the same problem. I'm using CSS to generate the actual ordering symbols (
lower-alphain this case):And extended my ListIndexingPlugin like so:
For my
AlphabeticalListItem(based onNumericalListItem) to work I had to patchcoreto exportupdateblock, though.Happy to learn about a workaround – or have that exported by default...? 🙂