import {ScaleType, SignalRef} from 'vega'; import {array} from 'vega-util'; import {AxisConfig} from '../../axis.js'; import {PositionScaleChannel} from '../../channel.js'; import {Config, StyleConfigIndex} from '../../config.js'; import {isQuantitative} from '../../scale.js'; import {keys, titleCase} from '../../util.js'; import {isSignalRef} from '../../vega.schema.js'; import {getStyleConfig, signalOrStringValue} from '../common.js'; function getAxisConfigFromConfigTypes( configTypes: string[], config: Config, channel: 'x' | 'y', orient: string | SignalRef, ) { // TODO: add special casing to add conditional value based on orient signal return Object.assign.apply(null, [ {}, ...configTypes.map((configType) => { if (configType === 'axisOrient') { const orient1 = channel === 'x' ? 'bottom' : 'left'; const orientConfig1 = config[channel === 'x' ? 'axisBottom' : 'axisLeft'] || {}; const orientConfig2 = config[channel === 'x' ? 'axisTop' : 'axisRight'] || {}; const props = new Set([...keys(orientConfig1), ...keys(orientConfig2)]); const conditionalOrientAxisConfig = {}; for (const prop of props.values()) { (conditionalOrientAxisConfig as any)[prop] = { // orient is surely signal in this case signal: `${(orient as any)['signal']} === "${orient1}" ? ${signalOrStringValue( orientConfig1[prop], )} : ${signalOrStringValue(orientConfig2[prop])}`, }; } return conditionalOrientAxisConfig; } return (config as any)[configType]; }), ]); } export type AxisConfigs = ReturnType; export function getAxisConfigs( channel: PositionScaleChannel, scaleType: ScaleType, orient: string | SignalRef, config: Config, ) { const typeBasedConfigTypes = scaleType === 'band' ? ['axisDiscrete', 'axisBand'] : scaleType === 'point' ? ['axisDiscrete', 'axisPoint'] : isQuantitative(scaleType) ? ['axisQuantitative'] : scaleType === 'time' || scaleType === 'utc' ? ['axisTemporal'] : []; const axisChannel = channel === 'x' ? 'axisX' : 'axisY'; const axisOrient = isSignalRef(orient) ? 'axisOrient' : `axis${titleCase(orient)}`; // axisTop, axisBottom, ... const vlOnlyConfigTypes = [ // technically Vega does have axisBand, but if we make another separation here, // it will further introduce complexity in the code ...typeBasedConfigTypes, ...typeBasedConfigTypes.map((c) => axisChannel + c.substr(4)), ]; const vgConfigTypes = ['axis', axisOrient, axisChannel]; return { vlOnlyAxisConfig: getAxisConfigFromConfigTypes(vlOnlyConfigTypes, config, channel, orient), vgAxisConfig: getAxisConfigFromConfigTypes(vgConfigTypes, config, channel, orient), axisConfigStyle: getAxisConfigStyle([...vgConfigTypes, ...vlOnlyConfigTypes], config), }; } export function getAxisConfigStyle(axisConfigTypes: string[], config: Config) { const toMerge = [{}]; for (const configType of axisConfigTypes) { // TODO: add special casing to add conditional value based on orient signal let style = (config as any)[configType]?.style; if (style) { style = array(style); for (const s of style) { toMerge.push(config.style[s]); } } } return Object.assign.apply(null, toMerge); } export function getAxisConfig( property: keyof AxisConfig, styleConfigIndex: StyleConfigIndex, style: string | string[], axisConfigs: Partial = {}, ): {configFrom?: string; configValue?: any} { const styleConfig = getStyleConfig(property, style, styleConfigIndex); if (styleConfig !== undefined) { return { configFrom: 'style', configValue: styleConfig, }; } for (const configFrom of ['vlOnlyAxisConfig', 'vgAxisConfig', 'axisConfigStyle'] as const) { if (axisConfigs[configFrom]?.[property] !== undefined) { return {configFrom, configValue: axisConfigs[configFrom][property]}; } } return {}; }