Um pequeno gancho do React para transformar elementos em superfícies de conteúdo totalmente renderizáveis e editáveis, como editores de código, usando conteúdoditable (e mágica)
useEditable é um gancho pequeno que permite que os elementos sejam contenteditable e ainda sejam totalmente renderizáveis. Isso é ideal para criar pequenos editores de código ou textaras em prosa em apenas 2kB !
Ele pretende permitir que qualquer elemento seja editável, enquanto ainda pode renderizar elementos de reação normal a ele - sem innerHTML e ter que lidar com operar ou renderizar para HTML bruto ou iniciar um projeto de editor completo a partir do zero.
Confira a demonstração completa no Código e caixa com prism-react-renderer !
Primeiro instale use-editable ao lado react :
yarn add use-editable
# or
npm install --save use-editable Você poderá importar useEditable e passar em uma referência HTMLElement e um manipulador onChange .
import React , { useState , useRef } from 'react' ;
import { useEditable } from 'use-editable' ;
const RainbowCode = ( ) => {
const [ code , setCode ] = useState ( 'function test() {}nconsole.log("hello");' ) ;
const editorRef = useRef ( null ) ;
useEditable ( editorRef , setCode ) ;
return (
< div className = "App" >
< pre
style = { { whiteSpace : 'pre-wrap' , fontFamily : 'monospace' } }
ref = { editorRef }
>
{ code . split ( / r?n / ) . map ( ( content , i , arr ) => (
< React . Fragment key = { i } >
< span style = { { color : `hsl( ${ ( ( i % 20 ) * 17 ) | 0 } , 80%, 50%)` } } >
{ content }
</ span >
{ i < arr . length - 1 ? 'n' : null }
</ React . Fragment >
) ) }
</ pre >
</ div >
) ;
} ; E assim, conectamos useEditable ao nosso editorRef , que aponta para o elemento <pre> que está sendo renderizado e para setCode , que aciona nosso estado que contém algum código.
Esta biblioteca foi testada e deve funcionar corretamente usando:
Há problemas conhecidos no IE 11 devido ao fato de o método MutationObserver não conseguir ler nós de texto que foram removidos por meio do contenteditable .
Tradicionalmente, houve três opções ao escolher superfícies de edição no React. Qualquer um poderia optar por um grande projeto como Prosemirror / Codemirror ou similar, que assume o controle de grande parte dos eventos de edição e renderização e, portanto, é bastante opinativo, ou é possível usar apenas contenteditable e renderizar ao HTML bruto que é substituído no conteúdo do elemento, ou o último pode combinar um textarea com um div que se renderize.
Todas as três opções não permitem muita personalização em termos do que realmente é renderizado ou coloca restrições irracionais sobre como é fácil renderizar e gerenciar o conteúdo de um editável.
Então, o que torna a renderização de um elemento contenteditable com tanta força?
Normalmente, isso é difícil porque eles editam o DOM diretamente. Isso faz com que a maioria das bibliotecas de renderização, como React e Preact, seja confundida, pois seus DOMs virtuais subjacentes não combinam mais com a estrutura DOM real. Para impedir que esse problema use-editable , cria um MutationObserver , que vigia todas as alterações feitas no elemento contenteditable . Antes de relatar, essas alterações para reagir primeiro reviram todas as mudanças no DOM, para que o React veja o que espera.
Além disso, ele também preserva a posição atual do cuidador, a seleção e a restaura uma vez que o React atualize o próprio DOM. Essa é uma técnica bastante comum para editores contenteditable , mas a adição MutationObserver é o que permite que use-editable permitir que outra biblioteca atualize o conteúdo do elemento.
Atualmente, o conteúdo de texto dos elementos renderizados deve eventualmente corresponder exatamente à entrada de código ou sua implementação deve ser capaz de converter o conteúdo de texto renderizado novamente no que você está usando como estado. Essa é uma limitação de como o trabalho do contenteditable , pois eles apenas capturam o conteúdo real do DOM. Como use-editable não pretende ser um componente completo que gerencia o ciclo de renderização, ele não precisa manter nenhum estado extra, mas só passará o texto do DOM de volta ao retorno de chamada onChange .
Usando o retorno de chamada onChange você também receberá um objeto Position que descreve a posição do cursor, o número da linha atual e o conteúdo da linha até o cursor, que é útil para sugestões automáticas, que poderiam ser aplicadas com a função de update que usa devoluções useEditable para atualizar a posição do cursor.
O primeiro argumento é elementRef e aceita um objeto de referência de tipo RefObject<HTMLElement> que aponta para o elemento que deve se tornar editável. Este ref pode ser null ou mudar durante o tempo de execução do gancho. Enquanto as mudanças do REF forem acionadas pelo React, tudo deve se comportar conforme o esperado.
O segundo argumento é onChange e aceita um retorno de chamada do tipo (text: string, pos: Position) => void que é chamado sempre que o conteúdo das alterações contenteditable . Isso precisa ser configurado para acionar um reprodução do conteúdo do elemento.
O text que onChange recebe é apenas a representação textual do conteúdo do elemento, enquanto a Position que recebe contém a posição atual do cursor, o número da linha (indexado zero) e o conteúdo da linha atual até o cursor, que é útil para auto-sugestões.
O terceiro argumento é um objeto options opcionais. Atualmente, isso aceita duas opções para alterar o comportamento de edição do gancho:
disabled desativa a edição do editável, removendo o atributo contentEditable dele novamente.indentation pode ser um número de espaços exibidos para recuo. Isso também permite o comportamento aprimorado da chave Tab , que recuará a linha atual ou deduzirá a linha atual quando o turno for mantido (esteja ciente de que isso fará com que o editor atue como uma armadilha de foco!) Quando options.indentation for definido, useEditable impedirá a inserção dos caracteres da guia e, em vez disso, inserirá a quantidade especificada de espaços em branco, o que facilita muito o manuseio de colunas.
Além disso, o gancho useEditable retorna um identificador Edit com vários métodos, conforme documentado abaixo.
Edit.update(content: string): void
Substitui todo o conteúdo da editável enquanto ajusta a posição do zelador. Isso mudará o cuidador pela diferença de comprimento entre o conteúdo atual e o conteúdo passado.
Edit.insert(append: string, offset?: number): void
Insira um novo texto na posição de cuidar ao excluir o texto na faixa do deslocamento (que aceita compensações negativas). Por exemplo, quando offset é definido como -1 , um único caractere é excluído à esquerda do cuidador antes de inserir qualquer novo texto. Quando está definido como 2 , dois caracteres à direita dos cuidados são excluídos. O texto append também pode ser definido como uma string vazia para aplicar apenas exclusões sem inserir nenhum texto. Quando qualquer texto é selecionado, ele é simplesmente apagado primeiro e offset é ignorado.
Edit.move(pos: number | { row: number; column: number }): void
Isso move o cuidador para a posição especificada. A posição pode ser um índice de caracteres (um number ) ou coordenadas especificando uma row e column separadamente.
Edit.getState(): { text: string; position: Position }
Esse método permite obter o estado atual do editável, o mesmo que o que onChange geralmente recebe. Isso é útil ao adicionar ações de edição personalizadas em um manipulador de chave para baixo ou quando programentaticamente imitar onChange de outra forma, enquanto o editável é selecionado.
react-live , no qual trabalhei, teve um dos primeiros editores do Tiny contenteditable . (Mas com atualizações em HTML bruto)react-simple-code-editor foi a primeira (?) Biblioteca a usar uma textarea dividida e a implementação da superfície da renderização, que apresentava como seria uma boa API de edição.codejar contém os melhores truques para gerenciar seleções, embora não tenha algumas soluções alternativas do Firefox. Ele também usa destaque / atualização em HTML bruto.codemirror.next é uma fonte inestimável para ver diferentes técnicas ao lidar com a entrada de texto e os truques de atualização do DOM. Estável: A Formidável não planeja desenvolver novos recursos para este projeto. Ainda estamos respondendo a relatórios de bugs e preocupações de segurança. Ainda estamos recebendo o PRS para este projeto, mas os PRs que incluem novos recursos devem ser pequenos e fáceis de integrar e não devem incluir mudanças de quebra.