vega-lite
Version:
Vega-Lite is a concise high-level language for interactive visualization.
130 lines (113 loc) • 4.07 kB
text/typescript
import type {SignalRef} from 'vega';
import {isString} from 'vega-util';
import {Field} from '../channeldef.js';
import {Config, initConfig} from '../config.js';
import * as log from '../log/index.js';
import {
FacetedUnitSpec,
isLayerSpec,
isUnitSpec,
LayoutSizeMixins,
NonNormalizedSpec,
NormalizedSpec,
RepeatSpec,
TopLevelSpec,
} from '../spec/index.js';
import {AutoSizeParams, AutosizeType, TopLevel} from '../spec/toplevel.js';
import {deepEqual} from '../util.js';
import {NormalizerParams} from './base.js';
import {CoreNormalizer} from './core.js';
import {SelectionCompatibilityNormalizer} from './selectioncompat.js';
import {TopLevelSelectionsNormalizer} from './toplevelselection.js';
export function normalize(
spec: TopLevelSpec & LayoutSizeMixins,
config?: Config<SignalRef>,
): TopLevel<NormalizedSpec> & LayoutSizeMixins {
if (config === undefined) {
config = initConfig(spec.config);
}
const normalizedSpec = normalizeGenericSpec(spec, config);
const {width, height} = spec;
const autosize = normalizeAutoSize(normalizedSpec, {width, height, autosize: spec.autosize}, config);
return {
...normalizedSpec,
...(autosize ? {autosize} : {}),
};
}
const coreNormalizer = new CoreNormalizer();
const selectionCompatNormalizer = new SelectionCompatibilityNormalizer();
const topLevelSelectionNormalizer = new TopLevelSelectionsNormalizer();
/**
* Decompose extended unit specs into composition of pure unit specs.
* And push top-level selection definitions down to unit specs.
*/
function normalizeGenericSpec(
spec: NonNormalizedSpec | FacetedUnitSpec<Field, any> | RepeatSpec,
config: Config<SignalRef> = {},
) {
const normParams = {config};
return topLevelSelectionNormalizer.map(
coreNormalizer.map(selectionCompatNormalizer.map(spec, normParams), normParams),
normParams,
);
}
function _normalizeAutoSize(autosize: AutosizeType | AutoSizeParams) {
return isString(autosize) ? {type: autosize} : (autosize ?? {});
}
/**
* Normalize autosize and deal with width or height == "container".
*/
export function normalizeAutoSize(
spec: TopLevel<NormalizedSpec>,
sizeInfo: {autosize: AutosizeType | AutoSizeParams} & LayoutSizeMixins,
config?: Config,
) {
let {width, height} = sizeInfo;
const isFitCompatible = isUnitSpec(spec) || isLayerSpec(spec);
const autosizeDefault: AutoSizeParams = {};
if (!isFitCompatible) {
// If spec is not compatible with autosize == "fit", discard width/height == container
if (width == 'container') {
log.warn(log.message.containerSizeNonSingle('width'));
width = undefined;
}
if (height == 'container') {
log.warn(log.message.containerSizeNonSingle('height'));
height = undefined;
}
} else {
// Default autosize parameters to fit when width/height is "container"
if (width == 'container' && height == 'container') {
autosizeDefault.type = 'fit';
autosizeDefault.contains = 'padding';
} else if (width == 'container') {
autosizeDefault.type = 'fit-x';
autosizeDefault.contains = 'padding';
} else if (height == 'container') {
autosizeDefault.type = 'fit-y';
autosizeDefault.contains = 'padding';
}
}
const autosize: AutoSizeParams = {
type: 'pad',
...autosizeDefault,
...(config ? _normalizeAutoSize(config.autosize) : {}),
..._normalizeAutoSize(spec.autosize),
};
if (autosize.type === 'fit' && !isFitCompatible) {
log.warn(log.message.FIT_NON_SINGLE);
autosize.type = 'pad';
}
if (width == 'container' && !(autosize.type == 'fit' || autosize.type == 'fit-x')) {
log.warn(log.message.containerSizeNotCompatibleWithAutosize('width'));
}
if (height == 'container' && !(autosize.type == 'fit' || autosize.type == 'fit-y')) {
log.warn(log.message.containerSizeNotCompatibleWithAutosize('height'));
}
// Delete autosize property if it's Vega's default
if (deepEqual(autosize, {type: 'pad'})) {
return undefined;
}
return autosize;
}
export type {NormalizerParams};