@opendata-ai/openchart-engine 6.9.0 → 6.10.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.
package/dist/index.js CHANGED
@@ -774,6 +774,20 @@ function computeBarMarks(spec, scales, _chartArea, _strategy) {
774
774
  const categoryGroups = groupByField(spec.data, yChannel.field);
775
775
  const needsStacking = Array.from(categoryGroups.values()).some((rows) => rows.length > 1);
776
776
  if (needsStacking) {
777
+ const stackDisabled = xChannel.stack === null || xChannel.stack === false;
778
+ if (stackDisabled) {
779
+ return computeGroupedBars(
780
+ spec.data,
781
+ xChannel.field,
782
+ yChannel.field,
783
+ colorField,
784
+ xScale,
785
+ yScale,
786
+ bandwidth,
787
+ baseline,
788
+ scales
789
+ );
790
+ }
777
791
  return computeStackedBars(
778
792
  spec.data,
779
793
  xChannel.field,
@@ -834,6 +848,51 @@ function computeStackedBars(data, valueField, categoryField, colorField, xScale,
834
848
  }
835
849
  return marks;
836
850
  }
851
+ function computeGroupedBars(data, valueField, categoryField, colorField, xScale, yScale, bandwidth, baseline, scales) {
852
+ const marks = [];
853
+ const categoryGroups = groupByField(data, categoryField);
854
+ const groupIndexMap = /* @__PURE__ */ new Map();
855
+ for (const row of data) {
856
+ const key = String(row[colorField] ?? "");
857
+ if (!groupIndexMap.has(key)) {
858
+ groupIndexMap.set(key, groupIndexMap.size);
859
+ }
860
+ }
861
+ const groupCount = groupIndexMap.size;
862
+ if (groupCount === 0) return marks;
863
+ const gap = Math.min(1, bandwidth * 0.05);
864
+ const subBandHeight = Math.max((bandwidth - gap * (groupCount - 1)) / groupCount, MIN_BAR_WIDTH);
865
+ for (const [category, rows] of categoryGroups) {
866
+ const bandY = yScale(category);
867
+ if (bandY === void 0) continue;
868
+ for (const row of rows) {
869
+ const groupKey = String(row[colorField] ?? "");
870
+ const value2 = Number(row[valueField] ?? 0);
871
+ if (!Number.isFinite(value2)) continue;
872
+ const groupIndex = groupIndexMap.get(groupKey) ?? 0;
873
+ const color2 = getColor(scales, groupKey);
874
+ const xPos = value2 >= 0 ? baseline : xScale(value2);
875
+ const barWidth = Math.max(Math.abs(xScale(value2) - baseline), MIN_BAR_WIDTH);
876
+ const subY = bandY + groupIndex * (subBandHeight + gap);
877
+ const aria = {
878
+ label: `${category}, ${groupKey}: ${formatBarValue(value2)}`
879
+ };
880
+ marks.push({
881
+ type: "rect",
882
+ x: xPos,
883
+ y: subY,
884
+ width: barWidth,
885
+ height: subBandHeight,
886
+ fill: color2,
887
+ cornerRadius: 2,
888
+ data: row,
889
+ aria,
890
+ orient: "horizontal"
891
+ });
892
+ }
893
+ }
894
+ return marks;
895
+ }
837
896
  function computeColoredBars(data, valueField, categoryField, colorField, xScale, yScale, bandwidth, baseline, scales) {
838
897
  const marks = [];
839
898
  for (const row of data) {
@@ -1072,6 +1131,20 @@ function computeColumnMarks(spec, scales, _chartArea, _strategy) {
1072
1131
  const categoryGroups = groupByField(spec.data, xChannel.field);
1073
1132
  const needsStacking = Array.from(categoryGroups.values()).some((rows) => rows.length > 1);
1074
1133
  if (needsStacking) {
1134
+ const stackDisabled = yChannel.stack === null || yChannel.stack === false;
1135
+ if (stackDisabled) {
1136
+ return computeGroupedColumns(
1137
+ spec.data,
1138
+ xChannel.field,
1139
+ yChannel.field,
1140
+ colorField,
1141
+ xScale,
1142
+ yScale,
1143
+ bandwidth,
1144
+ baseline,
1145
+ scales
1146
+ );
1147
+ }
1075
1148
  return computeStackedColumns(
1076
1149
  spec.data,
1077
1150
  xChannel.field,
@@ -1179,6 +1252,55 @@ function computeColoredColumns(data, categoryField, valueField, colorField, xSca
1179
1252
  }
1180
1253
  return marks;
1181
1254
  }
1255
+ function computeGroupedColumns(data, categoryField, valueField, colorField, xScale, yScale, bandwidth, baseline, scales) {
1256
+ const marks = [];
1257
+ const categoryGroups = groupByField(data, categoryField);
1258
+ const groupIndexMap = /* @__PURE__ */ new Map();
1259
+ for (const row of data) {
1260
+ const key = String(row[colorField] ?? "");
1261
+ if (!groupIndexMap.has(key)) {
1262
+ groupIndexMap.set(key, groupIndexMap.size);
1263
+ }
1264
+ }
1265
+ const groupCount = groupIndexMap.size;
1266
+ if (groupCount === 0) return marks;
1267
+ const gap = Math.min(1, bandwidth * 0.05);
1268
+ const subBandWidth = Math.max(
1269
+ (bandwidth - gap * (groupCount - 1)) / groupCount,
1270
+ MIN_COLUMN_HEIGHT
1271
+ );
1272
+ for (const [category, rows] of categoryGroups) {
1273
+ const bandX = xScale(category);
1274
+ if (bandX === void 0) continue;
1275
+ for (const row of rows) {
1276
+ const groupKey = String(row[colorField] ?? "");
1277
+ const value2 = Number(row[valueField] ?? 0);
1278
+ if (!Number.isFinite(value2)) continue;
1279
+ const groupIndex = groupIndexMap.get(groupKey) ?? 0;
1280
+ const color2 = getColor(scales, groupKey);
1281
+ const yPos = yScale(value2);
1282
+ const columnHeight = Math.max(Math.abs(baseline - yPos), MIN_COLUMN_HEIGHT);
1283
+ const y2 = value2 >= 0 ? yPos : baseline;
1284
+ const subX = bandX + groupIndex * (subBandWidth + gap);
1285
+ const aria = {
1286
+ label: `${category}, ${groupKey}: ${formatColumnValue(value2)}`
1287
+ };
1288
+ marks.push({
1289
+ type: "rect",
1290
+ x: subX,
1291
+ y: y2,
1292
+ width: subBandWidth,
1293
+ height: columnHeight,
1294
+ fill: color2,
1295
+ cornerRadius: 2,
1296
+ data: row,
1297
+ aria,
1298
+ orient: "vertical"
1299
+ });
1300
+ }
1301
+ }
1302
+ return marks;
1303
+ }
1182
1304
  function computeStackedColumns(data, categoryField, valueField, colorField, xScale, yScale, bandwidth, _baseline, scales) {
1183
1305
  const marks = [];
1184
1306
  const categoryGroups = groupByField(data, categoryField);
@@ -8130,7 +8252,8 @@ function computeScales(spec, chartArea, data) {
8130
8252
  }
8131
8253
  if (encoding.x) {
8132
8254
  let xData = data;
8133
- if (spec.markType === "bar" && encoding.color && encoding.x.type === "quantitative") {
8255
+ const xStackDisabled = encoding.x.stack === null || encoding.x.stack === false;
8256
+ if (spec.markType === "bar" && encoding.color && encoding.x.type === "quantitative" && !xStackDisabled) {
8134
8257
  const yField = encoding.y?.field;
8135
8258
  const xField = encoding.x.field;
8136
8259
  if (yField) {
@@ -8158,7 +8281,8 @@ function computeScales(spec, chartArea, data) {
8158
8281
  if (encoding.y) {
8159
8282
  let yData = data;
8160
8283
  const isVerticalBar = spec.markType === "bar" && (encoding.x?.type === "nominal" || encoding.x?.type === "ordinal") && encoding.y.type === "quantitative";
8161
- if ((isVerticalBar || spec.markType === "area") && encoding.color && encoding.y.type === "quantitative") {
8284
+ const yStackDisabled = encoding.y.stack === null || encoding.y.stack === false;
8285
+ if ((isVerticalBar || spec.markType === "area") && encoding.color && encoding.y.type === "quantitative" && !yStackDisabled) {
8162
8286
  const xField = encoding.x?.field;
8163
8287
  const yField = encoding.y.field;
8164
8288
  if (xField) {