Aguarde...

24 de agosto de 2024

Outro componente da Web – Compressor de tabela

Outro componente da Web – Compressor de tabela

No começo desta semana, eu estava navegando em um site que mostrava uma lista tabular de dados. Inicialmente, ele mostrava algo como dez linhas e tinha um item clicável que mostrava o restante dos dados. Pensei em criar um componente web rápido que imitasse essa funcionalidade.

Meu pensamento era que você envolveria uma tabela HTML regular (muito parecido com meu componente de classificação de tabelas) e o componente truncaria e adicionaria a lógica ‘clique para expandir’. Agora, para ser claro, isso ainda significa que o usuário está baixando o conjunto inteiro de dados, mas visualmente ocuparia menos espaço até que o usuário selecionasse mostrar o restante dos dados.

Deixe-me compartilhar o componente aqui e depois explicarei como ele funciona:

class CompressTable extends HTMLElement {

	constructor() {
		super();
		this.rows = this.hasAttribute('rows') ? parseInt(this.getAttribute('rows'),10) : 50;
	}
	
	connectedCallback() {
		let table = this.querySelector('table');
		if(!table) {
			console.warn('<compress-table> - No table found.');
			return;
		}
		
		// ok, so how big is our table?
		let rows = table.querySelectorAll('tbody tr');
		
		// can we leave if the table is small?
		if(rows.length <= this.rows) return;
		
		// ok, construct a click to show doohicky
		this.showRow = document.createElement('tr');
		let showTd = document.createElement('td');
		showTd.setAttribute('colspan',1000);
		showTd.style.textAlign = 'center';
		showTd.style.cursor = 'pointer';
		showTd.innerText = 'Click to Expand';
		this.showRow.appendChild(showTd);
		this.showRow.addEventListener('click', () => this.returnRows());
		
		// now, store rows this.rows +1 to rowCount
		let selector = `tbody tr:nth-child(n+${this.rows+1}):nth-child(-n+${rows.length})`;
		this.rowsToHide = table.querySelectorAll(selector);
		console.log('rowsToHide', this.rowsToHide.length);
		this.rowsToHide.forEach(r => {
			r.style.display = 'none';
		});
		
		table.querySelector('tbody').appendChild(this.showRow);

	}
	
	returnRows() {
		this.rowsToHide.forEach(r => {
			r.style.display = '';
		});
		this.showRow.style.display = 'none';		
	}
	
}

if(!customElements.get('compress-table')) customElements.define('compress-table', CompressTable);

Do topo, começo procurando por um rowsatributo. Se não for especificado, o padrão é 50. O trabalho real começa em connectedCallback.

Primeiro, procuro uma mesa e, se não encontrar nenhuma, simplesmente vou embora.

let table = this.querySelector('table');
if(!table) {
	console.warn('<compress-table> - No table found.');
	return;
}

Então vejo quantas linhas temos e, se for menor que o ponto de corte desejado, simplesmente deixo:

let rows = table.querySelectorAll('tbody tr');

// can we leave if the table is small?
if(rows.length <= this.rows) return;

Em seguida, crio minha parte ‘clique para expandir’. Eu poderia fazer o texto aqui algo que você passa por meio de um atributo, mas deixei codificado por enquanto. Observe também o ‘hack’ do colspan ali. Descobri que especificar um número maior do que as colunas na tabela parece não ter efeitos colaterais. Além disso, de acordo com o MDN, o valor máximo para colspan é 1000. Eu não tinha ideia.

// ok, construct a click to show doohicky
this.showRow = document.createElement('tr');
let showTd = document.createElement('td');
showTd.setAttribute('colspan',1000);
showTd.style.textAlign = 'center';
showTd.style.cursor = 'pointer';
showTd.innerText = 'Click to Expand';
this.showRow.appendChild(showTd);
this.showRow.addEventListener('click', () => this.returnRows());

Agora a parte divertida. Eu precisava esconder as linhas sobre meu ponto de corte desejado. Acontece que você pode fazer isso em CSS porque CSS, pelo menos na última década, é incrivelmente incrível. Aqui está como eu fiz:

// now, store rows this.rows +1 to rowCount
let selector = `tbody tr:nth-child(n+${this.rows+1}):nth-child(-n+${rows.length})`;
this.rowsToHide = table.querySelectorAll(selector);
this.rowsToHide.forEach(r => {
	r.style.display = 'none';
});

A parte final deste método acrescenta minha click to expandparte:

table.querySelector('tbody').appendChild(this.showRow);

A última parte é o manipulador de clique para revelar as linhas ocultas da tabela e ocultar o clicador (eu poderia removê-lo do DOM, suponho):

returnRows() {
	this.rowsToHide.forEach(r => {
		r.style.display = '';
	});
	this.showRow.style.display = 'none';		
}

Na verdade, usá-lo é simples: basta embrulhar sua mesa!

<compress-table rows=20>
	<table>
		<thead>
			<tr>
				<th>Name</th><th>Something</th><th>Age</th>
			</tr>
		</thead>
		<tbody>
<tr><td>Cat 0</td><td>Foo</td><td>0 years old.</td></tr>
<tr><td>Cat 1</td><td>Foo</td><td>2 years old.</td></tr>
<tr><td>Cat 2</td><td>Foo</td><td>4 years old.</td></tr>
<!-- lots of rows here, like, lots and lots -->
<tr><td>Cat 98</td><td>Foo</td><td>196 years old.</td></tr>
<tr><td>Cat 99</td><td>Foo</td><td>198 years old.</td></tr>		
		</tbody>
	</table>
</compress-table>

Você pode brincar com ele abaixo, e se as pessoas acharem que vale a pena, vou adicioná-lo ao NPM também.

Postado em BlogTags:
Escreva um comentário