As planilhas se tornaram uma parte integral da computação moderna. Elas permitem que os usuários organizem, manipulem e analisem dados em um formato tabular. Aplicativos como o Google Sheets estabeleceram o padrão para planilhas interativas e poderosas.
Neste post do blog, vamos guiá-lo pelo processo de construção de um aplicativo de planilha usando JavaScript. Vamos nos concentrar em conceitos-chave de programação, explorar recursos do JavaScript e incluir trechos de código detalhados com explicações.
O código-fonte completo está disponível aqui no meu Codepen.
O que é o Google Spreadsheet?
O Google Spreadsheet é um aplicativo baseado na web que permite aos usuários criar, editar e colaborar em planilhas online. Ele oferece recursos como fórmulas, validação de dados, gráficos e formatação condicional.
Nosso projeto emula alguns recursos principais do Google Spreadsheet, focando em:
- Células editáveis.
- Análise e avaliação de fórmulas.
- Atualizações em tempo real através de um modelo Pub/Sub.
- Navegação por teclado e seleção de células.
- Avaliação dinâmica de dependências entre células.
Recursos deste projeto
- Células editáveis: Permite que os usuários insiram texto ou equações nas células.
- Suporte a fórmulas: Processa fórmulas começando com
=
e avalia expressões. - Atualizações ao vivo: Mudanças em células dependentes acionam atualizações usando um modelo Pub/Sub.
- Navegação por teclado: Permite movimento entre células usando as teclas de seta.
- Avaliação dinâmica: Garante atualizações em tempo real para fórmulas dependentes de outras células.
- Tratamento de erros: Fornece mensagens de erro significativas para entradas inválidas ou dependências circulares.
- Design escalável: Permite fácil extensão para adicionar mais linhas, colunas ou recursos.
Componentes-chave da Aplicação
1. Gerenciamento de Modos
const Mode = {
EDIT: 'edit',
DEFAULT: 'default'
};
Este enum define dois modos:
- EDITAR: Permite a edição de uma célula selecionada.
- DEFAULT: Permite navegação e interação sem edição.
Por que usar modos?
Modos simplificam o gerenciamento do estado da interface do usuário. Por exemplo, no modo DEFAULT, entradas do teclado movem entre células, enquanto no modo EDITAR, entradas modificam o conteúdo da célula.
2. Classe Pub/Sub
O modelo Pub/Sub gerencia assinaturas e atualizações ao vivo. Células podem se inscrever em outras células e atualizar dinamicamente quando as dependências mudam.
class PubSub {
constructor() {
this.map = {};
}
get(source) {
let result = [];
let queue = [ (this.map[source] || [])];
while (queue.length) {
let next = queue.shift();
result.push(next.toUpperCase());
if (this.map[next]) queue.unshift(this.map[next]);
}
return result;
}
subscribeAll(sources, destination) {
sources.forEach((source) => {
this.map[source] = this.map[source] || [];
this.map[source].push(destination);
});
}
}
Recursos principais:
- Gerenciamento dinâmico de dependências: Rastreia dependências entre células.
- Propagação de atualizações: Atualiza células dependentes quando células de origem mudam.
- Busca em largura: Evita loops infinitos rastreando todos os nós dependentes.
Exemplo de uso:
let ps = new PubSub();
ps.subscribeAll(['A1'], 'B1');
ps.subscribeAll(['B1'], 'C1');
console.log(ps.get('A1')); // Output: ['B1', 'C1']
3. Criando Linhas e Células
class Cell {
constructor(cell, row, col) {
cell.id = `${String.fromCharCode(col + 65)}${row}`;
cell.setAttribute('data-eq', '');
cell.setAttribute('data-value', '');
if (row > 0 && col > -1) cell.classList.add('editable');
cell.textContent = col === -1 ? row : '';
}
}
class Row {
constructor(row, r) {
for (let c = -1; c < 13; c++) {
new Cell(row.insertCell(), r, c);
}
}
}
Principais características:
- Geração dinâmica de tabela: Permite adicionar linhas e colunas programaticamente.
- Identificação de células: Gera IDs com base na posição (por exemplo, A1, B2).
- Células editáveis: As células são editáveis apenas se forem válidas (linhas/colunas que não são de cabeçalho).
Por que Usar Linhas e Células Dinâmicas?
Essa abordagem permite que o tamanho da tabela seja escalável e flexível, suportando recursos como adição de linhas ou colunas sem alterar a estrutura.
4. Manipulação de Eventos para Interação
addEventListeners() {
this.table.addEventListener('click', this.onCellClick.bind(this));
this.table.addEventListener('dblclick', this.onCellDoubleClick.bind(this));
window.addEventListener('keydown', this.onKeyDown.bind(this));
}
Principais características:
- Evento de clique: Seleciona ou edita células.
- Evento de duplo clique: Permite a edição de fórmulas.
- Evento de tecla pressionada: Suporta navegação com teclas de seta.
5. Análise e Avaliação de Fórmulas
function calcCell(expression) {
if (!expression) return 0;
return expression.split('+').reduce((sum, term) => {
let value = isNaN(term) ? getCellValue(term) : Number(term);
if (value === null) throw new Error(`Invalid cell: ${term}`);
return sum + Number(value);
}, 0);
}
Principais características:
- Calculação dinâmica: Calcula fórmulas referenciando outras células.
- Avaliação recursiva: Resolve dependências aninhadas.
- Tratamento de erros: Identifica referências inválidas e dependências circulares.
6. Tratamento de Erros para Entrada do Usuário
function isValidCell(str) {
let regex = /^[A-Z]{1}[0-9]+$/;
return regex.test(str);
}
Principais características:
- Validação: Garante que a entrada faça referência a IDs de células válidos.
- Escalabilidade: Suporta expansão dinâmica da tabela sem quebrar a validação.
Tópicos de JavaScript Abordados
1. Manipulação de Eventos
Gerencia interações como cliques e pressionamentos de tecla.
window.addEventListener('keydown', this.onKeyDown.bind(this));
2. Manipulação do DOM
Cria e modifica elementos do DOM dinamicamente.
let cell = document.createElement('td'); cell.appendChild(document.createTextNode('A1'));
3. Recursão
Processa dependências dinamicamente.
function calcCell(str) { if (isNaN(str)) { return calcCell(getCellValue(str)); } }
4. Tratamento de Erros
Detecta células inválidas e dependências circulares.
if (!isValidCell(p)) throw new Error(`invalid cell ${p}`);
Conclusão
Este projeto demonstra uma planilha poderosa usando JavaScript. Ele aproveita o tratamento de eventos, recursão e padrões de Pub/Sub, estabelecendo a base para aplicações web complexas. Expanda-o adicionando recursos como exportação de dados, gráficos ou regras de formatação.
Referências
Source:
https://dzone.com/articles/spreadsheet-application-javascript-guide