diff options
Diffstat (limited to 'src/client/views/nodes/DataVizBox/DocCreatorMenu/DocCreatorMenu.tsx')
-rw-r--r-- | src/client/views/nodes/DataVizBox/DocCreatorMenu/DocCreatorMenu.tsx | 1153 |
1 files changed, 149 insertions, 1004 deletions
diff --git a/src/client/views/nodes/DataVizBox/DocCreatorMenu/DocCreatorMenu.tsx b/src/client/views/nodes/DataVizBox/DocCreatorMenu/DocCreatorMenu.tsx index 64416c26d..fb083ea75 100644 --- a/src/client/views/nodes/DataVizBox/DocCreatorMenu/DocCreatorMenu.tsx +++ b/src/client/views/nodes/DataVizBox/DocCreatorMenu/DocCreatorMenu.tsx @@ -1,36 +1,32 @@ +import { IconProp } from '@fortawesome/fontawesome-svg-core'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { Colors } from '@dash/components'; import { action, computed, makeObservable, observable, runInAction } from 'mobx'; import { observer } from 'mobx-react'; -import { IDisposer } from 'mobx-utils'; import * as React from 'react'; -import ReactLoading from 'react-loading'; -import { ClientUtils, returnEmptyFilter, returnFalse, setupMoveUpEvents } from '../../../../../ClientUtils'; +import { returnFalse, setupMoveUpEvents } from '../../../../../ClientUtils'; import { emptyFunction } from '../../../../../Utils'; -import { Doc, NumListCast, StrListCast, returnEmptyDoclist } from '../../../../../fields/Doc'; +import { Doc, StrListCast } from '../../../../../fields/Doc'; import { Id } from '../../../../../fields/FieldSymbols'; -import { ImageCast, StrCast } from '../../../../../fields/Types'; -import { ImageField } from '../../../../../fields/URLField'; -import { Networking } from '../../../../Network'; -import { GPTCallType, gptAPICall, gptImageCall } from '../../../../apis/gpt/GPT'; -import { Docs, DocumentOptions } from '../../../../documents/Documents'; +import { GPTCallType, gptAPICall } from '../../../../apis/gpt/GPT'; import { DragManager } from '../../../../util/DragManager'; import { SnappingManager } from '../../../../util/SnappingManager'; import { UndoManager, undoable } from '../../../../util/UndoManager'; import { ObservableReactComponent } from '../../../ObservableReactComponent'; import { CollectionFreeFormView } from '../../../collections/collectionFreeForm/CollectionFreeFormView'; -import { DocumentView, DocumentViewInternal } from '../../DocumentView'; +import { DocumentView } from '../../DocumentView'; import { OpenWhere } from '../../OpenWhere'; import { DataVizBox } from '../DataVizBox'; import './DocCreatorMenu.scss'; -import { DefaultStyleProvider } from '../../../StyleProvider'; -import { Transform } from '../../../../util/Transform'; -import { TemplateFieldSize, TemplateFieldType, TemplateLayouts } from './TemplateBackend'; -import { TemplateManager } from './TemplateManager'; +import { ViewType } from './TemplateFieldTypes/TemplateField'; import { Template } from './Template'; -import { Field, FieldContentType } from './FieldTypes/Field'; -import { IconProp } from '@fortawesome/fontawesome-svg-core'; -import { Upload } from '../../../../../server/SharedMediaTypes'; +import { TemplateFieldSize, TemplateFieldType, TemplateLayouts } from './TemplateBackend'; +import { TemplateManager } from './Backend/TemplateManager'; +import { TemplateMenuAIUtils } from './Backend/TemplateMenuAIUtils'; +import { TemplatePreviewGrid } from './Menu/TemplatePreviewGrid'; +import { FireflyStructureOptions, TemplateEditingWindow } from './Menu/TemplateEditingWindow'; +import { DocCreatorMenuButton } from './Menu/DocCreatorMenuButton'; +import { TemplatesRenderPreviewWindow } from './Menu/TemplateRenderPreviewWindow'; +import { TemplateMenuFieldOptions } from './Menu/TemplateMenuFieldOptions'; export enum LayoutType { FREEFORM = 'Freeform', @@ -61,6 +57,7 @@ export type Col = { title: string; type: TemplateFieldType; defaultContent?: string; + AIGenerated?: boolean; }; interface DocCreateMenuProps { @@ -72,33 +69,20 @@ export class DocCreatorMenu extends ObservableReactComponent<DocCreateMenuProps> // eslint-disable-next-line no-use-before-define static Instance: DocCreatorMenu; - private _disposers: { [name: string]: IDisposer } = {}; - + DEBUG_MODE: boolean = false; private _ref: HTMLDivElement | null = null; - private templateManager: TemplateManager; - @observable _fullyRenderedDocs: Doc[] = []; - @observable _renderedDocCollectionPreview: Doc | undefined = undefined; - @observable _renderedDocCollection: Doc | undefined = undefined; - @observable _docsRendering: boolean = false; + @observable _docsRendering: boolean = false; // dictates loading symbol - @observable _userTemplates: { template: Template; doc: Doc }[] = []; //!!! used to keep track of all templates, should be refactored to work with actual templates and not docs + @observable _userTemplates: Template[] = []; @observable _selectedTemplate: Template | undefined = undefined; @observable _currEditingTemplate: Template | undefined = undefined; + @observable _editedTemplateTrail: Template[] = []; @observable _userCreatedFields: Col[] = []; - @observable _selectedCols: { title: string; type: string; desc: string }[] | undefined = []; - - @observable _layout: { type: LayoutType; yMargin: number; xMargin: number; columns?: number; repeat: number } = { type: LayoutType.FREEFORM, yMargin: 10, xMargin: 10, columns: 3, repeat: 0 }; - @observable _layoutPreviewScale: number = 1; - @observable _savedLayouts: DataVizTemplateLayout[] = []; - @observable _expandedPreview: Doc | undefined = undefined; @observable _suggestedTemplates: Template[] = []; - @observable _suggestedTemplatePreviews: { doc: Doc; template: Template }[] = []; - @observable _GPTOpt: boolean = false; - @observable _callCount: number = 0; @observable _GPTLoading: boolean = false; @observable _pageX: number = 0; @@ -110,9 +94,8 @@ export class DocCreatorMenu extends ObservableReactComponent<DocCreateMenuProps> @observable _startPos?: { x: number; y: number }; @observable _shouldDisplay: boolean = false; - @observable _menuContent: 'templates' | 'options' | 'saved' | 'dashboard' = 'templates'; + @observable _menuContent: 'templates' | 'renderPreview' | 'saved' | 'dashboard' | 'templateEditing' = 'templates'; @observable _dragging: boolean = false; - @observable _draggingIndicator: boolean = false; @observable _dataViz?: DataVizBox; @observable _interactionLock: boolean | undefined; @observable _snapPt: { x: number; y: number } = { x: 0, y: 0 }; @@ -122,7 +105,8 @@ export class DocCreatorMenu extends ObservableReactComponent<DocCreateMenuProps> @observable _resizeUndo: UndoManager.Batch | undefined = undefined; @observable _initDimensions: { width: number; height: number; x?: number; y?: number } = { width: 300, height: 400, x: undefined, y: undefined }; @observable _menuDimensions: { width: number; height: number } = { width: 400, height: 400 }; - @observable _editing: boolean = false; + + @observable _variations: Template[] = []; constructor(props: DocCreateMenuProps) { super(props); @@ -134,56 +118,13 @@ export class DocCreatorMenu extends ObservableReactComponent<DocCreateMenuProps> @action setDataViz = (dataViz: DataVizBox) => { this._dataViz = dataViz; this._selectedTemplate = undefined; - this._renderedDocCollection = undefined; - this._renderedDocCollectionPreview = undefined; - this._fullyRenderedDocs = []; - this._suggestedTemplatePreviews = []; this._suggestedTemplates = []; this._userCreatedFields = []; }; - @action addUserTemplate = (template: Template) => { - this._userTemplates.push({ template: template.cloneBase(), doc: template.getRenderedDoc() }); - }; - @action removeUserTemplate = (template: Template) => { - this._userTemplates = this._userTemplates.filter(info => info.template !== template); - }; - @action updateTemplatePreview = (template: Template) => { - template.renderUpdates(); - const preview = { template: template, doc: template.getRenderedDoc() }; - this._suggestedTemplatePreviews = this._suggestedTemplatePreviews.map(t => { return t.template === preview.template ? preview : t }); //prettier-ignore - this._userTemplates = this._userTemplates.map(t => { return t.template === preview.template ? preview : t }); //prettier-ignore - }; @action setSuggestedTemplates = (templates: Template[]) => { - this._suggestedTemplates = templates; - this._suggestedTemplatePreviews = templates.map(template => {return {template: template, doc: template.getRenderedDoc()}}); //prettier-ignore + this._suggestedTemplates = templates; //prettier-ignore }; - @computed get docsToRender() { - 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); } @@ -210,10 +151,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 }; @@ -221,17 +158,11 @@ export class DocCreatorMenu extends ObservableReactComponent<DocCreateMenuProps> } setUpButtonClick = (e: React.PointerEvent, func: () => void) => { - setupMoveUpEvents( - this, - e, - returnFalse, - emptyFunction, - undoable(clickEv => { - clickEv.stopPropagation(); - clickEv.preventDefault(); - func(); - }, 'create docs') - ); + setupMoveUpEvents(this, e, returnFalse, emptyFunction, clickEv => { + clickEv.stopPropagation(); + clickEv.preventDefault(); + undoable(func, 'create docs')(); + }); }; @action @@ -269,7 +200,6 @@ export class DocCreatorMenu extends ObservableReactComponent<DocCreateMenuProps> } componentWillUnmount() { - Object.values(this._disposers).forEach(disposer => disposer?.()); document.removeEventListener('pointerdown', this.onPointerDown, true); document.removeEventListener('pointerup', this.onPointerUp); } @@ -319,10 +249,10 @@ export class DocCreatorMenu extends ObservableReactComponent<DocCreateMenuProps> const { scale, refPt, transl } = this.getResizeVals(thisPt, dragHdl); !this._interactionLock && runInAction(async () => { // resize selected docs if we're not in the middle of a resize (ie, throttle input events to frame rate) - this._interactionLock = true; - const scaleAspect = {x: scale.x, y: scale.y}; - this.resizeView(refPt, scaleAspect, transl); // prettier-ignore - await new Promise<boolean | undefined>(res => { setTimeout(() => { res(this._interactionLock = undefined)})}); + this._interactionLock = true; + const scaleAspect = {x: scale.x, y: scale.y}; + this.resizeView(refPt, scaleAspect, transl); // prettier-ignore + await new Promise<boolean | undefined>(res => { setTimeout(() => { res(this._interactionLock = undefined)})}); }); // prettier-ignore return true; }; @@ -365,66 +295,29 @@ export class DocCreatorMenu extends ObservableReactComponent<DocCreateMenuProps> this._pageY = y + translation.y; }; - async getIcon(doc: Doc) { - const docView = DocumentView.getDocumentView(doc); - if (docView) { - docView.ComponentView?.updateIcon?.(); - return new Promise<ImageField | undefined>(res => setTimeout(() => res(ImageCast(docView.Document.icon)), 500)); - } - return undefined; + async createDocsForPreview() { + return this._dataViz && this._selectedTemplate ? ((await this.templateManager.createDocsFromTemplate(this._dataViz, this._selectedTemplate, this.fieldsInfos, this.DEBUG_MODE)).filter(doc => doc).map(doc => doc!) ?? []) : []; } - @action updateSelectedTemplate = async (template: Template) => { - if (this._selectedTemplate === template) { - this._selectedTemplate = undefined; - return; - } else { - this._selectedTemplate = template; - template.renderUpdates(); - this._fullyRenderedDocs = (await this.createDocsFromTemplate(template)) ?? []; - this.updateRenderedDocCollection(); - } - }; - - @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; + @action updateSelectedTemplate = (template: Template) => { + this._selectedTemplate = this._selectedTemplate === template ? undefined : template; // toggle selection }; - editTemplate = (doc: Doc) => { - DocumentViewInternal.addDocTabFunc(doc, OpenWhere.addRight); - DocumentView.DeselectAll(); - Doc.UnBrushDoc(doc); - }; + // testTemplate = async () => { + // this._suggestedTemplates = this.templateManager.templates; //prettier-ignore + // }; @action addField = () => { - const newFields: Col[] = this._userCreatedFields.concat([{ title: '', type: TemplateFieldType.UNSET, desc: '', sizes: [] }]); - this._userCreatedFields = newFields; + this._userCreatedFields = this._userCreatedFields.concat([{ title: '', type: TemplateFieldType.UNSET, desc: '', sizes: [], AIGenerated: true }]); }; @action removeField = (field: { title: string; type: string; desc: string }) => { if (this._dataViz?.axes.includes(field.title)) { this._dataViz.selectAxes(this._dataViz.axes.filter(col => col !== field.title)); } else { - const toRemove = this._userCreatedFields.filter(f => f === field); - if (!toRemove) return; - - if (toRemove.length > 1) { - while (toRemove.length > 1) { - toRemove.pop(); - } - } - - if (this._userCreatedFields.length === 1) { - this._userCreatedFields = []; - } else { - this._userCreatedFields.splice(this._userCreatedFields.indexOf(toRemove[0]), 1); + const toRemove = this._userCreatedFields.findIndex(f => f === field); + if (toRemove !== -1) { + this._userCreatedFields.splice(toRemove, 1); } } }; @@ -439,11 +332,18 @@ export class DocCreatorMenu extends ObservableReactComponent<DocCreateMenuProps> }; @action setColType = (column: Col, type: TemplateFieldType) => { + if (type === TemplateFieldType.DATA) { + this.templateManager.addDataField(column.title); + } else if (column.type === TemplateFieldType.DATA) { + this.templateManager.removeDataField(column.title); + } + if (this.selectedFields.includes(column.title)) { this._dataViz?.setColumnType(column.title, type); } else { column.type = type; } + this.forceUpdate(); }; @@ -469,81 +369,22 @@ export class DocCreatorMenu extends ObservableReactComponent<DocCreateMenuProps> this.forceUpdate(); }; - generateGPTImage = async (prompt: string): Promise<string | undefined> => { - try { - const res = await gptImageCall(prompt); - - if (res) { - const result = (await Networking.PostToServer('/uploadRemoteImage', { sources: res })) as Upload.FileInformation[]; - const source = ClientUtils.prepend(result[0].accessPaths.agnostic.client); - return source; - } - } catch (e) { - console.log(e); - } - }; - - /** - * Populates a preset template framework with content from a datavizbox or any AI-generated content. - * @param template the preloaded template framework being filled in - * @param assignments a list of template field numbers (from top to bottom) and their assigned columns from the linked dataviz - * @returns a doc containing the fully rendered template - */ - applyGPTContentToTemplate = async (template: Template, assignments: { [field: string]: Col }): Promise<Template | undefined> => { - const GPTTextCalls = Object.entries(assignments).filter(([, col]) => col.type === TemplateFieldType.TEXT && this._userCreatedFields.includes(col)); - const GPTIMGCalls = Object.entries(assignments).filter(([, col]) => col.type === TemplateFieldType.VISUAL && this._userCreatedFields.includes(col)); - - if (GPTTextCalls.length) { - const promises = GPTTextCalls.map(([str, col]) => { - return this.renderGPTTextCall(template, col, Number(str)); - }); - - await Promise.all(promises); - } - - if (GPTIMGCalls.length) { - const promises = GPTIMGCalls.map(async ([fieldNum, col]) => { - return this.renderGPTImageCall(template, col, Number(fieldNum)); - }); - - await Promise.all(promises); - } - - return template; - }; - - compileFieldDescriptions = (templates: Template[]): string => { - let descriptions: string = ''; - templates.forEach(template => { - descriptions += `---------- NEW TEMPLATE TO INCLUDE: The title is: ${template.mainField.getTitle()}. Its fields are: `; - descriptions += template.descriptionSummary; - }); + compileFieldDescriptions = (templates: Template[]) => + templates.map(template => `---------- NEW TEMPLATE TO INCLUDE: The title is: ${template.title}. Its fields are: ` + template.descriptionSummary).join(''); // prettier-ignore - return descriptions; - }; + compileColDescriptions = (cols: Col[]) => + ' ------------- COL DESCRIPTIONS START HERE:' + cols.map(col => `{title: ${col.title}, sizes: ${String(col.sizes)}, type: ${col.type}, descreiption: ${col.desc}} `).join(''); // prettier-ignore - compileColDescriptions = (cols: Col[]): string => { - let descriptions: string = ' ------------- COL DESCRIPTIONS START HERE:'; - cols.forEach(col => (descriptions += `{title: ${col.title}, sizes: ${String(col.sizes)}, type: ${col.type}, descreiption: ${col.desc}} `)); - - return descriptions; - }; - - getColByTitle = (title: string) => { - return this.fieldsInfos.filter(col => col.title === title)[0]; - }; + getColByTitle = (title: string): Col | undefined => this.fieldsInfos.filter(col => col.title === title)[0]; @action assignColsToFields = async (templates: Template[], cols: Col[]): Promise<[Template, { [field: number]: Col }][]> => { - const fieldDescriptions: string = this.compileFieldDescriptions(templates); - const colDescriptions: string = this.compileColDescriptions(cols); + const fieldDescriptions = this.compileFieldDescriptions(templates); + const colDescriptions = this.compileColDescriptions(cols); const inputText = fieldDescriptions.concat(colDescriptions); - ++this._callCount; - const origCount = this._callCount; - - const prompt: string = `(${origCount}) ${inputText}`; + const prompt = `(${Math.random() * 100000}) ${inputText}`; this._GPTLoading = true; @@ -555,24 +396,26 @@ export class DocCreatorMenu extends ObservableReactComponent<DocCreateMenuProps> const brokenDownAssignments: [Template, { [fieldID: number]: Col }][] = []; Object.entries(assignments).forEach(([tempTitle, assignment]) => { - const template = templates.filter(t => t.mainField.getTitle() === tempTitle)[0]; - if (!template) return; - const toObj = Object.entries(assignment).reduce( - (a, [fieldID, colTitle]) => { - const col = this.getColByTitle(colTitle); - if (!this._userCreatedFields.includes(col)) { - // do the following for any fields not added by the user; will change in the future, for now only GPT content works with user-added fields - const field = template.getFieldByID(Number(fieldID)); - field.setContent(col.defaultContent ?? '', col.type === TemplateFieldType.VISUAL ? FieldContentType.IMAGE : FieldContentType.STRING); - field.setTitle(col.title); - } else { - a[Number(fieldID)] = this.getColByTitle(colTitle); - } - return a; - }, - {} as { [field: number]: Col } - ); - brokenDownAssignments.push([template, toObj]); + const template = templates.filter(temp => temp.title === tempTitle)[0]; + if (template) { + const toObj = Object.entries(assignment).reduce( + (a, [fieldID, colTitle]) => { + const col = this.getColByTitle(colTitle); + if (col) { + if (!col.AIGenerated) { + const field = template.getFieldByID(Number(fieldID)); + field?.setContent(col.defaultContent ?? '', col.type === TemplateFieldType.VISUAL ? ViewType.IMG : ViewType.TEXT); + field?.setTitle(col.title); + } else { + a[Number(fieldID)] = col; + } + } + return a; + }, + {} as { [field: number]: Col } + ); + brokenDownAssignments.push([template, toObj]); + } }); return brokenDownAssignments; @@ -584,777 +427,100 @@ export class DocCreatorMenu extends ObservableReactComponent<DocCreateMenuProps> return []; }; - generatePresetTemplates = async () => { - this._dataViz?.updateColDefaults(); - - const cols = this.fieldsInfos; - const templates = this.templateManager.getValidTemplates(cols); - - const assignments: [Template, { [field: number]: Col }][] = await this.assignColsToFields(templates, cols); - - const renderedTemplatePromises: Promise<Template | undefined>[] = assignments.map(([template, asns]) => this.applyGPTContentToTemplate(template, asns)); - - await Promise.all(renderedTemplatePromises); - - setTimeout(() => { - this.setSuggestedTemplates(templates); + generatePresetTemplates = action(() => { + if (this.DEBUG_MODE) { + this.setSuggestedTemplates(this.templateManager._templates); this._GPTLoading = false; - }); - }; - - renderGPTImageCall = async (template: Template, col: Col, fieldNumber: number): Promise<boolean> => { - const generateAndLoadImage = async (fieldNum: string, column: Col, prompt: string) => { - const url = await this.generateGPTImage(prompt); - const field: Field = template.getFieldByID(Number(fieldNum)); - - field.setContent(url ?? '', FieldContentType.IMAGE); - field.setTitle(column.title); - }; - - const fieldContent: string = template.compiledContent; - - try { - const sysPrompt = - 'Your job is to create a prompt for an AI image generator to help it generate an image based on existing content in a template and a user prompt. Your prompt should focus heavily on visual elements to help the image generator; avoid unecessary info that might distract it. ONLY INCLUDE THE PROMPT, NO OTHER TEXT OR EXPLANATION. The existing content is as follows: ' + - fieldContent + - ' **** The user prompt is: ' + - col.desc; - - const prompt = await gptAPICall(sysPrompt, GPTCallType.COMPLETEPROMPT); - - await generateAndLoadImage(String(fieldNumber), col, prompt); - } catch (e) { - console.log(e); + } else { + this._dataViz?.updateColDefaults(); + const contentFields = this.fieldsInfos.filter(field => field.type !== TemplateFieldType.DATA); + const templates = this.templateManager.getValidTemplates(contentFields); + + return this.assignColsToFields(templates, contentFields) + .then(pairs => + Promise.all(pairs.map(([templ, assgns]) => TemplateMenuAIUtils.applyGPTContentToTemplate(templ, assgns)))) + .then(action(() => { + this.setSuggestedTemplates(templates); + this._GPTLoading = false; + })); // prettier-ignore } - return true; - }; - - renderGPTTextCall = async (template: Template, col: Col, fieldNum: number): Promise<boolean> => { - const wordLimit = (size: TemplateFieldSize) => { - switch (size) { - case TemplateFieldSize.TINY: - return 2; - case TemplateFieldSize.SMALL: - return 5; - case TemplateFieldSize.MEDIUM: - return 20; - case TemplateFieldSize.LARGE: - return 50; - case TemplateFieldSize.HUGE: - return 100; - default: - return 10; - } - }; - - const textAssignment = `--- title: ${col.title}, prompt: ${col.desc}, word limit: ${wordLimit(col.sizes[0])} words, assigned field: ${fieldNum} ---`; + }); - const fieldContent: string = template.compiledContent; - - try { - const prompt = fieldContent + textAssignment; + // eslint-disable-next-line @typescript-eslint/no-unused-vars + generateVariations = async (onDoc: Doc, prompt: string, options: FireflyStructureOptions) => { + // const { numVariations, temperature, useStyleRef } = options; + this.variations = []; + const mainCollection = this._dataViz?.DocumentView?.().containerViewPath?.().lastElement()?.ComponentView as CollectionFreeFormView; - const res = await gptAPICall(`${++this._callCount}: ${prompt}`, GPTCallType.FILL); + const clone = Doc.MakeClone(onDoc).clone; + mainCollection.addDocument(clone); + clone.x = 10000; + clone.y = 10000; - if (res) { - const assignments: { [title: string]: { number: string; content: string } } = JSON.parse(res); - Object.entries(assignments).forEach(([title, info]) => { - const field: Field = template.getFieldByID(Number(info.number)); - const column = this.getColByTitle(title); - - field.setContent(info.content ?? '', FieldContentType.STRING); - field.setTitle(column.title); - }); - } - } catch (err) { - console.log(err); - } + // await DrawingFillHandler.drawingToImage(clone, 100 - temperature, prompt, useStyleRef ? clone : undefined, this, numVariations) - return true; + return this.variations; }; - createDocsFromTemplate = async (template: Template) => { - const dv = this._dataViz; - - if (!dv) return; - - this._docsRendering = true; - - const fields: string[] = Array.from(Object.keys(dv.records[0])); - const selectedRows = NumListCast(dv.layoutDoc.dataViz_selectedRows); - - const rowContents: { [title: string]: string }[] = selectedRows.map(row => { - const values: { [title: string]: string } = {}; - fields.forEach(col => { - values[col] = dv.records[row][col]; - }); + variations: string[] = []; - return values; - }); - - const processContent = async (content: { [title: string]: string }) => { - const templateCopy = template.cloneBase(); - - fields - .filter(title => title) - .forEach(title => { - const field = templateCopy.getFieldByTitle(title); - if (field === undefined) { - return; - } - field.setContent(content[title]); - }); - - const gptPromises = this._userCreatedFields - .filter(field => field.type === TemplateFieldType.TEXT) - .map(field => { - const title = field.title; - const templateField = templateCopy.getFieldByTitle(title); - if (templateField === undefined) { - return; - } - const templatefieldID = templateField.getID; - - return this.renderGPTTextCall(templateCopy, field, templatefieldID); - }); - - const imagePromises = this._userCreatedFields - .filter(field => field.type === TemplateFieldType.VISUAL) - .map(field => { - const title = field.title; - const templateField = templateCopy.getFieldByTitle(title); - if (templateField === undefined) { - return; - } - const templatefieldID = templateField.getID; - - return this.renderGPTImageCall(templateCopy, field, templatefieldID); - }); - - await Promise.all(gptPromises); - - await Promise.all(imagePromises); - - return templateCopy.getRenderedDoc(); - }; - - const promises = rowContents.map(content => processContent(content)); - - const renderedDocs = await Promise.all(promises); - - this._docsRendering = false; + @action addVariation = (url: string) => { + this.variations.push(url); + }; - return renderedDocs; + addRenderedCollectionToMainview = (collection: Doc) => { + if (collection) { + const mainCollection = this._dataViz?.DocumentView?.().containerViewPath?.().lastElement()?.ComponentView as CollectionFreeFormView; + collection.x = this._pageX - this._menuDimensions.width; + collection.y = this._pageY - this._menuDimensions.height; + mainCollection?.addDocument(collection); + this.closeMenu(); + } }; - addRenderedCollectionToMainview = () => { - const collection = this._renderedDocCollection; - if (!collection) return; - const mainCollection = this._dataViz?.DocumentView?.().containerViewPath?.().lastElement()?.ComponentView as CollectionFreeFormView; - collection.x = this._pageX - this._menuDimensions.width; - collection.y = this._pageY - this._menuDimensions.height; - mainCollection.addDocument(collection); - this.closeMenu(); + @action editLastTemplate = () => { + if (this._editedTemplateTrail.length) this._currEditingTemplate = this._editedTemplateTrail.pop(); }; @action setExpandedView = (template: Template | undefined) => { if (template) { - this._currEditingTemplate = template; - this._expandedPreview = template.mainField.renderedDoc(); //Docs.Create.FreeformDocument([doc], { _height: NumListCast(doc._height)[0], _width: NumListCast(doc._width)[0], title: ''}); + this._menuContent = 'templateEditing'; + this._currEditingTemplate && this._editedTemplateTrail.push(this._currEditingTemplate); } else { - this._currEditingTemplate = undefined; - this._expandedPreview = undefined; - } - }; - - get editingWindow() { - const rendered = !this._expandedPreview ? null : ( - <div className="docCreatorMenu-expanded-template-preview"> - <DocumentView - Document={this._expandedPreview} - isContentActive={emptyFunction} - addDocument={returnFalse} - moveDocument={returnFalse} - removeDocument={returnFalse} - PanelWidth={() => this._menuDimensions.width - 10} - PanelHeight={() => this._menuDimensions.height - 60} - ScreenToLocalTransform={() => new Transform(-this._pageX - 5, -this._pageY - 35, 1)} - renderDepth={5} - whenChildContentsActiveChanged={emptyFunction} - focus={emptyFunction} - styleProvider={DefaultStyleProvider} - addDocTab={DocumentViewInternal.addDocTabFunc} - pinToPres={() => undefined} - childFilters={returnEmptyFilter} - childFiltersByRanges={returnEmptyFilter} - searchFilterDocs={returnEmptyDoclist} - fitContentsToBox={returnFalse} - fitWidth={returnFalse} - /> - </div> - ); - - return ( - <div className="docCreatorMenu-expanded-template-preview"> - <div className="top-panel" /> - {rendered} - <div className="right-buttons-panel"> - <button - className="docCreatorMenu-menu-button section-reveal-options top-right" - onPointerDown={e => - this.setUpButtonClick(e, () => { - this._currEditingTemplate && this.updateTemplatePreview(this._currEditingTemplate); - this.setExpandedView(undefined); - }) - }> - <FontAwesomeIcon icon="minimize" /> - </button> - <button - className="docCreatorMenu-menu-button section-reveal-options top-right-lower" - onPointerDown={e => - this.setUpButtonClick(e, () => { - this._currEditingTemplate?.resetToBase(); - this.setExpandedView(this._currEditingTemplate); - }) - }> - <FontAwesomeIcon icon="arrows-rotate" color="white" /> - </button> - </div> - </div> - ); - } - - get templatesPreviewContents() { - const GPTOptions = <div></div>; - - return ( - <div className={`docCreatorMenu-templates-view`}> - {this._expandedPreview ? ( - this.editingWindow - ) : ( - <div> - <div className="docCreatorMenu-section" style={{ height: this._GPTOpt ? 200 : 200 }}> - <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, () => runInAction(() => (this._menuContent = 'dashboard')))}> - <FontAwesomeIcon icon="gear" /> - </button> - </div> - <div className="docCreatorMenu-templates-preview-window" style={{ justifyContent: this._GPTLoading || this._menuDimensions.width > 400 ? 'center' : '' }}> - {this._GPTLoading ? ( - <div className="loading-spinner"> - <ReactLoading type="spin" color={StrCast(Doc.UserDoc().userVariantColor)} height={30} width={30} /> - </div> - ) : ( - this._suggestedTemplatePreviews.map(({ doc, template }) => ( - <div - className="docCreatorMenu-preview-window" - key="0" - style={{ - border: this._selectedTemplate === template ? `solid 3px ${Colors.MEDIUM_BLUE}` : '', - boxShadow: this._selectedTemplate === template ? `0 0 15px rgba(68, 118, 247, .8)` : '', - }} - onPointerDown={e => this.setUpButtonClick(e, () => runInAction(() => this.updateSelectedTemplate(template)))}> - <button - className="option-button left" - onPointerDown={e => - this.setUpButtonClick(e, () => { - this.setExpandedView(template); - }) - }> - <FontAwesomeIcon icon="magnifying-glass" color="white" /> - </button> - <button className="option-button right" onPointerDown={e => this.setUpButtonClick(e, () => this.addUserTemplate(template))}> - <FontAwesomeIcon icon="plus" color="white" /> - </button> - <DocumentView - Document={doc} - isContentActive={emptyFunction} // !!! should be return false - addDocument={returnFalse} - moveDocument={returnFalse} - removeDocument={returnFalse} - PanelWidth={() => (this._selectedTemplate === template ? 104 : 111)} - PanelHeight={() => (this._selectedTemplate === template ? 104 : 111)} - ScreenToLocalTransform={() => new Transform(-this._pageX - 5, -this._pageY - 35, 1)} - renderDepth={1} - whenChildContentsActiveChanged={emptyFunction} - focus={emptyFunction} - styleProvider={DefaultStyleProvider} - addDocTab={this._props.addDocTab} - pinToPres={() => undefined} - childFilters={returnEmptyFilter} - childFiltersByRanges={returnEmptyFilter} - searchFilterDocs={returnEmptyDoclist} - fitContentsToBox={returnFalse} - fitWidth={returnFalse} - hideDecorations={true} - /> - </div> - )) - )} - </div> - <div className="docCreatorMenu-GPT-options"> - <div className="docCreatorMenu-GPT-options-container"> - <button className="docCreatorMenu-menu-button" onPointerDown={e => this.setUpButtonClick(e, () => this.generatePresetTemplates())}> - <FontAwesomeIcon icon="arrows-rotate" /> - </button> - </div> - {this._GPTOpt ? GPTOptions : null} - </div> - </div> - <hr className="docCreatorMenu-option-divider full no-margin" /> - <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-templates-preview-window" style={{ justifyContent: this._menuDimensions.width > 400 ? 'center' : '' }}> - <div className="docCreatorMenu-preview-window empty"> - <FontAwesomeIcon icon="plus" color="rgb(160, 160, 160)" /> - </div> - {this._userTemplates.map(({ template, doc }) => ( - <div - className="docCreatorMenu-preview-window" - key="0" - style={{ - border: this._selectedTemplate === template ? `solid 3px ${Colors.MEDIUM_BLUE}` : '', - boxShadow: this._selectedTemplate === template ? `0 0 15px rgba(68, 118, 247, .8)` : '', - }} - onPointerDown={e => this.setUpButtonClick(e, () => runInAction(() => this.updateSelectedTemplate(template)))}> - <button - className="option-button left" - onPointerDown={e => - this.setUpButtonClick(e, () => { - this.setExpandedView(template); - }) - }> - <FontAwesomeIcon icon="magnifying-glass" color="white" /> - </button> - <button className="option-button right" onPointerDown={e => this.setUpButtonClick(e, () => this.removeUserTemplate(template))}> - <FontAwesomeIcon icon="minus" color="white" /> - </button> - <DocumentView - Document={doc} - isContentActive={emptyFunction} // !!! should be return false - addDocument={returnFalse} - moveDocument={returnFalse} - removeDocument={returnFalse} - PanelWidth={() => (this._selectedTemplate === template ? 104 : 111)} - PanelHeight={() => (this._selectedTemplate === template ? 104 : 111)} - ScreenToLocalTransform={() => new Transform(-this._pageX - 5, -this._pageY - 35, 1)} - renderDepth={1} - whenChildContentsActiveChanged={emptyFunction} - focus={emptyFunction} - styleProvider={DefaultStyleProvider} - addDocTab={this._props.addDocTab} - pinToPres={() => undefined} - childFilters={returnEmptyFilter} - childFiltersByRanges={returnEmptyFilter} - searchFilterDocs={returnEmptyDoclist} - fitContentsToBox={returnFalse} - fitWidth={returnFalse} - hideDecorations={true} - /> - </div> - ))} - </div> - </div> - </div> - )} - </div> - ); - } - - @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; + this._menuContent = 'templates'; } - } - applyLayout = (collection: Doc, docs: Doc[]) => { - const { horizontalSpan, verticalSpan } = this.previewInfo; - collection._height = verticalSpan; - collection._width = horizontalSpan; - - const layout = this._layout; - const columns: number = layout.columns ?? this.columnsCount; - const xGap: number = layout.xMargin; - const yGap: number = layout.yMargin; - // const repeat: number = templateInfo.layout.repeat; - 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; - } + this._currEditingTemplate = template; - 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; - } + //Docs.Create.FreeformDocument([doc], { _height: NumListCast(doc._height)[0], _width: NumListCast(doc._width)[0], title: ''}); }; @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 { horizontalSpan, verticalSpan } = this.previewInfo; - - 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; - } - }; - - const collection: Doc = collectionFactory()(this._fullyRenderedDocs, { - isDefaultTemplateDoc: true, - _height: verticalSpan, - _width: horizontalSpan, - title: 'title', - backgroundColor: 'gray', - }); - - this.applyLayout(collection, this._fullyRenderedDocs); - - this._renderedDocCollection = collection; - }; - - layoutPreviewContents = () => { - 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} - /> - </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> - {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]; - + get templatesView() { 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 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> - {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) 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> ); } - get dashboardContents() { - const sizes: string[] = ['tiny', 'small', 'medium', 'large', 'huge']; - - const fieldPanel = (field: Col, id: number) => { - return ( - <div className="field-panel" key={id}> - <div className="top-bar"> - <span className="field-title">{`${field.title} Field`}</span> - <button className="docCreatorMenu-menu-button section-reveal-options no-margin" onPointerDown={e => this.setUpButtonClick(e, () => this.removeField(field))} style={{ position: 'absolute', right: '0px' }}> - <FontAwesomeIcon icon="minus" /> - </button> - </div> - <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' : ''}</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> - </div> - </div> - </div> - </div> - <div className="sizes-box"> - <div className="top-bar"> Valid Sizes </div> - <div className="content"> - <div className="bubbles"> - {sizes.map(size => ( - <> - <input - className="bubble" - type="checkbox" - name="type" - checked={field.sizes.includes(size as TemplateFieldSize)} - onChange={e => { - this.modifyColSizes(field, size as TemplateFieldSize, e.target.checked); - }} - /> - <div className="text">{size}</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> - ); - }; - - return ( - <div className="docCreatorMenu-dashboard-view"> - <div className="topbar"> - <button className="docCreatorMenu-menu-button section-reveal-options" onPointerDown={e => this.setUpButtonClick(e, this.addField)}> - <FontAwesomeIcon icon="plus" /> - </button> - <button className="docCreatorMenu-menu-button section-reveal-options float-right" onPointerDown={e => this.setUpButtonClick(e, () => runInAction(() => (this._menuContent = 'templates')))}> - <FontAwesomeIcon icon="arrow-left" /> - </button> - </div> - <div className="panels-container">{this.fieldsInfos.map((field, i) => fieldPanel(field, i))}</div> - </div> - ); - } + private optionsButtonOpts: [IconProp, () => void] = ['gear', () => (this._menuContent = 'dashboard')]; get renderSelectedViewType() { switch (this._menuContent) { - case 'templates': - return this.templatesPreviewContents; - case 'options': - return this.optionsMenuContents; - case 'dashboard': - return this.dashboardContents; - default: - return undefined; - } + 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; } get resizePanes() { @@ -1375,34 +541,17 @@ export class DocCreatorMenu extends ObservableReactComponent<DocCreateMenuProps> } render() { - const topButton = (icon: string, opt: string, func: () => void, tag: string) => { - return ( - <div className={`top-button-container ${tag} ${opt === this._menuContent ? 'selected' : ''}`}> - <div - className="top-button-content" - onPointerDown={e => - this.setUpButtonClick(e, () => - runInAction(() => { - func(); - }) - ) - }> - <FontAwesomeIcon icon={icon as IconProp} /> - </div> + const topButton = (icon: string, opt: string, func: () => void, tag: string) => ( + <div className={`top-button-container ${tag} ${opt === this._menuContent ? 'selected' : ''}`}> + <div className="top-button-content" onPointerDown={e => this.setUpButtonClick(e, action(func))}> + <FontAwesomeIcon icon={icon as IconProp} /> </div> - ); - }; + </div> + ); - 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 onPreviewSelected = () => (this._menuContent = 'templates'); + const onSavedSelected = () => (this._menuContent = 'dashboard'); + const onOptionsSelected = () => (this._menuContent = 'renderPreview'); return ( <div className="docCreatorMenu"> @@ -1435,9 +584,7 @@ export class DocCreatorMenu extends ObservableReactComponent<DocCreateMenuProps> return true; }, emptyFunction, - undoable(clickEv => { - clickEv.stopPropagation(); - }, 'drag menu') + undoable(clickEv => clickEv.stopPropagation(), 'drag menu') ) }> <div className="docCreatorMenu-top-buttons-container"> @@ -1445,9 +592,7 @@ export class DocCreatorMenu extends ObservableReactComponent<DocCreateMenuProps> {topButton('magnifying-glass', 'options', onOptionsSelected, 'middle')} {topButton('bars', 'saved', onSavedSelected, 'right')} </div> - <button className="docCreatorMenu-menu-button close-menu" onPointerDown={e => this.setUpButtonClick(e, this.closeMenu)}> - <FontAwesomeIcon icon={'minus'} /> - </button> + <DocCreatorMenuButton icon={'minus'} styles={'float-right'} function={this.closeMenu} /> </div> {this.renderSelectedViewType} </div> |