diff options
| author | bobzel <zzzman@gmail.com> | 2025-06-01 16:07:30 -0400 |
|---|---|---|
| committer | bobzel <zzzman@gmail.com> | 2025-06-01 16:07:30 -0400 |
| commit | 215f874c5f7f28964a3428909fcee0f667388bae (patch) | |
| tree | e9881d33b845032725e75cdf880d4637507e9a32 /src/client/views/nodes/DataVizBox/DocCreatorMenu/Menu | |
| parent | 451891b0e34c84873ccc89dde55442bb9bf23f3d (diff) | |
a number of fixes to document creator to fix preview of templates and templates with images.
Diffstat (limited to 'src/client/views/nodes/DataVizBox/DocCreatorMenu/Menu')
4 files changed, 354 insertions, 385 deletions
diff --git a/src/client/views/nodes/DataVizBox/DocCreatorMenu/Menu/TemplateEditingWindow.tsx b/src/client/views/nodes/DataVizBox/DocCreatorMenu/Menu/TemplateEditingWindow.tsx index b50fff9e0..c35099e82 100644 --- a/src/client/views/nodes/DataVizBox/DocCreatorMenu/Menu/TemplateEditingWindow.tsx +++ b/src/client/views/nodes/DataVizBox/DocCreatorMenu/Menu/TemplateEditingWindow.tsx @@ -1,25 +1,25 @@ -import { action, makeObservable, observable, reaction, runInAction } from "mobx"; -import React from "react"; -import { returnFalse, returnEmptyFilter } from "../../../../../../ClientUtils"; -import { emptyFunction } from "../../../../../../Utils"; -import { Doc, returnEmptyDoclist } from "../../../../../../fields/Doc"; -import { DefaultStyleProvider } from "../../../../StyleProvider"; -import { DocumentView, DocumentViewInternal } from "../../../DocumentView"; -import { DocCreatorMenu } from "../DocCreatorMenu"; -import { TemplatePreviewGrid } from "./TemplatePreviewGrid"; -import { observer } from "mobx-react"; -import { Transform } from "../../../../../util/Transform"; -import { Template } from "../Template"; -import { ObservableReactComponent } from "../../../../ObservableReactComponent"; -import { IDisposer } from "mobx-utils"; -import { DocCreatorMenuButton } from "./DocCreatorMenuButton"; -import { IconProp } from "@fortawesome/fontawesome-svg-core"; +import { action, makeObservable, observable, reaction } from 'mobx'; +import React from 'react'; +import { returnFalse, returnEmptyFilter } from '../../../../../../ClientUtils'; +import { emptyFunction } from '../../../../../../Utils'; +import { returnEmptyDoclist } from '../../../../../../fields/Doc'; +import { DefaultStyleProvider } from '../../../../StyleProvider'; +import { DocumentView, DocumentViewInternal } from '../../../DocumentView'; +import { DocCreatorMenu } from '../DocCreatorMenu'; +import { TemplatePreviewGrid } from './TemplatePreviewGrid'; +import { observer } from 'mobx-react'; +import { Transform } from '../../../../../util/Transform'; +import { Template } from '../Template'; +import { ObservableReactComponent } from '../../../../ObservableReactComponent'; +import { IDisposer } from 'mobx-utils'; +import { DocCreatorMenuButton } from './DocCreatorMenuButton'; +import { IconProp } from '@fortawesome/fontawesome-svg-core'; export type FireflyStructureOptions = { numVariations: number; temperature: number; useStyleRef: boolean; -} +}; interface FireflyVariationsTabProps { menu: DocCreatorMenu; @@ -28,112 +28,93 @@ interface FireflyVariationsTabProps { @observer export class FireflyVariationsTab extends ObservableReactComponent<FireflyVariationsTabProps> { + private _prompt: string = 'Use this template to generate an empty baseball card template.'; + private _optionsButtonOpts: [IconProp, () => void] = ['gear', emptyFunction]; + private _previewBoxRightButtonOpts: [IconProp, () => void] = ['gear', () => this.forceUpdate()]; - private prompt: string = 'Use this template to generate an empty baseball card template.'; - - @observable private promptInput: HTMLTextAreaElement | null = null; - + @observable _fireflyOptions: FireflyStructureOptions = { numVariations: 3, temperature: 0, useStyleRef: false }; + @observable _promptInput: HTMLTextAreaElement | null = null; @observable _loading: boolean = false; @observable _variationsTabOpen: boolean = false; @observable _variationURLs: string[] = []; - @observable private fireflyOptions: FireflyStructureOptions = {numVariations: 3, temperature: 0, useStyleRef: false}; - constructor(props: FireflyVariationsTabProps) { super(props); makeObservable(this); } - generateVariations = async () => { + generateVariations = action(async () => { this._props.menu._variations = []; this._loading = true; const cloneTemplate = this._props.template.clone(false); cloneTemplate.setMatteBackground(); - const doc: Doc = cloneTemplate.getRenderedDoc()!; - this._variationURLs = await this._props.menu.generateVariations(doc, this.prompt, this.fireflyOptions); - this._variationURLs.forEach(() => { - const newTemplate: Template = this._props.template.clone(true); - this._props.menu._variations.push(newTemplate); - }); - setTimeout(() => { - this._variationURLs.forEach((url, i) => { - this._props.menu._variations[i].setImageAsBackground(url, true); - }); - this._loading = false; - }); - } - - setPromptInputRef: React.LegacyRef<HTMLTextAreaElement> = (node) => { - this.promptInput = node; - } - - // eslint-disable-next-line - private optionsButtonOpts: [IconProp, () => any] = ['gear', () => {}]; - // eslint-disable-next-line - private previewBoxRightButtonOpts: [IconProp, () => any] = ['gear', () => this.forceUpdate()]; + const doc = cloneTemplate.getRenderedDoc()!; + this._props.menu.generateVariations(doc, this._prompt, this._fireflyOptions).then( + action((urls: string[]) => { + (this._variationURLs = urls).forEach(url => { + const template = this._props.template.clone(true); + template.setImageAsBackground(url, true); + this._props.menu._variations.push(template); + }); + this._loading = false; + }) + ); + }); render() { return ( - <div className='docCreatorMenu-editing-firefly-section'> - <div className="docCreatorMenu-option-divider full no-margin-bottom"/> + <div className="docCreatorMenu-editing-firefly-section"> + <div className="docCreatorMenu-option-divider full no-margin-bottom" /> <TemplatePreviewGrid menu={this._props.menu} - title={'Generate Variations'} + title="Generate Variations" loading={this._loading} - styles={'scrolling'} + styles="scrolling" templates={this._props.menu._variations} - optionsButtonOpts={this.optionsButtonOpts} - previewBoxRightButtonOpts={this.previewBoxRightButtonOpts} + optionsButtonOpts={this._optionsButtonOpts} + previewBoxRightButtonOpts={this._previewBoxRightButtonOpts} /> <div className="docCreatorMenu-firefly-options"> <div className="docCreatorMenu-variation-prompt-row"> <textarea className="docCreatorMenu-variation-prompt-input-textbox" - ref={this.setPromptInputRef} - onChange={e => { this.prompt = e.target.value }} + ref={action((node: HTMLTextAreaElement | null) => (this._promptInput = node))} + onChange={e => (this._prompt = e.target.value)} onInput={() => { - if (this.promptInput !== null) { - this.promptInput.style.height = 'auto'; - this.promptInput.style.height = this.promptInput.scrollHeight + 'px'; + if (this._promptInput !== null) { + this._promptInput.style.height = 'auto'; + this._promptInput.style.height = this._promptInput.scrollHeight + 'px'; } }} - defaultValue={''} - placeholder={'Enter a custom prompt here (optional)'} + defaultValue="" + placeholder="Enter a custom prompt here (optional)" /> - <DocCreatorMenuButton icon={'arrows-rotate'} styles={'border'} function={this.generateVariations}/> + <DocCreatorMenuButton icon={'arrows-rotate'} styles={'border'} function={this.generateVariations} /> </div> - <nav className="options‑menu"> - <label className="menu‑item switch"> - <input type="checkbox" checked={this.fireflyOptions.useStyleRef} - onChange={(e) => runInAction(() => { this.fireflyOptions.useStyleRef = e.target.checked })} - /> - <span className="slider round"></span> - <span className="firefly-option-label">Use template as style guide</span> - </label> - <div className="menu‑item"> - <span className="firefly-option-label">Variations</span> - <input type="range" id="variations" - min="1" - max="5" - value={this.fireflyOptions.numVariations} - onChange={(e) => runInAction(() => { this.fireflyOptions.numVariations = Number(e.target.value) })} - /> - <span className="value" id="varVal">{this.fireflyOptions.numVariations}</span> - </div> - <div className="menu‑item"> - <span className="firefly-option-label">Temperature</span> - <input type="range" id="temperature" - min="1" - max="100" - value={this.fireflyOptions.temperature} - onChange={(e) => runInAction(() => { this.fireflyOptions.temperature = Number(e.target.value) })} - /> - <span className="value" id="tempVal">{this.fireflyOptions.temperature}</span> - </div> - </nav> + <nav className="options‑menu"> + <label className="menu‑item switch"> + <input type="checkbox" checked={this._fireflyOptions.useStyleRef} onChange={action(e => (this._fireflyOptions.useStyleRef = e.target.checked))} /> + <span className="slider round"></span> + <span className="firefly-option-label">Use template as style guide</span> + </label> + <div className="menu‑item"> + <span className="firefly-option-label">Variations</span> + <input type="range" id="variations" min="1" max="5" value={this._fireflyOptions.numVariations} onChange={action(e => (this._fireflyOptions.numVariations = Number(e.target.value)))} /> + <span className="value" id="varVal"> + {this._fireflyOptions.numVariations} + </span> + </div> + <div className="menu‑item"> + <span className="firefly-option-label">Temperature</span> + <input type="range" id="temperature" min="1" max="100" value={this._fireflyOptions.temperature} onChange={action(e => (this._fireflyOptions.temperature = Number(e.target.value)))} /> + <span className="value" id="tempVal"> + {this._fireflyOptions.temperature} + </span> + </div> + </nav> </div> </div> - ) + ); } } @@ -144,11 +125,9 @@ interface TemplateEditingWindowProps { @observer export class TemplateEditingWindow extends ObservableReactComponent<TemplateEditingWindowProps> { - private disposers: { [name: string]: IDisposer } = {}; - @observable private previewWindow: HTMLDivElement | null = null; - + @observable private _previewWindow: HTMLDivElement | null = null; @observable _variationsTabOpen: boolean = false; constructor(props: TemplateEditingWindowProps) { @@ -157,83 +136,85 @@ export class TemplateEditingWindow extends ObservableReactComponent<TemplateEdit } componentDidMount(): void { - this.disposers.windowDimensions = reaction(() => - this._props.menu._resizing, - () => { this.forceUpdate() }, + this.disposers.windowDimensions = reaction( + () => this._props.menu._resizing, + () => this.forceUpdate(), { fireImmediately: true } - ); + ); } componentWillUnmount() { Object.values(this.disposers).forEach(disposer => disposer?.()); } - setContainerRef: React.LegacyRef<HTMLDivElement> = (node) => { - this.previewWindow = node; - } - @action setVariationTab = (open: boolean) => { this._variationsTabOpen = open; - if (this.previewWindow && open) { - this.previewWindow.style.height = String(Number(this.previewWindow.clientHeight) * .6); - } else if (this.previewWindow && !open) { - this.previewWindow.style.height = String(Number(this.previewWindow.clientHeight) * 5/3); + if (this._previewWindow && open) { + this._previewWindow.style.height = String(Number(this._previewWindow.clientHeight) * 0.6); + } else if (this._previewWindow && !open) { + this._previewWindow.style.height = String((Number(this._previewWindow.clientHeight) * 5) / 3); } - } - - get renderedDocPreview(){ - const doc: Doc = this._props.template.getRenderedDoc() as unknown as Doc; + }; + previewPanelWidth = () => this._previewWindow?.clientWidth ?? 500; + previewPanelHeight = () => this._previewWindow?.clientHeight ?? 500; + previewScreenToLocalXf = () => new Transform(-this._props.menu._pageX - 5, -this._props.menu._pageY - 35, 1); + get renderedDocPreview() { + const doc = this._props.template.getRenderedDoc(); return ( - <div className="docCreatorMenu-expanded-template-preview" ref={this.setContainerRef}> - {this.previewWindow ? <DocumentView - Document={doc} - isContentActive={emptyFunction} - addDocument={returnFalse} - moveDocument={returnFalse} - removeDocument={returnFalse} - PanelWidth={() => this.previewWindow?.clientWidth ?? 500} - PanelHeight={() => this.previewWindow?.clientHeight ?? 500} - ScreenToLocalTransform={() => new Transform(-this._props.menu._pageX - 5, -this._props.menu._pageY - 35, 1)} - renderDepth={5} - whenChildContentsActiveChanged={emptyFunction} - focus={emptyFunction} - styleProvider={DefaultStyleProvider} - addDocTab={DocumentViewInternal.addDocTabFunc} - pinToPres={() => undefined} - childFilters={returnEmptyFilter} - childFiltersByRanges={returnEmptyFilter} - searchFilterDocs={returnEmptyDoclist} - fitContentsToBox={returnFalse} - fitWidth={returnFalse} - /> : null} + <div className="docCreatorMenu-expanded-template-preview" ref={action((node: HTMLDivElement | null) => (this._previewWindow = node))}> + {this._previewWindow && doc ? ( + <DocumentView + Document={doc} + isContentActive={emptyFunction} + addDocument={returnFalse} + moveDocument={returnFalse} + removeDocument={returnFalse} + PanelWidth={this.previewPanelWidth} + PanelHeight={this.previewPanelHeight} + ScreenToLocalTransform={this.previewScreenToLocalXf} + renderDepth={5} + whenChildContentsActiveChanged={emptyFunction} + focus={emptyFunction} + styleProvider={DefaultStyleProvider} + addDocTab={DocumentViewInternal.addDocTabFunc} + pinToPres={emptyFunction} + childFilters={returnEmptyFilter} + childFiltersByRanges={returnEmptyFilter} + searchFilterDocs={returnEmptyDoclist} + // fitContentsToBox={returnFalse} + // fitWidth={returnFalse} + /> + ) : null} </div> - ) + ); } + expandFunc = () => { + // if (this._props.template === this._props.menu._selectedTemplate) { + // this._props.menu.updateRenderedPreviewCollection(this._props.template); + // } + this._props.menu.setExpandedView(undefined); + }; + lastFunc = () => { + this._props.menu.editLastTemplate(); + this.forceUpdate(); + }; + variationFunc = () => this.setVariationTab(!this._variationsTabOpen); render() { return ( - <div className='docCreatorMenu-templates-view'> + <div className="docCreatorMenu-templates-view"> <div className="docCreatorMenu-expanded-template-preview"> - <div className="top-panel"/> + <div className="top-panel" /> {this.renderedDocPreview} - {this._variationsTabOpen ? <FireflyVariationsTab - menu={this._props.menu} - template={this._props.template} - /> - : null} + {this._variationsTabOpen ? <FireflyVariationsTab menu={this._props.menu} template={this._props.template} /> : null} <div className="right-buttons-panel"> - <DocCreatorMenuButton icon={'minimize'} function={() => { - // if (this._props.template === this._props.menu._selectedTemplate) { - // this._props.menu.updateRenderedPreviewCollection(this._props.template); - // } - this._props.menu.setExpandedView(undefined); - }}/> - <DocCreatorMenuButton icon={'lightbulb'} function={() => this.setVariationTab(!this._variationsTabOpen)}/> - <DocCreatorMenuButton icon={'arrow-rotate-backward'} function={() => { this._props.menu.editLastTemplate(); this.forceUpdate(); }}/> + <DocCreatorMenuButton icon="minimize" function={this.expandFunc} /> + <DocCreatorMenuButton icon="lightbulb" function={this.variationFunc} /> + <DocCreatorMenuButton icon="arrow-rotate-backward" function={this.lastFunc} /> </div> </div> </div> ); } -}
\ No newline at end of file +} diff --git a/src/client/views/nodes/DataVizBox/DocCreatorMenu/Menu/TemplateMenuFieldOptions.tsx b/src/client/views/nodes/DataVizBox/DocCreatorMenu/Menu/TemplateMenuFieldOptions.tsx index a4da54392..f0e20837c 100644 --- a/src/client/views/nodes/DataVizBox/DocCreatorMenu/Menu/TemplateMenuFieldOptions.tsx +++ b/src/client/views/nodes/DataVizBox/DocCreatorMenu/Menu/TemplateMenuFieldOptions.tsx @@ -1,11 +1,11 @@ -import { makeObservable, observable, runInAction } from "mobx"; -import { observer } from "mobx-react"; -import { ObservableReactComponent } from "../../../../ObservableReactComponent"; -import { Col, DocCreatorMenu } from "../DocCreatorMenu"; -import React from "react"; -import { Conditional, TemplateManager } from "../Backend/TemplateManager"; -import { TemplateFieldType, TemplateFieldSize } from "../TemplateBackend"; -import { DocCreatorMenuButton } from "./DocCreatorMenuButton"; +import { action, makeObservable, observable } from 'mobx'; +import { observer } from 'mobx-react'; +import { ObservableReactComponent } from '../../../../ObservableReactComponent'; +import { Col, DocCreatorMenu } from '../DocCreatorMenu'; +import React from 'react'; +import { Conditional, TemplateManager } from '../Backend/TemplateManager'; +import { TemplateFieldType, TemplateFieldSize } from '../TemplateBackend'; +import { DocCreatorMenuButton } from './DocCreatorMenuButton'; interface TemplateMenuFieldOptionsProps { menu: DocCreatorMenu; @@ -14,7 +14,6 @@ interface TemplateMenuFieldOptionsProps { @observer export class TemplateMenuFieldOptions extends ObservableReactComponent<TemplateMenuFieldOptionsProps> { - @observable _collapsedCols: string[] = []; //any columns whose options panels are hidden constructor(props: TemplateMenuFieldOptionsProps) { @@ -34,160 +33,153 @@ export class TemplateMenuFieldOptions extends ObservableReactComponent<TemplateM condition: '', target: 'Own', attribute: '', - value: '' + value: '', }); } return this._newCondCache[title]; }; conditionForm = (title: string, parameters?: Conditional, empty: boolean = false) => { - - const contentFieldTitles = this._props.menu.fieldsInfos.filter(field => field.type !== TemplateFieldType.DATA).map(field => field.title).concat('Template'); + const contentFieldTitles = this._props.menu.fieldsInfos + .filter(field => field.type !== TemplateFieldType.DATA) + .map(field => field.title) + .concat('Template'); const params: Conditional = this.getParams(title, parameters); return ( - <div className='form'> - <div className='form-row'> - <div className='form-row-plain-text'>If</div> - <div className='form-row-plain-text'>{title}</div> - <div className="operator-options-dropdown"> - <span className="operator-dropdown-current">{params.operator ?? '='}</span> - <div className='operator-dropdown-option' onPointerDown={() => {params.operator = '='}}>{'='}</div> - </div> - <input - className="form-row-textarea" - onChange={e => runInAction(() => { params.condition = e.target.value })} - placeholder='value' - value={params.condition} - /> - <div className='form-row-plain-text'>then</div> - <div className="operator-options-dropdown"> - <span className="operator-dropdown-current">{params.target ?? 'Own'}</span> - {contentFieldTitles.map((fieldTitle, i) => - <div className='operator-dropdown-option' key={i} onPointerDown={() => {params.target = fieldTitle}}>{fieldTitle === title ? 'Own' : fieldTitle}</div> - )} + <div className="form"> + <div className="form-row"> + <div className="form-row-plain-text">If</div> + <div className="form-row-plain-text">{title}</div> + <div className="operator-options-dropdown"> + <span className="operator-dropdown-current">{params.operator ?? '='}</span> + <div className="operator-dropdown-option" onPointerDown={() => (params.operator = '=')}> + {'='} + </div> + </div> + <input className="form-row-textarea" onChange={action(e => (params.condition = e.target.value))} placeholder="value" value={params.condition} /> + <div className="form-row-plain-text">then</div> + <div className="operator-options-dropdown"> + <span className="operator-dropdown-current">{params.target ?? 'Own'}</span> + {contentFieldTitles.map((fieldTitle, i) => ( + <div className="operator-dropdown-option" key={i} onPointerDown={() => (params.target = fieldTitle)}> + {fieldTitle === title ? 'Own' : fieldTitle} + </div> + ))} + </div> + <input className="form-row-textarea" onChange={action(e => (params.attribute = e.target.value))} placeholder="attribute" value={params.attribute} /> + <div className="form-row-plain-text">{'becomes'}</div> + <input className="form-row-textarea" onChange={action(e => (params.value = e.target.value))} placeholder="value" value={params.value} /> </div> - <input - className="form-row-textarea" - onChange={e => runInAction(() => { params.attribute = e.target.value })} - placeholder='attribute' - value={params.attribute} - /> - <div className='form-row-plain-text'>{'becomes'}</div> - <input - className="form-row-textarea" - onChange={e => runInAction(() => { params.value = e.target.value })} - placeholder='value' - value={params.value} - /> + {empty ? ( + <DocCreatorMenuButton + icon={'plus'} + styles={'float-right border'} + function={() => { + this._newCondCache[title] = observable<Conditional>({ + field: title, + operator: '=', + condition: '', + target: 'Own', + attribute: '', + value: '', + }); + this._props.templateManager.addFieldCondition(title, params); + }} + /> + ) : ( + <DocCreatorMenuButton icon={'minus'} styles={'float-right border'} function={() => this._props.templateManager.removeFieldCondition(title, params)} /> + )} </div> - {empty ? - <DocCreatorMenuButton icon={'plus'} styles={'float-right border'} function={() => { - this._newCondCache[title] = observable<Conditional>({ - field: title, - operator: '=', - condition: '', - target: 'Own', - attribute: '', - value: '' - }); - this._props.templateManager.addFieldCondition(title, params); - }}/> - : - <DocCreatorMenuButton icon={'minus'} styles={'float-right border'} function={() => this._props.templateManager.removeFieldCondition(title, params)}/> - } - </div> - ) - } + ); + }; fieldPanel = (field: Col, id: number) => ( <div className="field-panel" key={id}> - <div className="top-bar" onPointerDown={e => this._props.menu.setUpButtonClick(e, runInAction(() => () => { - if (this._collapsedCols.includes(field.title)) { - this._collapsedCols = this._collapsedCols.filter(col => col !== field.title); - } else { - this._collapsedCols.push(field.title); - } - }))}> + <div + className="top-bar" + onPointerDown={e => + this._props.menu.setUpButtonClick( + e, + action(() => { + if (this._collapsedCols.includes(field.title)) { + this._collapsedCols = this._collapsedCols.filter(col => col !== field.title); + } else { + this._collapsedCols.push(field.title); + } + }) + ) + }> <span className="field-title">{`${field.title} Field`}</span> - <DocCreatorMenuButton icon={'minus'} styles={'no-margin absolute-right'} function={() => this._props.menu.removeField(field)}/> + <DocCreatorMenuButton icon={'minus'} styles={'no-margin absolute-right'} function={() => this._props.menu.removeField(field)} /> </div> - { this._collapsedCols.includes(field.title) ? null : + {this._collapsedCols.includes(field.title) ? null : ( <> - <div className="opts-bar"> - <div className="opt-box"> - <div className="top-bar"> Title </div> - <textarea className="content" style={{ width: '100%', height: 'calc(100% - 20px)' }} value={field.title} placeholder={'Enter title'} onChange={e => this._props.menu.setColTitle(field, e.target.value)} /> - </div> - <div className="opt-box"> - <div className="top-bar"> Type </div> - <div className="content"> - <span className="type-display">{ - field.type === TemplateFieldType.TEXT ? 'Text Field' - : field.type === TemplateFieldType.VISUAL ? 'File Field' - : field.type === TemplateFieldType.DATA ? 'Data Field' - : '' - }</span> - <div className="bubbles"> - <input className="bubble" type="radio" name="type" onClick={() => this._props.menu.setColType(field, TemplateFieldType.TEXT)} /> - <div className="text">Text</div> - <input className="bubble" type="radio" name="type" onClick={() => this._props.menu.setColType(field, TemplateFieldType.VISUAL)} /> - <div className="text">File</div> - <input className="bubble" type="radio" name="type" onClick={() => this._props.menu.setColType(field, TemplateFieldType.DATA)} /> - <div className="text">Data</div> - </div> + <div className="opts-bar"> + <div className="opt-box"> + <div className="top-bar"> Title </div> + <textarea className="content" style={{ width: '100%', height: 'calc(100% - 20px)' }} value={field.title} placeholder={'Enter title'} onChange={e => this._props.menu.setColTitle(field, e.target.value)} /> </div> - </div> - </div> - { field.type === TemplateFieldType.DATA ? null : - (<> - <div className="sizes-box"> - <div className="top-bar"> Valid Sizes </div> + <div className="opt-box"> + <div className="top-bar"> Type </div> <div className="content"> + <span className="type-display">{field.type === TemplateFieldType.TEXT ? 'Text Field' : field.type === TemplateFieldType.VISUAL ? 'File Field' : field.type === TemplateFieldType.DATA ? 'Data Field' : ''}</span> <div className="bubbles"> - {Object.values(TemplateFieldSize).map(size => ( - <div key={field + size}> - <input className="bubble" type="checkbox" name="type" checked={field.sizes.includes(size)} onChange={e => this._props.menu.modifyColSizes(field, size, e.target.checked)} /> - <div className="text">{size}</div> - </div> - ))} + <input className="bubble" type="radio" name="type" onClick={() => this._props.menu.setColType(field, TemplateFieldType.TEXT)} /> + <div className="text">Text</div> + <input className="bubble" type="radio" name="type" onClick={() => this._props.menu.setColType(field, TemplateFieldType.VISUAL)} /> + <div className="text">File</div> + <input className="bubble" type="radio" name="type" onClick={() => this._props.menu.setColType(field, TemplateFieldType.DATA)} /> + <div className="text">Data</div> </div> </div> </div> - <div className="desc-box"> - <div className="top-bar"> Prompt </div> - <textarea - className="content" - onChange={e => this._props.menu.setColDesc(field, e.target.value)} - defaultValue={field.desc === this._props.menu._dataViz?.GPTSummary?.get(field.title)?.desc ? '' : field.desc} - placeholder={this._props.menu._dataViz?.GPTSummary?.get(field.title)?.desc ?? 'Add a description/prompt to help with template generation.'} - /> - </div> - </>) - } - <div className="conditionals-section"> - <span className="conditionals-title">Conditional Logic</span> - {this.conditionForm(field.title, undefined, true)} - {this._props.templateManager.conditionalFieldLogic[field.title]?.map(condition => this.conditionForm(condition.field, condition))} - </div> + </div> + {field.type === TemplateFieldType.DATA ? null : ( + <> + <div className="sizes-box"> + <div className="top-bar"> Valid Sizes </div> + <div className="content"> + <div className="bubbles"> + {Object.values(TemplateFieldSize).map(size => ( + <div key={field + size}> + <input className="bubble" type="checkbox" name="type" checked={field.sizes.includes(size)} onChange={e => this._props.menu.modifyColSizes(field, size, e.target.checked)} /> + <div className="text">{size}</div> + </div> + ))} + </div> + </div> + </div> + <div className="desc-box"> + <div className="top-bar"> Prompt </div> + <textarea + className="content" + onChange={e => this._props.menu.setColDesc(field, e.target.value)} + defaultValue={field.desc === this._props.menu._dataViz?.GPTSummary?.get(field.title)?.desc ? '' : field.desc} + placeholder={this._props.menu._dataViz?.GPTSummary?.get(field.title)?.desc ?? 'Add a description/prompt to help with template generation.'} + /> + </div> + </> + )} + <div className="conditionals-section"> + <span className="conditionals-title">Conditional Logic</span> + {this.conditionForm(field.title, undefined, true)} + {this._props.templateManager._conditionalFieldLogic[field.title]?.map(condition => this.conditionForm(condition.field, condition))} + </div> </> - } + )} </div> ); - - render() { return ( <div className="docCreatorMenu-dashboard-view"> <div className="topbar"> - <DocCreatorMenuButton icon={'plus'} function={this._props.menu.addField}/> - <DocCreatorMenuButton icon={'arrow-left'} styles={'float-right'} function={() => runInAction(() => (this._props.menu._menuContent = 'templates'))}/> + <DocCreatorMenuButton icon="plus" function={this._props.menu.addField} /> + <DocCreatorMenuButton icon="arrow-left" styles="float-right" function={action(() => (this._props.menu._menuContent = 'templates'))} /> </div> <div className="panels-container">{this._props.menu.fieldsInfos.map((field, i) => this.fieldPanel(field, i))}</div> </div> ); } - - -}
\ No newline at end of file +} diff --git a/src/client/views/nodes/DataVizBox/DocCreatorMenu/Menu/TemplatePreviewBox.tsx b/src/client/views/nodes/DataVizBox/DocCreatorMenu/Menu/TemplatePreviewBox.tsx index dc4c35789..7d02fff12 100644 --- a/src/client/views/nodes/DataVizBox/DocCreatorMenu/Menu/TemplatePreviewBox.tsx +++ b/src/client/views/nodes/DataVizBox/DocCreatorMenu/Menu/TemplatePreviewBox.tsx @@ -1,35 +1,30 @@ -import { Colors } from "@dash/components/src"; -import { FontAwesomeIcon} from "@fortawesome/react-fontawesome"; -import { Template } from "../Template"; -import { makeObservable, observable } from "mobx"; -import React from "react"; -import { ObservableReactComponent } from "../../../../ObservableReactComponent"; -import { DocCreatorMenu } from "../DocCreatorMenu"; -import { IconProp } from "@fortawesome/fontawesome-svg-core"; -import { DocumentView } from "../../../DocumentView"; -import { emptyFunction } from "../../../../../../Utils"; -import { returnEmptyFilter, returnFalse } from "../../../../../../ClientUtils"; -import { Transform } from "../../../../../util/Transform"; -import { DefaultStyleProvider } from "../../../../StyleProvider"; -import { Doc, returnEmptyDoclist } from "../../../../../../fields/Doc"; -import { observer } from "mobx-react"; +import { Colors } from '@dash/components/src'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { Template } from '../Template'; +import { action, makeObservable, observable } from 'mobx'; +import React from 'react'; +import { ObservableReactComponent } from '../../../../ObservableReactComponent'; +import { DocCreatorMenu } from '../DocCreatorMenu'; +import { IconProp } from '@fortawesome/fontawesome-svg-core'; +import { DocumentView } from '../../../DocumentView'; +import { emptyFunction } from '../../../../../../Utils'; +import { returnEmptyFilter, returnFalse } from '../../../../../../ClientUtils'; +import { Transform } from '../../../../../util/Transform'; +import { DefaultStyleProvider } from '../../../../StyleProvider'; +import { Doc, returnEmptyDoclist } from '../../../../../../fields/Doc'; +import { observer } from 'mobx-react'; export interface TemplatePreviewBoxProps { template: Template; menu: DocCreatorMenu; // eslint-disable-next-line - leftButtonOpts?: [icon: IconProp, func: (...args: any) => void] // eslint-disable-next-line - rightButtonOpts?: [icon: IconProp, func: (...args: any) => void] + leftButtonOpts?: [icon: IconProp, func: (...args: any) => void]; // eslint-disable-next-line + rightButtonOpts?: [icon: IconProp, func: (...args: any) => void]; } @observer export class TemplatePreviewBox extends ObservableReactComponent<TemplatePreviewBoxProps> { - @observable private previewWindow: HTMLDivElement | null = null; - setContainerRef: React.LegacyRef<HTMLDivElement> = (node) => { - this.previewWindow = node; - } - constructor(props: TemplatePreviewBoxProps) { super(props); makeObservable(this); @@ -39,6 +34,10 @@ export class TemplatePreviewBox extends ObservableReactComponent<TemplatePreview return this._props.template.getRenderedDoc() as Doc; } + docPanelWidth = () => this.previewWindow?.clientWidth ?? this._props.menu._menuDimensions.height * 0.3; + docPanelHeight = () => this.previewWindow?.clientHeight ?? this._props.menu._menuDimensions.height * 0.3; + docScreenToLocalXf = () => new Transform(-this._props.menu._pageX - 5, -this._props.menu._pageY - 35, 1); + render() { const template = this._props.template; @@ -46,49 +45,45 @@ export class TemplatePreviewBox extends ObservableReactComponent<TemplatePreview <div key={template.title} className="docCreatorMenu-preview-window" - ref={this.setContainerRef} + ref={action((node: HTMLDivElement | null) => (this.previewWindow = node))} style={{ border: this._props.menu._selectedTemplate === template ? `solid 3px ${Colors.MEDIUM_BLUE}` : '', boxShadow: this._props.menu._selectedTemplate === template ? `0 0 15px rgba(68, 118, 247, .8)` : '', }} - onPointerDown={e => this._props.menu.setUpButtonClick(e, () => this._props.menu.updateSelectedTemplate(template))} - > - { this._props.leftButtonOpts ? - <button - className="option-button left" - onPointerDown={e => - this._props.menu.setUpButtonClick(e, () => this._props.leftButtonOpts) - }> + onPointerDown={e => this._props.menu.setUpButtonClick(e, () => this._props.menu.updateSelectedTemplate(template))}> + {this._props.leftButtonOpts ? ( + <button className="option-button left" onPointerDown={e => this._props.menu.setUpButtonClick(e, () => this._props.leftButtonOpts)}> <FontAwesomeIcon icon={this._props.leftButtonOpts![0]} color="white" /> - </button> : null - } - { this._props.rightButtonOpts ? + </button> + ) : null} + {this._props.rightButtonOpts ? ( <button className="option-button right" onPointerDown={e => this._props.menu.setUpButtonClick(e, () => this._props.rightButtonOpts)}> <FontAwesomeIcon icon={this._props.rightButtonOpts![0]} color="white" /> - </button> : null } - <DocumentView - Document={this.doc} - isContentActive={emptyFunction} // !!! should be return false - addDocument={returnFalse} - moveDocument={returnFalse} - removeDocument={returnFalse} - PanelWidth={() => this.previewWindow?.clientWidth ?? this._props.menu._menuDimensions.height * .3} - PanelHeight={() => this.previewWindow?.clientHeight ?? this._props.menu._menuDimensions.height * .3} - ScreenToLocalTransform={() => new Transform(-this._props.menu._pageX - 5, -this._props.menu._pageY - 35, 1)} - renderDepth={1} - 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} - /> + </button> + ) : null} + <DocumentView + Document={this.doc} + isContentActive={emptyFunction} // !!! should be return false + addDocument={returnFalse} + moveDocument={returnFalse} + removeDocument={returnFalse} + PanelWidth={this.docPanelWidth} + PanelHeight={this.docPanelHeight} + ScreenToLocalTransform={this.docScreenToLocalXf} + renderDepth={1} + whenChildContentsActiveChanged={emptyFunction} + focus={emptyFunction} + styleProvider={DefaultStyleProvider} + addDocTab={this._props.menu._props.addDocTab} + pinToPres={emptyFunction} + childFilters={returnEmptyFilter} + childFiltersByRanges={returnEmptyFilter} + searchFilterDocs={returnEmptyDoclist} + // fitContentsToBox={returnFalse} + // fitWidth={returnFalse} + hideDecorations={true} + /> </div> - ) + ); } -}
\ No newline at end of file +} diff --git a/src/client/views/nodes/DataVizBox/DocCreatorMenu/Menu/TemplateRenderPreviewWindow.tsx b/src/client/views/nodes/DataVizBox/DocCreatorMenu/Menu/TemplateRenderPreviewWindow.tsx index f281f770e..9222d7349 100644 --- a/src/client/views/nodes/DataVizBox/DocCreatorMenu/Menu/TemplateRenderPreviewWindow.tsx +++ b/src/client/views/nodes/DataVizBox/DocCreatorMenu/Menu/TemplateRenderPreviewWindow.tsx @@ -1,20 +1,20 @@ -import { action, computed, makeObservable, observable, runInAction } from "mobx"; -import { observer } from "mobx-react"; -import { ObservableReactComponent } from "../../../../ObservableReactComponent"; -import { 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 { action, computed, makeObservable, observable, runInAction } from 'mobx'; +import { observer } from 'mobx-react'; +import { ObservableReactComponent } from '../../../../ObservableReactComponent'; +import { 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 { NumCast, StrCast } from '../../../../../../fields/Types'; +import { DefaultStyleProvider } from '../../../../StyleProvider'; +import { DocumentView } from '../../../DocumentView'; +import { Transform } from '../../../../../util/Transform'; +import { Docs, DocumentOptions } from '../../../../../documents/Documents'; interface TemplatesRenderPreviewWindowProps { menu: DocCreatorMenu; @@ -22,8 +22,7 @@ interface TemplatesRenderPreviewWindowProps { @observer export class TemplatesRenderPreviewWindow extends ObservableReactComponent<TemplatesRenderPreviewWindowProps> { - - @observable private _layout: { type: LayoutType; yMargin: number; xMargin: number; columns?: number; repeat: number } = { type: LayoutType.FREEFORM, yMargin: 10, xMargin: 10, columns: 0, repeat: 0 }; + @observable private _layout: { type: LayoutType; yMargin: number; xMargin: number; columns?: number; repeat: number } = { type: LayoutType.FREEFORM, yMargin: 10, xMargin: 10, columns: 1, repeat: 0 }; @observable private renderedDocs: Doc[] = []; @observable private renderedDocCollection: Doc | undefined = undefined; @@ -62,7 +61,7 @@ export class TemplatesRenderPreviewWindow extends ObservableReactComponent<Templ @computed get columnsCount() { switch (this._layout.type) { case LayoutType.FREEFORM: - return this._layout.columns ?? 0; + return this._layout.columns ?? 1; case LayoutType.CAROUSEL3D: return 3; default: @@ -130,28 +129,27 @@ export class TemplatesRenderPreviewWindow extends ObservableReactComponent<Templ 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; + collection.layout_fitWidth = true; + collection.freeform_fitContentsToBox = true; + + const columns = (this._layout.columns ?? this.columnsCount) || 1; + const xGap = this._layout.xMargin; + const yGap = this._layout.yMargin; + const startX = -collection._width / 2; + const startY = -collection._height / 2; + const docHeight = NumCast(docs[0]?._height); + const docWidth = NumCast(docs[0]?._width); + + let i = 0; + let docsChanged = 0; + let curX = startX; + let curY = startY; while (docsChanged < docs.length) { while (i < columns && docsChanged < docs.length) { docs[docsChanged].x = curX; docs[docsChanged].y = curY; + docs[docsChanged].layout_fitWidth = false; curX += docWidth + xGap; ++docsChanged; ++i; @@ -164,8 +162,8 @@ export class TemplatesRenderPreviewWindow extends ObservableReactComponent<Templ @computed get previewInfo() { - const docHeight: number = Number(this.renderedDocs[0]._height); - const docWidth: number = Number(this.renderedDocs[0]._width); + const docHeight = NumCast(this.renderedDocs[0]?._height); + const docWidth = NumCast(this.renderedDocs[0]?._width); const layout = this._layout; return { docHeight: docHeight, @@ -201,6 +199,10 @@ export class TemplatesRenderPreviewWindow extends ObservableReactComponent<Templ } } + layoutPanelWidth = () => this._props.menu._menuDimensions.width - 80; + layoutPanelHeight = () => this._props.menu._menuDimensions.height - 105; + layoutScreenToLocalXf = () => new Transform(-this._props.menu._pageX - 5, -this._props.menu._pageY - 35, 1); + layoutPreviewContents = action(() => { return this.loading ? ( <div className="docCreatorMenu-layout-preview-window-wrapper loading"> @@ -216,20 +218,18 @@ export class TemplatesRenderPreviewWindow extends ObservableReactComponent<Templ addDocument={returnFalse} moveDocument={returnFalse} removeDocument={returnFalse} - PanelWidth={() => this._props.menu._menuDimensions.width - 80} - PanelHeight={() => this._props.menu._menuDimensions.height - 105} - ScreenToLocalTransform={() => new Transform(-this._props.menu._pageX - 5, -this._props.menu._pageY - 35, 1)} + PanelWidth={this.layoutPanelWidth} + PanelHeight={this.layoutPanelHeight} + ScreenToLocalTransform={this.layoutScreenToLocalXf} renderDepth={5} whenChildContentsActiveChanged={emptyFunction} focus={emptyFunction} styleProvider={DefaultStyleProvider} addDocTab={this._props.menu._props.addDocTab} - pinToPres={() => undefined} + pinToPres={emptyFunction} childFilters={returnEmptyFilter} childFiltersByRanges={returnEmptyFilter} searchFilterDocs={returnEmptyDoclist} - fitContentsToBox={returnFalse} - fitWidth={returnFalse} hideDecorations={true} /> </div> @@ -273,7 +273,6 @@ export class TemplatesRenderPreviewWindow extends ObservableReactComponent<Templ }; get optionsMenuContents() { - const repeatOptions = [0, 1, 2, 3, 4, 5]; return ( @@ -341,5 +340,7 @@ export class TemplatesRenderPreviewWindow extends ObservableReactComponent<Templ ); } - render() { return this.optionsMenuContents } -}
\ No newline at end of file + render() { + return this.optionsMenuContents; + } +} |
