import React , { ComponentType , isValidElement , ReactNode } from 'react' ; import { $Values , isPrimitive } from 'utility-types' ; import { v1 as uuidV1 } from 'uuid' ; import { PrimitiveValue } from '../chart_types/partition_chart/layout/utils/group_by_rollup' ; import { Color , Colors } from '../common/colors' ; import { Degrees , Radian } from '../common/geometry' ; import { BaseDatum } from '../specs' ; import { AdditiveNumber } from './accessor' ; import { Point } from './point' ; public export const Position = Object . freeze ( { Top : 'top' as const , Bottom : 'bottom' as const , Left : 'left' as const , Right : 'right' as const , } ) ; public export type Position = $Values < typeof Position > ; public export const LayoutDirection = Object . freeze ( { Horizontal : 'horizontal' as const , Vertical : 'vertical' as const , } ) ; public export type LayoutDirection = $Values < typeof LayoutDirection > ; public export const ColorVariant = Object . freeze ( { /**
     * Uses series color. Rather than setting a static color, this will use the
     * default series color for a given series.
     */ Series : '__use__series__color__' as const , /**
     * Uses empty color, similar to transparent.
     */ None : '__use__empty__color__' as const , /**
     * Computes best color based on background contrast
     */ Adaptive : '__use__adaptive__color__' as const , } ) ; public export type ColorVariant = $Values < typeof ColorVariant > ; public export const HorizontalAlignment = Object . freeze ( { Center : 'center' as const , Right : Position . Right , Left : Position . Left , /**
     * Aligns to near side of axis depending on position
     *
     * Examples:
     * - Left Axis, `Near` will push the label to the `Right`, _near_ the axis
     * - Right Axis, `Near` will push the axis labels to the `Left`
     * - Top/Bottom Axes, `Near` will default to `Center`
     */ Near : 'near' as const , /**
     * Aligns to far side of axis depending on position
     *
     * Examples:
     * - Left Axis, `Far` will push the label to the `Left`, _far_ from the axis
     * - Right Axis, `Far` will push the axis labels to the `Right`
     * - Top/Bottom Axes, `Far` will default to `Center`
     */ Far : 'far' as const , } ) ; public export type HorizontalAlignment = $Values < typeof HorizontalAlignment > ; public export const VerticalAlignment = Object . freeze ( { Middle : 'middle' as const , Top : Position . Top , Bottom : Position . Bottom , /**
     * Aligns to near side of axis depending on position
     *
     * Examples:
     * - Top Axis, `Near` will push the label to the `Right`, _near_ the axis
     * - Bottom Axis, `Near` will push the axis labels to the `Left`
     * - Left/Right Axes, `Near` will default to `Middle`
     */ Near : 'near' as const , /**
     * Aligns to far side of axis depending on position
     *
     * Examples:
     * - Top Axis, `Far` will push the label to the `Top`, _far_ from the axis
     * - Bottom Axis, `Far` will push the axis labels to the `Bottom`
     * - Left/Right Axes, `Far` will default to `Middle`
     */ Far : 'far' as const , } ) ; public export type VerticalAlignment = $Values < typeof VerticalAlignment > ; public export type Datum = any ; public export type Rotation = 0 | 90 | - 90 | 180 ; public export type Rendering = 'canvas' | 'svg' ; public export type StrokeStyle = Color ; internal export function compareByValueAsc ( a , b ) { return a > b ? 1 : a < b ? - 1 : 0 ; } internal export function clamp ( value , lowerBound , upperBound ) { return Math . min ( Math . max ( value , lowerBound ) , upperBound ) ; } internal export function getColorFromVariant ( seriesColor , color ? ) { if ( color === ColorVariant . Series ) { return seriesColor ; } if ( color === ColorVariant . None ) { return Colors . Transparent . keyword ; } return color || seriesColor ; } internal export const degToRad = ( angle ) : Radian => ( angle / 180 ) * Math . PI ; internal export function htmlIdGenerator ( idPrefix ? ) { const prefix = idPrefix || `i${ uuidV1 ( ) }` ; return ( suffix ? ) => `${ prefix }_${ suffix || uuidV1 ( ) }` ; } public export type RecursivePartial < T > = { [ P in keyof T ] ? : T [ P ] extends NonAny [ ] ? T [ P ] : T [ P ] extends ReadonlyArray < NonAny > ? T [ P ] : T [ P ] extends ( infer U ) [ ] ? RecursivePartial < U > [ ] : T [ P ] extends ReadonlyArray < infer U > ? ReadonlyArray < RecursivePartial < U > > : T [ P ] extends Set < infer V > ? Set < RecursivePartial < V > > : T [ P ] extends Map < infer K , infer V > ? Map < K , RecursivePartial < V > > : T [ P ] extends NonAny ? T [ P ] : IsUnknown < T [ P ] , 1 , 0 > extends 1 ? T [ P ] : RecursivePartial < T [ P ] > ; } ; public export type IsAny < T , True , False = never > = True | False extends ( T extends never ? True : False ) ? True : False ; public export type IsUnknown < T , True , False = never > = unknown extends T ? IsAny < T , False , True > : False ; public export type NonAny = number | boolean | string | symbol | null ; public export interface MergeOptions { defaultValue mergeOptionalPartialValues ? : boolean ; defaultValue mergeMaps ? : boolean ; } internal export function getPartialValue < T > ( base , partial ? , partials = [ ] ) { const partialWithValue = partial !== undefined ? partial : partials . find ( ( v ) => v !== undefined ) ; return partialWithValue !== undefined ? ( partialWithValue as T ) : base ; } param object param objects internal export function getAllKeys ( object ? , objects = [ ] ) { return new Set ( [ object , ... objects ] . filter ( Boolean ) . reduce ( ( keys , obj ) => { if ( obj && typeof obj === 'object' ) { const newKeys = obj ? ( obj instanceof Map ? obj . keys ( ) : Object . keys ( obj ) ) : [ ] ; keys . push ( ... newKeys ) ; } return keys ; } , [ ] ) ) ; } internal export function isArrayOrSet < T > ( value ) { return Array . isArray ( value ) || value instanceof Set ; } internal export function isNil ( value ) { return value === null || value === undefined ; } internal export function hasPartialObjectToMerge < T > ( base , partial ? , additionalPartials = [ ] ) { if ( isArrayOrSet ( base ) ) { return false ; } if ( typeof base === 'object' && base !== null ) { if ( typeof partial === 'object' && ! isArrayOrSet ( partial ) && partial !== null ) { return true ; } return additionalPartials . some ( ( p ) => typeof p === 'object' && ! Array . isArray ( p ) ) ; } return false ; } internal export function shallowClone ( value ) { if ( Array . isArray ( value ) ) { return [ ... value ] ; } if ( value instanceof Set ) { return new Set ( [ ... value ] ) ; } if ( typeof value === 'object' && value !== null ) { if ( value instanceof Map ) { return new Map ( value . entries ( ) ) ; } return { ... value } ; } return value ; } function isReactNode ( el ) { return isNil ( el ) || isPrimitive ( el ) || isValidElement ( el ) ; } function isReactComponent < P extends Record < string , any > > ( el ) { return ! isReactNode ( el ) ; } internal export function renderWithProps < P extends Record < string , any > > ( El , props ) { return isReactComponent < P > ( El ) ? React . createElement ( El , props ) : El ; } note param base param partial returns internal export function mergePartial < T > ( base , partial ? , options = { } , additionalPartials = [ ] ) { const baseClone = shallowClone ( base ) ; if ( hasPartialObjectToMerge ( base , partial , additionalPartials ) ) { const mapCondition = ! ( baseClone instanceof Map ) || options . mergeMaps ; const partialKeys = getAllKeys ( partial , additionalPartials ) ; if ( partialKeys . size > 0 && ( options . mergeOptionalPartialValues ?? true ) && mapCondition ) { partialKeys . forEach ( ( key ) => { if ( baseClone instanceof Map ) { if ( ! baseClone . has ( key ) ) { baseClone . set ( key , ( partial as any ) . get ( key ) !== undefined ? ( partial as any ) . get ( key ) : additionalPartials . find ( ( v ) => v . get ( key ) !== undefined ) || new Map ( ) . get ( key ) ) ; } } else if ( ! ( key in baseClone ) ) { baseClone [ key ] = ( partial as any ) ?. [ key ] !== undefined ? ( partial as any ) [ key ] : ( additionalPartials . find ( ( v ) => v ?. [ key ] !== undefined ) ?? ( { } as any ) ) [ key ] ; } } ) ; } if ( baseClone instanceof Map ) { if ( options . mergeMaps ) { return [ ... baseClone . keys ( ) ] . reduce ( ( newBase , key ) => { const partialValue = partial && ( partial as any ) . get ( key ) ; const partialValues = additionalPartials . map ( ( v ) => typeof v === 'object' && v instanceof Map ? v . get ( key ) : undefined ) ; const baseValue = ( base as any ) . get ( key ) ; newBase . set ( key , mergePartial ( baseValue , partialValue , options , partialValues ) ) ; return newBase ; } , baseClone as any ) ; } if ( partial !== undefined ) { return partial as any ; } const additional = additionalPartials . find ( ( p ) => p !== undefined ) ; if ( additional ) { return additional as any ; } return baseClone as any ; } return Object . keys ( baseClone ) . reduce ( ( newBase , key ) => { const partialValue = partial && ( partial as any ) [ key ] ; const partialValues = additionalPartials . map ( ( v ) => ( typeof v === 'object' ? ( v as any ) [ key ] : undefined ) ) ; const baseValue = ( base as any ) [ key ] ; newBase [ key ] = mergePartial ( baseValue , partialValue , options , partialValues ) ; return newBase ; } , baseClone ) ; } return getPartialValue < T > ( baseClone , partial , additionalPartials ) ; } public export type ValueFormatter = ( value : number ) => string ; public export type ValueAccessor < D extends BaseDatum = Datum > = ( d : D ) => AdditiveNumber ; public export type LabelAccessor < T = PrimitiveValue > = ( value : T ) => string ; public export type ShowAccessor = ( value : PrimitiveValue ) => boolean ; internal export function getDistance ( a , b ) { return Math . sqrt ( Math . pow ( b . x - a . x , 2 ) + Math . pow ( b . y - a . y , 2 ) ) ; } internal export function stringifyNullsUndefined ( value ? ) { if ( value === undefined ) { return 'undefined' ; } if ( value === null ) { return 'null' ; } return value ; } internal param { B [ ] } arr param { ( d : B ) => T } extractor export function isUniqueArray < B , T > ( arr , extractor ? ) { const values = new Set < B | T > ( ) ; return ( function isUniqueArrayFn ( ) { return arr . every ( ( v ) => { const value = extractor ? extractor ( v ) : v ; if ( values . has ( value ) ) { return false ; } values . add ( value ) ; return true ; } ) ; } ) ( ) ; } internal export function isRTLString ( s , ratio = 0.5 ) { const stripped = s . replace ( /[\u0591-\u07FF\uFB1D-\uFDFD\uFE70-\uFEFC]|\s|\d/gi , '' ) ; return stripped . length / s . replace ( /\s|\d/gi , '' ) . length < ratio ; } internal export function hasMostlyRTLItems < T extends string > ( items , ratio = 0.5 ) { const filteredItems = items . filter ( Boolean ) ; const rtlItemCount = filteredItems . filter ( ( s ) => isRTLString ( s ) ) . length ; return rtlItemCount / filteredItems . length > ratio ; } internal export function isDefined < T > ( value ? ) { return value !== null && value !== undefined ; } internal export function isDefinedFrom < T > ( typeCheck ) { return ( value ? ) : value is NonNullable < T > => { if ( value === undefined ) { return false ; } try { return typeCheck ( value ) ; } catch { return false ; } } ; } internal export const round = ( value , fractionDigits = 0 ) : number => { const precision = Math . pow ( 10 , Math . max ( fractionDigits , 0 ) ) ; const scaledValue = Math . floor ( value * precision ) ; return scaledValue / precision ; } ; internal export function getPercentageValue < T > ( ratio , relativeValue , defaultValue ) { if ( typeof ratio === 'number' ) { return Math . abs ( ratio ) ; } const ratioStr = ratio . trim ( ) ; if ( /\d+%$/ . test ( ratioStr ) ) { const percentage = Math . abs ( Number . parseInt ( ratioStr . slice ( 0 , - 1 ) , 10 ) ) ; return relativeValue * ( percentage / 100 ) ; } const num = Number . parseFloat ( ratioStr ) ; return Number . isFinite ( num ) ? Math . abs ( num ) : defaultValue ; } example internal export function keepDistinct < T > ( d , i , a ) { return a . indexOf ( d ) === i ; } public export function toEntries < T extends Record < string , string > , S > ( array , accessor , staticValue ) { return array . reduce < Record < string , S > > ( ( acc , curr ) => { acc [ curr [ accessor ] ] = staticValue ; return acc ; } , { } ) ; } internal export function safeFormat < V = any > ( value , formatter ? ) { if ( formatter ) { try { return formatter ( value ) ; } catch { } } return `${ value }` ; } internal export const range = ( from , to , step ) : number [ ] => Array . from ( { length : Math . abs ( Math . round ( ( to - from ) / ( step || 1 ) ) ) } , ( _ , i ) => from + i * step ) ; const oppositeAlignmentMap = { [ HorizontalAlignment . Left ] : HorizontalAlignment . Right , [ HorizontalAlignment . Right ] : HorizontalAlignment . Left , [ VerticalAlignment . Top ] : VerticalAlignment . Bottom , [ VerticalAlignment . Bottom ] : VerticalAlignment . Top , } ; internal export function getOppositeAlignment < A extends HorizontalAlignment | VerticalAlignment > ( alignment ) { return ( oppositeAlignmentMap [ alignment ] as A ) ?? alignment ; } internal export function isFiniteNumber ( value ) { return Number . isFinite ( value ) ; } internal export function stripUndefined < R extends Record < string , unknown > > ( source ) { return Object . keys ( source ) . reduce ( ( acc , key ) => { const val = source [ key ] ; if ( val !== undefined ) { acc [ key ] = val ; } return acc ; } , { } as R ) ; }