@softize/opus 12.5.3 → 12.5.5

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
@@ -7,6 +7,25 @@ Depois de qualquer bump, rode os gates (`typecheck` · `test` · `opus check` ·
7
7
  `opus copy --check` · `base copy check` · `manifest:check`) — eles apontam o que a
8
8
  mudança cobra do seu código.
9
9
 
10
+ ## 12.5.5 — 2026-08-25
11
+
12
+ Só documentação: leva ao pacote a correção de nota que a 12.5.4 recebeu no repositório depois de
13
+ publicada. A nota descrevia os três testes novos da `Dock` como discriminantes; só o do foco que
14
+ sai da barra distingue o código corrigido do anterior — os outros dois guardam propriedades da
15
+ barra, não regressões. Nenhuma mudança de comportamento.
16
+
17
+ ## 12.5.4 — 2026-08-25
18
+
19
+ A `Dock` lembra por onde o Tab entra depois que o foco sai dela: quem andou até a quinta ação e
20
+ voltou para a tela reentra na quinta, não no começo da barra. É essa a correção, e é ela que o
21
+ teste novo cobra — o teste da 12.5.3 remontava a barra em vez de re-renderizá-la, então passava
22
+ igual contra o código bugado.
23
+
24
+ Duas correções de nota, para quem for ler o histórico: a da 12.5.3 dizia que todo re-render
25
+ devolvia a entrada ao primeiro ícone, o que não acontecia (o efeito já rodava a cada render); e
26
+ dos três casos que a 12.5.4 acrescentou, só o do foco que sai da barra distingue o código corrigido
27
+ do anterior — os outros dois guardam propriedades da barra, não regressões.
28
+
10
29
  ## 12.5.3 — 2026-08-24
11
30
 
12
31
  O roving tabindex da `Dock` sobrevive ao re-render do consumidor. Antes o efeito devolvia o ponto
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@softize/opus",
3
- "version": "12.5.3",
3
+ "version": "12.5.5",
4
4
  "description": "End-to-end action protocol for TypeScript. Single package with subpath exports (core + adapters).",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -35,20 +35,29 @@ export interface DockProps {
35
35
  /** Barra flutuante ancorada à superfície. O contêiner precisa ser `relative`. */
36
36
  export function Dock({ position = 'bottom', label, className, children }: DockProps): React.ReactElement {
37
37
  const ref = React.useRef<HTMLDivElement>(null)
38
+ // Onde o Tab entra na barra. Fica num ref porque precisa sobreviver ao foco SAIR dela: quem
39
+ // andou até o quinto ícone e voltou pra tela espera reentrar no quinto, não no primeiro.
40
+ const entrada = React.useRef<HTMLButtonElement | null>(null)
38
41
 
39
42
  // Roving tabindex: uma toolbar tem UM ponto de entrada no Tab; dentro dela, as setas andam.
40
43
  // Sem isto as setas seriam um caminho a mais, e a barra continuaria cobrando um Tab por ícone.
41
- const roving = React.useCallback((focado?: HTMLElement) => {
44
+ const roving = React.useCallback((focado?: HTMLElement | null) => {
42
45
  const items = Array.from(ref.current?.querySelectorAll<HTMLButtonElement>('button') ?? [])
43
- const entrada = items.find((item) => item === focado) ?? items.find((item) => !item.disabled)
44
- for (const item of items) item.tabIndex = item === entrada ? 0 : -1
46
+ // Foco fora da barra não move a entrada confirma a que já estava valendo.
47
+ if (focado instanceof HTMLButtonElement && items.includes(focado)) entrada.current = focado
48
+ const lembrada = entrada.current
49
+ const alvo =
50
+ (lembrada !== null && items.includes(lembrada) && !lembrada.disabled ? lembrada : undefined) ??
51
+ items.find((item) => !item.disabled)
52
+ entrada.current = alvo ?? null
53
+ for (const item of items) item.tabIndex = item === alvo ? 0 : -1
45
54
  }, [])
46
55
 
47
56
  React.useEffect(() => {
48
- // Passa o foco corrente: sem isto, todo re-render do consumidor devolvia o ponto de entrada
49
- // ao primeiro ícone e tirava do Tab quem estava focado.
50
- const ativo = document.activeElement
51
- roving(ativo instanceof HTMLElement ? ativo : undefined)
57
+ // Roda a cada render porque as ações da barra mudam com o estado da tela: sem recalcular, um
58
+ // ícone recém-montado entraria no Tab por conta própria, e sumir com o ícone que era a
59
+ // entrada deixaria a barra inteira fora do Tab.
60
+ roving(document.activeElement as HTMLElement | null)
52
61
  })
53
62
 
54
63
  // Navegação de toolbar: as setas andam entre as ações, e Home/End vão às pontas. Sem isso