diff --git a/client/src/app/markleft/markleft.html b/client/src/app/markleft/markleft.html index 5699a3f..97bab6f 100644 --- a/client/src/app/markleft/markleft.html +++ b/client/src/app/markleft/markleft.html @@ -1,7 +1,4 @@ -
- - -
+
diff --git a/client/src/app/markleft/markleft.ts b/client/src/app/markleft/markleft.ts index cda9e87..a089383 100644 --- a/client/src/app/markleft/markleft.ts +++ b/client/src/app/markleft/markleft.ts @@ -1,18 +1,24 @@ import { Component, ElementRef, Renderer2, ViewChild } from '@angular/core'; -import { ReactiveFormsModule, FormsModule, FormControl, FormGroup, FormBuilder } from '@angular/forms'; +import { FormsModule } from '@angular/forms' // type descriptions +// a formatDescriptor contains information for the renderer to apply formatting styles to a node interface formatDescriptor { className: string; }; +// a textNode is an element produced by the parser to link text to applied format specifiers. The renderer will +// apply each style to the text in the node according to the styles registry. interface textNode { type: string; text: string; styles: string[] } +// a blockNode is an element produced by the parser to structure nodes by blocks. blocks contain child nodes, either +// other blocks or text nodes at the bottom. A block node's html element type is described by kind: p, h1, h2, div, etc +// Maybe we blocks can also have styles, and then we can join textNode and blockNode into a single type. interface blockNode { type: string; kind: string; @@ -20,6 +26,7 @@ interface blockNode { } // styles registry: this maps format specifiers to css classes +// might move this to its own file const styles: Record = { B: { className: "format-bold" @@ -35,9 +42,10 @@ const styles: Record = { }, }; // i.e. %B{...} will apply "format-bold" +// angular component class @Component({ selector: 'app-markleft', - imports: [FormsModule, ReactiveFormsModule], + imports: [FormsModule], templateUrl: './markleft.html', styleUrl: './markleft.scss', }) @@ -45,41 +53,36 @@ export class Markleft { @ViewChild('markup_container', { static: true }) container!: ElementRef; - constructor(private renderer: Renderer2, private fb: FormBuilder) { - console.log("component constructor"); + constructor(private renderer: Renderer2) { + // console.log("component constructor"); } ngOnInit() { // DOM manipulation can't occur in constructor - - this.markupInput = this.fb.group({ - inputText: [this.liveText], - }); - this.refresh(); } - markupInput = new FormGroup({ - inputText: new FormControl("") - }); - - onSubmit() { + // live update on each text input. + onTextUpdate(event: Event) { this.refresh(); } - // full demo right here + // default text liveText: string = ` #H{Hello World!} #P{ - This is some markleft text. This is %I{italic} and this is %B{bold}. Some might %I{even be %B{both}}. + This is some %B{PhAML} (%I{%B{Ph}ish %B{A}mazing %B{M}arkup %B{L}anguage}) text. This is %I{italic} and this is %B{bold}. Some might %I{even be %B{both}}. } This should be plain and unstylized. `; // TODO: live editor + // parses a single string into an array of nodes + // like noted at parseStyle, maybe can return a single node which itself contains the array of children. parsePlaintext(input: string): (textNode | blockNode)[] { - let index: number = 0; + let index: number = 0; // position in the input string let nodes: (textNode | blockNode)[] = []; + // TODO: I might restructure the node parsing so that it starts with a generic top level node and can process all children under that node function parseStyle(parentNode: blockNode | null = null, parentStyles: string[] | null = null) { index++; // step onto specifier code @@ -104,19 +107,11 @@ export class Markleft { if(input[index] === "%") { // encountered another style specifier // commit the current node - const node: textNode = { - type: "text", - text: text, - styles: styles - }; - if(parentNode) { - parentNode.children.push(node); - } else { - nodes.push(node); - } + addTextNode(text, styles, parentNode); text = ""; // clear the already commited text // and then move to the next parseStyle(parentNode ? parentNode : null, styles); // recursion + } else if(input[index] == "}") { // this node has closed addTextNode(text, styles, parentNode); //index++; // step to next @@ -127,6 +122,9 @@ export class Markleft { text += input[index]; } } + if(index >= input.length) { + break; + } } } @@ -152,15 +150,7 @@ export class Markleft { index++; if(input[index] === "%" || input[index] === "#") { // encountered another format specifier - // commit current plain text to the block - if(text.length !== 0) { - const textNode: textNode = { - type: "text", - text: text, - styles: [] // none - }; - node.children.push(textNode); - } + addTextNode(text, null, node); text = ""; // clear the already commited text // and then move to the next @@ -188,6 +178,9 @@ export class Markleft { text += input[index]; } } + if(index >= input.length) { + break; + } } } @@ -297,23 +290,22 @@ export class Markleft { } refresh() { - if(this.markupInput.value.inputText) { - // clear children - const parent = this.container.nativeElement; - while (parent.firstChild) { - this.renderer.removeChild(parent, parent.firstChild); - } - - // parse text - const nodes: (textNode | blockNode)[] = this.parsePlaintext(this.markupInput.value.inputText); - - // console.log(nodes) - - // render nodes - nodes.forEach((node) => { - this.renderNode(node); - }); + // clear children + const parent = this.container.nativeElement; + while (parent.firstChild) { + this.renderer.removeChild(parent, parent.firstChild); } + + // parse text + const nodes: (textNode | blockNode)[] = this.parsePlaintext(this.liveText); + + // console.log(nodes) + + // render nodes + nodes.forEach((node) => { + this.renderNode(node); + }); + } }