@quillsql/react 2.16.46 → 2.16.47

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.cjs CHANGED
@@ -15116,11 +15116,11 @@ function isValidPivot(pivot) {
15116
15116
  reason: 'Value field cannot be empty when aggregation is not "count" or "percentage"'
15117
15117
  };
15118
15118
  } else if (pivot.aggregations?.some(
15119
- (a) => a.aggregationType === "percentage" && !a.valueField && (!pivot.rowField || pivot.columnField)
15119
+ (a) => a.aggregationType === "percentage" && !a.valueField && !pivot.rowField
15120
15120
  )) {
15121
15121
  return {
15122
15122
  valid: false,
15123
- reason: "Empty percentage field only supported for pivot tables with a row field and no column field"
15123
+ reason: "Empty percentage field only supported for pivot tables with a row field"
15124
15124
  };
15125
15125
  }
15126
15126
  return { valid: true, reason: "" };
@@ -16403,7 +16403,7 @@ function formatAggregationTypeForDisplay(aggregationType) {
16403
16403
  case "max":
16404
16404
  return "Max";
16405
16405
  case "percentage":
16406
- return "Percentage";
16406
+ return "Percent";
16407
16407
  default:
16408
16408
  return aggregationType ? capitalize(String(aggregationType)) : "Value";
16409
16409
  }
@@ -16445,6 +16445,21 @@ function getDefaultPivotMeasureLabel(report) {
16445
16445
  return pivot.columnField ? "Count" : `Count ${snakeAndCamelCaseToTitleCase(tableNames[0] ?? "table")}`;
16446
16446
  }
16447
16447
  const target = valueField !== void 0 && String(valueField).trim() ? resolveValueFieldDisplayLabel(report, valueField) : tableNames.length > 0 ? tableNames.map((n) => snakeAndCamelCaseToTitleCase(n)).join(", ") : "table";
16448
+ if (aggregationType.toLowerCase() === "percentage") {
16449
+ const valueField2 = String(
16450
+ fromAggregations?.valueField2 ?? ""
16451
+ ).trim();
16452
+ if (valueField && valueField2) {
16453
+ return `Percent ${target} of ${resolveValueFieldDisplayLabel(report, valueField2)}`;
16454
+ }
16455
+ const valueFieldType = String(
16456
+ fromAggregations?.valueFieldType ?? ""
16457
+ ).trim().toLowerCase();
16458
+ if (valueField && valueFieldType === "bool") {
16459
+ return `Percent ${target}`;
16460
+ }
16461
+ return `Percent of Total ${target}`;
16462
+ }
16448
16463
  return target ? `${aggWord} ${target}` : aggWord;
16449
16464
  }
16450
16465
  function humanizeLegacyPivotMeasureLabelFragment(text) {
@@ -16452,7 +16467,10 @@ function humanizeLegacyPivotMeasureLabelFragment(text) {
16452
16467
  if (!t) return t;
16453
16468
  const m = t.match(LEGACY_AGG_SNAKE_FIELD);
16454
16469
  if (!m) return t;
16455
- return `${formatAggregationTypeForDisplay(m[1])} ${snakeAndCamelCaseToTitleCase(m[2])}`;
16470
+ if (String(m[1]).toLowerCase() === "percentage") {
16471
+ return `Percent of Total ${snakeAndCamelCaseToTitleCase(m[2] ?? "")}`;
16472
+ }
16473
+ return `${formatAggregationTypeForDisplay(m[1])} ${snakeAndCamelCaseToTitleCase(m[2] ?? "")}`;
16456
16474
  }
16457
16475
  function humanizeChartSeriesTooltipLabel(label) {
16458
16476
  const s = String(label ?? "").trim();
@@ -59847,7 +59865,8 @@ var AGGREGATION_OPTION_TYPES = [
59847
59865
  "average",
59848
59866
  "count",
59849
59867
  "max",
59850
- "min"
59868
+ "min",
59869
+ "percentage"
59851
59870
  ];
59852
59871
  var QUERY_BUILDER_MULTI_VALUE_OPERATORS = /* @__PURE__ */ new Set(["in", "notin"]);
59853
59872
  var TABLE_FORMAT_CACHE_MAX_ENTRIES = 5e3;
@@ -60337,9 +60356,31 @@ function buildPivotAggregationsWithFieldTables({
60337
60356
  schemaTables,
60338
60357
  preferredTables
60339
60358
  }) || void 0;
60359
+ const isPercentageAggregation = String(aggregation.aggregationType ?? "").toLowerCase() === "percentage";
60360
+ const typeFallbackColumns = [
60361
+ ...sourceReport?.columnInternal ?? sourceReport?.columns ?? []
60362
+ ];
60363
+ const valueFieldType = String(
60364
+ aggregation.valueFieldType ?? ""
60365
+ ).trim() || (isPercentageAggregation ? resolveFieldType({
60366
+ field: aggregation.valueField,
60367
+ table: valueFieldTable,
60368
+ schemaTables,
60369
+ fallbackColumns: typeFallbackColumns
60370
+ }) : void 0);
60371
+ const valueField2Type = String(
60372
+ aggregation.valueField2Type ?? ""
60373
+ ).trim() || (isPercentageAggregation ? resolveFieldType({
60374
+ field: aggregation.valueField2,
60375
+ table: valueFieldTable,
60376
+ schemaTables,
60377
+ fallbackColumns: typeFallbackColumns
60378
+ }) : void 0);
60340
60379
  result.push({
60341
60380
  ...aggregationWithoutPivotTables,
60342
60381
  aggregationType: aggregation.aggregationType,
60382
+ ...valueFieldType ? { valueFieldType } : {},
60383
+ ...valueField2Type ? { valueField2Type } : {},
60343
60384
  ...valueFieldTable ? { valueFieldTable } : {}
60344
60385
  });
60345
60386
  }
@@ -60383,8 +60424,9 @@ function getPivotAggregationSortFields(aggregationState, resolvedColumnField) {
60383
60424
  const aggregationType = String(aggregation.aggregationType ?? "").trim();
60384
60425
  if (!aggregationType) continue;
60385
60426
  const valueField = String(aggregation.valueField ?? "").trim();
60386
- const canonicalBaseField = valueField || "count";
60387
- const sortField = hasMultipleAggregations && aggregationType.toLowerCase() !== "count" ? `${canonicalBaseField}_${aggregationType}` : canonicalBaseField;
60427
+ const isValuelessPercentage = !valueField && aggregationType.toLowerCase() === "percentage";
60428
+ const canonicalBaseField = valueField || (isValuelessPercentage ? "percentage" : "count");
60429
+ const sortField = isValuelessPercentage ? "percentage" : hasMultipleAggregations && aggregationType.toLowerCase() !== "count" ? `${canonicalBaseField}_${aggregationType}` : canonicalBaseField;
60388
60430
  if (!seen.has(sortField)) {
60389
60431
  seen.add(sortField);
60390
60432
  fields.push(sortField);
@@ -60403,8 +60445,9 @@ function getPivotAggregationSortLabelOverrides(aggregationState, aggregationTabl
60403
60445
  const aggregationType = String(aggregation.aggregationType ?? "").trim();
60404
60446
  if (!aggregationType) continue;
60405
60447
  const valueField = String(aggregation.valueField ?? "").trim();
60406
- const canonicalBaseField = valueField || "count";
60407
- const sortField = hasMultipleAggregations && aggregationType.toLowerCase() !== "count" ? `${canonicalBaseField}_${aggregationType}` : canonicalBaseField;
60448
+ const isValuelessPercentage = !valueField && aggregationType.toLowerCase() === "percentage";
60449
+ const canonicalBaseField = valueField || (isValuelessPercentage ? "percentage" : "count");
60450
+ const sortField = isValuelessPercentage ? "percentage" : hasMultipleAggregations && aggregationType.toLowerCase() !== "count" ? `${canonicalBaseField}_${aggregationType}` : canonicalBaseField;
60408
60451
  if (labelBySortField.has(sortField)) continue;
60409
60452
  if (valueField || aggregationType.toLowerCase() !== "count") continue;
60410
60453
  const tableHint = String(aggregationTablesByIndex[index] ?? "").trim();
@@ -60558,6 +60601,9 @@ function trimAggregationSuffix(value, aggregationType) {
60558
60601
  }
60559
60602
  function getAggregationFieldKey(aggregation, hasMultipleAggregations) {
60560
60603
  if (!aggregation.aggregationType) return null;
60604
+ if (!aggregation.valueField && aggregation.aggregationType === "percentage") {
60605
+ return "percentage";
60606
+ }
60561
60607
  if (aggregation.valueField) {
60562
60608
  return hasMultipleAggregations ? `${aggregation.valueField}_${aggregation.aggregationType}` : aggregation.valueField;
60563
60609
  }
@@ -60678,6 +60724,22 @@ function buildAggregationLabel(aggregation, report) {
60678
60724
  }
60679
60725
  const rawTarget = aggregation.valueField ?? (tableNames.length > 0 ? tableNames.join(", ") : "table");
60680
60726
  const target = report ? aggregation.valueField ? resolveValueFieldDisplayLabel(report, aggregation.valueField) : rawTarget : rawTarget;
60727
+ if (String(aggregationType).toLowerCase() === "percentage") {
60728
+ const valueField2 = String(
60729
+ aggregation.valueField2 ?? ""
60730
+ ).trim();
60731
+ if (aggregation.valueField && valueField2) {
60732
+ const denominator = report ? resolveValueFieldDisplayLabel(report, valueField2) : valueField2;
60733
+ return `Percent ${target} of ${denominator}`;
60734
+ }
60735
+ const valueFieldType = String(
60736
+ aggregation.valueFieldType ?? ""
60737
+ ).trim().toLowerCase();
60738
+ if (aggregation.valueField && valueFieldType === "bool") {
60739
+ return `Percent ${target}`;
60740
+ }
60741
+ return `Percent of Total ${target}`;
60742
+ }
60681
60743
  return target ? `${aggWord} ${target}` : aggWord;
60682
60744
  }
60683
60745
  function normalizePivotColumnDisplayValue(value) {
@@ -62013,7 +62075,8 @@ function processPivotState(next, options = {}) {
62013
62075
  const {
62014
62076
  nextState,
62015
62077
  promoteColumnToRow = false,
62016
- fallbackColumnGroupBy
62078
+ fallbackColumnGroupBy,
62079
+ isBoolAggregationField
62017
62080
  } = options;
62018
62081
  const normalizedNext = { ...next };
62019
62082
  const isClearingRowGrouping = nextState?.groupRowsBy !== void 0 && String(nextState.groupRowsBy ?? "").trim().length === 0;
@@ -62056,6 +62119,29 @@ function processPivotState(next, options = {}) {
62056
62119
  normalizedNext.aggregationState = [{ aggregationType: "count" }];
62057
62120
  normalizedNext.aggregationTablesByIndex = [void 0];
62058
62121
  }
62122
+ if (!resolvedRowField) {
62123
+ normalizedNext.aggregationState = normalizedNext.aggregationState.map(
62124
+ (aggregation, index) => {
62125
+ if (String(aggregation.aggregationType ?? "").toLowerCase() !== "percentage") {
62126
+ return aggregation;
62127
+ }
62128
+ const valueField = String(aggregation.valueField ?? "").trim();
62129
+ if (!valueField) {
62130
+ return { ...aggregation, aggregationType: "count" };
62131
+ }
62132
+ const valueField2 = String(aggregation.valueField2 ?? "").trim();
62133
+ if (valueField2 && valueField2 !== valueField) {
62134
+ return aggregation;
62135
+ }
62136
+ const table = String(aggregation.valueFieldTable ?? "").trim() || String(normalizedNext.aggregationTablesByIndex[index] ?? "").trim() || void 0;
62137
+ const isBool = isBoolAggregationField?.(valueField, table);
62138
+ if (isBool === false) {
62139
+ return { ...aggregation, aggregationType: "sum" };
62140
+ }
62141
+ return aggregation;
62142
+ }
62143
+ );
62144
+ }
62059
62145
  if (isSettingRowGrouping && normalizedNext.aggregationState.length > 0) {
62060
62146
  normalizedNext.aggregationState = [...normalizedNext.aggregationState];
62061
62147
  }
@@ -63310,6 +63396,19 @@ function useReport(reportIdArg, options = {}) {
63310
63396
  const customFieldsRef = (0, import_react62.useRef)(schemaData.customFields);
63311
63397
  schemaForReportBuilderStateRef.current = schemaForReportBuilderState;
63312
63398
  customFieldsRef.current = schemaData.customFields;
63399
+ const isBoolAggregationField = (0, import_react62.useCallback)(
63400
+ (field, table2) => {
63401
+ const fieldType = resolveFieldType({
63402
+ field,
63403
+ table: table2,
63404
+ schemaTables: schemaForReportBuilderStateRef.current
63405
+ });
63406
+ if (!fieldType) return void 0;
63407
+ const normalized = String(fieldType).trim().toLowerCase();
63408
+ return isBoolType(normalized) || normalized === "boolean";
63409
+ },
63410
+ []
63411
+ );
63313
63412
  const schemaForeignKeyMap = (0, import_react62.useMemo)(
63314
63413
  () => getSchemaForeignKeyMapping(schemaForReportBuilderState),
63315
63414
  [schemaForReportBuilderState]
@@ -64217,9 +64316,10 @@ function useReport(reportIdArg, options = {}) {
64217
64316
  const explicitTable = getPivotGroupOptionTable(targetRaw);
64218
64317
  const field = explicitTable ? decodePivotGroupOptionValue(targetRaw) : targetRaw;
64219
64318
  if (!field) return {};
64319
+ const fieldForTableResolution = field.includes(":") ? String(field.split(":")[0] ?? "").trim() : field;
64220
64320
  const table2 = explicitTable ?? optionTableLookup.get(rawValue) ?? resolveFieldTable({
64221
64321
  report: sourceReport,
64222
- field,
64322
+ field: fieldForTableResolution,
64223
64323
  schemaTables: schemaForReportBuilderState,
64224
64324
  preferredTables
64225
64325
  }) ?? void 0;
@@ -64234,7 +64334,19 @@ function useReport(reportIdArg, options = {}) {
64234
64334
  if (target === "table") {
64235
64335
  return table2 ? { table: table2 } : preferredTables[0] ? { table: preferredTables[0] } : {};
64236
64336
  }
64237
- if (aggregationType !== "count") {
64337
+ if (aggregationType === "percentage" && target.includes(":")) {
64338
+ const [numeratorRaw, denominatorRaw] = target.split(":");
64339
+ const numerator = String(numeratorRaw ?? "").trim();
64340
+ const denominator = String(denominatorRaw ?? "").trim();
64341
+ if (numerator && denominator) {
64342
+ return {
64343
+ valueField: numerator,
64344
+ valueField2: denominator,
64345
+ ...table2 ? { table: table2 } : {}
64346
+ };
64347
+ }
64348
+ }
64349
+ if (aggregationType !== "count" && aggregationType !== "percentage") {
64238
64350
  return {
64239
64351
  valueField: target,
64240
64352
  ...table2 ? { table: table2 } : {}
@@ -64418,7 +64530,9 @@ function useReport(reportIdArg, options = {}) {
64418
64530
  const pivotAggregationsFromSource = Array.isArray(sourceReport.pivot?.aggregations) ? sourceReport.pivot.aggregations : sourceReport.pivot?.aggregationType ? [
64419
64531
  {
64420
64532
  valueField: sourceReport.pivot?.valueField,
64533
+ valueFieldType: sourceReport.pivot?.valueFieldType,
64421
64534
  valueField2: sourceReport.pivot?.valueField2,
64535
+ valueField2Type: sourceReport.pivot?.valueField2Type,
64422
64536
  aggregationType: sourceReport.pivot?.aggregationType
64423
64537
  }
64424
64538
  ] : [];
@@ -64503,7 +64617,8 @@ function useReport(reportIdArg, options = {}) {
64503
64617
  chartType: prev.chartType ?? sourceReport.chartType
64504
64618
  };
64505
64619
  const normalized = processPivotState(nextFromSource, {
64506
- promoteColumnToRow: shouldPromoteColumnToRowFromPendingFlag
64620
+ promoteColumnToRow: shouldPromoteColumnToRowFromPendingFlag,
64621
+ isBoolAggregationField
64507
64622
  });
64508
64623
  return normalized;
64509
64624
  }
@@ -64843,6 +64958,10 @@ function useReport(reportIdArg, options = {}) {
64843
64958
  aggregation.valueField,
64844
64959
  aggregationTablesByIndex[index]
64845
64960
  );
64961
+ appendPivotColumn(
64962
+ aggregation.valueField2,
64963
+ aggregationTablesByIndex[index]
64964
+ );
64846
64965
  });
64847
64966
  const joinColumns = getJoinColumnsFromTables(effectiveReportBuilderTables);
64848
64967
  const useWideTableColumns = expandReportBuilderColumnsForFlatTable && !pivotState && String(chartType ?? "").toLowerCase() === "table";
@@ -65172,9 +65291,13 @@ function useReport(reportIdArg, options = {}) {
65172
65291
  const aggregationType = String(aggregation.aggregationType);
65173
65292
  const normalizedAggregationType = aggregationType.toLowerCase();
65174
65293
  const tableHint = String(aggregationTablesByIndex[index] ?? "").trim();
65175
- const target = aggregation.valueField && aggregation.valueField.trim().length > 0 ? aggregation.valueField : normalizedAggregationType === "count" ? tableHint || aggregationFallbackTableName : aggregationFallbackTarget;
65294
+ const valueField = String(aggregation.valueField ?? "").trim();
65295
+ const valueField2 = String(aggregation.valueField2 ?? "").trim();
65296
+ const isPercentageRatio = normalizedAggregationType === "percentage" && Boolean(valueField) && Boolean(valueField2);
65297
+ const target = isPercentageRatio ? `${valueField}:${valueField2}` : valueField ? valueField : normalizedAggregationType === "count" || normalizedAggregationType === "percentage" ? tableHint || aggregationFallbackTableName : aggregationFallbackTarget;
65298
+ const isBoolPercentage = normalizedAggregationType === "percentage" && Boolean(valueField) && isBoolAggregationField(valueField, tableHint || void 0) === true;
65176
65299
  return {
65177
- label: `${formatAggregationTypeForDisplay(aggregationType)} ${toTitleCaseLabel(target)}`,
65300
+ label: isPercentageRatio ? `Percent ${toTitleCaseLabel(valueField)} of ${toTitleCaseLabel(valueField2)}` : normalizedAggregationType === "percentage" ? isBoolPercentage ? `Percent ${toTitleCaseLabel(target)}` : `Percent of Total ${toTitleCaseLabel(target)}` : `${formatAggregationTypeForDisplay(aggregationType)} ${toTitleCaseLabel(target)}`,
65178
65301
  value: `${aggregationType}:${target}`
65179
65302
  };
65180
65303
  });
@@ -65182,7 +65305,8 @@ function useReport(reportIdArg, options = {}) {
65182
65305
  aggregationFallbackTableName,
65183
65306
  aggregationFallbackTarget,
65184
65307
  aggregationState,
65185
- aggregationTablesByIndex
65308
+ aggregationTablesByIndex,
65309
+ isBoolAggregationField
65186
65310
  ]);
65187
65311
  const { aggregationBaseOptions, aggregationOptionTableLookup } = (0, import_react62.useMemo)(() => {
65188
65312
  const joinScoped = fieldOptionsAllowedTableNames ? joinCompatibleSchemaTableNames.filter(
@@ -65208,17 +65332,34 @@ function useReport(reportIdArg, options = {}) {
65208
65332
  if (tableName && !tableLookup.has(countValue)) {
65209
65333
  tableLookup.set(countValue, tableName);
65210
65334
  }
65335
+ if (resolvedGroupRowsBy) {
65336
+ const percentageValue = `percentage:${countTarget}`;
65337
+ if (!seen.has(percentageValue)) {
65338
+ options2.push({
65339
+ value: percentageValue,
65340
+ label: `Percent of Total ${toTitleCaseLabel(countTarget)}`
65341
+ });
65342
+ seen.add(percentageValue);
65343
+ }
65344
+ if (tableName && !tableLookup.has(percentageValue)) {
65345
+ tableLookup.set(percentageValue, tableName);
65346
+ }
65347
+ }
65211
65348
  for (const column of table2.columns ?? []) {
65212
65349
  const fieldName = String(column.field ?? "").trim();
65213
65350
  if (!fieldName) continue;
65351
+ const isBoolColumn = isBoolType(String(column.fieldType ?? "").toLowerCase()) || String(column.jsType ?? "").toLowerCase() === "boolean";
65214
65352
  for (const aggregationType of AGGREGATION_OPTION_TYPES) {
65215
65353
  if (aggregationType === "count") continue;
65216
65354
  if (!canAggregateColumn(column, aggregationType)) continue;
65355
+ const isNumberSharePercentage = aggregationType === "percentage" && !isBoolColumn;
65356
+ if (isNumberSharePercentage && !resolvedGroupRowsBy) continue;
65217
65357
  const value = `${aggregationType}:${fieldName}`;
65218
65358
  if (seen.has(value)) continue;
65359
+ const aggregationWord = aggregationType === "percentage" ? isBoolColumn ? "Percent" : "Percent of Total" : formatAggregationTypeForDisplay(aggregationType);
65219
65360
  options2.push({
65220
65361
  value,
65221
- label: tableName ? `${formatAggregationTypeForDisplay(aggregationType)} ${toTitleCaseLabel(tableName)} ${toTitleCaseLabel(fieldName)}` : `${formatAggregationTypeForDisplay(aggregationType)} ${toTitleCaseLabel(fieldName)}`
65362
+ label: tableName ? `${aggregationWord} ${toTitleCaseLabel(tableName)} ${toTitleCaseLabel(fieldName)}` : `${aggregationWord} ${toTitleCaseLabel(fieldName)}`
65222
65363
  });
65223
65364
  seen.add(value);
65224
65365
  if (tableName && !tableLookup.has(value)) {
@@ -65241,6 +65382,7 @@ function useReport(reportIdArg, options = {}) {
65241
65382
  cleanedAggregations,
65242
65383
  fieldOptionsAllowedTableNames,
65243
65384
  joinCompatibleSchemaTableNames,
65385
+ resolvedGroupRowsBy,
65244
65386
  schemaForReportBuilderState
65245
65387
  ]);
65246
65388
  const { schemaColumnOptions, columnIdentifierLookup } = (0, import_react62.useMemo)(() => {
@@ -65483,9 +65625,13 @@ function useReport(reportIdArg, options = {}) {
65483
65625
  aggregation.aggregationType ?? ""
65484
65626
  ).toLowerCase();
65485
65627
  const tableHint = String(aggregationTablesByIndex[index] ?? "").trim();
65486
- const currentTarget = aggregation.valueField && aggregation.valueField.trim().length > 0 ? aggregation.valueField : normalizedAggregationType === "count" ? tableHint || aggregationFallbackTableName : aggregationFallbackTarget;
65628
+ const currentValueField = String(aggregation.valueField ?? "").trim();
65629
+ const currentValueField2 = String(aggregation.valueField2 ?? "").trim();
65630
+ const isPercentageRatio = normalizedAggregationType === "percentage" && Boolean(currentValueField) && Boolean(currentValueField2);
65631
+ const currentTarget = isPercentageRatio ? `${currentValueField}:${currentValueField2}` : currentValueField ? currentValueField : normalizedAggregationType === "count" || normalizedAggregationType === "percentage" ? tableHint || aggregationFallbackTableName : aggregationFallbackTarget;
65487
65632
  const currentValue = aggregation.aggregationType ? `${aggregation.aggregationType}:${currentTarget}` : void 0;
65488
- const currentLabel = aggregation.aggregationType ? `${formatAggregationTypeForDisplay(aggregation.aggregationType)} ${toTitleCaseLabel(currentTarget)}` : "Select aggregation";
65633
+ const isBoolPercentage = normalizedAggregationType === "percentage" && Boolean(currentValueField) && isBoolAggregationField(currentValueField, tableHint || void 0) === true;
65634
+ const currentLabel = aggregation.aggregationType ? isPercentageRatio ? `Percent ${toTitleCaseLabel(currentValueField)} of ${toTitleCaseLabel(currentValueField2)}` : normalizedAggregationType === "percentage" ? isBoolPercentage ? `Percent ${toTitleCaseLabel(currentTarget)}` : `Percent of Total ${toTitleCaseLabel(currentTarget)}` : `${formatAggregationTypeForDisplay(aggregation.aggregationType)} ${toTitleCaseLabel(currentTarget)}` : "Select aggregation";
65489
65635
  const options2 = currentValue ? baseOptions.some((option) => option.value === currentValue) ? baseOptions : [{ value: currentValue, label: currentLabel }, ...baseOptions] : baseOptions;
65490
65636
  return {
65491
65637
  key: `aggregation-${index}`,
@@ -65499,7 +65645,8 @@ function useReport(reportIdArg, options = {}) {
65499
65645
  aggregationFallbackTarget,
65500
65646
  aggregationState,
65501
65647
  aggregationBaseOptions,
65502
- aggregationTablesByIndex
65648
+ aggregationTablesByIndex,
65649
+ isBoolAggregationField
65503
65650
  ]);
65504
65651
  const nextPivot = (0, import_react62.useMemo)(() => {
65505
65652
  if (!sourceReport) return null;
@@ -67272,10 +67419,12 @@ function useReport(reportIdArg, options = {}) {
67272
67419
  preferredTables
67273
67420
  );
67274
67421
  const existing = next.aggregationState[index];
67422
+ const preservedValueField2 = aggregationType !== "percentage" && existing?.valueField2 ? { valueField2: existing.valueField2 } : {};
67275
67423
  const nextAggregation = {
67276
- ...existing?.valueField2 ? { valueField2: existing.valueField2 } : {},
67424
+ ...preservedValueField2,
67277
67425
  aggregationType,
67278
67426
  ...normalizedSelection.valueField ? { valueField: normalizedSelection.valueField } : {},
67427
+ ...normalizedSelection.valueField2 ? { valueField2: normalizedSelection.valueField2 } : {},
67279
67428
  ...normalizedSelection.table ? { valueFieldTable: normalizedSelection.table } : {}
67280
67429
  };
67281
67430
  if (index === next.aggregationState.length) {
@@ -67312,6 +67461,7 @@ function useReport(reportIdArg, options = {}) {
67312
67461
  {
67313
67462
  aggregationType,
67314
67463
  ...normalizedSelection.valueField ? { valueField: normalizedSelection.valueField } : {},
67464
+ ...normalizedSelection.valueField2 ? { valueField2: normalizedSelection.valueField2 } : {},
67315
67465
  ...normalizedSelection.table ? { valueFieldTable: normalizedSelection.table } : {}
67316
67466
  }
67317
67467
  ];
@@ -67436,7 +67586,8 @@ function useReport(reportIdArg, options = {}) {
67436
67586
  next.chartType = effectiveNextState.chartType || void 0;
67437
67587
  const normalizedNext = processPivotState(next, {
67438
67588
  nextState: effectiveNextState,
67439
- promoteColumnToRow: false
67589
+ promoteColumnToRow: false,
67590
+ isBoolAggregationField
67440
67591
  });
67441
67592
  return normalizedNext;
67442
67593
  }
package/dist/index.d.cts CHANGED
@@ -2972,7 +2972,9 @@ type SetReportTableColumnSidebarPatch = {
2972
2972
  };
2973
2973
  type AggregationStateItem = Partial<{
2974
2974
  valueField: string;
2975
+ valueFieldType: string;
2975
2976
  valueField2?: string;
2977
+ valueField2Type?: string;
2976
2978
  aggregationType: AggregationType;
2977
2979
  /** Resolved datasource table for `valueField` (mirrors `aggregationTablesByIndex`). */
2978
2980
  valueFieldTable?: string;
@@ -3268,7 +3270,9 @@ declare function useReport(reportIdArg?: string, options?: UseReportOptions): {
3268
3270
  groupColumnsByOptions: SelectOption[];
3269
3271
  aggregations: Partial<{
3270
3272
  valueField: string;
3273
+ valueFieldType: string;
3271
3274
  valueField2?: string;
3275
+ valueField2Type?: string;
3272
3276
  aggregationType: AggregationType;
3273
3277
  /** Resolved datasource table for `valueField` (mirrors `aggregationTablesByIndex`). */
3274
3278
  valueFieldTable?: string;
package/dist/index.d.ts CHANGED
@@ -2972,7 +2972,9 @@ type SetReportTableColumnSidebarPatch = {
2972
2972
  };
2973
2973
  type AggregationStateItem = Partial<{
2974
2974
  valueField: string;
2975
+ valueFieldType: string;
2975
2976
  valueField2?: string;
2977
+ valueField2Type?: string;
2976
2978
  aggregationType: AggregationType;
2977
2979
  /** Resolved datasource table for `valueField` (mirrors `aggregationTablesByIndex`). */
2978
2980
  valueFieldTable?: string;
@@ -3268,7 +3270,9 @@ declare function useReport(reportIdArg?: string, options?: UseReportOptions): {
3268
3270
  groupColumnsByOptions: SelectOption[];
3269
3271
  aggregations: Partial<{
3270
3272
  valueField: string;
3273
+ valueFieldType: string;
3271
3274
  valueField2?: string;
3275
+ valueField2Type?: string;
3272
3276
  aggregationType: AggregationType;
3273
3277
  /** Resolved datasource table for `valueField` (mirrors `aggregationTablesByIndex`). */
3274
3278
  valueFieldTable?: string;
package/dist/index.js CHANGED
@@ -15162,11 +15162,11 @@ function isValidPivot(pivot) {
15162
15162
  reason: 'Value field cannot be empty when aggregation is not "count" or "percentage"'
15163
15163
  };
15164
15164
  } else if (pivot.aggregations?.some(
15165
- (a) => a.aggregationType === "percentage" && !a.valueField && (!pivot.rowField || pivot.columnField)
15165
+ (a) => a.aggregationType === "percentage" && !a.valueField && !pivot.rowField
15166
15166
  )) {
15167
15167
  return {
15168
15168
  valid: false,
15169
- reason: "Empty percentage field only supported for pivot tables with a row field and no column field"
15169
+ reason: "Empty percentage field only supported for pivot tables with a row field"
15170
15170
  };
15171
15171
  }
15172
15172
  return { valid: true, reason: "" };
@@ -16449,7 +16449,7 @@ function formatAggregationTypeForDisplay(aggregationType) {
16449
16449
  case "max":
16450
16450
  return "Max";
16451
16451
  case "percentage":
16452
- return "Percentage";
16452
+ return "Percent";
16453
16453
  default:
16454
16454
  return aggregationType ? capitalize(String(aggregationType)) : "Value";
16455
16455
  }
@@ -16491,6 +16491,21 @@ function getDefaultPivotMeasureLabel(report) {
16491
16491
  return pivot.columnField ? "Count" : `Count ${snakeAndCamelCaseToTitleCase(tableNames[0] ?? "table")}`;
16492
16492
  }
16493
16493
  const target = valueField !== void 0 && String(valueField).trim() ? resolveValueFieldDisplayLabel(report, valueField) : tableNames.length > 0 ? tableNames.map((n) => snakeAndCamelCaseToTitleCase(n)).join(", ") : "table";
16494
+ if (aggregationType.toLowerCase() === "percentage") {
16495
+ const valueField2 = String(
16496
+ fromAggregations?.valueField2 ?? ""
16497
+ ).trim();
16498
+ if (valueField && valueField2) {
16499
+ return `Percent ${target} of ${resolveValueFieldDisplayLabel(report, valueField2)}`;
16500
+ }
16501
+ const valueFieldType = String(
16502
+ fromAggregations?.valueFieldType ?? ""
16503
+ ).trim().toLowerCase();
16504
+ if (valueField && valueFieldType === "bool") {
16505
+ return `Percent ${target}`;
16506
+ }
16507
+ return `Percent of Total ${target}`;
16508
+ }
16494
16509
  return target ? `${aggWord} ${target}` : aggWord;
16495
16510
  }
16496
16511
  function humanizeLegacyPivotMeasureLabelFragment(text) {
@@ -16498,7 +16513,10 @@ function humanizeLegacyPivotMeasureLabelFragment(text) {
16498
16513
  if (!t) return t;
16499
16514
  const m = t.match(LEGACY_AGG_SNAKE_FIELD);
16500
16515
  if (!m) return t;
16501
- return `${formatAggregationTypeForDisplay(m[1])} ${snakeAndCamelCaseToTitleCase(m[2])}`;
16516
+ if (String(m[1]).toLowerCase() === "percentage") {
16517
+ return `Percent of Total ${snakeAndCamelCaseToTitleCase(m[2] ?? "")}`;
16518
+ }
16519
+ return `${formatAggregationTypeForDisplay(m[1])} ${snakeAndCamelCaseToTitleCase(m[2] ?? "")}`;
16502
16520
  }
16503
16521
  function humanizeChartSeriesTooltipLabel(label) {
16504
16522
  const s = String(label ?? "").trim();
@@ -60055,7 +60073,8 @@ var AGGREGATION_OPTION_TYPES = [
60055
60073
  "average",
60056
60074
  "count",
60057
60075
  "max",
60058
- "min"
60076
+ "min",
60077
+ "percentage"
60059
60078
  ];
60060
60079
  var QUERY_BUILDER_MULTI_VALUE_OPERATORS = /* @__PURE__ */ new Set(["in", "notin"]);
60061
60080
  var TABLE_FORMAT_CACHE_MAX_ENTRIES = 5e3;
@@ -60545,9 +60564,31 @@ function buildPivotAggregationsWithFieldTables({
60545
60564
  schemaTables,
60546
60565
  preferredTables
60547
60566
  }) || void 0;
60567
+ const isPercentageAggregation = String(aggregation.aggregationType ?? "").toLowerCase() === "percentage";
60568
+ const typeFallbackColumns = [
60569
+ ...sourceReport?.columnInternal ?? sourceReport?.columns ?? []
60570
+ ];
60571
+ const valueFieldType = String(
60572
+ aggregation.valueFieldType ?? ""
60573
+ ).trim() || (isPercentageAggregation ? resolveFieldType({
60574
+ field: aggregation.valueField,
60575
+ table: valueFieldTable,
60576
+ schemaTables,
60577
+ fallbackColumns: typeFallbackColumns
60578
+ }) : void 0);
60579
+ const valueField2Type = String(
60580
+ aggregation.valueField2Type ?? ""
60581
+ ).trim() || (isPercentageAggregation ? resolveFieldType({
60582
+ field: aggregation.valueField2,
60583
+ table: valueFieldTable,
60584
+ schemaTables,
60585
+ fallbackColumns: typeFallbackColumns
60586
+ }) : void 0);
60548
60587
  result.push({
60549
60588
  ...aggregationWithoutPivotTables,
60550
60589
  aggregationType: aggregation.aggregationType,
60590
+ ...valueFieldType ? { valueFieldType } : {},
60591
+ ...valueField2Type ? { valueField2Type } : {},
60551
60592
  ...valueFieldTable ? { valueFieldTable } : {}
60552
60593
  });
60553
60594
  }
@@ -60591,8 +60632,9 @@ function getPivotAggregationSortFields(aggregationState, resolvedColumnField) {
60591
60632
  const aggregationType = String(aggregation.aggregationType ?? "").trim();
60592
60633
  if (!aggregationType) continue;
60593
60634
  const valueField = String(aggregation.valueField ?? "").trim();
60594
- const canonicalBaseField = valueField || "count";
60595
- const sortField = hasMultipleAggregations && aggregationType.toLowerCase() !== "count" ? `${canonicalBaseField}_${aggregationType}` : canonicalBaseField;
60635
+ const isValuelessPercentage = !valueField && aggregationType.toLowerCase() === "percentage";
60636
+ const canonicalBaseField = valueField || (isValuelessPercentage ? "percentage" : "count");
60637
+ const sortField = isValuelessPercentage ? "percentage" : hasMultipleAggregations && aggregationType.toLowerCase() !== "count" ? `${canonicalBaseField}_${aggregationType}` : canonicalBaseField;
60596
60638
  if (!seen.has(sortField)) {
60597
60639
  seen.add(sortField);
60598
60640
  fields.push(sortField);
@@ -60611,8 +60653,9 @@ function getPivotAggregationSortLabelOverrides(aggregationState, aggregationTabl
60611
60653
  const aggregationType = String(aggregation.aggregationType ?? "").trim();
60612
60654
  if (!aggregationType) continue;
60613
60655
  const valueField = String(aggregation.valueField ?? "").trim();
60614
- const canonicalBaseField = valueField || "count";
60615
- const sortField = hasMultipleAggregations && aggregationType.toLowerCase() !== "count" ? `${canonicalBaseField}_${aggregationType}` : canonicalBaseField;
60656
+ const isValuelessPercentage = !valueField && aggregationType.toLowerCase() === "percentage";
60657
+ const canonicalBaseField = valueField || (isValuelessPercentage ? "percentage" : "count");
60658
+ const sortField = isValuelessPercentage ? "percentage" : hasMultipleAggregations && aggregationType.toLowerCase() !== "count" ? `${canonicalBaseField}_${aggregationType}` : canonicalBaseField;
60616
60659
  if (labelBySortField.has(sortField)) continue;
60617
60660
  if (valueField || aggregationType.toLowerCase() !== "count") continue;
60618
60661
  const tableHint = String(aggregationTablesByIndex[index] ?? "").trim();
@@ -60766,6 +60809,9 @@ function trimAggregationSuffix(value, aggregationType) {
60766
60809
  }
60767
60810
  function getAggregationFieldKey(aggregation, hasMultipleAggregations) {
60768
60811
  if (!aggregation.aggregationType) return null;
60812
+ if (!aggregation.valueField && aggregation.aggregationType === "percentage") {
60813
+ return "percentage";
60814
+ }
60769
60815
  if (aggregation.valueField) {
60770
60816
  return hasMultipleAggregations ? `${aggregation.valueField}_${aggregation.aggregationType}` : aggregation.valueField;
60771
60817
  }
@@ -60886,6 +60932,22 @@ function buildAggregationLabel(aggregation, report) {
60886
60932
  }
60887
60933
  const rawTarget = aggregation.valueField ?? (tableNames.length > 0 ? tableNames.join(", ") : "table");
60888
60934
  const target = report ? aggregation.valueField ? resolveValueFieldDisplayLabel(report, aggregation.valueField) : rawTarget : rawTarget;
60935
+ if (String(aggregationType).toLowerCase() === "percentage") {
60936
+ const valueField2 = String(
60937
+ aggregation.valueField2 ?? ""
60938
+ ).trim();
60939
+ if (aggregation.valueField && valueField2) {
60940
+ const denominator = report ? resolveValueFieldDisplayLabel(report, valueField2) : valueField2;
60941
+ return `Percent ${target} of ${denominator}`;
60942
+ }
60943
+ const valueFieldType = String(
60944
+ aggregation.valueFieldType ?? ""
60945
+ ).trim().toLowerCase();
60946
+ if (aggregation.valueField && valueFieldType === "bool") {
60947
+ return `Percent ${target}`;
60948
+ }
60949
+ return `Percent of Total ${target}`;
60950
+ }
60889
60951
  return target ? `${aggWord} ${target}` : aggWord;
60890
60952
  }
60891
60953
  function normalizePivotColumnDisplayValue(value) {
@@ -62221,7 +62283,8 @@ function processPivotState(next, options = {}) {
62221
62283
  const {
62222
62284
  nextState,
62223
62285
  promoteColumnToRow = false,
62224
- fallbackColumnGroupBy
62286
+ fallbackColumnGroupBy,
62287
+ isBoolAggregationField
62225
62288
  } = options;
62226
62289
  const normalizedNext = { ...next };
62227
62290
  const isClearingRowGrouping = nextState?.groupRowsBy !== void 0 && String(nextState.groupRowsBy ?? "").trim().length === 0;
@@ -62264,6 +62327,29 @@ function processPivotState(next, options = {}) {
62264
62327
  normalizedNext.aggregationState = [{ aggregationType: "count" }];
62265
62328
  normalizedNext.aggregationTablesByIndex = [void 0];
62266
62329
  }
62330
+ if (!resolvedRowField) {
62331
+ normalizedNext.aggregationState = normalizedNext.aggregationState.map(
62332
+ (aggregation, index) => {
62333
+ if (String(aggregation.aggregationType ?? "").toLowerCase() !== "percentage") {
62334
+ return aggregation;
62335
+ }
62336
+ const valueField = String(aggregation.valueField ?? "").trim();
62337
+ if (!valueField) {
62338
+ return { ...aggregation, aggregationType: "count" };
62339
+ }
62340
+ const valueField2 = String(aggregation.valueField2 ?? "").trim();
62341
+ if (valueField2 && valueField2 !== valueField) {
62342
+ return aggregation;
62343
+ }
62344
+ const table = String(aggregation.valueFieldTable ?? "").trim() || String(normalizedNext.aggregationTablesByIndex[index] ?? "").trim() || void 0;
62345
+ const isBool = isBoolAggregationField?.(valueField, table);
62346
+ if (isBool === false) {
62347
+ return { ...aggregation, aggregationType: "sum" };
62348
+ }
62349
+ return aggregation;
62350
+ }
62351
+ );
62352
+ }
62267
62353
  if (isSettingRowGrouping && normalizedNext.aggregationState.length > 0) {
62268
62354
  normalizedNext.aggregationState = [...normalizedNext.aggregationState];
62269
62355
  }
@@ -63518,6 +63604,19 @@ function useReport(reportIdArg, options = {}) {
63518
63604
  const customFieldsRef = useRef25(schemaData.customFields);
63519
63605
  schemaForReportBuilderStateRef.current = schemaForReportBuilderState;
63520
63606
  customFieldsRef.current = schemaData.customFields;
63607
+ const isBoolAggregationField = useCallback6(
63608
+ (field, table2) => {
63609
+ const fieldType = resolveFieldType({
63610
+ field,
63611
+ table: table2,
63612
+ schemaTables: schemaForReportBuilderStateRef.current
63613
+ });
63614
+ if (!fieldType) return void 0;
63615
+ const normalized = String(fieldType).trim().toLowerCase();
63616
+ return isBoolType(normalized) || normalized === "boolean";
63617
+ },
63618
+ []
63619
+ );
63521
63620
  const schemaForeignKeyMap = useMemo33(
63522
63621
  () => getSchemaForeignKeyMapping(schemaForReportBuilderState),
63523
63622
  [schemaForReportBuilderState]
@@ -64425,9 +64524,10 @@ function useReport(reportIdArg, options = {}) {
64425
64524
  const explicitTable = getPivotGroupOptionTable(targetRaw);
64426
64525
  const field = explicitTable ? decodePivotGroupOptionValue(targetRaw) : targetRaw;
64427
64526
  if (!field) return {};
64527
+ const fieldForTableResolution = field.includes(":") ? String(field.split(":")[0] ?? "").trim() : field;
64428
64528
  const table2 = explicitTable ?? optionTableLookup.get(rawValue) ?? resolveFieldTable({
64429
64529
  report: sourceReport,
64430
- field,
64530
+ field: fieldForTableResolution,
64431
64531
  schemaTables: schemaForReportBuilderState,
64432
64532
  preferredTables
64433
64533
  }) ?? void 0;
@@ -64442,7 +64542,19 @@ function useReport(reportIdArg, options = {}) {
64442
64542
  if (target === "table") {
64443
64543
  return table2 ? { table: table2 } : preferredTables[0] ? { table: preferredTables[0] } : {};
64444
64544
  }
64445
- if (aggregationType !== "count") {
64545
+ if (aggregationType === "percentage" && target.includes(":")) {
64546
+ const [numeratorRaw, denominatorRaw] = target.split(":");
64547
+ const numerator = String(numeratorRaw ?? "").trim();
64548
+ const denominator = String(denominatorRaw ?? "").trim();
64549
+ if (numerator && denominator) {
64550
+ return {
64551
+ valueField: numerator,
64552
+ valueField2: denominator,
64553
+ ...table2 ? { table: table2 } : {}
64554
+ };
64555
+ }
64556
+ }
64557
+ if (aggregationType !== "count" && aggregationType !== "percentage") {
64446
64558
  return {
64447
64559
  valueField: target,
64448
64560
  ...table2 ? { table: table2 } : {}
@@ -64626,7 +64738,9 @@ function useReport(reportIdArg, options = {}) {
64626
64738
  const pivotAggregationsFromSource = Array.isArray(sourceReport.pivot?.aggregations) ? sourceReport.pivot.aggregations : sourceReport.pivot?.aggregationType ? [
64627
64739
  {
64628
64740
  valueField: sourceReport.pivot?.valueField,
64741
+ valueFieldType: sourceReport.pivot?.valueFieldType,
64629
64742
  valueField2: sourceReport.pivot?.valueField2,
64743
+ valueField2Type: sourceReport.pivot?.valueField2Type,
64630
64744
  aggregationType: sourceReport.pivot?.aggregationType
64631
64745
  }
64632
64746
  ] : [];
@@ -64711,7 +64825,8 @@ function useReport(reportIdArg, options = {}) {
64711
64825
  chartType: prev.chartType ?? sourceReport.chartType
64712
64826
  };
64713
64827
  const normalized = processPivotState(nextFromSource, {
64714
- promoteColumnToRow: shouldPromoteColumnToRowFromPendingFlag
64828
+ promoteColumnToRow: shouldPromoteColumnToRowFromPendingFlag,
64829
+ isBoolAggregationField
64715
64830
  });
64716
64831
  return normalized;
64717
64832
  }
@@ -65051,6 +65166,10 @@ function useReport(reportIdArg, options = {}) {
65051
65166
  aggregation.valueField,
65052
65167
  aggregationTablesByIndex[index]
65053
65168
  );
65169
+ appendPivotColumn(
65170
+ aggregation.valueField2,
65171
+ aggregationTablesByIndex[index]
65172
+ );
65054
65173
  });
65055
65174
  const joinColumns = getJoinColumnsFromTables(effectiveReportBuilderTables);
65056
65175
  const useWideTableColumns = expandReportBuilderColumnsForFlatTable && !pivotState && String(chartType ?? "").toLowerCase() === "table";
@@ -65380,9 +65499,13 @@ function useReport(reportIdArg, options = {}) {
65380
65499
  const aggregationType = String(aggregation.aggregationType);
65381
65500
  const normalizedAggregationType = aggregationType.toLowerCase();
65382
65501
  const tableHint = String(aggregationTablesByIndex[index] ?? "").trim();
65383
- const target = aggregation.valueField && aggregation.valueField.trim().length > 0 ? aggregation.valueField : normalizedAggregationType === "count" ? tableHint || aggregationFallbackTableName : aggregationFallbackTarget;
65502
+ const valueField = String(aggregation.valueField ?? "").trim();
65503
+ const valueField2 = String(aggregation.valueField2 ?? "").trim();
65504
+ const isPercentageRatio = normalizedAggregationType === "percentage" && Boolean(valueField) && Boolean(valueField2);
65505
+ const target = isPercentageRatio ? `${valueField}:${valueField2}` : valueField ? valueField : normalizedAggregationType === "count" || normalizedAggregationType === "percentage" ? tableHint || aggregationFallbackTableName : aggregationFallbackTarget;
65506
+ const isBoolPercentage = normalizedAggregationType === "percentage" && Boolean(valueField) && isBoolAggregationField(valueField, tableHint || void 0) === true;
65384
65507
  return {
65385
- label: `${formatAggregationTypeForDisplay(aggregationType)} ${toTitleCaseLabel(target)}`,
65508
+ label: isPercentageRatio ? `Percent ${toTitleCaseLabel(valueField)} of ${toTitleCaseLabel(valueField2)}` : normalizedAggregationType === "percentage" ? isBoolPercentage ? `Percent ${toTitleCaseLabel(target)}` : `Percent of Total ${toTitleCaseLabel(target)}` : `${formatAggregationTypeForDisplay(aggregationType)} ${toTitleCaseLabel(target)}`,
65386
65509
  value: `${aggregationType}:${target}`
65387
65510
  };
65388
65511
  });
@@ -65390,7 +65513,8 @@ function useReport(reportIdArg, options = {}) {
65390
65513
  aggregationFallbackTableName,
65391
65514
  aggregationFallbackTarget,
65392
65515
  aggregationState,
65393
- aggregationTablesByIndex
65516
+ aggregationTablesByIndex,
65517
+ isBoolAggregationField
65394
65518
  ]);
65395
65519
  const { aggregationBaseOptions, aggregationOptionTableLookup } = useMemo33(() => {
65396
65520
  const joinScoped = fieldOptionsAllowedTableNames ? joinCompatibleSchemaTableNames.filter(
@@ -65416,17 +65540,34 @@ function useReport(reportIdArg, options = {}) {
65416
65540
  if (tableName && !tableLookup.has(countValue)) {
65417
65541
  tableLookup.set(countValue, tableName);
65418
65542
  }
65543
+ if (resolvedGroupRowsBy) {
65544
+ const percentageValue = `percentage:${countTarget}`;
65545
+ if (!seen.has(percentageValue)) {
65546
+ options2.push({
65547
+ value: percentageValue,
65548
+ label: `Percent of Total ${toTitleCaseLabel(countTarget)}`
65549
+ });
65550
+ seen.add(percentageValue);
65551
+ }
65552
+ if (tableName && !tableLookup.has(percentageValue)) {
65553
+ tableLookup.set(percentageValue, tableName);
65554
+ }
65555
+ }
65419
65556
  for (const column of table2.columns ?? []) {
65420
65557
  const fieldName = String(column.field ?? "").trim();
65421
65558
  if (!fieldName) continue;
65559
+ const isBoolColumn = isBoolType(String(column.fieldType ?? "").toLowerCase()) || String(column.jsType ?? "").toLowerCase() === "boolean";
65422
65560
  for (const aggregationType of AGGREGATION_OPTION_TYPES) {
65423
65561
  if (aggregationType === "count") continue;
65424
65562
  if (!canAggregateColumn(column, aggregationType)) continue;
65563
+ const isNumberSharePercentage = aggregationType === "percentage" && !isBoolColumn;
65564
+ if (isNumberSharePercentage && !resolvedGroupRowsBy) continue;
65425
65565
  const value = `${aggregationType}:${fieldName}`;
65426
65566
  if (seen.has(value)) continue;
65567
+ const aggregationWord = aggregationType === "percentage" ? isBoolColumn ? "Percent" : "Percent of Total" : formatAggregationTypeForDisplay(aggregationType);
65427
65568
  options2.push({
65428
65569
  value,
65429
- label: tableName ? `${formatAggregationTypeForDisplay(aggregationType)} ${toTitleCaseLabel(tableName)} ${toTitleCaseLabel(fieldName)}` : `${formatAggregationTypeForDisplay(aggregationType)} ${toTitleCaseLabel(fieldName)}`
65570
+ label: tableName ? `${aggregationWord} ${toTitleCaseLabel(tableName)} ${toTitleCaseLabel(fieldName)}` : `${aggregationWord} ${toTitleCaseLabel(fieldName)}`
65430
65571
  });
65431
65572
  seen.add(value);
65432
65573
  if (tableName && !tableLookup.has(value)) {
@@ -65449,6 +65590,7 @@ function useReport(reportIdArg, options = {}) {
65449
65590
  cleanedAggregations,
65450
65591
  fieldOptionsAllowedTableNames,
65451
65592
  joinCompatibleSchemaTableNames,
65593
+ resolvedGroupRowsBy,
65452
65594
  schemaForReportBuilderState
65453
65595
  ]);
65454
65596
  const { schemaColumnOptions, columnIdentifierLookup } = useMemo33(() => {
@@ -65691,9 +65833,13 @@ function useReport(reportIdArg, options = {}) {
65691
65833
  aggregation.aggregationType ?? ""
65692
65834
  ).toLowerCase();
65693
65835
  const tableHint = String(aggregationTablesByIndex[index] ?? "").trim();
65694
- const currentTarget = aggregation.valueField && aggregation.valueField.trim().length > 0 ? aggregation.valueField : normalizedAggregationType === "count" ? tableHint || aggregationFallbackTableName : aggregationFallbackTarget;
65836
+ const currentValueField = String(aggregation.valueField ?? "").trim();
65837
+ const currentValueField2 = String(aggregation.valueField2 ?? "").trim();
65838
+ const isPercentageRatio = normalizedAggregationType === "percentage" && Boolean(currentValueField) && Boolean(currentValueField2);
65839
+ const currentTarget = isPercentageRatio ? `${currentValueField}:${currentValueField2}` : currentValueField ? currentValueField : normalizedAggregationType === "count" || normalizedAggregationType === "percentage" ? tableHint || aggregationFallbackTableName : aggregationFallbackTarget;
65695
65840
  const currentValue = aggregation.aggregationType ? `${aggregation.aggregationType}:${currentTarget}` : void 0;
65696
- const currentLabel = aggregation.aggregationType ? `${formatAggregationTypeForDisplay(aggregation.aggregationType)} ${toTitleCaseLabel(currentTarget)}` : "Select aggregation";
65841
+ const isBoolPercentage = normalizedAggregationType === "percentage" && Boolean(currentValueField) && isBoolAggregationField(currentValueField, tableHint || void 0) === true;
65842
+ const currentLabel = aggregation.aggregationType ? isPercentageRatio ? `Percent ${toTitleCaseLabel(currentValueField)} of ${toTitleCaseLabel(currentValueField2)}` : normalizedAggregationType === "percentage" ? isBoolPercentage ? `Percent ${toTitleCaseLabel(currentTarget)}` : `Percent of Total ${toTitleCaseLabel(currentTarget)}` : `${formatAggregationTypeForDisplay(aggregation.aggregationType)} ${toTitleCaseLabel(currentTarget)}` : "Select aggregation";
65697
65843
  const options2 = currentValue ? baseOptions.some((option) => option.value === currentValue) ? baseOptions : [{ value: currentValue, label: currentLabel }, ...baseOptions] : baseOptions;
65698
65844
  return {
65699
65845
  key: `aggregation-${index}`,
@@ -65707,7 +65853,8 @@ function useReport(reportIdArg, options = {}) {
65707
65853
  aggregationFallbackTarget,
65708
65854
  aggregationState,
65709
65855
  aggregationBaseOptions,
65710
- aggregationTablesByIndex
65856
+ aggregationTablesByIndex,
65857
+ isBoolAggregationField
65711
65858
  ]);
65712
65859
  const nextPivot = useMemo33(() => {
65713
65860
  if (!sourceReport) return null;
@@ -67480,10 +67627,12 @@ function useReport(reportIdArg, options = {}) {
67480
67627
  preferredTables
67481
67628
  );
67482
67629
  const existing = next.aggregationState[index];
67630
+ const preservedValueField2 = aggregationType !== "percentage" && existing?.valueField2 ? { valueField2: existing.valueField2 } : {};
67483
67631
  const nextAggregation = {
67484
- ...existing?.valueField2 ? { valueField2: existing.valueField2 } : {},
67632
+ ...preservedValueField2,
67485
67633
  aggregationType,
67486
67634
  ...normalizedSelection.valueField ? { valueField: normalizedSelection.valueField } : {},
67635
+ ...normalizedSelection.valueField2 ? { valueField2: normalizedSelection.valueField2 } : {},
67487
67636
  ...normalizedSelection.table ? { valueFieldTable: normalizedSelection.table } : {}
67488
67637
  };
67489
67638
  if (index === next.aggregationState.length) {
@@ -67520,6 +67669,7 @@ function useReport(reportIdArg, options = {}) {
67520
67669
  {
67521
67670
  aggregationType,
67522
67671
  ...normalizedSelection.valueField ? { valueField: normalizedSelection.valueField } : {},
67672
+ ...normalizedSelection.valueField2 ? { valueField2: normalizedSelection.valueField2 } : {},
67523
67673
  ...normalizedSelection.table ? { valueFieldTable: normalizedSelection.table } : {}
67524
67674
  }
67525
67675
  ];
@@ -67644,7 +67794,8 @@ function useReport(reportIdArg, options = {}) {
67644
67794
  next.chartType = effectiveNextState.chartType || void 0;
67645
67795
  const normalizedNext = processPivotState(next, {
67646
67796
  nextState: effectiveNextState,
67647
- promoteColumnToRow: false
67797
+ promoteColumnToRow: false,
67798
+ isBoolAggregationField
67648
67799
  });
67649
67800
  return normalizedNext;
67650
67801
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@quillsql/react",
3
- "version": "2.16.46",
3
+ "version": "2.16.47",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": {