Trying to build a plugin to set start attribute for ol
in ckEditor5.
As best I can tell, the model contains a collection of listItems
.
The start attribute would need to be set on the ol
however, the parent of the list item, not on the list item itself.. is there a way to access the ol
from the model?
I can get the current li
with
first(editor.model.document.selection.getSelectedBlocks())
is there a way set an attribute on the ol
element?
EDIT -- code to get attribute on LI:
editor.model.schema.extend('listItem', { allowAttributes: 'listStart' });
editor.conversion.attributeToAttribute({
model: 'listStart',
view: 'start'
});
enables in the model: <listItem type="numbered" listStart="4">
, which will translate to this in the view:
<ol>
<li start="4">words</li>
</ol>
what I am trying to achieve is
<ol start="4">
<li>words</li>
</ol>
When I examine source, its looks as though the ol
(or ul
) is automatically created here:
function generateLiInUl( modelItem, conversionApi ) {
const mapper = conversionApi.mapper;
const viewWriter = conversionApi.writer;
const listType = modelItem.getAttribute( 'listType' ) == 'numbered' ? 'ol' : 'ul';
const viewItem = createViewListItemElement( viewWriter );
// ** OL or UL created here -->
const viewList = viewWriter.createContainerElement( listType, null );
viewWriter.insert( ViewPosition.createAt( viewList ), viewItem );
mapper.bindElements( modelItem, viewItem );
return viewItem;
}
Is there an event I could observe? Or is there a way in the conversion definition to target the attribute on the parent?
UPDATE 2
If we are going to mod source, we can intercept the downcast by adding this to the generateLiInUl
function (thanks MTilsted):
const listStart = modelItem.getAttribute('listStart');
if (listStart) {
viewWriter.setAttribute('start', listStart, viewList);
}
and to facilitate upcast add this to the viewModelConverter
function
const listStart = data.viewItem.parent.getAttribute('start');
if (listStart) {
writer.setAttribute( 'listStart', listStart, listItem );
}
It's a little ugly in that we are modifying source which is a pita for maintenance, and on upcast we are adding the listStart
attribute to every listItem
element in the model.. but its a start.
I looked briefly into adding dispatchers.. eg:
data.upcastDispatcher.on( 'element:li', myCustomUpcastFunction );
but could not figure out how to get a reference to listItem
element that was added to the model in the viewModelConverter
function mentioned above.
The answer that worked for me was very simple and I feel kinda silly not seeing it earlier: Use the LI value
attribute instead of the OL start
attribute.
<ol>
<li value="4">words</li>
</ol>
instead of:
<ol start="4">
<li>words</li>
</ol>
This keeps the attribute on the listItem and avoids all the complication (I completely missed the existence of the LI's value attribute in my original attempt):
editor.model.schema.extend('listItem', { allowAttributes: 'value' });
editor.conversion.attributeToAttribute({
model: 'value',
view: 'value'
});
The command to apply the value:
execute(arg) {
let val = arg.value;
const model = this.editor.model;
const block = first(model.document.selection.getSelectedBlocks());
model.change(writer => {
if (+val) {
writer.setAttribute('value', val, block);
} else {
writer.removeAttribute('value', block);
}
});
}
and to prevent the value being copied to next LI on enter:
editor.commands.get('enter').on('afterExecute', () => {
const block = first(editor.model.document.selection.getSelectedBlocks());
if ( block.name == 'listItem' && block.hasAttribute('value')) {
editor.model.change( writer => {
writer.removeAttribute('value', block);
});
}
});