@gooddata/sdk-ui-pivot 11.57.0-alpha.5 → 11.57.0-alpha.7
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/esm/impl/data/resultTypes.d.ts.map +1 -1
- package/esm/impl/data/resultTypes.js +1 -0
- package/esm/impl/structure/headers/TotalHeaderCell.js +1 -1
- package/esm/locales.d.ts.map +1 -1
- package/esm/locales.js +6 -0
- package/esm/next/PivotTableNext.d.ts.map +1 -1
- package/esm/next/PivotTableNext.js +2 -1
- package/esm/next/components/Cell/AttributeCell.d.ts.map +1 -1
- package/esm/next/components/Cell/AttributeCell.js +38 -25
- package/esm/next/components/Header/MeasureHeader.d.ts.map +1 -1
- package/esm/next/components/Header/MeasureHeader.js +18 -4
- package/esm/next/components/Header/PivotGroupHeader.d.ts.map +1 -1
- package/esm/next/components/Header/PivotGroupHeader.js +17 -4
- package/esm/next/components/Header/utils/constructAggregationsMenuItems.d.ts.map +1 -1
- package/esm/next/components/Header/utils/constructAggregationsMenuItems.js +3 -2
- package/esm/next/components/TotalLabel/RenameTotalLabelPopover.d.ts +19 -0
- package/esm/next/components/TotalLabel/RenameTotalLabelPopover.d.ts.map +1 -0
- package/esm/next/components/TotalLabel/RenameTotalLabelPopover.js +19 -0
- package/esm/next/components/TotalLabel/TotalActionMenu.d.ts +10 -0
- package/esm/next/components/TotalLabel/TotalActionMenu.d.ts.map +1 -0
- package/esm/next/components/TotalLabel/TotalActionMenu.js +33 -0
- package/esm/next/context/PivotTablePropsContext.d.ts +1 -1
- package/esm/next/context/PivotTablePropsContext.d.ts.map +1 -1
- package/esm/next/context/PivotTablePropsContext.js +1 -0
- package/esm/next/context/TotalLabelContext.d.ts +13 -0
- package/esm/next/context/TotalLabelContext.d.ts.map +1 -0
- package/esm/next/context/TotalLabelContext.js +74 -0
- package/esm/next/features/aggregations/totalLabelTarget.d.ts +35 -0
- package/esm/next/features/aggregations/totalLabelTarget.d.ts.map +1 -0
- package/esm/next/features/aggregations/totalLabelTarget.js +111 -0
- package/esm/next/features/aggregations/totals.d.ts +33 -0
- package/esm/next/features/aggregations/totals.d.ts.map +1 -0
- package/esm/next/features/aggregations/totals.js +73 -0
- package/esm/next/features/columns/shared.d.ts.map +1 -1
- package/esm/next/features/columns/shared.js +11 -3
- package/esm/next/features/transposition/totalHeaderColDef.d.ts.map +1 -1
- package/esm/next/features/transposition/totalHeaderColDef.js +7 -2
- package/esm/next/hooks/header/useHeaderCellAriaLabel.d.ts +11 -2
- package/esm/next/hooks/header/useHeaderCellAriaLabel.d.ts.map +1 -1
- package/esm/next/hooks/header/useHeaderCellAriaLabel.js +30 -6
- package/esm/next/hooks/header/useHeaderMenu.d.ts +2 -0
- package/esm/next/hooks/header/useHeaderMenu.d.ts.map +1 -1
- package/esm/next/hooks/header/useHeaderMenu.js +3 -2
- package/esm/next/hooks/header/useHeaderMenuSorting.d.ts +1 -1
- package/esm/next/hooks/header/useHeaderMenuSorting.d.ts.map +1 -1
- package/esm/next/hooks/header/useHeaderMenuSorting.js +7 -4
- package/esm/next/hooks/header/useTotalLabelHeader.d.ts +29 -0
- package/esm/next/hooks/header/useTotalLabelHeader.d.ts.map +1 -0
- package/esm/next/hooks/header/useTotalLabelHeader.js +48 -0
- package/esm/next/hooks/totals/useUpdateTotals.d.ts.map +1 -1
- package/esm/next/hooks/totals/useUpdateTotals.js +10 -4
- package/esm/next/hooks/useInteractionProps.d.ts.map +1 -1
- package/esm/next/hooks/useInteractionProps.js +45 -10
- package/esm/next/types/menu.d.ts +9 -0
- package/esm/next/types/menu.d.ts.map +1 -1
- package/esm/next/types/menu.js +1 -1
- package/esm/sdk-ui-pivot.d.ts +9 -0
- package/esm/tableState.d.ts.map +1 -1
- package/esm/tableState.js +1 -0
- package/package.json +16 -16
- package/styles/css/main.css +18 -0
- package/styles/css/main.css.map +1 -1
- package/styles/css/pivotTableNext.css +18 -0
- package/styles/css/pivotTableNext.css.map +1 -1
- package/styles/scss/pivotTableNext.scss +27 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resultTypes.d.ts","sourceRoot":"","sources":["../../../src/impl/data/resultTypes.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"resultTypes.d.ts","sourceRoot":"","sources":["../../../src/impl/data/resultTypes.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,oBAAoB,EAAE,KAAK,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AACzF,OAAO,EAAE,KAAK,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAIvD;;;;;;;;GAQG;AACH,MAAM,WAAW,QAAQ;IACrB;;OAEG;IACH,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;IAEnB;;OAEG;IACH,aAAa,EAAE;QACX,CAAC,GAAG,EAAE,MAAM,GAAG,cAAc,CAAC;KACjC,CAAC;IAEF;;OAEG;IACH,iBAAiB,CAAC,EAAE,kBAAkB,CAAC;IAEvC;;OAEG;IACH,yBAAyB,CAAC,EAAE,oBAAoB,CAAC;IAEjD;;;OAGG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IAEd,aAAa,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE;QACL,KAAK,EAAE,MAAM,CAAC;QACd,SAAS,EAAE,MAAM,CAAC;KACrB,CAAC;IACF,oBAAoB,CAAC,EAAE,MAAM,EAAE,CAAC;IAEhC,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;CACtB;AAED,MAAM,WAAW,WAAW;IACxB,OAAO,EAAE,QAAQ,EAAE,CAAC;IACpB,SAAS,EAAE,cAAc,EAAE,CAAC;CAC/B;AAED,wBAAgB,eAAe,CAAC,GAAG,EAAE,OAAO,GAAG,GAAG,IAAI,cAAc,CAEnE"}
|
package/esm/locales.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"locales.d.ts","sourceRoot":"","sources":["../src/locales.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,iBAAiB,EAAkB,MAAM,YAAY,CAAC;AAEpE,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAGrD,eAAO,MAAM,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,iBAAiB,
|
|
1
|
+
{"version":3,"file":"locales.d.ts","sourceRoot":"","sources":["../src/locales.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,iBAAiB,EAAkB,MAAM,YAAY,CAAC;AAEpE,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAGrD,eAAO,MAAM,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,iBAAiB,CA8CrD,CAAC;AAEH;;GAEG;AACH,eAAO,MAAM,iBAAiB,EAAE,MAAM,CAAC,SAAS,EAAE,iBAAiB,CAOlE,CAAC"}
|
package/esm/locales.js
CHANGED
|
@@ -10,6 +10,12 @@ export const messages = defineMessages({
|
|
|
10
10
|
med: { id: "visualizations.totals.dropdown.title.med" },
|
|
11
11
|
"disabled.mvf": { id: "visualizations.totals.dropdown.tooltip.nat.disabled.mvf" },
|
|
12
12
|
"disabled.ranking": { id: "visualizations.totals.dropdown.tooltip.nat.disabled.ranking" },
|
|
13
|
+
// Total label rename action menu / popover
|
|
14
|
+
totalLabelActions: { id: "visualizations.totals.labelActions.aria" },
|
|
15
|
+
renameTotalLabel: { id: "visualizations.totals.renameLabel.action" },
|
|
16
|
+
resetTotalLabel: { id: "visualizations.totals.resetLabel.action" },
|
|
17
|
+
cancel: { id: "cancel" },
|
|
18
|
+
save: { id: "save" },
|
|
13
19
|
// Aggregations menu
|
|
14
20
|
aggregationsSection: { id: "visualizations.menu.aggregations" },
|
|
15
21
|
rowsSection: { id: "visualizations.menu.aggregations.rows" },
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PivotTableNext.d.ts","sourceRoot":"","sources":["../../src/next/PivotTableNext.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"PivotTableNext.d.ts","sourceRoot":"","sources":["../../src/next/PivotTableNext.tsx"],"names":[],"mappings":"AAyCA,OAAO,EAAE,KAAK,wBAAwB,EAAE,MAAM,qBAAqB,CAAC;AACpE,OAAO,EAAE,KAAK,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AAI9D;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,oBAAoB,2CAKzD;AAED;;;GAGG;AACH,wBAAgB,4BAA4B,CAAC,KAAK,EAAE,wBAAwB,2CAoB3E"}
|
|
@@ -20,6 +20,7 @@ import { PivotTablePropsProvider, usePivotTableProps } from "./context/PivotTabl
|
|
|
20
20
|
import { PivotTableSizingProvider, usePivotTableSizingActions } from "./context/PivotTableSizingContext.js";
|
|
21
21
|
import { RuntimeErrorProvider, useRuntimeError } from "./context/RuntimeErrorContext.js";
|
|
22
22
|
import { TableReadyProvider } from "./context/TableReadyContext.js";
|
|
23
|
+
import { TotalLabelProvider } from "./context/TotalLabelContext.js";
|
|
23
24
|
import { b } from "./features/styling/bem.js";
|
|
24
25
|
import { useInitExecution } from "./hooks/init/useInitExecution.js";
|
|
25
26
|
import { useInitExecutionResult } from "./hooks/init/useInitExecutionResult.js";
|
|
@@ -78,7 +79,7 @@ function PivotTableNextWithInitialization() {
|
|
|
78
79
|
return _jsx(ErrorComponent, { error: error });
|
|
79
80
|
}
|
|
80
81
|
const { initialExecutionResult, initialDataView } = result;
|
|
81
|
-
return (_jsx(InitialExecutionContextProvider, { initialExecutionResult: initialExecutionResult, initialDataView: initialDataView, children: _jsx(TableReadyProvider, { children: _jsx(DrillableItemsRefProvider, { children: _jsx(ColumnDefsProvider, { children: _jsx(HeaderMenuProvider, { children: _jsx(RenderPivotTableNextAgGrid, {}) }) }) }) }) }));
|
|
82
|
+
return (_jsx(InitialExecutionContextProvider, { initialExecutionResult: initialExecutionResult, initialDataView: initialDataView, children: _jsx(TableReadyProvider, { children: _jsx(DrillableItemsRefProvider, { children: _jsx(ColumnDefsProvider, { children: _jsx(TotalLabelProvider, { children: _jsx(HeaderMenuProvider, { children: _jsx(RenderPivotTableNextAgGrid, {}) }) }) }) }) }) }));
|
|
82
83
|
}
|
|
83
84
|
function RenderPivotTableNextAgGrid() {
|
|
84
85
|
const agGridReactProps = useAgGridReactProps();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AttributeCell.d.ts","sourceRoot":"","sources":["../../../../src/next/components/Cell/AttributeCell.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"AttributeCell.d.ts","sourceRoot":"","sources":["../../../../src/next/components/Cell/AttributeCell.tsx"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAC9D,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,YAAY,CAAC;AAG5C,OAAO,EACH,KAAK,+BAA+B,EAKvC,MAAM,kBAAkB,CAAC;AAa1B,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,8BAA8B,CAAC;AAmB9D;;;;GAIG;AACH,wBAAgB,aAAa,CACzB,MAAM,EAAE,mBAAmB,GAAG;IAC1B,IAAI,EAAE,SAAS,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,gBAAgB,EAAE,+BAA+B,CAAC;IAClD,SAAS,CAAC,EAAE,SAAS,CAAC;CACzB,2CA8FJ"}
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
// (C) 2025-2026 GoodData Corporation
|
|
3
|
+
import { useCallback, useEffect, useRef } from "react";
|
|
2
4
|
import { emptyHeaderTitleFromIntl, isTableGrandTotalHeaderValue, isTableTotalHeaderValue, } from "@gooddata/sdk-ui";
|
|
3
|
-
import {
|
|
5
|
+
import { useTotalLabelContext } from "../../context/TotalLabelContext.js";
|
|
6
|
+
import { isFirstTotalHeaderAttributeCell, resolveTotalLabelTargetFromTotalLabelCellData, } from "../../features/aggregations/totalLabelTarget.js";
|
|
7
|
+
import { shouldGroupAttribute } from "../../features/columns/shared.js";
|
|
4
8
|
import { e } from "../../features/styling/bem.js";
|
|
5
9
|
import { getPivotCellAttributeImageTestIdProps, getPivotCellTestIdPropsFromCellTypes, } from "../../testing/dataTestIdGenerators.js";
|
|
6
10
|
import { ImageCell } from "./ImageCell.js";
|
|
@@ -27,31 +31,40 @@ export function AttributeCell(params) {
|
|
|
27
31
|
const colId = params.colId;
|
|
28
32
|
const columnDefinition = params.columnDefinition;
|
|
29
33
|
const dataTestIdProps = getPivotCellTestIdPropsFromCellTypes(params.cellTypes);
|
|
34
|
+
const cellData = params.data?.cellDataByColId?.[colId];
|
|
35
|
+
const isTotalHeaderCell = !!cellData && (isTableTotalHeaderValue(cellData) || isTableGrandTotalHeaderValue(cellData));
|
|
36
|
+
const isFirstTotalCell = isTotalHeaderCell && isFirstTotalHeaderAttributeCell(params.data, colId);
|
|
37
|
+
const { enabled: totalLabelsEditable } = useTotalLabelContext();
|
|
38
|
+
const isRenameableTotalCell = totalLabelsEditable && isFirstTotalCell && !!resolveTotalLabelTargetFromTotalLabelCellData(cellData);
|
|
39
|
+
const cellRef = useRef(null);
|
|
40
|
+
const setCellRef = useCallback((node) => {
|
|
41
|
+
cellRef.current = node;
|
|
42
|
+
}, []);
|
|
43
|
+
useEffect(() => {
|
|
44
|
+
const gridCell = cellRef.current?.closest("[role='gridcell']");
|
|
45
|
+
if (!gridCell || !isRenameableTotalCell) {
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
const previousAriaHasPopup = gridCell.getAttribute("aria-haspopup");
|
|
49
|
+
gridCell.setAttribute("aria-haspopup", "menu");
|
|
50
|
+
return () => {
|
|
51
|
+
if (previousAriaHasPopup === null) {
|
|
52
|
+
gridCell.removeAttribute("aria-haspopup");
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
gridCell.setAttribute("aria-haspopup", previousAriaHasPopup);
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
}, [isRenameableTotalCell]);
|
|
30
59
|
// 1) Empty value handling - must be checked first
|
|
31
60
|
if (!value) {
|
|
32
61
|
return _jsx("span", { ...dataTestIdProps, children: emptyHeaderTitleFromIntl(params.intl) });
|
|
33
62
|
}
|
|
34
|
-
//
|
|
35
|
-
//
|
|
36
|
-
//
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
if (isTotalHeaderCell) {
|
|
40
|
-
const attributeColIds = getAttributeColIds(params.data);
|
|
41
|
-
// Find the first attribute column (by columnIndex) that has a total/grand-total header in this row
|
|
42
|
-
const firstTotalAttrColId = attributeColIds
|
|
43
|
-
.filter((id) => {
|
|
44
|
-
const c = params.data?.cellDataByColId?.[id];
|
|
45
|
-
return !!c && (isTableTotalHeaderValue(c) || isTableGrandTotalHeaderValue(c));
|
|
46
|
-
})
|
|
47
|
-
.sort((a, b) => {
|
|
48
|
-
const ai = params.data.cellDataByColId[a].columnDefinition.columnIndex;
|
|
49
|
-
const bi = params.data.cellDataByColId[b].columnDefinition.columnIndex;
|
|
50
|
-
return ai - bi;
|
|
51
|
-
})[0];
|
|
52
|
-
if (firstTotalAttrColId && firstTotalAttrColId !== params.colId) {
|
|
53
|
-
return _jsx("span", { ...dataTestIdProps });
|
|
54
|
-
}
|
|
63
|
+
// If this is a total/grand-total header cell, render the title only in the first attribute
|
|
64
|
+
// column that carries the total header in this row (see isFirstTotalHeaderAttributeCell) -
|
|
65
|
+
// hide it in the others.
|
|
66
|
+
if (isTotalHeaderCell && !isFirstTotalCell) {
|
|
67
|
+
return _jsx("span", { ...dataTestIdProps });
|
|
55
68
|
}
|
|
56
69
|
// 3) Image rendering: check if attribute should be rendered as an image
|
|
57
70
|
// Images respect text wrapping configuration:
|
|
@@ -62,17 +75,17 @@ export function AttributeCell(params) {
|
|
|
62
75
|
const isImage = labelType === IMAGE_LABEL_TYPE;
|
|
63
76
|
if (isImage) {
|
|
64
77
|
const primaryLabelValue = getPrimaryLabelValue(cellData);
|
|
65
|
-
return (_jsx("div", { className: e("cell-image-wrapper"), ...getPivotCellAttributeImageTestIdProps(), children: _jsx(ImageCell, { src: value, alt: primaryLabelValue }) }));
|
|
78
|
+
return (_jsx("div", { ref: setCellRef, className: e("cell-image-wrapper"), ...getPivotCellAttributeImageTestIdProps(), children: _jsx(ImageCell, { src: value, alt: primaryLabelValue }) }));
|
|
66
79
|
}
|
|
67
80
|
// 4) Grouping suppression: do not render repeating attribute values
|
|
68
81
|
const rowIndex = params.node.rowIndex;
|
|
69
82
|
const previousRow = rowIndex ? params.api.getDisplayedRowAtIndex(rowIndex - 1) : null;
|
|
70
83
|
if (!previousRow?.data) {
|
|
71
|
-
return _jsx("span", { ...dataTestIdProps, children: value });
|
|
84
|
+
return (_jsx("span", { ref: setCellRef, ...dataTestIdProps, children: value }));
|
|
72
85
|
}
|
|
73
86
|
const shouldGroup = shouldGroupAttribute(params, previousRow, columnDefinition);
|
|
74
87
|
if (shouldGroup) {
|
|
75
88
|
return _jsx("span", { ...dataTestIdProps });
|
|
76
89
|
}
|
|
77
|
-
return _jsx("span", { ...dataTestIdProps, children: value });
|
|
90
|
+
return (_jsx("span", { ref: setCellRef, ...dataTestIdProps, children: value }));
|
|
78
91
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MeasureHeader.d.ts","sourceRoot":"","sources":["../../../../src/next/components/Header/MeasureHeader.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"MeasureHeader.d.ts","sourceRoot":"","sources":["../../../../src/next/components/Header/MeasureHeader.tsx"],"names":[],"mappings":"AAoBA,OAAO,EAAwB,KAAK,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAYtF;;GAEG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,kBAAkB,2CAiIvD"}
|
|
@@ -3,11 +3,13 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
3
3
|
import { useState } from "react";
|
|
4
4
|
import { isGrandTotalColumnDefinition, isSubtotalColumnDefinition } from "@gooddata/sdk-ui";
|
|
5
5
|
import { useColumnDefs } from "../../context/ColumnDefsContext.js";
|
|
6
|
+
import { resolveTotalLabelTargetFromColumnDefinition } from "../../features/aggregations/totalLabelTarget.js";
|
|
6
7
|
import { e } from "../../features/styling/bem.js";
|
|
7
8
|
import { useHeaderCellAriaLabel } from "../../hooks/header/useHeaderCellAriaLabel.js";
|
|
8
9
|
import { useHeaderMenu } from "../../hooks/header/useHeaderMenu.js";
|
|
9
10
|
import { useHeaderMenuKeyboard } from "../../hooks/header/useHeaderMenuKeyboard.js";
|
|
10
11
|
import { useHeaderSpaceKey } from "../../hooks/header/useHeaderSpaceKey.js";
|
|
12
|
+
import { useTotalLabelHeader } from "../../hooks/header/useTotalLabelHeader.js";
|
|
11
13
|
import { useIsTransposed } from "../../hooks/shared/useIsTransposed.js";
|
|
12
14
|
import { getPivotHeaderClickableAreaTestIdProps, getPivotHeaderTestIdProps, getPivotHeaderTextTestIdProps, } from "../../testing/dataTestIdGenerators.js";
|
|
13
15
|
import { HeaderMenu } from "./HeaderCell/HeaderMenu.js";
|
|
@@ -24,6 +26,14 @@ export function MeasureHeader(params) {
|
|
|
24
26
|
const { isPivoted } = useColumnDefs();
|
|
25
27
|
const colDef = params.column.getColDef();
|
|
26
28
|
const columnDefinition = colDef?.context?.columnDefinition;
|
|
29
|
+
// In a non-transposed table, a total/subtotal column's leaf headers still carry the total's
|
|
30
|
+
// column definition (that's how they pick up total/subtotal styling), but they display the
|
|
31
|
+
// measure name, not the total's own label - PivotGroupHeader owns that label there. Only in a
|
|
32
|
+
// transposed table does this header cell represent the total's own label.
|
|
33
|
+
const totalLabelTarget = isTransposed
|
|
34
|
+
? resolveTotalLabelTargetFromColumnDefinition(columnDefinition)
|
|
35
|
+
: undefined;
|
|
36
|
+
const { label: customTotalLabel, onClick: handleTotalLabelClick } = useTotalLabelHeader(params.eGridHeader, totalLabelTarget);
|
|
27
37
|
const isValueColDef = isValueColumnDef(columnDefinition);
|
|
28
38
|
const columnScope = getColumnScope(columnDefinition);
|
|
29
39
|
const pivotAttributeDescriptors = getPivotAttributeDescriptors(columnScope);
|
|
@@ -47,16 +57,20 @@ export function MeasureHeader(params) {
|
|
|
47
57
|
allowDrilling,
|
|
48
58
|
includeHeaderWrapping,
|
|
49
59
|
includeCellWrapping,
|
|
50
|
-
}, {
|
|
51
|
-
|
|
60
|
+
}, {
|
|
61
|
+
measureIdentifiers: measureIdentifier ? [measureIdentifier] : [],
|
|
62
|
+
pivotAttributeDescriptors,
|
|
63
|
+
ariaLabelDisplayNameOverride: customTotalLabel,
|
|
64
|
+
}, params);
|
|
65
|
+
useHeaderSpaceKey(params, handleTotalLabelClick ?? handleHeaderClick);
|
|
52
66
|
useHeaderMenuKeyboard(params, () => {
|
|
53
67
|
setIsKeyboardTriggered(true);
|
|
54
68
|
setIsMenuOpen(true);
|
|
55
69
|
}, hasMenuItems);
|
|
56
|
-
useHeaderCellAriaLabel(params.eGridHeader, headerCellAriaLabel);
|
|
70
|
+
useHeaderCellAriaLabel(params.eGridHeader, headerCellAriaLabel, params.displayName);
|
|
57
71
|
return (_jsx(HeaderKeyboardHint, { eGridHeader: params.eGridHeader, enabled: isFirstDisplayedColumn(params), canSort: allowSorting, canOpenMenu: hasMenuItems, children: _jsxs("div", { className: e("header-cell", {
|
|
58
72
|
"is-menu-open": isMenuOpen,
|
|
59
|
-
}), ...getPivotHeaderTestIdProps({ isTotal, isSubtotal }), children: [_jsxs("div", { className: "gd-header-content", "aria-hidden": "true", children: [_jsx("span", { className: "gd-header-text", ...getPivotHeaderTextTestIdProps(), children: params.displayName }), !!colDef.sortable && sortDirection ? (_jsx(SortIndicator, { sortDirection: sortDirection, sortIndex: sortIndex })) : null] }), !!colDef.sortable && (_jsx("div", { className: "gd-header-cell-clickable-area", "aria-hidden": "true", ...getPivotHeaderClickableAreaTestIdProps(), onClick: handleHeaderClick })), hasMenuItems ? (_jsx(HeaderMenu, { aggregationsItems: aggregationsItems, textWrappingItems: textWrappingItems, sortingItems: sortingItems, onAggregationsItemClick: handleAggregationsItemClick, onTextWrappingItemClick: handleTextWrappingItemClick, onSortingItemClick: handleSortingItemClick, isMenuOpened: isMenuOpen, onMenuOpenedChange: (opened) => {
|
|
73
|
+
}), onClick: handleTotalLabelClick, ...getPivotHeaderTestIdProps({ isTotal, isSubtotal }), children: [_jsxs("div", { className: "gd-header-content", "aria-hidden": "true", children: [_jsx("span", { className: "gd-header-text", ...getPivotHeaderTextTestIdProps(), children: customTotalLabel ?? params.displayName }), !!colDef.sortable && sortDirection ? (_jsx(SortIndicator, { sortDirection: sortDirection, sortIndex: sortIndex })) : null] }), !!colDef.sortable && (_jsx("div", { className: "gd-header-cell-clickable-area", "aria-hidden": "true", ...getPivotHeaderClickableAreaTestIdProps(), onClick: handleHeaderClick })), hasMenuItems ? (_jsx(HeaderMenu, { aggregationsItems: aggregationsItems, textWrappingItems: textWrappingItems, sortingItems: sortingItems, onAggregationsItemClick: handleAggregationsItemClick, onTextWrappingItemClick: handleTextWrappingItemClick, onSortingItemClick: handleSortingItemClick, isMenuOpened: isMenuOpen, onMenuOpenedChange: (opened) => {
|
|
60
74
|
setIsMenuOpen(opened);
|
|
61
75
|
if (!opened) {
|
|
62
76
|
setIsKeyboardTriggered(false);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PivotGroupHeader.d.ts","sourceRoot":"","sources":["../../../../src/next/components/Header/PivotGroupHeader.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"PivotGroupHeader.d.ts","sourceRoot":"","sources":["../../../../src/next/components/Header/PivotGroupHeader.tsx"],"names":[],"mappings":"AAsBA,OAAO,EAA6B,KAAK,uBAAuB,EAAE,MAAM,uBAAuB,CAAC;AAKhG,UAAU,qBAAsB,SAAQ,uBAAuB;IAC3D,kBAAkB,EAAE,MAAM,EAAE,CAAC;IAC7B,eAAe,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,qBAAqB,2CAuJ7D"}
|
|
@@ -2,12 +2,14 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
// (C) 2025-2026 GoodData Corporation
|
|
3
3
|
import { useCallback, useState } from "react";
|
|
4
4
|
import { isGrandTotalColumnDefinition, isSubtotalColumnDefinition } from "@gooddata/sdk-ui";
|
|
5
|
+
import { resolveTotalLabelTargetFromColumnDefinition } from "../../features/aggregations/totalLabelTarget.js";
|
|
5
6
|
import { e } from "../../features/styling/bem.js";
|
|
6
7
|
import { useHeaderCellAriaLabel } from "../../hooks/header/useHeaderCellAriaLabel.js";
|
|
7
8
|
import { useHeaderGroupDrilling } from "../../hooks/header/useHeaderGroupDrilling.js";
|
|
8
9
|
import { useHeaderMenu } from "../../hooks/header/useHeaderMenu.js";
|
|
9
10
|
import { useHeaderMenuKeyboard } from "../../hooks/header/useHeaderMenuKeyboard.js";
|
|
10
11
|
import { useHeaderSpaceKey } from "../../hooks/header/useHeaderSpaceKey.js";
|
|
12
|
+
import { useTotalLabelHeader } from "../../hooks/header/useTotalLabelHeader.js";
|
|
11
13
|
import { useIsTransposed } from "../../hooks/shared/useIsTransposed.js";
|
|
12
14
|
import { getPivotHeaderClickableAreaTestIdProps, getPivotHeaderTestIdProps, getPivotHeaderTextTestIdProps, } from "../../testing/dataTestIdGenerators.js";
|
|
13
15
|
import { HeaderMenu } from "./HeaderCell/HeaderMenu.js";
|
|
@@ -21,6 +23,7 @@ export function PivotGroupHeader(params) {
|
|
|
21
23
|
const isTransposed = useIsTransposed();
|
|
22
24
|
const colGroupDef = params.columnGroup.getColGroupDef();
|
|
23
25
|
const columnDefinition = colGroupDef?.context?.columnDefinition;
|
|
26
|
+
const resolvedTotalLabelTarget = resolveTotalLabelTargetFromColumnDefinition(columnDefinition);
|
|
24
27
|
const isValueColDef = isValueColumnDef(columnDefinition);
|
|
25
28
|
const columnScope = getColumnScope(columnDefinition);
|
|
26
29
|
const pivotAttributeDescriptors = getPivotAttributeDescriptors(columnScope);
|
|
@@ -35,6 +38,15 @@ export function PivotGroupHeader(params) {
|
|
|
35
38
|
colGroupDef.children?.some((child) => isSubtotalColumnDefinition(child.context?.columnDefinition));
|
|
36
39
|
const isTotalHeader = isTotal || isTotalGroup;
|
|
37
40
|
const isSubtotalHeader = isSubtotal || isSubtotalGroup;
|
|
41
|
+
// shouldSkipHeaderName blanks nested repeats of a total group ("Sum" > "Sum"); they must not
|
|
42
|
+
// become rename targets. When transposed, MeasureHeader's own leaf cell owns the total's label
|
|
43
|
+
// instead (see its comment) - this group must not also show/target it, or both cells end up
|
|
44
|
+
// showing "Sum" at once.
|
|
45
|
+
const hasVisibleHeaderName = colGroupDef?.headerName !== undefined;
|
|
46
|
+
const ownsTotalLabel = hasVisibleHeaderName && (!isTransposed || !(isTotalHeader || isSubtotalHeader));
|
|
47
|
+
const totalLabelTarget = ownsTotalLabel ? resolvedTotalLabelTarget : undefined;
|
|
48
|
+
const { label: customTotalLabel, onClick: handleTotalLabelClick } = useTotalLabelHeader(params.eGridHeader, totalLabelTarget);
|
|
49
|
+
const visibleHeaderText = ownsTotalLabel ? (customTotalLabel ?? params.displayName) : "";
|
|
38
50
|
const allowAggregations = params.pivotGroupDepth !== 0 && // Not description level of the pivoting group
|
|
39
51
|
isValueColDef &&
|
|
40
52
|
!isTransposed;
|
|
@@ -55,6 +67,7 @@ export function PivotGroupHeader(params) {
|
|
|
55
67
|
}, {
|
|
56
68
|
measureIdentifiers: params.measureIdentifiers,
|
|
57
69
|
pivotAttributeDescriptors,
|
|
70
|
+
ariaLabelDisplayNameOverride: customTotalLabel,
|
|
58
71
|
}, params);
|
|
59
72
|
const hasMenuItems = aggregationsItems.length > 0 || textWrappingItems.length > 0;
|
|
60
73
|
const { handleHeaderDrill, isDrillable } = useHeaderGroupDrilling(params);
|
|
@@ -64,20 +77,20 @@ export function PivotGroupHeader(params) {
|
|
|
64
77
|
handleHeaderDrill(e);
|
|
65
78
|
}
|
|
66
79
|
}, [isDrillable, handleHeaderDrill]);
|
|
67
|
-
useHeaderSpaceKey(params, handleHeaderClick);
|
|
80
|
+
useHeaderSpaceKey(params, handleTotalLabelClick ?? handleHeaderClick);
|
|
68
81
|
useHeaderMenuKeyboard(params, () => {
|
|
69
82
|
setIsKeyboardTriggered(true);
|
|
70
83
|
setIsMenuOpen(true);
|
|
71
84
|
}, hasMenuItems);
|
|
72
|
-
useHeaderCellAriaLabel(params.eGridHeader, headerCellAriaLabel);
|
|
85
|
+
useHeaderCellAriaLabel(params.eGridHeader, headerCellAriaLabel, params.displayName);
|
|
73
86
|
return (_jsxs("div", { className: e("header-cell", {
|
|
74
87
|
"is-menu-open": isMenuOpen,
|
|
75
88
|
drillable: isDrillable,
|
|
76
|
-
}), ...getPivotHeaderTestIdProps({
|
|
89
|
+
}), onClick: handleTotalLabelClick, ...getPivotHeaderTestIdProps({
|
|
77
90
|
drillable: isDrillable,
|
|
78
91
|
isTotal: isTotalHeader,
|
|
79
92
|
isSubtotal: isSubtotalHeader,
|
|
80
|
-
}), children: [_jsx("div", { className: "gd-header-content", "aria-hidden": "true", children: _jsx("span", { className: "gd-header-text", ...getPivotHeaderTextTestIdProps(), children:
|
|
93
|
+
}), children: [_jsx("div", { className: "gd-header-content", "aria-hidden": "true", children: _jsx("span", { className: "gd-header-text", ...(ownsTotalLabel ? getPivotHeaderTextTestIdProps() : {}), children: visibleHeaderText }) }), isDrillable ? (_jsx("div", { className: "gd-header-cell-clickable-area", "aria-hidden": "true", ...getPivotHeaderClickableAreaTestIdProps(), onClick: handleHeaderClick })) : null, hasMenuItems ? (_jsx(HeaderMenu, { aggregationsItems: aggregationsItems, textWrappingItems: textWrappingItems, sortingItems: sortingItems, onAggregationsItemClick: handleAggregationsItemClick, onTextWrappingItemClick: handleTextWrappingItemClick, onSortingItemClick: handleSortingItemClick, isMenuOpened: isMenuOpen, onMenuOpenedChange: (opened) => {
|
|
81
94
|
setIsMenuOpen(opened);
|
|
82
95
|
if (!opened) {
|
|
83
96
|
setIsKeyboardTriggered(false);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"constructAggregationsMenuItems.d.ts","sourceRoot":"","sources":["../../../../../src/next/components/Header/utils/constructAggregationsMenuItems.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,YAAY,CAAC;AAE5C,OAAO,EACH,KAAK,UAAU,EACf,KAAK,oBAAoB,EACzB,KAAK,MAAM,EAGd,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"constructAggregationsMenuItems.d.ts","sourceRoot":"","sources":["../../../../../src/next/components/Header/utils/constructAggregationsMenuItems.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,YAAY,CAAC;AAE5C,OAAO,EACH,KAAK,UAAU,EACf,KAAK,oBAAoB,EACzB,KAAK,MAAM,EAGd,MAAM,qBAAqB,CAAC;AAK7B,OAAO,EACH,KAAK,qBAAqB,EAE1B,KAAK,KAAK,EACb,MAAM,wBAAwB,CAAC;AA+HhC;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,8BAA8B,uBACnB,MAAM,EAAE,oBACV,MAAM,EAAE,uBACL,MAAM,EAAE,QACvB,UAAU,EAAE,WACT,UAAU,EAAE,2BACI,oBAAoB,EAAE,6BACpB,oBAAoB,EAAE,QAC3C,KAAK,QACL,SAAS,oBACG,MAAM,KACzB,qBAAqB,EAkDvB,CAAC"}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
// (C) 2025-2026 GoodData Corporation
|
|
2
|
-
import { compact
|
|
2
|
+
import { compact } from "lodash-es";
|
|
3
3
|
import { newTotal, } from "@gooddata/sdk-model";
|
|
4
4
|
import { messages } from "../../../../locales.js";
|
|
5
5
|
import { DEFAULT_TOTAL_FUNCTIONS } from "../../../constants/internal.js";
|
|
6
|
+
import { areTotalsSameDefinition } from "../../../features/aggregations/totals.js";
|
|
6
7
|
import { getPreviousAttributeHeaderName } from "./common.js";
|
|
7
8
|
/**
|
|
8
9
|
* Creates total item for the main attribute.
|
|
@@ -129,5 +130,5 @@ function isTotalActive(currentTotals, totalDefinitions) {
|
|
|
129
130
|
if (totalDefinitions.length === 0) {
|
|
130
131
|
return false;
|
|
131
132
|
}
|
|
132
|
-
return totalDefinitions.every((totalDef) => currentTotals.some((currentTotal) =>
|
|
133
|
+
return totalDefinitions.every((totalDef) => currentTotals.some((currentTotal) => areTotalsSameDefinition(currentTotal, totalDef)));
|
|
133
134
|
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { type IntlShape } from "react-intl";
|
|
2
|
+
export interface IRenameTotalLabelPopoverProps {
|
|
3
|
+
intl: IntlShape;
|
|
4
|
+
anchor: HTMLElement;
|
|
5
|
+
/** Current custom label (alias), or empty string when none is set. */
|
|
6
|
+
currentLabel: string;
|
|
7
|
+
/** Localized default function-name label, shown as the input placeholder. */
|
|
8
|
+
defaultLabel: string;
|
|
9
|
+
/** Called with the new label, or `undefined` to reset to the default. */
|
|
10
|
+
onSave: (newLabel: string | undefined) => void;
|
|
11
|
+
onCancel: () => void;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Small popover anchored to the selected total. Holds a label input (with the default label as
|
|
15
|
+
* placeholder fallback), plus Reset to default / Cancel / Save controls. Saving an empty value is
|
|
16
|
+
* treated as a reset to the default label.
|
|
17
|
+
*/
|
|
18
|
+
export declare function RenameTotalLabelPopover({ intl, anchor, currentLabel, defaultLabel, onSave, onCancel, }: IRenameTotalLabelPopoverProps): import("react/jsx-runtime").JSX.Element;
|
|
19
|
+
//# sourceMappingURL=RenameTotalLabelPopover.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RenameTotalLabelPopover.d.ts","sourceRoot":"","sources":["../../../../src/next/components/TotalLabel/RenameTotalLabelPopover.tsx"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,YAAY,CAAC;AAQ5C,MAAM,WAAW,6BAA6B;IAC1C,IAAI,EAAE,SAAS,CAAC;IAChB,MAAM,EAAE,WAAW,CAAC;IACpB,sEAAsE;IACtE,YAAY,EAAE,MAAM,CAAC;IACrB,6EAA6E;IAC7E,YAAY,EAAE,MAAM,CAAC;IACrB,yEAAyE;IACzE,MAAM,EAAE,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,KAAK,IAAI,CAAC;IAC/C,QAAQ,EAAE,MAAM,IAAI,CAAC;CACxB;AAED;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,EACpC,IAAI,EACJ,MAAM,EACN,YAAY,EACZ,YAAY,EACZ,MAAM,EACN,QAAQ,GACX,EAAE,6BAA6B,2CA2D/B"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
// (C) 2025-2026 GoodData Corporation
|
|
3
|
+
import { useState } from "react";
|
|
4
|
+
import { Button, Input, Overlay, UiFocusTrap } from "@gooddata/sdk-ui-kit";
|
|
5
|
+
import { messages } from "../../../locales.js";
|
|
6
|
+
const MAX_LABEL_LENGTH = 255;
|
|
7
|
+
/**
|
|
8
|
+
* Small popover anchored to the selected total. Holds a label input (with the default label as
|
|
9
|
+
* placeholder fallback), plus Reset to default / Cancel / Save controls. Saving an empty value is
|
|
10
|
+
* treated as a reset to the default label.
|
|
11
|
+
*/
|
|
12
|
+
export function RenameTotalLabelPopover({ intl, anchor, currentLabel, defaultLabel, onSave, onCancel, }) {
|
|
13
|
+
const [value, setValue] = useState(currentLabel);
|
|
14
|
+
const save = () => {
|
|
15
|
+
const trimmed = value.trim();
|
|
16
|
+
onSave(trimmed.length > 0 ? trimmed : undefined);
|
|
17
|
+
};
|
|
18
|
+
return (_jsx(Overlay, { className: "overlay", alignTo: anchor, alignPoints: [{ align: "bl tl" }, { align: "tl bl" }, { align: "br tr" }], closeOnOutsideClick: true, closeOnParentScroll: true, closeOnEscape: true, onClose: onCancel, children: _jsxs(UiFocusTrap, { root: _jsx("div", { className: "gd-pivot-table-rename-total-popover s-pivot-table-rename-total-popover", role: "dialog", "aria-label": intl.formatMessage(messages["renameTotalLabel"]) }), children: [_jsx(Input, { className: "s-rename-total-label-input", value: value, placeholder: defaultLabel, autofocus: true, isSmall: true, maxlength: MAX_LABEL_LENGTH, accessibilityConfig: { ariaLabel: intl.formatMessage(messages["renameTotalLabel"]) }, onChange: (newValue) => setValue(String(newValue)), onEnterKeyPress: save, onEscKeyPress: onCancel }), _jsxs("div", { className: "gd-pivot-table-rename-total-popover-buttons", children: [_jsx(Button, { className: "gd-button-link s-rename-total-reset", value: intl.formatMessage(messages["resetTotalLabel"]), onClick: () => onSave(undefined) }), _jsx(Button, { className: "gd-button-secondary s-rename-total-cancel", value: intl.formatMessage(messages["cancel"]), onClick: onCancel }), _jsx(Button, { className: "gd-button-action s-rename-total-save", value: intl.formatMessage(messages["save"]), onClick: save })] })] }) }));
|
|
19
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type IntlShape } from "react-intl";
|
|
2
|
+
export interface ITotalActionMenuProps {
|
|
3
|
+
intl: IntlShape;
|
|
4
|
+
anchor: HTMLElement;
|
|
5
|
+
onRenameClick: () => void;
|
|
6
|
+
onResetClick: () => void;
|
|
7
|
+
onClose: () => void;
|
|
8
|
+
}
|
|
9
|
+
export declare function TotalActionMenu({ intl, anchor, onRenameClick, onResetClick, onClose, }: ITotalActionMenuProps): import("react/jsx-runtime").JSX.Element;
|
|
10
|
+
//# sourceMappingURL=TotalActionMenu.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TotalActionMenu.d.ts","sourceRoot":"","sources":["../../../../src/next/components/TotalLabel/TotalActionMenu.tsx"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,YAAY,CAAC;AAM5C,MAAM,WAAW,qBAAqB;IAClC,IAAI,EAAE,SAAS,CAAC;IAChB,MAAM,EAAE,WAAW,CAAC;IACpB,aAAa,EAAE,MAAM,IAAI,CAAC;IAC1B,YAAY,EAAE,MAAM,IAAI,CAAC;IACzB,OAAO,EAAE,MAAM,IAAI,CAAC;CACvB;AAKD,wBAAgB,eAAe,CAAC,EAC5B,IAAI,EACJ,MAAM,EACN,aAAa,EACb,YAAY,EACZ,OAAO,GACV,EAAE,qBAAqB,2CAsDvB"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
// (C) 2025-2026 GoodData Corporation
|
|
3
|
+
import { useId, useMemo } from "react";
|
|
4
|
+
import { Overlay, UiMenu } from "@gooddata/sdk-ui-kit";
|
|
5
|
+
import { messages } from "../../../locales.js";
|
|
6
|
+
export function TotalActionMenu({ intl, anchor, onRenameClick, onResetClick, onClose, }) {
|
|
7
|
+
const menuId = useId();
|
|
8
|
+
const items = useMemo(() => [
|
|
9
|
+
{
|
|
10
|
+
type: "interactive",
|
|
11
|
+
id: "rename",
|
|
12
|
+
stringTitle: intl.formatMessage(messages["renameTotalLabel"]),
|
|
13
|
+
data: "rename",
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
type: "interactive",
|
|
17
|
+
id: "reset",
|
|
18
|
+
stringTitle: intl.formatMessage(messages["resetTotalLabel"]),
|
|
19
|
+
data: "reset",
|
|
20
|
+
},
|
|
21
|
+
], [intl]);
|
|
22
|
+
return (_jsx(Overlay, { alignTo: anchor, alignPoints: [{ align: "bl tl" }, { align: "tl bl" }, { align: "br tr" }], closeOnOutsideClick: true, closeOnParentScroll: true, closeOnEscape: true, closeOnMouseDrag: true, onClose: onClose, children: _jsx(UiMenu, { items: items, onSelect: (item) => {
|
|
23
|
+
if (item.data === "rename") {
|
|
24
|
+
onRenameClick();
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
onResetClick();
|
|
28
|
+
}
|
|
29
|
+
}, onClose: onClose, shouldCloseOnSelect: false, size: "small", dataTestId: "pivot-table-total-action-menu", itemDataTestId: (item) => item.type === "interactive" ? `total-action-${item.data}` : undefined, ariaAttributes: {
|
|
30
|
+
id: menuId,
|
|
31
|
+
"aria-label": intl.formatMessage(messages["totalLabelActions"]),
|
|
32
|
+
} }) }));
|
|
33
|
+
}
|
|
@@ -13,7 +13,7 @@ export declare function PivotTablePropsProvider({ children, ...props }: ICorePiv
|
|
|
13
13
|
type WithRequired<T, K extends keyof T> = T & Required<Pick<T, K>>;
|
|
14
14
|
type ColumnSizingDefaults = "columnWidths";
|
|
15
15
|
type ColumnSizingWithDefaults = WithRequired<IColumnSizing, ColumnSizingDefaults>;
|
|
16
|
-
type MenuDefaults = "aggregations" | "aggregationsSubMenu" | "aggregationsSubMenuForRows" | "aggregationTypes";
|
|
16
|
+
type MenuDefaults = "aggregations" | "aggregationsSubMenu" | "aggregationsSubMenuForRows" | "aggregationTypes" | "totalLabelsEditable";
|
|
17
17
|
type MenuWithDefaults = WithRequired<IMenu, MenuDefaults>;
|
|
18
18
|
type ConfigDefaults = "measureGroupDimension" | "columnHeadersPosition" | "columnSizing" | "textWrapping" | "menu";
|
|
19
19
|
type ConfigWithDefaults = WithRequired<PivotTableNextConfigWithConditionalFormatting, ConfigDefaults> & {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PivotTablePropsContext.d.ts","sourceRoot":"","sources":["../../../src/next/context/PivotTablePropsContext.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,SAAS,EAA6B,MAAM,OAAO,CAAC;AAElE,OAAO,EAAE,KAAK,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAgB5D,OAAO,EAAE,KAAK,6CAA6C,EAAE,MAAM,mCAAmC,CAAC;AACvG,OAAO,EAAE,KAAK,wBAAwB,EAAE,MAAM,sBAAsB,CAAC;AACrE,OAAO,EAAE,KAAK,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,KAAK,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAI1D;;GAEG;AACH,wBAAgB,uBAAuB,CAAC,EACpC,QAAQ,EACR,GAAG,KAAK,EACX,EAAE,wBAAwB,GAAG;IAAE,QAAQ,EAAE,SAAS,CAAA;CAAE,2CAEpD;AAED,KAAK,YAAY,CAAC,CAAC,EAAE,CAAC,SAAS,MAAM,CAAC,IAAI,CAAC,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;AAEnE,KAAK,oBAAoB,GAAG,cAAc,CAAC;AAE3C,KAAK,wBAAwB,GAAG,YAAY,CAAC,aAAa,EAAE,oBAAoB,CAAC,CAAC;AAElF,KAAK,YAAY,GACX,cAAc,GACd,qBAAqB,GACrB,4BAA4B,GAC5B,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"PivotTablePropsContext.d.ts","sourceRoot":"","sources":["../../../src/next/context/PivotTablePropsContext.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,SAAS,EAA6B,MAAM,OAAO,CAAC;AAElE,OAAO,EAAE,KAAK,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAgB5D,OAAO,EAAE,KAAK,6CAA6C,EAAE,MAAM,mCAAmC,CAAC;AACvG,OAAO,EAAE,KAAK,wBAAwB,EAAE,MAAM,sBAAsB,CAAC;AACrE,OAAO,EAAE,KAAK,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,KAAK,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAI1D;;GAEG;AACH,wBAAgB,uBAAuB,CAAC,EACpC,QAAQ,EACR,GAAG,KAAK,EACX,EAAE,wBAAwB,GAAG;IAAE,QAAQ,EAAE,SAAS,CAAA;CAAE,2CAEpD;AAED,KAAK,YAAY,CAAC,CAAC,EAAE,CAAC,SAAS,MAAM,CAAC,IAAI,CAAC,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;AAEnE,KAAK,oBAAoB,GAAG,cAAc,CAAC;AAE3C,KAAK,wBAAwB,GAAG,YAAY,CAAC,aAAa,EAAE,oBAAoB,CAAC,CAAC;AAElF,KAAK,YAAY,GACX,cAAc,GACd,qBAAqB,GACrB,4BAA4B,GAC5B,kBAAkB,GAClB,qBAAqB,CAAC;AAE5B,KAAK,gBAAgB,GAAG,YAAY,CAAC,KAAK,EAAE,YAAY,CAAC,CAAC;AAE1D,KAAK,cAAc,GACb,uBAAuB,GACvB,uBAAuB,GACvB,cAAc,GACd,cAAc,GACd,MAAM,CAAC;AAEb,KAAK,kBAAkB,GAAG,YAAY,CAAC,6CAA6C,EAAE,cAAc,CAAC,GAAG;IACpG,YAAY,EAAE,wBAAwB,CAAC;IACvC,IAAI,EAAE,gBAAgB,CAAC;CAC1B,CAAC;AAEF,KAAK,iBAAiB,GAChB,MAAM,GACN,SAAS,GACT,UAAU,GACV,SAAS,GACT,QAAQ,GACR,QAAQ,GACR,UAAU,GACV,gBAAgB,CAAC;AAEvB,KAAK,qBAAqB,GAAG,YAAY,CAAC,wBAAwB,EAAE,iBAAiB,CAAC,CAAC;AAEvF,KAAK,2BAA2B,GAAG,qBAAqB,GAAG;IACvD,MAAM,EAAE,kBAAkB,CAAC;IAC3B,UAAU,EAAE,gBAAgB,CAAC;CAChC,CAAC;AAEF;;GAEG;AACH,wBAAgB,2BAA2B,CAAC,KAAK,EAAE,wBAAwB,GAAG,2BAA2B,CAoCxG;AAED;;GAEG;AACH,wBAAgB,kBAAkB,IAAI,2BAA2B,CAgChE"}
|
|
@@ -43,6 +43,7 @@ export function applyPivotTableDefaultProps(props) {
|
|
|
43
43
|
aggregationsSubMenu: props.config?.menu?.aggregationsSubMenu ?? false,
|
|
44
44
|
aggregationsSubMenuForRows: props.config?.menu?.aggregationsSubMenuForRows ?? false,
|
|
45
45
|
aggregationTypes: props.config?.menu?.aggregationTypes ?? DEFAULT_TOTAL_FUNCTIONS,
|
|
46
|
+
totalLabelsEditable: props.config?.menu?.totalLabelsEditable ?? false,
|
|
46
47
|
},
|
|
47
48
|
},
|
|
48
49
|
execConfig: props.execConfig ?? EMPTY_OBJECT,
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type ReactNode } from "react";
|
|
2
|
+
import { type IOpenTotalLabelMenuRequest, type ITotalLabelTarget } from "../features/aggregations/totalLabelTarget.js";
|
|
3
|
+
interface ITotalLabelContextValue {
|
|
4
|
+
enabled: boolean;
|
|
5
|
+
openTotalLabelMenu: (request: IOpenTotalLabelMenuRequest) => void;
|
|
6
|
+
getCustomTotalLabel: (target: ITotalLabelTarget | undefined) => string | undefined;
|
|
7
|
+
}
|
|
8
|
+
export declare function TotalLabelProvider({ children }: {
|
|
9
|
+
children: ReactNode;
|
|
10
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
11
|
+
export declare function useTotalLabelContext(): ITotalLabelContextValue;
|
|
12
|
+
export {};
|
|
13
|
+
//# sourceMappingURL=TotalLabelContext.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TotalLabelContext.d.ts","sourceRoot":"","sources":["../../../src/next/context/TotalLabelContext.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,SAAS,EAAwE,MAAM,OAAO,CAAC;AAS7G,OAAO,EACH,KAAK,0BAA0B,EAC/B,KAAK,iBAAiB,EAEzB,MAAM,8CAA8C,CAAC;AAYtD,UAAU,uBAAuB;IAC7B,OAAO,EAAE,OAAO,CAAC;IACjB,kBAAkB,EAAE,CAAC,OAAO,EAAE,0BAA0B,KAAK,IAAI,CAAC;IAClE,mBAAmB,EAAE,CAAC,MAAM,EAAE,iBAAiB,GAAG,SAAS,KAAK,MAAM,GAAG,SAAS,CAAC;CACtF;AAID,wBAAgB,kBAAkB,CAAC,EAAE,QAAQ,EAAE,EAAE;IAAE,QAAQ,EAAE,SAAS,CAAA;CAAE,2CAiHvE;AAED,wBAAgB,oBAAoB,IAAI,uBAAuB,CAO9D"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
// (C) 2026 GoodData Corporation
|
|
3
|
+
import { createContext, useCallback, useContext, useEffect, useMemo, useState } from "react";
|
|
4
|
+
import { useIntl } from "react-intl";
|
|
5
|
+
import { bucketsFind } from "@gooddata/sdk-model";
|
|
6
|
+
import { totalTypeMessages } from "../../locales.js";
|
|
7
|
+
import { RenameTotalLabelPopover } from "../components/TotalLabel/RenameTotalLabelPopover.js";
|
|
8
|
+
import { TotalActionMenu } from "../components/TotalLabel/TotalActionMenu.js";
|
|
9
|
+
import { getTotalAliasForTarget, } from "../features/aggregations/totalLabelTarget.js";
|
|
10
|
+
import { updateTotalAlias } from "../features/aggregations/totals.js";
|
|
11
|
+
import { useCurrentDataView } from "./CurrentDataViewContext.js";
|
|
12
|
+
import { usePivotTableProps } from "./PivotTablePropsContext.js";
|
|
13
|
+
const TotalLabelContext = createContext(undefined);
|
|
14
|
+
export function TotalLabelProvider({ children }) {
|
|
15
|
+
const intl = useIntl();
|
|
16
|
+
const { config, execution, pushData } = usePivotTableProps();
|
|
17
|
+
const { currentDataView } = useCurrentDataView();
|
|
18
|
+
const [menuState, setMenuState] = useState(null);
|
|
19
|
+
const enabled = config.menu.totalLabelsEditable === true;
|
|
20
|
+
const getTotalsForWrite = useCallback((bucketType) => bucketsFind(execution.definition.buckets, bucketType)?.totals ?? [], [execution]);
|
|
21
|
+
const getCustomTotalLabel = useCallback((target) => {
|
|
22
|
+
if (!target) {
|
|
23
|
+
return undefined;
|
|
24
|
+
}
|
|
25
|
+
return getTotalAliasForTarget(currentDataView?.definition.buckets ?? [], target);
|
|
26
|
+
}, [currentDataView]);
|
|
27
|
+
const openTotalLabelMenu = useCallback((request) => {
|
|
28
|
+
if (!enabled) {
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
const currentLabel = getCustomTotalLabel(request) ?? "";
|
|
32
|
+
setMenuState({
|
|
33
|
+
...request,
|
|
34
|
+
currentLabel,
|
|
35
|
+
defaultLabel: intl.formatMessage(totalTypeMessages[request.type]),
|
|
36
|
+
mode: "menu",
|
|
37
|
+
});
|
|
38
|
+
}, [enabled, getCustomTotalLabel, intl]);
|
|
39
|
+
const closeMenu = useCallback(() => {
|
|
40
|
+
const anchor = menuState?.anchor;
|
|
41
|
+
setMenuState(null);
|
|
42
|
+
anchor?.focus();
|
|
43
|
+
}, [menuState?.anchor]);
|
|
44
|
+
useEffect(() => {
|
|
45
|
+
if (!enabled && menuState) {
|
|
46
|
+
closeMenu();
|
|
47
|
+
}
|
|
48
|
+
}, [enabled, menuState, closeMenu]);
|
|
49
|
+
const saveLabel = useCallback((newLabel) => {
|
|
50
|
+
if (!menuState || !pushData || !enabled) {
|
|
51
|
+
closeMenu();
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
const updatedTotals = updateTotalAlias(getTotalsForWrite(menuState.bucketType), menuState.type, menuState.attributeIdentifier, newLabel, menuState.measureIdentifier);
|
|
55
|
+
if (updatedTotals) {
|
|
56
|
+
pushData({
|
|
57
|
+
properties: {
|
|
58
|
+
totals: updatedTotals,
|
|
59
|
+
bucketType: menuState.bucketType,
|
|
60
|
+
},
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
closeMenu();
|
|
64
|
+
}, [closeMenu, enabled, getTotalsForWrite, menuState, pushData]);
|
|
65
|
+
const contextValue = useMemo(() => ({ enabled, openTotalLabelMenu, getCustomTotalLabel }), [enabled, getCustomTotalLabel, openTotalLabelMenu]);
|
|
66
|
+
return (_jsxs(TotalLabelContext.Provider, { value: contextValue, children: [children, menuState?.mode === "menu" ? (_jsx(TotalActionMenu, { intl: intl, anchor: menuState.anchor, onRenameClick: () => setMenuState((current) => (current ? { ...current, mode: "popover" } : null)), onResetClick: () => saveLabel(undefined), onClose: closeMenu })) : null, menuState?.mode === "popover" ? (_jsx(RenameTotalLabelPopover, { intl: intl, anchor: menuState.anchor, currentLabel: menuState.currentLabel, defaultLabel: menuState.defaultLabel, onSave: saveLabel, onCancel: closeMenu })) : null] }));
|
|
67
|
+
}
|
|
68
|
+
export function useTotalLabelContext() {
|
|
69
|
+
const context = useContext(TotalLabelContext);
|
|
70
|
+
if (!context) {
|
|
71
|
+
throw new Error("useTotalLabelContext must be used within TotalLabelProvider");
|
|
72
|
+
}
|
|
73
|
+
return context;
|
|
74
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { type IBucket, type TotalType } from "@gooddata/sdk-model";
|
|
2
|
+
import { type DataViewFacade, type ITableColumnDefinition, type ITableDataValue } from "@gooddata/sdk-ui";
|
|
3
|
+
import { type AgGridRowData } from "../../types/internal.js";
|
|
4
|
+
import { type TotalLabelBucketType } from "./totals.js";
|
|
5
|
+
export interface ITotalLabelTarget {
|
|
6
|
+
type: TotalType;
|
|
7
|
+
attributeIdentifier: string;
|
|
8
|
+
bucketType: TotalLabelBucketType;
|
|
9
|
+
measureIdentifier?: string;
|
|
10
|
+
}
|
|
11
|
+
export interface IOpenTotalLabelMenuRequest extends ITotalLabelTarget {
|
|
12
|
+
anchor: HTMLElement;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Resolves the total definition represented by a total/grand-total label cell (e.g. the "Sum A" or
|
|
16
|
+
* "RowSum Σ" cell, not a numeric value cell). Only grand-total and subtotal rows ever produce these
|
|
17
|
+
* label cells - every "totalHeader"/"grandTotalHeader" cell sdk-ui emits comes from a
|
|
18
|
+
* grandTotalRow/subtotalRow mapper, so its rowDefinition is always a total row. Total/subtotal
|
|
19
|
+
* *column* headers (not cell data) are resolved separately by resolveTotalLabelTargetFromColumnDefinition.
|
|
20
|
+
*/
|
|
21
|
+
export declare function resolveTotalLabelTargetFromTotalLabelCellData(cellData: ITableDataValue | undefined): ITotalLabelTarget | undefined;
|
|
22
|
+
export declare function isFirstTotalHeaderAttributeCell(rowData: AgGridRowData | undefined, colId: string): boolean;
|
|
23
|
+
/**
|
|
24
|
+
* Resolves the total definition represented by a total or subtotal column.
|
|
25
|
+
*/
|
|
26
|
+
export declare function resolveTotalLabelTargetFromColumnDefinition(columnDefinition: ITableColumnDefinition | undefined): ITotalLabelTarget | undefined;
|
|
27
|
+
export declare function getTotalAliasForTarget(buckets: IBucket[], target: ITotalLabelTarget): string | undefined;
|
|
28
|
+
/**
|
|
29
|
+
* Resolves the custom label (alias) for the total a cell represents, if it has one. A single source
|
|
30
|
+
* of truth for both applying the alias to a cell's formatted value and for later deciding whether
|
|
31
|
+
* that formatted value is already-final custom text that must not be run through total-type
|
|
32
|
+
* translation (an alias that happens to equal a raw type key, e.g. "sum", must still display verbatim).
|
|
33
|
+
*/
|
|
34
|
+
export declare function resolveCustomTotalLabel(cellData: ITableDataValue | undefined, dataView: DataViewFacade): string | undefined;
|
|
35
|
+
//# sourceMappingURL=totalLabelTarget.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"totalLabelTarget.d.ts","sourceRoot":"","sources":["../../../../src/next/features/aggregations/totalLabelTarget.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,OAAO,EAAE,KAAK,SAAS,EAAe,MAAM,qBAAqB,CAAC;AAChF,OAAO,EAEH,KAAK,cAAc,EACnB,KAAK,sBAAsB,EAC3B,KAAK,eAAe,EAYvB,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EAAE,KAAK,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAE7D,OAAO,EAAE,KAAK,oBAAoB,EAAqC,MAAM,aAAa,CAAC;AAE3F,MAAM,WAAW,iBAAiB;IAC9B,IAAI,EAAE,SAAS,CAAC;IAChB,mBAAmB,EAAE,MAAM,CAAC;IAC5B,UAAU,EAAE,oBAAoB,CAAC;IACjC,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED,MAAM,WAAW,0BAA2B,SAAQ,iBAAiB;IACjE,MAAM,EAAE,WAAW,CAAC;CACvB;AAED;;;;;;GAMG;AACH,wBAAgB,6CAA6C,CACzD,QAAQ,EAAE,eAAe,GAAG,SAAS,GACtC,iBAAiB,GAAG,SAAS,CAuC/B;AAaD,wBAAgB,+BAA+B,CAAC,OAAO,EAAE,aAAa,GAAG,SAAS,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAmB1G;AAED;;GAEG;AACH,wBAAgB,2CAA2C,CACvD,gBAAgB,EAAE,sBAAsB,GAAG,SAAS,GACrD,iBAAiB,GAAG,SAAS,CAwB/B;AAED,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,OAAO,EAAE,EAAE,MAAM,EAAE,iBAAiB,GAAG,MAAM,GAAG,SAAS,CAGxG;AAED;;;;;GAKG;AACH,wBAAgB,uBAAuB,CACnC,QAAQ,EAAE,eAAe,GAAG,SAAS,EACrC,QAAQ,EAAE,cAAc,GACzB,MAAM,GAAG,SAAS,CAOpB"}
|