aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/nodes/DataVizBox/DocCreatorMenu.tsx
diff options
context:
space:
mode:
authorbobzel <zzzman@gmail.com>2025-03-10 16:13:04 -0400
committerbobzel <zzzman@gmail.com>2025-03-10 16:13:04 -0400
commitb7989dded8bb001876de6cbca59bf77935f0daf7 (patch)
tree0dba0665674db7bb84770833df0a4100d0520701 /src/client/views/nodes/DataVizBox/DocCreatorMenu.tsx
parent4979415d4604d280e81a162bf9a9d39c731d3738 (diff)
parent5bf944035c0ba94ad15245416f51ca0329a51bde (diff)
Merge branch 'master' into alyssa-starter
Diffstat (limited to 'src/client/views/nodes/DataVizBox/DocCreatorMenu.tsx')
-rw-r--r--src/client/views/nodes/DataVizBox/DocCreatorMenu.tsx2367
1 files changed, 0 insertions, 2367 deletions
diff --git a/src/client/views/nodes/DataVizBox/DocCreatorMenu.tsx b/src/client/views/nodes/DataVizBox/DocCreatorMenu.tsx
deleted file mode 100644
index 7c601185e..000000000
--- a/src/client/views/nodes/DataVizBox/DocCreatorMenu.tsx
+++ /dev/null
@@ -1,2367 +0,0 @@
-import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { Colors } from 'browndash-components';
-import { action, computed, makeObservable, observable, reaction, 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 { emptyFunction } from '../../../../Utils';
-import { Doc, NumListCast, StrListCast, returnEmptyDoclist } from '../../../../fields/Doc';
-import { Id } from '../../../../fields/FieldSymbols';
-import { Cast, DocCast, ImageCast, StrCast } from '../../../../fields/Types';
-import { ImageField } from '../../../../fields/URLField';
-import { Networking } from '../../../Network';
-import { GPTCallType, gptAPICall, gptImageCall } from '../../../apis/gpt/GPT';
-import { Docs } from '../../../documents/Documents';
-import { DragManager } from '../../../util/DragManager';
-import { MakeTemplate } from '../../../util/DropConverter';
-import { SnappingManager } from '../../../util/SnappingManager';
-import { UndoManager, undoable } from '../../../util/UndoManager';
-import { LightboxView } from '../../LightboxView';
-import { ObservableReactComponent } from '../../ObservableReactComponent';
-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';
-
-export enum LayoutType {
- Stacked = 'stacked',
- Grid = 'grid',
- Row = 'row',
- Column = 'column',
- Custom = 'custom',
-}
-
-@observer
-export class DocCreatorMenu extends ObservableReactComponent<FieldViewProps> {
- static Instance: DocCreatorMenu;
-
- private _disposers: { [name: string]: IDisposer } = {};
-
- private _ref: HTMLDivElement | null = null;
-
- @observable _templateDocs: Doc[] = [];
- @observable _selectedTemplate: Doc | undefined = undefined;
- @observable _columns: Col[] = [];
- @observable _selectedCols: { title: string; type: string; desc: string }[] | undefined = [];
-
- @observable _layout: { type: LayoutType; yMargin: number; xMargin: number; columns?: number; repeat: number } = { type: LayoutType.Grid, yMargin: 0, xMargin: 0, repeat: 0 };
- @observable _layoutPreview: boolean = true;
- @observable _layoutPreviewScale: number = 1;
- @observable _savedLayouts: DataVizTemplateLayout[] = [];
- @observable _expandedPreview: { icon: ImageField; doc: Doc } | undefined = undefined;
-
- @observable _suggestedTemplates: Doc[] = [];
- @observable _GPTOpt: boolean = false;
- @observable _userPrompt: string = '';
- @observable _callCount: number = 0;
- @observable _GPTLoading: boolean = false;
-
- @observable _pageX: number = 0;
- @observable _pageY: number = 0;
- @observable _indicatorX: number | undefined = undefined;
- @observable _indicatorY: number | undefined = undefined;
-
- @observable _hoveredLayoutPreview: number | undefined = undefined;
- @observable _mouseX: number = -1;
- @observable _mouseY: number = -1;
- @observable _startPos?: { x: number; y: number };
- @observable _shouldDisplay: boolean = false;
-
- @observable _menuContent: 'templates' | 'options' | 'saved' | 'dashboard' = 'templates';
- @observable _dragging: boolean = false;
- @observable _draggingIndicator: boolean = false;
- @observable _dataViz?: DataVizBox;
- @observable _interactionLock: any;
- @observable _snapPt: any;
- @observable _resizeHdlId: string = '';
- @observable _resizing: boolean = false;
- @observable _offset: { x: number; y: number } = { x: 0, y: 0 };
- @observable _resizeUndo: UndoManager.Batch | undefined = undefined;
- @observable _initDimensions: { width: number; height: number; x?: number; y?: number } = { width: 300, height: 400, x: undefined, y: undefined };
- @observable _menuDimensions: { width: number; height: number } = { width: 400, height: 400 };
- @observable _editing: boolean = false;
-
- constructor(props: any) {
- super(props);
- makeObservable(this);
- DocCreatorMenu.Instance = this;
- //setTimeout(() => this.generateTemplates(''));
- }
-
- @action setDataViz = (dataViz: DataVizBox) => {
- this._dataViz = dataViz;
- };
- @action setTemplateDocs = (docs: Doc[]) => {
- this._templateDocs = docs.map(doc => (doc.annotationOn ? DocCast(doc.annotationOn) : doc));
- };
- @action setGSuggestedTemplates = (docs: Doc[]) => {
- this._suggestedTemplates = docs;
- };
-
- @computed get docsToRender() {
- return this._selectedTemplate ? NumListCast(this._dataViz?.layoutDoc.dataViz_selectedRows) : [];
- }
-
- @computed get rowsCount() {
- switch (this._layout.type) {
- case LayoutType.Row:
- case LayoutType.Stacked:
- return 1;
- case LayoutType.Column:
- return this.docsToRender.length;
- case LayoutType.Grid:
- return Math.ceil(this.docsToRender.length / (this._layout.columns ?? 1)) ?? 0;
- default:
- return 0;
- }
- }
-
- @computed get columnsCount() {
- switch (this._layout.type) {
- case LayoutType.Row:
- return this.docsToRender.length;
- case LayoutType.Column:
- case LayoutType.Stacked:
- return 1;
- case LayoutType.Grid:
- return this._layout.columns ?? 0;
- default:
- return 0;
- }
- }
-
- @computed get selectedFields() {
- return StrListCast(this._dataViz?.layoutDoc._dataViz_axes);
- }
-
- @computed get fieldsInfos(): Col[] {
- const colInfo = this._dataViz?.colsInfo;
- return this.selectedFields
- .map(field => {
- const fieldInfo = colInfo?.get(field);
-
- const col: Col = {
- title: field,
- type: fieldInfo?.type ?? TemplateFieldType.UNSET,
- desc: fieldInfo?.desc ?? '',
- sizes: fieldInfo?.sizes ?? [TemplateFieldSize.MEDIUM],
- };
-
- if (fieldInfo?.defaultContent !== undefined) {
- col.defaultContent = fieldInfo.defaultContent;
- }
-
- return col;
- })
- .concat(this._columns);
- }
-
- @computed get canMakeDocs() {
- return this._selectedTemplate !== undefined && this._layout !== undefined;
- }
-
- get bounds(): { t: number; b: number; l: number; r: number } {
- const rect = this._ref?.getBoundingClientRect();
- const bounds = { t: rect?.top ?? 0, b: rect?.bottom ?? 0, l: rect?.left ?? 0, r: rect?.right ?? 0 };
- return bounds;
- }
-
- setUpButtonClick = (e: any, func: Function) => {
- setupMoveUpEvents(
- this,
- e,
- returnFalse,
- emptyFunction,
- undoable(clickEv => {
- clickEv.stopPropagation();
- clickEv.preventDefault();
- func();
- }, 'create docs')
- );
- };
-
- @action
- onPointerDown = (e: PointerEvent) => {
- this._mouseX = e.clientX;
- this._mouseY = e.clientY;
- };
-
- @action
- onPointerUp = (e: PointerEvent) => {
- if (this._resizing) {
- this._initDimensions.width = this._menuDimensions.width;
- this._initDimensions.height = this._menuDimensions.height;
- this._initDimensions.x = this._pageX;
- this._initDimensions.y = this._pageY;
- document.removeEventListener('pointermove', this.onResize);
- SnappingManager.SetIsResizing(undefined);
- this._resizing = false;
- }
- if (this._dragging) {
- document.removeEventListener('pointermove', this.onDrag);
- this._dragging = false;
- }
- if (e.button !== 2 && !e.ctrlKey) return;
- const curX = e.clientX;
- const curY = e.clientY;
- if (Math.abs(this._mouseX - curX) > 1 || Math.abs(this._mouseY - curY) > 1) {
- this._shouldDisplay = false;
- }
- };
-
- componentDidMount() {
- document.addEventListener('pointerdown', this.onPointerDown, true);
- document.addEventListener('pointerup', this.onPointerUp);
- this._disposers.templates = reaction(
- () => this._templateDocs.slice(),
- docs => this.updateIcons(docs)
- );
- this._disposers.gpt = reaction(
- () => this._suggestedTemplates.slice(),
- docs => this.updateIcons(docs)
- );
- //this._disposers.columns = reaction(() => this._dataViz?.layoutDoc._dataViz_axes, () => {this.generateTemplates('')})
- this._disposers.lightbox = reaction(
- () => LightboxView.LightboxDoc(),
- doc => {
- // NOTE: bcz; commented this out because the doc creator would appear everytime I close out of the lightbox
- // doc ? this._shouldDisplay && this.closeMenu() : !this._shouldDisplay && this.openMenu();
- }
- );
- //this._disposers.fields = reaction(() => this._dataViz?.axes, cols => this._selectedCols = cols?.map(col => { return {title: col, type: '', desc: ''}}))
- }
-
- componentWillUnmount() {
- Object.values(this._disposers).forEach(disposer => disposer?.());
- document.removeEventListener('pointerdown', this.onPointerDown, true);
- document.removeEventListener('pointerup', this.onPointerUp);
- }
-
- updateIcons = (docs: Doc[]) => {
- console.log('called');
- docs.map(this.getIcon);
- };
-
- @action
- updateSelectedCols = (cols: string[]) => {
- this._selectedCols;
- };
-
- @action
- toggleDisplay = (x: number, y: number) => {
- if (this._shouldDisplay) {
- this._shouldDisplay = false;
- } else {
- this._pageX = x;
- this._pageY = y;
- this._shouldDisplay = true;
- }
- };
-
- @action
- closeMenu = () => {
- this._shouldDisplay = false;
- };
-
- @action
- openMenu = () => {
- const allTemplates = this._templateDocs.concat(this._suggestedTemplates);
- this._shouldDisplay = true;
- this.updateIcons(allTemplates);
- };
-
- @action
- onResizePointerDown = (e: React.PointerEvent): void => {
- this._resizing = true;
- document.addEventListener('pointermove', this.onResize);
- 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 bounds = e.currentTarget.getBoundingClientRect();
- this._offset = {
- x: id.toLowerCase().includes('left') ? bounds.right - e.clientX - pad : bounds.left - e.clientX + pad, //
- y: id.toLowerCase().includes('top') ? bounds.bottom - e.clientY - pad : bounds.top - e.clientY + pad,
- };
- this._resizeUndo = UndoManager.StartBatch('drag resizing');
- this._snapPt = { x: e.pageX, y: e.pageY };
- };
-
- @action
- onResize = (e: any): boolean => {
- const dragHdl = this._resizeHdlId.split(' ')[1];
- const thisPt = DragManager.snapDrag(e, -this._offset.x, -this._offset.y, this._offset.x, this._offset.y);
-
- const { scale, refPt, transl } = this.getResizeVals(thisPt, dragHdl);
- !this._interactionLock && runInAction(async () => { // resize selected docs if we're not in the middle of a resize (ie, throttle input events to frame rate)
- 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)})});
- }); // prettier-ignore
- return true;
- };
-
- @action
- onDrag = (e: any): boolean => {
- this._pageX = e.pageX - (this._startPos?.x ?? 0);
- this._pageY = e.pageY - (this._startPos?.y ?? 0);
- this._initDimensions.x = this._pageX;
- this._initDimensions.y = this._pageY;
- return true;
- };
-
- 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];
- 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;
- case 'topRight': vals = { scale: { x: 1 + moveX / w, y: 1 -moveY / h }, refPt: [this.bounds.l, this.bounds.b], transl: {x: 0, y: moveY } }; break;
- case 'top': vals = { scale: { x: 1, y: 1 -moveY / h }, refPt: [this.bounds.l, this.bounds.b], transl: {x: 0, y: moveY } }; break;
- case 'left': vals = { scale: { x: 1 - moveX / w, y: 1 }, refPt: [this.bounds.r, this.bounds.t], transl: {x: moveX, y: 0 } }; break;
- case 'bottomLeft': vals = { scale: { x: 1 - moveX / w, y: 1 + moveY / h }, refPt: [this.bounds.r, this.bounds.t], transl: {x: moveX, y: 0 } }; break;
- case 'right': vals = { scale: { x: 1 + moveX / w, y: 1 }, refPt: [this.bounds.l, this.bounds.t], transl: {x: 0, y: 0 } }; break;
- case 'bottomRight':vals = { scale: { x: 1 + moveX / w, y: 1 + moveY / h }, refPt: [this.bounds.l, this.bounds.t], transl: {x: 0, y: 0 } }; break;
- case 'bottom': vals = { scale: { x: 1, y: 1 + moveY / h }, refPt: [this.bounds.l, this.bounds.t], transl: {x: 0, y: 0 } }; break;
- default: vals = { scale: { x: 1, y: 1 }, refPt: [this.bounds.l, this.bounds.t], transl: {x: 0, y: 0 } }; break;
- } // prettier-ignore
- return vals;
- };
-
- 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;
-
- this._menuDimensions.width = Math.max(300, scale.x * width);
- this._menuDimensions.height = Math.max(200, scale.y * height);
- this._pageX = x + translation.x;
- this._pageY = y + translation.y;
- };
-
- async getIcon(doc: Doc) {
- const docView = DocumentView.getDocumentView(doc);
- if (docView) {
- docView.ComponentView?.updateIcon?.();
- return new Promise<ImageField | undefined>(res => setTimeout(() => res(ImageCast(docView.Document.icon)), 500));
- }
- return undefined;
- }
-
- @action updateSelectedTemplate = (template: Doc) => {
- if (this._selectedTemplate === template) {
- this._selectedTemplate = undefined;
- return;
- } else {
- this._selectedTemplate = template;
- MakeTemplate(template);
- }
- };
-
- @action updateSelectedSavedLayout = (layout: DataVizTemplateLayout) => {
- this._layout.xMargin = layout.layout.xMargin;
- this._layout.yMargin = layout.layout.yMargin;
- this._layout.type = layout.layout.type;
- this._layout.columns = layout.columns;
- };
-
- isSelectedLayout = (layout: DataVizTemplateLayout) => {
- return this._layout.xMargin === layout.layout.xMargin && this._layout.yMargin === layout.layout.yMargin && this._layout.type === layout.layout.type && this._layout.columns === layout.columns;
- };
-
- @action
- generateTemplates = async (inputText: string) => {
- ++this._callCount;
- const origCount = this._callCount;
-
- let prompt: string = `(#${origCount}) Please generate for the fields:`;
- this.selectedFields?.forEach(field => (prompt += ` ${field},`));
- prompt += ` (-----NOT A FIELD-----) Additional prompt: ${inputText}`;
-
- this._GPTLoading = true;
-
- try {
- const res = await gptAPICall(prompt, GPTCallType.TEMPLATE);
-
- if (res && this._callCount === origCount) {
- this._suggestedTemplates = [];
- const templates: { template_type: string; fieldVals: { title: string; tlx: string; tly: string; brx: string; bry: string }[] }[] = JSON.parse(res);
- this.createGeneratedTemplates(templates, 500, 500);
- }
- } catch (err) {
- console.error(err);
- }
- };
-
- @action
- createGeneratedTemplates = (layouts: { template_type: string; fieldVals: { title: string; tlx: string; tly: string; brx: string; bry: string }[] }[], tempWidth: number, tempHeight: number) => {
- const mainCollection = this._dataViz?.DocumentView?.().containerViewPath?.().lastElement()?.ComponentView as CollectionFreeFormView;
- const GPTTemplates: Doc[] = [];
-
- layouts.forEach(layout => {
- const fields: Doc[] = layout.fieldVals.map(field => {
- const left: number = (Number(field.tlx) * tempWidth) / 2;
- const top: number = Number(field.tly) * tempHeight / 2; //prettier-ignore
- const right: number = (Number(field.brx) * tempWidth) / 2;
- const bottom: number = Number(field.bry) * tempHeight / 2; //prettier-ignore
- const height = bottom - top;
- const width = right - left;
- const doc = !field.title.includes('$$')
- ? Docs.Create.TextDocument('', { _height: height, _width: width, title: field.title, x: left, y: top, text_fontSize: `${height / 2}` })
- : Docs.Create.ImageDocument('', { _height: height, _width: width, title: field.title.replace(/\$\$/g, ''), x: left, y: top });
- return doc;
- });
-
- const template = Docs.Create.FreeformDocument(fields, { _height: tempHeight, _width: tempWidth, title: layout.template_type, x: 400000, y: 400000 });
-
- mainCollection.addDocument(template);
-
- GPTTemplates.push(template);
- });
-
- setTimeout(() => {
- this.setGSuggestedTemplates(GPTTemplates); /*GPTTemplates.forEach(template => mainCollection.removeDocument(template))*/
- }, 100);
-
- this.forceUpdate();
- };
-
- editTemplate = (doc: Doc) => {
- //this.closeMenu();
- DocumentViewInternal.addDocTabFunc(doc, OpenWhere.addRight);
- DocumentView.DeselectAll();
- Doc.UnBrushDoc(doc);
- };
-
- removeTemplate = (doc: Doc) => {
- this._templateDocs.splice(this._templateDocs.indexOf(doc), 1);
- };
-
- testTemplate = async () => {
- this.updateIcons(this._suggestedTemplates.slice());
- this.forceUpdate();
-
- // try {
- // const res = await gptImageCall('Image of panda eating a cookie');
-
- // if (res) {
- // const result = await Networking.PostToServer('/uploadRemoteImage', { sources: res });
-
- // console.log(result);
- // }
- // } catch (e) {
- // console.log(e);
- // }
- };
-
- @action addField = () => {
- const newFields: Col[] = this._columns.concat([{ title: '', type: TemplateFieldType.UNSET, desc: '', sizes: [] }]);
- this._columns = newFields;
- };
-
- @action removeField = (field: { title: string; type: string; desc: string }) => {
- if (this._dataViz?.axes.includes(field.title)) {
- this._dataViz.selectAxes(this._dataViz.axes.filter(col => col !== field.title));
- } else {
- const toRemove = this._columns.filter(f => f === field);
- if (!toRemove) return;
-
- if (toRemove.length > 1) {
- while (toRemove.length > 1) {
- toRemove.pop();
- }
- }
-
- if (this._columns.length === 1) {
- this._columns = [];
- } else {
- this._columns.splice(this._columns.indexOf(toRemove[0]), 1);
- }
- }
- };
-
- @action setColTitle = (column: Col, title: string) => {
- if (this.selectedFields.includes(column.title)) {
- this._dataViz?.setColumnTitle(column.title, title);
- } else {
- column.title = title;
- }
- this.forceUpdate();
- };
-
- @action setColType = (column: Col, type: TemplateFieldType) => {
- if (this.selectedFields.includes(column.title)) {
- this._dataViz?.setColumnType(column.title, type);
- } else {
- column.type = type;
- }
- this.forceUpdate();
- };
-
- modifyColSizes = (column: Col, size: TemplateFieldSize, valid: boolean) => {
- if (this.selectedFields.includes(column.title)) {
- this._dataViz?.modifyColumnSizes(column.title, size, valid);
- } else {
- if (!valid && column.sizes.includes(size)) {
- column.sizes.splice(column.sizes.indexOf(size), 1);
- } else if (valid && !column.sizes.includes(size)) {
- column.sizes.push(size);
- }
- }
- this.forceUpdate();
- };
-
- setColDesc = (column: Col, desc: string) => {
- if (this.selectedFields.includes(column.title)) {
- this._dataViz?.setColumnDesc(column.title, desc);
- } else {
- column.desc = desc;
- }
- this.forceUpdate();
- };
-
- 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 source = ClientUtils.prepend(result[0].accessPaths.agnostic.client);
- return source;
- }
- } catch (e) {
- console.log(e);
- }
- };
-
- matchesForTemplate = (template: TemplateDocInfos, cols: Col[]): number[][] => {
- const colMatchesField = (col: Col, field: Field) => {
- return field.sizes?.some(size => col.sizes?.includes(size)) && field.types?.includes(col.type);
- };
-
- const matches: number[][] = Array(template.fields.length)
- .fill([])
- .map(() => []);
-
- template.fields.forEach((field, i) => {
- cols.forEach((col, v) => {
- if (colMatchesField(col, field)) {
- matches[i].push(v);
- }
- });
- });
-
- return matches;
- };
-
- maxMatches = (fieldsCt: number, matches: number[][]) => {
- const used: boolean[] = Array(fieldsCt).fill(false);
- const mt: number[] = Array(fieldsCt).fill(-1);
-
- const augmentingPath = (v: number): boolean => {
- if (used[v]) return false;
- used[v] = true;
- for (const to of matches[v]) {
- if (mt[to] === -1 || augmentingPath(mt[to])) {
- mt[to] = v;
- return true;
- }
- }
- return false;
- };
-
- for (let v = 0; v < fieldsCt; ++v) {
- used.fill(false);
- augmentingPath(v);
- }
-
- let count: number = 0;
-
- for (let i = 0; i < fieldsCt; ++i) {
- if (mt[i] !== -1) ++count;
- }
-
- return count;
- };
-
- findValidTemplates = (cols: Col[], templates: TemplateDocInfos[]) => {
- let validTemplates: any[] = [];
- templates.forEach(template => {
- const numFields = template.fields.length;
- if (!(numFields === cols.length)) return;
- const matches = this.matchesForTemplate(template, cols);
- if (this.maxMatches(numFields, matches) === numFields) {
- validTemplates.push(template.title);
- }
- });
-
- validTemplates = validTemplates.map(title => TemplateLayouts.getTemplateByTitle(title));
-
- return validTemplates;
- };
-
- // createColumnField = (template: TemplateDocInfos, field: Field, column: Col): Doc => {
-
- // if (field.subfields) {
- // const doc = FieldFuncs.FreeformField({
- // tl: field.tl,
- // br: field.br },
- // template.height,
- // template.width,
- // column.title,
- // '',
- // field.opts
- // );
-
- // field.subfields[1].forEach(f => {
- // const fDoc = ()
- // })
-
- // }
-
- // return new Doc;
- // }
-
- /**
- * Populates a preset template framework with content from a datavizbox or any AI-generated content.
- * @param template the preloaded template framework being filled in
- * @param assignments a list of template field numbers (from top to bottom) and their assigned columns from the linked dataviz
- * @returns a doc containing the fully rendered template
- */
- fillPresetTemplate = async (template: TemplateDocInfos, assignments: { [field: string]: Col }): Promise<Doc> => {
- const wordLimit = (size: TemplateFieldSize) => {
- switch (size) {
- case TemplateFieldSize.TINY:
- return 2;
- case TemplateFieldSize.SMALL:
- return 5;
- case TemplateFieldSize.MEDIUM:
- return 20;
- case TemplateFieldSize.LARGE:
- return 50;
- case TemplateFieldSize.HUGE:
- return 100;
- default:
- return 10;
- }
- };
-
- const renderTextCalls = async (): Promise<Doc[]> => {
- const rendered: Doc[] = [];
-
- if (GPTTextCalls.length) {
- try {
- const prompt = fieldContent + GPTTextAssignment;
-
- const res = await gptAPICall(prompt, GPTCallType.FILL);
-
- if (res) {
- const assignments: { [title: string]: { number: string; content: string } } = JSON.parse(res);
- //console.log('assignments', GPTAssignments, 'assignment string', GPTAssignmentString, 'field content', fieldContent, 'response', res, 'assignments', assignments);
- Object.entries(assignments).forEach(([title, info]) => {
- const field: Field = template.fields[Number(info.number)];
- const col = this.getColByTitle(title);
-
- const doc = FieldUtils.TextField(
- {
- tl: field.tl,
- br: field.br,
- },
- template.height,
- template.width,
- col.title,
- info.content ?? '',
- field.opts
- );
-
- rendered.push(doc);
- });
- }
- } catch (err) {
- console.log(err);
- }
- }
-
- return rendered;
- };
-
- const createGeneratedImage = async (fieldNum: string, col: Col, prompt: string) => {
- const url = await this.generateGPTImage(prompt);
- const field: Field = template.fields[Number(fieldNum)];
- const doc = FieldUtils.ImageField(
- {
- tl: field.tl,
- br: field.br,
- },
- template.height,
- template.width,
- col.title,
- url ?? '',
- field.opts
- );
-
- return doc;
- };
-
- const renderImageCalls = async (): Promise<Doc[]> => {
- const rendered: Doc[] = [];
- const calls = GPTIMGCalls;
-
- if (calls.length) {
- try {
- const renderedImages: Doc[] = await Promise.all(
- calls.map(async ([fieldNum, col]) => {
- const sysPrompt =
- 'Your job is to create a prompt for an AI image generator to help it generate an image based on existing content in a template and a user prompt. Your prompt should focus heavily on visual elements to help the image generator; avoid unecessary info that might distract it. ONLY INCLUDE THE PROMPT, NO OTHER TEXT OR EXPLANATION. The existing content is as follows: ' +
- fieldContent +
- ' **** The user prompt is: ' +
- col.desc;
-
- const prompt = await gptAPICall(sysPrompt, GPTCallType.COMPLETEPROMPT);
- console.log(sysPrompt, prompt);
-
- return createGeneratedImage(fieldNum, col, prompt);
- })
- );
-
- const renderedTemplates: Doc[] = await Promise.all(renderedImages);
- renderedTemplates.forEach(doc => rendered.push(doc));
- } catch (e) {
- console.log(e);
- }
- }
-
- return rendered;
- };
-
- const fields: Doc[] = [];
-
- const GPTAssignments = Object.entries(assignments).filter(([f, col]) => this._columns.includes(col));
- const nonGPTAssignments: [string, Col][] = Object.entries(assignments).filter(a => !GPTAssignments.includes(a));
- const GPTTextCalls = GPTAssignments.filter(([str, col]) => col.type === TemplateFieldType.TEXT);
- const GPTIMGCalls = GPTAssignments.filter(([str, col]) => col.type === TemplateFieldType.VISUAL);
-
- const stringifyGPTInfo = (calls: [string, Col][]): string => {
- let string: string = '*** COLUMN INFO:';
- calls.forEach(([fieldNum, col]) => {
- string += `--- title: ${col.title}, prompt: ${col.desc}, word limit: ${wordLimit(col.sizes[0])} words, assigned field: ${fieldNum} ---`;
- });
- return (string += ' ***');
- };
-
- const GPTTextAssignment = stringifyGPTInfo(GPTTextCalls);
-
- let fieldContent: string = '';
-
- Object.entries(nonGPTAssignments).forEach(([f, strCol]) => {
- const field: Field = template.fields[Number(f)];
- const col = strCol[1];
-
- const doc = (col.type === TemplateFieldType.VISUAL ? FieldUtils.ImageField : FieldUtils.TextField)(
- {
- tl: field.tl,
- br: field.br,
- },
- template.height,
- template.width,
- col.title,
- col.defaultContent ?? '',
- field.opts
- );
-
- fieldContent += `--- Field #${f} (title: ${col.title}): ${col.defaultContent ?? ''} ---`;
-
- fields.push(doc);
- });
-
- template.decorations.forEach(dec => {
- const doc = FieldUtils.FreeformField(
- {
- tl: dec.tl,
- br: dec.br,
- },
- template.height,
- template.width,
- '',
- '',
- dec.opts
- );
-
- fields.push(doc);
- });
-
- const createMainDoc = (): Doc => {
- const main = Docs.Create.FreeformDocument(fields, {
- _height: template.height,
- _width: template.width,
- title: template.title,
- backgroundColor: template.opts.backgroundColor,
- _layout_borderRounding: `${template.opts.cornerRounding}px` ?? '0px',
- borderWidth: template.opts.borderWidth,
- borderColor: template.opts.borderColor,
- x: 40000,
- y: 40000,
- });
-
- const mainCollection = this._dataViz?.DocumentView?.().containerViewPath?.().lastElement()?.ComponentView as CollectionFreeFormView;
- mainCollection.addDocument(main);
-
- return main;
- };
-
- const textCalls = await renderTextCalls();
- const imageCalls = await renderImageCalls();
-
- textCalls.forEach(doc => {
- fields.push(doc);
- });
- imageCalls.forEach(doc => {
- fields.push(doc);
- });
-
- return createMainDoc();
- };
-
- compileFieldDescriptions = (templates: TemplateDocInfos[]): string => {
- let descriptions: string = '';
- templates.forEach(template => {
- descriptions += `---------- NEW TEMPLATE TO INCLUDE: Description of template ${template.title}'s fields: `;
- template.fields.forEach((field, index) => {
- descriptions += `{Field #${index}: ${field.description}} `;
- });
- });
-
- return descriptions;
- };
-
- compileColDescriptions = (cols: Col[]): string => {
- let descriptions: string = ' ------------- COL DESCRIPTIONS START HERE:';
- cols.forEach(col => (descriptions += `{title: ${col.title}, sizes: ${String(col.sizes)}, type: ${col.type}, descreiption: ${col.desc}} `));
-
- return descriptions;
- };
-
- getColByTitle = (title: string) => {
- return this.fieldsInfos.filter(col => col.title === title)[0];
- };
-
- @action
- assignColsToFields = async (templates: TemplateDocInfos[], cols: Col[]): Promise<[TemplateDocInfos, { [field: number]: Col }][]> => {
- const fieldDescriptions: string = this.compileFieldDescriptions(templates);
- const colDescriptions: string = this.compileColDescriptions(cols);
-
- const inputText = fieldDescriptions.concat(colDescriptions);
-
- ++this._callCount;
- const origCount = this._callCount;
-
- let prompt: string = `(${origCount}) ${inputText}`;
-
- this._GPTLoading = true;
-
- try {
- const res = await gptAPICall(prompt, GPTCallType.TEMPLATE);
-
- if (res && this._callCount === origCount) {
- const assignments: { [templateTitle: string]: { [field: string]: string } } = JSON.parse(res);
- const brokenDownAssignments: [TemplateDocInfos, { [field: number]: Col }][] = [];
-
- Object.entries(assignments).forEach(([tempTitle, assignment]) => {
- const template = TemplateLayouts.getTemplateByTitle(tempTitle);
- if (!template) return;
- const toObj = Object.entries(assignment).reduce(
- (a, [fieldNum, colTitle]) => {
- a[Number(fieldNum)] = this.getColByTitle(colTitle);
- return a;
- },
- {} as { [field: number]: Col }
- );
- brokenDownAssignments.push([template, toObj]);
- });
- return brokenDownAssignments;
- }
- } catch (err) {
- console.error(err);
- }
-
- return [];
- };
-
- generatePresetTemplates = async () => {
- this._dataViz?.updateColDefaults();
-
- const cols = this.fieldsInfos;
- const templates = this.findValidTemplates(cols, TemplateLayouts.allTemplates);
-
- const assignments: [TemplateDocInfos, { [field: number]: Col }][] = await this.assignColsToFields(templates, cols);
-
- const renderedTemplatePromises: Promise<Doc>[] = assignments.map(([template, assignments]) => this.fillPresetTemplate(template, assignments));
-
- const renderedTemplates: Doc[] = await Promise.all(renderedTemplatePromises);
-
- setTimeout(() => {
- this.setGSuggestedTemplates(renderedTemplates);
- this._GPTLoading = false;
- });
- };
-
- @action setExpandedView = (info: { icon: ImageField; doc: Doc } | undefined) => {
- if (info) {
- const doc = info.doc;
- const wrapper: Doc = Docs.Create.FreeformDocument([info.doc], { _height: NumListCast(doc._height)[0], _width: NumListCast(doc._width)[0], title: '' });
- const newInfo = { icon: new ImageField(''), doc: wrapper };
- this._expandedPreview = newInfo;
- } else {
- this._expandedPreview = info;
- }
- };
-
- get editingWindow() {
- const doc = this._expandedPreview?.doc ?? new Doc();
- const rendered = (
- <div className="docCreatorMenu-expanded-template-preview">
- <CollectionFreeFormView
- Document={this._expandedPreview!.doc}
- docViewPath={returnEmptyDocViewList}
- childLayoutTemplate={() => Cast(doc.childLayoutTemplate, Doc, null)}
- isContentActive={emptyFunction}
- isAnyChildContentActive={() => true}
- select={emptyFunction}
- isSelected={returnFalse}
- fieldKey={Doc.LayoutFieldKey(doc)}
- addDocument={returnFalse}
- moveDocument={returnFalse}
- removeDocument={returnFalse}
- PanelWidth={() => this._menuDimensions.width - 10}
- PanelHeight={() => this._menuDimensions.height - 60}
- ScreenToLocalTransform={() => new Transform(-this._pageX, -this._pageY, 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}
- searchFilterDocs={returnEmptyDoclist}
- fitContentsToBox={returnTrue}
- xPadding={0}
- yPadding={0}
- />
- </div>
- );
-
- return (
- <div className="docCreatorMenu-expanded-template-preview">
- <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, () => {
- this._expandedPreview && this.updateIcons(this._suggestedTemplates.slice());
- this.setExpandedView(undefined);
- })
- }>
- <FontAwesomeIcon icon="minimize" />
- </button>
- <button className="docCreatorMenu-menu-button section-reveal-options top-right-lower" onPointerDown={e => this.setUpButtonClick(e, () => this._expandedPreview && this._templateDocs.push(this._expandedPreview.doc))}>
- <FontAwesomeIcon icon="plus" color="white" />
- </button>
- </div>
- </div>
- );
- }
-
- get templatesPreviewContents() {
- const renderedTemplates: Doc[] = [];
-
- const GPTOptions = <div></div>;
-
- //<img className='docCreatorMenu-preview-image expanded' src={this._expandedPreview.icon!.url.href.replace(".png", "_o.png")} />
-
- return (
- <div className={`docCreatorMenu-templates-view`}>
- {this._expandedPreview ? (
- this.editingWindow
- ) : (
- <div>
- <div className="docCreatorMenu-section" style={{ height: this._GPTOpt ? 200 : 200 }}>
- <div className="docCreatorMenu-section-topbar">
- <div className="docCreatorMenu-section-title">Suggested Templates</div>
- <button className="docCreatorMenu-menu-button section-reveal-options" onPointerDown={e => this.setUpButtonClick(e, () => runInAction(() => (this._menuContent = 'dashboard')))}>
- <FontAwesomeIcon icon="gear" />
- </button>
- </div>
- <div className="docCreatorMenu-templates-preview-window" style={{ justifyContent: this._GPTLoading || this._menuDimensions.width > 400 ? 'center' : '' }}>
- {this._GPTLoading ? (
- <div className="loading-spinner">
- <ReactLoading type="spin" color={StrCast(Doc.UserDoc().userVariantColor)} height={30} width={30} />
- </div>
- ) : (
- this._suggestedTemplates
- ?.map(doc => ({ icon: ImageCast(doc.icon), doc }))
- .filter(info => info.icon && info.doc)
- .map(info => (
- <div
- className="docCreatorMenu-preview-window"
- style={{
- border: this._selectedTemplate === info.doc ? `solid 3px ${Colors.MEDIUM_BLUE}` : '',
- boxShadow: this._selectedTemplate === info.doc ? `0 0 15px rgba(68, 118, 247, .8)` : '',
- }}
- onPointerDown={e => this.setUpButtonClick(e, () => runInAction(() => this.updateSelectedTemplate(info.doc)))}>
- <button
- className="option-button left"
- onPointerDown={e =>
- this.setUpButtonClick(e, () => {
- this.setExpandedView(info);
- })
- }>
- <FontAwesomeIcon icon="magnifying-glass" color="white" />
- </button>
- <button className="option-button right" onPointerDown={e => this.setUpButtonClick(e, () => this._templateDocs.push(info.doc))}>
- <FontAwesomeIcon icon="plus" color="white" />
- </button>
- <img className="docCreatorMenu-preview-image" src={info.icon!.url.href.replace('.png', '_o.png')} />
- </div>
- ))
- )}
- </div>
- <div className="docCreatorMenu-GPT-options">
- <div className="docCreatorMenu-GPT-options-container">
- <button className="docCreatorMenu-menu-button" onPointerDown={e => this.setUpButtonClick(e, () => this.generatePresetTemplates())}>
- <FontAwesomeIcon icon="arrows-rotate" />
- </button>
- </div>
- {this._GPTOpt ? GPTOptions : null}
- </div>
- </div>
- <hr className="docCreatorMenu-option-divider full no-margin" />
- <div className="docCreatorMenu-section">
- <div className="docCreatorMenu-section-topbar">
- <div className="docCreatorMenu-section-title">Your Templates</div>
- <button className="docCreatorMenu-menu-button section-reveal-options" onPointerDown={e => this.setUpButtonClick(e, () => (this._GPTOpt = !this._GPTOpt))}>
- <FontAwesomeIcon icon="gear" />
- </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()}>
- <FontAwesomeIcon icon="plus" color="rgb(160, 160, 160)" />
- </div>
- {this._templateDocs
- .map(doc => ({ icon: ImageCast(doc.icon), doc }))
- .filter(info => info.icon && info.doc)
- .map(info => {
- if (renderedTemplates.includes(info.doc)) return undefined;
- renderedTemplates.push(info.doc);
- return (
- <div
- className="docCreatorMenu-preview-window"
- style={{
- border: this._selectedTemplate === info.doc ? `solid 3px ${Colors.MEDIUM_BLUE}` : '',
- boxShadow: this._selectedTemplate === info.doc ? `0 0 15px rgba(68, 118, 247, .8)` : '',
- }}
- onPointerDown={e => this.setUpButtonClick(e, () => runInAction(() => this.updateSelectedTemplate(info.doc)))}>
- <button
- className="option-button left"
- onPointerDown={e =>
- this.setUpButtonClick(e, () => {
- this.editTemplate(info.doc);
- })
- }>
- <FontAwesomeIcon icon="pencil" color="black" />
- </button>
- <button
- className="option-button right"
- onPointerDown={e =>
- this.setUpButtonClick(e, () => {
- this.removeTemplate(info.doc);
- })
- }>
- <FontAwesomeIcon icon="trash" color="black" />
- </button>
- <img className="docCreatorMenu-preview-image" src={info.icon!.url.href.replace('.png', '_o.png')} />
- </div>
- );
- })}
- </div>
- </div>
- </div>
- )}
- </div>
- );
- }
-
- get savedLayoutsPreviewContents() {
- return (
- <div className="docCreatorMenu-preview-container">
- {this._savedLayouts.map((layout, index) => (
- <div
- className="docCreatorMenu-preview-window"
- style={{
- border: this.isSelectedLayout(layout) ? `solid 3px ${Colors.MEDIUM_BLUE}` : '',
- boxShadow: this.isSelectedLayout(layout) ? `0 0 15px rgba(68, 118, 247, .8)` : '',
- }}
- onPointerDown={e => this.setUpButtonClick(e, () => runInAction(() => this.updateSelectedSavedLayout(layout)))}>
- {this.layoutPreviewContents(87, layout, true, index)}
- </div>
- ))}
- </div>
- );
- }
-
- @action updateXMargin = (input: string) => {
- this._layout.xMargin = Number(input);
- };
- @action updateYMargin = (input: string) => {
- this._layout.yMargin = Number(input);
- };
- @action updateColumns = (input: string) => {
- this._layout.columns = Number(input);
- };
-
- get layoutConfigOptions() {
- const optionInput = (icon: string, func: Function, 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} />
- </div>
- <input defaultValue={def} onInput={e => func(e.currentTarget.value)} className="docCreatorMenu-input config layout-config" />
- </div>
- );
- };
-
- switch (this._layout.type) {
- case LayoutType.Row:
- return <div className="docCreatorMenu-configuration-bar">{optionInput('arrows-left-right', this.updateXMargin, this._layout.xMargin, '0')}</div>;
- case LayoutType.Column:
- return <div className="docCreatorMenu-configuration-bar">{optionInput('arrows-up-down', this.updateYMargin, this._layout.yMargin, '1')}</div>;
- case LayoutType.Grid:
- return (
- <div className="docCreatorMenu-configuration-bar">
- {optionInput('arrows-up-down', this.updateYMargin, this._layout.xMargin, '2')}
- {optionInput('arrows-left-right', this.updateXMargin, this._layout.xMargin, '3')}
- {optionInput('table-columns', this.updateColumns, this._layout.columns, '4', true)}
- </div>
- );
- case LayoutType.Stacked:
- return null;
- default:
- break;
- }
- }
-
- // doc = () => {
- // return Docs.Create.FreeformDocument([], { _height: 200, _width: 200, title: 'title'});
- // }
-
- screenToLocalTransform = () => this._props.ScreenToLocalTransform();
-
- layoutPreviewContents = (outerSpan: number, altLayout?: DataVizTemplateLayout, small: boolean = false, id?: number) => {
- const doc: Doc | undefined = altLayout ? altLayout.template : this._selectedTemplate;
- if (!doc) return;
-
- const layout = altLayout ? altLayout.layout : this._layout;
-
- const docWidth: number = Number(doc._width);
- const docHeight: number = Number(doc._height);
- const horizontalSpan: number = (docWidth + layout.xMargin) * (altLayout ? altLayout.columns : this.columnsCount) - layout.xMargin;
- const verticalSpan: number = (docHeight + layout.yMargin) * (altLayout ? altLayout.rows : this.rowsCount) - layout.yMargin;
- const largerSpan: number = horizontalSpan > verticalSpan ? horizontalSpan : verticalSpan;
- const scaledDown = (input: number) => {
- return input / ((largerSpan / outerSpan) * this._layoutPreviewScale);
- };
- const fontSize = Math.min(scaledDown(docWidth / 3), scaledDown(docHeight / 3));
-
- return (
- // <div className='divvv' style={{width: 100, height: 100, border: `1px solid white`}}>
- // <CollectionFreeFormView
- // // eslint-disable-next-line react/jsx-props-no-spreading
- // {...this._props}
- // Document={new Doc()}
- // isContentActive={returnFalse}
- // setContentViewBox={emptyFunction}
- // NativeWidth={() => 100}
- // NativeHeight={() => 100}
- // pointerEvents={SnappingManager.IsDragging ? returnAll : returnNone}
- // isAnnotationOverlay
- // isAnnotationOverlayScrollable
- // childDocumentsActive={returnFalse}
- // fieldKey={this._props.fieldKey + '_annotations'}
- // dropAction={dropActionType.move}
- // select={emptyFunction}
- // addDocument={returnFalse}
- // removeDocument={returnFalse}
- // moveDocument={returnFalse}
- // renderDepth={this._props.renderDepth + 1}>
- // {null}
- // </CollectionFreeFormView>
- // </div>
- <div className="docCreatorMenu-layout-preview-window-wrapper" id={String(id) ?? undefined}>
- <div className="docCreatorMenu-zoom-button-container">
- <button className="docCreatorMenu-zoom-button" onPointerDown={e => this.setUpButtonClick(e, () => runInAction(() => (this._layoutPreviewScale *= 1.25)))}>
- <FontAwesomeIcon icon={'minus'} />
- </button>
- <button className="docCreatorMenu-zoom-button zoom-in" onPointerDown={e => this.setUpButtonClick(e, () => runInAction(() => (this._layoutPreviewScale *= 0.75)))}>
- <FontAwesomeIcon icon={'plus'} />
- </button>
- {altLayout ? (
- <button className="docCreatorMenu-zoom-button trash" onPointerDown={e => this.setUpButtonClick(e, () => runInAction(() => this._savedLayouts.splice(this._savedLayouts.indexOf(altLayout), 1)))}>
- <FontAwesomeIcon icon={'trash'} />
- </button>
- ) : null}
- </div>
- {
- <div
- id={String(id) ?? undefined}
- className={`docCreatorMenu-layout-preview-window ${small ? 'small' : ''}`}
- style={{
- gridTemplateColumns: `repeat(${altLayout ? altLayout.columns : this.columnsCount}, ${scaledDown(docWidth)}px`,
- gridTemplateRows: `${scaledDown(docHeight)}px`,
- gridAutoRows: `${scaledDown(docHeight)}px`,
- rowGap: `${scaledDown(layout.yMargin)}px`,
- columnGap: `${scaledDown(layout.xMargin)}px`,
- }}>
- {this._layout.type === LayoutType.Stacked ? (
- <div
- className="docCreatorMenu-layout-preview-item"
- style={{
- width: scaledDown(docWidth),
- height: scaledDown(docHeight),
- fontSize: fontSize,
- }}>
- All
- </div>
- ) : (
- this.docsToRender.map(num => (
- <div
- onMouseEnter={() => this._dataViz?.setSpecialHighlightedRow(num)}
- onMouseLeave={() => this._dataViz?.setSpecialHighlightedRow(undefined)}
- className="docCreatorMenu-layout-preview-item"
- style={{
- width: scaledDown(docWidth),
- height: scaledDown(docHeight),
- fontSize: fontSize,
- }}>
- {num}
- </div>
- ))
- )}
- </div>
- }
- </div>
- );
- };
-
- get optionsMenuContents() {
- const layoutEquals = (layout: DataVizTemplateLayout) => {}; //TODO: ADD LATER
-
- const layoutOption = (option: LayoutType, optStyle?: {}, specialFunc?: Function) => {
- return (
- <div
- className="docCreatorMenu-dropdown-option"
- style={optStyle}
- onPointerDown={e =>
- this.setUpButtonClick(e, () => {
- specialFunc?.();
- runInAction(() => (this._layout.type = option));
- })
- }>
- {option}
- </div>
- );
- };
-
- const selectionBox = (width: number, height: number, icon: string, specClass?: string, options?: JSX.Element[], manual?: boolean): JSX.Element => {
- 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} />
- </div>
- {manual ? (
- <input className={`docCreatorMenu-input config ${specClass}`} style={{ width: width * 0.6, height: height }} />
- ) : (
- <select className={`docCreatorMenu-input config ${specClass}`} style={{ width: width * 0.6, height: height }}>
- {options}
- </select>
- )}
- </div>
- );
- };
-
- const repeatOptions = [0, 1, 2, 3, 4, 5];
-
- return (
- <div className="docCreatorMenu-menu-container">
- <div className="docCreatorMenu-option-container layout">
- <div className="docCreatorMenu-dropdown-hoverable">
- <div className="docCreatorMenu-option-title">{this._layout.type ? this._layout.type.toUpperCase() : 'Choose Layout'}</div>
- <div className="docCreatorMenu-dropdown-content">
- {layoutOption(LayoutType.Stacked)}
- {layoutOption(LayoutType.Grid, undefined, () => {
- if (!this._layout.columns) this._layout.columns = Math.ceil(Math.sqrt(this.docsToRender.length));
- })}
- {layoutOption(LayoutType.Row)}
- {layoutOption(LayoutType.Column)}
- {layoutOption(LayoutType.Custom, { borderBottom: `0px` })}
- </div>
- </div>
- <button className="docCreatorMenu-menu-button preview-toggle" onPointerDown={e => this.setUpButtonClick(e, () => runInAction(() => (this._layoutPreview = !this._layoutPreview)))}>
- <FontAwesomeIcon icon={this._layoutPreview ? 'minus' : 'magnifying-glass'} />
- </button>
- </div>
- {this._layout.type ? this.layoutConfigOptions : null}
- {this._layoutPreview ? this.layoutPreviewContents(this._menuDimensions.width * 0.75) : null}
- {selectionBox(
- 60,
- 20,
- 'repeat',
- undefined,
- repeatOptions.map(num => <option onPointerDown={e => (this._layout.repeat = num)}>{`${num}x`}</option>)
- )}
- <hr className="docCreatorMenu-option-divider" />
- <div className="docCreatorMenu-general-options-container">
- <button
- className="docCreatorMenu-save-layout-button"
- onPointerDown={e =>
- setupMoveUpEvents(
- this,
- e,
- returnFalse,
- emptyFunction,
- undoable(clickEv => {
- clickEv.stopPropagation();
- if (!this._selectedTemplate) return;
- const layout: DataVizTemplateLayout = {
- template: this._selectedTemplate,
- layout: { type: this._layout.type, xMargin: this._layout.xMargin, yMargin: this._layout.yMargin, repeat: 0 },
- columns: this.columnsCount,
- rows: this.rowsCount,
- docsNumList: this.docsToRender,
- };
- if (!this._savedLayouts.includes(layout)) {
- this._savedLayouts.push(layout);
- }
- }, 'make docs')
- )
- }>
- <FontAwesomeIcon icon="floppy-disk" />
- </button>
- <button
- className="docCreatorMenu-create-docs-button"
- style={{ backgroundColor: this.canMakeDocs ? '' : 'rgb(155, 155, 155)', border: this.canMakeDocs ? '' : 'solid 2px rgb(180, 180, 180)' }}
- onPointerDown={e =>
- setupMoveUpEvents(
- this,
- e,
- returnFalse,
- emptyFunction,
- undoable(clickEv => {
- clickEv.stopPropagation();
- if (!this._selectedTemplate) return;
- const templateInfo: DataVizTemplateInfo = { doc: this._selectedTemplate, layout: this._layout, referencePos: { x: this._pageX + 450, y: this._pageY }, columns: this.columnsCount };
- this._dataViz?.createDocsFromTemplate(templateInfo);
- }, 'make docs')
- )
- }>
- <FontAwesomeIcon icon="plus" />
- </button>
- </div>
- </div>
- );
- }
-
- get dashboardContents() {
- const sizes: string[] = ['tiny', 'small', 'medium', 'large', 'huge'];
-
- const fieldPanel = (field: Col) => {
- return (
- <div className="field-panel">
- <div className="top-bar">
- <span className="field-title">{`${field.title} Field`}</span>
- <button className="docCreatorMenu-menu-button section-reveal-options no-margin" onPointerDown={e => this.setUpButtonClick(e, () => this.removeField(field))} style={{ position: 'absolute', right: '0px' }}>
- <FontAwesomeIcon icon="minus" />
- </button>
- </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)' }} defaultValue={field.title} placeholder={'Enter title'} onChange={e => this.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' : ''}</span>
- <div className="bubbles">
- <input
- className="bubble"
- type="radio"
- name="type"
- onClick={() => {
- this.setColType(field, TemplateFieldType.TEXT);
- }}
- />
- <div className="text">Text</div>
- <input
- className="bubble"
- type="radio"
- name="type"
- onClick={() => {
- this.setColType(field, TemplateFieldType.VISUAL);
- }}
- />
- <div className="text">File</div>
- </div>
- </div>
- </div>
- </div>
- <div className="sizes-box">
- <div className="top-bar"> Valid Sizes </div>
- <div className="content">
- <div className="bubbles">
- {sizes.map(size => (
- <>
- <input
- className="bubble"
- type="checkbox"
- name="type"
- checked={field.sizes.includes(size as TemplateFieldSize)}
- onChange={e => {
- this.modifyColSizes(field, size as TemplateFieldSize, e.target.checked);
- }}
- />
- <div className="text">{size}</div>
- </>
- ))}
- </div>
- </div>
- </div>
- <div className="desc-box">
- <div className="top-bar"> Prompt </div>
- <textarea
- className="content"
- onChange={e => this.setColDesc(field, e.target.value)}
- defaultValue={field.desc === this._dataViz?.GPTSummary?.get(field.title)?.desc ? '' : field.desc}
- placeholder={this._dataViz?.GPTSummary?.get(field.title)?.desc ?? 'Add a description/prompt to help with template generation.'}
- />
- </div>
- </div>
- );
- };
-
- return (
- <div className="docCreatorMenu-dashboard-view">
- <div className="topbar">
- <button className="docCreatorMenu-menu-button section-reveal-options" onPointerDown={e => this.setUpButtonClick(e, this.addField)}>
- <FontAwesomeIcon icon="plus" />
- </button>
- <button className="docCreatorMenu-menu-button section-reveal-options float-right" onPointerDown={e => this.setUpButtonClick(e, () => runInAction(() => (this._menuContent = 'templates')))}>
- <FontAwesomeIcon icon="arrow-left" />
- </button>
- </div>
- <div className="panels-container">{this.fieldsInfos.map(field => fieldPanel(field))}</div>
- </div>
- );
- }
-
- get renderSelectedViewType() {
- switch (this._menuContent) {
- case 'templates':
- return this.templatesPreviewContents;
- case 'options':
- return this.optionsMenuContents;
- case 'saved':
- return this.savedLayoutsPreviewContents;
- case 'dashboard':
- return this.dashboardContents;
- default:
- return undefined;
- }
- }
-
- get resizePanes() {
- const ref = this._ref?.getBoundingClientRect();
- const height: number = ref?.height ?? 0;
- 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 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 left' onPointerDown={this.onResizePointerDown} style={{height: height, left: -7, top: 0}}/>,
- <div className='docCreatorMenu-resizer topRight' onPointerDown={this.onResizePointerDown} style={{left: width - 5, top: -10, cursor: 'nesw-resize'}}/>,
- <div className='docCreatorMenu-resizer topLeft' onPointerDown={this.onResizePointerDown} style={{left: -10, top: -10, cursor: 'nwse-resize'}}/>,
- <div className='docCreatorMenu-resizer bottomRight' onPointerDown={this.onResizePointerDown} style={{left: width - 5, top: height - 5, cursor: 'nwse-resize'}}/>,
- <div className='docCreatorMenu-resizer bottomLeft' onPointerDown={this.onResizePointerDown} style={{left: -10, top: height - 5, cursor: 'nesw-resize'}}/>
- ]; //prettier-ignore
- }
-
- render() {
- const topButton = (icon: string, opt: string, func: Function, tag: string) => {
- return (
- <div className={`top-button-container ${tag} ${opt === this._menuContent ? 'selected' : ''}`}>
- <div
- className="top-button-content"
- onPointerDown={e =>
- this.setUpButtonClick(e, () =>
- runInAction(() => {
- func();
- })
- )
- }>
- <FontAwesomeIcon icon={icon as any} />
- </div>
- </div>
- );
- };
-
- const onPreviewSelected = () => {
- this._menuContent = 'templates';
- };
- const onSavedSelected = () => {
- this._menuContent = 'dashboard';
- };
- const onOptionsSelected = () => {
- this._menuContent = 'options';
- if (!this._layout.columns) this._layout.columns = Math.ceil(Math.sqrt(this.docsToRender.length));
- };
-
- return (
- <div className="docCreatorMenu">
- {!this._shouldDisplay ? undefined : (
- <div
- className="docCreatorMenu-cont"
- ref={r => (this._ref = r)}
- style={{
- display: '',
- left: this._pageX,
- top: this._pageY,
- width: this._menuDimensions.width,
- height: this._menuDimensions.height,
- background: SnappingManager.userBackgroundColor,
- color: SnappingManager.userColor,
- }}>
- {this.resizePanes}
- <div
- className="docCreatorMenu-menu"
- onPointerDown={e =>
- setupMoveUpEvents(
- this,
- e,
- e => {
- 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);
- document.addEventListener('pointermove', this.onDrag);
- return true;
- },
- emptyFunction,
- undoable(clickEv => {
- clickEv.stopPropagation();
- }, 'drag menu')
- )
- }>
- <div className="docCreatorMenu-top-buttons-container">
- {topButton('table-cells', 'templates', onPreviewSelected, 'left')}
- {topButton('bars', 'options', onOptionsSelected, 'middle')}
- {topButton('floppy-disk', 'saved', onSavedSelected, 'right')}
- </div>
- <button className="docCreatorMenu-menu-button close-menu" onPointerDown={e => this.setUpButtonClick(e, this.closeMenu)}>
- <FontAwesomeIcon icon={'minus'} />
- </button>
- </div>
- {this.renderSelectedViewType}
- </div>
- )}
- </div>
- );
- }
-}
-
-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 enum TemplateFieldType {
- TEXT = 'text',
- VISUAL = 'visual',
- UNSET = 'unset',
-}
-
-export enum TemplateFieldSize {
- TINY = 'tiny',
- SMALL = 'small',
- MEDIUM = 'medium',
- LARGE = 'large',
- HUGE = 'huge',
-}
-
-export type Col = {
- sizes: TemplateFieldSize[];
- desc: string;
- title: string;
- type: TemplateFieldType;
- defaultContent?: string;
-};
-export interface FieldOpts {
- backgroundColor?: string;
- color?: string;
- cornerRounding?: number;
- borderWidth?: string;
- borderColor?: string;
- contentXCentering?: 'h-left' | 'h-center' | 'h-right';
- contentYCentering?: 'top' | 'center' | 'bottom';
- opacity?: number;
- rotation?: number;
- //animation?: boolean;
- fontBold?: boolean;
- fontTransform?: 'uppercase' | 'lowercase';
- fieldViewType?: 'freeform' | 'stacked';
-}
-
-type Field = {
- tl: [number, number];
- br: [number, number];
- opts: FieldOpts;
- subfields?: Field[];
- types?: TemplateFieldType[];
- sizes?: TemplateFieldSize[];
- isDecoration?: boolean;
- description?: string;
-};
-
-// class ContentField implements Field {
-// tl: [number, number];
-// br: [number, number];
-// opts: FieldOpts;
-// subfields?: Field[];
-// types?: TemplateFieldType[];
-// sizes?: TemplateFieldSize[];
-// description?: string;
-
-// constructor( tl: [number, number], br: [number, number],
-// opts: FieldOpts, subfields?: Field[],
-// types?: TemplateFieldType[],
-// sizes?: TemplateFieldSize[],
-// description?: string) {
-// this.tl = tl;
-// this.br = br;
-// this.opts = opts;
-// this.subfields = subfields;
-// this.types = types;
-// this.sizes = sizes;
-// this.description = description;
-// }
-
-// render = (content: any): Doc => {
-// return new Doc;
-// }
-// }
-
-type DecorationField = Field;
-
-type InkDecoration = {};
-
-type TemplateDecorations = Field | InkDecoration;
-
-interface TemplateOpts extends FieldOpts {}
-
-export interface TemplateDocInfos {
- title: string;
- height: number;
- width: number;
- opts: TemplateOpts;
- fields: Field[];
- decorations: Field[];
-}
-
-export class TemplateLayouts {
- public static get allTemplates(): TemplateDocInfos[] {
- return Object.values(TemplateLayouts).filter(value => typeof value === 'object' && value !== null && 'title' in value) as TemplateDocInfos[];
- }
-
- public static getTemplateByTitle = (title: string): TemplateDocInfos | undefined => {
- switch (title) {
- case 'fourfield1':
- return TemplateLayouts.FourField001;
- case 'fourfield2':
- return TemplateLayouts.FourField002;
- // case 'fourfield3':
- // return TemplateLayouts.FourField003;
- case 'fourfield4':
- return TemplateLayouts.FourField004;
- case 'threefield1':
- return TemplateLayouts.ThreeField001;
- case 'threefield2':
- return TemplateLayouts.ThreeField002;
- default:
- break;
- }
-
- return undefined;
- };
-
- public static FourField001: TemplateDocInfos = {
- title: 'fourfield1',
- width: 416,
- height: 700,
- opts: {
- backgroundColor: '#C0B887',
- cornerRounding: 20,
- borderColor: '#6B461F',
- borderWidth: '12',
- },
- fields: [
- {
- tl: [-0.95, -1],
- br: [0.95, -0.85],
- types: [TemplateFieldType.TEXT],
- sizes: [TemplateFieldSize.TINY],
- description: 'A title field for very short text that contextualizes the content.',
- opts: {
- backgroundColor: 'transparent',
- color: '#F1F0E9',
- contentXCentering: 'h-center',
- fontBold: true,
- },
- },
- {
- tl: [-0.87, -0.83],
- br: [0.87, 0.2],
- types: [TemplateFieldType.TEXT, TemplateFieldType.VISUAL],
- sizes: [TemplateFieldSize.MEDIUM, TemplateFieldSize.LARGE, TemplateFieldSize.HUGE],
- description: 'The main focus of the template; could be an image, long text, etc.',
- opts: {
- cornerRounding: 20,
- borderColor: '#8F5B25',
- borderWidth: '6',
- backgroundColor: '#CECAB9',
- },
- },
- {
- tl: [-0.8, 0.2],
- br: [0.8, 0.3],
- types: [TemplateFieldType.TEXT],
- sizes: [TemplateFieldSize.TINY, TemplateFieldSize.SMALL],
- description: 'A caption for field #2, very short to short text that contextualizes the content of field #2',
- opts: {
- backgroundColor: 'transparent',
- contentXCentering: 'h-center',
- color: '#F1F0E9',
- },
- },
- {
- tl: [-0.87, 0.37],
- br: [0.87, 0.96],
- types: [TemplateFieldType.TEXT, TemplateFieldType.VISUAL],
- sizes: [TemplateFieldSize.MEDIUM, TemplateFieldSize.LARGE, TemplateFieldSize.HUGE],
- description: 'A medium-sized field for medium/long text.',
- opts: {
- cornerRounding: 15,
- borderColor: '#8F5B25',
- borderWidth: '6',
- backgroundColor: '#CECAB9',
- },
- },
- ],
- decorations: [],
- };
-
- public static FourField002: TemplateDocInfos = {
- title: 'fourfield2',
- width: 425,
- height: 778,
- opts: {
- backgroundColor: '#242425',
- },
- fields: [
- {
- tl: [-0.83, -0.95],
- br: [0.83, -0.2],
- types: [TemplateFieldType.VISUAL, TemplateFieldType.TEXT],
- 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',
- borderColor: '#F8E71C',
- },
- },
- {
- tl: [-0.65, -0.2],
- br: [0.65, -0.02],
- types: [TemplateFieldType.TEXT],
- sizes: [TemplateFieldSize.TINY],
- description: 'A tiny field for just a word or two of plain text.',
- opts: {
- backgroundColor: 'transparent',
- color: 'white',
- contentXCentering: 'h-center',
- fontTransform: 'uppercase',
- },
- },
- {
- tl: [-0.65, 0],
- br: [0.65, 0.18],
- types: [TemplateFieldType.TEXT],
- sizes: [TemplateFieldSize.TINY],
- description: 'A tiny field for just a word or two of plain text.',
- opts: {
- backgroundColor: 'transparent',
- color: 'white',
- contentXCentering: 'h-center',
- fontTransform: 'uppercase',
- },
- },
- {
- tl: [-0.83, 0.2],
- br: [0.83, 0.95],
- types: [TemplateFieldType.TEXT, TemplateFieldType.VISUAL],
- sizes: [TemplateFieldSize.MEDIUM, TemplateFieldSize.LARGE, TemplateFieldSize.HUGE],
- description: 'A medium to large-sized field suitable for an image or longer text that should be the main focus, or share focus with field 1.',
- opts: {
- borderWidth: '8',
- borderColor: '#F8E71C',
- color: 'white',
- backgroundColor: '#242425',
- },
- },
- ],
- decorations: [
- {
- tl: [-0.8, -0.075],
- br: [-0.525, 0.075],
- opts: {
- backgroundColor: '#F8E71C',
- rotation: 45,
- },
- },
- {
- tl: [-0.3075, -0.0245],
- br: [-0.2175, 0.0245],
- opts: {
- backgroundColor: '#F8E71C',
- rotation: 45,
- },
- },
- {
- tl: [-0.045, -0.0245],
- br: [0.045, 0.0245],
- opts: {
- backgroundColor: '#F8E71C',
- rotation: 45,
- },
- },
- {
- tl: [0.2175, -0.0245],
- br: [0.3075, 0.0245],
- opts: {
- backgroundColor: '#F8E71C',
- rotation: 45,
- },
- },
- {
- tl: [0.525, -0.075],
- br: [0.8, 0.075],
- opts: {
- backgroundColor: '#F8E71C',
- rotation: 45,
- },
- },
- ],
- };
-
- // public static FourField003: TemplateDocInfos = {
- // title: 'fourfield3',
- // width: 477,
- // height: 662,
- // opts: {
- // backgroundColor: '#9E9C95'
- // },
- // fields: [{
- // tl: [-.875, -.9],
- // br: [.875, .7],
- // types: [TemplateFieldType.VISUAL],
- // sizes: [TemplateFieldSize.LARGE, TemplateFieldSize.HUGE],
- // description: '',
- // opts: {
- // borderWidth: '15',
- // borderColor: '#E0E0DA',
- // }
- // }, {
- // tl: [-.95, .8],
- // br: [-.1, .95],
- // types: [TemplateFieldType.TEXT],
- // sizes: [TemplateFieldSize.TINY, TemplateFieldSize.SMALL],
- // description: '',
- // opts: {
- // backgroundColor: 'transparent',
- // color: 'white',
- // contentXCentering: 'h-right',
- // }
- // }, {
- // tl: [.1, .8],
- // br: [.95, .95],
- // types: [TemplateFieldType.TEXT],
- // sizes: [TemplateFieldSize.TINY, TemplateFieldSize.SMALL],
- // description: '',
- // opts: {
- // backgroundColor: 'transparent',
- // color: 'red',
- // fontTransform: 'uppercase',
- // contentXCentering: 'h-left'
- // }
- // }, {
- // tl: [0, -.9],
- // br: [.85, -.66],
- // types: [TemplateFieldType.TEXT, TemplateFieldType.VISUAL],
- // sizes: [TemplateFieldSize.MEDIUM, TemplateFieldSize.LARGE, TemplateFieldSize.HUGE],
- // description: '',
- // opts: {
- // backgroundColor: 'transparent',
- // contentXCentering: 'h-right'
- // }
- // }],
- // decorations: [{
- // tl: [-.025, .8],
- // br: [.025, .95],
- // opts: {
- // backgroundColor: '#E0E0DA',
- // }
- // }]
- // };
-
- public static FourField004: TemplateDocInfos = {
- title: 'fourfield4',
- width: 414,
- height: 583,
- opts: {
- backgroundColor: '#6CCAF0',
- borderColor: '#1088C3',
- borderWidth: '10',
- },
- fields: [
- {
- tl: [-0.86, -0.92],
- br: [-0.075, -0.77],
- types: [TemplateFieldType.TEXT],
- sizes: [TemplateFieldSize.TINY],
- description: 'A tiny field for just a word or two of plain text.',
- opts: {
- backgroundColor: '#E2B4F5',
- borderWidth: '9',
- borderColor: '#9222F1',
- contentXCentering: 'h-center',
- },
- },
- {
- tl: [0.075, -0.92],
- br: [0.86, -0.77],
- types: [TemplateFieldType.TEXT],
- sizes: [TemplateFieldSize.TINY],
- description: 'A tiny field for just a word or two of plain text.',
- opts: {
- backgroundColor: '#F5B4DD',
- borderWidth: '9',
- borderColor: '#E260F3',
- contentXCentering: 'h-center',
- },
- },
- {
- tl: [-0.81, -0.64],
- br: [0.81, 0.48],
- types: [TemplateFieldType.VISUAL],
- 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',
- borderColor: '#A2BD77',
- },
- },
- {
- tl: [-0.86, 0.6],
- br: [0.86, 0.92],
- types: [TemplateFieldType.TEXT],
- sizes: [TemplateFieldSize.MEDIUM, TemplateFieldSize.LARGE],
- description: 'A medium to large field for text that describes the visual content above',
- opts: {
- borderWidth: '9',
- borderColor: '#F0D601',
- backgroundColor: '#F3F57D',
- },
- },
- ],
- decorations: [
- {
- tl: [-0.852, -0.67],
- br: [0.852, 0.51],
- opts: {
- backgroundColor: 'transparent',
- borderColor: '#007C0C',
- borderWidth: '10',
- },
- },
- ],
- };
-
- public static ThreeField001: TemplateDocInfos = {
- title: 'threefield1',
- width: 575,
- height: 770,
- opts: {
- backgroundColor: '#DDD3A9',
- },
- fields: [
- {
- tl: [-0.66, -0.747],
- br: [0.66, 0.247],
- types: [TemplateFieldType.VISUAL],
- sizes: [TemplateFieldSize.MEDIUM, TemplateFieldSize.LARGE, TemplateFieldSize.HUGE],
- description: 'A medium to large field for visual content that is the central focus.',
- opts: {
- borderColor: 'yellow',
- borderWidth: '8',
- rotation: 45,
- },
- },
- {
- tl: [-0.7, 0.2],
- br: [0.7, 0.46],
- types: [TemplateFieldType.TEXT],
- sizes: [TemplateFieldSize.TINY, TemplateFieldSize.SMALL],
- description: 'A very small text field for one to a few words. A good caption for the image.',
- opts: {
- backgroundColor: 'transparent',
- contentXCentering: 'h-center',
- },
- },
- {
- tl: [-0.95, 0.5],
- br: [0.95, 0.95],
- types: [TemplateFieldType.TEXT],
- sizes: [TemplateFieldSize.MEDIUM, TemplateFieldSize.LARGE],
- description: 'A medium to large text field for a thorough description of the image. ',
- opts: {
- backgroundColor: 'transparent',
- color: 'white',
- },
- },
- ],
- decorations: [
- {
- tl: [0.2, -1.32],
- br: [1.8, -0.66],
- opts: {
- backgroundColor: '#CEB155',
- rotation: 45,
- },
- },
- {
- tl: [-1.8, -1.32],
- br: [-0.2, -0.66],
- opts: {
- backgroundColor: '#CEB155',
- rotation: 135,
- },
- },
- {
- tl: [0.33, 0.75],
- br: [1.66, 1.25],
- opts: {
- backgroundColor: '#CEB155',
- rotation: 135,
- },
- },
- {
- tl: [-1.66, 0.75],
- br: [-0.33, 1.25],
- opts: {
- backgroundColor: '#CEB155',
- rotation: 45,
- },
- },
- ],
- };
-
- public static ThreeField002: TemplateDocInfos = {
- title: 'threefield2',
- width: 477,
- height: 662,
- opts: {
- backgroundColor: '#9E9C95',
- },
- fields: [
- {
- tl: [-0.875, -0.9],
- br: [0.875, 0.7],
- types: [TemplateFieldType.VISUAL],
- sizes: [TemplateFieldSize.MEDIUM, TemplateFieldSize.LARGE, TemplateFieldSize.HUGE],
- description: 'A medium to large visual field for the main content of the template',
- opts: {
- borderWidth: '15',
- borderColor: '#E0E0DA',
- },
- },
- {
- tl: [0.1, 0.775],
- br: [0.95, 0.975],
- types: [TemplateFieldType.TEXT],
- sizes: [TemplateFieldSize.TINY, TemplateFieldSize.SMALL],
- description: 'A very small text field for one to a few words. The content should represent a general categorization of the image.',
- opts: {
- backgroundColor: 'transparent',
- color: '#AF0D0D',
- fontTransform: 'uppercase',
- fontBold: true,
- contentXCentering: 'h-left',
- },
- },
- {
- tl: [-0.95, 0.775],
- br: [-0.1, 0.975],
- types: [TemplateFieldType.TEXT],
- sizes: [TemplateFieldSize.TINY, TemplateFieldSize.SMALL],
- description: 'A very small text field for one to a few words. The content should contextualize field 2.',
- opts: {
- backgroundColor: 'transparent',
- color: 'black',
- contentXCentering: 'h-right',
- },
- },
- ],
- decorations: [
- {
- tl: [-0.025, 0.8],
- br: [0.025, 0.95],
- opts: {
- backgroundColor: '#E0E0DA',
- },
- },
- ],
- };
-}
-
-export class FieldUtils {
- public static contentFields = (fields: Field[]) => {
- let toRet: Field[] = [];
- fields.forEach(field => {
- if (!field.isDecoration) {
- toRet.push(field);
- }
- toRet = toRet.concat(FieldUtils.contentFields(field.subfields ?? []));
- });
-
- return toRet;
- };
-
- public static calculateFontSize = (contWidth: number, contHeight: number, text: string, uppercase: boolean): number => {
- const words: string[] = text.split(/\s+/).filter(Boolean);
-
- let currFontSize = 1;
- let rowsCount = 1;
- let currTextHeight = currFontSize * rowsCount * 2;
-
- while (currTextHeight <= contHeight) {
- let wordIndex = 0;
- let currentRowWidth = 0;
- let wordsInCurrRow = 0;
- rowsCount = 1;
-
- while (wordIndex < words.length) {
- const word = words[wordIndex];
- const wordWidth = word.length * currFontSize * 0.5;
- //console.log(wordWidth)
-
- if (currentRowWidth + wordWidth <= contWidth) {
- currentRowWidth += wordWidth;
- ++wordsInCurrRow;
- } else {
- if (words.length !== 1 && words.length > wordsInCurrRow) {
- rowsCount++;
- currentRowWidth = wordWidth;
- wordsInCurrRow = 1;
- } else {
- break;
- }
- }
-
- wordIndex++;
- }
-
- currTextHeight = rowsCount * currFontSize * 2;
- //console.log(rowsCount, currFontSize, currTextHeight)
-
- currFontSize += 1;
- }
-
- return currFontSize - 1;
- };
-
- private static getDimensions = (coords: { tl: [number, number]; br: [number, number] }, parentWidth: number, parentHeight: number): { width: number; height: number; coord: { x: number; y: number } } => {
- const l = (coords.tl[0] * parentHeight) / 2;
- const t = coords.tl[1] * parentWidth / 2; //prettier-ignore
- const r = (coords.br[0] * parentHeight) / 2;
- const b = coords.br[1] * parentWidth / 2; //prettier-ignore
- const width = r - l;
- const height = b - t;
- const coord = { x: l, y: t };
- //console.log(coords, parentWidth, parentHeight, height);
- return { width, height, coord };
- };
-
- public static FreeformField = (coords: { tl: [number, number]; br: [number, number] }, parentWidth: number, parentHeight: number, title: string, content: string, opts: FieldOpts) => {
- const { width, height, coord } = FieldUtils.getDimensions(coords, parentWidth, parentHeight);
-
- const docWithBasicOpts = Docs.Create.FreeformDocument([], {
- isDefaultTemplateDoc: true,
- _height: height,
- _width: width,
- title: title,
- x: coord.x,
- y: coord.y,
- backgroundColor: opts.backgroundColor ?? '',
- _layout_borderRounding: `${opts.cornerRounding ?? 0}px`,
- borderColor: opts.borderColor,
- borderWidth: opts.borderWidth,
- opacity: opts.opacity,
- hCentering: opts.contentXCentering,
- _rotation: opts.rotation,
- });
-
- return docWithBasicOpts;
- };
-
- public static TextField = (coords: { tl: [number, number]; br: [number, number] }, parentWidth: number, parentHeight: number, title: string, content: string, opts: FieldOpts) => {
- const { width, height, coord } = FieldUtils.getDimensions(coords, parentWidth, parentHeight);
-
- const docWithBasicOpts = Docs.Create.TextDocument(content, {
- isDefaultTemplateDoc: true,
- _height: height,
- _width: width,
- title: title,
- x: coord.x,
- y: coord.y,
- text_fontSize: `${FieldUtils.calculateFontSize(width, height, content, true)}`,
- backgroundColor: opts.backgroundColor ?? '',
- text_fontColor: opts.color,
- contentBold: opts.fontBold,
- text_transform: opts.fontTransform,
- color: opts.color,
- _layout_borderRounding: `${opts.cornerRounding ?? 0}px`,
- borderColor: opts.borderColor,
- borderWidth: opts.borderWidth,
- opacity: opts.opacity,
- hCentering: opts.contentXCentering,
- _rotation: opts.rotation,
- });
-
- docWithBasicOpts._layout_hideScroll = true;
-
- return docWithBasicOpts;
- };
-
- public static ImageField = (coords: { tl: [number, number]; br: [number, number] }, parentWidth: number, parentHeight: number, title: string, content: string, opts: FieldOpts) => {
- const { width, height, coord } = FieldUtils.getDimensions(coords, parentWidth, parentHeight);
-
- const doc = Docs.Create.ImageDocument(content, {
- isDefaultTemplateDoc: true,
- _height: height,
- _width: width,
- title: title,
- x: coord.x,
- y: coord.y,
- _layout_fitWidth: false,
- backgroundColor: opts.backgroundColor ?? '',
- _layout_borderRounding: `${opts.cornerRounding ?? 0}px`,
- borderColor: opts.borderColor,
- borderWidth: opts.borderWidth,
- opacity: opts.opacity,
- _rotation: opts.rotation,
- });
-
- //setTimeout(() => {doc._height = height; doc._width = width}, 10);
-
- return doc;
- };
-
- public static CarouselField = (coords: { tl: [number, number]; br: [number, number] }, parentWidth: number, parentHeight: number, title: string, fields: Doc[]) => {
- const { width, height, coord } = FieldUtils.getDimensions(coords, parentWidth, parentHeight);
-
- const doc = Docs.Create.Carousel3DDocument(fields, { _height: height, _width: width, title: title, x: coord.x, y: coord.y, text_fontSize: `${height / 2}` });
-
- return doc;
- };
-}
-
-// public static FourField002: TemplateDocInfos = {
-// width: 450,
-// height: 600,
-// fields: [{
-// tl: [-.6, -.9],
-// br: [.6, -.8],
-// types: [FieldType.TEXT],
-// sizes: [FieldSize.TINY]
-// }, {
-// tl: [-.9, -.7],
-// br: [.9, .2],
-// types: [FieldType.TEXT, FieldType.VISUAL],
-// sizes: [FieldSize.MEDIUM, FieldSize.LARGE, FieldSize.HUGE]
-// }, {
-// tl: [-.9, .3],
-// br: [-.05, .9],
-// types: [FieldType.TEXT],
-// sizes: [FieldSize.TINY]
-// }, {
-// tl: [.05, .3],
-// br: [.9, .9],
-// types: [FieldType.TEXT, FieldType.VISUAL],
-// sizes: [FieldSize.MEDIUM, FieldSize.LARGE, FieldSize.HUGE]
-// }]
-// };
-
-// public static TwoFieldPlusCarousel: TemplateDocInfos = {
-// width: 500,
-// height: 600,
-// fields: [{
-// tl: [-.9, -.99],
-// br: [.9, -.7],
-// types: [FieldType.TEXT],
-// sizes: [FieldSize.TINY]
-// }, {
-// tl: [-.9, -.65],
-// br: [.9, .35],
-// types: [],
-// sizes: []
-// }, {
-// tl: [-.9, .4],
-// br: [.9, .95],
-// types: [FieldType.TEXT],
-// sizes: [FieldSize.TINY]
-// }]
-// };
-// }