import { Plugin } from 'ckeditor5/src/core.js'; import { addListToDropdown, createDropdown, ViewModel, type ListDropdownItemDefinition } from '@ckeditor/ckeditor5-ui'; import { Collection } from '@ckeditor/ckeditor5-utils'; import { ATTRIBUTES, COMMANDS, ELEMENTS, TOOLBAR_COMPONENT_NAME } from './constants.js'; import insertFootnoteIcon from '../theme/icons/insert-footnote.svg'; import { modelQueryElement, modelQueryElementsAll } from './utils.js'; export default class FootnoteUI extends Plugin { public init(): void { const editor = this.editor; const translate = editor.t; editor.ui.componentFactory.add( TOOLBAR_COMPONENT_NAME, locale => { const dropdownView = createDropdown( locale ); // Populate the list in the dropdown with items. // addListToDropdown( dropdownView, getDropdownItemsDefinitions( placeholderNames ) ); const command = editor.commands.get( COMMANDS.insertFootnote ); if ( !command ) { throw new Error( 'Command not found.' ); } dropdownView.buttonView.set( { label: translate( 'Footnote' ), icon: insertFootnoteIcon, tooltip: true } ); dropdownView.class = 'ck-code-block-dropdown'; dropdownView.bind( 'isEnabled' ).to( command ); dropdownView.on( 'change:isOpen', ( evt, propertyName, newValue ) => { dropdownView?.listView?.items.clear(); if ( newValue ) { addListToDropdown( dropdownView, this.getDropdownItemsDefinitions() as any ); } else { dropdownView?.listView?.items.clear(); const listElement = dropdownView?.listView?.element; if ( listElement && listElement.parentNode ) { listElement.parentNode.removeChild( listElement ); } } } ); // Execute the command when the dropdown item is clicked (executed). this.listenTo( dropdownView, 'execute', evt => { editor.execute( COMMANDS.insertFootnote, { footnoteIndex: ( evt.source as any ).commandParam } ); editor.editing.view.focus(); } ); return dropdownView; } ); } public getDropdownItemsDefinitions(): Collection { const itemDefinitions = new Collection(); const defaultDef: ListDropdownItemDefinition = { type: 'button', model: new ViewModel( { commandParam: 0, label: 'New footnote', withText: true } ) }; itemDefinitions.add( defaultDef ); const rootElement = this.editor.model.document.getRoot(); if ( !rootElement ) { throw new Error( 'Document has no root element.' ); } const footnoteSection = modelQueryElement( this.editor, rootElement, element => element.is( 'element', ELEMENTS.footnoteSection ) ); if ( footnoteSection ) { const footnoteItems = modelQueryElementsAll( this.editor, rootElement, element => element.is( 'element', ELEMENTS.footnoteItem ) ); footnoteItems.forEach( footnote => { const index = footnote.getAttribute( ATTRIBUTES.footnoteIndex ); const definition: ListDropdownItemDefinition = { type: 'button', model: new ViewModel( { commandParam: index, label: `Insert footnote ${ index }`, withText: true } ) }; itemDefinitions.add( definition ); } ); } return itemDefinitions; } }