diff options
Diffstat (limited to 'src/client/views/nodes/DiagramBox.tsx')
-rw-r--r-- | src/client/views/nodes/DiagramBox.tsx | 619 |
1 files changed, 515 insertions, 104 deletions
diff --git a/src/client/views/nodes/DiagramBox.tsx b/src/client/views/nodes/DiagramBox.tsx index 32969fa53..5a712b8b0 100644 --- a/src/client/views/nodes/DiagramBox.tsx +++ b/src/client/views/nodes/DiagramBox.tsx @@ -1,11 +1,13 @@ +/* eslint-disable prettier/prettier */ +/* eslint-disable jsx-a11y/control-has-associated-label */ import mermaid from 'mermaid'; -import { action, makeObservable, observable, reaction } from 'mobx'; +import { action, makeObservable, observable, reaction, computed } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; import { Doc, DocListCast } from '../../../fields/Doc'; import { List } from '../../../fields/List'; import { RichTextField } from '../../../fields/RichTextField'; -import { DocCast, NumCast } from '../../../fields/Types'; +import { DocCast, BoolCast } from '../../../fields/Types'; import { GPTCallType, gptAPICall } from '../../apis/gpt/GPT'; import { DocumentType } from '../../documents/DocumentTypes'; import { Docs } from '../../documents/Documents'; @@ -15,6 +17,16 @@ import { ViewBoxAnnotatableComponent } from '../DocComponent'; import { InkingStroke } from '../InkingStroke'; import './DiagramBox.scss'; import { FieldView, FieldViewProps } from './FieldView'; +import { PointData } from '../../../pen-gestures/GestureTypes'; +import { InkField } from '../../../fields/InkField'; + +enum menuState { + option, + mermaidCode, + drawing, + gpt, + justCreated, +} @observer export class DiagramBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { @@ -27,60 +39,97 @@ export class DiagramBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { super(props); makeObservable(this); } - + @observable menuState = menuState.justCreated; + @observable renderDiv: React.ReactNode; @observable inputValue = ''; + @observable createInputValue = ''; @observable loading = false; @observable errorMessage = ''; @observable mermaidCode = ''; + @observable isExampleMenuOpen = false; - @action handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => { + @action handleInputChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => { this.inputValue = e.target.value; + console.log(e.target.value); }; async componentDidMount() { this._props.setContentViewBox?.(this); mermaid.initialize({ securityLevel: 'loose', startOnLoad: true, - flowchart: { useMaxWidth: true, htmlLabels: true, curve: 'cardinal' }, + darkMode: true, + flowchart: { useMaxWidth: false, htmlLabels: true, curve: 'cardinal' }, + gantt: { useMaxWidth: true, useWidth: 2000 }, }); - this.mermaidCode = 'asdasdasd'; - const docArray: Doc[] = DocListCast(this.Document.data); - let mermaidCodeDoc = docArray.filter(doc => doc.type === 'rich text'); - mermaidCodeDoc = mermaidCodeDoc.filter(doc => (doc.text as RichTextField).Text === 'mermaidCodeTitle'); - if (mermaidCodeDoc[0]) { - if (typeof mermaidCodeDoc[0].title === 'string') { - console.log(mermaidCodeDoc[0].title); - if (mermaidCodeDoc[0].title !== '') { - this.renderMermaidAsync(mermaidCodeDoc[0].title); - } - } + if (!this.Document.testValue) { + this.Document.height = 500; + this.Document.width = 500; } - // this will create a text doc far away where the user cant to save the mermaid code, where it will then be accessed when flipped to the diagram box side - // the code is stored in the title since it is much easier to change than in the text - else { - DocumentManager.Instance.AddViewRenderedCb(this.Document, docViewForYourCollection => { - if (docViewForYourCollection && docViewForYourCollection.ComponentView) { - if (docViewForYourCollection.ComponentView.addDocument && docViewForYourCollection.ComponentView.removeDocument) { - const newDoc = Docs.Create.TextDocument('mermaidCodeTitle', { title: '', x: 9999 + NumCast(this.layoutDoc._width), y: 9999 }); - docViewForYourCollection.ComponentView?.addDocument(newDoc); - } - } - }); + this.Document.testValue = 'a'; + this.mermaidCode = 'a'; + if (typeof this.Document.drawingMermaidCode === 'string' && this.Document.menuState === 'drawing') { + this.renderMermaidAsync(this.Document.drawingMermaidCode); } - console.log(this.Document.title); // this is so that ever time a new doc, text node or ink node, is created, this.createMermaidCode will run which will create a save - reaction( - () => DocListCast(this.Document.data), - () => this.convertDrawingToMermaidCode(), - { fireImmediately: true } - ); + // reaction( + // () => DocListCast(this.Document.data), + // () => this.lockInkStroke(), + // { fireImmediately: true } + // ); + // reaction( + // () => + // DocListCast(this.Document.data) + // .filter(doc => doc.type === 'rich text') + // .map(doc => (doc.text as RichTextField).Text), + // () => this.convertDrawingToMermaidCode(), + // { fireImmediately: true } + // ); + // const rectangleXValues = computed(() => + // DocListCast(this.Document.data) + // .filter(doc => doc.title === 'rectangle') + // .map(doc => doc.x) + // ); + // reaction( + // () => rectangleXValues.get(), + // () => this.lockInkStroke(), + // { fireImmediately: true } + // ); + this.lockInkStroke(); + } + + componentDidUpdate = () => { + if (typeof this.Document.drawingMermaidCode === 'string' && this.Document.menuState === 'drawing') { + this.renderMermaidAsync(this.Document.drawingMermaidCode); + } + if (typeof this.Document.gptMermaidCode === 'string' && this.Document.menuState === 'gpt') { + this.renderMermaidAsync(this.Document.gptMermaidCode); + } + }; + switchRenderDiv() { + switch (this.Document.menuState) { + case 'option': + this.renderDiv = this.renderOption(); + break; + case 'drawing': + this.renderDiv = this.renderDrawing(); + break; + case 'gpt': + this.renderDiv = this.renderGpt(); + break; + case 'mermaidCode': + this.renderDiv = this.renderMermaidCode(); + break; + default: + this.menuState = menuState.option; + this.renderDiv = this.renderOption(); + } } renderMermaid = async (str: string) => { try { const { svg, bindFunctions } = await this.mermaidDiagram(str); return { svg, bindFunctions }; } catch (error) { - console.error('Error rendering mermaid diagram:', error); + // console.error('Error rendering mermaid diagram:', error); return { svg: '', bindFunctions: undefined }; } }; @@ -92,6 +141,7 @@ export class DiagramBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { const dashDiv = document.getElementById('dashDiv' + this.Document.title); if (dashDiv) { dashDiv.innerHTML = svg; + // this.changeHeightWidth(svg); if (bindFunctions) { bindFunctions(dashDiv); } @@ -100,51 +150,51 @@ export class DiagramBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { console.error('Error rendering Mermaid:', error); } } + changeHeightWidth(svgString: string) { + const pattern = /width="([\d.]+)"\s*height="([\d.]+)"/; + + const match = svgString.match(pattern); + + if (match) { + const width = parseFloat(match[1]); + const height = parseFloat(match[2]); + console.log(width); + console.log(height); + this.Document.width = width; + this.Document.height = height; + } + } @action handleRenderClick = () => { - this.generateMermaidCode(); + this.mermaidCode = ''; + if (this.inputValue) { + this.generateMermaidCode(); + } }; @action async generateMermaidCode() { console.log('Generating Mermaid Code'); + const dashDiv = document.getElementById('dashDiv' + this.Document.title); + if (dashDiv) { + dashDiv.innerHTML = ''; + } this.loading = true; let prompt = ''; - // let docArray: Doc[] = DocListCast(this.Document.data); - // let mermaidCodeDoc = docArray.filter(doc => doc.type == 'rich text') - // mermaidCodeDoc=mermaidCodeDoc.filter(doc=>(doc.text as RichTextField).Text=='mermaidCodeTitle') - // if(mermaidCodeDoc[0]){ - // console.log(mermaidCodeDoc[0].title) - // if(typeof mermaidCodeDoc[0].title=='string'){ - // console.log(mermaidCodeDoc[0].title) - // if(mermaidCodeDoc[0].title!=""){ - // prompt="Edit this code "+this.inputValue+": "+mermaidCodeDoc[0].title - // console.log("you have to see me") - // } - // } - // } - // else{ prompt = 'Write this in mermaid code and only give me the mermaid code: ' + this.inputValue; - console.log('there is no text save'); // } const res = await gptAPICall(prompt, GPTCallType.MERMAID); - this.loading = false; + this.loading = true; if (res === 'Error connecting with API.') { // If GPT call failed console.error('GPT call failed'); this.errorMessage = 'GPT call failed; please try again.'; } else if (res !== null) { // If GPT call succeeded, set htmlCode;;; TODO: check if valid html - if (this.isValidCode(res)) { - this.mermaidCode = res; - console.log('GPT call succeeded:' + res); - this.errorMessage = ''; - } else { - console.error('GPT call succeeded but invalid html; please try again.'); - this.errorMessage = 'GPT call succeeded but invalid html; please try again.'; - } + this.mermaidCode = res; + console.log('GPT call succeeded:' + res); + this.errorMessage = ''; } this.renderMermaidAsync.call(this, this.removeWords(this.mermaidCode)); - this.loading = false; + this.Document.gptMermaidCode = this.removeWords(this.mermaidCode); } - isValidCode = (html: string) => true; removeWords(inputStrIn: string) { const inputStr = inputStrIn.replace('```mermaid', ''); return inputStr.replace('```', ''); @@ -164,10 +214,12 @@ export class DiagramBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { }); await timeoutPromise(); const inkStrokeArray = lineArray.map(doc => DocumentManager.Instance.getDocumentView(doc, this.DocumentView?.())).filter(inkView => inkView?.ComponentView instanceof InkingStroke); - console.log(inkStrokeArray.length); - console.log(lineArray.length); if (inkStrokeArray[0] && inkStrokeArray.length === lineArray.length) { - mermaidCode = 'graph TD;'; + // if (this.isLeftRightDiagram(docArray)) { + // mermaidCode = 'graph LR;'; + // } else { + // mermaidCode = 'graph TD;'; + // } const inkingStrokeArray = inkStrokeArray.map(stroke => stroke?.ComponentView); for (let i = 0; i < rectangleArray.length; i++) { const rectangle = rectangleArray[i]; @@ -182,8 +234,6 @@ export class DiagramBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { ?.inkScaledData() .inkData.map(coord => coord.Y) .map(doc => doc * inkScaleY); - console.log(inkingStrokeArray.length); - console.log(lineArray.length); // need to minX and minY to since the inkStroke.x and.y is not relative to the doc. so I have to do some calcluations const minX: number = Math.min(...inkStrokeXArray); const minY: number = Math.min(...inkStrokeYArray); @@ -197,12 +247,11 @@ export class DiagramBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { if (this.isPointInBox(rectangle2, [endX, endY]) && typeof rectangle.x === 'number' && typeof rectangle2.x === 'number') { diagramExists = true; const linkedDocs: Doc[] = LinkManager.Instance.getAllRelatedLinks(lineArray[j]).map(d => DocCast(LinkManager.getOppositeAnchor(d, lineArray[j]))); - console.log(linkedDocs.length); if (linkedDocs.length !== 0) { const linkedText = (linkedDocs[0].text as RichTextField).Text; - mermaidCode += Math.abs(rectangle.x) + this.getTextInBox(rectangle, textArray) + '-->|' + linkedText + '|' + Math.abs(rectangle2.x) + this.getTextInBox(rectangle2, textArray) + ';'; + mermaidCode += Math.abs(rectangle.x) + this.getTextInBox(rectangle, textArray) + '---|' + linkedText + '|' + Math.abs(rectangle2.x) + this.getTextInBox(rectangle2, textArray) + ';'; } else { - mermaidCode += Math.abs(rectangle.x) + this.getTextInBox(rectangle, textArray) + '-->' + Math.abs(rectangle2.x) + this.getTextInBox(rectangle2, textArray) + ';'; + mermaidCode += Math.abs(rectangle.x) + this.getTextInBox(rectangle, textArray) + '---' + Math.abs(rectangle2.x) + this.getTextInBox(rectangle2, textArray) + ';'; } } } @@ -210,35 +259,166 @@ export class DiagramBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { } } // this will save the text - DocumentManager.Instance.AddViewRenderedCb(this.Document, docViewForYourCollection => { - if (docViewForYourCollection && docViewForYourCollection.ComponentView) { - if (docViewForYourCollection.ComponentView.addDocument && docViewForYourCollection.ComponentView.removeDocument) { - let docs: Doc[] = DocListCast(this.Document.data); - docs = docs.filter(doc => doc.type === 'rich text'); - const mermaidCodeDoc = docs.filter(doc => (doc.text as RichTextField).Text === 'mermaidCodeTitle'); - if (mermaidCodeDoc[0]) { - if (diagramExists) { - mermaidCodeDoc[0].title = mermaidCode; - } else { - mermaidCodeDoc[0].title = ''; - } - } - } - } - }); + if (diagramExists) { + this.Document.drawingMermaidCode = mermaidCode; + } else { + this.Document.drawingMermaidCode = ''; + } } } } - testInkingStroke = () => { + async lockInkStroke() { + console.log('hello'); + console.log( + DocListCast(this.Document.data) + .filter(doc => doc.title === 'rectangle') + .map(doc => doc.x) + ); if (this.Document.data instanceof List) { const docArray: Doc[] = DocListCast(this.Document.data); + const rectangleArray = docArray.filter(doc => doc.title === 'rectangle' || doc.title === 'circle'); + if (rectangleArray[0]) { + console.log(rectangleArray[0].x); + } const lineArray = docArray.filter(doc => doc.title === 'line' || doc.title === 'stroke'); - setTimeout(() => { - const inkStrokeArray = lineArray.map(doc => DocumentManager.Instance.getDocumentView(doc, this.DocumentView?.())).filter(inkView => inkView?.ComponentView instanceof InkingStroke); - console.log(inkStrokeArray); - }); + const timeoutPromise = () => + new Promise(resolve => { + setTimeout(resolve, 0); + }); + await timeoutPromise(); + const inkStrokeArray = lineArray.map(doc => DocumentManager.Instance.getDocumentView(doc, this.DocumentView?.())).filter(inkView => inkView?.ComponentView instanceof InkingStroke); + const inkingStrokeArray = inkStrokeArray.map(stroke => stroke?.ComponentView); + for (let j = 0; j < lineArray.length; j++) { + const inkScaleX = (inkingStrokeArray[j] as InkingStroke)?.inkScaledData().inkScaleX; + const inkScaleY = (inkingStrokeArray[j] as InkingStroke)?.inkScaledData().inkScaleY; + const inkStrokeXArray = (inkingStrokeArray[j] as InkingStroke) + ?.inkScaledData() + .inkData.map(coord => coord.X) + .map(doc => doc * inkScaleX); + const inkStrokeYArray = (inkingStrokeArray[j] as InkingStroke) + ?.inkScaledData() + .inkData.map(coord => coord.Y) + .map(doc => doc * inkScaleY); + // need to minX and minY to since the inkStroke.x and.y is not relative to the doc. so I have to do some calcluations + const minX: number = Math.min(...inkStrokeXArray); + const minY: number = Math.min(...inkStrokeYArray); + const startX = inkStrokeXArray[0] - minX + (lineArray[j]?.x as number); + const startY = inkStrokeYArray[0] - minY + (lineArray[j]?.y as number); + const endX = inkStrokeXArray[inkStrokeXArray.length - 1] - minX + (lineArray[j].x as number); + const endY = inkStrokeYArray[inkStrokeYArray.length - 1] - minY + (lineArray[j].y as number); + let closestStartRect: Doc = lineArray[0]; + let closestStartDistance = 9999999; + let closestEndRect: Doc = lineArray[0]; + let closestEndDistance = 9999999; + rectangleArray.forEach(rectangle => { + const midPoint = this.getMidPoint(rectangle); + if (this.euclideanDistance(midPoint.X, midPoint.Y, startX, startY) < closestStartDistance && this.euclideanDistance(midPoint.X, midPoint.Y, endX, endY) < closestEndDistance) { + if (this.euclideanDistance(midPoint.X, midPoint.Y, startX, startY) < this.euclideanDistance(midPoint.X, midPoint.Y, endX, endY)) { + closestStartDistance = this.euclideanDistance(midPoint.X, midPoint.Y, startX, startY); + closestStartRect = rectangle; + } else { + closestEndDistance = this.euclideanDistance(midPoint.X, midPoint.Y, startX, startY); + closestEndRect = rectangle; + } + } else if (this.euclideanDistance(midPoint.X, midPoint.Y, startX, startY) < closestStartDistance) { + closestStartDistance = this.euclideanDistance(midPoint.X, midPoint.Y, startX, startY); + closestStartRect = rectangle; + } else if (this.euclideanDistance(midPoint.X, midPoint.Y, endX, endY) < closestEndDistance) { + closestEndDistance = this.euclideanDistance(midPoint.X, midPoint.Y, startX, startY); + closestEndRect = rectangle; + } + }); + const inkToDelete: Doc = lineArray[j]; + if ( + typeof closestStartRect.x === 'number' && + typeof closestStartRect.y === 'number' && + typeof closestEndRect.x === 'number' && + typeof closestEndRect.y === 'number' && + typeof closestStartRect.width === 'number' && + typeof closestStartRect.height === 'number' && + typeof closestEndRect.height === 'number' && + typeof closestEndRect.width === 'number' + ) { + const points: PointData[] = [ + { X: closestStartRect.x, Y: closestStartRect.y }, + { X: closestStartRect.x, Y: closestStartRect.y }, + { X: closestEndRect.x, Y: closestEndRect.y }, + { X: closestEndRect.x, Y: closestEndRect.y }, + ]; + let inkX = 0; + let inkY = 0; + if (this.getMidPoint(closestEndRect).X < this.getMidPoint(closestStartRect).X) { + inkX = this.getMidPoint(closestEndRect).X; + } else { + inkX = this.getMidPoint(closestStartRect).X; + } + if (this.getMidPoint(closestEndRect).Y < this.getMidPoint(closestStartRect).Y) { + inkY = this.getMidPoint(closestEndRect).Y; + } else { + inkY = this.getMidPoint(closestStartRect).Y; + } + const newInkDoc = Docs.Create.AudioDocument(''); // get rid of this!! + // const newInkDoc:Doc=Docs.Create.InkDocument( + // points, + // { title: 'stroke', + // x: inkX, + // y: inkY, + // strokeWidth: Math.abs(closestEndRect.x+closestEndRect.width/2-closestStartRect.x-closestStartRect.width/2), + // _height: Math.abs(closestEndRect.y+closestEndRect.height/2-closestStartRect.y-closestStartRect.height/2), + // stroke_showLabel: BoolCast(Doc.UserDoc().activeInkHideTextLabels)}, // prettier-ignore + // 1) + + DocumentManager.Instance.AddViewRenderedCb(this.Document, docViewForYourCollection => { + if (docViewForYourCollection && docViewForYourCollection.ComponentView) { + if (docViewForYourCollection.ComponentView.addDocument && docViewForYourCollection.ComponentView.removeDocument) { + docViewForYourCollection.ComponentView?.removeDocument(inkToDelete); + docViewForYourCollection.ComponentView?.addDocument(newInkDoc); + + // const bruh2= DocListCast(this.Document.data).filter(doc => doc.title === 'line' || doc.title === 'stroke').map(doc => DocumentManager.Instance.getDocumentView(doc, this.DocumentView?.())).filter(inkView => inkView?.ComponentView instanceof InkingStroke).map(stroke => stroke?.ComponentView); + // console.log(bruh2) + // console.log((bruh2[0] as InkingStroke)?.inkScaledData()) + } + } + }); + } + } } - }; + } + getMidPoint(rectangle: Doc) { + let midPoint = { X: 0, Y: 0 }; + if (typeof rectangle.x === 'number' && typeof rectangle.width === 'number' && typeof rectangle.y === 'number' && typeof rectangle.height === 'number') { + midPoint = { X: rectangle.x + rectangle.width / 2, Y: rectangle.y + rectangle.height / 2 }; + } + return midPoint; + } + euclideanDistance(x1: number, y1: number, x2: number, y2: number): number { + const deltaX = x2 - x1; + const deltaY = y2 - y1; + return Math.sqrt(deltaX * deltaX + deltaY * deltaY); + } + // isLeftRightDiagram = (docArray: Doc[]) => { + // const filteredDocs = docArray.filter(doc => doc.title === 'rectangle' || doc.title === 'circle'); + // const xDoc = filteredDocs.map(doc => doc.x) as number[]; + // const minX = Math.min(...xDoc); + // const xWidthDoc = filteredDocs.map(doc => { + // if (typeof doc.x === 'number' && typeof doc.width === 'number') { + // return doc.x + doc.width; + // } + // }) as number[]; + // const maxX = Math.max(...xWidthDoc); + // const yDoc = filteredDocs.map(doc => doc.y) as number[]; + // const minY = Math.min(...yDoc); + // const yHeightDoc = filteredDocs.map(doc => { + // if (typeof doc.x === 'number' && typeof doc.width === 'number') { + // return doc.x + doc.width; + // } + // }) as number[]; + // const maxY = Math.max(...yHeightDoc); + // if (maxX - minX > maxY - minY) { + // return true; + // } + // return false; + // }; getTextInBox = (box: Doc, richTextArray: Doc[]): string => { for (let i = 0; i < richTextArray.length; i++) { const textDoc = richTextArray[i]; @@ -261,31 +441,262 @@ export class DiagramBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { } return false; }; + drawingButton = () => { + this.Document.menuState = 'drawing'; + }; + gptButton = () => { + this.Document.menuState = 'gpt'; + }; + mermaidButton = () => { + this.Document.menuState = 'mermaidCode'; + }; + optionButton = () => { + this.Document.menuState = 'option'; + }; + renderOption(): React.ReactNode { + return ( + <div className="buttonCollections"> + <button type="button" onClick={this.drawingButton}> + Drawing - Create diagram from ink drawing + </button> + <button type="button" onClick={this.gptButton}> + GPT - Generate diagram with AI prompt + </button> + <button type="button" onClick={this.mermaidButton}> + Mermaid Editor - Create diagram with mermaid code + </button> + </div> + ); + } + renderDrawing(): React.ReactNode { + return ( + <div ref={this._dragRef} className="DiagramBox-wrapper"> + <div className="content"> + <div className="topBar"> + <button className="backButtonDrawing" type="button" onClick={this.optionButton}> + Back + </button> + {!this.Document.mermaidCode && <p>Click the red pen icon to flip onto the collection side and draw a diagram with ink</p>} + </div> + <div id={'dashDiv' + this.Document.title} className="diagramBox" /> + </div> + </div> + ); + } - render() { + renderGpt(): React.ReactNode { return ( - <div ref={this._ref} className="DIYNodeBox"> - <div ref={this._dragRef} className="DIYNodeBox-wrapper"> + <div ref={this._dragRef} className="DiagramBox-wrapper"> + <div className="content"> <div className="search-bar"> - <input type="text" value={this.inputValue} onChange={this.handleInputChange} /> - <button type="button" onClick={this.handleRenderClick}> - Generate + <button className="backButton" type="button" onClick={this.optionButton}> + Back </button> + <textarea value={this.inputValue} placeholder="Enter GPT prompt" onChange={this.handleInputChange} onInput={e => this.autoResize(e.target as HTMLTextAreaElement)} /> + <div className="rightButtons"> + <button className="generateButton" type="button" onClick={this.handleRenderClick}> + Generate + </button> + <button className="convertButton" type="button" onClick={this.handleConvertButton}> + Edit + </button> + </div> </div> - <div className="content"> - {this.mermaidCode ? ( - <div id={'dashDiv' + this.Document.title} className="diagramBox" /> - ) : ( - <div>{this.loading ? <div className="loading-circle" /> : <div>{this.errorMessage ? this.errorMessage : 'Insert prompt to generate diagram'}</div>}</div> - )} + {this.mermaidCode ? ( + <div id={'dashDiv' + this.Document.title} className="diagramBox" /> + ) : ( + <div>{this.loading ? <div className="loading-circle" /> : <div>{this.errorMessage ? this.errorMessage : 'Insert prompt to generate diagram'}</div>}</div> + )} + </div> + </div> + ); + } + handleConvertButton = () => { + this.Document.menuState = 'mermaidCode'; + if (typeof this.Document.gptMermaidCode === 'string') { + this.createInputValue = this.removeFirstEmptyLine(this.Document.gptMermaidCode); + console.log(this.Document.gptMermaidCode); + this.renderMermaidAsync(this.Document.gptMermaidCode); + } + }; + removeFirstEmptyLine(input: string): string { + const lines = input.split('\n'); + let emptyLineRemoved = false; + const resultLines = lines.filter(line => { + if (!emptyLineRemoved && line.trim() === '') { + emptyLineRemoved = true; + return false; + } + return true; + }); + return resultLines.join('\n'); + } + + renderMermaidCode(): React.ReactNode { + return ( + <div ref={this._dragRef} className="DiagramBox-wrapper"> + <div className="contentCode"> + <div className="search-bar"> + <button className="backButton" type="button" onClick={this.optionButton}> + Back + </button> + <button className="exampleButton" type="button" onClick={this.exampleButton}> + Examples + </button> </div> + {this.isExampleMenuOpen && ( + <div className="exampleButtonContainer"> + <button type="button" onClick={this.flowButton}> + Flow + </button> + <button type="button" onClick={this.pieButton}> + Pie + </button> + <button type="button" onClick={this.timelineButton}> + Timeline + </button> + <button type="button" onClick={this.classButton}> + Class + </button> + <button type="button" onClick={this.mindmapButton}> + Mindmap + </button> + </div> + )} + <textarea value={this.createInputValue} placeholder="Enter Mermaid Code" onChange={this.handleInputChangeEditor} /> + <div id={'dashDiv' + this.Document.title} className="diagramBox" /> </div> </div> ); } + exampleButton = () => { + if (this.isExampleMenuOpen) { + this.isExampleMenuOpen = false; + } else { + this.isExampleMenuOpen = true; + } + }; + flowButton = () => { + this.createInputValue = `flowchart TD + A[Christmas] -->|Get money| B(Go shopping) + B --> C{Let me think} + C -->|One| D[Laptop] + C -->|Two| E[iPhone] + C -->|Three| F[fa:fa-car Car]`; + this.renderMermaidAsync(this.createInputValue); + }; + pieButton = () => { + this.createInputValue = `pie title Pets adopted by volunteers + "Dogs" : 386 + "Cats" : 85 + "Rats" : 15`; + this.renderMermaidAsync(this.createInputValue); + }; + timelineButton = () => { + this.createInputValue = `gantt + title A Gantt Diagram + dateFormat YYYY-MM-DD + section Section + A task :a1, 2014-01-01, 30d + Another task :after a1 , 20d + section Another + Task in sec :2014-01-12 , 12d + another task : 24d`; + this.renderMermaidAsync(this.createInputValue); + }; + classButton = () => { + this.createInputValue = `classDiagram + Animal <|-- Duck + Animal <|-- Fish + Animal <|-- Zebra + Animal : +int age + Animal : +String gender + Animal: +isMammal() + Animal: +mate() + class Duck{ + +String beakColor + +swim() + +quack() + } + class Fish{ + -int sizeInFeet + -canEat() + } + class Zebra{ + +bool is_wild + +run() + }`; + this.renderMermaidAsync(this.createInputValue); + }; + mindmapButton = () => { + this.createInputValue = `mindmap + root((mindmap)) + Origins + Long history + ::icon(fa fa-book) + Popularisation + British popular psychology author Tony Buzan + Research + On effectivness<br/>and features + On Automatic creation + Uses + Creative techniques + Strategic planning + Argument mapping + Tools + Pen and paper + Mermaid`; + this.renderMermaidAsync(this.createInputValue); + }; + handleInputChangeEditor = (e: React.ChangeEvent<HTMLTextAreaElement>) => { + if (typeof e.target.value === 'string') { + this.createInputValue = e.target.value; + this.renderMermaidAsync(e.target.value); + } + }; + removeWhitespace(str: string): string { + return str.replace(/\s+/g, ''); + } + autoResize(element: HTMLTextAreaElement): void { + element.style.height = '5px'; + element.style.height = element.scrollHeight + 'px'; + } + timeline = `gantt + title College Timeline + dateFormat YYYY-MM-DD + section Semester 1 + Orientation :done, des1, 2023-08-01, 2023-08-03 + Classes Start :active, des2, 2023-08-04, 2023-12-15 + Midterm Exams : des3, 2023-10-15, 2023-10-20 + End of Semester : des4, 2023-12-16, 2023-12-20 + section Semester 2 + Classes Start : des5, 2024-01-10, 2024-05-15 + Spring Break : des6, 2024-03-15, 2024-03-22 + Midterm Exams : des7, 2024-03-25, 2024-03-30 + Final Exams : des8, 2024-05-10, 2024-05-15 + section Summer Break + Internship : des9, 2024-06-01, 2024-08-31 + section Semester 3 + Classes Start : des10, 2024-09-01, 2025-12-15 + Midterm Exams : des11, 2024-11-15, 2024-11-20 + End of Semester : des12, 2025-12-16, 2025-12-20 + section Semester 4 + Classes Start : des13, 2025-01-10, 2025-05-15 + Spring Break : des14, 2025-03-15, 2025-03-22 + Midterm Exams : des15, 2025-03-25, 2025-03-30 + Final Exams : des16, 2025-05-10, 2025-05-15 + Graduation : des17, 2025-05-20, 2025-05-21`; + render() { + this.switchRenderDiv(); + return ( + <div ref={this._ref} className="DiagramBox"> + {this.renderDiv} + </div> + ); + } } Docs.Prototypes.TemplateMap.set(DocumentType.DIAGRAM, { layout: { view: DiagramBox, dataField: 'dadta' }, - options: { _height: 300, _layout_fitWidth: true, _layout_nativeDimEditable: true, _layout_reflowVertical: true, waitForDoubleClickToClick: 'always', systemIcon: 'BsGlobe' }, + options: { _height: 700, _width: 700, _layout_fitWidth: false, _layout_nativeDimEditable: true, _layout_reflowVertical: true, waitForDoubleClickToClick: 'always', _layout_reflowHorizontal: true, systemIcon: 'BsGlobe' }, }); |