import { PaletteOutput , PaletteRegistry } from 'src/plugins/charts/public' ; import type { Datatable , DatatableColumn } from 'src/plugins/expressions/public' ; import type { CustomPaletteParams , ColorStop } from '../../common' ; import { CUSTOM_PALETTE , defaultPaletteParams , DEFAULT_MAX_STOP , DEFAULT_MIN_STOP , } from '../constants' ; export function getStepValue ( colorStops , newColorStops , max ) { const length = newColorStops . length ; const dataStep = newColorStops [ length - 1 ] . stop - newColorStops [ length - 2 ] . stop || 1 ; let step = Number ( dataStep . toFixed ( 2 ) ) ; if ( max < colorStops [ length - 1 ] . stop + step ) { const diffToMax = max - colorStops [ length - 1 ] . stop ; step = diffToMax > 0 ? diffToMax : 1 ; } return step ; } function shiftPalette ( stops , max ) { const result = stops . map ( ( entry , i , array ) => ( { ... entry , stop : i + 1 < array . length ? array [ i + 1 ] . stop : max , } ) ) ; if ( stops [ stops . length - 1 ] . stop === max ) { const computedStep = getStepValue ( stops , result , max ) || 1 ; const step = Math . min ( 1 , computedStep ) ; result [ stops . length - 1 ] . stop = max + step ; } return result ; } function getOverallMinMax ( params , dataBounds ) { const { min : dataMin , max : dataMax } = getDataMinMax ( params ?. rangeType , dataBounds ) ; const minStopValue = params ?. colorStops ?. [ 0 ] ?. stop ?? Infinity ; const maxStopValue = params ?. colorStops ?. [ params . colorStops . length - 1 ] ?. stop ?? - Infinity ; const overallMin = Math . min ( dataMin , minStopValue ) ; const overallMax = Math . max ( dataMax , maxStopValue ) ; return { min : overallMin , max : overallMax } ; } export function getDataMinMax ( rangeType , dataBounds ) { const dataMin = rangeType === 'number' ? dataBounds . min : DEFAULT_MIN_STOP ; const dataMax = rangeType === 'number' ? dataBounds . max : DEFAULT_MAX_STOP ; return { min : dataMin , max : dataMax } ; } export function remapStopsByNewInterval ( controlStops , { newInterval , oldInterval , newMin , oldMin , } ) { return ( controlStops || [ ] ) . map ( ( { color , stop } ) => { return { color , stop : newMin + ( ( stop - oldMin ) * newInterval ) / oldInterval , } ; } ) ; } /**
 * This is a generic function to compute stops from the current parameters.
 */ export function getPaletteStops ( palettes , activePaletteParams , { prevPalette , dataBounds , mapFromMinValue , defaultPaletteName , } ) { const { min : minValue , max : maxValue } = getOverallMinMax ( activePaletteParams , dataBounds ) ; const interval = maxValue - minValue ; const { stops : currentStops , ... otherParams } = activePaletteParams || { } ; if ( activePaletteParams . name === 'custom' && activePaletteParams ?. colorStops ) { return shiftPalette ( activePaletteParams . colorStops , maxValue ) ; } const colorStopsFromPredefined = palettes . get ( prevPalette || activePaletteParams ?. name || defaultPaletteName || defaultPaletteParams . name ) . getCategoricalColors ( defaultPaletteParams . steps , otherParams ) ; const newStopsMin = mapFromMinValue ? minValue : interval / defaultPaletteParams . steps ; const stops = remapStopsByNewInterval ( colorStopsFromPredefined . map ( ( color , index ) => ( { color , stop : index } ) ) , { newInterval : interval , oldInterval : colorStopsFromPredefined . length , newMin : newStopsMin , oldMin : 0 , } ) ; return stops ; } export function reversePalette ( paletteColorRepresentation = [ ] ) { const stops = paletteColorRepresentation . map ( ( { stop } ) => stop ) ; return paletteColorRepresentation . map ( ( { color } , i ) => ( { color , stop : stops [ paletteColorRepresentation . length - i - 1 ] , } ) ) . reverse ( ) ; } /**
 * Some name conventions here:
 * * `displayStops` => It's an additional transformation of `stops` into a [0, N] domain for the EUIPaletteDisplay component.
 * * `stops` => final steps used to table coloring. It is a rightShift of the colorStops
 * * `colorStops` => user's color stop inputs.  Used to compute range min.
 *
 * When the user inputs the colorStops, they are designed to be the initial part of the color segment,
 * so the next stops indicate where the previous stop ends.
 * Both table coloring logic and EuiPaletteDisplay format implementation works differently than our current `colorStops`,
 * by having the stop values at the end of each color segment rather than at the beginning: `stops` values are computed by a rightShift of `colorStops`.
 * EuiPaletteDisplay has an additional requirement as it is always mapped against a domain [0, N]: from `stops` the `displayStops` are computed with
 * some continuity enrichment and a remap against a [0, 100] domain to make the palette component work ok.
 *
 * These naming conventions would be useful to track the code flow in this feature as multiple transformations are happening
 * for a single change.
 */ export function applyPaletteParams < T extends PaletteOutput < CustomPaletteParams > > ( palettes , activePalette , dataBounds ) { let displayStops = getPaletteStops ( palettes , activePalette ?. params || { } , { dataBounds , defaultPaletteName : activePalette ?. name , } ) ; if ( activePalette ?. params ?. reverse && activePalette ?. params ?. name !== CUSTOM_PALETTE ) { displayStops = reversePalette ( displayStops ) ; } return displayStops ; } function getId ( id ) { return id ; } export function getNumericValue ( rowValue ) { if ( rowValue == null || Array . isArray ( rowValue ) ) { return ; } return rowValue ; } export const findMinMaxByColumnId = ( columnIds , table , getOriginalId = getId ) => { const minMax = { } ; if ( table != null ) { for ( const columnId of columnIds ) { const originalId = getOriginalId ( columnId ) ; minMax [ originalId ] = minMax [ originalId ] || { max : - Infinity , min : Infinity } ; table . rows . forEach ( ( row ) => { const rowValue = row [ columnId ] ; const numericValue = getNumericValue ( rowValue ) ; if ( numericValue != null ) { if ( minMax [ originalId ] . min > numericValue ) { minMax [ originalId ] . min = numericValue ; } if ( minMax [ originalId ] . max < numericValue ) { minMax [ originalId ] . max = numericValue ; } } } ) ; if ( minMax [ originalId ] . max === - Infinity ) { minMax [ originalId ] = { max : 100 , min : 0 , fallback : true } ; } } } return minMax ; } ; interface SourceParams { order ? : string ; orderBy ? : string ; otherBucket ? : boolean ; } export const getSortPredicate = ( column ) => { const params = column . meta ?. sourceParams ?. params as SourceParams | undefined ; const sort = params ?. orderBy ; if ( params ?. otherBucket || ! sort ) return 'dataIndex' ; if ( sort && sort !== '_key' ) { if ( params ?. order === 'desc' ) { return 'numDesc' ; } else { return 'numAsc' ; } } else { if ( params ?. order === 'desc' ) { return 'alphaDesc' ; } else { return 'alphaAsc' ; } } } ;