ilibras-widget 1.5.2 → 1.6.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
@@ -4,6 +4,86 @@ Todas as mudanças notáveis neste projeto serão documentadas neste arquivo.
4
4
 
5
5
  ---
6
6
 
7
+ ## [1.6.0] - 2026-09-29
8
+
9
+ ### 🧭 Avisa antes de levar a pessoa para o iLibras
10
+
11
+ Ao clicar em **Iniciar atendimento em Libras** ou em **Agendar uma conversa**,
12
+ o widget agora mostra, no lugar do formulário, o aviso de que a pessoa será
13
+ redirecionada para o iLibras — numa aba nova — e só segue quando ela confirma
14
+ em **Continuar para o iLibras**. **Voltar** (ou Esc) devolve o formulário com o
15
+ nome ainda preenchido.
16
+
17
+ O atendimento só é criado **depois** do "sim". Criar antes e perguntar depois
18
+ deixaria um atendimento órfão na fila de quem desistiu — com o alarme dos
19
+ intérpretes tocando para ninguém.
20
+
21
+ ### 🗂️ A aba nova abre dentro do clique
22
+
23
+ A aba do iLibras passa a ser aberta no próprio clique de confirmação, em
24
+ branco ("Abrindo o iLibras…"), e só recebe o endereço quando o atendimento
25
+ volta criado. Antes ela era aberta depois da resposta da API — e `window.open`
26
+ depois de um `await` já não conta como gesto do usuário, que é quando o
27
+ bloqueador de pop-up age. Se a criação falhar, a aba reservada é fechada e a
28
+ pessoa volta ao formulário, onde está a explicação do que houve. O plano B de
29
+ sempre (trocar a página da aba atual) continua valendo para quando nem a aba
30
+ do clique é permitida.
31
+
32
+ ### ⚙️ Nova opção: `confirmarRedirecionamento`
33
+
34
+ Ligada por padrão. Com `false`, o widget se comporta como antes da 1.6.0: o
35
+ botão cria o atendimento e abre o iLibras direto.
36
+
37
+ ### ♿ Acessibilidade
38
+
39
+ - O aviso é um `alertdialog` com título e texto ligados: ao receber o foco, o
40
+ leitor de tela lê os dois antes do botão.
41
+ - O foco vai para **Continuar** ao abrir o aviso e volta ao botão que a pessoa
42
+ tinha apertado ao sair dele.
43
+ - A região de status saiu de dentro do formulário: escondida junto com ele
44
+ durante o aviso, deixaria de anunciar o andamento do envio.
45
+ - O botão de contorno (agendar, e agora também **Voltar**) ganhou foco visível
46
+ próprio, inclusive em alto contraste — antes dependia do padrão do navegador.
47
+
48
+ ### 🧪 Testes
49
+
50
+ `tests/confirmacao.test.mjs` exercita o fluxo na build publicada, com rede e
51
+ janelas simuladas: nada enviado antes da confirmação, aba aberta antes da
52
+ requisição, aba fechada na falha, clique duplo criando um atendimento só.
53
+ Rodada contra a 1.5.3, a suíte reprova em 9 de 10 casos — o único que passa é
54
+ o da opção desligada, que é justamente o comportamento antigo.
55
+
56
+ ---
57
+
58
+ ## [1.5.3] - 2026-09-18
59
+
60
+ ### 🩹 Conserta o widget que sumiu da tela na 1.5.1
61
+
62
+ A 1.5.1 escopou o estilo ao container para o tema do site não pintar o widget —
63
+ e escopou demais.
64
+
65
+ As classes de posição (`.ilibras-widget-bottom-right` e irmãs) ficam no
66
+ **próprio** container, não num descendente dele. Viraram
67
+ `#ilibras-widget-container .ilibras-widget-bottom-right`, com espaço, que não
68
+ casa com nada: o container perdeu `bottom` e `right`, ficou `fixed` sem âncora
69
+ e foi parar onde o fluxo o largou — milhares de pixels abaixo da tela. O widget
70
+ sumiu de um site em produção.
71
+
72
+ Agora o escopo dessas quatro é composto — `#ilibras-widget-container.classe` —
73
+ e o das demais segue descendente.
74
+
75
+ A mesma passagem trazia outros dois furos, dos quais só este deu as caras:
76
+
77
+ - listas de seletores quebradas em várias linhas tinham só a última linha
78
+ escopada, e as demais ficavam expostas ao tema;
79
+ - blocos `@media` inteiros passaram sem escopo, porque a regra logo depois da
80
+ chave do `@media` não era reconhecida.
81
+
82
+ Os três estão cobertos por teste agora: 124 seletores escopados, 16 deles
83
+ compostos, nenhum solto.
84
+
85
+ ---
86
+
7
87
  ## [1.5.2] - 2026-09-18
8
88
 
9
89
  ### 🗂️ Uma aba, não duas
package/README.md CHANGED
@@ -168,6 +168,7 @@ new ILibrasWidget({
168
168
  | `buttonText` | `string` | `'Iniciar atendimento em Libras'` | Texto do botão de envio |
169
169
  | `agendamento` | `boolean` | `true` | Mostra o segundo botão, que leva direto à escolha de data e horário em vez da fila ao vivo. Deixe `false` em site que só atende ao vivo |
170
170
  | `scheduleButtonText` | `string` | `'Agendar uma conversa'` | Texto do botão de agendamento |
171
+ | `confirmarRedirecionamento` | `boolean` | `true` | Avisa que a pessoa vai para o site do iLibras, numa aba nova, e só cria o atendimento depois que ela confirma. Com `false`, o botão cria o atendimento e abre o iLibras direto, como antes da v1.6.0 |
171
172
  | `token` | `string` | `''` | **OBRIGATÓRIO** — token de autenticação da API |
172
173
  | `zIndex` | `number` | `9999` | Z-index do widget |
173
174
  | `apiUrl` | `string` | endpoint de produção | Endpoint de cadastro. Troque apenas para apontar a um ambiente de homologação |
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * iLibras Widget v1.5.2
2
+ * iLibras Widget v1.6.0
3
3
  * https://github.com/Dimendes-Solucoes/Ilibras_widget
4
4
  * Licença MIT
5
5
  *
@@ -53,22 +53,22 @@ var CSS = `/**
53
53
  }
54
54
 
55
55
  /* Posicionamento */
56
- #ilibras-widget-container .ilibras-widget-bottom-right {
56
+ #ilibras-widget-container.ilibras-widget-bottom-right {
57
57
  bottom: 30px;
58
58
  right: 30px;
59
59
  }
60
60
 
61
- #ilibras-widget-container .ilibras-widget-bottom-left {
61
+ #ilibras-widget-container.ilibras-widget-bottom-left {
62
62
  bottom: 30px;
63
63
  left: 30px;
64
64
  }
65
65
 
66
- #ilibras-widget-container .ilibras-widget-top-right {
66
+ #ilibras-widget-container.ilibras-widget-top-right {
67
67
  top: 30px;
68
68
  right: 30px;
69
69
  }
70
70
 
71
- #ilibras-widget-container .ilibras-widget-top-left {
71
+ #ilibras-widget-container.ilibras-widget-top-left {
72
72
  top: 30px;
73
73
  left: 30px;
74
74
  }
@@ -101,7 +101,7 @@ var CSS = `/**
101
101
  height: 64px;
102
102
  }
103
103
 
104
- .ilibras-widget-button:hover,
104
+ #ilibras-widget-container .ilibras-widget-button:hover,
105
105
  #ilibras-widget-container .ilibras-widget-button:active {
106
106
  transform: none;
107
107
  box-shadow: none;
@@ -131,7 +131,8 @@ var CSS = `/**
131
131
  }
132
132
 
133
133
  @keyframes ilibras-pulse {
134
- 0%, 100% {
134
+ 0%,
135
+ 100% {
135
136
  transform: scale(1);
136
137
  }
137
138
  50% {
@@ -164,23 +165,23 @@ var CSS = `/**
164
165
  }
165
166
 
166
167
  /* Posicionamento do modal baseado na posição do botão */
167
- .ilibras-widget-bottom-right .ilibras-widget-modal,
168
- #ilibras-widget-container .ilibras-widget-bottom-left .ilibras-widget-modal {
168
+ #ilibras-widget-container.ilibras-widget-bottom-right .ilibras-widget-modal,
169
+ #ilibras-widget-container.ilibras-widget-bottom-left .ilibras-widget-modal {
169
170
  bottom: 105px;
170
171
  }
171
172
 
172
- .ilibras-widget-top-right .ilibras-widget-modal,
173
- #ilibras-widget-container .ilibras-widget-top-left .ilibras-widget-modal {
173
+ #ilibras-widget-container.ilibras-widget-top-right .ilibras-widget-modal,
174
+ #ilibras-widget-container.ilibras-widget-top-left .ilibras-widget-modal {
174
175
  top: 105px;
175
176
  }
176
177
 
177
- .ilibras-widget-bottom-right .ilibras-widget-modal,
178
- #ilibras-widget-container .ilibras-widget-top-right .ilibras-widget-modal {
178
+ #ilibras-widget-container.ilibras-widget-bottom-right .ilibras-widget-modal,
179
+ #ilibras-widget-container.ilibras-widget-top-right .ilibras-widget-modal {
179
180
  right: 0;
180
181
  }
181
182
 
182
- .ilibras-widget-bottom-left .ilibras-widget-modal,
183
- #ilibras-widget-container .ilibras-widget-top-left .ilibras-widget-modal {
183
+ #ilibras-widget-container.ilibras-widget-bottom-left .ilibras-widget-modal,
184
+ #ilibras-widget-container.ilibras-widget-top-left .ilibras-widget-modal {
184
185
  left: 0;
185
186
  }
186
187
 
@@ -274,7 +275,7 @@ var CSS = `/**
274
275
  padding-left: 4px;
275
276
  }
276
277
 
277
- .ilibras-widget-form-group input[type="text"],
278
+ #ilibras-widget-container .ilibras-widget-form-group input[type="text"],
278
279
  #ilibras-widget-container .ilibras-widget-form-group input[type="tel"] {
279
280
  background: #ffffff;
280
281
  border: 1px solid rgba(20,30,45,0.06);
@@ -283,7 +284,7 @@ var CSS = `/**
283
284
  border-radius: 8px;
284
285
  }
285
286
 
286
- .ilibras-widget-form-group input[type="text"]:focus,
287
+ #ilibras-widget-container .ilibras-widget-form-group input[type="text"]:focus,
287
288
  #ilibras-widget-container .ilibras-widget-form-group input[type="tel"]:focus {
288
289
  background: white;
289
290
  border-color: #357ABD;
@@ -291,7 +292,7 @@ var CSS = `/**
291
292
  outline: none;
292
293
  }
293
294
 
294
- .ilibras-widget-form-group input[type="text"]::placeholder,
295
+ #ilibras-widget-container .ilibras-widget-form-group input[type="text"]::placeholder,
295
296
  #ilibras-widget-container .ilibras-widget-form-group input[type="tel"]::placeholder {
296
297
  color: #6c757d;
297
298
  }
@@ -384,6 +385,42 @@ var CSS = `/**
384
385
  text-align: center;
385
386
  }
386
387
 
388
+ /* Aviso de saída para o iLibras: toma o lugar do formulário até a pessoa
389
+ confirmar ou voltar. O atributo hidden precisa de regra própria nos dois —
390
+ o display: flex de cada um venceria o atributo, e os dois apareceriam
391
+ juntos. (Sem crase nos comentários deste arquivo: ele é embutido num
392
+ template literal do JS, e uma crase aqui muda a forma do build inteiro.) */
393
+ #ilibras-widget-container .ilibras-widget-form[hidden],
394
+ #ilibras-widget-container .ilibras-widget-confirmacao[hidden] {
395
+ display: none !important;
396
+ }
397
+
398
+ #ilibras-widget-container .ilibras-widget-confirmacao {
399
+ display: flex;
400
+ flex-direction: column;
401
+ gap: 12px;
402
+ }
403
+
404
+ #ilibras-widget-container .ilibras-widget-confirmacao-titulo {
405
+ margin: 0;
406
+ font-size: 17px;
407
+ font-weight: 600;
408
+ line-height: 1.35;
409
+ color: #073657;
410
+ }
411
+
412
+ #ilibras-widget-container .ilibras-widget-confirmacao-texto {
413
+ margin: 0 0 6px;
414
+ font-size: 14px;
415
+ line-height: 1.5;
416
+ color: #495057;
417
+ }
418
+
419
+ /* "Voltar" tem o visual do botão de agendar, sem a margem que o cola à dica. */
420
+ #ilibras-widget-container .ilibras-widget-confirmacao .ilibras-widget-agendar {
421
+ margin-top: 0;
422
+ }
423
+
387
424
  /* Responsividade */
388
425
  @media (max-width: 480px) {
389
426
  #ilibras-widget-container .ilibras-widget-modal {
@@ -391,15 +428,15 @@ var CSS = `/**
391
428
  max-width: 100%;
392
429
  }
393
430
 
394
- .ilibras-widget-bottom-right,
395
- #ilibras-widget-container .ilibras-widget-bottom-left {
431
+ #ilibras-widget-container.ilibras-widget-bottom-right,
432
+ #ilibras-widget-container.ilibras-widget-bottom-left {
396
433
  bottom: 20px;
397
434
  left: 20px !important;
398
435
  right: 20px !important;
399
436
  }
400
437
 
401
- .ilibras-widget-top-right,
402
- #ilibras-widget-container .ilibras-widget-top-left {
438
+ #ilibras-widget-container.ilibras-widget-top-right,
439
+ #ilibras-widget-container.ilibras-widget-top-left {
403
440
  top: 20px;
404
441
  left: 20px !important;
405
442
  right: 20px !important;
@@ -433,8 +470,8 @@ var CSS = `/**
433
470
  }
434
471
  }
435
472
 
436
- .ilibras-widget-button:focus-visible,
437
- .ilibras-widget-close:focus-visible,
473
+ #ilibras-widget-container .ilibras-widget-button:focus-visible,
474
+ #ilibras-widget-container .ilibras-widget-close:focus-visible,
438
475
  #ilibras-widget-container .ilibras-widget-submit:focus-visible {
439
476
  outline: 3px solid rgba(53,122,189,0.18);
440
477
  outline-offset: 3px;
@@ -459,8 +496,8 @@ var CSS = `/**
459
496
  /* Estados de erro: ver o bloco de acessibilidade no fim do arquivo. */
460
497
 
461
498
  /* Accessibilidade */
462
- .ilibras-widget-button:focus-visible,
463
- .ilibras-widget-close:focus-visible,
499
+ #ilibras-widget-container .ilibras-widget-button:focus-visible,
500
+ #ilibras-widget-container .ilibras-widget-close:focus-visible,
464
501
  #ilibras-widget-container .ilibras-widget-submit:focus-visible {
465
502
  outline: 3px solid #4A90E2;
466
503
  outline-offset: 2px;
@@ -485,14 +522,14 @@ var CSS = `/**
485
522
  color: #b0b0b0;
486
523
  }
487
524
 
488
- .ilibras-widget-form-group input[type="text"],
525
+ #ilibras-widget-container .ilibras-widget-form-group input[type="text"],
489
526
  #ilibras-widget-container .ilibras-widget-form-group input[type="tel"] {
490
527
  background: #363636;
491
528
  border-color: #4a4a4a;
492
529
  color: #e0e0e0;
493
530
  }
494
531
 
495
- .ilibras-widget-form-group input[type="text"]::placeholder,
532
+ #ilibras-widget-container .ilibras-widget-form-group input[type="text"]::placeholder,
496
533
  #ilibras-widget-container .ilibras-widget-form-group input[type="tel"]::placeholder {
497
534
  color: #6c6c6c;
498
535
  }
@@ -632,20 +669,22 @@ var CSS = `/**
632
669
  font-weight: 600;
633
670
  }
634
671
 
672
+ /* Fora do formulário desde a v1.6.0, então a distância vem daqui e não do gap. */
635
673
  #ilibras-widget-container .ilibras-widget-status {
636
674
  font-size: 13px;
637
675
  color: #495057;
638
- margin: 0;
676
+ margin: 14px 0 0;
639
677
  min-height: 1em;
640
678
  }
641
679
 
642
680
  /* Foco visível em tudo que recebe foco, inclusive campos — o :focus original
643
681
  trocava a sombra, que some em alto contraste. */
644
- .ilibras-widget-button:focus-visible,
645
- .ilibras-widget-close:focus-visible,
646
- .ilibras-widget-submit:focus-visible,
647
- .ilibras-widget-form-group input:focus-visible,
648
- .ilibras-widget-checkbox-label input:focus-visible,
682
+ #ilibras-widget-container .ilibras-widget-button:focus-visible,
683
+ #ilibras-widget-container .ilibras-widget-close:focus-visible,
684
+ #ilibras-widget-container .ilibras-widget-submit:focus-visible,
685
+ #ilibras-widget-container .ilibras-widget-agendar:focus-visible,
686
+ #ilibras-widget-container .ilibras-widget-form-group input:focus-visible,
687
+ #ilibras-widget-container .ilibras-widget-checkbox-label input:focus-visible,
649
688
  #ilibras-widget-container .ilibras-widget-fallback a:focus-visible {
650
689
  outline: 3px solid #1f4f7a;
651
690
  outline-offset: 2px;
@@ -654,16 +693,17 @@ var CSS = `/**
654
693
  /* Windows em alto contraste remove cor de fundo e borda sutil: sem isto o
655
694
  campo e o botão ficam sem contorno. */
656
695
  @media (forced-colors: active) {
657
- .ilibras-widget-modal,
658
- .ilibras-widget-form-group input,
659
- .ilibras-widget-submit,
696
+ #ilibras-widget-container .ilibras-widget-modal,
697
+ #ilibras-widget-container .ilibras-widget-form-group input,
698
+ #ilibras-widget-container .ilibras-widget-submit,
660
699
  #ilibras-widget-container .ilibras-widget-aviso {
661
700
  border: 1px solid CanvasText;
662
701
  }
663
702
 
664
- .ilibras-widget-button:focus-visible,
665
- .ilibras-widget-close:focus-visible,
666
- .ilibras-widget-submit:focus-visible,
703
+ #ilibras-widget-container .ilibras-widget-button:focus-visible,
704
+ #ilibras-widget-container .ilibras-widget-close:focus-visible,
705
+ #ilibras-widget-container .ilibras-widget-submit:focus-visible,
706
+ #ilibras-widget-container .ilibras-widget-agendar:focus-visible,
667
707
  #ilibras-widget-container .ilibras-widget-form-group input:focus-visible {
668
708
  outline: 3px solid Highlight;
669
709
  }
@@ -673,23 +713,23 @@ var CSS = `/**
673
713
  das pessoas com sensibilidade vestibular, isso causa mal-estar. */
674
714
  @media (prefers-reduced-motion: reduce) {
675
715
  #ilibras-widget-container,
676
- .ilibras-widget-modal,
677
- .ilibras-widget-submit,
678
- .ilibras-widget-close,
716
+ #ilibras-widget-container .ilibras-widget-modal,
717
+ #ilibras-widget-container .ilibras-widget-submit,
718
+ #ilibras-widget-container .ilibras-widget-close,
679
719
  #ilibras-widget-container .ilibras-widget-badge {
680
720
  animation: none !important;
681
721
  transition: none !important;
682
722
  }
683
723
 
684
- .ilibras-widget-submit:hover,
724
+ #ilibras-widget-container .ilibras-widget-submit:hover,
685
725
  #ilibras-widget-container .ilibras-widget-close:hover {
686
726
  transform: none;
687
727
  }
688
728
  }
689
729
 
690
730
  @media (prefers-color-scheme: dark) {
691
- .ilibras-widget-legenda,
692
- .ilibras-widget-dica,
731
+ #ilibras-widget-container .ilibras-widget-legenda,
732
+ #ilibras-widget-container .ilibras-widget-dica,
693
733
  #ilibras-widget-container .ilibras-widget-status {
694
734
  color: #c9c9c9;
695
735
  }
@@ -717,10 +757,15 @@ var CSS = `/**
717
757
  color: #9ecbff;
718
758
  }
719
759
 
720
- #ilibras-widget-container .ilibras-widget-title {
760
+ #ilibras-widget-container .ilibras-widget-title,
761
+ #ilibras-widget-container .ilibras-widget-confirmacao-titulo {
721
762
  color: #e6f0f8;
722
763
  }
723
764
 
765
+ #ilibras-widget-container .ilibras-widget-confirmacao-texto {
766
+ color: #c9c9c9;
767
+ }
768
+
724
769
  #ilibras-widget-container .ilibras-widget-header {
725
770
  background: #1e1e1e;
726
771
  border-left-color: #333;
@@ -748,6 +793,10 @@ var defaultConfig = {
748
793
  // uma agenda que ninguém cobre é pior do que não ter o botão.
749
794
  agendamento: true,
750
795
  scheduleButtonText: "Agendar uma conversa",
796
+ // Avisa que a pessoa vai sair do site para o iLibras e espera a confirmação
797
+ // antes de criar o atendimento. Sair do site em que se estava, para uma aba
798
+ // nova, sem saber antes, desorienta — sobretudo quem usa leitor de tela.
799
+ confirmarRedirecionamento: true,
751
800
  token: "",
752
801
  zIndex: 9999,
753
802
  apiUrl: "https://sistema.ilibras.com.br/api/public/widget/cadastrar",
@@ -778,6 +827,7 @@ var ILibrasWidget = class {
778
827
  this.enviando = false;
779
828
  this.abertoEm = null;
780
829
  this.focoAnterior = null;
830
+ this.modoPendente = null;
781
831
  this.init();
782
832
  }
783
833
  init() {
@@ -880,10 +930,30 @@ var ILibrasWidget = class {
880
930
  <span class="ilibras-widget-dica ilibras-widget-dica-agendar" id="ilibras-widget-agendar-dica">
881
931
  Escolha data e horário em vez de esperar na fila.
882
932
  </span>` : ""}
883
-
884
- <!-- Estado do envio, anunciado sem roubar o foco de quem digita. -->
885
- <p class="ilibras-widget-status" id="ilibras-widget-status" role="status" aria-live="polite"></p>
886
933
  </form>
934
+
935
+ <!-- Aviso de saída do site. Ocupa o lugar do formulário em vez de
936
+ abrir uma janela por cima: o diálogo já é modal, e um segundo
937
+ diálogo empilhado confunde quem navega por leitor de tela. -->
938
+ <div id="ilibras-widget-confirmacao" class="ilibras-widget-confirmacao" role="alertdialog"
939
+ aria-labelledby="ilibras-widget-confirmacao-titulo"
940
+ aria-describedby="ilibras-widget-confirmacao-texto" hidden>
941
+ <h3 class="ilibras-widget-confirmacao-titulo" id="ilibras-widget-confirmacao-titulo">
942
+ Você será redirecionado para o iLibras
943
+ </h3>
944
+ <p class="ilibras-widget-confirmacao-texto" id="ilibras-widget-confirmacao-texto"></p>
945
+ <button type="button" id="ilibras-widget-confirmar" class="ilibras-widget-submit">
946
+ Continuar para o iLibras
947
+ </button>
948
+ <button type="button" id="ilibras-widget-voltar" class="ilibras-widget-agendar">
949
+ Voltar
950
+ </button>
951
+ </div>
952
+
953
+ <!-- Estado do envio, anunciado sem roubar o foco de quem digita. Fica
954
+ fora do formulário: escondido junto com ele durante o aviso de
955
+ saída, o leitor de tela deixaria de anunciar o que acontece. -->
956
+ <p class="ilibras-widget-status" id="ilibras-widget-status" role="status" aria-live="polite"></p>
887
957
  </div>
888
958
  </div>
889
959
  `;
@@ -896,6 +966,10 @@ var ILibrasWidget = class {
896
966
  this.agendar = container.querySelector("#ilibras-widget-agendar");
897
967
  this.aviso = container.querySelector("#ilibras-widget-aviso");
898
968
  this.status = container.querySelector("#ilibras-widget-status");
969
+ this.confirmacao = container.querySelector("#ilibras-widget-confirmacao");
970
+ this.confirmacaoTexto = container.querySelector("#ilibras-widget-confirmacao-texto");
971
+ this.confirmar = container.querySelector("#ilibras-widget-confirmar");
972
+ this.voltar = container.querySelector("#ilibras-widget-voltar");
899
973
  }
900
974
  escapar(texto) {
901
975
  const div = document.createElement("div");
@@ -908,6 +982,10 @@ var ILibrasWidget = class {
908
982
  this.modal.addEventListener("keydown", (e) => {
909
983
  if (e.key === "Escape") {
910
984
  e.stopPropagation();
985
+ if (!this.confirmacao.hidden) {
986
+ if (!this.enviando) this.cancelarConfirmacao();
987
+ return;
988
+ }
911
989
  this.closeWidget();
912
990
  return;
913
991
  }
@@ -923,6 +1001,8 @@ var ILibrasWidget = class {
923
1001
  if (this.agendar) {
924
1002
  this.agendar.addEventListener("click", (e) => this.handleSubmit(e, "agendamento"));
925
1003
  }
1004
+ this.confirmar.addEventListener("click", () => this.confirmarRedirecionamento());
1005
+ this.voltar.addEventListener("click", () => this.cancelarConfirmacao());
926
1006
  }
927
1007
  prenderFoco(evento) {
928
1008
  const focaveis = Array.from(this.modal.querySelectorAll(FOCAVEIS)).filter((el) => el.offsetParent !== null || el === document.activeElement);
@@ -952,6 +1032,7 @@ var ILibrasWidget = class {
952
1032
  this.container.querySelector("#ilibras-name").focus();
953
1033
  }
954
1034
  closeWidget() {
1035
+ this.mostrarFormulario();
955
1036
  this.modal.classList.remove("ilibras-widget-modal-open");
956
1037
  this.botao.classList.remove("ilibras-widget-button-open");
957
1038
  this.botao.setAttribute("aria-expanded", "false");
@@ -1119,6 +1200,85 @@ var ILibrasWidget = class {
1119
1200
  invalido.focus();
1120
1201
  return;
1121
1202
  }
1203
+ if (this.config.confirmarRedirecionamento) {
1204
+ this.pedirConfirmacao(modo);
1205
+ return;
1206
+ }
1207
+ await this.criarAtendimento(modo);
1208
+ }
1209
+ // ─── Aviso de saída ─────────────────────────────────────────────────────
1210
+ /**
1211
+ * Troca o formulário pelo aviso de que a pessoa vai para o iLibras. Nada é
1212
+ * enviado ainda: o atendimento só nasce no "Continuar".
1213
+ */
1214
+ pedirConfirmacao(modo) {
1215
+ this.modoPendente = modo;
1216
+ this.confirmacaoTexto.textContent = modo === "agendamento" ? "O agendamento é feito no site do iLibras, que abre em uma nova aba. Lá você escolhe o dia e o horário da conversa." : "O atendimento em Libras acontece no site do iLibras, que abre em uma nova aba. Lá você entra na fila e aguarda um intérprete.";
1217
+ this.confirmar.textContent = "Continuar para o iLibras";
1218
+ this.form.hidden = true;
1219
+ this.confirmacao.hidden = false;
1220
+ this.anunciar("");
1221
+ this.confirmar.focus();
1222
+ }
1223
+ /** "Voltar": o formulário reaparece como estava, com o nome digitado. */
1224
+ cancelarConfirmacao() {
1225
+ if (this.enviando) return;
1226
+ const modo = this.modoPendente;
1227
+ this.mostrarFormulario();
1228
+ const origem = modo === "agendamento" && this.agendar ? this.agendar : this.submit;
1229
+ origem.focus();
1230
+ }
1231
+ mostrarFormulario() {
1232
+ this.modoPendente = null;
1233
+ this.confirmacao.hidden = true;
1234
+ this.form.hidden = false;
1235
+ }
1236
+ async confirmarRedirecionamento() {
1237
+ if (this.enviando || !this.modoPendente) return;
1238
+ const aba = this.reservarAba();
1239
+ await this.criarAtendimento(this.modoPendente, aba);
1240
+ }
1241
+ /**
1242
+ * Abre uma aba em branco para receber o iLibras assim que o atendimento
1243
+ * existir. Devolve null quando o navegador barra — aí vale o caminho antigo.
1244
+ */
1245
+ reservarAba() {
1246
+ let aba = null;
1247
+ try {
1248
+ aba = window.open("", "_blank");
1249
+ } catch (e) {
1250
+ aba = null;
1251
+ }
1252
+ if (!aba) return null;
1253
+ aba.opener = null;
1254
+ try {
1255
+ const doc = aba.document;
1256
+ doc.title = "Abrindo o iLibras…";
1257
+ doc.documentElement.lang = "pt-BR";
1258
+ doc.body.style.cssText = "font:16px/1.5 system-ui,-apple-system,Segoe UI,sans-serif;padding:32px;color:#073657";
1259
+ doc.body.textContent = "Abrindo o iLibras…";
1260
+ } catch (e) {
1261
+ }
1262
+ return aba;
1263
+ }
1264
+ /**
1265
+ * Leva a pessoa ao endereço devolvido pelo iLibras: na aba reservada quando
1266
+ * ela existe, numa aba nova quando não, e na atual como último recurso.
1267
+ */
1268
+ irPara(destino, abaReservada) {
1269
+ if (abaReservada && !abaReservada.closed) {
1270
+ abaReservada.location.href = destino;
1271
+ return;
1272
+ }
1273
+ const aba = window.open(destino, "_blank");
1274
+ if (aba) {
1275
+ aba.opener = null;
1276
+ } else {
1277
+ window.location.assign(destino);
1278
+ }
1279
+ }
1280
+ // ─── Criação do atendimento ─────────────────────────────────────────────
1281
+ async criarAtendimento(modo, abaReservada = null) {
1122
1282
  const v = this.valores();
1123
1283
  const corpo = new FormData();
1124
1284
  corpo.append("nome", v.nome);
@@ -1131,15 +1291,17 @@ var ILibrasWidget = class {
1131
1291
  const destino = resultado.link_fila || resultado.link || resultado.url || resultado.redirect;
1132
1292
  if (!destino) throw new FalhaDeServico("resposta_inesperada");
1133
1293
  this.anunciar(modo === "agendamento" ? "Cadastro feito. Abrindo o calendário para escolher data e horário." : "Atendimento criado. Abrindo a sala de atendimento.");
1134
- const aba = window.open(destino, "_blank");
1135
- if (aba) {
1136
- aba.opener = null;
1137
- } else {
1138
- window.location.assign(destino);
1139
- }
1294
+ this.irPara(destino, abaReservada);
1140
1295
  this.form.reset();
1141
1296
  this.closeWidget();
1142
1297
  } catch (erro) {
1298
+ if (abaReservada && !abaReservada.closed) {
1299
+ try {
1300
+ abaReservada.close();
1301
+ } catch (e) {
1302
+ }
1303
+ }
1304
+ this.mostrarFormulario();
1143
1305
  this.tratarFalha(erro);
1144
1306
  } finally {
1145
1307
  this.travarEnvio(false, modo);
@@ -1147,14 +1309,25 @@ var ILibrasWidget = class {
1147
1309
  }
1148
1310
  travarEnvio(travado, modo = "fila") {
1149
1311
  this.enviando = travado;
1150
- for (const botao of [this.submit, this.agendar]) {
1312
+ for (const botao of [this.submit, this.agendar, this.confirmar, this.voltar]) {
1151
1313
  if (botao) botao.disabled = travado;
1152
1314
  }
1153
- const usouAgendar = modo === "agendamento" && this.agendar;
1154
- const ativo = usouAgendar ? this.agendar : this.submit;
1155
- const rotulo = usouAgendar ? this.config.scheduleButtonText : this.config.buttonText;
1315
+ let ativo;
1316
+ let rotulo;
1317
+ if (!this.confirmacao.hidden) {
1318
+ ativo = this.confirmar;
1319
+ rotulo = "Continuar para o iLibras";
1320
+ } else {
1321
+ const usouAgendar = modo === "agendamento" && this.agendar;
1322
+ ativo = usouAgendar ? this.agendar : this.submit;
1323
+ rotulo = usouAgendar ? this.config.scheduleButtonText : this.config.buttonText;
1324
+ }
1156
1325
  ativo.setAttribute("aria-busy", travado ? "true" : "false");
1157
1326
  ativo.textContent = travado ? "Enviando..." : rotulo;
1327
+ if (!travado) {
1328
+ this.confirmar.setAttribute("aria-busy", "false");
1329
+ this.confirmar.textContent = "Continuar para o iLibras";
1330
+ }
1158
1331
  if (travado) this.anunciar("Enviando seus dados. Aguarde.");
1159
1332
  }
1160
1333
  /**