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