import { EditorComponent, ImageTool, Tool, editor } from 'edkit'; import { computed, observable, observableShallow } from 'mobx'; import { WebCellProps, WebField, component, formField, observer, reaction } from 'web-cell'; import { Constructor, parseDOM } from 'web-utility'; import { AudioTool, DefaultTools, VideoTool } from './tools'; const shallow = observableShallow || Reflect.get(observable, 'shallow', observable); export interface EditorProps extends Pick< WebCellProps, 'name' | 'defaultValue' > { tools?: Constructor[]; onChange?: (event: CustomEvent) => any; } export interface HTMLEditor extends WebField, EditorComponent {} @component({ tagName: 'html-editor', mode: 'open' }) @formField @observer @editor export class HTMLEditor extends HTMLElement implements WebField, EditorComponent { @shallow accessor tools: EditorProps['tools']; @observable accessor toolList: Tool[] = []; @computed get imageTool() { return this.toolList.find( tool => tool instanceof ImageTool ) as ImageTool; } @computed get audioTool() { return this.toolList.find( tool => tool instanceof AudioTool ) as AudioTool; } @computed get videoTool() { return this.toolList.find( tool => tool instanceof VideoTool ) as VideoTool; } protected box: HTMLElement; mountedCallback() { this.bootTools(); this.box = parseDOM( `
${this.defaultValue || ''}
` )[0] as HTMLElement; this.box.oninput = () => this.updateValue(this.box.innerHTML); this.box.onpaste = this.box.ondrop = this.handlePasteDrop; this.append(this.box); document.addEventListener('selectionchange', this.updateTools); } disconnectedCallback() { document.removeEventListener('selectionchange', this.updateTools); } @reaction(({ tools }) => tools) bootTools() { this.toolList = (this.tools || DefaultTools).map( ToolButton => new ToolButton() ); } updateTools = () => { if (this.box === document.activeElement) this.toolList = [...this.toolList]; }; updateValue = (markup: string) => this.emit('change', (this.value = markup.trim())); render() { return ( <>
{this.toolList.map(tool => tool.render(this.box))}
); } }