diff options
Diffstat (limited to 'src')
8 files changed, 215 insertions, 420 deletions
diff --git a/src/client/views/nodes/DataVizBox/DocCreatorMenu/DocCreatorMenu.tsx b/src/client/views/nodes/DataVizBox/DocCreatorMenu/DocCreatorMenu.tsx index 02fd7f869..36264f886 100644 --- a/src/client/views/nodes/DataVizBox/DocCreatorMenu/DocCreatorMenu.tsx +++ b/src/client/views/nodes/DataVizBox/DocCreatorMenu/DocCreatorMenu.tsx @@ -29,8 +29,10 @@ import './DocCreatorMenu.scss'; import { DefaultStyleProvider, returnEmptyDocViewList } from '../../../StyleProvider'; import { Transform } from '../../../../util/Transform'; import { IconProp } from '@fortawesome/fontawesome-svg-core'; -import { TemplateDocInfos, TemplateFieldSize, TemplateFieldType, TemplateLayouts } from './TemplateBackend'; -import { FieldOpts, FieldSettings } from './FieldTypes/StaticField'; +import { TemplateFieldSize, TemplateFieldType, TemplateLayouts } from './TemplateBackend'; +import { TemplateManager } from './TemplateManager'; +import { Template } from './Template'; +import { Field, FieldContentType } from './FieldTypes/Field'; export enum LayoutType { STACKED = 'stacked', @@ -49,6 +51,8 @@ export class DocCreatorMenu extends ObservableReactComponent<FieldViewProps> { private _ref: HTMLDivElement | null = null; + private templateManager: TemplateManager; + @observable _fullyRenderedDocs: Doc[] = []; @observable _renderedDocCollectionPreview: Doc | undefined = undefined; @observable _renderedDocCollection: Doc | undefined = undefined; @@ -99,6 +103,7 @@ export class DocCreatorMenu extends ObservableReactComponent<FieldViewProps> { super(props); makeObservable(this); DocCreatorMenu.Instance = this; + this.templateManager = new TemplateManager(TemplateLayouts.allTemplates); //setTimeout(() => this.generateTemplates('')); } @@ -562,79 +567,13 @@ export class DocCreatorMenu extends ObservableReactComponent<FieldViewProps> { } }; - matchesForTemplate = (template: TemplateDocInfos, cols: Col[]): number[][] => { - const colMatchesField = (col: Col, field: FieldSettings) => { - 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; - }; - /** * 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> => { + fillPresetTemplate = async (template: Template, assignments: { [field: string]: Col }): Promise<Doc> => { const wordLimit = (size: TemplateFieldSize) => { switch (size) { case TemplateFieldSize.TINY: @@ -665,22 +604,11 @@ export class DocCreatorMenu extends ObservableReactComponent<FieldViewProps> { 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: FieldSettings = template.fields[Number(info.number)]; + const field: Field = template.getFieldByID(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); + field.setContent(info.content ?? '', FieldContentType.STRING); + field.setTitle(col.title); }); } } catch (err) { @@ -691,22 +619,12 @@ export class DocCreatorMenu extends ObservableReactComponent<FieldViewProps> { return rendered; }; - const createGeneratedImage = async (fieldNum: string, col: Col, prompt: string) => { + const generateAndLoadImage = async (fieldNum: string, col: Col, prompt: string) => { const url = await this.generateGPTImage(prompt); - const field: FieldSettings = template.fields[Number(fieldNum)]; - const doc = FieldUtils.ImageField( - { - tl: field.tl, - br: field.br, - }, - template.height, - template.width, - col.title, - url ?? '', - field.opts - ); + const field: Field = template.getFieldByID(Number(fieldNum)); - return doc; + field.setContent(url ?? '', FieldContentType.IMAGE); + field.setTitle(col.title); }; const renderImageCalls = async (): Promise<Doc[]> => { @@ -715,7 +633,7 @@ export class DocCreatorMenu extends ObservableReactComponent<FieldViewProps> { if (calls.length) { try { - const renderedImages: Doc[] = await Promise.all( + 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: ' + @@ -724,14 +642,10 @@ export class DocCreatorMenu extends ObservableReactComponent<FieldViewProps> { col.desc; const prompt = await gptAPICall(sysPrompt, GPTCallType.COMPLETEPROMPT); - //console.log(sysPrompt, prompt); - return createGeneratedImage(fieldNum, col, prompt); + generateAndLoadImage(fieldNum, col, prompt); }) ); - - const renderedTemplates: Doc[] = await Promise.all(renderedImages); - renderedTemplates.forEach(doc => rendered.push(doc)); } catch (e) { console.log(e); } @@ -740,12 +654,12 @@ export class DocCreatorMenu extends ObservableReactComponent<FieldViewProps> { return rendered; }; - const fields: Doc[] = []; + const fields: Field[] = []; - 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 autoLoadedFields = Object.entries(assignments).filter(([f, col]) => this._columns.includes(col)); + const userCreatedFields: [string, Col][] = Object.entries(assignments).filter(a => !autoLoadedFields.includes(a)); + const GPTTextCalls = autoLoadedFields.filter(([str, col]) => col.type === TemplateFieldType.TEXT); + const GPTIMGCalls = autoLoadedFields.filter(([str, col]) => col.type === TemplateFieldType.VISUAL); const stringifyGPTInfo = (calls: [string, Col][]): string => { let string: string = '*** COLUMN INFO:'; @@ -759,9 +673,9 @@ export class DocCreatorMenu extends ObservableReactComponent<FieldViewProps> { let fieldContent: string = ''; - Object.entries(nonGPTAssignments).forEach(([f, strCol]) => { - const field: FieldSettings = template.fields[Number(f)]; - const col = strCol[1]; + Object.entries(userCreatedFields).forEach(([fieldID, strAndCol]) => { + const field: Field = template.getFieldByID(Number(fieldID)); + const col = strAndCol[1]; const doc = (col.type === TemplateFieldType.VISUAL ? FieldUtils.ImageField : FieldUtils.TextField)( { @@ -775,7 +689,7 @@ export class DocCreatorMenu extends ObservableReactComponent<FieldViewProps> { field.opts ); - fieldContent += `--- Field #${f} (title: ${col.title}): ${col.defaultContent ?? ''} ---`; + fieldContent += `--- Field #${fieldID} (title: ${col.title}): ${col.defaultContent ?? ''} ---`; fields.push(doc); }); @@ -829,7 +743,7 @@ export class DocCreatorMenu extends ObservableReactComponent<FieldViewProps> { return createMainDoc(); }; - compileFieldDescriptions = (templates: TemplateDocInfos[]): string => { + compileFieldDescriptions = (templates: Template[]): string => { let descriptions: string = ''; templates.forEach(template => { descriptions += `---------- NEW TEMPLATE TO INCLUDE: Description of template ${template.title}'s fields: `; @@ -898,7 +812,7 @@ export class DocCreatorMenu extends ObservableReactComponent<FieldViewProps> { this._dataViz?.updateColDefaults(); const cols = this.fieldsInfos; - const templates = this.findValidTemplates(cols, TemplateLayouts.allTemplates); + const templates = this.templateManager.getValidTemplates(cols); const assignments: [TemplateDocInfos, { [field: number]: Col }][] = await this.assignColsToFields(templates, cols); @@ -1668,151 +1582,3 @@ export type Col = { type: TemplateFieldType; defaultContent?: string; }; - -type DecorationField = FieldSettings; - -type InkDecoration = {}; - -type TemplateDecorations = FieldSettings | InkDecoration; - -interface TemplateOpts extends FieldOpts {} - -export class FieldUtils { - 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, - textTransform: 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; - }; -} diff --git a/src/client/views/nodes/DataVizBox/DocCreatorMenu/FieldTypes/DynamicField.tsx b/src/client/views/nodes/DataVizBox/DocCreatorMenu/FieldTypes/DynamicField.tsx index 361476446..a55f44589 100644 --- a/src/client/views/nodes/DataVizBox/DocCreatorMenu/FieldTypes/DynamicField.tsx +++ b/src/client/views/nodes/DataVizBox/DocCreatorMenu/FieldTypes/DynamicField.tsx @@ -2,7 +2,7 @@ import { Doc } from "../../../../../../fields/Doc"; import { Docs } from "../../../../../documents/Documents"; import { Col } from "../DocCreatorMenu"; import { TemplateLayouts } from "../TemplateBackend"; -import { Field, FieldDimensions, FieldSettings, ViewType } from "./Field"; +import { Field, FieldContentType, FieldDimensions, FieldSettings, ViewType } from "./Field"; import { FieldUtils } from "./FieldUtils"; import { StaticField } from "./StaticField"; @@ -12,6 +12,7 @@ export class DynamicField implements Field { private id: number; private settings: FieldSettings; + private title: string = ''; private parent: Field; private dimensions: FieldDimensions; @@ -30,9 +31,12 @@ export class DynamicField implements Field { this.subfields = this.setupSubfields(); } - setContent = (content: string) => { return }; + setContent = (content: string, type: FieldContentType) => { return }; getContent = () => { return '' }; + setTitle = (title: string) => { this.title = title }; + getTitle = () => { return this.title }; + get getSubfields() { return this.subfields }; get getAllSubfields() { const fields: Field[] = []; @@ -45,15 +49,16 @@ export class DynamicField implements Field { get getDimensions() { return this.dimensions }; get getID() { return this.id }; + get getViewType() { return this.type }; - matches = () => { + matches = (cols: Col[]): Array<number> => { return []; } setupSubfields = (): Field[] => { this.settings.subfields?.forEach(fieldSettings => { let field: Field; - const dynamicType = fieldSettings.dynamicType; + const dynamicType = fieldSettings.viewType; if (dynamicType) { field = new DynamicField(fieldSettings, dynamicType, 0, this); } @@ -78,10 +83,12 @@ export class DynamicField implements Field { switch (this.type) { case ViewType.CAROUSEL3D: const carouselDoc = Docs.Create.Carousel3DDocument([], { + title: this.title, }); FieldUtils.applyBasicOpts(carouselDoc, this.dimensions, this.settings); case ViewType.FREEFORM: const freeformDoc = Docs.Create.FreeformDocument([], { + title: this.title, }); FieldUtils.applyBasicOpts(freeformDoc, this.dimensions, this.settings); default: diff --git a/src/client/views/nodes/DataVizBox/DocCreatorMenu/FieldTypes/Field.tsx b/src/client/views/nodes/DataVizBox/DocCreatorMenu/FieldTypes/Field.tsx index 0c1b6e5fc..aa1d1de33 100644 --- a/src/client/views/nodes/DataVizBox/DocCreatorMenu/FieldTypes/Field.tsx +++ b/src/client/views/nodes/DataVizBox/DocCreatorMenu/FieldTypes/Field.tsx @@ -1,23 +1,27 @@ import { Doc } from "../../../../../../fields/Doc"; +import { Col } from "../DocCreatorMenu"; import { TemplateFieldSize, TemplateFieldType } from "../TemplateBackend"; export interface Field { getContent: () => string; - setContent: (content: string) => void; + setContent: (content: string, type: FieldContentType) => void; getDimensions: FieldDimensions; getSubfields: Field[]; getAllSubfields: Field[]; getID: number; + getViewType: ViewType; + getTitle: () => string; + setTitle: (title: string) => void; setupSubfields: () => Field[]; renderedDoc: (content: string) => Doc; - matches: () => number[][]; + matches: (cols: Col[]) => number[]; } export type FieldSettings = { tl: [number, number]; br: [number, number]; opts: FieldOpts; - dynamicType?: ViewType; + viewType: ViewType; subfields?: FieldSettings[]; types?: TemplateFieldType[]; sizes?: TemplateFieldSize[]; @@ -32,6 +36,8 @@ export enum FieldContentType { export enum ViewType { CAROUSEL3D = 'carousel3d', FREEFORM = 'freeform', + STATIC = 'static', + DEC = 'decoration' } export type FieldDimensions = { diff --git a/src/client/views/nodes/DataVizBox/DocCreatorMenu/FieldTypes/FieldUtils.tsx b/src/client/views/nodes/DataVizBox/DocCreatorMenu/FieldTypes/FieldUtils.tsx index ead46d721..c389704cf 100644 --- a/src/client/views/nodes/DataVizBox/DocCreatorMenu/FieldTypes/FieldUtils.tsx +++ b/src/client/views/nodes/DataVizBox/DocCreatorMenu/FieldTypes/FieldUtils.tsx @@ -71,74 +71,4 @@ export class FieldUtils { return currFontSize - 1; }; -} - -export class TemplateMatcher { - - static matchesForTemplate = (field: FieldSettings, cols: Col[]): number[][] => { - const colMatchesField = (col: Col, field: FieldSettings) => { - return field.sizes?.some(size => col.sizes?.includes(size)) && field.types?.includes(col.type); - }; - - const matches: number[][] = Array(field.subfields?.length) - .fill([]) - .map(() => []); - - field.subfields?.forEach((field, i) => { - cols.forEach((col, v) => { - if (colMatchesField(col, field)) { - matches[i].push(v); - } - }); - }); - - return matches; - }; - - static 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: FieldSettings[]) => { - let validTemplates: any[] = []; - templates.forEach(template => { - const numFields = template.subfields?.length; - if (!(numFields === cols.length)) return; - const matches = TemplateMatcher.matchesForTemplate(template, cols); - if (TemplateMatcher.maxMatches(numFields, matches) === numFields) { - validTemplates.push(''); - } - }); - - validTemplates = validTemplates.map(title => TemplateLayouts.getTemplateByTitle(title)); - - return validTemplates; - }; - }
\ No newline at end of file diff --git a/src/client/views/nodes/DataVizBox/DocCreatorMenu/FieldTypes/StaticField.tsx b/src/client/views/nodes/DataVizBox/DocCreatorMenu/FieldTypes/StaticField.tsx index efceae65a..cd1008429 100644 --- a/src/client/views/nodes/DataVizBox/DocCreatorMenu/FieldTypes/StaticField.tsx +++ b/src/client/views/nodes/DataVizBox/DocCreatorMenu/FieldTypes/StaticField.tsx @@ -4,10 +4,9 @@ import { FieldType } from "../../../../../../fields/ObjectField"; import { Docs, DocumentOptions } from "../../../../../documents/Documents"; import { Col } from "../DocCreatorMenu"; import { Template } from "../Template"; -import { TemplateDocInfos, TemplateFieldSize, TemplateFieldType, TemplateLayouts } from "../TemplateBackend"; import { DynamicField } from "./DynamicField"; import { FieldUtils } from "./FieldUtils"; -import { Field, FieldContentType, FieldDimensions, FieldSettings } from "./Field"; +import { Field, FieldContentType, FieldDimensions, FieldSettings, ViewType } from "./Field"; export class StaticField { private content: string; @@ -15,6 +14,7 @@ export class StaticField { private subfields: Field[] = []; private id: number; + private title: string = ''; private settings: FieldSettings; @@ -36,36 +36,55 @@ export class StaticField { const fields: Field[] = []; this.subfields?.forEach(field => { fields.push(field); - fields.concat(field.getAllSubfields) + fields.concat(field.getAllSubfields); }); return fields; }; get getDimensions() { return this.dimensions }; get getID() { return this.id }; + get getViewType() { return ViewType.STATIC }; - setContent = (newContent: string) => { this.content = newContent }; + setContent = (newContent: string, type: FieldContentType) => { + this.content = newContent; + this.contentType = type; + }; getContent() { return this.content }; + setTitle = (title: string) => { this.title = title }; + getTitle = () => { return this.title }; + setupSubfields = (): Field[] => { const fields: Field[] = []; this.settings.subfields?.forEach(fieldSettings => { let field: Field; - const dynamicType = fieldSettings.dynamicType; + const dynamicType = fieldSettings.viewType; if (dynamicType) { field = new DynamicField(fieldSettings, dynamicType, 0, this); } else { field = new StaticField(fieldSettings, this, 0); - } + }; fields.push(field); }); return fields; }; - matches = (): number[][] => { - return []; + matches = (cols: Col[]): number[] => { + const colMatchesField = (col: Col) => { + return this.settings.sizes?.some(size => col.sizes?.includes(size)) && this.settings.types?.includes(col.type); + }; + + const matches: Array<number> = []; + + cols.forEach((col, v) => { + if (colMatchesField(col)) { + matches.push(v); + } + }); + + return matches; }; renderedDoc = (content: string): Doc => { @@ -77,6 +96,7 @@ export class StaticField { case FieldContentType.STRING: const text = String(content); const textDoc = Docs.Create.TextDocument(text, { + title: this.title, text_fontColor: opts.color, contentBold: opts.fontBold, textTransform: opts.fontTransform, @@ -88,6 +108,7 @@ export class StaticField { case FieldContentType.IMAGE: const url = String(content); const imgDoc = Docs.Create.ImageDocument(url, { + title: this.title, _layout_fitWidth: false, }); FieldUtils.applyBasicOpts(imgDoc, this.dimensions, this.settings); diff --git a/src/client/views/nodes/DataVizBox/DocCreatorMenu/Template.tsx b/src/client/views/nodes/DataVizBox/DocCreatorMenu/Template.tsx index f05f61b61..486fe7f7e 100644 --- a/src/client/views/nodes/DataVizBox/DocCreatorMenu/Template.tsx +++ b/src/client/views/nodes/DataVizBox/DocCreatorMenu/Template.tsx @@ -1,11 +1,11 @@ import { Doc } from "../../../../../fields/Doc"; import { Docs } from "../../../../documents/Documents"; +import { Col } from "./DocCreatorMenu"; import { DynamicField } from "./FieldTypes/DynamicField"; import { Field, FieldSettings, ViewType } from "./FieldTypes/Field"; import { } from "./FieldTypes/FieldUtils"; import { } from "./FieldTypes/StaticField"; -import { TemplateDocInfos } from "./TemplateBackend"; export class Template { @@ -17,9 +17,61 @@ export class Template { get childFields(): Field[] { return this.mainField.getSubfields }; get allFields(): Field[] { return this.mainField.getAllSubfields }; + get contentFields(): Field[] { return this.allFields.filter(field => field.getViewType === ViewType.STATIC) }; + + getFieldByID = (id: number): Field => { + return this.allFields.filter(field => field.getID === id)[0]; + } setupMainField = (templateInfo: FieldSettings) => { return new DynamicField(templateInfo, ViewType.FREEFORM, 0); } + isValidTemplate = (cols: Col[]) => { + return this.maxMatches(this.getMatches(cols)) === this.contentFields.length; + } + + getMatches = (cols: Col[]) => { + const matches: number[][] = Array(this.contentFields.length) + .fill([]) + .map(() => []); + + this.contentFields.forEach((field, i) => { + matches[i].concat(field.matches(cols)); + }); + + return matches; + } + + maxMatches = (matches: number[][]) => { + const fieldsCt = this.contentFields.length; + 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; + }; + }
\ No newline at end of file diff --git a/src/client/views/nodes/DataVizBox/DocCreatorMenu/TemplateBackend.tsx b/src/client/views/nodes/DataVizBox/DocCreatorMenu/TemplateBackend.tsx index 43b0b1572..00eb8fab4 100644 --- a/src/client/views/nodes/DataVizBox/DocCreatorMenu/TemplateBackend.tsx +++ b/src/client/views/nodes/DataVizBox/DocCreatorMenu/TemplateBackend.tsx @@ -1,4 +1,4 @@ -import { FieldOpts, FieldSettings } from "./FieldTypes/Field"; +import { FieldOpts, FieldSettings, ViewType } from "./FieldTypes/Field"; import { } from "./FieldTypes/StaticField"; export enum TemplateFieldType { @@ -15,53 +15,24 @@ export enum TemplateFieldSize { HUGE = 'huge', } -export interface TemplateDocInfos { - title: string; - height: number; - width: number; - opts: FieldOpts; - fields: FieldSettings[]; - decorations: FieldSettings[]; -} - 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 get allTemplates(): FieldSettings[] { + return Object.values(TemplateLayouts).filter(value => typeof value === 'object' && value !== null && 'title' in value) as FieldSettings[]; } - 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, + public static FourField001: FieldSettings = { + tl: [0, 0], + br: [416, 700], + viewType: ViewType.FREEFORM, opts: { backgroundColor: '#C0B887', cornerRounding: 20, borderColor: '#6B461F', borderWidth: '12', }, - fields: [ + subfields: [ { + viewType: ViewType.STATIC, tl: [-0.95, -1], br: [0.95, -0.85], types: [TemplateFieldType.TEXT], @@ -75,6 +46,7 @@ export class TemplateLayouts { }, }, { + viewType: ViewType.STATIC, tl: [-0.87, -0.83], br: [0.87, 0.2], types: [TemplateFieldType.TEXT, TemplateFieldType.VISUAL], @@ -88,6 +60,7 @@ export class TemplateLayouts { }, }, { + viewType: ViewType.STATIC, tl: [-0.8, 0.2], br: [0.8, 0.3], types: [TemplateFieldType.TEXT], @@ -100,6 +73,7 @@ export class TemplateLayouts { }, }, { + viewType: ViewType.STATIC, tl: [-0.87, 0.37], br: [0.87, 0.96], types: [TemplateFieldType.TEXT, TemplateFieldType.VISUAL], @@ -113,18 +87,18 @@ export class TemplateLayouts { }, }, ], - decorations: [], }; - public static FourField002: TemplateDocInfos = { - title: 'fourfield2', - width: 425, - height: 778, + public static FourField002: FieldSettings = { + viewType: ViewType.FREEFORM, + tl: [0,0], + br: [425, 778], opts: { backgroundColor: '#242425', }, - fields: [ + subfields: [ { + viewType: ViewType.STATIC, tl: [-0.83, -0.95], br: [0.83, -0.2], types: [TemplateFieldType.VISUAL, TemplateFieldType.TEXT], @@ -136,6 +110,7 @@ export class TemplateLayouts { }, }, { + viewType: ViewType.STATIC, tl: [-0.65, -0.2], br: [0.65, -0.02], types: [TemplateFieldType.TEXT], @@ -149,6 +124,7 @@ export class TemplateLayouts { }, }, { + viewType: ViewType.STATIC, tl: [-0.65, 0], br: [0.65, 0.18], types: [TemplateFieldType.TEXT], @@ -162,6 +138,7 @@ export class TemplateLayouts { }, }, { + viewType: ViewType.STATIC, tl: [-0.83, 0.2], br: [0.83, 0.95], types: [TemplateFieldType.TEXT, TemplateFieldType.VISUAL], @@ -174,46 +151,49 @@ export class TemplateLayouts { backgroundColor: '#242425', }, }, - ], - decorations: [ { + viewType: ViewType.DEC, tl: [-0.8, -0.075], br: [-0.525, 0.075], opts: { backgroundColor: '#F8E71C', - //rotation: 45, + rotation: 45, }, }, { + viewType: ViewType.DEC, tl: [-0.3075, -0.0245], br: [-0.2175, 0.0245], opts: { backgroundColor: '#F8E71C', - //rotation: 45, + rotation: 45, }, }, { + viewType: ViewType.DEC, tl: [-0.045, -0.0245], br: [0.045, 0.0245], opts: { backgroundColor: '#F8E71C', - //rotation: 45, + rotation: 45, }, }, { + viewType: ViewType.DEC, tl: [0.2175, -0.0245], br: [0.3075, 0.0245], opts: { backgroundColor: '#F8E71C', - //rotation: 45, + rotation: 45, }, }, { + viewType: ViewType.DEC, tl: [0.525, -0.075], br: [0.8, 0.075], opts: { backgroundColor: '#F8E71C', - //rotation: 45, + rotation: 45, }, }, ], @@ -279,17 +259,18 @@ export class TemplateLayouts { // }] // }; - public static FourField004: TemplateDocInfos = { - title: 'fourfield4', - width: 414, - height: 583, + public static FourField004: FieldSettings = { + viewType: ViewType.FREEFORM, + tl: [0,0], + br: [414,583], opts: { backgroundColor: '#6CCAF0', borderColor: '#1088C3', borderWidth: '10', }, - fields: [ + subfields: [ { + viewType: ViewType.STATIC, tl: [-0.86, -0.92], br: [-0.075, -0.77], types: [TemplateFieldType.TEXT], @@ -303,6 +284,7 @@ export class TemplateLayouts { }, }, { + viewType: ViewType.STATIC, tl: [0.075, -0.92], br: [0.86, -0.77], types: [TemplateFieldType.TEXT], @@ -316,6 +298,7 @@ export class TemplateLayouts { }, }, { + viewType: ViewType.STATIC, tl: [-0.81, -0.64], br: [0.81, 0.48], types: [TemplateFieldType.VISUAL], @@ -327,6 +310,7 @@ export class TemplateLayouts { }, }, { + viewType: ViewType.STATIC, tl: [-0.86, 0.6], br: [0.86, 0.92], types: [TemplateFieldType.TEXT], @@ -338,9 +322,8 @@ export class TemplateLayouts { backgroundColor: '#F3F57D', }, }, - ], - decorations: [ { + viewType: ViewType.DEC, tl: [-0.852, -0.67], br: [0.852, 0.51], opts: { @@ -352,15 +335,16 @@ export class TemplateLayouts { ], }; - public static ThreeField001: TemplateDocInfos = { - title: 'threefield1', - width: 575, - height: 770, + public static ThreeField001: FieldSettings = { + viewType: ViewType.FREEFORM, + tl: [0,0], + br: [575, 770], opts: { backgroundColor: '#DDD3A9', }, - fields: [ + subfields: [ { + viewType: ViewType.STATIC, tl: [-0.66, -0.747], br: [0.66, 0.247], types: [TemplateFieldType.VISUAL], @@ -373,6 +357,7 @@ export class TemplateLayouts { }, }, { + viewType: ViewType.STATIC, tl: [-0.7, 0.2], br: [0.7, 0.46], types: [TemplateFieldType.TEXT], @@ -384,6 +369,7 @@ export class TemplateLayouts { }, }, { + viewType: ViewType.STATIC, tl: [-0.95, 0.5], br: [0.95, 0.95], types: [TemplateFieldType.TEXT], @@ -394,9 +380,8 @@ export class TemplateLayouts { color: 'white', }, }, - ], - decorations: [ { + viewType: ViewType.DEC, tl: [0.2, -1.32], br: [1.8, -0.66], opts: { @@ -405,6 +390,7 @@ export class TemplateLayouts { }, }, { + viewType: ViewType.DEC, tl: [-1.8, -1.32], br: [-0.2, -0.66], opts: { @@ -413,6 +399,7 @@ export class TemplateLayouts { }, }, { + viewType: ViewType.DEC, tl: [0.33, 0.75], br: [1.66, 1.25], opts: { @@ -421,6 +408,7 @@ export class TemplateLayouts { }, }, { + viewType: ViewType.DEC, tl: [-1.66, 0.75], br: [-0.33, 1.25], opts: { @@ -431,15 +419,16 @@ export class TemplateLayouts { ], }; - public static ThreeField002: TemplateDocInfos = { - title: 'threefield2', - width: 477, - height: 662, + public static ThreeField002: FieldSettings = { + viewType: ViewType.FREEFORM, + tl: [0,0], + br: [477, 662], opts: { backgroundColor: '#9E9C95', }, - fields: [ + subfields: [ { + viewType: ViewType.STATIC, tl: [-0.875, -0.9], br: [0.875, 0.7], types: [TemplateFieldType.VISUAL], @@ -451,6 +440,7 @@ export class TemplateLayouts { }, }, { + viewType: ViewType.STATIC, tl: [0.1, 0.775], br: [0.95, 0.975], types: [TemplateFieldType.TEXT], @@ -465,6 +455,7 @@ export class TemplateLayouts { }, }, { + viewType: ViewType.STATIC, tl: [-0.95, 0.775], br: [-0.1, 0.975], types: [TemplateFieldType.TEXT], @@ -476,9 +467,8 @@ export class TemplateLayouts { contentXCentering: 'h-right', }, }, - ], - decorations: [ { + viewType: ViewType.DEC, tl: [-0.025, 0.8], br: [0.025, 0.95], opts: { diff --git a/src/client/views/nodes/DataVizBox/DocCreatorMenu/TemplateManager.tsx b/src/client/views/nodes/DataVizBox/DocCreatorMenu/TemplateManager.tsx new file mode 100644 index 000000000..890bc6f73 --- /dev/null +++ b/src/client/views/nodes/DataVizBox/DocCreatorMenu/TemplateManager.tsx @@ -0,0 +1,23 @@ +import { Col } from "./DocCreatorMenu"; +import { FieldSettings } from "./FieldTypes/Field"; +import { Template } from "./Template"; +import { TemplateLayouts } from "./TemplateBackend"; + +export class TemplateManager { + + templates: Template[] = []; + + constructor(templateSettings: FieldSettings[]) { + this.templates = this.initializeTemplates(templateSettings); + } + + initializeTemplates = (templateSettings: FieldSettings[]): Template[] => { + const initializedTemplates: Template[] = []; + templateSettings.forEach(settings => initializedTemplates.push(new Template(settings))); + return initializedTemplates; + } + + getValidTemplates = (cols: Col[]): Template[] => { + return this.templates.filter(template => template.isValidTemplate(cols)); + } +}
\ No newline at end of file |