Creating Extensions

Extension

The recommended way to customize the editor is creating an extension, even if the change is small, because you can quickly enable/disable it, and reuse it for other projects.

The extension must be a component that is created by extending the Component base class.

import { Component } from '@ryusei/code';
export class MyComponent extends Component {
...
}
JavaScript

To make the extension work, you need to compose it by the RyuseiCode.compose():

import { RyuseiCode } from '@ryusei/code';
import { MyComponent } from 'path-to-your-extension';
RyuseiCode.compose( { MyComponent } );
JavaScript

Visit this page for more details about the Component class.

Small Example

This example displays a log message on the console every time when the focus line changes:

import { Component } from '@ryusei/code';
export class MyComponent extends Component {
mount( element ) {
this.on( 'focusLineChanged', () => {
console.log( `Line: ${ this.Selection.focus[ 0 ] }` );
} );
}
}
JavaScript

RyuseiCode invokes the mount() method after:

  • mounting all core components
  • collecting all essential elements

The component class exposes all core components as member properties. The example above gets the focus position from the Selection component.

Advanced Example

Let's take a look on more complex extension. In this example, we will create a popup UI that displays current location on the bottom of the caret.

Displays the current location on the bottom of the caret

First, create an element for the UI and append it to the overlay element that displayed in the foreground of the editor.

import { Component } from '@ryusei/code';
export class MyComponent extends Component {
mount( element ) {
super.mount( elements );
this.create();
}
create() {
this.popup = document.createElement( 'div' );
this.elements.overlay.appendChild( this.popup );
this.setStyles( {
position : 'absolute',
background: 'black',
color : 'white',
padding : '.2em .5em',
display : 'none', // Hide it initially.
} );
}
setStyles( styles ) {
Object.keys( styles ).forEach( prop => {
this.popup.style[ prop ] = styles[ prop ];
} );
}
}
JavaScript

The setStyles method is just a utility function to set inline styles to the popup element.

Next, display it when users move the caret. We can accomplish that by observing the selection states:

export class MyComponent extends Component {
mount( element ) {
super.mount( elements );
this.create();
this.listen();
}
...
listen() {
this.on( 'selected', ( e, Selection, state ) => {
if ( state === Selection.STATES.COLLAPSED ) {
this.show();
} else {
this.hide();
}
} );
}
show() {
this.setStyles( { display: 'block' } );
}
hide() {
this.setStyles( { display: 'none' } );
}
}
JavaScript

RyuseiCode fires the selected event whenever detecting change of the selection state. The code above shows the popup element when the selection is collapsed, and otherwise hides it.

Finally, move the element on the bottom of the caret:

export class MyComponent extends Component {
mount( element ) {
super.mount( elements );
this.create();
this.listen();
}
...
show() {
const { rect: caretRect } = this.Caret;
const { scrollerRect } = this.Measure;
this.setStyles( {
display: `block`,
top : `${ caretRect.bottom - scrollerRect.top }px`,
left : `${ caretRect.left - scrollerRect.left }px`,
} );
this.popup.textContent = this.Selection.getLocation();
}
}
JavaScript

The Caret component provides the DOMRect object that describes dimensions and positions of the caret, and the Measure component provides the one for the scroller element. Because the DOMRect positions are relative to the viewport (window), we need to calculate offset positions by them.