import { MarkType , Spec } from 'vega' ; import { compile as vlCompile } from 'vega-lite' ; import { Channel } from 'vega-lite/src/channel' ; import { ChannelDef } from 'vega-lite/src/channeldef' ; import { InlineData } from 'vega-lite/src/data' ; import { NormalizedUnitSpec , TopLevelUnitSpec } from 'vega-lite/src/spec/unit' ; import { batchGroupBy } from '../../reducers/historyOptions' ; import { LyraVegaLiteSpec , Mark , MarkRecord } from '../../store/factory/Mark' ; import duplicate from '../../util/duplicate' ; import { setVlUnit } from '../markActions' ; import updateAggregateDependencies from './aggregateDependencies' ; import cleanupUnused from './cleanupUnused' ; import parseData from './parseData' ; import parseGuides from './parseGuides' ; import parseMarks from './parseMarks' ; import parseScales from './parseScales' ; const AGGREGATE_OPS = require ( '../../constants/aggregateOps' ) , getInVis = require ( '../../util/immutable-utils' ) . getInVis , dsUtils = require ( '../../util/dataset-utils' ) ; const TYPES = { rect : 'bar' , symbol : 'circle' , text : 'text' , line : 'line' , area : 'area' } ; export const CELLW = 517 , CELLH = 392 ; export interface CompiledBinding { input : TopLevelUnitSpec ; output : Spec ; map : any ; mark : MarkRecord ; markId : number ; markType : MarkType ; property : string ; channel : Channel ; dsId : number ; plId : number ; } param { number } dsId param { Object } field param { number } markId param { string } property returns { Function } export default function bindChannel ( dsId , field , markId , property ) { return function ( dispatch , getState ) { const state = getState ( ) , mark = getInVis ( state , 'marks.' + markId ) , markType = mark . type , spec = vlSpec ( mark ) , mapping = map ( spec ) , channel = channelName ( property ) , plId = getInVis ( state , 'datasets.' + dsId + '._parent' ) ; const from = mark . from ; if ( from && from . data ) { if ( getInVis ( state , `datasets.${ from . data }._parent` ) !== plId ) { throw Error ( 'Mark and field must be from the same pipeline.' ) ; } } batchGroupBy . start ( ) ; spec . encoding [ channel ] = channelDef ( field ) ; const parsed = compile ( spec , property , dsId ) ; parsed . map = mapping ; parsed . mark = mark ; parsed . markId = markId ; parsed . markType = markType ; parsed . property = property ; parsed . channel = channel ; parsed . dsId = dsId ; parsed . plId = plId ; parseData ( dispatch , state , parsed ) ; parseScales ( dispatch , state , parsed ) ; parseMarks ( dispatch , state , parsed ) ; cleanupUnused ( dispatch , state ) ; parseGuides ( dispatch , getState ( ) , parsed ) ; dispatch ( setVlUnit ( spec , markId ) ) ; batchGroupBy . end ( ) ; } ; } param { Object } spec param { string } property param { number } dsId returns { Object } function compile ( spec , property , dsId ) { spec = duplicate ( spec ) ; ( spec . data as InlineData ) . values = dsUtils . output ( dsId ) ; spec . config . view = { width : CELLW , height : CELLH } ; spec . config . mark = { filled : property === 'fill' } ; return { input : spec , output : vlCompile ( spec ) . spec } as CompiledBinding ; } param { Mark } mark returns { Object } function vlSpec ( mark ) { return mark . _vlUnit || { mark : TYPES [ mark . type ] , data : { values : [ ] } , encoding : { } , config : { } } ; } param { Object } vlUnit returns { Object } function map ( vlUnit ) { return ( vlUnit . _lyraMap || ( vlUnit . _lyraMap = { data : { } , scales : { } , axes : { } , legends : { } , marks : { } } ) ) ; } param { string } name returns { string } export function channelName ( name ) { switch ( name ) { case 'x' : case 'x+' : case 'x2' : case 'width' : return 'x' ; case 'y' : case 'y+' : case 'y2' : case 'height' : return 'y' ; case 'fill' : case 'stroke' : return 'color' ; case 'size' : case 'text' : case 'detail' : return name ; } } const re = { agg : new RegExp ( '^(' + AGGREGATE_OPS . join ( '|' ) + ')_(.*?)$' ) , bin : new RegExp ( '^(bin)_(.*?)(_start|_mid|_end)$' ) } ; private memberOf param { Object } field returns { Object } function channelDef ( field ) { const name = field . name , agg = field . aggregate , bin = field . bin , ref = { type : field . mtype } ; let res ; if ( agg || ( res = re . agg . exec ( name ) ) ) { ref . aggregate = res ? res [ 1 ] : agg ; } else if ( bin || ( res = re . bin . exec ( name ) ) ) { ref . bin = true ; } ref . field = res ? res [ 2 ] : name ; return ref ; }