@thibezer/ui-components-kit 2.14.0 → 2.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,16 +1,28 @@
1
1
  # Changelog
2
2
 
3
+ ## 2.15.0 — 2026-11-05
4
+
5
+ ### `<ui-tabela>`: células interativas (botão, checkbox, campo editável) sem perder foco nem texto
6
+
7
+ - **Rolagem virtual preserva as linhas visíveis:** novo modo `renderBody('rolagem')` (usado pelo scroll e pela rolagem programática). Linhas com o mesmo item, a mesma posição e as mesmas colunas ficam intactas; só as que entram na janela são criadas. O campo em edição mantém o nó, o foco e o texto digitado, e o `render` roda só para as linhas novas.
8
+ - **Foco devolvido após renderização completa** (trocar `dados`, filtrar, ordenar): o foco volta ao mesmo controle da mesma linha (achada pelo objeto, pelo `data-id` ou pela posição), com texto digitado, checkbox marcado e seleção de texto de `input`/`textarea`/`select` nativos, se o dado não mudou nesse meio-tempo (senão o dado novo prevalece). A tabela nunca grava nos dados. Novo micro-módulo `tabela-foco.ts`.
9
+ - **Cliques em controles não selecionam a linha:** além de `button`, `input`, `select`, `textarea`, `a` e `summary`, agora também `label`, `contenteditable` e elementos com `role` de controle (`checkbox`, `switch`, `radio`, `textbox`, `combobox`, `slider`, `spinbutton`, `menuitem`, `tab`, `link`), como `<ui-checkbox>` e `<ui-switch>`.
10
+ - **`render(valor, item, indice, indiceDados)`:** novo 4º argumento com a posição do item em `dados` (estável ao ordenar e filtrar). `indice` continua sendo a posição exibida.
11
+ - **Leitor de tela:** a região de anúncio do filtro só é escrita quando o texto muda (antes era reescrita a cada quadro de rolagem com filtro ativo).
12
+ - **Atributos:** mudar `aria-label`, `texto-erro` ou `texto-carregando` não recria mais as linhas.
13
+ - Testes de regressão em `ui-tabela-robustez.test.ts`; README com o contrato de células interativas.
14
+
3
15
  ## 2.14.0 — 2026-11-05
4
16
 
5
- ### `<ui-canvas-cad>` & `gerencigeo-canvas`: auditoria, seleção avançada, navegação e HUD de coordenadas
6
-
7
- - **Seleção aditiva e subtrativa:** Suporte a `Shift` (adicionar à seleção) e `Ctrl`/`Cmd` (remover/subtrair da seleção) tanto no clique em feições quanto na caixa de seleção por arraste (caixa vermelha estilizada para operação subtrativa).
8
- - **Escape na Edição de Vértices:** Pressionar `Escape` com nó selecionado desmarca o nó; pressionar `Escape` sem nó selecionado cancela a edição e reverte a feição para a geometria original (`cancelar()`).
9
- - **Navegação com Barra de Espaço (`Hand Tool`):** Segurar a barra de espaço permite pan fluido com botão esquerdo do mouse (ideal para notebooks e trackpads), respeitando campos digitáveis.
10
- - **HUD de Coordenadas em Tempo Real:** Rodapé do canvas exibindo coordenadas UTM (Fuso, Hemisfério, Este e Norte formatados no padrão brasileiro) e coordenadas geográficas (Lat/Lon decimais), com suporte ao atributo `sem-hud-coordenadas`.
11
- - **Desempenho & GC:** Otimização de Garbage Collection em `feature_snap.ts` eliminando alocações de arrays e tuplas intermediárias durante o hot-path do cursor; cancelamento de timers concorrentes no hover de feições.
12
- - Testes abrangentes em `tests/canvas-melhorias-auditoria.test.ts`.
13
-
17
+ ### `<ui-canvas-cad>` & `gerencigeo-canvas`: auditoria, seleção avançada, navegação e HUD de coordenadas
18
+
19
+ - **Seleção aditiva e subtrativa:** Suporte a `Shift` (adicionar à seleção) e `Ctrl`/`Cmd` (remover/subtrair da seleção) tanto no clique em feições quanto na caixa de seleção por arraste (caixa vermelha estilizada para operação subtrativa).
20
+ - **Escape na Edição de Vértices:** Pressionar `Escape` com nó selecionado desmarca o nó; pressionar `Escape` sem nó selecionado cancela a edição e reverte a feição para a geometria original (`cancelar()`).
21
+ - **Navegação com Barra de Espaço (`Hand Tool`):** Segurar a barra de espaço permite pan fluido com botão esquerdo do mouse (ideal para notebooks e trackpads), respeitando campos digitáveis.
22
+ - **HUD de Coordenadas em Tempo Real:** Rodapé do canvas exibindo coordenadas UTM (Fuso, Hemisfério, Este e Norte formatados no padrão brasileiro) e coordenadas geográficas (Lat/Lon decimais), com suporte ao atributo `sem-hud-coordenadas`.
23
+ - **Desempenho & GC:** Otimização de Garbage Collection em `feature_snap.ts` eliminando alocações de arrays e tuplas intermediárias durante o hot-path do cursor; cancelamento de timers concorrentes no hover de feições.
24
+ - Testes abrangentes em `tests/canvas-melhorias-auditoria.test.ts`.
25
+
14
26
  ## 2.13.0 — 2026-11-05
15
27
 
16
28
  ### `<ui-canvas-cad>`: atributo `qualidade` (`alta` | `media` | `baixa`)
package/README.md CHANGED
@@ -505,7 +505,9 @@ const ok = await tabela.carregarDoEndpoint('/api/v1/vertices'); // true em suces
505
505
  | `ui-fetch-erro` | `{ url, erro, origem, status? }` | Falha. `origem`: `rede`, `http` (com `status`), `json` ou `render` |
506
506
  | `ui-fetch-cancelado` | `{ url }` | Requisição abortada ou substituída (fecha o par do `ui-fetch-start`) |
507
507
 
508
- Boas práticas: trate `dados` como imutável (atribua sempre um array novo), mantenha `chave-id` única e não nula, memorize `colunas` (atribuí-las de novo refaz a tabela) e coloque controles interativos das células com `data-prevent-select` se não quiser que o clique selecione a linha. `render` devolvendo `string` vira texto (não HTML); devolva um `HTMLElement` novo por chamada.
508
+ Boas práticas: trate `dados` como imutável (atribua sempre um array novo), mantenha `chave-id` única e não nula, memorize `colunas` (atribuí-las de novo refaz a tabela) e coloque controles interativos das células com `data-prevent-select` se não quiser que o clique selecione a linha (`button`, `input`, `select`, `textarea`, `a`, `label`, `contenteditable` e elementos com `role` de controle, como o `<ui-checkbox>` e o `<ui-switch>`, já são ignorados). `render` devolvendo `string` vira texto (não HTML); devolva um `HTMLElement` novo por chamada.
509
+
510
+ **Células interativas (`render`)**: `render(valor, item, indice, indiceDados)` pode devolver botão, checkbox ou campo editável. `indice` é a posição **exibida** (muda ao ordenar e filtrar); para editar ou remover use `item` ou `indiceDados`, a posição do item em `dados`. Com virtualização, a rolagem mantém as linhas que continuam visíveis (o campo em edição conserva foco e texto) e só renderiza as que entram. Trocar `dados`, filtrar ou ordenar recria as linhas e roda o `render` de novo: o foco volta ao mesmo controle da mesma linha, com o texto digitado (ou o checkbox marcado) e a seleção de texto, desde que o dado do item não tenha mudado nesse meio-tempo (senão o dado novo prevalece). A tabela nunca grava nos dados: grave o valor em `item` no seu handler (`input`/`change`). Vale para `input`, `textarea` e `select` nativos; componentes próprios (`<ui-campo-texto>` etc.) mantêm o foco, mas não o texto não gravado. `dados` guarda uma cópia do array (entradas nulas são descartadas): para refletir um `push`, atribua um array novo.
509
511
 
510
512
  ---
511
513
 
@@ -1,4 +1,5 @@
1
1
  import { TabelaColuna } from './tipos';
2
+ export type ModoRenderCorpo = 'completo' | 'rolagem';
2
3
  export interface ContextoCorpoTabela {
3
4
  tbodyElement: HTMLTableSectionElement | null;
4
5
  tableElement: HTMLTableElement | null;
@@ -15,6 +16,10 @@ export interface ContextoCorpoTabela {
15
16
  filtroAtivo?: boolean;
16
17
  textoVazio?: string;
17
18
  textoSemResultados?: string;
19
+ /** `completo` recria as linhas; `rolagem` preserva as que seguem na janela virtual. */
20
+ modo?: ModoRenderCorpo;
21
+ /** Posição do item em `dados` (4º argumento do `render`); sem ela vale a posição exibida. */
22
+ indiceOriginal?: (item: Record<string, any>) => number;
18
23
  isItemSelecionado: (item: Record<string, any>, index: number) => boolean;
19
24
  onLinhaClique: (item: Record<string, any>, index: number) => void;
20
25
  formatWidth: (largura?: string | number) => string;
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Foco dentro das células da <ui-tabela>.
3
+ * Responsabilidade única: guardar qual controle de qual linha tinha o foco antes de recriar o corpo e
4
+ * devolvê-lo ao controle equivalente depois (a linha é achada pelo objeto, pelo `data-id` ou pela posição).
5
+ */
6
+ /** Controle focado dentro de uma célula (não a própria linha). */
7
+ export interface FocoCelula {
8
+ item: Record<string, any> | undefined;
9
+ dataId: string | null;
10
+ indiceLinha: number;
11
+ indiceColuna: number;
12
+ /** Posição do elemento entre os descendentes da célula (a mesma estrutura é recriada pelo `render`). */
13
+ ordinal: number;
14
+ selecaoInicio: number | null;
15
+ selecaoFim: number | null;
16
+ /** Valor que o usuário digitou/marcou e ainda não foi gravado nos dados (undefined = sem edição pendente). */
17
+ digitado?: string | boolean;
18
+ /** Valor com que o controle foi renderizado: serve para saber se os dados mudaram por baixo da edição. */
19
+ valorInicial?: string | boolean;
20
+ }
21
+ /** Quem renderizou cada linha: permite achar o item de uma `<tr>` depois que o corpo é reconstruído. */
22
+ export declare const itemDaLinha: WeakMap<Element, Record<string, any>>;
23
+ /** Guarda o valor inicial dos campos nativos da célula recém-renderizada. */
24
+ export declare function registrarValoresRenderizados(celula: Element): void;
25
+ /** Registra o controle focado dentro do corpo, ou `null` se o foco está fora das células. */
26
+ export declare function capturarFocoCelula(tbody: HTMLTableSectionElement): FocoCelula | null;
27
+ /** Devolve o foco ao controle equivalente após recriar o corpo. Sem equivalente, não faz nada. */
28
+ export declare function restaurarFocoCelula(tbody: HTMLTableSectionElement, foco: FocoCelula | null): void;
@@ -4,6 +4,7 @@ export declare function normalizarTexto(t: string): string;
4
4
  export declare class TabelaOrquestradorDados {
5
5
  private dadosOriginais;
6
6
  private dadosExibicao;
7
+ private indicePorItem;
7
8
  private colunaOrdenada;
8
9
  private direcaoOrdenacao;
9
10
  private ultimoFiltro;
@@ -19,6 +20,8 @@ export declare class TabelaOrquestradorDados {
19
20
  setColunasFiltro(ids: string[]): void;
20
21
  getDadosOriginais(): Record<string, any>[];
21
22
  setDadosOriginais(dados: Record<string, any>[]): void;
23
+ /** Posição do item em `dados` (-1 se não pertence). Índice por objeto montado sob demanda, uma vez por troca de dados. */
24
+ indiceOriginal(item: Record<string, any>): number;
22
25
  getDadosExibicao(): Record<string, any>[];
23
26
  getColunaOrdenada(): string | null;
24
27
  setColunaOrdenada(id: string | null): void;
@@ -1,4 +1,5 @@
1
1
  import { TabelaColuna } from './tipos';
2
+ import { ModoRenderCorpo } from './tabela-corpo';
2
3
  import { ListenerBag } from '../../core/listener-bag';
3
4
  import { EstruturaDOMElementos } from './tabela-dom';
4
5
  import { TabelaOrquestradorDados } from './tabela-orquestrador-dados';
@@ -20,6 +21,8 @@ export interface ContextoOrquestradorRender {
20
21
  virtualizar: boolean;
21
22
  rowHeight: number;
22
23
  indiceAtivo: number | null;
24
+ /** `rolagem` preserva as linhas que seguem visíveis na janela virtual (padrão: `completo`). */
25
+ modoCorpo?: ModoRenderCorpo;
23
26
  textoVazio?: string;
24
27
  textoSemResultados?: string;
25
28
  onSetIsResizing: (resizing: boolean) => void;
@@ -17,7 +17,13 @@ export interface TabelaColuna<T = any> {
17
17
  comparador?: (a: any, b: any, itemA: T, itemB: T) => number;
18
18
  /** Texto pesquisável da célula (o filtro padrão olha o valor bruto); útil quando `render` formata o valor. */
19
19
  textoFiltro?: (valor: any, item: T) => string;
20
- render?: (valor: any, item: T, index: number) => HTMLElement | string;
20
+ /**
21
+ * Conteúdo da célula: `HTMLElement` (botão, checkbox, campo...) ou texto (nunca HTML).
22
+ * `index` é a posição **exibida** (muda ao ordenar e filtrar); `indiceDados` é a posição do item em `dados`
23
+ * (estável). Para editar ou remover, prefira `item` ou `indiceDados` a `index`.
24
+ * Na rolagem com virtualização, linhas que continuam visíveis não são renderizadas de novo.
25
+ */
26
+ render?: (valor: any, item: T, index: number, indiceDados: number) => HTMLElement | string;
21
27
  }
22
28
  export interface UISortDetail {
23
29
  idColuna: string | null;
@@ -1,4 +1,5 @@
1
1
  import { DensidadeTabela, TabelaColuna, TabelaFetcher, UISortDetail, UIColumnResizeDetail, UIRowScrollOptions, UISelecaoRemovidaDetail, UIFetchErroDetail } from './tipos';
2
+ import { ModoRenderCorpo } from './tabela-corpo';
2
3
  import { SafeHTMLElement } from '../../core/ssr-safe';
3
4
  export type { DensidadeTabela, TabelaColuna, TabelaFetcher, UISortDetail, UIColumnResizeDetail, UIRowScrollOptions, UISelecaoRemovidaDetail, UIFetchErroDetail };
4
5
  export declare class UITabela extends SafeHTMLElement {
@@ -119,7 +120,11 @@ export declare class UITabela extends SafeHTMLElement {
119
120
  private handleTecladoCorpo;
120
121
  private obterContextoRenderizador;
121
122
  private renderHeader;
122
- renderBody(): void;
123
+ /**
124
+ * Renderiza o corpo. `completo` (padrão) recria as linhas e roda o `render` das colunas de novo;
125
+ * `rolagem` só ajusta a janela virtual e mantém as linhas que continuam visíveis (foco e texto digitado intactos).
126
+ */
127
+ renderBody(modo?: ModoRenderCorpo): void;
123
128
  /**
124
129
  * Rola até um item e opcionalmente o seleciona. Aceita ID (pela `chave-id`, depois `id`, `_id`,
125
130
  * `codigo`, `key`), predicado ou índice. Um número é procurado primeiro como ID; use