export { allDataHaveIdentitites , doesEditorHaveUnallocatedFields , getDataset , getEmptyDataset , getIdentityIndices , getMetadata , getMetadataByKeys , getTemplateFieldsFromMetadata , getValues , getValuesByIndices , getValuesForDatum , resolveDataFromItem , resolveDatumToArray , IVisualDataset , ITableColumnMetadata , IVisualValueMetadata , IVisualValueRow } ; import powerbi from 'powerbi-visuals-api' ; import DataViewMetadataColumn = powerbi . DataViewMetadataColumn ; import ISelectionId = powerbi . visuals . ISelectionId ; import keys from 'lodash/keys' ; import pick from 'lodash/pick' ; import pickBy from 'lodash/pickBy' ; import matches from 'lodash/matches' ; import reduce from 'lodash/reduce' ; import { ITemplateDatasetField } from '../template/schema' ; import { getState } from '../../store' ; import { IVegaViewDatum } from '../vega' ; import { isInteractivityReservedWord , TDataPointSelectionStatus } from '../interactivity' ; /**
 * Confirm that each dataum in a datset contains a reconcilable identifier for selection purposes.
 */ const allDataHaveIdentitites = ( data ) => data ?. filter ( ( d ) => d ?. hasOwnProperty ( 'identityIndex' ) ) ?. length === data ?. length ; /**
 * Compare two sets of dataset metadata, as well as the current state of the comparison (if supplied)
 * to ensure that any discrepancies can still be flagged to Deneb for addressing by the creator.
 */ const doesEditorHaveUnallocatedFields = ( datasetMetadata , editorFieldsInUseMetadata , currentState = false ) => reduce ( editorFieldsInUseMetadata , ( result , value , key ) => ! ( key in datasetMetadata ) || result , false ) || currentState ; /**
 * Get current processed dataset (metadata and values) from Deneb's store.
 */ const getDataset = ( ) : IVisualDataset => getState ( ) . dataset ; /**
 * Ensures an empty dataset is made available.
 */ const getEmptyDataset = ( ) : IVisualDataset => ( { metadata : { } , values : [ ] } ) ; /**
 * Get array of all data row indices for a supplied dataset.
 */ const getIdentityIndices = ( data ) : number [ ] => data ?. map ( ( d ) => d ?. identityIndex ) ; /**
 * Get all metadata for current processed dataset from Deneb's store.
 */ const getMetadata = ( ) => getDataset ( ) . metadata ; /**
 * Get a reduced set of metadata based on an array of key names from Deneb's store.
 */ const getMetadataByKeys = ( keys = [ ] ) => pick ( getMetadata ( ) , keys ) ; /**
 * Get the template columns from a supplied set of metadata.
 */ const getTemplateFieldsFromMetadata = ( metadata ) : ITemplateDatasetField [ ] => reduce ( metadata , ( result , value , key ) => result . concat ( value . templateMetadata ) , < ITemplateDatasetField [ ] > [ ] ) ; /**
 * Get all values (excluding metadata) for current processed dataset from Deneb's store.
 */ const getValues = ( ) => getDataset ( ) . values ; /**
 * Returns `getValues()`, but filtered for a supplied list `identityIndex` values.
 */ const getValuesByIndices = ( indices ) => getValues ( ) . filter ( ( v ) => indices . indexOf ( v . identityIndex ) > - 1 ) ; /**
 * For the supplied (subset of) `metadata` and `datum`, attempt to find the first matching row in the visual's processed dataset for this combination.
 * Note that if Vega/Vega-Lite applies a prefixed aggregate in the datum, we can't reconcile this wihtout further processing. We could consider
 * processing for agg prefix, e.g. and seeing if we can match like this: (?:max|min|sum|argMax|argMin[etc...]_){1}(.*), but this may open up a whole
 * other can of worms, like having to match on an aggregated value and doing this ourselves. I'll leave this here as a reminder to think about it.
 */ const getValuesForDatum = ( metadata , data ) : IVisualValueRow [ ] => { const matches = getMatchedValues ( metadata , data ) ; if ( matches ?. length > 0 ) { return matches ; } return getMatchedValues ( pickBy ( metadata , ( md ) => ! md . isMeasure ) , data ) ; } ; /**
 * For the supplied (subset of) `metadata` and `data`, attempt to find any matching rows in the visual's processed dataset for this combination.
 */ const getMatchedValues = ( metadata , data ) : IVisualValueRow [ ] => { const resolvedMd = resolveDatumForMetadata ( metadata , data ?. [ 0 ] ) , matchedRows = getValues ( ) . filter ( matches ( resolvedMd ) ) ; if ( matchedRows . length > 0 ) { return matchedRows ; } return ( matchedRows . length > 0 && matchedRows ) || null ; } ; /**
 * Do a simple lookup against the store dataset for a given key
 */ export const lookupMetadataColumn = ( key ) => Object . entries ( getState ( ) . dataset ) ?. find ( ( [ k , v ] ) => v . queryName === key ) ?. [ 1 ] ; /**
 * For a given (subset of) `metadata` and `datum`, create an `IVisualValueRow` that can be used to search for matching values in the visual's dataset.
 */ const resolveDatumForMetadata = ( metadata , datum ) => { const reducedDatum = < IVisualValueRow > pick ( datum , keys ( metadata ) ) || null ; return reduce ( reducedDatum , ( result , value , key ) => { result [ key ] = resolveDatumValueForMetadataColumn ( metadata [ key ] , value ) ; return result ; } , < IVisualValueRow > { } ) ; } ; /**
 * Because Vega's tooltip channel supplies datum field values as strings, for a supplied metadata `column` and `datum`, attempt to resolve it to a pure type,
 * so that we can try to use its value to reconcile against the visual's dataset in order to resolve selection IDs.
 */ const resolveDatumValueForMetadataColumn = ( column , value ) => { switch ( true ) { case column . type . dateTime : { return new Date ( value ) ; } case column . type . numeric : case column . type . integer : { return Number . parseFloat ( value ) ; } default : return value ; } } ; /**
 * For a given datum, resolve it to an array of keys and values. Addiitonally, we can (optionally) ensure that the
 * `interactivityReservedWords` are stripped out so that we can get actual fields and values assigned to a datum.
 */ const resolveDatumToArray = ( obj , filterReserved = true ) => Object . entries ( { ... obj } ) . filter ( ( [ k , v ] ) => ( filterReserved && ! isInteractivityReservedWord ( k ) ) || k ) ; /**
 * Take an item from a Vega event and attempt to resolve .
 */ const resolveDataFromItem = ( item ) : IVegaViewDatum [ ] => { switch ( true ) { case item === undefined : return null ; case item ?. context ?. data ?. facet ?. values ?. value : return item ?. context ?. data ?. facet ?. values ?. value ?. slice ( ) ; default : return [ { ... item ?. datum } ] ; } } ; /**
 * Processed visual data and column metadata for rendering.
 */ interface IVisualDataset { metadata : IVisualValueMetadata ; values : IVisualValueRow [ ] ; } /**
 * The structure of our visual dataset column metadata.
 */ interface IVisualValueMetadata { [ key ] : ITableColumnMetadata ; } /**
 * Custom data role metadata, needed to manage functionality within the editors.
 */ interface ITableColumnMetadata extends DataViewMetadataColumn { isColumn : boolean ; sourceIndex : number ; templateMetadata : ITemplateDatasetField ; } /**
 * Represents each values entry from the data view.
 */ interface IVisualValueRow { [ key ] : any ; identityIndex : number ; __selected__ : TDataPointSelectionStatus ; __identity__ : ISelectionId ; __key__ : string ; }