# end goal for the vendor/user Have a toolbar that works: - bold/italic/... buttons show whether they are currently active - bold/italic/... buttons behave as expected, both on selections and on cursor position # background: Observables vs Events Very close to each other, a lot depends on implementation details Conceptually it's a different beast though ## events + we already use events + more "pure", everyone is responsible for their own state + difference between "enterList", "leftList" vs "inList" as state + all logic goes to handlers + easy to have an api to stop event bubbling - all logic goes to handlers - debugging events might be harder vs observables ## observables + might be easier to debug + might be more efficient (state only calculated once) + state is always calculated, even if you missed an event - maintaining state is hard, the editor already has a lot of state - possibility for race conditions # what can plugins do event driven A plugin can only: - execute commands through the editor controller (aka rawEditor). - send events - register event listeners observable driven A plugin can only: - execute commands through the editor controller (aka rawEditor). - register observers - update state in the statestore # Stories 1. build a selectionchange event listener that calculates our virtual dom (experiment) this listener is responsible for calculating the properties of the current selection and save the latest state in the editor controller. ``` { selection: RDFABlock, attributes: { rdfaContexts: contexts, inList: unknown , bold: unknown, italic: unkown} } ``` ``` <bold>foo</bold><italic>bar</italic> ``` we do this for the attributes bold, italic as an experiment and implement it as a eventListener on the DOM selectionchange event the listener updates the richSelection field on the controller and sends out a CustomEvent editorSelectionChange detection of italic/bold: - for a caret, is nested within a strong or em tag - for a selection, if the all the elements selection have the computedstyle strong/em => enabled - for a selection, if the not all the elements selection contains more than 1 element and have the computedstyle strong/em => unknown - for a selection, if none the elements selection contains more than 1 element and have the computedstyle strong/em => disabled the goal is centralizing logic now spread out within the editor RISK: what triggers the selectionchange? 3d 2. Create a makeBold & disableBold command with support for collapsed selections This command can be registered on the editorController via a new registerCommand API. This command can be called via the editorController via a new executeCommand API. consider using https://developer.mozilla.org/en-US/docs/Web/API/Range/surroundContents - The disable command removes bold from the selection - The make bold command makes the selection bold 15d 3. Extend makeBold & disableBold command with support for non collapsed selections The disable command removes the wrapping strong tag and maintains cursor position The make bold command inserts a strong tag and moves the caret inside the strong tag 10d 4. Implement a component that provides a toolbar button for bold The toolbar button shows the active state by listening to editorSelectionChange event. If the state is enabled, the button shows as pressed. when clicked it executes the disableBold command If the state is disabled, the button shows as unpressed. when clicked it executes the makeBold command If the state is unknown, the button shows as unpressed. when clicked it executes the makeBold command 1d 4. Provide a statestore in the editor so plugins can register a state The idea is that we’d have plugins that listen to certain events (such as selectionchange) and would calculate state and store that within the editor for consumption in toolbar items or other. See below for a rough example. In this story we add a store and a mechanism for observing the store. This means being able to register an observer and having an api to api state in the store. The store is a pojo containing observables (these are key/value pairs). - Consider using https://rxjs.dev/guide/overview which already provides tooling for observables. 1. Provide a statePlugin that calculates basic style state This is a piece of code that listens to basic events (such as selectionchange) and then calculates whether the current selection is bold, italic, underlined or striked through 1. Create a bold command(‘makeBold’) ```js editor.executeCommand(‘makeSelectionBold’) editor.executeCommand(‘enableBold’) ``` 1. create a toolbar that renders components loaded from a configuration file the components receive the editor as argument. 1. Create a basic styles (bold, italic,underline, strikethrough) toolbar item, replicating current behaviour. 1. Improve basic styles based on events offered by rawEditor 1. Create a list (ordered list, unordered list, indent, unindent) plugin # Sample Code ```js //the component class BoldButton { @tracked isActive; editor constructor(){ This.editor = this.args.editor; editor.observe(‘inList’, ( { ...} ) => { this.isActive = newState; } } //per state definition of callback arguments @action toggleBold(){ this.editor.executeCommand(‘enableBold’) } } } ``` ```js class Plugin { constructor(editor){ this.editor = editor; editor.register('onChange', this.fancyCalcuculator) } function fancyCalculator(event){ // this.editor.setState('inList', true); } } ``` ```js class Editor { //state must be mutually exclusive stateStore: { 'inList': true } listeners: [] //for plugins observers: [] // for e.g. components registerListener(eventType, callback){ } registerObserver(observable, callBack){ } setState(string, bool){ //case string == inList for(obs of observers){ obs.notify(); } } } ```