aboutsummaryrefslogtreecommitdiff
path: root/src/client/util/DropConverter.ts
blob: 0314af06bd42de6bea91144fdaff6e3279bc164e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
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 docs = DocListCast(layoutDoc[fieldKey]);
    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 (layoutDoc[fieldKey] instanceof RichTextField || layoutDoc[fieldKey] 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: any) {
        convertDropDataToButtons(dragData as DragManager.DocumentDragData);
    },
    'converts the dropped data to buttons',
    '(dragData: any)'
);