;
+ const nextCell = index === undefined ? undefined : visibleCells[index + 1];
+ // Column dividers are only drawn between two attribute columns, so the bulk select
+ // and the (possibly empty) actions column don't produce stray lines.
+ const showDivider =
+ !!nextCell && !UTILITY_COLUMNS.has(cell.column.id) && !UTILITY_COLUMNS.has(nextCell.column.id);
return (
|
+ $hidePadding={columnMeta?.hideCellPadding}
+ $showDivider={showDivider}
+ $textAlign={columnMeta?.columnRenderer?.textAlign}
+ $wrapContent={columnMeta?.columnRenderer?.wrapContent}>
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
);
@@ -133,7 +170,7 @@ const Table = ({
/>
) : (
<>
- {visibleCells.map(renderCell)}
+ {visibleCells.map(renderCell)}
(
$isEvenRow ? theme.colors.table.row.background : theme.colors.table.row.backgroundStriped,
])};
height: 100%;
- align-items: flex-start;
+ align-items: center;
`,
);
diff --git a/graylog2-web-interface/src/components/common/EntityDataTable/hooks/useAttributeColumnDefinitions.tsx b/graylog2-web-interface/src/components/common/EntityDataTable/hooks/useAttributeColumnDefinitions.tsx
index f1906ba36b2b..3b2be35c8132 100644
--- a/graylog2-web-interface/src/components/common/EntityDataTable/hooks/useAttributeColumnDefinitions.tsx
+++ b/graylog2-web-interface/src/components/common/EntityDataTable/hooks/useAttributeColumnDefinitions.tsx
@@ -38,6 +38,26 @@ import ActiveSliceColContext from 'components/common/EntityDataTable/contexts/Ac
import SortIcon from '../SortIcon';
+// Column drag-to-reorder and resize controls are hidden until the header is hovered or focused,
+// reducing visual clutter. Uses opacity (not display) so the reserved width stays stable for
+// useHeaderSectionObserver and the label doesn't shift when the control appears. Devices
+// without hover (touch) keep the controls always visible, since they could not reveal them.
+const HandleSlot = styled.div<{ $forceVisible?: boolean }>(
+ ({ $forceVisible }) => css`
+ opacity: ${$forceVisible ? 1 : 0};
+ transition: opacity 150ms ease-in-out;
+
+ th:hover &,
+ th:focus-within & {
+ opacity: 1;
+ }
+
+ @media (hover: none) {
+ opacity: 1;
+ }
+ `,
+);
+
export const ThInner = styled.div`
display: flex;
justify-content: space-between;
@@ -119,13 +139,15 @@ const AttributeHeader = ({
{columnMeta?.enableColumnOrdering && (
-
+
+
+
)}
({
{ctx.header.column.getCanResize() && (
-
+
+
+
)}
diff --git a/graylog2-web-interface/src/components/common/EntityDataTable/types.ts b/graylog2-web-interface/src/components/common/EntityDataTable/types.ts
index e610aa8c01ab..e7536e57bad3 100644
--- a/graylog2-web-interface/src/components/common/EntityDataTable/types.ts
+++ b/graylog2-web-interface/src/components/common/EntityDataTable/types.ts
@@ -45,11 +45,14 @@ export type ColumnSchema = {
export type ColumnRenderer = {
renderCell?: (value: unknown, entity: Entity, meta: Meta, additionalInfo?: unknown) => React.ReactNode;
renderHeader?: (title: string) => React.ReactNode;
- textAlign?: string;
+ // Horizontal cell content alignment. Cells are centered by default; title and link cells should be left-aligned.
+ textAlign?: 'left' | 'center' | 'right';
minWidth?: number; // px
width?: number; // fraction of unassigned table width, similar to CSS unit fr.
// Uses the rendered title width as the fixed width; or provide a px value, if the title width is too small.
staticWidth?: number | 'matchHeader';
+ // Opt out of the default single-line truncation for cells whose content needs to wrap (e.g. multi-line renderers).
+ wrapContent?: boolean;
};
export type ColumnRenderersByAttribute = {
diff --git a/graylog2-web-interface/src/components/events/events/ColumnRenderers.tsx b/graylog2-web-interface/src/components/events/events/ColumnRenderers.tsx
index 21afc7188c49..01307651fc42 100644
--- a/graylog2-web-interface/src/components/events/events/ColumnRenderers.tsx
+++ b/graylog2-web-interface/src/components/events/events/ColumnRenderers.tsx
@@ -146,6 +146,7 @@ const CustomColumnRenderers: ColumnRenderers = {
...getGeneralEventAttributeRenderers(),
event_definition_id: {
width: 0.3,
+ textAlign: 'left',
renderCell: (eventDefinitionId: string, _, meta: EventsAdditionalData) => (
),
diff --git a/graylog2-web-interface/src/components/lookup-tables/lookup-table-list/column-renderers.tsx b/graylog2-web-interface/src/components/lookup-tables/lookup-table-list/column-renderers.tsx
index fa910cf304d3..4538c25ac685 100644
--- a/graylog2-web-interface/src/components/lookup-tables/lookup-table-list/column-renderers.tsx
+++ b/graylog2-web-interface/src/components/lookup-tables/lookup-table-list/column-renderers.tsx
@@ -119,10 +119,12 @@ const columnRenderers: ColumnRenderers ,
},
data_adapter_id: {
width: 0.1,
+ textAlign: 'left',
renderCell: (data_adapter_id: string, _c, meta) => (
),
diff --git a/graylog2-web-interface/src/components/streams/StreamsOverview/ColumnRenderers.tsx b/graylog2-web-interface/src/components/streams/StreamsOverview/ColumnRenderers.tsx
index 3222dd77e2ec..fd4e07b98050 100644
--- a/graylog2-web-interface/src/components/streams/StreamsOverview/ColumnRenderers.tsx
+++ b/graylog2-web-interface/src/components/streams/StreamsOverview/ColumnRenderers.tsx
@@ -53,10 +53,12 @@ const customColumnRenderers = (
title: {
renderCell: (_title: string, stream) => ,
width: 0.5,
+ wrapContent: true,
},
index_set_title: {
renderCell: (_index_set_title: string, stream) => ,
width: 0.3,
+ textAlign: 'left',
},
throughput: {
renderCell: (_throughput: string, stream) => ,
diff --git a/graylog2-web-interface/src/components/streams/StreamsOverview/cells/TitleCell.tsx b/graylog2-web-interface/src/components/streams/StreamsOverview/cells/TitleCell.tsx
index 983bd08684c1..22eaab925291 100644
--- a/graylog2-web-interface/src/components/streams/StreamsOverview/cells/TitleCell.tsx
+++ b/graylog2-web-interface/src/components/streams/StreamsOverview/cells/TitleCell.tsx
@@ -30,22 +30,41 @@ const DefaultLabel = styled(Label)`
vertical-align: inherit;
`;
+const Container = styled.div`
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ min-height: 2lh; /* reserve two lines so rows with and without description have the same height */
+`;
+
+const TitleRow = styled.div`
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+`;
+
const StyledText = styled(Text)(
({ theme }) => css`
color: ${theme.colors.text.secondary};
+ line-height: inherit; /* match the title line, so two lines fill the container exactly */
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
`,
);
const TitleCell = ({ stream }: Props) => (
- <>
- {stream.title}
- {stream.is_default && (
-
- Default
-
- )}
- {stream.description}
- >
+
+
+ {stream.title}
+ {stream.is_default && (
+
+ Default
+
+ )}
+
+ {stream.description && {stream.description}}
+
);
export default TitleCell;