ilibras-widget 1.5.3 → 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,57 @@ 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
+
7
58
  ## [1.5.3] - 2026-09-18
8
59
 
9
60
  ### 🩹 Conserta o widget que sumiu da tela na 1.5.1
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.3
2
+ * iLibras Widget v1.6.0
3
3
  * https://github.com/Dimendes-Solucoes/Ilibras_widget
4
4
  * Licença MIT
5
5
  *
@@ -385,6 +385,42 @@ var CSS = `/**
385
385
  text-align: center;
386
386
  }
387
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
+
388
424
  /* Responsividade */
389
425
  @media (max-width: 480px) {
390
426
  #ilibras-widget-container .ilibras-widget-modal {
@@ -633,10 +669,11 @@ var CSS = `/**
633
669
  font-weight: 600;
634
670
  }
635
671
 
672
+ /* Fora do formulário desde a v1.6.0, então a distância vem daqui e não do gap. */
636
673
  #ilibras-widget-container .ilibras-widget-status {
637
674
  font-size: 13px;
638
675
  color: #495057;
639
- margin: 0;
676
+ margin: 14px 0 0;
640
677
  min-height: 1em;
641
678
  }
642
679
 
@@ -645,6 +682,7 @@ var CSS = `/**
645
682
  #ilibras-widget-container .ilibras-widget-button:focus-visible,
646
683
  #ilibras-widget-container .ilibras-widget-close:focus-visible,
647
684
  #ilibras-widget-container .ilibras-widget-submit:focus-visible,
685
+ #ilibras-widget-container .ilibras-widget-agendar:focus-visible,
648
686
  #ilibras-widget-container .ilibras-widget-form-group input:focus-visible,
649
687
  #ilibras-widget-container .ilibras-widget-checkbox-label input:focus-visible,
650
688
  #ilibras-widget-container .ilibras-widget-fallback a:focus-visible {
@@ -665,6 +703,7 @@ var CSS = `/**
665
703
  #ilibras-widget-container .ilibras-widget-button:focus-visible,
666
704
  #ilibras-widget-container .ilibras-widget-close:focus-visible,
667
705
  #ilibras-widget-container .ilibras-widget-submit:focus-visible,
706
+ #ilibras-widget-container .ilibras-widget-agendar:focus-visible,
668
707
  #ilibras-widget-container .ilibras-widget-form-group input:focus-visible {
669
708
  outline: 3px solid Highlight;
670
709
  }
@@ -718,10 +757,15 @@ var CSS = `/**
718
757
  color: #9ecbff;
719
758
  }
720
759
 
721
- #ilibras-widget-container .ilibras-widget-title {
760
+ #ilibras-widget-container .ilibras-widget-title,
761
+ #ilibras-widget-container .ilibras-widget-confirmacao-titulo {
722
762
  color: #e6f0f8;
723
763
  }
724
764
 
765
+ #ilibras-widget-container .ilibras-widget-confirmacao-texto {
766
+ color: #c9c9c9;
767
+ }
768
+
725
769
  #ilibras-widget-container .ilibras-widget-header {
726
770
  background: #1e1e1e;
727
771
  border-left-color: #333;
@@ -749,6 +793,10 @@ var defaultConfig = {
749
793
  // uma agenda que ninguém cobre é pior do que não ter o botão.
750
794
  agendamento: true,
751
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,
752
800
  token: "",
753
801
  zIndex: 9999,
754
802
  apiUrl: "https://sistema.ilibras.com.br/api/public/widget/cadastrar",
@@ -779,6 +827,7 @@ var ILibrasWidget = class {
779
827
  this.enviando = false;
780
828
  this.abertoEm = null;
781
829
  this.focoAnterior = null;
830
+ this.modoPendente = null;
782
831
  this.init();
783
832
  }
784
833
  init() {
@@ -881,10 +930,30 @@ var ILibrasWidget = class {
881
930
  <span class="ilibras-widget-dica ilibras-widget-dica-agendar" id="ilibras-widget-agendar-dica">
882
931
  Escolha data e horário em vez de esperar na fila.
883
932
  </span>` : ""}
884
-
885
- <!-- Estado do envio, anunciado sem roubar o foco de quem digita. -->
886
- <p class="ilibras-widget-status" id="ilibras-widget-status" role="status" aria-live="polite"></p>
887
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>
888
957
  </div>
889
958
  </div>
890
959
  `;
@@ -897,6 +966,10 @@ var ILibrasWidget = class {
897
966
  this.agendar = container.querySelector("#ilibras-widget-agendar");
898
967
  this.aviso = container.querySelector("#ilibras-widget-aviso");
899
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");
900
973
  }
901
974
  escapar(texto) {
902
975
  const div = document.createElement("div");
@@ -909,6 +982,10 @@ var ILibrasWidget = class {
909
982
  this.modal.addEventListener("keydown", (e) => {
910
983
  if (e.key === "Escape") {
911
984
  e.stopPropagation();
985
+ if (!this.confirmacao.hidden) {
986
+ if (!this.enviando) this.cancelarConfirmacao();
987
+ return;
988
+ }
912
989
  this.closeWidget();
913
990
  return;
914
991
  }
@@ -924,6 +1001,8 @@ var ILibrasWidget = class {
924
1001
  if (this.agendar) {
925
1002
  this.agendar.addEventListener("click", (e) => this.handleSubmit(e, "agendamento"));
926
1003
  }
1004
+ this.confirmar.addEventListener("click", () => this.confirmarRedirecionamento());
1005
+ this.voltar.addEventListener("click", () => this.cancelarConfirmacao());
927
1006
  }
928
1007
  prenderFoco(evento) {
929
1008
  const focaveis = Array.from(this.modal.querySelectorAll(FOCAVEIS)).filter((el) => el.offsetParent !== null || el === document.activeElement);
@@ -953,6 +1032,7 @@ var ILibrasWidget = class {
953
1032
  this.container.querySelector("#ilibras-name").focus();
954
1033
  }
955
1034
  closeWidget() {
1035
+ this.mostrarFormulario();
956
1036
  this.modal.classList.remove("ilibras-widget-modal-open");
957
1037
  this.botao.classList.remove("ilibras-widget-button-open");
958
1038
  this.botao.setAttribute("aria-expanded", "false");
@@ -1120,6 +1200,85 @@ var ILibrasWidget = class {
1120
1200
  invalido.focus();
1121
1201
  return;
1122
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) {
1123
1282
  const v = this.valores();
1124
1283
  const corpo = new FormData();
1125
1284
  corpo.append("nome", v.nome);
@@ -1132,15 +1291,17 @@ var ILibrasWidget = class {
1132
1291
  const destino = resultado.link_fila || resultado.link || resultado.url || resultado.redirect;
1133
1292
  if (!destino) throw new FalhaDeServico("resposta_inesperada");
1134
1293
  this.anunciar(modo === "agendamento" ? "Cadastro feito. Abrindo o calendário para escolher data e horário." : "Atendimento criado. Abrindo a sala de atendimento.");
1135
- const aba = window.open(destino, "_blank");
1136
- if (aba) {
1137
- aba.opener = null;
1138
- } else {
1139
- window.location.assign(destino);
1140
- }
1294
+ this.irPara(destino, abaReservada);
1141
1295
  this.form.reset();
1142
1296
  this.closeWidget();
1143
1297
  } catch (erro) {
1298
+ if (abaReservada && !abaReservada.closed) {
1299
+ try {
1300
+ abaReservada.close();
1301
+ } catch (e) {
1302
+ }
1303
+ }
1304
+ this.mostrarFormulario();
1144
1305
  this.tratarFalha(erro);
1145
1306
  } finally {
1146
1307
  this.travarEnvio(false, modo);
@@ -1148,14 +1309,25 @@ var ILibrasWidget = class {
1148
1309
  }
1149
1310
  travarEnvio(travado, modo = "fila") {
1150
1311
  this.enviando = travado;
1151
- for (const botao of [this.submit, this.agendar]) {
1312
+ for (const botao of [this.submit, this.agendar, this.confirmar, this.voltar]) {
1152
1313
  if (botao) botao.disabled = travado;
1153
1314
  }
1154
- const usouAgendar = modo === "agendamento" && this.agendar;
1155
- const ativo = usouAgendar ? this.agendar : this.submit;
1156
- 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
+ }
1157
1325
  ativo.setAttribute("aria-busy", travado ? "true" : "false");
1158
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
+ }
1159
1331
  if (travado) this.anunciar("Enviando seus dados. Aguarde.");
1160
1332
  }
1161
1333
  /**
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * iLibras Widget v1.5.3
2
+ * iLibras Widget v1.6.0
3
3
  * https://github.com/Dimendes-Solucoes/Ilibras_widget
4
4
  * Licença MIT
5
5
  *
@@ -359,6 +359,42 @@
359
359
  text-align: center;
360
360
  }
361
361
 
362
+ /* Aviso de saída para o iLibras: toma o lugar do formulário até a pessoa
363
+ confirmar ou voltar. O atributo hidden precisa de regra própria nos dois —
364
+ o display: flex de cada um venceria o atributo, e os dois apareceriam
365
+ juntos. (Sem crase nos comentários deste arquivo: ele é embutido num
366
+ template literal do JS, e uma crase aqui muda a forma do build inteiro.) */
367
+ #ilibras-widget-container .ilibras-widget-form[hidden],
368
+ #ilibras-widget-container .ilibras-widget-confirmacao[hidden] {
369
+ display: none !important;
370
+ }
371
+
372
+ #ilibras-widget-container .ilibras-widget-confirmacao {
373
+ display: flex;
374
+ flex-direction: column;
375
+ gap: 12px;
376
+ }
377
+
378
+ #ilibras-widget-container .ilibras-widget-confirmacao-titulo {
379
+ margin: 0;
380
+ font-size: 17px;
381
+ font-weight: 600;
382
+ line-height: 1.35;
383
+ color: #073657;
384
+ }
385
+
386
+ #ilibras-widget-container .ilibras-widget-confirmacao-texto {
387
+ margin: 0 0 6px;
388
+ font-size: 14px;
389
+ line-height: 1.5;
390
+ color: #495057;
391
+ }
392
+
393
+ /* "Voltar" tem o visual do botão de agendar, sem a margem que o cola à dica. */
394
+ #ilibras-widget-container .ilibras-widget-confirmacao .ilibras-widget-agendar {
395
+ margin-top: 0;
396
+ }
397
+
362
398
  /* Responsividade */
363
399
  @media (max-width: 480px) {
364
400
  #ilibras-widget-container .ilibras-widget-modal {
@@ -607,10 +643,11 @@
607
643
  font-weight: 600;
608
644
  }
609
645
 
646
+ /* Fora do formulário desde a v1.6.0, então a distância vem daqui e não do gap. */
610
647
  #ilibras-widget-container .ilibras-widget-status {
611
648
  font-size: 13px;
612
649
  color: #495057;
613
- margin: 0;
650
+ margin: 14px 0 0;
614
651
  min-height: 1em;
615
652
  }
616
653
 
@@ -619,6 +656,7 @@
619
656
  #ilibras-widget-container .ilibras-widget-button:focus-visible,
620
657
  #ilibras-widget-container .ilibras-widget-close:focus-visible,
621
658
  #ilibras-widget-container .ilibras-widget-submit:focus-visible,
659
+ #ilibras-widget-container .ilibras-widget-agendar:focus-visible,
622
660
  #ilibras-widget-container .ilibras-widget-form-group input:focus-visible,
623
661
  #ilibras-widget-container .ilibras-widget-checkbox-label input:focus-visible,
624
662
  #ilibras-widget-container .ilibras-widget-fallback a:focus-visible {
@@ -639,6 +677,7 @@
639
677
  #ilibras-widget-container .ilibras-widget-button:focus-visible,
640
678
  #ilibras-widget-container .ilibras-widget-close:focus-visible,
641
679
  #ilibras-widget-container .ilibras-widget-submit:focus-visible,
680
+ #ilibras-widget-container .ilibras-widget-agendar:focus-visible,
642
681
  #ilibras-widget-container .ilibras-widget-form-group input:focus-visible {
643
682
  outline: 3px solid Highlight;
644
683
  }
@@ -692,10 +731,15 @@
692
731
  color: #9ecbff;
693
732
  }
694
733
 
695
- #ilibras-widget-container .ilibras-widget-title {
734
+ #ilibras-widget-container .ilibras-widget-title,
735
+ #ilibras-widget-container .ilibras-widget-confirmacao-titulo {
696
736
  color: #e6f0f8;
697
737
  }
698
738
 
739
+ #ilibras-widget-container .ilibras-widget-confirmacao-texto {
740
+ color: #c9c9c9;
741
+ }
742
+
699
743
  #ilibras-widget-container .ilibras-widget-header {
700
744
  background: #1e1e1e;
701
745
  border-left-color: #333;
@@ -723,6 +767,10 @@
723
767
  // uma agenda que ninguém cobre é pior do que não ter o botão.
724
768
  agendamento: true,
725
769
  scheduleButtonText: "Agendar uma conversa",
770
+ // Avisa que a pessoa vai sair do site para o iLibras e espera a confirmação
771
+ // antes de criar o atendimento. Sair do site em que se estava, para uma aba
772
+ // nova, sem saber antes, desorienta — sobretudo quem usa leitor de tela.
773
+ confirmarRedirecionamento: true,
726
774
  token: "",
727
775
  zIndex: 9999,
728
776
  apiUrl: "https://sistema.ilibras.com.br/api/public/widget/cadastrar",
@@ -753,6 +801,7 @@
753
801
  this.enviando = false;
754
802
  this.abertoEm = null;
755
803
  this.focoAnterior = null;
804
+ this.modoPendente = null;
756
805
  this.init();
757
806
  }
758
807
  init() {
@@ -855,10 +904,30 @@
855
904
  <span class="ilibras-widget-dica ilibras-widget-dica-agendar" id="ilibras-widget-agendar-dica">
856
905
  Escolha data e horário em vez de esperar na fila.
857
906
  </span>` : ""}
858
-
859
- <!-- Estado do envio, anunciado sem roubar o foco de quem digita. -->
860
- <p class="ilibras-widget-status" id="ilibras-widget-status" role="status" aria-live="polite"></p>
861
907
  </form>
908
+
909
+ <!-- Aviso de saída do site. Ocupa o lugar do formulário em vez de
910
+ abrir uma janela por cima: o diálogo já é modal, e um segundo
911
+ diálogo empilhado confunde quem navega por leitor de tela. -->
912
+ <div id="ilibras-widget-confirmacao" class="ilibras-widget-confirmacao" role="alertdialog"
913
+ aria-labelledby="ilibras-widget-confirmacao-titulo"
914
+ aria-describedby="ilibras-widget-confirmacao-texto" hidden>
915
+ <h3 class="ilibras-widget-confirmacao-titulo" id="ilibras-widget-confirmacao-titulo">
916
+ Você será redirecionado para o iLibras
917
+ </h3>
918
+ <p class="ilibras-widget-confirmacao-texto" id="ilibras-widget-confirmacao-texto"></p>
919
+ <button type="button" id="ilibras-widget-confirmar" class="ilibras-widget-submit">
920
+ Continuar para o iLibras
921
+ </button>
922
+ <button type="button" id="ilibras-widget-voltar" class="ilibras-widget-agendar">
923
+ Voltar
924
+ </button>
925
+ </div>
926
+
927
+ <!-- Estado do envio, anunciado sem roubar o foco de quem digita. Fica
928
+ fora do formulário: escondido junto com ele durante o aviso de
929
+ saída, o leitor de tela deixaria de anunciar o que acontece. -->
930
+ <p class="ilibras-widget-status" id="ilibras-widget-status" role="status" aria-live="polite"></p>
862
931
  </div>
863
932
  </div>
864
933
  `;
@@ -871,6 +940,10 @@
871
940
  this.agendar = container.querySelector("#ilibras-widget-agendar");
872
941
  this.aviso = container.querySelector("#ilibras-widget-aviso");
873
942
  this.status = container.querySelector("#ilibras-widget-status");
943
+ this.confirmacao = container.querySelector("#ilibras-widget-confirmacao");
944
+ this.confirmacaoTexto = container.querySelector("#ilibras-widget-confirmacao-texto");
945
+ this.confirmar = container.querySelector("#ilibras-widget-confirmar");
946
+ this.voltar = container.querySelector("#ilibras-widget-voltar");
874
947
  }
875
948
  escapar(texto) {
876
949
  const div = document.createElement("div");
@@ -883,6 +956,10 @@
883
956
  this.modal.addEventListener("keydown", (e) => {
884
957
  if (e.key === "Escape") {
885
958
  e.stopPropagation();
959
+ if (!this.confirmacao.hidden) {
960
+ if (!this.enviando) this.cancelarConfirmacao();
961
+ return;
962
+ }
886
963
  this.closeWidget();
887
964
  return;
888
965
  }
@@ -898,6 +975,8 @@
898
975
  if (this.agendar) {
899
976
  this.agendar.addEventListener("click", (e) => this.handleSubmit(e, "agendamento"));
900
977
  }
978
+ this.confirmar.addEventListener("click", () => this.confirmarRedirecionamento());
979
+ this.voltar.addEventListener("click", () => this.cancelarConfirmacao());
901
980
  }
902
981
  prenderFoco(evento) {
903
982
  const focaveis = Array.from(this.modal.querySelectorAll(FOCAVEIS)).filter((el) => el.offsetParent !== null || el === document.activeElement);
@@ -927,6 +1006,7 @@
927
1006
  this.container.querySelector("#ilibras-name").focus();
928
1007
  }
929
1008
  closeWidget() {
1009
+ this.mostrarFormulario();
930
1010
  this.modal.classList.remove("ilibras-widget-modal-open");
931
1011
  this.botao.classList.remove("ilibras-widget-button-open");
932
1012
  this.botao.setAttribute("aria-expanded", "false");
@@ -1094,6 +1174,85 @@
1094
1174
  invalido.focus();
1095
1175
  return;
1096
1176
  }
1177
+ if (this.config.confirmarRedirecionamento) {
1178
+ this.pedirConfirmacao(modo);
1179
+ return;
1180
+ }
1181
+ await this.criarAtendimento(modo);
1182
+ }
1183
+ // ─── Aviso de saída ─────────────────────────────────────────────────────
1184
+ /**
1185
+ * Troca o formulário pelo aviso de que a pessoa vai para o iLibras. Nada é
1186
+ * enviado ainda: o atendimento só nasce no "Continuar".
1187
+ */
1188
+ pedirConfirmacao(modo) {
1189
+ this.modoPendente = modo;
1190
+ 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.";
1191
+ this.confirmar.textContent = "Continuar para o iLibras";
1192
+ this.form.hidden = true;
1193
+ this.confirmacao.hidden = false;
1194
+ this.anunciar("");
1195
+ this.confirmar.focus();
1196
+ }
1197
+ /** "Voltar": o formulário reaparece como estava, com o nome digitado. */
1198
+ cancelarConfirmacao() {
1199
+ if (this.enviando) return;
1200
+ const modo = this.modoPendente;
1201
+ this.mostrarFormulario();
1202
+ const origem = modo === "agendamento" && this.agendar ? this.agendar : this.submit;
1203
+ origem.focus();
1204
+ }
1205
+ mostrarFormulario() {
1206
+ this.modoPendente = null;
1207
+ this.confirmacao.hidden = true;
1208
+ this.form.hidden = false;
1209
+ }
1210
+ async confirmarRedirecionamento() {
1211
+ if (this.enviando || !this.modoPendente) return;
1212
+ const aba = this.reservarAba();
1213
+ await this.criarAtendimento(this.modoPendente, aba);
1214
+ }
1215
+ /**
1216
+ * Abre uma aba em branco para receber o iLibras assim que o atendimento
1217
+ * existir. Devolve null quando o navegador barra — aí vale o caminho antigo.
1218
+ */
1219
+ reservarAba() {
1220
+ let aba = null;
1221
+ try {
1222
+ aba = window.open("", "_blank");
1223
+ } catch (e) {
1224
+ aba = null;
1225
+ }
1226
+ if (!aba) return null;
1227
+ aba.opener = null;
1228
+ try {
1229
+ const doc = aba.document;
1230
+ doc.title = "Abrindo o iLibras…";
1231
+ doc.documentElement.lang = "pt-BR";
1232
+ doc.body.style.cssText = "font:16px/1.5 system-ui,-apple-system,Segoe UI,sans-serif;padding:32px;color:#073657";
1233
+ doc.body.textContent = "Abrindo o iLibras…";
1234
+ } catch (e) {
1235
+ }
1236
+ return aba;
1237
+ }
1238
+ /**
1239
+ * Leva a pessoa ao endereço devolvido pelo iLibras: na aba reservada quando
1240
+ * ela existe, numa aba nova quando não, e na atual como último recurso.
1241
+ */
1242
+ irPara(destino, abaReservada) {
1243
+ if (abaReservada && !abaReservada.closed) {
1244
+ abaReservada.location.href = destino;
1245
+ return;
1246
+ }
1247
+ const aba = window.open(destino, "_blank");
1248
+ if (aba) {
1249
+ aba.opener = null;
1250
+ } else {
1251
+ window.location.assign(destino);
1252
+ }
1253
+ }
1254
+ // ─── Criação do atendimento ─────────────────────────────────────────────
1255
+ async criarAtendimento(modo, abaReservada = null) {
1097
1256
  const v = this.valores();
1098
1257
  const corpo = new FormData();
1099
1258
  corpo.append("nome", v.nome);
@@ -1106,15 +1265,17 @@
1106
1265
  const destino = resultado.link_fila || resultado.link || resultado.url || resultado.redirect;
1107
1266
  if (!destino) throw new FalhaDeServico("resposta_inesperada");
1108
1267
  this.anunciar(modo === "agendamento" ? "Cadastro feito. Abrindo o calendário para escolher data e horário." : "Atendimento criado. Abrindo a sala de atendimento.");
1109
- const aba = window.open(destino, "_blank");
1110
- if (aba) {
1111
- aba.opener = null;
1112
- } else {
1113
- window.location.assign(destino);
1114
- }
1268
+ this.irPara(destino, abaReservada);
1115
1269
  this.form.reset();
1116
1270
  this.closeWidget();
1117
1271
  } catch (erro) {
1272
+ if (abaReservada && !abaReservada.closed) {
1273
+ try {
1274
+ abaReservada.close();
1275
+ } catch (e) {
1276
+ }
1277
+ }
1278
+ this.mostrarFormulario();
1118
1279
  this.tratarFalha(erro);
1119
1280
  } finally {
1120
1281
  this.travarEnvio(false, modo);
@@ -1122,14 +1283,25 @@
1122
1283
  }
1123
1284
  travarEnvio(travado, modo = "fila") {
1124
1285
  this.enviando = travado;
1125
- for (const botao of [this.submit, this.agendar]) {
1286
+ for (const botao of [this.submit, this.agendar, this.confirmar, this.voltar]) {
1126
1287
  if (botao) botao.disabled = travado;
1127
1288
  }
1128
- const usouAgendar = modo === "agendamento" && this.agendar;
1129
- const ativo = usouAgendar ? this.agendar : this.submit;
1130
- const rotulo = usouAgendar ? this.config.scheduleButtonText : this.config.buttonText;
1289
+ let ativo;
1290
+ let rotulo;
1291
+ if (!this.confirmacao.hidden) {
1292
+ ativo = this.confirmar;
1293
+ rotulo = "Continuar para o iLibras";
1294
+ } else {
1295
+ const usouAgendar = modo === "agendamento" && this.agendar;
1296
+ ativo = usouAgendar ? this.agendar : this.submit;
1297
+ rotulo = usouAgendar ? this.config.scheduleButtonText : this.config.buttonText;
1298
+ }
1131
1299
  ativo.setAttribute("aria-busy", travado ? "true" : "false");
1132
1300
  ativo.textContent = travado ? "Enviando..." : rotulo;
1301
+ if (!travado) {
1302
+ this.confirmar.setAttribute("aria-busy", "false");
1303
+ this.confirmar.textContent = "Continuar para o iLibras";
1304
+ }
1133
1305
  if (travado) this.anunciar("Enviando seus dados. Aguarde.");
1134
1306
  }
1135
1307
  /**
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * iLibras Widget v1.5.3
2
+ * iLibras Widget v1.6.0
3
3
  * https://github.com/Dimendes-Solucoes/Ilibras_widget
4
4
  * Licença MIT
5
5
  *
@@ -359,6 +359,42 @@ var CSS = `/**
359
359
  text-align: center;
360
360
  }
361
361
 
362
+ /* Aviso de saída para o iLibras: toma o lugar do formulário até a pessoa
363
+ confirmar ou voltar. O atributo hidden precisa de regra própria nos dois —
364
+ o display: flex de cada um venceria o atributo, e os dois apareceriam
365
+ juntos. (Sem crase nos comentários deste arquivo: ele é embutido num
366
+ template literal do JS, e uma crase aqui muda a forma do build inteiro.) */
367
+ #ilibras-widget-container .ilibras-widget-form[hidden],
368
+ #ilibras-widget-container .ilibras-widget-confirmacao[hidden] {
369
+ display: none !important;
370
+ }
371
+
372
+ #ilibras-widget-container .ilibras-widget-confirmacao {
373
+ display: flex;
374
+ flex-direction: column;
375
+ gap: 12px;
376
+ }
377
+
378
+ #ilibras-widget-container .ilibras-widget-confirmacao-titulo {
379
+ margin: 0;
380
+ font-size: 17px;
381
+ font-weight: 600;
382
+ line-height: 1.35;
383
+ color: #073657;
384
+ }
385
+
386
+ #ilibras-widget-container .ilibras-widget-confirmacao-texto {
387
+ margin: 0 0 6px;
388
+ font-size: 14px;
389
+ line-height: 1.5;
390
+ color: #495057;
391
+ }
392
+
393
+ /* "Voltar" tem o visual do botão de agendar, sem a margem que o cola à dica. */
394
+ #ilibras-widget-container .ilibras-widget-confirmacao .ilibras-widget-agendar {
395
+ margin-top: 0;
396
+ }
397
+
362
398
  /* Responsividade */
363
399
  @media (max-width: 480px) {
364
400
  #ilibras-widget-container .ilibras-widget-modal {
@@ -607,10 +643,11 @@ var CSS = `/**
607
643
  font-weight: 600;
608
644
  }
609
645
 
646
+ /* Fora do formulário desde a v1.6.0, então a distância vem daqui e não do gap. */
610
647
  #ilibras-widget-container .ilibras-widget-status {
611
648
  font-size: 13px;
612
649
  color: #495057;
613
- margin: 0;
650
+ margin: 14px 0 0;
614
651
  min-height: 1em;
615
652
  }
616
653
 
@@ -619,6 +656,7 @@ var CSS = `/**
619
656
  #ilibras-widget-container .ilibras-widget-button:focus-visible,
620
657
  #ilibras-widget-container .ilibras-widget-close:focus-visible,
621
658
  #ilibras-widget-container .ilibras-widget-submit:focus-visible,
659
+ #ilibras-widget-container .ilibras-widget-agendar:focus-visible,
622
660
  #ilibras-widget-container .ilibras-widget-form-group input:focus-visible,
623
661
  #ilibras-widget-container .ilibras-widget-checkbox-label input:focus-visible,
624
662
  #ilibras-widget-container .ilibras-widget-fallback a:focus-visible {
@@ -639,6 +677,7 @@ var CSS = `/**
639
677
  #ilibras-widget-container .ilibras-widget-button:focus-visible,
640
678
  #ilibras-widget-container .ilibras-widget-close:focus-visible,
641
679
  #ilibras-widget-container .ilibras-widget-submit:focus-visible,
680
+ #ilibras-widget-container .ilibras-widget-agendar:focus-visible,
642
681
  #ilibras-widget-container .ilibras-widget-form-group input:focus-visible {
643
682
  outline: 3px solid Highlight;
644
683
  }
@@ -692,10 +731,15 @@ var CSS = `/**
692
731
  color: #9ecbff;
693
732
  }
694
733
 
695
- #ilibras-widget-container .ilibras-widget-title {
734
+ #ilibras-widget-container .ilibras-widget-title,
735
+ #ilibras-widget-container .ilibras-widget-confirmacao-titulo {
696
736
  color: #e6f0f8;
697
737
  }
698
738
 
739
+ #ilibras-widget-container .ilibras-widget-confirmacao-texto {
740
+ color: #c9c9c9;
741
+ }
742
+
699
743
  #ilibras-widget-container .ilibras-widget-header {
700
744
  background: #1e1e1e;
701
745
  border-left-color: #333;
@@ -723,6 +767,10 @@ var defaultConfig = {
723
767
  // uma agenda que ninguém cobre é pior do que não ter o botão.
724
768
  agendamento: true,
725
769
  scheduleButtonText: "Agendar uma conversa",
770
+ // Avisa que a pessoa vai sair do site para o iLibras e espera a confirmação
771
+ // antes de criar o atendimento. Sair do site em que se estava, para uma aba
772
+ // nova, sem saber antes, desorienta — sobretudo quem usa leitor de tela.
773
+ confirmarRedirecionamento: true,
726
774
  token: "",
727
775
  zIndex: 9999,
728
776
  apiUrl: "https://sistema.ilibras.com.br/api/public/widget/cadastrar",
@@ -753,6 +801,7 @@ var ILibrasWidget = class {
753
801
  this.enviando = false;
754
802
  this.abertoEm = null;
755
803
  this.focoAnterior = null;
804
+ this.modoPendente = null;
756
805
  this.init();
757
806
  }
758
807
  init() {
@@ -855,10 +904,30 @@ var ILibrasWidget = class {
855
904
  <span class="ilibras-widget-dica ilibras-widget-dica-agendar" id="ilibras-widget-agendar-dica">
856
905
  Escolha data e horário em vez de esperar na fila.
857
906
  </span>` : ""}
858
-
859
- <!-- Estado do envio, anunciado sem roubar o foco de quem digita. -->
860
- <p class="ilibras-widget-status" id="ilibras-widget-status" role="status" aria-live="polite"></p>
861
907
  </form>
908
+
909
+ <!-- Aviso de saída do site. Ocupa o lugar do formulário em vez de
910
+ abrir uma janela por cima: o diálogo já é modal, e um segundo
911
+ diálogo empilhado confunde quem navega por leitor de tela. -->
912
+ <div id="ilibras-widget-confirmacao" class="ilibras-widget-confirmacao" role="alertdialog"
913
+ aria-labelledby="ilibras-widget-confirmacao-titulo"
914
+ aria-describedby="ilibras-widget-confirmacao-texto" hidden>
915
+ <h3 class="ilibras-widget-confirmacao-titulo" id="ilibras-widget-confirmacao-titulo">
916
+ Você será redirecionado para o iLibras
917
+ </h3>
918
+ <p class="ilibras-widget-confirmacao-texto" id="ilibras-widget-confirmacao-texto"></p>
919
+ <button type="button" id="ilibras-widget-confirmar" class="ilibras-widget-submit">
920
+ Continuar para o iLibras
921
+ </button>
922
+ <button type="button" id="ilibras-widget-voltar" class="ilibras-widget-agendar">
923
+ Voltar
924
+ </button>
925
+ </div>
926
+
927
+ <!-- Estado do envio, anunciado sem roubar o foco de quem digita. Fica
928
+ fora do formulário: escondido junto com ele durante o aviso de
929
+ saída, o leitor de tela deixaria de anunciar o que acontece. -->
930
+ <p class="ilibras-widget-status" id="ilibras-widget-status" role="status" aria-live="polite"></p>
862
931
  </div>
863
932
  </div>
864
933
  `;
@@ -871,6 +940,10 @@ var ILibrasWidget = class {
871
940
  this.agendar = container.querySelector("#ilibras-widget-agendar");
872
941
  this.aviso = container.querySelector("#ilibras-widget-aviso");
873
942
  this.status = container.querySelector("#ilibras-widget-status");
943
+ this.confirmacao = container.querySelector("#ilibras-widget-confirmacao");
944
+ this.confirmacaoTexto = container.querySelector("#ilibras-widget-confirmacao-texto");
945
+ this.confirmar = container.querySelector("#ilibras-widget-confirmar");
946
+ this.voltar = container.querySelector("#ilibras-widget-voltar");
874
947
  }
875
948
  escapar(texto) {
876
949
  const div = document.createElement("div");
@@ -883,6 +956,10 @@ var ILibrasWidget = class {
883
956
  this.modal.addEventListener("keydown", (e) => {
884
957
  if (e.key === "Escape") {
885
958
  e.stopPropagation();
959
+ if (!this.confirmacao.hidden) {
960
+ if (!this.enviando) this.cancelarConfirmacao();
961
+ return;
962
+ }
886
963
  this.closeWidget();
887
964
  return;
888
965
  }
@@ -898,6 +975,8 @@ var ILibrasWidget = class {
898
975
  if (this.agendar) {
899
976
  this.agendar.addEventListener("click", (e) => this.handleSubmit(e, "agendamento"));
900
977
  }
978
+ this.confirmar.addEventListener("click", () => this.confirmarRedirecionamento());
979
+ this.voltar.addEventListener("click", () => this.cancelarConfirmacao());
901
980
  }
902
981
  prenderFoco(evento) {
903
982
  const focaveis = Array.from(this.modal.querySelectorAll(FOCAVEIS)).filter((el) => el.offsetParent !== null || el === document.activeElement);
@@ -927,6 +1006,7 @@ var ILibrasWidget = class {
927
1006
  this.container.querySelector("#ilibras-name").focus();
928
1007
  }
929
1008
  closeWidget() {
1009
+ this.mostrarFormulario();
930
1010
  this.modal.classList.remove("ilibras-widget-modal-open");
931
1011
  this.botao.classList.remove("ilibras-widget-button-open");
932
1012
  this.botao.setAttribute("aria-expanded", "false");
@@ -1094,6 +1174,85 @@ var ILibrasWidget = class {
1094
1174
  invalido.focus();
1095
1175
  return;
1096
1176
  }
1177
+ if (this.config.confirmarRedirecionamento) {
1178
+ this.pedirConfirmacao(modo);
1179
+ return;
1180
+ }
1181
+ await this.criarAtendimento(modo);
1182
+ }
1183
+ // ─── Aviso de saída ─────────────────────────────────────────────────────
1184
+ /**
1185
+ * Troca o formulário pelo aviso de que a pessoa vai para o iLibras. Nada é
1186
+ * enviado ainda: o atendimento só nasce no "Continuar".
1187
+ */
1188
+ pedirConfirmacao(modo) {
1189
+ this.modoPendente = modo;
1190
+ 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.";
1191
+ this.confirmar.textContent = "Continuar para o iLibras";
1192
+ this.form.hidden = true;
1193
+ this.confirmacao.hidden = false;
1194
+ this.anunciar("");
1195
+ this.confirmar.focus();
1196
+ }
1197
+ /** "Voltar": o formulário reaparece como estava, com o nome digitado. */
1198
+ cancelarConfirmacao() {
1199
+ if (this.enviando) return;
1200
+ const modo = this.modoPendente;
1201
+ this.mostrarFormulario();
1202
+ const origem = modo === "agendamento" && this.agendar ? this.agendar : this.submit;
1203
+ origem.focus();
1204
+ }
1205
+ mostrarFormulario() {
1206
+ this.modoPendente = null;
1207
+ this.confirmacao.hidden = true;
1208
+ this.form.hidden = false;
1209
+ }
1210
+ async confirmarRedirecionamento() {
1211
+ if (this.enviando || !this.modoPendente) return;
1212
+ const aba = this.reservarAba();
1213
+ await this.criarAtendimento(this.modoPendente, aba);
1214
+ }
1215
+ /**
1216
+ * Abre uma aba em branco para receber o iLibras assim que o atendimento
1217
+ * existir. Devolve null quando o navegador barra — aí vale o caminho antigo.
1218
+ */
1219
+ reservarAba() {
1220
+ let aba = null;
1221
+ try {
1222
+ aba = window.open("", "_blank");
1223
+ } catch (e) {
1224
+ aba = null;
1225
+ }
1226
+ if (!aba) return null;
1227
+ aba.opener = null;
1228
+ try {
1229
+ const doc = aba.document;
1230
+ doc.title = "Abrindo o iLibras…";
1231
+ doc.documentElement.lang = "pt-BR";
1232
+ doc.body.style.cssText = "font:16px/1.5 system-ui,-apple-system,Segoe UI,sans-serif;padding:32px;color:#073657";
1233
+ doc.body.textContent = "Abrindo o iLibras…";
1234
+ } catch (e) {
1235
+ }
1236
+ return aba;
1237
+ }
1238
+ /**
1239
+ * Leva a pessoa ao endereço devolvido pelo iLibras: na aba reservada quando
1240
+ * ela existe, numa aba nova quando não, e na atual como último recurso.
1241
+ */
1242
+ irPara(destino, abaReservada) {
1243
+ if (abaReservada && !abaReservada.closed) {
1244
+ abaReservada.location.href = destino;
1245
+ return;
1246
+ }
1247
+ const aba = window.open(destino, "_blank");
1248
+ if (aba) {
1249
+ aba.opener = null;
1250
+ } else {
1251
+ window.location.assign(destino);
1252
+ }
1253
+ }
1254
+ // ─── Criação do atendimento ─────────────────────────────────────────────
1255
+ async criarAtendimento(modo, abaReservada = null) {
1097
1256
  const v = this.valores();
1098
1257
  const corpo = new FormData();
1099
1258
  corpo.append("nome", v.nome);
@@ -1106,15 +1265,17 @@ var ILibrasWidget = class {
1106
1265
  const destino = resultado.link_fila || resultado.link || resultado.url || resultado.redirect;
1107
1266
  if (!destino) throw new FalhaDeServico("resposta_inesperada");
1108
1267
  this.anunciar(modo === "agendamento" ? "Cadastro feito. Abrindo o calendário para escolher data e horário." : "Atendimento criado. Abrindo a sala de atendimento.");
1109
- const aba = window.open(destino, "_blank");
1110
- if (aba) {
1111
- aba.opener = null;
1112
- } else {
1113
- window.location.assign(destino);
1114
- }
1268
+ this.irPara(destino, abaReservada);
1115
1269
  this.form.reset();
1116
1270
  this.closeWidget();
1117
1271
  } catch (erro) {
1272
+ if (abaReservada && !abaReservada.closed) {
1273
+ try {
1274
+ abaReservada.close();
1275
+ } catch (e) {
1276
+ }
1277
+ }
1278
+ this.mostrarFormulario();
1118
1279
  this.tratarFalha(erro);
1119
1280
  } finally {
1120
1281
  this.travarEnvio(false, modo);
@@ -1122,14 +1283,25 @@ var ILibrasWidget = class {
1122
1283
  }
1123
1284
  travarEnvio(travado, modo = "fila") {
1124
1285
  this.enviando = travado;
1125
- for (const botao of [this.submit, this.agendar]) {
1286
+ for (const botao of [this.submit, this.agendar, this.confirmar, this.voltar]) {
1126
1287
  if (botao) botao.disabled = travado;
1127
1288
  }
1128
- const usouAgendar = modo === "agendamento" && this.agendar;
1129
- const ativo = usouAgendar ? this.agendar : this.submit;
1130
- const rotulo = usouAgendar ? this.config.scheduleButtonText : this.config.buttonText;
1289
+ let ativo;
1290
+ let rotulo;
1291
+ if (!this.confirmacao.hidden) {
1292
+ ativo = this.confirmar;
1293
+ rotulo = "Continuar para o iLibras";
1294
+ } else {
1295
+ const usouAgendar = modo === "agendamento" && this.agendar;
1296
+ ativo = usouAgendar ? this.agendar : this.submit;
1297
+ rotulo = usouAgendar ? this.config.scheduleButtonText : this.config.buttonText;
1298
+ }
1131
1299
  ativo.setAttribute("aria-busy", travado ? "true" : "false");
1132
1300
  ativo.textContent = travado ? "Enviando..." : rotulo;
1301
+ if (!travado) {
1302
+ this.confirmar.setAttribute("aria-busy", "false");
1303
+ this.confirmar.textContent = "Continuar para o iLibras";
1304
+ }
1133
1305
  if (travado) this.anunciar("Enviando seus dados. Aguarde.");
1134
1306
  }
1135
1307
  /**
package/index.d.ts CHANGED
@@ -42,6 +42,15 @@ export interface ILibrasConfig {
42
42
  /** Texto do botão de agendamento. Padrão: `'Agendar uma conversa'`. */
43
43
  scheduleButtonText?: string;
44
44
 
45
+ /**
46
+ * Avisa que a pessoa vai para o site do iLibras, numa aba nova, e espera a
47
+ * confirmação antes de criar o atendimento. Padrão: `true`.
48
+ *
49
+ * Com `false`, o widget volta ao comportamento anterior à v1.6.0: cria o
50
+ * atendimento e abre o iLibras direto do botão.
51
+ */
52
+ confirmarRedirecionamento?: boolean;
53
+
45
54
  /** Empilhamento. Padrão: `9999`. */
46
55
  zIndex?: number;
47
56
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ilibras-widget",
3
- "version": "1.5.3",
3
+ "version": "1.6.0",
4
4
  "description": "Widget de acessibilidade em Libras: formulário embarcável que leva a pessoa surda ao atendimento com intérprete, ao vivo ou agendado",
5
5
  "type": "module",
6
6
  "main": "./dist/ilibras-widget.cjs",