Implementando suporte a tabelas no Trix Editor

No artigo anterior, explorei algumas formas de personalizar o Trix Editor, desde pequenas alterações na barra de ferramentas até a criação de novos comportamentos. Agora, quero focar em um desafio específico — e bastante comum: adicionar suporte a tabelas.

Os criadores do Trix, a 37signals, sempre deixaram clara sua filosofia. O editor existe para atender às necessidades do Basecamp, e não há intenção de transformá-lo em um editor genérico e cheio de funcionalidades extras. Ainda assim, o projeto foi pensado de forma extensível, permitindo que desenvolvedores adaptem o editor às suas próprias necessidades.

O suporte a tabelas é justamente um desses recursos que não fazem parte dos planos oficiais do projeto. Em um comentário no repositório do Trix, um dos mantenedores deixa claro:

A reação da comunidade — visível pelos inúmeros thumbs down — mostra que essa decisão não agrada a todos.

Apesar de tabelas não serem essenciais em todos os contextos, para muitos projetos elas são um requisito básico. Na prática, essa limitação faz com que diversos desenvolvedores descartem o Trix logo de início. Esse quase foi o nosso caso. Chegamos a avaliar outros editores, mas a excelente integração do Trix com o ecossistema Rails nos fez insistir um pouco mais. E foi aí que descobrimos algo interessante: outros desenvolvedores já haviam enfrentado esse problema e encontrado soluções próprias.

A partir desses exemplos, começamos a explorar o problema por conta própria, estudando abordagens existentes, testando ideias e entendendo melhor os limites e as possibilidades do Trix. Esse processo inicial foi fundamental para ganhar familiaridade com a arquitetura do editor e identificar onde seria possível estender seu comportamento.

Em alguns pontos, porém, essa exploração esbarrou em detalhes mais sutis do
funcionamento interno do Trix. Foi nesse momento que uma ajuda externa se mostrou essencial. Esse trabalho só foi possível graças à ajuda de Benoit Tremblay, um desenvolvedor que eu não conheço pessoalmente, mas que gentilmente respondeu alguns emails e compartilhou insights decisivos para avançarmos.

Neste artigo, compartilho passo a passo como foi o processo de adicionar suporte a tabelas ao Trix Editor.

Criando o seletor de tabela

Meu primeiro objetivo era reproduzir aquele seletor visual de linhas e colunas comum em editores de texto — uma interface simples, interativa e intuitiva para inserir tabelas. Em vez de criar tudo do zero, decidi aproveitar uma estrutura que o próprio Trix já fornece: a caixa de diálogo utilizada para inserção de links.

Na barra de ferramentas original do editor, ela funciona assim:

A ideia é relativamente simples: um botão com o atributo data-trix-action dispara uma ação interna do editor. No caso acima, a ação link abre automaticamente o diálogo associado ao atributo href .

Para criar algo semelhante, precisamos seguir a convenção descrita no repositório do Trix: ações customizadas devem utilizar o prefixo x- . Quando um botão com data-trixaction=” x-alguma-coisa” é clicado, o editor dispara o evento trix-action-invoke , permitindo que adicionemos nosso próprio comportamento.

Com isso, conseguimos abrir e fechar diálogos personalizados utilizando apenas um pouco de JavaScript e algumas classes CSS.

O HTML da nova ação ficou assim:

E a implementação em JavaScript, adicionada ao mesmo controller Stimulus utilizado no artigo anterior:

Nesse ponto, ainda não existe nenhum seletor de tabelas de verdade — apenas um diálogo simples contendo um “hello world!” . Mas isso já é suficiente para validar a integração com o sistema interno de ações do Trix e confirmar que conseguimos estender sua interface sem modificar diretamente o código-fonte do editor.

Agora, com Stimulus, criamos um controller para gerenciar o seletor de tabelas.

E o diálogo para representa esse controller.

Com isso, ao clicar no botão da tabela na barra de ferramentas, um diálogo é aberto e permite escolher visualmente a dimensão da tabela, isto é, o número de linhas e colunas.

Ao clicar em um dos quadrados, uma ação identificada como trix#openTableEditor será executada. É aqui que começamos a construir a tabela propriamente dita.

Criando o editor de tabela

Uma das coisas que torna o Stimulus muito interessante é sua capacidade de inicializar controllers automaticamente quando novos elementos são inseridos no DOM. É justamente esse comportamento que iremos aproveitar para adicionar nosso editor de tabelas.

De volta ao trix-controller , iremos implementar openTableEditor .

Quando o usuário seleciona a dimensão desejada, essa função é executada e adiciona ao DOM um controller Stimulus responsável por criar o editor de tabelas.
Como você sabe, o Trix não possui suporte nativo a tabelas. Então, a solução que
encontramos para simular uma tabela é criar uma grade HTML e posicionar uma instância do editor em cada célula.

Isso nos permite reutilizar os recursos de edição do próprio Trix em cada célula, mas cria um novo problema: não podemos deixar o usuário perceber que, por trás da interface, existem vários editores diferentes.

Também precisamos lidar com as diferentes barras de ferramentas.

Como utilizamos Bootstrap, aproveitamos o modal fornecido pela biblioteca, envolvido por um wrapper que permite customizá-lo facilmente. Como cada projeto pode utilizar ferramentas visuais diferentes, vou me ater aqui aos trechos estritamente necessários para o funcionamento da tabela.

O código acima começa posicionando um container para as barras de ferramentas no topo do modal. Em seguida, a tabela é criada de acordo com as dimensões selecionadas, com um trix-editor para cada célula.

Os editores são identificados pelo target editor , e quando o controller detecta que um novo editor foi adicionado (através de editorTargetConnected ), a barra de ferramentas original é trocada por uma outra (customizada, conforme demonstrado no artigo anterior) e essa nova barra de ferramentas é posicionada no container mencionado anteriormente.

Então, um EventListener é ligado ao eventos de foco na tabela, de forma que quando o usuário interagir com uma célula (tabela) específica, sua barra de ferramentas é mostrada enquanto as outras são ocultadas, tudo via CSS.
Por fim, adicionamos um botão responsável por salvar o conteúdo da tabela no editor principal.

É aqui que a ajuda de Benoit foi imprescindível.

Salvando o conteúdo no editor principal

Minhas primeiras tentativas de salvar o conteúdo da tabela no editor principal falhavam porque a tabela precisava ser incluída como um anexo HTML.

O que eu não sabia era que seria necessário criar um content-type próprio para esse anexo. Foi essa informação crucial que Benoit compartilhou, junto de algumas funções para lidar com a tabela na forma de um anexo.

Vamos adicionar os seguintes métodos ao trix_controller.js:

Em attachTable, utilizamos um parser de tabela para transformar o conteúdo de cada editor em células de uma tabela HTML real. Em seguida, criamos um Trix.Attachment utilizando nosso content-type personalizado:

application/vnd.acsiv.table.html

É através desse tipo que conseguimos inserir um anexo personalizado no Trix e,
posteriormente, identificar se o anexo selecionado pelo usuário é uma tabela.

Essa identificação será importante para permitir que o usuário edite uma tabela que já tenha sido inserida no documento.

Por fim, precisamos adicionar um novo EventListener à inicialização do nosso controller Stimulus e incluir o TrixTableParser.

O método openTablePickerDialog foi então estendido para identificar se o anexo
selecionado é uma tabela e, caso seja, permitir que o usuário a edite.

Para isso, o conteúdo do anexo é carregado pelo parser, que reconstrói a estrutura da tabela e transforma suas células novamente em editores Trix.

Em seguida, criamos um novo trix-table-editor, desta vez carregando o conteúdo
original da tabela.

detectTableAttachment é responsável pelo feedback visual: quando o cursor está
sobre uma tabela, o botão correspondente na barra de ferramentas é marcado como ativo.

Agora, confira o código do TrixTableParser :

A classe expõe dois métodos principais.

import() recebe uma tabela HTML literal e a transforma novamente em uma estrutura que pode ser manipulada pelo controller Stimulus trix-table-editor . Cada célula é reconstruída como uma instância do Trix.

Já export() faz o caminho inverso: recebe a estrutura utilizada pelo editor e a transforma em uma tabela HTML literal, pronta para ser armazenada como conteúdo do anexo.

Com isso, conseguimos implementar tabelas HTML funcionais dentro do Trix, utilizando essencialmente o Stimulus e os mecanismos de extensão oferecidos pelo próprio editor.

Foi um trabalho relativamente extenso, mas bastante gratificante — especialmente porque, apesar de o Trix não possuir suporte nativo a tabelas, conseguimos construir uma solução sem precisar modificar diretamente seu código-fonte.

E agora?


Porém, há uma pequena ironia nessa história.

A 37signals, criadora do Trix, anunciou recentemente que pretende substituir o Trix pelo Lexxy, um editor de texto moderno baseado no Lexical, do Facebook.

E, para a nossa felicidade, o novo editor possui integração com o ecossistema Rails e suporte nativo a tabelas. Ou seja: depois de todo esse trabalho para colocar tabelas no Trix, talvez não precisemos mais fazer nada disso no futuro.

Mas, no fim das contas, o objetivo deste artigo não é apenas mostrar uma implementação específica. O mais interessante foi descobrir até onde podemos levar o Trix utilizando os próprios mecanismos de extensão do editor.

E, para quem ainda precisa trabalhar com Trix, espero que esta implementação possa servir como ponto de partida.

Escrito por: Fillipe Palhares