Add sortable large table headers

This commit is contained in:
2026-06-04 18:04:07 +02:00
parent 58aabfc873
commit 9f6745d044
2 changed files with 154 additions and 5 deletions
+123 -5
View File
@@ -83,11 +83,36 @@
</div> </div>
<div class="sg-card-segment sg-card-segment--body sg-card-segment--white sg-large-table__row sg-large-table__row--header" role="row" data-component-part="large-table-header-row"> <div class="sg-card-segment sg-card-segment--body sg-card-segment--white sg-large-table__row sg-large-table__row--header" role="row" data-component-part="large-table-header-row">
<div class="sg-large-table__cell" role="columnheader">Spalte 1</div> <div class="sg-large-table__cell sg-large-table__cell--header" role="columnheader" aria-sort="none" data-sort-key="0">
<div class="sg-large-table__cell" role="columnheader">Spalte 2</div> <button class="sg-large-table__sort-button" type="button" aria-label="Spalte 1 sortieren">
<div class="sg-large-table__cell" role="columnheader">Spalte 3</div> <span class="sg-large-table__sort-label">Spalte 1</span>
<div class="sg-large-table__cell" role="columnheader">Spalte 4</div> <span class="sg-large-table__sort-icon" aria-hidden="true"></span>
<div class="sg-large-table__cell" role="columnheader">Spalte 5</div> </button>
</div>
<div class="sg-large-table__cell sg-large-table__cell--header" role="columnheader" aria-sort="none" data-sort-key="1">
<button class="sg-large-table__sort-button" type="button" aria-label="Spalte 2 sortieren">
<span class="sg-large-table__sort-label">Spalte 2</span>
<span class="sg-large-table__sort-icon" aria-hidden="true"></span>
</button>
</div>
<div class="sg-large-table__cell sg-large-table__cell--header" role="columnheader" aria-sort="none" data-sort-key="2">
<button class="sg-large-table__sort-button" type="button" aria-label="Spalte 3 sortieren">
<span class="sg-large-table__sort-label">Spalte 3</span>
<span class="sg-large-table__sort-icon" aria-hidden="true"></span>
</button>
</div>
<div class="sg-large-table__cell sg-large-table__cell--header" role="columnheader" aria-sort="none" data-sort-key="3">
<button class="sg-large-table__sort-button" type="button" aria-label="Spalte 4 sortieren">
<span class="sg-large-table__sort-label">Spalte 4</span>
<span class="sg-large-table__sort-icon" aria-hidden="true"></span>
</button>
</div>
<div class="sg-large-table__cell sg-large-table__cell--header" role="columnheader" aria-sort="none" data-sort-key="4">
<button class="sg-large-table__sort-button" type="button" aria-label="Spalte 5 sortieren">
<span class="sg-large-table__sort-label">Spalte 5</span>
<span class="sg-large-table__sort-icon" aria-hidden="true"></span>
</button>
</div>
</div> </div>
<div class="sg-card-segment sg-card-segment--body sg-card-segment--white sg-large-table__row" role="row" data-component-part="large-table-row"> <div class="sg-card-segment sg-card-segment--body sg-card-segment--white sg-large-table__row" role="row" data-component-part="large-table-row">
@@ -173,5 +198,98 @@
</section> </section>
<script>
(function () {
const table = document.querySelector('[data-component="large-table"]');
if (!table) {
return;
}
const headerCells = Array.from(table.querySelectorAll('[role="columnheader"][data-sort-key]'));
const headerButtons = headerCells.map((cell) => cell.querySelector('.sg-large-table__sort-button'));
const rows = Array.from(table.querySelectorAll('[data-component-part="large-table-row"]'));
const headerRow = table.querySelector('[data-component-part="large-table-header-row"]');
const state = {
columnIndex: 0,
direction: 'ascending',
};
function setHeaderState(activeIndex, direction) {
headerCells.forEach((cell, index) => {
const icon = cell.querySelector('.sg-large-table__sort-icon');
const isActive = index === activeIndex;
cell.setAttribute('aria-sort', isActive ? direction : 'none');
if (icon) {
icon.textContent = isActive ? (direction === 'ascending' ? '↑' : '↓') : '↕';
}
});
headerButtons.forEach((button, index) => {
if (!button) {
return;
}
const label = `Spalte ${index + 1}`;
button.setAttribute(
'aria-label',
index === activeIndex
? `${label} ${direction === 'ascending' ? 'aufsteigend' : 'absteigend'} sortiert`
: `${label} sortieren`
);
});
}
function compareValues(left, right, direction) {
const normalizedLeft = left.trim();
const normalizedRight = right.trim();
const comparison = normalizedLeft.localeCompare(normalizedRight, 'de', {
numeric: true,
sensitivity: 'base',
});
return direction === 'ascending' ? comparison : -comparison;
}
function sortRows(columnIndex, direction) {
const sortedRows = rows.slice().sort((leftRow, rightRow) => {
const leftCell = leftRow.querySelectorAll('[role="cell"]')[columnIndex];
const rightCell = rightRow.querySelectorAll('[role="cell"]')[columnIndex];
const leftValue = leftCell ? leftCell.textContent : '';
const rightValue = rightCell ? rightCell.textContent : '';
return compareValues(leftValue, rightValue, direction);
});
sortedRows.forEach((row) => {
table.appendChild(row);
});
setHeaderState(columnIndex, direction);
}
headerButtons.forEach((button, index) => {
if (!button) {
return;
}
button.addEventListener('click', () => {
const direction = state.columnIndex === index && state.direction === 'ascending'
? 'descending'
: 'ascending';
state.columnIndex = index;
state.direction = direction;
sortRows(index, direction);
});
});
if (headerRow) {
setHeaderState(state.columnIndex, state.direction);
}
sortRows(state.columnIndex, state.direction);
})();
</script>
</body> </body>
</html> </html>
+31
View File
@@ -96,3 +96,34 @@
text-overflow: ellipsis; text-overflow: ellipsis;
white-space: nowrap; white-space: nowrap;
} }
.sg-large-table__cell--header {
padding: 0;
}
.sg-large-table__sort-button {
display: flex;
align-items: center;
justify-content: space-between;
gap: var(--spacing-small);
width: 100%;
padding: 0 var(--spacing-small);
border: var(--border-none);
background: var(--color-transparent);
color: inherit;
font: inherit;
font-weight: var(--font-weight-semibold);
text-align: left;
cursor: pointer;
}
.sg-large-table__sort-button:focus-visible {
outline: 2px solid var(--color-darkblue);
outline-offset: -2px;
}
.sg-large-table__sort-icon {
flex: 0 0 auto;
font-size: 0.85em;
line-height: 1;
}