mirror of
https://github.com/zadam/trilium.git
synced 2025-11-13 00:35:50 +01:00
chore(ckeditor5/plugins): integrate includenote
This commit is contained in:
@@ -8,6 +8,7 @@ declare global {
|
||||
interface EditorComponent extends Component {
|
||||
loadReferenceLinkTitle($el: JQuery<HTMLElement>, href: string): Promise<void>;
|
||||
createNoteForReferenceLink(title: string): Promise<string>;
|
||||
loadIncludedNote(noteId: string, $el: JQuery<HTMLElement>): void;
|
||||
}
|
||||
|
||||
var glob: {
|
||||
|
||||
1
packages/ckeditor5/src/icons/note.svg
Normal file
1
packages/ckeditor5/src/icons/note.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M19,3H5C3.897,3,3,3.897,3,5v14c0,1.103,0.897,2,2,2h8c0.131,0,0.26-0.026,0.381-0.076s0.232-0.123,0.326-0.217l7-7 c0.086-0.086,0.147-0.187,0.196-0.293c0.014-0.03,0.022-0.061,0.033-0.093c0.028-0.084,0.046-0.17,0.051-0.259 C20.989,13.041,21,13.021,21,13V5C21,3.897,20.103,3,19,3z M5,5h14v7h-6c-0.553,0-1,0.448-1,1v6H5V5z M14,17.586V14h3.586 L14,17.586z"/></svg>
|
||||
|
After Width: | Height: | Size: 449 B |
@@ -11,6 +11,7 @@ import SpecialCharactersEmojiPlugin from "./plugins/special_characters_emoji.js"
|
||||
import IndentBlockShortcutPlugin from "./plugins/indent_block_shortcut.js";
|
||||
import MarkdownImportPlugin from "./plugins/markdownimport.js";
|
||||
import MentionCustomization from "./plugins/mention_customization.js";
|
||||
import IncludeNote from "./plugins/includenote.js";
|
||||
|
||||
const TRILIUM_PLUGINS: typeof Plugin[] = [
|
||||
CutToNotePlugin,
|
||||
@@ -23,7 +24,8 @@ const TRILIUM_PLUGINS: typeof Plugin[] = [
|
||||
SpecialCharactersEmojiPlugin,
|
||||
IndentBlockShortcutPlugin,
|
||||
MarkdownImportPlugin,
|
||||
MentionCustomization
|
||||
MentionCustomization,
|
||||
IncludeNote
|
||||
];
|
||||
|
||||
export const COMMON_PLUGINS: typeof Plugin[] = [
|
||||
|
||||
176
packages/ckeditor5/src/plugins/includenote.ts
Normal file
176
packages/ckeditor5/src/plugins/includenote.ts
Normal file
@@ -0,0 +1,176 @@
|
||||
import { ButtonView, Command, Plugin, toWidget, Widget, type Editor, type Observable } from 'ckeditor5';
|
||||
import noteIcon from '../icons/note.svg?raw';
|
||||
|
||||
export default class IncludeNote extends Plugin {
|
||||
static get requires() {
|
||||
return [ IncludeNoteEditing, IncludeNoteUI ];
|
||||
}
|
||||
}
|
||||
|
||||
class IncludeNoteUI extends Plugin {
|
||||
init() {
|
||||
const editor = this.editor;
|
||||
const t = editor.t;
|
||||
|
||||
// The "includeNote" button must be registered among the UI components of the editor
|
||||
// to be displayed in the toolbar.
|
||||
editor.ui.componentFactory.add( 'includeNote', locale => {
|
||||
// The state of the button will be bound to the widget command.
|
||||
const command = editor.commands.get( 'insertIncludeNote' );
|
||||
|
||||
// The button will be an instance of ButtonView.
|
||||
const buttonView = new ButtonView( locale );
|
||||
|
||||
buttonView.set( {
|
||||
// The t() function helps localize the editor. All strings enclosed in t() can be
|
||||
// translated and change when the language of the editor changes.
|
||||
label: t( 'Include note' ),
|
||||
icon: noteIcon,
|
||||
tooltip: true
|
||||
} );
|
||||
|
||||
// Bind the state of the button to the command.
|
||||
if (command) {
|
||||
buttonView.bind( 'isOn', 'isEnabled' ).to( command as Observable & { value: boolean; } & { isEnabled: boolean; }, 'value', 'isEnabled' );
|
||||
}
|
||||
|
||||
// Execute the command when the button is clicked (executed).
|
||||
this.listenTo( buttonView, 'execute', () => editor.execute( 'insertIncludeNote' ) );
|
||||
|
||||
return buttonView;
|
||||
} );
|
||||
}
|
||||
}
|
||||
|
||||
class IncludeNoteEditing extends Plugin {
|
||||
static get requires() {
|
||||
return [ Widget ];
|
||||
}
|
||||
|
||||
init() {
|
||||
this._defineSchema();
|
||||
this._defineConverters();
|
||||
|
||||
this.editor.commands.add( 'insertIncludeNote', new InsertIncludeNoteCommand( this.editor ) );
|
||||
}
|
||||
|
||||
_defineSchema() {
|
||||
const schema = this.editor.model.schema;
|
||||
|
||||
schema.register( 'includeNote', {
|
||||
// Behaves like a self-contained object (e.g. an image).
|
||||
isObject: true,
|
||||
|
||||
allowAttributes: [ 'noteId', 'boxSize' ],
|
||||
|
||||
// Allow in places where other blocks are allowed (e.g. directly in the root).
|
||||
allowWhere: '$block'
|
||||
} );
|
||||
}
|
||||
|
||||
_defineConverters() {
|
||||
const editor = this.editor;
|
||||
const conversion = editor.conversion;
|
||||
|
||||
// <includeNote> converters
|
||||
conversion.for( 'upcast' ).elementToElement( {
|
||||
model: ( viewElement, { writer: modelWriter } ) => {
|
||||
|
||||
return modelWriter.createElement( 'includeNote', {
|
||||
noteId: viewElement.getAttribute( 'data-note-id' ),
|
||||
boxSize: viewElement.getAttribute( 'data-box-size' ),
|
||||
} );
|
||||
},
|
||||
view: {
|
||||
name: 'section',
|
||||
classes: 'include-note'
|
||||
}
|
||||
} );
|
||||
conversion.for( 'dataDowncast' ).elementToElement( {
|
||||
model: 'includeNote',
|
||||
view: ( modelElement, { writer: viewWriter } ) => {
|
||||
// it would make sense here to downcast to <iframe>, with this even HTML export can support note inclusion
|
||||
return viewWriter.createContainerElement( 'section', {
|
||||
class: 'include-note',
|
||||
'data-note-id': modelElement.getAttribute( 'noteId' ),
|
||||
'data-box-size': modelElement.getAttribute( 'boxSize' ),
|
||||
} );
|
||||
}
|
||||
} );
|
||||
conversion.for( 'editingDowncast' ).elementToElement( {
|
||||
model: 'includeNote',
|
||||
view: ( modelElement, { writer: viewWriter } ) => {
|
||||
|
||||
const noteId = modelElement.getAttribute( 'noteId' ) as string;
|
||||
const boxSize = modelElement.getAttribute( 'boxSize' );
|
||||
|
||||
const section = viewWriter.createContainerElement( 'section', {
|
||||
class: 'include-note box-size-' + boxSize,
|
||||
'data-note-id': noteId,
|
||||
'data-box-size': boxSize
|
||||
} );
|
||||
|
||||
const includedNoteWrapper = viewWriter.createUIElement( 'div', {
|
||||
class: 'include-note-wrapper',
|
||||
"data-cke-ignore-events": true
|
||||
}, function( domDocument ) {
|
||||
const domElement = this.toDomElement( domDocument );
|
||||
|
||||
const editorEl = editor.editing.view.getDomRoot();
|
||||
const component = glob.getComponentByEl<EditorComponent>( editorEl );
|
||||
|
||||
component.loadIncludedNote( noteId, $( domElement ) );
|
||||
|
||||
preventCKEditorHandling( domElement, editor );
|
||||
|
||||
return domElement;
|
||||
} );
|
||||
|
||||
viewWriter.insert( viewWriter.createPositionAt( section, 0 ), includedNoteWrapper );
|
||||
|
||||
return toWidget( section, viewWriter, { label: 'include note widget' } );
|
||||
}
|
||||
} );
|
||||
}
|
||||
}
|
||||
|
||||
class InsertIncludeNoteCommand extends Command {
|
||||
execute() {
|
||||
const editorEl = this.editor.editing.view.getDomRoot();
|
||||
const component = glob.getComponentByEl(editorEl);
|
||||
|
||||
component.triggerCommand('addIncludeNoteToText');
|
||||
}
|
||||
|
||||
refresh() {
|
||||
const model = this.editor.model;
|
||||
const selection = model.document.selection;
|
||||
const firstPosition = selection.getFirstPosition();
|
||||
const allowedIn = firstPosition && model.schema.findAllowedParent( firstPosition, 'includeNote' );
|
||||
|
||||
this.isEnabled = allowedIn !== null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Hack coming from https://github.com/ckeditor/ckeditor5/issues/4465
|
||||
* Source issue: https://github.com/zadam/trilium/issues/1117
|
||||
*/
|
||||
function preventCKEditorHandling( domElement: HTMLElement, editor: Editor ) {
|
||||
// Prevent the editor from listening on below events in order to stop rendering selection.
|
||||
|
||||
// commenting out click events to allow link click handler to still work
|
||||
//domElement.addEventListener( 'click', stopEventPropagationAndHackRendererFocus, { capture: true } );
|
||||
domElement.addEventListener( 'mousedown', stopEventPropagationAndHackRendererFocus, { capture: true } );
|
||||
domElement.addEventListener( 'focus', stopEventPropagationAndHackRendererFocus, { capture: true } );
|
||||
|
||||
// Prevents TAB handling or other editor keys listeners which might be executed on editors selection.
|
||||
domElement.addEventListener( 'keydown', stopEventPropagationAndHackRendererFocus, { capture: true } );
|
||||
|
||||
function stopEventPropagationAndHackRendererFocus( evt: Event ) {
|
||||
evt.stopPropagation();
|
||||
// This prevents rendering changed view selection thus preventing to changing DOM selection while inside a widget.
|
||||
//@ts-expect-error: We are accessing a private field.
|
||||
editor.editing.view._renderer.isFocused = false;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user