import {
getDataModelFromIdentifiers,
FieldType,
mergeRecursive
} from 'muze-utils';
import { applyInteractionPolicy } from '../helper';
const defaultInteractionPolicy = (valueMatrix, firebolt) => {
const isMeasure = field => field.type() === FieldType.MEASURE;
const canvas = firebolt.context;
const visualGroup = canvas.composition().visualGroup;
const xFields = [].concat(...visualGroup.getFieldsFromChannel('x'));
const yFields = [].concat(...visualGroup.getFieldsFromChannel('y'));
const colDim = xFields.every(field => field.type() === FieldType.DIMENSION);
const fieldInf = visualGroup.resolver().getAllFields();
const rowFacets = fieldInf.rowFacets;
const colFacets = fieldInf.colFacets;
valueMatrix.each((cell) => {
const unitFireBolt = cell.valueOf().firebolt();
if (!(xFields.every(isMeasure) && yFields.every(isMeasure))) {
const facetFields = cell.valueOf().facetByFields()[0];
const unitColFacets = facetFields.filter(d => colFacets.findIndex(v => v.equals(d)) !== -1);
const unitRowFacets = facetFields.filter(d => rowFacets.findIndex(v => v.equals(d)) !== -1);
let propFields;
if (colDim) {
propFields = unitColFacets.map(d => `${d}`);
} else {
propFields = unitRowFacets.map(d => `${d}`);
}
unitFireBolt.propagateWith('*', propFields, true);
}
});
};
const defaultCrossInteractionPolicy = {
behaviours: {
'*': (propagationPayload, context) => {
const propagationCanvasAlias = propagationPayload.sourceCanvas;
const canvasAlias = context.parentAlias();
return propagationCanvasAlias ? canvasAlias === propagationCanvasAlias : true;
}
},
sideEffects: {
tooltip: (propagationPayload, context) => {
const propagationUnit = propagationPayload.sourceUnit;
const propagationCanvas = propagationPayload.sourceCanvas;
const unitId = context.id();
const canvasAlias = context.parentAlias();
if (propagationCanvas) {
return propagationCanvas !== canvasAlias ? true : unitId === propagationUnit;
}
return true;
}
}
};
/**
* This class is responsible for dispatching any behavioural action to all the visual units housed by the canvas.
* It is created by {@link Canvas}. This class does not handle any physical actions. Physical actions get triggered
* in {@link VisualUnit} which is managed by it's own firebolt instance. The firebolt instance of canvas only
* propagates the action to all the visual units in it's composition.
*
* To get the firebolt instance of {@link Canvas}
* ```
* const firebolt = canvas.firebolt();
* ```
*
* @class GroupFireBolt
* @public
*/
export default class GroupFireBolt {
constructor (context) {
this.context = context;
this._interactionPolicy = this.constructor.defaultInteractionPolicy();
this._crossInteractionPolicy = this.constructor.defaultCrossInteractionPolicy();
this.context.once('canvas.updated').then(() => {
applyInteractionPolicy([this._interactionPolicy], this);
const crossInteractionPolicy = this._crossInteractionPolicy;
const behaviours = crossInteractionPolicy.behaviours;
const sideEffects = crossInteractionPolicy.sideEffects;
const visualGroup = context.composition().visualGroup;
const valueMatrix = visualGroup.composition().matrices.value;
valueMatrix.each((cell) => {
const unitFireBolt = cell.valueOf().firebolt();
for (const key in behaviours) {
unitFireBolt.changeBehaviourStateOnPropagation(key, behaviours[key]);
}
for (const key in sideEffects) {
unitFireBolt.changeSideEffectStateOnPropagation(key, sideEffects[key]);
}
});
});
}
static defaultInteractionPolicy () {
return defaultInteractionPolicy;
}
static defaultCrossInteractionPolicy () {
return defaultCrossInteractionPolicy;
}
interactionPolicy (...policy) {
if (policy.length) {
this._interactionPolicy = policy[0] || this.constructor.defaultInteractionPolicy();
return this;
}
return this._interactionPolicy;
}
crossInteractionPolicy (...policy) {
if (policy.length) {
this._crossInteractionPolicy = mergeRecursive(mergeRecursive({},
this.constructor.defaultCrossInteractionPolicy()), policy[0] || {});
return this;
}
return this._crossInteractionPolicy;
}
/**
* Dispatches a behavioural action with a payload. It takes the name of the behavioural action and a payload
* object which contains the criteria aend an array of side effects which determines what side effects are
* going to be shown in each visual unit of the canvas. It prepares the datamodel from the given criteria
* and initiates a propagation from the datamodel of canvas. Then all the visual units of canvas which listens
* to the propagation gets informed on which rows got selected and dispatches the behavioural action sent during
* propagation.
*
* To dispatch a behavioural action on the canvas
* ```
* // Get the firebolt instance of the canvas
* const firebolt = canvas.firebolt();
* // Dispatch a brush behaviour
* firebolt.dispatchBehaviour('brush', {
* // Selects all the rows with Horsepower having range between 100 and 200.
* criteria: {
* Horsepower: [100, 200]
* }
* });
* // On dispatch of this behavioural action, a selection box gets created and plots gets faded out which are the
* // default side effects mapped to this behavioural action.
* ```
*
* ```
* Additionally, it can also be passed an array of side effects in the payload.
* // Dispatch a select behaviour with only crossline as side effect.
* firebolt.dispatchBehaviour('select', {
* criteria: {
* Cylinders: ['8']
* },
* sideEffects: ['crossline']
* });
* ```
*
* @public
*
* @param {string} behaviour Name of the behavioural action
* @param {Object} payload Object which contains the interaction information.
* @param {Object | Array.<Array>} payload.criteria Identifiers by which the selection happens.
* @param {Array.<string|Object>} payload.sideEffects Side effects which needs to be shown.
*
* @return {GroupFireBolt} Instance of firebolt.
*/
dispatchBehaviour (behaviour, payload) {
const propPayload = Object.assign(payload);
const criteria = propPayload.criteria;
const data = this.context.data();
propPayload.action = behaviour;
const model = getDataModelFromIdentifiers(data, criteria);
data.propagate(model, propPayload, {
sourceId: this.context.alias()
});
return this;
}
}