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; } @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: 1, 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 ?? 1; 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; 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; } i = 0; curX = startX; curY += docHeight + yGap; } }; @computed get previewInfo() { const docHeight = NumCast(this.renderedDocs[0]?._height); const docWidth = NumCast(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; } } 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 ? (
) : !this.renderedDocCollection ? null : (
); }); 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; } }