mirror of
				https://github.com/zadam/trilium.git
				synced 2025-10-31 18:36:30 +01:00 
			
		
		
		
	refactor(ckeditor5/codeblock): split dropdown into own plugin
This commit is contained in:
		| @@ -24,6 +24,7 @@ import "@triliumnext/ckeditor5-admonition/index.css"; | |||||||
| import "@triliumnext/ckeditor5-footnotes/index.css"; | import "@triliumnext/ckeditor5-footnotes/index.css"; | ||||||
| import "@triliumnext/ckeditor5-math/index.css"; | import "@triliumnext/ckeditor5-math/index.css"; | ||||||
| import CodeBlockToolbar from "./plugins/code_block_toolbar.js"; | import CodeBlockToolbar from "./plugins/code_block_toolbar.js"; | ||||||
|  | import CodeBlockLanguageDropdown from "./plugins/code_block_language_dropdown.js"; | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * Plugins that are specific to Trilium and not part of the CKEditor 5 core, included in both text editors but not in the attribute editor. |  * Plugins that are specific to Trilium and not part of the CKEditor 5 core, included in both text editors but not in the attribute editor. | ||||||
| @@ -40,6 +41,7 @@ const TRILIUM_PLUGINS: typeof Plugin[] = [ | |||||||
|     IncludeNote, |     IncludeNote, | ||||||
|     Uploadfileplugin, |     Uploadfileplugin, | ||||||
|     SyntaxHighlighting, |     SyntaxHighlighting, | ||||||
|  |     CodeBlockLanguageDropdown, | ||||||
|     CodeBlockToolbar |     CodeBlockToolbar | ||||||
| ]; | ]; | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										103
									
								
								packages/ckeditor5/src/plugins/code_block_language_dropdown.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										103
									
								
								packages/ckeditor5/src/plugins/code_block_language_dropdown.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,103 @@ | |||||||
|  | import { Editor, CodeBlock, Plugin, type ListDropdownButtonDefinition, Collection, type CodeBlockCommand, ViewModel, createDropdown, addListToDropdown, DropdownButtonView } from "ckeditor5"; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Toolbar item which displays the list of languages in a dropdown, with the text visible (similar to the headings switcher), as opposed to the default split button implementation. | ||||||
|  |  */ | ||||||
|  | export default class CodeBlockLanguageDropdown extends Plugin { | ||||||
|  |  | ||||||
|  |     static get requires() { | ||||||
|  |         return [ CodeBlock ]; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public init() { | ||||||
|  |         const editor = this.editor; | ||||||
|  |         const componentFactory = editor.ui.componentFactory; | ||||||
|  |  | ||||||
|  |         const normalizedLanguageDefs = this._getNormalizedAndLocalizedLanguageDefinitions(editor); | ||||||
|  |         const itemDefinitions = this._getLanguageListItemDefinitions(normalizedLanguageDefs); | ||||||
|  |         const command: CodeBlockCommand = editor.commands.get( 'codeBlock' )!; | ||||||
|  |  | ||||||
|  |         componentFactory.add("codeBlockDropdown", locale => { | ||||||
|  |             const dropdownView = createDropdown(this.editor.locale, DropdownButtonView); | ||||||
|  |             dropdownView.buttonView.set({ | ||||||
|  |                 withText: true | ||||||
|  |             }); | ||||||
|  |             dropdownView.bind( 'isEnabled' ).to( command, 'value', value => !!value ); | ||||||
|  |             dropdownView.buttonView.bind( 'label' ).to( command, 'value', (value) => { | ||||||
|  |                 const itemDefinition = normalizedLanguageDefs.find((def) => def.language === value); | ||||||
|  |                 return itemDefinition?.label; | ||||||
|  |             }); | ||||||
|  |             dropdownView.on( 'execute', evt => { | ||||||
|  |                 editor.execute( 'codeBlock', { | ||||||
|  |                     language: ( evt.source as any )._codeBlockLanguage, | ||||||
|  |                     forceValue: true | ||||||
|  |                 }); | ||||||
|  |  | ||||||
|  |                 editor.editing.view.focus(); | ||||||
|  |             }); | ||||||
|  |             addListToDropdown(dropdownView, itemDefinitions); | ||||||
|  |             return dropdownView; | ||||||
|  |         }); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // Adapted from packages/ckeditor5-code-block/src/codeblockui.ts | ||||||
|  |     private _getLanguageListItemDefinitions( | ||||||
|  | 		normalizedLanguageDefs: Array<CodeBlockLanguageDefinition> | ||||||
|  | 	): Collection<ListDropdownButtonDefinition> { | ||||||
|  | 		const editor = this.editor; | ||||||
|  | 		const command: CodeBlockCommand = editor.commands.get( 'codeBlock' )!; | ||||||
|  | 		const itemDefinitions = new Collection<ListDropdownButtonDefinition>(); | ||||||
|  |  | ||||||
|  | 		for ( const languageDef of normalizedLanguageDefs ) { | ||||||
|  | 			const definition: ListDropdownButtonDefinition = { | ||||||
|  | 				type: 'button', | ||||||
|  | 				model: new ViewModel( { | ||||||
|  | 					_codeBlockLanguage: languageDef.language, | ||||||
|  | 					label: languageDef.label, | ||||||
|  | 					role: 'menuitemradio', | ||||||
|  | 					withText: true | ||||||
|  | 				} ) | ||||||
|  | 			}; | ||||||
|  |  | ||||||
|  | 			definition.model.bind( 'isOn' ).to( command, 'value', value => { | ||||||
|  | 				return value === definition.model._codeBlockLanguage; | ||||||
|  | 			} ); | ||||||
|  |  | ||||||
|  | 			itemDefinitions.add( definition ); | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		return itemDefinitions; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  |     // Adapted from packages/ckeditor5-code-block/src/utils.ts | ||||||
|  |     private _getNormalizedAndLocalizedLanguageDefinitions(editor: Editor) { | ||||||
|  |         const languageDefs = editor.config.get( 'codeBlock.languages' ) as Array<CodeBlockLanguageDefinition>; | ||||||
|  |         for ( const def of languageDefs ) { | ||||||
|  |             if ( def.class === undefined ) { | ||||||
|  |                 def.class = `language-${ def.language }`; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         return languageDefs; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  | } | ||||||
|  |  | ||||||
|  | interface CodeBlockLanguageDefinition { | ||||||
|  |  | ||||||
|  | 	/** | ||||||
|  | 	 * The name of the language that will be stored in the model attribute. Also, when `class` | ||||||
|  | 	 * is not specified, it will be used to create the CSS class associated with the language (prefixed by "language-"). | ||||||
|  | 	 */ | ||||||
|  | 	language: string; | ||||||
|  |  | ||||||
|  | 	/** | ||||||
|  | 	 * The human–readable label associated with the language and displayed in the UI. | ||||||
|  | 	 */ | ||||||
|  | 	label: string; | ||||||
|  |  | ||||||
|  | 	/** | ||||||
|  | 	 * The CSS class associated with the language. When not specified the `language` | ||||||
|  |  	 * property is used to create a class prefixed by "language-". | ||||||
|  | 	 */ | ||||||
|  | 	class?: string; | ||||||
|  | } | ||||||
| @@ -1,40 +1,10 @@ | |||||||
| import { Editor, CodeBlock, Plugin, ViewDocumentFragment, WidgetToolbarRepository, type ViewNode, type ListDropdownButtonDefinition, Collection, type CodeBlockCommand, ViewModel, createDropdown, addListToDropdown, DropdownButtonView } from "ckeditor5"; | import { CodeBlock, Plugin, ViewDocumentFragment, WidgetToolbarRepository, type ViewNode } from "ckeditor5"; | ||||||
|  | import CodeBlockLanguageDropdown from "./code_block_language_dropdown"; | ||||||
|  |  | ||||||
| export default class CodeBlockToolbar extends Plugin { | export default class CodeBlockToolbar extends Plugin { | ||||||
|  |  | ||||||
|     static get requires() { |     static get requires() { | ||||||
|         return [ WidgetToolbarRepository, CodeBlock ] as const; |         return [ WidgetToolbarRepository, CodeBlock, CodeBlockLanguageDropdown ] as const; | ||||||
|     } |  | ||||||
|  |  | ||||||
|     public init(): void { |  | ||||||
|         const editor = this.editor; |  | ||||||
|         const componentFactory = editor.ui.componentFactory; |  | ||||||
|  |  | ||||||
|         const normalizedLanguageDefs = this._getNormalizedAndLocalizedLanguageDefinitions(editor); |  | ||||||
| 		const itemDefinitions = this._getLanguageListItemDefinitions(normalizedLanguageDefs); |  | ||||||
|         const command: CodeBlockCommand = editor.commands.get( 'codeBlock' )!; |  | ||||||
|  |  | ||||||
|         componentFactory.add("codeBlockDropdown", locale => { |  | ||||||
|             const dropdownView = createDropdown(this.editor.locale, DropdownButtonView); |  | ||||||
|             dropdownView.buttonView.set({ |  | ||||||
|                 withText: true |  | ||||||
|             }); |  | ||||||
|             dropdownView.bind( 'isEnabled' ).to( command, 'value', value => !!value ); |  | ||||||
|             dropdownView.buttonView.bind( 'label' ).to( command, 'value', (value) => { |  | ||||||
|                 const itemDefinition = normalizedLanguageDefs.find((def) => def.language === value); |  | ||||||
|                 return itemDefinition?.label; |  | ||||||
|             }); |  | ||||||
|             dropdownView.on( 'execute', evt => { |  | ||||||
| 				editor.execute( 'codeBlock', { |  | ||||||
| 					language: ( evt.source as any )._codeBlockLanguage, |  | ||||||
| 					forceValue: true |  | ||||||
| 				}); |  | ||||||
|  |  | ||||||
| 				editor.editing.view.focus(); |  | ||||||
| 			}); |  | ||||||
|             addListToDropdown(dropdownView, itemDefinitions); |  | ||||||
|             return dropdownView; |  | ||||||
|         }); |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     afterInit() { |     afterInit() { | ||||||
| @@ -65,64 +35,4 @@ export default class CodeBlockToolbar extends Plugin { | |||||||
|         }); |         }); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     // Adapted from packages/ckeditor5-code-block/src/codeblockui.ts |  | ||||||
|     private _getLanguageListItemDefinitions( |  | ||||||
| 		normalizedLanguageDefs: Array<CodeBlockLanguageDefinition> |  | ||||||
| 	): Collection<ListDropdownButtonDefinition> { |  | ||||||
| 		const editor = this.editor; |  | ||||||
| 		const command: CodeBlockCommand = editor.commands.get( 'codeBlock' )!; |  | ||||||
| 		const itemDefinitions = new Collection<ListDropdownButtonDefinition>(); |  | ||||||
|  |  | ||||||
| 		for ( const languageDef of normalizedLanguageDefs ) { |  | ||||||
| 			const definition: ListDropdownButtonDefinition = { |  | ||||||
| 				type: 'button', |  | ||||||
| 				model: new ViewModel( { |  | ||||||
| 					_codeBlockLanguage: languageDef.language, |  | ||||||
| 					label: languageDef.label, |  | ||||||
| 					role: 'menuitemradio', |  | ||||||
| 					withText: true |  | ||||||
| 				} ) |  | ||||||
| 			}; |  | ||||||
|  |  | ||||||
| 			definition.model.bind( 'isOn' ).to( command, 'value', value => { |  | ||||||
| 				return value === definition.model._codeBlockLanguage; |  | ||||||
| 			} ); |  | ||||||
|  |  | ||||||
| 			itemDefinitions.add( definition ); |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		return itemDefinitions; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
|     // Adapted from packages/ckeditor5-code-block/src/utils.ts |  | ||||||
|     private _getNormalizedAndLocalizedLanguageDefinitions(editor: Editor) { |  | ||||||
|         const languageDefs = editor.config.get( 'codeBlock.languages' ) as Array<CodeBlockLanguageDefinition>; |  | ||||||
|         for ( const def of languageDefs ) { |  | ||||||
|             if ( def.class === undefined ) { |  | ||||||
|                 def.class = `language-${ def.language }`; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|         return languageDefs; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
| } |  | ||||||
|  |  | ||||||
| interface CodeBlockLanguageDefinition { |  | ||||||
|  |  | ||||||
| 	/** |  | ||||||
| 	 * The name of the language that will be stored in the model attribute. Also, when `class` |  | ||||||
| 	 * is not specified, it will be used to create the CSS class associated with the language (prefixed by "language-"). |  | ||||||
| 	 */ |  | ||||||
| 	language: string; |  | ||||||
|  |  | ||||||
| 	/** |  | ||||||
| 	 * The human–readable label associated with the language and displayed in the UI. |  | ||||||
| 	 */ |  | ||||||
| 	label: string; |  | ||||||
|  |  | ||||||
| 	/** |  | ||||||
| 	 * The CSS class associated with the language. When not specified the `language` |  | ||||||
|  	 * property is used to create a class prefixed by "language-". |  | ||||||
| 	 */ |  | ||||||
| 	class?: string; |  | ||||||
| } | } | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user