From f92a02ec5d676359cb268a35d30e5bf9886199c1 Mon Sep 17 00:00:00 2001 From: Nathan-SR <144961007+Nathan-SR@users.noreply.github.com> Date: Fri, 30 May 2025 03:27:03 -0400 Subject: couple more component refactors --- .../Menu/TemplateRenderPreviewWindow.tsx | 346 +++++++++++++++++++++ 1 file changed, 346 insertions(+) create mode 100644 src/client/views/nodes/DataVizBox/DocCreatorMenu/Menu/TemplateRenderPreviewWindow.tsx (limited to 'src/client/views/nodes/DataVizBox/DocCreatorMenu/Menu/TemplateRenderPreviewWindow.tsx') diff --git a/src/client/views/nodes/DataVizBox/DocCreatorMenu/Menu/TemplateRenderPreviewWindow.tsx b/src/client/views/nodes/DataVizBox/DocCreatorMenu/Menu/TemplateRenderPreviewWindow.tsx new file mode 100644 index 000000000..219152549 --- /dev/null +++ b/src/client/views/nodes/DataVizBox/DocCreatorMenu/Menu/TemplateRenderPreviewWindow.tsx @@ -0,0 +1,346 @@ +import { action, computed, makeObservable, observable, runInAction } from "mobx"; +import { observer } from "mobx-react"; +import { ObservableReactComponent } from "../../../../ObservableReactComponent"; +import { DataVizTemplateLayout, DocCreatorMenu, LayoutType } from "../DocCreatorMenu"; +import React from "react"; +import { IconProp } from "@fortawesome/fontawesome-svg-core"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { setupMoveUpEvents, returnFalse, returnEmptyFilter } from "../../../../../../ClientUtils"; +import { emptyFunction } from "../../../../../../Utils"; +import { undoable } from "../../../../../util/UndoManager"; +import ReactLoading from "react-loading"; +import { Doc, NumListCast, returnEmptyDoclist } from "../../../../../../fields/Doc"; +import { StrCast } from "../../../../../../fields/Types"; +import { DefaultStyleProvider } from "../../../../StyleProvider"; +import { DocumentView } from "../../../DocumentView"; +import { Transform } from "../../../../../util/Transform"; +import { Docs, DocumentOptions } from "../../../../../documents/Documents"; +import { Template } from "../Template"; + +interface TemplatesRenderPreviewWindowProps { + menu: DocCreatorMenu; +} + +@observer +export class TemplatesRenderPreviewWindow extends ObservableReactComponent { + + @observable private _layout: { type: LayoutType; yMargin: number; xMargin: number; columns?: number; repeat: number } = { type: LayoutType.FREEFORM, yMargin: 10, xMargin: 10, columns: 0, repeat: 0 }; + + @observable private renderedDocs: Doc[] = []; + @observable private renderedDocCollection: Doc | undefined = undefined; + + @observable private loading: boolean = false; + + constructor(props: TemplatesRenderPreviewWindowProps) { + super(props); + makeObservable(this); + this.updateRenderedPreviewCollection(); + } + + @computed get canMakeDocs() { + return this._props.menu._selectedTemplate !== undefined && this._layout !== undefined; + } + + @computed get docsToRender() { + if (this._props.menu.DEBUG_MODE) { + return [1, 2, 3, 4]; + } else { + return NumListCast(this._props.menu._dataViz?.layoutDoc.dataViz_selectedRows); + } + } + + @computed get rowsCount() { + switch (this._layout.type) { + case LayoutType.FREEFORM: + return Math.ceil(this.docsToRender.length / (this._layout.columns ?? 1)) ?? 0; + case LayoutType.CAROUSEL3D: + return 1.8; + default: + return 1; + } + } + + @computed get columnsCount() { + switch (this._layout.type) { + case LayoutType.FREEFORM: + return this._layout.columns ?? 0; + case LayoutType.CAROUSEL3D: + return 3; + default: + return 1; + } + } + + @action updateRenderedPreviewCollection = async () => { + this.loading = true; + this.renderedDocs = await this._props.menu.createDocsForPreview(); + this.updateRenderedDocCollection(); + }; + + /** + * Updates the preview that shows how all docs will be rendered in the chosen collection type. + @type the type of collection the docs should render to (ie. freeform, carousel, card) + */ + updateRenderedDocCollection = () => { + if (!this.renderedDocs) return; + + const collectionFactory = (): ((docs: Doc[], options: DocumentOptions) => Doc) => { + switch (this._layout.type) { + case LayoutType.CAROUSEL3D: return Docs.Create.Carousel3DDocument; + case LayoutType.FREEFORM: return Docs.Create.FreeformDocument; + case LayoutType.CARD: return Docs.Create.CardDeckDocument; + case LayoutType.MASONRY: return Docs.Create.MasonryDocument; + case LayoutType.CAROUSEL: return Docs.Create.CarouselDocument; + default: return Docs.Create.FreeformDocument; + } // prettier-ignore + }; + + const collection = collectionFactory()(this.renderedDocs, { + isDefaultTemplateDoc: true, + title: 'title', + backgroundColor: 'gray', + x: 200, + y: 200, + _width: 4000, + _height: 4000, + }); + + this.applyLayout(collection, this.renderedDocs); + + this.renderedDocCollection = collection; + + this.loading = false; + + this.forceUpdate(); + }; + + @action updateMargin = (input: string, xOrY: 'x' | 'y') => { + this._layout[`${xOrY}Margin`] = Number(input); + setTimeout(() => { + if (!this.renderedDocCollection || !this.renderedDocs) return; + this.applyLayout(this.renderedDocCollection, this.renderedDocs); + }); + }; + + @action updateColumns = (input: string) => { + this._layout.columns = Number(input); + this.updateRenderedDocCollection(); + }; + + applyLayout = (collection: Doc, docs: Doc[]) => { + const { horizontalSpan, verticalSpan } = this.previewInfo; + collection._height = verticalSpan; + collection._width = horizontalSpan; + + const columns: number = this._layout.columns ?? this.columnsCount; + const xGap: number = this._layout.xMargin; + const yGap: number = this._layout.yMargin; + const startX: number = -Number(collection._width) / 2; + const startY: number = -Number(collection._height) / 2; + const docHeight: number = Number(docs[0]._height); + const docWidth: number = Number(docs[0]._width); + + if (columns === 0 || docs.length === 0) { + return; + } + + let i: number = 0; + let docsChanged: number = 0; + let curX: number = startX; + let curY: number = startY; + + while (docsChanged < docs.length) { + while (i < columns && docsChanged < docs.length) { + docs[docsChanged].x = curX; + docs[docsChanged].y = curY; + curX += docWidth + xGap; + ++docsChanged; + ++i; + } + i = 0; + curX = startX; + curY += docHeight + yGap; + } + }; + + @computed + get previewInfo() { + const docHeight: number = Number(this.renderedDocs[0]._height); + const docWidth: number = Number(this.renderedDocs[0]._width); + const layout = this._layout; + return { + docHeight: docHeight, + docWidth: docWidth, + horizontalSpan: (docWidth + layout.xMargin) * this.columnsCount - layout.xMargin, + verticalSpan: (docHeight + layout.yMargin) * this.rowsCount - layout.yMargin, + }; + } + + get layoutConfigOptions() { + const optionInput = (icon: string, func: (input: string) => void, def?: number, key?: string, noMargin?: boolean) => { + return ( +
+
+ +
+ func(e.currentTarget.value)} className="docCreatorMenu-input config layout-config" /> +
+ ); + }; + + switch (this._layout.type) { + case LayoutType.FREEFORM: + return ( +
+ {optionInput('arrows-up-down', (input: string) => this.updateMargin(input, 'y'), this._layout.xMargin, '2')} + {optionInput('arrows-left-right', (input: string) => this.updateMargin(input, 'x'), this._layout.xMargin, '3')} + {optionInput('table-columns', this.updateColumns, this._layout.columns, '4', true)} +
+ ); + default: + break; + } + } + + layoutPreviewContents = action(() => { + return this.loading ? ( +
+
+ +
+
+ ) : !this.renderedDocCollection ? null : ( +
+ this._props.menu._menuDimensions.width - 80} + PanelHeight={() => this._props.menu._menuDimensions.height - 105} + ScreenToLocalTransform={() => new Transform(-this._props.menu._pageX - 5, -this._props.menu._pageY - 35, 1)} + renderDepth={5} + whenChildContentsActiveChanged={emptyFunction} + focus={emptyFunction} + styleProvider={DefaultStyleProvider} + addDocTab={this._props.menu._props.addDocTab} + pinToPres={() => undefined} + childFilters={returnEmptyFilter} + childFiltersByRanges={returnEmptyFilter} + searchFilterDocs={returnEmptyDoclist} + fitContentsToBox={returnFalse} + fitWidth={returnFalse} + hideDecorations={true} + /> +
+ ); + }); + + selectionBox = (width: number, height: number, icon: string, specClass?: string, options?: JSX.Element[], manual?: boolean): JSX.Element => { + return ( +
+
+ +
+ {manual ? ( + + ) : ( + + )} +
+ ); + }; + + layoutOption = (option: LayoutType, optStyle?: object, specialFunc?: () => void) => { + return ( +
+ this._props.menu.setUpButtonClick(e, () => { + specialFunc?.(); + runInAction(() => { + this._layout.type = option; + this.updateRenderedDocCollection(); + }); + }) + }> + {option} +
+ ); + }; + + get optionsMenuContents() { + + const repeatOptions = [0, 1, 2, 3, 4, 5]; + + return ( +
+
+
+
{this._layout.type ? this._layout.type.toUpperCase() : 'Choose Layout'}
+
+ {this.layoutOption(LayoutType.FREEFORM, undefined, () => { + if (!this._layout.columns) this._layout.columns = Math.ceil(Math.sqrt(this.docsToRender.length)); + })} + {this.layoutOption(LayoutType.CAROUSEL)} + {this.layoutOption(LayoutType.CAROUSEL3D)} + {this.layoutOption(LayoutType.MASONRY)} +
+
+
+ {this._layout.type ? this.layoutConfigOptions : null} + {this.layoutPreviewContents()} + {this.selectionBox( + 60, + 20, + 'repeat', + undefined, + repeatOptions.map(num => ) + )} +
+
+ + +
+
+ ); + } + + render() { return this.optionsMenuContents } +} \ No newline at end of file -- cgit v1.2.3-70-g09d2 From 312a07364ac8880e74e1b0a911d3e5b380886858 Mon Sep 17 00:00:00 2001 From: Nathan-SR <144961007+Nathan-SR@users.noreply.github.com> Date: Fri, 30 May 2025 04:07:22 -0400 Subject: linting --- .../DocCreatorMenu/Backend/TemplateManager.ts | 2 +- .../DocCreatorMenu/Backend/TemplateMenuAIUtils.ts | 6 +---- .../DataVizBox/DocCreatorMenu/DocCreatorMenu.tsx | 31 +++++++--------------- .../DocCreatorMenu/Menu/ConditionalsTextarea.tsx | 2 +- .../DocCreatorMenu/Menu/DocCreatorMenuButton.tsx | 3 ++- .../DocCreatorMenu/Menu/TemplateEditingWindow.tsx | 21 +++++++-------- .../Menu/TemplateMenuFieldOptions.tsx | 14 +++++----- .../DocCreatorMenu/Menu/TemplatePreviewBox.tsx | 9 +++---- .../DocCreatorMenu/Menu/TemplatePreviewGrid.tsx | 15 +++++------ .../Menu/TemplateRenderPreviewWindow.tsx | 3 +-- .../nodes/DataVizBox/DocCreatorMenu/Template.ts | 9 ------- .../DocCreatorMenu/TemplateFieldTypes/DataField.ts | 3 +-- .../TemplateFieldTypes/DynamicField.ts | 9 ++----- .../TemplateFieldTypes/StaticContentField.ts | 1 - .../TemplateFieldTypes/TemplateField.ts | 3 +-- 15 files changed, 45 insertions(+), 86 deletions(-) (limited to 'src/client/views/nodes/DataVizBox/DocCreatorMenu/Menu/TemplateRenderPreviewWindow.tsx') diff --git a/src/client/views/nodes/DataVizBox/DocCreatorMenu/Backend/TemplateManager.ts b/src/client/views/nodes/DataVizBox/DocCreatorMenu/Backend/TemplateManager.ts index 6fcca7e30..78235d000 100644 --- a/src/client/views/nodes/DataVizBox/DocCreatorMenu/Backend/TemplateManager.ts +++ b/src/client/views/nodes/DataVizBox/DocCreatorMenu/Backend/TemplateManager.ts @@ -2,7 +2,7 @@ import { action, makeAutoObservable } from 'mobx'; import { Col } from '../DocCreatorMenu'; import { FieldSettings, TemplateField } from '../TemplateFieldTypes/TemplateField'; import { Template } from '../Template'; -import { Doc, NumListCast } from '../../../../../../fields/Doc'; +import { NumListCast } from '../../../../../../fields/Doc'; import { DataVizBox } from '../../DataVizBox'; import { TemplateFieldType } from '../TemplateBackend'; import { TemplateMenuAIUtils } from './TemplateMenuAIUtils'; diff --git a/src/client/views/nodes/DataVizBox/DocCreatorMenu/Backend/TemplateMenuAIUtils.ts b/src/client/views/nodes/DataVizBox/DocCreatorMenu/Backend/TemplateMenuAIUtils.ts index 9bc2bfce2..162b7a1b1 100644 --- a/src/client/views/nodes/DataVizBox/DocCreatorMenu/Backend/TemplateMenuAIUtils.ts +++ b/src/client/views/nodes/DataVizBox/DocCreatorMenu/Backend/TemplateMenuAIUtils.ts @@ -1,5 +1,3 @@ -import { action } from "mobx"; -import { Upload } from "openai/resources"; import { ClientUtils } from "../../../../../../ClientUtils"; import { Networking } from "../../../../../Network"; import { gptImageCall, gptAPICall, GPTCallType } from "../../../../../apis/gpt/GPT"; @@ -7,9 +5,6 @@ import { Col } from "../DocCreatorMenu"; import { TemplateFieldSize, TemplateFieldType } from "../TemplateBackend"; import { TemplateField, ViewType } from "../TemplateFieldTypes/TemplateField"; import { Template } from "../Template"; -import { Doc } from "../../../../../../fields/Doc"; -import { DrawingFillHandler } from "../../../../smartdraw/DrawingFillHandler"; -import { CollectionFreeFormView } from "../../../../collections/collectionFreeForm"; export class TemplateMenuAIUtils { @@ -30,6 +25,7 @@ export class TemplateMenuAIUtils { public static renderGPTImageCall = async (template: Template, col: Col, fieldNumber: number): Promise => { const generateAndLoadImage = async (id: number, prompt: string) => { const url = await this.generateGPTImage(prompt); + // eslint-disable-next-line var field: TemplateField = template.getFieldByID(id); field.setContent(url ?? '', ViewType.IMG); diff --git a/src/client/views/nodes/DataVizBox/DocCreatorMenu/DocCreatorMenu.tsx b/src/client/views/nodes/DataVizBox/DocCreatorMenu/DocCreatorMenu.tsx index 9a84e69a9..ebfa3fc65 100644 --- a/src/client/views/nodes/DataVizBox/DocCreatorMenu/DocCreatorMenu.tsx +++ b/src/client/views/nodes/DataVizBox/DocCreatorMenu/DocCreatorMenu.tsx @@ -1,44 +1,31 @@ -import { Colors } from '@dash/components'; + import { IconProp } from '@fortawesome/fontawesome-svg-core'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; 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 { Upload } from '../../../../../server/SharedMediaTypes'; -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 { Transform } from '../../../../util/Transform'; import { UndoManager, undoable } from '../../../../util/UndoManager'; import { ObservableReactComponent } from '../../../ObservableReactComponent'; -import { DefaultStyleProvider } from '../../../StyleProvider'; 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 { TemplateField, ViewType } from './TemplateFieldTypes/TemplateField'; +import { ViewType } from './TemplateFieldTypes/TemplateField'; import { Template } from './Template'; import { TemplateFieldSize, TemplateFieldType, TemplateLayouts } from './TemplateBackend'; -import { Conditional, TemplateManager } from './Backend/TemplateManager'; -import { DrawingFillHandler } from '../../../smartdraw/DrawingFillHandler'; -import { CgPathIntersect } from 'react-icons/cg'; -import { StaticContentField } from './TemplateFieldTypes/StaticContentField'; +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 { ConditionalsTextArea } from './Menu/ConditionalsTextarea'; import { TemplatesRenderPreviewWindow } from './Menu/TemplateRenderPreviewWindow'; import { TemplateMenuFieldOptions } from './Menu/TemplateMenuFieldOptions'; @@ -500,7 +487,7 @@ export class DocCreatorMenu extends ObservableReactComponent }; generateVariations = async (onDoc: Doc, prompt: string, options: FireflyStructureOptions): Promise => { - const { numVariations, temperature, useStyleRef } = options; + // const { numVariations, temperature, useStyleRef } = options; this.variations = []; const mainCollection = this._dataViz?.DocumentView?.().containerViewPath?.().lastElement()?.ComponentView as CollectionFreeFormView; @@ -509,7 +496,7 @@ export class DocCreatorMenu extends ObservableReactComponent clone.x = 10000; clone.y = 10000; - await DrawingFillHandler.drawingToImage(clone, 100 - temperature, prompt, useStyleRef ? clone : undefined, this, numVariations) + // await DrawingFillHandler.drawingToImage(clone, 100 - temperature, prompt, useStyleRef ? clone : undefined, this, numVariations) return this.variations; } diff --git a/src/client/views/nodes/DataVizBox/DocCreatorMenu/Menu/ConditionalsTextarea.tsx b/src/client/views/nodes/DataVizBox/DocCreatorMenu/Menu/ConditionalsTextarea.tsx index 2ca0bde3f..89c2e44ff 100644 --- a/src/client/views/nodes/DataVizBox/DocCreatorMenu/Menu/ConditionalsTextarea.tsx +++ b/src/client/views/nodes/DataVizBox/DocCreatorMenu/Menu/ConditionalsTextarea.tsx @@ -51,7 +51,7 @@ export class ConditionalsTextArea extends ObservableReactComponent { runInAction(() => { - this.props.conditional[this.props.property] = e.target.value as any; + this.props.conditional[this.props.property] = e.target.value as "=" | ">" | "<" | "contains"; }); this.updateInputWidth(); }} diff --git a/src/client/views/nodes/DataVizBox/DocCreatorMenu/Menu/DocCreatorMenuButton.tsx b/src/client/views/nodes/DataVizBox/DocCreatorMenu/Menu/DocCreatorMenuButton.tsx index 1d8139d40..48d2de4de 100644 --- a/src/client/views/nodes/DataVizBox/DocCreatorMenu/Menu/DocCreatorMenuButton.tsx +++ b/src/client/views/nodes/DataVizBox/DocCreatorMenu/Menu/DocCreatorMenuButton.tsx @@ -9,13 +9,14 @@ import { observer } from "mobx-react"; interface DocCreatorMenuButtonProps { icon: IconProp; + // eslint-disable-next-line function: () => any; styles?: string; } @observer export class DocCreatorMenuButton extends ObservableReactComponent { - + // eslint-disable-next-line setupButtonClick = (e: React.PointerEvent, func: (...args: any) => void) => { setupMoveUpEvents( this, diff --git a/src/client/views/nodes/DataVizBox/DocCreatorMenu/Menu/TemplateEditingWindow.tsx b/src/client/views/nodes/DataVizBox/DocCreatorMenu/Menu/TemplateEditingWindow.tsx index 3eaed79b6..b50fff9e0 100644 --- a/src/client/views/nodes/DataVizBox/DocCreatorMenu/Menu/TemplateEditingWindow.tsx +++ b/src/client/views/nodes/DataVizBox/DocCreatorMenu/Menu/TemplateEditingWindow.tsx @@ -1,7 +1,6 @@ -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { action, makeAutoObservable, makeObservable, observable, reaction, runInAction } from "mobx"; +import { action, makeObservable, observable, reaction, runInAction } from "mobx"; import React from "react"; -import { returnFalse, returnEmptyFilter, returnTrue } from "../../../../../../ClientUtils"; +import { returnFalse, returnEmptyFilter } from "../../../../../../ClientUtils"; import { emptyFunction } from "../../../../../../Utils"; import { Doc, returnEmptyDoclist } from "../../../../../../fields/Doc"; import { DefaultStyleProvider } from "../../../../StyleProvider"; @@ -11,14 +10,10 @@ import { TemplatePreviewGrid } from "./TemplatePreviewGrid"; import { observer } from "mobx-react"; import { Transform } from "../../../../../util/Transform"; import { Template } from "../Template"; -import { TemplateMenuAIUtils } from "../Backend/TemplateMenuAIUtils"; import { ObservableReactComponent } from "../../../../ObservableReactComponent"; import { IDisposer } from "mobx-utils"; -import { ImageField } from "../../../../../../fields/URLField"; import { DocCreatorMenuButton } from "./DocCreatorMenuButton"; -import { TbHistory } from "react-icons/tb"; import { IconProp } from "@fortawesome/fontawesome-svg-core"; -import { docStyle } from "pdfjs-dist/types/web/ui_utils"; export type FireflyStructureOptions = { numVariations: number; @@ -56,7 +51,7 @@ export class FireflyVariationsTab extends ObservableReactComponent { + this._variationURLs.forEach(() => { const newTemplate: Template = this._props.template.clone(true); this._props.menu._variations.push(newTemplate); }); @@ -72,7 +67,9 @@ export class FireflyVariationsTab extends ObservableReactComponent any] = ['gear', () => {}]; + // eslint-disable-next-line private previewBoxRightButtonOpts: [IconProp, () => any] = ['gear', () => this.forceUpdate()]; render() { @@ -93,7 +90,7 @@ export class FireflyVariationsTab extends ObservableReactComponent this.prompt = e.target.value} + onChange={e => { this.prompt = e.target.value }} onInput={() => { if (this.promptInput !== null) { this.promptInput.style.height = 'auto'; @@ -108,7 +105,7 @@ export class FireflyVariationsTab extends ObservableReactComponent