UNPKG

vega-lite

Version:

Vega-Lite is a concise high-level language for interactive visualization.

162 lines (143 loc) 4.52 kB
import {isArray} from 'vega-util'; import { ChannelDef, DatumDef, Field, FieldDef, FieldName, hasConditionalFieldOrDatumDef, isConditionalDef, isFieldDef, isFieldOrDatumDef, isRepeatRef, isSortableFieldDef, ScaleFieldDef, ValueDef, } from '../channeldef.js'; import {Encoding} from '../encoding.js'; import * as log from '../log/index.js'; import {isSortField} from '../sort.js'; import {FacetFieldDef, FacetMapping, isFacetMapping} from '../spec/facet.js'; import {hasProperty} from '../util.js'; export interface RepeaterValue { row?: string; column?: string; repeat?: string; layer?: string; } export function replaceRepeaterInFacet( facet: FacetFieldDef<Field> | FacetMapping<Field>, repeater: RepeaterValue, ): FacetFieldDef<FieldName> | FacetMapping<FieldName> { if (!repeater) { return facet as FacetFieldDef<FieldName>; } if (isFacetMapping(facet)) { return replaceRepeaterInMapping(facet, repeater) as FacetMapping<FieldName>; } return replaceRepeaterInFieldDef(facet, repeater) as FacetFieldDef<FieldName>; } export function replaceRepeaterInEncoding<E extends Encoding<Field>>( encoding: E, repeater: RepeaterValue, ): Encoding<FieldName> { if (!repeater) { return encoding as Encoding<FieldName>; } return replaceRepeaterInMapping(encoding, repeater) as Encoding<FieldName>; } /** * Replaces repeated value and returns if the repeated value is valid. */ function replaceRepeatInProp<T>(prop: keyof T, o: T, repeater: RepeaterValue): T { const val = o[prop]; if (isRepeatRef(val)) { if (val.repeat in repeater) { return {...o, [prop]: repeater[val.repeat]}; } else { log.warn(log.message.noSuchRepeatedValue(val.repeat)); return undefined; } } return o; } /** * Replace repeater values in a field def with the concrete field name. */ function replaceRepeaterInFieldDef(fieldDef: FieldDef<Field>, repeater: RepeaterValue) { fieldDef = replaceRepeatInProp('field', fieldDef, repeater); if (fieldDef === undefined) { // the field def should be ignored return undefined; } else if (fieldDef === null) { return null; } if (isSortableFieldDef(fieldDef) && isSortField(fieldDef.sort)) { const sort = replaceRepeatInProp('field', fieldDef.sort, repeater); fieldDef = { ...fieldDef, ...(sort ? {sort} : {}), }; } return fieldDef as ScaleFieldDef<FieldName>; } function replaceRepeaterInFieldOrDatumDef(def: FieldDef<Field> | DatumDef<Field>, repeater: RepeaterValue) { if (isFieldDef(def)) { return replaceRepeaterInFieldDef(def, repeater); } else { const datumDef = replaceRepeatInProp('datum', def, repeater); if (datumDef !== def && !datumDef.type) { datumDef.type = 'nominal'; } return datumDef; } } function replaceRepeaterInChannelDef(channelDef: ChannelDef<Field>, repeater: RepeaterValue) { if (isFieldOrDatumDef(channelDef)) { const fd = replaceRepeaterInFieldOrDatumDef(channelDef, repeater); if (fd) { return fd; } else if (isConditionalDef<ChannelDef<Field>>(channelDef)) { return {condition: channelDef.condition}; } } else { if (hasConditionalFieldOrDatumDef(channelDef)) { const fd = replaceRepeaterInFieldOrDatumDef(channelDef.condition, repeater); if (fd) { return { ...channelDef, condition: fd, } as ChannelDef; } else { const {condition, ...channelDefWithoutCondition} = channelDef; return channelDefWithoutCondition as ChannelDef; } } return channelDef as ValueDef; } return undefined; } type EncodingOrFacet<F extends Field> = Encoding<F> | FacetMapping<F>; function replaceRepeaterInMapping( mapping: EncodingOrFacet<Field>, repeater: RepeaterValue, ): EncodingOrFacet<FieldName> { const out: EncodingOrFacet<FieldName> = {}; for (const channel in mapping) { if (hasProperty(mapping, channel)) { const channelDef: ChannelDef<Field> | ChannelDef<Field>[] = mapping[channel]; if (isArray(channelDef)) { // array cannot have condition (out as any)[channel] = (channelDef as ChannelDef<Field>[]) // somehow we need to cast it here .map((cd) => replaceRepeaterInChannelDef(cd, repeater)) .filter((cd) => cd); } else { const cd = replaceRepeaterInChannelDef(channelDef, repeater); if (cd !== undefined) { (out as any)[channel] = cd; } } } } return out; }