Skip to content

Commit d078b14

Browse files
committed
feat: Make it possible to limit table column widths
...to try to make them fit the container.
1 parent 9b2745a commit d078b14

File tree

11 files changed

+163
-40
lines changed

11 files changed

+163
-40
lines changed

app/charts/table/cell-desktop.tsx

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -61,12 +61,13 @@ export const CellDesktop = ({
6161
barShowBackground,
6262
} = columnMeta;
6363
const classes = useStyles();
64-
const { links } = useChartState() as TableChartState;
64+
const { links, shouldApplyWidthLimits } = useChartState() as TableChartState;
6565

6666
switch (columnMeta.type) {
6767
case "text":
6868
return (
6969
<Flex
70+
{...cell.getCellProps()}
7071
sx={{
7172
alignItems: "center",
7273
justifyContent:
@@ -78,19 +79,29 @@ export const CellDesktop = ({
7879
fontWeight: textStyle,
7980
px: 3,
8081
}}
81-
{...cell.getCellProps()}
8282
>
8383
<LinkedCellWrapper cell={cell} columnMeta={columnMeta} links={links}>
84-
{columnMeta.formatter(cell)}
84+
<Box
85+
component="span"
86+
sx={{
87+
...(shouldApplyWidthLimits && {
88+
overflow: "hidden",
89+
textOverflow: "ellipsis",
90+
whiteSpace: "nowrap",
91+
}),
92+
}}
93+
>
94+
{columnMeta.formatter(cell)}
95+
</Box>
8596
</LinkedCellWrapper>
8697
</Flex>
8798
);
8899
case "category":
89100
const { colorScale: cColorScale } = columnMeta;
90101
return (
91102
<Flex
92-
sx={{ alignItems: "center", fontWeight: textStyle, pl: 1, pr: 3 }}
93103
{...cell.getCellProps()}
104+
sx={{ alignItems: "center", fontWeight: textStyle, pl: 1, pr: 3 }}
94105
>
95106
<LinkedCellWrapper cell={cell} columnMeta={columnMeta} links={links}>
96107
<Tag tagColor={cColorScale(cell.value)}>

app/charts/table/constants.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
export const TABLE_HEIGHT = 600;
22
export const BAR_CELL_PADDING = 12;
33
export const SORTING_ARROW_WIDTH = 24;
4+
export const LIMITED_COLUMN_WIDTH = 120;

app/charts/table/table-content.tsx

Lines changed: 37 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { HeaderGroup } from "react-table";
66

77
import { SORTING_ARROW_WIDTH } from "@/charts/table/constants";
88
import { ColumnMeta } from "@/charts/table/table-state";
9+
import { columnCanBeWidthLimited } from "@/charts/table/width-limit";
910
import { Flex } from "@/components/flex";
1011
import { OpenMetadataPanelWrapper } from "@/components/metadata-panel";
1112
import { Observation } from "@/domain/data";
@@ -16,6 +17,7 @@ type TableContentProps = {
1617
tableColumnsMeta: Record<string, ColumnMeta>;
1718
customSortCount: number;
1819
totalColumnsWidth: number;
20+
shouldApplyWidthLimits: boolean;
1921
};
2022

2123
const TableContentContext = createContext<TableContentProps | undefined>(
@@ -27,6 +29,7 @@ export const TableContentProvider = ({
2729
tableColumnsMeta,
2830
customSortCount,
2931
totalColumnsWidth,
32+
shouldApplyWidthLimits,
3033
children,
3134
}: TableContentProps & { children: ReactNode }) => {
3235
const value = useMemo(() => {
@@ -35,8 +38,15 @@ export const TableContentProvider = ({
3538
tableColumnsMeta,
3639
customSortCount,
3740
totalColumnsWidth,
41+
shouldApplyWidthLimits,
3842
};
39-
}, [headerGroups, tableColumnsMeta, customSortCount, totalColumnsWidth]);
43+
}, [
44+
headerGroups,
45+
tableColumnsMeta,
46+
customSortCount,
47+
totalColumnsWidth,
48+
shouldApplyWidthLimits,
49+
]);
4050

4151
return (
4252
<TableContentContext.Provider value={value}>
@@ -75,8 +85,13 @@ export const TableContent = ({ children }: { children: ReactNode }) => {
7585
throw Error("Please wrap TableContent in TableContentProvider");
7686
}
7787

78-
const { headerGroups, tableColumnsMeta, customSortCount, totalColumnsWidth } =
79-
ctx;
88+
const {
89+
headerGroups,
90+
tableColumnsMeta,
91+
customSortCount,
92+
totalColumnsWidth,
93+
shouldApplyWidthLimits,
94+
} = ctx;
8095

8196
return (
8297
<>
@@ -87,10 +102,12 @@ export const TableContent = ({ children }: { children: ReactNode }) => {
87102
// eslint-disable-next-line react/jsx-key
88103
<Box {...headerGroup.getHeaderGroupProps()}>
89104
{headerGroup.headers.map((column) => {
90-
const { dim, columnComponentType } =
105+
const { type, dim, columnComponentType } =
91106
tableColumnsMeta[column.id];
92107
// We assume that the customSortCount items are at the beginning of the sorted array, so any item with a lower index must be a custom sorted one
93108
const isCustomSorted = column.sortedIndex < customSortCount;
109+
const hasWidthLimit =
110+
shouldApplyWidthLimits && columnCanBeWidthLimited(type);
94111

95112
return (
96113
// eslint-disable-next-line react/jsx-key
@@ -102,6 +119,7 @@ export const TableContent = ({ children }: { children: ReactNode }) => {
102119
: undefined
103120
)}
104121
{...column.getHeaderProps(column.getSortByToggleProps())}
122+
title={`${column.Header}`}
105123
>
106124
<TableSortLabel
107125
active={isCustomSorted}
@@ -110,12 +128,25 @@ export const TableContent = ({ children }: { children: ReactNode }) => {
110128
"& svg": {
111129
opacity: isCustomSorted ? 1 : 0.5,
112130
},
131+
...(hasWidthLimit && {
132+
minWidth: 0,
133+
}),
113134
}}
114135
>
115136
<OpenMetadataPanelWrapper component={dim}>
116-
<span style={{ fontWeight: "bold" }}>
137+
<Box
138+
component="span"
139+
sx={{
140+
fontWeight: "bold",
141+
...(hasWidthLimit && {
142+
overflow: "hidden",
143+
textOverflow: "ellipsis",
144+
whiteSpace: "nowrap",
145+
}),
146+
}}
147+
>
117148
{column.render("Header")}
118-
</span>
149+
</Box>
119150
</OpenMetadataPanelWrapper>
120151
</TableSortLabel>
121152
</Flex>

app/charts/table/table-state.tsx

Lines changed: 77 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,18 @@ import {
2626
CommonChartState,
2727
} from "@/charts/shared/chart-state";
2828
import { useSize } from "@/charts/shared/use-size";
29-
import { BAR_CELL_PADDING, TABLE_HEIGHT } from "@/charts/table/constants";
29+
import {
30+
BAR_CELL_PADDING,
31+
LIMITED_COLUMN_WIDTH,
32+
TABLE_HEIGHT,
33+
} from "@/charts/table/constants";
3034
import { getTableUIElementsOffset } from "@/charts/table/table";
3135
import {
3236
TableStateVariables,
3337
useTableStateData,
3438
useTableStateVariables,
3539
} from "@/charts/table/table-state-props";
40+
import { columnCanBeWidthLimited } from "@/charts/table/width-limit";
3641
import {
3742
ColumnStyleCategory,
3843
ColumnStyleHeatmap,
@@ -118,6 +123,7 @@ export type TableChartState = CommonChartState &
118123
groupingIds: string[];
119124
hiddenIds: string[];
120125
sortingIds: { id: string; desc: boolean }[];
126+
shouldApplyWidthLimits: boolean;
121127
links: TableLinks;
122128
};
123129

@@ -210,11 +216,10 @@ const useTableState = (
210216
[chartData, types]
211217
);
212218

213-
// Columns used by react-table
214-
const tableColumns = useMemo(() => {
219+
const tableColumnsData = useMemo(() => {
215220
const allComponents = [...dimensions, ...measures];
216221

217-
const columns = orderedTableColumns.map((c) => {
222+
const columnData = orderedTableColumns.map((c) => {
218223
const headerComponent = allComponents.find((d) => d.id === c.componentId);
219224

220225
if (!headerComponent) {
@@ -228,7 +233,7 @@ const useTableState = (
228233
const headerLabel = getLabelWithUnit(headerComponent);
229234

230235
// The column width depends on the estimated width of the
231-
// longest value in the column, with a minimum of 150px.
236+
// longest value in the column, with a minimum of 50px.
232237
const columnItems = [...new Set(chartData.map((d) => d[c.componentId]))];
233238
const columnItemSizes = [
234239
...columnItems.map((item) => {
@@ -241,40 +246,81 @@ const useTableState = (
241246
}),
242247
];
243248

244-
const width = Math.max(
249+
const naturalWidth = Math.max(
245250
50,
246251
getTextWidth(headerLabel, { fontSize: 16 }) + 44,
247252
...columnItemSizes
248253
);
249254

250255
return {
251-
Header: headerLabel,
252-
// Slugify accessor to avoid id's "." to be parsed as JS object notation.
253-
accessor: getSlugifiedId(c.componentId),
254-
width,
255-
sortType: (
256-
rowA: Row<Observation>,
257-
rowB: Row<Observation>,
258-
colId: string
259-
) => {
260-
for (const d of sorters) {
261-
const result = ascending(
262-
d(rowA.values[colId]),
263-
d(rowB.values[colId])
264-
);
265-
266-
if (result) {
267-
return result;
268-
}
269-
}
270-
271-
return 0;
272-
},
256+
c,
257+
headerComponent,
258+
headerLabel,
259+
sorters,
260+
naturalWidth,
261+
canBeWidthLimited: columnCanBeWidthLimited(
262+
fields[c.componentId].columnStyle.type
263+
),
273264
};
274265
});
275266

276-
return columns;
277-
}, [dimensions, measures, orderedTableColumns, chartData, formatNumber]);
267+
const totalNaturalWidth = columnData.reduce(
268+
(sum, col) => sum + col.naturalWidth,
269+
0
270+
);
271+
272+
const shouldApplyLimits =
273+
settings.limitColumnWidths && totalNaturalWidth > chartWidth;
274+
275+
const columns = columnData.map(
276+
({ c, headerLabel, sorters, naturalWidth, canBeWidthLimited }) => {
277+
const width =
278+
shouldApplyLimits && canBeWidthLimited
279+
? Math.min(naturalWidth, LIMITED_COLUMN_WIDTH)
280+
: naturalWidth;
281+
282+
return {
283+
Header: headerLabel,
284+
// Slugify accessor to avoid id's "." to be parsed as JS object notation.
285+
accessor: getSlugifiedId(c.componentId),
286+
width,
287+
sortType: (
288+
rowA: Row<Observation>,
289+
rowB: Row<Observation>,
290+
colId: string
291+
) => {
292+
for (const d of sorters) {
293+
const result = ascending(
294+
d(rowA.values[colId]),
295+
d(rowB.values[colId])
296+
);
297+
298+
if (result) {
299+
return result;
300+
}
301+
}
302+
303+
return 0;
304+
},
305+
};
306+
}
307+
);
308+
309+
return { columns, shouldApplyLimits };
310+
}, [
311+
dimensions,
312+
measures,
313+
orderedTableColumns,
314+
chartData,
315+
formatNumber,
316+
fields,
317+
chartWidth,
318+
settings.limitColumnWidths,
319+
]);
320+
321+
// Columns used by react-table
322+
const tableColumns = tableColumnsData.columns;
323+
const shouldApplyWidthLimits = tableColumnsData.shouldApplyLimits;
278324

279325
// Groupings used by react-table
280326
const groupingIds = useMemo(
@@ -436,6 +482,7 @@ const useTableState = (
436482
groupingIds,
437483
hiddenIds,
438484
sortingIds,
485+
shouldApplyWidthLimits,
439486
xScaleTimeRange,
440487
links,
441488
...variables,

app/charts/table/table.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ export const Table = () => {
100100
groupingIds,
101101
hiddenIds,
102102
sortingIds,
103+
shouldApplyWidthLimits,
103104
} = useChartState() as TableChartState;
104105
const classes = useStyles();
105106

@@ -370,6 +371,7 @@ export const Table = () => {
370371
tableColumnsMeta={tableColumnsMeta}
371372
customSortCount={customSortCount}
372373
totalColumnsWidth={totalColumnsWidth}
374+
shouldApplyWidthLimits={shouldApplyWidthLimits}
373375
>
374376
<AutoSizer disableWidth>
375377
{({ height }: { height: number }) => (

app/charts/table/width-limit.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import { ColumnMeta } from "@/charts/table/table-state";
2+
3+
export const columnCanBeWidthLimited = (columnType: ColumnMeta["type"]) => {
4+
return columnType === "text" || columnType === "category";
5+
};

app/configurator/table/table-chart-configurator.tsx

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,16 @@ export const ChartConfiguratorTable = ({
110110
mainLabel={<Trans id="controls.table.sorting">Sorting</Trans>}
111111
/>
112112
</ControlSectionContent>
113+
<ControlSectionContent>
114+
<ChartOptionCheckboxField
115+
label={t({
116+
id: "controls.tableSettings.limitColumnWidths",
117+
message: "Limit column widths",
118+
})}
119+
field={null}
120+
path="settings.limitColumnWidths"
121+
/>
122+
</ControlSectionContent>
113123
</ControlSection>
114124
<ControlSection collapse>
115125
<SectionTitle id="controls-data">

app/locales/de/messages.po

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1124,6 +1124,10 @@ msgstr "Messung der linken Achse"
11241124
msgid "wmts.legend-title"
11251125
msgstr "Legende"
11261126

1127+
#: app/configurator/table/table-chart-configurator.tsx
1128+
msgid "controls.tableSettings.limitColumnWidths"
1129+
msgstr "Begrenzen die Spaltenbreite"
1130+
11271131
#: app/components/footer.tsx
11281132
msgid "footer.button.lindas"
11291133
msgstr "LINDAS Linked Data Dienste"

app/locales/en/messages.po

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1124,6 +1124,10 @@ msgstr "Left axis measure"
11241124
msgid "wmts.legend-title"
11251125
msgstr "Legend"
11261126

1127+
#: app/configurator/table/table-chart-configurator.tsx
1128+
msgid "controls.tableSettings.limitColumnWidths"
1129+
msgstr "Limit column widths"
1130+
11271131
#: app/components/footer.tsx
11281132
msgid "footer.button.lindas"
11291133
msgstr "LINDAS Linked Data Services"

app/locales/fr/messages.po

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1124,6 +1124,10 @@ msgstr "Mesure de l'axe gauche"
11241124
msgid "wmts.legend-title"
11251125
msgstr "Légende"
11261126

1127+
#: app/configurator/table/table-chart-configurator.tsx
1128+
msgid "controls.tableSettings.limitColumnWidths"
1129+
msgstr "Limiter la largeur des colonnes"
1130+
11271131
#: app/components/footer.tsx
11281132
msgid "footer.button.lindas"
11291133
msgstr "Services de données liées LINDAS"

0 commit comments

Comments
 (0)