diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/client/views/nodes/DataVizBox/DataVizBox.tsx | 45 | ||||
-rw-r--r-- | src/client/views/nodes/DataVizBox/DocCreatorMenu.scss | 128 | ||||
-rw-r--r-- | src/client/views/nodes/DataVizBox/DocCreatorMenu.tsx | 147 | ||||
-rw-r--r-- | src/client/views/nodes/ImageBox.tsx | 1 |
4 files changed, 228 insertions, 93 deletions
diff --git a/src/client/views/nodes/DataVizBox/DataVizBox.tsx b/src/client/views/nodes/DataVizBox/DataVizBox.tsx index c5a1da1da..a6d3bfa49 100644 --- a/src/client/views/nodes/DataVizBox/DataVizBox.tsx +++ b/src/client/views/nodes/DataVizBox/DataVizBox.tsx @@ -568,51 +568,6 @@ export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { this.applyLayout(templateInfo, docs); } - generateTemplates = async () => { - try { - const res = await gptAPICall('Please generate for the fields: Image, Description, Divider, Response', GPTCallType.TEMPLATE); - - if (res) { - const templates: {template_type: string, fieldVals: {title: string, tlx: string, tly: string, brx: string, bry: string}[]}[] = JSON.parse(res); - this.createGeneratedTemplates(templates, 500, 500); - //console.log(res); - } - } catch (err) { - console.error(err); - } - - } - - createGeneratedTemplates = (layouts: {template_type: string, fieldVals: {title: string, tlx: string, tly: string, brx: string, bry: string}[]}[], tempWidth: number, tempHeight: number) => { - const mainCollection = this.DocumentView?.().containerViewPath?.().lastElement()?.ComponentView as CollectionFreeFormView; - - layouts.forEach(layout => { - const fields: Doc[] = layout.fieldVals.map(field => { - const left: number = Number(field.tlx) * tempWidth / 2; const top: number = Number(field.tly) * tempHeight / 2; //prettier-ignore - const right: number = Number(field.brx) * tempWidth / 2; const bottom: number = Number(field.bry) * tempHeight / 2; //prettier-ignore - const height = bottom - top; const width = right - left; //prettier-ignore - const doc = !field.title.includes('$$') ? Docs.Create.TextDocument('', { _height: height, _width: width, title: field.title, x: left, y: top, _text_fontSize: `${height/2}` }) : Docs.Create.ImageDocument('', { _height: height, _width: 250, title: field.title.replace(/$$/g, ''), x: left, y: top }); - return doc; - }); - - const template = Docs.Create.FreeformDocument(fields, { _height: tempHeight, _width: tempWidth, title: layout.template_type, x: 400, y: 400 }); - mainCollection.addDocument(template); - }); - } - - createBasicTemplates = (colsToLayout: {width: number, height: number, x: number, y: number, title: string}[]) => { - const mainCollection = this.DocumentView?.().containerViewPath?.().lastElement()?.ComponentView as CollectionFreeFormView; - - const fields: Doc[] = colsToLayout.map(layout => { - const field = Docs.Create.TextDocument('', { _height: layout.height, _width: layout.width, title: layout.title, x: layout.x, y: layout.y }); - return field; - }); - - const template = Docs.Create.FreeformDocument(fields, { _height: 500, _width: 500, title: 'template', x: 400, y: 400 }) - - mainCollection.addDocument(template); - } - /** * creates a new dataviz document filter from this one * it appears to the right of this document, with the diff --git a/src/client/views/nodes/DataVizBox/DocCreatorMenu.scss b/src/client/views/nodes/DataVizBox/DocCreatorMenu.scss index b3bacb13c..e2b4014bd 100644 --- a/src/client/views/nodes/DataVizBox/DocCreatorMenu.scss +++ b/src/client/views/nodes/DataVizBox/DocCreatorMenu.scss @@ -22,14 +22,14 @@ } .docCreatorMenu-menu-button { - width: 30px; - height: 30px; + width: 25px; + height: 25px; background: whitesmoke; - background-color: rgb(34, 34, 37); + background-color: rgb(50, 50, 50); border-radius: 5px; border: 1px solid rgb(180, 180, 180); padding: 0px; - font-size: 14px; + font-size: 13px; //box-shadow: 3px 3px rgb(29, 29, 31); &:hover { @@ -222,7 +222,7 @@ .docCreatorMenu-option-divider { border-top: 1px solid rgb(180, 180, 180); - width: 80%; + width: 95%; margin-top: 10px; margin-bottom: 10px; } @@ -256,6 +256,18 @@ // DocCreatorMenu templates preview CSS //-------------------------------------------------------------------------------------------------------------------------------------------- +.docCreatorMenu-templates-view { + display: flex; + flex-direction: column; + justify-content: center; + align-items: flex-start; + margin: 5px; + margin-top: 0px; + width: calc(100% - 10px); + height: calc(100% - 30px); + border: 1px solid rgb(180, 180, 180); + border-radius: 5px; +} .docCreatorMenu-preview-container { display: grid; @@ -263,20 +275,18 @@ grid-template-rows: 140px; grid-auto-rows: 141px; overflow-y: scroll; - margin: 5px; + margin: 0px; margin-top: 0px; - width: calc(100% - 10px); - height: calc(100% - 30px); - border: 1px solid rgb(180, 180, 180); - border-radius: 5px; + width: 100%; + height: 100%; } .docCreatorMenu-preview-window { display: flex; justify-content: center; align-items: center; - width: 125px; - height: 125px; + width: 113px; + height: 113px; margin-top: 10px; margin-left: 10px; border: 1px solid rgb(163, 163, 163); @@ -295,9 +305,103 @@ &.empty { font-size: 35px; + flex: 0 0 auto; + + &.GPT { + margin-top: 0px; + } + } +} + +.docCreatorMenu-section { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + position: relative; + margin: 0px; + width: 100%; + height: 250; + flex: 0 0 auto; +} + +.docCreatorMenu-GPT-options-container { + display: flex; + flex-direction: row; + justify-content: center; + align-items: center; + position: relative; + width: auto; + margin: 0px; + margin-top: 5px; + padding: 0px; +} + +.docCreatorMenu-GPT-templates-preview { + display: flex; + flex-direction: row; + //justify-content: center; + align-items: center; + overflow-y: scroll; + position: relative; + height: 125px; + width: calc(100% - 10px); + -ms-overflow-style: none; + scrollbar-width: none; +} + +.docCreatorMenu-section-topbar { + position: relative; + display: flex; + flex-direction: row; + width: 100%; + + .section-reveal-options { + margin: 0px; + margin-left: auto; + border: 0px; + background: none; + } +} + +.docCreatorMenu-section-title { + border: 1px solid rgb(163, 163, 163); + border-top: 0px; + border-left: 0px; + border-bottom-right-radius: 5px; + font-size: 12px; + padding: 2px; + padding-left: 3px; + padding-right: 3px; + margin-bottom: 3px; +} + +.docCreatorMenu-GPT-generate { + height: 30px; + width: 30px; + background-color: rgb(176, 229, 149); + border: 1px solid rgb(126, 219, 80); + border-radius: 5px; + padding: 0px; + font-size: 14px; + color: white; + letter-spacing: 1px; + flex: 0 0 auto; + + &:hover { + background-color: rgb(129, 223, 83); + border: 2px solid rgb(80, 185, 28); } } +.docCreatorMenu-GPT-prompt-input { + width: 140px; + overflow-y: scroll; + border: 1px solid rgb(180, 180, 180); + background-color: rgb(35, 35, 35); + border-radius: 3px; +} + //------------------------------------------------------------------------------------------------------------------------------------------ // DocCreatorMenu options CSS //-------------------------------------------------------------------------------------------------------------------------------------------- diff --git a/src/client/views/nodes/DataVizBox/DocCreatorMenu.tsx b/src/client/views/nodes/DataVizBox/DocCreatorMenu.tsx index 411257ff7..781cbcb55 100644 --- a/src/client/views/nodes/DataVizBox/DocCreatorMenu.tsx +++ b/src/client/views/nodes/DataVizBox/DocCreatorMenu.tsx @@ -3,7 +3,7 @@ import { IReactionDisposer, ObservableMap, action, computed, makeObservable, obs import { observer } from 'mobx-react'; import * as React from 'react'; import { returnAll, returnFalse, setupMoveUpEvents } from '../../../../ClientUtils'; -import { Doc, NumListCast } from '../../../../fields/Doc'; +import { Doc, NumListCast, StrListCast } from '../../../../fields/Doc'; import { DocCast, ImageCast } from '../../../../fields/Types'; import { ImageField } from '../../../../fields/URLField'; import { emptyFunction } from '../../../../Utils'; @@ -17,6 +17,9 @@ import { Id } from '../../../../fields/FieldSymbols'; import { Colors, IconButton, Size } from 'browndash-components'; import { MakeTemplate } from '../../../util/DropConverter'; import { DragManager } from '../../../util/DragManager'; +import { GPTCallType, gptAPICall } from '../../../apis/gpt/GPT'; +import { CollectionFreeFormView } from '../../collections/collectionFreeForm/CollectionFreeFormView'; +import { Docs } from '../../../documents/Documents'; export enum LayoutType { Stacked = 'stacked', @@ -41,6 +44,10 @@ export class DocCreatorMenu extends ObservableReactComponent<{}> { @observable _layoutPreviewScale: number = 1; @observable _savedLayouts: DataVizTemplateLayout[] = []; + @observable _GPTTemplates: Doc[] | undefined = undefined; + @observable _GPTOpt: boolean = false; + @observable _userPrompt: string = ''; + @observable _pageX: number = 0; @observable _pageY: number = 0; @observable _indicatorX: number | undefined = undefined; @@ -105,6 +112,10 @@ export class DocCreatorMenu extends ObservableReactComponent<{}> { } } + @computed get selectedFields(){ + return StrListCast(this._dataViz?.layoutDoc._dataViz_axes); + } + @computed get canMakeDocs(){ return this._selectedTemplate !== undefined && this._layout !== undefined; } @@ -254,8 +265,8 @@ export class DocCreatorMenu extends ObservableReactComponent<{}> { if (this._initDimensions.y === undefined) this._initDimensions.y = this._pageY; const {height, width, x, y} = this._initDimensions; - this._menuDimensions.width = Math.max(100, scale.x * width); - this._menuDimensions.height = Math.max(100, scale.y * height); + this._menuDimensions.width = Math.max(300, scale.x * width); + this._menuDimensions.height = Math.max(200, scale.y * height); this._pageX = x + translation.x; this._pageY = y + translation.y; }; @@ -293,10 +304,104 @@ export class DocCreatorMenu extends ObservableReactComponent<{}> { && this._layout.columns === layout.columns; } + generateTemplates = async (inputText: string) => { + let prompt: string = 'Please generate for the fields:'; + this.selectedFields?.forEach(field => prompt += ` ${field},`) + prompt += ` Additional prompt: ${inputText}`; + + try { + const res = await gptAPICall(prompt, GPTCallType.TEMPLATE); + + if (res) { + const templates: {template_type: string, fieldVals: {title: string, tlx: string, tly: string, brx: string, bry: string}[]}[] = JSON.parse(res); + this.createGeneratedTemplates(templates, 500, 500); + } + } catch (err) { + console.error(err); + } + + } + + createGeneratedTemplates = (layouts: {template_type: string, fieldVals: {title: string, tlx: string, tly: string, brx: string, bry: string}[]}[], tempWidth: number, tempHeight: number) => { + const mainCollection = this._dataViz?.DocumentView?.().containerViewPath?.().lastElement()?.ComponentView as CollectionFreeFormView; + + layouts.forEach(layout => { + const fields: Doc[] = layout.fieldVals.map(field => { + const left: number = Number(field.tlx) * tempWidth / 2; const top: number = Number(field.tly) * tempHeight / 2; //prettier-ignore + const right: number = Number(field.brx) * tempWidth / 2; const bottom: number = Number(field.bry) * tempHeight / 2; //prettier-ignore + const height = bottom - top; + const width = right - left; + const doc = !field.title.includes('$$') ? Docs.Create.TextDocument('', { _height: height, _width: width, title: field.title, x: left, y: top, _text_fontSize: `${height/2}` }) : Docs.Create.ImageDocument('', { _height: height, _width: width, title: field.title.replace(/$$/g, ''), x: left, y: top }); + return doc; + }); + + const template = Docs.Create.FreeformDocument(fields, { _height: tempHeight, _width: tempWidth, title: layout.template_type, x: 400, y: 400 }); + mainCollection.addDocument(template); + }); + } + get templatesPreviewContents(){ const renderedTemplates: Doc[] = []; + + const GPTOptions = + <div></div> + + return ( - <div className='docCreatorMenu-preview-container' + <div className='docCreatorMenu-templates-view'> + <div className='docCreatorMenu-section'> + <div className='docCreatorMenu-section-topbar'> + <div className='docCreatorMenu-section-title'>Suggested Templates</div> + <button className='docCreatorMenu-menu-button section-reveal-options' onPointerDown={e => this.setUpButtonClick(e, () => this._GPTOpt = !this._GPTOpt)}> + <FontAwesomeIcon icon='gear'/> + </button> + </div> + <div className='docCreatorMenu-GPT-templates-preview'> + <div className='docCreatorMenu-preview-window empty GPT'></div> + <div className='docCreatorMenu-preview-window empty GPT'></div> + <div className='docCreatorMenu-preview-window empty GPT'></div> + <div className='docCreatorMenu-preview-window empty GPT'></div> + </div> + {this._GPTOpt ? (<div className='docCreatorMenu-GPT-options'> + <div className='docCreatorMenu-GPT-options-container'> + <button className='docCreatorMenu-menu-button' onPointerDown={e => this.setUpButtonClick(e, () => this.generateTemplates(this._userPrompt))}> + <FontAwesomeIcon icon='plus'/> + </button> + <input className='docCreatorMenu-GPT-prompt-input'/> + </div> + {this._GPTOpt ? GPTOptions : null} + </div>) : null} + </div> + <hr className='docCreatorMenu-option-divider'/> + <div className='docCreatorMenu-section'> + <div className='docCreatorMenu-section-topbar'> + <div className='docCreatorMenu-section-title'>Your Templates</div> + <button className='docCreatorMenu-menu-button section-reveal-options' onPointerDown={e => this.setUpButtonClick(e, () => this._GPTOpt = !this._GPTOpt)}> + <FontAwesomeIcon icon='gear'/> + </button> + </div> + <div className='docCreatorMenu-GPT-templates-preview'> + <div className='docCreatorMenu-preview-window empty' + onPointerDown={e => this.setUpButtonClick(e, this.basicTemplateTest)} + > + <FontAwesomeIcon icon='plus' color='rgb(160, 160, 160)'/> + </div> + {this._templateDocs.map(doc => ({icon: ImageCast(doc.icon), doc})).filter(info => info.icon && info.doc).map(info => { + if (renderedTemplates.includes(info.doc)) return undefined; + renderedTemplates.push(info.doc); + return (<div + className='docCreatorMenu-preview-window' + style={{ + border: this._selectedTemplate === info.doc ? `solid 3px ${Colors.MEDIUM_BLUE}` : '', + boxShadow: this._selectedTemplate === info.doc ? `0 0 15px rgba(68, 118, 247, .8)` : '' + }} + onPointerDown={e => this.setUpButtonClick(e, () => runInAction(() => this.updateSelectedTemplate(info.doc)))}> + <img className='docCreatorMenu-preview-image' src={info.icon!.url.href.replace(".png", "_o.png")} /> + </div> + )})} + </div> + </div> + {/* <div className='docCreatorMenu-preview-container' style={{gridTemplateColumns: `repeat(${Math.floor((this._menuDimensions.width - 10) / 140)}, 140px)`}}> {this._templateDocs.map(doc => ({icon: ImageCast(doc.icon), doc})).filter(info => info.icon && info.doc).map(info => { if (renderedTemplates.includes(info.doc)) return undefined; @@ -316,6 +421,7 @@ export class DocCreatorMenu extends ObservableReactComponent<{}> { > <FontAwesomeIcon icon='plus' color='rgb(160, 160, 160)'/> </div> + </div> */} </div> ); } @@ -546,12 +652,7 @@ export class DocCreatorMenu extends ObservableReactComponent<{}> { } basicTemplateTest = () => { - const temps: {width: number; height: number; x: number; y: number; title: string}[] = [ - {width: 200, height: 50, x: -100, y: -200, title: 'title'}, - {width: 300, height: 300, x: -150, y: -100, title: 'image'}, - {width: 200, height: 50, x: -100, y: -200, title: 'description'}, - ] - this._dataViz?.generateTemplates(); + this.generateTemplates(this._userPrompt); //this._dataViz?.createBasicTemplates(temps); } @@ -609,31 +710,6 @@ export class DocCreatorMenu extends ObservableReactComponent<{}> { return ( <div className='docCreatorMenu'> {!this._shouldDisplay ? undefined : - <> - {/* <div className='docCreatorMenu-placement-indicator' - style={{ - display: '', - left: this._indicatorX ?? this._pageX + 300, - top: this._indicatorY ?? this._pageY, - }} - onPointerMove={e => this.onPointerMove(e)} - onPointerDown={e => - setupMoveUpEvents( - this, - e, - (e) => { - this._draggingIndicator = true; - this._startPos = {x: 0, y: 0}; - this._startPos.x = e.pageX - (this._ref?.getBoundingClientRect().left ?? 0); - this._startPos.y = e.pageY - (this._ref?.getBoundingClientRect().top ?? 0); - return true; - }, - emptyFunction, - undoable(clickEv => { - clickEv.stopPropagation(); - }, 'drag menu') - ) - }/> */} <div className="docCreatorMenu-cont" ref={r => this._ref = r} @@ -681,7 +757,6 @@ export class DocCreatorMenu extends ObservableReactComponent<{}> { </div> {this.renderSelectedViewType} </div> - </> } </div> ) diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index 92a5a1533..faea05104 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -295,6 +295,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { @computed get nativeSize() { TraceMobx(); + if (this.paths.length && this.paths[0].includes('icon-hi')) return { nativeWidth: NumCast(this.layoutDoc._width), nativeHeight: NumCast(this.layoutDoc._height), nativeOrientation: 0} const nativeWidth = NumCast(this.dataDoc[this.fieldKey + '_nativeWidth'], NumCast(this.layoutDoc[this.fieldKey + '_nativeWidth'], 500)); const nativeHeight = NumCast(this.dataDoc[this.fieldKey + '_nativeHeight'], NumCast(this.layoutDoc[this.fieldKey + '_nativeHeight'], 500)); const nativeOrientation = NumCast(this.dataDoc[this.fieldKey + '_nativeOrientation'], 1); |