genesys-spark-chart-components 4.202.0 → 4.203.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (24) hide show
  1. package/dist/cjs/{color-palette-DOJh4cNZ.js → color-palette-CU1chhBt.js} +1 -1
  2. package/dist/cjs/gux-chart-column-beta.cjs.entry.js +1 -1
  3. package/dist/cjs/gux-chart-donut-beta.cjs.entry.js +1 -1
  4. package/dist/cjs/gux-chart-line-beta.cjs.entry.js +1 -1
  5. package/dist/cjs/gux-chart-pie-beta.cjs.entry.js +1 -1
  6. package/dist/cjs/gux-chart-scatter-plot-beta.cjs.entry.js +1 -1
  7. package/dist/cjs/gux-visualization-beta.cjs.entry.js +437 -298
  8. package/dist/esm/{color-palette-CqY6Km9m.js → color-palette-DyPalgli.js} +1 -1
  9. package/dist/esm/gux-chart-column-beta.entry.js +1 -1
  10. package/dist/esm/gux-chart-donut-beta.entry.js +1 -1
  11. package/dist/esm/gux-chart-line-beta.entry.js +1 -1
  12. package/dist/esm/gux-chart-pie-beta.entry.js +1 -1
  13. package/dist/esm/gux-chart-scatter-plot-beta.entry.js +1 -1
  14. package/dist/esm/gux-visualization-beta.entry.js +437 -298
  15. package/dist/genesys-chart-webcomponents/genesys-chart-webcomponents.esm.js +1 -1
  16. package/dist/genesys-chart-webcomponents/p-0613c0d5.entry.js +1 -0
  17. package/dist/genesys-chart-webcomponents/{p-4a5cb50a.entry.js → p-197db998.entry.js} +3 -3
  18. package/dist/genesys-chart-webcomponents/{p-ee1b963c.entry.js → p-39f1db9c.entry.js} +1 -1
  19. package/dist/genesys-chart-webcomponents/{p-eb769e31.entry.js → p-87dd0b8f.entry.js} +1 -1
  20. package/dist/genesys-chart-webcomponents/{p-CqY6Km9m.js → p-DyPalgli.js} +1 -1
  21. package/dist/genesys-chart-webcomponents/{p-5b977d65.entry.js → p-a7d9fe74.entry.js} +1 -1
  22. package/dist/genesys-chart-webcomponents/{p-0bd6f695.entry.js → p-da9e8b8c.entry.js} +1 -1
  23. package/package.json +7 -7
  24. package/dist/genesys-chart-webcomponents/p-b10de83b.entry.js +0 -1
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  var index$2 = require('./index-DEg9dZRu.js');
4
- var colorPalette = require('./color-palette-DOJh4cNZ.js');
4
+ var colorPalette = require('./color-palette-CU1chhBt.js');
5
5
 
6
6
  // Note: This regex matches even invalid JSON strings, but since we’re
7
7
  // working on the output of `JSON.stringify` we know that only valid strings
@@ -44674,7 +44674,7 @@ var expression$1 = {
44674
44674
  }
44675
44675
  };
44676
44676
 
44677
- var version$1$1 = "6.4.1";
44677
+ var version$1$1 = "6.4.2";
44678
44678
  var pkg$1 = {
44679
44679
  version: version$1$1};
44680
44680
 
@@ -44906,6 +44906,12 @@ const entries$1 = Object.entries;
44906
44906
  function isBoolean(b) {
44907
44907
  return b === true || b === false;
44908
44908
  }
44909
+ /**
44910
+ * Returns true if the value is a primitive type.
44911
+ */
44912
+ function isPrimitive(v) {
44913
+ return isString(v) || isNumber$1(v) || isBoolean(v);
44914
+ }
44909
44915
  /**
44910
44916
  * Convert a string into a valid variable name
44911
44917
  */
@@ -46380,6 +46386,10 @@ const MORE_THAN_ONE_SORT = 'Domains that should be unioned has conflicting sort
46380
46386
  const FACETED_INDEPENDENT_DIFFERENT_SOURCES = 'Detected faceted independent scales that union domain of multiple fields from different data sources. We will use the first field. The result view size may be incorrect.';
46381
46387
  const FACETED_INDEPENDENT_SAME_FIELDS_DIFFERENT_SOURCES = 'Detected faceted independent scales that union domain of the same fields from different source. We will assume that this is the same field from a different fork of the same data source. However, if this is not the case, the result view size may be incorrect.';
46382
46388
  const FACETED_INDEPENDENT_SAME_SOURCE = 'Detected faceted independent scales that union domain of multiple fields from the same data source. We will use the first field. The result view size may be incorrect.';
46389
+ // LEGEND
46390
+ function legendValuesUnioned(channelA, channelB) {
46391
+ return `Unioning discrete legend values from ${channelA} and ${channelB}.`;
46392
+ }
46383
46393
  // STACK
46384
46394
  function cannotStackRangedMark(channel) {
46385
46395
  return `Cannot stack "${channel}" if there is already "${channel}2".`;
@@ -48390,7 +48400,7 @@ function getFieldOrDatumDef(channelDef) {
48390
48400
  * Convert type to full, lowercase type, or augment the fieldDef with a default type if missing.
48391
48401
  */
48392
48402
  function initChannelDef(channelDef, channel, config, opt = {}) {
48393
- if (isString(channelDef) || isNumber$1(channelDef) || isBoolean$2(channelDef)) {
48403
+ if (isPrimitive(channelDef)) {
48394
48404
  const primitiveType = isString(channelDef) ? 'string' : isNumber$1(channelDef) ? 'number' : 'boolean';
48395
48405
  warn(primitiveChannelDef(channel, primitiveType, channelDef));
48396
48406
  return { value: channelDef };
@@ -53963,8 +53973,11 @@ function tooltipRefForEncoding(encoding, stack, config, { reactiveGeom } = {}) {
53963
53973
  * Transforms a tooltip value that is an array to a string with line breaks
53964
53974
  */
53965
53975
  function addLineBreaksToTooltip(channelDef, config, expr = 'datum') {
53966
- if (isFieldDef(channelDef) && isDiscrete$1(channelDef.type) && !hasProperty(channelDef, 'format')) {
53967
- const fieldString = `datum["${channelDef.field}"]`;
53976
+ if (isFieldDef(channelDef) &&
53977
+ isDiscrete$1(channelDef.type) &&
53978
+ !getFormatMixins(channelDef).format &&
53979
+ !getFormatMixins(channelDef).formatType) {
53980
+ const fieldString = `${expr}["${channelDef.field}"]`;
53968
53981
  return {
53969
53982
  signal: `isValid(${fieldString}) ? isArray(${fieldString}) ? join(${fieldString}, '\\n') : ${fieldString} : ""+${fieldString}`,
53970
53983
  };
@@ -56985,298 +56998,6 @@ function mergeSymbolType(st1, st2) {
56985
56998
  return st1;
56986
56999
  }
56987
57000
 
56988
- function setLegendEncode(legend, part, vgProp, vgRef) {
56989
- legend.encode ??= {};
56990
- legend.encode[part] ??= {};
56991
- legend.encode[part].update ??= {};
56992
- // @ts-expect-error expression is too complex for typescript to understand
56993
- legend.encode[part].update[vgProp] = vgRef;
56994
- }
56995
- function assembleLegends(model) {
56996
- const legendComponentIndex = model.component.legends;
56997
- const legendByDomain = {};
56998
- for (const channel of keys(legendComponentIndex)) {
56999
- const scaleComponent = model.getScaleComponent(channel);
57000
- const domainHash = stringify$1(scaleComponent.get('domains'));
57001
- if (legendByDomain[domainHash]) {
57002
- for (const mergedLegendComponent of legendByDomain[domainHash]) {
57003
- const merged = mergeLegendComponent(mergedLegendComponent, legendComponentIndex[channel]);
57004
- if (!merged) {
57005
- // If cannot merge, need to add this legend separately
57006
- legendByDomain[domainHash].push(legendComponentIndex[channel]);
57007
- }
57008
- }
57009
- }
57010
- else {
57011
- legendByDomain[domainHash] = [legendComponentIndex[channel].clone()];
57012
- }
57013
- }
57014
- const legends = vals(legendByDomain)
57015
- .flat()
57016
- .map((l) => assembleLegend(l, model.config))
57017
- .filter((l) => l !== undefined);
57018
- return legends;
57019
- }
57020
- function assembleLegend(legendCmpt, config) {
57021
- const { disable, labelExpr, selections, ...legend } = legendCmpt.combine();
57022
- if (disable) {
57023
- return undefined;
57024
- }
57025
- if (config.aria === false && legend.aria == undefined) {
57026
- legend.aria = false;
57027
- }
57028
- if (legend.encode?.symbols) {
57029
- const out = legend.encode.symbols.update;
57030
- if (out.fill && out.fill['value'] !== 'transparent' && !out.stroke && !legend.stroke) {
57031
- // For non color channel's legend, we need to override symbol stroke config from Vega config if stroke channel is not used.
57032
- out.stroke = { value: 'transparent' };
57033
- }
57034
- // Remove properties that the legend is encoding.
57035
- for (const property of LEGEND_SCALE_CHANNELS) {
57036
- if (legend[property]) {
57037
- delete out[property];
57038
- }
57039
- }
57040
- }
57041
- if (!legend.title) {
57042
- // title schema doesn't include null, ''
57043
- delete legend.title;
57044
- }
57045
- if (labelExpr !== undefined) {
57046
- let expr = labelExpr;
57047
- if (legend.encode?.labels?.update && isSignalRef(legend.encode.labels.update.text)) {
57048
- expr = replaceAll(labelExpr, 'datum.label', legend.encode.labels.update.text.signal);
57049
- }
57050
- setLegendEncode(legend, 'labels', 'text', { signal: expr });
57051
- }
57052
- return legend;
57053
- }
57054
-
57055
- function assembleProjections(model) {
57056
- if (isLayerModel(model) || isConcatModel(model)) {
57057
- return assembleProjectionsForModelAndChildren(model);
57058
- }
57059
- else {
57060
- return assembleProjectionForModel(model);
57061
- }
57062
- }
57063
- function assembleProjectionsForModelAndChildren(model) {
57064
- return model.children.reduce((projections, child) => {
57065
- return projections.concat(child.assembleProjections());
57066
- }, assembleProjectionForModel(model));
57067
- }
57068
- function assembleProjectionForModel(model) {
57069
- const component = model.component.projection;
57070
- if (!component || component.merged) {
57071
- return [];
57072
- }
57073
- const projection = component.combine();
57074
- const { name } = projection; // we need to extract name so that it is always present in the output and pass TS type validation
57075
- if (!component.data) {
57076
- // generate custom projection, no automatic fitting
57077
- return [
57078
- {
57079
- name,
57080
- // translate to center by default
57081
- translate: { signal: '[width / 2, height / 2]' },
57082
- // parameters, overwrite default translate if specified
57083
- ...projection,
57084
- },
57085
- ];
57086
- }
57087
- else {
57088
- // generate projection that uses extent fitting
57089
- const size = {
57090
- signal: `[${component.size.map((ref) => ref.signal).join(', ')}]`,
57091
- };
57092
- const fits = component.data.reduce((sources, data) => {
57093
- const source = isSignalRef(data) ? data.signal : `data('${model.lookupDataSource(data)}')`;
57094
- if (!contains(sources, source)) {
57095
- // build a unique list of sources
57096
- sources.push(source);
57097
- }
57098
- return sources;
57099
- }, []);
57100
- if (fits.length <= 0) {
57101
- throw new Error("Projection's fit didn't find any data sources");
57102
- }
57103
- return [
57104
- {
57105
- name,
57106
- size,
57107
- fit: {
57108
- signal: fits.length > 1 ? `[${fits.join(', ')}]` : fits[0],
57109
- },
57110
- ...projection,
57111
- },
57112
- ];
57113
- }
57114
- }
57115
-
57116
- const PROJECTION_PROPERTIES = [
57117
- 'type',
57118
- 'clipAngle',
57119
- 'clipExtent',
57120
- 'center',
57121
- 'rotate',
57122
- 'precision',
57123
- 'reflectX',
57124
- 'reflectY',
57125
- 'coefficient',
57126
- 'distance',
57127
- 'fraction',
57128
- 'lobes',
57129
- 'parallel',
57130
- 'radius',
57131
- 'ratio',
57132
- 'spacing',
57133
- 'tilt',
57134
- ];
57135
-
57136
- class ProjectionComponent extends Split {
57137
- specifiedProjection;
57138
- size;
57139
- data;
57140
- merged = false;
57141
- constructor(name, specifiedProjection, size, data) {
57142
- super({ ...specifiedProjection }, // all explicit properties of projection
57143
- { name });
57144
- this.specifiedProjection = specifiedProjection;
57145
- this.size = size;
57146
- this.data = data;
57147
- }
57148
- /**
57149
- * Whether the projection parameters should fit provided data.
57150
- */
57151
- get isFit() {
57152
- return !!this.data;
57153
- }
57154
- }
57155
-
57156
- function parseProjection(model) {
57157
- model.component.projection = isUnitModel(model) ? parseUnitProjection(model) : parseNonUnitProjections(model);
57158
- }
57159
- function parseUnitProjection(model) {
57160
- if (model.hasProjection) {
57161
- const proj = replaceExprRef(model.specifiedProjection);
57162
- const fit = !(proj && (proj.scale != null || proj.translate != null));
57163
- const size = fit ? [model.getSizeSignalRef('width'), model.getSizeSignalRef('height')] : undefined;
57164
- const data = fit ? gatherFitData(model) : undefined;
57165
- const projComp = new ProjectionComponent(model.projectionName(true), {
57166
- ...replaceExprRef(model.config.projection),
57167
- ...proj,
57168
- }, size, data);
57169
- if (!projComp.get('type')) {
57170
- projComp.set('type', 'equalEarth', false);
57171
- }
57172
- return projComp;
57173
- }
57174
- return undefined;
57175
- }
57176
- function gatherFitData(model) {
57177
- const data = [];
57178
- const { encoding } = model;
57179
- for (const posssiblePair of [
57180
- [LONGITUDE, LATITUDE],
57181
- [LONGITUDE2, LATITUDE2],
57182
- ]) {
57183
- if (getFieldOrDatumDef(encoding[posssiblePair[0]]) || getFieldOrDatumDef(encoding[posssiblePair[1]])) {
57184
- data.push({
57185
- signal: model.getName(`geojson_${data.length}`),
57186
- });
57187
- }
57188
- }
57189
- if (model.channelHasField(SHAPE) && model.typedFieldDef(SHAPE).type === GEOJSON) {
57190
- data.push({
57191
- signal: model.getName(`geojson_${data.length}`),
57192
- });
57193
- }
57194
- if (data.length === 0) {
57195
- // main source is geojson, so we can just use that
57196
- data.push(model.requestDataName(DataSourceType.Main));
57197
- }
57198
- return data;
57199
- }
57200
- function mergeIfNoConflict(first, second) {
57201
- const allPropertiesShared = every(PROJECTION_PROPERTIES, (prop) => {
57202
- // neither has the property
57203
- if (!has$1(first.explicit, prop) && !has$1(second.explicit, prop)) {
57204
- return true;
57205
- }
57206
- // both have property and an equal value for property
57207
- if (has$1(first.explicit, prop) &&
57208
- has$1(second.explicit, prop) &&
57209
- // some properties might be signals or objects and require hashing for comparison
57210
- deepEqual(first.get(prop), second.get(prop))) {
57211
- return true;
57212
- }
57213
- return false;
57214
- });
57215
- const size = deepEqual(first.size, second.size);
57216
- if (size) {
57217
- if (allPropertiesShared) {
57218
- return first;
57219
- }
57220
- else if (deepEqual(first.explicit, {})) {
57221
- return second;
57222
- }
57223
- else if (deepEqual(second.explicit, {})) {
57224
- return first;
57225
- }
57226
- }
57227
- // if all properties don't match, let each unit spec have its own projection
57228
- return null;
57229
- }
57230
- function parseNonUnitProjections(model) {
57231
- if (model.children.length === 0) {
57232
- return undefined;
57233
- }
57234
- let nonUnitProjection;
57235
- // parse all children first
57236
- for (const child of model.children) {
57237
- parseProjection(child);
57238
- }
57239
- // analyze parsed projections, attempt to merge
57240
- const mergable = every(model.children, (child) => {
57241
- const projection = child.component.projection;
57242
- if (!projection) {
57243
- // child layer does not use a projection
57244
- return true;
57245
- }
57246
- else if (!nonUnitProjection) {
57247
- // cached 'projection' is null, cache this one
57248
- nonUnitProjection = projection;
57249
- return true;
57250
- }
57251
- else {
57252
- const merge = mergeIfNoConflict(nonUnitProjection, projection);
57253
- if (merge) {
57254
- nonUnitProjection = merge;
57255
- }
57256
- return !!merge;
57257
- }
57258
- });
57259
- // if cached one and all other children share the same projection,
57260
- if (nonUnitProjection && mergable) {
57261
- // so we can elevate it to the layer level
57262
- const name = model.projectionName(true);
57263
- const modelProjection = new ProjectionComponent(name, nonUnitProjection.specifiedProjection, nonUnitProjection.size, duplicate(nonUnitProjection.data));
57264
- // rename and assign all others as merged
57265
- for (const child of model.children) {
57266
- const projection = child.component.projection;
57267
- if (projection) {
57268
- if (projection.isFit) {
57269
- modelProjection.data.push(...child.component.projection.data);
57270
- }
57271
- child.renameProjection(projection.get('name'), name);
57272
- projection.merged = true;
57273
- }
57274
- }
57275
- return modelProjection;
57276
- }
57277
- return undefined;
57278
- }
57279
-
57280
57001
  function rangeFormula(model, fieldDef, channel, config) {
57281
57002
  if (binRequiresRange(fieldDef, channel)) {
57282
57003
  // read format from axis or legend, if there is no format then use config.numberFormat
@@ -59503,7 +59224,7 @@ function parseSingleChannelDomain(scaleType, domain, model, channel) {
59503
59224
  sort: sort === true || !isObject(sort)
59504
59225
  ? {
59505
59226
  field: model.vgField(channel, {}),
59506
- op: 'min', // min or max doesn't matter since we sort by the start of the bin range
59227
+ op: 'min',
59507
59228
  }
59508
59229
  : sort,
59509
59230
  },
@@ -59835,6 +59556,423 @@ function assembleDomain(model, channel) {
59835
59556
  return mergeDomains(domains);
59836
59557
  }
59837
59558
 
59559
+ function setLegendEncode(legend, part, vgProp, vgRef) {
59560
+ legend.encode ??= {};
59561
+ legend.encode[part] ??= {};
59562
+ legend.encode[part].update ??= {};
59563
+ // @ts-expect-error expression is too complex for typescript to understand
59564
+ legend.encode[part].update[vgProp] = vgRef;
59565
+ }
59566
+ /**
59567
+ * Determines the underlying field name for a given scale channel within a model hierarchy.
59568
+ * @param model - The model to search for the field definition
59569
+ * @param channel - The scale channel (e.g., 'color', 'size', 'shape') to find the field for
59570
+ * @returns The field name if found; otherwise undefined
59571
+ */
59572
+ function getFieldKeyForChannel(model, channel) {
59573
+ if (isUnitModel(model)) {
59574
+ const fd = model.fieldDef(channel);
59575
+ if (fd?.field) {
59576
+ return fd.field;
59577
+ }
59578
+ }
59579
+ // Use explicit fields from children
59580
+ const childFields = (model.children ?? [])
59581
+ .map((child) => getFieldKeyForChannel(child, channel))
59582
+ .filter((f) => !!f);
59583
+ if (childFields.length > 0) {
59584
+ const unique$1 = unique(childFields, hash);
59585
+ if (unique$1.length === 1) {
59586
+ return unique$1[0];
59587
+ }
59588
+ return undefined;
59589
+ }
59590
+ return undefined;
59591
+ }
59592
+ function legendsAreMergeCompatible(model, channelA, channelB) {
59593
+ if (channelA === channelB)
59594
+ return true;
59595
+ const typeA = model.getScaleType(channelA);
59596
+ const typeB = model.getScaleType(channelB);
59597
+ if (!typeA || !typeB)
59598
+ return false;
59599
+ // Only require discrete/continuous compatibility here. Domain handling is done later.
59600
+ const aIsDiscrete = hasDiscreteDomain(typeA);
59601
+ const bIsDiscrete = hasDiscreteDomain(typeB);
59602
+ return aIsDiscrete === bIsDiscrete;
59603
+ }
59604
+ function getLegendGroupKey(fieldKey, channel) {
59605
+ return fieldKey ? `field:${fieldKey}` : `channel:${String(channel)}`;
59606
+ }
59607
+ function extractDiscreteValuesFromDomain(domain) {
59608
+ if (isArray(domain)) {
59609
+ const primitives = domain.filter(isPrimitive);
59610
+ return primitives.length > 0 ? primitives : null;
59611
+ }
59612
+ if (isDataRefUnionedDomain(domain)) {
59613
+ const values = [];
59614
+ values.push(...domain.fields.flatMap((f) => (isArray(f) ? f.filter(isPrimitive) : [])));
59615
+ if (values.length > 0) {
59616
+ return unique(values, hash);
59617
+ }
59618
+ }
59619
+ return null;
59620
+ }
59621
+ /**
59622
+ * Compute the union of discrete values from the domains of two channels.
59623
+ *
59624
+ * @param model - The model to compute the union of discrete values for
59625
+ * @param channelA - The first channel to compute the union of discrete values for
59626
+ * @param channelB - The second channel to compute the union of discrete values for
59627
+ * @returns The union of discrete values
59628
+ */
59629
+ function getDiscreteValuesForChannel(model, channel) {
59630
+ try {
59631
+ const domain = assembleDomain(model, channel);
59632
+ return extractDiscreteValuesFromDomain(domain);
59633
+ }
59634
+ catch {
59635
+ return null;
59636
+ }
59637
+ }
59638
+ function unionDiscreteValuesForChannels(model, channelA, channelB) {
59639
+ const vA = getDiscreteValuesForChannel(model, channelA);
59640
+ const vB = getDiscreteValuesForChannel(model, channelB);
59641
+ return vA && vB ? unique([...vA, ...vB], hash) : null;
59642
+ }
59643
+ function setImplicitLegendValues(cmpt, values, warnMessage) {
59644
+ if (values && values.length > 0) {
59645
+ const valuesProp = cmpt.getWithExplicit('values');
59646
+ if (!valuesProp?.explicit) {
59647
+ if (warnMessage) {
59648
+ warn(warnMessage);
59649
+ }
59650
+ cmpt.set('values', values, false);
59651
+ }
59652
+ }
59653
+ }
59654
+ function domainsExplicitAndEqual(model, channelA, channelB) {
59655
+ const scA = model.getScaleComponent(channelA);
59656
+ const scB = model.getScaleComponent(channelB);
59657
+ if (!scA || !scB)
59658
+ return false;
59659
+ const dA = scA.getWithExplicit('domains');
59660
+ const dB = scB.getWithExplicit('domains');
59661
+ if (!(dA?.explicit && dB?.explicit))
59662
+ return false;
59663
+ const assembledA = assembleDomain(model, channelA);
59664
+ const assembledB = assembleDomain(model, channelB);
59665
+ return hash(assembledA) === hash(assembledB);
59666
+ }
59667
+ /**
59668
+ * Assemble legends for a model. We group legends by the underlying field used by the encoding.
59669
+ *
59670
+ * @param model - The model to assemble legends for
59671
+ * @returns The assembled legends
59672
+ */
59673
+ function assembleLegends(model) {
59674
+ const legendComponentIndex = model.component.legends;
59675
+ const legendsByGroup = {};
59676
+ for (const channel of keys(legendComponentIndex)) {
59677
+ const fieldKey = getFieldKeyForChannel(model, channel);
59678
+ const groupKey = getLegendGroupKey(fieldKey, channel);
59679
+ if (!legendsByGroup[groupKey]) {
59680
+ legendsByGroup[groupKey] = [{ channel, cmpt: legendComponentIndex[channel].clone() }];
59681
+ continue;
59682
+ }
59683
+ let mergedIntoExisting = false;
59684
+ for (const existing of legendsByGroup[groupKey]) {
59685
+ if (!legendsAreMergeCompatible(model, existing.channel, channel)) {
59686
+ continue;
59687
+ }
59688
+ const merged = mergeLegendComponent(existing.cmpt, legendComponentIndex[channel]);
59689
+ if (merged) {
59690
+ const typeA = model.getScaleType(existing.channel);
59691
+ const typeB = model.getScaleType(channel);
59692
+ if (typeA && typeB && hasDiscreteDomain(typeA) && hasDiscreteDomain(typeB)) {
59693
+ if (domainsExplicitAndEqual(model, existing.channel, channel)) {
59694
+ setImplicitLegendValues(existing.cmpt, getDiscreteValuesForChannel(model, existing.channel));
59695
+ }
59696
+ else {
59697
+ setImplicitLegendValues(existing.cmpt, unionDiscreteValuesForChannels(model, existing.channel, channel),
59698
+ // Warn when unioning discrete legend values so that users are aware
59699
+ legendValuesUnioned(existing.channel, channel));
59700
+ }
59701
+ }
59702
+ mergedIntoExisting = true;
59703
+ break;
59704
+ }
59705
+ }
59706
+ if (!mergedIntoExisting) {
59707
+ legendsByGroup[groupKey].push({ channel, cmpt: legendComponentIndex[channel].clone() });
59708
+ }
59709
+ }
59710
+ const legends = vals(legendsByGroup)
59711
+ .flat()
59712
+ .map((entry) => assembleLegend(entry.cmpt, model.config))
59713
+ .filter((l) => l !== undefined);
59714
+ return legends;
59715
+ }
59716
+ function assembleLegend(legendCmpt, config) {
59717
+ const { disable, labelExpr, selections, ...legend } = legendCmpt.combine();
59718
+ if (disable) {
59719
+ return undefined;
59720
+ }
59721
+ if (config.aria === false && legend.aria == undefined) {
59722
+ legend.aria = false;
59723
+ }
59724
+ if (legend.encode?.symbols) {
59725
+ const out = legend.encode.symbols.update;
59726
+ if (out.fill && out.fill['value'] !== 'transparent' && !out.stroke && !legend.stroke) {
59727
+ // For non color channel's legend, we need to override symbol stroke config from Vega config if stroke channel is not used.
59728
+ out.stroke = { value: 'transparent' };
59729
+ }
59730
+ // Remove properties that the legend is encoding.
59731
+ for (const property of LEGEND_SCALE_CHANNELS) {
59732
+ if (legend[property]) {
59733
+ delete out[property];
59734
+ }
59735
+ }
59736
+ }
59737
+ if (!legend.title) {
59738
+ // title schema doesn't include null, ''
59739
+ delete legend.title;
59740
+ }
59741
+ if (labelExpr !== undefined) {
59742
+ let expr = labelExpr;
59743
+ if (legend.encode?.labels?.update && isSignalRef(legend.encode.labels.update.text)) {
59744
+ expr = replaceAll(labelExpr, 'datum.label', legend.encode.labels.update.text.signal);
59745
+ }
59746
+ setLegendEncode(legend, 'labels', 'text', { signal: expr });
59747
+ }
59748
+ return legend;
59749
+ }
59750
+
59751
+ function assembleProjections(model) {
59752
+ if (isLayerModel(model) || isConcatModel(model)) {
59753
+ return assembleProjectionsForModelAndChildren(model);
59754
+ }
59755
+ else {
59756
+ return assembleProjectionForModel(model);
59757
+ }
59758
+ }
59759
+ function assembleProjectionsForModelAndChildren(model) {
59760
+ return model.children.reduce((projections, child) => {
59761
+ return projections.concat(child.assembleProjections());
59762
+ }, assembleProjectionForModel(model));
59763
+ }
59764
+ function assembleProjectionForModel(model) {
59765
+ const component = model.component.projection;
59766
+ if (!component || component.merged) {
59767
+ return [];
59768
+ }
59769
+ const projection = component.combine();
59770
+ const { name } = projection; // we need to extract name so that it is always present in the output and pass TS type validation
59771
+ if (!component.data) {
59772
+ // generate custom projection, no automatic fitting
59773
+ return [
59774
+ {
59775
+ name,
59776
+ // translate to center by default
59777
+ translate: { signal: '[width / 2, height / 2]' },
59778
+ // parameters, overwrite default translate if specified
59779
+ ...projection,
59780
+ },
59781
+ ];
59782
+ }
59783
+ else {
59784
+ // generate projection that uses extent fitting
59785
+ const size = {
59786
+ signal: `[${component.size.map((ref) => ref.signal).join(', ')}]`,
59787
+ };
59788
+ const fits = component.data.reduce((sources, data) => {
59789
+ const source = isSignalRef(data) ? data.signal : `data('${model.lookupDataSource(data)}')`;
59790
+ if (!contains(sources, source)) {
59791
+ // build a unique list of sources
59792
+ sources.push(source);
59793
+ }
59794
+ return sources;
59795
+ }, []);
59796
+ if (fits.length <= 0) {
59797
+ throw new Error("Projection's fit didn't find any data sources");
59798
+ }
59799
+ return [
59800
+ {
59801
+ name,
59802
+ size,
59803
+ fit: {
59804
+ signal: fits.length > 1 ? `[${fits.join(', ')}]` : fits[0],
59805
+ },
59806
+ ...projection,
59807
+ },
59808
+ ];
59809
+ }
59810
+ }
59811
+
59812
+ const PROJECTION_PROPERTIES = [
59813
+ 'type',
59814
+ 'clipAngle',
59815
+ 'clipExtent',
59816
+ 'center',
59817
+ 'rotate',
59818
+ 'precision',
59819
+ 'reflectX',
59820
+ 'reflectY',
59821
+ 'coefficient',
59822
+ 'distance',
59823
+ 'fraction',
59824
+ 'lobes',
59825
+ 'parallel',
59826
+ 'radius',
59827
+ 'ratio',
59828
+ 'spacing',
59829
+ 'tilt',
59830
+ ];
59831
+
59832
+ class ProjectionComponent extends Split {
59833
+ specifiedProjection;
59834
+ size;
59835
+ data;
59836
+ merged = false;
59837
+ constructor(name, specifiedProjection, size, data) {
59838
+ super({ ...specifiedProjection }, // all explicit properties of projection
59839
+ { name });
59840
+ this.specifiedProjection = specifiedProjection;
59841
+ this.size = size;
59842
+ this.data = data;
59843
+ }
59844
+ /**
59845
+ * Whether the projection parameters should fit provided data.
59846
+ */
59847
+ get isFit() {
59848
+ return !!this.data;
59849
+ }
59850
+ }
59851
+
59852
+ function parseProjection(model) {
59853
+ model.component.projection = isUnitModel(model) ? parseUnitProjection(model) : parseNonUnitProjections(model);
59854
+ }
59855
+ function parseUnitProjection(model) {
59856
+ if (model.hasProjection) {
59857
+ const proj = replaceExprRef(model.specifiedProjection);
59858
+ const fit = !(proj && (proj.scale != null || proj.translate != null));
59859
+ const size = fit ? [model.getSizeSignalRef('width'), model.getSizeSignalRef('height')] : undefined;
59860
+ const data = fit ? gatherFitData(model) : undefined;
59861
+ const projComp = new ProjectionComponent(model.projectionName(true), {
59862
+ ...replaceExprRef(model.config.projection),
59863
+ ...proj,
59864
+ }, size, data);
59865
+ if (!projComp.get('type')) {
59866
+ projComp.set('type', 'equalEarth', false);
59867
+ }
59868
+ return projComp;
59869
+ }
59870
+ return undefined;
59871
+ }
59872
+ function gatherFitData(model) {
59873
+ const data = [];
59874
+ const { encoding } = model;
59875
+ for (const posssiblePair of [
59876
+ [LONGITUDE, LATITUDE],
59877
+ [LONGITUDE2, LATITUDE2],
59878
+ ]) {
59879
+ if (getFieldOrDatumDef(encoding[posssiblePair[0]]) || getFieldOrDatumDef(encoding[posssiblePair[1]])) {
59880
+ data.push({
59881
+ signal: model.getName(`geojson_${data.length}`),
59882
+ });
59883
+ }
59884
+ }
59885
+ if (model.channelHasField(SHAPE) && model.typedFieldDef(SHAPE).type === GEOJSON) {
59886
+ data.push({
59887
+ signal: model.getName(`geojson_${data.length}`),
59888
+ });
59889
+ }
59890
+ if (data.length === 0) {
59891
+ // main source is geojson, so we can just use that
59892
+ data.push(model.requestDataName(DataSourceType.Main));
59893
+ }
59894
+ return data;
59895
+ }
59896
+ function mergeIfNoConflict(first, second) {
59897
+ const allPropertiesShared = every(PROJECTION_PROPERTIES, (prop) => {
59898
+ // neither has the property
59899
+ if (!has$1(first.explicit, prop) && !has$1(second.explicit, prop)) {
59900
+ return true;
59901
+ }
59902
+ // both have property and an equal value for property
59903
+ if (has$1(first.explicit, prop) &&
59904
+ has$1(second.explicit, prop) &&
59905
+ // some properties might be signals or objects and require hashing for comparison
59906
+ deepEqual(first.get(prop), second.get(prop))) {
59907
+ return true;
59908
+ }
59909
+ return false;
59910
+ });
59911
+ const size = deepEqual(first.size, second.size);
59912
+ if (size) {
59913
+ if (allPropertiesShared) {
59914
+ return first;
59915
+ }
59916
+ else if (deepEqual(first.explicit, {})) {
59917
+ return second;
59918
+ }
59919
+ else if (deepEqual(second.explicit, {})) {
59920
+ return first;
59921
+ }
59922
+ }
59923
+ // if all properties don't match, let each unit spec have its own projection
59924
+ return null;
59925
+ }
59926
+ function parseNonUnitProjections(model) {
59927
+ if (model.children.length === 0) {
59928
+ return undefined;
59929
+ }
59930
+ let nonUnitProjection;
59931
+ // parse all children first
59932
+ for (const child of model.children) {
59933
+ parseProjection(child);
59934
+ }
59935
+ // analyze parsed projections, attempt to merge
59936
+ const mergable = every(model.children, (child) => {
59937
+ const projection = child.component.projection;
59938
+ if (!projection) {
59939
+ // child layer does not use a projection
59940
+ return true;
59941
+ }
59942
+ else if (!nonUnitProjection) {
59943
+ // cached 'projection' is null, cache this one
59944
+ nonUnitProjection = projection;
59945
+ return true;
59946
+ }
59947
+ else {
59948
+ const merge = mergeIfNoConflict(nonUnitProjection, projection);
59949
+ if (merge) {
59950
+ nonUnitProjection = merge;
59951
+ }
59952
+ return !!merge;
59953
+ }
59954
+ });
59955
+ // if cached one and all other children share the same projection,
59956
+ if (nonUnitProjection && mergable) {
59957
+ // so we can elevate it to the layer level
59958
+ const name = model.projectionName(true);
59959
+ const modelProjection = new ProjectionComponent(name, nonUnitProjection.specifiedProjection, nonUnitProjection.size, duplicate(nonUnitProjection.data));
59960
+ // rename and assign all others as merged
59961
+ for (const child of model.children) {
59962
+ const projection = child.component.projection;
59963
+ if (projection) {
59964
+ if (projection.isFit) {
59965
+ modelProjection.data.push(...child.component.projection.data);
59966
+ }
59967
+ child.renameProjection(projection.get('name'), name);
59968
+ projection.merged = true;
59969
+ }
59970
+ }
59971
+ return modelProjection;
59972
+ }
59973
+ return undefined;
59974
+ }
59975
+
59838
59976
  function assembleScales(model) {
59839
59977
  if (isLayerModel(model) || isConcatModel(model)) {
59840
59978
  // For concat and layer, include scales of children too
@@ -64803,6 +64941,7 @@ var vegaLiteImport = /*#__PURE__*/Object.freeze({
64803
64941
  isInternalField: isInternalField,
64804
64942
  isNullOrFalse: isNullOrFalse,
64805
64943
  isNumeric: isNumeric,
64944
+ isPrimitive: isPrimitive,
64806
64945
  keys: keys,
64807
64946
  logicalExpr: logicalExpr,
64808
64947
  mergeDeep: mergeDeep$1,