Source: visual-unit/src/firebolt/index.js

import { FieldType } from 'muze-utils';
import { Firebolt, SpawnableSideEffect } from '@chartshq/muze-firebolt';
import { registerListeners } from './helper';
import { payloadGenerator } from './payload-generator';
import { propagateValues } from './data-propagator';

/**
 * This class manages the interactions of visual unit. It associates physical actions with
 * behavioural actions. It also propagates the behavioural actions to other datamodels.
 */
export default class UnitFireBolt extends Firebolt {
    constructor (...params) {
        super(...params);
        registerListeners(this);
    }

    propagate (behaviour, payload, selectionSet, sideEffects) {
        propagateValues(this, behaviour, {
            payload,
            selectionSet,
            sideEffects,
            propagationFields: this._propagationFields
        });
    }

    getApplicableSideEffects (sideEffects, payload, propagationInf) {
        const context = this.context;
        const unitId = context.id();
        const aliasName = context.parentAlias();
        const propagationSourceCanvas = propagationInf.propPayload && propagationInf.propPayload.sourceCanvas;
        const sourceUnitId = propagationInf.propPayload && propagationInf.propPayload.sourceUnit;
        const sourceSideEffects = this._sourceSideEffects;
        const sideEffectInstances = this.sideEffects();
        const actionOnSource = sourceUnitId ? sourceUnitId === unitId : true;

        const applicableSideEffects = payload.sideEffects ? [{
            effects: payload.sideEffects,
            behaviours: [payload.action]
        }] : sideEffects;
        applicableSideEffects.forEach((d) => {
            let mappedEffects = d.effects;
            mappedEffects = mappedEffects.filter((se) => {
                const mutates = sideEffectInstances[se.name || se].constructor.mutates();
                if (mutates && propagationInf.isMutableAction === false) {
                    return false;
                }
                if (!actionOnSource && payload.criteria !== null) {
                    const sideEffectChecker = sourceSideEffects[se.name || se];
                    return sideEffectChecker ? sideEffectChecker(propagationInf.propPayload, context) : true;
                }
                if (propagationSourceCanvas === aliasName || actionOnSource) {
                    return se.applyOnSource !== false;
                }

                return true;
            });
            d.effects = mappedEffects;
        });
        return applicableSideEffects;
    }

    shouldApplySideEffects (propagate) {
        return propagate === false;
    }

    onDataModelPropagation () {
        return (data, config) => {
            let isSourceFieldPresent = true;
            let isMutableAction = false;
            const propPayload = config.payload;
            const sourceIdentifiers = config.sourceIdentifiers;
            const enabledFn = config.enabled;
            const action = config.action;
            const payloadFn = payloadGenerator[action] || payloadGenerator.__default;

            if (sourceIdentifiers) {
                const fieldsConfig = sourceIdentifiers.getFieldsConfig();
                const sourceIdentifierFields = Object.keys(fieldsConfig);
                const propFields = Object.keys(data[0].getFieldsConfig());
                if (!Object.values(fieldsConfig).some(d => d.def.type === FieldType.MEASURE)) {
                    isSourceFieldPresent = sourceIdentifierFields.some(d => propFields.indexOf(d) !== -1);
                }
            }

            const payload = payloadFn(this.context, data, config);
            const sourceBehaviours = this._sourceBehaviours;
            const filterFn = sourceBehaviours[action] || sourceBehaviours['*'];
            let enabled = true;

            if (filterFn) {
                enabled = filterFn(propPayload || {}, this.context);
            }

            if (enabledFn) {
                enabled = enabledFn(config, this) && enabled !== false;
            }

            if (enabled) {
                const effects = this._behaviourEffectMap[action];
                const sideEffectInstances = this.sideEffects();
                isMutableAction = config.groupId ?
                    effects.some(d => sideEffectInstances[d.name || d].constructor.mutates()) : config.isMutableAction;

                const propagationInf = {
                    propagate: false,
                    data,
                    propPayload,
                    sourceIdentifiers,
                    persistent: false,
                    isSourceFieldPresent,
                    sourceId: config.propagationSourceId,
                    isMutableAction: config.isMutableAction
                };

                this._actionHistory[action] = {
                    payload,
                    propagationInf,
                    isMutableAction
                };
                this.dispatchBehaviour(action, payload, propagationInf);
            }
        };
    }

    initializeSideEffects () {
        super.initializeSideEffects();
        const sideEffects = this.sideEffects();
        for (const key in sideEffects) {
            if ({}.hasOwnProperty.call(sideEffects, key)) {
                sideEffects[key] instanceof SpawnableSideEffect && sideEffects[key].drawingContext(() => {
                    const context = this.context;
                    return context.getDrawingContext();
                });
            }
        }
    }

    prepareSelectionSets (behaviours) {
        const data = this.context.data();
        if (data) {
            this.createSelectionSet(data.getData().uids, behaviours);
        }
        return this;
    }

    remove () {
        this.context.cachedData()[0].unsubscribe('propagation');
        return this;
    }
}