import { Doc, DocListCast, StrListCast } from '../../fields/Doc'; import { DocData } from '../../fields/DocSymbols'; import { ObjectField } from '../../fields/ObjectField'; import { RichTextField } from '../../fields/RichTextField'; import { ComputedField, ScriptField } from '../../fields/ScriptField'; import { StrCast } from '../../fields/Types'; import { ImageField } from '../../fields/URLField'; import { Docs } from '../documents/Documents'; import { DocumentType } from '../documents/DocumentTypes'; import { ButtonType, FontIconBox } from '../views/nodes/FontIconBox/FontIconBox'; import { DragManager } from './DragManager'; import { ScriptingGlobals } from './ScriptingGlobals'; /** * * Recursively converts 'doc' into a template that can be used to render other documents. * * For recurive Docs in the template, their target fieldKey is defined by their title, * not by whatever fieldKey they used in their layout. * @param doc * @param first whether this is the topmost root of the recursive template * @returns whether a template was successfully created */ function makeTemplate(doc: Doc, first: boolean = true): boolean { const layoutDoc = doc.layout instanceof Doc && doc.layout.isTemplateForField ? doc.layout : doc; if (layoutDoc.layout instanceof Doc) { return true; // its already a template } const layout = StrCast(layoutDoc.layout).match(/fieldKey={'[^']*'}/)?.[0]; const fieldKey = layout?.replace("fieldKey={'", '').replace(/'}$/, ''); const docData = fieldKey ? layoutDoc[fieldKey] : undefined; const docs = DocListCast(docData); let isTemplate = false; docs.forEach(d => { if (!StrCast(d.title).startsWith('-')) { isTemplate = Doc.MakeMetadataFieldTemplate(d, layoutDoc[DocData]) || isTemplate; } else if (d.type === DocumentType.COL || d.data instanceof RichTextField) { isTemplate = makeTemplate(d, false) || isTemplate; } }); if (first && !docs.length) { // bcz: feels hacky : if the root level document has items, it's not a field template isTemplate = Doc.MakeMetadataFieldTemplate(doc, layoutDoc[DocData], true) || isTemplate; } else if (docData instanceof RichTextField || docData instanceof ImageField) { if (!StrCast(layoutDoc.title).startsWith('-')) { isTemplate = Doc.MakeMetadataFieldTemplate(layoutDoc, layoutDoc[DocData], true); } } return isTemplate; } /** * Converts a Doc to a render template that can be applied to other Docs to customize how they render while * still using the other Doc as the backing data store (ie, dataDoc). During rendering, if a layout Doc is provided * with 'isTemplateDoc' set, then the layout Doc is treated as a template for the rendered Doc. The template Doc is * "expanded" to create an template instance for the rendered Doc. * * * @param doc the doc to convert to a template * @returns 'doc' */ export function MakeTemplate(doc: Doc) { doc.isTemplateDoc = makeTemplate(doc, true); return doc; } export function makeUserTemplateButton(doc: Doc) { const layoutDoc = doc; // doc.layout instanceof Doc && doc.layout.isTemplateForField ? doc.layout : doc; if (layoutDoc.type !== DocumentType.FONTICON) { !layoutDoc.isTemplateDoc && makeTemplate(layoutDoc); } layoutDoc.isTemplateDoc = true; const dbox = Docs.Create.FontIconDocument({ _nativeWidth: 100, _nativeHeight: 100, _width: 100, _height: 100, backgroundColor: StrCast(doc.backgroundColor), title: StrCast(layoutDoc.title), btnType: ButtonType.ClickButton, icon: 'bolt', isSystem: false, }); dbox.title = ComputedField.MakeFunction('this.dragFactory.title'); dbox.dragFactory = layoutDoc; dbox.dropPropertiesToRemove = doc.dropPropertiesToRemove instanceof ObjectField ? ObjectField.MakeCopy(doc.dropPropertiesToRemove) : undefined; dbox.onDragStart = ScriptField.MakeFunction('getCopy(this.dragFactory)'); return dbox; } export function convertDropDataToButtons(data: DragManager.DocumentDragData) { data?.draggedDocuments.forEach((doc, i) => { let dbox = doc; // bcz: isButtonBar is intended to allow a collection of linear buttons to be dropped and nested into another collection of buttons... it's not being used yet, and isn't very elegant if (doc.type === DocumentType.FONTICON || StrCast(Doc.Layout(doc).layout).includes(FontIconBox.name)) { if (data.dropPropertiesToRemove || dbox.dropPropertiesToRemove) { // dbox = Doc.MakeEmbedding(doc); // don't need to do anything if dropping an icon doc onto an icon bar since there should be no layout data for an icon dbox = Doc.MakeEmbedding(dbox); const dragProps = StrListCast(dbox.dropPropertiesToRemove); const remProps = (data.dropPropertiesToRemove || []).concat(Array.from(dragProps)); remProps.forEach(prop => { dbox[prop] = undefined; }); } } else if (!doc.onDragStart && !doc.isButtonBar) { dbox = makeUserTemplateButton(doc); } else if (doc.isButtonBar) { dbox.ignoreClick = true; } data.droppedDocuments[i] = dbox; }); } ScriptingGlobals.add( // eslint-disable-next-line prefer-arrow-callback function convertToButtons(dragData: DragManager.DocumentDragData) { convertDropDataToButtons(dragData); }, 'converts the dropped data to buttons', '(dragData: any)' );