diff options
Diffstat (limited to 'src/client/views/nodes/DataVizBox/DocCreatorMenu/Template.ts')
-rw-r--r-- | src/client/views/nodes/DataVizBox/DocCreatorMenu/Template.ts | 220 |
1 files changed, 220 insertions, 0 deletions
diff --git a/src/client/views/nodes/DataVizBox/DocCreatorMenu/Template.ts b/src/client/views/nodes/DataVizBox/DocCreatorMenu/Template.ts new file mode 100644 index 000000000..fd87ae973 --- /dev/null +++ b/src/client/views/nodes/DataVizBox/DocCreatorMenu/Template.ts @@ -0,0 +1,220 @@ +import { makeAutoObservable } from 'mobx'; +import { Col } from './DocCreatorMenu'; +import { TemplateFieldType, TemplateLayouts } from './TemplateBackend'; +import { DynamicField } from './TemplateFieldTypes/DynamicField'; +import { FieldSettings, TemplateField, ViewType } from './TemplateFieldTypes/TemplateField'; +import { Conditional } from './Backend/TemplateManager'; +import { ImageField } from '../../../../../fields/URLField'; +import { Doc } from '../../../../../fields/Doc'; +import { TemplateDataField } from './TemplateFieldTypes/DataField'; + +export class Template { + _mainField: DynamicField; + + private dataFields: TemplateDataField[] = []; + + /** + * A Template can be created from a description of its fields (FieldSettings) or from a DynamicField + * @param definition definition of template as settings or DynamicField + */ + constructor(definition: FieldSettings | DynamicField) { + makeAutoObservable(this); + this._mainField = definition instanceof DynamicField ? definition : this.setupMainField(definition); + } + + get childFields(): TemplateField[] { + return this._mainField?.getSubfields ?? []; + } + get allFields(): TemplateField[] { + return this._mainField?.getAllSubfields ?? []; + } + get contentFields(): TemplateField[] { + return this.allFields.filter(field => field.isContentField); + } + get doc() { + return this._mainField?.renderedDoc; + } + get title() { + return this._mainField?.getTitle(); + } + + get descriptionSummary(): string { + let summary: string = ''; + this.contentFields.forEach(field => { + summary += `--- Field #${field.getID} (title: ${field.getTitle()}): ${field.getDescription ?? ''} ---`; + }); + return summary; + } + + get compiledContent(): string { + let summary: string = ''; + this.contentFields.forEach(field => { + summary += `--- Field #${field.getID} (title: ${field.getTitle()}): ${field.getContent() ?? ''} ---`; + }); + return summary; + } + + cleanup = () => { + //dispose each subfields disposers, etc. + }; + + clone = (withContent: boolean = false) => { + const clone = new Template(this._mainField?.makeClone(undefined, withContent) ?? TemplateLayouts.BasicSettings); + this.dataFields.forEach(field => clone.addDataField(field.title)); + return clone; + }; + + getRenderedDoc = () => this.doc; + + getFieldByID = (id: number): TemplateField => this.allFields.filter(field => field.getID === id)[0]; + + getFieldByTitle = (title: string) => [...this.allFields, ...this.dataFields].filter(field => field.getTitle() === title)[0]; + + setupMainField = (templateInfo: FieldSettings) => TemplateField.CreateField(templateInfo, 1, undefined) as DynamicField; + + printFieldInfo = () => { + this.allFields.forEach(field => { + const doc = field.renderedDoc; + }); + }; + + assignColToField = (fieldID: number, col: Col) => { + const field = this.getFieldByID(fieldID); + field.setContent(col.defaultContent ?? '', col.type === TemplateFieldType.VISUAL ? ViewType.IMG : ViewType.TEXT); + field.setTitle(col.title); + } + + addDataField = (title: string, content?: string) => { + this.dataFields.push(new TemplateDataField(title, content)); + } + + removeDataField = (title: string) => { + this.dataFields = this.dataFields.filter(field => !(field.title === title)); + } + + isValidTemplate = (cols: Col[]) => { + const maxMatches = this.maxMatches(this.getMatches(cols)); + return maxMatches === this.contentFields.length && this.title !== 'template_framework'; + }; + + applyConditionalLogicToField = (field: TemplateField | TemplateDataField, logic: Record<string, Conditional[]>) => { + if (field instanceof DynamicField) return; + const fieldStatements: Conditional[] = logic[field.getTitle()]; + const content = field.getContent() + fieldStatements && fieldStatements.forEach(statement => { + console.log(statement); + if (content === statement.condition) { + if (statement.target === 'Template') { + this._mainField.renderedDoc![statement.attribute] = statement.value; + Object.assign(this._mainField.settings.opts, {[statement.attribute]: statement.value}); + } else { + const targetField: TemplateField = this.getFieldByTitle(statement.target) as TemplateField; + if (targetField) { + targetField.renderedDoc![statement.attribute] = statement.value; + Object.assign(targetField.settings.opts, {[statement.attribute]: statement.value}); + } + } + } + }) + } + + applyConditionalLogic = (logic: Record<string, Conditional[]>) => { + const fields: (TemplateField | TemplateDataField)[] = [...this.allFields, ...this.dataFields]; + fields.forEach(field => this.applyConditionalLogicToField(field, logic)); + } + + setImageAsBackground(url: string, makeTransparent: boolean = false) { + const fieldSettings: FieldSettings = { + tl: [-1, -1], + br: [1, 1], + opts: {}, + viewType: ViewType.IMG, + } + + const field: TemplateField = TemplateField.CreateField(fieldSettings, Math.random() * 100 + 100, this._mainField); + field.setContent(url); + + if (makeTransparent) { + this.allFields.forEach(field => { + field.updateDocSetting('backgroundColor', 'transparent'); + field.updateDocSetting('borderWidth', '0'); + }); + } + + this._mainField.makeBackgroundField(field); + } + + /** + * This function is just a hack for now to get around weird document icon stuff (specifically it misses the background) + */ + setMatteBackground(makeTransparent: boolean = false) { + if (this._mainField.hasBackground) { + return; + } + + const fieldSettings: FieldSettings = { + tl: [-1, -1], + br: [1, 1], + opts: {backgroundColor: String(this._mainField.renderedDoc!.backgroundColor)}, + viewType: ViewType.TEXT, + } + + const field: TemplateField = TemplateField.CreateField(fieldSettings, Math.random() * 100 + 100, this._mainField); + + if (makeTransparent) { + this.allFields.forEach(field => { + field.updateDocSetting('backgroundColor', 'transparent'); + field.updateDocSetting('borderWidth', '0'); + }); + } + + this._mainField.makeBackgroundField(field); + } + + getMatches = (cols: Col[]): number[][] => { + const numFields = this.contentFields.length; + + if (cols.length !== numFields) return []; + + const matches: number[][] = Array(numFields) + .fill([]) + .map(() => []); + + this.contentFields.forEach((field, i) => (matches[i] = field.matches(cols))); + + return matches; + }; + + maxMatches = (matches: number[][]) => { + if (matches.length === 0) return 0; + + 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]) { + 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; + }; +} |