Skip to content

Commit

Permalink
fix: allow sorting of dynamically added columns2 (#2246)
Browse files Browse the repository at this point in the history
  • Loading branch information
felix-ico authored Jan 10, 2024
1 parent bab9369 commit c33b083
Show file tree
Hide file tree
Showing 2 changed files with 293 additions and 9 deletions.
50 changes: 41 additions & 9 deletions packages/components/src/components/table/table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/

import { Component, Prop, h, Element, Host } from '@stencil/core';
import { Component, Prop, h, Element, Host, State } from '@stencil/core';
import classNames from 'classnames';
import statusNote from '../../utils/status-note';

Expand All @@ -19,6 +19,8 @@ import statusNote from '../../utils/status-note';
shadow: false,
})
export class Table {
mutationObserver: MutationObserver;

@Element() hostElement: HTMLElement;
/** (optional) Display sort arrows on/off */
@Prop() showSort?: boolean = false;
Expand All @@ -28,19 +30,36 @@ export class Table {
@Prop() striped?: boolean = false;
/** (optional) Injected CSS styles */
@Prop() styles?: string;
/** "forceUpdate" hack, set it to trigger and re-render */
@State() forceUpdate: string;
/** object of the slots in use */
slots: { header?: Element; table?: Element } = {};

addSortIndicator(el) {
el.insertAdjacentHTML(
'afterbegin',
`
<span class="scale-sort-indicator" aria-hidden="true">
<scale-icon-content-sort-indicator-up class="scale-sort-indicator-icon up" size="16"></scale-icon-content-sort-indicator-up>
<scale-icon-content-sort-indicator-down class="scale-sort-indicator-icon down" size="16"></scale-icon-content-sort-indicator-down>
</span>`
);
}

componentWillLoad() {
this.hostElement.querySelectorAll('th').forEach((th) => {
th.insertAdjacentHTML(
'afterbegin',
`
<span class="scale-sort-indicator" aria-hidden="true">
<scale-icon-content-sort-indicator-up class="scale-sort-indicator-icon up" size="16"></scale-icon-content-sort-indicator-up>
<scale-icon-content-sort-indicator-down class="scale-sort-indicator-icon down" size="16"></scale-icon-content-sort-indicator-down>
</span>`
);
this.addSortIndicator(th);
});
}

componentWillUpdate() {
this.hostElement.querySelectorAll('th').forEach((th) => {
// only cols that are NOT added dynamically have children (the sorting icon), added on componentWillLoad
if (th.children.length === 0) {
// this may not be needed
th.classList.add('dynamically-added');
this.addSortIndicator(th);
}
});
}

Expand All @@ -52,6 +71,13 @@ export class Table {
el.showStatus = false;
});
}
this.mutationObserver = new MutationObserver(() => {
this.forceUpdate = String(Date.now());
});
this.mutationObserver.observe(this.hostElement, {
childList: true,
subtree: true,
});
}

componentDidRender() {
Expand All @@ -66,6 +92,12 @@ export class Table {
}
}

disconnectedCallback() {
if (this.mutationObserver) {
this.mutationObserver.disconnect();
}
}

render() {
return (
<Host class={this.getCssClassMap()}>
Expand Down
252 changes: 252 additions & 0 deletions packages/components/src/html/table-dynamic-cols.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,252 @@
<!DOCTYPE html>
<html dir="ltr" lang="en">
<head>
<meta charset="utf-8" />
<meta
name="viewport"
content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=5.0"
/>
<title>Stencil Component Starter</title>

<script type="module" src="/build/scale-components.esm.js"></script>
<link rel="stylesheet" href="/build/scale-components.css" />
</head>

<body>
<h3>Table</h3>

<script>
const getNextSort = (sort) => {
// if (!sort || ['none', 'other'].includes(sort)) {
// return 'descending';
// }
if (sort === 'descending') {
return 'ascending';
} else {
return 'descending';
}
};

function sortTable(th) {
const currentSort = th.getAttribute('aria-sort');
const nextSort = getNextSort(currentSort);
const tableHeaders = Array.from(document.getElementsByTagName('TH'));
const columnIndex = tableHeaders.findIndex((x) => x === th);

// clean up previous aria-sort value
tableHeaders.forEach((tableHeader) => {
tableHeader.setAttribute('aria-sort', 'none');
});

// set actual sort
th.setAttribute('aria-sort', nextSort);

// Taken from:
// https://www.w3schools.com/howto/tryit.asp?filename=tryhow_js_sort_table
var table, rows, switching, i, x, y, shouldSwitch;
table = document.getElementById('sortable-table');
switching = true;
/*Make a loop that will continue until
no switching has been done:*/
while (switching) {
//start by saying: no switching is done:
switching = false;
rows = table.rows;
/*Loop through all table rows (except the
first, which contains table headers):*/
for (i = 1; i < rows.length - 1; i++) {
//start by saying there should be no switching:
shouldSwitch = false;

/*Get the two elements you want to compare,
one from current row and one from the next:*/
x = rows[i].getElementsByTagName('TD')[columnIndex];
y = rows[i + 1].getElementsByTagName('TD')[columnIndex];

if (
!['ascending', 'descending'].includes(nextSort) ||
x.parentElement.parentElement.tagName === 'TFOOT' ||
y.parentElement.parentElement.tagName === 'TFOOT'
) {
break;
}

//check if the two rows should switch place:
if (
(nextSort === 'descending' ? y : x).innerHTML.toLowerCase() >
(nextSort === 'descending' ? x : y).innerHTML.toLowerCase()
) {
//if so, mark as a switch and break the loop:
shouldSwitch = true;
break;
}
}
if (shouldSwitch) {
/*If a switch has been marked, make the switch
and mark that a switch has been done:*/
rows[i].parentNode.insertBefore(rows[i + 1], rows[i]);
switching = true;
}
}
}
function handleKeyDown(e, th) {
if (['Space', 'Enter'].includes(e.code)) {
sortTable(th);
}
}
</script>

<scale-table size="default" show-sort="true">
<table id="sortable-table">
<thead>
<tr id="foo">
<th
aria-sort="descending"
onclick="sortTable(this)"
onkeydown="handleKeyDown(event, this)"
tabindex="0"
width="13%"
>
Title
</th>
<th aria-disabled="true" width="18%">Tags</th>
<th
onclick="sortTable(this)"
onkeydown="handleKeyDown(event, this)"
tabindex="0"
width="23%"
>
Stats
</th>
<th
onclick="sortTable(this)"
onkeydown="handleKeyDown(event, this)"
style="text-align: right"
tabindex="0"
width="18%"
>
Time
</th>
<th
onclick="sortTable(this)"
onkeydown="handleKeyDown(event, this)"
style="text-align: right"
tabindex="0"
width="18%"
>
Euros
</th>
</tr>
</thead>
<tbody>
<tr>
<td>Jane</td>
<td>
<scale-tag size="small" style="margin-right: 8px"
>Other</scale-tag
>
<scale-tag size="small" style="margin-right: 8px">N/A</scale-tag>
<scale-tag size="small">Demo</scale-tag>
</td>
<td>
<div style="display: inline-flex; align-items: center">
<span style="margin-right: 8px">9.356</span>
<scale-progress-bar
variant="info"
style="max-width: 120px"
stroke-width="6"
percentage="90"
/>
</div>
</td>
<td style="text-align: right; font-weight: 100">00:00:20</td>
<td style="text-align: right; font-weight: 100">100.245,10</td>
</tr>
<tr>
<td>Jack</td>
<td>
<scale-tag size="small" style="margin-right: 8px"
>Other</scale-tag
>
<scale-tag size="small" style="margin-right: 8px">N/A</scale-tag>
<scale-tag size="small">Demo</scale-tag>
</td>
<td>
<div style="display: inline-flex; align-items: center">
<span style="margin-right: 8px">3.356</span>
<scale-progress-bar
variant="info"
style="max-width: 120px"
stroke-width="6"
percentage="90"
/>
</div>
</td>
<td style="text-align: right; font-weight: 100">00:00:30</td>
<td style="text-align: right; font-weight: 100">100.345,10</td>
</tr>
<tr>
<td>John</td>
<td>
<scale-tag size="small" style="margin-right: 8px"
>Other</scale-tag
>
<scale-tag size="small" style="margin-right: 8px">N/A</scale-tag>
<scale-tag size="small">Demo</scale-tag>
</td>
<td>
<div style="display: inline-flex; align-items: center">
<span style="margin-right: 8px">6.356</span>
<scale-progress-bar
variant="info"
style="max-width: 120px"
stroke-width="6"
percentage="90"
/>
</div>
</td>
<td style="text-align: right; font-weight: 100">00:00:40</td>
<td style="text-align: right; font-weight: 100">100.445,10</td>
</tr>
</tbody>
<tfoot>
<tr>
<td>Total</td>
<td />
<td />
<td style="text-align: right">00:00:20</td>
<td style="text-align: right">100.245,10</td>
<td style="text-align: right">lol</td>
</tr>
</tfoot>
</table>
</scale-table>
</body>
<script>
const foo = document.getElementById('foo');
const newCol = document.createElement('th');
newCol.innerHTML = 'New Column';

const tbody = document.querySelector('tbody');
let newData = 0;
const children = tbody.children;
console.log('CHILDREN', children);

setTimeout(() => {
foo.appendChild(newCol);
console.log('foo', foo, newCol);
for (const child of children) {
console.log(child.tagName);
const newTd = document.createElement('td');
// newTd.style = "text-align: right"
console.log('newTD', newTd);
newData = newData + 1;
newTd.innerHTML = newData;
child.appendChild(newTd);
newCol.setAttribute('style', 'text-align: right');
newCol.setAttribute('width', '10%');
newCol.setAttribute('onClick', 'sortTable(this)');
}
}, 2000);
</script>
</html>

0 comments on commit c33b083

Please sign in to comment.