diff options
author | sharkiecodes <lanyi_stroud@brown.edu> | 2025-06-10 11:12:23 -0400 |
---|---|---|
committer | sharkiecodes <lanyi_stroud@brown.edu> | 2025-06-10 11:12:23 -0400 |
commit | 272534c8a7517d08c70928c96d487d84b14772f6 (patch) | |
tree | 0e3e9a8cc83b8ba96b2bd49c9fc726b16411d60c /src/client/views/nodes/DataVizBox/DocCreatorMenu/Menu/TemplateEditingWindow.tsx | |
parent | c5981504058e0696827b4048fcd908a58fb0b0d3 (diff) | |
parent | b91057d00512446339e48fb8488a97a1e5ef03d5 (diff) |
Merge branch 'master' into lanyi-branch
Diffstat (limited to 'src/client/views/nodes/DataVizBox/DocCreatorMenu/Menu/TemplateEditingWindow.tsx')
-rw-r--r-- | src/client/views/nodes/DataVizBox/DocCreatorMenu/Menu/TemplateEditingWindow.tsx | 220 |
1 files changed, 220 insertions, 0 deletions
diff --git a/src/client/views/nodes/DataVizBox/DocCreatorMenu/Menu/TemplateEditingWindow.tsx b/src/client/views/nodes/DataVizBox/DocCreatorMenu/Menu/TemplateEditingWindow.tsx new file mode 100644 index 000000000..c35099e82 --- /dev/null +++ b/src/client/views/nodes/DataVizBox/DocCreatorMenu/Menu/TemplateEditingWindow.tsx @@ -0,0 +1,220 @@ +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; + template: Template; +} + +@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()]; + + @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[] = []; + + constructor(props: FireflyVariationsTabProps) { + super(props); + makeObservable(this); + } + + generateVariations = action(async () => { + this._props.menu._variations = []; + this._loading = true; + const cloneTemplate = this._props.template.clone(false); + cloneTemplate.setMatteBackground(); + 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" /> + <TemplatePreviewGrid + menu={this._props.menu} + title="Generate Variations" + loading={this._loading} + styles="scrolling" + templates={this._props.menu._variations} + 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={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'; + } + }} + defaultValue="" + placeholder="Enter a custom prompt here (optional)" + /> + <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={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> + ); + } +} + +interface TemplateEditingWindowProps { + menu: DocCreatorMenu; + template: Template; +} + +@observer +export class TemplateEditingWindow extends ObservableReactComponent<TemplateEditingWindowProps> { + private disposers: { [name: string]: IDisposer } = {}; + + @observable private _previewWindow: HTMLDivElement | null = null; + @observable _variationsTabOpen: boolean = false; + + constructor(props: TemplateEditingWindowProps) { + super(props); + makeObservable(this); + } + + componentDidMount(): void { + this.disposers.windowDimensions = reaction( + () => this._props.menu._resizing, + () => this.forceUpdate(), + { fireImmediately: true } + ); + } + + componentWillUnmount() { + Object.values(this.disposers).forEach(disposer => disposer?.()); + } + + @action setVariationTab = (open: boolean) => { + this._variationsTabOpen = open; + 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); + } + }; + + 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={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-expanded-template-preview"> + <div className="top-panel" /> + {this.renderedDocPreview} + {this._variationsTabOpen ? <FireflyVariationsTab menu={this._props.menu} template={this._props.template} /> : null} + <div className="right-buttons-panel"> + <DocCreatorMenuButton icon="minimize" function={this.expandFunc} /> + <DocCreatorMenuButton icon="lightbulb" function={this.variationFunc} /> + <DocCreatorMenuButton icon="arrow-rotate-backward" function={this.lastFunc} /> + </div> + </div> + </div> + ); + } +} |