diff options
author | Nathan-SR <144961007+Nathan-SR@users.noreply.github.com> | 2025-05-30 03:27:03 -0400 |
---|---|---|
committer | Nathan-SR <144961007+Nathan-SR@users.noreply.github.com> | 2025-05-30 03:27:03 -0400 |
commit | f92a02ec5d676359cb268a35d30e5bf9886199c1 (patch) | |
tree | 1183ca326374a6231115c3a6fcc0593a5a33a74e /src | |
parent | 47043f9435b3b07ea567634620205ec0a124cf5d (diff) |
couple more component refactors
Diffstat (limited to 'src')
8 files changed, 580 insertions, 578 deletions
diff --git a/src/client/views/nodes/DataVizBox/DocCreatorMenu/DocCreatorMenu.tsx b/src/client/views/nodes/DataVizBox/DocCreatorMenu/DocCreatorMenu.tsx index 1e54c0628..9a84e69a9 100644 --- a/src/client/views/nodes/DataVizBox/DocCreatorMenu/DocCreatorMenu.tsx +++ b/src/client/views/nodes/DataVizBox/DocCreatorMenu/DocCreatorMenu.tsx @@ -39,6 +39,8 @@ import { TemplatePreviewGrid } from './Menu/TemplatePreviewGrid'; import { FireflyStructureOptions, TemplateEditingWindow } from './Menu/TemplateEditingWindow'; import { DocCreatorMenuButton } from './Menu/DocCreatorMenuButton'; import { ConditionalsTextArea } from './Menu/ConditionalsTextarea'; +import { TemplatesRenderPreviewWindow } from './Menu/TemplateRenderPreviewWindow'; +import { TemplateMenuFieldOptions } from './Menu/TemplateMenuFieldOptions'; export enum LayoutType { FREEFORM = 'Freeform', @@ -81,12 +83,10 @@ export class DocCreatorMenu extends ObservableReactComponent<DocCreateMenuProps> // eslint-disable-next-line no-use-before-define static Instance: DocCreatorMenu; - private DEBUG_MODE: boolean = false; + DEBUG_MODE: boolean = false; private _ref: HTMLDivElement | null = null; private templateManager: TemplateManager; - @observable _fullyRenderedDocs: Doc[] = []; // collection of templates filled in with content - @observable _renderedDocCollection: Doc | undefined = undefined; // fullyRenderedDocs in a parent collection @observable _docsRendering: boolean = false; // dictates loading symbol @observable _userTemplates: Template[] = []; @@ -95,12 +95,6 @@ export class DocCreatorMenu extends ObservableReactComponent<DocCreateMenuProps> @observable _editedTemplateTrail: Template[] = []; @observable _userCreatedFields: Col[] = []; - @observable _collapsedCols: String[] = []; //any columns whose options panels are hidden - @observable _currEditingConditional: Conditional = {} as Conditional; - - @observable _layout: { type: LayoutType; yMargin: number; xMargin: number; columns?: number; repeat: number } = { type: LayoutType.FREEFORM, yMargin: 10, xMargin: 10, columns: 3, repeat: 0 }; - @observable _savedLayouts: DataVizTemplateLayout[] = []; - @observable _loadingVariants: boolean = false; @observable _suggestedTemplates: Template[] = []; @observable _GPTLoading: boolean = false; @@ -114,7 +108,7 @@ export class DocCreatorMenu extends ObservableReactComponent<DocCreateMenuProps> @observable _startPos?: { x: number; y: number }; @observable _shouldDisplay: boolean = false; - @observable _menuContent: 'templates' | 'options' | 'saved' | 'dashboard' | 'templateEditing' = 'templates'; + @observable _menuContent: 'templates' | 'renderPreview' | 'saved' | 'dashboard' | 'templateEditing' = 'templates'; @observable _dragging: boolean = false; @observable _dataViz?: DataVizBox; @observable _interactionLock: boolean | undefined; @@ -138,8 +132,6 @@ export class DocCreatorMenu extends ObservableReactComponent<DocCreateMenuProps> @action setDataViz = (dataViz: DataVizBox) => { this._dataViz = dataViz; this._selectedTemplate = undefined; - this._renderedDocCollection = undefined; - this._fullyRenderedDocs = []; this._suggestedTemplates = []; this._userCreatedFields = []; }; @@ -147,36 +139,6 @@ export class DocCreatorMenu extends ObservableReactComponent<DocCreateMenuProps> this._suggestedTemplates = templates; //prettier-ignore }; - @computed get docsToRender() { - if (this.DEBUG_MODE) { - return [1, 2, 3, 4]; - } else { - return this._selectedTemplate ? NumListCast(this._dataViz?.layoutDoc.dataViz_selectedRows) : []; - } - } - - @computed get rowsCount() { - switch (this._layout.type) { - case LayoutType.FREEFORM: - return Math.ceil(this.docsToRender.length / (this._layout.columns ?? 1)) ?? 0; - case LayoutType.CAROUSEL3D: - return 1.8; - default: - return 1; - } - } - - @computed get columnsCount() { - switch (this._layout.type) { - case LayoutType.FREEFORM: - return this._layout.columns ?? 0; - case LayoutType.CAROUSEL3D: - return 3; - default: - return 1; - } - } - @computed get selectedFields() { return StrListCast(this._dataViz?.layoutDoc._dataViz_axes); } @@ -203,10 +165,6 @@ export class DocCreatorMenu extends ObservableReactComponent<DocCreateMenuProps> .concat(this._userCreatedFields); } - @computed get canMakeDocs() { - return this._selectedTemplate !== undefined && this._layout !== undefined; - } - get bounds(): { t: number; b: number; l: number; r: number } { const rect = this._ref?.getBoundingClientRect(); const bounds = { t: rect?.top ?? 0, b: rect?.bottom ?? 0, l: rect?.left ?? 0, r: rect?.right ?? 0 }; @@ -356,12 +314,8 @@ export class DocCreatorMenu extends ObservableReactComponent<DocCreateMenuProps> this._pageX = x + translation.x; this._pageY = y + translation.y; }; - - @action updateRenderedPreviewCollection = async (template: Template) => { - this._docsRendering = true; - this._fullyRenderedDocs = this._dataViz ? ((await this.templateManager.createDocsFromTemplate(this._dataViz, template, this.fieldsInfos, this.DEBUG_MODE)).filter(doc => doc).map(doc => doc!) ?? []) as unknown as Doc[] : []; - this.updateRenderedDocCollection(); - }; + + async createDocsForPreview(): Promise<Doc[]> { return this._dataViz && this._selectedTemplate ? ((await this.templateManager.createDocsFromTemplate(this._dataViz, this._selectedTemplate, this.fieldsInfos, this.DEBUG_MODE)).filter(doc => doc).map(doc => doc!) ?? []) as unknown as Doc[] : []; } @action updateSelectedTemplate = async (template: Template) => { if (this._selectedTemplate === template) { @@ -369,53 +323,12 @@ export class DocCreatorMenu extends ObservableReactComponent<DocCreateMenuProps> return; } else { this._selectedTemplate = template; - this.updateRenderedPreviewCollection(template); } }; - @action updateSelectedSavedLayout = (layout: DataVizTemplateLayout) => { - this._layout.xMargin = layout.layout.xMargin; - this._layout.yMargin = layout.layout.yMargin; - this._layout.type = layout.layout.type; - this._layout.columns = layout.columns; - }; - - isSelectedLayout = (layout: DataVizTemplateLayout) => { - return this._layout.xMargin === layout.layout.xMargin && this._layout.yMargin === layout.layout.yMargin && this._layout.type === layout.layout.type && this._layout.columns === layout.columns; - }; - - editTemplate = (doc: Doc) => { - DocumentViewInternal.addDocTabFunc(doc, OpenWhere.addRight); - DocumentView.DeselectAll(); - Doc.UnBrushDoc(doc); - }; - - testTemplate = async () => { - this._suggestedTemplates = this.templateManager.templates; //prettier-ignore - - //console.log(this.templateManager.templates) - - // const mainCollection = this._dataViz?.DocumentView?.().containerViewPath?.().lastElement()?.ComponentView as CollectionFreeFormView; - - // this.templateManager.templates.forEach(template => { - // const doc = template.mainField.renderedDoc(); - // mainCollection.addDocument(doc); - // }) - - // this.forceUpdate(); - - // try { - // const res = await gptImageCall('Image of panda eating a cookie'); - - // if (res) { - // const result = await Networking.PostToServer('/uploadRemoteImage', { sources: res }); - - // console.log(result); - // } - // } catch (e) { - // console.log(e); - // } - }; + // testTemplate = async () => { + // this._suggestedTemplates = this.templateManager.templates; //prettier-ignore + // }; @action addField = () => { const newFields: Col[] = this._userCreatedFields.concat([{ title: '', type: TemplateFieldType.UNSET, desc: '', sizes: [], AIGenerated: true }]); @@ -607,8 +520,7 @@ export class DocCreatorMenu extends ObservableReactComponent<DocCreateMenuProps> this.variations.push(url); } - addRenderedCollectionToMainview = () => { - const collection = this._renderedDocCollection; + addRenderedCollectionToMainview = (collection: Doc) => { if (collection) { const mainCollection = this._dataViz?.DocumentView?.().containerViewPath?.().lastElement()?.ComponentView as CollectionFreeFormView; collection.x = this._pageX - this._menuDimensions.width; @@ -634,484 +546,34 @@ export class DocCreatorMenu extends ObservableReactComponent<DocCreateMenuProps> //Docs.Create.FreeformDocument([doc], { _height: NumListCast(doc._height)[0], _width: NumListCast(doc._width)[0], title: ''}); }; - @action updateXMargin = (input: string) => { - this._layout.xMargin = Number(input); - setTimeout(() => { - if (!this._renderedDocCollection || !this._fullyRenderedDocs) return; - this.applyLayout(this._renderedDocCollection, this._fullyRenderedDocs); - }); - }; - @action updateYMargin = (input: string) => { - this._layout.yMargin = Number(input); - setTimeout(() => { - if (!this._renderedDocCollection || !this._fullyRenderedDocs) return; - this.applyLayout(this._renderedDocCollection, this._fullyRenderedDocs); - }); - }; - @action updateColumns = (input: string) => { - this._layout.columns = Number(input); - this.updateRenderedDocCollection(); - }; - - get layoutConfigOptions() { - const optionInput = (icon: string, func: (input: string) => void, def?: number, key?: string, noMargin?: boolean) => { - return ( - <div className="docCreatorMenu-option-container small no-margin" key={key} style={{ marginTop: noMargin ? '0px' : '' }}> - <div className="docCreatorMenu-option-title config layout-config"> - <FontAwesomeIcon icon={icon as IconProp} /> - </div> - <input defaultValue={def} onInput={e => func(e.currentTarget.value)} className="docCreatorMenu-input config layout-config" /> - </div> - ); - }; - - switch (this._layout.type) { - case LayoutType.FREEFORM: - return ( - <div className="docCreatorMenu-configuration-bar"> - {optionInput('arrows-up-down', this.updateYMargin, this._layout.xMargin, '2')} - {optionInput('arrows-left-right', this.updateXMargin, this._layout.xMargin, '3')} - {optionInput('table-columns', this.updateColumns, this._layout.columns, '4', true)} - </div> - ); - default: - break; - } - } - - applyLayout = (collection: Doc, docs: Doc[]) => { - const { horizontalSpan, verticalSpan } = this.previewInfo; - collection._height = verticalSpan; - collection._width = horizontalSpan; - - const columns: number = this._layout.columns ?? this.columnsCount; - const xGap: number = this._layout.xMargin; - const yGap: number = this._layout.yMargin; - const startX: number = -Number(collection._width) / 2; - const startY: number = -Number(collection._height) / 2; - const docHeight: number = Number(docs[0]._height); - const docWidth: number = Number(docs[0]._width); - - if (columns === 0 || docs.length === 0) { - return; - } - - let i: number = 0; - let docsChanged: number = 0; - let curX: number = startX; - let curY: number = startY; - - while (docsChanged < docs.length) { - while (i < columns && docsChanged < docs.length) { - docs[docsChanged].x = curX; - docs[docsChanged].y = curY; - curX += docWidth + xGap; - ++docsChanged; - ++i; - } - i = 0; - curX = startX; - curY += docHeight + yGap; - } - }; - - @computed - get previewInfo() { - const docHeight: number = Number(this._fullyRenderedDocs[0]._height); - const docWidth: number = Number(this._fullyRenderedDocs[0]._width); - const layout = this._layout; - return { - docHeight: docHeight, - docWidth: docWidth, - horizontalSpan: (docWidth + layout.xMargin) * this.columnsCount - layout.xMargin, - verticalSpan: (docHeight + layout.yMargin) * this.rowsCount - layout.yMargin, - }; - } - - /** - * Updates the preview that shows how all docs will be rendered in the chosen collection type. - @type the type of collection the docs should render to (ie. freeform, carousel, card) - */ - updateRenderedDocCollection = () => { - if (!this._fullyRenderedDocs) return; - - const collectionFactory = (): ((docs: Doc[], options: DocumentOptions) => Doc) => { - switch (this._layout.type) { - case LayoutType.CAROUSEL3D: return Docs.Create.Carousel3DDocument; - case LayoutType.FREEFORM: return Docs.Create.FreeformDocument; - case LayoutType.CARD: return Docs.Create.CardDeckDocument; - case LayoutType.MASONRY: return Docs.Create.MasonryDocument; - case LayoutType.CAROUSEL: return Docs.Create.CarouselDocument; - default: return Docs.Create.FreeformDocument; - } // prettier-ignore - }; - - const collection = collectionFactory()(this._fullyRenderedDocs, { - isDefaultTemplateDoc: true, - title: 'title', - backgroundColor: 'gray', - x: 200, - y: 200, - _width: 4000, - _height: 4000, - }); - - this.applyLayout(collection, this._fullyRenderedDocs); - - this._renderedDocCollection = collection; - - this._docsRendering = false; - - this.forceUpdate(); - }; - - layoutPreviewContents = action(() => { - return this._docsRendering ? ( - <div className="docCreatorMenu-layout-preview-window-wrapper loading"> - <div className="loading-spinner"> - <ReactLoading type="spin" color={StrCast(Doc.UserDoc().userVariantColor)} height={30} width={30} /> - </div> - </div> - ) : !this._renderedDocCollection ? null : ( - <div className="docCreatorMenu-layout-preview-window-wrapper"> - <DocumentView - Document={this._renderedDocCollection} - isContentActive={emptyFunction} - addDocument={returnFalse} - moveDocument={returnFalse} - removeDocument={returnFalse} - PanelWidth={() => this._menuDimensions.width - 80} - PanelHeight={() => this._menuDimensions.height - 105} - ScreenToLocalTransform={() => new Transform(-this._pageX - 5, -this._pageY - 35, 1)} - renderDepth={5} - whenChildContentsActiveChanged={emptyFunction} - focus={emptyFunction} - styleProvider={DefaultStyleProvider} - addDocTab={this._props.addDocTab} - pinToPres={() => undefined} - childFilters={returnEmptyFilter} - childFiltersByRanges={returnEmptyFilter} - searchFilterDocs={returnEmptyDoclist} - fitContentsToBox={returnFalse} - fitWidth={returnFalse} - hideDecorations={true} + @computed + get templatesView() { return ( + <div className='docCreatorMenu-templates-view'> + <div className="docCreatorMenu-templates-displays"> + <TemplatePreviewGrid + title={'Suggested Templates'} + menu={this} + loading={this._GPTLoading} + optionsButtonOpts={this.optionsButtonOpts} + templates={this._suggestedTemplates} /> - </div> - ); - }); - - get optionsMenuContents() { - const layoutOption = (option: LayoutType, optStyle?: object, specialFunc?: () => void) => { - return ( - <div - className="docCreatorMenu-dropdown-option" - style={optStyle} - onPointerDown={e => - this.setUpButtonClick(e, () => { - specialFunc?.(); - runInAction(() => { - this._layout.type = option; - this.updateRenderedDocCollection(); - }); - }) - }> - {option} - </div> - ); - }; - - const selectionBox = (width: number, height: number, icon: string, specClass?: string, options?: JSX.Element[], manual?: boolean): JSX.Element => { - return ( - <div className="docCreatorMenu-option-container"> - <div className={`docCreatorMenu-option-title config ${specClass}`} style={{ width: width * 0.4, height: height }}> - <FontAwesomeIcon icon={icon as IconProp} /> + <div className="docCreatorMenu-GPT-options"> + <div className="docCreatorMenu-GPT-options-container"> + <DocCreatorMenuButton icon={'arrows-rotate'} styles={'border'} function={this.generatePresetTemplates}/> </div> - {manual ? ( - <input className={`docCreatorMenu-input config ${specClass}`} style={{ width: width * 0.6, height: height }} /> - ) : ( - <select className={`docCreatorMenu-input config ${specClass}`} style={{ width: width * 0.6, height: height }}> - {options} - </select> - )} - </div> - ); - }; - - const repeatOptions = [0, 1, 2, 3, 4, 5]; - - return ( - <div className="docCreatorMenu-menu-container"> - <div className="docCreatorMenu-option-container layout"> - <div className="docCreatorMenu-dropdown-hoverable"> - <div className="docCreatorMenu-option-title">{this._layout.type ? this._layout.type.toUpperCase() : 'Choose Layout'}</div> - <div className="docCreatorMenu-dropdown-content"> - {layoutOption(LayoutType.FREEFORM, undefined, () => { - if (!this._layout.columns) this._layout.columns = Math.ceil(Math.sqrt(this.docsToRender.length)); - })} - {layoutOption(LayoutType.CAROUSEL)} - {layoutOption(LayoutType.CAROUSEL3D)} - {layoutOption(LayoutType.MASONRY)} - </div> - </div> - </div> - {this._layout.type ? this.layoutConfigOptions : null} - {this.layoutPreviewContents()} - {selectionBox( - 60, - 20, - 'repeat', - undefined, - repeatOptions.map(num => <option key={num} onPointerDown={() => (this._layout.repeat = num)}>{`${num}x`}</option>) - )} - <hr className="docCreatorMenu-option-divider" /> - <div className="docCreatorMenu-general-options-container"> - <button - className="docCreatorMenu-save-layout-button" - onPointerDown={e => - setupMoveUpEvents( - this, - e, - returnFalse, - emptyFunction, - undoable(clickEv => { - clickEv.stopPropagation(); - if (!this._selectedTemplate || !this._selectedTemplate.getRenderedDoc()) return; - const layout: DataVizTemplateLayout = { - template: this._selectedTemplate.getRenderedDoc()!, - layout: { type: this._layout.type, xMargin: this._layout.xMargin, yMargin: this._layout.yMargin, repeat: 0 }, - columns: this.columnsCount, - rows: this.rowsCount, - docsNumList: this.docsToRender, - }; - if (!this._savedLayouts.includes(layout)) { - this._savedLayouts.push(layout); - } - }, 'make docs') - ) - }> - <FontAwesomeIcon icon="floppy-disk" /> - </button> - <button - className="docCreatorMenu-create-docs-button" - style={{ backgroundColor: this.canMakeDocs ? '' : 'rgb(155, 155, 155)', border: this.canMakeDocs ? '' : 'solid 2px rgb(180, 180, 180)' }} - onPointerDown={e => - setupMoveUpEvents( - this, - e, - returnFalse, - emptyFunction, - undoable(clickEv => { - clickEv.stopPropagation(); - if (!this._selectedTemplate) return; - this.addRenderedCollectionToMainview(); - }, 'make docs') - ) - }> - <FontAwesomeIcon icon="plus" /> - </button> </div> </div> - ); - } - - @observable private _newCondCache: Record<string, Conditional> = {}; - - getParams = (title: string, parameters?: Conditional): Conditional => { - if (parameters) return parameters; - - if (!this._newCondCache[title]) { - this._newCondCache[title] = observable<Conditional>({ - field: title, - operator: '=', - condition: '', - target: '', - attribute: '', - value: '' - }); - } - return this._newCondCache[title]; - }; - - get dashboardContents() { - const contentFieldTitles = this.fieldsInfos.filter(field => field.type !== TemplateFieldType.DATA).map(field => field.title).concat('Template'); - - const conditionForm = (title: string, parameters?: Conditional, empty: boolean = false) => { - - var params: Conditional = this.getParams(title, parameters); - - return ( - <div className='form'> - <div className='form-row'> - <div className='form-row-plain-text'>If</div> - <div className='form-row-plain-text'>{title}</div> - <div className="operator-options-dropdown"> - <span className="operator-dropdown-current">{params.operator ?? '='}</span> - <div className='operator-dropdown-option' onPointerDown={() => {params.operator = '='}}>{'='}</div> - <div className='operator-dropdown-option' onPointerDown={() => {params.operator = '>'}}>{'>'}</div> - <div className='operator-dropdown-option' onPointerDown={() => {params.operator = '<'}}>{'<'}</div> - <div className='operator-dropdown-option' onPointerDown={() => {params.operator = 'contains'}}>{'has'}</div> - </div> - <input - className="form-row-textarea" - onChange={e => runInAction(() => params.condition = e.target.value)} - placeholder='value' - value={params.condition} - /> - <div className='form-row-plain-text'>then</div> - <div className="operator-options-dropdown"> - <span className="operator-dropdown-current">{params.target}</span> - {contentFieldTitles.map(fieldTitle => - <div className='operator-dropdown-option' onPointerDown={() => {params.target = fieldTitle}}>{fieldTitle === title ? 'Own' : fieldTitle}</div> - )} - </div> - <input - className="form-row-textarea" - onChange={e => runInAction(() => params.attribute = e.target.value)} - placeholder='attribute' - value={params.attribute} - /> - <div className='form-row-plain-text'>{'becomes'}</div> - <input - className="form-row-textarea" - onChange={e => runInAction(() => params.value = e.target.value)} - placeholder='value' - value={params.value} - /> - </div> - {empty ? - <DocCreatorMenuButton icon={'plus'} styles={'float-right border'} function={() => { - this._newCondCache[title] = observable<Conditional>({ - field: title, - operator: '=', - condition: '', - target: '', - attribute: '', - value: '' - }); - this.templateManager.addFieldCondition(title, params); - }}/> - : - <DocCreatorMenuButton icon={'minus'} styles={'float-right border'} function={() => this.templateManager.removeFieldCondition(title, params)}/> - } - </div> - ) - } - - const fieldPanel = (field: Col, id: number) => ( - <div className="field-panel" key={id}> - <div className="top-bar" onPointerDown={e => this.setUpButtonClick(e, runInAction(() => () => { - if (this._collapsedCols.includes(field.title)) { - this._collapsedCols = this._collapsedCols.filter(col => col !== field.title); - } else { - this._collapsedCols.push(field.title); - } - }))}> - <span className="field-title">{`${field.title} Field`}</span> - <DocCreatorMenuButton icon={'minus'} styles={'no-margin absolute-right'} function={() => this.removeField(field)}/> - </div> - { this._collapsedCols.includes(field.title) ? null : - <> - <div className="opts-bar"> - <div className="opt-box"> - <div className="top-bar"> Title </div> - <textarea className="content" style={{ width: '100%', height: 'calc(100% - 20px)' }} value={field.title} placeholder={'Enter title'} onChange={e => this.setColTitle(field, e.target.value)} /> - </div> - <div className="opt-box"> - <div className="top-bar"> Type </div> - <div className="content"> - <span className="type-display">{ - field.type === TemplateFieldType.TEXT ? 'Text Field' - : field.type === TemplateFieldType.VISUAL ? 'File Field' - : field.type === TemplateFieldType.DATA ? 'Data Field' - : '' - }</span> - <div className="bubbles"> - <input className="bubble" type="radio" name="type" onClick={() => this.setColType(field, TemplateFieldType.TEXT)} /> - <div className="text">Text</div> - <input className="bubble" type="radio" name="type" onClick={() => this.setColType(field, TemplateFieldType.VISUAL)} /> - <div className="text">File</div> - <input className="bubble" type="radio" name="type" onClick={() => this.setColType(field, TemplateFieldType.DATA)} /> - <div className="text">Data</div> - </div> - </div> - </div> - </div> - { field.type === TemplateFieldType.DATA ? null : - (<> - <div className="sizes-box"> - <div className="top-bar"> Valid Sizes </div> - <div className="content"> - <div className="bubbles"> - {Object.values(TemplateFieldSize).map(size => ( - <div key={field + size}> - <input className="bubble" type="checkbox" name="type" checked={field.sizes.includes(size)} onChange={e => this.modifyColSizes(field, size, e.target.checked)} /> - <div className="text">{size}</div> - </div> - ))} - </div> - </div> - </div> - <div className="desc-box"> - <div className="top-bar"> Prompt </div> - <textarea - className="content" - onChange={e => this.setColDesc(field, e.target.value)} - defaultValue={field.desc === this._dataViz?.GPTSummary?.get(field.title)?.desc ? '' : field.desc} - placeholder={this._dataViz?.GPTSummary?.get(field.title)?.desc ?? 'Add a description/prompt to help with template generation.'} - /> - </div> - </>) - } - <div className="conditionals-section"> - <span className="conditionals-title">Conditional Logic</span> - {conditionForm(field.title, undefined, true)} - {this.templateManager.conditionalFieldLogic[field.title]?.map(condition => conditionForm(condition.field, condition))} - </div> - </> - } - </div> - ); - - return ( - <div className="docCreatorMenu-dashboard-view"> - <div className="topbar"> - <DocCreatorMenuButton icon={'plus'} function={this.addField}/> - <DocCreatorMenuButton icon={'arrow-left'} styles={'float-right'} function={() => runInAction(() => (this._menuContent = 'templates'))}/> - </div> - <div className="panels-container">{this.fieldsInfos.map((field, i) => fieldPanel(field, i))}</div> - </div> - ); - } - - @computed get editingView() { return <TemplateEditingWindow template={this._currEditingTemplate as Template} menu={this} /> } + </div> + )}; private optionsButtonOpts: [IconProp, () => any] = ['gear', () => (this._menuContent = 'dashboard')]; get renderSelectedViewType() { switch (this._menuContent) { - case 'templates': - return ( - <div className='docCreatorMenu-templates-view'> - <div className="docCreatorMenu-templates-displays"> - <TemplatePreviewGrid - title={'Suggested Templates'} - menu={this} - loading={this._GPTLoading} - optionsButtonOpts={this.optionsButtonOpts} - templates={this._suggestedTemplates} - /> - <div className="docCreatorMenu-GPT-options"> - <div className="docCreatorMenu-GPT-options-container"> - <DocCreatorMenuButton icon={'arrows-rotate'} styles={'border'} function={this.generatePresetTemplates}/> - </div> - </div> - </div> - </div> - ) - case 'templateEditing': - return this.editingView - case 'options': return this.optionsMenuContents; - case 'dashboard': return this.dashboardContents; + case 'templates': return this.templatesView; + case 'templateEditing': return <TemplateEditingWindow template={this._currEditingTemplate as Template} menu={this} />; + case 'renderPreview': return <TemplatesRenderPreviewWindow menu={this}/>; + case 'dashboard': return <TemplateMenuFieldOptions menu={this} templateManager={this.templateManager}/>; } // prettier-ignore return undefined; } @@ -1144,10 +606,7 @@ export class DocCreatorMenu extends ObservableReactComponent<DocCreateMenuProps> const onPreviewSelected = () => (this._menuContent = 'templates'); const onSavedSelected = () => (this._menuContent = 'dashboard'); - const onOptionsSelected = () => { - this._menuContent = 'options'; - if (!this._layout.columns) this._layout.columns = Math.ceil(Math.sqrt(this.docsToRender.length)); - }; + const onOptionsSelected = () => (this._menuContent = 'renderPreview'); return ( <div className="docCreatorMenu"> diff --git a/src/client/views/nodes/DataVizBox/DocCreatorMenu/Menu/ExpandedTemplatePreview.tsx b/src/client/views/nodes/DataVizBox/DocCreatorMenu/Menu/ExpandedTemplatePreview.tsx deleted file mode 100644 index e69de29bb..000000000 --- a/src/client/views/nodes/DataVizBox/DocCreatorMenu/Menu/ExpandedTemplatePreview.tsx +++ /dev/null diff --git a/src/client/views/nodes/DataVizBox/DocCreatorMenu/Menu/FieldOptionsScreen.tsx b/src/client/views/nodes/DataVizBox/DocCreatorMenu/Menu/FieldOptionsScreen.tsx deleted file mode 100644 index e69de29bb..000000000 --- a/src/client/views/nodes/DataVizBox/DocCreatorMenu/Menu/FieldOptionsScreen.tsx +++ /dev/null diff --git a/src/client/views/nodes/DataVizBox/DocCreatorMenu/Menu/TemplateEditingWindow.tsx b/src/client/views/nodes/DataVizBox/DocCreatorMenu/Menu/TemplateEditingWindow.tsx index cb87e9b47..3eaed79b6 100644 --- a/src/client/views/nodes/DataVizBox/DocCreatorMenu/Menu/TemplateEditingWindow.tsx +++ b/src/client/views/nodes/DataVizBox/DocCreatorMenu/Menu/TemplateEditingWindow.tsx @@ -227,9 +227,9 @@ export class TemplateEditingWindow extends ObservableReactComponent<TemplateEdit : null} <div className="right-buttons-panel"> <DocCreatorMenuButton icon={'minimize'} function={() => { - if (this._props.template === this._props.menu._selectedTemplate) { - this._props.menu.updateRenderedPreviewCollection(this._props.template); - } + // if (this._props.template === this._props.menu._selectedTemplate) { + // this._props.menu.updateRenderedPreviewCollection(this._props.template); + // } this._props.menu.setExpandedView(undefined); }}/> <DocCreatorMenuButton icon={'lightbulb'} function={() => this.setVariationTab(!this._variationsTabOpen)}/> diff --git a/src/client/views/nodes/DataVizBox/DocCreatorMenu/Menu/TemplateMenuFieldOptions.tsx b/src/client/views/nodes/DataVizBox/DocCreatorMenu/Menu/TemplateMenuFieldOptions.tsx new file mode 100644 index 000000000..beda45ac3 --- /dev/null +++ b/src/client/views/nodes/DataVizBox/DocCreatorMenu/Menu/TemplateMenuFieldOptions.tsx @@ -0,0 +1,193 @@ +import { makeObservable, observable, runInAction } from "mobx"; +import { observer } from "mobx-react"; +import { ObservableReactComponent } from "../../../../ObservableReactComponent"; +import { Col, DocCreatorMenu } from "../DocCreatorMenu"; +import React from "react"; +import { Conditional, TemplateManager } from "../Backend/TemplateManager"; +import { TemplateFieldType, TemplateFieldSize } from "../TemplateBackend"; +import { DocCreatorMenuButton } from "./DocCreatorMenuButton"; + +interface TemplateMenuFieldOptionsProps { + menu: DocCreatorMenu; + templateManager: TemplateManager; +} + +@observer +export class TemplateMenuFieldOptions extends ObservableReactComponent<TemplateMenuFieldOptionsProps> { + + @observable _collapsedCols: String[] = []; //any columns whose options panels are hidden + + constructor(props: TemplateMenuFieldOptionsProps) { + super(props); + makeObservable(this); + } + + @observable private _newCondCache: Record<string, Conditional> = {}; + + getParams = (title: string, parameters?: Conditional): Conditional => { + if (parameters) return parameters; + + if (!this._newCondCache[title]) { + this._newCondCache[title] = observable<Conditional>({ + field: title, + operator: '=', + condition: '', + target: 'Own', + attribute: '', + value: '' + }); + } + return this._newCondCache[title]; + }; + + conditionForm = (title: string, parameters?: Conditional, empty: boolean = false) => { + + const contentFieldTitles = this._props.menu.fieldsInfos.filter(field => field.type !== TemplateFieldType.DATA).map(field => field.title).concat('Template'); + var params: Conditional = this.getParams(title, parameters); + + return ( + <div className='form'> + <div className='form-row'> + <div className='form-row-plain-text'>If</div> + <div className='form-row-plain-text'>{title}</div> + <div className="operator-options-dropdown"> + <span className="operator-dropdown-current">{params.operator ?? '='}</span> + <div className='operator-dropdown-option' onPointerDown={() => {params.operator = '='}}>{'='}</div> + </div> + <input + className="form-row-textarea" + onChange={e => runInAction(() => params.condition = e.target.value)} + placeholder='value' + value={params.condition} + /> + <div className='form-row-plain-text'>then</div> + <div className="operator-options-dropdown"> + <span className="operator-dropdown-current">{params.target ?? 'Own'}</span> + {contentFieldTitles.map(fieldTitle => + <div className='operator-dropdown-option' onPointerDown={() => {params.target = fieldTitle}}>{fieldTitle === title ? 'Own' : fieldTitle}</div> + )} + </div> + <input + className="form-row-textarea" + onChange={e => runInAction(() => params.attribute = e.target.value)} + placeholder='attribute' + value={params.attribute} + /> + <div className='form-row-plain-text'>{'becomes'}</div> + <input + className="form-row-textarea" + onChange={e => runInAction(() => params.value = e.target.value)} + placeholder='value' + value={params.value} + /> + </div> + {empty ? + <DocCreatorMenuButton icon={'plus'} styles={'float-right border'} function={() => { + this._newCondCache[title] = observable<Conditional>({ + field: title, + operator: '=', + condition: '', + target: 'Own', + attribute: '', + value: '' + }); + this._props.templateManager.addFieldCondition(title, params); + }}/> + : + <DocCreatorMenuButton icon={'minus'} styles={'float-right border'} function={() => this._props.templateManager.removeFieldCondition(title, params)}/> + } + </div> + ) + } + + fieldPanel = (field: Col, id: number) => ( + <div className="field-panel" key={id}> + <div className="top-bar" onPointerDown={e => this._props.menu.setUpButtonClick(e, runInAction(() => () => { + if (this._collapsedCols.includes(field.title)) { + this._collapsedCols = this._collapsedCols.filter(col => col !== field.title); + } else { + this._collapsedCols.push(field.title); + } + }))}> + <span className="field-title">{`${field.title} Field`}</span> + <DocCreatorMenuButton icon={'minus'} styles={'no-margin absolute-right'} function={() => this._props.menu.removeField(field)}/> + </div> + { this._collapsedCols.includes(field.title) ? null : + <> + <div className="opts-bar"> + <div className="opt-box"> + <div className="top-bar"> Title </div> + <textarea className="content" style={{ width: '100%', height: 'calc(100% - 20px)' }} value={field.title} placeholder={'Enter title'} onChange={e => this._props.menu.setColTitle(field, e.target.value)} /> + </div> + <div className="opt-box"> + <div className="top-bar"> Type </div> + <div className="content"> + <span className="type-display">{ + field.type === TemplateFieldType.TEXT ? 'Text Field' + : field.type === TemplateFieldType.VISUAL ? 'File Field' + : field.type === TemplateFieldType.DATA ? 'Data Field' + : '' + }</span> + <div className="bubbles"> + <input className="bubble" type="radio" name="type" onClick={() => this._props.menu.setColType(field, TemplateFieldType.TEXT)} /> + <div className="text">Text</div> + <input className="bubble" type="radio" name="type" onClick={() => this._props.menu.setColType(field, TemplateFieldType.VISUAL)} /> + <div className="text">File</div> + <input className="bubble" type="radio" name="type" onClick={() => this._props.menu.setColType(field, TemplateFieldType.DATA)} /> + <div className="text">Data</div> + </div> + </div> + </div> + </div> + { field.type === TemplateFieldType.DATA ? null : + (<> + <div className="sizes-box"> + <div className="top-bar"> Valid Sizes </div> + <div className="content"> + <div className="bubbles"> + {Object.values(TemplateFieldSize).map(size => ( + <div key={field + size}> + <input className="bubble" type="checkbox" name="type" checked={field.sizes.includes(size)} onChange={e => this._props.menu.modifyColSizes(field, size, e.target.checked)} /> + <div className="text">{size}</div> + </div> + ))} + </div> + </div> + </div> + <div className="desc-box"> + <div className="top-bar"> Prompt </div> + <textarea + className="content" + onChange={e => this._props.menu.setColDesc(field, e.target.value)} + defaultValue={field.desc === this._props.menu._dataViz?.GPTSummary?.get(field.title)?.desc ? '' : field.desc} + placeholder={this._props.menu._dataViz?.GPTSummary?.get(field.title)?.desc ?? 'Add a description/prompt to help with template generation.'} + /> + </div> + </>) + } + <div className="conditionals-section"> + <span className="conditionals-title">Conditional Logic</span> + {this.conditionForm(field.title, undefined, true)} + {this._props.templateManager.conditionalFieldLogic[field.title]?.map(condition => this.conditionForm(condition.field, condition))} + </div> + </> + } + </div> + ); + + + + render() { + return ( + <div className="docCreatorMenu-dashboard-view"> + <div className="topbar"> + <DocCreatorMenuButton icon={'plus'} function={this._props.menu.addField}/> + <DocCreatorMenuButton icon={'arrow-left'} styles={'float-right'} function={() => runInAction(() => (this._props.menu._menuContent = 'templates'))}/> + </div> + <div className="panels-container">{this._props.menu.fieldsInfos.map((field, i) => this.fieldPanel(field, i))}</div> + </div> + ); + } + + +}
\ No newline at end of file diff --git a/src/client/views/nodes/DataVizBox/DocCreatorMenu/Menu/TemplateRenderPreviewWindow.tsx b/src/client/views/nodes/DataVizBox/DocCreatorMenu/Menu/TemplateRenderPreviewWindow.tsx new file mode 100644 index 000000000..219152549 --- /dev/null +++ b/src/client/views/nodes/DataVizBox/DocCreatorMenu/Menu/TemplateRenderPreviewWindow.tsx @@ -0,0 +1,346 @@ +import { action, computed, makeObservable, observable, runInAction } from "mobx"; +import { observer } from "mobx-react"; +import { ObservableReactComponent } from "../../../../ObservableReactComponent"; +import { DataVizTemplateLayout, DocCreatorMenu, LayoutType } from "../DocCreatorMenu"; +import React from "react"; +import { IconProp } from "@fortawesome/fontawesome-svg-core"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { setupMoveUpEvents, returnFalse, returnEmptyFilter } from "../../../../../../ClientUtils"; +import { emptyFunction } from "../../../../../../Utils"; +import { undoable } from "../../../../../util/UndoManager"; +import ReactLoading from "react-loading"; +import { Doc, NumListCast, returnEmptyDoclist } from "../../../../../../fields/Doc"; +import { StrCast } from "../../../../../../fields/Types"; +import { DefaultStyleProvider } from "../../../../StyleProvider"; +import { DocumentView } from "../../../DocumentView"; +import { Transform } from "../../../../../util/Transform"; +import { Docs, DocumentOptions } from "../../../../../documents/Documents"; +import { Template } from "../Template"; + +interface TemplatesRenderPreviewWindowProps { + menu: DocCreatorMenu; +} + +@observer +export class TemplatesRenderPreviewWindow extends ObservableReactComponent<TemplatesRenderPreviewWindowProps> { + + @observable private _layout: { type: LayoutType; yMargin: number; xMargin: number; columns?: number; repeat: number } = { type: LayoutType.FREEFORM, yMargin: 10, xMargin: 10, columns: 0, repeat: 0 }; + + @observable private renderedDocs: Doc[] = []; + @observable private renderedDocCollection: Doc | undefined = undefined; + + @observable private loading: boolean = false; + + constructor(props: TemplatesRenderPreviewWindowProps) { + super(props); + makeObservable(this); + this.updateRenderedPreviewCollection(); + } + + @computed get canMakeDocs() { + return this._props.menu._selectedTemplate !== undefined && this._layout !== undefined; + } + + @computed get docsToRender() { + if (this._props.menu.DEBUG_MODE) { + return [1, 2, 3, 4]; + } else { + return NumListCast(this._props.menu._dataViz?.layoutDoc.dataViz_selectedRows); + } + } + + @computed get rowsCount() { + switch (this._layout.type) { + case LayoutType.FREEFORM: + return Math.ceil(this.docsToRender.length / (this._layout.columns ?? 1)) ?? 0; + case LayoutType.CAROUSEL3D: + return 1.8; + default: + return 1; + } + } + + @computed get columnsCount() { + switch (this._layout.type) { + case LayoutType.FREEFORM: + return this._layout.columns ?? 0; + case LayoutType.CAROUSEL3D: + return 3; + default: + return 1; + } + } + + @action updateRenderedPreviewCollection = async () => { + this.loading = true; + this.renderedDocs = await this._props.menu.createDocsForPreview(); + this.updateRenderedDocCollection(); + }; + + /** + * Updates the preview that shows how all docs will be rendered in the chosen collection type. + @type the type of collection the docs should render to (ie. freeform, carousel, card) + */ + updateRenderedDocCollection = () => { + if (!this.renderedDocs) return; + + const collectionFactory = (): ((docs: Doc[], options: DocumentOptions) => Doc) => { + switch (this._layout.type) { + case LayoutType.CAROUSEL3D: return Docs.Create.Carousel3DDocument; + case LayoutType.FREEFORM: return Docs.Create.FreeformDocument; + case LayoutType.CARD: return Docs.Create.CardDeckDocument; + case LayoutType.MASONRY: return Docs.Create.MasonryDocument; + case LayoutType.CAROUSEL: return Docs.Create.CarouselDocument; + default: return Docs.Create.FreeformDocument; + } // prettier-ignore + }; + + const collection = collectionFactory()(this.renderedDocs, { + isDefaultTemplateDoc: true, + title: 'title', + backgroundColor: 'gray', + x: 200, + y: 200, + _width: 4000, + _height: 4000, + }); + + this.applyLayout(collection, this.renderedDocs); + + this.renderedDocCollection = collection; + + this.loading = false; + + this.forceUpdate(); + }; + + @action updateMargin = (input: string, xOrY: 'x' | 'y') => { + this._layout[`${xOrY}Margin`] = Number(input); + setTimeout(() => { + if (!this.renderedDocCollection || !this.renderedDocs) return; + this.applyLayout(this.renderedDocCollection, this.renderedDocs); + }); + }; + + @action updateColumns = (input: string) => { + this._layout.columns = Number(input); + this.updateRenderedDocCollection(); + }; + + applyLayout = (collection: Doc, docs: Doc[]) => { + const { horizontalSpan, verticalSpan } = this.previewInfo; + collection._height = verticalSpan; + collection._width = horizontalSpan; + + const columns: number = this._layout.columns ?? this.columnsCount; + const xGap: number = this._layout.xMargin; + const yGap: number = this._layout.yMargin; + const startX: number = -Number(collection._width) / 2; + const startY: number = -Number(collection._height) / 2; + const docHeight: number = Number(docs[0]._height); + const docWidth: number = Number(docs[0]._width); + + if (columns === 0 || docs.length === 0) { + return; + } + + let i: number = 0; + let docsChanged: number = 0; + let curX: number = startX; + let curY: number = startY; + + while (docsChanged < docs.length) { + while (i < columns && docsChanged < docs.length) { + docs[docsChanged].x = curX; + docs[docsChanged].y = curY; + curX += docWidth + xGap; + ++docsChanged; + ++i; + } + i = 0; + curX = startX; + curY += docHeight + yGap; + } + }; + + @computed + get previewInfo() { + const docHeight: number = Number(this.renderedDocs[0]._height); + const docWidth: number = Number(this.renderedDocs[0]._width); + const layout = this._layout; + return { + docHeight: docHeight, + docWidth: docWidth, + horizontalSpan: (docWidth + layout.xMargin) * this.columnsCount - layout.xMargin, + verticalSpan: (docHeight + layout.yMargin) * this.rowsCount - layout.yMargin, + }; + } + + get layoutConfigOptions() { + const optionInput = (icon: string, func: (input: string) => void, def?: number, key?: string, noMargin?: boolean) => { + return ( + <div className="docCreatorMenu-option-container small no-margin" key={key} style={{ marginTop: noMargin ? '0px' : '' }}> + <div className="docCreatorMenu-option-title config layout-config"> + <FontAwesomeIcon icon={icon as IconProp} /> + </div> + <input defaultValue={def} onInput={e => func(e.currentTarget.value)} className="docCreatorMenu-input config layout-config" /> + </div> + ); + }; + + switch (this._layout.type) { + case LayoutType.FREEFORM: + return ( + <div className="docCreatorMenu-configuration-bar"> + {optionInput('arrows-up-down', (input: string) => this.updateMargin(input, 'y'), this._layout.xMargin, '2')} + {optionInput('arrows-left-right', (input: string) => this.updateMargin(input, 'x'), this._layout.xMargin, '3')} + {optionInput('table-columns', this.updateColumns, this._layout.columns, '4', true)} + </div> + ); + default: + break; + } + } + + layoutPreviewContents = action(() => { + return this.loading ? ( + <div className="docCreatorMenu-layout-preview-window-wrapper loading"> + <div className="loading-spinner"> + <ReactLoading type="spin" color={StrCast(Doc.UserDoc().userVariantColor)} height={30} width={30} /> + </div> + </div> + ) : !this.renderedDocCollection ? null : ( + <div className="docCreatorMenu-layout-preview-window-wrapper"> + <DocumentView + Document={this.renderedDocCollection} + isContentActive={emptyFunction} + addDocument={returnFalse} + moveDocument={returnFalse} + removeDocument={returnFalse} + PanelWidth={() => this._props.menu._menuDimensions.width - 80} + PanelHeight={() => this._props.menu._menuDimensions.height - 105} + ScreenToLocalTransform={() => new Transform(-this._props.menu._pageX - 5, -this._props.menu._pageY - 35, 1)} + renderDepth={5} + whenChildContentsActiveChanged={emptyFunction} + focus={emptyFunction} + styleProvider={DefaultStyleProvider} + addDocTab={this._props.menu._props.addDocTab} + pinToPres={() => undefined} + childFilters={returnEmptyFilter} + childFiltersByRanges={returnEmptyFilter} + searchFilterDocs={returnEmptyDoclist} + fitContentsToBox={returnFalse} + fitWidth={returnFalse} + hideDecorations={true} + /> + </div> + ); + }); + + selectionBox = (width: number, height: number, icon: string, specClass?: string, options?: JSX.Element[], manual?: boolean): JSX.Element => { + return ( + <div className="docCreatorMenu-option-container"> + <div className={`docCreatorMenu-option-title config ${specClass}`} style={{ width: width * 0.4, height: height }}> + <FontAwesomeIcon icon={icon as IconProp} /> + </div> + {manual ? ( + <input className={`docCreatorMenu-input config ${specClass}`} style={{ width: width * 0.6, height: height }} /> + ) : ( + <select className={`docCreatorMenu-input config ${specClass}`} style={{ width: width * 0.6, height: height }}> + {options} + </select> + )} + </div> + ); + }; + + layoutOption = (option: LayoutType, optStyle?: object, specialFunc?: () => void) => { + return ( + <div + className="docCreatorMenu-dropdown-option" + style={optStyle} + onPointerDown={e => + this._props.menu.setUpButtonClick(e, () => { + specialFunc?.(); + runInAction(() => { + this._layout.type = option; + this.updateRenderedDocCollection(); + }); + }) + }> + {option} + </div> + ); + }; + + get optionsMenuContents() { + + const repeatOptions = [0, 1, 2, 3, 4, 5]; + + return ( + <div className="docCreatorMenu-menu-container"> + <div className="docCreatorMenu-option-container layout"> + <div className="docCreatorMenu-dropdown-hoverable"> + <div className="docCreatorMenu-option-title">{this._layout.type ? this._layout.type.toUpperCase() : 'Choose Layout'}</div> + <div className="docCreatorMenu-dropdown-content"> + {this.layoutOption(LayoutType.FREEFORM, undefined, () => { + if (!this._layout.columns) this._layout.columns = Math.ceil(Math.sqrt(this.docsToRender.length)); + })} + {this.layoutOption(LayoutType.CAROUSEL)} + {this.layoutOption(LayoutType.CAROUSEL3D)} + {this.layoutOption(LayoutType.MASONRY)} + </div> + </div> + </div> + {this._layout.type ? this.layoutConfigOptions : null} + {this.layoutPreviewContents()} + {this.selectionBox( + 60, + 20, + 'repeat', + undefined, + repeatOptions.map(num => <option key={num} onPointerDown={() => (this._layout.repeat = num)}>{`${num}x`}</option>) + )} + <hr className="docCreatorMenu-option-divider" /> + <div className="docCreatorMenu-general-options-container"> + <button + className="docCreatorMenu-save-layout-button" + onPointerDown={e => + setupMoveUpEvents( + this, + e, + returnFalse, + emptyFunction, + undoable(clickEv => { + clickEv.stopPropagation(); + //previous implementation deprecated; return later to add or scrap + return; + }, 'save layout') + ) + }> + <FontAwesomeIcon icon="floppy-disk" /> + </button> + <button + className="docCreatorMenu-create-docs-button" + style={{ backgroundColor: this.canMakeDocs ? '' : 'rgb(155, 155, 155)', border: this.canMakeDocs ? '' : 'solid 2px rgb(180, 180, 180)' }} + onPointerDown={e => + setupMoveUpEvents( + this, + e, + returnFalse, + emptyFunction, + undoable(clickEv => { + clickEv.stopPropagation(); + this.renderedDocCollection && this._props.menu.addRenderedCollectionToMainview(this.renderedDocCollection); + }, 'make docs') + ) + }> + <FontAwesomeIcon icon="plus" /> + </button> + </div> + </div> + ); + } + + render() { return this.optionsMenuContents } +}
\ No newline at end of file diff --git a/src/client/views/nodes/DataVizBox/DocCreatorMenu/TemplateFieldTypes/StaticContentField.ts b/src/client/views/nodes/DataVizBox/DocCreatorMenu/TemplateFieldTypes/StaticContentField.ts index 112f37cc6..2a8e4f09b 100644 --- a/src/client/views/nodes/DataVizBox/DocCreatorMenu/TemplateFieldTypes/StaticContentField.ts +++ b/src/client/views/nodes/DataVizBox/DocCreatorMenu/TemplateFieldTypes/StaticContentField.ts @@ -29,6 +29,7 @@ export abstract class StaticContentField extends TemplateField { export class ImageTemplateField extends StaticContentField { setContent(url: string, type?: ViewType) { this.setDataContent(ViewType.IMG, 'data', new ImageField(url), url, type); + this._renderDoc!['backgroundColor'] = 'white'; } initRenderDoc(settings: FieldSettings) { @@ -39,8 +40,11 @@ export class ImageTemplateField extends StaticContentField { } updateDocSetting(setting: string, newVal: string) { - if (setting === 'backgroundColor') return; - super.updateDocSetting(setting, newVal); + if (this._renderDoc) this._renderDoc[setting] = newVal; + if (setting !== 'backgroundColor') { + const settings: {[s: string]: string } = {[setting]: newVal} + Object.assign(this.settings.opts, settings); + } } } diff --git a/src/server/ApiManagers/FireflyManager.ts b/src/server/ApiManagers/FireflyManager.ts index a272494b8..955504c23 100644 --- a/src/server/ApiManagers/FireflyManager.ts +++ b/src/server/ApiManagers/FireflyManager.ts @@ -37,8 +37,8 @@ export default class FireflyManager extends ApiManager { body: JSON.stringify({ prompt, numVariations: variations, - detailLevel: 'preview', - modelVersion: 'image3_fast', + detailLevel: 'high', + modelVersion: 'image3', size: { width, height }, structure: !structureUrl ? undefined |