diff options
Diffstat (limited to 'src/client/views/nodes/DataVizBox/DocCreatorMenu')
8 files changed, 675 insertions, 767 deletions
diff --git a/src/client/views/nodes/DataVizBox/DocCreatorMenu/DocCreatorMenu.tsx b/src/client/views/nodes/DataVizBox/DocCreatorMenu/DocCreatorMenu.tsx index ffdc6626d..a702218b0 100644 --- a/src/client/views/nodes/DataVizBox/DocCreatorMenu/DocCreatorMenu.tsx +++ b/src/client/views/nodes/DataVizBox/DocCreatorMenu/DocCreatorMenu.tsx @@ -1,39 +1,36 @@ +import { Colors } from '@dash/components'; +import { IconProp } from '@fortawesome/fontawesome-svg-core'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { Colors } from 'browndash-components'; -import { action, computed, makeObservable, observable, reaction, runInAction } from 'mobx'; +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, returnTrue, setupMoveUpEvents } from '../../../../../ClientUtils'; +import { ClientUtils, returnEmptyFilter, returnFalse, setupMoveUpEvents } from '../../../../../ClientUtils'; import { emptyFunction } from '../../../../../Utils'; -import { Doc, FieldType, NumListCast, StrListCast, returnEmptyDoclist } from '../../../../../fields/Doc'; +import { Doc, NumListCast, StrListCast, returnEmptyDoclist } from '../../../../../fields/Doc'; import { Id } from '../../../../../fields/FieldSymbols'; -import { Cast, DocCast, ImageCast, StrCast } from '../../../../../fields/Types'; +import { ImageCast, StrCast } from '../../../../../fields/Types'; import { ImageField } from '../../../../../fields/URLField'; +import { Upload } from '../../../../../server/SharedMediaTypes'; import { Networking } from '../../../../Network'; import { GPTCallType, gptAPICall, gptImageCall } from '../../../../apis/gpt/GPT'; import { Docs, DocumentOptions } from '../../../../documents/Documents'; import { DragManager } from '../../../../util/DragManager'; -import { MakeTemplate } from '../../../../util/DropConverter'; import { SnappingManager } from '../../../../util/SnappingManager'; +import { Transform } from '../../../../util/Transform'; import { UndoManager, undoable } from '../../../../util/UndoManager'; -import { LightboxView } from '../../../LightboxView'; import { ObservableReactComponent } from '../../../ObservableReactComponent'; +import { DefaultStyleProvider } from '../../../StyleProvider'; import { CollectionFreeFormView } from '../../../collections/collectionFreeForm/CollectionFreeFormView'; import { DocumentView, DocumentViewInternal } from '../../DocumentView'; -import { FieldViewProps } from '../../FieldView'; import { OpenWhere } from '../../OpenWhere'; import { DataVizBox } from '../DataVizBox'; import './DocCreatorMenu.scss'; -import { DefaultStyleProvider, returnEmptyDocViewList } from '../../../StyleProvider'; -import { Transform } from '../../../../util/Transform'; -import { IconProp } from '@fortawesome/fontawesome-svg-core'; +import { Field, ViewType } from './FieldTypes/Field'; +import { Template } from './Template'; import { TemplateFieldSize, TemplateFieldType, TemplateLayouts } from './TemplateBackend'; import { TemplateManager } from './TemplateManager'; -import { Template } from './Template'; -import { Field, ViewType } from './FieldTypes/Field'; -import { TabDocView } from '../../../collections/TabDocView'; export enum LayoutType { FREEFORM = 'Freeform', @@ -43,8 +40,36 @@ export enum LayoutType { CARD = 'Card View', } +export interface DataVizTemplateInfo { + doc: Doc; + layout: { type: LayoutType; xMargin: number; yMargin: number; repeat: number }; + columns: number; + referencePos: { x: number; y: number }; +} + +export interface DataVizTemplateLayout { + template: Doc; + docsNumList: number[]; + layout: { type: LayoutType; xMargin: number; yMargin: number; repeat: number }; + columns: number; + rows: number; +} + +export type Col = { + sizes: TemplateFieldSize[]; + desc: string; + title: string; + type: TemplateFieldType; + defaultContent?: string; +}; + +interface DocCreateMenuProps { + addDocTab: (doc: Doc | Doc[], where: OpenWhere) => boolean; +} + @observer -export class DocCreatorMenu extends ObservableReactComponent<FieldViewProps> { +export class DocCreatorMenu extends ObservableReactComponent<DocCreateMenuProps> { + // eslint-disable-next-line no-use-before-define static Instance: DocCreatorMenu; private DEBUG_MODE: boolean = false; @@ -53,13 +78,13 @@ export class DocCreatorMenu extends ObservableReactComponent<FieldViewProps> { private _ref: HTMLDivElement | null = null; - private templateManager: TemplateManager; + 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[] = []; + @observable _userTemplates: Template[] = []; @observable _selectedTemplate: Template | undefined = undefined; @observable _currEditingTemplate: Template | undefined = undefined; @@ -71,9 +96,11 @@ export class DocCreatorMenu extends ObservableReactComponent<FieldViewProps> { @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 _DOCCC: Doc | undefined; @observable _pageX: number = 0; @observable _pageY: number = 0; @@ -88,8 +115,8 @@ export class DocCreatorMenu extends ObservableReactComponent<FieldViewProps> { @observable _dragging: boolean = false; @observable _draggingIndicator: boolean = false; @observable _dataViz?: DataVizBox; - @observable _interactionLock: any; - @observable _snapPt: any; + @observable _interactionLock: boolean | undefined; + @observable _snapPt: { x: number; y: number } = { x: 0, y: 0 }; @observable _resizeHdlId: string = ''; @observable _resizing: boolean = false; @observable _offset: { x: number; y: number } = { x: 0, y: 0 }; @@ -98,7 +125,7 @@ export class DocCreatorMenu extends ObservableReactComponent<FieldViewProps> { @observable _menuDimensions: { width: number; height: number } = { width: 400, height: 400 }; @observable _editing: boolean = false; - constructor(props: any) { + constructor(props: DocCreateMenuProps) { super(props); makeObservable(this); DocCreatorMenu.Instance = this; @@ -118,16 +145,16 @@ export class DocCreatorMenu extends ObservableReactComponent<FieldViewProps> { }; @action removeUserTemplate = (template: Template) => { this._userTemplates.splice(this._userTemplates.indexOf(template), 1); - } + }; @action setSuggestedTemplates = (templates: Template[]) => { this._suggestedTemplates = templates; //prettier-ignore }; @computed get docsToRender() { - if (this.DEBUG_MODE){ + if (this.DEBUG_MODE) { return [1, 2, 3, 4]; } else { - return this._selectedTemplate ? NumListCast(this._dataViz?.layoutDoc.dataViz_selectedRows) : []; + return this._selectedTemplate ? NumListCast(this._dataViz?.layoutDoc.dataViz_selectedRows) : []; } } @@ -189,7 +216,7 @@ export class DocCreatorMenu extends ObservableReactComponent<FieldViewProps> { return bounds; } - setUpButtonClick = (e: any, func: Function) => { + setUpButtonClick = (e: React.PointerEvent, func: () => void) => { setupMoveUpEvents( this, e, @@ -235,18 +262,6 @@ export class DocCreatorMenu extends ObservableReactComponent<FieldViewProps> { componentDidMount() { document.addEventListener('pointerdown', this.onPointerDown, true); document.addEventListener('pointerup', this.onPointerUp); - //this._disposers.columns = reaction(() => this._dataViz?.layoutDoc._dataViz_axes, () => {this.generateTemplates('')}) - this._disposers.lightbox = reaction( - () => LightboxView.LightboxDoc(), - doc => { - doc ? this._shouldDisplay && this.closeMenu() : !this._shouldDisplay && this.openMenu(); - } - ); - // this._disposers.layout = reaction( - // () => this._layout, - // layout => { this.updateRenderedDocCollection(); } - // ); - //this._disposers.fields = reaction(() => this._dataViz?.axes, cols => this._selectedCols = cols?.map(col => { return {title: col, type: '', desc: ''}})) } componentWillUnmount() { @@ -256,11 +271,6 @@ export class DocCreatorMenu extends ObservableReactComponent<FieldViewProps> { } @action - updateSelectedCols = (cols: string[]) => { - this._selectedCols; - }; - - @action toggleDisplay = (x: number, y: number) => { if (this._shouldDisplay) { this._shouldDisplay = false; @@ -288,7 +298,7 @@ export class DocCreatorMenu extends ObservableReactComponent<FieldViewProps> { SnappingManager.SetIsResizing(DocumentView.Selected().lastElement()?.Document[Id]); // turns off pointer events on things like youtube videos and web pages so that dragging doesn't get "stuck" when cursor moves over them e.stopPropagation(); const id = (this._resizeHdlId = e.currentTarget.className); - const pad = id.includes('Left') || id.includes('Right') ? Number(getComputedStyle(e.target as any).width.replace('px', '')) / 2 : 0; + const pad = id.includes('Left') || id.includes('Right') ? Number(getComputedStyle(e.target as HTMLElement).width.replace('px', '')) / 2 : 0; const bounds = e.currentTarget.getBoundingClientRect(); this._offset = { x: id.toLowerCase().includes('left') ? bounds.right - e.clientX - pad : bounds.left - e.clientX + pad, // @@ -299,7 +309,7 @@ export class DocCreatorMenu extends ObservableReactComponent<FieldViewProps> { }; @action - onResize = (e: any): boolean => { + onResize = (e: PointerEvent): boolean => { const dragHdl = this._resizeHdlId.split(' ')[1]; const thisPt = DragManager.snapDrag(e, -this._offset.x, -this._offset.y, this._offset.x, this._offset.y); @@ -308,13 +318,13 @@ export class DocCreatorMenu extends ObservableReactComponent<FieldViewProps> { this._interactionLock = true; const scaleAspect = {x: scale.x, y: scale.y}; this.resizeView(refPt, scaleAspect, transl); // prettier-ignore - await new Promise<any>(res => { setTimeout(() => { res(this._interactionLock = undefined)})}); + await new Promise<boolean | undefined>(res => { setTimeout(() => { res(this._interactionLock = undefined)})}); }); // prettier-ignore return true; }; @action - onDrag = (e: any): boolean => { + onDrag = (e: PointerEvent): boolean => { this._pageX = e.pageX - (this._startPos?.x ?? 0); this._pageY = e.pageY - (this._startPos?.y ?? 0); this._initDimensions.x = this._pageX; @@ -324,7 +334,7 @@ export class DocCreatorMenu extends ObservableReactComponent<FieldViewProps> { getResizeVals = (thisPt: { x: number; y: number }, dragHdl: string) => { const [w, h] = [this._initDimensions.width, this._initDimensions.height]; - const [moveX, moveY] = [thisPt.x - this._snapPt.x, thisPt.y - this._snapPt.y]; + const [moveX, moveY] = [thisPt.x - this._snapPt!.x, thisPt.y - this._snapPt!.y]; let vals: { scale: { x: number; y: number }; refPt: [number, number]; transl: { x: number; y: number } }; switch (dragHdl) { case 'topLeft': vals = { scale: { x: 1 - moveX / w, y: 1 -moveY / h }, refPt: [this.bounds.r, this.bounds.b], transl: {x: moveX, y: moveY } }; break; @@ -341,7 +351,6 @@ export class DocCreatorMenu extends ObservableReactComponent<FieldViewProps> { }; resizeView = (refPt: number[], scale: { x: number; y: number }, translation: { x: number; y: number }) => { - const refCent = [refPt[0], refPt[1]]; // fixed reference point for resize (ie, a point that doesn't move) if (this._initDimensions.x === undefined) this._initDimensions.x = this._pageX; if (this._initDimensions.y === undefined) this._initDimensions.y = this._pageY; const { height, width, x, y } = this._initDimensions; @@ -362,10 +371,10 @@ export class DocCreatorMenu extends ObservableReactComponent<FieldViewProps> { } @action updateRenderedPreviewCollection = async (template: Template) => { - this._fullyRenderedDocs = await this.createDocsFromTemplate(template) ?? []; + this._fullyRenderedDocs = (await this.createDocsFromTemplate(template)) ?? []; console.log(this._fullyRenderedDocs); this.updateRenderedDocCollection(); - } + }; @action updateSelectedTemplate = async (template: Template) => { if (this._selectedTemplate === template) { @@ -390,20 +399,18 @@ export class DocCreatorMenu extends ObservableReactComponent<FieldViewProps> { }; editTemplate = (doc: Doc) => { - //this.closeMenu(); 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); @@ -491,13 +498,11 @@ export class DocCreatorMenu extends ObservableReactComponent<FieldViewProps> { }; generateGPTImage = async (prompt: string): Promise<string | undefined> => { - console.log(prompt); - try { const res = await gptImageCall(prompt); if (res) { - const result = await Networking.PostToServer('/uploadRemoteImage', { sources: res }); + const result = (await Networking.PostToServer('/uploadRemoteImage', { sources: res })) as Upload.FileInformation[]; const source = ClientUtils.prepend(result[0].accessPaths.agnostic.client); return source; } @@ -513,11 +518,8 @@ export class DocCreatorMenu extends ObservableReactComponent<FieldViewProps> { * @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(([str, col]) => col.type === TemplateFieldType.TEXT && this._userCreatedFields.includes(col)); - const GPTIMGCalls = Object.entries(assignments).filter(([str, col]) => col.type === TemplateFieldType.VISUAL && this._userCreatedFields.includes(col)); - - let fieldContent: string = template.compiledContent; + 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]) => { @@ -527,15 +529,13 @@ export class DocCreatorMenu extends ObservableReactComponent<FieldViewProps> { await Promise.all(promises); } - console.log(GPTIMGCalls) - if (GPTIMGCalls.length) { const promises = GPTIMGCalls.map(async ([fieldNum, col]) => { return this.renderGPTImageCall(template, col, Number(fieldNum)); }); await Promise.all(promises); - }; + } return template; }; @@ -571,7 +571,7 @@ export class DocCreatorMenu extends ObservableReactComponent<FieldViewProps> { ++this._callCount; const origCount = this._callCount; - let prompt: string = `(${origCount}) ${inputText}`; + const prompt: string = `(${origCount}) ${inputText}`; this._GPTLoading = true; @@ -583,12 +583,13 @@ export class DocCreatorMenu extends ObservableReactComponent<FieldViewProps> { const brokenDownAssignments: [Template, { [fieldID: number]: Col }][] = []; Object.entries(assignments).forEach(([tempTitle, assignment]) => { - const template = templates.filter(template => template.title === tempTitle)[0]; + const template = templates.filter(temp => temp.title === 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 field + 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 field const field = template.getFieldByID(Number(fieldID)); field.setContent(col.defaultContent ?? '', col.type === TemplateFieldType.VISUAL ? ViewType.IMG : ViewType.TEXT); field.setTitle(col.title); @@ -612,18 +613,24 @@ export class DocCreatorMenu extends ObservableReactComponent<FieldViewProps> { }; generatePresetTemplates = async () => { - this._dataViz?.updateColDefaults(); + const templates: Template[] = []; - const cols = this.fieldsInfos; - const templates = this.templateManager.getValidTemplates(cols); + if (this.DEBUG_MODE) { + templates.push(...this.templateManager.templates); + } else { + this._dataViz?.updateColDefaults(); - console.log(templates) + const cols = this.fieldsInfos; + templates.push(...this.templateManager.getValidTemplates(cols)); - const assignments: [Template, { [field: number]: Col }][] = await this.assignColsToFields(templates, cols); + const assignments: [Template, { [field: number]: Col }][] = await this.assignColsToFields(templates, cols); - const renderedTemplatePromises: Promise<Template | undefined>[] = assignments.map(([template, assignments]) => this.applyGPTContentToTemplate(template, assignments)); + const renderedTemplatePromises: Promise<Template | undefined>[] = assignments.map(([template, assgns]) => this.applyGPTContentToTemplate(template, assgns)); + + await Promise.all(renderedTemplatePromises); + } - const renderedTemplates: (Template | undefined)[] = await Promise.all(renderedTemplatePromises); + templates.forEach(template => template.mainField.initializeDocument({ subfields: [], title: template.title, opts: {}, viewType: ViewType.FREEFORM, tl: [0, 0], br: [900, 900] })); setTimeout(() => { this.setSuggestedTemplates(templates); @@ -631,17 +638,16 @@ export class DocCreatorMenu extends ObservableReactComponent<FieldViewProps> { }); }; - renderGPTImageCall = async (template: Template, col: Col, fieldNum: number): Promise<boolean> => { - const generateAndLoadImage = async (fieldNum: string, col: Col, prompt: string) => { + 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); - console.log('url: ', url) const field: Field = template.getFieldByID(Number(fieldNum)); field.setContent(url ?? '', ViewType.IMG); field.setTitle(col.title); }; - let fieldContent: string = template.compiledContent; + const fieldContent: string = template.compiledContent; try { const sysPrompt = @@ -652,12 +658,12 @@ export class DocCreatorMenu extends ObservableReactComponent<FieldViewProps> { const prompt = await gptAPICall(sysPrompt, GPTCallType.COMPLETEPROMPT); - await generateAndLoadImage(String(fieldNum), col, prompt); + await generateAndLoadImage(String(fieldNumber), col, prompt); } catch (e) { console.log(e); } return true; - } + }; renderGPTTextCall = async (template: Template, col: Col, fieldNum: number): Promise<boolean> => { const wordLimit = (size: TemplateFieldSize) => { @@ -679,20 +685,20 @@ export class DocCreatorMenu extends ObservableReactComponent<FieldViewProps> { const textAssignment = `--- title: ${col.title}, prompt: ${col.desc}, word limit: ${wordLimit(col.sizes[0])} words, assigned field: ${fieldNum} ---`; - let fieldContent: string = template.compiledContent; + const fieldContent: string = template.compiledContent; try { const prompt = fieldContent + textAssignment; const res = await gptAPICall(`${++this._callCount}: ${prompt}`, GPTCallType.FILL); - console.log('prompt: ', prompt, ' response: ', res); + // console.log('prompt: ', prompt, ' response: ', res); if (res) { const assignments: { [title: string]: { number: string; content: string } } = JSON.parse(res); - Object.entries(assignments).forEach(([title, info]) => { + Object.entries(assignments).forEach(([, /* title */ info]) => { const field: Field = template.getFieldByID(Number(info.number)); - const col = this.getColByTitle(title); + // const column = this.getColByTitle(title); field.setContent(info.content ?? '', ViewType.TEXT); field.setTitle(col.title); @@ -703,7 +709,7 @@ export class DocCreatorMenu extends ObservableReactComponent<FieldViewProps> { } return true; - } + }; createDocsFromTemplate = async (template: Template) => { const dv = this._dataViz; @@ -712,59 +718,72 @@ export class DocCreatorMenu extends ObservableReactComponent<FieldViewProps> { this._docsRendering = true; - const fields: string[] = Array.from(Object.keys(dv.records[0])); + const fields: string[] = Array.from(Object.keys(dv.records[0])); const selectedRows = NumListCast(dv.layoutDoc.dataViz_selectedRows); - - 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], field.viewType); - }); - - 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 rowContents: { [title: string]: string }[] = selectedRows.map(row => { + const values: { [title: string]: string } = {}; + fields.forEach(col => { + values[col] = dv.records[row][col]; }); - 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); - }); - + return values; + }); + + const processContent = async (content: { [title: string]: string }) => { + const templateCopy = await template.cloneBase(); + + fields + .filter(title => title) + .forEach(title => { + const field = templateCopy.getFieldByTitle(title); + if (field === undefined) return; + field.setContent(content[title], field.viewType); + }); + + 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(); + + this._DOCCC = templateCopy.mainField.renderedDoc; + return templateCopy.mainField.renderedDoc; }; - const rows = [1, 2, 3, 4]; - - //const promises = rowContents.map(content => processContent(content)); - - const promises = rows.map(row => processContent({})); + let docs: Promise<Doc>[]; + if (this.DEBUG_MODE) { + docs = [1, 2, 3, 4].map(() => processContent({})); + } else { + docs = rowContents.map(content => processContent(content)); + } - const renderedDocs = await Promise.all(promises); + const renderedDocs = await Promise.all(docs); this._docsRendering = false; // removes loading indicator - console.log('docs: ', renderedDocs); - console.log('first doc height: ', renderedDocs[0]._height); - return renderedDocs; - } + }; addRenderedCollectionToMainview = () => { const collection = this._renderedDocCollection; @@ -774,7 +793,7 @@ export class DocCreatorMenu extends ObservableReactComponent<FieldViewProps> { collection.y = this._pageY - this._menuDimensions.height; mainCollection.addDocument(collection); this.closeMenu(); - } + }; @action setExpandedView = (template: Template | undefined) => { if (template) { @@ -786,8 +805,8 @@ export class DocCreatorMenu extends ObservableReactComponent<FieldViewProps> { } }; - get editingWindow(){ - const rendered = !this._expandedPreview ? null : + get editingWindow() { + const rendered = !this._expandedPreview ? null : ( <div className="docCreatorMenu-expanded-template-preview"> <DocumentView Document={this._expandedPreview} @@ -797,13 +816,12 @@ export class DocCreatorMenu extends ObservableReactComponent<FieldViewProps> { removeDocument={returnFalse} PanelWidth={() => this._menuDimensions.width - 10} PanelHeight={() => this._menuDimensions.height - 60} - ScreenToLocalTransform={() => new Transform(-this._pageX - 5,-this._pageY - 35, 1)} + ScreenToLocalTransform={() => new Transform(-this._pageX - 5, -this._pageY - 35, 1)} renderDepth={5} whenChildContentsActiveChanged={emptyFunction} focus={emptyFunction} styleProvider={DefaultStyleProvider} addDocTab={DocumentViewInternal.addDocTabFunc} - // eslint-disable-next-line no-use-before-define pinToPres={() => undefined} childFilters={returnEmptyFilter} childFiltersByRanges={returnEmptyFilter} @@ -812,22 +830,34 @@ export class DocCreatorMenu extends ObservableReactComponent<FieldViewProps> { fitWidth={returnFalse} /> </div> + ); return ( <div className="docCreatorMenu-expanded-template-preview"> - <div className="top-panel"/> + <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, () => { - if (!this._currEditingTemplate) return; - if (this._currEditingTemplate === this._selectedTemplate) { - this.updateRenderedPreviewCollection(this._currEditingTemplate); - } - this.setExpandedView(undefined); - })}> + <button + className="docCreatorMenu-menu-button section-reveal-options top-right" + onPointerDown={e => + this.setUpButtonClick(e, () => { + if (!this._currEditingTemplate) return; + if (this._currEditingTemplate === this._selectedTemplate) { + this.updateRenderedPreviewCollection(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?.printFieldInfo();/*this._currEditingTemplate?.resetToBase();*/ this.setExpandedView(this._currEditingTemplate);})}> + <button + className="docCreatorMenu-menu-button section-reveal-options top-right-lower" + onPointerDown={e => + this.setUpButtonClick(e, () => { + this._currEditingTemplate?.printFieldInfo(); + /*this._currEditingTemplate?.resetToBase();*/ this.setExpandedView(this._currEditingTemplate); + }) + }> <FontAwesomeIcon icon="arrows-rotate" color="white" /> </button> </div> @@ -836,33 +866,32 @@ export class DocCreatorMenu extends ObservableReactComponent<FieldViewProps> { } get templatesPreviewContents() { - const GPTOptions = <div></div>; - const previewDoc = (doc: Doc, template: Template) => - <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} - // eslint-disable-next-line no-use-before-define - pinToPres={() => undefined} - childFilters={returnEmptyFilter} - childFiltersByRanges={returnEmptyFilter} - searchFilterDocs={returnEmptyDoclist} - fitContentsToBox={returnFalse} - fitWidth={returnFalse} - hideDecorations={true} - /> + const previewDoc = (doc: Doc, template: Template) => ( + <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} + /> + ); //<img className='docCreatorMenu-preview-image expanded' src={this._expandedPreview.icon!.url.href.replace(".png", "_o.png")} /> @@ -885,30 +914,30 @@ export class DocCreatorMenu extends ObservableReactComponent<FieldViewProps> { <ReactLoading type="spin" color={StrCast(Doc.UserDoc().userVariantColor)} height={30} width={30} /> </div> ) : ( - this._suggestedTemplates - .map(template => ( - <div - className="docCreatorMenu-preview-window" - 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> - {previewDoc(template.getRenderedDoc(), template)} - </div> - )) + this._suggestedTemplates.map(template => ( + <div + key={template.toString()} + className="docCreatorMenu-preview-window" + 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> + {previewDoc(template.getRenderedDoc(), template)} + </div> + )) )} </div> <div className="docCreatorMenu-GPT-options"> @@ -929,33 +958,33 @@ export class DocCreatorMenu extends ObservableReactComponent<FieldViewProps> { </button> </div> <div className="docCreatorMenu-templates-preview-window" style={{ justifyContent: this._menuDimensions.width > 400 ? 'center' : '' }}> - <div className="docCreatorMenu-preview-window empty" onPointerDown={e => this.testTemplate()}> + <div className="docCreatorMenu-preview-window empty"> <FontAwesomeIcon icon="plus" color="rgb(160, 160, 160)" /> </div> - {this._userTemplates - .map(template => ( - <div - className="docCreatorMenu-preview-window" - 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> - {previewDoc(template.getRenderedDoc(), template)} - </div> - ))} + {this._userTemplates.map(template => ( + <div + key={template.toString()} + className="docCreatorMenu-preview-window" + 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> + {previewDoc(template.getRenderedDoc(), template)} + </div> + ))} </div> </div> </div> @@ -964,7 +993,6 @@ export class DocCreatorMenu extends ObservableReactComponent<FieldViewProps> { ); } - @action updateXMargin = (input: string) => { this._layout.xMargin = Number(input); setTimeout(() => { @@ -985,11 +1013,11 @@ export class DocCreatorMenu extends ObservableReactComponent<FieldViewProps> { }; get layoutConfigOptions() { - const optionInput = (icon: string, func: Function, def?: number, key?: string, noMargin?: boolean) => { + 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 any} /> + <FontAwesomeIcon icon={icon as IconProp} /> </div> <input defaultValue={def} onInput={e => func(e.currentTarget.value)} className="docCreatorMenu-input config layout-config" /> </div> @@ -1010,8 +1038,6 @@ export class DocCreatorMenu extends ObservableReactComponent<FieldViewProps> { } } - screenToLocalTransform = () => this._props.ScreenToLocalTransform(); - applyLayout = (collection: Doc, docs: Doc[]) => { const { horizontalSpan, verticalSpan } = this.previewInfo; collection._height = verticalSpan; @@ -1020,12 +1046,12 @@ export class DocCreatorMenu extends ObservableReactComponent<FieldViewProps> { 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 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){ + if (columns === 0 || docs.length === 0) { return; } @@ -1049,16 +1075,16 @@ export class DocCreatorMenu extends ObservableReactComponent<FieldViewProps> { }; @computed - get previewInfo(){ + 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, - } + horizontalSpan: (docWidth + layout.xMargin) * this.columnsCount - layout.xMargin, + verticalSpan: (docHeight + layout.yMargin) * this.rowsCount - layout.yMargin, + }; } /** @@ -1066,12 +1092,9 @@ export class DocCreatorMenu extends ObservableReactComponent<FieldViewProps> { @type the type of collection the docs should render to (ie. freeform, carousel, card) */ updateRenderedDocCollection = () => { - console.log('called'); if (!this._fullyRenderedDocs) return; - //const largerSpan: number = horizontalSpan > verticalSpan ? horizontalSpan : verticalSpan; - - const collectionFactory = (): (docs: Doc[], options: DocumentOptions) => Doc => { + const collectionFactory = (): ((docs: Doc[], options: DocumentOptions) => Doc) => { switch (this._layout.type) { case LayoutType.CAROUSEL3D: return Docs.Create.Carousel3DDocument; @@ -1086,14 +1109,16 @@ export class DocCreatorMenu extends ObservableReactComponent<FieldViewProps> { default: return Docs.Create.FreeformDocument; } - } + }; const collection = collectionFactory()(this._fullyRenderedDocs, { isDefaultTemplateDoc: true, title: 'title', backgroundColor: 'gray', x: 200, - y: 200 + y: 200, + _width: 4000, + _height: 4000, }); this.applyLayout(collection, this._fullyRenderedDocs); @@ -1104,18 +1129,17 @@ export class DocCreatorMenu extends ObservableReactComponent<FieldViewProps> { mainCollection.addDocument(collection); console.log('changed to: ', collection); - } - - layoutPreviewContents = (id?: number) => { + }; + layoutPreviewContents = () => { return this._docsRendering ? ( - <div className="docCreatorMenu-layout-preview-window-wrapper loading" id={String(id) ?? undefined}> + <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> </div> - ) : !this._renderedDocCollection ? null : ( - <div className="docCreatorMenu-layout-preview-window-wrapper" id={String(id) ?? undefined}> + ) : !this._renderedDocCollection ? null : ( + <div className="docCreatorMenu-layout-preview-window-wrapper"> <DocumentView Document={this._renderedDocCollection} isContentActive={emptyFunction} @@ -1124,13 +1148,12 @@ export class DocCreatorMenu extends ObservableReactComponent<FieldViewProps> { removeDocument={returnFalse} PanelWidth={() => this._menuDimensions.width - 80} PanelHeight={() => this._menuDimensions.height - 105} - ScreenToLocalTransform={() => new Transform(-this._pageX - 5,-this._pageY - 35, 1)} + ScreenToLocalTransform={() => new Transform(-this._pageX - 5, -this._pageY - 35, 1)} renderDepth={5} whenChildContentsActiveChanged={emptyFunction} focus={emptyFunction} styleProvider={DefaultStyleProvider} addDocTab={this._props.addDocTab} - // eslint-disable-next-line no-use-before-define pinToPres={() => undefined} childFilters={returnEmptyFilter} childFiltersByRanges={returnEmptyFilter} @@ -1140,13 +1163,11 @@ export class DocCreatorMenu extends ObservableReactComponent<FieldViewProps> { hideDecorations={true} /> </div> - ) + ); }; get optionsMenuContents() { - const layoutEquals = (layout: DataVizTemplateLayout) => {}; //TODO: ADD LATER - - const layoutOption = (option: LayoutType, optStyle?: {}, specialFunc?: Function) => { + const layoutOption = (option: LayoutType, optStyle?: object, specialFunc?: () => void) => { return ( <div className="docCreatorMenu-dropdown-option" @@ -1169,7 +1190,7 @@ export class DocCreatorMenu extends ObservableReactComponent<FieldViewProps> { return ( <div className="docCreatorMenu-option-container"> <div className={`docCreatorMenu-option-title config ${specClass}`} style={{ width: width * 0.4, height: height }}> - <FontAwesomeIcon icon={icon as any} /> + <FontAwesomeIcon icon={icon as IconProp} /> </div> {manual ? ( <input className={`docCreatorMenu-input config ${specClass}`} style={{ width: width * 0.6, height: height }} /> @@ -1200,13 +1221,13 @@ export class DocCreatorMenu extends ObservableReactComponent<FieldViewProps> { </div> </div> {this._layout.type ? this.layoutConfigOptions : null} - {this.layoutPreviewContents(this._menuDimensions.width * 0.75)} + {this.layoutPreviewContents()} {selectionBox( 60, 20, 'repeat', undefined, - repeatOptions.map(num => <option onPointerDown={e => (this._layout.repeat = num)}>{`${num}x`}</option>) + 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"> @@ -1371,19 +1392,19 @@ export class DocCreatorMenu extends ObservableReactComponent<FieldViewProps> { const width: number = ref?.width ?? 0; return [ - <div className='docCreatorMenu-resizer top' onPointerDown={this.onResizePointerDown} style={{width: width, left: 0, top: -7}}/>, - <div className='docCreatorMenu-resizer left' onPointerDown={this.onResizePointerDown} style={{height: height, left: -7, top: 0}}/>, - <div className='docCreatorMenu-resizer right' onPointerDown={this.onResizePointerDown} style={{height: height, left: width - 3, top: 0}}/>, - <div className='docCreatorMenu-resizer bottom' onPointerDown={this.onResizePointerDown} style={{width: width, left: 0, top: height - 3}}/>, - <div className='docCreatorMenu-resizer topLeft' onPointerDown={this.onResizePointerDown} style={{left: -10, top: -10, cursor: 'nwse-resize'}}/>, - <div className='docCreatorMenu-resizer topRight' onPointerDown={this.onResizePointerDown} style={{left: width - 5, top: -10, cursor: 'nesw-resize'}}/>, - <div className='docCreatorMenu-resizer bottomLeft' onPointerDown={this.onResizePointerDown} style={{left: -10, top: height - 5, cursor: 'nesw-resize'}}/>, - <div className='docCreatorMenu-resizer bottomRight' onPointerDown={this.onResizePointerDown} style={{left: width - 5, top: height - 5, cursor: 'nwse-resize'}}/>, + <div className='docCreatorMenu-resizer top' key='0' onPointerDown={this.onResizePointerDown} style={{width: width, left: 0, top: -7}}/>, + <div className='docCreatorMenu-resizer left' key='1' onPointerDown={this.onResizePointerDown} style={{height: height, left: -7, top: 0}}/>, + <div className='docCreatorMenu-resizer right' key='2' onPointerDown={this.onResizePointerDown} style={{height: height, left: width - 3, top: 0}}/>, + <div className='docCreatorMenu-resizer bottom' key='3' onPointerDown={this.onResizePointerDown} style={{width: width, left: 0, top: height - 3}}/>, + <div className='docCreatorMenu-resizer topLeft' key='4' onPointerDown={this.onResizePointerDown} style={{left: -10, top: -10, cursor: 'nwse-resize'}}/>, + <div className='docCreatorMenu-resizer topRight' key='5' onPointerDown={this.onResizePointerDown} style={{left: width - 5, top: -10, cursor: 'nesw-resize'}}/>, + <div className='docCreatorMenu-resizer bottomLeft' key='6' onPointerDown={this.onResizePointerDown} style={{left: -10, top: height - 5, cursor: 'nesw-resize'}}/>, + <div className='docCreatorMenu-resizer bottomRight' key='7' onPointerDown={this.onResizePointerDown} style={{left: width - 5, top: height - 5, cursor: 'nwse-resize'}}/>, ]; //prettier-ignore } render() { - const topButton = (icon: string, opt: string, func: Function, tag: string) => { + const topButton = (icon: string, opt: string, func: () => void, tag: string) => { return ( <div className={`top-button-container ${tag} ${opt === this._menuContent ? 'selected' : ''}`}> <div @@ -1395,7 +1416,7 @@ export class DocCreatorMenu extends ObservableReactComponent<FieldViewProps> { }) ) }> - <FontAwesomeIcon icon={icon as any} /> + <FontAwesomeIcon icon={icon as IconProp} /> </div> </div> ); @@ -1434,11 +1455,11 @@ export class DocCreatorMenu extends ObservableReactComponent<FieldViewProps> { setupMoveUpEvents( this, e, - e => { + event => { this._dragging = true; this._startPos = { x: 0, y: 0 }; - this._startPos.x = e.pageX - (this._ref?.getBoundingClientRect().left ?? 0); - this._startPos.y = e.pageY - (this._ref?.getBoundingClientRect().top ?? 0); + this._startPos.x = event.pageX - (this._ref?.getBoundingClientRect().left ?? 0); + this._startPos.y = event.pageY - (this._ref?.getBoundingClientRect().top ?? 0); document.addEventListener('pointermove', this.onDrag); return true; }, @@ -1464,26 +1485,3 @@ export class DocCreatorMenu extends ObservableReactComponent<FieldViewProps> { ); } } - -export interface DataVizTemplateInfo { - doc: Doc; - layout: { type: LayoutType; xMargin: number; yMargin: number; repeat: number }; - columns: number; - referencePos: { x: number; y: number }; -} - -export interface DataVizTemplateLayout { - template: Doc; - docsNumList: number[]; - layout: { type: LayoutType; xMargin: number; yMargin: number; repeat: number }; - columns: number; - rows: number; -} - -export type Col = { - sizes: TemplateFieldSize[]; - desc: string; - title: string; - type: TemplateFieldType; - defaultContent?: string; -}; diff --git a/src/client/views/nodes/DataVizBox/DocCreatorMenu/FieldTypes/DecorationField.tsx b/src/client/views/nodes/DataVizBox/DocCreatorMenu/FieldTypes/DecorationField.tsx index efe398aaa..98a9dc7a6 100644 --- a/src/client/views/nodes/DataVizBox/DocCreatorMenu/FieldTypes/DecorationField.tsx +++ b/src/client/views/nodes/DataVizBox/DocCreatorMenu/FieldTypes/DecorationField.tsx @@ -1,38 +1,3 @@ -import { Doc, DocListCast } from "../../../../../../fields/Doc"; -import { Docs, DocumentOptions } from "../../../../../documents/Documents"; -import { FieldUtils } from "./FieldUtils"; -import { Field, FieldDimensions, FieldSettings, ViewType } from "./Field"; -import { RichTextField } from "../../../../../../fields/RichTextField"; -import { DocData } from "../../../../../../fields/DocSymbols"; -import { ImageField } from "../../../../../../fields/URLField"; +import { DynamicField } from './DynamicField'; -export class DecorationField extends Field { - - protected content: string = ''; - protected subfields: Field[]; - - protected renderedDocument: Doc; - - protected dimensions: FieldDimensions; - - constructor(settings: FieldSettings, parent: Field, id: number) { - super(settings, id, FieldUtils.initField, parent); - this.dimensions = FieldUtils.getLocalDimensions({tl: settings.tl, br: settings.br}, this.parent.getDimensions); - this.subfields = this.setupSubfields(this); - this.renderedDocument = this.initRenderedDoc(); - }; - - setContent = (content: string, type?: ViewType) => { return } - getContent = () => { return 'none'}; - get isContentField(): boolean { return false }; - - initRenderedDoc = (): Doc => { - const renderedSubfields: Doc[] = this.subfields.map(field => field.renderedDoc); - let doc: Doc = Docs.Create.FreeformDocument(renderedSubfields, { title: this.title, }); - FieldUtils.applyBasicOpts(doc, this.dimensions, this.settings); - - this.renderedDocument = doc; - - return doc; - }; -}
\ No newline at end of file +export class DecorationField extends DynamicField {} diff --git a/src/client/views/nodes/DataVizBox/DocCreatorMenu/FieldTypes/DynamicField.tsx b/src/client/views/nodes/DataVizBox/DocCreatorMenu/FieldTypes/DynamicField.tsx index 5ee047841..f100830d3 100644 --- a/src/client/views/nodes/DataVizBox/DocCreatorMenu/FieldTypes/DynamicField.tsx +++ b/src/client/views/nodes/DataVizBox/DocCreatorMenu/FieldTypes/DynamicField.tsx @@ -1,84 +1,189 @@ -import { reaction } from "mobx"; -import { Doc, DocListCast } from "../../../../../../fields/Doc"; -import { Docs } from "../../../../../documents/Documents"; -import { Col } from "../DocCreatorMenu"; -import { Field, FieldDimensions, FieldSettings, ViewType } from "./Field"; -import { FieldUtils } from "./FieldUtils"; +import { Doc, DocListCast } from '../../../../../../fields/Doc'; +import { Docs } from '../../../../../documents/Documents'; +import { Field, FieldSettings, ViewType } from './Field'; +import { List } from '../../../../../../fields/List'; +import { DocData } from '../../../../../../fields/DocSymbols'; +import { DocumentType } from '../../../../../documents/DocumentTypes'; +import { reaction } from 'mobx'; +import { IDisposer } from 'mobx-utils'; export class DynamicField extends Field { - protected parent: Field; - protected dimensions: FieldDimensions; - protected subfields: Field[]; - - protected renderedDocument: Doc; - - constructor(settings: FieldSettings, id: number, parent?: Field, protoDoc?: Doc) { - super(settings, id, FieldUtils.initField, parent); - if (!parent) { - this.parent = this; - this.dimensions = {width: this.settings.br[0] - this.settings.tl[0], height: this.settings.br[1] - this.settings.tl[1], coord: {x: this.settings.tl[0], y: this.settings.tl[1]}}; - } else { - this.parent = parent; - this.dimensions = FieldUtils.getLocalDimensions({tl: settings.tl, br: settings.br}, this.parent.getDimensions); - } - //console.log('new field dimensions: ', this.dimensions); - this.subfields = this.setupSubfields(this); - this.renderedDocument = this.initRenderedDoc(); //!!! - - // this.disposers.fieldList = reaction( - // () => DocListCast(this.renderedDoc[Doc.LayoutFieldKey(this.doc)]), - // docs => { - // this.handleFieldUpdate(docs); - // } - // ); + protected subfields!: Field[]; + + protected disposers: { [name: string]: IDisposer } = {}; + init(settings: FieldSettings, id: number, parent?: DynamicField) { + super.baseinit(undefined as unknown as Doc, settings, parent, id); + const { doc, subFields } = this.initializeDocument(settings); + this.Document = doc; + this.subfields = subFields; + + this.disposers.fieldList = reaction( + () => DocListCast(this.Document[Doc.LayoutFieldKey(this.Document)]), + docs => { + console.log('updated'); + this.handleFieldUpdate(docs); + } + ); + return this; } - setContent = (content: string, type: ViewType) => { return }; - getContent = () => { return '' }; - get isContentField(): boolean { return false }; + static setupSubfields(subFieldSettings: FieldSettings[] | undefined, parent?: DynamicField): Field[] { + return subFieldSettings?.map((fieldSettings, index) => Field.initField!(fieldSettings, index, parent)) || []; + } - matches = (cols: Col[]): Array<number> => { - return []; + get getSubfields(): Field[] { + return this.subfields ?? []; } + get getAllSubfields() { + let fields: Field[] = []; + this.subfields?.forEach(field => { + fields.push(field); + fields = fields.concat(field instanceof DynamicField ? field.getAllSubfields : ([] as Field[])); + }); + return fields; + } + + setSubFields = (fields: Field[]) => { + this.subfields = fields; + }; + handleFieldUpdate = (newDocsList: Doc[]) => { + const currRenderedDocs: Set<Doc> = new Set(); + this.subfields.forEach(field => currRenderedDocs.add(field.Document)); + newDocsList.forEach(doc => { + if (!currRenderedDocs.has(doc)) { + this.addFieldFromDoc(doc); + } + }); + currRenderedDocs.forEach(doc => { + if (!newDocsList.includes(doc)) { + const fields = this.subfields.filter(field => field.Document === doc); + fields.forEach(field => this.removeField(field)); + } + }); + }; + + addFieldFromDoc = (doc: Doc) => { + const par = this.Document; + const settings: FieldSettings = { + tl: [Number(doc._x) / Number(par._width), Number(doc._y) / Number(par._height)], + br: [(Number(doc._x) + Number(doc._width)) / Number(par._width), (Number(doc._y) + Number(doc._height)) / Number(par._height)], + viewType: doc.type === DocumentType.COL ? ViewType.FREEFORM : ViewType.STATIC, + opts: {}, + }; - getChildDimensions = (coords: { tl: [number, number]; br: [number, number] }): FieldDimensions => { - const l = (coords.tl[0] * this.dimensions.height) / 2; - const t = coords.tl[1] * this.dimensions.width / 2; //prettier-ignore - const r = (coords.br[0] * this.dimensions.height) / 2; - const b = coords.br[1] * this.dimensions.width / 2; //prettier-ignore - const width = r - l; - const height = b - t; - const coord = { x: l, y: t }; - return { width, height, coord }; + const newField: Field = Field.initField!(settings, this.subfields.length, this); + this.subfields.push(newField); }; - initRenderedDoc = (): Doc => { - console.log('dynamic field updated'); - let doc: Doc; - const renderedSubfields: Doc[] = this.subfields.map(field => field.renderedDoc); - switch (this.settings.viewType) { + addField = (field: Field) => { + if (!this.subfields.includes(field)) { + this.subfields.push(field); + console.log('field added'); + // Doc.SetContainer(field.Document, this.Document); + } + }; + + dispose = () => { + Object.values(this.disposers).forEach(disposer => disposer?.()); + }; + + removeField = (field: Field) => { + // var childDocs: Doc[] = DocListCast(this.Document[Doc.LayoutFieldKey(this.Document)]); + // this.Document[Doc.LayoutFieldKey(this.Document)] = new List<Doc>([...childDocs.splice(childDocs.indexOf(field.Document), 1)]); + this.subfields.splice(this.subfields.indexOf(field), 1); + field instanceof DynamicField && field.dispose(); + }; + + exchangeFields = (newField: Field, oldField: Field) => { + this.subfields.splice(this.subfields.indexOf(oldField), 1, newField); + // this.renderedDoc. + }; + // eslint-disable-next-line @typescript-eslint/no-unused-vars + setContent = (content: string, type: ViewType) => { + return; + }; + getContent = () => { + return ''; + }; + get isContentField(): boolean { + return false; + } + + addChildToDocument = (doc: Doc) => { + Doc.SetContainer(doc, this.Document); + }; + + matches = (): Array<number> => { + return []; + }; + + async makeClone(parent?: DynamicField): Promise<Field> { + const field = (await super.makeClone(parent)) as unknown as DynamicField; + field.subfields = await Promise.all(this.subfields.map(async cloneField => await cloneField.makeClone(field))); + field.Document[DocData].data = new List<Doc>(field.subfields.map(subfield => subfield.renderedDoc)); + return field; + } + initializeDocument = (settings: FieldSettings) => { + const subFields = DynamicField.setupSubfields(settings.subfields, this); + const renderedSubfields = subFields.map(field => field.renderedDoc); + settings.opts.title = settings.title; + switch (settings.viewType) { case ViewType.CAROUSEL3D: - doc = Docs.Create.Carousel3DDocument(renderedSubfields, { - title: this.title, - }); - FieldUtils.applyBasicOpts(doc, this.dimensions, this.settings); - break; + return { doc: Docs.Create.Carousel3DDocument(renderedSubfields, settings.opts), subFields }; case ViewType.FREEFORM: - doc = Docs.Create.FreeformDocument(renderedSubfields, { - title: this.title, - }); - FieldUtils.applyBasicOpts(doc, this.dimensions, this.settings); - break; + return { doc: Docs.Create.FreeformDocument(renderedSubfields, settings.opts), subFields }; default: - doc = Docs.Create.Carousel3DDocument(renderedSubfields, { - title: this.title, - }); - FieldUtils.applyBasicOpts(doc, this.dimensions, this.settings); - break; + return { doc: Docs.Create.FreeformDocument(renderedSubfields, settings.opts), subFields }; } + }; +} - this.renderedDocument = doc; - return doc; - } +// export class DynamicField extends Field { +// protected subfields: Field[]; -} +// protected Document!: Doc; + +// constructor(settings: FieldSettings, id: number, parent?: Field) { +// super(settings, id, parent); +// this.subfields = this.setupSubfields(this); +// this.initializeDocument(); +// } + +// setContent = (content: string, type: ViewType) => { return }; +// getContent = () => { return '' }; +// get isContentField(): boolean { return false }; + +// addChildToDocument = (doc: Doc) => { +// Doc.SetContainer(doc, this.Document); +// } + +// matches = (cols: Col[]): Array<number> => { +// return []; +// } + +// initializeDocument = (): Doc => { +// let doc: Doc; +// const renderedSubfields: Doc[] = this.subfields.map(field => field.renderedDoc); +// switch (this.settings.viewType) { +// case ViewType.CAROUSEL3D: +// doc = Docs.Create.Carousel3DDocument(renderedSubfields, { +// title: this.title, +// }); +// break; +// case ViewType.FREEFORM: +// doc = Docs.Create.FreeformDocument(renderedSubfields, { +// title: this.title, +// }); +// break; +// default: +// doc = Docs.Create.FreeformDocument(renderedSubfields, { +// title: this.title, +// }); +// break; +// } + +// this.Document = doc; +// return doc; +// } + +// } diff --git a/src/client/views/nodes/DataVizBox/DocCreatorMenu/FieldTypes/Field.tsx b/src/client/views/nodes/DataVizBox/DocCreatorMenu/FieldTypes/Field.tsx index 49931486c..111ad8328 100644 --- a/src/client/views/nodes/DataVizBox/DocCreatorMenu/FieldTypes/Field.tsx +++ b/src/client/views/nodes/DataVizBox/DocCreatorMenu/FieldTypes/Field.tsx @@ -1,188 +1,100 @@ -import { makeAutoObservable, makeObservable, reaction } from "mobx"; -import { Doc, DocListCast, FieldType } from "../../../../../../fields/Doc"; -import { Col } from "../DocCreatorMenu"; -import { TemplateFieldSize, TemplateFieldType } from "../TemplateBackend"; -import { IDisposer } from "mobx-utils"; -import { DocumentType } from "../../../../../documents/DocumentTypes"; -import { SearchUtil } from "../../../../../util/SearchUtil"; -import { OmitKeys } from "../../../../../../ClientUtils"; +/* eslint-disable @typescript-eslint/no-explicit-any */ +/* eslint-disable no-use-before-define */ +import { computed } from 'mobx'; +import { Doc } from '../../../../../../fields/Doc'; +import { DocData } from '../../../../../../fields/DocSymbols'; +import { Copy } from '../../../../../../fields/FieldSymbols'; +import { ObjectField } from '../../../../../../fields/ObjectField'; +import { ComputedField } from '../../../../../../fields/ScriptField'; +import { DocumentOptions } from '../../../../../documents/Documents'; +import { Col } from '../DocCreatorMenu'; +import { TemplateFieldSize, TemplateFieldType } from '../TemplateBackend'; export abstract class Field { + Document!: Doc; - protected disposers: { [name: string]: IDisposer } = {}; - private initField: (settings: FieldSettings, index: number, parent: Field, protoDoc?: Doc) => Field; + static initField?: (settings: FieldSettings, index: number, parent: any /* DynamicField */ | undefined, sameId?: boolean) => Field = undefined; - protected abstract subfields: Field[]; - - protected abstract renderedDocument: Doc; - - protected parent: Field; - protected id: number; - protected settings: FieldSettings; + protected parent?: any; + protected id!: number; + settings!: FieldSettings; protected title: string = ''; + dimensions!: FieldDimensions; - protected abstract dimensions: FieldDimensions; + constructor() {} - constructor(settings: FieldSettings, id: number, fieldFactory: (settings: FieldSettings, index: number, parent: Field, protoDoc?: Doc) => Field, parent?: Field) { - this.initField = fieldFactory; - this.parent = parent ? parent : this; + baseinit(doc: Doc, settings: FieldSettings, parent?: any, id: number = 1) { this.id = id; + this.Document = doc; + this.parent = parent; this.settings = settings; this.title = settings.title ?? ''; - - this.disposers.fieldList = reaction( - () => DocListCast(this.renderedDocument[Doc.LayoutFieldKey(this.renderedDocument)]), - docs => { - this.handleFieldUpdate(docs); - } - ); + this.dimensions = this.getLocalDimensions({ tl: this.settings.tl, br: this.settings.br }, this.parent?.getDimensions); + this.applyBasicOpts(this.dimensions, settings); + return this; } - get getSubfields(): Field[] { return this.subfields ?? []; }; - get getAllSubfields() { - let fields: Field[] = []; - this.subfields?.forEach(field => { - fields.push(field); - fields = fields.concat(field.getAllSubfields) - }); - return fields; - }; - - get renderedDoc(){ return this.renderedDocument }; - get renderedDocChildDocs() { return DocListCast(this.renderedDocument[Doc.LayoutFieldKey(this.renderedDocument)])} - get getDimensions() { return this.dimensions }; - get getID() { return this.id }; - get getDescription(): string { return this.settings.description ?? '' }; - get viewType(): ViewType { return this.settings.viewType } + get renderedDoc() { + return this.Document; + } + get getDimensions() { + return this.dimensions; + } + get getID() { + return this.id; + } + get getDescription(): string { + return this.settings.description ?? ''; + } + get viewType(): ViewType { + return this.settings.viewType; + } - setTitle = (title: string) => { + setTitle = (title: string) => { this.title = title; - this.renderedDocument.title = title; + this.Document.title = title; + }; + getTitle = () => { + return this.title; }; - getTitle = () => { return this.title }; abstract get isContentField(): boolean; abstract setContent(content: string, type: ViewType): void; abstract getContent(): string; - changeFieldType = (newType: ViewType): Field => { - this.settings.viewType = newType; - const newField: Field = this.initField(this.settings, this.id, this.parent, this.renderedDoc); - this.parent.exchangeFields(newField, this); - return newField; - } - - exchangeFields = (newField: Field, oldField: Field) => { - this.subfields.splice(this.subfields.indexOf(oldField), 1, newField); - // this.renderedDoc. - } - - setupFieldChangeReaction = () => { - + async makeClone(parent?: any /* DynamicField*/): Promise<Field> { + const field = Field.initField!(this.settings, this.id, parent, true); // create a value for this.Document/subfields that we want to ignore + field.Document = (await Doc.MakeClone(this.Document)).clone; + field.title = this.title; + field.dimensions = this.dimensions; + return field; } - addFieldFromDoc = (doc: Doc) => { - console.log('add field called'); - const par = this.renderedDocument; - const settings: FieldSettings = { - tl: [Number(doc._x) / Number(par._width), Number(doc._y) / Number(par._height)], - br: [(Number(doc._x) + Number(doc._width)) / Number(par._width), (Number(doc._y) + Number(doc._height)) / Number(par._height)], - viewType: doc.type === DocumentType.COL ? ViewType.FREEFORM : ViewType.STATIC, - opts: {}, - }; - - Object.getOwnPropertyNames(FieldOpts.prototype).forEach(([key, value]) => { - switch (key) { - case 'text_fontColor': - case 'backgroundColor': - case 'borderWidth': - case 'borderColor': - settings.opts[key] = String(doc[key]); - break; - - case '_layout_borderRounding': - case 'opacity': - case '_rotation': - settings.opts[key] = Number(doc[key]); - break; - - case 'contentBold': - settings.opts[key] = Boolean(doc[key]); - break; - - case 'hCentering': - settings.opts[key] = String(doc[key]) as 'h-left' | 'h-center' | 'h-right'; - break; - case 'contentYCentering': - settings.opts[key] = String(doc[key]) as 'top' | 'center' | 'bottom'; - break; - case 'textTransform': - settings.opts[key] = String(doc[key]) as 'uppercase' | 'lowercase'; - break; - case 'fieldViewType': - settings.opts[key] = String(doc[key]) as 'freeform' | 'stacked'; - break; - - default: - break; + @computed get documentOptions(): DocumentOptions { + const opts: DocumentOptions = {}; + Object.assign(opts, this.Document[DocData]); + Object.entries(opts).forEach(([key, field]) => { + if (field instanceof ObjectField) { + Object.assign(opts, { [key]: ObjectField.MakeCopy(field) }); + } else if (field instanceof ComputedField) { + Object.assign(opts, { [key]: field[Copy]() }); } }); - - const newField: Field = this.initField(settings, this.subfields.length, this); - this.subfields.push(newField); - }; - - addField = (type?: ViewType) => { - + return opts; } - removeField = (field: Field) => { - this.subfields.splice(this.subfields.indexOf(field), 1); - field.dispose(); + // eslint-disable-next-line @typescript-eslint/no-unused-vars + changeFieldType = (newType: ViewType): Field => { + const newField = Field.initField!(this.settings, this.id, this.parent, true); + this.parent?.exchangeFields(newField, this); + return newField; }; - setupSubfields = (parent: Field): Field[] => { - return this.settings.subfields?.map((fieldSettings, index) => { - return this.initField(fieldSettings, index, parent); - }) || []; - } - - applyAttributes(field: Field) { - field.setTitle(this.title); - field.initRenderedDoc(this.renderedDoc); - field.subfields = this.subfields; - } - - abstract initRenderedDoc(oldDoc?: Doc): void; - - dispose = () => { - Object.values(this.disposers).forEach(disposer => disposer?.()); - } - - handleFieldUpdate = (newDocsList: Doc[]) => { - const currRenderedDocs: Set<Doc> = new Set(); - this.subfields.forEach(field => currRenderedDocs.add(field.renderedDoc)); - newDocsList.forEach(doc => { - if (!currRenderedDocs.has(doc)) { - this.addFieldFromDoc(doc); - } - }); - currRenderedDocs.forEach(doc => { - if (!newDocsList.includes(doc)){ - const fields = this.subfields.filter(field => field.renderedDoc === doc); - fields.forEach(field => this.removeField(field)); - } - }); - } - matches = (cols: Col[]): number[] => { const colMatchesField = (col: Col) => { - const isMatch: boolean = ( - this.settings.sizes?.some(size => col.sizes?.includes(size)) - && this.settings.types?.includes(col.type)) - ?? false; + const isMatch: boolean = (this.settings.sizes?.some(size => col.sizes?.includes(size)) && this.settings.types?.includes(col.type)) ?? false; return isMatch; - } + }; const matches: Array<number> = []; @@ -195,12 +107,38 @@ export abstract class Field { return matches; }; + private getLocalDimensions = (coords: { tl: [number, number]; br: [number, number] }, parentDimensions?: FieldDimensions): FieldDimensions => { + if (!parentDimensions) { + return { width: coords.br[0] - coords.tl[0], height: coords.br[1] - coords.tl[1], coord: { x: coords.tl[0], y: coords.tl[1] } }; + } + const l = (coords.tl[0] * parentDimensions.width) / 2; + const t = coords.tl[1] * parentDimensions.height / 2; //prettier-ignore + const r = (coords.br[0] * parentDimensions.width) / 2; + const b = coords.br[1] * parentDimensions.height / 2; //prettier-ignore + const width = r - l; + const height = b - t; + const coord = { x: l, y: t }; + return { width, height, coord }; + }; + + private applyBasicOpts = (dimensions: FieldDimensions, settings: FieldSettings) => { + const opts: DocumentOptions = settings.opts; + opts.isDefaultTemplateDoc ??= true; + opts._layout_hideScroll ??= true; + opts.x ??= dimensions.coord.x; + opts.y ??= dimensions.coord.y; + opts._height ??= dimensions.height; + opts._width ??= dimensions.width; + opts._nativeWidth ??= dimensions.width; + opts._nativeHeight ??= dimensions.height; + opts._layout_nativeDimEditable ??= true; + }; } export type FieldSettings = { tl: [number, number]; br: [number, number]; - opts: FieldOpts; + opts: DocumentOptions; viewType: ViewType; title?: string; subfields?: FieldSettings[]; @@ -221,21 +159,9 @@ export enum ViewType { export type FieldDimensions = { width: number; height: number; - coord: {x: number, y: number}; -} + coord: { x: number; y: number }; +}; -export class FieldOpts { - backgroundColor?: string; - text_fontColor?: string; - _layout_borderRounding?: number; - borderWidth?: string; - borderColor?: string; - hCentering?: 'h-left' | 'h-center' | 'h-right'; - contentYCentering?: 'top' | 'center' | 'bottom'; - opacity?: number; - _rotation?: number; - //animation?: boolean; - contentBold?: boolean; - textTransform?: 'uppercase' | 'lowercase'; - fieldViewType?: 'freeform' | 'stacked'; -} +export type FieldTree = { + node: { field: Field; subfields: FieldTree[] }; +}; diff --git a/src/client/views/nodes/DataVizBox/DocCreatorMenu/FieldTypes/FieldUtils.tsx b/src/client/views/nodes/DataVizBox/DocCreatorMenu/FieldTypes/FieldUtils.tsx index b60d66d1a..0582c9ed3 100644 --- a/src/client/views/nodes/DataVizBox/DocCreatorMenu/FieldTypes/FieldUtils.tsx +++ b/src/client/views/nodes/DataVizBox/DocCreatorMenu/FieldTypes/FieldUtils.tsx @@ -1,68 +1,26 @@ -import { Doc } from "../../../../../../fields/Doc"; -import { ComputedField, ScriptField } from "../../../../../../fields/ScriptField"; -import { Col } from "../DocCreatorMenu"; -import { TemplateFieldSize, TemplateFieldType, TemplateLayouts } from "../TemplateBackend"; -import { DynamicField } from "./DynamicField"; -import { Field, FieldDimensions, FieldSettings, ViewType } from "./Field"; -import { TextTemplateField, ImageTemplateField } from "./StaticContentField"; -import { DecorationField } from "./DecorationField"; +import { Field, FieldSettings, ViewType } from './Field'; +import { TextTemplateField, ImageTemplateField } from './StaticContentField'; +import { DecorationField } from './DecorationField'; +import { DynamicField } from './DynamicField'; export class FieldUtils { - public static getLocalDimensions = (coords: { tl: [number, number]; br: [number, number] }, parentDimensions: FieldDimensions): FieldDimensions => { - //console.log('parent dimensions', parentDimensions, 'coords: ', coords); - const l = (coords.tl[0] * parentDimensions.width) / 2; - const t = coords.tl[1] * parentDimensions.height / 2; //prettier-ignore - const r = (coords.br[0] * parentDimensions.width) / 2; - const b = coords.br[1] * parentDimensions.height / 2; //prettier-ignore - const width = r - l; - const height = b - t; - const coord = { x: l, y: t }; - //console.log('width: ', width, 'height: ', height); - return { width, height, coord }; - }; - - public static initField = (settings: FieldSettings, index: number, parent: Field, protoDoc?: Doc): Field => { - const id = Number(`${parent.getID}${index}`); + public static initField = (settings: FieldSettings, index: number, parent: DynamicField | undefined, sameId: boolean = false): Field => { + const id = sameId ? index : parent ? Number(`${parent.getID}${index}`) : 1; switch (settings.viewType) { - case ViewType.FREEFORM: case ViewType.CAROUSEL3D: - return new DynamicField(settings, id, parent); + case ViewType.FREEFORM: + case ViewType.CAROUSEL3D: + return new DynamicField().init(settings, id, parent); case ViewType.IMG: - return new ImageTemplateField(settings, parent, id); + return new ImageTemplateField().init(settings, id, parent); case ViewType.TEXT: - return new TextTemplateField(settings, parent, id); + return new TextTemplateField().init(settings, id, parent); case ViewType.DEC: - return new DecorationField(settings, parent, id); + return new DecorationField().init(settings, id, parent); } - return new TextTemplateField(settings, parent, id); - } - - public static textProperties: string[] = [ - "textTransform", - "contentBold", - "text_fontColor", - "hCentering", - ] - - public static applyBasicOpts = (doc: Doc, parentDimensions: FieldDimensions, settings: FieldSettings, oldDoc?: Doc) => { - const opts = settings.opts; - doc.isDefaultTemplateDoc = oldDoc ? oldDoc.isDefaultTemplateDoc : true; - doc._layout_hideScroll = oldDoc ? oldDoc._layout_hideScroll : true; - doc.x = oldDoc ? oldDoc.x : parentDimensions.coord.x; - doc.y = oldDoc ? oldDoc.y : parentDimensions.coord.y; - doc._height = oldDoc ? oldDoc.height : parentDimensions.height; - doc._width = oldDoc ? oldDoc.width : parentDimensions.width; - doc.backgroundColor = oldDoc ? oldDoc.backgroundColor : opts.backgroundColor ?? ''; - //doc._layout_borderRounding = !opts._layout_borderRounding ? '0px' : ScriptField.MakeFunction(`${opts._layout_borderRounding} * this.width + 'px'`); - doc.borderColor = oldDoc ? oldDoc.borderColor : opts.borderColor; - doc.borderWidth = oldDoc ? oldDoc.borderWidth : opts.borderWidth; - doc.opacity = oldDoc ? oldDoc.opacity : opts.opacity; - doc._rotation = oldDoc ? oldDoc._rotation : opts._rotation; - doc.hCentering = oldDoc ? oldDoc.hCentering : opts.hCentering; - doc.nativeWidth = parentDimensions.width; - doc.nativeHeight = parentDimensions.height; - doc._layout_nativeDimEditable = true; + return new TextTemplateField().init(settings, id, parent); }; + // eslint-disable-next-line @typescript-eslint/no-unused-vars public static calculateFontSize = (contWidth: number, contHeight: number, text: string, uppercase: boolean): number => { const words: string[] = text.split(/\s+/).filter(Boolean); @@ -103,4 +61,4 @@ export class FieldUtils { return currFontSize - 1; }; -}
\ No newline at end of file +} diff --git a/src/client/views/nodes/DataVizBox/DocCreatorMenu/FieldTypes/StaticContentField.tsx b/src/client/views/nodes/DataVizBox/DocCreatorMenu/FieldTypes/StaticContentField.tsx index 8c50dc21d..75aea5440 100644 --- a/src/client/views/nodes/DataVizBox/DocCreatorMenu/FieldTypes/StaticContentField.tsx +++ b/src/client/views/nodes/DataVizBox/DocCreatorMenu/FieldTypes/StaticContentField.tsx @@ -1,45 +1,35 @@ -import { Doc, DocListCast } from "../../../../../../fields/Doc"; -import { Docs, DocumentOptions } from "../../../../../documents/Documents"; -import { FieldUtils } from "./FieldUtils"; -import { Field, FieldDimensions, FieldSettings, ViewType } from "./Field"; -import { RichTextField } from "../../../../../../fields/RichTextField"; -import { DocData } from "../../../../../../fields/DocSymbols"; -import { ImageField } from "../../../../../../fields/URLField"; -import { ViewComponentType } from "@fullcalendar/core"; +import { DocData } from '../../../../../../fields/DocSymbols'; +import { RichTextField } from '../../../../../../fields/RichTextField'; +import { ImageField } from '../../../../../../fields/URLField'; +import { Docs } from '../../../../../documents/Documents'; +import { Field, FieldSettings, ViewType } from './Field'; +import { FieldUtils } from './FieldUtils'; +import { DynamicField } from './DynamicField'; export abstract class StaticContentField extends Field { + protected content: string = ''; - protected content: string = ''; - protected subfields: Field[]; - - protected dimensions: FieldDimensions; - - constructor(settings: FieldSettings, parent: Field, id: number, protoDoc?: Doc) { - super(settings, id, FieldUtils.initField, parent); - this.dimensions = FieldUtils.getLocalDimensions({tl: settings.tl, br: settings.br}, this.parent.getDimensions); - this.subfields = this.setupSubfields(this); + abstract setContent(content: string, type?: ViewType): void; + getContent = () => { + return this.content ?? 'unset'; }; - - abstract setContent(content: string, type: ViewType): void; - getContent = () => { return this.content ?? 'unset'}; - get isContentField(): boolean { return true }; - - abstract initRenderedDoc(): Doc; + get isContentField(): boolean { + return true; + } } export class ImageTemplateField extends StaticContentField { - - protected renderedDocument: Doc; - - constructor(settings: FieldSettings, parent: Field, id: number, protoDoc?: Doc){ - super(settings, parent, id) - this.renderedDocument = this.initRenderedDoc(protoDoc); + init(settings: FieldSettings, id: number, parent: DynamicField | undefined) { + super.baseinit(this.initializeDocument(settings), settings, parent, id); + return this; } - setContent = (url: string, type: ViewType) => { - if (type === ViewType.IMG){ + setContent = (url: string, type?: ViewType) => { + this.settings.viewType = type ?? this.settings.viewType; + + if (type === ViewType.IMG || type === undefined) { const imgField = new ImageField(url); - this.renderedDocument[DocData]['data'] = imgField; + this.Document[DocData].data = imgField; this.content = url; } else { const updatedField = this.changeFieldType(type); @@ -47,39 +37,24 @@ export class ImageTemplateField extends StaticContentField { } }; - initRenderedDoc = (proto?: Doc): Doc => { - const url = String(this.content); - let doc: Doc = Docs.Create.ImageDocument(url, { - title: this.title, - _layout_fitWidth: false, - }, /*proto*/); - // if (!proto) { - FieldUtils.applyBasicOpts(doc, this.dimensions, this.settings); - // } else { - // doc.nativeWidth = this.dimensions.width; - // doc.nativeHeight = this.dimensions.height; - // doc._layout_nativeDimEditable = true; - // } + initializeDocument = (settings: FieldSettings) => { + settings.opts.title = settings.title; + settings.opts._layout_fitWidth = false; - console.log('data is: ', doc['data']); - - this.renderedDocument = doc; - - return doc; + return Docs.Create.ImageDocument('', settings.opts); }; } export class TextTemplateField extends StaticContentField { - - protected renderedDocument: Doc; - - constructor(settings: FieldSettings, parent: Field, id: number, protoDoc?: Doc){ - super(settings, parent, id) - this.renderedDocument = this.initRenderedDoc(); + init(settings: FieldSettings, id: number, parent?: DynamicField) { + super.baseinit(this.initializeDocument(settings), settings, parent, id); + this.Document.text_fontSize = `${FieldUtils.calculateFontSize(this.dimensions.width, this.dimensions.height, '', true)}`; + return this; } + setContent = (text: string, type?: ViewType) => { + this.settings.viewType = type ?? this.settings.viewType; - setContent = (text: string, type: ViewType) => { - if (type === ViewType.TEXT) { + if (type === ViewType.TEXT || type === undefined) { const rtf = { doc: { type: 'doc', @@ -100,28 +75,16 @@ export class TextTemplateField extends StaticContentField { }; this.content = text; const field = new RichTextField(JSON.stringify(rtf), text); - this.renderedDocument[DocData]['text'] = field; + this.Document[DocData]['text'] = field; } else { const updatedField = this.changeFieldType(type); updatedField.setContent(text, type); } }; - initRenderedDoc = (): Doc => { - const opts = this.settings.opts; - const text = String(this.content); - let doc: Doc = Docs.Create.TextDocument(text, { - title: this.title, - text_fontColor: opts.text_fontColor, - contentBold: opts.contentBold, - textTransform: opts.textTransform, - color: opts.text_fontColor, - _text_fontSize: `${FieldUtils.calculateFontSize(this.dimensions.width, this.dimensions.height, text, true)}` - }); - FieldUtils.applyBasicOpts(doc, this.dimensions, this.settings); - - this.renderedDocument = doc; - - return doc; + initializeDocument = (settings: FieldSettings) => { + const opts = settings.opts; + opts.title = settings.title; + return Docs.Create.TextDocument('', opts); }; -}
\ No newline at end of file +} diff --git a/src/client/views/nodes/DataVizBox/DocCreatorMenu/Template.tsx b/src/client/views/nodes/DataVizBox/DocCreatorMenu/Template.tsx index e8e9dca4f..3c610b36a 100644 --- a/src/client/views/nodes/DataVizBox/DocCreatorMenu/Template.tsx +++ b/src/client/views/nodes/DataVizBox/DocCreatorMenu/Template.tsx @@ -1,17 +1,10 @@ - -import { makeAutoObservable, reaction } from "mobx"; -import { Doc, DocListCast, FieldType } from "../../../../../fields/Doc"; -import { Docs } from "../../../../documents/Documents"; -import { Col } from "./DocCreatorMenu"; -import { DynamicField } from "./FieldTypes/DynamicField"; -import { Field, FieldSettings, ViewType } from "./FieldTypes/Field"; -import { } from "./FieldTypes/FieldUtils"; -import { observer } from "mobx-react"; -import { IDisposer } from "mobx-utils"; -import { Width } from "../../../../../fields/DocSymbols"; +import { makeAutoObservable } from 'mobx'; +import { Col } from './DocCreatorMenu'; +import { DynamicField } from './FieldTypes/DynamicField'; +import { Field, FieldSettings } from './FieldTypes/Field'; +import { TemplateLayouts } from './TemplateBackend'; export class Template { - mainField: DynamicField; private settings: FieldSettings; @@ -21,49 +14,56 @@ export class Template { this.mainField = this.setupMainField(templateInfo); } - get childFields(): Field[] { return this.mainField.getSubfields }; - get allFields(): Field[] { return this.mainField.getAllSubfields }; - get contentFields(): Field[] { return this.allFields.filter(field => field.isContentField) }; - get doc(){ return this.mainField.renderedDoc; }; - get title() { return this.mainField.getTitle() }; + get childFields(): Field[] { + return this.mainField.getSubfields; + } + get allFields(): Field[] { + return this.mainField.getAllSubfields; + } + get contentFields(): Field[] { + return this.allFields.filter(field => field.isContentField); + } + get doc() { + return this.mainField.renderedDoc; + } + get title() { + return this.mainField.getTitle(); + } cleanup = () => { //dispose each subfields disposers, etc. - } + }; - cloneBase = () => { - const clone: Template = new Template(this.settings); - this.mainField.applyAttributes(clone.mainField); - clone.allFields.filter(field => field !== clone.mainField).forEach(field => { - const matchingField: Field = this.allFields.filter(f => f.getID === field.getID)[0]; - matchingField.applyAttributes(field); - }) - clone.mainField.initRenderedDoc(); + cloneBase = async (): Promise<Template> => { + const clone: Template = new Template(TemplateLayouts.BasicSettings); + clone.mainField = (await this.mainField.makeClone(undefined)) as unknown as DynamicField; + clone.mainField.renderedDoc._width = this.mainField.renderedDoc._width; + clone.mainField.renderedDoc._height = this.mainField.renderedDoc._height; return clone; - } + }; printFieldInfo = () => { this.allFields.forEach(field => { const doc = field.renderedDoc; console.log('title: ', field.getTitle(), ' width: ', doc.width); }); - } + }; getRenderedDoc = () => { return this.doc; - } + }; getFieldByID = (id: number): Field => { return this.allFields.filter(field => field.getID === id)[0]; - } + }; getFieldByTitle = (title: string) => { return this.allFields.filter(field => field.getTitle() === title)[0]; - } + }; setupMainField = (templateInfo: FieldSettings) => { - return new DynamicField(templateInfo, 1); - } + return new DynamicField().init(templateInfo, 1); + }; get descriptionSummary(): string { let summary: string = ''; @@ -81,17 +81,11 @@ export class Template { return summary; } - resetToBase = () => { - this.allFields.forEach(field => { - field.initRenderedDoc(); - }) - } - isValidTemplate = (cols: Col[]) => { const matches: number[][] = this.getMatches(cols); const maxMatches: number = this.maxMatches(matches); return maxMatches === this.contentFields.length; - } + }; getMatches = (cols: Col[]): number[][] => { const numFields = this.contentFields.length; @@ -103,11 +97,11 @@ export class Template { .map(() => []); this.contentFields.forEach((field, i) => { - matches[i] = (field.matches(cols)); + matches[i] = field.matches(cols); }); return matches; - } + }; maxMatches = (matches: number[][]) => { if (matches.length === 0) return 0; @@ -142,5 +136,4 @@ export class Template { return count; }; - -}
\ No newline at end of file +} diff --git a/src/client/views/nodes/DataVizBox/DocCreatorMenu/TemplateBackend.tsx b/src/client/views/nodes/DataVizBox/DocCreatorMenu/TemplateBackend.tsx index 583b2f472..2b32d49aa 100644 --- a/src/client/views/nodes/DataVizBox/DocCreatorMenu/TemplateBackend.tsx +++ b/src/client/views/nodes/DataVizBox/DocCreatorMenu/TemplateBackend.tsx @@ -1,4 +1,4 @@ -import { FieldOpts, FieldSettings, ViewType } from "./FieldTypes/Field"; +import { FieldSettings, ViewType } from "./FieldTypes/Field"; export enum TemplateFieldType { TEXT = 'text', @@ -34,7 +34,7 @@ export class TemplateLayouts { viewType: ViewType.FREEFORM, opts: { backgroundColor: '#C0B887', - _layout_borderRounding: .05, + _layout_borderRounding: '.05', //borderColor: '#6B461F', //borderWidth: '12', }, @@ -61,9 +61,9 @@ export class TemplateLayouts { sizes: [TemplateFieldSize.MEDIUM, TemplateFieldSize.LARGE, TemplateFieldSize.HUGE], description: 'The main focus of the template; could be an image, long text, etc.', opts: { - _layout_borderRounding: .05, + _layout_borderRounding: '.05', borderColor: '#8F5B25', - borderWidth: '6', + borderWidth: 6, backgroundColor: '#CECAB9', }, }, @@ -88,9 +88,9 @@ export class TemplateLayouts { sizes: [TemplateFieldSize.MEDIUM, TemplateFieldSize.LARGE, TemplateFieldSize.HUGE], description: 'A medium-sized field for medium/long text.', opts: { - _layout_borderRounding: .05, + _layout_borderRounding: '.05', borderColor: '#8F5B25', - borderWidth: '6', + borderWidth: 6, backgroundColor: '#CECAB9', }, }, @@ -114,7 +114,7 @@ export class TemplateLayouts { sizes: [TemplateFieldSize.MEDIUM, TemplateFieldSize.LARGE], description: 'A medium to large-sized field suitable for an image or longer text that should be the main focus.', opts: { - borderWidth: '8', + borderWidth: 8, borderColor: '#F8E71C', backgroundColor: '#242425', text_fontColor: 'white', @@ -131,7 +131,7 @@ export class TemplateLayouts { backgroundColor: 'transparent', text_fontColor: 'white', hCentering: 'h-center', - textTransform: 'uppercase', + text_transform: 'uppercase', }, }, { @@ -145,7 +145,7 @@ export class TemplateLayouts { backgroundColor: 'transparent', text_fontColor: 'white', hCentering: 'h-center', - textTransform: 'uppercase', + text_transform: 'uppercase', }, }, { @@ -156,7 +156,7 @@ export class TemplateLayouts { sizes: [TemplateFieldSize.MEDIUM, TemplateFieldSize.LARGE, TemplateFieldSize.HUGE], description: 'A medium to large-sized field suitable for longer text that should contextualize field 1.', opts: { - borderWidth: '8', + borderWidth: 8, borderColor: '#F8E71C', text_fontColor: 'white', backgroundColor: '#242425', @@ -290,7 +290,7 @@ export class TemplateLayouts { description: 'A tiny field for just a word or two of plain text.', opts: { backgroundColor: '#E2B4F5', - borderWidth: '9', + borderWidth: 9, borderColor: '#9222F1', hCentering: 'h-center', }, @@ -304,7 +304,7 @@ export class TemplateLayouts { description: 'A tiny field for just a word or two of plain text.', opts: { backgroundColor: '#F5B4DD', - borderWidth: '9', + borderWidth: 9, borderColor: '#E260F3', hCentering: 'h-center', }, @@ -317,7 +317,7 @@ export class TemplateLayouts { sizes: [TemplateFieldSize.MEDIUM, TemplateFieldSize.LARGE, TemplateFieldSize.HUGE], description: 'A large to huge field for visual content that is the main content of the template.', opts: { - borderWidth: '16', + borderWidth: 16, borderColor: '#A2BD77', }, }, @@ -329,7 +329,7 @@ export class TemplateLayouts { sizes: [TemplateFieldSize.MEDIUM, TemplateFieldSize.LARGE], description: 'A medium to large field for text that describes the visual content above', opts: { - borderWidth: '9', + borderWidth: 9, borderColor: '#F0D601', backgroundColor: '#F3F57D', }, @@ -341,7 +341,7 @@ export class TemplateLayouts { opts: { backgroundColor: 'transparent', borderColor: '#007C0C', - borderWidth: '10', + borderWidth: 10, }, }, ], @@ -365,7 +365,7 @@ export class TemplateLayouts { description: 'A small text field for a title or word(s) that categorize the rest of the content.', opts: { borderColor: '#3B4A2C', - borderWidth: '8', + borderWidth: 8, hCentering: "h-center", backgroundColor: '#B8DC90', }, @@ -379,7 +379,7 @@ export class TemplateLayouts { description: 'A small text field for a title that categorizes the rest of the content.', opts: { borderColor: '#3B4A2C', - borderWidth: '8', + borderWidth: 8, hCentering: "h-center", backgroundColor: '#B8DC90', }, @@ -391,7 +391,7 @@ export class TemplateLayouts { opts: { backgroundColor: '#94B058', borderColor: '#3B4A2C', - borderWidth: '8', + borderWidth: 8, }, }, { @@ -403,7 +403,7 @@ export class TemplateLayouts { description: 'A medium to large field in the center of the template, for the main visual content.', opts: { borderColor: '#3B4A2C', - borderWidth: '8', + borderWidth: 8, backgroundColor: '#B8DC90', }, }, @@ -416,7 +416,7 @@ export class TemplateLayouts { description: 'A medium to large field at the bottom of the template, for the main text content.', opts: { borderColor: '#3B4A2C', - borderWidth: '8', + borderWidth: 8, hCentering: "h-center", backgroundColor: '#B8DC90', }, @@ -428,7 +428,7 @@ export class TemplateLayouts { opts: { backgroundColor: '#7A9D31', borderColor: '#3B4A2C', - borderWidth: '8', + borderWidth: 8, }, }, { @@ -438,7 +438,7 @@ export class TemplateLayouts { opts: { backgroundColor: '#94B058', borderColor: '#3B4A2C', - borderWidth: '8', + borderWidth: 8, }, }, { @@ -448,7 +448,7 @@ export class TemplateLayouts { opts: { backgroundColor: '#728745', borderColor: '#3B4A2C', - borderWidth: '8', + borderWidth: 8, }, }, { @@ -458,7 +458,7 @@ export class TemplateLayouts { opts: { backgroundColor: '#7A9D31', borderColor: '#3B4A2C', - borderWidth: '8', + borderWidth: 8, }, }, { @@ -468,7 +468,7 @@ export class TemplateLayouts { opts: { backgroundColor: '#728745', borderColor: '#3B4A2C', - borderWidth: '8', + borderWidth: 8, }, }, { @@ -478,7 +478,7 @@ export class TemplateLayouts { opts: { backgroundColor: '#94B058', borderColor: '#3B4A2C', - borderWidth: '8', + borderWidth: 8, }, }, ] @@ -503,7 +503,7 @@ export class TemplateLayouts { opts: { hCentering: "h-center", backgroundColor: 'transparent', - textTransform: 'uppercase', + text_transform: 'uppercase', }, }, { @@ -512,7 +512,7 @@ export class TemplateLayouts { br: [0.9, .25], opts: { borderColor: '#847F69', - borderWidth: '8', + borderWidth: 8, backgroundColor: '#C8BA94', }, subfields: [ @@ -586,7 +586,7 @@ export class TemplateLayouts { description: 'A medium to large field for visual content that is the central focus.', opts: { borderColor: 'yellow', - borderWidth: '8', + borderWidth: 8, backgroundColor: '#DDD3A9', _rotation: 45, }, @@ -724,7 +724,7 @@ export class TemplateLayouts { sizes: [TemplateFieldSize.MEDIUM, TemplateFieldSize.LARGE, TemplateFieldSize.HUGE], description: 'A medium to large visual field for the main content of the template', opts: { - borderWidth: '15', + borderWidth: 15, borderColor: '#E0E0DA', }, }, @@ -738,7 +738,7 @@ export class TemplateLayouts { opts: { backgroundColor: 'transparent', text_fontColor: '#AF0D0D', - textTransform: 'uppercase', + text_transform: 'uppercase', contentBold: true, hCentering: 'h-left', }, |
