Add sortable large table headers
This commit is contained in:
@@ -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>
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user