aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/nodes/DiagramBox.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/views/nodes/DiagramBox.tsx')
-rw-r--r--src/client/views/nodes/DiagramBox.tsx619
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' },
});