mirror of
https://github.com/TriliumNext/Notes.git
synced 2025-07-27 18:12:29 +08:00
refactor(ckeditor5/codeblock): split dropdown into own plugin
This commit is contained in:
parent
178ce31064
commit
751ed0b5d4
@ -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;
|
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user