diff --git a/AGENTS.md b/AGENTS.md
index 0ec3242..ab8393e 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -173,6 +173,10 @@ tooltip; the toolbar shows icon plus text, group headers and rows show icons
only, separated into logical blocks with `IconButtonSeparator`. Destructive
actions use `variant="danger"` (trash icon for circuits and groups, `ListX` for
removing a device row).
+Column headers show the label (click to sort) with a funnel icon for the
+value filter; an active filter is highlighted and shows its value count.
+Header labels break only between words and put a trailing unit such as
+`[kW]` on a second line.
## Bootstrap / Styling Rule
diff --git a/src/app/globals.css b/src/app/globals.css
index ec92d91..fe81d07 100644
--- a/src/app/globals.css
+++ b/src/app/globals.css
@@ -892,24 +892,37 @@ a.kpi:hover {
white-space: nowrap;
}
+/* One header row: label (sort) on the left, filter icon on the right. */
.tree-grid .header-cell {
display: flex;
- flex-direction: column;
- align-items: stretch;
+ flex-direction: row;
+ align-items: flex-start;
+ justify-content: space-between;
gap: 0.2rem;
min-width: 0;
}
.tree-grid .header-filter-btn {
- display: block;
- width: 100%;
- border: 1px solid var(--panel-border-strong);
- background: var(--panel-bg);
- color: var(--text-strong);
+ display: inline-flex;
+ flex: none;
+ align-items: center;
+ gap: 0.15rem;
+ border: 1px solid transparent;
+ background: transparent;
+ color: var(--text-faint);
border-radius: 3px;
- font-size: 0.75rem;
- padding: 0.15rem 0.35rem;
+ font-size: 0.7rem;
+ font-weight: 700;
+ line-height: 1;
+ padding: 0.15rem 0.2rem;
white-space: nowrap;
+ cursor: pointer;
+}
+
+.tree-grid .header-filter-btn:hover,
+.tree-grid .header-filter-btn:focus-visible {
+ border-color: var(--panel-border-strong);
+ color: var(--text-strong);
}
.tree-grid .header-sort-btn {
@@ -927,7 +940,10 @@ a.kpi:hover {
font-size: 0.78rem;
font-weight: 600;
line-height: 1.15;
- overflow-wrap: anywhere;
+ /* Break only between words; never inside a word such as "Bemerkung". */
+ overflow-wrap: normal;
+ word-break: normal;
+ hyphens: manual;
white-space: normal;
text-align: left;
cursor: pointer;
@@ -967,6 +983,18 @@ a.kpi:hover {
font-size: 0.78rem;
}
+.tree-grid .header-cell .header-sort-btn {
+ flex: 1 1 auto;
+ width: auto;
+ align-items: flex-start;
+}
+
+.tree-grid .header-unit {
+ display: block;
+ font-weight: 500;
+ color: var(--text-faint);
+}
+
.tree-grid .header-sort-btn:hover span:first-child,
.tree-grid .header-sort-btn:focus-visible span:first-child {
text-decoration: underline;
@@ -983,6 +1011,11 @@ a.kpi:hover {
background: var(--accent-blue-soft-bg);
}
+.tree-grid .header-filter-count {
+ min-width: 0.7rem;
+ text-align: center;
+}
+
.tree-grid .header-filter-menu {
position: absolute;
z-index: 7;
diff --git a/src/frontend/components/circuit-tree-editor.tsx b/src/frontend/components/circuit-tree-editor.tsx
index cc3d522..e559303 100644
--- a/src/frontend/components/circuit-tree-editor.tsx
+++ b/src/frontend/components/circuit-tree-editor.tsx
@@ -9,6 +9,7 @@ import {
Columns3,
FilterX,
FolderPlus,
+ Funnel,
ListOrdered,
ListPlus,
ListX,
@@ -268,6 +269,24 @@ function createValuesFromEditPatch(
);
}
+/**
+ * Header labels break only between words: hyphens become non-breaking and a
+ * trailing unit such as "[kW]" is always shown on its own second line, so
+ * header heights stay uniform.
+ */
+function renderColumnHeaderLabel(label: string) {
+ const match = /^(.*?)s*([[^]]+])$/.exec(label);
+ const name = (match ? match[1] : label).replace(/-/g, "‑");
+ return match ? (
+ <>
+ {name}
+ {match[2]}
+ >
+ ) : (
+ name
+ );
+}
+
export function CircuitTreeEditor(props: { projectId: string; circuitListId: string }) {
/*
Circuit-tree editor invariant overview:
@@ -3440,7 +3459,9 @@ export function CircuitTreeEditor(props: { projectId: string; circuitListId: str
});
}}
>
- {column.label}
+
+ {renderColumnHeaderLabel(column.label)}
+
{sortState?.key === column.key ? (
{sortState.direction === "asc" ? "↑" : "↓"}
@@ -3451,6 +3472,7 @@ export function CircuitTreeEditor(props: { projectId: string; circuitListId: str
type="button"
className={`header-filter-btn ${(columnFilters[column.key]?.length ?? 0) > 0 ? "active" : ""}`}
onClick={() => toggleColumnFilterMenu(column.key)}
+ aria-label={`${getFullColumnLabel(column)} filtern`}
aria-pressed={(columnFilters[column.key]?.length ?? 0) > 0}
title={
(columnFilters[column.key]?.length ?? 0) > 0
@@ -3458,7 +3480,10 @@ export function CircuitTreeEditor(props: { projectId: string; circuitListId: str
: `${getFullColumnLabel(column)} filtern`
}
>
- Filtern{(columnFilters[column.key]?.length ?? 0) > 0 ? ` (${columnFilters[column.key]?.length})` : ""}
+
+ {(columnFilters[column.key]?.length ?? 0) > 0 ? (
+ {columnFilters[column.key]?.length}
+ ) : null}
{openFilterColumn === column.key ? (